aboutsummaryrefslogtreecommitdiff
path: root/parser.go
diff options
context:
space:
mode:
Diffstat (limited to 'parser.go')
-rw-r--r--parser.go107
1 files changed, 107 insertions, 0 deletions
diff --git a/parser.go b/parser.go
new file mode 100644
index 0000000..014624f
--- /dev/null
+++ b/parser.go
@@ -0,0 +1,107 @@
+package main
+
+import (
+ "fmt"
+ "math/big"
+)
+
+type Op int
+
+const (
+ OpInvalid Op = iota
+ OpAdd
+ OpSub
+ OpMul
+ OpDiv
+ OpExp
+)
+
+type Expr interface {
+ Eval() *big.Int
+}
+
+type NumExpr struct {
+ N *big.Int
+}
+
+func (n *NumExpr) Eval() *big.Int {
+ return n.N
+}
+
+type NopExpr struct {
+}
+
+func (e *NopExpr) Eval() *big.Int {
+ return &big.Int{}
+}
+
+type OpExpr struct {
+ Op Op
+ L Expr
+ R Expr
+}
+
+func (o *OpExpr) Eval() *big.Int {
+ l := o.L.Eval()
+ r := o.R.Eval()
+ n := new(big.Int)
+ switch o.Op {
+ case OpAdd:
+ n.Add(l, r)
+ case OpSub:
+ n.Sub(l, r)
+ case OpMul:
+ n.Mul(l, r)
+ case OpDiv:
+ n.Div(l, r)
+ case OpExp:
+ n.Exp(l, r, nil)
+ }
+ return n
+}
+
+func Parse(d []byte) (e Expr, err error) {
+ sc := newScanner(d)
+ t, d := sc.Scan()
+ switch t {
+ case NUM:
+ s := string(d)
+ n, ok := big.NewInt(0).SetString(s, 10)
+ if !ok {
+ return nil, fmt.Errorf("invalid number %s at %d", s, sc.Pos())
+ }
+ l := &NumExpr{N: n}
+ var op Op
+ t, d = sc.Scan()
+ switch t {
+ case EOF:
+ return l, nil
+ case ADD:
+ op = OpAdd
+ case SUB:
+ op = OpSub
+ case MUL:
+ op = OpMul
+ case DIV:
+ op = OpDiv
+ case EXP:
+ op = OpExp
+ default:
+ return nil, fmt.Errorf("expected operator at %d", sc.Pos())
+ }
+ t, d = sc.Scan()
+ if t != NUM {
+ return nil, fmt.Errorf("expected number at %d", sc.Pos())
+ }
+ s = string(d)
+ n2, ok := big.NewInt(0).SetString(s, 10)
+ if !ok {
+ return nil, fmt.Errorf("invalid nummber %s at %d", s, sc.Pos())
+ }
+ r := &NumExpr{N: n2}
+ return &OpExpr{Op: op, L: l, R: r}, nil
+ case EOF:
+ return &NopExpr{}, nil
+ }
+ return nil, fmt.Errorf("invalid token %s at index %d", t, sc.Pos())
+}