diff --git a/Makefile b/Makefile index c00e7006..314530fd 100644 --- a/Makefile +++ b/Makefile @@ -137,7 +137,6 @@ test_control_flow_w_jlox: build ]" \ $(shell pwd)/dist/main.out - test_functions_w_jlox: build CODECRAFTERS_REPOSITORY_DIR=./craftinginterpreters/build/gen/chap10_functions \ CODECRAFTERS_TEST_CASES_JSON="[ \ @@ -154,7 +153,7 @@ test_functions_w_jlox: build $(shell pwd)/dist/main.out test_resolving_w_jlox: build - CODECRAFTERS_REPOSITORY_DIR=./craftinginterpreters/build/gen/chap10_functions \ + CODECRAFTERS_REPOSITORY_DIR=./craftinginterpreters/build/gen/chap11_resolving \ CODECRAFTERS_TEST_CASES_JSON="[ \ {\"slug\":\"r1\",\"tester_log_prefix\":\"stage_701\",\"title\":\"Stage #701: Resolving: Function Resolution\"}, \ {\"slug\":\"r2\",\"tester_log_prefix\":\"stage_702\",\"title\":\"Stage #702: Resolving: Variable Resolution\"}, \ @@ -166,4 +165,4 @@ test_resolving_w_jlox: build ]" \ $(shell pwd)/dist/main.out -test_all: test_scanning_w_jlox test_parsing_w_jlox test_evaluation_w_jlox test_statements_w_jlox test_control_flow_w_jlox +test_all: test_scanning_w_jlox test_parsing_w_jlox test_evaluation_w_jlox test_statements_w_jlox test_control_flow_w_jlox test_functions_w_jlox test_resolving_w_jlox diff --git a/internal/lox/api/run_api.go b/internal/lox/api/run_api.go index 29581e46..f1ca17f1 100644 --- a/internal/lox/api/run_api.go +++ b/internal/lox/api/run_api.go @@ -15,10 +15,16 @@ func Run(source string) (string, int, string) { tokens := scanner.ScanTokens(mockStdout, mockStderr) parser := lox.NewParser(tokens) statements := parser.Parse(mockStdout, mockStderr) - lox.Interpret(statements, mockStdout, mockStderr) + locals, err := lox.Resolve(statements) + if err != nil && lox.HadSemanticError { + return "", 65, err.Error() + } + + env := lox.NewGlobal() + lox.Interpret(statements, env, locals, mockStdout, mockStderr) exitCode := 0 - if lox.HadParseError { + if lox.HadParseError || lox.HadSemanticError { exitCode = 65 } else if lox.HadRuntimeError { exitCode = 70 diff --git a/internal/lox/environment.go b/internal/lox/environment.go index 15ed0953..b34db227 100644 --- a/internal/lox/environment.go +++ b/internal/lox/environment.go @@ -46,6 +46,20 @@ func (e *Environment) Get(name Token) (interface{}, error) { return nil, MakeRuntimeError(name, fmt.Sprintf("Undefined variable '%s'.", name.Lexeme)) } +// GetAt lookups a variable a certain distance up the chain of environments +func (e *Environment) GetAt(distance int, name Token) (interface{}, error) { + return e.Ancestor(distance).Get(name) +} + +// Ancestor reaches an environment up the environment chain +func (e *Environment) Ancestor(distance int) *Environment { + env := e + for range distance { + env = env.enclosing + } + return env +} + // Assign sets a new value to an old variable func (e *Environment) Assign(name Token, value interface{}) error { if _, prs := e.values[name.Lexeme]; prs { @@ -57,3 +71,8 @@ func (e *Environment) Assign(name Token, value interface{}) error { } return MakeRuntimeError(name, fmt.Sprintf("Undefined variable '%s'.", name.Lexeme)) } + +// AssignAt sets a new value to an old variable +func (e *Environment) AssignAt(distance int, name Token, value any) error { + return e.Ancestor(distance).Assign(name, value) +} diff --git a/internal/lox/functions.go b/internal/lox/functions.go index 96d0d9af..db081db9 100644 --- a/internal/lox/functions.go +++ b/internal/lox/functions.go @@ -37,11 +37,16 @@ type UserFunction struct { Callable Declaration *Function Closure *Environment + Locals Locals // TODO: Pass pointer to Locals } // NewUserFunction creates a new UserFunction -func NewUserFunction(declaration *Function, closure *Environment) *UserFunction { - return &UserFunction{Declaration: declaration, Closure: closure} +func NewUserFunction(declaration *Function, closure *Environment, locals Locals) *UserFunction { + return &UserFunction{ + Declaration: declaration, + Closure: closure, + Locals: locals, + } } // Call executes a user-defined Lox function @@ -54,7 +59,7 @@ func (u *UserFunction) Call(arguments []interface{}, globalEnv *Environment, std } for _, stmt := range u.Declaration.Body { - _, err := Eval(stmt, env, stdout, stderr) + _, err := Eval(stmt, env, u.Locals, stdout, stderr) if err != nil { if r, ok := err.(ReturnError); ok { diff --git a/internal/lox/globals.go b/internal/lox/globals.go index f1ba9246..d3aada03 100644 --- a/internal/lox/globals.go +++ b/internal/lox/globals.go @@ -4,8 +4,12 @@ import ( "time" ) -func InitializeNativeFunctions(env *Environment) { - env.Define("clock", &NativeFunction{ +// GlobalEnv is the global environment +var GlobalEnv = NewGlobal() +var globals = GlobalEnv + +func InitializeNativeFunctions() { + GlobalEnv.Define("clock", &NativeFunction{ arity: 0, nativeCall: func(args []interface{}) (interface{}, error) { exponentNotation := float64(time.Now().Unix()) @@ -14,3 +18,8 @@ func InitializeNativeFunctions(env *Environment) { }, }) } + +// ResetGlobalEnv resets the GlobalEnv to its original reference +func ResetGlobalEnv() { + GlobalEnv = globals +} diff --git a/internal/lox/interpreter.go b/internal/lox/interpreter.go index c8053cd5..20b152d7 100644 --- a/internal/lox/interpreter.go +++ b/internal/lox/interpreter.go @@ -18,7 +18,7 @@ type ReturnError struct { } func BasicInterpret(expression Expr, stdout io.Writer, stderr io.Writer) { - result, err := Eval(expression, NewGlobal(), stdout, stderr) + result, err := Eval(expression, NewGlobal(), Locals{}, stdout, stderr) if err != nil { LogRuntimeError(err, stderr) return @@ -29,27 +29,30 @@ func BasicInterpret(expression Expr, stdout io.Writer, stderr io.Writer) { fmt.Fprintln(stdout, result) } -func Interpret(statements []Stmt, stdout io.Writer, stderr io.Writer) { - env := NewGlobal() - InitializeNativeFunctions(env) +func Interpret(statements []Stmt, env *Environment, locals Locals, stdout io.Writer, stderr io.Writer) { + OldGlobalEnv := GlobalEnv + GlobalEnv = env + InitializeNativeFunctions() + for _, stmt := range statements { - _, err := Eval(stmt, env, stdout, stderr) + _, err := Eval(stmt, env, locals, stdout, stderr) if err != nil { LogRuntimeError(err, stderr) return } } + GlobalEnv = OldGlobalEnv } // Eval evaluates the given AST -func Eval(node Node, environment *Environment, stdout io.Writer, stderr io.Writer) (interface{}, error) { +func Eval(node Node, environment *Environment, locals Locals, stdout io.Writer, stderr io.Writer) (interface{}, error) { switch n := node.(type) { case *Literal: return n.Value, nil case *Grouping: - return Eval(n.Expression, environment, stdout, stderr) + return Eval(n.Expression, environment, locals, stdout, stderr) case *Unary: - right, err := Eval(n.Right, environment, stdout, stderr) + right, err := Eval(n.Right, environment, locals, stdout, stderr) if err != nil { return right, err } else if n.Operator.Type == MINUS { @@ -62,11 +65,11 @@ func Eval(node Node, environment *Environment, stdout io.Writer, stderr io.Write return !isTruthy(right), nil } case *Binary: - left, err := Eval(n.Left, environment, stdout, stderr) + left, err := Eval(n.Left, environment, locals, stdout, stderr) if err != nil { return left, err } - right, err := Eval(n.Right, environment, stdout, stderr) + right, err := Eval(n.Right, environment, locals, stdout, stderr) if err != nil { return right, err } @@ -161,7 +164,7 @@ func Eval(node Node, environment *Environment, stdout io.Writer, stderr io.Write return isEqual(left, right), nil } case *Print: - value, err := Eval(n.Expression, environment, stdout, stderr) + value, err := Eval(n.Expression, environment, locals, stdout, stderr) if err != nil { return value, err } @@ -176,14 +179,14 @@ func Eval(node Node, environment *Environment, stdout io.Writer, stderr io.Write } return nil, nil case *Expression: - r, err := Eval(n.Expression, environment, stdout, stderr) + r, err := Eval(n.Expression, environment, locals, stdout, stderr) if err != nil { return r, err } return nil, nil case *Var: if n.Initializer != nil { - value, err := Eval(n.Initializer, environment, stdout, stderr) + value, err := Eval(n.Initializer, environment, locals, stdout, stderr) if err != nil { return nil, err } @@ -194,38 +197,46 @@ func Eval(node Node, environment *Environment, stdout io.Writer, stderr io.Write } return nil, nil case *Variable: - return environment.Get(n.Name) + if distance, ok := locals[n]; ok { + return environment.GetAt(distance, n.Name) + } + return GlobalEnv.Get(n.Name) case *Assign: - value, err := Eval(n.Value, environment, stdout, stderr) + value, err := Eval(n.Value, environment, locals, stdout, stderr) if err != nil { return nil, err } - if err = environment.Assign(n.Name, value); err == nil { + if distance, ok := locals[n]; ok { + if err := environment.AssignAt(distance, n.Name, value); err == nil { + return value, nil + } + return nil, err + } else if err := GlobalEnv.Assign(n.Name, value); err == nil { return value, nil } return nil, err case *Block: newEnvironment := New(environment) for _, stmt := range n.Statements { - _, err := Eval(stmt, newEnvironment, stdout, stderr) + _, err := Eval(stmt, newEnvironment, locals, stdout, stderr) if err != nil { return nil, err } } return nil, nil case *If: - condition, err := Eval(n.Condition, environment, stdout, stderr) + condition, err := Eval(n.Condition, environment, locals, stdout, stderr) if err != nil { return nil, err } if isTruthy(condition) { - return Eval(n.ThenBranch, environment, stdout, stderr) + return Eval(n.ThenBranch, environment, locals, stdout, stderr) } else if n.ElseBranch != nil { - return Eval(n.ElseBranch, environment, stdout, stderr) + return Eval(n.ElseBranch, environment, locals, stdout, stderr) } return nil, nil case *Logical: - left, err := Eval(n.Left, environment, stdout, stderr) + left, err := Eval(n.Left, environment, locals, stdout, stderr) if err != nil { return nil, err } @@ -239,16 +250,16 @@ func Eval(node Node, environment *Environment, stdout io.Writer, stderr io.Write return left, nil } } - return Eval(n.Right, environment, stdout, stderr) + return Eval(n.Right, environment, locals, stdout, stderr) case *Call: - callee, err := Eval(n.Callee, environment, stdout, stderr) + callee, err := Eval(n.Callee, environment, locals, stdout, stderr) if err != nil { return nil, err } args := make([]interface{}, 0) for _, arg := range n.Arguments { - a, err := Eval(arg, environment, stdout, stderr) + a, err := Eval(arg, environment, locals, stdout, stderr) if err == nil { args = append(args, a) } else { @@ -268,28 +279,28 @@ func Eval(node Node, environment *Environment, stdout io.Writer, stderr io.Write return function.Call(args, environment, stdout, stderr) case *While: for { - condition, err := Eval(n.Condition, environment, stdout, stderr) + condition, err := Eval(n.Condition, environment, locals, stdout, stderr) if err != nil { return nil, err } if !isTruthy(condition) { break } - _, err = Eval(n.Statement, environment, stdout, stderr) + _, err = Eval(n.Statement, environment, locals, stdout, stderr) if err != nil { return nil, err } } return nil, nil case *Function: - function := NewUserFunction(n, environment) + function := NewUserFunction(n, environment, locals) environment.Define(n.Name.Lexeme, function) return nil, nil case *Return: var value interface{} var err error if n.Value != nil { - value, err = Eval(n.Value, environment, stdout, stderr) + value, err = Eval(n.Value, environment, locals, stdout, stderr) if err != nil { return nil, err } @@ -331,4 +342,5 @@ func checkNumberOperand(operator Token, value interface{}, msg string) error { func ClearErrorFlags() { HadParseError = false HadRuntimeError = false + HadSemanticError = false } diff --git a/internal/lox/resolver.go b/internal/lox/resolver.go new file mode 100644 index 00000000..5dc69d8a --- /dev/null +++ b/internal/lox/resolver.go @@ -0,0 +1,211 @@ +package lox + +import ( + "fmt" +) + +// Locals is the output of the resolve phase +type Locals = map[Expr]int + +// Scope represents a Lox scope +type Scope = map[string]bool + +// Resolve performs name resolution to the given statements +func Resolve(statements []Stmt) (Locals, error) { + locals := make(Locals) + resolver := &Resolver{scopes: make([]Scope, 0), currentFunctionType: ftNone} + err := resolver.resolveStatements(statements, locals) + return locals, err +} + +// FunctionType represents the type of a function +const ( + ftNone = iota + ftFunction = iota +) + +// Resolver performs variable resolution on an AST +type Resolver struct { + scopes []Scope + currentFunctionType int +} + +func (r *Resolver) resolve(node Node, locals Locals) error { + switch n := node.(type) { + case *Block: + r.pushScope() + defer r.popScope() + for _, stmt := range n.Statements { + if err := r.resolve(stmt, locals); err != nil { + return err + } + } + case *Var: + if err := r.declare(n.Name); err != nil { + return MakeSemanticError("Already a variable with this name in this scope.") + } + if n.Initializer != nil { + if err := r.resolve(n.Initializer, locals); err != nil { + return MakeSemanticError("Can't read local variable in its own initializer.") + } + } + r.define(n.Name) + case *Variable: + if len(r.scopes) != 0 { + // Local scope + if b, ok := r.scopes[len(r.scopes)-1][n.Name.Lexeme]; ok && !b { + return MakeSemanticError("Cannot read local variable in its own initializer.") + } + } + r.resolveLocal(n, n.Name, locals) + case *Assign: + if err := r.resolve(n.Value, locals); err != nil { + return err + } + r.resolveLocal(n, n.Name, locals) + case *Function: + if err := r.declare(n.Name); err != nil { + return err + } + r.define(n.Name) + if err := r.resolveFunction(n, locals, ftFunction); err != nil { + return err + } + case *Expression: + if err := r.resolve(n.Expression, locals); err != nil { + return err + } + case *If: + if err := r.resolve(n.Condition, locals); err != nil { + return err + } + if err := r.resolve(n.ThenBranch, locals); err != nil { + return err + } + if n.ElseBranch != nil { + if err := r.resolve(n.ElseBranch, locals); err != nil { + return err + } + } + case *Print: + if err := r.resolve(n.Expression, locals); err != nil { + return err + } + case *Return: + if r.currentFunctionType == ftNone { + return MakeSemanticError("Cannot return from top-level code.") + } + if n.Value != nil { + if err := r.resolve(n.Value, locals); err != nil { + return err + } + } + case *While: + if err := r.resolve(n.Condition, locals); err != nil { + return err + } + if err := r.resolve(n.Statement, locals); err != nil { + return err + } + case *Binary: + if err := r.resolve(n.Left, locals); err != nil { + return err + } + if err := r.resolve(n.Right, locals); err != nil { + return err + } + case *Call: + if err := r.resolve(n.Callee, locals); err != nil { + return err + } + + for _, e := range n.Arguments { + if err := r.resolve(e, locals); err != nil { + return err + } + } + case *Grouping: + if err := r.resolve(n.Expression, locals); err != nil { + return err + } + case *Logical: + if err := r.resolve(n.Left, locals); err != nil { + return err + } + if err := r.resolve(n.Right, locals); err != nil { + return err + } + case *Unary: + if err := r.resolve(n.Right, locals); err != nil { + return err + } + } + return nil +} + +func (r *Resolver) resolveStatements(statements []Stmt, locals Locals) error { + for _, stmt := range statements { + if err := r.resolve(stmt, locals); err != nil { + return err + } + } + return nil +} + +func (r *Resolver) resolveFunction(function *Function, locals Locals, functionType int) error { + enclosingFunctionType := r.currentFunctionType + r.currentFunctionType = functionType + + resetCurrentFunction := func() { + r.currentFunctionType = enclosingFunctionType + } + + defer resetCurrentFunction() + + r.pushScope() + defer r.popScope() + + for _, param := range function.Params { + if err := r.declare(param); err != nil { + return err + } + r.define(param) + } + return r.resolveStatements(function.Body, locals) +} + +func (r *Resolver) resolveLocal(expr Expr, name Token, locals Locals) { + for i := len(r.scopes) - 1; i >= 0; i-- { + if _, ok := r.scopes[i][name.Lexeme]; ok { + locals[expr] = len(r.scopes) - i - 1 + return + } + } +} + +func (r *Resolver) pushScope() { + r.scopes = append(r.scopes, make(Scope)) +} + +func (r *Resolver) popScope() { + r.scopes = r.scopes[:len(r.scopes)-1] +} + +func (r *Resolver) declare(name Token) error { + if len(r.scopes) != 0 { + scope := r.scopes[len(r.scopes)-1] + if _, ok := scope[name.Lexeme]; ok { + return MakeSemanticError( + fmt.Sprintf("Variable '%s' already declared in this scope.", name.Lexeme)) + } + scope[name.Lexeme] = false + } + return nil +} + +func (r *Resolver) define(name Token) { + if len(r.scopes) != 0 { + scope := r.scopes[len(r.scopes)-1] + scope[name.Lexeme] = true + } +} diff --git a/internal/lox/semantic_error.go b/internal/lox/semantic_error.go new file mode 100644 index 00000000..3722a1c6 --- /dev/null +++ b/internal/lox/semantic_error.go @@ -0,0 +1,21 @@ +package lox + +import ( + "fmt" + "os" +) + +// PrintSemanticError reports a semantic error +func PrintSemanticError(message string) { + fmt.Fprintf(os.Stderr, "%v\n", message) + HadSemanticError = true +} + +// MakeSemanticError creates a new semantic error +func MakeSemanticError(message string) error { + HadSemanticError = true + return fmt.Errorf("%s", message) +} + +// HadSemanticError is true if an evaluation error was encountered +var HadSemanticError = false diff --git a/internal/stages_test.go b/internal/stages_test.go index 66a72d48..601ac835 100644 --- a/internal/stages_test.go +++ b/internal/stages_test.go @@ -75,6 +75,20 @@ func TestStages(t *testing.T) { StdoutFixturePath: "./test_helpers/fixtures/pass_functions_final", NormalizeOutputFunc: normalizeTesterOutput, }, + "pass_resolving_inprogress_jlox": { + StageSlugs: []string{"r1", "r2", "r3", "r4", "r5", "r6", "r7"}, + CodePath: "../craftinginterpreters/build/gen/chap11_resolving", + ExpectedExitCode: 0, + StdoutFixturePath: "./test_helpers/fixtures/pass_resolving", + NormalizeOutputFunc: normalizeTesterOutput, + }, + "pass_resolving_completed_jlox": { + StageSlugs: []string{"r1", "r2", "r3", "r4", "r5", "r6", "r7"}, + CodePath: "../craftinginterpreters/build/gen/chap13_inheritance", + ExpectedExitCode: 0, + StdoutFixturePath: "./test_helpers/fixtures/pass_resolving_final", + NormalizeOutputFunc: normalizeTesterOutput, + }, } tester_utils_testing.TestTesterOutput(t, testerDefinition, testCases) diff --git a/internal/test_helpers/fixtures/pass_functions b/internal/test_helpers/fixtures/pass_functions index 7bf88655..8880dabe 100644 --- a/internal/test_helpers/fixtures/pass_functions +++ b/internal/test_helpers/fixtures/pass_functions @@ -635,7 +635,7 @@ Debug = true [stage-1] [test-2] [test.lox] return isEven(n - 1); [stage-1] [test-2] [test.lox] } [stage-1] [test-2] [test.lox] -[stage-1] [test-2] [test.lox] print isEven(93); +[stage-1] [test-2] [test.lox] print isEven(75); [stage-1] [test-2] [test.lox] } [stage-1] [test-2] $ ./your_program.sh run test.lox [your_program] false diff --git a/internal/test_helpers/fixtures/pass_functions_final b/internal/test_helpers/fixtures/pass_functions_final index 08fccc79..29df5cef 100644 --- a/internal/test_helpers/fixtures/pass_functions_final +++ b/internal/test_helpers/fixtures/pass_functions_final @@ -635,7 +635,7 @@ Debug = true [stage-1] [test-2] [test.lox] return isEven(n - 1); [stage-1] [test-2] [test.lox] } [stage-1] [test-2] [test.lox] -[stage-1] [test-2] [test.lox] print isEven(93); +[stage-1] [test-2] [test.lox] print isEven(75); [stage-1] [test-2] [test.lox] } [stage-1] [test-2] $ ./your_program.sh run test.lox [your_program] false diff --git a/internal/test_helpers/fixtures/pass_resolving b/internal/test_helpers/fixtures/pass_resolving new file mode 100644 index 00000000..c9fa74de --- /dev/null +++ b/internal/test_helpers/fixtures/pass_resolving @@ -0,0 +1,306 @@ +Debug = true + +[stage-7] Running tests for Stage #7: r1 +[stage-7] [test-1] Running test case: 1 +[stage-7] [test-1] Writing contents to ./test.lox: +[stage-7] [test-1] [test.lox] var a = "outer"; +[stage-7] [test-1] [test.lox] { +[stage-7] [test-1] [test.lox] fun foo() { +[stage-7] [test-1] [test.lox] print a; +[stage-7] [test-1] [test.lox] } +[stage-7] [test-1] [test.lox] +[stage-7] [test-1] [test.lox] foo(); // expect: outer +[stage-7] [test-1] [test.lox] var a = "inner"; +[stage-7] [test-1] [test.lox] foo(); // expect: outer +[stage-7] [test-1] [test.lox] } +[stage-7] [test-1] $ ./your_program.sh run test.lox +[your_program] outer +[your_program] outer +[stage-7] [test-1] ✓ 2 line(s) match on stdout +[stage-7] [test-1] ✓ Received exit code 0. +[stage-7] [test-2] Running test case: 2 +[stage-7] [test-2] Writing contents to ./test.lox: +[stage-7] [test-2] [test.lox] fun global() { +[stage-7] [test-2] [test.lox] print "global"; +[stage-7] [test-2] [test.lox] } +[stage-7] [test-2] [test.lox] +[stage-7] [test-2] [test.lox] { +[stage-7] [test-2] [test.lox] fun f() { +[stage-7] [test-2] [test.lox] global(); +[stage-7] [test-2] [test.lox] } +[stage-7] [test-2] [test.lox] +[stage-7] [test-2] [test.lox] f(); +[stage-7] [test-2] [test.lox] fun global() { +[stage-7] [test-2] [test.lox] print "local"; +[stage-7] [test-2] [test.lox] } +[stage-7] [test-2] [test.lox] f(); +[stage-7] [test-2] [test.lox] } +[stage-7] [test-2] $ ./your_program.sh run test.lox +[your_program] global +[your_program] global +[stage-7] [test-2] ✓ 2 line(s) match on stdout +[stage-7] [test-2] ✓ Received exit code 0. +[stage-7] [test-3] Running test case: 3 +[stage-7] [test-3] Writing contents to ./test.lox: +[stage-7] [test-3] [test.lox] // Multiple Nested Functions with Shadowing +[stage-7] [test-3] [test.lox] var x = "global"; +[stage-7] [test-3] [test.lox] +[stage-7] [test-3] [test.lox] fun outer() { +[stage-7] [test-3] [test.lox] var x = "outer"; +[stage-7] [test-3] [test.lox] +[stage-7] [test-3] [test.lox] fun middle() { +[stage-7] [test-3] [test.lox] fun inner() { +[stage-7] [test-3] [test.lox] print x; // Should capture "outer", not "global" or "inner" +[stage-7] [test-3] [test.lox] } +[stage-7] [test-3] [test.lox] +[stage-7] [test-3] [test.lox] inner(); +[stage-7] [test-3] [test.lox] var x = "middle"; +[stage-7] [test-3] [test.lox] inner(); // Should still print "outer" +[stage-7] [test-3] [test.lox] } +[stage-7] [test-3] [test.lox] +[stage-7] [test-3] [test.lox] middle(); +[stage-7] [test-3] [test.lox] } +[stage-7] [test-3] [test.lox] +[stage-7] [test-3] [test.lox] outer(); +[stage-7] [test-3] $ ./your_program.sh run test.lox +[your_program] outer +[your_program] outer +[stage-7] [test-3] ✓ 2 line(s) match on stdout +[stage-7] [test-3] ✓ Received exit code 0. +[stage-7] [test-4] Running test case: 4 +[stage-7] [test-4] Writing contents to ./test.lox: +[stage-7] [test-4] [test.lox] // Function Returning a Function +[stage-7] [test-4] [test.lox] var count = 0; +[stage-7] [test-4] [test.lox] +[stage-7] [test-4] [test.lox] { +[stage-7] [test-4] [test.lox] fun makeCounter() { +[stage-7] [test-4] [test.lox] fun counter() { +[stage-7] [test-4] [test.lox] count = count + 1; +[stage-7] [test-4] [test.lox] print count; +[stage-7] [test-4] [test.lox] } +[stage-7] [test-4] [test.lox] return counter; +[stage-7] [test-4] [test.lox] } +[stage-7] [test-4] [test.lox] +[stage-7] [test-4] [test.lox] var counter1 = makeCounter(); +[stage-7] [test-4] [test.lox] counter1(); // Should print 1 +[stage-7] [test-4] [test.lox] counter1(); // Should print 2 +[stage-7] [test-4] [test.lox] +[stage-7] [test-4] [test.lox] var count = 0; +[stage-7] [test-4] [test.lox] counter1(); // Should print 3 +[stage-7] [test-4] [test.lox] } +[stage-7] [test-4] $ ./your_program.sh run test.lox +[your_program] 1 +[your_program] 2 +[your_program] 3 +[stage-7] [test-4] ✓ 3 line(s) match on stdout +[stage-7] [test-4] ✓ Received exit code 0. +[stage-7] Test passed. + +[stage-6] Running tests for Stage #6: r2 +[stage-6] [test-1] Running test case: 1 +[stage-6] [test-1] Writing contents to ./test.lox: +[stage-6] [test-1] [test.lox] +[stage-6] [test-1] $ ./your_program.sh run test.lox +[stage-6] [test-1] ✓ 1 line(s) match on stdout +[stage-6] [test-1] ✓ Received exit code 0. +[stage-6] [test-2] Running test case: 2 +[stage-6] [test-2] Writing contents to ./test.lox: +[stage-6] [test-2] [test.lox] +[stage-6] [test-2] $ ./your_program.sh run test.lox +[stage-6] [test-2] ✓ 1 line(s) match on stdout +[stage-6] [test-2] ✓ Received exit code 0. +[stage-6] [test-3] Running test case: 3 +[stage-6] [test-3] Writing contents to ./test.lox: +[stage-6] [test-3] [test.lox] +[stage-6] [test-3] $ ./your_program.sh run test.lox +[stage-6] [test-3] ✓ 1 line(s) match on stdout +[stage-6] [test-3] ✓ Received exit code 0. +[stage-6] [test-4] Running test case: 4 +[stage-6] [test-4] Writing contents to ./test.lox: +[stage-6] [test-4] [test.lox] +[stage-6] [test-4] $ ./your_program.sh run test.lox +[stage-6] [test-4] ✓ 1 line(s) match on stdout +[stage-6] [test-4] ✓ Received exit code 0. +[stage-6] Test passed. + +[stage-5] Running tests for Stage #5: r3 +[stage-5] [test-1] Running test case: 1 +[stage-5] [test-1] Writing contents to ./test.lox: +[stage-5] [test-1] [test.lox] +[stage-5] [test-1] $ ./your_program.sh run test.lox +[stage-5] [test-1] ✓ 1 line(s) match on stdout +[stage-5] [test-1] ✓ Received exit code 0. +[stage-5] [test-2] Running test case: 2 +[stage-5] [test-2] Writing contents to ./test.lox: +[stage-5] [test-2] [test.lox] +[stage-5] [test-2] $ ./your_program.sh run test.lox +[stage-5] [test-2] ✓ 1 line(s) match on stdout +[stage-5] [test-2] ✓ Received exit code 0. +[stage-5] [test-3] Running test case: 3 +[stage-5] [test-3] Writing contents to ./test.lox: +[stage-5] [test-3] [test.lox] +[stage-5] [test-3] $ ./your_program.sh run test.lox +[stage-5] [test-3] ✓ 1 line(s) match on stdout +[stage-5] [test-3] ✓ Received exit code 0. +[stage-5] [test-4] Running test case: 4 +[stage-5] [test-4] Writing contents to ./test.lox: +[stage-5] [test-4] [test.lox] +[stage-5] [test-4] $ ./your_program.sh run test.lox +[stage-5] [test-4] ✓ 1 line(s) match on stdout +[stage-5] [test-4] ✓ Received exit code 0. +[stage-5] Test passed. + +[stage-4] Running tests for Stage #4: r4 +[stage-4] [test-1] Running test case: 1 +[stage-4] [test-1] Writing contents to ./test.lox: +[stage-4] [test-1] [test.lox] var a = "value"; +[stage-4] [test-1] [test.lox] var a = a; // global scope, so this is allowed +[stage-4] [test-1] [test.lox] print a; // expect: value +[stage-4] [test-1] [test.lox] +[stage-4] [test-1] $ ./your_program.sh run test.lox +[your_program] value +[stage-4] [test-1] ✓ 1 line(s) match on stdout +[stage-4] [test-1] ✓ Received exit code 0. +[stage-4] [test-2] Running test case: 2 +[stage-4] [test-2] Writing contents to ./test.lox: +[stage-4] [test-2] [test.lox] var a = "outer"; +[stage-4] [test-2] [test.lox] { +[stage-4] [test-2] [test.lox] var a = a; // Error at 'a': Can't read local variable in its own initializer. +[stage-4] [test-2] [test.lox] } +[stage-4] [test-2] $ ./your_program.sh run test.lox +[your_program] [line 3] Error at 'a': Can't read local variable in its own initializer. +[stage-4] [test-2] ✓ 1 line(s) match on stdout +[stage-4] [test-2] ✓ Received exit code 65. +[stage-4] [test-3] Running test case: 3 +[stage-4] [test-3] Writing contents to ./test.lox: +[stage-4] [test-3] [test.lox] +[stage-4] [test-3] $ ./your_program.sh run test.lox +[stage-4] [test-3] ✓ 1 line(s) match on stdout +[stage-4] [test-3] ✓ Received exit code 0. +[stage-4] [test-4] Running test case: 4 +[stage-4] [test-4] Writing contents to ./test.lox: +[stage-4] [test-4] [test.lox] +[stage-4] [test-4] $ ./your_program.sh run test.lox +[stage-4] [test-4] ✓ 1 line(s) match on stdout +[stage-4] [test-4] ✓ Received exit code 0. +[stage-4] Test passed. + +[stage-3] Running tests for Stage #3: r5 +[stage-3] [test-1] Running test case: 1 +[stage-3] [test-1] Writing contents to ./test.lox: +[stage-3] [test-1] [test.lox] return "foo"; +[stage-3] [test-1] $ ./your_program.sh run test.lox +[your_program] [line 1] Error at 'return': Can't return from top-level code. +[stage-3] [test-1] ✓ 1 line(s) match on stdout +[stage-3] [test-1] ✓ Received exit code 65. +[stage-3] [test-2] Running test case: 2 +[stage-3] [test-2] Writing contents to ./test.lox: +[stage-3] [test-2] [test.lox] +[stage-3] [test-2] $ ./your_program.sh run test.lox +[stage-3] [test-2] ✓ 1 line(s) match on stdout +[stage-3] [test-2] ✓ Received exit code 0. +[stage-3] [test-3] Running test case: 3 +[stage-3] [test-3] Writing contents to ./test.lox: +[stage-3] [test-3] [test.lox] +[stage-3] [test-3] $ ./your_program.sh run test.lox +[stage-3] [test-3] ✓ 1 line(s) match on stdout +[stage-3] [test-3] ✓ Received exit code 0. +[stage-3] [test-4] Running test case: 4 +[stage-3] [test-4] Writing contents to ./test.lox: +[stage-3] [test-4] [test.lox] +[stage-3] [test-4] $ ./your_program.sh run test.lox +[stage-3] [test-4] ✓ 1 line(s) match on stdout +[stage-3] [test-4] ✓ Received exit code 0. +[stage-3] Test passed. + +[stage-2] Running tests for Stage #2: r6 +[stage-2] [test-1] Running test case: 1 +[stage-2] [test-1] Writing contents to ./test.lox: +[stage-2] [test-1] [test.lox] if (false) { +[stage-2] [test-1] [test.lox] print notDefined; +[stage-2] [test-1] [test.lox] } +[stage-2] [test-1] [test.lox] +[stage-2] [test-1] [test.lox] print "ok"; // expect: ok +[stage-2] [test-1] [test.lox] +[stage-2] [test-1] $ ./your_program.sh run test.lox +[your_program] ok +[stage-2] [test-1] ✓ 1 line(s) match on stdout +[stage-2] [test-1] ✓ Received exit code 0. +[stage-2] [test-2] Running test case: 2 +[stage-2] [test-2] Writing contents to ./test.lox: +[stage-2] [test-2] [test.lox] print a; // expect: compile error +[stage-2] [test-2] [test.lox] var a = "value"; +[stage-2] [test-2] [test.lox] print a; // expect: value +[stage-2] [test-2] [test.lox] +[stage-2] [test-2] $ ./your_program.sh run test.lox +[your_program] Undefined variable 'a'. +[your_program] [line 1] +[stage-2] [test-2] ✓ 1 line(s) match on stdout +[stage-2] [test-2] ✓ Received exit code 70. +[stage-2] [test-3] Running test case: 3 +[stage-2] [test-3] Writing contents to ./test.lox: +[stage-2] [test-3] [test.lox] +[stage-2] [test-3] $ ./your_program.sh run test.lox +[stage-2] [test-3] ✓ 1 line(s) match on stdout +[stage-2] [test-3] ✓ Received exit code 0. +[stage-2] [test-4] Running test case: 4 +[stage-2] [test-4] Writing contents to ./test.lox: +[stage-2] [test-4] [test.lox] +[stage-2] [test-4] $ ./your_program.sh run test.lox +[stage-2] [test-4] ✓ 1 line(s) match on stdout +[stage-2] [test-4] ✓ Received exit code 0. +[stage-2] Test passed. + +[stage-1] Running tests for Stage #1: r7 +[stage-1] [test-1] Running test case: 1 +[stage-1] [test-1] Writing contents to ./test.lox: +[stage-1] [test-1] [test.lox] { +[stage-1] [test-1] [test.lox] var a = "value"; +[stage-1] [test-1] [test.lox] var a = "other"; // Error at 'a': Already a variable with this name in this scope. +[stage-1] [test-1] [test.lox] } +[stage-1] [test-1] [test.lox] +[stage-1] [test-1] $ ./your_program.sh run test.lox +[your_program] [line 3] Error at 'a': Already a variable with this name in this scope. +[stage-1] [test-1] ✓ 1 line(s) match on stdout +[stage-1] [test-1] ✓ Received exit code 65. +[stage-1] [test-2] Running test case: 2 +[stage-1] [test-2] Writing contents to ./test.lox: +[stage-1] [test-2] [test.lox] fun foo(a) { +[stage-1] [test-2] [test.lox] var a; // Error at 'a': Already a variable with this name in this scope. +[stage-1] [test-2] [test.lox] } +[stage-1] [test-2] [test.lox] +[stage-1] [test-2] $ ./your_program.sh run test.lox +[your_program] [line 2] Error at 'a': Already a variable with this name in this scope. +[stage-1] [test-2] ✓ 1 line(s) match on stdout +[stage-1] [test-2] ✓ Received exit code 65. +[stage-1] [test-3] Running test case: 3 +[stage-1] [test-3] Writing contents to ./test.lox: +[stage-1] [test-3] [test.lox] fun foo(arg, +[stage-1] [test-3] [test.lox] arg) { // Error at 'arg': Already a variable with this name in this scope. +[stage-1] [test-3] [test.lox] "body"; +[stage-1] [test-3] [test.lox] } +[stage-1] [test-3] [test.lox] +[stage-1] [test-3] $ ./your_program.sh run test.lox +[your_program] [line 2] Error at 'arg': Already a variable with this name in this scope. +[stage-1] [test-3] ✓ 1 line(s) match on stdout +[stage-1] [test-3] ✓ Received exit code 65. +[stage-1] [test-4] Running test case: 4 +[stage-1] [test-4] Writing contents to ./test.lox: +[stage-1] [test-4] [test.lox] var a = "1"; +[stage-1] [test-4] [test.lox] print a; // expect: 1 +[stage-1] [test-4] [test.lox] var a; +[stage-1] [test-4] [test.lox] print a; // expect: nil +[stage-1] [test-4] [test.lox] +[stage-1] [test-4] [test.lox] var a = "2"; +[stage-1] [test-4] [test.lox] print a; // expect: 2 +[stage-1] [test-4] [test.lox] +[stage-1] [test-4] [test.lox] { +[stage-1] [test-4] [test.lox] var a = "1"; +[stage-1] [test-4] [test.lox] var a = "2"; +[stage-1] [test-4] [test.lox] print a; // expect: compile error +[stage-1] [test-4] [test.lox] } +[stage-1] [test-4] $ ./your_program.sh run test.lox +[your_program] [line 11] Error at 'a': Already a variable with this name in this scope. +[stage-1] [test-4] ✓ 1 line(s) match on stdout +[stage-1] [test-4] ✓ Received exit code 65. +[stage-1] Test passed. diff --git a/internal/test_helpers/fixtures/pass_resolving_final b/internal/test_helpers/fixtures/pass_resolving_final new file mode 100644 index 00000000..c9fa74de --- /dev/null +++ b/internal/test_helpers/fixtures/pass_resolving_final @@ -0,0 +1,306 @@ +Debug = true + +[stage-7] Running tests for Stage #7: r1 +[stage-7] [test-1] Running test case: 1 +[stage-7] [test-1] Writing contents to ./test.lox: +[stage-7] [test-1] [test.lox] var a = "outer"; +[stage-7] [test-1] [test.lox] { +[stage-7] [test-1] [test.lox] fun foo() { +[stage-7] [test-1] [test.lox] print a; +[stage-7] [test-1] [test.lox] } +[stage-7] [test-1] [test.lox] +[stage-7] [test-1] [test.lox] foo(); // expect: outer +[stage-7] [test-1] [test.lox] var a = "inner"; +[stage-7] [test-1] [test.lox] foo(); // expect: outer +[stage-7] [test-1] [test.lox] } +[stage-7] [test-1] $ ./your_program.sh run test.lox +[your_program] outer +[your_program] outer +[stage-7] [test-1] ✓ 2 line(s) match on stdout +[stage-7] [test-1] ✓ Received exit code 0. +[stage-7] [test-2] Running test case: 2 +[stage-7] [test-2] Writing contents to ./test.lox: +[stage-7] [test-2] [test.lox] fun global() { +[stage-7] [test-2] [test.lox] print "global"; +[stage-7] [test-2] [test.lox] } +[stage-7] [test-2] [test.lox] +[stage-7] [test-2] [test.lox] { +[stage-7] [test-2] [test.lox] fun f() { +[stage-7] [test-2] [test.lox] global(); +[stage-7] [test-2] [test.lox] } +[stage-7] [test-2] [test.lox] +[stage-7] [test-2] [test.lox] f(); +[stage-7] [test-2] [test.lox] fun global() { +[stage-7] [test-2] [test.lox] print "local"; +[stage-7] [test-2] [test.lox] } +[stage-7] [test-2] [test.lox] f(); +[stage-7] [test-2] [test.lox] } +[stage-7] [test-2] $ ./your_program.sh run test.lox +[your_program] global +[your_program] global +[stage-7] [test-2] ✓ 2 line(s) match on stdout +[stage-7] [test-2] ✓ Received exit code 0. +[stage-7] [test-3] Running test case: 3 +[stage-7] [test-3] Writing contents to ./test.lox: +[stage-7] [test-3] [test.lox] // Multiple Nested Functions with Shadowing +[stage-7] [test-3] [test.lox] var x = "global"; +[stage-7] [test-3] [test.lox] +[stage-7] [test-3] [test.lox] fun outer() { +[stage-7] [test-3] [test.lox] var x = "outer"; +[stage-7] [test-3] [test.lox] +[stage-7] [test-3] [test.lox] fun middle() { +[stage-7] [test-3] [test.lox] fun inner() { +[stage-7] [test-3] [test.lox] print x; // Should capture "outer", not "global" or "inner" +[stage-7] [test-3] [test.lox] } +[stage-7] [test-3] [test.lox] +[stage-7] [test-3] [test.lox] inner(); +[stage-7] [test-3] [test.lox] var x = "middle"; +[stage-7] [test-3] [test.lox] inner(); // Should still print "outer" +[stage-7] [test-3] [test.lox] } +[stage-7] [test-3] [test.lox] +[stage-7] [test-3] [test.lox] middle(); +[stage-7] [test-3] [test.lox] } +[stage-7] [test-3] [test.lox] +[stage-7] [test-3] [test.lox] outer(); +[stage-7] [test-3] $ ./your_program.sh run test.lox +[your_program] outer +[your_program] outer +[stage-7] [test-3] ✓ 2 line(s) match on stdout +[stage-7] [test-3] ✓ Received exit code 0. +[stage-7] [test-4] Running test case: 4 +[stage-7] [test-4] Writing contents to ./test.lox: +[stage-7] [test-4] [test.lox] // Function Returning a Function +[stage-7] [test-4] [test.lox] var count = 0; +[stage-7] [test-4] [test.lox] +[stage-7] [test-4] [test.lox] { +[stage-7] [test-4] [test.lox] fun makeCounter() { +[stage-7] [test-4] [test.lox] fun counter() { +[stage-7] [test-4] [test.lox] count = count + 1; +[stage-7] [test-4] [test.lox] print count; +[stage-7] [test-4] [test.lox] } +[stage-7] [test-4] [test.lox] return counter; +[stage-7] [test-4] [test.lox] } +[stage-7] [test-4] [test.lox] +[stage-7] [test-4] [test.lox] var counter1 = makeCounter(); +[stage-7] [test-4] [test.lox] counter1(); // Should print 1 +[stage-7] [test-4] [test.lox] counter1(); // Should print 2 +[stage-7] [test-4] [test.lox] +[stage-7] [test-4] [test.lox] var count = 0; +[stage-7] [test-4] [test.lox] counter1(); // Should print 3 +[stage-7] [test-4] [test.lox] } +[stage-7] [test-4] $ ./your_program.sh run test.lox +[your_program] 1 +[your_program] 2 +[your_program] 3 +[stage-7] [test-4] ✓ 3 line(s) match on stdout +[stage-7] [test-4] ✓ Received exit code 0. +[stage-7] Test passed. + +[stage-6] Running tests for Stage #6: r2 +[stage-6] [test-1] Running test case: 1 +[stage-6] [test-1] Writing contents to ./test.lox: +[stage-6] [test-1] [test.lox] +[stage-6] [test-1] $ ./your_program.sh run test.lox +[stage-6] [test-1] ✓ 1 line(s) match on stdout +[stage-6] [test-1] ✓ Received exit code 0. +[stage-6] [test-2] Running test case: 2 +[stage-6] [test-2] Writing contents to ./test.lox: +[stage-6] [test-2] [test.lox] +[stage-6] [test-2] $ ./your_program.sh run test.lox +[stage-6] [test-2] ✓ 1 line(s) match on stdout +[stage-6] [test-2] ✓ Received exit code 0. +[stage-6] [test-3] Running test case: 3 +[stage-6] [test-3] Writing contents to ./test.lox: +[stage-6] [test-3] [test.lox] +[stage-6] [test-3] $ ./your_program.sh run test.lox +[stage-6] [test-3] ✓ 1 line(s) match on stdout +[stage-6] [test-3] ✓ Received exit code 0. +[stage-6] [test-4] Running test case: 4 +[stage-6] [test-4] Writing contents to ./test.lox: +[stage-6] [test-4] [test.lox] +[stage-6] [test-4] $ ./your_program.sh run test.lox +[stage-6] [test-4] ✓ 1 line(s) match on stdout +[stage-6] [test-4] ✓ Received exit code 0. +[stage-6] Test passed. + +[stage-5] Running tests for Stage #5: r3 +[stage-5] [test-1] Running test case: 1 +[stage-5] [test-1] Writing contents to ./test.lox: +[stage-5] [test-1] [test.lox] +[stage-5] [test-1] $ ./your_program.sh run test.lox +[stage-5] [test-1] ✓ 1 line(s) match on stdout +[stage-5] [test-1] ✓ Received exit code 0. +[stage-5] [test-2] Running test case: 2 +[stage-5] [test-2] Writing contents to ./test.lox: +[stage-5] [test-2] [test.lox] +[stage-5] [test-2] $ ./your_program.sh run test.lox +[stage-5] [test-2] ✓ 1 line(s) match on stdout +[stage-5] [test-2] ✓ Received exit code 0. +[stage-5] [test-3] Running test case: 3 +[stage-5] [test-3] Writing contents to ./test.lox: +[stage-5] [test-3] [test.lox] +[stage-5] [test-3] $ ./your_program.sh run test.lox +[stage-5] [test-3] ✓ 1 line(s) match on stdout +[stage-5] [test-3] ✓ Received exit code 0. +[stage-5] [test-4] Running test case: 4 +[stage-5] [test-4] Writing contents to ./test.lox: +[stage-5] [test-4] [test.lox] +[stage-5] [test-4] $ ./your_program.sh run test.lox +[stage-5] [test-4] ✓ 1 line(s) match on stdout +[stage-5] [test-4] ✓ Received exit code 0. +[stage-5] Test passed. + +[stage-4] Running tests for Stage #4: r4 +[stage-4] [test-1] Running test case: 1 +[stage-4] [test-1] Writing contents to ./test.lox: +[stage-4] [test-1] [test.lox] var a = "value"; +[stage-4] [test-1] [test.lox] var a = a; // global scope, so this is allowed +[stage-4] [test-1] [test.lox] print a; // expect: value +[stage-4] [test-1] [test.lox] +[stage-4] [test-1] $ ./your_program.sh run test.lox +[your_program] value +[stage-4] [test-1] ✓ 1 line(s) match on stdout +[stage-4] [test-1] ✓ Received exit code 0. +[stage-4] [test-2] Running test case: 2 +[stage-4] [test-2] Writing contents to ./test.lox: +[stage-4] [test-2] [test.lox] var a = "outer"; +[stage-4] [test-2] [test.lox] { +[stage-4] [test-2] [test.lox] var a = a; // Error at 'a': Can't read local variable in its own initializer. +[stage-4] [test-2] [test.lox] } +[stage-4] [test-2] $ ./your_program.sh run test.lox +[your_program] [line 3] Error at 'a': Can't read local variable in its own initializer. +[stage-4] [test-2] ✓ 1 line(s) match on stdout +[stage-4] [test-2] ✓ Received exit code 65. +[stage-4] [test-3] Running test case: 3 +[stage-4] [test-3] Writing contents to ./test.lox: +[stage-4] [test-3] [test.lox] +[stage-4] [test-3] $ ./your_program.sh run test.lox +[stage-4] [test-3] ✓ 1 line(s) match on stdout +[stage-4] [test-3] ✓ Received exit code 0. +[stage-4] [test-4] Running test case: 4 +[stage-4] [test-4] Writing contents to ./test.lox: +[stage-4] [test-4] [test.lox] +[stage-4] [test-4] $ ./your_program.sh run test.lox +[stage-4] [test-4] ✓ 1 line(s) match on stdout +[stage-4] [test-4] ✓ Received exit code 0. +[stage-4] Test passed. + +[stage-3] Running tests for Stage #3: r5 +[stage-3] [test-1] Running test case: 1 +[stage-3] [test-1] Writing contents to ./test.lox: +[stage-3] [test-1] [test.lox] return "foo"; +[stage-3] [test-1] $ ./your_program.sh run test.lox +[your_program] [line 1] Error at 'return': Can't return from top-level code. +[stage-3] [test-1] ✓ 1 line(s) match on stdout +[stage-3] [test-1] ✓ Received exit code 65. +[stage-3] [test-2] Running test case: 2 +[stage-3] [test-2] Writing contents to ./test.lox: +[stage-3] [test-2] [test.lox] +[stage-3] [test-2] $ ./your_program.sh run test.lox +[stage-3] [test-2] ✓ 1 line(s) match on stdout +[stage-3] [test-2] ✓ Received exit code 0. +[stage-3] [test-3] Running test case: 3 +[stage-3] [test-3] Writing contents to ./test.lox: +[stage-3] [test-3] [test.lox] +[stage-3] [test-3] $ ./your_program.sh run test.lox +[stage-3] [test-3] ✓ 1 line(s) match on stdout +[stage-3] [test-3] ✓ Received exit code 0. +[stage-3] [test-4] Running test case: 4 +[stage-3] [test-4] Writing contents to ./test.lox: +[stage-3] [test-4] [test.lox] +[stage-3] [test-4] $ ./your_program.sh run test.lox +[stage-3] [test-4] ✓ 1 line(s) match on stdout +[stage-3] [test-4] ✓ Received exit code 0. +[stage-3] Test passed. + +[stage-2] Running tests for Stage #2: r6 +[stage-2] [test-1] Running test case: 1 +[stage-2] [test-1] Writing contents to ./test.lox: +[stage-2] [test-1] [test.lox] if (false) { +[stage-2] [test-1] [test.lox] print notDefined; +[stage-2] [test-1] [test.lox] } +[stage-2] [test-1] [test.lox] +[stage-2] [test-1] [test.lox] print "ok"; // expect: ok +[stage-2] [test-1] [test.lox] +[stage-2] [test-1] $ ./your_program.sh run test.lox +[your_program] ok +[stage-2] [test-1] ✓ 1 line(s) match on stdout +[stage-2] [test-1] ✓ Received exit code 0. +[stage-2] [test-2] Running test case: 2 +[stage-2] [test-2] Writing contents to ./test.lox: +[stage-2] [test-2] [test.lox] print a; // expect: compile error +[stage-2] [test-2] [test.lox] var a = "value"; +[stage-2] [test-2] [test.lox] print a; // expect: value +[stage-2] [test-2] [test.lox] +[stage-2] [test-2] $ ./your_program.sh run test.lox +[your_program] Undefined variable 'a'. +[your_program] [line 1] +[stage-2] [test-2] ✓ 1 line(s) match on stdout +[stage-2] [test-2] ✓ Received exit code 70. +[stage-2] [test-3] Running test case: 3 +[stage-2] [test-3] Writing contents to ./test.lox: +[stage-2] [test-3] [test.lox] +[stage-2] [test-3] $ ./your_program.sh run test.lox +[stage-2] [test-3] ✓ 1 line(s) match on stdout +[stage-2] [test-3] ✓ Received exit code 0. +[stage-2] [test-4] Running test case: 4 +[stage-2] [test-4] Writing contents to ./test.lox: +[stage-2] [test-4] [test.lox] +[stage-2] [test-4] $ ./your_program.sh run test.lox +[stage-2] [test-4] ✓ 1 line(s) match on stdout +[stage-2] [test-4] ✓ Received exit code 0. +[stage-2] Test passed. + +[stage-1] Running tests for Stage #1: r7 +[stage-1] [test-1] Running test case: 1 +[stage-1] [test-1] Writing contents to ./test.lox: +[stage-1] [test-1] [test.lox] { +[stage-1] [test-1] [test.lox] var a = "value"; +[stage-1] [test-1] [test.lox] var a = "other"; // Error at 'a': Already a variable with this name in this scope. +[stage-1] [test-1] [test.lox] } +[stage-1] [test-1] [test.lox] +[stage-1] [test-1] $ ./your_program.sh run test.lox +[your_program] [line 3] Error at 'a': Already a variable with this name in this scope. +[stage-1] [test-1] ✓ 1 line(s) match on stdout +[stage-1] [test-1] ✓ Received exit code 65. +[stage-1] [test-2] Running test case: 2 +[stage-1] [test-2] Writing contents to ./test.lox: +[stage-1] [test-2] [test.lox] fun foo(a) { +[stage-1] [test-2] [test.lox] var a; // Error at 'a': Already a variable with this name in this scope. +[stage-1] [test-2] [test.lox] } +[stage-1] [test-2] [test.lox] +[stage-1] [test-2] $ ./your_program.sh run test.lox +[your_program] [line 2] Error at 'a': Already a variable with this name in this scope. +[stage-1] [test-2] ✓ 1 line(s) match on stdout +[stage-1] [test-2] ✓ Received exit code 65. +[stage-1] [test-3] Running test case: 3 +[stage-1] [test-3] Writing contents to ./test.lox: +[stage-1] [test-3] [test.lox] fun foo(arg, +[stage-1] [test-3] [test.lox] arg) { // Error at 'arg': Already a variable with this name in this scope. +[stage-1] [test-3] [test.lox] "body"; +[stage-1] [test-3] [test.lox] } +[stage-1] [test-3] [test.lox] +[stage-1] [test-3] $ ./your_program.sh run test.lox +[your_program] [line 2] Error at 'arg': Already a variable with this name in this scope. +[stage-1] [test-3] ✓ 1 line(s) match on stdout +[stage-1] [test-3] ✓ Received exit code 65. +[stage-1] [test-4] Running test case: 4 +[stage-1] [test-4] Writing contents to ./test.lox: +[stage-1] [test-4] [test.lox] var a = "1"; +[stage-1] [test-4] [test.lox] print a; // expect: 1 +[stage-1] [test-4] [test.lox] var a; +[stage-1] [test-4] [test.lox] print a; // expect: nil +[stage-1] [test-4] [test.lox] +[stage-1] [test-4] [test.lox] var a = "2"; +[stage-1] [test-4] [test.lox] print a; // expect: 2 +[stage-1] [test-4] [test.lox] +[stage-1] [test-4] [test.lox] { +[stage-1] [test-4] [test.lox] var a = "1"; +[stage-1] [test-4] [test.lox] var a = "2"; +[stage-1] [test-4] [test.lox] print a; // expect: compile error +[stage-1] [test-4] [test.lox] } +[stage-1] [test-4] $ ./your_program.sh run test.lox +[your_program] [line 11] Error at 'a': Already a variable with this name in this scope. +[stage-1] [test-4] ✓ 1 line(s) match on stdout +[stage-1] [test-4] ✓ Received exit code 65. +[stage-1] Test passed. diff --git a/test_programs/f9/2.lox b/test_programs/f9/2.lox index 53b3fcd9..a0dbeb5e 100644 --- a/test_programs/f9/2.lox +++ b/test_programs/f9/2.lox @@ -18,5 +18,5 @@ expected_error_type: none return isEven(n - 1); } - print isEven(<>); + print isEven(75); } \ No newline at end of file