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 /subex | |
| parent | 053a403a77e5b4c46e82932e94d5fb7a4117ce43 (diff) | |
| download | stred-go-0072e6aad2f9969348d5315a692f1f5e2ebc075d.tar | |
Adds product/and operator
Diffstat (limited to 'subex')
| -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})) +} | 
