/*
 *  matiec - a compiler for the programming languages defined in IEC 61131-3
 *
 *  Copyright (C) 2003-2011  Mario de Sousa (msousa@fe.up.pt)
 *  Copyright (C) 2007-2011  Laurent Bessard and Edouard Tisserant
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * This code is made available on the understanding that it will not be
 * used in safety-critical situations without a full and competent review.
 */

/*
 * An IEC 61131-3 compiler.
 *
 * Based on the
 * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10)
 *
 */


/*
 * Code to be included into the code generated by the 4th stage.
 *
 * This is part of the 4th stage that generates
 * a ST and IL source program equivalent to the IL and ST
 * code.
 * This is expected to be used mainly in debuging the syntax
 * and lexical parser, and as a starting point for other
 * 4th stages.
 */



/* Compile with the following option, to print out the const_value of each symbol, which was filled in by constant_folding_c during stage3 */
// #define DEBUG_CONST_VALUE



// #include <stdio.h>  /* required for NULL */
#include <string>
#include <iostream>
#include <sstream>
#include <typeinfo>
#include "generate_iec.hh"

#include "../stage4.hh"
#include "../../main.hh" // required for ERROR() and ERROR_MSG() macros.



/***********************************************************************/
/***********************************************************************/
/***********************************************************************/
/***********************************************************************/

/* Parse command line options passed from main.c !! */

int  stage4_parse_options(char *options) {return 0;}

void stage4_print_options(void) {
  printf("          (no options available when generating IEC 61131-3 code)\n"); 
}

/***********************************************************************/
/***********************************************************************/
/***********************************************************************/
/***********************************************************************/


//class generate_iec_c: public iterator_visitor_c {
class generate_iec_c: public visitor_c {
  private:
    stage4out_c &s4o;


  public:
    generate_iec_c(stage4out_c *s4o_ptr): s4o(*s4o_ptr) {}
    ~generate_iec_c(void) {}


  private:

    
void print_const_value(symbol_c *symbol) {
#ifdef DEBUG_CONST_VALUE
  if (NULL == symbol) return;
  bool first = true;
  
  if (NULL != symbol->const_value_uint64) {
    first?s4o.print("{"):s4o.print("; ");
    first = false;
    s4o.print("uint64:");
    if (symbol->const_value_uint64->status == symbol_c::cs_const_value)
      s4o.print_uint64(symbol->const_value_uint64->value);
    if (symbol->const_value_uint64->status == symbol_c::cs_overflow)
    s4o.print("OVERFLOW");
  }
  if (NULL != symbol->const_value_int64) {
    first?s4o.print("{"):s4o.print("; ");
    first = false;
    s4o.print("int64:");
    if (symbol->const_value_int64->status == symbol_c::cs_const_value)
      s4o.print_int64(symbol->const_value_int64->value);
    if (symbol->const_value_int64->status == symbol_c::cs_overflow)
    s4o.print("OVERFLOW");
  }
  if (NULL != symbol->const_value_real64) {
    first?s4o.print("{"):s4o.print("; ");
    first = false;
    s4o.print("real64:");
    if (symbol->const_value_real64->status == symbol_c::cs_const_value)
      s4o.print_real64(symbol->const_value_real64->value);
    if (symbol->const_value_real64->status == symbol_c::cs_overflow)
    s4o.print("OVERFLOW");
  }
  if (NULL != symbol->const_value_bool) {
    first?s4o.print("{"):s4o.print("; ");
    first = false;
    s4o.print("bool:");
    if (symbol->const_value_bool->status == symbol_c::cs_const_value)
      s4o.print((symbol->const_value_bool->value)?"true":"false");
    if (symbol->const_value_bool->status == symbol_c::cs_overflow)
    s4o.print("OVERFLOW");
  }
  if (!first) s4o.print("}");  
#endif
  return;
}



void *print_token(token_c *token) {
  print_const_value(token);
  return s4o.print(token->value);
}


void *print_literal(symbol_c *type, symbol_c *value) {
  print_const_value(value);
  if (NULL != type) {
    type->accept(*this);
    s4o.print("#");
  }
  value->accept(*this);
  return NULL;
}

void *print_list(list_c *list,
			       std::string pre_elem_str = "",
			       std::string inter_elem_str = "",
			       std::string post_elem_str = "") {
  if (list->n > 0) {
    s4o.print(pre_elem_str);
    list->elements[0]->accept(*this);
  }

  for(int i = 1; i < list->n; i++) {
    s4o.print(inter_elem_str);
    list->elements[i]->accept(*this);
  }

  if (list->n > 0)
    s4o.print(post_elem_str);

  return NULL;
}


void *print_binary_expression(symbol_c *symbol,
			      symbol_c *l_exp,
			      symbol_c *r_exp,
			      const char *operation) {
  print_const_value(symbol);
  s4o.print("(");
  l_exp->accept(*this);
  s4o.print(operation);
  r_exp->accept(*this);
  s4o.print(")");
  return NULL;
}

void *print_unary_expression(symbol_c *symbol,
			     symbol_c *exp,
			     const char *operation) {
  print_const_value(symbol);
  s4o.print(operation);
  exp->accept(*this);
  return NULL;
}





