Improve error messages
This commit is contained in:
parent
cef6cde8fa
commit
a4a3d05ddd
55
rpn/lexer.go
55
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
15
rpn/rpn.go
15
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
rpn/stack.go
12
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]
|
||||
|
|
|
@ -32,4 +32,5 @@ type Token struct {
|
|||
Type TokenType
|
||||
Operator Operator
|
||||
Value float64
|
||||
Original string
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue