From 8cf10efe3b5a1bcc70bc6e5590ee63fd5eb00c5b Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Wed, 19 Jul 2023 11:57:59 +0100 Subject: Huge refactor to a more value based system, doing away with terminals. Also introduces unit testing --- subex/arithmetic.go | 114 ++++++++++++++++++++++------------------------------ 1 file changed, 47 insertions(+), 67 deletions(-) (limited to 'subex/arithmetic.go') diff --git a/subex/arithmetic.go b/subex/arithmetic.go index 1ebd1a6..9e5e530 100644 --- a/subex/arithmetic.go +++ b/subex/arithmetic.go @@ -6,27 +6,23 @@ import ( "strconv" ) -func sumValues(atoms []walk.Atom) ([]walk.Atom, error) { +func sumValues(values walk.ValueList) (walk.ValueList, error) { allBools := true var sum float64 = 0 var any bool = false - values, err := walk.Compound(atoms) - if err != nil { - return nil, err - } for _, value := range values { switch v := value.(type) { - case walk.ValueNull: + case walk.NullScalar: allBools = false - case walk.ValueBool: - if bool(v) { + case walk.BoolScalar: + if v { sum += 1 any = true } - case walk.ValueNumber: + case walk.NumberScalar: allBools = false sum += float64(v) - case walk.ValueString: + case walk.StringStructure: allBools = false num, err := strconv.ParseFloat(string(v), 64) if err == nil { @@ -39,35 +35,31 @@ func sumValues(atoms []walk.Atom) ([]walk.Atom, error) { } } if allBools { - return []walk.Atom{walk.NewAtomBool(any)}, nil + return walk.ValueList{walk.BoolScalar(any)}, nil } else { - return []walk.Atom{walk.NewAtomNumber(sum)}, nil + return walk.ValueList{walk.NumberScalar(sum)}, nil } } // 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.Atom, error) { +func multiplyValues(values walk.ValueList) (walk.ValueList, error) { allBools := true var product float64 = 1 var all bool = false - values, err := walk.Compound(atoms) - if err != nil { - return nil, err - } for _, value := range values { switch v := value.(type) { - case walk.ValueNull: + case walk.NullScalar: allBools = false product *= 0 - case walk.ValueBool: - if !bool(v) { + case walk.BoolScalar: + if !v { product *= 0 all = false } - case walk.ValueNumber: + case walk.NumberScalar: allBools = false product *= float64(v) - case walk.ValueString: + case walk.StringStructure: allBools = false num, err := strconv.ParseFloat(string(v), 64) if err == nil { @@ -80,35 +72,31 @@ func multiplyValues(atoms []walk.Atom) ([]walk.Atom, error) { } } if allBools { - return []walk.Atom{walk.NewAtomBool(all)}, nil + return walk.ValueList{walk.BoolScalar(all)}, nil } else { - return []walk.Atom{walk.NewAtomNumber(product)}, nil + return walk.ValueList{walk.NumberScalar(product)}, nil } } // Does tries to cast all to numbers and negates them -func negateValues(atoms []walk.Atom) ([]walk.Atom, error) { - var negatedNumbers []walk.Atom - values, err := walk.Compound(atoms) - if err != nil { - return nil, err - } +func negateValues(values walk.ValueList) (walk.ValueList, error) { + var negatedNumbers walk.ValueList for _, value := range values { switch v := value.(type) { - case walk.ValueNull: - negatedNumbers = append(negatedNumbers, walk.NewAtomNumber(0)) - case walk.ValueBool: - if bool(v) { - negatedNumbers = append(negatedNumbers, walk.NewAtomNumber(-1)) + case walk.NullScalar: + negatedNumbers = append(negatedNumbers, walk.NumberScalar(0)) + case walk.BoolScalar: + if v { + negatedNumbers = append(negatedNumbers, walk.NumberScalar(-1)) } else { - negatedNumbers = append(negatedNumbers, walk.NewAtomNumber(0)) + negatedNumbers = append(negatedNumbers, walk.NumberScalar(0)) } - case walk.ValueNumber: - negatedNumbers = append(negatedNumbers, walk.NewAtomNumber(-float64(v))) - case walk.ValueString: + case walk.NumberScalar: + negatedNumbers = append(negatedNumbers, walk.NumberScalar(-float64(v))) + case walk.StringStructure: num, err := strconv.ParseFloat(string(v), 64) if err == nil { - negatedNumbers = append(negatedNumbers, walk.NewAtomNumber(-num)) + negatedNumbers = append(negatedNumbers, walk.NumberScalar(-num)) } else { return nil, errors.New("Tried to negate non-castable string") } @@ -121,28 +109,24 @@ func negateValues(atoms []walk.Atom) ([]walk.Atom, error) { // 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.Compound(atoms) - if err != nil { - return nil, err - } +func reciprocalValues(values walk.ValueList) (walk.ValueList, error) { + var reciprocals walk.ValueList for _, value := range values { switch v := value.(type) { - case walk.ValueNull: + case walk.NullScalar: return nil, errors.New("Tried to take reciprocal of null") - case walk.ValueBool: - if bool(v) { - reciprocals = append(reciprocals, walk.NewAtomNumber(1)) + case walk.BoolScalar: + if v { + reciprocals = append(reciprocals, walk.NumberScalar(1)) } else { return nil, errors.New("Tried to take reciprocal of false") } - case walk.ValueNumber: - reciprocals = append(reciprocals, walk.NewAtomNumber(1 / float64(v))) - case walk.ValueString: + case walk.NumberScalar: + reciprocals = append(reciprocals, walk.NumberScalar(1 / float64(v))) + case walk.StringStructure: num, err := strconv.ParseFloat(string(v), 64) if err == nil { - reciprocals = append(reciprocals, walk.NewAtomNumber(1 / num)) + reciprocals = append(reciprocals, walk.NumberScalar(1 / num)) } else { return nil, errors.New("Tried to take reciprocal of non-castable string") } @@ -155,21 +139,17 @@ func reciprocalValues(atoms []walk.Atom) ([]walk.Atom, error) { // If all are castable to booleans, NOTs all and returns them // Else errors -func notValues(atoms []walk.Atom) (notted []walk.Atom, err error) { - values, err := walk.Compound(atoms) - if err != nil { - return nil, err - } +func notValues(values walk.ValueList) (notted walk.ValueList, err error) { for _, value := range values { switch v := value.(type) { - case walk.ValueNull: - notted = append(notted, walk.NewAtomBool(true)) - case walk.ValueBool: - notted = append(notted, walk.NewAtomBool(!bool(v))) - case walk.ValueNumber: - notted = append(notted, walk.NewAtomBool(v == 0)) - case walk.ValueString: - notted = append(notted, walk.NewAtomBool(len(v) == 0)) + case walk.NullScalar: + notted = append(notted, walk.BoolScalar(true)) + case walk.BoolScalar: + notted = append(notted, walk.BoolScalar(!bool(v))) + case walk.NumberScalar: + notted = append(notted, walk.BoolScalar(v == 0)) + case walk.StringStructure: + notted = append(notted, walk.BoolScalar(len(v) == 0)) default: return nil, errors.New("Tried to NOT non-boolean") } -- cgit v1.2.3