about summary refs log tree commit diff
path: root/pkg/lang/parser/stmts.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/stmts.go
parent5267c0e8653b431cfd2c06212cdba4f22225bd02 (diff)
downloadjinx-b5a9660b6ac42bce27c746e76013c3ce5992743a.tar.zst
jinx-b5a9660b6ac42bce27c746e76013c3ce5992743a.zip
Lang parser prototype
Diffstat (limited to 'pkg/lang/parser/stmts.go')
-rw-r--r--pkg/lang/parser/stmts.go372
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
+}