Made yield expressions more consistent and allowed them as default arguments in function declarations. Fixed parser bug with nil assignments and added a warning about dynamic const declarations in the optimizer

This commit is contained in:
nocturn9x 2021-10-04 14:01:21 +02:00
parent a9c09bd585
commit 69e20e4446
4 changed files with 38 additions and 12 deletions

View File

@ -519,7 +519,7 @@ proc `$`*(self: ASTNode): string =
result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, let={self.isLet}, static={self.isStatic}, private={self.isPrivate})" result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, let={self.isLet}, static={self.isStatic}, private={self.isPrivate})"
of funDecl: of funDecl:
var self = FunDecl(self) var self = FunDecl(self)
result &= &"name={self.name}, body={self.body}, arguments=[{self.arguments.join(\", \")}], async={self.isAsync}, generator={self.isGenerator}, static={self.isStatic}, isPrivate={self.isPrivate})" result &= &"FunDecl(name={self.name}, body={self.body}, arguments=[{self.arguments.join(\", \")}], async={self.isAsync}, generator={self.isGenerator}, static={self.isStatic}, private={self.isPrivate})"
of classDecl: of classDecl:
var self = ClassDecl(self) var self = ClassDecl(self)
result &= &"Class(name={self.name}, body={self.body}, parents=[{self.parents.join(\", \")}], static={self.isStatic}, private={self.isPrivate})" result &= &"Class(name={self.name}, body={self.body}, parents=[{self.parents.join(\", \")}], static={self.isStatic}, private={self.isPrivate})"

View File

@ -27,7 +27,8 @@ type
isWithALiteral, isWithALiteral,
equalityWithSingleton, equalityWithSingleton,
valueOverflow, valueOverflow,
implicitConversion implicitConversion,
dynamicConstIgnored
Warning* = ref object Warning* = ref object
kind*: WarningKind kind*: WarningKind
@ -268,6 +269,16 @@ proc optimizeNode(self: Optimizer, node: ASTNode): ASTNode =
for keyword in node.arguments.keyword: for keyword in node.arguments.keyword:
newArgs.keyword.add((name: keyword.name, value: self.optimizeNode(keyword.value))) newArgs.keyword.add((name: keyword.name, value: self.optimizeNode(keyword.value)))
result = CallExpr(kind: callExpr, callee: node.callee, arguments: newArgs) result = CallExpr(kind: callExpr, callee: node.callee, arguments: newArgs)
of blockStmt:
var newBlock = newBlockStmt(@[])
for node in BlockStmt(node).code:
newBlock.code.add(self.optimizeNode(node))
result = newBlock
of varDecl:
var decl = VarDecl(node)
if decl.isConst and not decl.isStatic:
self.newWarning(dynamicConstIgnored, decl)
result = decl
else: else:
result = node result = node

View File

