diff options
Diffstat (limited to 'calc')
-rw-r--r-- | calc/calc.go | 80 | ||||
-rw-r--r-- | calc/calc_test.go | 54 |
2 files changed, 134 insertions, 0 deletions
diff --git a/calc/calc.go b/calc/calc.go new file mode 100644 index 0000000..d24dcea --- /dev/null +++ b/calc/calc.go @@ -0,0 +1,80 @@ +package calc + +import ( + "errors" + "fmt" + + . "github.com/vektah/goparsify" +) + +var ( + value Parser + + sumOp = Chars("+-", 1, 1) + prodOp = Chars("/*", 1, 1) + + groupExpr = Map(And("(", sum, ")"), func(n Node) Node { + return Node{Result: n.Child[1].Result} + }) + + number = Map(NumberLit(), func(n Node) Node { + switch i := n.Result.(type) { + case int64: + return Node{Result: float64(i)} + case float64: + return Node{Result: i} + default: + panic(fmt.Errorf("unknown value %#v", i)) + } + }) + + sum = Map(And(prod, Kleene(And(sumOp, prod))), func(n Node) Node { + i := n.Child[0].Result.(float64) + + for _, op := range n.Child[1].Child { + switch op.Child[0].Token { + case "+": + i += op.Child[1].Result.(float64) + case "-": + i -= op.Child[1].Result.(float64) + } + } + + return Node{Result: i} + }) + + prod = Map(And(&value, Kleene(And(prodOp, &value))), func(n Node) Node { + i := n.Child[0].Result.(float64) + + for _, op := range n.Child[1].Child { + switch op.Child[0].Token { + case "/": + i /= op.Child[1].Result.(float64) + case "*": + i *= op.Child[1].Result.(float64) + } + } + + return Node{Result: i} + }) + + Y = Maybe(sum) +) + +func init() { + value = Any(number, groupExpr) +} + +func Calc(input string) (float64, error) { + result, remaining, err := ParseString(Y, input) + + if err != nil { + return 0, err + } + + if remaining != "" { + return result.(float64), errors.New("left unparsed: " + remaining) + } + + return result.(float64), nil +} diff --git a/calc/calc_test.go b/calc/calc_test.go new file mode 100644 index 0000000..13b2f5b --- /dev/null +++ b/calc/calc_test.go @@ -0,0 +1,54 @@ +package calc + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNumbers(t *testing.T) { + result, err := Calc(`1`) + require.NoError(t, err) + require.EqualValues(t, 1, result) +} + +func TestAddition(t *testing.T) { + result, err := Calc(`1+1`) + require.NoError(t, err) + require.EqualValues(t, 2, result) +} + +func TestSubtraction(t *testing.T) { + result, err := Calc(`1-1`) + require.NoError(t, err) + require.EqualValues(t, 0, result) +} + +func TestDivision(t *testing.T) { + result, err := Calc(`1/2`) + require.NoError(t, err) + require.EqualValues(t, .5, result) +} + +func TestMultiplication(t *testing.T) { + result, err := Calc(`1*2`) + require.NoError(t, err) + require.EqualValues(t, 2, result) +} + +func TestOrderOfOperations(t *testing.T) { + result, err := Calc(`1+10*2`) + require.NoError(t, err) + require.EqualValues(t, 21, result) +} +func TestParenthesis(t *testing.T) { + result, err := Calc(`(1+10)*2`) + require.NoError(t, err) + require.EqualValues(t, 22, result) +} + +func TestRecursive(t *testing.T) { + result, err := Calc(`(1+(2*(3-(4/(5)))))`) + require.NoError(t, err) + require.EqualValues(t, 5.4, result) +} |