<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/subex
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2023-04-19 12:59:22 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2023-04-19 12:59:22 +0100
commit0072e6aad2f9969348d5315a692f1f5e2ebc075d (patch)
tree0c75e9466a688694383279f56d69a46f014e3680 /subex
parent053a403a77e5b4c46e82932e94d5fb7a4117ce43 (diff)
downloadstred-go-0072e6aad2f9969348d5315a692f1f5e2ebc075d.tar
Adds product/and operator
Diffstat (limited to 'subex')
-rw-r--r--subex/parse.go2
-rw-r--r--subex/subexast.go20
-rw-r--r--subex/subexstate.go87
3 files changed, 87 insertions, 22 deletions
diff --git a/subex/parse.go b/subex/parse.go
index 2389c3b..f73d060 100644
--- a/subex/parse.go
+++ b/subex/parse.go
@@ -236,6 +236,8 @@ func parseSubex(l *RuneReader, minPower int) SubexAST {
}
case r == '+' && minPower <= 8:
lhs = SubexASTSum {lhs}
+ case r == '*' && minPower <= 8:
+ lhs = SubexASTProduct {lhs}
case r == '$' && minPower <= 8:
slot := l.next()
if slot == eof {
diff --git a/subex/subexast.go b/subex/subexast.go
index e191a30..431ea26 100644
--- a/subex/subexast.go
+++ b/subex/subexast.go
@@ -27,7 +27,7 @@ type SubexASTStore struct {
slot rune
}
func (ast SubexASTStore) compileWith(next SubexState) SubexState {
- return &SubexStoreBeginState {
+ return &SubexCaptureBeginState {
next: ast.match.compileWith(&SubexStoreEndState {
slot: ast.slot,
next: next,
@@ -183,15 +183,27 @@ func (ast SubexASTRange) compileWith(next SubexState) SubexState {
}
}
-// 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
+// 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) SubexState {
- return &SubexSumBeginState {
+ return &SubexCaptureBeginState {
next: ast.content.compileWith(&SubexSumEndState {
next: next,
}),
}
}
+
+// Like sum but for AND and product
+type SubexASTProduct struct {
+ content SubexAST
+}
+func (ast SubexASTProduct) compileWith(next SubexState) SubexState {
+ return &SubexCaptureBeginState {
+ next: ast.content.compileWith(&SubexProductEndState {
+ next: next,
+ }),
+ }
+}
diff --git a/subex/subexstate.go b/subex/subexstate.go
index 0a4f1bb..6a80eff 100644
--- a/subex/subexstate.go
+++ b/subex/subexstate.go
@@ -26,15 +26,15 @@ func (state SubexGroupState) accepting(store Store, outputStack OutputStack) []O
return append(state.first.accepting(store, outputStack), state.second.accepting(store, outputStack)...)
}
-// Push an empty value onto the OutputStack and epsilon transition to next
-// This value will be added to until SubexStoreEndState is reached when it will be stored
-type SubexStoreBeginState struct {
+// Just pushes to the OutputStack and hands over to the next state
+// Used to capture the output of the state being handed over to
+type SubexCaptureBeginState struct {
next SubexState
}
-func (state SubexStoreBeginState) eat(store Store, outputStack OutputStack, char walk.Atom) []SubexBranch {
+func (state SubexCaptureBeginState) eat(store Store, outputStack OutputStack, char walk.Atom) []SubexBranch {
return state.next.eat(store, outputStack.push(nil), char)
}
-func (state SubexStoreBeginState) accepting(store Store, outputStack OutputStack) []OutputStack {
+func (state SubexCaptureBeginState) accepting(store Store, outputStack OutputStack) []OutputStack {
return state.next.accepting(store, outputStack.push(nil))
}
@@ -187,7 +187,7 @@ func sumValues(atoms []walk.Atom) (walk.WalkValue, error) {
case walk.ValueNull:
allBools = false
case walk.ValueBool:
- if (bool(v)) {
+ if bool(v) {
sum += 1
any = true
}
@@ -213,19 +213,8 @@ func sumValues(atoms []walk.Atom) (walk.WalkValue, error) {
}
}
-// At the start of a sum, just pushes to the OutputStack allowing the end to capture what was output in the middle
-// Tries to cast values to numbers to sum them and rejects if values are not castable
-type SubexSumBeginState struct {
- next SubexState
-}
-func (state SubexSumBeginState) eat(store Store, outputStack OutputStack, char walk.Atom) []SubexBranch {
- return state.next.eat(store, outputStack.push(nil), char)
-}
-func (state SubexSumBeginState) accepting(store Store, outputStack OutputStack) []OutputStack {
- return state.next.accepting(store, outputStack.push(nil))
-}
-
// At the end of a sum, pops what has been output since the start, sums and outputs it
+// If all values are booleans does OR, if not tries to cast values to numbers to sum them and rejects if values are not castable
type SubexSumEndState struct {
next SubexState
}
@@ -245,3 +234,65 @@ func (state SubexSumEndState) accepting(store Store, outputStack OutputStack) []
}
return state.next.accepting(store, topAppend(newStack, []walk.Atom{sum}))
}
+
+// Compounds atoms into values, if all values are booleans, does AND, if not, tries to cast to numbers and multiply
+func multiplyValues(atoms []walk.Atom) (walk.WalkValue, error) {
+ allBools := true
+ var product float64 = 1
+ var all bool = false
+ values, err := walk.MemoryCompound(atoms)
+ if err != nil {
+ return walk.ValueNull{}, err
+ }
+ for _, value := range values {
+ switch v := value.(type) {
+ case walk.ValueNull:
+ allBools = false
+ product *= 0
+ case walk.ValueBool:
+ if !bool(v) {
+ product *= 0
+ all = false
+ }
+ case walk.ValueNumber:
+ allBools = false
+ product *= float64(v)
+ case walk.ValueString:
+ allBools = false
+ num, err := strconv.ParseFloat(string(v), 64)
+ if err == nil {
+ product *= num
+ } else {
+ return walk.ValueNull{}, errors.New("Tried to sum non-castable string")
+ }
+ default:
+ return walk.ValueNull{}, errors.New("Tried to sum non-number")
+ }
+ }
+ if allBools {
+ return walk.ValueBool(all), nil
+ } else {
+ return walk.ValueNumber(product), nil
+ }
+}
+
+// Does AND or product
+type SubexProductEndState struct {
+ next SubexState
+}
+func (state SubexProductEndState) eat(store Store, outputStack OutputStack, char walk.Atom) []SubexBranch {
+ toMultiply, newStack := outputStack.pop()
+ product, err := multiplyValues(toMultiply)
+ if err != nil {
+ return nil
+ }
+ return state.next.eat(store, topAppend(newStack, []walk.Atom{product}), char)
+}
+func (state SubexProductEndState) accepting(store Store, outputStack OutputStack) []OutputStack {
+ toMultiply, newStack := outputStack.pop()
+ product, err := multiplyValues(toMultiply)
+ if err != nil {
+ return nil
+ }
+ return state.next.accepting(store, topAppend(newStack, []walk.Atom{product}))
+}