diff options
| author | Bobby <[email protected]> | 2024-04-04 14:46:58 +0000 |
|---|---|---|
| committer | Bobby <[email protected]> | 2024-04-04 14:46:58 +0000 |
| commit | 3d4f24f7c4ea05471109c0b13abbc95e70c6924b (patch) | |
| tree | 5f4d07cf04336ff25e64b8ffc76a821e1b1861f5 | |
| parent | 98b3baa1d9d05551948b6657b4130cf05c11934d (diff) | |
| download | mana-3d4f24f7c4ea05471109c0b13abbc95e70c6924b.tar.xz mana-3d4f24f7c4ea05471109c0b13abbc95e70c6924b.zip | |
parsing array expressions
| -rw-r--r-- | ast/ast.go | 23 | ||||
| -rw-r--r-- | lexer/lexer.go | 4 | ||||
| -rw-r--r-- | lexer/lexer_test.go | 7 | ||||
| -rw-r--r-- | parser/parser.go | 36 | ||||
| -rw-r--r-- | parser/parser_test.go | 25 | ||||
| -rw-r--r-- | tokens/tokens.go | 10 |
6 files changed, 100 insertions, 5 deletions
@@ -306,3 +306,26 @@ type StringLiteral struct { func (sl *StringLiteral) expressionNode() {} func (sl *StringLiteral) TokenLiteral() string { return sl.Token.Literal } func (sl *StringLiteral) String() string { return sl.Token.Literal } + +type ArrayLiteral struct { + Token tokens.Token // the '[' token + Elements []Expression +} + +func (al *ArrayLiteral) expressionNode() {} +func (al *ArrayLiteral) TokenLiteral() string { return al.Token.Literal } +func (al *ArrayLiteral) String() string { + var out bytes.Buffer + + elements := []string{} + + for _, el := range al.Elements { + elements = append(elements, el.String()) + } + + out.WriteString("[") + out.WriteString(strings.Join(elements, ", ")) + out.WriteString("]") + + return out.String() +} diff --git a/lexer/lexer.go b/lexer/lexer.go index 5774120..73326b3 100644 --- a/lexer/lexer.go +++ b/lexer/lexer.go @@ -65,6 +65,10 @@ func (l *Lexer) NextToken() tokens.Token { tok = newToken(tokens.LBRACE, l.ch) case '}': tok = newToken(tokens.RBRACE, l.ch) + case '[': + tok = newToken(tokens.LBRACKET, l.ch) + case ']': + tok = newToken(tokens.RBRACKET, l.ch) case '"': tok.Type = tokens.STRING tok.Literal = l.readString() diff --git a/lexer/lexer_test.go b/lexer/lexer_test.go index 7df8db2..bac1102 100644 --- a/lexer/lexer_test.go +++ b/lexer/lexer_test.go @@ -29,6 +29,7 @@ func TestNextToken(t *testing.T) { 10 != 9; "foobar" "foo bar" + [1, 2]; ` var tests = []struct { @@ -110,6 +111,12 @@ func TestNextToken(t *testing.T) { {tokens.SEMICOLON, ";"}, {tokens.STRING, "foobar"}, {tokens.STRING, "foo bar"}, + {tokens.LBRACKET, "["}, + {tokens.INT, "1"}, + {tokens.COMMA, ","}, + {tokens.INT, "2"}, + {tokens.RBRACKET, "]"}, + {tokens.SEMICOLON, ";"}, {tokens.EOF, ""}, } diff --git a/parser/parser.go b/parser/parser.go index 6ca88b3..a4472b2 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -72,6 +72,7 @@ func New(l *lexer.Lexer) *Parser { p.registerPrefix(tokens.FUNCTION, p.parseFunctionLiteral) p.registerPrefix(tokens.LPAREN, p.parseGroupedExpression) p.registerPrefix(tokens.STRING, p.parseStringLiteral) + p.registerPrefix(tokens.LBRACKET, p.parseArrayLiteral) // Initialize the infix parse functions. p.infixParseFns = make(map[tokens.TokenType]infixParseFn) @@ -407,7 +408,7 @@ func (p *Parser) parseGroupedExpression() ast.Expression { // parseCallExpression parses a call expression. func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression { exp := &ast.CallExpression{Token: p.curToken, Function: function} - exp.Arguments = p.parseCallArguments() + exp.Arguments = p.parseExpressionList(tokens.RPAREN) return exp } @@ -444,6 +445,39 @@ func (p *Parser) parseStringLiteral() ast.Expression { } } +func (p *Parser) parseArrayLiteral() ast.Expression { + array := &ast.ArrayLiteral{Token: p.curToken} + + array.Elements = p.parseExpressionList(tokens.RBRACKET) + + return array +} + +func (p *Parser) parseExpressionList(end tokens.TokenType) []ast.Expression { + list := []ast.Expression{} + + if p.peekTokenIs(end) { + p.nextToken() + return list + } + + p.nextToken() + + list = append(list, p.parseExpression(LOWEST)) + + for p.peekTokenIs(tokens.COMMA) { + p.nextToken() + p.nextToken() + list = append(list, p.parseExpression(LOWEST)) + } + + if !p.expectPeek(end) { + return nil + } + + return list +} + // 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 3a574a7..f1b8b8c 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -791,3 +791,28 @@ func TestStringLiteralExpression(t *testing.T) { t.Errorf("literal.Value not %q. got=%q", "hello world", literal.Value) } } + +func TestParsingArrayLiterals(t *testing.T) { + input := "[1, 2 * 2, 3 + 3]" + + 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) + array, ok := stmt.Expression.(*ast.ArrayLiteral) + + if !ok { + t.Fatalf("exp is not ast.ArrayLiteral. got=%T", stmt.Expression) + } + + if len(array.Elements) != 3 { + t.Fatalf("len(array.Elements) not 3. got=%d", len(array.Elements)) + } + + testIntegerLiteral(t, array.Elements[0], 1) + testInfixExpression(t, array.Elements[1], 2, "*", 2) + testInfixExpression(t, array.Elements[2], 3, "+", 3) +} diff --git a/tokens/tokens.go b/tokens/tokens.go index 64f44fa..aac82d3 100644 --- a/tokens/tokens.go +++ b/tokens/tokens.go @@ -32,10 +32,12 @@ const ( COMMA = "," SEMICOLON = ";" - LPAREN = "(" - RPAREN = ")" - LBRACE = "{" - RBRACE = "}" + LPAREN = "(" + RPAREN = ")" + LBRACE = "{" + RBRACE = "}" + LBRACKET = "[" + RBRACKET = "]" // Keywords FUNCTION = "FUNCTION" |
