- macros work in the new way (the "right" way?) - added test.py script, with basic test cases. Need to fill out new test builtins.
280 lines
9 KiB
C
280 lines
9 KiB
C
|
|
#include <assert.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <debug.h>
|
|
|
|
#include <string/struct.h>
|
|
|
|
#include <gc/inc_external_refcount.h>
|
|
#include <gc/dec_external_refcount.h>
|
|
|
|
#include <stringtree/new.h>
|
|
#include <stringtree/prepend.h>
|
|
#include <stringtree/println.h>
|
|
#include <stringtree/free.h>
|
|
|
|
#include <value/struct.h>
|
|
|
|
#include <value/user_lambda/new.h>
|
|
#include <value/environment/new.h>
|
|
#include <value/environment/define.h>
|
|
#include <value/environment/lookup.h>
|
|
#include <value/prettyprint.h>
|
|
|
|
#include "evaluate.h"
|
|
|
|
struct value* evaluate(
|
|
struct value* value,
|
|
struct value* environment,
|
|
struct gc* gc,
|
|
bool verbose,
|
|
size_t verbose_print_depth)
|
|
{
|
|
ENTER;
|
|
|
|
if (verbose)
|
|
{
|
|
struct stringtree* tree = value_prettyprint(value);
|
|
|
|
stringtree_prepend_string_const(tree, "evaluate: ");
|
|
|
|
for (size_t i = 0; i < verbose_print_depth; i++)
|
|
{
|
|
stringtree_prepend_string_const(tree, " ");
|
|
}
|
|
|
|
stringtree_println(tree, stdout);
|
|
|
|
free_stringtree(tree);
|
|
}
|
|
|
|
struct value* result;
|
|
|
|
switch (value->kind)
|
|
{
|
|
case vk_null:
|
|
case vk_boolean:
|
|
case vk_integer:
|
|
case vk_user_lambda:
|
|
case vk_builtin_lambda:
|
|
{
|
|
result = gc_inc_external_refcount(gc, value);
|
|
|
|
break;
|
|
}
|
|
|
|
case vk_identifier:
|
|
{
|
|
struct identifier_value* specific = &value->subclass.identifier;
|
|
|
|
result = environment_value_lookup(environment, specific->name);
|
|
|
|
if (!result)
|
|
{
|
|
fprintf(stderr, "unknown variable: %s\n", specific->name->data);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case vk_list:
|
|
{
|
|
struct list_value* specific = &value->subclass.list;
|
|
|
|
struct value* first = evaluate(
|
|
/* expression: */ specific->first,
|
|
/* environment: */ environment,
|
|
/* garbage collector: */ gc,
|
|
/* verbose? */ verbose,
|
|
/* verbose print-depth: */ verbose_print_depth + 1);
|
|
|
|
switch (first->kind)
|
|
{
|
|
case vk_builtin_lambda:
|
|
{
|
|
result = (first->subclass.builtin_lambda.funcptr)(
|
|
/* arguments: */ specific->rest,
|
|
/* environment: */ environment,
|
|
/* garbage collector: */ gc,
|
|
/* verbose?: */ verbose,
|
|
/* verbose print-depth: */ verbose_print_depth + 1);
|
|
|
|
break;
|
|
}
|
|
|
|
case vk_user_lambda:
|
|
{
|
|
struct user_lambda_value* lambda =
|
|
&first->subclass.user_lambda;
|
|
|
|
bool is_macro = lambda->is_macro;
|
|
|
|
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;
|
|
|
|
// shortcut:
|
|
struct string* const name =
|
|
parameter_name->subclass.identifier.name;
|
|
|
|
if (parameter_name->kind != vk_identifier)
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
|
|
if (is_macro)
|
|
{
|
|
environment_value_define(
|
|
/* environment: */ parameter_environment,
|
|
/* name: */ name,
|
|
/* value: */ parameter_expression);
|
|
}
|
|
else
|
|
{
|
|
struct value* parameter_value = evaluate(
|
|
/* expression: */ parameter_expression,
|
|
/* environment: */ environment,
|
|
/* garbage collection: */ gc,
|
|
/* verbose? */ verbose,
|
|
/* verbose print-depth: */ verbose_print_depth + 1);
|
|
|
|
environment_value_define(
|
|
/* environment: */ parameter_environment,
|
|
/* name: */ name,
|
|
/* value: */ parameter_value);
|
|
|
|
gc_dec_external_refcount(gc, parameter_value);
|
|
}
|
|
}
|
|
|
|
if (parameter_names->kind == vk_list)
|
|
{
|
|
assert(parameter_expressions->kind == vk_null);
|
|
|
|
result = new_user_lambda_value_given_macro(
|
|
/* garbage collection: */ gc,
|
|
/* environment: */ parameter_environment,
|
|
/* parameter names: */ parameter_names,
|
|
/* body: */ lambda->body,
|
|
/* is macro?: */ lambda->is_macro);
|
|
}
|
|
else if (parameter_expressions->kind == vk_list)
|
|
{
|
|
assert(parameter_names->kind == vk_null);
|
|
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
else if (is_macro)
|
|
{
|
|
struct value* expand_macro =
|
|
evaluate(
|
|
/* expression: */ lambda->body,
|
|
/* environment: */ parameter_environment,
|
|
/* garbage collector: */ gc,
|
|
/* verbose? */ verbose,
|
|
/* verbose print-depth: */ verbose_print_depth + 1);
|
|
|
|
result = evaluate(
|
|
/* expression: */ expand_macro,
|
|
/* environment: */ environment,
|
|
/* garbage collector: */ gc,
|
|
/* verbose? */ verbose,
|
|
/* verbose print-depth: */ verbose_print_depth + 1);
|
|
|
|
gc_dec_external_refcount(gc, expand_macro);
|
|
}
|
|
else
|
|
{
|
|
result = evaluate(
|
|
/* expression: */ lambda->body,
|
|
/* environment: */ parameter_environment,
|
|
/* garbage collector: */ gc,
|
|
/* verbose? */ verbose,
|
|
/* verbose print-depth: */ verbose_print_depth + 1);
|
|
}
|
|
|
|
gc_dec_external_refcount(gc, parameter_environment);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
gc_dec_external_refcount(gc, first);
|
|
|
|
break;
|
|
}
|
|
|
|
case vk_quote:
|
|
{
|
|
struct quote_value* specific = &value->subclass.quote;
|
|
|
|
result = gc_inc_external_refcount(gc, specific->subexpression);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
dpvu(value->kind);
|
|
|
|
TODO;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (verbose)
|
|
{
|
|
struct stringtree* tree = value_prettyprint(result);
|
|
|
|
stringtree_prepend_string_const(tree, "result: ");
|
|
|
|
for (size_t i = 0; i < verbose_print_depth; i++)
|
|
{
|
|
stringtree_prepend_string_const(tree, " ");
|
|
}
|
|
|
|
stringtree_println(tree, stdout);
|
|
|
|
free_stringtree(tree);
|
|
}
|
|
|
|
EXIT;
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|