├── .gitattributes ├── README.md ├── sample.fail ├── src └── com │ └── company │ ├── fail │ ├── AstPrinter.java │ ├── BreakJump.java │ ├── Callable.java │ ├── ContinueJump.java │ ├── Environment.java │ ├── Expr.java │ ├── Fail.java │ ├── FailClass.java │ ├── Function.java │ ├── Instance.java │ ├── Interpreter.java │ ├── Parser.java │ ├── Resolver.java │ ├── Return.java │ ├── RpnPrinter.java │ ├── RuntimeError.java │ ├── Scanner.java │ ├── Stmt.java │ ├── Token.java │ └── TokenType.java │ └── tool │ └── GenerateAst.java ├── test ├── assignment │ ├── associativity.fail │ ├── global.fail │ ├── grouping.fail │ ├── infix_operator.fail │ ├── local.fail │ ├── prefix_operator.fail │ ├── shorthand.fail │ ├── syntax.fail │ ├── to_this.fail │ └── undefined.fail ├── benchmark │ ├── binary_trees.fail │ ├── equality.fail │ ├── fib.fail │ ├── invocation.fail │ ├── method_call.fail │ ├── properties.fail │ └── string_equality.fail ├── block │ ├── empty.fail │ ├── scope.fail │ └── scope_complex.fail ├── bool │ ├── equality.fail │ └── not.fail ├── call │ ├── bool.fail │ ├── none.fail │ ├── num.fail │ ├── object.fail │ └── string.fail ├── class │ ├── empty.fail │ ├── getter.fail │ ├── inherited_method.fail │ ├── local_reference_self.fail │ ├── reference_self.fail │ └── staticmethod.fail ├── closure │ ├── assign_to_closure.fail │ ├── assign_to_shadowed_later.fail │ ├── close_over_function_parameter.fail │ ├── close_over_later_variable.fail │ ├── close_over_method_parameter.fail │ ├── closed_closure_in_function.fail │ ├── nested_closure.fail │ ├── open_closure_in_function.fail │ ├── reference_closure_multiple_times.fail │ ├── reuse_closure_slot.fail │ ├── shadow_closure_with_local.fail │ ├── unused_closure.fail │ └── unused_later_closure.wren ├── comments │ ├── line_at_eof.fail │ ├── only_line_comment.fail │ ├── only_line_comment_and_line.fail │ └── unicode.fail ├── constructor ├── do-while │ └── do-while.fail ├── empty_file.fail ├── expressions │ ├── evaluate.fail │ └── parse.fail ├── field │ ├── call_function_field.fail │ ├── call_nonfunction_field.fail │ ├── get_on_bool.fail │ ├── get_on_class.fail │ ├── get_on_function.fail │ ├── get_on_none.fail │ ├── get_on_num.fail │ ├── get_on_string.fail │ ├── many.fail │ ├── method.fail │ ├── method_binds_this.fail │ ├── on_instance.fail │ ├── set_on_bool.fail │ ├── set_on_class.fail │ ├── set_on_function.fail │ ├── set_on_none.fail │ ├── set_on_num.fail │ ├── set_on_string.fail │ └── undefined.fail ├── for │ ├── class_in_body.fail │ ├── closure_in_body.fail │ ├── for_complex.fail │ ├── fun_in_body.fail │ ├── return_closure.fail │ ├── return_inside.fail │ ├── scope.fail │ ├── statement_condition.fail │ ├── statement_increment.fail │ ├── statement_initializer.fail │ ├── syntax.fail │ └── var_in_body.fail ├── function │ ├── body_must_be_block.fail │ ├── empty_body.fail │ ├── extra_arguments.fail │ ├── local_mutual_recursion.fail │ ├── local_recursion.fail │ ├── missing_arguments.fail │ ├── missing_comma_in_parameters.fail │ ├── mutual_recursion.fail │ ├── parameters.fail │ ├── print.fail │ ├── recursion.fail │ ├── too_many_arguments.fail │ ├── too_many_parameters.fail │ └── used_before_declared.fail ├── global_functions │ ├── len.fail │ └── str.fail ├── if │ ├── class_in_else.fail │ ├── class_in_then.fail │ ├── condition_assignment.fail │ ├── dangling_else.fail │ ├── else.fail │ ├── fun_in_else.fail │ ├── fun_in_then.fail │ ├── if.fail │ ├── truth.fail │ ├── var_in_else.fail │ └── var_in_then.fail ├── inheritance │ ├── inherit_from_function.fail │ ├── inherit_from_none.fail │ ├── inherit_from_number.fail │ ├── inherit_methods.fail │ ├── parenthesized_superclass.fail │ └── set_fields_from_base_class.fail ├── lambda │ ├── binding.fail │ ├── declaration.fail │ ├── if.fail │ ├── lambda.fail │ ├── nested.fail │ ├── semicolon.fail │ └── while.fail ├── limit │ ├── loop_too_large.fail │ ├── reuse_constants.fail │ ├── stack_overflow.fail │ ├── too_many_constants.fail │ ├── too_many_locals.fail │ └── too_many_upvalues.fail ├── logical_operator │ ├── and.fail │ ├── and_truth.fail │ ├── or.fail │ ├── or_truth.fail │ └── ternary.fail ├── method │ ├── arity.fail │ ├── empty_block.fail │ ├── extra_arguments.fail │ ├── missing_arguments.fail │ ├── not_found.fail │ ├── refer_to_name.fail │ ├── too_many_arguments.fail │ └── too_many_parameters.fail ├── none │ └── literal.fail ├── number │ ├── decimal_point_at_eof.fail │ ├── leading_dot.fail │ ├── literals.fail │ └── trailing_dot.fail ├── operator │ ├── add.fail │ ├── add_bool_none.fail │ ├── add_bool_num.fail │ ├── add_bool_string.fail │ ├── add_none_none.fail │ ├── add_num_none.fail │ ├── add_string_none.fail │ ├── comma.fail │ ├── comma_all_asign.fail │ ├── comma_evaluate_all.fail │ ├── comma_precedence.fail │ ├── comma_return.fail │ ├── comparison.fail │ ├── decrement_none.fail │ ├── decrement_none_var.fail │ ├── decrement_num.fail │ ├── decrement_string.fail │ ├── decrement_variable.fail │ ├── divide.fail │ ├── divide_nonnum_num.fail │ ├── divide_num_nonnum.fail │ ├── equals.fail │ ├── equals_class.fail │ ├── greater_nonnum_num.fail │ ├── greater_num_nonnum.fail │ ├── greater_or_equal_nonnum_num.fail │ ├── greater_or_equal_num_nonnum.fail │ ├── increment_none.fail │ ├── increment_none_var.fail │ ├── increment_num.fail │ ├── increment_string.fail │ ├── increment_variable.fail │ ├── less_nonnum_num.fail │ ├── less_num_nonnum.fail │ ├── less_or_equal_nonnum_num.fail │ ├── less_or_equal_num_nonnum.fail │ ├── multiply.fail │ ├── multiply_nonnum_num.fail │ ├── multiply_num_nonnum.fail │ ├── negate.fail │ ├── negate_nonnum.fail │ ├── not.fail │ ├── not_class.fail │ ├── not_equals.fail │ ├── subtract.fail │ ├── subtract_nonnum_num.fail │ └── subtract_num_nonnum.fail ├── precedence.fail ├── print │ └── missing_argument.fail ├── regression │ └── 40.fail ├── return │ ├── after_else.fail │ ├── after_if.fail │ ├── after_while.fail │ ├── at_top_level.fail │ ├── in_function.fail │ ├── in_method.fail │ └── return_none_if_no_value.fail ├── scanning │ ├── identifiers.fail │ ├── keywords.fail │ ├── numbers.fail │ ├── punctuators.fail │ ├── strings.fail │ └── whitespace.fail ├── string │ ├── error_after_multiline.fail │ ├── literals.fail │ ├── multiline.fail │ └── unterminated.fail ├── super │ ├── bound_method.fail │ ├── call_other_method.fail │ ├── call_same_method.fail │ ├── closure.fail │ ├── constructor.fail │ ├── extra_arguments.fail │ ├── indirectly_inherited.fail │ ├── missing_arguments.fail │ ├── no_superclass_bind.fail │ ├── no_superclass_call.fail │ ├── no_superclass_method.fail │ ├── parenthesized.fail │ ├── reassign_superclass.fail │ ├── super_at_top_level.fail │ ├── super_in_closure_in_inherited_method.fail │ ├── super_in_inherited_method.fail │ ├── super_in_top_level_function.fail │ ├── super_without_dot.fail │ ├── super_without_name.fail │ └── this_in_superclass_method.fail ├── this │ ├── closure.fail │ ├── nested_class.fail │ ├── nested_closure.fail │ ├── this_at_top_level.fail │ ├── this_in_method.fail │ └── this_in_top_level_function.fail ├── unexpected_character.fail ├── variable │ ├── collide_with_parameter.fail │ ├── duplicate_local.fail │ ├── duplicate_parameter.fail │ ├── early_bound.fail │ ├── in_middle_of_block.fail │ ├── in_nested_block.fail │ ├── local_from_method.fail │ ├── redeclare_global.fail │ ├── redefine_global.fail │ ├── refer_self_initializer.fail │ ├── scope_reuse_in_different_blocks.fail │ ├── shadow_and_local.fail │ ├── shadow_global.fail │ ├── shadow_local.fail │ ├── undefined_global.fail │ ├── undefined_local.fail │ ├── uninitialized.fail │ ├── unreached_undefined.fail │ ├── use_false_as_var.fail │ ├── use_global_in_initializer.fail │ ├── use_local_in_initializer.fail │ ├── use_none_as_var.fail │ └── use_this_as_var.fail └── while │ ├── break.fail │ ├── class_in_body.fail │ ├── closure_in_body.fail │ ├── continue.fail │ ├── fun_in_body.fail │ ├── return_closure.fail │ ├── return_inside.fail │ ├── syntax.fail │ └── var_in_body.fail └── util └── test.py /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | * text=auto 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fail language 2 | 3 | Language based on lox and tweaked from book "Crafting Interpreters" by @munificent. 4 | 5 | ## Properties 6 | - regular grammar 7 | - recursive descent parser 8 | 9 | ## Keywords 10 | ### Constructs 11 | - if, else 12 | - for, while, do-while (break, continue) 13 | - fun 14 | - class (this, super) 15 | 16 | ### Data types 17 | - var 18 | - Boolean: true, false, and, or 19 | - none 20 | 21 | ### Misc 22 | - print 23 | 24 | ## Grammar 25 | Statements: 26 | 27 | program → declaration* EOF ; 28 | declaration → classDecl 29 | | funDecl 30 | | varDecl 31 | | statement ; 32 | classDecl → "class" IDENTIFIER ( "<" IDENTIFIER )? 33 | "{" function* "}" ; 34 | funDecl → "fun" function ; 35 | varDecl → "var" IDENTIFIER ( "=" expression )? ";" ; 36 | statement → exprStmt 37 | | forStmt 38 | | ifStmt 39 | | printStmt 40 | | returnStmt 41 | | whileStmt 42 | | block; 43 | exprStmt → expression ";" ; 44 | forStmt → "for" "(" ( varDecl | exprStmt | ";" ) 45 | expression? ";" 46 | expression? ")" statement ; 47 | ifStmt → "if" "(" expression ")" statement ( "else" statement )? ; 48 | printStmt → "print" expression ";" ; 49 | doWhileStmt → "do" statement "while" "(" expression ")" ";" ; 50 | whileStmt → "while" "(" expression ")" statement ; 51 | breakStmt → "break" ";" ; 52 | continueStmt → "continue" ";" ; 53 | block → "{" declaration* "}" ; 54 | 55 | Expressions: 56 | 57 | expression → comma ; 58 | comma → assignment ( "," assignment )* 59 | assignment → ( call "." )? ( ( "=" | "+=" | "-=" | "*=" | "/=" | "**=" ) assignment )? 60 | | ternary ; 61 | ternary → logic_or ( "?" expression ":" ternary )? 62 | logic_or → logic_and ( "or" logic_and )* 63 | logic_and → equality ( "and" equality )* 64 | equality → comparison ( ( "!=" | "==" ) comparison )* 65 | comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* 66 | term → factor ( ( "-" | "+" ) factor )* 67 | factor → unary ( ( "/" | "*" ) unary )* 68 | unary → ( "!" | "-" | "++" | "--" ) unary 69 | | exponent ; 70 | exponent → (prefix "**" unary) 71 | | prefix ; 72 | prefix → ("++" | "--") primary 73 | | postfix ; 74 | postfix → primary ( "++" | "--" )* 75 | | call ; 76 | call → primary ( "(" arguments? ")" | "." IDENTIFIER )* ; 77 | primary → "true" | "false" | "none" | "this" 78 | | NUMBER | STRING | IDENTIFIER | "(" expression ")" 79 | | lambda 80 | | "super" "." IDENTIFIER 81 | // Error productions... 82 | | ( "!=" | "==" ) equality 83 | | ( ">" | ">=" | "<" | "<=" ) comparison 84 | | ( "+" ) term 85 | | ( "/" | "*" ) factor 86 | | ("**") exponent; 87 | 88 | Other: 89 | 90 | arguments → expression ( "," expression )* ; 91 | parameters → IDENTIFIER ( "," IDENTIFIER )* ; 92 | function → IDENTIFIER functionBody; 93 | method → function; 94 | static method→ "class" method; 95 | constructor → "init" functionBody; # returns this 96 | functionBody → "(" parameters? ")" block ; 97 | lambda → "fun" functionBody ; 98 | 99 | ### Notes 100 | Unary '+' operator is not supported. 101 | 102 | Currently continue statement does not work as expected for for loops, incrementors must be manually incremented. 103 | 104 | ## Rules 105 | ### Operator precedence (highest → lowest) 106 | 107 | Name Operators Associates 108 | Call a() Left 109 | Postfix a++ a-- Left 110 | Prefix ++a --a Right 111 | Exponent ** Right 112 | Unary ! - Right 113 | Factor / * Left 114 | Term - + Left 115 | Comparison > >= < <= Left 116 | Equality == != Left 117 | Logical And and Left 118 | Logical Or or Left 119 | Ternary ?: Right 120 | Assignment =, +=, -=, /=, *=, **= Right 121 | Comma , Left 122 | 123 | ### Truthyness 124 | Fail follows Ruby’s simple rule: false and none are falsey and everything else is truthy. 125 | 126 | ## Escape sequences 127 | \" – double quote 128 | \\ – single backslash 129 | \b – backspace 130 | \r – carriage return 131 | \n – newline 132 | \t – tab 133 | 134 | ## Data types 135 | Variables can change it's data type at runtime as in Python. Data types are implied from expressions. 136 | ### Built-in data types 137 | - Boolean: true or false 138 | - string 139 | - number (`double` precision) 140 | - none - a null pointer 141 | ### User defined data types 142 | Those can be defined via `class` keyword. 143 | 144 | ## Standard library 145 | ### Global functions 146 | - `clock()` - Gets the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC. Then it changes milliseconds to seconds as a floating point value. 147 | - `len(x)` - This function changes input to a string and gets its length. 148 | - `str(x)` - Changes input to its string representation. 149 | 150 | ## Added features 151 | Additional features mostly based on tasks from book: 152 | - multiline comments 153 | - postfix and prefix increment/decrement operators 154 | - ternary operator 155 | - exponent operator 156 | - prevent access to unassigned variables (no implicit initialization to none) 157 | - accept escape sequences 158 | - comma operator 159 | - operator overload for string multiplication ("abc" * 2 → "abcabc") 160 | - break and continue 161 | - do-while statement 162 | - shorthand assignment operators +=, -=, *=, /=, **= 163 | - prevention of assignment inside if, loop and ternary condition expressions 164 | - lambdas 165 | - warnings, if local variable is unused 166 | - static methods, getters and setters -------------------------------------------------------------------------------- /sample.fail: -------------------------------------------------------------------------------- 1 | print "\nFibonacci (first 21):"; 2 | var a = 0; 3 | var b = 1; 4 | 5 | while (a < 10000) { 6 | print a; 7 | var temp = a; 8 | a = b; 9 | b = temp + b; 10 | } 11 | 12 | fun sayHi(first, last) { 13 | print "Hi, " + first + " " + last + "!"; 14 | } 15 | 16 | sayHi("Dear", "Reader"); 17 | 18 | print "\nAgain fibonacci, but recursive:"; 19 | fun fibonacci(n) { 20 | if (n <= 1) return n; 21 | return fibonacci(n - 2) + fibonacci(n - 1); 22 | } 23 | for (var i = 0; i < 21; i = i + 1) { 24 | print fibonacci(i); 25 | } 26 | 27 | print "\nLocal function:"; 28 | fun makeCounter() { 29 | var i = 0; 30 | fun count() { 31 | i = i + 1; 32 | print i; 33 | } 34 | 35 | return count; 36 | } 37 | 38 | var counter = makeCounter(); 39 | counter(); // "1". 40 | counter(); // "2". 41 | 42 | print "\nClosure:"; 43 | fun makePoint(x, y) { 44 | fun closure(method) { 45 | if (method == "x") return x; 46 | if (method == "y") return y; 47 | print "unknown method " + method; 48 | } 49 | 50 | return closure; 51 | } 52 | 53 | var point = makePoint(2, 3); 54 | print point("x"); // "2". 55 | print point("y"); // "3". 56 | 57 | print "\nLambda:"; 58 | fun thrice(fn) { 59 | for (var i = 1; i <= 3; i = i + 1) { 60 | fn(i); 61 | } 62 | } 63 | 64 | thrice(fun (a) { 65 | print a; 66 | }); 67 | 68 | print "\nbinding test:"; 69 | var a = "global"; 70 | { 71 | fun showA() { 72 | print a; 73 | } 74 | 75 | showA(); 76 | var a = "block"; 77 | showA(); 78 | } 79 | { 80 | var notUsed = "x"; 81 | print "notused"; 82 | } 83 | 84 | print "\nTesting class:"; 85 | class DevonshireCream { 86 | serveOn() { 87 | return "Scones"; 88 | } 89 | } 90 | 91 | print DevonshireCream; 92 | print (5 - (3 - 1)) + -1; 93 | 94 | print "\nInheritance:"; 95 | 96 | class Doughnut { 97 | cook() { 98 | print "Fry until golden brown."; 99 | } 100 | } 101 | 102 | class BostonCream < Doughnut { 103 | cook() { 104 | super.cook(); 105 | print "Pipe full of custard and coat with chocolate."; 106 | } 107 | } 108 | 109 | BostonCream().cook(); -------------------------------------------------------------------------------- /src/com/company/fail/AstPrinter.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | // Creates an unambiguous, if ugly, string representation of AST nodes. 4 | // 5 | 6 | class AstPrinter implements Expr.Visitor, Stmt.Visitor { 7 | String print(Expr expr) { 8 | return expr.accept(this); 9 | } 10 | 11 | //Expressions 12 | @Override 13 | public String visitAssignExpr(Expr.Assign expr) { 14 | return parenthesize2("=", expr.name.lexeme, expr.value); 15 | } 16 | 17 | @Override 18 | public String visitBinaryExpr(Expr.Binary expr) { 19 | return parenthesize(expr.operator.lexeme, expr.left, expr.right); 20 | } 21 | 22 | @Override 23 | public String visitFunctionExpr(Expr.Function expr) { 24 | StringBuilder builder = new StringBuilder(); 25 | builder.append("(fun ("); 26 | 27 | for (Token param : expr.parameters) { 28 | if (param != expr.parameters.get(0)) builder.append(" "); 29 | builder.append(param.lexeme); 30 | } 31 | 32 | builder.append(") "); 33 | 34 | for (Stmt body : expr.body) { 35 | builder.append(body.accept(this)); 36 | } 37 | 38 | builder.append(")"); 39 | return builder.toString(); 40 | } 41 | 42 | @Override 43 | public String visitCallExpr(Expr.Call expr) { 44 | return parenthesize2("call", expr.callee, expr.arguments); 45 | } 46 | 47 | @Override 48 | public String visitSuperExpr(Expr.Super expr) { 49 | return "super"; 50 | } 51 | 52 | @Override 53 | public String visitThisExpr(Expr.This expr) { 54 | return "this"; 55 | } 56 | 57 | @Override 58 | public String visitGetExpr(Expr.Get expr) { 59 | return parenthesize2("get", expr.name.lexeme); 60 | } 61 | 62 | @Override 63 | public String visitSetExpr(Expr.Set expr) { 64 | return parenthesize2("set", expr.name.lexeme, expr.value.toString()); 65 | } 66 | 67 | @Override 68 | public String visitTernaryExpr(Expr.Ternary expr) { 69 | return parenthesize("?", expr.expr, expr.thenBranch, expr.elseBranch); 70 | } 71 | 72 | @Override 73 | public String visitVariableExpr(Expr.Variable expr) { 74 | return expr.name.lexeme; 75 | } 76 | 77 | @Override 78 | public String visitGroupingExpr(Expr.Grouping expr) { 79 | return parenthesize("group", expr.expression); 80 | } 81 | 82 | @Override 83 | public String visitLiteralExpr(Expr.Literal expr) { 84 | return expr.value.toString(); 85 | } 86 | 87 | @Override 88 | public String visitLogicalExpr(Expr.Logical expr) { 89 | return parenthesize(expr.operator.lexeme, expr.left, expr.right); 90 | } 91 | 92 | @Override 93 | public String visitUnaryExpr(Expr.Unary expr) { 94 | return parenthesize(expr.operator.lexeme, expr.right); 95 | } 96 | 97 | //Statements: 98 | @Override 99 | public String visitBlockStmt(Stmt.Block stmt) { 100 | StringBuilder builder = new StringBuilder(); 101 | builder.append("(block "); 102 | 103 | for (Stmt statement : stmt.statements) { 104 | builder.append(statement.accept(this)); 105 | } 106 | 107 | builder.append(")"); 108 | return builder.toString(); 109 | } 110 | 111 | @Override 112 | public String visitClassStmt(Stmt.Class stmt) { 113 | return stmt.name.lexeme; 114 | } 115 | 116 | @Override 117 | public String visitExpressionStmt(Stmt.Expression stmt) { 118 | return parenthesize(";", stmt.expression); 119 | } 120 | 121 | @Override 122 | public String visitFunctionStmt(Stmt.Function stmt) { 123 | StringBuilder builder = new StringBuilder(); 124 | builder.append("(fun " + stmt.name.lexeme + "("); 125 | 126 | for (Token param : stmt.function.parameters) { 127 | if (param != stmt.function.parameters.get(0)) builder.append(" "); 128 | builder.append(param.lexeme); 129 | } 130 | 131 | builder.append(") "); 132 | 133 | for (Stmt body : stmt.function.body) { 134 | builder.append(body.accept(this)); 135 | } 136 | 137 | builder.append(")"); 138 | return builder.toString(); 139 | } 140 | 141 | @Override 142 | public String visitIfStmt(Stmt.If stmt) { 143 | if (stmt.elseBranch == null) { 144 | return parenthesize2("if", stmt.condition, stmt.thenBranch); 145 | } 146 | 147 | return parenthesize2("if-else", stmt.condition, stmt.thenBranch, 148 | stmt.elseBranch); 149 | } 150 | 151 | @Override 152 | public String visitPrintStmt(Stmt.Print stmt) { 153 | return parenthesize("print", stmt.expression); 154 | } 155 | 156 | @Override 157 | public String visitReturnStmt(Stmt.Return stmt) { 158 | return parenthesize("return", stmt.value); 159 | } 160 | 161 | @Override 162 | public String visitVarStmt(Stmt.Var stmt) { 163 | if (stmt.initializer == null) { 164 | return parenthesize2("var", stmt.name); 165 | } 166 | 167 | return parenthesize2("var", stmt.name, "=", stmt.initializer); 168 | } 169 | 170 | @Override 171 | public String visitWhileStmt(Stmt.While stmt) { 172 | return parenthesize2("while", stmt.condition, stmt.body); 173 | } 174 | 175 | public String visitBreakStmt(Stmt.Break stmt) { 176 | return "(break)"; 177 | } 178 | 179 | public String visitContinueStmt(Stmt.Continue stmt) { 180 | return "(continue)"; 181 | } 182 | 183 | //Utilities: 184 | private String parenthesize(String name, Expr... exprs) { 185 | StringBuilder builder = new StringBuilder(); 186 | 187 | builder.append("(").append(name); 188 | for (Expr expr : exprs) { 189 | builder.append(" "); 190 | builder.append(expr.accept(this)); 191 | } 192 | builder.append(")"); 193 | 194 | return builder.toString(); 195 | } 196 | 197 | private String parenthesize2(String name, Object... parts) { 198 | StringBuilder builder = new StringBuilder(); 199 | 200 | builder.append("(").append(name); 201 | 202 | for (Object part : parts) { 203 | builder.append(" "); 204 | 205 | if (part instanceof Expr) { 206 | builder.append(((Expr) part).accept(this)); 207 | } else if (part instanceof Stmt) { 208 | builder.append(((Stmt) part).accept(this)); 209 | } else if (part instanceof Token) { 210 | builder.append(((Token) part).lexeme); 211 | } else { 212 | builder.append(part); 213 | } 214 | } 215 | builder.append(")"); 216 | 217 | return builder.toString(); 218 | } 219 | 220 | public static void main(String[] args) { 221 | Expr expression = new Expr.Binary( 222 | new Expr.Unary( 223 | new Token(TokenType.MINUS, "-", null, 1), 224 | new Expr.Literal(123), 225 | false), 226 | new Token(TokenType.STAR, "*", null, 1), 227 | new Expr.Grouping( 228 | new Expr.Literal(45.67))); 229 | 230 | System.out.println(new AstPrinter().print(expression)); 231 | } 232 | } -------------------------------------------------------------------------------- /src/com/company/fail/BreakJump.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | class BreakJump extends RuntimeException { 4 | BreakJump() { 5 | super("break jump"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/com/company/fail/Callable.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | import java.util.List; 4 | 5 | interface Callable { 6 | int arity(); 7 | Object call(Interpreter interpreter, List arguments); 8 | } -------------------------------------------------------------------------------- /src/com/company/fail/ContinueJump.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | class ContinueJump extends RuntimeException { 4 | ContinueJump() { 5 | super("continue jump"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/com/company/fail/Environment.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | class Environment { 8 | public Environment enclosing; 9 | private final Map values = new HashMap<>(); 10 | 11 | Environment() { 12 | enclosing = null; 13 | } 14 | 15 | Environment(Environment enclosing) { 16 | this.enclosing = enclosing; 17 | } 18 | 19 | void define(String name, Object value) { 20 | values.put(name, value); 21 | } 22 | 23 | Environment ancestor(int n) { 24 | Environment environment = this; 25 | for (int i = 0; i < n; i++) { 26 | environment = environment.enclosing; 27 | } 28 | return environment; 29 | } 30 | 31 | Object get(Token name) { 32 | if (values.containsKey(name.lexeme)) return values.get(name.lexeme); 33 | 34 | if (enclosing != null) return enclosing.get(name); 35 | 36 | throw new RuntimeError(name, 37 | "Undefined variable '" + name.lexeme + "'."); 38 | } 39 | 40 | Object getAt(int distance, String name) { 41 | return ancestor(distance).values.get(name); 42 | } 43 | 44 | void assign(Token name, Object value) { 45 | if (values.containsKey(name.lexeme)) { 46 | values.put(name.lexeme, value); 47 | return; 48 | } 49 | 50 | if (enclosing != null) { 51 | enclosing.assign(name, value); 52 | return; 53 | } 54 | 55 | throw new RuntimeError(name, 56 | "Undefined variable '" + name.lexeme + "'."); 57 | } 58 | } -------------------------------------------------------------------------------- /src/com/company/fail/Expr.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | import java.util.List; 4 | 5 | abstract class Expr { 6 | interface Visitor { 7 | R visitAssignExpr(Assign expr); 8 | R visitBinaryExpr(Binary expr); 9 | R visitFunctionExpr(Function expr); 10 | R visitCallExpr(Call expr); 11 | R visitSuperExpr(Super expr); 12 | R visitThisExpr(This expr); 13 | R visitGetExpr(Get expr); 14 | R visitSetExpr(Set expr); 15 | R visitGroupingExpr(Grouping expr); 16 | R visitLiteralExpr(Literal expr); 17 | R visitLogicalExpr(Logical expr); 18 | R visitUnaryExpr(Unary expr); 19 | R visitTernaryExpr(Ternary expr); 20 | R visitVariableExpr(Variable expr); 21 | } 22 | 23 | static class Assign extends Expr { 24 | Assign(Token name, Expr value, Token equals) { 25 | this.name = name; 26 | this.value = value; 27 | this.equals = equals; 28 | } 29 | 30 | R accept(Visitor visitor) { 31 | return visitor.visitAssignExpr(this); 32 | } 33 | 34 | final Token name; 35 | final Expr value; 36 | final Token equals; 37 | } 38 | 39 | static class Binary extends Expr { 40 | Binary(Expr left, Token operator, Expr right) { 41 | this.left = left; 42 | this.operator = operator; 43 | this.right = right; 44 | } 45 | 46 | R accept(Visitor visitor) { 47 | return visitor.visitBinaryExpr(this); 48 | } 49 | 50 | final Expr left; 51 | final Token operator; 52 | final Expr right; 53 | } 54 | 55 | static class Function extends Expr { 56 | Function(List parameters, List body) { 57 | this.parameters = parameters; 58 | this.body = body; 59 | } 60 | 61 | R accept(Visitor visitor) { 62 | return visitor.visitFunctionExpr(this); 63 | } 64 | 65 | final List parameters; 66 | final List body; 67 | } 68 | 69 | static class Call extends Expr { 70 | Call(Expr callee, Token paren, List arguments) { 71 | this.callee = callee; 72 | this.paren = paren; 73 | this.arguments = arguments; 74 | } 75 | 76 | R accept(Visitor visitor) { 77 | return visitor.visitCallExpr(this); 78 | } 79 | 80 | final Expr callee; 81 | final Token paren; 82 | final List arguments; 83 | } 84 | 85 | static class Super extends Expr { 86 | Super(Token keyword, Token method) { 87 | this.keyword = keyword; 88 | this.method = method; 89 | } 90 | 91 | R accept(Visitor visitor) { 92 | return visitor.visitSuperExpr(this); 93 | } 94 | 95 | final Token keyword; 96 | final Token method; 97 | } 98 | 99 | static class This extends Expr { 100 | This(Token keyword) { 101 | this.keyword = keyword; 102 | } 103 | 104 | R accept(Visitor visitor) { 105 | return visitor.visitThisExpr(this); 106 | } 107 | 108 | final Token keyword; 109 | } 110 | 111 | static class Get extends Expr { 112 | Get(Expr object, Token name) { 113 | this.object = object; 114 | this.name = name; 115 | } 116 | 117 | R accept(Visitor visitor) { 118 | return visitor.visitGetExpr(this); 119 | } 120 | 121 | final Expr object; 122 | final Token name; 123 | } 124 | 125 | static class Set extends Expr { 126 | Set(Expr object, Token name, Expr value) { 127 | this.object = object; 128 | this.name = name; 129 | this.value = value; 130 | } 131 | 132 | R accept(Visitor visitor) { 133 | return visitor.visitSetExpr(this); 134 | } 135 | 136 | final Expr object; 137 | final Token name; 138 | final Expr value; 139 | } 140 | 141 | static class Grouping extends Expr { 142 | Grouping(Expr expression) { 143 | this.expression = expression; 144 | } 145 | 146 | R accept(Visitor visitor) { 147 | return visitor.visitGroupingExpr(this); 148 | } 149 | 150 | final Expr expression; 151 | } 152 | 153 | static class Literal extends Expr { 154 | Literal(Object value) { 155 | this.value = value; 156 | } 157 | 158 | R accept(Visitor visitor) { 159 | return visitor.visitLiteralExpr(this); 160 | } 161 | 162 | final Object value; 163 | } 164 | 165 | static class Logical extends Expr { 166 | Logical(Expr left, Token operator, Expr right) { 167 | this.left = left; 168 | this.operator = operator; 169 | this.right = right; 170 | } 171 | 172 | R accept(Visitor visitor) { 173 | return visitor.visitLogicalExpr(this); 174 | } 175 | 176 | final Expr left; 177 | final Token operator; 178 | final Expr right; 179 | } 180 | 181 | static class Unary extends Expr { 182 | Unary(Token operator, Expr right, Boolean postfix) { 183 | this.operator = operator; 184 | this.right = right; 185 | this.postfix = postfix; 186 | } 187 | 188 | R accept(Visitor visitor) { 189 | return visitor.visitUnaryExpr(this); 190 | } 191 | 192 | final Token operator; 193 | final Expr right; 194 | final Boolean postfix; 195 | } 196 | 197 | static class Ternary extends Expr { 198 | Ternary(Expr expr, Expr thenBranch, Expr elseBranch) { 199 | this.expr = expr; 200 | this.thenBranch = thenBranch; 201 | this.elseBranch = elseBranch; 202 | } 203 | 204 | R accept(Visitor visitor) { 205 | return visitor.visitTernaryExpr(this); 206 | } 207 | 208 | final Expr expr; 209 | final Expr thenBranch; 210 | final Expr elseBranch; 211 | } 212 | 213 | static class Variable extends Expr { 214 | Variable(Token name) { 215 | this.name = name; 216 | } 217 | 218 | R accept(Visitor visitor) { 219 | return visitor.visitVariableExpr(this); 220 | } 221 | 222 | final Token name; 223 | } 224 | 225 | abstract R accept(Visitor visitor); 226 | } 227 | -------------------------------------------------------------------------------- /src/com/company/fail/Fail.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.nio.charset.Charset; 7 | import java.nio.file.Files; 8 | import java.nio.file.Paths; 9 | import java.util.List; 10 | 11 | public class Fail { 12 | private static final Interpreter interpreter = new Interpreter(); 13 | private static boolean hadError = false; 14 | private static boolean hadRuntimeError = false; 15 | 16 | public static void main(String[] args) throws IOException { 17 | if (args.length > 1) { 18 | System.out.println("Usage: jfail [script]"); 19 | } else if (args.length == 1) { 20 | runFile(args[0]); 21 | } else { 22 | runPrompt(); 23 | } 24 | } 25 | 26 | private static void runFile(String path) throws IOException { 27 | byte[] bytes = Files.readAllBytes(Paths.get(path)); 28 | run(new String(bytes, Charset.defaultCharset())); 29 | if (hadError) System.exit(65); 30 | if (hadRuntimeError) System.exit(70); 31 | } 32 | 33 | private static void runPrompt() throws IOException { 34 | InputStreamReader input = new InputStreamReader(System.in); 35 | BufferedReader reader = new BufferedReader(input); 36 | 37 | for (;;) { 38 | hadError = false; 39 | 40 | System.out.print("> "); 41 | Scanner scanner = new Scanner(reader.readLine()); 42 | List tokens = scanner.scanTokens(); 43 | 44 | Parser parser = new Parser(tokens); 45 | Object syntax = parser.parseRepl(); 46 | 47 | // Ignore it if there was a syntax error. 48 | if (hadError) continue; 49 | 50 | if (syntax instanceof List) { 51 | List statements = (List)syntax; 52 | Resolver resolver = new Resolver(interpreter); 53 | resolver.resolve(statements); 54 | 55 | // Stop if there was a resolution error. 56 | if (hadError) continue; 57 | 58 | interpreter.interpret(statements); 59 | } else if (syntax instanceof Expr) { 60 | Expr expr = (Expr)syntax; 61 | Resolver resolver = new Resolver(interpreter); 62 | resolver.resolve(expr); 63 | 64 | // Stop if there was a resolution error. 65 | if (hadError) continue; 66 | 67 | String result = interpreter.interpret(expr); 68 | if (result != null) { 69 | System.out.println("= " + result); 70 | } 71 | } 72 | } 73 | } 74 | 75 | private static void run(String source) { 76 | Scanner scanner = new Scanner(source); 77 | List tokens = scanner.scanTokens(); 78 | Parser parser = new Parser(tokens); 79 | List statements = parser.parse(); 80 | 81 | // Stop if there was a syntax error. 82 | if (hadError) return; 83 | 84 | Resolver resolver = new Resolver(interpreter); 85 | resolver.resolve(statements); 86 | 87 | // Stop if there was a resolution error. 88 | if (hadError) return; 89 | 90 | interpreter.interpret(statements); 91 | 92 | /* 93 | //Print AST 94 | if (!hadError) { 95 | System.out.println(new AstPrinter().print(expression)); 96 | } 97 | */ 98 | 99 | /* 100 | // For now, just print the tokens. 101 | for (Token token : tokens) { 102 | System.out.println(token); 103 | } 104 | */ 105 | } 106 | 107 | static void error(int line, String message) { 108 | reportError(line, "", message); 109 | } 110 | 111 | static private void reportError(int line, String where, String message) { 112 | System.err.println( 113 | "[line " + line + "] Error" + where + ": " + message); 114 | hadError = true; 115 | } 116 | 117 | static void error(Token token, String message) { 118 | if (token.type == TokenType.EOF) { 119 | reportError(token.line, " at end", message); 120 | } else { 121 | reportError(token.line, " at '" + token.lexeme + "'", message); 122 | } 123 | } 124 | 125 | static private void reportWarning(int line, String where, String message) { 126 | System.err.println( 127 | "[line " + line + "] Warning" + where + ": " + message); 128 | System.err.flush(); 129 | } 130 | 131 | static void warning(int line, String message) { 132 | reportWarning(line, "", message); 133 | } 134 | 135 | static void warning(Token token, String message) { 136 | if (token.type == TokenType.EOF) { 137 | reportWarning(token.line, " at end", message); 138 | } else { 139 | reportWarning(token.line, " at '" + token.lexeme + "'", message); 140 | } 141 | } 142 | 143 | static void runtimeError(RuntimeError error) { 144 | System.err.println(error.getMessage() + 145 | "\n[line " + error.token.line + "]"); 146 | hadRuntimeError = true; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/com/company/fail/FailClass.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | import java.util.List; 3 | import java.util.Map; 4 | 5 | class FailClass extends Instance implements Callable { 6 | final String name; 7 | private final Map methods; 8 | final FailClass superclass; 9 | 10 | FailClass(FailClass metaclass, FailClass superclass, String name, 11 | Map methods) { 12 | super(metaclass); 13 | this.superclass = superclass; 14 | this.name = name; 15 | this.methods = methods; 16 | } 17 | 18 | Function findMethod(Instance instance, String name) { 19 | if (methods.containsKey(name)) { 20 | return methods.get(name).bind(instance); 21 | } 22 | 23 | if (superclass != null) { 24 | return superclass.findMethod(instance, name); 25 | } 26 | 27 | return null; 28 | } 29 | 30 | @Override 31 | public Object call(Interpreter interpreter, List arguments) { 32 | Instance instance = new Instance(this); 33 | Function initializer = methods.get("init"); 34 | if (initializer != null) { 35 | initializer.bind(instance).call(interpreter, arguments); 36 | } 37 | 38 | return instance; 39 | } 40 | 41 | @Override 42 | public int arity() { 43 | Function initializer = methods.get("init"); 44 | if (initializer == null) return 0; 45 | return initializer.arity(); 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return name; 51 | } 52 | } -------------------------------------------------------------------------------- /src/com/company/fail/Function.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | import java.util.List; 4 | 5 | public class Function implements Callable { 6 | private final String name; 7 | private final Expr.Function declaration; 8 | private final Environment closure; 9 | private final boolean isInitializer; 10 | 11 | Function(String name, Expr.Function declaration, Environment closure, boolean isInitializer) { 12 | this.name = name; 13 | this.declaration = declaration; 14 | this.closure = closure; 15 | this.isInitializer = isInitializer; 16 | } 17 | 18 | @Override 19 | public int arity() { 20 | return declaration.parameters.size(); 21 | } 22 | 23 | @Override 24 | public Object call(Interpreter interpreter, List arguments) { 25 | Environment environment = new Environment(closure); 26 | 27 | if (declaration.parameters != null) { 28 | for (int i = 0; i < declaration.parameters.size(); i++) { 29 | environment.define(declaration.parameters.get(i).lexeme, 30 | arguments.get(i)); 31 | } 32 | } 33 | 34 | try { 35 | interpreter.executeBlock(declaration.body, environment); 36 | } catch (Return returnValue) { 37 | return returnValue.value; 38 | } 39 | 40 | if (isInitializer) return closure.getAt(0, "this"); 41 | return null; 42 | } 43 | 44 | Function bind(Instance instance) { 45 | Environment environment = new Environment(closure); 46 | environment.define("this", instance); 47 | return new Function(name, declaration, environment, isInitializer); 48 | } 49 | 50 | public boolean isGetter() { 51 | return declaration.parameters == null; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | if (name == null) return ""; 57 | return ""; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/com/company/fail/Instance.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | import java.util.HashMap; 3 | import java.util.Map; 4 | 5 | class Instance { 6 | private FailClass klass; 7 | private final Map fields = new HashMap<>(); 8 | 9 | Instance(FailClass klass) { 10 | this.klass = klass; 11 | } 12 | 13 | Object get(Token name) { 14 | if (fields.containsKey(name.lexeme)) { 15 | return fields.get(name.lexeme); 16 | } 17 | 18 | Function method = klass.findMethod(this, name.lexeme); 19 | if (method != null) return method; 20 | 21 | throw new RuntimeError(name, 22 | "Undefined property '" + name.lexeme + "'."); 23 | } 24 | 25 | void set(Token name, Object value) { 26 | fields.put(name.lexeme, value); 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return klass.name + " instance"; 32 | } 33 | } -------------------------------------------------------------------------------- /src/com/company/fail/Interpreter.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | import java.util.*; 4 | 5 | class Interpreter implements Expr.Visitor, Stmt.Visitor { 6 | private Environment environment = new Environment(); 7 | private final Map locals = new HashMap<>(); 8 | private static Object uninitialized = new Object(); 9 | 10 | Interpreter() { 11 | environment.define("clock", new Callable() { 12 | @Override 13 | public int arity() { 14 | return 0; 15 | } 16 | 17 | @Override 18 | public Object call(Interpreter interpreter, 19 | List arguments) { 20 | return (double)System.currentTimeMillis() / 1000.0; 21 | } 22 | }); 23 | environment.define("len", new Callable() { 24 | @Override 25 | public int arity() { 26 | return 1; 27 | } 28 | 29 | @Override 30 | public Object call(Interpreter interpreter, 31 | List arguments) { 32 | return stringify(arguments.get(0)).length(); 33 | } 34 | }); 35 | environment.define("str", new Callable() { 36 | @Override 37 | public int arity() { 38 | return 1; 39 | } 40 | 41 | @Override 42 | public Object call(Interpreter interpreter, 43 | List arguments) { 44 | return stringify(arguments.get(0)); 45 | } 46 | }); 47 | } 48 | 49 | String interpret(Expr expression) { 50 | try { 51 | Object value = evaluate(expression); 52 | return stringify(value); 53 | } catch (RuntimeError error) { 54 | Fail.runtimeError(error); 55 | return null; 56 | } 57 | } 58 | 59 | void interpret(List statements) { 60 | try { 61 | for (Stmt statement : statements) { 62 | execute(statement); 63 | } 64 | } catch (RuntimeError error) { 65 | Fail.runtimeError(error); 66 | } 67 | } 68 | 69 | private Object evaluate(Expr expr) { 70 | return expr.accept(this); 71 | } 72 | 73 | private void execute(Stmt stmt) { 74 | stmt.accept(this); 75 | } 76 | 77 | void resolve(Expr expr, int depth) { 78 | locals.put(expr, depth); 79 | } 80 | 81 | void executeBlock(List statements, Environment environment) { 82 | Environment previous = this.environment; 83 | try { 84 | this.environment = environment; 85 | 86 | for (Stmt statement : statements) { 87 | execute(statement); 88 | } 89 | } finally { 90 | this.environment = previous; 91 | } 92 | } 93 | 94 | @Override 95 | public Void visitBlockStmt(Stmt.Block stmt) { 96 | executeBlock(stmt.statements, new Environment(environment)); 97 | return null; 98 | } 99 | 100 | @Override 101 | public Void visitClassStmt(Stmt.Class stmt) { 102 | environment.define(stmt.name.lexeme, null); 103 | 104 | Object superclass = null; 105 | if (stmt.superclass != null) { 106 | superclass = evaluate(stmt.superclass); 107 | if (!(superclass instanceof FailClass)) { 108 | throw new RuntimeError(stmt.name, 109 | "Superclass must be a class."); 110 | } 111 | environment = new Environment(environment); 112 | environment.define("super", superclass); 113 | } 114 | 115 | Map classMethods = new HashMap<>(); 116 | for (Stmt.Function method : stmt.classMethods) { 117 | Function function = new Function(method.name.lexeme, method.function, environment, false); 118 | classMethods.put(method.name.lexeme, function); 119 | } 120 | 121 | FailClass metaclass = new FailClass(null, (FailClass) superclass, 122 | stmt.name.lexeme + " metaclass", classMethods); 123 | 124 | Map methods = new HashMap<>(); 125 | for (Stmt.Function method : stmt.methods) { 126 | Function function = new Function(method.name.lexeme, method.function, environment, 127 | method.name.lexeme.equals("init")); 128 | methods.put(method.name.lexeme, function); 129 | } 130 | 131 | FailClass klass = new FailClass(metaclass, (FailClass) superclass, stmt.name.lexeme, methods); 132 | 133 | if (superclass != null) { 134 | environment = environment.enclosing; 135 | } 136 | 137 | environment.assign(stmt.name, klass); 138 | return null; 139 | } 140 | 141 | @Override 142 | public Void visitExpressionStmt(Stmt.Expression stmt) { 143 | evaluate(stmt.expression); 144 | return null; 145 | } 146 | 147 | @Override 148 | public Void visitFunctionStmt(Stmt.Function stmt) { 149 | Function function = new Function(stmt.name.lexeme, stmt.function, environment, false); 150 | environment.define(stmt.name.lexeme, function); 151 | return null; 152 | } 153 | 154 | @Override 155 | public Void visitIfStmt(Stmt.If stmt) { 156 | Object condition = evaluate(stmt.condition); 157 | 158 | if (isTruthy(condition)) { 159 | execute(stmt.thenBranch); 160 | } else if (stmt.elseBranch != null) { 161 | execute(stmt.elseBranch); 162 | } 163 | return null; 164 | } 165 | 166 | @Override 167 | public Void visitPrintStmt(Stmt.Print stmt) { 168 | Object value = evaluate(stmt.expression); 169 | System.out.println(stringify(value)); 170 | return null; 171 | } 172 | 173 | @Override 174 | public Void visitReturnStmt(Stmt.Return stmt) { 175 | Object value = null; 176 | if (stmt.value != null) value = evaluate(stmt.value); 177 | 178 | throw new Return(value); 179 | } 180 | 181 | @Override 182 | public Void visitVarStmt(Stmt.Var stmt) { 183 | Object value = uninitialized; 184 | if (stmt.initializer != null) { 185 | value = evaluate(stmt.initializer); 186 | } 187 | 188 | environment.define(stmt.name.lexeme, value); 189 | return null; 190 | } 191 | 192 | @Override 193 | public Void visitWhileStmt(Stmt.While stmt) { 194 | while (isTruthy(evaluate(stmt.condition))) { 195 | try { 196 | execute(stmt.body); 197 | } catch (BreakJump breakJump) { 198 | break; 199 | } catch (ContinueJump continueJump) { 200 | //Do nothing. 201 | } 202 | } 203 | return null; 204 | } 205 | 206 | @Override 207 | public Object visitAssignExpr(Expr.Assign expr) { 208 | Object value = evaluate(expr.value); 209 | switch (expr.equals.type) { 210 | case EQUAL: 211 | break; 212 | case PLUS_EQUAL: { 213 | Object current = environment.get(expr.name); 214 | checkNumberOperands(expr.equals, current, value); 215 | value = (double) current + (double) value; 216 | break; 217 | } 218 | case MINUS_EQUAL: { 219 | Object current = environment.get(expr.name); 220 | checkNumberOperands(expr.equals, current, value); 221 | value = (double) current - (double) value; 222 | break; 223 | } 224 | case STAR_EQUAL: { 225 | Object current = environment.get(expr.name); 226 | checkNumberOperands(expr.equals, current, value); 227 | value = (double) current * (double) value; 228 | break; 229 | } 230 | case SLASH_EQUAL: { 231 | Object current = environment.get(expr.name); 232 | checkNumberOperands(expr.equals, current, value); 233 | value = (double) current / (double) value; 234 | break; 235 | } 236 | case STAR_STAR_EQUAL: { 237 | Object current = environment.get(expr.name); 238 | checkNumberOperands(expr.equals, current, value); 239 | value = Math.pow((double) current, (double) value); 240 | break; 241 | } 242 | } 243 | 244 | Integer distance = locals.get(expr); 245 | environment.ancestor(distance).assign(expr.name, value); 246 | return value; 247 | } 248 | 249 | @Override 250 | public Object visitBinaryExpr(Expr.Binary expr) { 251 | Object left = evaluate(expr.left); 252 | Object right = evaluate(expr.right); 253 | 254 | switch (expr.operator.type) { 255 | case GREATER: 256 | checkNumberOperands(expr.operator, left, right); 257 | return (double) left > (double) right; 258 | case GREATER_EQUAL: 259 | checkNumberOperands(expr.operator, left, right); 260 | return (double) left >= (double) right; 261 | case LESS: 262 | checkNumberOperands(expr.operator, left, right); 263 | return (double) left < (double) right; 264 | case LESS_EQUAL: 265 | checkNumberOperands(expr.operator, left, right); 266 | return (double) left <= (double) right; 267 | case BANG_EQUAL: 268 | return !isEqual(left, right); 269 | case EQUAL_EQUAL: 270 | return isEqual(left, right); 271 | case MINUS: 272 | checkNumberOperands(expr.operator, left, right); 273 | return (double) left - (double) right; 274 | case PLUS: 275 | if (left instanceof Double && right instanceof Double) { 276 | return (double) left + (double) right; 277 | } 278 | 279 | if (left instanceof String && right instanceof String) { 280 | return (String) left + (String) right; 281 | } 282 | 283 | /* converting everything to string 284 | if(left instanceof String || right instanceof String) { 285 | return stringify(left) + stringify(right); 286 | } 287 | */ 288 | 289 | throw new RuntimeError(expr.operator, 290 | "Operands must be two numbers or two strings."); 291 | case SLASH: 292 | checkNumberOperands(expr.operator, left, right); 293 | //code to prevent division by zero 294 | //if ((double) right == 0) throw new RuntimeError(expr.operator, "Division by zero not allowed."); 295 | return (double) left / (double) right; 296 | case STAR: 297 | if (left instanceof Double && !(right instanceof Double)) { 298 | return multiplyString(stringify(right), (double) left, expr.operator); 299 | } 300 | if (!(left instanceof Double) && right instanceof Double) { 301 | return multiplyString(stringify(left), (double) right, expr.operator); 302 | } 303 | 304 | checkNumberOperands(expr.operator, left, right); 305 | return (double) left * (double) right; 306 | case STAR_STAR: 307 | checkNumberOperands(expr.operator, left, right); 308 | return Math.pow((double) left, (double) right); 309 | case COMMA: 310 | return right; 311 | } 312 | 313 | // Unreachable. 314 | return null; 315 | } 316 | 317 | @Override 318 | public Object visitCallExpr(Expr.Call expr) { 319 | Object callee = evaluate(expr.callee); 320 | 321 | List arguments = new ArrayList<>(); 322 | for (Expr argument : expr.arguments) { 323 | arguments.add(evaluate(argument)); 324 | } 325 | 326 | if (!(callee instanceof Callable)) { 327 | throw new RuntimeError(expr.paren, 328 | "Can only call functions and classes."); 329 | } 330 | 331 | Callable function = (Callable)callee; 332 | if (arguments.size() != function.arity()) { 333 | throw new RuntimeError(expr.paren, "Expected " + 334 | function.arity() + " arguments but got " + 335 | arguments.size() + "."); 336 | } 337 | return function.call(this, arguments); 338 | } 339 | 340 | @Override 341 | public Object visitGetExpr(Expr.Get expr) { 342 | Object object = evaluate(expr.object); 343 | 344 | if (object instanceof Instance) { 345 | Object result = ((Instance) object).get(expr.name); 346 | if (result instanceof Function && 347 | ((Function) result).isGetter()) { 348 | result = ((Function) result).call(this, null); 349 | } 350 | 351 | return result; 352 | } 353 | 354 | throw new RuntimeError(expr.name, 355 | "Only instances have properties."); 356 | } 357 | 358 | @Override 359 | public Object visitSetExpr(Expr.Set expr) { 360 | Object object = evaluate(expr.object); 361 | 362 | if (!(object instanceof Instance)) { 363 | throw new RuntimeError(expr.name, "Only instances have fields."); 364 | } 365 | 366 | Object value = evaluate(expr.value); 367 | ((Instance)object).set(expr.name, value); 368 | return value; 369 | } 370 | 371 | @Override 372 | public Object visitSuperExpr(Expr.Super expr) { 373 | int distance = locals.get(expr); 374 | FailClass superclass = (FailClass) environment.getAt( 375 | distance, "super"); 376 | 377 | // "this" is always one level nearer than "super"'s environment. 378 | Instance object = (Instance)environment.getAt( 379 | distance - 1, "this"); 380 | 381 | Function method = superclass.findMethod( 382 | object, expr.method.lexeme); 383 | 384 | if (method == null) { 385 | throw new RuntimeError(expr.method, 386 | "Undefined property '" + expr.method.lexeme + "'."); 387 | } 388 | 389 | return method; 390 | } 391 | 392 | @Override 393 | public Object visitThisExpr(Expr.This expr) { 394 | return lookUpVariable(expr.keyword, expr); 395 | } 396 | 397 | public Object visitFunctionExpr(Expr.Function function) { 398 | return new Function(null, function, environment, false); 399 | } 400 | 401 | @Override 402 | public Object visitGroupingExpr(Expr.Grouping expr) { 403 | return evaluate(expr.expression); 404 | } 405 | 406 | @Override 407 | public Object visitLiteralExpr(Expr.Literal expr) { 408 | return expr.value; 409 | } 410 | 411 | @Override 412 | public Object visitLogicalExpr(Expr.Logical expr) { 413 | Object left = evaluate(expr.left); 414 | 415 | if (expr.operator.type == TokenType.OR) { 416 | if (isTruthy(left)) return left; 417 | } else { 418 | if (!isTruthy(left)) return left; 419 | } 420 | 421 | return evaluate(expr.right); 422 | } 423 | 424 | @Override 425 | public Void visitBreakStmt(Stmt.Break stmt) { 426 | throw new BreakJump(); 427 | } 428 | 429 | @Override 430 | public Void visitContinueStmt(Stmt.Continue stmt) { 431 | throw new ContinueJump(); 432 | } 433 | 434 | @Override 435 | public Object visitUnaryExpr(Expr.Unary expr) { 436 | Object right = evaluate(expr.right); 437 | 438 | switch (expr.operator.type) { 439 | case BANG: 440 | return !isTruthy(right); 441 | case MINUS: 442 | if("muffin".equals(stringify(right))) { 443 | throw new RuntimeError(expr.operator, "I don't know, man, can you negate a muffin?"); 444 | } 445 | checkNumberOperand(expr.operator, right); 446 | return -(double) right; 447 | case PLUS_PLUS: { 448 | if (!(expr.right instanceof Expr.Variable)) 449 | throw new RuntimeError(expr.operator, 450 | "Operand of an increment operator must be a variable."); 451 | 452 | checkNumberOperand(expr.operator, right); 453 | double value = (double) right; 454 | Expr.Variable variable = (Expr.Variable) expr.right; 455 | environment.assign(variable.name, value + 1); 456 | 457 | if (expr.postfix) 458 | return value; 459 | else 460 | return value + 1; 461 | } 462 | case MINUS_MINUS: { 463 | if (!(expr.right instanceof Expr.Variable)) 464 | throw new RuntimeError(expr.operator, 465 | "Operand of a decrement operator must be a variable."); 466 | 467 | checkNumberOperand(expr.operator, right); 468 | double value = (double) right; 469 | Expr.Variable variable = (Expr.Variable) expr.right; 470 | environment.assign(variable.name, value - 1); 471 | 472 | if (expr.postfix) 473 | return value; 474 | else 475 | return value - 1; 476 | } 477 | } 478 | 479 | // Unreachable. 480 | return null; 481 | } 482 | 483 | @Override 484 | public Object visitVariableExpr(Expr.Variable expr) { 485 | return lookUpVariable(expr.name, expr); 486 | } 487 | 488 | private Object lookUpVariable(Token name, Expr expr) { 489 | Integer distance = locals.get(expr); 490 | Object value; 491 | 492 | value = environment.ancestor(distance).get(name); 493 | 494 | if (value == uninitialized) { 495 | throw new RuntimeError(name, 496 | "Variable must be initialized before use."); 497 | } 498 | 499 | return value; 500 | } 501 | 502 | @Override 503 | public Object visitTernaryExpr(Expr.Ternary expr) { 504 | Object check = evaluate(expr.expr); 505 | 506 | if (isTruthy(check)) 507 | return evaluate(expr.thenBranch); 508 | else 509 | return evaluate(expr.elseBranch); 510 | } 511 | 512 | private boolean isTruthy(Object object) { 513 | if (object == null) return false; 514 | if (object instanceof Boolean) return (boolean) object; 515 | return true; 516 | } 517 | 518 | private void checkNumberOperand(Token operator, Object operand) { 519 | if (operand instanceof Double) return; 520 | throw new RuntimeError(operator, "Operand must be a number."); 521 | } 522 | 523 | private void checkNumberOperands(Token operator, 524 | Object left, Object right) { 525 | if (left instanceof Double && right instanceof Double) return; 526 | 527 | throw new RuntimeError(operator, "Operands must be numbers."); 528 | } 529 | 530 | private boolean isEqual(Object a, Object b) { 531 | // none is only equal to none. 532 | return a == null && b == null || a != null && a.equals(b); 533 | } 534 | 535 | private String stringify(Object object) { 536 | if (object == null) return "none"; 537 | 538 | // Hack. Work around Java adding ".0" to integer-valued doubles. 539 | if (object instanceof Double) { 540 | String text = object.toString(); 541 | if (text.endsWith(".0")) { 542 | text = text.substring(0, text.length() - 2); 543 | } 544 | return text; 545 | } 546 | 547 | return object.toString(); 548 | } 549 | 550 | private String multiplyString(String s, double n, Token token) { 551 | if (n % 1 != 0) throw new RuntimeError(token, 552 | "String multiplier must be an integer."); 553 | int multiplier = (int) n; 554 | if (multiplier < 0) multiplier = 0; 555 | 556 | return String.join("", Collections.nCopies(multiplier, s)); 557 | } 558 | } -------------------------------------------------------------------------------- /src/com/company/fail/Resolver.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Stack; 7 | 8 | class Resolver implements Expr.Visitor, Stmt.Visitor { 9 | private final Interpreter interpreter; 10 | private final Stack> scopes = new Stack<>(); 11 | private FunctionType currentFunction = FunctionType.NONE; 12 | private boolean preventAssignment = false; 13 | private ClassType currentClass = ClassType.NONE; 14 | 15 | Resolver(Interpreter interpreter) { 16 | this.interpreter = interpreter; 17 | } 18 | 19 | private enum ClassType { 20 | NONE, 21 | CLASS, 22 | SUBCLASS 23 | } 24 | 25 | private enum FunctionType { 26 | NONE, 27 | FUNCTION, 28 | INITIALIZER, 29 | METHOD 30 | } 31 | 32 | private static class Variable { 33 | final Token name; 34 | VariableState state; 35 | 36 | private Variable(Token name, VariableState state) { 37 | this.name = name; 38 | this.state = state; 39 | } 40 | } 41 | 42 | private enum VariableState { 43 | DECLARED, 44 | DEFINED, 45 | READ 46 | } 47 | 48 | void resolve(List statements) { 49 | for (Stmt statement : statements) { 50 | resolve(statement); 51 | } 52 | } 53 | 54 | @Override 55 | public Void visitBlockStmt(Stmt.Block stmt) { 56 | beginScope(); 57 | resolve(stmt.statements); 58 | endScope(); 59 | return null; 60 | } 61 | 62 | @Override 63 | public Void visitClassStmt(Stmt.Class stmt) { 64 | declare(stmt.name); 65 | define(stmt.name); 66 | ClassType enclosingClass = currentClass; 67 | currentClass = ClassType.CLASS; 68 | 69 | if (stmt.superclass != null) { 70 | currentClass = ClassType.SUBCLASS; 71 | resolve(stmt.superclass); 72 | beginScope(); 73 | scopes.peek().put("super", createSystemVariable("super", true)); 74 | } 75 | 76 | beginScope(); 77 | scopes.peek().put("this", createSystemVariable("this", true)); 78 | 79 | for (Stmt.Function method : stmt.methods) { 80 | FunctionType declaration = FunctionType.METHOD; 81 | if (method.name.lexeme.equals("init")) { 82 | declaration = FunctionType.INITIALIZER; 83 | } 84 | resolveFunction(method, declaration); 85 | } 86 | 87 | for (Stmt.Function method : stmt.classMethods) { 88 | beginScope(); 89 | scopes.peek().put("this", createSystemVariable("this", true)); 90 | resolveFunction(method, FunctionType.METHOD); 91 | endScope(); 92 | } 93 | 94 | endScope(); 95 | 96 | if (stmt.superclass != null) endScope(); 97 | 98 | currentClass = enclosingClass; 99 | return null; 100 | } 101 | 102 | @Override 103 | public Void visitExpressionStmt(Stmt.Expression stmt) { 104 | resolve(stmt.expression); 105 | return null; 106 | } 107 | 108 | @Override 109 | public Void visitFunctionStmt(Stmt.Function stmt) { 110 | declare(stmt.name); 111 | define(stmt.name); 112 | 113 | resolveFunction(stmt, FunctionType.FUNCTION); 114 | return null; 115 | } 116 | 117 | @Override 118 | public Void visitIfStmt(Stmt.If stmt) { 119 | preventAssignment = true; 120 | resolve(stmt.condition); 121 | preventAssignment = false; 122 | resolve(stmt.thenBranch); 123 | if (stmt.elseBranch != null) resolve(stmt.elseBranch); 124 | return null; 125 | } 126 | 127 | @Override 128 | public Void visitPrintStmt(Stmt.Print stmt) { 129 | resolve(stmt.expression); 130 | return null; 131 | } 132 | 133 | @Override 134 | public Void visitReturnStmt(Stmt.Return stmt) { 135 | if (currentFunction == FunctionType.NONE) { 136 | Fail.error(stmt.keyword, "Cannot return from top-level code."); 137 | } 138 | 139 | if (stmt.value != null) { 140 | if (currentFunction == FunctionType.INITIALIZER) { 141 | Fail.error(stmt.keyword, 142 | "Cannot return a value from an initializer."); 143 | } 144 | 145 | resolve(stmt.value); 146 | } 147 | 148 | return null; 149 | } 150 | 151 | @Override 152 | public Void visitVarStmt(Stmt.Var stmt) { 153 | declare(stmt.name); 154 | if (stmt.initializer != null) { 155 | resolve(stmt.initializer); 156 | } 157 | define(stmt.name); 158 | return null; 159 | } 160 | 161 | @Override 162 | public Void visitWhileStmt(Stmt.While stmt) { 163 | preventAssignment = true; 164 | resolve(stmt.condition); 165 | preventAssignment = false; 166 | resolve(stmt.body); 167 | return null; 168 | } 169 | 170 | @Override 171 | public Void visitBreakStmt(Stmt.Break stmt) { 172 | return null; 173 | } 174 | 175 | @Override 176 | public Void visitContinueStmt(Stmt.Continue stmt) { 177 | return null; 178 | } 179 | 180 | @Override 181 | public Void visitVariableExpr(Expr.Variable expr) { 182 | if (!scopes.isEmpty() && 183 | scopes.peek().containsKey(expr.name.lexeme) && 184 | scopes.peek().get(expr.name.lexeme).state == VariableState.DECLARED) { 185 | Fail.error(expr.name, 186 | "Cannot read local variable in its own initializer."); 187 | } 188 | 189 | resolveReference(expr, expr.name, true); 190 | return null; 191 | } 192 | 193 | @Override 194 | public Void visitAssignExpr(Expr.Assign expr) { 195 | if (preventAssignment) 196 | Fail.error(expr.equals, "Assignment is not allowed within if, loop or ternary condition."); 197 | 198 | resolve(expr.value); 199 | resolveReference(expr, expr.name, false); 200 | return null; 201 | } 202 | 203 | @Override 204 | public Void visitBinaryExpr(Expr.Binary expr) { 205 | resolve(expr.left); 206 | resolve(expr.right); 207 | return null; 208 | } 209 | 210 | @Override 211 | public Void visitFunctionExpr(Expr.Function expr) { 212 | FunctionType enclosingFunction = currentFunction; 213 | currentFunction = FunctionType.FUNCTION; 214 | 215 | beginScope(); 216 | for (Token param : expr.parameters) { 217 | declare(param); 218 | define(param); 219 | } 220 | resolve(expr.body); 221 | endScope(); 222 | currentFunction = enclosingFunction; 223 | return null; 224 | } 225 | 226 | @Override 227 | public Void visitCallExpr(Expr.Call expr) { 228 | resolve(expr.callee); 229 | 230 | for (Expr argument : expr.arguments) { 231 | resolve(argument); 232 | } 233 | 234 | return null; 235 | } 236 | 237 | @Override 238 | public Void visitGetExpr(Expr.Get expr) { 239 | resolve(expr.object); 240 | return null; 241 | } 242 | 243 | @Override 244 | public Void visitSetExpr(Expr.Set expr) { 245 | resolve(expr.value); 246 | resolve(expr.object); 247 | return null; 248 | } 249 | 250 | @Override 251 | public Void visitSuperExpr(Expr.Super expr) { 252 | if (currentClass == ClassType.NONE) { 253 | Fail.error(expr.keyword, 254 | "Cannot use 'super' outside of a class."); 255 | } else if (currentClass != ClassType.SUBCLASS) { 256 | Fail.error(expr.keyword, 257 | "Cannot use 'super' in a class with no superclass."); 258 | } 259 | 260 | resolveReference(expr, expr.keyword, true); 261 | return null; 262 | } 263 | 264 | @Override 265 | public Void visitThisExpr(Expr.This expr) { 266 | if (currentClass == ClassType.NONE) { 267 | Fail.error(expr.keyword, 268 | "Cannot use 'this' outside of a class."); 269 | return null; 270 | } 271 | resolveReference(expr, expr.keyword, true); 272 | return null; 273 | } 274 | 275 | @Override 276 | public Void visitGroupingExpr(Expr.Grouping expr) { 277 | resolve(expr.expression); 278 | return null; 279 | } 280 | 281 | @Override 282 | public Void visitLiteralExpr(Expr.Literal expr) { 283 | return null; 284 | } 285 | 286 | @Override 287 | public Void visitLogicalExpr(Expr.Logical expr) { 288 | resolve(expr.left); 289 | resolve(expr.right); 290 | return null; 291 | } 292 | 293 | @Override 294 | public Void visitUnaryExpr(Expr.Unary expr) { 295 | resolve(expr.right); 296 | return null; 297 | } 298 | 299 | @Override 300 | public Void visitTernaryExpr(Expr.Ternary expr) { 301 | preventAssignment = true; 302 | resolve(expr.expr); 303 | preventAssignment = false; 304 | resolve(expr.thenBranch); 305 | resolve(expr.elseBranch); 306 | return null; 307 | } 308 | 309 | private void resolve(Stmt stmt) { 310 | stmt.accept(this); 311 | } 312 | 313 | void resolve(Expr expr) { 314 | expr.accept(this); 315 | } 316 | 317 | private void resolveFunction(Stmt.Function function, FunctionType type) { 318 | FunctionType enclosingFunction = currentFunction; 319 | currentFunction = type; 320 | 321 | beginScope(); 322 | if (function.function.parameters != null) { 323 | for (Token param : function.function.parameters) { 324 | declare(param); 325 | define(param); 326 | } 327 | } 328 | resolve(function.function.body); 329 | endScope(); 330 | currentFunction = enclosingFunction; 331 | } 332 | 333 | private void beginScope() { 334 | scopes.push(new HashMap()); 335 | } 336 | 337 | private void endScope() { 338 | Map scope = scopes.pop(); 339 | 340 | for (Map.Entry entry : scope.entrySet()) { 341 | if (entry.getValue().state == VariableState.DEFINED) { 342 | Fail.warning(entry.getValue().name, "Local variable is not used."); 343 | } 344 | } 345 | } 346 | 347 | private void declare(Token name) { 348 | if (scopes.isEmpty()) return; 349 | 350 | Map scope = scopes.peek(); 351 | if (scope.containsKey(name.lexeme)) { 352 | Fail.error(name, 353 | "Variable with this name already declared in this scope."); 354 | } 355 | 356 | scope.put(name.lexeme, new Variable(name, VariableState.DECLARED)); 357 | } 358 | 359 | private void define(Token name) { 360 | if (scopes.isEmpty()) return; 361 | scopes.peek().get(name.lexeme).state = VariableState.DEFINED; 362 | } 363 | 364 | private void resolveReference(Expr expr, Token name, boolean isRead) { 365 | for (int i = scopes.size() - 1; i >= 0; i--) { 366 | if (scopes.get(i).containsKey(name.lexeme)) { 367 | interpreter.resolve(expr, scopes.size() - 1 - i); 368 | 369 | // Mark it used. 370 | if (isRead) { 371 | scopes.get(i).get(name.lexeme).state = VariableState.READ; 372 | } 373 | return; 374 | } 375 | } 376 | 377 | // Not found. Assume it is global. 378 | interpreter.resolve(expr, scopes.size()); 379 | } 380 | 381 | private Variable createSystemVariable(String name, Object value) { 382 | return new Variable(new Token(TokenType.IDENTIFIER, name, value, 0), VariableState.READ); 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /src/com/company/fail/Return.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | class Return extends RuntimeException { 4 | final Object value; 5 | 6 | Return(Object value) { 7 | super(null, null, false, false); 8 | this.value = value; 9 | } 10 | } -------------------------------------------------------------------------------- /src/com/company/fail/RpnPrinter.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | class RpnPrinter implements Expr.Visitor { 4 | String print(Expr expr) { 5 | return expr.accept(this); 6 | } 7 | 8 | @Override 9 | public String visitAssignExpr(Expr.Assign expr) { 10 | return postfix(expr.name + " =", expr.value); 11 | } 12 | 13 | @Override 14 | public String visitBinaryExpr(Expr.Binary expr) { 15 | return postfix(expr.operator.lexeme, expr.left, expr.right); 16 | } 17 | 18 | @Override 19 | public String visitFunctionExpr(Expr.Function expr) { 20 | return "lambda"; 21 | } 22 | 23 | @Override 24 | public String visitCallExpr(Expr.Call expr) { 25 | return print(expr.callee); 26 | } 27 | 28 | @Override 29 | public String visitSuperExpr(Expr.Super expr) { 30 | return "super"; 31 | } 32 | 33 | @Override 34 | public String visitThisExpr(Expr.This expr) { 35 | return "this"; 36 | } 37 | 38 | @Override 39 | public String visitGetExpr(Expr.Get expr) { 40 | return "get " + expr.name.lexeme; 41 | } 42 | 43 | @Override 44 | public String visitSetExpr(Expr.Set expr) { 45 | return "set " + expr.name.lexeme; 46 | } 47 | 48 | @Override 49 | public String visitTernaryExpr(Expr.Ternary expr) { 50 | return postfix("?", expr.expr, expr.thenBranch, expr.elseBranch); 51 | } 52 | 53 | @Override 54 | public String visitVariableExpr(Expr.Variable expr) { 55 | return expr.name.lexeme; 56 | } 57 | 58 | @Override 59 | public String visitGroupingExpr(Expr.Grouping expr) { 60 | return print(expr.expression); 61 | } 62 | 63 | @Override 64 | public String visitLiteralExpr(Expr.Literal expr) { 65 | return expr.value.toString(); 66 | } 67 | 68 | @Override 69 | public String visitLogicalExpr(Expr.Logical expr) { 70 | return postfix(expr.operator.lexeme, expr.left, expr.right); 71 | } 72 | 73 | @Override 74 | public String visitUnaryExpr(Expr.Unary expr) { 75 | return expr.right.accept(this) + " " + expr.operator.lexeme + "u"; 76 | } 77 | 78 | private String postfix(String name, Expr... exprs) { 79 | StringBuilder builder = new StringBuilder(); 80 | 81 | for (Expr expr : exprs) { 82 | builder.append(" "); 83 | builder.append(expr.accept(this)); 84 | } 85 | 86 | builder.append(" ").append(name); 87 | 88 | return builder.toString(); 89 | } 90 | 91 | public static void main(String[] args) { 92 | Expr expression = new Expr.Binary( 93 | new Expr.Unary( 94 | new Token(TokenType.MINUS, "-", null, 1), 95 | new Expr.Literal(123), 96 | false), 97 | new Token(TokenType.STAR, "*", null, 1), 98 | new Expr.Grouping( 99 | new Expr.Literal(45.67))); 100 | 101 | System.out.println(new RpnPrinter().print(expression)); 102 | 103 | Expr expression2 = new Expr.Binary( 104 | new Expr.Assign( 105 | new Token(TokenType.IDENTIFIER, "a", null, 1), 106 | new Expr.Literal(1), 107 | new Token(TokenType.EQUAL, "=", null, 1)), 108 | new Token(TokenType.STAR, "*", null, 1), 109 | new Expr.Literal("str") 110 | ); 111 | 112 | System.out.println(new RpnPrinter().print(expression2)); 113 | } 114 | } -------------------------------------------------------------------------------- /src/com/company/fail/RuntimeError.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | class RuntimeError extends RuntimeException { 4 | final Token token; 5 | 6 | RuntimeError(Token token, String message) { 7 | super(message); 8 | this.token = token; 9 | } 10 | } -------------------------------------------------------------------------------- /src/com/company/fail/Scanner.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import static com.company.fail.TokenType.*; 9 | 10 | class Scanner { 11 | private final StringBuilder source; 12 | private final List tokens = new ArrayList<>(); 13 | private int start = 0; 14 | private int current = 0; 15 | private int line = 1; 16 | private static final Map keywords; 17 | 18 | static { 19 | keywords = new HashMap<>(); 20 | keywords.put("and", AND); 21 | keywords.put("class", CLASS); 22 | keywords.put("else", ELSE); 23 | keywords.put("false", FALSE); 24 | keywords.put("for", FOR); 25 | keywords.put("fun", FUN); 26 | keywords.put("if", IF); 27 | keywords.put("none", NONE); 28 | keywords.put("or", OR); 29 | keywords.put("print", PRINT); 30 | keywords.put("return", RETURN); 31 | keywords.put("super", SUPER); 32 | keywords.put("this", THIS); 33 | keywords.put("true", TRUE); 34 | keywords.put("var", VAR); 35 | keywords.put("do", DO); 36 | keywords.put("while", WHILE); 37 | keywords.put("break", BREAK); 38 | keywords.put("continue", CONTINUE); 39 | } 40 | 41 | Scanner(String source) { 42 | this.source = new StringBuilder(source); 43 | } 44 | 45 | List scanTokens() { 46 | while (!isAtEnd()) { 47 | // We are the beginning of the next lexeme. 48 | start = current; 49 | scanToken(); 50 | } 51 | 52 | tokens.add(new Token(EOF, "", null, line)); 53 | return tokens; 54 | } 55 | 56 | private boolean isAtEnd() { 57 | return current >= source.length(); 58 | } 59 | 60 | private void scanToken() { 61 | char c = advance(); 62 | switch (c) { 63 | case '(': 64 | addToken(LEFT_PAREN); 65 | break; 66 | case ')': 67 | addToken(RIGHT_PAREN); 68 | break; 69 | case '{': 70 | addToken(LEFT_BRACE); 71 | break; 72 | case '}': 73 | addToken(RIGHT_BRACE); 74 | break; 75 | case ',': 76 | addToken(COMMA); 77 | break; 78 | case '.': 79 | addToken(DOT); 80 | break; 81 | case '-': 82 | addToken(match('-') ? MINUS_MINUS : 83 | match('=') ? MINUS_EQUAL : 84 | MINUS); 85 | break; 86 | case '+': 87 | addToken(match('+') ? PLUS_PLUS : 88 | match('=') ? PLUS_EQUAL : 89 | PLUS); 90 | break; 91 | case ';': 92 | addToken(SEMICOLON); 93 | break; 94 | case ':': 95 | addToken(COLON); 96 | break; 97 | case '?': 98 | addToken(QUESTION_MARK); 99 | break; 100 | case '*': 101 | addToken(match('*') ? 102 | match('=') ? STAR_STAR_EQUAL : STAR_STAR : 103 | match('=') ? STAR_EQUAL : STAR); 104 | break; 105 | case '!': 106 | addToken(match('=') ? BANG_EQUAL : BANG); 107 | break; 108 | case '=': 109 | addToken(match('=') ? EQUAL_EQUAL : EQUAL); 110 | break; 111 | case '<': 112 | addToken(match('=') ? LESS_EQUAL : LESS); 113 | break; 114 | case '>': 115 | addToken(match('=') ? GREATER_EQUAL : GREATER); 116 | break; 117 | case '/': 118 | if (match('/')) { 119 | // A comment goes until the end of the line. 120 | while (peek() != '\n' && !isAtEnd()) advance(); 121 | } else if (match('*')) { 122 | int toMatch = 1; 123 | while (!isAtEnd()) { 124 | if (peek() == '*' && peekNext() == '/') toMatch--; 125 | if (peek() == '/' && peekNext() == '*') toMatch++; 126 | if (peek() == '\n') line++; 127 | if (toMatch == 0) break; 128 | advance(); 129 | } 130 | if (peek() == '*' && peekNext() == '/') { 131 | advance(); 132 | advance(); 133 | } else { 134 | Fail.error(line, "No closing of block comment."); 135 | } 136 | } else if (match('=')) { 137 | addToken(SLASH_EQUAL); 138 | } else { 139 | addToken(SLASH); 140 | } 141 | break; 142 | 143 | case ' ': 144 | case '\r': 145 | case '\t': 146 | // Ignore whitespace. 147 | break; 148 | 149 | case '\n': 150 | line++; 151 | break; 152 | 153 | case '"': 154 | string(); 155 | break; 156 | 157 | default: 158 | if (isDigit(c)) { 159 | number(); 160 | } else if (isAlpha(c)) { 161 | identifier(); 162 | } else { 163 | Fail.error(line, "Unexpected character."); 164 | } 165 | break; 166 | } 167 | } 168 | 169 | private char advance() { 170 | current++; 171 | return source.charAt(current - 1); 172 | } 173 | 174 | private void addToken(TokenType type) { 175 | addToken(type, null); 176 | } 177 | 178 | private void addToken(TokenType type, Object literal) { 179 | String text = source.substring(start, current); 180 | tokens.add(new Token(type, text, literal, line)); 181 | } 182 | 183 | private boolean match(char expected) { 184 | if (isAtEnd()) return false; 185 | if (source.charAt(current) != expected) return false; 186 | 187 | current++; 188 | return true; 189 | } 190 | 191 | private char peek() { 192 | if (current >= source.length()) return '\0'; 193 | return source.charAt(current); 194 | } 195 | 196 | private char peekNext() { 197 | if (current + 1 >= source.length()) return '\0'; 198 | return source.charAt(current + 1); 199 | } 200 | 201 | private void string() { 202 | while (peek() != '"' && !isAtEnd()) { 203 | char first = peek(); 204 | if (first == '\n') line++; 205 | 206 | //handle escape sequences 207 | if(first == '\\'){ 208 | char second = peekNext(); 209 | if (second == '\"') { 210 | source.deleteCharAt(current); 211 | source.setCharAt(current, '\"'); 212 | } else if (second == '\\') { 213 | source.deleteCharAt(current); 214 | source.setCharAt(current, '\\'); 215 | } else if (second == 'b') { 216 | source.deleteCharAt(current); 217 | source.setCharAt(current, '\b'); 218 | } else if (second == 'r') { 219 | source.deleteCharAt(current); 220 | source.setCharAt(current, '\r'); 221 | } else if (second == 'n') { 222 | source.deleteCharAt(current); 223 | source.setCharAt(current, '\n'); 224 | } else if (second == 't') { 225 | source.deleteCharAt(current); 226 | source.setCharAt(current, '\t'); 227 | } 228 | } 229 | advance(); 230 | } 231 | 232 | // Unterminated string. 233 | if (isAtEnd()) { 234 | Fail.error(line, "Unterminated string."); 235 | return; 236 | } 237 | 238 | // The closing ". 239 | advance(); 240 | 241 | // Trim the surrounding quotes. 242 | String value = source.substring(start + 1, current - 1); 243 | addToken(STRING, value); 244 | } 245 | 246 | private boolean isDigit(char c) { 247 | return c >= '0' && c <= '9'; 248 | } 249 | 250 | private void number() { 251 | while (isDigit(peek())) advance(); 252 | 253 | // Look for a fractional part. 254 | if (peek() == '.' && isDigit(peekNext())) { 255 | // Consume the "." 256 | advance(); 257 | 258 | while (isDigit(peek())) advance(); 259 | } 260 | 261 | addToken(NUMBER, 262 | Double.parseDouble(source.substring(start, current))); 263 | } 264 | 265 | private void identifier() { 266 | while (isAlphaNumeric(peek())) advance(); 267 | 268 | // See if the identifier is a reserved word. 269 | String text = source.substring(start, current); 270 | 271 | TokenType type = keywords.get(text); 272 | if (type == null) type = IDENTIFIER; 273 | addToken(type); 274 | } 275 | 276 | private boolean isAlpha(char c) { 277 | return (c >= 'a' && c <= 'z') || 278 | (c >= 'A' && c <= 'Z') || 279 | c == '_'; 280 | } 281 | 282 | private boolean isAlphaNumeric(char c) { 283 | return isAlpha(c) || isDigit(c); 284 | } 285 | } -------------------------------------------------------------------------------- /src/com/company/fail/Stmt.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | import java.util.List; 4 | 5 | abstract class Stmt { 6 | interface Visitor { 7 | R visitBlockStmt(Block stmt); 8 | R visitClassStmt(Class stmt); 9 | R visitExpressionStmt(Expression stmt); 10 | R visitFunctionStmt(Function stmt); 11 | R visitIfStmt(If stmt); 12 | R visitPrintStmt(Print stmt); 13 | R visitReturnStmt(Return stmt); 14 | R visitVarStmt(Var stmt); 15 | R visitWhileStmt(While stmt); 16 | R visitBreakStmt(Break stmt); 17 | R visitContinueStmt(Continue stmt); 18 | } 19 | 20 | static class Block extends Stmt { 21 | Block(List statements) { 22 | this.statements = statements; 23 | } 24 | 25 | R accept(Visitor visitor) { 26 | return visitor.visitBlockStmt(this); 27 | } 28 | 29 | final List statements; 30 | } 31 | 32 | static class Class extends Stmt { 33 | Class(Token name, Expr superclass, List methods, List classMethods) { 34 | this.name = name; 35 | this.superclass = superclass; 36 | this.methods = methods; 37 | this.classMethods = classMethods; 38 | } 39 | 40 | R accept(Visitor visitor) { 41 | return visitor.visitClassStmt(this); 42 | } 43 | 44 | final Token name; 45 | final Expr superclass; 46 | final List methods; 47 | final List classMethods; 48 | } 49 | 50 | static class Expression extends Stmt { 51 | Expression(Expr expression) { 52 | this.expression = expression; 53 | } 54 | 55 | R accept(Visitor visitor) { 56 | return visitor.visitExpressionStmt(this); 57 | } 58 | 59 | final Expr expression; 60 | } 61 | 62 | static class Function extends Stmt { 63 | Function(Token name, Expr.Function function) { 64 | this.name = name; 65 | this.function = function; 66 | } 67 | 68 | R accept(Visitor visitor) { 69 | return visitor.visitFunctionStmt(this); 70 | } 71 | 72 | final Token name; 73 | final Expr.Function function; 74 | } 75 | 76 | static class If extends Stmt { 77 | If(Expr condition, Stmt thenBranch, Stmt elseBranch) { 78 | this.condition = condition; 79 | this.thenBranch = thenBranch; 80 | this.elseBranch = elseBranch; 81 | } 82 | 83 | R accept(Visitor visitor) { 84 | return visitor.visitIfStmt(this); 85 | } 86 | 87 | final Expr condition; 88 | final Stmt thenBranch; 89 | final Stmt elseBranch; 90 | } 91 | 92 | static class Print extends Stmt { 93 | Print(Expr expression) { 94 | this.expression = expression; 95 | } 96 | 97 | R accept(Visitor visitor) { 98 | return visitor.visitPrintStmt(this); 99 | } 100 | 101 | final Expr expression; 102 | } 103 | 104 | static class Return extends Stmt { 105 | Return(Token keyword, Expr value) { 106 | this.keyword = keyword; 107 | this.value = value; 108 | } 109 | 110 | R accept(Visitor visitor) { 111 | return visitor.visitReturnStmt(this); 112 | } 113 | 114 | final Token keyword; 115 | final Expr value; 116 | } 117 | 118 | static class Var extends Stmt { 119 | Var(Token name, Expr initializer) { 120 | this.name = name; 121 | this.initializer = initializer; 122 | } 123 | 124 | R accept(Visitor visitor) { 125 | return visitor.visitVarStmt(this); 126 | } 127 | 128 | final Token name; 129 | final Expr initializer; 130 | } 131 | 132 | static class While extends Stmt { 133 | While(Expr condition, Stmt body) { 134 | this.condition = condition; 135 | this.body = body; 136 | } 137 | 138 | R accept(Visitor visitor) { 139 | return visitor.visitWhileStmt(this); 140 | } 141 | 142 | final Expr condition; 143 | final Stmt body; 144 | } 145 | 146 | static class Break extends Stmt { 147 | Break() { 148 | } 149 | 150 | R accept(Visitor visitor) { 151 | return visitor.visitBreakStmt(this); 152 | } 153 | 154 | } 155 | 156 | static class Continue extends Stmt { 157 | Continue() { 158 | } 159 | 160 | R accept(Visitor visitor) { 161 | return visitor.visitContinueStmt(this); 162 | } 163 | 164 | } 165 | 166 | abstract R accept(Visitor visitor); 167 | } 168 | -------------------------------------------------------------------------------- /src/com/company/fail/Token.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | import com.company.fail.TokenType; 4 | 5 | class Token { 6 | final TokenType type; 7 | final String lexeme; 8 | final Object literal; 9 | final int line; 10 | 11 | Token(TokenType type, String lexeme, Object literal, int line) { 12 | this.type = type; 13 | this.lexeme = lexeme; 14 | this.literal = literal; 15 | this.line = line; 16 | } 17 | 18 | public String toString() { 19 | return type + " " + lexeme + " " + literal; 20 | } 21 | } -------------------------------------------------------------------------------- /src/com/company/fail/TokenType.java: -------------------------------------------------------------------------------- 1 | package com.company.fail; 2 | 3 | enum TokenType { 4 | // Single-character tokens. 5 | LEFT_PAREN, RIGHT_PAREN, LEFT_BRACE, RIGHT_BRACE, 6 | COMMA, DOT, MINUS, PLUS, SEMICOLON, SLASH, STAR, 7 | 8 | // One or two character tokens. 9 | BANG, BANG_EQUAL, 10 | EQUAL, EQUAL_EQUAL, 11 | GREATER, GREATER_EQUAL, 12 | LESS, LESS_EQUAL, 13 | PLUS_PLUS, MINUS_MINUS, 14 | //Exponent 15 | STAR_STAR, 16 | //Special assignments 17 | PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, SLASH_EQUAL, STAR_STAR_EQUAL, 18 | 19 | //Conditional 20 | QUESTION_MARK, COLON, 21 | 22 | // Literals. 23 | IDENTIFIER, STRING, NUMBER, 24 | 25 | // Keywords. 26 | AND, CLASS, ELSE, FALSE, FUN, FOR, IF, NONE, OR, 27 | PRINT, RETURN, SUPER, THIS, TRUE, VAR, DO, WHILE, BREAK, CONTINUE, 28 | 29 | EOF 30 | } -------------------------------------------------------------------------------- /src/com/company/tool/GenerateAst.java: -------------------------------------------------------------------------------- 1 | package com.company.tool; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | public class GenerateAst { 9 | public static void main(String[] args) throws IOException { 10 | if (args.length != 1) { 11 | System.err.println("Usage: generate_ast "); 12 | System.exit(1); 13 | } 14 | String outputDir = args[0]; 15 | defineAst(outputDir, "Expr", Arrays.asList( 16 | "Assign : Token name, Expr value, Token equals", 17 | "Binary : Expr left, Token operator, Expr right", 18 | "Function : List parameters, List body", 19 | "Call : Expr callee, Token paren, List arguments", 20 | "Super : Token keyword, Token method", 21 | "This : Token keyword", 22 | "Get : Expr object, Token name", 23 | "Set : Expr object, Token name, Expr value", 24 | "Grouping : Expr expression", 25 | "Literal : Object value", 26 | "Logical : Expr left, Token operator, Expr right", 27 | "Unary : Token operator, Expr right, Boolean postfix", 28 | "Ternary : Expr expr, Expr thenBranch, Expr elseBranch", 29 | "Variable : Token name" 30 | )); 31 | defineAst(outputDir, "Stmt", Arrays.asList( 32 | "Block : List statements", 33 | "Class : Token name, Expr superclass, List methods, List classMethods", 34 | "Expression : Expr expression", 35 | "Function : Token name, Expr.Function function", 36 | "If : Expr condition, Stmt thenBranch, Stmt elseBranch", 37 | "Print : Expr expression", 38 | "Return : Token keyword, Expr value", 39 | "Var : Token name, Expr initializer", 40 | "While : Expr condition, Stmt body", 41 | "Break : ", 42 | "Continue : " 43 | )); 44 | } 45 | private static void defineAst( 46 | String outputDir, String baseName, List types) 47 | throws IOException { 48 | String path = outputDir + "/" + baseName + ".java"; 49 | PrintWriter writer = new PrintWriter(path, "UTF-8"); 50 | 51 | writer.println("package com.company.fail;"); 52 | writer.println(""); 53 | writer.println("import java.util.List;"); 54 | writer.println(""); 55 | writer.println("abstract class " + baseName + " {"); 56 | 57 | defineVisitor(writer, baseName, types); 58 | 59 | // The AST classes. 60 | for (String type : types) { 61 | String className = type.split(":")[0].trim(); 62 | String fields = type.split(":")[1].trim(); 63 | defineType(writer, baseName, className, fields); 64 | } 65 | 66 | // The base accept() method. 67 | writer.println(""); 68 | writer.println(" abstract R accept(Visitor visitor);"); 69 | 70 | writer.println("}"); 71 | writer.close(); 72 | } 73 | 74 | private static void defineType( 75 | PrintWriter writer, String baseName, 76 | String className, String fieldList) { 77 | writer.println(""); 78 | writer.println(" static class " + className + " extends " + 79 | baseName + " {"); 80 | 81 | // Constructor. 82 | writer.println(" " + className + "(" + fieldList + ") {"); 83 | 84 | // Store parameters in fields. 85 | 86 | String[] fields = fieldList.split(", "); 87 | if (fieldList.isEmpty()) fields = new String[0]; 88 | for (String field : fields) { 89 | String name = field.split(" ")[1]; 90 | writer.println(" this." + name + " = " + name + ";"); 91 | } 92 | 93 | writer.println(" }"); 94 | 95 | // Visitor pattern. 96 | writer.println(); 97 | writer.println(" R accept(Visitor visitor) {"); 98 | writer.println(" return visitor.visit" + 99 | className + baseName + "(this);"); 100 | writer.println(" }"); 101 | 102 | // Fields. 103 | writer.println(); 104 | for (String field : fields) { 105 | writer.println(" final " + field + ";"); 106 | } 107 | 108 | writer.println(" }"); 109 | } 110 | 111 | private static void defineVisitor( 112 | PrintWriter writer, String baseName, List types) { 113 | writer.println(" interface Visitor {"); 114 | 115 | for (String type : types) { 116 | String typeName = type.split(":")[0].trim(); 117 | writer.println(" R visit" + typeName + baseName + "(" + 118 | typeName + " " + baseName.toLowerCase() + ");"); 119 | } 120 | 121 | writer.println(" }"); 122 | } 123 | } -------------------------------------------------------------------------------- /test/assignment/associativity.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/assignment/global.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/assignment/grouping.fail: -------------------------------------------------------------------------------- 1 | var a = "a"; 2 | (a) = "value"; // Error at '=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /test/assignment/infix_operator.fail: -------------------------------------------------------------------------------- 1 | var a = "a"; 2 | var b = "b"; 3 | a + b = "value"; // Error at '=': Invalid assignment target. 4 | -------------------------------------------------------------------------------- /test/assignment/local.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/assignment/prefix_operator.fail: -------------------------------------------------------------------------------- 1 | var a = "a"; 2 | !a = "value"; // Error at '=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /test/assignment/shorthand.fail: -------------------------------------------------------------------------------- 1 | var x = 1; 2 | print x += 2; // expect: 3 3 | x = 3; 4 | print x /= 3; // expect: 1 5 | x = 1; 6 | print x -= (-2); // expect: 3 7 | x = 3; 8 | print x *= 2; // expect: 6 9 | x = 3; 10 | print x **= 2; // expect: 9 -------------------------------------------------------------------------------- /test/assignment/syntax.fail: -------------------------------------------------------------------------------- 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 | 7 | var d = 1; 8 | print d = 2; // expect: 2 -------------------------------------------------------------------------------- /test/assignment/to_this.fail: -------------------------------------------------------------------------------- 1 | class Foo { 2 | Foo() { 3 | this = "value"; // Error at '=': Invalid assignment target. 4 | } 5 | } 6 | 7 | Foo(); 8 | -------------------------------------------------------------------------------- /test/assignment/undefined.fail: -------------------------------------------------------------------------------- 1 | unknown = "what"; // expect runtime error: Undefined variable 'unknown'. 2 | -------------------------------------------------------------------------------- /test/benchmark/binary_trees.fail: -------------------------------------------------------------------------------- 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 = 12; 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 | -------------------------------------------------------------------------------- /test/benchmark/equality.fail: -------------------------------------------------------------------------------- 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 "elapsed"; 32 | print elapsed; 33 | print "equals"; 34 | print elapsed - loopTime; 35 | -------------------------------------------------------------------------------- /test/benchmark/fib.fail: -------------------------------------------------------------------------------- 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(30) == 832040; 8 | print clock() - start; 9 | -------------------------------------------------------------------------------- /test/benchmark/invocation.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/benchmark/method_call.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/benchmark/properties.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/benchmark/string_equality.fail: -------------------------------------------------------------------------------- 1 | var a1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1"; 2 | var a2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2"; 3 | var a3 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3"; 4 | var a4 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4"; 5 | var a5 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa5"; 6 | var a6 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa6"; 7 | var a7 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa7"; 8 | var a8 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa8"; 9 | 10 | var i = 0; 11 | 12 | var loopStart = clock(); 13 | 14 | while (i < 100000) { 15 | i = i + 1; 16 | 17 | a1; a1; a1; a2; a1; a3; a1; a4; a1; a5; a1; a6; a1; a7; a1; a8; 18 | a2; a1; a2; a2; a2; a3; a2; a4; a2; a5; a2; a6; a2; a7; a2; a8; 19 | a3; a1; a3; a2; a3; a3; a3; a4; a3; a5; a3; a6; a3; a7; a3; a8; 20 | a4; a1; a4; a2; a4; a3; a4; a4; a4; a5; a4; a6; a4; a7; a4; a8; 21 | a5; a1; a5; a2; a5; a3; a5; a4; a5; a5; a5; a6; a5; a7; a5; a8; 22 | a6; a1; a6; a2; a6; a3; a6; a4; a6; a5; a6; a6; a6; a7; a6; a8; 23 | a7; a1; a7; a2; a7; a3; a7; a4; a7; a5; a7; a6; a7; a7; a7; a8; 24 | a8; a1; a8; a2; a8; a3; a8; a4; a8; a5; a8; a6; a8; a7; a8; a8; 25 | 26 | a1; a1; a1; a2; a1; a3; a1; a4; a1; a5; a1; a6; a1; a7; a1; a8; 27 | a2; a1; a2; a2; a2; a3; a2; a4; a2; a5; a2; a6; a2; a7; a2; a8; 28 | a3; a1; a3; a2; a3; a3; a3; a4; a3; a5; a3; a6; a3; a7; a3; a8; 29 | a4; a1; a4; a2; a4; a3; a4; a4; a4; a5; a4; a6; a4; a7; a4; a8; 30 | a5; a1; a5; a2; a5; a3; a5; a4; a5; a5; a5; a6; a5; a7; a5; a8; 31 | a6; a1; a6; a2; a6; a3; a6; a4; a6; a5; a6; a6; a6; a7; a6; a8; 32 | a7; a1; a7; a2; a7; a3; a7; a4; a7; a5; a7; a6; a7; a7; a7; a8; 33 | a8; a1; a8; a2; a8; a3; a8; a4; a8; a5; a8; a6; a8; a7; a8; a8; 34 | 35 | a1; a1; a1; a2; a1; a3; a1; a4; a1; a5; a1; a6; a1; a7; a1; a8; 36 | a2; a1; a2; a2; a2; a3; a2; a4; a2; a5; a2; a6; a2; a7; a2; a8; 37 | a3; a1; a3; a2; a3; a3; a3; a4; a3; a5; a3; a6; a3; a7; a3; a8; 38 | a4; a1; a4; a2; a4; a3; a4; a4; a4; a5; a4; a6; a4; a7; a4; a8; 39 | a5; a1; a5; a2; a5; a3; a5; a4; a5; a5; a5; a6; a5; a7; a5; a8; 40 | a6; a1; a6; a2; a6; a3; a6; a4; a6; a5; a6; a6; a6; a7; a6; a8; 41 | a7; a1; a7; a2; a7; a3; a7; a4; a7; a5; a7; a6; a7; a7; a7; a8; 42 | a8; a1; a8; a2; a8; a3; a8; a4; a8; a5; a8; a6; a8; a7; a8; a8; 43 | 44 | a1; a1; a1; a2; a1; a3; a1; a4; a1; a5; a1; a6; a1; a7; a1; a8; 45 | a2; a1; a2; a2; a2; a3; a2; a4; a2; a5; a2; a6; a2; a7; a2; a8; 46 | a3; a1; a3; a2; a3; a3; a3; a4; a3; a5; a3; a6; a3; a7; a3; a8; 47 | a4; a1; a4; a2; a4; a3; a4; a4; a4; a5; a4; a6; a4; a7; a4; a8; 48 | a5; a1; a5; a2; a5; a3; a5; a4; a5; a5; a5; a6; a5; a7; a5; a8; 49 | a6; a1; a6; a2; a6; a3; a6; a4; a6; a5; a6; a6; a6; a7; a6; a8; 50 | a7; a1; a7; a2; a7; a3; a7; a4; a7; a5; a7; a6; a7; a7; a7; a8; 51 | a8; a1; a8; a2; a8; a3; a8; a4; a8; a5; a8; a6; a8; a7; a8; a8; 52 | 53 | a1; a1; a1; a2; a1; a3; a1; a4; a1; a5; a1; a6; a1; a7; a1; a8; 54 | a2; a1; a2; a2; a2; a3; a2; a4; a2; a5; a2; a6; a2; a7; a2; a8; 55 | a3; a1; a3; a2; a3; a3; a3; a4; a3; a5; a3; a6; a3; a7; a3; a8; 56 | a4; a1; a4; a2; a4; a3; a4; a4; a4; a5; a4; a6; a4; a7; a4; a8; 57 | a5; a1; a5; a2; a5; a3; a5; a4; a5; a5; a5; a6; a5; a7; a5; a8; 58 | a6; a1; a6; a2; a6; a3; a6; a4; a6; a5; a6; a6; a6; a7; a6; a8; 59 | a7; a1; a7; a2; a7; a3; a7; a4; a7; a5; a7; a6; a7; a7; a7; a8; 60 | a8; a1; a8; a2; a8; a3; a8; a4; a8; a5; a8; a6; a8; a7; a8; a8; 61 | 62 | a1; a1; a1; a2; a1; a3; a1; a4; a1; a5; a1; a6; a1; a7; a1; a8; 63 | a2; a1; a2; a2; a2; a3; a2; a4; a2; a5; a2; a6; a2; a7; a2; a8; 64 | a3; a1; a3; a2; a3; a3; a3; a4; a3; a5; a3; a6; a3; a7; a3; a8; 65 | a4; a1; a4; a2; a4; a3; a4; a4; a4; a5; a4; a6; a4; a7; a4; a8; 66 | a5; a1; a5; a2; a5; a3; a5; a4; a5; a5; a5; a6; a5; a7; a5; a8; 67 | a6; a1; a6; a2; a6; a3; a6; a4; a6; a5; a6; a6; a6; a7; a6; a8; 68 | a7; a1; a7; a2; a7; a3; a7; a4; a7; a5; a7; a6; a7; a7; a7; a8; 69 | a8; a1; a8; a2; a8; a3; a8; a4; a8; a5; a8; a6; a8; a7; a8; a8; 70 | 71 | a1; a1; a1; a2; a1; a3; a1; a4; a1; a5; a1; a6; a1; a7; a1; a8; 72 | a2; a1; a2; a2; a2; a3; a2; a4; a2; a5; a2; a6; a2; a7; a2; a8; 73 | a3; a1; a3; a2; a3; a3; a3; a4; a3; a5; a3; a6; a3; a7; a3; a8; 74 | a4; a1; a4; a2; a4; a3; a4; a4; a4; a5; a4; a6; a4; a7; a4; a8; 75 | a5; a1; a5; a2; a5; a3; a5; a4; a5; a5; a5; a6; a5; a7; a5; a8; 76 | a6; a1; a6; a2; a6; a3; a6; a4; a6; a5; a6; a6; a6; a7; a6; a8; 77 | a7; a1; a7; a2; a7; a3; a7; a4; a7; a5; a7; a6; a7; a7; a7; a8; 78 | a8; a1; a8; a2; a8; a3; a8; a4; a8; a5; a8; a6; a8; a7; a8; a8; 79 | 80 | a1; a1; a1; a2; a1; a3; a1; a4; a1; a5; a1; a6; a1; a7; a1; a8; 81 | a2; a1; a2; a2; a2; a3; a2; a4; a2; a5; a2; a6; a2; a7; a2; a8; 82 | a3; a1; a3; a2; a3; a3; a3; a4; a3; a5; a3; a6; a3; a7; a3; a8; 83 | a4; a1; a4; a2; a4; a3; a4; a4; a4; a5; a4; a6; a4; a7; a4; a8; 84 | a5; a1; a5; a2; a5; a3; a5; a4; a5; a5; a5; a6; a5; a7; a5; a8; 85 | a6; a1; a6; a2; a6; a3; a6; a4; a6; a5; a6; a6; a6; a7; a6; a8; 86 | a7; a1; a7; a2; a7; a3; a7; a4; a7; a5; a7; a6; a7; a7; a7; a8; 87 | a8; a1; a8; a2; a8; a3; a8; a4; a8; a5; a8; a6; a8; a7; a8; a8; 88 | 89 | a1; a1; a1; a2; a1; a3; a1; a4; a1; a5; a1; a6; a1; a7; a1; a8; 90 | a2; a1; a2; a2; a2; a3; a2; a4; a2; a5; a2; a6; a2; a7; a2; a8; 91 | a3; a1; a3; a2; a3; a3; a3; a4; a3; a5; a3; a6; a3; a7; a3; a8; 92 | a4; a1; a4; a2; a4; a3; a4; a4; a4; a5; a4; a6; a4; a7; a4; a8; 93 | a5; a1; a5; a2; a5; a3; a5; a4; a5; a5; a5; a6; a5; a7; a5; a8; 94 | a6; a1; a6; a2; a6; a3; a6; a4; a6; a5; a6; a6; a6; a7; a6; a8; 95 | a7; a1; a7; a2; a7; a3; a7; a4; a7; a5; a7; a6; a7; a7; a7; a8; 96 | a8; a1; a8; a2; a8; a3; a8; a4; a8; a5; a8; a6; a8; a7; a8; a8; 97 | 98 | a1; a1; a1; a2; a1; a3; a1; a4; a1; a5; a1; a6; a1; a7; a1; a8; 99 | a2; a1; a2; a2; a2; a3; a2; a4; a2; a5; a2; a6; a2; a7; a2; a8; 100 | a3; a1; a3; a2; a3; a3; a3; a4; a3; a5; a3; a6; a3; a7; a3; a8; 101 | a4; a1; a4; a2; a4; a3; a4; a4; a4; a5; a4; a6; a4; a7; a4; a8; 102 | a5; a1; a5; a2; a5; a3; a5; a4; a5; a5; a5; a6; a5; a7; a5; a8; 103 | a6; a1; a6; a2; a6; a3; a6; a4; a6; a5; a6; a6; a6; a7; a6; a8; 104 | a7; a1; a7; a2; a7; a3; a7; a4; a7; a5; a7; a6; a7; a7; a7; a8; 105 | a8; a1; a8; a2; a8; a3; a8; a4; a8; a5; a8; a6; a8; a7; a8; a8; 106 | } 107 | 108 | var loopTime = clock() - loopStart; 109 | 110 | var start = clock(); 111 | 112 | i = 0; 113 | while (i < 100000) { 114 | i = i + 1; 115 | 116 | // 1 == 1; 1 == 2; 1 == nil; 1 == "str"; 1 == true; 117 | // nil == nil; nil == 1; nil == "str"; nil == true; 118 | // true == true; true == 1; true == false; true == "str"; true == nil; 119 | // "str" == "str"; "str" == "stru"; "str" == 1; "str" == nil; "str" == true; 120 | 121 | a1 == a1; a1 == a2; a1 == a3; a1 == a4; a1 == a5; a1 == a6; a1 == a7; a1 == a8; 122 | a2 == a1; a2 == a2; a2 == a3; a2 == a4; a2 == a5; a2 == a6; a2 == a7; a2 == a8; 123 | a3 == a1; a3 == a2; a3 == a3; a3 == a4; a3 == a5; a3 == a6; a3 == a7; a3 == a8; 124 | a4 == a1; a4 == a2; a4 == a3; a4 == a4; a4 == a5; a4 == a6; a4 == a7; a4 == a8; 125 | a5 == a1; a5 == a2; a5 == a3; a5 == a4; a5 == a5; a5 == a6; a5 == a7; a5 == a8; 126 | a6 == a1; a6 == a2; a6 == a3; a6 == a4; a6 == a5; a6 == a6; a6 == a7; a6 == a8; 127 | a7 == a1; a7 == a2; a7 == a3; a7 == a4; a7 == a5; a7 == a6; a7 == a7; a7 == a8; 128 | a8 == a1; a8 == a2; a8 == a3; a8 == a4; a8 == a5; a8 == a6; a8 == a7; a8 == a8; 129 | 130 | a1 == a1; a1 == a2; a1 == a3; a1 == a4; a1 == a5; a1 == a6; a1 == a7; a1 == a8; 131 | a2 == a1; a2 == a2; a2 == a3; a2 == a4; a2 == a5; a2 == a6; a2 == a7; a2 == a8; 132 | a3 == a1; a3 == a2; a3 == a3; a3 == a4; a3 == a5; a3 == a6; a3 == a7; a3 == a8; 133 | a4 == a1; a4 == a2; a4 == a3; a4 == a4; a4 == a5; a4 == a6; a4 == a7; a4 == a8; 134 | a5 == a1; a5 == a2; a5 == a3; a5 == a4; a5 == a5; a5 == a6; a5 == a7; a5 == a8; 135 | a6 == a1; a6 == a2; a6 == a3; a6 == a4; a6 == a5; a6 == a6; a6 == a7; a6 == a8; 136 | a7 == a1; a7 == a2; a7 == a3; a7 == a4; a7 == a5; a7 == a6; a7 == a7; a7 == a8; 137 | a8 == a1; a8 == a2; a8 == a3; a8 == a4; a8 == a5; a8 == a6; a8 == a7; a8 == a8; 138 | 139 | a1 == a1; a1 == a2; a1 == a3; a1 == a4; a1 == a5; a1 == a6; a1 == a7; a1 == a8; 140 | a2 == a1; a2 == a2; a2 == a3; a2 == a4; a2 == a5; a2 == a6; a2 == a7; a2 == a8; 141 | a3 == a1; a3 == a2; a3 == a3; a3 == a4; a3 == a5; a3 == a6; a3 == a7; a3 == a8; 142 | a4 == a1; a4 == a2; a4 == a3; a4 == a4; a4 == a5; a4 == a6; a4 == a7; a4 == a8; 143 | a5 == a1; a5 == a2; a5 == a3; a5 == a4; a5 == a5; a5 == a6; a5 == a7; a5 == a8; 144 | a6 == a1; a6 == a2; a6 == a3; a6 == a4; a6 == a5; a6 == a6; a6 == a7; a6 == a8; 145 | a7 == a1; a7 == a2; a7 == a3; a7 == a4; a7 == a5; a7 == a6; a7 == a7; a7 == a8; 146 | a8 == a1; a8 == a2; a8 == a3; a8 == a4; a8 == a5; a8 == a6; a8 == a7; a8 == a8; 147 | 148 | a1 == a1; a1 == a2; a1 == a3; a1 == a4; a1 == a5; a1 == a6; a1 == a7; a1 == a8; 149 | a2 == a1; a2 == a2; a2 == a3; a2 == a4; a2 == a5; a2 == a6; a2 == a7; a2 == a8; 150 | a3 == a1; a3 == a2; a3 == a3; a3 == a4; a3 == a5; a3 == a6; a3 == a7; a3 == a8; 151 | a4 == a1; a4 == a2; a4 == a3; a4 == a4; a4 == a5; a4 == a6; a4 == a7; a4 == a8; 152 | a5 == a1; a5 == a2; a5 == a3; a5 == a4; a5 == a5; a5 == a6; a5 == a7; a5 == a8; 153 | a6 == a1; a6 == a2; a6 == a3; a6 == a4; a6 == a5; a6 == a6; a6 == a7; a6 == a8; 154 | a7 == a1; a7 == a2; a7 == a3; a7 == a4; a7 == a5; a7 == a6; a7 == a7; a7 == a8; 155 | a8 == a1; a8 == a2; a8 == a3; a8 == a4; a8 == a5; a8 == a6; a8 == a7; a8 == a8; 156 | 157 | a1 == a1; a1 == a2; a1 == a3; a1 == a4; a1 == a5; a1 == a6; a1 == a7; a1 == a8; 158 | a2 == a1; a2 == a2; a2 == a3; a2 == a4; a2 == a5; a2 == a6; a2 == a7; a2 == a8; 159 | a3 == a1; a3 == a2; a3 == a3; a3 == a4; a3 == a5; a3 == a6; a3 == a7; a3 == a8; 160 | a4 == a1; a4 == a2; a4 == a3; a4 == a4; a4 == a5; a4 == a6; a4 == a7; a4 == a8; 161 | a5 == a1; a5 == a2; a5 == a3; a5 == a4; a5 == a5; a5 == a6; a5 == a7; a5 == a8; 162 | a6 == a1; a6 == a2; a6 == a3; a6 == a4; a6 == a5; a6 == a6; a6 == a7; a6 == a8; 163 | a7 == a1; a7 == a2; a7 == a3; a7 == a4; a7 == a5; a7 == a6; a7 == a7; a7 == a8; 164 | a8 == a1; a8 == a2; a8 == a3; a8 == a4; a8 == a5; a8 == a6; a8 == a7; a8 == a8; 165 | 166 | a1 == a1; a1 == a2; a1 == a3; a1 == a4; a1 == a5; a1 == a6; a1 == a7; a1 == a8; 167 | a2 == a1; a2 == a2; a2 == a3; a2 == a4; a2 == a5; a2 == a6; a2 == a7; a2 == a8; 168 | a3 == a1; a3 == a2; a3 == a3; a3 == a4; a3 == a5; a3 == a6; a3 == a7; a3 == a8; 169 | a4 == a1; a4 == a2; a4 == a3; a4 == a4; a4 == a5; a4 == a6; a4 == a7; a4 == a8; 170 | a5 == a1; a5 == a2; a5 == a3; a5 == a4; a5 == a5; a5 == a6; a5 == a7; a5 == a8; 171 | a6 == a1; a6 == a2; a6 == a3; a6 == a4; a6 == a5; a6 == a6; a6 == a7; a6 == a8; 172 | a7 == a1; a7 == a2; a7 == a3; a7 == a4; a7 == a5; a7 == a6; a7 == a7; a7 == a8; 173 | a8 == a1; a8 == a2; a8 == a3; a8 == a4; a8 == a5; a8 == a6; a8 == a7; a8 == a8; 174 | 175 | a1 == a1; a1 == a2; a1 == a3; a1 == a4; a1 == a5; a1 == a6; a1 == a7; a1 == a8; 176 | a2 == a1; a2 == a2; a2 == a3; a2 == a4; a2 == a5; a2 == a6; a2 == a7; a2 == a8; 177 | a3 == a1; a3 == a2; a3 == a3; a3 == a4; a3 == a5; a3 == a6; a3 == a7; a3 == a8; 178 | a4 == a1; a4 == a2; a4 == a3; a4 == a4; a4 == a5; a4 == a6; a4 == a7; a4 == a8; 179 | a5 == a1; a5 == a2; a5 == a3; a5 == a4; a5 == a5; a5 == a6; a5 == a7; a5 == a8; 180 | a6 == a1; a6 == a2; a6 == a3; a6 == a4; a6 == a5; a6 == a6; a6 == a7; a6 == a8; 181 | a7 == a1; a7 == a2; a7 == a3; a7 == a4; a7 == a5; a7 == a6; a7 == a7; a7 == a8; 182 | a8 == a1; a8 == a2; a8 == a3; a8 == a4; a8 == a5; a8 == a6; a8 == a7; a8 == a8; 183 | 184 | a1 == a1; a1 == a2; a1 == a3; a1 == a4; a1 == a5; a1 == a6; a1 == a7; a1 == a8; 185 | a2 == a1; a2 == a2; a2 == a3; a2 == a4; a2 == a5; a2 == a6; a2 == a7; a2 == a8; 186 | a3 == a1; a3 == a2; a3 == a3; a3 == a4; a3 == a5; a3 == a6; a3 == a7; a3 == a8; 187 | a4 == a1; a4 == a2; a4 == a3; a4 == a4; a4 == a5; a4 == a6; a4 == a7; a4 == a8; 188 | a5 == a1; a5 == a2; a5 == a3; a5 == a4; a5 == a5; a5 == a6; a5 == a7; a5 == a8; 189 | a6 == a1; a6 == a2; a6 == a3; a6 == a4; a6 == a5; a6 == a6; a6 == a7; a6 == a8; 190 | a7 == a1; a7 == a2; a7 == a3; a7 == a4; a7 == a5; a7 == a6; a7 == a7; a7 == a8; 191 | a8 == a1; a8 == a2; a8 == a3; a8 == a4; a8 == a5; a8 == a6; a8 == a7; a8 == a8; 192 | 193 | a1 == a1; a1 == a2; a1 == a3; a1 == a4; a1 == a5; a1 == a6; a1 == a7; a1 == a8; 194 | a2 == a1; a2 == a2; a2 == a3; a2 == a4; a2 == a5; a2 == a6; a2 == a7; a2 == a8; 195 | a3 == a1; a3 == a2; a3 == a3; a3 == a4; a3 == a5; a3 == a6; a3 == a7; a3 == a8; 196 | a4 == a1; a4 == a2; a4 == a3; a4 == a4; a4 == a5; a4 == a6; a4 == a7; a4 == a8; 197 | a5 == a1; a5 == a2; a5 == a3; a5 == a4; a5 == a5; a5 == a6; a5 == a7; a5 == a8; 198 | a6 == a1; a6 == a2; a6 == a3; a6 == a4; a6 == a5; a6 == a6; a6 == a7; a6 == a8; 199 | a7 == a1; a7 == a2; a7 == a3; a7 == a4; a7 == a5; a7 == a6; a7 == a7; a7 == a8; 200 | a8 == a1; a8 == a2; a8 == a3; a8 == a4; a8 == a5; a8 == a6; a8 == a7; a8 == a8; 201 | 202 | a1 == a1; a1 == a2; a1 == a3; a1 == a4; a1 == a5; a1 == a6; a1 == a7; a1 == a8; 203 | a2 == a1; a2 == a2; a2 == a3; a2 == a4; a2 == a5; a2 == a6; a2 == a7; a2 == a8; 204 | a3 == a1; a3 == a2; a3 == a3; a3 == a4; a3 == a5; a3 == a6; a3 == a7; a3 == a8; 205 | a4 == a1; a4 == a2; a4 == a3; a4 == a4; a4 == a5; a4 == a6; a4 == a7; a4 == a8; 206 | a5 == a1; a5 == a2; a5 == a3; a5 == a4; a5 == a5; a5 == a6; a5 == a7; a5 == a8; 207 | a6 == a1; a6 == a2; a6 == a3; a6 == a4; a6 == a5; a6 == a6; a6 == a7; a6 == a8; 208 | a7 == a1; a7 == a2; a7 == a3; a7 == a4; a7 == a5; a7 == a6; a7 == a7; a7 == a8; 209 | a8 == a1; a8 == a2; a8 == a3; a8 == a4; a8 == a5; a8 == a6; a8 == a7; a8 == a8; 210 | 211 | } 212 | 213 | var elapsed = clock() - start; 214 | print "loop"; 215 | print loopTime; 216 | print "elapsed"; 217 | print elapsed; 218 | print "equals"; 219 | print elapsed - loopTime; 220 | -------------------------------------------------------------------------------- /test/block/empty.fail: -------------------------------------------------------------------------------- 1 | {} // By itself. 2 | 3 | // In a statement. 4 | if (true) {} 5 | if (false) {} else {} 6 | 7 | print "ok"; // expect: ok 8 | -------------------------------------------------------------------------------- /test/block/scope.fail: -------------------------------------------------------------------------------- 1 | var a = "outer"; 2 | 3 | { 4 | var a = "inner"; 5 | print a; // expect: inner 6 | } 7 | 8 | print a; // expect: outer 9 | -------------------------------------------------------------------------------- /test/block/scope_complex.fail: -------------------------------------------------------------------------------- 1 | var a = "global a"; 2 | var b = "global b"; 3 | var c = "global c"; 4 | { 5 | var a = "outer a"; 6 | var b = "outer b"; 7 | { 8 | var a = "inner a"; 9 | print a; // expect: inner a 10 | print b; // expect: outer b 11 | print c; // expect: global c 12 | } 13 | print a; // expect: outer a 14 | print b; // expect: outer b 15 | print c; // expect: global c 16 | } 17 | print a; // expect: global a 18 | print b; // expect: global b 19 | print c; // expect: global c -------------------------------------------------------------------------------- /test/bool/equality.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/bool/not.fail: -------------------------------------------------------------------------------- 1 | print !true; // expect: false 2 | print !false; // expect: true 3 | print !!true; // expect: true 4 | -------------------------------------------------------------------------------- /test/call/bool.fail: -------------------------------------------------------------------------------- 1 | true(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /test/call/none.fail: -------------------------------------------------------------------------------- 1 | none(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /test/call/num.fail: -------------------------------------------------------------------------------- 1 | 123(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /test/call/object.fail: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | foo(); // expect runtime error: Can only call functions and classes. 5 | -------------------------------------------------------------------------------- /test/call/string.fail: -------------------------------------------------------------------------------- 1 | "str"(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /test/class/empty.fail: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | print Foo; // expect: Foo 4 | -------------------------------------------------------------------------------- /test/class/getter.fail: -------------------------------------------------------------------------------- 1 | class Circle { 2 | init(radius) { 3 | this.radius = radius; 4 | } 5 | 6 | area { 7 | return 3.14 * this.radius * this.radius; 8 | } 9 | } 10 | 11 | var circle = Circle(4); 12 | print circle.area; // expect: 50.24 -------------------------------------------------------------------------------- /test/class/inherited_method.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/class/local_reference_self.fail: -------------------------------------------------------------------------------- 1 | class Foo { 2 | returnSelf() { 3 | return Foo; 4 | } 5 | } 6 | 7 | print Foo().returnSelf(); // expect: Foo 8 | -------------------------------------------------------------------------------- /test/class/reference_self.fail: -------------------------------------------------------------------------------- 1 | { 2 | class Foo { 3 | returnSelf() { 4 | return Foo; 5 | } 6 | } 7 | 8 | print Foo().returnSelf(); // expect: Foo 9 | } 10 | -------------------------------------------------------------------------------- /test/class/staticmethod.fail: -------------------------------------------------------------------------------- 1 | class Math { 2 | class square(n) { 3 | return n * n; 4 | } 5 | } 6 | 7 | print Math.square(3); // expect: 9 -------------------------------------------------------------------------------- /test/closure/assign_to_closure.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/closure/assign_to_shadowed_later.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/closure/close_over_function_parameter.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/closure/close_over_later_variable.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/closure/close_over_method_parameter.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/closure/closed_closure_in_function.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/closure/nested_closure.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/closure/open_closure_in_function.fail: -------------------------------------------------------------------------------- 1 | { 2 | var local = "local"; 3 | fun f() { 4 | print local; // expect: local 5 | } 6 | f(); 7 | } 8 | -------------------------------------------------------------------------------- /test/closure/reference_closure_multiple_times.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/closure/reuse_closure_slot.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/closure/shadow_closure_with_local.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/closure/unused_closure.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/closure/unused_later_closure.wren: -------------------------------------------------------------------------------- 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 | closure = Fn.new { a } 15 | if (false) Fn.new { b } 16 | } 17 | 18 | System.print(closure.call()) // expect: a 19 | } 20 | -------------------------------------------------------------------------------- /test/comments/line_at_eof.fail: -------------------------------------------------------------------------------- 1 | print "ok"; // expect: ok 2 | // comment -------------------------------------------------------------------------------- /test/comments/only_line_comment.fail: -------------------------------------------------------------------------------- 1 | // comment -------------------------------------------------------------------------------- /test/comments/only_line_comment_and_line.fail: -------------------------------------------------------------------------------- 1 | // comment 2 | -------------------------------------------------------------------------------- /test/comments/unicode.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/constructor/arguments.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/constructor/call_init_explicitly.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/constructor/default.fail: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | print foo; // expect: Foo instance 5 | -------------------------------------------------------------------------------- /test/constructor/default_arguments.fail: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(1, 2, 3); // expect runtime error: Expected 0 arguments but got 3. 4 | -------------------------------------------------------------------------------- /test/constructor/early_return.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/constructor/extra_arguments.fail: -------------------------------------------------------------------------------- 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. 9 | -------------------------------------------------------------------------------- /test/constructor/missing_arguments.fail: -------------------------------------------------------------------------------- 1 | 2 | class Foo { 3 | init(a, b) {} 4 | } 5 | 6 | var foo = Foo(1); // expect runtime error: Expected 2 arguments but got 1. -------------------------------------------------------------------------------- /test/constructor/return_in_nested_function.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/constructor/return_value.fail: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init() { 3 | return "result"; // Error at 'return': Cannot return a value from an initializer. 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/do-while/do-while.fail: -------------------------------------------------------------------------------- 1 | do { 2 | print "hello"; 3 | } while (false); 4 | // expect: hello 5 | 6 | var i = 0; 7 | do { 8 | print "hello"; 9 | i++; 10 | } while (i < 2); 11 | // expect: hello 12 | // expect: hello -------------------------------------------------------------------------------- /test/empty_file.fail: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsacer/FailLang/2bd68369c6af9a042a18e81f7e19e4cb39b2d0b3/test/empty_file.fail -------------------------------------------------------------------------------- /test/expressions/evaluate.fail: -------------------------------------------------------------------------------- 1 | // Note: This is just for the expression evaluating chapter which evaluates an 2 | // expression directly. 3 | (5 - (3 - 1)) + -1; 4 | // expect: 2 5 | -------------------------------------------------------------------------------- /test/expressions/parse.fail: -------------------------------------------------------------------------------- 1 | // Note: This is just for the expression parsing chapter which prints the AST. 2 | (5 - (3 - 1)) + -1 3 | // expect: (+ (group (- 5.0 (group (- 3.0 1.0)))) (- 1.0)) 4 | -------------------------------------------------------------------------------- /test/field/call_function_field.fail: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | fun bar() { 4 | print "bar"; 5 | } 6 | 7 | var foo = Foo(); 8 | foo.bar = bar; 9 | 10 | foo.bar(); // expect: bar 11 | -------------------------------------------------------------------------------- /test/field/call_nonfunction_field.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/field/get_on_bool.fail: -------------------------------------------------------------------------------- 1 | true.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /test/field/get_on_class.fail: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | Foo.bar; // expect runtime error: Undefined property 'bar'. 3 | -------------------------------------------------------------------------------- /test/field/get_on_function.fail: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | 3 | foo.bar; // expect runtime error: Only instances have properties. 4 | -------------------------------------------------------------------------------- /test/field/get_on_none.fail: -------------------------------------------------------------------------------- 1 | none.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /test/field/get_on_num.fail: -------------------------------------------------------------------------------- 1 | 123.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /test/field/get_on_string.fail: -------------------------------------------------------------------------------- 1 | "str".foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /test/field/many.fail: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | foo.bilberry = "bilberry"; 5 | foo.lime = "lime"; 6 | foo.elderberry = "elderberry"; 7 | foo.raspberry = "raspberry"; 8 | foo.gooseberry = "gooseberry"; 9 | foo.longan = "longan"; 10 | foo.mandarine = "mandarine"; 11 | foo.kiwifruit = "kiwifruit"; 12 | foo.orange = "orange"; 13 | foo.pomegranate = "pomegranate"; 14 | foo.tomato = "tomato"; 15 | foo.banana = "banana"; 16 | foo.juniper = "juniper"; 17 | foo.damson = "damson"; 18 | foo.blackcurrant = "blackcurrant"; 19 | foo.peach = "peach"; 20 | foo.grape = "grape"; 21 | foo.mango = "mango"; 22 | foo.redcurrant = "redcurrant"; 23 | foo.watermelon = "watermelon"; 24 | foo.plumcot = "plumcot"; 25 | foo.papaya = "papaya"; 26 | foo.cloudberry = "cloudberry"; 27 | foo.rambutan = "rambutan"; 28 | foo.salak = "salak"; 29 | foo.physalis = "physalis"; 30 | foo.huckleberry = "huckleberry"; 31 | foo.coconut = "coconut"; 32 | foo.date = "date"; 33 | foo.tamarind = "tamarind"; 34 | foo.lychee = "lychee"; 35 | foo.raisin = "raisin"; 36 | foo.apple = "apple"; 37 | foo.avocado = "avocado"; 38 | foo.nectarine = "nectarine"; 39 | foo.pomelo = "pomelo"; 40 | foo.melon = "melon"; 41 | foo.currant = "currant"; 42 | foo.plum = "plum"; 43 | foo.persimmon = "persimmon"; 44 | foo.olive = "olive"; 45 | foo.cranberry = "cranberry"; 46 | foo.boysenberry = "boysenberry"; 47 | foo.blackberry = "blackberry"; 48 | foo.passionfruit = "passionfruit"; 49 | foo.mulberry = "mulberry"; 50 | foo.marionberry = "marionberry"; 51 | foo.plantain = "plantain"; 52 | foo.lemon = "lemon"; 53 | foo.yuzu = "yuzu"; 54 | foo.loquat = "loquat"; 55 | foo.kumquat = "kumquat"; 56 | foo.salmonberry = "salmonberry"; 57 | foo.tangerine = "tangerine"; 58 | foo.durian = "durian"; 59 | foo.pear = "pear"; 60 | foo.cantaloupe = "cantaloupe"; 61 | foo.quince = "quince"; 62 | foo.guava = "guava"; 63 | foo.strawberry = "strawberry"; 64 | foo.nance = "nance"; 65 | foo.apricot = "apricot"; 66 | foo.jambul = "jambul"; 67 | foo.grapefruit = "grapefruit"; 68 | foo.clementine = "clementine"; 69 | foo.jujube = "jujube"; 70 | foo.cherry = "cherry"; 71 | foo.feijoa = "feijoa"; 72 | foo.jackfruit = "jackfruit"; 73 | foo.fig = "fig"; 74 | foo.cherimoya = "cherimoya"; 75 | foo.pineapple = "pineapple"; 76 | foo.blueberry = "blueberry"; 77 | foo.jabuticaba = "jabuticaba"; 78 | foo.miracle = "miracle"; 79 | foo.dragonfruit = "dragonfruit"; 80 | foo.satsuma = "satsuma"; 81 | foo.tamarillo = "tamarillo"; 82 | foo.honeydew = "honeydew"; 83 | 84 | print foo.apple; // expect: apple 85 | print foo.apricot; // expect: apricot 86 | print foo.avocado; // expect: avocado 87 | print foo.banana; // expect: banana 88 | print foo.bilberry; // expect: bilberry 89 | print foo.blackberry; // expect: blackberry 90 | print foo.blackcurrant; // expect: blackcurrant 91 | print foo.blueberry; // expect: blueberry 92 | print foo.boysenberry; // expect: boysenberry 93 | print foo.cantaloupe; // expect: cantaloupe 94 | print foo.cherimoya; // expect: cherimoya 95 | print foo.cherry; // expect: cherry 96 | print foo.clementine; // expect: clementine 97 | print foo.cloudberry; // expect: cloudberry 98 | print foo.coconut; // expect: coconut 99 | print foo.cranberry; // expect: cranberry 100 | print foo.currant; // expect: currant 101 | print foo.damson; // expect: damson 102 | print foo.date; // expect: date 103 | print foo.dragonfruit; // expect: dragonfruit 104 | print foo.durian; // expect: durian 105 | print foo.elderberry; // expect: elderberry 106 | print foo.feijoa; // expect: feijoa 107 | print foo.fig; // expect: fig 108 | print foo.gooseberry; // expect: gooseberry 109 | print foo.grape; // expect: grape 110 | print foo.grapefruit; // expect: grapefruit 111 | print foo.guava; // expect: guava 112 | print foo.honeydew; // expect: honeydew 113 | print foo.huckleberry; // expect: huckleberry 114 | print foo.jabuticaba; // expect: jabuticaba 115 | print foo.jackfruit; // expect: jackfruit 116 | print foo.jambul; // expect: jambul 117 | print foo.jujube; // expect: jujube 118 | print foo.juniper; // expect: juniper 119 | print foo.kiwifruit; // expect: kiwifruit 120 | print foo.kumquat; // expect: kumquat 121 | print foo.lemon; // expect: lemon 122 | print foo.lime; // expect: lime 123 | print foo.longan; // expect: longan 124 | print foo.loquat; // expect: loquat 125 | print foo.lychee; // expect: lychee 126 | print foo.mandarine; // expect: mandarine 127 | print foo.mango; // expect: mango 128 | print foo.marionberry; // expect: marionberry 129 | print foo.melon; // expect: melon 130 | print foo.miracle; // expect: miracle 131 | print foo.mulberry; // expect: mulberry 132 | print foo.nance; // expect: nance 133 | print foo.nectarine; // expect: nectarine 134 | print foo.olive; // expect: olive 135 | print foo.orange; // expect: orange 136 | print foo.papaya; // expect: papaya 137 | print foo.passionfruit; // expect: passionfruit 138 | print foo.peach; // expect: peach 139 | print foo.pear; // expect: pear 140 | print foo.persimmon; // expect: persimmon 141 | print foo.physalis; // expect: physalis 142 | print foo.pineapple; // expect: pineapple 143 | print foo.plantain; // expect: plantain 144 | print foo.plum; // expect: plum 145 | print foo.plumcot; // expect: plumcot 146 | print foo.pomegranate; // expect: pomegranate 147 | print foo.pomelo; // expect: pomelo 148 | print foo.quince; // expect: quince 149 | print foo.raisin; // expect: raisin 150 | print foo.rambutan; // expect: rambutan 151 | print foo.raspberry; // expect: raspberry 152 | print foo.redcurrant; // expect: redcurrant 153 | print foo.salak; // expect: salak 154 | print foo.salmonberry; // expect: salmonberry 155 | print foo.satsuma; // expect: satsuma 156 | print foo.strawberry; // expect: strawberry 157 | print foo.tamarillo; // expect: tamarillo 158 | print foo.tamarind; // expect: tamarind 159 | print foo.tangerine; // expect: tangerine 160 | print foo.tomato; // expect: tomato 161 | print foo.watermelon; // expect: watermelon 162 | print foo.yuzu; // expect: yuzu 163 | -------------------------------------------------------------------------------- /test/field/method.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/field/method_binds_this.fail: -------------------------------------------------------------------------------- 1 | class Foo { 2 | sayName() { 3 | print this.name; 4 | } 5 | } 6 | 7 | var foo1 = Foo(); 8 | foo1.name = "foo1"; 9 | 10 | var foo2 = Foo(); 11 | foo2.name = "foo2"; 12 | 13 | // Store the method reference on another object. 14 | foo2.fn = foo1.sayName; 15 | // Still retains original receiver. 16 | foo2.fn(); // expect: foo1 17 | -------------------------------------------------------------------------------- /test/field/on_instance.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/field/set_on_bool.fail: -------------------------------------------------------------------------------- 1 | true.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /test/field/set_on_class.fail: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | Foo.bar = "value"; 3 | print Foo.bar; // expect: value -------------------------------------------------------------------------------- /test/field/set_on_function.fail: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | 3 | foo.bar = "value"; // expect runtime error: Only instances have fields. 4 | -------------------------------------------------------------------------------- /test/field/set_on_none.fail: -------------------------------------------------------------------------------- 1 | none.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /test/field/set_on_num.fail: -------------------------------------------------------------------------------- 1 | 123.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /test/field/set_on_string.fail: -------------------------------------------------------------------------------- 1 | "str".foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /test/field/undefined.fail: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | var foo = Foo(); 3 | 4 | foo.bar; // expect runtime error: Undefined property 'bar'. 5 | -------------------------------------------------------------------------------- /test/for/class_in_body.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | for (;;) class Foo {} 3 | -------------------------------------------------------------------------------- /test/for/closure_in_body.fail: -------------------------------------------------------------------------------- 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() { print j; } 8 | 9 | if (j == 1) f1 = f; 10 | else if (j == 2) f2 = f; 11 | else f3 = f; 12 | } 13 | 14 | f1(); // expect: 1 15 | f2(); // expect: 2 16 | f3(); // expect: 3 17 | -------------------------------------------------------------------------------- /test/for/for_complex.fail: -------------------------------------------------------------------------------- 1 | // for loop complex (to 5 - zero and one based), skips 2 using if: 2 | for(var i = 0, j = 1; i < 5; i++, j++) { 3 | if (j != 2) 4 | print j; 5 | } 6 | // expect: 1 7 | // expect: 3 8 | // expect: 4 9 | // expect: 5 -------------------------------------------------------------------------------- /test/for/fun_in_body.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | for (;;) fun foo() {} 3 | -------------------------------------------------------------------------------- /test/for/return_closure.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/for/return_inside.fail: -------------------------------------------------------------------------------- 1 | fun f() { 2 | for (;;) { 3 | var i = "i"; 4 | return i; 5 | } 6 | } 7 | 8 | print f(); 9 | // expect: i 10 | -------------------------------------------------------------------------------- /test/for/scope.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/for/statement_condition.fail: -------------------------------------------------------------------------------- 1 | // [line 3] Error at '{': Expect expression. 2 | // [line 3] Error at ')': Expect ';' after expression. 3 | for (var a = 1; {}; a = a + 1) {} 4 | -------------------------------------------------------------------------------- /test/for/statement_increment.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at '{': Expect expression. 2 | for (var a = 1; a < 2; {}) {} 3 | -------------------------------------------------------------------------------- /test/for/statement_initializer.fail: -------------------------------------------------------------------------------- 1 | // [line 3] Error at '{': Expect expression. 2 | // [line 3] Error at ')': Expect ';' after expression. 3 | for ({}; a < 2; a = a + 1) {} 4 | -------------------------------------------------------------------------------- /test/for/syntax.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/for/var_in_body.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | for (;;) var foo; 3 | -------------------------------------------------------------------------------- /test/function/body_must_be_block.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/function/empty_body.fail: -------------------------------------------------------------------------------- 1 | fun f() {} 2 | print f(); // expect: none 3 | -------------------------------------------------------------------------------- /test/function/extra_arguments.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/function/local_mutual_recursion.fail: -------------------------------------------------------------------------------- 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 | return isEven(n - 1); 9 | } 10 | 11 | isEven(4); 12 | } -------------------------------------------------------------------------------- /test/function/local_recursion.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/function/missing_arguments.fail: -------------------------------------------------------------------------------- 1 | fun f(a, b) {} 2 | 3 | f(1); // expect runtime error: Expected 2 arguments but got 1. 4 | -------------------------------------------------------------------------------- /test/function/missing_comma_in_parameters.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/function/mutual_recursion.fail: -------------------------------------------------------------------------------- 1 | fun isEven(n) { 2 | if (n == 0) return true; 3 | return isOdd(n - 1); 4 | } 5 | 6 | fun isOdd(n) { 7 | return isEven(n - 1); 8 | } 9 | 10 | print isEven(4); // expect: true 11 | print isOdd(3); // expect: true 12 | -------------------------------------------------------------------------------- /test/function/parameters.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/function/print.fail: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | print foo; // expect: -------------------------------------------------------------------------------- /test/function/recursion.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/function/too_many_arguments.fail: -------------------------------------------------------------------------------- 1 | foo(1, 2, 3, 4, 5, 6, 7, 8, 9); // Error at '9': Cannot have more than 8 arguments. 2 | -------------------------------------------------------------------------------- /test/function/too_many_parameters.fail: -------------------------------------------------------------------------------- 1 | // 9 parameters. 2 | fun f(a, b, c, d, e, f, g, h, i) {} // Error at 'i': Cannot have more than 8 parameters. 3 | -------------------------------------------------------------------------------- /test/function/used_before_declared.fail: -------------------------------------------------------------------------------- 1 | x(1); // expect runtime error: Undefined variable 'x'. 2 | fun x (y) { 3 | print y; 4 | } -------------------------------------------------------------------------------- /test/global_functions/len.fail: -------------------------------------------------------------------------------- 1 | print len("hello"); // expect: 5 -------------------------------------------------------------------------------- /test/global_functions/str.fail: -------------------------------------------------------------------------------- 1 | print str(1) + str(2) + str(3); 2 | // expect: 123 -------------------------------------------------------------------------------- /test/if/class_in_else.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | if (true) "ok"; else class Foo {} 3 | -------------------------------------------------------------------------------- /test/if/class_in_then.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | if (true) class Foo {} 3 | -------------------------------------------------------------------------------- /test/if/condition_assignment.fail: -------------------------------------------------------------------------------- 1 | // Assignment in if condition. 2 | var a = false; 3 | if (a = true) print a; // Error at '=': Assignment is not allowed within if, loop or ternary condition. -------------------------------------------------------------------------------- /test/if/dangling_else.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/if/else.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/if/fun_in_else.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | if (true) "ok"; else fun foo() {} 3 | -------------------------------------------------------------------------------- /test/if/fun_in_then.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | if (true) fun foo() {} 3 | -------------------------------------------------------------------------------- /test/if/if.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/if/truth.fail: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | if (false) print "bad"; else print "false"; // expect: false 3 | if (none) print "bad"; else print "none"; // expect: none 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 | -------------------------------------------------------------------------------- /test/if/var_in_else.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | if (true) "ok"; else var foo; 3 | -------------------------------------------------------------------------------- /test/if/var_in_then.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | if (true) var foo; 3 | -------------------------------------------------------------------------------- /test/inheritance/inherit_from_function.fail: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | 3 | class Subclass < foo {} // expect runtime error: Superclass must be a class. 4 | -------------------------------------------------------------------------------- /test/inheritance/inherit_from_none.fail: -------------------------------------------------------------------------------- 1 | var None = none; 2 | class Foo < None {} // expect runtime error: Superclass must be a class. 3 | -------------------------------------------------------------------------------- /test/inheritance/inherit_from_number.fail: -------------------------------------------------------------------------------- 1 | var Number = 123; 2 | class Foo < Number {} // expect runtime error: Superclass must be a class. 3 | -------------------------------------------------------------------------------- /test/inheritance/inherit_methods.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/inheritance/parenthesized_superclass.fail: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | // [line 4] Error at '(': Expect superclass name. 4 | class Bar < (Foo) {} 5 | -------------------------------------------------------------------------------- /test/inheritance/set_fields_from_base_class.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/lambda/binding.fail: -------------------------------------------------------------------------------- 1 | var a = "outer"; 2 | { 3 | var foo = fun() { 4 | print a; 5 | }; 6 | 7 | foo(); // expect: outer 8 | var a = "inner"; 9 | foo(); // expect: outer 10 | } 11 | -------------------------------------------------------------------------------- /test/lambda/declaration.fail: -------------------------------------------------------------------------------- 1 | fun () {}; -------------------------------------------------------------------------------- /test/lambda/if.fail: -------------------------------------------------------------------------------- 1 | if(true) fun() {}; -------------------------------------------------------------------------------- /test/lambda/lambda.fail: -------------------------------------------------------------------------------- 1 | fun thrice(fn) { 2 | for (var i = 1; i <= 3; i = i + 1) { 3 | fn(i); 4 | } 5 | } 6 | 7 | thrice(fun (a) { 8 | print a; 9 | }); 10 | 11 | // expect: 1 12 | // expect: 2 13 | // expect: 3 -------------------------------------------------------------------------------- /test/lambda/nested.fail: -------------------------------------------------------------------------------- 1 | var g = 3; 2 | fun (y){ 3 | var r = 1; 4 | r++; 5 | var f = fun(y) { 6 | print y; 7 | print r; 8 | print g; 9 | }(y); 10 | }(1); 11 | 12 | // expect: 1 13 | // expect: 2 14 | // expect: 3 -------------------------------------------------------------------------------- /test/lambda/semicolon.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at end: Expect ';' after expression. 2 | fun () {} -------------------------------------------------------------------------------- /test/lambda/while.fail: -------------------------------------------------------------------------------- 1 | var i = 0; 2 | while(i++ < 3) { 3 | fun() { print i; }(); 4 | } 5 | 6 | // expect: 1 7 | // expect: 2 8 | // expect: 3 -------------------------------------------------------------------------------- /test/limit/reuse_constants.fail: -------------------------------------------------------------------------------- 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; 2; 3; 4; // ok 36 | } 37 | 38 | print "ok"; // expect: ok -------------------------------------------------------------------------------- /test/limit/stack_overflow.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/limit/too_many_constants.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/limit/too_many_locals.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/limit/too_many_upvalues.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/logical_operator/and.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/logical_operator/and_truth.fail: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | print false and "bad"; // expect: false 3 | print none and "bad"; // expect: none 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 | -------------------------------------------------------------------------------- /test/logical_operator/or.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/logical_operator/or_truth.fail: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | print false or "ok"; // expect: ok 3 | print none 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 | -------------------------------------------------------------------------------- /test/logical_operator/ternary.fail: -------------------------------------------------------------------------------- 1 | var arg = "T"; 2 | var vehicle = ( ( arg == "B" ) ? "bus" : 3 | ( arg == "A" ) ? "airplane" : 4 | ( arg == "T" ) ? "train" : 5 | ( arg == "C" ) ? "car" : 6 | ( arg == "H" ) ? "horse" : 7 | "feet" ); 8 | print vehicle; // expect: train 9 | 10 | var i = 0; 11 | var j = i == 0 ? ++i : ++i; 12 | print i; // expect: 1 -------------------------------------------------------------------------------- /test/method/arity.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/method/empty_block.fail: -------------------------------------------------------------------------------- 1 | class Foo { 2 | bar() {} 3 | } 4 | 5 | print Foo().bar(); // expect: none 6 | -------------------------------------------------------------------------------- /test/method/extra_arguments.fail: -------------------------------------------------------------------------------- 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. -------------------------------------------------------------------------------- /test/method/missing_arguments.fail: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method(a, b) {} 3 | } 4 | 5 | Foo().method(1); // expect runtime error: Expected 2 arguments but got 1. -------------------------------------------------------------------------------- /test/method/not_found.fail: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | Foo().unknown(); // expect runtime error: Undefined property 'unknown'. 4 | -------------------------------------------------------------------------------- /test/method/refer_to_name.fail: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method() { 3 | print method; // expect runtime error: Undefined variable 'method'. 4 | } 5 | } 6 | 7 | Foo().method(); 8 | -------------------------------------------------------------------------------- /test/method/too_many_arguments.fail: -------------------------------------------------------------------------------- 1 | true.method(1, 2, 3, 4, 5, 6, 7, 8, 9); // Error at '9': Cannot have more than 8 arguments. 2 | -------------------------------------------------------------------------------- /test/method/too_many_parameters.fail: -------------------------------------------------------------------------------- 1 | class Foo { 2 | // 9 parameters. 3 | method(a, b, c, d, e, f, g, h, i) {} // Error at 'i': Cannot have more than 8 parameters. 4 | } 5 | -------------------------------------------------------------------------------- /test/none/literal.fail: -------------------------------------------------------------------------------- 1 | print none; // expect: none 2 | -------------------------------------------------------------------------------- /test/number/decimal_point_at_eof.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at end: Expect property name after '.'. 2 | 123. -------------------------------------------------------------------------------- /test/number/leading_dot.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at '.': Expect expression. 2 | .123; 3 | -------------------------------------------------------------------------------- /test/number/literals.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/number/trailing_dot.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at ';': Expect property name after '.'. 2 | 123.; 3 | -------------------------------------------------------------------------------- /test/operator/add.fail: -------------------------------------------------------------------------------- 1 | print 123 + 456; // expect: 579 2 | print "str" + "ing"; // expect: string 3 | -------------------------------------------------------------------------------- /test/operator/add_bool_none.fail: -------------------------------------------------------------------------------- 1 | true + none; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /test/operator/add_bool_num.fail: -------------------------------------------------------------------------------- 1 | true + 123; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /test/operator/add_bool_string.fail: -------------------------------------------------------------------------------- 1 | true + "s"; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /test/operator/add_none_none.fail: -------------------------------------------------------------------------------- 1 | none + none; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /test/operator/add_num_none.fail: -------------------------------------------------------------------------------- 1 | 1 + none; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /test/operator/add_string_none.fail: -------------------------------------------------------------------------------- 1 | "s" + none; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /test/operator/comma.fail: -------------------------------------------------------------------------------- 1 | // basic comma test returns 4 (last value) 2 | var i = (1,2,4); 3 | print i; // expect: 4 4 | 5 | // var comma test (assigns all values and uses last val 2) 6 | var a=1, b=2, c=3; 7 | var j = (a, b); 8 | print a; // expect: 1 9 | print b; // expect: 2 10 | print c; // expect: 3 11 | print j; // expect: 2 12 | 13 | // prints the last value (6) 14 | print a = 4, b = 5, c = 6; // expect: 6 15 | -------------------------------------------------------------------------------- /test/operator/comma_all_asign.fail: -------------------------------------------------------------------------------- 1 | var a = 4, b, c=5; // Error at ',': Expect assignment in multiple variable declaration. 2 | print b; -------------------------------------------------------------------------------- /test/operator/comma_evaluate_all.fail: -------------------------------------------------------------------------------- 1 | var d = 0, f = 0; 2 | d++, f++; 3 | print d; // expect: 1 4 | print f; // expect: 1 -------------------------------------------------------------------------------- /test/operator/comma_precedence.fail: -------------------------------------------------------------------------------- 1 | var a=1, b=2, c=3; 2 | var i; 3 | i = a = a + 2, a + b; 4 | print i; // expect: 3 -------------------------------------------------------------------------------- /test/operator/comma_return.fail: -------------------------------------------------------------------------------- 1 | fun last() { 2 | return (1), 2, 3; 3 | } 4 | 5 | print last(); // expect: 3 -------------------------------------------------------------------------------- /test/operator/comparison.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/operator/decrement_none.fail: -------------------------------------------------------------------------------- 1 | none--; // expect runtime error: Operand of a decrement operator must be a variable. -------------------------------------------------------------------------------- /test/operator/decrement_none_var.fail: -------------------------------------------------------------------------------- 1 | var a = none; 2 | a--; // expect runtime error: Operand must be a number. -------------------------------------------------------------------------------- /test/operator/decrement_num.fail: -------------------------------------------------------------------------------- 1 | 1--; // expect runtime error: Operand of a decrement operator must be a variable. -------------------------------------------------------------------------------- /test/operator/decrement_string.fail: -------------------------------------------------------------------------------- 1 | "a"--; // expect runtime error: Operand of a decrement operator must be a variable. -------------------------------------------------------------------------------- /test/operator/decrement_variable.fail: -------------------------------------------------------------------------------- 1 | var a = 0; 2 | print --a; // expect: -1 3 | 4 | var b = 2; 5 | print b--; // expect: 2 -------------------------------------------------------------------------------- /test/operator/divide.fail: -------------------------------------------------------------------------------- 1 | print 8 / 2; // expect: 4 2 | print 12.34 / 12.34; // expect: 1 3 | -------------------------------------------------------------------------------- /test/operator/divide_nonnum_num.fail: -------------------------------------------------------------------------------- 1 | "1" / 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/operator/divide_num_nonnum.fail: -------------------------------------------------------------------------------- 1 | 1 / "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/operator/equals.fail: -------------------------------------------------------------------------------- 1 | print none == none; // 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 none == false; // expect: false 13 | print false == 0; // expect: false 14 | print 0 == "0"; // expect: false 15 | -------------------------------------------------------------------------------- /test/operator/equals_class.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/operator/greater_nonnum_num.fail: -------------------------------------------------------------------------------- 1 | "1" > 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/operator/greater_num_nonnum.fail: -------------------------------------------------------------------------------- 1 | 1 > "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/operator/greater_or_equal_nonnum_num.fail: -------------------------------------------------------------------------------- 1 | "1" >= 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/operator/greater_or_equal_num_nonnum.fail: -------------------------------------------------------------------------------- 1 | 1 >= "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/operator/increment_none.fail: -------------------------------------------------------------------------------- 1 | print none++; // expect runtime error: Operand of an increment operator must be a variable. -------------------------------------------------------------------------------- /test/operator/increment_none_var.fail: -------------------------------------------------------------------------------- 1 | var a = none; 2 | print a++; // expect runtime error: Operand must be a number. -------------------------------------------------------------------------------- /test/operator/increment_num.fail: -------------------------------------------------------------------------------- 1 | 1++; // expect runtime error: Operand of an increment operator must be a variable. -------------------------------------------------------------------------------- /test/operator/increment_string.fail: -------------------------------------------------------------------------------- 1 | print "a"++; // expect runtime error: Operand of an increment operator must be a variable. -------------------------------------------------------------------------------- /test/operator/increment_variable.fail: -------------------------------------------------------------------------------- 1 | var a = 0; 2 | print ++a; // expect: 1 3 | 4 | var b = 1; 5 | print b++; // expect: 1 -------------------------------------------------------------------------------- /test/operator/less_nonnum_num.fail: -------------------------------------------------------------------------------- 1 | "1" < 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/operator/less_num_nonnum.fail: -------------------------------------------------------------------------------- 1 | 1 < "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/operator/less_or_equal_nonnum_num.fail: -------------------------------------------------------------------------------- 1 | "1" <= 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/operator/less_or_equal_num_nonnum.fail: -------------------------------------------------------------------------------- 1 | 1 <= "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/operator/multiply.fail: -------------------------------------------------------------------------------- 1 | print 5 * 3; // expect: 15 2 | print 12.34 * 0.3; // expect: 3.702 3 | -------------------------------------------------------------------------------- /test/operator/multiply_nonnum_num.fail: -------------------------------------------------------------------------------- 1 | print "1" * 1; // expect: 1 2 | print "st" * 0; // expect: 3 | print "ab" * 3; // expect: ababab 4 | print "ab" * -3; // expect: 5 | -------------------------------------------------------------------------------- /test/operator/multiply_num_nonnum.fail: -------------------------------------------------------------------------------- 1 | print 1 * "1"; // expect: 1 2 | print 0 * "st"; // expect: 3 | print 3 * "ab"; // expect: ababab 4 | print -3 * "ab"; // expect: 5 | -------------------------------------------------------------------------------- /test/operator/negate.fail: -------------------------------------------------------------------------------- 1 | print -(3); // expect: -3 2 | print -(-3); // expect: 3 3 | print -(-(-3)); // expect: -3 4 | -------------------------------------------------------------------------------- /test/operator/negate_nonnum.fail: -------------------------------------------------------------------------------- 1 | -"s"; // expect runtime error: Operand must be a number. 2 | -------------------------------------------------------------------------------- /test/operator/not.fail: -------------------------------------------------------------------------------- 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 !none; // expect: true 9 | 10 | print !""; // expect: false 11 | 12 | fun foo() {} 13 | print !foo; // expect: false 14 | -------------------------------------------------------------------------------- /test/operator/not_class.fail: -------------------------------------------------------------------------------- 1 | class Bar {} 2 | print !Bar; // expect: false 3 | print !Bar(); // expect: false 4 | -------------------------------------------------------------------------------- /test/operator/not_equals.fail: -------------------------------------------------------------------------------- 1 | print none != none; // 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 none != false; // expect: true 13 | print false != 0; // expect: true 14 | print 0 != "0"; // expect: true 15 | -------------------------------------------------------------------------------- /test/operator/subtract.fail: -------------------------------------------------------------------------------- 1 | print 4 - 3; // expect: 1 2 | print 1.2 - 1.2; // expect: 0 3 | -------------------------------------------------------------------------------- /test/operator/subtract_nonnum_num.fail: -------------------------------------------------------------------------------- 1 | "1" - 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/operator/subtract_num_nonnum.fail: -------------------------------------------------------------------------------- 1 | 1 - "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /test/precedence.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/print/missing_argument.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at ';': Expect expression. 2 | print; 3 | -------------------------------------------------------------------------------- /test/regression/40.fail: -------------------------------------------------------------------------------- 1 | fun caller(g) { 2 | g(); 3 | // g should be a function, not none. 4 | print g == none; // 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 | -------------------------------------------------------------------------------- /test/return/after_else.fail: -------------------------------------------------------------------------------- 1 | fun f() { 2 | if (false) "no"; else return "ok"; 3 | } 4 | 5 | print f(); // expect: ok 6 | -------------------------------------------------------------------------------- /test/return/after_if.fail: -------------------------------------------------------------------------------- 1 | fun f() { 2 | if (true) return "ok"; 3 | } 4 | 5 | print f(); // expect: ok 6 | -------------------------------------------------------------------------------- /test/return/after_while.fail: -------------------------------------------------------------------------------- 1 | fun f() { 2 | while (true) return "ok"; 3 | } 4 | 5 | print f(); // expect: ok 6 | -------------------------------------------------------------------------------- /test/return/at_top_level.fail: -------------------------------------------------------------------------------- 1 | return "wat"; // Error at 'return': Cannot return from top-level code. 2 | -------------------------------------------------------------------------------- /test/return/in_function.fail: -------------------------------------------------------------------------------- 1 | fun f() { 2 | return "ok"; 3 | print "bad"; 4 | } 5 | 6 | print f(); // expect: ok 7 | -------------------------------------------------------------------------------- /test/return/in_method.fail: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method() { 3 | return "ok"; 4 | print "bad"; 5 | } 6 | } 7 | 8 | print Foo().method(); // expect: ok 9 | -------------------------------------------------------------------------------- /test/return/return_none_if_no_value.fail: -------------------------------------------------------------------------------- 1 | fun f() { 2 | return; 3 | print "bad"; 4 | } 5 | 6 | print f(); // expect: none 7 | -------------------------------------------------------------------------------- /test/scanning/identifiers.fail: -------------------------------------------------------------------------------- 1 | andy formless fo _ _123 _abc ab123 2 | abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_ 3 | 4 | // expect: IDENTIFIER andy null 5 | // expect: IDENTIFIER formless null 6 | // expect: IDENTIFIER fo null 7 | // expect: IDENTIFIER _ null 8 | // expect: IDENTIFIER _123 null 9 | // expect: IDENTIFIER _abc null 10 | // expect: IDENTIFIER ab123 null 11 | // expect: IDENTIFIER abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_ null 12 | // expect: EOF null 13 | -------------------------------------------------------------------------------- /test/scanning/keywords.fail: -------------------------------------------------------------------------------- 1 | and class else false for fun if nil or return super this true var while 2 | 3 | // expect: AND and null 4 | // expect: CLASS class null 5 | // expect: ELSE else null 6 | // expect: FALSE false null 7 | // expect: FOR for null 8 | // expect: FUN fun null 9 | // expect: IF if null 10 | // expect: NIL nil null 11 | // expect: OR or null 12 | // expect: RETURN return null 13 | // expect: SUPER super null 14 | // expect: THIS this null 15 | // expect: TRUE true null 16 | // expect: VAR var null 17 | // expect: WHILE while null 18 | // expect: EOF null 19 | -------------------------------------------------------------------------------- /test/scanning/numbers.fail: -------------------------------------------------------------------------------- 1 | 123 2 | 123.456 3 | .456 4 | 123. 5 | 6 | // expect: NUMBER 123 123.0 7 | // expect: NUMBER 123.456 123.456 8 | // expect: DOT . null 9 | // expect: NUMBER 456 456.0 10 | // expect: NUMBER 123 123.0 11 | // expect: DOT . null 12 | // expect: EOF null 13 | -------------------------------------------------------------------------------- /test/scanning/punctuators.fail: -------------------------------------------------------------------------------- 1 | (){};,+-*!===<=>=!=<>/. 2 | 3 | // expect: LEFT_PAREN ( null 4 | // expect: RIGHT_PAREN ) null 5 | // expect: LEFT_BRACE { null 6 | // expect: RIGHT_BRACE } null 7 | // expect: SEMICOLON ; null 8 | // expect: COMMA , null 9 | // expect: PLUS + null 10 | // expect: MINUS - null 11 | // expect: STAR * null 12 | // expect: BANG_EQUAL != null 13 | // expect: EQUAL_EQUAL == null 14 | // expect: LESS_EQUAL <= null 15 | // expect: GREATER_EQUAL >= null 16 | // expect: BANG_EQUAL != null 17 | // expect: LESS < null 18 | // expect: GREATER > null 19 | // expect: SLASH / null 20 | // expect: DOT . null 21 | // expect: EOF null 22 | -------------------------------------------------------------------------------- /test/scanning/strings.fail: -------------------------------------------------------------------------------- 1 | "" 2 | "string" 3 | 4 | // expect: STRING "" 5 | // expect: STRING "string" string 6 | // expect: EOF null -------------------------------------------------------------------------------- /test/scanning/whitespace.fail: -------------------------------------------------------------------------------- 1 | space tabs newlines 2 | 3 | 4 | 5 | 6 | end 7 | 8 | // expect: IDENTIFIER space null 9 | // expect: IDENTIFIER tabs null 10 | // expect: IDENTIFIER newlines null 11 | // expect: IDENTIFIER end null 12 | // expect: EOF null 13 | -------------------------------------------------------------------------------- /test/string/error_after_multiline.fail: -------------------------------------------------------------------------------- 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'. -------------------------------------------------------------------------------- /test/string/literals.fail: -------------------------------------------------------------------------------- 1 | print "(" + "" + ")"; // expect: () 2 | print "a string"; // expect: a string 3 | 4 | // Non-ASCII. 5 | print "A~¶Þॐஃ"; // expect: A~¶Þॐஃ 6 | -------------------------------------------------------------------------------- /test/string/multiline.fail: -------------------------------------------------------------------------------- 1 | var a = "1 2 | 2 3 | 3"; 4 | print a; 5 | // expect: 1 6 | // expect: 2 7 | // expect: 3 8 | -------------------------------------------------------------------------------- /test/string/unterminated.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error: Unterminated string. 2 | "this string has no close quote -------------------------------------------------------------------------------- /test/super/bound_method.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/super/call_other_method.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/super/call_same_method.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/super/closure.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/super/constructor.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/super/extra_arguments.fail: -------------------------------------------------------------------------------- 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(); -------------------------------------------------------------------------------- /test/super/indirectly_inherited.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/super/missing_arguments.fail: -------------------------------------------------------------------------------- 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(); -------------------------------------------------------------------------------- /test/super/no_superclass_bind.fail: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo() { 3 | super.doesNotExist; // Error at 'super': Cannot use 'super' in a class with no superclass. 4 | } 5 | } 6 | 7 | Base().foo(); 8 | -------------------------------------------------------------------------------- /test/super/no_superclass_call.fail: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo() { 3 | super.doesNotExist(1); // Error at 'super': Cannot use 'super' in a class with no superclass. 4 | } 5 | } 6 | 7 | Base().foo(); 8 | -------------------------------------------------------------------------------- /test/super/no_superclass_method.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/super/parenthesized.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/super/reassign_superclass.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/super/super_at_top_level.fail: -------------------------------------------------------------------------------- 1 | super.foo; // Error at 'super': Cannot use 'super' outside of a class. 2 | super.foo("bar"); // Error at 'super': Cannot use 'super' outside of a class. 3 | -------------------------------------------------------------------------------- /test/super/super_in_closure_in_inherited_method.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/super/super_in_inherited_method.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/super/super_in_top_level_function.fail: -------------------------------------------------------------------------------- 1 | fun foo() { 2 | super.bar(); // Error at 'super': Cannot use 'super' outside of a class. 3 | } 4 | -------------------------------------------------------------------------------- /test/super/super_without_dot.fail: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | class B < A { 4 | method() { 5 | // [line 6] Error at ';': Expect '.' after 'super'. 6 | super; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/super/super_without_name.fail: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | class B < A { 4 | method() { 5 | super.; // Error at ';': Expect superclass method name. 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/super/this_in_superclass_method.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/this/closure.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/this/nested_class.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/this/nested_closure.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/this/this_at_top_level.fail: -------------------------------------------------------------------------------- 1 | this; // Error at 'this': Cannot use 'this' outside of a class. 2 | -------------------------------------------------------------------------------- /test/this/this_in_method.fail: -------------------------------------------------------------------------------- 1 | class Foo { 2 | bar() { return this; } 3 | baz() { return "baz"; } 4 | } 5 | 6 | print Foo().bar().baz(); // expect: baz 7 | -------------------------------------------------------------------------------- /test/this/this_in_top_level_function.fail: -------------------------------------------------------------------------------- 1 | fun foo() { 2 | this; // Error at 'this': Cannot use 'this' outside of a class. 3 | } 4 | -------------------------------------------------------------------------------- /test/unexpected_character.fail: -------------------------------------------------------------------------------- 1 | // [line 3] Error: Unexpected character. 2 | // [java line 3] Error at 'b': Expect ')' after arguments. 3 | foo(a | b); 4 | -------------------------------------------------------------------------------- /test/variable/collide_with_parameter.fail: -------------------------------------------------------------------------------- 1 | fun foo(a) { 2 | var a; // Error at 'a': Variable with this name already declared in this scope. 3 | } 4 | -------------------------------------------------------------------------------- /test/variable/duplicate_local.fail: -------------------------------------------------------------------------------- 1 | { 2 | var a = "value"; 3 | var a = "other"; // Error at 'a': Variable with this name already declared in this scope. 4 | } 5 | -------------------------------------------------------------------------------- /test/variable/duplicate_parameter.fail: -------------------------------------------------------------------------------- 1 | fun foo(arg, 2 | arg) { // Error at 'arg': Variable with this name already declared in this scope. 3 | "body"; 4 | } 5 | -------------------------------------------------------------------------------- /test/variable/early_bound.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/variable/in_middle_of_block.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/variable/in_nested_block.fail: -------------------------------------------------------------------------------- 1 | { 2 | var a = "outer"; 3 | { 4 | print a; // expect: outer 5 | } 6 | } -------------------------------------------------------------------------------- /test/variable/local_from_method.fail: -------------------------------------------------------------------------------- 1 | var foo = "variable"; 2 | 3 | class Foo { 4 | method() { 5 | print foo; 6 | } 7 | } 8 | 9 | Foo().method(); // expect: variable 10 | -------------------------------------------------------------------------------- /test/variable/redeclare_global.fail: -------------------------------------------------------------------------------- 1 | var a = "1"; 2 | var a; 3 | print a; // expect runtime error: Variable must be initialized before use. 4 | -------------------------------------------------------------------------------- /test/variable/redefine_global.fail: -------------------------------------------------------------------------------- 1 | var a = "1"; 2 | var a = "2"; 3 | print a; // expect: 2 4 | -------------------------------------------------------------------------------- /test/variable/refer_self_initializer.fail: -------------------------------------------------------------------------------- 1 | var a = "outer"; 2 | { 3 | var a = a; // Error at 'a': Cannot read local variable in its own initializer. 4 | } -------------------------------------------------------------------------------- /test/variable/scope_reuse_in_different_blocks.fail: -------------------------------------------------------------------------------- 1 | { 2 | var a = "first"; 3 | print a; // expect: first 4 | } 5 | 6 | { 7 | var a = "second"; 8 | print a; // expect: second 9 | } 10 | -------------------------------------------------------------------------------- /test/variable/shadow_and_local.fail: -------------------------------------------------------------------------------- 1 | { 2 | var a = "outer"; 3 | { 4 | print a; // expect: outer 5 | var a = "inner"; 6 | print a; // expect: inner 7 | } 8 | } -------------------------------------------------------------------------------- /test/variable/shadow_global.fail: -------------------------------------------------------------------------------- 1 | var a = "global"; 2 | { 3 | var a = "shadow"; 4 | print a; // expect: shadow 5 | } 6 | print a; // expect: global 7 | -------------------------------------------------------------------------------- /test/variable/shadow_local.fail: -------------------------------------------------------------------------------- 1 | { 2 | var a = "local"; 3 | { 4 | var a = "shadow"; 5 | print a; // expect: shadow 6 | } 7 | print a; // expect: local 8 | } 9 | -------------------------------------------------------------------------------- /test/variable/undefined_global.fail: -------------------------------------------------------------------------------- 1 | print notDefined; // expect runtime error: Undefined variable 'notDefined'. 2 | -------------------------------------------------------------------------------- /test/variable/undefined_local.fail: -------------------------------------------------------------------------------- 1 | { 2 | print notDefined; // expect runtime error: Undefined variable 'notDefined'. 3 | } 4 | -------------------------------------------------------------------------------- /test/variable/uninitialized.fail: -------------------------------------------------------------------------------- 1 | var a; 2 | print a; // expect runtime error: Variable must be initialized before use. 3 | -------------------------------------------------------------------------------- /test/variable/unreached_undefined.fail: -------------------------------------------------------------------------------- 1 | if (false) { 2 | print notDefined; 3 | } 4 | 5 | print "ok"; // expect: ok 6 | -------------------------------------------------------------------------------- /test/variable/use_false_as_var.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'false': Expect variable name. 2 | var false = "value"; 3 | -------------------------------------------------------------------------------- /test/variable/use_global_in_initializer.fail: -------------------------------------------------------------------------------- 1 | var a = "value"; 2 | var a = a; 3 | print a; // expect: value 4 | 5 | // needs to be changed to err: "Cannot read local variable in its own initializer." as in use_local_in_initializer. -------------------------------------------------------------------------------- /test/variable/use_local_in_initializer.fail: -------------------------------------------------------------------------------- 1 | var a = "outer"; 2 | { 3 | var a = a; // Error at 'a': Cannot read local variable in its own initializer. 4 | } 5 | -------------------------------------------------------------------------------- /test/variable/use_none_as_var.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'none': Expect variable name. 2 | var none = "value"; 3 | -------------------------------------------------------------------------------- /test/variable/use_this_as_var.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'this': Expect variable name. 2 | var this = "value"; 3 | -------------------------------------------------------------------------------- /test/while/break.fail: -------------------------------------------------------------------------------- 1 | while(true) { 2 | print "hi"; 3 | break; 4 | } 5 | // expect: hi -------------------------------------------------------------------------------- /test/while/class_in_body.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | while (true) class Foo {} 3 | -------------------------------------------------------------------------------- /test/while/closure_in_body.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/while/continue.fail: -------------------------------------------------------------------------------- 1 | var i = 0; 2 | while (i < 3) { 3 | if (i == 1) 4 | { 5 | i++; 6 | continue; 7 | } 8 | print i; 9 | i++; 10 | } 11 | 12 | // expect: 0 13 | // expect: 2 -------------------------------------------------------------------------------- /test/while/fun_in_body.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | while (true) fun foo() {} 3 | -------------------------------------------------------------------------------- /test/while/return_closure.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/while/return_inside.fail: -------------------------------------------------------------------------------- 1 | fun f() { 2 | while (true) { 3 | var i = "i"; 4 | return i; 5 | } 6 | } 7 | 8 | print f(); 9 | // expect: i 10 | -------------------------------------------------------------------------------- /test/while/syntax.fail: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/while/var_in_body.fail: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | while (true) var foo; 3 | --------------------------------------------------------------------------------