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:
parent
a9c09bd585
commit
69e20e4446
|
@ -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})"
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue