src/ast/references.c
External references
Returns a list of external references.
#include <stdlib.h>
#include <string.h>
#include "ast.h"
#include "symbols.h"
typedef struct {
Ast * scope;
Stack * nonlocals, * attributes;
int n, dimension;
} Accelerator;
static
bool is_local_declaration (Ast * n, Stack * stack, Ast * scope)
{
if (!strcmp (ast_terminal (n)->start, "point"))
return true;
Ast ** d;
for (int i = 0; (d = stack_index (stack, i)); i++)
if (*d == n)
return true;
else if (*d == scope)
return false;
return false;
}
static
void external_references (Ast * n, Stack * stack, Accelerator * a);
static
void add_external_reference (const char * name, Stack * stack, Accelerator * a)
{
Ast * ref = ast_identifier_declaration (stack, name);
if (!ref) {
#if 0
fprintf (stderr, "%s:%d: warning: '%s' undeclared\n", ast_terminal (n)->file, ast_terminal (n)->line,
ast_terminal (n)->start);
#endif
return; // assumes this is OK i.e. this corresponds mostly with macros and undeclared library functions
}
if (!strcmp (ast_terminal (ref)->file, "ast/defaults.h")) // ignore "internal" variables and macros
return;
if (!is_local_declaration (ref, stack, a->scope) &&
!fast_stack_find (a->nonlocals, ast_terminal (ref)->start)) {
Function call
Ast * definition = ast_parent (ref, sym_function_definition), * def;
if (definition && (def = ast_find (definition, sym_direct_declarator,
0, sym_direct_declarator,
0, sym_generic_identifier,
0, sym_IDENTIFIER)) &&
!strcmp (ast_terminal (def)->start, name)) {
Accelerator b = *a;
b.scope = definition;
stack_push (stack, &definition);
external_references (definition, stack, &b);
ast_pop_scope (stack, definition);
a->nonlocals = b.nonlocals;
a->attributes = b.attributes;
}
stack_push (a->nonlocals, &ref);
}
}
static
void external_references (Ast * n, Stack * stack, Accelerator * a)
{
if (!n || n == ast_placeholder ||
(n->sym == sym_initializer && n->parent->sym == sym_parameter_declaration))
return;
Ast * scope = ast_push_declarations (n, stack);
if (n->child)
for (Ast ** c = n->child; *c; c++)
external_references (*c, stack, a);
if (ast_schema (n->parent, sym_primary_expression,
0, sym_IDENTIFIER) &&
strcmp (ast_terminal (n)->start, "_attribute"))
add_external_reference (ast_terminal (n)->start, stack, a);
else if ((n->sym == sym_IDENTIFIER && ast_attribute_access (ast_ancestor (n, 3), stack)) ||
(n = ast_attribute_array_access (ast_ancestor (n, 3)))) {
Scalar attribute
Ast * found = fast_stack_find (a->attributes, ast_terminal (n)->start);
if (!found) {
Ast * attributes = ast_find (ast_ancestor (ast_identifier_declaration (stack, "_Attributes"), 6),
sym_struct_declaration_list);
assert (attributes);
found = NULL;
foreach_item (attributes, 1, decl) {
Ast * list = ast_schema (decl, sym_struct_declaration,
1, sym_struct_declarator_list);
foreach_item (list, 2, j) {
Ast * identifier = ast_find (j, sym_IDENTIFIER);
if (identifier && !strcmp (ast_terminal (identifier)->start, ast_terminal (n)->start)) {
found = identifier; break;
}
}
if (found)
break;
}
if (found)
stack_push (a->attributes, &found);
}
}
ast_pop_scope (stack, scope);
}
static
char * add_reference (Ast * ref, char * references, Ast * scope, Stack * stack, Stack * functions)
{
const char * start = ast_terminal (ref)->start;
if (!strcmp (start, "NULL"))
return references;
AstDimensions dim = {0};
Ast * type = ast_identifier_type (ref, &dim, stack);
Ast * attributes = ast_parent (ref, sym_struct_or_union_specifier);
if (type == (Ast *) &ast_function) {
if (!strcmp (start, "qassert"))
return references;
Function pointers
if (ast_schema (ast_ancestor (ref, 4), sym_direct_declarator,
1, sym_declarator,
0, sym_pointer)) {
str_append (references, "{.name=\"", attributes ? "." : "", start,
"\",.type=sym_function_declaration");
if (attributes)
str_append (references, ",.nd=attroffset(", start, ")},");
else
str_append (references, ",.pointer=(void *)(long)", start, "},");
}
Function definitions
else if (ast_ancestor (ref, 6)->sym == sym_function_definition) {
str_append (references, "{.name=\"", attributes ? "." : "", start,
"\",.type=sym_function_definition,.pointer=(void *)(long)", start, "},");
if (!fast_stack_find (functions, ast_terminal (ref)->start))
stack_push (functions, &ref);
}
return references;
}
str_append (references, "{.name=\"", attributes ? "." : "", !strcmp (start, "val") ? "_val" : start, "\"");
Type
Ast * def;
if (ast_schema (ast_ancestor (type, 5), sym_declaration,
0, sym_declaration_specifiers,
0, sym_storage_class_specifier,
0, sym_TYPEDEF) &&
(def = ast_schema (ast_ancestor (type, 5), 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))) {
// typedef
if (!strcmp (ast_terminal (def)->start, "scalar"))
str_append (references, ",.type=sym_SCALAR");
else if (!strcmp (ast_terminal (def)->start, "vector"))
str_append (references, ",.type=sym_VECTOR");
else if (!strcmp (ast_terminal (def)->start, "tensor"))
str_append (references, ",.type=sym_TENSOR");
else if (!strcmp (ast_terminal (def)->start, "coord"))
str_append (references, ",.type=sym_COORD");
else if (!strcmp (ast_terminal (def)->start, "_coord"))
str_append (references, ",.type=sym__COORD");
else if (!strcmp (ast_terminal (def)->start, "vec4"))
str_append (references, ",.type=sym_VEC4");
else if (!strcmp (ast_terminal (def)->start, "ivec"))
str_append (references, ",.type=sym_IVEC");
else if (!strcmp (ast_terminal (def)->start, "bool"))
str_append (references, ",.type=sym_BOOL");
else
str_append (references, ",.type=sym_TYPEDEF");
}
else if (ref->parent->sym == sym_enumeration_constant)
str_append (references, ",.type=sym_enumeration_constant");
else {
char s[20]; snprintf (s, 19, "%d", type->sym);
str_append (references, ",.type=", s);
}
Is this a global variable?
if (!attributes && !ast_parent (ref, sym_compound_statement) && !ast_parent (ref, sym_parameter_declaration))
str_append (references, ",.global=1");
Assumes ’double *‘are references to arrays with ’nl’ elements. Fixme: this is very specific and should be made more general e.g. systematically using ‘fat pointers’ to get array sizes.
Ast * nl;
if (type->sym == sym_DOUBLE && dim.pointer == 1 && !dim.dimension && strcmp (ast_terminal (ref)->start, "_constant") &&
(nl = ast_identifier_declaration (stack, "nl"))) {
dim.pointer = 0;
dim.dimension = malloc (2*sizeof (Ast *));
dim.dimension[0] = nl;
dim.dimension[1] = NULL;
}
Pointer
if (!(attributes || ref->parent->sym == sym_enumeration_constant))
str_append (references, ",.pointer=(void *)", dim.pointer || (dim.dimension && (*dim.dimension)->sym != sym_VOID) ?
"" : "&", start);
Attribute offset or enumeration constant or number of pointer dereferences
if (attributes)
str_append (references, ",.nd=attroffset(", start, ")");
else if (ref->parent->sym == sym_enumeration_constant)
str_append (references, ",.nd=", start);
else if (dim.pointer) {
char s[10];
snprintf (s, 10, "%d", dim.pointer);
str_append (references, ",.nd=", s);
}
Reduction
Ast * parameters = ast_child (scope, sym_argument_expression_list);
if (parameters)
foreach_item (parameters, 2, item) {
Ast * reductions = ast_find (item, sym_reduction_list);
foreach_item (reductions, 1, reduction) {
Ast * identifier = ast_schema (reduction, sym_reduction,
4, sym_reduction_array,
0, sym_generic_identifier,
0, sym_IDENTIFIER);
if (!strcmp (ast_terminal (identifier)->start, start)) {
char * operator = ast_left_terminal (reduction->child[2])->start;
Ast * array = ast_schema (reduction, sym_reduction,
4, sym_reduction_array,
3, sym_expression);
if (array) {
// fixme: not implemented yet
}
else
str_append (references, ",.reduct=",
!strcmp(operator, "min") ? "'m'" :
!strcmp(operator, "max") ? "'M'" :
!strcmp(operator, "+") ? "'+'" :
"'?'");
}
}
}
Array dimensions
if (dim.dimension) {
if ((*dim.dimension)->sym != sym_VOID) {
str_append (references, ",.data=(int[]){");
for (Ast ** d = dim.dimension; *d; d++)
references = ast_str_append (*d, references);
str_append (references, ",0}");
}
else if (*(dim.dimension + 1) == NULL) { // a single undefined dimension
Ast * initializer = ast_schema (ast_parent (ref, sym_init_declarator), sym_init_declarator,
2, sym_initializer,
1, sym_initializer_list);
if (initializer) {
int n = 0;
foreach_item (initializer, 2, item)
n++;
char s[20];
snprintf (s, 19, "%d", n);
str_append (references, ",.data=(int[]){", s, ",0}");
}
}
}
free (dim.dimension);
str_append (references, "},");
return references;
}
char * ast_external_references (Ast * scope, char * references, Stack * functions)
{
AstRoot * root = ast_get_root (scope);
Stack * stack = root->stack;
stack_push (stack, &scope);
Accelerator a = { scope };
a.nonlocals = stack_new (sizeof (Ast *));
a.attributes = stack_new (sizeof (Ast *));
external_references (scope, stack, &a);
ast_pop_scope (stack, scope);
Ast ** n;
for (int i = 0; (n = stack_indexi (a.attributes, i)) && (!references || !strstr (references, "@error ")); i++)
references = add_reference (*n, references, scope, stack, functions);
for (int i = 0; (n = stack_indexi (a.nonlocals, i)) && (!references || !strstr (references, "@error ")); i++)
references = add_reference (*n, references, scope, stack, functions);
stack_destroy (a.nonlocals);
stack_destroy (a.attributes);
return references;
}