aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.go30
-rw-r--r--parser.go107
-rw-r--r--scanner.go92
-rw-r--r--token_string.go29
4 files changed, 258 insertions, 0 deletions
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..ed75659
--- /dev/null
+++ b/main.go
@@ -0,0 +1,30 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+)
+
+func main() {
+ var err error
+ f := os.Stdin
+ if len(os.Args) > 1 {
+ f, err = os.Open(os.Args[1])
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+ }
+ sc := bufio.NewScanner(f)
+ for sc.Scan() {
+ s := sc.Bytes()
+ expr, err := Parse(s)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, "Parse:", err)
+ continue
+ }
+ n := expr.Eval()
+ fmt.Println(n.String())
+ }
+}
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())
+}
diff --git a/scanner.go b/scanner.go
new file mode 100644
index 0000000..a07981d
--- /dev/null
+++ b/scanner.go
@@ -0,0 +1,92 @@
+package main
+
+//go:generate stringer -type=Token
+type Token int
+
+const (
+ INVALID Token = iota
+ EOF
+ NUM
+ ADD
+ SUB
+ MUL
+ DIV
+ EXP
+)
+
+type scanner struct {
+ c byte
+ i int
+ buf []byte
+}
+
+func newScanner(s []byte) *scanner {
+ return &scanner{
+ buf: s,
+ }
+}
+
+func (s *scanner) read() bool {
+ if len(s.buf) <= s.i {
+ return false
+ }
+ s.c = s.buf[s.i]
+ s.i++
+ return true
+}
+
+func (s *scanner) unread() {
+ s.i--
+ s.c = s.buf[s.i]
+}
+
+// Pos returns the index of the next character
+func (s *scanner) Pos() int {
+ return s.i
+}
+
+func (s *scanner) Scan() (t Token, d []byte) {
+read:
+ ok := s.read()
+ if !ok {
+ return EOF, []byte{}
+ }
+ switch s.c {
+ case '+':
+ return ADD, nil
+ case '-':
+ return SUB, nil
+ case '*':
+ return MUL, nil
+ case '/':
+ return DIV, nil
+ case '^':
+ return EXP, nil
+ case ' ', '\n', '\t':
+ goto read
+ default:
+ if (s.c >= '0' && s.c <= '9') {
+ s.unread()
+ return s.scanNumber(false)
+ }
+ if s.c == '-' {
+ return s.scanNumber(true)
+ }
+ }
+ return INVALID, []byte{s.c}
+}
+
+func (s *scanner) scanNumber(neg bool) (t Token, d []byte) {
+ if neg {
+ d = append(d, '-')
+ }
+ for s.read() {
+ if (s.c >= '0' && s.c <= '9') {
+ d = append(d, s.c)
+ } else {
+ s.unread()
+ break
+ }
+ }
+ return NUM, d
+}
diff --git a/token_string.go b/token_string.go
new file mode 100644
index 0000000..a9dc2c5
--- /dev/null
+++ b/token_string.go
@@ -0,0 +1,29 @@
+// Code generated by "stringer -type=Token"; DO NOT EDIT.
+
+package main
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[INVALID-0]
+ _ = x[EOF-1]
+ _ = x[NUM-2]
+ _ = x[ADD-3]
+ _ = x[SUB-4]
+ _ = x[MUL-5]
+ _ = x[DIV-6]
+}
+
+const _Token_name = "INVALIDEOFNUMADDSUBMULDIV"
+
+var _Token_index = [...]uint8{0, 7, 10, 13, 16, 19, 22, 25}
+
+func (i Token) String() string {
+ if i < 0 || i >= Token(len(_Token_index)-1) {
+ return "Token(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _Token_name[_Token_index[i]:_Token_index[i+1]]
+}