src/ast/macro.h
Macros
The C preprocessor (also known as CPP) macros are familiar to C programmers (and are briefly introduced in the Tutorial). While they are very useful, their shortcomings are also well-known, for example:
- multiline macros are hard to write, read and debug,
- macros only deal with text i.e. they do not enforce the C syntax/grammar (which also makes them hard to write and debug),
- macro statements cannot ‘return’ a value,
- while macros can be used to implement simple iterators, the syntax is cumbersome.
As an illustration, consider the following simple iteration macro
#define iterator(start, end, index, expr) do { \
for (int index = start; index <= end; index++) \
expr \
printf ("do something after the loop\n"); \
} while(0)
int main() {
iterator (0, 10, i, { printf ("%d\n", i); });
}Using Basilisk macros, this would be written
macro iterator (int start, int end, int index) {
for (int index = start; index <= end; index++)
{...}
printf ("do something after the loop\n");
}
int main() {
iterator (0, 10, i)
printf ("%d\n", i);
}The syntax/grammar of the macro definition is almost identical to the
definition of a standard C function and is checked as such by the
compiler. Using the macro also respects the C grammar (i.e. that of
standard C iterators: for, while etc.).
Two new reserved keywords have been introduced:
macrowhich indicates that what follows is a macro definition, and{...}which is expanded to the statement used when the macro is called (i.e. toprintf ("%d\n", i);in this example).
Macro expansion
The expansion of the arguments passed to Basilisk macros is what makes them different from functions. To illustrate this, consider the following example
macro initialize (scalar s, double expr)
{
foreach()
s[] = expr;
}
int main() {
init_grid (16);
scalar a[];
initialize (a, sqrt (x*x + y*y));
}It is clear that if initialize was a function
(i.e. macro initialize... would just be replaced by
void initialize...), this would not compile, because
x and y are not defined when
initialize is called in main. This works
because Basilisk macros are expanded before compilation (like their CPP
counterpart). After macro expansion the code would read
...
int main() {
init_grid (16);
scalar a[];
foreach()
a[] = sqrt (x*x + y*y);
}Note also that in this example the macro does not use the “ellipsis
macro” operator {...}.
Macros returning a value
As mentioned above, standard C macros cannot ‘return’ a value like functions do. Basilisk macros can. There are two types of “return macros” in Basilisk.
Inlined return macros
They are simple macros which behave very much like their standard CPP counterpart. The body of the macro must consist of a single return statement e.g.
macro number sq (number x) {
return x*x;
}The returned expression (x*x here) is expanded directly
where the macro is called (i.e. it is “inlined”). Note that, unlike what
the equivalent C function (not macro) would do, the arguments and the
returned value are not implicitly cast to the specified type
(number here). So the sq() macro will preserve the type of
its arguments i.e. it will work with any numeric type (int,
double, float etc.).
Complex return macros
Complex return macros return a value which is more complex than a simple expression. They cannot be inlined, because they are composed of several statements. Instead they are expanded as additional statements, which are inserted before the calling statement. In contrast with inlined macros, the type of the return value is automatically cast to the type given in the macro definition.
Optional arguments
Since macros share their syntax with that of functions, they can also
define optional
arguments. The default value None is specific to macros
and will be expanded to nothing.
Breaking out of macro iterators
By default trying to break out of a macro iterator (like
the iterator example above) will cause an error. Breaking
must explicitly be set when defining the macro, for example
macro iterator (int start, int end, int index, break = break) {
...
}where breaking uses the standard break statement, or
macro iterator (int start, int end, int index, break = (index = end + 1)) {
...
}where the given expression will be expanded in replacement of the
break statement.
Overloading
Unlike for C functions, one is allowed to define the same macro several times. The latest definition (in order of appearance in the source file) “overloads” the previous ones.
It is sometimes useful to break this rule to specify a “default”
definition which will only apply if no other definition is present. This
can be done by adding the auto “storage class specifier” to
the macro definition.
Inheritance
Macros implement a simple mechanism for “inheritance”. A “recursive” definition of macros is possible, which allows to include the expansion of a previous definition of a macro into the new (overloaded) definition.
Non-casting of arguments
The comment made above regarding the non-type-casting of the return values of inlined macros, also applies to the arguments of any macro. This is different from what happens for standard C functions.
Postmacros
Macro definitions can use an “order” parameter which define their
expansion priority when preprocessing by qcc.
This is used to control which macros are expanded and thus seen or not
by the interpreter (only
macro are expanded) or by computation
kernels (macro and macro1 are
expanded).
This is a low-level functionality which must not be used for “user” macros.
See also
Implementation
typedef struct {
Ast * statement, * arguments, * parameters, * call, * label, * initial, * scope;
Stack * sparameters;
int * returnindex;
bool nolineno, complex_call;
int postmacros;
} MacroReplacement;
static Ast * argument_value (Ast * identifier, Stack * stack, const MacroReplacement * r)
{
Ast * decl = ast_identifier_declaration (r->sparameters, ast_terminal (identifier)->start);
if (!decl)
return NULL;
Ast * value = NULL;
if (r->arguments) {
Ast * parent = ast_parent (decl, sym_parameter_declaration);
Ast * parameters = r->parameters->child[0];
while (parameters && parameters->child[0]->sym == parameters->sym)
parameters = parameters->child[0];
Ast * arguments = r->arguments;
foreach_item_r (arguments, sym_argument_expression_list_item, argument) {
Ast * parameter = ast_child (parameters, sym_parameter_declaration);
parameters = parameters->parent;
assert (parameter);
if (parameter == parent) {
value = argument;
break;
}
}
}
if (!value) {
AstTerminal * t = ast_left_terminal (r->call);
fprintf (stderr, "%s:%d: error: missing '%s' macro parameter\n",
t->file, t->line, ast_terminal (identifier)->start);
exit (1);
}
return value;
}
static Ast * remove_leading_spaces (Ast * n)
{
AstTerminal * t = ast_left_terminal (n);
for (char * s = t->before; s && *s != '\0'; s++)
if (!strchr (" \t\n", *s))
return n;
free (t->before); t->before = NULL;
return n;
}
static void replace_arguments (Ast * n, Stack * stack, void * data)
{
const MacroReplacement * r = data;
Ast * identifier;
if ((identifier = ast_schema (n, sym_postfix_expression,
0, sym_primary_expression,
0, sym_IDENTIFIER))) {
Ast * value = argument_value (identifier, stack, r);
if (value) {
if (value->child[0]->sym == sym_assignment_expression) {
Ast * primary = ast_is_simple_expression (value->child[0]);
if (primary) {
primary = remove_leading_spaces (ast_copy (primary->parent));
ast_set_line (primary, ast_terminal (identifier), true);
Ast * identifier = ast_schema (primary, sym_primary_expression,
0, sym_IDENTIFIER);
if (identifier && !strcmp (ast_terminal (identifier)->start, "None"))
ast_terminal (identifier)->start[0] = '\0';
}
else
primary = NN(n, sym_primary_expression,
NCA(n, "("),
NN(n, sym_expression_error,
NN(n, sym_expression,
remove_leading_spaces (ast_copy (value->child[0])))),
NCA(n, ")"));
ast_replace_child (n, 0, primary);
}
else { // postfix_initializer
assert (value->child[0]->sym == sym_postfix_initializer);
Ast * parent = ast_ancestor (n, 3);
assert (ast_schema (parent, sym_cast_expression,
1, sym_type_name));
parent->sym = sym_postfix_expression;
ast_replace_child (parent, 3, value->child[0]);
int index = ast_child_index (parent);
Ast * grandparent = parent->parent;
ast_set_child (grandparent, index,
NN(grandparent, sym_cast_expression,
NN(grandparent, sym_unary_expression,
parent)));
}
}
else if (r->initial) {
if (!strcmp (ast_terminal (identifier)->start, "S__FILE__")) {
char * filename = NULL;
str_append (filename, "\"", ast_left_terminal (r->initial)->file, "\"");
ast_replace_child (identifier->parent, 0, NN(n, sym_string,
NB(n, sym_STRING_LITERAL, filename)));
free (filename);
}
else if (!strcmp (ast_terminal (identifier)->start, "S_LINENO")) {
int line = r->nolineno ? 0 : ast_left_terminal (r->initial)->line;
char sline[20];
snprintf (sline, 19, "%d", line);
ast_replace_child (identifier->parent, 0, NN(n, sym_constant,
NB(n, sym_I_CONSTANT, sline)));
}
}
}
else if ((identifier = ast_schema (n, sym_direct_declarator,
0, sym_generic_identifier,
0, sym_IDENTIFIER))) {
Ast * value = argument_value (identifier, stack, r);
if (value) {
assert (value->child[0]->sym == sym_assignment_expression); // does not know yet how to deal with postfix_initializer
Ast * replacement = ast_is_identifier_expression (ast_schema (value, sym_argument_expression_list_item,
0, sym_assignment_expression));
if (!replacement) {
AstTerminal * t = ast_left_terminal (value);
fprintf (stderr, "%s:%d: error: macro argument '%s' must be a simple identifier\n",
t->file, t->line, ast_terminal (identifier)->start);
t = ast_terminal (identifier);
fprintf (stderr, "%s:%d: error: because it is used here as a declarator\n",
t->file, t->line);
exit (1);
}
free (ast_terminal (identifier)->start);
ast_terminal (identifier)->start = strdup (ast_terminal (replacement)->start);
}
}
}
static void replace_ellipsis (Ast * n, Stack * stack, void * data)
{
if (ast_schema (ast_child (n, sym_statement), sym_statement,
0, sym_basilisk_statements,
0, sym_ELLIPSIS_MACRO)) {
Ast * child = ast_child (n, sym_statement);
MacroReplacement * r = data;
ast_set_child (n, ast_child_index (child), ast_copy (r->statement));
ast_destroy (child);
}
}
Ast * ast_is_macro_declaration (const Ast * function_declaration)
{
return ast_find (ast_schema (function_declaration, sym_function_declaration,
0, sym_declaration_specifiers),
sym_declaration_specifiers,
0, sym_storage_class_specifier,
0, sym_MACRODEF) ?
ast_schema (function_declaration, sym_function_declaration,
1, sym_declarator,
0, sym_direct_declarator,
0, sym_direct_declarator,
0, sym_generic_identifier,
0, sym_IDENTIFIER) : NULL;
}
static void replace_break (Ast * n, Ast * breaking, Ast * parent)
{
if (ast_schema (n, sym_statement,
0, sym_jump_statement,
0, sym_BREAK)) {
Ast * loop = n->parent;
while (loop != parent &&
loop->sym != sym_forin_declaration_statement &&
loop->sym != sym_forin_statement &&
loop->sym != sym_iteration_statement &&
(loop->sym != sym_selection_statement ||
loop->child[0]->sym != sym_SWITCH) &&
!ast_is_foreach_statement (loop))
loop = loop->parent;
if (loop == parent) {
if (breaking) {
if (breaking->sym == sym_BREAK)
; // do nothing
else {
assert (breaking->sym == sym_assignment_expression);
n->child[0]->sym = sym_expression_statement;
ast_replace_child (n->child[0], 0,
NN(n, sym_expression,
ast_copy (breaking)));
}
}
else {
Ast * breaking = ast_schema (n, sym_statement,
0, sym_jump_statement,
0, sym_BREAK);
Ast * identifier = ast_schema (ast_parent (breaking, sym_macro_statement), sym_macro_statement,
0, sym_MACRO);
fprintf (stderr, "%s:%d: error: cannot break out of macro '%s'\n",
ast_terminal (breaking)->file, ast_terminal (breaking)->line,
ast_terminal (identifier)->start);
exit (1);
}
}
}
else if (n->child)
for (Ast ** c = n->child; *c; c++)
replace_break (*c, breaking, parent);
}
static void replace_return (Ast * n, Stack * stack, void * data)
{
if (n->sym == sym_RETURN) {
Ast * expr = ast_schema (n->parent, sym_jump_statement,
1, sym_expression);
n->sym = sym_GOTO;
free (ast_terminal (n)->start);
ast_terminal (n)->start = strdup ("goto");
MacroReplacement * r = data;
if (!r->label) {
char rindex[25]; snprintf (rindex, 24, "_return_%d", (*r->returnindex)++);
r->label = NN(n, sym_generic_identifier,
NA(n, sym_IDENTIFIER, rindex));
}
Ast * label = ast_copy (r->label);
ast_before (label, " ");
ast_set_line (label, ast_terminal (n), true);
if (expr) // 'return expr;' replaced with 'goto label;'
ast_set_child (n->parent, 1, label);
else // 'return;' replaced with 'goto label;'
ast_new_children (n->parent, n->parent->child[0], label, n->parent->child[1]);
Ast * statement = ast_parent (n, sym_statement);
Ast * parent = statement->parent;
int index = ast_child_index (statement);
Ast * compound;
if (r->call->sym == sym_function_call) {
if (!expr) {
fprintf (stderr, "%s:%d: error: 'void' return value in a macro returning non-void\n",
ast_terminal (n)->file, ast_terminal (n)->line);
exit (1);
}
char * val = strdup (ast_terminal (ast_child(label, sym_IDENTIFIER))->start);
str_append (val, "val");
AstTerminal * open = NCB(parent, "{"), * close = NCB(parent, "}");
Ast * assign = NN(n, sym_statement,
NN(n, sym_expression_statement,
NN(n, sym_expression,
NN(n, sym_assignment_expression,
NN(n, sym_unary_expression,
NN(n, sym_postfix_expression,
NN(n, sym_primary_expression,
NB(n, sym_IDENTIFIER, val)))),
NN(n, sym_assignment_operator,
NCB(n, "=")),
ast_child (expr, sym_assignment_expression))),
NCB(n, ";")));
free (val);
compound = NN(parent, sym_statement,
NN(parent, sym_compound_statement,
open,
NN(parent, sym_block_item_list,
NN(parent, sym_block_item_list,
NN(parent, sym_block_item,
assign)),
NN(parent, sym_block_item,
statement)),
close));
}
else
compound = statement;
ast_set_child (parent, index, compound);
}
}
static
Ast * get_macro_definition (Stack * stack, const Ast * identifier, Ast * scope)
{
Ast * decl = scope ?
ast_identifier_declaration_from_to (stack, ast_terminal (identifier)->start, scope, NULL) :
ast_identifier_declaration (stack, ast_terminal (identifier)->start);
Ast * macro_definition = ast_schema (ast_ancestor (decl, 6), sym_function_definition);
if (!macro_definition) {
fprintf (stderr, "%s:%d: error: undefined macro '%s'\n",
ast_terminal (identifier)->file, ast_terminal (identifier)->line, ast_terminal (identifier)->start);
exit (1);
}
Ast * non_auto_macro = macro_definition;
while (ast_find (ast_schema (non_auto_macro, sym_function_definition,
0, sym_function_declaration),
sym_declaration_specifiers,
0, sym_storage_class_specifier,
0, sym_AUTO)) {
Ast * other = ast_identifier_declaration_from_to (stack, ast_terminal (identifier)->start, decl, NULL);
if (other == decl)
break;
non_auto_macro = ast_schema (ast_ancestor (other, 6), sym_function_definition);
decl = other;
}
if (non_auto_macro)
macro_definition = non_auto_macro;
if (macro_definition == ast_parent (identifier, sym_function_definition)) { // This is a "recursive" macro
Ast * other = ast_identifier_declaration_from_to (stack, ast_terminal (identifier)->start, decl, NULL);
if (other == decl) {
fprintf (stderr, "%s:%d: error: cannot find ancestor of recursive macro '%s'\n",
ast_terminal (decl)->file, ast_terminal (decl)->line, ast_terminal (decl)->start);
exit (1);
}
macro_definition = ast_schema (ast_ancestor (other, 6), sym_function_definition);
}
return macro_definition;
}
static void replace_macros (Ast * n, Stack * stack, void * data)
{
if (n->sym == sym_statement || n->sym == sym_function_call) {
MacroReplacement * r = data;
ast_macro_replacement (n, r->initial, stack, r->nolineno, r->postmacros, true, r->returnindex, r->scope);
}
}
void ast_macro_replacement (Ast * statement, Ast * initial, Stack * stack,
bool nolineno, int postmacros, bool expand_definitions,
int * return_macro_index, Ast * scope)
{
Ast * identifier, * macro_statement = ast_schema (statement, sym_statement,
0, sym_basilisk_statements,
0, sym_macro_statement);
if (macro_statement)
identifier = ast_child (macro_statement, sym_MACRO);
else if ((identifier = ast_schema (statement, sym_function_call,
0, sym_postfix_expression,
0, sym_primary_expression,
0, sym_MACRO)))
macro_statement = statement;
if (!macro_statement)
return;
if (!strcmp (ast_terminal (identifier)->start, "OMP_PARALLEL"))
return;
if (!expand_definitions) {
Ast * definition = ast_parent (statement, sym_function_definition);
if (definition && ast_is_macro_declaration (definition->child[0]))
return;
}
if (!strcmp (ast_terminal (identifier)->start, "foreach_block") &&
(inforeach (statement) || point_declaration (stack)))
str_append (ast_terminal (identifier)->start, "_inner");
Ast * macro_definition = get_macro_definition (stack, identifier, scope);
if (postmacros >= 0) {
Ast * macrodef = ast_find (ast_schema (macro_definition, sym_function_definition,
0, sym_function_declaration),
sym_declaration_specifiers,
0, sym_storage_class_specifier,
0, sym_MACRODEF);
assert (macrodef);
const char * suffix = ast_terminal (macrodef)->start + 5;
if (atoi (suffix) > postmacros)
return;
}
optional_arguments (macro_statement, stack);
MacroReplacement r = {
.statement = ast_child (macro_statement, sym_statement),
.arguments = ast_child (macro_statement, sym_argument_expression_list),
.parameters = ast_schema (macro_definition, sym_function_definition,
0, sym_function_declaration,
1, sym_declarator,
0, sym_direct_declarator,
2, sym_parameter_type_list),
.call = macro_statement,
.nolineno = nolineno,
.complex_call = !ast_schema (ast_find (macro_definition, sym_compound_statement),
sym_compound_statement,
1, sym_block_item_list,
0, sym_block_item,
0, sym_statement,
0, sym_jump_statement,
0, sym_RETURN),
.returnindex = return_macro_index,
.postmacros = postmacros,
.scope = scope
};
if (r.parameters) {
r.sparameters = stack_new (sizeof (Ast *));
ast_push_declaration (r.sparameters, ast_child (r.parameters, sym_parameter_list));
}
if (initial) {
Ast * definition = ast_parent (initial, sym_function_definition);
if (definition && ast_is_macro_declaration (definition->child[0]))
r.initial = NULL;
else
r.initial = initial;
}
int na = 0;
if (r.arguments)
foreach_item (r.arguments, 2, argument) na++;
int np = 0;
if (r.parameters) {
Ast * list = r.parameters->child[0];
foreach_item (list, 2, parameter)
if (!ast_schema (parameter, sym_parameter_declaration,
0, sym_BREAK))
np++;
}
if (na != np) {
AstTerminal * t = ast_terminal (identifier);
fprintf (stderr, "%s:%d: error: too %s arguments for macro '%s'\n",
t->file, t->line, na > np ? "many" : "few", ast_terminal (identifier)->start);
t = ast_left_terminal (macro_definition);
fprintf (stderr, "%s:%d: error: macro '%s' is defined here\n", t->file, t->line, ast_terminal (identifier)->start);
#if 0
ast_print_tree (macro_definition->child[0], stderr, 0, 0, -1);
ast_print (r.call, stderr, 0);
#endif
exit (1);
}
#if 0
fprintf (stderr, "***** replacing macro: \n");
ast_print (macro_statement, stderr, 0);
fprintf (stderr, "\n===== with: \n");
ast_print (ast_child (macro_definition, sym_function_declaration), stderr, 0);
ast_print (ast_child (macro_definition, sym_compound_statement), stderr, 0);
fputc ('\n', stderr);
#endifReplace ‘break’ with its macro definition (if it exists).
if (r.statement) {
Ast * breaking = NULL;
if (r.parameters && !(breaking = ast_find (r.parameters, sym_parameter_declaration,
2, sym_initializer,
0, sym_assignment_expression)))
breaking = ast_find (r.parameters, sym_parameter_declaration,
2, sym_BREAK);
Ast * copy = breaking;
if (breaking) {
if (r.parameters) {
copy = ast_copy (breaking);
stack_push (stack, ©);
ast_traverse (copy, stack, replace_arguments, &r);
ast_pop_scope (stack, copy);
}
}
replace_break (r.statement, copy, r.statement);
if (copy != breaking)
ast_destroy (copy);
}
Ast * copy = ast_copy (ast_find (macro_definition, sym_compound_statement));
stack_push (stack, ©);
if (r.parameters) {
ast_traverse (copy, stack, replace_arguments, &r);
stack_destroy (r.sparameters);
}
if (r.complex_call)
ast_traverse (copy, stack, replace_return, &r);
if (r.statement)
ast_traverse (copy, stack, replace_ellipsis, &r);
ast_pop_scope (stack, copy);
str_prepend (ast_left_terminal (copy)->before, ast_left_terminal (macro_statement)->before);
stack_push (stack, ©);
ast_traverse (copy, stack, replace_macros, &r);
ast_pop_scope (stack, copy);Return label
if (r.label) {
ast_set_line (r.label, ast_right_terminal (copy), true);
ast_block_list_append (ast_schema (copy, sym_compound_statement,
1, sym_block_item_list), sym_block_item,
NN(copy, sym_statement,
NN(copy, sym_labeled_statement,
r.label,
NCA(copy, ":"),
NN(copy, sym_statement,
NN(copy, sym_expression_statement,
NCA(copy, ";"))))));
}Statement
if (statement->sym == sym_statement) {
if (statement->parent->sym == sym_block_item) {
Ast * list = ast_schema (copy, sym_compound_statement,
1, sym_block_item_list);
Ast * parent = ast_ancestor (statement, 2);
assert (parent->sym == sym_block_item_list);
foreach_item (list, 1, item) {
if (ast_child_index (item))
ast_block_list_append (parent, sym_block_item, item->child[0]);
else {
ast_before (item, ast_left_terminal (statement)->before);
ast_set_child (parent, parent->child[1] ? 1 : 0, item);
}
}
ast_destroy (copy);
}
else
ast_set_child (statement, 0, copy);
ast_destroy (macro_statement);
}Function call
else {
assert (statement->sym == sym_function_call);
Ast * jump = ast_schema (copy, sym_compound_statement,
1, sym_block_item_list,
0, sym_block_item,
0, sym_statement,
0, sym_jump_statement);
if (ast_schema (jump, sym_jump_statement,
0, sym_RETURN)) {
assert (!r.complex_call);This is a simple return macro i.e. ‘macro int func(…){ return …; }’.
Ast * expr = ast_schema (jump, sym_jump_statement,
1, sym_expression);
if (!expr) {
AstTerminal * t = ast_terminal (identifier);
fprintf (stderr, "%s:%d: error: using value of macro '%s' returning void\n",
t->file, t->line, ast_terminal (identifier)->start);
t = ast_terminal (ast_schema (jump, sym_jump_statement,
0, sym_RETURN));
fprintf (stderr, "%s:%d: error: return value is defined here\n", t->file, t->line);
exit (1);
}
AstTerminal * o = NCB(statement, "("), * c = NCB(statement, ")");
statement->sym = sym_primary_expression;
for (Ast ** c = statement->child; *c; c++)
ast_destroy (*c);
ast_set_line (expr, o, true);
ast_new_children (statement, o,
NN(expr, sym_expression_error,
expr), c);
ast_destroy (copy);
}
else {This is a complex return macro.
assert (r.complex_call);
assert (r.label);
Ast * returntype = ast_schema (macro_definition, sym_function_definition,
0, sym_function_declaration,
0, sym_declaration_specifiers,
1, sym_declaration_specifiers);
if (!returntype) {
fprintf (stderr, "%s:%d: error: the type of a macro returning a value must be defined\n",
ast_left_terminal (macro_definition)->file,
ast_left_terminal (macro_definition)->line);
exit (1);
}
char * label = strdup (ast_terminal (ast_schema (r.label, sym_generic_identifier,
0, sym_IDENTIFIER))->start);
str_append (label, "val");
Ast * declaration = NN(copy, sym_declaration,
ast_copy (returntype),
NN(copy, sym_init_declarator_list,
NN(copy, sym_init_declarator,
NN(copy, sym_declarator,
NN(copy, sym_direct_declarator,
NN(copy, sym_generic_identifier,
NB(copy, sym_IDENTIFIER, label)))))),
NCB(copy, ";"));
AstTerminal * val = NB(statement, sym_IDENTIFIER, label);
for (Ast ** c = statement->child; *c; c++)
ast_destroy (*c), *c = NULL;
statement->sym = sym_primary_expression;
ast_set_child (statement, 0, (Ast *) val);
Ast * parent = statement->parent;
while (parent->sym != sym_statement && parent->sym != sym_declaration)
parent = parent->parent;
Ast * gp = parent->parent;
if (gp->sym == sym_block_item) {
Ast * s = NN(gp, sym_statement, copy);
ast_block_list_insert_before2 (gp, s);
assert (s->parent->sym == sym_block_item);
ast_block_list_insert_before2 (s->parent, declaration);
}
else {
int index = ast_child_index (parent);
AstTerminal * open = NCB(gp, "{"), * close = NCA(parent, "}");
Ast * compound = NN(gp, sym_statement,
NN(gp, sym_compound_statement,
open,
NN(gp, sym_block_item_list,
NN(gp, sym_block_item_list,
NN(gp, sym_block_item_list,
NN(gp, sym_block_item,
declaration)),
NN(gp, sym_block_item,
NN(gp, sym_statement,
copy))),
NN(gp, sym_block_item,
parent)),
close));
ast_set_child (gp, index, compound);
}
free (label);
}
}
}