expression.nim
## Expression parsing is in its own module since
## this code is very messy and I don't want it to clutter up the main
## parser logic
{.experimental: "codeReordering".}
from parser import Parser, peek, prev, advance, match, consume
from ../token import Token, TokenKind
from ast import ASTNodeKind, ASTNode, printNode
proc parseInt(parser: var Parser): ASTNode =
let token = parser.peek
if parser.match(tkInt):
result = ASTNode(kind: ankInt, intVal: token.intVal)
proc parseString(parser: var Parser): ASTNode =
let token = parser.peek
if parser.match(tkString):
result = ASTNode(kind: ankString, stringVal: token.stringVal)
proc parseLiteralExpr(parser: var Parser): ASTNode =
if parser.match(tkInt):
result = parser.parseInt()
elif parser.match(tkString):
result = parser.parseString()
elif parser.match(tkLParen):
# Parse a grouping
let exp = parser.parseInt()
discard parser.consume(tkRParen, "Expected ')' after an expression")
result = ASTNode(kind: ankGroup, group: exp)
proc parseUnary(parser: var Parser): ASTNode =
## Parse a unary expression
## unary = ("-" | "+") int
## | int
var
token = parser.peek()
right: ASTNode
if parser.match(tkMinus) or parser.match(tkPlus):
# Unary expression
right = parser.parseInt()
result = ASTNode(kind: ankUnary, unaryOp: token, value: right)
else:
result = parser.parseLiteralExpr()
proc parseFactor(parser: var Parser): ASTNode =
## Parse a factor
## factor = (unary ("*" | "/") unary)*
var
left: ASTNode
right: ASTNode
op: Token
left = parser.parseUnary()
while parser.match(tkStar) or parser.match(tkSlash):
op = parser.prev()
right = parser.parseUnary()
left = ASTNode(kind: ankBinOp, left: left, binOp: op, right: right)
return left
proc parseTerm(parser: var Parser): ASTNode =
## Parse a term
## term = (factor ("+" | "-") factor)*
var
left: ASTNode
right: ASTNode
op: Token
left = parser.parseFactor()
while parser.match(tkPlus) or parser.match(tkMinus):
op = parser.prev()
right = parser.parseFactor()
left = ASTNode(kind: ankBinOp, left: left, binOp: op, right: right)
return left
proc parseRelational*(parser: var Parser): ASTNode =
## Parse a relational/comparison expression
## All relational operators have the same precedence in Tsuki
## rel = term (("==" | "!=" | "<" | ">" | "<=" | ">=" )) term
var
left: ASTNode
right: ASTNode
op: Token
left = parser.parseTerm()
# TODO: clean this up?
while parser.match(tkDoubleEqual) or parser.match(tkNotEqual) or
parser.match(tkLess) or parser.match(tkGreater) or
parser.match(tkLTEqual) or parser.match(tkGTEqual):
op = parser.prev()
discard parser.advance()
right = parser.parseTerm()
left = ASTNode(kind: ankBinOp, left: left, binOp: op, right: right)
return left
parser.nim
import std/[strutils, strformat]
from ../token import Token, TokenKind
from ast import ASTNodeKind, ASTNode, printNode
import ../error_reporting
type Parser* = object
pos: int
tokens: seq[Token]
tree: ASTNode
func initParser*(tokens: seq[Token]): Parser = Parser(pos: 0, tokens: tokens)
# Helpers
func atEnd*(parser: Parser): bool = parser.pos >= parser.tokens.len
func advance*(parser: var Parser): Token =
if not parser.atEnd:
result = parser.tokens[parser.pos]
parser.pos += 1
func peek*(parser: Parser): Token =
if not parser.atEnd:
result = parser.tokens[parser.pos]
func prev*(parser: Parser): Token = parser.tokens[parser.pos - 1]
func match*(parser: var Parser, tk: TokenKind): bool =
if not parser.atEnd and parser.peek.kind == tk:
discard parser.advance()
result = true
func consume*(parser: var Parser, tk: TokenKind, msg: string): Token =
## Attempt to consume a token of the given kind, and raise an error if
## we cannot for whatever reason
if not parser.atEnd and parser.peek.kind == tk:
result = parser.advance()
raise parserError(parser.pos, parser.peek.kind, msg)
# Expression parser
# {.push warning[UnusedImport]: off.}
import expression
tsuki.nim
import front/lexer
import front/parse/parser
from front/parse/ast import printNode
var lex = initLexer("""
-- abcdef
1+2
""")
when isMainModule:
var tokens = lex.scan()
var parse = initParser(tokens)
var node = parse.parseRelational()
printNode(node)