stage1_2/iec_bison.yy
author Andrey Skvortsov <andrej.skvortzov@gmail.com>
Sun, 14 Oct 2018 20:14:13 +0300
changeset 1073 24ef30a9bcee
parent 1053 e94368340160
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)
 *
 */

/*
 * Stage 2
 * =======
 *
 *  This file contains the syntax definition of the textual
 *  languages IL and ST, as well as the textual version of SFC.
 *  The syntax parser, comprising the 2nd stage of the overall 
 *  compiler, is generated by runing bison on this file.
 */




/**********************************************************************/
/**********************************************************************/
/**********************************************************************/
/**********************************************************************/
/*******                                                        *******/
/*******   The following syntax does not have any conflicts.    *******/
/*******                                                        *******/
/*******   P L E A S E    K E E P    I T    T H A T    W A Y !  *******/
/*******   ===================================================  *******/
/*******                                                        *******/
/**********************************************************************/
/**********************************************************************/
/**********************************************************************/
/**********************************************************************/

/* NOTE: the following file contains many rules used merely for detecting errors in
 * the IEC source code being parsed.
 * To remove all these rules, simply execute the command (first replace all '%' with '/'):
 * $sed '\:%\* ERROR_CHECK_BEGIN \*%:,\:%\* ERROR_CHECK_END \*%: d' iec_bison.yy
 *
 * The above command had to be edited ('/' replaced by '%') so as not to include the C syntax that closes
 * comments inside this comment!
 * If you place the command in a shell script, be sure to remove the backslashes '\' before each asterisk '*' !!
 */





%{
#include <string.h>	/* required for strdup()  */


/* declare the token parser generated by flex... */
int yylex(void);

/* declare the error handler defined at the end of this file */
void yyerror (const char *error_msg);

/* produce a more verbose parsing error message */
#define YYERROR_VERBOSE

/* Include debuging code.
 * Printing of debug info must then be activated by setting
 * the variable yydebug to 1.
 */
#define YYDEBUG 0


/* file with declaration of absyntax classes... */
#include "../absyntax/absyntax.hh"

/* file with declaration of token constants. Generated by bison! */
#include "iec_bison.hh"

/* The interface through which bison and flex interact. */
#include "stage1_2_priv.hh"
#include "create_enumtype_conversion_functions.hh"

#include "../absyntax_utils/add_en_eno_param_decl.hh"	/* required for  add_en_eno_param_decl_c */

/* an ugly hack!!
 * We will probably not need it when we decide
 *  to cut down the abstract syntax down to size.
 *  We keep it as it is until we get to write
 *  stages 3 and 4 of the compiler. Who knows,
 *  we might just find out that we really do need
 *  the abstract syntax tree to stay as it is
 *  afterall!
 */
/* for each element <elem> in list_c * <list>
 * execute the code <code>
 */
#define FOR_EACH_ELEMENT(elem, list, code) {		\
  symbol_c *elem;					\
  for(int i = 0; i < list->n; i++) {			\
    elem = list->get_element(i);			\
    code;						\
  }							\
}



/* Macros used to pass the line and column locations when
 * creating a new object for the abstract syntax tree.
 */
#define locloc(foo) foo.first_line, foo.first_column, foo.first_file, foo.first_order, foo.last_line, foo.last_column, foo.last_file, foo.last_order
#define   locf(foo) foo.first_line, foo.first_column, foo.first_file, foo.first_order
#define   locl(foo) foo.last_line,  foo.last_column,  foo.last_file,  foo.last_order

/* Redefine the default action to take for each rule, so that the filenames are correctly processed... */
# define YYLLOC_DEFAULT(Current, Rhs, N)                                \
         do                                                                  \
           if (N)                                                            \
             {                                                               \
               (Current).first_line   = YYRHSLOC(Rhs, 1).first_line;         \
               (Current).first_column = YYRHSLOC(Rhs, 1).first_column;       \
               (Current).first_file   = YYRHSLOC(Rhs, 1).first_file;         \
               (Current).first_order  = YYRHSLOC(Rhs, 1).first_order;        \
               (Current).last_line    = YYRHSLOC(Rhs, N).last_line;          \
               (Current).last_column  = YYRHSLOC(Rhs, N).last_column;        \
               (Current).last_file    = YYRHSLOC(Rhs, 1).last_file;          \
               (Current).last_order   = YYRHSLOC(Rhs, 1).last_order;         \
             }                                                               \
           else                                                              \
             {                                                               \
               (Current).first_line   = (Current).last_line   =              \
                 YYRHSLOC(Rhs, 0).last_line;                                 \
               (Current).first_column = (Current).last_column =              \
                 YYRHSLOC(Rhs, 0).last_column;                               \
               (Current).first_file   = (Current).last_file   =              \
                 YYRHSLOC(Rhs, 0).last_file;                                 \
               (Current).first_order  = (Current).last_order  =              \
                 YYRHSLOC(Rhs, 0).last_order;                                \
             }                                                               \
         while (0)


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



/*************************/
/* global variables...   */
/*************************/
/* NOTE: For some strange reason bison ver 2.3 is including these declarations
 *       in the iec_bison.hh file, which is in turn included by flex.
 *       We cannot therefore define any variables over here, but merely declare 
 *       their existance (otherwise we get errors when linking the code, since we
 *       would get a new variable defined each time iec_bison.hh is included!).
 *       Even though the variables are declared 'extern' over here, they will in
 *       fact be defined towards the end of this same file (i.e. in the prologue)
 */


/* NOTE: These variable are really parameters we would like the stage2__ function to pass
 *       to the yyparse() function. However, the yyparse() function is created automatically
 *       by bison, so we cannot add parameters to this function. The only other
 *       option is to use global variables! yuck!
 */

/* A global flag used to tell the parser if overloaded funtions should be allowed.
 * The IEC 61131-3 standard allows overloaded funtions in the standard library,
 * but disallows them in user code...
 */
extern bool allow_function_overloading;

/* A flag to tell the compiler whether to allow the declaration
 * of extensible function (i.e. functions that may have a variable number of
 * input parameters, such as AND(word#33, word#44, word#55, word#66).
 * This is an extension to the standard syntax.
 * See comments below for details why we support this!
 */
extern bool allow_extensible_function_parameters;

/* A global flag used to tell the parser whether to allow use of DREF and '^' operators (defined in IEC 61131-3 v3) */
extern bool allow_ref_dereferencing;

/* A global flag used to tell the parser whether to allow use of REF_TO ANY datatypes (non-standard extension to IEC 61131-3 v3) */
extern bool allow_ref_to_any;

/* A global flag used to tell the parser whether to allow use of REF_TO as a struct or array element (non-standard extension) */
extern bool allow_ref_to_in_derived_datatypes;

/* A pointer to the root of the parsing tree that will be generated  by bison. */
extern symbol_c *tree_root;



/************************/
/* forward declarations */
/************************/
/* The functions declared here are defined at the end of this file... */

/* Convert an il_operator_c into an identifier_c */
identifier_c         *il_operator_c_2_identifier_c        (symbol_c *il_operator);
/* Convert an il_operator_c into an poutype_identifier_c */
poutype_identifier_c *il_operator_c_2_poutype_identifier_c(symbol_c *il_operator);


/* return if current token is a syntax element */
/* ERROR_CHECK_BEGIN */
bool is_current_syntax_token();
/* ERROR_CHECK_END */

/* print an error message */
void print_err_msg(int first_line,
                   int first_column,
                   const char *first_filename,
                   long int first_order,
                   int last_line,
                   int last_column,
                   const char *last_filename,
                   long int last_order,
                   const char *additional_error_msg);    
%}




// %glr-parser
// %expect-rr 1


/* The following definitions need to be inside a '%code requires' 
 * so that they are also included in the header files. If this were not the case,
 * YYLTYPE would be delcared as something in the iec.cc file, and another thing
 * (actually the default value of YYLTYPE) in the iec_bison.hh heder file.
 */
%code requires {
/* define a new data type to store the locations, so we can also store
 * the filename in which the token is expressed.
 */
/* NOTE: since this code will be placed in the iec_bison.hh header file,
 * as well as the iec.cc file that also includes the iec_bison.hh header file,
 * declaring the typedef struct yyltype__local here would result in a 
 * compilation error when compiling iec.cc, as this struct would be
 * declared twice.
 * We therefore use the #if !defined YYLTYPE ...
 * to make sure only the first declaration is parsed by the C++ compiler.
 */
#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
typedef struct YYLTYPE {
    int         first_line;
    int         first_column;
    const char *first_file;
    long int    first_order;
    int         last_line;
    int         last_column;
    const char *last_file;
    long int    last_order;
} YYLTYPE;
#define YYLTYPE_IS_DECLARED 1
#define YYLTYPE_IS_TRIVIAL 0
#endif

}



%union {
    symbol_c 	*leaf;
    list_c	*list;
    char 	*ID;	/* token value */
}

/*
 TODO: DO we need to define a destructor do free
       memory when recovering from errors, or do the
       class destructors already handle this?
       Following is example on how to define
       detructors, using the syntax:
       %destructor { CODE } SYMBOLS
%union
      {
        char *string;
      }
      %token <string> STRING
      %type  <string> string
      %destructor { free ($$); } STRING string
*/




/*************************************/
/* Prelimenary helpful constructs... */
/*************************************/
/* A token used to identify the very end of the input file
 * after all includes have already been processed.
 *
 * Flex automatically returns the token with value 0
 * at the end of the file. We therefore specify here
 * a token with that exact same value here, so we can use it
 * to detect the very end of the input files.
 */
%token END_OF_INPUT 0

/* A bogus token that, in principle, flex MUST NEVER generate */
/* USE 1:
 * ======
 * This token is currently also being used as the default
 * initialisation value of the token_id member in
 * the symbol_c base class.
 *
 * USE 2
 * =====
 * This token may also be used in the future to remove
 * mysterious reduce/reduce conflicts due to the fact
 * that our grammar may not be LALR(1) but merely LR(1).
 * This means that bison cannot handle it without some
 * caoxing from ourselves. We will then need this token
 * to do the coaxing...
 */
%token BOGUS_TOKEN_ID

%type <leaf>	start

%type <leaf>	any_identifier

%token <ID>	prev_declared_variable_name_token
%token <ID>	prev_declared_direct_variable_token
%token <ID>	prev_declared_fb_name_token
%type <leaf>	prev_declared_variable_name
%type <leaf>	prev_declared_direct_variable
%type <leaf>	prev_declared_fb_name

%token  <ID>	prev_declared_simple_type_name_token
%token  <ID>	prev_declared_subrange_type_name_token
%token  <ID>	prev_declared_enumerated_type_name_token
%token  <ID>	prev_declared_array_type_name_token
%token  <ID>	prev_declared_structure_type_name_token
%token  <ID>	prev_declared_string_type_name_token
%token  <ID>	prev_declared_ref_type_name_token  /* defined in IEC 61131-3 v3 */

%type  <leaf>	prev_declared_simple_type_name
%type  <leaf>	prev_declared_subrange_type_name
%type  <leaf>	prev_declared_enumerated_type_name
%type  <leaf>	prev_declared_array_type_name
%type  <leaf>	prev_declared_structure_type_name
%type  <leaf>	prev_declared_string_type_name
%type  <leaf>	prev_declared_ref_type_name  /* defined in IEC 61131-3 v3 */

%token <ID>	prev_declared_derived_function_name_token
%token <ID>	prev_declared_derived_function_block_name_token
%token <ID>	prev_declared_program_type_name_token
%type  <leaf>	prev_declared_derived_function_name
%type  <leaf>	prev_declared_derived_function_block_name
%type  <leaf>	prev_declared_program_type_name

/* Tokens used to help resolve a reduce/reduce conflict */
/* The mentioned conflict only arises due to a non-standard feature added to matiec.
 * Namely, the permission to call functions returning VOID as an ST statement.
 *   e.g.:   FUNCTION foo: VOID
 *             VAR_INPUT i: INT; END_VAR;
 *             ...
 *           END_FUNCTION
 *
 *           FUNCTION BAR: BOOL
 *             VAR b: bool; END_VAR
 *             foo(i:=42);   <--- Calling foo outside an expression. Function invocation is considered an ST statement!!
 *           END_FUNCTION
 *
 *  The above function invocation may also be reduced to a formal IL function invocation, so we get a 
 *  reduce/reduce conflict to st_statement_list/instruction_list  (or something equivalent).
 *
 *  We solve this by having flex determine if it is ST or IL invocation (ST ends with a ';' !!).
 *  At the start of a function/FB/program body, flex will tell bison whether to expect ST or IL code!
 *  This is why we need the following two tokens!
 *
 *  NOTE: flex was already determing whther it was parsing ST or IL code as it can only send 
 *        EOL tokens when parsing IL. However, did this silently without telling bison about this.
 *        Now, it does
 */
%token          start_ST_body_token
%token          start_IL_body_token



/**********************************************************************************/
/* B XXX - Things that are missing from the standard, but should have been there! */
/**********************************************************************************/

/* Pragmas that our compiler will accept.
 * See the comment in iec.flex for why these pragmas exist. 
 */
%token          disable_code_generation_pragma_token
%token          enable_code_generation_pragma_token
%type <leaf>	disable_code_generation_pragma
%type <leaf>	enable_code_generation_pragma


/* All other pragmas that we do not support... */
/* In most stage 4, the text inside the pragmas will simply be copied to the output file.
 * This allows us to insert C code (if using stage 4 generating C code) 
 * inside/interningled with the IEC 61131-3 code!
 */
%token <ID>	pragma_token
%type <leaf>	pragma

/* The joining of all previous pragmas, i.e. any possible pragma */
%type <leaf>	any_pragma


/* Where do these tokens belong?? They are missing from the standard! */
/* NOTE: There are other tokens related to these 'EN' ENO', that are also 
 * missing from the standard. However, their location in the annex B is 
 * relatively obvious, so they have been inserted in what seems to us their 
 * correct place in order to ease understanding of the parser...
 *
 * please read the comment above the definition of 'variable' in section B1.4 for details.
 */
%token	EN
%token	ENO
%type <leaf>	en_identifier
%type <leaf>	eno_identifier

/* Keywords in IEC 61131-3 v3 */
%token	REF
%token	DREF
%token	REF_TO
%token	NULL_token  /* cannot use simply 'NULL', as it conflicts with the NULL keyword in C++ */



/***************************/
/* B 0 - Programming Model */
/***************************/
%type <list>	library
%type <leaf>	library_element_declaration


/*******************************************/
/* B 1.1 - Letters, digits and identifiers */
/*******************************************/
/* Done totally within flex...
  letter
  digit
  octal_digit
  hex_digit
*/
%token <ID>	identifier_token
%type  <leaf>	identifier

/*********************/
/* B 1.2 - Constants */
/*********************/
%type <leaf>	constant
%type <leaf>	non_int_or_real_constant

/*********************************/
/* B 1.2.XX - Reference Literals */
/*********************************/
/* NOTE: The following syntax was added by MJS in order to add support for the NULL keyword, defined in  IEC 61131-3 v3
 *       In v3 expressions that reduce to a reference datatype (REF_TO) are handled explicitly in the syntax 
 *       (e.g., any variable that is a of reference datatpe falls under the 'ref_name' rule), which means 
 *       that we would need to keep track of which variables are declared as REF_TO. 
 *       In order to reduce the changes to the current IEC 61131-3 v2 syntax, I have opted not to do this,
 *       and simply let the ref_expressions (Ref_Assign, Ref_Compare) be interpreted as all other standard expressions 
 *       in v2. However, ref_expressions allow the use of the 'NULL' constant, which is handled explicitly
 *       in the ref_expressions syntax of v3.
 *       To allow the use of the 'NULL' constant in this extended v2, I have opted to interpret this 'NULL' constant 
 *       as a literal.
 */
%type  <leaf>	ref_value_null_literal  /* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */


/******************************/
/* B 1.2.1 - Numeric Literals */
/******************************/
/* Done totally within flex...
  bit
*/
%type  <leaf> numeric_literal
%type  <leaf> integer_literal
%type  <leaf> signed_integer
%token <ID>   integer_token
%type  <leaf> integer
%token <ID>   binary_integer_token
%type  <leaf> binary_integer
%token <ID>   octal_integer_token
%type  <leaf> octal_integer
%token <ID>   hex_integer_token
%type  <leaf> hex_integer
%token <ID>   real_token
%type  <leaf> real
%type  <leaf> signed_real
%type  <leaf> real_literal
// %type  <leaf> exponent
%type  <leaf> bit_string_literal
%type  <leaf> boolean_literal

%token safeboolean_true_literal_token
%token safeboolean_false_literal_token
%token boolean_true_literal_token
%token boolean_false_literal_token

%token FALSE
%token TRUE


/*******************************/
/* B 1.2.2 - Character Strings */
/*******************************/
%token <ID>   single_byte_character_string_token
%token <ID>   double_byte_character_string_token

%type  <leaf> character_string
%type  <leaf> single_byte_character_string
%type  <leaf> double_byte_character_string


/***************************/
/* B 1.2.3 - Time Literals */
/***************************/
%type  <leaf> time_literal


/************************/
/* B 1.2.3.1 - Duration */
/************************/
%type  <leaf>	duration
%type  <leaf>	interval
%type  <leaf>	days
%type  <leaf>	fixed_point
%type  <leaf>	hours
%type  <leaf>	minutes
%type  <leaf>	seconds
%type  <leaf>	milliseconds

%token <ID>	fixed_point_token
%token <ID>	fixed_point_d_token
%token <ID>	integer_d_token
%token <ID>	fixed_point_h_token
%token <ID>	integer_h_token
%token <ID>	fixed_point_m_token
%token <ID>	integer_m_token
%token <ID>	fixed_point_s_token
%token <ID>	integer_s_token
%token <ID>	fixed_point_ms_token
%token <ID>	integer_ms_token
%token <ID>	end_interval_token
%token <ID>	erroneous_interval_token
// %token TIME
%token T_SHARP


/************************************/
/* B 1.2.3.2 - Time of day and Date */
/************************************/
%type  <leaf>	time_of_day
%type  <leaf>	daytime
%type  <leaf>	day_hour
%type  <leaf>	day_minute
%type  <leaf>	day_second
%type  <leaf>	date
%type  <leaf>	date_literal
%type  <leaf>	year
%type  <leaf>	month
%type  <leaf>	day
%type  <leaf>	date_and_time

// %token TIME_OF_DAY
// %token DATE
%token D_SHARP
// %token DATE_AND_TIME


/**********************/
/* B 1.3 - Data Types */
/**********************/
/* Strangely, the following symbol does seem to be required! */
// %type  <leaf> data_type_name
%type  <leaf> non_generic_type_name


/***********************************/
/* B 1.3.1 - Elementary Data Types */
/***********************************/
/* NOTES:
 *
 *    - To make the definition of bit_string_literal more
 *      concise, it is useful to use an extra non-terminal
 *      symbol (i.e. a grouping or construct) that groups the
 *      following elements (BYTE, WORD, DWORD, LWORD).
 *      Note that the definition of bit_string_type_name
 *      (according to the spec) includes the above elements
 *      and an extra BOOL.
 *      We could use an extra construct with the first four
 *      elements to be used solely in the definition of
 *      bit_string_literal, but with the objective of not
 *      having to replicate the actions (if we ever need
 *      to change them, they would need to be changed in both
 *      bit_string_type_name and the extra grouping), we
 *      have re-defined bit_string_type_name as only including
 *      the first four elements.
 *      In order to have our parser implement the specification
 *      correctly we have augmented every occurence of
 *      bit_string_type_name in other rules with the BOOL
 *      token. Since bit_string_type_name only appears in
 *      the rule for elementary_type_name, this does not
 *      seem to be a big concession to make!
 *
 *    - We have added a helper symbol to concentrate the
 *      instantiation of STRING and WSTRING into a single
 *      location (elementary_string_type_name).
 *      These two elements show up in several other rules,
 *      but we want to create the equivalent abstract syntax
 *      in a single location of this file, in order to make
 *      possible future changes easier to edit...
 */
%type  <leaf>	elementary_type_name
%type  <leaf>	numeric_type_name
%type  <leaf>	integer_type_name
%type  <leaf>	signed_integer_type_name
%type  <leaf>	unsigned_integer_type_name
%type  <leaf>	real_type_name
%type  <leaf>	date_type_name
%type  <leaf>	bit_string_type_name
/* helper symbol to concentrate the instantiation
 * of STRING and WSTRING into a single location
 */
%type  <leaf>	elementary_string_type_name

%token BYTE
%token WORD
%token DWORD
%token LWORD

%token LREAL
%token REAL

%token SINT
%token INT
%token DINT
%token LINT

%token USINT
%token UINT
%token UDINT
%token ULINT

%token WSTRING
%token STRING
%token BOOL

%token TIME
%token DATE
%token DATE_AND_TIME
%token DT
%token TIME_OF_DAY
%token TOD

/* A non-standard extension! */
%token VOID

/******************************************************/
/* Symbols defined in                                 */
/* "Safety Software Technical Specification,          */
/*  Part 1: Concepts and Function Blocks,             */
/*  Version 1.0 – Official Release"                   */
/* by PLCopen - Technical Committee 5 - 2006-01-31    */
/******************************************************/

%token SAFEBYTE
%token SAFEWORD
%token SAFEDWORD
%token SAFELWORD

%token SAFELREAL
%token SAFEREAL

%token SAFESINT
%token SAFEINT
%token SAFEDINT
%token SAFELINT

%token SAFEUSINT
%token SAFEUINT
%token SAFEUDINT
%token SAFEULINT

%token SAFEWSTRING
%token SAFESTRING
%token SAFEBOOL

%token SAFETIME
%token SAFEDATE
%token SAFEDATE_AND_TIME
%token SAFEDT
%token SAFETIME_OF_DAY
%token SAFETOD

/********************************/
/* B 1.3.2 - Generic data types */
/********************************/
/* Strangely, the following symbol does seem to be required! */
// %type  <leaf>	generic_type_name

/* The following tokens do not seem to be used either
 * but we declare them so they become reserved words...
 */
%token ANY
%token ANY_DERIVED
%token ANY_ELEMENTARY
%token ANY_MAGNITUDE
%token ANY_NUM
%token ANY_REAL
%token ANY_INT
%token ANY_BIT
%token ANY_STRING
%token ANY_DATE


/********************************/
/* B 1.3.3 - Derived data types */
/********************************/
%type  <leaf>	derived_type_name
%type  <leaf>	single_element_type_name
// %type  <leaf>	simple_type_name
// %type  <leaf>	subrange_type_name
// %type  <leaf>	enumerated_type_name
// %type  <leaf>	array_type_name
// %type  <leaf>	structure_type_name

%type  <leaf>	data_type_declaration
/* helper symbol for data_type_declaration */
%type  <list>	type_declaration_list
%type  <leaf>	type_declaration
%type  <leaf>	single_element_type_declaration

%type  <leaf>	simple_type_declaration
%type  <leaf>	simple_spec_init
%type  <leaf>	simple_specification

%type  <leaf>	subrange_type_declaration
%type  <leaf>	subrange_spec_init
%type  <leaf>	subrange_specification
%type  <leaf>	subrange
/* A non standard construct, used to support the use of variables in array subranges. e.g.: ARRAY [12..max] OF INT */
%type  <leaf>	subrange_with_var

%type  <leaf>	enumerated_type_declaration
%type  <leaf>	enumerated_spec_init
%type  <leaf>	enumerated_specification
/* helper symbol for enumerated_value */
%type  <list>	enumerated_value_list
%type  <leaf>	enumerated_value
//%type  <leaf>	enumerated_value_without_identifier

%type  <leaf>	array_type_declaration
%type  <leaf>	array_spec_init
%type  <leaf>	array_specification
/* helper symbol for array_specification */
%type  <list>	array_subrange_list
%type  <leaf>	array_initialization
/* helper symbol for array_initialization */
%type  <list>	array_initial_elements_list
%type  <leaf>	array_initial_elements
%type  <leaf>	array_initial_element

%type  <leaf>	structure_type_declaration
%type  <leaf>	structure_specification
%type  <leaf>	initialized_structure
%type  <leaf>	structure_declaration
/* helper symbol for structure_declaration */
%type  <list>	structure_element_declaration_list
%type  <leaf>	structure_element_declaration
%type  <leaf>	structure_element_name
%type  <leaf>	structure_initialization
/* helper symbol for structure_initialization */
%type  <list>	structure_element_initialization_list
%type  <leaf>	structure_element_initialization

//%type  <leaf>	string_type_name
%type  <leaf>	string_type_declaration
/* helper symbol for string_type_declaration */
%type  <leaf>	string_type_declaration_size
/* helper symbol for string_type_declaration */
%type  <leaf>	string_type_declaration_init

%token ASSIGN
%token DOTDOT  /* ".." */
%token TYPE
%token END_TYPE
%token ARRAY
%token OF
%token STRUCT
%token END_STRUCT


%type  <leaf>	ref_spec                 /* defined in IEC 61131-3 v3 */
%type  <leaf>	ref_spec_non_recursive   /* helper symbol */
%type  <leaf>	ref_spec_init            /* defined in IEC 61131-3 v3 */
%type  <leaf>	ref_type_decl            /* defined in IEC 61131-3 v3 */



/*********************/
/* B 1.4 - Variables */
/*********************/
%type  <leaf>	variable
%type  <leaf>	symbolic_variable
/* helper symbol for prog_cnxn */
%type  <leaf>	any_symbolic_variable
%type  <leaf>	variable_name




/********************************************/
/* B.1.4.1   Directly Represented Variables */
/********************************************/
/* Done totally within flex...
 location_prefix
 size_prefix
*/
%token <ID>	direct_variable_token
//%type  <leaf>	direct_variable


/*************************************/
/* B.1.4.2   Multi-element Variables */
/*************************************/
%type  <leaf>	multi_element_variable
/* helper symbol for any_symbolic_variable */
%type  <leaf>	any_multi_element_variable
%type  <leaf>	array_variable
/* helper symbol for any_symbolic_variable */
%type  <leaf>	any_array_variable
%type  <leaf>	subscripted_variable
/* helper symbol for any_symbolic_variable */
%type  <leaf>	any_subscripted_variable
%type  <list>	subscript_list
%type  <leaf>	subscript
%type  <leaf>	structured_variable
/* helper symbol for any_symbolic_variable */
%type  <leaf>	any_structured_variable
%type  <leaf>	record_variable
/* helper symbol for any_symbolic_variable */
%type  <leaf>	any_record_variable
%type  <leaf>	field_selector


/******************************************/
/* B 1.4.3 - Declaration & Initialisation */
/******************************************/
%type  <leaf>	input_declarations
/* helper symbol for input_declarations */
%type  <list>	input_declaration_list
%type  <leaf>	input_declaration
%type  <leaf>	edge_declaration
/* en_param_declaration is not in the standard, but should be! */
%type  <leaf>	en_param_declaration
%type  <leaf>	var_init_decl
%type  <leaf>	var1_init_decl
%type  <list>	var1_list
%type  <leaf>	array_var_init_decl
%type  <leaf>	structured_var_init_decl
%type  <leaf>	fb_name_decl
/* helper symbol for fb_name_decl */
%type  <list>	fb_name_list_with_colon
/* helper symbol for fb_name_list_with_colon */
%type  <list>	var1_list_with_colon
// %type  <list>	fb_name_list
// %type  <leaf>	fb_name
%type  <leaf>	output_declarations
%type  <leaf>	var_output_init_decl
%type  <list>	var_output_init_decl_list
/* eno_param_declaration is not in the standard, but should be! */
%type  <leaf>	eno_param_declaration
%type  <leaf>	input_output_declarations
/* helper symbol for input_output_declarations */
%type  <list>	var_declaration_list
%type  <leaf>	var_declaration
%type  <leaf>	temp_var_decl
%type  <leaf>	var1_declaration
%type  <leaf>	array_var_declaration
%type  <leaf>	structured_var_declaration
%type  <leaf>	var_declarations
%type  <leaf>	retentive_var_declarations
%type  <leaf>	located_var_declarations
/* helper symbol for located_var_declarations */
%type  <list>	located_var_decl_list
%type  <leaf>	located_var_decl
%type  <leaf>	external_var_declarations
/* helper symbol for external_var_declarations */
%type  <list>	external_declaration_list
%type  <leaf>	external_declaration
%type  <leaf>	global_var_name
%type  <leaf>	global_var_declarations
/* helper symbol for global_var_declarations */
%type  <list>	global_var_decl_list
%type  <leaf>	global_var_decl
%type  <leaf>	global_var_spec
%type  <leaf>	located_var_spec_init
%type  <leaf>	location
%type  <list>	global_var_list
%type  <leaf>	string_var_declaration
%type  <leaf>	single_byte_string_var_declaration
%type  <leaf>	single_byte_string_spec
%type  <leaf>	double_byte_string_var_declaration
%type  <leaf>	double_byte_string_spec
%type  <leaf>	incompl_located_var_declarations
/* helper symbol for incompl_located_var_declarations */
%type  <list>	incompl_located_var_decl_list
%type  <leaf>	incompl_located_var_decl
%type  <leaf>	incompl_location
%type  <leaf>	var_spec
/* helper symbol for var_spec */
%type  <leaf>	string_spec
/* intermediate helper symbol for:
 *  - non_retentive_var_decls
 *  - var_declarations
 */
%type  <list>	var_init_decl_list

%token  <ID>	incompl_location_token

%token VAR_INPUT
%token VAR_OUTPUT
%token VAR_IN_OUT
%token VAR_EXTERNAL
%token VAR_GLOBAL
%token END_VAR
%token RETAIN
%token NON_RETAIN
%token R_EDGE
%token F_EDGE
%token AT


/***********************/
/* B 1.5.1 - Functions */
/***********************/
// %type  <leaf>	function_name
/* helper symbol for IL language */
%type  <leaf>	function_name_no_clashes
%type  <leaf>	function_name_simpleop_clashes
//%type  <leaf>	function_name_expression_clashes
/* helper symbols for ST language */
//%type  <leaf>	function_name_NOT_clashes
%type  <leaf>	function_name_no_NOT_clashes

//%type  <leaf>	standard_function_name
/* helper symbols for IL language */
%type  <leaf>	standard_function_name_no_clashes
%type  <leaf>	standard_function_name_simpleop_clashes
%type  <leaf>	standard_function_name_expression_clashes
/* helper symbols for ST language */
%type  <leaf>	standard_function_name_NOT_clashes
%type  <leaf>	standard_function_name_no_NOT_clashes

%type  <leaf>	derived_function_name
%type  <leaf>	function_declaration
/* helper symbol for function_declaration */
%type  <leaf>	function_name_declaration
%type  <leaf>	io_var_declarations
%type  <leaf>	function_var_decls
%type  <leaf>	function_body
%type  <leaf>	var2_init_decl
/* intermediate helper symbol for function_declaration */
%type  <list>	io_OR_function_var_declarations_list
/* intermediate helper symbol for function_var_decls */
%type  <list>	var2_init_decl_list

%token <ID>	standard_function_name_token

%token FUNCTION
%token END_FUNCTION
%token CONSTANT


/*****************************/
/* B 1.5.2 - Function Blocks */
/*****************************/
%type  <leaf>	function_block_type_name
%type  <leaf>	standard_function_block_name
%type  <leaf>	derived_function_block_name
%type  <leaf>	function_block_declaration
%type  <leaf>	other_var_declarations
%type  <leaf>	temp_var_decls
%type  <leaf>	non_retentive_var_decls
%type  <leaf>	function_block_body
/* intermediate helper symbol for function_declaration */
%type  <list>	io_OR_other_var_declarations_list
/* intermediate helper symbol for temp_var_decls */
%type  <list>	temp_var_decls_list

%token <ID>	standard_function_block_name_token

%token FUNCTION_BLOCK
%token END_FUNCTION_BLOCK
%token VAR_TEMP
// %token END_VAR
%token VAR
// %token NON_RETAIN
// %token END_VAR


/**********************/
/* B 1.5.3 - Programs */
/**********************/
%type  <leaf>	program_type_name
%type  <leaf>	program_declaration
/* helper symbol for program_declaration */
%type  <list>	program_var_declarations_list

%token PROGRAM
%token END_PROGRAM


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

%type  <list>	sequential_function_chart
%type  <list>	sfc_network
%type  <leaf>	initial_step
%type  <leaf>	step
%type  <list>	action_association_list
%type  <leaf>	step_name
%type  <leaf>	action_association
/* helper symbol for action_association */
%type  <list>	indicator_name_list
%type  <leaf>	action_name
%type  <leaf>	action_qualifier
%type  <leaf>	qualifier
%type  <leaf>	timed_qualifier
%type  <leaf>	action_time
%type  <leaf>	indicator_name
%type  <leaf>	transition
%type  <leaf>	steps
%type  <list>	step_name_list
%type  <leaf>	transition_priority
%type  <leaf>	transition_condition
%type  <leaf>	action
%type  <leaf>	action_body
%type  <leaf>	transition_name


// %token ASSIGN
%token ACTION
%token END_ACTION

%token TRANSITION
%token END_TRANSITION
%token FROM
%token TO
%token PRIORITY

%token INITIAL_STEP
%token STEP
%token END_STEP

%token L
%token D
%token SD
%token DS
%token SL

%token N
%token P
%token P0
%token P1
/* NOTE: the following two clash with the R and S IL operators.
 * It will have to be handled when we include parsing of SFC...
 */
/*
%token R
%token S
*/


/********************************/
/* B 1.7 Configuration elements */
/********************************/
%type  <leaf>	configuration_name
%type  <leaf>	resource_type_name
%type  <leaf>	configuration_declaration
// helper symbol for
//  - configuration_declaration
//  - resource_declaration
//
%type  <list>	global_var_declarations_list
// helper symbol for configuration_declaration
%type  <leaf>	optional_access_declarations
// helper symbol for configuration_declaration
%type  <leaf>	optional_instance_specific_initializations
// helper symbol for configuration_declaration
%type  <list>	resource_declaration_list
%type  <leaf>	resource_declaration
%type  <leaf>	single_resource_declaration
// helper symbol for single_resource_declaration
%type  <list>	task_configuration_list
// helper symbol for single_resource_declaration
%type  <list>	program_configuration_list
%type  <leaf>	resource_name
// %type  <leaf>	access_declarations
// helper symbol for access_declarations
// %type  <leaf>	access_declaration_list
// %type  <leaf>	access_declaration
// %type  <leaf>	access_path
// helper symbol for access_path
%type  <list>	any_fb_name_list
%type  <leaf>	global_var_reference
// %type  <leaf>	access_name
%type  <leaf>	program_output_reference
%type  <leaf>	program_name
// %type  <leaf>	direction
%type  <leaf>	task_configuration
%type  <leaf>	task_name
%type  <leaf>	task_initialization
// 3 helper symbols for task_initialization
%type  <leaf>	task_initialization_single
%type  <leaf>	task_initialization_interval
%type  <leaf>	task_initialization_priority

%type  <leaf>	data_source
%type  <leaf>	program_configuration
// helper symbol for program_configuration
%type  <leaf>	optional_task_name
// helper symbol for program_configuration
%type  <leaf>	optional_prog_conf_elements
%type  <list>	prog_conf_elements
%type  <leaf>	prog_conf_element
%type  <leaf>	fb_task
%type  <leaf>	prog_cnxn
%type  <leaf>	prog_data_source
%type  <leaf>	data_sink
%type  <leaf>	instance_specific_initializations
// helper symbol for instance_specific_initializations
%type  <list>	instance_specific_init_list
%type  <leaf>	instance_specific_init
// helper symbol for instance_specific_init
%type  <leaf>	fb_initialization

%type  <leaf>	prev_declared_global_var_name
%token  <ID>	prev_declared_global_var_name_token

%type  <leaf>	prev_declared_program_name
%token  <ID>	prev_declared_program_name_token

%type  <leaf>	prev_declared_resource_name
%token  <ID>	prev_declared_resource_name_token

%type  <leaf>	prev_declared_configuration_name
%token  <ID>	prev_declared_configuration_name_token

// %type  <leaf>	prev_declared_task_name
// %token  <ID>	prev_declared_task_name_token

%token CONFIGURATION
%token END_CONFIGURATION
%token TASK
%token RESOURCE
%token ON
%token END_RESOURCE
%token VAR_CONFIG
%token VAR_ACCESS
// %token END_VAR
%token WITH
// %token PROGRAM
// %token RETAIN
// %token NON_RETAIN
// %token PRIORITY
%token SINGLE
%token INTERVAL
%token READ_WRITE
%token READ_ONLY


/***********************************/
/* B 2.1 Instructions and Operands */
/***********************************/
%type  <list>	instruction_list
%type  <leaf>	il_instruction
%type  <leaf>	il_incomplete_instruction
%type  <leaf>	label
%type  <leaf>	il_simple_operation
// helper symbol for il_simple_operation
//%type <tmp_symbol> il_simple_operator_clash_il_operand
%type  <leaf>	il_expression
%type  <leaf>	il_jump_operation
%type  <leaf>	il_fb_call
%type  <leaf>	il_formal_funct_call
// helper symbol for il_formal_funct_call
%type  <leaf> il_expr_operator_clash_eol_list
%type  <leaf>	il_operand
%type  <list>	il_operand_list
// helper symbol for il_simple_operation
%type  <list>	il_operand_list2
%type  <list>	simple_instr_list
%type  <leaf>	il_simple_instruction
%type  <list>	il_param_list
%type  <list>	il_param_instruction_list
%type  <leaf>	il_param_instruction
%type  <leaf>	il_param_last_instruction
%type  <leaf>	il_param_assignment
%type  <leaf>	il_param_out_assignment

%token EOL


/*******************/
/* B 2.2 Operators */
/*******************/
%token <ID>	sendto_identifier_token
%type  <leaf>	sendto_identifier

%type  <leaf>	LD_operator
%type  <leaf>	LDN_operator
%type  <leaf>	ST_operator
%type  <leaf>	STN_operator
%type  <leaf>	NOT_operator
%type  <leaf>	S_operator
%type  <leaf>	R_operator
%type  <leaf>	S1_operator
%type  <leaf>	R1_operator
%type  <leaf>	CLK_operator
%type  <leaf>	CU_operator
%type  <leaf>	CD_operator
%type  <leaf>	PV_operator
%type  <leaf>	IN_operator
%type  <leaf>	PT_operator
%type  <leaf>	AND_operator
%type  <leaf>	AND2_operator
%type  <leaf>	OR_operator
%type  <leaf>	XOR_operator
%type  <leaf>	ANDN_operator
%type  <leaf>	ANDN2_operator
%type  <leaf>	ORN_operator
%type  <leaf>	XORN_operator
%type  <leaf>	ADD_operator
%type  <leaf>	SUB_operator
%type  <leaf>	MUL_operator
%type  <leaf>	DIV_operator
%type  <leaf>	MOD_operator
%type  <leaf>	GT_operator
%type  <leaf>	GE_operator
%type  <leaf>	EQ_operator
%type  <leaf>	LT_operator
%type  <leaf>	LE_operator
%type  <leaf>	NE_operator
%type  <leaf>	CAL_operator
%type  <leaf>	CALC_operator
%type  <leaf>	CALCN_operator
%type  <leaf>	RET_operator
%type  <leaf>	RETC_operator
%type  <leaf>	RETCN_operator
%type  <leaf>	JMP_operator
%type  <leaf>	JMPC_operator
%type  <leaf>	JMPCN_operator

%type  <leaf>	il_simple_operator
%type  <leaf>	il_simple_operator_clash
%type  <leaf>	il_simple_operator_clash1
%type  <leaf>	il_simple_operator_clash2
%type  <leaf>	il_simple_operator_clash3
%type  <leaf>	il_simple_operator_noclash

//%type  <leaf>	il_expr_operator
%type  <leaf>	il_expr_operator_clash
%type  <leaf>	il_expr_operator_noclash

%type  <leaf>	il_assign_operator
%type  <leaf>	il_assign_out_operator
%type  <leaf>	il_call_operator
%type  <leaf>	il_return_operator
%type  <leaf>	il_jump_operator


%token LD
%token LDN
%token ST
%token STN
%token NOT
%token S
%token R
%token S1
%token R1
%token CLK
%token CU
%token CD
%token PV
%token IN
%token PT
%token AND
%token AND2  /* character '&' in the source code*/
%token OR
%token XOR
%token ANDN
%token ANDN2 /* characters '&N' in the source code */
%token ORN
%token XORN
%token ADD
%token SUB
%token MUL
%token DIV
%token MOD
%token GT
%token GE
%token EQ
%token LT
%token LE
%token NE
%token CAL
%token CALC
%token CALCN
%token RET
%token RETC
%token RETCN
%token JMP
%token JMPC
%token JMPCN

%token SENDTO   /* "=>" */


/***********************/
/* B 3.1 - Expressions */
/***********************/
/* NOTE:
 *
 *    - unary_operator, multiply_operator,
 *      add_operator and comparison_operator
 *      are not required. Their values are integrated
 *      directly into other rules...
 */
%type  <leaf>	  ref_expression  /* an extension to the IEC 61131-3 v2 standard, based on the IEC 61131-3 v3 standard */ 
%type  <leaf>	deref_expression  /* an extension to the IEC 61131-3 v2 standard, based on the IEC 61131-3 v3 standard */ 
%type  <leaf>	expression
%type  <leaf>	xor_expression
%type  <leaf>	and_expression
%type  <leaf>	comparison
%type  <leaf>	equ_expression
// %type  <leaf>	comparison_operator
%type  <leaf>	add_expression
// %type  <leaf>	add_operator
%type  <leaf>	term
// %type  <leaf>	multiply_operator
%type  <leaf>	power_expression
%type  <leaf>	unary_expression
// %type  <leaf>	unary_operator
%type  <leaf>	primary_expression
%type  <leaf>	non_int_or_real_primary_expression
/* intermediate helper symbol for primary_expression */
%type  <leaf>	function_invocation

// %token AND
// %token XOR
// %token OR
// %token MOD
// %token NOT
%token OPER_NE
%token OPER_GE
%token OPER_LE
%token OPER_EXP


/********************/
/* B 3.2 Statements */
/********************/
%type <list> statement_list
%type <leaf> statement



/*********************************/
/* B 3.2.1 Assignment Statements */
/*********************************/
%type <leaf> assignment_statement
// %token ASSIGN   /* ":=" */


/*****************************************/
/* B 3.2.2 Subprogram Control Statements */
/*****************************************/
%type <leaf>	subprogram_control_statement
%type <leaf>	return_statement
%type <leaf>	fb_invocation
// %type <leaf>	param_assignment
%type <leaf>	param_assignment_formal
%type <leaf>	param_assignment_nonformal
/* helper symbols for fb_invocation */
%type <list> param_assignment_formal_list
%type <list> param_assignment_nonformal_list

// %token ASSIGN
// %token SENDTO   /* "=>" */
%token RETURN


/********************************/
/* B 3.2.3 Selection Statements */
/********************************/
%type <leaf>	selection_statement
%type <leaf>	if_statement
%type <leaf>	case_statement
%type <leaf>	case_element
%type <list>	case_list
%type <leaf>	case_list_element
/* helper symbol for if_statement */
%type <list>	elseif_statement_list
/* helper symbol for elseif_statement_list */
%type <leaf>	elseif_statement
/* helper symbol for case_statement */
%type <list>	case_element_list

%token IF
%token THEN
%token ELSIF
%token ELSE
%token END_IF

%token CASE
// %token OF
// %token ELSE
%token END_CASE



/********************************/
/* B 3.2.4 Iteration Statements */
/********************************/
%type <leaf>	iteration_statement
%type <leaf>	for_statement
%type <leaf>	control_variable
%type <leaf>	while_statement
%type <leaf>	repeat_statement
%type <leaf>	exit_statement
/* Integrated directly into for_statement */
// %type <leaf>	for_list

%token FOR
// %token ASSIGN
// %token TO
%token BY
%token DO
%token END_FOR

%token WHILE
// %token DO
%token END_WHILE

%token REPEAT
%token UNTIL
%token END_REPEAT

%token EXIT


%%




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


start:
  library	{$$ = $1;}
;


/**********************************************************************************/
/* B XXX - Things that are missing from the standard, but should have been there! */
/**********************************************************************************/


/* the pragmas... */


disable_code_generation_pragma:
  disable_code_generation_pragma_token	{$$ = new disable_code_generation_pragma_c(locloc(@$));}

enable_code_generation_pragma:
  enable_code_generation_pragma_token	{$$ = new enable_code_generation_pragma_c(locloc(@$));}

pragma:
  pragma_token	{$$ = new pragma_c($1, locloc(@$));}

any_pragma:
  disable_code_generation_pragma
| enable_code_generation_pragma
| pragma
;


/* EN/ENO */
/* Tese tokens are essentially used as variable names, so we handle them 
 * similarly to these...
 */
en_identifier:
  EN	{$$ = new identifier_c("EN", locloc(@$));}
;

eno_identifier:
  ENO	{$$ = new identifier_c("ENO", locloc(@$));}
;



/*************************************/
/* Prelimenary helpful constructs... */
/*************************************/

/* NOTE:
 *       short version:
 *       identifier is used for previously undeclared identifiers
 *       any_identifier is used when any identifier, previously
 *       declared or not, is required in the syntax.
 *
 *       long version:
 *       When flex comes across an identifier, it first
 *       searches through the currently declared variables,
 *       functions, types, etc... to determine if it has
 *       been previously declared.
 *       Only if the identifier has not yet been declared
 *       will it return an identifier_token (later turned into
 *       an identifier symbol by the bison generated syntax parser).
 *
 *       Some constructs in the syntax, such as when calling
 *       a function 'F(var1 := 1; var2 := 2);', will accept _any_
 *       identifier in 'var1', even if it has been previously
 *       declared in the current scope, since var1 belongs to
 *       another scope (the variables declared in function F).
 *
 *       For the above reason, we need to define the symbol
 *       any_identifier. All the symbols that may become an
 *       any_identifier are expected to be stored in the
 *       abstract syntax as a identifier_c
 */
/* NOTE:
 *  Type names, function names, function block type names and
 *  program type names are considerd keywords once they are defined,
 *  so may no longer be used for variable names!
 *  BUT the spec is confusing on this issue, as it is not clear when
 *  a function name should be considered as defined. If it is to be
 *  considered defined only from the location from where it is declared
 *  and onwards, it means that before it is declared its name may be
 *  used for variable names!
 *  This means that we must allow names previously used for functions
 *  (et. al.) to also constitue an any_identifier!
 */
any_identifier:
  identifier
| prev_declared_fb_name
| prev_declared_variable_name
/**/
    /* ref_type_name is defined in IEC 61131-3 v3 */
| prev_declared_ref_type_name               {$$ = new identifier_c(((token_c *)$1)->value, locloc(@$));}; // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
| prev_declared_simple_type_name            {$$ = new identifier_c(((token_c *)$1)->value, locloc(@$));}; // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
| prev_declared_subrange_type_name          {$$ = new identifier_c(((token_c *)$1)->value, locloc(@$));}; // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
| prev_declared_enumerated_type_name        {$$ = new identifier_c(((token_c *)$1)->value, locloc(@$));}; // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
| prev_declared_array_type_name             {$$ = new identifier_c(((token_c *)$1)->value, locloc(@$));}; // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
| prev_declared_structure_type_name         {$$ = new identifier_c(((token_c *)$1)->value, locloc(@$));}; // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
| prev_declared_string_type_name            {$$ = new identifier_c(((token_c *)$1)->value, locloc(@$));}; // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
| prev_declared_derived_function_name       {$$ = new identifier_c(((token_c *)$1)->value, locloc(@$));}; // change the          poutype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
| prev_declared_derived_function_block_name {$$ = new identifier_c(((token_c *)$1)->value, locloc(@$));}; // change the          poutype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
| prev_declared_program_type_name           {$$ = new identifier_c(((token_c *)$1)->value, locloc(@$));}; // change the          poutype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
/**/
| prev_declared_resource_name
| prev_declared_program_name
| prev_declared_global_var_name
;

/* NOTE: Notice that the symbol classes:
 *            - derived_datatype_identifier_c
 *            - poutype_identifier_c
 *       are only inserted into the AST when referencing a derived dataype or a POU
 *       (e.g. when declaring a variable, making a function call, instantiating a program in a resource,
 *        or delaring a derived datatype that derives from another previously delcared datatype).
 *
 *       In the declaration of the datatype or POU itself, the name of the datatype or POU will be stored
 *       inside an identifier_c instead!!
 */
prev_declared_variable_name:        prev_declared_variable_name_token        {$$ = new identifier_c($1, locloc(@$));};
prev_declared_fb_name:              prev_declared_fb_name_token              {$$ = new identifier_c($1, locloc(@$));};

prev_declared_simple_type_name:     prev_declared_simple_type_name_token     {$$ = new derived_datatype_identifier_c($1, locloc(@$));};
prev_declared_subrange_type_name:   prev_declared_subrange_type_name_token   {$$ = new derived_datatype_identifier_c($1, locloc(@$));};
prev_declared_enumerated_type_name: prev_declared_enumerated_type_name_token {$$ = new derived_datatype_identifier_c($1, locloc(@$));};
prev_declared_array_type_name:      prev_declared_array_type_name_token      {$$ = new derived_datatype_identifier_c($1, locloc(@$));};
prev_declared_structure_type_name:  prev_declared_structure_type_name_token  {$$ = new derived_datatype_identifier_c($1, locloc(@$));};
prev_declared_string_type_name:     prev_declared_string_type_name_token     {$$ = new derived_datatype_identifier_c($1, locloc(@$));};
prev_declared_ref_type_name:        prev_declared_ref_type_name_token        {$$ = new derived_datatype_identifier_c($1, locloc(@$));};  /* defined in IEC 61131-3 v3 */

prev_declared_derived_function_name:       prev_declared_derived_function_name_token       {$$ = new poutype_identifier_c($1, locloc(@$));};
prev_declared_derived_function_block_name: prev_declared_derived_function_block_name_token {$$ = new poutype_identifier_c($1, locloc(@$));};
prev_declared_program_type_name:           prev_declared_program_type_name_token           {$$ = new poutype_identifier_c($1, locloc(@$));};
/* NOTE: The poutype_identifier_c was introduced to allow the implementation of remove_forward_dependencies_c */




/***************************/
/* B 0 - Programming Model */
/***************************/
library:
  /* empty */
	{if (tree_root == NULL)
	  tree_root = new library_c();
	 $$ = (list_c *)tree_root;
	}
| library library_element_declaration
	{$$ = $1; $$->add_element($2);}
| library any_pragma
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| library error library_element_declaration
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unknown syntax error."); yyerrok;}
| library error END_OF_INPUT
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unknown syntax error."); yyerrok;}
/* ERROR_CHECK_END */
;


library_element_declaration:
  data_type_declaration
| function_declaration
| function_block_declaration
| program_declaration
| configuration_declaration
;



/*******************************************/
/* B 1.1 - Letters, digits and identifiers */
/*******************************************/
/* NOTE: the spec defines identifier as:
 *         identifier ::= (letter|('_' (letter|digit))) {['_'] (letter|digit)}
 *       In essence, any sequence of letters or digits, starting with a letter
 *       or '_'.
 *
 *       On section 2.1.3 (pg 26) , the spec states
 *       "The keywords listed in annex C shall not be used for any other purpose,
 *         e.g., variable names or extensions as defined in 1.5.1."
 *       (NOTE: the spec itself does not follow this rule, as it defines standard
 *       functions with names identidal to keywords, e.g. 'MOD', 'NOT' !!. This is
 *       another issue altogether, and is worked around somewhere else...)
 *
 *       This means that we must re-define indentifier so as to exclude
 *       any keywords defined in annex C.
 *
 *       Note also that the list includes
 *          - Data type names
 *          - Function names
 *          - Function Block names
 *       This means that any named used for a function name, data type name
 *       or function block name, essentially becomes a keyword, and may therefore
 *       no longer be re-used for any other use! (see NOTE 2)
 *
 *       In our case, excluding the keywords is achieved in the lexical parser,
 *       by two mechanisms:
 *         (1) giving higher priority to the keywords (tokens) than to identifiers,
 *             so when the lexical parser finds a keyword it will be parsed as a
 *             token before being parsed as an identifier.
 *         (2) when an identifier is found that is not a keyword, the lexical parser
 *             then looks in the global symbol table, and will not return an identifier
 *             if the name has been previously used as a data type name, function name,
 *             or function block name! (In these cases it will return a
 *             prev_declared_function_name_token, etc...).
 *
 *       Unfortunately, the language (especially IL) uses tokens that are
 *       not defined as keywords in the spec (e.g. 'IN', 'R1', 'S1', 'PT', etc...)!
 *       This means that it is valid to name a function 'IN', a variable 'PT', etc...
 *       In order to solve this potential ambiguity, flex only parses the above 
 *       identifiers as keywords / tokens if we are currently parsing IL code.
 *       When parsing all code other than IL code, the above identifiers are treated
 *       just like any other identifier.
 *
 *
 *
 *
 * NOTE 2:
 *         I (Mario) find it strange that the writers of the spec really want
 *         names previously used for function names, data type names or function
 *         block names, to become full fledged keywords. I understand that they
 *         do not want these names being used as variable names, but how about
 *         enumeration values? How about structure element names?
 *         If we interpret the spec literally, these would not be accepted,
 *         which would probably burden the programmer quite a bit, in making sure
 *         all these name don't clash!
 *
 *
 *
 * NOTE 3: The keywords, as specified in Annex C are...
 *
 *          - Data type names
 *          - Function names
 *          - Function Block names
 *          - ACTION...END_ACTION
 *          - ARRAY...OF
 *          - AT
 *          - CASE...OF...ELSE...END_CASE
 *          - CONFIGURATION...END_CONFIGURATION
 *          - CONSTANT
 *          - EN, ENO
 *          - EXIT
 *          - FALSE
 *          - F_EDGE
 *          - FOR...TO...BY...DO...END_FOR
 *          - FUNCTION...END_FUNCTION
 *          - FUNCTION_BLOCK...END_FUNCTION_BLOCK
 *          - IF...THEN...ELSIF...ELSE...END_IF
 *          - INITIAL_STEP...END_STEP
 *          - NOT, MOD, AND, XOR, OR
 *          - PROGRAM...WITH...
 *          - PROGRAM...END_PROGRAM
 *          - R_EDGE
 *          - READ_ONLY, READ_WRITE
 *          - REPEAT...UNTIL...END_REPEAT
 *          - RESOURCE...ON...END_RESOURCE
 *          - RETAIN, NON_RETAIN
 *          - RETURN
 *          - STEP...END_STEP
 *          - STRUCT...END_STRUCT
 *          - TASK
 *          - TRANSITION...FROM...TO...END_TRANSITION
 *          - TRUE
 *          - TYPE...END_TYPE
 *          - VAR...END_VAR
 *          - VAR_INPUT...END_VAR
 *          - VAR_OUTPUT...END_VAR
 *          - VAR_IN_OUT...END_VAR
 *          - VAR_TEMP...END_VAR
 *          - VAR_EXTERNAL...END_VAR
 *          - VAR_ACCESS...END_VAR
 *          - VAR_CONFIG...END_VAR
 *          - VAR_GLOBAL...END_VAR
 *          - WHILE...DO...END_WHILE
 *          - WITH
 */

identifier:
  identifier_token	{$$ = new identifier_c($1, locloc(@$));}
;



/*********************/
/* B 1.2 - Constants */
/*********************/
constant:
  character_string
| time_literal
| bit_string_literal
| boolean_literal
| numeric_literal  
/* NOTE: Our definition of numeric_literal is diferent than the one in the standard.
 *       We will now add what is missing in our definition of numeric literal, so our
 *       definition of constant matches what the definition of constant in the standard.
 */
/* NOTE: in order to remove reduce/reduce conflicts,
 * [between -9.5 being parsed as 
 *     (i)   a signed real, 
 *     (ii)  or as a real preceded by the '-' operator
 *  ]
 *  we need to define a variant of the constant construct
 *  where any real or integer constant is always preceded by 
 *  a sign (i.e. the '-' or '+' characters).
 *  (For more info, see comment in the construct non_int_or_real_primary_expression)
 *
 * For the above reason, our definition of the numeric_literal construct
 * is missing the integer and real constrcuts (when not preceded by a sign)
 * so we add then here explicitly!
 */
| real
| integer
/* NOTE: unsigned_integer, although used in some
 * rules, is not defined in the spec!
 * We therefore replaced unsigned_integer as integer
 */
| ref_value_null_literal /* defined in IEC 61131-3 v3. Basically the 'NULL' keyword! */
;




non_int_or_real_constant:
  character_string
| time_literal
| bit_string_literal
| boolean_literal
| numeric_literal  
/* NOTE: Our definition of numeric_literal is diferent than the one in the standard.
 *       It is missing the integer and real when not prefixed by a sign 
 *       (i.e. -54, +42 is included in numerical_literal,
 *        but   54,  42 is not parsed as a numeric_literal!!)
 */
/* NOTE: in order to remove reduce/reduce conflicts,
 * [between -9.5 being parsed as 
 *     (i)   a signed real, 
 *     (ii)  or as a real preceded by the '-' operator
 *  ]
 * [and a similar situation for integers!]
 *  we need to define a variant of the constant construct
 *  where any real or integer constant is always preceded by 
 *  a sign (i.e. the '-' or '+' characters).
 *
 * For the above reason, our definition of the numeric_literal construct
 * is missing the integer and real constrcuts (when not preceded by a sign)
 */
;

/*********************************/
/* B 1.2.XX - Reference Literals */
/*********************************/
/* NOTE: The following syntax was added by MJS in order to add support for the NULL keyword, defined in  IEC 61131-3 v3
 *       Please read the comment where the 'ref_value_null_literal' is declared as a <leaf> 
 */
/* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */
ref_value_null_literal: 
  NULL_token	{$$ = new ref_value_null_literal_c(locloc(@$));}
;

/******************************/
/* B 1.2.1 - Numeric Literals */
/******************************/
/* NOTES:
 *
 *    - integer is parsed by flex, but signed_integer
 *      is parsed by bison. Flex cannot parse a signed
 *      integer correctly!  For example: '123+456'
 *      would be parsed by flex as an {integer} {signed_integer}
 *      instead of {integer} '+' {integer}
 *
 *    - Neither flex nor bison can parse a real_literal
 *      completely (and correctly).
 *      Note that we cannot use the definition of real in bison as
 *      real: signed_integer '.' integer [exponent]
 *      exponent: {'E'|'e'} ['+'|'-'] integer
 *      because 123e45 would be parsed by flex as
 *      integer (123) identifier (e45).
 *      I.e., flex never hands over an 'e' directly to
 *      bison, but rather interprets it as an identifier.
 *      I guess we could jump through hoops and get it
 *      working in bison, but the following alternative
 *      seems more straight forward...
 *
 *      We therefore had to break up the definition of
 *      real_literal in discrete parts:
 *      real_literal: [real_type_name '#'] singned_real
 *      signed_real: ['+'|'-'] real
 *      Flex handles real, while bison handles signed_real
 *      and real_literal.
 *
 *    - According to the spec, integer '.' integer
 *      may be reduced to either a real or a fixed_point.
 *      It is nevertheless possible to figure out from the
 *      context which of the two rules should be used in
 *      the reduction.
 *      Unfortunately, due to the issue described above
 *      regarding the exponent of a real, the syntax
 *      integer '.' integer
 *      must be parsed by flex as a single token (i.e.
 *      fixed_point_token). This means we must add fixed_point
 *      to the definition of real!
 *
 *    - The syntax also uses a construct
 *        fixed_point: integer ['.' integer]
 *      Notice that real is not defined based on fixed point,
 *      but rather off integer thus:
 *        real: integer '.' integer [exponent]
 *      This means that a real may not be composed of a single
 *      integer, unlike the construct fixed_point!
 *      This also means that a
 *        integer '.' integer
 *      could be reduced to either a real or a fixed_point
 *      construct. It is probably possible to decide by looking
 *      at the context, BUT:
 *       Unfortunatley, due to the reasons explained way above,
 *      a real (with an exponent) has to be handled by flex as a
 *      whole. This means that we cannot leave to bison (the syntax
 *      parser) the decision of how to reduce an
 *        integer '.' integer
 *      (either to real or to fixed_point)
 *      The decision on how to reduce it would need to be done by
 *      ther lexical analyser (i.e. flex). But flex cannot do this
 *      sort of thing.
 *      The solution I (Mario) adopted is to have flex return
 *      a real_token on (notice that exponent is no longer optional)
 *        integer '.' integer exponent
 *      and to return a fixed_point_token when it finds
 *        integer '.' integer
 *      We now redefine real and fixed_point to be
 *        fixed_point: fixed_point_token | integer
 *        real: real_token | fixed_point_token
 */
real:
  real_token		{$$ = new real_c($1, locloc(@$));}
| fixed_point_token	{$$ = new real_c($1, locloc(@$));}
;

integer:	integer_token		{$$ = new integer_c($1, locloc(@$));};
binary_integer:	binary_integer_token	{$$ = new binary_integer_c($1, locloc(@$));};
octal_integer:	octal_integer_token	{$$ = new octal_integer_c($1, locloc(@$));};
hex_integer:	hex_integer_token	{$$ = new hex_integer_c($1, locloc(@$));};

numeric_literal:
  integer_literal
| real_literal
;


integer_literal:
  integer_type_name '#' signed_integer
	{$$ = new integer_literal_c($1, $3, locloc(@$));}
| integer_type_name '#' binary_integer
	{$$ = new integer_literal_c($1, $3, locloc(@$));}
| integer_type_name '#' octal_integer
	{$$ = new integer_literal_c($1, $3, locloc(@$));}
| integer_type_name '#' hex_integer
	{$$ = new integer_literal_c($1, $3, locloc(@$));}
| binary_integer
| octal_integer
| hex_integer
//|signed_integer  /* We expand the construct signed_integer here, so we can remove one of its constituents */
//|  integer       /* REMOVED! see note in the definition of constant for reason why integer is missing here! */
| '+' integer   {$$ = $2;}
| '-' integer	{$$ = new neg_integer_c($2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| integer_type_name signed_integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between integer type name and value in integer literal."); yynerrs++;}
| integer_type_name binary_integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between integer type name and value in integer literal."); yynerrs++;}
| integer_type_name octal_integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between integer type name and value in integer literal."); yynerrs++;}
| integer_type_name hex_integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between integer type name and value in integer literal."); yynerrs++;}
| integer_type_name '#' error
	{$$ = NULL; 
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for integer literal.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value for integer literal."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/* NOTE: this construct is used in the definition of integer_literal. However, in order to remove
 *       a reduce/reduce conflict (see NOTE in definition of constant for reason why)
 *       it is not used directly, but rather its expansion is copied there.
 *
 *       If for some reason you need to change the definition of signed_integer, don't forget
 *       to change its expansion in integer_literal too!
*/
signed_integer:
  integer
| '+' integer   {$$ = $2;}
| '-' integer	{$$ = new neg_integer_c($2, locloc(@$));}
;


real_literal:
// signed_real /* We expand the construct signed_integer here, so we can remove one of its constituents */
// real        /* REMOVED! see note in the definition of constant for reason why real is missing here! */
  '+' real	{$$ = $2;}
| '-' real	{$$ = new neg_real_c($2, locloc(@2));}
| real_type_name '#' signed_real
	{$$ = new real_literal_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| real_type_name signed_real
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between real type name and value in real literal."); yynerrs++;}
| real_type_name '#' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for real literal.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value for real literal."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/* NOTE: this construct is used in the definition of real_literal. However, in order to remove
 *       a reduce/reduce conflict (see NOTE in definition of constant for reason why)
 *       it is not used directly, but rather its expansion is copied there.
 *
 *       If for some reason you need to change the definition of signed_real, don't forget
 *       to change its expansion in real_literal too!
*/
signed_real:
  real
| '+' real	{$$ = $2;}
| '-' real	{$$ = new neg_real_c($2, locloc(@2));}
;


bit_string_literal:
  bit_string_type_name '#' integer  /* i.e. unsigned_integer */
	{$$ = new bit_string_literal_c($1, $3, locloc(@$));}
| bit_string_type_name '#' binary_integer
	{$$ = new bit_string_literal_c($1, $3, locloc(@$));}
| bit_string_type_name '#' octal_integer
	{$$ = new bit_string_literal_c($1, $3, locloc(@$));}
| bit_string_type_name '#' hex_integer
	{$$ = new bit_string_literal_c($1, $3, locloc(@$));}
/* NOTE: see note in the definition of constant for reason
 * why unsigned_integer, binary_integer, octal_integer
 * and hex_integer are missing here!
 */
/* NOTE: see note under the B 1.2.1 section of token
 * and grouping type definition for reason why the use of
 * bit_string_type_name, although seemingly incorrect, is
 * really correct here!
 */
/* ERROR_CHECK_BEGIN */
| bit_string_type_name integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between bit string type name and value in bit string literal."); yynerrs++;}
| bit_string_type_name binary_integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between bit string type name and value in bit string literal."); yynerrs++;}
| bit_string_type_name octal_integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between bit string type name and value in bit string literal."); yynerrs++;}
| bit_string_type_name hex_integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between bit string type name and value in bit string literal."); yynerrs++;}
| bit_string_type_name '#' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for bit string literal.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value for bit string literal."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


boolean_literal:
  boolean_true_literal_token
	{$$ = new boolean_literal_c(new bool_type_name_c(locloc(@$)),
				    new boolean_true_c(locloc(@$)),
				    locloc(@$));
	}
| boolean_false_literal_token
	{$$ = new boolean_literal_c(new bool_type_name_c(locloc(@$)),
				    new boolean_false_c(locloc(@$)),
				    locloc(@$));
	}
| safeboolean_true_literal_token
	{$$ = new boolean_literal_c(new safebool_type_name_c(locloc(@$)),
				    new boolean_true_c(locloc(@$)),
				    locloc(@$));
	}
| safeboolean_false_literal_token
	{$$ = new boolean_literal_c(new safebool_type_name_c(locloc(@$)),
				    new boolean_false_c(locloc(@$)),
				    locloc(@$));
	}
| FALSE
	{$$ = new boolean_literal_c(NULL,
				    new boolean_false_c(locloc(@$)),
				    locloc(@$));
	}
| TRUE
	{$$ = new boolean_literal_c(NULL,
				    new boolean_true_c(locloc(@$)),
				    locloc(@$));
	}
/*
|	BOOL '#' '1' {}
|	BOOL '#' '0' {}
*/
/* NOTE: the rules
 * BOOL '#' '1'
 * and
 * BOOL '#' '0'
 * do not work as expected...
 * Consider that we are using 'BOOL' and '#' as tokens
 * that flex hands over to bison (yacc). Because flex would
 * then parse the single '1' or '0' as an integer,
 * the rule in bison would have to be
 * BOOL '#' integer, followed by verifying of the
 * integer has the correct value!
 *
 * We therefore have flex return TRUE whenever it
 * comes across 'TRUE' or 'BOOL#1', and FALSE whenever
 * it comes across 'FALSE' or 'BOOL#0'.
 * Note that this means that flex will parse "BOOL#01"
 * as FALSE followed by an integer ('1').
 * Bison should detect this as an error, so we should
 * be OK.
 *
 * Another option would be to change the rules to accept
 * BOOL '#' integer
 * but then check whether the integer has a correct
 * value! At the moment I feel that the first option
 * is more straight forward.
 */
;



/*******************************/
/* B 1.2.2 - Character Strings */
/*******************************/
/* Transform the tokens given us by flex into leafs */
single_byte_character_string:	single_byte_character_string_token
	{$$ = new single_byte_character_string_c($1, locloc(@$));};

double_byte_character_string:	double_byte_character_string_token
	{$$ = new double_byte_character_string_c($1, locloc(@$));};


character_string:
  single_byte_character_string
| double_byte_character_string
;





/***************************/
/* B 1.2.3 - Time Literals */
/***************************/
time_literal:
  time_of_day
| date
| date_and_time
| duration
;


/************************/
/* B 1.2.3.1 - Duration */
/************************/
duration:
/*  (T | TIME) '#' ['-'] interval */
/* NOTE: since TIME is also a data type, it is a keyword
 *       and may therefore be handled by a token.
 *
 *       Unfortunately T is not a data type, and therefore
 *       not a keyword. This means that we may have variables named T!
 *       Flex cannot return the token TIME when it comes across a single T!
 *
 *       We therefore have flex returning the token T_SHARP
 *       when it comes across 'T#'
 */
  TIME '#' interval
	{$$ = new duration_c(new time_type_name_c(locloc(@1)), NULL, $3, locloc(@$));}
| TIME '#' '-' interval
	{$$ = new duration_c(new time_type_name_c(locloc(@1)), new neg_time_c(locloc(@$)), $4, locloc(@$));}
| T_SHARP interval
	{$$ = new duration_c(new time_type_name_c(locloc(@1)), NULL, $2, locloc(@$));}
| T_SHARP '-' interval
	{$$ = new duration_c(new time_type_name_c(locloc(@1)), new neg_time_c(locloc(@$)), $3, locloc(@$));}
| SAFETIME '#' interval
	{$$ = new duration_c(new safetime_type_name_c(locloc(@1)), NULL, $3, locloc(@$));}
| SAFETIME '#' '-' interval
	{$$ = new duration_c(new safetime_type_name_c(locloc(@1)), new neg_time_c(locloc(@$)), $4, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| TIME interval
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between 'TIME' and interval in duration."); yynerrs++;}
| TIME '-' interval
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between 'TIME' and interval in duration."); yynerrs++;}
| TIME '#' erroneous_interval_token
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid value for duration."); yynerrs++;}
| T_SHARP erroneous_interval_token
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid value for duration."); yynerrs++;}
| TIME '#' '-' erroneous_interval_token
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid value for duration."); yynerrs++;}
| T_SHARP '-' erroneous_interval_token
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid value for duration."); yynerrs++;}
/* ERROR_CHECK_END */
;

fixed_point:
  integer
| fixed_point_token	{$$ = new fixed_point_c($1, locloc(@$));};


interval:
  days hours minutes seconds milliseconds end_interval_token
	{$$ = new interval_c($1, $2, $3, $4, $5, locloc(@$));};
;


days:   /*  fixed_point ('d') */
  /* empty */		{$$ = NULL;}
| fixed_point_d_token	{$$ = new fixed_point_c($1, locloc(@$));};
| integer_d_token	{$$ = new integer_c($1, locloc(@$));};
;

hours:  /*  fixed_point ('h') */
  /* empty */		{$$ = NULL;}
| fixed_point_h_token	{$$ = new fixed_point_c($1, locloc(@$));};
| integer_h_token	{$$ = new integer_c($1, locloc(@$));};
;

minutes: /*  fixed_point ('m') */
  /* empty */		{$$ = NULL;}
| fixed_point_m_token	{$$ = new fixed_point_c($1, locloc(@$));};
| integer_m_token	{$$ = new integer_c($1, locloc(@$));};
;

seconds: /*  fixed_point ('s') */
  /* empty */		{$$ = NULL;}
| fixed_point_s_token	{$$ = new fixed_point_c($1, locloc(@$));};
| integer_s_token	{$$ = new integer_c($1, locloc(@$));};
;

milliseconds: /*  fixed_point ('ms') */
  /* empty */		{$$ = NULL;}
| fixed_point_ms_token	{$$ = new fixed_point_c($1, locloc(@$));};
| integer_ms_token	{$$ = new integer_c($1, locloc(@$));};
;



/************************************/
/* B 1.2.3.2 - Time of day and Date */
/************************************/
time_of_day:
  TIME_OF_DAY '#' daytime
	{$$ = new time_of_day_c(new tod_type_name_c(locloc(@1)), $3, locloc(@$));}
| SAFETIME_OF_DAY '#' daytime
	{$$ = new time_of_day_c(new safetod_type_name_c(locloc(@1)), $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| TIME_OF_DAY daytime
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between 'TIME_OF_DAY' and daytime in time of day."); yynerrs++;}
| TIME_OF_DAY '#' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for time of day.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value for time of day."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


daytime:
  day_hour ':' day_minute ':' day_second
	{$$ = new daytime_c($1, $3, $5, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| ':' day_minute ':' day_second
  {$$ = NULL; print_err_msg(locf(@1), locl(@4), "no value defined for hours in daytime."); yynerrs++;}
| error ':' day_minute ':' day_second
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid value defined for hours in daytime."); yyerrok;}
| day_hour day_minute ':' day_second
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between hours and minutes in daytime."); yynerrs++;}
| day_hour ':' ':' day_second
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no value defined for minutes in daytime."); yynerrs++;}
| day_hour ':' error ':' day_second
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid value defined for minutes in daytime."); yyerrok;}
| day_hour ':' day_minute day_second
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "':' missing between minutes and seconds in daytime."); yynerrs++;}
| day_hour ':' day_minute ':' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@4), locf(@5), "no value defined for seconds in daytime.");}
	 else {print_err_msg(locf(@5), locl(@5), "invalid value for seconds in daytime."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


day_hour: integer;
day_minute: integer;
day_second: fixed_point;


date:
  DATE '#' date_literal
	{$$ = new date_c(new date_type_name_c(locloc(@1)), $3, locloc(@$));}
| D_SHARP date_literal
	{$$ = new date_c(new date_type_name_c(locloc(@1)), $2, locloc(@$));}
| SAFEDATE '#' date_literal
	{$$ = new date_c(new safedate_type_name_c(locloc(@1)), $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| DATE date_literal
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between 'DATE' and date literal in date."); yynerrs++;}
| DATE '#' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for date.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value for date."); yyclearin;}
	 yyerrok;
	}
| D_SHARP error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@1), locf(@2), "no value defined for date.");}
	 else {print_err_msg(locf(@2), locl(@2), "invalid value for date."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


date_literal:
  year '-' month '-' day
	{$$ = new date_literal_c($1, $3, $5, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| '-' month '-' day
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no value defined for year in date literal."); yynerrs++;}
| year month '-' day
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "'-' missing between year and month in date literal."); yynerrs++;}
| year '-' '-' day
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no value defined for month in date literal."); yynerrs++;}
| year '-' error '-' day
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid value defined for month in date literal."); yyerrok;}
| year '-' month day
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "'-' missing between month and day in date literal."); yynerrs++;}
| year '-' month '-' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@4), locf(@5), "no value defined for day in date literal.");}
	 else {print_err_msg(locf(@5), locl(@5), "invalid value for day in date literal."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


year: integer;
month: integer;
day: integer;


date_and_time:
  DATE_AND_TIME '#' date_literal '-' daytime
	{$$ = new date_and_time_c(new dt_type_name_c(locloc(@1)), $3, $5, locloc(@$));}
| SAFEDATE_AND_TIME '#' date_literal '-' daytime
	{$$ = new date_and_time_c(new safedt_type_name_c(locloc(@1)), $3, $5, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| DATE_AND_TIME date_literal '-' daytime
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between 'DATE_AND_TIME' and date literal in date and time."); yynerrs++;}
| DATE_AND_TIME '#' '-' daytime
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no value defined for date literal in date and time."); yynerrs++;}
| DATE_AND_TIME '#' error '-' daytime
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid value for date literal in date and time."); yyerrok;}
| DATE_AND_TIME '#' date_literal daytime
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "'-' missing between date literal and daytime in date and time."); yynerrs++;}
| DATE_AND_TIME '#' date_literal '-' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@4), locf(@5), "no value defined for daytime in date and time.");}
	 else {print_err_msg(locf(@5), locl(@5), "invalid value for daytime in date and time."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;






/**********************/
/* B 1.3 - Data Types */
/**********************/
/* Strangely, the following symbol does seem to be required! */
/*
data_type_name:
  non_generic_type_name
| generic_type_name
;
*/

non_generic_type_name:
  elementary_type_name
| derived_type_name
;



/***********************************/
/* B 1.3.1 - Elementary Data Types */
/***********************************/
    /******************************************************/
    /* SAFExxxx Symbols defined in                        */
    /* "Safety Software Technical Specification,          */
    /*  Part 1: Concepts and Function Blocks,             */
    /*  Version 1.0 – Official Release"                   */
    /* by PLCopen - Technical Committee 5 - 2006-01-31    */
    /******************************************************/

elementary_type_name:
  numeric_type_name
| date_type_name
| bit_string_type_name
| elementary_string_type_name
| TIME		{$$ = new time_type_name_c(locloc(@$));}
| BOOL		{$$ = new bool_type_name_c(locloc(@$));}
/* NOTE: see note under the B 1.2.1 section of token
 * and grouping type definition for reason why BOOL
 * was added to this definition.
 */
| SAFETIME	{$$ = new safetime_type_name_c(locloc(@$));}
| SAFEBOOL	{$$ = new safebool_type_name_c(locloc(@$));}
;

numeric_type_name:
  integer_type_name
| real_type_name
;

integer_type_name:
  signed_integer_type_name
| unsigned_integer_type_name
;

signed_integer_type_name:
  SINT		{$$ = new sint_type_name_c(locloc(@$));}
| INT		{$$ = new int_type_name_c(locloc(@$));}
| DINT		{$$ = new dint_type_name_c(locloc(@$));}
| LINT		{$$ = new lint_type_name_c(locloc(@$));}
| SAFESINT	{$$ = new safesint_type_name_c(locloc(@$));}
| SAFEINT	{$$ = new safeint_type_name_c(locloc(@$));}
| SAFEDINT	{$$ = new safedint_type_name_c(locloc(@$));}
| SAFELINT	{$$ = new safelint_type_name_c(locloc(@$));}
;

unsigned_integer_type_name:
  USINT		{$$ = new usint_type_name_c(locloc(@$));}
| UINT		{$$ = new uint_type_name_c(locloc(@$));}
| UDINT		{$$ = new udint_type_name_c(locloc(@$));}
| ULINT		{$$ = new ulint_type_name_c(locloc(@$));}
| SAFEUSINT	{$$ = new safeusint_type_name_c(locloc(@$));}
| SAFEUINT	{$$ = new safeuint_type_name_c(locloc(@$));}
| SAFEUDINT	{$$ = new safeudint_type_name_c(locloc(@$));}
| SAFEULINT	{$$ = new safeulint_type_name_c(locloc(@$));}
;

real_type_name:
  REAL		{$$ = new real_type_name_c(locloc(@$));}
| LREAL		{$$ = new lreal_type_name_c(locloc(@$));}
| SAFEREAL	{$$ = new safereal_type_name_c(locloc(@$));}
| SAFELREAL	{$$ = new safelreal_type_name_c(locloc(@$));}
;

date_type_name:
  DATE			{$$ = new date_type_name_c(locloc(@$));}
| TIME_OF_DAY		{$$ = new tod_type_name_c(locloc(@$));}
| TOD			{$$ = new tod_type_name_c(locloc(@$));}
| DATE_AND_TIME		{$$ = new dt_type_name_c(locloc(@$));}
| DT			{$$ = new dt_type_name_c(locloc(@$));}
| SAFEDATE		{$$ = new safedate_type_name_c(locloc(@$));}
| SAFETIME_OF_DAY	{$$ = new safetod_type_name_c(locloc(@$));}
| SAFETOD		{$$ = new safetod_type_name_c(locloc(@$));}
| SAFEDATE_AND_TIME	{$$ = new safedt_type_name_c(locloc(@$));}
| SAFEDT		{$$ = new safedt_type_name_c(locloc(@$));}
;


bit_string_type_name:
  BYTE		{$$ = new byte_type_name_c(locloc(@$));}
| WORD		{$$ = new word_type_name_c(locloc(@$));}
| DWORD		{$$ = new dword_type_name_c(locloc(@$));}
| LWORD		{$$ = new lword_type_name_c(locloc(@$));}
| SAFEBYTE	{$$ = new safebyte_type_name_c(locloc(@$));}
| SAFEWORD	{$$ = new safeword_type_name_c(locloc(@$));}
| SAFEDWORD	{$$ = new safedword_type_name_c(locloc(@$));}
| SAFELWORD	{$$ = new safelword_type_name_c(locloc(@$));}
/* NOTE: see note under the B 1.2.1 section of token
 * and grouping type definition for reason why the BOOL
 * was omitted from this definition.
 */
;


/* Helper symbol to concentrate the instantiation
 * of STRING and WSTRING into a single location.
 *
 * These two elements show up in several other rules,
 * but we want to create the equivalent abstract syntax
 * in a single location of this file, in order to make
 * possible future changes easier to edit...
 */
elementary_string_type_name:
  STRING	{$$ = new string_type_name_c(locloc(@$));}
| WSTRING	{$$ = new wstring_type_name_c(locloc(@$));}
| SAFESTRING	{$$ = new safestring_type_name_c(locloc(@$));}
| SAFEWSTRING	{$$ = new safewstring_type_name_c(locloc(@$));}
;



/********************************/
/* B 1.3.2 - Generic data types */
/********************************/
/* Strangely, the following symbol does not seem to be required! */
/*
generic_type_name:
  ANY
| ANY_DERIVED
| ANY_ELEMENTARY
| ANY_MAGNITUDE
| ANY_NUM
| ANY_REAL
| ANY_INT
| ANY_BIT
| ANY_STRING
| ANY_DATE
;
*/


/********************************/
/* B 1.3.3 - Derived data types */
/********************************/

derived_type_name:
  single_element_type_name
| prev_declared_array_type_name
| prev_declared_structure_type_name
| prev_declared_string_type_name
| prev_declared_ref_type_name  /* as defined in IEC 61131-3 v3 */
;

single_element_type_name:
  prev_declared_simple_type_name
/* Include the following if arrays of function blocks are to be allowed!
 * Since the standard does not allow them,
 * we leave it commented out for the time being...
 */
//| prev_declared_derived_function_block_name
| prev_declared_subrange_type_name
| prev_declared_enumerated_type_name
;

/* NOTE: in order to remove a reduce/reduce conflict,
 *       all occurences of simple_type_name, etc...
 *       have been replaced with identifier!
 */
/*
simple_type_name: identifier;
subrange_type_name: identifier;
enumerated_type_name: identifier;
array_type_name: identifier;
structure_type_name: identifier;
*/

data_type_declaration:
  TYPE type_declaration_list END_TYPE
	{$$ = new data_type_declaration_c($2, locloc(@$)); if (runtime_options.conversion_functions) include_string((create_enumtype_conversion_functions_c::get_declaration($$)).c_str());}
/* ERROR_CHECK_BEGIN */
| TYPE END_TYPE
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no data type declared in data type(s) declaration."); yynerrs++;}
| TYPE error type_declaration_list END_TYPE
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'TYPE' in data type(s) declaration."); yyerrok;}
| TYPE type_declaration_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed data type(s) declaration."); yyerrok;}
| TYPE error END_TYPE
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in data type(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

/* helper symbol for data_type_declaration */
type_declaration_list:
  type_declaration ';'
	{$$ = new type_declaration_list_c(locloc(@$)); $$->add_element($1);}
| type_declaration_list type_declaration ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| error ';'
	{$$ = new type_declaration_list_c(locloc(@$)); print_err_msg(locf(@1), locl(@1), "invalid data type declaration."); yyerrok;}
| type_declaration error
	{$$ = new type_declaration_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at end of data type declaration."); yyerrok;}
| type_declaration_list type_declaration error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of data type declaration."); yyerrok;}
| type_declaration_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid data type declaration."); yyerrok;}
| type_declaration_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after data type declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;

type_declaration:
  single_element_type_declaration
| array_type_declaration
| structure_type_declaration
| string_type_declaration
;

single_element_type_declaration:
  simple_type_declaration
| subrange_type_declaration
| enumerated_type_declaration
| ref_type_decl  /* defined in IEC 61131-3 v3 */
;

simple_type_declaration:
/*  simple_type_name ':' simple_spec_init */
/* To understand why simple_spec_init was brocken up into its consituent components in the following rules, please see note in the definition of 'enumerated_type_declaration'. */
/* PRE_PARSING or SINGLE_PHASE_PARSING */
/*  The following rules will be run either by:
 *      - the pre_parsing phase of two phase parsing (when preparsing command line option is chosen).
 *      - the standard single phase parser (when preparsing command line option is not chosen).
 */
  identifier ':' simple_specification           {library_element_symtable.insert($1, prev_declared_simple_type_name_token);}
	{if (!get_preparse_state()) $$ = new simple_type_declaration_c($1, $3, locloc(@$));}
| identifier ':' elementary_type_name           {library_element_symtable.insert($1, prev_declared_simple_type_name_token);} ASSIGN constant
	{if (!get_preparse_state()) $$ = new simple_type_declaration_c($1, new simple_spec_init_c($3, $6, locf(@3), locl(@5)), locloc(@$));}
| identifier ':' prev_declared_simple_type_name {library_element_symtable.insert($1, prev_declared_simple_type_name_token);} ASSIGN constant
	{if (!get_preparse_state()) $$ = new simple_type_declaration_c($1, new simple_spec_init_c($3, $6, locf(@3), locl(@5)), locloc(@$));}
/* POST_PARSING */
/*  These rules will be run after the preparser phase of two phase parsing has finished (only gets to execute if preparsing command line option is chosen). */
| prev_declared_simple_type_name ':' simple_spec_init
	{$$ = new simple_type_declaration_c(new identifier_c(((token_c *)$1)->value, locloc(@1)), $3, locloc(@$));} // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
/* These three rules can now be safely replaced by the original rule abvoe!! */
/*
| prev_declared_simple_type_name ':' simple_specification
	{$$ = new simple_type_declaration_c($1, $3, locloc(@$));}
| prev_declared_simple_type_name ':' elementary_type_name           ASSIGN constant
	{$$ = new simple_type_declaration_c($1, new simple_spec_init_c($3, $5, locf(@3), locl(@5)), locloc(@$));}
| prev_declared_simple_type_name ':' prev_declared_simple_type_name ASSIGN constant
	{$$ = new simple_type_declaration_c($1, new simple_spec_init_c($3, $5, locf(@3), locl(@5)), locloc(@$));}
*/
/* ERROR_CHECK_BEGIN */
| error ':' simple_spec_init
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid name defined for data type declaration.");yyerrok;}
| identifier simple_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between data type name and specification in simple type declaration."); yynerrs++;}
| identifier ':' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no specification defined in data type declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid specification in data type declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


simple_spec_init:
  simple_specification
  /* The following commented line was changed to the 
   * next two lines so that we wouldn't
   * have the first element of a simple_spec_init_c()
   * pointing to another simple_spec_init_c!
   */
/*
| simple_specification ASSIGN constant
	{$$ = new simple_spec_init_c($1, $3);}
*/
| elementary_type_name ASSIGN constant
	{$$ = new simple_spec_init_c($1, $3, locloc(@$));}
| prev_declared_simple_type_name ASSIGN constant
	{$$ = new simple_spec_init_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| elementary_type_name constant
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing in specification with initialization."); yynerrs++;}
| prev_declared_simple_type_name constant
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing in specification with initialization."); yynerrs++;}
| elementary_type_name ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no initial value defined in specification with initialization.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid initial value in specification with initialization."); yyclearin;}
	 yyerrok;
	}
| prev_declared_simple_type_name ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no initial value defined in specification with initialization.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid initial value in specification with initialization."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/* When converting to C/C++, we need to know whether
 * the elementary_type_name is being used in a variable
 * declaration or elsewhere (ex. declaration of a derived
 * type), so the abstract syntax has the elementary_type_name
 * wrapped inside a simple_spec_init_c.
 * The exact same thing occurs with prev_declared_simple_type_name.
 *
 * This is why in the definition of simple_spec_init,
 * simple_specification was brocken up into its
 * constituent components...
 */
simple_specification:
// elementary_type_name | simple_type_name
  elementary_type_name
	{$$ = new simple_spec_init_c($1, NULL, locloc(@$));}
| prev_declared_simple_type_name
	{$$ = new simple_spec_init_c($1, NULL, locloc(@$));}
;


subrange_type_declaration:
/*  subrange_type_name ':' subrange_spec_init */
/* PRE_PARSING or SINGLE_PHASE_PARSING */
/*  The following rules will be run either by:
 *      - the pre_parsing phase of two phase parsing (when preparsing command line option is chosen).
 *      - the standard single phase parser (when preparsing command line option is not chosen).
 */
  identifier ':' subrange_spec_init	{library_element_symtable.insert($1, prev_declared_subrange_type_name_token);}
	{if (!get_preparse_state()) $$ = new subrange_type_declaration_c($1, $3, locloc(@$));}  
/* POST_PARSING */
/*  These rules will be run after the preparser phase of two phase parsing has finished (only gets to execute if preparsing command line option is chosen). */
| prev_declared_subrange_type_name ':' subrange_spec_init
	{$$ = new subrange_type_declaration_c(new identifier_c(((token_c *)$1)->value, locloc(@1)), $3, locloc(@$));} // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
/* ERROR_CHECK_BEGIN */
| error ':' subrange_spec_init
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid name defined for subrange type declaration."); yyerrok;}
| identifier subrange_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between data type name and specification in subrange type declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;

subrange_spec_init:
  subrange_specification
	{$$ = new subrange_spec_init_c($1, NULL, locloc(@$));}
| subrange_specification ASSIGN signed_integer
	{$$ = new subrange_spec_init_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| subrange_specification signed_integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing in subrange specification with initialization."); yynerrs++;}
| subrange_specification ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no initial value defined in subrange specification with initialization.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid initial value in subrange specification with initialization."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

subrange_specification:
  integer_type_name '(' subrange ')'
	{$$ = new subrange_specification_c($1, $3, locloc(@$));}
| prev_declared_subrange_type_name
  {$$ = new subrange_specification_c($1, NULL, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| integer_type_name '(' ')'
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no subrange defined in subrange specification."); yynerrs++;}
| integer_type_name '(' error ')'
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid subrange defined in subrange specification."); yyerrok;}
| integer_type_name '(' subrange error
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "')' missing after subrange defined in subrange specification."); yyerrok;}
/* ERROR_CHECK_END */
;


/* a non standard construct, used to allow the declaration of array subranges using a variable */
subrange_with_var:
  signed_integer DOTDOT signed_integer
	{$$ = new subrange_c($1, $3, locloc(@$));}
| any_identifier DOTDOT signed_integer
	{$$ = new subrange_c(new symbolic_constant_c($1, locloc(@1)), $3, locloc(@$));
	 if (!runtime_options.nonliteral_in_array_size) {
	   print_err_msg(locf(@1), locl(@1), "Use of variables in array size limits is not allowed in IEC 61131-3 (use -a option to activate support for this non-standard feature)."); 
	   yynerrs++;
	 }
	}
| signed_integer DOTDOT any_identifier
	{$$ = new subrange_c($1, new symbolic_constant_c($3, locloc(@3)), locloc(@$));
	 if (!runtime_options.nonliteral_in_array_size) {
	   print_err_msg(locf(@3), locl(@3), "Use of variables in array size limits is not allowed in IEC 61131-3 (use -a option to activate support for this non-standard feature)."); 
	   yynerrs++;
	 }
	}
| any_identifier DOTDOT any_identifier
	{$$ = new subrange_c(new symbolic_constant_c($1, locloc(@1)), new symbolic_constant_c($3, locloc(@3)), locloc(@$));
	 if (!runtime_options.nonliteral_in_array_size) {
	   print_err_msg(locf(@$), locl(@$), "Use of variables in array size limits is not allowed in IEC 61131-3 (use -a option to activate support for this non-standard feature)."); 
	   yynerrs++;
	 }
	}
/* ERROR_CHECK_BEGIN */
| signed_integer signed_integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'..' missing between bounds in subrange definition."); yynerrs++;}
| signed_integer DOTDOT error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for upper bound in subrange definition.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value for upper bound in subrange definition."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


subrange:
  signed_integer DOTDOT signed_integer
	{$$ = new subrange_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| signed_integer signed_integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'..' missing between bounds in subrange definition."); yynerrs++;}
| signed_integer DOTDOT error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for upper bound in subrange definition.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value for upper bound in subrange definition."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


enumerated_type_declaration:
/*  enumerated_type_name ':' enumerated_spec_init */
/* NOTE: The 'identifier' used for the name of the new enumerated type is inserted early into the library_element_symtable so it may be used
 *       in defining the default initial value of this type, using the fully qualified enumerated constant syntax: type_name#enum_value
 *       In other words, this allows us to correclty parse the following IEC 61131-3 code:
 *           TYPE enum_t : (x1, x2, x3) := enum_t#x3; END_TYPE
 *                                         ^^^^^^^
 *
 *       However, we can only introduce it after we are sure we are parsing an enumerated_spec. For this reason, instead of using the
 *       symbol enumerated_spec_init in this rule, we decompose it here instead!
 *       
 *       If it were not for the above, we could use the rule
 *           identifier ':' enumerated_spec_init
 *       and include the library_element_symtable.insert(...) code in the rule actions!
 */
/* PRE_PARSING or SINGLE_PHASE_PARSING */
/*  The following rules will be run either by:
 *      - the pre_parsing phase of two phase parsing (when preparsing command line option is chosen).
 *      - the standard single phase parser (when preparsing command line option is not chosen).
 */
  identifier ':' enumerated_specification {library_element_symtable.insert($1, prev_declared_enumerated_type_name_token);}
	{if (!get_preparse_state()) $$ = new enumerated_type_declaration_c($1, new enumerated_spec_init_c($3, NULL, locloc(@3)), locloc(@$));}
| identifier ':' enumerated_specification {library_element_symtable.insert($1, prev_declared_enumerated_type_name_token);} ASSIGN enumerated_value
	{if (!get_preparse_state()) $$ = new enumerated_type_declaration_c($1, new enumerated_spec_init_c($3, $6, locf(@3), locl(@6)), locloc(@$));}
/* POST_PARSING */
/*  These rules will be run after the preparser phase of two phase parsing has finished (only gets to execute if preparsing command line option is chosen). */
/* Since the enumerated type name is placed in the library_element_symtable during preparsing, we can now safely use the single rule: */
| prev_declared_enumerated_type_name ':' enumerated_spec_init 
	{$$ = new enumerated_type_declaration_c(new identifier_c(((token_c *)$1)->value, locloc(@1)), $3, locloc(@$));} // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
  /* These two rules are equivalent to the above rule */
/*
| prev_declared_enumerated_type_name ':' enumerated_specification {library_element_symtable.insert($1, prev_declared_enumerated_type_name_token);}
	{$$ = new enumerated_type_declaration_c($1, new enumerated_spec_init_c($3, NULL, locloc(@3)), locloc(@$));}
| prev_declared_enumerated_type_name ':' enumerated_specification {library_element_symtable.insert($1, prev_declared_enumerated_type_name_token);} ASSIGN enumerated_value
	{$$ = new enumerated_type_declaration_c($1, new enumerated_spec_init_c($3, $6, locf(@3), locl(@6)), locloc(@$));}
*/
/* ERROR_CHECK_BEGIN */
| error ':' enumerated_spec_init
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid name defined for enumerated type declaration."); yyerrok;}
| identifier enumerated_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between data type name and specification in enumerated type declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


enumerated_spec_init:
  enumerated_specification
	{$$ = new enumerated_spec_init_c($1, NULL, locloc(@$));}
| enumerated_specification ASSIGN enumerated_value
	{$$ = new enumerated_spec_init_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| enumerated_specification enumerated_value
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing in enumerated specification with initialization."); yynerrs++;}
| enumerated_specification ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined in enumerated specification with initialization.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value in enumerated specification with initialization."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

enumerated_specification:
  '(' enumerated_value_list ')'
	{$$ = $2;}
| prev_declared_enumerated_type_name
/* ERROR_CHECK_BEGIN */
| '(' ')'
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no enumerated value list defined in enumerated specification."); yynerrs++;}
| '(' error ')'
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid enumerated value list defined in enumerated specification.");yyerrok;}
| '(' enumerated_value_list error
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "')' missing at the end of enumerated specification."); yyerrok;}
/* ERROR_CHECK_END */
;

/* helper symbol for enumerated_specification */
enumerated_value_list:
  enumerated_value
	{$$ = new enumerated_value_list_c(locloc(@$)); $$->add_element($1);}
| enumerated_value_list ',' enumerated_value
	{$$ = $1; $$->add_element($3);}
/* ERROR_CHECK_BEGIN */
| enumerated_value_list enumerated_value
	{$$ = $1; print_err_msg(locl(@1), locf(@2), "',' missing in enumerated value list.");}
| enumerated_value_list ',' error
	{$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined in enumerated value list.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value in enumerated value list."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


enumerated_value:
  identifier 
  {$$ = new enumerated_value_c(NULL, $1, locloc(@$));}
| prev_declared_enumerated_type_name '#' any_identifier
	{$$ = new enumerated_value_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| prev_declared_enumerated_type_name any_identifier
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between enumerated type name and value in enumerated literal."); yynerrs++;}
| prev_declared_enumerated_type_name '#' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for enumerated literal.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value for enumerated literal."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


/*
enumerated_value_without_identifier:
  prev_declared_enumerated_type_name '#' any_identifier
	{$$ = new enumerated_value_c($1, $3, locloc(@$));}
;
*/


array_type_declaration:
/*  array_type_name ':' array_spec_init */
/* PRE_PARSING or SINGLE_PHASE_PARSING */
/*  The following rules will be run either by:
 *      - the pre_parsing phase of two phase parsing (when preparsing command line option is chosen).
 *      - the standard single phase parser (when preparsing command line option is not chosen).
 */
  identifier ':' array_spec_init   {library_element_symtable.insert($1, prev_declared_array_type_name_token);}
	{if (!get_preparse_state()) $$ = new array_type_declaration_c($1, $3, locloc(@$));}
/* POST_PARSING */
/*  These rules will be run after the preparser phase of two phase parsing has finished (only gets to execute if preparsing command line option is chosen). */
| prev_declared_array_type_name ':' array_spec_init
	{$$ = new array_type_declaration_c(new identifier_c(((token_c *)$1)->value, locloc(@1)), $3, locloc(@$));} // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
/* ERROR_CHECK_BEGIN */
| identifier array_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between data type name and specification in array type declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;

array_spec_init:
  array_specification
	{$$ = new array_spec_init_c($1, NULL, locloc(@$));}
| array_specification ASSIGN array_initialization
	{$$ = new array_spec_init_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| array_specification array_initialization
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing in array specification with initialization."); yynerrs++;}
| array_specification ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no initial value defined in array specification with initialization.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid initial value in array specification with initialization."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


array_specification:
  prev_declared_array_type_name
| ARRAY '[' array_subrange_list ']' OF non_generic_type_name
	{$$ = new array_specification_c($3, $6, locloc(@$));}
| ARRAY '[' array_subrange_list ']' OF ref_spec_non_recursive
	/* non standard extension: Allow use of arrays storing REF_TO datatypes that are declared as 'ARRAY [1..3] OF REF_TO INT' */
	/*                                                                                                            ^^^^^^      */
	/* NOTE: We use ref_spec and not ref_spec_init as for the moment I do not want to allow direct specification of initial value.
	 *       I (MJS) am not too sure whether this is currently supported in code generation, so leave it out for now.
	 *       It also does not seem to be a very good idea to allow initial value specification when declaring the array,
	 *       since the standard syntax does not allow it either for any other datatype!
	 * NOTE: We use ref_spec_non_recursive instead of ref_spec in order to remove a reduce/reduce conflict.
	 *       Note that non_generic_type_name that is used in the previous rule already include the prev_declared_ref_type_name.
	 *       which leads to the reduce/reduce conflict, as it is also included in ref_spec.
	 */
	{$$ = new array_specification_c($3, $6, locloc(@$));
	 if (!allow_ref_to_in_derived_datatypes) {
	   print_err_msg(locf(@$), locl(@$), "REF_TO may not be used in an ARRAY specification (use -R option to activate support for this non-standard syntax)."); 
	   yynerrs++;
	 }
	}
/* ERROR_CHECK_BEGIN */
| ARRAY array_subrange_list ']' OF non_generic_type_name
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'[' missing before subrange list in array specification."); yynerrs++;}
| ARRAY '[' ']' OF non_generic_type_name
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no subrange list defined in array specification."); yynerrs++;}
| ARRAY '[' error ']' OF non_generic_type_name
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid subrange list defined in array specification."); yyerrok;}
| ARRAY OF non_generic_type_name
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no subrange list defined in array specification."); yynerrs++;}
| ARRAY error OF non_generic_type_name
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid subrange list defined in array specification."); yyerrok;}
| ARRAY '[' array_subrange_list OF non_generic_type_name
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "']' missing after subrange list in array specification."); yynerrs++;}
| ARRAY '[' array_subrange_list ']' non_generic_type_name
	{$$ = NULL; print_err_msg(locl(@4), locf(@5), "'OF' missing between subrange list and item type name in array specification."); yynerrs++;}
| ARRAY '[' array_subrange_list ']' OF error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no item data type defined in array specification.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid item data type in array specification."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/* helper symbol for array_specification */
array_subrange_list:
/* the construct 'subrange' has been replaced with 'subrange_with_var' in order to support the declaration of array ranges using a varable: e.g. ARRAY [2..max] OF INT */
  subrange_with_var
	{$$ = new array_subrange_list_c(locloc(@$)); $$->add_element($1);}
| array_subrange_list ',' subrange_with_var
	{$$ = $1; $$->add_element($3);}
/* ERROR_CHECK_BEGIN */
| array_subrange_list subrange
	{$$ = $1; print_err_msg(locl(@1), locf(@2), "',' missing in subrange list."); yynerrs++;}
| array_subrange_list ',' error
	{$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no subrange defined in subrange list.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid subrange in subrange list."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


array_initialization:
  '[' array_initial_elements_list ']'
	{$$ = $2;}
/* ERROR_CHECK_BEGIN */
| '[' ']'
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no initial values list defined in array initialization."); yynerrs++;}
| '[' error ']'
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid initial values list defined in array initialization."); yyerrok;}
| '[' array_initial_elements_list error
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "']' missing at the end of array initialization."); yyerrok;}
/* ERROR_CHECK_END */
;


/* helper symbol for array_initialization */
array_initial_elements_list:
  array_initial_elements
	{$$ = new array_initial_elements_list_c(locloc(@$)); $$->add_element($1);}
| array_initial_elements_list ',' array_initial_elements
	{$$ = $1; $$->add_element($3);}
/* ERROR_CHECK_BEGIN */
/* The following error checking rules have been commented out. Why? Was it a typo? 
 * Lets keep them commented out for now...
 */
/*
| array_initial_elements_list ',' error
	{$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no array initial value in array initial values list.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid array initial value in array initial values list."); yyclearin;}
	 yyerrok;
	}
*/
/* ERROR_CHECK_END */
;


array_initial_elements:
  array_initial_element
| integer '(' ')'
	{$$ = new array_initial_elements_c($1, NULL, locloc(@$));}
| integer '(' array_initial_element ')'
	{$$ = new array_initial_elements_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| integer '(' error ')'
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid array initial value in array initial values list."); yyerrok;}
| integer '(' array_initial_element error
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "')' missing at the end of array initial value in array initial values list."); yyerrok;}
/* ERROR_CHECK_END */
;


array_initial_element:
  constant
| enumerated_value
| structure_initialization
| array_initialization
;



structure_type_declaration:
/*  structure_type_name ':' structure_specification */
/* PRE_PARSING or SINGLE_PHASE_PARSING */
/*  The following rules will be run either by:
 *      - the pre_parsing phase of two phase parsing (when preparsing command line option is chosen).
 *      - the standard single phase parser (when preparsing command line option is not chosen).
 */
  identifier ':' structure_specification  {library_element_symtable.insert($1, prev_declared_structure_type_name_token);}
	{if (!get_preparse_state()) $$ = new structure_type_declaration_c($1, $3, locloc(@$));}
/* POST_PARSING */
/*  These rules will be run after the preparser phase of two phase parsing has finished (only gets to execute if preparsing command line option is chosen). */
| prev_declared_structure_type_name ':' structure_specification
	{$$ = new structure_type_declaration_c(new identifier_c(((token_c *)$1)->value, locloc(@1)), $3, locloc(@$));} // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
/* ERROR_CHECK_BEGIN */
| identifier structure_specification
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between data type name and specification in structure type declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


structure_specification:
  structure_declaration
| initialized_structure
;


initialized_structure:
  prev_declared_structure_type_name
	{$$ = new initialized_structure_c($1, NULL, locloc(@$));}
| prev_declared_structure_type_name ASSIGN structure_initialization
	{$$ = new initialized_structure_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| prev_declared_structure_type_name structure_initialization
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing in structure specification with initialization."); yynerrs++;}
| prev_declared_structure_type_name ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined in structure specification with initialization.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value in structure specification with initialization."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


structure_declaration:
  STRUCT structure_element_declaration_list END_STRUCT
	{$$ = $2;}
/* ERROR_CHECK_BEGIN */
| STRUCT END_STRUCT
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no structure element declared in structure type declaration."); yynerrs++;}
| STRUCT error structure_element_declaration_list END_STRUCT
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'STRUCT' in structure type declaration."); yyerrok;}
| STRUCT structure_element_declaration_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed structure type declaration."); yyerrok;}
| STRUCT error END_STRUCT
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in structure type declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

/* helper symbol for structure_declaration */
structure_element_declaration_list:
  structure_element_declaration ';'
	{$$ = new structure_element_declaration_list_c(locloc(@$)); $$->add_element($1);}
| structure_element_declaration_list structure_element_declaration ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| error ';'
	{$$ = new structure_element_declaration_list_c(locloc(@$)); print_err_msg(locf(@1), locl(@1), "invalid structure element declaration."); yyerrok;}
| structure_element_declaration error
	{$$ = new structure_element_declaration_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at end of structure element declaration."); yyerrok;}
| structure_element_declaration_list structure_element_declaration error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of structure element declaration."); yyerrok;}
| structure_element_declaration_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid structure element declaration."); yyerrok;}
| structure_element_declaration_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after structure element declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


structure_element_declaration:
  structure_element_name ':' simple_spec_init
	{$$ = new structure_element_declaration_c($1, $3, locloc(@$)); $$->token = $1->token;}
| structure_element_name ':' subrange_spec_init
	{$$ = new structure_element_declaration_c($1, $3, locloc(@$)); $$->token = $1->token;}
| structure_element_name ':' enumerated_spec_init
	{$$ = new structure_element_declaration_c($1, $3, locloc(@$)); $$->token = $1->token;}
| structure_element_name ':' array_spec_init
	{$$ = new structure_element_declaration_c($1, $3, locloc(@$)); $$->token = $1->token;}
| structure_element_name ':' initialized_structure
	{$$ = new structure_element_declaration_c($1, $3, locloc(@$)); $$->token = $1->token;}
| structure_element_name ':' ref_spec_init                              /* non standard extension: Allow use of struct elements storing REF_TO datatypes (either using REF_TO or a previosuly declared ref type) */
	{ $$ = new structure_element_declaration_c($1, $3, locloc(@$));
	  if (!allow_ref_to_in_derived_datatypes) {
	    print_err_msg(locf(@$), locl(@$), "REF_TO and reference datatypes may not be used in a STRUCT element (use -R option to activate support for this non-standard syntax)."); 
	    yynerrs++;
	  }
	}
/* ERROR_CHECK_BEGIN */
| structure_element_name simple_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between structure element name and simple specification."); yynerrs++;}
| structure_element_name subrange_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between structure element name and subrange specification."); yynerrs++;}
| structure_element_name enumerated_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between structure element name and enumerated specification."); yynerrs++;}
| structure_element_name array_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between structure element name and array specification."); yynerrs++;}
| structure_element_name initialized_structure
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between structure element name and structure specification."); yynerrs++;}
| structure_element_name ':' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no specification defined in structure element declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid specification in structure element declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


structure_element_name: any_identifier;


structure_initialization:
  '(' structure_element_initialization_list ')'
	{$$ = $2;}
/* ERROR_CHECK_BEGIN */
| '(' error ')'
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid structure element initialization list in structure initialization."); yyerrok;}
| '(' structure_element_initialization_list error
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "expecting ')' at the end of structure element initialization list in structure initialization."); yyerrok;}
/* ERROR_CHECK_END */
;

/* helper symbol for structure_initialization */
structure_element_initialization_list:
  structure_element_initialization
	{$$ = new structure_element_initialization_list_c(locloc(@$)); $$->add_element($1);}
| structure_element_initialization_list ',' structure_element_initialization
	{$$ = $1; $$->add_element($3);}
/* ERROR_CHECK_BEGIN */
/* The following error checking rules have been commented out. Why? Was it a typo? 
 * Lets keep them commented out for now...
 */
/*
| structure_element_initialization_list structure_element_initialization
	{$$ = $1; print_err_msg(locl(@1), locf(@2), "',' missing in structure element initialization list in structure initialization."); yynerrs++;}
| structure_element_initialization_list ',' error
	{$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no structure element initialization defined in structure initialization.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid structure element initialization in structure initialization."); yyclearin;}
	 yyerrok;
	}
*/
/* ERROR_CHECK_END */
;


structure_element_initialization:
  structure_element_name ASSIGN constant
	{$$ = new structure_element_initialization_c($1, $3, locloc(@$));}
| structure_element_name ASSIGN enumerated_value
	{$$ = new structure_element_initialization_c($1, $3, locloc(@$));}
| structure_element_name ASSIGN array_initialization
	{$$ = new structure_element_initialization_c($1, $3, locloc(@$));}
| structure_element_name ASSIGN structure_initialization
	{$$ = new structure_element_initialization_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| structure_element_name constant
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing in structure element initialization."); yynerrs++;}
| structure_element_name enumerated_value
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing in enumerated structure element initialization."); yynerrs++;}
| structure_element_name array_initialization
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing in array structure element initialization."); yynerrs++;}
| structure_element_name structure_initialization
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing in structured structure element initialization."); yynerrs++;}
| structure_element_name ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no initial value defined in structured structure element initialization.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid initial value in structured structure element initialization."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/* NOTE: in order to remove a reduce/reduce conflict,
 *       all occurences of string_type_name
 *       have been replaced with identifier!
 */
/*
string_type_name: identifier;
*/

string_type_declaration:
/*  string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */
/* PRE_PARSING or SINGLE_PHASE_PARSING */
/*  The following rules will be run either by:
 *      - the pre_parsing phase of two phase parsing (when preparsing command line option is chosen).
 *      - the standard single phase parser (when preparsing command line option is not chosen).
 */
  identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init	{library_element_symtable.insert($1, prev_declared_string_type_name_token);}
	{if (!get_preparse_state()) $$ = new string_type_declaration_c($1, $3, $4, $5, locloc(@$));}
/* POST_PARSING */
/*  These rules will be run after the preparser phase of two phase parsing has finished (only gets to execute if preparsing command line option is chosen). */
| prev_declared_string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init
	{$$ = new string_type_declaration_c(new identifier_c(((token_c *)$1)->value, locloc(@1)), $3, $4, $5, locloc(@$));} // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
;


/* helper symbol for string_type_declaration */
string_type_declaration_size:
  '[' integer ']'
	{$$ = $2;}
/* REMOVED !! */
//|  /* empty */
//	{$$ = NULL;}
;
/* The syntax contains a reduce/reduce conflict.
 * The optional '[' <size> ']'
 * has been changed to become mandatory to remove the conflict.
 *
 * The conflict arises because
 *  new_str_type : STRING := "hello!"
 * may be reduced to a string_type_declaration OR
 * a simple_type_declaration.
 *
 * Our change forces it to be reduced to a
 * simple_type_declaration!
 * We chose this option because changing the definition
 * of simple_spec_init would force us to change all the other
 * rules in which it appears. The change we made has no
 * side-effects!
 */

/* helper symbol for string_type_declaration */
string_type_declaration_init:
  /* empty */
	{$$ = NULL;}
| ASSIGN character_string
	{$$ = $2;}
;


/* Taken fron IEC 61131-3 v3
 * // Table 14 - Reference operations
 * Ref_Type_Decl  : Ref_Type_Name ':' Ref_Spec_Init ;
 * Ref_Spec_Init  : Ref_Spec ( ':=' Ref_Value )? ;
 * Ref_Spec       : 'REF_TO' Non_Gen_Type_Name ;
 * Ref_Type_Name  : Identifier ;
 * Ref_Name       : Identifier ;
 * Ref_Value      : Ref_Addr | 'NULL' ;
 * Ref_Addr       : 'REF' '(' (Symbolic_Variable | FB_Name | Class_Instance_Name ) ')' ;
 * Ref_Assign     : Ref_Name ':=' (Ref_Name | Ref_Deref | Ref_Value ) ;
 * Ref_Deref      : 'DREF' '(' Ref_Name ')' ;
 */

/* NOTE: in IEC 61131-3 v3, the formal syntax definition does not define non_generic_type_name to include FB type names.
 *       However, in section "6.3.4.10 References", example 4 includes a  REF_TO a FB type!
 *       We have therefore explicitly added the "REF_TO function_block_type_name" to this rule!
 * NOTE: the REF_TO ANY is a non-standard extension to the standard. This is basically equivalent to a (void *)
 */
ref_spec_non_recursive: /* helper symbol, used to remove a reduce/reduce conflict in a non-standard syntax I (Mario) have added!! */
  REF_TO non_generic_type_name
	{$$ = new ref_spec_c($2, locloc(@$));}
| REF_TO function_block_type_name
	{$$ = new ref_spec_c($2, locloc(@$));}
| REF_TO ANY
	{$$ = new ref_spec_c(new generic_type_any_c(locloc(@2)), locloc(@$));
	 if (!allow_ref_to_any) {
	   print_err_msg(locf(@$), locl(@$), "REF_TO ANY datatypes are not allowed (use -R option to activate support for this non-standard syntax)."); 
	   yynerrs++;
	 }
	}
;

ref_spec: /* defined in IEC 61131-3 v3 */
  ref_spec_non_recursive
| prev_declared_ref_type_name 
;


/* The IEC 61131-3 v3 standard actually only defines the following syntax:
 * 
 *  Ref_Type_Decl: Ref_Type_Name ':' Ref_Spec_Init;
 *  Ref_Spec_Init: Ref_Spec ( ':=' Ref_Value )?;
 *  Ref_Spec     : 'REF_TO' + Data_Type_Access;
 *
 * Note that the above syntax it is not possible to define a REF_TO datatype as 
 * an alias to an already previously declared REF_TO datatype.
 *
 * I (Mario) believe that this is probably a bug in the IEC 61131-3 syntax, and I have therefore
 * changed that standard definition to...
 *
 *  Ref_Type_Decl: Ref_Type_Name ':' Ref_Spec_Init;
 *  Ref_Spec_Init: Ref_Spec ( ':=' Ref_Value )?;
 *  Ref_Spec     : ('REF_TO' + Data_Type_Access) | Ref_Type_Name;
 *  
 * For example:
 *       TYPE
 *          ref1_t: REF_TO INT;
 *          ref2_t: ref1_t;    <-- without the above changes, this would not be allowed!!
 *       END_TYPE
 *
 * This change also makes it possible to declare variables using a previously declared REF_TO datatype
 *  For example:
 *     VAR  refvar: ref1_t; END_VAR
 *
 * This change also makes it possible to declare arrays containing a previously declared ref type.
 *  For example:
 *     VAR  refvar: ARRAY [1..3] OF ref1_t;     END_VAR   <--- becomes OK
 *     VAR  refvar: ARRAY [1..3] OF REF_TO INT; END_VAR   <--- still not OK. (Only becomes OK with other non-standard rules in another location of this file!)
 *
 * Interestingly, this change does NOT make it possible to declare structure elements of a previously declared ref type.
 *  For example:
 *     TYPE struct_t: STRUCT elem1: ref1_t;     END_STRUCT; END_TYPE;    <--- still not OK. (Only becomes OK with other non-standard rules in another location of this file!)
 *     TYPE struct_t: STRUCT elem1: REF_TO INT; END_STRUCT; END_TYPE;    <--- still not OK. (Only becomes OK with other non-standard rules in another location of this file!)
 */



ref_spec_init: /* defined in IEC 61131-3 v3 */
  ref_spec
	{$$ = new ref_spec_init_c($1, NULL, locloc(@$));}
/*  For the moment, we do not support initialising reference data types...
| ref_spec ASSIGN ... 
	{$$ = new ref_spec_init_c($1, $3, locloc(@$));}
*/
;

ref_type_decl:  /* defined in IEC 61131-3 v3 */
/* PRE_PARSING or SINGLE_PHASE_PARSING */
/*  The following rules will be run either by:
 *      - the pre_parsing phase of two phase parsing (when preparsing command line option is chosen).
 *      - the standard single phase parser (when preparsing command line option is not chosen).
 */
  identifier ':' ref_spec_init  {library_element_symtable.insert($1, prev_declared_ref_type_name_token);}
	{if (!get_preparse_state()) $$ = new ref_type_decl_c($1, $3, locloc(@$));}
/* POST_PARSING */
/*  These rules will be run after the preparser phase of two phase parsing has finished (only gets to execute if preparsing command line option is chosen). */
| prev_declared_ref_type_name ':' ref_spec_init
	{$$ = new ref_type_decl_c(new identifier_c(((token_c *)$1)->value, locloc(@1)), $3, locloc(@$));}  // change the derived_datatype_identifier_c into an identifier_c, as it will be taking the place of an identifier!
;






/*********************/
/* B 1.4 - Variables */
/*********************/
/* NOTE: The standard is erroneous in it's definition of 'variable' because:
 *         - The standard considers 'ENO' as a keyword...
 *         - ...=> which means that it may never be parsed as an 'identifier'...
 *         - ...=> and therefore may never be used as the name of a variable inside an expression.
 *         - However, a function/FB must be able to assign the ENO parameter
 *           it's value, doing it in an assignment statement, and therefore using the 'ENO'
 *           character sequence as an identifier!
 *        The obvious solution is to also allow the ENO keyword to be 
 *         used as the name of a variable. Note that this variable may be used
 *         even though it is not explicitly declared as a function/FB variable,
 *         as the standard requires us to define it implicitly in this case!
 *        There are three ways of achieving this:
 *          (i) simply not define EN and ENO as keywords in flex (lexical analyser)
 *              and let them be considered 'identifiers'. Aditionally, add some code
 *              so that if they are not explicitly declared, we add them automatically to
 *              the declaration of each Functions and FB, where they would then be parsed
 *              as a previously_declared_variable.
 *              This approach has the advantage the EN and ENO would automatically be valid
 *              in every location where it needs to be valid, namely in the explicit declaration 
 *              of these same variables, or when they are used within expressions.
 *              However, this approach has the drawback that 
 *              EN and ENO could then also be used anywhere a standard identifier is allowed,
 *              including in the naming of Functions, FBs, Programs, Configurations, Resources, 
 *              SFC Actions, SFC Steps, etc...
 *              This would mean that we would then have to add a lexical analysis check
 *              within the bison code (syntax analyser) to all the above constructs to make sure
 *              that the identifier being used is not EN or ENO.
 *         (ii) The other approach is to define EN and ENO as keywords / tokens in flex
 *              (lexical analyser) and then change the syntax in bison to acomodate 
 *              these tokens wherever they could correctly appear.
 *              This has the drawback that we need to do some changes to the synax defintion.
 *        (iii) Yet a another option is to mix the above two methods.
 *              Define EN and ENO as tokens in flex, but change (only) the syntax for
 *              variable declaration to allow these tokens to also be used in declaring variables.
 *              From this point onwards these tokens are then considered a previously_declared_variable,
 *              since flex will first check for this before even checking for tokens.
 *
 *              I (Mario) cuurretnly (2011) believe the cleanest method of achieving this goal
 *              is to use option (iii)
 *              However, considering that:
 *                - I have already previously implemented option (ii);
 *                - option (iii) requires that flex parse the previously_declared_variable
 *                   before parsing any token. We already support this (remeber that this is 
 *                   used mainly to allow some IL operators as well as PRIORITY, etc. tokens
 *                   to be used as identifiers, since the standard does not define them as keywords),
 *                   but this part of the code in flex is often commented out as usually people do not expect
 *                   us to follow the standard in the strict sense, but rather consider those
 *                   tokens as keywords;
 *                considering the above, we currently carry on using option (ii).
 */
variable:
  symbolic_variable
| prev_declared_direct_variable
| eno_identifier
	{$$ = new symbolic_variable_c($1, locloc(@$)); $$->token = $1->token;}
;


symbolic_variable:
/* NOTE: To be entirely correct, variable_name must be replacemed by
 *         prev_declared_variable_name | prev_declared_fb_name | prev_declared_global_var_name
 */
  prev_declared_fb_name
	{$$ = new symbolic_variable_c($1, locloc(@$)); $$->token = $1->token;}
| prev_declared_global_var_name
	{$$ = new symbolic_variable_c($1, locloc(@$)); $$->token = $1->token;}
| prev_declared_variable_name
	{$$ = new symbolic_variable_c($1, locloc(@$)); $$->token = $1->token;}
| multi_element_variable
/*
| identifier
	{$$ = new symbolic_variable_c($1, locloc(@$)); $$->token = $1->token;}
*/
| symbolic_variable '^'     
	/* Dereferencing operator defined in IEC 61131-3 v3. However, implemented here differently then how it is defined in the standard! See following note for explanation! */
	{$$ = new deref_operator_c($1, locloc(@$));
	 if (!allow_ref_dereferencing) {
	   print_err_msg(locf(@$), locl(@$), "Derefencing REF_TO datatypes with '^' is not allowed (use -r option to activate support for this IEC 61131-3 v3 feature)."); 
	   yynerrs++;
	 }
}
;
/*
 * NOTE: The syntax defined in the v3 standard for the dereferencing operator '^' seems to me to be un-intentionally
 *       limited. For example
 *         ref_to_bool_var := REF(        array_of_bool [1] );   <---     Allowed!
 *         ref_to_bool_var := REF( ref_to_array_of_bool^[1] );   <---     Allowed!
 *         bool_var        := array_of_ref_to_bool[1]^;          <--- NOT Allowed!
 *         ref_to_array_of_bool^[1] := FALSE;                    <---     Allowed!
 *       I consider this a bug in the v3 standard!!
 *       I have therefore opted to implement this by simply adding a rule to symbolic_variable 
 *         symbolic_variable: 
 *                ...
 *           | symbolic_variable '^'
 *       This simple rule should be able to cover all the needed dereferencing syntax!
 *       I have also added a dereferencing expression for the DREF() operator.
 *       Since both of them do the exact same operation, they will both be translated to the exact same
 *       entry type in the abstract syntax tree (an deref_expression_c)
 */


/* NOTE: in section B 1.7, when configuring a program, symbolic_variable
 *       is used. Nevertheless, during the parsing of a configuration,
 *       the variables in question are out of scope, so we should
 *       be allowing any_identifier instead of prev_declared_variable_name!
 *
 *       We therefore need a new any_symbolic_variable construct that
 *       allows the use of any_identifier instead of previously declared
 *       variables, function blocks, etc...
 */
any_symbolic_variable:
// variable_name -> replaced by any_identifier
  any_identifier
	{$$ = new symbolic_variable_c($1, locloc(@$)); $$->token = $1->token;}
| any_multi_element_variable
;


/* for yet undeclared variable names ! */
variable_name: identifier;





/********************************************/
/* B.1.4.1   Directly Represented Variables */
/********************************************/
prev_declared_direct_variable: prev_declared_direct_variable_token	{$$ = new direct_variable_c($1, locloc(@$));};




/*************************************/
/* B.1.4.2   Multi-element Variables */
/*************************************/
multi_element_variable:
  array_variable
| structured_variable
;

/* please see note above any_symbolic_variable */
any_multi_element_variable:
  any_array_variable
| any_structured_variable
;


array_variable:
  subscripted_variable '[' subscript_list ']'
	{$$ = new array_variable_c($1, $3, locloc(@$));}
;

/* please see note above any_symbolic_variable */
any_array_variable:
  any_subscripted_variable '[' subscript_list ']'
	{$$ = new array_variable_c($1, $3, locloc(@$));}
;


subscripted_variable:
  symbolic_variable
;


/* please see note above any_symbolic_variable */
any_subscripted_variable:
  any_symbolic_variable
;


subscript_list:
  subscript
	{$$ = new subscript_list_c(locloc(@$)); $$->add_element($1);}
| subscript_list ',' subscript
	{$$ = $1; $$->add_element($3);}
;


subscript:  expression;


structured_variable:
  record_variable '.' field_selector
	{$$ = new structured_variable_c($1, $3, locloc(@$));}
| record_variable '.' il_simple_operator_clash3
    {$$ = new structured_variable_c($1, il_operator_c_2_identifier_c($3), locloc(@$));}
;


/* please see note above any_symbolic_variable */
any_structured_variable:
  any_record_variable '.' field_selector
	{$$ = new structured_variable_c($1, $3, locloc(@$));}
| any_record_variable '.' il_simple_operator_clash3
	{$$ = new structured_variable_c($1, $3, locloc(@$));}
;



record_variable:
  symbolic_variable
;


/* please see note above any_symbolic_variable */
any_record_variable:
  any_symbolic_variable
;


field_selector: 
  any_identifier
| eno_identifier
;






/******************************************/
/* B 1.4.3 - Declaration & Initialisation */
/******************************************/
input_declarations:
  VAR_INPUT            input_declaration_list END_VAR
	{$$ = new input_declarations_c(NULL, $2, new explicit_definition_c(), locloc(@$));}
| VAR_INPUT RETAIN     input_declaration_list END_VAR
	{$$ = new input_declarations_c(new retain_option_c(locloc(@2)), $3, new explicit_definition_c(), locloc(@$));}
| VAR_INPUT NON_RETAIN input_declaration_list END_VAR
	{$$ = new input_declarations_c(new non_retain_option_c(locloc(@2)), $3, new explicit_definition_c(), locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR_INPUT END_VAR
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no variable declared in input variable(s) declaration."); yynerrs++;}
| VAR_INPUT RETAIN END_VAR
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable declared in retentive input variable(s) declaration."); yynerrs++;}
| VAR_INPUT NON_RETAIN END_VAR
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable declared in non-retentive input variable(s) declaration."); yynerrs++;}
| VAR_INPUT error input_declaration_list END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'VAR_INPUT' in input variable(s) declaration."); yyerrok;}
| VAR_INPUT RETAIN error input_declaration_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'RETAIN' in retentive input variable(s) declaration."); yyerrok;}
| VAR_INPUT NON_RETAIN error input_declaration_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'NON_RETAIN' in non-retentive input variable(s) declaration."); yyerrok;}
| VAR_INPUT input_declaration_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed input variable(s) declaration."); yyerrok;}
| VAR_INPUT RETAIN input_declaration_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed retentive input variable(s) declaration."); yyerrok;}
| VAR_INPUT NON_RETAIN input_declaration_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed non-retentive input variable(s) declaration."); yyerrok;}
| VAR_INPUT error END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in input variable(s) declaration."); yyerrok;}
| VAR_INPUT RETAIN error END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unknown error in retentive input variable(s) declaration."); yyerrok;}
| VAR_INPUT NON_RETAIN error END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unknown error in non-retentive input variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

/* helper symbol for input_declarations */
input_declaration_list:
  input_declaration ';'
	{$$ = new input_declaration_list_c(locloc(@$)); $$->add_element($1);}
| input_declaration_list input_declaration ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| error ';'
	{$$ = new input_declaration_list_c(locloc(@$)); print_err_msg(locf(@1), locl(@1), "invalid input variable(s) declaration."); yyerrok;}
| input_declaration error
	{$$ = new input_declaration_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at end of input variable(s) declaration."); yyerrok;}
| input_declaration_list input_declaration error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of input variable(s) declaration."); yyerrok;}
| input_declaration_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid input variable(s) declaration."); yyerrok;}
| input_declaration_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after input variable(s) declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


/* NOTE: The formal definition of 'input_declaration' as defined in the standard is erroneous,
 *       as it does not allow a user defined 'EN' input parameter. However,
 *       The semantic description of the languages clearly states that this is allowed.
 *       We have added the 'en_param_declaration' clause to cover for this.
 */
input_declaration:
  var_init_decl
| edge_declaration
| en_param_declaration
;


edge_declaration:
  var1_list ':' BOOL R_EDGE
	{$$ = new edge_declaration_c(new raising_edge_option_c(locloc(@3)), $1, locloc(@$));}
| var1_list ':' BOOL F_EDGE
	{$$ = new edge_declaration_c(new falling_edge_option_c(locloc(@3)), $1, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| var1_list BOOL R_EDGE
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and specification in edge declaration."); yynerrs++;}
| var1_list BOOL F_EDGE
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and specification in edge declaration."); yynerrs++;}
| var1_list ':' BOOL R_EDGE F_EDGE
	{$$ = NULL; print_err_msg(locl(@5), locf(@5), "'R_EDGE' and 'F_EDGE' can't be present at the same time in edge declaration."); yynerrs++;}
| var1_list ':' BOOL F_EDGE R_EDGE
	{$$ = NULL; print_err_msg(locl(@5), locf(@5), "'R_EDGE' and 'F_EDGE' can't be present at the same time in edge declaration."); yynerrs++;}
| var1_list ':' R_EDGE
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "'BOOL' missing in edge declaration."); yynerrs++;}
| var1_list ':' F_EDGE
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "'BOOL' missing in edge declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


/* NOTE: The formal definition of the standard is erroneous, as it simply does not
 *       consider the EN and ENO keywords!
 *       The semantic description of the languages clearly states that these may be
 *       used in several ways. One of them is to declare an EN input parameter.
 *       We have added the 'en_param_declaration' clause to cover for this.
 *
 *       Please read the comment above the definition of 'variable' in section B1.4 for details.
 */
en_param_declaration:
  en_identifier ':' BOOL ASSIGN boolean_literal
  {$$ = new en_param_declaration_c($1, new simple_spec_init_c(new bool_type_name_c(locloc(@3)), $5, locf(@3), locl(@5)), new explicit_definition_c(), locloc(@$));}
| en_identifier ':' BOOL ASSIGN integer
  {$$ = new en_param_declaration_c($1, new simple_spec_init_c(new bool_type_name_c(locloc(@3)), $5, locf(@3), locl(@5)), new explicit_definition_c(), locloc(@$));}
/* ERROR_CHECK_BEGIN */
| en_identifier BOOL ASSIGN boolean_literal
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and specification in EN declaration."); yynerrs++;}
| en_identifier BOOL ASSIGN integer
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and specification in EN declaration."); yynerrs++;}
| en_identifier ':' ASSIGN boolean_literal
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "'BOOL' missing in EN declaration."); yynerrs++;}
| en_identifier ':' ASSIGN integer
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "'BOOL' missing in EN declaration."); yynerrs++;}
| en_identifier ':' BOOL ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no specification defined in EN declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid specification in EN declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

var_init_decl:
  var1_init_decl
| array_var_init_decl
| structured_var_init_decl
| fb_name_decl
| string_var_declaration
;




var1_init_decl:
  var1_list ':' simple_spec_init
	{$$ = new var1_init_decl_c($1, $3, locloc(@$));}
| var1_list ':' subrange_spec_init
	{$$ = new var1_init_decl_c($1, $3, locloc(@$));}
| var1_list ':' enumerated_spec_init
	{$$ = new var1_init_decl_c($1, $3, locloc(@$));}
| var1_list ':' ref_spec_init   /* defined in IEC 61131-3 v3   (REF_TO ...)*/
	{$$ = new var1_init_decl_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| var1_list simple_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and simple specification."); yynerrs++;}
| var1_list subrange_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and subrange specification."); yynerrs++;}
| var1_list enumerated_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and enumerated specification."); yynerrs++;}
| var1_list ':' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no specification defined in variable declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid specification in variable declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


/* NOTE: 
 * The syntax 
 *    variable_name DOTDOT 
 * 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.
 */
var1_list:
  variable_name
	{$$ = new var1_list_c(locloc(@$)); $$->add_element($1);
	 variable_name_symtable.insert($1, prev_declared_variable_name_token);
	}
| variable_name integer DOTDOT
	{$$ = new var1_list_c(locloc(@$)); $$->add_element(new extensible_input_parameter_c($1, $2, locloc(@$)));
	 variable_name_symtable.insert($1, prev_declared_variable_name_token);
	 if (!allow_extensible_function_parameters) print_err_msg(locf(@1), locl(@2), "invalid syntax in variable name declaration.");
	}
 | var1_list ',' variable_name
	{$$ = $1; $$->add_element($3);
	 variable_name_symtable.insert($3, prev_declared_variable_name_token);
	}
 | var1_list ',' variable_name integer DOTDOT
	{$$ = $1; $$->add_element(new extensible_input_parameter_c($3, $4, locloc(@$)));
	 variable_name_symtable.insert($3, prev_declared_variable_name_token);
	 if (!allow_extensible_function_parameters) print_err_msg(locf(@1), locl(@2), "invalid syntax in variable name declaration.");
	}
/* ERROR_CHECK_BEGIN */
| var1_list variable_name
	{$$ = $1; print_err_msg(locl(@1), locf(@2), "',' missing in variable list."); yynerrs++;}
| var1_list ',' error
	{$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no variable name defined in variable declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid variable name in variable declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;



array_var_init_decl:
 var1_list ':' array_spec_init
	{$$ = new array_var_init_decl_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| var1_list array_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and array specification."); yynerrs++;}
/* ERROR_CHECK_END */
;


structured_var_init_decl:
  var1_list ':' initialized_structure
	{$$ = new structured_var_init_decl_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| var1_list initialized_structure
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and structured specification."); yynerrs++;}
/* ERROR_CHECK_END */
;


/* NOTE: see notes above fb_name_list and var1_list
 *       for reason why ':' was removed from this rule!
 *       In essence, to remove a shift/reduce conflict,
 *       the ':' was moved to var1_list and fb_name_list!
 */
fb_name_decl:
/*  fb_name_list ':' function_block_type_name */
  fb_name_list_with_colon function_block_type_name
	{$$ = new fb_name_decl_c($1, new fb_spec_init_c($2, NULL,locloc(@2)), locloc(@$));}
/*| fb_name_list ':' function_block_type_name ASSIGN structure_initialization */
| fb_name_list_with_colon function_block_type_name ASSIGN structure_initialization
	{$$ = new fb_name_decl_c($1, new fb_spec_init_c($2, $4, locf(@2), locl(@4)), locloc(@$));}
/* ERROR_CHECK_BEGIN */
| fb_name_list_with_colon ASSIGN structure_initialization
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no function block type name defined in function block declaration with initialization."); yynerrs++;}
| fb_name_list_with_colon function_block_type_name structure_initialization
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "':=' missing in function block declaration with initialization."); yynerrs++;}
| fb_name_list_with_colon function_block_type_name ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@3), locf(@4), "no initialization defined in function block declaration.");}
	 else {print_err_msg(locf(@4), locl(@4), "invalid initialization in function block declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;



/* NOTE: In order to remove a reduce/reduce conflict between
 *       var1_list and fb_name_list, which are identical to each
 *       other, fb_name_list has been redefined to be a var1_list.
 *
 *        In order to remove a further shift/reduce conflict, var1_list
 *        is imediately transfomred into var1_list_with_colon
 *        (i.e. it includes the ':' following the list), which
 *        means that fb_name_list is built from a
 *        var1_list_with_colon after all!
 */
/*
fb_name_list:
 (*  fb_name *)
  identifier
	{$$ = new fb_name_list_c($1);
	 variable_name_symtable.insert($1, prev_declared_fb_name_token);
	}
(* | fb_name_list ',' fb_name *)
| fb_name_list ',' identifier
	{$$ = $1; $$->add_element($3);
	 variable_name_symtable.insert($3, prev_declared_fb_name_token);
	}
;
*/

fb_name_list_with_colon:
  var1_list_with_colon
	{$$ = new fb_name_list_c(locloc(@$));
	 /* fill up the new fb_name_list_c object with the references
	  * contained in the var1_list_c object.
	  */
	 FOR_EACH_ELEMENT(elem, $1, {$$->add_element(elem);});
	 delete $1;
	 /* change the tokens associated with the symbols stored in
	  * the variable name symbol table from prev_declared_variable_name_token
	  * to prev_declared_fb_name_token
	  */
	 FOR_EACH_ELEMENT(elem, $$, {variable_name_symtable.set(elem, prev_declared_fb_name_token);});
	}
;

/* helper symbol for fb_name_list_with_colon */
var1_list_with_colon:
  var1_list ':'
;


// fb_name: identifier;



output_declarations:
  VAR_OUTPUT var_output_init_decl_list END_VAR
	{$$ = new output_declarations_c(NULL, $2, new explicit_definition_c(), locloc(@$));}
| VAR_OUTPUT RETAIN var_output_init_decl_list END_VAR
	{$$ = new output_declarations_c(new retain_option_c(locloc(@2)), $3, new explicit_definition_c(), locloc(@$));}
| VAR_OUTPUT NON_RETAIN var_output_init_decl_list END_VAR
	{$$ = new output_declarations_c(new non_retain_option_c(locloc(@2)), $3, new explicit_definition_c(), locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR_OUTPUT END_VAR
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no variable declared in output variable(s) declaration."); yynerrs++;}
| VAR_OUTPUT RETAIN END_VAR
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable declared in retentive output variable(s) declaration."); yynerrs++;}
| VAR_OUTPUT NON_RETAIN END_VAR
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable declared in non-retentive output variable(s) declaration."); yynerrs++;}
| VAR_OUTPUT error var_output_init_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'VAR_OUPUT' in output variable(s) declaration."); yyerrok;}
| VAR_OUTPUT RETAIN error var_output_init_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'RETAIN' in retentive output variable(s) declaration."); yyerrok;}
| VAR_OUTPUT NON_RETAIN error var_output_init_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'NON_RETAIN' in non-retentive output variable(s) declaration."); yyerrok;}
| VAR_OUTPUT var_output_init_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed output variable(s) declaration."); yyerrok;}
| VAR_OUTPUT RETAIN var_output_init_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed retentive output variable(s) declaration."); yyerrok;}
| VAR_OUTPUT NON_RETAIN var_output_init_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed non-retentive output variable(s) declaration."); yyerrok;}
| VAR_OUTPUT error END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in output variable(s) declaration."); yyerrok;}
| VAR_OUTPUT RETAIN error END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unknown error in retentive output variable(s) declaration."); yyerrok;}
| VAR_OUTPUT NON_RETAIN error END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unknown error in non-retentive output variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;


/* NOTE: The formal definition of 'var_output_init_decl' as defined in the standard is erroneous,
 *       as it does not allow a user defined 'ENO' output parameter. However,
 *       The semantic description of the languages clearly states that this is allowed.
 *       We have added the 'eno_param_declaration' clause to cover for this.
 *
 *       Please read the comment above the definition of 'variable' in section B1.4 for details.
 */
var_output_init_decl:
  var_init_decl
| eno_param_declaration
;

var_output_init_decl_list:
  var_output_init_decl ';'
	{$$ = new var_init_decl_list_c(locloc(@$)); $$->add_element($1);}
| var_output_init_decl_list var_output_init_decl ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| var_output_init_decl_list var_output_init_decl error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of variable(s) declaration."); yyerrok;}
| var_output_init_decl_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;


/* NOTE: The formal definition of the standard is erroneous, as it simply does not
 *       consider the EN and ENO keywords!
 *       The semantic description of the languages clearly states that these may be
 *       used in several ways. One of them is to declare an ENO output parameter.
 *       We have added the 'eno_param_declaration' clause to cover for this.
 *
 *       Please read the comment above the definition of 'variable' in section B1.4 for details.
 */
eno_param_declaration:
  eno_identifier ':' BOOL
  /* NOTE We do _NOT_ include this variable in the previously_declared_variable symbol table!
   *      Please read the comment above the definition of 'variable' for the reason for this.
   */
  {$$ = new eno_param_declaration_c($1, new bool_type_name_c(locloc(@$)), new explicit_definition_c(), locloc(@$));}
/* ERROR_CHECK_BEGIN */
| eno_identifier BOOL
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and specification in ENO declaration."); yynerrs++;}
| eno_identifier ':' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no specification defined in ENO declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid specification in ENO declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


input_output_declarations:
  VAR_IN_OUT var_declaration_list END_VAR
	{$$ = new input_output_declarations_c($2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR_IN_OUT END_VAR
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no variable declared in in_out variable(s) declaration."); yynerrs++;}
| VAR_IN_OUT error var_declaration_list END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'VAR_IN_OUT' in in_out variable(s) declaration."); yyerrok;}
| VAR_IN_OUT var_declaration_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed in_out variable(s) declaration."); yyerrok;}
| VAR_IN_OUT error END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in in_out variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;



/* helper symbol for input_output_declarations */
var_declaration_list:
  var_declaration ';'
	{$$ = new var_declaration_list_c(locloc(@$)); $$->add_element($1);}
| var_declaration_list var_declaration ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| error ';'
	{$$ = new var_declaration_list_c(locloc(@$)); print_err_msg(locf(@1), locl(@1), "invalid variable(s) declaration."); yyerrok;}
| var_declaration error
	{$$ = new var_declaration_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at end of variable(s) declaration."); yyerrok;}
| var_declaration_list var_declaration error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of variable(s) declaration."); yyerrok;}
| var_declaration_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid variable(s) declaration."); yyerrok;}
| var_declaration_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after variable(s) declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


var_declaration:
  temp_var_decl
| fb_name_decl
;


temp_var_decl:
  var1_declaration
| array_var_declaration
| structured_var_declaration
| string_var_declaration
;

var1_declaration:
  var1_list ':' simple_specification
	{$$ = new var1_init_decl_c($1, $3, locloc(@$));}
| var1_list ':' subrange_specification
	{$$ = new var1_init_decl_c($1, $3, locloc(@$));}
| var1_list ':' enumerated_specification
	{$$ = new var1_init_decl_c($1, $3, locloc(@$));}
| var1_list ':' ref_spec   /* defined in IEC 61131-3 v3   (REF_TO ...)*/
	{$$ = new var1_init_decl_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| var1_list simple_specification
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and simple specification."); yynerrs++;}
| var1_list subrange_specification
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and subrange specification."); yynerrs++;}
| var1_list enumerated_specification
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and enumerated specification."); yynerrs++;}
/* ERROR_CHECK_END */
;



array_var_declaration:
  var1_list ':' array_specification
	{$$ = new array_var_declaration_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| var1_list array_specification
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and array specification."); yynerrs++;}
/* ERROR_CHECK_END */
;

structured_var_declaration:
  var1_list ':' prev_declared_structure_type_name
	{$$ = new structured_var_declaration_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| var1_list prev_declared_structure_type_name
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and structured specification."); yynerrs++;}
/* ERROR_CHECK_END */
;


var_declarations:
  VAR var_init_decl_list END_VAR
	{$$ = new var_declarations_c(NULL, $2, locloc(@$));}
| VAR CONSTANT var_init_decl_list END_VAR
	{$$ = new var_declarations_c(new constant_option_c(locloc(@2)), $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR END_VAR
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no variable declared in variable(s) declaration."); yynerrs++;}
| VAR CONSTANT END_VAR
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable declared in constant variable(s) declaration."); yynerrs++;}
| VAR error var_init_decl_list END_VAR
	{$$ = NULL; print_err_msg(locl(@1), locf(@3), "unexpected token after 'VAR' in variable(s) declaration."); yyerrok;}
| VAR CONSTANT error var_init_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'CONSTANT' in constant variable(s) declaration."); yyerrok;}
| VAR var_init_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed variable(s) declaration."); yyerrok;}
| VAR CONSTANT var_init_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed constant variable(s) declaration."); yyerrok;}
| VAR error END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in variable(s) declaration."); yyerrok;}
| VAR CONSTANT error END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unknown error in constant variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;


retentive_var_declarations:
  VAR RETAIN var_init_decl_list END_VAR
	{$$ = new retentive_var_declarations_c($3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR RETAIN END_VAR
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable declared in retentive variable(s) declaration."); yynerrs++;}
| VAR RETAIN error var_init_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'RETAIN' in retentive variable(s) declaration."); yyerrok;}
| VAR RETAIN var_init_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed retentive variable(s) declaration."); yyerrok;}
| VAR RETAIN error END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unknown error in retentive variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;


located_var_declarations:
  VAR located_var_decl_list END_VAR
	{$$ = new located_var_declarations_c(NULL, $2, locloc(@$));}
| VAR CONSTANT located_var_decl_list END_VAR
	{$$ = new located_var_declarations_c(new constant_option_c(locloc(@2)), $3, locloc(@$));}
| VAR RETAIN located_var_decl_list END_VAR
	{$$ = new located_var_declarations_c(new retain_option_c(locloc(@2)), $3, locloc(@$));}
| VAR NON_RETAIN located_var_decl_list END_VAR
	{$$ = new located_var_declarations_c(new non_retain_option_c(locloc(@2)), $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR NON_RETAIN END_VAR
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable declared in non-retentive located variable(s) declaration."); yynerrs++;}
| VAR error located_var_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'VAR' in located variable(s) declaration."); yyerrok;}
| VAR CONSTANT error located_var_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'CONSTANT' in constant located variable(s) declaration."); yyerrok;}
| VAR RETAIN error located_var_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'RETAIN' in retentive located variable(s) declaration."); yyerrok;}
| VAR NON_RETAIN error located_var_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'NON_RETAIN' in non-retentive located variable(s) declaration."); yyerrok;}
| VAR located_var_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed located variable(s) declaration."); yyerrok;}
| VAR CONSTANT located_var_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed constant located variable(s) declaration."); yyerrok;}
| VAR RETAIN located_var_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed retentive located variable(s) declaration."); yyerrok;}
| VAR NON_RETAIN located_var_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed non-retentive located variable(s) declaration."); yyerrok;}
| VAR NON_RETAIN error END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unknown error in non retentive variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;


/* helper symbol for located_var_declarations */
located_var_decl_list:
  located_var_decl ';'
	{$$ = new located_var_decl_list_c(locloc(@$)); $$->add_element($1);}
| located_var_decl_list located_var_decl ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| error ';'
	{$$ = new located_var_decl_list_c(locloc(@$)); print_err_msg(locf(@1), locl(@1), "invalid located variable declaration."); yyerrok;}
| located_var_decl error
	{$$ = new located_var_decl_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at end of located variable declaration."); yyerrok;}
| located_var_decl_list located_var_decl error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of located variable declaration."); yyerrok;}
| located_var_decl_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid located variable declaration."); yyerrok;}
| located_var_decl_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after located variable declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


located_var_decl:
  variable_name location ':' located_var_spec_init
	{$$ = new located_var_decl_c($1, $2, $4, locloc(@$));
	 variable_name_symtable.insert($1, prev_declared_variable_name_token);
	}
| location ':' located_var_spec_init
	{$$ = new located_var_decl_c(NULL, $1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| variable_name location located_var_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between located variable location and specification."); yynerrs++;}
| location located_var_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between located variable location and specification."); yynerrs++;}
| variable_name location ':' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no specification defined in located variable declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid specification in located variable declaration."); yyclearin;}
	 yyerrok;
	}
| location ':' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no specification defined in located variable declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid specification in located variable declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;




external_var_declarations:
  VAR_EXTERNAL external_declaration_list END_VAR
	{$$ = new external_var_declarations_c(NULL, $2, locloc(@$));}
| VAR_EXTERNAL CONSTANT external_declaration_list END_VAR
	{$$ = new external_var_declarations_c(new constant_option_c(locloc(@2)), $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR_EXTERNAL END_VAR
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no variable declared in external variable(s) declaration."); yynerrs++;}
| VAR_EXTERNAL CONSTANT END_VAR
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable declared in constant external variable(s) declaration."); yynerrs++;}
| VAR_EXTERNAL error external_declaration_list END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'VAR_EXTERNAL' in external variable(s) declaration."); yyerrok;}
| VAR_EXTERNAL CONSTANT error external_declaration_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'CONSTANT' in constant external variable(s) declaration."); yyerrok;}
| VAR_EXTERNAL external_declaration_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed external variable(s) declaration."); yyerrok;}
| VAR_EXTERNAL CONSTANT external_declaration_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed constant external variable(s) declaration."); yyerrok;}
| VAR_EXTERNAL error END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in external variable(s) declaration."); yyerrok;}
| VAR_EXTERNAL CONSTANT error END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unknown error in constant external variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

/* helper symbol for external_var_declarations */
external_declaration_list:
  external_declaration ';'
	{$$ = new external_declaration_list_c(locloc(@$)); $$->add_element($1);}
| external_declaration_list external_declaration ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| error ';'
	{$$ = new external_declaration_list_c(locloc(@$)); print_err_msg(locf(@1), locl(@1), "invalid external variable declaration."); yyerrok;}
| external_declaration error
	{$$ = new external_declaration_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at end of external variable declaration."); yyerrok;}
| external_declaration_list external_declaration error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of external variable declaration."); yyerrok;}
| external_declaration_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid external variable declaration."); yyerrok;}
| external_declaration_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after external variable declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;



/* Warning: When handling VAR_EXTERNAL declarations, the constant folding algorithm may (depending on the command line parameters) 
 *          set the symbol_c->const_value annotations on both the external_var_name as well as on its VAR_EXTERNAL datatype specification symbol.
 *          Setting the const_value on the datatype specification symbol of a VAR_EXTERNAL declaration is only possible if the declaration of 
 *          several external variables in a list is not allowed (as each variable could have a potentially distinct initial value).
 *           VAR_EXTERNAL
 *             a, b, c, d: INT;  (* incorrect syntax! *)
 *           END_VAR
 *          
 *          If anybody considers extending this standard syntax to allow the above syntax (several variables in a list), then be sure to go
 *          and fix the constant folding algorithm (more precisely, the constant_folding_c::handle_var_extern_global_pair() function.
 */
external_declaration:
  global_var_name ':' simple_specification
	{$$ = new external_declaration_c($1, $3, locloc(@$));
	 variable_name_symtable.insert($1, prev_declared_variable_name_token);
	}
| global_var_name ':' subrange_specification
	{$$ = new external_declaration_c($1, $3, locloc(@$));
	 variable_name_symtable.insert($1, prev_declared_variable_name_token);
	}
| global_var_name ':' enumerated_specification
	{$$ = new external_declaration_c($1, $3, locloc(@$));
	 variable_name_symtable.insert($1, prev_declared_variable_name_token);
	}
| global_var_name ':' array_specification
	{$$ = new external_declaration_c($1, $3, locloc(@$));
	 variable_name_symtable.insert($1, prev_declared_variable_name_token);
	}
| global_var_name ':' prev_declared_structure_type_name
	{$$ = new external_declaration_c($1, $3, locloc(@$));
	 variable_name_symtable.insert($1, prev_declared_variable_name_token);
	}
| global_var_name ':' function_block_type_name
	{$$ = new external_declaration_c($1, new fb_spec_init_c($3, NULL, locloc(@3)), locloc(@$));
	 variable_name_symtable.insert($1, prev_declared_fb_name_token);
	}
| global_var_name ':' ref_spec /* defined in IEC 61131-3 v3   (REF_TO ...)*/
	{$$ = new external_declaration_c($1, $3, locloc(@$));
	 variable_name_symtable.insert($1, prev_declared_fb_name_token);
	}
/* ERROR_CHECK_BEGIN */
| global_var_name simple_specification
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between external variable name and simple specification."); yynerrs++;}
| global_var_name subrange_specification
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between external variable name and subrange specification."); yynerrs++;}
| global_var_name enumerated_specification
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between external variable name and enumerated specification."); yynerrs++;}
| global_var_name array_specification
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between external variable name and array specification."); yynerrs++;}
| global_var_name prev_declared_structure_type_name
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between external variable name and structured specification."); yynerrs++;}
| global_var_name function_block_type_name
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between external variable name and function block type specification."); yynerrs++;}
| global_var_name ':' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no specification defined in external variable declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid specification in external variable declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


global_var_name: identifier;


global_var_declarations:
  VAR_GLOBAL global_var_decl_list END_VAR
	{$$ = new global_var_declarations_c(NULL, $2, locloc(@$));}
| VAR_GLOBAL CONSTANT global_var_decl_list END_VAR
	{$$ = new global_var_declarations_c(new constant_option_c(locloc(@2)), $3, locloc(@$));}
| VAR_GLOBAL RETAIN global_var_decl_list END_VAR
	{$$ = new global_var_declarations_c(new retain_option_c(locloc(@2)), $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR_GLOBAL END_VAR
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no variable declared in global variable(s) declaration."); yynerrs++;}
| VAR_GLOBAL CONSTANT END_VAR
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable declared in constant global variable(s) declaration."); yynerrs++;}
| VAR_GLOBAL RETAIN END_VAR
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable declared in retentive global variable(s) declaration."); yynerrs++;}
| VAR_GLOBAL error global_var_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'VAR_GLOBAL' in global variable(s) declaration."); yyerrok;}
| VAR_GLOBAL CONSTANT error global_var_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'CONSTANT' in constant global variable(s) declaration."); yyerrok;}
| VAR_GLOBAL RETAIN error global_var_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'RETAIN' in retentive global variable(s) declaration."); yyerrok;}
| VAR_GLOBAL global_var_decl_list error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed global variable(s) declaration."); yyerrok;}
| VAR_GLOBAL CONSTANT global_var_decl_list error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed constant global variable(s) declaration."); yyerrok;}
| VAR_GLOBAL RETAIN global_var_decl_list error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed retentive global variable(s) declaration."); yyerrok;}
| VAR_GLOBAL error END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in global variable(s) declaration."); yyerrok;}
| VAR_GLOBAL CONSTANT error END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unknown error in constant global variable(s) declaration."); yyerrok;}
| VAR_GLOBAL RETAIN error END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unknown error in constant global variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;


/* helper symbol for global_var_declarations */
global_var_decl_list:
  global_var_decl ';'
	{$$ = new global_var_decl_list_c(locloc(@$)); $$->add_element($1);}
| global_var_decl_list global_var_decl ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| error ';'
	{$$ = new global_var_decl_list_c(locloc(@$)); print_err_msg(locf(@1), locl(@1), "invalid global variable(s) declaration."); yyerrok;}
| global_var_decl error
	{$$ = new global_var_decl_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at end of global variable(s) declaration."); yyerrok;}
| global_var_decl_list global_var_decl error
	{$$ = $1; print_err_msg(locl(@1), locf(@2), "';' missing at end of global variable(s) declaration."); yyerrok;}
| global_var_decl_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid global variable(s) declaration."); yyerrok;}
| global_var_decl_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after global variable(s) declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


global_var_decl:
/* NOTE : This possibility defined in standard has no sense and generate a conflict (disabled)
  global_var_spec ':'
	{$$ = new global_var_decl_c($1, NULL, locloc(@$));}
*/
  global_var_spec ':' located_var_spec_init
	{$$ = new global_var_decl_c($1, $3, locloc(@$));}
| global_var_spec ':' function_block_type_name
	{$$ = new global_var_decl_c($1, new fb_spec_init_c($3, NULL, locloc(@3)), locloc(@$));}
/* ERROR_CHECK_BEGIN */
| global_var_list located_var_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between global variable list and type specification."); yynerrs++;}
| global_var_name location located_var_spec_init
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between global variable specification and type specification."); yynerrs++;}
| global_var_spec function_block_type_name
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between global variable specification and function block type specification."); yynerrs++;}
| global_var_spec ':' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no specification defined in global variable declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid specification in global variable declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


global_var_spec:
  global_var_list	{$$ = $1;}
| location
	{$$ = new global_var_spec_c(NULL, $1, locloc(@$));}
| global_var_name location
	{$$ = new global_var_spec_c($1, $2, locloc(@$));
	 variable_name_symtable.insert($1, prev_declared_global_var_name_token);
	}
;


located_var_spec_init:
  simple_spec_init
| subrange_spec_init
| enumerated_spec_init
| array_spec_init
| initialized_structure
| single_byte_string_spec
| double_byte_string_spec
| ref_spec_init /* defined in IEC 61131-3 v3 (REF_TO ...) */
;


location:
  AT direct_variable_token
	{$$ = new location_c(new direct_variable_c($2, locloc(@$)), locloc(@$));
	 direct_variable_symtable.insert($2, prev_declared_direct_variable_token);
	}
/* ERROR_CHECK_BEGIN */
| AT error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@1), locf(@2), "no location defined in location declaration.");}
	 else {print_err_msg(locf(@2), locl(@2), "invalid location in global location declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;



global_var_list:
  global_var_name
	{$$ = new global_var_list_c(locloc(@$)); $$->add_element($1);
	 variable_name_symtable.insert($1, prev_declared_global_var_name_token);
	}
| global_var_list ',' global_var_name
	{$$ = $1; $$->add_element($3);
	 variable_name_symtable.insert($3, prev_declared_global_var_name_token);
	}
/* ERROR_CHECK_BEGIN */
| global_var_list global_var_name
	{$$ = new global_var_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "',' missing in global variable list."); yynerrs++;}
| global_var_list ',' error
	{$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no variable name defined in global variable declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid variable name in global variable declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;



string_var_declaration:
  single_byte_string_var_declaration
| double_byte_string_var_declaration
;

single_byte_string_var_declaration:
  var1_list ':' single_byte_string_spec
	{$$ = new single_byte_string_var_declaration_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| var1_list single_byte_string_spec
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and string type specification."); yynerrs++;}
/* ERROR_CHECK_END */
;

/* NOTE: The constructs
 *
 *       [W]STRING
 *       and
 *       [W]STRING ASSIGN single_byte_character_string
 *
 *       were removed as they are already contained
 *       within a other constructs.
 *
 *       single_byte_string_spec is used in:
 *        - single_byte_string_var_declaration ->
 *           -> string_var_declaration ---> var_init_decl
 *                                     |--> temp_var_decl
 *                                     |--> var2_init_decl
 *        - located_var_spec_init
 *
 *       STRING [ASSIGN string_constant] -> elementary_string_type_name ->
 *        -> simple_spec -> simple_specification -> simple_spec_init ->
 *        -> located_var_spec_init
 *
 *       STRING [ASSIGN string_constant] -> elementary_string_type_name ->
 *        -> simple_spec -> simple_specification -> simple_spec_init ->
 *        -> var1_init_decl -> var_init_decl
 *
 *       STRING [ASSIGN string_constant] -> elementary_string_type_name ->
 *        -> simple_spec -> simple_specification -> simple_spec_init ->
 *        -> var1_init_decl -> var2_init_decl
 *
 *       STRING [ASSIGN string_constant] -> elementary_string_type_name ->
 *        -> simple_spec -> simple_specification ->
 *        -> var1_declaration -> temp_var_decl
 */
single_byte_string_spec:
/*  STRING
	{$$ = new single_byte_string_spec_c(NULL, NULL);}
*/
  STRING '[' integer ']'
	{$$ = new single_byte_string_spec_c(new single_byte_limited_len_string_spec_c(new string_type_name_c(locloc(@1)), $3, locloc(@$)), NULL, locloc(@$));}
/*
| STRING ASSIGN single_byte_character_string
	{$$ = new single_byte_string_spec_c($1, NULL, $3, locloc(@$));}
*/
| STRING '[' integer ']' ASSIGN single_byte_character_string
	{$$ = new single_byte_string_spec_c(new single_byte_limited_len_string_spec_c(new string_type_name_c(locloc(@1)), $3, locloc(@$)), $6, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| STRING '[' error ']'
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid length value for limited string type specification."); yyerrok;}
| STRING '[' error ']' ASSIGN single_byte_character_string
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid length value for limited string type specification."); yyerrok;}
| STRING '[' ']'
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "missing length value for limited string type specification."); yynerrs++;}
| STRING '[' ']' ASSIGN single_byte_character_string
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "missing length value for limited string type specification."); yynerrs++;}
| STRING '[' integer error
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "expecting ']' after length definition for limited string type specification."); yyerrok;}
| STRING '[' integer ']' single_byte_character_string
	{$$ = NULL; print_err_msg(locl(@4), locf(@5), "':=' missing before limited string type initialization."); yynerrs++;}
| STRING '[' integer ']' ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@5), locf(@6), "no initial value defined in limited string type initialization.");}
	 else {print_err_msg(locf(@6), locl(@6), "invalid initial value in limited string type initialization."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


double_byte_string_var_declaration:
  var1_list ':' double_byte_string_spec
	{$$ = new double_byte_string_var_declaration_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| var1_list double_byte_string_spec
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between variable list and double byte string type specification."); yynerrs++;}
/* ERROR_CHECK_END */
;

double_byte_string_spec:
/*  WSTRING
	{$$ = new double_byte_string_spec_c($1, NULL, NULL, locloc(@$));}
*/
  WSTRING '[' integer ']'
	{$$ = new double_byte_string_spec_c(new double_byte_limited_len_string_spec_c(new wstring_type_name_c(locloc(@1)), $3, locloc(@$)), NULL, locloc(@$));}

/*
| WSTRING ASSIGN double_byte_character_string
	{$$ = new double_byte_string_spec_c($1, NULL, $3, locloc(@$));}
*/
| WSTRING '[' integer ']' ASSIGN double_byte_character_string
	{$$ = new double_byte_string_spec_c(new double_byte_limited_len_string_spec_c(new wstring_type_name_c(locloc(@1)), $3, locloc(@$)), $6, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| WSTRING '[' error ']'
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid length value for limited double byte string type specification."); yyerrok;}
| WSTRING '[' error ']' ASSIGN single_byte_character_string
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid length value for limited double byte string type specification."); yyerrok;}
| WSTRING '[' ']'
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "missing length value for limited double byte string type specification."); yynerrs++;}
| WSTRING '[' ']' ASSIGN single_byte_character_string
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "missing length value for limited double byte string type specification."); yynerrs++;}
| WSTRING '[' integer error
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "expecting ']' after length definition for limited double byte string type specification."); yyerrok;}
| WSTRING '[' integer ']' single_byte_character_string
	{$$ = NULL; print_err_msg(locl(@4), locf(@5), "':=' missing before limited double byte string type initialization."); yynerrs++;}
| WSTRING '[' integer ']' ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@5), locf(@6), "no initial value defined double byte in limited string type initialization.");}
	 else {print_err_msg(locf(@6), locl(@6), "invalid initial value in limited double byte string type initialization."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;



incompl_located_var_declarations:
  VAR            incompl_located_var_decl_list END_VAR
	{$$ = new incompl_located_var_declarations_c(NULL, $2, locloc(@$));}
| VAR     RETAIN incompl_located_var_decl_list END_VAR
	{$$ = new incompl_located_var_declarations_c(new retain_option_c(locloc(@2)), $3, locloc(@$));}
| VAR NON_RETAIN incompl_located_var_decl_list END_VAR
	{$$ = new incompl_located_var_declarations_c(new non_retain_option_c(locloc(@2)), $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR incompl_located_var_decl_list error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed incomplete located variable(s) declaration."); yyerrok;}
| VAR RETAIN incompl_located_var_decl_list error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed incomplete retentive located variable(s) declaration."); yyerrok;}
| VAR NON_RETAIN incompl_located_var_decl_list error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed incomplete non-retentive located variable(s) declaration."); yyerrok;}
| VAR error incompl_located_var_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'VAR' in incomplete located variable(s) declaration."); yyerrok;}
| VAR RETAIN error incompl_located_var_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'RETAIN' in retentive located variable(s) declaration."); yyerrok;}
| VAR NON_RETAIN error incompl_located_var_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'NON_RETAIN' in non-retentive located variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

/* helper symbol for incompl_located_var_declarations */
incompl_located_var_decl_list:
  incompl_located_var_decl ';'
	{$$ = new incompl_located_var_decl_list_c(locloc(@$)); $$->add_element($1);}
| incompl_located_var_decl_list incompl_located_var_decl ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| incompl_located_var_decl error
	{$$ = new incompl_located_var_decl_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at end of incomplete located variable declaration."); yyerrok;}
| incompl_located_var_decl_list incompl_located_var_decl error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of incomplete located variable declaration."); yyerrok;}
| incompl_located_var_decl_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid incomplete located variable declaration."); yyerrok;}
| incompl_located_var_decl_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after incomplete located variable declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


incompl_located_var_decl:
  variable_name incompl_location ':' var_spec
	{$$ = new incompl_located_var_decl_c($1, $2, $4, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| variable_name incompl_location var_spec
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing between incomplete located variable and type specification."); yynerrs++;
	}
| variable_name incompl_location ':' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no specification defined in incomplete located variable declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid specification in incomplete located variable declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


incompl_location:
  AT incompl_location_token
	{$$ = new incompl_location_c($2, locloc(@$));}
;


var_spec:
  simple_specification
| subrange_specification
| enumerated_specification
| array_specification
| prev_declared_structure_type_name
| string_spec
;


/* helper symbol for var_spec */
string_spec:
/*  STRING
	{$$ = new single_byte_limited_len_string_spec_c($1, NULL, locloc(@$));}
*/
  STRING '[' integer ']'
	{$$ = new single_byte_limited_len_string_spec_c(new string_type_name_c(locloc(@1)), $3, locloc(@$));}
/*
| WSTRING
	{$$ = new double_byte_limited_len_string_spec_c($1, NULL, locloc(@$));}
*/
| WSTRING '[' integer ']'
	{$$ = new double_byte_limited_len_string_spec_c(new wstring_type_name_c(locloc(@1)), $3, locloc(@$));}
;




/* intermediate helper symbol for:
 *  - non_retentive_var_decls
 *  - var_declarations
 */
var_init_decl_list:
  var_init_decl ';'
	{$$ = new var_init_decl_list_c(locloc(@$)); $$->add_element($1);}
| var_init_decl_list var_init_decl ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| var_init_decl_list var_init_decl error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of variable(s) declaration."); yyerrok;}
| var_init_decl_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;




/***********************/
/* B 1.5.1 - Functions */
/***********************/
/*
function_name:
  prev_declared_derived_function_name
| standard_function_name 
;
*/

/* The following rules should be set such as:
 * function_name: function_name_no_clashes | function_name_simpleop_clashes | function_name_expression_clashes
 * function_name: function_name_no_NOT_clashes | function_name_NOT_clashes;
 */

function_name_no_clashes: prev_declared_derived_function_name | standard_function_name_no_clashes;
function_name_simpleop_clashes: standard_function_name_simpleop_clashes;
//function_name_expression_clashes: standard_function_name_expression_clashes;

function_name_no_NOT_clashes: prev_declared_derived_function_name | standard_function_name_no_NOT_clashes;
//function_name_NOT_clashes: standard_function_name_NOT_clashes;


/* NOTE: The list of standard function names
 *       includes the standard functions MOD(), NOT()
 *
 *       Strangely enough, MOD and NOT are reserved keywords,
 *       so shouldn't be used for function names.
 *
 *       The specification contradicts itself!
 *       Our workaround  is to treat MOD as a token,
 *       but to include this token as a
 *       standard_function_name.
 *
 *       The names of all other standard functions get
 *       preloaded into the library_element_symbol_table
 *       with the token value of
 *       standard_function_name_token
 *       Actually, simply for completeness, MOD is also
 *       loaded into the library_element_symbol_table, but
 *       it is irrelevant since flex will catch MOD as a
 *       token, before it interprets it as an identifier,
 *       and looks in the library_element_symbol_table to check
 *       whether it has been previously declared.
 *
 * NOTE: The same as the above also occurs with the IL
 *       operators NOT AND OR XOR ADD SUB MUL DIV MOD
 *       GT GE EQ LT LE NE.
 *       Note that MOD is once again in the list!
 *       Anyway, we give these the same treatement as
 *       MOD, since we are writing a parser for ST and
 *       IL simultaneously. If this were not the case,
 *       the ST parser would not need the tokens NOT AND ...
 *
 * NOTE: Note that 'NOT' is special, as it conflicts
 *       with two operators: the  IL 'NOT' operator, and
 *       the unary operator 'NOT' in ST!!
 *
 * NOTE: The IL language is ambiguous, since using NOT, AND, ...
 *       may be interpreted as either an IL operator, or
 *       as a standard function call!
 *       I (Mario) opted to interpret it as an IL operator.
 *       This requires changing the syntax for IL language
 *       function   calling, to exclude all function with
 *       names that clash with IL operators. I therefore
 *       created the constructs
 *       function_name_without_clashes
 *       standard_function_name_without_clashes
 *       to include all function names, except those that clash
 *       with IL operators. These constructs are only used
 *       within the IL language!
 */
/* The following rules should be set such as:
 * standard_function_name: standard_function_name_no_clashes | standard_function_name_simpleop_clashes | standard_function_name_expression_clashes
 * standard_function_name: standard_function_name_no_NOT_clashes | standard_function_name_NOT_clashes;
 */

/*
standard_function_name:
  standard_function_name_no_clashes
| standard_function_name_expression_clashes
| standard_function_name_NOT_clashes
//| standard_function_name_simpleop_only_clashes
;
*/

standard_function_name_no_NOT_clashes:
  standard_function_name_no_clashes
| standard_function_name_expression_clashes
//| standard_function_name_simpleop_only_clashes
;

/* standard_function_name_no_clashes is only used in function invocations, so we use the poutype_identifier_c class! */
standard_function_name_no_clashes:
  standard_function_name_token
	{$$ = new poutype_identifier_c($1, locloc(@$));}
;


standard_function_name_simpleop_clashes:
  standard_function_name_NOT_clashes
//| standard_function_name_simpleop_only_clashes
;

/* standard_function_name_NOT_clashes is only used in function invocations, so we use the poutype_identifier_c class! */
standard_function_name_NOT_clashes:
  NOT
	{$$ = new poutype_identifier_c(strdup("NOT"), locloc(@$));}
;

/* Add here any other IL simple operators that collide
 * with standard function names!
 * Don't forget to uncomment the equivalent lines in
 *   - standard_function_name_simpleop_clashes
 *   - standard_function_name
 *   - standard_function_name_no_NOT_clashes
 */
/*
standard_function_name_simpleop_only_clashes:
;
*/

/* standard_function_name_expression_clashes is only used in function invocations, so we use the poutype_identifier_c class! */
standard_function_name_expression_clashes:
  AND	{$$ = new poutype_identifier_c(strdup("AND"), locloc(@$));}
| OR	{$$ = new poutype_identifier_c(strdup("OR"), locloc(@$));}
| XOR	{$$ = new poutype_identifier_c(strdup("XOR"), locloc(@$));}
| ADD	{$$ = new poutype_identifier_c(strdup("ADD"), locloc(@$));}
| SUB	{$$ = new poutype_identifier_c(strdup("SUB"), locloc(@$));}
| MUL	{$$ = new poutype_identifier_c(strdup("MUL"), locloc(@$));}
| DIV	{$$ = new poutype_identifier_c(strdup("DIV"), locloc(@$));}
| MOD	{$$ = new poutype_identifier_c(strdup("MOD"), locloc(@$));}
| GT	{$$ = new poutype_identifier_c(strdup("GT"), locloc(@$));}
| GE	{$$ = new poutype_identifier_c(strdup("GE"), locloc(@$));}
| EQ	{$$ = new poutype_identifier_c(strdup("EQ"), locloc(@$));}
| LT	{$$ = new poutype_identifier_c(strdup("LT"), locloc(@$));}
| LE	{$$ = new poutype_identifier_c(strdup("LE"), locloc(@$));}
| NE	{$$ = new poutype_identifier_c(strdup("NE"), locloc(@$));}
/*
  AND_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
//NOTE: AND2 (corresponding to the source code string '&') does not clash
//      with a standard function name, so should be commented out!
//| AND2_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| OR_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| XOR_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| ADD_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| SUB_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| MUL_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| DIV_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| MOD_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| GT_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| GE_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| EQ_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| LT_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| LE_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
| NE_operator	{$$ = il_operator_c_2_poutype_identifier_c($1);}
*/
;


derived_function_name:
  identifier  /* will never occur during normal parsing, only needed for preparsing to change it to a prev_declared_derived_function_name! */
| prev_declared_derived_function_name
	{$$ = new identifier_c(((token_c *)$1)->value, locloc(@$)); // transform the poutype_identifier_c into an identifier_c
	 if (get_preparse_state() && !allow_function_overloading) {print_err_msg(locloc(@$), "Function overloading not allowed. Invalid identifier.\n"); yynerrs++;}
	}
| AND
	{$$ = new identifier_c("AND", locloc(@$));
	 if (!allow_function_overloading) {print_err_msg(locloc(@$), "Function overloading not allowed. Invalid identifier.\n"); yynerrs++;}
	}
| OR
	{$$ = new identifier_c("OR", locloc(@$));
	 if (!allow_function_overloading) {print_err_msg(locloc(@$), "Function overloading not allowed. Invalid identifier.\n"); yynerrs++;}
	}
| XOR
	{$$ = new identifier_c("XOR", locloc(@$));
	 if (!allow_function_overloading) {print_err_msg(locloc(@$), "Function overloading not allowed. Invalid identifier.\n"); yynerrs++;}
	}
| NOT
	{$$ = new identifier_c("NOT", locloc(@$));
	 if (!allow_function_overloading) {print_err_msg(locloc(@$), "Function overloading not allowed. Invalid identifier.\n"); yynerrs++;}
	}
| MOD
	{$$ = new identifier_c("MOD", locloc(@$));
	 if (!allow_function_overloading) {print_err_msg(locloc(@$), "Function overloading not allowed. Invalid identifier.\n"); yynerrs++;}
	}
;


function_declaration:
/*  FUNCTION derived_function_name ':' elementary_type_name io_OR_function_var_declarations_list function_body END_FUNCTION */
/* PRE_PARSING: The rules expected to be applied by the preparser. */
  FUNCTION derived_function_name END_FUNCTION   /* rule that is only expected to be used during preparse state => MUST print an error if used outside preparse() state!! */
	{$$ = NULL; 
	 if (get_preparse_state())    {library_element_symtable.insert($2, prev_declared_derived_function_name_token);}
	 else                         {print_err_msg(locl(@1), locf(@3), "FUNCTION with no variable declarations and no body."); yynerrs++;}
	 }
/* POST_PARSING and STANDARD_PARSING: The rules expected to be applied after the preparser has finished. */
| function_name_declaration ':' elementary_type_name io_OR_function_var_declarations_list function_body END_FUNCTION
	{$$ = new function_declaration_c($1, $3, $4, $5, locloc(@$));
	 if (!runtime_options.disable_implicit_en_eno) add_en_eno_param_decl_c::add_to($$); /* add EN and ENO declarations, if not already there */
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
	 library_element_symtable.insert($1, prev_declared_derived_function_name_token);
	}
/* | FUNCTION derived_function_name ':' derived_type_name io_OR_function_var_declarations_list function_body END_FUNCTION */
| function_name_declaration ':' derived_type_name io_OR_function_var_declarations_list function_body END_FUNCTION
	{$$ = new function_declaration_c($1, $3, $4, $5, locloc(@$));
	 if (!runtime_options.disable_implicit_en_eno) add_en_eno_param_decl_c::add_to($$); /* add EN and ENO declarations, if not already there */
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
	 library_element_symtable.insert($1, prev_declared_derived_function_name_token);
	}
/* | FUNCTION derived_function_name ':' VOID io_OR_function_var_declarations_list function_body END_FUNCTION */
| function_name_declaration ':' VOID io_OR_function_var_declarations_list function_body END_FUNCTION
	{$$ = new function_declaration_c($1, new void_type_name_c(locloc(@3)), $4, $5, locloc(@$));
	 if (!runtime_options.disable_implicit_en_eno) add_en_eno_param_decl_c::add_to($$); /* add EN and ENO declarations, if not already there */
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
	 library_element_symtable.insert($1, prev_declared_derived_function_name_token);
	}
/* ERROR_CHECK_BEGIN */
| function_name_declaration elementary_type_name io_OR_function_var_declarations_list function_body END_FUNCTION
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing after function name in function declaration."); yynerrs++;}
| function_name_declaration derived_type_name io_OR_function_var_declarations_list function_body END_FUNCTION
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing after function name in function declaration."); yynerrs++;}
| function_name_declaration ':' io_OR_function_var_declarations_list function_body END_FUNCTION
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no return type defined in function declaration."); yynerrs++;}
| function_name_declaration ':' error io_OR_function_var_declarations_list function_body END_FUNCTION
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid return type defined in function declaration."); yyerrok;}
| function_name_declaration ':' elementary_type_name function_body END_FUNCTION
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "no variable(s) declared in function declaration."); yynerrs++;}
| function_name_declaration ':' derived_type_name function_body END_FUNCTION
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "no variable(s) declared in function declaration."); yynerrs++;}
| function_name_declaration ':' elementary_type_name io_OR_function_var_declarations_list END_FUNCTION
	{$$ = NULL; print_err_msg(locl(@4), locf(@5), "no body defined in function declaration."); yynerrs++;}
| function_name_declaration ':' derived_type_name io_OR_function_var_declarations_list END_FUNCTION
	{$$ = NULL; print_err_msg(locl(@4), locf(@5), "no body defined in function declaration."); yynerrs++;}
| function_name_declaration ':' elementary_type_name END_FUNCTION
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "no variable(s) declared and body defined in function declaration."); yynerrs++;}
| function_name_declaration ':' derived_type_name END_FUNCTION
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "no variable(s) declared and body defined in function declaration."); yynerrs++;}
| function_name_declaration ':' elementary_type_name io_OR_function_var_declarations_list function_body END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locf(@3), "unclosed function declaration."); yynerrs++;}
| function_name_declaration ':' derived_type_name io_OR_function_var_declarations_list function_body END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@3), "unclosed function declaration."); yynerrs++;}
| function_name_declaration error END_FUNCTION
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in function declaration."); yyerrok;}
/* ERROR_CHECK_END */
;



/* helper symbol for function_declaration */
/* NOTE: due to reduce/reduce conflicts between identifiers
 *       being reduced to either a variable or an enumerator value,
 *       we were forced to keep a symbol table of the names
 *       of all declared variables. Variables are no longer
 *       created from simple identifier_token, but from
 *       prev_declared_variable_name_token.
 *
 *       BUT, in functions the function name itself may be used as
 *       a variable! In order to be able to parse this correctly,
 *       the token parser (flex) must return a prev_declared_variable_name_token
 *       when it comes across the function name, while parsing
 *       the function itself.
 *       We do this by inserting the function name into the variable
 *       symbol table, and having flex return a prev_declared_variable_name_token
 *       whenever it comes across it.
 *       When we finish parsing the function the variable name
 *       symbol table is cleared of all entries, and the function
 *       name is inserted into the library element symbol table. This
 *       means that from then onwards flex will return a
 *       derived_function_name_token whenever it comes across the
 *       function name.
 *
 *       In order to insert the function name into the variable_name
 *       symbol table BEFORE the function body gets parsed, we
 *       need the parser to reduce a construct that contains the
 *       the function name. That is why we created this extra
 *       construct (function_name_declaration), i.e. to force
 *       the parser to reduce it, before parsing the function body!
 */
function_name_declaration:
  /* FUNCTION derived_function_name */
  FUNCTION derived_function_name
	{$$ = $2;
	 /* the function name functions as a
	  * variable within the function itself!
	  *
	  * Remember that the variable_name_symtable
	  * is cleared once the end of the function
	  * is parsed.
	  */
	 variable_name_symtable.insert($2, prev_declared_variable_name_token);
	}
/* ERROR_CHECK_BEGIN */
| FUNCTION error 
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@1), locf(@2), "no function name defined in function declaration.");}
	 else {print_err_msg(locf(@2), locl(@2), "invalid function name in function declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;



/* intermediate helper symbol for function_declaration */
io_OR_function_var_declarations_list:
  io_var_declarations
  {$$ = new var_declarations_list_c(locloc(@1));$$->add_element($1);}
| function_var_decls
	{$$ = new var_declarations_list_c(locloc(@1));$$->add_element($1);}
| io_OR_function_var_declarations_list io_var_declarations
	{$$ = $1; $$->add_element($2);}
| io_OR_function_var_declarations_list function_var_decls
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| io_OR_function_var_declarations_list retentive_var_declarations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected retentive variable(s) declaration in function declaration."); yynerrs++;}
| io_OR_function_var_declarations_list located_var_declarations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected located variable(s) declaration in function declaration."); yynerrs++;}
| io_OR_function_var_declarations_list external_var_declarations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected external variable(s) declaration in function declaration."); yynerrs++;}
| io_OR_function_var_declarations_list global_var_declarations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected global variable(s) declaration in function declaration."); yynerrs++;}
| io_OR_function_var_declarations_list incompl_located_var_declarations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected incomplete located variable(s) declaration in function declaration."); yynerrs++;}
| io_OR_function_var_declarations_list temp_var_decls
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected temporary located variable(s) declaration in function declaration."); yynerrs++;}
| io_OR_function_var_declarations_list non_retentive_var_decls
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected non-retentive variable(s) declaration in function declaration."); yynerrs++;}
/*| io_OR_function_var_declarations_list access_declarations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected access variable(s) declaration in function declaration."); yynerrs++;}*/
| io_OR_function_var_declarations_list instance_specific_initializations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected instance specific initialization(s) in function declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


io_var_declarations:
  input_declarations
| output_declarations
| input_output_declarations
;


function_var_decls:
  VAR CONSTANT var2_init_decl_list END_VAR
	{$$ = new function_var_decls_c(new constant_option_c(locloc(@2)), $3, locloc(@$));}
| VAR var2_init_decl_list END_VAR
	{$$ = new function_var_decls_c(NULL, $2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR error var2_init_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'VAR' in function variable(s) declaration."); yyerrok;}
| VAR CONSTANT error var2_init_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'CONSTANT' in constant function variable(s) declaration."); yyerrok;}
| VAR var2_init_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed function variable(s) declaration."); yyerrok;}
| VAR CONSTANT var2_init_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed constant function variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

/* intermediate helper symbol for function_var_decls */
var2_init_decl_list:
  var2_init_decl ';'
	{$$ = new var2_init_decl_list_c(locloc(@$)); $$->add_element($1);}
| var2_init_decl_list var2_init_decl ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| var2_init_decl error
	{$$ = new var2_init_decl_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at end of function variable(s) declaration."); yyerrok;}
| var2_init_decl_list var2_init_decl error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of function variable(s) declaration."); yyerrok;}
| var2_init_decl_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid function variable(s) declaration."); yyerrok;}
| var2_init_decl_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after function variable(s) declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


function_body:
  start_ST_body_token statement_list	{$$ = $2;}
| start_IL_body_token instruction_list	{$$ = $2;}
/*
| ladder_diagram
| function_block_diagram
*/
;


var2_init_decl:
  var1_init_decl
| array_var_init_decl
| structured_var_init_decl
| string_var_declaration
;



/*****************************/
/* B 1.5.2 - Function Blocks */
/*****************************/
function_block_type_name:
  prev_declared_derived_function_block_name
| standard_function_block_name
;


standard_function_block_name: standard_function_block_name_token {$$ = new identifier_c($1, locloc(@$));};

derived_function_block_name: identifier;


function_block_declaration:
/* PRE_PARSING: The rules expected to be applied by the preparser. Will only run if pre-parsing command line option is ON. */
  FUNCTION_BLOCK derived_function_block_name END_FUNCTION_BLOCK   /* rule that is only expected to be used during preparse state => MUST print an error if used outside preparse() state!! */
	{$$ = NULL; 
	 if (get_preparse_state())    {library_element_symtable.insert($2, prev_declared_derived_function_block_name_token);}
	 else                         {print_err_msg(locl(@1), locf(@3), "FUNCTION_BLOCK with no variable declarations and no body."); yynerrs++;}
	 }
/* POST_PARSING: The rules expected to be applied after the preparser runs. Will only run if pre-parsing command line option is ON. */
| FUNCTION_BLOCK prev_declared_derived_function_block_name io_OR_other_var_declarations_list function_block_body END_FUNCTION_BLOCK
	{$$ = new function_block_declaration_c($2, $3, $4, locloc(@$));
	 if (!runtime_options.disable_implicit_en_eno) add_en_eno_param_decl_c::add_to($$); /* add EN and ENO declarations, if not already there */
	 /* Clear the variable_name_symtable. Since we have finished parsing the function block,
	  * the variable names are now out of scope, so are no longer valid!
	  */
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
	}
/* STANDARD_PARSING: The rules expected to be applied in single-phase parsing. Will only run if pre-parsing command line option is OFF. */
| FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations_list function_block_body END_FUNCTION_BLOCK
	{$$ = new function_block_declaration_c($2, $3, $4, locloc(@$));
	 library_element_symtable.insert($2, prev_declared_derived_function_block_name_token);
	 if (!runtime_options.disable_implicit_en_eno) add_en_eno_param_decl_c::add_to($$); /* add EN and ENO declarations, if not already there */
	 /* Clear the variable_name_symtable. Since we have finished parsing the function block,
	  * the variable names are now out of scope, so are no longer valid!
	  */
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
	}
/* ERROR_CHECK_BEGIN */
| FUNCTION_BLOCK io_OR_other_var_declarations_list function_block_body END_FUNCTION_BLOCK
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no function block name defined in function block declaration."); yynerrs++;}
| FUNCTION_BLOCK error io_OR_other_var_declarations_list function_block_body END_FUNCTION_BLOCK
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid function block name in function block declaration."); yyerrok;}
| FUNCTION_BLOCK derived_function_block_name function_block_body END_FUNCTION_BLOCK
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable(s) declared in function declaration."); yynerrs++;}
| FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations_list END_FUNCTION_BLOCK
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "no body defined in function block declaration."); yynerrs++;}
/*  Rule already covered by the rule to handle the preparse state!
| FUNCTION_BLOCK derived_function_block_name END_FUNCTION_BLOCK
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable(s) declared and body defined in function block declaration."); yynerrs++;}
*/
| FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations_list function_block_body END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "expecting END_FUNCTION_BLOCK before end of file."); yynerrs++;}	
| FUNCTION_BLOCK error END_FUNCTION_BLOCK
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in function block declaration."); yyerrok;}
/* ERROR_CHECK_END */
;



/* intermediate helper symbol for function_declaration */
/*  { io_var_declarations | other_var_declarations }   */
/*
 * NOTE: we re-use the var_declarations_list_c
 */
io_OR_other_var_declarations_list:
  io_var_declarations
  {$$ = new var_declarations_list_c(locloc(@$));$$->add_element($1);}
| other_var_declarations
  {$$ = new var_declarations_list_c(locloc(@$));$$->add_element($1);}
| io_OR_other_var_declarations_list io_var_declarations
	{$$ = $1; $$->add_element($2);}
| io_OR_other_var_declarations_list other_var_declarations
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| io_OR_other_var_declarations_list located_var_declarations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected located variable(s) declaration in function block declaration."); yynerrs++;}
| io_OR_other_var_declarations_list global_var_declarations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected global variable(s) declaration in function block declaration."); yynerrs++;}
/*| io_OR_other_var_declarations_list access_declarations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected access variable(s) declaration in function block declaration."); yynerrs++;}*/
| io_OR_other_var_declarations_list instance_specific_initializations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected instance specific initialization(s) in function block declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;

/* NOTE:
 *  The IEC specification gives the following definition:
 *  other_var_declarations ::=
 *     external_var_declarations
 *   | var_declarations
 *   | retentive_var_declarations
 *   | non_retentive_var_declarations
 *   | temp_var_decls
 *   | incompl_located_var_declarations
 *
 *  Nvertheless, the symbol non_retentive_var_declarations
 *  is not defined in the spec. This seems to me (Mario)
 *  to be a typo, so non_retentive_var_declarations
 *  has been replaced with non_retentive_var_decls
 *  in the following rule!
 */
other_var_declarations:
  temp_var_decls
| non_retentive_var_decls
| external_var_declarations
| var_declarations
| retentive_var_declarations
| incompl_located_var_declarations
;


temp_var_decls:
  VAR_TEMP temp_var_decls_list END_VAR
	{$$ = new temp_var_decls_c($2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR_TEMP END_VAR
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no variable declared in temporary variable(s) declaration."); yynerrs++;}
| VAR_TEMP temp_var_decls_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "unclosed temporary variable(s) declaration."); yyerrok;}
| VAR_TEMP error temp_var_decls_list END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'VAR_TEMP' in function variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;


/* intermediate helper symbol for temp_var_decls */
temp_var_decls_list:
  temp_var_decl ';'
	{$$ = new temp_var_decls_list_c(locloc(@$)); $$->add_element($1);}
| temp_var_decls_list temp_var_decl ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| error ';'
	{$$ = new temp_var_decls_list_c(locloc(@$)); print_err_msg(locf(@1), locl(@1), "invalid temporary variable(s) declaration."); yyerrok;}
| temp_var_decl error
	{$$ = new temp_var_decls_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at end of temporary variable(s) declaration."); yyerrok;}
| temp_var_decls_list temp_var_decl error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of temporary variable(s) declaration."); yyerrok;}
| temp_var_decls_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid temporary variable(s) declaration."); yyerrok;}
| temp_var_decls_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after temporary variable(s) declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


non_retentive_var_decls:
  VAR NON_RETAIN var_init_decl_list END_VAR
	{$$ = new non_retentive_var_decls_c($3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR NON_RETAIN var_init_decl_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unclosed non-retentive temporary variable(s) declaration."); yyerrok;}
| VAR NON_RETAIN error var_init_decl_list END_VAR
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'NON_RETAIN' in non-retentive temporary variable(s) declaration."); yyerrok;}
/* ERROR_CHECK_END */
;



function_block_body:
  /* NOTE: start_ST_body_token is a dummy token generated by flex when it determines it is starting to parse a POU body in ST
   *       start_IL_body_token is a dummy token generated by flex when it determines it is starting to parse a POU body in IL
   *     These tokens help remove a reduce/reduce conflict in bison, between a formal function invocation in IL, and a
   *     function invocation used as a statement (a non-standard extension added to matiec) 
   *       e.g: FUNCTION_BLOCK foo
   *            VAR ... END_VAR
   *              func_returning_void(in1 := 3        
   *                                 );               --> only the presence or absence of ';' will determine whether this is a IL or ST 
   *                                                      function invocation. (In standard ST this would be ilegal, in matiec we allow it 
   *                                                      when activated by a command line option)
   *            END_FUNCTION
   */
  start_ST_body_token statement_list	{$$ = $2;}  
| start_IL_body_token instruction_list	{$$ = $2;}
| sequential_function_chart		{$$ = $1;}
/*
| ladder_diagram
| function_block_diagram
| <other languages>
*/
;




/**********************/
/* B 1.5.3 - Programs */
/**********************/
program_type_name: identifier;


program_declaration:
/* PRE_PARSING: The rules expected to be applied by the preparser. Will only run if pre-parsing command line option is ON. */
  PROGRAM program_type_name END_PROGRAM   /* rule that is only expected to be used during preparse state => MUST print an error if used outside preparse() state!! */
	{$$ = NULL; 
	 if (get_preparse_state())    {library_element_symtable.insert($2, prev_declared_program_type_name_token);}
	 else                         {print_err_msg(locl(@1), locf(@3), "PROGRAM with no variable declarations and no body."); yynerrs++;}
	 }
/* POST_PARSING: The rules expected to be applied after the preparser runs. Will only run if pre-parsing command line option is ON. */
| PROGRAM prev_declared_program_type_name program_var_declarations_list function_block_body END_PROGRAM
	{$$ = new program_declaration_c($2, $3, $4, locloc(@$));
	 /* Clear the variable_name_symtable. Since we have finished parsing the program declaration,
	  * the variable names are now out of scope, so are no longer valid!
	  */
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
	}
/* STANDARD_PARSING: The rules expected to be applied in single-phase parsing. Will only run if pre-parsing command line option is OFF. */
| PROGRAM program_type_name {library_element_symtable.insert($2, prev_declared_program_type_name_token);} program_var_declarations_list function_block_body END_PROGRAM
	{$$ = new program_declaration_c($2, $4, $5, locloc(@$));
	 /* Clear the variable_name_symtable. Since we have finished parsing the program declaration,
	  * the variable names are now out of scope, so are no longer valid!
	  */
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
	}
/* ERROR_CHECK_BEGIN */
| PROGRAM program_var_declarations_list function_block_body END_PROGRAM
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no program name defined in program declaration.");}
| PROGRAM error program_var_declarations_list function_block_body END_PROGRAM
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid program name in program declaration."); yyerrok;}
| PROGRAM prev_declared_program_type_name function_block_body END_PROGRAM
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable(s) declared in program declaration."); yynerrs++;}
| PROGRAM prev_declared_program_type_name program_var_declarations_list END_PROGRAM
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "no body defined in program declaration."); yynerrs++;}
/*  Rule already covered by the rule to handle the preparse state!
| PROGRAM prev_declared_program_type_name END_PROGRAM 
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable(s) declared and body defined in program declaration."); yynerrs++;}
*/
| PROGRAM prev_declared_program_type_name program_var_declarations_list function_block_body END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed program declaration."); yynerrs++;}
| PROGRAM error END_PROGRAM
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in program declaration."); yyerrok;}
/* ERROR_CHECK_END */
;


/* helper symbol for program_declaration */
/*
 * NOTE: we re-use the var_declarations_list_c
 */
program_var_declarations_list:
  io_var_declarations
	{$$ = new var_declarations_list_c(locloc(@$)); $$->add_element($1);}
| other_var_declarations
	{$$ = new var_declarations_list_c(locloc(@$)); $$->add_element($1);}
| located_var_declarations
	{$$ = new var_declarations_list_c(locloc(@$)); $$->add_element($1);}
| program_var_declarations_list io_var_declarations
	{$$ = $1; $$->add_element($2);}
| program_var_declarations_list other_var_declarations
	{$$ = $1; $$->add_element($2);}
| program_var_declarations_list located_var_declarations
	{$$ = $1; $$->add_element($2);}
/*
| program_var_declarations_list program_access_decls
	{$$ = $1; $$->add_element($2);}
*/
/* ERROR_CHECK_BEGIN */
| program_var_declarations_list global_var_declarations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected global variable(s) declaration in function block declaration."); yynerrs++;}
/*| program_var_declarations_list access_declarations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected access variable(s) declaration in function block declaration."); yynerrs++;}*/
| program_var_declarations_list instance_specific_initializations
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected instance specific initialization(s) in function block declaration."); yynerrs++;
	}
/* ERROR_CHECK_END */
;


/* TODO ... */
/*
program_access_decls:
  VAR_ACCESS program_access_decl_list END_VAR
;
*/

/* helper symbol for program_access_decls */
/*
program_access_decl_list:
  program_access_decl ';'
| program_access_decl_list program_access_decl ';'
;
*/

/*
program_access_decl:
  access_name ':' symbolic_variable ':' non_generic_type_name
| access_name ':' symbolic_variable ':' non_generic_type_name direction
;
*/



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

sequential_function_chart:
  sfc_network
	{$$ = new sequential_function_chart_c(locloc(@$)); $$->add_element($1);}
| sequential_function_chart sfc_network
	{$$ = $1; $$->add_element($2);}
;

sfc_network:
  initial_step
	{$$ = new sfc_network_c(locloc(@$)); $$->add_element($1);}
| sfc_network step
	{$$ = $1; $$->add_element($2);}
| sfc_network transition
	{$$ = $1; $$->add_element($2);}
| sfc_network action
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| sfc_network error 
	{$$ = $1; print_err_msg(locl(@1), locf(@2), "unexpected token after SFC network in sequencial function chart."); yyerrok;}
/* ERROR_CHECK_END */
;

initial_step:
  INITIAL_STEP step_name ':' action_association_list END_STEP
//  INITIAL_STEP identifier ':' action_association_list END_STEP
	{$$ = new initial_step_c($2, $4, locloc(@$));
	 variable_name_symtable.insert($2, prev_declared_variable_name_token); // A step name may later be used as a structured variable!!
	}
/* ERROR_CHECK_BEGIN */
| INITIAL_STEP ':' action_association_list END_STEP
  {$$ = NULL; print_err_msg(locf(@1), locl(@2), "no step name defined in initial step declaration."); yynerrs++;}
| INITIAL_STEP error ':' action_association_list END_STEP
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid step name defined in initial step declaration."); yyerrok;}
| INITIAL_STEP step_name action_association_list END_STEP
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "':' missing after step name in initial step declaration."); yynerrs++;}
| INITIAL_STEP step_name ':' error END_STEP
	{$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid action association list in initial step declaration."); yyerrok;}
| INITIAL_STEP step_name ':' action_association_list END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@3), "unclosed initial step declaration."); yynerrs++;}
| INITIAL_STEP error END_STEP
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in initial step declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

step:
  STEP step_name ':' action_association_list END_STEP
//  STEP identifier ':' action_association_list END_STEP
	{$$ = new step_c($2, $4, locloc(@$));
	 variable_name_symtable.insert($2, prev_declared_variable_name_token); // A step name may later be used as a structured variable!!
	}
/* ERROR_CHECK_BEGIN */
| STEP ':' action_association_list END_STEP
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no step name defined in step declaration."); yynerrs++;}
| STEP error ':' action_association_list END_STEP
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid step name defined in step declaration."); yyerrok;}
| STEP step_name action_association_list END_STEP
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "':' missing after step name in step declaration."); yynerrs++;}
| STEP step_name ':' error END_STEP
	{$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid action association list in step declaration."); yyerrok;}
| STEP step_name ':' action_association_list END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@3), "invalid action association list in step declaration."); yynerrs++;}
| STEP error END_STEP
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in step declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

/* helper symbol for:
 *  - initial_step
 *  - step
 */
action_association_list:
  /* empty */
	{$$ = new action_association_list_c(locloc(@$));}
| action_association_list action_association ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| action_association_list action_association error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at end of action association declaration."); yyerrok;}
| action_association_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after action association declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


// step_name: identifier;
step_name: any_identifier;

action_association:
  action_name '(' {cmd_goto_sfc_qualifier_state();} action_qualifier {cmd_pop_state();} indicator_name_list ')'
	{$$ = new action_association_c($1, $4, $6, locloc(@$));}
/* ERROR_CHECK_BEGIN */
/*| action_name '(' error ')'
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid qualifier defined in action association."); yyerrok;}*/
/* ERROR_CHECK_END */
;

/* helper symbol for action_association */
indicator_name_list:
  /* empty */
	{$$ = new indicator_name_list_c(locloc(@$));}
| indicator_name_list ',' indicator_name
	{$$ = $1; $$->add_element($3);}
/* ERROR_CHECK_BEGIN */
| indicator_name_list indicator_name
	{$$ = $1; print_err_msg(locl(@1), locf(@2), "',' missing at end of action association declaration."); yynerrs++;}
| indicator_name_list ',' error
	{$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no indicator defined in indicator list.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid indicator in indicator list."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

// action_name: identifier;
action_name: any_identifier;

action_qualifier:
  /* empty */
	{$$ = NULL;}
| qualifier
	{$$ = new action_qualifier_c($1, NULL, locloc(@$));}
| timed_qualifier ',' action_time
	{$$ = new action_qualifier_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| timed_qualifier action_time
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "',' missing between timed qualifier and action time in action qualifier."); yynerrs++;}
| timed_qualifier ',' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no action time defined in action qualifier.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid action time in action qualifier."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

qualifier:
  N		{$$ = new qualifier_c(strdup("N"), locloc(@$));}
| R		{$$ = new qualifier_c(strdup("R"), locloc(@$));}
| S		{$$ = new qualifier_c(strdup("S"), locloc(@$));}
| P		{$$ = new qualifier_c(strdup("P"), locloc(@$));}
| P0	{$$ = new qualifier_c(strdup("P0"), locloc(@$));}
| P1	{$$ = new qualifier_c(strdup("P1"), locloc(@$));}
;

timed_qualifier:
  L		{$$ = new timed_qualifier_c(strdup("L"), locloc(@$));}
| D		{$$ = new timed_qualifier_c(strdup("D"), locloc(@$));}
| SD		{$$ = new timed_qualifier_c(strdup("SD"), locloc(@$));}
| DS		{$$ = new timed_qualifier_c(strdup("DS"), locloc(@$));}
| SL		{$$ = new timed_qualifier_c(strdup("SL"), locloc(@$));}
;

/* NOTE: A step_name may be used as a structured vaqriable, in order to access the status bit (e.g. Step1.X) 
 *       or the time it has been active (e.g. Step1.T). 
 *       In order to allow the step name to be used as a variable inside ST expressions (only ST expressions ??)
 *       when defining transitions, we need to add the step_name to the list of previously declared variables.
 *       This allows the step name to be used as a variable inside all transition expressions, as the user
 *       can clearly define the transition _after_ the step itself has been defined/declared, so the 
 *       'variable' is previously 'declared'.
 *
 *       However, when defining/declaring a step, a variable name can also be used to define a timed
 *       action association. In this case, we may have a circular reference:
 *        e.g.
 *            ...
 *             STEP step1:
 *                action1 (D,t#100ms);
 *             end_step
 *
 *             STEP step2:
 *                action1 (D,step3.T);  <---- forward reference to step3.T !!!!!!
 *             end_step
 *
 *             STEP step3:
 *                action1 (D,step2.T);  <---- back reference to step2.T
 *             end_step
 *
 *
 *         There is no way the user can always use the step3.T variable only after it has
 *         been 'declared'. So adding the steps to the list of previously declared variables 
 *         when the steps are declared is not a solution to the above situation.
 *
 *         Fortunately, the standard does not allow ST expressions in the above syntax
 *         (i.e. when defining the delay of a timed actions), but only either a 
 *         Time literal, or a variable.
 *         This is why we change the definition of action_time from
 *         action_time:
 *           duration
 *         | variable
 *         ;
 *
 *         to:
 *         action_time:
 *           duration
 *         | any_symbolic_variable
 *         ;
 *
 *       NOTE that this same problem does not occur with the 'indicator_name': it does not
 *       make sense to set/indicate a step1.X variable, as these variables are read-only!
 */     
    
action_time:
  duration
//| variable
  | any_symbolic_variable
;

indicator_name: variable;

// transition_name: identifier;
transition_name: any_identifier;


steps:
  step_name
	{$$ = new steps_c($1, NULL, locloc(@$));}
| '(' step_name_list ')'
	{$$ = new steps_c(NULL, $2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| '(' step_name_list error
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "expecting ')' at the end of step list in transition declaration."); yyerrok;}
| '(' error ')'
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid step list in transition declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

step_name_list:
  step_name ',' step_name
	{$$ = new step_name_list_c(locloc(@$)); $$->add_element($1); $$->add_element($3);}
| step_name_list ',' step_name
	{$$ = $1; $$->add_element($3);}
/* ERROR_CHECK_BEGIN */
| step_name_list step_name
	{$$ = $1; print_err_msg(locl(@1), locf(@2), "',' missing in step list."); yynerrs++;}
| step_name_list ',' error
	{$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no step name defined in step list.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid step name in step list."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


/* NOTE: flex will automatically pop() out of body_state to previous state.
 *       We do not need to give a command from bison to return to previous flex state,
 *       after forcing flex to go to body_state.
 */
transition:
  TRANSITION transition_priority
    FROM steps TO steps 
    {cmd_goto_body_state();} transition_condition 
  END_TRANSITION 
	{$$ = new transition_c(NULL, $2, $4, $6, $8, locloc(@$));}
//| TRANSITION identifier FROM steps TO steps ... 
| TRANSITION transition_name transition_priority
    FROM steps TO steps 
    {cmd_goto_body_state();} transition_condition 
  END_TRANSITION 
	{$$ = new transition_c($2, $3, $5, $7, $9, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| TRANSITION error transition_priority FROM steps TO steps {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid transition name defined in transition declaration."); yyerrok;}
| TRANSITION transition_name error FROM steps TO steps {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid transition priority defined in transition declaration."); yyerrok;}
| TRANSITION transition_priority FROM TO steps {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "no origin step(s) defined in transition declaration."); yynerrs++;}
| TRANSITION transition_name transition_priority FROM TO steps {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locl(@4), locf(@5), "no origin step(s) defined in transition declaration."); yynerrs++;}
| TRANSITION transition_priority FROM error TO steps {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid origin step(s) defined in transition declaration."); yyerrok;}
| TRANSITION transition_name transition_priority FROM error TO steps {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locf(@5), locl(@5), "invalid origin step(s) defined in transition declaration."); yyerrok;}
| TRANSITION transition_priority FROM steps steps {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locl(@4), locf(@5), "'TO' missing between origin step(s) and destination step(s) in transition declaration."); yynerrs++;}
| TRANSITION transition_name transition_priority FROM steps steps {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locl(@5), locf(@6), "'TO' missing between origin step(s) and destination step(s) in transition declaration."); yynerrs++;}
| TRANSITION transition_priority FROM steps TO {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locl(@5), locf(@7), "no destination step(s) defined in transition declaration."); yynerrs++;}
| TRANSITION transition_name transition_priority FROM steps TO {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locl(@6), locf(@8), "no destination step(s) defined in transition declaration."); yynerrs++;}
| TRANSITION transition_priority FROM steps TO error {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locf(@6), locl(@6), "invalid destination step(s) defined in transition declaration."); yyerrok;}
| TRANSITION transition_name transition_priority FROM steps TO error {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locf(@7), locl(@7), "invalid destination step(s) defined in transition declaration."); yyerrok;}
| TRANSITION transition_priority {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locl(@2), locf(@4), "no origin and destination step(s) defined in transition declaration."); yynerrs++;}
| TRANSITION transition_name transition_priority {cmd_goto_body_state();} transition_condition END_TRANSITION
	{$$ = NULL; print_err_msg(locl(@3), locf(@5), "no origin and destination step(s) defined in transition declaration."); yynerrs++;}
/*| TRANSITION transition_priority FROM steps TO steps {cmd_goto_body_state();} transition_condition error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@6), "unclosed transition declaration."); yyerrok;}
| TRANSITION transition_name transition_priority FROM steps TO steps {cmd_goto_body_state();} transition_condition error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@7), "unclosed transition declaration."); yyerrok;}*/
| TRANSITION error END_TRANSITION
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in transition declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

transition_priority:
  /* empty */
  {$$ = NULL;}
| '(' {cmd_goto_sfc_priority_state();} PRIORITY {cmd_pop_state();} ASSIGN integer ')'
	{$$ = $6;}
/* ERROR_CHECK_BEGIN */
/* The following error checking rules have been intentionally commented out. */
/*
| '(' ASSIGN integer ')'
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'PRIORITY' missing between '(' and ':=' in transition declaration with priority."); yynerrs++;}
| '(' error ASSIGN integer ')'
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "expecting 'PRIORITY' between '(' and ':=' in transition declaration with priority."); yyerrok;}
*/
/* ERROR_CHECK_END */
;


transition_condition:
 start_IL_body_token ':' eol_list simple_instr_list
	{$$ = new transition_condition_c($4, NULL, locloc(@$));}
| ASSIGN expression ';'
	{$$ = new transition_condition_c(NULL, $2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| start_IL_body_token eol_list simple_instr_list
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "':' missing before IL condition in transition declaration."); yynerrs++;}
| start_IL_body_token ':' eol_list error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@3), locf(@4), "no instructions defined in IL condition of transition declaration.");}
	 else {print_err_msg(locf(@4), locl(@4), "invalid instructions in IL condition of transition declaration."); yyclearin;}
	 yyerrok;
	}
| ASSIGN ';'
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no expression defined in ST condition of transition declaration."); yynerrs++;}
| ASSIGN error ';'
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid expression defined in ST condition of transition declaration."); yyerrok;}
| ASSIGN expression error
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "expecting ';' after expression defined in ST condition of transition declaration."); yyerrok;}
/* ERROR_CHECK_END */
;



action:
//  ACTION identifier ':' ... 
  ACTION action_name {cmd_goto_body_state();} action_body END_ACTION
	{$$ = new action_c($2, $4, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| ACTION {cmd_goto_body_state();} action_body END_ACTION
  {$$ = NULL; print_err_msg(locl(@1), locf(@3), "no action name defined in action declaration."); yynerrs++;}
| ACTION error {cmd_goto_body_state();} action_body END_ACTION
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid action name defined in action declaration."); yyerrok;}
| ACTION action_name {cmd_goto_body_state();} function_block_body END_ACTION
	{$$ = NULL; print_err_msg(locl(@2), locf(@4), "':' missing after action name in action declaration."); yynerrs++;}
/*| ACTION action_name {cmd_goto_body_state();} action_body END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed action declaration."); yyerrok;}*/
| ACTION error END_ACTION
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in action declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

action_body:
  ':' function_block_body
  {$$ = $2;}
/* ERROR_CHECK_BEGIN */
| ':' error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@1), locf(@2), "no body defined in action declaration.");}
	 else {print_err_msg(locf(@2), locl(@2), "invalid body defined in action declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


/********************************/
/* B 1.7 Configuration elements */
/********************************/
/* NOTE:
 * It is not clear from reading the specification to which namespace
 * the names of resources, tasks and programs belong to.
 *
 * The following syntax assumes that resource and program names belong to the
 * same namespace as the variables defined within
 * the resource/configuration (i.e. VAR_GLOBAL).
 * Task names belong to a namespace all of their own, since they don't
 * produce conflicts in the syntax parser, so we might just as well
 * leave them be! ;-)
 * The above decision was made taking into
 * account that inside a VAR_CONFIG declaration global variables
 * may be referenced starting off from the resource name as:
 *   resource_name.program_name.variable_name
 * Notice how resource names and program names are used in a very similar
 * manner as are variable names.
 * Using a single namespace for all the above mentioned names
 * also makes it easier to write the syntax parser!! ;-) Using a private
 * namespace for each of the name types (resource names, program names,
 * global varaiable names), i.e. letting the names be re-used across
 * each of the groups (resource, program, global variables), produces
 * reduce/reduce conflicts in the syntax parser. Actually, it is only
 * the resource names that need to be distinguished into a 
 * prev_declared_resource_name so as not to conflict with [gloabl] variable
 * names in the 'data' construct.
 * The program names are only tracked to make sure that two programs do not
 * get the same name.
 *
 * Using a single namespace does have the drawback that the user will
 * not be able to re-use names for resources or programs if these
 * have already been used to name a variable!
 *
 * If it ever becomes necessary to change this interpretation of
 * the syntax, then this section of the syntax parser must be updated!
 */
prev_declared_global_var_name:    prev_declared_global_var_name_token    {$$ = new identifier_c($1, locloc(@$));};
prev_declared_resource_name:      prev_declared_resource_name_token      {$$ = new identifier_c($1, locloc(@$));};
prev_declared_program_name:       prev_declared_program_name_token       {$$ = new identifier_c($1, locloc(@$));};
prev_declared_configuration_name: prev_declared_configuration_name_token {$$ = new identifier_c($1, locloc(@$));};
// prev_declared_task_name:       prev_declared_task_name_token          {$$ = new identifier_c($1, locloc(@$));};






configuration_name: identifier;

/* NOTE: The specification states that valid resource type names
 *       are implementation defined, i.e. each implementaion will define
 *       what resource types it supports.
 *       We are implementing this syntax parser to be used by any
 *       implementation, so at the moment we accept any identifier
 *       as a resource type name.
 *       This implementation should probably be changed in the future. We
 *       should probably have a resource_type_name_token, and let the
 *       implementation load the global symbol library with the
 *       accepted resource type names before parsing the code.
 *
 */
resource_type_name: any_identifier;

configuration_declaration:
/* PRE_PARSING: The rules expected to be applied by the preparser. Will only run if pre-parsing command line option is ON. */
  CONFIGURATION configuration_name END_CONFIGURATION   /* rule that is only expected to be used during preparse state */
	{$$ = NULL; 
	 if (get_preparse_state())    {library_element_symtable.insert($2, prev_declared_configuration_name_token);}
	 else                         {print_err_msg(locl(@1), locf(@3), "no resource(s) nor program(s) defined in configuration declaration."); yynerrs++;}
	 }
/* POST_PARSING: The rules expected to be applied after the preparser runs. Will only run if pre-parsing command line option is ON. */
| CONFIGURATION prev_declared_configuration_name
   global_var_declarations_list
   single_resource_declaration
   {variable_name_symtable.pop();
    direct_variable_symtable.pop();}
   optional_access_declarations
   optional_instance_specific_initializations
  END_CONFIGURATION
	{$$ = new configuration_declaration_c($2, $3, $4, $6, $7, locloc(@$));
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
	}
| CONFIGURATION prev_declared_configuration_name
   global_var_declarations_list
   resource_declaration_list
   optional_access_declarations
   optional_instance_specific_initializations
 END_CONFIGURATION
	{$$ = new configuration_declaration_c($2, $3, $4, $5, $6, locloc(@$));
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
}
/* STANDARD_PARSING: The rules expected to be applied in single-phase parsing. Will only run if pre-parsing command line option is OFF. */
| CONFIGURATION configuration_name
   global_var_declarations_list
   single_resource_declaration
   {variable_name_symtable.pop();
    direct_variable_symtable.pop();}
   optional_access_declarations
   optional_instance_specific_initializations
  END_CONFIGURATION
	{$$ = new configuration_declaration_c($2, $3, $4, $6, $7, locloc(@$));
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
	 library_element_symtable.insert($2, prev_declared_configuration_name_token);
	}
| CONFIGURATION configuration_name
   global_var_declarations_list
   resource_declaration_list
   optional_access_declarations
   optional_instance_specific_initializations
 END_CONFIGURATION
	{$$ = new configuration_declaration_c($2, $3, $4, $5, $6, locloc(@$));
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
	 library_element_symtable.insert($2, prev_declared_configuration_name_token);
}
/* ERROR_CHECK_BEGIN */
| CONFIGURATION 
   global_var_declarations_list
   single_resource_declaration
   {variable_name_symtable.pop();
    direct_variable_symtable.pop();}
   optional_access_declarations
   optional_instance_specific_initializations
  END_CONFIGURATION
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no configuration name defined in configuration declaration."); yynerrs++;}
| CONFIGURATION
   global_var_declarations_list
   resource_declaration_list
   optional_access_declarations
   optional_instance_specific_initializations
  END_CONFIGURATION
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no configuration name defined in configuration declaration."); yynerrs++;}
| CONFIGURATION error
   global_var_declarations_list
   single_resource_declaration
   {variable_name_symtable.pop();
    direct_variable_symtable.pop();}
   optional_access_declarations
   optional_instance_specific_initializations
  END_CONFIGURATION
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid configuration name defined in configuration declaration."); yyerrok;}
| CONFIGURATION error
   global_var_declarations_list
   resource_declaration_list
   optional_access_declarations
   optional_instance_specific_initializations
  END_CONFIGURATION
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid configuration name defined in configuration declaration."); yyerrok;}
/*  Rule already covered by the rule to handle the preparse state!
| CONFIGURATION configuration_name
   global_var_declarations_list
   optional_access_declarations
   optional_instance_specific_initializations
  END_CONFIGURATION
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "no resource(s) defined in configuration declaration."); yynerrs++;}
*/
| CONFIGURATION configuration_name
   global_var_declarations_list
   error
   optional_access_declarations
   optional_instance_specific_initializations
  END_CONFIGURATION
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid resource(s) defined in configuration declaration."); yyerrok;}
/*| CONFIGURATION configuration_name
   global_var_declarations_list
   single_resource_declaration
   {variable_name_symtable.pop();
    direct_variable_symtable.pop();}
   optional_access_declarations
   optional_instance_specific_initializations
  END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed configuration declaration."); yyerrok;}*/
| CONFIGURATION configuration_name
   global_var_declarations_list
   resource_declaration_list
   optional_access_declarations
   optional_instance_specific_initializations
  END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed configuration declaration."); yyerrok;}
| CONFIGURATION error END_CONFIGURATION
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in configuration declaration."); yyerrok;}
/* ERROR_CHECK_END */
;

// helper symbol for
//  - configuration_declaration
//  - resource_declaration
//
/* NOTE: The IEC 61131-3 v2 standard defines this list as being: [global_var_declarations]
 *        e.g.:
 *          'CONFIGURATION' configuration_name  [global_var_declarations] ...
 *
 *       However, this means that a single VAR_GLOBAL ... END_VAR construct is allowed
 *       in each CONFIGURATION or RESOURCE declaration. If the user wishes to have global
 *       variables with distinct properties (e.g. some with RETAIN, others with CONSTANT,
 *       and yet other variables with none of these qualifiers), the syntax defined in the 
 *       standard does not allow this.
 *       Amazingly, IEC 61131-3 v3 also does not seem to allow it either!!
 *       Since this is most likely a bug in the standard, we are changing the syntax slightly
 *       to become:
 *          'CONFIGURATION' configuration_name  {global_var_declarations} ...
 *
 *       Remember that:
 *          {S}, closure, meaning zero or more concatenations of S.
 *          [S], option, meaning zero or one occurrence of S.
 */
global_var_declarations_list:
  // empty
	{$$ = new global_var_declarations_list_c(locloc(@$));}
| global_var_declarations_list global_var_declarations
	{$$ = $1; $$->add_element($2);}
;

// helper symbol for configuration_declaration //
optional_access_declarations:
  // empty
	{$$ = NULL;}
//| access_declarations
;

// helper symbol for configuration_declaration //
optional_instance_specific_initializations:
  // empty
	{$$ = NULL;}
| instance_specific_initializations
;

// helper symbol for configuration_declaration //
resource_declaration_list:
  resource_declaration
	{$$ = new resource_declaration_list_c(locloc(@$)); $$->add_element($1);}
| resource_declaration_list resource_declaration
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| resource_declaration_list error
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected token after resource declaration."); yyerrok;}
/* ERROR_CHECK_END */
;


resource_declaration:
  RESOURCE {variable_name_symtable.push();direct_variable_symtable.push();} resource_name {variable_name_symtable.insert($3, prev_declared_resource_name_token);} ON resource_type_name
   global_var_declarations_list
   single_resource_declaration
  END_RESOURCE
	{$$ = new resource_declaration_c($3, $6, $7, $8, locloc(@$));
	 variable_name_symtable.pop();
	 direct_variable_symtable.pop();
	 variable_name_symtable.insert($3, prev_declared_resource_name_token);
	}
/* ERROR_CHECK_BEGIN */
| RESOURCE {variable_name_symtable.push();direct_variable_symtable.push();} ON resource_type_name
   global_var_declarations_list
   single_resource_declaration
  END_RESOURCE
  {$$ = NULL; print_err_msg(locl(@1), locf(@3), "no resource name defined in resource declaration."); yynerrs++;}
/*|	RESOURCE {variable_name_symtable.push();direct_variable_symtable.push();} resource_name ON resource_type_name
   global_var_declarations_list
   single_resource_declaration
  END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@5), "unclosed resource declaration."); yyerrok;}*/
| RESOURCE error END_RESOURCE
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in resource declaration."); yyerrok;}
/* ERROR_CHECK_END */
;


single_resource_declaration:
 task_configuration_list program_configuration_list
	{$$ = new single_resource_declaration_c($1, $2, locloc(@$));}
;


// helper symbol for single_resource_declaration //
task_configuration_list:
  // empty
	{$$ = new task_configuration_list_c(locloc(@$));}
| task_configuration_list task_configuration ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| task_configuration_list task_configuration error
  {$$ = $1; print_err_msg(locl(@1), locf(@2), "';' missing at the end of task configuration in resource declaration."); yyerrok;}
| task_configuration_list ';'
  {$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after task configuration in resource declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


// helper symbol for single_resource_declaration //
program_configuration_list:
  program_configuration ';'
	{$$ = new program_configuration_list_c(locloc(@$)); $$->add_element($1);}
| program_configuration_list program_configuration ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| program_configuration error
  {$$ = new program_configuration_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at the end of program configuration in resource declaration."); yyerrok;}
| program_configuration_list program_configuration error
  {$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at the end of program configuration in resource declaration."); yyerrok;}
| program_configuration_list error ';'
  {$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid program configuration in resource declaration."); yyerrok;}
| program_configuration_list ';'
  {$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after program configuration in resource declaration."); yynerrs++;}
/* ERROR_CHECK_END */
;


resource_name: identifier;

/*
access_declarations:
 VAR_ACCESS access_declaration_list END_VAR
	{$$ = NULL;}
// ERROR_CHECK_BEGIN //
| VAR_ACCESS END_VAR
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no variable declared in access variable(s) declaration."); yynerrs++;}
| VAR_ACCESS error access_declaration_list END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'VAR_ACCESS' in access variable(s) declaration."); yyerrok;}
| VAR_ACCESS access_declaration_list error END_VAR
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed access variable(s) declaration."); yyerrok;}
| VAR_ACCESS error END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in access variable(s) declaration."); yyerrok;}
// ERROR_CHECK_END //
;

// helper symbol for access_declarations //
access_declaration_list:
  access_declaration ';'
| access_declaration_list access_declaration ';'
// ERROR_CHECK_BEGIN //
| error ';'
  {$$ = // create a new list //;
	 print_err_msg(locf(@1), locl(@1), "invalid access variable declaration."); yyerrok;}
| access_declaration error
  {$$ = // create a new list //;
	 print_err_msg(locl(@1), locf(@2), "';' missing at the end of access variable declaration."); yyerrok;}
| access_declaration_list access_declaration error
  {$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at the end of access variable declaration."); yyerrok;}
| access_declaration_list error ';'
  {$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid access variable declaration."); yyerrok;}
| access_declaration_list ';'
  {$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after access variable declaration."); yynerrs++;}
// ERROR_CHECK_END //
;


access_declaration:
  access_name ':' access_path ':' non_generic_type_name
| access_name ':' access_path ':' non_generic_type_name direction
;


access_path:
  prev_declared_direct_variable
| prev_declared_resource_name '.' prev_declared_direct_variable
| any_fb_name_list symbolic_variable
| prev_declared_resource_name '.' any_fb_name_list symbolic_variable
| prev_declared_program_name '.'  any_fb_name_list symbolic_variable
| prev_declared_resource_name '.' prev_declared_program_name '.' any_fb_name_list symbolic_variable
;
*/

// helper symbol for
//  - access_path
//  - instance_specific_init
//
/* NOTE: The fb_name_list refers to funtion block variables
 *       that have been declared in a scope outside the one we are
 *       currently parsing, so we must accept them to be any_identifier!
 *
 *       Beware that other locations of this syntax parser also require
 *       a fb_name_list. In those locations the function blocks are being declared,
 *       so only currently un-used identifiers (i.e. identifier) may be accepted.
 *
 *       In order to distinguish the two, here we use any_fb_name_list, while
 *       in the the locations we simply use fb_name_list!
 */
any_fb_name_list:
  // empty
	{$$ = new any_fb_name_list_c(locloc(@$));}
//| fb_name_list fb_name '.'
| any_fb_name_list any_identifier '.'
	{$$ = $1; $$->add_element($2);}
;



global_var_reference:
//  [resource_name '.'] global_var_name ['.' structure_element_name] //
                                  prev_declared_global_var_name
	{$$ = new global_var_reference_c(NULL, $1, NULL, locloc(@$));}
|                                 prev_declared_global_var_name '.' structure_element_name
	{$$ = new global_var_reference_c(NULL, $1, $3, locloc(@$));}
| prev_declared_resource_name '.' prev_declared_global_var_name
	{$$ = new global_var_reference_c($1, $3, NULL, locloc(@$));}
| prev_declared_resource_name '.' prev_declared_global_var_name '.' structure_element_name
	{$$ = new global_var_reference_c($1, $3, $5, locloc(@$));}
;


//access_name: identifier;


program_output_reference:
/* NOTE:
 * program_output_reference is merely used within data_source.
 * data_source is merely used within task_initialization
 * task_initialization appears in a configuration declaration
 * _before_ the programs are declared, so we cannot use
 * prev_declared_program_name, as what might seem correct at first.
 *
 * The semantic checker must later check whether the identifier
 * used really refers to a program declared after the task
 * initialization!
 */
//  prev_declared_program_name '.' symbolic_variable
  program_name '.' symbolic_variable
	{$$ = new program_output_reference_c($1, $3, locloc(@$));}
;

program_name: identifier;

/*
direction:
  READ_WRITE
	{$$ = NULL;}
| READ_ONLY
	{$$ = NULL;}
;
*/

task_configuration:
  TASK task_name task_initialization
	{$$ = new task_configuration_c($2, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| TASK task_initialization
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no task name defined in task declaration."); yynerrs++;}
| TASK error task_initialization
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid task name defined in task declaration."); yyerrok;}
| TASK task_name error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no task initialization defined in task declaration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid task initialization in task declaration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/* NOTE: The specification does not mention the namespace to which task names
 *       should belong to. Unlike resource and program names, for the moment we
 *       let the task names belong to their own private namespace, as they do not
 *       produce any conflicts in the syntax parser.
 *       If in the future our interpretation of the spec. turns out to be incorrect,
 *       the definition of task_name may have to be changed!
 */
task_name: any_identifier;


task_initialization:
//  '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' //
  '(' {cmd_goto_task_init_state();} task_initialization_single task_initialization_interval task_initialization_priority ')'
	{$$ = new task_initialization_c($3, $4, $5, locloc(@$));}
;


task_initialization_single:
// [SINGLE ASSIGN data_source ',']
  /* empty */
	{$$ = NULL;}
| SINGLE ASSIGN {cmd_pop_state();} data_source ',' {cmd_goto_task_init_state();} 
	{$$ = $4;}
/* ERROR_CHECK_BEGIN */
| SINGLE {cmd_pop_state();} data_source ',' {cmd_goto_task_init_state();}
  {$$ = NULL; print_err_msg(locl(@1), locf(@3), "':=' missing after 'SINGLE' in task initialization."); yynerrs++;}
| SINGLE ASSIGN {cmd_pop_state();} ',' {cmd_goto_task_init_state();}
  {$$ = NULL; print_err_msg(locl(@2), locf(@4), "no data source defined in 'SINGLE' statement of task initialization."); yynerrs++;}
| SINGLE ASSIGN {cmd_pop_state();} error ',' {cmd_goto_task_init_state();}
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid data source defined in 'SINGLE' statement of task initialization."); yyerrok;}
/* ERROR_CHECK_END */
;


task_initialization_interval:
// [INTERVAL ASSIGN data_source ','] 
  /* empty */
	{$$ = NULL;}
| INTERVAL ASSIGN {cmd_pop_state();} data_source ',' {cmd_goto_task_init_state();}
	{$$ = $4;}
/* ERROR_CHECK_BEGIN */
| INTERVAL {cmd_pop_state();} data_source ',' {cmd_goto_task_init_state();}
  {$$ = NULL; print_err_msg(locl(@1), locf(@3), "':=' missing after 'INTERVAL' in task initialization.");}
| INTERVAL ASSIGN {cmd_pop_state();} ',' {cmd_goto_task_init_state();}
  {$$ = NULL; print_err_msg(locl(@2), locf(@4), "no data source defined in 'INTERVAL' statement of task initialization."); yynerrs++;}
| INTERVAL ASSIGN {cmd_pop_state();} error ',' {cmd_goto_task_init_state();}
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid data source defined in 'INTERVAL' statement of task initialization."); yyerrok;}
/* ERROR_CHECK_END */
;



task_initialization_priority:
// PRIORITY ASSIGN integer
  PRIORITY ASSIGN {cmd_pop_state();} integer 
	{$$ = $4;}
/* ERROR_CHECK_BEGIN */
| PRIORITY {cmd_pop_state();} integer
  {$$ = NULL; print_err_msg(locl(@1), locf(@3), "':=' missing after 'PRIORITY' in task initialization."); yynerrs++;}
| PRIORITY ASSIGN {cmd_pop_state();} error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@4), "no priority number defined in 'PRIORITY' statement of task initialization.");}
	 else {print_err_msg(locf(@4), locl(@4), "invalid priority number in 'PRIORITY' statement of task initialization."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;



data_source:
  constant
| global_var_reference
| program_output_reference
| prev_declared_direct_variable
;

program_configuration:
//  PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] //
  PROGRAM program_name optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
	{$$ = new program_configuration_c(NULL, $2, $3, $5, $6, locloc(@$));
	 variable_name_symtable.insert($2, prev_declared_program_name_token);
	}
| PROGRAM RETAIN program_name optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
	{$$ = new program_configuration_c(new retain_option_c(locloc(@2)), $3, $4, $6, $7, locloc(@$));
	 variable_name_symtable.insert($3, prev_declared_program_name_token);
	}
| PROGRAM NON_RETAIN program_name optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
	{$$ = new program_configuration_c(new non_retain_option_c(locloc(@2)), $3, $4, $6, $7, locloc(@$));
	 variable_name_symtable.insert($3, prev_declared_program_name_token);
	}
/* ERROR_CHECK_BEGIN */
| PROGRAM program_name optional_task_name ':' identifier optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locf(@5), locl(@5), "invalid program type name after ':' in program configuration."); yynerrs++;}
| PROGRAM RETAIN program_name optional_task_name ':' identifier optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locf(@6), locl(@6), "invalid program type name after ':' in program configuration."); yynerrs++;}
| PROGRAM NON_RETAIN program_name optional_task_name ':' identifier optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locf(@6), locl(@6), "invalid program type name after ':' in program configuration."); yynerrs++;}
| PROGRAM error program_name optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'PROGRAM' in program configuration."); yyerrok;}
| PROGRAM RETAIN error program_name optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'RETAIN' in retentive program configuration."); yyerrok;}
| PROGRAM NON_RETAIN error program_name optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "unexpected token after 'NON_RETAIN' in non-retentive program configuration."); yyerrok;}
| PROGRAM optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no program name defined in program configuration."); yynerrs++;}
| PROGRAM RETAIN optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "no program name defined in retentive program configuration."); yynerrs++;}
| PROGRAM NON_RETAIN optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "no program name defined in non-retentive program configuration."); yynerrs++;}
| PROGRAM error optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid program name defined in program configuration."); yyerrok;}
| PROGRAM RETAIN error optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid program name defined in retentive program configuration."); yyerrok;}
| PROGRAM NON_RETAIN error optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid program name defined in non-retentive program configuration."); yyerrok;}
| PROGRAM program_name optional_task_name prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "':' missing after program name or optional task name in program configuration."); yynerrs++;}
| PROGRAM RETAIN program_name optional_task_name prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locl(@4), locf(@5), "':' missing after program name or optional task name in retentive program configuration."); yynerrs++;}
| PROGRAM NON_RETAIN program_name optional_task_name prev_declared_program_type_name optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locl(@4), locf(@5), "':' missing after program name or optional task name in non-retentive program configuration."); yynerrs++;}
| PROGRAM program_name optional_task_name ':' optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locl(@4), locf(@5), "no program type defined in program configuration."); yynerrs++;}
| PROGRAM RETAIN program_name optional_task_name ':' optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locl(@5), locf(@6), "no program type defined in retentive program configuration."); yynerrs++;}
| PROGRAM NON_RETAIN program_name optional_task_name ':' optional_prog_conf_elements
  {$$ = NULL; print_err_msg(locl(@5), locf(@6), "no program type defined in non-retentive program configuration."); yynerrs++;}
/* ERROR_CHECK_END */
;

// helper symbol for program_configuration //
optional_task_name:
  // empty //
	{$$ = NULL;}
| WITH task_name
	{$$ = $2;}
/* ERROR_CHECK_BEGIN */
| WITH error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@1), locf(@2), "no task name defined in optional task name of program configuration.");}
	 else {print_err_msg(locf(@2), locl(@2), "invalid task name in optional task name of program configuration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

// helper symbol for program_configuration //
optional_prog_conf_elements:
  // empty //
	{$$ = NULL;}
| '(' prog_conf_elements ')'
	{$$ = $2;}
/* ERROR_CHECK_BEGIN */
| '(' error ')'
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid program configuration elements in program configuration."); yyerrok;}
| '(' prog_conf_elements error
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "')' missing at the end of program configuration elements in program configuration."); yyerrok;}
/* ERROR_CHECK_END */
;


prog_conf_elements:
  prog_conf_element
	{$$ = new prog_conf_elements_c(locloc(@$)); $$->add_element($1);}
| prog_conf_elements ',' prog_conf_element
	{$$ = $1; $$->add_element($3);}
/* ERROR_CHECK_BEGIN */
| prog_conf_elements prog_conf_element
  {$$ = $1; print_err_msg(locl(@1), locf(@2), "',' missing in program configuration elements list."); yynerrs++;}
| prog_conf_elements ',' error
  {$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for program configuration element in program configuration list.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value for program configuration element in program configuration list."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


prog_conf_element:
  fb_task
| prog_cnxn
;


fb_task:
  // fb_name WITH task_name
/* NOTE: The fb_name refers to funtion block variables
 *       that have been declared in a scope outside the one we are
 *       currently parsing, so we must accept them to be any_identifier!
 */
  any_identifier WITH task_name
	{$$ = new fb_task_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| any_identifier WITH error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no task name defined in function block configuration.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid task name in function block configuration."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


/* NOTE:
 *  The semantics of configuring a program are rather confusing, so here is
 *  my (Mario) understanding on the issue...
 *
 *  A function/program may have as its input variables a simple variable
 *  (BYTE, WORD, etc...), an array (ARRAY [1 .. 3] OF BYTE, ...) , or a structure.
 *  Nevertheless, when calling this function from within a st or il language statement
 *  it is not possible to allocate a value to a single element of the array or structure
 *  typed input variable, as the accepted syntax is simply '(' variable_name ':=' variable ')'
 *  Notice how the variable_name does not include things such as 'a.elem1' or 'a[1]'!
 *
 *  Nevertheless, when configuring a program from within a configuration,
 *  it becomes possible to allocate values to individual elements of the
 *  array or structured type input variable, as the syntax is now
 *  '(' symbolic_variable ':=' data_sink|prog_data_source ')'
 *  Notice how the symbolic_variable _does_ include things such as 'a.elem1' or 'a[1]'!
 *
 *  Conclusion: Unlike other locations in the syntax where SENDTO appears,
 *  here it is not valid to replace symbolic_variable with any_identifier!
 *  Nevertheless, it is also not correct to leave symbolic_variable as it is,
 *  as we have defined it to only include previously declared variables,
 *  which is not the case in this situation. Here symbolic_variable is refering
 *  to variables that were defined within the scope of the program that is being
 *  called, and _not_ within the scope of the configuration that is calling the
 *  program, so the variables in question are not declared in the current scope!
 *
 *  We therefore need to define a new symbolic_variable, that accepts any_identifier
 *  instead of previosuly declared variable names, to be used in the definition of
 *  prog_cnxn!
 */
prog_cnxn:
  any_symbolic_variable ASSIGN prog_data_source
	{$$ = new prog_cnxn_assign_c($1, $3, locloc(@$));}
| any_symbolic_variable SENDTO data_sink
	{$$ = new prog_cnxn_sendto_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| any_symbolic_variable constant
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing between parameter and value in program configuration element."); yynerrs++;}
| any_symbolic_variable enumerated_value
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing between parameter and value in program configuration element."); yynerrs++;}
| any_symbolic_variable data_sink
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' or '=>' missing between parameter and variable in program configuration element."); yynerrs++;}
| any_symbolic_variable ASSIGN error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value or variable defined in program configuration assignment element.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid value or variable in program configuration assignment element."); yyclearin;}
	 yyerrok;
	}
| any_symbolic_variable SENDTO error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no variable defined in program configuration sendto element.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid variable in program configuration sendto element."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

prog_data_source:
  constant
| enumerated_value
| global_var_reference
| prev_declared_direct_variable
;

data_sink:
  global_var_reference
| prev_declared_direct_variable
;

instance_specific_initializations:
 VAR_CONFIG instance_specific_init_list END_VAR
	{$$ = new instance_specific_initializations_c($2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| VAR_CONFIG END_VAR
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no variable declared in configuration variable(s) initialization."); yynerrs++;}
| VAR_CONFIG error instance_specific_init_list END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unexpected token after 'VAR_CONFIG' in configuration variable(s) initialization."); yyerrok;}
| VAR_CONFIG instance_specific_init_list error END_OF_INPUT
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed configuration variable(s) initialization."); yyerrok;}
| VAR_CONFIG error END_VAR
	{$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in configuration variable(s) initialization."); yyerrok;}
/* ERROR_CHECK_END */
;

// helper symbol for instance_specific_initializations //
instance_specific_init_list:
  instance_specific_init ';'
	{$$ = new instance_specific_init_list_c(locloc(@$)); $$->add_element($1);}
| instance_specific_init_list instance_specific_init ';'
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| error ';'
  {$$ = new instance_specific_init_list_c(locloc(@$)); print_err_msg(locf(@1), locl(@1), "invalid configuration variable initialization."); yyerrok;}
| instance_specific_init error
  {$$ = new instance_specific_init_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at the end of configuration variable initialization."); yyerrok;}
| instance_specific_init_list instance_specific_init error
  {$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at the end of configuration variable initialization."); yyerrok;}
| instance_specific_init_list error ';'
  {$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid configuration variable initialization."); yyerrok;}
| instance_specific_init_list ';'
  {$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after configuration variable initialization."); yynerrs++;}
/* ERROR_CHECK_END */
;


instance_specific_init:
//
//  resource_name '.' program_name '.' {fb_name '.'}
//  ((variable_name [location] ':' located_var_spec_init) | (fb_name ':' function_block_type_name ':=' structure_initialization))
//
//  prev_declared_resource_name '.' prev_declared_program_name '.' any_fb_name_list variable_name ':' located_var_spec_init
/* NOTE: variable_name has been changed to any_identifier (and not simply identifier) because the
 *       variables being referenced have been declared outside the scope currently being parsed!
 */
/* NOTE: program_name has not been changed to prev_declared_program_name because the
 *       programs being referenced have been declared outside the scope currently being parsed!
 *       The programs are only kept inside the scope of the resource in which they are defined.
 */
  prev_declared_resource_name '.' program_name '.' any_fb_name_list any_identifier ':' located_var_spec_init
	{$$ = new instance_specific_init_c($1, $3, $5, $6, NULL, $8, locloc(@$));}
| prev_declared_resource_name '.' program_name '.' any_fb_name_list any_identifier location ':' located_var_spec_init
	{$$ = new instance_specific_init_c($1, $3, $5, $6, $7, $9, locloc(@$));}
| prev_declared_resource_name '.' program_name '.' any_fb_name_list any_identifier ':' fb_initialization
	{$5->add_element($6); $$ = new instance_specific_init_c($1, $3, $5, NULL, NULL, $8, locloc(@$));}
;


/* helper symbol for instance_specific_init */
fb_initialization:
  function_block_type_name ASSIGN structure_initialization
	{$$ = new fb_initialization_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| function_block_type_name structure_initialization
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "':=' missing between function block name and initialization in function block initialization."); yynerrs++;}
| function_block_type_name ASSIGN error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no initial value defined in function block initialization.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid initial value in function block initialization."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/***********************************/
/* B 2.1 Instructions and Operands */
/***********************************/
/* helper symbol for many IL instructions, etc... */
/* eat up any extra EOL tokens... */

eol_list:
  EOL
| eol_list EOL
;



instruction_list:
  il_instruction
	{$$ = new instruction_list_c(locloc(@$)); $$->add_element($1);}
| any_pragma eol_list
	{$$ = new instruction_list_c(locloc(@1)); $$->add_element($1);} /* locloc(@1) is not a bug! We ignore trailing EOLs when determining symbol location! */
| instruction_list il_instruction
	{$$ = $1; $$->add_element($2);}
| instruction_list any_pragma
	{$$ = $1; $$->add_element($2);}
;



il_instruction:
  il_incomplete_instruction eol_list
	{$$ = new il_instruction_c(NULL, $1, locloc(@1));} /* locloc(@1) is not a bug! We ignore trailing EOLs when determining symbol location! */
| label ':' il_incomplete_instruction eol_list
	{$$ = new il_instruction_c($1, $3, locf(@1), locl(@3));} /* locf(@1), locl(@3) is not a bug! We ignore trailing EOLs when determining symbol location! */
| label ':' eol_list
	{$$ = new il_instruction_c($1, NULL, locf(@1), locl(@2));} /* locf(@1), locl(@2) is not a bug! We ignore trailing EOLs when determining symbol location! */
/* ERROR_CHECK_BEGIN */
| error eol_list
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid IL instruction."); yyerrok;}
| il_incomplete_instruction error
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "EOL missing at the end of IL instruction."); yyerrok;}
| error ':' il_incomplete_instruction eol_list
	{$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid label in IL instruction."); yyerrok;}
| label il_incomplete_instruction eol_list
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing after label in IL instruction."); yynerrs++;}
| label ':' error eol_list
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid IL instruction."); yyerrok;}
| label ':' il_incomplete_instruction error
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "EOL missing at the end of IL instruction."); yyerrok;}
/* ERROR_CHECK_END */
;


/* helper symbol for il_instruction */
il_incomplete_instruction:
  il_simple_operation
| il_expression
| il_jump_operation
| il_fb_call
| il_formal_funct_call
| il_return_operator
;


label: identifier;



il_simple_operation:
// (il_simple_operator [il_operand]) | (function_name [il_operand_list])
  il_simple_operator
	{$$ = new il_simple_operation_c($1, NULL, locloc(@$));}
/*
 * Note: Bison is getting confused with the following rule,
 *       i.e. it is finding conflicts where there seemingly are really none.
 *       The rule was therefore replaced by the equivalent following
 *       two rules.
 */
/*
| il_simple_operator il_operand
	{$$ = new il_simple_operation_c($1, $2, locloc(@$));}
*/
| il_simple_operator_noclash il_operand
	{$$ = new il_simple_operation_c($1, $2, locloc(@$));}
| il_simple_operator_clash il_operand
	{$$ = new il_simple_operation_c($1, $2, locloc(@$));}
/* NOTE: the line
 *         | il_simple_operator
 *       already contains the 'NOT' operator, as well as all the
 *       expression operators ('MOD', 'AND', etc...), all of which
 *       may also be a function name! This means that these operators/functions,
 *       without any operands, could be reduced to either an operator or a
 *       function call. 
 *
 *       I (Mario) have chosen to reduce it to an operator.
 *       In order to do this, we must remove from the syntax that defines
 *       function calls all the functions whose names clash with the IL operators.
 *
 *       The line
 *         | function_name
 *       has been replaced with the lines
 *         | function_name_no_clashes
 *       in order to include all possible function names except
 *       those whose names coincide with operators !!
 */
| function_name_no_clashes
	{$$ = new il_function_call_c($1, NULL, locloc(@$)); if (NULL == dynamic_cast<poutype_identifier_c*>($1)) ERROR;} // $1 should be a poutype_identifier_c
/* NOTE: the line
 *         | il_simple_operator il_operand
 *       already contains the 'NOT', 'MOD', etc. operators, followed by a single il_operand.
 *       However, this same code (MOD x) may also be reduced to a function call to the MOD
 *       function. This means that (MOD, AND,...) could be interpret as a function name
 *       or as an IL operator! This would lead us to a reduce/reduce conflict!
 *
 *       I (Mario) have chosen to reduce it to an operand, rather than a function call.
 *       In order to do this, we must remove from the syntax that defines
 *       function calls all the functions whose names clash with the IL operators.
 *
 *       The line
 *         | function_name il_operand_list
 *       has been replaced with the line
 *         | function_name_no_clashes il_operand_list
 *       in order to include all possible function names except
 *       for the function names which clash with expression and simple operators.
 *
 *       Note that:
 *       this alternative syntax does not cover the possibility of
 *       the function 'NOT', 'MOD', etc... being called with more than one il_operand,
 *       in which case it is always a function call, and not an IL instruction.
 *       We therefore need to include an extra rule where the
 *       function_name_expression_clashes and function_name_simpleop_clashes
 *       are followed by a il_operand_list with __two__ or more il_operands!!
 */
| function_name_no_clashes il_operand_list
	{$$ = new il_function_call_c($1, $2, locloc(@$)); if (NULL == dynamic_cast<poutype_identifier_c*>($1)) ERROR;} // $1 should be a poutype_identifier_c
| il_simple_operator_clash il_operand_list2
	{$$ = new il_function_call_c(il_operator_c_2_poutype_identifier_c($1), $2, locloc(@$));}
;



il_expression:
// il_expr_operator '(' [il_operand] EOL {EOL} [simple_instr_list] ')'
/* IMPORTANT NOTE:
 *  When the <il_operand> exists, and to make it easier to handle the <il_operand> as a general case (i.e. without C++ code handling this as a special case), 
 *  we will create an equivalent LD <il_operand> IL instruction, and prepend it into the <simple_instr_list>. 
 *  The remainder of the compiler may from now on assume that the code being compiled does not contain any IL code like
 *     LD 1
 *     ADD ( 2
 *        SUB 3
 *        )
 *
 * but instead, the equivalent code
 *     LD 1
 *     ADD ( 
 *        LD  2
 *        SUB 3
 *        )
 *
 * Note, however, that in the first case, we still store in the il_expression_c a pointer to the <il_operand> (the literal '2' in the above example), in case
 * somewhere further on in the compiler we really want to handle it as a special case. To handle it as a special case, it should be easy to simply delete the first
 * artificial entry in <simple_instr_list> with il_expression->simple_instr_list->remove_element(0)  !!
 */
/*
 * Note: Bison is getting confused with the use of il_expr_operator,
 *       i.e. it is finding conflicts where there seemingly are really none.
 *       il_expr_operator was therefore replaced by the equivalent 
 *       il_expr_operator_noclash | il_expr_operator_clash.
 */
  il_expr_operator_noclash '(' eol_list ')'
	{$$ = new il_expression_c($1, NULL, NULL, locloc(@$));}
| il_expr_operator_noclash '(' il_operand eol_list ')'
	{ simple_instr_list_c *tmp_simple_instr_list = new simple_instr_list_c(locloc(@3));
	  tmp_simple_instr_list ->insert_element(new il_simple_instruction_c(new il_simple_operation_c(new LD_operator_c(locloc(@3)), $3, locloc(@3)), locloc(@3)), 0);
	  $$ = new il_expression_c($1, $3, tmp_simple_instr_list, locloc(@$));
	}
| il_expr_operator_noclash '(' eol_list simple_instr_list ')'
	{$$ = new il_expression_c($1, NULL, $4, locloc(@$));}
| il_expr_operator_noclash '(' il_operand eol_list simple_instr_list ')'
	{ simple_instr_list_c *tmp_simple_instr_list = dynamic_cast <simple_instr_list_c *> $5;
	  tmp_simple_instr_list ->insert_element(new il_simple_instruction_c(new il_simple_operation_c(new LD_operator_c(locloc(@3)), $3, locloc(@3)), locloc(@3)), 0);
	  $$ = new il_expression_c($1, $3, $5, locloc(@$));
	}
| il_expr_operator_clash '(' eol_list ')'
	{$$ = new il_expression_c($1, NULL, NULL, locloc(@$));}
| il_expr_operator_clash '(' il_operand eol_list ')'
	{ simple_instr_list_c *tmp_simple_instr_list = new simple_instr_list_c(locloc(@3));
	  tmp_simple_instr_list ->insert_element(new il_simple_instruction_c(new il_simple_operation_c(new LD_operator_c(locloc(@3)), $3, locloc(@3)), locloc(@3)), 0);
	  $$ = new il_expression_c($1, $3, tmp_simple_instr_list, locloc(@$));
	}
| il_expr_operator_clash '(' il_operand eol_list simple_instr_list ')'
	{ simple_instr_list_c *tmp_simple_instr_list = dynamic_cast <simple_instr_list_c *> $5;
	  tmp_simple_instr_list ->insert_element(new il_simple_instruction_c(new il_simple_operation_c(new LD_operator_c(locloc(@3)), $3, locloc(@3)), locloc(@3)), 0);
	  $$ = new il_expression_c($1, $3, $5, locloc(@$));
	}
| il_expr_operator_clash_eol_list simple_instr_list ')'
	{$$ = new il_expression_c($1, NULL, $2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| il_expr_operator_noclash '(' eol_list error
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "')' missing at the end of IL expression."); yyerrok;}
| il_expr_operator_noclash '(' il_operand eol_list error
  {$$ = NULL; print_err_msg(locl(@4), locf(@5), "')' missing at the end of IL expression."); yyerrok;}
| il_expr_operator_noclash '(' eol_list simple_instr_list error
  {$$ = NULL; print_err_msg(locl(@4), locf(@5), "')' missing at the end of IL expression."); yyerrok;}
| il_expr_operator_noclash '(' il_operand eol_list simple_instr_list error
  {$$ = NULL; print_err_msg(locl(@5), locf(@6), "')' missing at the end of IL expression."); yyerrok;}
| il_expr_operator_clash '(' il_operand eol_list error
  {$$ = NULL; print_err_msg(locl(@4), locf(@5), "')' missing at the end of IL expression."); yyerrok;}
| il_expr_operator_clash '(' il_operand eol_list simple_instr_list error
  {$$ = NULL; print_err_msg(locl(@5), locf(@6), "')' missing at the end of IL expression."); yyerrok;}
| il_expr_operator_clash_eol_list simple_instr_list error
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "')' missing at the end of IL expression."); yyerrok;}
/* ERROR_CHECK_END */
;


il_jump_operation:
  il_jump_operator label
	{$$ = new il_jump_operation_c($1, $2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| il_jump_operator error
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid label defined in IL jump operation."); yyerrok;}
/* ERROR_CHECK_END */
;


il_fb_call:
// il_call_operator fb_name ['(' (EOL {EOL} [il_param_list]) | [il_operand_list] ')']
  il_call_operator prev_declared_fb_name
	{$$ = new il_fb_call_c($1, $2, NULL, NULL, locloc(@$));}
| il_call_operator prev_declared_fb_name '(' ')'
	{$$ = new il_fb_call_c($1, $2, NULL, NULL, locloc(@$));}
| il_call_operator prev_declared_fb_name '(' eol_list ')'
	{$$ = new il_fb_call_c($1, $2, NULL, NULL, locloc(@$));}
| il_call_operator prev_declared_fb_name '(' il_operand_list ')'
	{$$ = new il_fb_call_c($1, $2, $4, NULL, locloc(@$));}
| il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')'
	{$$ = new il_fb_call_c($1, $2, NULL, $5, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| il_call_operator error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@1), locf(@2), "no function block name defined in IL function block call.");}
	 else {print_err_msg(locf(@2), locl(@2), "invalid function block name in IL function block call."); yyclearin;}
	 yyerrok;
	}
| il_call_operator '(' ')'
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no function block name defined in IL function block call."); yynerrs++;}
| il_call_operator '(' eol_list ')'
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no function block name defined in IL function block call."); yynerrs++;}
| il_call_operator '(' il_operand_list ')'
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no function block name defined in IL function block call."); yynerrs++;}
| il_call_operator '(' eol_list il_param_list ')'
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no function block name defined in IL function block call."); yynerrs++;}
| il_call_operator error '(' ')'
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid function block name defined in IL function block call."); yyerrok;}
| il_call_operator error '(' eol_list ')'
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid function block name defined in IL function block call."); yyerrok;}
| il_call_operator error '(' il_operand_list ')'
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid function block name defined in IL function block call."); yyerrok;}
| il_call_operator error '(' eol_list il_param_list ')'
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid function block name defined in IL function block call."); yyerrok;}
| il_call_operator prev_declared_fb_name ')'
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "'(' missing after function block name defined in IL function block call."); yynerrs++;}
| il_call_operator prev_declared_fb_name il_operand_list ')'
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "'(' missing after function block name defined in IL function block call."); yynerrs++;}
| il_call_operator prev_declared_fb_name '(' error
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "')' missing at the end of IL function block call."); yyerrok;}
| il_call_operator prev_declared_fb_name '(' eol_list error
  {$$ = NULL; print_err_msg(locl(@4), locf(@5), "')' missing at the end of IL function block call."); yyerrok;}
| il_call_operator prev_declared_fb_name '(' il_operand_list error
  {$$ = NULL; print_err_msg(locl(@4), locf(@5), "')' missing at the end of IL function block call."); yyerrok;}
/* ERROR_CHECK_END */
;


/* NOTE: Please read note above the definition of function_name_without_clashes */
il_formal_funct_call:
// function_name '(' EOL {EOL} [il_param_list] ')'
/*  function_name '(' eol_list ')'  */
/* NOTE: il_formal_funct_call is only used in the definition of
 *         - il_incomplete_instruction
 *         - il_simple_instruction
 *       In both of the above, il_expression also
 *       shows up as another option. This means that the functions whose
 *       names clash with expressions, followed by '(' eol_list ')', are
 *       already included. We must therefore leave them out in this
 *       definition in order to remove reduce/reduce conflicts.
 *
 *       In summary: 'MOD' '(' eol_list ')', and all other functions whose
 *       names clash with expressions may be interpreted by the syntax by
 *       two different routes. I (Mario) chose to interpret them
 *       as operators, rather than as function calls!
 *       (AND MOD OR XOR ADD DIV EQ GT GE LT LE MUL NE SUB)
 */
  function_name_no_clashes '(' eol_list ')'
	{$$ = new il_formal_funct_call_c($1, NULL, locloc(@$)); if (NULL == dynamic_cast<poutype_identifier_c*>($1)) ERROR;} // $1 should be a poutype_identifier_c
| function_name_simpleop_clashes '(' eol_list ')'
	{$$ = new il_formal_funct_call_c($1, NULL, locloc(@$)); if (NULL == dynamic_cast<poutype_identifier_c*>($1)) ERROR;} // $1 should be a poutype_identifier_c
/* | function_name '(' eol_list il_param_list ')' */
/* For the above syntax, we no longer have two ways of interpreting the
 * same syntax. The above is always a function call!
 * However, some of the functions that we may be calling
 * may have the same name as an IL operator. This means that
 * flex will be parsing them and handing them over to bison as
 * IL operator tokens, and not as function name tokens.
 * (when parsing ST, flex no longer recognizes IL operators,
 * so will always return the correct function name, unless that
 * name also coincides with an operator used in ST -> XOR, OR, MOD, AND, NOT)
 *
 * We must therefore interpret the IL operators as function names!
 */
| function_name_no_clashes '(' eol_list il_param_list ')'
	{$$ = new il_formal_funct_call_c($1, $4, locloc(@$)); if (NULL == dynamic_cast<poutype_identifier_c*>($1)) ERROR;} // $1 should be a poutype_identifier_c
| function_name_simpleop_clashes '(' eol_list il_param_list ')'
	{$$ = new il_formal_funct_call_c($1, $4, locloc(@$)); if (NULL == dynamic_cast<poutype_identifier_c*>($1)) ERROR;} // $1 should be a poutype_identifier_c
/* The following line should read:
 *
 * | function_name_expression_clashes '(' eol_list il_param_list ')'
 *
 * but the function_name_expression_clashes had to be first reduced to
 * an intermediary symbol in order to remove a reduce/reduce conflict.
 * In essence, the syntax requires more than one look ahead token
 * in order to be parsed. We resolve this by reducing a collection of
 * symbols into a temporary symbol (il_expr_operator_clash_eol_list), that
 * will later be replaced by the correct symbol. The correct symbol will
 * now be determined by a single look ahead token, as all the common
 * symbols have been reduced to the temporary symbol
 * il_expr_operator_clash_eol_list !
 *
 * Unfortunately, this work around results in the wrong symbol
 * being created for the abstract syntax tree.
 * We need to figure out which symbol was created, destroy it,
 * and create the correct symbol for our case.
 * This is a lot of work, so I put it in a function
 * at the end of this file... il_operator_c_2_poutype_identifier_c()
 */
| il_expr_operator_clash_eol_list il_param_list ')'
	{$$ = new il_formal_funct_call_c(il_operator_c_2_poutype_identifier_c($1), $2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| function_name_no_clashes '(' eol_list error ')'
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid parameter list defined in IL formal function call."); yyerrok;} 
| function_name_simpleop_clashes '(' eol_list error ')'
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid parameter list defined in IL formal function call."); yyerrok;} 
| il_expr_operator_clash_eol_list error ')'
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid parameter list defined in IL formal function call."); yyerrok;} 
/* ERROR_CHECK_END */
;


il_expr_operator_clash_eol_list:
  il_expr_operator_clash '(' eol_list
	{$$ = $1;}
/* ERROR_CHECK_BEGIN */
| il_expr_operator_clash '(' error
  {$$ = $1; print_err_msg(locl(@2), locf(@3), "EOL missing after '(' in IL instruction."); yyerrok;}
/* ERROR_CHECK_END */
;


il_operand:
  variable
| enumerated_value
| constant
;


il_operand_list:
  il_operand
	{$$ = new il_operand_list_c(locloc(@$)); $$->add_element($1);}
| il_operand_list2
;


/* List with 2 or more il_operands */ 
il_operand_list2:
  il_operand ',' il_operand 
	{$$ = new il_operand_list_c(locloc(@$)); $$->add_element($1); $$->add_element($3);}
| il_operand_list2 ',' il_operand
	{$$ = $1; $$->add_element($3);}
/* ERROR_CHECK_BEGIN */
| il_operand_list2 il_operand
  {$$ = $1; print_err_msg(locl(@1), locf(@2), "',' missing in IL operand list."); yynerrs++;}
| il_operand ',' error
  {$$ = new il_operand_list_c(locloc(@$));
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no operand defined in IL operand list.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid operand name in IL operand list."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


simple_instr_list:
  il_simple_instruction
	{$$ = new simple_instr_list_c(locloc(@$)); $$->add_element($1);}
| simple_instr_list il_simple_instruction
	{$$ = $1; $$->add_element($2);}
;


il_simple_instruction:
  il_simple_operation eol_list
	{$$ = new il_simple_instruction_c($1, locloc(@1));} /* locloc(@1) is not a bug! We ignore trailing EOLs when determining symbol location! */
| il_expression eol_list
	{$$ = new il_simple_instruction_c($1, locloc(@1));} /* locloc(@1) is not a bug! We ignore trailing EOLs when determining symbol location! */
| il_formal_funct_call eol_list
	{$$ = new il_simple_instruction_c($1, locloc(@1));} /* locloc(@1) is not a bug! We ignore trailing EOLs when determining symbol location! */
/* ERROR_CHECK_BEGIN */
| il_expression error
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "EOL missing after expression IL instruction."); yyerrok;}
| il_formal_funct_call error
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "EOL missing after formal function call IL instruction."); yyerrok;}
/* ERROR_CHECK_END */
;


/* NOTE: the correct definition of il_param_list is
 * il_param_list ::= {il_param_instruction} il_param_last_instruction
 *
 * where {...} denotes zero or many il_param_instruction's.
 *
 * We could do this by defining the following:
 * il_param_list: il_param_instruction_list il_param_last_instruction;
 * il_param_instruction_list : ** empty ** | il_param_instruction_list il_param_instruction;
 *
 * Unfortunately, the above leads to reduce/reduce conflicts.
 * The chosen alternative (as follows) does not have any conflicts!
 * il_param_list: il_param_last_instruction | il_param_instruction_list il_param_last_instruction;
 * il_param_instruction_list : il_param_instruction_list | il_param_instruction_list il_param_instruction;
 */
il_param_list:
  il_param_instruction_list il_param_last_instruction
	{$$ = $1; $$->add_element($2);}
| il_param_last_instruction
	{$$ = new il_param_list_c(locloc(@$)); $$->add_element($1);}
/* ERROR_CHECK_BEGIN */
| il_param_instruction_list error
  {$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid parameter assignment in parameter assignment list."); yyerrok;}
| il_param_last_instruction il_param_last_instruction
  {$$ = new il_param_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "',' missing at the end of parameter assignment in parameter assignment list."); yynerrs++;}
| il_param_instruction_list il_param_last_instruction il_param_last_instruction
  {$$ = $1; print_err_msg(locl(@2), locf(@3), "',' missing at the end of parameter assignment in parameter assignment list."); yynerrs++;}
/* ERROR_CHECK_END */
;


/* Helper symbol for il_param_list */
il_param_instruction_list:
  il_param_instruction
	{$$ = new il_param_list_c(locloc(@$)); $$->add_element($1);}
| il_param_instruction_list il_param_instruction
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| il_param_last_instruction il_param_instruction
  {$$ = new il_param_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "',' missing at the end of parameter assignment in parameter assignment list."); yynerrs++;}
| il_param_instruction_list il_param_last_instruction il_param_instruction
  {$$ = $1; print_err_msg(locl(@2), locf(@3), "',' missing at the end of parameter assignment in parameter assignment list."); yynerrs++;}
/* ERROR_CHECK_END */
;


il_param_instruction:
  il_param_assignment ',' eol_list 
| il_param_out_assignment ',' eol_list
/* ERROR_CHECK_BEGIN */
| il_param_assignment ',' error
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "EOL missing at the end of parameter assignment in parameter assignment list."); yyerrok;}
| il_param_out_assignment ',' error
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "EOL missing at the end of parameter out assignment in parameter assignment list."); yyerrok;}
/* ERROR_CHECK_END */
;


il_param_last_instruction:
  il_param_assignment eol_list
| il_param_out_assignment eol_list
/* ERROR_CHECK_BEGIN */
| il_param_assignment error
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "EOL missing at the end of last parameter assignment in parameter assignment list."); yyerrok;}
| il_param_out_assignment error
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "EOL missing at the end of last parameter out assignment in parameter assignment list."); yyerrok;}
/* ERROR_CHECK_END */

;


il_param_assignment:
  il_assign_operator il_operand
	{$$ = new il_param_assignment_c($1, $2, NULL, locloc(@$));}
| il_assign_operator '(' eol_list simple_instr_list ')'
	{$$ = new il_param_assignment_c($1, NULL, $4, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| error il_operand
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid operator in parameter assignment."); yyerrok;}
| error '(' eol_list simple_instr_list ')'
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid operator in parameter assignment."); yyerrok;}
| il_assign_operator error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@1), locf(@2), "no operand defined in parameter assignment.");}
	 else {print_err_msg(locf(@2), locl(@2), "invalid operand in parameter assignment."); yyclearin;}
	 yyerrok;
	}
| il_assign_operator '(' eol_list ')'
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "no instruction list defined in parameter assignment."); yynerrs++;}
| il_assign_operator '(' eol_list error ')'
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid instruction list defined in parameter assignment."); yyerrok;}
| il_assign_operator '(' eol_list simple_instr_list error
  {$$ = NULL; print_err_msg(locl(@4), locf(@5), "')' missing at the end of instruction list defined in parameter assignment."); yyerrok;}
/* ERROR_CHECK_END */
;


il_param_out_assignment:
  il_assign_out_operator variable
	{$$ = new il_param_out_assignment_c($1, $2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| il_assign_out_operator error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@1), locf(@2), "no variable defined in IL operand list.");}
	 else {print_err_msg(locf(@2), locl(@2), "invalid variable in IL operand list."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;



/*******************/
/* B 2.2 Operators */
/*******************/
sendto_identifier: sendto_identifier_token {$$ = new identifier_c($1, locloc(@$));};


/* NOTE:
 *  The spec includes the operator 'EQ '
 * Note that EQ is followed by a space.
 * I am considering this a typo, and defining the operator
 * as 'EQ'
 * (Mario)
 */
LD_operator: 		LD 	{$$ = new LD_operator_c(locloc(@$));};
LDN_operator: 		LDN 	{$$ = new LDN_operator_c(locloc(@$));};
ST_operator: 		ST 	{$$ = new ST_operator_c(locloc(@$));};
STN_operator: 		STN 	{$$ = new STN_operator_c(locloc(@$));};
NOT_operator: 		NOT 	{$$ = new NOT_operator_c(locloc(@$));};
S_operator: 		S 	{$$ = new S_operator_c(locloc(@$));};
R_operator: 		R 	{$$ = new R_operator_c(locloc(@$));};
S1_operator: 		S1 	{$$ = new S1_operator_c(locloc(@$));};
R1_operator: 		R1 	{$$ = new R1_operator_c(locloc(@$));};
CLK_operator: 		CLK 	{$$ = new CLK_operator_c(locloc(@$));};
CU_operator: 		CU 	{$$ = new CU_operator_c(locloc(@$));};
CD_operator: 		CD 	{$$ = new CD_operator_c(locloc(@$));};
PV_operator: 		PV 	{$$ = new PV_operator_c(locloc(@$));};
IN_operator: 		IN 	{$$ = new IN_operator_c(locloc(@$));};
PT_operator: 		PT 	{$$ = new PT_operator_c(locloc(@$));};
AND_operator: 		AND 	{$$ = new AND_operator_c(locloc(@$));};
AND2_operator: 		AND2 	{$$ = new AND_operator_c(locloc(@$));}; /* '&' in the source code! */
OR_operator: 		OR 	{$$ = new OR_operator_c(locloc(@$));};
XOR_operator: 		XOR 	{$$ = new XOR_operator_c(locloc(@$));};
ANDN_operator: 		ANDN 	{$$ = new ANDN_operator_c(locloc(@$));};
ANDN2_operator:		ANDN2 	{$$ = new ANDN_operator_c(locloc(@$));}; /* '&N' in the source code! */
ORN_operator: 		ORN 	{$$ = new ORN_operator_c(locloc(@$));};
XORN_operator: 		XORN 	{$$ = new XORN_operator_c(locloc(@$));};
ADD_operator: 		ADD 	{$$ = new ADD_operator_c(locloc(@$));};
SUB_operator: 		SUB 	{$$ = new SUB_operator_c(locloc(@$));};
MUL_operator: 		MUL 	{$$ = new MUL_operator_c(locloc(@$));};
DIV_operator: 		DIV 	{$$ = new DIV_operator_c(locloc(@$));};
MOD_operator: 		MOD 	{$$ = new MOD_operator_c(locloc(@$));};
GT_operator: 		GT 	{$$ = new GT_operator_c(locloc(@$));};
GE_operator: 		GE 	{$$ = new GE_operator_c(locloc(@$));};
EQ_operator: 		EQ 	{$$ = new EQ_operator_c(locloc(@$));};
LT_operator: 		LT 	{$$ = new LT_operator_c(locloc(@$));};
LE_operator: 		LE 	{$$ = new LE_operator_c(locloc(@$));};
NE_operator: 		NE 	{$$ = new NE_operator_c(locloc(@$));};
CAL_operator: 		CAL 	{$$ = new CAL_operator_c(locloc(@$));};
CALC_operator: 		CALC 	{$$ = new CALC_operator_c(locloc(@$));};
CALCN_operator: 	CALCN 	{$$ = new CALCN_operator_c(locloc(@$));};
RET_operator: 		RET 	{$$ = new RET_operator_c(locloc(@$));};
RETC_operator: 		RETC 	{$$ = new RETC_operator_c(locloc(@$));};
RETCN_operator: 	RETCN 	{$$ = new RETCN_operator_c(locloc(@$));};
JMP_operator: 		JMP 	{$$ = new JMP_operator_c(locloc(@$));};
JMPC_operator: 		JMPC 	{$$ = new JMPC_operator_c(locloc(@$));};
JMPCN_operator: 	JMPCN 	{$$ = new JMPCN_operator_c(locloc(@$));};


il_simple_operator:
  il_simple_operator_clash
| il_simple_operator_noclash
;


il_simple_operator_noclash:
  LDN_operator
| ST_operator
| STN_operator
| il_expr_operator_noclash
;


il_simple_operator_clash:
  il_simple_operator_clash1
| il_simple_operator_clash2
| il_simple_operator_clash3
;

il_simple_operator_clash1:
  NOT_operator
;

il_simple_operator_clash2:
  il_expr_operator_clash
;

il_simple_operator_clash3:
  LD_operator
| S_operator
| R_operator
| S1_operator
| R1_operator
| CLK_operator
| CU_operator
| CD_operator
| PV_operator
| IN_operator
| PT_operator
;

/*
il_expr_operator:
  il_expr_operator_noclash
| il_expr_operator_clash
;
*/

il_expr_operator_clash:
  AND_operator
| OR_operator
| XOR_operator
| ADD_operator
| SUB_operator
| MUL_operator
| DIV_operator
| MOD_operator
| GT_operator
| GE_operator
| EQ_operator
| LT_operator
| LE_operator
| NE_operator
;


il_expr_operator_noclash:
  ANDN_operator
| ANDN2_operator  /* string '&N' in source code! */
| AND2_operator  /* string '&' in source code! */
| ORN_operator
| XORN_operator
;




il_assign_operator:
/*  variable_name ASSIGN */
  any_identifier ASSIGN
	{$$ = new il_assign_operator_c($1, locloc(@$));}
| en_identifier ASSIGN
	{$$ = new il_assign_operator_c($1, locloc(@$));}
| S1_operator ASSIGN
	{$$ = new il_assign_operator_c(il_operator_c_2_identifier_c($1), locloc(@$));}
| R1_operator ASSIGN
	{$$ = new il_assign_operator_c(il_operator_c_2_identifier_c($1), locloc(@$));}
| CLK_operator ASSIGN
	{$$ = new il_assign_operator_c(il_operator_c_2_identifier_c($1), locloc(@$));}
| CU_operator ASSIGN
	{$$ = new il_assign_operator_c(il_operator_c_2_identifier_c($1), locloc(@$));}
| CD_operator ASSIGN
	{$$ = new il_assign_operator_c(il_operator_c_2_identifier_c($1), locloc(@$));}
| PV_operator ASSIGN
	{$$ = new il_assign_operator_c(il_operator_c_2_identifier_c($1), locloc(@$));}
| IN_operator ASSIGN
	{$$ = new il_assign_operator_c(il_operator_c_2_identifier_c($1), locloc(@$));}
| PT_operator ASSIGN
	{$$ = new il_assign_operator_c(il_operator_c_2_identifier_c($1), locloc(@$));}
/* ERROR_CHECK_BEGIN */
| error ASSIGN
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid parameter defined in parameter assignment."); yyerrok;}
/* ERROR_CHECK_END */
;


il_assign_out_operator:
/*  variable_name SENDTO */
/*  any_identifier SENDTO */
  sendto_identifier SENDTO
	{$$ = new il_assign_out_operator_c(NULL, $1, locloc(@$));}
/* The following is not required, as the sendto_identifier_token returned by flex will 
 * also include the 'ENO' identifier.
 * The resulting abstract syntax tree is identical with or without this following rule,
 * as both the eno_identifier and the sendto_identifier are stored as
 * an identifier_c !!
 *
 * To understand why we must even explicitly consider the use of ENO here,  
 * please read the comment above the definition of 'variable' in section B1.4 for details.
 */
/*
| eno_identifier SENDTO
	{$$ = new il_assign_out_operator_c(NULL, $1, locloc(@$));}
*/
/*| NOT variable_name SENDTO */
| NOT sendto_identifier SENDTO
	{$$ = new il_assign_out_operator_c(new not_paramassign_c(locloc(@1)), $2, locloc(@$));}
/* The following is not required, as the sendto_identifier_token returned by flex will 
 * also include the 'ENO' identifier.
 * The resulting abstract syntax tree is identical with or without this following rule,
 * as both the eno_identifier and the sendto_identifier are stored as
 * an identifier_c !!
 *
 * To understand why we must even explicitly consider the use of ENO here,  
 * please read the comment above the definition of 'variable' in section B1.4 for details.
 *
 * NOTE: Removing the following rule also removes a shift/reduce conflict from the parser.
 *       This conflict is not really an error/ambiguity in the syntax, but rather
 *       due to the fact that more than a single look-ahead token would be required
 *       to correctly parse the syntax, something that bison does not support.
 *
 *       The shift/reduce conflict arises because bison does not know whether
 *       to parse the 'NOT ENO' in the following code
 *         LD 1
 *         funct_name (
 *                      NOT ENO => bool_var,
 *                      EN := TRUE
 *                    )
 *        as either a il_param_assignment (wrong!) or an il_param_out_assignment.(correct).
 *        The '=>' delimiter (known as SEND_TO in this iec.y file) is a dead giveaway that 
 *        it should be parsed as an il_param_out_assignment, but still, bison gets confused!
 *        Bison considers the possibility of reducing the 'NOT ENO' as an NOT_operator with
 *        the 'ENO' operand
 *        (NOT_operator -> il_simple_operator -> il_simple_operation -> il_simple_instruction ->
 *          -> simple_instr_list -> il_param_assignment)
 *        instead of reducing it to an il_param_out_operator.
 *        ( il_param_out_operator -> il_param_out_assignment)
 *
 *        Note that the shift/reduce conflict only manifests itself in the il_formal_funct_call,
 *        where both the il_param_out_assignment and il_param_assignment are used!
 * 
 *          il_param_out_assignment --+--> il_param_instruction -> il_param_instruction_list --+
 *                                    |                                                        |
 *          il_param_assignment     --+                                                        |
 *                                                                                             |
 *                                                     il_formal_funct_call <- il_param_list <-+
 *
 */
/*
| NOT eno_identifier SENDTO
	{$$ = new il_assign_out_operator_c(new not_paramassign_c(locloc(@1)), $2, locloc(@$));}
*/
/* ERROR_CHECK_BEGIN */
| error SENDTO
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid parameter defined in parameter out assignment."); yyerrok;}
| NOT SENDTO
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no parameter defined in parameter out assignment."); yynerrs++;}
| NOT error SENDTO
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid parameter defined in parameter out assignment."); yyerrok;}
/* ERROR_CHECK_END */
;


il_call_operator:
  CAL_operator
| CALC_operator
| CALCN_operator
;


il_return_operator:
  RET_operator
| RETC_operator
| RETCN_operator
;


il_jump_operator:
  JMP_operator
| JMPC_operator
| JMPCN_operator
;


/***********************/
/* B 3.1 - Expressions */
/***********************/
expression:
  xor_expression
| ref_expression    /* an extension to the IEC 61131-3 v2 standard, based on the IEC 61131-3 v3 standard */ 
| deref_expression  /* an extension to the IEC 61131-3 v2 standard, based on the IEC 61131-3 v3 standard */ 
| expression OR xor_expression
	{$$ = new or_expression_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| expression OR error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after 'OR' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after 'OR' in ST expression."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/*  REF(var_name) */
/*  This is an extension to the IEC 61131-3 standard. It is actually defined in the IEC 61131-3 v3 standard */
/*  The REF() operator returns the adrress of the variable. Basically, it returns a pointer to the variable */
ref_expression:
  REF '(' symbolic_variable ')'
	{$$ = new ref_expression_c($3, locloc(@$));}
;

/*  DREF(var_name) */
/*  This is an extension to the IEC 61131-3 standard. It is actually defined in the IEC 61131-3 v3 standard */
/*  The DREF() operator accesses the variable stored in the specified address. Basically, it dereferences a pointer to the variable */
deref_expression:
  DREF '(' symbolic_variable ')'
	{$$ = new deref_expression_c($3, locloc(@$));}
;

xor_expression:
  and_expression
| xor_expression XOR and_expression
	{$$ = new xor_expression_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| xor_expression XOR error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after 'XOR' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after 'XOR' in ST expression."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

and_expression:
  comparison
| and_expression '&' comparison
	{$$ = new and_expression_c($1, $3, locloc(@$));}
| and_expression AND comparison
	{$$ = new and_expression_c($1, $3, locloc(@$));}
/* NOTE: The lexical parser never returns the token '&'.
 *       The '&' string is interpreted by the lexcial parser as the token
 *       AND2!
 *       This means that the first rule with '&' is actually not required,
 *       but we leave it in nevertheless just in case we later decide
 *       to remove the AND2 token...
 */
| and_expression AND2 comparison
	{$$ = new and_expression_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| and_expression '&' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '&' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '&' in ST expression."); yyclearin;}
	 yyerrok;
	}
| and_expression AND error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after 'AND' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after 'AND' in ST expression."); yyclearin;}
	 yyerrok;
	}
| and_expression AND2 error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '&' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '&' in ST expression."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

comparison:
  equ_expression
| comparison '=' equ_expression
	{$$ = new equ_expression_c($1, $3, locloc(@$));}
| comparison OPER_NE equ_expression
	{$$ = new notequ_expression_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| comparison '=' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '=' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '=' in ST expression."); yyclearin;}
	 yyerrok;
	}
| comparison OPER_NE error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '<>' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '<>' in ST expression."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

equ_expression:
  add_expression
| equ_expression '<' add_expression
	{$$ = new lt_expression_c($1, $3, locloc(@$));}
| equ_expression '>' add_expression
	{$$ = new gt_expression_c($1, $3, locloc(@$));}
| equ_expression OPER_LE add_expression
	{$$ = new le_expression_c($1, $3, locloc(@$));}
| equ_expression OPER_GE add_expression
	{$$ = new ge_expression_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| equ_expression '<' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '<' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '<' in ST expression."); yyclearin;}
	 yyerrok;
	}
| equ_expression '>' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '>' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '>' in ST expression."); yyclearin;}
	 yyerrok;
	}
| equ_expression OPER_LE error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '<=' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '<=' in ST expression."); yyclearin;}
	 yyerrok;
	}
| equ_expression OPER_GE error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '>=' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '>=' in ST expression."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/* Not required...
comparison_operator: '<' | '>' | '>=' '<='
*/

add_expression:
  term
| add_expression '+' term
	{$$ = new add_expression_c($1, $3, locloc(@$));}
| add_expression '-' term
	{$$ = new sub_expression_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| add_expression '+' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '+' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '+' in ST expression."); yyclearin;}
	 yyerrok;
	}
| add_expression '-' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '-' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '-' in ST expression."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/* Not required...
add_operator: '+' | '-'
*/

term:
  power_expression
| term '*' power_expression
	{$$ = new mul_expression_c($1, $3, locloc(@$));}
| term '/' power_expression
	{$$ = new div_expression_c($1, $3, locloc(@$));}
| term MOD power_expression
	{$$ = new mod_expression_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| term '*' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '*' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '*' in ST expression."); yyclearin;}
	 yyerrok;
	}
| term '/' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '/' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '/' in ST expression."); yyclearin;}
	 yyerrok;
	}
| term MOD error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after 'MOD' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after 'MOD' in ST expression."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/* Not required...
multiply_operator: '*' | '/' | 'MOD'
*/

power_expression:
  unary_expression
| power_expression OPER_EXP unary_expression
	{$$ = new power_expression_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| power_expression OPER_EXP error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after '**' in ST expression.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after '**' in ST expression."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


unary_expression:
  primary_expression
| '-' non_int_or_real_primary_expression
	{$$ = new neg_expression_c($2, locloc(@$));}
| NOT primary_expression
	{$$ = new not_expression_c($2, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| '-' error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@1), locf(@2), "no expression defined after '-' in ST expression.");}
	 else {print_err_msg(locf(@2), locl(@2), "invalid expression after '-' in ST expression."); yyclearin;}
	 yyerrok;
	}
| NOT error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@1), locf(@2), "no expression defined after 'NOT' in ST expression.");}
	 else {print_err_msg(locf(@2), locl(@2), "invalid expression after 'NOT' in ST expression."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/* Not required...
unary_operator: '-' | 'NOT'
*/


/* NOTE: using constant as a possible symbol for primary_expression
 *       leads to a reduce/reduce conflict.
 *
 *       The text '-9' may be parsed as either a
 *       expression<-primary_expression<-constant<-signed_integer
 *       (i.e. the constant 9 negative)
 *       OR
 *       expression<-unary_expression<-constant<-integer
 *       (i.e. the constant 9, preceded by a unary negation)
 *
 *       To remove the conflict, we only allow constants without
 *       integer or reals that are not preceded by a sign 
 *       (i.e. a '-' or '+' character) to be used in primary_expression
 *       (i.e. as a parameter to the unary negation operator)
 *
 *       e.g.  '-42', '+54', '42', '54' are all allowed in primary expression
 *       according to the standard. However, we will allow only '-42' and '+54'
 *       to be used as an argument to the negation operator ('-').
 */
/* NOTE: Notice that the standard considers the following syntax correct:
 *         VAR intv: INT; END_VAR
 *         intv :=      42;         <----- OK
 *         intv :=     -42;         <----- OK
 *         intv :=     +42;         <----- OK
 *         intv :=    --42;         <----- OK!!
 *         intv :=    -+42;         <----- OK!!
 *         intv :=  -(--42);        <----- OK!!
 *         intv :=  -(-+42);        <----- OK!!
 *         intv :=-(-(--42));       <----- OK!!
 *         intv :=-(-(-+42));       <----- OK!!
 *     but does NOT allow the following syntax:
 *         VAR intv: INT; END_VAR
 *         intv :=   ---42;       <----- ERROR!!
 *         intv :=   --+42;       <----- ERROR!!
 *         intv :=  ----42;       <----- ERROR!!
 *         intv :=  ---+42;       <----- ERROR!!
 *
 *    Although strange, we follow the standard to the letter, and do exactly
 *    as stated above!!
 */
/* NOTE: We use enumerated_value_without_identifier instead of enumerated_value
 *       in order to remove a reduce/reduce conflict between reducing an
 *       identifier to a variable or an enumerated_value.
 *
 *       This change follows the IEC specification. The specification seems to
 *       imply (by introducing syntax that allows to unambiguosly reference an
 *       enumerated value - enum_type#enum_value) that in case the same identifier is used
 *       for a variable and an enumerated value, then the variable shall be
 *       considered.
 */
non_int_or_real_primary_expression:
  non_int_or_real_constant
//| enumerated_value_without_identifier
| enumerated_value
| variable
| '(' expression ')'
	{$$ = $2;}
|  function_invocation
/* ERROR_CHECK_BEGIN */
| '(' expression error
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "')' missing at the end of expression in ST expression."); yyerrok;}
/* ERROR_CHECK_END */
;


primary_expression:
  constant
//| enumerated_value_without_identifier
| enumerated_value
| variable
| '(' expression ')'
	{$$ = $2;}
|  function_invocation
/* ERROR_CHECK_BEGIN */
| '(' expression error
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "')' missing at the end of expression in ST expression."); yyerrok;}
/* ERROR_CHECK_END */
;



/* intermediate helper symbol for primary_expression */
/* NOTE: function_name includes the standard function name 'NOT' !
 *       This introduces a reduce/reduce conflict, as NOT(var)
 *       may be parsed as either a function_invocation, or a
 *       unary_expression.
 *
 *       I (Mario) have opted to remove the possible reduction
 *       to function invocation, which means replacing the rule
 *           function_name '(' param_assignment_list ')'
 *       with
 *           function_name_no_NOT_clashes '(' param_assignment_list ')'
 *
 *       Notice how the new rule does not include the situation where
 *       the function NOT is called with more than one parameter, which
 *       the original rule does include! Callinf the NOT function with more
 *       than one argument is probably a semantic error anyway, so it
 *       doesn't make much sense to take it into account.
 *
 *       Nevertheless, if we were to to it entirely correctly,
 *       leaving the semantic checks for the next compiler stage,
 *       this syntax parser would need to include such a possibility.
 *
 *       We will leave this out for now. No need to complicate the syntax
 *       more than the specification does by contradicting itself, and
 *       letting names clash!
 */
function_invocation:
/*  function_name '(' [param_assignment_list] ')' */
  function_name_no_NOT_clashes '(' param_assignment_formal_list ')'
	{$$ = new function_invocation_c($1, $3, NULL, locloc(@$)); if (NULL == dynamic_cast<poutype_identifier_c*>($1)) ERROR;} // $1 should be a poutype_identifier_c
| function_name_no_NOT_clashes '(' param_assignment_nonformal_list ')'
	{$$ = new function_invocation_c($1, NULL, $3, locloc(@$)); if (NULL == dynamic_cast<poutype_identifier_c*>($1)) ERROR;} // $1 should be a poutype_identifier_c
| function_name_no_NOT_clashes '(' ')'
	{if (NULL == dynamic_cast<poutype_identifier_c*>($1)) ERROR; // $1 should be a poutype_identifier_c
	 if (runtime_options.allow_missing_var_in)
		{$$ = new function_invocation_c($1, NULL, NULL, locloc(@$));}
	 else
		{$$ = NULL; print_err_msg(locl(@2), locf(@3), "no parameter defined in function invocation of ST expression."); yynerrs++;}
	}
/* ERROR_CHECK_BEGIN */ 
| function_name_no_NOT_clashes param_assignment_formal_list ')'
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "'(' missing after function name in ST expression."); yynerrs++;}
| function_name_no_NOT_clashes '(' error ')'
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid parameter(s) defined in function invocation of ST expression."); yyerrok;}
| function_name_no_NOT_clashes '(' param_assignment_formal_list error
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "')' missing at the end of function invocation in ST expression."); yyerrok;}
| function_name_no_NOT_clashes '(' param_assignment_nonformal_list error
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "')' missing at the end of function invocation in ST expression."); yyerrok;}
/* ERROR_CHECK_END */
;


/********************/
/* B 3.2 Statements */
/********************/
statement_list:
  statement ';'
	{$$ = new statement_list_c(locloc(@$)); $$->add_element($1);}
| any_pragma
	{$$ = new statement_list_c(locloc(@$)); $$->add_element($1);}
| statement_list statement ';'
	{$$ = $1; $$->add_element($2);}
| statement_list any_pragma
	{$$ = $1; $$->add_element($2);}
/* ERROR_CHECK_BEGIN */
| statement error
	{$$ = new statement_list_c(locloc(@$)); print_err_msg(locl(@1), locf(@2), "';' missing at the end of statement in ST statement."); yyerrok;}
| statement_list statement error
	{$$ = $1; print_err_msg(locl(@2), locf(@3), "';' missing at the end of statement in ST statement."); yyerrok;}
| statement_list error ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "invalid statement in ST statement."); yyerrok;}
| statement_list ';'
	{$$ = $1; print_err_msg(locf(@2), locl(@2), "unexpected ';' after statement in ST statement."); yynerrs++;}
/* ERROR_CHECK_END */
;


statement:
  assignment_statement
| subprogram_control_statement
| selection_statement
| iteration_statement
| function_invocation 
	{ /* This is a non-standard extension (calling a function outside an ST expression!) */
	  /* Only allow this if command line option has been selected...                     */
	  $$ = $1; 
	  if (!runtime_options.allow_void_datatype) {
	    print_err_msg(locf(@1), locl(@1), "Function invocation in ST code is not allowed outside an expression. To allow this non-standard syntax, activate the apropriate command line option."); 
	    yynerrs++;
	  }
	}  
;


/*********************************/
/* B 3.2.1 Assignment Statements */
/*********************************/
assignment_statement:
  variable ASSIGN expression
	{$$ = new assignment_statement_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| error ASSIGN expression
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid variable before ':=' in ST assignment statement."); yyerrok;}
| variable ASSIGN error
	{$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined after ':=' in ST assignment statement.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression after ':=' in ST assignment statement."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;




/*****************************************/
/* B 3.2.2 Subprogram Control Statements */
/*****************************************/
subprogram_control_statement:
  fb_invocation
| return_statement
;

return_statement:
  RETURN	{$$ = new return_statement_c(locloc(@$));}
;



fb_invocation:
  prev_declared_fb_name '(' ')'
	{$$ = new fb_invocation_c($1, NULL, NULL, locloc(@$));	}
| prev_declared_fb_name '(' param_assignment_formal_list ')'
	{$$ = new fb_invocation_c($1, $3, NULL, locloc(@$));}
| prev_declared_fb_name '(' param_assignment_nonformal_list ')'
	{$$ = new fb_invocation_c($1, NULL, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| prev_declared_fb_name ')'
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'(' missing after function block name in ST statement."); yynerrs++;}
| prev_declared_fb_name param_assignment_formal_list ')'
	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'(' missing after function block name in ST statement."); yynerrs++;}
| prev_declared_fb_name '(' error ')'
	{$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid parameter list in function block invocation in ST statement."); yyerrok;}
| prev_declared_fb_name '(' error
	{$$ = NULL; print_err_msg(locl(@2), locf(@3), "')' missing after parameter list of function block invocation in ST statement."); yyerrok;}
| prev_declared_fb_name '(' param_assignment_formal_list error
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "')' missing after parameter list of function block invocation in ST statement."); yyerrok;}
| prev_declared_fb_name '(' param_assignment_nonformal_list error
	{$$ = NULL; print_err_msg(locl(@3), locf(@4), "')' missing after parameter list of function block invocation in ST statement."); yyerrok;}
/* ERROR_CHECK_END */
;


/* helper symbol for
 * - fb_invocation
 * - function_invocation
 */
param_assignment_formal_list:
  param_assignment_formal
	{$$ = new param_assignment_list_c(locloc(@$)); $$->add_element($1);}
| param_assignment_formal_list ',' param_assignment_formal
	{$$ = $1; $$->add_element($3);}
/* ERROR_CHECK_BEGIN */
| param_assignment_formal_list ',' error
  {$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no parameter assignment defined in ST parameter assignment list.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid parameter assignment in ST parameter assignment list."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;

/* helper symbol for
 * - fb_invocation
 * - function_invocation
 */
param_assignment_nonformal_list:
  param_assignment_nonformal
	{$$ = new param_assignment_list_c(locloc(@$)); $$->add_element($1);}
| param_assignment_nonformal_list ',' param_assignment_nonformal
	{$$ = $1; $$->add_element($3);}
/* ERROR_CHECK_BEGIN */
| param_assignment_nonformal_list ',' error
  {$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no parameter assignment defined in ST parameter assignment list.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid parameter assignment in ST parameter assignment list."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


/* NOTE: According to the IEC 61131-3 standard, there are two possible
 *       syntaxes for calling function blocks within ST.
 *       The formal method has the form:
 *        fb ( invar := x, inoutvar := var1, outvar => var2);
 *       The non-formal method has the form:
 *        fb (x, var1, var2);
 *       In the text of IEC 61131-3 (where the semantics are defined),
 *       it is obvious that mixing the two syntaxes is considered incorrect.
 *       The following should therefore be incorrect: 
 *        fb ( invar := x, var1, var2);
 *       However, according to the syntax definition, as defined in IEC 61131-3,
 *       mixing the formal and non-formal methods of invocation is allowed.
 *       We have two alternatives:
 *        (a) implement the syntax here in iec.y according to the standard,
 *            and leave it to the semantic analyser stage to find this error
 *        (b) or implement the syntax in iec.y correctly, not allowing 
 *            the mixing of formal and non-formal invocation syntaxes.
 *       Considering that this is a syntax issue, and not semantic issue,
 *       I (Mario) have decided to go with alternative (a).
 *       In other words, in iec.y we do not follow the syntax as defined in
 *       Annex B of the IEC 61131-3 standard, but rather implement
 *       the syntax also taking into account the textual part of the standard too.
 */
/*
param_assignment:
  variable_name ASSIGN expression 
*/
param_assignment_nonformal:
  expression
;


param_assignment_formal:
  any_identifier ASSIGN expression
	{$$ = new input_variable_param_assignment_c($1, $3, locloc(@$));}
| en_identifier ASSIGN expression
	{$$ = new input_variable_param_assignment_c($1, $3, locloc(@$));}
/*| variable_name SENDTO variable */
/*| any_identifier SENDTO variable */
| sendto_identifier SENDTO variable
	{$$ = new output_variable_param_assignment_c(NULL, $1, $3, locloc(@$));}
/* The following is not required, as the sendto_identifier_token returned by flex will 
 * also include the 'ENO' identifier.
 * The resulting abstract syntax tree is identical with or without this following rule,
 * as both the eno_identifier and the sendto_identifier are stored as
 * an identifier_c !!
 *
 * To understand why we must even explicitly consider the use of ENO here,  
 * please read the comment above the definition of 'variable' in section B1.4 for details.
 */
/*
| eno_identifier SENDTO variable
	{$$ = new output_variable_param_assignment_c(NULL, $1, $3, locloc(@$));}
*/
/*| NOT variable_name SENDTO variable */
/*| NOT any_identifier SENDTO variable*/
| NOT sendto_identifier SENDTO variable
	{$$ = new output_variable_param_assignment_c(new not_paramassign_c(locloc(@$)), $2, $4, locloc(@$));}
/* The following is not required, as the sendto_identifier_token returned by flex will 
 * also include the 'ENO' identifier.
 * The resulting abstract syntax tree is identical with or without this following rule,
 * as both the eno_identifier and the sendto_identifier are stored as
 * an identifier_c !!
 *
 * To understand why we must even explicitly consider the use of ENO here,  
 * please read the comment above the definition of 'variable' in section B1.4 for details.
 */
/*
| NOT eno_identifier SENDTO variable
	{$$ = new output_variable_param_assignment_c(new not_paramassign_c(locloc(@$)), $2, $4, locloc(@$));}
*/
/* ERROR_CHECK_BEGIN */
| any_identifier ASSIGN error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined in ST formal parameter assignment.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression in ST formal parameter assignment."); yyclearin;}
	 yyerrok;
	}
| en_identifier ASSIGN error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined in ST formal parameter assignment.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression in ST formal parameter assignment."); yyclearin;}
	 yyerrok;
	}
| sendto_identifier SENDTO error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined in ST formal parameter out assignment.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression in ST formal parameter out assignment."); yyclearin;}
	 yyerrok;
	}
/*
| eno_identifier SENDTO error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no expression defined in ST formal parameter out assignment.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid expression in ST formal parameter out assignment."); yyclearin;}
	 yyerrok;
	}
*/
| NOT SENDTO variable
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no parameter name defined in ST formal parameter out negated assignment."); yynerrs++;}
| NOT error SENDTO variable
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid parameter name defined in ST formal parameter out negated assignment."); yyerrok;}
| NOT sendto_identifier SENDTO error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@3), locf(@4), "no expression defined in ST formal parameter out negated assignment.");}
	 else {print_err_msg(locf(@4), locl(@4), "invalid expression in ST formal parameter out negated assignment."); yyclearin;}
	 yyerrok;
	}
/*
| NOT eno_identifier SENDTO error
  {$$ = NULL;
	 if (is_current_syntax_token()) {print_err_msg(locl(@3), locf(@4), "no expression defined in ST formal parameter out negated assignment.");}
	 else {print_err_msg(locf(@4), locl(@4), "invalid expression in ST formal parameter out negated assignment."); yyclearin;}
	 yyerrok;
	}
*/
/* ERROR_CHECK_END */
;





/********************************/
/* B 3.2.3 Selection Statements */
/********************************/
selection_statement:
  if_statement
| case_statement
;


if_statement:
  IF expression THEN statement_list elseif_statement_list END_IF
	{$$ = new if_statement_c($2, $4, $5, NULL, locloc(@$));}
| IF expression THEN statement_list elseif_statement_list ELSE statement_list END_IF
	{$$ = new if_statement_c($2, $4, $5, $7, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| IF THEN statement_list elseif_statement_list END_IF
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no test expression defined in ST 'IF' statement."); yynerrs++;}
| IF THEN statement_list elseif_statement_list ELSE statement_list END_IF
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no test expression defined in ST 'IF' statement."); yynerrs++;}
| IF error THEN statement_list elseif_statement_list END_IF
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid test expression defined for ST 'IF' statement."); yyerrok;}
| IF error THEN statement_list elseif_statement_list ELSE statement_list END_IF
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid test expression defined for ST 'IF' statement."); yyerrok;}
| IF expression error statement_list elseif_statement_list END_IF
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "expecting 'THEN' after test expression in ST 'IF' statement."); yyerrok;}
| IF expression error statement_list elseif_statement_list ELSE statement_list END_IF
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "expecting 'THEN' after test expression in ST 'IF' statement."); yyerrok;}
| IF expression THEN elseif_statement_list END_IF
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "no statement defined after 'THEN' in ST 'IF' statement."); yynerrs++;}
| IF expression THEN elseif_statement_list ELSE statement_list END_IF
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "no statement defined after 'THEN' in ST 'IF' statement."); yynerrs++;}
| IF expression THEN statement_list elseif_statement_list ELSE END_IF
  {$$ = NULL; print_err_msg(locl(@6), locf(@7), "no statement defined after 'ELSE' in ST 'IF' statement."); yynerrs++;}
| IF expression THEN statement_list elseif_statement_list ELSE error END_IF
  {$$ = NULL; print_err_msg(locf(@7), locl(@7), "invalid statement defined after 'ELSE' in ST 'IF' statement."); yynerrs++; yyerrok;}
| IF expression error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed 'IF' statement in ST."); yyerrok;}
| IF expression THEN statement_list elseif_statement_list END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@3), "unclosed 'IF' statement in ST."); yynerrs++;}
| IF expression THEN statement_list elseif_statement_list ELSE statement_list END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@3), "unclosed 'IF' statement in ST."); yynerrs++;}
| IF error END_IF
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in ST 'IF' statement."); yyerrok;}
/* ERROR_CHECK_END */
;

/* helper symbol for if_statement */
elseif_statement_list:
  /* empty */
	{$$ = new elseif_statement_list_c(locloc(@$));}
| elseif_statement_list elseif_statement
	{$$ = $1; $$->add_element($2);}
;

/* helper symbol for elseif_statement_list */
elseif_statement:
  ELSIF expression THEN statement_list
	{$$ = new elseif_statement_c($2, $4, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| ELSIF THEN statement_list
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no test expression defined for 'ELSEIF' statement in ST 'IF' statement."); yynerrs++;}
| ELSIF error THEN statement_list
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid test expression defined for 'ELSEIF' statement in ST 'IF' statement."); yyerrok;}
| ELSIF expression error statement_list
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "expecting 'THEN' after test expression in 'ELSEIF' statement of ST 'IF' statement."); yyerrok;}
| ELSIF expression THEN error
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid statement list in 'ELSEIF' statement of ST 'IF' statement."); yyerrok;}
/* ERROR_CHECK_END */
;


case_statement:
  CASE expression OF case_element_list END_CASE
	{$$ = new case_statement_c($2, $4, NULL, locloc(@$));}
| CASE expression OF case_element_list ELSE statement_list END_CASE
	{$$ = new case_statement_c($2, $4, $6, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| CASE OF case_element_list END_CASE
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no test expression defined in ST 'CASE' statement."); yynerrs++;}
| CASE OF case_element_list ELSE statement_list END_CASE
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no test expression defined in ST 'CASE' statement."); yynerrs++;}
| CASE error OF case_element_list END_CASE
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid test expression defined for ST 'CASE' statement."); yyerrok;}
| CASE error OF case_element_list ELSE statement_list END_CASE
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid test expression defined for ST 'CASE' statement."); yyerrok;}
| CASE expression error case_element_list END_CASE
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "expecting 'OF' after test expression in ST 'CASE' statement."); yyerrok;}
| CASE expression error case_element_list ELSE statement_list END_CASE
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "expecting 'OF' after test expression in ST 'CASE' statement."); yyerrok;}
| CASE expression OF END_CASE
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "no case element(s) defined after 'OF' in ST 'CASE' statement."); yynerrs++;}
| CASE expression OF ELSE statement_list END_CASE
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "no case element(s) defined after 'OF' in ST 'CASE' statement."); yynerrs++;}
| CASE expression OF error END_CASE
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid case element(s) defined after 'OF' in ST 'CASE' statement."); yyerrok;}
| CASE expression OF error ELSE statement_list END_CASE
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid case element(s) defined after 'OF' in ST 'CASE' statement."); yyerrok;}
| CASE expression OF case_element_list ELSE END_CASE
  {$$ = NULL; print_err_msg(locl(@5), locf(@6), "no statement defined after 'ELSE' in ST 'CASE' statement."); yynerrs++;}
| CASE expression OF case_element_list ELSE error END_CASE
  {$$ = NULL; print_err_msg(locf(@6), locl(@6), "invalid statement defined after 'ELSE' in ST 'CASE' statement."); yyerrok;}
| CASE expression error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@2), "unclosed 'CASE' statement in ST."); yyerrok;}
| CASE expression OF case_element_list END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@3), "unclosed 'CASE' statement in ST."); yynerrs++;}
| CASE expression OF case_element_list ELSE statement_list END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@3), "unclosed 'CASE' statement in ST."); yynerrs++;}
| CASE error END_CASE
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in ST 'CASE' statement."); yyerrok;}
/* ERROR_CHECK_END */
;


/* helper symbol for case_statement */
case_element_list:
  case_element
	{$$ = new case_element_list_c(locloc(@$)); $$->add_element($1);}
| case_element_list case_element
	{$$ = $1; $$->add_element($2);}
;


case_element:
  case_list ':' statement_list
	{$$ = new case_element_c($1, $3, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| case_list statement_list
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "':' missing after case list in ST 'CASE' statement."); yynerrs++;}
| case_list ':' error
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid statement in case element of ST 'CASE' statement."); yyerrok;}
/* ERROR_CHECK_END */
;


case_list:
  case_list_element
	{$$ = new case_list_c(locloc(@$)); $$->add_element($1);}
| case_list ',' case_list_element
	{$$ = $1; $$->add_element($3);}
/* ERROR_CHECK_BEGIN */
| case_list ',' error
  {$$ = $1;
	 if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no case defined in case list of ST parameter assignment list.");}
	 else {print_err_msg(locf(@3), locl(@3), "invalid case in case list of ST parameter assignment list."); yyclearin;}
	 yyerrok;
	}
/* ERROR_CHECK_END */
;


case_list_element:
  signed_integer
| subrange
| enumerated_value
;





/********************************/
/* B 3.2.4 Iteration Statements */
/********************************/
iteration_statement:
  for_statement
| while_statement
| repeat_statement
| exit_statement
;


for_statement:
  FOR control_variable ASSIGN expression TO expression BY expression DO statement_list END_FOR
	{$$ = new for_statement_c($2, $4, $6, $8, $10, locloc(@$));}
| FOR control_variable ASSIGN expression TO expression DO statement_list END_FOR
	{$$ = new for_statement_c($2, $4, $6, NULL, $8, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| FOR ASSIGN expression TO expression BY expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no control variable defined in ST 'FOR' statement."); yynerrs++;}
| FOR ASSIGN expression TO expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no control variable defined in ST 'FOR' statement."); yynerrs++;}
| FOR error ASSIGN expression TO expression BY expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid control variable defined for ST 'FOR' statement."); yyerrok;}
| FOR error ASSIGN expression TO expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid control variable defined for ST 'FOR' statement."); yyerrok;}
| FOR control_variable expression TO expression BY expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "':=' missing between control variable and start expression in ST 'FOR' statement."); yynerrs++;}
| FOR control_variable expression TO expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locl(@2), locf(@3), "':=' missing between control variable and start expression in ST 'FOR' statement."); yynerrs++;}
| FOR control_variable error expression TO expression BY expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "expecting ':=' between control variable and start expression in ST 'FOR' statement."); yyerrok;}
| FOR control_variable error expression TO expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "expecting ':=' between control variable and start expression in ST 'FOR' statement."); yyerrok;}
| FOR control_variable ASSIGN TO expression BY expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "no start expression defined in ST 'FOR' statement."); yynerrs++;}
| FOR control_variable ASSIGN TO expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "no start expression defined in ST 'FOR' statement."); yynerrs++;}
| FOR control_variable ASSIGN error TO expression BY expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid start expression defined in ST 'FOR' statement."); yyerrok;}
| FOR control_variable ASSIGN error TO expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid start expression in ST 'FOR' statement."); yyerrok;}
| FOR control_variable ASSIGN expression error expression BY expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locf(@5), locl(@5), "expecting 'TO' between start expression and end expression in ST 'FOR' statement."); yyerrok;}
| FOR control_variable ASSIGN expression error expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locf(@5), locl(@5), "expecting 'TO' between start expression and end expression in ST 'FOR' statement."); yyerrok;}
| FOR control_variable ASSIGN expression TO expression error expression DO statement_list END_FOR
  {$$ = NULL; print_err_msg(locf(@7), locl(@7), "expecting 'BY' between end expression and step expression in ST 'FOR' statement."); yyerrok;}
| FOR control_variable ASSIGN expression TO expression BY expression error statement_list END_FOR
  {$$ = NULL; print_err_msg(locf(@9), locl(@9), "expecting 'DO' after step expression in ST 'FOR' statement."); yyerrok;}
| FOR control_variable ASSIGN expression TO expression error statement_list END_FOR
  {$$ = NULL; print_err_msg(locf(@7), locl(@7), "expecting 'DO' after end expression in ST 'FOR' statement."); yyerrok;}
| FOR control_variable ASSIGN expression TO expression BY expression DO END_FOR
  {$$ = NULL; print_err_msg(locl(@9), locf(@10), "no statement(s) defined after 'DO' in ST 'FOR' statement."); yynerrs++;}
| FOR control_variable ASSIGN expression TO expression DO END_FOR
  {$$ = NULL; print_err_msg(locl(@7), locf(@8), "no statement(s) defined after 'DO' in ST 'FOR' statement."); yynerrs++;}
| FOR control_variable ASSIGN expression TO expression BY expression DO error END_FOR
  {$$ = NULL; print_err_msg(locf(@10), locl(@10), "invalid statement(s) defined after 'DO' in ST 'FOR' statement."); yyerrok;}
| FOR control_variable ASSIGN expression TO expression DO error END_FOR
  {$$ = NULL; print_err_msg(locf(@8), locl(@8), "invalid statement(s) defined after 'DO' in ST 'FOR' statement."); yyerrok;}
| FOR control_variable error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed 'FOR' statement in ST."); yyerrok;}
| FOR control_variable ASSIGN expression error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed 'FOR' statement in ST."); yyerrok;}
| FOR control_variable ASSIGN expression TO expression DO statement_list END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed 'FOR' statement in ST."); yynerrs++;}
| FOR control_variable ASSIGN expression TO expression BY expression error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed 'FOR' statement in ST."); yyerrok;}
| FOR control_variable ASSIGN expression TO expression BY expression DO statement_list END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed 'FOR' statement in ST."); yynerrs++;}
| FOR error END_FOR
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in ST 'FOR' statement."); yyerrok;}
/* ERROR_CHECK_END */
;

/* The spec has the syntax
 * control_variable: identifier;
 * but then defines the semantics of control_variable
 * (Section 3.3.2.4) as being of an integer type
 * (e.g., SINT, INT, or DINT).
 *
 * Obviously this presuposes that the control_variable
 * must have been declared in some VAR .. END_VAR
 * We must therefore change the syntax to read
 * control_variable: prev_declared_variable_name;
 * 
 * If we don't, then the correct use of any previosuly declared 
 * variable would result in an incorrect syntax error
*/
control_variable: 
  prev_declared_variable_name 
	{$$ = new symbolic_variable_c($1,locloc(@$)); $$->token = $1->token;};
// control_variable: identifier {$$ = $1;};

/* Integrated directly into for_statement */
/*
for_list:
  expression TO expression [BY expression]
;
*/


while_statement:
  WHILE expression DO statement_list END_WHILE
	{$$ = new while_statement_c($2, $4, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| WHILE DO statement_list END_WHILE
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no test expression defined in ST 'WHILE' statement."); yynerrs++;}
| WHILE error DO statement_list END_WHILE
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid test expression defined for ST 'WHILE' statement."); yyerrok;}
| WHILE expression error statement_list END_WHILE
  {$$ = NULL; print_err_msg(locf(@3), locl(@3), "expecting 'DO' after test expression in ST 'WHILE' statement."); yyerrok;}
| WHILE expression DO END_WHILE
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "no statement(s) defined after 'DO' in ST 'WHILE' statement."); yynerrs++;}
| WHILE expression DO error END_WHILE
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid statement(s) defined after 'DO' in ST 'WHILE' statement."); yyerrok;}
| WHILE expression error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed 'WHILE' statement in ST."); yyerrok;}
| WHILE expression DO statement_list END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed 'WHILE' statement in ST."); yynerrs++;}
| WHILE error END_WHILE
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in ST 'WHILE' statement."); yyerrok;}
/* ERROR_CHECK_END */
;


repeat_statement:
  REPEAT statement_list UNTIL expression END_REPEAT
	{$$ = new repeat_statement_c($2, $4, locloc(@$));}
/* ERROR_CHECK_BEGIN */
| REPEAT UNTIL expression END_REPEAT
  {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no statement(s) defined after 'REPEAT' in ST 'REPEAT' statement."); yynerrs++;}
| REPEAT error UNTIL expression END_REPEAT
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid statement(s) defined after 'REPEAT' for ST 'REPEAT' statement."); yyerrok;}
| REPEAT statement_list UNTIL END_REPEAT
  {$$ = NULL; print_err_msg(locl(@3), locf(@4), "no test expression defined after 'UNTIL' in ST 'REPEAT' statement.");}
| REPEAT statement_list UNTIL error END_REPEAT
  {$$ = NULL; print_err_msg(locf(@4), locl(@4), "invalid test expression defined after 'UNTIL' in ST 'REPEAT' statement."); yyerrok;}
| REPEAT statement_list END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed 'REPEAT' statement in ST."); yynerrs++;}
| REPEAT statement_list UNTIL expression error END_OF_INPUT
  {$$ = NULL; print_err_msg(locf(@1), locl(@1), "unclosed 'REPEAT' statement in ST."); yyerrok;}
| REPEAT error END_REPEAT
  {$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in ST 'REPEAT' statement."); yyerrok;}
/* ERROR_CHECK_END */
;


exit_statement:
  EXIT	{$$ = new exit_statement_c(locloc(@$));}
;





%%

#include <stdio.h>	/* required for printf() */
#include <errno.h>
#include "../util/symtable.hh"




/*************************************************************************************************/
/* NOTE: These variables are really parameters we would like the stage2__ function to pass       */
/*       to the yyparse() function. However, the yyparse() function is created automatically     */
/*       by bison, so we cannot add parameters to this function. The only other                  */
/*       option is to use global variables! yuck!                                                */ 
/*************************************************************************************************/

/* A global flag used to tell the parser if overloaded funtions should be allowed.
 * The IEC 61131-3 standard allows overloaded funtions in the standard library,
 * but disallows them in user code...
 *
 * In essence, a parameter we would like to pass to the yyparse() function but
 * have to do it using a global variable, as the yyparse() prototype is fixed by bison.
 */
bool allow_function_overloading = false;

/* | [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.
 */
bool allow_extensible_function_parameters = false;

/* A global flag used to tell the parser whether to allow use of DREF and '^' operators (defined in IEC 61131-3 v3) */
bool allow_ref_dereferencing;
/* A global flag used to tell the parser whether to allow use of REF_TO ANY datatypes (non-standard extension) */
bool allow_ref_to_any = false;
/* A global flag used to tell the parser whether to allow use of REF_TO as a struct or array element (non-standard extension) */
bool allow_ref_to_in_derived_datatypes = false;

/* A pointer to the root of the parsing tree that will be generated by bison. */
symbol_c *tree_root;



/* The following function is called automatically by bison whenever it comes across
 * an error. Unfortunately it calls this function before executing the code that handles
 * the error itself, so we cannot print out the correct line numbers of the error location
 * over here.
 * Our solution is to store the current error message in a global variable, and have all
 * error action handlers call the function print_err_msg() after setting the location
 * (line number) variable correctly.
 */
const char *current_error_msg;
void yyerror (const char *error_msg) {
  current_error_msg = error_msg;
/* fprintf(stderr, "error %d: %s\n", yynerrs // global variable //, error_msg); */
/*  print_include_stack(); */
}


/* ERROR_CHECK_BEGIN */
bool is_current_syntax_token() {
  switch (yychar) {
    case ';':
    case ',':
    case ')':
    case ']':
    case '+':
    case '*':
    case '-':
    case '/':
    case '<':
    case '>':
    case '=':
    case '&':
    case OR:
    case XOR:
    case AND:
    case AND2:
    case OPER_NE:
    case OPER_LE:
    case OPER_GE:
    case MOD:
    case OPER_EXP:
    case NOT:
      return true;
    default:
     return false;
  }
}
/* ERROR_CHECK_END */


void print_err_msg(int first_line,
                   int first_column,
                   const char *first_filename,
                   long int first_order,
                   int last_line,
                   int last_column,
                   const char *last_filename,
                   long int last_order,
                   const char *additional_error_msg) {

  const char *unknown_file = "<unknown_file>";
  if (first_filename == NULL) first_filename = unknown_file;
  if ( last_filename == NULL)  last_filename = unknown_file;

  if (runtime_options.full_token_loc) {
    if (first_filename == last_filename)
      fprintf(stderr, "%s:%d-%d..%d-%d: error: %s\n", first_filename, first_line, first_column, last_line, last_column, additional_error_msg);
    else
      fprintf(stderr, "%s:%d-%d..%s:%d-%d: error: %s\n", first_filename, first_line, first_column, last_filename, last_line, last_column, additional_error_msg);
  } else {
      fprintf(stderr, "%s:%d: error: %s\n", first_filename, first_line, additional_error_msg);
  }
  //fprintf(stderr, "error %d: %s\n", yynerrs /* a global variable */, additional_error_msg);
  print_include_stack();
}



/* If function overloading is on, we allow several functions with the same name.
 *
 * However, to support standard functions, we also allow functions named
 *   AND, MOD, NOT, OR, XOR, ADD, ...
 */
/*
identifier_c *token_2_identifier_c(char *value, ) {
  identifier_c tmp = new identifier_c(value, locloc(@$));
	 if (!allow_function_overloading) {
	   fprintf(stderr, "Function overloading not allowed. Invalid identifier %s\n", ((token_c *)($$))->value);
	   ERROR;
	 }
	}
}
*/

/* convert between an il_operator to a function name */
/* This a kludge!
 * It is required because our language requires more than one
 * look ahead token, and bison only works with one!
 */
#define op_2_str(op, str) {\
  op ## _operator_c *ptr = dynamic_cast<op ## _operator_c *>(il_operator); \
  if (ptr != NULL) name = str; \
}

/* NOTE: this code is very ugly and un-eficient, but I (Mario) have many
 *       more things to worry about right now, so just let it be...
 */
poutype_identifier_c *il_operator_c_2_poutype_identifier_c(symbol_c *il_operator) {
  identifier_c         *    id = il_operator_c_2_identifier_c(il_operator);
  poutype_identifier_c *pou_id = new poutype_identifier_c(strdup(id->value));

  *(symbol_c *)pou_id = *(symbol_c *)id;
  delete id;
  return pou_id;
}
  

identifier_c *il_operator_c_2_identifier_c(symbol_c *il_operator) {
  const char *name = NULL;
  identifier_c *res;

  op_2_str(NOT,   "NOT");

  op_2_str(AND,   "AND");
  op_2_str(OR,    "OR");
  op_2_str(XOR,   "XOR");
  op_2_str(ADD,   "ADD");
  op_2_str(SUB,   "SUB");
  op_2_str(MUL,   "MUL");
  op_2_str(DIV,   "DIV");
  op_2_str(MOD,   "MOD");
  op_2_str(GT,    "GT");
  op_2_str(GE,    "GE");
  op_2_str(EQ,    "EQ");
  op_2_str(LT,    "LT");
  op_2_str(LE,    "LE");
  op_2_str(NE,    "NE");

  op_2_str(LD,    "LD");
  op_2_str(LDN,   "LDN");
  op_2_str(ST,    "ST");
  op_2_str(STN,   "STN");

  op_2_str(S,     "S");
  op_2_str(R,     "R");
  op_2_str(S1,    "S1");
  op_2_str(R1,    "R1");

  op_2_str(CLK,   "CLK");
  op_2_str(CU,    "CU");
  op_2_str(CD,    "CD");
  op_2_str(PV,    "PV");
  op_2_str(IN,    "IN");
  op_2_str(PT,    "PT");

  op_2_str(ANDN,  "ANDN");
  op_2_str(ORN,   "ORN");
  op_2_str(XORN,  "XORN");

  op_2_str(ADD,   "ADD");
  op_2_str(SUB,   "SUB");
  op_2_str(MUL,   "MUL");
  op_2_str(DIV,   "DIV");

  op_2_str(GT,    "GT");
  op_2_str(GE,    "GE");
  op_2_str(EQ,    "EQ");
  op_2_str(LT,    "LT");
  op_2_str(LE,    "LE");
  op_2_str(NE,    "NE");

  op_2_str(CAL,   "CAL");
  op_2_str(CALC,  "CALC");
  op_2_str(CALCN, "CALCN");
  op_2_str(RET,   "RET");
  op_2_str(RETC,  "RETC");
  op_2_str(RETCN, "RETCN");
  op_2_str(JMP,   "JMP");
  op_2_str(JMPC,  "JMPC");
  op_2_str(JMPCN, "JMPCN");

  if (name == NULL)
    ERROR;
/*
  res = new identifier_c(strdup(name), 
                         il_operator->first_line,
                         il_operator->first_column,
                         il_operator->first_file,
                         il_operator->first_order,
                         il_operator->last_line,
                         il_operator->last_column,
                         il_operator->last_file,
                         il_operator->last_order
                        );
  free(il_operator);
*/
  res = new identifier_c(strdup(name));
  *(symbol_c *)res = *(symbol_c *)il_operator;
  delete il_operator;
  
  return res;
}


#include "standard_function_names.c"

const char *standard_function_block_names[] = {
// 2.5.2.3.1  Bistable elements
//   Table 34 - Standard bistable function blocks
"SR","RS",
// 2.5.2.3.2  Edge detection
//   Table 35 - Standard edge detection function blocks
"R_TRIG","F_TRIG",
// 2.5.2.3.3  Counters
//   Table 36 - Standard counter function blocks
"CTU","CTU_DINT","CTU_LINT","CTU_UDINT","CTU_ULINT",
"CTD","CTD_DINT","CTD_LINT","CTD_UDINT","CTD_ULINT",
"CTUD","CTUD_DINT","CTUD_LINT","CTUD_ULINT",
// 2.5.2.3.4  Timers
//   Table 37 - Standard timer function blocks
"TP","TON","TOF",
/* end of array marker! Do not remove! */
NULL
};


#define LIBFILE "ieclib.txt"
#define DEF_LIBFILENAME LIBDIRECTORY "/" LIBFILE

extern const char *INCLUDE_DIRECTORIES[];


static int parse_files(const char *libfilename, const char *filename) {
  /* first parse the standard library file... */  
  /*   Do not debug the standard library, even if debug flag is set!
  #if YYDEBUG
    yydebug = 1;
  #endif
  */
  FILE *libfile = NULL;
  if((libfile = parse_file(libfilename)) == NULL) {
    char *errmsg = strdup2("Error opening library file ", libfilename);
    perror(errmsg);
    free(errmsg);
    /* we give up... */
    return -1;
  }

  allow_function_overloading           = true;
  allow_extensible_function_parameters = true;
  allow_ref_dereferencing              = runtime_options.ref_standard_extensions;
  allow_ref_to_any                     = runtime_options.ref_nonstand_extensions;
  allow_ref_to_in_derived_datatypes    = runtime_options.ref_nonstand_extensions;
  if (yyparse() != 0) {
    fprintf (stderr, "\nParsing failed because of too many consecutive syntax errors in standard library. Bailing out!\n");
    exit(EXIT_FAILURE);
  }
  fclose(libfile);
      
  if (yynerrs > 0) {  /* NOTE: yynerrs is a global variable */
    /* Hopefully the libraries do not contain any errors, so this should not occur! */
    fprintf (stderr, "\n%d error(s) found in %s. Bailing out!\n", yynerrs, libfilename);
    return -2;
  }

  /* if by any chance the library is not complete, we now add the missing reserved keywords to the list!!!  */
  for(int i = 0; standard_function_block_names[i] != NULL; i++)
    if (library_element_symtable.find(standard_function_block_names[i]) ==
        library_element_symtable.end())
      library_element_symtable.insert(standard_function_block_names[i], standard_function_block_name_token);

  /* now parse the input file... */
  #if YYDEBUG
    yydebug = 1;
  #endif
  FILE *mainfile = NULL;
  if ((mainfile = parse_file(filename)) == NULL) {
    char *errmsg = strdup2("Error opening main file ", filename);
    perror(errmsg);
    free(errmsg);
    return -3;
  }

  allow_function_overloading           = false;
  allow_extensible_function_parameters = false;
  allow_ref_dereferencing              = runtime_options.ref_standard_extensions;
  allow_ref_to_any                     = runtime_options.ref_nonstand_extensions;
  allow_ref_to_in_derived_datatypes    = runtime_options.ref_nonstand_extensions;
  //allow_ref_to_any = false;    /* we only allow REF_TO ANY in library functions/FBs, no matter what the user asks for in the command line */

  if (yyparse() != 0) {
    fprintf (stderr, "\nParsing failed because of too many consecutive syntax errors. Bailing out!\n");
    exit(EXIT_FAILURE);
  }
  fclose(mainfile);
  
  if (yynerrs > 0) {
    fprintf (stderr, "\n%d error(s) found. Bailing out!\n", yynerrs /* global variable */);
    exit(EXIT_FAILURE);
  }

  return 0;
}  





/* We parse the input source code twice!!
 *  1st pass -->  Pre-parsing
 *  -------------------------
 *  The intention of the first pass is to fill up the library_element_symtable with the names of all
 *  the POUs (Functions, FBs, Programs and Configurations), as well as all the Derived Datatypes.
 * 
 *  During this pass POUs are only parsed until their name is obtained, and the remaining source
 *  code (variable declarations and body) is completely thrown away by flex. Datatype declarations
 *  however are parsed normally!
 *
 *  At the end of the pre-parsing, the AST will contain only the derived datatype declarations,
 *  and this tree will be trown away (by simply resetting tree_root = NULL).
 *  More importantly, the library_element_symtable will contain the names of all the POUs and 
 *  derived datatypes.
 *
 *  2st pass -->  Normal parsing
 *  ----------------------------
 *  In this second parse the whole source code is parsed correctly, and the AST is generated
 *  completely.
 *
 *  However, if the pre-parsing has been done before this normal parsing, the POUs may appear
 *  in the source code in any order, as calling a POU (e.g. calling a function) that has not yet
 *  been declared will no longer generate a parsing error because the name of the function being 
 *  called is already in the library_element_symtable.
 *
 *  Declaring variables of datatypes that have not yet been declared will also be possible, as the
 *  datatypes will also already be in the library_element_symtable!
 */

int stage2__(const char *filename, 
             symbol_c **tree_root_ref
            ) {             
  char *libfilename = NULL;

  /* Determine the full path name of the standard library file... */
  if (runtime_options.includedir != NULL)
    INCLUDE_DIRECTORIES[0] = runtime_options.includedir;

  if ((libfilename = strdup3(INCLUDE_DIRECTORIES[0], "/", LIBFILE)) == NULL) {
    fprintf (stderr, "Out of memory. Bailing out!\n");
    exit(EXIT_FAILURE);
  }

  /*******************************/
  /* Do the  PRE parsing run...! */
  /*******************************/
  if (runtime_options.pre_parsing) {
    // fprintf (stderr, "----> Starting pre-parsing!\n");
    tree_root = NULL;
    set_preparse_state();
    if (parse_files(libfilename, filename) < 0)
      exit(EXIT_FAILURE);
    // TODO: delete the current AST. For the moment, we leave all the objects in memory (not much of an issue in a program that always runs to completion).
  }
  /*******************************/
  /* Do the main parsing run...! */
  /*******************************/
  // fprintf (stderr, "----> Starting normal parsing!\n");
  tree_root = NULL;
  rst_preparse_state();
  if (parse_files(libfilename, filename) < 0)
    exit(EXIT_FAILURE);
  

  /* Final clean-up... */
  free(libfilename);
  if (tree_root_ref != NULL)
    *tree_root_ref = tree_root;

  return 0;
}