summaryrefslogtreecommitdiff
path: root/calc/calc.go
diff options
context:
space:
mode:
Diffstat (limited to 'calc/calc.go')
-rw-r--r--calc/calc.go80
1 files changed, 80 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
+}