Got rid of Parser.context attribute (using Parser.currentFunction now) and added basic docs on the Parser object
This commit is contained in:
parent
a8bb212092
commit
cce5047a61
|
@ -26,8 +26,6 @@ export token, ast, errors
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
ParseContext = enum
|
|
||||||
Script, Function
|
|
||||||
|
|
||||||
LoopContext = enum
|
LoopContext = enum
|
||||||
Loop, None
|
Loop, None
|
||||||
|
@ -35,25 +33,55 @@ type
|
||||||
Parser* = ref object
|
Parser* = ref object
|
||||||
## A recursive-descent top-down
|
## A recursive-descent top-down
|
||||||
## parser implementation
|
## parser implementation
|
||||||
|
# Index into self.tokens
|
||||||
current: int
|
current: int
|
||||||
|
# The name of the file being parsed.
|
||||||
|
# Only meaningful for parse errors
|
||||||
file: string
|
file: string
|
||||||
|
# The list of tokens representing
|
||||||
|
# the source code to be parsed.
|
||||||
|
# In most cases, those will come
|
||||||
|
# from the builtin lexer, but this
|
||||||
|
# behavior is not enforced and the
|
||||||
|
# tokenizer is entirely separate from
|
||||||
|
# the parser
|
||||||
tokens: seq[Token]
|
tokens: seq[Token]
|
||||||
context: ParseContext
|
# Little internal attribute that tells
|
||||||
|
# us if we're inside a loop or not. This
|
||||||
|
# allows us to detect errors like break
|
||||||
|
# being used outside loops
|
||||||
currentLoop: LoopContext
|
currentLoop: LoopContext
|
||||||
|
# Stores the current function
|
||||||
|
# being parsed. This is a reference
|
||||||
|
# to either a FunDecl or LambdaExpr
|
||||||
|
# AST node and is mostly used to allow
|
||||||
|
# implicit generators to work. What that
|
||||||
|
# means is that there is no need for the
|
||||||
|
# programmer to specifiy a function is a
|
||||||
|
# generator like in nim, (which uses the
|
||||||
|
# 'iterator' keyword): any function is
|
||||||
|
# automatically a generator if it contains
|
||||||
|
# any number of yield statement(s) or
|
||||||
|
# yield expression(s). This attribute
|
||||||
|
# is nil when the parser is at the top-level
|
||||||
|
# code and is what allows the parser to detect
|
||||||
|
# errors like return outside functions before
|
||||||
|
# compilation even begins
|
||||||
currentFunction: ASTNode
|
currentFunction: ASTNode
|
||||||
|
|
||||||
|
|
||||||
proc initParser*(): Parser =
|
proc initParser*(): Parser =
|
||||||
## Initializes a new Parser object
|
## Initializes a new Parser object
|
||||||
new(result)
|
new(result)
|
||||||
result.current = 0
|
result.current = 0
|
||||||
result.file = ""
|
result.file = ""
|
||||||
result.tokens = @[]
|
result.tokens = @[]
|
||||||
result.context = Script
|
|
||||||
result.currentFunction = nil
|
result.currentFunction = nil
|
||||||
result.currentLoop = None
|
result.currentLoop = None
|
||||||
|
|
||||||
|
|
||||||
# Handy templates to make our life easier, thanks nim!
|
# Handy templates to make our life easier, thanks nim!
|
||||||
|
|
||||||
template endOfFile: Token = Token(kind: EndOfFile, lexeme: "", line: -1)
|
template endOfFile: Token = Token(kind: EndOfFile, lexeme: "", line: -1)
|
||||||
template endOfLine(msg: string) = self.expect(Semicolon, msg)
|
template endOfLine(msg: string) = self.expect(Semicolon, msg)
|
||||||
|
|
||||||
|
@ -257,7 +285,7 @@ proc primary(self: Parser): ASTNode =
|
||||||
result = dictObject
|
result = dictObject
|
||||||
of Yield:
|
of Yield:
|
||||||
discard self.step()
|
discard self.step()
|
||||||
if self.context != Function:
|
if self.currentFunction == nil:
|
||||||
self.error("'yield' cannot be outside functions")
|
self.error("'yield' cannot be outside functions")
|
||||||
if self.currentFunction.kind == funDecl:
|
if self.currentFunction.kind == funDecl:
|
||||||
FunDecl(self.currentFunction).isGenerator = true
|
FunDecl(self.currentFunction).isGenerator = true
|
||||||
|
@ -269,7 +297,7 @@ proc primary(self: Parser): ASTNode =
|
||||||
result = newYieldExpr(newNilExpr())
|
result = newYieldExpr(newNilExpr())
|
||||||
of Await:
|
of Await:
|
||||||
discard self.step()
|
discard self.step()
|
||||||
if self.context != Function:
|
if self.currentFunction == nil:
|
||||||
self.error("'await' cannot be used outside functions")
|
self.error("'await' cannot be used outside functions")
|
||||||
if self.currentFunction.kind == lambdaExpr or not FunDecl(self.currentFunction).isAsync:
|
if self.currentFunction.kind == lambdaExpr or not FunDecl(self.currentFunction).isAsync:
|
||||||
self.error("'await' can only be used inside async functions")
|
self.error("'await' can only be used inside async functions")
|
||||||
|
@ -521,7 +549,7 @@ proc breakStmt(self: Parser): ASTNode =
|
||||||
|
|
||||||
proc deferStmt(self: Parser): ASTNode =
|
proc deferStmt(self: Parser): ASTNode =
|
||||||
## Parses defer statements
|
## Parses defer statements
|
||||||
if self.context != Function:
|
if self.currentFunction == nil:
|
||||||
self.error("'defer' cannot be used outside functions")
|
self.error("'defer' cannot be used outside functions")
|
||||||
result = newDeferStmt(self.expression())
|
result = newDeferStmt(self.expression())
|
||||||
endOfLine("missing semicolon after defer statement")
|
endOfLine("missing semicolon after defer statement")
|
||||||
|
@ -537,7 +565,7 @@ proc continueStmt(self: Parser): ASTNode =
|
||||||
|
|
||||||
proc returnStmt(self: Parser): ASTNode =
|
proc returnStmt(self: Parser): ASTNode =
|
||||||
## Parses return statements
|
## Parses return statements
|
||||||
if self.context != Function:
|
if self.currentFunction == nil:
|
||||||
self.error("'return' cannot be used outside functions")
|
self.error("'return' cannot be used outside functions")
|
||||||
var value: ASTNode
|
var value: ASTNode
|
||||||
if not self.check(Semicolon):
|
if not self.check(Semicolon):
|
||||||
|
@ -552,7 +580,7 @@ proc returnStmt(self: Parser): ASTNode =
|
||||||
|
|
||||||
proc yieldStmt(self: Parser): ASTNode =
|
proc yieldStmt(self: Parser): ASTNode =
|
||||||
## Parses yield Statements
|
## Parses yield Statements
|
||||||
if self.context != Function:
|
if self.currentFunction == nil:
|
||||||
self.error("'yield' cannot be outside functions")
|
self.error("'yield' cannot be outside functions")
|
||||||
if self.currentFunction.kind == funDecl:
|
if self.currentFunction.kind == funDecl:
|
||||||
FunDecl(self.currentFunction).isGenerator = true
|
FunDecl(self.currentFunction).isGenerator = true
|
||||||
|
@ -567,7 +595,7 @@ proc yieldStmt(self: Parser): ASTNode =
|
||||||
|
|
||||||
proc awaitStmt(self: Parser): ASTNode =
|
proc awaitStmt(self: Parser): ASTNode =
|
||||||
## Parses yield Statements
|
## Parses yield Statements
|
||||||
if self.context != Function:
|
if self.currentFunction == nil:
|
||||||
self.error("'await' cannot be used outside functions")
|
self.error("'await' cannot be used outside functions")
|
||||||
if self.currentFunction.kind == lambdaExpr or not FunDecl(self.currentFunction).isAsync:
|
if self.currentFunction.kind == lambdaExpr or not FunDecl(self.currentFunction).isAsync:
|
||||||
self.error("'await' can only be used inside async functions")
|
self.error("'await' can only be used inside async functions")
|
||||||
|
@ -758,8 +786,6 @@ proc varDecl(self: Parser, isStatic: bool = true, isPrivate: bool = true): ASTNo
|
||||||
proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPrivate: bool = true, isLambda: bool = false): ASTNode =
|
proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPrivate: bool = true, isLambda: bool = false): ASTNode =
|
||||||
## Parses function and lambda declarations. Note that lambdas count as expressions!
|
## Parses function and lambda declarations. Note that lambdas count as expressions!
|
||||||
var enclosingFunction = self.currentFunction
|
var enclosingFunction = self.currentFunction
|
||||||
let enclosingContext = self.context
|
|
||||||
self.context = Function
|
|
||||||
var arguments: seq[ASTNode] = @[]
|
var arguments: seq[ASTNode] = @[]
|
||||||
var defaults: seq[ASTNode] = @[]
|
var defaults: seq[ASTNode] = @[]
|
||||||
if not isLambda:
|
if not isLambda:
|
||||||
|
@ -795,7 +821,6 @@ proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPriva
|
||||||
FunDecl(self.currentFunction).body = self.blockStmt()
|
FunDecl(self.currentFunction).body = self.blockStmt()
|
||||||
else:
|
else:
|
||||||
LambdaExpr(self.currentFunction).body = self.blockStmt()
|
LambdaExpr(self.currentFunction).body = self.blockStmt()
|
||||||
self.context = enclosingContext
|
|
||||||
result = self.currentFunction
|
result = self.currentFunction
|
||||||
self.currentFunction = enclosingFunction
|
self.currentFunction = enclosingFunction
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue