package parser
import (
"github.com/marzeq/quokka/shared"
"github.com/marzeq/quokka/tokeniser"
)
func (p *Parser) ParseExpression() (*Node, error) {
return p.ParseLogicalOr()
}
func (p *Parser) ParseLogicalOr() (*Node, error) {
beginLoc := p.CurrLoc()
left, err := p.ParseLogicalAnd()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TOKEN_TYPE_KEYWORD) && p.Peek().Value == "or" {
p.Inc()
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
right, err := p.ParseLogicalAnd()
if err != nil {
return nil, err
}
left = &Node{
Type: NODE_TYPE_BINARY_OP,
Value: "or",
Left: left,
Right: right,
Loc: beginLoc,
}
}
return left, nil
}
func (p *Parser) ParseLogicalAnd() (*Node, error) {
beginLoc := p.CurrLoc()
left, err := p.ParseLogicalNot()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TOKEN_TYPE_KEYWORD) && p.Peek().Value == "and" {
p.Inc()
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
right, err := p.ParseLogicalNot()
if err != nil {
return nil, err
}
left = &Node{
Type: NODE_TYPE_BINARY_OP,
Value: "and",
Left: left,
Right: right,
Loc: beginLoc,
}
}
return left, nil
}
func (p *Parser) ParseLogicalNot() (*Node, error) {
beginLoc := p.CurrLoc()
if p.Match(tokeniser.TOKEN_TYPE_KEYWORD) && p.Peek().Value == "not" {
p.Inc()
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
expr, err := p.ParseLogicalNot()
if err != nil {
return nil, err
}
return &Node{
Type: NODE_TYPE_UNARY_OP,
Value: "not",
Right: expr,
Loc: beginLoc,
}, nil
}
return p.ParseComparison()
}
func (p *Parser) ParseUnary() (*Node, error) {
beginLoc := p.CurrLoc()
if p.Match(tokeniser.TOKEN_TYPE_MINUS, tokeniser.TOKEN_TYPE_ASTERISK, tokeniser.TOKEN_TYPE_AMPERSAND) {
op := p.Consume()
val := ""
switch op.Type {
case tokeniser.TOKEN_TYPE_MINUS:
val = "-"
case tokeniser.TOKEN_TYPE_ASTERISK:
val = "*"
case tokeniser.TOKEN_TYPE_AMPERSAND:
val = "&"
}
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
expr, err := p.ParseUnary()
if err != nil {
return nil, err
}
return &Node{
Type: NODE_TYPE_UNARY_OP,
Value: val,
Right: expr,
Loc: beginLoc,
}, nil
}
return p.ParseTerm()
}
func (p *Parser) ParseComparison() (*Node, error) {
beginLoc := p.CurrLoc()
left, err := p.ParseAddSub()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TOKEN_TYPE_EQUALS_EQUALS, tokeniser.TOKEN_TYPE_NOT_EQUALS, tokeniser.TOKEN_TYPE_LESS, tokeniser.TOKEN_TYPE_GREATER, tokeniser.TOKEN_TYPE_LESS_EQUALS, tokeniser.TOKEN_TYPE_GREATER_EQUALS) {
op := p.Consume()
val := ""
switch op.Type {
case tokeniser.TOKEN_TYPE_EQUALS_EQUALS:
val = "=="
case tokeniser.TOKEN_TYPE_NOT_EQUALS:
val = "!="
case tokeniser.TOKEN_TYPE_LESS:
val = "<"
case tokeniser.TOKEN_TYPE_GREATER:
val = ">"
case tokeniser.TOKEN_TYPE_LESS_EQUALS:
val = "<="
case tokeniser.TOKEN_TYPE_GREATER_EQUALS:
val = ">="
}
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
right, err := p.ParseAddSub()
if err != nil {
return nil, err
}
left = &Node{
Type: NODE_TYPE_BINARY_OP,
Value: val,
Left: left,
Right: right,
Loc: beginLoc,
}
}
return left, nil
}
func (p *Parser) ParseAddSub() (*Node, error) {
beginLoc := p.CurrLoc()
left, err := p.ParseMulDiv()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TOKEN_TYPE_PLUS, tokeniser.TOKEN_TYPE_MINUS) {
op := p.Consume()
val := ""
if op.Type == tokeniser.TOKEN_TYPE_PLUS {
val = "+"
} else {
val = "-"
}
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
right, err := p.ParseMulDiv()
if err != nil {
return nil, err
}
left = &Node{
Type: NODE_TYPE_BINARY_OP,
Value: val,
Left: left,
Right: right,
Loc: beginLoc,
}
}
return left, nil
}
func (p *Parser) ParseMulDiv() (*Node, error) {
beginLoc := p.CurrLoc()
left, err := p.ParseUnary()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TOKEN_TYPE_ASTERISK, tokeniser.TOKEN_TYPE_SLASH, tokeniser.TOKEN_TYPE_PERCENT) {
op := p.Consume()
val := ""
switch op.Type {
case tokeniser.TOKEN_TYPE_ASTERISK:
val = "*"
case tokeniser.TOKEN_TYPE_SLASH:
val = "/"
default:
val = "%"
}
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
right, err := p.ParseUnary()
if err != nil {
return nil, err
}
left = &Node{
Type: NODE_TYPE_BINARY_OP,
Value: val,
Left: left,
Right: right,
Loc: beginLoc,
}
}
return left, nil
}
func (p *Parser) ParseTerm() (*Node, error) {
beginLoc := p.CurrLoc()
if p.Match(tokeniser.TOKEN_TYPE_KEYWORD) && p.Peek().Value == "if" {
expr, err := p.ParseIfExpression()
if err != nil {
return nil, err
}
return expr, nil
}
if p.Match(tokeniser.TOKEN_TYPE_KEYWORD) && p.Peek().Value == "cast" {
expr, err := p.ParseCast()
if err != nil {
return nil, err
}
return expr, nil
}
if p.Match(tokeniser.TOKEN_TYPE_OPEN_PAREN) {
p.Consume()
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
expr, err := p.ParseExpression()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
if !p.Expect(tokeniser.TOKEN_TYPE_CLOSE_PAREN) {
return nil, shared.NewError(p.PrevLoc(), "expected ')'")
}
return expr, nil
}
if p.Match(tokeniser.TOKEN_TYPE_IDENT) {
p.Inc()
if p.Match(tokeniser.TOKEN_TYPE_OPEN_PAREN) {
p.Dec()
return p.ParseFunctionCall()
}
if p.Match(tokeniser.TOKEN_TYPE_OPEN_CURLY) {
p.Dec()
return p.ParseStructLiteral()
}
p.Dec()
return p.ParseIdent()
}
if p.Match(tokeniser.TOKEN_TYPE_KEYWORD) && p.Peek().Value == "true" || p.Peek().Value == "false" {
bLit := p.Consume()
return &Node{
Type: NODE_TYPE_BOOL_LITERAL,
Value: bLit.Value,
Loc: beginLoc,
}, nil
}
if p.Match(tokeniser.TOKEN_TYPE_NUMBER) {
nLit := p.Consume()
return &Node{
Type: NODE_TYPE_NUMBER_LITERAL,
Value: nLit.Value,
Loc: beginLoc,
}, nil
}
if p.Match(tokeniser.TOKEN_TYPE_CHAR) {
cLit := p.Consume()
return &Node{
Type: NODE_TYPE_CHAR_LITERAL,
Value: []byte(cLit.Value)[0],
Loc: beginLoc,
}, nil
}
if p.Match(tokeniser.TOKEN_TYPE_STRING) {
sLit := p.Consume()
return &Node{
Type: NODE_TYPE_STRING_LITERAL,
Value: sLit.Value,
Loc: beginLoc,
}, nil
}
return nil, shared.NewError(p.CurrLoc(), "unexpected token %s", p.Peek())
}
func (p *Parser) ParseCast() (*Node, error) {
beginLoc := p.CurrLoc()
if t, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_KEYWORD); !ok || t.Value != "cast" {
return nil, shared.NewError(beginLoc, "expected 'cast' keyword")
}
if !p.Expect(tokeniser.TOKEN_TYPE_OPEN_PAREN) {
return nil, shared.NewError(p.PrevLoc(), "expected '(")
}
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
tpe, err := p.ParseIdent()
if err != nil {
return nil, err
}
if !p.Expect(tokeniser.TOKEN_TYPE_COMMA) {
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
}
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
if !p.Expect(tokeniser.TOKEN_TYPE_CLOSE_PAREN) {
return nil, shared.NewError(p.PrevLoc(), "expected ')")
}
return &Node{
Type: NODE_TYPE_CAST,
Left: &Node{Type: NODE_TYPE_IDENTIFIER, Value: tpe.Value},
Right: expr,
Loc: beginLoc,
}, nil
}
func (p *Parser) ParseFunctionCall() (*Node, error) {
beginLoc := p.CurrLoc()
name, err := p.ParseIdent()
if err != nil {
return nil, err
}
var args []*Node
p.Consume()
if !p.Match(tokeniser.TOKEN_TYPE_CLOSE_PAREN) {
for {
arg, err := p.ParseExpression()
if err != nil {
return nil, err
}
args = append(args, arg)
if !p.Match(tokeniser.TOKEN_TYPE_COMMA) {
break
}
p.Inc()
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
}
}
if !p.Expect(tokeniser.TOKEN_TYPE_CLOSE_PAREN) {
return nil, shared.NewError(p.PrevLoc(), "expected '('")
}
return &Node{
Type: NODE_TYPE_FUNCTION_CALL,
Value: &Node{Type: NODE_TYPE_IDENTIFIER, Value: name.Value},
Children: args,
Loc: beginLoc,
}, nil
}
func (p *Parser) ParseIfExpression() (*Node, error) {
beginLoc := p.CurrLoc()
if !p.Expect(tokeniser.TOKEN_TYPE_KEYWORD) {
return nil, shared.NewError(p.PrevLoc(), "expected 'if' keyword")
}
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.ParseBlockExpression()
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.ParseBlockExpression()
if err != nil {
return nil, err
}
elseIfBranch := &IfBranch{
Condition: elseifCondition,
Node: elseifBlock,
}
ifNodeValue.ElseIfBranches = append(ifNodeValue.ElseIfBranches, elseIfBranch)
} else {
elseBlock, err := p.ParseBlockExpression()
if err != nil {
return nil, err
}
ifNodeValue.ElseBranch = &IfBranch{
Condition: nil,
Node: elseBlock,
}
break
}
}
return &Node{
Type: NODE_TYPE_IF_EXPR,
Value: ifNodeValue,
Loc: beginLoc,
}, nil
}
func (p *Parser) ParseBlockExpression() (*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()
}
blockExpression, err := p.ParseExpression()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
if !p.Expect(tokeniser.TOKEN_TYPE_CLOSE_CURLY) {
return nil, shared.NewError(p.PrevLoc(), "expected '}' to end block")
}
return blockExpression, err
}
func (p *Parser) ParseStructLiteral() (*Node, error) {
beginLoc := p.CurrLoc()
name, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected struct name")
}
if !p.Expect(tokeniser.TOKEN_TYPE_OPEN_CURLY) {
return nil, shared.NewError(p.PrevLoc(), "expected '{' to start struct literal")
}
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
value := &StructLiteralValue{}
for {
if p.Match(tokeniser.TOKEN_TYPE_CLOSE_CURLY) {
p.Inc()
break
}
fieldName, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected field name")
}
if !p.Expect(tokeniser.TOKEN_TYPE_EQUALS) {
return nil, shared.NewError(p.PrevLoc(), "expected '='")
}
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
fieldValue, err := p.ParseExpression()
if err != nil {
return nil, err
}
value.Fields = append(value.Fields, shared.Pair[*Node, *Node]{L: &Node{Type: NODE_TYPE_IDENTIFIER, Value: fieldName.Value}, R: fieldValue})
if !p.Match(tokeniser.TOKEN_TYPE_COMMA, tokeniser.TOKEN_TYPE_NEWLINE) {
break
}
p.Inc()
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
}
if !p.Expect(tokeniser.TOKEN_TYPE_CLOSE_CURLY) {
return nil, shared.NewError(p.PrevLoc(), "expected '}' to end struct literal")
}
return &Node{
Type: NODE_TYPE_STRUCT_LITERAL,
Value: value,
Left: &Node{Type: NODE_TYPE_IDENTIFIER, Value: name.Value},
Loc: beginLoc,
}, nil
}
func (p *Parser) ParseIdent() (*Node, error) {
beginLoc := p.CurrLoc()
firstIdent, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected identifier")
}
node := &Node{
Type: NODE_TYPE_IDENTIFIER,
Value: firstIdent.Value,
Loc: beginLoc,
}
currnode := node
for p.Match(tokeniser.TOKEN_TYPE_DOT) {
p.Inc()
for p.Match(tokeniser.TOKEN_TYPE_NEWLINE) {
p.Inc()
}
loc := p.CurrLoc()
nextIdent, ok := p.ExpectGet(tokeniser.TOKEN_TYPE_IDENT)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected identifier")
}
currnode.Right = &Node{
Type: NODE_TYPE_IDENTIFIER,
Value: nextIdent.Value,
Loc: loc,
}
currnode = currnode.Right
}
return node, nil
}