aboutsummaryrefslogtreecommitdiff
path: root/pkg/lang/parser
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/lang/parser')
-rw-r--r--pkg/lang/parser/exprs.go113
-rw-r--r--pkg/lang/parser/parser_test.go49
-rw-r--r--pkg/lang/parser/stmts.go2
3 files changed, 122 insertions, 42 deletions
diff --git a/pkg/lang/parser/exprs.go b/pkg/lang/parser/exprs.go
index 80b0fd6..48d1a8c 100644
--- a/pkg/lang/parser/exprs.go
+++ b/pkg/lang/parser/exprs.go
@@ -65,7 +65,7 @@ func (p *Parser) parseUnaryExpr() (ast.Expr, error) {
func (p *Parser) parsePostfixExpr() (ast.Expr, error) {
// PostfixExpr = SubscriptionExpr | CallExpr
- obj, err := p.parseUnitExpr()
+ obj, err := p.parseMemberExpr()
if err != nil {
return ast.Expr{}, err
}
@@ -81,42 +81,41 @@ func (p *Parser) parsePostfixExpr() (ast.Expr, error) {
}
func (p *Parser) parseArrayIndexExpr(obj ast.Expr) (ast.Expr, error) {
- // SubscriptionExpr = UnitExpr ( "[" Expr "]" )*
+ // SubscriptionExpr = MemberExpr ( "[" Expr "]" )*
- if _, err := p.expect(token.LBracket); err != nil {
- return ast.Expr{}, err
- }
+ for p.peek().Kind == token.LBracket {
+ p.next()
- index, err := p.parseExpr()
- if err != nil {
- return ast.Expr{}, err
- }
+ index, err := p.parseExpr()
+ if err != nil {
+ return ast.Expr{}, err
+ }
- if _, err := p.expect(token.RBracket); err != nil {
- return ast.Expr{}, err
- }
+ if _, err := p.expect(token.RBracket); err != nil {
+ return ast.Expr{}, err
+ }
- return ast.Expr{
- At: obj.At,
- Kind: ast.ExprKindSubscription,
- Value: ast.ExprSubscription{
- Obj: obj,
- Key: index,
- },
- }, nil
+ obj = ast.Expr{
+ At: obj.At,
+ Kind: ast.ExprKindSubscription,
+ Value: ast.ExprSubscription{
+ Obj: obj,
+ Key: index,
+ },
+ }
+ }
+ return obj, nil
}
func (p *Parser) parseCallExpr(callee ast.Expr) (ast.Expr, error) {
- // CallExpr = UnitExpr ( "(" ( Expr ( "," Expr )* )? ")" )*
+ // CallExpr = MemberExpr ( "(" ( Expr ( "," Expr )* )? ")" )*
- if _, err := p.expect(token.LParen); err != nil {
- return ast.Expr{}, err
- }
+ for p.peek().Kind == token.LParen {
+ p.next()
- args := []ast.Expr{}
- if p.peek().Kind != token.RParen {
- for {
+ args := []ast.Expr{}
+ for p.peek().Kind != token.RParen {
arg, err := p.parseExpr()
if err != nil {
return ast.Expr{}, err
@@ -124,29 +123,61 @@ func (p *Parser) parseCallExpr(callee ast.Expr) (ast.Expr, error) {
args = append(args, arg)
- if p.peek().Kind == token.RParen {
+ if p.peek().Kind == token.Comma {
+ p.next()
+ } else {
break
}
+ }
- if _, err := p.expect(token.Comma); err != nil {
- return ast.Expr{}, err
- }
+ if _, err := p.expect(token.RParen); err != nil {
+ return ast.Expr{}, err
+ }
+
+ callee = ast.Expr{
+ At: callee.At,
+ Kind: ast.ExprKindCall,
+ Value: ast.ExprCall{
+ Callee: callee,
+ Args: args,
+ },
}
}
- if _, err := p.expect(token.RParen); err != nil {
+ return callee, nil
+}
+
+func (p *Parser) parseMemberExpr() (ast.Expr, error) {
+ // MemberExpr = UnitExpr ( "." Ident )*
+
+ left, err := p.parseUnitExpr()
+ if err != nil {
return ast.Expr{}, err
}
- return ast.Expr{
- At: callee.At,
- Kind: ast.ExprKindCall,
- Value: ast.ExprCall{
- Callee: callee,
- Args: args,
- },
- }, nil
+ for {
+ if p.peek().Kind != token.Dot {
+ break
+ }
+
+ p.next()
+
+ node, err := p.parseIdent()
+ if err != nil {
+ return ast.Expr{}, err
+ }
+ left = ast.Expr{
+ At: left.At,
+ Kind: ast.ExprKindMember,
+ Value: ast.ExprMember{
+ Obj: left,
+ Key: node,
+ },
+ }
+ }
+
+ return left, nil
}
func (p *Parser) parseUnitExpr() (ast.Expr, error) {
@@ -176,7 +207,7 @@ func (p *Parser) parseUnitExpr() (ast.Expr, error) {
case token.KwTrue, token.KwFalse, token.KwNull, token.KwThis:
return p.parseValueLitExpr()
default:
- panic(fmt.Errorf("unexpected token '%v', wanted unit expression start", p.peek()))
+ panic(fmt.Errorf("unexpected token '%v', wanted unit expression start", p.peek().Kind))
}
}
diff --git a/pkg/lang/parser/parser_test.go b/pkg/lang/parser/parser_test.go
index de97928..24f3044 100644
--- a/pkg/lang/parser/parser_test.go
+++ b/pkg/lang/parser/parser_test.go
@@ -370,6 +370,55 @@ func TestMultipleStmts(t *testing.T) {
require.Equal(t, expected, program.Stmts)
}
+func TestRepeatingNestedExpr(t *testing.T) {
+ src := "x.y.z()()"
+
+ p := cheatWithScanner(t, src)
+ program, err := p.Parse()
+ require.NoError(t, err)
+
+ require.Equal(t, 1, len(program.Stmts))
+ require.Equal(t, ast.Stmt{
+ At: source.NewLoc(0, 0),
+ Kind: ast.StmtKindExpr,
+ Value: ast.StmtExpr{
+ Value: ast.Expr{
+ At: source.NewLoc(0, 0),
+ Kind: ast.ExprKindCall,
+ Value: ast.ExprCall{
+ Callee: ast.Expr{
+ At: source.NewLoc(0, 0),
+ Kind: ast.ExprKindCall,
+ Value: ast.ExprCall{
+ Callee: ast.Expr{
+ At: source.NewLoc(0, 0),
+ Kind: ast.ExprKindMember,
+ Value: ast.ExprMember{
+ Obj: ast.Expr{
+ At: source.NewLoc(0, 0),
+ Kind: ast.ExprKindMember,
+ Value: ast.ExprMember{
+ Obj: ast.Expr{
+ At: source.NewLoc(0, 0),
+ Kind: ast.ExprKindIdent,
+ Value: ast.ExprIdent{Value: ast.IdentNode{At: source.NewLoc(0, 0), Value: "x"}},
+ },
+ Key: ast.IdentNode{At: source.NewLoc(0, 2), Value: "y"},
+ },
+ },
+ Key: ast.IdentNode{At: source.NewLoc(0, 4), Value: "z"},
+ },
+ },
+ Args: []ast.Expr{},
+ },
+ },
+ Args: []ast.Expr{},
+ },
+ },
+ },
+ }, program.Stmts[0])
+}
+
func TestIfStmt(t *testing.T) {
src := sourceify(
`if false {`,
diff --git a/pkg/lang/parser/stmts.go b/pkg/lang/parser/stmts.go
index a0bba28..33719ce 100644
--- a/pkg/lang/parser/stmts.go
+++ b/pkg/lang/parser/stmts.go
@@ -546,7 +546,7 @@ func (p *Parser) parseEmptyStmt() (ast.Stmt, error) {
func (p *Parser) parseStmtEnd() error {
tok := p.peek()
if !tok.CanEndStmt() {
- panic(fmt.Errorf("wanted statement end, received: '%v'", tok))
+ return fmt.Errorf("wanted statement end, received: '%v'", tok.Kind)
}
p.next()
return nil