  public:

/*  EN/ENO */
#if 0
void *visit(en_param_c *symbol) {
  s4o.print("EN");
  return NULL;
}

void *visit(eno_param_c *symbol) {
  s4o.print("ENO");
  return NULL;
}
void *visit(en_param_c *symbol) {
  return symbol->param_name->accept(*this);
}

void *visit(eno_param_c *symbol) {
  return symbol->param_name->accept(*this);
}
#endif


/* A class used to identify an entry (literal, variable, etc...) in the abstract syntax tree with an invalid data type */
/* This is only used from stage3 onwards. Stages 1 and 2 will never create any instances of invalid_type_name_c */
// SYM_REF0(invalid_type_name_c)
void *visit(invalid_type_name_c *symbol) {
  ERROR;
  return NULL;
}


/******************/
/* 2.1.6 Pragmas  */
/******************/
void *visit(enable_code_generation_pragma_c*)  {s4o.print("{enable code generation}");  return NULL;}
void *visit(disable_code_generation_pragma_c*) {s4o.print("{disable code generation}"); return NULL;}
void *visit(pragma_c *symbol)                  {return print_token(symbol);}


/***************************/
/* B 0 - Programming Model */
/***************************/
void *visit(library_c *symbol) {return print_list(symbol);}

/*******************************************/
/* B 1.1 - Letters, digits and identifiers */
/*******************************************/
void *visit(                 identifier_c *symbol) {return print_token(symbol);}
void *visit(derived_datatype_identifier_c *symbol) {return print_token(symbol);}
void *visit(         poutype_identifier_c *symbol) {return print_token(symbol);}

/*********************/
/* B 1.2 - Constants */
/*********************/
/*********************************/
/* B 1.2.XX - Reference Literals */
/*********************************/
/* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */
void *visit(ref_value_null_literal_c *symbol)  {s4o.print("NULL"); return NULL;}

/******************************/
/* B 1.2.1 - Numeric Literals */
/******************************/
void *visit(real_c *symbol)               {return print_token(symbol);}
void *visit(neg_real_c *symbol)           {return print_unary_expression(symbol, symbol->exp, "-");}
void *visit(integer_c *symbol)            {return print_token(symbol);}
void *visit(neg_integer_c *symbol)        {return print_unary_expression(symbol, symbol->exp, "-");}
void *visit(binary_integer_c *symbol)     {return print_token(symbol);}
void *visit(octal_integer_c *symbol)      {return print_token(symbol);}
void *visit(hex_integer_c *symbol)        {return print_token(symbol);}

void *visit(integer_literal_c *symbol)    {return print_literal(symbol->type, symbol->value);}
void *visit(real_literal_c *symbol)       {return print_literal(symbol->type, symbol->value);}
void *visit(bit_string_literal_c *symbol) {return print_literal(symbol->type, symbol->value);}
void *visit(boolean_literal_c *symbol)    {return print_literal(symbol->type, symbol->value);}

/* helper class for boolean_literal_c */
void *visit(boolean_true_c *symbol)       {s4o.print(/*"TRUE" */"1"); return NULL;}
void *visit(boolean_false_c *symbol)      {s4o.print(/*"FALSE"*/"0"); return NULL;}

/*******************************/
/* B.1.2.2   Character Strings */
/*******************************/
void *visit(double_byte_character_string_c *symbol) {return print_token(symbol);}
void *visit(single_byte_character_string_c *symbol) {return print_token(symbol);}


/***************************/
/* B 1.2.3 - Time Literals */
/***************************/

/************************/
/* B 1.2.3.1 - Duration */
/************************/
void *visit(neg_time_c *symbol) {s4o.print("-"); return NULL;}

void *visit(duration_c *symbol) {
  s4o.print("TIME#");
  if (symbol->neg != NULL)
    symbol->neg->accept(*this);
  symbol->interval->accept(*this);
  return NULL;
}

void *visit(fixed_point_c *symbol) {return print_token(symbol);}

/* SYM_REF5(interval_c, days, hours, minutes, seconds, milliseconds) */
void *visit(interval_c *symbol) {
  if (NULL != symbol->days) {
    symbol->days->accept(*this);
    s4o.print("d");
  }

  if (NULL != symbol->hours) {
    symbol->hours->accept(*this);
    s4o.print("h");
  }

  if (NULL != symbol->minutes) {
    symbol->minutes->accept(*this);
    s4o.print("m");
  }

  if (NULL != symbol->seconds) {
    symbol->seconds->accept(*this);
    s4o.print("s");
  }

  if (NULL != symbol->milliseconds) {
    symbol->milliseconds->accept(*this);
    s4o.print("ms");
  }

  return NULL;
}



/************************************/
/* B 1.2.3.2 - Time of day and Date */
/************************************/

void *visit(time_of_day_c *symbol) {
  s4o.print("TIME_OF_DAY#");
  symbol->daytime->accept(*this);
  return NULL;
}

void *visit(daytime_c *symbol) {
  symbol->day_hour->accept(*this);
  s4o.print(":");
  symbol->day_minute->accept(*this);
  s4o.print(":");
  symbol->day_second->accept(*this);
  return NULL;
}


void *visit(date_c *symbol) {
  s4o.print("DATE#");
  symbol->date_literal->accept(*this);
  return NULL;
}

void *visit(date_literal_c *symbol) {
  symbol->year->accept(*this);
  s4o.print("-");
  symbol->month->accept(*this);
  s4o.print("-");
  symbol->day->accept(*this);
  return NULL;
}

void *visit(date_and_time_c *symbol) {
  s4o.print("DATE_AND_TIME#");
  symbol->date_literal->accept(*this);
  s4o.print("-");
  symbol->daytime->accept(*this);
  return NULL;
}



/***********************************/
/* B 1.3.1 - Elementary Data Types */
/***********************************/
void *visit(time_type_name_c *symbol)        {s4o.print("TIME");        return NULL;}
void *visit(bool_type_name_c *symbol)        {s4o.print("BOOL");        return NULL;}
void *visit(sint_type_name_c *symbol)        {s4o.print("SINT");        return NULL;}
void *visit(int_type_name_c *symbol)         {s4o.print("INT");         return NULL;}
void *visit(dint_type_name_c *symbol)        {s4o.print("DINT");        return NULL;}
void *visit(lint_type_name_c *symbol)        {s4o.print("LINT");        return NULL;}
void *visit(usint_type_name_c *symbol)       {s4o.print("USINT");       return NULL;}
void *visit(uint_type_name_c *symbol)        {s4o.print("UINT");        return NULL;}
void *visit(udint_type_name_c *symbol)       {s4o.print("UDINT");       return NULL;}
void *visit(ulint_type_name_c *symbol)       {s4o.print("ULINT");       return NULL;}
void *visit(real_type_name_c *symbol)        {s4o.print("REAL");        return NULL;}
void *visit(lreal_type_name_c *symbol)       {s4o.print("LREAL");       return NULL;}
void *visit(date_type_name_c *symbol)        {s4o.print("DATE");        return NULL;}
void *visit(tod_type_name_c *symbol)         {s4o.print("TOD");         return NULL;}
void *visit(dt_type_name_c *symbol)          {s4o.print("DT");          return NULL;}
void *visit(byte_type_name_c *symbol)        {s4o.print("BYTE");        return NULL;}
void *visit(word_type_name_c *symbol)        {s4o.print("WORD");        return NULL;}
void *visit(lword_type_name_c *symbol)       {s4o.print("LWORD");       return NULL;}
void *visit(dword_type_name_c *symbol)       {s4o.print("DWORD");       return NULL;}
void *visit(string_type_name_c *symbol)      {s4o.print("STRING");      return NULL;}
void *visit(wstring_type_name_c *symbol)     {s4o.print("WSTRING");     return NULL;}

void *visit(safetime_type_name_c *symbol)    {s4o.print("SAFETIME");    return NULL;}
void *visit(safebool_type_name_c *symbol)    {s4o.print("SAFEBOOL");    return NULL;}
void *visit(safesint_type_name_c *symbol)    {s4o.print("SAFESINT");    return NULL;}
void *visit(safeint_type_name_c *symbol)     {s4o.print("SAFEINT");     return NULL;}
void *visit(safedint_type_name_c *symbol)    {s4o.print("SAFEDINT");    return NULL;}
void *visit(safelint_type_name_c *symbol)    {s4o.print("SAFELINT");    return NULL;}
void *visit(safeusint_type_name_c *symbol)   {s4o.print("SAFEUSINT");   return NULL;}
void *visit(safeuint_type_name_c *symbol)    {s4o.print("SAFEUINT");    return NULL;}
void *visit(safeudint_type_name_c *symbol)   {s4o.print("SAFEUDINT");   return NULL;}
void *visit(safeulint_type_name_c *symbol)   {s4o.print("SAFEULINT");   return NULL;}
void *visit(safereal_type_name_c *symbol)    {s4o.print("SAFEREAL");    return NULL;}
void *visit(safelreal_type_name_c *symbol)   {s4o.print("SAFELREAL");   return NULL;}
void *visit(safedate_type_name_c *symbol)    {s4o.print("SAFEDATE");    return NULL;}
void *visit(safetod_type_name_c *symbol)     {s4o.print("SAFETOD");     return NULL;}
void *visit(safedt_type_name_c *symbol)      {s4o.print("SAFEDT");      return NULL;}
void *visit(safebyte_type_name_c *symbol)    {s4o.print("SAFEBYTE");    return NULL;}
void *visit(safeword_type_name_c *symbol)    {s4o.print("SAFEWORD");    return NULL;}
void *visit(safelword_type_name_c *symbol)   {s4o.print("SAFELWORD");   return NULL;}
void *visit(safedword_type_name_c *symbol)   {s4o.print("SAFEDWORD");   return NULL;}
void *visit(safestring_type_name_c *symbol)  {s4o.print("SAFESTRING");  return NULL;}
void *visit(safewstring_type_name_c *symbol) {s4o.print("SAFEWSTRING"); return NULL;}

/********************************/
/* B.1.3.2 - Generic data types */
/********************************/
void *visit(generic_type_any_c      *symbol) {s4o.print("ANY");         return NULL;}

/********************************/
/* B 1.3.3 - Derived data types */
/********************************/
/*  TYPE type_declaration_list END_TYPE */
void *visit(data_type_declaration_c *symbol) {
  s4o.print("TYPE\n");
  s4o.indent_right();
  symbol->type_declaration_list->accept(*this);
  s4o.indent_left();
  s4o.print("END_TYPE\n\n\n");
  return NULL;
}


/* helper symbol for data_type_declaration */
/*| type_declaration_list type_declaration ';' */
void *visit(type_declaration_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}


/*  simple_type_name ':' simple_spec_init */
void *visit(simple_type_declaration_c *symbol) {
  symbol->simple_type_name->accept(*this);
  s4o.print(" : ");
  symbol->simple_spec_init->accept(*this);
  return NULL;
}

/* simple_specification ASSIGN constant */
void *visit(simple_spec_init_c *symbol) {
  symbol->simple_specification->accept(*this);
  if (symbol->constant != NULL) {
    s4o.print(" := ");
    symbol->constant->accept(*this);
  }
  return NULL;
}

/*  subrange_type_name ':' subrange_spec_init */
void *visit(subrange_type_declaration_c *symbol) {
  symbol->subrange_type_name->accept(*this);
  s4o.print(" : ");
  symbol->subrange_spec_init->accept(*this);
  return NULL;
}

/* subrange_specification ASSIGN signed_integer */
void *visit(subrange_spec_init_c *symbol) {
  symbol->subrange_specification->accept(*this);
  if (symbol->signed_integer != NULL) {
    s4o.print(" := ");
    symbol->signed_integer->accept(*this);
  }
  return NULL;
}

/*  integer_type_name '(' subrange')' */
void *visit(subrange_specification_c *symbol) {
  symbol->integer_type_name->accept(*this);
  if (symbol->subrange != NULL) {
    s4o.print("(");
    symbol->subrange->accept(*this);
    s4o.print(")");
  }
  return NULL;
}

/*  signed_integer DOTDOT signed_integer */
void *visit(subrange_c *symbol) {
  symbol->lower_limit->accept(*this);
  s4o.print(" .. ");
  symbol->upper_limit->accept(*this);
  return NULL;
}

/*  enumerated_type_name ':' enumerated_spec_init */
void *visit(enumerated_type_declaration_c *symbol) {
  symbol->enumerated_type_name->accept(*this);
  s4o.print(" : ");
  symbol->enumerated_spec_init->accept(*this);
  return NULL;
}

/* enumerated_specification ASSIGN enumerated_value */
void *visit(enumerated_spec_init_c *symbol) {
  symbol->enumerated_specification->accept(*this);
  if (symbol->enumerated_value != NULL) {
    s4o.print(" := ");
    symbol->enumerated_value->accept(*this);
  }
  return NULL;
}

/* helper symbol for enumerated_specification->enumerated_spec_init */
/* enumerated_value_list ',' enumerated_value */
void *visit(enumerated_value_list_c *symbol) {print_list(symbol, "(", ", ", ")"); return NULL;}

/* enumerated_type_name '#' identifier */
void *visit(enumerated_value_c *symbol) {
  if (symbol->type != NULL) {
    symbol->type->accept(*this);
    s4o.print("#");
  }
  symbol->value->accept(*this);
  return NULL;
}

/*  identifier ':' array_spec_init */
void *visit(array_type_declaration_c *symbol) {
  symbol->identifier->accept(*this);
  s4o.print(" : ");
  symbol->array_spec_init->accept(*this);
  return NULL;
}

/* array_specification [ASSIGN array_initialization} */
/* array_initialization may be NULL ! */
void *visit(array_spec_init_c *symbol) {
  symbol->array_specification->accept(*this);
  if (symbol->array_initialization != NULL) {
    s4o.print(" := ");
    symbol->array_initialization->accept(*this);
  }
  return NULL;
}

/* ARRAY '[' array_subrange_list ']' OF non_generic_type_name */
void *visit(array_specification_c *symbol) {
  s4o.print("ARRAY [");
  symbol->array_subrange_list->accept(*this);
  s4o.print("] OF ");
  symbol->non_generic_type_name->accept(*this);
  return NULL;
}

/* helper symbol for array_specification */
/* array_subrange_list ',' subrange */
void *visit(array_subrange_list_c *symbol) {print_list(symbol, "", ", "); return NULL;}

/* helper symbol for array_initialization */
/* array_initial_elements_list ',' array_initial_elements */
void *visit(array_initial_elements_list_c *symbol) {print_list(symbol, "[", ", ", "]"); return NULL;}

/* integer '(' [array_initial_element] ')' */
/* array_initial_element may be NULL ! */
void *visit(array_initial_elements_c *symbol) {
  symbol->integer->accept(*this);
  s4o.print("(");
  if (symbol->array_initial_element != NULL)
    symbol->array_initial_element->accept(*this);
  s4o.print(")");
  return NULL;
}

/*  structure_type_name ':' structure_specification */
void *visit(structure_type_declaration_c *symbol) {
  symbol->structure_type_name->accept(*this);
  s4o.print(" : ");
  symbol->structure_specification->accept(*this);
  return NULL;
}

/* structure_type_name ASSIGN structure_initialization */
/* structure_initialization may be NULL ! */
void *visit(initialized_structure_c *symbol) {
  symbol->structure_type_name->accept(*this);
  if (symbol->structure_initialization != NULL) {
    s4o.print(" := ");
    symbol->structure_initialization->accept(*this);
  }
  return NULL;
}

/* helper symbol for structure_declaration */
/* structure_declaration:  STRUCT structure_element_declaration_list END_STRUCT */
/* structure_element_declaration_list structure_element_declaration ';' */
void *visit(structure_element_declaration_list_c *symbol) {
  s4o.print("STRUCT\n");
  s4o.indent_right();
  print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n" + s4o.indent_spaces);
  s4o.print("END_STRUCT");
  s4o.indent_left();
  return NULL;
}

/*  structure_element_name ':' *_spec_init */
void *visit(structure_element_declaration_c *symbol) {
  symbol->structure_element_name->accept(*this);
  s4o.print(" : ");
  symbol->spec_init->accept(*this);
  return NULL;
}

/* helper symbol for structure_initialization */
/* structure_initialization: '(' structure_element_initialization_list ')' */
/* structure_element_initialization_list ',' structure_element_initialization */
void *visit(structure_element_initialization_list_c *symbol) {print_list(symbol, "(", ", ", ")"); return NULL;}

/*  structure_element_name ASSIGN value */
void *visit(structure_element_initialization_c *symbol) {
  symbol->structure_element_name->accept(*this);
  s4o.print(" := ");
  symbol->value->accept(*this);
  return NULL;
}

/*  string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */
void *visit(string_type_declaration_c *symbol) {
  symbol->string_type_name->accept(*this);
  s4o.print(" : ");
  symbol->elementary_string_type_name->accept(*this);
  symbol->string_type_declaration_size->accept(*this);
  if (symbol->string_type_declaration_init != NULL)
    symbol->string_type_declaration_init->accept(*this);
  return NULL;
}

/*  function_block_type_name ASSIGN structure_initialization */
/* structure_initialization -> may be NULL ! */
void *visit(fb_spec_init_c *symbol) {
  symbol->function_block_type_name->accept(*this);
  if (symbol->structure_initialization != NULL) {
    s4o.print(" := ");
    symbol->structure_initialization->accept(*this);
  }
  return NULL;
}



/* ref_spec:  REF_TO (non_generic_type_name | function_block_type_name) */
// SYM_REF1(ref_spec_c, type_name)
void *visit(ref_spec_c *symbol) {
  s4o.print("REF_TO ");
  symbol->type_name->accept(*this);
  return NULL;
}

/* For the moment, we do not support initialising reference data types */
/* ref_spec_init: ref_spec [ ASSIGN ref_initialization ]; */ 
/* NOTE: ref_initialization may be NULL!! */
// SYM_REF2(ref_spec_init_c, ref_spec, ref_initialization)
void *visit(ref_spec_init_c *symbol) {
  symbol->ref_spec->accept(*this);
  if (symbol->ref_initialization != NULL) {
    s4o.print(" := ");
    symbol->ref_initialization->accept(*this);
  }
  return NULL;
}

/* ref_type_decl: identifier ':' ref_spec_init */
// SYM_REF2(ref_type_decl_c, ref_type_name, ref_spec_init)
void *visit(ref_type_decl_c *symbol) {
  symbol->ref_type_name->accept(*this);
  s4o.print(" : ");
  symbol->ref_spec_init->accept(*this);
  return NULL;
}








/*********************/
/* B 1.4 - Variables */
/*********************/
void *visit(symbolic_variable_c *symbol) {return symbol->var_name->accept(*this);}
void *visit(symbolic_constant_c *symbol) {return symbol->var_name->accept(*this);}

/********************************************/
/* B.1.4.1   Directly Represented Variables */
/********************************************/
void *visit(direct_variable_c *symbol) {return print_token(symbol);}


/*************************************/
/* B.1.4.2   Multi-element Variables */
/*************************************/
/*  subscripted_variable '[' subscript_list ']' */
void *visit(array_variable_c *symbol) {
  symbol->subscripted_variable->accept(*this);
  s4o.print("[");
  symbol->subscript_list->accept(*this);
  s4o.print("]");
  return NULL;
}


/* subscript_list ',' subscript */
void *visit(subscript_list_c *symbol) {return print_list(symbol, "", ", ");}

/*  record_variable '.' field_selector */
void *visit(structured_variable_c *symbol) {
  symbol->record_variable->accept(*this);
  s4o.print(".");
  symbol->field_selector->accept(*this);
  return NULL;
}


/******************************************/
/* B 1.4.3 - Declaration & Initialisation */
/******************************************/
void *visit(constant_option_c *symbol) {s4o.print("CONSTANT"); return NULL;}
void *visit(retain_option_c *symbol) {s4o.print("RETAIN"); return NULL;}
void *visit(non_retain_option_c *symbol) {s4o.print("NON_RETAIN"); return NULL;}

/* VAR_INPUT [RETAIN | NON_RETAIN] input_declaration_list END_VAR */
/* option -> the RETAIN/NON_RETAIN/<NULL> directive... */
void *visit(input_declarations_c *symbol) {
  if (typeid(*(symbol->method)) == typeid(explicit_definition_c)) {
    s4o.print(s4o.indent_spaces); s4o.print("VAR_INPUT ");
    if (symbol->option != NULL)
      symbol->option->accept(*this);
    s4o.print("\n");
    s4o.indent_right();
    symbol->input_declaration_list->accept(*this);
    s4o.indent_left();
    s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n");
  }
  return NULL;
}

/* helper symbol for input_declarations */
/*| input_declaration_list input_declaration ';' */
void *visit(input_declaration_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}

/* edge -> The F_EDGE or R_EDGE directive */
void *visit(edge_declaration_c *symbol) {
  symbol->var1_list->accept(*this);
  s4o.print(" : BOOL ");
  symbol->edge->accept(*this);
  return NULL;
}

/* dummy classes only used as flags! */
void *visit(explicit_definition_c *symbol) {return NULL;}
void *visit(implicit_definition_c *symbol) {return NULL;}

/* EN : BOOL := 1 */
void *visit(en_param_declaration_c *symbol) {
  if (typeid(*(symbol->method)) == typeid(explicit_definition_c)) {
    symbol->name->accept(*this);
    s4o.print(" : ");
    symbol->type_decl->accept(*this);
  }
  return NULL;
}

/* ENO : BOOL */
void *visit(eno_param_declaration_c *symbol) {
  if (typeid(*(symbol->method)) == typeid(explicit_definition_c)) {
    symbol->name->accept(*this);
    s4o.print(" : ");
    symbol->type->accept(*this);
  }
  return NULL;
}

void *visit(raising_edge_option_c *symbol) {
  s4o.print("R_EDGE");
  return NULL;
}

void *visit(falling_edge_option_c *symbol) {
  s4o.print("F_EDGE");
  return NULL;
}


/* var1_list is one of the following...
 *    simple_spec_init_c *
 *    subrange_spec_init_c *
 *    enumerated_spec_init_c *
 */
void *visit(var1_init_decl_c *symbol) {
  symbol->var1_list->accept(*this);
  s4o.print(" : ");
  symbol->spec_init->accept(*this);
  return NULL;
}


void *visit(var1_list_c *symbol) {return print_list(symbol, "", ", ");}

/* | [var1_list ','] variable_name '..' */
/* NOTE: This is an extension to the standard!!! */
/* In order to be able to handle extensible standard functions
 * (i.e. standard functions that may have a variable number of
 * input parameters, such as AND(word#33, word#44, word#55, word#66),
 * we have extended the acceptable syntax to allow var_name '..'
 * in an input variable declaration.
 *
 * This allows us to parse the declaration of standard
 * extensible functions and load their interface definition
 * into the abstract syntax tree just like we do to other 
 * user defined functions.
 * This has the advantage that we can later do semantic
 * checking of calls to functions (be it a standard or user defined
 * function) in (almost) exactly the same way.
 *
 * Of course, we have a flag that disables this syntax when parsing user
 * written code, so we only allow this extra syntax while parsing the 
 * 'header' file that declares all the standard IEC 61131-3 functions.
 */
void *visit(extensible_input_parameter_c *symbol) {
  symbol->var_name->accept(*this);
  s4o.print(" .. ");
  return NULL;
}


/* var1_list ':' array_spec_init */
void *visit(array_var_init_decl_c *symbol) {
  symbol->var1_list->accept(*this);
  s4o.print(" : ");
  symbol->array_spec_init->accept(*this);
  return NULL;
}


/*  var1_list ':' initialized_structure */
void *visit(structured_var_init_decl_c *symbol) {
  symbol->var1_list->accept(*this);
  s4o.print(" : ");
  symbol->initialized_structure->accept(*this);
  return NULL;
}

/* name_list ':' function_block_type_name ASSIGN structure_initialization */
/* structure_initialization -> may be NULL ! */
void *visit(fb_name_decl_c *symbol) {
  symbol->fb_name_list->accept(*this);
  s4o.print(" : ");
  symbol->fb_spec_init->accept(*this);
  return NULL;
}

/* name_list ',' fb_name */
void *visit(fb_name_list_c *symbol) {return print_list(symbol, "", ", ");}

/* VAR_OUTPUT [RETAIN | NON_RETAIN] var_init_decl_list END_VAR */
/* option -> may be NULL ! */
void *visit(output_declarations_c *symbol) {
  if (typeid(*(symbol->method)) == typeid(explicit_definition_c)) {
    s4o.print(s4o.indent_spaces); s4o.print("VAR_OUTPUT ");
    if (symbol->option != NULL)
      symbol->option->accept(*this);
    s4o.print("\n");
    s4o.indent_right();
    symbol->var_init_decl_list->accept(*this);
    s4o.indent_left();
    s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n");
  }
  return NULL;
}

/*  VAR_IN_OUT  END_VAR */
void *visit(input_output_declarations_c *symbol) {
  s4o.print(s4o.indent_spaces); s4o.print("VAR_IN_OUT ");
  s4o.print("\n");
  s4o.indent_right();
  symbol->var_declaration_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n");
  return NULL;
}

/* helper symbol for input_output_declarations */
/* var_declaration_list var_declaration ';' */
void *visit(var_declaration_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}

/*  var1_list ':' array_specification */
void *visit(array_var_declaration_c *symbol) {
  symbol->var1_list->accept(*this);
  s4o.print(" : ");
  symbol->array_specification->accept(*this);
  return NULL;
}

/*  var1_list ':' structure_type_name */
void *visit(structured_var_declaration_c *symbol) {
  symbol->var1_list->accept(*this);
  s4o.print(" : ");
  symbol->structure_type_name->accept(*this);
  return NULL;
}

/* VAR [CONSTANT] var_init_decl_list END_VAR */
/* option -> may be NULL ! */
void *visit(var_declarations_c *symbol) {
  s4o.print(s4o.indent_spaces); s4o.print("VAR ");
  if (symbol->option != NULL)
    symbol->option->accept(*this);
  s4o.print("\n");
  s4o.indent_right();
  symbol->var_init_decl_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n");
  return NULL;
}

/*  VAR RETAIN var_init_decl_list END_VAR */
void *visit(retentive_var_declarations_c *symbol) {
  s4o.print(s4o.indent_spaces); s4o.print("VAR RETAIN ");
  s4o.print("\n");
  s4o.indent_right();
  symbol->var_init_decl_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n");
  return NULL;
}

/*  VAR [CONSTANT|RETAIN|NON_RETAIN] located_var_decl_list END_VAR */
/* option -> may be NULL ! */
void *visit(located_var_declarations_c *symbol) {
  s4o.print(s4o.indent_spaces); s4o.print("VAR ");
  if (symbol->option != NULL)
    symbol->option->accept(*this);
  s4o.print("\n");
  s4o.indent_right();
  symbol->located_var_decl_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n");
  return NULL;
}

/* helper symbol for located_var_declarations */
/* located_var_decl_list located_var_decl ';' */
void *visit(located_var_decl_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}

/*  [variable_name] location ':' located_var_spec_init */
/* variable_name -> may be NULL ! */
void *visit(located_var_decl_c *symbol) {
  if (symbol->variable_name != NULL) {
    symbol->variable_name->accept(*this);
    s4o.print(" ");
  }
  symbol->location->accept(*this);
  s4o.print(" : ");
  symbol->located_var_spec_init->accept(*this);
  return NULL;
}


/*| VAR_EXTERNAL [CONSTANT] external_declaration_list END_VAR */
/* option -> may be NULL ! */
void *visit(external_var_declarations_c *symbol) {
  s4o.print(s4o.indent_spaces); s4o.print("VAR_EXTERNAL ");
  if (symbol->option != NULL)
    symbol->option->accept(*this);
  s4o.print("\n");
  s4o.indent_right();
  symbol->external_declaration_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n");
  return NULL;
}

/* helper symbol for external_var_declarations */
/*| external_declaration_list external_declaration';' */
void *visit(external_declaration_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}

/*  global_var_name ':' (simple_specification|subrange_specification|enumerated_specification|array_specification|prev_declared_structure_type_name|function_block_type_name) */
void *visit(external_declaration_c *symbol) {
  symbol->global_var_name->accept(*this);
  s4o.print(" : ");
  symbol->specification->accept(*this);
  return NULL;
}

/*| VAR_GLOBAL [CONSTANT|RETAIN] global_var_decl_list END_VAR */
/* option -> may be NULL ! */
void *visit(global_var_declarations_c *symbol) {
  s4o.print(s4o.indent_spaces); s4o.print("VAR_GLOBAL ");
  if (symbol->option != NULL)
    symbol->option->accept(*this);
  s4o.print("\n");
  s4o.indent_right();
  symbol->global_var_decl_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n");
  return NULL;
}

/* helper symbol for global_var_declarations */
/*| global_var_decl_list global_var_decl ';' */
void *visit(global_var_decl_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}

/*| global_var_spec ':' [located_var_spec_init|function_block_type_name] */
/* type_specification ->may be NULL ! */
void *visit(global_var_decl_c *symbol) {
  symbol->global_var_spec->accept(*this);
  s4o.print(" : ");
  if (symbol->type_specification != NULL)
    symbol->type_specification->accept(*this);
  return NULL;
}

/*| global_var_name location */
void *visit(global_var_spec_c *symbol) {
  symbol->global_var_name->accept(*this);
  s4o.print(" ");
  symbol->location->accept(*this);
  return NULL;
}

/*  AT direct_variable */
void *visit(location_c *symbol) {
  s4o.print("AT ");
  symbol->direct_variable->accept(*this);
  return NULL;
}

/*| global_var_list ',' global_var_name */
void *visit(global_var_list_c *symbol) {return print_list(symbol, "", ", ");}

/*  var1_list ':' single_byte_string_spec */
void *visit(single_byte_string_var_declaration_c *symbol) {
  symbol->var1_list->accept(*this);
  s4o.print(" : ");
  symbol->single_byte_string_spec->accept(*this);
  return NULL;
}

/*  single_byte_limited_len_string_spec [ASSIGN single_byte_character_string] */
/* single_byte_character_string ->may be NULL ! */
void *visit(single_byte_string_spec_c *symbol) {
  symbol->string_spec->accept(*this);
  if (symbol->single_byte_character_string != NULL) {
    s4o.print(" := ");
    symbol->single_byte_character_string->accept(*this);
  }
  return NULL;
}

/*  STRING ['[' integer ']'] */
/* integer ->may be NULL ! */
void *visit(single_byte_limited_len_string_spec_c *symbol) {
  symbol->string_type_name->accept(*this);
  if (symbol->character_string_len != NULL) {
    s4o.print(" [");
    symbol->character_string_len->accept(*this);
    s4o.print("]");
  }
  return NULL;
}

/*  var1_list ':' double_byte_string_spec */
void *visit(double_byte_string_var_declaration_c *symbol) {
  symbol->var1_list->accept(*this);
  s4o.print(" : ");
  symbol->double_byte_string_spec->accept(*this);
  return NULL;
}

/*  double_byte_limited_len_string_spec [ASSIGN double_byte_character_string] */
/* integer ->may be NULL ! */
/* double_byte_character_string ->may be NULL ! */
void *visit(double_byte_string_spec_c *symbol) {
  symbol->string_spec->accept(*this);
  if (symbol->double_byte_character_string != NULL) {
    s4o.print(" := ");
    symbol->double_byte_character_string->accept(*this);
  }
  return NULL;
}

/* WSTRING ['[' integer ']'] */
/* integer ->may be NULL ! */
void *visit(double_byte_limited_len_string_spec_c *symbol) {
  symbol->string_type_name->accept(*this);
  if (symbol->character_string_len != NULL) {
    s4o.print(" [");
    symbol->character_string_len->accept(*this);
    s4o.print("]");
  }
  return NULL;
}

/*| VAR [RETAIN|NON_RETAIN] incompl_located_var_decl_list END_VAR */
/* option ->may be NULL ! */
void *visit(incompl_located_var_declarations_c *symbol) {
  s4o.print(s4o.indent_spaces); s4o.print("VAR ");
  if (symbol->option != NULL)
    symbol->option->accept(*this);
  s4o.print("\n");
  s4o.indent_right();
  symbol->incompl_located_var_decl_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n");
  return NULL;
}

/* helper symbol for incompl_located_var_declarations */
/*| incompl_located_var_decl_list incompl_located_var_decl ';' */
void *visit(incompl_located_var_decl_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}

/*  variable_name incompl_location ':' var_spec */
void *visit(incompl_located_var_decl_c *symbol) {
  symbol->variable_name->accept(*this);
  s4o.print(" ");
  symbol->incompl_location->accept(*this);
  s4o.print(" : ");
  symbol->var_spec->accept(*this);
  return NULL;
}


/*  AT incompl_location_token */
void *visit(incompl_location_c *symbol) {
  s4o.print(" AT ");
  return print_token(symbol);
}


/* intermediate helper symbol for:
 *  - non_retentive_var_decls
 *  - output_declarations
 */
/* | var_init_decl_list var_init_decl ';' */
void *visit(var_init_decl_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}


/***********************/
/* B 1.5.1 - Functions */
/***********************/
void *visit(function_declaration_c *symbol) {
  s4o.print("FUNCTION ");
  symbol->derived_function_name->accept(*this);
  s4o.print(" : ");
  symbol->type_name->accept(*this);
  s4o.print("\n");
  s4o.indent_right();
  symbol->var_declarations_list->accept(*this);
  s4o.print("\n");
  symbol->function_body->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces + "END_FUNCTION\n\n\n");
  return NULL;
}

