/*
* (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 <string.h> /* required for strdup() */
/* declare the token parser generated by flex... */
int yylex(void);
/* declare the error handler defined at the end of this file */
void yyerror (const char *error_msg);
/* produce a more verbose parsing error message */
#define YYERROR_VERBOSE
/* Include debuging code.
* Printing of debug info must then be activated by setting
* the variable yydebug to 1.
*/
#define YYDEBUG 0
/* file with declaration of absyntax classes... */
#include "../absyntax/absyntax.hh"
/* file with declaration of token constants. Generated by bison! */
#include "iec.y.hh"
/* file with the declarations of symbol tables... */
#include "../util/symtable.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 <elem> in list_c * <list>
* execute the code <code>
*/
#define FOR_EACH_ELEMENT(elem, list, code) { \
symbol_c *elem; \
for(int i = 0; i < list->n; i++) { \
elem = list->elements[i]; \
code; \
} \
}
/* 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);
/*********************************/
/* The global symbol tables... */
/*********************************/
/* NOTE: declared static because they are not accessed
* directly by the lexical parser (flex), but rather
* through the function get_identifier_token()
*/
/* A symbol table to store all the library elements */
/* e.g.: <function_name , function_decl>
* <fb_name , fb_decl>
* <type_name , type_decl>
* <program_name , program_decl>
* <configuration_name , configuration_decl>
*/
static symtable_c<int, BOGUS_TOKEN_ID> library_element_symtable;
/* A symbol table to store the declared variables of
* the function currently being parsed...
*/
static symtable_c<int, BOGUS_TOKEN_ID> variable_name_symtable;
/*************************/
/* global variables... */
/*************************/
static symbol_c *tree_root = NULL;
/* The name of the file currently being parsed...
* Note that flex accesses and updates this global variable
* apropriately whenever it comes across an (*#include <filename> *)
* directive...
*/
const char *current_filename = NULL;
/* 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;
/************************/
/* 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);
/************************/
/* forward declarations */
/************************/
/* The functions declared here are defined in iec.flex... */
void print_include_stack(void);
%}
%union {
symbol_c *leaf;
list_c *list;
char *ID; /* token value */
struct {
symbol_c *first;
symbol_c *second;
} double_symbol; /* used by il_simple_operator_clash_il_operand */
}
/*****************************/
/* Prelimenary constructs... */
/*****************************/
%type <leaf> start
%type <leaf> any_identifier
%token <ID> prev_declared_variable_name_token
%token <ID> prev_declared_fb_name_token
%type <leaf> prev_declared_variable_name
%type <leaf> prev_declared_fb_name
%token <ID> prev_declared_simple_type_name_token
%token <ID> prev_declared_subrange_type_name_token
%token <ID> prev_declared_enumerated_type_name_token
%token <ID> prev_declared_array_type_name_token
%token <ID> prev_declared_structure_type_name_token
%token <ID> prev_declared_string_type_name_token
%type <leaf> prev_declared_simple_type_name
%type <leaf> prev_declared_subrange_type_name
%type <leaf> prev_declared_enumerated_type_name
%type <leaf> prev_declared_array_type_name
%type <leaf> prev_declared_structure_type_name
%type <leaf> prev_declared_string_type_name
%token <ID> prev_declared_derived_function_name_token
%token <ID> prev_declared_derived_function_block_name_token
%token <ID> prev_declared_program_type_name_token
%type <leaf> prev_declared_derived_function_name
%type <leaf> prev_declared_derived_function_block_name
%type <leaf> prev_declared_program_type_name
/* 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
/* The pragmas... */
%token <ID> pragma_token
%type <leaf> pragma
/* Where do these tokens belong ?? */
/* TODO: get the syntax parser to handle these tokens... */
%token EN
%token ENO
/***************************/
/* B 0 - Programming Model */
/***************************/
%type <list> library
%type <leaf> library_element_declaration
/*******************************************/
/* B 1.1 - Letters, digits and identifiers */
/*******************************************/
/* Done totally within flex...
letter
digit
octal_digit
hex_digit
*/
%token <ID> identifier_token
%type <leaf> identifier
/*********************/
/* B 1.2 - Constants */
/*********************/
%type <leaf> constant
/* a helper symbol for expression */
%type <leaf> non_negative_constant
/******************************/
/* B 1.2.1 - Numeric Literals */
/******************************/
/* Done totally within flex...
bit
*/
%type <leaf> numeric_literal
/* helper symbol for non_negative_constant */
%type <leaf> non_negative_numeric_literal
%type <leaf> integer_literal
%type <leaf> signed_integer
/* a helper symbol for non_negative_constant */
%type <leaf> non_negative_signed_integer
%token <ID> integer_token
%type <leaf> integer
%token <ID> binary_integer_token
%type <leaf> binary_integer
%token <ID> octal_integer_token
%type <leaf> octal_integer
%token <ID> hex_integer_token
%type <leaf> hex_integer
%token <ID> real_token
%type <leaf> real
%type <leaf> signed_real
/* helper symbol for non_negative_real_literal */
%type <leaf> non_negative_signed_real
%type <leaf> real_literal
/* helper symbol for non_negative_numeric_literal */
%type <leaf> non_negative_real_literal
// %type <leaf> exponent
%type <leaf> bit_string_literal
%type <leaf> boolean_literal
%token FALSE
%token TRUE
/*******************************/
/* B 1.2.2 - Character Strings */
/*******************************/
%token <ID> single_byte_character_string_token
%token <ID> double_byte_character_string_token
%type <leaf> character_string
%type <leaf> single_byte_character_string
%type <leaf> double_byte_character_string
/***************************/
/* B 1.2.3 - Time Literals */
/***************************/
%type <leaf> time_literal
/************************/
/* B 1.2.3.1 - Duration */
/************************/
%type <leaf> duration
%type <leaf> interval
%type <leaf> days
%type <leaf> fixed_point
%type <leaf> hours
%type <leaf> minutes
%type <leaf> seconds
%type <leaf> milliseconds
%type <leaf> integer_d
%type <leaf> integer_h
%type <leaf> integer_m
%type <leaf> integer_s
%type <leaf> integer_ms
%type <leaf> fixed_point_d
%type <leaf> fixed_point_h
%type <leaf> fixed_point_m
%type <leaf> fixed_point_s
%type <leaf> fixed_point_ms
%token <ID> fixed_point_token
%token <ID> fixed_point_d_token
%token <ID> integer_d_token
%token <ID> fixed_point_h_token
%token <ID> integer_h_token
%token <ID> fixed_point_m_token
%token <ID> integer_m_token
%token <ID> fixed_point_s_token
%token <ID> integer_s_token
%token <ID> fixed_point_ms_token
%token <ID> integer_ms_token
%token TIME
%token T_SHARP
/************************************/
/* B 1.2.3.2 - Time of day and Date */
/************************************/
%type <leaf> time_of_day
%type <leaf> daytime
%type <leaf> day_hour
%type <leaf> day_minute
%type <leaf> day_second
%type <leaf> date
%type <leaf> date_literal
%type <leaf> year
%type <leaf> month
%type <leaf> day
%type <leaf> date_and_time
%token TIME_OF_DAY
%token DATE
%token D_SHARP
%token DATE_AND_TIME
/**********************/
/* B 1.3 - Data Types */
/**********************/
/* Strangely, the following symbol does seem to be required! */
// %type <leaf> data_type_name
%type <leaf> non_generic_type_name
/***********************************/
/* B 1.3.1 - Elementary Data Types */
/***********************************/
/* NOTES:
*
* - To make the definition of bit_string_literal more
* concise, it is useful to use an extra non-terminal
* symbol (i.e. a grouping or construct) that groups the
* following elements (BYTE, WORD, DWORD, LWORD).
* Note that the definition of bit_string_type_name
* (according to the spec) includes the above elements
* and an extra BOOL.
* We could use an extra construct with the first four
* elements to be used solely in the definition of
* bit_string_literal, but with the objective of not
* having to replicate the actions (if we ever need
* to change them, they would need to be changed in both
* bit_string_type_name and the extra grouping), we
* have re-defined bit_string_type_name as only including
* the first four elements.
* In order to have our parser implement the specification
* correctly we have augmented every occurence of
* bit_string_type_name in other rules with the BOOL
* token. Since bit_string_type_name only appears in
* the rule for elementary_type_name, this does not
* seem to be a big concession to make!
*
* - We have added a helper symbol to concentrate the
* instantiation of STRING and WSTRING into a single
* location (elementary_string_type_name).
* These two elements show up in several other rules,
* but we want to create the equivalent abstract syntax
* in a single location of this file, in order to make
* possible future changes easier to edit...
*/
%type <leaf> elementary_type_name
%type <leaf> numeric_type_name
%type <leaf> integer_type_name
%type <leaf> signed_integer_type_name
%type <leaf> unsigned_integer_type_name
%type <leaf> real_type_name
%type <leaf> date_type_name
%type <leaf> bit_string_type_name
/* helper symbol to concentrate the instantiation
* of STRING and WSTRING into a single location
*/
%type <leaf> elementary_string_type_name
%token BYTE
%token WORD
%token DWORD
%token LWORD
%token LREAL
%token REAL
%token SINT
%token INT
%token DINT
%token LINT
%token USINT
%token UINT
%token UDINT
%token ULINT
%token WSTRING
%token STRING
%token BOOL
%token TIME
%token DATE
%token DATE_AND_TIME
%token DT
%token TIME_OF_DAY
%token TOD
/********************************/
/* B 1.3.2 - Generic data types */
/********************************/
/* Strangely, the following symbol does seem to be required! */
// %type <leaf> generic_type_name
/* The following tokens do not seem to be used either
* but we declare them so they become reserved words...
*/
%token ANY
%token ANY_DERIVED
%token ANY_ELEMENTARY
%token ANY_MAGNITUDE
%token ANY_NUM
%token ANY_REAL
%token ANY_INT
%token ANY_BIT
%token ANY_STRING
%token ANY_DATE
/********************************/
/* B 1.3.3 - Derived data types */
/********************************/
%type <leaf> derived_type_name
%type <leaf> single_element_type_name
// %type <leaf> simple_type_name
// %type <leaf> subrange_type_name
// %type <leaf> enumerated_type_name
// %type <leaf> array_type_name
// %type <leaf> structure_type_name
%type <leaf> data_type_declaration
/* helper symbol for data_type_declaration */
%type <list> type_declaration_list
%type <leaf> type_declaration
%type <leaf> single_element_type_declaration
%type <leaf> simple_type_declaration
%type <leaf> simple_spec_init
%type <leaf> simple_specification
%type <leaf> subrange_type_declaration
%type <leaf> subrange_spec_init
%type <leaf> subrange_specification
%type <leaf> subrange
%type <leaf> enumerated_type_declaration
%type <leaf> enumerated_spec_init
%type <leaf> enumerated_specification
/* helper symbol for enumerated_value */
%type <list> enumerated_value_list
%type <leaf> enumerated_value
%type <leaf> array_type_declaration
%type <leaf> array_spec_init
%type <leaf> array_specification
/* helper symbol for array_specification */
%type <list> array_subrange_list
%type <leaf> array_initialization
/* helper symbol for array_initialization */
%type <list> array_initial_elements_list
%type <leaf> array_initial_elements
%type <leaf> array_initial_element
%type <leaf> structure_type_declaration
%type <leaf> structure_specification
%type <leaf> initialized_structure
%type <leaf> structure_declaration
/* helper symbol for structure_declaration */
%type <list> structure_element_declaration_list
%type <leaf> structure_element_declaration
%type <leaf> structure_element_name
%type <leaf> structure_initialization
/* helper symbol for structure_initialization */
%type <list> structure_element_initialization_list
%type <leaf> structure_element_initialization
//%type <leaf> string_type_name
%type <leaf> string_type_declaration
/* helper symbol for string_type_declaration */
%type <leaf> string_type_declaration_size
/* helper symbol for string_type_declaration */
%type <leaf> string_type_declaration_init
%token ASSIGN
%token DOTDOT /* ".." */
%token TYPE
%token END_TYPE
%token ARRAY
%token OF
%token STRUCT
%token END_STRUCT
/*********************/
/* B 1.4 - Variables */
/*********************/
%type <leaf> variable
%type <leaf> symbolic_variable
/* helper symbol for prog_cnxn */
%type <leaf> any_symbolic_variable
%type <leaf> variable_name
/********************************************/
/* B.1.4.1 Directly Represented Variables */
/********************************************/
/* Done totally within flex...
location_prefix
size_prefix
*/
%token <ID> direct_variable_token
%type <leaf> direct_variable
/*************************************/
/* B.1.4.2 Multi-element Variables */
/*************************************/
%type <leaf> multi_element_variable
/* helper symbol for any_symbolic_variable */
%type <leaf> any_multi_element_variable
%type <leaf> array_variable
/* helper symbol for any_symbolic_variable */
%type <leaf> any_array_variable
%type <leaf> subscripted_variable
/* helper symbol for any_symbolic_variable */
%type <leaf> any_subscripted_variable
%type <list> subscript_list
%type <leaf> subscript
%type <leaf> structured_variable
/* helper symbol for any_symbolic_variable */
%type <leaf> any_structured_variable
%type <leaf> record_variable
/* helper symbol for any_symbolic_variable */
%type <leaf> any_record_variable
%type <leaf> field_selector
/******************************************/
/* B 1.4.3 - Declaration & Initialisation */
/******************************************/
%type <leaf> input_declarations
/* helper symbol for input_declarations */
%type <list> input_declaration_list
%type <leaf> input_declaration
%type <leaf> edge_declaration
%type <leaf> var_init_decl
%type <leaf> var1_init_decl
%type <list> var1_list
%type <leaf> array_var_init_decl
%type <leaf> structured_var_init_decl
%type <leaf> fb_name_decl
/* helper symbol for fb_name_decl */
%type <list> fb_name_list_with_colon
/* helper symbol for fb_name_list_with_colon */
%type <list> var1_list_with_colon
// %type <list> fb_name_list
// %type <leaf> fb_name
%type <leaf> output_declarations
%type <leaf> input_output_declarations
/* helper symbol for input_output_declarations */
%type <list> var_declaration_list
%type <leaf> var_declaration
%type <leaf> temp_var_decl
%type <leaf> var1_declaration
%type <leaf> array_var_declaration
%type <leaf> structured_var_declaration
%type <leaf> var_declarations
%type <leaf> retentive_var_declarations
%type <leaf> located_var_declarations
/* helper symbol for located_var_declarations */
%type <list> located_var_decl_list
%type <leaf> located_var_decl
%type <leaf> external_var_declarations
/* helper symbol for external_var_declarations */
%type <list> external_declaration_list
%type <leaf> external_declaration
%type <leaf> global_var_name
%type <leaf> global_var_declarations
/* helper symbol for global_var_declarations */
%type <list> global_var_decl_list
%type <leaf> global_var_decl
%type <leaf> global_var_spec
%type <leaf> located_var_spec_init
%type <leaf> location
%type <list> global_var_list
%type <leaf> string_var_declaration
%type <leaf> single_byte_string_var_declaration
%type <leaf> single_byte_string_spec
%type <leaf> double_byte_string_var_declaration
%type <leaf> double_byte_string_spec
%type <leaf> incompl_located_var_declarations
/* helper symbol for incompl_located_var_declarations */
%type <list> incompl_located_var_decl_list
%type <leaf> incompl_located_var_decl
%type <leaf> incompl_location
%type <leaf> var_spec
/* helper symbol for var_spec */
%type <leaf> string_spec
/* intermediate helper symbol for:
* - non_retentive_var_decls
* - output_declarations
*/
%type <list> var_init_decl_list
%token <ID> incompl_location_token
%token VAR_INPUT
%token VAR_OUTPUT
%token VAR_IN_OUT
%token VAR_EXTERNAL
%token VAR_GLOBAL
%token END_VAR
%token RETAIN
%token NON_RETAIN
%token R_EDGE
%token F_EDGE
%token AT
/***********************/
/* B 1.5.1 - Functions */
/***********************/
//%type <leaf> function_name
/* helper symbol for IL language */
%type <leaf> function_name_no_clashes
%type <leaf> function_name_simpleop_clashes
//%type <leaf> function_name_expression_clashes
/* helper symbols for ST language */
//%type <leaf> function_name_NOT_clashes
%type <leaf> function_name_no_NOT_clashes
//%type <leaf> standard_function_name
/* helper symbols for IL language */
%type <leaf> standard_function_name_no_clashes
%type <leaf> standard_function_name_simpleop_clashes
%type <leaf> standard_function_name_expression_clashes
/* helper symbols for ST language */
%type <leaf> standard_function_name_NOT_clashes
%type <leaf> standard_function_name_no_NOT_clashes
%type <leaf> derived_function_name
%type <leaf> function_declaration
/* helper symbol for function_declaration */
%type <leaf> function_name_declaration
%type <leaf> io_var_declarations
%type <leaf> function_var_decls
%type <leaf> function_body
%type <leaf> var2_init_decl
/* intermediate helper symbol for function_declaration */
%type <list> io_OR_function_var_declarations_list
/* intermediate helper symbol for function_var_decls */
%type <list> var2_init_decl_list
%token <ID> standard_function_name_token
%token FUNCTION
%token END_FUNCTION
%token CONSTANT
/*****************************/
/* B 1.5.2 - Function Blocks */
/*****************************/
%type <leaf> function_block_type_name
%type <leaf> standard_function_block_name
%type <leaf> derived_function_block_name
%type <leaf> function_block_declaration
%type <leaf> other_var_declarations
%type <leaf> temp_var_decls
%type <leaf> non_retentive_var_decls
%type <leaf> function_block_body
/* intermediate helper symbol for function_declaration */
%type <list> io_OR_other_var_declarations_list
/* intermediate helper symbol for temp_var_decls */
%type <list> temp_var_decls_list
%token <ID> standard_function_block_name_token
%token FUNCTION_BLOCK
%token END_FUNCTION_BLOCK
%token VAR_TEMP
%token END_VAR
%token VAR
%token NON_RETAIN
%token END_VAR
/**********************/
/* B 1.5.3 - Programs */
/**********************/
%type <leaf> program_type_name
%type <leaf> program_declaration
/* helper symbol for program_declaration */
%type <list> program_var_declarations_list
%token PROGRAM
%token END_PROGRAM
/********************************************/
/* B 1.6 Sequential Function Chart elements */
/********************************************/
/* TODO */
%type <list> sequential_function_chart
%type <list> sfc_network
%type <leaf> initial_step
%type <leaf> step
%type <list> action_association_list
%type <leaf> step_name
%type <leaf> action_association
/* helper symbol for action_association */
%type <list> indicator_name_list
%type <leaf> action_name
%type <leaf> action_qualifier
%type <leaf> qualifier
%type <leaf> timed_qualifier
%type <leaf> action_time
%type <leaf> indicator_name
%type <leaf> transition
%type <leaf> steps
%type <list> step_name_list
%type <leaf> transition_condition
%type <leaf> action
%type <leaf> transition_name
%token ASSIGN
%token ACTION
%token END_ACTION
%token TRANSITION
%token END_TRANSITION
%token FROM
%token TO
%token PRIORITY
%token INITIAL_STEP
%token STEP
%token END_STEP
%token L
%token D
%token SD
%token DS
%token SL
%token N
%token P
/* NOTE: the following two clash with the R and S IL operators.
* It will have to be handled when we include parsing of SFC...
*/
/*
%token R
%token S
*/
/********************************/
/* B 1.7 Configuration elements */
/********************************/
%type <leaf> configuration_name
%type <leaf> resource_type_name
%type <leaf> configuration_declaration
// helper symbol for
// - configuration_declaration
// - resource_declaration
//
%type <leaf> optional_global_var_declarations
// helper symbol for configuration_declaration
%type <leaf> optional_access_declarations
// helper symbol for configuration_declaration
%type <leaf> optional_instance_specific_initializations
// helper symbol for configuration_declaration
%type <list> resource_declaration_list
%type <leaf> resource_declaration
%type <leaf> single_resource_declaration
// helper symbol for single_resource_declaration
%type <list> task_configuration_list
// helper symbol for single_resource_declaration
%type <list> program_configuration_list
%type <leaf> resource_name
// %type <leaf> access_declarations
// helper symbol for access_declarations
// %type <leaf> access_declaration_list
// %type <leaf> access_declaration
// %type <leaf> access_path
// helper symbol for access_path
%type <list> any_fb_name_list
%type <leaf> global_var_reference
// %type <leaf> access_name
%type <leaf> program_output_reference
%type <leaf> program_name
// %type <leaf> direction
%type <leaf> task_configuration
%type <leaf> task_name
%type <leaf> task_initialization
%type <leaf> data_source
%type <leaf> program_configuration
// helper symbol for program_configuration
%type <leaf> optional_task_name
// helper symbol for program_configuration
%type <leaf> optional_prog_conf_elements
%type <list> prog_conf_elements
%type <leaf> prog_conf_element
%type <leaf> fb_task
%type <leaf> prog_cnxn
%type <leaf> prog_data_source
%type <leaf> data_sink
%type <leaf> instance_specific_initializations
// helper symbol for instance_specific_initializations
%type <list> instance_specific_init_list
%type <leaf> instance_specific_init
// helper symbol for instance_specific_init
%type <leaf> fb_initialization
%type <leaf> prev_declared_global_var_name
%token <ID> prev_declared_global_var_name_token
%type <leaf> prev_declared_program_name
%token <ID> prev_declared_program_name_token
%type <leaf> prev_declared_resource_name
%token <ID> prev_declared_resource_name_token
%token <ID> prev_declared_configuration_name_token
// %type <leaf> prev_declared_task_name
// %token <ID> prev_declared_task_name_token
%token CONFIGURATION
%token END_CONFIGURATION
%token TASK
%token RESOURCE
%token ON
%token END_RESOURCE
%token VAR_CONFIG
%token VAR_ACCESS
%token END_VAR
%token WITH
%token PROGRAM
%token RETAIN
%token NON_RETAIN
%token PRIORITY
%token SINGLE
%token INTERVAL
%token READ_WRITE
%token READ_ONLY
/***********************************/
/* B 2.1 Instructions and Operands */
/***********************************/
%type <list> instruction_list
%type <leaf> il_instruction
%type <leaf> il_incomplete_instruction
%type <leaf> label
%type <leaf> il_simple_operation
// helper symbol for il_simple_operation
%type <double_symbol> il_simple_operator_clash_il_operand
%type <leaf> il_expression
%type <leaf> il_jump_operation
%type <leaf> il_fb_call
%type <leaf> il_formal_funct_call
// helper symbol for il_formal_funct_call
%type <leaf> il_expr_operator_clash_eol_list
%type <leaf> il_operand
%type <list> il_operand_list
%type <list> simple_instr_list
%type <leaf> il_simple_instruction
%type <list> il_param_list
%type <list> il_param_instruction_list
%type <leaf> il_param_instruction
%type <leaf> il_param_last_instruction
%type <leaf> il_param_assignment
%type <leaf> il_param_out_assignment
%token EOL
/*******************/
/* B 2.2 Operators */
/*******************/
%token <ID> sendto_identifier_token
%type <leaf> sendto_identifier
%type <leaf> LD_operator
%type <leaf> LDN_operator
%type <leaf> ST_operator
%type <leaf> STN_operator
%type <leaf> NOT_operator
%type <leaf> S_operator
%type <leaf> R_operator
%type <leaf> S1_operator
%type <leaf> R1_operator
%type <leaf> CLK_operator
%type <leaf> CU_operator
%type <leaf> CD_operator
%type <leaf> PV_operator
%type <leaf> IN_operator
%type <leaf> PT_operator
%type <leaf> AND_operator
%type <leaf> AND2_operator
%type <leaf> OR_operator
%type <leaf> XOR_operator
%type <leaf> ANDN_operator
%type <leaf> ANDN2_operator
%type <leaf> ORN_operator
%type <leaf> XORN_operator
%type <leaf> ADD_operator
%type <leaf> SUB_operator
%type <leaf> MUL_operator
%type <leaf> DIV_operator
%type <leaf> MOD_operator
%type <leaf> GT_operator
%type <leaf> GE_operator
%type <leaf> EQ_operator
%type <leaf> LT_operator
%type <leaf> LE_operator
%type <leaf> NE_operator
%type <leaf> CAL_operator
%type <leaf> CALC_operator
%type <leaf> CALCN_operator
%type <leaf> RET_operator
%type <leaf> RETC_operator
%type <leaf> RETCN_operator
%type <leaf> JMP_operator
%type <leaf> JMPC_operator
%type <leaf> JMPCN_operator
%type <leaf> il_simple_operator
%type <leaf> il_simple_operator_clash
%type <leaf> il_simple_operator_clash1
%type <leaf> il_simple_operator_clash2
%type <leaf> il_simple_operator_noclash
//%type <leaf> il_expr_operator
%type <leaf> il_expr_operator_clash
%type <leaf> il_expr_operator_noclash
%type <leaf> il_assign_operator
%type <leaf> il_assign_out_operator
%type <leaf> il_call_operator
%type <leaf> il_return_operator
%type <leaf> il_jump_operator
%token LD
%token LDN
%token ST
%token STN
%token NOT
%token S
%token R
%token S1
%token R1
%token CLK
%token CU
%token CD
%token PV
%token IN
%token PT
%token AND
%token AND2 /* character '&' in the source code*/
%token OR
%token XOR
%token ANDN
%token ANDN2 /* characters '&N' in the source code */
%token ORN
%token XORN
%token ADD
%token SUB
%token MUL
%token DIV
%token MOD
%token GT
%token GE
%token EQ
%token LT
%token LE
%token NE
%token CAL
%token CALC
%token CALCN
%token RET
%token RETC
%token RETCN
%token JMP
%token JMPC
%token JMPCN
%token SENDTO /* "=>" */
/***********************/
/* B 3.1 - Expressions */
/***********************/
/* NOTE:
*
* - unary_operator, multiply_operator,
* add_operator and comparison_operator
* are not required. Their values are integrated
* directly into other rules...
*/
%type <leaf> expression
%type <leaf> xor_expression
%type <leaf> and_expression
%type <leaf> comparison
%type <leaf> equ_expression
// %type <leaf> comparison_operator
%type <leaf> add_expression
// %type <leaf> add_operator
%type <leaf> term
// %type <leaf> multiply_operator
%type <leaf> power_expression
%type <leaf> unary_expression
// %type <leaf> unary_operator
%type <leaf> primary_expression
/* intermediate helper symbol for primary_expression */
%type <leaf> function_invocation
%token AND
%token XOR
%token OR
%token MOD
%token NOT
%token OPER_NE
%token OPER_GE
%token OPER_LE
%token OPER_EXP
/********************/
/* B 3.2 Statements */
/********************/
%type <list> statement_list
%type <leaf> statement
/*********************************/
/* B 3.2.1 Assignment Statements */
/*********************************/
%type <leaf> assignment_statement
%token ASSIGN /* ":=" */
/*****************************************/
/* B 3.2.2 Subprogram Control Statements */
/*****************************************/
%type <leaf> subprogram_control_statement
%type <leaf> return_statement
%type <leaf> fb_invocation
%type <leaf> param_assignment
/* helper symbol for fb_invocation */
%type <list> param_assignment_list
%token ASSIGN
%token SENDTO /* "=>" */
%token RETURN
/********************************/
/* B 3.2.3 Selection Statements */
/********************************/
%type <leaf> selection_statement
%type <leaf> if_statement
%type <leaf> case_statement
%type <leaf> case_element
%type <list> case_list
%type <leaf> case_list_element
/* helper symbol for if_statement */
%type <list> elseif_statement_list
/* helper symbol for elseif_statement_list */
%type <leaf> elseif_statement
/* helper symbol for case_statement */
%type <list> case_element_list
%token IF
%token THEN
%token ELSIF
%token ELSE
%token END_IF
%token CASE
%token OF
%token ELSE
%token END_CASE
/********************************/
/* B 3.2.4 Iteration Statements */
/********************************/
%type <leaf> iteration_statement
%type <leaf> for_statement
%type <leaf> control_variable
%type <leaf> while_statement
%type <leaf> repeat_statement
%type <leaf> exit_statement
/* Integrated directly into for_statement */
// %type <leaf> for_list
%token FOR
%token ASSIGN
%token TO
%token BY
%token DO
%token END_FOR
%token WHILE
%token DO
%token END_WHILE
%token REPEAT
%token UNTIL
%token END_REPEAT
%token EXIT
%%
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/********************************************************/
/*****************************/
/* Prelimenary constructs... */
/*****************************/
start:
library {$$ = $1;}
;
/* the pragmas... */
pragma:
pragma_token {$$ = new pragma_c($1);}
/* 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);};
prev_declared_fb_name: prev_declared_fb_name_token {$$ = new identifier_c($1);};
prev_declared_simple_type_name: prev_declared_simple_type_name_token {$$ = new identifier_c($1);};
prev_declared_subrange_type_name: prev_declared_subrange_type_name_token {$$ = new identifier_c($1);};
prev_declared_enumerated_type_name: prev_declared_enumerated_type_name_token {$$ = new identifier_c($1);};
prev_declared_array_type_name: prev_declared_array_type_name_token {$$ = new identifier_c($1);};
prev_declared_structure_type_name: prev_declared_structure_type_name_token {$$ = new identifier_c($1);};
prev_declared_string_type_name: prev_declared_string_type_name_token {$$ = new identifier_c($1);};
prev_declared_derived_function_name: prev_declared_derived_function_name_token {$$ = new identifier_c($1);};
prev_declared_derived_function_block_name: prev_declared_derived_function_block_name_token {$$ = new identifier_c($1);};
prev_declared_program_type_name: prev_declared_program_type_name_token {$$ = new identifier_c($1);};
/***************************/
/* B 0 - Programming Model */
/***************************/
library:
/* empty */
{if (tree_root == NULL)
tree_root = new library_c();
$$ = (list_c *)tree_root;
}
| library library_element_declaration
{$$ = $1; $$->add_element($2);}
| library error
{$$ = NULL;
print_err_msg(current_filename, @2.last_line, "unknown error.");
/* yychar */
yyerrok;
}
;
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...
* BUT, the lexical parser will interpret these names as tokens (keywords).
* To allow these names to be used as function names, variable names, etc...,
* I (Mario) have augmented the definition of identifier to also include the tokens
* that are not explicitly defined as keywords in the spec!!
*
*
*
*
* 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);}
/* Make sure that all tokens (names) not defined as keywords are included here...
* I (Mario) have already done this, but if any changes are made to this file,
* this list MUST be kept consistent!!
*/
/**/
| PRIORITY {$$ = new identifier_c(strdup("PRIORITY"));}
| SINGLE {$$ = new identifier_c(strdup("SINGLE"));}
| INTERVAL {$$ = new identifier_c(strdup("INTERVAL"));}
/**/
| LD_operator {$$ = il_operator_c_2_identifier_c($1);}
| LDN_operator {$$ = il_operator_c_2_identifier_c($1);}
| ST_operator {$$ = il_operator_c_2_identifier_c($1);}
| STN_operator {$$ = il_operator_c_2_identifier_c($1);}
| S_operator {$$ = il_operator_c_2_identifier_c($1);}
| R_operator {$$ = il_operator_c_2_identifier_c($1);}
| S1_operator {$$ = il_operator_c_2_identifier_c($1);}
| R1_operator {$$ = il_operator_c_2_identifier_c($1);}
| CLK_operator {$$ = il_operator_c_2_identifier_c($1);}
| CU_operator {$$ = il_operator_c_2_identifier_c($1);}
| CD_operator {$$ = il_operator_c_2_identifier_c($1);}
| PV_operator {$$ = il_operator_c_2_identifier_c($1);}
| IN_operator {$$ = il_operator_c_2_identifier_c($1);}
| PT_operator {$$ = il_operator_c_2_identifier_c($1);}
| ANDN_operator {$$ = il_operator_c_2_identifier_c($1);}
/* NOTE: ANDN2_operator corresponds to the string '&N' in the source code!
* This is __not__ a valid name, so it is omitted from this list!!
*| ANDN2_operator {$$ = il_operator_c_2_identifier_c($1);}
*/
/* NOTE: 'AND' is a keyword, so should not appear on this list... */
| ORN_operator {$$ = il_operator_c_2_identifier_c($1);}
| XORN_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);}
| 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);}
| CAL_operator {$$ = il_operator_c_2_identifier_c($1);}
| CALC_operator {$$ = il_operator_c_2_identifier_c($1);}
| CALCN_operator {$$ = il_operator_c_2_identifier_c($1);}
| RET_operator {$$ = il_operator_c_2_identifier_c($1);}
| RETC_operator {$$ = il_operator_c_2_identifier_c($1);}
| RETCN_operator {$$ = il_operator_c_2_identifier_c($1);}
| JMP_operator {$$ = il_operator_c_2_identifier_c($1);}
| JMPC_operator {$$ = il_operator_c_2_identifier_c($1);}
| JMPCN_operator {$$ = il_operator_c_2_identifier_c($1);}
;
/*********************/
/* 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);}
| fixed_point_token {$$ = new real_c($1);}
;
integer: integer_token {$$ = new integer_c($1);};
binary_integer: binary_integer_token {$$ = new binary_integer_c($1);};
octal_integer: octal_integer_token {$$ = new octal_integer_c($1);};
hex_integer: hex_integer_token {$$ = new hex_integer_c($1);};
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);}
| integer_type_name '#' binary_integer
{$$ = new integer_literal_c($1, $3);}
| integer_type_name '#' octal_integer
{$$ = new integer_literal_c($1, $3);}
| integer_type_name '#' hex_integer
{$$ = new integer_literal_c($1, $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);}
;
/* 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);}
;
/* 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);}
;
signed_real:
real
| '+' real {$$ = $2;}
| '-' real {$$ = new neg_expression_c($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);}
| bit_string_type_name '#' binary_integer
{$$ = new bit_string_literal_c($1, $3);}
| bit_string_type_name '#' octal_integer
{$$ = new bit_string_literal_c($1, $3);}
| bit_string_type_name '#' hex_integer
{$$ = new bit_string_literal_c($1, $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(),
new boolean_true_c());}
| FALSE {$$ = new boolean_literal_c(new bool_type_name_c(),
new boolean_false_c());}
/*
| 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);};
double_byte_character_string: double_byte_character_string_token
{$$ = new double_byte_character_string_c($1);};
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);}
| TIME '#' '-' interval
{$$ = new duration_c(new neg_time_c(), $4);}
| T_SHARP interval
{$$ = new duration_c(NULL, $2);}
| T_SHARP '-' interval
{$$ = new duration_c(new neg_time_c(), $3);}
;
interval:
days
| hours
| minutes
| seconds
| milliseconds
;
integer_d: integer_d_token {$$ = new integer_c($1);};
integer_h: integer_h_token {$$ = new integer_c($1);};
integer_m: integer_m_token {$$ = new integer_c($1);};
integer_s: integer_s_token {$$ = new integer_c($1);};
integer_ms: integer_ms_token {$$ = new integer_c($1);};
fixed_point_d:
fixed_point_d_token
{$$ = new fixed_point_c($1);}
| integer_d
;
fixed_point_h:
fixed_point_h_token
{$$ = new fixed_point_c($1);}
| integer_h
;
fixed_point_m:
fixed_point_m_token
{$$ = new fixed_point_c($1);}
| integer_m
;
fixed_point_s:
fixed_point_s_token
{$$ = new fixed_point_c($1);}
| integer_s
;
fixed_point_ms:
fixed_point_ms_token
{$$ = new fixed_point_c($1);}
| integer_ms
;
fixed_point:
fixed_point_token
{$$ = new fixed_point_c($1);}
| integer
;
days:
/* fixed_point ('d') */
fixed_point_d
{$$ = new days_c($1, NULL);}
/*| integer ('d') ['_'] hours */
| integer_d hours
{$$ = new days_c($1, $2);}
| integer_d '_' hours
{$$ = new days_c($1, $3);}
;
hours:
/* fixed_point ('h') */
fixed_point_h
{$$ = new hours_c($1, NULL);}
/*| integer ('h') ['_'] minutes */
| integer_h minutes
{$$ = new hours_c($1, $2);}
| integer_h '_' minutes
{$$ = new hours_c($1, $3);}
;
minutes:
/* fixed_point ('m') */
fixed_point_m
{$$ = new minutes_c($1, NULL);}
/*| integer ('m') ['_'] seconds */
| integer_m seconds
{$$ = new minutes_c($1, $2);}
| integer_m '_' seconds
{$$ = new minutes_c($1, $3);}
;
seconds:
/* fixed_point ('s') */
fixed_point_s
{$$ = new seconds_c($1, NULL);}
/*| integer ('s') ['_'] milliseconds */
| integer_s milliseconds
{$$ = new seconds_c($1, $2);}
| integer_s '_' milliseconds
{$$ = new seconds_c($1, $3);}
;
milliseconds:
/* fixed_point ('ms') */
fixed_point_ms
{$$ = new milliseconds_c($1);}
;
/************************************/
/* B 1.2.3.2 - Time of day and Date */
/************************************/
time_of_day:
TIME_OF_DAY '#' daytime
{$$ = new time_of_day_c($3);}
;
daytime:
day_hour ':' day_minute ':' day_second
{$$ = new daytime_c($1, $3, $5);}
;
day_hour: integer;
day_minute: integer;
day_second: fixed_point;
date:
DATE '#' date_literal
{$$ = new date_c($3);}
| D_SHARP date_literal
{$$ = new date_c($2);}
;
date_literal:
year '-' month '-' day
{$$ = new date_literal_c($1, $3, $5);}
;
year: integer;
month: integer;
day: integer;
date_and_time:
DATE_AND_TIME '#' date_literal '-' daytime
{$$ = new date_and_time_c($3, $5);}
;
/**********************/
/* 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();}
| BOOL {$$ = new bool_type_name_c();}
/* 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();}
| INT {$$ = new int_type_name_c();}
| DINT {$$ = new dint_type_name_c();}
| LINT {$$ = new lint_type_name_c();}
;
unsigned_integer_type_name:
USINT {$$ = new usint_type_name_c();}
| UINT {$$ = new uint_type_name_c();}
| UDINT {$$ = new udint_type_name_c();}
| ULINT {$$ = new ulint_type_name_c();}
;
real_type_name:
REAL {$$ = new real_type_name_c();}
| LREAL {$$ = new lreal_type_name_c();}
;
date_type_name:
DATE {$$ = new date_type_name_c();}
| TIME_OF_DAY {$$ = new tod_type_name_c();}
| TOD {$$ = new tod_type_name_c();}
| DATE_AND_TIME {$$ = new dt_type_name_c();}
| DT {$$ = new dt_type_name_c();}
;
bit_string_type_name:
BYTE {$$ = new byte_type_name_c();}
| WORD {$$ = new word_type_name_c();}
| DWORD {$$ = new dword_type_name_c();}
| LWORD {$$ = new lword_type_name_c();}
/* 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();}
| WSTRING {$$ = new wstring_type_name_c();}
;
/********************************/
/* B 1.3.2 - Generic data types */
/********************************/
/* Strangely, the following symbol does 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 {$$ = $1;}
| prev_declared_structure_type_name {$$ = $1;}
| prev_declared_string_type_name {$$ = $1;}
;
single_element_type_name:
prev_declared_simple_type_name {$$ = $1;}
/* 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 {$$ = $1;}
| prev_declared_subrange_type_name {$$ = $1;}
| prev_declared_enumerated_type_name {$$ = $1;}
;
/* 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);}
;
/* helper symbol for data_type_declaration */
type_declaration_list:
type_declaration ';'
{$$ = new type_declaration_list_c(); $$->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);
library_element_symtable.insert($1, prev_declared_simple_type_name_token);
}
;
simple_spec_init:
simple_specification
/* The following line was changed 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);}
| prev_declared_simple_type_name ASSIGN constant
{$$ = new simple_spec_init_c($1, $3);}
;
/* 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);}
| prev_declared_simple_type_name
{$$ = new simple_spec_init_c($1, NULL);}
;
subrange_type_declaration:
/* subrange_type_name ':' subrange_spec_init */
identifier ':' subrange_spec_init
{$$ = new subrange_type_declaration_c($1, $3);
library_element_symtable.insert($1, prev_declared_subrange_type_name_token);
}
;
subrange_spec_init:
subrange_specification
{$$ = new subrange_spec_init_c($1, NULL);}
| subrange_specification ASSIGN signed_integer
{$$ = new subrange_spec_init_c($1, $3);}
;
subrange_specification:
integer_type_name '(' subrange')'
{$$ = new subrange_specification_c($1, $3);}
| prev_declared_subrange_type_name {$$ = $1;}
;
subrange:
signed_integer DOTDOT signed_integer
{$$ = new subrange_c($1, $3);}
;
enumerated_type_declaration:
/* enumerated_type_name ':' enumerated_spec_init */
identifier ':' enumerated_spec_init
{$$ = new enumerated_type_declaration_c($1, $3);
library_element_symtable.insert($1, prev_declared_enumerated_type_name_token);
}
;
enumerated_spec_init:
enumerated_specification
{$$ = new enumerated_spec_init_c($1, NULL);}
| enumerated_specification ASSIGN enumerated_value
{$$ = new enumerated_spec_init_c($1, $3);}
;
enumerated_specification:
'(' enumerated_value_list ')'
{$$ = $2;}
| prev_declared_enumerated_type_name {$$ = $1;}
;
/* helper symbol for enumerated_specification */
enumerated_value_list:
enumerated_value
{$$ = new enumerated_value_list_c(); $$->add_element($1);}
| enumerated_value_list ',' enumerated_value
{$$ = $1; $$->add_element($3);}
;
enumerated_value:
identifier
{$$ = $1;}
| prev_declared_enumerated_type_name '#' any_identifier
{$$ = new enumerated_value_c($1, $3);}
;
array_type_declaration:
/* array_type_name ':' array_spec_init */
identifier ':' array_spec_init
{$$ = new array_type_declaration_c($1, $3);
library_element_symtable.insert($1, prev_declared_array_type_name_token);
}
;
array_spec_init:
array_specification
{$$ = new array_spec_init_c($1, NULL);}
| array_specification ASSIGN array_initialization
{$$ = new array_spec_init_c($1, $3);}
;
array_specification:
prev_declared_array_type_name
{$$ = $1;}
| ARRAY '[' array_subrange_list ']' OF non_generic_type_name
{$$ = new array_specification_c($3, $6);}
;
/* helper symbol for array_specification */
array_subrange_list:
subrange
{$$ = new array_subrange_list_c(); $$->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(); $$->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);}
;
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);
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);}
| prev_declared_structure_type_name ASSIGN structure_initialization
{$$ = new initialized_structure_c($1, $3);}
;
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(); $$->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);}
| structure_element_name ':' subrange_spec_init
{$$ = new structure_element_declaration_c($1, $3);}
| structure_element_name ':' enumerated_spec_init
{$$ = new structure_element_declaration_c($1, $3);}
| structure_element_name ':' array_spec_init
{$$ = new structure_element_declaration_c($1, $3);}
| structure_element_name ':' initialized_structure
{$$ = new structure_element_declaration_c($1, $3);}
;
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(); $$->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);}
| structure_element_name ASSIGN enumerated_value
{$$ = new structure_element_initialization_c($1, $3);}
| structure_element_name ASSIGN array_initialization
{$$ = new structure_element_initialization_c($1, $3);}
| structure_element_name ASSIGN structure_initialization
{$$ = new structure_element_initialization_c($1, $3);}
;
/* 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);
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 '[' <size> ']'
* has been changed to become mandatory to remove the conflict.
*
* The conflict arises because
* new_str_type : STRING := "hello!"
* may be reduced to a string_type_declaration OR
* a simple_type_declaration.
*
* Our change forces it to be reduced to a
* simple_type_declaration!
* We chose this option because changing the definition
* of simple_spec_init would force us to change all the other
* rules in which it appears. The change we made has no
* side-effects!
*/
/* helper symbol for string_type_declaration */
string_type_declaration_init:
/* empty */
{$$ = NULL;}
| ASSIGN character_string
{$$ = $2;}
;
/*********************/
/* B 1.4 - Variables */
/*********************/
variable:
symbolic_variable
| direct_variable
;
symbolic_variable:
/* NOTE: To be entirely correct, variable_name should be replacemed by
* prev_declared_variable_name | prev_declared_fb_name | prev_declared_global_var_name
*/
prev_declared_variable_name
{$$ = new symbolic_variable_c($1);}
| prev_declared_fb_name
{$$ = new symbolic_variable_c($1);}
| prev_declared_global_var_name
{$$ = new symbolic_variable_c($1);}
| multi_element_variable
;
/* 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);}
| 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);};
/*************************************/
/* 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);}
;
/* please see note above any_symbolic_variable */
any_array_variable:
any_subscripted_variable '[' subscript_list ']'
{$$ = new array_variable_c($1, $3);}
;
subscripted_variable:
symbolic_variable
;
/* please see note above any_symbolic_variable */
any_subscripted_variable:
any_symbolic_variable
;
subscript_list:
subscript
{$$ = new subscript_list_c(); $$->add_element($1);}
| subscript_list ',' subscript
{$$ = $1; $$->add_element($3);}
;
subscript: expression;
structured_variable:
record_variable '.' field_selector
{$$ = new structured_variable_c($1, $3);}
;
/* please see note above any_symbolic_variable */
any_structured_variable:
any_record_variable '.' field_selector
{$$ = new structured_variable_c($1, $3);}
;
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);}
| VAR_INPUT RETAIN input_declaration_list END_VAR
{$$ = new input_declarations_c(new retain_option_c(), $3);}
| VAR_INPUT NON_RETAIN input_declaration_list END_VAR
{$$ = new input_declarations_c(new non_retain_option_c(), $3);}
;
/* helper symbol for input_declarations */
input_declaration_list:
input_declaration ';'
{$$ = new input_declaration_list_c(); $$->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(), $1);}
| var1_list ':' BOOL F_EDGE
{$$ = new edge_declaration_c(new falling_edge_option_c(), $1);}
;
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);}
| var1_list ':' subrange_spec_init
{$$ = new var1_init_decl_c($1, $3);}
| var1_list ':' enumerated_spec_init
{$$ = new var1_init_decl_c($1, $3);}
;
var1_list:
variable_name
{$$ = new var1_list_c(); $$->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);}
;
structured_var_init_decl:
var1_list ':' initialized_structure
{$$ = new structured_var_init_decl_c($1, $3);}
;
/* 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);}
/*| 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);}
;
/* 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();
/* 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);}
| VAR_OUTPUT RETAIN var_init_decl_list END_VAR
{$$ = new output_declarations_c(new retain_option_c(), $3);}
| VAR_OUTPUT NON_RETAIN var_init_decl_list END_VAR
{$$ = new output_declarations_c(new non_retain_option_c(), $3);}
;
input_output_declarations:
VAR_IN_OUT var_declaration_list END_VAR
{$$ = new input_output_declarations_c($2);}
;
/* helper symbol for input_output_declarations */
var_declaration_list:
var_declaration ';'
{$$ = new var_declaration_list_c(); $$->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);}
| var1_list ':' subrange_specification
{$$ = new var1_init_decl_c($1, $3);}
| var1_list ':' enumerated_specification
{$$ = new var1_init_decl_c($1, $3);}
;
array_var_declaration:
var1_list ':' array_specification
{$$ = new array_var_declaration_c($1, $3);}
;
structured_var_declaration:
var1_list ':' prev_declared_structure_type_name
{$$ = new structured_var_declaration_c($1, $3);}
;
var_declarations:
VAR var_init_decl_list END_VAR
{$$ = new var_declarations_c(NULL, $2);}
| VAR CONSTANT var_init_decl_list END_VAR
{$$ = new var_declarations_c(new constant_option_c(), $3);}
;
retentive_var_declarations:
VAR RETAIN var_init_decl_list END_VAR
{$$ = new retentive_var_declarations_c($3);}
;
located_var_declarations:
VAR located_var_decl_list END_VAR
{$$ = new located_var_declarations_c(NULL, $2);}
| VAR CONSTANT located_var_decl_list END_VAR
{$$ = new located_var_declarations_c(new constant_option_c(), $3);}
| VAR RETAIN located_var_decl_list END_VAR
{$$ = new located_var_declarations_c(new retain_option_c(), $3);}
| VAR NON_RETAIN located_var_decl_list END_VAR
{$$ = new located_var_declarations_c(new non_retain_option_c(), $3);}
;
/* helper symbol for located_var_declarations */
located_var_decl_list:
located_var_decl ';'
{$$ = new located_var_decl_list_c(); $$->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);
variable_name_symtable.insert($1, prev_declared_variable_name_token);
}
| location ':' located_var_spec_init
{$$ = new located_var_decl_c(NULL, $1, $3);}
;
external_var_declarations:
VAR_EXTERNAL external_declaration_list END_VAR
{$$ = new external_var_declarations_c(NULL, $2);}
| VAR_EXTERNAL CONSTANT external_declaration_list END_VAR
{$$ = new external_var_declarations_c(new constant_option_c(), $3);}
;
/* helper symbol for external_var_declarations */
external_declaration_list:
external_declaration ';'
{$$ = new external_declaration_list_c(); $$->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);
variable_name_symtable.insert($1, prev_declared_variable_name_token);
}
| global_var_name ':' subrange_specification
{$$ = new external_declaration_c($1, $3);
variable_name_symtable.insert($1, prev_declared_variable_name_token);
}
| global_var_name ':' enumerated_specification
{$$ = new external_declaration_c($1, $3);
variable_name_symtable.insert($1, prev_declared_variable_name_token);
}
| global_var_name ':' array_specification
{$$ = new external_declaration_c($1, $3);
variable_name_symtable.insert($1, prev_declared_variable_name_token);
}
| global_var_name ':' prev_declared_structure_type_name
{$$ = new external_declaration_c($1, $3);
variable_name_symtable.insert($1, prev_declared_variable_name_token);
}
| global_var_name ':' function_block_type_name
{$$ = new external_declaration_c($1, $3);
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);}
| VAR_GLOBAL CONSTANT global_var_decl_list END_VAR
{$$ = new global_var_declarations_c(new constant_option_c(), $3);}
| VAR_GLOBAL RETAIN global_var_decl_list END_VAR
{$$ = new global_var_declarations_c(new retain_option_c(), $3);}
;
/* helper symbol for global_var_declarations */
global_var_decl_list:
global_var_decl ';'
{$$ = new global_var_decl_list_c(); $$->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);}
| global_var_spec ':' located_var_spec_init
{$$ = new global_var_decl_c($1, $3);}
| global_var_spec ':' function_block_type_name
{$$ = new global_var_decl_c($1, $3);}
;
global_var_spec:
global_var_list {$$ = $1;}
| location
| global_var_name location
{$$ = new global_var_spec_c($1, $2);
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);}
;
global_var_list:
global_var_name
{$$ = new global_var_list_c(); $$->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);}
;
/* 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);}
/*
| STRING ASSIGN single_byte_character_string
{$$ = new single_byte_string_spec_c(NULL, $3);}
*/
| STRING '[' integer ']' ASSIGN single_byte_character_string
{$$ = new single_byte_string_spec_c($3, $6);}
;
double_byte_string_var_declaration:
var1_list ':' double_byte_string_spec
{$$ = new double_byte_string_var_declaration_c($1, $3);}
;
double_byte_string_spec:
/* WSTRING
{$$ = new double_byte_string_spec_c(NULL, NULL);}
*/
WSTRING '[' integer ']'
{$$ = new double_byte_string_spec_c($3, NULL);}
/*
| WSTRING ASSIGN double_byte_character_string
{$$ = new double_byte_string_spec_c(NULL, $3);}
*/
| WSTRING '[' integer ']' ASSIGN double_byte_character_string
{$$ = new double_byte_string_spec_c($3, $6);}
;
incompl_located_var_declarations:
VAR incompl_located_var_decl_list END_VAR
{$$ = new incompl_located_var_declarations_c(NULL, $2);}
| VAR RETAIN incompl_located_var_decl_list END_VAR
{$$ = new incompl_located_var_declarations_c(new retain_option_c(), $3);}
| VAR NON_RETAIN incompl_located_var_decl_list END_VAR
{$$ = new incompl_located_var_declarations_c(new non_retain_option_c(), $3);}
;
/* helper symbol for incompl_located_var_declarations */
incompl_located_var_decl_list:
incompl_located_var_decl ';'
{$$ = new incompl_located_var_decl_list_c(); $$->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);}
;
incompl_location:
AT incompl_location_token
{$$ = new incompl_location_c($2);}
;
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);}
*/
STRING '[' integer ']'
{$$ = new single_byte_string_spec_c($3, NULL);}
/*
| WSTRING
{$$ = new double_byte_string_spec_c(NULL, NULL);}
*/
| WSTRING '[' integer ']'
{$$ = new double_byte_string_spec_c($3, NULL);}
;
/* 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(); $$->add_element($1);}
| var_init_decl_list var_init_decl ';'
{$$ = $1; $$->add_element($2);}
;
/***********************/
/* B 1.5.1 - Functions */
/***********************/
/* 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;
/*
function_name:
prev_declared_derived_function_name
| standard_function_name
;
*/
/* 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);}
;
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"));}
;
/* 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))
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);
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);
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);
}
}
;
/* 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();}
| 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(), $3);}
| VAR var2_init_decl_list END_VAR
{$$ = new function_var_decls_c(NULL, $2);}
;
/* intermediate helper symbol for function_var_decls */
var2_init_decl_list:
var2_init_decl ';'
{$$ = new var2_init_decl_list_c(); $$->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);};
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);
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();
}
;
/* 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();}
| 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);}
;
/* intermediate helper symbol for temp_var_decls */
temp_var_decls_list:
temp_var_decl ';'
{$$ = new temp_var_decls_list_c(); $$->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);}
;
function_block_body:
statement_list {$$ = $1;}
| instruction_list {$$ = $1;}
| sequential_function_chart {$$ = $1;}
/*
| ladder_diagram
| function_block_diagram
| <other languages>
*/
;
/**********************/
/* 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);
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();
}
;
/* helper symbol for program_declaration */
/*
* NOTE: we re-use the var_declarations_list_c
*/
program_var_declarations_list:
/* empty */
{$$ = new var_declarations_list_c();}
| 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 *
/********************************************/////////////////////////////////////////////////////////////////////////////////////////////
/* TODO ... */
sequential_function_chart:
sfc_network
{$$ = new sequential_function_chart_c(); $$->add_element($1);}
| sequential_function_chart sfc_network
{$$ = $1; $$->add_element($2);}
;
sfc_network:
initial_step
{$$ = new sfc_network_c(); $$->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
{$$ = new initial_step_c($2, $4);}
;
step:
STEP step_name ':' action_association_list END_STEP
{$$ = new step_c($2, $4);}
;
/* helper symbol for:
* - initial_step
* - step
*/
action_association_list:
/* empty */
{$$ = new action_association_list_c();}
| action_association_list action_association ';'
{$$ = $1; $$->add_element($2);}
;
step_name: identifier;
action_association:
action_name '(' action_qualifier indicator_name_list ')'
{$$ = new action_association_c($1, $3, $4, NULL);}
;
/* helper symbol for action_association */
indicator_name_list:
/* empty */
{$$ = new indicator_name_list_c();}
| indicator_name_list ',' indicator_name
{$$ = $1; $$->add_element($3);}
;
action_name: identifier;
action_qualifier:
/* empty */
{$$ = NULL;}
| qualifier
{$$ = new action_qualifier_c($1, NULL);}
| timed_qualifier ',' action_time
{$$ = new action_qualifier_c($1, $3);}
;
//N_token: N {$$ = new N_token_c();};
qualifier:
N {$$ = new qualifier_c(strdup("N"));}
/* 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 identifier_c(strdup("R"));}
| S {$$ = new identifier_c(strdup("S"));}
*/
| P {$$ = new qualifier_c(strdup("P"));}
;
timed_qualifier:
L {$$ = new timed_qualifier_c(strdup("L"));}
| D {$$ = new timed_qualifier_c(strdup("D"));}
| SD {$$ = new timed_qualifier_c(strdup("SD"));}
| DS {$$ = new timed_qualifier_c(strdup("DS"));}
| SL {$$ = new timed_qualifier_c(strdup("SL"));}
;
action_time:
duration
| variable_name
| transition_name
;
indicator_name: variable_name;
transition:
TRANSITION FROM steps TO steps transition_condition END_TRANSITION
{$$ = new transition_c(NULL, NULL, $3, $5, $6, NULL);}
| TRANSITION transition_name FROM steps TO steps transition_condition END_TRANSITION
{$$ = new transition_c($2, NULL, $4, $6, $7, NULL);}
| TRANSITION '(' PRIORITY ASSIGN integer ')' FROM steps TO steps transition_condition END_TRANSITION
{$$ = new transition_c(NULL, $5, $8, $10, $11, NULL);}
| TRANSITION transition_name '(' PRIORITY ASSIGN integer ')' FROM steps TO steps transition_condition END_TRANSITION
{$$ = new transition_c($2, $6, $9, $11, $12, NULL);}
;
transition_name: identifier;
steps:
step_name
{$$ = new steps_c($1, NULL);}
| '(' step_name_list ')'
{$$ = new steps_c(NULL, $2);}
;
step_name_list:
step_name ',' step_name
{$$ = new step_name_list_c(); $$->add_element($1); $$->add_element($3);}
| step_name_list ',' step_name
{$$ = $1; $$->add_element($3);}
;
transition_condition:
':' simple_instr_list
{$$ = new transition_condition_c($2, NULL);}
| ASSIGN expression ';'
{$$ = new transition_condition_c(NULL, $2);}
;
action:
ACTION action_name ':' function_block_body END_ACTION
{$$ = new action_c($2, $4);}
;
/********************************/
/* 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);};
prev_declared_resource_name: prev_declared_resource_name_token {$$ = new identifier_c($1);};
prev_declared_program_name: prev_declared_program_name_token {$$ = new identifier_c($1);};
// prev_declared_task_name: prev_declared_task_name_token {$$ = new identifier_c($1);};
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);
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);
library_element_symtable.insert($2, prev_declared_configuration_name_token);
variable_name_symtable.pop();
}
| CONFIGURATION error END_CONFIGURATION
{$$ = NULL;
print_err_msg(current_filename, @2.last_line, "error in configuration declaration.");
/* yychar */
yyerrok;
}
;
// 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(); $$->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);
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);}
;
// helper symbol for single_resource_declaration //
task_configuration_list:
// empty
{$$ = new task_configuration_list_c();}
| 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(); $$->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();}
//| 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);}
| prev_declared_global_var_name '.' structure_element_name
{$$ = new global_var_reference_c(NULL, $1, $3);}
| prev_declared_resource_name '.' prev_declared_global_var_name
{$$ = new global_var_reference_c($1, $3, NULL);}
| prev_declared_resource_name '.' prev_declared_global_var_name '.' structure_element_name
{$$ = new global_var_reference_c($1, $3, $5);}
;
//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);}
;
program_name: identifier;
/*
direction:
READ_WRITE
{$$ = NULL;}
| READ_ONLY
{$$ = NULL;}
;
*/
task_configuration:
TASK task_name task_initialization
{$$ = new task_configuration_c($2, $3);}
;
/* NOTE: The specification does nopt 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 ')' //
'(' PRIORITY ASSIGN integer ')'
{$$ = new task_initialization_c(NULL, NULL, $4);}
| '(' SINGLE ASSIGN data_source ',' PRIORITY ASSIGN integer ')'
{$$ = new task_initialization_c($4, NULL, $8);}
| '(' INTERVAL ASSIGN data_source ',' PRIORITY ASSIGN integer ')'
{$$ = new task_initialization_c(NULL, $4, $8);}
| '(' SINGLE ASSIGN data_source ',' INTERVAL ASSIGN data_source ',' PRIORITY ASSIGN integer ')'
{$$ = new task_initialization_c($4, $8, $12);}
;
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);
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(), $3, $4, $6, $7);
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(), $3, $4, $6, $7);
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(); $$->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);}
;
/* 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);}
| any_symbolic_variable SENDTO data_sink
{$$ = new prog_cnxn_sendto_c($1, $3);}
;
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);}
;
// helper symbol for instance_specific_initializations //
instance_specific_init_list:
instance_specific_init ';'
{$$ = new instance_specific_init_list_c(); $$->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);}
| 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);}
| 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);}
;
/* helper symbol for instance_specific_init */
fb_initialization:
function_block_type_name ASSIGN structure_initialization
{$$ = new fb_initialization_c($1, $3);}
;
/***********************************/
/* 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(); $$->add_element($1);}
| instruction_list il_instruction
{$$ = $1; $$->add_element($2);}
| instruction_list pragma
{$$ = $1; $$->add_element($2);}
| instruction_list error
{$$ = $1;
print_err_msg(current_filename, @2.last_line, "error in IL instruction.");
/* yychar */
yyerrok;
}
;
il_instruction:
il_incomplete_instruction eol_list
{$$ = new il_instruction_c(NULL, $1);}
| label ':' il_incomplete_instruction eol_list
{$$ = new il_instruction_c($1, $3);}
;
/* 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);}
| il_simple_operator_noclash il_operand
{$$ = new il_simple_operation_c($1, $2);}
| il_simple_operator_clash_il_operand
{$$ = new il_simple_operation_c($1.first, $1.second);}
/* 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.
*
* 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);}
/* NOTE: the line
* | il_simple_operator il_operand
* already contains the 'NOT', 'MOD', etc. operators, followed by a single il_operand,
* which may also be reduced to a function name with an operand_list of a single
* il_operand! This would lead us to a reduce/reduce conflict!
*
* 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!
* 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);}
| il_simple_operator_clash_il_operand ',' il_operand_list
{list_c *list = new il_operand_list_c();
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);
}
;
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);}
| il_expr_operator_noclash '(' il_operand eol_list ')'
{$$ = new il_expression_c($1, $3, NULL);}
| il_expr_operator_noclash '(' eol_list simple_instr_list ')'
{$$ = new il_expression_c($1, NULL, $4);}
| il_expr_operator_noclash '(' il_operand eol_list simple_instr_list ')'
{$$ = new il_expression_c($1, $3, $5);}
/*
*/
| il_expr_operator_clash '(' eol_list ')'
{$$ = new il_expression_c($1, NULL, NULL);}
| il_expr_operator_clash '(' il_operand eol_list ')'
{$$ = new il_expression_c($1, $3, NULL);}
| il_expr_operator_clash '(' il_operand eol_list simple_instr_list ')'
{$$ = new il_expression_c($1, $3, $5);}
/*
*/
| il_expr_operator_clash_eol_list simple_instr_list ')'
{$$ = new il_expression_c($1, NULL, $2);}
;
il_jump_operation:
il_jump_operator label
{$$ = new il_jump_operation_c($1, $2);}
;
il_fb_call:
il_call_operator prev_declared_fb_name
{$$ = new il_fb_call_c($1, $2, NULL, NULL);}
| il_call_operator prev_declared_fb_name '(' ')'
{$$ = new il_fb_call_c($1, $2, NULL, NULL);}
| il_call_operator prev_declared_fb_name '(' eol_list ')'
{$$ = new il_fb_call_c($1, $2, NULL, NULL);}
| il_call_operator prev_declared_fb_name '(' il_operand_list ')'
{$$ = new il_fb_call_c($1, $2, $4, NULL);}
| il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')'
{$$ = new il_fb_call_c($1, $2, NULL, $5);}
;
/* 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!
*/
function_name_no_clashes '(' eol_list ')'
{$$ = new il_formal_funct_call_c($1, NULL);}
| function_name_simpleop_clashes '(' eol_list ')'
{$$ = new il_formal_funct_call_c($1, NULL);}
/* | function_name '(' eol_list il_param_list ')' */
| function_name_no_clashes '(' eol_list il_param_list ')'
{$$ = new il_formal_funct_call_c($1, $4);}
| function_name_simpleop_clashes '(' eol_list il_param_list ')'
{$$ = new il_formal_funct_call_c($1, $4);}
/* 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);}
;
il_expr_operator_clash_eol_list:
il_expr_operator_clash '(' eol_list
{$$ = $1;}
;
il_operand:
variable
| enumerated_value
| constant
;
il_operand_list:
il_operand
{$$ = new il_operand_list_c(); $$->add_element($1);}
| il_operand_list ',' il_operand
{$$ = $1; $$->add_element($3);}
;
simple_instr_list:
il_simple_instruction
{$$ = new simple_instr_list_c(); $$->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(); $$->add_element($1);}
;
/* Helper symbol for il_param_list */
il_param_instruction_list:
il_param_instruction
{$$ = new il_param_list_c(); $$->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);}
| il_assign_operator '(' eol_list simple_instr_list ')'
{$$ = new il_param_assignment_c($1, NULL, $4);}
;
il_param_out_assignment:
il_assign_out_operator variable
{$$ = new il_param_out_assignment_c($1, $2);}
;
/*******************/
/* B 2.2 Operators */
/*******************/
sendto_identifier: sendto_identifier_token {$$ = new identifier_c($1);};
/* 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();};
LDN_operator: LDN {$$ = new LDN_operator_c();};
ST_operator: ST {$$ = new ST_operator_c();};
STN_operator: STN {$$ = new STN_operator_c();};
NOT_operator: NOT {$$ = new NOT_operator_c();};
S_operator: S {$$ = new S_operator_c();};
R_operator: R {$$ = new R_operator_c();};
S1_operator: S1 {$$ = new S1_operator_c();};
R1_operator: R1 {$$ = new R1_operator_c();};
CLK_operator: CLK {$$ = new CLK_operator_c();};
CU_operator: CU {$$ = new CU_operator_c();};
CD_operator: CD {$$ = new CD_operator_c();};
PV_operator: PV {$$ = new PV_operator_c();};
IN_operator: IN {$$ = new IN_operator_c();};
PT_operator: PT {$$ = new PT_operator_c();};
AND_operator: AND {$$ = new AND_operator_c();};
AND2_operator: AND2 {$$ = new AND_operator_c();}; /* '&' in the source code! */
OR_operator: OR {$$ = new OR_operator_c();};
XOR_operator: XOR {$$ = new XOR_operator_c();};
ANDN_operator: ANDN {$$ = new ANDN_operator_c();};
ANDN2_operator: ANDN2 {$$ = new ANDN_operator_c();}; /* '&N' in the source code! */
ORN_operator: ORN {$$ = new ORN_operator_c();};
XORN_operator: XORN {$$ = new XORN_operator_c();};
ADD_operator: ADD {$$ = new ADD_operator_c();};
SUB_operator: SUB {$$ = new SUB_operator_c();};
MUL_operator: MUL {$$ = new MUL_operator_c();};
DIV_operator: DIV {$$ = new DIV_operator_c();};
MOD_operator: MOD {$$ = new MOD_operator_c();};
GT_operator: GT {$$ = new GT_operator_c();};
GE_operator: GE {$$ = new GE_operator_c();};
EQ_operator: EQ {$$ = new EQ_operator_c();};
LT_operator: LT {$$ = new LT_operator_c();};
LE_operator: LE {$$ = new LE_operator_c();};
NE_operator: NE {$$ = new NE_operator_c();};
CAL_operator: CAL {$$ = new CAL_operator_c();};
CALC_operator: CALC {$$ = new CALC_operator_c();};
CALCN_operator: CALCN {$$ = new CALCN_operator_c();};
RET_operator: RET {$$ = new RET_operator_c();};
RETC_operator: RETC {$$ = new RETC_operator_c();};
RETCN_operator: RETCN {$$ = new RETCN_operator_c();};
JMP_operator: JMP {$$ = new JMP_operator_c();};
JMPC_operator: JMPC {$$ = new JMPC_operator_c();};
JMPCN_operator: JMPCN {$$ = new JMPCN_operator_c();};
/*
MAY CONFLICT WITH STANDARD FUNCTION NAMES!!!
NOT_operator: NOT {new NOT_operator_c();};
AND_operator: AND {new AND_operator_c();};
OR_operator: OR {new OR_operator_c();};
XOR_operator: XOR {new XOR_operator_c();};
ADD_operator: ADD {new ADD_operator_c();};
SUB_operator: SUB {new SUB_operator_c();};
MUL_operator: MUL {new MUL_operator_c();};
DIV_operator: DIV {new DIV_operator_c();};
MOD_operator: MOD {new MOD_operator_c();};
GT_operator: GT {new GT_operator_c();};
GE_operator: GE {new GE_operator_c();};
EQ_operator: EQ {new EQ_operator_c();};
LT_operator: LT {new LT_operator_c();};
LE_operator: LE {new LE_operator_c();};
NE_operator: NE {new NE_operator_c();};
*/
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);}
/*| NOT variable_name SENDTO */
| NOT sendto_identifier SENDTO
{$$ = new il_assign_out_operator_c(new not_paramassign_c(), $2);}
;
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);}
;
xor_expression:
and_expression
| xor_expression XOR and_expression
{$$ = new xor_expression_c($1, $3);}
;
and_expression:
comparison
| and_expression '&' comparison
{$$ = new and_expression_c($1, $3);}
| and_expression AND comparison
{$$ = new and_expression_c($1, $3);}
/* 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);}
;
comparison:
equ_expression
| comparison '=' equ_expression
{$$ = new equ_expression_c($1, $3);}
| comparison OPER_NE equ_expression
{$$ = new notequ_expression_c($1, $3);}
;
equ_expression:
add_expression
| equ_expression '<' add_expression
{$$ = new lt_expression_c($1, $3);}
| equ_expression '>' add_expression
{$$ = new gt_expression_c($1, $3);}
| equ_expression OPER_LE add_expression
{$$ = new le_expression_c($1, $3);}
| equ_expression OPER_GE add_expression
{$$ = new ge_expression_c($1, $3);}
;
/* Not required...
comparison_operator: '<' | '>' | '>=' '<='
*/
add_expression:
term
| add_expression '+' term
{$$ = new add_expression_c($1, $3);}
| add_expression '-' term
{$$ = new sub_expression_c($1, $3);}
;
/* Not required...
add_operator: '+' | '-'
*/
term:
power_expression
| term '*' power_expression
{$$ = new mul_expression_c($1, $3);}
| term '/' power_expression
{$$ = new div_expression_c($1, $3);}
| term MOD power_expression
{$$ = new mod_expression_c($1, $3);}
;
/* Not required...
multiply_operator: '*' | '/' | 'MOD'
*/
power_expression:
unary_expression
| power_expression OPER_EXP unary_expression
{$$ = new power_expression_c($1, $3);}
;
unary_expression:
primary_expression
| '-' primary_expression
{$$ = new neg_expression_c($2);}
| NOT primary_expression
{$$ = new not_expression_c($2);}
;
/* 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
*/
primary_expression:
/* constant */
non_negative_constant
| enumerated_value
| 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);}
;
/********************/
/* B 3.2 Statements */
/********************/
statement_list:
/* empty */
{$$ = new statement_list_c();}
| statement_list statement ';'
{$$ = $1; $$->add_element($2);}
| statement_list pragma
{$$ = $1; $$->add_element($2);}
| statement_list error ';'
{$$ = $1;
print_err_msg(current_filename, @2.last_line, "error in statement.");
/* yychar */
yyerrok;
}
;
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);}
;
/*****************************************/
/* B 3.2.2 Subprogram Control Statements */
/*****************************************/
subprogram_control_statement:
fb_invocation
| return_statement
;
return_statement:
RETURN {$$ = new return_statement_c();}
;
fb_invocation:
prev_declared_fb_name '(' ')'
{$$ = new fb_invocation_c($1, NULL); }
| prev_declared_fb_name '(' param_assignment_list ')'
{$$ = new fb_invocation_c($1, $3);}
;
/* helper symbol for
* - fb_invocation
* - function_invocation
*/
param_assignment_list:
param_assignment
{$$ = new param_assignment_list_c(); $$->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);}
| expression
/*| variable_name SENDTO variable */
/*| any_identifier SENDTO variable */
| sendto_identifier SENDTO variable
{$$ = new output_variable_param_assignment_c(NULL,$1, $3);}
/*| variable_name SENDTO variable */
/*| NOT any_identifier SENDTO variable*/
| NOT sendto_identifier SENDTO variable
{$$ = new output_variable_param_assignment_c(new not_paramassign_c(),$2, $4);}
;
/********************************/
/* 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);}
| IF expression THEN statement_list elseif_statement_list ELSE statement_list END_IF
{$$ = new if_statement_c($2, $4, $5, $7);}
;
/* helper symbol for if_statement */
elseif_statement_list:
/* empty */
{$$ = new elseif_statement_list_c();}
| 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);}
;
case_statement:
CASE expression OF case_element_list END_CASE
{$$ = new case_statement_c($2, $4, NULL);}
| CASE expression OF case_element_list ELSE statement_list END_CASE
{$$ = new case_statement_c($2, $4, $6);}
;
/* helper symbol for case_statement */
case_element_list:
case_element
{$$ = new case_element_list_c(); $$->add_element($1);}
| case_element_list case_element
{$$ = $1; $$->add_element($2);}
;
case_element:
case_list ':' statement_list
{$$ = new case_element_c($1, $3);}
;
case_list:
case_list_element
{$$ = new case_list_c(); $$->add_element($1);}
| case_list ',' case_list_element
{$$ = $1; $$->add_element($3);}
;
case_list_element:
signed_integer
| enumerated_value
| subrange
;
/********************************/
/* 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);}
| FOR control_variable ASSIGN expression TO expression DO statement_list END_FOR
{$$ = new for_statement_c($2, $4, $6, NULL, $8);}
;
/* 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
* construct, so I (Mario) changed the syntax to read
* control_variable: prev_declared_variable_name;
*/
control_variable: prev_declared_variable_name {$$ = $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);}
;
repeat_statement:
REPEAT statement_list UNTIL expression END_REPEAT
{$$ = new repeat_statement_c($2, $4);}
;
exit_statement:
EXIT {$$ = new exit_statement_c();}
;
%%
#include <stdio.h> /* required for printf() */
#include <errno.h>
#include "../util/symtable.hh"
/* variables defined in code generated by flex... */
extern FILE *yyin;
extern int yylineno;
/* 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, additional_error_msg);
print_include_stack();
fprintf(stderr, "%s:%d: %s\n", filename, lineno, current_error_msg);
}
/* Function only called from within flex!
*
* search for a symbol in either of the two symbol tables
* declared above, and return the token id of the first
* symbol found.
* Searches first in the variables, and only if not found
* does it continue searching in the library elements
*/
int get_identifier_token(const char *identifier_str) {
// std::cout << "get_identifier_token(" << identifier_str << "): \n";
int token_id;
if ((token_id = variable_name_symtable.find_value(identifier_str)) == variable_name_symtable.end_value())
if ((token_id = library_element_symtable.find_value(identifier_str)) == library_element_symtable.end_value())
return identifier_token;
return token_id;
}
/* convert between an il_operator to a function name */
/* This a kludge!
* It is required because our language requires more than one
* look ahead token, and bison only works with one!
*/
#define op_2_str(op, str) {\
op ## _operator_c *ptr = dynamic_cast<op ## _operator_c *>(il_operator); \
if (ptr != NULL) name = str; \
}
/* NOTE: this code is very ugly and un-eficient, but I (Mario) have many
* more things to worry about right now, so just let it be...
*/
symbol_c *il_operator_c_2_identifier_c(symbol_c *il_operator) {
const char *name = NULL;
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;
free(il_operator);
return new identifier_c(strdup(name));
}
/*
* Join two strings together. Allocate space with malloc(3).
*/
static char *strdup2(const char *a, const char *b) {
char *res = (char *)malloc(strlen(a) + strlen(b) + 1);
if (!res)
return NULL;
return strcat(strcpy(res, a), b); /* safe, actually */
}
/*
* Join three strings together. Allocate space with malloc(3).
*/
static char *strdup3(const char *a, const char *b, const char *c) {
char *res = (char *)malloc(strlen(a) + strlen(b) + strlen(c) + 1);
if (!res)
return NULL;
return strcat(strcat(strcpy(res, a), b), c); /* safe, actually */
}
const char *standard_function_names[] = {
// 2.5.1.5.1 Type conversion functions
/*
*_TO_**
TRUNC
*_BCD_TO_**
**_TO_BCD_*
(REAL or LREAL to SINT, INT, DINT or LINT)
*/
"TRUNC",
"TIME_TO_REAL",
// 2.5.1.5.2 Numerical functions
// Table 23 - Standard functions of one numeric variable
"ABS","SQRT","LN","LOG","EXP","SIN","COS","TAN","ASIN","ACOS","ATAN",
// Table 24 - Standard arithmetic functions
"ADD","MUL","SUB","DIV","MOD"/* See note (a) */,"EXPT","MOVE",
// 2.5.1.5.3 Bit string functions
// Table 25 - Standard bit shift functions
"SHL","SHR","ROR","ROL",
// 2.5.1.5.4 Selection and comparison functions
// Table 26 - Standard bitwise Boolean functions
"AND","OR","XOR","NOT",
// Table 27 - Standard selection functions
"SEL","MAX","MIN","LIMIT","MUX",
// Table 28 - Standard comparison functions
"GT","GE","EQ","LE","LT","NE",
// 2.5.1.5.5 Character string functions
// Table 29 - Standard character string functions
"LEN","LEFT","RIGHT","MID","CONCAT","INSERT","DELETE","REPLACE","FIND",
// 2.5.1.5.6 Functions of time data types
// Table 30 - Functions of time data types
"ADD_TIME","ADD_TOD_TIME","ADD_DT_TIME","SUB_TIME","SUB_DATE_DATE",
"SUB_TOD_TIME","SUB_TOD_TOD","SUB_DT_TIME","SUB_DT_DT","MULTIME",
"DIVTIME","CONCAT_DATE_TOD",
// 2.5.1.5.7 Functions of enumerated data types
// Table 31 - Functions of enumerated data types
// "SEL", /* already above! We cannot have duplicates! */
// "MUX", /* already above! We cannot have duplicates! */
// "EQ", /* already above! We cannot have duplicates! */
// "NE", /* already above! We cannot have duplicates! */
/* end of array marker! Do not remove! */
NULL
/* Note (a):
* This function has a name equal to a reserved keyword.
* This means that adding it here is irrelevant because the
* lexical parser will consider it the XXX token before
* it interprets it as an identifier and looks it up
* in the library elements symbol table.
*/
};
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_LINT","CTU_UDINT","CTU_ULINT",
"CTD","CTD_DINT","CTD_LINT","CTD_UDINT",
"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
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) {
if ((libfilename = strdup3(includedir, "/", 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 try again... */
if ((libfilename = strdup(DEF_LIBFILENAME)) == 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;
}