Minor improvements to modularization (moved even more procedures to generalized methods)
This commit is contained in:
parent
db021cb821
commit
ffe57c134a
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -67,4 +67,5 @@ proc newNativeCCompiler*(replMode: bool = false): NativeCCompiler =
|
|||
|
||||
method literal*(self: Compiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
|
||||
## Compiles literal expressions
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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]]
|
||||
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
|
||||
|
|
@ -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]
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue