main.go (6597B)
1 package subex 2 3 import ( 4 "main/walk" 5 ) 6 7 type Transducer struct { 8 storeSize int 9 initialState SubexState 10 } 11 12 type StoreItem struct {} 13 // Where slots are stored 14 type Store []walk.OutputList 15 // Return a new store with all the data from this one 16 func (store Store) clone() Store { 17 newStore := make([]walk.OutputList, len(store)) 18 copy(newStore, store) 19 return newStore 20 } 21 // Return a copy of this store but with an additional slot set 22 func (store Store) withValue(key int, value walk.OutputList) Store { 23 newStore := store.clone() 24 newStore[key] = value 25 return newStore 26 } 27 28 type SlotMap struct { 29 nextId int 30 ids map[rune]int 31 } 32 func (m *SlotMap) getId(slot rune) int { 33 id, exists := m.ids[slot] 34 if exists { 35 return id 36 } 37 id = m.nextId 38 m.nextId++ 39 m.ids[slot] = id 40 return id 41 } 42 43 // Compile the SubexAST into a transducer SubexState that can be run 44 func CompileTransducer(transducerAst SubexAST) Transducer { 45 slotMap := SlotMap { 46 nextId: 0, 47 ids: make(map[rune]int), 48 } 49 initial := transducerAst.compileWith(&SubexNoneState{}, &slotMap, false) 50 return Transducer { 51 storeSize: slotMap.nextId, 52 initialState: initial, 53 } 54 } 55 56 // An immutable stack for outputting to 57 type OutputStack struct { 58 head walk.OutputList 59 tail *OutputStack 60 } 61 func (stack OutputStack) pop() (walk.OutputList, OutputStack) { 62 return stack.head, *stack.tail 63 } 64 func (stack OutputStack) push(atoms walk.OutputList) OutputStack { 65 return OutputStack { 66 head: atoms, 67 tail: &stack, 68 } 69 } 70 func (stack OutputStack) replace(atoms walk.OutputList) OutputStack { 71 return OutputStack { 72 head: atoms, 73 tail: stack.tail, 74 } 75 } 76 func (stack OutputStack) peek() walk.OutputList { 77 return stack.head 78 } 79 80 func topAppend(outputStack OutputStack, values []walk.Value) OutputStack { 81 head, isValues := outputStack.peek().(walk.ValueList) 82 if !isValues { 83 panic("Tried to append a value to an output of non-values") 84 } 85 head = append(walk.ValueList{}, head...) 86 head = append(head, values...) 87 return outputStack.replace(head) 88 } 89 90 func topAppendRunes(outputStack OutputStack, runes walk.RuneList) OutputStack { 91 head, isRunes := outputStack.peek().(walk.RuneList) 92 if !isRunes { 93 panic("Tried to append runes to a non-rune list") 94 } 95 head = append(walk.RuneList{}, head...) 96 head = append(head, runes...) 97 return outputStack.replace(head) 98 } 99 100 // Addition state that goes along with a subex state in an execution branch 101 type auxiliaryState struct { 102 // Content of slots in this branch 103 store Store 104 // The output stack. At the end of the program, whatever is on top of this will be output 105 // States may push or pop to the stack as they wish, creating sort of a call stack that allows states to capture the output of other states 106 outputStack OutputStack 107 // How deeply nested the current execution is inside of the overall value 108 // i.e. starts at zero, is incremented to one when entering an array 109 nesting int 110 } 111 112 func (aux auxiliaryState) cloneStore() auxiliaryState { 113 aux.store = aux.store.clone() 114 return aux 115 } 116 117 func (aux auxiliaryState) withValue(slot int, value walk.OutputList) auxiliaryState { 118 aux.store = aux.store.withValue(slot, value) 119 return aux 120 } 121 122 func (aux auxiliaryState) pushOutput(data walk.OutputList) auxiliaryState { 123 aux.outputStack = aux.outputStack.push(data) 124 return aux 125 } 126 127 func (aux auxiliaryState) popOutput() (walk.OutputList, auxiliaryState) { 128 data, output := aux.outputStack.pop() 129 aux.outputStack = output 130 return data, aux 131 } 132 133 func (aux auxiliaryState) topAppend(values walk.OutputList) auxiliaryState { 134 switch output := values.(type) { 135 case walk.ValueList: 136 aux.outputStack = topAppend(aux.outputStack, output) 137 case walk.RuneList: 138 aux.outputStack = topAppendRunes(aux.outputStack, output) 139 } 140 return aux 141 } 142 143 func (aux auxiliaryState) incNest() auxiliaryState { 144 aux.nesting++ 145 return aux 146 } 147 148 func (aux auxiliaryState) decNest() auxiliaryState { 149 aux.nesting-- 150 return aux 151 } 152 153 // One branch of subex execution 154 type SubexBranch struct { 155 // State in this branch 156 state SubexState 157 // Axiliary state 158 aux auxiliaryState 159 } 160 // Read a single character and return all the branches resulting from this branch consuming it 161 func (pair SubexBranch) eat(char walk.Edible) []SubexBranch { 162 return pair.state.eat(pair.aux, char) 163 } 164 func (pair SubexBranch) accepting() []OutputStack { 165 return pair.state.accepting(pair.aux) 166 } 167 168 func equalStates(left SubexBranch, right SubexBranch) bool { 169 // Only care about if they are the same pointer 170 return left.state == right.state && left.aux.nesting == right.aux.nesting 171 } 172 173 // If two branches have the same state, only the first has a chance of being successful 174 // This function removes all of the pointless execution branches to save execution time 175 func pruneStates(states []SubexBranch) []SubexBranch { 176 uniqueStates := 0 177 outer: for _, state := range states { 178 for i := 0; i < uniqueStates; i++ { 179 if equalStates(state, states[i]) { 180 continue outer 181 } 182 } 183 states[uniqueStates] = state 184 uniqueStates++ 185 } 186 return states[:uniqueStates] 187 } 188 189 func processInput(states []SubexBranch, input walk.StructureIter, nesting int) []SubexBranch { 190 if len(states) == 0 { 191 return states 192 } 193 var tmp []SubexBranch 194 newStates := make([]SubexBranch, 0, 2) 195 for { 196 piece := input.Next() 197 if piece == nil { 198 break 199 } 200 201 // TODO: break if there are no states at this nesting level left 202 // TODO: What to do if there are remaining nested states after all pieces have been used? 203 for _, state := range states { 204 if state.aux.nesting == nesting { 205 newStates = append(newStates, state.eat(piece)...) 206 } else { 207 newStates = append(newStates, state) 208 } 209 } 210 211 structure, isStructure := piece.(walk.Structure) 212 if isStructure { 213 iter := structure.Iter() 214 newStates = processInput(newStates, iter, nesting + 1) 215 } 216 217 tmp = states 218 states = pruneStates(newStates) 219 newStates = tmp[:0] 220 if len(states) == 0 { 221 return states 222 } 223 } 224 return states 225 } 226 227 // Run the subex transducer 228 func RunTransducer(transducer Transducer, input walk.ValueList) (output []walk.Value, err bool) { 229 states := []SubexBranch{{ 230 state: transducer.initialState, 231 aux: auxiliaryState { 232 outputStack: OutputStack { 233 head: walk.ValueList{}, 234 tail: nil, 235 }, 236 store: make([]walk.OutputList, transducer.storeSize), 237 nesting: 0, 238 }, 239 }} 240 241 states = processInput(states, walk.NewValueIter(input), 0) 242 243 for _, state := range states { 244 acceptingStacks := state.accepting() 245 for _, stack := range acceptingStacks { 246 output, isValues := stack.head.(walk.ValueList) 247 if !isValues { 248 panic("Tried to output a non-values list") 249 } 250 return output, false 251 } 252 } 253 return nil, true 254 }