├── tests ├── integration │ ├── empty_file.lox │ ├── comments │ │ ├── only_line_comment.lox │ │ ├── only_line_comment_and_line.lox │ │ ├── line_at_eof.lox │ │ └── unicode.lox │ ├── nil │ │ └── literal.lox │ ├── helloworld.lox │ ├── variable │ │ ├── uninitialized.lox │ │ ├── redeclare_global.lox │ │ ├── redefine_global.lox │ │ ├── use_global_in_initializer.lox │ │ ├── use_nil_as_var.lox │ │ ├── in_nested_block.lox │ │ ├── undefined_global.lox │ │ ├── use_false_as_var.lox │ │ ├── use_this_as_var.lox │ │ ├── unreached_undefined.lox │ │ ├── undefined_local.lox │ │ ├── collide_with_parameter.lox │ │ ├── duplicate_local.lox │ │ ├── shadow_global.lox │ │ ├── duplicate_parameter.lox │ │ ├── use_local_in_initializer.lox │ │ ├── shadow_and_local.lox │ │ ├── shadow_local.lox │ │ ├── local_from_method.lox │ │ ├── scope_reuse_in_different_blocks.lox │ │ ├── early_bound.lox │ │ └── in_middle_of_block.lox │ ├── class │ │ ├── empty.lox │ │ ├── inherit_self.lox │ │ ├── local_inherit_other.lox │ │ ├── reference_self.lox │ │ ├── local_inherit_self.lox │ │ ├── local_reference_self.lox │ │ └── inherited_method.lox │ ├── function │ │ ├── empty_body.lox │ │ ├── print.lox │ │ ├── missing_arguments.lox │ │ ├── recursion.lox │ │ ├── extra_arguments.lox │ │ ├── body_must_be_block.lox │ │ ├── local_recursion.lox │ │ ├── missing_comma_in_parameters.lox │ │ ├── mutual_recursion.lox │ │ ├── local_mutual_recursion.lox │ │ ├── nested_call_with_arguments.lox │ │ ├── parameters.lox │ │ ├── too_many_parameters.lox │ │ └── too_many_arguments.lox │ ├── call │ │ ├── nil.lox │ │ ├── num.lox │ │ ├── bool.lox │ │ ├── string.lox │ │ └── object.lox │ ├── number │ │ ├── leading_dot.lox │ │ ├── trailing_dot.lox │ │ ├── decimal_point_at_eof.lox │ │ ├── literals.lox │ │ └── nan_equality.lox │ ├── operator │ │ ├── subtract.lox │ │ ├── negate_nonnum.lox │ │ ├── add.lox │ │ ├── divide_nonnum_num.lox │ │ ├── divide_num_nonnum.lox │ │ ├── greater_nonnum_num.lox │ │ ├── greater_num_nonnum.lox │ │ ├── less_nonnum_num.lox │ │ ├── less_num_nonnum.lox │ │ ├── multiply.lox │ │ ├── divide.lox │ │ ├── multiply_nonnum_num.lox │ │ ├── multiply_num_nonnum.lox │ │ ├── subtract_nonnum_num.lox │ │ ├── subtract_num_nonnum.lox │ │ ├── greater_or_equal_nonnum_num.lox │ │ ├── greater_or_equal_num_nonnum.lox │ │ ├── less_or_equal_nonnum_num.lox │ │ ├── less_or_equal_num_nonnum.lox │ │ ├── add_nil_nil.lox │ │ ├── add_num_nil.lox │ │ ├── add_bool_nil.lox │ │ ├── add_bool_num.lox │ │ ├── add_bool_string.lox │ │ ├── add_string_nil.lox │ │ ├── negate.lox │ │ ├── not_class.lox │ │ ├── equals_method.lox │ │ ├── not.lox │ │ ├── equals.lox │ │ ├── equals_class.lox │ │ ├── not_equals.lox │ │ └── comparison.lox │ ├── field │ │ ├── get_on_nil.lox │ │ ├── get_on_num.lox │ │ ├── get_on_bool.lox │ │ ├── get_on_string.lox │ │ ├── set_on_bool.lox │ │ ├── set_on_nil.lox │ │ ├── set_on_num.lox │ │ ├── set_on_string.lox │ │ ├── get_on_class.lox │ │ ├── set_on_class.lox │ │ ├── get_on_function.lox │ │ ├── set_evaluation_order.lox │ │ ├── set_on_function.lox │ │ ├── undefined.lox │ │ ├── call_nonfunction_field.lox │ │ ├── method.lox │ │ ├── call_function_field.lox │ │ ├── on_instance.lox │ │ ├── method_binds_this.lox │ │ ├── get_and_set_method.lox │ │ └── many.lox │ ├── print │ │ └── missing_argument.lox │ ├── for │ │ ├── var_in_body.lox │ │ ├── class_in_body.lox │ │ ├── fun_in_body.lox │ │ ├── statement_increment.lox │ │ ├── return_inside.lox │ │ ├── statement_condition.lox │ │ ├── statement_initializer.lox │ │ ├── return_closure.lox │ │ ├── closure_in_body.lox │ │ ├── scope.lox │ │ └── syntax.lox │ ├── if │ │ ├── var_in_then.lox │ │ ├── class_in_then.lox │ │ ├── fun_in_then.lox │ │ ├── var_in_else.lox │ │ ├── fun_in_else.lox │ │ ├── class_in_else.lox │ │ ├── dangling_else.lox │ │ ├── else.lox │ │ ├── truth.lox │ │ └── if.lox │ ├── this │ │ ├── this_at_top_level.lox │ │ ├── this_in_top_level_function.lox │ │ ├── this_in_method.lox │ │ ├── closure.lox │ │ ├── nested_closure.lox │ │ └── nested_class.lox │ ├── regression │ │ ├── 394.lox │ │ └── 40.lox │ ├── return │ │ ├── at_top_level.lox │ │ ├── after_if.lox │ │ ├── after_while.lox │ │ ├── in_function.lox │ │ ├── after_else.lox │ │ ├── return_nil_if_no_value.lox │ │ └── in_method.lox │ ├── string │ │ ├── unterminated.lox │ │ ├── multiline.lox │ │ ├── literals.lox │ │ └── error_after_multiline.lox │ ├── while │ │ ├── var_in_body.lox │ │ ├── fun_in_body.lox │ │ ├── class_in_body.lox │ │ ├── return_inside.lox │ │ ├── return_closure.lox │ │ ├── closure_in_body.lox │ │ └── syntax.lox │ ├── assignment │ │ ├── grouping.lox │ │ ├── undefined.lox │ │ ├── prefix_operator.lox │ │ ├── infix_operator.lox │ │ ├── to_this.lox │ │ ├── syntax.lox │ │ ├── global.lox │ │ ├── associativity.lox │ │ └── local.lox │ ├── constructor │ ├── method │ │ ├── empty_block.lox │ │ ├── not_found.lox │ │ ├── print_bound_method.lox │ │ ├── missing_arguments.lox │ │ ├── refer_to_name.lox │ │ ├── extra_arguments.lox │ │ ├── arity.lox │ │ ├── too_many_parameters.lox │ │ └── too_many_arguments.lox │ ├── bool │ │ ├── not.lox │ │ └── equality.lox │ ├── inheritance │ │ ├── inherit_from_nil.lox │ │ ├── inherit_from_function.lox │ │ ├── inherit_from_number.lox │ │ ├── parenthesized_superclass.lox │ │ ├── constructor.lox │ │ ├── inherit_methods.lox │ │ └── set_fields_from_base_class.lox │ ├── super │ │ ├── super_in_top_level_function.lox │ │ ├── super_without_name.lox │ │ ├── super_without_dot.lox │ │ ├── super_at_top_level.lox │ │ ├── no_superclass_bind.lox │ │ ├── no_superclass_call.lox │ │ ├── parenthesized.lox │ │ ├── no_superclass_method.lox │ │ ├── indirectly_inherited.lox │ │ ├── missing_arguments.lox │ │ ├── call_other_method.lox │ │ ├── call_same_method.lox │ │ ├── this_in_superclass_method.lox │ │ ├── constructor.lox │ │ ├── super_in_inherited_method.lox │ │ ├── extra_arguments.lox │ │ ├── bound_method.lox │ │ ├── closure.lox │ │ ├── super_in_closure_in_inherited_method.lox │ │ └── reassign_superclass.lox │ ├── block │ │ ├── empty.lox │ │ └── scope.lox │ ├── closure │ │ ├── open_closure_in_function.lox │ │ ├── closed_closure_in_function.lox │ │ ├── close_over_function_parameter.lox │ │ ├── reference_closure_multiple_times.lox │ │ ├── close_over_method_parameter.lox │ │ ├── assign_to_shadowed_later.lox │ │ ├── shadow_closure_with_local.lox │ │ ├── reuse_closure_slot.lox │ │ ├── nested_closure.lox │ │ ├── unused_closure.lox │ │ ├── assign_to_closure.lox │ │ ├── close_over_later_variable.lox │ │ └── unused_later_closure.lox │ ├── unexpected_character.lox │ ├── logical_operator │ │ ├── or_truth.lox │ │ ├── and_truth.lox │ │ ├── and.lox │ │ └── or.lox │ ├── limit │ │ ├── stack_overflow.lox │ │ ├── no_reuse_constants.lox │ │ ├── too_many_constants.lox │ │ ├── too_many_locals.lox │ │ └── too_many_upvalues.lox │ └── precedence.lox ├── benchmarks │ ├── lox │ │ ├── fib.lox │ │ ├── arithmetic.lox │ │ ├── instantiation.lox │ │ ├── trees.lox │ │ ├── zoo.lox │ │ ├── equality.lox │ │ ├── invocation.lox │ │ ├── binary_trees.lox │ │ ├── method_call.lox │ │ └── properties.lox │ ├── python │ │ ├── fib.py │ │ ├── arithmetic.py │ │ ├── instantiation.py │ │ ├── trees.py │ │ ├── zoo.py │ │ ├── equality.py │ │ ├── binary_trees.py │ │ ├── method_call.py │ │ ├── invocation.py │ │ └── properties.py │ └── perl │ │ ├── fib.pl │ │ ├── arithmetic.pl │ │ ├── instantiation.pl │ │ ├── equality.pl │ │ ├── zoo.pl │ │ ├── trees.pl │ │ ├── invocation.pl │ │ ├── binary_trees.pl │ │ ├── method_call.pl │ │ └── properties.pl ├── manual │ ├── gc_test.lox │ └── test.lox └── integration.rs ├── .gitignore ├── src ├── error.rs ├── main.rs ├── objects.rs ├── scanner.rs ├── gc.rs └── chunk.rs ├── perf.sh ├── Cargo.toml ├── run_lox_benchmarks.py ├── LICENSE ├── Cargo.lock └── README.md /tests/integration/empty_file.lox: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integration/comments/only_line_comment.lox: -------------------------------------------------------------------------------- 1 | // comment -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .vscode 3 | .fleet 4 | perf.data 5 | -------------------------------------------------------------------------------- /tests/integration/nil/literal.lox: -------------------------------------------------------------------------------- 1 | print nil; // expect: nil 2 | -------------------------------------------------------------------------------- /tests/integration/comments/only_line_comment_and_line.lox: -------------------------------------------------------------------------------- 1 | // comment 2 | -------------------------------------------------------------------------------- /tests/integration/helloworld.lox: -------------------------------------------------------------------------------- 1 | print "Hello World"; // expect: Hello World -------------------------------------------------------------------------------- /tests/integration/comments/line_at_eof.lox: -------------------------------------------------------------------------------- 1 | print "ok"; // expect: ok 2 | // comment -------------------------------------------------------------------------------- /tests/integration/variable/uninitialized.lox: -------------------------------------------------------------------------------- 1 | var a; 2 | print a; // expect: nil 3 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | pub enum LoxError { 2 | CompileError, 3 | RuntimeError, 4 | } 5 | -------------------------------------------------------------------------------- /tests/integration/class/empty.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | print Foo; // expect: Foo 4 | -------------------------------------------------------------------------------- /tests/integration/function/empty_body.lox: -------------------------------------------------------------------------------- 1 | fun f() {} 2 | print f(); // expect: nil 3 | -------------------------------------------------------------------------------- /tests/integration/call/nil.lox: -------------------------------------------------------------------------------- 1 | nil(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /tests/integration/call/num.lox: -------------------------------------------------------------------------------- 1 | 123(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /tests/integration/number/leading_dot.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at '.': Expect expression. 2 | .123; 3 | -------------------------------------------------------------------------------- /tests/integration/operator/subtract.lox: -------------------------------------------------------------------------------- 1 | print 4 - 3; // expect: 1 2 | print 1.2 - 1.2; // expect: 0 3 | -------------------------------------------------------------------------------- /tests/integration/variable/redeclare_global.lox: -------------------------------------------------------------------------------- 1 | var a = "1"; 2 | var a; 3 | print a; // expect: nil 4 | -------------------------------------------------------------------------------- /tests/integration/call/bool.lox: -------------------------------------------------------------------------------- 1 | true(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /tests/integration/call/string.lox: -------------------------------------------------------------------------------- 1 | "str"(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_nil.lox: -------------------------------------------------------------------------------- 1 | nil.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_num.lox: -------------------------------------------------------------------------------- 1 | 123.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/negate_nonnum.lox: -------------------------------------------------------------------------------- 1 | -"s"; // expect runtime error: Operand must be a number. 2 | -------------------------------------------------------------------------------- /tests/integration/print/missing_argument.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at ';': Expect expression. 2 | print; 3 | -------------------------------------------------------------------------------- /tests/integration/variable/redefine_global.lox: -------------------------------------------------------------------------------- 1 | var a = "1"; 2 | var a = "2"; 3 | print a; // expect: 2 4 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_bool.lox: -------------------------------------------------------------------------------- 1 | true.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_string.lox: -------------------------------------------------------------------------------- 1 | "str".foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /tests/integration/for/var_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | for (;;) var foo; 3 | -------------------------------------------------------------------------------- /tests/integration/if/var_in_then.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | if (true) var foo; 3 | -------------------------------------------------------------------------------- /tests/integration/number/trailing_dot.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at ';': Expect property name after '.'. 2 | 123.; 3 | -------------------------------------------------------------------------------- /tests/integration/operator/add.lox: -------------------------------------------------------------------------------- 1 | print 123 + 456; // expect: 579 2 | print "str" + "ing"; // expect: string 3 | -------------------------------------------------------------------------------- /tests/integration/operator/divide_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" / 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/divide_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 / "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/greater_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" > 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/greater_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 > "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/less_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" < 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/less_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 < "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/multiply.lox: -------------------------------------------------------------------------------- 1 | print 5 * 3; // expect: 15 2 | print 12.34 * 0.3; // expect: 3.702 3 | -------------------------------------------------------------------------------- /tests/integration/this/this_at_top_level.lox: -------------------------------------------------------------------------------- 1 | this; // Error at 'this': Can't use 'this' outside of a class. 2 | -------------------------------------------------------------------------------- /tests/integration/class/inherit_self.lox: -------------------------------------------------------------------------------- 1 | class Foo < Foo {} // Error at 'Foo': A class can't inherit from itself. 2 | -------------------------------------------------------------------------------- /tests/integration/field/set_on_bool.lox: -------------------------------------------------------------------------------- 1 | true.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /tests/integration/field/set_on_nil.lox: -------------------------------------------------------------------------------- 1 | nil.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /tests/integration/field/set_on_num.lox: -------------------------------------------------------------------------------- 1 | 123.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /tests/integration/for/class_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | for (;;) class Foo {} 3 | -------------------------------------------------------------------------------- /tests/integration/for/fun_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | for (;;) fun foo() {} 3 | -------------------------------------------------------------------------------- /tests/integration/if/class_in_then.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | if (true) class Foo {} 3 | -------------------------------------------------------------------------------- /tests/integration/if/fun_in_then.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | if (true) fun foo() {} 3 | -------------------------------------------------------------------------------- /tests/integration/number/decimal_point_at_eof.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at end: Expect property name after '.'. 2 | 123. -------------------------------------------------------------------------------- /tests/integration/operator/divide.lox: -------------------------------------------------------------------------------- 1 | print 8 / 2; // expect: 4 2 | print 12.34 / 12.34; // expect: 1 3 | -------------------------------------------------------------------------------- /tests/integration/operator/multiply_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" * 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/multiply_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 * "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/subtract_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" - 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/subtract_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 - "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/regression/394.lox: -------------------------------------------------------------------------------- 1 | { 2 | class A {} 3 | class B < A {} 4 | print B; // expect: B 5 | } 6 | -------------------------------------------------------------------------------- /tests/integration/return/at_top_level.lox: -------------------------------------------------------------------------------- 1 | return "wat"; // Error at 'return': Can't return from top-level code. 2 | -------------------------------------------------------------------------------- /tests/integration/string/unterminated.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error: Unterminated string. 2 | "this string has no close quote -------------------------------------------------------------------------------- /tests/integration/while/var_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | while (true) var foo; 3 | -------------------------------------------------------------------------------- /tests/integration/assignment/grouping.lox: -------------------------------------------------------------------------------- 1 | var a = "a"; 2 | (a) = "value"; // Error at '=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /tests/integration/assignment/undefined.lox: -------------------------------------------------------------------------------- 1 | unknown = "what"; // expect runtime error: Undefined variable 'unknown'. 2 | -------------------------------------------------------------------------------- /tests/integration/constructor/default.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | print foo; // expect: Foo instance 5 | -------------------------------------------------------------------------------- /tests/integration/field/set_on_string.lox: -------------------------------------------------------------------------------- 1 | "str".foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /tests/integration/if/var_in_else.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | if (true) "ok"; else var foo; 3 | -------------------------------------------------------------------------------- /tests/integration/method/empty_block.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | bar() {} 3 | } 4 | 5 | print Foo().bar(); // expect: nil 6 | -------------------------------------------------------------------------------- /tests/integration/operator/greater_or_equal_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" >= 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/greater_or_equal_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 >= "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/less_or_equal_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" <= 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/less_or_equal_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 <= "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/return/after_if.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | if (true) return "ok"; 3 | } 4 | 5 | print f(); // expect: ok 6 | -------------------------------------------------------------------------------- /tests/integration/variable/use_global_in_initializer.lox: -------------------------------------------------------------------------------- 1 | var a = "value"; 2 | var a = a; 3 | print a; // expect: value 4 | -------------------------------------------------------------------------------- /tests/integration/variable/use_nil_as_var.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'nil': Expect variable name. 2 | var nil = "value"; 3 | -------------------------------------------------------------------------------- /tests/integration/while/fun_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | while (true) fun foo() {} 3 | -------------------------------------------------------------------------------- /tests/integration/assignment/prefix_operator.lox: -------------------------------------------------------------------------------- 1 | var a = "a"; 2 | !a = "value"; // Error at '=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_class.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | Foo.bar; // expect runtime error: Only instances have properties. 3 | -------------------------------------------------------------------------------- /tests/integration/for/statement_increment.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at '{': Expect expression. 2 | for (var a = 1; a < 2; {}) {} 3 | -------------------------------------------------------------------------------- /tests/integration/if/fun_in_else.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | if (true) "ok"; else fun foo() {} 3 | -------------------------------------------------------------------------------- /tests/integration/operator/add_nil_nil.lox: -------------------------------------------------------------------------------- 1 | nil + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/add_num_nil.lox: -------------------------------------------------------------------------------- 1 | 1 + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/return/after_while.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | while (true) return "ok"; 3 | } 4 | 5 | print f(); // expect: ok 6 | -------------------------------------------------------------------------------- /tests/integration/variable/in_nested_block.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "outer"; 3 | { 4 | print a; // expect: outer 5 | } 6 | } -------------------------------------------------------------------------------- /tests/integration/variable/undefined_global.lox: -------------------------------------------------------------------------------- 1 | print notDefined; // expect runtime error: Undefined variable 'notDefined'. 2 | -------------------------------------------------------------------------------- /tests/integration/variable/use_false_as_var.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'false': Expect variable name. 2 | var false = "value"; 3 | -------------------------------------------------------------------------------- /tests/integration/variable/use_this_as_var.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'this': Expect variable name. 2 | var this = "value"; 3 | -------------------------------------------------------------------------------- /tests/integration/while/class_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | while (true) class Foo {} 3 | -------------------------------------------------------------------------------- /tests/integration/field/set_on_class.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | Foo.bar = "value"; // expect runtime error: Only instances have fields. 3 | -------------------------------------------------------------------------------- /tests/integration/function/print.lox: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | print foo; // expect: 3 | 4 | print clock; // expect: 5 | -------------------------------------------------------------------------------- /tests/integration/if/class_in_else.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | if (true) "ok"; else class Foo {} 3 | -------------------------------------------------------------------------------- /tests/integration/operator/add_bool_nil.lox: -------------------------------------------------------------------------------- 1 | true + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/add_bool_num.lox: -------------------------------------------------------------------------------- 1 | true + 123; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/add_bool_string.lox: -------------------------------------------------------------------------------- 1 | true + "s"; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/add_string_nil.lox: -------------------------------------------------------------------------------- 1 | "s" + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/negate.lox: -------------------------------------------------------------------------------- 1 | print -(3); // expect: -3 2 | print --(3); // expect: 3 3 | print ---(3); // expect: -3 4 | -------------------------------------------------------------------------------- /tests/integration/operator/not_class.lox: -------------------------------------------------------------------------------- 1 | class Bar {} 2 | print !Bar; // expect: false 3 | print !Bar(); // expect: false 4 | -------------------------------------------------------------------------------- /tests/integration/return/in_function.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | return "ok"; 3 | print "bad"; 4 | } 5 | 6 | print f(); // expect: ok 7 | -------------------------------------------------------------------------------- /tests/integration/string/multiline.lox: -------------------------------------------------------------------------------- 1 | var a = "1 2 | 2 3 | 3"; 4 | print a; 5 | // expect: 1 6 | // expect: 2 7 | // expect: 3 8 | -------------------------------------------------------------------------------- /tests/integration/variable/unreached_undefined.lox: -------------------------------------------------------------------------------- 1 | if (false) { 2 | print notDefined; 3 | } 4 | 5 | print "ok"; // expect: ok 6 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_function.lox: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | 3 | foo.bar; // expect runtime error: Only instances have properties. 4 | -------------------------------------------------------------------------------- /tests/integration/method/not_found.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | Foo().unknown(); // expect runtime error: Undefined property 'unknown'. 4 | -------------------------------------------------------------------------------- /tests/integration/return/after_else.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | if (false) "no"; else return "ok"; 3 | } 4 | 5 | print f(); // expect: ok 6 | -------------------------------------------------------------------------------- /tests/integration/bool/not.lox: -------------------------------------------------------------------------------- 1 | print !true; // expect: false 2 | print !false; // expect: true 3 | print !!true; // expect: true 4 | -------------------------------------------------------------------------------- /tests/integration/field/set_evaluation_order.lox: -------------------------------------------------------------------------------- 1 | undefined1.bar // expect runtime error: Undefined variable 'undefined1'. 2 | = undefined2; -------------------------------------------------------------------------------- /tests/integration/field/set_on_function.lox: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | 3 | foo.bar = "value"; // expect runtime error: Only instances have fields. 4 | -------------------------------------------------------------------------------- /tests/integration/function/missing_arguments.lox: -------------------------------------------------------------------------------- 1 | fun f(a, b) {} 2 | 3 | f(1); // expect runtime error: Expected 2 arguments but got 1. 4 | -------------------------------------------------------------------------------- /tests/integration/return/return_nil_if_no_value.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | return; 3 | print "bad"; 4 | } 5 | 6 | print f(); // expect: nil 7 | -------------------------------------------------------------------------------- /tests/integration/variable/undefined_local.lox: -------------------------------------------------------------------------------- 1 | { 2 | print notDefined; // expect runtime error: Undefined variable 'notDefined'. 3 | } 4 | -------------------------------------------------------------------------------- /tests/integration/assignment/infix_operator.lox: -------------------------------------------------------------------------------- 1 | var a = "a"; 2 | var b = "b"; 3 | a + b = "value"; // Error at '=': Invalid assignment target. 4 | -------------------------------------------------------------------------------- /tests/integration/field/undefined.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | var foo = Foo(); 3 | 4 | foo.bar; // expect runtime error: Undefined property 'bar'. 5 | -------------------------------------------------------------------------------- /tests/integration/inheritance/inherit_from_nil.lox: -------------------------------------------------------------------------------- 1 | var Nil = nil; 2 | class Foo < Nil {} // expect runtime error: Superclass must be a class. 3 | -------------------------------------------------------------------------------- /tests/integration/this/this_in_top_level_function.lox: -------------------------------------------------------------------------------- 1 | fun foo() { 2 | this; // Error at 'this': Can't use 'this' outside of a class. 3 | } 4 | -------------------------------------------------------------------------------- /tests/integration/call/object.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | foo(); // expect runtime error: Can only call functions and classes. 5 | -------------------------------------------------------------------------------- /tests/integration/super/super_in_top_level_function.lox: -------------------------------------------------------------------------------- 1 | super.bar(); // Error at 'super': Can't use 'super' outside of a class. 2 | fun foo() { 3 | } -------------------------------------------------------------------------------- /tests/integration/variable/collide_with_parameter.lox: -------------------------------------------------------------------------------- 1 | fun foo(a) { 2 | var a; // Error at 'a': Already variable with this name in this scope. 3 | } 4 | -------------------------------------------------------------------------------- /tests/integration/class/local_inherit_other.lox: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | fun f() { 4 | class B < A {} 5 | return B; 6 | } 7 | 8 | print f(); // expect: B 9 | -------------------------------------------------------------------------------- /tests/integration/constructor/default_arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(1, 2, 3); // expect runtime error: Expected 0 arguments but got 3. 4 | -------------------------------------------------------------------------------- /tests/integration/inheritance/inherit_from_function.lox: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | 3 | class Subclass < foo {} // expect runtime error: Superclass must be a class. 4 | -------------------------------------------------------------------------------- /tests/integration/inheritance/inherit_from_number.lox: -------------------------------------------------------------------------------- 1 | var Number = 123; 2 | class Foo < Number {} // expect runtime error: Superclass must be a class. 3 | -------------------------------------------------------------------------------- /tests/integration/method/print_bound_method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method() { } 3 | } 4 | var foo = Foo(); 5 | print foo.method; // expect: 6 | -------------------------------------------------------------------------------- /perf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | cargo build --release 5 | perf record --call-graph dwarf,16384 -e cpu-clock -F 997 target/release/loxido $@ -------------------------------------------------------------------------------- /tests/integration/block/empty.lox: -------------------------------------------------------------------------------- 1 | {} // By itself. 2 | 3 | // In a statement. 4 | if (true) {} 5 | if (false) {} else {} 6 | 7 | print "ok"; // expect: ok 8 | -------------------------------------------------------------------------------- /tests/integration/block/scope.lox: -------------------------------------------------------------------------------- 1 | var a = "outer"; 2 | 3 | { 4 | var a = "inner"; 5 | print a; // expect: inner 6 | } 7 | 8 | print a; // expect: outer 9 | -------------------------------------------------------------------------------- /tests/integration/class/reference_self.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | returnSelf() { 3 | return Foo; 4 | } 5 | } 6 | 7 | print Foo().returnSelf(); // expect: Foo 8 | -------------------------------------------------------------------------------- /tests/integration/inheritance/parenthesized_superclass.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | // [line 4] Error at '(': Expect superclass name. 4 | class Bar < (Foo) {} 5 | -------------------------------------------------------------------------------- /tests/integration/assignment/to_this.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | Foo() { 3 | this = "value"; // Error at '=': Invalid assignment target. 4 | } 5 | } 6 | 7 | Foo(); 8 | -------------------------------------------------------------------------------- /tests/integration/closure/open_closure_in_function.lox: -------------------------------------------------------------------------------- 1 | { 2 | var local = "local"; 3 | fun f() { 4 | print local; // expect: local 5 | } 6 | f(); 7 | } 8 | -------------------------------------------------------------------------------- /tests/integration/function/recursion.lox: -------------------------------------------------------------------------------- 1 | fun fib(n) { 2 | if (n < 2) return n; 3 | return fib(n - 1) + fib(n - 2); 4 | } 5 | 6 | print fib(8); // expect: 21 7 | -------------------------------------------------------------------------------- /tests/integration/unexpected_character.lox: -------------------------------------------------------------------------------- 1 | // [line 3] Error: Unexpected character. 2 | // [java line 3] Error at 'b': Expect ')' after arguments. 3 | foo(a | b); 4 | -------------------------------------------------------------------------------- /tests/integration/variable/duplicate_local.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "value"; 3 | var a = "other"; // Error at 'a': Already variable with this name in this scope. 4 | } 5 | -------------------------------------------------------------------------------- /tests/integration/variable/shadow_global.lox: -------------------------------------------------------------------------------- 1 | var a = "global"; 2 | { 3 | var a = "shadow"; 4 | print a; // expect: shadow 5 | } 6 | print a; // expect: global 7 | -------------------------------------------------------------------------------- /tests/integration/for/return_inside.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | for (;;) { 3 | var i = "i"; 4 | return i; 5 | } 6 | } 7 | 8 | print f(); 9 | // expect: i 10 | -------------------------------------------------------------------------------- /tests/integration/method/missing_arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method(a, b) {} 3 | } 4 | 5 | Foo().method(1); // expect runtime error: Expected 2 arguments but got 1. 6 | -------------------------------------------------------------------------------- /tests/integration/return/in_method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method() { 3 | return "ok"; 4 | print "bad"; 5 | } 6 | } 7 | 8 | print Foo().method(); // expect: ok 9 | -------------------------------------------------------------------------------- /tests/integration/this/this_in_method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | bar() { return this; } 3 | baz() { return "baz"; } 4 | } 5 | 6 | print Foo().bar().baz(); // expect: baz 7 | -------------------------------------------------------------------------------- /tests/integration/variable/duplicate_parameter.lox: -------------------------------------------------------------------------------- 1 | fun foo(arg, 2 | arg) { // Error at 'arg': Already variable with this name in this scope. 3 | "body"; 4 | } 5 | -------------------------------------------------------------------------------- /tests/integration/variable/use_local_in_initializer.lox: -------------------------------------------------------------------------------- 1 | var a = "outer"; 2 | { 3 | var a = a; // Error at 'a': Can't read local variable in its own initializer. 4 | } 5 | -------------------------------------------------------------------------------- /tests/integration/assignment/syntax.lox: -------------------------------------------------------------------------------- 1 | // Assignment on RHS of variable. 2 | var a = "before"; 3 | var c = a = "var"; 4 | print a; // expect: var 5 | print c; // expect: var 6 | -------------------------------------------------------------------------------- /tests/integration/constructor/missing_arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init(a, b) {} 3 | } 4 | 5 | var foo = Foo(1); // expect runtime error: Expected 2 arguments but got 1. 6 | -------------------------------------------------------------------------------- /tests/integration/while/return_inside.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | while (true) { 3 | var i = "i"; 4 | return i; 5 | } 6 | } 7 | 8 | print f(); 9 | // expect: i 10 | -------------------------------------------------------------------------------- /tests/integration/constructor/return_value.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init() { 3 | return "result"; // Error at 'return': Can't return a value from an initializer. 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/integration/function/extra_arguments.lox: -------------------------------------------------------------------------------- 1 | fun f(a, b) { 2 | print a; 3 | print b; 4 | } 5 | 6 | f(1, 2, 3, 4); // expect runtime error: Expected 2 arguments but got 4. 7 | -------------------------------------------------------------------------------- /tests/integration/string/literals.lox: -------------------------------------------------------------------------------- 1 | print "(" + "" + ")"; // expect: () 2 | print "a string"; // expect: a string 3 | 4 | // Non-ASCII. 5 | print "A~¶Þॐஃ"; // expect: A~¶Þॐஃ 6 | -------------------------------------------------------------------------------- /tests/integration/super/super_without_name.lox: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | class B < A { 4 | method() { 5 | super.; // Error at ';': Expect superclass method name. 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/integration/function/body_must_be_block.lox: -------------------------------------------------------------------------------- 1 | // [line 3] Error at '123': Expect '{' before function body. 2 | // [c line 4] Error at end: Expect '}' after block. 3 | fun f() 123; 4 | -------------------------------------------------------------------------------- /tests/integration/variable/shadow_and_local.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "outer"; 3 | { 4 | print a; // expect: outer 5 | var a = "inner"; 6 | print a; // expect: inner 7 | } 8 | } -------------------------------------------------------------------------------- /tests/integration/variable/shadow_local.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "local"; 3 | { 4 | var a = "shadow"; 5 | print a; // expect: shadow 6 | } 7 | print a; // expect: local 8 | } 9 | -------------------------------------------------------------------------------- /tests/integration/class/local_inherit_self.lox: -------------------------------------------------------------------------------- 1 | { 2 | class Foo < Foo {} // Error at 'Foo': A class can't inherit from itself. 3 | } 4 | // [c line 5] Error at end: Expect '}' after block. 5 | -------------------------------------------------------------------------------- /tests/integration/for/statement_condition.lox: -------------------------------------------------------------------------------- 1 | // [line 3] Error at '{': Expect expression. 2 | // [line 3] Error at ')': Expect ';' after expression. 3 | for (var a = 1; {}; a = a + 1) {} 4 | -------------------------------------------------------------------------------- /tests/integration/for/statement_initializer.lox: -------------------------------------------------------------------------------- 1 | // [line 3] Error at '{': Expect expression. 2 | // [line 3] Error at ')': Expect ';' after expression. 3 | for ({}; a < 2; a = a + 1) {} 4 | -------------------------------------------------------------------------------- /tests/integration/function/local_recursion.lox: -------------------------------------------------------------------------------- 1 | { 2 | fun fib(n) { 3 | if (n < 2) return n; 4 | return fib(n - 1) + fib(n - 2); 5 | } 6 | 7 | print fib(8); // expect: 21 8 | } 9 | -------------------------------------------------------------------------------- /tests/integration/method/refer_to_name.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method() { 3 | print method; // expect runtime error: Undefined variable 'method'. 4 | } 5 | } 6 | 7 | Foo().method(); 8 | -------------------------------------------------------------------------------- /tests/integration/super/super_without_dot.lox: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | class B < A { 4 | method() { 5 | // [line 6] Error at ';': Expect '.' after 'super'. 6 | super; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/integration/super/super_at_top_level.lox: -------------------------------------------------------------------------------- 1 | super.foo("bar"); // Error at 'super': Can't use 'super' outside of a class. 2 | super.foo; // Error at 'super': Can't use 'super' outside of a class. -------------------------------------------------------------------------------- /tests/benchmarks/lox/fib.lox: -------------------------------------------------------------------------------- 1 | fun fib(n) { 2 | if (n < 2) return n; 3 | return fib(n - 2) + fib(n - 1); 4 | } 5 | 6 | var start = clock(); 7 | print fib(35) == 9227465; 8 | print clock() - start; 9 | -------------------------------------------------------------------------------- /tests/integration/class/local_reference_self.lox: -------------------------------------------------------------------------------- 1 | { 2 | class Foo { 3 | returnSelf() { 4 | return Foo; 5 | } 6 | } 7 | 8 | print Foo().returnSelf(); // expect: Foo 9 | } 10 | -------------------------------------------------------------------------------- /tests/integration/field/call_nonfunction_field.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | foo.bar = "not fn"; 5 | 6 | foo.bar(); // expect runtime error: Can only call functions and classes. 7 | -------------------------------------------------------------------------------- /tests/integration/variable/local_from_method.lox: -------------------------------------------------------------------------------- 1 | var foo = "variable"; 2 | 3 | class Foo { 4 | method() { 5 | print foo; 6 | } 7 | } 8 | 9 | Foo().method(); // expect: variable 10 | -------------------------------------------------------------------------------- /tests/integration/for/return_closure.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | for (;;) { 3 | var i = "i"; 4 | fun g() { print i; } 5 | return g; 6 | } 7 | } 8 | 9 | var h = f(); 10 | h(); // expect: i 11 | -------------------------------------------------------------------------------- /tests/integration/function/missing_comma_in_parameters.lox: -------------------------------------------------------------------------------- 1 | // [line 3] Error at 'c': Expect ')' after parameters. 2 | // [c line 4] Error at end: Expect '}' after block. 3 | fun foo(a, b c, d, e, f) {} 4 | -------------------------------------------------------------------------------- /tests/integration/assignment/global.lox: -------------------------------------------------------------------------------- 1 | var a = "before"; 2 | print a; // expect: before 3 | 4 | a = "after"; 5 | print a; // expect: after 6 | 7 | print a = "arg"; // expect: arg 8 | print a; // expect: arg 9 | -------------------------------------------------------------------------------- /tests/integration/super/no_superclass_bind.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo() { 3 | super.doesNotExist; // Error at 'super': Can't use 'super' in a class with no superclass. 4 | } 5 | } 6 | 7 | Base().foo(); 8 | -------------------------------------------------------------------------------- /tests/integration/variable/scope_reuse_in_different_blocks.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "first"; 3 | print a; // expect: first 4 | } 5 | 6 | { 7 | var a = "second"; 8 | print a; // expect: second 9 | } 10 | -------------------------------------------------------------------------------- /tests/integration/closure/closed_closure_in_function.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | 3 | { 4 | var local = "local"; 5 | fun f_() { 6 | print local; 7 | } 8 | f = f_; 9 | } 10 | 11 | f(); // expect: local 12 | -------------------------------------------------------------------------------- /tests/integration/if/dangling_else.lox: -------------------------------------------------------------------------------- 1 | // A dangling else binds to the right-most if. 2 | if (true) if (false) print "bad"; else print "good"; // expect: good 3 | if (false) if (true) print "bad"; else print "bad"; 4 | -------------------------------------------------------------------------------- /tests/integration/super/no_superclass_call.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo() { 3 | super.doesNotExist(1); // Error at 'super': Can't use 'super' in a class with no superclass. 4 | } 5 | } 6 | 7 | Base().foo(); 8 | -------------------------------------------------------------------------------- /tests/integration/variable/early_bound.lox: -------------------------------------------------------------------------------- 1 | var a = "outer"; 2 | { 3 | fun foo() { 4 | print a; 5 | } 6 | 7 | foo(); // expect: outer 8 | var a = "inner"; 9 | foo(); // expect: outer 10 | } 11 | -------------------------------------------------------------------------------- /tests/integration/while/return_closure.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | while (true) { 3 | var i = "i"; 4 | fun g() { print i; } 5 | return g; 6 | } 7 | } 8 | 9 | var h = f(); 10 | h(); // expect: i 11 | -------------------------------------------------------------------------------- /tests/integration/closure/close_over_function_parameter.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | 3 | fun foo(param) { 4 | fun f_() { 5 | print param; 6 | } 7 | f = f_; 8 | } 9 | foo("param"); 10 | 11 | f(); // expect: param 12 | -------------------------------------------------------------------------------- /tests/integration/method/extra_arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method(a, b) { 3 | print a; 4 | print b; 5 | } 6 | } 7 | 8 | Foo().method(1, 2, 3, 4); // expect runtime error: Expected 2 arguments but got 4. 9 | -------------------------------------------------------------------------------- /tests/integration/constructor/extra_arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init(a, b) { 3 | this.a = a; 4 | this.b = b; 5 | } 6 | } 7 | 8 | var foo = Foo(1, 2, 3, 4); // expect runtime error: Expected 2 arguments but got 4. -------------------------------------------------------------------------------- /tests/integration/field/method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | bar(arg) { 3 | print arg; 4 | } 5 | } 6 | 7 | var bar = Foo().bar; 8 | print "got method"; // expect: got method 9 | bar("arg"); // expect: arg 10 | -------------------------------------------------------------------------------- /tests/integration/string/error_after_multiline.lox: -------------------------------------------------------------------------------- 1 | // Tests that we correctly track the line info across multiline strings. 2 | var a = "1 3 | 2 4 | 3 5 | "; 6 | 7 | err; // // expect runtime error: Undefined variable 'err'. -------------------------------------------------------------------------------- /tests/integration/super/parenthesized.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | method() {} 3 | } 4 | 5 | class B < A { 6 | method() { 7 | // [line 8] Error at ')': Expect '.' after 'super'. 8 | (super).method(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/integration/constructor/early_return.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init() { 3 | print "init"; 4 | return; 5 | print "nope"; 6 | } 7 | } 8 | 9 | var foo = Foo(); // expect: init 10 | print foo; // expect: Foo instance 11 | -------------------------------------------------------------------------------- /tests/integration/assignment/associativity.lox: -------------------------------------------------------------------------------- 1 | var a = "a"; 2 | var b = "b"; 3 | var c = "c"; 4 | 5 | // Assignment is right-associative. 6 | a = b = c; 7 | print a; // expect: c 8 | print b; // expect: c 9 | print c; // expect: c 10 | -------------------------------------------------------------------------------- /tests/integration/assignment/local.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "before"; 3 | print a; // expect: before 4 | 5 | a = "after"; 6 | print a; // expect: after 7 | 8 | print a = "arg"; // expect: arg 9 | print a; // expect: arg 10 | } 11 | -------------------------------------------------------------------------------- /tests/integration/closure/reference_closure_multiple_times.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | 3 | { 4 | var a = "a"; 5 | fun f_() { 6 | print a; 7 | print a; 8 | } 9 | f = f_; 10 | } 11 | 12 | f(); 13 | // expect: a 14 | // expect: a 15 | -------------------------------------------------------------------------------- /tests/integration/constructor/return_in_nested_function.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init() { 3 | fun init() { 4 | return "bar"; 5 | } 6 | print init(); // expect: bar 7 | } 8 | } 9 | 10 | print Foo(); // expect: Foo instance 11 | -------------------------------------------------------------------------------- /tests/integration/number/literals.lox: -------------------------------------------------------------------------------- 1 | print 123; // expect: 123 2 | print 987654; // expect: 987654 3 | print 0; // expect: 0 4 | print -0; // expect: -0 5 | 6 | print 123.456; // expect: 123.456 7 | print -0.001; // expect: -0.001 8 | -------------------------------------------------------------------------------- /tests/integration/number/nan_equality.lox: -------------------------------------------------------------------------------- 1 | var nan = 0/0; 2 | 3 | print nan == 0; // expect: false 4 | print nan != 1; // expect: true 5 | 6 | // NaN is not equal to self. 7 | print nan == nan; // expect: false 8 | print nan != nan; // expect: true 9 | -------------------------------------------------------------------------------- /tests/integration/super/no_superclass_method.lox: -------------------------------------------------------------------------------- 1 | class Base {} 2 | 3 | class Derived < Base { 4 | foo() { 5 | super.doesNotExist(1); // expect runtime error: Undefined property 'doesNotExist'. 6 | } 7 | } 8 | 9 | Derived().foo(); 10 | -------------------------------------------------------------------------------- /tests/integration/closure/close_over_method_parameter.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | 3 | class Foo { 4 | method(param) { 5 | fun f_() { 6 | print param; 7 | } 8 | f = f_; 9 | } 10 | } 11 | 12 | Foo().method("param"); 13 | f(); // expect: param 14 | -------------------------------------------------------------------------------- /tests/integration/constructor/arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init(a, b) { 3 | print "init"; // expect: init 4 | this.a = a; 5 | this.b = b; 6 | } 7 | } 8 | 9 | var foo = Foo(1, 2); 10 | print foo.a; // expect: 1 11 | print foo.b; // expect: 2 12 | -------------------------------------------------------------------------------- /tests/benchmarks/python/fib.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | def fib(n): 5 | if n < 2: 6 | return n 7 | return fib(n - 2) + fib(n - 1) 8 | 9 | 10 | start = time.process_time() 11 | print(fib(35) == 9227465) 12 | print(time.process_time() - start) 13 | -------------------------------------------------------------------------------- /tests/integration/closure/assign_to_shadowed_later.lox: -------------------------------------------------------------------------------- 1 | var a = "global"; 2 | 3 | { 4 | fun assign() { 5 | a = "assigned"; 6 | } 7 | 8 | var a = "inner"; 9 | assign(); 10 | print a; // expect: inner 11 | } 12 | 13 | print a; // expect: assigned 14 | -------------------------------------------------------------------------------- /tests/integration/inheritance/constructor.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | init(param) { 3 | this.field = param; 4 | } 5 | 6 | test() { 7 | print this.field; 8 | } 9 | } 10 | 11 | class B < A {} 12 | 13 | var b = B("value"); 14 | b.test(); // expect: value 15 | -------------------------------------------------------------------------------- /tests/integration/comments/unicode.lox: -------------------------------------------------------------------------------- 1 | // Unicode characters are allowed in comments. 2 | // 3 | // Latin 1 Supplement: £§¶ÜÞ 4 | // Latin Extended-A: ĐĦŋœ 5 | // Latin Extended-B: ƂƢƩǁ 6 | // Other stuff: ឃᢆ᯽₪ℜ↩⊗┺░ 7 | // Emoji: ☃☺♣ 8 | 9 | print "ok"; // expect: ok 10 | -------------------------------------------------------------------------------- /tests/integration/constructor/call_init_early_return.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init() { 3 | print "init"; 4 | return; 5 | print "nope"; 6 | } 7 | } 8 | 9 | var foo = Foo(); // expect: init 10 | print foo.init(); // expect: init 11 | // expect: Foo instance 12 | -------------------------------------------------------------------------------- /tests/integration/constructor/init_not_method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init(arg) { 3 | print "Foo.init(" + arg + ")"; 4 | this.field = "init"; 5 | } 6 | } 7 | 8 | fun init() { 9 | print "not initializer"; 10 | } 11 | 12 | init(); // expect: not initializer 13 | -------------------------------------------------------------------------------- /tests/integration/field/call_function_field.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | fun bar(a, b) { 4 | print "bar"; 5 | print a; 6 | print b; 7 | } 8 | 9 | var foo = Foo(); 10 | foo.bar = bar; 11 | 12 | foo.bar(1, 2); 13 | // expect: bar 14 | // expect: 1 15 | // expect: 2 16 | -------------------------------------------------------------------------------- /tests/integration/variable/in_middle_of_block.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "a"; 3 | print a; // expect: a 4 | var b = a + " b"; 5 | print b; // expect: a b 6 | var c = a + " c"; 7 | print c; // expect: a c 8 | var d = b + " d"; 9 | print d; // expect: a b d 10 | } 11 | -------------------------------------------------------------------------------- /tests/integration/field/on_instance.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | 5 | print foo.bar = "bar value"; // expect: bar value 6 | print foo.baz = "baz value"; // expect: baz value 7 | 8 | print foo.bar; // expect: bar value 9 | print foo.baz; // expect: baz value 10 | -------------------------------------------------------------------------------- /tests/benchmarks/lox/arithmetic.lox: -------------------------------------------------------------------------------- 1 | var start = clock(); 2 | 3 | var result = 0; 4 | for (var i = 0; i < 10000000; i = i + 1) { 5 | result = result + 11; 6 | result = result * 10; 7 | result = result - (result / 100) * 99; 8 | } 9 | 10 | print result; 11 | print clock() - start; -------------------------------------------------------------------------------- /tests/integration/logical_operator/or_truth.lox: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | print false or "ok"; // expect: ok 3 | print nil or "ok"; // expect: ok 4 | 5 | // Everything else is true. 6 | print true or "ok"; // expect: true 7 | print 0 or "ok"; // expect: 0 8 | print "s" or "ok"; // expect: s 9 | -------------------------------------------------------------------------------- /tests/integration/closure/shadow_closure_with_local.lox: -------------------------------------------------------------------------------- 1 | { 2 | var foo = "closure"; 3 | fun f() { 4 | { 5 | print foo; // expect: closure 6 | var foo = "shadow"; 7 | print foo; // expect: shadow 8 | } 9 | print foo; // expect: closure 10 | } 11 | f(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/integration/function/mutual_recursion.lox: -------------------------------------------------------------------------------- 1 | fun isEven(n) { 2 | if (n == 0) return true; 3 | return isOdd(n - 1); 4 | } 5 | 6 | fun isOdd(n) { 7 | if (n == 0) return false; 8 | return isEven(n - 1); 9 | } 10 | 11 | print isEven(4); // expect: true 12 | print isOdd(3); // expect: true 13 | -------------------------------------------------------------------------------- /tests/integration/if/else.lox: -------------------------------------------------------------------------------- 1 | // Evaluate the 'else' expression if the condition is false. 2 | if (true) print "good"; else print "bad"; // expect: good 3 | if (false) print "bad"; else print "good"; // expect: good 4 | 5 | // Allow block body. 6 | if (false) nil; else { print "block"; } // expect: block 7 | -------------------------------------------------------------------------------- /tests/integration/logical_operator/and_truth.lox: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | print false and "bad"; // expect: false 3 | print nil and "bad"; // expect: nil 4 | 5 | // Everything else is true. 6 | print true and "ok"; // expect: ok 7 | print 0 and "ok"; // expect: ok 8 | print "" and "ok"; // expect: ok 9 | -------------------------------------------------------------------------------- /tests/integration/this/closure.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | getClosure() { 3 | fun closure() { 4 | return this.toString(); 5 | } 6 | return closure; 7 | } 8 | 9 | toString() { return "Foo"; } 10 | } 11 | 12 | var closure = Foo().getClosure(); 13 | print closure(); // expect: Foo 14 | -------------------------------------------------------------------------------- /tests/benchmarks/python/arithmetic.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | start = time.process_time() 4 | 5 | result = 0 6 | for i in range(10000000): 7 | result = result + 11 8 | result = result * 10 9 | result = result - (result / 100) * 99 10 | 11 | print(result) 12 | print(time.process_time() - start) 13 | -------------------------------------------------------------------------------- /tests/integration/super/indirectly_inherited.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | foo() { 3 | print "A.foo()"; 4 | } 5 | } 6 | 7 | class B < A {} 8 | 9 | class C < B { 10 | foo() { 11 | print "C.foo()"; 12 | super.foo(); 13 | } 14 | } 15 | 16 | C().foo(); 17 | // expect: C.foo() 18 | // expect: A.foo() 19 | -------------------------------------------------------------------------------- /tests/integration/super/missing_arguments.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo(a, b) { 3 | print "Base.foo(" + a + ", " + b + ")"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | foo() { 9 | super.foo(1); // expect runtime error: Expected 2 arguments but got 1. 10 | } 11 | } 12 | 13 | Derived().foo(); 14 | -------------------------------------------------------------------------------- /tests/integration/super/call_other_method.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo() { 3 | print "Base.foo()"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | bar() { 9 | print "Derived.bar()"; 10 | super.foo(); 11 | } 12 | } 13 | 14 | Derived().bar(); 15 | // expect: Derived.bar() 16 | // expect: Base.foo() 17 | -------------------------------------------------------------------------------- /tests/integration/super/call_same_method.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo() { 3 | print "Base.foo()"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | foo() { 9 | print "Derived.foo()"; 10 | super.foo(); 11 | } 12 | } 13 | 14 | Derived().foo(); 15 | // expect: Derived.foo() 16 | // expect: Base.foo() 17 | -------------------------------------------------------------------------------- /tests/integration/function/local_mutual_recursion.lox: -------------------------------------------------------------------------------- 1 | { 2 | fun isEven(n) { 3 | if (n == 0) return true; 4 | return isOdd(n - 1); // expect runtime error: Undefined variable 'isOdd'. 5 | } 6 | 7 | fun isOdd(n) { 8 | if (n == 0) return false; 9 | return isEven(n - 1); 10 | } 11 | 12 | isEven(4); 13 | } -------------------------------------------------------------------------------- /tests/integration/function/nested_call_with_arguments.lox: -------------------------------------------------------------------------------- 1 | fun returnArg(arg) { 2 | return arg; 3 | } 4 | 5 | fun returnFunCallWithArg(func, arg) { 6 | return returnArg(func)(arg); 7 | } 8 | 9 | fun printArg(arg) { 10 | print arg; 11 | } 12 | 13 | returnFunCallWithArg(printArg, "hello world"); // expect: hello world 14 | -------------------------------------------------------------------------------- /tests/integration/if/truth.lox: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | if (false) print "bad"; else print "false"; // expect: false 3 | if (nil) print "bad"; else print "nil"; // expect: nil 4 | 5 | // Everything else is true. 6 | if (true) print true; // expect: true 7 | if (0) print 0; // expect: 0 8 | if ("") print "empty"; // expect: empty 9 | -------------------------------------------------------------------------------- /tests/benchmarks/perl/fib.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use v5.10; 4 | 5 | use Time::HiRes qw(clock); 6 | 7 | sub fib { 8 | my $n = shift; 9 | if ($n < 2) { 10 | return $n; 11 | } 12 | return fib($n - 2) + fib($n - 1); 13 | } 14 | 15 | my $start = clock(); 16 | say fib(35) == 9227465; 17 | say clock() - $start; 18 | -------------------------------------------------------------------------------- /tests/integration/if/if.lox: -------------------------------------------------------------------------------- 1 | // Evaluate the 'then' expression if the condition is true. 2 | if (true) print "good"; // expect: good 3 | if (false) print "bad"; 4 | 5 | // Allow block body. 6 | if (true) { print "block"; } // expect: block 7 | 8 | // Assignment in if condition. 9 | var a = false; 10 | if (a = true) print a; // expect: true 11 | -------------------------------------------------------------------------------- /tests/integration/super/this_in_superclass_method.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | init(a) { 3 | this.a = a; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | init(a, b) { 9 | super.init(a); 10 | this.b = b; 11 | } 12 | } 13 | 14 | var derived = Derived("a", "b"); 15 | print derived.a; // expect: a 16 | print derived.b; // expect: b 17 | -------------------------------------------------------------------------------- /tests/integration/super/constructor.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | init(a, b) { 3 | print "Base.init(" + a + ", " + b + ")"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | init() { 9 | print "Derived.init()"; 10 | super.init("a", "b"); 11 | } 12 | } 13 | 14 | Derived(); 15 | // expect: Derived.init() 16 | // expect: Base.init(a, b) 17 | -------------------------------------------------------------------------------- /tests/integration/super/super_in_inherited_method.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | say() { 3 | print "A"; 4 | } 5 | } 6 | 7 | class B < A { 8 | test() { 9 | super.say(); 10 | } 11 | 12 | say() { 13 | print "B"; 14 | } 15 | } 16 | 17 | class C < B { 18 | say() { 19 | print "C"; 20 | } 21 | } 22 | 23 | C().test(); // expect: A 24 | -------------------------------------------------------------------------------- /tests/integration/closure/reuse_closure_slot.lox: -------------------------------------------------------------------------------- 1 | { 2 | var f; 3 | 4 | { 5 | var a = "a"; 6 | fun f_() { print a; } 7 | f = f_; 8 | } 9 | 10 | { 11 | // Since a is out of scope, the local slot will be reused by b. Make sure 12 | // that f still closes over a. 13 | var b = "b"; 14 | f(); // expect: a 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/integration/while/closure_in_body.lox: -------------------------------------------------------------------------------- 1 | var f1; 2 | var f2; 3 | var f3; 4 | 5 | var i = 1; 6 | while (i < 4) { 7 | var j = i; 8 | fun f() { print j; } 9 | 10 | if (j == 1) f1 = f; 11 | else if (j == 2) f2 = f; 12 | else f3 = f; 13 | 14 | i = i + 1; 15 | } 16 | 17 | f1(); // expect: 1 18 | f2(); // expect: 2 19 | f3(); // expect: 3 20 | -------------------------------------------------------------------------------- /tests/integration/operator/equals_method.lox: -------------------------------------------------------------------------------- 1 | // Bound methods have identity equality. 2 | class Foo { 3 | method() {} 4 | } 5 | 6 | var foo = Foo(); 7 | var fooMethod = foo.method; 8 | 9 | // Same bound method. 10 | print fooMethod == fooMethod; // expect: true 11 | 12 | // Different closurizations. 13 | print foo.method == foo.method; // expect: false 14 | -------------------------------------------------------------------------------- /tests/integration/limit/stack_overflow.lox: -------------------------------------------------------------------------------- 1 | fun foo() { 2 | var a1; 3 | var a2; 4 | var a3; 5 | var a4; 6 | var a5; 7 | var a6; 8 | var a7; 9 | var a8; 10 | var a9; 11 | var a10; 12 | var a11; 13 | var a12; 14 | var a13; 15 | var a14; 16 | var a15; 17 | var a16; 18 | foo(); // expect runtime error: Stack overflow. 19 | } 20 | 21 | foo(); 22 | -------------------------------------------------------------------------------- /tests/integration/operator/not.lox: -------------------------------------------------------------------------------- 1 | print !true; // expect: false 2 | print !false; // expect: true 3 | print !!true; // expect: true 4 | 5 | print !123; // expect: false 6 | print !0; // expect: false 7 | 8 | print !nil; // expect: true 9 | 10 | print !""; // expect: false 11 | 12 | fun foo() {} 13 | print !foo; // expect: false 14 | -------------------------------------------------------------------------------- /tests/integration/super/extra_arguments.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo(a, b) { 3 | print "Base.foo(" + a + ", " + b + ")"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | foo() { 9 | print "Derived.foo()"; // expect: Derived.foo() 10 | super.foo("a", "b", "c", "d"); // expect runtime error: Expected 2 arguments but got 4. 11 | } 12 | } 13 | 14 | Derived().foo(); 15 | -------------------------------------------------------------------------------- /tests/benchmarks/perl/arithmetic.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use v5.10; 4 | 5 | use Time::HiRes qw(clock); 6 | 7 | my $start = clock(); 8 | 9 | my $result = 0; 10 | for (my $i = 0; $i < 10000000; $i++) { 11 | $result = $result + 11; 12 | $result = $result * 10; 13 | $result = $result - ($result / 100) * 99; 14 | } 15 | 16 | say $result; 17 | say clock() - $start; -------------------------------------------------------------------------------- /tests/integration/inheritance/inherit_methods.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | methodOnFoo() { print "foo"; } 3 | override() { print "foo"; } 4 | } 5 | 6 | class Bar < Foo { 7 | methodOnBar() { print "bar"; } 8 | override() { print "bar"; } 9 | } 10 | 11 | var bar = Bar(); 12 | bar.methodOnFoo(); // expect: foo 13 | bar.methodOnBar(); // expect: bar 14 | bar.override(); // expect: bar 15 | -------------------------------------------------------------------------------- /tests/integration/super/bound_method.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | method(arg) { 3 | print "A.method(" + arg + ")"; 4 | } 5 | } 6 | 7 | class B < A { 8 | getClosure() { 9 | return super.method; 10 | } 11 | 12 | method(arg) { 13 | print "B.method(" + arg + ")"; 14 | } 15 | } 16 | 17 | 18 | var closure = B().getClosure(); 19 | closure("arg"); // expect: A.method(arg) 20 | -------------------------------------------------------------------------------- /tests/integration/super/closure.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | toString() { return "Base"; } 3 | } 4 | 5 | class Derived < Base { 6 | getClosure() { 7 | fun closure() { 8 | return super.toString(); 9 | } 10 | return closure; 11 | } 12 | 13 | toString() { return "Derived"; } 14 | } 15 | 16 | var closure = Derived().getClosure(); 17 | print closure(); // expect: Base 18 | -------------------------------------------------------------------------------- /tests/integration/class/inherited_method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | inFoo() { 3 | print "in foo"; 4 | } 5 | } 6 | 7 | class Bar < Foo { 8 | inBar() { 9 | print "in bar"; 10 | } 11 | } 12 | 13 | class Baz < Bar { 14 | inBaz() { 15 | print "in baz"; 16 | } 17 | } 18 | 19 | var baz = Baz(); 20 | baz.inFoo(); // expect: in foo 21 | baz.inBar(); // expect: in bar 22 | baz.inBaz(); // expect: in baz 23 | -------------------------------------------------------------------------------- /tests/integration/this/nested_closure.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | getClosure() { 3 | fun f() { 4 | fun g() { 5 | fun h() { 6 | return this.toString(); 7 | } 8 | return h; 9 | } 10 | return g; 11 | } 12 | return f; 13 | } 14 | 15 | toString() { return "Foo"; } 16 | } 17 | 18 | var closure = Foo().getClosure(); 19 | print closure()()(); // expect: Foo 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "loxido" 3 | version = "0.1.0" 4 | authors = ["Manuel Cerón "] 5 | edition = "2021" 6 | 7 | [profile.release] 8 | debug = true 9 | 10 | [features] 11 | default = [] 12 | debug_log_gc = [] 13 | debug_stress_gc = [] 14 | debug_trace_execution = [] 15 | 16 | [dependencies] 17 | cpu-time = "1.0.0" 18 | 19 | [dev-dependencies] 20 | regex = "1.5.4" 21 | test-generator = "0.3.0" -------------------------------------------------------------------------------- /tests/integration/field/method_binds_this.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | sayName(a) { 3 | print this.name; 4 | print a; 5 | } 6 | } 7 | 8 | var foo1 = Foo(); 9 | foo1.name = "foo1"; 10 | 11 | var foo2 = Foo(); 12 | foo2.name = "foo2"; 13 | 14 | // Store the method reference on another object. 15 | foo2.fn = foo1.sayName; 16 | // Still retains original receiver. 17 | foo2.fn(1); 18 | // expect: foo1 19 | // expect: 1 20 | -------------------------------------------------------------------------------- /tests/integration/closure/nested_closure.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | 3 | fun f1() { 4 | var a = "a"; 5 | fun f2() { 6 | var b = "b"; 7 | fun f3() { 8 | var c = "c"; 9 | fun f4() { 10 | print a; 11 | print b; 12 | print c; 13 | } 14 | f = f4; 15 | } 16 | f3(); 17 | } 18 | f2(); 19 | } 20 | f1(); 21 | 22 | f(); 23 | // expect: a 24 | // expect: b 25 | // expect: c 26 | -------------------------------------------------------------------------------- /tests/integration/this/nested_class.lox: -------------------------------------------------------------------------------- 1 | class Outer { 2 | method() { 3 | print this; // expect: Outer instance 4 | 5 | fun f() { 6 | print this; // expect: Outer instance 7 | 8 | class Inner { 9 | method() { 10 | print this; // expect: Inner instance 11 | } 12 | } 13 | 14 | Inner().method(); 15 | } 16 | f(); 17 | } 18 | } 19 | 20 | Outer().method(); 21 | -------------------------------------------------------------------------------- /tests/integration/closure/unused_closure.lox: -------------------------------------------------------------------------------- 1 | // This is a regression test. There was a bug where the VM would try to close 2 | // an upvalue even if the upvalue was never created because the codepath for 3 | // the closure was not executed. 4 | 5 | { 6 | var a = "a"; 7 | if (false) { 8 | fun foo() { a; } 9 | } 10 | } 11 | 12 | // If we get here, we didn't segfault when a went out of scope. 13 | print "ok"; // expect: ok 14 | -------------------------------------------------------------------------------- /tests/integration/for/closure_in_body.lox: -------------------------------------------------------------------------------- 1 | var f1; 2 | var f2; 3 | var f3; 4 | 5 | for (var i = 1; i < 4; i = i + 1) { 6 | var j = i; 7 | fun f() { 8 | print i; 9 | print j; 10 | } 11 | 12 | if (j == 1) f1 = f; 13 | else if (j == 2) f2 = f; 14 | else f3 = f; 15 | } 16 | 17 | f1(); // expect: 4 18 | // expect: 1 19 | f2(); // expect: 4 20 | // expect: 2 21 | f3(); // expect: 4 22 | // expect: 3 23 | -------------------------------------------------------------------------------- /tests/integration/super/super_in_closure_in_inherited_method.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | say() { 3 | print "A"; 4 | } 5 | } 6 | 7 | class B < A { 8 | getClosure() { 9 | fun closure() { 10 | super.say(); 11 | } 12 | return closure; 13 | } 14 | 15 | say() { 16 | print "B"; 17 | } 18 | } 19 | 20 | class C < B { 21 | say() { 22 | print "C"; 23 | } 24 | } 25 | 26 | C().getClosure()(); // expect: A 27 | -------------------------------------------------------------------------------- /tests/integration/operator/equals.lox: -------------------------------------------------------------------------------- 1 | print nil == nil; // expect: true 2 | 3 | print true == true; // expect: true 4 | print true == false; // expect: false 5 | 6 | print 1 == 1; // expect: true 7 | print 1 == 2; // expect: false 8 | 9 | print "str" == "str"; // expect: true 10 | print "str" == "ing"; // expect: false 11 | 12 | print nil == false; // expect: false 13 | print false == 0; // expect: false 14 | print 0 == "0"; // expect: false 15 | -------------------------------------------------------------------------------- /tests/integration/constructor/call_init_explicitly.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init(arg) { 3 | print "Foo.init(" + arg + ")"; 4 | this.field = "init"; 5 | } 6 | } 7 | 8 | var foo = Foo("one"); // expect: Foo.init(one) 9 | foo.field = "field"; 10 | 11 | var foo2 = foo.init("two"); // expect: Foo.init(two) 12 | print foo2; // expect: Foo instance 13 | 14 | // Make sure init() doesn't create a fresh instance. 15 | print foo.field; // expect: init 16 | -------------------------------------------------------------------------------- /tests/integration/operator/equals_class.lox: -------------------------------------------------------------------------------- 1 | // Bound methods have identity equality. 2 | class Foo {} 3 | class Bar {} 4 | 5 | print Foo == Foo; // expect: true 6 | print Foo == Bar; // expect: false 7 | print Bar == Foo; // expect: false 8 | print Bar == Bar; // expect: true 9 | 10 | print Foo == "Foo"; // expect: false 11 | print Foo == nil; // expect: false 12 | print Foo == 123; // expect: false 13 | print Foo == true; // expect: false 14 | -------------------------------------------------------------------------------- /tests/integration/operator/not_equals.lox: -------------------------------------------------------------------------------- 1 | print nil != nil; // expect: false 2 | 3 | print true != true; // expect: false 4 | print true != false; // expect: true 5 | 6 | print 1 != 1; // expect: false 7 | print 1 != 2; // expect: true 8 | 9 | print "str" != "str"; // expect: false 10 | print "str" != "ing"; // expect: true 11 | 12 | print nil != false; // expect: true 13 | print false != 0; // expect: true 14 | print 0 != "0"; // expect: true 15 | -------------------------------------------------------------------------------- /tests/integration/while/syntax.lox: -------------------------------------------------------------------------------- 1 | // Single-expression body. 2 | var c = 0; 3 | while (c < 3) print c = c + 1; 4 | // expect: 1 5 | // expect: 2 6 | // expect: 3 7 | 8 | // Block body. 9 | var a = 0; 10 | while (a < 3) { 11 | print a; 12 | a = a + 1; 13 | } 14 | // expect: 0 15 | // expect: 1 16 | // expect: 2 17 | 18 | // Statement bodies. 19 | while (false) if (true) 1; else 2; 20 | while (false) while (true) 1; 21 | while (false) for (;;) 1; 22 | -------------------------------------------------------------------------------- /tests/integration/closure/assign_to_closure.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | var g; 3 | 4 | { 5 | var local = "local"; 6 | fun f_() { 7 | print local; 8 | local = "after f"; 9 | print local; 10 | } 11 | f = f_; 12 | 13 | fun g_() { 14 | print local; 15 | local = "after g"; 16 | print local; 17 | } 18 | g = g_; 19 | } 20 | 21 | f(); 22 | // expect: local 23 | // expect: after f 24 | 25 | g(); 26 | // expect: after f 27 | // expect: after g 28 | -------------------------------------------------------------------------------- /tests/integration/super/reassign_superclass.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | method() { 3 | print "Base.method()"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | method() { 9 | super.method(); 10 | } 11 | } 12 | 13 | class OtherBase { 14 | method() { 15 | print "OtherBase.method()"; 16 | } 17 | } 18 | 19 | var derived = Derived(); 20 | derived.method(); // expect: Base.method() 21 | Base = OtherBase; 22 | derived.method(); // expect: Base.method() 23 | -------------------------------------------------------------------------------- /tests/integration/regression/40.lox: -------------------------------------------------------------------------------- 1 | fun caller(g) { 2 | g(); 3 | // g should be a function, not nil. 4 | print g == nil; // expect: false 5 | } 6 | 7 | fun callCaller() { 8 | var capturedVar = "before"; 9 | var a = "a"; 10 | 11 | fun f() { 12 | // Commenting the next line out prevents the bug! 13 | capturedVar = "after"; 14 | 15 | // Returning anything also fixes it, even nil: 16 | //return nil; 17 | } 18 | 19 | caller(f); 20 | } 21 | 22 | callCaller(); 23 | -------------------------------------------------------------------------------- /tests/integration/closure/close_over_later_variable.lox: -------------------------------------------------------------------------------- 1 | // This is a regression test. There was a bug where if an upvalue for an 2 | // earlier local (here "a") was captured *after* a later one ("b"), then it 3 | // would crash because it walked to the end of the upvalue list (correct), but 4 | // then didn't handle not finding the variable. 5 | 6 | fun f() { 7 | var a = "a"; 8 | var b = "b"; 9 | fun g() { 10 | print b; // expect: b 11 | print a; // expect: a 12 | } 13 | g(); 14 | } 15 | f(); 16 | -------------------------------------------------------------------------------- /tests/integration/field/get_and_set_method.lox: -------------------------------------------------------------------------------- 1 | // Bound methods have identity equality. 2 | class Foo { 3 | method(a) { 4 | print "method"; 5 | print a; 6 | } 7 | other(a) { 8 | print "other"; 9 | print a; 10 | } 11 | } 12 | 13 | var foo = Foo(); 14 | var method = foo.method; 15 | 16 | // Setting a property shadows the instance method. 17 | foo.method = foo.other; 18 | foo.method(1); 19 | // expect: other 20 | // expect: 1 21 | 22 | // The old method handle still points to the original method. 23 | method(2); 24 | // expect: method 25 | // expect: 2 26 | -------------------------------------------------------------------------------- /tests/integration/logical_operator/and.lox: -------------------------------------------------------------------------------- 1 | // Note: These tests implicitly depend on ints being truthy. 2 | 3 | // Return the first non-true argument. 4 | print false and 1; // expect: false 5 | print true and 1; // expect: 1 6 | print 1 and 2 and false; // expect: false 7 | 8 | // Return the last argument if all are true. 9 | print 1 and true; // expect: true 10 | print 1 and 2 and 3; // expect: 3 11 | 12 | // Short-circuit at the first false argument. 13 | var a = "before"; 14 | var b = "before"; 15 | (a = true) and 16 | (b = false) and 17 | (a = "bad"); 18 | print a; // expect: true 19 | print b; // expect: false 20 | -------------------------------------------------------------------------------- /tests/integration/for/scope.lox: -------------------------------------------------------------------------------- 1 | { 2 | var i = "before"; 3 | 4 | // New variable is in inner scope. 5 | for (var i = 0; i < 1; i = i + 1) { 6 | print i; // expect: 0 7 | 8 | // Loop body is in second inner scope. 9 | var i = -1; 10 | print i; // expect: -1 11 | } 12 | } 13 | 14 | { 15 | // New variable shadows outer variable. 16 | for (var i = 0; i > 0; i = i + 1) {} 17 | 18 | // Goes out of scope after loop. 19 | var i = "after"; 20 | print i; // expect: after 21 | 22 | // Can reuse an existing variable. 23 | for (i = 0; i < 1; i = i + 1) { 24 | print i; // expect: 0 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/integration/logical_operator/or.lox: -------------------------------------------------------------------------------- 1 | // Note: These tests implicitly depend on ints being truthy. 2 | 3 | // Return the first true argument. 4 | print 1 or true; // expect: 1 5 | print false or 1; // expect: 1 6 | print false or false or true; // expect: true 7 | 8 | // Return the last argument if all are false. 9 | print false or false; // expect: false 10 | print false or false or false; // expect: false 11 | 12 | // Short-circuit at the first true argument. 13 | var a = "before"; 14 | var b = "before"; 15 | (a = false) or 16 | (b = true) or 17 | (a = "bad"); 18 | print a; // expect: false 19 | print b; // expect: true 20 | -------------------------------------------------------------------------------- /tests/manual/gc_test.lox: -------------------------------------------------------------------------------- 1 | fun test_interner() { 2 | // The interner hashmap should be weak and be cleaned when gc clean 3 | 4 | print "Testing gc interner..."; 5 | 6 | var s = "unique" + "string"; 7 | s = "other" + "thing"; 8 | s = " unique" + "string"; 9 | print s; 10 | } 11 | 12 | fun test_string_concat() { 13 | // String concatenation should not pop from the stack before concatenating 14 | 15 | print "Testing string concatenation"; 16 | 17 | var s = ("one" + "two") + ("three" + "four"); 18 | print s; 19 | } 20 | 21 | test_interner(); 22 | test_string_concat(); 23 | 24 | print "++++++++++++++END OF THE TEST+++++++++++++++++++++"; -------------------------------------------------------------------------------- /tests/benchmarks/lox/instantiation.lox: -------------------------------------------------------------------------------- 1 | // This benchmark stresses instance creation and initializer calling. 2 | 3 | class Foo { 4 | init() {} 5 | } 6 | 7 | var start = clock(); 8 | var i = 0; 9 | while (i < 500000) { 10 | Foo(); 11 | Foo(); 12 | Foo(); 13 | Foo(); 14 | Foo(); 15 | Foo(); 16 | Foo(); 17 | Foo(); 18 | Foo(); 19 | Foo(); 20 | Foo(); 21 | Foo(); 22 | Foo(); 23 | Foo(); 24 | Foo(); 25 | Foo(); 26 | Foo(); 27 | Foo(); 28 | Foo(); 29 | Foo(); 30 | Foo(); 31 | Foo(); 32 | Foo(); 33 | Foo(); 34 | Foo(); 35 | Foo(); 36 | Foo(); 37 | Foo(); 38 | Foo(); 39 | Foo(); 40 | i = i + 1; 41 | } 42 | 43 | print clock() - start; 44 | -------------------------------------------------------------------------------- /tests/integration/closure/unused_later_closure.lox: -------------------------------------------------------------------------------- 1 | // This is a regression test. When closing upvalues for discarded locals, it 2 | // wouldn't make sure it discarded the upvalue for the correct stack slot. 3 | // 4 | // Here we create two locals that can be closed over, but only the first one 5 | // actually is. When "b" goes out of scope, we need to make sure we don't 6 | // prematurely close "a". 7 | var closure; 8 | 9 | { 10 | var a = "a"; 11 | 12 | { 13 | var b = "b"; 14 | fun returnA() { 15 | return a; 16 | } 17 | 18 | closure = returnA; 19 | 20 | if (false) { 21 | fun returnB() { 22 | return b; 23 | } 24 | } 25 | } 26 | 27 | print closure(); // expect: a 28 | } 29 | -------------------------------------------------------------------------------- /tests/integration/inheritance/set_fields_from_base_class.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | foo(a, b) { 3 | this.field1 = a; 4 | this.field2 = b; 5 | } 6 | 7 | fooPrint() { 8 | print this.field1; 9 | print this.field2; 10 | } 11 | } 12 | 13 | class Bar < Foo { 14 | bar(a, b) { 15 | this.field1 = a; 16 | this.field2 = b; 17 | } 18 | 19 | barPrint() { 20 | print this.field1; 21 | print this.field2; 22 | } 23 | } 24 | 25 | var bar = Bar(); 26 | bar.foo("foo 1", "foo 2"); 27 | bar.fooPrint(); 28 | // expect: foo 1 29 | // expect: foo 2 30 | 31 | bar.bar("bar 1", "bar 2"); 32 | bar.barPrint(); 33 | // expect: bar 1 34 | // expect: bar 2 35 | 36 | bar.fooPrint(); 37 | // expect: bar 1 38 | // expect: bar 2 39 | -------------------------------------------------------------------------------- /tests/benchmarks/lox/trees.lox: -------------------------------------------------------------------------------- 1 | class Tree { 2 | init(depth) { 3 | this.depth = depth; 4 | if (depth > 0) { 5 | this.a = Tree(depth - 1); 6 | this.b = Tree(depth - 1); 7 | this.c = Tree(depth - 1); 8 | this.d = Tree(depth - 1); 9 | this.e = Tree(depth - 1); 10 | } 11 | } 12 | 13 | walk() { 14 | if (this.depth == 0) return 0; 15 | return this.depth 16 | + this.a.walk() 17 | + this.b.walk() 18 | + this.c.walk() 19 | + this.d.walk() 20 | + this.e.walk(); 21 | } 22 | } 23 | 24 | var tree = Tree(8); 25 | var start = clock(); 26 | for (var i = 0; i < 100; i = i + 1) { 27 | if (tree.walk() != 122068) print "Error"; 28 | } 29 | print clock() - start; 30 | -------------------------------------------------------------------------------- /tests/benchmarks/python/instantiation.py: -------------------------------------------------------------------------------- 1 | # This benchmark stresses instance creation and initializer calling. 2 | import time 3 | 4 | 5 | class Foo: 6 | def __init__(self): 7 | pass 8 | 9 | 10 | start = time.process_time() 11 | i = 0 12 | while i < 500000: 13 | Foo() 14 | Foo() 15 | Foo() 16 | Foo() 17 | Foo() 18 | Foo() 19 | Foo() 20 | Foo() 21 | Foo() 22 | Foo() 23 | Foo() 24 | Foo() 25 | Foo() 26 | Foo() 27 | Foo() 28 | Foo() 29 | Foo() 30 | Foo() 31 | Foo() 32 | Foo() 33 | Foo() 34 | Foo() 35 | Foo() 36 | Foo() 37 | Foo() 38 | Foo() 39 | Foo() 40 | Foo() 41 | Foo() 42 | Foo() 43 | i = i + 1 44 | 45 | print(time.process_time() - start) 46 | -------------------------------------------------------------------------------- /run_lox_benchmarks.py: -------------------------------------------------------------------------------- 1 | #!/bin/python3 2 | 3 | import argparse 4 | import pathlib 5 | import subprocess 6 | import time 7 | 8 | BENCHMARK_DIR = "tests/benchmarks/lox" 9 | 10 | 11 | parser = argparse.ArgumentParser(description='Run Lox benchmarks') 12 | parser.add_argument('lox_path', metavar='lox-path', type=str, help='Path to the Lox interpreter binary') 13 | args = parser.parse_args() 14 | lox = pathlib.Path(args.lox_path).resolve() 15 | 16 | for benchmark in sorted(pathlib.Path(BENCHMARK_DIR).glob("*.lox")): 17 | times = [] 18 | for i in range(5): 19 | output = subprocess.check_output([str(lox), str(benchmark.resolve())]) 20 | times.append(float(output.splitlines()[-1])) 21 | time.sleep(2) 22 | print(f"{benchmark.name}: {round(min(times), 4)}") -------------------------------------------------------------------------------- /tests/benchmarks/lox/zoo.lox: -------------------------------------------------------------------------------- 1 | class Zoo { 2 | init() { 3 | this.aarvark = 1; 4 | this.baboon = 1; 5 | this.cat = 1; 6 | this.donkey = 1; 7 | this.elephant = 1; 8 | this.fox = 1; 9 | } 10 | ant() { return this.aarvark; } 11 | banana() { return this.baboon; } 12 | tuna() { return this.cat; } 13 | hay() { return this.donkey; } 14 | grass() { return this.elephant; } 15 | mouse() { return this.fox; } 16 | } 17 | 18 | var zoo = Zoo(); 19 | var sum = 0; 20 | var start = clock(); 21 | while (sum < 10000000) { 22 | sum = sum + zoo.ant() 23 | + zoo.banana() 24 | + zoo.tuna() 25 | + zoo.hay() 26 | + zoo.grass() 27 | + zoo.mouse(); 28 | } 29 | 30 | print sum; 31 | print clock() - start; 32 | -------------------------------------------------------------------------------- /tests/integration/operator/comparison.lox: -------------------------------------------------------------------------------- 1 | print 1 < 2; // expect: true 2 | print 2 < 2; // expect: false 3 | print 2 < 1; // expect: false 4 | 5 | print 1 <= 2; // expect: true 6 | print 2 <= 2; // expect: true 7 | print 2 <= 1; // expect: false 8 | 9 | print 1 > 2; // expect: false 10 | print 2 > 2; // expect: false 11 | print 2 > 1; // expect: true 12 | 13 | print 1 >= 2; // expect: false 14 | print 2 >= 2; // expect: true 15 | print 2 >= 1; // expect: true 16 | 17 | // Zero and negative zero compare the same. 18 | print 0 < -0; // expect: false 19 | print -0 < 0; // expect: false 20 | print 0 > -0; // expect: false 21 | print -0 > 0; // expect: false 22 | print 0 <= -0; // expect: true 23 | print -0 <= 0; // expect: true 24 | print 0 >= -0; // expect: true 25 | print -0 >= 0; // expect: true 26 | -------------------------------------------------------------------------------- /tests/benchmarks/python/trees.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class Tree: 5 | def __init__(self, depth): 6 | self.depth = depth 7 | if depth > 0: 8 | self.a = Tree(depth - 1) 9 | self.b = Tree(depth - 1) 10 | self.c = Tree(depth - 1) 11 | self.d = Tree(depth - 1) 12 | self.e = Tree(depth - 1) 13 | 14 | def walk(self): 15 | if self.depth == 0: 16 | return 0 17 | 18 | return ( 19 | self.depth 20 | + self.a.walk() 21 | + self.b.walk() 22 | + self.c.walk() 23 | + self.d.walk() 24 | + self.e.walk() 25 | ) 26 | 27 | 28 | tree = Tree(8) 29 | start = time.process_time() 30 | for i in range(100): 31 | if tree.walk() != 122068: 32 | print("Error") 33 | 34 | print(time.process_time() - start) 35 | -------------------------------------------------------------------------------- /tests/integration/function/parameters.lox: -------------------------------------------------------------------------------- 1 | fun f0() { return 0; } 2 | print f0(); // expect: 0 3 | 4 | fun f1(a) { return a; } 5 | print f1(1); // expect: 1 6 | 7 | fun f2(a, b) { return a + b; } 8 | print f2(1, 2); // expect: 3 9 | 10 | fun f3(a, b, c) { return a + b + c; } 11 | print f3(1, 2, 3); // expect: 6 12 | 13 | fun f4(a, b, c, d) { return a + b + c + d; } 14 | print f4(1, 2, 3, 4); // expect: 10 15 | 16 | fun f5(a, b, c, d, e) { return a + b + c + d + e; } 17 | print f5(1, 2, 3, 4, 5); // expect: 15 18 | 19 | fun f6(a, b, c, d, e, f) { return a + b + c + d + e + f; } 20 | print f6(1, 2, 3, 4, 5, 6); // expect: 21 21 | 22 | fun f7(a, b, c, d, e, f, g) { return a + b + c + d + e + f + g; } 23 | print f7(1, 2, 3, 4, 5, 6, 7); // expect: 28 24 | 25 | fun f8(a, b, c, d, e, f, g, h) { return a + b + c + d + e + f + g + h; } 26 | print f8(1, 2, 3, 4, 5, 6, 7, 8); // expect: 36 27 | -------------------------------------------------------------------------------- /tests/integration/bool/equality.lox: -------------------------------------------------------------------------------- 1 | print true == true; // expect: true 2 | print true == false; // expect: false 3 | print false == true; // expect: false 4 | print false == false; // expect: true 5 | 6 | // Not equal to other types. 7 | print true == 1; // expect: false 8 | print false == 0; // expect: false 9 | print true == "true"; // expect: false 10 | print false == "false"; // expect: false 11 | print false == ""; // expect: false 12 | 13 | print true != true; // expect: false 14 | print true != false; // expect: true 15 | print false != true; // expect: true 16 | print false != false; // expect: false 17 | 18 | // Not equal to other types. 19 | print true != 1; // expect: true 20 | print false != 0; // expect: true 21 | print true != "true"; // expect: true 22 | print false != "false"; // expect: true 23 | print false != ""; // expect: true 24 | -------------------------------------------------------------------------------- /tests/integration/precedence.lox: -------------------------------------------------------------------------------- 1 | // * has higher precedence than +. 2 | print 2 + 3 * 4; // expect: 14 3 | 4 | // * has higher precedence than -. 5 | print 20 - 3 * 4; // expect: 8 6 | 7 | // / has higher precedence than +. 8 | print 2 + 6 / 3; // expect: 4 9 | 10 | // / has higher precedence than -. 11 | print 2 - 6 / 3; // expect: 0 12 | 13 | // < has higher precedence than ==. 14 | print false == 2 < 1; // expect: true 15 | 16 | // > has higher precedence than ==. 17 | print false == 1 > 2; // expect: true 18 | 19 | // <= has higher precedence than ==. 20 | print false == 2 <= 1; // expect: true 21 | 22 | // >= has higher precedence than ==. 23 | print false == 1 >= 2; // expect: true 24 | 25 | // 1 - 1 is not space-sensitive. 26 | print 1 - 1; // expect: 0 27 | print 1 -1; // expect: 0 28 | print 1- 1; // expect: 0 29 | print 1-1; // expect: 0 30 | 31 | // Using () for grouping. 32 | print (2 * (6 - (2 + 2))); // expect: 4 33 | -------------------------------------------------------------------------------- /tests/benchmarks/lox/equality.lox: -------------------------------------------------------------------------------- 1 | var i = 0; 2 | 3 | var loopStart = clock(); 4 | 5 | while (i < 10000000) { 6 | i = i + 1; 7 | 8 | 1; 1; 1; 2; 1; nil; 1; "str"; 1; true; 9 | nil; nil; nil; 1; nil; "str"; nil; true; 10 | true; true; true; 1; true; false; true; "str"; true; nil; 11 | "str"; "str"; "str"; "stru"; "str"; 1; "str"; nil; "str"; true; 12 | } 13 | 14 | var loopTime = clock() - loopStart; 15 | 16 | var start = clock(); 17 | 18 | i = 0; 19 | while (i < 10000000) { 20 | i = i + 1; 21 | 22 | 1 == 1; 1 == 2; 1 == nil; 1 == "str"; 1 == true; 23 | nil == nil; nil == 1; nil == "str"; nil == true; 24 | true == true; true == 1; true == false; true == "str"; true == nil; 25 | "str" == "str"; "str" == "stru"; "str" == 1; "str" == nil; "str" == true; 26 | } 27 | 28 | var elapsed = clock() - start; 29 | print "loop"; 30 | print loopTime; 31 | print "equals"; 32 | print elapsed - loopTime; 33 | print "elapsed"; 34 | print elapsed; 35 | -------------------------------------------------------------------------------- /tests/benchmarks/python/zoo.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class Zoo: 5 | def __init__(self): 6 | self.aarvark = 1 7 | self.baboon = 1 8 | self.cat = 1 9 | self.donkey = 1 10 | self.elephant = 1 11 | self.fox = 1 12 | 13 | def ant(self): 14 | return self.aarvark 15 | 16 | def banana(self): 17 | return self.baboon 18 | 19 | def tuna(self): 20 | return self.cat 21 | 22 | def hay(self): 23 | return self.donkey 24 | 25 | def grass(self): 26 | return self.elephant 27 | 28 | def mouse(self): 29 | return self.fox 30 | 31 | 32 | zoo = Zoo() 33 | s = 0 34 | start = time.process_time() 35 | 36 | while s < 10000000: 37 | s = ( 38 | s 39 | + zoo.ant() 40 | + zoo.banana() 41 | + zoo.tuna() 42 | + zoo.hay() 43 | + zoo.grass() 44 | + zoo.mouse() 45 | ) 46 | 47 | print(s) 48 | print(time.process_time() - start) 49 | -------------------------------------------------------------------------------- /tests/benchmarks/perl/instantiation.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use v5.10; 4 | 5 | use Time::HiRes qw(clock); 6 | 7 | { 8 | package Foo; 9 | 10 | sub new { 11 | my $class = shift; 12 | my $self = {}; 13 | bless $self => $class; 14 | return $self; 15 | } 16 | } 17 | 18 | my $start = clock(); 19 | my $i = 0; 20 | while ($i < 500000) { 21 | Foo->new(); 22 | Foo->new(); 23 | Foo->new(); 24 | Foo->new(); 25 | Foo->new(); 26 | Foo->new(); 27 | Foo->new(); 28 | Foo->new(); 29 | Foo->new(); 30 | Foo->new(); 31 | Foo->new(); 32 | Foo->new(); 33 | Foo->new(); 34 | Foo->new(); 35 | Foo->new(); 36 | Foo->new(); 37 | Foo->new(); 38 | Foo->new(); 39 | Foo->new(); 40 | Foo->new(); 41 | Foo->new(); 42 | Foo->new(); 43 | Foo->new(); 44 | Foo->new(); 45 | Foo->new(); 46 | Foo->new(); 47 | Foo->new(); 48 | Foo->new(); 49 | Foo->new(); 50 | Foo->new(); 51 | $i++; 52 | } 53 | 54 | say clock() - $start; 55 | -------------------------------------------------------------------------------- /tests/integration/for/syntax.lox: -------------------------------------------------------------------------------- 1 | // Single-expression body. 2 | for (var c = 0; c < 3;) print c = c + 1; 3 | // expect: 1 4 | // expect: 2 5 | // expect: 3 6 | 7 | // Block body. 8 | for (var a = 0; a < 3; a = a + 1) { 9 | print a; 10 | } 11 | // expect: 0 12 | // expect: 1 13 | // expect: 2 14 | 15 | // No clauses. 16 | fun foo() { 17 | for (;;) return "done"; 18 | } 19 | print foo(); // expect: done 20 | 21 | // No variable. 22 | var i = 0; 23 | for (; i < 2; i = i + 1) print i; 24 | // expect: 0 25 | // expect: 1 26 | 27 | // No condition. 28 | fun bar() { 29 | for (var i = 0;; i = i + 1) { 30 | print i; 31 | if (i >= 2) return; 32 | } 33 | } 34 | bar(); 35 | // expect: 0 36 | // expect: 1 37 | // expect: 2 38 | 39 | // No increment. 40 | for (var i = 0; i < 2;) { 41 | print i; 42 | i = i + 1; 43 | } 44 | // expect: 0 45 | // expect: 1 46 | 47 | // Statement bodies. 48 | for (; false;) if (true) 1; else 2; 49 | for (; false;) while (true) 1; 50 | for (; false;) for (;;) 1; 51 | -------------------------------------------------------------------------------- /tests/integration/method/arity.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method0() { return "no args"; } 3 | method1(a) { return a; } 4 | method2(a, b) { return a + b; } 5 | method3(a, b, c) { return a + b + c; } 6 | method4(a, b, c, d) { return a + b + c + d; } 7 | method5(a, b, c, d, e) { return a + b + c + d + e; } 8 | method6(a, b, c, d, e, f) { return a + b + c + d + e + f; } 9 | method7(a, b, c, d, e, f, g) { return a + b + c + d + e + f + g; } 10 | method8(a, b, c, d, e, f, g, h) { return a + b + c + d + e + f + g + h; } 11 | } 12 | 13 | var foo = Foo(); 14 | print foo.method0(); // expect: no args 15 | print foo.method1(1); // expect: 1 16 | print foo.method2(1, 2); // expect: 3 17 | print foo.method3(1, 2, 3); // expect: 6 18 | print foo.method4(1, 2, 3, 4); // expect: 10 19 | print foo.method5(1, 2, 3, 4, 5); // expect: 15 20 | print foo.method6(1, 2, 3, 4, 5, 6); // expect: 21 21 | print foo.method7(1, 2, 3, 4, 5, 6, 7); // expect: 28 22 | print foo.method8(1, 2, 3, 4, 5, 6, 7, 8); // expect: 36 23 | -------------------------------------------------------------------------------- /tests/benchmarks/perl/equality.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use v5.10; 4 | 5 | use Time::HiRes qw(clock); 6 | 7 | no warnings qw(void uninitialized); 8 | 9 | my $i = 0; 10 | my $loopStart = clock(); 11 | 12 | while ($i < 10000000) { 13 | $i = $i + 1; 14 | 15 | 1; 1; 1; 2; 1; undef; 1; "str"; 1; 1; 16 | undef; undef; undef; 1; undef; "str"; undef; 1; 17 | 1; 1; 1; 1; 1; 0; 1; "str"; 1; undef; 18 | "str"; "str"; "str"; "stru"; "str"; 1; "str"; undef; "str"; 1; 19 | } 20 | 21 | my $loopTime = clock() - $loopStart; 22 | 23 | my $start = clock(); 24 | 25 | $i = 0; 26 | while ($i < 10000000) { 27 | $i = $i + 1; 28 | 29 | 1 == 1; 1 == 2; 1 eq undef; 1 eq "str"; 1 == 1; 30 | undef eq undef; undef == 1; undef eq "str"; undef == 1; 31 | 1 == 1; 1 == 1; 1 == 0; 1 eq "str"; 1 eq undef; 32 | "str" eq "str"; "str" eq "stru"; "str" eq 1; "str" eq undef; "str" eq 1; 33 | } 34 | 35 | my $elapsed = clock() - $start; 36 | say "loop"; 37 | say $loopTime; 38 | say "elapsed"; 39 | say $elapsed; 40 | say "equals"; 41 | say $elapsed - $loopTime; 42 | -------------------------------------------------------------------------------- /tests/benchmarks/perl/zoo.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use v5.10; 4 | 5 | use Time::HiRes qw(clock); 6 | 7 | { 8 | package Zoo; 9 | 10 | sub new { 11 | my $class = shift; 12 | my $self = { 13 | aarvark => 1, 14 | baboon => 1, 15 | cat => 1, 16 | donkey => 1, 17 | elephant => 1, 18 | fox => 1, 19 | }; 20 | bless $self => $class; 21 | return $self; 22 | } 23 | 24 | sub ant { $_[0]->{aarvark}; } 25 | sub banana { $_[0]->{baboon}; } 26 | sub tuna { $_[0]->{cat}; } 27 | sub hay { $_[0]->{donkey}; } 28 | sub grass { $_[0]->{elephant}; } 29 | sub mouse { $_[0]->{fox}; } 30 | } 31 | 32 | my $zoo = Zoo->new(); 33 | my $sum = 0; 34 | my $start = clock(); 35 | while ($sum < 10000000) { 36 | $sum = $sum + $zoo->ant() 37 | + $zoo->banana() 38 | + $zoo->tuna() 39 | + $zoo->hay() 40 | + $zoo->grass() 41 | + $zoo->mouse(); 42 | } 43 | 44 | say $sum; 45 | say clock() - $start; 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Manuel Cerón 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /tests/benchmarks/perl/trees.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use v5.10; 4 | 5 | use Time::HiRes qw(clock); 6 | 7 | { 8 | package Tree; 9 | 10 | sub new { 11 | my $class = shift; 12 | my $depth = shift; 13 | my $self = { depth => $depth }; 14 | 15 | if ($depth > 0) { 16 | $self->{a} = Tree->new($depth - 1); 17 | $self->{b} = Tree->new($depth - 1); 18 | $self->{c} = Tree->new($depth - 1); 19 | $self->{d} = Tree->new($depth - 1); 20 | $self->{e} = Tree->new($depth - 1); 21 | } 22 | 23 | bless $self => $class; 24 | return $self; 25 | } 26 | 27 | sub walk { 28 | my $self = shift; 29 | 30 | if ($self->{depth} == 0) { 31 | return 0 32 | } 33 | 34 | return $self->{depth} 35 | + $self->{a}->walk() 36 | + $self->{b}->walk() 37 | + $self->{c}->walk() 38 | + $self->{d}->walk() 39 | + $self->{e}->walk(); 40 | } 41 | } 42 | 43 | my $tree = Tree->new(8); 44 | my $start = clock(); 45 | 46 | for (my $i = 0; $i < 100; $i++) { 47 | if ($tree->walk() != 122068) { 48 | say "Error"; 49 | } 50 | } 51 | say clock() - $start; 52 | -------------------------------------------------------------------------------- /tests/benchmarks/python/equality.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | i = 0 4 | loopStart = time.process_time() 5 | 6 | while i < 10000000: 7 | i = i + 1 8 | 9 | 1 10 | 1 11 | 1 12 | 2 13 | 1 14 | None 15 | 1 16 | "str" 17 | 1 18 | True 19 | 20 | None 21 | None 22 | None 23 | 1 24 | None 25 | "str" 26 | None 27 | True 28 | 29 | True 30 | True 31 | True 32 | 1 33 | True 34 | False 35 | True 36 | "str" 37 | True 38 | None 39 | 40 | "str" 41 | "str" 42 | "str" 43 | "stru" 44 | "str" 45 | 1 46 | "str" 47 | None 48 | "str" 49 | True 50 | 51 | loopTime = time.process_time() - loopStart 52 | start = time.process_time() 53 | i = 0 54 | 55 | while i < 10000000: 56 | i = i + 1 57 | 58 | 1 == 1 59 | 1 == 2 60 | 1 == None 61 | 1 == "str" 62 | 1 == True 63 | 64 | None == None 65 | None == 1 66 | None == "str" 67 | None == True 68 | 69 | True == True 70 | True == 1 71 | True == False 72 | True == "str" 73 | True == None 74 | 75 | "str" == "str" 76 | "str" == "stru" 77 | "str" == 1 78 | "str" == None 79 | "str" == True 80 | 81 | elapsed = time.process_time() - start 82 | 83 | print("loop") 84 | print(loopTime) 85 | print("elapsed") 86 | print(elapsed) 87 | print("equals") 88 | print(elapsed - loopTime) 89 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod chunk; 2 | mod compiler; 3 | mod error; 4 | mod gc; 5 | mod objects; 6 | mod scanner; 7 | mod vm; 8 | 9 | use error::LoxError; 10 | use std::env; 11 | use std::fs; 12 | use std::io::{self, Write}; 13 | use std::process; 14 | use vm::Vm; 15 | 16 | fn repl(vm: &mut Vm) { 17 | loop { 18 | print!("> "); 19 | io::stdout().flush().unwrap(); 20 | let mut line = String::new(); 21 | io::stdin() 22 | .read_line(&mut line) 23 | .expect("Unable to read line from the REPL"); 24 | if line.is_empty() { 25 | break; 26 | } 27 | vm.interpret(&line).ok(); 28 | } 29 | } 30 | 31 | fn run_file(vm: &mut Vm, path: &str) { 32 | let code = match fs::read_to_string(path) { 33 | Ok(content) => content, 34 | Err(error) => { 35 | eprint!("Unable to read file {}: {}", path, error); 36 | process::exit(74); 37 | } 38 | }; 39 | if let Err(error) = vm.interpret(&code) { 40 | match error { 41 | LoxError::CompileError => process::exit(65), 42 | LoxError::RuntimeError => process::exit(70), 43 | } 44 | } 45 | } 46 | 47 | fn main() { 48 | let args: Vec = env::args().collect(); 49 | let mut vm = Vm::new(); 50 | match args.len() { 51 | 1 => repl(&mut vm), 52 | 2 => run_file(&mut vm, &args[1]), 53 | _ => process::exit(64), 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/integration/limit/no_reuse_constants.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | 0; 1; 2; 3; 4; 5; 6; 7; 3 | 8; 9; 10; 11; 12; 13; 14; 15; 4 | 16; 17; 18; 19; 20; 21; 22; 23; 5 | 24; 25; 26; 27; 28; 29; 30; 31; 6 | 32; 33; 34; 35; 36; 37; 38; 39; 7 | 40; 41; 42; 43; 44; 45; 46; 47; 8 | 48; 49; 50; 51; 52; 53; 54; 55; 9 | 56; 57; 58; 59; 60; 61; 62; 63; 10 | 64; 65; 66; 67; 68; 69; 70; 71; 11 | 72; 73; 74; 75; 76; 77; 78; 79; 12 | 80; 81; 82; 83; 84; 85; 86; 87; 13 | 88; 89; 90; 91; 92; 93; 94; 95; 14 | 96; 97; 98; 99; 100; 101; 102; 103; 15 | 104; 105; 106; 107; 108; 109; 110; 111; 16 | 112; 113; 114; 115; 116; 117; 118; 119; 17 | 120; 121; 122; 123; 124; 125; 126; 127; 18 | 128; 129; 130; 131; 132; 133; 134; 135; 19 | 136; 137; 138; 139; 140; 141; 142; 143; 20 | 144; 145; 146; 147; 148; 149; 150; 151; 21 | 152; 153; 154; 155; 156; 157; 158; 159; 22 | 160; 161; 162; 163; 164; 165; 166; 167; 23 | 168; 169; 170; 171; 172; 173; 174; 175; 24 | 176; 177; 178; 179; 180; 181; 182; 183; 25 | 184; 185; 186; 187; 188; 189; 190; 191; 26 | 192; 193; 194; 195; 196; 197; 198; 199; 27 | 200; 201; 202; 203; 204; 205; 206; 207; 28 | 208; 209; 210; 211; 212; 213; 214; 215; 29 | 216; 217; 218; 219; 220; 221; 222; 223; 30 | 224; 225; 226; 227; 228; 229; 230; 231; 31 | 232; 233; 234; 235; 236; 237; 238; 239; 32 | 240; 241; 242; 243; 244; 245; 246; 247; 33 | 248; 249; 250; 251; 252; 253; 254; 255; 34 | 35 | 1; // Error at '1': Too many constants in one chunk. 36 | } 37 | -------------------------------------------------------------------------------- /tests/integration/limit/too_many_constants.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | 0; 1; 2; 3; 4; 5; 6; 7; 3 | 8; 9; 10; 11; 12; 13; 14; 15; 4 | 16; 17; 18; 19; 20; 21; 22; 23; 5 | 24; 25; 26; 27; 28; 29; 30; 31; 6 | 32; 33; 34; 35; 36; 37; 38; 39; 7 | 40; 41; 42; 43; 44; 45; 46; 47; 8 | 48; 49; 50; 51; 52; 53; 54; 55; 9 | 56; 57; 58; 59; 60; 61; 62; 63; 10 | 64; 65; 66; 67; 68; 69; 70; 71; 11 | 72; 73; 74; 75; 76; 77; 78; 79; 12 | 80; 81; 82; 83; 84; 85; 86; 87; 13 | 88; 89; 90; 91; 92; 93; 94; 95; 14 | 96; 97; 98; 99; 100; 101; 102; 103; 15 | 104; 105; 106; 107; 108; 109; 110; 111; 16 | 112; 113; 114; 115; 116; 117; 118; 119; 17 | 120; 121; 122; 123; 124; 125; 126; 127; 18 | 128; 129; 130; 131; 132; 133; 134; 135; 19 | 136; 137; 138; 139; 140; 141; 142; 143; 20 | 144; 145; 146; 147; 148; 149; 150; 151; 21 | 152; 153; 154; 155; 156; 157; 158; 159; 22 | 160; 161; 162; 163; 164; 165; 166; 167; 23 | 168; 169; 170; 171; 172; 173; 174; 175; 24 | 176; 177; 178; 179; 180; 181; 182; 183; 25 | 184; 185; 186; 187; 188; 189; 190; 191; 26 | 192; 193; 194; 195; 196; 197; 198; 199; 27 | 200; 201; 202; 203; 204; 205; 206; 207; 28 | 208; 209; 210; 211; 212; 213; 214; 215; 29 | 216; 217; 218; 219; 220; 221; 222; 223; 30 | 224; 225; 226; 227; 228; 229; 230; 231; 31 | 232; 233; 234; 235; 236; 237; 238; 239; 32 | 240; 241; 242; 243; 244; 245; 246; 247; 33 | 248; 249; 250; 251; 252; 253; 254; 255; 34 | 35 | "oops"; // Error at '"oops"': Too many constants in one chunk. 36 | } 37 | -------------------------------------------------------------------------------- /tests/benchmarks/lox/invocation.lox: -------------------------------------------------------------------------------- 1 | // This benchmark stresses just method invocation. 2 | 3 | class Foo { 4 | method0() {} 5 | method1() {} 6 | method2() {} 7 | method3() {} 8 | method4() {} 9 | method5() {} 10 | method6() {} 11 | method7() {} 12 | method8() {} 13 | method9() {} 14 | method10() {} 15 | method11() {} 16 | method12() {} 17 | method13() {} 18 | method14() {} 19 | method15() {} 20 | method16() {} 21 | method17() {} 22 | method18() {} 23 | method19() {} 24 | method20() {} 25 | method21() {} 26 | method22() {} 27 | method23() {} 28 | method24() {} 29 | method25() {} 30 | method26() {} 31 | method27() {} 32 | method28() {} 33 | method29() {} 34 | } 35 | 36 | var foo = Foo(); 37 | var start = clock(); 38 | var i = 0; 39 | while (i < 500000) { 40 | foo.method0(); 41 | foo.method1(); 42 | foo.method2(); 43 | foo.method3(); 44 | foo.method4(); 45 | foo.method5(); 46 | foo.method6(); 47 | foo.method7(); 48 | foo.method8(); 49 | foo.method9(); 50 | foo.method10(); 51 | foo.method11(); 52 | foo.method12(); 53 | foo.method13(); 54 | foo.method14(); 55 | foo.method15(); 56 | foo.method16(); 57 | foo.method17(); 58 | foo.method18(); 59 | foo.method19(); 60 | foo.method20(); 61 | foo.method21(); 62 | foo.method22(); 63 | foo.method23(); 64 | foo.method24(); 65 | foo.method25(); 66 | foo.method26(); 67 | foo.method27(); 68 | foo.method28(); 69 | foo.method29(); 70 | i = i + 1; 71 | } 72 | 73 | print clock() - start; 74 | -------------------------------------------------------------------------------- /tests/benchmarks/lox/binary_trees.lox: -------------------------------------------------------------------------------- 1 | class Tree { 2 | init(item, depth) { 3 | this.item = item; 4 | this.depth = depth; 5 | if (depth > 0) { 6 | var item2 = item + item; 7 | depth = depth - 1; 8 | this.left = Tree(item2 - 1, depth); 9 | this.right = Tree(item2, depth); 10 | } else { 11 | this.left = nil; 12 | this.right = nil; 13 | } 14 | } 15 | 16 | check() { 17 | if (this.left == nil) { 18 | return this.item; 19 | } 20 | 21 | return this.item + this.left.check() - this.right.check(); 22 | } 23 | } 24 | 25 | var minDepth = 4; 26 | var maxDepth = 14; 27 | var stretchDepth = maxDepth + 1; 28 | 29 | var start = clock(); 30 | 31 | print "stretch tree of depth:"; 32 | print stretchDepth; 33 | print "check:"; 34 | print Tree(0, stretchDepth).check(); 35 | 36 | var longLivedTree = Tree(0, maxDepth); 37 | 38 | // iterations = 2 ** maxDepth 39 | var iterations = 1; 40 | var d = 0; 41 | while (d < maxDepth) { 42 | iterations = iterations * 2; 43 | d = d + 1; 44 | } 45 | 46 | var depth = minDepth; 47 | while (depth < stretchDepth) { 48 | var check = 0; 49 | var i = 1; 50 | while (i <= iterations) { 51 | check = check + Tree(i, depth).check() + Tree(-i, depth).check(); 52 | i = i + 1; 53 | } 54 | 55 | print "num trees:"; 56 | print iterations * 2; 57 | print "depth:"; 58 | print depth; 59 | print "check:"; 60 | print check; 61 | 62 | iterations = iterations / 4; 63 | depth = depth + 2; 64 | } 65 | 66 | print "long lived tree of depth:"; 67 | print maxDepth; 68 | print "check:"; 69 | print longLivedTree.check(); 70 | print "elapsed:"; 71 | print clock() - start; 72 | -------------------------------------------------------------------------------- /tests/benchmarks/python/binary_trees.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class Tree: 5 | def __init__(self, item, depth): 6 | self.item = item 7 | self.depth = depth 8 | if depth > 0: 9 | item2 = item + item 10 | depth = depth - 1 11 | self.left = Tree(item2 - 1, depth) 12 | self.right = Tree(item2, depth) 13 | else: 14 | self.left = None 15 | self.right = None 16 | 17 | def check(self): 18 | if self.left == None: 19 | return self.item 20 | 21 | return self.item + self.left.check() - self.right.check() 22 | 23 | 24 | minDepth = 4 25 | maxDepth = 14 26 | stretchDepth = maxDepth + 1 27 | 28 | start = time.process_time() 29 | 30 | print("stretch tree of depth:") 31 | print(stretchDepth) 32 | print("check:") 33 | print(Tree(0, stretchDepth).check()) 34 | 35 | longLivedTree = Tree(0, maxDepth) 36 | 37 | # iterations = 2 ** maxDepth 38 | iterations = 1 39 | d = 0 40 | while d < maxDepth: 41 | iterations = iterations * 2 42 | d = d + 1 43 | 44 | depth = minDepth 45 | while depth < stretchDepth: 46 | check = 0 47 | i = 1 48 | while i <= iterations: 49 | check = check + Tree(i, depth).check() + Tree(-i, depth).check() 50 | i = i + 1 51 | 52 | print("num trees:") 53 | print(iterations * 2) 54 | print("depth:") 55 | print(depth) 56 | print("check:") 57 | print(check) 58 | 59 | iterations = iterations / 4 60 | depth = depth + 2 61 | 62 | print("long lived tree of depth:") 63 | print(maxDepth) 64 | print("check:") 65 | print(longLivedTree.check()) 66 | print("elapsed:") 67 | print(time.process_time() - start) 68 | -------------------------------------------------------------------------------- /tests/benchmarks/lox/method_call.lox: -------------------------------------------------------------------------------- 1 | class Toggle { 2 | init(startState) { 3 | this.state = startState; 4 | } 5 | 6 | value() { return this.state; } 7 | 8 | activate() { 9 | this.state = !this.state; 10 | return this; 11 | } 12 | } 13 | 14 | class NthToggle < Toggle { 15 | init(startState, maxCounter) { 16 | super.init(startState); 17 | this.countMax = maxCounter; 18 | this.count = 0; 19 | } 20 | 21 | activate() { 22 | this.count = this.count + 1; 23 | if (this.count >= this.countMax) { 24 | super.activate(); 25 | this.count = 0; 26 | } 27 | 28 | return this; 29 | } 30 | } 31 | 32 | var start = clock(); 33 | var n = 100000; 34 | var val = true; 35 | var toggle = Toggle(val); 36 | 37 | for (var i = 0; i < n; i = i + 1) { 38 | val = toggle.activate().value(); 39 | val = toggle.activate().value(); 40 | val = toggle.activate().value(); 41 | val = toggle.activate().value(); 42 | val = toggle.activate().value(); 43 | val = toggle.activate().value(); 44 | val = toggle.activate().value(); 45 | val = toggle.activate().value(); 46 | val = toggle.activate().value(); 47 | val = toggle.activate().value(); 48 | } 49 | 50 | print toggle.value(); 51 | 52 | val = true; 53 | var ntoggle = NthToggle(val, 3); 54 | 55 | for (var i = 0; i < n; i = i + 1) { 56 | val = ntoggle.activate().value(); 57 | val = ntoggle.activate().value(); 58 | val = ntoggle.activate().value(); 59 | val = ntoggle.activate().value(); 60 | val = ntoggle.activate().value(); 61 | val = ntoggle.activate().value(); 62 | val = ntoggle.activate().value(); 63 | val = ntoggle.activate().value(); 64 | val = ntoggle.activate().value(); 65 | val = ntoggle.activate().value(); 66 | } 67 | 68 | print ntoggle.value(); 69 | print clock() - start; 70 | -------------------------------------------------------------------------------- /tests/benchmarks/python/method_call.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class Toggle: 5 | def __init__(self, startState): 6 | self.state = startState 7 | 8 | def value(self): 9 | return self.state 10 | 11 | def activate(self): 12 | self.state = not self.state 13 | return self 14 | 15 | 16 | class NthToggle(Toggle): 17 | def __init__(self, startState, maxCounter): 18 | super().__init__(startState) 19 | self.countMax = maxCounter 20 | self.count = 0 21 | 22 | def activate(self): 23 | self.count = self.count + 1 24 | if self.count >= self.countMax: 25 | super().activate() 26 | self.count = 0 27 | 28 | return self 29 | 30 | 31 | start = time.process_time() 32 | n = 100000 33 | val = True 34 | toggle = Toggle(val) 35 | 36 | 37 | for i in range(n): 38 | val = toggle.activate().value() 39 | val = toggle.activate().value() 40 | val = toggle.activate().value() 41 | val = toggle.activate().value() 42 | val = toggle.activate().value() 43 | val = toggle.activate().value() 44 | val = toggle.activate().value() 45 | val = toggle.activate().value() 46 | val = toggle.activate().value() 47 | val = toggle.activate().value() 48 | 49 | 50 | print(toggle.value()) 51 | 52 | val = True 53 | ntoggle = NthToggle(val, 3) 54 | 55 | for i in range(n): 56 | val = ntoggle.activate().value() 57 | val = ntoggle.activate().value() 58 | val = ntoggle.activate().value() 59 | val = ntoggle.activate().value() 60 | val = ntoggle.activate().value() 61 | val = ntoggle.activate().value() 62 | val = ntoggle.activate().value() 63 | val = ntoggle.activate().value() 64 | val = ntoggle.activate().value() 65 | val = ntoggle.activate().value() 66 | 67 | print(ntoggle.value()) 68 | print(time.process_time() - start) 69 | -------------------------------------------------------------------------------- /tests/benchmarks/perl/invocation.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use v5.10; 4 | 5 | use Time::HiRes qw(clock); 6 | 7 | { 8 | package Foo; 9 | 10 | sub new { 11 | my $class = shift; 12 | my $self = {}; 13 | bless $self => $class; 14 | return $self; 15 | } 16 | 17 | sub method0 { } 18 | sub method1 { } 19 | sub method2 { } 20 | sub method3 { } 21 | sub method4 { } 22 | sub method5 { } 23 | sub method6 { } 24 | sub method7 { } 25 | sub method8 { } 26 | sub method9 { } 27 | sub method10 { } 28 | sub method11 { } 29 | sub method12 { } 30 | sub method13 { } 31 | sub method14 { } 32 | sub method15 { } 33 | sub method16 { } 34 | sub method17 { } 35 | sub method18 { } 36 | sub method19 { } 37 | sub method20 { } 38 | sub method21 { } 39 | sub method22 { } 40 | sub method23 { } 41 | sub method24 { } 42 | sub method25 { } 43 | sub method26 { } 44 | sub method27 { } 45 | sub method28 { } 46 | sub method29 { } 47 | } 48 | 49 | 50 | my $foo = Foo->new(); 51 | my $start = clock(); 52 | my $i = 0; 53 | while ($i < 500000) { 54 | $foo->method0(); 55 | $foo->method1(); 56 | $foo->method2(); 57 | $foo->method3(); 58 | $foo->method4(); 59 | $foo->method5(); 60 | $foo->method6(); 61 | $foo->method7(); 62 | $foo->method8(); 63 | $foo->method9(); 64 | $foo->method10(); 65 | $foo->method11(); 66 | $foo->method12(); 67 | $foo->method13(); 68 | $foo->method14(); 69 | $foo->method15(); 70 | $foo->method16(); 71 | $foo->method17(); 72 | $foo->method18(); 73 | $foo->method19(); 74 | $foo->method20(); 75 | $foo->method21(); 76 | $foo->method22(); 77 | $foo->method23(); 78 | $foo->method24(); 79 | $foo->method25(); 80 | $foo->method26(); 81 | $foo->method27(); 82 | $foo->method28(); 83 | $foo->method29(); 84 | $i++; 85 | } 86 | 87 | say clock() - $start; 88 | -------------------------------------------------------------------------------- /tests/benchmarks/perl/binary_trees.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use v5.10; 4 | 5 | use Time::HiRes qw(clock); 6 | 7 | { 8 | package Tree; 9 | 10 | sub new { 11 | my $class = shift; 12 | my $item = shift; 13 | my $depth = shift; 14 | 15 | my $self = { 16 | item => $item, 17 | depth => $depth 18 | }; 19 | 20 | if ($depth > 0) { 21 | my $item2 = $item + $item; 22 | $depth = $depth - 1; 23 | $self->{left} = Tree->new($item2 - 1, $depth); 24 | $self->{right} = Tree->new($item2, $depth); 25 | } else { 26 | $self->{left} = undef; 27 | $self->{right} = undef; 28 | } 29 | 30 | bless $self => $class; 31 | return $self; 32 | } 33 | 34 | sub check { 35 | my $self = shift; 36 | 37 | if (!defined $self->{left}) { 38 | return $self->{item}; 39 | } 40 | 41 | return $self->{item} + $self->{left}->check() - $self->{right}->check(); 42 | } 43 | } 44 | 45 | my $minDepth = 4; 46 | my $maxDepth = 14; 47 | my $stretchDepth = $maxDepth + 1; 48 | 49 | my $start = clock(); 50 | 51 | say "stretch tree of depth:"; 52 | say $stretchDepth; 53 | say "check:"; 54 | say Tree->new(0, $stretchDepth)->check(); 55 | 56 | my $longLivedTree = Tree->new(0, $maxDepth); 57 | 58 | # iterations = 2 ** $maxDepth 59 | my $iterations = 1; 60 | my $d = 0; 61 | while ($d < $maxDepth) { 62 | $iterations = $iterations * 2; 63 | $d++; 64 | } 65 | 66 | my $depth = $minDepth; 67 | while ($depth < $stretchDepth) { 68 | my $check = 0; 69 | my $i = 1; 70 | while ($i <= $iterations) { 71 | $check = $check + Tree->new($i, $depth)->check() + Tree->new(-$i, $depth)->check(); 72 | $i++; 73 | } 74 | 75 | say "num trees:"; 76 | say $iterations * 2; 77 | say "depth:"; 78 | say $depth; 79 | say "check:"; 80 | say $check; 81 | 82 | $iterations = $iterations / 4; 83 | $depth = $depth + 2; 84 | } 85 | 86 | say "long lived tree of depth:"; 87 | say $maxDepth; 88 | say "check:"; 89 | say $longLivedTree->check(); 90 | say "elapsed:"; 91 | say clock() - $start; 92 | -------------------------------------------------------------------------------- /tests/benchmarks/perl/method_call.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use v5.10; 4 | 5 | use Time::HiRes qw(clock); 6 | 7 | { 8 | package Toggle; 9 | 10 | sub new { 11 | my $class = shift; 12 | my $startState = shift; 13 | 14 | my $self = { 15 | state => $startState 16 | }; 17 | 18 | bless $self => $class; 19 | return $self; 20 | } 21 | 22 | sub value { 23 | my $self = shift; 24 | return $self->{state}; 25 | } 26 | 27 | sub activate { 28 | my $self = shift; 29 | $self->{state} = !$self->{state}; 30 | return $self; 31 | } 32 | 33 | 1; 34 | 35 | package NthToggle; 36 | our @ISA = qw(Toggle); 37 | 38 | sub new { 39 | my $class = shift; 40 | my $startState = shift; 41 | my $maxCounter = shift; 42 | 43 | my $self = $class->SUPER::new($startState); 44 | $self->{countMax} = $maxCounter; 45 | $self->{count} = 0; 46 | 47 | bless $self => $class; 48 | return $self; 49 | } 50 | 51 | sub activate { 52 | my $self = shift; 53 | $self->{count}++; 54 | if ($self->{count} >= $self->{countMax}) { 55 | $self->SUPER::activate(); 56 | $self->{count} = 0; 57 | } 58 | 59 | return $self; 60 | } 61 | } 62 | 63 | my $start = clock(); 64 | my $n = 100000; 65 | my $val = 1; 66 | my $toggle = Toggle->new($val); 67 | 68 | for (my $i = 0; $i < $n; $i++) { 69 | $val = $toggle->activate()->value(); 70 | $val = $toggle->activate()->value(); 71 | $val = $toggle->activate()->value(); 72 | $val = $toggle->activate()->value(); 73 | $val = $toggle->activate()->value(); 74 | $val = $toggle->activate()->value(); 75 | $val = $toggle->activate()->value(); 76 | $val = $toggle->activate()->value(); 77 | $val = $toggle->activate()->value(); 78 | $val = $toggle->activate()->value(); 79 | } 80 | 81 | say $toggle->value(); 82 | 83 | $val = 1; 84 | my $ntoggle = NthToggle->new($val, 3); 85 | 86 | for (my $i = 0; $i < $n; $i++) { 87 | $val = $ntoggle->activate()->value(); 88 | $val = $ntoggle->activate()->value(); 89 | $val = $ntoggle->activate()->value(); 90 | $val = $ntoggle->activate()->value(); 91 | $val = $ntoggle->activate()->value(); 92 | $val = $ntoggle->activate()->value(); 93 | $val = $ntoggle->activate()->value(); 94 | $val = $ntoggle->activate()->value(); 95 | $val = $ntoggle->activate()->value(); 96 | $val = $ntoggle->activate()->value(); 97 | } 98 | say $ntoggle->value(); 99 | say clock() - $start; 100 | -------------------------------------------------------------------------------- /tests/benchmarks/python/invocation.py: -------------------------------------------------------------------------------- 1 | # This benchmark stresses just method invocation. 2 | 3 | import time 4 | 5 | 6 | class Foo: 7 | def method0(self): 8 | pass 9 | 10 | def method1(self): 11 | pass 12 | 13 | def method2(self): 14 | pass 15 | 16 | def method3(self): 17 | pass 18 | 19 | def method4(self): 20 | pass 21 | 22 | def method5(self): 23 | pass 24 | 25 | def method6(self): 26 | pass 27 | 28 | def method7(self): 29 | pass 30 | 31 | def method8(self): 32 | pass 33 | 34 | def method9(self): 35 | pass 36 | 37 | def method10(self): 38 | pass 39 | 40 | def method11(self): 41 | pass 42 | 43 | def method12(self): 44 | pass 45 | 46 | def method13(self): 47 | pass 48 | 49 | def method14(self): 50 | pass 51 | 52 | def method15(self): 53 | pass 54 | 55 | def method16(self): 56 | pass 57 | 58 | def method17(self): 59 | pass 60 | 61 | def method18(self): 62 | pass 63 | 64 | def method19(self): 65 | pass 66 | 67 | def method20(self): 68 | pass 69 | 70 | def method21(self): 71 | pass 72 | 73 | def method22(self): 74 | pass 75 | 76 | def method23(self): 77 | pass 78 | 79 | def method24(self): 80 | pass 81 | 82 | def method25(self): 83 | pass 84 | 85 | def method26(self): 86 | pass 87 | 88 | def method27(self): 89 | pass 90 | 91 | def method28(self): 92 | pass 93 | 94 | def method29(self): 95 | pass 96 | 97 | 98 | foo = Foo() 99 | start = time.process_time() 100 | i = 0 101 | while i < 500000: 102 | foo.method0() 103 | foo.method1() 104 | foo.method2() 105 | foo.method3() 106 | foo.method4() 107 | foo.method5() 108 | foo.method6() 109 | foo.method7() 110 | foo.method8() 111 | foo.method9() 112 | foo.method10() 113 | foo.method11() 114 | foo.method12() 115 | foo.method13() 116 | foo.method14() 117 | foo.method15() 118 | foo.method16() 119 | foo.method17() 120 | foo.method18() 121 | foo.method19() 122 | foo.method20() 123 | foo.method21() 124 | foo.method22() 125 | foo.method23() 126 | foo.method24() 127 | foo.method25() 128 | foo.method26() 129 | foo.method27() 130 | foo.method28() 131 | foo.method29() 132 | i = i + 1 133 | 134 | print(time.process_time() - start) 135 | -------------------------------------------------------------------------------- /tests/integration/limit/too_many_locals.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | // var v00; First slot already taken. 3 | 4 | var v01; var v02; var v03; var v04; var v05; var v06; var v07; 5 | var v08; var v09; var v0a; var v0b; var v0c; var v0d; var v0e; var v0f; 6 | 7 | var v10; var v11; var v12; var v13; var v14; var v15; var v16; var v17; 8 | var v18; var v19; var v1a; var v1b; var v1c; var v1d; var v1e; var v1f; 9 | 10 | var v20; var v21; var v22; var v23; var v24; var v25; var v26; var v27; 11 | var v28; var v29; var v2a; var v2b; var v2c; var v2d; var v2e; var v2f; 12 | 13 | var v30; var v31; var v32; var v33; var v34; var v35; var v36; var v37; 14 | var v38; var v39; var v3a; var v3b; var v3c; var v3d; var v3e; var v3f; 15 | 16 | var v40; var v41; var v42; var v43; var v44; var v45; var v46; var v47; 17 | var v48; var v49; var v4a; var v4b; var v4c; var v4d; var v4e; var v4f; 18 | 19 | var v50; var v51; var v52; var v53; var v54; var v55; var v56; var v57; 20 | var v58; var v59; var v5a; var v5b; var v5c; var v5d; var v5e; var v5f; 21 | 22 | var v60; var v61; var v62; var v63; var v64; var v65; var v66; var v67; 23 | var v68; var v69; var v6a; var v6b; var v6c; var v6d; var v6e; var v6f; 24 | 25 | var v70; var v71; var v72; var v73; var v74; var v75; var v76; var v77; 26 | var v78; var v79; var v7a; var v7b; var v7c; var v7d; var v7e; var v7f; 27 | 28 | var v80; var v81; var v82; var v83; var v84; var v85; var v86; var v87; 29 | var v88; var v89; var v8a; var v8b; var v8c; var v8d; var v8e; var v8f; 30 | 31 | var v90; var v91; var v92; var v93; var v94; var v95; var v96; var v97; 32 | var v98; var v99; var v9a; var v9b; var v9c; var v9d; var v9e; var v9f; 33 | 34 | var va0; var va1; var va2; var va3; var va4; var va5; var va6; var va7; 35 | var va8; var va9; var vaa; var vab; var vac; var vad; var vae; var vaf; 36 | 37 | var vb0; var vb1; var vb2; var vb3; var vb4; var vb5; var vb6; var vb7; 38 | var vb8; var vb9; var vba; var vbb; var vbc; var vbd; var vbe; var vbf; 39 | 40 | var vc0; var vc1; var vc2; var vc3; var vc4; var vc5; var vc6; var vc7; 41 | var vc8; var vc9; var vca; var vcb; var vcc; var vcd; var vce; var vcf; 42 | 43 | var vd0; var vd1; var vd2; var vd3; var vd4; var vd5; var vd6; var vd7; 44 | var vd8; var vd9; var vda; var vdb; var vdc; var vdd; var vde; var vdf; 45 | 46 | var ve0; var ve1; var ve2; var ve3; var ve4; var ve5; var ve6; var ve7; 47 | var ve8; var ve9; var vea; var veb; var vec; var ved; var vee; var vef; 48 | 49 | var vf0; var vf1; var vf2; var vf3; var vf4; var vf5; var vf6; var vf7; 50 | var vf8; var vf9; var vfa; var vfb; var vfc; var vfd; var vfe; var vff; 51 | 52 | var oops; // Error at 'oops': Too many local variables in function. 53 | } 54 | -------------------------------------------------------------------------------- /tests/benchmarks/lox/properties.lox: -------------------------------------------------------------------------------- 1 | // This benchmark stresses both field and method lookup. 2 | 3 | class Foo { 4 | init() { 5 | this.field0 = 1; 6 | this.field1 = 1; 7 | this.field2 = 1; 8 | this.field3 = 1; 9 | this.field4 = 1; 10 | this.field5 = 1; 11 | this.field6 = 1; 12 | this.field7 = 1; 13 | this.field8 = 1; 14 | this.field9 = 1; 15 | this.field10 = 1; 16 | this.field11 = 1; 17 | this.field12 = 1; 18 | this.field13 = 1; 19 | this.field14 = 1; 20 | this.field15 = 1; 21 | this.field16 = 1; 22 | this.field17 = 1; 23 | this.field18 = 1; 24 | this.field19 = 1; 25 | this.field20 = 1; 26 | this.field21 = 1; 27 | this.field22 = 1; 28 | this.field23 = 1; 29 | this.field24 = 1; 30 | this.field25 = 1; 31 | this.field26 = 1; 32 | this.field27 = 1; 33 | this.field28 = 1; 34 | this.field29 = 1; 35 | } 36 | 37 | method0() { return this.field0; } 38 | method1() { return this.field1; } 39 | method2() { return this.field2; } 40 | method3() { return this.field3; } 41 | method4() { return this.field4; } 42 | method5() { return this.field5; } 43 | method6() { return this.field6; } 44 | method7() { return this.field7; } 45 | method8() { return this.field8; } 46 | method9() { return this.field9; } 47 | method10() { return this.field10; } 48 | method11() { return this.field11; } 49 | method12() { return this.field12; } 50 | method13() { return this.field13; } 51 | method14() { return this.field14; } 52 | method15() { return this.field15; } 53 | method16() { return this.field16; } 54 | method17() { return this.field17; } 55 | method18() { return this.field18; } 56 | method19() { return this.field19; } 57 | method20() { return this.field20; } 58 | method21() { return this.field21; } 59 | method22() { return this.field22; } 60 | method23() { return this.field23; } 61 | method24() { return this.field24; } 62 | method25() { return this.field25; } 63 | method26() { return this.field26; } 64 | method27() { return this.field27; } 65 | method28() { return this.field28; } 66 | method29() { return this.field29; } 67 | } 68 | 69 | var foo = Foo(); 70 | var start = clock(); 71 | var i = 0; 72 | while (i < 500000) { 73 | foo.method0(); 74 | foo.method1(); 75 | foo.method2(); 76 | foo.method3(); 77 | foo.method4(); 78 | foo.method5(); 79 | foo.method6(); 80 | foo.method7(); 81 | foo.method8(); 82 | foo.method9(); 83 | foo.method10(); 84 | foo.method11(); 85 | foo.method12(); 86 | foo.method13(); 87 | foo.method14(); 88 | foo.method15(); 89 | foo.method16(); 90 | foo.method17(); 91 | foo.method18(); 92 | foo.method19(); 93 | foo.method20(); 94 | foo.method21(); 95 | foo.method22(); 96 | foo.method23(); 97 | foo.method24(); 98 | foo.method25(); 99 | foo.method26(); 100 | foo.method27(); 101 | foo.method28(); 102 | foo.method29(); 103 | i = i + 1; 104 | } 105 | 106 | print clock() - start; 107 | -------------------------------------------------------------------------------- /tests/benchmarks/perl/properties.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use v5.10; 4 | 5 | use Time::HiRes qw(clock); 6 | 7 | { 8 | package Foo; 9 | 10 | sub new { 11 | my $class = shift; 12 | my $self = { 13 | field0 => 1, 14 | field1 => 1, 15 | field2 => 1, 16 | field3 => 1, 17 | field4 => 1, 18 | field5 => 1, 19 | field6 => 1, 20 | field7 => 1, 21 | field8 => 1, 22 | field9 => 1, 23 | field10 => 1, 24 | field11 => 1, 25 | field12 => 1, 26 | field13 => 1, 27 | field14 => 1, 28 | field15 => 1, 29 | field16 => 1, 30 | field17 => 1, 31 | field18 => 1, 32 | field19 => 1, 33 | field20 => 1, 34 | field21 => 1, 35 | field22 => 1, 36 | field23 => 1, 37 | field24 => 1, 38 | field25 => 1, 39 | field26 => 1, 40 | field27 => 1, 41 | field28 => 1, 42 | field29 => 1, 43 | }; 44 | 45 | bless $self => $class; 46 | return $self; 47 | } 48 | 49 | sub method0 { return $_[0]->{field0}; } 50 | sub method1 { return $_[0]->{field1}; } 51 | sub method2 { return $_[0]->{field2}; } 52 | sub method3 { return $_[0]->{field3}; } 53 | sub method4 { return $_[0]->{field4}; } 54 | sub method5 { return $_[0]->{field5}; } 55 | sub method6 { return $_[0]->{field6}; } 56 | sub method7 { return $_[0]->{field7}; } 57 | sub method8 { return $_[0]->{field8}; } 58 | sub method9 { return $_[0]->{field9}; } 59 | sub method10 { return $_[0]->{field10}; } 60 | sub method11 { return $_[0]->{field11}; } 61 | sub method12 { return $_[0]->{field12}; } 62 | sub method13 { return $_[0]->{field13}; } 63 | sub method14 { return $_[0]->{field14}; } 64 | sub method15 { return $_[0]->{field15}; } 65 | sub method16 { return $_[0]->{field16}; } 66 | sub method17 { return $_[0]->{field17}; } 67 | sub method18 { return $_[0]->{field18}; } 68 | sub method19 { return $_[0]->{field19}; } 69 | sub method20 { return $_[0]->{field20}; } 70 | sub method21 { return $_[0]->{field21}; } 71 | sub method22 { return $_[0]->{field22}; } 72 | sub method23 { return $_[0]->{field23}; } 73 | sub method24 { return $_[0]->{field24}; } 74 | sub method25 { return $_[0]->{field25}; } 75 | sub method26 { return $_[0]->{field26}; } 76 | sub method27 { return $_[0]->{field27}; } 77 | sub method28 { return $_[0]->{field28}; } 78 | sub method29 { return $_[0]->{field29}; } 79 | } 80 | 81 | my $foo = Foo->new(); 82 | my $start = clock(); 83 | my $i = 0; 84 | while ($i < 500000) { 85 | $foo->method0(); 86 | $foo->method1(); 87 | $foo->method2(); 88 | $foo->method3(); 89 | $foo->method4(); 90 | $foo->method5(); 91 | $foo->method6(); 92 | $foo->method7(); 93 | $foo->method8(); 94 | $foo->method9(); 95 | $foo->method10(); 96 | $foo->method11(); 97 | $foo->method12(); 98 | $foo->method13(); 99 | $foo->method14(); 100 | $foo->method15(); 101 | $foo->method16(); 102 | $foo->method17(); 103 | $foo->method18(); 104 | $foo->method19(); 105 | $foo->method20(); 106 | $foo->method21(); 107 | $foo->method22(); 108 | $foo->method23(); 109 | $foo->method24(); 110 | $foo->method25(); 111 | $foo->method26(); 112 | $foo->method27(); 113 | $foo->method28(); 114 | $foo->method29(); 115 | $i++; 116 | } 117 | 118 | say clock() - $start; 119 | -------------------------------------------------------------------------------- /tests/integration/function/too_many_parameters.lox: -------------------------------------------------------------------------------- 1 | // 256 parameters. 2 | fun f( 3 | a1, 4 | a2, 5 | a3, 6 | a4, 7 | a5, 8 | a6, 9 | a7, 10 | a8, 11 | a9, 12 | a10, 13 | a11, 14 | a12, 15 | a13, 16 | a14, 17 | a15, 18 | a16, 19 | a17, 20 | a18, 21 | a19, 22 | a20, 23 | a21, 24 | a22, 25 | a23, 26 | a24, 27 | a25, 28 | a26, 29 | a27, 30 | a28, 31 | a29, 32 | a30, 33 | a31, 34 | a32, 35 | a33, 36 | a34, 37 | a35, 38 | a36, 39 | a37, 40 | a38, 41 | a39, 42 | a40, 43 | a41, 44 | a42, 45 | a43, 46 | a44, 47 | a45, 48 | a46, 49 | a47, 50 | a48, 51 | a49, 52 | a50, 53 | a51, 54 | a52, 55 | a53, 56 | a54, 57 | a55, 58 | a56, 59 | a57, 60 | a58, 61 | a59, 62 | a60, 63 | a61, 64 | a62, 65 | a63, 66 | a64, 67 | a65, 68 | a66, 69 | a67, 70 | a68, 71 | a69, 72 | a70, 73 | a71, 74 | a72, 75 | a73, 76 | a74, 77 | a75, 78 | a76, 79 | a77, 80 | a78, 81 | a79, 82 | a80, 83 | a81, 84 | a82, 85 | a83, 86 | a84, 87 | a85, 88 | a86, 89 | a87, 90 | a88, 91 | a89, 92 | a90, 93 | a91, 94 | a92, 95 | a93, 96 | a94, 97 | a95, 98 | a96, 99 | a97, 100 | a98, 101 | a99, 102 | a100, 103 | a101, 104 | a102, 105 | a103, 106 | a104, 107 | a105, 108 | a106, 109 | a107, 110 | a108, 111 | a109, 112 | a110, 113 | a111, 114 | a112, 115 | a113, 116 | a114, 117 | a115, 118 | a116, 119 | a117, 120 | a118, 121 | a119, 122 | a120, 123 | a121, 124 | a122, 125 | a123, 126 | a124, 127 | a125, 128 | a126, 129 | a127, 130 | a128, 131 | a129, 132 | a130, 133 | a131, 134 | a132, 135 | a133, 136 | a134, 137 | a135, 138 | a136, 139 | a137, 140 | a138, 141 | a139, 142 | a140, 143 | a141, 144 | a142, 145 | a143, 146 | a144, 147 | a145, 148 | a146, 149 | a147, 150 | a148, 151 | a149, 152 | a150, 153 | a151, 154 | a152, 155 | a153, 156 | a154, 157 | a155, 158 | a156, 159 | a157, 160 | a158, 161 | a159, 162 | a160, 163 | a161, 164 | a162, 165 | a163, 166 | a164, 167 | a165, 168 | a166, 169 | a167, 170 | a168, 171 | a169, 172 | a170, 173 | a171, 174 | a172, 175 | a173, 176 | a174, 177 | a175, 178 | a176, 179 | a177, 180 | a178, 181 | a179, 182 | a180, 183 | a181, 184 | a182, 185 | a183, 186 | a184, 187 | a185, 188 | a186, 189 | a187, 190 | a188, 191 | a189, 192 | a190, 193 | a191, 194 | a192, 195 | a193, 196 | a194, 197 | a195, 198 | a196, 199 | a197, 200 | a198, 201 | a199, 202 | a200, 203 | a201, 204 | a202, 205 | a203, 206 | a204, 207 | a205, 208 | a206, 209 | a207, 210 | a208, 211 | a209, 212 | a210, 213 | a211, 214 | a212, 215 | a213, 216 | a214, 217 | a215, 218 | a216, 219 | a217, 220 | a218, 221 | a219, 222 | a220, 223 | a221, 224 | a222, 225 | a223, 226 | a224, 227 | a225, 228 | a226, 229 | a227, 230 | a228, 231 | a229, 232 | a230, 233 | a231, 234 | a232, 235 | a233, 236 | a234, 237 | a235, 238 | a236, 239 | a237, 240 | a238, 241 | a239, 242 | a240, 243 | a241, 244 | a242, 245 | a243, 246 | a244, 247 | a245, 248 | a246, 249 | a247, 250 | a248, 251 | a249, 252 | a250, 253 | a251, 254 | a252, 255 | a253, 256 | a254, 257 | a255, a) {} // Error at 'a': Can't have more than 255 parameters. 258 | -------------------------------------------------------------------------------- /tests/benchmarks/python/properties.py: -------------------------------------------------------------------------------- 1 | # This benchmark stresses both field and method lookup. 2 | 3 | import time 4 | 5 | 6 | class Foo: 7 | def __init__(self): 8 | self.field0 = 1 9 | self.field1 = 1 10 | self.field2 = 1 11 | self.field3 = 1 12 | self.field4 = 1 13 | self.field5 = 1 14 | self.field6 = 1 15 | self.field7 = 1 16 | self.field8 = 1 17 | self.field9 = 1 18 | self.field10 = 1 19 | self.field11 = 1 20 | self.field12 = 1 21 | self.field13 = 1 22 | self.field14 = 1 23 | self.field15 = 1 24 | self.field16 = 1 25 | self.field17 = 1 26 | self.field18 = 1 27 | self.field19 = 1 28 | self.field20 = 1 29 | self.field21 = 1 30 | self.field22 = 1 31 | self.field23 = 1 32 | self.field24 = 1 33 | self.field25 = 1 34 | self.field26 = 1 35 | self.field27 = 1 36 | self.field28 = 1 37 | self.field29 = 1 38 | 39 | def method0(self): 40 | return self.field0 41 | 42 | def method1(self): 43 | return self.field1 44 | 45 | def method2(self): 46 | return self.field2 47 | 48 | def method3(self): 49 | return self.field3 50 | 51 | def method4(self): 52 | return self.field4 53 | 54 | def method5(self): 55 | return self.field5 56 | 57 | def method6(self): 58 | return self.field6 59 | 60 | def method7(self): 61 | return self.field7 62 | 63 | def method8(self): 64 | return self.field8 65 | 66 | def method9(self): 67 | return self.field9 68 | 69 | def method10(self): 70 | return self.field10 71 | 72 | def method11(self): 73 | return self.field11 74 | 75 | def method12(self): 76 | return self.field12 77 | 78 | def method13(self): 79 | return self.field13 80 | 81 | def method14(self): 82 | return self.field14 83 | 84 | def method15(self): 85 | return self.field15 86 | 87 | def method16(self): 88 | return self.field16 89 | 90 | def method17(self): 91 | return self.field17 92 | 93 | def method18(self): 94 | return self.field18 95 | 96 | def method19(self): 97 | return self.field19 98 | 99 | def method20(self): 100 | return self.field20 101 | 102 | def method21(self): 103 | return self.field21 104 | 105 | def method22(self): 106 | return self.field22 107 | 108 | def method23(self): 109 | return self.field23 110 | 111 | def method24(self): 112 | return self.field24 113 | 114 | def method25(self): 115 | return self.field25 116 | 117 | def method26(self): 118 | return self.field26 119 | 120 | def method27(self): 121 | return self.field27 122 | 123 | def method28(self): 124 | return self.field28 125 | 126 | def method29(self): 127 | return self.field29 128 | 129 | 130 | foo = Foo() 131 | start = time.process_time() 132 | i = 0 133 | 134 | while i < 500000: 135 | foo.method0() 136 | foo.method1() 137 | foo.method2() 138 | foo.method3() 139 | foo.method4() 140 | foo.method5() 141 | foo.method6() 142 | foo.method7() 143 | foo.method8() 144 | foo.method9() 145 | foo.method10() 146 | foo.method11() 147 | foo.method12() 148 | foo.method13() 149 | foo.method14() 150 | foo.method15() 151 | foo.method16() 152 | foo.method17() 153 | foo.method18() 154 | foo.method19() 155 | foo.method20() 156 | foo.method21() 157 | foo.method22() 158 | foo.method23() 159 | foo.method24() 160 | foo.method25() 161 | foo.method26() 162 | foo.method27() 163 | foo.method28() 164 | foo.method29() 165 | i = i + 1 166 | 167 | print(time.process_time() - start) 168 | -------------------------------------------------------------------------------- /tests/integration/method/too_many_parameters.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | // 256 parameters. 3 | method( 4 | a1, 5 | a2, 6 | a3, 7 | a4, 8 | a5, 9 | a6, 10 | a7, 11 | a8, 12 | a9, 13 | a10, 14 | a11, 15 | a12, 16 | a13, 17 | a14, 18 | a15, 19 | a16, 20 | a17, 21 | a18, 22 | a19, 23 | a20, 24 | a21, 25 | a22, 26 | a23, 27 | a24, 28 | a25, 29 | a26, 30 | a27, 31 | a28, 32 | a29, 33 | a30, 34 | a31, 35 | a32, 36 | a33, 37 | a34, 38 | a35, 39 | a36, 40 | a37, 41 | a38, 42 | a39, 43 | a40, 44 | a41, 45 | a42, 46 | a43, 47 | a44, 48 | a45, 49 | a46, 50 | a47, 51 | a48, 52 | a49, 53 | a50, 54 | a51, 55 | a52, 56 | a53, 57 | a54, 58 | a55, 59 | a56, 60 | a57, 61 | a58, 62 | a59, 63 | a60, 64 | a61, 65 | a62, 66 | a63, 67 | a64, 68 | a65, 69 | a66, 70 | a67, 71 | a68, 72 | a69, 73 | a70, 74 | a71, 75 | a72, 76 | a73, 77 | a74, 78 | a75, 79 | a76, 80 | a77, 81 | a78, 82 | a79, 83 | a80, 84 | a81, 85 | a82, 86 | a83, 87 | a84, 88 | a85, 89 | a86, 90 | a87, 91 | a88, 92 | a89, 93 | a90, 94 | a91, 95 | a92, 96 | a93, 97 | a94, 98 | a95, 99 | a96, 100 | a97, 101 | a98, 102 | a99, 103 | a100, 104 | a101, 105 | a102, 106 | a103, 107 | a104, 108 | a105, 109 | a106, 110 | a107, 111 | a108, 112 | a109, 113 | a110, 114 | a111, 115 | a112, 116 | a113, 117 | a114, 118 | a115, 119 | a116, 120 | a117, 121 | a118, 122 | a119, 123 | a120, 124 | a121, 125 | a122, 126 | a123, 127 | a124, 128 | a125, 129 | a126, 130 | a127, 131 | a128, 132 | a129, 133 | a130, 134 | a131, 135 | a132, 136 | a133, 137 | a134, 138 | a135, 139 | a136, 140 | a137, 141 | a138, 142 | a139, 143 | a140, 144 | a141, 145 | a142, 146 | a143, 147 | a144, 148 | a145, 149 | a146, 150 | a147, 151 | a148, 152 | a149, 153 | a150, 154 | a151, 155 | a152, 156 | a153, 157 | a154, 158 | a155, 159 | a156, 160 | a157, 161 | a158, 162 | a159, 163 | a160, 164 | a161, 165 | a162, 166 | a163, 167 | a164, 168 | a165, 169 | a166, 170 | a167, 171 | a168, 172 | a169, 173 | a170, 174 | a171, 175 | a172, 176 | a173, 177 | a174, 178 | a175, 179 | a176, 180 | a177, 181 | a178, 182 | a179, 183 | a180, 184 | a181, 185 | a182, 186 | a183, 187 | a184, 188 | a185, 189 | a186, 190 | a187, 191 | a188, 192 | a189, 193 | a190, 194 | a191, 195 | a192, 196 | a193, 197 | a194, 198 | a195, 199 | a196, 200 | a197, 201 | a198, 202 | a199, 203 | a200, 204 | a201, 205 | a202, 206 | a203, 207 | a204, 208 | a205, 209 | a206, 210 | a207, 211 | a208, 212 | a209, 213 | a210, 214 | a211, 215 | a212, 216 | a213, 217 | a214, 218 | a215, 219 | a216, 220 | a217, 221 | a218, 222 | a219, 223 | a220, 224 | a221, 225 | a222, 226 | a223, 227 | a224, 228 | a225, 229 | a226, 230 | a227, 231 | a228, 232 | a229, 233 | a230, 234 | a231, 235 | a232, 236 | a233, 237 | a234, 238 | a235, 239 | a236, 240 | a237, 241 | a238, 242 | a239, 243 | a240, 244 | a241, 245 | a242, 246 | a243, 247 | a244, 248 | a245, 249 | a246, 250 | a247, 251 | a248, 252 | a249, 253 | a250, 254 | a251, 255 | a252, 256 | a253, 257 | a254, 258 | a255, a) {} // Error at 'a': Can't have more than 255 parameters. 259 | } 260 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "cpu-time" 16 | version = "1.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e9e393a7668fe1fad3075085b86c781883000b4ede868f43627b34a87c8b7ded" 19 | dependencies = [ 20 | "libc", 21 | "winapi", 22 | ] 23 | 24 | [[package]] 25 | name = "glob" 26 | version = "0.3.0" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" 29 | 30 | [[package]] 31 | name = "libc" 32 | version = "0.2.108" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" 35 | 36 | [[package]] 37 | name = "loxido" 38 | version = "0.1.0" 39 | dependencies = [ 40 | "cpu-time", 41 | "regex", 42 | "test-generator", 43 | ] 44 | 45 | [[package]] 46 | name = "memchr" 47 | version = "2.4.1" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 50 | 51 | [[package]] 52 | name = "proc-macro2" 53 | version = "0.4.30" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 56 | dependencies = [ 57 | "unicode-xid", 58 | ] 59 | 60 | [[package]] 61 | name = "quote" 62 | version = "0.6.13" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 65 | dependencies = [ 66 | "proc-macro2", 67 | ] 68 | 69 | [[package]] 70 | name = "regex" 71 | version = "1.5.4" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 74 | dependencies = [ 75 | "aho-corasick", 76 | "memchr", 77 | "regex-syntax", 78 | ] 79 | 80 | [[package]] 81 | name = "regex-syntax" 82 | version = "0.6.25" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 85 | 86 | [[package]] 87 | name = "syn" 88 | version = "0.15.44" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" 91 | dependencies = [ 92 | "proc-macro2", 93 | "quote", 94 | "unicode-xid", 95 | ] 96 | 97 | [[package]] 98 | name = "test-generator" 99 | version = "0.3.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "ea97be90349ab3574f6e74d1566e1c5dd3a4bc74b89f4af4cc10ca010af103c0" 102 | dependencies = [ 103 | "glob", 104 | "proc-macro2", 105 | "quote", 106 | "syn", 107 | ] 108 | 109 | [[package]] 110 | name = "unicode-xid" 111 | version = "0.1.0" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 114 | 115 | [[package]] 116 | name = "winapi" 117 | version = "0.3.9" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 120 | dependencies = [ 121 | "winapi-i686-pc-windows-gnu", 122 | "winapi-x86_64-pc-windows-gnu", 123 | ] 124 | 125 | [[package]] 126 | name = "winapi-i686-pc-windows-gnu" 127 | version = "0.4.0" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 130 | 131 | [[package]] 132 | name = "winapi-x86_64-pc-windows-gnu" 133 | version = "0.4.0" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 136 | -------------------------------------------------------------------------------- /tests/integration/limit/too_many_upvalues.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | var v00; var v01; var v02; var v03; var v04; var v05; var v06; var v07; 3 | var v08; var v09; var v0a; var v0b; var v0c; var v0d; var v0e; var v0f; 4 | 5 | var v10; var v11; var v12; var v13; var v14; var v15; var v16; var v17; 6 | var v18; var v19; var v1a; var v1b; var v1c; var v1d; var v1e; var v1f; 7 | 8 | var v20; var v21; var v22; var v23; var v24; var v25; var v26; var v27; 9 | var v28; var v29; var v2a; var v2b; var v2c; var v2d; var v2e; var v2f; 10 | 11 | var v30; var v31; var v32; var v33; var v34; var v35; var v36; var v37; 12 | var v38; var v39; var v3a; var v3b; var v3c; var v3d; var v3e; var v3f; 13 | 14 | var v40; var v41; var v42; var v43; var v44; var v45; var v46; var v47; 15 | var v48; var v49; var v4a; var v4b; var v4c; var v4d; var v4e; var v4f; 16 | 17 | var v50; var v51; var v52; var v53; var v54; var v55; var v56; var v57; 18 | var v58; var v59; var v5a; var v5b; var v5c; var v5d; var v5e; var v5f; 19 | 20 | var v60; var v61; var v62; var v63; var v64; var v65; var v66; var v67; 21 | var v68; var v69; var v6a; var v6b; var v6c; var v6d; var v6e; var v6f; 22 | 23 | var v70; var v71; var v72; var v73; var v74; var v75; var v76; var v77; 24 | var v78; var v79; var v7a; var v7b; var v7c; var v7d; var v7e; var v7f; 25 | 26 | fun g() { 27 | var v80; var v81; var v82; var v83; var v84; var v85; var v86; var v87; 28 | var v88; var v89; var v8a; var v8b; var v8c; var v8d; var v8e; var v8f; 29 | 30 | var v90; var v91; var v92; var v93; var v94; var v95; var v96; var v97; 31 | var v98; var v99; var v9a; var v9b; var v9c; var v9d; var v9e; var v9f; 32 | 33 | var va0; var va1; var va2; var va3; var va4; var va5; var va6; var va7; 34 | var va8; var va9; var vaa; var vab; var vac; var vad; var vae; var vaf; 35 | 36 | var vb0; var vb1; var vb2; var vb3; var vb4; var vb5; var vb6; var vb7; 37 | var vb8; var vb9; var vba; var vbb; var vbc; var vbd; var vbe; var vbf; 38 | 39 | var vc0; var vc1; var vc2; var vc3; var vc4; var vc5; var vc6; var vc7; 40 | var vc8; var vc9; var vca; var vcb; var vcc; var vcd; var vce; var vcf; 41 | 42 | var vd0; var vd1; var vd2; var vd3; var vd4; var vd5; var vd6; var vd7; 43 | var vd8; var vd9; var vda; var vdb; var vdc; var vdd; var vde; var vdf; 44 | 45 | var ve0; var ve1; var ve2; var ve3; var ve4; var ve5; var ve6; var ve7; 46 | var ve8; var ve9; var vea; var veb; var vec; var ved; var vee; var vef; 47 | 48 | var vf0; var vf1; var vf2; var vf3; var vf4; var vf5; var vf6; var vf7; 49 | var vf8; var vf9; var vfa; var vfb; var vfc; var vfd; var vfe; var vff; 50 | 51 | var oops; 52 | 53 | fun h() { 54 | v00; v01; v02; v03; v04; v05; v06; v07; 55 | v08; v09; v0a; v0b; v0c; v0d; v0e; v0f; 56 | 57 | v10; v11; v12; v13; v14; v15; v16; v17; 58 | v18; v19; v1a; v1b; v1c; v1d; v1e; v1f; 59 | 60 | v20; v21; v22; v23; v24; v25; v26; v27; 61 | v28; v29; v2a; v2b; v2c; v2d; v2e; v2f; 62 | 63 | v30; v31; v32; v33; v34; v35; v36; v37; 64 | v38; v39; v3a; v3b; v3c; v3d; v3e; v3f; 65 | 66 | v40; v41; v42; v43; v44; v45; v46; v47; 67 | v48; v49; v4a; v4b; v4c; v4d; v4e; v4f; 68 | 69 | v50; v51; v52; v53; v54; v55; v56; v57; 70 | v58; v59; v5a; v5b; v5c; v5d; v5e; v5f; 71 | 72 | v60; v61; v62; v63; v64; v65; v66; v67; 73 | v68; v69; v6a; v6b; v6c; v6d; v6e; v6f; 74 | 75 | v70; v71; v72; v73; v74; v75; v76; v77; 76 | v78; v79; v7a; v7b; v7c; v7d; v7e; v7f; 77 | 78 | v80; v81; v82; v83; v84; v85; v86; v87; 79 | v88; v89; v8a; v8b; v8c; v8d; v8e; v8f; 80 | 81 | v90; v91; v92; v93; v94; v95; v96; v97; 82 | v98; v99; v9a; v9b; v9c; v9d; v9e; v9f; 83 | 84 | va0; va1; va2; va3; va4; va5; va6; va7; 85 | va8; va9; vaa; vab; vac; vad; vae; vaf; 86 | 87 | vb0; vb1; vb2; vb3; vb4; vb5; vb6; vb7; 88 | vb8; vb9; vba; vbb; vbc; vbd; vbe; vbf; 89 | 90 | vc0; vc1; vc2; vc3; vc4; vc5; vc6; vc7; 91 | vc8; vc9; vca; vcb; vcc; vcd; vce; vcf; 92 | 93 | vd0; vd1; vd2; vd3; vd4; vd5; vd6; vd7; 94 | vd8; vd9; vda; vdb; vdc; vdd; vde; vdf; 95 | 96 | ve0; ve1; ve2; ve3; ve4; ve5; ve6; ve7; 97 | ve8; ve9; vea; veb; vec; ved; vee; vef; 98 | 99 | vf0; vf1; vf2; vf3; vf4; vf5; vf6; vf7; 100 | vf8; vf9; vfa; vfb; vfc; vfd; vfe; vff; 101 | 102 | oops; // Error at 'oops': Too many closure variables in function. 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /tests/integration.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | use std::{env, fs, process::Command}; 3 | 4 | use regex::Regex; 5 | 6 | extern crate test_generator; 7 | 8 | use test_generator::test_resources; 9 | 10 | fn loxido_command() -> Command { 11 | // Create full path to binary 12 | let mut path = env::current_exe() 13 | .unwrap() 14 | .parent() 15 | .unwrap() 16 | .parent() 17 | .unwrap() 18 | .to_owned(); 19 | path.push(env!("CARGO_PKG_NAME")); 20 | path.set_extension(env::consts::EXE_EXTENSION); 21 | Command::new(path.into_os_string()) 22 | } 23 | 24 | struct RuntimeError { 25 | line_prefix: String, 26 | message: String, 27 | } 28 | 29 | struct Expected { 30 | out: Vec, 31 | compile_err: Vec, 32 | runtime_err: Option, 33 | } 34 | 35 | fn parse_comments(path: &PathBuf) -> Expected { 36 | let output_re = Regex::new(r"// expect: ?(.*)").unwrap(); 37 | let error_re = Regex::new(r"// (Error.*)").unwrap(); 38 | let error_line_re = Regex::new(r"// \[(?:c )?line (\d+)\] (Error.*)").unwrap(); 39 | let runtime_error_re = Regex::new(r"// expect runtime error: (.+)").unwrap(); 40 | 41 | let mut expected = Expected { 42 | out: vec![], 43 | compile_err: vec![], 44 | runtime_err: None, 45 | }; 46 | 47 | println!("{}", path.display()); 48 | let content = fs::read_to_string(path).unwrap(); 49 | for (i, line) in content.lines().enumerate() { 50 | if let Some(m) = output_re.captures(line) { 51 | let s = m.get(1).unwrap().as_str().to_owned(); 52 | expected.out.push(s); 53 | } 54 | if let Some(m) = error_line_re.captures(line) { 55 | let line = m.get(1).unwrap().as_str(); 56 | let msg = m.get(2).unwrap().as_str(); 57 | let s = format!("[line {}] {}", line, msg); 58 | expected.compile_err.push(s); 59 | } 60 | if let Some(m) = error_re.captures(line) { 61 | let msg = m.get(1).unwrap().as_str(); 62 | let s = format!("[line {}] {}", i + 1, msg); 63 | expected.compile_err.push(s); 64 | } 65 | if let Some(m) = runtime_error_re.captures(line) { 66 | let message = m.get(1).unwrap().as_str().to_owned(); 67 | let line_prefix = format!("[line {}]", i + 1); 68 | expected.runtime_err = Some(RuntimeError { 69 | line_prefix, 70 | message, 71 | }); 72 | } 73 | } 74 | expected 75 | } 76 | 77 | #[test_resources("tests/integration/*/*.lox")] 78 | fn run_file_test(filename: &str) { 79 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 80 | path.push(filename); 81 | let expected = parse_comments(&path); 82 | 83 | let output = loxido_command().arg(path).output().unwrap(); 84 | 85 | let out: Vec = String::from_utf8(output.stdout) 86 | .unwrap() 87 | .lines() 88 | .map(|x| x.to_owned()) 89 | .collect(); 90 | let err: Vec = String::from_utf8(output.stderr) 91 | .unwrap() 92 | .lines() 93 | .map(|x| x.to_owned()) 94 | .collect(); 95 | 96 | match ( 97 | expected.runtime_err.is_none(), 98 | expected.compile_err.is_empty(), 99 | ) { 100 | (true, true) => assert!( 101 | output.status.success(), 102 | "Program exited with failure, expected success" 103 | ), 104 | (false, true) => assert_eq!( 105 | output.status.code().unwrap(), 106 | 70, 107 | "Runtime errors should have error code 70" 108 | ), 109 | (true, false) => assert_eq!( 110 | output.status.code().unwrap(), 111 | 65, 112 | "Compile errors should have error code 65" 113 | ), 114 | (false, false) => panic!("Simultaneous error and compile error"), 115 | } 116 | 117 | if let Some(e) = expected.runtime_err { 118 | assert_eq!(e.message, err[0], "Runtime error should match"); 119 | assert_eq!( 120 | err[1][0..e.line_prefix.len()], 121 | e.line_prefix, 122 | "Runtime error line should match" 123 | ); 124 | } else { 125 | if !err.is_empty() { 126 | assert_eq!( 127 | output.status.code().unwrap(), 128 | 65, 129 | "Compile errors should have error code 65" 130 | ); 131 | } 132 | assert_eq!(expected.compile_err, err, "Compile error should match"); 133 | } 134 | 135 | assert_eq!(expected.out, out, "Output should match"); 136 | } 137 | -------------------------------------------------------------------------------- /tests/integration/method/too_many_arguments.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = 1; 3 | true.method( 4 | a, // 1 5 | a, // 2 6 | a, // 3 7 | a, // 4 8 | a, // 5 9 | a, // 6 10 | a, // 7 11 | a, // 8 12 | a, // 9 13 | a, // 10 14 | a, // 11 15 | a, // 12 16 | a, // 13 17 | a, // 14 18 | a, // 15 19 | a, // 16 20 | a, // 17 21 | a, // 18 22 | a, // 19 23 | a, // 20 24 | a, // 21 25 | a, // 22 26 | a, // 23 27 | a, // 24 28 | a, // 25 29 | a, // 26 30 | a, // 27 31 | a, // 28 32 | a, // 29 33 | a, // 30 34 | a, // 31 35 | a, // 32 36 | a, // 33 37 | a, // 34 38 | a, // 35 39 | a, // 36 40 | a, // 37 41 | a, // 38 42 | a, // 39 43 | a, // 40 44 | a, // 41 45 | a, // 42 46 | a, // 43 47 | a, // 44 48 | a, // 45 49 | a, // 46 50 | a, // 47 51 | a, // 48 52 | a, // 49 53 | a, // 50 54 | a, // 51 55 | a, // 52 56 | a, // 53 57 | a, // 54 58 | a, // 55 59 | a, // 56 60 | a, // 57 61 | a, // 58 62 | a, // 59 63 | a, // 60 64 | a, // 61 65 | a, // 62 66 | a, // 63 67 | a, // 64 68 | a, // 65 69 | a, // 66 70 | a, // 67 71 | a, // 68 72 | a, // 69 73 | a, // 70 74 | a, // 71 75 | a, // 72 76 | a, // 73 77 | a, // 74 78 | a, // 75 79 | a, // 76 80 | a, // 77 81 | a, // 78 82 | a, // 79 83 | a, // 80 84 | a, // 81 85 | a, // 82 86 | a, // 83 87 | a, // 84 88 | a, // 85 89 | a, // 86 90 | a, // 87 91 | a, // 88 92 | a, // 89 93 | a, // 90 94 | a, // 91 95 | a, // 92 96 | a, // 93 97 | a, // 94 98 | a, // 95 99 | a, // 96 100 | a, // 97 101 | a, // 98 102 | a, // 99 103 | a, // 100 104 | a, // 101 105 | a, // 102 106 | a, // 103 107 | a, // 104 108 | a, // 105 109 | a, // 106 110 | a, // 107 111 | a, // 108 112 | a, // 109 113 | a, // 110 114 | a, // 111 115 | a, // 112 116 | a, // 113 117 | a, // 114 118 | a, // 115 119 | a, // 116 120 | a, // 117 121 | a, // 118 122 | a, // 119 123 | a, // 120 124 | a, // 121 125 | a, // 122 126 | a, // 123 127 | a, // 124 128 | a, // 125 129 | a, // 126 130 | a, // 127 131 | a, // 128 132 | a, // 129 133 | a, // 130 134 | a, // 131 135 | a, // 132 136 | a, // 133 137 | a, // 134 138 | a, // 135 139 | a, // 136 140 | a, // 137 141 | a, // 138 142 | a, // 139 143 | a, // 140 144 | a, // 141 145 | a, // 142 146 | a, // 143 147 | a, // 144 148 | a, // 145 149 | a, // 146 150 | a, // 147 151 | a, // 148 152 | a, // 149 153 | a, // 150 154 | a, // 151 155 | a, // 152 156 | a, // 153 157 | a, // 154 158 | a, // 155 159 | a, // 156 160 | a, // 157 161 | a, // 158 162 | a, // 159 163 | a, // 160 164 | a, // 161 165 | a, // 162 166 | a, // 163 167 | a, // 164 168 | a, // 165 169 | a, // 166 170 | a, // 167 171 | a, // 168 172 | a, // 169 173 | a, // 170 174 | a, // 171 175 | a, // 172 176 | a, // 173 177 | a, // 174 178 | a, // 175 179 | a, // 176 180 | a, // 177 181 | a, // 178 182 | a, // 179 183 | a, // 180 184 | a, // 181 185 | a, // 182 186 | a, // 183 187 | a, // 184 188 | a, // 185 189 | a, // 186 190 | a, // 187 191 | a, // 188 192 | a, // 189 193 | a, // 190 194 | a, // 191 195 | a, // 192 196 | a, // 193 197 | a, // 194 198 | a, // 195 199 | a, // 196 200 | a, // 197 201 | a, // 198 202 | a, // 199 203 | a, // 200 204 | a, // 201 205 | a, // 202 206 | a, // 203 207 | a, // 204 208 | a, // 205 209 | a, // 206 210 | a, // 207 211 | a, // 208 212 | a, // 209 213 | a, // 210 214 | a, // 211 215 | a, // 212 216 | a, // 213 217 | a, // 214 218 | a, // 215 219 | a, // 216 220 | a, // 217 221 | a, // 218 222 | a, // 219 223 | a, // 220 224 | a, // 221 225 | a, // 222 226 | a, // 223 227 | a, // 224 228 | a, // 225 229 | a, // 226 230 | a, // 227 231 | a, // 228 232 | a, // 229 233 | a, // 230 234 | a, // 231 235 | a, // 232 236 | a, // 233 237 | a, // 234 238 | a, // 235 239 | a, // 236 240 | a, // 237 241 | a, // 238 242 | a, // 239 243 | a, // 240 244 | a, // 241 245 | a, // 242 246 | a, // 243 247 | a, // 244 248 | a, // 245 249 | a, // 246 250 | a, // 247 251 | a, // 248 252 | a, // 249 253 | a, // 250 254 | a, // 251 255 | a, // 252 256 | a, // 253 257 | a, // 254 258 | a, // 255 259 | a); // Error at 'a': Can't have more than 255 arguments. 260 | } 261 | -------------------------------------------------------------------------------- /tests/integration/function/too_many_arguments.lox: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | { 3 | var a = 1; 4 | foo( 5 | a, // 1 6 | a, // 2 7 | a, // 3 8 | a, // 4 9 | a, // 5 10 | a, // 6 11 | a, // 7 12 | a, // 8 13 | a, // 9 14 | a, // 10 15 | a, // 11 16 | a, // 12 17 | a, // 13 18 | a, // 14 19 | a, // 15 20 | a, // 16 21 | a, // 17 22 | a, // 18 23 | a, // 19 24 | a, // 20 25 | a, // 21 26 | a, // 22 27 | a, // 23 28 | a, // 24 29 | a, // 25 30 | a, // 26 31 | a, // 27 32 | a, // 28 33 | a, // 29 34 | a, // 30 35 | a, // 31 36 | a, // 32 37 | a, // 33 38 | a, // 34 39 | a, // 35 40 | a, // 36 41 | a, // 37 42 | a, // 38 43 | a, // 39 44 | a, // 40 45 | a, // 41 46 | a, // 42 47 | a, // 43 48 | a, // 44 49 | a, // 45 50 | a, // 46 51 | a, // 47 52 | a, // 48 53 | a, // 49 54 | a, // 50 55 | a, // 51 56 | a, // 52 57 | a, // 53 58 | a, // 54 59 | a, // 55 60 | a, // 56 61 | a, // 57 62 | a, // 58 63 | a, // 59 64 | a, // 60 65 | a, // 61 66 | a, // 62 67 | a, // 63 68 | a, // 64 69 | a, // 65 70 | a, // 66 71 | a, // 67 72 | a, // 68 73 | a, // 69 74 | a, // 70 75 | a, // 71 76 | a, // 72 77 | a, // 73 78 | a, // 74 79 | a, // 75 80 | a, // 76 81 | a, // 77 82 | a, // 78 83 | a, // 79 84 | a, // 80 85 | a, // 81 86 | a, // 82 87 | a, // 83 88 | a, // 84 89 | a, // 85 90 | a, // 86 91 | a, // 87 92 | a, // 88 93 | a, // 89 94 | a, // 90 95 | a, // 91 96 | a, // 92 97 | a, // 93 98 | a, // 94 99 | a, // 95 100 | a, // 96 101 | a, // 97 102 | a, // 98 103 | a, // 99 104 | a, // 100 105 | a, // 101 106 | a, // 102 107 | a, // 103 108 | a, // 104 109 | a, // 105 110 | a, // 106 111 | a, // 107 112 | a, // 108 113 | a, // 109 114 | a, // 110 115 | a, // 111 116 | a, // 112 117 | a, // 113 118 | a, // 114 119 | a, // 115 120 | a, // 116 121 | a, // 117 122 | a, // 118 123 | a, // 119 124 | a, // 120 125 | a, // 121 126 | a, // 122 127 | a, // 123 128 | a, // 124 129 | a, // 125 130 | a, // 126 131 | a, // 127 132 | a, // 128 133 | a, // 129 134 | a, // 130 135 | a, // 131 136 | a, // 132 137 | a, // 133 138 | a, // 134 139 | a, // 135 140 | a, // 136 141 | a, // 137 142 | a, // 138 143 | a, // 139 144 | a, // 140 145 | a, // 141 146 | a, // 142 147 | a, // 143 148 | a, // 144 149 | a, // 145 150 | a, // 146 151 | a, // 147 152 | a, // 148 153 | a, // 149 154 | a, // 150 155 | a, // 151 156 | a, // 152 157 | a, // 153 158 | a, // 154 159 | a, // 155 160 | a, // 156 161 | a, // 157 162 | a, // 158 163 | a, // 159 164 | a, // 160 165 | a, // 161 166 | a, // 162 167 | a, // 163 168 | a, // 164 169 | a, // 165 170 | a, // 166 171 | a, // 167 172 | a, // 168 173 | a, // 169 174 | a, // 170 175 | a, // 171 176 | a, // 172 177 | a, // 173 178 | a, // 174 179 | a, // 175 180 | a, // 176 181 | a, // 177 182 | a, // 178 183 | a, // 179 184 | a, // 180 185 | a, // 181 186 | a, // 182 187 | a, // 183 188 | a, // 184 189 | a, // 185 190 | a, // 186 191 | a, // 187 192 | a, // 188 193 | a, // 189 194 | a, // 190 195 | a, // 191 196 | a, // 192 197 | a, // 193 198 | a, // 194 199 | a, // 195 200 | a, // 196 201 | a, // 197 202 | a, // 198 203 | a, // 199 204 | a, // 200 205 | a, // 201 206 | a, // 202 207 | a, // 203 208 | a, // 204 209 | a, // 205 210 | a, // 206 211 | a, // 207 212 | a, // 208 213 | a, // 209 214 | a, // 210 215 | a, // 211 216 | a, // 212 217 | a, // 213 218 | a, // 214 219 | a, // 215 220 | a, // 216 221 | a, // 217 222 | a, // 218 223 | a, // 219 224 | a, // 220 225 | a, // 221 226 | a, // 222 227 | a, // 223 228 | a, // 224 229 | a, // 225 230 | a, // 226 231 | a, // 227 232 | a, // 228 233 | a, // 229 234 | a, // 230 235 | a, // 231 236 | a, // 232 237 | a, // 233 238 | a, // 234 239 | a, // 235 240 | a, // 236 241 | a, // 237 242 | a, // 238 243 | a, // 239 244 | a, // 240 245 | a, // 241 246 | a, // 242 247 | a, // 243 248 | a, // 244 249 | a, // 245 250 | a, // 246 251 | a, // 247 252 | a, // 248 253 | a, // 249 254 | a, // 250 255 | a, // 251 256 | a, // 252 257 | a, // 253 258 | a, // 254 259 | a, // 255 260 | a); // Error at 'a': Can't have more than 255 arguments. 261 | } 262 | -------------------------------------------------------------------------------- /tests/manual/test.lox: -------------------------------------------------------------------------------- 1 | fun assert_eq(a, b) { 2 | if (a != b) { 3 | panic("assertion failed", a, b); 4 | } 5 | } 6 | 7 | fun assert_true(a) { 8 | if (!a) { 9 | panic("assertion failed", a); 10 | } 11 | } 12 | 13 | fun assert_false(a) { 14 | if (a) { 15 | panic("assertion failed", a); 16 | } 17 | } 18 | 19 | fun test_arithmetic() { 20 | print "Testing arithmetic..."; 21 | 22 | assert_eq(2 * 3, 6); 23 | assert_eq(2 + 3, 5); 24 | assert_eq(30 - 20, 10); 25 | assert_eq(100 / 4, 25); 26 | 27 | var a = 25; 28 | var b = 4; 29 | assert_eq(100, a * b); 30 | 31 | assert_eq(100 + 2 * 5, 110); 32 | assert_eq((100 + 2) * 5, 510); 33 | } 34 | 35 | fun test_boolean() { 36 | print "Testing boolean..."; 37 | 38 | assert_true(true); 39 | assert_true(!false); 40 | assert_true(true and true); 41 | assert_false(true and false); 42 | assert_true(true or false); 43 | assert_true(false or true); 44 | assert_true(10 > 5); 45 | assert_false(10 < 5); 46 | assert_true(10 >= 5); 47 | assert_true(10 >= 10); 48 | assert_false(10 > 10); 49 | } 50 | 51 | fun test_strings() { 52 | print "Testing strings..."; 53 | 54 | var a = "hello" + " world"; 55 | assert_eq("hello world", a); 56 | } 57 | 58 | fun test_if() { 59 | print "Testing if..."; 60 | 61 | var a = 0; 62 | if (5 < 10) { 63 | a = 1; 64 | } else { 65 | a = 2; 66 | } 67 | 68 | assert_eq(1, a); 69 | 70 | if (5 > 10) { 71 | a = 1; 72 | } else { 73 | a = 2; 74 | } 75 | 76 | assert_eq(2, a); 77 | } 78 | 79 | fun test_loops() { 80 | print "Testing loops..."; 81 | 82 | var a = 5; 83 | while (a > 0) { 84 | a = a - 1; 85 | } 86 | assert_eq(0, a); 87 | 88 | var b = 0; 89 | for (var i = 1; i < 100; i = i + 1) { 90 | b = b + i; 91 | } 92 | assert_eq(4950, b); 93 | } 94 | 95 | fun test_functions() { 96 | print "Testing functions..."; 97 | fun a() { 98 | return 1; 99 | } 100 | 101 | assert_eq(1, a()); 102 | 103 | fun b(x, y) { 104 | return x + y; 105 | } 106 | 107 | assert_eq(15, b(10, 5)); 108 | } 109 | 110 | fun test_closures() { 111 | print "Testing closures..."; 112 | var result = nil; 113 | fun makeClosure() { 114 | var local = "local"; 115 | fun closure() { 116 | print " Local is " + local; 117 | result = "result"; 118 | } 119 | return closure; 120 | } 121 | 122 | var closure = makeClosure(); 123 | closure(); 124 | 125 | assert_eq("result", result); 126 | } 127 | 128 | fun test_closures2() { 129 | print "Testing closures 2..."; 130 | fun makeClosure2(value) { 131 | fun closure() { 132 | print " " + value; 133 | } 134 | return closure; 135 | } 136 | 137 | var doughnut = makeClosure2("doughnut"); 138 | var bagel = makeClosure2("bagel"); 139 | doughnut(); 140 | bagel(); 141 | } 142 | 143 | fun test_gc_trigger() { 144 | print "Testing gc trigger..."; 145 | 146 | var s = "hello"; 147 | for (var i = 1; i < 10000; i = i + 1) { 148 | s = s + "world"; 149 | } 150 | print "end"; 151 | } 152 | 153 | fun test_class_fields() { 154 | print "Testing class fields..."; 155 | 156 | class Foo {} 157 | 158 | var foo = Foo(); 159 | foo.name = "Manuel"; 160 | foo.id = 123; 161 | 162 | assert_eq("Manuel", foo.name); 163 | assert_eq(123, foo.id); 164 | 165 | foo.id = 456; 166 | assert_eq(456, foo.id); 167 | } 168 | 169 | fun test_class_method() { 170 | print "Testing class method..."; 171 | class Scone { 172 | topping(first, second) { 173 | print " scone with " + first + " and " + second; 174 | } 175 | } 176 | 177 | var scone = Scone(); 178 | scone.topping("berries", "cream"); 179 | } 180 | 181 | fun test_class_this() { 182 | print "Testing class this..."; 183 | class Foo { 184 | method() { 185 | return this.name; 186 | } 187 | } 188 | var f = Foo(); 189 | f.name = "manuel"; 190 | assert_eq("manuel", f.method()); 191 | } 192 | 193 | fun test_class_initializer() { 194 | print "Testing class initializer..."; 195 | 196 | class CoffeeMaker { 197 | init(coffee) { 198 | this.coffee = coffee; 199 | } 200 | 201 | brew() { 202 | print " Enjoy your cup of " + this.coffee; 203 | this.coffee = "other"; 204 | } 205 | } 206 | 207 | var maker = CoffeeMaker("coffee and chicory"); 208 | assert_eq("coffee and chicory", maker.coffee); 209 | maker.brew(); 210 | assert_eq("other", maker.coffee); 211 | } 212 | 213 | fun test_class_invoke_non_method() { 214 | print "Testing class invoking non method..."; 215 | 216 | class Oops { 217 | init() { 218 | fun f() { 219 | return "not a method"; 220 | } 221 | 222 | this.field = f; 223 | } 224 | } 225 | 226 | var oops = Oops(); 227 | assert_eq("not a method", oops.field()); 228 | } 229 | 230 | fun test_class_super() { 231 | print "Testing class super..."; 232 | class A { 233 | method() { 234 | return "A method"; 235 | } 236 | } 237 | 238 | class B < A { 239 | method() { 240 | return "B method"; 241 | } 242 | 243 | test() { 244 | return super.method(); 245 | } 246 | } 247 | 248 | class C < B {} 249 | 250 | assert_eq("B method", B().method()); 251 | assert_eq("A method", C().test()); 252 | } 253 | 254 | test_arithmetic(); 255 | test_boolean(); 256 | test_strings(); 257 | test_if(); 258 | test_loops(); 259 | test_functions(); 260 | test_closures(); 261 | test_closures2(); 262 | test_class_fields(); 263 | test_class_method(); 264 | test_class_this(); 265 | test_class_initializer(); 266 | test_class_invoke_non_method(); 267 | test_class_super(); 268 | test_gc_trigger(); 269 | 270 | 271 | print "++++++++++++++END OF THE TEST+++++++++++++++++++++"; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Loxido 2 | 3 | A [Rust](https://www.rust-lang.org/) implementation of the Lox programming 4 | language from the amazing book [Crafting 5 | Interpreters](https://craftinginterpreters.com/) by Bob Nystrom. 6 | 7 | This is based on the [clox reference 8 | implementation](https://github.com/munificent/craftinginterpreters) in C. It 9 | implements a byte-code compiler and VM. 10 | 11 | You can read a detailed blog post about this here: 12 | 13 | https://ceronman.com/2021/07/22/my-experience-crafting-an-interpreter-with-rust/ 14 | 15 | This implementation passes the 243 integration tests from *clox* and *jlox*. 16 | 17 | ## Two branches 18 | 19 | This repository contains two main branches: 20 | 21 | - The [master](https://github.com/ceronman/loxido/tree/unsafe) branch is written 22 | using purely safe code. As a result it's quite slower than the *clox* reference 23 | implementation. 24 | 25 | - The [unsafe](https://github.com/ceronman/loxido/tree/unsafe) branch is based 26 | on the master code but it introduces some unsafe Rust code. It has a different 27 | GC implementation and some hand-written data structures, including a *Stack* and 28 | *HashMap*. This version is almost as fast as *clox* (see benchmarks bellow). 29 | 30 | ## Differences between loxido and clox 31 | 32 | The safe implementation follows the design of *clox*, but trying to 33 | be Rust idiomatic. Some of the differences are: 34 | 35 | - The design of the GC is quite different to adapt to Rust safety limitations. 36 | A vector is used to store objects allocated and another vector of *tombstones* 37 | is used to keep track of deleted objects. Trait based polymorphism is used to 38 | implement different tracing strategies for different objects. 39 | 40 | - The GC doesn't run while compiling code, only when the VM is running. This 41 | simplifies dealing with the borrow checker. 42 | 43 | - The GC is not able to precisely keep track of all the allocated bytes; only an 44 | approximation. For this reason, the GC behavior might be less predictable. 45 | 46 | - For the most part, data structures from the Rust standard library are used. 47 | This includes *vectors*, *hashmaps* and *strings*. 48 | 49 | - The VM uses fixed length instructions, as opposed to the variable length from 50 | clox. This allows using enums for instructions, which makes the code nicer to 51 | write. 52 | 53 | - This implementation avoids *NaN Boxing* completely. 54 | 55 | ## Changes in the *unsafe* branch 56 | 57 | The [unsafe](https://github.com/ceronman/loxido/tree/unsafe) branch introduces 58 | some unsafe code to improve performance. The changes are: 59 | 60 | - Using raw pointers to keep track of the current frame and bytecode chunk on 61 | every cycle of the VM. 62 | 63 | - Redesigned GC using raw pointers, very similar to the one used in clox. 64 | Although objects are represented as an enum instead of trait objects to avoid 65 | dynamic dispatch. 66 | 67 | - Stack is implemented using an array and a raw pointer indicating the top. 68 | Similar to clox. 69 | 70 | - The Program Counter (PC) is implemented using a raw pointer. 71 | 72 | - Custom *HashMap* implementation based on the code from *clox*. 73 | 74 | ## How to run 75 | 76 | To compile and run this project you need 77 | [Cargo](https://doc.rust-lang.org/cargo/). 78 | 79 | To run the REPL, just go for: 80 | 81 | ``` 82 | cargo run 83 | ``` 84 | 85 | To run a specific file: 86 | 87 | ``` 88 | cargo run program.lox 89 | ``` 90 | 91 | ## Running the tests 92 | 93 | To run the tests just run: 94 | 95 | ``` 96 | cargo test 97 | ``` 98 | 99 | ## Performance 100 | 101 | I measured the running times of the different benchmarks available in the [lox 102 | repository](https://github.com/munificent/craftinginterpreters). I also ported 103 | the benchmarks to Python and Perl. Here are the compared running times of 104 | different implementations of Lox, including mind. 105 | 106 | These were run on my Thinkpad X1 laptop with Intel Core i7-8565U CPU 107 | 108 | 109 | **note**: The horizontal axis is capped at 15 seconds to improve visibility. 110 | ![Performance of Lox implementation](benchmarks.svg) 111 | 112 | | | clox | jlox | loxido | loxido unsafe | Python 3.9.2 | Perl 5.32.1 | 113 | | ----------------- | :----------: | :-----------: | :-----------: | :-----------: | :-----------: | :----------: | 114 | | **arithmetic** | 0.6304s | 3.3200s | 5.4746s | 0.9706s | 1.6815s | 1.0798s | 115 | | **binary_tree** | 2.6283s | 7.0670s | 8.4780s | 3.4534s | 4.1730s | 6.3510s | 116 | | **equality** | 1.3740s | 3.6209s | 11.337s | 1.9433s | 3.4657s | 2.2632s | 117 | | **fib** | 0.9067s | 4.2789s | 5.0318s | 1.2042s | 2.1110s | 4.2134s | 118 | | **instantiation** | 0.5264s | 1.5280s | 7.2826s | 0.9068s | 1.4895s | 3.8319s | 119 | | **invocation** | 0.2916s | 1.3600s | 1.6865s | 0.3566s | 0.7789s | 0.9535s | 120 | | **method_call** | 0.1574s | 1.9440s | 1.1231s | 0.2255s | 0.5696s | 0.9560s | 121 | | **properties** | 0.3716s | 3.9539s | 2.2317s | 0.5132s | 1.4074s | 1.4339s | 122 | | **trees** | 1.9714s | 21.677s | 14.478s | 2.9840s | 6.4791s | 13.019s | 123 | | **zoo** | 0.3132s | 2.8380s | 1.6914s | 0.3836s | 0.9739s | 1.1222s | 124 | 125 | 126 | ## Tree walker implementation 127 | 128 | I also wrote another implementation of the tree walk interpreter in Clojure 129 | called [cloxure](https://github.com/ceronman/cloxure). 130 | 131 | ## Copyright note 132 | 133 | Most of the test and benchmark files are copied from [Crafting Interpreters 134 | repository](https://github.com/munificent/craftinginterpreters). The copyright 135 | for these belongs to Bob Nystrom and are copied here because their license 136 | allows it (MIT). 137 | 138 | The source code for this implementation is copyright of Manuel Cerón. And it's 139 | also licensed as MIT (see [LICENCE](LICENSE) for details.) 140 | -------------------------------------------------------------------------------- /tests/integration/field/many.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | fun setFields() { 5 | foo.bilberry = "bilberry"; 6 | foo.lime = "lime"; 7 | foo.elderberry = "elderberry"; 8 | foo.raspberry = "raspberry"; 9 | foo.gooseberry = "gooseberry"; 10 | foo.longan = "longan"; 11 | foo.mandarine = "mandarine"; 12 | foo.kiwifruit = "kiwifruit"; 13 | foo.orange = "orange"; 14 | foo.pomegranate = "pomegranate"; 15 | foo.tomato = "tomato"; 16 | foo.banana = "banana"; 17 | foo.juniper = "juniper"; 18 | foo.damson = "damson"; 19 | foo.blackcurrant = "blackcurrant"; 20 | foo.peach = "peach"; 21 | foo.grape = "grape"; 22 | foo.mango = "mango"; 23 | foo.redcurrant = "redcurrant"; 24 | foo.watermelon = "watermelon"; 25 | foo.plumcot = "plumcot"; 26 | foo.papaya = "papaya"; 27 | foo.cloudberry = "cloudberry"; 28 | foo.rambutan = "rambutan"; 29 | foo.salak = "salak"; 30 | foo.physalis = "physalis"; 31 | foo.huckleberry = "huckleberry"; 32 | foo.coconut = "coconut"; 33 | foo.date = "date"; 34 | foo.tamarind = "tamarind"; 35 | foo.lychee = "lychee"; 36 | foo.raisin = "raisin"; 37 | foo.apple = "apple"; 38 | foo.avocado = "avocado"; 39 | foo.nectarine = "nectarine"; 40 | foo.pomelo = "pomelo"; 41 | foo.melon = "melon"; 42 | foo.currant = "currant"; 43 | foo.plum = "plum"; 44 | foo.persimmon = "persimmon"; 45 | foo.olive = "olive"; 46 | foo.cranberry = "cranberry"; 47 | foo.boysenberry = "boysenberry"; 48 | foo.blackberry = "blackberry"; 49 | foo.passionfruit = "passionfruit"; 50 | foo.mulberry = "mulberry"; 51 | foo.marionberry = "marionberry"; 52 | foo.plantain = "plantain"; 53 | foo.lemon = "lemon"; 54 | foo.yuzu = "yuzu"; 55 | foo.loquat = "loquat"; 56 | foo.kumquat = "kumquat"; 57 | foo.salmonberry = "salmonberry"; 58 | foo.tangerine = "tangerine"; 59 | foo.durian = "durian"; 60 | foo.pear = "pear"; 61 | foo.cantaloupe = "cantaloupe"; 62 | foo.quince = "quince"; 63 | foo.guava = "guava"; 64 | foo.strawberry = "strawberry"; 65 | foo.nance = "nance"; 66 | foo.apricot = "apricot"; 67 | foo.jambul = "jambul"; 68 | foo.grapefruit = "grapefruit"; 69 | foo.clementine = "clementine"; 70 | foo.jujube = "jujube"; 71 | foo.cherry = "cherry"; 72 | foo.feijoa = "feijoa"; 73 | foo.jackfruit = "jackfruit"; 74 | foo.fig = "fig"; 75 | foo.cherimoya = "cherimoya"; 76 | foo.pineapple = "pineapple"; 77 | foo.blueberry = "blueberry"; 78 | foo.jabuticaba = "jabuticaba"; 79 | foo.miracle = "miracle"; 80 | foo.dragonfruit = "dragonfruit"; 81 | foo.satsuma = "satsuma"; 82 | foo.tamarillo = "tamarillo"; 83 | foo.honeydew = "honeydew"; 84 | } 85 | 86 | setFields(); 87 | 88 | fun printFields() { 89 | print foo.apple; // expect: apple 90 | print foo.apricot; // expect: apricot 91 | print foo.avocado; // expect: avocado 92 | print foo.banana; // expect: banana 93 | print foo.bilberry; // expect: bilberry 94 | print foo.blackberry; // expect: blackberry 95 | print foo.blackcurrant; // expect: blackcurrant 96 | print foo.blueberry; // expect: blueberry 97 | print foo.boysenberry; // expect: boysenberry 98 | print foo.cantaloupe; // expect: cantaloupe 99 | print foo.cherimoya; // expect: cherimoya 100 | print foo.cherry; // expect: cherry 101 | print foo.clementine; // expect: clementine 102 | print foo.cloudberry; // expect: cloudberry 103 | print foo.coconut; // expect: coconut 104 | print foo.cranberry; // expect: cranberry 105 | print foo.currant; // expect: currant 106 | print foo.damson; // expect: damson 107 | print foo.date; // expect: date 108 | print foo.dragonfruit; // expect: dragonfruit 109 | print foo.durian; // expect: durian 110 | print foo.elderberry; // expect: elderberry 111 | print foo.feijoa; // expect: feijoa 112 | print foo.fig; // expect: fig 113 | print foo.gooseberry; // expect: gooseberry 114 | print foo.grape; // expect: grape 115 | print foo.grapefruit; // expect: grapefruit 116 | print foo.guava; // expect: guava 117 | print foo.honeydew; // expect: honeydew 118 | print foo.huckleberry; // expect: huckleberry 119 | print foo.jabuticaba; // expect: jabuticaba 120 | print foo.jackfruit; // expect: jackfruit 121 | print foo.jambul; // expect: jambul 122 | print foo.jujube; // expect: jujube 123 | print foo.juniper; // expect: juniper 124 | print foo.kiwifruit; // expect: kiwifruit 125 | print foo.kumquat; // expect: kumquat 126 | print foo.lemon; // expect: lemon 127 | print foo.lime; // expect: lime 128 | print foo.longan; // expect: longan 129 | print foo.loquat; // expect: loquat 130 | print foo.lychee; // expect: lychee 131 | print foo.mandarine; // expect: mandarine 132 | print foo.mango; // expect: mango 133 | print foo.marionberry; // expect: marionberry 134 | print foo.melon; // expect: melon 135 | print foo.miracle; // expect: miracle 136 | print foo.mulberry; // expect: mulberry 137 | print foo.nance; // expect: nance 138 | print foo.nectarine; // expect: nectarine 139 | print foo.olive; // expect: olive 140 | print foo.orange; // expect: orange 141 | print foo.papaya; // expect: papaya 142 | print foo.passionfruit; // expect: passionfruit 143 | print foo.peach; // expect: peach 144 | print foo.pear; // expect: pear 145 | print foo.persimmon; // expect: persimmon 146 | print foo.physalis; // expect: physalis 147 | print foo.pineapple; // expect: pineapple 148 | print foo.plantain; // expect: plantain 149 | print foo.plum; // expect: plum 150 | print foo.plumcot; // expect: plumcot 151 | print foo.pomegranate; // expect: pomegranate 152 | print foo.pomelo; // expect: pomelo 153 | print foo.quince; // expect: quince 154 | print foo.raisin; // expect: raisin 155 | print foo.rambutan; // expect: rambutan 156 | print foo.raspberry; // expect: raspberry 157 | print foo.redcurrant; // expect: redcurrant 158 | print foo.salak; // expect: salak 159 | print foo.salmonberry; // expect: salmonberry 160 | print foo.satsuma; // expect: satsuma 161 | print foo.strawberry; // expect: strawberry 162 | print foo.tamarillo; // expect: tamarillo 163 | print foo.tamarind; // expect: tamarind 164 | print foo.tangerine; // expect: tangerine 165 | print foo.tomato; // expect: tomato 166 | print foo.watermelon; // expect: watermelon 167 | print foo.yuzu; // expect: yuzu 168 | } 169 | 170 | printFields(); 171 | -------------------------------------------------------------------------------- /src/objects.rs: -------------------------------------------------------------------------------- 1 | use std::{any::Any, fmt, mem}; 2 | 3 | use crate::{ 4 | chunk::Chunk, 5 | chunk::{Instruction, Table, Value}, 6 | gc::{Gc, GcRef, GcTrace}, 7 | vm::Vm, 8 | }; 9 | 10 | impl GcTrace for String { 11 | fn format(&self, f: &mut fmt::Formatter, _gc: &Gc) -> fmt::Result { 12 | write!(f, "{}", self) 13 | } 14 | fn size(&self) -> usize { 15 | mem::size_of::() + self.as_bytes().len() 16 | } 17 | fn trace(&self, _gc: &mut Gc) {} 18 | fn as_any(&self) -> &dyn Any { 19 | self 20 | } 21 | fn as_any_mut(&mut self) -> &mut dyn Any { 22 | self 23 | } 24 | } 25 | 26 | #[derive(Clone, Copy)] 27 | pub struct NativeFunction(pub fn(&Vm, &[Value]) -> Value); 28 | 29 | impl fmt::Debug for NativeFunction { 30 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 31 | write!(f, "") 32 | } 33 | } 34 | 35 | impl PartialEq for NativeFunction { 36 | fn eq(&self, other: &Self) -> bool { 37 | std::ptr::eq(self, other) 38 | } 39 | } 40 | 41 | #[derive(Copy, Clone, Debug)] 42 | pub struct FunctionUpvalue { 43 | pub index: u8, 44 | pub is_local: bool, 45 | } 46 | 47 | #[derive(Debug)] 48 | pub struct Function { 49 | pub arity: usize, 50 | pub chunk: Chunk, 51 | pub name: GcRef, 52 | pub upvalues: Vec, 53 | } 54 | 55 | impl Function { 56 | pub fn new(name: GcRef) -> Self { 57 | Self { 58 | arity: 0, 59 | chunk: Chunk::new(), 60 | name, 61 | upvalues: Vec::new(), 62 | } 63 | } 64 | } 65 | 66 | impl GcTrace for Function { 67 | fn format(&self, f: &mut fmt::Formatter, gc: &Gc) -> fmt::Result { 68 | let name = gc.deref(self.name); 69 | if name.is_empty() { 70 | write!(f, "