<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2023-04-18 15:07:52 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2023-04-18 15:07:52 +0100
commitffd1b73b4f3294d9f3aa2ed600da3ba053aeb47c (patch)
treeac3075e8ea26190838e0e6df3f5e6af65909ca51
parentfebdc5dcd5b25a090b90c920914775265da98d39 (diff)
downloadstred-go-ffd1b73b4f3294d9f3aa2ed600da3ba053aeb47c.tar
Adds the sum operator
Currently doesn't parse strings as each atom is considered independantly. Instead individual characters in strings can be cast
-rw-r--r--subex/parse.go13
-rw-r--r--subex/subexast.go12
-rw-r--r--subex/subexstate.go62
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
+}