| [c2b37db] | 1 | import sys
|
|---|
| 2 | from dataclasses import dataclass
|
|---|
| 3 |
|
|---|
| 4 | from state_space import *
|
|---|
| 5 | from lexical_stream import *
|
|---|
| 6 |
|
|---|
| 7 | def createTransition(stateSpace, contentStr, pid, fromState, toState):
|
|---|
| 8 | transition = Transition(contentStr, pid, fromState, toState)
|
|---|
| 9 | stateSpace.addTransition(transition)
|
|---|
| 10 | return transition
|
|---|
| 11 |
|
|---|
| 12 | @dataclass(frozen=True)
|
|---|
| 13 | class StartTransitionToken:
|
|---|
| 14 | pid: int
|
|---|
| 15 | fromStateID: StateID
|
|---|
| 16 |
|
|---|
| 17 | @dataclass(frozen=True)
|
|---|
| 18 | class SubTransitionToken:
|
|---|
| 19 | contentStr: str
|
|---|
| 20 |
|
|---|
| 21 | @dataclass(frozen=True)
|
|---|
| 22 | class StopTransitionToken:
|
|---|
| 23 | toStateID: StateID
|
|---|
| 24 |
|
|---|
| 25 | class StateTerminal:
|
|---|
| 26 | first = "State"
|
|---|
| 27 |
|
|---|
| 28 | pattern = r"(\d+(?:\.\d+)?) \(id=(\d+)\)"
|
|---|
| 29 |
|
|---|
| 30 | def parseHead(file):
|
|---|
| 31 | file.start()
|
|---|
| 32 | try:
|
|---|
| 33 | file.match(StateTerminal.first)
|
|---|
| 34 | file.skipws()
|
|---|
| 35 | file.match("(")
|
|---|
| 36 | leftID = int(file.regmatch(r"\d+").group(0))
|
|---|
| 37 | if file.match("."):
|
|---|
| 38 | rightID = int(file.regmatch(r"\d+").group(0))
|
|---|
| 39 | file.match(")")
|
|---|
| 40 | else:
|
|---|
| 41 | rightID = 0
|
|---|
| 42 | file.skipws()
|
|---|
| 43 | file.match("(id=")
|
|---|
| 44 | uniqueID = int(file.regmatch(r"\d+").group(0))
|
|---|
| 45 | file.match(")")
|
|---|
| 46 | except Exception as err:
|
|---|
| 47 | file.revert()
|
|---|
| 48 | print(err)
|
|---|
| 49 | raise ParseError(file.pos().lineNum)
|
|---|
| 50 |
|
|---|
| 51 | return file.complete(), StateID(leftID, rightID, uniqueID)
|
|---|
| 52 |
|
|---|
| 53 | def parseFrameEntry(file):
|
|---|
| 54 | file.start()
|
|---|
| 55 | try:
|
|---|
| 56 | if not file.match("| | | | Frame[function="):
|
|---|
| 57 | file.revert()
|
|---|
| 58 | return None
|
|---|
| 59 |
|
|---|
| 60 | functionName = file.regmatch(r"[^\s,]+").group(0)
|
|---|
| 61 | file.match(", location=")
|
|---|
| 62 |
|
|---|
| 63 | location = int(file.regmatch(r"\d+").group(0))
|
|---|
| 64 | file.match(", ")
|
|---|
| 65 |
|
|---|
| 66 | match = file.regmatch(r"(.+), dyscope=d(\d+)\]\n$")
|
|---|
| 67 | source = match.group(1)
|
|---|
| 68 | dyscope = int(match.group(2))
|
|---|
| 69 |
|
|---|
| 70 | return file.complete(), FrameEntry(functionName, location, source, dyscope)
|
|---|
| 71 | except Exception as err:
|
|---|
| 72 | file.revert()
|
|---|
| 73 | print(err)
|
|---|
| 74 | raise ParseError(file.pos().lineNum)
|
|---|
| 75 |
|
|---|
| 76 | def parseProcessState(file):
|
|---|
| 77 | file.start()
|
|---|
| 78 | try:
|
|---|
| 79 | if not file.match("| | process "):
|
|---|
| 80 | file.revert()
|
|---|
| 81 | return None
|
|---|
| 82 |
|
|---|
| 83 | procMatch = file.regmatch(r"\d+")
|
|---|
| 84 | file.skipws()
|
|---|
| 85 | procId = int(procMatch.group(0))
|
|---|
| 86 |
|
|---|
| 87 | atomicCount = 0
|
|---|
| 88 | if file.match("| | | atomicCount="):
|
|---|
| 89 | atomicCount = int(file.regmatch(r"\d+"))
|
|---|
| 90 | file.skipws()
|
|---|
| 91 |
|
|---|
| 92 | if not file.match("| | | call stack"):
|
|---|
| 93 | raise Exception("Expected call stack")
|
|---|
| 94 | file.skipws()
|
|---|
| 95 |
|
|---|
| 96 | frames = []
|
|---|
| 97 | while (tuple := StateTerminal.parseFrameEntry(file)):
|
|---|
| 98 | _, frameEntry = tuple
|
|---|
| 99 | frames.append(frameEntry)
|
|---|
| 100 |
|
|---|
| 101 | return file.complete(), ProcessState(procId, atomicCount, frames)
|
|---|
| 102 | except Exception as err:
|
|---|
| 103 | file.revert()
|
|---|
| 104 | print(err)
|
|---|
| 105 | raise ParseError(file.pos().lineNum)
|
|---|
| 106 |
|
|---|
| 107 | def parse(file):
|
|---|
| 108 | headRange, sid = StateTerminal.parseHead(file)
|
|---|
| 109 | file.skipws()
|
|---|
| 110 | contentStr = ""
|
|---|
| 111 | reachedEmptySpace = False
|
|---|
| 112 | procStateLine = "| Process states\n"
|
|---|
| 113 |
|
|---|
| 114 | while not file.match(procStateLine) and not file.eof():
|
|---|
| 115 | contentStr += file.readline()
|
|---|
| 116 | contentStr += procStateLine
|
|---|
| 117 |
|
|---|
| 118 | procStates = []
|
|---|
| 119 | while (tuple := StateTerminal.parseProcessState(file)):
|
|---|
| 120 | procStateRange, procState = tuple
|
|---|
| 121 | contentStr += procStateRange.getStr()
|
|---|
| 122 | procStates.append(procState)
|
|---|
| 123 |
|
|---|
| 124 | return State(headRange.getStr().strip(), contentStr, sid, procStates)
|
|---|
| 125 |
|
|---|
| 126 | class StartTransitionTerminal:
|
|---|
| 127 | first = "Executed"
|
|---|
| 128 |
|
|---|
| 129 | def parse(file):
|
|---|
| 130 | file.start()
|
|---|
| 131 | try:
|
|---|
| 132 | file.match(StartTransitionTerminal.first)
|
|---|
| 133 | file.skipws()
|
|---|
| 134 | file.match("by")
|
|---|
| 135 | file.skipws()
|
|---|
| 136 | file.match("p")
|
|---|
| 137 | procID = int(file.regmatch(r"\d+").group(0))
|
|---|
| 138 | file.skipws()
|
|---|
| 139 | file.match("from")
|
|---|
| 140 | file.skipws()
|
|---|
| 141 | _, fromStateID = StateTerminal.parseHead(file)
|
|---|
| 142 | except Exception:
|
|---|
| 143 | file.revert()
|
|---|
| 144 | raise ParseError(file.pos().lineNum)
|
|---|
| 145 |
|
|---|
| 146 | file.complete()
|
|---|
| 147 | return StartTransitionToken(procID, fromStateID)
|
|---|
| 148 |
|
|---|
| 149 | class SubTransitionTerminal:
|
|---|
| 150 | first = r"\d+->(\d+|RET):"
|
|---|
| 151 |
|
|---|
| 152 | def parse(file):
|
|---|
| 153 | return SubTransitionToken(file.readline())
|
|---|
| 154 |
|
|---|
| 155 | class StopTransitionTerminal:
|
|---|
| 156 | first = "-->"
|
|---|
| 157 |
|
|---|
| 158 | def parse(file):
|
|---|
| 159 | file.start()
|
|---|
| 160 | try:
|
|---|
| 161 | file.match(StopTransitionTerminal.first)
|
|---|
| 162 | file.skipws()
|
|---|
| 163 | _, toStateID = StateTerminal.parseHead(file)
|
|---|
| 164 | except Exception:
|
|---|
| 165 | file.revert()
|
|---|
| 166 | raise ParseError(file.pos().lineNum)
|
|---|
| 167 |
|
|---|
| 168 | file.complete()
|
|---|
| 169 | return StopTransitionToken(toStateID)
|
|---|
| 170 |
|
|---|
| 171 | def parseTerminal(file, *potentialTerms):
|
|---|
| 172 | for terminal in potentialTerms:
|
|---|
| 173 | if file.peek(terminal.first):
|
|---|
| 174 | return terminal.parse(file)
|
|---|
| 175 | return None
|
|---|
| 176 |
|
|---|
| 177 | def parseInitState(file, stateSpace):
|
|---|
| 178 | try:
|
|---|
| 179 | while not (initState := parseTerminal(file, StateTerminal)) and not file.eof():
|
|---|
| 180 | file.readline()
|
|---|
| 181 | except ParseError as err:
|
|---|
| 182 | print("Parsing error: Failed to parse initial state.", err)
|
|---|
| 183 |
|
|---|
| 184 | if not initState:
|
|---|
| 185 | print("No States found.")
|
|---|
| 186 | return
|
|---|
| 187 |
|
|---|
| 188 | stateSpace.addInitState(initState)
|
|---|
| 189 |
|
|---|
| 190 | def parseState(file, stateSpace):
|
|---|
| 191 | try:
|
|---|
| 192 | return parseTerminal(file, StateTerminal)
|
|---|
| 193 | except ParseError as err:
|
|---|
| 194 | print("Parsing error: Failed to parse initial state.", err)
|
|---|
| 195 | raise
|
|---|
| 196 |
|
|---|
| 197 | def parseStartTransition(file):
|
|---|
| 198 | try:
|
|---|
| 199 | return parseTerminal(file, StartTransitionTerminal)
|
|---|
| 200 | except ParseError as err:
|
|---|
| 201 | print("Parsing error: Failed to parse transition", err)
|
|---|
| 202 | raise
|
|---|
| 203 |
|
|---|
| 204 | def parseSubTransition(file):
|
|---|
| 205 | try:
|
|---|
| 206 | if not (subTransToken := parseTerminal(file, SubTransitionTerminal)):
|
|---|
| 207 | raise ParseError(file.pos().lineNum)
|
|---|
| 208 | return subTransToken
|
|---|
| 209 | except ParseError as err:
|
|---|
| 210 | print("Parsing error: Failed to parse sub transition", err)
|
|---|
| 211 | raise
|
|---|
| 212 |
|
|---|
| 213 | def parseStopTransition(file):
|
|---|
| 214 | try:
|
|---|
| 215 | if not (stopToken := parseTerminal(file, StopTransitionTerminal)):
|
|---|
| 216 | raise ParseError(file.pos().lineNum)
|
|---|
| 217 | return stopToken
|
|---|
| 218 | except ParseError as err:
|
|---|
| 219 | print("Parsing error: Failed to parse sub transition", err)
|
|---|
| 220 | raise
|
|---|
| 221 |
|
|---|
| 222 | def parseLargeTransitionStep(file, stateSpace):
|
|---|
| 223 | if not (startToken := parseStartTransition(file)):
|
|---|
| 224 | return False
|
|---|
| 225 | lastState = stateSpace.getState(startToken.fromStateID)
|
|---|
| 226 |
|
|---|
| 227 | file.skipws()
|
|---|
| 228 | subTransToken = parseSubTransition(file)
|
|---|
| 229 |
|
|---|
| 230 | file.skipws()
|
|---|
| 231 | while (nextState := parseState(file, stateSpace)):
|
|---|
| 232 | newTransition = createTransition(stateSpace, subTransToken.contentStr, startToken.pid, lastState, nextState)
|
|---|
| 233 | lastState = nextState
|
|---|
| 234 |
|
|---|
| 235 | file.skipws()
|
|---|
| 236 | subTransToken = parseSubTransition(file)
|
|---|
| 237 |
|
|---|
| 238 | file.skipws()
|
|---|
| 239 | stopToken = parseStopTransition(file)
|
|---|
| 240 |
|
|---|
| 241 | file.skipws()
|
|---|
| 242 | if not (endState := parseState(file, stateSpace)):
|
|---|
| 243 | endState = stateSpace.getState(stopToken.toStateID)
|
|---|
| 244 | lastTransition = createTransition(stateSpace, subTransToken.contentStr, startToken.pid, lastState, endState)
|
|---|
| 245 |
|
|---|
| 246 | return True
|
|---|
| 247 |
|
|---|
| 248 | def parseStateSpace(fileName):
|
|---|
| 249 | stateSpace = StateSpace()
|
|---|
| 250 |
|
|---|
| 251 | with open(fileName, 'r', encoding="utf-8") as f:
|
|---|
| 252 | file = LexicalStream(f)
|
|---|
| 253 | try:
|
|---|
| 254 | parseInitState(file, stateSpace)
|
|---|
| 255 | file.skipws()
|
|---|
| 256 | while parseLargeTransitionStep(file, stateSpace):
|
|---|
| 257 | file.skipws()
|
|---|
| 258 | except ParseError:
|
|---|
| 259 | pass
|
|---|
| 260 |
|
|---|
| 261 | return stateSpace
|
|---|