From d3d17484012dc603ae326bec419cff990898e6a0 Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Wed, 19 Apr 2023 13:48:15 +0100 Subject: Adds the reciprocal operator --- subex/arithmetic.go | 34 ++++++++++++++++++++++++++++++++++ subex/parse.go | 2 ++ subex/subexast.go | 15 +++++++++++++++ 3 files changed, 51 insertions(+) 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, + }), + } +} -- cgit v1.2.3