implicit return for functions

This commit is contained in:
prod2 2022-02-09 06:47:00 +01:00
parent a16026b364
commit 431e8f9a66
8 changed files with 51 additions and 60 deletions

View File

@ -34,12 +34,6 @@ proc parseArgs(comp: Compiler): int =
proc parseCall(comp: Compiler) =
# ( consumed
# create the call env
# current stack before opCall:
# ... <funct obj> <arg1> <arg2> <arg3>
# opCall converts it to this
# ... <ret val> <arg1> <arg2> <arg3>
let argcount = comp.parseArgs()
# emit call

View File

@ -18,22 +18,17 @@ proc beginScope*(comp: Compiler, function: bool = false) =
when debugCompiler:
debugEcho &"Begin scope called for depth {comp.scopes.len} function? {function}"
if function:
scope.labels.add("result")
scope.labels.add("function")
else:
if not function:
while comp.match(tkLabel):
let label = comp.previous.text[1..^1]
scope.labels.add(label)
if function:
# if it's a function scope, the frame will move
# access to outside locals is also limited to upvalues and closures
comp.stackIndex = 0
else:
# only put the opNil if it's not a function scope, since
# function scopes are initialized by the caller
comp.writeChunk(1, opNil)
else:
# if it's a function scope, the frame will move
# access to outside locals is also limited to upvalues and closures
comp.stackIndex = 0
for label in scope.labels:
comp.addLocal(&":{label}", delta = 0)
@ -48,12 +43,12 @@ proc restore*(comp: Compiler, scope: Scope) =
debugEcho &"Restored scope: delta {delta}"
proc restoreInFunct*(comp: Compiler, scope: Scope) =
let pops = comp.stackIndex
comp.writePops(pops)
#let pops = comp.stackIndex
#comp.writePops(pops)
comp.stackIndex = scope.goalStackIndex
when debugCompiler:
debugEcho &"Restored function scope: delta {pops}; new stackindex: {comp.stackIndex}"
#when debugCompiler:
# debugEcho &"Restored function scope: delta {pops}; new stackindex: {comp.stackIndex}"
proc jumpToEnd*(comp: Compiler, scope: Scope) =
## Jumps to the end of scope, does not affect stackIndex
@ -93,6 +88,10 @@ proc endScope*(comp: Compiler): Scope =
comp.restore(popped)
# patch jumps to after the scope (such jumps from breaks emit the pops before jumping)
for jump in popped.jumps:
when assertionsCompiler:
if function:
# all jumps should only happen to named block expressions
comp.error("Assertion failed: jump attempt to end function.")
comp.patchJump(jump, popped.goalStackIndex)
if function:
comp.writeChunk(0, opReturn)

View File

@ -226,7 +226,12 @@ proc run*(chunk: Chunk): InterpretResult =
if frames.len() == 1:
break
else:
ip = frames.pop().returnIp # remove frame that's over
let retval = stack.pop()
let oldframe = frames.pop()
ip = oldframe.returnIp # remove frame that's over
let length = stack.len() - oldframe.stackBottom
stack.deleteTopN(length)
stack.push(retval)
of opTrue:
stack.add(fromBool(true))
of opFalse:
@ -303,15 +308,8 @@ proc run*(chunk: Chunk): InterpretResult =
runtimeError(&"Wrong number of arguments, expected {arity}, got {argcount}.")
break
of opCall:
# create the call env
# current stack before opCall:
# ... <funct obj> <arg1> <arg2> <arg3>
# opCall converts it to this
# ... <ret val> <arg1> <arg2> <arg3>
let argcount = ip.readUI8()
let funct = stack.getIndexNeg(argcount)
stack.setIndexNeg(argcount, fromNil()) # replace the function with nil: this is the return value slot
funct.call(argcount):
break
of opCreateList:

View File

