Work on keyword arguments. REPL has been disabled (it's broken). Fixed various issues with casts and typevars
This commit is contained in:
parent
86231291c4
commit
71f44313da
|
@ -690,7 +690,7 @@ when debugVM: # So nim shuts up
|
||||||
styledEcho(fgRed, "Unknown command ", fgYellow, &"'{command}'")
|
styledEcho(fgRed, "Unknown command ", fgYellow, &"'{command}'")
|
||||||
|
|
||||||
|
|
||||||
proc dispatch*(self: var PeonVM) =
|
proc dispatch*(self: var PeonVM) {.inline.} =
|
||||||
## Main bytecode dispatch loop
|
## Main bytecode dispatch loop
|
||||||
var instruction {.register.}: OpCode
|
var instruction {.register.}: OpCode
|
||||||
while true:
|
while true:
|
||||||
|
@ -764,10 +764,6 @@ proc dispatch*(self: var PeonVM) =
|
||||||
# not needed there anymore
|
# not needed there anymore
|
||||||
discard self.pop()
|
discard self.pop()
|
||||||
discard self.pop()
|
discard self.pop()
|
||||||
of ReplExit:
|
|
||||||
# Preserves the VM's state for the next
|
|
||||||
# execution. Used in the REPL
|
|
||||||
return
|
|
||||||
of Return:
|
of Return:
|
||||||
# Returns from a function.
|
# Returns from a function.
|
||||||
# Every peon program is wrapped
|
# Every peon program is wrapped
|
||||||
|
@ -1079,14 +1075,16 @@ proc run*(self: var PeonVM, chunk: Chunk, breakpoints: seq[uint64] = @[], repl:
|
||||||
|
|
||||||
proc resume*(self: var PeonVM, chunk: Chunk) =
|
proc resume*(self: var PeonVM, chunk: Chunk) =
|
||||||
## Resumes execution of the given chunk (which
|
## Resumes execution of the given chunk (which
|
||||||
## may have changed since the last call to run()).
|
## may have changed since the last call to run()). No other
|
||||||
## No other state mutation occurs and all stacks as
|
## state mutation occurs and all stacks as well as other
|
||||||
## well as other metadata are left intact. This should
|
## metadata are left intact. This should not be used directly
|
||||||
## not be used directly unless you know what you're
|
## unless you know what you're doing, as incremental compilation
|
||||||
## doing, as incremental compilation support is very
|
## support is very experimental and highly unstable
|
||||||
## experimental and highly unstable
|
|
||||||
self.chunk = chunk
|
|
||||||
try:
|
try:
|
||||||
|
self.chunk = chunk
|
||||||
|
when debugVM:
|
||||||
|
if self.breakpoints == @[0'u64]:
|
||||||
|
self.debugNext = true
|
||||||
self.dispatch()
|
self.dispatch()
|
||||||
except NilAccessDefect:
|
except NilAccessDefect:
|
||||||
stderr.writeLine("Memory Access Violation: SIGSEGV")
|
stderr.writeLine("Memory Access Violation: SIGSEGV")
|
||||||
|
|
|
@ -59,6 +59,9 @@ type
|
||||||
Type* = ref object
|
Type* = ref object
|
||||||
## A wrapper around
|
## A wrapper around
|
||||||
## compile-time types
|
## compile-time types
|
||||||
|
|
||||||
|
# Is this type a builtin?
|
||||||
|
isBuiltin*: bool
|
||||||
case kind*: TypeKind:
|
case kind*: TypeKind:
|
||||||
of Function:
|
of Function:
|
||||||
isLambda*: bool
|
isLambda*: bool
|
||||||
|
@ -86,6 +89,9 @@ type
|
||||||
name*: string
|
name*: string
|
||||||
of Union:
|
of Union:
|
||||||
types*: seq[tuple[match: bool, kind: Type]]
|
types*: seq[tuple[match: bool, kind: Type]]
|
||||||
|
of Typevar:
|
||||||
|
# What type do we represent?
|
||||||
|
wrapped*: Type
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
@ -141,8 +147,6 @@ type
|
||||||
# Has the compiler generated this name internally or
|
# Has the compiler generated this name internally or
|
||||||
# does it come from user code?
|
# does it come from user code?
|
||||||
isReal*: bool
|
isReal*: bool
|
||||||
# Is this name a builtin?
|
|
||||||
isBuiltin*: bool
|
|
||||||
|
|
||||||
## BACKEND-SPECIFIC FIELDS
|
## BACKEND-SPECIFIC FIELDS
|
||||||
|
|
||||||
|
@ -234,6 +238,7 @@ proc getSource*(self: Compiler): string {.inline.} = self.source
|
||||||
## implemented in the same module). They are methods because we need to dispatch to their actual specific
|
## implemented in the same module). They are methods because we need to dispatch to their actual specific
|
||||||
## implementations inside each target module, so we need the runtime type of the compiler object to be
|
## implementations inside each target module, so we need the runtime type of the compiler object to be
|
||||||
## taken into account
|
## taken into account
|
||||||
|
method makeConcrete(self: Compiler, node: GenericExpr, compile: bool = true): Type {.base.} = nil
|
||||||
method expression*(self: Compiler, node: Expression, compile: bool = true): Type {.discardable, base.} = nil
|
method expression*(self: Compiler, node: Expression, compile: bool = true): Type {.discardable, base.} = nil
|
||||||
method identifier*(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool = true, strict: bool = true): Type {.discardable, base.} = nil
|
method identifier*(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool = true, strict: bool = true): Type {.discardable, base.} = nil
|
||||||
method call*(self: Compiler, node: CallExpr, compile: bool = true): Type {.discardable, base.} = nil
|
method call*(self: Compiler, node: CallExpr, compile: bool = true): Type {.discardable, base.} = nil
|
||||||
|
@ -258,7 +263,7 @@ method dispatchDelayedPragmas(self: Compiler, name: Name) {.base.} = discard
|
||||||
## Utility functions
|
## Utility functions
|
||||||
|
|
||||||
proc `$`*(self: Name): string = $(self[])
|
proc `$`*(self: Name): string = $(self[])
|
||||||
proc `$`(self: Type): string = $(self[])
|
proc `$`*(self: Type): string = $(self[])
|
||||||
proc hash(self: Name): Hash = self.ident.token.lexeme.hash()
|
proc hash(self: Name): Hash = self.ident.token.lexeme.hash()
|
||||||
|
|
||||||
|
|
||||||
|
@ -421,20 +426,22 @@ method compare*(self: Compiler, a, b: Type): bool =
|
||||||
# code in any way. It's used mostly for matching
|
# code in any way. It's used mostly for matching
|
||||||
# function return types (at least until we don't
|
# function return types (at least until we don't
|
||||||
# have return type inference) and it matches any
|
# have return type inference) and it matches any
|
||||||
# type, including nil
|
# type, including nil (nim's nil, not our nil)
|
||||||
if a.isNil():
|
if a.isNil():
|
||||||
return b.isNil() or b.kind == All
|
return b.isNil() or b.kind == All
|
||||||
elif b.isNil():
|
elif b.isNil():
|
||||||
return a.isNil() or a.kind == All
|
return a.isNil() or a.kind == All
|
||||||
elif a.kind == All or b.kind == All:
|
elif a.kind == All or b.kind == All:
|
||||||
return true
|
return true
|
||||||
elif a.kind == b.kind:
|
if a.kind == b.kind:
|
||||||
# Here we compare types with the same kind discriminant
|
# Here we compare types with the same kind discriminant
|
||||||
case a.kind:
|
case a.kind:
|
||||||
of Int8, UInt8, Int16, UInt16, Int32,
|
of Int8, UInt8, Int16, UInt16, Int32,
|
||||||
UInt32, Int64, UInt64, Float32, Float64,
|
UInt32, Int64, UInt64, Float32, Float64,
|
||||||
Char, Byte, String, Nil, TypeKind.Nan, Bool, TypeKind.Inf, Any:
|
Char, Byte, String, Nil, TypeKind.Nan, Bool, TypeKind.Inf, Any:
|
||||||
return true
|
return true
|
||||||
|
of Typevar:
|
||||||
|
return self.compare(a.wrapped, b.wrapped)
|
||||||
of Union:
|
of Union:
|
||||||
return self.compareUnions(a.types, b.types)
|
return self.compareUnions(a.types, b.types)
|
||||||
of Generic:
|
of Generic:
|
||||||
|
@ -455,36 +462,22 @@ method compare*(self: Compiler, a, b: Type): bool =
|
||||||
return false
|
return false
|
||||||
var i = 0
|
var i = 0
|
||||||
for (argA, argB) in zip(a.args, b.args):
|
for (argA, argB) in zip(a.args, b.args):
|
||||||
# When we compare functions with forward
|
if argA.name == "":
|
||||||
# declarations, or forward declarations
|
continue
|
||||||
# between each other, we need to be more
|
if argB.name == "":
|
||||||
# strict (as in: check argument names and
|
continue
|
||||||
# their default values, any pragma associated
|
if argA.name != argB.name:
|
||||||
# with the function, and whether they are pure)
|
return false
|
||||||
if a.forwarded:
|
|
||||||
if b.forwarded:
|
|
||||||
if argA.name != argB.name:
|
|
||||||
return false
|
|
||||||
else:
|
|
||||||
if argB.name == "":
|
|
||||||
# An empty argument name means
|
|
||||||
# we crafted this type object
|
|
||||||
# manually, so we don't need
|
|
||||||
# to match the argument name
|
|
||||||
continue
|
|
||||||
if argA.name != argB.name:
|
|
||||||
return false
|
|
||||||
elif b.forwarded:
|
|
||||||
if a.forwarded:
|
|
||||||
if argA.name != argB.name:
|
|
||||||
return false
|
|
||||||
else:
|
|
||||||
if argA.name == "":
|
|
||||||
continue
|
|
||||||
if argA.name != argB.name:
|
|
||||||
return false
|
|
||||||
if not self.compare(argA.kind, argB.kind):
|
if not self.compare(argA.kind, argB.kind):
|
||||||
return false
|
return false
|
||||||
|
if a.forwarded or b.forwarded:
|
||||||
|
# We need to be more strict when checking forward
|
||||||
|
# declarations
|
||||||
|
if b.fun.pragmas.len() != a.fun.pragmas.len():
|
||||||
|
return false
|
||||||
|
for (pragA, pragB) in zip(a.fun.pragmas, b.fun.pragmas):
|
||||||
|
if pragA != pragB:
|
||||||
|
return false
|
||||||
return true
|
return true
|
||||||
else:
|
else:
|
||||||
discard # TODO: Custom types, enums
|
discard # TODO: Custom types, enums
|
||||||
|
@ -615,6 +608,8 @@ method infer*(self: Compiler, node: Expression): Type =
|
||||||
if node.isNil():
|
if node.isNil():
|
||||||
return nil
|
return nil
|
||||||
case node.kind:
|
case node.kind:
|
||||||
|
of NodeKind.genericExpr:
|
||||||
|
result = self.makeConcrete(GenericExpr(node), compile=false)
|
||||||
of NodeKind.identExpr:
|
of NodeKind.identExpr:
|
||||||
result = self.identifier(IdentExpr(node), compile=false, strict=false)
|
result = self.identifier(IdentExpr(node), compile=false, strict=false)
|
||||||
of NodeKind.unaryExpr:
|
of NodeKind.unaryExpr:
|
||||||
|
@ -659,16 +654,30 @@ method stringify*(self: Compiler, typ: Type): string =
|
||||||
of Int8, UInt8, Int16, UInt16, Int32,
|
of Int8, UInt8, Int16, UInt16, Int32,
|
||||||
UInt32, Int64, UInt64, Float32, Float64,
|
UInt32, Int64, UInt64, Float32, Float64,
|
||||||
Char, Byte, String, Nil, TypeKind.Nan, Bool,
|
Char, Byte, String, Nil, TypeKind.Nan, Bool,
|
||||||
TypeKind.Inf, Auto:
|
TypeKind.Inf, Auto, Any:
|
||||||
result &= ($typ.kind).toLowerAscii()
|
result &= ($typ.kind).toLowerAscii()
|
||||||
|
of Typevar:
|
||||||
|
result = self.stringify(typ.wrapped)
|
||||||
of Pointer:
|
of Pointer:
|
||||||
result &= &"ptr {self.stringify(typ.value)}"
|
result &= &"ptr {self.stringify(typ.value)}"
|
||||||
of Reference:
|
of Reference:
|
||||||
result &= &"ref {self.stringify(typ.value)}"
|
result &= &"ref {self.stringify(typ.value)}"
|
||||||
of Function:
|
of Function:
|
||||||
result &= "fn ("
|
result &= "fn "
|
||||||
|
if typ.fun.generics.len() > 0:
|
||||||
|
result &= "["
|
||||||
|
for i, gen in typ.fun.generics:
|
||||||
|
result &= &"{gen.name.token.lexeme}: {self.stringify(self.inferOrError(gen.cond))}"
|
||||||
|
if i < typ.fun.generics.len() - 1:
|
||||||
|
result &= ", "
|
||||||
|
result &= "]"
|
||||||
|
result &= "("
|
||||||
for i, (argName, argType, argDefault) in typ.args:
|
for i, (argName, argType, argDefault) in typ.args:
|
||||||
result &= &"{argName}: {self.stringify(argType)}"
|
result &= &"{argName}: "
|
||||||
|
if argType.kind == Generic:
|
||||||
|
result &= argType.name
|
||||||
|
else:
|
||||||
|
result &= self.stringify(argType)
|
||||||
if not argDefault.isNil():
|
if not argDefault.isNil():
|
||||||
result &= &" = {argDefault}"
|
result &= &" = {argDefault}"
|
||||||
if i < typ.args.len() - 1:
|
if i < typ.args.len() - 1:
|
||||||
|
@ -690,8 +699,6 @@ method stringify*(self: Compiler, typ: Type): string =
|
||||||
result &= ", "
|
result &= ", "
|
||||||
else:
|
else:
|
||||||
result &= "}"
|
result &= "}"
|
||||||
of Any:
|
|
||||||
return "any"
|
|
||||||
of Union:
|
of Union:
|
||||||
for i, condition in typ.types:
|
for i, condition in typ.types:
|
||||||
if i > 0:
|
if i > 0:
|
||||||
|
@ -725,9 +732,10 @@ method findInModule*(self: Compiler, name: string, module: Name): 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 with the given name.
|
## public within the given module with the given name.
|
||||||
## Returns all objects that apply. If the name is an
|
## Returns all objects that apply. If the name is an
|
||||||
## empty string, returns all objects within the given
|
## empty string, returns all public names within the
|
||||||
## module, regardless of whether they are exported to
|
## given module, regardless of whether they are exported
|
||||||
## the current one or not
|
## to the current one or not (this is used at import time
|
||||||
|
## and for the export statement)
|
||||||
if name == "":
|
if name == "":
|
||||||
for obj in reversed(self.names):
|
for obj in reversed(self.names):
|
||||||
if obj.owner.isNil():
|
if obj.owner.isNil():
|
||||||
|
@ -765,13 +773,14 @@ proc check*(self: Compiler, term: Expression, kind: Type) {.inline.} =
|
||||||
let k = self.inferOrError(term)
|
let k = self.inferOrError(term)
|
||||||
if not self.compare(k, kind):
|
if not self.compare(k, kind):
|
||||||
self.error(&"expecting value of type {self.stringify(kind)}, got {self.stringify(k)}", term)
|
self.error(&"expecting value of type {self.stringify(kind)}, got {self.stringify(k)}", term)
|
||||||
elif k.kind == Any and kind.kind != Any:
|
if k.kind == Any and kind.kind notin [Any, Generic]:
|
||||||
self.error(&"any is not a valid type in this context")
|
self.error(&"any is not a valid type in this context")
|
||||||
|
|
||||||
|
|
||||||
proc isAny*(typ: Type): bool =
|
proc isAny*(typ: Type): bool =
|
||||||
## Returns true if the given type is
|
## Returns true if the given type is
|
||||||
## of (or contains) the any type
|
## of (or contains) the any type. Not
|
||||||
|
## applicable to typevars
|
||||||
case typ.kind:
|
case typ.kind:
|
||||||
of Any:
|
of Any:
|
||||||
return true
|
return true
|
||||||
|
@ -791,10 +800,7 @@ method match*(self: Compiler, name: string, kind: Type, node: ASTNode = nil, all
|
||||||
## Tries to find a matching function implementation
|
## Tries to find a matching function implementation
|
||||||
## compatible with the given type and returns its
|
## compatible with the given type and returns its
|
||||||
## name object
|
## name object
|
||||||
var impl: seq[Name] = @[]
|
var impl: seq[Name] = self.findByType(name, kind)
|
||||||
for obj in self.findByName(name):
|
|
||||||
if self.compare(kind, obj.valueType):
|
|
||||||
impl.add(obj)
|
|
||||||
if impl.len() == 0:
|
if impl.len() == 0:
|
||||||
let names = self.findByName(name)
|
let names = self.findByName(name)
|
||||||
var msg = &"failed to find a suitable implementation for '{name}'"
|
var msg = &"failed to find a suitable implementation for '{name}'"
|
||||||
|
@ -812,6 +818,8 @@ method match*(self: Compiler, name: string, kind: Type, node: ASTNode = nil, all
|
||||||
msg &= &": wrong number of arguments (expected {name.valueType.args.len()}, got {kind.args.len()})"
|
msg &= &": wrong number of arguments (expected {name.valueType.args.len()}, got {kind.args.len()})"
|
||||||
else:
|
else:
|
||||||
for i, arg in kind.args:
|
for i, arg in kind.args:
|
||||||
|
if arg.name != "" and name.valueType.args[i].name != "" and arg.name != name.valueType.args[i].name:
|
||||||
|
msg &= &": unexpected argument '{arg.name}' at position {i + 1}"
|
||||||
if not self.compare(arg.kind, name.valueType.args[i].kind):
|
if not self.compare(arg.kind, name.valueType.args[i].kind):
|
||||||
msg &= &": first mismatch at position {i + 1}: (expected {self.stringify(name.valueType.args[i].kind)}, got {self.stringify(arg.kind)})"
|
msg &= &": first mismatch at position {i + 1}: (expected {self.stringify(name.valueType.args[i].kind)}, got {self.stringify(arg.kind)})"
|
||||||
break
|
break
|
||||||
|
@ -838,6 +846,7 @@ method match*(self: Compiler, name: string, kind: Type, node: ASTNode = nil, all
|
||||||
if impl[0].valueType.forwarded and not allowFwd:
|
if impl[0].valueType.forwarded and not allowFwd:
|
||||||
self.error(&"expecting an implementation for function '{impl[0].ident.token.lexeme}' declared in module '{impl[0].owner.ident.token.lexeme}' at line {impl[0].ident.token.line} of type '{self.stringify(impl[0].valueType)}'")
|
self.error(&"expecting an implementation for function '{impl[0].ident.token.lexeme}' declared in module '{impl[0].owner.ident.token.lexeme}' at line {impl[0].ident.token.line} of type '{self.stringify(impl[0].valueType)}'")
|
||||||
result = impl[0]
|
result = impl[0]
|
||||||
|
result.resolved = true
|
||||||
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)
|
||||||
|
@ -849,56 +858,36 @@ proc beginScope*(self: Compiler) =
|
||||||
inc(self.depth)
|
inc(self.depth)
|
||||||
|
|
||||||
|
|
||||||
proc unpackGenerics*(self: Compiler, condition: Expression, list: var seq[tuple[match: bool, kind: Type]], accept: bool = true) =
|
proc unpackTypes*(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:
|
||||||
of identExpr:
|
of identExpr:
|
||||||
list.add((accept, self.inferOrError(condition)))
|
var typ = self.inferOrError(condition)
|
||||||
if list[^1].kind.kind == Auto:
|
if typ.kind != Typevar:
|
||||||
|
self.error(&"expecting a type name, got value of type {self.stringify(typ)} instead", condition)
|
||||||
|
typ = typ.wrapped
|
||||||
|
if typ.kind == Auto:
|
||||||
self.error("automatic types cannot be used within generics", condition)
|
self.error("automatic types cannot be used within generics", condition)
|
||||||
|
list.add((accept, typ))
|
||||||
of binaryExpr:
|
of binaryExpr:
|
||||||
let condition = BinaryExpr(condition)
|
let condition = BinaryExpr(condition)
|
||||||
case condition.operator.lexeme:
|
case condition.operator.lexeme:
|
||||||
of "|":
|
of "|":
|
||||||
self.unpackGenerics(condition.a, list)
|
self.unpackTypes(condition.a, list)
|
||||||
self.unpackGenerics(condition.b, list)
|
self.unpackTypes(condition.b, list)
|
||||||
else:
|
else:
|
||||||
self.error("invalid type constraint in generic declaration", condition)
|
self.error("invalid type constraint in generic declaration", condition)
|
||||||
of unaryExpr:
|
of unaryExpr:
|
||||||
let condition = UnaryExpr(condition)
|
let condition = UnaryExpr(condition)
|
||||||
case condition.operator.lexeme:
|
case condition.operator.lexeme:
|
||||||
of "~":
|
of "~":
|
||||||
self.unpackGenerics(condition.a, list, accept=false)
|
self.unpackTypes(condition.a, list, accept=false)
|
||||||
else:
|
else:
|
||||||
self.error("invalid type constraint in generic declaration", condition)
|
self.error("invalid type constraint in generic declaration", condition)
|
||||||
else:
|
else:
|
||||||
self.error("invalid type constraint in generic declaration", condition)
|
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.} =
|
proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||||
## 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
|
||||||
|
@ -957,19 +946,6 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||||
fn.valueType.compiled = true
|
fn.valueType.compiled = true
|
||||||
if node.generics.len() > 0:
|
if node.generics.len() > 0:
|
||||||
fn.isGeneric = true
|
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.names.add(fn)
|
||||||
self.prepareFunction(fn)
|
self.prepareFunction(fn)
|
||||||
n = fn
|
n = fn
|
||||||
|
@ -1017,10 +993,13 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||||
case node.value.kind:
|
case node.value.kind:
|
||||||
of identExpr:
|
of identExpr:
|
||||||
n.valueType = self.inferOrError(node.value)
|
n.valueType = self.inferOrError(node.value)
|
||||||
|
if n.valueType.kind == Typevar:
|
||||||
|
# Type alias!
|
||||||
|
n.valueType = n.valueType.wrapped
|
||||||
of binaryExpr:
|
of binaryExpr:
|
||||||
# Type union
|
# Type union
|
||||||
n.valueType = Type(kind: Union, types: @[])
|
n.valueType = Type(kind: Union, types: @[])
|
||||||
self.unpackUnion(node.value, n.valueType.types)
|
self.unpackTypes(node.value, n.valueType.types)
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
|
@ -1043,7 +1022,7 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||||
continue
|
continue
|
||||||
if name.kind in [NameKind.Var, NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
if name.kind in [NameKind.Var, NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
||||||
if name.depth < n.depth:
|
if name.depth < n.depth:
|
||||||
self.warning(WarningKind.ShadowOuterScope, &"'{declaredName}' at depth {name.depth} shadows a name from an outer scope ({name.owner.file}.pn:{name.ident.token.line}:{name.ident.token.relPos.start})", n)
|
self.warning(WarningKind.ShadowOuterScope, &"'{declaredName}' shadows a name from an outer scope ({name.owner.file}:{name.ident.token.line}:{name.ident.token.relPos.start})", node=n.ident)
|
||||||
if name.owner != n.owner:
|
if name.owner != n.owner:
|
||||||
self.warning(WarningKind.ShadowOuterScope, &"'{declaredName}' at depth {name.depth} shadows a name from an outer module ({name.owner.file}.pn:{name.ident.token.line}:{name.ident.token.relPos.start})", n)
|
self.warning(WarningKind.ShadowOuterScope, &"'{declaredName}' shadows a name from an outer module ({name.owner.file}:{name.ident.token.line}:{name.ident.token.relPos.start})", node=n.ident)
|
||||||
return n
|
return n
|
||||||
|
|
|
@ -201,7 +201,6 @@ type
|
||||||
SysClock64, # Pushes the output of a monotonic clock on the stack
|
SysClock64, # Pushes the output of a monotonic clock on the stack
|
||||||
LoadTOS, # Pushes the top of the call stack onto the operand stack
|
LoadTOS, # Pushes the top of the call stack onto the operand stack
|
||||||
DupTop, # Duplicates the top of the operand stack onto the operand stack
|
DupTop, # Duplicates the top of the operand stack onto the operand stack
|
||||||
ReplExit, # Exits the VM immediately, leaving its state intact. Used in the REPL
|
|
||||||
LoadGlobal # Loads a global variable
|
LoadGlobal # Loads a global variable
|
||||||
|
|
||||||
|
|
||||||
|
@ -282,7 +281,6 @@ const simpleInstructions* = {Return, LoadNil,
|
||||||
Float32GreaterOrEqual,
|
Float32GreaterOrEqual,
|
||||||
Float32LessOrEqual,
|
Float32LessOrEqual,
|
||||||
DupTop,
|
DupTop,
|
||||||
ReplExit,
|
|
||||||
Identity
|
Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -466,7 +466,7 @@ proc handleBuiltinFunction(self: BytecodeCompiler, fn: Type, args: seq[Expressio
|
||||||
"Identity": Identity
|
"Identity": Identity
|
||||||
}.to_table()
|
}.to_table()
|
||||||
if fn.builtinOp == "print":
|
if fn.builtinOp == "print":
|
||||||
let typ = self.inferOrError(args[0])
|
var typ = self.inferOrError(args[0])
|
||||||
case typ.kind:
|
case typ.kind:
|
||||||
of Int64:
|
of Int64:
|
||||||
self.emitByte(PrintInt64, line)
|
self.emitByte(PrintInt64, line)
|
||||||
|
@ -528,6 +528,13 @@ proc handleBuiltinFunction(self: BytecodeCompiler, fn: Type, args: seq[Expressio
|
||||||
let jump = self.emitJump(JumpIfFalseOrPop, line)
|
let jump = self.emitJump(JumpIfFalseOrPop, line)
|
||||||
self.expression(args[1])
|
self.expression(args[1])
|
||||||
self.patchJump(jump)
|
self.patchJump(jump)
|
||||||
|
of "cast":
|
||||||
|
# Type casts are a merely compile-time construct:
|
||||||
|
# they don't produce any code at runtime because
|
||||||
|
# the underlying data representation does not change!
|
||||||
|
# The only reason why there's a "cast" pragma is to
|
||||||
|
# make it so that the peon stub can have no body
|
||||||
|
discard
|
||||||
else:
|
else:
|
||||||
self.error(&"unknown built-in: '{fn.builtinOp}'", fn.fun)
|
self.error(&"unknown built-in: '{fn.builtinOp}'", fn.fun)
|
||||||
|
|
||||||
|
@ -543,6 +550,9 @@ proc patchForwardDeclarations(self: BytecodeCompiler) =
|
||||||
if forwarded.isPrivate != impl.isPrivate:
|
if forwarded.isPrivate != impl.isPrivate:
|
||||||
self.error(&"implementation of '{impl.ident.token.lexeme}' has a mismatching visibility modifier from its forward declaration", impl.ident)
|
self.error(&"implementation of '{impl.ident.token.lexeme}' has a mismatching visibility modifier from its forward declaration", impl.ident)
|
||||||
if position == 0:
|
if position == 0:
|
||||||
|
# Forward declaration created by funDecl (it's
|
||||||
|
# necessary to make sure that there's no unimplemented
|
||||||
|
# forward declarations)
|
||||||
continue
|
continue
|
||||||
pos = impl.codePos.toLong()
|
pos = impl.codePos.toLong()
|
||||||
self.chunk.consts[position] = pos[0]
|
self.chunk.consts[position] = pos[0]
|
||||||
|
@ -585,7 +595,7 @@ proc endScope(self: BytecodeCompiler) =
|
||||||
if name.kind notin [NameKind.Var, NameKind.Argument]:
|
if name.kind notin [NameKind.Var, NameKind.Argument]:
|
||||||
continue
|
continue
|
||||||
elif name.kind == NameKind.Argument and not name.belongsTo.isNil():
|
elif name.kind == NameKind.Argument and not name.belongsTo.isNil():
|
||||||
if name.belongsTo.isBuiltin:
|
if name.belongsTo.valueType.isBuiltin:
|
||||||
# Arguments to builtin functions become temporaries on the
|
# Arguments to builtin functions become temporaries on the
|
||||||
# stack and are popped automatically
|
# stack and are popped automatically
|
||||||
continue
|
continue
|
||||||
|
@ -607,7 +617,7 @@ proc endScope(self: BytecodeCompiler) =
|
||||||
self.warning(UnusedName, &"'{name.ident.token.lexeme}' is declared but not used (add '_' prefix to silence warning)", name)
|
self.warning(UnusedName, &"'{name.ident.token.lexeme}' is declared but not used (add '_' prefix to silence warning)", name)
|
||||||
of NameKind.Argument:
|
of NameKind.Argument:
|
||||||
if not name.ident.token.lexeme.startsWith("_") and name.isPrivate:
|
if not name.ident.token.lexeme.startsWith("_") and name.isPrivate:
|
||||||
if not name.belongsTo.isNil() and not name.belongsTo.isBuiltin and name.belongsTo.isReal and name.belongsTo.resolved:
|
if not name.belongsTo.isNil() and not name.belongsTo.valueType.isBuiltin and name.belongsTo.isReal and name.belongsTo.resolved:
|
||||||
# Builtin functions never use their arguments. We also don't emit this
|
# Builtin functions never use their arguments. We also don't emit this
|
||||||
# warning if the function was generated internally by the compiler (for
|
# warning if the function was generated internally by the compiler (for
|
||||||
# example as a result of generic specialization) because such objects do
|
# example as a result of generic specialization) because such objects do
|
||||||
|
@ -639,56 +649,6 @@ proc endScope(self: BytecodeCompiler) =
|
||||||
inc(idx)
|
inc(idx)
|
||||||
|
|
||||||
|
|
||||||
proc unpackGenerics(self: BytecodeCompiler, 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: BytecodeCompiler, 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 emitLoop(self: BytecodeCompiler, begin: int, line: int) =
|
proc emitLoop(self: BytecodeCompiler, begin: int, line: int) =
|
||||||
## Emits a JumpBackwards instruction with the correct
|
## Emits a JumpBackwards instruction with the correct
|
||||||
## jump offset
|
## jump offset
|
||||||
|
@ -717,19 +677,20 @@ proc handleMagicPragma(self: BytecodeCompiler, 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 (expected 1, got {len(pragma.args)})")
|
||||||
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 argument type (constant string expected, got {self.stringify(self.inferOrError(pragma.args[0]))})")
|
||||||
elif name.node.kind == NodeKind.funDecl:
|
elif name.node.kind == NodeKind.funDecl:
|
||||||
name.isBuiltin = true
|
name.valueType.isBuiltin = true
|
||||||
name.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2]
|
name.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2]
|
||||||
|
name.valueType.compiled = true
|
||||||
elif name.node.kind == NodeKind.typeDecl:
|
elif name.node.kind == NodeKind.typeDecl:
|
||||||
name.valueType = pragma.args[0].token.lexeme[1..^2].toIntrinsic()
|
name.valueType = pragma.args[0].token.lexeme[1..^2].toIntrinsic()
|
||||||
if name.valueType.kind == All:
|
if name.valueType.kind == All:
|
||||||
self.error("don't even think about it (compiler-chan is angry at you)", pragma)
|
self.error("don't even think about it (compiler-chan is angry at you :/)", pragma)
|
||||||
if name.valueType.isNil():
|
if name.valueType.isNil():
|
||||||
self.error("'magic' pragma: wrong argument value", pragma.args[0])
|
self.error("'magic' pragma: wrong argument value", pragma.args[0])
|
||||||
name.isBuiltin = true
|
name.valueType.isBuiltin = true
|
||||||
else:
|
else:
|
||||||
self.error("'magic' pragma is not valid in this context")
|
self.error("'magic' pragma is not valid in this context")
|
||||||
|
|
||||||
|
@ -814,6 +775,9 @@ proc generateCall(self: BytecodeCompiler, fn: Type, args: seq[Expression], line:
|
||||||
## instead of Name objects (used for lambdas and
|
## instead of Name objects (used for lambdas and
|
||||||
## consequent calls). The function's address is
|
## consequent calls). The function's address is
|
||||||
## assumed to be on the stack
|
## assumed to be on the stack
|
||||||
|
if fn.isBuiltin:
|
||||||
|
self.handleBuiltinFunction(fn, args, line)
|
||||||
|
return
|
||||||
self.emitByte(LoadUInt64, line)
|
self.emitByte(LoadUInt64, line)
|
||||||
self.emitBytes(self.chunk.writeConstant(0.toLong()), line)
|
self.emitBytes(self.chunk.writeConstant(0.toLong()), line)
|
||||||
let pos = self.chunk.consts.len() - 8
|
let pos = self.chunk.consts.len() - 8
|
||||||
|
@ -840,7 +804,7 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
||||||
# 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 fn.node.generics:
|
for gen in fn.node.generics:
|
||||||
self.unpackGenerics(gen.cond, constraints)
|
self.unpackTypes(gen.cond, constraints)
|
||||||
self.names.add(Name(depth: fn.depth + 1,
|
self.names.add(Name(depth: fn.depth + 1,
|
||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
valueType: Type(kind: Generic, name: gen.name.token.lexeme, cond: constraints),
|
valueType: Type(kind: Generic, name: gen.name.token.lexeme, cond: constraints),
|
||||||
|
@ -857,19 +821,23 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
||||||
let idx = self.stackIndex
|
let idx = self.stackIndex
|
||||||
self.stackIndex = 1
|
self.stackIndex = 1
|
||||||
var default: Expression
|
var default: Expression
|
||||||
var node = FunDecl(fn.node)
|
let node = FunDecl(fn.node)
|
||||||
var i = 0
|
var i = 0
|
||||||
|
var typ: Type
|
||||||
for argument in node.arguments:
|
for argument in 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")
|
||||||
inc(self.stackIndex)
|
inc(self.stackIndex)
|
||||||
|
typ = self.inferOrError(argument.valueType)
|
||||||
|
if typ.kind == Typevar:
|
||||||
|
typ = typ.wrapped
|
||||||
self.names.add(Name(depth: fn.depth + 1,
|
self.names.add(Name(depth: fn.depth + 1,
|
||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
owner: fn.owner,
|
owner: fn.owner,
|
||||||
file: fn.file,
|
file: fn.file,
|
||||||
isConst: false,
|
isConst: false,
|
||||||
ident: argument.name,
|
ident: argument.name,
|
||||||
valueType: if not fn.valueType.isAuto: self.inferOrError(argument.valueType) else: Type(kind: Any),
|
valueType: typ,
|
||||||
codePos: 0,
|
codePos: 0,
|
||||||
isLet: false,
|
isLet: false,
|
||||||
line: argument.name.token.line,
|
line: argument.name.token.line,
|
||||||
|
@ -881,14 +849,16 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
||||||
))
|
))
|
||||||
if node.arguments.high() - node.defaults.high() <= node.arguments.high():
|
if node.arguments.high() - node.defaults.high() <= node.arguments.high():
|
||||||
# There's a default argument!
|
# There's a default argument!
|
||||||
fn.valueType.args.add((self.names[^1].ident.token.lexeme, self.names[^1].valueType, node.defaults[i]))
|
fn.valueType.args.add((self.names[^1].ident.token.lexeme, typ, node.defaults[i]))
|
||||||
inc(i)
|
inc(i)
|
||||||
else:
|
else:
|
||||||
# This argument has no default
|
# This argument has no default
|
||||||
fn.valueType.args.add((self.names[^1].ident.token.lexeme, self.names[^1].valueType, default))
|
fn.valueType.args.add((self.names[^1].ident.token.lexeme, typ, default))
|
||||||
# The function needs a return type too!
|
# The function needs a return type too!
|
||||||
if not FunDecl(fn.node).returnType.isNil():
|
if not node.returnType.isNil():
|
||||||
fn.valueType.returnType = self.inferOrError(FunDecl(fn.node).returnType)
|
fn.valueType.returnType = self.inferOrError(node.returnType)
|
||||||
|
if fn.valueType.returnType.kind == Typevar:
|
||||||
|
fn.valueType.returnType = fn.valueType.returnType.wrapped
|
||||||
fn.position = self.stackIndex
|
fn.position = self.stackIndex
|
||||||
self.stackIndex = idx
|
self.stackIndex = idx
|
||||||
if node.isTemplate:
|
if node.isTemplate:
|
||||||
|
@ -941,8 +911,7 @@ proc prepareAutoFunction(self: BytecodeCompiler, fn: Name, args: seq[tuple[name:
|
||||||
proc generateCall(self: BytecodeCompiler, fn: Name, args: seq[Expression], line: int) =
|
proc generateCall(self: BytecodeCompiler, fn: Name, args: seq[Expression], line: int) =
|
||||||
## Small wrapper that abstracts emitting a call instruction
|
## Small wrapper that abstracts emitting a call instruction
|
||||||
## for a given function
|
## for a given function
|
||||||
self.dispatchDelayedPragmas(fn)
|
if fn.valueType.isBuiltin:
|
||||||
if fn.isBuiltin:
|
|
||||||
self.handleBuiltinFunction(fn.valueType, args, line)
|
self.handleBuiltinFunction(fn.valueType, args, line)
|
||||||
return
|
return
|
||||||
case fn.kind:
|
case fn.kind:
|
||||||
|
@ -969,15 +938,13 @@ proc generateCall(self: BytecodeCompiler, fn: Name, args: seq[Expression], line:
|
||||||
|
|
||||||
|
|
||||||
proc specialize(self: BytecodeCompiler, typ: Type, args: seq[Expression]): Type {.discardable.} =
|
proc specialize(self: BytecodeCompiler, typ: Type, args: seq[Expression]): Type {.discardable.} =
|
||||||
## Specializes a generic type.
|
## Instantiates a generic type
|
||||||
## Used for typechecking at the
|
|
||||||
## call site
|
|
||||||
var mapping: TableRef[string, Type] = newTable[string, Type]()
|
var mapping: TableRef[string, Type] = newTable[string, Type]()
|
||||||
var kind: Type
|
var kind: Type
|
||||||
result = deepCopy(typ)
|
result = deepCopy(typ)
|
||||||
case result.kind:
|
case result.kind:
|
||||||
of TypeKind.Function:
|
of TypeKind.Function:
|
||||||
# This first loop checks if a user tries to reassign a generic's
|
# This loop checks if a user tries to reassign a generic's
|
||||||
# name to a different type
|
# name to a different type
|
||||||
for i, (name, typ, default) in result.args:
|
for i, (name, typ, default) in result.args:
|
||||||
if typ.kind != Generic:
|
if typ.kind != Generic:
|
||||||
|
@ -990,6 +957,17 @@ proc specialize(self: BytecodeCompiler, typ: Type, args: seq[Expression]): Type
|
||||||
if not result.returnType.isNil() and result.returnType.kind == Generic:
|
if not result.returnType.isNil() and result.returnType.kind == Generic:
|
||||||
if result.returnType.name in mapping:
|
if result.returnType.name in mapping:
|
||||||
result.returnType = mapping[result.returnType.name]
|
result.returnType = mapping[result.returnType.name]
|
||||||
|
elif mapping.len() == 0:
|
||||||
|
# The function has no generic arguments,
|
||||||
|
# just a generic return type
|
||||||
|
var typ: Type
|
||||||
|
for i, gen in result.fun.generics:
|
||||||
|
if gen.name.token.lexeme == result.returnType.name:
|
||||||
|
typ = result.args[i].kind
|
||||||
|
break
|
||||||
|
if typ.isNil():
|
||||||
|
self.error(&"unknown generic argument name '{result.returnType.name}'", result.fun)
|
||||||
|
result.returnType = typ
|
||||||
else:
|
else:
|
||||||
self.error(&"unknown generic argument name '{result.returnType.name}'", result.fun)
|
self.error(&"unknown generic argument name '{result.returnType.name}'", result.fun)
|
||||||
else:
|
else:
|
||||||
|
@ -1000,12 +978,10 @@ proc terminateProgram(self: BytecodeCompiler, pos: int) =
|
||||||
## Utility to terminate a peon program
|
## Utility to terminate a peon program
|
||||||
self.patchForwardDeclarations()
|
self.patchForwardDeclarations()
|
||||||
self.endScope()
|
self.endScope()
|
||||||
if self.replMode:
|
|
||||||
self.emitByte(ReplExit, self.peek().token.line)
|
self.emitByte(OpCode.Return, self.peek().token.line)
|
||||||
else:
|
self.emitByte(0, self.peek().token.line) # Entry point has no return value
|
||||||
self.emitByte(OpCode.Return, self.peek().token.line)
|
self.patchReturnAddress(pos)
|
||||||
self.emitByte(0, self.peek().token.line) # Entry point has no return value
|
|
||||||
self.patchReturnAddress(pos)
|
|
||||||
|
|
||||||
|
|
||||||
proc beginProgram(self: BytecodeCompiler): int =
|
proc beginProgram(self: BytecodeCompiler): int =
|
||||||
|
@ -1207,6 +1183,10 @@ method identifier(self: BytecodeCompiler, node: IdentExpr, name: Name = nil, com
|
||||||
if s.isNil() and not strict:
|
if s.isNil() and not strict:
|
||||||
return nil
|
return nil
|
||||||
result = s.valueType
|
result = s.valueType
|
||||||
|
if s.kind == NameKind.CustomType:
|
||||||
|
# This makes it so that the type of
|
||||||
|
# a type comes out as "typevar"
|
||||||
|
result = Type(kind: Typevar, wrapped: result)
|
||||||
if not compile:
|
if not compile:
|
||||||
return result
|
return result
|
||||||
var node = s.ident
|
var node = s.ident
|
||||||
|
@ -1224,7 +1204,12 @@ method identifier(self: BytecodeCompiler, node: IdentExpr, name: Name = nil, com
|
||||||
# they're referenced
|
# they're referenced
|
||||||
self.emitByte(LoadUInt64, node.token.line)
|
self.emitByte(LoadUInt64, node.token.line)
|
||||||
self.emitBytes(self.chunk.writeConstant(s.codePos.toLong()), node.token.line)
|
self.emitBytes(self.chunk.writeConstant(s.codePos.toLong()), node.token.line)
|
||||||
elif s.isBuiltin:
|
elif s.kind == NameKind.CustomType:
|
||||||
|
# Types have no runtime representation either, but we need
|
||||||
|
# to have something on the stack to pop off (just to act as
|
||||||
|
# a placeholder)
|
||||||
|
self.emitByte(LoadNil, node.token.line)
|
||||||
|
elif s.valueType.isBuiltin:
|
||||||
case s.ident.token.lexeme:
|
case s.ident.token.lexeme:
|
||||||
of "nil":
|
of "nil":
|
||||||
self.emitByte(LoadNil, node.token.line)
|
self.emitByte(LoadNil, node.token.line)
|
||||||
|
@ -1284,6 +1269,44 @@ method assignment(self: BytecodeCompiler, node: ASTNode, compile: bool = true):
|
||||||
self.error(&"invalid AST node of kind {node.kind} at assignment(): {node} (This is an internal error and most likely a bug)")
|
self.error(&"invalid AST node of kind {node.kind} at assignment(): {node} (This is an internal error and most likely a bug)")
|
||||||
|
|
||||||
|
|
||||||
|
method makeConcrete(self: BytecodeCompiler, node: GenericExpr, compile: bool = true): Type =
|
||||||
|
## Builds a concrete type from the given generic
|
||||||
|
## instantiation
|
||||||
|
var name = self.resolveOrError(node.ident)
|
||||||
|
if not name.isGeneric:
|
||||||
|
self.error(&"cannot instantiate concrete type from {self.stringify(name.valueType)}: a generic is required")
|
||||||
|
var fun = FunDecl(name.node)
|
||||||
|
if fun.generics.len() != node.args.len():
|
||||||
|
self.error(&"wrong number of types supplied for generic instantiation (expected {fun.generics.len()}, got {node.args.len()} instead)")
|
||||||
|
var concrete = deepCopy(name.valueType)
|
||||||
|
var types: seq[Type] = @[]
|
||||||
|
var map = newTable[string, Type]()
|
||||||
|
for arg in node.args:
|
||||||
|
types.add(self.inferOrError(arg))
|
||||||
|
if types[^1].kind != Typevar:
|
||||||
|
self.error(&"expecting type name during generic instantiation, got {self.stringify(types[^1])} instead", arg)
|
||||||
|
for (gen, value) in zip(fun.generics, node.args):
|
||||||
|
map[gen.name.token.lexeme] = self.inferOrError(value)
|
||||||
|
for i, argument in concrete.args:
|
||||||
|
if argument.kind.kind != Generic:
|
||||||
|
continue
|
||||||
|
elif argument.name in map:
|
||||||
|
concrete.args[i].kind = map[argument.name]
|
||||||
|
else:
|
||||||
|
self.error(&"unknown generic argument name '{argument.name}'", concrete.fun)
|
||||||
|
if not concrete.returnType.isNil() and concrete.returnType.kind == Generic:
|
||||||
|
if concrete.returnType.name in map:
|
||||||
|
concrete.returnType = map[concrete.returnType.name]
|
||||||
|
else:
|
||||||
|
self.error(&"unknown generic argument name '{concrete.returnType.name}'", concrete.fun)
|
||||||
|
if compile:
|
||||||
|
# Types don't exist at runtime, but if you want to
|
||||||
|
# assign them to variables then you need *something*
|
||||||
|
# to pop off the stack, so we just push a nil
|
||||||
|
self.emitByte(LoadNil, node.token.line)
|
||||||
|
result = concrete
|
||||||
|
|
||||||
|
|
||||||
method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type {.discardable.} =
|
method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type {.discardable.} =
|
||||||
## Compiles function calls
|
## Compiles function calls
|
||||||
var args: seq[tuple[name: string, kind: Type, default: Expression]] = @[]
|
var args: seq[tuple[name: string, kind: Type, default: Expression]] = @[]
|
||||||
|
@ -1321,7 +1344,9 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
|
||||||
if not impl.valueType.compiled:
|
if not impl.valueType.compiled:
|
||||||
self.funDecl(FunDecl(result.fun), impl)
|
self.funDecl(FunDecl(result.fun), impl)
|
||||||
result = result.returnType
|
result = result.returnType
|
||||||
|
self.dispatchDelayedPragmas(impl)
|
||||||
if compile:
|
if compile:
|
||||||
|
# Lambdas can't be templates :P
|
||||||
if impl.valueType.fun.kind == funDecl and FunDecl(impl.valueType.fun).isTemplate:
|
if impl.valueType.fun.kind == funDecl and FunDecl(impl.valueType.fun).isTemplate:
|
||||||
for arg in reversed(argExpr):
|
for arg in reversed(argExpr):
|
||||||
self.expression(arg)
|
self.expression(arg)
|
||||||
|
@ -1357,7 +1382,7 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
|
||||||
self.generateCall(result, argExpr, node.token.line)
|
self.generateCall(result, argExpr, node.token.line)
|
||||||
result = result.returnType
|
result = result.returnType
|
||||||
of NodeKind.getItemExpr:
|
of NodeKind.getItemExpr:
|
||||||
var node = GetItemExpr(node.callee)
|
let node = GetItemExpr(node.callee)
|
||||||
result = self.getItemExpr(node, compile=false, matching=Type(kind: Function, args: args, returnType: Type(kind: All)))
|
result = self.getItemExpr(node, compile=false, matching=Type(kind: Function, args: args, returnType: Type(kind: All)))
|
||||||
var fn: Name
|
var fn: Name
|
||||||
# getItemExpr returns a Type object, but
|
# getItemExpr returns a Type object, but
|
||||||
|
@ -1375,17 +1400,29 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
|
||||||
if compile:
|
if compile:
|
||||||
self.generateCall(fn, argExpr, node.token.line)
|
self.generateCall(fn, argExpr, node.token.line)
|
||||||
of NodeKind.lambdaExpr:
|
of NodeKind.lambdaExpr:
|
||||||
|
# Calling a lambda
|
||||||
var node = LambdaExpr(node.callee)
|
var node = LambdaExpr(node.callee)
|
||||||
var impl = self.lambdaExpr(node, compile=compile)
|
let impl = self.lambdaExpr(node, compile=compile)
|
||||||
result = impl.returnType
|
result = impl.returnType
|
||||||
if compile:
|
if compile:
|
||||||
self.generateCall(impl, argExpr, node.token.line)
|
self.generateCall(impl, argExpr, node.token.line)
|
||||||
|
of NodeKind.genericExpr:
|
||||||
|
# Instantiating a generic type
|
||||||
|
let node = GenericExpr(node.callee)
|
||||||
|
let concrete = self.makeConcrete(node)
|
||||||
|
var impl = self.resolve(node.ident).deepCopy()
|
||||||
|
impl.valueType = concrete
|
||||||
|
result = impl.valueType.returnType
|
||||||
|
if compile:
|
||||||
|
self.generateCall(impl, argExpr, node.token.line)
|
||||||
else:
|
else:
|
||||||
let typ = self.infer(node)
|
let typ = self.infer(node)
|
||||||
if typ.isNil():
|
if typ.isNil():
|
||||||
self.error(&"expression has no type", node)
|
self.error(&"expression has no type", node)
|
||||||
else:
|
else:
|
||||||
self.error(&"object of type '{self.stringify(typ)}' is not callable", node)
|
self.error(&"object of type '{self.stringify(typ)}' is not callable", node)
|
||||||
|
if not result.isNil() and result.kind == Typevar:
|
||||||
|
result = result.wrapped
|
||||||
|
|
||||||
|
|
||||||
method getItemExpr(self: BytecodeCompiler, node: GetItemExpr, compile: bool = true, matching: Type = nil): Type {.discardable.} =
|
method getItemExpr(self: BytecodeCompiler, node: GetItemExpr, compile: bool = true, matching: Type = nil): Type {.discardable.} =
|
||||||
|
@ -1547,6 +1584,8 @@ method lambdaExpr(self: BytecodeCompiler, node: LambdaExpr, compile: bool = true
|
||||||
method expression(self: BytecodeCompiler, node: Expression, compile: bool = true): Type {.discardable.} =
|
method expression(self: BytecodeCompiler, node: Expression, compile: bool = true): Type {.discardable.} =
|
||||||
## Compiles all expressions
|
## Compiles all expressions
|
||||||
case node.kind:
|
case node.kind:
|
||||||
|
of NodeKind.genericExpr:
|
||||||
|
return self.makeConcrete(GenericExpr(node))
|
||||||
of NodeKind.callExpr:
|
of NodeKind.callExpr:
|
||||||
return self.call(CallExpr(node), compile)
|
return self.call(CallExpr(node), compile)
|
||||||
of NodeKind.getItemExpr:
|
of NodeKind.getItemExpr:
|
||||||
|
@ -1796,7 +1835,7 @@ proc namedBlock(self: BytecodeCompiler, node: NamedBlockStmt) =
|
||||||
|
|
||||||
|
|
||||||
proc switchStmt(self: BytecodeCompiler, node: SwitchStmt) =
|
proc switchStmt(self: BytecodeCompiler, node: SwitchStmt) =
|
||||||
## Compiles C-style switch statements
|
## Compiles switch statements
|
||||||
self.expression(node.switch)
|
self.expression(node.switch)
|
||||||
let typeOfA = self.inferOrError(node.switch)
|
let typeOfA = self.inferOrError(node.switch)
|
||||||
var ifJump: int = -1
|
var ifJump: int = -1
|
||||||
|
@ -1895,9 +1934,11 @@ proc varDecl(self: BytecodeCompiler, node: VarDecl) =
|
||||||
if node.value.isNil():
|
if node.value.isNil():
|
||||||
# Variable has no value: the type declaration
|
# Variable has no value: the type declaration
|
||||||
# takes over
|
# takes over
|
||||||
typ = self.inferOrError(node.valueType)
|
|
||||||
if typ.kind == Auto:
|
if typ.kind == Auto:
|
||||||
self.error("automatic types require initialization", node)
|
self.error("automatic types require initialization", node)
|
||||||
|
typ = self.inferOrError(node.valueType)
|
||||||
|
if typ.kind != Typevar:
|
||||||
|
self.error(&"expecting type name, got value of type {self.stringify(typ)} instead", node.name)
|
||||||
elif node.valueType.isNil():
|
elif node.valueType.isNil():
|
||||||
# Variable has no type declaration: the type
|
# Variable has no type declaration: the type
|
||||||
# of its value takes over
|
# of its value takes over
|
||||||
|
@ -1915,7 +1956,7 @@ proc varDecl(self: BytecodeCompiler, node: VarDecl) =
|
||||||
# Let the compiler infer the type (this
|
# Let the compiler infer the type (this
|
||||||
# is the default behavior already, but
|
# is the default behavior already, but
|
||||||
# some users may prefer to be explicit!)
|
# some users may prefer to be explicit!)
|
||||||
typ = self.infer(node.value)
|
typ = self.inferOrError(node.value)
|
||||||
self.expression(node.value)
|
self.expression(node.value)
|
||||||
self.emitByte(AddVar, node.token.line)
|
self.emitByte(AddVar, node.token.line)
|
||||||
inc(self.stackIndex)
|
inc(self.stackIndex)
|
||||||
|
@ -1945,7 +1986,7 @@ proc funDecl(self: BytecodeCompiler, node: FunDecl, name: Name) =
|
||||||
self.forwarded.add((name, 0))
|
self.forwarded.add((name, 0))
|
||||||
name.valueType.forwarded = true
|
name.valueType.forwarded = true
|
||||||
return
|
return
|
||||||
if name.isBuiltin:
|
if name.valueType.isBuiltin:
|
||||||
# Builtins are handled at call time
|
# Builtins are handled at call time
|
||||||
return
|
return
|
||||||
self.currentFunction = name
|
self.currentFunction = name
|
||||||
|
@ -2087,8 +2128,6 @@ proc compile*(self: BytecodeCompiler, ast: seq[Declaration], file: string, lines
|
||||||
else:
|
else:
|
||||||
self.ast = ast
|
self.ast = ast
|
||||||
self.current = 0
|
self.current = 0
|
||||||
if not incremental:
|
|
||||||
self.stackIndex = 1
|
|
||||||
self.lines = lines
|
self.lines = lines
|
||||||
self.source = source
|
self.source = source
|
||||||
self.isMainModule = isMainModule
|
self.isMainModule = isMainModule
|
||||||
|
@ -2099,9 +2138,8 @@ proc compile*(self: BytecodeCompiler, ast: seq[Declaration], file: string, lines
|
||||||
if not incremental:
|
if not incremental:
|
||||||
self.jumps = @[]
|
self.jumps = @[]
|
||||||
self.modules = newTable[string, Name]()
|
self.modules = newTable[string, Name]()
|
||||||
|
self.stackIndex = 1
|
||||||
let pos = self.beginProgram()
|
let pos = self.beginProgram()
|
||||||
let idx = self.stackIndex
|
|
||||||
self.stackIndex = idx
|
|
||||||
while not self.done():
|
while not self.done():
|
||||||
self.declaration(Declaration(self.step()))
|
self.declaration(Declaration(self.step()))
|
||||||
self.terminateProgram(pos)
|
self.terminateProgram(pos)
|
||||||
|
|
|
@ -79,6 +79,7 @@ type
|
||||||
pragmaExpr,
|
pragmaExpr,
|
||||||
refExpr,
|
refExpr,
|
||||||
ptrExpr,
|
ptrExpr,
|
||||||
|
genericExpr,
|
||||||
switchStmt
|
switchStmt
|
||||||
|
|
||||||
# Here I would've rather used object variants, and in fact that's what was in
|
# Here I would've rather used object variants, and in fact that's what was in
|
||||||
|
@ -155,6 +156,11 @@ type
|
||||||
name: IdentExpr, value: Expression]]]
|
name: IdentExpr, value: Expression]]]
|
||||||
closeParen*: Token # Needed for error reporting
|
closeParen*: Token # Needed for error reporting
|
||||||
|
|
||||||
|
GenericExpr* = ref object of Expression
|
||||||
|
ident*: IdentExpr
|
||||||
|
args*: seq[Expression]
|
||||||
|
|
||||||
|
|
||||||
UnaryExpr* = ref object of Expression
|
UnaryExpr* = ref object of Expression
|
||||||
operator*: Token
|
operator*: Token
|
||||||
a*: Expression
|
a*: Expression
|
||||||
|
@ -187,7 +193,7 @@ type
|
||||||
ends*: seq[Expression]
|
ends*: seq[Expression]
|
||||||
|
|
||||||
AssignExpr* = ref object of Expression
|
AssignExpr* = ref object of Expression
|
||||||
name*: Expression
|
name*: IdentExpr
|
||||||
value*: Expression
|
value*: Expression
|
||||||
|
|
||||||
ExprStmt* = ref object of Statement
|
ExprStmt* = ref object of Statement
|
||||||
|
@ -459,6 +465,13 @@ proc newCallExpr*(callee: Expression, arguments: tuple[positionals: seq[
|
||||||
result.token = token
|
result.token = token
|
||||||
|
|
||||||
|
|
||||||
|
proc newGenericExpr*(ident: IdentExpr, args: seq[Expression]): GenericExpr =
|
||||||
|
result = GenericExpr(kind: genericExpr)
|
||||||
|
result.ident = ident
|
||||||
|
result.args = args
|
||||||
|
result.token = ident.token
|
||||||
|
|
||||||
|
|
||||||
proc newSliceExpr*(expression: Expression, ends: seq[Expression], token: Token): SliceExpr =
|
proc newSliceExpr*(expression: Expression, ends: seq[Expression], token: Token): SliceExpr =
|
||||||
result = SliceExpr(kind: sliceExpr)
|
result = SliceExpr(kind: sliceExpr)
|
||||||
result.expression = expression
|
result.expression = expression
|
||||||
|
@ -487,7 +500,7 @@ proc newYieldExpr*(expression: Expression, token: Token): YieldExpr =
|
||||||
result.token = token
|
result.token = token
|
||||||
|
|
||||||
|
|
||||||
proc newAssignExpr*(name: Expression, value: Expression,
|
proc newAssignExpr*(name: IdentExpr, value: Expression,
|
||||||
token: Token): AssignExpr =
|
token: Token): AssignExpr =
|
||||||
result = AssignExpr(kind: assignExpr)
|
result = AssignExpr(kind: assignExpr)
|
||||||
result.name = name
|
result.name = name
|
||||||
|
@ -786,6 +799,9 @@ proc `$`*(self: ASTNode): string =
|
||||||
result &= &"Ref({Ref(self).value})"
|
result &= &"Ref({Ref(self).value})"
|
||||||
of ptrExpr:
|
of ptrExpr:
|
||||||
result &= &"Ptr({Ptr(self).value})"
|
result &= &"Ptr({Ptr(self).value})"
|
||||||
|
of genericExpr:
|
||||||
|
var self = GenericExpr(self)
|
||||||
|
result &= &"Generic(ident={self.ident}, args={self.args})"
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
@ -843,6 +859,13 @@ proc getRelativeBoundaries*(self: ASTNode): tuple[start, stop: int] =
|
||||||
stop = self.token.relPos.stop + 1
|
stop = self.token.relPos.stop + 1
|
||||||
# -8 so the error highlights the #pragma[ part as well
|
# -8 so the error highlights the #pragma[ part as well
|
||||||
result = (self.token.relPos.start - 8, stop)
|
result = (self.token.relPos.start - 8, stop)
|
||||||
|
of genericExpr:
|
||||||
|
var self = GenericExpr(self)
|
||||||
|
let ident = getRelativeBoundaries(self.ident)
|
||||||
|
var stop: int = ident.stop + 2
|
||||||
|
if self.args.len() > 0:
|
||||||
|
stop = getRelativeBoundaries(self.args[^1]).stop
|
||||||
|
result = (ident.start, stop)
|
||||||
else:
|
else:
|
||||||
result = (0, 0)
|
result = (0, 0)
|
||||||
|
|
|
@ -418,11 +418,12 @@ proc makeCall(self: Parser, callee: Expression): CallExpr =
|
||||||
self.error("can not pass more than 255 arguments in function call")
|
self.error("can not pass more than 255 arguments in function call")
|
||||||
break
|
break
|
||||||
argument = self.expression()
|
argument = self.expression()
|
||||||
if argument.kind == binaryExpr and BinaryExpr(argument).operator.lexeme == "=":
|
if argument.kind == assignExpr:
|
||||||
if IdentExpr(BinaryExpr(argument).a) in argNames:
|
var assign = AssignExpr(argument)
|
||||||
self.error("duplicate keyword argument in function call is not allowed")
|
if assign.name in argNames:
|
||||||
argNames.add(IdentExpr(BinaryExpr(argument).a))
|
self.error("duplicate keyword arguments are not allowed", assign.name.token)
|
||||||
arguments.keyword.add((name: IdentExpr(BinaryExpr(argument).a), value: BinaryExpr(argument).b))
|
argNames.add(assign.name)
|
||||||
|
arguments.keyword.add((name: assign.name, value: assign.value))
|
||||||
elif arguments.keyword.len() == 0:
|
elif arguments.keyword.len() == 0:
|
||||||
arguments.positionals.add(argument)
|
arguments.positionals.add(argument)
|
||||||
else:
|
else:
|
||||||
|
@ -436,10 +437,18 @@ proc makeCall(self: Parser, callee: Expression): CallExpr =
|
||||||
result.closeParen = self.peek(-1)
|
result.closeParen = self.peek(-1)
|
||||||
|
|
||||||
|
|
||||||
proc parseGenericArgs(self: Parser) =
|
proc parseGenericArgs(self: Parser): Expression =
|
||||||
## Parses function generic arguments
|
## Parses expressions like someType[someGeneric]
|
||||||
## like function[type](arg)
|
## that are needed to instantiate generics
|
||||||
discard # TODO
|
var item = newIdentExpr(self.peek(-2), self.scopeDepth)
|
||||||
|
var types: seq[Expression] = @[]
|
||||||
|
while not self.check(RightBracket) and not self.done():
|
||||||
|
self.expect(Identifier)
|
||||||
|
types.add(newIdentExpr(self.peek(-1), self.scopeDepth))
|
||||||
|
if not self.match(Comma):
|
||||||
|
break
|
||||||
|
self.expect(RightBracket)
|
||||||
|
return newGenericExpr(item, types)
|
||||||
|
|
||||||
|
|
||||||
proc call(self: Parser): Expression =
|
proc call(self: Parser): Expression =
|
||||||
|
@ -454,8 +463,9 @@ proc call(self: Parser): Expression =
|
||||||
result = newGetItemExpr(result, newIdentExpr(self.peek(-1), self.scopeDepth), self.peek(-1))
|
result = newGetItemExpr(result, newIdentExpr(self.peek(-1), self.scopeDepth), self.peek(-1))
|
||||||
result.file = self.file
|
result.file = self.file
|
||||||
elif self.match(LeftBracket):
|
elif self.match(LeftBracket):
|
||||||
self.parseGenericArgs() # TODO
|
if self.peek(-2).kind != Identifier:
|
||||||
result = self.makeCall(result)
|
self.error("invalid syntax")
|
||||||
|
result = self.parseGenericArgs()
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -564,7 +574,7 @@ proc parseAssign(self: Parser): Expression =
|
||||||
var value = self.expression()
|
var value = self.expression()
|
||||||
case result.kind:
|
case result.kind:
|
||||||
of identExpr, sliceExpr:
|
of identExpr, sliceExpr:
|
||||||
result = newAssignExpr(result, value, tok)
|
result = newAssignExpr(IdentExpr(result), value, tok)
|
||||||
result.file = self.file
|
result.file = self.file
|
||||||
of getItemExpr:
|
of getItemExpr:
|
||||||
result = newSetItemExpr(GetItemExpr(result).obj, GetItemExpr(result).name, value, tok)
|
result = newSetItemExpr(GetItemExpr(result).obj, GetItemExpr(result).name, value, tok)
|
||||||
|
@ -839,7 +849,7 @@ proc tryStmt(self: Parser): Statement =
|
||||||
var elseClause: Statement
|
var elseClause: Statement
|
||||||
while self.match(Except):
|
while self.match(Except):
|
||||||
if self.match(LeftBrace):
|
if self.match(LeftBrace):
|
||||||
handlers.add((body: self.blockStmt(), exc: newIdentExpr(self.peek(-1))))
|
handlers.add((body: self.blockStmt(), exc: newIdentExpr(self.peek(-1), self.scopeDepth)))
|
||||||
else:
|
else:
|
||||||
self.expect(Identifier, "expecting exception name after 'except'")
|
self.expect(Identifier, "expecting exception name after 'except'")
|
||||||
self.expect(LeftBrace, "expecting '{' after exception name")
|
self.expect(LeftBrace, "expecting '{' after exception name")
|
||||||
|
|
11
src/main.nim
11
src/main.nim
|
@ -50,7 +50,7 @@ proc getLineEditor: LineEditor =
|
||||||
let history = result.plugHistory()
|
let history = result.plugHistory()
|
||||||
result.bindHistory(history)
|
result.bindHistory(history)
|
||||||
|
|
||||||
|
#[
|
||||||
proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug, breakpoints: seq[uint64] = @[]) =
|
proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug, breakpoints: seq[uint64] = @[]) =
|
||||||
styledEcho fgMagenta, "Welcome into the peon REPL!"
|
styledEcho fgMagenta, "Welcome into the peon REPL!"
|
||||||
var
|
var
|
||||||
|
@ -104,7 +104,7 @@ proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: Comp
|
||||||
for node in tree:
|
for node in tree:
|
||||||
styledEcho fgGreen, "\t", $node
|
styledEcho fgGreen, "\t", $node
|
||||||
echo ""
|
echo ""
|
||||||
compiled = compiler.compile(tree, "stdin", tokenizer.getLines(), input, chunk=compiled, showMismatches=mismatches, disabledWarnings=warnings, mode=mode, incremental=true)
|
discard compiler.compile(tree, "stdin", tokenizer.getLines(), input, chunk=compiled, showMismatches=mismatches, disabledWarnings=warnings, mode=mode, incremental=first)
|
||||||
if debugCompiler:
|
if debugCompiler:
|
||||||
styledEcho fgCyan, "Compilation step:\n"
|
styledEcho fgCyan, "Compilation step:\n"
|
||||||
debugger.disassembleChunk(compiled, "stdin")
|
debugger.disassembleChunk(compiled, "stdin")
|
||||||
|
@ -152,6 +152,7 @@ proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: Comp
|
||||||
file = relativePath(file, getCurrentDir())
|
file = relativePath(file, getCurrentDir())
|
||||||
stderr.styledWriteLine(fgRed, styleBright, "Error while (de-)serializing ", fgYellow, file, fgDefault, &": {getCurrentException().msg}")
|
stderr.styledWriteLine(fgRed, styleBright, "Error while (de-)serializing ", fgYellow, file, fgDefault, &": {getCurrentException().msg}")
|
||||||
quit(0)
|
quit(0)
|
||||||
|
]#
|
||||||
|
|
||||||
|
|
||||||
proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints: seq[uint64] = @[],
|
proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints: seq[uint64] = @[],
|
||||||
|
@ -219,6 +220,9 @@ proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints
|
||||||
stderr.styledWriteLine(fgRed, styleBright, "Error: ", fgDefault, "the selected backend is not implemented yet")
|
stderr.styledWriteLine(fgRed, styleBright, "Error: ", fgDefault, "the selected backend is not implemented yet")
|
||||||
elif backend == PeonBackend.Bytecode:
|
elif backend == PeonBackend.Bytecode:
|
||||||
serialized = serializer.loadFile(f)
|
serialized = serializer.loadFile(f)
|
||||||
|
if debugCompiler:
|
||||||
|
styledEcho fgCyan, "Compilation step:\n"
|
||||||
|
debugger.disassembleChunk(serialized.chunk, f)
|
||||||
if backend == PeonBackend.Bytecode and debugSerializer:
|
if backend == PeonBackend.Bytecode and debugSerializer:
|
||||||
styledEcho fgCyan, "Serialization step: "
|
styledEcho fgCyan, "Serialization step: "
|
||||||
styledEcho fgBlue, "\t- Peon version: ", fgYellow, &"{serialized.version.major}.{serialized.version.minor}.{serialized.version.patch}", fgBlue, " (commit ", fgYellow, serialized.commit[0..8], fgBlue, ") on branch ", fgYellow, serialized.branch
|
styledEcho fgBlue, "\t- Peon version: ", fgYellow, &"{serialized.version.major}.{serialized.version.minor}.{serialized.version.patch}", fgBlue, " (commit ", fgYellow, serialized.commit[0..8], fgBlue, ") on branch ", fgYellow, serialized.branch
|
||||||
|
@ -415,6 +419,7 @@ when isMainModule:
|
||||||
if breaks.len() == 0 and debugVM:
|
if breaks.len() == 0 and debugVM:
|
||||||
breaks.add(0)
|
breaks.add(0)
|
||||||
if file == "":
|
if file == "":
|
||||||
repl(warnings, mismatches, mode, breaks)
|
echo "Sorry, the REPL is currently broken :("
|
||||||
|
#repl(warnings, mismatches, mode, breaks)
|
||||||
else:
|
else:
|
||||||
runFile(file, fromString, dump, breaks, warnings, mismatches, mode, run, backend, output)
|
runFile(file, fromString, dump, breaks, warnings, mismatches, mode, run, backend, output)
|
||||||
|
|
|
@ -68,6 +68,12 @@ type auto* = object {
|
||||||
#pragma[magic: "auto"]
|
#pragma[magic: "auto"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type typevar* = object {
|
||||||
|
#pragma[magic: "typevar"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Some convenience aliases
|
# Some convenience aliases
|
||||||
type int* = int64;
|
type int* = int64;
|
||||||
type float* = float64;
|
type float* = float64;
|
||||||
|
|
|
@ -22,3 +22,8 @@ var test* = 0x60;
|
||||||
fn testGlobals*: bool {
|
fn testGlobals*: bool {
|
||||||
return version == 1 and _private == 5 and test == 0x60;
|
return version == 1 and _private == 5 and test == 0x60;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn cast*[T: any](x: any): T {
|
||||||
|
#pragma[magic: "cast"]
|
||||||
|
}
|
|
@ -49,9 +49,9 @@ proc print*(exc: CompileError) =
|
||||||
var contents: string
|
var contents: string
|
||||||
if file notin ["<string>", "", "stdin"]:
|
if file notin ["<string>", "", "stdin"]:
|
||||||
file = relativePath(exc.file, getCurrentDir())
|
file = relativePath(exc.file, getCurrentDir())
|
||||||
contents = readFile(file).splitLines()[exc.line - 1].strip(chars={'\n'})
|
contents = readFile(file).strip(chars={'\n'}).splitLines()[exc.line - 1]
|
||||||
else:
|
else:
|
||||||
contents = exc.compiler.getSource().splitLines()[exc.line - 1].strip(chars={'\n'})
|
contents = exc.compiler.getSource().strip(chars={'\n'}).splitLines()[exc.line - 1]
|
||||||
printError(file, contents, exc.line, exc.node.getRelativeBoundaries(), exc.function,
|
printError(file, contents, exc.line, exc.node.getRelativeBoundaries(), exc.function,
|
||||||
exc.msg)
|
exc.msg)
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ proc print*(exc: ParseError) =
|
||||||
var file = exc.file
|
var file = exc.file
|
||||||
if file notin ["<string>", ""]:
|
if file notin ["<string>", ""]:
|
||||||
file = relativePath(exc.file, getCurrentDir())
|
file = relativePath(exc.file, getCurrentDir())
|
||||||
printError(file, exc.parser.getSource().splitLines()[exc.line - 1].strip(chars={'\n'}),
|
printError(file, exc.parser.getSource().strip(chars={'\n'}).splitLines()[exc.line - 1],
|
||||||
exc.line, exc.token.relPos, exc.parser.getCurrentFunction(),
|
exc.line, exc.token.relPos, exc.parser.getCurrentFunction(),
|
||||||
exc.msg)
|
exc.msg)
|
||||||
|
|
||||||
|
@ -73,6 +73,6 @@ proc print*(exc: LexingError) =
|
||||||
var file = exc.file
|
var file = exc.file
|
||||||
if file notin ["<string>", ""]:
|
if file notin ["<string>", ""]:
|
||||||
file = relativePath(exc.file, getCurrentDir())
|
file = relativePath(exc.file, getCurrentDir())
|
||||||
printError(file, exc.lexer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'}),
|
printError(file, exc.lexer.getSource().strip(chars={'\n'}).splitLines()[exc.line - 1],
|
||||||
exc.line, exc.pos, nil, exc.msg)
|
exc.line, exc.pos, nil, exc.msg)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue