CallExpression ast, parser and tests completed.

git-svn-id: https://svn.tlawal.org/svn/monkey@39 f6afcba9-9ef1-4bdd-9b72-7484f5705bac
This commit is contained in:
Tijani Lawal 2022-08-01 04:58:28 +00:00
parent c421b040d5
commit 7e34164019
3 changed files with 167 additions and 7 deletions

View File

@ -261,3 +261,28 @@ func (fl *FunctionLiteral) String() string {
return out.String() return out.String()
} }
// Call Expression
type CallExpression struct {
Token token.Token // The '(' token
Function Expression // Identifier or FunctionLiteral
Arguments []Expression
}
func (ce *CallExpression) expression_node() {}
func (ce *CallExpression) TokenLiteral() string { return ce.Token.Literal }
func (ce *CallExpression) String() string {
var out bytes.Buffer
args := []string{}
for _, a := range ce.Arguments {
args = append(args, a.String())
}
out.WriteString(ce.Function.String())
out.WriteString("(")
out.WriteString(strings.Join(args, ", "))
out.WriteString(")")
return out.String()
}

View File

@ -30,6 +30,7 @@ var precedences = map[token.TokenType]int{
token.MINUS: SUM, token.MINUS: SUM,
token.SLASH: PRODUCT, token.SLASH: PRODUCT,
token.ASTERISK: PRODUCT, token.ASTERISK: PRODUCT,
token.LPAREN: CALL,
} }
func (l_parser *Parser) peek_precedence() int { func (l_parser *Parser) peek_precedence() int {
@ -109,6 +110,9 @@ func New(l_lexer *lexer.Lexer) *Parser {
// Function Literals // Function Literals
l_parser.register_prefix(token.FUNCTION, l_parser.parse_function_literal) l_parser.register_prefix(token.FUNCTION, l_parser.parse_function_literal)
// Call Expression
l_parser.register_infix(token.LPAREN, l_parser.parse_call_expression)
return l_parser return l_parser
} }
@ -349,7 +353,7 @@ func (l_parser *Parser) parse_block_statement() *ast.BlockStatement {
} }
func (l_parser *Parser) parse_function_literal() ast.Expression { func (l_parser *Parser) parse_function_literal() ast.Expression {
literal := &ast.FunctionLiteral{ Token: l_parser.current_token } literal := &ast.FunctionLiteral{Token: l_parser.current_token}
if !l_parser.expect_peek(token.LPAREN) { if !l_parser.expect_peek(token.LPAREN) {
return nil return nil
} }
@ -358,6 +362,7 @@ func (l_parser *Parser) parse_function_literal() ast.Expression {
if !l_parser.expect_peek(token.LBRACE) { if !l_parser.expect_peek(token.LBRACE) {
return nil return nil
} }
literal.Body = l_parser.parse_block_statement() literal.Body = l_parser.parse_block_statement()
return literal return literal
} }
@ -386,3 +391,32 @@ func (l_parser *Parser) parse_function_parameters() []*ast.Identifier {
} }
return identifiers return identifiers
} }
func (l_parser *Parser) parse_call_expression(function ast.Expression) ast.Expression {
expression := &ast.CallExpression{Token: l_parser.current_token, Function: function}
expression.Arguments = l_parser.parse_call_arguments()
return expression
}
func (l_parser *Parser) parse_call_arguments() []ast.Expression {
args := []ast.Expression{}
if l_parser.peek_token_is(token.RPAREN) {
l_parser.next_token()
return args
}
l_parser.next_token()
args = append(args, l_parser.parse_expression(LOWEST))
for l_parser.peek_token_is(token.COMMA) {
l_parser.next_token()
l_parser.next_token()
args = append(args, l_parser.parse_expression(LOWEST))
}
if !l_parser.expect_peek(token.RPAREN) {
return nil
}
return args
}

View File

