package parser import ( "github.com/marzeq/quokka/shared" "github.com/marzeq/quokka/tokeniser" ) func (p *Parser) ParseDeclaration() (*Node, error) { kw, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_KEYWORD) if !ok || kw.Value != "let" && kw.Value != "var" { return nil, shared.NewError(p.PrevLoc(), "expected `let` or `var` keyword") } ident, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT) if !ok { return nil, shared.NewError(p.PrevLoc(), "expected name") } var tpe *Node if p.Match(tokeniser.TOKEN_TYPE_COLON) { p.Inc() ident, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT) if !ok { return nil, shared.NewError(p.PrevLoc(), "expected type name") } tpe = &Node{Type: NODE_TYPE_IDENTIFIER, Value: ident.Value} } if !p.Expect(tokeniser.TOKEN_TYPE_EQUALS) { return nil, shared.NewError(p.PrevLoc(), "expected '='") } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } expr, err := p.ParseExpression() if err != nil { return nil, err } return &Node{ Type: NODE_TYPE_DECLARATION, Value: &DeclarationValue{ Mutable: kw.Value == "var", Type: tpe, }, Left: &Node{ Type: NODE_TYPE_IDENTIFIER, Value: ident.Value, }, Right: expr, }, nil } func (p *Parser) ParseAssignment() (*Node, error) { ident, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT) if !ok { return nil, shared.NewError(p.PrevLoc(), "expected name") } if !p.Expect(tokeniser.TOKEN_TYPE_EQUALS) { return nil, shared.NewError(p.PrevLoc(), "expected '='") } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } expr, err := p.ParseExpression() if err != nil { return nil, err } return &Node{ Type: NODE_TYPE_ASSIGNMENT, Left: &Node{ Type: NODE_TYPE_IDENTIFIER, Value: ident.Value, }, Right: expr, }, err } func (p *Parser) ParseAssignmentBy() (*Node, error) { ident, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT) if !ok { return nil, shared.NewError(p.PrevLoc(), "expected name") } if !p.Match(tokeniser.TOKEN_TYPE_INC_BY, tokeniser.TOKEN_TYPE_DEC_BY, tokeniser.TOKEN_TYPE_MUL_BY, tokeniser.TOKEN_TYPE_DIV_BY) { return nil, shared.NewError(p.PrevLoc(), "expected '+=', '-=', '*=' or '/='") } opType := p.Consume() var op tokeniser.TokenType switch opType.Type { case tokeniser.TOKEN_TYPE_INC_BY: op = tokeniser.TOKEN_TYPE_PLUS case tokeniser.TOKEN_TYPE_DEC_BY: op = tokeniser.TOKEN_TYPE_MINUS case tokeniser.TOKEN_TYPE_MUL_BY: op = tokeniser.TOKEN_TYPE_ASTERISK case tokeniser.TOKEN_TYPE_DIV_BY: op = tokeniser.TOKEN_TYPE_SLASH } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } expr, err := p.ParseExpression() if err != nil { return nil, err } identNode := &Node{ Type: NODE_TYPE_IDENTIFIER, Value: ident.Value, } return &Node{ Type: NODE_TYPE_ASSIGNMENT, Left: identNode, Right: &Node{ Type: NODE_TYPE_BINARY_OP, Value: op, Left: identNode, Right: expr, }, }, err } func (p *Parser) ParseBlock() (*Node, error) { if !p.Expect(tokeniser.TOKEN_TYPE_OPEN_CURLY) { return nil, shared.NewError(p.PrevLoc(), "expected '{' to start block") } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } var children []*Node for !p.Match(tokeniser.TOKEN_TYPE_CLOSE_CURLY, tokeniser.TOKEN_TYPE_EOF) { stmt, err := p.ParseStatement() if err != nil { return nil, err } children = append(children, stmt) if p.Match(tokeniser.TOKEN_TYPE_CLOSE_CURLY) { break } if !p.Match(tokeniser.TOKEN_TYPE_SEMICOLON, tokeniser.TOKEN_TYPE_NEWLINE) { return nil, shared.NewError(p.CurrLoc(), "expected ';' or '\\n' to end statement") } for p.Match(tokeniser.TOKEN_TYPE_SEMICOLON, tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } } if !p.Expect(tokeniser.TOKEN_TYPE_CLOSE_CURLY) { return nil, shared.NewError(p.PrevLoc(), "expected '}' to close block") } return &Node{ Type: NODE_TYPE_BLOCK, Children: children, }, nil } func (p *Parser) ParseFunctionDefinition() (*Node, error) { if !p.Expect(tokeniser.TOKEN_TYPE_KEYWORD) { return nil, shared.NewError(p.PrevLoc(), "expected 'let' keyword") } name, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT) if !ok { return nil, shared.NewError(p.PrevLoc(), "expected function name") } if !p.Expect(tokeniser.TOKEN_TYPE_OPEN_PAREN) { return nil, shared.NewError(p.PrevLoc(), "expected '('") } var args []shared.Pair[*Node, *Node] for !p.Match(tokeniser.TOKEN_TYPE_CLOSE_PAREN) { argName, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT) if !ok { return nil, shared.NewError(p.PrevLoc(), "expected argument name") } if !p.Expect(tokeniser.TOKEN_TYPE_COLON) { return nil, shared.NewError(p.PrevLoc(), "expected ':'") } argType, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT) if !ok { return nil, shared.NewError(p.PrevLoc(), "expected argument type") } args = append(args, shared.Pair[*Node, *Node]{ L: &Node{Type: NODE_TYPE_IDENTIFIER, Value: argName.Value}, R: &Node{Type: NODE_TYPE_IDENTIFIER, Value: argType.Value}, }) if !p.Match(tokeniser.TOKEN_TYPE_COMMA) { break } p.Consume() } if !p.Expect(tokeniser.TOKEN_TYPE_CLOSE_PAREN) { return nil, shared.NewError(p.PrevLoc(), "expected ')'") } if !p.Expect(tokeniser.TOKEN_TYPE_COLON) { return nil, shared.NewError(p.PrevLoc(), "expected ':'") } returnType, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT) if !ok { return nil, shared.NewError(p.PrevLoc(), "expected return type") } if !p.Expect(tokeniser.TOKEN_TYPE_EQUALS) { return nil, shared.NewError(p.PrevLoc(), "expected '='") } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } var body *Node if p.Match(tokeniser.TOKEN_TYPE_OPEN_CURLY) { b, err := p.ParseBlock() if err != nil { return nil, err } body = b } else { b, err := p.ParseExpression() if err != nil { return nil, err } body = b } return &Node{ Type: NODE_TYPE_FUNCTION_DEF, Value: &FunctionValue{ Name: &Node{Type: NODE_TYPE_IDENTIFIER, Value: name.Value}, Args: args, RetType: &Node{Type: NODE_TYPE_IDENTIFIER, Value: returnType.Value}, }, Children: []*Node{body}, }, nil } func (p *Parser) ParseImport() (*Node, error) { if kw, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_KEYWORD); !ok || kw.Value != "import" { return nil, shared.NewError(p.PrevLoc(), "expected 'import' keyword") } ident, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT) if !ok { return nil, shared.NewError(p.PrevLoc(), "expected name") } return &Node{ Type: NODE_TYPE_IMPORT, Right: &Node{Type: NODE_TYPE_IDENTIFIER, Value: ident.Value}, }, nil } func (p *Parser) ParseStatement() (*Node, error) { if p.Match(tokeniser.TOKEN_TYPE_KEYWORD) { kw := p.Peek().Value switch kw { case "let": p.Inc() if !p.Expect(tokeniser.TOKEN_TYPE_IDENT) { return nil, shared.NewError(p.PrevLoc(), "expected name") } p.Dec() isParen := p.Next().Type == tokeniser.TOKEN_TYPE_OPEN_PAREN p.Dec() if isParen { return p.ParseFunctionDefinition() } return p.ParseDeclaration() case "var": return p.ParseDeclaration() case "return", "naked_return", "break", "continue": return p.ParseControlKeyword() case "if": return p.ParseIfStatement() case "for": return p.ParseForLoop() default: return nil, shared.NewError(p.CurrLoc(), "unexpected keyword: %s", kw) } } if p.Match(tokeniser.TOKEN_TYPE_IDENT) { ident := p.Consume() if p.Match(tokeniser.TOKEN_TYPE_EQUALS) { p.Dec() return p.ParseAssignment() } else if p.Match(tokeniser.TOKEN_TYPE_INC_BY, tokeniser.TOKEN_TYPE_DEC_BY, tokeniser.TOKEN_TYPE_MUL_BY, tokeniser.TOKEN_TYPE_DIV_BY) { p.Dec() return p.ParseAssignmentBy() } else if p.Match(tokeniser.TOKEN_TYPE_OPEN_PAREN) { return p.ParseFunctionCall(ident) } return nil, shared.NewError(p.CurrLoc(), "unexpected token after identifier %s", p.Peek()) } if p.Match(tokeniser.TOKEN_TYPE_OPEN_CURLY) { return p.ParseBlock() } return nil, shared.NewError(p.CurrLoc(), "unexpected token %s", p.Peek()) } func (p *Parser) ParseIfStatement() (*Node, error) { if !p.Expect(tokeniser.TOKEN_TYPE_KEYWORD) { return nil, shared.NewError(p.PrevLoc(), "expected 'if'") } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } condition, err := p.ParseExpression() if err != nil { return nil, err } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } thenBlock, err := p.ParseBlock() if err != nil { return nil, err } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } ifBranch := &IfBranch{ Condition: condition, Node: thenBlock, } ifNodeValue := &IfNodeValue{ IfBranch: ifBranch, ElseIfBranches: []*IfBranch{}, } for p.Match(tokeniser.TOKEN_TYPE_KEYWORD) && p.Peek().Value == "else" { p.Consume() for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } if p.Match(tokeniser.TOKEN_TYPE_KEYWORD) && p.Peek().Value == "if" { p.Consume() for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } elseifCondition, err := p.ParseExpression() if err != nil { return nil, err } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } elseifBlock, err := p.ParseBlock() if err != nil { return nil, err } elseIfBranch := &IfBranch{ Condition: elseifCondition, Node: elseifBlock, } ifNodeValue.ElseIfBranches = append(ifNodeValue.ElseIfBranches, elseIfBranch) } else { elseBlock, err := p.ParseBlock() if err != nil { return nil, err } ifNodeValue.ElseBranch = &IfBranch{ Condition: nil, Node: elseBlock, } break } } return &Node{ Type: NODE_TYPE_IF, Value: ifNodeValue, }, nil } func (p *Parser) ParseControlKeyword() (*Node, error) { kw, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_KEYWORD) if !ok { return nil, shared.NewError(p.PrevLoc(), "expected 'return', 'break' or 'continue'") } var expr *Node if kw.Value == "return" && !p.Match(tokeniser.TOKEN_TYPE_NEWLINE, tokeniser.TOKEN_TYPE_SEMICOLON) { got, err := p.ParseExpression() if err != nil { return nil, err } expr = got } return &Node{ Type: NODE_TYPE_CONTROL_KEYWORD, Value: kw.Value, Right: expr, }, nil } func (p *Parser) ParseForLoop() (*Node, error) { if !p.Expect(tokeniser.TOKEN_TYPE_KEYWORD) { return nil, shared.NewError(p.PrevLoc(), "expected 'for'") } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } var err error exprsOrStmts := []*Node{} for !p.Match(tokeniser.TOKEN_TYPE_OPEN_CURLY) { ogLoc := p.CurrLoc() ogPos := p.pos exOrSt, err := p.ParseStatement() if err != nil { p.pos = ogPos exOrSt, err = p.ParseExpression() if err != nil { return nil, shared.NewError(ogLoc, "expected a valid statement or expression") } } exprsOrStmts = append(exprsOrStmts, exOrSt) if p.Match(tokeniser.TOKEN_TYPE_OPEN_CURLY) { break } if !p.Expect(tokeniser.TOKEN_TYPE_SEMICOLON) { return nil, shared.NewError(p.PrevLoc(), "expected ';' to end statement or expression") } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } } for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) { p.Inc() } body, err := p.ParseBlock() if err != nil { return nil, err } return &Node{ Type: NODE_TYPE_FOR, Value: &ForLoopValue{ ExprsOrStmts: exprsOrStmts, Body: body, }, }, nil }