aboutsummaryrefslogtreecommitdiff
path: root/pkg/lang
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-07-28 21:18:09 +0000
committerMel <einebeere@gmail.com>2022-07-28 21:18:09 +0000
commit895120776a0ae4dd121029f1730d29ca886e201e (patch)
tree98e4771fd83ec88d559de95f058f724747405f1a /pkg/lang
parentd79973cb9df8660fe89810507557f5ba86256f30 (diff)
downloadjinx-895120776a0ae4dd121029f1730d29ca886e201e.tar.zst
jinx-895120776a0ae4dd121029f1730d29ca886e201e.zip
Fix nested stacked exprs and parse members
Diffstat (limited to 'pkg/lang')
-rw-r--r--pkg/lang/ast/expr.go6
-rw-r--r--pkg/lang/ast/op.go8
-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
5 files changed, 128 insertions, 50 deletions
diff --git a/pkg/lang/ast/expr.go b/pkg/lang/ast/expr.go
index b0ed599..aceed18 100644
--- a/pkg/lang/ast/expr.go
+++ b/pkg/lang/ast/expr.go
@@ -11,6 +11,7 @@ const (
ExprKindUnary
ExprKindCall
ExprKindSubscription
+ ExprKindMember
ExprKindGroup
ExprKindFnLit
@@ -57,6 +58,11 @@ type ExprSubscription struct {
Key Expr
}
+type ExprMember struct {
+ Obj Expr
+ Key IdentNode
+}
+
type ExprGroup struct {
Value Expr
}
diff --git a/pkg/lang/ast/op.go b/pkg/lang/ast/op.go
index 063a309..6f48e0c 100644
--- a/pkg/lang/ast/op.go
+++ b/pkg/lang/ast/op.go
@@ -21,8 +21,6 @@ const (
BinOpLte
BinOpGt
BinOpGte
-
- BinOpDot
)
func BinOpFromToken(tok token.Token) (BinOp, bool) {
@@ -52,8 +50,6 @@ func BinOpFromToken(tok token.Token) (BinOp, bool) {
op = BinOpGt
case token.Gte:
op = BinOpGte
- case token.Dot:
- op = BinOpDot
default:
return 0, false
}
@@ -71,8 +67,6 @@ func (op BinOp) Precedence() int {
return 3
case BinOpStar, BinOpSlash, BinOpPercent:
return 4
- case BinOpDot:
- return 5
default:
panic(fmt.Sprintf("unknown binary operator: %d", op))
@@ -94,8 +88,6 @@ func (op BinOp) Associativity() Associativity {
return AssociativityRight
case BinOpEq, BinOpNeq, BinOpLt, BinOpLte, BinOpGt, BinOpGte:
return AssociativityLeft
- case BinOpDot:
- return AssociativityLeft
default:
panic(fmt.Sprintf("unknown binary operator: %d", op))
}
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