Added more expressions to the parser
This commit is contained in:
parent
e1a6aea05c
commit
9bb1bc8462
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue