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:
parent
c421b040d5
commit
7e34164019
25
ast/ast.go
25
ast/ast.go
@ -261,3 +261,28 @@ func (fl *FunctionLiteral) String() 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()
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ var precedences = map[token.TokenType]int{
|
||||
token.MINUS: SUM,
|
||||
token.SLASH: PRODUCT,
|
||||
token.ASTERISK: PRODUCT,
|
||||
token.LPAREN: CALL,
|
||||
}
|
||||
|
||||
func (l_parser *Parser) peek_precedence() int {
|
||||
@ -109,6 +110,9 @@ func New(l_lexer *lexer.Lexer) *Parser {
|
||||
// Function Literals
|
||||
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
|
||||
}
|
||||
|
||||
@ -349,7 +353,7 @@ func (l_parser *Parser) parse_block_statement() *ast.BlockStatement {
|
||||
}
|
||||
|
||||
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) {
|
||||
return nil
|
||||
}
|
||||
@ -358,6 +362,7 @@ func (l_parser *Parser) parse_function_literal() ast.Expression {
|
||||
if !l_parser.expect_peek(token.LBRACE) {
|
||||
return nil
|
||||
}
|
||||
|
||||
literal.Body = l_parser.parse_block_statement()
|
||||
return literal
|
||||
}
|
||||
@ -386,3 +391,32 @@ func (l_parser *Parser) parse_function_parameters() []*ast.Identifier {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -310,6 +310,18 @@ func TestOperatorPrecedenceParsing(l_test *testing.T) {
|
||||
"!(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 {
|
||||
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 {
|
||||
input string
|
||||
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"}},
|
||||
{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 {
|
||||
@ -512,7 +524,7 @@ func TestFunctionParameterParsing(l_test *testing.T){
|
||||
statement := program.Statements[0].(*ast.ExpressionStatement)
|
||||
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",
|
||||
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
|
||||
|
||||
func check_parser_errors(l_test *testing.T, l_parser *Parser) {
|
||||
|
Loading…
Reference in New Issue
Block a user