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 findByType*(self: Compiler, name: string, kind: Type): seq[Name]
|
||||||
method compare*(self: Compiler, a, b: Type): bool
|
method compare*(self: Compiler, a, b: Type): bool
|
||||||
method match*(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allowFwd: bool = true): Name
|
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
|
## End of forward declarations
|
||||||
|
|
||||||
## Utility functions
|
## 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):
|
for (a, b) in zip(result.valueType.args, kind.args):
|
||||||
if not a.kind.isAny() and b.kind.isAny():
|
if not a.kind.isAny() and b.kind.isAny():
|
||||||
self.error("any is not a valid type in this context", node)
|
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 handleMagicPragma(self: BytecodeCompiler, pragma: Pragma, name: BytecodeName)
|
||||||
proc handlePurePragma(self: BytecodeCompiler, pragma: Pragma, name: BytecodeName)
|
proc handlePurePragma(self: BytecodeCompiler, pragma: Pragma, name: BytecodeName)
|
||||||
proc handleErrorPragma(self: BytecodeCompiler, pragma: Pragma, name: BytecodeName)
|
proc handleErrorPragma(self: BytecodeCompiler, pragma: Pragma, name: BytecodeName)
|
||||||
proc dispatchPragmas(self: BytecodeCompiler, name: BytecodeName)
|
method dispatchPragmas(self: BytecodeCompiler, name: BytecodeName)
|
||||||
proc dispatchDelayedPragmas(self: BytecodeCompiler, name: BytecodeName)
|
method dispatchDelayedPragmas(self: BytecodeCompiler, name: BytecodeName)
|
||||||
proc funDecl(self: BytecodeCompiler, node: FunDecl, name: BytecodeName)
|
proc funDecl(self: BytecodeCompiler, node: FunDecl, name: BytecodeName)
|
||||||
proc compileModule(self: BytecodeCompiler, module: BytecodeName)
|
proc compileModule(self: BytecodeCompiler, module: BytecodeName)
|
||||||
proc generateCall(self: BytecodeCompiler, fn: BytecodeName, args: seq[Expression], line: int)
|
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
|
# 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)
|
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) =
|
proc patchForwardDeclarations(self: BytecodeCompiler) =
|
||||||
## Patches forward declarations and looks
|
## Patches forward declarations and looks
|
||||||
## for their implementations so that calls
|
## 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")
|
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
|
## Dispatches pragmas bound to objects
|
||||||
if name.node.isNil():
|
if name.node.isNil():
|
||||||
return
|
return
|
||||||
|
@ -936,7 +930,7 @@ proc dispatchPragmas(self: BytecodeCompiler, name: BytecodeName) =
|
||||||
f.handler(self, pragma, name)
|
f.handler(self, pragma, name)
|
||||||
|
|
||||||
|
|
||||||
proc dispatchDelayedPragmas(self: BytecodeCompiler, name: BytecodeName) =
|
method dispatchDelayedPragmas(self: BytecodeCompiler, name: BytecodeName) =
|
||||||
## Dispatches pragmas bound to objects once they
|
## Dispatches pragmas bound to objects once they
|
||||||
## are called. Only applies to functions
|
## are called. Only applies to functions
|
||||||
if name.node.isNil():
|
if name.node.isNil():
|
||||||
|
@ -989,7 +983,7 @@ proc generateCall(self: BytecodeCompiler, fn: Type, args: seq[Expression], line:
|
||||||
self.patchReturnAddress(pos)
|
self.patchReturnAddress(pos)
|
||||||
|
|
||||||
|
|
||||||
proc prepareFunction(self: BytecodeCompiler, fn: BytecodeName) =
|
method prepareFunction(self: BytecodeCompiler, fn: BytecodeName) =
|
||||||
## "Prepares" a function declaration by declaring
|
## "Prepares" a function declaration by declaring
|
||||||
## its arguments and typechecking it
|
## 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.} =
|
method literal*(self: Compiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
|
||||||
## Compiles literal expressions
|
## Compiles literal expressions
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,21 @@
|
||||||
|
|
||||||
## Reusable objects to simplify code generation using templates
|
## Reusable objects to simplify code generation using templates
|
||||||
|
|
||||||
type
|
type
|
||||||
CodeGenerator* = ref object of RootObj
|
GeneratorKind* = enum
|
||||||
|
## A code generator enumeration
|
||||||
|
Literal,
|
||||||
|
CodeGenerator* = object
|
||||||
## A generic code generator
|
## A generic code generator
|
||||||
code: string
|
case kind*: GeneratorKind
|
||||||
vars: seq[tuple[name, value: string]]
|
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 {
|
operator `/`*(a, b: float64): float64 {
|
||||||
#pragma[magic: "DivFloat64", pure]
|
#pragma[magic: "DivideFloat64", pure]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
operator `/`*(a, b: float32): float32 {
|
operator `/`*(a, b: float32): float32 {
|
||||||
#pragma[magic: "DivFloat32", pure]
|
#pragma[magic: "DivideFloat32", pure]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue