package subex // TODO: Simplify this implementation by combining similar states into one type // e.g. Combine all of the copy states into a single type that has a filter function import ( "main/walk" ) // A state of execution for the transducer type SubexState interface { // Eat a Atom and transition to any number of new states eat(aux auxiliaryState, char walk.Edible) []SubexBranch // Find accepting states reachable through epsilon transitions and return their outputs accepting(aux auxiliaryState) []OutputStack } // Try first, if it fails then try second type SubexGroupState struct { first, second SubexState } func (state SubexGroupState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { otherAux := aux.cloneStore() return append(state.first.eat(aux, char), state.second.eat(otherAux, char)...) } func (state SubexGroupState) accepting(aux auxiliaryState) []OutputStack { otherAux := aux.cloneStore() return append(state.first.accepting(aux), state.second.accepting(otherAux)...) } type SubexCopyState struct { next SubexState filter valueFilter } func (state SubexCopyState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { value, isValue := edible.(walk.Value) if !isValue || !state.filter.valueFilter(value) { return nil } return []SubexBranch{{ state: state.next, aux: aux.topAppend(walk.ValueList{value}), }} } func (state SubexCopyState) accepting(aux auxiliaryState) []OutputStack { return nil } type SubexCopyRuneState struct { next SubexState filter runeFilter } func (state SubexCopyRuneState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { r, isRune := edible.(walk.StringRuneAtom) if !isRune || !state.filter.runeFilter(r) { return nil } return []SubexBranch{{ state: state.next, aux: aux.topAppend(walk.RuneList{r}), }} } func (state SubexCopyRuneState) accepting(aux auxiliaryState) []OutputStack { return nil } // Just pushes to the OutputStack and hands over to the next state // Used to capture the output of the state being handed over to type SubexCaptureBeginState struct { next SubexState } func (state SubexCaptureBeginState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { return state.next.eat(aux.pushOutput(walk.ValueList{}), char) } func (state SubexCaptureBeginState) accepting(aux auxiliaryState) []OutputStack { return state.next.accepting(aux.pushOutput(walk.ValueList{})) } func (state SubexCaptureBeginState) String() string { return "CaptureBeginState" } type SubexCaptureRunesBeginState struct { next SubexState } func (state SubexCaptureRunesBeginState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { return state.next.eat(aux.pushOutput(walk.RuneList{}), char) } func (state SubexCaptureRunesBeginState) accepting(aux auxiliaryState) []OutputStack { return state.next.accepting(aux.pushOutput(walk.RuneList{})) } // Discard the top of the OutputStack type SubexDiscardState struct { next SubexState } func (state SubexDiscardState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { _, newAux := aux.popOutput() return state.next.eat(newAux, char) } func (state SubexDiscardState) accepting(aux auxiliaryState) []OutputStack { _, newAux := aux.popOutput() return state.next.accepting(newAux) } // Pop the top of the OutputStack which contains the stuff outputted since the start of the store // This outputted data gets stored in a slot type SubexStoreEndState struct { slot int next SubexState } func (state SubexStoreEndState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { toStore, aux := aux.popOutput() aux = aux.withValue(state.slot, toStore) return state.next.eat(aux, char) } func (state SubexStoreEndState) accepting(aux auxiliaryState) []OutputStack { toStore, aux := aux.popOutput() aux = aux.withValue(state.slot, toStore) return state.next.accepting(aux) } // A part of an output literal, either an Atom or a slot from which to load type OutputContent interface { // Given the current store, return the ValueList produced by the TransducerOutput buildValues(Store) walk.ValueList // Given the current store, return the RuneList produced by the TransducerOutput buildRunes(Store) walk.RuneList } // An OutputContent which is just a Value literal type OutputValueLiteral struct { value walk.Value } func (replacement OutputValueLiteral) buildValues(store Store) walk.ValueList { return walk.ValueList{replacement.value} } func (replacement OutputValueLiteral) buildRunes(store Store) walk.RuneList { // TODO: serialise to JSON panic("Unimplemented!") } // An OutputContent which is just a rune literal type OutputRuneLiteral struct { rune walk.StringRuneAtom } func (replacement OutputRuneLiteral) buildValues(store Store) walk.ValueList { // TODO: Try to deserialise panic("Unimplemented!") } func (replacement OutputRuneLiteral) buildRunes(store Store) walk.RuneList { return walk.RuneList {replacement.rune} } // An OutputContent which is a slot that is loaded from type OutputLoad struct { slot int } func (replacement OutputLoad) buildValues(store Store) walk.ValueList { values, isValues := store[replacement.slot].(walk.ValueList) if !isValues { panic("Tried to output non-values list") } return values } func (replacement OutputLoad) buildRunes(store Store) walk.RuneList { runes, isRunes := store[replacement.slot].(walk.RuneList) if !isRunes { panic("Tried to output non-runes as runes") } return runes } // Don't read in anything, just output the series of data and slots specified type SubexOutputState struct { content []OutputContent next SubexState } // Given a store, return what is outputted by an epsilon transition from this state // TODO: separate into buildValues and buildRunes func (state SubexOutputState) build(store Store) walk.ValueList { var result walk.ValueList for _, part := range state.content { result = append(result, part.buildValues(store)...) } return result } func (state SubexOutputState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { content := state.build(aux.store) nextStates := state.next.eat(aux.topAppend(content), char) return nextStates } func (state SubexOutputState) accepting(aux auxiliaryState) []OutputStack { content := state.build(aux.store) outputStacks := state.next.accepting(aux.topAppend(content)) return outputStacks } // A final state, transitions to nothing but is accepting type SubexNoneState struct {} func (state SubexNoneState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { return nil } func (state SubexNoneState) accepting(aux auxiliaryState) []OutputStack { return []OutputStack{aux.outputStack} } // A dead end state, handy for making internals work nicer but technically redundant type SubexDeadState struct {} func (state SubexDeadState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { return nil } func (state SubexDeadState) accepting (aux auxiliaryState) []OutputStack { return nil } // Read in an Atom and apply a map to generate an Atom to output // If the input isn't in the map transition to nothing // TODO // type SubexRangeState struct { // parts map[walk.Atom]walk.Atom // next SubexState // } // func (state SubexRangeState) eat(aux auxiliaryState, char walk.Atom) []SubexBranch { // out, exists := state.parts[char] // if !exists { // return nil // } else { // return []SubexBranch{{ // state: state.next, // outputStack: topAppend(outputStack, []walk.Atom{out}), // store: store, // }} // } // } // func (state SubexRangeState) accepting(aux auxiliaryState) []OutputStack { // return nil // } type SubexArithmeticEndState struct { next SubexState calculate func(walk.ValueList) (walk.ValueList, error) } func (state SubexArithmeticEndState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch { toCompute, aux := aux.popOutput() values, isValues := toCompute.(walk.ValueList) if !isValues { panic("Tried to do arithmetic on non-values") } result, err := state.calculate(values) if err != nil { return nil } return state.next.eat(aux.topAppend(result), char) } func (state SubexArithmeticEndState) accepting(aux auxiliaryState) []OutputStack { toCompute, aux := aux.popOutput() values, isValues := toCompute.(walk.ValueList) if !isValues { panic("Tried to do arithmetic on non-values") } result, err := state.calculate(values) if err != nil { return nil } return state.next.accepting(aux.topAppend(result)) } type SubexDiscardTerminalState struct { terminal walk.Terminal next SubexState } func (state SubexDiscardTerminalState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { if edible != state.terminal { return nil } return []SubexBranch{{ state: state.next, aux: aux, }} } func (state SubexDiscardTerminalState) accepting(aux auxiliaryState) []OutputStack { return nil } type SubexConstructArrayState struct { next SubexState } func (state SubexConstructArrayState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { outputs, aux := aux.popOutput() values, isValues := outputs.(walk.ValueList) if !isValues { panic("Tried to create an array from non-values") } array := walk.ArrayStructure(values) return state.next.eat(aux.topAppend(walk.ValueList{array}), edible) } func (state SubexConstructArrayState) accepting(aux auxiliaryState) []OutputStack { outputs, aux := aux.popOutput() values, isValues := outputs.(walk.ValueList) if !isValues { panic("Tried to create an array from non-values") } array := walk.ArrayStructure(values) return state.next.accepting(aux.topAppend(walk.ValueList{array})) } type SubexConstructStringState struct { next SubexState } func (state SubexConstructStringState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { outputs, aux := aux.popOutput() runes, isRunes := outputs.(walk.RuneList) if !isRunes { panic("Tried to create a string from non-runes") } s := walk.StringStructure(runes) return state.next.eat(aux.topAppend(walk.ValueList{s}), edible) } func (state SubexConstructStringState) accepting(aux auxiliaryState) []OutputStack { outputs, aux := aux.popOutput() runes, isRunes := outputs.(walk.RuneList) if !isRunes { panic("Tried to create a string from non-runes") } s := walk.StringStructure(runes) return state.next.accepting(aux.topAppend(walk.ValueList{s})) } type SubexIncrementNestState struct { next SubexState } func (state SubexIncrementNestState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { return state.next.eat(aux.incNest(), edible) } func (state SubexIncrementNestState) accepting(aux auxiliaryState) []OutputStack { return state.next.accepting(aux.incNest()) } func (state SubexIncrementNestState) String() string { return "IncrementNestState" } type SubexDecrementNestState struct { next SubexState } func (state SubexDecrementNestState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { return state.next.eat(aux.decNest(), edible) } func (state SubexDecrementNestState) accepting(aux auxiliaryState) []OutputStack { return state.next.accepting(aux.decNest()) }