/*
* Function call evaluation
* (C) 2006, Pascal Schmidt <arena-language@ewetel.net>
* see file ../doc/LICENSE for license
*/
#include <stdlib.h>
#include "eval.h"
/*
* Free all floating arguments
*/
void eval_free_floats(arena_state *s)
{
value **float_args = s->float_args;
value ***vectors = s->vectors;
int i;
for (i = 0; i < s->float_count; i++) {
value_free(float_args[i]);
}
free(s->float_args);
for (i = 0; i < s->vector_count; i++) {
free(vectors[i]);
}
free(s->vectors);
}
/*
* Add allocated function argument to floating list
*/
static void add_float(arena_state *s, value *val)
{
value **float_args = s->float_args;
float_args = oom(realloc(float_args,
(s->float_count + 1) * sizeof(value *)));
float_args[s->float_count++] = val;
s->float_args = float_args;
}
/*
* Add allocated argument vector to floating list
*/
static void add_vector(arena_state *s, value **val)
{
value ***vectors = s->vectors;
vectors = oom(realloc(vectors, (s->vector_count + 1) * sizeof(value **)));
vectors[s->vector_count++] = val;
s->vectors = vectors;
}
/*
* Evaluate function call arguments
*/
void eval_call_args(arena_state *s, unsigned int argc, expr **args,
value ***argv)
{
unsigned int i;
sanity((argc == 0 || args) && argv);
*argv = oom(calloc(sizeof(value *), argc));
add_vector(s, *argv);
for (i = 0; i < argc; i++) {
(*argv)[i] = eval_expr(s, args[i]);
add_float(s, (*argv)[i]);
}
}
/*
* Free function call arguments
*/
void free_call_args(arena_state *s, unsigned int argc, value ***argv)
{
unsigned int i;
sanity(argc == 0 || (argv && *argv));
for (i = 0; i < argc; i++) {
value_free((*argv)[i]);
}
s->float_count -= argc;
free(*argv);
--s->vector_count;
}
/*
* Length of a reference expression
*/
static int reflength(expr *ex)
{
unsigned int i;
int buf = 0;
if (ex->type == EXPR_PASS_REF) {
buf = reflength(ex->inner);
} else if (ex->type == EXPR_REF) {
buf = 1;
} else if (ex->type == EXPR_REF_ARRAY) {
buf = 1;
for (i = 0; i < ex->argc; i++) {
buf += reflength(ex->argv[i]);
}
}
return buf;
}
/*
* Maximum length of occuring references
*/
static int maxlength(unsigned int argc, expr **argv)
{
unsigned int i;
int max = 0, curr;
for (i = 0; i < argc; i++) {
curr = reflength(argv[i]);
if (curr > max) max = curr;
}
return max;
}
/*
* Update references after function call
*/
void update_call_args(arena_state *s, signature *sig, unsigned int argc,
expr **argv)
{
char **names;
expr *ex;
symtab *sym;
symtab_entry *entry;
value *val;
unsigned int i;
int maxref, len;
sanity(sig && (argc == 0 || argv));
sym = symtab_stack_pop(s);
names = sig->data;
maxref = maxlength(argc, argv);
for (len = maxref; len >= 0; len--) {
for (i = 0; i < argc; i++) {
ex = argv[i];
if (reflength(ex) != len) {
continue;
}
if (ex->type != EXPR_PASS_REF ||
(ex->inner->type != EXPR_REF && ex->inner->type != EXPR_REF_ARRAY)) {
continue;
}
entry = symtab_lookup(sym, names[i]);
if (!entry || entry->type == SYMTAB_ENTRY_TMPL) {
continue;
}
if (ex->inner->type == EXPR_REF) {
if (entry->type == SYMTAB_ENTRY_VAR) {
symtab_stack_add_variable(s, ex->inner->name, entry->entry_u.var);
} else {
symtab_stack_add_function(s, ex->inner->name, entry->entry_u.sig);
}
} else {
if (entry->type == SYMTAB_ENTRY_VAR) {
eval_assign_array_direct(s, ex->inner->name,
ex->inner->argc, ex->inner->argv, entry->entry_u.var);
} else {
val = value_make_fn(entry->entry_u.sig);
eval_assign_array_direct(s, ex->inner->name,
ex->inner->argc, ex->inner->argv, val);
value_free(val);
}
}
}
}
symtab_free(sym);
}
/*
* Evaluate function call
*/
value *eval_call(arena_state *s, expr *ex)
{
symtab_entry *entry;
signature *sig;
value *res = NULL;
value **argv;
sanity(ex && ex->name);
entry = symtab_stack_lookup(s, ex->name);
if (!entry || entry->type == SYMTAB_ENTRY_TMPL ||
(entry->type == SYMTAB_ENTRY_VAR &&
entry->entry_u.var->type != VALUE_TYPE_FN)) {
fatal(s, "call to undefined function `%s'", ex->name);
return value_make_void();
}
if (entry->type == SYMTAB_ENTRY_FUNCTION) {
sig = entry->entry_u.sig;
} else {
sig = entry->entry_u.var->value_u.fn_val;
}
eval_call_args(s, ex->argc, ex->argv, &argv);
if (entry->entry_u.sig->type == FUNCTION_TYPE_BUILTIN) {
res = call_function(s, sig, ex->argc, argv);
} else {
symtab_stack_enter(s);
res = call_function(s, sig, ex->argc, argv);
update_call_args(s, sig, ex->argc, ex->argv);
}
free_call_args(s, ex->argc, &argv);
return res;
}
syntax highlighted by Code2HTML, v. 0.9.1