src/ast/interpreter/interpreter.c
A generic C Interpreter
This file defines the function
which interprets
the code defined by n
, an Abstract Syntax Tree (AST)
obtained by parsing a Basilisk C code using the AST Library.
The root
parameter is the root of the AST associated
with n
(they can be identical or different).
The verbosity
parameter defines the level of the
messages displayed by the interpreter (“errors”, “warnings”, etc. see
below).
The maxcalls
parameter defines the maximum number of
(recursive) calls made by the interpreter. This is a rough measure of
the maximum number of instructions which will be interpreted and can be
used to limit the time taken by the interpreter. A negative value means
no limit.
The user_data
parameter is a pointer passed to the ‘hook
functions’.
Two special functions can be used within the interpreted code to display local information during interpretation.
interpreter_verbosity (int verbosity)
: sets the level of verbosity within the (local) scope of this call.display_value (expression)
: displays the internal representation ofexpression
.
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <complex.h>
#include <limits.h>
#include "ast.h"
#include "symbols.h"
#include "khash.h"
(INT64, void *)
KHASH_MAP_INIT_INT64
#if 0 // for debugging memory accesses with valgrind
# define allocate(alloc, size) calloc (1, size)
#endif
static unsigned
= 32; // INT_MAX;
maximum_iterations
static const unsigned
= 2,
maximum_recursion = false,
display_pointers
= 1,
error_verbosity = 2,
warning_verbosity = 3,
function_verbosity = 4;
expression_verbosity
enum {
= 1 << 0,
unset = 1 << 1
constant_expression };
typedef struct {
void * p;
char * start;
int size, pscope;
} Pointer;
typedef struct _Value Value;
struct _Value {
AstTerminal n;
Ast * type;
Pointer data; // fixme: should just be a void *
int size, pointer, vscope;
int * dimension;
This could be used in principle to “unset” the values of array members accessed with an undefined index, but this breaks some test cases in a non-trivial way. The relevant test case is test26.c.
#if UNSET_ARRAY
Value * unset_array;
Ast * unset_member;
#endif
};
staticvoid display_value (const Value * v);
Value * ast_run_node (Ast * n, Stack * stack);
extern Value * (* run) (Ast *, Stack *);
extern void (* after_run) (Ast *, Stack *);
extern Value * (* ast_choose_hook) (Ast *, Stack *, Value *, Value *);
extern void (* ast_value_print_hook) (const Value * v, FILE * fp);
extern Value * (* ast_binary_operation_hook) (Ast *, Stack *, Value *, Value *, Value *);
extern Value * (* ast_internal_functions_hook) (Ast * call, Ast * identifier, Value ** parameters,
Stack * stack, Value * value);
Value * (* ast_assign_hook) (Ast * n, Value * dst, Value * src, Stack * stack) = NULL;
typedef struct {
Allocator * alloc, * static_alloc;
int verbosity, maxcalls, warnings, scope, call;
(INT64) * messages, * nonlocals;
khash_tint conditional;
Ast * root, * stencil, * end_stencil;
void * data;
} StackData;
staticAllocator * stack_alloc (const Stack * s)
{
return ((StackData *)stack_get_data (s))->alloc;
}
staticAllocator * stack_static_alloc (const Stack * s)
{
return ((StackData *)stack_get_data (s))->static_alloc;
}
staticint stack_verbosity (const Stack * s)
{
return ((StackData *)stack_get_data (s))->verbosity;
}
staticvoid * interpreter_get_data (const Stack * s)
{
return ((StackData *)stack_get_data (s))->data;
}
typedef struct {
int pointer, size;
int * dimension;
} Dimensions;
staticPointer stack_allocate (Stack * stack, int size)
{
Pointer p;
.p = allocate (stack_alloc (stack), size);
p.start = p.p;
p.size = size;
p.pscope = ((StackData *)stack_get_data (stack))->scope;
preturn p;
}
staticbool is_new_message (const Ast * n, const Stack * stack)
{
(INT64) * messages = ((StackData *)stack_get_data (stack))->messages;
khash_tkhiter_t k = kh_get (INT64, messages, (long) n);
return (k == kh_end (messages));
}
staticvoid * message (const Ast * scope, const Ast * n, const char * msg, int level, Stack * stack)
{
if (stack_verbosity (stack) >= level && is_new_message (n, stack)) {
AstTerminal * t = ast_left_terminal (n);
char * s = ast_str_append (n, NULL);
fprintf (stderr, "%s:%d: %s (interpreter): ", t->file, t->line,
== error_verbosity ? "error" :
level == warning_verbosity ? "warning" :
level "info");
fprintf (stderr, msg, ast_crop_before (s));
free (s);
int ret;
(INT64, ((StackData *)stack_get_data (stack))->messages, (long) n, &ret);
kh_put assert (ret > 0);
}
assert (!scope);
if (level <= warning_verbosity)
((StackData *)stack_get_data (stack))->warnings++;
return NULL;
}
static inline
void * not_implemented_internal (Ast * scope, Ast * n, const char * file, int line, Stack * stack)
{
if (stack_verbosity (stack) > error_verbosity) {
char * s = ast_str_append (n, NULL);
fprintf (stderr, "%s:%d: warning (interpreter): operation '%s' not implemented\n",
, line, ast_crop_before (s));
filefree (s);
}
assert (!scope);
return NULL;
}
#define not_implemented(scope, n, stack) not_implemented_internal (scope, n, __FILE__, __LINE__, stack)
#define value_data(v, type) (*((type *)(v)->data.p))
typedef char Flags;
static inline
bool has_flags (const Value * v)
{
return v && (v->pointer || (v->type->sym != sym_struct_or_union_specifier &&
->type->sym != sym_CHAR));
v}
static inline
Flags value_flags (const Value * v)
{
if (!has_flags (v))
return 0;
Flags f = *((Flags *)(((char *)v->data.p) + v->size - sizeof (Flags)));
#if UNSET_ARRAY
if (v->unset_array)
|= unset;
f #endif
return f;
}
static inline
void value_unset_flags (Value * v, Flags flags)
{
if (has_flags (v))
*((Flags *)(((char *)v->data.p) + v->size - sizeof (Flags))) &= ~flags;
}
static inline
void value_set_flags (Value * v, Flags flags)
{
if (has_flags (v)) {
*((Flags *)(((char *)v->data.p) + v->size - sizeof (Flags))) |= flags;
if (flags & unset)
value_unset_flags (v, constant_expression);
}
}
static inline
bool is_constant_expression (const Value * v)
{
// return v->flags & constant_expression;
return value_flags (v) & constant_expression;
}
static inline
void set_constant_expression (Value * v)
{
// v->flags |= constant_expression;
value_set_flags (v, constant_expression);
}
staticValue * struct_member_value (Ast * n, Value * value, Ast * member, int index, Stack * stack);
staticvoid unset_value (Value * value, Stack * stack)
{
if (value->pointer == 0 && value->type->sym == sym_struct_or_union_specifier) {
int index = 0;
Value * v;
while ((v = struct_member_value ((Ast *)value, value, NULL, index++, stack)))
unset_value (v, stack);
}
elsevalue_set_flags (value, unset);
}
static inline
void value_set_write (Value * v, Stack * stack)
{
StackData * d = stack_get_data (stack);
#if 0
if (d->nonlocals && stack_verbosity (stack) > 1) {
fprintf (stderr, "conditional: %d stack->scope: %d considering:\n", d->conditional, d->scope);
display_value (v);
}
#endif
if (d->nonlocals && v->vscope <= d->conditional // value is not local to the conditional
#if 1
Ignore pointers except function pointers : this is necessary
for test16.c (i.e. "undefined" allocations of scalars).
&& (!v->pointer || (v->pointer == 1 && v->type->sym == sym_function_definition))
#endif
) {
int ret;
khiter_t k = kh_put (INT64, d->nonlocals, (long) v->data.p, &ret);
if (ret) { // new key
char * p = malloc (sizeof (Value) + v->size);
(p, v, sizeof (Value));
memcpy (p + sizeof (Value), v->data.p, v->size);
memcpy (d->nonlocals, k) = p;
kh_value #if 0
if (stack_verbosity (stack) > 1) {
// ast_print_tree ((Ast *)v, stderr, 0, 0, -1);
display_value (v);
fprintf (stderr, "conditional: %d stack->scope: %d\n", d->conditional, d->scope);
}
#endif
}
}
}
staticvoid value_set_parent (Value * v, const Ast * parent)
{
if (!v)
return;
(v, parent, ast_terminal (parent) ? sizeof (AstTerminal) : sizeof (Ast));
memcpy ((AstTerminal *) v)->value = v;
}
void (* ast_value_print_hook) (const Value * v, FILE * fp) = NULL;
staticvoid value_print (const Value * v, FILE * fp)
{
if (!v) {
('!', fp);
fputc return;
}
if (v->pointer) {
if (v->type->sym == sym_function_definition && v->pointer == 1) {
Ast * n = value_data (v, Ast *);
if (n) {
AstTerminal * t = ast_left_terminal (n);
char * s = ast_str_append (n, NULL);
fprintf (fp, "%s (%s:%d)", ast_crop_before (s), t->file, t->line);
free (s);
}
else("(fnull)", fp);
fputs }
else if (display_pointers)
fprintf (fp, "%p", value_data (v, void *));
elsefprintf (fp, "0xaddress");
}
elseswitch (v->type->sym) {
case sym_CHAR : fprintf (fp, "%d '%c'", value_data (v, char), value_data (v, char)); break;
case sym_INT : fprintf (fp, "%d", value_data (v, int)); break;
case sym_LONG : fprintf (fp, "%ld", value_data (v, long)); break;
case sym_FLOAT : fprintf (fp, "%g", value_data (v, float)); break;
case sym_DOUBLE : fprintf (fp, "%g", value_data (v, double)); break;
case sym_SIGNED : fprintf (fp, "%d", value_data (v, signed)); break;
case sym_UNSIGNED : fprintf (fp, "%u", value_data (v, unsigned)); break;
case sym_BOOL : fprintf (fp, "%d", value_data (v, bool)); break;
case sym_struct_or_union_specifier : fputs ("{...}", fp); break;
default:
('e', fp);
fputc }
if (value_flags (v) & unset)
(" (unset)", fp);
fputs if (ast_value_print_hook)
(v, fp);
ast_value_print_hook }
staticvoid display_value (const Value * v)
{
if (!v)
return;
("-------------------", stderr);
fputs ((Ast *) v, stderr, 0);
ast_print ('\n', stderr);
fputc (v->type, stderr, 0);
ast_print ('\n', stderr);
fputc fprintf (stderr, "size: %d, flags: %d, pointer: %d, scope: %d, value: ",
->size, value_flags (v), v->pointer, v->vscope);
vvalue_print (v, stderr);
('\n', stderr);
fputc if (v->pointer && (v->pointer > 1 || v->type->sym != sym_function_definition)) {
Pointer p = value_data (v, Pointer);
fprintf (stderr, "p: %p, start: %p, size: %d, scope: %d\nchars: '", p.p, p.start, p.size, p.pscope);
int maxchars = 80;
for (char * s = p.p; s < p.start + p.size && maxchars; s++, maxchars--)
(*s, stderr);
fputc if (!maxchars)
("...", stderr);
fputs ("'\n", stderr);
fputs }
if (v->dimension) {
("dimensions:", stderr);
fputs for (int *i = v->dimension; *i >= 0; i++)
fprintf (stderr, " %d", *i);
('\n', stderr);
fputc }
("===================\n", stderr);
fputs }
static void * undefined (Ast * scope, Ast * n, Stack * stack)
{
message (scope, n, "undefined argument(s) in '%s'\n", warning_verbosity, stack);
return NULL;
}
int ast_base_type_size (const Ast * type)
{
switch (type->sym) {
case sym_VOID: return 0;
case sym_SHORT: return sizeof (short);
case sym_INT: return sizeof (int);
case sym_LONG: return sizeof (long);
case sym_FLOAT: return sizeof (float);
case sym_DOUBLE: return sizeof (double);
case sym_SIGNED: return sizeof (signed);
case sym_UNSIGNED: return sizeof (unsigned);
case sym_BOOL: return sizeof (bool);
case sym_enum_specifier: return sizeof (int);
case sym_COMPLEX: return sizeof (complex double);
case sym_IMAGINARY:
default:
assert (false);
}
}
int (* ast_type_size) (const Ast *) = ast_base_type_size;
staticAst * identifier_type (Ast * n, Dimensions * d, Stack * stack);
Ast * base_type (Ast * type, Dimensions * d, Stack * stack)
{
if (type && (type->sym == sym_TYPEDEF_NAME || type->sym == sym_IDENTIFIER)) {
Ast * type_identifier = ast_identifier_declaration (stack, ast_terminal (type)->start);
if (!type_identifier)
return message (NULL, type, "unknown typedef '%s'\n", warning_verbosity, stack);
return identifier_type (type_identifier, d, stack);
}
elsereturn type;
}
staticAst * struct_size (Ast * type,
const char * member, int index,
Dimensions * d,
int * size, Stack * stack);
// fixme: this is a real mess...
staticint type_size (int pointer, Ast * type, Stack * stack)
{
if (type && pointer <= 1 && type->sym == sym_function_definition)
return sizeof (void *) + sizeof (Flags);
if (pointer)
return sizeof (Pointer) + sizeof (Flags);
if (!type)
return 0;
if (type->sym == sym_CHAR)
return sizeof (char);
Dimensions d = { .size = 1 };
= base_type (type, &d, stack);
type // fixme: may not always work
assert ((!d.pointer || type->sym == sym_function_definition) && d.size == 1);
if (!type) {
message (NULL, type, "could not find type of '%s'\n", warning_verbosity, stack);
return 0;
}
if (type->sym == sym_struct_or_union_specifier) {
if (!ast_child (type, sym_struct_declaration_list))
return type_size (0, ast_child (type, sym_generic_identifier)->child[0], stack);
int size;
struct_size (type, NULL, -1, NULL, &size, stack);
return size;
}
else if (type->sym == sym_function_definition)
return sizeof (void *) + sizeof (Flags);
else if (type->sym == sym_CHAR)
return sizeof (char);
elsereturn ast_type_size (type) + sizeof (Flags);
}
staticValue * new_value (Stack * stack, Ast * n, Ast * type, int pointer)
{
int size = type_size (pointer, type, stack);
Value * value = allocate (stack_alloc (stack), sizeof (Value) + size);
value_set_parent (value, n);
->type = type, value->size = size, value->pointer = pointer;
value->data.p = ((char *)value) + sizeof (Value);
value->data.start = value->data.p;
value->data.size = size;
value->vscope = ((StackData *)stack_get_data (stack))->scope;
value#if UNSET_ARRAY
->unset_array = NULL;
value->unset_member = NULL;
value#endif
return value;
}
staticValue * value_copy (Value * v, Allocator * alloc, bool data)
{
if (!v)
return NULL;
Value * copy = allocate (alloc, sizeof (Value) + v->size);
(copy, v, sizeof (Value));
memcpy ->data.p = ((char *)copy) + sizeof (Value);
copy->data.start = copy->data.p;
copy->data.size = v->size;
copyif (data && v->size)
(copy->data.p, v->data.p, v->size);
memcpy if (!data || (value_flags (v) & unset))
value_set_flags (copy, unset);
elsevalue_unset_flags (copy, unset);
return copy;
}
staticbool value_bool (const Value * v, bool * error, Stack * stack)
{
if (v->pointer)
return value_data (v, void *) != NULL;
switch (v->type->sym) {
case sym_CHAR: return value_data (v, char) != 0;
case sym_INT: return value_data (v, int) != 0;
case sym_UNSIGNED: return value_data (v, unsigned) != 0;
case sym_LONG: return value_data (v, long) != 0;
case sym_FLOAT: return value_data (v, float) != 0.;
case sym_DOUBLE: return value_data (v, double) != 0.;
default:
message (NULL, (Ast *)v, "cannot cast '%s' to bool\n", warning_verbosity, stack);
}
*error = true;
return 0;
}
staticint value_int (Value * v, bool * error, Stack * stack)
{
if (v->pointer)
return (long) value_data (v, void *);
switch (v->type->sym) {
case sym_CHAR: return value_data (v, char);
case sym_INT: return value_data (v, int);
case sym_UNSIGNED: return value_data (v, unsigned);
case sym_LONG: return value_data (v, long);
case sym_FLOAT: return value_data (v, float);
case sym_DOUBLE: return value_data (v, double);
default:
message (NULL, (Ast *)v, "cannot cast '%s' to int\n", warning_verbosity, stack);
}
*error = true;
return 0;
}
staticlong value_long (Value * v, bool * error, Stack * stack)
{
if (v->pointer)
return (long) value_data (v, void *);
switch (v->type->sym) {
case sym_CHAR: return value_data (v, char);
case sym_INT: return value_data (v, int);
case sym_UNSIGNED: return value_data (v, unsigned);
case sym_LONG: return value_data (v, long);
case sym_FLOAT: return value_data (v, float);
case sym_DOUBLE: return value_data (v, double);
default:
message (NULL, (Ast *)v, "cannot cast '%s' to long\n", warning_verbosity, stack);
}
*error = true;
return 0;
}
staticdouble value_double (Value * v, bool * error, Stack * stack)
{
switch (v->type->sym) {
case sym_CHAR: return value_data (v, char);
case sym_INT: return value_data (v, int);
case sym_UNSIGNED: return value_data (v, unsigned);
case sym_LONG: return value_data (v, long);
case sym_FLOAT: return value_data (v, float);
case sym_DOUBLE: return value_data (v, double);
default:
message (NULL, (Ast *)v, "cannot cast '%s' to double\n", warning_verbosity, stack);
}
*error = true;
return 0;
}
static inline
int array_dimension (const Value * array, int * index)
{
int nd = 0;
if (array->dimension)
for (int * i = array->dimension + 1; *i >= 0; i++)
*index *= *i, nd++;
return nd;
}
staticValue * array_member_value (Ast * n, Value * array, int index, int unset, Stack * stack)
{
if (!array) return NULL;
if (!array->pointer)
return message (NULL, (Ast *) array, "'%s' is not a pointer\n", error_verbosity, stack);
Value * value;
char * data = value_data (array, void *);
if (!data) {
#if 0
if (stack_verbosity (stack) >= warning_verbosity)
fprintf (stderr, "data: %p \n", data);
#endif
return message (NULL, n, "unallocated array access in '%s'\n", warning_verbosity, stack);
}
int nd = array_dimension (array, &index);
= new_value (stack, n, array->type, array->pointer - 1);
value #if UNSET_ARRAY
if (unset == true)
->unset_array = array;
value#endif
Pointer p = value_data (array, Pointer);
->vscope = p.pscope;
value#if 0
if (stack_verbosity (stack) > 1) {
display_value (array);
}
#endif
if (nd) {
assert (value->pointer);
(value, Pointer) = p;
value_data += index*type_size (array->pointer - nd - 1, array->type, stack);
data (value, void *) = data;
value_data }
else {
+= index*value->size;
data ->data.p = data;
value->data.start = p.start;
value->data.size = p.size;
valueif (p.start && // fixme: p.start should always be defined
(data < p.start || data + value->size > p.start + p.size)) {
#if 0
fprintf (stderr, "data: %p, p.start: %p, p.size: %d\n", data, p.start, p.size);
display_value (array);
display_value (value);
#endif
if (unset == 2)
return NULL;
else if (!unset)
return message (NULL, n, data < p.start ?
"array index underflow in '%s'\n" :
"array index overflow in '%s'\n", error_verbosity, stack);
else {
If the index is unset and there is an underflow or overflow, we return the first element of the array.
-= index*value->size;
data if (data < p.start || data + value->size > p.start + p.size)
return message (NULL, n, data < p.start ?
"array index underflow in '%s'\n" :
"array index overflow in '%s'\n", error_verbosity, stack);
else->data.p = data;
value}
}
}
#if 0
if (stack_verbosity (stack) >= warning_verbosity) {
fprintf (stderr, "==== array member value ====\n");
display_value (array);
fprintf (stderr, "%p %d %d\n", data, index, value->size);
display_value (value);
fprintf (stderr, "==== end array member value ====\n");
}
#endif
if (array->dimension)
->dimension = *(array->dimension + 1) >= 0 ? array->dimension + 1 : NULL;
valueif (value_flags (array) & unset)
unset_value (value, stack);
return value;
}
staticValue * assign (Ast * n, Value * dst, Value * src, Stack * stack)
{
if (!dst)
return undefined (NULL, n, stack);
value_set_flags (dst, unset);
if (!src)
return undefined (NULL, n, stack);
value_set_write (dst, stack);
bool error = false;
if (dst->pointer && src->pointer) {
if (dst->pointer == 1 && dst->type->sym == sym_function_definition) {
if (src->pointer == 1)
(dst->data.p, src->data.p, dst->size);
memcpy else {
if (src->type->sym != sym_function_definition || src->pointer != 2)
return message (NULL, n, "assignment between incompatible types\n", error_verbosity, stack);
(dst, Ast *) = *value_data (src, Ast **);
value_data }
}
else {
assert (src->size == dst->size);
#if 0
if (stack_verbosity (stack) > 1) {
(n, stderr, 0);
ast_print display_value (dst);
}
#endif
(dst->data.p, src->data.p, dst->size);
memcpy #if 1
(dst, Pointer).pscope = fmin (value_data (src, Pointer).pscope,
value_data ((StackData *)stack_get_data (stack))->scope);
#endif
}
}
else if (dst->type->sym == src->type->sym && dst->pointer == src->pointer) {
if (dst->size != src->size)
return message (NULL, n, "assignment between incompatible types\n", error_verbosity, stack);
(dst->data.p, src->data.p, src->size);
memcpy }
else if (dst->type->sym == sym_DOUBLE)
(dst, double) = value_double (src, &error, stack);
value_data else if (dst->type->sym == sym_FLOAT)
(dst, float) = value_double (src, &error, stack);
value_data else if (dst->type->sym == sym_CHAR)
(dst, char) = value_int (src, &error, stack);
value_data else if (dst->type->sym == sym_INT)
(dst, int) = value_int (src, &error, stack);
value_data else if (dst->type->sym == sym_UNSIGNED)
(dst, unsigned) = value_int (src, &error, stack);
value_data else if (dst->type->sym == sym_LONG)
(dst, long) = value_long (src, &error, stack);
value_data
/* fixme: what about value_data (dst, Pointer) ? */
else if (dst->pointer && src->type->sym == sym_INT)
(dst, void *) = (void *)(long)value_data (src, int);
value_data else if (dst->pointer && src->type->sym == sym_LONG)
(dst, void *) = (void *)value_data (src, long);
value_data else if (dst->type->sym == sym_struct_or_union_specifier &&
((src->type == (Ast *)&ast_int && value_data (src, int) == 0) ||
(src->type == (Ast *)&ast_long && value_data (src, long) == 0)))
(dst->data.p, 0, dst->size);
memset
elsereturn message (NULL, n, "assignment between incompatible types\n", error_verbosity, stack);
if (!(value_flags (src) & unset))
value_unset_flags (dst, unset);
#if UNSET_ARRAY
int a = 0;
if (dst->unset_array && !array_dimension (dst->unset_array, &a)) {
int index = 0;
Value * v;
while ((v = array_member_value ((Ast *)dst->unset_array, dst->unset_array, index++, 2, stack))) {
if (dst->unset_member)
= struct_member_value ((Ast *)v, v, dst->unset_member, -1, stack);
v unset_value (v, stack);
}
}
#endif
value_unset_flags (dst, constant_expression);
return ast_assign_hook ? ast_assign_hook (n, dst, src, stack) : src;
}
staticValue * pointer_unary_operation (Ast * n, Ast * op, Value * a, Stack * stack)
{
if (!a)
return undefined (NULL, n, stack);
if (value_flags (a) & unset)
return message (NULL, n, "unary operation '%s' on unset pointer\n", warning_verbosity, stack);
char * data = value_data (a, void *);
switch (op->sym) {
case sym_INC_OP:
value_set_write (a, stack);
(a, void *) = data + type_size (a->pointer - 1, a->type, stack);
value_data return a;
case sym_DEC_OP:
value_set_write (a, stack);
(a, void *) = data - type_size (a->pointer - 1, a->type, stack);
value_data return a;
case sym_unary_operator:
if (op->child[0]->sym == token_symbol ('*')) {
if (a->type->sym == sym_function_definition && a->pointer == 1)
return a;
return array_member_value (n, a, 0, false, stack);
}
else if (op->child[0]->sym == token_symbol ('!')) {
Value * v = new_value (stack, n, (Ast *)&ast_int, 0);
(v, int) = !data;
value_data return v;
}
else if (op->child[0]->sym == token_symbol ('&')) {
Value * v = new_value (stack, n, a->type, a->pointer + 1);
->vscope = a->vscope;
v(v, void *) = a->data.p;
value_data (v, Pointer).start = a->data.start;
value_data (v, Pointer).size = a->data.size;
value_data (v, Pointer).pscope = a->vscope;
value_data return v;
}
default:
return not_implemented (NULL, n, stack);
}
return NULL;
}
staticValue * unary_operation (Ast * n, Ast * op, Value * a, Stack * stack)
{
if (!a)
return undefined (NULL, n, stack);
if (a->pointer)
return pointer_unary_operation (n, op, a, stack);
Value * v = NULL;
switch (op->sym) {
case sym_INC_OP:
value_set_write (a, stack);
if (a->type->sym == sym_INT)
++value_data (a, int);
else if (a->type->sym == sym_LONG)
++value_data (a, long);
else if (a->type->sym == sym_FLOAT)
++value_data (a, float);
else if (a->type->sym == sym_DOUBLE)
++value_data (a, double);
elsereturn not_implemented (NULL, n, stack);
return a;
case sym_DEC_OP:
value_set_write (a, stack);
if (a->type->sym == sym_INT)
--value_data (a, int);
else if (a->type->sym == sym_LONG)
--value_data (a, long);
else if (a->type->sym == sym_FLOAT)
--value_data (a, float);
else if (a->type->sym == sym_DOUBLE)
--value_data (a, double);
elsereturn not_implemented (NULL, n, stack);
return a;
case sym_unary_operator:
switch (ast_terminal (op->child[0])->start[0]) {
case '&':
= new_value (stack, n, a->type, a->pointer + 1);
v ->vscope = a->vscope;
v(v, void *) = a->data.p;
value_data (v, Pointer).start = a->data.start;
value_data (v, Pointer).size = a->data.size;
value_data (v, Pointer).pscope = a->vscope;
value_data break;
case '+':
return a;
case '-': {
= value_copy (a, stack_alloc (stack), true);
v if (value_flags (a) & unset)
value_set_flags (v, unset);
elseswitch (a->type->sym) {
case sym_INT:
(v, int) = - value_data (a, int);
value_data break;
case sym_LONG:
(v, long) = - value_data (a, long);
value_data break;
case sym_FLOAT:
(v, float) = - value_data (a, float);
value_data break;
case sym_DOUBLE:
(v, double) = - value_data (a, double);
value_data break;
default:
return not_implemented (NULL, n, stack);
}
break;
}
case '!': {
= new_value (stack, n, (Ast *)&ast_int, 0);
v if (value_flags (a) & unset)
value_set_flags (v, unset);
else {
if (is_constant_expression (a))
set_constant_expression (v);
switch (a->type->sym) {
case sym_INT:
(v, int) = !value_data (a, int);
value_data break;
case sym_LONG:
(v, int) = !value_data (a, long);
value_data break;
case sym_FLOAT:
(v, int) = !value_data (a, float);
value_data break;
case sym_DOUBLE:
(v, int) = !value_data (a, double);
value_data break;
default:
return not_implemented (NULL, n, stack);
}
}
break;
}
case '~': {
= new_value (stack, n, a->type, 0);
v if (value_flags (a) & unset)
value_set_flags (v, unset);
else {
if (is_constant_expression (a))
set_constant_expression (v);
if (a->type->sym == sym_INT)
(v, int) = ~value_data (a, int);
value_data else if (a->type->sym == sym_LONG)
(v, long) = ~value_data (a, long);
value_data
elsereturn not_implemented (NULL, n, stack);
}
break;
}
default:
return not_implemented (NULL, n, stack);
}
break;
default:
return not_implemented (NULL, n, stack);
}
return v;
}
#define GET_VALUE(a, va) \
if (a->type->sym == sym_INT) \
= value_data (a, int); \
va else if (a->type->sym == sym_UNSIGNED) \
= value_data (a, unsigned); \
va else if (a->type->sym == sym_LONG) \
= value_data (a, long); \
va else if (a->type->sym == sym_FLOAT) \
= value_data (a, float); \
va else if (a->type->sym == sym_DOUBLE) \
= value_data (a, double); \
va else if (a->type->sym == sym_CHAR) \
= value_data (a, char); \
va else \
return not_implemented (NULL, n, stack)
staticValue * no_hook (Ast * n, Stack * stack, Value * a, Value * b, Value * value) { return value; }
/* fixme: change hooking to global */
Value * (* ast_binary_operation_hook) (Ast *, Stack *, Value *, Value *, Value *) = no_hook;
staticValue * binary_operation (Ast * n, Stack * stack)
{
int op = n->child[1]->sym;
And/Or operations require special treatment due to partial evaluation.
if (op == sym_AND_OP || op == sym_OR_OP) {
Value * a = run (n->child[0], stack);
if (!a)
return undefined (NULL, n->child[0], stack);
Value * value = new_value (stack, n, (Ast *) &ast_int, 0);
bool error;
bool va = value_bool (a, &error, stack);
if (va && op == sym_OR_OP && !(value_flags (a) & unset)) {
(value, int) = true;
value_data if (is_constant_expression (a))
set_constant_expression (value);
return value;
}
if (!va && op == sym_AND_OP && !(value_flags (a) & unset)) {
if (is_constant_expression (a))
set_constant_expression (value);
return value;
}
Value * b = run (n->child[2], stack);
if (!b)
return undefined (NULL, n->child[2], stack);
if (is_constant_expression (a) && is_constant_expression (b))
set_constant_expression (value);
bool vb = value_bool (b, &error, stack);
if (vb && op == sym_OR_OP && !(value_flags (b) & unset)) {
(value, int) = true;
value_data return value;
}
if (!vb && op == sym_AND_OP && !(value_flags (b) & unset))
return value;
if ((value_flags (a) & unset) ||
(value_flags (b) & unset))
value_set_flags (value, unset);
else if (op == sym_AND_OP && va && vb)
(value, int) = true;
value_data return value;
}
In other cases we must evaluate both operands.
Value * a = run (n->child[0], stack), * b = run (n->child[2], stack);
if (!a)
return undefined (NULL, n->child[0], stack);
if (!b)
return undefined (NULL, n->child[2], stack);
Pointer arithmetic.
if (a->pointer || b->pointer) {
if (op == sym_EQ_OP || op == sym_NE_OP) {
Value * value = ast_binary_operation_hook (n, stack, a, b, new_value (stack, n, (Ast *) &ast_int, 0));
if ((value_flags (a) & unset) || (value_flags (b) & unset)) {
value_set_flags (value, unset);
return value;
}
bool erra = false, errb = false;
switch (op) {
case sym_EQ_OP:
(value, int) =
value_data value_long (a, &erra, stack) == value_long (b, &errb, stack);
break;
case sym_NE_OP:
(value, int) =
value_data value_long (a, &erra, stack) != value_long (b, &errb, stack);
break;
}
if (erra || errb)
return not_implemented (NULL, n, stack);
return value;
}
if (a->pointer && b->pointer) {
if (op == token_symbol ('-')) {
Value * value = ast_binary_operation_hook (n, stack, a, b, new_value (stack, n, (Ast *) &ast_long, 0));
if ((value_flags (a) & unset) || (value_flags (b) & unset)) {
value_set_flags (value, unset);
return value;
}
// fixme: will work only for char * pointers!!!
char * va = value_data (a, char *);
char * vb = value_data (b, char *);
(value, long) = va - vb;
value_data return value;
}
}
else {
bool error = false;
int vb = value_int (b, &error, stack);
if (a->pointer && !error) {
Value * value;
if (n->sym != sym_assignment_expression)
= ast_binary_operation_hook (n, stack, a, b, value_copy (a, stack_alloc (stack), true));
value else {
= ast_binary_operation_hook (n, stack, a, b, a);
value value_set_write (value, stack);
}
if ((value_flags (a) & unset) || (value_flags (b) & unset)) {
value_set_flags (value, unset);
return value;
}
char * data = value_data (a, void *);
*= type_size (a->pointer - 1, a->type, stack);
vb if (op == token_symbol('+') ||
(n->child[1], sym_assignment_operator, 0, sym_ADD_ASSIGN))
ast_schema += vb;
data else if (op == token_symbol('-') ||
(n->child[1], sym_assignment_operator, 0, sym_SUB_ASSIGN))
ast_schema -= vb;
data
elsereturn message (NULL, n, "invalid pointer arithmetic operation '%s'\n", error_verbosity, stack);
(value, void *) = data;
value_data return value;
}
}
return not_implemented (NULL, n, stack);
}
Float/integer arithmetic.
#define OPERATION(res, va, vb) \
= true; \
set if (op == token_symbol('*') || \
(n->child[1], sym_assignment_operator, 0, sym_MUL_ASSIGN)) \
ast_schema = va*vb; \
res else if (op == token_symbol('/') || \
(n->child[1], sym_assignment_operator, 0, sym_DIV_ASSIGN)) \
ast_schema = vb ? va/vb : 0.; \
res else if (op == token_symbol('+') || \
(n->child[1], sym_assignment_operator, 0, sym_ADD_ASSIGN)) \
ast_schema = va + vb; \
res else if (op == token_symbol('-') || \
(n->child[1], sym_assignment_operator, 0, sym_SUB_ASSIGN)) \
ast_schema = va - vb; \
res else \
= false
set
#define LOGICAL_OPERATION(res, va, vb) \
= true; \
set if (op == token_symbol('<')) \
= va < vb; \
res else if (op == token_symbol('>')) \
= va > vb; \
res else if (op == sym_LE_OP) \
= va <= vb; \
res else if (op == sym_GE_OP) \
= va >= vb; \
res else if (op == sym_EQ_OP) \
= va == vb; \
res else if (op == sym_NE_OP) \
= va != vb; \
res else \
= false
set
#define BITWISE_OPERATION(res, va, vb) \
= true; \
set if (op == sym_LEFT_OP) \
= va << vb; \
res else if (op == sym_RIGHT_OP) \
= va >> vb; \
res else if (op == token_symbol('&')) \
= va & vb; \
res else if (op == token_symbol('^')) \
= va ^ vb; \
res else if (op == token_symbol('|')) \
= va | vb; \
res else if (op == token_symbol('%')) \
= vb ? va % vb : 0; \
res else \
= false
set
#define SET_VALUE(a, va) \
if (a->type->sym == sym_INT) \
(a, int) = va; \
value_data else if (a->type->sym == sym_UNSIGNED) \
(a, unsigned) = va; \
value_data else if (a->type->sym == sym_LONG) \
(a, long) = va; \
value_data else if (a->type->sym == sym_FLOAT) \
(a, float) = va; \
value_data else if (a->type->sym == sym_DOUBLE) \
(a, double) = va; \
value_data else \
return not_implemented (NULL, n, stack)
#define GENERIC_OPERATION(type) do { \
; \
type resbool set; \
(a, va); \
GET_VALUE (b, vb); \
GET_VALUE (res, va, vb); \
OPERATION if (set) { \
Value * value; \
if (n->sym == sym_assignment_expression) \
value_set_write (a, stack), value = ast_binary_operation_hook (n, stack, a, b, a); \
else { \
= new_value (stack, n, (Ast *) &ast_##type, 0); \
value if (is_constant_expression (a) && is_constant_expression (b)) \
set_constant_expression (value); \
= ast_binary_operation_hook (n, stack, a, b, value); \
value } \
(value, res); \
SET_VALUE if ((value_flags (a) & unset) || (value_flags (b) & unset)) \
value_set_flags (value, unset); \
return value; \
} \
else { \
int res; \
(res, va, vb); \
LOGICAL_OPERATION if (set) { \
Value * value = new_value (stack, n, (Ast *) &ast_int, 0); \
if (is_constant_expression (a) && is_constant_expression (b)) \
set_constant_expression (value); \
(n, stack, a, b, value); \
ast_binary_operation_hook (value, res); \
SET_VALUE if ((value_flags (a) & unset) || (value_flags (b) & unset)) \
value_set_flags (value, unset); \
return value; \
} \
} \
} while (0)
#define INTEGER_OPERATION(type) do { \
; \
type resbool set; \
(res, va, vb); \
BITWISE_OPERATION if (set) { \
Value * value = new_value (stack, n, (Ast *) &ast_##type, 0); \
if (is_constant_expression (a) && is_constant_expression (b)) \
set_constant_expression (value); \
= ast_binary_operation_hook (n, stack, a, b, value); \
value (value, res); \
SET_VALUE if ((value_flags (a) & unset) || (value_flags (b) & unset)) \
value_set_flags (value, unset); \
return value; \
} \
} while (0)
if (a->type->sym == sym_DOUBLE || b->type->sym == sym_DOUBLE) {
double va, vb;
(double);
GENERIC_OPERATION }
else if (a->type->sym == sym_FLOAT || b->type->sym == sym_FLOAT) {
float va, vb;
(float);
GENERIC_OPERATION }
else if (a->type->sym == sym_LONG || b->type->sym == sym_LONG) {
long va, vb;
(long);
GENERIC_OPERATION (long);
INTEGER_OPERATION }
else {
int va, vb;
(int);
GENERIC_OPERATION (int);
INTEGER_OPERATION }
return not_implemented (NULL, n, stack);
}
#define has_value(identifier) (ast_terminal (identifier)->value == (identifier))
staticValue * constant_value (Ast * n, Stack * stack)
{
#if 0 // fixme: really necessary? apparently not
if (ast_terminal (n)->value) // constants have a "static" value
return ast_terminal (n)->value;
#endif
switch (n->sym) {
case sym_I_CONSTANT: {
Value * v = new_value (stack, n, (Ast *) &ast_long, 0);
set_constant_expression (v);
if (ast_terminal (n)->start[0] == '\'') {
// character constant
if (ast_terminal (n)->start[1] == '\\') {
switch (ast_terminal (n)->start[2]) {
case '\'': value_data (v, long) = '\''; break;
case '"': value_data (v, long) = '\"'; break;
case '?': value_data (v, long) = '\?'; break;
case '\\': value_data (v, long) = '\\'; break;
case 'a': value_data (v, long) = '\a'; break;
case 'b': value_data (v, long) = '\b'; break;
case 'f': value_data (v, long) = '\f'; break;
case 'n': value_data (v, long) = '\n'; break;
case 'r': value_data (v, long) = '\r'; break;
case 't': value_data (v, long) = '\t'; break;
case 'v': value_data (v, long) = '\v'; break;
default:
(v, long) = atol (ast_terminal (n)->start + 2);
value_data }
}
else(v, long) = ast_terminal (n)->start[1];
value_data }
else(v, long) = atol (ast_terminal (n)->start);
value_data #if 0
{
AstTerminal * t = ast_terminal (n);
if (t->file && !strcmp (t->file, BASILISK "/grid/events.h"))
fprintf (stderr, "%s:%d: %s %p %p\n", t->file, t->line, t->start, n, v);
}
#endif
((Ast *)v)->value = ast_terminal (n)->value = v;
ast_terminal return v;
}
case sym_F_CONSTANT: {
Value * v = new_value (stack, n, (Ast *) &ast_double, 0);
set_constant_expression (v);
(v, double) = atof (ast_terminal (n)->start);
value_data ((Ast *)v)->value = ast_terminal (n)->value = v;
ast_terminal return v;
}
case sym_STRING_LITERAL: case sym_FUNC_NAME: {
Value * v = new_value (stack, n, (Ast *) &ast_char, 1);
int size = strlen (ast_terminal (n)->start) - 1;
Pointer p = {0};
.size = size*sizeof (char);
p.p = allocate (((StackData *)stack_get_data (stack))->static_alloc, p.size);
p.start = p.p;
p(v, Pointer) = p;
value_data (value_data (v, char *), ast_terminal (n)->start + 1, (size - 1)*sizeof (char));
memcpy ((Ast *)v)->value = ast_terminal (n)->value = v;
ast_terminal return v;
}
default:
assert (false);
}
return NULL;
}
static Ast * get_array_dimensions (Ast * direct_declarator, int symbol, Dimensions * d, int nd, Stack * stack)
{
assert (d->dimension == NULL);
->dimension = allocate (stack_alloc (stack), (nd + 1)*sizeof (int));
d->dimension[nd] = -1;
d= 0;
nd while (ast_schema (direct_declarator->parent, symbol,
0, token_symbol ('[')) ||
(direct_declarator->parent, symbol,
ast_schema 1, token_symbol ('['))) {
Ast * nelem = ast_schema (direct_declarator->parent, symbol,
0, token_symbol ('[')) ?
(direct_declarator->parent, symbol,
ast_schema 1, sym_assignment_expression) :
(direct_declarator->parent, symbol,
ast_schema 2, sym_assignment_expression);
Value * s;
if (nelem) {
= run (nelem, stack);
s if (!s)
return message (NULL, nelem, "undefined array size '%s'\n", warning_verbosity, stack);
if (value_flags (s) & unset)
return message (NULL, nelem, "array size '%s' not set\n", warning_verbosity, stack);
}
else if (ast_schema (direct_declarator->parent, symbol,
1, token_symbol (']')) ||
(direct_declarator->parent, symbol,
ast_schema 2, token_symbol (']')))
= NULL;
s
else// fixme: more complex size syntaxes not handled
return not_implemented (NULL, direct_declarator, stack);
if (s) {
bool error = false;
->dimension[nd] = value_int (s, &error, stack);
d->size *= d->dimension[nd];
dif (error)
return NULL;
}
else {
if (nd != 0)
return message (NULL, direct_declarator,
"only the first dimension of a multidimensional array can be undefined\n",
1, stack);
->dimension[0] = 0;
d->size = -1;
d}
++;
nd= direct_declarator->parent;
direct_declarator }
return direct_declarator;
}
staticAst * direct_declarator_type (Ast * direct_declarator, Dimensions * d, Stack * stack)
{
if (direct_declarator->sym == sym_enumerator)
return (Ast *) &ast_enum;
if (direct_declarator->sym == sym_struct_or_union_specifier)
return direct_declarator;
Array sizes.
if (ast_schema (direct_declarator, sym_direct_declarator,
0, sym_generic_identifier)) {
int nd = 0;
Ast * array = direct_declarator;
while (ast_schema (array->parent, sym_direct_declarator,
1, token_symbol ('['))) {
++;
nd= array->parent;
array }
if (nd && !(direct_declarator = get_array_dimensions (direct_declarator, sym_direct_declarator, d, nd, stack)))
return NULL;
}
Ast * pointer = ast_schema (direct_declarator->parent, sym_declarator);
while ((pointer = ast_child (pointer, sym_pointer)))
->pointer++;
d
if (ast_schema (ast_ancestor (direct_declarator, 2), sym_direct_declarator,
1, sym_declarator))
= ast_ancestor (direct_declarator, 2);
direct_declarator
if (direct_declarator->sym == sym_struct_or_union_specifier)
return direct_declarator;
else if (ast_schema (direct_declarator->parent, sym_direct_declarator,
1, token_symbol ('('))) {
// Function declaration
if (!d->pointer)
->pointer = 1;
dreturn (Ast *) &ast_function;
}
else {
// Standard declaration
Ast * specifiers = NULL;
if (ast_schema (ast_ancestor (direct_declarator, 2), sym_init_declarator,
0, sym_declarator))
= ast_schema (ast_parent (direct_declarator, sym_declaration), sym_declaration,
specifiers 0, sym_declaration_specifiers);
else if (ast_schema (ast_ancestor (direct_declarator, 2), sym_struct_declarator,
0, sym_declarator))
= ast_schema (ast_parent (direct_declarator, sym_struct_declaration),
specifiers ,
sym_struct_declaration0, sym_specifier_qualifier_list);
else if ((specifiers = ast_schema (ast_ancestor (direct_declarator, 2),
,
sym_forin_declaration_statement2, sym_declaration_specifiers)))
;
else if ((specifiers = ast_schema (ast_ancestor (direct_declarator, 2),
,
sym_parameter_declaration0, sym_declaration_specifiers)))
;
assert (specifiers);
Ast * type = ast_find (specifiers, sym_types);
assert (type);
return type->child[0];
}
(direct_declarator, stderr, 0, 0, -1);
ast_print_tree assert (false);
return NULL;
}
staticAst * identifier_type (Ast * n, Dimensions * d, Stack * stack)
{
Ast * type = direct_declarator_type (ast_ancestor (n, 2), d, stack);
if (!type)
return NULL;
if (type->sym == sym_struct_or_union_specifier &&
!ast_child (type, sym_struct_declaration_list)) {
if (ast_child (type, sym_YYerror))
return NULL;
= ast_child (type, sym_generic_identifier)->child[0];
type }
if (n->sym == sym_IDENTIFIER && type->sym == sym_IDENTIFIER &&
!strcmp (ast_terminal (n)->start, ast_terminal (type)->start))
// type and identifier are identical e.g. 'typedef struct Foo Foo;'
return type;
return base_type (type, d, stack);
}
staticAst * return_type (Ast * function_declaration, Dimensions * d, Stack * stack)
{
if (!function_declaration)
return NULL;
Ast * declarator = ast_child (function_declaration, sym_declarator);
while (declarator) {
Ast * pointer = declarator;
while ((pointer = ast_child (pointer, sym_pointer)))
->pointer++;
dif (ast_child (declarator, token_symbol ('[')))
->pointer++;
dif (ast_child (declarator, sym_direct_declarator))
= ast_child (declarator, sym_direct_declarator);
declarator
else= ast_child (declarator, sym_declarator);
declarator }
Ast * specifiers = ast_child (function_declaration, sym_declaration_specifiers);
assert (specifiers);
Ast * type = ast_find (specifiers, sym_types);
assert (type);
= type->child[0];
type if (type->sym == sym_struct_or_union_specifier &&
!ast_child (type, sym_struct_declaration_list)) {
if (ast_child (type, sym_YYerror))
return NULL;
= ast_child (type, sym_generic_identifier)->child[0];
type }
return base_type (type, d, stack);
}
staticAst * type_name_type (Ast * type_name, Dimensions * d, Stack * stack)
{
Ast * declarator = ast_child (type_name, sym_abstract_declarator);
while (declarator) {
Ast * pointer = declarator;
while ((pointer = ast_child (pointer, sym_pointer)))
->pointer++;
dif (ast_child (declarator, token_symbol ('[')))
->pointer++;
dif (ast_child (declarator, sym_direct_abstract_declarator))
= ast_child (declarator, sym_direct_abstract_declarator);
declarator
else= ast_child (declarator, sym_abstract_declarator);
declarator }
Ast * specifiers = ast_schema (type_name, sym_type_name,
0, sym_specifier_qualifier_list);
assert (specifiers);
Ast * type = ast_find (specifiers, sym_types);
assert (type);
= type->child[0];
type
if (type->sym == sym_struct_or_union_specifier &&
!ast_child (type, sym_struct_declaration_list)) {
if (ast_child (type, sym_YYerror))
return NULL;
= ast_child (type, sym_generic_identifier)->child[0];
type }
return base_type (type, d, stack);
}
staticAst * struct_size (Ast * type,
const char * member, int index,
Dimensions * d,
int * size, Stack * stack)
{
Ast * list = ast_child (type, sym_struct_declaration_list);
while (!list) {
= base_type (ast_child (type, sym_generic_identifier)->child[0], d, stack);
type if (!type)
return NULL;
assert (type->sym == sym_struct_or_union_specifier);
= ast_child (type, sym_struct_declaration_list);
list }
*size = 0;
int i = 0;
(list, sym_struct_declaration, item) {
foreach_item_r Ast * list = ast_child (item, sym_struct_declarator_list);
(list, sym_struct_declarator, item) {
foreach_item_r if (ast_child (item, sym_constant_expression))
return not_implemented (NULL, type, stack);
Ast * declarator = ast_child (item, sym_declarator);
Ast * direct_declarator = ast_child (declarator, sym_direct_declarator);
while (direct_declarator->child[0]->sym == sym_direct_declarator ||
->child[0]->sym == token_symbol('('))
direct_declarator= direct_declarator->child[0]->sym == sym_direct_declarator ?
direct_declarator ->child[0] :
direct_declaratorast_child (direct_declarator->child[1], sym_direct_declarator);
Dimensions dm = { .size = 1 };
Ast * type = base_type (direct_declarator_type (direct_declarator, &dm, stack),
&dm, stack);
Ast * identifier;
if ((!member && i == index) ||
(member &&
((identifier = ast_find (direct_declarator->child[0], sym_IDENTIFIER)) ||
(identifier = ast_find (direct_declarator->child[0], sym_TYPEDEF_NAME))) &&
!strcmp (member, ast_terminal (identifier)->start))) {
*d = dm;
return type;
}
*size += type_size (dm.pointer, type, stack)*dm.size;
++;
i}
}
return NULL;
}
staticValue * struct_member_value (Ast * n, Value * value, Ast * member, int index, Stack * stack)
{
int offset;
Dimensions d = { .size = 1 };
Ast * type = struct_size (value->type, member ? ast_terminal (member)->start : NULL,
, &d, &offset, stack);
indexif (!type) {
if (member)
return message (NULL, member, "could not find type of '%s'\n", warning_verbosity, stack);
return NULL;
}
if (d.dimension) {
assert (d.dimension[0] >= 0);
.size *= type_size (d.pointer, type, stack);
dfor (int * i = d.dimension; *i >= 0; i++)
.pointer++;
d}
Value * v = new_value (stack, n, type, d.pointer);
char * data;
if (value->pointer) {
if (value_flags (value) & unset)
return message (NULL, n, "undefined structure pointer in '%s'\n", warning_verbosity, stack);
= value_data (value, char *) + offset;
data Pointer p = value_data (value, Pointer);
if (data < p.start || data + v->size > p.start + p.size)
return message (NULL, n, data < p.start ?
"structure pointer underflow in '%s'\n" :
"structure pointer overflow in '%s'\n",
1, stack);
->vscope = p.pscope;
v}
else {
= ((char *)value->data.p) + offset;
data ->vscope = value->vscope;
v}
if (d.dimension) {
->dimension = d.dimension;
v(v, void *) = data;
value_data if (d.size > 0)
(v, Pointer).start = data,
value_data (v, Pointer).size = d.size;
value_data
elsevalue_set_flags (v, unset);
(v, Pointer).pscope = v->vscope;
value_data }
else {
->data.p = data;
vif (value->pointer) {
->data.start = value_data (value, Pointer).start;
v->data.size = value_data (value, Pointer).size;
v}
else {
->data.start = value->data.start;
v->data.size = value->data.size;
v}
assert (((char *)v->data.p) + v->size <= v->data.start + v->data.size);
}
#if UNSET_ARRAY
if (value->unset_array) {
->unset_array = value->unset_array;
v->unset_member = member;
v}
#endif
Ast * attributes;
if (v->vscope == 0 && member && (value_flags (v) & unset) &&
(attributes = ast_schema (ast_parent (value->type, sym_declaration), sym_declaration,
1, sym_init_declarator_list,
0, sym_init_declarator,
0, sym_declarator,
0, sym_direct_declarator,
0, sym_generic_identifier,
0, sym_IDENTIFIER)) &&
!strcmp (ast_terminal (attributes)->start, "_Attributes") &&
!strcmp (ast_terminal (member)->start, "freed"))
value_unset_flags (v, unset);
return v;
}
staticvoid print_expression (Ast * n, Value * value)
{
AstTerminal * t = ast_left_terminal (n);
if (t->file && t->line) {
char * s = ast_str_append (n, NULL);
fprintf (stderr, "%s:%d: %s: ", t->file, t->line, ast_crop_before (s));
value_print (value, stderr);
('\n', stderr);
fputc free (s);
}
}
staticvoid print_function_call (Ast * n, FILE * fp, Stack * stack)
{
char * s = ast_str_append (n->child[0], NULL);
fprintf (fp, "%s", ast_crop_before (s));
free (s);
Ast * arguments = ast_schema (n, sym_function_call,
2, sym_argument_expression_list);
if (arguments) {
(" (", fp);
fputs bool first = true;
(arguments, sym_argument_expression_list_item, arg) {
foreach_item_r if (!first)
(", ", fp);
fputs = false;
first value_print (run (arg, stack), fp);
}
(')', fp);
fputc }
else("()", fp);
fputs }
static bool allocate_array (Stack * stack, Value * value, Ast * initializer)
{
if (value->pointer && !value_data (value, void *)) {
int size = 0;
Ast * list = initializer->child[1];
(list, 2, i) size++;
foreach_item int pointer = value->pointer;
if (value->dimension) {
assert (value->dimension[0] == 0);
->dimension[0] = size;
value= 1;
size for (int * i = value->dimension; *i >= 0; i++)
--, size *= *i;
pointerassert (size > 0);
++;
pointer}
(value, Pointer) = stack_allocate (stack, size*type_size (pointer - 1, value->type, stack));
value_data value_unset_flags (value, unset);
#if 0
if (stack_verbosity (stack) > error_verbosity) {
fprintf (stderr, "--- Array allocation ---\n");
display_value (value);
fprintf (stderr, "--- End Array allocation ---\n");
}
#endif
return true;
}
return false;
}
staticvoid initialize (Stack * stack, Ast * n, Value * value, Ast * initializer);
staticint initialize_struct_member (Stack * stack, Value * value, int index, Ast * initializer)
{
int cindex = ast_child_index (initializer);
if (cindex == 0 || cindex == 2) {
Value * v = struct_member_value ((Ast *)value, value, NULL, index, stack);
if (!v) {
message (NULL, initializer, "too many elements in initializer?\n", error_verbosity, stack);
return index + 1;
}
initialize (stack, initializer, v, initializer);
}
else { // designation
Ast * designation = ast_child (initializer->parent, sym_designation);
// fixme: this only handles simple designators i.e. '.identifier = ...'
Ast * designator = ast_find (designation, sym_IDENTIFIER);
if (!designator)
= ast_find (designation, sym_TYPEDEF_NAME);
designator Value * v = struct_member_value (designator, value, designator, -1, stack);
if (!v) {
message (NULL, designator, "unknown structure member '%s'\n", error_verbosity, stack);
return index + 1;
}
initialize (stack, designator, v, initializer);
}
return index + 1;
}
staticvoid initialize (Stack * stack, Ast * n, Value * value, Ast * initializer)
{
if (!value)
return;
#if 0
if (stack_verbosity (stack) > error_verbosity) {
(n, stderr, 0, 0, -1);
ast_print_tree display_value (value);
}
#endif
if (initializer && initializer->child[0]->sym == sym_assignment_expression)
assign (n, value, run (initializer->child[0], stack), stack);
else if (value->pointer) {
int index = 0;
if (initializer) {
allocate_array (stack, value, initializer);
Ast * list = initializer->child[1];
(list, sym_initializer, j) {
foreach_item_r int cindex = ast_child_index (j);
if (cindex == 0 || cindex == 2) {
Value * v = array_member_value ((Ast *)value, value, index, false, stack);
if (!v) {
message (NULL, n, "too many elements in initializer?\n", error_verbosity, stack);
return;
}
initialize (stack, j, v, j);
}
++;
index}
}
}
else if (value->type->sym == sym_struct_or_union_specifier) {
int index = 0;
if (initializer) {
Ast * list = initializer->child[1];
(list, sym_initializer, initializer)
foreach_item_r = initialize_struct_member (stack, value, index, initializer);
index }
We set to “zero”, the remaining members of the structure.
Value * v;
int verbosity = stack_verbosity (stack);
((StackData *)stack_get_data (stack))->verbosity = 0;
while ((v = struct_member_value ((Ast *)value, value, NULL, index, stack))) {
((StackData *)stack_get_data (stack))->verbosity = verbosity;
initialize (stack, n, v, NULL);
((StackData *)stack_get_data (stack))->verbosity = 0;
++;
index}
((StackData *)stack_get_data (stack))->verbosity = verbosity;
}
else if (!initializer) {
Ast * parent = n->parent;
->parent = ((StackData *)stack_get_data (stack))->root;
n= ast_new_constant (n, sym_I_CONSTANT, "0");
initializer ->parent = parent;
nassign (n, value, run (initializer, stack), stack);
(initializer);
ast_destroy }
elsemessage (NULL, n, "lists can only initialize structures or arrays\n", error_verbosity, stack);
}
staticValue * value_from_type (Ast * n, Ast * type, Dimensions * d, Ast * initializer, Stack * stack)
{
Array allocation.
Value * v;
if (d->dimension) {
assert (d->dimension[0] >= 0);
->size *= type_size (d->pointer, type, stack);
dfor (int * i = d->dimension; *i >= 0; i++)
->pointer++;
d= new_value (stack, n, type, d->pointer);
v ->dimension = d->dimension;
vif (d->size > 0)
(v, Pointer) = stack_allocate (stack, d->size);
value_data
elsevalue_set_flags (v, unset);
}
Standard allocation.
else {
= new_value (stack, n, type, d->pointer);
v if (type->sym == sym_function_definition && d->pointer == 1)
(v, Ast *) = n;
value_data else if (type == (Ast *) &ast_enum) {
Ast * enumerator = ast_parent (n, sym_enumerator);
Ast * constant = ast_child (enumerator, sym_constant_expression);
if (constant)
assign (n, v, run (constant, stack), stack);
else {
Ast * previous = ast_child (ast_child (ast_ancestor (enumerator, 1), sym_enumerator_list),
);
sym_enumeratorif (previous) {
Ast * identifier =
(stack, ast_terminal (ast_find (previous, sym_IDENTIFIER))->start);
ast_identifier_declaration assert (has_value (identifier));
(v, int) = value_data ((Value *)identifier, int) + 1;
value_data }
}
}
else if (!initializer)
unset_value (v, stack);
}
Initialization
if (initializer)
initialize (stack, initializer->parent, v, initializer);
return v;
}
staticValue * identifier_value (Ast * n, Stack * stack)
{
Dimensions d = { .size = 1 };
Ast * type = identifier_type (n, &d, stack);
if (!type)
return message (NULL, n, "could not find type of '%s'\n", warning_verbosity, stack);
if (type->sym == sym_IDENTIFIER) {
= ast_identifier_declaration (stack, ast_terminal (type)->start);
type if (!type || ast_ancestor (type, 2)->sym != sym_struct_or_union_specifier)
return message (NULL, n, "could not find type of '%s'\n", warning_verbosity, stack);
= ast_ancestor (type, 2);
type }
return value_from_type (n, type, &d, ast_child (ast_parent (n, sym_init_declarator), sym_initializer), stack);
}
staticAst ** function_parameters (Ast * identifier)
{
Ast * parameters = ast_find (ast_parent (identifier, sym_declarator), sym_direct_declarator,
2, sym_parameter_type_list,
0, sym_parameter_list);
int n = 0;
(parameters, 2, param) n++;
foreach_item Ast ** params = malloc ((n + 1)*sizeof (Ast *));
[n] = NULL;
params(parameters, 2, param)
foreach_item [--n] = param;
paramsreturn params;
}
staticvoid * pointer_check (Ast * n, Value * v, long size, Stack * stack)
{
Pointer p = value_data (v, Pointer);
if ((char *) p.p < p.start || (char *) p.p + size > p.start + p.size)
return message (NULL, n, (char *) p.p < p.start ?
"pointer underflow in '%s'\n" :
"pointer overflow in '%s'\n", error_verbosity, stack);
return v;
}
typedef struct {
size_t id, size;
} pmdata;
static void * pmalloc (pmdata * d, size_t size)
{
assert (d != NULL);
->id = 1;
d->size = size;
dreturn ((char *)d) + sizeof(pmdata);
}
static void * pfree (void * ptr, Ast * n, Stack * stack)
{
if (!ptr)
return ptr;
pmdata * d = (pmdata *) (((char *)ptr) - sizeof(pmdata));
if (d->id != 1) {
if (d->size == 0)
return message (NULL, n, "possible double free\n", warning_verbosity, stack);
return message (NULL, n, "corrupted pointer\n", warning_verbosity, stack);
}
->id = 0;
d->size = 0;
dreturn d;
}
staticValue * internal_functions_no_hook (Ast * call, Ast * identifier, Value ** parameters, Stack * stack, Value * value) {
return value;
}
Value * (* ast_internal_functions_hook) (Ast * call, Ast * identifier, Value ** parameters,
Stack * stack, Value * value) =
internal_functions_no_hook;
typedef struct {
size_t size;
} FatPointer;
Memory sizes above DYNAMIC_SIZE are allocated/freed using
malloc/free
rather than using static allocation.
#define DYNAMIC_SIZE (1024*1024)
staticvoid * static_malloc (size_t size, Stack * stack)
{
if (!size)
return NULL;
FatPointer * p;
if (size > DYNAMIC_SIZE)
= malloc (sizeof (FatPointer) + size);
p else {
StackData * d = stack_get_data (stack);
= allocate (d->static_alloc, sizeof (FatPointer) + size);
p }
->size = size;
preturn (void *) (((char *)p) + sizeof (FatPointer));
}
staticvoid static_free (void * ptr, Stack * stack)
{
if (ptr) {
FatPointer * p = (FatPointer *) (((char *)ptr) - sizeof (FatPointer));
if (p->size > DYNAMIC_SIZE)
free (p);
}
}
staticvoid * static_calloc (size_t nmemb, size_t size, Stack * stack)
{
if (!size)
return NULL;
void * p = static_malloc (size*nmemb, stack);
(p, 0, size*nmemb);
memset return p;
}
staticvoid * static_realloc (void * ptr, size_t size, Stack * stack)
{
if (ptr && !size) {
static_free (ptr, stack);
return NULL;
}
void * p = static_malloc (size, stack);
if (p && ptr) {
FatPointer * f = (FatPointer *)(((char *)ptr) - sizeof (FatPointer));
if (f->size < size) size = f->size;
(p, ptr, size);
memcpy static_free (ptr, stack);
}
return p;
}
staticbool continue_iterations (Ast * n, Stack * stack, int iter, int calls)
{
if (iter <= 0) {
message (NULL, n, "reached maximum number of iterations\n", warning_verbosity, stack);
return false;
}
return true;
#if 0
= maximum_iterations - iter;
iter = calls - ((StackData *)stack_get_data (stack))->maxcalls;
calls
// if (iter == maximum_iterations)
#if 0
fprintf (stderr, "%s:%d: %d #iterations %d\n",
ast_left_terminal (n)->file, ast_left_terminal (n)->line,
, 20000000/(calls/iter));
iter#endif
#if 1
if (iter > 2 && 20000000/(calls/iter) < 1000) {
if (!strncmp (ast_left_terminal (n)->file, "ast/", 4) && iter < 32)
return true;
message (NULL, n, "could reach maximum number of iterations\n", error_verbosity, stack);
return false;
}
#endif
return true;
#endif
}
staticValue * internal_functions (Ast * call, Ast * identifier, Ast ** parameters, bool constant_arguments, Stack * stack)
{
const char * name = ast_terminal (identifier)->start;
if (!strcmp (name, "malloc")) {
Value * params[] = { run (parameters[0], stack) };
if (!params[0])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_void, 1);
if (value_flags (params[0]) & unset)
value_set_flags (value, unset);
else {
long size = value_data (params[0], long);
if (size < 0) size = 0;
(value, void *) = memset (pmalloc (static_calloc (1, sizeof(pmdata) + size, stack), size), 0, size);
value_data (value, Pointer).start = value_data (value, void *);
value_data (value, Pointer).size = size;
value_data (value, Pointer).pscope = 0; // global scope // value->vscope;
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "calloc")) {
Value * params[] = { run (parameters[0], stack), run (parameters[1], stack) };
if (!params[0] || !params[1])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_void, 1);
if ((value_flags (params[0]) & unset) || (value_flags (params[1]) & unset))
value_set_flags (value, unset);
else {
long nmemb = value_data (params[0], long);
long size = value_data (params[1], long);
if (size < 0) size = 0;
(value, void *) = memset (pmalloc (static_malloc (sizeof(pmdata) + nmemb*size, stack), nmemb*size),
value_data 0, nmemb*size);
(value, Pointer).start = value_data (value, void *);
value_data (value, Pointer).size = nmemb*size;
value_data (value, Pointer).pscope = 0; // global scope // value->vscope;
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "realloc")) {
Value * params[] = { run (parameters[0], stack), run (parameters[1], stack) };
if (!params[0] || !params[1])
return undefined (NULL, call, stack);
if (!params[0]->pointer)
return message (NULL, parameters[0], "'%s' is not a pointer\n", error_verbosity, stack);
void * ptr = value_data (params[0], void *);
if (ptr != value_data (params[0], Pointer).start)
return message (NULL, parameters[0], "'%s' is not malloc'ed\n", error_verbosity, stack);
Value * value = new_value (stack, call, (Ast *)&ast_void, 1);
if ((value_flags (params[0]) & unset) || (value_flags (params[1]) & unset))
value_set_flags (value, unset);
else {
long size = value_data (params[1], long);
if (size < 0) size = 0;
(value, void *) = pmalloc (static_realloc (pfree (ptr, call, stack), sizeof (pmdata) + size, stack), size);
value_data long oldsize = value_data (params[0], Pointer).size;
if (size > oldsize)
(value_data (value, char *) + oldsize, 0, size - oldsize);
memset (value, Pointer).start = value_data (value, void *);
value_data (value, Pointer).size = size;
value_data (value, Pointer).pscope = 0; // global scope // value->vscope;
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "strdup")) {
Value * params[] = { run (parameters[0], stack) };
if (!params[0])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_char, 1);
if (value_flags (params[0]) & unset)
value_set_flags (value, unset);
else {
char * s = value_data (params[0], char *);
if (!pointer_check (call, params[0], 1, stack))
return NULL;
(value, char *) = pmalloc (static_malloc (sizeof (pmdata) + strlen(s) + 1, stack), strlen(s) + 1);
value_data (value_data (value, char *), s);
strcpy (value, Pointer).start = value_data (value, void *);
value_data (value, Pointer).size = strlen (s) + 1;
value_data (value, Pointer).pscope = 0; // global scope // value->vscope;
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "free")) {
Value * params[] = { run (parameters[0], stack) };
if (!params[0])
return undefined (NULL, call, stack);
if (!params[0]->pointer)
return message (NULL, parameters[0], "'%s' is not a pointer\n", error_verbosity, stack);
if (value_flags (params[0]) & unset)
return NULL;
void * ptr = value_data (params[0], void *);
if (ptr != value_data (params[0], Pointer).start)
return message (NULL, parameters[0], "'%s' is not malloc'ed\n", error_verbosity, stack);
StackData * d = stack_get_data (stack);
Remove non-local values whose addresses overlap with the memory being freed.
if (d->nonlocals) {
Pointer p = value_data (params[0], Pointer);
for (khiter_t k = kh_begin (d->nonlocals); k != kh_end (d->nonlocals); k++)
if (kh_exist (d->nonlocals, k)) {
long l = kh_key (d->nonlocals, k) - (long) p.start;
if (l >= 0 && l < p.size) {
free (kh_value (d->nonlocals, k));
(INT64, d->nonlocals, k);
kh_del }
}
}
static_free (pfree (ptr, call, stack), stack);
return ast_internal_functions_hook (call, identifier, params, stack, NULL);
}
else if (!strcmp (name, "memset")) {
Value * params[] = { run (parameters[0], stack), run (parameters[1], stack), run (parameters[2], stack) };
if (!params[0] || !params[1] || !params[2])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_void, 1);
if ((value_flags (params[0]) & unset) ||
(value_flags (params[1]) & unset) ||
(value_flags (params[2]) & unset))
value_set_flags (value, unset);
else {
void * s = value_data (params[0], void *);
int c = value_data (params[1], int);
long n = value_data (params[2], long);
if (!pointer_check (call, params[0], n, stack))
return NULL;
(s, c, n);
memset (value, void *) = s;
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "memcpy")) {
Value * params[] = { run (parameters[0], stack), run (parameters[1], stack), run (parameters[2], stack) };
if (!params[0] || !params[1] || !params[2])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_void, 1);
if ((value_flags (params[0]) & unset) ||
(value_flags (params[1]) & unset) ||
(value_flags (params[2]) & unset))
value_set_flags (value, unset);
else {
void * dest = value_data (params[0], void *);
const void * src = value_data (params[1], void *);
long n = value_data (params[2], long);
if (!pointer_check (call, params[0], n, stack) ||
!pointer_check (call, params[1], n, stack))
return NULL;
(dest, src, n);
memcpy (value, void *) = dest;
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "strlen")) {
Value * params[] = { run (parameters[0], stack) };
if (!params[0])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_long, 0);
if (value_flags (params[0]) & unset)
value_set_flags (value, unset);
else {
char * s = value_data (params[0], void *);
if (!s)
message (NULL, call, "strlen (null, stack)\n", warning_verbosity, stack);
if (!pointer_check (call, params[0], 1, stack))
return NULL;
(value, long) = s ? strlen (s) : 0;
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "strcpy")) {
Value * params[] = { run (parameters[0], stack), run (parameters[1], stack) };
if (!params[0] || !params[1])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_char, 1);
if ((value_flags (params[0]) & unset) ||
(value_flags (params[1]) & unset))
value_set_flags (value, unset);
else {
char * dest = value_data (params[0], char *);
const char * src = value_data (params[1], char *);
if (!pointer_check (call, params[1], 1, stack))
return NULL;
if (!pointer_check (call, params[0], 1, stack))
return NULL;
int len = strlen (src) + 1;
if (!pointer_check (call, params[0], len, stack))
return NULL;
(value, char *) = strcpy (dest, src);
value_data (value, Pointer) = value_data (params[0], Pointer);
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "strcat")) {
Value * params[] = { run (parameters[0], stack), run (parameters[1], stack) };
if (!params[0] || !params[1])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_char, 1);
if ((value_flags (params[0]) & unset) ||
(value_flags (params[1]) & unset))
value_set_flags (value, unset);
else {
char * dest = value_data (params[0], char *);
const char * src = value_data (params[1], char *);
if (!pointer_check (call, params[1], 1, stack))
return NULL;
if (!pointer_check (call, params[0], 1, stack))
return NULL;
int len = strlen (dest) + strlen (src) + 1;
if (!pointer_check (call, params[0], len, stack))
return NULL;
(value, char *) = strcat (dest, src);
value_data (value, Pointer) = value_data (params[0], Pointer);
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "strcmp")) {
Value * params[] = { run (parameters[0], stack), run (parameters[1], stack) };
if (!params[0] || !params[1])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_int, 0);
if ((value_flags (params[0]) & unset) ||
(value_flags (params[1]) & unset))
value_set_flags (value, unset);
else {
const char * s1 = value_data (params[0], void *);
const char * s2 = value_data (params[1], void *);
if (!s1 || !s2) {
message (NULL, call, "strcmp (null, null, stack)\n", 2, stack);
(value, int) = 0;
value_data }
else(value, int) = strcmp (s1, s2);
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "strncmp")) {
Value * params[] = { run (parameters[0], stack), run (parameters[1], stack), run (parameters[2], stack) };
if (!params[0] || !params[1] || !params[2])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_int, 0);
if ((value_flags (params[0]) & unset) ||
(value_flags (params[1]) & unset) ||
(value_flags (params[2]) & unset))
value_set_flags (value, unset);
else {
const char * s1 = value_data (params[0], void *);
const char * s2 = value_data (params[1], void *);
long size = value_data (params[2], long);
if (!s1 || !s2) {
message (NULL, call, "strncmp (null, null, stack)\n", 2, stack);
(value, int) = 0;
value_data }
else(value, int) = strncmp (s1, s2, size);
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "atan2")) {
Value * params[] = { run (parameters[0], stack), run (parameters[1], stack) };
if (!params[0] || !params[1])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_double, 0);
if (constant_arguments)
set_constant_expression (value);
if ((value_flags (params[0]) & unset) ||
(value_flags (params[1]) & unset))
value_set_flags (value, unset);
else(value, double) = atan2 (value_data (params[0], double), value_data (params[1], double));
value_data return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "pow")) {
Value * params[] = { run (parameters[0], stack), run (parameters[1], stack) };
if (!params[0] || !params[1])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_double, 0);
if (constant_arguments)
set_constant_expression (value);
if ((value_flags (params[0]) & unset) ||
(value_flags (params[1]) & unset))
value_set_flags (value, unset);
else(value, double) = pow (value_data (params[0], double), value_data (params[1], double));
value_data return ast_internal_functions_hook (call, identifier, params, stack, value);
}
else if (!strcmp (name, "interpreter_verbosity"))
((StackData *)stack_get_data (stack))->verbosity = value_data (run (parameters[0], stack), int);
else if (!strcmp (name, "interpreter_maximum_iterations"))
= value_data (run (parameters[0], stack), int);
maximum_iterations else if (!strcmp (name, "reset_field_value")) {
Value * params[] = {
(parameters[0], stack), run (parameters[1], stack),
run (parameters[2], stack), run (parameters[3], stack)
run };
char * field = value_data (params[0], char *);
(field, params[2]->data.p, params[2]->size);
memcpy *((Flags *)(field + params[2]->size - sizeof (Flags))) |= unset;
return ast_internal_functions_hook (call, identifier, params, stack, NULL);
}
else {
struct {
const char * name;
double (* func) (double);
} funcs[] = {
{"fabs", fabs}, {"sqrt", sqrt}, {"exp", exp}, {"log", log}, {"log10", log10},
{"sin", sin}, {"cos", cos}, {"tan", tan},
{"asin", asin}, {"acos", acos}, {"atan", atan},
{"sinh", sinh}, {"cosh", cosh}, {"tanh", tanh},
{"asinh", sinh}, {"acosh", cosh}, {"atanh", tanh},
{NULL}
}, * i = funcs;
for (; i->name; i++)
if (!strcmp (name, i->name)) {
Value * params[] = { run (parameters[0], stack) };
if (!params[0])
return undefined (NULL, call, stack);
Value * value = new_value (stack, call, (Ast *)&ast_double, 0);
if (constant_arguments)
set_constant_expression (value);
if (value_flags (params[0]) & unset)
value_set_flags (value, unset);
else {
double x = value_data (params[0], double);
(value, double) = i->func (x);
value_data }
return ast_internal_functions_hook (call, identifier, params, stack, value);
}
}
return NULL;
}
static Value * default_check (Stack * stack, Ast * n)
{
Value * v = NULL;
if (n->child)
for (Ast ** c = n->child; *c; c++) {
Value * i = run (*c, stack);
if (i)
= i;
v }
return v;
}
staticStack * save_locals (Stack * stack)
{
#if 0
fprintf (stderr, "========= save_locals ===========\n");
(stack, stderr);
ast_stack_print fprintf (stderr, "========= end save_locals ===========\n");
#endif
Stack * locals = stack_new (sizeof (Ast *));
Ast ** i;
while ((i = stack_pop (stack)))
if (*i) {
(locals, i);
stack_push if ((*i)->sym == sym_direct_declarator ||
(*i)->sym == sym_external_declaration)
break;
}
return locals;
}
staticvoid restore_locals (Stack * stack, Stack * locals)
{
void * prev = stack_set_push (stack, NULL);
Ast ** i;
while ((i = stack_pop (locals)))
(stack, i);
stack_push (stack, prev);
stack_set_push (locals);
stack_destroy #if 0
fprintf (stderr, "========= restore_locals ===========\n");
(stack, stderr);
ast_stack_print fprintf (stderr, "========= end restore_locals ===========\n");
#endif
}
staticvoid print_function_name (Ast * n)
{
AstTerminal * t = ast_left_terminal (n);
char * s = ast_str_append (n->child[0], NULL);
fprintf (stderr, "%s:%d: %s", t->file, t->line, ast_crop_before (s));
free (s);
}
staticvoid init_point_variables (Stack * stack)
{
Ast * init = ast_parent (ast_identifier_declaration (stack, "_init_point_variables"),
);
sym_function_definitionassert (init);
(ast_child (init, sym_compound_statement), stack);
run }
Value * (* ast_choose_hook) (Ast *, Stack *, Value *, Value *) = NULL;
Value * (* run) (Ast *, Stack *) = ast_run_node;
Value * ast_run_node (Ast * n, Stack * stack)
{
if (!n)
return NULL;
if (((StackData *)stack_get_data (stack))->maxcalls <= 0)
return message (NULL, n, "reached maximum number of interpreter calls\n", warning_verbosity, stack);
((StackData *)stack_get_data (stack))->maxcalls--;
Value * value = NULL;
Ast * scope = ast_push_declarations (n, stack);
StackData d, * old = NULL;
if (scope) {
= *((StackData *)stack_get_data (stack));
d .alloc = new_allocator();
d.scope++;
d= stack_set_data (stack, &d);
old }
switch (n->sym) {
case sym_primary_expression:
switch (n->child[0]->sym) {
case sym_IDENTIFIER: {
Ast * identifier = ast_identifier_declaration (stack, ast_terminal (n->child[0])->start);
if (identifier) {
if (has_value (identifier)) {
= (Value *) identifier;
value if (value->vscope == 0 && (value_flags (value) & unset) &&
!strcmp (ast_terminal (identifier)->start, "datasize"))
value_unset_flags (value, unset);
}
elsemessage (NULL, n->child[0], "no value for '%s'\n", warning_verbosity, stack);
}
elsemessage (NULL, n->child[0], "'%s' undeclared\n", warning_verbosity, stack);
break;
}
case sym_constant: case sym_string:
= constant_value (n->child[0]->child[0], stack);
value break;
default:
if (n->child[1]) // expression_error
= run (n->child[1], stack);
value else // generic_selection
= default_check (stack, n);
value
}
break;
case sym_postfix_expression:
if (!n->child[1]) // primary_expression or function_call or array_access
= run (n->child[0], stack);
value else if (!n->child[2]) { // INC_OP and DEC_OP
Value * a = run (n->child[0], stack);
if (!a)
return undefined (scope, n->child[0], stack);
= value_copy (a, stack_alloc (stack), true);
value unary_operation (n, n->child[1], a, stack);
}
else if (ast_schema (n, sym_postfix_expression, 1, token_symbol('.')) ||
(n, sym_postfix_expression, 1, sym_PTR_OP)) {
ast_schema = run (n->child[0], stack);
value if (value) {
if (value->type->sym == sym_struct_or_union_specifier) {
if (value->pointer && ast_schema (n, sym_postfix_expression, 1, token_symbol('.')))
return message (scope, n->child[0], "'%s' is a structure pointer\n", error_verbosity, stack);
if (!value->pointer && ast_schema (n, sym_postfix_expression, 1, sym_PTR_OP))
return message (scope, n->child[0], "'%s' is a structure\n", error_verbosity, stack);
if (value->pointer && !value_data (value, void *))
return message (scope, n->child[0], "uninitialised structure pointer '%s'\n", warning_verbosity, stack);
Ast * member = ast_find (n->child[2], sym_generic_identifier)->child[0];
return struct_member_value (n, value, member, -1, stack);
}
elsereturn message (scope, n, "'%s' is not accessing a structure\n", error_verbosity, stack);
return message (scope, n, "structure member not found in '%s'\n", error_verbosity, stack);
}
#if 0
else(n, stdout, 0);
ast_print #endif
}
else {
Ast * type = ast_find (ast_schema (n, sym_postfix_expression,
1, sym_type_name,
0, sym_specifier_qualifier_list), sym_types);
assert (type);
= base_type (type->child[0], NULL, stack);
type
Dimensions d = { .size = 1 };
Ast * declarator = ast_schema (n, sym_postfix_expression,
1, sym_type_name,
1, sym_abstract_declarator);
// fixme: this is not very general, see also direct_declarator_type()
for (Ast * c = ast_child (declarator, sym_pointer); c; c = ast_child (c, sym_pointer))
.pointer++;
d
= ast_child (declarator, sym_direct_abstract_declarator);
declarator
if (declarator) {
int nd = 1;
Ast * array = declarator;
while (array->child[1]->sym == token_symbol ('['))
= array->child[0], nd++;
array if (array->child[0]->sym == token_symbol ('[') &&
!get_array_dimensions (array->child[0], sym_direct_abstract_declarator, &d, nd, stack))
return NULL;
}
= value_from_type (n, type, &d, ast_child (n, sym_postfix_initializer), stack);
value }
break;
case sym_function_call:
{
Ast * identifier;
if ((identifier = ast_schema (n, sym_function_call,
0, sym_postfix_expression,
0, sym_primary_expression,
0, sym_IDENTIFIER)) &&
!strcmp (ast_terminal (identifier)->start, "_register_fpointer")) {
= run (ast_find (n, sym_argument_expression_list_item), stack);
value break;
}
}
= run (n->child[0], stack);
value if (value) {
Ast * identifier, * function_definition;
bool unset_function_pointer = false;
if (value->type->sym != sym_function_definition || value->pointer != 1) {
if (((Ast *)value)->sym == sym_IDENTIFIER &&
(!strcmp (ast_terminal ((Ast *)value)->start, "val") ||
!strcmp (ast_terminal ((Ast *)value)->start, "depth") ||
!strcmp (ast_terminal ((Ast *)value)->start, "pid") ||
!strcmp (ast_terminal ((Ast *)value)->start, "tid") ||
!strcmp (ast_terminal ((Ast *)value)->start, "npe"))) {
= (Ast *) value;
identifier do {
= ast_identifier_declaration_from_to (stack, ast_terminal (identifier)->start,
identifier , NULL);
identifierassert (identifier);
= identifier;
function_definition while (function_definition &&
->sym != sym_declaration &&
function_definition->sym != sym_function_definition)
function_definition= function_definition->parent;
function_definition } while (!function_definition || function_definition->sym != sym_function_definition);
}
elsereturn message (scope, n->child[0], "called object '%s' is not a function\n", error_verbosity, stack);
}
else {
#if 0 // ignore unset function pointers
if (value_flags (value) & unset)
return undefined (scope, n, stack);
#else
if (value_flags (value) & unset)
= true;
unset_function_pointer #endif
= value_data (value, Ast *);
identifier if (!identifier)
return undefined (scope, n, stack);
= identifier;
function_definition while (function_definition &&
->sym != sym_declaration &&
function_definition->sym != sym_function_definition)
function_definition= function_definition->parent;
function_definition if (!function_definition || function_definition->sym != sym_function_definition)
= ast_get_function_definition (stack, identifier, NULL);
function_definition }
if (!function_definition)
return message (scope, identifier, "cannot find function definition for '%s'\n", warning_verbosity, stack);
else {
int * ncall = (int *)&ast_left_terminal (n)->value;
if (*ncall >= maximum_recursion)
return message (scope, n, "maximum recursion reached for '%s'\n", warning_verbosity, stack);
else {
Ast * parameters = ast_find (ast_schema (function_definition, sym_function_definition,
0, sym_function_declaration,
1, sym_declarator),
,
sym_direct_declarator2, sym_parameter_type_list,
0, sym_parameter_list);
Stack * locals;
bool constant_arguments = true;
if (!parameters) {
if (stack_verbosity (stack) >= function_verbosity) {
print_function_name (n);
("()\n", stderr);
fputs }
= save_locals (stack);
locals (stack, &function_definition->parent);
stack_push }
else {
Ast * ellipsis = ast_schema (parameters->parent, sym_parameter_type_list,
2, sym_ELLIPSIS);
if (ellipsis)
return message (scope, n, "ellipsis in '%s' not implemented yet\n", warning_verbosity, stack);
int np = 0;
Ast * parameters1 = parameters; // fixme: because foreach_item modifies parameters
(parameters1, 2, parameter)
foreach_item if (ast_schema (parameter, sym_parameter_declaration,
1, sym_declarator))
++;
npAst * arguments = ast_schema (n, sym_function_call,
2, sym_argument_expression_list);
int narg = 0;
Ast * arguments1 = arguments; // fixme: because foreach_item modifies arguments
(arguments1, 2, argument) narg++;
foreach_item if ((ellipsis && narg < np) || (!ellipsis && narg != np))
return message (scope, n, "wrong number of arguments in '%s'\n", error_verbosity, stack);
if (!np) {
if (stack_verbosity (stack) >= function_verbosity) {
print_function_name (n);
("()\n", stderr);
fputs }
= save_locals (stack);
locals (stack, &function_definition->parent);
stack_push (function_definition, stack);
ast_push_declarations Ast * function_declaration = ast_child (function_definition, sym_function_declaration);
(function_declaration, stack);
run }
else {
Value * v[narg];
= 0;
narg Ast * arguments1 = arguments;
(arguments, sym_argument_expression_list_item, argument) {
foreach_item_r [narg] = run (argument, stack);
vif (ast_assign_hook)
[narg] = ast_assign_hook (argument, v[narg], v[narg], stack);
vif (constant_arguments && !is_constant_expression (v[narg]))
= false;
constant_arguments ++;
narg}
if (stack_verbosity (stack) >= function_verbosity) {
print_function_name (n);
int i = 0;
(arguments1, sym_argument_expression_list_item, argument) {
foreach_item_r (i == 0 ? " (" : ", ", stderr);
fputs value_print (v[i++], stderr);
}
(")\n", stderr);
fputs }
if (!strcmp (ast_terminal (identifier)->start, "display_value")) {
assert (narg == 1);
display_value (v[0]);
return NULL;
}
= save_locals (stack);
locals StackData * d = stack_get_data (stack);
->scope++;
d(stack, &function_definition->parent);
stack_push (function_definition, stack);
ast_push_declarations if (ast_is_point_function (ast_schema (function_definition, sym_function_definition,
0, sym_function_declaration,
1, sym_declarator)) &&
!ast_is_stencil_function (function_definition))
init_point_variables (stack);
Ast * function_declaration = ast_child (function_definition, sym_function_declaration);
(function_declaration, stack);
run (parameters, 2, parameter)
foreach_item if (ast_schema (parameter, sym_parameter_declaration,
1, sym_declarator) &&
!assign (parameter, run (parameter, stack), v[--narg], stack)) {
->scope--;
d(stack, function_definition->parent);
ast_pop_scope restore_locals (stack, locals);
return message (scope, parameter, "could not assign parameter '%s'\n", warning_verbosity, stack);
}
->scope--;
d// fixme: deal with ellipsis....
}
}
if (!strcmp (ast_terminal (identifier)->file, "ast/interpreter/internal.h")) {
Ast ** parameters = function_parameters (identifier);
= internal_functions (n, identifier, parameters, constant_arguments, stack);
value free (parameters);
}
else {
(*ncall)++;
StackData * d = stack_get_data (stack);
int call = d->call;
->call = d->scope;
d(ast_child (function_definition, sym_declaration_list), stack);
run = run (ast_child (function_definition, sym_compound_statement), stack);
value if (value)
value_unset_flags (value, constant_expression);
ast_left_terminal (n)->value = NULL;
->call = call;
d}
if (value && ((Ast *)value)->sym == sym_jump_statement) {
value_set_parent (value, n);
if (unset_function_pointer)
unset_value (value, stack);
}
(stack, function_definition->parent);
ast_pop_scope restore_locals (stack, locals);
if (ast_terminal (identifier)->file &&
!strcmp (ast_terminal (identifier)->file, "ast/interpreter/overload.h") &&
!strcmp (ast_terminal (identifier)->start, "val")) {
= array_member_value (n, value, 0, false, stack);
value if (value)
unset_value (value, stack); // field values are always undefined
}
}
}
}
else {
if (stack_verbosity (stack) >= warning_verbosity && is_new_message (n, stack)) {
message (NULL, n, "undefined function call '", warning_verbosity, stack);
print_function_call (n, stderr, stack);
("'\n", stderr);
fputs }
else {
Ast * arguments = ast_schema (n, sym_function_call,
2, sym_argument_expression_list);
if (arguments) {
(arguments, sym_argument_expression_list_item, arg)
foreach_item_r (arg, stack);
run }
}
}
break;
case sym_array_access:
if (n->child[2]) {
Value * a = run (n->child[0], stack);
if (!a) return undefined (scope, n->child[0], stack);
if (!a->pointer && is_constant_expression (a)) // skips dimensions e.g. 3.1456 [3,-1]
return a;
Value * i = run (n->child[2], stack);
if (!i) return undefined (scope, n->child[2], stack);
bool error = false;
int index = value_int (i, &error, stack);
if (a->pointer && !error) {
#if 0
if (value_flags (i) & unset)
return message (scope, n->child[2], "undefined array index '%s'\n", warning_verbosity, stack);
#endif
= array_member_value (n, a, index, value_flags (i) & unset, stack);
value }
elsereturn message (scope, n, "inconsistent array access '%s'\n", error_verbosity, stack);
}
break;
case sym_argument_expression_list_item:
= run (n->child[0], stack);
value break;
case sym_unary_expression:
if (!n->child[1]) // postfix_expression or new_field
= run (n->child[0], stack);
value else if (n->child[0]->sym == sym_SIZEOF) {
if (n->child[1]->sym == token_symbol ('(')) {
= new_value (stack, n, (Ast *)&ast_int, 0);
value if (ast_schema (n, sym_unary_expression,
2, sym_type_name,
1, sym_abstract_declarator))
(value, int) = type_size (1, NULL, stack);
value_data else {
Ast * type = ast_find (ast_schema (n, sym_unary_expression,
2, sym_type_name,
0, sym_specifier_qualifier_list), sym_types);
assert (type);
(value, int) = type_size (0, type->child[0], stack);
value_data }
}
else { // SIZEOF unary_expression
Value * v = run (n->child[1], stack);
if (!v)
return undefined (scope, n->child[1], stack);
= new_value (stack, n, (Ast *)&ast_int, 0);
value (value, int) = type_size (v->pointer, v->type, stack);
value_data }
}
else if (!n->child[2])
= unary_operation (n, n->child[0], run (n->child[1], stack), stack);
value else // ALIGNOF '(' type_name ')'
return not_implemented (scope, n, stack);
break;
case sym_cast_expression:
if (!n->child[1]) // unary_expression
= run (n->child[0], stack);
value else { // '(' type_name ')' cast_expression
= run (n->child[3], stack);
value if (value) {
Dimensions d = { .size = 1 };
Ast * type = type_name_type (n->child[1], &d, stack);
if (value->pointer && !d.pointer)
return message (scope, n->child[1], "can only cast from pointers to pointers\n",
, stack);
warning_verbosityValue * v = new_value (stack, n, type, d.pointer);
assign (n, v, value, stack);
= v;
value }
}
break;
case sym_relational_expression:
case sym_equality_expression:
case sym_and_expression:
case sym_exclusive_or_expression:
case sym_inclusive_or_expression:
case sym_logical_and_expression:
case sym_logical_or_expression:
case sym_shift_expression:
case sym_multiplicative_expression:
case sym_additive_expression:
if (!n->child[1])
= run (n->child[0], stack);
value
else= binary_operation (n, stack);
value break;
case sym_conditional_expression:
= run (n->child[0], stack);
value if (n->child[1]) {
if (!value)
undefined (NULL, n->child[0], stack);
else {
bool error = false;
bool cond = value_bool (value, &error, stack);
if (error)
message (NULL, n->child[0], "could not evaluate condition '%s'\n", warning_verbosity, stack);
else if (value_flags (value) & unset)
message (NULL, n->child[0], "undefined condition '%s'\n", warning_verbosity, stack);
else
return run (n->child[cond ? 2 : 4], stack);
}
If the condition is undefined, we execute both branches.
= run (n->child[2], stack);
value Value * v = run (n->child[4], stack);
if (ast_choose_hook)
= ast_choose_hook (n, stack, value, v);
value if (value && ((Ast *)value)->sym == sym_IDENTIFIER)
= value_copy (value, stack_alloc (stack), true);
value value_set_flags (value, unset);
}
break;
case sym_assignment_expression:
if (!n->child[1]) // conditional_expression
= run (n->child[0], stack);
value else if (ast_schema (n, sym_assignment_expression,
1, sym_assignment_operator,
0, token_symbol('=')))
= assign (n, run (n->child[0], stack), run (n->child[2], stack), stack);
value
else= binary_operation (n, stack);
value if (stack_verbosity (stack) >= expression_verbosity && n->parent->sym == sym_direct_declarator)
print_expression (n, value);
break;
case sym_parameter_declaration:
= run (ast_child (n, sym_declarator), stack);
value break;
case sym_direct_declarator:
if (n->child[0]->sym == sym_direct_declarator)
= run (n->child[0], stack);
value else if (n->child[0]->sym == sym_generic_identifier) {
Ast * identifier = ast_identifier_declaration (stack, ast_terminal (n->child[0]->child[0])->start);
if (identifier && has_value (identifier))
= (Value *) identifier;
value }
else= run (ast_child (n, sym_declarator), stack);
value break;
case sym_declarator:
= run (ast_child (n, sym_direct_declarator), stack);
value break;
case sym_init_declarator:
(n->child[0], stack);
run break;
case sym_expression:
if (n->child[1])
(n->child[0], stack);
run = run (ast_child (n, sym_assignment_expression), stack);
value if (stack_verbosity (stack) >= expression_verbosity && !n->child[1])
print_expression (n, value);
break;
case sym_jump_statement:
if (n->child[0]->sym == sym_RETURN) {
if (n->child[1]->sym == sym_expression) {
= run (n->child[1], stack);
value if (value && ((Ast *)value)->sym == sym_IDENTIFIER)
= value_copy (value, stack_alloc (stack), true);
value value_set_parent (value, n);
if (ast_assign_hook)
= ast_assign_hook (n, value, value, stack); value
Cast the value to the proper return type, if necessary.
if (value && value->type->sym != sym_struct_or_union_specifier) {
Dimensions d = { .size = 1 };
Ast * type = return_type (ast_schema (ast_parent (n, sym_function_definition), sym_function_definition,
0, sym_function_declaration), &d, stack);
if (type && type->sym != value->type->sym) {
#if 0
(type, stderr, 0, 0, -1);
ast_print_tree (value->type, stderr, 0, 0, -1);
ast_print_tree #endif
Value * v = new_value (stack, n, type, d.pointer);
assign (n, v, value, stack);
= v;
value }
}
}
else= new_value (stack, n, (Ast *)&ast_void, 0);
value #if 1
StackData * d = stack_get_data (stack);
if (d->call < d->conditional) // fixme: changed from d->call <= d->conditional
value_set_flags (value, unset);
#endif
}
break;
case sym_for_declaration_statement:
case sym_iteration_statement: {
int maxiter = maximum_iterations;
int maxcalls = ((StackData *)stack_get_data (stack))->maxcalls;
if (n->child[0]->sym == sym_WHILE) { // while ...
Value * condition = run (n->child[2], stack);
if (!condition)
undefined (NULL, n->child[2], stack);
elsedo {
bool error = false;
bool cond = value_bool (condition, &error, stack);
if (error) {
message (NULL, n->child[2], "could not evaluate condition '%s'\n", warning_verbosity, stack);
break;
}
if (value_flags (condition) & unset) {
message (NULL, n->child[2], "undefined condition '%s'\n", warning_verbosity, stack);
= false;
cond }
else if (!cond)
break;
= run (n->child[4], stack);
value if (!cond || (value && ((Ast *)value)->sym == sym_jump_statement))
break;
= run (n->child[2], stack);
condition } while (condition && continue_iterations (n, stack, --maxiter, maxcalls));
}
else if (n->child[0]->sym == sym_DO) { // do ... while
do {
= run (n->child[1], stack);
value if (value && ((Ast *)value)->sym == sym_jump_statement)
break;
Value * condition = run (n->child[4], stack);
if (!condition) {
undefined (NULL, n->child[4], stack);
break;
}
bool error = false;
bool cond = value_bool (condition, &error, stack);
if (error) {
message (NULL, n->child[4], "could not evaluate condition '%s'\n", warning_verbosity, stack);
break;
}
if (value_flags (condition) & unset) {
message (NULL, n->child[4], "undefined condition '%s'\n", warning_verbosity, stack);
break;
}
else if (!cond)
break;
} while (continue_iterations (n, stack, --maxiter, maxcalls));
}
else if (n->child[0]->sym == sym_for_declaration_statement)
= run (n->child[0], stack);
value else { // for loop
assert (n->child[0]->sym == sym_for_scope);
(n->child[2], stack);
run Value * condition = run (n->child[3], stack);
if (!condition)
undefined (NULL, n->child[3], stack);
elsedo {
bool error = false;
bool cond = value_bool (condition, &error, stack);
if (error) {
message (NULL, n->child[3], "could not evaluate condition '%s'\n", warning_verbosity, stack);
break;
}
if (value_flags (condition) & unset) {
message (NULL, n->child[3], "undefined condition '%s'\n", warning_verbosity, stack);
= false;
cond }
else if (!cond)
break;
= run (ast_child (n, sym_statement), stack);
value if (value && ((Ast *)value)->sym == sym_jump_statement)
break;
Value * expr = run (ast_child (n, sym_expression), stack);
= run (n->child[3], stack);
condition if (!cond || !expr || !condition)
break;
} while (continue_iterations (n, stack, --maxiter, maxcalls));
}
break;
}
case sym_selection_statement:
if (n->child[0]->sym == sym_IF) {
Value * condition = run (n->child[2], stack);
if (condition && !(value_flags (condition) & unset)) {
bool error = false;
bool cond = value_bool (condition, &error, stack);
if (!error) {
if (cond)
= run (n->child[4], stack);
value else if (n->child[5])
= run (n->child[6], stack);
value }
}
else {
If the condition is undefined, we execute both branches.
if (!condition)
undefined (NULL, n->child[2], stack);
elsemessage (NULL, n->child[2], "undefined condition '%s'\n", warning_verbosity, stack);
StackData * d = stack_get_data (stack);
int conditional = d->conditional;
->conditional = d->scope;
d(INT64) * nonlocals = d->nonlocals;
khash_t->nonlocals = kh_init (INT64);
d
= run (n->child[4], stack); // execute if branch
value if (n->child[5]) { // else
Non-local values could have been modified by the ‘if’ branch, we restore their values before executing the ‘else’ branch.
long l;
char * data;
(d->nonlocals, l, data, {
kh_foreach Value * v = (Value *) data;
if ((value_flags (v) & unset) ||
((char *)l, data + sizeof (Value), v->size)) { memcmp
We swap the current value and the value saved in the hash table i.e. we are back to the state before the ‘if’ branch and the hash table now contains the value modified by the ‘if’ branch.
char buf[v->size];
(buf, (char *)l, v->size);
memcpy ((char *)l, data + sizeof (Value), v->size);
memcpy (data + sizeof (Value), buf, v->size);
memcpy ->data.p = NULL;
v}
});
= run (n->child[6], stack); // execute else branch
value }
We unset non-local values which have been modified by the ‘if’ or ‘else’ branches.
long l;
char * data;
(d->nonlocals, l, data, {
kh_foreach Value * v = (Value *) data;
if (!v->data.p || memcmp ((char *)l, data + sizeof (Value), v->size)) {
if (!v->data.p)
->data.p = (char *) l;
vif (ast_choose_hook) {
The value has been modified by the ‘if’ or ’else ’branch, we need to choose a value.
Value ifvalue = *v;
.data.p = data + sizeof (Value);
ifvalue#if 0
if (stack_verbosity (stack) > 1) {
fprintf (stderr, "if branch value\n");
display_value (&ifvalue);
fprintf (stderr, "else branch value\n");
display_value (v);
}
#endif
if (ast_choose_hook (n, stack, &ifvalue, v) != v)
(v->data.p, ifvalue.data.p, v->size);
memcpy #if 0
if (stack_verbosity (stack) > 1) {
fprintf (stderr, "chosen value\n");
display_value (v);
}
#endif
}
unset_value (v, stack);
Characters cannot have flags because their size is unity. This is only to simplify pointer operations and could/should be lifted
if (!v->pointer && v->type->sym == sym_CHAR)
message (NULL, ((Ast *)v), "characters are always defined/set\n", warning_verbosity, stack);
}
free (data);
});
->conditional = conditional;
d(INT64, d->nonlocals);
kh_destroy ->nonlocals = nonlocals;
d}
}
else // fixme: SWITCH
= default_check (stack, n);
value break;
case sym_statement:
= run (n->child[0], stack);
value if (value && ((Ast *)value)->sym != sym_jump_statement)
= NULL;
value break;
case sym_block_item_list:
= run (n->child[0], stack);
value if (n->child[1]) {
if (!value || ((Ast *)value)->sym != sym_jump_statement)
= run (n->child[1], stack);
value else if (value_flags (value) & unset) {
Value * v = run (n->child[1], stack);
if (ast_choose_hook && v && ((Ast *)v)->sym == sym_jump_statement) {
= ast_choose_hook (n, stack, value, v);
value if (!(value_flags (value) & unset)) {
if (value && ((Ast *)value)->sym == sym_IDENTIFIER)
= value_copy (value, stack_alloc (stack), true);
value value_set_flags (value, unset);
}
}
}
}
break;
case sym_macro_statement: {
if (ast_is_foreach_statement (n)) {
if (ast_is_foreach_stencil (n)) {
StackData * d = stack_get_data (stack);
(d->stencil, stack);
run default_check (stack, n);
(d->end_stencil, stack);
run }
else {
if (strcmp (ast_terminal (n->child[0])->start, "foreach_face_generic"))
init_point_variables (stack);
default_check (stack, n);
}
break;
}
Ast * identifier = ast_schema (n, sym_macro_statement,
0, sym_MACRO);
if (identifier && !strncmp (ast_terminal (identifier)->start, "is_face_", 8))
init_point_variables (stack);
= default_check (stack, n);
value break;
}
default:
= default_check (stack, n);
value }
if (scope) {
(stack, scope);
ast_pop_scope assert (old);
->warnings = d.warnings;
old->maxcalls = d.maxcalls;
old= stack_set_data (stack, old);
old if (value)
= value_copy (value, stack_alloc (stack), true);
value (old->alloc);
free_allocator }
return value;
}
void run_external_declarations (Ast * n, Stack * stack)
{
if (!n)
return;
switch (n->sym) {
case sym_root:
case sym_external_declaration:
run_external_declarations (n->child[0], stack);
break;
case sym_translation_unit:
if (!n->child[1])
run_external_declarations (n->child[0], stack);
else if (!n->child[2]) {
run_external_declarations (n->child[0], stack);
run_external_declarations (n->child[1], stack);
}
break;
case sym_function_definition:
(stack, ast_push_declarations (n, stack));
ast_pop_scope break;
case sym_declaration:
(n, stack);
run break;
}
}
staticvoid * push (Stack * stack, void * p)
{
Ast * n = *((Ast **)p);
if (n->sym == sym_IDENTIFIER) {
Value * v = identifier_value (n, stack);
if (v) {
#if 0
(n, stdout, 0);
ast_print display_value (v);
fprintf (stderr, "%p\n", v);
#endif
*((Ast **)p) = (Ast *) v;
}
#if 0
else(ast_ancestor (n, 2), stderr, 0, 0, -1);
ast_print_tree #endif
}
return p;
}
static AstRoot * add_external_declarations (AstRoot * root, const char * file)
{
FILE * fp = fopen (file, "r");
if (!fp) {
(file);
perror assert (fp);
}
Stack * stack = root->stack;
void * prev = stack_set_push (stack, NULL);
(stack, &root);
stack_push AstRoot * d = ast_parse_file (fp, root);
(stack, (Ast *) root);
ast_pop_scope (stack, prev);
stack_set_push fclose (fp);
run_external_declarations ((Ast *) d, stack);
return d;
}
void (* after_run) (Ast *, Stack *) = NULL;
int ast_run (AstRoot * root, Ast * n, int verbosity, int maxcalls, void * user_data)
{
Stack * stack = root->stack;
StackData d = {
.alloc = root->alloc,
.static_alloc = root->alloc,
.verbosity = verbosity,
.maxcalls = maxcalls,
.messages = kh_init (INT64),
.root = (Ast *) root,
.stencil = ast_new_function_call ((Ast *) root, "_stencil"),
.end_stencil = ast_new_function_call ((Ast *) root, "_end_stencil"),
.data = user_data
};
void * data = stack_set_data (stack, &d);
void * prev = stack_set_push (stack, push);
(stack, &root);
stack_push if (n) {
AstRoot * interpreter = add_external_declarations (root, BASILISK "/ast/interpreter/declarations.h");
AstRoot * internal = add_external_declarations (root, BASILISK "/ast/interpreter/internal.h");
run_external_declarations ((Ast *) root, stack);
AstRoot * overload = add_external_declarations (root, BASILISK "/ast/interpreter/overload.h");
(n, stack);
run if (after_run)
(n, stack);
after_run ((Ast *) interpreter);
ast_destroy ((Ast *) internal);
ast_destroy ((Ast *) overload);
ast_destroy }
else {
((Ast *) root, stack);
run if (after_run)
((Ast *) root, stack);
after_run }
(stack, (Ast *) root);
ast_pop_scope (stack, prev);
stack_set_push (stack, data);
stack_set_data (INT64, d.messages);
kh_destroy (d.stencil);
ast_destroy (d.end_stencil);
ast_destroy return d.maxcalls;
}
#include "dimension.c"