From ffd1b73b4f3294d9f3aa2ed600da3ba053aeb47c Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Tue, 18 Apr 2023 15:07:52 +0100 Subject: Adds the sum operator Currently doesn't parse strings as each atom is considered independantly. Instead individual characters in strings can be cast --- subex/parse.go | 13 ++++------- subex/subexast.go | 12 +++++++++++ subex/subexstate.go | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/subex/parse.go b/subex/parse.go index 6e1493b..0208142 100644 --- a/subex/parse.go +++ b/subex/parse.go @@ -206,7 +206,7 @@ func parseSubex(l *RuneReader, minPower int) SubexAST { case '[': rangeParts := parseRangeSubex(l) lhs = SubexASTRange {rangeParts} - case ')', '|', ';', '{': + case ')', '|', ';', '{', '+': l.rewind() return nil case '$': @@ -243,10 +243,12 @@ func parseSubex(l *RuneReader, minPower int) SubexAST { r := l.next() switch { case r == '{' && minPower <= 8: - lhs = SubexASTRepeat{ + lhs = SubexASTRepeat { content: lhs, acceptable: parseRepeatRange(l), } + case r == '+' && minPower <= 8: + lhs = SubexASTSum {lhs} case r == '|' && minPower <= 4: rhs := parseSubex(l, 5) if rhs == nil { @@ -262,13 +264,6 @@ func parseSubex(l *RuneReader, minPower int) SubexAST { content: lhs, delimiter: rhs, } - //case r == '+' && minPower <= 6: - // rhs := parseSubex(l, 7) - // if rhs == nil { - // panic("Missing subex after +") - // } - // // TODO: Implement this. Runs subex on the left, then subex on the right, then sums the outputs of each and outputs that - // lhs = SubexASTAdd{lhs, rhs} default: l.rewind() break loop diff --git a/subex/subexast.go b/subex/subexast.go index 5e63f03..9e7067b 100644 --- a/subex/subexast.go +++ b/subex/subexast.go @@ -181,3 +181,15 @@ func (ast SubexASTRange) compileWith(next SubexState) SubexState { 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 &SubexSumState { + inputState: ast.content.compileWith(&SubexNoneState{}), + next: next, + } +} diff --git a/subex/subexstate.go b/subex/subexstate.go index 3c554a2..cbcd210 100644 --- a/subex/subexstate.go +++ b/subex/subexstate.go @@ -188,3 +188,65 @@ func (state SubexRangeState) eat(store Store, char walk.Atom) []SubexBranch { func (state SubexRangeState) accepting(store Store) [][]walk.Atom { return nil } + +func sumValues(values []walk.Atom) walk.ValueNumber { + var sum float64 = 0 + for _, value := range values { + switch v := value.(type) { + case walk.ValueBool: + if (bool(v)) { + sum += 1 + } + case walk.ValueNumber: + sum += float64(v) + case rune: + if '0' <= v && v <= '9' { + sum += float64(v - '0') + } + default: + } + } + return walk.ValueNumber(sum) +} + +// Run the inputState machine and sum any values output, output the sum +// Cast non numbers into numbers, ignore anything uncastable +type SubexSumState struct { + inputState SubexState + next SubexState + sum walk.ValueNumber +} +func (state SubexSumState) eat(store Store, char walk.Atom) (nextStates []SubexBranch) { + acceptedOutputs := state.inputState.accepting(store) + for _, acceptedOutput := range acceptedOutputs { + nextNextStates := state.next.eat(store.clone(), char) + for i := range nextNextStates { + nextNextStates[i].output = walk.ConcatData([]walk.Atom{sumValues(append(acceptedOutput, state.sum))}, nextNextStates[i].output) + } + nextStates = append(nextStates, nextNextStates...) + } + nextInputStates := state.inputState.eat(store.clone(), char) + for _, inputState := range nextInputStates { + nextStates = append(nextStates, SubexBranch { + state: &SubexSumState { + inputState: inputState.state, + next: state.next, + sum: sumValues(append(inputState.output, state.sum)), + }, + output: nil, + store: inputState.store, + }) + } + return nextStates +} +func (state SubexSumState) accepting(store Store) (outputs [][]walk.Atom) { + acceptedOutputs := state.inputState.accepting(store) + for _, acceptedOutput := range acceptedOutputs { + nextOutputs := state.next.accepting(store.clone()) + for i := range nextOutputs { + nextOutputs[i] = walk.ConcatData([]walk.Atom{sumValues(append(acceptedOutput, state.sum))}, nextOutputs[i]) + } + outputs = append(outputs, nextOutputs...) + } + return outputs +} -- cgit v1.2.3