From 5a37de8ab0dd30949a794d5b93255a4b0e621bd6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 6 Sep 2026 13:31:46 +0000 Subject: [PATCH] fix: unwrap return object at the call expression boundary evaluate_call_expression returned evaluate_block's result verbatim, so a function whose body used an explicit return handed an Object::Return back to its caller instead of the returned value. The top level evaluate unwraps, which hid the bug for a call in statement position, but one level in it surfaced as a spurious error: let a = fn() { return 5; }(); -> cannot assign an object of type RETURN fn() { return 5; }() + 1; -> the + operator is not supported for RETURN n * factorial(n - 1) -> type mismatch, INTEGER and RETURN A return only unwinds as far as the function it is in, so the wrapper is now stripped when the call completes. Propagation out of nested blocks and out of a top level return is unchanged. Adds call expression cases 9 to 14 covering assignment, infix operands, recursion, a returned function, an early return and string operands, plus an early_return.mk code sample, since the existing factorial.mk only uses implicit returns. All 14 new evaluation and environment assertions fail without the fix. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PYeGs22hkAMgsogBEd5Ced --- src/evaluator/expression/call/mod.rs | 8 +- src/tests/call_expression/mod.rs | 18 ++ ...t_call_expression_case_10_environment.snap | 10 ++ ...st_call_expression_case_10_evaluation.snap | 7 + ...l_expression_case_10_lexical_analysis.snap | 23 +++ ...ll_expression_case_10_syntax_analysis.snap | 31 ++++ ...t_call_expression_case_11_environment.snap | 71 ++++++++ ...st_call_expression_case_11_evaluation.snap | 7 + ...l_expression_case_11_lexical_analysis.snap | 64 +++++++ ...ll_expression_case_11_syntax_analysis.snap | 83 +++++++++ ...t_call_expression_case_12_environment.snap | 36 ++++ ...st_call_expression_case_12_evaluation.snap | 7 + ...l_expression_case_12_lexical_analysis.snap | 37 ++++ ...ll_expression_case_12_syntax_analysis.snap | 47 +++++ ...t_call_expression_case_13_environment.snap | 50 ++++++ ...st_call_expression_case_13_evaluation.snap | 7 + ...l_expression_case_13_lexical_analysis.snap | 59 +++++++ ...ll_expression_case_13_syntax_analysis.snap | 75 ++++++++ ...t_call_expression_case_14_environment.snap | 52 ++++++ ...st_call_expression_case_14_evaluation.snap | 7 + ...l_expression_case_14_lexical_analysis.snap | 64 +++++++ ...ll_expression_case_14_syntax_analysis.snap | 83 +++++++++ ...st_call_expression_case_9_environment.snap | 14 ++ ...est_call_expression_case_9_evaluation.snap | 7 + ...ll_expression_case_9_lexical_analysis.snap | 28 +++ ...all_expression_case_9_syntax_analysis.snap | 33 ++++ src/tests/code_samples/early_return.mk | 17 ++ src/tests/code_samples/mod.rs | 2 +- ...amples__test_early_return_environment.snap | 111 ++++++++++++ ...samples__test_early_return_evaluation.snap | 7 + ...s__test_early_return_lexical_analysis.snap | 126 ++++++++++++++ ...es__test_early_return_syntax_analysis.snap | 161 ++++++++++++++++++ 32 files changed, 1350 insertions(+), 2 deletions(-) create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_environment.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_evaluation.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_lexical_analysis.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_syntax_analysis.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_environment.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_evaluation.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_lexical_analysis.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_syntax_analysis.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_environment.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_evaluation.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_lexical_analysis.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_syntax_analysis.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_environment.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_evaluation.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_lexical_analysis.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_syntax_analysis.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_environment.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_evaluation.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_lexical_analysis.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_syntax_analysis.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_environment.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_evaluation.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_lexical_analysis.snap create mode 100644 src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_syntax_analysis.snap create mode 100644 src/tests/code_samples/early_return.mk create mode 100644 src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_environment.snap create mode 100644 src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_evaluation.snap create mode 100644 src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_lexical_analysis.snap create mode 100644 src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_syntax_analysis.snap diff --git a/src/evaluator/expression/call/mod.rs b/src/evaluator/expression/call/mod.rs index 8ee675f7..1faa065e 100644 --- a/src/evaluator/expression/call/mod.rs +++ b/src/evaluator/expression/call/mod.rs @@ -29,7 +29,13 @@ impl Evaluator { let block_call_evaluation = self.evaluate_block(block)?; self.environment.pop(); - Ok(block_call_evaluation) + + // A return only unwinds as far as the function it is in, unwrap + // it here so it does not leak into the caller as a value. + Ok(match block_call_evaluation { + Object::Return { value } => *value, + object => object, + }) } object => anyhow::bail!( "Cannot call an object of type {}, only functions are callable.", diff --git a/src/tests/call_expression/mod.rs b/src/tests/call_expression/mod.rs index a4acb216..58a9a959 100644 --- a/src/tests/call_expression/mod.rs +++ b/src/tests/call_expression/mod.rs @@ -20,6 +20,24 @@ use super::*; case( "fn(x) { fn(y) { fn(z) { z * 2 } } }(1)(2)(3);", "call_expression_case_8" + ), + case("let a = fn() { return 5; }(); a;", "call_expression_case_9"), + case("fn() { return 5; }() + 1;", "call_expression_case_10"), + case( + "let factorial = fn(n) { if (n < 2) { return 1; } return n * factorial(n - 1); }; factorial(5);", + "call_expression_case_11" + ), + case( + "let outer = fn() { return fn() { return 7; }; }; outer()();", + "call_expression_case_12" + ), + case( + "let early = fn(x) { if (x > 10) { return x; } 0; }; early(20) + early(1);", + "call_expression_case_13" + ), + case( + "let describe = fn(x) { if (x > 0) { return \"positive\"; } return \"negative\"; }; describe(1) + describe(0 - 1);", + "call_expression_case_14" ) )] fn call_expression_cases(code: &str, snapshot_name: &str) {} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_environment.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_environment.snap new file mode 100644 index 00000000..1a334a01 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_environment.snap @@ -0,0 +1,10 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluator +--- +Evaluator { + environment: Environment { + variables: {}, + sub_environment: None, + }, +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_evaluation.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_evaluation.snap new file mode 100644 index 00000000..91956cad --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_evaluation.snap @@ -0,0 +1,7 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluation +--- +Integer { + value: 6, +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_lexical_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_lexical_analysis.snap new file mode 100644 index 00000000..f8f4eabc --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_lexical_analysis.snap @@ -0,0 +1,23 @@ +--- +source: src/tests/call_expression/mod.rs +expression: tokens +--- +[ + Function, + OpeningRoundBracket, + ClosingRoundBracket, + OpeningCurlyBracket, + Return, + Integer { + literal: 5, + }, + SemiColon, + ClosingCurlyBracket, + OpeningRoundBracket, + ClosingRoundBracket, + Plus, + Integer { + literal: 1, + }, + SemiColon, +] diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_syntax_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_syntax_analysis.snap new file mode 100644 index 00000000..2089bdc1 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_10_syntax_analysis.snap @@ -0,0 +1,31 @@ +--- +source: src/tests/call_expression/mod.rs +expression: abstract_syntax_tree +--- +[ + Expression { + expression: Infix { + left_hand: Call { + function: Function { + parameters: [], + block: Block { + nodes: [ + Statement { + statement: Return { + expression: Integer { + literal: 5, + }, + }, + }, + ], + }, + }, + arguments: [], + }, + operator: Plus, + right_hand: Integer { + literal: 1, + }, + }, + }, +] diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_environment.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_environment.snap new file mode 100644 index 00000000..05924581 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_environment.snap @@ -0,0 +1,71 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluator +--- +Evaluator { + environment: Environment { + variables: { + "factorial": Function { + parameters: [ + "n", + ], + block: Block { + nodes: [ + Expression { + expression: If { + condition: Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: LesserThan, + right_hand: Integer { + literal: 2, + }, + }, + consequence: Block { + nodes: [ + Statement { + statement: Return { + expression: Integer { + literal: 1, + }, + }, + }, + ], + }, + alternative: None, + }, + }, + Statement { + statement: Return { + expression: Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: Multiply, + right_hand: Call { + function: Identifier { + identifier: "factorial", + }, + arguments: [ + Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: Minus, + right_hand: Integer { + literal: 1, + }, + }, + ], + }, + }, + }, + }, + ], + }, + }, + }, + sub_environment: None, + }, +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_evaluation.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_evaluation.snap new file mode 100644 index 00000000..2914b351 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_evaluation.snap @@ -0,0 +1,7 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluation +--- +Integer { + value: 120, +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_lexical_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_lexical_analysis.snap new file mode 100644 index 00000000..1e9be8aa --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_lexical_analysis.snap @@ -0,0 +1,64 @@ +--- +source: src/tests/call_expression/mod.rs +expression: tokens +--- +[ + Let, + Identifier { + literal: "factorial", + }, + Assign, + Function, + OpeningRoundBracket, + Identifier { + literal: "n", + }, + ClosingRoundBracket, + OpeningCurlyBracket, + If, + OpeningRoundBracket, + Identifier { + literal: "n", + }, + LesserThan, + Integer { + literal: 2, + }, + ClosingRoundBracket, + OpeningCurlyBracket, + Return, + Integer { + literal: 1, + }, + SemiColon, + ClosingCurlyBracket, + Return, + Identifier { + literal: "n", + }, + Multiply, + Identifier { + literal: "factorial", + }, + OpeningRoundBracket, + Identifier { + literal: "n", + }, + Minus, + Integer { + literal: 1, + }, + ClosingRoundBracket, + SemiColon, + ClosingCurlyBracket, + SemiColon, + Identifier { + literal: "factorial", + }, + OpeningRoundBracket, + Integer { + literal: 5, + }, + ClosingRoundBracket, + SemiColon, +] diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_syntax_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_syntax_analysis.snap new file mode 100644 index 00000000..3a61bb6b --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_11_syntax_analysis.snap @@ -0,0 +1,83 @@ +--- +source: src/tests/call_expression/mod.rs +expression: abstract_syntax_tree +--- +[ + Statement { + statement: Let { + identifier: "factorial", + expression: Function { + parameters: [ + "n", + ], + block: Block { + nodes: [ + Expression { + expression: If { + condition: Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: LesserThan, + right_hand: Integer { + literal: 2, + }, + }, + consequence: Block { + nodes: [ + Statement { + statement: Return { + expression: Integer { + literal: 1, + }, + }, + }, + ], + }, + alternative: None, + }, + }, + Statement { + statement: Return { + expression: Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: Multiply, + right_hand: Call { + function: Identifier { + identifier: "factorial", + }, + arguments: [ + Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: Minus, + right_hand: Integer { + literal: 1, + }, + }, + ], + }, + }, + }, + }, + ], + }, + }, + }, + }, + Expression { + expression: Call { + function: Identifier { + identifier: "factorial", + }, + arguments: [ + Integer { + literal: 5, + }, + ], + }, + }, +] diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_environment.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_environment.snap new file mode 100644 index 00000000..b982df76 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_environment.snap @@ -0,0 +1,36 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluator +--- +Evaluator { + environment: Environment { + variables: { + "outer": Function { + parameters: [], + block: Block { + nodes: [ + Statement { + statement: Return { + expression: Function { + parameters: [], + block: Block { + nodes: [ + Statement { + statement: Return { + expression: Integer { + literal: 7, + }, + }, + }, + ], + }, + }, + }, + }, + ], + }, + }, + }, + sub_environment: None, + }, +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_evaluation.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_evaluation.snap new file mode 100644 index 00000000..7a4e7b27 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_evaluation.snap @@ -0,0 +1,7 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluation +--- +Integer { + value: 7, +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_lexical_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_lexical_analysis.snap new file mode 100644 index 00000000..5fd2db65 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_lexical_analysis.snap @@ -0,0 +1,37 @@ +--- +source: src/tests/call_expression/mod.rs +expression: tokens +--- +[ + Let, + Identifier { + literal: "outer", + }, + Assign, + Function, + OpeningRoundBracket, + ClosingRoundBracket, + OpeningCurlyBracket, + Return, + Function, + OpeningRoundBracket, + ClosingRoundBracket, + OpeningCurlyBracket, + Return, + Integer { + literal: 7, + }, + SemiColon, + ClosingCurlyBracket, + SemiColon, + ClosingCurlyBracket, + SemiColon, + Identifier { + literal: "outer", + }, + OpeningRoundBracket, + ClosingRoundBracket, + OpeningRoundBracket, + ClosingRoundBracket, + SemiColon, +] diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_syntax_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_syntax_analysis.snap new file mode 100644 index 00000000..e060ee03 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_12_syntax_analysis.snap @@ -0,0 +1,47 @@ +--- +source: src/tests/call_expression/mod.rs +expression: abstract_syntax_tree +--- +[ + Statement { + statement: Let { + identifier: "outer", + expression: Function { + parameters: [], + block: Block { + nodes: [ + Statement { + statement: Return { + expression: Function { + parameters: [], + block: Block { + nodes: [ + Statement { + statement: Return { + expression: Integer { + literal: 7, + }, + }, + }, + ], + }, + }, + }, + }, + ], + }, + }, + }, + }, + Expression { + expression: Call { + function: Call { + function: Identifier { + identifier: "outer", + }, + arguments: [], + }, + arguments: [], + }, + }, +] diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_environment.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_environment.snap new file mode 100644 index 00000000..7e261ec2 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_environment.snap @@ -0,0 +1,50 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluator +--- +Evaluator { + environment: Environment { + variables: { + "early": Function { + parameters: [ + "x", + ], + block: Block { + nodes: [ + Expression { + expression: If { + condition: Infix { + left_hand: Identifier { + identifier: "x", + }, + operator: GreaterThan, + right_hand: Integer { + literal: 10, + }, + }, + consequence: Block { + nodes: [ + Statement { + statement: Return { + expression: Identifier { + identifier: "x", + }, + }, + }, + ], + }, + alternative: None, + }, + }, + Expression { + expression: Integer { + literal: 0, + }, + }, + ], + }, + }, + }, + sub_environment: None, + }, +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_evaluation.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_evaluation.snap new file mode 100644 index 00000000..82d98886 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_evaluation.snap @@ -0,0 +1,7 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluation +--- +Integer { + value: 20, +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_lexical_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_lexical_analysis.snap new file mode 100644 index 00000000..469658ba --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_lexical_analysis.snap @@ -0,0 +1,59 @@ +--- +source: src/tests/call_expression/mod.rs +expression: tokens +--- +[ + Let, + Identifier { + literal: "early", + }, + Assign, + Function, + OpeningRoundBracket, + Identifier { + literal: "x", + }, + ClosingRoundBracket, + OpeningCurlyBracket, + If, + OpeningRoundBracket, + Identifier { + literal: "x", + }, + GreaterThan, + Integer { + literal: 10, + }, + ClosingRoundBracket, + OpeningCurlyBracket, + Return, + Identifier { + literal: "x", + }, + SemiColon, + ClosingCurlyBracket, + Integer { + literal: 0, + }, + SemiColon, + ClosingCurlyBracket, + SemiColon, + Identifier { + literal: "early", + }, + OpeningRoundBracket, + Integer { + literal: 20, + }, + ClosingRoundBracket, + Plus, + Identifier { + literal: "early", + }, + OpeningRoundBracket, + Integer { + literal: 1, + }, + ClosingRoundBracket, + SemiColon, +] diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_syntax_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_syntax_analysis.snap new file mode 100644 index 00000000..db27923f --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_13_syntax_analysis.snap @@ -0,0 +1,75 @@ +--- +source: src/tests/call_expression/mod.rs +expression: abstract_syntax_tree +--- +[ + Statement { + statement: Let { + identifier: "early", + expression: Function { + parameters: [ + "x", + ], + block: Block { + nodes: [ + Expression { + expression: If { + condition: Infix { + left_hand: Identifier { + identifier: "x", + }, + operator: GreaterThan, + right_hand: Integer { + literal: 10, + }, + }, + consequence: Block { + nodes: [ + Statement { + statement: Return { + expression: Identifier { + identifier: "x", + }, + }, + }, + ], + }, + alternative: None, + }, + }, + Expression { + expression: Integer { + literal: 0, + }, + }, + ], + }, + }, + }, + }, + Expression { + expression: Infix { + left_hand: Call { + function: Identifier { + identifier: "early", + }, + arguments: [ + Integer { + literal: 20, + }, + ], + }, + operator: Plus, + right_hand: Call { + function: Identifier { + identifier: "early", + }, + arguments: [ + Integer { + literal: 1, + }, + ], + }, + }, + }, +] diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_environment.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_environment.snap new file mode 100644 index 00000000..308b8cea --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_environment.snap @@ -0,0 +1,52 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluator +--- +Evaluator { + environment: Environment { + variables: { + "describe": Function { + parameters: [ + "x", + ], + block: Block { + nodes: [ + Expression { + expression: If { + condition: Infix { + left_hand: Identifier { + identifier: "x", + }, + operator: GreaterThan, + right_hand: Integer { + literal: 0, + }, + }, + consequence: Block { + nodes: [ + Statement { + statement: Return { + expression: String { + literal: "positive", + }, + }, + }, + ], + }, + alternative: None, + }, + }, + Statement { + statement: Return { + expression: String { + literal: "negative", + }, + }, + }, + ], + }, + }, + }, + sub_environment: None, + }, +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_evaluation.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_evaluation.snap new file mode 100644 index 00000000..001eb1a0 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_evaluation.snap @@ -0,0 +1,7 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluation +--- +String { + value: "positivenegative", +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_lexical_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_lexical_analysis.snap new file mode 100644 index 00000000..9483672f --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_lexical_analysis.snap @@ -0,0 +1,64 @@ +--- +source: src/tests/call_expression/mod.rs +expression: tokens +--- +[ + Let, + Identifier { + literal: "describe", + }, + Assign, + Function, + OpeningRoundBracket, + Identifier { + literal: "x", + }, + ClosingRoundBracket, + OpeningCurlyBracket, + If, + OpeningRoundBracket, + Identifier { + literal: "x", + }, + GreaterThan, + Integer { + literal: 0, + }, + ClosingRoundBracket, + OpeningCurlyBracket, + Return, + String { + literal: "positive", + }, + SemiColon, + ClosingCurlyBracket, + Return, + String { + literal: "negative", + }, + SemiColon, + ClosingCurlyBracket, + SemiColon, + Identifier { + literal: "describe", + }, + OpeningRoundBracket, + Integer { + literal: 1, + }, + ClosingRoundBracket, + Plus, + Identifier { + literal: "describe", + }, + OpeningRoundBracket, + Integer { + literal: 0, + }, + Minus, + Integer { + literal: 1, + }, + ClosingRoundBracket, + SemiColon, +] diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_syntax_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_syntax_analysis.snap new file mode 100644 index 00000000..f9938071 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_14_syntax_analysis.snap @@ -0,0 +1,83 @@ +--- +source: src/tests/call_expression/mod.rs +expression: abstract_syntax_tree +--- +[ + Statement { + statement: Let { + identifier: "describe", + expression: Function { + parameters: [ + "x", + ], + block: Block { + nodes: [ + Expression { + expression: If { + condition: Infix { + left_hand: Identifier { + identifier: "x", + }, + operator: GreaterThan, + right_hand: Integer { + literal: 0, + }, + }, + consequence: Block { + nodes: [ + Statement { + statement: Return { + expression: String { + literal: "positive", + }, + }, + }, + ], + }, + alternative: None, + }, + }, + Statement { + statement: Return { + expression: String { + literal: "negative", + }, + }, + }, + ], + }, + }, + }, + }, + Expression { + expression: Infix { + left_hand: Call { + function: Identifier { + identifier: "describe", + }, + arguments: [ + Integer { + literal: 1, + }, + ], + }, + operator: Plus, + right_hand: Call { + function: Identifier { + identifier: "describe", + }, + arguments: [ + Infix { + left_hand: Integer { + literal: 0, + }, + operator: Minus, + right_hand: Integer { + literal: 1, + }, + }, + ], + }, + }, + }, +] diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_environment.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_environment.snap new file mode 100644 index 00000000..e95faa3b --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_environment.snap @@ -0,0 +1,14 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluator +--- +Evaluator { + environment: Environment { + variables: { + "a": Integer { + value: 5, + }, + }, + sub_environment: None, + }, +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_evaluation.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_evaluation.snap new file mode 100644 index 00000000..36be9e65 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_evaluation.snap @@ -0,0 +1,7 @@ +--- +source: src/tests/call_expression/mod.rs +expression: evaluation +--- +Integer { + value: 5, +} diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_lexical_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_lexical_analysis.snap new file mode 100644 index 00000000..09542ff9 --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_lexical_analysis.snap @@ -0,0 +1,28 @@ +--- +source: src/tests/call_expression/mod.rs +expression: tokens +--- +[ + Let, + Identifier { + literal: "a", + }, + Assign, + Function, + OpeningRoundBracket, + ClosingRoundBracket, + OpeningCurlyBracket, + Return, + Integer { + literal: 5, + }, + SemiColon, + ClosingCurlyBracket, + OpeningRoundBracket, + ClosingRoundBracket, + SemiColon, + Identifier { + literal: "a", + }, + SemiColon, +] diff --git a/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_syntax_analysis.snap b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_syntax_analysis.snap new file mode 100644 index 00000000..09aba9da --- /dev/null +++ b/src/tests/call_expression/snapshots/monkey_interpreter__tests__call_expression__test_call_expression_case_9_syntax_analysis.snap @@ -0,0 +1,33 @@ +--- +source: src/tests/call_expression/mod.rs +expression: abstract_syntax_tree +--- +[ + Statement { + statement: Let { + identifier: "a", + expression: Call { + function: Function { + parameters: [], + block: Block { + nodes: [ + Statement { + statement: Return { + expression: Integer { + literal: 5, + }, + }, + }, + ], + }, + }, + arguments: [], + }, + }, + }, + Expression { + expression: Identifier { + identifier: "a", + }, + }, +] diff --git a/src/tests/code_samples/early_return.mk b/src/tests/code_samples/early_return.mk new file mode 100644 index 00000000..6a3569a0 --- /dev/null +++ b/src/tests/code_samples/early_return.mk @@ -0,0 +1,17 @@ +let factorial = fn(n) { + if (n < 2) { + return 1; + } + + return n * factorial(n - 1); +} + +let clamp = fn(n, limit) { + if (n > limit) { + return limit; + } + + n +} + +clamp(factorial(5), 100) + clamp(factorial(3), 100) diff --git a/src/tests/code_samples/mod.rs b/src/tests/code_samples/mod.rs index d4ccf67f..729249d1 100644 --- a/src/tests/code_samples/mod.rs +++ b/src/tests/code_samples/mod.rs @@ -1,7 +1,7 @@ use super::*; #[template] -#[rstest(name, case("factorial"))] +#[rstest(name, case("factorial"), case("early_return"))] fn code_samples(name: &str) {} fn get_code(name: &str) -> String { diff --git a/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_environment.snap b/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_environment.snap new file mode 100644 index 00000000..c330d7ea --- /dev/null +++ b/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_environment.snap @@ -0,0 +1,111 @@ +--- +source: src/tests/code_samples/mod.rs +expression: evaluator +--- +Evaluator { + environment: Environment { + variables: { + "clamp": Function { + parameters: [ + "n", + "limit", + ], + block: Block { + nodes: [ + Expression { + expression: If { + condition: Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: GreaterThan, + right_hand: Identifier { + identifier: "limit", + }, + }, + consequence: Block { + nodes: [ + Statement { + statement: Return { + expression: Identifier { + identifier: "limit", + }, + }, + }, + ], + }, + alternative: None, + }, + }, + Expression { + expression: Identifier { + identifier: "n", + }, + }, + ], + }, + }, + "factorial": Function { + parameters: [ + "n", + ], + block: Block { + nodes: [ + Expression { + expression: If { + condition: Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: LesserThan, + right_hand: Integer { + literal: 2, + }, + }, + consequence: Block { + nodes: [ + Statement { + statement: Return { + expression: Integer { + literal: 1, + }, + }, + }, + ], + }, + alternative: None, + }, + }, + Statement { + statement: Return { + expression: Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: Multiply, + right_hand: Call { + function: Identifier { + identifier: "factorial", + }, + arguments: [ + Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: Minus, + right_hand: Integer { + literal: 1, + }, + }, + ], + }, + }, + }, + }, + ], + }, + }, + }, + sub_environment: None, + }, +} diff --git a/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_evaluation.snap b/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_evaluation.snap new file mode 100644 index 00000000..8e8b4aee --- /dev/null +++ b/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_evaluation.snap @@ -0,0 +1,7 @@ +--- +source: src/tests/code_samples/mod.rs +expression: evaluation +--- +Integer { + value: 106, +} diff --git a/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_lexical_analysis.snap b/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_lexical_analysis.snap new file mode 100644 index 00000000..25029205 --- /dev/null +++ b/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_lexical_analysis.snap @@ -0,0 +1,126 @@ +--- +source: src/tests/code_samples/mod.rs +expression: tokens +--- +[ + Let, + Identifier { + literal: "factorial", + }, + Assign, + Function, + OpeningRoundBracket, + Identifier { + literal: "n", + }, + ClosingRoundBracket, + OpeningCurlyBracket, + If, + OpeningRoundBracket, + Identifier { + literal: "n", + }, + LesserThan, + Integer { + literal: 2, + }, + ClosingRoundBracket, + OpeningCurlyBracket, + Return, + Integer { + literal: 1, + }, + SemiColon, + ClosingCurlyBracket, + Return, + Identifier { + literal: "n", + }, + Multiply, + Identifier { + literal: "factorial", + }, + OpeningRoundBracket, + Identifier { + literal: "n", + }, + Minus, + Integer { + literal: 1, + }, + ClosingRoundBracket, + SemiColon, + ClosingCurlyBracket, + Let, + Identifier { + literal: "clamp", + }, + Assign, + Function, + OpeningRoundBracket, + Identifier { + literal: "n", + }, + Comma, + Identifier { + literal: "limit", + }, + ClosingRoundBracket, + OpeningCurlyBracket, + If, + OpeningRoundBracket, + Identifier { + literal: "n", + }, + GreaterThan, + Identifier { + literal: "limit", + }, + ClosingRoundBracket, + OpeningCurlyBracket, + Return, + Identifier { + literal: "limit", + }, + SemiColon, + ClosingCurlyBracket, + Identifier { + literal: "n", + }, + ClosingCurlyBracket, + Identifier { + literal: "clamp", + }, + OpeningRoundBracket, + Identifier { + literal: "factorial", + }, + OpeningRoundBracket, + Integer { + literal: 5, + }, + ClosingRoundBracket, + Comma, + Integer { + literal: 100, + }, + ClosingRoundBracket, + Plus, + Identifier { + literal: "clamp", + }, + OpeningRoundBracket, + Identifier { + literal: "factorial", + }, + OpeningRoundBracket, + Integer { + literal: 3, + }, + ClosingRoundBracket, + Comma, + Integer { + literal: 100, + }, + ClosingRoundBracket, +] diff --git a/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_syntax_analysis.snap b/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_syntax_analysis.snap new file mode 100644 index 00000000..a1daf416 --- /dev/null +++ b/src/tests/code_samples/snapshots/monkey_interpreter__tests__code_samples__test_early_return_syntax_analysis.snap @@ -0,0 +1,161 @@ +--- +source: src/tests/code_samples/mod.rs +expression: abstract_syntax_tree +--- +[ + Statement { + statement: Let { + identifier: "factorial", + expression: Function { + parameters: [ + "n", + ], + block: Block { + nodes: [ + Expression { + expression: If { + condition: Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: LesserThan, + right_hand: Integer { + literal: 2, + }, + }, + consequence: Block { + nodes: [ + Statement { + statement: Return { + expression: Integer { + literal: 1, + }, + }, + }, + ], + }, + alternative: None, + }, + }, + Statement { + statement: Return { + expression: Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: Multiply, + right_hand: Call { + function: Identifier { + identifier: "factorial", + }, + arguments: [ + Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: Minus, + right_hand: Integer { + literal: 1, + }, + }, + ], + }, + }, + }, + }, + ], + }, + }, + }, + }, + Statement { + statement: Let { + identifier: "clamp", + expression: Function { + parameters: [ + "n", + "limit", + ], + block: Block { + nodes: [ + Expression { + expression: If { + condition: Infix { + left_hand: Identifier { + identifier: "n", + }, + operator: GreaterThan, + right_hand: Identifier { + identifier: "limit", + }, + }, + consequence: Block { + nodes: [ + Statement { + statement: Return { + expression: Identifier { + identifier: "limit", + }, + }, + }, + ], + }, + alternative: None, + }, + }, + Expression { + expression: Identifier { + identifier: "n", + }, + }, + ], + }, + }, + }, + }, + Expression { + expression: Infix { + left_hand: Call { + function: Identifier { + identifier: "clamp", + }, + arguments: [ + Call { + function: Identifier { + identifier: "factorial", + }, + arguments: [ + Integer { + literal: 5, + }, + ], + }, + Integer { + literal: 100, + }, + ], + }, + operator: Plus, + right_hand: Call { + function: Identifier { + identifier: "clamp", + }, + arguments: [ + Call { + function: Identifier { + identifier: "factorial", + }, + arguments: [ + Integer { + literal: 3, + }, + ], + }, + Integer { + literal: 100, + }, + ], + }, + }, + }, +]