/* intermediate helper symbol for function_declaration */
void *visit(var_declarations_list_c *symbol) {return print_list(symbol);}

void *visit(function_var_decls_c *symbol) {
  //s4o.print(s4o.indent_spaces); 
  s4o.print("VAR ");
  if (symbol->option != NULL)
    symbol->option->accept(*this);
  s4o.print("\n");
  s4o.indent_right();
  symbol->decl_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n");
  return NULL;
}

/* intermediate helper symbol for function_var_decls */
void *visit(var2_init_decl_list_c *symbol) {
  print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
  return NULL;
}

/*****************************/
/* B 1.5.2 - Function Blocks */
/*****************************/
/*  FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */
void *visit(function_block_declaration_c *symbol) {
  s4o.print("FUNCTION_BLOCK ");
  symbol->fblock_name->accept(*this);
  s4o.print("\n");
  s4o.indent_right();
  symbol->var_declarations->accept(*this);
  s4o.print("\n");
  symbol->fblock_body->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces + "END_FUNCTION_BLOCK\n\n\n");
  return NULL;
}

/*  VAR_TEMP temp_var_decl_list END_VAR */
void *visit(temp_var_decls_c *symbol) {
  s4o.print("VAR_TEMP\n");
  s4o.indent_right();
  symbol->var_decl_list->accept(*this);
  s4o.indent_left();
  s4o.print("END_VAR\n");
  return NULL;
}

