src/ast/translate.c

    The Basilisk C to C99 translator

    Uses the AST library to transform the AST obtained when parsing code with the Basilisk C grammar into an AST respecting the C99 grammar (with added macros).

    Utility functions

    #include <stdlib.h>
    #include <assert.h>
    #include <string.h>
    #include <math.h>
    #include "ast.h"
    #include "symbols.h"

    By default grammar checks are turned off.

    #if 0
    # define CHECK(x, recursive) ast_check_grammar(x, recursive)
    #else
    # define CHECK(x, recursive) ((void) x)
    #endif
    
    Ast * ast_is_typedef (const Ast * identifier)
    {
      const Ast * declaration = identifier;
      while (declaration && declaration->sym != sym_declaration)
        declaration = declaration->parent;
      if (declaration)
        return ast_schema (declaration, sym_declaration,
    		       0, sym_declaration_specifiers,
    		       0, sym_storage_class_specifier,
    		       0, sym_TYPEDEF);
      return NULL;
    }
    
    Ast * ast_find_function (Ast * n, const char * name)
    {
      Ast * found = NULL;
      if (n->sym == sym_function_definition) {
        Ast * identifier = ast_find (n, sym_direct_declarator,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER);
        if (!strcmp (ast_terminal(identifier)->start, name))
          found = n;
      }
      if (n->child)
        for (Ast ** c = n->child; *c && !found; c++)
          found = ast_find_function (*c, name);
      return found;
    }
    
    Ast * ast_function_identifier (const Ast * function_definition)
    {
      return ast_schema (function_definition, sym_function_definition,
    		     0, sym_function_declaration,
    		     1, sym_declarator,
    		     0, sym_direct_declarator,
    		     0, sym_direct_declarator,
    		     0, sym_generic_identifier,
    		     0, sym_IDENTIFIER);
    }
    
    Ast * ast_function_call_identifier (const Ast * n)
    {
      return ast_schema (n, sym_function_call,
    		     0, sym_postfix_expression,
    		     0, sym_primary_expression,
    		     0, sym_IDENTIFIER);
    }

    Appends (block) list list1 to (block) list list.

    Ast * ast_list_append_list (Ast * list, Ast * list1)
    {
      assert (list->sym == list1->sym);
      Ast * oldparent = list->parent;
      int index = ast_child_index (list);
      Ast * parent = list1;
      while (parent->child[1])
        parent = parent->child[0];
      Ast * item = parent->child[0];
      ast_new_children (parent, list, item);
      ast_set_child (oldparent, index, list1);
      return list1;
    }

    Appends item to (block) list. The list item symbol is item_sym.

    Ast * ast_block_list_append (Ast * list, int item_sym, Ast * item)
    {
      ast_set_line (item, ast_right_terminal (list));
      Ast * parent = list->parent;
      int index = ast_child_index (list);
      Ast * l = ast_new_children (ast_new (parent, list->sym),
    			      list, 
    			      ast_attach (ast_new (list, item_sym), item));
      ast_set_child (parent, index, l);
      return l;
    }

    Appends item to (comma-separated) list. The list item symbol is item_sym.

    Ast * ast_list_append (Ast * list, int item_sym, Ast * item)
    {
      ast_set_line (item, ast_right_terminal (list));
      Ast * parent = list->parent;
      int index = ast_child_index (list);
      Ast * l;
      if (item->sym == item_sym)
        l = ast_new_children (ast_new (parent, list->sym),
    			  list, 
    			  ast_terminal_new_char (item, ","),
    			  item);
      else {
        l =  ast_new_children (ast_new (parent, list->sym),
    			   list, 
    			   ast_terminal_new_char (item, ","),
    			   ast_new (item, item_sym));
        ast_attach (l->child[2], item);
      }
      ast_set_child (parent, index, l);
      return l;
    }

    Prepends item to list. The list item symbol is item_sym.

    Ast * ast_block_list_prepend (Ast * list, int item_sym, Ast * item)
    {
      Ast * r = list;
      while (r->child[0]->sym != item_sym)
        r = r->child[0];
      Ast * l = ast_block_list_append (r, item_sym, item), * tmp = r->child[0];
      ast_set_child (r, 0, l->child[1]);
      ast_set_child (l, 1, tmp);
      return r != list ? list : l;
    }

    Prepends item to (comma-separated) list. The list item symbol is item_sym.

    Ast * ast_list_prepend (Ast * list, int item_sym, Ast * item)
    {
      Ast * r = list;
      while (r->child[0]->sym != item_sym)
        r = r->child[0];
      Ast * l = ast_list_append (r, item_sym, item), * tmp = r->child[0];
      ast_set_child (r, 0, l->child[2]);
      ast_set_child (l, 2, tmp);
      return r != list ? list : l;
    }

    Removes item from the (comma-separated) list and returns the new list or NULL if the list contains only item.

    Ast * ast_list_remove (Ast * list, Ast * item)
    {
      Ast * grand_parent = item->parent->parent;
      if (ast_child_index (item) == 0) {
        if (grand_parent->sym == list->sym) {
          ast_replace_child (grand_parent, 0, grand_parent->child[2]);
          ast_destroy (grand_parent->child[1]);
          grand_parent->child[1] = NULL;
        }
        else
          return NULL;
      }
      else {
        Ast * parent = item->parent;
        list = parent->child[0];
        ast_replace_child (grand_parent, ast_child_index (parent), list);
      }
      return list;
    }

    Removes item from the (block) list and returns the new list or NULL if the list contains only item.

    Ast * ast_block_list_remove (Ast * list, Ast * item)
    {
      Ast * grand_parent = item->parent->parent;
      if (ast_child_index (item) == 0) {
        if (grand_parent->sym == list->sym) {
          ast_replace_child (grand_parent, 0, grand_parent->child[1]);
          grand_parent->child[1] = NULL;
        }
        else
          return NULL;
      }
      else {
        Ast * parent = item->parent;
        list = parent->child[0];
        ast_replace_child (grand_parent, ast_child_index (parent), list);
      }
      return list;
    }

    Transforms a list of expressions into a list of arguments.

    void ast_argument_list (Ast * expression)
    {
      while (expression->sym == sym_expression) {
        int child = expression->child[1] ? 2 : 0;
        expression->sym = sym_argument_expression_list;
        Ast * item = ast_new (expression, sym_argument_expression_list_item);
        ast_new_children (item, expression->child[child]);
        ast_set_child (expression, child, item);
        expression = expression->child[0];
      }
    }

    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;
    }
    
    Ast * ast_new_cast_expression (Ast * parent)
    {
      return ast_new (parent,
    		  sym_assignment_expression,
    		  sym_conditional_expression,
    		  sym_logical_or_expression,
    		  sym_logical_and_expression,
    		  sym_inclusive_or_expression,
    		  sym_exclusive_or_expression,
    		  sym_and_expression,
    		  sym_equality_expression,
    		  sym_relational_expression,
    		  sym_shift_expression,
    		  sym_additive_expression,
    		  sym_multiplicative_expression,
    		  sym_cast_expression);
    }
    
    Ast * ast_new_unary_expression (Ast * parent)
    {
      return ast_attach (ast_new_cast_expression (parent),
    		     ast_new (parent, sym_unary_expression));
    }
    
    Ast * ast_is_unary_expression (const Ast * n)
    {
      if (!n)
        return NULL;
      int sym[] = {
        sym_assignment_expression,
        sym_conditional_expression,
        sym_logical_or_expression,
        sym_logical_and_expression,
        sym_inclusive_or_expression,
        sym_exclusive_or_expression,
        sym_and_expression,
        sym_equality_expression,
        sym_relational_expression,
        sym_shift_expression,
        sym_additive_expression,
        sym_multiplicative_expression,
        sym_cast_expression,
        sym_unary_expression,
        -1
      }, * i;
      for (i = sym; *i >= 0 && *i != n->sym; i++);
      for (; n != ast_placeholder && *i == n->sym && n->child; i++, n = n->child[0])
        if (n->sym == sym_unary_expression)
          return (Ast *) n;
      return NULL;
    }
    
    Ast * ast_new_assignment_function_call (Ast * parent, const char * func)
    {
      return ast_attach (ast_new_unary_expression (parent),
    		     NN(parent, sym_postfix_expression,
    			NN(parent, sym_function_call,
    			   NN(parent, sym_postfix_expression,
    			      NN(parent, sym_primary_expression,
    				 NA(parent, sym_IDENTIFIER, func))),
    			   NCA(parent, "("),
    			   NCA(parent, ")"))));
    }
    
    Ast * ast_new_function_call (Ast * parent, const char * func)
    {
      return NN(parent, sym_statement,
    	    NN(parent, sym_expression_statement,
    	       NN(parent, sym_expression,
    		  ast_new_assignment_function_call (parent, func)),
    	       NCA(parent, ";")));
    }
    
    Ast * ast_is_identifier_expression (const Ast * n)
    {
      n = ast_is_unary_expression (n);
      if (n)
        n = ast_schema (n, sym_unary_expression,
    		    0, sym_postfix_expression,
    		    0, sym_primary_expression,
    		    0, sym_IDENTIFIER);
      return (Ast *) n;
    }
    
    Ast * ast_is_simple_expression (const Ast * n)
    {
      n = ast_schema (ast_is_unary_expression (n), sym_unary_expression,
    		  0, sym_postfix_expression,
    		  0, sym_primary_expression);
      if (n) {
        n = n->child[0];
        if (n->sym == sym_IDENTIFIER ||
    	n->sym == sym_constant ||
    	n->sym == sym_string)
          return (Ast *) n;
      }
      return NULL;
    }
    
    Ast * ast_is_iteration_statement (const Ast * n)
    {
      if (n && (n->sym == sym_iteration_statement ||
    	    n->sym == sym_foreach_statement ||
    	    n->sym == sym_foreach_inner_statement ||
    	    n->sym == sym_forin_declaration_statement ||
    	    n->sym == sym_forin_statement))
        return (Ast *) n;
      return NULL;
    }
    
    Ast * ast_new_constant (Ast * parent, int symbol, const char * value)
    {
      return ast_attach (ast_new_unary_expression (parent),
    		     ast_new (parent,
    			      sym_postfix_expression,
    			      sym_primary_expression,
    			      sym_constant),
    		     ast_terminal_new (parent, symbol, value));
    }
    
    Ast * ast_new_empty_scalar (Ast * parent)
    {
      return NN(parent, sym_initializer,
    	    NCA(parent, "{"),
    	    NN(parent, sym_initializer_list,
    	       NN(parent, sym_initializer,
    		  ast_attach(ast_new_cast_expression (parent),
    			     NN(parent, sym_unary_expression,
    				NN(parent, sym_unary_operator,
    				   NCA(parent, "-")),
    				NN(parent, sym_cast_expression,
    				   NN(parent, sym_unary_expression,
    				      NN(parent, sym_postfix_expression,
    					 NN(parent, sym_primary_expression,
    					    NN(parent, sym_constant,
    					       NA(parent, sym_I_CONSTANT, "1")))))))))),
    	    NCA(parent, "}"));
    }
    
    Ast * ast_new_empty_vector (Ast * parent, int dimension)
    {
      Ast * list = NN(parent, sym_initializer_list,
    		  ast_new_empty_scalar (parent));
      Ast * ret = NN(parent, sym_initializer,
    		 NCA(parent, "{"), list, NCA(parent, "}"));
      for (int i = 1; i < dimension; i++)
        ast_list_append (list, sym_initializer, ast_new_empty_scalar (parent));
      return ret;
    }
    
    Ast * ast_new_empty_tensor (Ast * parent, int dimension)
    {
      Ast * list = NN(parent, sym_initializer_list,
    		  ast_new_empty_vector (parent, dimension));
      Ast * ret = NN(parent, sym_initializer,
    		 NCA(parent, "{"), list, NCA(parent, "}"));
      for (int i = 1; i < dimension; i++)
        ast_list_append (list, sym_initializer, ast_new_empty_vector (parent, dimension));
      return ret;
    }
    
    Ast * ast_new_identifier (Ast * parent, const char * name)
    {
      return ast_attach (ast_new (parent,
    			      sym_postfix_expression,
    			      sym_primary_expression),
    		     ast_terminal_new (parent, sym_IDENTIFIER, name)); 
    }
    
    Ast * ast_new_member_identifier (Ast * parent, const char * name)
    {
      return ast_attach (ast_new (parent,
    			      sym_member_identifier,
    			      sym_generic_identifier),
    		     ast_terminal_new (parent, sym_IDENTIFIER, name));
    }
    
    Ast * ast_get_struct_name (Ast * declaration_specifiers)
    {
      return ast_schema (declaration_specifiers, sym_declaration_specifiers,
    		     0, sym_type_specifier,
    		     0, sym_types,
    		     0, sym_struct_or_union_specifier,
    		     1, sym_generic_identifier,
    		     0, sym_IDENTIFIER);
    }
    
    static Ast * find_struct_member (Ast * n, const char * member)
    {
      if (!n)
        return NULL;
      Ast * identifier = ast_schema (n, sym_struct_declarator,
    				 0, sym_declarator,
    				 0, sym_direct_declarator,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER);
      if (identifier && !strcmp (ast_terminal (identifier)->start, member))
        return identifier;
      if (n->child)
        for (Ast ** c = n->child; *c; c++) {
          Ast * found = find_struct_member (*c, member);
          if (found)
    	return found;
        }
      return NULL;
    }
    
    Ast * ast_declaration_from_type (const Ast * type)
    {
      if (!type)
        return NULL;
      while (type->sym != sym_declaration &&
    	 type->sym != sym_function_declaration &&
    	 type->sym != sym_parameter_declaration &&
    	 type->sym != sym_struct_declaration &&
    	 type->sym != sym_forin_declaration_statement)
        type = type->parent;
      assert (type);
      return (Ast *) type;
    }
    
    Ast * ast_expression_type (Ast * expr, Stack * stack, bool higher_dimension)
    {
      if (!expr || expr == ast_placeholder)
        return NULL;
      switch (expr->sym) {
    
      case sym_IDENTIFIER:
        if (ast_ancestor (expr, 2)->sym == sym_member_identifier)
          return ast_expression_type (ast_ancestor (expr, 3), stack,
    				  higher_dimension);
        else
          return ast_identifier_declaration (stack, ast_terminal (expr)->start);
    
      case sym_unary_expression:
      case sym_primary_expression:
      case sym_argument_expression_list_item:
        return ast_expression_type (expr->child[0], stack, higher_dimension);
        
      case sym_initializer:
      case sym_assignment_expression:
        while (expr->child && expr->sym != sym_postfix_expression)
          expr = expr->child[0];
        return expr->sym == sym_postfix_expression ?
          ast_expression_type (expr, stack, higher_dimension) : NULL;
        
      case sym_postfix_expression:
        assert (expr->child && expr->child[0]);
        if (expr->child[1] == NULL || expr->child[2] == NULL)
          return ast_expression_type (expr->child[0], stack, higher_dimension);
        if (expr->child[1]->sym == token_symbol('.') || expr->child[1]->sym == sym_PTR_OP) {
          // struct member access
          Ast * str = ast_expression_type (expr->child[0], stack, higher_dimension);
          if (str) {
    	Ast * member = ast_find (expr->child[2], sym_member_identifier,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER);
    	Ast * declaration = ast_find (ast_declaration_from_type (str), sym_types);
    	assert (declaration);
    	AstTerminal * typename = (AstTerminal *)
    	  ast_schema (declaration, sym_types,
    		      0, sym_TYPEDEF_NAME);
    	if (!typename)
    	  typename = (AstTerminal *)
    	    ast_schema (declaration, sym_types,
    			0, sym_struct_or_union_specifier,
    			1, sym_generic_identifier,
    			0, sym_IDENTIFIER);
    	if (typename) {
    	  Ast * type = ast_identifier_declaration (stack, typename->start);
    	  if (!type) {
    #if 0	    
    	    fprintf (stderr, "%s:%d: warning: unknown type name '%s'\n",
    		     typename->file, typename->line, typename->start);
    #endif
    	    return NULL;
    	  }
    
    	  if (!member)
    	    return NULL;

    Special treatment of vector and tensor fields, to deal with possibly undefined components in lower dimensions.

    	  const char * mname = 
    	    (higher_dimension &&
    	     ast_terminal (member)->start[1] == '\0' &&
    	     strchr ("xyz", ast_terminal (member)->start[0]) &&
    	     (!strcmp (ast_terminal (type)->start, "vector") ||
    	      !strcmp (ast_terminal (type)->start, "tensor"))) ? "x" :
    	    ast_terminal (member)->start;
    
    	  if (!strcmp (ast_terminal (type)->start, "scalar"))
    	    type = ast_identifier_declaration (stack, "_Attributes");
    
    	  while (type->sym != sym_declaration)
    	    type = type->parent;
    	  return
    	    find_struct_member (ast_find (type, sym_struct_declaration_list),
    				mname);
    	}
    	else if ((str = ast_schema (declaration, sym_types,
    				    0, sym_struct_or_union_specifier,
    				    2, sym_struct_declaration_list)))
    	  return member ? find_struct_member (str, ast_terminal (member)->start) : NULL;
          }
        }
        break;
        
      }  
      return NULL;
    }
    
    static char * typedef_name_from_declaration (Ast * declaration)
    {
      Ast * types = ast_find (declaration, sym_types), * n;
      if ((n = ast_schema (types, sym_types, 0, sym_TYPEDEF_NAME)))
        return ast_terminal(n)->start;
      return NULL;
    }
    
    AstTerminal * ast_type (const Ast * identifier)
    {
      if (!identifier)
        return NULL;
      const Ast * declarator = ast_parent (identifier, sym_declarator);
      if (!ast_schema (declarator, sym_declarator,
    		   0, sym_direct_declarator,
    		   0, sym_generic_identifier,
    		   0, sym_IDENTIFIER) &&
          !ast_schema (declarator, sym_declarator,
    		   0, sym_direct_declarator,
    		   0, sym_direct_declarator,
    		   0, sym_generic_identifier,
    		   0, sym_IDENTIFIER))
        return NULL; // this is a pointer
      return ast_terminal (ast_find (ast_declaration_from_type (identifier),
    				 sym_types)->child[0]);
    }
    
    char * ast_typedef_name (const Ast * identifier)
    {
      AstTerminal * type = ast_type (identifier);
      if (!type || ((Ast *)type)->sym != sym_TYPEDEF_NAME)
        return NULL;
      return type->start;
    }
    
    static Ast * inforeach (Ast * n)
    {
      Ast * parent = n->parent;
      while (parent) {
        if (parent->sym == sym_foreach_statement)
          return parent;
        parent = parent->parent;
      }
      return NULL;
    }
    
    static bool point_declaration (Stack * stack)
    {
      const char * typename =
        ast_typedef_name (ast_identifier_declaration (stack, "point"));
      return typename && !strcmp (typename, "Point");
    }

    Add arguments (‘0’) to function_call so that the call has exactly n arguments.

    static void complete_arguments (Ast * function_call, int n)
    {
      Ast * args = ast_child (function_call, sym_argument_expression_list);
      if (!args) { // function call without arguments
        ast_new_children (function_call,
    		      function_call->child[0],
    		      function_call->child[1],
    		      ast_attach (ast_new (function_call,
    					   sym_argument_expression_list,
    					   sym_argument_expression_list_item),
    				  ast_new_constant (function_call->child[1],
    						    sym_I_CONSTANT, "0")),
    		      function_call->child[2]);
        args = ast_child (function_call, sym_argument_expression_list);
      }
      
      int i = 0;
      foreach_item (args, 2, item)
        i++;
      for (; i < n; i++) {
        args = ast_list_append (args,
    			    sym_argument_expression_list_item,
    			    ast_new_constant (function_call->child[3],
    					      sym_I_CONSTANT, "0"));
        ast_set_child (function_call, 2, args);
      }
    }
    
    static Ast * rotate_arguments (Ast * list, int dimension)
    {
      for (int i = 0; i < 3 - dimension; i++) {
        assert (list->child[1]);
        list = list->child[0];
      }
      if (!list->child[1])
        ast_print (list, stderr, 0);
      assert (list->child[1]);
      Ast * next = list->child[0], * item = list->child[2];
      for (int i = 1; i < dimension && next; i++) {
        if (next->child[1]) {
          ast_set_child (list, 2, next->child[2]);
          list = next;
          next = list->child[0];
        }
        else {
          ast_set_child (list, 2, next->child[0]);
          list = next;
          next = NULL;
        }	    
      }
      if (list->child[1])
        ast_set_child (list, 2, item);
      else
        ast_set_child (list, 0, item);
      return list;
    }
    
    typedef struct {
      Ast * identifier;
      int type, index, dimension, symmetric;
    } Field;
    
    static char * field_value (Field * c, const char * prefix, int type)
    {
      bool constant = false;
      int cindex = c->index;
      if (cindex >= 65535)
        cindex -= 65535, constant = true;
      char * src = NULL;
      if (c->type == 3) { // tensor
        int index = cindex, m[c->dimension][c->dimension];    
        for (int j = 0; j < c->dimension; j++) {
          if (type > 1)
    	str_append (src, "{");
          for (int i = 0; i < c->dimension; i++) {
    	char s[20];
    	if (c->symmetric) {
    	  m[i][j] = i >= j ? index++ : m[j][i];
    	  snprintf (s, 19, "%s%d", constant ? "_NVARMAX+" : "", m[i][j]);
    	}
    	else
    	  snprintf (s, 19, "%s%d", constant ? "_NVARMAX+" : "", index++);
    	str_append (src, "{", prefix, s, "}",
    		    i < c->dimension - 1 ? "," : "");
          }
          str_append (src, type > 1 ? "}" : "", j < c->dimension - 1 ? "," : "");
        }
      }
      else if (c->type == 2) // vector
        for (int i = 0; i < c->dimension; i++) {
          char s[20];
          snprintf (s, 19, "%s%d", constant ? "_NVARMAX+" : "", cindex + i);
          str_append (src, "{", prefix, s, "}",
    		  i < c->dimension - 1 ? "," : "");
        }
      else if (c->type == 1) { // scalar
        char s[30];
        snprintf (s, 29, "%s%d", constant ? "_NVARMAX+" : "", cindex);
        str_append (src, prefix, s);
      }
      if (type >= c->type) {
        str_prepend (src, "{");
        str_append (src, "}");
      }
      return src;
    }
    
    static void field_init (Field * c, const char * typename,
    			int dimension, int * index)
    {
      c->index = *index;
      if (!strcmp (typename, "scalar") ||
          !strcmp (typename, "vertex scalar"))
        c->type = 1, c->dimension = 1, *index += 1;
      else if (!strcmp (typename, "vector") ||
    	   !strcmp (typename, "face vector"))
        c->type = 2, c->dimension = dimension, *index += dimension;
      else if (!strcmp (typename, "tensor")) {
        c->type = 3, c->dimension = dimension;
        if (c->symmetric)
          *index += dimension*(dimension + 1)/2;
        else
          *index += dimension*dimension;
      }
      else if (!strcmp (typename, "symmetric tensor"))
        c->type = 3, c->dimension = dimension, c->symmetric = 1,
          *index += dimension*(dimension + 1)/2;
    }
    
    static Field * field_append (Field ** fields, Ast * identifier,
    			     const char * typename, int dimension, int * index)
    {
      int len = 0;
      for (Field * c = *fields; c->identifier; c++, len++);
      *fields = realloc (*fields, (len + 2)*sizeof (Field));
      (*fields)[len + 1] = (Field){0};
      Field * c = &(*fields)[len];
      c->identifier = identifier;
      c->symmetric = 0;
      field_init (c, typename, dimension, index);
      return c;
    }
    
    typedef struct {
      int dimension;
      bool nolineno, parallel, cpu;
      Field * constants;
      int constants_index, fields_index, nboundary;
      Ast * init_solver, * init_events, * init_fields, * last_events;
      Ast * boundary;
      char * swigname, * swigdecl, * swiginit;
    } TranslateData;
    
    static Ast * in_stencil_point_function (Ast * n)
    {
      n = ast_parent (n, sym_function_definition);
      if (!n)
        return NULL;
      if (ast_is_stencil_function (n))
        return n;
      return NULL;
    }
    
    static int stencil_access_function (const char * name)
    {
      if (!strcmp (name, "val") ||
          !strcmp (name, "val_a") || !strcmp (name, "val_r") || !strcmp (name, "val_o") ||
          !strcmp (name, "fine") ||
          !strcmp (name, "coarse"))
        return 4;
      else if (!strcmp (name, "allocated") ||
    	   !strcmp (name, "allocated_child") ||
    	   !strcmp (name, "neighbor") ||
    	   !strcmp (name, "neighborp") ||
    	   !strcmp (name, "aparent") ||
    	   !strcmp (name, "aparent_a") || !strcmp (name, "aparent_r") || !strcmp (name, "aparent_o") ||
    	   !strcmp (name, "child"))
        return 3;
      return 0;
    }
    
    static void rotate (Ast * n, Stack * stack, void * data)
    {
      TranslateData * d = data;
      switch (n->sym) {
        
      case sym_IDENTIFIER: case sym_FOREACH: {
        AstTerminal * t = ast_terminal (n);
        int len = strlen (t->start);
        if (len >= 2 && t->start[len - 2] == '_' &&
    	strchr ("xyz", t->start[len - 1]))
          t->start[len - 1] = 'x' + (t->start[len - 1] + 1 - 'x') % d->dimension;
        else if (d->dimension > 1) {
          if (!strcmp (t->start, "right"))
    	free (t->start), t->start = strdup ("top");
          else if (!strcmp (t->start, "left"))
    	free (t->start), t->start = strdup ("bottom");
          else if (!strcmp (t->start, "top"))
    	free (t->start), t->start = strdup ("front");
          else if (!strcmp (t->start, "bottom"))
    	free (t->start), t->start = strdup ("back");
          else if (!strcmp (t->start, "front"))
    	free (t->start), t->start = strdup ("right");
          else if (!strcmp (t->start, "back"))
    	free (t->start), t->start = strdup ("left");
        }
        break;
      }
    
      case sym_member_identifier: {
        AstTerminal * t = ast_terminal (ast_schema (n, sym_member_identifier,
    						0, sym_generic_identifier,
    						0, sym_IDENTIFIER));
        if (t->start[1] == '\0' && strchr ("xyz", *t->start))
          *t->start = 'x' + (*t->start + 1 - 'x') % d->dimension;
        break;
      }
    
      case sym_function_call: {
        if (d->dimension > 1) {
          Ast * identifier = ast_function_call_identifier (n);
          if (identifier) {
    	const char * name = ast_terminal (identifier)->start;
    	if (strcmp (name, "child") && stencil_access_function (name) &&
    	    (inforeach (n) || point_declaration (stack) ||
    	     in_stencil_point_function (n)))
    	  rotate_arguments (n->child[2], d->dimension);
          }
        }
        break;
      }
        
      }
    }
    
    static void rotate_list_item (Ast * item, Ast * n,
    			      Stack * stack, TranslateData * d)
    {
      int dimension = d->dimension;
      if (n->child[4]) {
        d->dimension = atoi (ast_terminal (n->child[2])->start);
        if (d->dimension > dimension)
          d->dimension = dimension;
      }
      
      Ast * list = item->parent;
      Ast * body = ast_last_child (n), * copy = body;
      if (d->dimension == 1) {
        stack_push (stack, &copy);
        ast_traverse (copy, stack, rotate, d);
        ast_pop_scope (stack, copy);    
      }
      else
        for (int i = 1; i < d->dimension; i++) {
          copy = ast_copy (copy);
          stack_push (stack, &copy);
          ast_traverse (copy, stack, rotate, d);
          ast_pop_scope (stack, copy);
          list = ast_block_list_append (list, item->sym, copy);
        }
      ast_set_child (item, 0, body);
      ast_remove (n, ast_left_terminal (body));
    
      d->dimension = dimension;
    }

    This function returns a block_item containing statement.

    Ast * ast_block_list_get_item (Ast * statement)
    {
      assert (statement->sym == sym_statement ||
    	  statement->sym == sym_declaration);
      Ast * item = statement->parent;

    if item is not already a block item we need to replace it with a compound statement containing a new block_item_list.

      if (item->sym != sym_block_item) {
        AstTerminal * l = ast_left_terminal (statement);
        Ast * left = ast_terminal_new_char ((Ast *) l, "{"),
          * right =
          ast_terminal_new_char ((Ast *) ast_right_terminal (statement), "}");
        ast_terminal (left)->before = l->before, l->before = NULL;
        Ast * parent = item;
        int index = ast_child_index (statement);
        item = ast_new_children (ast_new (parent, sym_block_item), statement);
        Ast * list = ast_new_children (ast_new (parent, sym_block_item_list),
    				   item);
        Ast * compound =
          ast_new_children (ast_new (parent, sym_statement),
    			ast_new_children (ast_new (parent,
    						   sym_compound_statement),
    					  left, list, right));
        ast_replace_child (parent, index, compound);
      }
      
      return item;
    }
    
    static
    void maybeconstfield (Ast * n, Stack * stack,
    		      void func (Ast * n, Ast * type, void * data),
    		      void * data)
    {
      Ast * identifier = ast_schema (n, sym_primary_expression,
    				 0, sym_IDENTIFIER);
      if (identifier) {
        Ast * type = ast_identifier_declaration (stack,
    					     ast_terminal (identifier)->start);
        if (type) {
          Ast * declaration = type;
          while (declaration &&
    	     declaration->sym != sym_declaration &&
    	     declaration->sym != sym_parameter_declaration &&
    	     declaration->sym != sym_forin_declaration_statement)
    	declaration = declaration->parent;
          if (ast_schema (ast_child (declaration, sym_declaration_specifiers),
    		      sym_declaration_specifiers,
    		      0, sym_type_qualifier,
    		      0, sym_MAYBECONST)) {
    	if (!strcmp (ast_terminal (identifier)->start, "fs") &&
    	    ast_terminal (identifier)->line == 233) {
    	  ast_stack_print (stack, stderr);
    	  ast_print (identifier, stderr, 0);
    	  abort();
    	}
    	func (n, type, data);
          }
        }
      }
      if (n->child)
        for (Ast ** c = n->child; *c; c++)
          maybeconstfield (*c, stack, func, data);  
    }
    
    Ast * ast_is_point_point (const Ast * n)
    {
      Ast * identifier = ast_schema (n, sym_IDENTIFIER);
      if (!identifier)
        identifier = ast_schema (n, sym_generic_identifier,
    			     0, sym_IDENTIFIER);
      if (identifier && identifier->parent->parent->sym == sym_direct_declarator &&
          !strcmp (ast_terminal (identifier)->start, "point")) {    
        const Ast * decl = n;
        while (decl->sym != sym_declaration &&
    	   decl->sym != sym_parameter_declaration)
          decl = decl->parent;
        Ast * type = ast_schema (decl->child[0],
    			     sym_declaration_specifiers,
    			     0, sym_type_specifier,
    			     0, sym_types,
    			     0, sym_TYPEDEF_NAME);
        if (type && !strcmp (ast_terminal (type)->start, "Point")) {
          if (decl->sym == sym_declaration)
    	return (Ast *) decl;
          else if (decl->sym == sym_parameter_declaration) {
    	while (decl->sym != sym_parameter_type_list)
    	  decl = decl->parent;
    	if ((decl = decl->parent)->sym != sym_direct_declarator ||
    	    (decl = decl->parent)->sym != sym_declarator ||
    	    (decl = decl->parent)->sym != sym_function_declaration ||
    	    (decl = decl->parent)->sym != sym_function_definition)
    	  return NULL;
    	return ast_last_child (decl)->child[0];
          }
        }
      }
      return NULL;
    }
    
    Ast * ast_is_point_function (const Ast * declarator)
    {
      Ast * parameters = ast_find (declarator, sym_parameter_type_list,
    			       0, sym_parameter_list);
      if (parameters)
        foreach_item (parameters, 2, param) {
          Ast * identifier = ast_find (param, sym_IDENTIFIER);
          if (identifier &&
    	  identifier->parent->parent->sym == sym_direct_declarator &&
    	  !strcmp (ast_terminal (identifier)->start, "point")) {
    	const Ast * decl = identifier;
    	while (decl->sym != sym_declaration &&
    	       decl->sym != sym_parameter_declaration)
    	  decl = decl->parent;
    	Ast * type = ast_schema (decl->child[0],
    				 sym_declaration_specifiers,
    				 0, sym_type_specifier,
    				 0, sym_types,
    				 0, sym_TYPEDEF_NAME);
    	if (type && !strcmp (ast_terminal (type)->start, "Point"))
    	  return identifier;
          }
        }
      return NULL;
    }
    
    typedef struct {
      void (* func) (Ast * n, Ast * type, void * data);
      void * data;
    } ConstData;
    
    static
    void maybeconst_traverse (Ast * n, Stack * stack, void * vd)
    {
      Ast * identifier = ast_function_call_identifier (n);
      if (identifier) {
        ConstData * d = vd;
        const char * name = ast_terminal (identifier)->start;
        if (!strcmp (name, "val") || !strcmp (name, "fine") || !strcmp (name, "coarse"))
          maybeconstfield (ast_find (n->child[2], sym_argument_expression_list_item),
    		       stack, d->func, d->data);
      }
    }
    
    static
    void maybeconst (Ast * n, Stack * stack,
    		 void func (Ast * n, Ast * type, void * data),
    		 void * data)
    {
      stack_push (stack, &n);
      ConstData d = { func, data };
      ast_traverse (n, stack, maybeconst_traverse, &d);
      ast_pop_scope (stack, n);
    }
    
    static
    void append_const (Ast * n, Ast * type, void * data)
    {
      Ast *** m = data;
      if (!*m) {
        *m = malloc (2*sizeof (Ast *));
        (*m)[0] = type;
        (*m)[1] = NULL;
      }
      else {
        int size = 0;
        Ast ** c;
        for (c = *m; *c && *c != type; c++, size++);
        if (*c != type) {
          *m = realloc (*m, (size + 2)*sizeof (Ast *));
          (*m)[size] = type;
          (*m)[size + 1] = NULL;
        }
      }
    }

    Replaces child at index of parent with replacement or with a parent of replacement of the same symbol as the child.

    void ast_replace_child_same_symbol (Ast * parent, int index, Ast * replacement)
    {
      while (replacement && replacement->sym != parent->child[index]->sym)
        replacement = replacement->parent;
      assert (replacement);
      ast_replace_child (parent, index, replacement);
    }
    
    static double sq (double x) { return x*x; }
    static double cube (double x) { return x*x*x; }

    Evaluates a constant (numerical) expression. Return DBL_MAX if the expression is not a constant.

    double ast_evaluate_constant_expression (const Ast * n)
    {
      if (!n)
        return DBL_MAX;
      
      switch (n->sym) {
    
      case sym_I_CONSTANT: case sym_F_CONSTANT: case sym_ENUMERATION_CONSTANT:
        return ast_terminal (n)->start ? atof (ast_terminal (n)->start) : 0.;
    
      case sym_constant:
        return ast_evaluate_constant_expression (n->child[0]);
    
      case sym_expression:
        return ast_evaluate_constant_expression (ast_child (n, sym_assignment_expression));
        
      case sym_expression_error:
        return ast_evaluate_constant_expression (n->child[0]);
        
      case sym_primary_expression:
        if (n->child[0]->sym == sym_constant)
          return ast_evaluate_constant_expression (n->child[0]);
        else if (n->child[1])
          return ast_evaluate_constant_expression (n->child[1]);
        break;
        
      case sym_assignment_expression:
        if (!n->child[1])
          return ast_evaluate_constant_expression (n->child[0]);
        break;
    
      case sym_postfix_expression:
        if (n->child[0]->sym == sym_primary_expression ||
    	n->child[0]->sym == sym_function_call ||
    	n->child[0]->sym == sym_array_access)
          return ast_evaluate_constant_expression (n->child[0]);
        break;
        
      case sym_cast_expression:
        if (n->child[0]->sym == sym_unary_expression)
          return ast_evaluate_constant_expression (n->child[0]);
        break;
        
      case sym_unary_expression:
        if (n->child[0]->sym == sym_postfix_expression)
          return ast_evaluate_constant_expression (n->child[0]);
        if (n->child[0]->sym == sym_unary_operator &&
    	strchr ("+-", ast_terminal (n->child[0]->child[0])->start[0])) {
          double v = ast_evaluate_constant_expression (n->child[1]);
          return v < DBL_MAX ? (ast_terminal (n->child[0]->child[0])->start[0] == '+' ? 1. : - 1.)*v : DBL_MAX;
        }
        break;
    
      case sym_conditional_expression: { // fixme not sure that this is correct
        double cond = ast_evaluate_constant_expression (n->child[0]);
        if (!n->child[1])
          return cond;
        if (cond == DBL_MAX)
          return DBL_MAX;
        if (cond)
          return ast_evaluate_constant_expression (n->child[2]);
        else
          return ast_evaluate_constant_expression (n->child[4]);
      }
    
      case sym_logical_or_expression: {
        double v = ast_evaluate_constant_expression (n->child[0]);
        if (!n->child[1])
          return v;
        else {
          double v1 = ast_evaluate_constant_expression (n->child[2]);
          if (v < DBL_MAX && v1 < DBL_MAX)
    	return v || v1;
        }
        break;
      }
        
      case sym_logical_and_expression: {
        double v = ast_evaluate_constant_expression (n->child[0]);
        if (!n->child[1])
          return v;
        else {
          double v1 = ast_evaluate_constant_expression (n->child[2]);
          if (v < DBL_MAX && v1 < DBL_MAX)
    	return v && v1;
        }
        break;
      }
        
      case sym_inclusive_or_expression: {
        double v = ast_evaluate_constant_expression (n->child[0]);
        if (!n->child[1])
          return v;
        else {
          double v1 = ast_evaluate_constant_expression (n->child[2]);
          if (v < DBL_MAX && v1 < DBL_MAX)
    	return ((int) v) | ((int) v1);
        }
        break;
      }
        
      case sym_exclusive_or_expression: {
        double v = ast_evaluate_constant_expression (n->child[0]);
        if (!n->child[1])
          return v;
        else {
          double v1 = ast_evaluate_constant_expression (n->child[2]);
          if (v < DBL_MAX && v1 < DBL_MAX)
    	return ((int) v) ^ ((int) v1);
        }
        break;
      }
        
      case sym_and_expression: {
        double v = ast_evaluate_constant_expression (n->child[0]);
        if (!n->child[1])
          return v;
        else {
          double v1 = ast_evaluate_constant_expression (n->child[2]);
          if (v < DBL_MAX && v1 < DBL_MAX)
    	return ((int) v) & ((int) v1);
        }
        break;
      }
    
      case sym_equality_expression: {
        double v = ast_evaluate_constant_expression (n->child[0]);
        if (!n->child[1])
          return v;
        else {
          double v1 = ast_evaluate_constant_expression (n->child[2]);
          if (v < DBL_MAX && v1 < DBL_MAX) {
    	if (n->child[1]->sym == sym_EQ_OP)
    	  return v == v1;
    	else
    	  return v != v1;
          }
        }
        break;
      }
        
      case sym_relational_expression: {
        double v = ast_evaluate_constant_expression (n->child[0]);
        if (!n->child[1])
          return v;
        else if (v < DBL_MAX) {
          double v1 = ast_evaluate_constant_expression (n->child[2]);
          if (v1 < DBL_MAX) {
    	if (n->child[1]->sym == sym_LE_OP)
    	  return v <= v1;
    	else if (n->child[1]->sym == sym_GE_OP)
    	  return v >= v1;
    	else if (n->child[1]->sym == token_symbol('>'))
    	  return v > v1;
    	else if (n->child[1]->sym == token_symbol('<'))
    	  return v < v1;
          }
        }
        break;
      }
    
      case sym_shift_expression: {
        double v = ast_evaluate_constant_expression (n->child[0]);
        if (!n->child[1])
          return v;
        else {
          double v1 = ast_evaluate_constant_expression (n->child[2]);
          if (v < DBL_MAX && v1 < DBL_MAX) {
    	if (n->child[1]->sym == sym_LEFT_OP)
    	  return ((int) v) << ((int) v1);
    	else
    	  return ((int) v) >> ((int) v1);
          }
        }
        break;
      }
          
      case sym_additive_expression: {
        double v = ast_evaluate_constant_expression (n->child[0]);
        if (!n->child[1])
          return v;
        else {
          double v1 = ast_evaluate_constant_expression (n->child[2]);
          if (v < DBL_MAX && v1 < DBL_MAX) {
    	if (n->child[1]->sym == token_symbol('+'))
    	  return v + v1;
    	else
    	  return v - v1;
          }
        }
        break;
      }
        
      case sym_multiplicative_expression: {
        double v = ast_evaluate_constant_expression (n->child[0]);
        if (!n->child[1])
          return v;
        else {
          double v1 = ast_evaluate_constant_expression (n->child[2]);
          if (v < DBL_MAX && v1 < DBL_MAX) {
    	if (n->child[1]->sym == token_symbol('*'))
    	  return v*v1;
    	if (n->child[1]->sym == token_symbol('/'))
    	  return v/v1;
    	else
    	  return ((int) v) % ((int) v1);
          }
        }
        break;
      }
    
      case sym_array_access:
        return ast_evaluate_constant_expression (n->child[0]);
    
    
      case sym_function_call: {
        Ast * name = ast_schema (n, sym_function_call,
    			     0, sym_postfix_expression,
    			     0, sym_primary_expression,
    			     0, sym_IDENTIFIER);
        if (name && n->child[3]) {
          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},
    	{"sq", sq}, {"cube", cube},
    	{NULL}
          }, * i = funcs;
          for (; i->name; i++)
    	if (!strcmp (ast_terminal (name)->start, i->name)) {
    	  double arg = ast_evaluate_constant_expression (n->child[2]);
    	  if (arg == DBL_MAX)
    	    return arg;
    	  return i->func (arg);
    	}
        }    
        break;
      }
    
      case sym_argument_expression_list:
      case sym_argument_expression_list_item:
        return ast_evaluate_constant_expression (n->child[0]);    
        
      }
    
      return DBL_MAX;
    }

    (const) fields combinations

    typedef struct {
      Ast ** consts;
      int bits;
    } ReplaceConst;
    
    static
    void replace_const (Ast * n, Ast * type, void * data)
    {
      ReplaceConst * r = data;
      int index = 0;
      Ast ** c;
      for (c = r->consts; *c && *c != type; c++, index++);
      assert (*c == type);
      if (r->bits & (1 << index)) {
        Ast * unary = n;
        while (unary->sym != sym_unary_expression)
          unary = unary->parent;
        unary = unary->parent;
        while (unary->sym != sym_unary_expression)
          unary = unary->parent;
        unary = unary->parent;
    
        Ast * p = unary;
        while (p->sym != sym_expression && !p->child[1])
          p = p->parent;
        if (!p->child[1] && p->parent->sym == sym_expression_statement) {

    Remove statement with no effect (to avoid compiler warnings)

          p = p->parent;
          ast_destroy (p->child[0]);
          p->child[0] = p->child[1]; p->child[1] = NULL;
        }
        else {
          str_prepend (ast_terminal (n->child[0])->start, "_const_");
          ast_replace_child_same_symbol (unary, 0, n);
        }
      }
    }
    
    static
    char * combination_constants (TranslateData * d, Ast ** consts, int bits,
    			      char * constants)
    {
      int nmaybeconst = 0;
      for (Ast ** c = consts; *c; c++, nmaybeconst++);
      for (int i = 0; i < nmaybeconst; i++)
        if (bits & (1 << i)) {
          const char * name = ast_terminal (consts[i])->start;
          const char * typename = ast_typedef_name (consts[i]);
          if (!strcmp (typename, "vector") ||
    	  !strcmp (typename, "face vector")) {
    	str_append (constants, "_coord _const_", name, "={_constant[",
    		    name,  ".x.i-_NVARMAX]");
    	for (int j = 1; j < d->dimension; j++) {
    	  char s[] = ".x.i-_NVARMAX]"; s[1] = 'x' + j;
    	  str_append (constants, ",_constant[", name, s);
    	}
    	str_append (constants, "};");
          }
          else
    	str_append (constants,
    		    "double _const_", name, "=_constant[",
    		    name, ".i-_NVARMAX];");
          str_append (constants, "NOT_UNUSED(_const_", name, ");");
        }
      return constants;
    }
    
    static void combinations (Ast * n, Stack * stack, TranslateData * d,
    			  Ast ** consts,
    			  Ast * list, Ast * item, const char * key)
    {
      int nmaybeconst = 0;
      for (Ast ** c = consts; *c; c++, nmaybeconst++);
      int n2 = 1 << nmaybeconst;
      char * condition = NULL;
      for (int bits = 0; bits < n2; bits++) {
        if (bits > 0)
          str_append (condition, "else ");
        if (bits == n2 - 1)
          str_append (condition, "{");
        else {
          str_append (condition, "if(");
          for (int i = 0; i < nmaybeconst; i++) {
    	const char * name = ast_terminal (consts[i])->start;
    	const char * typename = ast_typedef_name (consts[i]);
    	str_append (condition,
    		    (bits & (1 << i)) ? "" : "!",
    		    "is_constant(", name,
    		    !strcmp (typename, "vector") ||
    		    !strcmp (typename, "face vector") ? ".x" : "",
    		    ")");
    	if (i < nmaybeconst - 1)
    	  str_append (condition, " && ");
          }
          str_append (condition, "){");
        }
        condition = combination_constants (d, consts, bits, condition);
        char index[20];
        snprintf (index, 19, "%d", bits);
        str_append (condition, key, "{_statement", index, "_;}}");
      }
      Ast * conditional = ast_parse_expression (condition, ast_get_root (n));
      free (condition);
      for (int bits = 1; bits < n2; bits++) {
        Ast * copy = ast_copy (n);
        maybeconst (copy, stack, replace_const, &(ReplaceConst){consts, bits});
        char statement[100];
        snprintf (statement, 99, "_statement%d_", bits);
        assert (ast_replace (conditional, statement, copy));
      }
      assert (ast_replace (conditional, "_statement0_", n));
      ast_replace_child (item, 0, ast_new_children (ast_new (list, sym_statement),
    						conditional));
    }
    
    static int field_list_type (Ast * list, Stack * stack, bool mustbe)
    {
      int type = 4; // tensor
      foreach_item (list, 2, expr) {
        const char * typename =
          ast_typedef_name (ast_expression_type (expr, stack, false));
        if (!typename ||
    	(strcmp (typename, "scalar") &&
    	 strcmp (typename, "vector") &&
    	 strcmp (typename, "tensor"))) {
          if (mustbe) {
    	AstTerminal * t = ast_left_terminal (expr);
    	fprintf (stderr,
    		 "%s:%d: error: '%s' is not a scalar, vector or tensor\n",
    		 t->file, t->line, ast_str_append (expr, NULL));
    	exit (1);
          }
          return -1;
        }
        if (type > 1 && !strcmp (typename, "scalar")) type = 1;
        else if (type > 2 && !strcmp (typename, "vector")) type = 2;
        else if (type > 3 && !strcmp (typename, "tensor")) type = 3;
      }
      return type > 3 ? -1 : type;
    }
    
    bool ast_is_field (const char * typename)
    {
      return typename && (!strcmp (typename, "scalar") ||
    		      !strcmp (typename, "vertex scalar") ||
    		      !strcmp (typename, "vector") ||
    		      !strcmp (typename, "face vector") ||
    		      !strcmp (typename, "tensor") ||
    		      !strcmp (typename, "symmetric tensor"));
    }
    
    static Ast * declarator_is_allocator (Ast * declarator)
    {
      Ast * allocator;
      if ((allocator = ast_schema (declarator, sym_declarator,
    			       0, sym_direct_declarator)) &&
          allocator->child[0]->sym == sym_direct_declarator &&
          allocator->child[1]->sym == token_symbol('[') &&
          !allocator->child[3] &&
          (allocator = allocator->child[0]->child[0])->sym
          == sym_generic_identifier)
        return allocator;
      return NULL;
    }
    
    static Ast * automatic_argument (const Ast * init_declarator)
    {
      Ast
        * initializer = ast_child (init_declarator, sym_initializer),
        * unary = ast_is_unary_expression (ast_child (initializer,
    						  sym_assignment_expression)),
        * function_call = ast_schema (unary, sym_unary_expression,
    				  0, sym_postfix_expression,
    				  0, sym_function_call),
        * function_name = ast_function_call_identifier (function_call),
        * argument = ast_schema (function_call, sym_function_call,
    			     2, sym_argument_expression_list,
    			     0, sym_argument_expression_list_item,
    			     0, sym_assignment_expression);
      if (function_name && argument &&
          !strcmp (ast_terminal (function_name)->start, "automatic"))
        return argument;
      return NULL;
    }
    
    static Ast * declarator_is_automatic (const Ast * declarator)
    {
      Ast * allocator = ast_schema (declarator, sym_declarator,
    				0, sym_direct_declarator,
    				0, sym_generic_identifier);
      if (!allocator)
        return NULL;
      if (automatic_argument (declarator->parent))
        return allocator;
      return NULL;
    }
    
    static
    void foreach_field_allocator (Stack * stack, TranslateData * t, Ast * scope,
    			      void func (Stack *, TranslateData *,
    					 const char *,
    					 Ast *, Ast *, Ast *, void *),
    			      void * data)
    {
      Ast ** d;
      for (int i = 0; (d = stack_index (stack, i)) && *d != scope; i++)
        if (*d && (*d)->sym == sym_IDENTIFIER) {
          Ast * declarator, * init_declarator, * allocator;
          if (((declarator = ast_ancestor (*d, 4)) &&
    	   (init_declarator = declarator->parent)->sym == sym_init_declarator &&
    	   (allocator = declarator_is_allocator (declarator))) ||
    	  ((declarator = ast_ancestor (*d, 3)) &&
    	   (init_declarator = declarator->parent)->sym == sym_init_declarator &&
    	   (allocator = declarator_is_automatic (declarator)))) {
    	Ast * declaration = ast_declaration_from_type (allocator);
    	const char * typename = typedef_name_from_declaration (declaration);
    	if (ast_is_field (typename))
    	  func (stack, t, typename, init_declarator, declarator, allocator,
    		data);
          }
        }
    }
    
    static void
    field_deallocation (Stack * stack, TranslateData * d,
    		    const char * typename,
    		    Ast * init_declarator, Ast * declarator, Ast * allocator,
    		    void * data)
    {
      char ** delete = data;
      Ast * argument = automatic_argument (init_declarator);
      if (argument) {
        char * arg = ast_str_append (argument, NULL);
        if (strchr (typename, ' '))
          typename = strchr (typename, ' ') + 1;
        str_append (delete[1], "if(!(", arg, ")",
    		!strcmp (typename, "scalar") ? ".i" :
    		!strcmp (typename, "vector") ? ".x.i" : ".x.x.i",
    		")delete((scalar*){",
    		ast_terminal (allocator->child[0])->start,
    		"});");
        free (arg);
      }
      else
        str_append (delete[0], ast_terminal (allocator->child[0])->start, ",");
    }
    
    static char * delete_fields (char ** delete)
    {
      char * fields = delete[0], * automatics = delete[1];
      if (fields || automatics) {
        if (fields) {
          str_prepend (fields, "delete((scalar*){");
          fields[strlen (fields) - 1] = '\0';
          str_append (fields, "});");
        }
        if (automatics)
          str_append (fields, automatics);
        delete[0] = fields;
        return fields;
      }
      return NULL;
    }
    
    static void
    field_allocation (Stack * stack, TranslateData * d,
    		  const char * typename,
    		  Ast * init_declarator, Ast * declarator, Ast * allocator,
    		  void * data)
    {
      field_deallocation (stack, d, typename,
    		      init_declarator, declarator, allocator, data);
      
      char * src = strdup (typename);
      for (char * s = src; *s != '\0'; s++)
        if (*s == ' ')
          *s = '_';
      const char * name = ast_terminal (allocator->child[0])->start;
      if (strchr (typename, ' '))
        typename = strchr (typename, ' ') + 1;
    
      Ast * argument = automatic_argument (init_declarator);
      if (argument) {
        char * arg = ast_str_append (argument, NULL);
        str_prepend (src, typename, " _field_=(", arg, ")",
    		 !strcmp (typename, "scalar") ? ".i" :
    		 !strcmp (typename, "vector") ? ".x.i" : ".x.x.i",
    		 ">0?(", arg, "):new_"); // fixme: should be >= 0
        free (arg);
      }
      else
        str_prepend (src, typename, " _field_=new_");
      str_append (src, "(\"", name, "\");");
    	    
      Ast * expr = ast_parse_expression (src, ast_get_root (init_declarator));
      free (src);  
      ast_set_line (expr, ast_right_terminal (declarator));
      declarator = ast_find (expr, sym_init_declarator);
      ast_replace_child (declarator, 0, init_declarator->child[0]);
      ast_replace_child (init_declarator->parent,
    		     ast_child_index (init_declarator), declarator);
      ast_destroy (expr);

    Remove ‘[]’ from declarator if necessary.

      declarator = declarator->child[0];
      Ast * direct = ast_schema (declarator, sym_declarator,
    			     0, sym_direct_declarator,
    			     0, sym_direct_declarator);
      if (direct)
        ast_replace_child (declarator, 0, direct);
    }
    
    static Ast * compound_jump (Ast * return_statement, Ast * function_definition,
    			    const char * expression)
    {
      assert (return_statement->sym == sym_jump_statement);
      Ast * ret = ast_child (return_statement, sym_RETURN);  
      if (ret && return_statement->child[2] &&
          !ast_is_simple_expression (return_statement->child[1]->child[0])) {
        // return sthg (complicated);
        char * src = NULL;
        str_append (src, "{int ");
        Ast * pointer = ast_schema (function_definition, sym_function_definition,
    				0, sym_function_declaration,
    				1, sym_declarator,
    				0, sym_pointer);
        if (pointer)
          src = ast_str_append (pointer, src);
        str_append (src, "_ret=val;", expression, "return _ret;}");
        Ast * compound =
          ast_parse_expression (src, ast_get_root (function_definition));
        free (src);
        ast_replace (compound, "val", ast_find (return_statement,
    					    sym_assignment_expression));
        if (function_definition->sym == sym_function_definition) {
          Ast * func = ast_find (function_definition, sym_direct_declarator);
          while (func->child[0]->sym == sym_direct_declarator)
    	func = func->child[0];
          Ast * declarator = ast_flatten (ast_copy (func, sym_IDENTIFIER),
    				      ast_left_terminal (return_statement));
          AstTerminal * t = ast_terminal (ast_find (declarator, sym_IDENTIFIER));
          free (t->start); t->start = strdup ("_ret");
          ast_replace (compound, "_ret", declarator);
          
          Ast * type_specifier =
    	ast_flatten (ast_copy (ast_find (function_definition,
    					 sym_declaration_specifiers,
    					 0, sym_type_specifier)),
    		     ast_left_terminal (return_statement));
          ast_replace (compound, "int", type_specifier);
        }
        else
          assert (function_definition->sym == sym_event_definition);
        
        ast_replace_child (return_statement->parent, 0, compound);
        return compound;
      }
      else {
        // return;
        char * src = NULL;
        str_append (src, "{", expression, "return _ret;}");
        Ast * compound =
          ast_parse_expression (src, ast_get_root (function_definition));
        free (src);
        Ast * parent = return_statement->parent;
        ast_replace (compound, "_ret", return_statement);
        ast_replace_child (parent, 0, compound);
        return compound;
      }
      return NULL;
    }

    Boundary conditions

    This function replaces neumann/dirichlet(…) with neumann/dirichlet(0) and returns the number of replacements.

    static int homogeneize (Ast * n)
    {
      int nh = 0;
      if (n->sym == sym_function_call) {
        Ast * identifier = ast_function_call_identifier (n);
        if (identifier &&
    	(!strcmp (ast_terminal (identifier)->start, "_neumann") ||
    	 !strcmp (ast_terminal (identifier)->start, "_dirichlet") ||
    	 !strcmp (ast_terminal (identifier)->start, "_dirichlet_face"))) {
          str_append (ast_terminal (identifier)->start, "_homogeneous");
          nh = 1;
        }
      }
      if (n->child)
        for (Ast ** c = n->child; *c; c++)
          nh += homogeneize (*c);
      return nh;
    }
    
    static void boundary_expr (Ast * n, Stack * stack, void * data)
    {
      switch (n->sym) {
    
      case sym_postfix_expression: {

    Replaces .n, .t and .r relative vector components with the corresponding absolute .x, .y or .z absolute vector components.

        if (n->child[1] && n->child[1]->sym == token_symbol('.')) {
          const char * typename =
    	ast_typedef_name (ast_expression_type (n->child[0], stack, false));
          if (typename && (!strcmp (typename, "vector") ||
    		       !strcmp (typename, "face vector"))) {
    	Ast * member = ast_find (n->child[2], sym_member_identifier,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER);
    	TranslateData * d = data;
    	char * name = ast_terminal(member)->start,
    	  * dir = ast_left_terminal (d->boundary->child[2])->start;
    	if (!strcmp (dir, "left") || !strcmp (dir, "right")) {
    	  if (!strcmp (name, "n"))
    	    name[0] = 'x';
    	  else if (!strcmp (name, "t"))
    	    name[0] = 'y';
    	  else if (!strcmp (name, "r"))
    	    name[0] = 'z';
    	}
    	else if (!strcmp (dir, "top") || !strcmp (dir, "bottom")) {
    	  if (!strcmp (name, "n"))
    	    name[0] = 'y';
    	  else if (!strcmp (name, "t"))
    	    name[0] = d->dimension > 2 ? 'z' : 'x';
    	  else if (!strcmp (name, "r"))
    	    name[0] = 'x';
    	}
    	else if (!strcmp (dir, "front") || !strcmp (dir, "back")) {
    	  if (!strcmp (name, "n"))
    	    name[0] = 'z';
    	  else if (!strcmp (name, "t"))
    	    name[0] = 'x';
    	  else if (!strcmp (name, "r"))
    	    name[0] = 'y';
    	}
          }
        }

    Replaces a boundary field with its local value _s.

        TranslateData * d = data;
        if (ast_are_identical (n, d->boundary->child[0]))
          ast_replace_child (n->parent, ast_child_index (n),
    			 ast_new_identifier (d->boundary, "_s"));
        
        break;
      }

    Replaces ghost with the corresponding indices.

      case sym_array_access: {
        Ast * identifier;
        if (n->child[3] &&
    	(identifier = ast_is_identifier_expression (n->child[2]->child[0])) &&
    	!strcmp (ast_terminal(identifier)->start, "ghost")) {
          TranslateData * d = data;
          char * dir = ast_left_terminal (d->boundary->child[2])->start,
    	* index = (!strcmp (dir, "left") ? "a[-1,0,0];" :
    		   !strcmp (dir, "right") ? "a[1,0,0];" :
    		   !strcmp (dir, "bottom") ? "a[0,-1,0];" :
    		   !strcmp (dir, "top") ? "a[0,1,0];" :
    		   !strcmp (dir, "back") ? "a[0,0,-1];" :
    		   !strcmp (dir, "front") ? "a[0,0,1];" : NULL);
          assert (index);
          Ast * expr = ast_parse_expression (index, ast_get_root (d->boundary));
          ast_replace_child (n, 2, ast_find (expr, sym_array_access,
    					 2, sym_expression));
          ast_destroy (expr);
        }
        break;
      }

    Dirichlet boundary conditions for normal components of face fields.

      case sym_function_call: {
        TranslateData * d = data;
        Ast * member = ast_schema (d->boundary->child[0], sym_postfix_expression,
    			       2, sym_member_identifier,
    			       0, sym_generic_identifier,
    			       0, sym_IDENTIFIER);
        if (member && !strcmp (ast_terminal(member)->start, "x")) {
          Ast * identifier = ast_function_call_identifier (n);
          if (identifier &&
    	  !strcmp (ast_terminal (identifier)->start, "_dirichlet")) {
    	const char * typename =
    	  ast_typedef_name (ast_expression_type
    			    (d->boundary->child[0]->child[0],
    			     stack, false));
    	if (!strcmp (typename, "face vector"))
    	  str_append (ast_terminal (identifier)->start, "_face");
          }
        }
        break;
      }
        
      }
    }
    
    #define foreach_map(map)					\
      Ast ** _m, * map;						\
      for (int _i = 0; (_m = stack_index (stack, _i)); _i++)	\
        if ((map = ast_schema (*_m, sym_macro_statement,		\
    			   1, sym_compound_statement,		\
    			   1, sym_block_item_list)))
    
    static Ast * boundary_function (Ast * expr, Stack * stack, TranslateData * d,
    				char * before, char * ind)
    {
      char * src = NULL;
      snprintf (ind, 19, "%d", d->nboundary++);
      str_append (src,
    	      "static double _boundary", ind,
    	      "(Point point,Point neighbor,scalar _s,void *data){{"); // The double brackets are important
          
      char * index[] = {"i","j","k"}, * dir[] = {"x","y","z"};
      for (int i = 0; i < d->dimension; i++)
        str_append (src, "int ",
    		index[i], "g=neighbor.", index[i], "-point.", index[i], ";"
    		"if(", index[i], "g==0)", index[i], "g=_attribute[_s.i].d.",
    		dir[i], ";",
    		"NOT_UNUSED(", index[i], "g);");
      assert (before);
      str_append (src, "POINT_VARIABLES;");
      str_append (src, "{return(", before, "_expr_);}}}");
      free (before);
      Ast * boundary =
        ast_child (ast_parse_external_declaration (src, ast_get_root (expr)),
    	       sym_function_definition);
      ast_get_root (boundary)->alloc = ast_get_root (expr)->alloc;
      free (src);
      assert (expr->sym == sym_assignment_expression);
      ast_replace (boundary, "_expr_", expr);
      ast_set_line (boundary, ast_left_terminal (expr));
      Ast * compound = ast_find (ast_find (boundary, sym_compound_statement)->child[1], sym_compound_statement);
      Ast * list = ast_schema (compound, sym_compound_statement,
    			   1, sym_block_item_list);
      foreach_map (m)
        foreach_item (m, 1, item)
          ast_block_list_prepend (list, sym_block_item, ast_copy (item->child[0]));
      stack_push (stack, &expr);
      ast_traverse (expr, stack, boundary_expr, d);
      ast_pop_scope (stack, expr);
      return boundary;
    }
    
    static void set_boundary_component (Ast * member_identifier)
    {
      Ast * member = ast_schema (member_identifier, sym_member_identifier,
    			     0, sym_generic_identifier,
    			     0, sym_IDENTIFIER);
      if (member) {
        if (!strcmp (ast_terminal(member)->start, "n"))
          ast_terminal(member)->start[0] = 'x';
        else if (!strcmp (ast_terminal(member)->start, "t"))
          ast_terminal(member)->start[0] = 'y';
        else if (!strcmp (ast_terminal(member)->start, "r"))
          ast_terminal(member)->start[0] = 'z';
      }  
    }
    
    static char * set_boundary (Ast * array, char * ind)
    {
      assert (array->sym == sym_array_access);
      char * bc = ast_str_append (array->child[2], NULL);
      char * scalar = ast_str_append (array->child[0], NULL);
      char * set = NULL;
      str_append (set,
    	      "_attribute[", scalar, ".i].dirty=1,",
    	      "_attribute[", scalar, ".i].boundary[", bc,
    	      "]=_boundary", ind, ",",
    	      "_attribute[", scalar, ".i].boundary_homogeneous[", bc,
    	      "]=_boundary", ind);
      free (scalar);
      free (bc);
      return set;
    }
    
    static Ast * function_scope (Ast * n, Stack * stack)
    {
      if (point_declaration (stack))
        return NULL;
      while (n) {
        if (n->sym == sym_foreach_statement)
          return NULL;
        if (n->sym == sym_function_definition ||
    	n->sym == sym_event_definition)
          return n;
        n = n->parent;
      }
      return NULL;
    }

    Inserts item after insert in the list containing insert.

    Ast * ast_list_insert_after (Ast * insert, Ast * item)
    {
      Ast * list_item = insert->parent, * list = list_item->parent,
        * parent = list->parent;
      int item_sym = list_item->sym;
      int index = ast_child_index (list);
      Ast * nlist = NN(list, list->sym,
    		   list,
    		   NCB (list, ","),
    		   NN (list, item_sym,
    		       item));
      if (parent->sym != list->sym)
        ast_set_child (parent, index, nlist);
      else 
        ast_set_child (parent, 0, nlist);
      return list;
    }

    Inserts item after insert in the (block) list containing insert.

    Ast * ast_block_list_insert_after (Ast * insert, Ast * item)
    {
      Ast * list_item = insert->parent, * list = list_item->parent,
        * parent = list->parent;
      int item_sym = list_item->sym;
      assert (parent->sym == list->sym);	
      ast_set_child (parent, 0,
    		 ast_new_children (ast_new (list, list->sym),
    				   list,
    				   ast_new_children (ast_new (list, item_sym),
    						     item)));
      return list;
    }

    Inserts item before insert in the (block) list containing insert.

    Ast * ast_block_list_insert_before (Ast * insert, Ast * item)
    {
      return ast_block_list_insert_after
        (insert->parent->parent->child[0]->child[1]->child[0], item);
    }
    
    Ast * ast_block_list_insert_before2 (Ast * insert, Ast * item)
    {
      // fixme: merge with above
      Ast * parent = insert->child[0];
      Ast * list = ast_block_list_append (insert->parent, insert->sym, item);
      ast_set_child (insert, 0, list->child[1]->child[0]);
      ast_set_child (list->child[1], 0, parent);
      return list;
    }
    
    const char * ast_crop_before (const char * s)
    {
      while (strchr (" \t\n\r", *s)) s++;
      while (*s == '#' || *s == '@') {
        s++;
        while (*s != '\0' && *s != '\n') s++;
        while (strchr (" \t\n\r", *s)) s++;
      }
      return s;
    }
    
    static
    void compound_init (Ast * compound, Ast * n)
    {
      Ast * list = NN(compound, sym_block_item_list,
    		  NN(compound, sym_block_item,
    		     n));
      ast_new_children (compound, compound->child[0], list, compound->child[1]);
    }
    
    static
    void compound_append (Ast * compound, Ast * n)
    {
      Ast * list = ast_schema (compound, sym_compound_statement,
    			   1, sym_block_item_list);
      if (!list)
        compound_init (compound, n);
      else
        ast_block_list_append (list, sym_block_item, n);
    }
      
    static
    void compound_prepend (Ast * compound, Ast * n)
    {
      Ast * list = ast_schema (compound, sym_compound_statement,
    			   1, sym_block_item_list);
      if (!list)
        compound_init (compound, n);
      else
        ast_block_list_prepend (list, sym_block_item, n);
    }

    First pass: Global boundaries and stencils

    static void global_boundaries_and_stencils (Ast * n, Stack * stack, void * data)
    {
      switch (n->sym) {

    Warnings for Basilisk C parse errors

      case sym_YYerror: {
        AstTerminal * t = ast_left_terminal (n);
        char * s = NULL;
        s = ast_str_append (n, s);
        fprintf (stderr, "%s:%d: warning: Basilisk C parse error near `%s'\n",
    	     t->file, t->line, ast_crop_before (s));
        free (s);
        break;
      }

    Local boundary conditions

      case sym_array_access: {
    
        Ast * assign = ast_ancestor (n, 3), * scope;
        if (assign->sym == sym_assignment_expression &&
    	(scope = function_scope (n, stack))) {
          const char * typename =
    	ast_typedef_name (ast_expression_type (n->child[0], stack, false));
          Ast * member = NULL;
          if ((typename &&
    	   (!strcmp (typename, "scalar") ||
    	    !strcmp (typename, "vertex scalar"))) ||
    	  ((member = ast_schema (n->child[0], sym_postfix_expression,
    				 2, sym_member_identifier,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER)) &&
    	   (!strcmp (ast_terminal (member)->start, "n") ||
    	    !strcmp (ast_terminal (member)->start, "t") ||
    	    !strcmp (ast_terminal (member)->start, "r")) &&
    	   (typename =
    	    ast_typedef_name (ast_expression_type (n->child[0]->child[0],
    						   stack, false))) &&
    	   (!strcmp (typename, "vector") ||
    	    !strcmp (typename, "face vector")))) {
    	AstTerminal * t = ast_left_terminal (assign);
    	char * before = t->before;
    	t->before = NULL;
    	char ind[20];
    	TranslateData * d = data;
    	d->boundary = n;
    	Ast * boundary =
    	  boundary_function (ast_child (assign, sym_assignment_expression),
    			     stack, data, before, ind);
    	char * set = set_boundary (n, ind);
    	ast_block_list_insert_before (scope, boundary);
    	
    	Ast * homogeneous = ast_copy (boundary);
    	if (!homogeneize (homogeneous)) {
    	  ast_destroy (homogeneous);
    	  str_append (set, ";\n");
    	}
    	else {
    	  Ast * func = ast_find (homogeneous, sym_IDENTIFIER);
    	  str_append (ast_terminal (func)->start, "_homogeneous");
    	  str_append (set, "_homogeneous;\n");
    	  ast_block_list_insert_before (scope, homogeneous);
    	}	
    	Ast * expr = ast_parse_expression (set, ast_get_root (n));
    	free (set);
    	Ast * parent = ast_ancestor (assign, 2);
    	assert (parent->sym == sym_expression_statement);
    	ast_replace_child (parent, 0, ast_child (expr, sym_expression));
    	ast_destroy (expr);
          }
        }
        break;
      }

    Global boundary conditions

      case sym_boundary_definition: {
        Ast * expr = ast_schema (n, sym_boundary_definition,
    			     0, sym_assignment_expression,
    			     2, sym_assignment_expression);
        Ast * array = ast_find (n, sym_array_access);
        if (expr && array) {
          AstTerminal * t = ast_left_terminal (n);
          char * before = t->before;
          t->before = NULL;
          set_boundary_component (ast_schema (array->child[0],
    					  sym_postfix_expression,
    					  2, sym_member_identifier));
          char ind[20];
          TranslateData * d = data;
          d->boundary = array;      
          Ast * boundary = boundary_function (expr, stack, data, before, ind);
          char * set = set_boundary (array, ind);
          ast_replace_child (n->parent, 0, boundary);
          
          Ast * homogeneous = ast_copy (boundary);
          if (!homogeneize (homogeneous)) {
    	ast_destroy (homogeneous);
    	str_append (set, ";\n");
          }
          else {
    	Ast * func = ast_find (homogeneous, sym_IDENTIFIER);
    	str_append (ast_terminal (func)->start, "_homogeneous");
    	str_append (set, "_homogeneous;\n");
    	ast_block_list_insert_after (n, homogeneous);
          }
          Ast * expr = ast_parse_expression (set, ast_get_root (n));
          compound_append (d->last_events, NN(n, sym_statement, expr));
          free (set);
        }
        break;
      }

    Stencils

      case sym_foreach_statement: {
        if (!strcmp (ast_terminal (n->child[0])->start, "foreach") ||
    	!strcmp (ast_terminal (n->child[0])->start, "foreach_vertex") ||
    	!strcmp (ast_terminal (n->child[0])->start, "foreach_face") ||
    	!strcmp (ast_terminal (n->child[0])->start, "foreach_visible") ||
    	!strcmp (ast_terminal (n->child[0])->start, "foreach_point") ||
    	!strcmp (ast_terminal (n->child[0])->start, "foreach_region")) {
          bool overflow = false, nowarning = false, gpu = false;
          Ast * parameters = ast_child (n, sym_foreach_parameters);
          foreach_item (parameters, 2, item) {
    	Ast * identifier = ast_is_identifier_expression (item->child[0]);
    	bool noauto;
    	if (identifier) {
    	  const char * start = ast_terminal (identifier)->start;
    	  if (!strcmp (start, "gpu"))
    	    gpu = true;
    	  else if ((noauto = !strcmp (start, "noauto")) ||
    		   !strcmp (start, "overflow") ||
    		   !strcmp (start, "nowarning")) {
    	    if (!strcmp (start, "overflow"))
    	      overflow = true;
    	    else if (!strcmp (start, "nowarning"))
    	      nowarning = true;
    	    parameters = ast_list_remove (parameters, item);
    	    if (parameters == NULL) {
    	      ast_destroy (n->child[2]);
    	      for (Ast ** c = n->child + 2; *c; c++)
    		*c = *(c + 1);
    	    }
    	    if (noauto)
    	      return;
    	  }
    	}
          }
          TranslateData * d = data;
          bool parallel = d->parallel &&
    	strcmp (ast_terminal (n->child[0])->start, "foreach_visible");
          Ast * stencil = ast_copy (n);
          if (!ast_stencil (stencil, parallel, overflow, nowarning)) {
    	ast_destroy (stencil);
    	if (!gpu)
    	  break;
    	else
    	  stencil = NN(n, sym_foreach_statement,
    		       NB(n, sym_FOREACH, ast_terminal (n->child[0])->start),
    		       NCB(n, "("),
    		       NCB(n, ")"),
    		       NN(n, sym_statement,
    			  NN(n, sym_expression_statement,
    			     NCB(n, ";"))));
          }
          str_append (ast_terminal (ast_child (stencil, sym_FOREACH))->start, "_stencil");
          if (n->child[4])
    	ast_new_children (n, n->child[0], n->child[1], n->child[2],
    			  n->child[3], n->child[4], stencil);
          else
    	ast_new_children (n, n->child[0], n->child[1], n->child[2], n->child[3], stencil);
        }
        break;
      }
      
      }
    }

    Second pass: Most transformations

    static void diagonalize (Ast * n, Stack * stack, void * field)
    {
      if (n->sym == sym_function_call) {
        Ast * identifier = ast_function_call_identifier (n);
        if (identifier) {
          Ast * arg;
          if (!strcmp (ast_terminal (identifier)->start, "val") &&
    	  (inforeach (n) || point_declaration (stack)) &&
    	  (arg = ast_is_identifier_expression
    	   (ast_find (n, sym_assignment_expression))) &&
    	  !strcmp (ast_terminal (arg)->start,
    		   ast_terminal ((Ast *)field)->start))
    	str_append (ast_terminal (identifier)->start, "_diagonal");
        }
      }
    }
    
    bool ast_is_foreach_stencil (const Ast * n)
    {
      Ast * foreach = ast_schema (n, sym_foreach_statement,
    			      0, sym_FOREACH);
      if (!foreach)
        return false;
      int len = strlen (ast_terminal (foreach)->start) - 8;
      return len > 0 && !strcmp (ast_terminal (foreach)->start + len, "_stencil");
    }
    
    static Ast * higher_dimension (Ast * n)
    {
      char * s = in_stencil_point_function (n) ? "_stencil_val_higher_dimension" : "_val_higher_dimension";
      return ast_attach (ast_new (n, sym_primary_expression),
    		     ast_terminal_new (n, sym_IDENTIFIER, s));
    }
    
    static char * append_initializer (char * init, Ast * initializer, const char * typename)
    {
      if (!strcmp (typename, "vector")) {
        str_append (init, "(double[])");
        Ast * list = ast_schema (initializer, sym_initializer,
    			     1, sym_initializer_list);
        if (list) {
          char * initialize = ast_str_append (list, NULL);
          str_append (init, "{", initialize);
          free (initialize);
          int nr = 3;
          foreach_item (list, 2, item) nr--;
          while (nr--)
    	str_append (init, ",0.");
          str_append (init, "}");
          return init;
        }
      }
      char * initialize = ast_str_append (initializer, NULL);
      str_append (init, initialize);
      free (initialize);
      return init;
    }

    Attribute declaration

    static void attribute (Ast * n, Stack * stack, void * data)
    {
      if (n->sym != sym_attribute)
        return;
      Ast * identifier = ast_schema (n, sym_attribute,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER);
      if (identifier &&
          !strcmp (ast_terminal (identifier)->start, "attribute")) {

    Remove ‘attribute’ from external declarations.

        Ast * translation = n->parent->parent;
        assert (translation->child[1]);
        Ast * next = translation->child[0];
        assert (translation->parent->child[1]);
        ast_set_child (translation->parent, 0, next);
        assert (next->child[1]);
        str_prepend (ast_left_terminal (translation->parent->child[1])->before,
    		 ast_left_terminal (n)->before);

    Add attributes to typedef ’_Attributes’.

        Ast * attr = ast_identifier_declaration (stack, "_Attributes");
        while (attr->sym != sym_declaration)
          attr = attr->parent;
        ast_list_append_list (ast_find (attr, sym_struct_declaration_list),
    			  n->child[2]);

    Cleanup.

        ast_destroy (translation);
      }
    }
    
    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 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;
    }
    
    Ast * ast_constant_postfix_expression (const Ast * n, Stack * stack)
    {
      Ast * identifier = ast_schema (n, sym_postfix_expression,
    				 0, sym_primary_expression,
    				 0, sym_IDENTIFIER);
      if (!identifier)
        identifier = ast_schema (n, sym_postfix_expression,
    			     0, sym_postfix_expression,
    			     0, sym_primary_expression,
    			     0, sym_IDENTIFIER);
      if (!identifier)
        identifier = ast_schema (n, sym_postfix_expression,
    			     0, sym_postfix_expression,
    			     0, sym_postfix_expression,
    			     0, sym_primary_expression,
    			     0, sym_IDENTIFIER);
      if (identifier) {
        Ast * type = ast_identifier_declaration (stack, ast_terminal (identifier)->start);
        while (type && type->sym != sym_declaration)
          type = type->parent;
        if (type)
          return ast_schema (type->child[0], sym_declaration_specifiers,
    			 0, sym_type_qualifier,
    			 0, sym_CONST);
      }
      return NULL;
    }

    Stencil access

    This function transforms stencil accesses of the form s[i,j] into the function call val(s,i,j,0).

    void ast_stencil_access (Ast * n, Stack * stack, int dimension)
    {
      const char * typename =
        ast_typedef_name (ast_expression_type (n->child[0], stack, false));
      Ast * member, * foreach = NULL;
      if (typename &&
          (!strcmp (typename, "scalar") ||
           !strcmp (typename, "vertex scalar")) &&
          ((foreach = inforeach (n)) || point_declaration (stack))) {
        n->sym = sym_function_call;
        ast_set_char (ast_child (n, token_symbol('[')), '(');
    
        Ast * list = ast_child (n, sym_expression);
        if (list)
          ast_argument_list (list);
        complete_arguments (n, 3);
        list = ast_child (n, sym_argument_expression_list);
        char * before = ast_left_terminal (n)->before;
        ast_left_terminal (n)->before = NULL;
        Ast * func;
        if (ast_constant_postfix_expression (n->child[0], stack))
          func = ast_new_identifier (n, "_val_constant");
        else
          func = ast_new_identifier (n, "val");
        ast_set_child (n, 2,
    		   ast_list_prepend (list,
    				     sym_argument_expression_list_item,
    				     ast_attach (ast_new_unary_expression (n),
    						 n->child[0])));
        ast_set_char (n->child[3], ')');
        ast_set_child (n, 0, func);
        ast_left_terminal (n)->before = before;
      }

    Check whether we are trying to access (undeclared) ‘y’ or ‘z’ members of a vector or tensor field (i.e. higher dimension members).

      else if ((member = ast_schema (n->child[0], sym_postfix_expression,
    				 2, sym_member_identifier,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER)) &&
    	   ((dimension < 2 &&
    	     (!strcmp (ast_terminal (member)->start, "y") ||
    	      !strcmp (ast_terminal (member)->start, "t"))) ||
    	    (dimension < 3 &&
    	     (!strcmp (ast_terminal (member)->start, "z") ||
    	      !strcmp (ast_terminal (member)->start, "r")))) &&
    	   (typename =
    	    ast_typedef_name (ast_expression_type (n->child[0]->child[0],
    						   stack, false))) &&
    	   (!strcmp (typename, "vector") ||
    	    !strcmp (typename, "face vector")) &&
    	   ((foreach = inforeach (n)) || point_declaration (stack)))
        ast_replace_child (n->parent, 0, higher_dimension (n));
      else if ((member = ast_schema (n->child[0], sym_postfix_expression,
    				 0, sym_postfix_expression,
    				 2, sym_member_identifier,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER)) &&
    	   ((dimension < 2 &&
    	     !strcmp (ast_terminal (member)->start, "y")) ||
    	    (dimension < 3 &&
    	     !strcmp (ast_terminal (member)->start, "z"))) &&
    	   (typename =
    	    ast_typedef_name (ast_expression_type
    			      (n->child[0]->child[0]->child[0],
    			       stack, false))) &&
    	   !strcmp (typename, "tensor") &&
    	   (inforeach (n) || point_declaration (stack)))
        ast_replace_child (n->parent, 0, higher_dimension (n));
    }
    
    static void translate (Ast * n, Stack * stack, void * data)
    {
      typedef struct {
        char * target, * replacement;
      } Replacement;
      
      switch (n->sym) {

    foreach_dimension()

      case sym_foreach_dimension_statement: {
        Ast * item = ast_block_list_get_item (n->parent->parent);
        rotate_list_item (item, n, stack, data);
        break;
      }

    External foreach_dimension()

      case sym_external_foreach_dimension: {
        rotate_list_item (n->parent, n, stack, data);
        break;
      }

    Diagonalize

      case sym_macro_statement: {
        Ast * identifier = ast_schema (n, sym_macro_statement,
    				   0, sym_function_call,
    				   0, sym_postfix_expression,
    				   0, sym_primary_expression,
    				   0, sym_IDENTIFIER);
        if (!strcmp (ast_terminal (identifier)->start, "diagonalize")) {
          Ast * field = ast_schema (n, sym_macro_statement,
    				0, sym_function_call,
    				2, sym_argument_expression_list,
    				0, sym_argument_expression_list_item,
    				0, sym_assignment_expression);
          if (field && (field = ast_is_identifier_expression (field))) {
    	stack_push (stack, &n);
    	ast_traverse (n, stack, diagonalize, field);
    	ast_pop_scope (stack, n);
          }
        }
        break; 
      }

    Foreach statements

      case sym_foreach_statement: {

    foreach_face() statements

        bool is_face_stencil = !strcmp (ast_terminal (n->child[0])->start,
    				    "foreach_face_stencil");
        if (is_face_stencil ||
    	!strcmp (ast_terminal (n->child[0])->start, "foreach_face")) {
          char order[] = "xyz";

    The complicated stuff below is just to read each (optional) x, y and z arguments, in the correct order, and update the order string.

          Ast * parameters = ast_schema (n, sym_foreach_statement, 2, sym_foreach_parameters);
          if (parameters) {
    	char * s = order + 2;
    	foreach_item (parameters, 2, param)
    	  if (param->child[0]->sym == sym_assignment_expression) {
    	    Ast * identifier = ast_find (param, sym_postfix_expression,
    					 0, sym_primary_expression,
    					 0, sym_IDENTIFIER);
    	    if (identifier && ast_terminal (identifier)->start[1] == '\0' &&
    		strchr ("xyz", ast_terminal (identifier)->start[0])) {
    	      *s-- = ast_terminal (identifier)->start[0];
    	      parameters = ast_list_remove (parameters, param);
    	    }
    	  }
    	if (s != order + 2 && s >= order)
    	  memmove (order, s + 1, strlen(s));
    	if (parameters == NULL) {
    	  ast_destroy (n->child[2]);
    	  for (Ast ** c = n->child + 2; *c; c++)
    	    *c = *(c + 1);
    	}
          }

    Here we add the is_face_x() condition to the loop statement.

          Ast * expr = ast_parse_expression
    	(is_face_stencil ? "_stencil_is_face_x(){;}" : "is_face_x(){;}",
    	 ast_get_root (n));
          Ast * cond = ast_find (expr, sym_IDENTIFIER);
          ast_terminal (cond)->start[strlen(ast_terminal (cond)->start) - 1] = order[0];
          Ast * statement = ast_child (n, sym_statement);
          int index = ast_child_index (statement);
          ast_replace (expr, ";", statement);
          ast_set_line (expr, ast_left_terminal (n));
          ast_replace_child (n, index, ast_new_children (ast_new (n, sym_statement), expr));

    Finally, we “dimension-rotate” the statement.

          if (strlen (order) > 1) {
    	Ast * statement = ast_child (n, sym_statement);
    	Ast * item = ast_block_list_get_item (statement);
    	TranslateData * d = data;
    	int dimension = d->dimension;
    	d->dimension = strlen (order);
    	if (d->dimension > dimension) d->dimension = dimension;
    	
    	Ast * list = item->parent, * copy = statement;
    	for (int i = 1; i < d->dimension; i++) {
    	  copy = ast_copy (copy);
    	  stack_push (stack, &copy);
    	  ast_traverse (copy, stack, rotate, d);
    	  ast_pop_scope (stack, copy);
    	  Ast * cond = ast_find (copy, sym_IDENTIFIER);
    	  ast_terminal (cond)->start[strlen(ast_terminal (cond)->start) - 1] =
    	    order[i];
    	  list = ast_block_list_append (list, item->sym, copy);
    	}
    	if (statement->sym != sym_statement)
    	  statement = ast_new_children (ast_new (n, sym_statement), statement);
    	ast_set_child (item, 0, statement);
    
    	d->dimension = dimension;
          }
        }

    (const) fields combinations (except for stencils)

        if (!ast_is_foreach_stencil (n)) {
          Ast ** consts = NULL;
          maybeconst (n, stack, append_const, &consts);
          if (consts) {
    	Ast * item = ast_block_list_get_item (n->parent->parent);
    	Ast * list = item->parent;
    	combinations (n, stack, data, consts, list, item, "foreach()");
    	free (consts);
          }
        }
        
        break;
      }

    (const) fields combinations for Point functions

      case sym_function_definition: {
        if (obsolete_function_declaration (n)) {
          AstTerminal * t = ast_left_terminal (n);
          fprintf (stderr, "%s:%d: warning: obsolete optional/named arguments syntax\n", t->file, t->line);      
        }
        if (ast_is_point_function (ast_schema (n, sym_function_definition,
    					   0, sym_function_declaration,
    					   1, sym_declarator)) &&
    	!ast_is_stencil_function (n)) {
          Ast ** consts = NULL;
          maybeconst (n, stack, append_const, &consts);
          if (consts) {
    	Ast * compoundi = ast_schema (n, sym_function_definition,
    				      1, sym_compound_statement);
    	Ast * compound = ast_copy (compoundi);
    	Ast * list = ast_child (compoundi, sym_block_item_list);
    	Ast * item = list->child[0];
    	if (list->child[1]) {
    	  ast_destroy (list->child[1]);
    	  list->child[1] = NULL;
    	}
    	item->sym = sym_block_item;
    	ast_destroy (item->child[0]);
    	if (item->child[1]) {
    	  ast_destroy (item->child[1]);
    	  item->child[1] = NULL;
    	}
    	combinations (compound, stack, data, consts, list, item, "");
    	free (consts);
          }
        }
        break;
      }
    
      case sym_array_access:
        ast_stencil_access (n, stack, ((TranslateData *)data)->dimension);
        break;
    
      case sym_attribute:
        attribute (n, stack, data);
        break;

    Replacement of some identifiers

      case sym_IDENTIFIER: {
        if (n->parent->sym == sym_primary_expression) {
          static Replacement replacements[] = {
    	{ "stderr", "ferr" },
    	{ "stdout", "fout" },
    	{ "qerr", "qstderr()" },
    	{ "qout", "qstdout()" },
    	{ NULL, NULL }
          };
          Replacement * i = replacements;
          AstTerminal * identifier = ast_terminal (n);
          while (i->target) {
    	if (identifier->start && !strcmp (identifier->start, i->target)) {
    	  free (identifier->start);
    	  identifier->start = strdup (i->replacement);
    	}
    	i++;
          }
        }
        break;
      }

    Breaks within foreach_inner loops

      case sym_BREAK: {
        Ast * loop = n->parent;
        while (loop &&
    	   loop->sym != sym_foreach_inner_statement &&
    	   loop->sym != sym_foreach_statement &&
    	   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))
          loop = loop->parent;
        if (loop && loop->sym == sym_foreach_inner_statement) {
          ast_before (n, ast_terminal (loop->child[0])->start, "_");
          ast_after (n, "()");
        }
        break;
      }

    Constant field and global field allocations

      case sym_init_declarator: {
        Ast * declarator = declarator_is_allocator (n->child[0]);
        if (declarator) {
          Ast * declaration = ast_declaration_from_type (declarator);
          const char * typename = typedef_name_from_declaration (declaration);
          if (ast_is_field (typename)) {
    	char * func = strdup (typename);
    	for (char * s = func; *s != '\0'; s++)
    	  if (*s == ' ')
    	    *s = '_';
    	AstTerminal * field = ast_terminal (declarator->child[0]);
    	const char * name = field->start;
    	if (strchr (typename, ' '))
    	  typename = strchr (typename, ' ') + 1;

    Constant fields initialization

    	if (ast_schema (declaration, sym_declaration,
    			0, sym_declaration_specifiers,
    			0, sym_type_qualifier,
    			0, sym_CONST)) {
    	  const char * const_func = strchr (func, '_');
    	  const_func = const_func ? const_func + 1 : func;
    	  TranslateData * d = data;
    	  
    	  if (!n->child[1]) {
    	    AstTerminal * t = ast_left_terminal (n);
    	    fprintf (stderr,
    		     "%s:%d: error: constant fields must be initialized\n",
    		     t->file, t->line);
    	    exit (1);
    	  }
    
    	  if (declaration->parent->sym == sym_external_declaration) {

    Global constant field declaration

    	    Field * c = field_append (&d->constants, declarator->child[0],
    				      typename, d->dimension,
    				      &d->constants_index);
    	    field->value = (void *)(long) c->index + 65536;
    	    char * src = field_value (c, "_NVARMAX+", c->type);
    	    char * init = NULL;
    	    str_append (init,
    			"init_const_", const_func, "((", typename, ")",
    			src, ",\"", name, "\",");
    	    init = append_initializer (init, n->child[2], typename);
    	    str_append (init, ");");
    	    Ast * finit = ast_parse_expression (init, ast_get_root (n));
    	    free (init);
    	    ast_set_line (finit, ast_left_terminal (n->child[2]));
    	    compound_append (d->init_fields, NN(n, sym_statement, finit));
    	    
    	    str_prepend (src, typename, " s=");
    	    str_append (src, ";");
    	    Ast * expr = ast_parse_expression (src, ast_get_root (n));
    	    free (src);
    	    ast_replace_child (n, 2, ast_find (expr, sym_initializer));
    	    ast_destroy (expr);
    	  }
    	  else {

    Local constant field declaration

    	    char * src = NULL, ind[10];
    	    snprintf (ind, 9, "%d", d->constants_index);
    	    d->constants_index +=
    	      !strcmp (typename, "scalar") ? 1 : d->dimension;
    	    str_append (src, "double a = new_const_",
    			const_func, "(\"", name, "\",",
    			ind, ",");
    	    src = append_initializer (src, n->child[2], typename);
    	    str_append (src, ");");
    	    Ast * expr = ast_parse_expression (src, ast_get_root (n));
    	    free (src);
    	    ast_replace_child (n, 2, ast_find (expr, sym_initializer));	    
    	    ast_destroy (expr);
    	  }
    	}

    Global field allocation

    	else if (declaration->parent->sym == sym_external_declaration) {
    	  TranslateData * d = data;
    	  Field c;
    	  c.symmetric = !strcmp (func, "symmetric_tensor");
    	  field_init (&c, typename, d->dimension, &d->fields_index);
    	  field->value = (void *)(long) c.index + 1;
    	  char * src = field_value (&c, "", c.type);
    	  char * init = NULL;
    	  str_append (init,
    		      "  init_", func, "((", typename, ")", src, ",\"",
    		      name, "\");");
    	  Ast * finit = ast_parse_expression (init, ast_get_root (n));
    	  free (init);
    	  compound_append (d->init_fields, NN(n, sym_statement, finit));
    	  str_prepend (src, typename, " _field_=");
    	  str_append (src, ";");
    
    	  Ast * expr = ast_parse_expression (src, ast_get_root (n));
    	  free (src);
    	  ast_set_line (expr, ast_right_terminal (n->child[0]));
    	  declarator = ast_find (expr, sym_init_declarator);
    	  ast_replace_child (declarator, 0, n->child[0]);
    	  ast_replace_child (n->parent, ast_child_index (n), declarator);
    	  n = declarator;
    	  ast_destroy (expr);

    SWIG interface

    	  if (d->swigname) {
    	    str_append (d->swigdecl, "extern ", typename, " ", name, ";\n");
    	    str_append (d->swiginit, name, "=", typename,
    			 "(_", d->swigname, ".cvar.", name, ")\n");
    	  }
    	}

    This is a an automatic (local) field allocations, which is treated at the end of the scope (together with deallocation).

    	else {
    	  free (func);
    	  break;
    	}

    Remove ‘[]’ from declarator.

    	declarator = n->child[0];
    	Ast * direct = declarator->child[0];
    	ast_replace_child (declarator, 0, direct->child[0]);
    	free (func);
          }
        }
        else if (n->child[1] && ast_declaration_from_type (n)->parent->sym
    	     == sym_external_declaration) {

    Global constant field initialization

          Ast * identifier = ast_is_identifier_expression (n->child[2]->child[0]);
          if (identifier) {
    	TranslateData * d = data;
    	for (Field * c = d->constants; c->identifier; c++)
    	  if (!strcmp (ast_terminal (c->identifier)->start,
    		       ast_terminal (identifier)->start)) {
    	    char * src = field_value (c, "_NVARMAX+", c->type);
    	    str_prepend (src, "double s=");
    	    str_append (src, ";");
    	    Ast * expr = ast_parse_expression (src, ast_get_root (n));
    	    free (src);
    	    ast_replace_child (n, 2, ast_find (expr, sym_initializer));
    	    ast_destroy (expr);
    	    break;
    	  }
          }
        }
            
        break;
      }

    Function calls

      case sym_function_call: {
        Ast * identifier = ast_function_call_identifier (n);
        if (identifier) {
          AstTerminal * t = ast_terminal (identifier);
          TranslateData * d = data;

    Memory allocation tracing

          static Replacement replacements[] = {
    	{ "malloc",  "pmalloc" },
    	{ "calloc",  "pcalloc" },
    	{ "realloc", "prealloc" },
    	{ "free",    "pfree" },
    	{ "strdup",  "pstrdup" },
    	{ NULL, NULL }
          };
          Replacement * i = replacements;
          while (i->target) {
    	if (!strcmp (t->start, i->target)) {
    	  free (t->start);
    	  t->start = strdup (i->replacement);
    	  assert (n->child[3]);
    	  ast_before (n->child[3], ",__func__,__FILE__,",
    		      d->nolineno ? "0" : "__LINE__");
    	  return;
    	}
    	i++;
          }

    Stencil functions

          int args = stencil_access_function (t->start);
          if (args && (inforeach (n) || point_declaration (stack)))
    	complete_arguments (n, args);
          if (!strcmp (ast_terminal (identifier)->start, "_overflow") ||
    	  !strcmp (ast_terminal (identifier)->start, "_assign") ||
    	  !strcmp (ast_terminal (identifier)->start, "r_assign")) {
    	Ast * val = ast_find (n->child[2], sym_function_call);
    	if (val) {
    	  Ast * name = ast_function_call_identifier (val);
    	  str_append (ast_terminal (name)->start,
    		      !strcmp (ast_terminal (identifier)->start, "_overflow") ?
    		      "_o" :
    		      ast_terminal (identifier)->start[0] == '_' ? "_a" : "_r");
    	  ast_replace_child (n->parent, ast_child_index (n), val);
    	  return;
    	}
          }

    Macro statement

          if (n->parent->sym == sym_macro_statement) {
    	char * name = NULL;
    	str_append (name, "begin_", t->start);
    	Ast * type = ast_identifier_declaration (stack, name);
    	if (type &&
    	    ast_declaration_from_type (type)->sym == sym_function_declaration) {
    	  ast_before (identifier, "{");
    	  ast_after (n, ";");
    	  ast_after (n->parent, "end_", t->start, "();}");
    	  free (t->start);
    	  t->start = name;
    	}
    	else // fixme: should this be an error?
    	  free (name);
          }

    Functions with optional arguments

          Ast * type = ast_identifier_declaration (stack, t->start);
          if (type) {
    	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 (n, sym_argument_expression_list);
    	    if (!arguments) {
    	      Ast * expr = ast_parse_expression ("func((struct Name){0});",
    						 ast_get_root (n));
    	      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 (n->child[1]));
    	      ast_new_children (n, n->child[0], n->child[1],
    				ast_placeholder,
    				n->child[2]);
    	      ast_replace_child (n, 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 (n));
    		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 (n, 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 (ast_find (parameters, sym_parameter_declaration,
    			     3, sym_initializer) ||
    		   ast_find (n, sym_argument_expression_list_item,
    			     0, sym_assignment_expression,
    			     1, sym_assignment_operator)) {
    	    parameters = ast_copy (parameters); // fixme: memory is leaking from here
    	    Ast * parameters1 = parameters;
    	    while (parameters && parameters->child[0]->sym == parameters->sym)
    	      parameters = parameters->child[0];
    	    Ast * arguments = ast_schema (n, sym_function_call,
    					  2, 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 function parameter '%s'\n",
    			     t->file, t->line, t->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];
    		}
    		assert (parameter);
    		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(n, "="),
    				    NN(n, 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 (n);
    		fprintf (stderr, "%s:%d: error: missing compulsory parameter '%s' in function call\n",
    			 t->file, t->line, ast_terminal (id)->start);
    		exit (1);
    	      }
    	      Ast * assign = ast_schema (initializer, sym_initializer,
    					 0, sym_assignment_expression);
    	      if (assign)
    		ast_new_children (parameter, assign);
    	      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(n, sym_type_name,
    		     NN(n, sym_specifier_qualifier_list,
    			type_specifier),
    		     abstract) :
    		  NN(n, sym_type_name,
    		     NN(n, sym_specifier_qualifier_list,
    			type_specifier));
    		ast_new_children (parameter, ast_attach
    				  (ast_new_unary_expression (parameter),				 
    				   NN(n, sym_postfix_expression,
    				      ob, type_name, cb,
    				      initializer)));
    	      }
    	      parameter->sym = sym_argument_expression_list_item;
    	      parameter->parent->sym = sym_argument_expression_list;
    	    }
    	    if (n->child[3])
    	      ast_set_child (n, 2, parameters1);
    	    else
    	      ast_new_children (n, n->child[0], n->child[1], parameters1, n->child[2]);
    	  }	  
    	}
          }
        }
        break;
        
        if (!identifier || strcmp (ast_terminal (identifier)->start, "automatic"))
          break;
        else {

    This is a call to automatic() which will be treated with sym_NEW_FIELD below.

          n = identifier;
        }
      }

    New' andautomatic’ fields

      case sym_NEW_FIELD: {
        Ast * parent = n;
        while (parent &&
    	   parent->sym != sym_init_declarator &&
    	   (parent->sym != sym_assignment_expression || !parent->child[1]))
          parent = parent->parent;
        if (!parent) {
          AstTerminal * t = ast_terminal (n);
          fprintf (stderr,
    	       "%s:%d: error: '%s' must be used within a declarator "
    	       "or an assignment expression\n", t->file, t->line, t->start);
          exit (1);
        }
        Ast * identifier = NULL, * declaration = NULL;
        if ((identifier = ast_schema (parent, sym_init_declarator,
    				  0, sym_declarator,
    				  0, sym_direct_declarator,
    				  0, sym_generic_identifier,
    				  0, sym_IDENTIFIER)))
          declaration = ast_declaration_from_type (identifier);
        else if ((identifier = ast_schema (parent, sym_assignment_expression,
    				       0, sym_unary_expression,
    				       0, sym_postfix_expression,
    				       0, sym_primary_expression,
    				       0, sym_IDENTIFIER))) {
          AstTerminal * t = ast_terminal (identifier);
          declaration = ast_identifier_declaration (stack, t->start);
          if (!declaration) {
    	fprintf (stderr,
    		 "%s:%d: error: undeclared variable '%s'\n",
    		 t->file, t->line, t->start);
    	exit (1);
          }
          declaration = ast_declaration_from_type (declaration);
        }
        else {
          AstTerminal * t = ast_terminal (n);
          fprintf (stderr,
    	       "%s:%d: error: '%s' must be used to initialize a named field\n",
    	       t->file, t->line, t->start);
          exit (1);
        }
        const char * typename = typedef_name_from_declaration (declaration);
        if (ast_is_field (typename)) {
          if (!strstr (ast_terminal (n)->start, typename)) {
    	AstTerminal * t = ast_terminal (n);
    	fprintf (stderr,
    		 "%s:%d: error: type mismatch for `new', "
    		 "expected '%s' got '%s'\n",
    		 t->file, t->line, typename, t->start);
    	exit (1);	
          }
    
          char * src = strdup (typename);
          for (char * s = src; *s; s++)
    	if (*s == ' ')
    	  *s = '_';
          Ast * layers = ast_schema (n->parent, sym_new_field,
    				 2, sym_postfix_expression);
          if (layers) {
    	str_append (src, "(\"", ast_terminal (identifier)->start,
    		    !strcmp (src, "scalar") ? "\",\"\"," : "\",");
    	src = ast_str_append (layers, src);
    	str_append (src, ");");
    	str_prepend (src, "new_block_");
          }
          else {
    	str_prepend (src, "new_");
    	str_append (src, "(\"", ast_terminal (identifier)->start, "\");");
          }
          Ast * expr = ast_parse_expression (src, ast_get_root (n));
          free (src);
          ast_set_line (expr, ast_terminal (n));
    
          Ast * r = ast_find (expr, sym_assignment_expression);
          ast_remove (n, ast_left_terminal (r));
          if (parent->sym == sym_init_declarator) {
    	parent = ast_schema (parent, sym_init_declarator,
    			     2, sym_initializer);
    	ast_replace_child (parent, 0, r);
          }
          else
    	ast_replace_child (parent, 2, r);
          ast_destroy (expr);
        }
        else {
          AstTerminal * t = ast_terminal (n);
          fprintf (stderr,
    	       "%s:%d: error: '%s' must be used to initialize a "
    	       "scalar, vector or tensor field\n",
    	       t->file, t->line, t->start);
          exit (1);      
        }
        break;
      }

    Static FILE *

      case sym_declaration: {
        Ast * type, * pointer, * identifier, * equal;
        if (ast_schema (n, sym_declaration,
    		    0, sym_declaration_specifiers,
    		    0, sym_storage_class_specifier,
    		    0, sym_STATIC) &&
    	(type = ast_schema (n, sym_declaration,
    			    0, sym_declaration_specifiers,
    			    1, sym_declaration_specifiers,
    			    0, sym_type_specifier,
    			    0, sym_types,
    			    0, sym_TYPEDEF_NAME)) &&
    	!strcmp (ast_terminal(type)->start, "FILE") &&
    	(pointer = ast_schema (n, sym_declaration,
    			       1, sym_init_declarator_list,
    			       0, sym_init_declarator,
    			       0, sym_declarator,
    			       0, sym_pointer)) &&
    	!pointer->child[1] &&
    	ast_parent (n, sym_event_definition) &&
    	(identifier = ast_schema (n, sym_declaration,
    				  1, sym_init_declarator_list,
    				  0, sym_init_declarator,
    				  0, sym_declarator,
    				  1, sym_direct_declarator,
    				  0, sym_generic_identifier,
    				  0, sym_IDENTIFIER)) &&
    	(equal = ast_schema (n, sym_declaration,
    			     1, sym_init_declarator_list,
    			     0, sym_init_declarator,
    			     1, token_symbol ('='))))
          ast_after (equal, "NULL;if(!",
    		 ast_terminal (identifier)->start,
    		 "||i==0)",
    		 ast_terminal (identifier)->start,
    		 "=pid()>0?fopen(\"/dev/null\",\"w\"):");
        break;
      }

    Events

      case sym_event_definition: {
        if (!strcmp (ast_left_terminal (n)->start, "event")) {

    Make the name unique.

          AstTerminal * t = ast_left_terminal (n->child[1]);
          char * name = malloc (strlen (t->start) + 20),
    	* suffix = name + strlen(t->start);
          strcpy (name, t->start);
          long last = 0;
          Ast * parent = ast_identifier_declaration (stack, name);
          if (parent)
    	last = (long) ast_terminal (parent)->value;
          int i = 0;
          while (ast_identifier_declaration (stack, name))
    	snprintf (suffix, 19, "_%d", i++);

    Define the event expressions.

          char * iarray = NULL, * tarray = NULL, anexpr[20];
          AstRoot * root = ast_get_root (n);
          int nexpr = 0;
          foreach_item (n->child[3], 2, event_parameter) {
    	Ast * initializer = ast_child (event_parameter, sym_postfix_initializer);
    	if (initializer) {
    	  Ast * identifier = ast_is_identifier_expression
    	    (ast_child (event_parameter, sym_unary_expression));
    	  if (!identifier || (strcmp (ast_terminal (identifier)->start, "t") &&
    			      strcmp (ast_terminal (identifier)->start, "i"))) {
    	    AstTerminal * t = ast_left_terminal (event_parameter);
    	    fprintf (stderr,
    		     "%s:%d: error: an event list can only be used "
    		     "to set 't' or 'i'\n", t->file, t->line);
    	    exit (1);
    	  }
    	  snprintf (anexpr, 19, "%d", nexpr++);
    	  {
    	    char * expr = NULL;
    	    str_append (expr, "static int ", name, "_expr", anexpr,
    			"(int *ip,double *tp,Event *_ev)"
    			"{int i=*ip;double t=*tp;"
    			"int ret=(1);*ip=i;*tp=t;return ret;}");
    	    Ast * expr0 = ast_parse_external_declaration (expr, root);
    	    ast_set_line (expr0, ast_left_terminal (n));
    	    free (expr);
    	    ast_block_list_insert_before (n->parent->child[0], expr0->child[0]);
    	  }
    	  {
    	    char * expr = NULL;
    	    if (!strcmp (ast_terminal (identifier)->start, "t")) {
    	      str_append (tarray, name, "_array");
    	      str_append (expr, "static double ", tarray, "[]=");
    	    }
    	    else {
    	      str_append (iarray, name, "_array");
    	      str_append (expr, "static int ", iarray, "[]=");
    	    }
    	    ast_before (ast_last_child(initializer), ",-1");
    	    expr = ast_str_append (initializer, expr);
    	    str_append (expr, ";");
    	    Ast * expr0 = ast_parse_external_declaration (expr, root);
    	    ast_set_line (expr0, ast_left_terminal (n));
    	    free (expr);
    	    ast_block_list_insert_before (n->parent->child[0], expr0->child[0]);
    	  }
    	  break;
    	}
    	else {
    	  Ast * identifier;
    	  if (!ast_child (event_parameter, sym_assignment_operator) &&
    	      (identifier = ast_is_identifier_expression
    	       (ast_child (event_parameter, sym_conditional_expression))) &&
    	      (!strcmp (ast_terminal (identifier)->start, "last") ||
    	       !strcmp (ast_terminal (identifier)->start, "first"))) {
    	    if (!strcmp (ast_terminal (identifier)->start, "last"))
    	      last = 1;
    	    else
    	      last = 0;
    	  }
    	  else {
    	    snprintf (anexpr, 19, "%d", nexpr++);
    	    char * expr = NULL;
    	    str_append (expr, "static int ", name, "_expr", anexpr,
    			"(int *ip,double *tp,Event *_ev)"
    			"{int i=*ip;double t=*tp;"
    			"int ret=(");
    	    Ast * rhs = ast_child (event_parameter,
    				   sym_conditional_expression), * identifier;
    	    if (rhs && (identifier = ast_is_identifier_expression (rhs)) &&
    		!strcmp (ast_terminal (identifier)->start, "end")) {
    	      free (ast_terminal (identifier)->start);
    	      ast_terminal (identifier)->start = strdup ("TEND_EVENT");
    	    }
    	    expr = ast_str_append (event_parameter, expr);
    	    str_append (expr, ")!=0;*ip=i;*tp=t;return ret;}");
    	    Ast * expr0 = ast_parse_external_declaration (expr, root);
    	    ast_set_line (expr0, ast_left_terminal (n));
    	    free (expr);
    	    ast_block_list_insert_before (n->parent->child[0], expr0->child[0]);
    	  }
    	}
          }

    Register the event.

          char * reg = NULL;
          snprintf (anexpr, 19, "%d", nexpr);
          str_append (reg, "  event_register((Event){0,", anexpr, ",", name, ",{");
          for (int i = 0; i < nexpr; i++) {
    	snprintf (anexpr, 19, "%d", i);
    	str_append (reg, name, "_expr", anexpr, i < nexpr - 1 ? "," : "");
          }
          TranslateData * d = data;
          str_append (reg, "},",
    		  iarray ? iarray : "((int *)0)",
    		  ",",
    		  tarray ? tarray : "((double *)0)",
    		  ",",
    		  ast_file_line (t, d->nolineno), ",\"", t->start, "\"});\n");
          Ast * registration = NN(n, sym_statement,
    			      ast_parse_expression (reg, root));
          ast_set_line (registration, t);
          if (last)
    	compound_append (d->last_events, registration);
          else
    	compound_append (d->init_events, registration);
          free (reg);
          free (iarray);
          free (tarray);

    Define the action fonction.

          char * src = NULL;
          Ast * statement = ast_child (n, sym_statement);
          str_append (src,
    		  ast_schema (statement, sym_statement,
    			      0, sym_compound_statement,
    			      1, token_symbol ('}')) ||
    		  ast_schema (statement, sym_statement,
    			      0, sym_expression_statement,
    			      0, token_symbol (';'))
    		  ? "" : "trace ",
    		  "static int ", name,
    		  "(const int i,const double t,Event *_ev)"
    		  "{_statement_;return 0;}");
          Ast * def = ast_parse_external_declaration (src, root);
          Ast * identifier = ast_schema (def, sym_external_declaration,
    				     0, sym_function_definition,
    				     0, sym_function_declaration,
    				     1, sym_declarator,
    				     0, sym_direct_declarator,
    				     0, sym_direct_declarator,
    				     0, sym_generic_identifier,
    				     0, sym_IDENTIFIER);
          ast_terminal (identifier)->value = (void *) last;
          free (src);
          ast_replace (def, "_statement_", statement);
          ast_replace_child (n->parent, 0, def->child[0]);
          ast_destroy (def);
    
          free (name);      
        }
        break;
      }

    Automatic field deallocation before jump statements

      case sym_jump_statement: {
        if (n->child[0]->sym == sym_GOTO) {
          AstTerminal * t = ast_terminal (n->child[0]);
          fprintf (stderr, "%s:%d: warning: goto statements are unsafe in Basilisk "
    	       "(and are bad programming style)\n",
    	       t->file, t->line);
          break;
        }
    
        int jump_sym = n->child[0]->sym;
        Ast * parent = n;
        while (parent &&
    	   ((jump_sym == sym_BREAK &&
    	     parent->child[0]->sym != sym_SWITCH &&
    	     !ast_is_iteration_statement (parent)) ||
    	    (jump_sym == sym_CONTINUE &&
    	     !ast_is_iteration_statement (parent)) ||
    	    (jump_sym == sym_RETURN &&
    	     parent->sym != sym_function_definition &&
    	     parent->sym != sym_event_definition)))
          parent = parent->parent;
        Ast * scope = ast_find (parent, sym_compound_statement);
        if (scope) {
          char * delete[2] = {NULL};
          foreach_field_allocator (stack, data, scope, field_deallocation, delete);
          char * fields = delete_fields (delete);
          if (fields)
    	compound_jump (n, parent, fields);
          free (delete[0]);
          free (delete[1]);
        }
        break;
      }

    Boundary ids

      case sym_external_declaration: {
        Ast * identifier = ast_schema (n, sym_external_declaration,
    				   0, sym_declaration,
    				   0, sym_declaration_specifiers,
    				   0, sym_type_specifier,
    				   0, sym_types,
    				   0, sym_TYPEDEF_NAME);
        if (identifier && !strcmp (ast_terminal (identifier)->start, "bid")) {
          Ast * list = ast_schema (n, sym_external_declaration,
    			       0, sym_declaration,
    			       1, sym_init_declarator_list);
          if (list)
    	foreach_item (list, 2, item)
    	  if ((identifier = ast_schema (item, sym_init_declarator,
    					0, sym_declarator,
    					0, sym_direct_declarator,
    					0, sym_generic_identifier,
    					0, sym_IDENTIFIER))) {
    	    Ast * init =
    	      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,
    				   ast_copy (identifier)))),
    			  NN(n, sym_assignment_operator,
    			     NCA(n, "=")),
    			  ast_new_assignment_function_call (n, "new_bid"))),
    		    NCA(n, ";")));
    	    TranslateData * d = data;
    	    compound_append (d->init_fields, init);
    	  }
        }
        break;
      }
    
      }

    Automatic field allocation and deallocation

      if (n->sym == token_symbol('}') && n->parent->sym == sym_compound_statement) {
        char * delete[2] = {NULL};
        foreach_field_allocator (stack, data, n->parent, field_allocation, delete);

    Field deallocation

        char * fields = delete_fields (delete);
        if (fields) {
          Ast * expr = ast_parse_expression (fields, ast_get_root (n));
          ast_block_list_append (ast_child (n->parent, sym_block_item_list),
    			     sym_block_item,
    			     ast_new_children (ast_new (n, sym_statement),
    					       expr));
        }
        free (delete[0]);
        free (delete[1]);
      }
    }
    
    static void trace_return (Ast * n, Stack * stack, void * data)
    {
      Ast * function_definition = ((void **)data)[0];
      AstTerminal * function_identifier = ((void **)data)[1];
      if (ast_schema (n, sym_jump_statement, 0, sym_RETURN)) {
        char * end_tracing = NULL;
        TranslateData * d = ((void **)data)[2];
        str_append (end_tracing,
    		"end_tracing(\"", function_identifier->start, "\",",
    		ast_file_line (n->child[0], d->nolineno), ");");
        compound_jump (n, function_definition, end_tracing);
        free (end_tracing);
      }
    }
    
    static const char * get_field_type (Ast * declaration, AstTerminal * t)
    {
      if (declaration)
        declaration = ast_declaration_from_type (declaration);
      const char * typename = NULL;
      if (!declaration ||
          !(typename = typedef_name_from_declaration (declaration)) ||
          (strcmp (typename, "scalar") &&
           strcmp (typename, "vector") &&
           strcmp (typename, "tensor"))) {
        fprintf (stderr,
    	     "%s:%d: error: '%s' is not a scalar, vector or tensor\n",
    	     t->file, t->line, t->start);
        exit (1);
      }
      return typename;
    }
    
    static void mpi_operator (Ast * n, Ast * op)
    {
      char * operator = ast_left_terminal (op)->start;
      ast_after (n,
    	     !strcmp(operator, "min") ? "MPI_MIN" :
    	     !strcmp(operator, "max") ? "MPI_MAX" :
    	     !strcmp(operator, "+")   ? "MPI_SUM" :
    	     !strcmp(operator, "||")  ? "MPI_LOR" :
    	     "Unknown", ",");
    }
    
    static void maps (Ast * n, Stack * stack, void * data)
    {
      if (n->sym == sym_foreach_statement && !ast_is_foreach_stencil (n)) {
        if (!strcmp (ast_terminal (n->child[0])->start, "foreach_face")) {
          free (ast_terminal (n->child[0])->start);
          ast_terminal (n->child[0])->start = strdup ("foreach_face_generic");
        }
        else { // maps for !foreach_face() loops
          foreach_map (m) {
    	Ast * list = ast_block_list_get_item (ast_child (n, sym_statement))->parent;
    	foreach_item (m, 1, item)
    	  ast_block_list_prepend (list, sym_block_item, ast_copy (item->child[0]));
          }
        }    
      }
    }

    Third pass: foreach stencils

    static void stencils (Ast * n, Stack * stack, void * data)
    {
      switch (n->sym) {
        
      case sym_foreach_statement:
        assert (ast_last_child(n)->sym == sym_statement); // make sure all stencils have been dealt with
        if (ast_is_foreach_stencil (n)) {
          Ast * foreach = n->parent;
          if (foreach->sym == sym_foreach_statement && ast_last_child (foreach) == n) {
    	Ast ** c;
    	for (c = foreach->child; *c; c++);
    	*(--c) = NULL;
    	Ast * statement = foreach->parent->parent;
    	Ast * item = ast_block_list_get_item (statement), * list = item->parent;
    	list = ast_block_list_append
    	  (list, item->sym,
    	   ast_new_children (ast_new (foreach, sym_statement),
    			     ast_new_children (ast_new (foreach,
    							sym_basilisk_statements),
    					       n)));
    	ast_set_child (item, 0, list->child[1]->child[0]);
    	ast_set_child (list->child[1], 0, statement);
          }
    
          Ast * parameters = ast_child (n, sym_foreach_parameters);
          if (parameters) {
    	ast_destroy (parameters);
    	for (Ast ** c = n->child + 2; *c; c++)
    	  *c = *(c + 1);
          }

    Kernel for GPUs

          Ast * params = ast_schema (n, sym_foreach_statement,
    				 1, token_symbol('('));
          assert (params);
          parameters = ast_child (foreach, sym_foreach_parameters);
          bool gpu = !((TranslateData *)data)->cpu;
          int parallel = (gpu ?
    		      1 :  // parallel on CPU || GPU
    		      2 ); // parallel on CPU
          if (parameters)
    	foreach_item (parameters, 2, item) {
    	  Ast * identifier = ast_is_identifier_expression (item->child[0]);
    	  if (identifier) {
    	    AstTerminal * t = ast_terminal (identifier);
    	    if (!strcmp (t->start, "serial"))
    	      parallel = 0, gpu = false;
    	    else if (!strcmp (t->start, "cpu"))
    	      parallel = 2, gpu = false;
    	    else if (!strcmp (t->start, "gpu"))
    	      parallel = 3, gpu = true;
    	  }
    	}
    
          ast_after (params,
    		 parallel == 0 ? "0" : parallel == 1 ? "1" : parallel == 2 ? "2" : "3",
    		 ",{(NonLocal[])");
          ast_non_local_references (foreach, params);
          if (gpu) {
    	ast_after (params, ",");
    	ast_kernel (foreach, params);
    	ast_after (params, "}");
          }
          else
    	ast_after (params, ",NULL}");
        }
    
        break;

    Foreach inner statements

      case sym_foreach_inner_statement: {
        AstTerminal * t = ast_left_terminal(n);
        Ast * foreach = inforeach (n);
        if (foreach) {
          if (!strcmp (t->start, "foreach_block"))
    	str_append (t->start, "_inner");
        }
        ast_before (n, "{");
        ast_after (n, "end_", t->start, "()}");
        break;
      }
        
      case sym_macro_statement: {
        Ast * identifier = ast_schema (n, sym_macro_statement,
    				   0, sym_function_call,
    				   0, sym_postfix_expression,
    				   0, sym_primary_expression,
    				   0, sym_IDENTIFIER);
        if (identifier) {
          AstTerminal * t = ast_terminal (identifier);

    is_face_… statements

          if (!strcmp (t->start, "is_face_x") ||
    	  !strcmp (t->start, "is_face_y") ||
    	  !strcmp (t->start, "is_face_z")) {
    	foreach_map (m) {
    	  Ast * list = ast_schema (n, sym_macro_statement,
    				   1, sym_compound_statement,
    				   1, sym_block_item_list);
    	  foreach_item (m, 1, item)
    	    ast_block_list_prepend (list, sym_block_item, ast_copy (item->child[0]));
    	}
    	ast_after (n, "end_", t->start, "()");
          }

    stencil_is_face… statements

          else if (!strcmp (t->start, "_stencil_is_face_x") ||
    	       !strcmp (t->start, "_stencil_is_face_y") ||
    	       !strcmp (t->start, "_stencil_is_face_z"))
    	ast_after (n, "end_", t->start, "()");

    Map

          else if (!strcmp (ast_terminal (identifier)->start, "map")) {
    	Ast * item = n->parent;
    	if (item->sym == sym_external_declaration) {
    	  assert (ast_child_index (item) == 1);
    	  Ast * parent = item->parent, * grand_parent = parent->parent;
    	  ast_set_child (grand_parent, ast_child_index (parent),
    			 parent->child[0]);
    	}
          }
        }
        break;
      }

    Point point

      case sym_IDENTIFIER: {
        Ast * decl = ast_is_point_point (n);
        if (decl) {
          AstTerminal * t = ast_terminal (n);
          if (ast_parent (n, sym_declaration) &&
    	  strncmp (t->file, BASILISK "/grid/", strlen (BASILISK "/grid/")) &&
    	  strncmp (t->file, "./grid/", strlen ("./grid/")))
    	fprintf (stderr,
    		 "%s:%d: warning: 'Point point' is obsolete, use 'foreach_point/region' instead\n",
    		 t->file, t->line);
          TranslateData * d = data;
          static const char * name[3] = {"ig", "jg", "kg"};
          for (int i = 0; i < d->dimension; i++)
    	ast_after (decl, "int ", name[i], "=0;"
    		   "NOT_UNUSED(", name[i], ");");
          str_append (ast_right_terminal (decl)->after, "POINT_VARIABLES;");
          if (decl->sym == sym_declaration) {
    	foreach_map (m) {
    	  Ast * list = ast_block_list_get_item (decl)->parent;
    	  foreach_item (m, 1, item)
    	    ast_block_list_append (list, sym_block_item, ast_copy (item->child[0]));
    	}
          }
          else {
    	Ast * list = ast_schema (decl->parent, sym_compound_statement,
    				 1, sym_block_item_list);
    	foreach_map (m)
    	  foreach_item (m, 1, item)
    	    ast_block_list_prepend (list, sym_block_item, ast_copy (item->child[0]));
          }
        }
        break;
      }

    Hide Basilisk C keywords

      case sym_MAYBECONST: ast_hide (ast_terminal (n)); break;
      case sym_TYPEDEF_NAME: {
        AstTerminal * t = ast_terminal (n);
        if (!strcmp (t->start, "face vector") ||
    	!strcmp (t->start, "vertex scalar") ||
    	!strcmp (t->start, "symmetric tensor")) {
          char * s = strchr (t->start, ' ') + 1;
          memmove (t->start, s, strlen (s) + 1);
        }
        break;
      }

    Remove ’_val_higher_dimension’ statements with no effect (to avoid compiler warnings)

      case sym_expression_statement: {
        Ast * id;
        if ((id = ast_is_identifier_expression (ast_schema (n, sym_expression_statement,
    							0, sym_expression,
    							0, sym_assignment_expression))) &&      
    	!strcmp (ast_terminal (id)->start, "_val_higher_dimension")) {
          ast_destroy (n->child[0]);
          n->child[0] = n->child[1]; n->child[1] = NULL;
        }
        break;
      }
        
      }
    }
    
    static char * get_type (const char * name, Stack * stack)
    {
      Ast * decl = ast_find (ast_declaration_from_type (ast_identifier_declaration (stack, name)),
    			 sym_declaration_specifiers);
      if (!decl) return NULL;
      AstTerminal * t = ast_left_terminal (decl);
      char * before = t->before;
      t->before = NULL;
      char * type = ast_str_append (decl, NULL);
      t->before = before;
      return type;
    }

    Fourth pass: “macro” expressions

    This pass should regroup all transformations which require the use of macros which are not included in the Basilisk C grammar.

    static void macros (Ast * n, Stack * stack, void * data)
    {
      switch (n->sym) {

    Stencil access function calls

      case sym_function_call: {
        Ast * identifier = ast_function_call_identifier (n);
        if (identifier) {
          AstTerminal * t = ast_terminal (identifier);
          Ast * foreach = NULL;
          if (stencil_access_function (t->start) &&
    	  (((foreach = inforeach (n)) && ast_is_foreach_stencil (foreach)) ||
    	   in_stencil_point_function (n)))
    	str_prepend (t->start, "_stencil_");
        }
        break;
      }
    
      case sym_foreach_statement: {
    
        assert (ast_last_child(n)->sym == sym_statement); // make sure all stencils have been dealt with

    Foreach stencils

        if (ast_is_foreach_stencil (n)) {
          ast_after (n, "end_", ast_left_terminal(n)->start, "();");
          break;
        }

    Foreach statements

        Ast * foreach = inforeach (n);
        if (foreach) {
          AstTerminal * t = ast_terminal (n->child[0]);
          AstTerminal * p = ast_terminal (foreach->child[0]);
          fprintf (stderr,
    	       "%s:%d: error: this %s cannot include\n", p->file, p->line,
    	       foreach->sym == sym_foreach_statement ?
    	       ast_is_foreach_stencil (foreach) ?
    	       "'Point point' scope" :
    	       "foreach*() iterator" :
    	       "point function");
          fprintf (stderr,
    	       "%s:%d: error: this %s\n", t->file, t->line,
    	       n->sym == sym_foreach_statement ?
    	       ast_is_foreach_stencil (n) ?
    	       "'Point point' scope" :
    	       "foreach*() iterator" :
    	       "point function");
          exit (1);
        }
            
        ast_after (n, "end_", ast_left_terminal(n)->start, "();");

    Reductions

        Ast * parameters = ast_child (n, sym_foreach_parameters);
        bool serial = false;
        char * sreductions = NULL;
        if (parameters) {
          foreach_item (parameters, 2, item) {
    	Ast * identifier = ast_is_identifier_expression (item->child[0]);
    	if (identifier && !strcmp (ast_terminal (identifier)->start, "serial")) {
    	  serial = true;
    	  parameters = ast_list_remove (parameters, item);
    	}
    	else if (identifier && (!strcmp (ast_terminal (identifier)->start, "cpu") ||
    				!strcmp (ast_terminal (identifier)->start, "gpu")))
    	  parameters = ast_list_remove (parameters, item);
    	else if (item->child[0]->sym == sym_reduction_list) {
    	  Ast * reductions = item->child[0];
    	  foreach_item (reductions, 1, reduction) {
    	    Ast * identifier = ast_schema (reduction, sym_reduction,
    					   4, sym_reduction_array,
    					   0, sym_generic_identifier,
    					   0, sym_IDENTIFIER);
    	    AstTerminal * t = ast_terminal (identifier);
    	    Ast * array = ast_schema (reduction, sym_reduction,
    				      4, sym_reduction_array,
    				      3, sym_expression);
    	    char * type = get_type (t->start, stack);
    	    if (!type) {
    	      fprintf (stderr,
    		       "%s:%d: error: cannot determine type of '%s'\n",
    		       t->file, t->line, t->start);
    	      exit (1);
    	    }
    	    if (strcmp (type, "coord") &&
    		strcmp (type, "double") &&
    		strcmp (type, "int") &&
    		strcmp (type, "long") &&
    		strcmp (type, "bool") &&
    		strcmp (type, "unsigned char")) {
    	      fprintf (stderr,
    		       "%s:%d: error: does not know how to reduce "
    		       "type '%s' of '%s'\n",
    		       t->file, t->line, type, t->start);
    	      exit (1);
    	    }
    	    if (array) {
    	      ast_after (n, "mpi_all_reduce_array(", t->start, ",", type, ",");
    	      mpi_operator (n, reduction->child[2]);
    	      ast_right_terminal (n)->after = ast_str_append (array, ast_right_terminal (n)->after);
    	      ast_after (n, ");");
    	    }
    	    else {
    	      char s[20] = "1";
    	      ast_after (n, "mpi_all_reduce_array(&", t->start);
    	      if (strcmp (type, "coord"))
    		ast_after (n, ",", type);
    	      else {
    		TranslateData * d = data;
    		snprintf (s, 19, "%d", d->dimension);
    		ast_after (n, ".x,double");
    	      }
    	      ast_after (n, ",");
    	      mpi_operator (n, reduction->child[2]);
    	      ast_after (n, s, ");");
    	    }
    	    sreductions = ast_str_append (reduction, sreductions);
    	    free (type);
    	  }
    	  parameters = ast_list_remove (parameters, item);
    	}
          }
          if (parameters == NULL) {
    	ast_destroy (n->child[2]);
    	for (Ast ** c = n->child + 2; *c; c++)
    	  *c = *(c + 1);
          }
        }
    
        if (serial)
          ast_before (n, "\n"
    		  "#if _OPENMP\n"
    		  "  #undef OMP\n"
    		  "  #define OMP(x)\n"
    		  "#endif\n");
        if (sreductions) {
          ast_before (n, "\n"
    		  "#undef OMP_PARALLEL\n"
    		  "#define OMP_PARALLEL()\n"
    		  "OMP(omp parallel ", sreductions, "){");
          ast_after (n,
    		 "\n"
    		 "#undef OMP_PARALLEL\n"
    		 "#define OMP_PARALLEL() OMP(omp parallel)\n}");
          free (sreductions);
        }
        else {
          ast_before (n, "{");
          ast_after (n, "}");
        }
        if (serial)
          ast_after (n, "\n"
    		 "#if _OPENMP\n"
    		 "  #undef OMP\n"
    		 "  #define OMP(x) _Pragma(#x)\n"
    		 "#endif\n");
        
        break;
      }

    forin_declaration_statement

      case sym_forin_declaration_statement: {
        Ast * declarator = n->child[3];
        Ast * identifier = ast_schema (declarator, sym_declarator,
    				   0, sym_direct_declarator,
    				   0, sym_generic_identifier,
    				   0, sym_IDENTIFIER);
        if (!identifier) {
          AstTerminal * t = ast_left_terminal (n);
          fprintf (stderr,
    	       "%s:%d: error: incorrect declaration\n",
    	       t->file, t->line);
          exit (1);
        }
        const char * typename =
          get_field_type (n->child[2], ast_terminal(identifier));
        char * src = NULL, * name = ast_terminal(identifier)->start;
        str_append (src, "{", typename, "*_i=(", typename, "*)(list);if(_i)"
    		"for(", typename, " ", name, "=*_i;(&",
    		name,
    		!strcmp (typename, "scalar") ? ")->i" :
    		!strcmp (typename, "vector") ? ")->x.i" :
    		")->x.x.i",
    		">=0;", name, "=*++_i){_statement_;}}");
        Ast * expr = ast_parse_expression (src, ast_get_root (n));
        free (src);
        Ast * parent = n->parent;
        Ast * arg = ast_child (n, sym_forin_arguments)->child[0];
        if (arg->sym == sym_expression) {
          Ast * initializer = ast_find (expr, sym_expression_error);
          ast_replace_child (initializer, 0, arg);
        }
        else {
          arg = ast_find (arg, sym_postfix_initializer);
          Ast * initializer = ast_find (expr, sym_cast_expression);
          ast_replace_child (initializer, 3, arg);
          initializer->sym = sym_postfix_expression;
          Ast * parent = initializer->parent;
          int index = ast_child_index (initializer);
          Ast * unary = ast_new_children (ast_new (n, sym_unary_expression),
    				      initializer);
          Ast * cast = ast_new_children (ast_new (n, sym_cast_expression),
    				     unary);
          char * before = ast_left_terminal (arg)->before;
          ast_replace_child (parent, index, cast);
          ast_left_terminal (arg)->before = before;
          ast_left_terminal (parent->child[index])->before = NULL;
        }
        assert (ast_replace (expr, "_statement_", ast_child (n, sym_statement)));
        ast_replace_child (parent->parent, ast_child_index (parent), expr);
        break;
      }

    forin_statement

      case sym_forin_statement: {
        Ast * arg = ast_child (n, sym_forin_arguments)->child[0];
        char * decl = strdup ("{"), * fors = strdup ("if(_i0)for("), * fore = NULL;
        int index = 0;
        foreach_item (n->child[2], 2, expr) {
          Ast * identifier = ast_is_identifier_expression (expr);
          if (!identifier) {
    	AstTerminal * t = ast_left_terminal (expr);
    	fprintf (stderr,
    		 "%s:%d: error: not a scalar, vector or tensor\n",
    		 t->file, t->line);
    	exit (1);
          }
          AstTerminal * t = ast_terminal (identifier);
          const char * typename =
    	get_field_type (ast_identifier_declaration (stack, t->start), t);
          if (!arg) {
    	fprintf (stderr,
    		 "%s:%d: error: lists must have the same size\n",
    		 t->file, t->line);
    	exit (1);
          }
          Ast * l;
          if (arg->sym == sym_postfix_initializer || !arg->child[1]) {
    	l = arg;
    	arg = NULL;
          }
          else {
    	l = arg->child[2];
    	arg = arg->child[0];
          }
          char ind[20];
          snprintf (ind, 19, "%d", index);
          str_append (decl, typename, "*_i", ind, "=");
          decl = ast_str_append (l, decl);
          str_append (decl, ";");
          str_append (fors, index > 0 ? "," : "", t->start, "=*_i", ind);
          if (!fore)
    	str_append (fore, "_i", ind,
    		    !strcmp (typename, "scalar") ? "->i" :
    		    !strcmp (typename, "vector") ? "->x.i" :
    		    "->x.x.i",
    		    ">= 0;");
          str_append (fore, index > 0 ? "," : "", t->start, "=*++_i", ind);
          index++;
        }
        str_append (decl, fors, ";", fore, "){_statement_;}}");
        Ast * expr = ast_parse_expression (decl, ast_get_root (n));
        free (decl); free (fors); free (fore);
        assert (ast_replace (expr, "_statement_", ast_child (n, sym_statement)));
        ast_replace_child (n->parent->parent, ast_child_index (n->parent), expr);
        break;
      }

    Attribute access

      case sym_postfix_expression: {
        if (n->child[1] && n->child[1]->sym == token_symbol('.')) {
          const char * typename =
    	ast_typedef_name (ast_expression_type (n->child[0], stack, false));
          if (typename && (!strcmp (typename, "scalar") ||
    		       !strcmp (typename, "vertex scalar"))) {
    	Ast * member = ast_find (n->child[2], sym_member_identifier,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER);
    	Ast * type = ast_identifier_declaration (stack, "scalar");
    	assert (type);
    	while (type->sym != sym_declaration)
    	  type = type->parent;
    	if (!find_struct_member (ast_find (type, sym_struct_declaration_list),
    				 ast_terminal (member)->start)) {
    	  Ast * expr = ast_parse_expression ("_attribute[_field_.i];",
    					     ast_get_root (n));
    	  ast_replace (expr, "_field_", n->child[0]);
    	  ast_replace_child (n, 0, ast_find (expr, sym_postfix_expression));
    	  ast_destroy (expr);
    	}
          }

    Boundary vector component access

          else if (typename && (!strcmp (typename, "vector") ||
    			    !strcmp (typename, "face vector")))
    	set_boundary_component (ast_find (n->child[2], sym_member_identifier));
        }
        break;
      }

    Field lists

      case sym_postfix_initializer: {

    Do not consider lists explicitly cast as structures.

        if (n->parent->sym == sym_postfix_expression &&
    	ast_child_index (n) == 3 &&
    	ast_schema (n->parent->child[1], sym_type_name,
    		    0, sym_specifier_qualifier_list,
    		    0, sym_type_specifier,
    		    0, sym_types,
    		    0, sym_struct_or_union_specifier,
    		    0, sym_struct_or_union,
    		    0, sym_STRUCT))
          break;
        // else fall through
      }
    
      case sym_initializer: {
        if (n->child[1] && n->child[2]) {
          Ast * list = n->child[1];
          int type = field_list_type (list, stack, false);
          if (type > 0) {

    External/global lists

    	bool external = true;
    	Ast * scope = n->parent;
    	while (external && scope) {
    	  if (scope->sym == sym_compound_statement)
    	    external = false;
    	  scope = scope->parent;
    	}
    	if (external) {
    	  char * src = NULL;
    	  foreach_item (list, 2, expr) {
    	    const char * typename =
    	      ast_typedef_name (ast_expression_type (expr, stack, false));
    	    assert (typename);
    	    Ast * unary = ast_is_unary_expression (expr->child[0]);
    	    if (!unary) {
    	      AstTerminal * t = ast_terminal (expr);
    	      fprintf (stderr,
    		       "%s:%d: error: global lists can only be initialized "
    		       "with simple expressions\n", t->file, t->line);
    	      exit (1);
    	    }
    	    int stype = 1; // identifier
    	    Ast * identifier = ast_schema (unary, sym_unary_expression,
    					   0, sym_postfix_expression,
    					   0, sym_primary_expression,
    					   0, sym_IDENTIFIER);
    	    if (!identifier) {
    	      stype = 2;  // identifier.x
    	      identifier = ast_schema (unary, sym_unary_expression,
    				       0, sym_postfix_expression,
    				       0, sym_postfix_expression,
    				       0, sym_primary_expression,
    				       0, sym_IDENTIFIER);
    	    }
    	    if (!identifier) {
    	      stype = 3;  // identifier.x.x
    	      identifier = ast_schema (unary, sym_unary_expression,
    				       0, sym_postfix_expression,
    				       0, sym_postfix_expression,
    				       0, sym_postfix_expression,
    				       0, sym_primary_expression,
    				       0, sym_IDENTIFIER);
    	    }
    	    if (identifier) {
    	      AstTerminal * t = ast_terminal (identifier);
    	      Ast * declaration = ast_identifier_declaration (stack, t->start);
    	      const char * typename1 = get_field_type (declaration, t);
    	      if (!ast_terminal (declaration)->value) {
    		fprintf (stderr,
    			 "%s:%d: error: variable `%s' is not initialized\n",
    			 t->file, t->line, t->start);
    		exit (1);			
    	      }
    	      Field c = {
    		.identifier = NULL,
    		.type = (!strcmp (typename1, "scalar") ? 1 :
    			 !strcmp (typename1, "vector") ? 2 : 3),
    		.index = ((long) ast_terminal (declaration)->value) - 1,
    		.dimension = ((TranslateData *)data)->dimension
    	      };
    	      if (stype > 1) { // .x or .x.x
    		Ast * member = ast_schema (unary, sym_unary_expression,
    					   0, sym_postfix_expression,
    					   2, sym_member_identifier,
    					   0, sym_generic_identifier,
    					   0, sym_IDENTIFIER);
    		if (member) {
    		  if (stype == 2) { // .x
    		    c.index += (ast_terminal(member)->start[0] - 'x')*
    		      (c.type == 3 ? c.dimension : 1);
    		    if (type == 1 && c.type == 3)
    		      c.type = 2;
    		    else
    		      c.type = type;
    		  }
    		  else if (stype == 3) { // .x.x
    		    Ast * member1 = ast_schema (unary, sym_unary_expression,
    						0, sym_postfix_expression,
    						0, sym_postfix_expression,
    						2, sym_member_identifier,
    						0, sym_generic_identifier,
    						0, sym_IDENTIFIER);
    		    if (member1) {
    		      c.index += (ast_terminal(member)->start[0] - 'x') +
    			(ast_terminal(member1)->start[0] - 'x')*c.dimension;
    		      c.type = 1;
    		      ast_terminal(member1)->start[0] = '\0';
    		      Ast * dot = ast_schema (unary, sym_unary_expression,
    					      0, sym_postfix_expression,
    					      0, sym_postfix_expression,
    					      1, token_symbol ('.'));
    		      ast_terminal(dot)->start[0] = '\0';
    		    }
    		  }
    		  ast_terminal(member)->start[0] = '\0';
    		  Ast * dot = ast_schema (unary, sym_unary_expression,
    					  0, sym_postfix_expression,
    					  1, token_symbol ('.'));
    		  ast_terminal(dot)->start[0] = '\0';
    		}
    	      }
    	      str_prepend (src, field_value (&c, "", type), src ? "," : "");
    	    }
    	  }
    	  if (src) {
    	    str_prepend (src, "int a = {");
    	    str_append (src, "};");
    	    Ast * expr = ast_parse_expression (src, ast_get_root (n));
    	    free (src);
    	    Ast * initializer = ast_find (expr, sym_initializer);
    	    ast_replace_child (n->parent, ast_child_index (n), initializer);
    	    ast_destroy (expr);
    	    n = initializer;
    	  }
    	}

    Local lists

    	else
    	  foreach_item (list, 2, expr) {
    	    const char * typename =
    	      ast_typedef_name (ast_expression_type (expr, stack, false));
    	    if ((type == 1 && !strcmp (typename, "vector")) ||
    		(type == 2 && !strcmp (typename, "tensor"))) {
    	      Ast * unary = ast_find (expr, sym_unary_expression);
    	      ast_set_child (unary, 0,
    			     NN(expr, sym_postfix_expression,
    				unary->child[0],
    				NCA(expr, "."),
    				NN(expr, sym_member_identifier,
    				   NN(expr, sym_generic_identifier,
    				      NA(expr, sym_IDENTIFIER, "x")))));
    	      Ast * a = expr->child[0];
    	      TranslateData * d = data;
    	      for (int i = 1; i < d->dimension; i++) {
    		Ast * b = ast_copy (a);
    		Ast * id = ast_find (b, sym_member_identifier,
    				     0, sym_generic_identifier,
    				     0, sym_IDENTIFIER);
    		ast_terminal (id)->start[0] = 'x' + i;
    		ast_list_insert_after (a, b);
    		a = b;
    	      }
    	    }
    	    else if (type == 1 && !strcmp (typename, "tensor")) {
    	      Ast * unary = ast_find (expr, sym_unary_expression);
    	      ast_set_child (unary, 0,
    			     NN(expr, sym_postfix_expression,
    				NN(expr, sym_postfix_expression,
    				   unary->child[0],
    				   NCA(expr, "."),
    				   NN(expr, sym_member_identifier,
    				      NN(expr, sym_generic_identifier,
    					 NA(expr, sym_IDENTIFIER, "x")))),
    				NCA(expr, "."),
    				NN(expr, sym_member_identifier,
    				   NN(expr, sym_generic_identifier,
    				      NA(expr, sym_IDENTIFIER, "x")))));
    	      Ast * a = expr->child[0];
    	      TranslateData * d = data;
    	      for (int i = 0; i < d->dimension; i++)
    		for (int j = 0; j < d->dimension; j++)
    		  if (i || j) {
    		    Ast * b = ast_copy (a);
    		    Ast * i1 = ast_find (b, sym_postfix_expression,
    					 0, sym_postfix_expression,
    					 2, sym_member_identifier,
    					 0, sym_generic_identifier,
    					 0, sym_IDENTIFIER);
    		    ast_terminal (i1)->start[0] = 'x' + i;
    		    Ast * i2 = ast_find (b, sym_postfix_expression,
    					 2, sym_member_identifier,
    					 0, sym_generic_identifier,
    					 0, sym_IDENTIFIER);
    		    ast_terminal (i2)->start[0] = 'x' + j;
    		    ast_list_insert_after (a, b);
    		    a = b;
    		  }
    	    }
    	  }

    Finalize both global and local lists

    	if (type == 1) // scalar
    	  ast_list_append (n->child[1], sym_initializer, ast_new_empty_scalar (n));
    	else if (type == 2) { // vector
    	  TranslateData * d = data;
    	  ast_list_append (n->child[1], sym_initializer, ast_new_empty_vector (n, d->dimension));
    	}
    	else { // tensor
    	  TranslateData * d = data;
    	  ast_list_append (n->child[1], sym_initializer, ast_new_empty_tensor (n, d->dimension));
    	}
    
    	Ast * type_name = NN(n, sym_type_name,
    			     NN(n, sym_specifier_qualifier_list,
    				NN(n, sym_type_specifier,
    				   NN(n, sym_types,
    				      NA(n, sym_TYPEDEF_NAME,
    					 (type == 1 ? "scalar" : type == 2 ? "vector" : "tensor"))))),
    			     NN(n, sym_abstract_declarator,
    				NN(n, sym_direct_abstract_declarator,
    				   NCA(n, "["), NCA(n, "]"))));
    	int index = ast_child_index (n);
    	Ast * parent = n->parent;
    	Ast * unary = NN(parent, sym_unary_expression,
    			 NN(parent, sym_postfix_expression,
    			    NN(parent, sym_primary_expression,
    			       NCA(n, "("),
    			       NN(parent, sym_expression_error,
    				  NN(parent, sym_expression,
    				     ast_attach (ast_new_unary_expression (parent),
    						 NN(parent, sym_postfix_expression,
    						    NCA(n, "("), type_name, NCA(n, ")"),
    						    n)))),
    			       NCA(n, ")"))));
    	n->sym = sym_postfix_initializer;
    	if (parent->sym == sym_forin_arguments)
    	  ast_set_child (parent, index,
    			 NN(parent, sym_expression,
    			    ast_attach (ast_new_cast_expression (parent), unary)));
    	else if (parent->sym == sym_initializer_list ||
    		 parent->sym == sym_init_declarator)
    	  ast_set_child (parent, index,
    			 NN(parent, sym_initializer,
    			    ast_attach (ast_new_cast_expression (parent), unary)));
    	else if (parent->sym == sym_postfix_expression) {
    	  assert (index == 3);
    	  ast_set_child (parent, 3,
    			 NN(parent, sym_cast_expression, unary));
    	  Ast * ancestor = ast_ancestor (parent, 2);
    	  if (ancestor->sym == sym_cast_expression)
    	    ast_set_child (ancestor->parent, ast_child_index (ancestor), parent);
    	  else
    	    ast_set_child (ast_ancestor (parent, 3), 0, parent);
    	  parent->sym = sym_cast_expression;
    	}
    	else
    	  ast_set_child (parent, index,
    			 ast_attach (ast_new_cast_expression (parent), unary));
          }
        }
        break;
      }
    
      case sym_function_definition: {

    Function profiling with trace

        Ast * trace = ast_schema (n, sym_function_definition,
    			      0, sym_function_declaration,
    			      0, sym_declaration_specifiers,
    			      0, sym_storage_class_specifier,
    			      0, sym_TRACE);
        if (trace) {
          TranslateData * d = data;
          ast_hide (ast_terminal (trace));      
          Ast * identifier = ast_find (n, sym_direct_declarator,
    				   0, sym_generic_identifier,
    				   0, sym_IDENTIFIER);
          Ast * compound_statement = ast_child (n, sym_compound_statement);
          ast_after (compound_statement->child[0],
    		 "tracing(\"", ast_terminal (identifier)->start, "\",",
    		 ast_file_line (identifier, d->nolineno), ");");
          Ast * end = ast_child (compound_statement, token_symbol ('}'));
          ast_before (end,
    		  "end_tracing(\"", ast_terminal (identifier)->start, "\",",
    		  ast_file_line (end, d->nolineno), ");");
          if (compound_statement->child[1]->sym == sym_block_item_list) {
    	void * adata[] = { n, identifier, data };
    	ast_traverse (compound_statement, stack, trace_return, adata);
          }
        }

    Solver initialization and termination.

        Ast * identifier = ast_function_identifier (n);
        char * init;
        if (identifier && !strcmp (ast_terminal (identifier)->start, "main")) {
          Ast * compound_statement = ast_child (n, sym_compound_statement);
          compound_prepend (compound_statement, ast_new_function_call (n, "_init_solver"));
          compound_append (compound_statement, ast_new_function_call (n, "free_solver"));
        }
        else if (identifier && ast_left_terminal (n)->before &&
    	     (init = strstr (ast_left_terminal (n)->before, "@init_solver"))) {
          for (int i = 0; i < 12; i++)
    	init[i] = ' ';
          TranslateData * d = data;
          compound_prepend (d->last_events, ast_new_function_call (n, ast_terminal (identifier)->start));
        }
        break;
      }
    
      }
    }

    Traversal functions

    These functions traverse the tree while maintaining a stack of declared symbols.

    void ast_push_declaration (Stack * stack, Ast * n)
    {
      if (n == ast_placeholder)
        return;
      if (n->sym == sym_parameter_type_list ||
          n->sym == sym_struct_declaration_list)
        return; // skip function arguments and struct members
      Ast * identifier = ast_schema (n, sym_direct_declarator,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER);
      if (!identifier)
        identifier = ast_schema (n, sym_enumeration_constant,
    			     0, sym_IDENTIFIER);
      if (!identifier && n->sym == sym_struct_or_union_specifier &&
          n->child[2])
        identifier = ast_schema (n, sym_struct_or_union_specifier,
    			     1, sym_generic_identifier,
    			     0, sym_IDENTIFIER);
      if (identifier)
        stack_push (stack, &identifier);
      if (n->child)
        for (Ast ** c = n->child; *c; c++)
          ast_push_declaration (stack, *c);
    }
    
    void ast_pop_scope (Stack * stack, Ast * scope)
    {
      if (!scope)
        return;
      while (*((Ast **)stack_pop (stack)) != scope);
    }
    
    Ast * ast_push_function_definition (Stack * stack, Ast * declarator)
    {
      Ast * identifier = ast_find (declarator, sym_direct_declarator,
    			       0, sym_generic_identifier,
    			       0, sym_IDENTIFIER);
      stack_push (stack, &identifier);
      stack_push (stack, &declarator);
      Ast * parameters = ast_find (declarator, sym_parameter_list);
      if (parameters)
        ast_push_declaration (stack, parameters);
      return identifier;
    }
    
    static void declare_point_variables (Stack * stack)
    {
      Ast * list = ast_find (ast_parent (ast_identifier_declaration (stack, "_Variables"),
    				     sym_function_definition),
    			 sym_block_item_list);
      assert (list);
      foreach_item (list, 1, item) {
        Ast * declaration = ast_schema (item, sym_block_item,
    				    0, sym_declaration);
        ast_push_declaration (stack, declaration);
      }
    }
    
    Ast * ast_push_declarations (Ast * n, Stack * stack)
    {
      switch (n->sym) {

    These should match the corresponding action/mid-action rules in basilisk.yacc.

      case sym_function_definition: {
        Ast * declarator = ast_find (n, sym_direct_declarator);
        ast_push_function_definition (stack, declarator);
        if (ast_is_point_function (ast_schema (n, sym_function_definition,
    					   0, sym_function_declaration,
    					   1, sym_declarator)) &&
    	!ast_is_stencil_function (n))
          declare_point_variables (stack);
        return declarator;
      }
        
      case sym_compound_statement:
      case sym_for_declaration_statement:
        stack_push (stack, &n);
        return n;
        
      case sym_forin_declaration_statement:
        stack_push (stack, &n);
        ast_push_declaration (stack, n->child[3]);
        return n;
    
      case sym_foreach_statement:
        stack_push (stack, &n);
        declare_point_variables (stack);
        return n;
    
      case sym_macro_statement: {
        Ast * identifier = ast_schema (n, sym_macro_statement,
    				   0, sym_function_call,
    				   0, sym_postfix_expression,
    				   0, sym_primary_expression,
    				   0, sym_IDENTIFIER);
        if (!strcmp (ast_terminal (identifier)->start, "map"))
          stack_push (stack, &n);
        return NULL;
      }
    
      case sym_declaration:
        ast_push_declaration (stack, n);
        return NULL;

    Local boundary conditions

      case sym_assignment_expression: {
        Ast * array;
        if (n->child[1] && function_scope (n, stack) &&
    	(array = ast_schema (n, sym_assignment_expression,
    			     0, sym_unary_expression,
    			     0, sym_postfix_expression,
    			     0, sym_array_access))) {
          const char * typename =
    	ast_typedef_name (ast_expression_type (array->child[0], stack, false));
          Ast * member = NULL;
          if ((typename &&
    	   (!strcmp (typename, "scalar") ||
    	    !strcmp (typename, "vertex scalar"))) ||
    	  ((member = ast_schema (array->child[0], sym_postfix_expression,
    				 2, sym_member_identifier,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER)) &&
    	   (!strcmp (ast_terminal (member)->start, "n") ||
    	    !strcmp (ast_terminal (member)->start, "t") ||
    	    !strcmp (ast_terminal (member)->start, "r")) &&
    	   (typename =
    	    ast_typedef_name (ast_expression_type (array->child[0]->child[0],
    						   stack, false))) &&
    	   (!strcmp (typename, "vector") ||
    	    !strcmp (typename, "face vector")))) {
    	stack_push (stack, &n);
    	declare_point_variables (stack);
    	return n;
          }
        }
        return NULL;
      }

    Global boundary conditions

      case sym_boundary_definition: {
        Ast * expr = ast_schema (n, sym_boundary_definition,
    			     0, sym_assignment_expression,
    			     2, sym_assignment_expression);
        Ast * array = ast_find (n, sym_array_access);
        if (expr && array) {
          stack_push (stack, &n);
          declare_point_variables (stack);
          return n;
        }
        return NULL;
      }

    Point point

      case sym_direct_declarator:
        if (ast_schema (ast_is_point_point (ast_schema (n, sym_direct_declarator,
    						    0, sym_generic_identifier,
    						    0, sym_IDENTIFIER)),
    		    sym_declaration))
          declare_point_variables (stack);
        return NULL;
        
      }
    
      return NULL;
    }
    
    void ast_traverse (Ast * n, Stack * stack,
    		   void func (Ast *, Stack *, void *),
    		   void * data)
    {
      if (!n || n == ast_placeholder)
        return;
    
      Ast * scope = ast_push_declarations (n, stack);
    
      if (n->child)
        for (Ast ** c = n->child; *c; c++)
          ast_traverse (*c, stack, func, data);
      func (n, stack, data);
    
      ast_pop_scope (stack, scope);
    }

    The entry function

    Called by qcc to trigger the translation.

    static void checks (AstRoot * root, AstRoot * d, TranslateData * data)
    {
      CHECK ((Ast *) root, true);
      CHECK ((Ast *) d, true);
      CHECK (data->init_solver, true);
    }
    
    void * endfor (FILE * fin, FILE * fout,
    	       const char * grid, int dimension,
    	       bool nolineno, bool progress, bool catch, bool parallel, bool cpu,
    	       FILE * swigfp, char * swigname)
    {
      char * buffer = NULL;
      size_t len = 0, maxlen = 0;
      int c;
      while ((c = fgetc (fin)) != EOF) {
        if (len >= maxlen) {
          maxlen += 4096;
          buffer = realloc (buffer, maxlen);      
        }
        buffer[len++] = c;
      }
      if (len >= maxlen) {
        maxlen++;
        buffer = realloc (buffer, maxlen);      
      }
      buffer[len++] = '\0';
    
      FILE * fp = fopen (BASILISK "/ast/defaults.h", "r");
      assert (fp);
      AstRoot * d = ast_parse_file (fp, NULL);
      fclose (fp);
      
      AstRoot * root = ast_parse (buffer, d);
      free (buffer);
      if (!root) {
        fprintf (stderr, "qcc: error: cannot parse input (missing closing braces?)\n");
        exit (1);
      }
      root->stack = d->stack; d->stack = NULL;
      root->alloc = d->alloc; d->alloc = NULL;
    
      TranslateData data = {
        .dimension = dimension, .nolineno = nolineno, .parallel = parallel, .cpu = cpu,
        .constants_index = 0, .fields_index = 0, .nboundary = 0,
        // fixme: splitting of events and fields is not used yet
        .init_solver = NULL, .init_events = NULL, .init_fields = NULL,
        .swigname = NULL, .swigdecl = NULL, .swiginit = NULL
      };
      data.constants = calloc (1, sizeof (Field));
      data.swigname = swigfp ? swigname : NULL;
    
      fp = fopen (BASILISK "/ast/init_solver.h", "r");
      AstRoot * init = ast_parse_file (fp, root);
      fclose (fp);
      data.init_solver = ast_find ((Ast *) init, sym_function_definition);
      assert (data.init_solver);
      str_prepend (ast_left_terminal (data.init_solver)->before, "\n");  
      ast_block_list_append (ast_find ((Ast *)root, sym_translation_unit),
    			 sym_external_declaration, data.init_solver);
      data.last_events = 
        ast_find (ast_find (data.init_solver, sym_compound_statement)->child[1],
    	      sym_compound_statement);
      assert (data.last_events);
      data.init_fields =
        ast_find (ast_find (data.last_events, sym_compound_statement)->child[1],
    	      sym_compound_statement);
      assert (data.init_fields);
      data.init_events =
        ast_find (ast_find (data.init_fields, sym_compound_statement)->child[1],
    	      sym_compound_statement);
      assert (data.init_events);
      ast_destroy ((Ast *) init);
    
      typedef void (* TraverseFunc) (Ast *, Stack *, void *);
      for (TraverseFunc * pass = (TraverseFunc[]){ global_boundaries_and_stencils, translate, maps, stencils, macros, NULL }; *pass; pass++) {
        stack_push (root->stack, &root);
        ast_traverse ((Ast *) root, root->stack, *pass, &data);
        ast_pop_scope (root->stack, (Ast *) root);
        checks (root, d, &data);    
      }
      
      if (data.fields_index) {
        Ast * call_init_solver = ast_find (data.init_solver, sym_function_call);
        char n[10];
        snprintf (n, 9, "%d", data.fields_index);
        char * src = NULL;
        str_append (src, "datasize=", n, "*sizeof(double);");
        Ast * expr = ast_parse_expression (src, root);
        free (src);
        ast_block_list_insert_before2 (ast_parent (call_init_solver, sym_block_item),
    				   NN(call_init_solver, sym_statement, expr));
      }
    
      char methods[strlen(grid) + strlen("_methods") + 1];
      strcpy (methods, grid);
      strcat (methods, "_methods");
      compound_prepend (data.last_events, ast_new_function_call (data.last_events, methods));
      if (catch)
        compound_append (data.last_events, 
    		     ast_new_function_call (data.last_events, "catch_fpe"));
      if (progress)
        compound_append (data.last_events, 
    		     ast_new_function_call (data.last_events, "last_events"));
    
    
      /* SWIG interface */
      if (data.swigname) {
        if (data.swigdecl) {
          fprintf (swigfp,
    	       "\n%%{\n"
    	       "%s"
    	       "%%}\n"
    	       "\n"
    	       "%s",
    	       data.swigdecl,
    	       data.swigdecl);
          free (data.swigdecl);
        }
        if (data.swiginit) {
          fprintf (swigfp,
    	       "\n"
    	       "%%pythoncode %%{\n"
    	       "%s"
    	       "%%}\n",
    	       data.swiginit);
          free (data.swiginit);
        }
        fclose (swigfp);
      }
    
      checks (root, d, &data);
      
      free (data.constants);
      
      ast_print ((Ast *) root, fout, 0);
    
      ((Ast *)root)->parent = (Ast *) d;
      return root;
    }
    
    bool check_dimensions (AstRoot * root,
    		       bool nolineno,
    		       int run, FILE * dimensions,
    		       int finite, int redundant, int warn, int maxcalls)
    {
      Ast * d = ((Ast *)root)->parent;
      ((Ast *)root)->parent = NULL;
      Ast * main = ast_parent (ast_identifier_declaration (root->stack, "main"),
    			   sym_function_definition);
      bool ret = true;
      if (main) {
        if (dimensions != stdout)
          ret = ast_check_dimensions (root, main, run >= 0 ? run : 0,
    				  maxcalls, dimensions, finite, redundant, !nolineno, warn);  
        else if (run >= 0)
          ast_run (root, main, run, maxcalls, NULL);
      }
    
      ast_destroy (d);
      ast_destroy ((Ast *) root);
      return ret;
    }

    Kernels

    static
    void kernel (Ast * n, Stack * stack, void * data)
    {
      switch (n->sym) {

    forin_declaration_statement

      case sym_forin_declaration_statement: {
        Ast * declarator = n->child[3];
        Ast * identifier = ast_schema (declarator, sym_declarator,
    				   0, sym_direct_declarator,
    				   0, sym_generic_identifier,
    				   0, sym_IDENTIFIER);
        if (!identifier) {
          AstTerminal * t = ast_left_terminal (n);
          fprintf (stderr,
    	       "%s:%d: error: incorrect declaration\n",
    	       t->file, t->line);
          exit (1);
        }
        ast_before (n, "{");
        ast_after ((Ast *)ast_right_terminal (n->child[0]), "in");
        ast_after ((Ast *)ast_right_terminal (n->child[2]), ",");
        free (ast_terminal (n->child[4])->start);
        ast_terminal (n->child[4])->start = strdup (",");
        ast_after (n, " endforin()}");
        break;
      }

    forin_statement

      case sym_forin_statement: {
        int narg = 0;
        foreach_item (n->child[2], 2, expr)
          narg++;
        ast_before (n, "{");
        char suffix[20]; snprintf (suffix, 19, "%d", narg);
        ast_after ((Ast *)ast_right_terminal (ast_child (n, sym_for_scope)), "in", suffix);
        free (ast_terminal (ast_child (n, sym_IDENTIFIER))->start);
        ast_terminal (ast_child (n, sym_IDENTIFIER))->start = strdup (",");
        ast_after (n, " endforin", suffix, "()}");
        break;
      }

    Dereference of “inout” parameters

      case sym_unary_operator: {
        Ast * identifier, * ref, * type;
        if (n->child[0]->sym == token_symbol ('*') &&
    	(identifier = ast_schema (n->parent, sym_unary_expression,
    				  1, sym_cast_expression,
    				  0, sym_unary_expression,
    				  0, sym_postfix_expression,
    				  0, sym_primary_expression,
    				  0, sym_IDENTIFIER)) &&
    	(ref = ast_identifier_declaration (stack, ast_terminal (identifier)->start)) &&
    	(type = ast_schema (ast_ancestor (ref, 4), sym_parameter_declaration,
    			    0, sym_declaration_specifiers,
    			    0, sym_type_specifier,
    			    0, sym_types)) &&
    	!strcmp (ast_terminal (type->child[0])->before + strlen (ast_terminal (type->child[0])->before) - 6, "inout ")) {
          free (ast_terminal (n->child[0])->start);
          ast_terminal (n->child[0])->start = strdup("");
        }
        break;
      }

    Field assignments

    Kernels often need to know the type of access to fields (i.e. read or write). Here we append “_out" to stencil access functions linked to assignments (i.e. “write” operations ).

      case sym_function_call: {
        Ast * identifier = ast_function_call_identifier (n);
        if (!identifier) break;
        AstTerminal * t = ast_terminal (identifier);
        if (!t) break;
        if (!strcmp (t->start, "val")) {
          if (ast_child (ast_parent (n, sym_assignment_expression), sym_assignment_operator))
    	str_append (t->start, "_out_");
          break;
        }

    Function calls

        if (!(identifier = ast_identifier_declaration (stack, t->start))) break;
        Ast * definition = ast_parent (identifier, sym_function_definition);
        if (!(identifier = ast_find (definition, sym_direct_declarator,
    				 0, sym_direct_declarator,
    				 0, sym_generic_identifier,
    				 0, sym_IDENTIFIER)) ||
    	strcmp (ast_terminal (identifier)->start, t->start)) break;
    
        Ast * previous = fast_stack_find (data, t->start);
        if (!previous) {
          definition = ast_copy (definition);
          identifier = ast_find (definition, sym_direct_declarator,
    			     0, sym_direct_declarator,
    			     0, sym_generic_identifier,
    			     0, sym_IDENTIFIER);
          stack_push (data, &identifier);
        }

    Here we check for “inout” arguments (i.e. pointers taken using ‘&’).

        Ast * parameters = ast_find (ast_schema (definition, sym_function_definition,
    					     0, sym_function_declaration,
    					     1, sym_declarator),
    				 sym_direct_declarator,
    				 2, sym_parameter_type_list,
    				 0, sym_parameter_list);
        Ast * arguments = ast_schema (n, sym_function_call,
    				  2, sym_argument_expression_list);
        if (arguments) {
          while (parameters->child[0]->sym == parameters->sym)
    	parameters = parameters->child[0];
          Ast * parameter = ast_child (parameters, sym_parameter_declaration);
          foreach_item_r (arguments, sym_argument_expression_list_item, argument) {
    	Ast * ampersand, * type, * pointer;
    	if ((ampersand = ast_schema (ast_is_unary_expression (argument->child[0]), sym_unary_expression,
    				     0, sym_unary_operator,
    				     0, token_symbol ('&'))) &&
    	    (pointer = ast_schema (parameter, sym_parameter_declaration,
    				   1, sym_declarator,
    				   0, sym_pointer,
    				   0, token_symbol ('*'))) &&
    	    (type = ast_schema (parameter, sym_parameter_declaration,
    				0, sym_declaration_specifiers,
    				0, sym_type_specifier,
    				0, sym_types)) &&
    	    (type->child[0]->sym == sym_DOUBLE ||
    	     type->child[0]->sym == sym_FLOAT ||
    	     type->child[0]->sym == sym_INT)) {
    	  if (!previous) {
    	    ast_before (type->child[0], "inout ");
    	    free (ast_terminal (pointer)->start);
    	    ast_terminal (pointer)->start = strdup ("");
    	  }
    	  free (ast_terminal (ampersand)->start);
    	  ast_terminal (ampersand)->start = strdup ("");
    	}
    	parameters = parameters->parent, parameter = ast_child (parameters, sym_parameter_declaration);
    	if (!parameter) break;
          }
        }
    
        if (!previous)
          ast_traverse (definition, stack, kernel, data);
        break;
      }
        
      }
    }
    
    static
    void stringify (Ast * n, Ast * argument)
    {
      char * s = ast_str_print (n, NULL, 0, 1);
      for (char * i = s; *i; i++) {
        char a[] = "1";
        switch (*i) {
        case '\n': ast_after (argument, "\\n"); break;
        case '\\': ast_after (argument, "\\\\"); break;
        case '"': ast_after (argument, "\\\""); break;
        case '#':
          if (i[-1] == '\n') {
    	ast_after (argument, "// #"); break;
          }
          // fall through
        default:  a[0] = *i; ast_after (argument, a); break;
        }
      }
      free (s);
      ast_destroy (n);
    }
    
    void ast_kernel (Ast * n, Ast * argument)
    {
      AstRoot * root = ast_get_root (n);
      Stack * stack = root->stack;
      stack_push (stack, &n);
      Stack * funcs = stack_new (sizeof (Ast *));
      Ast * statement = ast_copy (ast_child (n, sym_statement));
      ast_traverse (statement, stack, kernel, funcs);
      ast_after (argument, "_(\"");
      Ast ** func;
      while ((func = stack_pop (funcs)))
        stringify (ast_parent (*func, sym_function_definition), argument);
      stack_destroy (funcs);
      ast_after (argument, "\"),_(\"");
      stringify (statement, argument);
      ast_after (argument, "\")");
      ast_pop_scope (stack, n);
    }