package subex import ( "fmt" "main/walk" ) // A node in the AST of a subex type SubexAST interface { compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) 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, inType Type, outType Type) SubexState { return ast.First.compileWith( ast.Second.compileWith(next, slotMap, inType, outType), slotMap, inType, outType, ) } 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 SubexASTStoreValues struct { Match SubexAST Slot rune } func (ast SubexASTStoreValues) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { if inType != ValueType { panic("Invalid inType storing to value slot") } id := slotMap.getId(ast.Slot) var endState SubexState = &SubexStoreEndState { slot: id, next: next, } switch ast.Slot { case '+': endState = &SubexCaptureBeginState { next: ast.Match.compileWith(&SubexArithmeticEndState { calculate: arithmeticSum, next: endState, }, slotMap, inType, outType), } case '*': endState = &SubexCaptureBeginState { next: ast.Match.compileWith(&SubexArithmeticEndState { calculate: arithmeticProduct, next: endState, }, slotMap, inType, outType), } default: endState = ast.Match.compileWith(endState, slotMap, inType, outType) } return &SubexCaptureBeginState { next: endState, } } func (ast SubexASTStoreValues) String() string { return fmt.Sprintf("$%c(%v)", ast.Slot, ast.Match) } type SubexASTAppendStoreValues struct { Match SubexAST Slot rune } func (ast SubexASTAppendStoreValues) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { id := slotMap.getId(ast.Slot) newNext := ast.Match.compileWith(&SubexStoreEndState { slot: id, next: next, }, slotMap, inType, ValueType) return &SubexOutputValueLoadState { slot: id, next: &SubexCaptureBeginState { next: newNext, }, } } type SubexASTStoreRunes struct { Match SubexAST Slot rune } func (ast SubexASTStoreRunes) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { id := slotMap.getRuneId(ast.Slot) newNext := ast.Match.compileWith(&SubexStoreRunesEndState { slot: id, next: next, }, slotMap, inType, RuneType) return &SubexCaptureRunesBeginState { next: newNext, } } func (ast SubexASTStoreRunes) String() string { return fmt.Sprintf("(%v)$%c", ast.Match, ast.Slot) } // Try to run the first subex, if it fails then backtrack and use the second type SubexASTAppendStoreRunes struct { Match SubexAST Slot rune } func (ast SubexASTAppendStoreRunes) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { id := slotMap.getId(ast.Slot) newNext := ast.Match.compileWith(&SubexStoreEndState { slot: id, next: next, }, slotMap, inType, RuneType) return &SubexOutputRuneLoadState { slot: id, next: &SubexCaptureBeginState { next: newNext, }, } } type SubexASTOr struct { First, Second SubexAST } func (ast SubexASTOr) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { return &SubexGroupState { ast.First.compileWith(next, slotMap, inType, outType), ast.Second.compileWith(next, slotMap, inType, outType), } } 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, inType Type, outType Type) SubexState { min, _ := cr.minmax() if min != 0 { return content.compileWith( cr.decrement().compile(content, next, slotMap, inType, outType), slotMap, inType, outType, ) } if cr.Start == -1 { state := &SubexGroupState {nil, next} state.first = content.compileWith(state, slotMap, inType, outType) return state } if cr.End == -1 { state := &SubexGroupState {next, nil} state.second = content.compileWith(state, slotMap, inType, outType) return state } if cr.End == 0 { state := next; for i := 0; i < cr.Start; i += 1 { state = &SubexGroupState { content.compileWith(state, slotMap, inType, outType), next, } } return state } else { state := next; for i := 0; i < cr.End; i += 1 { state = &SubexGroupState { next, content.compileWith(state, slotMap, inType, outType), } } 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, inType Type, outType Type) SubexState { var state SubexState = &SubexDeadState{} for _, convex := range ast.Acceptable { state = &SubexGroupState {state, convex.compile(ast.Content, next, slotMap, inType, outType)} } 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, inType Type, outType Type) SubexState { if inType != ValueType || outType != ValueType { panic("Invalid types for SubexASTCopyScalar") } 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, inType Type, outType Type) SubexState { if inType != RuneType || outType != RuneType { panic("Invalid types for SubexASTCopyAnyRune") } return &SubexCopyRuneState { next: next, filter: anyRuneFilter{}, } } func (ast SubexASTCopyAnyRune) String() string { return ".RUNE" } type SubexASTCopyRune struct { rune rune } func (ast SubexASTCopyRune) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { if inType != RuneType || outType != RuneType { panic("Invalid types for SubexASTCopyRune") } return &SubexCopyRuneState { next: next, filter: selectRuneFilter {ast.rune}, } } func (ast SubexASTCopyRune) String() string { return string(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, inType Type, outType Type) SubexState { if inType != ValueType || outType != ValueType { panic("Invalid types for SubexASTCopyBool") } 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, inType Type, outType Type) SubexState { if inType != ValueType || outType != ValueType { panic("Invalid types for SubexASTCopyNumber") } return &SubexCopyState { next: next, filter: anyNumberFilter{}, } } func (ast SubexASTCopyNumber) String() string { return "%" } type SubexASTNumberFilter interface { compile() numberFilter computable() bool compute() float64 } type SubexASTNumberFilterLiteral struct { value float64 } func (ast SubexASTNumberFilterLiteral) compile() numberFilter { return equalNumberFilter {ast.value} } func (ast SubexASTNumberFilterLiteral) computable() bool { return true } func (ast SubexASTNumberFilterLiteral) compute() float64 { return ast.value } type NumberSubset int const ( NumberSubsetReal NumberSubset = iota NumberSubsetInteger NumberSubsetPositiveInteger NumberSubsetZeroToOne NumberSubsetPositiveReal NumberSubsetNonNegativeReal ) type SubexASTNumberFilterSubset struct { subset NumberSubset } func (ast SubexASTNumberFilterSubset) compile() numberFilter { switch ast.subset { case NumberSubsetReal: return anyNumberFilter{} case NumberSubsetInteger: return divisibleNumberFilter { divisor: 1.0, target: 0.0, } case NumberSubsetPositiveInteger: return andNumberFilter { lhs: divisibleNumberFilter { divisor: 1.0, target: 0.0, }, rhs: greaterThanNumberFilter {0.0}, } case NumberSubsetZeroToOne: return andNumberFilter { lhs: notNumberFilter { lessThanNumberFilter {0}, }, rhs: notNumberFilter { greaterThanNumberFilter {1}, }, } case NumberSubsetPositiveReal: return greaterThanNumberFilter {0} case NumberSubsetNonNegativeReal: return notNumberFilter { lessThanNumberFilter {0}, } default: panic("Invalid NumberSubset") } } func (ast SubexASTNumberFilterSubset) computable() bool { return false } func (ast SubexASTNumberFilterSubset) compute() float64 { panic("Tried to compute uncomputable") } type SubexASTNumberFilterCount struct { count int } func (ast SubexASTNumberFilterCount) compile() numberFilter { return andNumberFilter { lhs: andNumberFilter { lhs: notNumberFilter { lessThanNumberFilter {0.0}, }, rhs: lessThanNumberFilter {float64(ast.count)}, }, rhs: divisibleNumberFilter { divisor: 1.0, target: 0.0, }, } } func (ast SubexASTNumberFilterCount) computable() bool { return false } func (ast SubexASTNumberFilterCount) compute() float64 { panic("Tried to compute uncomputable") } type SubexASTNumberFilterAdd struct { lhs, rhs SubexASTNumberFilter } func (ast SubexASTNumberFilterAdd) compile() numberFilter { if ast.lhs.computable() { return ast.rhs.compile().add(ast.lhs.compute()) } else { return ast.lhs.compile().add(ast.rhs.compute()) } } func (ast SubexASTNumberFilterAdd) computable() bool { return ast.lhs.computable() && ast.rhs.computable() } func (ast SubexASTNumberFilterAdd) compute() float64 { return ast.lhs.compute() + ast.rhs.compute() } func (ast SubexASTNumberFilterAdd) String() string { return fmt.Sprintf("(%v + %v)", ast.lhs, ast.rhs) } type SubexASTNumberFilterMultiply struct { lhs, rhs SubexASTNumberFilter } func (ast SubexASTNumberFilterMultiply) compile() numberFilter { if ast.lhs.computable() { return ast.rhs.compile().multiply(ast.lhs.compute()) } else { return ast.lhs.compile().multiply(ast.rhs.compute()) } } func (ast SubexASTNumberFilterMultiply) computable() bool { return ast.lhs.computable() && ast.rhs.computable() } func (ast SubexASTNumberFilterMultiply) compute() float64 { return ast.lhs.compute() * ast.rhs.compute() } func (ast SubexASTNumberFilterMultiply) String() string { return fmt.Sprintf("(%v * %v)", ast.lhs, ast.rhs) } type SubexASTCopyNumberFilter struct { filter SubexASTNumberFilter } func (ast SubexASTCopyNumberFilter) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { if inType != ValueType || outType != ValueType { panic("Invalid types for SubexASTCopyNumberFilter") } return &SubexCopyNumberState { next: next, filter: ast.filter.compile(), } } // Read in a null, bool, number, string or empty array or map and output it unchanged type SubexASTCopyAnySimpleValue struct {} func (ast SubexASTCopyAnySimpleValue) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { if inType != ValueType || outType != ValueType { panic("Invalid types for SubexASTCopyAnySimpleValue") } return &SubexCopyState { next: next, filter: simpleValueFilter{}, } } // Read in any single Atom and output it unchanged type SubexASTCopyAnyValue struct {} func (ast SubexASTCopyAnyValue) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { if inType != ValueType || outType != ValueType { panic("Invalid types for SubexASTCopyAnyValue") } 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 "=...=" } */ type SubexASTOutputValueLiteral struct { literal walk.Scalar } func (ast SubexASTOutputValueLiteral) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { if outType != ValueType { panic("Invalid outType for SubexASTOutputValueLiteral") } return &SubexOutputValueLiteralState { literal: ast.literal, next: next, } } type SubexASTOutputValueLoad struct { slot rune } func (ast SubexASTOutputValueLoad) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { if outType != ValueType { panic("Invalid outType for SubexASTOutputValueLoad") } return &SubexOutputValueLoadState { slot: slotMap.getId(ast.slot), next: next, } } type SubexASTOutputRuneLiteral struct { literal rune } func (ast SubexASTOutputRuneLiteral) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { if outType != RuneType { panic("Invalid outType for SubexASTOutputRuneLiteral") } return &SubexOutputRuneLiteralState { literal: ast.literal, next: next, } } type SubexASTOutputRuneLoad struct { slot rune } func (ast SubexASTOutputRuneLoad) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { if outType != RuneType { panic("Invalid outType for SubexASTOutputRuneLoad") } return &SubexOutputRuneLoadState { slot: slotMap.getRuneId(ast.slot), next: 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, slotMap *SlotMap) SubexState { // return &SubexRangeState { // parts: ast.Parts, // next: next, // } // } // func (ast SubexASTRange) String() string { // return fmt.Sprintf("[abc=xyz]") // } type SubexASTBinop struct { op func ([]walk.Value) ([]walk.Value, error) lhs, rhs SubexAST } func (ast SubexASTBinop) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { if outType != ValueType { panic("Invalid types for SubexASTBinop") } return &SubexCaptureBeginState { next: ast.lhs.compileWith( ast.rhs.compileWith( &SubexArithmeticEndState { next: next, calculate: ast.op, }, slotMap, inType, outType, ), slotMap, inType, outType, ), } } // Does nothing type SubexASTEmpty struct {} func (ast SubexASTEmpty) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { return next } func (ast SubexASTEmpty) String() string { return "()" } // Discards the output from the content subex type SubexASTDiscard struct { Content SubexAST InnerOutType Type } func (ast SubexASTDiscard) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { newNext := ast.Content.compileWith(&SubexDiscardState {next}, slotMap, inType, ast.InnerOutType) if inType == ValueType { return &SubexCaptureBeginState { next: newNext, } } else { return &SubexCaptureRunesBeginState { next: newNext, } } } func (ast SubexASTDiscard) String() string { return fmt.Sprintf("(%v)$_", ast.Content) } type SubexASTDestructure struct { Destructure Structure Structure Structure Content SubexAST } func (ast SubexASTDestructure) compileWith(next SubexState, slotMap *SlotMap, inType Type, outType Type) SubexState { var innerOutType Type var construct SubexState switch ast.Structure { case NoneStructure: innerOutType = outType construct = next case StringStructure: innerOutType = RuneType construct = &SubexConstructStringState { next: next, } case ArrayStructure: innerOutType = ValueType construct = &SubexConstructArrayState { next: next, } case ArrayValuesStructure: innerOutType = ValueType construct = &SubexConstructArrayValuesState { next: next, } case MapStructure: innerOutType = ValueType construct = &SubexConstructMapState { next: next, } default: panic("Invalid ast structure") } var innerInType Type var destructFooter SubexState switch ast.Destructure { case NoneStructure: innerInType = inType destructFooter = construct case StringStructure: innerInType = RuneType destructFooter = &SubexDiscardTerminalState { terminal: walk.StringEnd, next: &SubexDecrementNestState { next: construct, }, } case ArrayStructure: innerInType = ValueType destructFooter = &SubexDiscardTerminalState { terminal: walk.ArrayEnd, next: &SubexDecrementNestState { next: construct, }, } case ArrayValuesStructure: innerInType = ValueType destructFooter = &SubexDiscardTerminalState { terminal: walk.ArrayEnd, next: &SubexDecrementNestState { next: construct, }, } case MapStructure: innerInType = ValueType destructFooter = &SubexDiscardTerminalState { terminal: walk.MapEnd, next: &SubexDecrementNestState { next: construct, }, } default: panic("Invalid ast destructure") } inner := ast.Content.compileWith( destructFooter, slotMap, innerInType, innerOutType, ) var beginConstruct SubexState switch ast.Structure { case NoneStructure: beginConstruct = inner case StringStructure: beginConstruct = &SubexCaptureRunesBeginState { next: inner, } case ArrayStructure: beginConstruct = &SubexCaptureBeginState { next: inner, } case ArrayValuesStructure: beginConstruct = &SubexCaptureBeginState { next: inner, } case MapStructure: beginConstruct = &SubexCaptureBeginState { next: inner, } default: panic("Invalid ast structure") } switch ast.Destructure { case NoneStructure: return beginConstruct case StringStructure: return &SubexCaptureBeginState { next: &SubexCopyState { filter: anyStringFilter{}, next: &SubexDiscardState { next: &SubexIncrementNestState { keys: true, next: beginConstruct, }, }, }, } case ArrayStructure: return &SubexCaptureBeginState { next: &SubexCopyState { filter: anyArrayFilter{}, next: &SubexDiscardState { next: &SubexIncrementNestState { keys: true, next: beginConstruct, }, }, }, } case ArrayValuesStructure: return &SubexCaptureBeginState { next: &SubexCopyState { filter: anyArrayFilter{}, next: &SubexDiscardState { next: &SubexIncrementNestState { keys: false, next: beginConstruct, }, }, }, } case MapStructure: return &SubexCaptureBeginState { next: &SubexCopyState { filter: anyMapFilter{}, next: &SubexDiscardState { next: &SubexIncrementNestState { keys: true, next: beginConstruct, }, }, }, } default: panic("Invalid destructure in ast") } } func (ast SubexASTDestructure) String() string { return fmt.Sprintf("%v(%v)%v", ast.Destructure, ast.Content, ast.Structure) }