package parser import ( "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 } // Adjust for precedence and associativity. binary := p.mergeIntoBinary(left, op, right) left = ast.Expr{ At: left.At, Kind: ast.ExprKindBinary, Value: binary, } } 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.parseMemberExpr() 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 = MemberExpr ( "[" Expr "]" )* for p.peek().Kind == token.LBracket { p.next() index, err := p.parseExpr() if err != nil { return ast.Expr{}, err } if _, err := p.expect(token.RBracket); err != nil { return ast.Expr{}, err } 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 = MemberExpr ( "(" ( Expr ( "," Expr )* )? ")" )* for p.peek().Kind == token.LParen { p.next() args := []ast.Expr{} for p.peek().Kind != token.RParen { arg, err := p.parseExpr() if err != nil { return ast.Expr{}, err } args = append(args, arg) if p.peek().Kind == token.Comma { p.next() } else { break } } 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, }, } } return callee, nil } func (p *Parser) parseMemberExpr() (ast.Expr, error) { // MemberExpr = UnitExpr ( "." Ident )* left, err := p.parseUnitExpr() if err != nil { return ast.Expr{}, err } 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) { // 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: tok := p.peek() return ast.Expr{}, ErrExpectedUnitExpressionStart{tok.At, tok} } } func (p *Parser) parseGroupExpr() (ast.Expr, error) { // GroupExpr = "(" Expr ")" openTok, err := p.expect(token.LParen) if 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 ast.Expr{ At: openTok.At, Kind: ast.ExprKindGroup, Value: ast.ExprGroup{ Value: 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 } hasThis, params, err := p.parseFnParams() if err != nil { return ast.Expr{}, err } if hasThis { return ast.Expr{}, ErrFunctionLiteralNoThisAllowed{fnTok.At} } // 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 } } } if _, err := p.expect(token.RBracket); 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: return ast.Expr{}, ErrExpectedValueLiteral{At: tok.At, Got: tok} } }