package subex import ( "fmt" "main/walk" ) // A node in the AST of a subex type SubexAST interface { compileWith(next SubexState, slotMap *SlotMap, runic bool) 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, slotMap *SlotMap, runic bool) SubexState { return ast.First.compileWith(ast.Second.compileWith(next, slotMap, runic), slotMap, runic) } 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, slotMap *SlotMap, runic bool) SubexState { id := slotMap.getId(ast.Slot) newNext := ast.Match.compileWith(&SubexStoreEndState { slot: id, next: next, }, slotMap, runic) if !runic { return &SubexCaptureBeginState { next: newNext, } } else { return &SubexCaptureRunesBeginState { next: newNext, } } } 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, slotMap *SlotMap, runic bool) SubexState { return &SubexGroupState { ast.First.compileWith(next, slotMap, runic), ast.Second.compileWith(next, slotMap, runic), } } func (ast SubexASTOr) String() string { return fmt.Sprintf("(%v)|(%v)", ast.First, ast.Second) } 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, slotMap *SlotMap, runic bool) SubexState { min, _ := cr.minmax() if min != 0 { return content.compileWith(cr.decrement().compile(content, next, slotMap, runic), slotMap, runic) } if cr.Start == -1 { state := &SubexGroupState {nil, next} state.first = content.compileWith(state, slotMap, runic) return state } if cr.End == -1 { state := &SubexGroupState {next, nil} state.second = content.compileWith(state, slotMap, runic) return state } if cr.End == 0 { state := next; for i := 0; i < cr.Start; i += 1 { state = &SubexGroupState { content.compileWith(state, slotMap, runic), next, } } return state } else { state := next; for i := 0; i < cr.End; i += 1 { state = &SubexGroupState { next, content.compileWith(state, slotMap, runic), } } 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, slotMap *SlotMap, runic bool) SubexState { var state SubexState = &SubexDeadState{} for _, convex := range ast.Acceptable { state = &SubexGroupState {state, convex.compile(ast.Content, next, slotMap, runic)} } return state } func (ast SubexASTRepeat) String() string { return fmt.Sprintf("(%v){...}", ast.Content) } // Read in a single specific Atom and output it unchanged type SubexASTCopyScalar struct { Scalar walk.Scalar } func (ast SubexASTCopyScalar) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCopyState{ filter: selectScalarFilter {ast.Scalar}, next: next, } } func (ast SubexASTCopyScalar) String() string { return fmt.Sprintf("a") } type SubexASTCopyAnyRune struct {} func (ast SubexASTCopyAnyRune) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCopyRuneState { next: next, filter: anyRuneFilter{}, } } type SubexASTCopyRune struct { rune rune } func (ast SubexASTCopyRune) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCopyRuneState { next: next, filter: selectRuneFilter {ast.rune}, } } // Read in a single atom that must be a boolean and output it unchanged type SubexASTCopyBool struct {} func (ast SubexASTCopyBool) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCopyState { next: next, filter: anyBoolFilter{}, } } func (ast SubexASTCopyBool) String() string { return "?" } // Read in a single atom that must be a number and output it unchanged type SubexASTCopyNumber struct {} func (ast SubexASTCopyNumber) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCopyState { next: next, filter: anyNumberFilter{}, } } func (ast SubexASTCopyNumber) String() string { return "%" } // Read in a full string value and copy it out unchanged // # is equivalent to "_{-0}" // TODO // type SubexASTCopyString struct {} // func (ast SubexASTCopyString) compileWith(next SubexState, slotMap *SlotMap) SubexState { // stringAtomState := &SubexCopyStringAtomState { // next: nil, // } // stringContentState := &SubexGroupState { // &SubexCopyScalarState { // scalar: walk.NewAtomStringTerminal(), // next: next, // }, // stringAtomState, // } // stringAtomState.next = stringContentState // return &SubexCopyScalarState { // scalar: walk.NewAtomStringTerminal(), // next: stringContentState, // } // } // func (ast SubexASTCopyString) String() string { // return "#" // } // Read in any single Atom and output it unchanged type SubexASTCopyAnyValue struct {} func (ast SubexASTCopyAnyValue) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCopyState { next: next, filter: anyValueFilter{}, } } func (ast SubexASTCopyAnyValue) String() string { return "." } type OutputContentAST interface { compile(slotMap *SlotMap) OutputContent } type OutputLoadAST struct { slot rune } func (ast OutputLoadAST) compile(slotMap *SlotMap) OutputContent { return OutputLoad {slotMap.getId(ast.slot)} } type OutputValueLiteralAST struct { atom walk.Value } func (ast OutputValueLiteralAST) compile(slotMap *SlotMap) OutputContent { return OutputValueLiteral {ast.atom} } type OutputRuneLiteralAST struct { rune walk.StringRuneAtom } func (ast OutputRuneLiteralAST) compile(slotMap *SlotMap) OutputContent { return OutputRuneLiteral {ast.rune} } // Output a series of Atoms without reading anything from input type SubexASTOutput struct { Replacement []OutputContentAST } func (ast SubexASTOutput) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { var content []OutputContent for _, el := range ast.Replacement { content = append(content, el.compile(slotMap)) } return &SubexOutputState{ content: content, next: next, } } func (ast SubexASTOutput) String() string { return "=...=" } // Read in a repeated subex separated by a delimiter. Greedy type SubexASTJoin struct { Content, Delimiter SubexAST } func (ast SubexASTJoin) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { afterContentState := &SubexGroupState { nil, next, } manyContentsState := ast.Content.compileWith(afterContentState, slotMap, runic) afterContentState.first = ast.Delimiter.compileWith(manyContentsState, slotMap, runic) return &SubexGroupState { manyContentsState, next, } } func (ast SubexASTJoin) String() string { return fmt.Sprintf("(%v);(%v)", ast.Content, ast.Delimiter) } // 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, slotMap *SlotMap) SubexState { // return &SubexRangeState { // parts: ast.Parts, // next: next, // } // } // func (ast SubexASTRange) String() string { // return fmt.Sprintf("[abc=xyz]") // } // Run content, if content is a list of booleans, OR them, if all values are castable to numbers, sum them and output the total // Reject if neither of these cases match type SubexASTSum struct { Content SubexAST } func (ast SubexASTSum) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCaptureBeginState { next: ast.Content.compileWith(&SubexArithmeticEndState { next: next, calculate: sumValues, }, slotMap, runic), } } func (ast SubexASTSum) String() string { return fmt.Sprintf("(%v)+", ast.Content) } // Like sum but for AND and product type SubexASTProduct struct { Content SubexAST } func (ast SubexASTProduct) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCaptureBeginState { next: ast.Content.compileWith(&SubexArithmeticEndState { next: next, calculate: multiplyValues, }, slotMap, runic), } } func (ast SubexASTProduct) String() string { return fmt.Sprintf("(%v)*", ast.Content) } // Runs the content Subex, if all outputted atoms can be cast to numbers, outputs them all negated // Rejects if this fails type SubexASTNegate struct { Content SubexAST } func (ast SubexASTNegate) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCaptureBeginState { next: ast.Content.compileWith(&SubexArithmeticEndState { next: next, calculate: negateValues, }, slotMap, runic), } } func (ast SubexASTNegate) String() string { return fmt.Sprintf("(%v)-", ast.Content) } // Runs the content Subex and collects the output // If it is a list of atoms castable to numbers, it takes the reciprocal of them all and outputs them // Else it rejects type SubexASTReciprocal struct { Content SubexAST } func (ast SubexASTReciprocal) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCaptureBeginState { next: ast.Content.compileWith(&SubexArithmeticEndState { next: next, calculate: reciprocalValues, }, slotMap, runic), } } func (ast SubexASTReciprocal) String() string { return fmt.Sprintf("(%v)/", ast.Content) } // Runs the content Subex and collects the output // Maps over the values in the output, casting each to a boolean, notting each and then outputs them // Rejects if it cannot cast to boolean type SubexASTNot struct { Content SubexAST } func (ast SubexASTNot) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCaptureBeginState { next: ast.Content.compileWith(&SubexArithmeticEndState { next: next, calculate: notValues, }, slotMap, runic), } } func (ast SubexASTNot) String() string { return fmt.Sprintf("(%v)!", ast.Content) } // Does nothing type SubexASTEmpty struct {} func (ast SubexASTEmpty) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return next } func (ast SubexASTEmpty) String() string { return "()" } // Discards the output from the content subex type SubexASTDiscard struct { Content SubexAST } func (ast SubexASTDiscard) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { newNext := ast.Content.compileWith(&SubexDiscardState {next}, slotMap, runic) if !runic { return &SubexCaptureBeginState { next: newNext, } } else { return &SubexCaptureRunesBeginState { next: newNext, } } } func (ast SubexASTDiscard) String() string { return fmt.Sprintf("(%v)$_", ast.Content) } // Go into an array, pass the content each of the values in the array to eat and then leave the array type SubexASTEnterArray struct { Content SubexAST } func (ast SubexASTEnterArray) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCaptureBeginState { next: &SubexIncrementNestState { next: &SubexCopyState { filter: anyArrayFilter{}, next: &SubexDiscardState { next: &SubexCaptureBeginState { next: ast.Content.compileWith( &SubexDiscardTerminalState { terminal: walk.ArrayEndTerminal{}, next: &SubexDecrementNestState { next: &SubexConstructArrayState {next: next}, }, }, slotMap, runic, ), }, }, }, }, } } type SubexASTEnterString struct { Content SubexAST } func (ast SubexASTEnterString) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState { return &SubexCaptureBeginState { next: &SubexIncrementNestState { next: &SubexCopyState { filter: anyStringFilter{}, next: &SubexDiscardState { next: &SubexCaptureRunesBeginState { next: ast.Content.compileWith( &SubexDecrementNestState { next: &SubexDiscardTerminalState { terminal: walk.StringEndTerminal{}, next: &SubexConstructStringState {next: next}, }, }, slotMap, true, ), }, }, }, }, } }