From 5c5cd1defd937db7ec5f63e80d8e493345f50db3 Mon Sep 17 00:00:00 2001 From: ckganesan Date: Tue, 5 Dec 2023 20:47:51 +0530 Subject: [PATCH 1/3] feat: Add Support for Accessing Index Values Using the Dot Operator --- parser/parser.go | 31 +++++++++++++++++++++++++------ parser/parser_test.go | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/parser/parser.go b/parser/parser.go index 0049bc1..8a6202d 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -558,11 +558,11 @@ func (p *Parser) parseTernaryExpression(condition ast.Expression) ast.Expression Token: p.curToken, Condition: condition, } - p.nextToken() //skip the '?' + p.nextToken() // skip the '?' precedence := p.curPrecedence() expression.IfTrue = p.parseExpression(precedence) - if !p.expectPeek(token.COLON) { //skip the ":" + if !p.expectPeek(token.COLON) { // skip the ":" return nil } @@ -999,14 +999,33 @@ func (p *Parser) parseHashLiteral() ast.Expression { return hash } -// parseMethodCallExpression parses an object-based method-call. +// parseMethodCallExpression parses an object-based method call expression. func (p *Parser) parseMethodCallExpression(obj ast.Expression) ast.Expression { - methodCall := &ast.ObjectCallExpression{Token: p.curToken, Object: obj} + currentToken := p.curToken p.nextToken() + + // If the next token is not a left parenthesis, it's an index expression. + if p.peekToken.Type != token.LPAREN { + var index ast.Expression + + // If the current token is an identifier, treat it as a string literal index. + if p.curToken.Type == token.IDENT { + // Create a string literal index token. + indexToken := token.Token{Type: token.STRING, Literal: p.curToken.Literal} + index = &ast.StringLiteral{Token: indexToken, Value: indexToken.Literal} + } else { + // Otherwise, parse the index expression. + index = p.parseExpression(LOWEST) + } + // Return an index expression. + return &ast.IndexExpression{Token: currentToken, Left: obj, Index: index} + } + + // Parse the method name as an identifier. name := p.parseIdentifier() p.nextToken() - methodCall.Call = p.parseCallExpression(name) - return methodCall + // Parse the method call expression and return an object call expression. + return &ast.ObjectCallExpression{Token: currentToken, Object: obj, Call: p.parseCallExpression(name)} } // curTokenIs tests if the current token has the given type. diff --git a/parser/parser_test.go b/parser/parser_test.go index 5d264c5..1110e50 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -60,7 +60,7 @@ func TestBadLetConstStatement(t *testing.T) { } } -//TestConstStatements tests the "const" token. +// TestConstStatements tests the "const" token. func TestConstStatements(t *testing.T) { tests := []struct { input string @@ -751,6 +751,46 @@ func TestParsingIndexExpression(t *testing.T) { } } +func TestParsingIndexExpressionByDot(t *testing.T) { + input := `myArray.name; + myArray.2` + l := lexer.New(input) + p := New(l) + program := p.ParseProgram() + checkParserErrors(t, p) + stmt, _ := program.Statements[0].(*ast.ExpressionStatement) + indexExp, ok := stmt.Expression.(*ast.IndexExpression) + if !ok { + t.Fatalf("exp not *ast.IndexExpression. got=%T", stmt.Expression) + } + if !testIdentifier(t, indexExp.Left, "myArray") { + return + } + literal, ok := indexExp.Index.(*ast.StringLiteral) + if !ok { + t.Fatalf("exp not *ast.StringLiteral. got=%T", indexExp.Index) + } + if literal.Value != "name" { + t.Errorf("literal.Value not %q, got=%q", "name", literal.Value) + } + + stmt, _ = program.Statements[1].(*ast.ExpressionStatement) + indexExp, ok = stmt.Expression.(*ast.IndexExpression) + if !ok { + t.Fatalf("exp not *ast.IndexExpression. got=%T", stmt.Expression) + } + if !testIdentifier(t, indexExp.Left, "myArray") { + return + } + bLiteral, ok := indexExp.Index.(*ast.IntegerLiteral) + if !ok { + t.Fatalf("exp not *ast.IntegerLiteral. got=%T", indexExp.Index) + } + if bLiteral.Value != 2 { + t.Errorf("literal.Value not %q, got=%q", "2", bLiteral.Value) + } +} + func TestParsingHashLiteral(t *testing.T) { input := `{"one":1, "two":2, "three":3}` l := lexer.New(input) From 7b61a23c847f55f31ee9ad2cc7cb85b538c4f72b Mon Sep 17 00:00:00 2001 From: Ganesan Karuppasamy Date: Wed, 6 Dec 2023 16:55:30 +0530 Subject: [PATCH 2/3] feat: Add Support for Accessing Index Values Using the Dot Operator --- parser/parser.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/parser/parser.go b/parser/parser.go index 8a6202d..dc2903f 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -1008,15 +1008,22 @@ func (p *Parser) parseMethodCallExpression(obj ast.Expression) ast.Expression { if p.peekToken.Type != token.LPAREN { var index ast.Expression - // If the current token is an identifier, treat it as a string literal index. - if p.curToken.Type == token.IDENT { - // Create a string literal index token. + switch p.curToken.Type { + case token.IDENT: + // If the current token is an identifier, treat it as a string literal index. indexToken := token.Token{Type: token.STRING, Literal: p.curToken.Literal} index = &ast.StringLiteral{Token: indexToken, Value: indexToken.Literal} - } else { + case token.INT: + // If it's an integer, parse it as an integer literal expression. + index = p.parseIntegerLiteral() + case token.TRUE, token.FALSE: + // If it's a boolean literal, parse it as a boolean expression. + index = p.parseBoolean() + default: // Otherwise, parse the index expression. index = p.parseExpression(LOWEST) } + // Return an index expression. return &ast.IndexExpression{Token: currentToken, Left: obj, Index: index} } @@ -1024,6 +1031,7 @@ func (p *Parser) parseMethodCallExpression(obj ast.Expression) ast.Expression { // Parse the method name as an identifier. name := p.parseIdentifier() p.nextToken() + // Parse the method call expression and return an object call expression. return &ast.ObjectCallExpression{Token: currentToken, Object: obj, Call: p.parseCallExpression(name)} } From df724cbf5dbb6ba700fc3d3fe4f5196049ae3342 Mon Sep 17 00:00:00 2001 From: ckganesan Date: Wed, 6 Dec 2023 17:24:09 +0530 Subject: [PATCH 3/3] feat: Add Support for Accessing Index Values Using the Dot Operator --- parser/parser.go | 59 ++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/parser/parser.go b/parser/parser.go index dc2903f..8e24666 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -1000,40 +1000,51 @@ func (p *Parser) parseHashLiteral() ast.Expression { } // parseMethodCallExpression parses an object-based method call expression. +// It distinguishes between index expressions and method calls based on the next token. func (p *Parser) parseMethodCallExpression(obj ast.Expression) ast.Expression { currentToken := p.curToken p.nextToken() - // If the next token is not a left parenthesis, it's an index expression. + // Handle index expressions if the next token is not a left parenthesis. if p.peekToken.Type != token.LPAREN { - var index ast.Expression - - switch p.curToken.Type { - case token.IDENT: - // If the current token is an identifier, treat it as a string literal index. - indexToken := token.Token{Type: token.STRING, Literal: p.curToken.Literal} - index = &ast.StringLiteral{Token: indexToken, Value: indexToken.Literal} - case token.INT: - // If it's an integer, parse it as an integer literal expression. - index = p.parseIntegerLiteral() - case token.TRUE, token.FALSE: - // If it's a boolean literal, parse it as a boolean expression. - index = p.parseBoolean() - default: - // Otherwise, parse the index expression. - index = p.parseExpression(LOWEST) + return &ast.IndexExpression{ + Token: currentToken, + Left: obj, + Index: p.parseDotIndexExpression(), } - - // Return an index expression. - return &ast.IndexExpression{Token: currentToken, Left: obj, Index: index} } - // Parse the method name as an identifier. - name := p.parseIdentifier() + // For method calls, parse the method name and the call expression. + methodName := p.parseIdentifier() p.nextToken() + return &ast.ObjectCallExpression{ + Token: currentToken, + Object: obj, + Call: p.parseCallExpression(methodName), + } +} - // Parse the method call expression and return an object call expression. - return &ast.ObjectCallExpression{Token: currentToken, Object: obj, Call: p.parseCallExpression(name)} +// parseDotIndexExpression handles parsing expressions that follow a dot (.) in an object. +// It supports identifiers, integers, and booleans, treating them appropriately. +func (p *Parser) parseDotIndexExpression() ast.Expression { + switch p.curToken.Type { + case token.IDENT: + // Treat identifiers as string literals for indexing purposes. + indexToken := token.Token{Type: token.STRING, Literal: p.curToken.Literal} + return &ast.StringLiteral{ + Token: indexToken, + Value: indexToken.Literal, + } + case token.INT: + // Parse integer literals. + return p.parseIntegerLiteral() + case token.TRUE, token.FALSE: + // Parse boolean literals. + return p.parseBoolean() + default: + // Handle other types of expressions. + return p.parseExpression(LOWEST) + } } // curTokenIs tests if the current token has the given type.