Skip to content

Instantly share code, notes, and snippets.

@fowlmouth
Last active January 27, 2021 14:11
Show Gist options
  • Save fowlmouth/3cc1b54bcc5f1e1544259e301b373e78 to your computer and use it in GitHub Desktop.
Save fowlmouth/3cc1b54bcc5f1e1544259e301b373e78 to your computer and use it in GitHub Desktop.
/*
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;
}
/*
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;
}
#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