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 } // 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.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 ")" 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 } 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 } } } 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: panic(fmt.Errorf("unexpected token %+v, wanted value literal", tok)) } }