aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-04-04 14:46:58 +0000
committerBobby <[email protected]>2024-04-04 14:46:58 +0000
commit3d4f24f7c4ea05471109c0b13abbc95e70c6924b (patch)
tree5f4d07cf04336ff25e64b8ffc76a821e1b1861f5
parent98b3baa1d9d05551948b6657b4130cf05c11934d (diff)
downloadmana-3d4f24f7c4ea05471109c0b13abbc95e70c6924b.tar.xz
mana-3d4f24f7c4ea05471109c0b13abbc95e70c6924b.zip
parsing array expressions
-rw-r--r--ast/ast.go23
-rw-r--r--lexer/lexer.go4
-rw-r--r--lexer/lexer_test.go7
-rw-r--r--parser/parser.go36
-rw-r--r--parser/parser_test.go25
-rw-r--r--tokens/tokens.go10
6 files changed, 100 insertions, 5 deletions
diff --git a/ast/ast.go b/ast/ast.go
index 0fe7985..9473f1a 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -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"