/* intermediate helper symbol for temp_var_decls */
void *visit(temp_var_decls_list_c *symbol) {return print_list(symbol);}

/*  VAR NON_RETAIN var_init_decl_list END_VAR */
void *visit(non_retentive_var_decls_c *symbol) {
  s4o.print("VAR NON_RETAIN\n");
  s4o.indent_right();
  symbol->var_decl_list->accept(*this);
  s4o.indent_left();
  s4o.print("END_VAR\n");
  return NULL;
}


/**********************/
/* B 1.5.3 - Programs */
/**********************/
/*  PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM */
void *visit(program_declaration_c *symbol) {
  s4o.print("PROGRAM ");
  symbol->program_type_name->accept(*this);
  s4o.print("\n");
  s4o.indent_right();
  symbol->var_declarations->accept(*this);
  s4o.print("\n");
  symbol->function_block_body->accept(*this);
  s4o.indent_left();
  s4o.print("END_PROGRAM\n\n\n");
  return NULL;
}

/***********************************/
/* B 1.6 Sequential Function Chart */
/***********************************/

/* sequential_function_chart {sfc_network} */
void *visit(sequential_function_chart_c *symbol) {
  print_list(symbol, "", "\n", "");
  return NULL;
}

/* sfc_network {step | transition | action} */
void *visit(sfc_network_c *symbol) {
  print_list(symbol, "", "\n", "");
  return NULL;
}

