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, }, } } func (state SubexGroupState) String() string { return fmt.Sprintf("{%T %p, %T %p}", state.first, state.first, state.second, state.second) } 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) } type SubexCopyNumberState struct { next SubexState filter numberFilter } func (state SubexCopyNumberState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch { number, isNumber := edible.(walk.NumberValue) if !isNumber || !state.filter.numberFilter(float64(number)) { return nil } return []SubexBranch {{ state: state.next, aux: aux.topAppend([]walk.Value {number}), }} } func (state SubexCopyNumberState) 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) 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), }} } type SubexStoreRunesEndState struct { slot int next SubexState } func (state SubexStoreRunesEndState) epsilon(aux auxiliaryState) []SubexBranch { toStore, aux := aux.popOutputRunes() aux.store = aux.store.withRunes(state.slot, toStore) return []SubexBranch {{ state: state.next, aux: aux, }} } type SubexOutputValueLiteralState struct { literal walk.Scalar next SubexState } func (state SubexOutputValueLiteralState) epsilon(aux auxiliaryState) []SubexBranch { return []SubexBranch {{ state: state.next, aux: aux.topAppend([]walk.Value {state.literal}), }} } type SubexOutputValueLoadState struct { slot int next SubexState } func (state SubexOutputValueLoadState) epsilon(aux auxiliaryState) []SubexBranch { return []SubexBranch {{ state: state.next, aux: aux.topAppend(aux.store.values[state.slot]), }} } type SubexOutputRuneLiteralState struct { literal rune next SubexState } func (state SubexOutputRuneLiteralState) epsilon(aux auxiliaryState) []SubexBranch { return []SubexBranch {{ state: state.next, aux: aux.topAppendRune([]rune {state.literal}), }} } type SubexOutputRuneLoadState struct { slot int next SubexState } func (state SubexOutputRuneLoadState) epsilon(aux auxiliaryState) []SubexBranch { return []SubexBranch {{ state: state.next, aux: aux.topAppendRune(aux.store.runes[state.slot]), }} } // 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, }} }