subexstate.go (11067B)
1 package subex 2 3 // TODO: Simplify this implementation by combining similar states into one type 4 // e.g. Combine all of the copy states into a single type that has a filter function 5 6 import ( 7 "main/walk" 8 ) 9 10 // A state of execution for the transducer 11 type SubexState interface { 12 // Eat a Atom and transition to any number of new states 13 eat(aux auxiliaryState, char walk.Edible) []SubexBranch 14 // Find accepting states reachable through epsilon transitions and return their outputs 15 accepting(aux auxiliaryState) []OutputStack 16 } 17 18 // Try first, if it fails then try second 19 type SubexGroupState struct { 20 first, second SubexState 21 } 22 func (state SubexGroupState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { 23 otherAux := aux.cloneStore() 24 return append(state.first.eat(aux, char), state.second.eat(otherAux, char)...) 25 } 26 func (state SubexGroupState) accepting(aux auxiliaryState) []OutputStack { 27 otherAux := aux.cloneStore() 28 return append(state.first.accepting(aux), state.second.accepting(otherAux)...) 29 } 30 31 type SubexCopyState struct { 32 next SubexState 33 filter valueFilter 34 } 35 func (state SubexCopyState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { 36 value, isValue := edible.(walk.Value) 37 if !isValue || !state.filter.valueFilter(value) { 38 return nil 39 } 40 return []SubexBranch{{ 41 state: state.next, 42 aux: aux.topAppend(walk.ValueList{value}), 43 }} 44 } 45 func (state SubexCopyState) accepting(aux auxiliaryState) []OutputStack { 46 return nil 47 } 48 49 type SubexCopyRuneState struct { 50 next SubexState 51 filter runeFilter 52 } 53 func (state SubexCopyRuneState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { 54 r, isRune := edible.(walk.StringRuneAtom) 55 if !isRune || !state.filter.runeFilter(r) { 56 return nil 57 } 58 return []SubexBranch{{ 59 state: state.next, 60 aux: aux.topAppend(walk.RuneList{r}), 61 }} 62 } 63 func (state SubexCopyRuneState) accepting(aux auxiliaryState) []OutputStack { 64 return nil 65 } 66 67 // Just pushes to the OutputStack and hands over to the next state 68 // Used to capture the output of the state being handed over to 69 type SubexCaptureBeginState struct { 70 next SubexState 71 } 72 func (state SubexCaptureBeginState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { 73 return state.next.eat(aux.pushOutput(walk.ValueList{}), char) 74 } 75 func (state SubexCaptureBeginState) accepting(aux auxiliaryState) []OutputStack { 76 return state.next.accepting(aux.pushOutput(walk.ValueList{})) 77 } 78 func (state SubexCaptureBeginState) String() string { 79 return "CaptureBeginState" 80 } 81 82 type SubexCaptureRunesBeginState struct { 83 next SubexState 84 } 85 func (state SubexCaptureRunesBeginState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { 86 return state.next.eat(aux.pushOutput(walk.RuneList{}), char) 87 } 88 func (state SubexCaptureRunesBeginState) accepting(aux auxiliaryState) []OutputStack { 89 return state.next.accepting(aux.pushOutput(walk.RuneList{})) 90 } 91 92 // Discard the top of the OutputStack 93 type SubexDiscardState struct { 94 next SubexState 95 } 96 func (state SubexDiscardState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { 97 _, newAux := aux.popOutput() 98 return state.next.eat(newAux, char) 99 } 100 func (state SubexDiscardState) accepting(aux auxiliaryState) []OutputStack { 101 _, newAux := aux.popOutput() 102 return state.next.accepting(newAux) 103 } 104 105 // Pop the top of the OutputStack which contains the stuff outputted since the start of the store 106 // This outputted data gets stored in a slot 107 type SubexStoreEndState struct { 108 slot int 109 next SubexState 110 } 111 func (state SubexStoreEndState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { 112 toStore, aux := aux.popOutput() 113 aux = aux.withValue(state.slot, toStore) 114 return state.next.eat(aux, char) 115 } 116 func (state SubexStoreEndState) accepting(aux auxiliaryState) []OutputStack { 117 toStore, aux := aux.popOutput() 118 aux = aux.withValue(state.slot, toStore) 119 return state.next.accepting(aux) 120 } 121 122 // A part of an output literal, either an Atom or a slot from which to load 123 type OutputContent interface { 124 // Given the current store, return the ValueList produced by the TransducerOutput 125 buildValues(Store) walk.ValueList 126 // Given the current store, return the RuneList produced by the TransducerOutput 127 buildRunes(Store) walk.RuneList 128 } 129 130 // An OutputContent which is just a Value literal 131 type OutputValueLiteral struct { 132 value walk.Value 133 } 134 func (replacement OutputValueLiteral) buildValues(store Store) walk.ValueList { 135 return walk.ValueList{replacement.value} 136 } 137 func (replacement OutputValueLiteral) buildRunes(store Store) walk.RuneList { 138 // TODO: serialise to JSON 139 panic("Unimplemented!") 140 } 141 142 // An OutputContent which is just a rune literal 143 type OutputRuneLiteral struct { 144 rune walk.StringRuneAtom 145 } 146 func (replacement OutputRuneLiteral) buildValues(store Store) walk.ValueList { 147 // TODO: Try to deserialise 148 panic("Unimplemented!") 149 } 150 func (replacement OutputRuneLiteral) buildRunes(store Store) walk.RuneList { 151 return walk.RuneList {replacement.rune} 152 } 153 154 // An OutputContent which is a slot that is loaded from 155 type OutputLoad struct { 156 slot int 157 } 158 func (replacement OutputLoad) buildValues(store Store) walk.ValueList { 159 values, isValues := store[replacement.slot].(walk.ValueList) 160 if !isValues { 161 panic("Tried to output non-values list") 162 } 163 return values 164 } 165 func (replacement OutputLoad) buildRunes(store Store) walk.RuneList { 166 runes, isRunes := store[replacement.slot].(walk.RuneList) 167 if !isRunes { 168 panic("Tried to output non-runes as runes") 169 } 170 return runes 171 } 172 173 // Don't read in anything, just output the series of data and slots specified 174 type SubexOutputState struct { 175 content []OutputContent 176 next SubexState 177 } 178 // Given a store, return what is outputted by an epsilon transition from this state 179 // TODO: separate into buildValues and buildRunes 180 func (state SubexOutputState) build(store Store) walk.ValueList { 181 var result walk.ValueList 182 for _, part := range state.content { 183 result = append(result, part.buildValues(store)...) 184 } 185 return result 186 } 187 func (state SubexOutputState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { 188 content := state.build(aux.store) 189 nextStates := state.next.eat(aux.topAppend(content), char) 190 return nextStates 191 } 192 func (state SubexOutputState) accepting(aux auxiliaryState) []OutputStack { 193 content := state.build(aux.store) 194 outputStacks := state.next.accepting(aux.topAppend(content)) 195 return outputStacks 196 } 197 198 // A final state, transitions to nothing but is accepting 199 type SubexNoneState struct {} 200 func (state SubexNoneState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { 201 return nil 202 } 203 func (state SubexNoneState) accepting(aux auxiliaryState) []OutputStack { 204 return []OutputStack{aux.outputStack} 205 } 206 207 // A dead end state, handy for making internals work nicer but technically redundant 208 type SubexDeadState struct {} 209 func (state SubexDeadState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { 210 return nil 211 } 212 func (state SubexDeadState) accepting (aux auxiliaryState) []OutputStack { 213 return nil 214 } 215 216 // Read in an Atom and apply a map to generate an Atom to output 217 // If the input isn't in the map transition to nothing 218 // TODO 219 // type SubexRangeState struct { 220 // parts map[walk.Atom]walk.Atom 221 // next SubexState 222 // } 223 // func (state SubexRangeState) eat(aux auxiliaryState, char walk.Atom) []SubexBranch { 224 // out, exists := state.parts[char] 225 // if !exists { 226 // return nil 227 // } else { 228 // return []SubexBranch{{ 229 // state: state.next, 230 // outputStack: topAppend(outputStack, []walk.Atom{out}), 231 // store: store, 232 // }} 233 // } 234 // } 235 // func (state SubexRangeState) accepting(aux auxiliaryState) []OutputStack { 236 // return nil 237 // } 238 239 240 type SubexArithmeticEndState struct { 241 next SubexState 242 calculate func(walk.ValueList) (walk.ValueList, error) 243 } 244 func (state SubexArithmeticEndState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { 245 toCompute, aux := aux.popOutput() 246 values, isValues := toCompute.(walk.ValueList) 247 if !isValues { 248 panic("Tried to do arithmetic on non-values") 249 } 250 result, err := state.calculate(values) 251 if err != nil { 252 return nil 253 } 254 return state.next.eat(aux.topAppend(result), char) 255 } 256 func (state SubexArithmeticEndState) accepting(aux auxiliaryState) []OutputStack { 257 toCompute, aux := aux.popOutput() 258 values, isValues := toCompute.(walk.ValueList) 259 if !isValues { 260 panic("Tried to do arithmetic on non-values") 261 } 262 result, err := state.calculate(values) 263 if err != nil { 264 return nil 265 } 266 return state.next.accepting(aux.topAppend(result)) 267 } 268 269 type SubexDiscardTerminalState struct { 270 terminal walk.Terminal 271 next SubexState 272 } 273 func (state SubexDiscardTerminalState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { 274 if edible != state.terminal { 275 return nil 276 } 277 return []SubexBranch{{ 278 state: state.next, 279 aux: aux, 280 }} 281 } 282 func (state SubexDiscardTerminalState) accepting(aux auxiliaryState) []OutputStack { 283 return nil 284 } 285 286 type SubexConstructArrayState struct { 287 next SubexState 288 } 289 func (state SubexConstructArrayState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { 290 outputs, aux := aux.popOutput() 291 values, isValues := outputs.(walk.ValueList) 292 if !isValues { 293 panic("Tried to create an array from non-values") 294 } 295 array := walk.ArrayStructure(values) 296 return state.next.eat(aux.topAppend(walk.ValueList{array}), edible) 297 } 298 func (state SubexConstructArrayState) accepting(aux auxiliaryState) []OutputStack { 299 outputs, aux := aux.popOutput() 300 values, isValues := outputs.(walk.ValueList) 301 if !isValues { 302 panic("Tried to create an array from non-values") 303 } 304 array := walk.ArrayStructure(values) 305 return state.next.accepting(aux.topAppend(walk.ValueList{array})) 306 } 307 308 type SubexConstructStringState struct { 309 next SubexState 310 } 311 func (state SubexConstructStringState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { 312 outputs, aux := aux.popOutput() 313 runes, isRunes := outputs.(walk.RuneList) 314 if !isRunes { 315 panic("Tried to create a string from non-runes") 316 } 317 s := walk.StringStructure(runes) 318 return state.next.eat(aux.topAppend(walk.ValueList{s}), edible) 319 } 320 func (state SubexConstructStringState) accepting(aux auxiliaryState) []OutputStack { 321 outputs, aux := aux.popOutput() 322 runes, isRunes := outputs.(walk.RuneList) 323 if !isRunes { 324 panic("Tried to create a string from non-runes") 325 } 326 s := walk.StringStructure(runes) 327 return state.next.accepting(aux.topAppend(walk.ValueList{s})) 328 } 329 330 type SubexIncrementNestState struct { 331 next SubexState 332 } 333 func (state SubexIncrementNestState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { 334 return state.next.eat(aux.incNest(), edible) 335 } 336 func (state SubexIncrementNestState) accepting(aux auxiliaryState) []OutputStack { 337 return state.next.accepting(aux.incNest()) 338 } 339 func (state SubexIncrementNestState) String() string { 340 return "IncrementNestState" 341 } 342 343 type SubexDecrementNestState struct { 344 next SubexState 345 } 346 func (state SubexDecrementNestState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { 347 return state.next.eat(aux.decNest(), edible) 348 } 349 func (state SubexDecrementNestState) accepting(aux auxiliaryState) []OutputStack { 350 return state.next.accepting(aux.decNest()) 351 }