/* INITIAL_STEP step_name ':' [action_association_list] END_STEP */
void *visit(initial_step_c *symbol) {
  s4o.print(s4o.indent_spaces);
  s4o.print("INITIAL_STEP ");
  symbol->step_name->accept(*this);
  s4o.print(":\n");
  s4o.indent_right();
  symbol->action_association_list->accept(*this);
  s4o.print("\n");
  s4o.indent_left();
  s4o.print(s4o.indent_spaces);
  s4o.print("END_STEP\n");
  return NULL;
}

/* STEP step_name ':' [action_association_list] END_STEP */
void *visit(step_c *symbol) {
  s4o.print(s4o.indent_spaces);
  s4o.print("STEP ");
  symbol->step_name->accept(*this);
  s4o.print(":\n");
  s4o.indent_right();
  symbol->action_association_list->accept(*this);
  s4o.print("\n");
  s4o.indent_left();
  s4o.print(s4o.indent_spaces);
  s4o.print("END_STEP\n");
  return NULL;
}

/* action_association_list {action_association} */
void *visit(action_association_list_c *symbol) {
  print_list(symbol, "", "\n", "");
  return NULL;
}

/* action_name '(' [action_qualifier] [indicator_name_list] ')' */
void *visit(action_association_c *symbol) {
  s4o.print(s4o.indent_spaces);
  symbol->action_name->accept(*this);
  s4o.print("(");
  if(symbol->action_qualifier != NULL){
    symbol->action_qualifier->accept(*this);
  }
  if(symbol->indicator_name_list != NULL){
  	symbol->indicator_name_list->accept(*this);
  }
  s4o.print(");");
  return NULL;
}

/* indicator_name_list ',' indicator_name */
void *visit(indicator_name_list_c *symbol) {
  print_list(symbol, ", ", ", ", "");
  return NULL;
}

/* action_qualifier [',' action_time] */
void *visit(action_qualifier_c *symbol) {
  symbol->action_qualifier->accept(*this);
  if(symbol->action_time != NULL){
    s4o.print(", ");
    symbol->action_time->accept(*this);
  }
  return NULL;
}

/* N | R | S | P */
void *visit(qualifier_c *symbol) {
  print_token(symbol);
  return NULL;
}

/* L | D | SD | DS | SL */
void *visit(timed_qualifier_c *symbol) {
  print_token(symbol);
  return NULL;
}

