From 3dead5a555d726a5233ede99f889c7a288b5add3 Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Sat, 5 Nov 2022 10:57:28 +0100 Subject: [PATCH] Fixed various bugs and added more tests. Also added nim.cfg --- nim.cfg | 1 + src/frontend/compiler.nim | 145 +++++++++++++++++++++++--------------- src/frontend/parser.nim | 5 +- src/main.nim | 18 ++--- tests/closures2.pn | 12 ++++ tests/cross_shadowing.pn | 21 ++++++ tests/dispatch.pn | 2 +- tests/generics.pn | 1 + tests/mutable.pn | 2 +- 9 files changed, 138 insertions(+), 69 deletions(-) create mode 100644 nim.cfg create mode 100644 tests/closures2.pn create mode 100644 tests/cross_shadowing.pn diff --git a/nim.cfg b/nim.cfg new file mode 100644 index 0000000..b4c3334 --- /dev/null +++ b/nim.cfg @@ -0,0 +1 @@ +--hints:off --warnings:off \ No newline at end of file diff --git a/src/frontend/compiler.nim b/src/frontend/compiler.nim index 25ed39d..95915b3 100644 --- a/src/frontend/compiler.nim +++ b/src/frontend/compiler.nim @@ -194,7 +194,7 @@ type # List of closed-over variables closures: seq[Name] # Compiler procedures called by pragmas - compilerProcs: TableRef[string, proc (self: Compiler, pragma: Pragma, node: ASTNode, name: Name)] + compilerProcs: TableRef[string, proc (self: Compiler, pragma: Pragma, name: Name)] # Stores line data for error reporting lines: seq[tuple[start, stop: int]] # The source of the current module, @@ -237,9 +237,9 @@ proc findByModule(self: Compiler, name: string): seq[Name] proc findByType(self: Compiler, name: string, kind: Type, depth: int = -1): seq[Name] proc compare(self: Compiler, a, b: Type): bool proc patchReturnAddress(self: Compiler, pos: int) -proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTnode, name: Name) -proc handlePurePragma(self: Compiler, pragma: Pragma, node: ASTnode, name: Name) -proc dispatchPragmas(self: Compiler, node: ASTnode, name: Name) +proc handleMagicPragma(self: Compiler, pragma: Pragma, name: Name) +proc handlePurePragma(self: Compiler, pragma: Pragma, name: Name) +proc dispatchPragmas(self: Compiler, name: Name) proc funDecl(self: Compiler, node: FunDecl, name: Name) proc typeDecl(self: Compiler, node: TypeDecl, name: Name) proc compileModule(self: Compiler, moduleName: string) @@ -260,7 +260,7 @@ proc newCompiler*(replMode: bool = false): Compiler = result.currentFunction = nil result.replMode = replMode result.currentModule = "" - result.compilerProcs = newTable[string, proc (self: Compiler, pragma: Pragma, node: ASTNode, name: Name)]() + result.compilerProcs = newTable[string, proc (self: Compiler, pragma: Pragma, name: Name)]() result.compilerProcs["magic"] = handleMagicPragma result.compilerProcs["pure"] = handlePurePragma result.source = "" @@ -491,10 +491,12 @@ proc resolve(self: Compiler, name: string): Name = ## Traverses all existing namespaces and returns ## the first object with the given name. Returns ## nil when the name can't be found. Note that - ## when a declaration is first resolved, it is - ## also compiled on-the-fly + ## when a type or function declaration is first + ## resolved, it is also compiled on-the-fly for obj in reversed(self.names): if obj.ident.token.lexeme == name: + if obj.kind == NameKind.Argument and obj.belongsTo != self.currentFunction: + continue if obj.owner != self.currentModule: # We don't own this name, but we # may still have access to it @@ -573,21 +575,28 @@ proc getStackPos(self: Compiler, name: Name): int = # Argument of a function we haven't compiled yet (or one that we're # not in). Ignore it, as it won't exist at runtime continue - elif not variable.belongsTo.isNil() and variable.belongsTo.valueType.isBuiltinFunction: - # Builtin functions don't exist at runtime either, so variables belonging to them - # are not present in the stack - continue - elif not variable.valueType.isNil() and variable.valueType.kind == Generic: - # Generics are also a purely compile-time construct and are therefore - # ignored as far as stack positioning goes - continue + elif not variable.belongsTo.isNil(): + if variable.belongsTo.valueType.isBuiltinFunction: + # Builtin functions don't exist at runtime either, so variables belonging to them + # are not present in the stack + continue + elif variable.valueType.kind == Generic: + # Generics are also a purely compile-time construct and are therefore + # ignored as far as stack positioning goes + continue + elif variable.belongsTo != name.belongsTo: + # Since referencing a function immediately compiles it, this means + # that if there's a function A with an argument x that calls another + # function B with an argument also named x, that second "x" would + # shadow the first one, leading to an incorrect stack offset + continue elif variable.owner != self.currentModule: # We don't own this variable, so we check # if the owner exported it to us. If not, # we skip it and pretend it doesn't exist if variable.isPrivate or not variable.exported: continue - elif name == variable: + if name == variable: # After all of these checks, we can # finally check whether the two names # match (note: this also includes scope @@ -931,20 +940,35 @@ proc typeToStr(self: Compiler, typ: Type): string = proc findByName(self: Compiler, name: string): seq[Name] = ## Looks for objects that have been already declared - ## with the given name. Returns all objects that apply + ## with the given name. Returns all objects that apply. + ## As with resolve(), this will cause type and function + ## declarations to be compiled on-the-fly + for obj in reversed(self.names): if obj.ident.token.lexeme == name: if obj.owner != self.currentModule: if obj.isPrivate or not obj.exported: continue result.add(obj) + for n in result: + if n.resolved: + continue + n.resolved = true + case n.kind: + of NameKind.CustomType: + self.typeDecl(TypeDecl(n.node), n) + of NameKind.Function: + if not n.valueType.isGeneric: + self.funDecl(FunDecl(n.node), n) + else: + discard proc findByModule(self: Compiler, name: string): seq[Name] = ## Looks for objects that have been already declared AS ## public within the given module. Returns all objects that apply for obj in reversed(self.names): - if obj.owner == name: + if not obj.isPrivate and obj.owner == name: result.add(obj) @@ -1134,8 +1158,6 @@ proc endScope(self: Compiler) = return for name in self.names: if name.depth > self.depth: - if not name.belongsTo.isNil() and not name.belongsTo.resolved: - continue names.add(name) #[if not name.resolved: # TODO: Emit a warning? @@ -1148,7 +1170,7 @@ proc endScope(self: Compiler) = if name.kind == NameKind.Var: inc(popCount) elif name.kind == NameKind.Argument: - if not name.belongsTo.valueType.isBuiltinFunction and name.belongsTo.resolved: + if not name.belongsTo.valueType.isBuiltinFunction and name.belongsTo.resolved and not name.belongsTo.valueType.isGeneric: # We don't pop arguments to builtin functions because those don't # actually have scopes: their arguments are temporaries on the stack inc(popCount) @@ -1202,7 +1224,6 @@ proc endScope(self: Compiler) = inc(idx) - proc unpackGenerics(self: Compiler, condition: Expression, list: var seq[tuple[match: bool, kind: Type]], accept: bool = true) = ## Recursively unpacks a type constraint in a generic type case condition.kind: @@ -1227,7 +1248,7 @@ proc unpackGenerics(self: Compiler, condition: Expression, list: var seq[tuple[m self.error("invalid type constraint in generic declaration", condition) -proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name = +proc declareName(self: Compiler, node: ASTNode, mutable: bool = false) = ## Statically declares a name into the current scope. ## "Declaring" a name only means updating our internal ## list of identifiers so that further calls to resolve() @@ -1235,15 +1256,16 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name = ## declare a variable at runtime: the value is already ## on the stack var declaredName: string = "" + var n: Name case node.kind: of NodeKind.varDecl: var node = VarDecl(node) - # Creates a new Name entry so that self.identifier emits the proper stack offset if self.names.high() > 16777215: # If someone ever hits this limit in real-world scenarios, I swear I'll # slap myself 100 times with a sign saying "I'm dumb". Mark my words self.error("cannot declare more than 16777215 variables at a time") declaredName = node.name.token.lexeme + # Creates a new Name entry so that self.identifier emits the proper stack offset self.names.add(Name(depth: self.depth, ident: node.name, isPrivate: node.isPrivate, @@ -1256,12 +1278,13 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name = kind: NameKind.Var, node: node )) + n = self.names[^1] if mutable: self.names[^1].valueType.mutable = true - result = self.names[^1] of NodeKind.funDecl: var node = FunDecl(node) - result = Name(depth: self.depth, + declaredName = node.name.token.lexeme + var fn = Name(depth: self.depth, isPrivate: node.isPrivate, isConst: false, owner: self.currentModule, @@ -1276,31 +1299,32 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name = line: node.token.line, kind: NameKind.Function, belongsTo: self.currentFunction) + n = fn # First we declare the function's generics, if it has any. # This is because the function's return type may in itself # be a generic, so it needs to exist first var constraints: seq[tuple[match: bool, kind: Type]] = @[] for gen in node.generics: self.unpackGenerics(gen.cond, constraints) - self.names.add(Name(depth: result.depth + 1, + self.names.add(Name(depth: fn.depth + 1, isPrivate: true, valueType: Type(kind: Generic, name: gen.name.token.lexeme, mutable: false, cond: constraints), codePos: 0, isLet: false, - line: result.node.token.line, - belongsTo: result, + line: fn.node.token.line, + belongsTo: fn, ident: gen.name, owner: self.currentModule)) constraints = @[] if not node.returnType.isNil(): - result.valueType.returnType = self.inferOrError(node.returnType, allowGeneric=true) - self.names.add(result) + fn.valueType.returnType = self.inferOrError(node.returnType, allowGeneric=true) + self.names.add(fn) # We now declare and typecheck the function's # arguments - for argument in FunDecl(result.node).arguments: + for argument in FunDecl(fn.node).arguments: if self.names.high() > 16777215: self.error("cannot declare more than 16777215 variables at a time") - self.names.add(Name(depth: result.depth + 1, + self.names.add(Name(depth: fn.depth + 1, isPrivate: true, owner: self.currentModule, isConst: false, @@ -1309,12 +1333,12 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name = codePos: 0, isLet: false, line: argument.name.token.line, - belongsTo: result, + belongsTo: fn, kind: NameKind.Argument )) - result.valueType.args.add((self.names[^1].ident.token.lexeme, self.names[^1].valueType)) + fn.valueType.args.add((self.names[^1].ident.token.lexeme, self.names[^1].valueType)) if node.generics.len() > 0: - result.valueType.isGeneric = true + fn.valueType.isGeneric = true of NodeKind.importStmt: var node = ImportStmt(node) var name = node.moduleName.token.lexeme.extractFilename().replace(".pn", "") @@ -1326,11 +1350,12 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name = kind: NameKind.Module, isPrivate: false )) - result = self.names[^1] + n = self.names[^1] else: discard # TODO: Types, enums + self.dispatchPragmas(n) for name in self.findByName(declaredName): - if name == result: + if name == n: continue elif (name.kind == NameKind.Var and name.depth == self.depth) or name.kind in [NameKind.Module, NameKind.CustomType, NameKind.Enum]: self.error(&"attempt to redeclare '{name.ident.token.lexeme}', which was previously defined in '{name.owner}' at line {name.line}") @@ -1357,47 +1382,49 @@ proc patchBreaks(self: Compiler) = self.patchJump(brk) -proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTNode, name: Name) = +proc handleMagicPragma(self: Compiler, pragma: Pragma, name: Name) = ## Handles the "magic" pragma. Assumes the given name is already ## declared if pragma.args.len() != 1: self.error("'magic' pragma: wrong number of arguments") elif pragma.args[0].kind != strExpr: self.error("'magic' pragma: wrong type of argument (constant string expected)") - elif node.kind != NodeKind.funDecl: + elif name.node.kind != NodeKind.funDecl: self.error("'magic' pragma is not valid in this context") - var node = FunDecl(node) + var node = FunDecl(name.node) name.valueType.isBuiltinFunction = true name.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2] # The magic pragma ignores the function's body node.body = nil -proc handlePurePragma(self: Compiler, pragma: Pragma, node: ASTNode, name: Name) = +proc handlePurePragma(self: Compiler, pragma: Pragma, name: Name) = ## Handles the "pure" pragma - case node.kind: + case name.node.kind: of NodeKind.funDecl: - FunDecl(node).isPure = true + FunDecl(name.node).isPure = true of lambdaExpr: - LambdaExpr(node).isPure = true + LambdaExpr(name.node).isPure = true else: self.error("'pure' pragma is not valid in this context") -proc dispatchPragmas(self: Compiler, node: ASTnode, name: Name) = +proc dispatchPragmas(self: Compiler, name: Name) = ## Dispatches pragmas bound to objects + if name.node.isNil(): + return var pragmas: seq[Pragma] = @[] - case node.kind: + case name.node.kind: of NodeKind.funDecl, NodeKind.typeDecl, NodeKind.varDecl: - pragmas = Declaration(node).pragmas + pragmas = Declaration(name.node).pragmas of lambdaExpr: - pragmas = LambdaExpr(node).pragmas + pragmas = LambdaExpr(name.node).pragmas else: discard # Unreachable for pragma in pragmas: if pragma.name.token.lexeme notin self.compilerProcs: self.error(&"unknown pragma '{pragma.name.token.lexeme}'") - self.compilerProcs[pragma.name.token.lexeme](self, pragma, node, name) + self.compilerProcs[pragma.name.token.lexeme](self, pragma, name) proc patchReturnAddress(self: Compiler, pos: int) = @@ -1629,7 +1656,7 @@ proc identifier(self: Compiler, node: IdentExpr) = else: # Static name resolution, loads value at index in the stack. Very fast. Much wow. self.emitByte(LoadVar, node.token.line) - # No need to check for -1 here: we already did a nil check above!รน + # No need to check for -1 here: we already did a nil check above! self.emitBytes(self.getStackPos(s).toTriple(), node.token.line) @@ -1764,6 +1791,7 @@ proc specialize(self: Compiler, name: Name, args: seq[Expression]): Name = var mapping: TableRef[string, Type] = newTable[string, Type]() var kind: Type result = deepCopy(name) + result.valueType.isGeneric = false case name.kind: of NameKind.Function: # This first loop checks if a user tries to reassign a generic's @@ -1793,6 +1821,7 @@ proc specialize(self: Compiler, name: Name, args: seq[Expression]): Name = )) if result.valueType.returnType.kind == Generic: result.valueType.returnType = mapping[result.valueType.returnType.name] + # self.funDecl(FunDecl(result.node), result) else: discard # TODO: Custom user-defined types @@ -1825,6 +1854,7 @@ proc callExpr(self: Compiler, node: CallExpr): Name {.discardable.} = # very last moment to compile it, once # that info is available to us result = self.specialize(result, argExpr) + self.funDecl(FunDecl(result.node), result) # Now we call it self.generateCall(result, argExpr, node.token.line) of NodeKind.callExpr: @@ -1990,7 +2020,7 @@ proc importStmt(self: Compiler, node: ImportStmt) = let filename = splitPath(node.moduleName.token.lexeme).tail try: self.compileModule(node.moduleName.token.lexeme) - discard self.declareName(node) + self.declareName(node) except IOError: self.error(&"""could not import '{filename}': {getCurrentExceptionMsg()}""") except OSError: @@ -2148,6 +2178,8 @@ proc funDecl(self: Compiler, node: FunDecl, name: Name) = ## Compiles function declarations if node.token.kind == Operator and node.name.token.lexeme in [".", ]: self.error(&"Due to current compiler limitations, the '{node.name.token.lexeme}' operator cannot be overridden", node.name) + if name.valueType.isBuiltinFunction: + return var node = node var jmp: int # We store the current function @@ -2177,6 +2209,7 @@ proc funDecl(self: Compiler, node: FunDecl, name: Name) = else: self.chunk.cfi.add(0.toDouble()) if BlockStmt(node.body).code.len() == 0: + raise newException(IndexDefect, "") self.error("cannot declare function with empty body") # Since the deferred array is a linear # sequence of instructions and we want @@ -2241,9 +2274,8 @@ proc declaration(self: Compiler, node: Declaration) = ## the first time case node.kind: of NodeKind.varDecl, NodeKind.funDecl, NodeKind.typeDecl: - self.dispatchPragmas(node, self.declareName(node)) + self.declareName(node) if node.kind == NodeKind.varDecl: - self.names[^1].resolved = true # We compile this immediately because we # need to keep the stack in the right state # at runtime @@ -2285,7 +2317,10 @@ proc compileModule(self: Compiler, moduleName: string) = ## using the compiler's internal parser and lexer objects var path = "" for i, searchPath in moduleLookupPaths: - path = joinPath(getCurrentDir(), joinPath(searchPath, moduleName)) + if searchPath == "": + path = joinPath(getCurrentDir(), joinPath(splitPath(self.file).head, moduleName)) + else: + path = joinPath(getCurrentDir(), joinPath(searchPath, moduleName)) if fileExists(path): break elif i == searchPath.high(): diff --git a/src/frontend/parser.nim b/src/frontend/parser.nim index c1a9a91..8aeb249 100644 --- a/src/frontend/parser.nim +++ b/src/frontend/parser.nim @@ -730,7 +730,10 @@ proc importStmt(self: Parser, fromStmt: bool = false): Statement = lexer.fillSymbolTable() var path = "" for i, searchPath in moduleLookupPaths: - path = joinPath(getCurrentDir(), joinPath(searchPath, moduleName)) + if searchPath == "": + path = joinPath(getCurrentDir(), joinPath(splitPath(self.file).head, moduleName)) + else: + path = joinPath(getCurrentDir(), joinPath(searchPath, moduleName)) if fileExists(path): break elif i == searchPath.high(): diff --git a/src/main.nim b/src/main.nim index 8b296b4..cfa0c8c 100644 --- a/src/main.nim +++ b/src/main.nim @@ -67,7 +67,7 @@ proc repl = tokenizer.fillSymbolTable() editor.bindEvent(jeQuit): stdout.styledWriteLine(fgGreen, "Goodbye!") - editor.prompt = "" + editor.prompt = "=> " keep = false input = "" editor.bindKey("ctrl+a"): @@ -76,10 +76,6 @@ proc repl = editor.content.`end`() while keep: try: - # We incrementally add content to the input - # so that you can, for example, define a function - # then press enter and use it at the next iteration - # of the read loop input = editor.read() if input.len() == 0: continue @@ -153,7 +149,7 @@ proc repl = fgYellow, &"'{exc.file.extractFilename()}'", fgRed, ", line ", fgYellow, $exc.line, fgRed, " at ", fgYellow, &"'{exc.lexeme.escape()}'", fgRed, ": ", fgGreen , getCurrentExceptionMsg()) styledEcho fgBlue, "Source line: " , fgDefault, line - styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(exc.lexeme)) & "^".repeat(relPos.stop - relPos.start - line.find(exc.lexeme)) + styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(exc.lexeme)) & "^".repeat(abs(relPos.stop - relPos.start - line.find(exc.lexeme))) except ParseError: input = "" let exc = ParseError(getCurrentException()) @@ -169,7 +165,7 @@ proc repl = fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'", fgRed, ": ", fgGreen , getCurrentExceptionMsg()) styledEcho fgBlue, "Source line: " , fgDefault, line - styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(lexeme)) & "^".repeat(relPos.stop - relPos.start - line.find(lexeme)) + styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(lexeme)) & "^".repeat(abs(relPos.stop - relPos.start - line.find(lexeme))) except CompileError: let exc = CompileError(getCurrentException()) let lexeme = exc.node.token.lexeme @@ -184,7 +180,7 @@ proc repl = fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'", fgRed, ": ", fgGreen , getCurrentExceptionMsg()) styledEcho fgBlue, "Source line: " , fgDefault, line - styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(lexeme)) & "^".repeat(relPos.stop - relPos.start - line.find(lexeme)) + styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(lexeme)) & "^".repeat(abs(relPos.stop - relPos.start - line.find(lexeme))) except SerializationError: let exc = SerializationError(getCurrentException()) stderr.styledWriteLine(fgRed, "A fatal error occurred while (de-)serializing", fgYellow, &"'{exc.file}'", fgGreen, ": ", getCurrentExceptionMsg()) @@ -280,7 +276,7 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false, dum fgYellow, &"'{exc.file.extractFilename()}'", fgRed, ", line ", fgYellow, $exc.line, fgRed, " at ", fgYellow, &"'{exc.lexeme.escape()}'", fgRed, ": ", fgGreen , getCurrentExceptionMsg()) styledEcho fgBlue, "Source line: " , fgDefault, line - styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(exc.lexeme)) & "^".repeat(relPos.stop - relPos.start - line.find(exc.lexeme)) + styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(exc.lexeme)) & "^".repeat(abs(relPos.stop - relPos.start - line.find(exc.lexeme))) except ParseError: let exc = ParseError(getCurrentException()) let lexeme = exc.token.lexeme @@ -295,7 +291,7 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false, dum fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'", fgRed, ": ", fgGreen , getCurrentExceptionMsg()) styledEcho fgBlue, "Source line: " , fgDefault, line - styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(lexeme)) & "^".repeat(relPos.stop - relPos.start - line.find(lexeme)) + styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(lexeme)) & "^".repeat(abs(relPos.stop - relPos.start - line.find(lexeme))) except CompileError: let exc = CompileError(getCurrentException()) let lexeme = exc.node.token.lexeme @@ -310,7 +306,7 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false, dum fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'", fgRed, ": ", fgGreen , getCurrentExceptionMsg()) styledEcho fgBlue, "Source line: " , fgDefault, line - styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(lexeme)) & "^".repeat(relPos.stop - relPos.start - line.find(lexeme)) + styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(lexeme)) & "^".repeat(abs(relPos.stop - relPos.start - line.find(lexeme))) except SerializationError: let exc = SerializationError(getCurrentException()) stderr.styledWriteLine(fgRed, "A fatal error occurred while (de-)serializing", fgYellow, &"'{exc.file}'", fgGreen, ": ", getCurrentExceptionMsg()) diff --git a/tests/closures2.pn b/tests/closures2.pn new file mode 100644 index 0000000..e21efaa --- /dev/null +++ b/tests/closures2.pn @@ -0,0 +1,12 @@ +import std; + + +fn makeAdder(x: int): fn (n: int): int { + fn adder(n: int): int { + return x + n; + } + return adder; +} + + +print(makeAdder(5)(2)); # 7 \ No newline at end of file diff --git a/tests/cross_shadowing.pn b/tests/cross_shadowing.pn new file mode 100644 index 0000000..5f14908 --- /dev/null +++ b/tests/cross_shadowing.pn @@ -0,0 +1,21 @@ +# Tests shadowing of arguments and local variables +# across functions +import std; + + +fn first(x: int): int { + var y = x; + y = y + 1; + return y; +} + + +fn second(x: int): int { + var y = first(x); + y = y + 1; + return y; +} + + + +print(second(0)); # 2 diff --git a/tests/dispatch.pn b/tests/dispatch.pn index b1521af..6b0ad64 100644 --- a/tests/dispatch.pn +++ b/tests/dispatch.pn @@ -7,4 +7,4 @@ operator `+`(a: int): int { +1; # Works: defined for int64 -+1'i32; # Will not work +# +1'i32; # Will not work diff --git a/tests/generics.pn b/tests/generics.pn index 389d777..5de4c76 100644 --- a/tests/generics.pn +++ b/tests/generics.pn @@ -1,3 +1,4 @@ +# Tests generic functions import std; diff --git a/tests/mutable.pn b/tests/mutable.pn index 0c879c4..795e1dd 100644 --- a/tests/mutable.pn +++ b/tests/mutable.pn @@ -1,4 +1,4 @@ -# Tests var parameters +# Tests var parameters. TODO: They don't actually exist yet, they're just checked statically import std;