diff --git a/Makefile b/Makefile index 78cd83f..bf81b1a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ BINAY_NAME=glox TESTS_DIR=./tests -TEST_SET=. ./utils ./reporting ./token ./scanner ./expression ./astprinter ./cmd/ast +TEST_SET=. ./utils ./reporting ./token ./scanner ./expression ./astprinter ./interpreter ./cmd/ast build: go build -o glox . diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go new file mode 100644 index 0000000..b5749e4 --- /dev/null +++ b/interpreter/interpreter.go @@ -0,0 +1,196 @@ +package interpreter + +import ( + "errors" + "math" + "reflect" + + "github.com/ByteHunter/glox/expression" + "github.com/ByteHunter/glox/reporting" + "github.com/ByteHunter/glox/token" +) + +type Interpreter struct{} + +func NewInterpreter() *Interpreter { + return &Interpreter{} +} + +func (i *Interpreter) VisitBinaryExpression(expr *expression.Binary) any { + if expr.Left == nil { + reporting.LoxError( + expr.Operator.Line, + "Left operand expected to be an expression, nil found (InterpreterError)", + ) + return nil + } + if expr.Right == nil { + reporting.LoxError( + expr.Operator.Line, + "Right operand expected to be an expression, nil found (InterpreterError)", + ) + return nil + } + left := i.Evaluate(expr.Left) + right := i.Evaluate(expr.Right) + + switch expr.Operator.Type { + case token.GREATER: + l, r, err := i.parseTwoOperands(left, right) + if err != nil { + reporting.LoxError(expr.Operator.Line, err.Error()) + return nil + } + return l > r + case token.GREATER_EQUAL: + l, r, err := i.parseTwoOperands(left, right) + if err != nil { + reporting.LoxError(expr.Operator.Line, err.Error()) + return nil + } + return l >= r + case token.LESS: + l, r, err := i.parseTwoOperands(left, right) + if err != nil { + reporting.LoxError(expr.Operator.Line, err.Error()) + return nil + } + return l < r + case token.LESS_EQUAL: + l, r, err := i.parseTwoOperands(left, right) + if err != nil { + reporting.LoxError(expr.Operator.Line, err.Error()) + return nil + } + return l <= r + case token.BANQ_EQUAL: + return !i.isEqual(left, right) + case token.EQUAL_EQUAL: + return i.isEqual(left, right) + case token.MINUS: + l, r, err := i.parseTwoOperands(left, right) + if err != nil { + reporting.LoxError(expr.Operator.Line, err.Error()) + return nil + } + return l - r + case token.SLASH: + l, r, err := i.parseTwoOperands(left, right) + if err != nil { + reporting.LoxError(expr.Operator.Line, err.Error()) + return nil + } + return l / r + case token.STAR: + l, r, err := i.parseTwoOperands(left, right) + if err != nil { + reporting.LoxError(expr.Operator.Line, err.Error()) + return nil + } + return l * r + case token.PLUS: + left_type := reflect.TypeOf(left).String() + right_type := reflect.TypeOf(right).String() + if left_type == "int" && right_type == "int" { + return float64(left.(int)) + float64(right.(int)) + } + if left_type == "string" && right_type == "string" { + l := left.(string) + r := right.(string) + return l + r + } + + reporting.LoxError(expr.Operator.Line, "Incompatible types in PLUS operation (InterpreterError)") + return nil + } + + reporting.LoxError(expr.Operator.Line, "Unknown binary operator (InterpreterError)") + return nil +} + +func (i *Interpreter) VisitGroupingExpression(expr *expression.Grouping) any { + return i.Evaluate(expr.Expr) +} + +func (i *Interpreter) VisitLiteralExpression(expr *expression.Literal) any { + return expr.Value +} + +func (i *Interpreter) VisitUnaryExpression(expr *expression.Unary) any { + if expr.Right == nil { + reporting.LoxError(expr.Operator.Line, "Expected an expression, nil found (InterpreterError)") + return nil + } + right := i.Evaluate(expr.Right) + + switch expr.Operator.Type { + case token.BANG: + return !i.getBoolean(right) + case token.MINUS: + res, err := i.getFloat(right) + if err != nil { + reporting.LoxError(expr.Operator.Line, err.Error()) + return res + } + return -res + } + + reporting.LoxError(expr.Operator.Line, "Unknown unary operator (InterpreterError)") + return nil +} + +func (i *Interpreter) Evaluate(expr expression.Expression) any { + return expr.Accept(i) +} + +func (i *Interpreter) getFloat(v any) (float64, error) { + switch t := v.(type) { + case int: + return float64(t), nil + default: + return math.NaN(), errors.New("Cannot convert to float64, unexpected type (ConversionError)") + } +} + +func (i *Interpreter) parseTwoOperands(left, right any) (float64, float64, error) { + l, le := i.getFloat(left) + r, re := i.getFloat(right) + + if le != nil { + return l, r, le + } + if re != nil { + return l, r, re + } + + return l, r, nil +} + +func (i *Interpreter) getBoolean(v any) bool { + if v == nil || v == false { + return false + } + return true +} + +func (i *Interpreter) isEqual(a, b any) bool { + if a == nil && b == nil { + return true + } + if a == nil { + return false + } + atype := reflect.TypeOf(a).String() + btype := reflect.TypeOf(b).String() + + if atype != btype { + return false + } + + switch atype { + case "int", "string": + return a == b; + } + + return false +} diff --git a/interpreter/interpreter_test.go b/interpreter/interpreter_test.go new file mode 100644 index 0000000..7049073 --- /dev/null +++ b/interpreter/interpreter_test.go @@ -0,0 +1,539 @@ +package interpreter + +import ( + "fmt" + + "github.com/ByteHunter/glox/expression" + "github.com/ByteHunter/glox/token" +) + +// Evaluating Unary expressions + +func ExampleInterpreter_Evaluate_unary_nil() { + i := NewInterpreter() + expr := expression.NewUnary( + *token.NewToken(token.MINUS, "-", nil, 1), + nil, + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Expected an expression, nil found (InterpreterError) + // +} + +func ExampleInterpreter_Evaluate_unary_unkown_token() { + i := NewInterpreter() + expr := expression.NewUnary( + *token.NewToken(token.PLUS, "+", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Unknown unary operator (InterpreterError) + // +} + +func ExampleInterpreter_Evaluate_unary_minus_nan() { + i := NewInterpreter() + expr := expression.NewUnary( + *token.NewToken(token.MINUS, "-", nil, 1), + expression.NewLiteral(true), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Cannot convert to float64, unexpected type (ConversionError) + // NaN +} + +func ExampleInterpreter_Evaluate_unary_minus() { + i := NewInterpreter() + expr := expression.NewUnary( + *token.NewToken(token.MINUS, "-", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // -42 +} + +func ExampleInterpreter_Evaluate_unary_minus2() { + i := NewInterpreter() + expr := expression.NewUnary( + *token.NewToken(token.MINUS, "-", nil, 1), + expression.NewLiteral(-42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // 42 +} + +func ExampleInterpreter_Evaluate_unary_bang_true() { + i := NewInterpreter() + expr := expression.NewUnary( + *token.NewToken(token.BANG, "!", nil, 1), + expression.NewLiteral(true), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // false +} + +func ExampleInterpreter_Evaluate_unary_bang_false() { + i := NewInterpreter() + expr := expression.NewUnary( + *token.NewToken(token.BANG, "!", nil, 1), + expression.NewLiteral(false), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // true +} + +func ExampleInterpreter_Evaluate_unary_bang_other() { + i := NewInterpreter() + expr := expression.NewUnary( + *token.NewToken(token.BANG, "!", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // false +} + +// Evaluating Binary Expressions + +func ExampleInterpreter_Evaluate_binary_invalid_operator() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(42), + *token.NewToken(token.DOT, "-", nil, 1), + expression.NewLiteral(1), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Unknown binary operator (InterpreterError) + // +} + +func ExampleInterpreter_Evaluate_binary_missing_left_operand() { + i := NewInterpreter() + expr := expression.NewBinary( + nil, + *token.NewToken(token.MINUS, "-", nil, 1), + expression.NewLiteral(1), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Left operand expected to be an expression, nil found (InterpreterError) + // +} + +func ExampleInterpreter_Evaluate_binary_missing_right_operand() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(42), + *token.NewToken(token.MINUS, "-", nil, 1), + nil, + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Right operand expected to be an expression, nil found (InterpreterError) + // +} + +func ExampleInterpreter_Evaluate_binary_minus() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(43), + *token.NewToken(token.MINUS, "-", nil, 1), + expression.NewLiteral(1), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // 42 +} + +func ExampleInterpreter_Evaluate_binary_minus_error_left() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(43), + *token.NewToken(token.MINUS, "-", nil, 1), + expression.NewLiteral(nil), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Cannot convert to float64, unexpected type (ConversionError) + // +} + +func ExampleInterpreter_Evaluate_binary_minus_error_right() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(nil), + *token.NewToken(token.MINUS, "-", nil, 1), + expression.NewLiteral(1), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Cannot convert to float64, unexpected type (ConversionError) + // +} + +func ExampleInterpreter_Evaluate_binary_slash() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(42), + *token.NewToken(token.SLASH, "/", nil, 1), + expression.NewLiteral(2), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // 21 +} + +func ExampleInterpreter_Evaluate_binary_slash_error_left() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(nil), + *token.NewToken(token.SLASH, "/", nil, 1), + expression.NewLiteral(2), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Cannot convert to float64, unexpected type (ConversionError) + // +} + +func ExampleInterpreter_Evaluate_binary_slash_error_right() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(42), + *token.NewToken(token.SLASH, "/", nil, 1), + expression.NewLiteral(nil), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Cannot convert to float64, unexpected type (ConversionError) + // +} + +func ExampleInterpreter_Evaluate_binary_star() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(21), + *token.NewToken(token.STAR, "*", nil, 1), + expression.NewLiteral(2), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // 42 +} + +func ExampleInterpreter_Evaluate_binary_star_error_left() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(nil), + *token.NewToken(token.STAR, "*", nil, 1), + expression.NewLiteral(2), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Cannot convert to float64, unexpected type (ConversionError) + // +} + +func ExampleInterpreter_Evaluate_binary_star_error_right() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(21), + *token.NewToken(token.STAR, "*", nil, 1), + expression.NewLiteral(nil), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Cannot convert to float64, unexpected type (ConversionError) + // +} + +func ExampleInterpreter_Evaluate_binary_plus_numbers() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(42), + *token.NewToken(token.PLUS, "+", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // 84 +} + +func ExampleInterpreter_Evaluate_binary_plus_string() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral("hello "), + *token.NewToken(token.PLUS, "+", nil, 1), + expression.NewLiteral("world"), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // hello world +} + +func ExampleInterpreter_Evaluate_binary_plus_incompatible_types() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral("42"), + *token.NewToken(token.PLUS, "+", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Incompatible types in PLUS operation (InterpreterError) + // +} + +func ExampleInterpreter_Evaluate_binary_greater() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(43), + *token.NewToken(token.GREATER, ">", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // true +} + +func ExampleInterpreter_Evaluate_binary_greater_error() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(43), + *token.NewToken(token.GREATER, ">", nil, 1), + expression.NewLiteral(nil), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Cannot convert to float64, unexpected type (ConversionError) + // +} + +func ExampleInterpreter_Evaluate_binary_greater_equal() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(42), + *token.NewToken(token.GREATER_EQUAL, ">=", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // true +} + +func ExampleInterpreter_Evaluate_binary_greater_equal_error() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(42), + *token.NewToken(token.GREATER_EQUAL, ">=", nil, 1), + expression.NewLiteral(nil), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Cannot convert to float64, unexpected type (ConversionError) + // +} + +func ExampleInterpreter_Evaluate_binary_less() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(41), + *token.NewToken(token.LESS, "<", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // true +} + +func ExampleInterpreter_Evaluate_binary_less_error() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(41), + *token.NewToken(token.LESS, "<", nil, 1), + expression.NewLiteral(nil), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Cannot convert to float64, unexpected type (ConversionError) + // +} + +func ExampleInterpreter_Evaluate_binary_less_equal() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(42), + *token.NewToken(token.LESS_EQUAL, "<=", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // true +} + +func ExampleInterpreter_Evaluate_binary_less_equal_error() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(42), + *token.NewToken(token.LESS_EQUAL, "<=", nil, 1), + expression.NewLiteral(nil), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // [line 1] Error : Cannot convert to float64, unexpected type (ConversionError) + // +} + +func ExampleInterpreter_Evaluate_binary_bang_equal() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(43), + *token.NewToken(token.BANQ_EQUAL, "!=", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // true +} + +func ExampleInterpreter_Evaluate_binary_equal_equal() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(42), + *token.NewToken(token.EQUAL_EQUAL, "==", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // true +} + +func ExampleInterpreter_Evaluate_binary_equal_both_nil() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(nil), + *token.NewToken(token.EQUAL_EQUAL, "==", nil, 1), + expression.NewLiteral(nil), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // true +} + +func ExampleInterpreter_Evaluate_binary_equal_one_nil() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(nil), + *token.NewToken(token.EQUAL_EQUAL, "==", nil, 1), + expression.NewLiteral(42), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // false +} + +func ExampleInterpreter_Evaluate_binary_equal_not_same_types() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(42), + *token.NewToken(token.EQUAL_EQUAL, "==", nil, 1), + expression.NewLiteral(true), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // false +} + +func ExampleInterpreter_Evaluate_binary_equal_false() { + i := NewInterpreter() + expr := expression.NewBinary( + expression.NewLiteral(true), + *token.NewToken(token.EQUAL_EQUAL, "==", nil, 1), + expression.NewLiteral(false), + ) + result := i.Evaluate(expr) + fmt.Println(result) + + // Output: + // false +}