package subex import ( "strings" ) type SubexState interface { eat(store Store, char rune) []SubexBranch accepting(store Store) []string } type SubexGroupState struct { first, second SubexState } func (state SubexGroupState) eat(store Store, char rune) []SubexBranch { otherStore := store.clone() return append(state.first.eat(store, char), state.second.eat(otherStore, char)...) } func (state SubexGroupState) accepting(store Store) []string { return append(state.first.accepting(store), state.second.accepting(store)...) } type SubexStoreState struct { match SubexState slot rune next SubexState toStore string } func (state SubexStoreState) eat(store Store, char rune) (nextStates []SubexBranch) { acceptedOutputs := state.match.accepting(store) for _, acceptedOutput := range acceptedOutputs { nextStore := store.withValue(state.slot, state.toStore + acceptedOutput) nextStates = append(nextStates, state.next.eat(nextStore.clone(), char)...) } nextMatchStates := state.match.eat(store.clone(), char) for _, matchState := range nextMatchStates { nextStates = append(nextStates, SubexBranch { state: SubexStoreState { match: matchState.state, slot: state.slot, next: state.next, toStore: state.toStore + matchState.output, }, output: "", store: store.clone(), }) } return nextStates } func (state SubexStoreState) accepting(store Store) (outputs []string) { acceptedOutputs := state.match.accepting(store) for _, acceptedOutput := range acceptedOutputs { nextStore := store.withValue(state.slot, state.toStore + acceptedOutput) outputs = append(outputs, state.next.accepting(nextStore)...) } return outputs } type SubexOutputState struct { content []TransducerOutput next SubexState } func (state SubexOutputState) build(store Store) string { var builder strings.Builder for _, part := range state.content { builder.WriteString(part.build(store)) } return builder.String() } func (state SubexOutputState) eat(store Store, char rune) []SubexBranch { content := state.build(store) nextStates := state.next.eat(store, char) for i := range nextStates { nextStates[i].output = content + nextStates[i].output } return nextStates } func (state SubexOutputState) accepting(store Store) []string { content := state.build(store) outputs := state.next.accepting(store) for i := range outputs { outputs[i] = content + outputs[i] } return outputs } type SubexNoneState struct {} func (state SubexNoneState) eat(store Store, char rune) []SubexBranch { return nil } func (state SubexNoneState) accepting(store Store) []string { return []string{""} } type SubexCopyRuneState struct { rune rune next SubexState } func (state SubexCopyRuneState) eat(store Store, char rune) []SubexBranch { if char == state.rune { return []SubexBranch{{ state: state.next, output: string(char), store: store, }} } return nil } func (state SubexCopyRuneState) accepting(store Store) []string { return nil } type SubexCopyAnyState struct { next SubexState } func (state SubexCopyAnyState) eat(store Store, char rune) []SubexBranch { return []SubexBranch{{ state: state.next, output: string(char), store: store, }} } func (state SubexCopyAnyState) accepting(store Store) []string { return nil } type SubexRangeState struct { parts map[rune]rune next SubexState } func (state SubexRangeState) eat(store Store, char rune) []SubexBranch { out, exists := state.parts[char] if !exists { return nil } else { return []SubexBranch{{ state: state.next, output: string(out), store: store, }} } } func (state SubexRangeState) accepting(store Store) []string { return nil }