src/ast/optional.h
Optional arguments for function calls
Transforms a list of arguments (‘argument_expression_list’) into a list of initializers (‘initializer_list’).
Ast * ast_initializer_list (Ast * list)
{
Ast * start = list;
while (list->sym == sym_argument_expression_list) {
list->sym = sym_initializer_list;
Ast * initializer = list->child[1] ? list->child[2] : list->child[0];
if (initializer) {
initializer->sym = sym_initializer;
Ast * equals = ast_schema (initializer, sym_initializer,
0, sym_assignment_expression,
1, sym_assignment_operator,
0, token_symbol('='));
if (equals) {
Ast * name = ast_schema (initializer, sym_initializer,
0, sym_assignment_expression,
0, sym_unary_expression,
0, sym_postfix_expression,
0, sym_primary_expression,
0, sym_IDENTIFIER);
if (!name)
name = ast_schema (initializer, sym_initializer,
0, sym_assignment_expression,
0, sym_TYPEDEF_NAME);
if (name) {
Ast * designator = ast_new (initializer, sym_designator);
Ast * identifier = ast_new (initializer, sym_generic_identifier);
Ast * dot = ast_terminal_new_char (initializer, ".");
ast_new_children (designator, dot, identifier);
ast_new_children (identifier, name);
AstTerminal * left = ast_left_terminal (identifier);
ast_terminal (dot)->line = left->line;
ast_terminal (dot)->before = left->before; left->before = NULL;
Ast * designator_list =
ast_new_children (ast_new (initializer, sym_designator_list),
designator);
Ast * designation =
ast_new_children (ast_new (initializer, sym_designation),
designator_list, equals);
if (initializer->child[0]->child[2]->sym == sym_assignment_expression)
ast_set_child (initializer, 0, initializer->child[0]->child[2]);
else {
assert (initializer->child[0]->child[2]->sym ==
sym_postfix_initializer);
initializer = initializer->child[0]->child[2];
initializer->sym = sym_initializer;
}
if (list->child[1])
ast_new_children (list,
list->child[0], list->child[1], designation,
initializer);
else
ast_new_children (list, designation, initializer);
}
}
else if (ast_schema (initializer, sym_initializer,
0, sym_postfix_initializer)) {
initializer = initializer->child[0];
initializer->sym = sym_initializer;
ast_set_child (list, list->child[1] ? 2 : 0, initializer);
}
}
list = list->child[0];
}
return start;
}
static Ast * obsolete_function_declaration (const Ast * type)
{
Ast * parameters = ast_find (type, sym_parameter_list);
Obsolete optional arguments syntax using ‘struct …’ parameters.
Ast * struct_name;
if (parameters && !parameters->child[1] &&
(struct_name = ast_get_struct_name (ast_schema (parameters, sym_parameter_list,
0, sym_parameter_declaration,
0, sym_declaration_specifiers))) &&
ast_schema (parameters, sym_parameter_list,
0, sym_parameter_declaration,
1, sym_declarator,
0, sym_direct_declarator,
0, sym_generic_identifier,
0, sym_IDENTIFIER))
return struct_name;
else
return NULL;
}
static Ast * abstract_declarator_from_declarator (Ast * n)
{
switch (n->sym) {
case sym_generic_identifier: {
ast_destroy (n);
return NULL;
}
case sym_declarator: n->sym = sym_abstract_declarator; break;
case sym_direct_declarator: n->sym = sym_direct_abstract_declarator; break;
}
if (!n->child)
return n;
for (Ast ** c = n->child; *c; c++)
if (!abstract_declarator_from_declarator (*c)) {
for (Ast ** d = c; *d; d++)
*d = *(d + 1);
c--;
}
if (!*n->child) {
ast_destroy (n);
return NULL;
}
return n;
}
static void optional_arguments (Ast * call, Stack * stack)
{
AstTerminal * t = ast_terminal (call->sym == sym_function_call ? ast_function_call_identifier (call) :
ast_schema (call, sym_macro_statement, 0, sym_MACRO));
Ast * type = ast_identifier_declaration (stack, t->start);
if (type) {
AstTerminal * tname = t;
while (type->sym != sym_declarator)
type = type->parent;
if (!ast_schema (type, sym_declarator,
0, sym_pointer)) { // exclude function pointers
while (type->sym != sym_declaration &&
type->sym != sym_function_definition)
type = type->parent;
Ast * parameters = ast_find (type, sym_parameter_list);
Obsolete optional arguments syntax using ‘struct …’ parameters.
Ast * struct_name = obsolete_function_declaration (type);
if (struct_name) {
Ast * arguments = ast_find (call, sym_argument_expression_list);
if (!arguments) {
Ast * expr = ast_parse_expression ("func((struct Name){0});",
ast_get_root (call));
Ast * list = ast_find (expr, sym_argument_expression_list);
AstTerminal * t = ast_terminal (ast_find (list, sym_IDENTIFIER));
free (t->start);
t->start = strdup (ast_terminal (struct_name)->start);
ast_set_line (list, ast_terminal (call->child[1]), false);
ast_new_children (call, call->child[0], call->child[1],
ast_placeholder,
call->child[2]);
ast_replace_child (call, 2, list);
ast_destroy (expr);
}
else {
Ast * struct_arg = arguments->child[1] ? NULL :
ast_is_identifier_expression (arguments->child[0]->child[0]);
if (struct_arg) {
Ast * type =
ast_identifier_declaration (stack,
ast_terminal (struct_arg)->start);
while (type &&
type->sym != sym_declaration &&
type->sym != sym_parameter_declaration)
type = type->parent;
Ast * struct_namep =
ast_get_struct_name (ast_child (type,
sym_declaration_specifiers));
if (!struct_namep ||
strcmp (ast_terminal (struct_namep)->start,
ast_terminal (struct_name)->start))
struct_arg = NULL;
}
if (!struct_arg) {
Ast * expr = ast_parse_expression ("func((struct Name){a});",
ast_get_root (call));
Ast * list = ast_find (expr, sym_argument_expression_list);
AstTerminal * t = ast_terminal (ast_find (list, sym_IDENTIFIER));
free (t->start);
t->start = strdup (ast_terminal (struct_name)->start);
Ast * initializer_list = ast_initializer_list (arguments);
ast_replace (list, "a", initializer_list);
ast_replace_child (call, 2, list);
if (initializer_list->child[1] &&
initializer_list->child[1]->sym == token_symbol (',') &&
!initializer_list->child[2]) {
Ast * postfix = initializer_list->parent;
assert (postfix->sym == sym_postfix_initializer &&
postfix->child[2]->sym == token_symbol ('}'));
ast_new_children (postfix,
postfix->child[0],
initializer_list->child[0],
initializer_list->child[1],
postfix->child[2]);
}
ast_destroy (expr);
}
}
}
Check for optional or named function call arguments.
else if (parameters &&
(ast_find (parameters, sym_parameter_declaration,
3, sym_initializer) ||
ast_find (call, sym_argument_expression_list_item,
0, sym_assignment_expression,
1, sym_assignment_operator))) {
parameters = ast_copy (parameters); // fixme: memory is leaking from here
Ast * breaking = ast_find (parameters, sym_parameter_declaration,
0, sym_BREAK);
if (breaking) {
if (ast_ancestor (breaking, 2) != parameters) {
fprintf (stderr, "%s:%d: error: 'break' must be the last parameter\n",
ast_terminal (breaking)->file, ast_terminal (breaking)->line);
exit (1);
}
if (ast_child_index (breaking->parent) == 0) {
ast_destroy (parameters);
parameters = NULL;
}
else {
ast_destroy (parameters->child[1]);
ast_destroy (parameters->child[2]);
parameters->child[1] = parameters->child[2] = NULL;
parameters = parameters->child[0];
}
}
Ast * parameters1 = parameters;
while (parameters && parameters->child[0]->sym == parameters->sym)
parameters = parameters->child[0];
Ast * arguments = ast_child (call, sym_argument_expression_list);
if (arguments) {
foreach_item_r (arguments, sym_argument_expression_list_item, argument) {
Ast * identifier = ast_schema (argument, sym_argument_expression_list_item,
0, sym_assignment_expression,
1, sym_assignment_operator) ?
ast_schema (argument, sym_argument_expression_list_item,
0, sym_assignment_expression,
0, sym_unary_expression,
0, sym_postfix_expression,
0, sym_primary_expression,
0, sym_IDENTIFIER) : NULL;
Ast * parameter;
if (identifier) {
parameter = NULL;
foreach_item (parameters1, 2, i) {
Ast * id = ast_find (i, sym_direct_declarator,
0, sym_generic_identifier,
0, sym_IDENTIFIER);
if (!strcmp (ast_terminal (identifier)->start, ast_terminal (id)->start)) {
parameter = i;
break;
}
}
if (!parameter) {
AstTerminal * t = ast_terminal (identifier);
fprintf (stderr, "%s:%d: error: unknown %s parameter '%s'\n",
t->file, t->line,
call->sym == sym_macro_statement ? "macro" : "function",
ast_terminal (identifier)->start);
t = ast_left_terminal (type);
fprintf (stderr, "%s:%d: error: %s '%s' is defined here\n",
t->file, t->line,
call->sym == sym_macro_statement ? "macro" : "function",
tname->start);
exit (1);
}
argument = ast_schema (argument, sym_argument_expression_list_item,
0, sym_assignment_expression)->child[2];
}
else {
parameter = ast_child (parameters, sym_parameter_declaration);
parameters = parameters->parent;
argument = argument->child[0];
}
if (!parameter) {
fprintf (stderr, "%s:%d: error: too many arguments when calling '%s'\n",
t->file, t->line,
t->start);
AstTerminal * tt = ast_left_terminal (type);
fprintf (stderr, "%s:%d: error: %s '%s' is defined here\n",
tt->file, tt->line,
call->sym == sym_macro_statement ? "macro" : "function",
tname->start);
exit (1);
}
if (ast_schema (parameter, sym_parameter_declaration,
3, sym_initializer))
ast_set_child (parameter->child[3], 0, argument);
else if (ast_schema (parameter, sym_parameter_declaration,
1, sym_declarator))
ast_new_children (parameter,
parameter->child[0],
parameter->child[1],
NCA(call, "="),
NN(call, sym_initializer,
argument));
else
assert (false); // not implemented
Ast * comma = ast_schema (parameter->parent, sym_parameter_list,
1, token_symbol (','));
if (comma) {
AstTerminal * t = ast_terminal (comma), * ta = ast_left_terminal (argument);
t->file = ta->file, t->line = ta->line;
}
}
}
foreach_item (parameters1, 2, parameter) {
Ast * initializer = ast_schema (parameter, sym_parameter_declaration,
3, sym_initializer);
if (!initializer) {
Ast * id = ast_find (parameter, sym_direct_declarator,
0, sym_generic_identifier,
0, sym_IDENTIFIER);
AstTerminal * t = ast_left_terminal (call);
fprintf (stderr, "%s:%d: error: missing compulsory parameter '%s' in %s call\n",
t->file, t->line, ast_terminal (id)->start,
call->sym == sym_macro_statement ? "macro" : "function");
exit (1);
}
Ast * assign = ast_schema (initializer, sym_initializer,
0, sym_assignment_expression);
if (assign)
ast_new_children (parameter, assign); // fixme: possible memory leak here
else if (ast_schema (initializer, sym_initializer,
0, sym_reduction_list))
ast_new_children (parameter, ast_schema (initializer, sym_initializer,
0, sym_reduction_list)); // fixme: possible memory leak here
else {
if (ast_schema (initializer, sym_initializer,
0, sym_postfix_initializer))
initializer = initializer->child[0];
else
assert (ast_schema (initializer, sym_initializer,
1, sym_initializer_list));
initializer->sym = sym_postfix_initializer;
Ast * type_specifier = ast_find (parameter, sym_declaration_specifiers,
0, sym_type_specifier);
Ast * declarator = ast_schema (parameter, sym_parameter_declaration,
1, sym_declarator);
Ast * abstract = abstract_declarator_from_declarator (declarator);
assert (type_specifier);
AstTerminal * ob = NCA(parameter, "("), * cb = NCA(parameter, ")");
Ast * type_name = abstract ?
NN(call, sym_type_name,
NN(call, sym_specifier_qualifier_list,
type_specifier),
abstract) :
NN(call, sym_type_name,
NN(call, sym_specifier_qualifier_list,
type_specifier));
ast_new_children (parameter, ast_attach
(ast_new_unary_expression (parameter),
NN(call, sym_postfix_expression,
ob, type_name, cb,
initializer)));
}
parameter->sym = sym_argument_expression_list_item;
parameter->parent->sym = sym_argument_expression_list;
}
Here we replace the arguments of the called function or macro with the completed arguments.
if (call->child[2]->sym == sym_argument_expression_list)
ast_set_child (call, 2, parameters1);
else if (call->sym == sym_macro_statement)
ast_new_children (call, call->child[0], call->child[1], parameters1, call->child[2], call->child[3]);
else // function call
ast_new_children (call, call->child[0], call->child[1], parameters1, call->child[2]);
}
}
}
}