package subex import ( "fmt" "main/walk" ) // A node in the AST of a subex type SubexAST interface { compileWith(next SubexState) SubexState } // Process the first subex, then the second, splitting the input text in two type SubexASTConcat struct { first, second SubexAST } func (ast SubexASTConcat) compileWith(next SubexState) SubexState { return ast.first.compileWith(ast.second.compileWith(next)) } func (ast SubexASTConcat) String() string { return fmt.Sprintf("(%v)(%v)", ast.first, ast.second) } // Processing a subex and storing the output in a slot instead of outputting it type SubexASTStore struct { match SubexAST slot rune } func (ast SubexASTStore) compileWith(next SubexState) SubexState { return &SubexStoreBeginState { next: ast.match.compileWith(&SubexStoreEndState { slot: ast.slot, next: next, }), } } func (ast SubexASTStore) String() string { return fmt.Sprintf("$%c(%v)", ast.slot, ast.match) } // Try to run the first subex, if it fails then backtrack and use the second type SubexASTOr struct { first, second SubexAST } func (ast SubexASTOr) compileWith(next SubexState) SubexState { return &SubexGroupState { ast.first.compileWith(next), ast.second.compileWith(next), } } type ConvexRange struct { start, end int } func (cr ConvexRange) minmax() (int, int) { if cr.start == -1 { return cr.end, -1 } else if cr.end == -1 { return cr.start, -1 } else if cr.start < cr.end { return cr.start, cr.end } else { return cr.end, cr.start } } func (cr ConvexRange) decrement() ConvexRange { if cr.start == -1 { return ConvexRange{-1, cr.end - 1} } else if cr.end == -1 { return ConvexRange{cr.start - 1, -1} } else { return ConvexRange{cr.start - 1, cr.end - 1} } } func (cr ConvexRange) compile(content SubexAST, next SubexState) SubexState { min, _ := cr.minmax() if min != 0 { return content.compileWith(cr.decrement().compile(content, next)) } if cr.start == -1 { state := &SubexGroupState {nil, next} state.first = content.compileWith(state) return state } if cr.end == -1 { state := &SubexGroupState {next, nil} state.second = content.compileWith(state) return state } if cr.end == 0 { state := next; for i := 0; i < cr.start; i += 1 { state = &SubexGroupState { content.compileWith(state), next, } } return state } else { state := next; for i := 0; i < cr.end; i += 1 { state = &SubexGroupState { next, content.compileWith(state), } } return state } } // Try to run the subex a number of times that is one of the numbers in the acceptable range // Prioritising the left type SubexASTRepeat struct { content SubexAST acceptable []ConvexRange } func (ast SubexASTRepeat) compileWith(next SubexState) SubexState { var state SubexState = &SubexDeadState{} for _, convex := range ast.acceptable { state = SubexGroupState {state, convex.compile(ast.content, next)} } return state } // Read in a single specific Atom and output it unchanged type SubexASTCopyAtom struct { atom walk.Atom } func (ast SubexASTCopyAtom) compileWith(next SubexState) SubexState { return &SubexCopyAtomState{ atom: ast.atom, next: next, } } // Read in any single Atom and output it unchanged type SubexASTCopyAny struct {} func (ast SubexASTCopyAny) compileWith(next SubexState) SubexState { return &SubexCopyAnyState{next} } func (ast SubexASTCopyAny) String() string { return "." } // Output a series of Atoms without reading anything from input type SubexASTOutput struct { replacement []OutputContent } func (ast SubexASTOutput) compileWith(next SubexState) SubexState { return &SubexOutputState{ content: ast.replacement, next: next, } } // Read in a repeated subex separated by a delimiter. Greedy type SubexASTJoin struct { content, delimiter SubexAST } func (ast SubexASTJoin) compileWith(next SubexState) SubexState { afterContentState := &SubexGroupState { nil, next, } manyContentsState := ast.content.compileWith(afterContentState) afterContentState.first = ast.delimiter.compileWith(manyContentsState) return &SubexGroupState { manyContentsState, next, } } // Run each input Atom through a map to produce an output Atom // Atoms not in the map cause this to not match type SubexASTRange struct { parts map[walk.Atom]walk.Atom } func (ast SubexASTRange) compileWith(next SubexState) SubexState { return &SubexRangeState { parts: ast.parts, next: next, } } // Run content and then assume that output is a series of numbers, sum them and output the total // Will cast strings, booleans and null to numbers type SubexASTSum struct { content SubexAST } func (ast SubexASTSum) compileWith(next SubexState) SubexState { return &SubexSumBeginState { next: ast.content.compileWith(&SubexSumEndState { next: next, }), } }