diff --git a/src/backend/meta/ast.nim b/src/backend/meta/ast.nim index 4b44c16..7191c41 100644 --- a/src/backend/meta/ast.nim +++ b/src/backend/meta/ast.nim @@ -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})" of funDecl: 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: var self = ClassDecl(self) result &= &"Class(name={self.name}, body={self.body}, parents=[{self.parents.join(\", \")}], static={self.isStatic}, private={self.isPrivate})" diff --git a/src/backend/optimizer.nim b/src/backend/optimizer.nim index c044a6f..cd4fe0d 100644 --- a/src/backend/optimizer.nim +++ b/src/backend/optimizer.nim @@ -27,7 +27,8 @@ type isWithALiteral, equalityWithSingleton, valueOverflow, - implicitConversion + implicitConversion, + dynamicConstIgnored Warning* = ref object kind*: WarningKind @@ -268,6 +269,16 @@ proc optimizeNode(self: Optimizer, node: ASTNode): ASTNode = for keyword in node.arguments.keyword: newArgs.keyword.add((name: keyword.name, value: self.optimizeNode(keyword.value))) 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: result = node diff --git a/src/backend/parser.nim b/src/backend/parser.nim index d824c0c..142978e 100644 --- a/src/backend/parser.nim +++ b/src/backend/parser.nim @@ -180,6 +180,7 @@ proc primary(self: Parser): ASTNode = discard self.step() result = newNanExpr() of Nil: + discard self.step() result = newNilExpr() of Float: result = newFloatExpr(self.step()) @@ -188,6 +189,7 @@ proc primary(self: Parser): ASTNode = of Identifier: result = newIdentExpr(self.step()) of LeftParen: + # TODO: Tuples discard self.step() result = self.expression() self.expect(RightParen, "unmatched '('") @@ -367,8 +369,12 @@ proc bitwiseOr(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.context != Function: + self.error("'yield' outside function") if self.check([Semicolon, RightBrace, RightParen, RightBracket]): # Ugly hack to allow empty yield expressions (which yield nil) result = newYieldExpr(newNilExpr()) @@ -567,7 +573,7 @@ proc varDecl(self: Parser, isStatic: bool = true, isPrivate: bool = true): ASTNo ## Parses variable declarations var varKind = self.peek(-1) var keyword = "" - var value: ASTNode = newNilExpr() + var value: ASTNode case varKind.kind: of Let: keyword = "let" @@ -579,6 +585,8 @@ proc varDecl(self: Parser, isStatic: bool = true, isPrivate: bool = true): ASTNo var name = newIdentExpr(self.peek(-1)) if self.match(Equal): value = self.expression() + else: + value = newNilExpr() self.expect(Semicolon, &"expecting semicolon after {keyword} declaration") case varKind.kind: of Let: @@ -586,14 +594,16 @@ proc varDecl(self: Parser, isStatic: bool = true, isPrivate: bool = true): ASTNo of Var: result = newVarDecl(name, value, isStatic=isStatic, isPrivate=isPrivate) 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: discard # Unreachable proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPrivate: bool = true): FunDecl = ## 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)) result = newFunDecl(ident, @[], @[], nil, isStatic=isStatic, isAsync=isAsync, isGenerator=false, isPrivate=isPrivate) if self.match(LeftBrace): @@ -612,10 +622,8 @@ proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPriva result.arguments.add(parameter) if self.match(Equal): 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: - 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): break 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 # if the user used 'await' in a non-async function and if the # 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: if line.kind == exprStmt: if ExprStmt(line).expression.kind == unaryExpr: @@ -633,7 +641,14 @@ proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPriva # is redundant result.isGenerator = true 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 = diff --git a/src/main.nim b/src/main.nim index 52d75ae..f45fca0 100644 --- a/src/main.nim +++ b/src/main.nim @@ -40,7 +40,7 @@ while true: optimized = initOptimizer().optimize(tree) except: echo &"A Nim runtime exception occurred: {getCurrentExceptionMsg()}" - raise + continue echo "Tokenization step: " for token in tokens: