<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/subex
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2023-04-19 13:48:15 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2023-04-19 13:48:15 +0100
commitd3d17484012dc603ae326bec419cff990898e6a0 (patch)
tree44aa5b05b6aa3f9b988e6cd59ddacec04660ff32 /subex
parent58d50737702adc48604f0a709080dcc587d7145f (diff)
downloadstred-go-d3d17484012dc603ae326bec419cff990898e6a0.tar
Adds the reciprocal operator
Diffstat (limited to 'subex')
-rw-r--r--subex/arithmetic.go34
-rw-r--r--subex/parse.go2
-rw-r--r--subex/subexast.go15
3 files changed, 51 insertions, 0 deletions
diff --git a/subex/arithmetic.go b/subex/arithmetic.go
index ff30a58..4404a1c 100644
--- a/subex/arithmetic.go
+++ b/subex/arithmetic.go
@@ -118,3 +118,37 @@ func negateValues(atoms []walk.Atom) ([]walk.Atom, error) {
}
return negatedNumbers, nil
}
+
+// If all are castable to numbers, takes reciprocals of all and returns them
+// Else errors
+func reciprocalValues(atoms []walk.Atom) ([]walk.Atom, error) {
+ var reciprocals []walk.Atom
+ values, err := walk.MemoryCompound(atoms)
+ if err != nil {
+ return nil, err
+ }
+ for _, value := range values {
+ switch v := value.(type) {
+ case walk.ValueNull:
+ return nil, errors.New("Tried to take reciprocal of null")
+ case walk.ValueBool:
+ if bool(v) {
+ reciprocals = append(reciprocals, walk.ValueNumber(1))
+ } else {
+ return nil, errors.New("Tried to take reciprocal of false")
+ }
+ case walk.ValueNumber:
+ reciprocals = append(reciprocals, walk.ValueNumber(1 / v))
+ case walk.ValueString:
+ num, err := strconv.ParseFloat(string(v), 64)
+ if err == nil {
+ reciprocals = append(reciprocals, walk.ValueNumber(1 / num))
+ } else {
+ return nil, errors.New("Tried to take reciprocal of non-castable string")
+ }
+ default:
+ return nil, errors.New("Tried to take reciprocal of non-number")
+ }
+ }
+ return reciprocals, nil
+}
diff --git a/subex/parse.go b/subex/parse.go
index 8635186..f5316c9 100644
--- a/subex/parse.go
+++ b/subex/parse.go
@@ -240,6 +240,8 @@ func parseSubex(l *RuneReader, minPower int) SubexAST {
lhs = SubexASTProduct {lhs}
case r == '-' && minPower <= 8:
lhs = SubexASTNegate {lhs}
+ case r == '/' && minPower <= 8:
+ lhs = SubexASTReciprocal {lhs}
case r == '$' && minPower <= 8:
slot := l.next()
if slot == eof {
diff --git a/subex/subexast.go b/subex/subexast.go
index 3c807ee..78c9aff 100644
--- a/subex/subexast.go
+++ b/subex/subexast.go
@@ -223,3 +223,18 @@ func (ast SubexASTNegate) compileWith(next SubexState) SubexState {
}),
}
}
+
+// Runs the content Subex and collects the output
+// If it is a list of atoms castable to numbers, it takes the reciprocal of them all and outputs them
+// Else it rejects
+type SubexASTReciprocal struct {
+ content SubexAST
+}
+func (ast SubexASTReciprocal) compileWith(next SubexState) SubexState {
+ return &SubexCaptureBeginState {
+ next: ast.content.compileWith(&SubexArithmeticEndState {
+ next: next,
+ calculate: reciprocalValues,
+ }),
+ }
+}