expression.nim
@@ -0,0 +1,101 @@
+## 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
\ No newline at end of file
parser.nim
@@ -0,0 +1,45 @@
+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
@@ -0,0 +1,16 @@
+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)
\ No newline at end of file