diff --git a/src/frontend/compiler/compiler.nim b/src/frontend/compiler/compiler.nim index df3e241..ca51308 100644 --- a/src/frontend/compiler/compiler.nim +++ b/src/frontend/compiler/compiler.nim @@ -233,6 +233,9 @@ method findInModule*(self: Compiler, name: string, module: Name): seq[Name] method findByType*(self: Compiler, name: string, kind: Type): seq[Name] method compare*(self: Compiler, a, b: Type): bool method match*(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allowFwd: bool = true): Name +method prepareFunction*(self: Compiler, name: Name) {.base.} = discard +method dispatchPragmas(self: Compiler, name: Name) {.base.} = discard +method dispatchDelayedPragmas(self: Compiler, name: Name) {.base.} = discard ## End of forward declarations ## Utility functions @@ -815,3 +818,207 @@ method match*(self: Compiler, name: string, kind: Type, node: ASTNode = nil, all for (a, b) in zip(result.valueType.args, kind.args): if not a.kind.isAny() and b.kind.isAny(): self.error("any is not a valid type in this context", node) + + +proc beginScope*(self: Compiler) = + ## Begins a new local scope by incrementing the current + ## scope's depth + inc(self.depth) + + +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: + of identExpr: + list.add((accept, self.inferOrError(condition))) + if list[^1].kind.kind == Auto: + self.error("automatic types cannot be used within generics", condition) + of binaryExpr: + let condition = BinaryExpr(condition) + case condition.operator.lexeme: + of "|": + self.unpackGenerics(condition.a, list) + self.unpackGenerics(condition.b, list) + else: + self.error("invalid type constraint in generic declaration", condition) + of unaryExpr: + let condition = UnaryExpr(condition) + case condition.operator.lexeme: + of "~": + self.unpackGenerics(condition.a, list, accept=false) + else: + self.error("invalid type constraint in generic declaration", condition) + else: + self.error("invalid type constraint in generic declaration", condition) + + +proc unpackUnion*(self: Compiler, condition: Expression, list: var seq[tuple[match: bool, kind: Type]], accept: bool = true) = + ## Recursively unpacks a type union + case condition.kind: + of identExpr: + list.add((accept, self.inferOrError(condition))) + of binaryExpr: + let condition = BinaryExpr(condition) + case condition.operator.lexeme: + of "|": + self.unpackUnion(condition.a, list) + self.unpackUnion(condition.b, list) + else: + self.error("invalid type constraint in type union", condition) + of unaryExpr: + let condition = UnaryExpr(condition) + case condition.operator.lexeme: + of "~": + self.unpackUnion(condition.a, list, accept=false) + else: + self.error("invalid type constraint in type union", condition) + else: + self.error("invalid type constraint in type union", condition) + + +proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} = + ## 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() + ## correctly return them. There is no code to actually + ## declare a variable at runtime: the value is already + ## on the stack + var declaredName: string = "" + var n: Name + 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 names at a time") + case node.kind: + of NodeKind.varDecl: + var node = VarDecl(node) + 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, + owner: self.currentModule, + file: self.file, + isConst: node.isConst, + valueType: nil, # Done later + isLet: node.isLet, + line: node.token.line, + belongsTo: self.currentFunction, + kind: NameKind.Var, + node: node, + isReal: true + )) + n = self.names[^1] + of NodeKind.funDecl: + var node = FunDecl(node) + declaredName = node.name.token.lexeme + var fn = Name(depth: self.depth, + isPrivate: node.isPrivate, + isConst: false, + owner: self.currentModule, + file: self.file, + valueType: Type(kind: Function, + returnType: nil, # We check it later + args: @[], + fun: node, + forwarded: node.body.isNil(), + isAuto: false), + ident: node.name, + node: node, + isLet: false, + line: node.token.line, + kind: NameKind.Function, + belongsTo: self.currentFunction, + isReal: true) + if node.isTemplate: + fn.valueType.compiled = true + if node.generics.len() > 0: + fn.isGeneric = true + var typ: Type + for argument in node.arguments: + typ = self.infer(argument.valueType) + if not typ.isNil() and typ.kind == Auto: + fn.valueType.isAuto = true + if fn.isGeneric: + self.error("automatic types cannot be used within generics", argument.valueType) + break + typ = self.infer(node.returnType) + if not typ.isNil() and typ.kind == Auto: + fn.valueType.isAuto = true + if fn.isGeneric: + self.error("automatic types cannot be used within generics", node.returnType) + self.names.add(fn) + self.prepareFunction(fn) + n = fn + of NodeKind.importStmt: + var node = ImportStmt(node) + # We change the name of the module internally so that + # if you import /path/to/mod, then doing mod.f() will + # still work without any extra work on our end. Note how + # we don't change the metadata about the identifier's + # position so that error messages still highlight the + # full path + let path = node.moduleName.token.lexeme + node.moduleName.token.lexeme = node.moduleName.token.lexeme.extractFilename() + self.names.add(Name(depth: self.depth, + owner: self.currentModule, + file: "", # The file of the module isn't known until it's compiled! + path: path, + ident: node.moduleName, + line: node.moduleName.token.line, + kind: NameKind.Module, + isPrivate: false, + isReal: true + )) + n = self.names[^1] + declaredName = self.names[^1].ident.token.lexeme + of NodeKind.typeDecl: + var node = TypeDecl(node) + self.names.add(Name(kind: NameKind.CustomType, + depth: self.depth, + owner: self.currentModule, + node: node, + ident: node.name, + line: node.token.line, + isPrivate: node.isPrivate, + isReal: true, + belongsTo: self.currentFunction + ) + ) + n = self.names[^1] + declaredName = node.name.token.lexeme + if node.value.isNil(): + discard # TODO: Fields + else: + case node.value.kind: + of identExpr: + n.valueType = self.inferOrError(node.value) + of binaryExpr: + # Type union + n.valueType = Type(kind: Union, types: @[]) + self.unpackUnion(node.value, n.valueType.types) + else: + discard + else: + discard # TODO: enums + if not n.isNil(): + self.dispatchPragmas(n) + for name in self.findByName(declaredName): + if name == n: + continue + # We don't check for name clashes with functions because self.match() does that + if name.kind in [NameKind.Var, NameKind.Module, NameKind.CustomType, NameKind.Enum] and name.depth == n.depth and name.owner == n.owner: + self.error(&"re-declaration of {declaredName} is not allowed (previously declared in {name.owner.ident.token.lexeme}:{name.ident.token.line}:{name.ident.token.relPos.start})") + for name in self.names: + if name == n: + break + if name.ident.token.lexeme != declaredName: + continue + if name.owner != n.owner and (name.isPrivate or n.owner notin name.exportedTo): + continue + if name.kind in [NameKind.Var, NameKind.Module, NameKind.CustomType, NameKind.Enum]: + if name.depth < n.depth: + self.warning(WarningKind.ShadowOuterScope, &"'{declaredName}' shadows a name from an outer scope ({name.owner.file}.pn:{name.ident.token.line}:{name.ident.token.relPos.start})", n) + elif name.owner != n.owner: + self.warning(WarningKind.ShadowOuterScope, &"'{declaredName}' shadows a name from an outer module ({name.owner.file}.pn:{name.ident.token.line}:{name.ident.token.relPos.start})", n) + return n diff --git a/src/frontend/compiler/targets/bytecode/target.nim b/src/frontend/compiler/targets/bytecode/target.nim index b4555dc..ab6bd3c 100644 --- a/src/frontend/compiler/targets/bytecode/target.nim +++ b/src/frontend/compiler/targets/bytecode/target.nim @@ -114,12 +114,12 @@ proc patchReturnAddress(self: BytecodeCompiler, pos: int) proc handleMagicPragma(self: BytecodeCompiler, pragma: Pragma, name: BytecodeName) proc handlePurePragma(self: BytecodeCompiler, pragma: Pragma, name: BytecodeName) proc handleErrorPragma(self: BytecodeCompiler, pragma: Pragma, name: BytecodeName) -proc dispatchPragmas(self: BytecodeCompiler, name: BytecodeName) -proc dispatchDelayedPragmas(self: BytecodeCompiler, name: BytecodeName) +method dispatchPragmas(self: BytecodeCompiler, name: BytecodeName) +method dispatchDelayedPragmas(self: BytecodeCompiler, name: BytecodeName) proc funDecl(self: BytecodeCompiler, node: FunDecl, name: BytecodeName) proc compileModule(self: BytecodeCompiler, module: BytecodeName) proc generateCall(self: BytecodeCompiler, fn: BytecodeName, args: seq[Expression], line: int) -proc prepareFunction(self: BytecodeCompiler, fn: BytecodeName) +method prepareFunction(self: BytecodeCompiler, fn: BytecodeName) # End of forward declarations @@ -544,12 +544,6 @@ proc handleBuiltinFunction(self: BytecodeCompiler, fn: Type, args: seq[Expressio self.error(&"unknown built-in: '{fn.builtinOp}'", fn.fun) -proc beginScope(self: BytecodeCompiler) = - ## Begins a new local scope by incrementing the current - ## scope's depth - inc(self.depth) - - proc patchForwardDeclarations(self: BytecodeCompiler) = ## Patches forward declarations and looks ## for their implementations so that calls @@ -914,7 +908,7 @@ proc handlePurePragma(self: BytecodeCompiler, pragma: Pragma, name: BytecodeName self.error("'pure' pragma is not valid in this context") -proc dispatchPragmas(self: BytecodeCompiler, name: BytecodeName) = +method dispatchPragmas(self: BytecodeCompiler, name: BytecodeName) = ## Dispatches pragmas bound to objects if name.node.isNil(): return @@ -936,7 +930,7 @@ proc dispatchPragmas(self: BytecodeCompiler, name: BytecodeName) = f.handler(self, pragma, name) -proc dispatchDelayedPragmas(self: BytecodeCompiler, name: BytecodeName) = +method dispatchDelayedPragmas(self: BytecodeCompiler, name: BytecodeName) = ## Dispatches pragmas bound to objects once they ## are called. Only applies to functions if name.node.isNil(): @@ -989,7 +983,7 @@ proc generateCall(self: BytecodeCompiler, fn: Type, args: seq[Expression], line: self.patchReturnAddress(pos) -proc prepareFunction(self: BytecodeCompiler, fn: BytecodeName) = +method prepareFunction(self: BytecodeCompiler, fn: BytecodeName) = ## "Prepares" a function declaration by declaring ## its arguments and typechecking it diff --git a/src/frontend/compiler/targets/native/target.nim b/src/frontend/compiler/targets/native/target.nim index c2787f8..1cfa34b 100644 --- a/src/frontend/compiler/targets/native/target.nim +++ b/src/frontend/compiler/targets/native/target.nim @@ -67,4 +67,5 @@ proc newNativeCCompiler*(replMode: bool = false): NativeCCompiler = method literal*(self: Compiler, node: ASTNode, compile: bool = true): Type {.discardable.} = ## Compiles literal expressions - \ No newline at end of file + + diff --git a/src/frontend/compiler/targets/native/util/generators.nim b/src/frontend/compiler/targets/native/util/generators.nim index c2f0ac7..baa982b 100644 --- a/src/frontend/compiler/targets/native/util/generators.nim +++ b/src/frontend/compiler/targets/native/util/generators.nim @@ -14,8 +14,21 @@ ## Reusable objects to simplify code generation using templates -type - CodeGenerator* = ref object of RootObj +type + GeneratorKind* = enum + ## A code generator enumeration + Literal, + CodeGenerator* = object ## A generic code generator - code: string - vars: seq[tuple[name, value: string]] \ No newline at end of file + case kind*: GeneratorKind + of Literal: + lit: string + + +proc generate*(self: CodeGenerator): string = + ## Generates the source code for the given + ## code generator object and returns it + case self.kind: + of Literal: + return self.lit + \ No newline at end of file diff --git a/src/peon/stdlib/builtins/arithmetics.pn b/src/peon/stdlib/builtins/arithmetics.pn index 0a1d9ed..f5f0dab 100644 --- a/src/peon/stdlib/builtins/arithmetics.pn +++ b/src/peon/stdlib/builtins/arithmetics.pn @@ -89,12 +89,12 @@ operator `/`*[T: UnsignedInteger](a, b: T): T { operator `/`*(a, b: float64): float64 { - #pragma[magic: "DivFloat64", pure] + #pragma[magic: "DivideFloat64", pure] } operator `/`*(a, b: float32): float32 { - #pragma[magic: "DivFloat32", pure] + #pragma[magic: "DivideFloat32", pure] }