Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions conformance/core.json
Original file line number Diff line number Diff line change
Expand Up @@ -299,5 +299,10 @@
"name": "typed Call and Run statements",
"source": "To combine with left and right:\n Say left plus right.\nEnd function.\nCall combine with 2 and 3.\nRun combine with 4 and 1.",
"output": "5\n5\n"
},
{
"name": "Tell method call discards return value",
"source": "Create a class called Counter:\n When creating a Counter:\n Remember 41 as this.value.\n End constructor.\n To get_value:\n Give back this.value.\n End function.\nEnd class.\nLet counter be a new Counter.\nTell counter to get_value.\nSay \"done\".",
"output": "done\n"
}
]
17 changes: 17 additions & 0 deletions native/internal/tell/bytecode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,23 @@ Say person.name.`
}
}

func TestBytecodeTellMethodSentenceDiscardsReturnValue(t *testing.T) {
source := `Create a class called Counter:
When creating a Counter:
Remember 41 as this.value.
End constructor.
To get_value:
Give back this.value.
End function.
End class.
Let counter be a new Counter.
Tell counter to get_value.
Say "done".`
if got, want := runCompiled(t, source), "done\n"; got != want {
t.Fatalf("compiled Tell method sentence output = %q, want %q", got, want)
}
}

func TestBytecodeSupportsTupleValues(t *testing.T) {
source := `Let first, second be (1, 2).
Let values be (first, second, 3).
Expand Down
2 changes: 1 addition & 1 deletion native/internal/tell/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ func parseSimpleStatement(source string, line, column int) *simpleStatement {
}
if valid {
callee := memberExpression{value: nameExpression{name: target}, member: method}
return &simpleStatement{kind: "expression", source: source, expression: callExpression{callee: callee, args: arguments}, hasExpression: true}
return &simpleStatement{kind: "call", source: source, expression: callExpression{callee: callee, args: arguments}, hasExpression: true}
}
}
}
Expand Down
23 changes: 22 additions & 1 deletion native/internal/tell/tell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,32 @@ func TestParserBuildsExpressionASTsForCommonStatements(t *testing.T) {
t.Fatalf("natural object construction did not produce a parsed statement: %#v, %v", constructed, err)
}
told, err := parse("Tell box to open with \"wide\".", "ast.tell")
if err != nil || told[0].simple == nil || told[0].simple.kind != "expression" {
if err != nil || told[0].simple == nil || told[0].simple.kind != "call" {
t.Fatalf("Tell method sentence did not produce an expression AST: %#v, %v", told, err)
}
}

func TestTellMethodSentenceDiscardsReturnValue(t *testing.T) {
source := `Create a class called Counter:
When creating a Counter:
Remember 41 as this.value.
End constructor.
To get_value:
Give back this.value.
End function.
End class.
Let counter be a new Counter.
Tell counter to get_value.
Say "done".`
var output bytes.Buffer
if err := RunWithIO(source, "tell-call.tell", bytes.NewReader(nil), &output); err != nil {
t.Fatal(err)
}
if got, want := output.String(), "done\n"; got != want {
t.Fatalf("Tell method sentence output = %q, want %q (method return must not echo)", got, want)
}
}

func TestMultipleAssignmentValidatesBeforeMutating(t *testing.T) {
runtime := newRuntime("test.tell", nil, &bytes.Buffer{}, DefaultExecutionLimits())
runtime.global.set("first", float64(1))
Expand Down
2 changes: 1 addition & 1 deletion tell/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,7 @@ def parsed_expression(expression: str) -> TellExpr | None:
if method:
callee = TellExpr("member", method, (TellExpr("name", parts[0]),))
return {
"kind": "expression",
"kind": "call",
"source": text,
"expression": TellExpr("call", children=(callee, *arguments), names=(None,) * len(arguments)),
}
Expand Down
18 changes: 18 additions & 0 deletions tests/test_tell.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def test_parser_builds_expression_asts_for_common_statements(self):
constructed = Parser("Let box be a new Box.").parse()[0]
self.assertEqual(constructed.data["simple"]["expression"].kind, "call")
told = Parser('Tell box to open with "wide".').parse()[0]
self.assertEqual(told.data["simple"]["kind"], "call")
self.assertEqual(told.data["simple"]["expression"].kind, "call")
member_assignment = Parser('Set box.name to "Ada".').parse()[0]
self.assertEqual(member_assignment.data["simple"]["kind"], "set_member")
Expand All @@ -36,6 +37,23 @@ def run_tell(self, source, answers=None):
interpreter.run(source, "test.tell")
return output, interpreter.global_env

def test_tell_method_sentence_discards_return_value(self):
source = """
Create a class called Counter:
When creating a Counter:
Remember 41 as this.value.
End constructor.
To get_value:
Give back this.value.
End function.
End class.
Let counter be a new Counter.
Tell counter to get_value.
Say "done".
"""
output, _ = self.run_tell(source)
self.assertEqual(output, ["done"])

def test_variables_math_and_interpolation(self):
output, env = self.run_tell('''
Let name be "Ada".
Expand Down
Loading