Revisions for untitled paste

View the changes made to this paste.

unlisted ⁨3⁩ ⁨files⁩ 2022-11-10 19:55:56 UTC

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