untitled paste

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

expression.nim

Raw
## 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

Raw
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

Raw
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)