diff options
-rw-r--r-- | main.go | 30 | ||||
-rw-r--r-- | parser.go | 107 | ||||
-rw-r--r-- | scanner.go | 92 | ||||
-rw-r--r-- | token_string.go | 29 |
4 files changed, 258 insertions, 0 deletions
@@ -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]] +} |