/* * 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 . * * * 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 /* 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 in list_c * * execute the 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 %type 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 start %type any_identifier %token prev_declared_variable_name_token %token prev_declared_direct_variable_token %token prev_declared_fb_name_token %type prev_declared_variable_name %type prev_declared_direct_variable %type prev_declared_fb_name %token prev_declared_simple_type_name_token %token prev_declared_subrange_type_name_token %token prev_declared_enumerated_type_name_token %token prev_declared_array_type_name_token %token prev_declared_structure_type_name_token %token prev_declared_string_type_name_token %token prev_declared_ref_type_name_token /* defined in IEC 61131-3 v3 */ %type prev_declared_simple_type_name %type prev_declared_subrange_type_name %type prev_declared_enumerated_type_name %type prev_declared_array_type_name %type prev_declared_structure_type_name %type prev_declared_string_type_name %type prev_declared_ref_type_name /* defined in IEC 61131-3 v3 */ %token prev_declared_derived_function_name_token %token prev_declared_derived_function_block_name_token %token prev_declared_program_type_name_token %type prev_declared_derived_function_name %type prev_declared_derived_function_block_name %type 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 disable_code_generation_pragma %type 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 pragma_token %type pragma /* The joining of all previous pragmas, i.e. any possible pragma */ %type 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 en_identifier %type 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 library %type library_element_declaration /*******************************************/ /* B 1.1 - Letters, digits and identifiers */ /*******************************************/ /* Done totally within flex... letter digit octal_digit hex_digit */ %token identifier_token %type identifier /*********************/ /* B 1.2 - Constants */ /*********************/ %type constant %type 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 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 numeric_literal %type integer_literal %type signed_integer %token integer_token %type integer %token binary_integer_token %type binary_integer %token octal_integer_token %type octal_integer %token hex_integer_token %type hex_integer %token real_token %type real %type signed_real %type real_literal // %type exponent %type bit_string_literal %type 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 single_byte_character_string_token %token double_byte_character_string_token %type character_string %type single_byte_character_string %type double_byte_character_string /***************************/ /* B 1.2.3 - Time Literals */ /***************************/ %type time_literal /************************/ /* B 1.2.3.1 - Duration */ /************************/ %type duration %type interval %type days %type fixed_point %type hours %type minutes %type seconds %type milliseconds %token fixed_point_token %token fixed_point_d_token %token integer_d_token %token fixed_point_h_token %token integer_h_token %token fixed_point_m_token %token integer_m_token %token fixed_point_s_token %token integer_s_token %token fixed_point_ms_token %token integer_ms_token %token end_interval_token %token erroneous_interval_token // %token TIME %token T_SHARP /************************************/ /* B 1.2.3.2 - Time of day and Date */ /************************************/ %type time_of_day %type daytime %type day_hour %type day_minute %type day_second %type date %type date_literal %type year %type month %type day %type 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 data_type_name %type 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 elementary_type_name %type numeric_type_name %type integer_type_name %type signed_integer_type_name %type unsigned_integer_type_name %type real_type_name %type date_type_name %type bit_string_type_name /* helper symbol to concentrate the instantiation * of STRING and WSTRING into a single location */ %type 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 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 derived_type_name %type single_element_type_name // %type simple_type_name // %type subrange_type_name // %type enumerated_type_name // %type array_type_name // %type structure_type_name %type data_type_declaration /* helper symbol for data_type_declaration */ %type type_declaration_list %type type_declaration %type single_element_type_declaration %type simple_type_declaration %type simple_spec_init %type simple_specification %type subrange_type_declaration %type subrange_spec_init %type subrange_specification %type subrange /* A non standard construct, used to support the use of variables in array subranges. e.g.: ARRAY [12..max] OF INT */ %type subrange_with_var %type enumerated_type_declaration %type enumerated_spec_init %type enumerated_specification /* helper symbol for enumerated_value */ %type enumerated_value_list %type enumerated_value //%type enumerated_value_without_identifier %type array_type_declaration %type array_spec_init %type array_specification /* helper symbol for array_specification */ %type array_subrange_list %type array_initialization /* helper symbol for array_initialization */ %type array_initial_elements_list %type array_initial_elements %type array_initial_element %type structure_type_declaration %type structure_specification %type initialized_structure %type structure_declaration /* helper symbol for structure_declaration */ %type structure_element_declaration_list %type structure_element_declaration %type structure_element_name %type structure_initialization /* helper symbol for structure_initialization */ %type structure_element_initialization_list %type structure_element_initialization //%type string_type_name %type string_type_declaration /* helper symbol for string_type_declaration */ %type string_type_declaration_size /* helper symbol for string_type_declaration */ %type string_type_declaration_init %token ASSIGN %token DOTDOT /* ".." */ %token TYPE %token END_TYPE %token ARRAY %token OF %token STRUCT %token END_STRUCT %type ref_spec /* defined in IEC 61131-3 v3 */ %type ref_spec_non_recursive /* helper symbol */ %type ref_spec_init /* defined in IEC 61131-3 v3 */ %type ref_type_decl /* defined in IEC 61131-3 v3 */ /*********************/ /* B 1.4 - Variables */ /*********************/ %type variable %type symbolic_variable /* helper symbol for prog_cnxn */ %type any_symbolic_variable %type variable_name /********************************************/ /* B.1.4.1 Directly Represented Variables */ /********************************************/ /* Done totally within flex... location_prefix size_prefix */ %token direct_variable_token //%type direct_variable /*************************************/ /* B.1.4.2 Multi-element Variables */ /*************************************/ %type multi_element_variable /* helper symbol for any_symbolic_variable */ %type any_multi_element_variable %type array_variable /* helper symbol for any_symbolic_variable */ %type any_array_variable %type subscripted_variable /* helper symbol for any_symbolic_variable */ %type any_subscripted_variable %type subscript_list %type subscript %type structured_variable /* helper symbol for any_symbolic_variable */ %type any_structured_variable %type record_variable /* helper symbol for any_symbolic_variable */ %type any_record_variable %type field_selector /******************************************/ /* B 1.4.3 - Declaration & Initialisation */ /******************************************/ %type input_declarations /* helper symbol for input_declarations */ %type input_declaration_list %type input_declaration %type edge_declaration /* en_param_declaration is not in the standard, but should be! */ %type en_param_declaration %type var_init_decl %type var1_init_decl %type var1_list %type array_var_init_decl %type structured_var_init_decl %type fb_name_decl /* helper symbol for fb_name_decl */ %type fb_name_list_with_colon /* helper symbol for fb_name_list_with_colon */ %type var1_list_with_colon // %type fb_name_list // %type fb_name %type output_declarations %type var_output_init_decl %type var_output_init_decl_list /* eno_param_declaration is not in the standard, but should be! */ %type eno_param_declaration %type input_output_declarations /* helper symbol for input_output_declarations */ %type var_declaration_list %type var_declaration %type temp_var_decl %type var1_declaration %type array_var_declaration %type structured_var_declaration %type var_declarations %type retentive_var_declarations %type located_var_declarations /* helper symbol for located_var_declarations */ %type located_var_decl_list %type located_var_decl %type external_var_declarations /* helper symbol for external_var_declarations */ %type external_declaration_list %type external_declaration %type global_var_name %type global_var_declarations /* helper symbol for global_var_declarations */ %type global_var_decl_list %type global_var_decl %type global_var_spec %type located_var_spec_init %type location %type global_var_list %type string_var_declaration %type single_byte_string_var_declaration %type single_byte_string_spec %type double_byte_string_var_declaration %type double_byte_string_spec %type incompl_located_var_declarations /* helper symbol for incompl_located_var_declarations */ %type incompl_located_var_decl_list %type incompl_located_var_decl %type incompl_location %type var_spec /* helper symbol for var_spec */ %type string_spec /* intermediate helper symbol for: * - non_retentive_var_decls * - var_declarations */ %type var_init_decl_list %token 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 function_name /* helper symbol for IL language */ %type function_name_no_clashes %type function_name_simpleop_clashes //%type function_name_expression_clashes /* helper symbols for ST language */ //%type function_name_NOT_clashes %type function_name_no_NOT_clashes //%type standard_function_name /* helper symbols for IL language */ %type standard_function_name_no_clashes %type standard_function_name_simpleop_clashes %type standard_function_name_expression_clashes /* helper symbols for ST language */ %type standard_function_name_NOT_clashes %type standard_function_name_no_NOT_clashes %type derived_function_name %type function_declaration /* helper symbol for function_declaration */ %type function_name_declaration %type io_var_declarations %type function_var_decls %type function_body %type var2_init_decl /* intermediate helper symbol for function_declaration */ %type io_OR_function_var_declarations_list /* intermediate helper symbol for function_var_decls */ %type var2_init_decl_list %token standard_function_name_token %token FUNCTION %token END_FUNCTION %token CONSTANT /*****************************/ /* B 1.5.2 - Function Blocks */ /*****************************/ %type function_block_type_name %type standard_function_block_name %type derived_function_block_name %type function_block_declaration %type other_var_declarations %type temp_var_decls %type non_retentive_var_decls %type function_block_body /* intermediate helper symbol for function_declaration */ %type io_OR_other_var_declarations_list /* intermediate helper symbol for temp_var_decls */ %type temp_var_decls_list %token 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 program_type_name %type program_declaration /* helper symbol for program_declaration */ %type program_var_declarations_list %token PROGRAM %token END_PROGRAM /********************************************/ /* B 1.6 Sequential Function Chart elements */ /********************************************/ %type sequential_function_chart %type sfc_network %type initial_step %type step %type action_association_list %type step_name %type action_association /* helper symbol for action_association */ %type indicator_name_list %type action_name %type action_qualifier %type qualifier %type timed_qualifier %type action_time %type indicator_name %type transition %type steps %type step_name_list %type transition_priority %type transition_condition %type action %type action_body %type 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 configuration_name %type resource_type_name %type configuration_declaration // helper symbol for // - configuration_declaration // - resource_declaration // %type global_var_declarations_list // helper symbol for configuration_declaration %type optional_access_declarations // helper symbol for configuration_declaration %type optional_instance_specific_initializations // helper symbol for configuration_declaration %type resource_declaration_list %type resource_declaration %type single_resource_declaration // helper symbol for single_resource_declaration %type task_configuration_list // helper symbol for single_resource_declaration %type program_configuration_list %type resource_name // %type access_declarations // helper symbol for access_declarations // %type access_declaration_list // %type access_declaration // %type access_path // helper symbol for access_path %type any_fb_name_list %type global_var_reference // %type access_name %type program_output_reference %type program_name // %type direction %type task_configuration %type task_name %type task_initialization // 3 helper symbols for task_initialization %type task_initialization_single %type task_initialization_interval %type task_initialization_priority %type data_source %type program_configuration // helper symbol for program_configuration %type optional_task_name // helper symbol for program_configuration %type optional_prog_conf_elements %type prog_conf_elements %type prog_conf_element %type fb_task %type prog_cnxn %type prog_data_source %type data_sink %type instance_specific_initializations // helper symbol for instance_specific_initializations %type instance_specific_init_list %type instance_specific_init // helper symbol for instance_specific_init %type fb_initialization %type prev_declared_global_var_name %token prev_declared_global_var_name_token %type prev_declared_program_name %token prev_declared_program_name_token %type prev_declared_resource_name %token prev_declared_resource_name_token %type prev_declared_configuration_name %token prev_declared_configuration_name_token // %type prev_declared_task_name // %token 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 instruction_list %type il_instruction %type il_incomplete_instruction %type label %type il_simple_operation // helper symbol for il_simple_operation //%type il_simple_operator_clash_il_operand %type il_expression %type il_jump_operation %type il_fb_call %type il_formal_funct_call // helper symbol for il_formal_funct_call %type il_expr_operator_clash_eol_list %type il_operand %type il_operand_list // helper symbol for il_simple_operation %type il_operand_list2 %type simple_instr_list %type il_simple_instruction %type il_param_list %type il_param_instruction_list %type il_param_instruction %type il_param_last_instruction %type il_param_assignment %type il_param_out_assignment %token EOL /*******************/ /* B 2.2 Operators */ /*******************/ %token sendto_identifier_token %type sendto_identifier %type LD_operator %type LDN_operator %type ST_operator %type STN_operator %type NOT_operator %type S_operator %type R_operator %type S1_operator %type R1_operator %type CLK_operator %type CU_operator %type CD_operator %type PV_operator %type IN_operator %type PT_operator %type AND_operator %type AND2_operator %type OR_operator %type XOR_operator %type ANDN_operator %type ANDN2_operator %type ORN_operator %type XORN_operator %type ADD_operator %type SUB_operator %type MUL_operator %type DIV_operator %type MOD_operator %type GT_operator %type GE_operator %type EQ_operator %type LT_operator %type LE_operator %type NE_operator %type CAL_operator %type CALC_operator %type CALCN_operator %type RET_operator %type RETC_operator %type RETCN_operator %type JMP_operator %type JMPC_operator %type JMPCN_operator %type il_simple_operator %type il_simple_operator_clash %type il_simple_operator_clash1 %type il_simple_operator_clash2 %type il_simple_operator_clash3 %type il_simple_operator_noclash //%type il_expr_operator %type il_expr_operator_clash %type il_expr_operator_noclash %type il_assign_operator %type il_assign_out_operator %type il_call_operator %type il_return_operator %type 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 ref_expression /* an extension to the IEC 61131-3 v2 standard, based on the IEC 61131-3 v3 standard */ %type deref_expression /* an extension to the IEC 61131-3 v2 standard, based on the IEC 61131-3 v3 standard */ %type expression %type xor_expression %type and_expression %type comparison %type equ_expression // %type comparison_operator %type add_expression // %type add_operator %type term // %type multiply_operator %type power_expression %type unary_expression // %type unary_operator %type primary_expression %type non_int_or_real_primary_expression /* intermediate helper symbol for primary_expression */ %type 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 statement_list %type statement /*********************************/ /* B 3.2.1 Assignment Statements */ /*********************************/ %type assignment_statement // %token ASSIGN /* ":=" */ /*****************************************/ /* B 3.2.2 Subprogram Control Statements */ /*****************************************/ %type subprogram_control_statement %type return_statement %type fb_invocation // %type param_assignment %type param_assignment_formal %type param_assignment_nonformal /* helper symbols for fb_invocation */ %type param_assignment_formal_list %type param_assignment_nonformal_list // %token ASSIGN // %token SENDTO /* "=>" */ %token RETURN /********************************/ /* B 3.2.3 Selection Statements */ /********************************/ %type selection_statement %type if_statement %type case_statement %type case_element %type case_list %type case_list_element /* helper symbol for if_statement */ %type elseif_statement_list /* helper symbol for elseif_statement_list */ %type elseif_statement /* helper symbol for case_statement */ %type 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 iteration_statement %type for_statement %type control_variable %type while_statement %type repeat_statement %type exit_statement /* Integrated directly into for_statement */ // %type 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 */ /* 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 '[' ']' * 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 | */ ; /**********************/ /* 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($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($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 exists, and to make it easier to handle the as a general case (i.e. without C++ code handling this as a special case), * we will create an equivalent LD IL instruction, and prepend it into the . * 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 (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 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 $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 $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($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($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($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($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($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($1)) ERROR;} // $1 should be a poutype_identifier_c | function_name_no_NOT_clashes '(' ')' {if (NULL == dynamic_cast($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 /* required for printf() */ #include #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 = ""; 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(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; }