Can now parse FunctionLiteral and its parameters. Tests are also included.

git-svn-id: https://svn.tlawal.org/svn/monkey@38 f6afcba9-9ef1-4bdd-9b72-7484f5705bac
This commit is contained in:
Tijani Lawal 2022-07-31 23:31:43 +00:00
parent c1d53c5101
commit c421b040d5
2 changed files with 112 additions and 0 deletions

View File

@ -106,6 +106,9 @@ func New(l_lexer *lexer.Lexer) *Parser {
// IF Expression
l_parser.register_prefix(token.IF, l_parser.parse_if_expression)
// Function Literals
l_parser.register_prefix(token.FUNCTION, l_parser.parse_function_literal)
return l_parser
}
@ -344,3 +347,42 @@ func (l_parser *Parser) parse_block_statement() *ast.BlockStatement {
}
return block
}
func (l_parser *Parser) parse_function_literal() ast.Expression {
literal := &ast.FunctionLiteral{ Token: l_parser.current_token }
if !l_parser.expect_peek(token.LPAREN) {
return nil
}
literal.Parameters = l_parser.parse_function_parameters()
if !l_parser.expect_peek(token.LBRACE) {
return nil
}
literal.Body = l_parser.parse_block_statement()
return literal
}
func (l_parser *Parser) parse_function_parameters() []*ast.Identifier {
identifiers := []*ast.Identifier{}
if l_parser.peek_token_is(token.RPAREN) {
l_parser.next_token()
return identifiers
}
l_parser.next_token()
ident := &ast.Identifier{Token: l_parser.current_token, Value: l_parser.current_token.Literal}
identifiers = append(identifiers, ident)
for l_parser.peek_token_is(token.COMMA) {
l_parser.next_token()
l_parser.next_token()
ident := &ast.Identifier{Token: l_parser.current_token, Value: l_parser.current_token.Literal}
identifiers = append(identifiers, ident)
}
if !l_parser.expect_peek(token.RPAREN) {
return nil
}
return identifiers
}

View File

@ -453,6 +453,76 @@ func TestIfElseExpression(l_test *testing.T) {
}
}
func TestFunctionLiteralParsing(l_test *testing.T) {
input := `fn(x, y) { x + y; }`
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("program.Statements[0] is not ast.ExpressionStatement, got=%T", program.Statements[0])
}
function, ok := statement.Expression.(*ast.FunctionLiteral)
if !ok {
l_test.Fatalf("statement.Expression is not ast.FunctionLiteral, got=%T", statement.Expression)
}
if len(function.Parameters) != 2 {
l_test.Fatalf("function literal parameters wrong, want 2, got=%d\n", len(function.Parameters))
}
testLiteralExpression(l_test, function.Parameters[0], "x")
testLiteralExpression(l_test, function.Parameters[1], "y")
if len(function.Body.Statements) != 1 {
l_test.Fatalf("function.Body.Statements does not have 1 statement, got=%d\n", len(function.Body.Statements))
}
body_statement, ok := function.Body.Statements[0].(*ast.ExpressionStatement)
if !ok {
l_test.Fatalf("function body statement is not ast.ExpressionStatemes, got=%T", function.Body.Statements[0])
}
testInfixExpression(l_test, body_statement.Expression, "x", "+", "y")
}
func TestFunctionParameterParsing(l_test *testing.T){
tests := []struct {
input string
expected_params []string
}{
{ input: "fn() {};", expected_params: []string{}},
{ input: "fn(x) {};", expected_params: []string{"x"}},
{ input: "fn(x, y, z) {};", expected_params: []string{"x", "y", "z"}},
}
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)
function := statement.Expression.(*ast.FunctionLiteral)
if len(function.Parameters) != len(tt.expected_params){
l_test.Errorf("length of parameters is wrong, want %d, got=%d\n",
len(tt.expected_params), len(function.Parameters))
}
for i, identifier := range tt.expected_params {
testLiteralExpression(l_test, function.Parameters[i], identifier)
}
}
}
// Helpers
func check_parser_errors(l_test *testing.T, l_parser *Parser) {