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]] +}  | 
