Improve error messages

This commit is contained in:
Pavle Portic 2024-01-09 10:12:49 +01:00
parent cef6cde8fa
commit a4a3d05ddd
Signed by: TheEdgeOfRage
GPG Key ID: 66AD4BA646FBC0D2
4 changed files with 42 additions and 41 deletions

View File

@ -11,7 +11,7 @@ type Lexer struct {
input *Input input *Input
} }
func (l *Lexer) parseNumber() (float64, error) { func (l *Lexer) parseNumber(negative bool) (*Token, error) {
numStr := "" numStr := ""
char := l.input.NextChar() char := l.input.NextChar()
for { for {
@ -25,10 +25,14 @@ func (l *Lexer) parseNumber() (float64, error) {
num, err := strconv.ParseFloat(numStr, 64) num, err := strconv.ParseFloat(numStr, 64)
if err != nil { 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) { func (l *Lexer) parseWord() (*Token, error) {
@ -45,25 +49,25 @@ func (l *Lexer) parseWord() (*Token, error) {
switch word { switch word {
case "sqrt": case "sqrt":
return &Token{unaryOp, sqrt, 0}, nil return &Token{unaryOp, sqrt, 0, word}, nil
case "dec": case "dec":
return &Token{dec, 0, 0}, nil return &Token{dec, 0, 0, word}, nil
case "bin": case "bin":
return &Token{bin, 0, 0}, nil return &Token{bin, 0, 0, word}, nil
case "hex": case "hex":
return &Token{hex, 0, 0}, nil return &Token{hex, 0, 0, word}, nil
case "pi": case "pi":
return &Token{number, 0, math.Pi}, nil return &Token{number, 0, math.Pi, word}, nil
case "pop": case "pop":
return &Token{pop, 0, 0}, nil return &Token{pop, 0, 0, word}, nil
case "swap": case "swap":
return &Token{swap, 0, 0}, nil return &Token{swap, 0, 0, word}, nil
case "clr": case "clr":
return &Token{clr, 0, 0}, nil return &Token{clr, 0, 0, word}, nil
case "help": case "help":
return &Token{help, 0, 0}, nil return &Token{help, 0, 0, word}, nil
case "exit": case "exit":
return &Token{exit, 0, 0}, nil return &Token{exit, 0, 0, word}, nil
default: default:
return nil, fmt.Errorf("Unknown input: %s", word) 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) { func (l *Lexer) Parse(input string) ([]*Token, error) {
var err error var err error
var num float64
var token *Token var token *Token
l.input = NewInput(input) l.input = NewInput(input)
tokens := []*Token{} tokens := []*Token{}
@ -84,30 +87,29 @@ func (l *Lexer) Parse(input string) ([]*Token, error) {
switch char { switch char {
case '+': case '+':
l.input.Eat() l.input.Eat()
token = &Token{binaryOp, plus, 0} token = &Token{binaryOp, plus, 0, string(char)}
case '-': case '-':
char = l.input.Eat() char = l.input.Eat()
if unicode.IsNumber(char) { if unicode.IsNumber(char) {
num, err = l.parseNumber() token, err = l.parseNumber(true)
token = &Token{number, 0, -num}
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else { } else {
token = &Token{binaryOp, minus, 0} token = &Token{binaryOp, minus, 0, string(char)}
} }
case '*': case '*':
l.input.Eat() l.input.Eat()
token = &Token{binaryOp, multiply, 0} token = &Token{binaryOp, multiply, 0, string(char)}
case '/': case '/':
l.input.Eat() l.input.Eat()
token = &Token{binaryOp, divide, 0} token = &Token{binaryOp, divide, 0, string(char)}
case '%': case '%':
l.input.Eat() l.input.Eat()
token = &Token{binaryOp, mod, 0} token = &Token{binaryOp, mod, 0, string(char)}
case '^': case '^':
l.input.Eat() l.input.Eat()
token = &Token{binaryOp, power, 0} token = &Token{binaryOp, power, 0, string(char)}
case ' ': case ' ':
l.input.Eat() l.input.Eat()
continue continue
@ -115,24 +117,23 @@ func (l *Lexer) Parse(input string) ([]*Token, error) {
l.input.Eat() l.input.Eat()
continue continue
case 0x04: // Ctrl-D case 0x04: // Ctrl-D
token = &Token{exit, 0, 0} token = &Token{exit, 0, 0, "^D"}
case '?': case '?':
l.input.Eat() l.input.Eat()
token = &Token{help, 0, 0} token = &Token{help, 0, 0, string(char)}
default: default:
if unicode.IsNumber(char) { if unicode.IsNumber(char) {
num, err = l.parseNumber() token, err = l.parseNumber(false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
token = &Token{number, 0, num}
} else if unicode.IsLetter(char) { } else if unicode.IsLetter(char) {
token, err = l.parseWord() token, err = l.parseWord()
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else { } else {
return nil, fmt.Errorf("Unknown input: %c", char) return nil, fmt.Errorf("Invalid input: %c", char)
} }
} }

View File

@ -1,12 +1,10 @@
package rpn package rpn
import ( import (
"errors" "fmt"
"math" "math"
) )
var ErrExit = errors.New("exit")
type RPN struct { type RPN struct {
stack *Stack stack *Stack
lexer *Lexer lexer *Lexer
@ -62,10 +60,15 @@ func (r *RPN) Eval(input string) error {
if a == 0 { if a == 0 {
r.stack.Push(b) r.stack.Push(b)
r.stack.Push(a) 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) r.stack.Push(b / a)
case mod: 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))) r.stack.Push(float64(int64(b) % int64(a)))
case power: case power:
r.stack.Push(math.Pow(b, a)) r.stack.Push(math.Pow(b, a))
@ -94,9 +97,9 @@ func (r *RPN) Eval(input string) error {
case help: case help:
printHelp() printHelp()
case exit: case exit:
return ErrExit return fmt.Errorf("exit")
default: default:
return errors.New("Unknown operation") return fmt.Errorf("Operation not implemented: %s", token.Original)
} }
} }

View File

@ -1,15 +1,11 @@
package rpn package rpn
import ( import (
"errors"
"fmt" "fmt"
"math" "math"
) )
var ( var ErrNotEnoughValuesTmpl = "Need at least %d values on the stack to perform this operation"
ErrStackEmpty = errors.New("stack is empty")
ErrNotEnoughValues = errors.New("not enough values on stack")
)
type Stack struct { type Stack struct {
values []float64 values []float64
@ -22,7 +18,7 @@ func (s *Stack) Push(value float64) {
func (s *Stack) Pop() (float64, error) { func (s *Stack) Pop() (float64, error) {
count := s.Len() count := s.Len()
if count == 0 { if count == 0 {
return 0, ErrStackEmpty return 0, fmt.Errorf(ErrNotEnoughValuesTmpl, 1)
} }
value := s.values[count-1] value := s.values[count-1]
s.values = 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) { func (s *Stack) Pop2() (float64, float64, error) {
count := s.Len() count := s.Len()
if count < 2 { if count < 2 {
return 0, 0, ErrNotEnoughValues return 0, 0, fmt.Errorf(ErrNotEnoughValuesTmpl, 2)
} }
a := s.values[count-1] a := s.values[count-1]
b := s.values[count-2] b := s.values[count-2]
@ -47,7 +43,7 @@ func (s *Stack) Len() int {
func (s *Stack) Swap() error { func (s *Stack) Swap() error {
count := s.Len() count := s.Len()
if count < 2 { if count < 2 {
return ErrNotEnoughValues return fmt.Errorf(ErrNotEnoughValuesTmpl, 2)
} }
a := s.values[count-1] a := s.values[count-1]
b := s.values[count-2] b := s.values[count-2]

View File

@ -32,4 +32,5 @@ type Token struct {
Type TokenType Type TokenType
Operator Operator Operator Operator
Value float64 Value float64
Original string
} }