Last active
January 27, 2021 14:11
-
-
Save fowlmouth/3cc1b54bcc5f1e1544259e301b373e78 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
an example of basic closures | |
*/ | |
#include "runtime.h" | |
#include <memory> | |
using closure_fn = value(*)(int, value*, void*); | |
struct closure : object | |
{ | |
closure_fn fn; | |
std::unique_ptr< char[] > data; | |
template< typename T > | |
T* data_cast() const | |
{ | |
return reinterpret_cast< T* >(data.get()); | |
} | |
value apply(int argc, value* argv) const | |
{ | |
return fn(argc, argv, data.get()); | |
} | |
}; | |
struct counter_data_t | |
{ | |
intptr_t next_id; | |
}; | |
value fn_counter(int argc, value* argv, void* data) | |
{ | |
counter_data_t* counter_data = reinterpret_cast< counter_data_t* >(data); | |
return counter_data->next_id++; | |
} | |
int main() | |
{ | |
closure c; | |
c.fn = fn_counter; | |
c.data = std::make_unique< char[] >(sizeof(counter_data_t)); | |
counter_data_t* data = c.data_cast< counter_data_t >(); | |
data->next_id = 0; | |
c.apply(0, nullptr); | |
c.apply(0, nullptr); | |
c.apply(0, nullptr); | |
assert(data->next_id == 3); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
a closure implementation that encloses over variables. | |
here we implement a nested function that encloses a local variable | |
function(x) { | |
let y = function(){ | |
return x = x * 2 | |
} | |
return y | |
} | |
*/ | |
#include <memory> | |
#include "runtime.h" | |
// upvalue stores a value which can be a variable on the stack | |
// or an enclosed value which lives in the upvalue itself | |
struct upvalue | |
{ | |
value* val; | |
value closed_val; | |
upvalue(value* val) | |
: val(val) | |
{ | |
} | |
upvalue() | |
: val(nullptr) | |
{ | |
} | |
bool is_closed() const | |
{ | |
return val == &closed_val; | |
} | |
void close_value() | |
{ | |
closed_val = *val; | |
val = &closed_val; | |
} | |
void set_value(value new_value) | |
{ | |
*val = new_value; | |
} | |
value get_value() const | |
{ | |
return *val; | |
} | |
}; | |
using upvalue_ref = std::shared_ptr< upvalue >; | |
// this helper object closes an upvalue when the scope of its variable ends | |
struct upvalue_scope | |
{ | |
upvalue_ref& upvalue; | |
upvalue_scope(upvalue_ref& upvalue) | |
: upvalue(upvalue) | |
{ | |
} | |
~upvalue_scope() | |
{ | |
upvalue->close_value(); | |
} | |
}; | |
using closure_fn = value(*)(int, value*, upvalue_ref*); | |
struct closure : object | |
{ | |
closure_fn fn; | |
int upvalue_count; | |
std::unique_ptr< upvalue_ref[] > upvalues; | |
closure(closure_fn fn, int upvalue_count) | |
: fn(fn), upvalue_count(0) | |
{ | |
if((upvalues = std::make_unique< upvalue_ref[] >(upvalue_count))) | |
{ | |
this->upvalue_count = upvalue_count; | |
} | |
} | |
value apply(int argc, value* argv) const | |
{ | |
return fn(argc, argv, upvalues.get()); | |
} | |
}; | |
value fn_inner(int, value*, upvalue_ref* data) | |
{ | |
upvalue_ref& x = data[0]; | |
x->set_value(x->get_value().get_int() * 2); | |
return x->get_value(); | |
} | |
value fn_outer(int, value* argv, upvalue_ref*) | |
{ | |
upvalue_ref upvalue_x = std::make_shared< upvalue >(&argv[0]); | |
upvalue_scope _(upvalue_x); | |
closure* cl = new closure(fn_inner, 1); | |
cl->upvalues[0] = upvalue_x; | |
cl->apply(0, nullptr); | |
return cl; | |
} | |
#include <iostream> | |
int main() | |
{ | |
closure outer(fn_outer, 0); | |
value argv[2]; | |
argv[0] = 3; | |
value fn = outer.apply(1, argv); | |
std::cout << "x = " << argv[0].get_int() << std::endl; | |
closure* cls = fn.object_cast< closure >(); | |
for(int i = 0; i < 3; ++i) | |
std::cout << cls->apply(0, nullptr).get_int() << std::endl; | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <cstdint> | |
struct object | |
{ | |
}; | |
struct value | |
{ | |
object* data; | |
value(intptr_t num) | |
{ | |
set_int(num); | |
} | |
value(object* obj) | |
: data(obj) | |
{ | |
} | |
value() | |
: value(intptr_t(0)) | |
{ | |
} | |
bool is_int() const | |
{ | |
return (intptr_t)data & 1; | |
} | |
intptr_t get_int() const | |
{ | |
return (intptr_t)data >> 1; | |
} | |
intptr_t set_int(intptr_t num) | |
{ | |
data = reinterpret_cast< object* >((num << 1) | 1); | |
return num; | |
} | |
template< typename T > | |
T* object_cast() const | |
{ | |
return static_cast< T* >(data); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment