Added more expressions to the parser

This commit is contained in:
nocturn9x 2021-08-21 15:04:46 +02:00
parent e1a6aea05c
commit 9bb1bc8462
2 changed files with 47 additions and 16 deletions

View File

@ -42,6 +42,7 @@ type
whileStmt, whileStmt,
blockStmt, blockStmt,
raiseStmt, raiseStmt,
assertStmt
fromStmt, fromStmt,
importStmt, importStmt,
# An expression followed by a semicolon # An expression followed by a semicolon

View File

@ -27,11 +27,11 @@ export token, ast
type Parser* = ref object type Parser* = ref object
## A recursive-descent top-down ## A recursive-descent top-down
## parser implementation ## parser implementation
current: int current*: int
file: string file: string
errored*: bool errored*: bool
errorMessage*: string errorMessage*: string
tokens: seq[Token] tokens*: seq[Token]
proc initParser*(self: Parser = nil): Parser = proc initParser*(self: Parser = nil): Parser =
@ -47,7 +47,7 @@ proc initParser*(self: Parser = nil): Parser =
result.tokens = @[] result.tokens = @[]
template endOfFile: Token = Token(kind: TokenType.EndOfFile, lexeme: "EOF", line: -1) template endOfFile: Token = Token(kind: TokenType.EndOfFile, lexeme: "", line: -1)
proc peek(self: Parser, distance: int = 0): Token = proc peek(self: Parser, distance: int = 0): Token =
@ -56,12 +56,13 @@ proc peek(self: Parser, distance: int = 0): Token =
## token is returned. A negative distance may ## token is returned. A negative distance may
## be used to retrieve previously consumed ## be used to retrieve previously consumed
## tokens ## tokens
if self.tokens.high() == -1 or self.current + distance > self.tokens.high(): if self.tokens.high() == -1 or self.current + distance > self.tokens.high() or self.current + distance < 0:
result = endOfFile result = endOfFile
else: else:
result = self.tokens[self.current + distance] result = self.tokens[self.current + distance]
proc done(self: Parser): bool = proc done(self: Parser): bool =
## Returns true if we're at the ## Returns true if we're at the
## end of the file. Note that the ## end of the file. Note that the
@ -89,7 +90,8 @@ proc error(self: Parser, message: string) =
if self.errored: if self.errored:
return return
self.errored = true self.errored = true
self.errorMessage = &"A fatal error occurred while parsing '{self.file}', line {self.peek().line} at '{self.peek().lexeme}' -> {message}" var lexeme = if not self.done(): self.peek().lexeme else: self.peek(-1).lexeme
self.errorMessage = &"A fatal error occurred while parsing '{self.file}', line {self.peek().line} at '{lexeme}' -> {message}"
proc check(self: Parser, kind: TokenType, distance: int = 0): bool = proc check(self: Parser, kind: TokenType, distance: int = 0): bool =
@ -174,9 +176,11 @@ proc primary(self: Parser): ASTNode =
result = newASTNode(self.step(), NodeKind.identExpr) result = newASTNode(self.step(), NodeKind.identExpr)
of TokenType.LeftParen: of TokenType.LeftParen:
discard self.step() discard self.step()
var expression = self.expression() result = self.expression()
if self.expect(TokenType.RightParen, "Unmatched '('"): if self.expect(TokenType.RightParen, "Unmatched '('"):
result = newASTNode(self.peek(-1), NodeKind.groupingExpr, @[expression]) result = newASTNode(self.peek(-3), NodeKind.groupingExpr, @[result])
of TokenType.RightParen:
self.error("Unmatched ')'")
else: else:
self.error("Invalid syntax") self.error("Invalid syntax")
@ -184,8 +188,7 @@ proc primary(self: Parser): ASTNode =
proc make_call(self: Parser, callee: ASTNode): ASTNode = proc make_call(self: Parser, callee: ASTNode): ASTNode =
## Utility function called iteratively by self.call() ## Utility function called iteratively by self.call()
## to parse a function-like call ## to parse a function-like call
var arguments: seq[ASTNode] = @[] var arguments: seq[ASTNode] = @[callee]
arguments.add(callee)
if not self.check(TokenType.RightParen): if not self.check(TokenType.RightParen):
while true: while true:
if len(arguments) >= 255: if len(arguments) >= 255:
@ -201,16 +204,15 @@ proc make_call(self: Parser, callee: ASTNode): ASTNode =
proc call(self: Parser): ASTNode = proc call(self: Parser): ASTNode =
## Parses call expressions and object ## Parses call expressions and object
## accessing ("dot syntax") ## accessing ("dot syntax")
var expression = self.primary() result = self.primary()
while true: while true:
if self.match(TokenType.LeftParen): if self.match(TokenType.LeftParen):
expression = self.make_call(expression) result = self.make_call(result)
elif self.match(TokenType.Dot): elif self.match(TokenType.Dot):
if self.expect(TokenType.Identifier, "Expecting attribute name after '.'"): if self.expect(TokenType.Identifier, "Expecting attribute name after '.'"):
expression = newASTNode(self.peek(-2), NodeKind.getExpr, @[newAstNode(self.peek(-1), NodeKind.identExpr, @[expression])]) result = newASTNode(self.peek(-2), NodeKind.getExpr, @[result, newAstNode(self.peek(-1), NodeKind.identExpr)])
else: else:
break break
result = expression
proc unary(self: Parser): ASTNode = proc unary(self: Parser): ASTNode =
@ -298,7 +300,35 @@ proc logical_or(self: Parser): ASTNode =
result = newASTNode(operator, NodeKind.binaryExpr, @[result, right]) result = newASTNode(operator, NodeKind.binaryExpr, @[result, right])
proc expression(self: Parser): ASTNode = self.logical_or() proc binary(self: Parser): ASTNode =
## Parses binary expressions
result = self.logical_or()
proc assignment(self: Parser): ASTNode =
## Parses assignment, the highest-level
## expression
result = self.binary()
if self.match(TokenType.Equal):
var tok = self.peek(-1)
var value = self.assignment()
if result.kind == NodeKind.identExpr:
result = newASTNode(tok, NodeKind.assignExpr, @[result, value])
elif result.kind == NodeKind.getExpr:
result = newASTNode(tok, NodeKind.setExpr, @[result.children[0], result.children[1], value])
proc expression(self: Parser): ASTNode =
## Parses expressions
self.assignment()
proc expressionStatement(self: Parser): ASTNode =
## Parses expression statements, which
## are expressions followed by a semicolon
var expression = self.expression()
discard self.expect(TokenType.Semicolon, "missing semicolon after expression")
result = newAstNode(self.peek(-1), NodeKind.exprStmt, @[expression])
proc parse*(self: Parser, tokens: seq[Token], file: string): seq[ASTNode] = proc parse*(self: Parser, tokens: seq[Token], file: string): seq[ASTNode] =
@ -308,8 +338,8 @@ proc parse*(self: Parser, tokens: seq[Token], file: string): seq[ASTNode] =
self.file = file self.file = file
var program: seq[ASTNode] = @[] var program: seq[ASTNode] = @[]
while not self.done(): while not self.done():
program.add(self.expression()) program.add(self.expressionStatement())
if self.errored: if self.errored:
program = @[] program = @[]
break break
result = program result = program