diff options
| author | Mel <einebeere@gmail.com> | 2022-04-21 03:19:06 +0200 |
|---|---|---|
| committer | Mel <einebeere@gmail.com> | 2022-04-21 03:19:06 +0200 |
| commit | b5a9660b6ac42bce27c746e76013c3ce5992743a (patch) | |
| tree | 0e9bd21a30aae25fe21a7a3994a48ac2cea8c955 /pkg/lang/parser/exprs.go | |
| parent | 5267c0e8653b431cfd2c06212cdba4f22225bd02 (diff) | |
| download | jinx-b5a9660b6ac42bce27c746e76013c3ce5992743a.tar.zst jinx-b5a9660b6ac42bce27c746e76013c3ce5992743a.zip | |
Lang parser prototype
Diffstat (limited to 'pkg/lang/parser/exprs.go')
| -rw-r--r-- | pkg/lang/parser/exprs.go | 390 |
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)) + } +} |
