about summary refs log tree commit diff
path: root/pkg/lang/parser/exprs.go
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-04-21 03:19:06 +0200
committerMel <einebeere@gmail.com>2022-04-21 03:19:06 +0200
commitb5a9660b6ac42bce27c746e76013c3ce5992743a (patch)
tree0e9bd21a30aae25fe21a7a3994a48ac2cea8c955 /pkg/lang/parser/exprs.go
parent5267c0e8653b431cfd2c06212cdba4f22225bd02 (diff)
downloadjinx-b5a9660b6ac42bce27c746e76013c3ce5992743a.tar.zst
jinx-b5a9660b6ac42bce27c746e76013c3ce5992743a.zip
Lang parser prototype
Diffstat (limited to 'pkg/lang/parser/exprs.go')
-rw-r--r--pkg/lang/parser/exprs.go390
1 files changed, 390 insertions, 0 deletions
diff --git a/pkg/lang/parser/exprs.go b/pkg/lang/parser/exprs.go
new file mode 100644
index 0000000..b08864a
--- /dev/null
+++ b/pkg/lang/parser/exprs.go
@@ -0,0 +1,390 @@
+package parser
+
+import (
+	"fmt"
+	"jinx/pkg/lang/ast"
+	"jinx/pkg/lang/scanner/token"
+)
+
+func (p *Parser) parseExpr() (ast.Expr, error) {
+	return p.parseBinaryExpr()
+}
+
+func (p *Parser) parseBinaryExpr() (ast.Expr, error) {
+	// BinaryExpr = UnaryExpr (BinOp BinaryExpr)?
+
+	left, err := p.parseUnaryExpr()
+	if err != nil {
+		return ast.Expr{}, err
+	}
+
+	if op, ok := ast.BinOpFromToken(p.peek()); ok {
+		p.next()
+
+		right, err := p.parseBinaryExpr()
+		if err != nil {
+			return ast.Expr{}, err
+		}
+
+		// Check precedence and associativity.
+		if right.Kind == ast.ExprKindBinary {
+			rightBin := right.Value.(ast.ExprBinary)
+
+			needsSwitch := (op.Precedence() > rightBin.Op.Precedence()) ||
+				(op.Precedence() == rightBin.Op.Precedence() && op.Associativity() == ast.AssociativityLeft)
+
+			if needsSwitch {
+				left = ast.Expr{
+					At:   left.At,
+					Kind: ast.ExprKindBinary,
+					Value: ast.ExprBinary{
+						Left:  left,
+						Op:    op,
+						Right: rightBin.Left,
+					},
+				}
+
+				right = rightBin.Right
+
+				op = rightBin.Op
+			}
+		}
+
+		left = ast.Expr{
+			At:   left.At,
+			Kind: ast.ExprKindBinary,
+			Value: ast.ExprBinary{
+				Left:  left,
+				Op:    op,
+				Right: right,
+			},
+		}
+	}
+
+	return left, nil
+}
+
+func (p *Parser) parseUnaryExpr() (ast.Expr, error) {
+	// UnaryExpr = PostfixExpr (UnOp UnaryExpr)?
+	if op, ok := ast.UnOpFromToken(p.peek()); ok {
+		tok := p.next()
+
+		expr, err := p.parseUnaryExpr()
+		if err != nil {
+			return ast.Expr{}, err
+		}
+
+		return ast.Expr{
+			At:   tok.At,
+			Kind: ast.ExprKindUnary,
+			Value: ast.ExprUnary{
+				Op:    op,
+				Value: expr,
+			},
+		}, nil
+	}
+
+	return p.parsePostfixExpr()
+}
+
+func (p *Parser) parsePostfixExpr() (ast.Expr, error) {
+	// PostfixExpr = SubscriptionExpr | CallExpr
+
+	obj, err := p.parseUnitExpr()
+	if err != nil {
+		return ast.Expr{}, err
+	}
+
+	switch p.peek().Kind {
+	case token.LBracket:
+		return p.parseArrayIndexExpr(obj)
+	case token.LParen:
+		return p.parseCallExpr(obj)
+	default:
+		return obj, nil
+	}
+}
+
+func (p *Parser) parseArrayIndexExpr(obj ast.Expr) (ast.Expr, error) {
+	// SubscriptionExpr = UnitExpr ( "[" Expr "]" )*
+
+	if _, err := p.expect(token.LBracket); 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
+	}
+
+	return ast.Expr{
+		At:   obj.At,
+		Kind: ast.ExprKindSubscription,
+		Value: ast.ExprSubscription{
+			Obj: obj,
+			Key: index,
+		},
+	}, nil
+
+}
+
+func (p *Parser) parseCallExpr(callee ast.Expr) (ast.Expr, error) {
+	// CallExpr = UnitExpr ( "(" ( Expr ( "," Expr )* )? ")" )*
+
+	if _, err := p.expect(token.LParen); err != nil {
+		return ast.Expr{}, err
+	}
+
+	args := []ast.Expr{}
+	if p.peek().Kind != token.RParen {
+		for {
+			arg, err := p.parseExpr()
+			if err != nil {
+				return ast.Expr{}, err
+			}
+
+			args = append(args, arg)
+
+			if p.peek().Kind == token.RParen {
+				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
+	}
+
+	return ast.Expr{
+		At:   callee.At,
+		Kind: ast.ExprKindCall,
+		Value: ast.ExprCall{
+			Callee: callee,
+			Args:   args,
+		},
+	}, nil
+
+}
+
+func (p *Parser) parseUnitExpr() (ast.Expr, error) {
+	// UnitExpr = GroupExpr |
+	//        FnLitExpr     |
+	//        ArrayLitExpr  |
+	//        Ident         |
+	//        IntLitExpr    |
+	//        FloatLitExpr  |
+	//        StringLitExpr |
+	//        ValueLitExpr  |
+	switch p.peek().Kind {
+	case token.LParen:
+		return p.parseGroupExpr()
+	case token.KwFn:
+		return p.parseFnLitExpr()
+	case token.LBracket:
+		return p.parseArrayLitExpr()
+	case token.Ident:
+		return p.parseIdentExpr()
+	case token.Int:
+		return p.parseIntLitExpr()
+	case token.Float:
+		return p.parseFloatLitExpr()
+	case token.String:
+		return p.parseStringLitExpr()
+	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()))
+	}
+}
+
+func (p *Parser) parseGroupExpr() (ast.Expr, error) {
+	// GroupExpr = "(" Expr ")"
+
+	if _, err := p.expect(token.LParen); err != nil {
+		return ast.Expr{}, err
+	}
+
+	expr, err := p.parseExpr()
+	if err != nil {
+		return ast.Expr{}, err
+	}
+
+	if _, err := p.expect(token.RParen); err != nil {
+		return ast.Expr{}, err
+	}
+
+	return expr, nil
+}
+
+func (p *Parser) parseFnLitExpr() (ast.Expr, error) {
+	// FnLitExpr = "fn" FnParams (Block | Expr)
+	fnTok, err := p.expect(token.KwFn)
+	if err != nil {
+		return ast.Expr{}, err
+	}
+
+	params, err := p.parseFnParams()
+	if err != nil {
+		return ast.Expr{}, err
+	}
+
+	// TODO: Also parse just an expression
+	block, err := p.parseBlock()
+	if err != nil {
+		return ast.Expr{}, err
+	}
+
+	return ast.Expr{
+		At:   fnTok.At,
+		Kind: ast.ExprKindFnLit,
+		Value: ast.ExprFnLit{
+			Args: params,
+			Body: block,
+		},
+	}, nil
+}
+
+func (p *Parser) parseArrayLitExpr() (ast.Expr, error) {
+	// ArrayLitExpr = "[" Expr ("," Expr)* "]"
+	bracketTok, err := p.expect(token.LBracket)
+	if err != nil {
+		return ast.Expr{}, err
+	}
+
+	elems := []ast.Expr{}
+	if p.peek().Kind != token.RBracket {
+		for {
+			elem, err := p.parseExpr()
+			if err != nil {
+				return ast.Expr{}, err
+			}
+
+			elems = append(elems, elem)
+
+			if p.peek().Kind == token.RBracket {
+				break
+			}
+
+			if _, err := p.expect(token.Comma); err != nil {
+				return ast.Expr{}, err
+			}
+		}
+	}
+
+	return ast.Expr{
+		At:   bracketTok.At,
+		Kind: ast.ExprKindArrayLit,
+		Value: ast.ExprArrayLit{
+			Values: elems,
+		},
+	}, nil
+}
+
+func (p *Parser) parseIdentExpr() (ast.Expr, error) {
+	// IdentExpr = IDENT
+	node, err := p.parseIdent()
+	if err != nil {
+		return ast.Expr{}, err
+	}
+
+	return ast.Expr{
+		At:   node.At,
+		Kind: ast.ExprKindIdent,
+		Value: ast.ExprIdent{
+			Value: node,
+		},
+	}, nil
+}
+
+func (p *Parser) parseIntLitExpr() (ast.Expr, error) {
+	// IntLitExpr = INT
+	tok, err := p.expect(token.Int)
+	if err != nil {
+		return ast.Expr{}, err
+	}
+
+	return ast.Expr{
+		At:   tok.At,
+		Kind: ast.ExprKindIntLit,
+		Value: ast.ExprIntLit{
+			Value: tok.Data.(uint64),
+		},
+	}, nil
+}
+
+func (p *Parser) parseFloatLitExpr() (ast.Expr, error) {
+	// FloatLitExpr = FLOAT
+	tok, err := p.expect(token.Float)
+	if err != nil {
+		return ast.Expr{}, err
+	}
+
+	return ast.Expr{
+		At:   tok.At,
+		Kind: ast.ExprKindFloatLit,
+		Value: ast.ExprFloatLit{
+			Value: tok.Data.(float64),
+		},
+	}, nil
+}
+
+func (p *Parser) parseStringLitExpr() (ast.Expr, error) {
+	// StringLitExpr = STRING
+	tok, err := p.expect(token.String)
+	if err != nil {
+		return ast.Expr{}, err
+	}
+
+	return ast.Expr{
+		At:   tok.At,
+		Kind: ast.ExprKindStringLit,
+		Value: ast.ExprStringLit{
+			Value: tok.Data.(string),
+		},
+	}, nil
+}
+
+func (p *Parser) parseValueLitExpr() (ast.Expr, error) {
+	// ValueLitExpr = "true" | "false" | "null" | "this"
+	tok := p.next()
+	switch tok.Kind {
+	case token.KwTrue:
+		return ast.Expr{
+			At:   tok.At,
+			Kind: ast.ExprKindBoolLit,
+			Value: ast.ExprBoolLit{
+				Value: true,
+			},
+		}, nil
+	case token.KwFalse:
+		return ast.Expr{
+			At:   tok.At,
+			Kind: ast.ExprKindBoolLit,
+			Value: ast.ExprBoolLit{
+				Value: false,
+			},
+		}, nil
+	case token.KwNull:
+		return ast.Expr{
+			At:    tok.At,
+			Kind:  ast.ExprKindNullLit,
+			Value: ast.ExprNullLit{},
+		}, nil
+	case token.KwThis:
+		return ast.Expr{
+			At:    tok.At,
+			Kind:  ast.ExprKindThis,
+			Value: ast.ExprThis{},
+		}, nil
+	default:
+		panic(fmt.Errorf("unexpected token %+v, wanted value literal", tok))
+	}
+}