diff options
Diffstat (limited to 'parser')
| -rw-r--r-- | parser/parser.go | 64 | ||||
| -rw-r--r-- | parser/parser_test.go | 43 | ||||
| -rw-r--r-- | parser/parser_tracing.go | 54 |
3 files changed, 110 insertions, 51 deletions
diff --git a/parser/parser.go b/parser/parser.go index a4472b2..4fa36b1 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -18,6 +18,7 @@ const ( PRODUCT // * PREFIX // -X or !X CALL // myFunction(X) + INDEX // array[index] ) var precedences = map[tokens.TokenType]int{ @@ -30,6 +31,7 @@ var precedences = map[tokens.TokenType]int{ tokens.SLASH: PRODUCT, tokens.ASTERISK: PRODUCT, tokens.LPAREN: CALL, + tokens.LBRACKET: INDEX, } type ( @@ -85,6 +87,7 @@ func New(l *lexer.Lexer) *Parser { p.registerInfix(tokens.LT, p.parseInfixExpression) p.registerInfix(tokens.GT, p.parseInfixExpression) p.registerInfix(tokens.LPAREN, p.parseCallExpression) + p.registerInfix(tokens.LBRACKET, p.parseIndexExpression) return p } @@ -117,9 +120,9 @@ func (p *Parser) ParseProgram() *ast.Program { // Parse the statement and append it to the program. var stmt ast.Statement = p.parseStatement() - if stmt != nil { - program.Statements = append(program.Statements, stmt) - } + // if stmt != nil { + program.Statements = append(program.Statements, stmt) + // } // Advance to the next token. p.nextToken() @@ -334,9 +337,9 @@ func (p *Parser) parseBlockStatement() *ast.BlockStatement { for !p.curTokenIs(tokens.RBRACE) && !p.curTokenIs(tokens.EOF) { stmt := p.parseStatement() - if stmt != nil { - block.Statements = append(block.Statements, stmt) - } + // if stmt != nil { + block.Statements = append(block.Statements, stmt) + // } p.nextToken() } @@ -414,29 +417,29 @@ func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression { } // parseCallArguments parses call arguments. -func (p *Parser) parseCallArguments() []ast.Expression { - args := []ast.Expression{} +// func (p *Parser) parseCallArguments() []ast.Expression { +// args := []ast.Expression{} - if p.peekTokenIs(tokens.RPAREN) { - p.nextToken() - return args - } +// if p.peekTokenIs(tokens.RPAREN) { +// p.nextToken() +// return args +// } - p.nextToken() - args = append(args, p.parseExpression(LOWEST)) +// p.nextToken() +// args = append(args, p.parseExpression(LOWEST)) - for p.peekTokenIs(tokens.COMMA) { - p.nextToken() - p.nextToken() - args = append(args, p.parseExpression(LOWEST)) - } +// for p.peekTokenIs(tokens.COMMA) { +// p.nextToken() +// p.nextToken() +// args = append(args, p.parseExpression(LOWEST)) +// } - if !p.expectPeek(tokens.RPAREN) { - return nil - } +// if !p.expectPeek(tokens.RPAREN) { +// return nil +// } - return args -} +// return args +// } func (p *Parser) parseStringLiteral() ast.Expression { return &ast.StringLiteral{ @@ -478,6 +481,19 @@ func (p *Parser) parseExpressionList(end tokens.TokenType) []ast.Expression { return list } +func (p *Parser) parseIndexExpression(left ast.Expression) ast.Expression { + exp := &ast.IndexExpression{Token: p.curToken, Left: left} + + p.nextToken() + exp.Index = p.parseExpression(LOWEST) + + if !p.expectPeek(tokens.RBRACKET) { + return nil + } + + return exp +} + // curTokenIs returns true if the current token is of the given type. func (p *Parser) curTokenIs(t tokens.TokenType) bool { return p.curToken.Type == t diff --git a/parser/parser_test.go b/parser/parser_test.go index f1b8b8c..7b347f5 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -519,6 +519,14 @@ func TestOperatorPrecedenceParsing(t *testing.T) { "add(a + b + c * d / f + g)", "add((((a + b) + ((c * d) / f)) + g))", }, + { + "a * [1, 2, 3, 4][b * c] * d", + "((a * ([1, 2, 3, 4][(b * c)])) * d)", + }, + { + "add(a * b[2], b[1], 2 * [1, 2][1])", + "add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))", + }, } for _, tt := range tests { @@ -802,6 +810,11 @@ func TestParsingArrayLiterals(t *testing.T) { checkParserErrors(t, p) stmt, ok := program.Statements[0].(*ast.ExpressionStatement) + + if !ok { + t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0]) + } + array, ok := stmt.Expression.(*ast.ArrayLiteral) if !ok { @@ -816,3 +829,33 @@ func TestParsingArrayLiterals(t *testing.T) { testInfixExpression(t, array.Elements[1], 2, "*", 2) testInfixExpression(t, array.Elements[2], 3, "+", 3) } + +func TestParsingIndexExpressions(t *testing.T) { + input := "myArray[1 + 1]" + + var l *lexer.Lexer = lexer.New(input) + var p *Parser = New(l) + + var program *ast.Program = p.ParseProgram() + checkParserErrors(t, p) + + stmt, ok := program.Statements[0].(*ast.ExpressionStatement) + + if !ok { + t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0]) + } + + indexExp, ok := stmt.Expression.(*ast.IndexExpression) + + if !ok { + t.Fatalf("exp is not ast.IndexExpression. got=%T", stmt.Expression) + } + + if !testIdentifier(t, indexExp.Left, "myArray") { + return + } + + if !testInfixExpression(t, indexExp.Index, 1, "+", 1) { + return + } +} diff --git a/parser/parser_tracing.go b/parser/parser_tracing.go index 2f3fcdb..76a8c45 100644 --- a/parser/parser_tracing.go +++ b/parser/parser_tracing.go @@ -1,37 +1,37 @@ package parser -import ( - "fmt" - "strings" -) +// import ( +// "fmt" +// "strings" +// ) -var traceLevel int = 0 +// var traceLevel int = 0 -const traceIdentPlaceholder string = "\t" +// const traceIdentPlaceholder string = "\t" -func identLevel() string { - return strings.Repeat(traceIdentPlaceholder, traceLevel-1) -} +// func identLevel() string { +// return strings.Repeat(traceIdentPlaceholder, traceLevel-1) +// } -func tracePrint(fs string) { - fmt.Printf("%s%s\n", identLevel(), fs) -} +// func tracePrint(fs string) { +// fmt.Printf("%s%s\n", identLevel(), fs) +// } -func incIdent() { - traceLevel = traceLevel + 1 -} +// func incIdent() { +// traceLevel = traceLevel + 1 +// } -func decIdent() { - traceLevel = traceLevel - 1 -} +// func decIdent() { +// traceLevel = traceLevel - 1 +// } -func trace(msg string) string { - incIdent() - tracePrint("BEGIN " + msg) - return msg -} +// func trace(msg string) string { +// incIdent() +// tracePrint("BEGIN " + msg) +// return msg +// } -func untrace(msg string) { - tracePrint("END " + msg) - decIdent() -} +// func untrace(msg string) { +// tracePrint("END " + msg) +// decIdent() +// } |