/* TRANSITION [transition_name] ['(''PRIORITY'':='integer')']
 *   FROMstepsTO steps 
 *   ':' simple_instr_list | ':=' expression
 * END_TRANSITION
 */
void *visit(transition_c *symbol) {
  s4o.print(s4o.indent_spaces);
  s4o.print("TRANSITION ");
  if (symbol->transition_name != NULL){
    symbol->transition_name->accept(*this);
    s4o.print(" ");
  }
  if (symbol->integer != NULL){
    s4o.print("(PRIORITY := ");
    symbol->integer->accept(*this);
    s4o.print(") ");
  }
  s4o.print("FROM ");
  symbol->from_steps->accept(*this);
  s4o.print(" TO ");
  symbol->to_steps->accept(*this);
  s4o.indent_right();
  symbol->transition_condition->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces);
  s4o.print("END_TRANSITION\n");
  return NULL;
}

void *visit(transition_condition_c *symbol) {
  if (symbol->transition_condition_il != NULL) {
    s4o.print(":\n");
    symbol->transition_condition_il->accept(*this);
  }
  if (symbol->transition_condition_st != NULL) {
    s4o.print("\n");
    s4o.print(s4o.indent_spaces);
    s4o.print(":= ");
    symbol->transition_condition_st->accept(*this);
    s4o.print(";\n");
  }
  return NULL;
}

/* step_name | step_name_list */
void *visit(steps_c *symbol) {
  if(symbol->step_name != NULL){
    symbol->step_name->accept(*this);
  }
  if(symbol->step_name_list != NULL){
    symbol->step_name_list->accept(*this);
  }
  return NULL;
}

/* '(' step_name ',' step_name {',' step_name} ')' */
void *visit(step_name_list_c *symbol) {
  print_list(symbol, "(", ", ", ")");
  return NULL;
}

/* ACTION action_name ':' function_block_body END_ACTION */
void *visit(action_c *symbol) {
  s4o.print(s4o.indent_spaces);
  s4o.print("ACTION ");
  symbol->action_name->accept(*this);
  s4o.print(":\n");
  s4o.indent_right();
  symbol->function_block_body->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces);
  s4o.print("END_ACTION\n");
  return NULL;
}
 
 
 
/********************************/
/* B 1.7 Configuration elements */
/********************************/
/*
CONFIGURATION configuration_name
   optional_global_var_declarations
   (resource_declaration_list | single_resource_declaration)
   optional_access_declarations
   optional_instance_specific_initializations
END_CONFIGURATION
*/
void *visit(configuration_declaration_c *symbol) {
  s4o.print("CONFIGURATION ");
  symbol->configuration_name->accept(*this);
  s4o.print("\n");
  s4o.indent_right();
  if (symbol->global_var_declarations != NULL)
    symbol->global_var_declarations->accept(*this);
  symbol->resource_declarations->accept(*this);
  if (symbol->access_declarations != NULL)
    symbol->access_declarations->accept(*this);
  if (symbol->instance_specific_initializations != NULL)
    symbol->instance_specific_initializations->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces + "END_CONFIGURATION\n\n\n");
  return NULL;
}


/* intermediate helper symbol for configuration_declaration  */
/*  { global_var_declarations_list }   */
void *visit(global_var_declarations_list_c *symbol) {return print_list(symbol);}

/* helper symbol for configuration_declaration */
/*| resource_declaration_list resource_declaration */
void *visit(resource_declaration_list_c *symbol) {return print_list(symbol);}


/*
RESOURCE resource_name ON resource_type_name
   optional_global_var_declarations
   single_resource_declaration
END_RESOURCE
*/
void *visit(resource_declaration_c *symbol) {
  s4o.print(s4o.indent_spaces + "RESOURCE ");
  symbol->resource_name->accept(*this);
  s4o.print(" ON ");
  symbol->resource_type_name->accept(*this);
  s4o.print("\n");
  s4o.indent_right();
  if (symbol->global_var_declarations != NULL)
    symbol->global_var_declarations->accept(*this);
  symbol->resource_declaration->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces + "END_RESOURCE\n");
  return NULL;
}


/* task_configuration_list program_configuration_list */
void *visit(single_resource_declaration_c *symbol) {
  symbol->task_configuration_list->accept(*this);
  symbol->program_configuration_list->accept(*this);
  return NULL;
}

/* helper symbol for single_resource_declaration */
/*| task_configuration_list task_configuration ';'*/
void *visit(task_configuration_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}


/* helper symbol for single_resource_declaration */
/*| program_configuration_list program_configuration ';'*/
void *visit(program_configuration_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}


/* helper symbol for
 *  - access_path
 *  - instance_specific_init
 */
/* | any_fb_name_list any_identifier '.'*/
void *visit(any_fb_name_list_c *symbol) {return print_list(symbol, ".", ".");}


/*  [resource_name '.'] global_var_name ['.' structure_element_name] */
void *visit(global_var_reference_c *symbol) {
  if (symbol->resource_name != NULL) {
    symbol->resource_name->accept(*this);
    s4o.print(".");
  }
  symbol->global_var_name->accept(*this);
  if (symbol->structure_element_name != NULL) {
    s4o.print(".");
    symbol->structure_element_name->accept(*this);
  }
  return NULL;
}

/* program_name '.' symbolic_variable */
void *visit(program_output_reference_c *symbol) {
  symbol->program_name->accept(*this);
  s4o.print(".");
  symbol->symbolic_variable->accept(*this);
  return NULL;
}

/*  TASK task_name task_initialization */
void *visit(task_configuration_c *symbol) {
  s4o.print("TASK ");
  symbol->task_name->accept(*this);
  s4o.print(" ");
  symbol->task_initialization->accept(*this);
  return NULL;
}

/*  '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' */
void *visit(task_initialization_c *symbol) {
  s4o.print("(");
  if (symbol->single_data_source != NULL) {
    s4o.print("SINGLE := ");
    symbol->single_data_source->accept(*this);
    s4o.print(", ");
  }
  if (symbol->interval_data_source != NULL) {
    s4o.print("INTERVAL := ");
    symbol->interval_data_source->accept(*this);
    s4o.print(", ");
  }
  s4o.print("PRIORITY := ");
  symbol->priority_data_source->accept(*this);
  s4o.print(")");
  return NULL;
}

/*  PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] */
void *visit(program_configuration_c *symbol) {
  s4o.print("PROGRAM ");
  if (symbol->retain_option != NULL)
    symbol->retain_option->accept(*this);
  symbol->program_name->accept(*this);
  if (symbol->task_name != NULL) {
    s4o.print(" WITH ");
    symbol->task_name->accept(*this);
  }
  s4o.print(" : ");
  symbol->program_type_name->accept(*this);
  if (symbol->prog_conf_elements != NULL) {
    s4o.print("(");
    symbol->prog_conf_elements->accept(*this);
    s4o.print(")");
  }
  return NULL;
}


/* prog_conf_elements ',' prog_conf_element */
void *visit(prog_conf_elements_c *symbol) {return print_list(symbol, "", ", ");}

/*  fb_name WITH task_name */
void *visit(fb_task_c *symbol) {
  symbol->fb_name->accept(*this);
  s4o.print(" WITH ");
  symbol->task_name->accept(*this);
  return NULL;
}

/*  any_symbolic_variable ASSIGN prog_data_source */
void *visit(prog_cnxn_assign_c *symbol) {
  symbol->symbolic_variable->accept(*this);
  s4o.print(" := ");
  symbol->prog_data_source->accept(*this);
  return NULL;
}

/* any_symbolic_variable SENDTO data_sink */
void *visit(prog_cnxn_sendto_c *symbol) {
  symbol->symbolic_variable->accept(*this);
  s4o.print(" => ");
  symbol->data_sink->accept(*this);
  return NULL;
}

/* VAR_CONFIG instance_specific_init_list END_VAR */
void *visit(instance_specific_initializations_c *symbol) {
  s4o.print(s4o.indent_spaces + "VAR_CONFIG\n");
  s4o.indent_right();
  symbol->instance_specific_init_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces + "END_VAR\n");
  return NULL;
}

/* helper symbol for instance_specific_initializations */
/*| instance_specific_init_list instance_specific_init ';'*/
void *visit(instance_specific_init_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}

/* resource_name '.' program_name '.' optional_fb_name_list '.'
    ((variable_name [location] ':' located_var_spec_init) | (fb_name ':' fb_initialization))
*/
void *visit(instance_specific_init_c *symbol) {
  symbol->resource_name->accept(*this);
  s4o.print(".");
  symbol->program_name->accept(*this);
  symbol->any_fb_name_list->accept(*this);
  if (symbol->variable_name != NULL) {
    s4o.print(".");
    symbol->variable_name->accept(*this);
  }
  if (symbol->location != NULL) {
    s4o.print(" ");
    symbol->location->accept(*this);
  }
  s4o.print(" : ");
  symbol->initialization->accept(*this);
  return NULL;
}

