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}'")
|
||||
|
||||
|
||||
proc dispatch*(self: var PeonVM) =
|
||||
proc dispatch*(self: var PeonVM) {.inline.} =
|
||||
## Main bytecode dispatch loop
|
||||
var instruction {.register.}: OpCode
|
||||
while true:
|
||||
|
@ -764,10 +764,6 @@ proc dispatch*(self: var PeonVM) =
|
|||
# not needed there anymore
|
||||
discard self.pop()
|
||||
discard self.pop()
|
||||
of ReplExit:
|
||||
# Preserves the VM's state for the next
|
||||
# execution. Used in the REPL
|
||||
return
|
||||
of Return:
|
||||
# Returns from a function.
|
||||
# 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) =
|
||||
## Resumes execution of the given chunk (which
|
||||
## may have changed since the last call to run()).
|
||||
## No other state mutation occurs and all stacks as
|
||||
## well as other metadata are left intact. This should
|
||||
## not be used directly unless you know what you're
|
||||
## doing, as incremental compilation support is very
|
||||
## experimental and highly unstable
|
||||
self.chunk = chunk
|
||||
## may have changed since the last call to run()). No other
|
||||
## state mutation occurs and all stacks as well as other
|
||||
## metadata are left intact. This should not be used directly
|
||||
## unless you know what you're doing, as incremental compilation
|
||||
## support is very experimental and highly unstable
|
||||
try:
|
||||
self.chunk = chunk
|
||||
when debugVM:
|
||||
if self.breakpoints == @[0'u64]:
|
||||
self.debugNext = true
|
||||
self.dispatch()
|
||||
except NilAccessDefect:
|
||||
stderr.writeLine("Memory Access Violation: SIGSEGV")
|
||||
|
|
|
@ -59,6 +59,9 @@ type
|
|||
Type* = ref object
|
||||
## A wrapper around
|
||||
## compile-time types
|
||||
|
||||
# Is this type a builtin?
|
||||
isBuiltin*: bool
|
||||
case kind*: TypeKind:
|
||||
of Function:
|
||||
isLambda*: bool
|
||||
|
@ -86,6 +89,9 @@ type
|
|||
name*: string
|
||||
of Union:
|
||||
types*: seq[tuple[match: bool, kind: Type]]
|
||||
of Typevar:
|
||||
# What type do we represent?
|
||||
wrapped*: Type
|
||||
else:
|
||||
discard
|
||||
|
||||
|
@ -141,8 +147,6 @@ type
|
|||
# Has the compiler generated this name internally or
|
||||
# does it come from user code?
|
||||
isReal*: bool
|
||||
# Is this name a builtin?
|
||||
isBuiltin*: bool
|
||||
|
||||
## 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
|
||||
## 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
|
||||
|
@ -258,7 +263,7 @@ method dispatchDelayedPragmas(self: Compiler, name: Name) {.base.} = discard
|
|||
## Utility functions
|
||||
|
||||
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()
|
||||
|
||||
|
||||
|
@ -421,20 +426,22 @@ method compare*(self: Compiler, a, b: Type): bool =
|
|||
# code in any way. It's used mostly for matching
|
||||
# function return types (at least until we don't
|
||||
# have return type inference) and it matches any
|
||||
# type, including nil
|
||||
# type, including nil (nim's nil, not our nil)
|
||||
if a.isNil():
|
||||
return b.isNil() or b.kind == All
|
||||
elif b.isNil():
|
||||
return a.isNil() or a.kind == All
|
||||
elif a.kind == All or b.kind == All:
|
||||
return true
|
||||
elif a.kind == b.kind:
|
||||
if a.kind == b.kind:
|
||||
# Here we compare types with the same kind discriminant
|
||||
case a.kind:
|
||||
of Int8, UInt8, Int16, UInt16, Int32,
|
||||
UInt32, Int64, UInt64, Float32, Float64,
|
||||
Char, Byte, String, Nil, TypeKind.Nan, Bool, TypeKind.Inf, Any:
|
||||
return true
|
||||
of Typevar:
|
||||
return self.compare(a.wrapped, b.wrapped)
|
||||
of Union:
|
||||
return self.compareUnions(a.types, b.types)
|
||||
of Generic:
|
||||
|
@ -455,36 +462,22 @@ method compare*(self: Compiler, a, b: Type): bool =
|
|||
return false
|
||||
var i = 0
|
||||
for (argA, argB) in zip(a.args, b.args):
|
||||
# When we compare functions with forward
|
||||
# declarations, or forward declarations
|
||||
# between each other, we need to be more
|
||||
# strict (as in: check argument names and
|
||||
# their default values, any pragma associated
|
||||
# with the function, and whether they are pure)
|
||||
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 argA.name == "":
|
||||
continue
|
||||
if argB.name == "":
|
||||
continue
|
||||
if argA.name != argB.name:
|
||||
return false
|
||||
if not self.compare(argA.kind, argB.kind):
|
||||
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
|
||||
else:
|
||||
discard # TODO: Custom types, enums
|
||||
|
@ -615,6 +608,8 @@ method infer*(self: Compiler, node: Expression): Type =
|
|||
if node.isNil():
|
||||
return nil
|
||||
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:
|
||||
|
@ -659,16 +654,30 @@ method stringify*(self: Compiler, typ: Type): string =
|
|||
of Int8, UInt8, Int16, UInt16, Int32,
|
||||
UInt32, Int64, UInt64, Float32, Float64,
|
||||
Char, Byte, String, Nil, TypeKind.Nan, Bool,
|
||||
TypeKind.Inf, Auto:
|
||||
TypeKind.Inf, Auto, Any:
|
||||
result &= ($typ.kind).toLowerAscii()
|
||||
of Typevar:
|
||||
result = self.stringify(typ.wrapped)
|
||||
of Pointer:
|
||||
result &= &"ptr {self.stringify(typ.value)}"
|
||||
of Reference:
|
||||
result &= &"ref {self.stringify(typ.value)}"
|
||||
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:
|
||||
result &= &"{argName}: {self.stringify(argType)}"
|
||||
result &= &"{argName}: "
|
||||
if argType.kind == Generic:
|
||||
result &= argType.name
|
||||
else:
|
||||
result &= self.stringify(argType)
|
||||
if not argDefault.isNil():
|
||||
result &= &" = {argDefault}"
|
||||
if i < typ.args.len() - 1:
|
||||
|
@ -690,8 +699,6 @@ method stringify*(self: Compiler, typ: Type): string =
|
|||
result &= ", "
|
||||
else:
|
||||
result &= "}"
|
||||
of Any:
|
||||
return "any"
|
||||
of Union:
|
||||
for i, condition in typ.types:
|
||||
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
|
||||
## public within the given module with the given name.
|
||||
## Returns all objects that apply. If the name is an
|
||||
## empty string, returns all objects within the given
|
||||
## module, regardless of whether they are exported to
|
||||
## the current one or not
|
||||
## empty string, returns all public names within the
|
||||
## given module, regardless of whether they are exported
|
||||
## to the current one or not (this is used at import time
|
||||
## and for the export statement)
|
||||
if name == "":
|
||||
for obj in reversed(self.names):
|
||||
if obj.owner.isNil():
|
||||
|
@ -765,13 +773,14 @@ proc check*(self: Compiler, term: Expression, kind: Type) {.inline.} =
|
|||
let k = self.inferOrError(term)
|
||||
if not self.compare(k, kind):
|
||||
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")
|
||||
|
||||
|
||||
proc isAny*(typ: Type): bool =
|
||||
## 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:
|
||||
of Any:
|
||||
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
|
||||
## compatible with the given type and returns its
|
||||
## name object
|
||||
var impl: seq[Name] = @[]
|
||||
for obj in self.findByName(name):
|
||||
if self.compare(kind, obj.valueType):
|
||||
impl.add(obj)
|
||||
var impl: seq[Name] = self.findByType(name, kind)
|
||||
if impl.len() == 0:
|
||||
let names = self.findByName(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()})"
|
||||
else:
|
||||
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):
|
||||
msg &= &": first mismatch at position {i + 1}: (expected {self.stringify(name.valueType.args[i].kind)}, got {self.stringify(arg.kind)})"
|
||||
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:
|
||||
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.resolved = true
|
||||
for (a, b) in zip(result.valueType.args, kind.args):
|
||||
if not a.kind.isAny() and b.kind.isAny():
|
||||
self.error("any is not a valid type in this context", node)
|
||||
|
@ -849,56 +858,36 @@ proc beginScope*(self: Compiler) =
|
|||
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
|
||||
case condition.kind:
|
||||
of identExpr:
|
||||
list.add((accept, self.inferOrError(condition)))
|
||||
if list[^1].kind.kind == Auto:
|
||||
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.unpackGenerics(condition.a, list)
|
||||
self.unpackGenerics(condition.b, list)
|
||||
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.unpackGenerics(condition.a, list, accept=false)
|
||||
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 unpackUnion*(self: Compiler, condition: Expression, list: var seq[tuple[match: bool, kind: Type]], accept: bool = true) =
|
||||
## Recursively unpacks a type union
|
||||
case condition.kind:
|
||||
of identExpr:
|
||||
list.add((accept, self.inferOrError(condition)))
|
||||
of binaryExpr:
|
||||
let condition = BinaryExpr(condition)
|
||||
case condition.operator.lexeme:
|
||||
of "|":
|
||||
self.unpackUnion(condition.a, list)
|
||||
self.unpackUnion(condition.b, list)
|
||||
else:
|
||||
self.error("invalid type constraint in type union", condition)
|
||||
of unaryExpr:
|
||||
let condition = UnaryExpr(condition)
|
||||
case condition.operator.lexeme:
|
||||
of "~":
|
||||
self.unpackUnion(condition.a, list, accept=false)
|
||||
else:
|
||||
self.error("invalid type constraint in type union", condition)
|
||||
else:
|
||||
self.error("invalid type constraint in type union", condition)
|
||||
|
||||
|
||||
proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||
## Statically declares a name into the current scope.
|
||||
## "Declaring" a name only means updating our internal
|
||||
|
@ -957,19 +946,6 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
|||
fn.valueType.compiled = true
|
||||
if node.generics.len() > 0:
|
||||
fn.isGeneric = true
|
||||
var typ: Type
|
||||
for argument in node.arguments:
|
||||
typ = self.infer(argument.valueType)
|
||||
if not typ.isNil() and typ.kind == Auto:
|
||||
fn.valueType.isAuto = true
|
||||
if fn.isGeneric:
|
||||
self.error("automatic types cannot be used within generics", argument.valueType)
|
||||
break
|
||||
typ = self.infer(node.returnType)
|
||||
if not typ.isNil() and typ.kind == Auto:
|
||||
fn.valueType.isAuto = true
|
||||
if fn.isGeneric:
|
||||
self.error("automatic types cannot be used within generics", node.returnType)
|
||||
self.names.add(fn)
|
||||
self.prepareFunction(fn)
|
||||
n = fn
|
||||
|
@ -1017,10 +993,13 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
|||
case node.value.kind:
|
||||
of identExpr:
|
||||
n.valueType = self.inferOrError(node.value)
|
||||
if n.valueType.kind == Typevar:
|
||||
# Type alias!
|
||||
n.valueType = n.valueType.wrapped
|
||||
of binaryExpr:
|
||||
# Type union
|
||||
n.valueType = Type(kind: Union, types: @[])
|
||||
self.unpackUnion(node.value, n.valueType.types)
|
||||
self.unpackTypes(node.value, n.valueType.types)
|
||||
else:
|
||||
discard
|
||||
else:
|
||||
|
@ -1043,7 +1022,7 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
|||
continue
|
||||
if name.kind in [NameKind.Var, NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
||||
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:
|
||||
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
|
||||
|
|
|
@ -201,7 +201,6 @@ type
|
|||
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
|
||||
ReplExit, # Exits the VM immediately, leaving its state intact. Used in the REPL
|
||||
LoadGlobal # Loads a global variable
|
||||
|
||||
|
||||
|
@ -282,7 +281,6 @@ const simpleInstructions* = {Return, LoadNil,
|
|||
Float32GreaterOrEqual,
|
||||
Float32LessOrEqual,
|
||||
DupTop,
|
||||
ReplExit,
|
||||
Identity
|
||||
}
|
||||
|
||||
|
|
|
@ -466,7 +466,7 @@ proc handleBuiltinFunction(self: BytecodeCompiler, fn: Type, args: seq[Expressio
|
|||
"Identity": Identity
|
||||
}.to_table()
|
||||
if fn.builtinOp == "print":
|
||||
let typ = self.inferOrError(args[0])
|
||||
var typ = self.inferOrError(args[0])
|
||||
case typ.kind:
|
||||
of Int64:
|
||||
self.emitByte(PrintInt64, line)
|
||||
|
@ -528,6 +528,13 @@ proc handleBuiltinFunction(self: BytecodeCompiler, fn: Type, args: seq[Expressio
|
|||
let jump = self.emitJump(JumpIfFalseOrPop, line)
|
||||
self.expression(args[1])
|
||||
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:
|
||||
self.error(&"unknown built-in: '{fn.builtinOp}'", fn.fun)
|
||||
|
||||
|
@ -543,6 +550,9 @@ proc patchForwardDeclarations(self: BytecodeCompiler) =
|
|||
if forwarded.isPrivate != impl.isPrivate:
|
||||
self.error(&"implementation of '{impl.ident.token.lexeme}' has a mismatching visibility modifier from its forward declaration", impl.ident)
|
||||
if position == 0:
|
||||
# Forward declaration created by funDecl (it's
|
||||
# necessary to make sure that there's no unimplemented
|
||||
# forward declarations)
|
||||
continue
|
||||
pos = impl.codePos.toLong()
|
||||
self.chunk.consts[position] = pos[0]
|
||||
|
@ -585,7 +595,7 @@ proc endScope(self: BytecodeCompiler) =
|
|||
if name.kind notin [NameKind.Var, NameKind.Argument]:
|
||||
continue
|
||||
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
|
||||
# stack and are popped automatically
|
||||
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)
|
||||
of NameKind.Argument:
|
||||
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
|
||||
# warning if the function was generated internally by the compiler (for
|
||||
# example as a result of generic specialization) because such objects do
|
||||
|
@ -639,56 +649,6 @@ proc endScope(self: BytecodeCompiler) =
|
|||
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) =
|
||||
## Emits a JumpBackwards instruction with the correct
|
||||
## jump offset
|
||||
|
@ -717,19 +677,20 @@ proc handleMagicPragma(self: BytecodeCompiler, pragma: Pragma, name: Name) =
|
|||
## Handles the "magic" pragma. Assumes the given name is already
|
||||
## declared
|
||||
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:
|
||||
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:
|
||||
name.isBuiltin = true
|
||||
name.valueType.isBuiltin = true
|
||||
name.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2]
|
||||
name.valueType.compiled = true
|
||||
elif name.node.kind == NodeKind.typeDecl:
|
||||
name.valueType = pragma.args[0].token.lexeme[1..^2].toIntrinsic()
|
||||
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():
|
||||
self.error("'magic' pragma: wrong argument value", pragma.args[0])
|
||||
name.isBuiltin = true
|
||||
name.valueType.isBuiltin = true
|
||||
else:
|
||||
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
|
||||
## consequent calls). The function's address is
|
||||
## assumed to be on the stack
|
||||
if fn.isBuiltin:
|
||||
self.handleBuiltinFunction(fn, args, line)
|
||||
return
|
||||
self.emitByte(LoadUInt64, line)
|
||||
self.emitBytes(self.chunk.writeConstant(0.toLong()), line)
|
||||
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
|
||||
var constraints: seq[tuple[match: bool, kind: Type]] = @[]
|
||||
for gen in fn.node.generics:
|
||||
self.unpackGenerics(gen.cond, constraints)
|
||||
self.unpackTypes(gen.cond, constraints)
|
||||
self.names.add(Name(depth: fn.depth + 1,
|
||||
isPrivate: true,
|
||||
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
|
||||
self.stackIndex = 1
|
||||
var default: Expression
|
||||
var node = FunDecl(fn.node)
|
||||
let node = FunDecl(fn.node)
|
||||
var i = 0
|
||||
var typ: Type
|
||||
for argument in node.arguments:
|
||||
if self.names.high() > 16777215:
|
||||
self.error("cannot declare more than 16777215 variables at a time")
|
||||
inc(self.stackIndex)
|
||||
typ = self.inferOrError(argument.valueType)
|
||||
if typ.kind == Typevar:
|
||||
typ = typ.wrapped
|
||||
self.names.add(Name(depth: fn.depth + 1,
|
||||
isPrivate: true,
|
||||
owner: fn.owner,
|
||||
file: fn.file,
|
||||
isConst: false,
|
||||
ident: argument.name,
|
||||
valueType: if not fn.valueType.isAuto: self.inferOrError(argument.valueType) else: Type(kind: Any),
|
||||
valueType: typ,
|
||||
codePos: 0,
|
||||
isLet: false,
|
||||
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():
|
||||
# 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)
|
||||
else:
|
||||
# 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!
|
||||
if not FunDecl(fn.node).returnType.isNil():
|
||||
fn.valueType.returnType = self.inferOrError(FunDecl(fn.node).returnType)
|
||||
if not node.returnType.isNil():
|
||||
fn.valueType.returnType = self.inferOrError(node.returnType)
|
||||
if fn.valueType.returnType.kind == Typevar:
|
||||
fn.valueType.returnType = fn.valueType.returnType.wrapped
|
||||
fn.position = self.stackIndex
|
||||
self.stackIndex = idx
|
||||
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) =
|
||||
## Small wrapper that abstracts emitting a call instruction
|
||||
## for a given function
|
||||
self.dispatchDelayedPragmas(fn)
|
||||
if fn.isBuiltin:
|
||||
if fn.valueType.isBuiltin:
|
||||
self.handleBuiltinFunction(fn.valueType, args, line)
|
||||
return
|
||||
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.} =
|
||||
## Specializes a generic type.
|
||||
## Used for typechecking at the
|
||||
## call site
|
||||
## Instantiates a generic type
|
||||
var mapping: TableRef[string, Type] = newTable[string, Type]()
|
||||
var kind: Type
|
||||
result = deepCopy(typ)
|
||||
case result.kind:
|
||||
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
|
||||
for i, (name, typ, default) in result.args:
|
||||
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 result.returnType.name in mapping:
|
||||
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:
|
||||
self.error(&"unknown generic argument name '{result.returnType.name}'", result.fun)
|
||||
else:
|
||||
|
@ -1000,12 +978,10 @@ proc terminateProgram(self: BytecodeCompiler, pos: int) =
|
|||
## Utility to terminate a peon program
|
||||
self.patchForwardDeclarations()
|
||||
self.endScope()
|
||||
if self.replMode:
|
||||
self.emitByte(ReplExit, self.peek().token.line)
|
||||
else:
|
||||
self.emitByte(OpCode.Return, self.peek().token.line)
|
||||
self.emitByte(0, self.peek().token.line) # Entry point has no return value
|
||||
self.patchReturnAddress(pos)
|
||||
|
||||
self.emitByte(OpCode.Return, self.peek().token.line)
|
||||
self.emitByte(0, self.peek().token.line) # Entry point has no return value
|
||||
self.patchReturnAddress(pos)
|
||||
|
||||
|
||||
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:
|
||||
return nil
|
||||
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:
|
||||
return result
|
||||
var node = s.ident
|
||||
|
@ -1224,7 +1204,12 @@ method identifier(self: BytecodeCompiler, node: IdentExpr, name: Name = nil, com
|
|||
# they're referenced
|
||||
self.emitByte(LoadUInt64, 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:
|
||||
of "nil":
|
||||
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)")
|
||||
|
||||
|
||||
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.} =
|
||||
## Compiles function calls
|
||||
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:
|
||||
self.funDecl(FunDecl(result.fun), impl)
|
||||
result = result.returnType
|
||||
self.dispatchDelayedPragmas(impl)
|
||||
if compile:
|
||||
# Lambdas can't be templates :P
|
||||
if impl.valueType.fun.kind == funDecl and FunDecl(impl.valueType.fun).isTemplate:
|
||||
for arg in reversed(argExpr):
|
||||
self.expression(arg)
|
||||
|
@ -1357,7 +1382,7 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
|
|||
self.generateCall(result, argExpr, node.token.line)
|
||||
result = result.returnType
|
||||
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)))
|
||||
var fn: Name
|
||||
# getItemExpr returns a Type object, but
|
||||
|
@ -1375,17 +1400,29 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
|
|||
if compile:
|
||||
self.generateCall(fn, argExpr, node.token.line)
|
||||
of NodeKind.lambdaExpr:
|
||||
# Calling a lambda
|
||||
var node = LambdaExpr(node.callee)
|
||||
var impl = self.lambdaExpr(node, compile=compile)
|
||||
let impl = self.lambdaExpr(node, compile=compile)
|
||||
result = impl.returnType
|
||||
if compile:
|
||||
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:
|
||||
let typ = self.infer(node)
|
||||
if typ.isNil():
|
||||
self.error(&"expression has no type", node)
|
||||
else:
|
||||
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.} =
|
||||
|
@ -1547,6 +1584,8 @@ 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.genericExpr:
|
||||
return self.makeConcrete(GenericExpr(node))
|
||||
of NodeKind.callExpr:
|
||||
return self.call(CallExpr(node), compile)
|
||||
of NodeKind.getItemExpr:
|
||||
|
@ -1796,7 +1835,7 @@ proc namedBlock(self: BytecodeCompiler, node: NamedBlockStmt) =
|
|||
|
||||
|
||||
proc switchStmt(self: BytecodeCompiler, node: SwitchStmt) =
|
||||
## Compiles C-style switch statements
|
||||
## Compiles switch statements
|
||||
self.expression(node.switch)
|
||||
let typeOfA = self.inferOrError(node.switch)
|
||||
var ifJump: int = -1
|
||||
|
@ -1895,9 +1934,11 @@ proc varDecl(self: BytecodeCompiler, node: VarDecl) =
|
|||
if node.value.isNil():
|
||||
# Variable has no value: the type declaration
|
||||
# takes over
|
||||
typ = self.inferOrError(node.valueType)
|
||||
if typ.kind == Auto:
|
||||
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():
|
||||
# Variable has no type declaration: the type
|
||||
# of its value takes over
|
||||
|
@ -1915,7 +1956,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.infer(node.value)
|
||||
typ = self.inferOrError(node.value)
|
||||
self.expression(node.value)
|
||||
self.emitByte(AddVar, node.token.line)
|
||||
inc(self.stackIndex)
|
||||
|
@ -1945,7 +1986,7 @@ proc funDecl(self: BytecodeCompiler, node: FunDecl, name: Name) =
|
|||
self.forwarded.add((name, 0))
|
||||
name.valueType.forwarded = true
|
||||
return
|
||||
if name.isBuiltin:
|
||||
if name.valueType.isBuiltin:
|
||||
# Builtins are handled at call time
|
||||
return
|
||||
self.currentFunction = name
|
||||
|
@ -2087,8 +2128,6 @@ proc compile*(self: BytecodeCompiler, ast: seq[Declaration], file: string, lines
|
|||
else:
|
||||
self.ast = ast
|
||||
self.current = 0
|
||||
if not incremental:
|
||||
self.stackIndex = 1
|
||||
self.lines = lines
|
||||
self.source = source
|
||||
self.isMainModule = isMainModule
|
||||
|
@ -2099,9 +2138,8 @@ proc compile*(self: BytecodeCompiler, ast: seq[Declaration], file: string, lines
|
|||
if not incremental:
|
||||
self.jumps = @[]
|
||||
self.modules = newTable[string, Name]()
|
||||
self.stackIndex = 1
|
||||
let pos = self.beginProgram()
|
||||
let idx = self.stackIndex
|
||||
self.stackIndex = idx
|
||||
while not self.done():
|
||||
self.declaration(Declaration(self.step()))
|
||||
self.terminateProgram(pos)
|
||||
|
|
|
@ -79,6 +79,7 @@ type
|
|||
pragmaExpr,
|
||||
refExpr,
|
||||
ptrExpr,
|
||||
genericExpr,
|
||||
switchStmt
|
||||
|
||||
# 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]]]
|
||||
closeParen*: Token # Needed for error reporting
|
||||
|
||||
GenericExpr* = ref object of Expression
|
||||
ident*: IdentExpr
|
||||
args*: seq[Expression]
|
||||
|
||||
|
||||
UnaryExpr* = ref object of Expression
|
||||
operator*: Token
|
||||
a*: Expression
|
||||
|
@ -187,7 +193,7 @@ type
|
|||
ends*: seq[Expression]
|
||||
|
||||
AssignExpr* = ref object of Expression
|
||||
name*: Expression
|
||||
name*: IdentExpr
|
||||
value*: Expression
|
||||
|
||||
ExprStmt* = ref object of Statement
|
||||
|
@ -459,6 +465,13 @@ proc newCallExpr*(callee: Expression, arguments: tuple[positionals: seq[
|
|||
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 =
|
||||
result = SliceExpr(kind: sliceExpr)
|
||||
result.expression = expression
|
||||
|
@ -487,7 +500,7 @@ proc newYieldExpr*(expression: Expression, token: Token): YieldExpr =
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newAssignExpr*(name: Expression, value: Expression,
|
||||
proc newAssignExpr*(name: IdentExpr, value: Expression,
|
||||
token: Token): AssignExpr =
|
||||
result = AssignExpr(kind: assignExpr)
|
||||
result.name = name
|
||||
|
@ -786,6 +799,9 @@ proc `$`*(self: ASTNode): string =
|
|||
result &= &"Ref({Ref(self).value})"
|
||||
of ptrExpr:
|
||||
result &= &"Ptr({Ptr(self).value})"
|
||||
of genericExpr:
|
||||
var self = GenericExpr(self)
|
||||
result &= &"Generic(ident={self.ident}, args={self.args})"
|
||||
else:
|
||||
discard
|
||||
|
||||
|
@ -843,6 +859,13 @@ proc getRelativeBoundaries*(self: ASTNode): tuple[start, stop: int] =
|
|||
stop = self.token.relPos.stop + 1
|
||||
# -8 so the error highlights the #pragma[ part as well
|
||||
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:
|
||||
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")
|
||||
break
|
||||
argument = self.expression()
|
||||
if argument.kind == binaryExpr and BinaryExpr(argument).operator.lexeme == "=":
|
||||
if IdentExpr(BinaryExpr(argument).a) in argNames:
|
||||
self.error("duplicate keyword argument in function call is not allowed")
|
||||
argNames.add(IdentExpr(BinaryExpr(argument).a))
|
||||
arguments.keyword.add((name: IdentExpr(BinaryExpr(argument).a), value: BinaryExpr(argument).b))
|
||||
if argument.kind == assignExpr:
|
||||
var assign = AssignExpr(argument)
|
||||
if assign.name in argNames:
|
||||
self.error("duplicate keyword arguments are not allowed", assign.name.token)
|
||||
argNames.add(assign.name)
|
||||
arguments.keyword.add((name: assign.name, value: assign.value))
|
||||
elif arguments.keyword.len() == 0:
|
||||
arguments.positionals.add(argument)
|
||||
else:
|
||||
|
@ -436,10 +437,18 @@ proc makeCall(self: Parser, callee: Expression): CallExpr =
|
|||
result.closeParen = self.peek(-1)
|
||||
|
||||
|
||||
proc parseGenericArgs(self: Parser) =
|
||||
## Parses function generic arguments
|
||||
## like function[type](arg)
|
||||
discard # TODO
|
||||
proc parseGenericArgs(self: Parser): Expression =
|
||||
## Parses expressions like someType[someGeneric]
|
||||
## that are needed to instantiate generics
|
||||
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 =
|
||||
|
@ -454,8 +463,9 @@ proc call(self: Parser): Expression =
|
|||
result = newGetItemExpr(result, newIdentExpr(self.peek(-1), self.scopeDepth), self.peek(-1))
|
||||
result.file = self.file
|
||||
elif self.match(LeftBracket):
|
||||
self.parseGenericArgs() # TODO
|
||||
result = self.makeCall(result)
|
||||
if self.peek(-2).kind != Identifier:
|
||||
self.error("invalid syntax")
|
||||
result = self.parseGenericArgs()
|
||||
else:
|
||||
break
|
||||
|
||||
|
@ -564,7 +574,7 @@ proc parseAssign(self: Parser): Expression =
|
|||
var value = self.expression()
|
||||
case result.kind:
|
||||
of identExpr, sliceExpr:
|
||||
result = newAssignExpr(result, value, tok)
|
||||
result = newAssignExpr(IdentExpr(result), value, tok)
|
||||
result.file = self.file
|
||||
of getItemExpr:
|
||||
result = newSetItemExpr(GetItemExpr(result).obj, GetItemExpr(result).name, value, tok)
|
||||
|
@ -839,7 +849,7 @@ proc tryStmt(self: Parser): Statement =
|
|||
var elseClause: Statement
|
||||
while self.match(Except):
|
||||
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:
|
||||
self.expect(Identifier, "expecting exception name after 'except'")
|
||||
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()
|
||||
result.bindHistory(history)
|
||||
|
||||
|
||||
#[
|
||||
proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug, breakpoints: seq[uint64] = @[]) =
|
||||
styledEcho fgMagenta, "Welcome into the peon REPL!"
|
||||
var
|
||||
|
@ -104,7 +104,7 @@ proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: Comp
|
|||
for node in tree:
|
||||
styledEcho fgGreen, "\t", $node
|
||||
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:
|
||||
styledEcho fgCyan, "Compilation step:\n"
|
||||
debugger.disassembleChunk(compiled, "stdin")
|
||||
|
@ -152,6 +152,7 @@ proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: Comp
|
|||
file = relativePath(file, getCurrentDir())
|
||||
stderr.styledWriteLine(fgRed, styleBright, "Error while (de-)serializing ", fgYellow, file, fgDefault, &": {getCurrentException().msg}")
|
||||
quit(0)
|
||||
]#
|
||||
|
||||
|
||||
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")
|
||||
elif backend == PeonBackend.Bytecode:
|
||||
serialized = serializer.loadFile(f)
|
||||
if debugCompiler:
|
||||
styledEcho fgCyan, "Compilation step:\n"
|
||||
debugger.disassembleChunk(serialized.chunk, f)
|
||||
if backend == PeonBackend.Bytecode and debugSerializer:
|
||||
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
|
||||
|
@ -415,6 +419,7 @@ when isMainModule:
|
|||
if breaks.len() == 0 and debugVM:
|
||||
breaks.add(0)
|
||||
if file == "":
|
||||
repl(warnings, mismatches, mode, breaks)
|
||||
echo "Sorry, the REPL is currently broken :("
|
||||
#repl(warnings, mismatches, mode, breaks)
|
||||
else:
|
||||
runFile(file, fromString, dump, breaks, warnings, mismatches, mode, run, backend, output)
|
||||
|
|
|
@ -68,6 +68,12 @@ type auto* = object {
|
|||
#pragma[magic: "auto"]
|
||||
}
|
||||
|
||||
|
||||
type typevar* = object {
|
||||
#pragma[magic: "typevar"]
|
||||
}
|
||||
|
||||
|
||||
# Some convenience aliases
|
||||
type int* = int64;
|
||||
type float* = float64;
|
||||
|
|
|
@ -21,4 +21,9 @@ var test* = 0x60;
|
|||
|
||||
fn testGlobals*: bool {
|
||||
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
|
||||
if file notin ["<string>", "", "stdin"]:
|
||||
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:
|
||||
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,
|
||||
exc.msg)
|
||||
|
||||
|
@ -62,7 +62,7 @@ proc print*(exc: ParseError) =
|
|||
var file = exc.file
|
||||
if file notin ["<string>", ""]:
|
||||
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.msg)
|
||||
|
||||
|
@ -73,6 +73,6 @@ proc print*(exc: LexingError) =
|
|||
var file = exc.file
|
||||
if file notin ["<string>", ""]:
|
||||
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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue