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]);
          }	  
        }
      }
    }