Super clunky lambda implementation

This commit is contained in:
Mattia Giambirtone 2023-03-05 16:49:14 +01:00
parent 97698b28af
commit e926696953
2 changed files with 28 additions and 46 deletions

View File

@ -543,6 +543,8 @@ proc toIntrinsic*(name: string): Type =
## otherwise ## otherwise
if name == "any": if name == "any":
return Type(kind: Any) return Type(kind: Any)
elif name == "all":
return Type(kind: All)
elif name == "auto": elif name == "auto":
return Type(kind: Auto) return Type(kind: Auto)
elif name in ["int", "int64", "i64"]: elif name in ["int", "int64", "i64"]:

View File

@ -86,6 +86,7 @@ type
# The topmost occupied stack slot # The topmost occupied stack slot
# in the current frame (0-indexed) # in the current frame (0-indexed)
stackIndex: int stackIndex: int
lambdas: seq[LambdaExpr]
# Forward declarations # Forward declarations
@ -119,6 +120,7 @@ proc newBytecodeCompiler*(replMode: bool = false): BytecodeCompiler =
result.depth = 0 result.depth = 0
result.lines = @[] result.lines = @[]
result.jumps = @[] result.jumps = @[]
result.lambdas = @[]
result.currentFunction = nil result.currentFunction = nil
result.replMode = replMode result.replMode = replMode
result.currentModule = nil result.currentModule = nil
@ -719,10 +721,10 @@ proc handleMagicPragma(self: BytecodeCompiler, pragma: Pragma, name: Name) =
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()
if name.valueType.isNil():
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)
if name.valueType.isNil():
self.error("'magic' pragma: wrong argument value", pragma.args[0])
name.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")
@ -1337,13 +1339,11 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
result = self.specialize(result, argExpr) result = self.specialize(result, argExpr)
if impl.valueType.isAuto: if impl.valueType.isAuto:
impl = self.prepareAutoFunction(impl, args) impl = self.prepareAutoFunction(impl, args)
result = impl.valueType result = impl.valueType
if not impl.valueType.compiled: if result.fun.kind == NodeKind.lambdaExpr:
case result.fun.kind: self.lambdaExpr(LambdaExpr(result.fun), compile=compile)
of NodeKind.lambdaExpr: elif not impl.valueType.compiled:
self.lambdaExpr(LambdaExpr(result.fun), compile=compile) self.funDecl(FunDecl(result.fun), impl)
else:
self.funDecl(FunDecl(result.fun), impl)
result = result.returnType result = result.returnType
if compile: if compile:
if impl.valueType.fun.kind == funDecl and FunDecl(impl.valueType.fun).isTemplate: if impl.valueType.fun.kind == funDecl and FunDecl(impl.valueType.fun).isTemplate:
@ -1391,14 +1391,13 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
self.generateCall(impl, argExpr, node.token.line) self.generateCall(impl, argExpr, node.token.line)
of NodeKind.lambdaExpr: of NodeKind.lambdaExpr:
var node = LambdaExpr(node.callee) var node = LambdaExpr(node.callee)
var impl = self.lambdaExpr(node, compile=false) var impl = self.lambdaExpr(node, compile=compile)
if impl.isAuto: if impl.isAuto:
impl = self.prepareAutoLambda(impl, args) impl = self.prepareAutoLambda(impl, args)
result = impl result = impl
result = result.returnType result = result.returnType
if compile: if compile:
self.generateCall(impl, argExpr, node.token.line) self.generateCall(impl, argExpr, node.token.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():
@ -1459,30 +1458,17 @@ proc blockStmt(self: BytecodeCompiler, node: BlockStmt, compile: bool = true) =
method lambdaExpr(self: BytecodeCompiler, node: LambdaExpr, compile: bool = true): Type {.discardable.} = method lambdaExpr(self: BytecodeCompiler, node: LambdaExpr, compile: bool = true): Type {.discardable.} =
## Compiles lambda functions as expressions ## Compiles lambda functions as expressions
result = Type(kind: Function, isLambda: true, fun: node, location: 0) result = Type(kind: Function, isLambda: true, fun: node, location: 0, compiled: true)
let function = self.currentFunction let function = self.currentFunction
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 default: Expression
var name: Name var name: Name
var i = 0 var i = 0
let stackIdx = self.stackIndex
self.stackIndex = 2
for argument in node.arguments: for argument in node.arguments:
if self.names.high() > 16777215: if self.names.high() > 16777215:
self.error("cannot declare more than 16777215 variables at a time") self.error("cannot declare more than 16777215 variables at a time")
name = Name(depth: self.depth, name = Name(depth: self.depth + 1,
isPrivate: true, isPrivate: true,
owner: self.currentModule, owner: self.currentModule,
file: self.currentModule.file, file: self.currentModule.file,
@ -1494,10 +1480,12 @@ method lambdaExpr(self: BytecodeCompiler, node: LambdaExpr, compile: bool = true
line: argument.name.token.line, line: argument.name.token.line,
belongsTo: nil, # TODO belongsTo: nil, # TODO
kind: NameKind.Argument, kind: NameKind.Argument,
node: argument.name node: argument.name,
position: self.stackIndex
) )
if compile: if compile:
self.names.add(name) self.names.add(name)
inc(self.stackIndex)
if node.arguments.high() - node.defaults.high() <= node.arguments.high(): if node.arguments.high() - node.defaults.high() <= node.arguments.high():
# There's a default argument! # There's a default argument!
result.args.add((name.ident.token.lexeme, name.valueType, node.defaults[i])) result.args.add((name.ident.token.lexeme, name.valueType, node.defaults[i]))
@ -1513,23 +1501,16 @@ method lambdaExpr(self: BytecodeCompiler, node: LambdaExpr, compile: bool = true
isConst: false, isConst: false,
owner: self.currentModule, owner: self.currentModule,
file: self.file, file: self.file,
valueType: Type(kind: Function, valueType: result,
returnType: result.returnType,
args: result.args,
fun: node,
forwarded: false,
isAuto: false),
ident: nil, ident: nil,
node: node, node: node,
isLet: false, isLet: false,
line: node.token.line, line: node.token.line,
kind: NameKind.Function, kind: NameKind.Function,
belongsTo: self.currentFunction, belongsTo: function,
isReal: true) isReal: true)
if compile: if compile and node notin self.lambdas:
result.compiled = true self.lambdas.add(node)
let stackIdx = self.stackIndex
self.stackIndex = name.position
let jmp = self.emitJump(JumpForwards, node.token.line) let jmp = self.emitJump(JumpForwards, node.token.line)
if BlockStmt(node.body).code.len() == 0: if BlockStmt(node.body).code.len() == 0:
self.error("cannot construct lambda with empty body") self.error("cannot construct lambda with empty body")
@ -1560,7 +1541,6 @@ method lambdaExpr(self: BytecodeCompiler, node: LambdaExpr, compile: bool = true
hasVal = hasVal and not typ.isNil() hasVal = hasVal and not typ.isNil()
for jump in self.currentFunction.valueType.retJumps: for jump in self.currentFunction.valueType.retJumps:
self.patchJump(jump) self.patchJump(jump)
self.endScope()
# Terminates the function's context # Terminates the function's context
self.emitByte(OpCode.Return, self.peek().token.line) self.emitByte(OpCode.Return, self.peek().token.line)
if hasVal: if hasVal:
@ -1570,13 +1550,13 @@ method lambdaExpr(self: BytecodeCompiler, node: LambdaExpr, compile: bool = true
# Well, we've compiled everything: time to patch # Well, we've compiled everything: time to patch
# the jump offset # the jump offset
self.patchJump(jmp) self.patchJump(jmp)
# Restores the enclosing function (if any).
# Makes nested calls work (including recursion)
self.currentFunction = function
self.stackIndex = stackIdx
self.endScope()
self.emitByte(LoadUInt64, node.token.line) self.emitByte(LoadUInt64, node.token.line)
self.emitBytes(self.chunk.writeConstant(result.location.toLong()), node.token.line) self.emitBytes(self.chunk.writeConstant(result.location.toLong()), node.token.line)
self.endScope()
# Restores the enclosing function (if any).
# Makes nested calls work (including recursion)
self.currentFunction = function
self.stackIndex = stackIdx
method expression(self: BytecodeCompiler, node: Expression, compile: bool = true): Type {.discardable.} = method expression(self: BytecodeCompiler, node: Expression, compile: bool = true): Type {.discardable.} =