import sugar import strformat import ../scanner import ../types/value import ../config import types import utils # PARSE RULE/PRECEDENCE MISC proc nop*(comp: Compiler) = discard var rules*: array[TokenType, ParseRule] template genRule*(ttype: TokenType, tprefix: (Compiler) -> void, tinfix: (Compiler) -> void, tprec: Precedence) = if tprec == pcUnary: raise newException(Exception, "pcUnary cannot be used as a rule precedence! Use pcNone for unary-only rules!") elif tprec == pcPrimary: raise newException(Exception, "Invalid rule: pcPrimary cannot be used for binary operators, if this rule is for a primary value, use pcNone!") elif tprec in {pcNonAssignTop, pcExprTop}: raise newException(Exception, "Invalid rule: a top pc is just a placeholder") elif tprec == pcNone and tinfix != nop: raise newException(Exception, "Invalid rule: pcNone only allowed for unary operators and primary values, not for infix ones!") rules[ttype] = ParseRule(name: $ttype, prefix: tprefix, infix: tinfix, prec: tprec) for i in TokenType: genRule(i, nop, nop, pcNone) proc getRule*(opType: TokenType): ParseRule = rules[opType] proc applyRule*(rule: ParseRule): Precedence = # returns the rule's precedence rule.prec proc increment*(prec: Precedence): Precedence = # increases precedence by one if prec == pcPrimary: raise newException(Exception, "Invalid ruletable, pcPrimary precedence increment attempted.") else: Precedence(int(prec) + 1) proc `<=`*(a, b: Precedence): bool = int(a) <= int(b) proc parsePrecedence*(comp: Compiler, prec: Precedence) = comp.advance() let rule = comp.previous.tokenType.getRule() if rule.prefix != nop: comp.canAssign = prec <= pcAssignment rule.prefix(comp) while prec <= comp.current.tokenType.getRule().prec: comp.advance() # checked for isSome in the loop # since advance moves current to previous if comp.previous.tokenType.getRule().infix == nop: # should never happen, as having a precedence set # means that it is a binary op comp.error("Invalid rule table.") return else: let infixRule = comp.previous.tokenType.getRule().infix infixRule(comp) else: comp.error(&"Expect expression, got {($comp.previous.tokenType)[2..^1]}.") proc expression*(comp: Compiler) = ## The lowest precedence among the pratt-parsed expressions # DO NOT ADD ANYTHING HERE # only use the pratt table for parsing expressions! when assertionsVM: let oldStackIndex = comp.stackIndex comp.parsePrecedence(pcExprTop) when assertionsVM: let diff = comp.stackIndex - oldStackIndex if diff != 1: comp.error(&"Assertion failed: expression increased ({oldStackIndex} -> {comp.stackIndex}) the stack index by {diff} (should be 1).")