glox/interpreter.go

149 lines
2.8 KiB
Go
Raw Normal View History

2024-10-04 15:24:01 +03:00
package main
import (
"fmt"
"reflect"
)
type Interpreter struct{}
type RuntimeError struct {
token Token
msg string
}
func (re RuntimeError) Error() string {
return re.msg
}
func newInterpreter() *Interpreter {
return &Interpreter{}
}
func (i *Interpreter) evaluate(e Expr) any {
defer i.recover()
return e.accept(i)
}
func (i *Interpreter) recover() {
if err := recover(); err != nil {
pe, ok := err.(RuntimeError)
if !ok {
panic(err)
}
reportRuntimeError(pe.token, pe.msg)
hadRuntimeError = true
}
}
func (i *Interpreter) visitBinary(b *Binary) any {
left := i.evaluate(b.left)
right := i.evaluate(b.right)
switch b.op.typ {
case MINUS:
checkIfFloats(b.op, left, right)
return left.(float64) - right.(float64)
case SLASH:
checkIfFloats(b.op, left, right)
return left.(float64) / right.(float64)
case STAR:
checkIfFloats(b.op, left, right)
return left.(float64) * right.(float64)
case GREATER:
checkIfFloats(b.op, left, right)
return left.(float64) > right.(float64)
case LESS:
checkIfFloats(b.op, left, right)
return left.(float64) < right.(float64)
case GREATER_EQUAL:
checkIfFloats(b.op, left, right)
return left.(float64) >= right.(float64)
case LESS_EQUAL:
checkIfFloats(b.op, left, right)
return left.(float64) <= right.(float64)
case BANG_EQUAL:
return !reflect.DeepEqual(left, right)
case EQUAL_EQUAL:
return reflect.DeepEqual(left, right)
case PLUS:
if isFloats(left, right) {
return left.(float64) + right.(float64)
}
if isStrings(left, right) {
return left.(string) + right.(string)
}
}
panic(RuntimeError{b.op, fmt.Sprintf("Operands must be numbers or strings: %v %s %v", left, b.op.lexeme, right)})
return nil
}
func (i *Interpreter) visitLiteral(l *Literal) any {
return l.value
}
func (i *Interpreter) visitGrouping(g *Grouping) any {
return i.evaluate(g.expression)
}
func (i *Interpreter) visitUnary(u *Unary) any {
val := i.evaluate(u.right)
switch u.op.typ {
case MINUS:
checkIfFloat(u.op, val)
return -val.(float64)
case BANG:
return !isTruthy(val)
}
return nil
}
func checkIfFloat(op Token, val any) {
if _, ok := val.(float64); ok {
return
}
panic(RuntimeError{op, "value must ne a number."})
}
func checkIfFloats(op Token, a any, b any) {
if isFloats(a, b) {
return
}
panic(RuntimeError{op, fmt.Sprintf("Operands must be numbers: %v %s %v", a, op.lexeme, b)})
}
func isFloats(a any, b any) bool {
ltype := reflect.TypeOf(a)
rtype := reflect.TypeOf(b)
return ltype.Kind() == rtype.Kind() && ltype.Kind() == reflect.Float64
}
func isStrings(a any, b any) bool {
ltype := reflect.TypeOf(a)
rtype := reflect.TypeOf(b)
return ltype.Kind() == rtype.Kind() && ltype.Kind() == reflect.String
}
func isTruthy(val any) bool {
if val == nil {
return false
}
if boolean, ok := val.(bool); ok {
return boolean
}
return true
}