Fixed various bugs and added more tests. Also added nim.cfg
This commit is contained in:
parent
6f444582a4
commit
3dead5a555
|
@ -194,7 +194,7 @@ type
|
||||||
# List of closed-over variables
|
# List of closed-over variables
|
||||||
closures: seq[Name]
|
closures: seq[Name]
|
||||||
# Compiler procedures called by pragmas
|
# 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
|
# Stores line data for error reporting
|
||||||
lines: seq[tuple[start, stop: int]]
|
lines: seq[tuple[start, stop: int]]
|
||||||
# The source of the current module,
|
# 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 findByType(self: Compiler, name: string, kind: Type, depth: int = -1): seq[Name]
|
||||||
proc compare(self: Compiler, a, b: Type): bool
|
proc compare(self: Compiler, a, b: Type): bool
|
||||||
proc patchReturnAddress(self: Compiler, pos: int)
|
proc patchReturnAddress(self: Compiler, pos: int)
|
||||||
proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTnode, name: Name)
|
proc handleMagicPragma(self: Compiler, pragma: Pragma, name: Name)
|
||||||
proc handlePurePragma(self: Compiler, pragma: Pragma, node: ASTnode, name: Name)
|
proc handlePurePragma(self: Compiler, pragma: Pragma, name: Name)
|
||||||
proc dispatchPragmas(self: Compiler, node: ASTnode, name: Name)
|
proc dispatchPragmas(self: Compiler, name: Name)
|
||||||
proc funDecl(self: Compiler, node: FunDecl, name: Name)
|
proc funDecl(self: Compiler, node: FunDecl, name: Name)
|
||||||
proc typeDecl(self: Compiler, node: TypeDecl, name: Name)
|
proc typeDecl(self: Compiler, node: TypeDecl, name: Name)
|
||||||
proc compileModule(self: Compiler, moduleName: string)
|
proc compileModule(self: Compiler, moduleName: string)
|
||||||
|
@ -260,7 +260,7 @@ proc newCompiler*(replMode: bool = false): Compiler =
|
||||||
result.currentFunction = nil
|
result.currentFunction = nil
|
||||||
result.replMode = replMode
|
result.replMode = replMode
|
||||||
result.currentModule = ""
|
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["magic"] = handleMagicPragma
|
||||||
result.compilerProcs["pure"] = handlePurePragma
|
result.compilerProcs["pure"] = handlePurePragma
|
||||||
result.source = ""
|
result.source = ""
|
||||||
|
@ -491,10 +491,12 @@ proc resolve(self: Compiler, name: string): Name =
|
||||||
## Traverses all existing namespaces and returns
|
## Traverses all existing namespaces and returns
|
||||||
## the first object with the given name. Returns
|
## the first object with the given name. Returns
|
||||||
## nil when the name can't be found. Note that
|
## nil when the name can't be found. Note that
|
||||||
## when a declaration is first resolved, it is
|
## when a type or function declaration is first
|
||||||
## also compiled on-the-fly
|
## resolved, it is also compiled on-the-fly
|
||||||
for obj in reversed(self.names):
|
for obj in reversed(self.names):
|
||||||
if obj.ident.token.lexeme == name:
|
if obj.ident.token.lexeme == name:
|
||||||
|
if obj.kind == NameKind.Argument and obj.belongsTo != self.currentFunction:
|
||||||
|
continue
|
||||||
if obj.owner != self.currentModule:
|
if obj.owner != self.currentModule:
|
||||||
# We don't own this name, but we
|
# We don't own this name, but we
|
||||||
# may still have access to it
|
# 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
|
# 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
|
# not in). Ignore it, as it won't exist at runtime
|
||||||
continue
|
continue
|
||||||
elif not variable.belongsTo.isNil() and variable.belongsTo.valueType.isBuiltinFunction:
|
elif not variable.belongsTo.isNil():
|
||||||
# Builtin functions don't exist at runtime either, so variables belonging to them
|
if variable.belongsTo.valueType.isBuiltinFunction:
|
||||||
# are not present in the stack
|
# Builtin functions don't exist at runtime either, so variables belonging to them
|
||||||
continue
|
# are not present in the stack
|
||||||
elif not variable.valueType.isNil() and variable.valueType.kind == Generic:
|
continue
|
||||||
# Generics are also a purely compile-time construct and are therefore
|
elif variable.valueType.kind == Generic:
|
||||||
# ignored as far as stack positioning goes
|
# Generics are also a purely compile-time construct and are therefore
|
||||||
continue
|
# 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:
|
elif variable.owner != self.currentModule:
|
||||||
# We don't own this variable, so we check
|
# We don't own this variable, so we check
|
||||||
# if the owner exported it to us. If not,
|
# if the owner exported it to us. If not,
|
||||||
# we skip it and pretend it doesn't exist
|
# we skip it and pretend it doesn't exist
|
||||||
if variable.isPrivate or not variable.exported:
|
if variable.isPrivate or not variable.exported:
|
||||||
continue
|
continue
|
||||||
elif name == variable:
|
if name == variable:
|
||||||
# After all of these checks, we can
|
# After all of these checks, we can
|
||||||
# finally check whether the two names
|
# finally check whether the two names
|
||||||
# match (note: this also includes scope
|
# 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] =
|
proc findByName(self: Compiler, name: string): seq[Name] =
|
||||||
## Looks for objects that have been already declared
|
## 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):
|
for obj in reversed(self.names):
|
||||||
if obj.ident.token.lexeme == name:
|
if obj.ident.token.lexeme == name:
|
||||||
if obj.owner != self.currentModule:
|
if obj.owner != self.currentModule:
|
||||||
if obj.isPrivate or not obj.exported:
|
if obj.isPrivate or not obj.exported:
|
||||||
continue
|
continue
|
||||||
result.add(obj)
|
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] =
|
proc findByModule(self: Compiler, name: string): seq[Name] =
|
||||||
## Looks for objects that have been already declared AS
|
## Looks for objects that have been already declared AS
|
||||||
## public within the given module. Returns all objects that apply
|
## public within the given module. Returns all objects that apply
|
||||||
for obj in reversed(self.names):
|
for obj in reversed(self.names):
|
||||||
if obj.owner == name:
|
if not obj.isPrivate and obj.owner == name:
|
||||||
result.add(obj)
|
result.add(obj)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1134,8 +1158,6 @@ proc endScope(self: Compiler) =
|
||||||
return
|
return
|
||||||
for name in self.names:
|
for name in self.names:
|
||||||
if name.depth > self.depth:
|
if name.depth > self.depth:
|
||||||
if not name.belongsTo.isNil() and not name.belongsTo.resolved:
|
|
||||||
continue
|
|
||||||
names.add(name)
|
names.add(name)
|
||||||
#[if not name.resolved:
|
#[if not name.resolved:
|
||||||
# TODO: Emit a warning?
|
# TODO: Emit a warning?
|
||||||
|
@ -1148,7 +1170,7 @@ proc endScope(self: Compiler) =
|
||||||
if name.kind == NameKind.Var:
|
if name.kind == NameKind.Var:
|
||||||
inc(popCount)
|
inc(popCount)
|
||||||
elif name.kind == NameKind.Argument:
|
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
|
# We don't pop arguments to builtin functions because those don't
|
||||||
# actually have scopes: their arguments are temporaries on the stack
|
# actually have scopes: their arguments are temporaries on the stack
|
||||||
inc(popCount)
|
inc(popCount)
|
||||||
|
@ -1202,7 +1224,6 @@ proc endScope(self: Compiler) =
|
||||||
inc(idx)
|
inc(idx)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc unpackGenerics(self: Compiler, condition: Expression, list: var seq[tuple[match: bool, kind: Type]], accept: bool = true) =
|
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
|
## Recursively unpacks a type constraint in a generic type
|
||||||
case condition.kind:
|
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)
|
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.
|
## Statically declares a name into the current scope.
|
||||||
## "Declaring" a name only means updating our internal
|
## "Declaring" a name only means updating our internal
|
||||||
## list of identifiers so that further calls to resolve()
|
## 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
|
## declare a variable at runtime: the value is already
|
||||||
## on the stack
|
## on the stack
|
||||||
var declaredName: string = ""
|
var declaredName: string = ""
|
||||||
|
var n: Name
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of NodeKind.varDecl:
|
of NodeKind.varDecl:
|
||||||
var node = VarDecl(node)
|
var node = VarDecl(node)
|
||||||
# Creates a new Name entry so that self.identifier emits the proper stack offset
|
|
||||||
if self.names.high() > 16777215:
|
if self.names.high() > 16777215:
|
||||||
# If someone ever hits this limit in real-world scenarios, I swear I'll
|
# 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
|
# 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")
|
self.error("cannot declare more than 16777215 variables at a time")
|
||||||
declaredName = node.name.token.lexeme
|
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,
|
self.names.add(Name(depth: self.depth,
|
||||||
ident: node.name,
|
ident: node.name,
|
||||||
isPrivate: node.isPrivate,
|
isPrivate: node.isPrivate,
|
||||||
|
@ -1256,12 +1278,13 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name =
|
||||||
kind: NameKind.Var,
|
kind: NameKind.Var,
|
||||||
node: node
|
node: node
|
||||||
))
|
))
|
||||||
|
n = self.names[^1]
|
||||||
if mutable:
|
if mutable:
|
||||||
self.names[^1].valueType.mutable = true
|
self.names[^1].valueType.mutable = true
|
||||||
result = self.names[^1]
|
|
||||||
of NodeKind.funDecl:
|
of NodeKind.funDecl:
|
||||||
var node = FunDecl(node)
|
var node = FunDecl(node)
|
||||||
result = Name(depth: self.depth,
|
declaredName = node.name.token.lexeme
|
||||||
|
var fn = Name(depth: self.depth,
|
||||||
isPrivate: node.isPrivate,
|
isPrivate: node.isPrivate,
|
||||||
isConst: false,
|
isConst: false,
|
||||||
owner: self.currentModule,
|
owner: self.currentModule,
|
||||||
|
@ -1276,31 +1299,32 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name =
|
||||||
line: node.token.line,
|
line: node.token.line,
|
||||||
kind: NameKind.Function,
|
kind: NameKind.Function,
|
||||||
belongsTo: self.currentFunction)
|
belongsTo: self.currentFunction)
|
||||||
|
n = fn
|
||||||
# First we declare the function's generics, if it has any.
|
# First we declare the function's generics, if it has any.
|
||||||
# This is because the function's return type may in itself
|
# This is because the function's return type may in itself
|
||||||
# be a generic, so it needs to exist first
|
# be a generic, so it needs to exist first
|
||||||
var constraints: seq[tuple[match: bool, kind: Type]] = @[]
|
var constraints: seq[tuple[match: bool, kind: Type]] = @[]
|
||||||
for gen in node.generics:
|
for gen in node.generics:
|
||||||
self.unpackGenerics(gen.cond, constraints)
|
self.unpackGenerics(gen.cond, constraints)
|
||||||
self.names.add(Name(depth: result.depth + 1,
|
self.names.add(Name(depth: fn.depth + 1,
|
||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
valueType: Type(kind: Generic, name: gen.name.token.lexeme, mutable: false, cond: constraints),
|
valueType: Type(kind: Generic, name: gen.name.token.lexeme, mutable: false, cond: constraints),
|
||||||
codePos: 0,
|
codePos: 0,
|
||||||
isLet: false,
|
isLet: false,
|
||||||
line: result.node.token.line,
|
line: fn.node.token.line,
|
||||||
belongsTo: result,
|
belongsTo: fn,
|
||||||
ident: gen.name,
|
ident: gen.name,
|
||||||
owner: self.currentModule))
|
owner: self.currentModule))
|
||||||
constraints = @[]
|
constraints = @[]
|
||||||
if not node.returnType.isNil():
|
if not node.returnType.isNil():
|
||||||
result.valueType.returnType = self.inferOrError(node.returnType, allowGeneric=true)
|
fn.valueType.returnType = self.inferOrError(node.returnType, allowGeneric=true)
|
||||||
self.names.add(result)
|
self.names.add(fn)
|
||||||
# We now declare and typecheck the function's
|
# We now declare and typecheck the function's
|
||||||
# arguments
|
# arguments
|
||||||
for argument in FunDecl(result.node).arguments:
|
for argument in FunDecl(fn.node).arguments:
|
||||||
if self.names.high() > 16777215:
|
if self.names.high() > 16777215:
|
||||||
self.error("cannot declare more than 16777215 variables at a time")
|
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,
|
isPrivate: true,
|
||||||
owner: self.currentModule,
|
owner: self.currentModule,
|
||||||
isConst: false,
|
isConst: false,
|
||||||
|
@ -1309,12 +1333,12 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name =
|
||||||
codePos: 0,
|
codePos: 0,
|
||||||
isLet: false,
|
isLet: false,
|
||||||
line: argument.name.token.line,
|
line: argument.name.token.line,
|
||||||
belongsTo: result,
|
belongsTo: fn,
|
||||||
kind: NameKind.Argument
|
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:
|
if node.generics.len() > 0:
|
||||||
result.valueType.isGeneric = true
|
fn.valueType.isGeneric = true
|
||||||
of NodeKind.importStmt:
|
of NodeKind.importStmt:
|
||||||
var node = ImportStmt(node)
|
var node = ImportStmt(node)
|
||||||
var name = node.moduleName.token.lexeme.extractFilename().replace(".pn", "")
|
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,
|
kind: NameKind.Module,
|
||||||
isPrivate: false
|
isPrivate: false
|
||||||
))
|
))
|
||||||
result = self.names[^1]
|
n = self.names[^1]
|
||||||
else:
|
else:
|
||||||
discard # TODO: Types, enums
|
discard # TODO: Types, enums
|
||||||
|
self.dispatchPragmas(n)
|
||||||
for name in self.findByName(declaredName):
|
for name in self.findByName(declaredName):
|
||||||
if name == result:
|
if name == n:
|
||||||
continue
|
continue
|
||||||
elif (name.kind == NameKind.Var and name.depth == self.depth) or name.kind in [NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
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}")
|
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)
|
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
|
## Handles the "magic" pragma. Assumes the given name is already
|
||||||
## declared
|
## declared
|
||||||
if pragma.args.len() != 1:
|
if pragma.args.len() != 1:
|
||||||
self.error("'magic' pragma: wrong number of arguments")
|
self.error("'magic' pragma: wrong number of arguments")
|
||||||
elif pragma.args[0].kind != strExpr:
|
elif pragma.args[0].kind != strExpr:
|
||||||
self.error("'magic' pragma: wrong type of argument (constant string expected)")
|
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")
|
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.isBuiltinFunction = true
|
||||||
name.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2]
|
name.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2]
|
||||||
# The magic pragma ignores the function's body
|
# The magic pragma ignores the function's body
|
||||||
node.body = nil
|
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
|
## Handles the "pure" pragma
|
||||||
case node.kind:
|
case name.node.kind:
|
||||||
of NodeKind.funDecl:
|
of NodeKind.funDecl:
|
||||||
FunDecl(node).isPure = true
|
FunDecl(name.node).isPure = true
|
||||||
of lambdaExpr:
|
of lambdaExpr:
|
||||||
LambdaExpr(node).isPure = true
|
LambdaExpr(name.node).isPure = true
|
||||||
else:
|
else:
|
||||||
self.error("'pure' pragma is not valid in this context")
|
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
|
## Dispatches pragmas bound to objects
|
||||||
|
if name.node.isNil():
|
||||||
|
return
|
||||||
var pragmas: seq[Pragma] = @[]
|
var pragmas: seq[Pragma] = @[]
|
||||||
case node.kind:
|
case name.node.kind:
|
||||||
of NodeKind.funDecl, NodeKind.typeDecl, NodeKind.varDecl:
|
of NodeKind.funDecl, NodeKind.typeDecl, NodeKind.varDecl:
|
||||||
pragmas = Declaration(node).pragmas
|
pragmas = Declaration(name.node).pragmas
|
||||||
of lambdaExpr:
|
of lambdaExpr:
|
||||||
pragmas = LambdaExpr(node).pragmas
|
pragmas = LambdaExpr(name.node).pragmas
|
||||||
else:
|
else:
|
||||||
discard # Unreachable
|
discard # Unreachable
|
||||||
for pragma in pragmas:
|
for pragma in pragmas:
|
||||||
if pragma.name.token.lexeme notin self.compilerProcs:
|
if pragma.name.token.lexeme notin self.compilerProcs:
|
||||||
self.error(&"unknown pragma '{pragma.name.token.lexeme}'")
|
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) =
|
proc patchReturnAddress(self: Compiler, pos: int) =
|
||||||
|
@ -1629,7 +1656,7 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
||||||
else:
|
else:
|
||||||
# Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
# Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
||||||
self.emitByte(LoadVar, node.token.line)
|
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)
|
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 mapping: TableRef[string, Type] = newTable[string, Type]()
|
||||||
var kind: Type
|
var kind: Type
|
||||||
result = deepCopy(name)
|
result = deepCopy(name)
|
||||||
|
result.valueType.isGeneric = false
|
||||||
case name.kind:
|
case name.kind:
|
||||||
of NameKind.Function:
|
of NameKind.Function:
|
||||||
# This first loop checks if a user tries to reassign a generic's
|
# 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:
|
if result.valueType.returnType.kind == Generic:
|
||||||
result.valueType.returnType = mapping[result.valueType.returnType.name]
|
result.valueType.returnType = mapping[result.valueType.returnType.name]
|
||||||
|
# self.funDecl(FunDecl(result.node), result)
|
||||||
else:
|
else:
|
||||||
discard # TODO: Custom user-defined types
|
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
|
# very last moment to compile it, once
|
||||||
# that info is available to us
|
# that info is available to us
|
||||||
result = self.specialize(result, argExpr)
|
result = self.specialize(result, argExpr)
|
||||||
|
self.funDecl(FunDecl(result.node), result)
|
||||||
# Now we call it
|
# Now we call it
|
||||||
self.generateCall(result, argExpr, node.token.line)
|
self.generateCall(result, argExpr, node.token.line)
|
||||||
of NodeKind.callExpr:
|
of NodeKind.callExpr:
|
||||||
|
@ -1990,7 +2020,7 @@ proc importStmt(self: Compiler, node: ImportStmt) =
|
||||||
let filename = splitPath(node.moduleName.token.lexeme).tail
|
let filename = splitPath(node.moduleName.token.lexeme).tail
|
||||||
try:
|
try:
|
||||||
self.compileModule(node.moduleName.token.lexeme)
|
self.compileModule(node.moduleName.token.lexeme)
|
||||||
discard self.declareName(node)
|
self.declareName(node)
|
||||||
except IOError:
|
except IOError:
|
||||||
self.error(&"""could not import '{filename}': {getCurrentExceptionMsg()}""")
|
self.error(&"""could not import '{filename}': {getCurrentExceptionMsg()}""")
|
||||||
except OSError:
|
except OSError:
|
||||||
|
@ -2148,6 +2178,8 @@ proc funDecl(self: Compiler, node: FunDecl, name: Name) =
|
||||||
## Compiles function declarations
|
## Compiles function declarations
|
||||||
if node.token.kind == Operator and node.name.token.lexeme in [".", ]:
|
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)
|
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 node = node
|
||||||
var jmp: int
|
var jmp: int
|
||||||
# We store the current function
|
# We store the current function
|
||||||
|
@ -2177,6 +2209,7 @@ proc funDecl(self: Compiler, node: FunDecl, name: Name) =
|
||||||
else:
|
else:
|
||||||
self.chunk.cfi.add(0.toDouble())
|
self.chunk.cfi.add(0.toDouble())
|
||||||
if BlockStmt(node.body).code.len() == 0:
|
if BlockStmt(node.body).code.len() == 0:
|
||||||
|
raise newException(IndexDefect, "")
|
||||||
self.error("cannot declare function with empty body")
|
self.error("cannot declare function with empty body")
|
||||||
# Since the deferred array is a linear
|
# Since the deferred array is a linear
|
||||||
# sequence of instructions and we want
|
# sequence of instructions and we want
|
||||||
|
@ -2241,9 +2274,8 @@ proc declaration(self: Compiler, node: Declaration) =
|
||||||
## the first time
|
## the first time
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of NodeKind.varDecl, NodeKind.funDecl, NodeKind.typeDecl:
|
of NodeKind.varDecl, NodeKind.funDecl, NodeKind.typeDecl:
|
||||||
self.dispatchPragmas(node, self.declareName(node))
|
self.declareName(node)
|
||||||
if node.kind == NodeKind.varDecl:
|
if node.kind == NodeKind.varDecl:
|
||||||
self.names[^1].resolved = true
|
|
||||||
# We compile this immediately because we
|
# We compile this immediately because we
|
||||||
# need to keep the stack in the right state
|
# need to keep the stack in the right state
|
||||||
# at runtime
|
# at runtime
|
||||||
|
@ -2285,7 +2317,10 @@ proc compileModule(self: Compiler, moduleName: string) =
|
||||||
## using the compiler's internal parser and lexer objects
|
## using the compiler's internal parser and lexer objects
|
||||||
var path = ""
|
var path = ""
|
||||||
for i, searchPath in moduleLookupPaths:
|
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):
|
if fileExists(path):
|
||||||
break
|
break
|
||||||
elif i == searchPath.high():
|
elif i == searchPath.high():
|
||||||
|
|
|
@ -730,7 +730,10 @@ proc importStmt(self: Parser, fromStmt: bool = false): Statement =
|
||||||
lexer.fillSymbolTable()
|
lexer.fillSymbolTable()
|
||||||
var path = ""
|
var path = ""
|
||||||
for i, searchPath in moduleLookupPaths:
|
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):
|
if fileExists(path):
|
||||||
break
|
break
|
||||||
elif i == searchPath.high():
|
elif i == searchPath.high():
|
||||||
|
|
18
src/main.nim
18
src/main.nim
|
@ -67,7 +67,7 @@ proc repl =
|
||||||
tokenizer.fillSymbolTable()
|
tokenizer.fillSymbolTable()
|
||||||
editor.bindEvent(jeQuit):
|
editor.bindEvent(jeQuit):
|
||||||
stdout.styledWriteLine(fgGreen, "Goodbye!")
|
stdout.styledWriteLine(fgGreen, "Goodbye!")
|
||||||
editor.prompt = ""
|
editor.prompt = "=> "
|
||||||
keep = false
|
keep = false
|
||||||
input = ""
|
input = ""
|
||||||
editor.bindKey("ctrl+a"):
|
editor.bindKey("ctrl+a"):
|
||||||
|
@ -76,10 +76,6 @@ proc repl =
|
||||||
editor.content.`end`()
|
editor.content.`end`()
|
||||||
while keep:
|
while keep:
|
||||||
try:
|
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()
|
input = editor.read()
|
||||||
if input.len() == 0:
|
if input.len() == 0:
|
||||||
continue
|
continue
|
||||||
|
@ -153,7 +149,7 @@ proc repl =
|
||||||
fgYellow, &"'{exc.file.extractFilename()}'", fgRed, ", line ", fgYellow, $exc.line, fgRed, " at ", fgYellow, &"'{exc.lexeme.escape()}'",
|
fgYellow, &"'{exc.file.extractFilename()}'", fgRed, ", line ", fgYellow, $exc.line, fgRed, " at ", fgYellow, &"'{exc.lexeme.escape()}'",
|
||||||
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
||||||
styledEcho fgBlue, "Source line: " , fgDefault, line
|
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:
|
except ParseError:
|
||||||
input = ""
|
input = ""
|
||||||
let exc = ParseError(getCurrentException())
|
let exc = ParseError(getCurrentException())
|
||||||
|
@ -169,7 +165,7 @@ proc repl =
|
||||||
fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'",
|
fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'",
|
||||||
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
||||||
styledEcho fgBlue, "Source line: " , fgDefault, line
|
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:
|
except CompileError:
|
||||||
let exc = CompileError(getCurrentException())
|
let exc = CompileError(getCurrentException())
|
||||||
let lexeme = exc.node.token.lexeme
|
let lexeme = exc.node.token.lexeme
|
||||||
|
@ -184,7 +180,7 @@ proc repl =
|
||||||
fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'",
|
fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'",
|
||||||
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
||||||
styledEcho fgBlue, "Source line: " , fgDefault, line
|
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:
|
except SerializationError:
|
||||||
let exc = SerializationError(getCurrentException())
|
let exc = SerializationError(getCurrentException())
|
||||||
stderr.styledWriteLine(fgRed, "A fatal error occurred while (de-)serializing", fgYellow, &"'{exc.file}'", fgGreen, ": ", getCurrentExceptionMsg())
|
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()}'",
|
fgYellow, &"'{exc.file.extractFilename()}'", fgRed, ", line ", fgYellow, $exc.line, fgRed, " at ", fgYellow, &"'{exc.lexeme.escape()}'",
|
||||||
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
||||||
styledEcho fgBlue, "Source line: " , fgDefault, line
|
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:
|
except ParseError:
|
||||||
let exc = ParseError(getCurrentException())
|
let exc = ParseError(getCurrentException())
|
||||||
let lexeme = exc.token.lexeme
|
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}'",
|
fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'",
|
||||||
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
||||||
styledEcho fgBlue, "Source line: " , fgDefault, line
|
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:
|
except CompileError:
|
||||||
let exc = CompileError(getCurrentException())
|
let exc = CompileError(getCurrentException())
|
||||||
let lexeme = exc.node.token.lexeme
|
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}'",
|
fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'",
|
||||||
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
||||||
styledEcho fgBlue, "Source line: " , fgDefault, line
|
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:
|
except SerializationError:
|
||||||
let exc = SerializationError(getCurrentException())
|
let exc = SerializationError(getCurrentException())
|
||||||
stderr.styledWriteLine(fgRed, "A fatal error occurred while (de-)serializing", fgYellow, &"'{exc.file}'", fgGreen, ": ", getCurrentExceptionMsg())
|
stderr.styledWriteLine(fgRed, "A fatal error occurred while (de-)serializing", fgYellow, &"'{exc.file}'", fgGreen, ": ", getCurrentExceptionMsg())
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -7,4 +7,4 @@ operator `+`(a: int): int {
|
||||||
|
|
||||||
|
|
||||||
+1; # Works: defined for int64
|
+1; # Works: defined for int64
|
||||||
+1'i32; # Will not work
|
# +1'i32; # Will not work
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# Tests generic functions
|
||||||
import std;
|
import std;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Tests var parameters
|
# Tests var parameters. TODO: They don't actually exist yet, they're just checked statically
|
||||||
|
|
||||||
import std;
|
import std;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue