diff --git a/parser/parser.go b/parser/parser.go index 0049bc1..8e24666 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,52 @@ func (p *Parser) parseHashLiteral() ast.Expression { return hash } -// parseMethodCallExpression parses an object-based method-call. +// 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 { - methodCall := &ast.ObjectCallExpression{Token: p.curToken, Object: obj} + currentToken := p.curToken p.nextToken() - name := p.parseIdentifier() + + // Handle index expressions if the next token is not a left parenthesis. + if p.peekToken.Type != token.LPAREN { + return &ast.IndexExpression{ + Token: currentToken, + Left: obj, + Index: p.parseDotIndexExpression(), + } + } + + // For method calls, parse the method name and the call expression. + methodName := p.parseIdentifier() p.nextToken() - methodCall.Call = p.parseCallExpression(name) - return methodCall + return &ast.ObjectCallExpression{ + Token: currentToken, + Object: obj, + Call: p.parseCallExpression(methodName), + } +} + +// 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. 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)