Skip to content

Commit f09df34

Browse files
committedMar 29, 2022
增加宏实现
1 parent 0bd7d59 commit f09df34

15 files changed

+696
-2
lines changed
 

‎.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.exe
2+
dist/*

‎ast/ast.go

+25
Original file line numberDiff line numberDiff line change
@@ -317,3 +317,28 @@ func (hl *HashLiteral) String() string {
317317
out.WriteString("}")
318318
return out.String()
319319
}
320+
321+
type MacroLiteral struct {
322+
Token token.Token // The 'macro' token
323+
Parameters []*Identifier
324+
Body *BlockStatement
325+
}
326+
327+
func (ml *MacroLiteral) expressionNode() {}
328+
func (ml *MacroLiteral) TokenLiteral() string { return ml.Token.Literal }
329+
func (ml *MacroLiteral) String() string {
330+
var out bytes.Buffer
331+
332+
params := []string{}
333+
for _, p := range ml.Parameters {
334+
params = append(params, p.String())
335+
}
336+
337+
out.WriteString(ml.TokenLiteral())
338+
out.WriteString("(")
339+
out.WriteString(strings.Join(params, ", "))
340+
out.WriteString(") ")
341+
out.WriteString(ml.Body.String())
342+
343+
return out.String()
344+
}

‎ast/modify.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package ast
2+
3+
type ModifierFunc func(Node) Node
4+
5+
func Modify(node Node, modifier ModifierFunc) Node {
6+
switch node := node.(type) {
7+
8+
case *Program:
9+
for i, statement := range node.Statements {
10+
node.Statements[i], _ = Modify(statement, modifier).(Statement)
11+
}
12+
13+
case *ExpressionStatement:
14+
node.Expression, _ = Modify(node.Expression, modifier).(Expression)
15+
16+
case *InfixExpression:
17+
node.Left, _ = Modify(node.Left, modifier).(Expression)
18+
node.Right, _ = Modify(node.Right, modifier).(Expression)
19+
20+
case *PrefixExpression:
21+
node.Right, _ = Modify(node.Right, modifier).(Expression)
22+
23+
case *IndexExpression:
24+
node.Left, _ = Modify(node.Left, modifier).(Expression)
25+
node.Index, _ = Modify(node.Index, modifier).(Expression)
26+
27+
case *IfExpression:
28+
node.Condition, _ = Modify(node.Condition, modifier).(Expression)
29+
node.Consequence, _ = Modify(node.Consequence, modifier).(*BlockStatement)
30+
if node.Alternative != nil {
31+
node.Alternative, _ = Modify(node.Alternative, modifier).(*BlockStatement)
32+
}
33+
34+
case *BlockStatement:
35+
for i, _ := range node.Statements {
36+
node.Statements[i], _ = Modify(node.Statements[i], modifier).(Statement)
37+
}
38+
case *ReturnStatement:
39+
node.ReturnValue, _ = Modify(node.ReturnValue, modifier).(Expression)
40+
41+
case *LetStatement:
42+
node.Value, _ = Modify(node.Value, modifier).(Expression)
43+
44+
case *FunctionLiteral:
45+
for i, _ := range node.Parameters {
46+
node.Parameters[i], _ = Modify(node.Parameters[i], modifier).(*Identifier)
47+
}
48+
node.Body, _ = Modify(node.Body, modifier).(*BlockStatement)
49+
50+
case *ArrayLiteral:
51+
for i, _ := range node.Elements {
52+
node.Elements[i], _ = Modify(node.Elements[i], modifier).(Expression)
53+
}
54+
55+
case *HashLiteral:
56+
newPairs := make(map[Expression]Expression)
57+
for key, val := range node.Pairs {
58+
newKey, _ := Modify(key, modifier).(Expression)
59+
newVal, _ := Modify(val, modifier).(Expression)
60+
newPairs[newKey] = newVal
61+
}
62+
node.Pairs = newPairs
63+
}
64+
65+
return modifier(node)
66+
}

‎ast/modify_test.go

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package ast
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestModify(t *testing.T) {
9+
one := func() Expression { return &IntegerLiteral{Value: 1} }
10+
two := func() Expression { return &IntegerLiteral{Value: 2} }
11+
12+
turnOneIntoTwo := func(node Node) Node {
13+
integer, ok := node.(*IntegerLiteral)
14+
if !ok {
15+
return node
16+
}
17+
18+
if integer.Value != 1 {
19+
return node
20+
}
21+
22+
integer.Value = 2
23+
return integer
24+
}
25+
26+
tests := []struct {
27+
input Node
28+
expected Node
29+
}{
30+
{
31+
one(),
32+
two(),
33+
},
34+
{
35+
&Program{
36+
Statements: []Statement{
37+
&ExpressionStatement{Expression: one()},
38+
},
39+
},
40+
&Program{
41+
Statements: []Statement{
42+
&ExpressionStatement{Expression: two()},
43+
},
44+
},
45+
},
46+
{
47+
&InfixExpression{Left: one(), Operator: "+", Right: two()},
48+
&InfixExpression{Left: two(), Operator: "+", Right: two()},
49+
},
50+
{
51+
&InfixExpression{Left: two(), Operator: "+", Right: one()},
52+
&InfixExpression{Left: two(), Operator: "+", Right: two()},
53+
},
54+
}
55+
56+
for _, tt := range tests {
57+
modified := Modify(tt.input, turnOneIntoTwo)
58+
59+
equal := reflect.DeepEqual(modified, tt.expected)
60+
if !equal {
61+
t.Errorf("not equal. got=%#v, want=%#v",
62+
modified, tt.expected)
63+
}
64+
}
65+
}

‎evaluator/evaluator.go

+4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
6363
return &object.Function{Parameters: params, Env: env, Body: body}
6464

6565
case *ast.CallExpression:
66+
if node.Function.TokenLiteral() == "quote" {
67+
return quote(node.Arguments[0], env)
68+
}
69+
6670
function := Eval(node.Function, env)
6771
if isError(function) {
6872
return function

‎evaluator/macro_expansion.go

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package evaluator
2+
3+
import (
4+
"monkeygo/ast"
5+
"monkeygo/object"
6+
)
7+
8+
func DefineMacros(program *ast.Program, env *object.Environment) {
9+
definitions := []int{}
10+
11+
for i, statement := range program.Statements {
12+
if isMacroDefinition(statement) {
13+
addMacro(statement, env)
14+
definitions = append(definitions, i)
15+
}
16+
}
17+
18+
for i := len(definitions) - 1; i >= 0; i = i - 1 {
19+
definitionIndex := definitions[i]
20+
program.Statements = append(
21+
program.Statements[:definitionIndex],
22+
program.Statements[definitionIndex+1:]...,
23+
)
24+
}
25+
}
26+
27+
func isMacroDefinition(node ast.Statement) bool {
28+
letStatement, ok := node.(*ast.LetStatement)
29+
if !ok {
30+
return false
31+
}
32+
33+
_, ok = letStatement.Value.(*ast.MacroLiteral)
34+
if !ok {
35+
return false
36+
}
37+
38+
return true
39+
}
40+
41+
func addMacro(stmt ast.Statement, env *object.Environment) {
42+
letStatement, _ := stmt.(*ast.LetStatement)
43+
macroLiteral, _ := letStatement.Value.(*ast.MacroLiteral)
44+
45+
macro := &object.Macro{
46+
Parameters: macroLiteral.Parameters,
47+
Env: env,
48+
Body: macroLiteral.Body,
49+
}
50+
51+
env.Set(letStatement.Name.Value, macro)
52+
}
53+
54+
func ExpandMacros(program ast.Node, env *object.Environment) ast.Node {
55+
return ast.Modify(program, func(node ast.Node) ast.Node {
56+
callExpression, ok := node.(*ast.CallExpression)
57+
if !ok {
58+
return node
59+
}
60+
61+
macro, ok := isMacroCall(callExpression, env)
62+
if !ok {
63+
return node
64+
}
65+
66+
args := quoteArgs(callExpression)
67+
evalEnv := extendMacroEnv(macro, args)
68+
69+
evaluated := Eval(macro.Body, evalEnv)
70+
71+
quote, ok := evaluated.(*object.Quote)
72+
if !ok {
73+
panic("we only support returning AST-nodes from macros")
74+
}
75+
76+
return quote.Node
77+
})
78+
}
79+
80+
func isMacroCall(
81+
exp *ast.CallExpression,
82+
env *object.Environment,
83+
) (*object.Macro, bool) {
84+
identifier, ok := exp.Function.(*ast.Identifier)
85+
if !ok {
86+
return nil, false
87+
}
88+
89+
obj, ok := env.Get(identifier.Value)
90+
if !ok {
91+
return nil, false
92+
}
93+
94+
macro, ok := obj.(*object.Macro)
95+
if !ok {
96+
return nil, false
97+
}
98+
99+
return macro, true
100+
}
101+
102+
func quoteArgs(exp *ast.CallExpression) []*object.Quote {
103+
args := []*object.Quote{}
104+
105+
for _, a := range exp.Arguments {
106+
args = append(args, &object.Quote{Node: a})
107+
}
108+
109+
return args
110+
}
111+
112+
func extendMacroEnv(
113+
macro *object.Macro,
114+
args []*object.Quote,
115+
) *object.Environment {
116+
extended := object.NewEnclosedEnvironment(macro.Env)
117+
118+
for paramIdx, param := range macro.Parameters {
119+
extended.Set(param.Value, args[paramIdx])
120+
}
121+
122+
return extended
123+
}

‎evaluator/macro_expansion_test.go

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package evaluator
2+
3+
import (
4+
"monkeygo/ast"
5+
"monkeygo/lexer"
6+
"monkeygo/object"
7+
"monkeygo/parser"
8+
"testing"
9+
)
10+
11+
func TestDefineMacros(t *testing.T) {
12+
13+
input := `
14+
let number = 1;
15+
let function = fn(x, y) { x + y };
16+
let mymacro = macro(x, y) { x + y; };
17+
`
18+
19+
env := object.NewEnvironment()
20+
program := testParseProgram(input)
21+
22+
DefineMacros(program, env)
23+
24+
if len(program.Statements) != 2 {
25+
t.Fatalf("Wrong number of statements. got=%d",
26+
len(program.Statements))
27+
}
28+
29+
_, ok := env.Get("number")
30+
if ok {
31+
t.Fatalf("number should not be defined")
32+
}
33+
_, ok = env.Get("function")
34+
if ok {
35+
t.Fatalf("function should not be defined")
36+
}
37+
38+
obj, ok := env.Get("mymacro")
39+
if !ok {
40+
t.Fatalf("macro not in environment.")
41+
}
42+
43+
macro, ok := obj.(*object.Macro)
44+
if !ok {
45+
t.Fatalf("object is not Macro. got=%T (%+v)", obj, obj)
46+
}
47+
48+
if len(macro.Parameters) != 2 {
49+
t.Fatalf("Wrong number of macro parameters. got=%d",
50+
len(macro.Parameters))
51+
}
52+
53+
if macro.Parameters[0].String() != "x" {
54+
t.Fatalf("parameter is not 'x'. got=%q", macro.Parameters[0])
55+
}
56+
if macro.Parameters[1].String() != "y" {
57+
t.Fatalf("parameter is not 'y'. got=%q", macro.Parameters[1])
58+
}
59+
60+
expectedBody := "(x + y)"
61+
62+
if macro.Body.String() != expectedBody {
63+
t.Fatalf("body is not %q. got=%q", expectedBody, macro.Body.String())
64+
}
65+
}
66+
67+
func testParseProgram(input string) *ast.Program {
68+
l := lexer.New(input)
69+
p := parser.New(l)
70+
return p.ParseProgram()
71+
}
72+
73+
//展开宏
74+
func TestExpandMacros(t *testing.T) {
75+
tests := []struct {
76+
input string
77+
expected string
78+
}{
79+
{
80+
`
81+
let infixExpression = macro() { quote(1 + 2); };
82+
83+
infixExpression();
84+
`,
85+
`(1 + 2)`,
86+
},
87+
{
88+
`
89+
let reverse = macro(a, b) { quote(unquote(b) - unquote(a)); };
90+
91+
reverse(2 + 2, 10 - 5);
92+
`,
93+
`(10 - 5) - (2 + 2)`,
94+
},
95+
{
96+
`
97+
let unless = macro(condition, consequence, alternative) {
98+
quote(if (!(unquote(condition))) {
99+
unquote(consequence);
100+
} else {
101+
unquote(alternative);
102+
});
103+
};
104+
105+
unless(10 > 5, puts("not greater"), puts("greater"));
106+
`,
107+
`if (!(10 > 5)) { puts("not greater") } else { puts("greater") }`,
108+
},
109+
}
110+
111+
for _, tt := range tests {
112+
expected := testParseProgram(tt.expected)
113+
program := testParseProgram(tt.input)
114+
115+
env := object.NewEnvironment()
116+
DefineMacros(program, env)
117+
expanded := ExpandMacros(program, env)
118+
119+
if expanded.String() != expected.String() {
120+
t.Errorf("not equal. want=%q, got=%q",
121+
expected.String(), expanded.String())
122+
}
123+
}
124+
}

‎evaluator/quote_unquote.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package evaluator
2+
3+
import (
4+
"fmt"
5+
"monkeygo/ast"
6+
"monkeygo/object"
7+
"monkeygo/token"
8+
)
9+
10+
func quote(node ast.Node, env *object.Environment) object.Object {
11+
node = evalUnquoteCalls(node, env)
12+
return &object.Quote{Node: node}
13+
}
14+
15+
func evalUnquoteCalls(quoted ast.Node, env *object.Environment) ast.Node {
16+
return ast.Modify(quoted, func(node ast.Node) ast.Node {
17+
if !isUnquoteCall(node) {
18+
return node
19+
}
20+
21+
call, ok := node.(*ast.CallExpression)
22+
if !ok {
23+
return node
24+
}
25+
26+
if len(call.Arguments) != 1 {
27+
return node
28+
}
29+
30+
unquoted := Eval(call.Arguments[0], env)
31+
return convertObjectToASTNode(unquoted)
32+
})
33+
}
34+
35+
func isUnquoteCall(node ast.Node) bool {
36+
callExpression, ok := node.(*ast.CallExpression)
37+
if !ok {
38+
return false
39+
}
40+
41+
return callExpression.Function.TokenLiteral() == "unquote"
42+
}
43+
44+
func convertObjectToASTNode(obj object.Object) ast.Node {
45+
switch obj := obj.(type) {
46+
case *object.Integer:
47+
t := token.Token{
48+
Type: token.INT,
49+
Literal: fmt.Sprintf("%d", obj.Value),
50+
}
51+
return &ast.IntegerLiteral{Token: t, Value: obj.Value}
52+
case *object.Boolean:
53+
var t token.Token
54+
if obj.Value {
55+
t = token.Token{Type: token.TRUE, Literal: "true"}
56+
} else {
57+
t = token.Token{Type: token.FALSE, Literal: "false"}
58+
}
59+
return &ast.Boolean{Token: t, Value: obj.Value}
60+
case *object.Quote:
61+
return obj.Node
62+
63+
default:
64+
return nil
65+
}
66+
}

‎evaluator/quote_unquote_test.go

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package evaluator
2+
3+
import (
4+
"testing"
5+
6+
"monkeygo/object"
7+
)
8+
9+
func TestQuote(t *testing.T) {
10+
tests := []struct {
11+
input string
12+
expected string
13+
}{
14+
{
15+
`quote(5)`,
16+
`5`,
17+
},
18+
{
19+
`quote(5 + 8)`,
20+
`(5 + 8)`,
21+
},
22+
{
23+
`quote(foobar)`,
24+
`foobar`,
25+
},
26+
{
27+
`quote(foobar + barfoo)`,
28+
`(foobar + barfoo)`,
29+
},
30+
{
31+
`quote(unquote(4))`,
32+
`4`,
33+
},
34+
{
35+
`quote(unquote(4 + 4))`,
36+
`8`,
37+
},
38+
{
39+
`quote(8 + unquote(4 + 4))`,
40+
`(8 + 8)`,
41+
},
42+
{
43+
`quote(unquote(4 + 4) + 8)`,
44+
`(8 + 8)`,
45+
},
46+
{
47+
`let foobar = 8;
48+
quote(foobar)`,
49+
`foobar`,
50+
},
51+
{
52+
`let foobar = 8;
53+
quote(unquote(foobar))`,
54+
`8`,
55+
},
56+
57+
{
58+
`quote(unquote(true))`,
59+
`true`,
60+
},
61+
{
62+
`quote(unquote(true == false))`,
63+
`false`,
64+
},
65+
{
66+
`quote(unquote(quote(4 + 4)))`,
67+
`(4 + 4)`,
68+
},
69+
{
70+
`let quotedInfixExpression = quote(4 + 4);
71+
quote(unquote(4 + 4) + unquote(quotedInfixExpression))`,
72+
`(8 + (4 + 4))`,
73+
},
74+
}
75+
76+
for _, tt := range tests {
77+
evaluated := testEval(tt.input)
78+
quote, ok := evaluated.(*object.Quote)
79+
if !ok {
80+
t.Fatalf("expected *object.Quote. got=%T (%+v)",
81+
evaluated, evaluated)
82+
}
83+
84+
if quote.Node == nil {
85+
t.Fatalf("quote.Node is nil")
86+
}
87+
88+
if quote.Node.String() != tt.expected {
89+
t.Errorf("not equal. got=%q, want=%q",
90+
quote.Node.String(), tt.expected)
91+
}
92+
}
93+
}

‎lexer/lexer_test.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ let result = add(five, ten);
1616
"foobar"
1717
"foo bar"
1818
[1, 2];
19-
{"foo": "bar"}`
19+
{"foo": "bar"}
20+
macro(x, y) { x + y; };`
21+
2022
tests := []struct {
2123
expectedType token.TokenType
2224
expectedLiteral string
@@ -70,6 +72,19 @@ let result = add(five, ten);
7072
{token.COLON, ":"},
7173
{token.STRING, "bar"},
7274
{token.RBRACE, "}"},
75+
{token.MACRO, "macro"},
76+
{token.LPAREN, "("},
77+
{token.IDENT, "x"},
78+
{token.COMMA, ","},
79+
{token.IDENT, "y"},
80+
{token.RPAREN, ")"},
81+
{token.LBRACE, "{"},
82+
{token.IDENT, "x"},
83+
{token.PLUS, "+"},
84+
{token.IDENT, "y"},
85+
{token.SEMICOLON, ";"},
86+
{token.RBRACE, "}"},
87+
{token.SEMICOLON, ";"},
7388
{token.EOF, ""},
7489
}
7590

‎object/object.go

+38
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const (
1919
BUILTIN_OBJ = "BUILTIN"
2020
ARRAY_OBJ = "ARRAY"
2121
HASH_OBJ = "HASH"
22+
QUOTE_OBJ = "QUOTE"
23+
MACRO_OBJ = "MACRO"
2224
)
2325

2426
type ObjectType string
@@ -166,3 +168,39 @@ func (h *Hash) Inspect() string {
166168
type Hashable interface {
167169
HashKey() HashKey
168170
}
171+
172+
//引用支持
173+
type Quote struct {
174+
Node ast.Node
175+
}
176+
177+
func (q *Quote) Type() ObjectType { return QUOTE_OBJ }
178+
func (q *Quote) Inspect() string {
179+
return "QUOTE(" + q.Node.String() + ")"
180+
}
181+
182+
//宏结构
183+
type Macro struct {
184+
Parameters []*ast.Identifier
185+
Body *ast.BlockStatement
186+
Env *Environment
187+
}
188+
189+
func (m *Macro) Type() ObjectType { return MACRO_OBJ }
190+
func (m *Macro) Inspect() string {
191+
var out bytes.Buffer
192+
193+
params := []string{}
194+
for _, p := range m.Parameters {
195+
params = append(params, p.String())
196+
}
197+
198+
out.WriteString("macro")
199+
out.WriteString("(")
200+
out.WriteString(strings.Join(params, ", "))
201+
out.WriteString(") {\n")
202+
out.WriteString(m.Body.String())
203+
out.WriteString("\n}")
204+
205+
return out.String()
206+
}

‎parser/parser.go

+20
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ func New(l *lexer.Lexer) *Parser {
9090
p.registerInfix(token.LBRACKET, p.parseIndexExpression)
9191

9292
p.registerPrefix(token.LBRACE, p.parseHashLiteral)
93+
94+
p.registerPrefix(token.MACRO, p.parseMacroLiteral)
9395
p.nextToken()
9496
p.nextToken()
9597
return p
@@ -458,3 +460,21 @@ func (p *Parser) parseHashLiteral() ast.Expression {
458460
}
459461
return hash
460462
}
463+
464+
func (p *Parser) parseMacroLiteral() ast.Expression {
465+
lit := &ast.MacroLiteral{Token: p.curToken}
466+
467+
if !p.expectPeek(token.LPAREN) {
468+
return nil
469+
}
470+
471+
lit.Parameters = p.parseFunctionParameters()
472+
473+
if !p.expectPeek(token.LBRACE) {
474+
return nil
475+
}
476+
477+
lit.Body = p.parseBlockStatement()
478+
479+
return lit
480+
}

‎parser/parser_test.go

+47
Original file line numberDiff line numberDiff line change
@@ -749,3 +749,50 @@ func TestParsingHashLiteralsWithExpressions(t *testing.T) {
749749
testFunc(value)
750750
}
751751
}
752+
753+
func TestMacroLiteralParsing(t *testing.T) {
754+
input := `macro(x, y) { x + y; }`
755+
756+
l := lexer.New(input)
757+
p := New(l)
758+
program := p.ParseProgram()
759+
checkParserErrors(t, p)
760+
761+
if len(program.Statements) != 1 {
762+
t.Fatalf("program.Statements does not contain %d statements. got=%d\n",
763+
1, len(program.Statements))
764+
}
765+
766+
stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
767+
if !ok {
768+
t.Fatalf("statement is not ast.ExpressionStatement. got=%T",
769+
program.Statements[0])
770+
}
771+
772+
macro, ok := stmt.Expression.(*ast.MacroLiteral)
773+
if !ok {
774+
t.Fatalf("stmt.Expression is not ast.MacroLiteral. got=%T",
775+
stmt.Expression)
776+
}
777+
778+
if len(macro.Parameters) != 2 {
779+
t.Fatalf("macro literal parameters wrong. want 2, got=%d\n",
780+
len(macro.Parameters))
781+
}
782+
783+
testLiteralExpression(t, macro.Parameters[0], "x")
784+
testLiteralExpression(t, macro.Parameters[1], "y")
785+
786+
if len(macro.Body.Statements) != 1 {
787+
t.Fatalf("macro.Body.Statements has not 1 statements. got=%d\n",
788+
len(macro.Body.Statements))
789+
}
790+
791+
bodyStmt, ok := macro.Body.Statements[0].(*ast.ExpressionStatement)
792+
if !ok {
793+
t.Fatalf("macro body stmt is not ast.ExpressionStatement. got=%T",
794+
macro.Body.Statements[0])
795+
}
796+
797+
testInfixExpression(t, bodyStmt.Expression, "x", "+", "y")
798+
}

‎repl/repl.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const PROMPT = ">> "
2121
func Start(in io.Reader, out io.Writer) {
2222
scanner := bufio.NewScanner(in)
2323
env := object.NewEnvironment()
24+
macroEnv := object.NewEnvironment()
2425
for {
2526
fmt.Fprintf(out, PROMPT)
2627
scanned := scanner.Scan()
@@ -41,7 +42,10 @@ func Start(in io.Reader, out io.Writer) {
4142
printParserErrors(out, p.Errors())
4243
continue
4344
}
44-
evaluated := evaluator.Eval(program, env)
45+
evaluator.DefineMacros(program, macroEnv)
46+
expanded := evaluator.ExpandMacros(program, macroEnv)
47+
48+
evaluated := evaluator.Eval(expanded, env)
4549
if evaluated != nil {
4650
io.WriteString(out, evaluated.Inspect())
4751
io.WriteString(out, "\n")

‎token/token.go

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const (
4545
IF = "IF"
4646
ELSE = "ELSE"
4747
RETURN = "RETURN"
48+
MACRO = "MACRO"
4849
)
4950

5051
var keywords = map[string]TokenType{
@@ -55,6 +56,7 @@ var keywords = map[string]TokenType{
5556
"if": IF,
5657
"else": ELSE,
5758
"return": RETURN,
59+
"macro": MACRO,
5860
}
5961

6062
func LookupIdent(ident string) TokenType {

0 commit comments

Comments
 (0)
Please sign in to comment.