@ -44,13 +44,13 @@
//expect:5.0
var f = funct() {
var f = funct() { @result
var y = 1;
var z = 3;
var p = 1;
:result = y + (z + p);
{
break @function;
break @result;
};
:result = 10;
};
@ -60,7 +60,7 @@ print (f());
//expect:15.0
f = funct(m, n)
:result = m + n
m + n
;
print (f(f(5, 5), 5));
@ -69,7 +69,7 @@ print (f(f(5, 5), 5));
//expect:10.0
var g = funct()
:result = {@a
{@a
:a = { @b
:b = { @c
:c = 10;
@ -83,7 +83,7 @@ print (g());
//expect:9.0
var h = funct()
:result = {@a
{@a
:a = { @b
:b = { @c
:a = 3;

View File

@ -1,16 +1,16 @@
// cascade
var f1 = funct() {
var f1 = funct() { @result
var x = 1;
var y = 5;
:result = funct() {
:result = funct() { @result
var z = 8;
print (x);
x = x + 1;
:result = funct() {
:result = funct() { @result
print (x);
x = x + 1;
:result = funct() {
:result = funct() { @result
print (x);
print (y);
print (z);
@ -33,7 +33,7 @@ f1()()()()();
// capturing closures in lists:
var f = funct() {
var f = funct() { @result
var y = 5;
var x = @[
funct() print (y),
@ -52,7 +52,7 @@ inst[0]();
// multiple different labels
var f2 = funct() {
var f2 = funct() { @result
var x = { @a @b
// this captures the internal value, not whatever it returns is assigned to
:result = @{
@ -73,7 +73,7 @@ inst2["get"]();
// capturing args
var argcap = funct(n)
:result = funct() print (n)
funct() print (n)
;
//expect:8.1
@ -82,12 +82,12 @@ argcap(8.1)();
// oop: constructors, getters, setters
var newAnimal = funct(species, color) {
var newAnimal = funct(species, color) { @result
var species = species; // copy it so that it's mutable, if args ever get immutable
var animal = @{
["getSpecies"] = funct() :result = species,
["getSpecies"] = funct() species,
["setSpecies"] = funct(newSpecies) species = newSpecies,
["getColor"] = funct() :result = color, // this captures an argument directly
["getColor"] = funct() color, // this captures an argument directly
};
:result = animal;
};
@ -116,7 +116,7 @@ print (turtle["getSpecies"]());
// 1:
var makeClosure = funct(value) {
var makeClosure = funct(value) { @result
var closure = funct() {
print (value);
};
@ -132,9 +132,9 @@ bagel();
// 2: (multi level closures)
var outer = funct() {
var outer = funct() { @result
var x = "value";
var middle = funct() {
var middle = funct() { @result
var inner = funct() {
print (x);
};
@ -157,7 +157,7 @@ in();
// 3: (mixed multi level closures)
outer = funct() {
outer = funct() { @result
var a = 1;
var b = 2;
var result;
@ -170,7 +170,7 @@ outer = funct() {
result = inner;
};
middle();
:result = funct() :result = result;
:result = funct() result;
};
//expect:10.0
@ -238,7 +238,7 @@ globalGet();
// bonus: the last one with a list twist
var bonus = funct() {
var bonus = funct() { @result
var a = 1;
var f = funct() {
print (a);

View File

@ -1,8 +1,8 @@
// a test about collections, WIP
var returnlist = funct() {
:result = @[1, 2, 3, 4];
};
var returnlist = funct()
@[1, 2, 3, 4]
;
//expect:3.0
print (returnlist()[2]);

View File

@ -15,7 +15,7 @@ print (-((3 + 2) * 2) + 1);
// calls and indexes
var returnlist = funct() {
var returnlist = funct() { @result
:result = @[];
:result[0] = 4;
:result[1] = 6;
@ -27,9 +27,9 @@ var returnlist = funct() {
print (returnlist()[2]);
// priority over unary
var truesayer = funct() {
:result = true;
};
var truesayer = funct()
true
;
//expect:false
print (!truesayer());

View File

@ -3,11 +3,11 @@
// :: piping function call
var double = funct(num) :result = num * 2;
var double = funct(num) num * 2;
var four = 2 :: double();
var multiply = funct(num, factor) :result = num * factor;
var multiply = funct(num, factor) num * factor;
var six = 2 :: multiply(3);
@ -65,7 +65,7 @@ class->method;
var multiplier = @{
multiple = 5,
do = funct(self, arg) :result = self.multiple * arg,
do = funct(self, arg) self.multiple * arg,
};
multiplier->do(7) :: print;
@ -74,7 +74,7 @@ multiplier->do(7) :: print;
// -> method call syntax with :: - check for precedence
var returner = @{
method = funct(self) :result = funct(n) :result = n * 2
method = funct(self) funct(n) n * 2
};
1.7 :: returner->method :: print;