stage4/generate_iec/generate_iec.cc
author Andrey Skvortsov <andrej.skvortzov@gmail.com>
Sun, 14 Oct 2018 20:14:13 +0300
changeset 1073 24ef30a9bcee
parent 1041 56ebe2a31b5b
permissions -rw-r--r--
revert commits improved performance of some extensible Standard Functions (ADD, MUL, AND, OR, XOR)

Following commits are reverted:
mjsousa 0b275a2 improve performance of some extensible Standard Functions (ADD, MUL, AND, OR, XOR) -- increase hardcoded limit to 499
mjsousa 2228799 improve performance of some extensible Standard Functions (ADD, MUL, AND, OR, XOR) -- Add comments!!
mjsousa ce81fa6 improve performance of some extensible Standard Functions (ADD, MUL, AND, OR, XOR)"

The reason is that they cause regression in some cases (if function is
used as argument for function block, for example) and this is not
fixed for a long time.
/*
 *  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->get_element(0)->accept(*this);
  }

  for(int i = 1; i < list->n; i++) {
    s4o.print(inter_elem_str);
    list->get_element(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(       void_type_name_c *symbol) {s4o.print("VOID");        return NULL;} /* a non-standard extension! */

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