@ -180,6 +180,7 @@ proc primary(self: Parser): ASTNode =
discard self.step() discard self.step()
result = newNanExpr() result = newNanExpr()
of Nil: of Nil:
discard self.step()
result = newNilExpr() result = newNilExpr()
of Float: of Float:
result = newFloatExpr(self.step()) result = newFloatExpr(self.step())
@ -188,6 +189,7 @@ proc primary(self: Parser): ASTNode =
of Identifier: of Identifier:
result = newIdentExpr(self.step()) result = newIdentExpr(self.step())
of LeftParen: of LeftParen:
# TODO: Tuples
discard self.step() discard self.step()
result = self.expression() result = self.expression()
self.expect(RightParen, "unmatched '('") self.expect(RightParen, "unmatched '('")
@ -367,8 +369,12 @@ proc bitwiseOr(self: Parser): ASTNode =
proc yieldExpr(self: Parser): ASTNode = proc yieldExpr(self: Parser): ASTNode =
## Parses yield expressions ## Parses yield expressions. They need
## to be expressions so that stuff like
## var a = yield; is valid
if self.match(Yield): if self.match(Yield):
if self.context != Function:
self.error("'yield' outside function")
if self.check([Semicolon, RightBrace, RightParen, RightBracket]): if self.check([Semicolon, RightBrace, RightParen, RightBracket]):
# Ugly hack to allow empty yield expressions (which yield nil) # Ugly hack to allow empty yield expressions (which yield nil)
result = newYieldExpr(newNilExpr()) result = newYieldExpr(newNilExpr())
@ -567,7 +573,7 @@ proc varDecl(self: Parser, isStatic: bool = true, isPrivate: bool = true): ASTNo
## Parses variable declarations ## Parses variable declarations
var varKind = self.peek(-1) var varKind = self.peek(-1)
var keyword = "" var keyword = ""
var value: ASTNode = newNilExpr() var value: ASTNode
case varKind.kind: case varKind.kind:
of Let: of Let:
keyword = "let" keyword = "let"
@ -579,6 +585,8 @@ proc varDecl(self: Parser, isStatic: bool = true, isPrivate: bool = true): ASTNo
var name = newIdentExpr(self.peek(-1)) var name = newIdentExpr(self.peek(-1))
if self.match(Equal): if self.match(Equal):
value = self.expression() value = self.expression()
else:
value = newNilExpr()
self.expect(Semicolon, &"expecting semicolon after {keyword} declaration") self.expect(Semicolon, &"expecting semicolon after {keyword} declaration")
case varKind.kind: case varKind.kind:
of Let: of Let:
@ -586,14 +594,16 @@ proc varDecl(self: Parser, isStatic: bool = true, isPrivate: bool = true): ASTNo
of Var: of Var:
result = newVarDecl(name, value, isStatic=isStatic, isPrivate=isPrivate) result = newVarDecl(name, value, isStatic=isStatic, isPrivate=isPrivate)
of Const: of Const:
result = newVarDecl(name, value, isConst=true, isPrivate=isPrivate) # Note that isStatic is ignored here, because constants are resolved differently,
# but we leave it so the optimizer can issue a warning about it
result = newVarDecl(name, value, isConst=true, isPrivate=isPrivate, isStatic=isStatic)
else: else:
discard # Unreachable discard # Unreachable
proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPrivate: bool = true): FunDecl = proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPrivate: bool = true): FunDecl =
## Parses function declarations ## Parses function declarations
self.expect(Identifier, "expecting function name after 'function'") self.expect(Identifier, "expecting function name after 'fun'")
var ident = newIdentExpr(self.peek(-1)) var ident = newIdentExpr(self.peek(-1))
result = newFunDecl(ident, @[], @[], nil, isStatic=isStatic, isAsync=isAsync, isGenerator=false, isPrivate=isPrivate) result = newFunDecl(ident, @[], @[], nil, isStatic=isStatic, isAsync=isAsync, isGenerator=false, isPrivate=isPrivate)
if self.match(LeftBrace): if self.match(LeftBrace):
@ -612,10 +622,8 @@ proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPriva
result.arguments.add(parameter) result.arguments.add(parameter)
if self.match(Equal): if self.match(Equal):
result.defaults.add(self.expression()) result.defaults.add(self.expression())
if result.defaults[^1].kind == NodeKind.yieldExpr:
self.error("yield can't be used in function declaration context")
elif result.defaults.len() > 0: elif result.defaults.len() > 0:
self.error("positional argument cannot follow default argument in function declaration") self.error("positional argument(s) cannot follow default argument(s) in function declaration")
if not self.match(Comma): if not self.match(Comma):
break break
self.expect(RightParen) self.expect(RightParen)
@ -624,7 +632,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPriva
# We do some analysis on the code of the function. Namely we check # We do some analysis on the code of the function. Namely we check
# if the user used 'await' in a non-async function and if the # if the user used 'await' in a non-async function and if the
# function has any yield expressions in them, making it a # function has any yield expressions in them, making it a
# generator. Async generators are also supported, by the way # generator. Async generators are also (syntactically) supported
for line in BlockStmt(result.body).code: for line in BlockStmt(result.body).code:
if line.kind == exprStmt: if line.kind == exprStmt:
if ExprStmt(line).expression.kind == unaryExpr: if ExprStmt(line).expression.kind == unaryExpr:
@ -633,7 +641,14 @@ proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPriva
# is redundant # is redundant
result.isGenerator = true result.isGenerator = true
elif UnaryExpr(ExprStmt(line).expression).operator.kind == Await and not result.isAsync: elif UnaryExpr(ExprStmt(line).expression).operator.kind == Await and not result.isAsync:
self.error("'await' cannot be used outside async functions") self.error("'await' outside async function")
elif not result.isGenerator and line.kind == NodeKind.funDecl:
# Nested function declarations with yield expressions as
# default arguments are valid, but for them to work we
# need to make the containing function a generator!
for default in FunDecl(line).defaults:
if default.kind == NodeKind.yieldExpr:
result.isGenerator = true
proc classDecl(self: Parser, isStatic: bool = true, isPrivate: bool = true): ASTNode = proc classDecl(self: Parser, isStatic: bool = true, isPrivate: bool = true): ASTNode =

View File

@ -40,7 +40,7 @@ while true:
optimized = initOptimizer().optimize(tree) optimized = initOptimizer().optimize(tree)
except: except:
echo &"A Nim runtime exception occurred: {getCurrentExceptionMsg()}" echo &"A Nim runtime exception occurred: {getCurrentExceptionMsg()}"
raise continue
echo "Tokenization step: " echo "Tokenization step: "
for token in tokens: for token in tokens: