/* * (c) 2003 Mario de Sousa * * Offered to the public under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 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. * * 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 IL and ST 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. 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 ! *******/ /******* =================================================== *******/ /******* *******/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ %{ #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.y.hh" /* The interface through which bison and flex interact. */ #include "stage1_2_priv.hh" /* 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->elements[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.last_line, foo.last_column #define locf(foo) foo.first_line, foo.first_column #define locl(foo) foo.last_line, foo.last_column /* A macro for printing out internal parser errors... */ #define ERROR error_exit(__FILE__,__LINE__) /* function defined in main.cc */ extern void error_exit(const char *file_name, int line_no); /*************************/ /* global variables... */ /*************************/ /* NOTE: For some strange reason bison ver 2.3 is including these declarations * in the iec.y.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.y.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) */ /* 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 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 */ symbol_c *il_operator_c_2_identifier_c(symbol_c *il_operator); /* print an error message */ void print_err_msg(const char *filename, int lineno, const char *additional_error_msg); %} // %glr-parser // %expect-rr 1 %union { symbol_c *leaf; list_c *list; char *ID; /* token value */ struct { symbol_c *first; symbol_c *second; symbol_c *third; symbol_c *fourth; } tmp_symbol; /* used as a temorary reference to symbols by: il_simple_operator_clash_il_operand transaction_tmp action_tmp */ } /* 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 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_fb_name_token %type prev_declared_variable_name %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 %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 %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 /* The pragmas... */ %token pragma_token %type pragma /* Where do these tokens belong ?? */ /* TODO: get the syntax parser to handle these tokens... */ %token EN %token ENO /***************************/ /* 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 /* a helper symbol for expression */ %type non_negative_constant /******************************/ /* B 1.2.1 - Numeric Literals */ /******************************/ /* Done totally within flex... bit */ %type numeric_literal /* helper symbol for non_negative_constant */ %type non_negative_numeric_literal %type integer_literal %type signed_integer /* a helper symbol for non_negative_constant */ %type non_negative_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 /* helper symbol for non_negative_real_literal */ %type non_negative_signed_real %type real_literal /* helper symbol for non_negative_numeric_literal */ %type non_negative_real_literal // %type exponent %type bit_string_literal %type boolean_literal %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 %type integer_d %type integer_h %type integer_m %type integer_s %type integer_ms %type fixed_point_d %type fixed_point_h %type fixed_point_m %type fixed_point_s %type fixed_point_ms %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 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 /********************************/ /* 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 %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 /*********************/ /* 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 %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 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 * - output_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_header %type transition_condition_il %type transition_condition_st %type action_header %type action %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 /* 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 optional_global_var_declarations // 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 %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 %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_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 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 /* 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 /* helper symbol for fb_invocation */ %type param_assignment_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 %% /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /********************************************************/ /*****************************/ /* Prelimenary constructs... */ /*****************************/ start: library {$$ = $1;} ; /* the pragmas... */ pragma: pragma_token {$$ = new pragma_c($1, locloc(@$));} /* 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 /**/ | prev_declared_enumerated_type_name | prev_declared_simple_type_name | prev_declared_subrange_type_name | prev_declared_array_type_name | prev_declared_structure_type_name | prev_declared_string_type_name | prev_declared_derived_function_name | prev_declared_derived_function_block_name | prev_declared_program_type_name /**/ | prev_declared_resource_name | prev_declared_program_name | prev_declared_global_var_name ; 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 identifier_c($1, locloc(@$));}; prev_declared_subrange_type_name: prev_declared_subrange_type_name_token {$$ = new identifier_c($1, locloc(@$));}; prev_declared_enumerated_type_name: prev_declared_enumerated_type_name_token {$$ = new identifier_c($1, locloc(@$));}; prev_declared_array_type_name: prev_declared_array_type_name_token {$$ = new identifier_c($1, locloc(@$));}; prev_declared_structure_type_name: prev_declared_structure_type_name_token {$$ = new identifier_c($1, locloc(@$));}; prev_declared_string_type_name: prev_declared_string_type_name_token {$$ = new identifier_c($1, locloc(@$));}; prev_declared_derived_function_name: prev_declared_derived_function_name_token {$$ = new identifier_c($1, locloc(@$));}; prev_declared_derived_function_block_name: prev_declared_derived_function_block_name_token {$$ = new identifier_c($1, locloc(@$));}; prev_declared_program_type_name: prev_declared_program_type_name_token {$$ = new identifier_c($1, locloc(@$));}; /***************************/ /* 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);} /* ERROR_CHECK_BEGIN */ | library error END_OF_INPUT {$$ = NULL; print_err_msg(current_filename, @2.first_line, "unknown 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: numeric_literal | character_string | time_literal | bit_string_literal | boolean_literal /* NOTE: in order to remove reduce/reduce conflicts, * unsigned_integer, signed_integer, binary_integer, octal_integer * and hex_integer have been integrated directly into * the constants construct, instead of belonging to * either the bit_string_literal or integer_literal * construct. */ /* NOTE: unsigned_integer, although used in some * rules, is not defined in the spec! * We therefore replaced unsigned_integer as integer */ /*| integer {} */ /* i.e. an unsigned_integer */ /* NOTE: already included as a signed integer! */ | signed_integer | binary_integer | octal_integer | hex_integer ; /* a helper symbol for expression */ /* A constant without any preceding '-', but may * include a preceding '+' ! */ non_negative_constant: non_negative_numeric_literal | character_string | time_literal | bit_string_literal | boolean_literal | non_negative_signed_integer | binary_integer | octal_integer | hex_integer ; /******************************/ /* 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, intger '.' 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 ; /* helper symbol for non_negative_constant */ non_negative_numeric_literal: integer_literal | non_negative_real_literal ; integer_literal: integer_type_name '#' signed_integer {$$ = new integer_literal_c($1, $3, locf(@1), locl(@3));} | integer_type_name '#' binary_integer {$$ = new integer_literal_c($1, $3, locf(@1), locl(@3));} | integer_type_name '#' octal_integer {$$ = new integer_literal_c($1, $3, locf(@1), locl(@3));} | integer_type_name '#' hex_integer {$$ = new integer_literal_c($1, $3, locf(@1), locl(@3));} /* NOTE: see note in the definition of constant for reason * why signed_integer, binary_integer, octal_integer * and hex_integer are missing here! */ ; signed_integer: integer | '+' integer {$$ = $2;} | '-' integer {$$ = new neg_expression_c($2, locloc(@$));} ; /* a helper symbol for non_negative_constant */ /* A integer without any preceding '-', but may * include a preceding '+' ! */ non_negative_signed_integer: integer | '+' integer {$$ = $2;} ; real_literal: signed_real | real_type_name '#' signed_real {$$ = new real_literal_c($1, $3, locf(@1), locl(@3));} ; /* helper symbol for non_negative_numeric_literal */ non_negative_real_literal: non_negative_signed_real | real_type_name '#' signed_real {$$ = new real_literal_c($1, $3, locf(@1), locl(@3));} ; signed_real: real | '+' real {$$ = $2;} | '-' real {$$ = new neg_expression_c($2, locloc(@2));} ; /* helper symbol for non_negative_real_literal */ non_negative_signed_real: real | '+' real {$$ = $2;} ; bit_string_literal: bit_string_type_name '#' integer /* i.e. unsigned_integer */ {$$ = new bit_string_literal_c($1, $3, locf(@1), locl(@3));} | bit_string_type_name '#' binary_integer {$$ = new bit_string_literal_c($1, $3, locf(@1), locl(@3));} | bit_string_type_name '#' octal_integer {$$ = new bit_string_literal_c($1, $3, locf(@1), locl(@3));} | bit_string_type_name '#' hex_integer {$$ = new bit_string_literal_c($1, $3, locf(@1), locl(@3));} /* 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! */ ; boolean_literal: TRUE {$$ = new boolean_literal_c(new bool_type_name_c(locloc(@$)), new boolean_true_c(locloc(@$)), locloc(@$));} | FALSE {$$ = new boolean_literal_c(new bool_type_name_c(locloc(@$)), new boolean_false_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(NULL, $3, locloc(@$));} | TIME '#' '-' interval {$$ = new duration_c(new neg_time_c(locloc(@$)), $4, locloc(@$));} | T_SHARP interval {$$ = new duration_c(NULL, $2, locloc(@$));} | T_SHARP '-' interval {$$ = new duration_c(new neg_time_c(locloc(@$)), $3, locloc(@$));} ; interval: days | hours | minutes | seconds | milliseconds ; integer_d: integer_d_token {$$ = new integer_c($1, locloc(@$));}; integer_h: integer_h_token {$$ = new integer_c($1, locloc(@$));}; integer_m: integer_m_token {$$ = new integer_c($1, locloc(@$));}; integer_s: integer_s_token {$$ = new integer_c($1, locloc(@$));}; integer_ms: integer_ms_token {$$ = new integer_c($1, locloc(@$));}; fixed_point_d: fixed_point_d_token {$$ = new fixed_point_c($1, locloc(@$));} | integer_d ; fixed_point_h: fixed_point_h_token {$$ = new fixed_point_c($1, locloc(@$));} | integer_h ; fixed_point_m: fixed_point_m_token {$$ = new fixed_point_c($1, locloc(@$));} | integer_m ; fixed_point_s: fixed_point_s_token {$$ = new fixed_point_c($1, locloc(@$));} | integer_s ; fixed_point_ms: fixed_point_ms_token {$$ = new fixed_point_c($1, locloc(@$));} | integer_ms ; fixed_point: fixed_point_token {$$ = new fixed_point_c($1, locloc(@$));} | integer ; days: /* fixed_point ('d') */ fixed_point_d {$$ = new days_c($1, NULL, locloc(@$));} /*| integer ('d') ['_'] hours */ | integer_d hours {$$ = new days_c($1, $2, locloc(@$));} | integer_d '_' hours {$$ = new days_c($1, $3, locloc(@$));} ; hours: /* fixed_point ('h') */ fixed_point_h {$$ = new hours_c($1, NULL, locloc(@$));} /*| integer ('h') ['_'] minutes */ | integer_h minutes {$$ = new hours_c($1, $2, locloc(@$));} | integer_h '_' minutes {$$ = new hours_c($1, $3, locloc(@$));} ; minutes: /* fixed_point ('m') */ fixed_point_m {$$ = new minutes_c($1, NULL, locloc(@$));} /*| integer ('m') ['_'] seconds */ | integer_m seconds {$$ = new minutes_c($1, $2, locloc(@$));} | integer_m '_' seconds {$$ = new minutes_c($1, $3, locloc(@$));} ; seconds: /* fixed_point ('s') */ fixed_point_s {$$ = new seconds_c($1, NULL, locloc(@$));} /*| integer ('s') ['_'] milliseconds */ | integer_s milliseconds {$$ = new seconds_c($1, $2, locloc(@$));} | integer_s '_' milliseconds {$$ = new seconds_c($1, $3, locloc(@$));} ; milliseconds: /* fixed_point ('ms') */ fixed_point_ms {$$ = new milliseconds_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($3, locloc(@$));} ; daytime: day_hour ':' day_minute ':' day_second {$$ = new daytime_c($1, $3, $5, locloc(@$));} ; day_hour: integer; day_minute: integer; day_second: fixed_point; date: DATE '#' date_literal {$$ = new date_c($3, locloc(@$));} | D_SHARP date_literal {$$ = new date_c($2, locloc(@$));} ; date_literal: year '-' month '-' day {$$ = new date_literal_c($1, $3, $5, locloc(@$));} ; year: integer; month: integer; day: integer; date_and_time: DATE_AND_TIME '#' date_literal '-' daytime {$$ = new date_and_time_c($3, $5, locloc(@$));} ; /**********************/ /* 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 */ /***********************************/ 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. */ ; 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(@$));} ; 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(@$));} ; real_type_name: REAL {$$ = new real_type_name_c(locloc(@$));} | LREAL {$$ = new lreal_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(@$));} ; 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(@$));} /* 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(@$));} ; /********************************/ /* 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 ; 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(@$));} ; /* 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);} ; 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 ; simple_type_declaration: /* simple_type_name ':' simple_spec_init */ identifier ':' simple_spec_init {$$ = new simple_type_declaration_c($1, $3, locloc(@$)); library_element_symtable.insert($1, prev_declared_simple_type_name_token); } ; 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(@$));} ; /* 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 */ identifier ':' subrange_spec_init {$$ = new subrange_type_declaration_c($1, $3, locloc(@$)); library_element_symtable.insert($1, prev_declared_subrange_type_name_token); } ; 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(@$));} ; subrange_specification: integer_type_name '(' subrange')' {$$ = new subrange_specification_c($1, $3, locloc(@$));} | prev_declared_subrange_type_name ; subrange: signed_integer DOTDOT signed_integer {$$ = new subrange_c($1, $3, locloc(@$));} ; enumerated_type_declaration: /* enumerated_type_name ':' enumerated_spec_init */ identifier ':' enumerated_spec_init {$$ = new enumerated_type_declaration_c($1, $3, locloc(@$)); library_element_symtable.insert($1, prev_declared_enumerated_type_name_token); } ; 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(@$));} ; enumerated_specification: '(' enumerated_value_list ')' {$$ = $2;} | prev_declared_enumerated_type_name ; /* 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);} ; enumerated_value: identifier | prev_declared_enumerated_type_name '#' any_identifier {$$ = new enumerated_value_c($1, $3, locloc(@$));} ; 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 */ identifier ':' array_spec_init {$$ = new array_type_declaration_c($1, $3, locloc(@$)); library_element_symtable.insert($1, prev_declared_array_type_name_token); } ; 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(@$));} ; array_specification: prev_declared_array_type_name | ARRAY '[' array_subrange_list ']' OF non_generic_type_name {$$ = new array_specification_c($3, $6, locloc(@$));} ; /* helper symbol for array_specification */ array_subrange_list: subrange {$$ = new array_subrange_list_c(locloc(@$)); $$->add_element($1);} | array_subrange_list ',' subrange {$$ = $1; $$->add_element($1);} ; array_initialization: '[' array_initial_elements_list ']' {$$ = $2;} ; /* 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);} ; array_initial_elements: array_initial_element | integer '(' ')' | integer '(' array_initial_element ')' {$$ = new array_initial_elements_c($1, $3, locloc(@$));} ; array_initial_element: constant | enumerated_value | structure_initialization | array_initialization ; structure_type_declaration: /* structure_type_name ':' structure_specification */ identifier ':' structure_specification {$$ = new structure_type_declaration_c($1, $3, locloc(@$)); library_element_symtable.insert($1, prev_declared_structure_type_name_token); } ; 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(@$));} ; structure_declaration: STRUCT structure_element_declaration_list END_STRUCT {$$ = $2;} ; /* 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);} ; structure_element_declaration: structure_element_name ':' simple_spec_init {$$ = new structure_element_declaration_c($1, $3, locloc(@$));} | structure_element_name ':' subrange_spec_init {$$ = new structure_element_declaration_c($1, $3, locloc(@$));} | structure_element_name ':' enumerated_spec_init {$$ = new structure_element_declaration_c($1, $3, locloc(@$));} | structure_element_name ':' array_spec_init {$$ = new structure_element_declaration_c($1, $3, locloc(@$));} | structure_element_name ':' initialized_structure {$$ = new structure_element_declaration_c($1, $3, locloc(@$));} ; structure_element_name: any_identifier; structure_initialization: '(' structure_element_initialization_list ')' {$$ = $2;} ; /* 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);} ; 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(@$));} ; /* 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 */ identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init {$$ = new string_type_declaration_c($1, $3, $4, $5, locloc(@$)); library_element_symtable.insert($1, prev_declared_string_type_name_token); } ; /* 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;} ; /*********************/ /* B 1.4 - Variables */ /*********************/ variable: symbolic_variable | direct_variable ; 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(@$));} | prev_declared_global_var_name {$$ = new symbolic_variable_c($1, locloc(@$));} | prev_declared_variable_name {$$ = new symbolic_variable_c($1, locloc(@$));} | multi_element_variable /* | identifier {$$ = new symbolic_variable_c($1, locloc(@$));} */ ; /* 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(@$));} | any_multi_element_variable ; /* for yet undeclared variable names ! */ variable_name: identifier; /********************************************/ /* B.1.4.1 Directly Represented Variables */ /********************************************/ direct_variable: 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(@$));} ; /* please see note above any_symbolic_variable */ any_structured_variable: any_record_variable '.' field_selector {$$ = 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; /******************************************/ /* B 1.4.3 - Declaration & Initialisation */ /******************************************/ input_declarations: VAR_INPUT input_declaration_list END_VAR {$$ = new input_declarations_c(NULL, $2, locloc(@$));} | VAR_INPUT RETAIN input_declaration_list END_VAR {$$ = new input_declarations_c(new retain_option_c(locloc(@2)), $3, locloc(@$));} | VAR_INPUT NON_RETAIN input_declaration_list END_VAR {$$ = new input_declarations_c(new non_retain_option_c(locloc(@2)), $3, locloc(@$));} ; /* 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);} ; input_declaration: var_init_decl | edge_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(@$));} ; 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: variable_name {$$ = new var1_list_c(locloc(@$)); $$->add_element($1); variable_name_symtable.insert($1, prev_declared_variable_name_token); } | var1_list ',' variable_name {$$ = $1; $$->add_element($3); variable_name_symtable.insert($3, prev_declared_variable_name_token); } ; array_var_init_decl: var1_list ':' array_spec_init {$$ = new array_var_init_decl_c($1, $3, locloc(@$));} ; structured_var_init_decl: var1_list ':' initialized_structure {$$ = new structured_var_init_decl_c($1, $3, locloc(@$));} ; /* 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, $2, NULL, 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, $2, $4, locloc(@$));} ; /* 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_init_decl_list END_VAR {$$ = new output_declarations_c(NULL, $2, locloc(@$));} | VAR_OUTPUT RETAIN var_init_decl_list END_VAR {$$ = new output_declarations_c(new retain_option_c(locloc(@2)), $3, locloc(@$));} | VAR_OUTPUT NON_RETAIN var_init_decl_list END_VAR {$$ = new output_declarations_c(new non_retain_option_c(locloc(@2)), $3, locloc(@$));} ; input_output_declarations: VAR_IN_OUT var_declaration_list END_VAR {$$ = new input_output_declarations_c($2, locloc(@$));} ; /* 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);} ; 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(@$));} ; array_var_declaration: var1_list ':' array_specification {$$ = new array_var_declaration_c($1, $3, locloc(@$));} ; structured_var_declaration: var1_list ':' prev_declared_structure_type_name {$$ = new structured_var_declaration_c($1, $3, locloc(@$));} ; 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(@$));} ; retentive_var_declarations: VAR RETAIN var_init_decl_list END_VAR {$$ = new retentive_var_declarations_c($3, locloc(@$));} ; 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(@$));} ; /* 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);} ; 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(@$));} ; 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(@$));} ; /* 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);} ; 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, $3, locloc(@$)); variable_name_symtable.insert($1, prev_declared_fb_name_token); } ; 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(@$));} ; /* 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);} ; global_var_decl: 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, $3, locloc(@$));} ; 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 ; location: AT direct_variable {$$ = new location_c($2, locloc(@$));} ; 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); } ; 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(@$));} ; /* 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($3, NULL, locloc(@$));} /* | STRING ASSIGN single_byte_character_string {$$ = new single_byte_string_spec_c(NULL, $3, locloc(@$));} */ | STRING '[' integer ']' ASSIGN single_byte_character_string {$$ = new single_byte_string_spec_c($3, $6, locloc(@$));} ; double_byte_string_var_declaration: var1_list ':' double_byte_string_spec {$$ = new double_byte_string_var_declaration_c($1, $3, locloc(@$));} ; double_byte_string_spec: /* WSTRING {$$ = new double_byte_string_spec_c(NULL, NULL, locloc(@$));} */ WSTRING '[' integer ']' {$$ = new double_byte_string_spec_c($3, NULL, locloc(@$));} /* | WSTRING ASSIGN double_byte_character_string {$$ = new double_byte_string_spec_c(NULL, $3, locloc(@$));} */ | WSTRING '[' integer ']' ASSIGN double_byte_character_string {$$ = new double_byte_string_spec_c($3, $6, locloc(@$));} ; 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(@$));} ; /* 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);} ; incompl_located_var_decl: variable_name incompl_location ':' var_spec {$$ = new incompl_located_var_decl_c($1, $2, $4, locloc(@$));} ; 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 */ /* NOTE: The constructs * * STRING * and * WSTRING * * were removed as they are already contained * within a simple_specification. */ string_spec: /* STRING {$$ = new single_byte_string_spec_c(NULL, NULL, locloc(@$));} */ STRING '[' integer ']' {$$ = new single_byte_string_spec_c($3, NULL, locloc(@$));} /* | WSTRING {$$ = new double_byte_string_spec_c(NULL, NULL, locloc(@$));} */ | WSTRING '[' integer ']' {$$ = new double_byte_string_spec_c($3, NULL, locloc(@$));} ; /* intermediate helper symbol for: * - non_retentive_var_decls * - output_declarations * - 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);} ; /***********************/ /* 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: standard_function_name_token {$$ = new 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: NOT {$$ = new 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: AND_operator {$$ = il_operator_c_2_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_identifier_c($1);} | OR_operator {$$ = il_operator_c_2_identifier_c($1);} | XOR_operator {$$ = il_operator_c_2_identifier_c($1);} | ADD_operator {$$ = il_operator_c_2_identifier_c($1);} | SUB_operator {$$ = il_operator_c_2_identifier_c($1);} | MUL_operator {$$ = il_operator_c_2_identifier_c($1);} | DIV_operator {$$ = il_operator_c_2_identifier_c($1);} | MOD_operator {$$ = il_operator_c_2_identifier_c($1);} | GT_operator {$$ = il_operator_c_2_identifier_c($1);} | GE_operator {$$ = il_operator_c_2_identifier_c($1);} | EQ_operator {$$ = il_operator_c_2_identifier_c($1);} | LT_operator {$$ = il_operator_c_2_identifier_c($1);} | LE_operator {$$ = il_operator_c_2_identifier_c($1);} | NE_operator {$$ = il_operator_c_2_identifier_c($1);} ; derived_function_name: identifier | prev_declared_derived_function_name {$$ = $1; if (not(allow_function_overloading)) { fprintf(stderr, "Function overloading not allowed. Invalid identifier %s\n", ((token_c *)($1))->value); ERROR; } } ; function_declaration: /* FUNCTION derived_function_name ':' elementary_type_name io_OR_function_var_declarations_list function_body END_FUNCTION */ 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(@$)); variable_name_symtable.pop(); if (allow_function_overloading) { switch (library_element_symtable.find_value($1)) { case prev_declared_derived_function_name_token: /* do nothing, already in map. */ break; case BOGUS_TOKEN_ID: /* Not yet in map. Must insert...*/ library_element_symtable.insert($1, prev_declared_derived_function_name_token); break; default: /* Already in map but associated with something else other than a funtion name! */ ERROR; } } else { 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(@$)); variable_name_symtable.pop(); if (allow_function_overloading) { switch (library_element_symtable.find_value($1)) { case prev_declared_derived_function_name_token: /* do nothing, already in map. */ break; case BOGUS_TOKEN_ID: library_element_symtable.insert($1, prev_declared_derived_function_name_token); break; default: ERROR; } } else { library_element_symtable.insert($1, prev_declared_derived_function_name_token); } } /* ERROR_CHECK_BEGIN */ | FUNCTION error END_FUNCTION {$$ = NULL; print_err_msg(current_filename, @2.first_line, "error in function declaration."); /* yychar */ 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 {$$ = $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); } ; /* intermediate helper symbol for function_declaration */ io_OR_function_var_declarations_list: /* empty */ {$$ = new var_declarations_list_c(locloc(@$));} | 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);} ; 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(@$));} ; /* 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);} ; function_body: statement_list {$$ = $1;} /* if we leave it for the default action we get a type clash! */ | instruction_list {$$ = $1;} /* if we leave it for the default action we get a type clash! */ /* | 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: 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); /* 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(); } /* ERROR_CHECK_BEGIN */ | FUNCTION_BLOCK error END_FUNCTION_BLOCK {$$ = NULL; print_err_msg(current_filename, @2.first_line, "error in function block declaration."); /* yychar */ 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: /* empty */ {$$ = new var_declarations_list_c(locloc(@$));} | 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);} ; /* 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(@$));} ; /* 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);} ; non_retentive_var_decls: VAR NON_RETAIN var_init_decl_list END_VAR {$$ = new non_retentive_var_decls_c($3, locloc(@$));} ; function_block_body: statement_list {$$ = $1;} | instruction_list {$$ = $1;} | sequential_function_chart {$$ = $1;} /* | ladder_diagram | function_block_diagram | */ ; /**********************/ /* B 1.5.3 - Programs */ /**********************/ program_type_name: identifier; program_declaration: PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM {$$ = new program_declaration_c($2, $3, $4, locloc(@$)); library_element_symtable.insert($2, prev_declared_program_type_name_token); /* 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(); } /* ERROR_CHECK_BEGIN */ | PROGRAM error END_PROGRAM {$$ = NULL; print_err_msg(current_filename, @2.first_line, "error in function block declaration."); /* yychar */ yyerrok; } /* ERROR_CHECK_END */ ; /* helper symbol for program_declaration */ /* * NOTE: we re-use the var_declarations_list_c */ program_var_declarations_list: /* empty */ {$$ = new var_declarations_list_c(locloc(@$));} | 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);} */ ; /* 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);} ; 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(@$));} ; step: STEP step_name ':' action_association_list END_STEP // STEP identifier ':' action_association_list END_STEP {$$ = new step_c($2, $4, locloc(@$));} ; /* 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);} ; // 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(@$));} ; /* helper symbol for action_association */ indicator_name_list: /* empty */ {$$ = new indicator_name_list_c(locloc(@$));} | indicator_name_list ',' indicator_name {$$ = $1; $$->add_element($3);} ; // 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(@$));} ; qualifier: N {$$ = new qualifier_c(strdup("N"), locloc(@$));} /* NOTE: the following two clash with the R and S IL operators. * It will have to be handled when we include parsing of SFC... */ | R {$$ = new qualifier_c(strdup("R"), locloc(@$));} | S {$$ = new qualifier_c(strdup("S"), locloc(@$));} | P {$$ = new qualifier_c(strdup("P"), 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(@$));} ; action_time: duration | 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(@$));} ; 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);} ; transition_header: TRANSITION FROM steps TO steps {$$.first = NULL; $$.second = NULL; $$.third = $3; $$.fourth = $5; cmd_goto_body_state();} | TRANSITION transition_name FROM steps TO steps //| TRANSITION identifier FROM steps TO steps {$$.first = $2; $$.second = NULL; $$.third = $4; $$.fourth = $6; cmd_goto_body_state();} | TRANSITION '(' PRIORITY ASSIGN integer ')' FROM steps TO steps {$$.first = NULL; $$.second = $5; $$.third = $8; $$.fourth = $10; cmd_goto_body_state();} | TRANSITION transition_name '(' PRIORITY ASSIGN integer ')' FROM steps TO steps //| TRANSITION identifier '(' PRIORITY ASSIGN integer ')' FROM steps TO steps {$$.first = $2; $$.second = $6; $$.third = $9; $$.fourth = $11; cmd_goto_body_state();} ; transition_condition_il: ':' eol_list simple_instr_list {$$ = $3;} ; transition_condition_st: ASSIGN expression ';' {$$ = $2} ; /* TODO: Can't we clean this up a bit ? */ transition: transition_header transition_condition_il END_TRANSITION {$$ = new transition_c($1.first, $1.second, $1.third, $1.fourth, $2, NULL, locloc(@$));} /* TODO: free the memory used up by the no longer used object $1 */ | transition_header transition_condition_st END_TRANSITION {$$ = new transition_c($1.first, $1.second, $1.third, $1.fourth, NULL, $2, locloc(@$));} ; action_header: ACTION action_name ':' // ACTION identifier ':' {$$.first = $2; cmd_goto_body_state();} ; action: action_header function_block_body END_ACTION {$$ = new action_c($1.first, $2, locloc(@$));} ; /********************************/ /* 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_delcared_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_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: CONFIGURATION configuration_name optional_global_var_declarations single_resource_declaration {variable_name_symtable.pop();} optional_access_declarations optional_instance_specific_initializations END_CONFIGURATION {$$ = new configuration_declaration_c($2, $3, $4, $6, $7, locloc(@$)); library_element_symtable.insert($2, prev_declared_configuration_name_token); variable_name_symtable.pop(); } | CONFIGURATION configuration_name optional_global_var_declarations resource_declaration_list optional_access_declarations optional_instance_specific_initializations END_CONFIGURATION {$$ = new configuration_declaration_c($2, $3, $4, $5, $6, locloc(@$)); library_element_symtable.insert($2, prev_declared_configuration_name_token); variable_name_symtable.pop(); } /* ERROR_CHECK_BEGIN */ | CONFIGURATION error END_CONFIGURATION {$$ = NULL; print_err_msg(current_filename, @2.first_line, "error in configuration declaration."); /* yychar */ yyerrok; } /* ERROR_CHECK_END */ ; // helper symbol for // - configuration_declaration // - resource_declaration // optional_global_var_declarations: // empty {$$ = NULL;} | global_var_declarations ; // 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);} ; resource_declaration: RESOURCE {variable_name_symtable.push();} resource_name ON resource_type_name optional_global_var_declarations single_resource_declaration END_RESOURCE {$$ = new resource_declaration_c($3, $5, $6, $7, locloc(@$)); variable_name_symtable.pop(); variable_name_symtable.insert($3, prev_declared_resource_name_token); } ; 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);} ; // 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);} ; resource_name: identifier; /* access_declarations: VAR_ACCESS access_declaration_list END_VAR {$$ = NULL;} ; // helper symbol for access_declarations // access_declaration_list: access_declaration ';' | access_declaration_list access_declaration ';' ; access_declaration: access_name ':' access_path ':' non_generic_type_name | access_name ':' access_path ':' non_generic_type_name direction ; access_path: direct_variable | prev_delcared_resource_name '.' direct_variable | any_fb_name_list symbolic_variable | prev_delcared_resource_name '.' any_fb_name_list symbolic_variable | prev_delcared_program_name '.' any_fb_name_list symbolic_variable | prev_delcared_resource_name '.' prev_delcared_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(@$));} ; /* 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;} ; task_initialization_interval: // [INTERVAL ASSIGN data_source ','] /* empty */ {$$ = NULL;} | INTERVAL ASSIGN {cmd_pop_state();} data_source ',' {cmd_goto_task_init_state();} {$$ = $4;} ; task_initialization_priority: // PRIORITY ASSIGN integer PRIORITY ASSIGN {cmd_pop_state();} integer {$$ = $4;} ; data_source: constant | global_var_reference | program_output_reference | 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); } ; // helper symbol for program_configuration // optional_task_name: // empty // {$$ = NULL;} | WITH task_name {$$ = $2;} ; // helper symbol for program_configuration // optional_prog_conf_elements: // empty // {$$ = NULL;} | '(' prog_conf_elements ')' {$$ = $2;} ; 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);} ; 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(@$));} ; /* 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(@$));} ; prog_data_source: constant | enumerated_value | global_var_reference | direct_variable ; data_sink: global_var_reference | direct_variable ; instance_specific_initializations: VAR_CONFIG instance_specific_init_list END_VAR {$$ = new instance_specific_initializations_c($2, locloc(@$));} ; // 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);} ; 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(@$));} ; /***********************************/ /* 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 ; /* eol_list: '\n' | eol_list '\n' ; */ instruction_list: il_instruction {$$ = new instruction_list_c(locloc(@$)); $$->add_element($1);} | pragma eol_list {$$ = new instruction_list_c(locloc(@$)); $$->add_element($1);} | instruction_list il_instruction {$$ = $1; $$->add_element($2);} | instruction_list pragma {$$ = $1; $$->add_element($2);} ; il_instruction: il_incomplete_instruction eol_list {$$ = new il_instruction_c(NULL, $1, locloc(@$));} | label ':' il_incomplete_instruction eol_list {$$ = new il_instruction_c($1, $3, locloc(@$));} /* ERROR_CHECK_BEGIN */ | error eol_list {$$ = NULL; print_err_msg(current_filename, @1.first_line, "error in IL instruction."); yyerrok; } /* ERROR_CHECK_END */ /* ERROR_CHECK_BEGIN */ | label ':' error eol_list {$$ = NULL; print_err_msg(current_filename, @1.first_line, "error in 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 {$$ = new il_simple_operation_c($1, NULL, 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.first, $1.second, locloc(@$));} /* TODO: free the memory used up by the no longer used $1 object! */ /* I don't do it now because I would have to test the change, and am * currently frying bigger fish... (Mario) /* free($1); */ /* 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(@$));} /* 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! * * 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. * * I (Mario) have chosen to reduce it to an operand, rather than a function call. * * 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(@$));} | il_simple_operator_clash_il_operand ',' il_operand_list {list_c *list = new il_operand_list_c(locloc(@$)); list->add_element($1.second); FOR_EACH_ELEMENT(elem, $3, {list->add_element(elem);}) $$ = new il_function_call_c(il_operator_c_2_identifier_c($1.first), list, locloc(@$)); /* TODO: free the memory used up by the no longer used $3 object! */ /* I don't do it now because I would have to test the change, and am * currently frying bigger fish... (Mario) */ /* free($3); */ } ; il_simple_operator_clash_il_operand: il_simple_operator_clash il_operand {$$.first = $1; $$.second = $2;} ; il_expression: il_expr_operator_noclash '(' eol_list ')' {$$ = new il_expression_c($1, NULL, NULL, locloc(@$));} | il_expr_operator_noclash '(' il_operand eol_list ')' {$$ = new il_expression_c($1, $3, NULL, 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 ')' {$$ = 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 ')' {$$ = new il_expression_c($1, $3, NULL, locloc(@$));} | il_expr_operator_clash '(' il_operand eol_list simple_instr_list ')' {$$ = 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(@$));} ; il_jump_operation: il_jump_operator label {$$ = new il_jump_operation_c($1, $2, locloc(@$));} ; il_fb_call: 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(@$));} ; /* NOTE: Please read note above the definition of function_name_without_clashes */ il_formal_funct_call: /* 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(@$));} | function_name_simpleop_clashes '(' eol_list ')' {$$ = new il_formal_funct_call_c($1, NULL, locloc(@$));} /* | 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(@$));} | function_name_simpleop_clashes '(' eol_list il_param_list ')' {$$ = new il_formal_funct_call_c($1, $4, locloc(@$));} /* 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_identifier_c() */ | il_expr_operator_clash_eol_list il_param_list ')' {$$ = new il_formal_funct_call_c(il_operator_c_2_identifier_c($1), $2, locloc(@$));} ; il_expr_operator_clash_eol_list: il_expr_operator_clash '(' eol_list {$$ = $1;} ; /* 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. */ il_operand: variable | enumerated_value_without_identifier | constant ; il_operand_list: il_operand {$$ = new il_operand_list_c(locloc(@$)); $$->add_element($1);} | il_operand_list ',' il_operand {$$ = $1; $$->add_element($3);} ; 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 | il_expression eol_list | il_formal_funct_call eol_list ; /* 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);} ; /* 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);} ; il_param_instruction: il_param_assignment ',' eol_list | il_param_out_assignment ',' eol_list ; il_param_last_instruction: il_param_assignment eol_list | il_param_out_assignment eol_list ; 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(@$));} ; il_param_out_assignment: il_assign_out_operator variable {$$ = new il_param_out_assignment_c($1, $2, locloc(@$));} ; /*******************/ /* 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: LD_operator | LDN_operator | ST_operator | STN_operator | S_operator | R_operator | S1_operator | R1_operator | CLK_operator | CU_operator | CD_operator | PV_operator | IN_operator | PT_operator | il_expr_operator_noclash ; il_simple_operator_clash: il_simple_operator_clash1 | il_simple_operator_clash2 ; il_simple_operator_clash1: NOT_operator ; il_simple_operator_clash2: il_expr_operator_clash ; /* 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 ; il_assign_out_operator: /* variable_name SENDTO */ /* any_identifier SENDTO */ sendto_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(@$));} ; 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 | expression OR xor_expression {$$ = new or_expression_c($1, $3, locloc(@$));} ; xor_expression: and_expression | xor_expression XOR and_expression {$$ = new xor_expression_c($1, $3, locloc(@$));} ; 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 theh AND2 token... */ | and_expression AND2 comparison {$$ = new and_expression_c($1, $3, locloc(@$));} ; 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(@$));} ; 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(@$));} ; /* 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(@$));} ; /* 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(@$));} ; /* Not required... multiply_operator: '*' | '/' | 'MOD' */ power_expression: unary_expression | power_expression OPER_EXP unary_expression {$$ = new power_expression_c($1, $3, locloc(@$));} ; unary_expression: primary_expression | '-' primary_expression {$$ = new neg_expression_c($2, locloc(@$));} | NOT primary_expression {$$ = new not_expression_c($2, locloc(@$));} ; /* 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 conlfict, we only allow constants without * a preceding '-' to be used in primary_expression */ /* 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 (or is it explicit?) that in case the same identifier id used * for a variable and an enumerated value, then the variable shall be * considered?????????????? */ primary_expression: /* constant */ non_negative_constant | enumerated_value_without_identifier | variable | '(' expression ')' {$$ = $2;} | function_invocation ; /* 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_list ')' {$$ = new function_invocation_c($1, $3, locloc(@$));} ; /********************/ /* B 3.2 Statements */ /********************/ statement_list: statement ';' {$$ = new statement_list_c(locloc(@$)); $$->add_element($1);} | pragma {$$ = new statement_list_c(locloc(@$)); $$->add_element($1);} | statement_list statement ';' {$$ = $1; $$->add_element($2);} | statement_list pragma {$$ = $1; $$->add_element($2);} /* ERROR_CHECK_BEGIN */ | statement_list error ';' {$$ = $1; print_err_msg(current_filename, @2.first_line, "error in statement."); /* yychar */ yyerrok; } /* ERROR_CHECK_END */ ; statement: assignment_statement | subprogram_control_statement | selection_statement | iteration_statement ; /*********************************/ /* B 3.2.1 Assignment Statements */ /*********************************/ assignment_statement: variable ASSIGN expression {$$ = new assignment_statement_c($1, $3, locloc(@$));} ; /*****************************************/ /* 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, locloc(@$)); } | prev_declared_fb_name '(' param_assignment_list ')' {$$ = new fb_invocation_c($1, $3, locloc(@$));} ; /* helper symbol for * - fb_invocation * - function_invocation */ param_assignment_list: param_assignment {$$ = new param_assignment_list_c(locloc(@$)); $$->add_element($1);} | param_assignment_list ',' param_assignment {$$ = $1; $$->add_element($3);} ; param_assignment: /* variable_name ASSIGN expression */ any_identifier ASSIGN expression {$$ = new input_variable_param_assignment_c($1, $3, locloc(@$));} | expression /*| variable_name SENDTO variable */ /*| any_identifier SENDTO variable */ | sendto_identifier SENDTO variable {$$ = new output_variable_param_assignment_c(NULL,$1, $3, locloc(@$));} /*| 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(@$));} ; /********************************/ /* 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(@$));} ; /* 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(@$));} ; 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(@$));} ; /* 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(@$));} ; case_list: case_list_element {$$ = new case_list_c(locloc(@$)); $$->add_element($1);} | case_list ',' case_list_element {$$ = $1; $$->add_element($3);} ; 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(@$));} ; /* 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 {$$ = $1;}; // 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(@$));} ; repeat_statement: REPEAT statement_list UNTIL expression END_REPEAT {$$ = new repeat_statement_c($2, $4, locloc(@$));} ; exit_statement: EXIT {$$ = new exit_statement_c(locloc(@$));} ; %% #include /* required for printf() */ #include #include "../util/symtable.hh" /* variables defined in code generated by flex... */ extern FILE *yyin; extern int yylineno; /* 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... */ bool allow_function_overloading = 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(); */ } void print_err_msg(const char *filename, int lineno, const char *additional_error_msg) { fprintf(stderr, "error %d: %s\n", yynerrs /* a global variable */, additional_error_msg); print_include_stack(); fprintf(stderr, "%s:%d: %s\n", filename, lineno, current_error_msg); } /* 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... */ symbol_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->last_line, il_operator->last_column ); free(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[]; int stage1_2__(const char *filename, const char *includedir, symbol_c **tree_root_ref) { FILE *in_file = NULL, *lib_file = NULL; char *libfilename = NULL; if((in_file = fopen(filename, "r")) == NULL) { char *errmsg = strdup2("Error opening main file ", filename); perror(errmsg); free(errmsg); return -1; } if (includedir != NULL) { INCLUDE_DIRECTORIES[0] = includedir; } if ((libfilename = strdup3(INCLUDE_DIRECTORIES[0], "/", LIBFILE)) == NULL) { fprintf (stderr, "Out of memory. Bailing out!\n"); return -1; } if((lib_file = fopen(libfilename, "r")) == NULL) { char *errmsg = strdup2("Error opening library file ", libfilename); perror(errmsg); free(errmsg); } if (lib_file == NULL) { /* we give up... */ free(libfilename); fclose(in_file); return -1; } /* first parse the standard library file... */ yyin = lib_file; yylineno = 1; allow_function_overloading = true; current_filename = libfilename; if (yyparse() != 0) ERROR; if (yynerrs > 0) { fprintf (stderr, "\nFound %d error(s) in %s. Bailing out!\n", yynerrs /* global variable */, libfilename); ERROR; } free(libfilename); fclose(lib_file); /* 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_names[i] != NULL; i++) if (library_element_symtable.find_value(standard_function_names[i]) == library_element_symtable.end_value()) library_element_symtable.insert(standard_function_names[i], standard_function_name_token); for(int i = 0; standard_function_block_names[i] != NULL; i++) if (library_element_symtable.find_value(standard_function_block_names[i]) == library_element_symtable.end_value()) library_element_symtable.insert(standard_function_block_names[i], standard_function_block_name_token); #if YYDEBUG yydebug = 1; #endif /* now parse the input file... */ yyin = in_file; yylineno = 1; allow_function_overloading = false; current_filename = filename; if (yyparse() != 0) exit(EXIT_FAILURE); if (yynerrs > 0) { fprintf (stderr, "\nFound %d error(s). Bailing out!\n", yynerrs /* global variable */); exit(EXIT_FAILURE); } if (tree_root_ref != NULL) *tree_root_ref = tree_root; fclose(in_file); return 0; }