/* helper symbol for instance_specific_init */
/* function_block_type_name ':=' structure_initialization */
void *visit(fb_initialization_c *symbol) {
  symbol->function_block_type_name->accept(*this);
  s4o.print(" := ");
  symbol->structure_initialization->accept(*this);
  return NULL;
}


/***********************************/
/* B 2.1 Instructions and Operands */
/***********************************/
/*| instruction_list il_instruction */
void *visit(instruction_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, "\n" + s4o.indent_spaces, "\n");
}

/* | label ':' [il_incomplete_instruction] eol_list */
void *visit(il_instruction_c *symbol) {
  if (symbol->label != NULL) {
    symbol->label->accept(*this);
    s4o.print(": ");
  }
  if (symbol->il_instruction != NULL) {
    symbol->il_instruction->accept(*this);
  }  
  return NULL;
}


/* | il_simple_operator [il_operand] */
void *visit(il_simple_operation_c *symbol) {
  symbol->il_simple_operator->accept(*this);
  if (symbol->il_operand != NULL)
    symbol->il_operand->accept(*this);
  return NULL;
}

/* | function_name [il_operand_list] */
void *visit(il_function_call_c *symbol) {
  symbol->function_name->accept(*this);
  s4o.print(" ");
  if (symbol->il_operand_list != NULL)
    symbol->il_operand_list->accept(*this);
  return NULL;
}


/* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */
void *visit(il_expression_c *symbol) {
  /* Since stage2 will insert an artificial (and equivalent) LD <il_operand> to the simple_instr_list when an 'il_operand' exists, we know
   * that if (symbol->il_operand != NULL), then the first IL instruction in the simple_instr_list will be the equivalent and artificial
   * 'LD <il_operand>' IL instruction.
   * Since we do not want the extra LD instruction, we simply remove it!
   */
  if (symbol->il_operand != NULL)
    ((list_c *)symbol->simple_instr_list)->remove_element(0);

  symbol->il_expr_operator->accept(*this);
  s4o.print("(");
  if (symbol->il_operand != NULL)
    symbol->il_operand->accept(*this);
  if (symbol->simple_instr_list != NULL) {
    s4o.print("\n");
    s4o.indent_right();
    symbol->simple_instr_list->accept(*this);
    s4o.print(s4o.indent_spaces);
    s4o.indent_left();
  }
  s4o.print(")");
  return NULL;
}

/*  il_jump_operator label */
void *visit(il_jump_operation_c *symbol) {
  symbol->il_jump_operator->accept(*this);
  symbol->label->accept(*this);
  return NULL;
}

/*   il_call_operator prev_declared_fb_name
 * | il_call_operator prev_declared_fb_name '(' ')'
 * | il_call_operator prev_declared_fb_name '(' eol_list ')'
 * | il_call_operator prev_declared_fb_name '(' il_operand_list ')'
 * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')'
 */
void *visit(il_fb_call_c *symbol) {
  symbol->il_call_operator->accept(*this);
  symbol->fb_name->accept(*this);
  s4o.print("(");
  if (symbol->il_operand_list != NULL)
    symbol->il_operand_list->accept(*this);
  if (symbol->il_param_list != NULL) {
    s4o.print("\n");
    s4o.indent_right();
    symbol->il_param_list->accept(*this);
    s4o.indent_left();
  }
  s4o.print(")");
  return NULL;
}


/* | function_name '(' eol_list [il_param_list] ')' */
void *visit(il_formal_funct_call_c *symbol) {
  symbol->function_name->accept(*this);
  s4o.print("(");
  s4o.print("\n");
  if (symbol->il_param_list != NULL) {
    s4o.indent_right();
    symbol->il_param_list->accept(*this);
    s4o.print(")");
    s4o.indent_left();
  }
  return NULL;
}


/* | il_operand_list ',' il_operand */
void *visit(il_operand_list_c *symbol) {
  return print_list(symbol, "", ", ");
}

/* | simple_instr_list il_simple_instruction */
void *visit(simple_instr_list_c *symbol) {
  return print_list(symbol,  s4o.indent_spaces, "\n" + s4o.indent_spaces, "\n");
}


/* il_simple_instruction:
 *   il_simple_operation eol_list
 * | il_expression eol_list
 * | il_formal_funct_call eol_list
 */
void *visit(il_simple_instruction_c *symbol)	{
  return symbol->il_simple_instruction->accept(*this);
}


/* | il_initial_param_list il_param_instruction */
void *visit(il_param_list_c *symbol) {
// return print_list(symbol);
  return print_list(symbol,  s4o.indent_spaces, ",\n" + s4o.indent_spaces, "\n" + s4o.indent_spaces);
}

/*  il_assign_operator il_operand
 * | il_assign_operator '(' eol_list simple_instr_list ')'
 */
void *visit(il_param_assignment_c *symbol) {
  symbol->il_assign_operator->accept(*this);
  s4o.print(" := ");
  if (symbol->il_operand != NULL)
    symbol->il_operand->accept(*this);
  if (symbol->simple_instr_list != NULL) {
    s4o.print("(\n");
    s4o.indent_right();
    symbol->simple_instr_list->accept(*this);
    s4o.print(s4o.indent_spaces);
    s4o.print(")");
    s4o.indent_left();
  }
  return NULL;
}

/*  il_assign_out_operator variable */
void *visit(il_param_out_assignment_c *symbol) {
  symbol->il_assign_out_operator->accept(*this);
  s4o.print(" => ");
  symbol->variable->accept(*this);
  return NULL;
}


/*******************/
/* B 2.2 Operators */
/*******************/
void *visit(LD_operator_c *symbol) {s4o.print("LD "); return NULL;}
void *visit(LDN_operator_c *symbol) {s4o.print("LDN "); return NULL;}
void *visit(ST_operator_c *symbol) {s4o.print("ST "); return NULL;}
void *visit(STN_operator_c *symbol) {s4o.print("STN "); return NULL;}
void *visit(NOT_operator_c *symbol) {s4o.print("NOT "); return NULL;}
void *visit(S_operator_c *symbol) {s4o.print("S "); return NULL;}
void *visit(R_operator_c *symbol) {s4o.print("R "); return NULL;}
void *visit(S1_operator_c *symbol) {s4o.print("S1 "); return NULL;}
void *visit(R1_operator_c *symbol) {s4o.print("R1 "); return NULL;}
void *visit(CLK_operator_c *symbol) {s4o.print("CLK "); return NULL;}
void *visit(CU_operator_c *symbol) {s4o.print("CU "); return NULL;}
void *visit(CD_operator_c *symbol) {s4o.print("CD "); return NULL;}
void *visit(PV_operator_c *symbol) {s4o.print("PV "); return NULL;}
void *visit(IN_operator_c *symbol) {s4o.print("IN "); return NULL;}
void *visit(PT_operator_c *symbol) {s4o.print("PT "); return NULL;}
void *visit(AND_operator_c *symbol) {s4o.print("AND "); return NULL;}
void *visit(OR_operator_c *symbol) {s4o.print("OR "); return NULL;}
void *visit(XOR_operator_c *symbol) {s4o.print("XOR "); return NULL;}
void *visit(ANDN_operator_c *symbol) {s4o.print("ANDN "); return NULL;}
void *visit(ORN_operator_c *symbol) {s4o.print("ORN "); return NULL;}
void *visit(XORN_operator_c *symbol) {s4o.print("XORN "); return NULL;}
void *visit(ADD_operator_c *symbol) {s4o.print("ADD "); return NULL;}
void *visit(SUB_operator_c *symbol) {s4o.print("SUB "); return NULL;}
void *visit(MUL_operator_c *symbol) {s4o.print("MUL "); return NULL;}
void *visit(DIV_operator_c *symbol) {s4o.print("DIV "); return NULL;}
void *visit(MOD_operator_c *symbol) {s4o.print("MOD "); return NULL;}
void *visit(GT_operator_c *symbol) {s4o.print("GT "); return NULL;}
void *visit(GE_operator_c *symbol) {s4o.print("GE "); return NULL;}
void *visit(EQ_operator_c *symbol) {s4o.print("EQ "); return NULL;}
void *visit(LT_operator_c *symbol) {s4o.print("LT "); return NULL;}
void *visit(LE_operator_c *symbol) {s4o.print("LE "); return NULL;}
void *visit(NE_operator_c *symbol) {s4o.print("NE "); return NULL;}
void *visit(CAL_operator_c *symbol) {s4o.print("CAL "); return NULL;}
void *visit(CALC_operator_c *symbol) {s4o.print("CALC "); return NULL;}
void *visit(CALCN_operator_c *symbol) {s4o.print("CALCN "); return NULL;}
void *visit(RET_operator_c *symbol) {s4o.print("RET "); return NULL;}
void *visit(RETC_operator_c *symbol) {s4o.print("RETC "); return NULL;}
void *visit(RETCN_operator_c *symbol) {s4o.print("RETCN "); return NULL;}
void *visit(JMP_operator_c *symbol) {s4o.print("JMP "); return NULL;}
void *visit(JMPC_operator_c *symbol) {s4o.print("JMPC "); return NULL;}
void *visit(JMPCN_operator_c *symbol) {s4o.print("JMPCN "); return NULL;}

