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 ( "fmt" "main/walk" "strings" ) type SubexState interface { } type SubexEpsilonState interface { SubexState epsilon(aux auxiliaryState) []SubexBranch } // A state of execution for the transducer type SubexEatState interface { SubexState // Eat a Atom and transition to any number of new states eat(aux auxiliaryState, edible 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) epsilon(aux auxiliaryState) []SubexBranch { otherAux := aux.cloneStore() return []SubexBranch { { state: state.first, aux: aux, }, { state: state.second, aux: 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.Value{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.RuneEdible) if !isRune || !state.filter.runeFilter(rune(r)) { return nil } return []SubexBranch{{ state: state.next, aux: aux.topAppendRune([]rune{rune(r)}), }} } func (state SubexCopyRuneState) accepting(aux auxiliaryState) []OutputStack { return nil } func (state SubexCopyRuneState) String() string { return fmt.Sprintf("SubexCopyRuneState[%v]", state.filter) } // 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) epsilon(aux auxiliaryState) []SubexBranch { return []SubexBranch {{ state: state.next, aux: aux.pushOutput(nil), }} } func (state SubexCaptureBeginState) String() string { return "CaptureBeginState" } type SubexCaptureRunesBeginState struct { next SubexState } func (state SubexCaptureRunesBeginState) epsilon(aux auxiliaryState) []SubexBranch { return []SubexBranch {{ state: state.next, aux: aux.pushOutputRunes(nil), }} } // Discard the top of the OutputStack type SubexDiscardState struct { next SubexState } func (state SubexDiscardState) epsilon(aux auxiliaryState) []SubexBranch { return []SubexBranch {{ state: state.next, aux: aux.popDiscardOutput(), }} } // 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) epsilon(aux auxiliaryState) []SubexBranch { toStore, aux := aux.popOutput() return []SubexBranch {{ state: state.next, aux: aux.withValue(state.slot, toStore), }} } /* // 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.Value) []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 } */ type OutputValue interface { build(store Store) []walk.Value } type OutputValueLoad struct { slot int } func (ov OutputValueLoad) build(store Store) []walk.Value { return store.values[ov.slot] } type OutputValueLiteral struct { scalar walk.Scalar } func (ov OutputValueLiteral) build(store Store) []walk.Value { return []walk.Value{ov.scalar} } type SubexOutputValuesState struct { content []OutputValue next SubexState } func (state SubexOutputValuesState) epsilon(aux auxiliaryState) []SubexBranch { var content []walk.Value for _, el := range state.content { content = append(content, el.build(aux.store)...) } return []SubexBranch {{ state: state.next, aux: aux.topAppend(content), }} } type OutputRune interface { } type OutputRuneLoad struct { slot int } type OutputRuneLiteral struct { r rune } // A final state, transitions to nothing but is accepting type SubexNoneState struct {} func (state SubexNoneState) eat(aux auxiliaryState, edible 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, edible 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.Value) ([]walk.Value, error) } func (state SubexArithmeticEndState) epsilon(aux auxiliaryState) []SubexBranch { values, aux := aux.popOutput() result, err := state.calculate(values) if err != nil { return nil } return []SubexBranch {{ state: state.next, aux: 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) epsilon(aux auxiliaryState) []SubexBranch { values, aux := aux.popOutput() var array walk.ArrayValue if len(values) % 2 != 0 { panic("Tried to construct array with odd length input") } for i := 0; i < len(values); i += 2 { index, isNum := values[i].(walk.NumberValue) if !isNum { panic("Tried to construct array with non-numeric index") } array = append(array, walk.ArrayElement { Index: int(index), Value: values[i + 1], }) } return []SubexBranch {{ state: state.next, aux: aux.topAppend([]walk.Value {array}), }} } type SubexConstructArrayValuesState struct { next SubexState } func (state SubexConstructArrayValuesState) epsilon(aux auxiliaryState) []SubexBranch { values, aux := aux.popOutput() var array walk.ArrayValue for _, v := range values { array = append(array, walk.ArrayElement { Index: 0, Value: v, }) } return []SubexBranch {{ state: state.next, aux: aux.topAppend([]walk.Value {array}), }} } type SubexConstructMapState struct { next SubexState } func (state SubexConstructMapState) epsilon(aux auxiliaryState) []SubexBranch { values, aux := aux.popOutput() var m walk.MapValue if len(values) % 2 != 0 { panic("Tried to construct array with odd length input") } for i := 0; i < len(values); i += 2 { key, isNum := values[i].(walk.StringValue) if !isNum { panic("Tried to construct array with non-numeric index") } m = append(m, walk.MapElement { Key: string(key), Value: values[i + 1], }) } return []SubexBranch {{ state: state.next, aux: aux.topAppend([]walk.Value {m}), }} } type SubexConstructStringState struct { next SubexState } func (state SubexConstructStringState) construct(runes []rune) []walk.Value { var builder strings.Builder for _, r := range runes { builder.WriteRune(r) } return []walk.Value{walk.StringValue(builder.String())} } func (state SubexConstructStringState) epsilon(aux auxiliaryState) []SubexBranch { runes, aux := aux.popOutputRunes() return []SubexBranch {{ state: state.next, aux: aux.topAppend(state.construct(runes)), }} } func (state SubexConstructStringState) String() string { return "SubexConstructStringState" } type SubexIncrementNestState struct { keys bool next SubexState } func (state SubexIncrementNestState) epsilon(aux auxiliaryState) []SubexBranch { aux.nesting = append(aux.nesting, state.keys) return []SubexBranch {{ state: state.next, aux: aux, }} } func (state SubexIncrementNestState) String() string { return "IncrementNestState" } type SubexDecrementNestState struct { next SubexState } func (state SubexDecrementNestState) epsilon(aux auxiliaryState) []SubexBranch { aux.nesting = aux.nesting[:len(aux.nesting) - 1] // aux.nestingValue will be set in addStates return []SubexBranch {{ state: state.next, aux: aux, }} }