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/stmts.go | |
| parent | 5267c0e8653b431cfd2c06212cdba4f22225bd02 (diff) | |
| download | jinx-b5a9660b6ac42bce27c746e76013c3ce5992743a.tar.zst jinx-b5a9660b6ac42bce27c746e76013c3ce5992743a.zip | |
Lang parser prototype
Diffstat (limited to 'pkg/lang/parser/stmts.go')
| -rw-r--r-- | pkg/lang/parser/stmts.go | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/pkg/lang/parser/stmts.go b/pkg/lang/parser/stmts.go new file mode 100644 index 0000000..52e590c --- /dev/null +++ b/pkg/lang/parser/stmts.go @@ -0,0 +1,372 @@ +package parser + +import ( + "fmt" + "jinx/pkg/lang/ast" + "jinx/pkg/lang/scanner/token" +) + +func (p *Parser) parseStmt() (ast.Stmt, error) { + switch p.peek().Kind { + case token.KwUse: + return p.parseUseStmt() + case token.KwFn: + return p.parseFnDeclStmt() + case token.KwObject: + return p.parseObjectDeclStmt() + case token.KwVar: + return p.parseVarDeclStmt() + case token.KwIf: + return p.parseIfStmt() + case token.KwTry: + return p.parseTryStmt() + case token.KwReturn: + return p.parseReturnStmt() + case token.KwContinue: + return p.parseContinueStmt() + case token.KwBreak: + return p.parseBreakStmt() + case token.KwThrow: + return p.parseThrowStmt() + default: + if p.peek().CanEndStmt() { + return p.parseEmptyStmt() + } else { + return p.parseExprStmt() + } + } +} + +func (p *Parser) parseUseStmt() (ast.Stmt, error) { + panic("not implemented") +} + +func (p *Parser) parseFnDeclStmt() (ast.Stmt, error) { + fnTok, err := p.expect(token.KwFn) + if err != nil { + return ast.Stmt{}, err + } + + name, err := p.parseIdent() + if err != nil { + return ast.Stmt{}, err + } + + params, err := p.parseFnParams() + if err != nil { + return ast.Stmt{}, err + } + + block, err := p.parseBlock() + if err != nil { + return ast.Stmt{}, err + } + + return ast.Stmt{ + At: fnTok.At, + Kind: ast.StmtKindFnDecl, + Value: ast.StmtFnDecl{ + Name: name, + Args: params, + Body: block, + }, + }, nil +} + +func (p *Parser) parseFnParams() ([]ast.IdentNode, error) { + // FnParams = "(" ( IDENT ("," IDENT)* )? ")" + + if _, err := p.expect(token.LParen); err != nil { + return nil, err + } + + params := []ast.IdentNode{} + + if p.peek().Kind != token.RParen { + for { + param, err := p.parseIdent() + if err != nil { + return nil, err + } + + params = append(params, param) + + if p.peek().Kind == token.RParen { + break + } + + if _, err := p.expect(token.Comma); err != nil { + return nil, err + } + } + } + + if _, err := p.expect(token.RParen); err != nil { + return nil, err + } + + return params, nil +} + +func (p *Parser) parseObjectDeclStmt() (ast.Stmt, error) { + panic("not implemented") +} + +func (p *Parser) parseVarDeclStmt() (ast.Stmt, error) { + // VarDeclStmt = "var" IDENT "=" Expr + + varTok, err := p.expect(token.KwVar) + if err != nil { + return ast.Stmt{}, err + } + + name, err := p.parseIdent() + if err != nil { + return ast.Stmt{}, err + } + + if _, err := p.expect(token.Assign); err != nil { + return ast.Stmt{}, err + } + + expr, err := p.parseExpr() + if err != nil { + return ast.Stmt{}, err + } + + return ast.Stmt{ + At: varTok.At, + Kind: ast.StmtKindVarDecl, + Value: ast.StmtVarDecl{ + Name: name, + Value: expr, + }, + }, nil +} + +func (p *Parser) parseIfStmt() (ast.Stmt, error) { + // IfStmt = "if" Expr Block ("elif" Expr Block)* ("else" Block)? + ifTok, err := p.expect(token.KwIf) + if err != nil { + return ast.Stmt{}, err + } + + cond, err := p.parseExpr() + if err != nil { + return ast.Stmt{}, err + } + + then, err := p.parseBlock() + if err != nil { + return ast.Stmt{}, err + } + + elifs := []ast.CondNode{} + + for p.peek().Kind == token.KwElif { + elifTok, err := p.expect(token.KwElif) + if err != nil { + return ast.Stmt{}, err + } + + elifCond, err := p.parseExpr() + if err != nil { + return ast.Stmt{}, err + } + + elifThen, err := p.parseBlock() + if err != nil { + return ast.Stmt{}, err + } + + elifs = append(elifs, ast.CondNode{ + At: elifTok.At, + Cond: elifCond, + Then: elifThen, + }) + } + + elseThen := ast.BlockNode{} + + if p.peek().Kind == token.KwElse { + _, err := p.expect(token.KwElse) + if err != nil { + return ast.Stmt{}, err + } + + elseThen, err = p.parseBlock() + if err != nil { + return ast.Stmt{}, err + } + } + + return ast.Stmt{ + At: ifTok.At, + Kind: ast.StmtKindIf, + Value: ast.StmtIf{ + Cond: cond, + Then: then, + Elifs: elifs, + Else: elseThen, + }, + }, nil +} + +func (p *Parser) parseTryStmt() (ast.Stmt, error) { + // TryStmt = "try" Block "catch" Ident ("finally" Block)? + + tryTok, err := p.expect(token.KwTry) + if err != nil { + return ast.Stmt{}, err + } + + tryBlock, err := p.parseBlock() + if err != nil { + return ast.Stmt{}, err + } + + if _, err = p.expect(token.KwCatch); err != nil { + return ast.Stmt{}, err + } + + catchName, err := p.parseIdent() + if err != nil { + return ast.Stmt{}, err + } + + catchBlock, err := p.parseBlock() + if err != nil { + return ast.Stmt{}, err + } + + finallyBlock := ast.BlockNode{} + if p.peek().Kind == token.KwFinally { + _, err := p.expect(token.KwFinally) + if err != nil { + return ast.Stmt{}, err + } + + finallyBlock, err = p.parseBlock() + if err != nil { + return ast.Stmt{}, err + } + } + + return ast.Stmt{ + At: tryTok.At, + Kind: ast.StmtKindTry, + Value: ast.StmtTry{ + Try: tryBlock, + CatchedName: catchName, + Catch: catchBlock, + Finally: finallyBlock, + }, + }, nil +} + +func (p *Parser) parseReturnStmt() (ast.Stmt, error) { + // ReturnStmt = "return" (Expr)? + returnTok, err := p.expect(token.KwReturn) + if err != nil { + return ast.Stmt{}, err + } + + expr := ast.Expr{} + if !p.peek().CanEndStmt() { + expr, err = p.parseExpr() + if err != nil { + return ast.Stmt{}, err + } + } + + return ast.Stmt{ + At: returnTok.At, + Kind: ast.StmtKindReturn, + Value: ast.StmtReturn{ + Value: expr, + }, + }, nil +} + +func (p *Parser) parseContinueStmt() (ast.Stmt, error) { + // ContinueStmt = "continue" + continueTok, err := p.expect(token.KwContinue) + if err != nil { + return ast.Stmt{}, err + } + + return ast.Stmt{ + At: continueTok.At, + Kind: ast.StmtKindContinue, + Value: ast.StmtContinue{}, + }, nil +} + +func (p *Parser) parseBreakStmt() (ast.Stmt, error) { + // BreakStmt = "break" + + breakTok, err := p.expect(token.KwBreak) + if err != nil { + return ast.Stmt{}, err + } + + return ast.Stmt{ + At: breakTok.At, + Kind: ast.StmtKindBreak, + Value: ast.StmtBreak{}, + }, nil +} + +func (p *Parser) parseThrowStmt() (ast.Stmt, error) { + // ThrowStmt = "throw" Expr + + throwTok, err := p.expect(token.KwThrow) + if err != nil { + return ast.Stmt{}, err + } + + expr, err := p.parseExpr() + if err != nil { + return ast.Stmt{}, err + } + + return ast.Stmt{ + At: throwTok.At, + Kind: ast.StmtKindThrow, + Value: ast.StmtThrow{ + Value: expr, + }, + }, nil +} + +func (p *Parser) parseExprStmt() (ast.Stmt, error) { + expr, err := p.parseExpr() + if err != nil { + return ast.Stmt{}, err + } + + return ast.Stmt{ + At: expr.At, + Kind: ast.StmtKindExpr, + Value: ast.StmtExpr{ + Value: expr, + }, + }, nil +} + +func (p *Parser) parseEmptyStmt() (ast.Stmt, error) { + return ast.Stmt{ + At: p.peek().At, + Kind: ast.StmtKindEmpty, + Value: ast.StmtEmpty{}, + }, nil +} + +func (p *Parser) parseStmtEnd() error { + tok := p.peek() + if !tok.CanEndStmt() { + panic(fmt.Errorf("wanted statement end, received: %+v", tok)) + } + p.next() + return nil +} |