/*| any_identifier ASSIGN */
void *visit(il_assign_operator_c *symbol) {
  symbol->variable_name->accept(*this);
  s4o.print(" := ");
  return NULL;
}

/*| [NOT] any_identifier SENDTO */
void *visit(il_assign_out_operator_c *symbol) {
  if (symbol->option != NULL)
    symbol->option->accept(*this);
  symbol->variable_name->accept(*this);
  s4o.print(" => ");
  return NULL;
}


/***********************/
/* B 3.1 - Expressions */
/***********************/
void *visit( deref_operator_c   *symbol) {return symbol->exp->accept(*this); s4o.print("^");}                      /* an extension to the IEC 61131-3 standard - based on the IEC 61131-3 v3 standard. ^      -> dereferences an address into a variable! */
void *visit( deref_expression_c *symbol) {return  s4o.print("DREF("); symbol->exp->accept(*this); s4o.print(")");} /* an extension to the IEC 61131-3 standard - based on the IEC 61131-3 v3 standard. DREF() -> dereferences an address into a variable! */
void *visit(   ref_expression_c *symbol) {return  s4o.print( "REF("); symbol->exp->accept(*this); s4o.print(")");} /* an extension to the IEC 61131-3 standard - based on the IEC 61131-3 v3 standard. REF()  -> returns address of the varible! */
void *visit(    or_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " OR ");}
void *visit(   xor_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " XOR ");}
void *visit(   and_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " AND ");}
void *visit(   equ_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " = ");}
void *visit(notequ_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " <> ");}
void *visit(    lt_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " < ");}
void *visit(    gt_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " > ");}
void *visit(    le_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " <= ");}
void *visit(    ge_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " >= ");}
void *visit(   add_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " + ");}
void *visit(   sub_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " - ");}
void *visit(   mul_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " * ");}
void *visit(   div_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " / ");}
void *visit(   mod_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " MOD ");}
void *visit( power_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " ** ");}
void *visit(   neg_expression_c *symbol) {return print_unary_expression (symbol, symbol->exp, "-");}
void *visit(   not_expression_c *symbol) {return print_unary_expression (symbol, symbol->exp, "NOT ");}

void *visit(function_invocation_c *symbol) {
  symbol->function_name->accept(*this);
  s4o.print("(");

  /* If the syntax parser is working correctly, exactly one of the 
   * following two symbols will be NULL, while the other is != NULL.
   */
  if (symbol->   formal_param_list != NULL) symbol->   formal_param_list->accept(*this);
  if (symbol->nonformal_param_list != NULL) symbol->nonformal_param_list->accept(*this);

  s4o.print(")");
  return NULL;
}

/********************/
/* B 3.2 Statements */
/********************/
void *visit(statement_list_c *symbol) {
  return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n");
}

/*********************************/
/* B 3.2.1 Assignment Statements */
/*********************************/
void *visit( assignment_statement_c *symbol) {
  symbol->l_exp->accept(*this);
  s4o.print(" := ");
  symbol->r_exp->accept(*this);
  return NULL;
}

/*****************************************/
/* B 3.2.2 Subprogram Control Statements */
/*****************************************/

/*  RETURN */
void *visit(return_statement_c *symbol) {
  s4o.print("RETURN");
  return NULL;
}


/* fb_name '(' [param_assignment_list] ')' */
void *visit(fb_invocation_c *symbol) {
  symbol->fb_name->accept(*this);
  s4o.print("(");
  /* If the syntax parser is working correctly, at most one of the 
   * following two symbols will be NULL, while the other is != NULL.
   * The two may be NULL simultaneously!
   */
  if (symbol->   formal_param_list != NULL) symbol->   formal_param_list->accept(*this);
  if (symbol->nonformal_param_list != NULL) symbol->nonformal_param_list->accept(*this);

  s4o.print(")");
  return NULL;
}


/* helper symbol for fb_invocation */
/* param_assignment_list ',' param_assignment */
void *visit(param_assignment_list_c *symbol) {return print_list(symbol, "", ", ");}


void *visit(input_variable_param_assignment_c *symbol) {
  symbol->variable_name->accept(*this);
  s4o.print(" := ");
  symbol->expression->accept(*this);
  return NULL;
}


void *visit(output_variable_param_assignment_c *symbol) {
  if (symbol->not_param != NULL)
    symbol->not_param->accept(*this);
  symbol->variable_name->accept(*this);
  s4o.print(" => ");
  symbol->variable->accept(*this);
  return NULL;
}


void *visit(not_paramassign_c *symbol) {
  s4o.print("NOT ");
  return NULL;
}


/********************************/
/* B 3.2.3 Selection Statements */
/********************************/
void *visit(if_statement_c *symbol) {
  s4o.print("IF ");
  symbol->expression->accept(*this);
  s4o.print(" THEN\n");
  s4o.indent_right();
  symbol->statement_list->accept(*this);
  s4o.indent_left();
  symbol->elseif_statement_list->accept(*this);

  if (symbol->else_statement_list != NULL) {
    s4o.print(s4o.indent_spaces); s4o.print("ELSE\n");
    s4o.indent_right();
    symbol->else_statement_list->accept(*this);
    s4o.indent_left();
  }
  s4o.print(s4o.indent_spaces); s4o.print("END_IF");
  return NULL;
}

/* helper symbol for if_statement */
void *visit(elseif_statement_list_c *symbol) {return print_list(symbol);}

/* helper symbol for elseif_statement_list */
void *visit(elseif_statement_c *symbol) {
  s4o.print(s4o.indent_spaces); s4o.print("ELSIF ");
  symbol->expression->accept(*this);
  s4o.print(s4o.indent_spaces); s4o.print("THEN\n");
  s4o.indent_right();
  symbol->statement_list->accept(*this);
  s4o.indent_left();
  return NULL;
}

void *visit(case_statement_c *symbol) {
  s4o.print("CASE ");
  symbol->expression->accept(*this);
  s4o.print(" OF\n");
  s4o.indent_right();
  symbol->case_element_list->accept(*this);
  if (symbol->statement_list != NULL) {
    s4o.print(s4o.indent_spaces + "ELSE\n");
    s4o.indent_right();
    symbol->statement_list->accept(*this);
    s4o.indent_left();
  }
  s4o.indent_left();
  s4o.print(s4o.indent_spaces + "END_CASE");
  return NULL;
}

/* helper symbol for case_statement */
void *visit(case_element_list_c *symbol) {
  return print_list(symbol);
}

void *visit(case_element_c *symbol) {
  s4o.print(s4o.indent_spaces);
  symbol->case_list->accept(*this);
  s4o.print(":\n");
  s4o.indent_right();
  symbol->statement_list->accept(*this);
  s4o.indent_left();
  return NULL;
}

void *visit(case_list_c *symbol) {return print_list(symbol, "", ", ");}

/********************************/
/* B 3.2.4 Iteration Statements */
/********************************/
void *visit(for_statement_c *symbol) {
  s4o.print("FOR ");
  symbol->control_variable->accept(*this);
  s4o.print(" := ");
  symbol->beg_expression->accept(*this);
  s4o.print(" TO ");
  symbol->end_expression->accept(*this);
  if (symbol->by_expression != NULL) {
    s4o.print(" BY ");
    symbol->by_expression->accept(*this);
  }
  s4o.print(" DO\n");
  s4o.indent_right();
  symbol->statement_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces); s4o.print("END_FOR");
  return NULL;
}

void *visit(while_statement_c *symbol) {
  s4o.print("WHILE ");
  symbol->expression->accept(*this);
  s4o.print(" DO\n");
  s4o.indent_right();
  symbol->statement_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces); s4o.print("END_WHILE");
  return NULL;
}

void *visit(repeat_statement_c *symbol) {
  s4o.print("REPEAT\n");
  s4o.indent_right();
  symbol->statement_list->accept(*this);
  s4o.indent_left();
  s4o.print(s4o.indent_spaces); s4o.print("UNTIL ");
  symbol->expression->accept(*this);
  s4o.print("\n" + s4o.indent_spaces + "END_REPEAT");
  return NULL;
}

void *visit(exit_statement_c *symbol) {
  s4o.print("EXIT");
  return NULL;
}


}; /* class generate_iec_c */



/***********************************************************************/
/***********************************************************************/
/***********************************************************************/
/***********************************************************************/
/***********************************************************************/
/***********************************************************************/
/***********************************************************************/
/***********************************************************************/




visitor_c *new_code_generator(stage4out_c *s4o, const char *builddir)  {return new generate_iec_c(s4o);}
void delete_code_generator(visitor_c *code_generator) {delete code_generator;}