@ -310,6 +310,18 @@ func TestOperatorPrecedenceParsing(l_test *testing.T) {
"!(true == true)", "!(true == true)",
"(!(true == true))", "(!(true == true))",
}, },
{
"a + add(b * c) + d",
"((a + add((b * c))) + d)",
},
{
"add(a, b, 1, 2 * 3, 4 + 5, add(6, 7 * 8))",
"add(a, b, 1, (2 * 3), (4 + 5), add(6, (7 * 8)))",
},
{
"add(a + b + c * d / f + g)",
"add((((a + b) + ((c * d) / f)) + g))",
},
} }
for _, tt := range tests { for _, tt := range tests {
l_lexer := lexer.New(tt.input) l_lexer := lexer.New(tt.input)
@ -493,14 +505,14 @@ func TestFunctionLiteralParsing(l_test *testing.T) {
} }
func TestFunctionParameterParsing(l_test *testing.T){ func TestFunctionParameterParsing(l_test *testing.T) {
tests := []struct { tests := []struct {
input string input string
expected_params []string expected_params []string
}{ }{
{ input: "fn() {};", expected_params: []string{}}, {input: "fn() {};", expected_params: []string{}},
{ input: "fn(x) {};", expected_params: []string{"x"}}, {input: "fn(x) {};", expected_params: []string{"x"}},
{ input: "fn(x, y, z) {};", expected_params: []string{"x", "y", "z"}}, {input: "fn(x, y, z) {};", expected_params: []string{"x", "y", "z"}},
} }
for _, tt := range tests { for _, tt := range tests {
@ -512,7 +524,7 @@ func TestFunctionParameterParsing(l_test *testing.T){
statement := program.Statements[0].(*ast.ExpressionStatement) statement := program.Statements[0].(*ast.ExpressionStatement)
function := statement.Expression.(*ast.FunctionLiteral) function := statement.Expression.(*ast.FunctionLiteral)
if len(function.Parameters) != len(tt.expected_params){ if len(function.Parameters) != len(tt.expected_params) {
l_test.Errorf("length of parameters is wrong, want %d, got=%d\n", l_test.Errorf("length of parameters is wrong, want %d, got=%d\n",
len(tt.expected_params), len(function.Parameters)) len(tt.expected_params), len(function.Parameters))
} }
@ -523,6 +535,95 @@ func TestFunctionParameterParsing(l_test *testing.T){
} }
} }
func TestCallExpressionParsing(l_test *testing.T) {
input := "add(1, 2 * 3, 4 + 5);"
l_lexer := lexer.New(input)
l_parser := New(l_lexer)
program := l_parser.ParseProgram()
check_parser_errors(l_test, l_parser)
if len(program.Statements) != 1 {
l_test.Fatalf("program.Statements does not contain %d statements, got=%d\n", 1, len(program.Statements))
}
statement, ok := program.Statements[0].(*ast.ExpressionStatement)
if !ok {
l_test.Fatalf("statement is not ast.ExpressionStatement, got=%T", program.Statements[0])
}
expression, ok := statement.Expression.(*ast.CallExpression)
if !ok {
l_test.Fatalf("statemnt.Expression is not ast.CallExpression, got=%T", statement.Expression)
}
if !testIdentifier(l_test, expression.Function, "add") {
return
}
if len(expression.Arguments) != 3 {
l_test.Fatalf("wrong length of arguments, got=%d", len(expression.Arguments))
}
testLiteralExpression(l_test, expression.Arguments[0], 1)
testInfixExpression(l_test, expression.Arguments[1], 2, "*", 3)
testInfixExpression(l_test, expression.Arguments[2], 4, "+", 5)
}
func TestCallExpressionParameterParsing(l_test *testing.T) {
tests := []struct {
input string
expected_ident string
expected_args []string
}{
{
input: "add();",
expected_ident: "add",
expected_args: []string{},
},
{
input: "add(1);",
expected_ident: "add",
expected_args: []string{"1"},
},
{
input: "add(1, 2 * 3, 4 + 5);",
expected_ident: "add",
expected_args: []string{"1", "(2 * 3)", "(4 + 5)"},
},
}
for _, tt := range tests {
l_lexer := lexer.New(tt.input)
l_parser := New(l_lexer)
program := l_parser.ParseProgram()
check_parser_errors(l_test, l_parser)
statement := program.Statements[0].(*ast.ExpressionStatement)
expression, ok := statement.Expression.(*ast.CallExpression)
if !ok {
l_test.Fatalf("statement.Expression is not ast.CallExpression, got=%T",
statement.Expression)
}
if !testIdentifier(l_test, expression.Function, tt.expected_ident) {
return
}
if len(expression.Arguments) != len(tt.expected_args) {
l_test.Fatalf("wrong number of arguments, want=%d, got=%d",
len(tt.expected_args), len(expression.Arguments))
}
for i, arg := range tt.expected_args {
if expression.Arguments[i].String() != arg {
l_test.Errorf("argument %d wrong. want=%q, got=%q", i,
arg, expression.Arguments[i].String())
}
}
}
}
// Helpers // Helpers
func check_parser_errors(l_test *testing.T, l_parser *Parser) { func check_parser_errors(l_test *testing.T, l_parser *Parser) {