Fixes to function calls and attempt to fix closures inside variables
This commit is contained in:
parent
8fb90bb0ef
commit
142e575497
|
@ -767,6 +767,7 @@ proc dispatch*(self: var PeonVM) =
|
||||||
of LoadUInt8:
|
of LoadUInt8:
|
||||||
self.push(uint64(self.constReadUInt8(int(self.readLong()))))
|
self.push(uint64(self.constReadUInt8(int(self.readLong()))))
|
||||||
of LoadString:
|
of LoadString:
|
||||||
|
# Loads the string's pointer onto the stack
|
||||||
self.push(cast[uint64](self.constReadString(int(self.readLong()), int(self.readLong()))))
|
self.push(cast[uint64](self.constReadString(int(self.readLong()), int(self.readLong()))))
|
||||||
# We cast instead of converting because, unlike with integers,
|
# We cast instead of converting because, unlike with integers,
|
||||||
# we don't want nim to touch any of the bits of the underlying
|
# we don't want nim to touch any of the bits of the underlying
|
||||||
|
|
|
@ -50,7 +50,6 @@ type
|
||||||
Type = ref object
|
Type = ref object
|
||||||
## A wrapper around
|
## A wrapper around
|
||||||
## compile-time types
|
## compile-time types
|
||||||
isBuiltin: bool
|
|
||||||
case kind: TypeKind:
|
case kind: TypeKind:
|
||||||
of Function:
|
of Function:
|
||||||
isLambda: bool
|
isLambda: bool
|
||||||
|
@ -66,6 +65,7 @@ type
|
||||||
parent: Type
|
parent: Type
|
||||||
retJumps: seq[int]
|
retJumps: seq[int]
|
||||||
forwarded: bool
|
forwarded: bool
|
||||||
|
location: int
|
||||||
of CustomType:
|
of CustomType:
|
||||||
fields: TableRef[string, Type]
|
fields: TableRef[string, Type]
|
||||||
of Reference, Pointer:
|
of Reference, Pointer:
|
||||||
|
@ -149,6 +149,8 @@ type
|
||||||
# Has the compiler generates this name internally or
|
# Has the compiler generates this name internally or
|
||||||
# does it come from user code?
|
# does it come from user code?
|
||||||
isReal: bool
|
isReal: bool
|
||||||
|
# Is this name a builtin?
|
||||||
|
isBuiltin: bool
|
||||||
|
|
||||||
Loop = object
|
Loop = object
|
||||||
## A "loop object" used
|
## A "loop object" used
|
||||||
|
@ -263,13 +265,13 @@ proc expression(self: Compiler, node: Expression, compile: bool = true): Type {.
|
||||||
proc statement(self: Compiler, node: Statement)
|
proc statement(self: Compiler, node: Statement)
|
||||||
proc declaration(self: Compiler, node: Declaration)
|
proc declaration(self: Compiler, node: Declaration)
|
||||||
proc peek(self: Compiler, distance: int = 0): ASTNode
|
proc peek(self: Compiler, distance: int = 0): ASTNode
|
||||||
proc identifier(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool = true): Name {.discardable.}
|
proc identifier(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool = true): Type {.discardable.}
|
||||||
proc varDecl(self: Compiler, node: VarDecl)
|
proc varDecl(self: Compiler, node: VarDecl)
|
||||||
proc match(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allowFwd: bool = true): Name
|
proc match(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allowFwd: bool = true): Name
|
||||||
proc call(self: Compiler, node: CallExpr, compile: bool = true): Type {.discardable.}
|
proc call(self: Compiler, node: CallExpr, compile: bool = true): Type {.discardable.}
|
||||||
proc getItemExpr(self: Compiler, node: GetItemExpr, compile: bool = true): Type {.discardable.}
|
proc getItemExpr(self: Compiler, node: GetItemExpr, compile: bool = true): Type {.discardable.}
|
||||||
proc unary(self: Compiler, node: UnaryExpr, compile: bool = true): Name {.discardable.}
|
proc unary(self: Compiler, node: UnaryExpr, compile: bool = true): Type {.discardable.}
|
||||||
proc binary(self: Compiler, node: BinaryExpr, compile: bool = true): Name {.discardable.}
|
proc binary(self: Compiler, node: BinaryExpr, compile: bool = true): Type {.discardable.}
|
||||||
proc infer(self: Compiler, node: LiteralExpr): Type
|
proc infer(self: Compiler, node: LiteralExpr): Type
|
||||||
proc infer(self: Compiler, node: Expression): Type
|
proc infer(self: Compiler, node: Expression): Type
|
||||||
proc inferOrError(self: Compiler, node: Expression): Type
|
proc inferOrError(self: Compiler, node: Expression): Type
|
||||||
|
@ -287,6 +289,7 @@ proc funDecl(self: Compiler, node: FunDecl, name: Name)
|
||||||
proc compileModule(self: Compiler, module: Name)
|
proc compileModule(self: Compiler, module: Name)
|
||||||
proc generateCall(self: Compiler, fn: Name, args: seq[Expression], line: int)
|
proc generateCall(self: Compiler, fn: Name, args: seq[Expression], line: int)
|
||||||
proc prepareFunction(self: Compiler, fn: Name)
|
proc prepareFunction(self: Compiler, fn: Name)
|
||||||
|
proc lambdaExpr(self: Compiler, node: LambdaExpr, compile: bool = true): Type {.discardable.}
|
||||||
# End of forward declarations
|
# End of forward declarations
|
||||||
|
|
||||||
|
|
||||||
|
@ -328,7 +331,7 @@ proc getSource*(self: Compiler): string = self.source
|
||||||
## Utility functions
|
## Utility functions
|
||||||
|
|
||||||
proc `$`*(self: Name): string = $self[]
|
proc `$`*(self: Name): string = $self[]
|
||||||
proc `$`(self: Type): string = $self[]
|
#proc `$`(self: Type): string = $self[]
|
||||||
proc hash(self: Name): Hash = self.ident.token.lexeme.hash()
|
proc hash(self: Name): Hash = self.ident.token.lexeme.hash()
|
||||||
|
|
||||||
|
|
||||||
|
@ -612,6 +615,8 @@ proc fixNames(self: Compiler, where, oldLen: int) =
|
||||||
for name in self.names:
|
for name in self.names:
|
||||||
if name.codePos > where:
|
if name.codePos > where:
|
||||||
name.codePos += offset
|
name.codePos += offset
|
||||||
|
if name.valueType.kind == Function:
|
||||||
|
name.valueType.location += offset
|
||||||
|
|
||||||
|
|
||||||
proc insertAt(self: Compiler, where: int, opcode: OpCode, data: openarray[uint8]): int =
|
proc insertAt(self: Compiler, where: int, opcode: OpCode, data: openarray[uint8]): int =
|
||||||
|
@ -730,7 +735,7 @@ proc getStackPos(self: Compiler, name: Name): int =
|
||||||
# temporaries. There is no stack frame for builtins, so we skip
|
# temporaries. There is no stack frame for builtins, so we skip
|
||||||
# these names too
|
# these names too
|
||||||
elif variable.kind == Argument:
|
elif variable.kind == Argument:
|
||||||
if variable.belongsTo.valueType.isBuiltin:
|
if variable.belongsTo.isBuiltin:
|
||||||
continue
|
continue
|
||||||
elif not variable.belongsTo.resolved:
|
elif not variable.belongsTo.resolved:
|
||||||
continue
|
continue
|
||||||
|
@ -989,26 +994,28 @@ proc infer(self: Compiler, node: Expression): Type =
|
||||||
if node.isNil():
|
if node.isNil():
|
||||||
return nil
|
return nil
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of identExpr:
|
of NodeKind.identExpr:
|
||||||
result = self.identifier(IdentExpr(node), compile=false).valueType
|
result = self.identifier(IdentExpr(node), compile=false)
|
||||||
of unaryExpr:
|
of NodeKind.unaryExpr:
|
||||||
result = self.unary(UnaryExpr(node), compile=false).valueType.returnType
|
result = self.unary(UnaryExpr(node), compile=false)
|
||||||
of binaryExpr:
|
of NodeKind.binaryExpr:
|
||||||
result = self.binary(BinaryExpr(node), compile=false).valueType.returnType
|
result = self.binary(BinaryExpr(node), compile=false)
|
||||||
of {intExpr, hexExpr, binExpr, octExpr,
|
of {NodeKind.intExpr, NodeKind.hexExpr, NodeKind.binExpr, NodeKind.octExpr,
|
||||||
strExpr, falseExpr, trueExpr, floatExpr
|
NodeKind.strExpr, NodeKind.falseExpr, NodeKind.trueExpr, NodeKind.floatExpr
|
||||||
}:
|
}:
|
||||||
result = self.infer(LiteralExpr(node))
|
result = self.infer(LiteralExpr(node))
|
||||||
of NodeKind.callExpr:
|
of NodeKind.callExpr:
|
||||||
result = self.call(CallExpr(node), compile=false).returnType
|
result = self.call(CallExpr(node), compile=false)
|
||||||
of refExpr:
|
of NodeKind.refExpr:
|
||||||
result = Type(kind: Reference, value: self.infer(Ref(node).value))
|
result = Type(kind: Reference, value: self.infer(Ref(node).value))
|
||||||
of ptrExpr:
|
of NodeKind.ptrExpr:
|
||||||
result = Type(kind: Pointer, value: self.infer(Ptr(node).value))
|
result = Type(kind: Pointer, value: self.infer(Ptr(node).value))
|
||||||
of groupingExpr:
|
of NodeKind.groupingExpr:
|
||||||
result = self.infer(GroupingExpr(node).expression)
|
result = self.infer(GroupingExpr(node).expression)
|
||||||
of NodeKind.getItemExpr:
|
of NodeKind.getItemExpr:
|
||||||
result = self.getItemExpr(GetItemExpr(node), compile=false)
|
result = self.getItemExpr(GetItemExpr(node), compile=false)
|
||||||
|
of NodeKind.lambdaExpr:
|
||||||
|
result = self.lambdaExpr(LambdaExpr(node), compile=false)
|
||||||
else:
|
else:
|
||||||
discard # TODO
|
discard # TODO
|
||||||
|
|
||||||
|
@ -1260,8 +1267,6 @@ proc handleBuiltinFunction(self: Compiler, fn: Type, args: seq[Expression], line
|
||||||
}.to_table()
|
}.to_table()
|
||||||
if fn.builtinOp == "print":
|
if fn.builtinOp == "print":
|
||||||
var typ = self.expression(args[0], compile=false)
|
var typ = self.expression(args[0], compile=false)
|
||||||
if typ.kind == Function:
|
|
||||||
typ = typ.returnType
|
|
||||||
case typ.kind:
|
case typ.kind:
|
||||||
of Int64:
|
of Int64:
|
||||||
self.emitByte(PrintInt64, line)
|
self.emitByte(PrintInt64, line)
|
||||||
|
@ -1291,6 +1296,8 @@ proc handleBuiltinFunction(self: Compiler, fn: Type, args: seq[Expression], line
|
||||||
self.emitByte(PrintNan, line)
|
self.emitByte(PrintNan, line)
|
||||||
of Inf:
|
of Inf:
|
||||||
self.emitByte(PrintInf, line)
|
self.emitByte(PrintInf, line)
|
||||||
|
of Function:
|
||||||
|
self.emitByte(PrintHex, line)
|
||||||
else:
|
else:
|
||||||
self.error("invalid type for built-in 'print'", args[0])
|
self.error("invalid type for built-in 'print'", args[0])
|
||||||
return
|
return
|
||||||
|
@ -1380,7 +1387,7 @@ proc endScope(self: Compiler) =
|
||||||
if name.kind notin [NameKind.Var, NameKind.Argument]:
|
if name.kind notin [NameKind.Var, NameKind.Argument]:
|
||||||
continue
|
continue
|
||||||
elif name.kind == NameKind.Argument:
|
elif name.kind == NameKind.Argument:
|
||||||
if name.belongsTo.valueType.isBuiltin:
|
if name.belongsTo.isBuiltin:
|
||||||
# Arguments to builtin functions become temporaries on the
|
# Arguments to builtin functions become temporaries on the
|
||||||
# stack and are popped automatically
|
# stack and are popped automatically
|
||||||
continue
|
continue
|
||||||
|
@ -1397,7 +1404,7 @@ proc endScope(self: Compiler) =
|
||||||
self.warning(UnusedName, &"'{name.ident.token.lexeme}' is declared but not used (add '_' prefix to silence warning)", name)
|
self.warning(UnusedName, &"'{name.ident.token.lexeme}' is declared but not used (add '_' prefix to silence warning)", name)
|
||||||
of NameKind.Argument:
|
of NameKind.Argument:
|
||||||
if not name.ident.token.lexeme.startsWith("_") and name.isPrivate:
|
if not name.ident.token.lexeme.startsWith("_") and name.isPrivate:
|
||||||
if not name.belongsTo.valueType.isBuiltin and name.belongsTo.isReal:
|
if not name.belongsTo.isBuiltin and name.belongsTo.isReal:
|
||||||
# Builtin functions never use their arguments. We also don't emit this
|
# Builtin functions never use their arguments. We also don't emit this
|
||||||
# warning if the function was generated internally by the compiler (for
|
# warning if the function was generated internally by the compiler (for
|
||||||
# example as a result of generic specialization) because such objects do
|
# example as a result of generic specialization) because such objects do
|
||||||
|
@ -1595,7 +1602,7 @@ proc declare(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||||
for name in self.findByName(declaredName):
|
for name in self.findByName(declaredName):
|
||||||
if name == n:
|
if name == n:
|
||||||
continue
|
continue
|
||||||
# We don't check for name clashes with functions because match does that
|
# We don't check for name clashes with functions because self.match() does that
|
||||||
elif name.kind in [NameKind.Var, NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
elif name.kind in [NameKind.Var, NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
||||||
if name.owner != self.currentModule:
|
if name.owner != self.currentModule:
|
||||||
if name.isPrivate:
|
if name.isPrivate:
|
||||||
|
@ -1640,7 +1647,7 @@ proc handleMagicPragma(self: Compiler, pragma: Pragma, name: Name) =
|
||||||
elif pragma.args[0].kind != strExpr:
|
elif pragma.args[0].kind != strExpr:
|
||||||
self.error("'magic' pragma: wrong type of argument (constant string expected)")
|
self.error("'magic' pragma: wrong type of argument (constant string expected)")
|
||||||
elif name.node.kind == NodeKind.funDecl:
|
elif name.node.kind == NodeKind.funDecl:
|
||||||
name.valueType.isBuiltin = true
|
name.isBuiltin = true
|
||||||
name.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2]
|
name.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2]
|
||||||
elif name.node.kind == NodeKind.typeDecl:
|
elif name.node.kind == NodeKind.typeDecl:
|
||||||
name.valueType = pragma.args[0].token.lexeme[1..^2].toIntrinsic()
|
name.valueType = pragma.args[0].token.lexeme[1..^2].toIntrinsic()
|
||||||
|
@ -1648,7 +1655,7 @@ proc handleMagicPragma(self: Compiler, pragma: Pragma, name: Name) =
|
||||||
self.error("'magic' pragma: wrong argument value", pragma.args[0])
|
self.error("'magic' pragma: wrong argument value", pragma.args[0])
|
||||||
if name.valueType.kind == All:
|
if name.valueType.kind == All:
|
||||||
self.error("don't even think about it (compiler-chan is angry at you)", pragma)
|
self.error("don't even think about it (compiler-chan is angry at you)", pragma)
|
||||||
name.valueType.isBuiltin = true
|
name.isBuiltin = true
|
||||||
else:
|
else:
|
||||||
self.error("'magic' pragma is not valid in this context")
|
self.error("'magic' pragma is not valid in this context")
|
||||||
|
|
||||||
|
@ -1669,7 +1676,7 @@ proc handlePurePragma(self: Compiler, pragma: Pragma, name: Name) =
|
||||||
case name.node.kind:
|
case name.node.kind:
|
||||||
of NodeKind.funDecl:
|
of NodeKind.funDecl:
|
||||||
FunDecl(name.node).isPure = true
|
FunDecl(name.node).isPure = true
|
||||||
of lambdaExpr:
|
of NodeKind.lambdaExpr:
|
||||||
LambdaExpr(name.node).isPure = true
|
LambdaExpr(name.node).isPure = true
|
||||||
else:
|
else:
|
||||||
self.error("'pure' pragma is not valid in this context")
|
self.error("'pure' pragma is not valid in this context")
|
||||||
|
@ -1683,7 +1690,7 @@ proc dispatchPragmas(self: Compiler, name: Name) =
|
||||||
case name.node.kind:
|
case name.node.kind:
|
||||||
of NodeKind.funDecl, NodeKind.typeDecl, NodeKind.varDecl:
|
of NodeKind.funDecl, NodeKind.typeDecl, NodeKind.varDecl:
|
||||||
pragmas = Declaration(name.node).pragmas
|
pragmas = Declaration(name.node).pragmas
|
||||||
of lambdaExpr:
|
of NodeKind.lambdaExpr:
|
||||||
pragmas = LambdaExpr(name.node).pragmas
|
pragmas = LambdaExpr(name.node).pragmas
|
||||||
else:
|
else:
|
||||||
discard # Unreachable
|
discard # Unreachable
|
||||||
|
@ -1795,19 +1802,26 @@ proc beginProgram(self: Compiler): int =
|
||||||
|
|
||||||
## End of utility functions
|
## End of utility functions
|
||||||
|
|
||||||
proc literal(self: Compiler, node: ASTNode) =
|
proc literal(self: Compiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
|
||||||
## Emits instructions for literals such
|
## Emits instructions for literals such
|
||||||
## as singletons, strings and numbers
|
## as singletons, strings and numbers
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of trueExpr:
|
of trueExpr:
|
||||||
self.emitByte(LoadTrue, node.token.line)
|
result = Type(kind: Bool)
|
||||||
|
if compile:
|
||||||
|
self.emitByte(LoadTrue, node.token.line)
|
||||||
of falseExpr:
|
of falseExpr:
|
||||||
self.emitByte(LoadFalse, node.token.line)
|
result = Type(kind: Bool)
|
||||||
|
if compile:
|
||||||
|
self.emitByte(LoadFalse, node.token.line)
|
||||||
of strExpr:
|
of strExpr:
|
||||||
self.emitConstant(LiteralExpr(node), Type(kind: String))
|
result = Type(kind: String)
|
||||||
|
if compile:
|
||||||
|
self.emitConstant(LiteralExpr(node), Type(kind: String))
|
||||||
of intExpr:
|
of intExpr:
|
||||||
let y = IntExpr(node)
|
let y = IntExpr(node)
|
||||||
let kind = self.infer(y)
|
let kind = self.infer(y)
|
||||||
|
result = kind
|
||||||
if kind.kind in [Int64, Int32, Int16, Int8]:
|
if kind.kind in [Int64, Int32, Int16, Int8]:
|
||||||
var x: int
|
var x: int
|
||||||
try:
|
try:
|
||||||
|
@ -1819,11 +1833,13 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
try:
|
try:
|
||||||
discard parseBiggestUInt(y.literal.lexeme, x)
|
discard parseBiggestUInt(y.literal.lexeme, x)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.error("integer value out of range")
|
self.error("integer value out of range")
|
||||||
self.emitConstant(y, kind)
|
if compile:
|
||||||
|
self.emitConstant(y, kind)
|
||||||
of hexExpr:
|
of hexExpr:
|
||||||
var x: int
|
var x: int
|
||||||
var y = HexExpr(node)
|
var y = HexExpr(node)
|
||||||
|
result = self.infer(y)
|
||||||
try:
|
try:
|
||||||
discard parseHex(y.literal.lexeme, x)
|
discard parseHex(y.literal.lexeme, x)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -1834,10 +1850,12 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
relPos: (start: y.token.relPos.start, stop: y.token.relPos.start + len($x))
|
relPos: (start: y.token.relPos.start, stop: y.token.relPos.start + len($x))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.emitConstant(node, self.infer(y))
|
if compile:
|
||||||
|
self.emitConstant(node, result)
|
||||||
of binExpr:
|
of binExpr:
|
||||||
var x: int
|
var x: int
|
||||||
var y = BinExpr(node)
|
var y = BinExpr(node)
|
||||||
|
result = self.infer(y)
|
||||||
try:
|
try:
|
||||||
discard parseBin(y.literal.lexeme, x)
|
discard parseBin(y.literal.lexeme, x)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -1848,10 +1866,12 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
relPos: (start: y.token.relPos.start, stop: y.token.relPos.start + len($x))
|
relPos: (start: y.token.relPos.start, stop: y.token.relPos.start + len($x))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.emitConstant(node, self.infer(y))
|
if compile:
|
||||||
|
self.emitConstant(node, result)
|
||||||
of octExpr:
|
of octExpr:
|
||||||
var x: int
|
var x: int
|
||||||
var y = OctExpr(node)
|
var y = OctExpr(node)
|
||||||
|
result = self.infer(y)
|
||||||
try:
|
try:
|
||||||
discard parseOct(y.literal.lexeme, x)
|
discard parseOct(y.literal.lexeme, x)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -1862,51 +1882,54 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
relPos: (start: y.token.relPos.start, stop: y.token.relPos.start + len($x))
|
relPos: (start: y.token.relPos.start, stop: y.token.relPos.start + len($x))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.emitConstant(node, self.infer(y))
|
if compile:
|
||||||
|
self.emitConstant(node, result)
|
||||||
of floatExpr:
|
of floatExpr:
|
||||||
var x: float
|
var x: float
|
||||||
var y = FloatExpr(node)
|
var y = FloatExpr(node)
|
||||||
|
result = self.infer(y)
|
||||||
try:
|
try:
|
||||||
discard parseFloat(y.literal.lexeme, x)
|
discard parseFloat(y.literal.lexeme, x)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.error("floating point value out of range")
|
self.error("floating point value out of range")
|
||||||
self.emitConstant(y, self.infer(y))
|
if compile:
|
||||||
|
self.emitConstant(y, result)
|
||||||
of awaitExpr:
|
of awaitExpr:
|
||||||
var y = AwaitExpr(node)
|
discard # TODO
|
||||||
self.expression(y.expression)
|
|
||||||
self.emitByte(OpCode.Await, node.token.line)
|
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid AST node of kind {node.kind} at literal(): {node} (This is an internal error and most likely a bug!)")
|
self.error(&"invalid AST node of kind {node.kind} at literal(): {node} (This is an internal error and most likely a bug!)")
|
||||||
|
|
||||||
|
|
||||||
proc unary(self: Compiler, node: UnaryExpr, compile: bool = true): Name {.discardable.} =
|
proc unary(self: Compiler, node: UnaryExpr, compile: bool = true): Type {.discardable.} =
|
||||||
## Compiles all unary expressions
|
## Compiles all unary expressions
|
||||||
var default: Expression
|
var default: Expression
|
||||||
let fn = Type(kind: Function,
|
let fn = Type(kind: Function,
|
||||||
returnType: Type(kind: Any),
|
returnType: Type(kind: Any),
|
||||||
args: @[("", self.inferOrError(node.a), default)])
|
args: @[("", self.inferOrError(node.a), default)])
|
||||||
result = self.match(node.token.lexeme, fn, node)
|
let impl = self.match(node.token.lexeme, fn, node)
|
||||||
|
result = impl.valueType.returnType
|
||||||
if compile:
|
if compile:
|
||||||
self.generateCall(result, @[node.a], result.line)
|
self.generateCall(impl, @[node.a], impl.line)
|
||||||
|
|
||||||
|
|
||||||
proc binary(self: Compiler, node: BinaryExpr, compile: bool = true): Name {.discardable.} =
|
proc binary(self: Compiler, node: BinaryExpr, compile: bool = true): Type {.discardable.} =
|
||||||
## Compiles all binary expressions
|
## Compiles all binary expressions
|
||||||
var default: Expression
|
var default: Expression
|
||||||
let fn = Type(kind: Function, returnType: Type(kind: Any), args: @[("", self.inferOrError(node.a), default), ("", self.inferOrError(node.b), default)])
|
let fn = Type(kind: Function, returnType: Type(kind: Any), args: @[("", self.inferOrError(node.a), default), ("", self.inferOrError(node.b), default)])
|
||||||
result = self.match(node.token.lexeme, fn, node)
|
let impl = self.match(node.token.lexeme, fn, node)
|
||||||
|
result = impl.valueType.returnType
|
||||||
if compile:
|
if compile:
|
||||||
self.generateCall(result, @[node.a, node.b], result.line)
|
self.generateCall(impl, @[node.a, node.b], impl.line)
|
||||||
|
|
||||||
|
|
||||||
proc identifier(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool = true): Name {.discardable.} =
|
proc identifier(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool = true): Type {.discardable.} =
|
||||||
## Compiles access to identifiers
|
## Compiles access to identifiers
|
||||||
var s = name
|
var s = name
|
||||||
if s.isNil():
|
if s.isNil():
|
||||||
s = self.resolveOrError(node)
|
s = self.resolveOrError(node)
|
||||||
var t = self.findByType(s.ident.token.lexeme, Type(kind: All))
|
var t = self.findByType(s.ident.token.lexeme, Type(kind: All))
|
||||||
s = t[0] # Shadowing!
|
s = t[0] # Shadowing!
|
||||||
result = s
|
result = s.valueType
|
||||||
if not compile:
|
if not compile:
|
||||||
return
|
return
|
||||||
if s.isConst:
|
if s.isConst:
|
||||||
|
@ -1919,8 +1942,8 @@ proc identifier(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool
|
||||||
# resolve them to their address into our bytecode when
|
# resolve them to their address into our bytecode when
|
||||||
# they're referenced
|
# they're referenced
|
||||||
self.emitByte(LoadUInt64, node.token.line)
|
self.emitByte(LoadUInt64, node.token.line)
|
||||||
self.emitBytes(self.chunk.writeConstant(s.codePos.toLong()), node.token.line)
|
self.emitBytes(self.chunk.writeConstant(s.valueType.location.toLong()), node.token.line)
|
||||||
elif s.valueType.isBuiltin:
|
elif s.isBuiltin:
|
||||||
case s.ident.token.lexeme:
|
case s.ident.token.lexeme:
|
||||||
of "nil":
|
of "nil":
|
||||||
self.emitByte(LoadNil, node.token.line)
|
self.emitByte(LoadNil, node.token.line)
|
||||||
|
@ -1990,7 +2013,15 @@ proc assignment(self: Compiler, node: ASTNode, compile: bool = true): Name {.dis
|
||||||
self.emitByte(StoreClosure, node.token.line)
|
self.emitByte(StoreClosure, node.token.line)
|
||||||
self.emitBytes(self.getClosurePos(r).toTriple(), node.token.line)
|
self.emitBytes(self.getClosurePos(r).toTriple(), node.token.line)
|
||||||
of setItemExpr:
|
of setItemExpr:
|
||||||
discard # TODO
|
let node = SetItemExpr(node)
|
||||||
|
let name = IdentExpr(node.name)
|
||||||
|
var r = self.resolveOrError(name)
|
||||||
|
if r.isConst:
|
||||||
|
self.error(&"cannot assign to '{name.token.lexeme}' (value is a constant)", name)
|
||||||
|
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)
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid AST node of kind {node.kind} at assignment(): {node} (This is an internal error and most likely a bug)")
|
self.error(&"invalid AST node of kind {node.kind} at assignment(): {node} (This is an internal error and most likely a bug)")
|
||||||
|
|
||||||
|
@ -2038,11 +2069,11 @@ proc whileStmt(self: Compiler, node: WhileStmt) =
|
||||||
self.patchJump(jump)
|
self.patchJump(jump)
|
||||||
|
|
||||||
|
|
||||||
# TODO: This will be needed for lambdas
|
|
||||||
proc generateCall(self: Compiler, fn: Type, args: seq[Expression], line: int) {.used.} =
|
proc generateCall(self: Compiler, fn: Type, args: seq[Expression], line: int) {.used.} =
|
||||||
## Version of generateCall that takes Type objects
|
## Version of generateCall that takes Type objects
|
||||||
## instead of Name objects. The function is assumed
|
## instead of Name objects (used for lambdas and
|
||||||
## to be on the stack
|
## consequent calls). The function's address is
|
||||||
|
## assumed to be on the stack
|
||||||
self.emitByte(LoadUInt64, line)
|
self.emitByte(LoadUInt64, line)
|
||||||
self.emitBytes(self.chunk.writeConstant(0.toLong()), line)
|
self.emitBytes(self.chunk.writeConstant(0.toLong()), line)
|
||||||
let pos = self.chunk.consts.len() - 8
|
let pos = self.chunk.consts.len() - 8
|
||||||
|
@ -2124,7 +2155,7 @@ proc generateCall(self: Compiler, fn: Name, args: seq[Expression], line: int) =
|
||||||
## Small wrapper that abstracts emitting a call instruction
|
## Small wrapper that abstracts emitting a call instruction
|
||||||
## for a given function
|
## for a given function
|
||||||
self.dispatchDelayedPragmas(fn)
|
self.dispatchDelayedPragmas(fn)
|
||||||
if fn.valueType.isBuiltin:
|
if fn.isBuiltin:
|
||||||
self.handleBuiltinFunction(fn.valueType, args, line)
|
self.handleBuiltinFunction(fn.valueType, args, line)
|
||||||
return
|
return
|
||||||
case fn.kind:
|
case fn.kind:
|
||||||
|
@ -2212,6 +2243,7 @@ proc call(self: Compiler, node: CallExpr, compile: bool = true): Type {.discarda
|
||||||
result = impl.valueType
|
result = impl.valueType
|
||||||
if impl.isGeneric:
|
if impl.isGeneric:
|
||||||
result = self.specialize(result, argExpr)
|
result = self.specialize(result, argExpr)
|
||||||
|
result = result.returnType
|
||||||
if compile:
|
if compile:
|
||||||
# Now we call it
|
# Now we call it
|
||||||
self.generateCall(impl, argExpr, node.token.line)
|
self.generateCall(impl, argExpr, node.token.line)
|
||||||
|
@ -2226,18 +2258,21 @@ proc call(self: Compiler, node: CallExpr, compile: bool = true): Type {.discarda
|
||||||
node = CallExpr(node).callee
|
node = CallExpr(node).callee
|
||||||
# Now that we know how many call expressions we
|
# Now that we know how many call expressions we
|
||||||
# need to compile, we start from the outermost
|
# need to compile, we start from the outermost
|
||||||
# one (which is at the end because we went all
|
# one and work our way to the innermost call
|
||||||
# the way back to the first one earlier) and work
|
for exp in all:
|
||||||
# our way to the innermost call
|
result = self.call(exp, compile)
|
||||||
for exp in reversed(all):
|
#echo result
|
||||||
self.call(exp, compile)
|
#result = result.returnType
|
||||||
|
if compile and result.kind == Function:
|
||||||
|
self.generateCall(result, argExpr, node.token.line)
|
||||||
|
result = result.returnType
|
||||||
# TODO: Calling lambdas on-the-fly (i.e. on the same line)
|
# TODO: Calling lambdas on-the-fly (i.e. on the same line)
|
||||||
else:
|
else:
|
||||||
let typ = self.infer(node)
|
let typ = self.infer(node)
|
||||||
if typ.isNil():
|
if typ.isNil():
|
||||||
self.error(&"expression has no type")
|
self.error(&"expression has no type", node)
|
||||||
else:
|
else:
|
||||||
self.error(&"object of type '{self.stringify(typ)}' is not callable")
|
self.error(&"object of type '{self.stringify(typ)}' is not callable", node)
|
||||||
|
|
||||||
|
|
||||||
proc getItemExpr(self: Compiler, node: GetItemExpr, compile: bool = true): Type {.discardable.} =
|
proc getItemExpr(self: Compiler, node: GetItemExpr, compile: bool = true): Type {.discardable.} =
|
||||||
|
@ -2263,6 +2298,59 @@ proc getItemExpr(self: Compiler, node: GetItemExpr, compile: bool = true): Type
|
||||||
self.error("invalid syntax", node)
|
self.error("invalid syntax", node)
|
||||||
|
|
||||||
|
|
||||||
|
proc lambdaExpr(self: Compiler, node: LambdaExpr, compile: bool = true): Type {.discardable.} =
|
||||||
|
## Compiles lambda functions as expressions
|
||||||
|
result = Type(kind: Function, isLambda: true, fun: node)
|
||||||
|
self.beginScope()
|
||||||
|
var constraints: seq[tuple[match: bool, kind: Type]] = @[]
|
||||||
|
for gen in node.generics:
|
||||||
|
self.unpackGenerics(gen.cond, constraints)
|
||||||
|
self.names.add(Name(depth: self.depth,
|
||||||
|
isPrivate: true,
|
||||||
|
valueType: Type(kind: Generic, name: gen.name.token.lexeme, cond: constraints),
|
||||||
|
codePos: 0,
|
||||||
|
isLet: false,
|
||||||
|
line: node.token.line,
|
||||||
|
belongsTo: nil, # TODO
|
||||||
|
ident: gen.name,
|
||||||
|
owner: self.currentModule,
|
||||||
|
file: self.file))
|
||||||
|
constraints = @[]
|
||||||
|
var default: Expression
|
||||||
|
var i = 0
|
||||||
|
for argument in node.arguments:
|
||||||
|
if self.names.high() > 16777215:
|
||||||
|
self.error("cannot declare more than 16777215 variables at a time")
|
||||||
|
self.names.add(Name(depth: self.depth,
|
||||||
|
isPrivate: true,
|
||||||
|
owner: self.currentModule,
|
||||||
|
file: self.currentModule.file,
|
||||||
|
isConst: false,
|
||||||
|
ident: argument.name,
|
||||||
|
valueType: self.inferOrError(argument.valueType),
|
||||||
|
codePos: 0,
|
||||||
|
isLet: false,
|
||||||
|
line: argument.name.token.line,
|
||||||
|
belongsTo: nil, # TODO
|
||||||
|
kind: NameKind.Argument,
|
||||||
|
node: argument.name
|
||||||
|
))
|
||||||
|
if node.arguments.high() - node.defaults.high() <= node.arguments.high():
|
||||||
|
# There's a default argument!
|
||||||
|
result.args.add((self.names[^1].ident.token.lexeme, self.names[^1].valueType, node.defaults[i]))
|
||||||
|
inc(i)
|
||||||
|
else:
|
||||||
|
# This argument has no default
|
||||||
|
result.args.add((self.names[^1].ident.token.lexeme, self.names[^1].valueType, default))
|
||||||
|
# The function needs a return type too!
|
||||||
|
if not node.returnType.isNil():
|
||||||
|
result.returnType = self.inferOrError(node.returnType)
|
||||||
|
if not compile:
|
||||||
|
return
|
||||||
|
# TODO
|
||||||
|
self.endScope()
|
||||||
|
|
||||||
|
|
||||||
proc expression(self: Compiler, node: Expression, compile: bool = true): Type {.discardable.} =
|
proc expression(self: Compiler, node: Expression, compile: bool = true): Type {.discardable.} =
|
||||||
## Compiles all expressions
|
## Compiles all expressions
|
||||||
case node.kind:
|
case node.kind:
|
||||||
|
@ -2276,27 +2364,29 @@ proc expression(self: Compiler, node: Expression, compile: bool = true): Type {.
|
||||||
# the node to its true type because that type information
|
# the node to its true type because that type information
|
||||||
# would be lost in the call anyway. The differentiation
|
# would be lost in the call anyway. The differentiation
|
||||||
# happens in self.assignment()
|
# happens in self.assignment()
|
||||||
of setItemExpr, assignExpr:
|
of NodeKind.setItemExpr, NodeKind.assignExpr:
|
||||||
return self.assignment(node, compile).valueType
|
return self.assignment(node, compile).valueType
|
||||||
of identExpr:
|
of NodeKind.identExpr:
|
||||||
return self.identifier(IdentExpr(node), compile=compile).valueType
|
return self.identifier(IdentExpr(node), compile=compile)
|
||||||
of unaryExpr:
|
of NodeKind.unaryExpr:
|
||||||
# Unary expressions such as ~5 and -3
|
# Unary expressions such as ~5 and -3
|
||||||
return self.unary(UnaryExpr(node), compile).valueType
|
return self.unary(UnaryExpr(node), compile)
|
||||||
of groupingExpr:
|
of NodeKind.groupingExpr:
|
||||||
# Grouping expressions like (2 + 1)
|
# Grouping expressions like (2 + 1)
|
||||||
return self.expression(GroupingExpr(node).expression, compile)
|
return self.expression(GroupingExpr(node).expression, compile)
|
||||||
of binaryExpr:
|
of NodeKind.binaryExpr:
|
||||||
# Binary expressions such as 2 ^ 5 and 0.66 * 3.14
|
# Binary expressions such as 2 ^ 5 and 0.66 * 3.14
|
||||||
return self.binary(BinaryExpr(node)).valueType
|
return self.binary(BinaryExpr(node))
|
||||||
of intExpr, hexExpr, binExpr, octExpr, strExpr, falseExpr, trueExpr,
|
of NodeKind.intExpr, NodeKind.hexExpr, NodeKind.binExpr, NodeKind.octExpr,
|
||||||
floatExpr:
|
NodeKind.strExpr, NodeKind.falseExpr, NodeKind.trueExpr, NodeKind.floatExpr:
|
||||||
# Since all of these AST nodes share the
|
# Since all of these AST nodes share the
|
||||||
# same overall structure and the kind
|
# same overall structure and the kind
|
||||||
# field is enough to tell one from the
|
# field is enough to tell one from the
|
||||||
# other, why bother with specialized
|
# other, why bother with specialized
|
||||||
# cases when one is enough?
|
# cases when one is enough?
|
||||||
self.literal(node)
|
return self.literal(node, compile)
|
||||||
|
of NodeKind.lambdaExpr:
|
||||||
|
return self.lambdaExpr(LambdaExpr(node), compile)
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid AST node of kind {node.kind} at expression(): {node} (This is an internal error and most likely a bug)")
|
self.error(&"invalid AST node of kind {node.kind} at expression(): {node} (This is an internal error and most likely a bug)")
|
||||||
|
|
||||||
|
@ -2555,13 +2645,14 @@ proc funDecl(self: Compiler, node: FunDecl, name: Name) =
|
||||||
function.valueType.children.add(name.valueType)
|
function.valueType.children.add(name.valueType)
|
||||||
name.valueType.parent = function.valueType
|
name.valueType.parent = function.valueType
|
||||||
self.currentFunction = name
|
self.currentFunction = name
|
||||||
if self.currentFunction.valueType.isBuiltin:
|
if self.currentFunction.isBuiltin:
|
||||||
self.currentFunction = function
|
self.currentFunction = function
|
||||||
return
|
return
|
||||||
# A function's code is just compiled linearly
|
# A function's code is just compiled linearly
|
||||||
# and then jumped over
|
# and then jumped over
|
||||||
jmp = self.emitJump(JumpForwards, node.token.line)
|
jmp = self.emitJump(JumpForwards, node.token.line)
|
||||||
name.codePos = self.chunk.code.len()
|
name.codePos = self.chunk.code.len()
|
||||||
|
name.valueType.location = name.codePos
|
||||||
# We let our debugger know this function's boundaries
|
# We let our debugger know this function's boundaries
|
||||||
self.chunk.functions.add(self.chunk.code.high().toTriple())
|
self.chunk.functions.add(self.chunk.code.high().toTriple())
|
||||||
self.functions.add((start: self.chunk.code.high(), stop: 0, pos: self.chunk.functions.len() - 3, fn: name))
|
self.functions.add((start: self.chunk.code.high(), stop: 0, pos: self.chunk.functions.len() - 3, fn: name))
|
||||||
|
|
|
@ -9,6 +9,6 @@ fn clock*: float {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn print*[T: Number | string | bool | nan | inf](x: T) {
|
fn print*[T: any](x: T) {
|
||||||
#pragma[magic: "print"]
|
#pragma[magic: "print"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Tests closures
|
# Tests closures
|
||||||
# import std;
|
import std;
|
||||||
|
|
||||||
|
|
||||||
fn makeClosure(x: int): fn: int {
|
fn makeClosure(x: int): fn: int {
|
||||||
|
@ -10,14 +10,7 @@ fn makeClosure(x: int): fn: int {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn makeClosureTwo(y: int): fn: int {
|
print(makeClosure(38)() == 38); # true;
|
||||||
fn inner: int {
|
var closure = makeClosure(42);
|
||||||
return y;
|
print(closure);
|
||||||
}
|
#closure(); # TODO: Fix
|
||||||
return inner;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
makeClosureTwo(38)();
|
|
||||||
|
|
Loading…
Reference in New Issue