diff options
author | Charlie Stanton <charlie@shtanton.xyz> | 2023-04-19 12:59:22 +0100 |
---|---|---|
committer | Charlie Stanton <charlie@shtanton.xyz> | 2023-04-19 12:59:22 +0100 |
commit | 0072e6aad2f9969348d5315a692f1f5e2ebc075d (patch) | |
tree | 0c75e9466a688694383279f56d69a46f014e3680 | |
parent | 053a403a77e5b4c46e82932e94d5fb7a4117ce43 (diff) | |
download | stred-go-0072e6aad2f9969348d5315a692f1f5e2ebc075d.tar |
Adds product/and operator
-rw-r--r-- | subex/parse.go | 2 | ||||
-rw-r--r-- | subex/subexast.go | 20 | ||||
-rw-r--r-- | subex/subexstate.go | 87 |
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})) +} |