improve performance of some extensible Standard Functions (ADD, MUL, AND, OR, XOR) -- Add comments!!
/*
* 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;
}