217 lines
6.9 KiB
C
217 lines
6.9 KiB
C
|
|
#include <assert.h>
|
|
|
|
#include <debug.h>
|
|
|
|
#include <value/struct.h>
|
|
|
|
#include <value/environment/new.h>
|
|
#include <value/environment/define.h>
|
|
#include <value/environment/lookup.h>
|
|
|
|
#include <value/quote/new.h>
|
|
|
|
#include <value/list/new.h>
|
|
|
|
#include <gc/inc_external_refcount.h>
|
|
#include <gc/dec_external_refcount.h>
|
|
|
|
#include "expand_macro.h"
|
|
|
|
struct value* expand_macro(
|
|
struct value* value,
|
|
struct value* environment,
|
|
struct gc* gc)
|
|
{
|
|
struct value* retval;
|
|
ENTER;
|
|
|
|
switch (value->kind)
|
|
{
|
|
case vk_null:
|
|
case vk_integer:
|
|
case vk_boolean:
|
|
case vk_string:
|
|
{
|
|
dputs("case null, integer, boolean, string:");
|
|
|
|
retval = gc_inc_external_refcount(gc, value);
|
|
|
|
break;
|
|
}
|
|
|
|
case vk_identifier:
|
|
{
|
|
dputs("case vk_identifier:");
|
|
|
|
struct string* name = value->subclass.identifier.name;
|
|
|
|
struct value* result = environment_value_lookup(environment, name);
|
|
|
|
// if the lookup fails, don't change the variable
|
|
retval = result ?: gc_inc_external_refcount(gc, value);
|
|
|
|
break;
|
|
}
|
|
|
|
case vk_list:
|
|
{
|
|
dputs("case vk_list:");
|
|
|
|
struct list_value* specific = &value->subclass.list;
|
|
|
|
struct value* first = expand_macro(specific->first, environment, gc);
|
|
|
|
switch (first->kind)
|
|
{
|
|
case vk_builtin_lambda:
|
|
{
|
|
struct builtin_lambda_value* builtin =
|
|
&first->subclass.builtin_lambda;
|
|
|
|
retval = (builtin->funcptr)(
|
|
/* list: */ specific->rest,
|
|
/* environment: */ environment,
|
|
/* garbage collector: */ gc,
|
|
/* evaluate function: */ expand_macro);
|
|
|
|
break;
|
|
}
|
|
|
|
case vk_user_lambda:
|
|
{
|
|
struct user_lambda_value* lambda =
|
|
&first->subclass.user_lambda;
|
|
|
|
struct value* parameter_environment = new_environment_value(
|
|
/* garbage collection: */ gc,
|
|
/* fallback environment: */ lambda->environment);
|
|
|
|
struct value *parameter_names, *parameter_expressions;
|
|
|
|
for (parameter_names = lambda->parameters,
|
|
parameter_expressions = specific->rest;
|
|
parameter_names->kind == vk_list &&
|
|
parameter_expressions->kind == vk_list;
|
|
parameter_names = parameter_names->subclass.list.rest,
|
|
parameter_expressions = parameter_expressions->subclass.list.rest)
|
|
{
|
|
struct value* parameter_name = parameter_names->subclass.list.first;
|
|
|
|
struct value* parameter_expression = parameter_expressions->subclass.list.first;
|
|
|
|
if (parameter_name->kind != vk_identifier)
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
|
|
struct value* parameter_value = expand_macro(
|
|
/* expression: */ parameter_expression,
|
|
/* environment: */ environment,
|
|
/* garbage collection: */ gc);
|
|
|
|
environment_value_define(
|
|
/* environment: */ parameter_environment,
|
|
/* name: */ parameter_name->subclass.identifier.name,
|
|
/* value: */ parameter_value);
|
|
|
|
gc_dec_external_refcount(gc, parameter_value);
|
|
}
|
|
|
|
if (false
|
|
|| parameter_names->kind == vk_list
|
|
|| parameter_expressions->kind == vk_list)
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
|
|
retval = expand_macro(lambda->body, parameter_environment, gc);
|
|
|
|
gc_dec_external_refcount(gc, parameter_environment);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
retval = new_list_value(gc, first);
|
|
|
|
struct value* tail = gc_inc_external_refcount(gc, retval);
|
|
|
|
struct value* link;
|
|
|
|
for (link = specific->rest;
|
|
link->kind == vk_list;
|
|
link = link->subclass.list.rest)
|
|
{
|
|
struct value* expanded = expand_macro(
|
|
/* element: */ link->subclass.list.first,
|
|
/* environment: */ environment,
|
|
/* garbage_collector: */ gc);
|
|
|
|
struct value* new = new_list_value(gc, expanded);
|
|
|
|
tail->subclass.list.rest = new;
|
|
gc_dec_external_refcount(gc, tail);
|
|
tail = gc_inc_external_refcount(gc, new);
|
|
|
|
gc_dec_external_refcount(gc, new);
|
|
|
|
gc_dec_external_refcount(gc, expanded);
|
|
}
|
|
|
|
assert(link->kind == vk_null);
|
|
|
|
tail->subclass.list.rest = link;
|
|
|
|
gc_dec_external_refcount(gc, tail);
|
|
break;
|
|
}
|
|
}
|
|
|
|
gc_dec_external_refcount(gc, first);
|
|
|
|
break;
|
|
}
|
|
|
|
case vk_quote:
|
|
{
|
|
dputs("case vk_quote:");
|
|
|
|
struct quote_value* specific = &value->subclass.quote;
|
|
|
|
struct value* expanded_subexpression = expand_macro(
|
|
/* expression: */ specific->subexpression,
|
|
/* environment: */ environment,
|
|
/* gc: */ gc);
|
|
|
|
retval = new_quote_value(gc, expanded_subexpression);
|
|
|
|
gc_dec_external_refcount(gc, expanded_subexpression);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
TODO;
|
|
break;
|
|
}
|
|
|
|
EXIT;
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|