diff --git a/rpn/lexer.go b/rpn/lexer.go index 1a01c15..6dbcc88 100644 --- a/rpn/lexer.go +++ b/rpn/lexer.go @@ -11,7 +11,7 @@ type Lexer struct { input *Input } -func (l *Lexer) parseNumber() (float64, error) { +func (l *Lexer) parseNumber(negative bool) (*Token, error) { numStr := "" char := l.input.NextChar() for { @@ -25,10 +25,14 @@ func (l *Lexer) parseNumber() (float64, error) { num, err := strconv.ParseFloat(numStr, 64) if err != nil { - return 0, err + return nil, err + } + if negative { + numStr = "-" + numStr + num = -num } - return num, nil + return &Token{number, 0, num, numStr}, nil } func (l *Lexer) parseWord() (*Token, error) { @@ -45,25 +49,25 @@ func (l *Lexer) parseWord() (*Token, error) { switch word { case "sqrt": - return &Token{unaryOp, sqrt, 0}, nil + return &Token{unaryOp, sqrt, 0, word}, nil case "dec": - return &Token{dec, 0, 0}, nil + return &Token{dec, 0, 0, word}, nil case "bin": - return &Token{bin, 0, 0}, nil + return &Token{bin, 0, 0, word}, nil case "hex": - return &Token{hex, 0, 0}, nil + return &Token{hex, 0, 0, word}, nil case "pi": - return &Token{number, 0, math.Pi}, nil + return &Token{number, 0, math.Pi, word}, nil case "pop": - return &Token{pop, 0, 0}, nil + return &Token{pop, 0, 0, word}, nil case "swap": - return &Token{swap, 0, 0}, nil + return &Token{swap, 0, 0, word}, nil case "clr": - return &Token{clr, 0, 0}, nil + return &Token{clr, 0, 0, word}, nil case "help": - return &Token{help, 0, 0}, nil + return &Token{help, 0, 0, word}, nil case "exit": - return &Token{exit, 0, 0}, nil + return &Token{exit, 0, 0, word}, nil default: return nil, fmt.Errorf("Unknown input: %s", word) } @@ -71,7 +75,6 @@ func (l *Lexer) parseWord() (*Token, error) { func (l *Lexer) Parse(input string) ([]*Token, error) { var err error - var num float64 var token *Token l.input = NewInput(input) tokens := []*Token{} @@ -84,30 +87,29 @@ func (l *Lexer) Parse(input string) ([]*Token, error) { switch char { case '+': l.input.Eat() - token = &Token{binaryOp, plus, 0} + token = &Token{binaryOp, plus, 0, string(char)} case '-': char = l.input.Eat() if unicode.IsNumber(char) { - num, err = l.parseNumber() - token = &Token{number, 0, -num} + token, err = l.parseNumber(true) if err != nil { return nil, err } } else { - token = &Token{binaryOp, minus, 0} + token = &Token{binaryOp, minus, 0, string(char)} } case '*': l.input.Eat() - token = &Token{binaryOp, multiply, 0} + token = &Token{binaryOp, multiply, 0, string(char)} case '/': l.input.Eat() - token = &Token{binaryOp, divide, 0} + token = &Token{binaryOp, divide, 0, string(char)} case '%': l.input.Eat() - token = &Token{binaryOp, mod, 0} + token = &Token{binaryOp, mod, 0, string(char)} case '^': l.input.Eat() - token = &Token{binaryOp, power, 0} + token = &Token{binaryOp, power, 0, string(char)} case ' ': l.input.Eat() continue @@ -115,24 +117,23 @@ func (l *Lexer) Parse(input string) ([]*Token, error) { l.input.Eat() continue case 0x04: // Ctrl-D - token = &Token{exit, 0, 0} + token = &Token{exit, 0, 0, "^D"} case '?': l.input.Eat() - token = &Token{help, 0, 0} + token = &Token{help, 0, 0, string(char)} default: if unicode.IsNumber(char) { - num, err = l.parseNumber() + token, err = l.parseNumber(false) if err != nil { return nil, err } - token = &Token{number, 0, num} } else if unicode.IsLetter(char) { token, err = l.parseWord() if err != nil { return nil, err } } else { - return nil, fmt.Errorf("Unknown input: %c", char) + return nil, fmt.Errorf("Invalid input: %c", char) } } diff --git a/rpn/rpn.go b/rpn/rpn.go index 558c8d5..0b56203 100644 --- a/rpn/rpn.go +++ b/rpn/rpn.go @@ -1,12 +1,10 @@ package rpn import ( - "errors" + "fmt" "math" ) -var ErrExit = errors.New("exit") - type RPN struct { stack *Stack lexer *Lexer @@ -62,10 +60,15 @@ func (r *RPN) Eval(input string) error { if a == 0 { r.stack.Push(b) r.stack.Push(a) - return errors.New("can't divide by zero") + return fmt.Errorf("Can't divide by zero") } r.stack.Push(b / a) case mod: + if a == 0 { + r.stack.Push(b) + r.stack.Push(a) + return fmt.Errorf("Can't divide by zero") + } r.stack.Push(float64(int64(b) % int64(a))) case power: r.stack.Push(math.Pow(b, a)) @@ -94,9 +97,9 @@ func (r *RPN) Eval(input string) error { case help: printHelp() case exit: - return ErrExit + return fmt.Errorf("exit") default: - return errors.New("Unknown operation") + return fmt.Errorf("Operation not implemented: %s", token.Original) } } diff --git a/rpn/stack.go b/rpn/stack.go index 3183349..c937c14 100644 --- a/rpn/stack.go +++ b/rpn/stack.go @@ -1,15 +1,11 @@ package rpn import ( - "errors" "fmt" "math" ) -var ( - ErrStackEmpty = errors.New("stack is empty") - ErrNotEnoughValues = errors.New("not enough values on stack") -) +var ErrNotEnoughValuesTmpl = "Need at least %d values on the stack to perform this operation" type Stack struct { values []float64 @@ -22,7 +18,7 @@ func (s *Stack) Push(value float64) { func (s *Stack) Pop() (float64, error) { count := s.Len() if count == 0 { - return 0, ErrStackEmpty + return 0, fmt.Errorf(ErrNotEnoughValuesTmpl, 1) } value := s.values[count-1] s.values = s.values[:count-1] @@ -32,7 +28,7 @@ func (s *Stack) Pop() (float64, error) { func (s *Stack) Pop2() (float64, float64, error) { count := s.Len() if count < 2 { - return 0, 0, ErrNotEnoughValues + return 0, 0, fmt.Errorf(ErrNotEnoughValuesTmpl, 2) } a := s.values[count-1] b := s.values[count-2] @@ -47,7 +43,7 @@ func (s *Stack) Len() int { func (s *Stack) Swap() error { count := s.Len() if count < 2 { - return ErrNotEnoughValues + return fmt.Errorf(ErrNotEnoughValuesTmpl, 2) } a := s.values[count-1] b := s.values[count-2] diff --git a/rpn/token.go b/rpn/token.go index 1d0623c..d6d899f 100644 --- a/rpn/token.go +++ b/rpn/token.go @@ -32,4 +32,5 @@ type Token struct { Type TokenType Operator Operator Value float64 + Original string }