Updates to README. Cleanup & refactoring
This commit is contained in:
parent
5fa463d9b4
commit
c1a2c9bc55
|
@ -5,6 +5,11 @@ Peon is a modern, multi-paradigm, async-first programming language with a focus
|
|||
[Go to the Manual](docs/manual.md)
|
||||
|
||||
|
||||
## DISCLAIMER
|
||||
|
||||
Peon is currently in the process of being rewritten, but the current design just doesn't allow for that. Hence, a [new repository](https://git.nocturn9x.space/nocturn9x/peon-rewrite)
|
||||
has been created, to start from a blank slate and take what I learned from Peon 0.1.x and make it significantly better
|
||||
|
||||
## What's peon?
|
||||
|
||||
__Note__: For simplicity reasons, the verbs in this section refer to the present even though part of what's described here is not implemented yet.
|
||||
|
@ -123,4 +128,4 @@ out for yourself. Fortunately, the process is quite straightforward:
|
|||
peon. Hopefully I will automate this soon, but as of right now the work is all manual
|
||||
|
||||
|
||||
__Note__: On Linux, peon will also look into `~/.local/peon/stdlib` by default, so you can just create the `~/.local/peon` folder and copy `src/peon/stdlib` there
|
||||
__Note__: On Linux, peon will also look into `~/.local/peon/stdlib` by default, so you can just create the `~/.local/peon` folder and copy `src/peon/stdlib` there
|
||||
|
|
|
@ -26,8 +26,8 @@ def main() -> int:
|
|||
try:
|
||||
cmd = f"nim {NIM_FLAGS} r src/main.nim {test_file} {PEON_FLAGS}"
|
||||
out = subprocess.run(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out.check_returncode()
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"An error occurred while executing test -> {type(e).__name__}: {e}")
|
||||
failed.add(test_file)
|
||||
continue
|
||||
if not all(map(lambda s: s == b"true", out.stdout.splitlines())):
|
||||
|
|
|
@ -32,7 +32,6 @@ import std/sets
|
|||
import std/monotimes
|
||||
|
||||
when debugVM or debugMem or debugGC or debugAlloc:
|
||||
import std/sequtils
|
||||
import std/terminal
|
||||
|
||||
|
||||
|
@ -227,17 +226,15 @@ proc markRoots(self: var PeonVM): HashSet[ptr HeapObject] =
|
|||
# the object to be reachable (potentially leading to a nasty
|
||||
# memory leak). Hopefully, in a 64-bit address space, this
|
||||
# occurrence is rare enough for us to ignore
|
||||
var result = initHashSet[uint64](self.gc.pointers.len())
|
||||
result = initHashSet[ptr HeapObject](self.gc.pointers.len())
|
||||
for obj in self.calls:
|
||||
if obj in self.gc.pointers:
|
||||
result.incl(obj)
|
||||
result.incl(cast[ptr HeapObject](obj))
|
||||
for obj in self.operands:
|
||||
if obj in self.gc.pointers:
|
||||
result.incl(obj)
|
||||
var obj: ptr HeapObject
|
||||
result.incl(cast[ptr HeapObject](obj))
|
||||
for p in result:
|
||||
obj = cast[ptr HeapObject](p)
|
||||
if obj.mark():
|
||||
if p.mark():
|
||||
when debugMarkGC:
|
||||
echo &"DEBUG - GC: Marked object: {obj[]}"
|
||||
when debugGC:
|
||||
|
@ -842,17 +839,9 @@ proc dispatch*(self: var PeonVM) {.inline.} =
|
|||
# Pops a value off the operand stack
|
||||
discard self.pop()
|
||||
of PushC:
|
||||
# Pushes a value from the operand stack
|
||||
# onto the call stack
|
||||
# Pops a value off the operand stack
|
||||
# and pushes it onto the call stack
|
||||
self.pushc(self.pop())
|
||||
of PopRepl:
|
||||
# Pops a peon object off the
|
||||
# operand stack and prints it.
|
||||
# Used in interactive REPL mode
|
||||
if self.frames.len() !> 1:
|
||||
discard self.pop()
|
||||
continue
|
||||
echo self.pop()
|
||||
of PopN:
|
||||
# Pops N elements off the call stack
|
||||
for _ in 0..<int(self.readShort()):
|
||||
|
@ -1003,7 +992,10 @@ proc dispatch*(self: var PeonVM) {.inline.} =
|
|||
of Float32LessOrEqual:
|
||||
self.push(self.getBool(cast[float32](self.pop()) <= cast[float32](self.pop())))
|
||||
of Identity:
|
||||
# Identity is implemented simply as pointer equality :)
|
||||
self.push(cast[uint64](self.pop() == self.pop()))
|
||||
of LogicalNot:
|
||||
self.push(uint64(not self.pop().bool))
|
||||
# Print opcodes
|
||||
of PrintInt64:
|
||||
echo cast[int64](self.pop())
|
||||
|
@ -1033,7 +1025,7 @@ proc dispatch*(self: var PeonVM) {.inline.} =
|
|||
else:
|
||||
echo "false"
|
||||
of PrintInf:
|
||||
if self.pop() == 0x3:
|
||||
if self.pop() == self.getInf(positive=true):
|
||||
echo "inf"
|
||||
else:
|
||||
echo "-inf"
|
||||
|
@ -1046,8 +1038,6 @@ proc dispatch*(self: var PeonVM) {.inline.} =
|
|||
stdout.write("\n")
|
||||
of SysClock64:
|
||||
self.push(cast[uint64](getMonoTime().ticks.float() / 1_000_000_000))
|
||||
of LogicalNot:
|
||||
self.push(uint64(not self.pop().bool))
|
||||
else:
|
||||
discard
|
||||
|
||||
|
|
|
@ -16,10 +16,8 @@
|
|||
import std/tables
|
||||
import std/strformat
|
||||
import std/algorithm
|
||||
import std/parseutils
|
||||
import std/strutils
|
||||
import std/sequtils
|
||||
import std/sets
|
||||
import std/os
|
||||
import std/terminal
|
||||
import std/hashes
|
||||
|
@ -54,7 +52,8 @@ type
|
|||
UInt32, Int64, UInt64, Float32, Float64,
|
||||
Char, Byte, String, Function, CustomType,
|
||||
Nil, Nan, Bool, Inf, Typevar, Generic,
|
||||
Reference, Pointer, Any, All, Union, Auto
|
||||
Reference, Pointer, Any, All, Union, Auto,
|
||||
Array
|
||||
|
||||
Type* = ref object
|
||||
## A wrapper around
|
||||
|
@ -79,17 +78,22 @@ type
|
|||
compiled*: bool
|
||||
of CustomType:
|
||||
fields*: TableRef[string, Type]
|
||||
decl*: TypeDecl
|
||||
of Array:
|
||||
elemType*: Type
|
||||
size*: int
|
||||
elements*: seq[Expression]
|
||||
of Reference, Pointer:
|
||||
value*: Type
|
||||
of Generic:
|
||||
# cond represents a type constraint. For
|
||||
# example, fn foo[T*: int & ~uint](...) {...}
|
||||
# would map to [(true, int), (false, uint)]
|
||||
cond*: seq[tuple[match: bool, kind: Type]]
|
||||
cond*: seq[tuple[match: bool, kind: Type, value: LiteralExpr]]
|
||||
asUnion*: bool # If this is true, the constraint is treated like a type union
|
||||
name*: string
|
||||
of Union:
|
||||
types*: seq[tuple[match: bool, kind: Type]]
|
||||
types*: seq[tuple[match: bool, kind: Type, value: LiteralExpr]]
|
||||
of Typevar:
|
||||
# What type do we represent?
|
||||
wrapped*: Type
|
||||
|
@ -100,7 +104,7 @@ type
|
|||
## A name enumeration type
|
||||
None, Module, Argument, Var, Function, CustomType, Enum
|
||||
|
||||
Name* = ref object
|
||||
Name* = ref object of RootObj
|
||||
## A generic name object
|
||||
|
||||
# Type of the identifier (NOT of the value!)
|
||||
|
@ -192,8 +196,6 @@ type
|
|||
# The current scope depth. If > 0, we're
|
||||
# in a local scope, otherwise it's global
|
||||
depth*: int
|
||||
# Are we in REPL mode?
|
||||
replMode*: bool
|
||||
# List of all compile-time names
|
||||
names*: seq[Name]
|
||||
# Stores line data for error reporting
|
||||
|
@ -222,12 +224,6 @@ type
|
|||
# Currently imported modules
|
||||
modules*: TableRef[string, Name]
|
||||
|
||||
TypedNode* = ref object
|
||||
## A wapper for AST nodes
|
||||
## with attached type information
|
||||
kind*: Type
|
||||
node*: ASTNode
|
||||
|
||||
|
||||
|
||||
## Public getters for nicer error formatting
|
||||
|
@ -235,19 +231,16 @@ proc getCurrentNode*(self: Compiler): ASTNode = (if self.current >= self.ast.len
|
|||
proc getCurrentFunction*(self: Compiler): Declaration {.inline.} = (if self.currentFunction.isNil(): nil else: self.currentFunction.valueType.fun)
|
||||
proc getSource*(self: Compiler): string {.inline.} = self.source
|
||||
|
||||
## Some forward declarations (some of them arere actually stubs because nim forces forward declarations to be
|
||||
## implemented in the same module). They are methods because we need to dispatch to their actual specific
|
||||
## Some forward declarations (some of them are actually stubs because nim forces forward declarations to be
|
||||
## implemented in the same module). Some of them 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
|
||||
## 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 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 getItemExpr*(self: Compiler, node: GetItemExpr, compile: bool = true, matching: Type = nil): Type {.discardable, base.} = nil
|
||||
method unary*(self: Compiler, node: UnaryExpr, compile: bool = true): Type {.discardable, base.} = nil
|
||||
method binary*(self: Compiler, node: BinaryExpr, compile: bool = true): Type {.discardable, base.} = nil
|
||||
method lambdaExpr*(self: Compiler, node: LambdaExpr, compile: bool = true): Type {.discardable, base.} = nil
|
||||
method literal*(self: Compiler, node: ASTNode, compile: bool = true): Type {.discardable, base.} = nil
|
||||
method prepareFunction*(self: Compiler, name: Name) {.base.} = discard
|
||||
method dispatchPragmas(self: Compiler, name: Name) {.base.} = discard
|
||||
method dispatchDelayedPragmas(self: Compiler, name: Name) {.base.} = discard
|
||||
# These are not methods because their behavior is shared across backends and does
|
||||
# not need to change
|
||||
proc infer*(self: Compiler, node: LiteralExpr): Type
|
||||
proc infer*(self: Compiler, node: Expression): Type
|
||||
proc inferOrError*(self: Compiler, node: Expression): Type
|
||||
|
@ -256,9 +249,9 @@ proc findInModule*(self: Compiler, name: string, module: Name): seq[Name]
|
|||
proc findByType*(self: Compiler, name: string, kind: Type): seq[Name]
|
||||
proc compare*(self: Compiler, a, b: Type): bool
|
||||
proc match*(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allowFwd: bool = true): Name
|
||||
method prepareFunction*(self: Compiler, name: Name) {.base.} = discard
|
||||
method dispatchPragmas(self: Compiler, name: Name) {.base.} = discard
|
||||
method dispatchDelayedPragmas(self: Compiler, name: Name) {.base.} = discard
|
||||
proc resolve*(self: Compiler, name: string): Name
|
||||
proc resolve*(self: Compiler, name: IdentExpr): Name
|
||||
proc resolveOrError*[T: IdentExpr | string](self: Compiler, name: T): Name
|
||||
## End of forward declarations
|
||||
|
||||
## Utility functions
|
||||
|
@ -294,7 +287,7 @@ proc error*(self: Compiler, message: string, node: ASTNode = nil) {.inline.} =
|
|||
|
||||
proc warning*(self: Compiler, kind: WarningKind, message: string, name: Name = nil, node: ASTNode = nil) =
|
||||
## Raises a warning. Note that warnings are always disabled in REPL mode
|
||||
if self.replMode or kind in self.disabledWarnings:
|
||||
if kind in self.disabledWarnings:
|
||||
return
|
||||
var node: ASTNode = node
|
||||
var fn: Declaration
|
||||
|
@ -354,7 +347,7 @@ proc wrap*(self: Type): Type =
|
|||
return self
|
||||
|
||||
|
||||
proc unwrap*(self: Type): Type =
|
||||
proc unwrap*(self: Type): Type {.inline.} =
|
||||
## Unwraps a typevar if it's not already
|
||||
## unwrapped
|
||||
if self.kind == Typevar:
|
||||
|
@ -417,7 +410,7 @@ proc resolveOrError*[T: IdentExpr | string](self: Compiler, name: T): Name =
|
|||
self.error(&"reference to undefined name '{name}'")
|
||||
|
||||
|
||||
proc compareUnions*(self: Compiler, a, b: seq[tuple[match: bool, kind: Type]]): bool =
|
||||
proc compareUnions(self: Compiler, a, b: seq[tuple[match: bool, kind: Type, value: LiteralExpr]]): bool =
|
||||
## Compares type unions between each other
|
||||
var
|
||||
long = a
|
||||
|
@ -449,7 +442,7 @@ proc compare*(self: Compiler, a, b: Type): bool =
|
|||
return b.isNil() or b.kind == All
|
||||
if b.isNil():
|
||||
return a.isNil() or a.kind == All
|
||||
if a.kind == All or b.kind == All:
|
||||
if a.kind in [All, Auto] or b.kind in [All, Auto]:
|
||||
return true
|
||||
if a.kind == b.kind:
|
||||
# Here we compare types with the same kind discriminant
|
||||
|
@ -459,7 +452,11 @@ proc compare*(self: Compiler, a, b: Type): bool =
|
|||
Char, Byte, String, Nil, TypeKind.Nan, Bool,
|
||||
TypeKind.Inf, Any, Auto:
|
||||
return true
|
||||
of Array:
|
||||
return self.compare(a.elemType, b.elemType) and a.size == b.size
|
||||
of Typevar:
|
||||
if a.wrapped.isNil() or b.wrapped.isNil():
|
||||
return true
|
||||
return self.compare(a.wrapped, b.wrapped)
|
||||
of Union:
|
||||
return self.compareUnions(a.types, b.types)
|
||||
|
@ -502,10 +499,6 @@ proc compare*(self: Compiler, a, b: Type): bool =
|
|||
return true
|
||||
else:
|
||||
discard # TODO: Custom types, enums
|
||||
if a.kind == Typevar:
|
||||
return self.compare(a.wrapped, b)
|
||||
if b.kind == Typevar:
|
||||
return self.compare(a, b.wrapped)
|
||||
if a.kind == Union:
|
||||
for constraint in a.types:
|
||||
if self.compare(constraint.kind, b) and constraint.match:
|
||||
|
@ -589,9 +582,11 @@ proc toIntrinsic*(name: string): Type =
|
|||
elif name == "bool":
|
||||
return Type(kind: Bool, isBuiltin: true)
|
||||
elif name == "typevar":
|
||||
return Type(kind: Typevar, isBuiltin: true)
|
||||
return Type(kind: Typevar, isBuiltin: true, wrapped: Type(kind: Any))
|
||||
elif name == "string":
|
||||
return Type(kind: String, isBuiltin: true)
|
||||
elif name == "array":
|
||||
return Type(kind: Array, isBuiltin: true, elements: @[])
|
||||
|
||||
|
||||
proc infer*(self: Compiler, node: LiteralExpr): Type =
|
||||
|
@ -632,33 +627,19 @@ proc infer*(self: Compiler, node: Expression): Type =
|
|||
## returns it
|
||||
if node.isNil():
|
||||
return nil
|
||||
if node.isConst():
|
||||
return self.infer(LiteralExpr(node))
|
||||
case node.kind:
|
||||
of NodeKind.genericExpr:
|
||||
result = self.makeConcrete(GenericExpr(node), compile=false)
|
||||
of NodeKind.identExpr:
|
||||
result = self.identifier(IdentExpr(node), compile=false, strict=false)
|
||||
of NodeKind.unaryExpr:
|
||||
result = self.unary(UnaryExpr(node), compile=false)
|
||||
of NodeKind.binaryExpr:
|
||||
result = self.binary(BinaryExpr(node), compile=false)
|
||||
of {NodeKind.intExpr, NodeKind.hexExpr, NodeKind.binExpr, NodeKind.octExpr,
|
||||
NodeKind.strExpr, NodeKind.falseExpr, NodeKind.trueExpr, NodeKind.floatExpr
|
||||
}:
|
||||
result = self.infer(LiteralExpr(node))
|
||||
of NodeKind.callExpr:
|
||||
result = self.call(CallExpr(node), compile=false)
|
||||
of NodeKind.refExpr:
|
||||
result = Type(kind: Reference, value: self.infer(Ref(node).value))
|
||||
of NodeKind.ptrExpr:
|
||||
result = Type(kind: Pointer, value: self.infer(Ptr(node).value))
|
||||
of NodeKind.groupingExpr:
|
||||
result = self.infer(GroupingExpr(node).expression)
|
||||
of NodeKind.getItemExpr:
|
||||
result = self.getItemExpr(GetItemExpr(node), compile=false)
|
||||
of NodeKind.lambdaExpr:
|
||||
result = self.lambdaExpr(LambdaExpr(node), compile=false)
|
||||
else:
|
||||
discard # TODO
|
||||
# For most cases we can just dispatch to the target
|
||||
# compiler which will tell us the type of the value
|
||||
result = self.expression(node, compile=false)
|
||||
|
||||
|
||||
proc inferOrError*(self: Compiler, node: Expression): Type =
|
||||
|
@ -682,11 +663,22 @@ proc stringify*(self: Compiler, typ: Type): string =
|
|||
TypeKind.Inf, Auto, Any:
|
||||
result &= ($typ.kind).toLowerAscii()
|
||||
of Typevar:
|
||||
result = self.stringify(typ.wrapped)
|
||||
result = &"typevar[{self.stringify(typ.wrapped)}]"
|
||||
of Pointer:
|
||||
result &= &"ptr {self.stringify(typ.value)}"
|
||||
of Reference:
|
||||
result &= &"ref {self.stringify(typ.value)}"
|
||||
of CustomType:
|
||||
result &= &"{typ.decl.name.token.lexeme}"
|
||||
if typ.decl.generics.len() > 0:
|
||||
result &= "["
|
||||
for i, gen in typ.decl.generics:
|
||||
result &= &"{gen.name.token.lexeme}: {self.stringify(self.inferOrError(gen.cond))}"
|
||||
if i < typ.decl.generics.len() - 1:
|
||||
result &= ", "
|
||||
result &= "]"
|
||||
of Array:
|
||||
result &= &"array[{self.stringify(typ.elemType)}, {typ.size}]"
|
||||
of Function:
|
||||
result &= "fn "
|
||||
if typ.fun.generics.len() > 0:
|
||||
|
@ -746,6 +738,8 @@ proc findByName*(self: Compiler, name: string): seq[Name] =
|
|||
## Looks for objects that have been already declared
|
||||
## with the given name. Returns all objects that apply.
|
||||
for obj in reversed(self.names):
|
||||
if obj.depth > self.depth:
|
||||
continue
|
||||
if obj.ident.token.lexeme == name:
|
||||
if obj.owner.absPath != self.currentModule.absPath:
|
||||
if obj.isPrivate or self.currentModule notin obj.exportedTo:
|
||||
|
@ -782,15 +776,6 @@ proc findByType*(self: Compiler, name: string, kind: Type): seq[Name] =
|
|||
result.add(obj)
|
||||
|
||||
|
||||
proc findAtDepth*(self: Compiler, name: string, depth: int): seq[Name] {.used.} =
|
||||
## Looks for objects that have been already declared
|
||||
## with the given name at the given scope depth.
|
||||
## Returns all objects that apply
|
||||
for obj in self.findByName(name):
|
||||
if obj.depth == depth:
|
||||
result.add(obj)
|
||||
|
||||
|
||||
proc check*(self: Compiler, term: Expression, kind: Type) {.inline.} =
|
||||
## Checks the type of term against a known type.
|
||||
## Raises an error if appropriate and returns
|
||||
|
@ -867,10 +852,11 @@ proc match*(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allow
|
|||
else:
|
||||
msg &= " (compile with --showMismatches for more details)"
|
||||
self.error(msg, node)
|
||||
# This is only true when we're called by self.patchForwardDeclarations()
|
||||
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)}'")
|
||||
result = impl[0]
|
||||
result.valueType = result.valueType.unwrap()
|
||||
# This is only true when we're called by self.patchForwardDeclarations()
|
||||
if result.valueType.forwarded and not allowFwd:
|
||||
self.error(&"expecting an implementation for '{result.ident.token.lexeme}' declared in '{result.owner.ident.token.lexeme}' at line {result.ident.token.line} of type '{self.stringify(result.valueType)}'")
|
||||
result.resolved = true
|
||||
if result.kind == NameKind.Var and not result.valueType.nameObj.isNil():
|
||||
# We found a function bound to a variable,
|
||||
|
@ -887,34 +873,37 @@ proc beginScope*(self: Compiler) =
|
|||
inc(self.depth)
|
||||
|
||||
|
||||
proc unpackTypes*(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, value: LiteralExpr]], accept: bool = true) =
|
||||
## Recursively unpacks a type constraint
|
||||
case condition.kind:
|
||||
of identExpr:
|
||||
var typ = self.inferOrError(condition)
|
||||
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)
|
||||
list.add((accept, typ))
|
||||
of binaryExpr:
|
||||
let condition = BinaryExpr(condition)
|
||||
case condition.operator.lexeme:
|
||||
of "|":
|
||||
self.unpackTypes(condition.a, list)
|
||||
self.unpackTypes(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.unpackTypes(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)
|
||||
if condition.isConst():
|
||||
list.add((accept, self.inferOrError(condition), LiteralExpr(condition)))
|
||||
else:
|
||||
case condition.kind:
|
||||
of identExpr:
|
||||
var typ = self.inferOrError(condition)
|
||||
if typ.kind != Typevar:
|
||||
self.error(&"expecting a type name, got value of type {self.stringify(typ)} instead", condition)
|
||||
typ = typ.unwrap()
|
||||
if typ.kind == Auto:
|
||||
self.error("automatic types cannot be used within generics", condition)
|
||||
list.add((accept, typ, nil))
|
||||
of binaryExpr:
|
||||
let condition = BinaryExpr(condition)
|
||||
case condition.operator.lexeme:
|
||||
of "|":
|
||||
self.unpackTypes(condition.a, list)
|
||||
self.unpackTypes(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.unpackTypes(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 declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||
|
@ -1010,10 +999,13 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
|||
isPrivate: node.isPrivate,
|
||||
isReal: true,
|
||||
belongsTo: self.currentFunction,
|
||||
valueType: Type(kind: CustomType)
|
||||
valueType: Type(kind: CustomType,
|
||||
decl: node)
|
||||
)
|
||||
)
|
||||
n = self.names[^1]
|
||||
if node.generics.len() > 0:
|
||||
n.isGeneric = true
|
||||
declaredName = node.name.token.lexeme
|
||||
if node.value.isNil():
|
||||
discard # TODO: Fields
|
||||
|
|
|
@ -14,10 +14,6 @@
|
|||
|
||||
## Low level bytecode implementation details
|
||||
|
||||
import std/strutils
|
||||
import std/strformat
|
||||
|
||||
|
||||
import util/multibyte
|
||||
|
||||
|
||||
|
@ -69,13 +65,10 @@ type
|
|||
# to unary opcodes, while a and b
|
||||
# represent arguments to binary
|
||||
# opcodes. Other variable names (c, d, ...)
|
||||
# may be used for more complex opcodes. If
|
||||
# an opcode takes any arguments at runtime,
|
||||
# they come from either the stack or the VM's
|
||||
# closure array. Some other opcodes (e.g.
|
||||
# jumps), take arguments in the form of 16
|
||||
# or 24 bit numbers that are defined statically
|
||||
# at compilation time into the bytecode
|
||||
# may be used for more complex opcodes.
|
||||
# Some opcodes (e.g. jumps), take arguments in
|
||||
# the form of 16 or 24 bit numbers that are defined
|
||||
# statically at compilation time into the bytecode
|
||||
|
||||
# These push a constant at position x in the
|
||||
# constant table onto the stack
|
||||
|
@ -165,8 +158,7 @@ type
|
|||
PrintInf,
|
||||
PrintString,
|
||||
## Basic stack operations
|
||||
Pop, # Pops an element off the stack and discards it
|
||||
PopRepl, # Same as Pop, but also prints the value of what's popped (used in REPL mode)
|
||||
Pop, # Pops an element off the operand stack and discards it
|
||||
PopN, # Pops x elements off the call stack (optimization for exiting local scopes which usually pop many elements)
|
||||
## Name resolution/handling
|
||||
LoadAttribute, # Pushes the attribute b of object a onto the stack
|
||||
|
@ -196,8 +188,8 @@ type
|
|||
## Misc
|
||||
Assert, # Raises an exception if x is false
|
||||
NoOp, # Just a no-op
|
||||
PopC, # Pop off the call stack onto the operand stack
|
||||
PushC, # Pop off the operand stack onto the call stack
|
||||
PopC, # Pop a value off the call stack and discard it
|
||||
PushC, # Pop a value off the operand stack and push it onto the call stack
|
||||
SysClock64, # Pushes the output of a monotonic clock on the 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
|
||||
|
|
|
@ -19,7 +19,6 @@ import std/algorithm
|
|||
import std/parseutils
|
||||
import std/strutils
|
||||
import std/sequtils
|
||||
import std/sets
|
||||
import std/os
|
||||
|
||||
|
||||
|
@ -80,12 +79,17 @@ type
|
|||
compilerProcs: TableRef[string, CompilerFunc]
|
||||
# Stores the position of all jumps
|
||||
jumps: seq[tuple[patched: bool, offset: int]]
|
||||
# Metadata about function locations
|
||||
# Metadata regarding function locations (used to construct
|
||||
# the debugging fields in the resulting bytecode)
|
||||
functions: seq[tuple[start, stop, pos: int, fn: Name]]
|
||||
# Metadata regarding forward declarations
|
||||
forwarded: seq[tuple[name: Name, pos: int]]
|
||||
# The topmost occupied stack slot
|
||||
# in the current frame (0-indexed)
|
||||
stackIndex: int
|
||||
# All the lambdas we encountered (to know
|
||||
# if we should compile them once we call
|
||||
# them)
|
||||
lambdas: seq[LambdaExpr]
|
||||
|
||||
|
||||
|
@ -106,10 +110,13 @@ method dispatchDelayedPragmas(self: BytecodeCompiler, name: Name)
|
|||
proc funDecl(self: BytecodeCompiler, node: FunDecl, name: Name)
|
||||
proc compileModule(self: BytecodeCompiler, module: Name)
|
||||
proc generateCall(self: BytecodeCompiler, fn: Name, args: seq[Expression], line: int)
|
||||
proc identifier(self: BytecodeCompiler, node: IdentExpr, name: Name = nil, compile: bool = true, strict: bool = true): Type {.discardable.}
|
||||
proc lambdaExpr(self: BytecodeCompiler, node: LambdaExpr, compile: bool = true): Type {.discardable.}
|
||||
proc getItemExpr(self: BytecodeCompiler, node: GetItemExpr, compile: bool = true, matching: Type = nil): Type {.discardable.}
|
||||
# End of forward declarations
|
||||
|
||||
|
||||
proc newBytecodeCompiler*(replMode: bool = false): BytecodeCompiler =
|
||||
proc newBytecodeCompiler*: BytecodeCompiler =
|
||||
## Initializes a new BytecodeCompiler object
|
||||
new(result)
|
||||
result.ast = @[]
|
||||
|
@ -122,7 +129,6 @@ proc newBytecodeCompiler*(replMode: bool = false): BytecodeCompiler =
|
|||
result.modules = newTable[string, Name]()
|
||||
result.lambdas = @[]
|
||||
result.currentFunction = nil
|
||||
result.replMode = replMode
|
||||
result.currentModule = nil
|
||||
result.compilerProcs = newTable[string, CompilerFunc]()
|
||||
result.compilerProcs["magic"] = CompilerFunc(kind: Immediate, handler: handleMagicPragma)
|
||||
|
@ -153,7 +159,7 @@ proc emitBytes(self: BytecodeCompiler, bytarr: openarray[OpCode | uint8], line:
|
|||
for b in bytarr:
|
||||
self.emitByte(b, line)
|
||||
|
||||
|
||||
#[
|
||||
proc printRepl(self: BytecodeCompiler, typ: Type, node: Expression) =
|
||||
## Emits instruction to print
|
||||
## peon types in REPL mode
|
||||
|
@ -188,7 +194,7 @@ proc printRepl(self: BytecodeCompiler, typ: Type, node: Expression) =
|
|||
self.emitByte(PrintString, node.token.line)
|
||||
else:
|
||||
self.emitByte(PrintHex, node.token.line)
|
||||
|
||||
]#
|
||||
|
||||
proc makeConstant(self: BytecodeCompiler, val: Expression, typ: Type): array[3, uint8] =
|
||||
## Adds a constant to the current chunk's constant table
|
||||
|
@ -287,7 +293,7 @@ proc emitJump(self: BytecodeCompiler, opcode: OpCode, line: int): int =
|
|||
self.emitBytes(0.toTriple(), line)
|
||||
result = self.jumps.high()
|
||||
|
||||
|
||||
#[
|
||||
proc fixFunctionOffsets(self: BytecodeCompiler, where, oldLen: int) =
|
||||
## Fixes function offsets after the size of our
|
||||
## bytecode has changed
|
||||
|
@ -384,7 +390,7 @@ proc insertAt(self: BytecodeCompiler, where: int, opcode: OpCode, data: openarra
|
|||
self.fixLines(where, self.chunk.code.len() - oldLen, true)
|
||||
self.fixNames(where, oldLen)
|
||||
self.fixFunctionOffsets(oldLen, where)
|
||||
|
||||
]#
|
||||
|
||||
|
||||
proc patchJump(self: BytecodeCompiler, offset: int) =
|
||||
|
@ -463,7 +469,7 @@ proc handleBuiltinFunction(self: BytecodeCompiler, fn: Type, args: seq[Expressio
|
|||
"LogicalNot": LogicalNot,
|
||||
"NegInf": LoadNInf,
|
||||
"Identity": Identity
|
||||
}.to_table()
|
||||
}.toTable()
|
||||
if fn.builtinOp == "print":
|
||||
var typ = self.inferOrError(args[0]).unwrap()
|
||||
case typ.kind:
|
||||
|
@ -576,8 +582,6 @@ proc endScope(self: BytecodeCompiler) =
|
|||
var names: seq[Name] = @[]
|
||||
var popCount = 0
|
||||
for name in self.names:
|
||||
if self.replMode and name.depth == 0:
|
||||
continue
|
||||
# We only pop names in scopes deeper than ours
|
||||
if name.depth > self.depth:
|
||||
if name.depth == 0 and not self.isMainModule:
|
||||
|
@ -688,8 +692,7 @@ proc handleMagicPragma(self: BytecodeCompiler, pragma: Pragma, name: Name) =
|
|||
if name.valueType.kind == All:
|
||||
self.error("don't even think about it (compiler-chan is angry at you :/)", pragma)
|
||||
if name.valueType.isNil():
|
||||
self.error("'magic' pragma: wrong argument value", pragma.args[0])
|
||||
name.valueType.isBuiltin = true
|
||||
self.error("'magic' pragma: wrong argument value", pragma.args[0])
|
||||
else:
|
||||
self.error("'magic' pragma is not valid in this context")
|
||||
|
||||
|
@ -799,7 +802,7 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
|||
## its arguments and typechecking it
|
||||
|
||||
# First we declare the function's generics, if it has any
|
||||
var constraints: seq[tuple[match: bool, kind: Type]] = @[]
|
||||
var constraints: seq[tuple[match: bool, kind: Type, value: LiteralExpr]] = @[]
|
||||
for gen in fn.node.generics:
|
||||
self.unpackTypes(gen.cond, constraints)
|
||||
self.names.add(Name(depth: fn.depth + 1,
|
||||
|
@ -811,6 +814,7 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
|||
belongsTo: fn,
|
||||
ident: gen.name,
|
||||
owner: self.currentModule,
|
||||
kind: NameKind.CustomType,
|
||||
file: self.file))
|
||||
constraints = @[]
|
||||
# We now declare and typecheck the function's
|
||||
|
@ -825,14 +829,14 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
|||
if self.names.high() > 16777215:
|
||||
self.error("cannot declare more than 16777215 variables at a time")
|
||||
inc(self.stackIndex)
|
||||
typ = self.inferOrError(argument.valueType)
|
||||
typ = self.inferOrError(argument.valueType).unwrap()
|
||||
# We can't use self.compare(), because it would
|
||||
# always just return true
|
||||
if typ.kind == Auto:
|
||||
fn.valueType.isAuto = true
|
||||
# Magic trick! We turn auto into any, just
|
||||
# to make our lives easier
|
||||
typ = "any".toIntrinsic()
|
||||
#typ = "any".toIntrinsic()
|
||||
self.names.add(Name(depth: fn.depth + 1,
|
||||
isPrivate: true,
|
||||
owner: fn.owner,
|
||||
|
@ -858,7 +862,7 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
|||
fn.valueType.args.add((self.names[^1].ident.token.lexeme, typ, default))
|
||||
# The function needs a return type too!
|
||||
if not node.returnType.isNil():
|
||||
fn.valueType.returnType = self.inferOrError(node.returnType)
|
||||
fn.valueType.returnType = self.inferOrError(node.returnType).unwrap()
|
||||
if fn.valueType.returnType.kind == Auto:
|
||||
fn.valueType.isAuto = true
|
||||
# Here we don't bother changing the return type
|
||||
|
@ -1042,7 +1046,7 @@ proc beginProgram(self: BytecodeCompiler): int =
|
|||
self.emitBytes(0.toTriple(), 1)
|
||||
|
||||
|
||||
method literal(self: BytecodeCompiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
|
||||
proc literal(self: BytecodeCompiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
|
||||
## Emits instructions for literals such
|
||||
## as singletons, strings and numbers
|
||||
case node.kind:
|
||||
|
@ -1140,7 +1144,7 @@ method literal(self: BytecodeCompiler, node: ASTNode, compile: bool = true): Typ
|
|||
self.error(&"invalid AST node of kind {node.kind} at literal(): {node} (This is an internal error and most likely a bug!)")
|
||||
|
||||
|
||||
method unary(self: BytecodeCompiler, node: UnaryExpr, compile: bool = true): Type {.discardable.} =
|
||||
proc unary(self: BytecodeCompiler, node: UnaryExpr, compile: bool = true): Type {.discardable.} =
|
||||
## Compiles all unary expressions
|
||||
var default: Expression
|
||||
let fn = Type(kind: Function,
|
||||
|
@ -1158,7 +1162,7 @@ method unary(self: BytecodeCompiler, node: UnaryExpr, compile: bool = true): Typ
|
|||
self.generateCall(impl, @[node.a], impl.line)
|
||||
|
||||
|
||||
method binary(self: BytecodeCompiler, node: BinaryExpr, compile: bool = true): Type {.discardable.} =
|
||||
proc binary(self: BytecodeCompiler, node: BinaryExpr, compile: bool = true): Type {.discardable.} =
|
||||
## Compiles all binary expressions
|
||||
var default: Expression
|
||||
let fn = Type(kind: Function, returnType: "any".toIntrinsic(), args: @[("", self.inferOrError(node.a), default), ("", self.inferOrError(node.b), default)])
|
||||
|
@ -1174,7 +1178,7 @@ method binary(self: BytecodeCompiler, node: BinaryExpr, compile: bool = true): T
|
|||
self.generateCall(impl, @[node.a, node.b], impl.line)
|
||||
|
||||
|
||||
method identifier(self: BytecodeCompiler, node: IdentExpr, name: Name = nil, compile: bool = true, strict: bool = true): Type {.discardable.} =
|
||||
proc identifier(self: BytecodeCompiler, node: IdentExpr, name: Name = nil, compile: bool = true, strict: bool = true): Type {.discardable.} =
|
||||
## Compiles access to identifiers
|
||||
var s = name
|
||||
if s.isNil():
|
||||
|
@ -1223,7 +1227,7 @@ method identifier(self: BytecodeCompiler, node: IdentExpr, name: Name = nil, com
|
|||
self.emitBytes(s.position.toTriple(), s.ident.token.line)
|
||||
|
||||
|
||||
method assignment(self: BytecodeCompiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
|
||||
proc assignment(self: BytecodeCompiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
|
||||
## Compiles assignment expressions
|
||||
case node.kind:
|
||||
of assignExpr:
|
||||
|
@ -1253,50 +1257,52 @@ method assignment(self: BytecodeCompiler, node: ASTNode, compile: bool = true):
|
|||
elif r.isLet:
|
||||
self.error(&"cannot reassign '{name.token.lexeme}' (value is immutable)", name)
|
||||
if r.valueType.kind != CustomType:
|
||||
self.error("only types have fields", node)
|
||||
self.error(&"cannot set attributes on object of type {self.stringify(r.valueType)}", node)
|
||||
else:
|
||||
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 =
|
||||
proc 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)")
|
||||
self.error(&"cannot instantiate a concrete type from non-generic type {self.stringify(name.valueType)}")
|
||||
var decl = name.node
|
||||
if decl.generics.len() != node.args.len():
|
||||
self.error(&"wrong number of types supplied for generic instantiation (expected {decl.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]
|
||||
for i, (gen, value) in zip(decl.generics, node.args):
|
||||
self.check(value, self.inferOrError(gen.cond))
|
||||
types.add(self.inferOrError(value))
|
||||
if types[^1].kind != Typevar and not value.isConst():
|
||||
self.error(&"expecting type name or constant as generic parameter, got {self.stringify(types[^1])} instead", value)
|
||||
map[decl.generics[i].name.token.lexeme] = types[^1]
|
||||
case concrete.kind:
|
||||
of Array:
|
||||
concrete.size = parseInt(node.args[1].token.lexeme)
|
||||
concrete.elemType = map["T"]
|
||||
of Function:
|
||||
for i, argument in concrete.args:
|
||||
if argument.kind.kind != Generic:
|
||||
continue
|
||||
elif argument.kind.name in map:
|
||||
concrete.args[i].kind = map[argument.kind.name]
|
||||
else:
|
||||
self.error(&"unknown generic argument name '{argument.kind.name}'", FunDecl(concrete.fun).arguments[i].name)
|
||||
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)
|
||||
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
|
||||
discard
|
||||
result = Type(kind: Typevar, wrapped: concrete)
|
||||
|
||||
|
||||
method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type {.discardable.} =
|
||||
proc call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type {.discardable.} =
|
||||
## Compiles function calls
|
||||
var args: seq[tuple[name: string, kind: Type, default: Expression]] = @[]
|
||||
var argExpr: seq[Expression] = @[]
|
||||
|
@ -1354,6 +1360,7 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
|
|||
self.generateCall(result, argExpr, node.token.line)
|
||||
result = result.returnType
|
||||
of NodeKind.getItemExpr:
|
||||
# Calling stuff like a.b()
|
||||
let node = GetItemExpr(node.callee)
|
||||
result = self.getItemExpr(node, compile=false, matching=Type(kind: Function, args: args, returnType: Type(kind: All)))
|
||||
var fn: Name
|
||||
|
@ -1383,7 +1390,7 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
|
|||
let node = GenericExpr(node.callee)
|
||||
let concrete = self.makeConcrete(node)
|
||||
var impl = self.resolve(node.ident).deepCopy()
|
||||
impl.valueType = concrete
|
||||
impl.valueType = concrete.unwrap()
|
||||
result = impl.valueType.returnType
|
||||
if compile:
|
||||
self.generateCall(impl, argExpr, node.token.line)
|
||||
|
@ -1395,7 +1402,7 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
|
|||
self.error(&"object of type '{self.stringify(typ)}' is not callable", node)
|
||||
|
||||
|
||||
method getItemExpr(self: BytecodeCompiler, node: GetItemExpr, compile: bool = true, matching: Type = nil): Type {.discardable.} =
|
||||
proc getItemExpr(self: BytecodeCompiler, node: GetItemExpr, compile: bool = true, matching: Type = nil): Type {.discardable.} =
|
||||
## Compiles accessing to fields of a type or
|
||||
## module namespace. If the compile flag is set
|
||||
## to false, no code is generated for resolving
|
||||
|
@ -1445,7 +1452,7 @@ proc blockStmt(self: BytecodeCompiler, node: BlockStmt, compile: bool = true) =
|
|||
self.endScope()
|
||||
|
||||
|
||||
method lambdaExpr(self: BytecodeCompiler, node: LambdaExpr, compile: bool = true): Type {.discardable.} =
|
||||
proc lambdaExpr(self: BytecodeCompiler, node: LambdaExpr, compile: bool = true): Type {.discardable.} =
|
||||
## Compiles lambda functions as expressions
|
||||
result = Type(kind: Function, isLambda: true, fun: node, location: 0, compiled: true)
|
||||
let function = self.currentFunction
|
||||
|
@ -1554,8 +1561,10 @@ method lambdaExpr(self: BytecodeCompiler, node: LambdaExpr, compile: bool = true
|
|||
method expression(self: BytecodeCompiler, node: Expression, compile: bool = true): Type {.discardable.} =
|
||||
## Compiles all expressions
|
||||
case node.kind:
|
||||
of NodeKind.arrayExpr:
|
||||
discard # TODO
|
||||
of NodeKind.genericExpr:
|
||||
return self.makeConcrete(GenericExpr(node))
|
||||
return self.makeConcrete(GenericExpr(node), compile)
|
||||
of NodeKind.callExpr:
|
||||
return self.call(CallExpr(node), compile)
|
||||
of NodeKind.getItemExpr:
|
||||
|
@ -1573,12 +1582,12 @@ method expression(self: BytecodeCompiler, node: Expression, compile: bool = true
|
|||
of NodeKind.unaryExpr:
|
||||
# Unary expressions such as ~5 and -3
|
||||
return self.unary(UnaryExpr(node), compile)
|
||||
of NodeKind.binaryExpr:
|
||||
# Binary expressions such as 2 ^ 5 and 0.66 * 3.14
|
||||
return self.binary(BinaryExpr(node), compile)
|
||||
of NodeKind.groupingExpr:
|
||||
# Grouping expressions like (2 + 1)
|
||||
return self.expression(GroupingExpr(node).expression, compile)
|
||||
of NodeKind.binaryExpr:
|
||||
# Binary expressions such as 2 ^ 5 and 0.66 * 3.14
|
||||
return self.binary(BinaryExpr(node))
|
||||
of NodeKind.intExpr, NodeKind.hexExpr, NodeKind.binExpr, NodeKind.octExpr,
|
||||
NodeKind.strExpr, NodeKind.falseExpr, NodeKind.trueExpr, NodeKind.floatExpr:
|
||||
# Since all of these AST nodes share the
|
||||
|
@ -1851,8 +1860,6 @@ proc statement(self: BytecodeCompiler, node: Statement) =
|
|||
# The expression has no type and produces no value,
|
||||
# so we don't have to pop anything
|
||||
discard
|
||||
elif self.replMode:
|
||||
self.printRepl(kind, expression)
|
||||
else:
|
||||
self.emitByte(Pop, node.token.line)
|
||||
of NodeKind.switchStmt:
|
||||
|
@ -1906,7 +1913,6 @@ proc varDecl(self: BytecodeCompiler, node: VarDecl) =
|
|||
if node.value.isNil():
|
||||
# Variable has no value: the type declaration
|
||||
# takes over
|
||||
# TODO: Implement T.default()!
|
||||
if self.compare(typ, "auto".toIntrinsic()):
|
||||
self.error("automatic types require initialization", node)
|
||||
typ = self.inferOrError(node.valueType)
|
||||
|
@ -1915,15 +1921,17 @@ proc varDecl(self: BytecodeCompiler, node: VarDecl) =
|
|||
# the typevar and compare the wrapped type, which is not what we want
|
||||
if typ.kind != Typevar:
|
||||
self.error(&"expecting type name, got value of type {self.stringify(typ)} instead", node.name)
|
||||
# TODO: Implement T.default()!
|
||||
self.error(&"cannot compute default value for {self.stringify(typ)}: please provide one explicitly")
|
||||
elif node.valueType.isNil():
|
||||
# Variable has no type declaration: the type
|
||||
# of its value takes over
|
||||
typ = self.inferOrError(node.value)
|
||||
typ = self.inferOrError(node.value).unwrap()
|
||||
else:
|
||||
# Variable has both a type declaration and
|
||||
# a value: the value's type must match the
|
||||
# type declaration
|
||||
let expected = self.inferOrError(node.valueType)
|
||||
let expected = self.inferOrError(node.valueType).unwrap()
|
||||
if not self.compare(expected, "auto".toIntrinsic()):
|
||||
self.check(node.value, expected)
|
||||
# If this doesn't fail, then we're good
|
||||
|
@ -1932,7 +1940,7 @@ proc varDecl(self: BytecodeCompiler, node: VarDecl) =
|
|||
# Let the compiler infer the type (this
|
||||
# is the default behavior already, but
|
||||
# some users may prefer to be explicit!)
|
||||
typ = self.inferOrError(node.value)
|
||||
typ = self.inferOrError(node.value).unwrap()
|
||||
self.expression(node.value)
|
||||
self.emitByte(AddVar, node.token.line)
|
||||
inc(self.stackIndex)
|
||||
|
@ -2095,15 +2103,10 @@ proc compile*(self: BytecodeCompiler, ast: seq[Declaration], file: string, lines
|
|||
self.file = file
|
||||
self.depth = 0
|
||||
self.currentFunction = nil
|
||||
if self.replMode:
|
||||
self.ast &= ast
|
||||
self.source &= "\n" & source
|
||||
self.lines &= lines
|
||||
else:
|
||||
self.ast = ast
|
||||
self.current = 0
|
||||
self.lines = lines
|
||||
self.source = source
|
||||
self.ast = ast
|
||||
self.current = 0
|
||||
self.lines = lines
|
||||
self.source = source
|
||||
self.isMainModule = isMainModule
|
||||
self.disabledWarnings = disabledWarnings
|
||||
self.showMismatches = showMismatches
|
||||
|
@ -2154,8 +2157,6 @@ proc compileModule(self: BytecodeCompiler, module: Name) =
|
|||
let currentModule = self.currentModule
|
||||
let mainModule = self.isMainModule
|
||||
let parentModule = self.parentModule
|
||||
let replMode = self.replMode
|
||||
self.replMode = false
|
||||
# Set the current module to the new module
|
||||
# and the current module as the parent module:
|
||||
# this is needed for export statements
|
||||
|
@ -2189,7 +2190,6 @@ proc compileModule(self: BytecodeCompiler, module: Name) =
|
|||
self.currentModule = currentModule
|
||||
self.isMainModule = mainModule
|
||||
self.parentModule = parentModule
|
||||
self.replMode = replMode
|
||||
self.lines = lines
|
||||
self.source = src
|
||||
self.modules[module.absPath] = module
|
||||
|
|
|
@ -17,7 +17,6 @@ import multibyte
|
|||
|
||||
|
||||
import std/strformat
|
||||
import std/strutils
|
||||
import std/terminal
|
||||
|
||||
|
||||
|
@ -223,8 +222,6 @@ proc disassembleInstruction*(self: Debugger) =
|
|||
self.current += 1
|
||||
|
||||
|
||||
|
||||
|
||||
proc parseFunctions(self: Debugger) =
|
||||
## Parses function information in the chunk
|
||||
var
|
||||
|
|
|
@ -79,6 +79,7 @@ type
|
|||
pragmaExpr,
|
||||
refExpr,
|
||||
ptrExpr,
|
||||
arrayExpr,
|
||||
genericExpr,
|
||||
switchStmt
|
||||
|
||||
|
@ -160,7 +161,6 @@ type
|
|||
ident*: IdentExpr
|
||||
args*: seq[Expression]
|
||||
|
||||
|
||||
UnaryExpr* = ref object of Expression
|
||||
operator*: Token
|
||||
a*: Expression
|
||||
|
@ -171,6 +171,11 @@ type
|
|||
# inherit from that and add a second operand
|
||||
b*: Expression
|
||||
|
||||
ArrayExpr* = ref object of Expression
|
||||
elements*: seq[Expression]
|
||||
startToken*: Token
|
||||
endToken*: Token
|
||||
|
||||
YieldExpr* = ref object of Expression
|
||||
expression*: Expression
|
||||
|
||||
|
@ -335,6 +340,15 @@ proc newPragma*(name: IdentExpr, args: seq[LiteralExpr]): Pragma =
|
|||
result.token = name.token
|
||||
|
||||
|
||||
proc newArrayExpr*(elements: seq[Expression], startToken, endToken: Token): ArrayExpr =
|
||||
new(result)
|
||||
result.kind = arrayExpr
|
||||
result.elements = elements
|
||||
result.startToken = startToken
|
||||
result.endToken = endToken
|
||||
result.token = startToken
|
||||
|
||||
|
||||
proc newRefExpr*(expression: Expression, token: Token): Ref =
|
||||
new(result)
|
||||
result.kind = refExpr
|
||||
|
@ -801,6 +815,9 @@ proc `$`*(self: ASTNode): string =
|
|||
of genericExpr:
|
||||
var self = GenericExpr(self)
|
||||
result &= &"Generic(ident={self.ident}, args={self.args})"
|
||||
of arrayExpr:
|
||||
var self = ArrayExpr(self)
|
||||
result &= &"Array(elements={self.elements})"
|
||||
else:
|
||||
discard
|
||||
|
||||
|
@ -865,6 +882,9 @@ proc getRelativeBoundaries*(self: ASTNode): tuple[start, stop: int] =
|
|||
if self.args.len() > 0:
|
||||
stop = getRelativeBoundaries(self.args[^1]).stop
|
||||
result = (ident.start, stop)
|
||||
of arrayExpr:
|
||||
var self = ArrayExpr(self)
|
||||
result = (self.startToken.relPos.start, self.endToken.relPos.stop)
|
||||
else:
|
||||
result = (0, 0)
|
||||
|
|
@ -395,6 +395,16 @@ proc primary(self: Parser): Expression =
|
|||
discard self.step()
|
||||
result = newPtrExpr(self.expression(), self.peek(-1))
|
||||
result.file = self.file
|
||||
of TokenType.LeftBracket:
|
||||
# Array
|
||||
let start = self.step()
|
||||
var elems: seq[Expression] = @[]
|
||||
while not self.done() and not self.check(RightBracket):
|
||||
elems.add(self.expression())
|
||||
if not self.match(Comma):
|
||||
break
|
||||
self.expect(RightBracket, "missing ']' in array construction")
|
||||
result = newArrayExpr(elems, start, self.peek(-1))
|
||||
else:
|
||||
self.error("invalid syntax")
|
||||
|
||||
|
@ -439,8 +449,7 @@ proc parseGenericArgs(self: Parser): Expression =
|
|||
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))
|
||||
types.add(self.expression())
|
||||
if not self.match(Comma):
|
||||
break
|
||||
self.expect(RightBracket)
|
||||
|
@ -962,15 +971,14 @@ proc varDecl(self: Parser, isLet: bool = false,
|
|||
var name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
let isPrivate = not self.match("*")
|
||||
self.checkDecl(isPrivate)
|
||||
var valueType: IdentExpr
|
||||
var valueType: Expression
|
||||
var hasInit = false
|
||||
var pragmas: seq[Pragma] = @[]
|
||||
if self.match(":"):
|
||||
# We don't enforce it here because
|
||||
# the compiler may be able to infer
|
||||
# the type later!
|
||||
self.expect(Identifier, "expecting type name after ':'")
|
||||
valueType = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
valueType = self.parseOr()
|
||||
if self.match("="):
|
||||
hasInit = true
|
||||
value = self.expression()
|
||||
|
|
|
@ -146,6 +146,7 @@ proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints
|
|||
print(exc)
|
||||
except CompileError as exc:
|
||||
print(exc)
|
||||
#raise
|
||||
except SerializationError as exc:
|
||||
var file = exc.file
|
||||
if file notin ["<string>", ""]:
|
||||
|
|
|
@ -74,6 +74,10 @@ type typevar* = object {
|
|||
}
|
||||
|
||||
|
||||
type array*[T: any, S: int] = object {
|
||||
#pragma[magic: "array"]
|
||||
}
|
||||
|
||||
# Some convenience aliases
|
||||
type int* = int64;
|
||||
type float* = float64;
|
||||
|
|
|
@ -24,6 +24,6 @@ fn testGlobals*: bool {
|
|||
}
|
||||
|
||||
|
||||
fn cast*[T: any](x: any): T {
|
||||
fn cast*[T: typevar, D: any](x: T): D {
|
||||
#pragma[magic: "cast"]
|
||||
}
|
|
@ -5,9 +5,5 @@ import std;
|
|||
# as the type I'm telling you, trust me bro". There is no data conversion
|
||||
# occurring whatsoever! For that, use converters (once they're implemented LoL)
|
||||
|
||||
print(cast[int](2.0) == 4611686018427387904);
|
||||
print(cast[float](4611686018427387904) == 2.0);
|
||||
# If that strikes your fancy, you can do this:
|
||||
var x = int;
|
||||
var caster = cast[x];
|
||||
print(caster(2.0) == 4611686018427387904);
|
||||
print(cast[int, float](2.0) == 4611686018427387904);
|
||||
print(cast[float, int](4611686018427387904) == 2.0);
|
||||
|
|
Loading…
Reference in New Issue