nondescript/src/ndspkg/compiler/precedence.nim

83 lines
2.8 KiB
Nim

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).")