From e5ed6e13a4adbbe61317194af36c33c82b33c90f Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Sun, 25 Mar 2018 01:50:10 +0100 Subject: lost chapter --- evaluator/macro_expansion_test.go | 123 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 evaluator/macro_expansion_test.go (limited to 'evaluator/macro_expansion_test.go') diff --git a/evaluator/macro_expansion_test.go b/evaluator/macro_expansion_test.go new file mode 100644 index 0000000..4a67f35 --- /dev/null +++ b/evaluator/macro_expansion_test.go @@ -0,0 +1,123 @@ +package evaluator + +import ( + "monkey/ast" + "monkey/lexer" + "monkey/object" + "monkey/parser" + "testing" +) + +func TestDefineMacros(t *testing.T) { + input := ` +let number = 1; +let function = fn(x, y) { x + y }; +let mymacro = macro(x, y) { x + y; }; +` + + env := object.NewEnvironment() + program := testParseProgram(input) + + DefineMacros(program, env) + + if len(program.Statements) != 2 { + t.Fatalf("Wrong number of statements. got=%d", + len(program.Statements)) + } + + _, ok := env.Get("number") + if ok { + t.Fatalf("number should not be defined") + } + _, ok = env.Get("function") + if ok { + t.Fatalf("function should not be defined") + } + + obj, ok := env.Get("mymacro") + if !ok { + t.Fatalf("macro not in environment.") + } + + macro, ok := obj.(*object.Macro) + if !ok { + t.Fatalf("object is not Macro. got=%T (%+v)", obj, obj) + } + + if len(macro.Parameters) != 2 { + t.Fatalf("Wrong number of macro parameters. got=%d", + len(macro.Parameters)) + } + + if macro.Parameters[0].String() != "x" { + t.Fatalf("parameter is not 'x'. got=%q", macro.Parameters[0]) + } + if macro.Parameters[1].String() != "y" { + t.Fatalf("parameter is not 'y'. got=%q", macro.Parameters[1]) + } + + expectedBody := "(x + y)" + + if macro.Body.String() != expectedBody { + t.Fatalf("body is not %q. got=%q", expectedBody, macro.Body.String()) + } +} + +func testParseProgram(input string) *ast.Program { + l := lexer.New(input) + p := parser.New(l) + return p.ParseProgram() +} + +func TestExpandMacros(t *testing.T) { + tests := []struct { + input string + expected string + }{ + { + ` + let infixExpression = macro() { quote(1 + 2); }; + + infixExpression(); + `, + `(1 + 2)`, + }, + { + ` + let reverse = macro(a, b) { quote(unquote(b) - unquote(a)); }; + + reverse(2 + 2, 10 - 5); + `, + `(10 - 5) - (2 + 2)`, + }, + { + ` + let unless = macro(condition, consequence, alternative) { + quote(if (!(unquote(condition))) { + unquote(consequence); + } else { + unquote(alternative); + }); + }; + + unless(10 > 5, puts("not greater"), puts("greater")); + `, + + `if (!(10 > 5)) { puts("not greater") } else { puts("greater") }`, + }, + } + + for _, tt := range tests { + expected := testParseProgram(tt.expected) + program := testParseProgram(tt.input) + + env := object.NewEnvironment() + DefineMacros(program, env) + expanded := ExpandMacros(program, env) + + if expanded.String() != expected.String() { + t.Errorf("not equal. want=%q, got=%q", + expected.String(), expanded.String()) + } + } +} -- cgit v1.2.3