# HG changeset patch # User Laurent Bessard # Date 1345646777 -7200 # Node ID c0bda77b37a07117e92571beaad55bf03ccaef2b # Parent aad38592bdde6ae08a697f321723744310d7ea05# Parent c2546c6e0cfa5ad55b288895f17f1b9f2a228f3b Merge with c2546c6e0cfa5ad55b288895f17f1b9f2a228f3b diff -r aad38592bdde -r c0bda77b37a0 absyntax/absyntax.cc --- a/absyntax/absyntax.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax/absyntax.cc Wed Aug 22 16:46:17 2012 +0200 @@ -40,12 +40,7 @@ #include "absyntax.hh" //#include "../stage1_2/iec.hh" /* required for BOGUS_TOKEN_ID, etc... */ #include "visitor.hh" - -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); - -#define ABORT(str) {printf("ERROR: %s\n", str); ERROR;} +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. @@ -61,6 +56,11 @@ this->last_line = last_line; this->last_column = last_column; this->last_order = last_order; + this->datatype = NULL; + this->const_value._real64.status = cs_undefined; + this->const_value._int64.status = cs_undefined; + this->const_value._uint64.status = cs_undefined; + this->const_value._bool.status = cs_undefined; } @@ -101,7 +101,7 @@ n++; elements = (symbol_c **)realloc(elements, n * sizeof(symbol_c *)); if (elements == NULL) - ABORT("Out of memory"); + ERROR_MSG("Out of memory"); elements[n - 1] = elem; if (elem == NULL) @@ -130,18 +130,27 @@ /* To insert into the begining of list, call with pos=0 */ /* To insert into the end of list, call with pos=list->n */ void list_c::insert_element(symbol_c *elem, int pos) { - int i; if (pos > n) ERROR; /* add new element to end of list. Basically alocate required memory... */ /* will also increment n by 1 ! */ add_element(elem); /* if not inserting into end position, shift all elements up one position, to open up a slot in pos for new element */ - if (pos < (n-1)) for (i = n-2; i >= pos; i--) elements[i+1] = elements[i]; + if (pos < (n-1)) for (int i = n-2; i >= pos; i--) elements[i+1] = elements[i]; elements[pos] = elem; } +/* remove element at position pos. */ +void list_c::remove_element(int pos) { + if (pos > n) ERROR; + + /* Shift all elements down one position, starting at the entry to delete. */ + for (int i = pos; i < n-1; i++) elements[i] = elements[i+1]; + /* corrent the new size, and free unused memory */ + n--; + elements = (symbol_c **)realloc(elements, n * sizeof(symbol_c *)); +} #define SYM_LIST(class_name_c, ...) \ class_name_c::class_name_c( \ diff -r aad38592bdde -r c0bda77b37a0 absyntax/absyntax.def --- a/absyntax/absyntax.def Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax/absyntax.def Wed Aug 22 16:46:17 2012 +0200 @@ -97,6 +97,12 @@ SYM_REF0(eno_param_c) */ +/* A class used to identify an entry (literal, variable, etc...) in the abstract syntax tree with an invalid data type */ +/* This is only used from stage3 onwards. Stages 1 and 2 will never create any instances of invalid_type_name_c */ +SYM_REF0(invalid_type_name_c) + + + /********************/ /* 2.1.6 - Pragmas */ /********************/ @@ -158,34 +164,15 @@ * OR * neg_literal_c -> real_literal_c * - * In the semantic verification and code generation stages of the compiler, - * the integer_c is treated as a basic (undefined) data type, since an integer - * constant may be used as a BYTE, BOOLEAN, REAL, etc..., depending on the - * context in which it is used. - * However, an integer_c that is preceded by a '-' may not be used - * as an ANY_BIT data type (BYTE, BOOLEAN, WORD, ...). - * We must therefore be able to determine, holding a simple pointer - * to an integer_c, if that integer_c value is preceded by a '-'. - * However, since the neg_literal_c points to the integer_c, and not - * vice-versa, we can't determine that. - * There are 3 simple ways of working around this: - * - change the order of the pointers: - * have the integer_c and real_c point to the neg_literal_c - * - maintain the order of the pointers, and - * add redundant info to the integer_c and real_c + * However, this has since been changed to... * - replace the neg_literal_c with two distinc classes * (neg_integer_c and neg_real_c), one for each - * lietral type. This means that we can now treat - * each of these classes as an unknown data type - * just as we do with the integer_c and real_c. - * - * The second option is simply ugly. - * and the first has a serious drawback: when generating code it is - * easier to encapsulate the real or integer values inside prefix - * and postfix symbols (e.g. NEG() - with postfix ')' ) - * if we keep the pointer order as is. - * - * For the above reasoning, we use the third option. + * lietral type. + * + * This change was done in order to ease the writing of semantic verification (stage3) code. + * However, that version of the code has since been replaced by a newer and better algoritm. + * This means the above change can now be undone, but there is really no need to undo it, + * so we leave it as it is. */ SYM_REF1(neg_real_c, exp) SYM_REF1(neg_integer_c, exp) @@ -223,13 +210,15 @@ /************************/ SYM_REF0(neg_time_c) SYM_REF3(duration_c, type_name, neg, interval) +SYM_REF5(interval_c, days, hours, minutes, seconds, milliseconds) SYM_TOKEN(fixed_point_c) +/* SYM_REF2(days_c, days, hours) SYM_REF2(hours_c, hours, minutes) SYM_REF2(minutes_c, minutes, seconds) SYM_REF2(seconds_c, seconds, milliseconds) SYM_REF1(milliseconds_c, milliseconds) - +*/ /************************************/ /* B 1.2.3.2 - Time of day and Date */ @@ -326,7 +315,8 @@ SYM_REF2(subrange_specification_c, integer_type_name, subrange) /* signed_integer DOTDOT signed_integer */ -SYM_REF2(subrange_c, lower_limit, upper_limit) +/* dimension will be filled in during stage 3 (array_range_check_c) with the number of elements in this subrange */ +SYM_REF2(subrange_c, lower_limit, upper_limit, unsigned long long int dimension;) /* enumerated_type_name ':' enumerated_spec_init */ SYM_REF2(enumerated_type_declaration_c, enumerated_type_name, enumerated_spec_init) @@ -476,8 +466,13 @@ /* record_variable '.' field_selector */ /* WARNING: input and/or output variables of function blocks * may be accessed as fields of a structured variable! - * Code handling a structured_variable_c must take - * this into account! + * Code handling a structured_variable_c must take this into account! + * (i.e. that a FB instance may be accessed as a structured variable)! + * + * WARNING: Status bit (.X) and activation time (.T) of STEPS in SFC diagrams + * may be accessed as fields of a structured variable! + * Code handling a structured_variable_c must take this into account + * (i.e. that an SFC STEP may be accessed as a structured variable)! */ SYM_REF2(structured_variable_c, record_variable, field_selector) @@ -913,18 +908,22 @@ SYM_LIST(instruction_list_c) /* | label ':' [il_incomplete_instruction] eol_list */ -SYM_REF2(il_instruction_c, label, il_instruction) +/* NOTE: The parameter 'prev_il_instruction' is used to point to all previous il instructions that may be executed imedaitely before this instruction. + * In case of an il instruction preceded by a label, this will include all IL instructions that jump to this label! + * It is filled in by the flow_control_analysis_c during stage 3. + */ +SYM_REF2(il_instruction_c, label, il_instruction, std::vector prev_il_instruction;) /* | il_simple_operator [il_operand] */ SYM_REF2(il_simple_operation_c, il_simple_operator, il_operand) /* | function_name [il_operand_list] */ -/* NOTE: The parameters 'called_function_declaration' is used to pass +/* NOTE: The parameter 'called_function_declaration', 'extensible_param_count' and 'candidate_functions' are used to pass data between the stage 3 and stage 4. * data between the stage 3 and stage 4. * See the comment above function_invocation_c for more details */ -SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;) +SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count; std::vector candidate_functions;) /* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ @@ -939,15 +938,15 @@ * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' */ -SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list) +/* NOTE: The parameter 'called_fb_declaration'is used to pass data between stage 3 and stage4 (although currently it is not used in stage 4 */ +SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list, symbol_c *called_fb_declaration;) /* | function_name '(' eol_list [il_param_list] ')' */ -/* NOTE: The parameters 'called_function_declaration' is used to pass - * data between the stage 3 and stage 4. - * See the comment above function_invocation_c for more details - */ -SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;) +/* NOTE: The parameter 'called_function_declaration', 'extensible_param_count' and 'candidate_functions' are used to pass data between the stage 3 and stage 4. + * See the comment above function_invocation_c for more details. + */ +SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count; std::vector candidate_functions;) /* | il_operand_list ',' il_operand */ SYM_LIST(il_operand_list_c) @@ -955,6 +954,15 @@ /* | simple_instr_list il_simple_instruction */ SYM_LIST(simple_instr_list_c) + +/* il_simple_instruction: + * il_simple_operation eol_list + * | il_expression eol_list + * | il_formal_funct_call eol_list + */ +/* NOTE: The parameter 'prev_il_instruction' is used to point to all previous il instructions that may be executed imedaitely before this instruction. */ +SYM_REF1(il_simple_instruction_c, il_simple_instruction, std::vector prev_il_instruction;) + /* | il_initial_param_list il_param_instruction */ SYM_LIST(il_param_list_c) @@ -971,31 +979,36 @@ /*******************/ /* B 2.2 Operators */ /*******************/ +/* NOTE: The parameter 'called_fb_declaration' is used to pass data between stage 3 and stage4 (although currently it is not used in stage 4 */ +/* NOTE: The parameter 'deprecated_operation' indicates that the operation, with the specific data types being used, is currently defined + * in the standard as being deprecated. This variable is filled in with the correct value in stage 3 (narrow_candidate_datatypes_c) + * and currently only used in stage 3 (print_datatypes_error_c). + */ SYM_REF0(LD_operator_c) SYM_REF0(LDN_operator_c) SYM_REF0(ST_operator_c) SYM_REF0(STN_operator_c) SYM_REF0(NOT_operator_c) -SYM_REF0(S_operator_c) -SYM_REF0(R_operator_c) -SYM_REF0(S1_operator_c) -SYM_REF0(R1_operator_c) -SYM_REF0(CLK_operator_c) -SYM_REF0(CU_operator_c) -SYM_REF0(CD_operator_c) -SYM_REF0(PV_operator_c) -SYM_REF0(IN_operator_c) -SYM_REF0(PT_operator_c) +SYM_REF0(S_operator_c, symbol_c *called_fb_declaration;) +SYM_REF0(R_operator_c, symbol_c *called_fb_declaration;) +SYM_REF0(S1_operator_c, symbol_c *called_fb_declaration;) +SYM_REF0(R1_operator_c, symbol_c *called_fb_declaration;) +SYM_REF0(CLK_operator_c, symbol_c *called_fb_declaration;) +SYM_REF0(CU_operator_c, symbol_c *called_fb_declaration;) +SYM_REF0(CD_operator_c, symbol_c *called_fb_declaration;) +SYM_REF0(PV_operator_c, symbol_c *called_fb_declaration;) +SYM_REF0(IN_operator_c, symbol_c *called_fb_declaration;) +SYM_REF0(PT_operator_c, symbol_c *called_fb_declaration;) SYM_REF0(AND_operator_c) SYM_REF0(OR_operator_c) SYM_REF0(XOR_operator_c) SYM_REF0(ANDN_operator_c) SYM_REF0(ORN_operator_c) SYM_REF0(XORN_operator_c) -SYM_REF0(ADD_operator_c) -SYM_REF0(SUB_operator_c) -SYM_REF0(MUL_operator_c) -SYM_REF0(DIV_operator_c) +SYM_REF0(ADD_operator_c, bool deprecated_operation;) +SYM_REF0(SUB_operator_c, bool deprecated_operation;) +SYM_REF0(MUL_operator_c, bool deprecated_operation;) +SYM_REF0(DIV_operator_c, bool deprecated_operation;) SYM_REF0(MOD_operator_c) SYM_REF0(GT_operator_c) SYM_REF0(GE_operator_c) @@ -1036,10 +1049,10 @@ SYM_REF2(gt_expression_c, l_exp, r_exp) SYM_REF2(le_expression_c, l_exp, r_exp) SYM_REF2(ge_expression_c, l_exp, r_exp) -SYM_REF2(add_expression_c, l_exp, r_exp) -SYM_REF2(sub_expression_c, l_exp, r_exp) -SYM_REF2(mul_expression_c, l_exp, r_exp) -SYM_REF2(div_expression_c, l_exp, r_exp) +SYM_REF2(add_expression_c, l_exp, r_exp, bool deprecated_operation;) +SYM_REF2(sub_expression_c, l_exp, r_exp, bool deprecated_operation;) +SYM_REF2(mul_expression_c, l_exp, r_exp, bool deprecated_operation;) +SYM_REF2(div_expression_c, l_exp, r_exp, bool deprecated_operation;) SYM_REF2(mod_expression_c, l_exp, r_exp) SYM_REF2(power_expression_c, l_exp, r_exp) SYM_REF1(neg_expression_c, exp) @@ -1047,18 +1060,36 @@ /* formal_param_list -> may be NULL ! */ /* nonformal_param_list -> may be NULL ! */ -/* NOTE: The parameters 'called_function_declaration' is used to pass - * data between the stage 3 and stage 4. +/* NOTES: + * The parameter 'called_function_declaration'... + * ...is used to pass data between the stage 3 and stage 4. * The IEC 61131-3 standard allows for overloaded standard functions. This means that some - * function calls are not compeletely defined by the name of the function being called, + * function calls are not completely defined by the name of the function being called, * and need to be disambiguated with using the data types of the parameters being passed. - * Stage 3 does this to verify semantic correctnes. + * Stage 3 does this to verify semantic correctness. * Stage 4 also needs to do this in order to determine which function to call. * It does not make sense to determine the exact function being called twice (once in stage 3, - * and again in stage 4), so stage 3 will store this infor in the parameter called_function_declaration + * and again in stage 4), so stage 3 will store this info in the parameter called_function_declaration * for stage 4 to use it later on. - */ -SYM_REF3(function_invocation_c, function_name, formal_param_list, nonformal_param_list, symbol_c *called_function_declaration; int extensible_param_count;) + * The parameter 'candidate_functions'... + * ...is used to pass data between two passes within stage 3 + * (actually between fill_candidate_datatypes_c and narrow_candidate_datatypes_c). + * It is used to store all the functions that may be legally called with the current parameters + * being used in this function invocation. Note that the standard includes some standard functions + * that have the exact same input parameter types, but return different data types. + * In order to determine which of these functions should be called, we first create a list + * of all possible functions, and then narrow down the list (hopefully down to 1 function) + * once we know the data type that the function invocation must return (this will take into + * account the expression in which the function invocation is inserted/occurs). + * The 'called_function_declaration' will eventually be set (in stage 3) to one of + * the functions in the 'candidate_functions' list! + * The parameter 'extensible_param_count'... + * ...is used to pass data between the stage 3 and stage 4. + * The IEC 61131-3 standard allows for extensible standard functions. This means that some + * standard functions may be called with a variable number of paramters. Stage 3 will store + * in extensible_param_count the number of parameters being passed to the extensible parameter. + */ +SYM_REF3(function_invocation_c, function_name, formal_param_list, nonformal_param_list, symbol_c *called_function_declaration; int extensible_param_count; std::vector candidate_functions;) /********************/ @@ -1083,7 +1114,8 @@ /* fb_name '(' [param_assignment_list] ')' */ /* formal_param_list -> may be NULL ! */ /* nonformal_param_list -> may be NULL ! */ -SYM_REF3(fb_invocation_c, fb_name, formal_param_list, nonformal_param_list) +/* NOTE: The parameter 'called_fb_declaration'is used to pass data between stage 3 and stage4 (although currently it is not used in stage 4 */ +SYM_REF3(fb_invocation_c, fb_name, formal_param_list, nonformal_param_list, symbol_c *called_fb_declaration;) /* helper symbol for fb_invocation */ /* param_assignment_list ',' param_assignment */ diff -r aad38592bdde -r c0bda77b37a0 absyntax/absyntax.hh --- a/absyntax/absyntax.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax/absyntax.hh Wed Aug 22 16:46:17 2012 +0200 @@ -47,9 +47,16 @@ #include // required for NULL +#include +#include +#include // required for uint64_t, etc... +#include "../main.hh" // required for uint8_t, real_64_t, ..., and the macros INT8_MAX, REAL32_MAX, ... */ + + + /* Forward declaration of the visitor interface - * dclared in the visitor.hh file + * declared in the visitor.hh file * We cannot include the visitor.hh file, as it will * include this same file first, as it too requires references * to the abstract syntax classes defined here. @@ -61,12 +68,21 @@ + + + + + + + + /* The base class of all symbols */ class symbol_c { public: /* - * Line number for the purposes of error checking + * Line number for the purposes of error checking. + * Annotated (inserted) by stage1_2 */ int first_line; int first_column; @@ -77,6 +93,49 @@ const char *last_file; /* filename referenced by last line/column */ long int last_order; /* relative order in which it is read by lexcial analyser */ + + /* + * Annotations produced during stage 3 + */ + /*** Data type analysis ***/ + std::vector candidate_datatypes; /* All possible data types the expression/literal/etc. may take. Filled in stage3 by fill_candidate_datatypes_c class */ + /* Data type of the expression/literal/etc. Filled in stage3 by narrow_candidate_datatypes_c + * If set to NULL, it means it has not yet been evaluated. + * If it points to an object of type invalid_type_name_c, it means it is invalid. + * Otherwise, it points to an object of the apropriate data type (e.g. int_type_name_c, bool_type_name_c, ...) + */ + symbol_c *datatype; + + /*** constant folding ***/ + /* During stage 3 (semantic analysis/checking) we will be doing constant folding. + * That algorithm will anotate the abstract syntax tree with the result of operations + * on literals (i.e. 44 + 55 will store the result 99). + * Since the same source code (e.g. 1 + 0) may actually be a BOOL or an ANY_INT, + * or an ANY_BIT, we need to handle all possibilities, and determine the result of the + * operation assuming each type. + * For this reason, we have one entry for each possible type, with some expressions + * having more than one entry filled in! + */ + typedef enum { cs_undefined, /* not defined/not yet evaluated --> const_value is not valid! */ + cs_non_const, /* we have deternmined that expression is not a const value --> const_value is not valid! */ + cs_const_value, /* const value is valid */ + cs_overflow /* result produced overflow or underflow --> const_value is not valid! */ + } const_status_t; + + typedef struct {const_status_t status; real64_t value; } const_value_real64_t; + typedef struct {const_status_t status; int64_t value; } const_value_int64_t; + typedef struct {const_status_t status; uint64_t value; } const_value_uint64_t; + typedef struct {const_status_t status; bool value; } const_value_bool_t; + + typedef struct { + const_value_real64_t _real64; /* status is initialised to UNDEFINED */ + const_value_int64_t _int64; /* status is initialised to UNDEFINED */ + const_value_uint64_t _uint64; /* status is initialised to UNDEFINED */ + const_value_bool_t _bool; /* status is initialised to UNDEFINED */ + } const_value_t; + const_value_t const_value; + + public: /* default constructor */ symbol_c(int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, /* order in which it is read by lexcial analyser */ @@ -125,12 +184,14 @@ /* To insert into the begining of list, call with pos=0 */ /* To insert into the end of list, call with pos=list->n */ virtual void insert_element(symbol_c *elem, int pos = 0); -}; - - - - -#define SYM_LIST(class_name_c, ...) \ + /* remove element at position pos. */ + virtual void remove_element(int pos = 0); +}; + + + + +#define SYM_LIST(class_name_c, ...) \ class class_name_c: public list_c { \ public: \ __VA_ARGS__ \ @@ -145,7 +206,7 @@ }; -#define SYM_TOKEN(class_name_c, ...) \ +#define SYM_TOKEN(class_name_c, ...) \ class class_name_c: public token_c { \ public: \ __VA_ARGS__ \ @@ -157,7 +218,7 @@ }; -#define SYM_REF0(class_name_c, ...) \ +#define SYM_REF0(class_name_c, ...) \ class class_name_c: public symbol_c { \ public: \ __VA_ARGS__ \ @@ -233,7 +294,7 @@ }; -#define SYM_REF5(class_name_c, ref1, ref2, ref3, ref4, ref5, ...) \ +#define SYM_REF5(class_name_c, ref1, ref2, ref3, ref4, ref5, ...) \ class class_name_c: public symbol_c { \ public: \ symbol_c *ref1; \ @@ -291,6 +352,4 @@ #undef SYM_REF5 #undef SYM_REF6 - - #endif /* _ABSYNTAX_HH */ diff -r aad38592bdde -r c0bda77b37a0 absyntax/visitor.cc --- a/absyntax/visitor.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax/visitor.cc Wed Aug 22 16:46:17 2012 +0200 @@ -73,13 +73,13 @@ #define SYM_LIST(class_name_c) \ void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} -#define SYM_TOKEN(class_name_c) \ - void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} - -#define SYM_REF0(class_name_c) \ - void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} - -#define SYM_REF1(class_name_c, ref1) \ +#define SYM_TOKEN(class_name_c, ...) \ + void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} + +#define SYM_REF0(class_name_c, ...) \ + void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} + +#define SYM_REF1(class_name_c, ref1, ...) \ void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} #define SYM_REF2(class_name_c, ref1, ref2, ...) \ @@ -136,13 +136,13 @@ #define SYM_LIST(class_name_c) \ void *iterator_visitor_c::visit(class_name_c *symbol) {return visit_list(symbol);} -#define SYM_TOKEN(class_name_c) \ +#define SYM_TOKEN(class_name_c, ...) \ void *iterator_visitor_c::visit(class_name_c *symbol) {return NULL;} -#define SYM_REF0(class_name_c) \ +#define SYM_REF0(class_name_c, ...) \ void *iterator_visitor_c::visit(class_name_c *symbol) {return NULL;} -#define SYM_REF1(class_name_c, ref1) \ +#define SYM_REF1(class_name_c, ref1, ...) \ void *iterator_visitor_c::visit(class_name_c *symbol) { \ if (symbol->ref1!=NULL) symbol->ref1->accept(*this); \ return NULL; \ @@ -238,13 +238,13 @@ #define SYM_LIST(class_name_c) \ void *search_visitor_c::visit(class_name_c *symbol) {return visit_list(symbol);} -#define SYM_TOKEN(class_name_c) \ +#define SYM_TOKEN(class_name_c, ...) \ void *search_visitor_c::visit(class_name_c *symbol) {return NULL;} -#define SYM_REF0(class_name_c) \ +#define SYM_REF0(class_name_c, ...) \ void *search_visitor_c::visit(class_name_c *symbol) {return NULL;} -#define SYM_REF1(class_name_c, ref1) \ +#define SYM_REF1(class_name_c, ref1, ...) \ void *search_visitor_c::visit(class_name_c *symbol) { \ if (symbol->ref1) return symbol->ref1->accept(*this); \ return NULL; \ diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/Makefile.am --- a/absyntax_utils/Makefile.am Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/Makefile.am Wed Aug 22 16:46:17 2012 +0200 @@ -11,8 +11,9 @@ function_call_iterator.cc \ function_call_param_iterator.cc \ function_param_iterator.cc \ - get_function_type.cc \ get_sizeof_datatype.cc \ + get_var_name.cc \ + search_il_label.cc \ search_base_type.cc \ search_constant_type.cc \ search_expression_type.cc \ diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/Makefile.in --- a/absyntax_utils/Makefile.in Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/Makefile.in Wed Aug 22 16:46:17 2012 +0200 @@ -77,8 +77,9 @@ case_element_iterator.$(OBJEXT) \ function_call_iterator.$(OBJEXT) \ function_call_param_iterator.$(OBJEXT) \ - function_param_iterator.$(OBJEXT) get_function_type.$(OBJEXT) \ - get_sizeof_datatype.$(OBJEXT) search_base_type.$(OBJEXT) \ + function_param_iterator.$(OBJEXT) \ + get_sizeof_datatype.$(OBJEXT) get_var_name.$(OBJEXT) \ + search_il_label.$(OBJEXT) search_base_type.$(OBJEXT) \ search_constant_type.$(OBJEXT) \ search_expression_type.$(OBJEXT) \ search_fb_instance_decl.$(OBJEXT) search_fb_typedecl.$(OBJEXT) \ @@ -211,8 +212,9 @@ function_call_iterator.cc \ function_call_param_iterator.cc \ function_param_iterator.cc \ - get_function_type.cc \ get_sizeof_datatype.cc \ + get_var_name.cc \ + search_il_label.cc \ search_base_type.cc \ search_constant_type.cc \ search_expression_type.cc \ @@ -312,13 +314,14 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/function_call_iterator.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/function_call_param_iterator.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/function_param_iterator.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_function_type.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_sizeof_datatype.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_var_name.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search_base_type.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search_constant_type.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search_expression_type.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search_fb_instance_decl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search_fb_typedecl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search_il_label.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search_var_instance_decl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search_varfb_instance_type.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spec_init_separator.Po@am__quote@ diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/absyntax_utils.cc --- a/absyntax_utils/absyntax_utils.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/absyntax_utils.cc Wed Aug 22 16:46:17 2012 +0200 @@ -50,12 +50,14 @@ #include #include #include -#include /* required for strlen() */ -#include /* required for atoi() */ +// #include /* required for strlen() */ +// #include /* required for atoi() */ +// #include /* required for errno */ #include "../util/symtable.hh" #include "../util/dsymtable.hh" #include "../absyntax/visitor.hh" +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. @@ -66,9 +68,6 @@ #define TRACE(classname) #endif -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); /***********************************************************************/ @@ -96,25 +95,6 @@ } -/* extract the value of an integer from an integer_c object !! */ -/* NOTE: it must ignore underscores! */ -int extract_integer(symbol_c *sym) { - std::string str = ""; - integer_c *integer; - neg_integer_c * neg_integer; - - if ((neg_integer = dynamic_cast(sym)) != NULL) - return - extract_integer((integer_c *)neg_integer->exp); - - if ((integer = dynamic_cast(sym)) == NULL) ERROR; - - for(unsigned int i = 0; i < strlen(integer->value); i++) - if (integer->value[i] != '_') str += integer->value[i]; - - return atoi(str.c_str()); -} - - /***********************************************************************/ /***********************************************************************/ @@ -294,7 +274,17 @@ } - + /* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ + // SYM_REF4(string_type_declaration_c, string_type_name, + // elementary_string_type_name, + // string_type_declaration_size, + // string_type_declaration_init) /* may be == NULL! */ + void *visit(string_type_declaration_c *symbol) { + TRACE("string_type_declaration_c"); + type_symtable.insert(symbol->string_type_name, symbol); + return NULL; +} + /*********************/ /* B 1.4 - Variables */ /*********************/ diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/absyntax_utils.hh --- a/absyntax_utils/absyntax_utils.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/absyntax_utils.hh Wed Aug 22 16:46:17 2012 +0200 @@ -55,9 +55,6 @@ /* returns 0 if the names are equal!! Case is ignored. */ int compare_identifiers(symbol_c *ident1, symbol_c *ident2); -/* extract the value of an integer from an integer_c object !! */ -int extract_integer(symbol_c *integer); - /* A symbol table with all globally declared functions... */ extern function_declaration_c null_symbol1; typedef dsymtable_c function_symtable_t; @@ -119,8 +116,8 @@ #include "search_expression_type.hh" #include "add_en_eno_param_decl.hh" #include "get_sizeof_datatype.hh" -#include "get_function_type.h" - +#include "search_il_label.hh" +#include "get_var_name.hh" /***********************************************************************/ /***********************************************************************/ diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/add_en_eno_param_decl.cc --- a/absyntax_utils/add_en_eno_param_decl.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/add_en_eno_param_decl.cc Wed Aug 22 16:46:17 2012 +0200 @@ -44,6 +44,7 @@ #include "add_en_eno_param_decl.hh" #include +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. // #define DEBUG @@ -54,9 +55,6 @@ #endif -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/array_dimension_iterator.cc --- a/absyntax_utils/array_dimension_iterator.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/array_dimension_iterator.cc Wed Aug 22 16:46:17 2012 +0200 @@ -49,6 +49,7 @@ #include "array_dimension_iterator.hh" +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. //#define DEBUG @@ -59,9 +60,6 @@ #endif -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); void* array_dimension_iterator_c::iterate_list(list_c *list) { void *res; @@ -102,7 +100,7 @@ * * Returns the subrange symbol! */ -symbol_c *array_dimension_iterator_c::next(void) { +subrange_c *array_dimension_iterator_c::next(void) { void *res = array_specification->accept(*this); if (res == NULL) return NULL; diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/array_dimension_iterator.hh --- a/absyntax_utils/array_dimension_iterator.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/array_dimension_iterator.hh Wed Aug 22 16:46:17 2012 +0200 @@ -54,7 +54,7 @@ /* a pointer to the array_specification_c currently being analyzed */ symbol_c *array_specification; /* used when called to iterate() for a parameter */ - symbol_c *current_array_dimension; + subrange_c *current_array_dimension; private: void* iterate_list(list_c *list); @@ -64,7 +64,7 @@ void reset(void); /* initialize the iterator object. - * We must be given a reference to a case_list_c that will be analyzed... + * We must be given a reference to a array_specification_c that will be analyzed... */ array_dimension_iterator_c(symbol_c *symbol); @@ -75,7 +75,8 @@ * * Returns the subrange symbol! */ - symbol_c *next(void); + subrange_c *next(void); + private: diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/case_element_iterator.cc --- a/absyntax_utils/case_element_iterator.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/case_element_iterator.cc Wed Aug 22 16:46:17 2012 +0200 @@ -49,6 +49,7 @@ #include "case_element_iterator.hh" +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. //#define DEBUG @@ -59,9 +60,6 @@ #endif -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); void* case_element_iterator_c::handle_case_element(symbol_c *case_element) { diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/function_call_iterator.cc --- a/absyntax_utils/function_call_iterator.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/function_call_iterator.cc Wed Aug 22 16:46:17 2012 +0200 @@ -45,6 +45,7 @@ #include "function_call_iterator.hh" +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. //#define DEBUG @@ -54,10 +55,6 @@ #define TRACE(classname) #endif -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); - diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/function_call_param_iterator.cc --- a/absyntax_utils/function_call_param_iterator.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/function_call_param_iterator.cc Wed Aug 22 16:46:17 2012 +0200 @@ -48,6 +48,7 @@ #include "function_call_param_iterator.hh" #include +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. //#define DEBUG @@ -57,9 +58,6 @@ #define TRACE(classname) #endif -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); @@ -184,6 +182,7 @@ */ symbol_c *function_call_param_iterator_c::next_f(void) { current_value = NULL; + current_assign_direction = assign_none; param_count = 0; iterate_f_next_param++; current_operation = function_call_param_iterator_c::iterate_f_op; @@ -201,6 +200,7 @@ */ symbol_c *function_call_param_iterator_c::next_nf(void) { current_value = NULL; + current_assign_direction = assign_none; param_count = 0; iterate_nf_next_param++; current_operation = function_call_param_iterator_c::iterate_nf_op; @@ -212,6 +212,7 @@ /* Search for the value passed to the parameter named ... */ symbol_c *function_call_param_iterator_c::search_f(symbol_c *param_name) { current_value = NULL; + current_assign_direction = assign_none; if (NULL == param_name) ERROR; search_param_name = dynamic_cast(param_name); if (NULL == search_param_name) ERROR; @@ -233,6 +234,12 @@ return current_value; } +/* Returns the value being passed to the current parameter. */ +function_call_param_iterator_c::assign_direction_t function_call_param_iterator_c::get_assign_direction(void) { + return current_assign_direction; +} + + /********************************/ /* B 1.7 Configuration elements */ /********************************/ @@ -339,6 +346,7 @@ if (NULL == symb_var) ERROR; + current_assign_direction = assign_in; return handle_parameter_assignment(symb_var->var_name, symbol->prog_data_source); } @@ -355,6 +363,7 @@ if (NULL == symb_var) ERROR; + current_assign_direction = assign_out; return handle_parameter_assignment(symb_var->var_name, symbol->data_sink); } @@ -459,6 +468,7 @@ // since we do not yet support it, it is best to simply stop than to fail silently... if (NULL != symbol->simple_instr_list) ERROR; + current_assign_direction = assign_in; return handle_parameter_assignment((symbol_c *)symbol->il_assign_operator->accept(*this), symbol->il_operand); } @@ -466,6 +476,7 @@ // SYM_REF2(il_param_out_assignment_c, il_assign_out_operator, variable); void *function_call_param_iterator_c::visit(il_param_out_assignment_c *symbol) { TRACE("il_param_out_assignment_c"); + current_assign_direction = assign_out; return handle_parameter_assignment((symbol_c *)symbol->il_assign_out_operator->accept(*this), symbol->variable); } @@ -564,6 +575,7 @@ // SYM_REF2(input_variable_param_assignment_c, variable_name, expression) void *function_call_param_iterator_c::visit(input_variable_param_assignment_c *symbol) { TRACE("input_variable_param_assignment_c"); + current_assign_direction = assign_in; return handle_parameter_assignment(symbol->variable_name, symbol->expression); } @@ -574,6 +586,7 @@ // TODO : Handle not_param !!! if (NULL != symbol->not_param) ERROR; // we do not yet support it, so it is best to simply stop than to fail silently... + current_assign_direction = assign_out; return handle_parameter_assignment(symbol->variable_name, symbol->variable); } diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/function_call_param_iterator.hh --- a/absyntax_utils/function_call_param_iterator.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/function_call_param_iterator.hh Wed Aug 22 16:46:17 2012 +0200 @@ -51,6 +51,58 @@ class function_call_param_iterator_c : public null_visitor_c { private: + void *search_list(list_c *list); + void *handle_parameter_assignment(symbol_c *variable_name, symbol_c *expression) ; + + + public: + /* start off at the first parameter once again... */ + void reset(void); + + /* initialise the iterator object. + * We must be given a reference to the function/program/function block call + * that will be analysed... + */ + function_call_param_iterator_c(symbol_c *f_call); + + /* Skip to the next formal parameter. After object creation, + * the object references on parameter _before_ the first, so + * this function must be called once to get the object to + * reference the first parameter... + * + * Returns the paramater name to which a value is being passed! + * You can determine the value being passed by calling + * function_call_param_iterator_c::search_f() + */ + symbol_c *next_f(void); + + /* Skip to the next non-formal parameter. After object creation, + * the object references on parameter _before_ the first, so + * this function must be called once to get the object to + * reference the first parameter... + * + * Returns whatever is being passed to the parameter! + */ + symbol_c *next_nf(void); + + /* Search for the value passed to the parameter named ... */ + symbol_c *search_f(symbol_c *param_name); + symbol_c *search_f(const char *param_name); + + /* Returns the value being passed to the current parameter. */ + symbol_c *get_current_value(void); + + /* A type to specify how the current parameter was assigned. + * param_name := var -> assign_in + * param_name => var -> assign_out + * -> assign_none (used when handling non formal calls, when no assignment type is used) + */ + typedef enum {assign_in, assign_out, assign_none} assign_direction_t ; + /* Returns the assignment direction of the current parameter. */ + assign_direction_t get_assign_direction(void); + + + private: /* a pointer to the function call * (or function block or program call!) */ @@ -58,6 +110,7 @@ int iterate_f_next_param, iterate_nf_next_param, param_count; identifier_c *search_param_name; symbol_c *current_value; + assign_direction_t current_assign_direction; /* Which operation of the class was called: * - iterate to the next non-formal parameter. @@ -67,49 +120,7 @@ typedef enum {iterate_nf_op, iterate_f_op, search_f_op} operation_t; operation_t current_operation; - private: - void *search_list(list_c *list); - void *handle_parameter_assignment(symbol_c *variable_name, symbol_c *expression) ; - - - public: - /* start off at the first parameter once again... */ - void reset(void); - - /* initialise the iterator object. - * We must be given a reference to the function/program/function block call - * that will be analysed... - */ - function_call_param_iterator_c(symbol_c *f_call); - - /* Skip to the next formal parameter. After object creation, - * the object references on parameter _before_ the first, so - * this function must be called once to get the object to - * reference the first parameter... - * - * Returns the paramater name to which a value is being passed! - * You can determine the value being passed by calling - * function_call_param_iterator_c::search_f() - */ - symbol_c *next_f(void); - - /* Skip to the next non-formal parameter. After object creation, - * the object references on parameter _before_ the first, so - * this function must be called once to get the object to - * reference the first parameter... - * - * Returns whatever is being passed to the parameter! - */ - symbol_c *next_nf(void); - - /* Search for the value passed to the parameter named ... */ - symbol_c *search_f(symbol_c *param_name); - symbol_c *search_f(const char *param_name); - - /* Returns the value being passed to the current parameter. */ - symbol_c *get_current_value(void); - - + private: /********************************/ /* B 1.7 Configuration elements */ diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/function_param_iterator.cc --- a/absyntax_utils/function_param_iterator.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/function_param_iterator.cc Wed Aug 22 16:46:17 2012 +0200 @@ -47,14 +47,14 @@ */ - -#include "absyntax_utils.hh" /* required for extract_integer() */ -// #include "function_param_iterator.hh" /* no longer required, aready included by absyntax_utils.hh */ -// #include "spec_init_separator.hh" /* no longer required, aready included by absyntax_utils.hh */ +#include "function_param_iterator.hh" /* no longer required, aready included by absyntax_utils.hh */ +#include "spec_init_separator.hh" /* no longer required, aready included by absyntax_utils.hh */ #include /* required for strtol() */ #include #include - +#include // required for std::numeric_limits< XXX >::max() +#include // required for errno +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. //#define DEBUG #ifdef DEBUG @@ -64,9 +64,59 @@ #endif -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); + + + +/* NOTE: The following function is not really needed, as we could get the value that constant_folding_c determined for this + * integer. Remember that currently constant_folding_c runs before this class is ever used/called! + * However, I (Mario) do not currently feel it would be a good idea to restrict the use of this + * abstract syntax utility to only after the constant_folding_c has had a chance to fill in the constant value + * of this symbol. + * For this reason only, I have opted to let this abstract syntax utility have its own private copy of the + * extract_integer() function. + * Another aspect that makes this OK is that this function will only be used to extract the integer value of the + * index for the first extensible paramater (examples follow shortly). Since this is an extension to IEC 61131-3 + * that we created to allow us to handle extensible functions with very little hard coding, it is OK if we + * impose extra/different limits on how an integer may be legally be formated in this case. This will also + * only show up in code that describes the interface to the standard function of IEC 61131-3, which the user + * will not ever get to see. We write that IEC 61131-3 code ourselves! + * + * Example of source code we will be parsing and analysing: + * + * FUNCTION ADD : REAL VAR_INPUT IN 1 .. : REAL; END_VAR RETURN; END_FUNCTION + * ^^^ + * + * FUNCTION MUX : REAL VAR_INPUT K : USINT; IN 0 .. : REAL; END_VAR RETURN; END_FUNCTION + * ^^^ + * + * Basically, currently this will only be either a '0' or a '1' !! + */ + +/* NOTE: it must ignore underscores! */ +static int extract_first_index_value(symbol_c *sym) { + std::string str = ""; + integer_c *integer; + long int ret; + + if ((integer = dynamic_cast(sym)) == NULL) ERROR; + for(unsigned int i = 0; i < strlen(integer->value); i++) + if (integer->value[i] != '_') str += integer->value[i]; + + errno = 0; // since strtoXX() may legally return 0, we must set errno to 0 to detect errors correctly! + ret = strtol(str.c_str(), NULL, 10); + if (errno != 0) ERROR; + if (ret < 0) ERROR; // the following code assumes that the first index will never be negative! + if (ret > std::numeric_limits< int >::max()) ERROR; // output of this function is only an int!! + + return ret; +} + + + + + + + @@ -130,7 +180,7 @@ if (extensible_parameter != NULL) { sym = extensible_parameter->var_name; current_param_is_extensible = true; - _first_extensible_param_index = extract_integer(extensible_parameter->first_index); + _first_extensible_param_index = extract_first_index_value(extensible_parameter->first_index); } identifier_c *variable_name = dynamic_cast(sym); if (variable_name == NULL) ERROR; @@ -167,7 +217,7 @@ if (extensible_parameter != NULL) { var_name = extensible_parameter->var_name; current_param_is_extensible = true; - _first_extensible_param_index = extract_integer(extensible_parameter->first_index); + _first_extensible_param_index = extract_first_index_value(extensible_parameter->first_index); } identifier_c *variable_name = dynamic_cast(var_name); if (variable_name == NULL) ERROR; @@ -206,7 +256,9 @@ _first_extensible_param_index = -1; current_param_is_extensible = false; current_param_name = NULL; - current_param_type = current_param_default_value = NULL; + current_param_type = NULL; + current_param_default_value = NULL; + last_returned_parameter = NULL; /* the last parameter returned by search() or next() */ } @@ -223,7 +275,8 @@ function_block_declaration_c *fb_decl = dynamic_cast(pou_decl); program_declaration_c * p_decl = dynamic_cast(pou_decl); - if ((NULL == f_decl) && (NULL == fb_decl) && (NULL == p_decl)) ERROR; + if ((NULL == f_decl) && (NULL == fb_decl) && (NULL == p_decl)) + ERROR; /* OK. Now initialise this object... */ this->f_decl = pou_decl; @@ -248,6 +301,7 @@ return current_param_name; } + last_returned_parameter = NULL; param_count = 0; en_eno_param_implicit = false; next_param++; @@ -261,13 +315,14 @@ if (extensible_parameter != NULL) { sym = extensible_parameter->var_name; current_param_is_extensible = true; - _first_extensible_param_index = extract_integer(extensible_parameter->first_index); + _first_extensible_param_index = extract_first_index_value(extensible_parameter->first_index); current_extensible_param_index = _first_extensible_param_index; } identifier = dynamic_cast(sym); if (identifier == NULL) ERROR; current_param_name = identifier; + last_returned_parameter = current_param_name; return current_param_name; } @@ -281,35 +336,53 @@ current_operation = function_param_iterator_c::search_op; void *res = f_decl->accept(*this); identifier_c *res_param_name = dynamic_cast((symbol_c *)res); + last_returned_parameter = res_param_name; return res_param_name; } +identifier_c *function_param_iterator_c::search(const char *param_name) { + identifier_c param_name_id(param_name); + return search(¶m_name_id); +} + + + /* Returns the currently referenced parameter's default value, * or NULL if none is specified in the function declrataion itself. */ symbol_c *function_param_iterator_c::default_value(void) { + if (NULL == last_returned_parameter) + return NULL; return current_param_default_value; } /* Returns the currently referenced parameter's type name. */ symbol_c *function_param_iterator_c::param_type(void) { + if (NULL == last_returned_parameter) + return NULL; return current_param_type; } /* Returns if currently referenced parameter is an implicit defined EN/ENO parameter. */ bool function_param_iterator_c::is_en_eno_param_implicit(void) { + if (NULL == last_returned_parameter) + ERROR; return en_eno_param_implicit; } /* Returns if currently referenced parameter is an extensible parameter. */ /* extensible paramters only occur in some standard functions, e.g. AND(word#34, word#44, word#65); */ bool function_param_iterator_c::is_extensible_param(void) { + if (NULL == last_returned_parameter) + ERROR; return current_param_is_extensible; } /* Returns the index of the current extensible parameter. */ /* If the current parameter is not an extensible paramter, returns -1 */ int function_param_iterator_c::extensible_param_index(void) { + if (NULL == last_returned_parameter) + ERROR; return (current_param_is_extensible? current_extensible_param_index : -1); } @@ -323,6 +396,8 @@ * i.e. VAR_INPUT, VAR_OUTPUT or VAR_INOUT */ function_param_iterator_c::param_direction_t function_param_iterator_c::param_direction(void) { + if (NULL == last_returned_parameter) + ERROR; return current_param_direction; } diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/function_param_iterator.hh --- a/absyntax_utils/function_param_iterator.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/function_param_iterator.hh Wed Aug 22 16:46:17 2012 +0200 @@ -105,7 +105,10 @@ */ typedef enum {iterate_op, search_op} operation_t; operation_t current_operation; - + + /* the last parameter/value returned by search() or next() */ + symbol_c *last_returned_parameter; + private: int cmp_extparam_names(const char* s1, const char* s2); void* handle_param_list(list_c *list); @@ -143,6 +146,7 @@ * of the found parameter. */ identifier_c *search(symbol_c *param_name); + identifier_c *search(const char *param_name); /* Returns the currently referenced parameter's default value, * or NULL if none is specified in the function declrataion itself. diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/function_type_decl.h --- a/absyntax_utils/function_type_decl.h Tue Aug 14 19:40:01 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,461 +0,0 @@ -/* - * Copyright (C) 2007-2011: Edouard TISSERANT and Laurent BESSARD - * - * See COPYING and COPYING.LESSER files for copyright details. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -/**** - * IEC 61131-3 standard function library - * generated code, do not edit by hand - */ - - -typedef enum { - function_real_to_sint, - function_real_to_lint, - function_real_to_dint, - function_real_to_date, - function_real_to_dword, - function_real_to_dt, - function_real_to_tod, - function_real_to_udint, - function_real_to_word, - function_real_to_string, - function_real_to_lword, - function_real_to_uint, - function_real_to_lreal, - function_real_to_byte, - function_real_to_usint, - function_real_to_ulint, - function_real_to_bool, - function_real_to_time, - function_real_to_int, - function_sint_to_real, - function_sint_to_lint, - function_sint_to_dint, - function_sint_to_date, - function_sint_to_dword, - function_sint_to_dt, - function_sint_to_tod, - function_sint_to_udint, - function_sint_to_word, - function_sint_to_string, - function_sint_to_lword, - function_sint_to_uint, - function_sint_to_lreal, - function_sint_to_byte, - function_sint_to_usint, - function_sint_to_ulint, - function_sint_to_bool, - function_sint_to_time, - function_sint_to_int, - function_lint_to_real, - function_lint_to_sint, - function_lint_to_dint, - function_lint_to_date, - function_lint_to_dword, - function_lint_to_dt, - function_lint_to_tod, - function_lint_to_udint, - function_lint_to_word, - function_lint_to_string, - function_lint_to_lword, - function_lint_to_uint, - function_lint_to_lreal, - function_lint_to_byte, - function_lint_to_usint, - function_lint_to_ulint, - function_lint_to_bool, - function_lint_to_time, - function_lint_to_int, - function_dint_to_real, - function_dint_to_sint, - function_dint_to_lint, - function_dint_to_date, - function_dint_to_dword, - function_dint_to_dt, - function_dint_to_tod, - function_dint_to_udint, - function_dint_to_word, - function_dint_to_string, - function_dint_to_lword, - function_dint_to_uint, - function_dint_to_lreal, - function_dint_to_byte, - function_dint_to_usint, - function_dint_to_ulint, - function_dint_to_bool, - function_dint_to_time, - function_dint_to_int, - function_date_to_real, - function_date_to_sint, - function_date_to_lint, - function_date_to_dint, - function_date_to_dword, - function_date_to_udint, - function_date_to_word, - function_date_to_string, - function_date_to_lword, - function_date_to_uint, - function_date_to_lreal, - function_date_to_byte, - function_date_to_usint, - function_date_to_ulint, - function_date_to_int, - function_dword_to_real, - function_dword_to_sint, - function_dword_to_lint, - function_dword_to_dint, - function_dword_to_date, - function_dword_to_dt, - function_dword_to_tod, - function_dword_to_udint, - function_dword_to_word, - function_dword_to_string, - function_dword_to_lword, - function_dword_to_uint, - function_dword_to_lreal, - function_dword_to_byte, - function_dword_to_usint, - function_dword_to_ulint, - function_dword_to_bool, - function_dword_to_time, - function_dword_to_int, - function_dt_to_real, - function_dt_to_sint, - function_dt_to_lint, - function_dt_to_dint, - function_dt_to_dword, - function_dt_to_udint, - function_dt_to_word, - function_dt_to_string, - function_dt_to_lword, - function_dt_to_uint, - function_dt_to_lreal, - function_dt_to_byte, - function_dt_to_usint, - function_dt_to_ulint, - function_dt_to_int, - function_tod_to_real, - function_tod_to_sint, - function_tod_to_lint, - function_tod_to_dint, - function_tod_to_dword, - function_tod_to_udint, - function_tod_to_word, - function_tod_to_string, - function_tod_to_lword, - function_tod_to_uint, - function_tod_to_lreal, - function_tod_to_byte, - function_tod_to_usint, - function_tod_to_ulint, - function_tod_to_int, - function_udint_to_real, - function_udint_to_sint, - function_udint_to_lint, - function_udint_to_dint, - function_udint_to_date, - function_udint_to_dword, - function_udint_to_dt, - function_udint_to_tod, - function_udint_to_word, - function_udint_to_string, - function_udint_to_lword, - function_udint_to_uint, - function_udint_to_lreal, - function_udint_to_byte, - function_udint_to_usint, - function_udint_to_ulint, - function_udint_to_bool, - function_udint_to_time, - function_udint_to_int, - function_word_to_real, - function_word_to_sint, - function_word_to_lint, - function_word_to_dint, - function_word_to_date, - function_word_to_dword, - function_word_to_dt, - function_word_to_tod, - function_word_to_udint, - function_word_to_string, - function_word_to_lword, - function_word_to_uint, - function_word_to_lreal, - function_word_to_byte, - function_word_to_usint, - function_word_to_ulint, - function_word_to_bool, - function_word_to_time, - function_word_to_int, - function_string_to_real, - function_string_to_sint, - function_string_to_lint, - function_string_to_dint, - function_string_to_date, - function_string_to_dword, - function_string_to_dt, - function_string_to_tod, - function_string_to_udint, - function_string_to_word, - function_string_to_lword, - function_string_to_uint, - function_string_to_lreal, - function_string_to_byte, - function_string_to_usint, - function_string_to_ulint, - function_string_to_bool, - function_string_to_time, - function_string_to_int, - function_lword_to_real, - function_lword_to_sint, - function_lword_to_lint, - function_lword_to_dint, - function_lword_to_date, - function_lword_to_dword, - function_lword_to_dt, - function_lword_to_tod, - function_lword_to_udint, - function_lword_to_word, - function_lword_to_string, - function_lword_to_uint, - function_lword_to_lreal, - function_lword_to_byte, - function_lword_to_usint, - function_lword_to_ulint, - function_lword_to_bool, - function_lword_to_time, - function_lword_to_int, - function_uint_to_real, - function_uint_to_sint, - function_uint_to_lint, - function_uint_to_dint, - function_uint_to_date, - function_uint_to_dword, - function_uint_to_dt, - function_uint_to_tod, - function_uint_to_udint, - function_uint_to_word, - function_uint_to_string, - function_uint_to_lword, - function_uint_to_lreal, - function_uint_to_byte, - function_uint_to_usint, - function_uint_to_ulint, - function_uint_to_bool, - function_uint_to_time, - function_uint_to_int, - function_lreal_to_real, - function_lreal_to_sint, - function_lreal_to_lint, - function_lreal_to_dint, - function_lreal_to_date, - function_lreal_to_dword, - function_lreal_to_dt, - function_lreal_to_tod, - function_lreal_to_udint, - function_lreal_to_word, - function_lreal_to_string, - function_lreal_to_lword, - function_lreal_to_uint, - function_lreal_to_byte, - function_lreal_to_usint, - function_lreal_to_ulint, - function_lreal_to_bool, - function_lreal_to_time, - function_lreal_to_int, - function_byte_to_real, - function_byte_to_sint, - function_byte_to_lint, - function_byte_to_dint, - function_byte_to_date, - function_byte_to_dword, - function_byte_to_dt, - function_byte_to_tod, - function_byte_to_udint, - function_byte_to_word, - function_byte_to_string, - function_byte_to_lword, - function_byte_to_uint, - function_byte_to_lreal, - function_byte_to_usint, - function_byte_to_ulint, - function_byte_to_bool, - function_byte_to_time, - function_byte_to_int, - function_usint_to_real, - function_usint_to_sint, - function_usint_to_lint, - function_usint_to_dint, - function_usint_to_date, - function_usint_to_dword, - function_usint_to_dt, - function_usint_to_tod, - function_usint_to_udint, - function_usint_to_word, - function_usint_to_string, - function_usint_to_lword, - function_usint_to_uint, - function_usint_to_lreal, - function_usint_to_byte, - function_usint_to_ulint, - function_usint_to_bool, - function_usint_to_time, - function_usint_to_int, - function_ulint_to_real, - function_ulint_to_sint, - function_ulint_to_lint, - function_ulint_to_dint, - function_ulint_to_date, - function_ulint_to_dword, - function_ulint_to_dt, - function_ulint_to_tod, - function_ulint_to_udint, - function_ulint_to_word, - function_ulint_to_string, - function_ulint_to_lword, - function_ulint_to_uint, - function_ulint_to_lreal, - function_ulint_to_byte, - function_ulint_to_usint, - function_ulint_to_bool, - function_ulint_to_time, - function_ulint_to_int, - function_bool_to_real, - function_bool_to_sint, - function_bool_to_lint, - function_bool_to_dint, - function_bool_to_date, - function_bool_to_dword, - function_bool_to_dt, - function_bool_to_tod, - function_bool_to_udint, - function_bool_to_word, - function_bool_to_string, - function_bool_to_lword, - function_bool_to_uint, - function_bool_to_lreal, - function_bool_to_byte, - function_bool_to_usint, - function_bool_to_ulint, - function_bool_to_time, - function_bool_to_int, - function_time_to_real, - function_time_to_sint, - function_time_to_lint, - function_time_to_dint, - function_time_to_dword, - function_time_to_udint, - function_time_to_word, - function_time_to_string, - function_time_to_lword, - function_time_to_uint, - function_time_to_lreal, - function_time_to_byte, - function_time_to_usint, - function_time_to_ulint, - function_time_to_int, - function_int_to_real, - function_int_to_sint, - function_int_to_lint, - function_int_to_dint, - function_int_to_date, - function_int_to_dword, - function_int_to_dt, - function_int_to_tod, - function_int_to_udint, - function_int_to_word, - function_int_to_string, - function_int_to_lword, - function_int_to_uint, - function_int_to_lreal, - function_int_to_byte, - function_int_to_usint, - function_int_to_ulint, - function_int_to_bool, - function_int_to_time, - function_trunc, - function_bcd_to_udint, - function_bcd_to_uint, - function_bcd_to_ulint, - function_bcd_to_usint, - function_udint_to_bcd, - function_uint_to_bcd, - function_usint_to_bcd, - function_ulint_to_bcd, - function_date_and_time_to_time_of_day, - function_date_and_time_to_date, - function_abs, - function_sqrt, - function_ln, - function_log, - function_exp, - function_sin, - function_cos, - function_tan, - function_asin, - function_acos, - function_atan, - function_add, - function_mul, - function_sub, - function_div, - function_mod, - function_expt, - function_move, - function_add_time, - function_add_tod_time, - function_add_dt_time, - function_multime, - function_sub_time, - function_sub_date_date, - function_sub_tod_time, - function_sub_tod_tod, - function_sub_dt_time, - function_divtime, - function_shl, - function_shr, - function_ror, - function_rol, - function_and, - function_or, - function_xor, - function_not, - function_sel, - function_max, - function_min, - function_limit, - function_mux, - function_gt, - function_ge, - function_eq, - function_lt, - function_le, - function_ne, - function_len, - function_left, - function_right, - function_mid, - function_concat, - function_concat_dat_tod, - function_insert, - function_delete, - function_replace, - function_find, - function_none -} function_type_t; diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/get_function_type.cc --- a/absyntax_utils/get_function_type.cc Tue Aug 14 19:40:01 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* - * matiec - a compiler for the programming languages defined in IEC 61131-3 - * - * Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) - * Copyright (C) 2007-2011 Laurent Bessard and Edouard Tisserant - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - - -/* - * An IEC 61131-3 IL and ST compiler. - * - * Based on the - * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) - * - */ - - -#include "../absyntax/absyntax.hh" -#include "function_type_decl.h" -#include - - -#include "get_function_type_decl.c" diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/get_function_type.h --- a/absyntax_utils/get_function_type.h Tue Aug 14 19:40:01 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* - * matiec - a compiler for the programming languages defined in IEC 61131-3 - * - * Copyright (C) 2008 Edouard TISSERANT - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - - -/* - * An IEC 61131-3 IL and ST compiler. - * - * Based on the - * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) - * - */ - -#include "function_type_decl.h" - -function_type_t get_function_type(identifier_c *function_name); - diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/get_sizeof_datatype.cc --- a/absyntax_utils/get_sizeof_datatype.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/get_sizeof_datatype.cc Wed Aug 22 16:46:17 2012 +0200 @@ -45,8 +45,10 @@ * NOTE: for base 10 numeric literals, any number taking up more than 64 bits * will only return a bitsize of 1024! * - * For numeric literals, we return the minimum number of bits - * required to store the value. + * NOTE: The code that does the following has been commented out, since we no longer need it! + * It has been superceded by the constant_folding.cc class. + * // For numeric literals, we return the minimum number of bits + * // required to store the value. * * E.g. TYPE new_int_t : INT; END_TYPE; * TYPE new_int2_t : INT = 2; END_TYPE; @@ -57,19 +59,21 @@ * sizeof(DINT) -> 32 * sizeof(LINT) -> 64 * - * sizeof('1') -> 1 - * sizeof('015') -> 4 # Leading zeros are ignored! - * sizeof('0') -> 1 # This is a special case! Even the value 0 needs at least 1 bit to store! - * sizeof('16') -> 5 - * sizeof('2#00101') -> 3 - * sizeof('8#334') -> 9 - * sizeof('16#2A') -> 8 - * - * sizeof('7.4') -> 32 # all real literals return 32 bits, the size of a 'REAL' - * # TODO: study IEC 60559 for the range of values that may be - * # stored in a REAL (basic single width floating point format) - * # and in a LREAL (basic double width floating point format) - * # and see if some real literals need to return 64 instead! + * NOTE: The code that does the following has been commented out, since we no longer need it! + * It has been superceded by the constant_folding.cc class. + * // sizeof('1') -> 1 + * // sizeof('015') -> 4 # Leading zeros are ignored! + * // sizeof('0') -> 1 # This is a special case! Even the value 0 needs at least 1 bit to store! + * // sizeof('16') -> 5 + * // sizeof('2#00101') -> 3 + * // sizeof('8#334') -> 9 + * // sizeof('16#2A') -> 8 + * + * // sizeof('7.4') -> 32 # all real literals return 32 bits, the size of a 'REAL' + * // # TODO: study IEC 60559 for the range of values that may be + * // # stored in a REAL (basic single width floating point format) + * // # and in a LREAL (basic double width floating point format) + * // # and see if some real literals need to return 64 instead! */ #include "get_sizeof_datatype.hh" @@ -77,14 +81,10 @@ #include #include #include // get definition of ULLONG_MAX -/* tell stdint.h we want the definition of UINT64_MAX */ -#define __STDC_LIMIT_MACROS -#include // get definition of uint64_t and UINT64_MAX - - -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); +#include + +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros, and uint64_t and UINT64_MAX + /* This class is a singleton. @@ -97,8 +97,7 @@ #define _decode_int(ptr) (((char *)ptr) - ((char *)NULL)) - - +#if 0 /* We no longer need the code for handling numeric literals. But keep it around for a little while longer... */ /* divide a base 10 literal in a string by 2 */ /* returns remainder of division (0 or 1) */ static int strdivby2(char **strptr) { @@ -121,7 +120,7 @@ return carry; } - +#endif /* Constructor for the singleton class */ int get_sizeof_datatype_c::getsize(symbol_c *data_type_symbol) { @@ -139,7 +138,7 @@ singleton = NULL; } - +#if 0 /* We no longer need the code for handling numeric literals. But keep it around for a little while longer... */ /*********************/ /* B 1.2 - Constants */ /*********************/ @@ -158,8 +157,60 @@ /* NOTE: all integer_c and real_c tokens will always be positive (i.e. no leading '-') * due to the way the source code is parsed by iec.flex. */ + +/* + * IEC6113-3 and C++ use IEC 60559 to rappresent floating point data types + * REAL => float => single precision 32 bit + * LREAL => double => double precision 64 bit + * ????? => long double => quadruple precision 128 bit + */ void *get_sizeof_datatype_c::visit(real_c *symbol) { - return _encode_int(32); + char *endp; + long double ld_test; + double d_test; + float f_test; + + /* copy the original string, but leave out any underscores... */ + char *sval, *oval; + const char *pval; + oval = sval = (char *)malloc(strlen(symbol->value)+1); + if (NULL == sval) ERROR; + + for (pval = symbol->value, sval = oval; *pval != '\0'; pval++) { + if ('_' != *pval) {*sval = *pval; sval++;} + } + *sval = '\0'; + + sval = oval; + if ('\0' == *sval) ERROR; + + /* now do the conversion using the new string... */ + f_test = strtof(sval, &endp); + if (*endp != '\0') ERROR; + if (ERANGE != errno) { + /* No overflow/underflow! => It fits in a float! */ + free(oval); + return _encode_int(32); + } + + d_test = strtod(sval, &endp); + if (*endp != '\0') ERROR; + if (ERANGE != errno) { + /* No overflow/underflow! => It fits in a double! */ + free(oval); + return _encode_int(64); + } + + ld_test = strtold(sval, &endp); + if (*endp != '\0') ERROR; + if (ERANGE != errno) { + /* No overflow/underflow! => It fits in a long double! */ + free(oval); + return _encode_int(128); + } + + free(oval); + return _encode_int(65535); /* a very large number!!! */ } void *get_sizeof_datatype_c::visit(neg_real_c *symbol) { @@ -282,7 +333,7 @@ if (('0' != *sval) && ('1' != *sval) && ('_' != *sval)) ERROR; - if ('_' != *sval) bitsize ++; /* 1 bits per binary digit */ + if ('_' != *sval) bitsize++; /* 1 bits per binary digit */ } /* special case... if (value == 0) <=> (bitsize == 0), return bit size of 1 ! */ @@ -352,7 +403,7 @@ /* Assumes ASCII */ if (!(('0' <= *sval) && ('9' >= *sval)) && !(('A' <= *sval) && ('F' >= *sval)) && - !(('a' <= *sval) && ('b' >= *sval)) && + !(('a' <= *sval) && ('f' >= *sval)) && ! ('_' == *sval)) ERROR; @@ -364,7 +415,7 @@ return _encode_int(bitsize); } - +#endif /***********************************/ /* B 1.3.1 - Elementary Data Types */ diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/get_sizeof_datatype.hh --- a/absyntax_utils/get_sizeof_datatype.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/get_sizeof_datatype.hh Wed Aug 22 16:46:17 2012 +0200 @@ -84,6 +84,7 @@ static get_sizeof_datatype_c *singleton; private: +#if 0 /* We no longer need the code for handling numeric literals. But keep it around for a little while longer... */ /*********************/ /* B 1.2 - Constants */ /*********************/ @@ -105,7 +106,7 @@ void *visit(binary_integer_c *symbol); void *visit(octal_integer_c *symbol); void *visit(hex_integer_c *symbol); - +#endif /***********************************/ /* B 1.3.1 - Elementary Data Types */ /***********************************/ diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/get_var_name.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/absyntax_utils/get_var_name.cc Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,120 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2012 Mario de Sousa (msousa@fe.up.pt) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* + * A small helper visitor class, that will + * return the name (tokn_c *) of a variable, as it will + * appear in the variable declaration. + */ + +/* For ex.: + * VAR + * A : int; + * B : ARRAY [1..9] of int; + * C : some_struct_t; + * END_VAR + * + * A := 56; + * B[8] := 99; + * C.e := 77; + * + * Calling this visitor class with symbolic_variable_c instance referencing 'A' in + * the line 'A := 56', will return the string "A". + * + * Calling this visitor class with array_variable_c instance referencing 'B[8]' in + * the line 'B[8] := 99', will return the string "B". + * + * Calling this visitor class with array_variable_c instance referencing 'C.e' in + * the line 'C.e := 77', will return the string "C". + */ + + +#include "absyntax_utils.hh" + + + + + + +get_var_name_c *get_var_name_c::singleton_instance_ = NULL; + + + + +token_c *get_var_name_c::get_name(symbol_c *symbol) { + if (NULL == singleton_instance_) singleton_instance_ = new get_var_name_c(); + if (NULL == singleton_instance_) ERROR; + + return (token_c *)(symbol->accept(*singleton_instance_)); +} + + +/*************************/ +/* B.1 - Common elements */ +/*************************/ +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ +// SYM_TOKEN(identifier_c) +void *get_var_name_c::visit(identifier_c *symbol) {return (void *)symbol;} + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ +// SYM_REF2(symbolic_variable_c, var_name, unused) +void *get_var_name_c::visit(symbolic_variable_c *symbol) {return symbol->var_name->accept(*this);} + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ +// SYM_TOKEN(direct_variable_c) + +/*************************************/ +/* B.1.4.2 Multi-element Variables */ +/*************************************/ +/* subscripted_variable '[' subscript_list ']' */ +// SYM_REF2(array_variable_c, subscripted_variable, subscript_list) +void *get_var_name_c::visit(array_variable_c *symbol) {return symbol->subscripted_variable->accept(*this);} + +/* subscript_list ',' subscript */ +// SYM_LIST(subscript_list_c) + +/* record_variable '.' field_selector */ +/* WARNING: input and/or output variables of function blocks + * may be accessed as fields of a tructured variable! + * Code handling a structured_variable_c must take + * this into account! + */ +// SYM_REF2(structured_variable_c, record_variable, field_selector) +void *get_var_name_c::visit(structured_variable_c *symbol) {return symbol->record_variable->accept(*this);} + + + diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/get_var_name.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/absyntax_utils/get_var_name.hh Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,115 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2012 Mario de Sousa (msousa@fe.up.pt) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* + * A small helper visitor class, that will + * return the name (tokn_c *) of a variable, as it will + * appear in the variable declaration. + */ + +/* For ex.: + * VAR + * A : int; + * B : ARRAY [1..9] of int; + * C : some_struct_t; + * END_VAR + * + * A := 56; + * B[8] := 99; + * C.e := 77; + * + * Calling this visitor class with symbolic_variable_c instance referencing 'A' in + * the line 'A := 56', will return the string "A". + * + * Calling this visitor class with array_variable_c instance referencing 'B[8]' in + * the line 'B[8] := 99', will return the string "B". + * + * Calling this visitor class with array_variable_c instance referencing 'C.e' in + * the line 'C.e := 77', will return the string "C". + */ + + + + +class get_var_name_c : public search_visitor_c { + public: + get_var_name_c(void) {}; + ~get_var_name_c(void) {}; + static token_c *get_name(symbol_c *symbol); + + private: + static get_var_name_c *singleton_instance_; + + private: + /*************************/ + /* B.1 - Common elements */ + /*************************/ + /*******************************************/ + /* B 1.1 - Letters, digits and identifiers */ + /*******************************************/ + // SYM_TOKEN(identifier_c) + void *visit(identifier_c *symbol); + + /*********************/ + /* B 1.4 - Variables */ + /*********************/ + // SYM_REF2(symbolic_variable_c, var_name, unused) + void *visit(symbolic_variable_c *symbol); + + /********************************************/ + /* B.1.4.1 Directly Represented Variables */ + /********************************************/ + // SYM_TOKEN(direct_variable_c) + // void *visit(direct_variable_c *symbol); + + /*************************************/ + /* B.1.4.2 Multi-element Variables */ + /*************************************/ + /* subscripted_variable '[' subscript_list ']' */ + // SYM_REF2(array_variable_c, subscripted_variable, subscript_list) + void *visit(array_variable_c *symbol); + + /* subscript_list ',' subscript */ + // SYM_LIST(subscript_list_c) + // void *visit(subscript_list_c *symbol); + + /* record_variable '.' field_selector */ + /* WARNING: input and/or output variables of function blocks + * may be accessed as fields of a tructured variable! + * Code handling a structured_variable_c must take + * this into account! + */ + // SYM_REF2(structured_variable_c, record_variable, field_selector) + void *visit(structured_variable_c *symbol); +}; + + + diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/search_base_type.cc --- a/absyntax_utils/search_base_type.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/search_base_type.cc Wed Aug 22 16:46:17 2012 +0200 @@ -1,7 +1,7 @@ /* * matiec - a compiler for the programming languages defined in IEC 61131-3 * - * Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2003-2012 Mario de Sousa (msousa@fe.up.pt) * Copyright (C) 2007-2011 Laurent Bessard and Edouard Tisserant * * This program is free software: you can redistribute it and/or modify @@ -46,25 +46,35 @@ * we may have FB instances declared of a specific FB type. */ #include "absyntax_utils.hh" - -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. search_base_type_c::search_base_type_c(void) {current_type_name = NULL;} -void *search_base_type_c::visit(identifier_c *type_name) { - this->current_type_name = type_name; - /* look up the type declaration... */ - symbol_c *type_decl = type_symtable.find_value(type_name); - if (type_decl == type_symtable.end_value()) - /* Type declaration not found!! */ - ERROR; - - return type_decl->accept(*this); -} + + + +symbol_c *search_base_type_c::get_basetype_decl(symbol_c *symbol) { + if (NULL == symbol) + return NULL; + + return (symbol_c *)symbol->accept(*this); +} + +symbol_c *search_base_type_c::get_basetype_id (symbol_c *symbol) { + if (NULL == symbol) + return NULL; + + current_type_name = NULL; /* just to be on the safe side... */ + symbol->accept(*this); + return (symbol_c *)current_type_name; +} + + +/* Note by MJS: The following two functions definately do not belong in this class!! Maybe create a new utility class? + * I will need to clean this up when the opportunity arises! + */ bool search_base_type_c::type_is_subrange(symbol_c* type_decl) { this->is_subrange = false; @@ -78,6 +88,35 @@ return this->is_enumerated; } + +/*************************/ +/* B.1 - Common elements */ +/*************************/ + +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ +void *search_base_type_c::visit(identifier_c *type_name) { + symbol_c *type_decl; + + this->current_type_name = type_name; + + /* look up the type declaration... */ + type_decl = type_symtable.find_value(type_name); + if (type_decl != type_symtable.end_value()) + return type_decl->accept(*this); + + type_decl = function_block_type_symtable.find_value(type_name); + if (type_decl != function_block_type_symtable.end_value()) + return type_decl->accept(*this); + + /* Type declaration not found!! */ + ERROR; + + return NULL; +} + + /*********************/ /* B 1.2 - Constants */ /*********************/ @@ -92,41 +131,41 @@ * with all inputs of the same data type. * If 'x' were a SINT, then the '30' would have to be a SINT too! */ -void *search_base_type_c::visit(real_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(neg_real_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(integer_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(neg_integer_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(binary_integer_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(octal_integer_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(hex_integer_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(boolean_true_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(boolean_false_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(real_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(neg_real_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(integer_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(neg_integer_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(binary_integer_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(octal_integer_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(hex_integer_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(boolean_true_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(boolean_false_c *symbol) {return (void *)symbol;} /***********************************/ /* B 1.3.1 - Elementary Data Types */ /***********************************/ -void *search_base_type_c::visit(time_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(bool_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(sint_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(int_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(dint_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(lint_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(usint_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(uint_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(udint_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(ulint_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(real_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(lreal_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(date_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(tod_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(dt_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(byte_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(word_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(dword_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(lword_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(string_type_name_c *symbol) {return (void *)symbol;} -void *search_base_type_c::visit(wstring_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(time_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(bool_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(sint_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(int_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(dint_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(lint_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(usint_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(uint_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(udint_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(ulint_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(real_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(lreal_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(date_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(tod_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(dt_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(byte_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(word_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(dword_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(lword_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(string_type_name_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(wstring_type_name_c *symbol) {return (void *)symbol;} /******************************************************/ @@ -188,7 +227,7 @@ } /* signed_integer DOTDOT signed_integer */ -void *search_base_type_c::visit(subrange_c *symbol) {ERROR; return NULL;} /* should never get called... */ +void *search_base_type_c::visit(subrange_c *symbol) {ERROR; return NULL;} /* should never get called... */ /* enumerated_type_name ':' enumerated_spec_init */ void *search_base_type_c::visit(enumerated_type_declaration_c *symbol) { @@ -204,14 +243,11 @@ /* helper symbol for enumerated_specification->enumerated_spec_init */ /* enumerated_value_list ',' enumerated_value */ -void *search_base_type_c::visit(enumerated_value_list_c *symbol) { - if (NULL == this->current_type_name) ERROR; - return (void *)this->current_type_name; -} +void *search_base_type_c::visit(enumerated_value_list_c *symbol) {return (void *)symbol;} /* enumerated_type_name '#' identifier */ // SYM_REF2(enumerated_value_c, type, value) -void *search_base_type_c::visit(enumerated_value_c *symbol) {ERROR; return NULL;} /* should never get called... */ +void *search_base_type_c::visit(enumerated_value_c *symbol) {ERROR; return NULL;} /* should never get called... */ /* identifier ':' array_spec_init */ void *search_base_type_c::visit(array_type_declaration_c *symbol) { @@ -222,28 +258,28 @@ /* array_specification [ASSIGN array_initialization} */ /* array_initialization may be NULL ! */ void *search_base_type_c::visit(array_spec_init_c *symbol) { + /* Note that the 'array_specification' may be either an identifier of a previsously defined array type, + * or an array_specification_c, so we can not stop here and simply return a array_spec_init_c, + * especially if we are looking for the base class! + */ return symbol->array_specification->accept(*this); } /* ARRAY '[' array_subrange_list ']' OF non_generic_type_name */ -void *search_base_type_c::visit(array_specification_c *symbol) { - if (NULL == this->current_type_name) - this->current_type_name = symbol->non_generic_type_name; - return symbol->non_generic_type_name->accept(*this); -} +void *search_base_type_c::visit(array_specification_c *symbol) {return (void *)symbol;} /* helper symbol for array_specification */ /* array_subrange_list ',' subrange */ -void *search_base_type_c::visit(array_subrange_list_c *symbol) {ERROR; return NULL;} /* should never get called... */ +void *search_base_type_c::visit(array_subrange_list_c *symbol) {ERROR; return NULL;} /* should never get called... */ /* array_initialization: '[' array_initial_elements_list ']' */ /* helper symbol for array_initialization */ /* array_initial_elements_list ',' array_initial_elements */ -void *search_base_type_c::visit(array_initial_elements_list_c *symbol) {ERROR; return NULL;} /* should never get called... */ +void *search_base_type_c::visit(array_initial_elements_list_c *symbol) {ERROR; return NULL;} /* should never get called... */ /* integer '(' [array_initial_element] ')' */ /* array_initial_element may be NULL ! */ -void *search_base_type_c::visit(array_initial_elements_c *symbol) {ERROR; return NULL;} /* should never get called... */ +void *search_base_type_c::visit(array_initial_elements_c *symbol) {ERROR; return NULL;} /* should never get called... */ /* structure_type_name ':' structure_specification */ /* NOTE: structure_specification will point to either a @@ -258,7 +294,7 @@ /* var1_list ':' structure_type_name */ void *search_base_type_c::visit(structured_var_declaration_c *symbol) { - return symbol->structure_type_name->accept(*this); + return symbol; } /* structure_type_name ASSIGN structure_initialization */ @@ -270,20 +306,18 @@ /* helper symbol for structure_declaration */ /* structure_declaration: STRUCT structure_element_declaration_list END_STRUCT */ /* structure_element_declaration_list structure_element_declaration ';' */ -void *search_base_type_c::visit(structure_element_declaration_list_c *symbol) { - return (void *)symbol; -} +void *search_base_type_c::visit(structure_element_declaration_list_c *symbol) {return (void *)symbol;} /* structure_element_name ':' *_spec_init */ -void *search_base_type_c::visit(structure_element_declaration_c *symbol) {ERROR; return NULL;} /* should never get called... */ +void *search_base_type_c::visit(structure_element_declaration_c *symbol) {ERROR; return NULL;} /* should never get called... */ /* helper symbol for structure_initialization */ /* structure_initialization: '(' structure_element_initialization_list ')' */ /* structure_element_initialization_list ',' structure_element_initialization */ -void *search_base_type_c::visit(structure_element_initialization_list_c *symbol) {ERROR; return NULL;} /* should never get called... */ +void *search_base_type_c::visit(structure_element_initialization_list_c *symbol) {ERROR; return NULL;} /* should never get called... */ /* structure_element_name ASSIGN value */ -void *search_base_type_c::visit(structure_element_initialization_c *symbol) {ERROR; return NULL;} /* should never get called... */ +void *search_base_type_c::visit(structure_element_initialization_c *symbol) {ERROR; return NULL;} /* should never get called... */ /* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ /* @@ -292,7 +326,7 @@ string_type_declaration_size, string_type_declaration_init) // may be == NULL! */ -void *search_base_type_c::visit(string_type_declaration_c *symbol) {return symbol;} +void *search_base_type_c::visit(string_type_declaration_c *symbol) {return (void *)symbol;} @@ -301,7 +335,24 @@ /*****************************/ /* FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */ // SYM_REF3(function_block_declaration_c, fblock_name, var_declarations, fblock_body) -void *search_base_type_c::visit(function_block_declaration_c *symbol) { - return symbol; -} - +void *search_base_type_c::visit(function_block_declaration_c *symbol) {return (void *)symbol;} + + + +/*********************************************/ +/* B.1.6 Sequential function chart elements */ +/*********************************************/ +/* INITIAL_STEP step_name ':' action_association_list END_STEP */ +// SYM_REF2(initial_step_c, step_name, action_association_list) +void *search_base_type_c::visit(initial_step_c *symbol) { + this->current_type_name = NULL; /* this pseudo data type does not have a type name! */ + return (void *)symbol; +} + +/* STEP step_name ':' action_association_list END_STEP */ +// SYM_REF2(step_c, step_name, action_association_list) +void *search_base_type_c::visit(step_c *symbol) { + this->current_type_name = NULL; /* this pseudo data type does not have a type name! */ + return (void *)symbol; +} + diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/search_base_type.hh --- a/absyntax_utils/search_base_type.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/search_base_type.hh Wed Aug 22 16:46:17 2012 +0200 @@ -1,7 +1,7 @@ /* * matiec - a compiler for the programming languages defined in IEC 61131-3 * - * Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2003-2012 Mario de Sousa (msousa@fe.up.pt) * Copyright (C) 2007-2011 Laurent Bessard and Edouard Tisserant * * This program is free software: you can redistribute it and/or modify @@ -60,18 +60,27 @@ search_base_type_c(void); public: - void *visit(identifier_c *type_name); + symbol_c *get_basetype_decl(symbol_c *symbol); + symbol_c *get_basetype_id (symbol_c *symbol); bool type_is_subrange(symbol_c* type_decl); bool type_is_enumerated(symbol_c* type_decl); public: - /*********************/ - /* B 1.2 - Constants */ - /*********************/ - - /******************************/ - /* B 1.2.1 - Numeric Literals */ - /******************************/ + /*************************/ + /* B.1 - Common elements */ + /*************************/ + /*******************************************/ + /* B 1.1 - Letters, digits and identifiers */ + /*******************************************/ + void *visit(identifier_c *type_name); + + + /*********************/ + /* B 1.2 - Constants */ + /*********************/ + /******************************/ + /* B 1.2.1 - Numeric Literals */ + /******************************/ /* Numeric literals without any explicit type cast have unknown data type, * so we continue considering them as their own basic data types until * they can be resolved (for example, when using '30+x' where 'x' is a LINT variable, the @@ -227,6 +236,18 @@ // SYM_REF3(function_block_declaration_c, fblock_name, var_declarations, fblock_body) void *visit(function_block_declaration_c *symbol); + /*********************************************/ + /* B.1.6 Sequential function chart elements */ + /*********************************************/ + /* INITIAL_STEP step_name ':' action_association_list END_STEP */ + // SYM_REF2(initial_step_c, step_name, action_association_list) + void *visit(initial_step_c *symbol); + + /* STEP step_name ':' action_association_list END_STEP */ + // SYM_REF2(step_c, step_name, action_association_list) + void *visit(step_c *symbol); + + }; // search_base_type_c diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/search_constant_type.cc --- a/absyntax_utils/search_constant_type.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/search_constant_type.cc Wed Aug 22 16:46:17 2012 +0200 @@ -44,11 +44,9 @@ #include "../util/symtable.hh" #include "search_constant_type.hh" #include "absyntax_utils.hh" +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); symbol_c *search_constant_type_c::get_type(symbol_c *constant) { return (symbol_c *)constant->accept(*this); @@ -105,11 +103,7 @@ void *search_constant_type_c::visit(neg_time_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ void *search_constant_type_c::visit(duration_c *symbol) {return (void *)(symbol->type_name);} void *search_constant_type_c::visit(fixed_point_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ -void *search_constant_type_c::visit(days_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ -void *search_constant_type_c::visit(hours_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ -void *search_constant_type_c::visit(minutes_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ -void *search_constant_type_c::visit(seconds_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ -void *search_constant_type_c::visit(milliseconds_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ +void *search_constant_type_c::visit(interval_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ /************************************/ /* B 1.2.3.2 - Time of day and Date */ @@ -133,6 +127,12 @@ return (void *)value_type; } + + + +invalid_type_name_c search_constant_type_c::invalid_type_name; + + real_type_name_c search_constant_type_c::real_type_name; sint_type_name_c search_constant_type_c::sint_type_name; lint_type_name_c search_constant_type_c::lint_type_name; @@ -155,17 +155,26 @@ time_type_name_c search_constant_type_c::time_type_name; int_type_name_c search_constant_type_c::int_type_name; -// safebool_type_name_c search_constant_type_c::safebool_type_name; - /* The following is required because the expression (TOD_var - TOD_var) will result in a data type - * (in this case, TIME) that is neither of the expression elements... - */ safetime_type_name_c search_constant_type_c::safetime_type_name; safetod_type_name_c search_constant_type_c::safetod_type_name; safedt_type_name_c search_constant_type_c::safedt_type_name; +safedate_type_name_c search_constant_type_c::safedate_type_name; +safereal_type_name_c search_constant_type_c::safereal_type_name; +safesint_type_name_c search_constant_type_c::safesint_type_name; +safelint_type_name_c search_constant_type_c::safelint_type_name; +safedint_type_name_c search_constant_type_c::safedint_type_name; +safedword_type_name_c search_constant_type_c::safedword_type_name; +safeudint_type_name_c search_constant_type_c::safeudint_type_name; +safeword_type_name_c search_constant_type_c::safeword_type_name; +safewstring_type_name_c search_constant_type_c::safewstring_type_name; +safestring_type_name_c search_constant_type_c::safestring_type_name; +safelword_type_name_c search_constant_type_c::safelword_type_name; +safeuint_type_name_c search_constant_type_c::safeuint_type_name; +safelreal_type_name_c search_constant_type_c::safelreal_type_name; +safebyte_type_name_c search_constant_type_c::safebyte_type_name; +safeusint_type_name_c search_constant_type_c::safeusint_type_name; +safeulint_type_name_c search_constant_type_c::safeulint_type_name; +safebool_type_name_c search_constant_type_c::safebool_type_name; +safeint_type_name_c search_constant_type_c::safeint_type_name; - -/* temporarily here until we remove the st_code_gen.c and il_code_gen.c files... */ -/* It should then move to search_expression_type_c */ -integer_c search_constant_type_c::integer("1"); -real_c search_constant_type_c::real("1.0"); diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/search_constant_type.hh --- a/absyntax_utils/search_constant_type.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/search_constant_type.hh Wed Aug 22 16:46:17 2012 +0200 @@ -30,6 +30,14 @@ * */ +/* NOTE: The use of this visitor class is now deprecated. + * The new version of stage3 data type checking adds an entry to + * every relevant object in the abstract syntax tree defining + * the data type of that object. Please use that instead! + */ + + + /* Determine the data type of a specific constant or variable. * A reference to the relevant type definition is returned. * @@ -49,6 +57,10 @@ class search_constant_type_c: public search_visitor_c { public: + /* object used to identify an entry in the abstract syntax tree with an invalid data type */ + /* This is only used from stage3 onwards. Stages 1 and 2 will never create any instances of invalid_type_name_c */ + static invalid_type_name_c invalid_type_name; + /**********************/ /* B.1.3 - Data types */ /**********************/ @@ -77,9 +89,6 @@ static time_type_name_c time_type_name; static int_type_name_c int_type_name; -/* temporarily here until we remove the st_code_gen.c and il_code_gen.c files... */ - static integer_c integer; - static real_c real; /******************************************************/ /* Extensions to the base standard as defined in */ @@ -96,8 +105,24 @@ static safetime_type_name_c safetime_type_name; static safetod_type_name_c safetod_type_name; static safedt_type_name_c safedt_type_name; - - + static safedate_type_name_c safedate_type_name; + static safereal_type_name_c safereal_type_name; + static safesint_type_name_c safesint_type_name; + static safelint_type_name_c safelint_type_name; + static safedint_type_name_c safedint_type_name; + static safedword_type_name_c safedword_type_name; + static safeudint_type_name_c safeudint_type_name; + static safeword_type_name_c safeword_type_name; + static safewstring_type_name_c safewstring_type_name; + static safestring_type_name_c safestring_type_name; + static safelword_type_name_c safelword_type_name; + static safeuint_type_name_c safeuint_type_name; + static safelreal_type_name_c safelreal_type_name; + static safebyte_type_name_c safebyte_type_name; + static safeusint_type_name_c safeusint_type_name; + static safeulint_type_name_c safeulint_type_name; + static safebool_type_name_c safebool_type_name; + static safeint_type_name_c safeint_type_name; public: symbol_c *get_type(symbol_c *constant); @@ -141,12 +166,8 @@ /************************/ void *visit(neg_time_c *symbol); void *visit(duration_c *symbol); + void *visit(interval_c *symbol); void *visit(fixed_point_c *symbol); - void *visit(days_c *symbol); - void *visit(hours_c *symbol); - void *visit(minutes_c *symbol); - void *visit(seconds_c *symbol); - void *visit(milliseconds_c *symbol); /************************************/ /* B 1.2.3.2 - Time of day and Date */ @@ -164,5 +185,4 @@ }; // search_constant_type_c - #endif /* ifndef _SEARCH_CONSTANT_TYPE_HH */ diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/search_il_label.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/absyntax_utils/search_il_label.cc Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,97 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2012 Mario de Sousa (msousa@fe.up.pt) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Search for a specific label in an IL list. + * + * when instantiated, must be given a pointer to one of the following + * - function_declaration_c + * - function_block_declaration_c + * - program_declaration_c + * - instruction_list_c + * + * which is where all calls to search for a specific label will look for said label. + */ + + + +#include "absyntax_utils.hh" + + + +/* set to 1 to see debug info during execution */ +static int debug = 0; + +search_il_label_c::search_il_label_c(symbol_c *search_scope) { + this->search_scope = search_scope; + this->search_label = NULL; +} + +search_il_label_c::~search_il_label_c(void) { +} + + +il_instruction_c *search_il_label_c::find_label(const char *label) { + return find_label(new identifier_c(label)); +} + + +il_instruction_c *search_il_label_c::find_label(symbol_c *label) { + search_label = label; + il_instruction_c *res = (il_instruction_c *)search_scope->accept(*this); + search_label = NULL; + return res; +} + + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ + +/* | label ':' [il_incomplete_instruction] eol_list */ +// SYM_REF2(il_instruction_c, label, il_instruction) +// void *visit(instruction_list_c *symbol); +void *search_il_label_c::visit(il_instruction_c *symbol) { +// printf("search_il_label_c::visit(il_instruction_c *symbol): searching for %s\n", ((identifier_c *)search_label)->value); + if (NULL != symbol->label) + if (compare_identifiers(search_label, symbol->label) == 0) + return symbol; + + return NULL; +} + + + + diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/search_il_label.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/absyntax_utils/search_il_label.hh Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,95 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2012 Mario de Sousa (msousa@fe.up.pt) + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Search for a specific label in an IL list. + * + * when instantiated, must be given a pointer to one of the following + * - function_declaration_c + * - function_block_declaration_c + * - program_declaration_c + * - instruction_list_c + * + * which is where all calls to search for a specific label will look for said label. + */ + + + +#include "../absyntax_utils/absyntax_utils.hh" + + +class search_il_label_c: public search_visitor_c { + + private: + search_varfb_instance_type_c *search_varfb_instance_type; + symbol_c *search_scope; + symbol_c *search_label; + + public: + search_il_label_c(symbol_c *search_scope); + virtual ~search_il_label_c(void); + + il_instruction_c *find_label(const char *label); + il_instruction_c *find_label(symbol_c *label); + + + /****************************************/ + /* B.2 - Language IL (Instruction List) */ + /****************************************/ + /***********************************/ + /* B 2.1 Instructions and Operands */ + /***********************************/ +// void *visit(instruction_list_c *symbol); + void *visit(il_instruction_c *symbol); +// void *visit(il_simple_operation_c *symbol); +// void *visit(il_function_call_c *symbol); +// void *visit(il_expression_c *symbol); +// void *visit(il_fb_call_c *symbol); +// void *visit(il_formal_funct_call_c *symbol); +// void *visit(il_operand_list_c *symbol); +// void *visit(simple_instr_list_c *symbol); +// void *visit(il_simple_instruction_c*symbol); +// void *visit(il_param_list_c *symbol); +// void *visit(il_param_assignment_c *symbol); +// void *visit(il_param_out_assignment_c *symbol); + + +}; // search_il_label_c + + + + + + + + diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/search_var_instance_decl.cc --- a/absyntax_utils/search_var_instance_decl.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/search_var_instance_decl.cc Wed Aug 22 16:46:17 2012 +0200 @@ -30,27 +30,51 @@ * */ -/* Determine the data type of a specific variable instance, including - * function block instances. - * A reference to the relevant variable declaration is returned. - * The variable instance may NOT be a member of a structure of a member +/* Search in a VAR* END_VAR declaration for the delcration of the specified variable instance. + * Will return: + * - the declaration itself (get_decl() ) + * - the type of declaration in which the variable was declared (get_vartype() ) + * + * The variable instance may NOT be a member of a structure of a memeber * of a structure of an element of an array of ... * - * example: + * For example, considering the following 'variables': * window.points[1].coordinate.x * window.points[1].colour - * etc... ARE NOT ALLOWED! + * offset[99] + * + * passing a reference to 'points', 'points[1]', 'points[1].colour', 'colour' + * ARE NOT ALLOWED! * * This class must only be passed the name of the variable that will appear * in the variable declaration. In the above examples, this would be - * 'window' !! - * - * - * If you need to pass a complete name of a variable instance (such as - * 'window.points[1].coordinate.x') use the search_varfb_instance_type_c instead! + * 'window.points[1].coordinate.x' + * 'window.points[1].coordinate' + * 'window.points[1]' + * 'window' + * 'window.points[1].colour' + * 'offset' + * 'offset[99]' + * + * */ - -/* Note that current_type_decl that this class returns may reference the + +/* Note: + * Determining the declaration type of a specific variable instance (including + * function block instances) really means determining whether the variable was declared in a + * VAR_INPUT + * VAR_OUTPUT + * VAR_IN_OUT + * VAR + * VAR_TEMP + * VAR_EXTERNAL + * VAR_GLOBAL + * VAR AT -> Located variable! + * + */ + +/* Note: + * The current_type_decl that this class returns may reference the * name of a type, or the type declaration itself! * For an example of the first, consider a variable declared as ... * x : AAA; @@ -61,25 +85,71 @@ * If it is the first, we will return a reference to the name, if the second * we return a reference to the declaration!! */ + + #include "absyntax_utils.hh" + + + search_var_instance_decl_c::search_var_instance_decl_c(symbol_c *search_scope) { this->current_vartype = none_vt; this->search_scope = search_scope; this->search_name = NULL; this->current_type_decl = NULL; -} - -symbol_c *search_var_instance_decl_c::get_decl(symbol_c *variable_instance_name) { + this->current_option = none_opt; +} + +symbol_c *search_var_instance_decl_c::get_decl(symbol_c *variable) { this->current_vartype = none_vt; - this->search_name = variable_instance_name; + this->current_option = none_opt; + this->search_name = get_var_name_c::get_name(variable); return (symbol_c *)search_scope->accept(*this); } -unsigned int search_var_instance_decl_c::get_vartype(void) { - return current_vartype; -} +search_var_instance_decl_c::vt_t search_var_instance_decl_c::get_vartype(symbol_c *variable) { + this->current_vartype = none_vt; + this->current_option = none_opt; + this->search_name = get_var_name_c::get_name(variable); + search_scope->accept(*this); + return this->current_vartype; +} + +search_var_instance_decl_c::opt_t search_var_instance_decl_c::get_option(symbol_c *variable) { + this->current_vartype = none_vt; + this->current_option = none_opt; + this->search_name = get_var_name_c::get_name(variable); + search_scope->accept(*this); + return this->current_option; +} + + + +/* This is a temporary fix. Hopefully, once I clean up stage4 code, and I change the way + * we generate C code, this function will no longer be needed! + */ +#include /* required for typeid() */ +bool search_var_instance_decl_c::type_is_complex(symbol_c *symbol) { + symbol_c *decl; + search_base_type_c search_base_type; + + decl = this->get_decl(symbol); + if (NULL == decl) ERROR; + decl = search_base_type.get_basetype_decl(decl); + if (NULL == decl) ERROR; + + return ((typeid( *(decl) ) == typeid( array_specification_c )) || +// (typeid( *(decl) ) == typeid( array_spec_init_c )) || /* does not seem to be necessary */ + (typeid( *(decl) ) == typeid( structure_type_declaration_c )) || + (typeid( *(decl) ) == typeid( structure_element_declaration_list_c )) || +// (typeid( *(decl) ) == typeid( structure_type_declaration_c )) || /* does not seem to be necessary */ + (typeid( *(decl) ) == typeid( initialized_structure_c )) + + ); +} + + /***************************/ /* B 0 - Programming Model */ @@ -100,12 +170,30 @@ /* edge -> The F_EDGE or R_EDGE directive */ // SYM_REF2(edge_declaration_c, edge, var1_list) // TODO +void *search_var_instance_decl_c::visit(constant_option_c *symbol) { + current_option = constant_opt; + return NULL; +} + +void *search_var_instance_decl_c::visit(retain_option_c *symbol) { + current_option = retain_opt; + return NULL; +} + +void *search_var_instance_decl_c::visit(non_retain_option_c *symbol) { + current_option = non_retain_opt; + return NULL; +} void *search_var_instance_decl_c::visit(input_declarations_c *symbol) { current_vartype = input_vt; + current_option = none_opt; /* not really required. Just to make the code more readable */ + if (NULL != symbol->option) + symbol->option->accept(*this); void *res = symbol->input_declaration_list->accept(*this); if (res == NULL) { current_vartype = none_vt; + current_option = none_opt; } return res; } @@ -114,9 +202,13 @@ /* option -> may be NULL ! */ void *search_var_instance_decl_c::visit(output_declarations_c *symbol) { current_vartype = output_vt; + current_option = none_opt; /* not really required. Just to make the code more readable */ + if (NULL != symbol->option) + symbol->option->accept(*this); void *res = symbol->var_init_decl_list->accept(*this); if (res == NULL) { current_vartype = none_vt; + current_option = none_opt; } return res; } @@ -124,6 +216,7 @@ /* VAR_IN_OUT var_declaration_list END_VAR */ void *search_var_instance_decl_c::visit(input_output_declarations_c *symbol) { current_vartype = inoutput_vt; + current_option = none_opt; /* not really required. Just to make the code more readable */ void *res = symbol->var_declaration_list->accept(*this); if (res == NULL) { current_vartype = none_vt; @@ -144,9 +237,13 @@ /* helper symbol for input_declarations */ void *search_var_instance_decl_c::visit(var_declarations_c *symbol) { current_vartype = private_vt; + current_option = none_opt; /* not really required. Just to make the code more readable */ + if (NULL != symbol->option) + symbol->option->accept(*this); void *res = symbol->var_init_decl_list->accept(*this); if (res == NULL) { current_vartype = none_vt; + current_option = none_opt; } return res; } @@ -154,9 +251,11 @@ /* VAR RETAIN var_init_decl_list END_VAR */ void *search_var_instance_decl_c::visit(retentive_var_declarations_c *symbol) { current_vartype = private_vt; + current_option = retain_opt; void *res = symbol->var_init_decl_list->accept(*this); if (res == NULL) { current_vartype = none_vt; + current_option = none_opt; } return res; } @@ -166,9 +265,13 @@ //SYM_REF2(located_var_declarations_c, option, located_var_decl_list) void *search_var_instance_decl_c::visit(located_var_declarations_c *symbol) { current_vartype = located_vt; + current_option = none_opt; /* not really required. Just to make the code more readable */ + if (NULL != symbol->option) + symbol->option->accept(*this); void *res = symbol->located_var_decl_list->accept(*this); if (res == NULL) { current_vartype = none_vt; + current_option = none_opt; } return res; } @@ -178,9 +281,13 @@ //SYM_REF2(external_var_declarations_c, option, external_declaration_list) void *search_var_instance_decl_c::visit(external_var_declarations_c *symbol) { current_vartype = external_vt; + current_option = none_opt; /* not really required. Just to make the code more readable */ + if (NULL != symbol->option) + symbol->option->accept(*this); void *res = symbol->external_declaration_list->accept(*this); if (res == NULL) { current_vartype = none_vt; + current_option = none_opt; } return res; } @@ -190,9 +297,13 @@ //SYM_REF2(global_var_declarations_c, option, global_var_decl_list) void *search_var_instance_decl_c::visit(global_var_declarations_c *symbol) { current_vartype = global_vt; + current_option = none_opt; /* not really required. Just to make the code more readable */ + if (NULL != symbol->option) + symbol->option->accept(*this); void *res = symbol->global_var_decl_list->accept(*this); if (res == NULL) { current_vartype = none_vt; + current_option = none_opt; } return res; } @@ -232,7 +343,7 @@ list_c *list = symbol; for(int i = 0; i < list->n; i++) { if (compare_identifiers(list->elements[i], search_name) == 0) - /* by now, current_fb_declaration should be != NULL */ + /* by now, current_fb_declaration should be != NULL */ return current_type_decl; } return NULL; @@ -262,7 +373,7 @@ /* var1_list ':' structure_type_name */ // SYM_REF2(structured_var_declaration_c, var1_list, structure_type_name) void *search_var_instance_decl_c::visit(structured_var_declaration_c *symbol) { - current_type_decl = symbol; + current_type_decl = symbol->structure_type_name; return symbol->var1_list->accept(*this); } @@ -400,21 +511,62 @@ /*****************************/ /* B 1.5.2 - Function Blocks */ /*****************************/ +/* FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */ +// SYM_REF3(function_block_declaration_c, fblock_name, var_declarations, fblock_body) void *search_var_instance_decl_c::visit(function_block_declaration_c *symbol) { - /* no need to search through all the body, so we only - * visit the variable declarations...! - */ - return symbol->var_declarations->accept(*this); + /* visit the variable declarations...! */ + void *res = symbol->var_declarations->accept(*this); + if (NULL != res) + return res; + + /* not yet found, so we look into the body, to see if it is an SFC step! */ + return symbol->fblock_body->accept(*this); } /**********************/ /* B 1.5.3 - Programs */ /**********************/ +/* PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM */ +// SYM_REF3(program_declaration_c, program_type_name, var_declarations, function_block_body) void *search_var_instance_decl_c::visit(program_declaration_c *symbol) { - /* no need to search through all the body, so we only - * visit the variable declarations...! - */ - return symbol->var_declarations->accept(*this); + /* visit the variable declarations...! */ + void *res = symbol->var_declarations->accept(*this); + if (NULL != res) + return res; + + /* not yet found, so we look into the body, to see if it is an SFC step! */ + return symbol->function_block_body->accept(*this); +} + + +/*********************************************/ +/* B.1.6 Sequential function chart elements */ +/*********************************************/ +/* | sequential_function_chart sfc_network */ +// SYM_LIST(sequential_function_chart_c) +/* search_var_instance_decl_c inherits from serach_visitor_c, so no need to implement the following method. */ +// void *search_var_instance_decl_c::visit(sequential_function_chart_c *symbol) {...} + +/* initial_step {step | transition | action} */ +// SYM_LIST(sfc_network_c) +/* search_var_instance_decl_c inherits from serach_visitor_c, so no need to implement the following method. */ +// void *search_var_instance_decl_c::visit(sfc_network_c *symbol) {...} + + +/* INITIAL_STEP step_name ':' action_association_list END_STEP */ +// SYM_REF2(initial_step_c, step_name, action_association_list) +void *search_var_instance_decl_c::visit(initial_step_c *symbol) { + if (compare_identifiers(symbol->step_name, search_name) == 0) + return symbol; + return NULL; +} + +/* STEP step_name ':' action_association_list END_STEP */ +// SYM_REF2(step_c, step_name, action_association_list) +void *search_var_instance_decl_c::visit(step_c *symbol) { + if (compare_identifiers(symbol->step_name, search_name) == 0) + return symbol; + return NULL; } @@ -469,35 +621,30 @@ return NULL; } -#if 0 -/*********************/ -/* B 1.4 - Variables */ -/*********************/ -SYM_REF2(symbolic_variable_c, var_name, unused) - -/********************************************/ -/* B.1.4.1 Directly Represented Variables */ -/********************************************/ -SYM_TOKEN(direct_variable_c) - -/*************************************/ -/* B.1.4.2 Multi-element Variables */ -/*************************************/ -/* subscripted_variable '[' subscript_list ']' */ -SYM_REF2(array_variable_c, subscripted_variable, subscript_list) - -/* subscript_list ',' subscript */ -SYM_LIST(subscript_list_c) - -/* record_variable '.' field_selector */ -/* WARNING: input and/or output variables of function blocks - * may be accessed as fields of a tructured variable! - * Code handling a structured_variable_c must take - * this into account! - */ -SYM_REF2(structured_variable_c, record_variable, field_selector) - - - -}; -#endif + + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ +/*| instruction_list il_instruction */ +// SYM_LIST(instruction_list_c) +void *search_var_instance_decl_c::visit(instruction_list_c *symbol) { + /* IL code does not contain any variable declarations! */ + return NULL; +} + + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/********************/ +/* B 3.2 Statements */ +/********************/ +// SYM_LIST(statement_list_c) +void *search_var_instance_decl_c::visit(statement_list_c *symbol) { + /* ST code does not contain any variable declarations! */ + return NULL; +} diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/search_var_instance_decl.hh --- a/absyntax_utils/search_var_instance_decl.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/search_var_instance_decl.hh Wed Aug 22 16:46:17 2012 +0200 @@ -31,26 +31,51 @@ */ -/* Determine the data type of a specific variable instance, including - * function block instances. - * A reference to the relevant variable declaration is returned. +/* Search in a VAR* END_VAR declaration for the delcration of the specified variable instance. + * Will return: + * - the declaration itself (get_decl() ) + * - the type of declaration in which the variable was declared (get_vartype() ) + * * The variable instance may NOT be a member of a structure of a memeber * of a structure of an element of an array of ... * - * example: + * For example, considering the following 'variables': * window.points[1].coordinate.x * window.points[1].colour - * etc... ARE NOT ALLOWED! + * offset[99] + * + * passing a reference to 'points', 'points[1]', 'points[1].colour', 'colour' + * ARE NOT ALLOWED! * * This class must only be passed the name of the variable that will appear * in the variable declaration. In the above examples, this would be - * 'window' !! - * - * - * If you need to pass a complete name of a variable instance (such as - * 'window.points[1].coordinate.x') use the search_varfb_instance_type_c instead! - */ -/* Note that current_type_decl that this class returns may reference the + * 'window.points[1].coordinate.x' + * 'window.points[1].coordinate' + * 'window.points[1]' + * 'window' + * 'window.points[1].colour' + * 'offset' + * 'offset[99]' + * + * + */ + +/* Note: + * Determining the declaration type of a specific variable instance (including + * function block instances) really means determining whether the variable was declared in a + * VAR_INPUT + * VAR_OUTPUT + * VAR_IN_OUT + * VAR + * VAR_TEMP + * VAR_EXTERNAL + * VAR_GLOBAL + * VAR AT -> Located variable! + * + */ + +/* Note: + * The current_type_decl that this class returns may reference the * name of a type, or the type declaration itself! * For an example of the first, consider a variable declared as ... * x : AAA; @@ -65,22 +90,29 @@ class search_var_instance_decl_c: public search_visitor_c { - private: - symbol_c *search_scope; - symbol_c *search_name; - symbol_c *current_type_decl; - - /* variable used to store the type of variable currently being processed... */ - /* Will contain a single value of generate_c_vardecl_c::XXXX_vt */ - unsigned int current_vartype; - public: search_var_instance_decl_c(symbol_c *search_scope); - symbol_c *get_decl(symbol_c *variable_instance_name); - unsigned int get_vartype(void); public: - + typedef enum { + input_vt , // VAR_INPUT + output_vt , // VAR_OUTPUT + inoutput_vt, // VAR_IN_OUT + private_vt , // VAR + temp_vt , // VAR_TEMP + external_vt, // VAR_EXTERNAL + global_vt , // VAR_GLOBAL + located_vt , // VAR AT + none_vt + } vt_t; + + typedef enum { + constant_opt , + retain_opt , + non_retain_opt, + none_opt + } opt_t; +#if 0 /* the types of variables that need to be processed... */ static const unsigned int none_vt = 0x0000; static const unsigned int input_vt = 0x0001; // VAR_INPUT @@ -92,6 +124,34 @@ static const unsigned int global_vt = 0x0040; // VAR_GLOBAL static const unsigned int located_vt = 0x0080; // VAR AT + static const unsigned int none_opt = 0x0000; + static const unsigned int constant_opt = 0x0001; + static const unsigned int retain_opt = 0x0002; + static const unsigned int non_retain_opt = 0x0003; +#endif + + symbol_c * get_decl (symbol_c *variable_instance_name); + vt_t get_vartype(symbol_c *variable_instance_name); + opt_t get_option (symbol_c *variable_instance_name); + + /* NOTE: The following function will be completely deleted in the (hopefully near) future. */ + /* Returns true if the variable is an ARRAY or a STRUCT, otherwise returns false. + * Note that for FB, also returns false! + */ + bool type_is_complex(symbol_c *variable_name); + + + + private: + symbol_c *search_scope; + symbol_c *search_name; + symbol_c *current_type_decl; + /* variable used to store the type of variable currently being processed... */ + /* Will contain a single value of generate_c_vardecl_c::XXXX_vt */ + vt_t current_vartype; + opt_t current_option; + + private: /***************************/ /* B 0 - Programming Model */ @@ -105,6 +165,11 @@ /* edge -> The F_EDGE or R_EDGE directive */ // SYM_REF2(edge_declaration_c, edge, var1_list) // TODO + void *visit(constant_option_c *symbol); + void *visit(retain_option_c *symbol); + void *visit(non_retain_option_c *symbol); + + void *visit(input_declarations_c *symbol); /* VAR_OUTPUT [RETAIN | NON_RETAIN] var_init_decl_list END_VAR */ /* option -> may be NULL ! */ @@ -236,6 +301,28 @@ /**********************/ void *visit(program_declaration_c *symbol); + /*********************************************/ + /* B.1.6 Sequential function chart elements */ + /*********************************************/ + /* | sequential_function_chart sfc_network */ + // SYM_LIST(sequential_function_chart_c) + /* search_var_instance_decl_c inherits from serach_visitor_c, so no need to implement the following method. */ + // void *visit(sequential_function_chart_c *symbol); + + /* initial_step {step | transition | action} */ + // SYM_LIST(sfc_network_c) + /* search_var_instance_decl_c inherits from serach_visitor_c, so no need to implement the following method. */ + // void *visit(sfc_network_c *symbol); + + + /* INITIAL_STEP step_name ':' action_association_list END_STEP */ + // SYM_REF2(initial_step_c, step_name, action_association_list) + void *visit(initial_step_c *symbol); + + /* STEP step_name ':' action_association_list END_STEP */ + // SYM_REF2(step_c, step_name, action_association_list) + void *visit(step_c *symbol); + /********************************/ /* B 1.7 Configuration elements */ /********************************/ @@ -265,34 +352,26 @@ // SYM_REF2(single_resource_declaration_c, task_configuration_list, program_configuration_list) void *visit(single_resource_declaration_c *symbol); -#if 0 -/*********************/ -/* B 1.4 - Variables */ -/*********************/ -SYM_REF2(symbolic_variable_c, var_name, unused) - -/********************************************/ -/* B.1.4.1 Directly Represented Variables */ -/********************************************/ -SYM_TOKEN(direct_variable_c) - -/*************************************/ -/* B.1.4.2 Multi-element Variables */ -/*************************************/ -/* subscripted_variable '[' subscript_list ']' */ -SYM_REF2(array_variable_c, subscripted_variable, subscript_list) - -/* subscript_list ',' subscript */ -SYM_LIST(subscript_list_c) - -/* record_variable '.' field_selector */ -/* WARNING: input and/or output variables of function blocks - * may be accessed as fields of a tructured variable! - * Code handling a structured_variable_c must take - * this into account! - */ -SYM_REF2(structured_variable_c, record_variable, field_selector) -#endif + + /****************************************/ + /* B.2 - Language IL (Instruction List) */ + /****************************************/ + /***********************************/ + /* B 2.1 Instructions and Operands */ + /***********************************/ + /*| instruction_list il_instruction */ + // SYM_LIST(instruction_list_c) + void *visit(instruction_list_c *symbol); + + + /***************************************/ + /* B.3 - Language ST (Structured Text) */ + /***************************************/ + /********************/ + /* B 3.2 Statements */ + /********************/ + // SYM_LIST(statement_list_c) + void *visit(statement_list_c *symbol); }; // search_var_instance_decl_c diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/search_varfb_instance_type.cc --- a/absyntax_utils/search_varfb_instance_type.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/search_varfb_instance_type.cc Wed Aug 22 16:46:17 2012 +0200 @@ -1,7 +1,7 @@ /* * matiec - a compiler for the programming languages defined in IEC 61131-3 * - * Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2003-2012 Mario de Sousa (msousa@fe.up.pt) * Copyright (C) 2007-2011 Laurent Bessard and Edouard Tisserant * * This program is free software: you can redistribute it and/or modify @@ -75,225 +75,143 @@ * * Member functions: * ================ + * get_basetype_id() ---> returns 2A (implemented, although currently it is not needed! ) * get_basetype_decl() ---> returns 2B * get_type_id() ---> returns 1A * - * Since we haven't yet needed them, we don't yet implement - * get_basetype_id() ----> would return 2A - * get_type_decl() ----> would return 1B + * Since we haven't yet needed it, we don't yet implement + * get_type_decl() ---> returns 1B */ -/* - * TODO: this code has a memory leak... - * We call 'new' in several locations, but bever get to 'delete' the object instances... - */ + #include "absyntax_utils.hh" +void search_varfb_instance_type_c::init(void) { + this->current_type_id = NULL; + this->current_basetype_id = NULL; + this->current_basetype_decl = NULL; + this->current_field_selector = NULL; +} + + search_varfb_instance_type_c::search_varfb_instance_type_c(symbol_c *search_scope): search_var_instance_decl(search_scope) { - this->decompose_var_instance_name = NULL; - this->current_structelement_name = NULL; - this->current_typeid = NULL; - this->current_basetypeid = NULL; -} - -symbol_c *search_varfb_instance_type_c::get_type_decl(symbol_c *variable_name) { - this->current_structelement_name = NULL; - this->current_typeid = NULL; - this->current_basetypeid = NULL; - this->decompose_var_instance_name = new decompose_var_instance_name_c(variable_name); - if (NULL == decompose_var_instance_name) ERROR; - - /* find the part of the variable name that will appear in the - * variable declaration, for e.g., in window.point.x, this would be - * window! + this->init(); +} + + +/* We expect to be passed a symbolic_variable_c */ +symbol_c *search_varfb_instance_type_c::get_type_id(symbol_c *variable_name) { + this->init(); + variable_name->accept(*this); + return current_type_id; +} + + +symbol_c *search_varfb_instance_type_c::get_basetype_id(symbol_c *variable_name) { + this->init(); + variable_name->accept(*this); + return current_basetype_id; +} + + +symbol_c *search_varfb_instance_type_c::get_basetype_decl(symbol_c *variable_name) { + this->init(); + variable_name->accept(*this); + return current_basetype_decl; +} + + + + +/*************************/ +/* B.1 - Common elements */ +/*************************/ +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ +// SYM_TOKEN(identifier_c) +void *search_varfb_instance_type_c::visit(identifier_c *variable_name) { + /* symbol should be a variable name!! */ + /* Note: although the method is called get_decl(), it is getting the declaration of the variable, which for us is the type_id of that variable! */ + current_type_id = search_var_instance_decl.get_decl (variable_name); + current_basetype_decl = search_base_type.get_basetype_decl(current_type_id); + current_basetype_id = search_base_type.get_basetype_id (current_type_id); + + /* What if the variable has not been declared? Then this should not be a compiler error! + * However, currently stage 2 of the compiler already detects when variables have not been delcared, + * so if the variable's declaration is not found, then that means that we have an internal compiler error! + * + * Actually, the above is not true anymore. See the use of the any_symbolic_variable in iec_bison.yy + * - when defining the delay of a delayed action association in SFC + * - in program connections inside configurations (will this search_varfb_instance_type_c class be called to handle this??) */ - symbol_c *var_name_part = decompose_var_instance_name->next_part(); - if (NULL == var_name_part) ERROR; - - /* Now we try to find the variable instance declaration, to determine its type... */ - symbol_c *var_decl = search_var_instance_decl.get_decl(var_name_part); - if (NULL == var_decl) ERROR; - - /* if it is a struct or function block, we must search the type - * of the struct or function block member. - * This is done by this class visiting the var_decl. - * This class, while visiting, will recursively call - * decompose_var_instance_name->get_next() when and if required... - */ - symbol_c *res = (symbol_c *)var_decl->accept(*this); - /* NOTE: A Null result is not really an internal compiler error, but rather an error in - * the IEC 61131-3 source code being compiled. This means we cannot just abort the compiler with ERROR. - * // if (NULL == res) ERROR; - */ - if (NULL == res) return NULL; - - /* make sure that we have decomposed all structure elements of the variable name */ - symbol_c *var_name = decompose_var_instance_name->next_part(); - /* NOTE: A non-NULL result is not really an internal compiler error, but rather an error in - * the IEC 61131-3 source code being compiled. - * (for example, 'int_var.struct_elem' in the source code, when 'int_var' is a simple integer, - * and not a structure, will result in this result being non-NULL!) - * This means we cannot just abort the compiler with ERROR. - * // if (NULL != var_name) ERROR; - */ - if (NULL != var_name) return NULL; - - return res; -} - - -symbol_c *search_varfb_instance_type_c::get_basetype_decl(symbol_c *variable_name) { - symbol_c *res = get_type_decl(variable_name); - if (NULL == res) return NULL; - return (symbol_c *)base_type(res); -} - - -unsigned int search_varfb_instance_type_c::get_vartype(symbol_c *variable_name) { - this->current_structelement_name = NULL; - this->current_typeid = NULL; - this->current_basetypeid = NULL; - this->is_complex = false; - this->decompose_var_instance_name = new decompose_var_instance_name_c(variable_name); - if (NULL == decompose_var_instance_name) ERROR; - - /* find the part of the variable name that will appear in the - * variable declaration, for e.g., in window.point.x, this would be - * window! - */ - symbol_c *var_name_part = decompose_var_instance_name->next_part(); - if (NULL == var_name_part) ERROR; - - /* Now we try to find the variable instance declaration, to determine its type... */ - symbol_c *var_decl = search_var_instance_decl.get_decl(var_name_part); - if (NULL == var_decl) { - /* variable instance declaration not found! */ - return 0; - } - - /* if it is a struct or function block, we must search the type - * of the struct or function block member. - * This is done by this class visiting the var_decl. - * This class, while visiting, will recursively call - * decompose_var_instance_name->get_next() when and if required... - */ - var_decl->accept(*this); - unsigned int res = search_var_instance_decl.get_vartype(); - - /* make sure that we have decomposed all structure elements of the variable name */ - symbol_c *var_name = decompose_var_instance_name->next_part(); - if (NULL != var_name) ERROR; - - return res; -} - -symbol_c *search_varfb_instance_type_c::get_type_id(symbol_c *variable_name) { - this->current_typeid = NULL; - symbol_c *vartype = this->get_type_decl(variable_name); - if (this->current_typeid != NULL) - return this->current_typeid; - else - return vartype; -} - -bool search_varfb_instance_type_c::type_is_complex(void) { - return this->is_complex; -} - -/* a helper function... */ -void *search_varfb_instance_type_c::visit_list(list_c *list) { - if (NULL == current_structelement_name) ERROR; - - for(int i = 0; i < list->n; i++) { - void *res = list->elements[i]->accept(*this); - if (res != NULL) - return res; - } - /* not found! */ - return NULL; -} - -/* a helper function... */ -void *search_varfb_instance_type_c::base_type(symbol_c *symbol) { - search_base_type_c search_base_type; - return symbol->accept(search_base_type); -} - -/* We override the base class' visitor to identifier_c. - * This is so because the base class does not consider a function block - * to be a type, unlike this class that allows a variable instance - * of a function block type... - */ -void *search_varfb_instance_type_c::visit(identifier_c *type_name) { - /* we only store the new type id if none had been found yet. - * Since we will recursively carry on looking at the base type - * to determine the base type declaration and id, we must only set this variable - * the first time. - * e.g. TYPE myint1_t : int := 1; - * myint2_t : int1_t := 2; - * myint3_t : int2_t := 3; - * END_TYPE; - * VAR - * myint1 : myint1_t; - * myint2 : myint2_t; - * myint3 : myint3_t; - * END_VAR - * - * If we ask for typeid of myint3, it must return myint3_t - * If we ask for basetypeid of myint3, it must return int - * - * When determining the data type of myint3, we will recursively go all the way - * down to int, but we must still only store myint3_t as the base type id. - */ - if (NULL == this->current_typeid) - this->current_typeid = type_name; - this->current_basetypeid = type_name; - - /* look up the type declaration... */ - symbol_c *fb_decl = function_block_type_symtable.find_value(type_name); - if (fb_decl != function_block_type_symtable.end_value()) - /* Type declaration found!! */ - return fb_decl->accept(*this); - - /* No. It is not a function block, so we let - * the base class take care of it... - */ - return search_base_type_c::visit(type_name); -} + // if (NULL == current_type_id) ERROR; + + return NULL; +} + + + + /********************************/ /* B 1.3.3 - Derived data types */ /********************************/ - /* identifier ':' array_spec_init */ +/* NOTE: I don't think this will ever get called, since in the visit method for array_variable_c + * we use the basetype_decl for recursively calling this class, and the base type should never be a + * array_type_declaration_c, but for now, let's leave it in... + */ void *search_varfb_instance_type_c::visit(array_type_declaration_c *symbol) { - return symbol->array_spec_init->accept(*this); + ERROR; + return NULL; } /* array_specification [ASSIGN array_initialization] */ /* array_initialization may be NULL ! */ +/* NOTE: I don't think this will ever get called, since in the visit method for array_variable_c + * we use the basetype_decl for recursively calling this class, and the base type should never be a + * array_spec_init_c, but for now, let's leave it in... + */ void *search_varfb_instance_type_c::visit(array_spec_init_c *symbol) { - return symbol->array_specification->accept(*this); + /* Note that the 'array_specification' may be either an identifier of a previsously defined array type, + * or an array_specification_c, so we can not stop here and simply return a array_spec_init_c, + * especially if we are looking for the base class! + */ + ERROR; + return NULL; } /* ARRAY '[' array_subrange_list ']' OF non_generic_type_name */ +/* NOTE: This method will be reached after being called from the + * search_varfb_instance_type_c::visit(array_variable_c *symbol) + * method, so we must return the data type of the data stored in the array, + * and not the data type of the array itself! + */ void *search_varfb_instance_type_c::visit(array_specification_c *symbol) { - this->is_complex = true; - this->current_typeid = symbol; - return symbol->non_generic_type_name->accept(*this); -} + /* found the type of the element we were looking for! */ + current_type_id = symbol->non_generic_type_name; + current_basetype_decl = search_base_type.get_basetype_decl(current_type_id); + current_basetype_id = search_base_type.get_basetype_id (current_type_id); + + return NULL; +} + /* structure_type_name ':' structure_specification */ /* NOTE: this is only used inside a TYPE ... END_TYPE declaration. * It is never used directly when declaring a new variable! */ +/* NOTE: I don't think this will ever get called, since in the visit method for structured_variable_c + * we use the basetype_decl for recursively calling this class, and the base type should never be a + * structure_type_declaration_c, but for now, let's leave it in... + */ void *search_varfb_instance_type_c::visit(structure_type_declaration_c *symbol) { - this->is_complex = true; - - if (NULL == current_structelement_name) ERROR; - return symbol->structure_specification->accept(*this); + if (NULL == current_field_selector) ERROR; + symbol->structure_specification->accept(*this); + return NULL; /* NOTE: structure_specification will point to either a * initialized_structure_c * OR A @@ -301,82 +219,45 @@ */ } -/* var1_list ':' structure_type_name */ -void *search_varfb_instance_type_c::visit(structured_var_declaration_c *symbol) { - this->is_complex = true; - if (NULL != current_structelement_name) ERROR; - - /* make sure that we have decomposed all structure elements of the variable name */ - symbol_c *var_name = decompose_var_instance_name->next_part(); - if (NULL == var_name) { - /* this is it... ! - * No need to look any further... - * Note also that, unlike for the struct types, a function block may - * not be defined based on another (i.e. no inheritance is allowed), - * so this function block is already the most base type. - * We simply return it. - */ - return (void *)symbol; - } - - /* reset current_type_id because of new structure element part */ - this->current_typeid = NULL; - - /* look for the var_name in the structure declaration */ - current_structelement_name = var_name; - - /* recursively find out the data type of current_structelement_name... */ - return symbol->structure_type_name->accept(*this); -} - /* structure_type_name ASSIGN structure_initialization */ /* structure_initialization may be NULL ! */ // SYM_REF2(initialized_structure_c, structure_type_name, structure_initialization) -/* NOTE: only the initialized structure is ever used when declaring a new variable instance */ +/* NOTE: only the initialized structure is never used when declaring a new variable instance */ +/* NOTE: I don't think this will ever get called, since in the visit method for structured_variable_c + * we use the basetype_decl for recursively calling this class, and the base type should never be a + * initialized_structure_c, but for now, let's leave it in... + */ void *search_varfb_instance_type_c::visit(initialized_structure_c *symbol) { - this->is_complex = true; - if (NULL != current_structelement_name) ERROR; - - /* make sure that we have decomposed all structure elements of the variable name */ - symbol_c *var_name = decompose_var_instance_name->next_part(); - if (NULL == var_name) { - /* this is it... ! - * No need to look any further... - * Note also that, unlike for the struct types, a function block may - * not be defined based on another (i.e. no inheritance is allowed), - * so this function block is already the most base type. - * We simply return it. - */ - return (void *)symbol; - } - - /* reset current_type_id because of new structure element part */ - this->current_typeid = NULL; - - /* look for the var_name in the structure declaration */ - current_structelement_name = var_name; - - /* recursively find out the data type of current_structelement_name... */ - return symbol->structure_type_name->accept(*this); + if (NULL != current_field_selector) ERROR; + + /* recursively find out the data type of current_field_selector... */ + symbol->structure_type_name->accept(*this); + return NULL; } /* helper symbol for structure_declaration */ /* structure_declaration: STRUCT structure_element_declaration_list END_STRUCT */ /* structure_element_declaration_list structure_element_declaration ';' */ void *search_varfb_instance_type_c::visit(structure_element_declaration_list_c *symbol) { - if (NULL == current_structelement_name) ERROR; + if (NULL == current_field_selector) ERROR; + /* now search the structure declaration */ - return visit_list(symbol); + for(int i = 0; i < symbol->n; i++) { + symbol->elements[i]->accept(*this); + } + + return NULL; } /* structure_element_name ':' spec_init */ void *search_varfb_instance_type_c::visit(structure_element_declaration_c *symbol) { - if (NULL == current_structelement_name) ERROR; - - if (compare_identifiers(symbol->structure_element_name, current_structelement_name) == 0) { - current_structelement_name = NULL; + if (NULL == current_field_selector) ERROR; + + if (compare_identifiers(symbol->structure_element_name, current_field_selector) == 0) { /* found the type of the element we were looking for! */ - return symbol->spec_init->accept(*this); + current_type_id = symbol->spec_init; + current_basetype_decl = search_base_type.get_basetype_decl(current_type_id); + current_basetype_id = search_base_type.get_basetype_id (current_type_id); } /* Did not find the type of the element we were looking for! */ @@ -392,6 +273,79 @@ void *search_varfb_instance_type_c::visit(structure_element_initialization_c *symbol) {ERROR; return NULL;} /* should never get called... */ +/*********************/ +/* B 1.4 - Variables */ +/*********************/ +// SYM_REF1(symbolic_variable_c, var_name) +void *search_varfb_instance_type_c::visit(symbolic_variable_c *symbol) { + symbol->var_name->accept(*this); + return NULL; +} + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ +// SYM_TOKEN(direct_variable_c) +/* We do not yet handle this. Will we ever need to handle it, as the data type of the direct variable is + * directly obtainable from the syntax of the direct variable itself? + */ + +/*************************************/ +/* B 1.4.2 - Multi-element variables */ +/*************************************/ +/* subscripted_variable '[' subscript_list ']' */ +// SYM_REF2(array_variable_c, subscripted_variable, subscript_list) +/* NOTE: when passed a array_variable_c, which represents some IEC61131-3 code similar to X[42] + * we must return the data type of the value _stored_ in the array. + * If you want to get the data type of the array itself (i.e. just the X variable, without the [42]) + * then this class must be called with the identifier_c 'X'. + */ +void *search_varfb_instance_type_c::visit(array_variable_c *symbol) { + /* determine the data type of the subscripted_variable... + * This should be an array_specification_c + * ARRAY [xx..yy] OF Stored_Data_Type + */ + symbol->subscripted_variable->accept(*this); + symbol_c *basetype_decl = current_basetype_decl; + this->init(); /* set all current_*** pointers to NULL ! */ + + /* Now we determine the 'Stored_Data_Type', i.e. the data type of the variable stored in the array. */ + if (NULL != basetype_decl) { + basetype_decl->accept(*this); + } + + return NULL; +} + + +/* record_variable '.' field_selector */ +/* WARNING: input and/or output variables of function blocks + * may be accessed as fields of a structured variable! + * Code handling a structured_variable_c must take this into account! + * (i.e. that a FB instance may be accessed as a structured variable)! + * + * WARNING: Status bit (.X) and activation time (.T) of STEPS in SFC diagrams + * may be accessed as fields of a structured variable! + * Code handling a structured_variable_c must take this into account + * (i.e. that an SFC STEP may be accessed as a structured variable)! + */ +// SYM_REF2(structured_variable_c, record_variable, field_selector) +void *search_varfb_instance_type_c::visit(structured_variable_c *symbol) { + symbol->record_variable->accept(*this); + symbol_c *basetype_decl = current_basetype_decl; + this->init(); /* set all current_*** pointers to NULL ! */ + + /* Now we search for the data type of the field... But only if we were able to determine the data type of the variable */ + if (NULL != basetype_decl) { + current_field_selector = symbol->field_selector; + basetype_decl->accept(*this); + current_field_selector = NULL; + } + + return NULL; +} + + /**************************************/ /* B.1.5 - Program organization units */ @@ -399,46 +353,53 @@ /*****************************/ /* B 1.5.2 - Function Blocks */ /*****************************/ + /* FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */ // SYM_REF4(function_block_declaration_c, fblock_name, var_declarations, fblock_body, unused) void *search_varfb_instance_type_c::visit(function_block_declaration_c *symbol) { - /* make sure that we have decomposed all structure elements of the variable name */ - symbol_c *var_name = decompose_var_instance_name->next_part(); - if (NULL == var_name) { - /* this is it... ! - * No need to look any further... - * Note also that, unlike for the struct types, a function block may - * not be defined based on another (i.e. no inheritance is allowed), - * so this function block is already the most base type. - * We simply return it. - */ - return (void *)symbol; - } - - /* reset current_type_id because of new structure element part */ - this->current_typeid = NULL; - - /* now search the function block declaration for the variable... */ - search_var_instance_decl_c search_decl(symbol); - symbol_c *var_decl = search_decl.get_decl(var_name); - if (NULL == var_decl) { - /* variable instance declaration not found! */ - return NULL; - } -#if 0 - /* We have found the declaration. - * Should we look any further? - */ - var_name = decompose_var_instance_name->next_part(); - if (NULL == var_name) { - /* this is it... ! */ - return base_type(var_decl); - } - - current_structelement_name = var_name; - /* recursively find out the data type of var_name... */ - return symbol->var_declarations->accept(*this); -#endif - /* carry on recursively, in case the variable has more elements to be decomposed... */ - return var_decl->accept(*this); -} + if (NULL == current_field_selector) ERROR; + + /* now search the function block declaration for the variable... */ + /* If not found, these pointers will all be set to NULL!! */ + search_var_instance_decl_c search_decl(symbol); + current_type_id = search_decl.get_decl(current_field_selector); + current_basetype_decl = search_base_type.get_basetype_decl(current_type_id); + current_basetype_id = search_base_type.get_basetype_id (current_type_id); + + return NULL; +} + + + +/*********************************************/ +/* B.1.6 Sequential function chart elements */ +/*********************************************/ +/* INITIAL_STEP step_name ':' action_association_list END_STEP */ +// SYM_REF2(initial_step_c, step_name, action_association_list) +/* NOTE: this method may be called from visit(structured_variable_c *symbol) method| */ +void *search_varfb_instance_type_c::visit(initial_step_c *symbol) { + if (NULL == current_field_selector) ERROR; + + identifier_c T("T"); + identifier_c X("X"); + + if (compare_identifiers(&T, current_field_selector) == 0) + current_type_id = &search_constant_type_c::time_type_name; + if (compare_identifiers(&X, current_field_selector) == 0) + current_type_id = &search_constant_type_c::bool_type_name; + + current_basetype_decl = search_base_type.get_basetype_decl(current_type_id); + current_basetype_id = search_base_type.get_basetype_id (current_type_id); + + return NULL; +} + + +/* STEP step_name ':' action_association_list END_STEP */ +// SYM_REF2(step_c, step_name, action_association_list) +/* NOTE: this method may be called from visit(structured_variable_c *symbol) method| */ +void *search_varfb_instance_type_c::visit(step_c *symbol) { + /* The code here should be identicial to the code in the visit(initial_step_c *) visitor! So we simply call the other visitor! */ + initial_step_c initial_step(NULL, NULL); + return initial_step.accept(*this); +} diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/search_varfb_instance_type.hh --- a/absyntax_utils/search_varfb_instance_type.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/search_varfb_instance_type.hh Wed Aug 22 16:46:17 2012 +0200 @@ -1,7 +1,7 @@ /* * matiec - a compiler for the programming languages defined in IEC 61131-3 * - * Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2003-2012 Mario de Sousa (msousa@fe.up.pt) * Copyright (C) 2007-2011 Laurent Bessard and Edouard Tisserant * * This program is free software: you can redistribute it and/or modify @@ -77,44 +77,38 @@ * * Member functions: * ================ + * get_basetype_id() ---> returns 2A (implemented, although currently it is not needed! ) * get_basetype_decl() ---> returns 2B * get_type_id() ---> returns 1A * - * Since we haven't yet needed them, we don't yet implement - * get_basetype_id() ----> would return 2A - * get_type_decl() ----> would return 1B + * Since we haven't yet needed it, we don't yet implement + * get_type_decl() ---> returns 1B */ -class search_varfb_instance_type_c: public search_base_type_c { +class search_varfb_instance_type_c : null_visitor_c { private: search_var_instance_decl_c search_var_instance_decl; - decompose_var_instance_name_c *decompose_var_instance_name; - symbol_c *current_structelement_name; - symbol_c *current_typeid; - symbol_c *current_basetypeid; - bool is_complex; + search_base_type_c search_base_type; + +// symbol_c *current_type_decl; + symbol_c *current_type_id; + symbol_c *current_basetype_decl; + symbol_c *current_basetype_id; + + symbol_c *current_field_selector; + + /* sets all the above variables to NULL, or false */ + void init(void); public: - search_varfb_instance_type_c(symbol_c *search_scope); - symbol_c *get_basetype_decl(symbol_c *variable_name); - symbol_c *get_type_decl(symbol_c *variable_name); - symbol_c *get_type_id(symbol_c *variable_name); - - /* NOTE: this function should be remvoed/deleted. - * However, it is currently used in stage 4, and before deleting it - * requires that the stage4 code be analysed and fixed (i.e. replace by - * a call to one of the above functions get_basetype_decl(), - * get_type_decl(), get_type_id(). - * - * At the moment, I have a feeling that this whole class search_varfb_instance_type_c - * will not be needed in the future (i.e. when we finish implementing type checking - * in stage 3 correctly, where we store on each symbol in the abstract syntax - * tree it's data type, so stage4 implementations will not need to deduce the data - * types again), so it does not make much sense to spend more time on it. - */ - unsigned int get_vartype(symbol_c *variable_name); - bool type_is_complex(void); + search_varfb_instance_type_c(symbol_c *search_scope ); + symbol_c *get_basetype_decl (symbol_c *variable_name); + symbol_c *get_basetype_id (symbol_c *variable_name); +// symbol_c *get_type_decl (symbol_c *variable_name); + symbol_c *get_type_id (symbol_c *variable_name); + + private: /* a helper function... */ @@ -125,17 +119,17 @@ private: - /* We override the base class' visitor to identifier_c. - * This is so because the base class does not consider a function block - * to be a type, unlike this class that allows a variable instance - * of a function block type... - */ - void *visit(identifier_c *type_name); - + /*************************/ + /* B.1 - Common elements */ + /*************************/ + /*******************************************/ + /* B 1.1 - Letters, digits and identifiers */ + /*******************************************/ + void *visit(identifier_c *variable_name); + /********************************/ /* B 1.3.3 - Derived data types */ /********************************/ - /* identifier ':' array_spec_init */ void *visit(array_type_declaration_c *symbol); @@ -149,9 +143,6 @@ /* structure_type_name ':' structure_specification */ void *visit(structure_type_declaration_c *symbol); - /* var1_list ':' structure_type_name */ - void *visit(structured_var_declaration_c *symbol); - /* structure_type_name ASSIGN structure_initialization */ /* structure_initialization may be NULL ! */ // SYM_REF2(initialized_structure_c, structure_type_name, structure_initialization) @@ -173,7 +164,20 @@ void *visit(structure_element_initialization_c *symbol); /* should never get called... */ - + /*********************/ + /* B 1.4 - Variables */ + /*********************/ + void *visit(symbolic_variable_c *symbol); + + /********************************************/ + /* B.1.4.1 Directly Represented Variables */ + /********************************************/ + /*************************************/ + /* B 1.4.2 - Multi-element variables */ + /*************************************/ + void *visit(array_variable_c *symbol); + void *visit(structured_variable_c *symbol); + /**************************************/ /* B.1.5 - Program organization units */ /**************************************/ @@ -184,6 +188,17 @@ // SYM_REF4(function_block_declaration_c, fblock_name, var_declarations, fblock_body, unused) void *visit(function_block_declaration_c *symbol); + + /*********************************************/ + /* B.1.6 Sequential function chart elements */ + /*********************************************/ + /* INITIAL_STEP step_name ':' action_association_list END_STEP */ + // SYM_REF2(initial_step_c, step_name, action_association_list) + void *visit(initial_step_c *symbol); + /* STEP step_name ':' action_association_list END_STEP */ + // SYM_REF2(step_c, step_name, action_association_list) + void *visit(step_c *symbol); + }; // search_varfb_instance_type_c diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/spec_init_separator.cc --- a/absyntax_utils/spec_init_separator.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/spec_init_separator.cc Wed Aug 22 16:46:17 2012 +0200 @@ -37,6 +37,8 @@ */ #include "spec_init_separator.hh" +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. + //#define DEBUG #ifdef DEBUG @@ -45,9 +47,6 @@ #define TRACE(classname) #endif -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); spec_init_sperator_c *spec_init_sperator_c::get_class_instance(void) { diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/type_initial_value.cc --- a/absyntax_utils/type_initial_value.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/type_initial_value.cc Wed Aug 22 16:46:17 2012 +0200 @@ -84,7 +84,7 @@ date_literal_0 = new date_literal_c(integer_1, integer_1, integer_1); date_literal_0 = new date_literal_c(new integer_c("1970"), integer_1, integer_1); daytime_literal_0 = new daytime_c(integer_0, integer_0, real_0); - time_0 = new duration_c(new time_type_name_c(), NULL, new seconds_c(integer_0, NULL)); // T#0S + time_0 = new duration_c(new time_type_name_c(), NULL, new interval_c(NULL, NULL, NULL, integer_0, NULL)); // T#0s date_0 = new date_c(new date_type_name_c(), date_literal_0); // D#0001-01-01 tod_0 = new time_of_day_c(new tod_type_name_c(), daytime_literal_0); // TOD#00:00:00 dt_0 = new date_and_time_c(new dt_type_name_c(), date_literal_0, daytime_literal_0); // DT#0001-01-01-00:00:00 @@ -366,13 +366,13 @@ * as would be expected! */ /* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ -#if 0 -SYM_REF4(string_type_declaration_c, string_type_name, - elementary_string_type_name, - string_type_declaration_size, - string_type_declaration_init) /* may be == NULL! */ -#endif -void *type_initial_value_c::visit(string_type_declaration_c *symbol) {return NULL;} +// SYM_REF4(string_type_declaration_c, string_type_name, +// elementary_string_type_name, +// string_type_declaration_size, +// string_type_declaration_init) /* may be == NULL! */ +void *type_initial_value_c::visit(string_type_declaration_c *symbol) { + return handle_type_spec(symbol->elementary_string_type_name, symbol->string_type_declaration_init); +} type_initial_value_c *type_initial_value_c::_instance = NULL; diff -r aad38592bdde -r c0bda77b37a0 absyntax_utils/type_initial_value.hh --- a/absyntax_utils/type_initial_value.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/absyntax_utils/type_initial_value.hh Wed Aug 22 16:46:17 2012 +0200 @@ -279,12 +279,10 @@ * as would be expected! */ /* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ -#if 0 -SYM_REF4(string_type_declaration_c, string_type_name, - elementary_string_type_name, - string_type_declaration_size, - string_type_declaration_init) /* may be == NULL! */ -#endif + // SYM_REF4(string_type_declaration_c, string_type_name, + // elementary_string_type_name, + // string_type_declaration_size, + // string_type_declaration_init) /* may be == NULL! */ void *visit(string_type_declaration_c *symbol); }; // type_initial_value_c diff -r aad38592bdde -r c0bda77b37a0 configure --- a/configure Tue Aug 14 19:40:01 2012 +0200 +++ b/configure Wed Aug 22 16:46:17 2012 +0200 @@ -4582,6 +4582,65 @@ RANLIB="$ac_cv_prog_RANLIB" fi +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + + +# Check bison version, we need a version great or equal than 2.4 to build matiec. +[[ $(bison --version) =~ ([0-9][.][0-9]*) ]] && version_bison="${BASH_REMATCH[1]}" + +if awk -v ver="$version_bison" 'BEGIN { if (ver < 2.4) exit 1; }'; then : + have_bison_correct=yes +else + have_bison_correct=no +fi + + +if test "x${have_bison_correct}" = xno; then + echo "------------------------------------------" + echo " Wrong bison version: $version_bison < 2.4 " + echo "------------------------------------------" + (exit 1); exit 1; +fi # Checks for header files. ac_ext=c @@ -5677,6 +5736,7 @@ + ac_config_files="$ac_config_files Makefile absyntax/Makefile absyntax_utils/Makefile stage1_2/Makefile stage3/Makefile stage4/Makefile stage4/generate_c/Makefile stage4/generate_iec/Makefile" cat >confcache <<\_ACEOF diff -r aad38592bdde -r c0bda77b37a0 configure.ac --- a/configure.ac Tue Aug 14 19:40:01 2012 +0200 +++ b/configure.ac Wed Aug 22 16:46:17 2012 +0200 @@ -25,6 +25,21 @@ AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_RANLIB +AC_PROG_AWK + +# Check bison version, we need a version great or equal than 2.4 to build matiec. +[[[ $(bison --version) =~ ([0-9][.][0-9]*) ]]] && version_bison="${BASH_REMATCH[[1]]}" + +AS_IF([awk -v ver="$version_bison" 'BEGIN { if (ver < 2.4) exit 1; }'], + [have_bison_correct=yes], [have_bison_correct=no]) + + +if test "x${have_bison_correct}" = xno; then + echo "------------------------------------------" + echo " Wrong bison version: $version_bison < 2.4 " + echo "------------------------------------------" + (exit 1); exit 1; +fi # Checks for header files. AC_CHECK_HEADERS([float.h limits.h stdint.h stdlib.h string.h strings.h sys/timeb.h unistd.h]) @@ -47,6 +62,7 @@ AC_FUNC_REALLOC AC_CHECK_FUNCS([clock_gettime memset pow strcasecmp strdup strtoul strtoull]) + AC_CONFIG_MACRO_DIR([config]) AC_CONFIG_FILES([Makefile \ diff -r aad38592bdde -r c0bda77b37a0 lib/iec_std_lib.h --- a/lib/iec_std_lib.h Tue Aug 14 19:40:01 2012 +0200 +++ b/lib/iec_std_lib.h Wed Aug 22 16:46:17 2012 +0200 @@ -81,28 +81,28 @@ #endif -#define __lit(type,value,sfx) (type)value##sfx -// Keep this macro expention step to let sfx change into L or LL -#define __literal(type,value,sfx) __lit(type,value,sfx) - -#define __BOOL_LITERAL(value) __literal(BOOL,value,) -#define __SINT_LITERAL(value) __literal(SINT,value,) -#define __INT_LITERAL(value) __literal(INT,value,) +#define __lit(type,value,...) (type)value##__VA_ARGS__ +// Keep this macro expention step to let sfx(__VA_ARGS__) change into L or LL +#define __literal(type,value,...) __lit(type,value,##__VA_ARGS__) + +#define __BOOL_LITERAL(value) __literal(BOOL,value) +#define __SINT_LITERAL(value) __literal(SINT,value) +#define __INT_LITERAL(value) __literal(INT,value) #define __DINT_LITERAL(value) __literal(DINT,value,__32b_sufix) #define __LINT_LITERAL(value) __literal(LINT,value,__64b_sufix) -#define __USINT_LITERAL(value) __literal(USINT,value,) -#define __UINT_LITERAL(value) __literal(UINT,value,) +#define __USINT_LITERAL(value) __literal(USINT,value) +#define __UINT_LITERAL(value) __literal(UINT,value) #define __UDINT_LITERAL(value) __literal(UDINT,value,__32b_sufix) #define __ULINT_LITERAL(value) __literal(ULINT,value,__64b_sufix) #define __REAL_LITERAL(value) __literal(REAL,value,__32b_sufix) #define __LREAL_LITERAL(value) __literal(LREAL,value,__64b_sufix) -#define __TIME_LITERAL(value) __literal(TIME,value,) -#define __DATE_LITERAL(value) __literal(DATE,value,) -#define __TOD_LITERAL(value) __literal(TOD,value,) -#define __DT_LITERAL(value) __literal(DT,value,) +#define __TIME_LITERAL(value) __literal(TIME,value) +#define __DATE_LITERAL(value) __literal(DATE,value) +#define __TOD_LITERAL(value) __literal(TOD,value) +#define __DT_LITERAL(value) __literal(DT,value) #define __STRING_LITERAL(count,value) (STRING){count,value} -#define __BYTE_LITERAL(value) __literal(BYTE,value,) -#define __WORD_LITERAL(value) __literal(WORD,value,) +#define __BYTE_LITERAL(value) __literal(BYTE,value) +#define __WORD_LITERAL(value) __literal(WORD,value) #define __DWORD_LITERAL(value) __literal(DWORD,value,__32b_sufix) #define __LWORD_LITERAL(value) __literal(LWORD,value,__64b_sufix) @@ -261,6 +261,7 @@ broken_down_time.tm_mday = day; /* day of month, from 1 to 31 */ broken_down_time.tm_mon = month - 1; /* month since January, in the range 0 to 11 */ broken_down_time.tm_year = year - 1900; /* number of years since 1900 */ + broken_down_time.tm_isdst = 0; /* disable daylight savings time */ epoch_seconds = mktime(&broken_down_time); /* determine number of seconds since the epoch, i.e. Jan 1st 1970 */ if ((time_t)(-1) == epoch_seconds) @@ -568,16 +569,16 @@ } if(IN.tv_nsec == 0){ res.len = snprintf((char*)&res.body, STR_MAX_LEN, "DT#%d-%2.2d-%2.2d-%2.2d:%2.2d:%d", - broken_down_time->tm_year, - broken_down_time->tm_mon, + broken_down_time->tm_year + 1900, + broken_down_time->tm_mon + 1, broken_down_time->tm_mday, broken_down_time->tm_hour, broken_down_time->tm_min, broken_down_time->tm_sec); }else{ res.len = snprintf((char*)&res.body, STR_MAX_LEN, "DT#%d-%2.2d-%2.2d-%2.2d:%2.2d:%g", - broken_down_time->tm_year, - broken_down_time->tm_mon, + broken_down_time->tm_year + 1900, + broken_down_time->tm_mon + 1, broken_down_time->tm_mday, broken_down_time->tm_hour, broken_down_time->tm_min, @@ -1752,7 +1753,6 @@ /**************/ /* NE */ /**************/ - #define __ne_num(fname, TYPENAME) \ static inline BOOL fname(EN_ENO_PARAMS, TYPENAME op1, TYPENAME op2){\ TEST_EN(BOOL)\ diff -r aad38592bdde -r c0bda77b37a0 main.cc --- a/main.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/main.cc Wed Aug 22 16:46:17 2012 +0200 @@ -80,11 +80,23 @@ #define HGVERSION "" #endif -#define ERROR error_exit(__FILE__,__LINE__) -void error_exit(const char *file_name, int line_no) { - std::cerr << "\nInternal compiler error in file " << file_name - << " at line " << line_no << "\n"; -// if (msg != NULL) std::cerr << message << "\n\n"; +#include "main.hh" // symbol_c type +#include // required for va_start(), va_list + +void error_exit(const char *file_name, int line_no, const char *errmsg, ...) { + va_list argptr; + va_start(argptr, errmsg); /* second argument is last fixed pamater of error_exit() */ + + fprintf(stderr, "\nInternal compiler error in file %s at line %d", file_name, line_no); + if (errmsg != NULL) { + fprintf(stderr, ": "); + vfprintf(stderr, errmsg, argptr); + } else { + fprintf(stderr, "."); + } + fprintf(stderr, "\n"); + va_end(argptr); + exit(EXIT_FAILURE); } @@ -124,6 +136,7 @@ char * builddir = NULL; stage1_2_options_t stage1_2_options = {false, false, NULL}; int optres, errflg = 0; + int path_len; /* extern char *optarg; extern int optind, optopt; @@ -148,9 +161,19 @@ stage1_2_options.safe_extensions = true; break; case 'I': + /* NOTE: To improve the usability under windows: + * We delete last char's path if it ends with "\". + * In this way compiler front-end accepts paths with or without + * slash terminator. + */ + path_len = strlen(optarg) - 1; + if (optarg[path_len] == '\\') optarg[path_len]= '\0'; stage1_2_options.includedir = optarg; break; case 'T': + /* NOTE: see note above */ + path_len = strlen(optarg) - 1; + if (optarg[path_len] == '\\') optarg[path_len]= '\0'; builddir = optarg; break; case ':': /* -I or -T without operand */ @@ -198,7 +221,7 @@ /* moved to bison, although it could perfectly well still be here instead of in bison code. */ //add_en_eno_param_decl_c::add_to(tree_root); - /* Only very simple (not yet complete) data type checking currently implemented... */ + /* Do semantic verification of code (data type and lvalue checking currently implemented) */ if (stage3(tree_root) < 0) return EXIT_FAILURE; diff -r aad38592bdde -r c0bda77b37a0 main.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.hh Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,127 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2007-2011 Laurent Bessard and Edouard Tisserant + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + + +#ifndef _MAIN_HH +#define _MAIN_HH + + + + /* Function used throughout the code --> used to report failed assertions (i.e. internal compiler errors)! */ +#include /* required for NULL */ + +#define ERROR error_exit(__FILE__,__LINE__) +#define ERROR_MSG(msg, ...) error_exit(__FILE__,__LINE__, msg) +// #define ERROR_MSG(msg, ...) error_exit(__FILE__,__LINE__, msg, __VA_ARGS__) + +extern void error_exit(const char *file_name, int line_no, const char *errmsg = NULL, ...); + + + + + + + /* Get the definition of INT16_MAX, INT16_MIN, UINT64_MAX, INT64_MAX, INT64_MIN, ... */ + +#define __STDC_LIMIT_MACROS /* required to have UINTxx_MAX defined when including stdint.h from C++ source code. */ +#include +#include + +#ifndef UINT64_MAX + #define UINT64_MAX (std::numeric_limits< uint64_t >::max()) +#endif +#ifndef INT64_MAX + #define INT64_MAX (std::numeric_limits< int64_t >::max()) +#endif +#ifndef INT64_MIN + #define INT64_MIN (std::numeric_limits< int64_t >::min()) +#endif + + + +/* Determine, for the current platform, which datas types (float, double or long double) use 64 and 32 bits. */ +/* NOTE: We cant use sizeof() in pre-processor directives, so we have to do it another way... */ +/* CURIOSITY: We can use sizeof() and offsetof() inside static_assert() but: + * - this only allows us to make assertions, and not #define new macros + * - is only available in the C standard [ISO/IEC 9899:2011] and the C++ 0X draft standard [Becker 2008]. It is not available in C99. + * https://www.securecoding.cert.org/confluence/display/seccode/DCL03-C.+Use+a+static+assertion+to+test+the+value+of+a+constant+expression + * struct {int a, b, c, d} header_t; + * e.g.: static_assert(offsetof(struct header_t, c) == 8, "Compile time error message."); + */ + +#include +#if (LDBL_MANT_DIG == 53) /* NOTE: 64 bit IEC559 real has 53 bits for mantissa! */ + #define long_double long double + #define real64_t long_double /* so we can later use #if (real64_t == long_double) directives in the code! */ + #define REAL64_MAX LDBL_MAX +#elif ( DBL_MANT_DIG == 53) /* NOTE: 64 bit IEC559 real has 53 bits for mantissa! */ + #define real64_t double + #define REAL64_MAX DBL_MAX +#elif ( FLT_MANT_DIG == 53) /* NOTE: 64 bit IEC559 real has 53 bits for mantissa! */ + #define real64_t float + #define REAL64_MAX FLT_MAX +#else + #error Could not find a 64 bit floating point data type on this platform. Aborting... +#endif + + +#if (LDBL_MANT_DIG == 24) /* NOTE: 32 bit IEC559 real has 24 bits for mantissa! */ + #ifndef long_double + #define long_double long double + #endif + #define real32_t long_double /* so we can later use #if (real32_t == long_double) directives in the code! */ + #define REAL32_MAX LDBL_MAX +#elif ( DBL_MANT_DIG == 24) /* NOTE: 32 bit IEC559 real has 24 bits for mantissa! */ + #define real32_t double + #define REAL32_MAX DBL_MAX +#elif ( FLT_MANT_DIG == 24) /* NOTE: 32 bit IEC559 real has 24 bits for mantissa! */ + #define real32_t float + #define REAL32_MAX FLT_MAX +#else + #error Could not find a 32 bit floating point data type on this platform. Aborting... +#endif + + + +#include +#ifndef INFINITY + #error Could not find the macro that defines the value for INFINITY in the current platform. +#endif +#ifndef NAN + #error Could not find the macro that defines the value for NAN in the current platform. +#endif + + + + + +#endif // #ifndef _MAIN_HH \ No newline at end of file diff -r aad38592bdde -r c0bda77b37a0 readme --- a/readme Tue Aug 14 19:40:01 2012 +0200 +++ b/readme Wed Aug 22 16:46:17 2012 +0200 @@ -8,7 +8,64 @@ FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) - Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) + Copyright (C) 2003-2012 Mario de Sousa (msousa@fe.up.pt) + + +**************************************************************** +**************************************************************** +**************************************************************** +********* ********* +********* ********* +********* O V E R A L L G O A L S ********* +********* ********* +********* ********* +**************************************************************** +**************************************************************** +**************************************************************** + + + + This project has the goal of producing an open source compiler for the programming languages defined +in the IEC 61131-3 standard. These programming languages are mostly used in the industrial automation +domain, to program PLCs (Programmable Logic Controllers). + + This standard defines 5 programming languages: + - IL : Instructtion List + A textual programming language, somewhat similar to assembly. + - ST : Structured Text + A textual programming language, somewhat similar to Pascal. + - FBD: Function Block Diagram + A graphical programming language, somewhat similar to an electrical circuit diagram based on small + scale integration ICs (Integrated Circuits) (counters, AND/OR/XOR/... logic gates, timers, ...). + - LD : Ladder Diagram + A graphical programming language, somewhat similar to an electrical circuit diagram based on + relays (used for basic cabled logic controllers). + - SFC: Sequential Function Chart + A graphical programming language, that defines a state machine, based largely on Grafcet. + (may also be expressed in textual format). + + Of the above 5 languages, the standard defines textual representations for IL, ST and SFC. +It is these 3 languages that we target, and we currently support all three, as long as they are +expressed in the textual format as defined in the standard. + + Currently the matiec project generates two compilers (more correctly, code translaters, but we like +to call them compilers :-O ): iec2c, and iec2iec + + Both compilers accept the same input: a text file with ST, IL and/or SFC code. + + The iec2c compiler generates ANSI C code which is equivalent to the IEC 61131-3 code expressed in the input file. + + The iec2iec compiler generates IEC61131-3 code which is equivalent to the IEC 61131-3 code expressed in the input file. +This last compiler should generate and output file which should be almost identical to the input file (some formating +may change, as well as the case of letters, etc.). This 'compiler' is mostly used by the matiec project contributors +to help debug the lexical and syntax portions of the compilers. + + + + To compile/build these compilers, just +$./configure; make + + @@ -25,26 +82,111 @@ **************************************************************** The compiler works in 4(+1) stages: - Stage 1 - Lexical analyser - implemented with flex (iec.flex) - Stage 2 - Syntax parser - implemented with bison (iec.y) - Stage 3 - Semantics analyser - currently in its early stages - Stage 4 - Code generator - implemented in C++ - Stage 4+1 - Binary code generator - gcc, javac, etc... + ================================== + Stage 1 - Lexical analyser - implemented with flex (stage1_2/iec_flex.ll) + Stage 2 - Syntax parser - implemented with bison (stage1_2/iec_bison.yy) + Stage pre3 - Populate symbol tables - Symbol tables that will ease searching for symbols in the abstract symbol tree. + Stage 3 - Semantics analyser - currently does type checking only + Stage 4 - Code generator - generates ANSI C code + + Stage 5 - Binary code generator - gcc, javac, etc... (Not integrated into matiec compiler. Must be called explicitly by the user.) + Data structures passed between stages, in global variables: - 1->2 : tokens (int), and token values (char *) - 2->1 : symbol tables (defined in symtable.hh) - 2->3 : abstract syntax tree (tree of C++ classes, in absyntax.hh file) - 3->4 : Same as 2->3 - 4->4+1 : file with program in c, java, etc... + ========================================================== + 1->2 : tokens (int), and token values (char *) (defined in stage1_2/stage1_2_priv.hh) + 2->1 : symbol tables (implemented in util/symtable.[hh|cc], and defined in stage1_2/stage1_2_priv.hh) + 2->3 : abstract syntax tree (tree of C++ objects, whose classes are defined in absyntax/absyntax.hh) +pre3->3,4 : global symbol tables (defined in util/[d]symtable.[hh|cc] and declared in absyntax_utils/absyntax_utils.hh) + 3->4 : abstract syntax tree (same as 2->3), but now annotated (i.e. some extra data inserted into the absyntax tree) + + 4->5 : file with program in c, java, etc... + + The compiler works in several passes: - Pass 1: executes stages 1 and 2 simultaneously - Pass 2: executes stage 3 - Pass 3: executes stage 4 - Pass 4: executes stage 4+1 + ==================================== + +Stage 1 and Stage 2 +------------------- + Executed in one single pass. This pass will: + - Do lexical analysis + - Do syntax analysis + - Execute the absyntax_utils/add_en_eno_param_decl_c visitor class + This class will add the EN and ENO parameter declarations to all + functions that do not have them already explicitly declared by the user. + This will let us handle these parameters in the remaining compiler just as if + they were standard input/output parameters. + + +Stage Pre3 +---------- + Executed in one single pass. This pass will populate the following symbol tables: + - function_symtable; /* A symbol table with all globally declared functions POUs. */ + - function_block_type_symtable; /* A symbol table with all globally declared functions block POUs. */ + - program_type_symtable; /* A symbol table with all globally declared program POUs. */ + - type_symtable; /* A symbol table with all user declared (non elementary) datat type definitions. */ + - enumerated_value_symtable; /* A symbol table with all identifiers (values) declared for enumerated types. */ + + +Stage 3 +------- + Executes two algorithms (flow control analysis, and data type analysis) in several passes. + + Flow control: + Pass 1: Does flow control analysis (for now only of IL code) + Implemented in -> stage3/flow_control_analysis_c + This will anotate the abstract syntax tree + (Every object of the class il_instruction_c that is in the abstract syntax tree will have the variable 'prev_il_instruction' correctly filled in.) + + Data Type Analysis + Pass 1: Analyses the possible data types each expression/literal/IL instruction/etc. may take + Implemented in -> stage3/fill_candidate_datatypes_c + This will anotate the abstract syntax tree + (Every object of in the abstract syntax tree that may have a data type, will have the variable 'candidate_datatypes' correctly filled in. + Additionally, objects in the abstract syntax tree that represen function invocations will have the variable + 'candidate_functions' correctly filled in.) +Pass 2: Narrows all the possible data types each expression/literal/IL instruction/etc. may take down to a single data type + Implemented in -> stage3/narrow_candidate_datatypes_c + This will anotate the abstract syntax tree + (Every object of in the abstract syntax tree that may have a data type, will have the variable 'datatype' correctly filled in. + Additionally, objects in the abstract syntax tree that represen function invocations will have the variables + 'called_function_declaration' and 'extensible_param_count' correctly filled in. + Additionally, objects in the abstract syntax tree that represen function block (FB) invocations will have the variable + 'called_fb_declaration' correctly filled in.) + Pass 2: Prints error messages in the event of the IEC 61131-3 source code being analysed contains semantic data type incompatibility errors. + Implemented in -> stage3/print_datatype_errors_c + + +Stage 4 +------- + Has 2 possible implementations. + + iec2c : Generates C source code in a single pass (stage4/generate_c). + iec2iec: Generates IEC61131 source code in a single pass (stage4/generate_iec). + + + + + + +**************************************************************** +**************************************************************** +**************************************************************** +********* ********* +********* ********* +********* N O T E S ********* +********* ********* +********* ********* +**************************************************************** +**************************************************************** +**************************************************************** + + + + NOTE 1 @@ -388,4 +530,4 @@ ************************************************************************** - Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) + Copyright (C) 2003-2012 Mario de Sousa (msousa@fe.up.pt) diff -r aad38592bdde -r c0bda77b37a0 stage1_2/iec_bison.yy --- a/stage1_2/iec_bison.yy Tue Aug 14 19:40:01 2012 +0200 +++ b/stage1_2/iec_bison.yy Wed Aug 22 16:46:17 2012 +0200 @@ -58,6 +58,17 @@ /**********************************************************************/ /**********************************************************************/ +/* NOTE: the following file contains many rules used merely for detecting errors in + * the IEC source code being parsed. + * To remove all these rules, simply execute the command (first replace all '%' with '/'): + * $sed '\:%\* ERROR_CHECK_BEGIN \*%:,\:%\* ERROR_CHECK_END \*%: d' iec_bison.yy + * + * The above command had to be edited ('/' replaced by '%') so as not to include the C syntax that closes + * comments inside this comment! + * If you place the command in a shell script, be sure to remove the backslashes '\' before each asterisk '*' !! + */ + + @@ -150,10 +161,7 @@ while (0) -/* 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); +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. @@ -250,17 +258,9 @@ * declared twice. * We therefore use the #if !defined YYLTYPE ... * to make sure only the first declaration is parsed by the C++ compiler. - * - * At first glance it seems that what we really should do is delcare the - * YYLTYPE directly as an anonymous struct, thus: - * #define YYLTYPE struct{ ...} - * however, this also results in compilation errors. - * - * I (Mario) think this is kind of a hack. If you know how to - * do this re-declaration of YYLTYPE properly, please let me know! */ #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED - typedef struct { +typedef struct YYLTYPE { int first_line; int first_column; const char *first_file; @@ -269,9 +269,11 @@ int last_column; const char *last_file; long int last_order; - } yyltype__local; - #define YYLTYPE yyltype__local +} YYLTYPE; +#define YYLTYPE_IS_DECLARED 1 +#define YYLTYPE_IS_TRIVIAL 1 #endif + } @@ -496,17 +498,6 @@ %type seconds %type milliseconds -%type integer_d -%type integer_h -%type integer_m -%type integer_s -%type integer_ms -%type fixed_point_d -%type fixed_point_h -%type fixed_point_m -%type fixed_point_s -%type fixed_point_ms - %token fixed_point_token %token fixed_point_d_token %token integer_d_token @@ -518,7 +509,8 @@ %token integer_s_token %token fixed_point_ms_token %token integer_ms_token - +%token end_interval_token +%token erroneous_interval_token // %token TIME %token T_SHARP @@ -1915,13 +1907,13 @@ integer_literal: integer_type_name '#' signed_integer - {$$ = new integer_literal_c($1, $3, locf(@1), locl(@3));} + {$$ = new integer_literal_c($1, $3, locloc(@$));} | integer_type_name '#' binary_integer - {$$ = new integer_literal_c($1, $3, locf(@1), locl(@3));} + {$$ = new integer_literal_c($1, $3, locloc(@$));} | integer_type_name '#' octal_integer - {$$ = new integer_literal_c($1, $3, locf(@1), locl(@3));} + {$$ = new integer_literal_c($1, $3, locloc(@$));} | integer_type_name '#' hex_integer - {$$ = new integer_literal_c($1, $3, locf(@1), locl(@3));} + {$$ = new integer_literal_c($1, $3, locloc(@$));} /* NOTE: see note in the definition of constant for reason * why signed_integer, binary_integer, octal_integer * and hex_integer are missing here! @@ -1957,7 +1949,7 @@ */ /* signed_real */ real_type_name '#' signed_real - {$$ = new real_literal_c($1, $3, locf(@1), locl(@3));} + {$$ = new real_literal_c($1, $3, locloc(@$));} /* ERROR_CHECK_BEGIN */ | real_type_name signed_real {$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between real type name and value in real literal."); yynerrs++;} @@ -1981,13 +1973,13 @@ bit_string_literal: bit_string_type_name '#' integer /* i.e. unsigned_integer */ - {$$ = new bit_string_literal_c($1, $3, locf(@1), locl(@3));} + {$$ = new bit_string_literal_c($1, $3, locloc(@$));} | bit_string_type_name '#' binary_integer - {$$ = new bit_string_literal_c($1, $3, locf(@1), locl(@3));} + {$$ = new bit_string_literal_c($1, $3, locloc(@$));} | bit_string_type_name '#' octal_integer - {$$ = new bit_string_literal_c($1, $3, locf(@1), locl(@3));} + {$$ = new bit_string_literal_c($1, $3, locloc(@$));} | bit_string_type_name '#' hex_integer - {$$ = new bit_string_literal_c($1, $3, locf(@1), locl(@3));} + {$$ = new bit_string_literal_c($1, $3, locloc(@$));} /* NOTE: see note in the definition of constant for reason * why unsigned_integer, binary_integer, octal_integer * and hex_integer are missing here! @@ -2144,156 +2136,56 @@ {$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between 'TIME' and interval in duration."); yynerrs++;} | TIME '-' interval {$$ = NULL; print_err_msg(locl(@1), locf(@2), "'#' missing between 'TIME' and interval in duration."); yynerrs++;} -| TIME '#' error - {$$ = NULL; - if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for duration.");} - else {print_err_msg(locf(@3), locl(@3), "invalid value for duration."); yyclearin;} - yyerrok; - } -| T_SHARP error - {$$ = NULL; - if (is_current_syntax_token()) {print_err_msg(locl(@1), locf(@2), "no value defined for duration.");} - else {print_err_msg(locf(@2), locl(@2), "invalid value for duration."); yyclearin;} - yyerrok; - } -/* ERROR_CHECK_END */ -; +| TIME '#' erroneous_interval_token + {$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid value for duration."); yynerrs++;} +| T_SHARP erroneous_interval_token + {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid value for duration."); yynerrs++;} +| TIME '#' '-' erroneous_interval_token + {$$ = NULL; print_err_msg(locf(@3), locl(@3), "invalid value for duration."); yynerrs++;} +| T_SHARP '-' erroneous_interval_token + {$$ = NULL; print_err_msg(locf(@2), locl(@2), "invalid value for duration."); yynerrs++;} +/* ERROR_CHECK_END */ +; + +fixed_point: + integer +| fixed_point_token {$$ = new fixed_point_c($1, locloc(@$));}; interval: - days -| hours -| minutes -| seconds -| milliseconds -; - -integer_d: integer_d_token {$$ = new integer_c($1, locloc(@$));}; -integer_h: integer_h_token {$$ = new integer_c($1, locloc(@$));}; -integer_m: integer_m_token {$$ = new integer_c($1, locloc(@$));}; -integer_s: integer_s_token {$$ = new integer_c($1, locloc(@$));}; -integer_ms: integer_ms_token {$$ = new integer_c($1, locloc(@$));}; - -fixed_point_d: - fixed_point_d_token - {$$ = new fixed_point_c($1, locloc(@$));} -| integer_d -; - -fixed_point_h: - fixed_point_h_token - {$$ = new fixed_point_c($1, locloc(@$));} -| integer_h -; - -fixed_point_m: - fixed_point_m_token - {$$ = new fixed_point_c($1, locloc(@$));} -| integer_m -; - -fixed_point_s: - fixed_point_s_token - {$$ = new fixed_point_c($1, locloc(@$));} -| integer_s -; - -fixed_point_ms: - fixed_point_ms_token - {$$ = new fixed_point_c($1, locloc(@$));} -| integer_ms -; - - -fixed_point: - fixed_point_token - {$$ = new fixed_point_c($1, locloc(@$));} -| integer -; - - -days: -/* fixed_point ('d') */ - fixed_point_d - {$$ = new days_c($1, NULL, locloc(@$));} -/*| integer ('d') ['_'] hours */ -| integer_d hours - {$$ = new days_c($1, $2, locloc(@$));} -| integer_d '_' hours - {$$ = new days_c($1, $3, locloc(@$));} -/* ERROR_CHECK_BEGIN */ -| integer_d '_' error - {$$ = NULL; - if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for hours in duration.");} - else {print_err_msg(locf(@3), locl(@3), "invalid value for hours in duration."); yyclearin;} - yyerrok; - } -/* ERROR_CHECK_END */ -; - - -hours: -/* fixed_point ('h') */ - fixed_point_h - {$$ = new hours_c($1, NULL, locloc(@$));} -/*| integer ('h') ['_'] minutes */ -| integer_h minutes - {$$ = new hours_c($1, $2, locloc(@$));} -| integer_h '_' minutes - {$$ = new hours_c($1, $3, locloc(@$));} -/* ERROR_CHECK_BEGIN */ -| integer_h '_' error - {$$ = NULL; - if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for minutes in duration.");} - else {print_err_msg(locf(@3), locl(@3), "invalid value for minutes in duration."); yyclearin;} - yyerrok; - } -/* ERROR_CHECK_END */ - -; - -minutes: -/* fixed_point ('m') */ - fixed_point_m - {$$ = new minutes_c($1, NULL, locloc(@$));} -/*| integer ('m') ['_'] seconds */ -| integer_m seconds - {$$ = new minutes_c($1, $2, locloc(@$));} -| integer_m '_' seconds - {$$ = new minutes_c($1, $3, locloc(@$));} -/* ERROR_CHECK_BEGIN */ -| integer_m '_' error - {$$ = NULL; - if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for seconds in duration.");} - else {print_err_msg(locf(@3), locl(@3), "invalid value for seconds in duration."); yyclearin;} - yyerrok; - } -/* ERROR_CHECK_END */ -; - -seconds: -/* fixed_point ('s') */ - fixed_point_s - {$$ = new seconds_c($1, NULL, locloc(@$));} -/*| integer ('s') ['_'] milliseconds */ -| integer_s milliseconds - {$$ = new seconds_c($1, $2, locloc(@$));} -| integer_s '_' milliseconds - {$$ = new seconds_c($1, $3, locloc(@$));} -/* ERROR_CHECK_BEGIN */ -| integer_s '_' error - {$$ = NULL; - if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no value defined for milliseconds in duration.");} - else {print_err_msg(locf(@3), locl(@3), "invalid value for milliseconds in duration."); yyclearin;} - yyerrok; - } -/* ERROR_CHECK_END */ -; - -milliseconds: -/* fixed_point ('ms') */ - fixed_point_ms - {$$ = new milliseconds_c($1, locloc(@$));} + days hours minutes seconds milliseconds end_interval_token + {$$ = new interval_c($1, $2, $3, $4, $5, locloc(@$));}; +; + + +days: /* fixed_point ('d') */ + /* empty */ {$$ = NULL;} +| fixed_point_d_token {$$ = new fixed_point_c($1, locloc(@$));}; +| integer_d_token {$$ = new integer_c($1, locloc(@$));}; +; + +hours: /* fixed_point ('h') */ + /* empty */ {$$ = NULL;} +| fixed_point_h_token {$$ = new fixed_point_c($1, locloc(@$));}; +| integer_h_token {$$ = new integer_c($1, locloc(@$));}; +; + +minutes: /* fixed_point ('m') */ + /* empty */ {$$ = NULL;} +| fixed_point_m_token {$$ = new fixed_point_c($1, locloc(@$));}; +| integer_m_token {$$ = new integer_c($1, locloc(@$));}; +; + +seconds: /* fixed_point ('s') */ + /* empty */ {$$ = NULL;} +| fixed_point_s_token {$$ = new fixed_point_c($1, locloc(@$));}; +| integer_s_token {$$ = new integer_c($1, locloc(@$));}; +; + +milliseconds: /* fixed_point ('ms') */ + /* empty */ {$$ = NULL;} +| fixed_point_ms_token {$$ = new fixed_point_c($1, locloc(@$));}; +| integer_ms_token {$$ = new integer_c($1, locloc(@$));}; ; @@ -2991,13 +2883,18 @@ {$$ = new array_initial_elements_list_c(locloc(@$)); $$->add_element($1);} | array_initial_elements_list ',' array_initial_elements {$$ = $1; $$->add_element($3);} -/* ERROR_CHECK_BEGIN +/* ERROR_CHECK_BEGIN */ +/* The following error checking rules have been commented out. Why? Was it a typo? + * Lets keep them commented out for now... + */ +/* | array_initial_elements_list ',' error {$$ = $1; if (is_current_syntax_token()) {print_err_msg(locl(@2), locf(@3), "no array initial value in array initial values list.");} else {print_err_msg(locf(@3), locl(@3), "invalid array initial value in array initial values list."); yyclearin;} yyerrok; } +*/ /* ERROR_CHECK_END */ ; @@ -3151,7 +3048,11 @@ {$$ = new structure_element_initialization_list_c(locloc(@$)); $$->add_element($1);} | structure_element_initialization_list ',' structure_element_initialization {$$ = $1; $$->add_element($3);} -/* ERROR_CHECK_BEGIN +/* ERROR_CHECK_BEGIN */ +/* The following error checking rules have been commented out. Why? Was it a typo? + * Lets keep them commented out for now... + */ +/* | structure_element_initialization_list structure_element_initialization {$$ = $1; print_err_msg(locl(@1), locf(@2), "',' missing in structure element initialization list in structure initialization."); yynerrs++;} | structure_element_initialization_list ',' error @@ -3160,6 +3061,7 @@ else {print_err_msg(locf(@3), locl(@3), "invalid structure element initialization in structure initialization."); yyclearin;} yyerrok; } +*/ /* ERROR_CHECK_END */ ; @@ -3407,7 +3309,7 @@ record_variable '.' field_selector {$$ = new structured_variable_c($1, $3, locloc(@$));} | record_variable '.' il_simple_operator_clash3 - {$$ = new structured_variable_c($1, $3, locloc(@$));} + {$$ = new structured_variable_c($1, il_operator_c_2_identifier_c($3), locloc(@$));} ; @@ -5237,7 +5139,9 @@ initial_step: INITIAL_STEP step_name ':' action_association_list END_STEP // INITIAL_STEP identifier ':' action_association_list END_STEP - {$$ = new initial_step_c($2, $4, locloc(@$));} + {$$ = new initial_step_c($2, $4, locloc(@$)); + variable_name_symtable.insert($2, prev_declared_variable_name_token); // A step name may later be used as a structured variable!! + } /* ERROR_CHECK_BEGIN */ | INITIAL_STEP ':' action_association_list END_STEP {$$ = NULL; print_err_msg(locf(@1), locl(@2), "no step name defined in initial step declaration."); yynerrs++;} @@ -5257,7 +5161,9 @@ step: STEP step_name ':' action_association_list END_STEP // STEP identifier ':' action_association_list END_STEP - {$$ = new step_c($2, $4, locloc(@$));} + {$$ = new step_c($2, $4, locloc(@$)); + variable_name_symtable.insert($2, prev_declared_variable_name_token); // A step name may later be used as a structured variable!! + } /* ERROR_CHECK_BEGIN */ | STEP ':' action_association_list END_STEP {$$ = NULL; print_err_msg(locl(@1), locf(@2), "no step name defined in step declaration."); yynerrs++;} @@ -5359,9 +5265,58 @@ | SL {$$ = new timed_qualifier_c(strdup("SL"), locloc(@$));} ; +/* NOTE: A step_name may be used as a structured vaqriable, in order to access the status bit (e.g. Step1.X) + * or the time it has been active (e.g. Step1.T). + * In order to allow the step name to be used as a variable inside ST expressions (only ST expressions ??) + * when defining transitions, we need to add the step_name to the list of previously declared variables. + * This allows the step name to be used as a variable inside all transition expressions, as the user + * can clearly define the transition _after_ the step itself has been defined/declared, so the + * 'variable' is previously 'declared'. + * + * However, when defining/declaring a step, a variable name can also be used to define a timed + * action association. In this case, we may have a circular reference: + * e.g. + * ... + * STEP step1: + * action1 (D,t#100ms); + * end_step + * + * STEP step2: + * action1 (D,step3.T); <---- forward reference to step3.T !!!!!! + * end_step + * + * STEP step3: + * action1 (D,step2.T); <---- back reference to step2.T + * end_step + * + * + * There is no way the user can always use the step3.T variable only after it has + * been 'declared'. So adding the steps to the list of previously declared variables + * when the steps are declared is not a solution to the above situation. + * + * Fortunately, the standard does not allow ST expressions in the above syntax + * (i.e. when defining the delay of a timed actions), but only either a + * Time literal, or a variable. + * This is why we change the definition of action_time from + * action_time: + * duration + * | variable + * ; + * + * to: + * action_time: + * duration + * | any_symbolic_variable + * ; + * + * NOTE that this same problem does not occur with the 'indicator_name': it does not + * make sense to set/indicate a step1.X variable, as these variables are read-only! + */ + action_time: duration -| variable +//| variable + | any_symbolic_variable ; indicator_name: variable; @@ -5460,12 +5415,15 @@ {$$ = NULL;} | '(' {cmd_goto_sfc_priority_state();} PRIORITY {cmd_pop_state();} ASSIGN integer ')' {$$ = $6;} -/* ERROR_CHECK_BEGIN +/* ERROR_CHECK_BEGIN */ +/* The following error checking rules have been intentionally commented out. */ +/* | '(' ASSIGN integer ')' {$$ = NULL; print_err_msg(locl(@1), locf(@2), "'PRIORITY' missing between '(' and ':=' in transition declaration with priority."); yynerrs++;} | '(' error ASSIGN integer ')' {$$ = NULL; print_err_msg(locf(@2), locl(@2), "expecting 'PRIORITY' between '(' and ':=' in transition declaration with priority."); yyerrok;} - ERROR_CHECK_END */ +*/ +/* ERROR_CHECK_END */ ; @@ -6293,7 +6251,7 @@ il_instruction {$$ = new instruction_list_c(locloc(@$)); $$->add_element($1);} | any_pragma eol_list - {$$ = new instruction_list_c(locloc(@$)); $$->add_element($1);} + {$$ = new instruction_list_c(locloc(@1)); $$->add_element($1);} /* locloc(@1) is not a bug! We ignore trailing EOLs when determining symbol location! */ | instruction_list il_instruction {$$ = $1; $$->add_element($2);} | instruction_list any_pragma @@ -6304,11 +6262,11 @@ il_instruction: il_incomplete_instruction eol_list - {$$ = new il_instruction_c(NULL, $1, locloc(@$));} + {$$ = new il_instruction_c(NULL, $1, locloc(@1));} /* locloc(@1) is not a bug! We ignore trailing EOLs when determining symbol location! */ | label ':' il_incomplete_instruction eol_list - {$$ = new il_instruction_c($1, $3, locloc(@$));} + {$$ = new il_instruction_c($1, $3, locf(@1), locl(@3));} /* locf(@1), locl(@3) is not a bug! We ignore trailing EOLs when determining symbol location! */ | label ':' eol_list - {$$ = new il_instruction_c($1, NULL, locloc(@$));} + {$$ = new il_instruction_c($1, NULL, locf(@1), locl(@2));} /* locf(@1), locl(@2) is not a bug! We ignore trailing EOLs when determining symbol location! */ /* ERROR_CHECK_BEGIN */ | error eol_list {$$ = NULL; print_err_msg(locf(@1), locl(@1), "invalid IL instruction."); yyerrok;} @@ -6643,8 +6601,11 @@ il_simple_instruction: il_simple_operation eol_list + {$$ = new il_simple_instruction_c($1, locloc(@1));} /* locloc(@1) is not a bug! We ignore trailing EOLs when determining symbol location! */ | il_expression eol_list + {$$ = new il_simple_instruction_c($1, locloc(@1));} /* locloc(@1) is not a bug! We ignore trailing EOLs when determining symbol location! */ | il_formal_funct_call eol_list + {$$ = new il_simple_instruction_c($1, locloc(@1));} /* locloc(@1) is not a bug! We ignore trailing EOLs when determining symbol location! */ /* ERROR_CHECK_BEGIN */ | il_expression error {$$ = NULL; print_err_msg(locl(@1), locf(@2), "EOL missing after expression IL instruction."); yyerrok;} @@ -8062,11 +8023,11 @@ if (full_token_loc) { if (first_filename == last_filename) - fprintf(stderr, "%s:%d-%d..%d-%d: error : %s\n", first_filename, first_line, first_column, last_line, last_column, additional_error_msg); + fprintf(stderr, "%s:%d-%d..%d-%d: error: %s\n", first_filename, first_line, first_column, last_line, last_column, additional_error_msg); else - fprintf(stderr, "%s:%d-%d..%s:%d-%d: error : %s\n", first_filename, first_line, first_column, last_filename, last_line, last_column, additional_error_msg); + fprintf(stderr, "%s:%d-%d..%s:%d-%d: error: %s\n", first_filename, first_line, first_column, last_filename, last_line, last_column, additional_error_msg); } else { - fprintf(stderr, "%s:%d: error : %s\n", first_filename, first_line, additional_error_msg); + fprintf(stderr, "%s:%d: error: %s\n", first_filename, first_line, additional_error_msg); } //fprintf(stderr, "error %d: %s\n", yynerrs /* a global variable */, additional_error_msg); print_include_stack(); @@ -8253,6 +8214,7 @@ } /* first parse the standard library file... */ + /* Do not debug the standard library, even if debug flag is set! */ /* #if YYDEBUG yydebug = 1; @@ -8268,7 +8230,7 @@ ERROR; if (yynerrs > 0) { - fprintf (stderr, "\nFound %d error(s) in %s. Bailing out!\n", yynerrs /* global variable */, libfilename); + fprintf (stderr, "\n%d error(s) found in %s. Bailing out!\n", yynerrs /* global variable */, libfilename); ERROR; } free(libfilename); @@ -8301,7 +8263,7 @@ } if (yynerrs > 0) { - fprintf (stderr, "\nFound %d error(s). Bailing out!\n", yynerrs /* global variable */); + fprintf (stderr, "\n%d error(s) found. Bailing out!\n", yynerrs /* global variable */); exit(EXIT_FAILURE); } diff -r aad38592bdde -r c0bda77b37a0 stage1_2/iec_flex.ll --- a/stage1_2/iec_flex.ll Tue Aug 14 19:40:01 2012 +0200 +++ b/stage1_2/iec_flex.ll Wed Aug 22 16:46:17 2012 +0200 @@ -91,8 +91,10 @@ */ %option noyy_top_state -/* We will not be using unput() in our flex code... */ +/* We will be using unput() in our flex code, so we cannot set the following option!... */ +/* %option nounput +*/ /**************************************************/ /* External Variable and Function declarations... */ @@ -171,8 +173,9 @@ * track of the locations, in order to give * more meaningful error messages! */ -extern YYLTYPE yylloc; - +/* + *extern YYLTYPE yylloc; +b*/ #define YY_INPUT(buf,result,max_size) {\ result = GetNextChar(buf, max_size);\ if ( result <= 0 )\ @@ -208,7 +211,6 @@ current_order++; \ } - /* Since this lexical parser we defined only works in ASCII based * systems, we might as well make sure it is being compiled on * one... @@ -241,6 +243,10 @@ %{ /* return all the text in the current token back to the input stream. */ void unput_text(unsigned int n); +/* return all the text in the current token back to the input stream, + * but first return to the stream an additional character to mark the end of the token. + */ +void unput_and_mark(const char c); %} @@ -340,6 +346,16 @@ * expecting any action qualifiers, flex does not return these tokens, and is free * to interpret them as previously defined variables/functions/... as the case may be. * + * The time_literal_state is required because TIME# literals are decomposed into + * portions, and wewant to send these portions one by one to bison. Each poertion will + * represent the value in days/hours/minutes/seconds/ms. + * Unfortunately, some of these portions may also be lexically analysed as an identifier. So, + * we need to disable lexical identification of identifiers while parsing TIME# literals! + * e.g.: TIME#55d_4h_56m + * We would like to return to bison the tokens 'TIME' '#' '55d' '_' '4h' '_' '56m' + * Unfortunately, flex will join '_' and '4h' to create a legal {identifier} '_4h', + * and return that identifier instead! So, we added this state! + * * The state machine has 7 possible states (INITIAL, config, decl, body, st, il, sfc) * Possible state changes are: * INITIAL -> goto(decl_state) @@ -418,7 +434,8 @@ /* we are parsing sfc code, and expecting the priority token. */ %s sfc_priority_state - +/* we are parsing a TIME# literal. We must not return any {identifier} tokens. */ +%x time_literal_state /*******************/ @@ -598,6 +615,15 @@ /* B.1.2.1 Numeric literals */ /******************************/ integer {digit}((_?{digit})*) + +/* Some helper symbols for parsing TIME literals... */ +integer_0_59 (0(_?))*([0-5](_?))?{digit} +integer_0_19 (0(_?))*([0-1](_?))?{digit} +integer_20_23 (0(_?))*2(_?)[0-3] +integer_0_23 {integer_0_19}|{integer_20_23} +integer_0_999 {digit}((_?{digit})?)((_?{digit})?) + + binary_integer 2#{bit}((_?{bit})*) bit [0-1] octal_integer 8#{octal_digit}((_?{octal_digit})*) @@ -673,21 +699,54 @@ /************************/ fixed_point {integer}\.{integer} -fixed_point_d {fixed_point}d -integer_d {integer}d - -fixed_point_h {fixed_point}h -integer_h {integer}h - -fixed_point_m {fixed_point}m -integer_m {integer}m - -fixed_point_s {fixed_point}s -integer_s {integer}s - -fixed_point_ms {fixed_point}ms -integer_ms {integer}ms - + +/* NOTE: The IEC 61131-3 v2 standard has an incorrect formal syntax definition of duration, + * as its definition does not match the standard's text. + * IEC 61131-3 v3 (committee draft) seems to have this fixed, so we use that + * definition instead! + * + * duration::= ('T' | 'TIME') '#' ['+'|'-'] interval + * interval::= days | hours | minutes | seconds | milliseconds + * fixed_point ::= integer [ '.' integer] + * days ::= fixed_point 'd' | integer 'd' ['_'] [ hours ] + * hours ::= fixed_point 'h' | integer 'h' ['_'] [ minutes ] + * minutes ::= fixed_point 'm' | integer 'm' ['_'] [ seconds ] + * seconds ::= fixed_point 's' | integer 's' ['_'] [ milliseconds ] + * milliseconds ::= fixed_point 'ms' + * + * + * The original IEC 61131-3 v2 definition is: + * duration ::= ('T' | 'TIME') '#' ['-'] interval + * interval ::= days | hours | minutes | seconds | milliseconds + * fixed_point ::= integer [ '.' integer] + * days ::= fixed_point 'd' | integer 'd' ['_'] hours + * hours ::= fixed_point 'h' | integer 'h' ['_'] minutes + * minutes ::= fixed_point 'm' | integer 'm' ['_'] seconds + * seconds ::= fixed_point 's' | integer 's' ['_'] milliseconds + * milliseconds ::= fixed_point 'ms' + + */ + +interval_ms_X ({integer_0_999}(\.{integer})?)ms +interval_s_X {integer_0_59}s(_?{interval_ms_X})? +interval_m_X {integer_0_59}m(_?{interval_s_X})? +interval_h_X {integer_0_23}h(_?{interval_m_X})? + +interval_ms {integer}ms|({fixed_point}ms) +interval_s {integer}s(_?{interval_ms_X})?|({fixed_point}s) +interval_m {integer}m(_?{interval_s_X})?|({fixed_point}m) +interval_h {integer}h(_?{interval_m_X})?|({fixed_point}h) +interval_d {integer}d(_?{interval_h_X})?|({fixed_point}d) + +interval {interval_ms}|{interval_s}|{interval_m}|{interval_h}|{interval_d} + +/* to help provide nice error messages, we also parse an incorrect but plausible interval... */ +/* NOTE that this erroneous interval will be parsed outside the time_literal_state, so must not + * be able to parse any other legal lexcial construct (besides a legal interval, but that + * is OK as this rule will appear _after_ the rule to parse legal intervals!). + */ +fixed_point_or_integer {fixed_point}|{integer} +erroneous_interval ({fixed_point_or_integer}d_?)?({fixed_point_or_integer}h_?)?({fixed_point_or_integer}m_?)?({fixed_point_or_integer}s_?)?({fixed_point_or_integer}ms)? /********************************************/ /* B.1.4.1 Directly Represented Variables */ @@ -719,9 +778,9 @@ * in which case we are currently using "%I3" as the variable * name. */ -direct_variable_matplc %{identifier} - -direct_variable {direct_variable_standard}|{direct_variable_matplc} +/* direct_variable_matplc %{identifier} */ +/* direct_variable {direct_variable_standard}|{direct_variable_matplc} */ +direct_variable {direct_variable_standard} /******************************************/ /* B 1.4.3 - Declaration & Initialisation */ @@ -1541,23 +1600,26 @@ /* B 1.2.3.1 - Duration */ /************************/ {fixed_point} {yylval.ID=strdup(yytext); return fixed_point_token;} - -{fixed_point_d} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_d_token;} -{integer_d} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_d_token;} - -{fixed_point_h} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_h_token;} -{integer_h} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_h_token;} - -{fixed_point_m} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_m_token;} -{integer_m} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_m_token;} - -{fixed_point_s} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_s_token;} -{integer_s} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_s_token;} - -{fixed_point_ms} {yylval.ID=strdup(yytext); yylval.ID[yyleng-2] = '\0'; return fixed_point_ms_token;} -{integer_ms} {yylval.ID=strdup(yytext); yylval.ID[yyleng-2] = '\0'; return integer_ms_token;} - - +{interval} {/*fprintf(stderr, "entering time_literal_state ##%s##\n", yytext);*/ unput_and_mark('#'); yy_push_state(time_literal_state);} +{erroneous_interval} {return erroneous_interval_token;} + +{ +{integer}d {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_d_token;} +{integer}h {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_h_token;} +{integer}m {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_m_token;} +{integer}s {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_s_token;} +{integer}ms {yylval.ID=strdup(yytext); yylval.ID[yyleng-2] = '\0'; return integer_ms_token;} +{fixed_point}d {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_d_token;} +{fixed_point}h {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_h_token;} +{fixed_point}m {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_m_token;} +{fixed_point}s {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_s_token;} +{fixed_point}ms {yylval.ID=strdup(yytext); yylval.ID[yyleng-2] = '\0'; return fixed_point_ms_token;} + +_ /* do nothing - eat it up!*/ +\# {/*fprintf(stderr, "popping from time_literal_state (###)\n");*/ yy_pop_state(); return end_interval_token;} +. {/*fprintf(stderr, "time_literal_state: found invalid character '%s'. Aborting!\n", yytext);*/ ERROR;} +\n {ERROR;} +} /*******************************/ /* B.1.2.2 Character Strings */ /*******************************/ @@ -1645,6 +1707,20 @@ } +/* return all the text in the current token back to the input stream, + * but first return to the stream an additional character to mark the end of the token. + */ +void unput_and_mark(const char c) { + char *yycopy = strdup( yytext ); /* unput() destroys yytext, so we copy it first */ + unput(c); + for (int i = yyleng-1; i >= 0; i--) + unput(yycopy[i]); + + free(yycopy); +} + + + /* Called by flex when it reaches the end-of-file */ int yywrap(void) { diff -r aad38592bdde -r c0bda77b37a0 stage3/Makefile.am --- a/stage3/Makefile.am Tue Aug 14 19:40:01 2012 +0200 +++ b/stage3/Makefile.am Wed Aug 22 16:46:17 2012 +0200 @@ -4,5 +4,12 @@ libstage3_a_SOURCES = \ stage3.cc \ - visit_expression_type.cc + flow_control_analysis.cc \ + fill_candidate_datatypes.cc \ + narrow_candidate_datatypes.cc \ + print_datatypes_error.cc \ + datatype_functions.cc \ + lvalue_check.cc \ + array_range_check.cc \ + constant_folding.cc diff -r aad38592bdde -r c0bda77b37a0 stage3/Makefile.in --- a/stage3/Makefile.in Tue Aug 14 19:40:01 2012 +0200 +++ b/stage3/Makefile.in Wed Aug 22 16:46:17 2012 +0200 @@ -33,7 +33,7 @@ PRE_UNINSTALL = : POST_UNINSTALL = : DIST_COMMON = $(srcdir)/../common.mk $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in + $(srcdir)/Makefile.in TODO subdir = stage3 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac @@ -71,7 +71,12 @@ libstage3_a_AR = $(AR) $(ARFLAGS) libstage3_a_LIBADD = am_libstage3_a_OBJECTS = stage3.$(OBJEXT) \ - visit_expression_type.$(OBJEXT) + flow_control_analysis.$(OBJEXT) \ + fill_candidate_datatypes.$(OBJEXT) \ + narrow_candidate_datatypes.$(OBJEXT) \ + print_datatypes_error.$(OBJEXT) datatype_functions.$(OBJEXT) \ + lvalue_check.$(OBJEXT) array_range_check.$(OBJEXT) \ + constant_folding.$(OBJEXT) libstage3_a_OBJECTS = $(am_libstage3_a_OBJECTS) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/config depcomp = $(SHELL) $(top_srcdir)/config/depcomp @@ -188,7 +193,14 @@ lib_LIBRARIES = libstage3.a libstage3_a_SOURCES = \ stage3.cc \ - visit_expression_type.cc + flow_control_analysis.cc \ + fill_candidate_datatypes.cc \ + narrow_candidate_datatypes.cc \ + print_datatypes_error.cc \ + datatype_functions.cc \ + lvalue_check.cc \ + array_range_check.cc \ + constant_folding.cc all: all-am @@ -267,8 +279,15 @@ distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array_range_check.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/constant_folding.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/datatype_functions.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fill_candidate_datatypes.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flow_control_analysis.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lvalue_check.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/narrow_candidate_datatypes.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print_datatypes_error.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stage3.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/visit_expression_type.Po@am__quote@ .cc.o: @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff -r aad38592bdde -r c0bda77b37a0 stage3/TODO --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/TODO Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,13 @@ + + + Things that we must still check for in stage 3: + + +1) Handling of CONSTANTs: + + 1.a) "Any program organization unit attempts to modify the value of a variable that has been declared with the CONSTANT qualifier;" + 1.b) From table 16.a "The CONSTANT qualifier shall not be used in the declaration of function block instances as described in 2.5.2.1." + + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/array_range_check.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/array_range_check.cc Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,322 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (conti.ma@alice.it) + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Array Range Checking: + * - Check whether array subscript values fall within the allowed range. + * Note that for the checking of subscript values to work correctly, we need to have constant folding working too: + * array_var[8 + 99] can not be checked without constant folding. + */ + + +#include "array_range_check.hh" +#include // required for std::numeric_limits + + +#define FIRST_(symbol1, symbol2) (((symbol1)->first_order < (symbol2)->first_order) ? (symbol1) : (symbol2)) +#define LAST_(symbol1, symbol2) (((symbol1)->last_order > (symbol2)->last_order) ? (symbol1) : (symbol2)) + +#define STAGE3_ERROR(error_level, symbol1, symbol2, ...) { \ + if (current_display_error_level >= error_level) { \ + fprintf(stderr, "%s:%d-%d..%d-%d: error: ", \ + FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ + LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + error_count++; \ + } \ +} + + +#define STAGE3_WARNING(symbol1, symbol2, ...) { \ + fprintf(stderr, "%s:%d-%d..%d-%d: warning: ", \ + FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ + LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + warning_found = true; \ +} + +#define GET_CVALUE(dtype, symbol) ((symbol)->const_value._##dtype.value) +#define VALID_CVALUE(dtype, symbol) (symbol_c::cs_const_value == (symbol)->const_value._##dtype.status) + +/* The cmp_unsigned_signed function compares two numbers u and s. + * It returns an integer indicating the relationship between the numbers: + * - A zero value indicates that both numbers are equal. + * - A value greater than zero indicates that numbers does not match and + * first has a greater value. + * - A value less than zero indicates that numbers does not match and + * first has a lesser value. + */ +static inline int cmp_unsigned_signed(const uint64_t u, const int64_t s) { + const uint64_t INT64_MAX_uvar = INT64_MAX; + if (u <= INT64_MAX_uvar) + return ((int64_t)u - s); + return -1; +} + +array_range_check_c::array_range_check_c(symbol_c *ignore) { + error_count = 0; + current_display_error_level = 0; +} + + + +array_range_check_c::~array_range_check_c(void) { +} + + + +int array_range_check_c::get_error_count() { + return error_count; +} + + + +void array_range_check_c::check_dimension_count(array_variable_c *symbol) { + int dimension_count; + symbol_c *var_decl; + + var_decl = search_varfb_instance_type->get_basetype_decl(symbol->subscripted_variable); + array_dimension_iterator_c array_dimension_iterator(var_decl); + for (dimension_count = 0; NULL != array_dimension_iterator.next(); dimension_count++); + if (dimension_count != ((list_c *)symbol->subscript_list)->n) + STAGE3_ERROR(0, symbol, symbol, "Number of subscripts/indexes does not match the number of subscripts/indexes in the array's declaration (array has %d indexes)", dimension_count); +} + + + +void array_range_check_c::check_bounds(array_variable_c *symbol) { + list_c *l; /* the subscript_list */ + symbol_c *var_decl; + + l = (list_c *)symbol->subscript_list; + var_decl = search_varfb_instance_type->get_basetype_decl(symbol->subscripted_variable); + array_dimension_iterator_c array_dimension_iterator(var_decl); + for (int i = 0; i < l->n; i++) { + subrange_c *dimension = array_dimension_iterator.next(); + /* mismatch between number of indexes/subscripts. This error will be caught in check_dimension_count() so we ignore it. */ + if (NULL == dimension) + return; + + /* Check lower limit */ + if ( VALID_CVALUE( int64, l->elements[i]) && VALID_CVALUE( int64, dimension->lower_limit)) + if ( GET_CVALUE( int64, l->elements[i]) < GET_CVALUE( int64, dimension->lower_limit) ) + {STAGE3_ERROR(0, symbol, symbol, "Array access out of bounds."); continue;} + + if ( VALID_CVALUE( int64, l->elements[i]) && VALID_CVALUE(uint64, dimension->lower_limit)) + if ( cmp_unsigned_signed( GET_CVALUE(uint64, dimension->lower_limit), GET_CVALUE( int64, l->elements[i])) > 0 ) + {STAGE3_ERROR(0, symbol, symbol, "Array access out of bounds."); continue;} + + if ( VALID_CVALUE(uint64, l->elements[i]) && VALID_CVALUE(uint64, dimension->lower_limit)) + if ( GET_CVALUE(uint64, l->elements[i]) < GET_CVALUE(uint64, dimension->lower_limit)) + {STAGE3_ERROR(0, symbol, symbol, "Array access out of bounds."); continue;} + + if ( VALID_CVALUE(uint64, l->elements[i]) && VALID_CVALUE( int64, dimension->lower_limit)) + if ( cmp_unsigned_signed(GET_CVALUE(uint64, l->elements[i]), GET_CVALUE( int64, dimension->lower_limit)) < 0 ) + {STAGE3_ERROR(0, symbol, symbol, "Array access out of bounds."); continue;} + + /* Repeat the same check, now for upper limit */ + if ( VALID_CVALUE( int64, l->elements[i]) && VALID_CVALUE( int64, dimension->upper_limit)) + if ( GET_CVALUE( int64, l->elements[i]) > GET_CVALUE( int64, dimension->upper_limit)) + {STAGE3_ERROR(0, symbol, symbol, "Array access out of bounds."); continue;} + + if ( VALID_CVALUE( int64, l->elements[i]) && VALID_CVALUE(uint64, dimension->upper_limit)) + if ( cmp_unsigned_signed( GET_CVALUE(uint64, dimension->upper_limit), GET_CVALUE( int64, l->elements[i])) < 0 ) + {STAGE3_ERROR(0, symbol, symbol, "Array access out of bounds."); continue;} + + if ( VALID_CVALUE(uint64, l->elements[i]) && VALID_CVALUE(uint64, dimension->upper_limit)) + if ( GET_CVALUE(uint64, l->elements[i]) > GET_CVALUE(uint64, dimension->upper_limit)) + {STAGE3_ERROR(0, symbol, symbol, "Array access out of bounds."); continue;} + + if ( VALID_CVALUE(uint64, l->elements[i]) && VALID_CVALUE( int64, dimension->upper_limit)) + if ( cmp_unsigned_signed(GET_CVALUE(uint64, l->elements[i]), GET_CVALUE( int64, dimension->upper_limit)) > 0 ) + {STAGE3_ERROR(0, symbol, symbol, "Array access out of bounds."); continue;} + + } +} + + + + + + + + + +/*************************/ +/* B.1 - Common elements */ +/*************************/ +/**********************/ +/* B.1.3 - Data types */ +/**********************/ +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ + +/* signed_integer DOTDOT signed_integer */ +/* dimension will be filled in during stage 3 (array_range_check_c) with the number of elements in this subrange */ +// SYM_REF2(subrange_c, lower_limit, upper_limit, unsigned long long int dimension) +void *array_range_check_c::visit(subrange_c *symbol) { + unsigned long long int dimension = 0; // we use unsigned long long instead of uint64_t since it might just happen to be larger than uint64_t in the platform used for compiling this code!! + + /* Determine the size of the array... */ + if (VALID_CVALUE( int64, symbol->upper_limit) && VALID_CVALUE( int64, symbol->lower_limit)) { + // do the sums in such a way that no overflow is possible... even during intermediate steps used by compiler! + // remember that the result (dimension) is unsigned, while the operands are signed!! + // dimension = GET_CVALUE( int64, symbol->upper_limit) - VALID_CVALUE( int64, symbol->lower_limit); + if (GET_CVALUE( int64, symbol->lower_limit) >= 0) { + dimension = GET_CVALUE( int64, symbol->upper_limit) - GET_CVALUE( int64, symbol->lower_limit); + } else { + dimension = -GET_CVALUE( int64, symbol->lower_limit); + dimension += GET_CVALUE( int64, symbol->upper_limit); + } + } else if (VALID_CVALUE(uint64, symbol->upper_limit) && VALID_CVALUE(uint64, symbol->lower_limit)) { + dimension = GET_CVALUE(uint64, symbol->upper_limit) - GET_CVALUE(uint64, symbol->lower_limit); + } else if (VALID_CVALUE(uint64, symbol->upper_limit) && VALID_CVALUE( int64, symbol->lower_limit)) { + if (GET_CVALUE( int64, symbol->lower_limit) >= 0) { + dimension = GET_CVALUE(uint64, symbol->upper_limit) - GET_CVALUE( int64, symbol->lower_limit); + } else { + unsigned long long int lower_ull; + lower_ull = -GET_CVALUE( int64, symbol->lower_limit); + dimension = GET_CVALUE(uint64, symbol->upper_limit) + lower_ull; + if (dimension < lower_ull) + STAGE3_ERROR(0, symbol, symbol, "Number of elements in array subrange exceeds maximum number of elements (%llu).", std::numeric_limits< unsigned long long int >::max()); + } + } else ERROR; + + /* correct value for dimension is actually ---> dimension = upper_limit - lower_limit + 1 + * Up to now, we have only determined dimension = upper_limit - lower_limit + * We must first check whether this last increment will cause an overflow! + */ + if (dimension == std::numeric_limits< unsigned long long int >::max()) + STAGE3_ERROR(0, symbol, symbol, "Number of elements in array subrange exceeds maximum number of elements (%llu).", std::numeric_limits< unsigned long long int >::max()); + + /* correct value for dimension is actually ---> dimension = upper_limit - lower_limit + 1 */ + dimension++; + + symbol->dimension = dimension; + return NULL; +} + + + + + +/* integer '(' [array_initial_element] ')' */ +/* array_initial_element may be NULL ! */ +// SYM_REF2(array_initial_elements_c, integer, array_initial_element) +void *array_range_check_c::visit(array_initial_elements_c *symbol) { + if (VALID_CVALUE( int64, symbol->integer) && (GET_CVALUE( int64, symbol->integer) < 0)) + ERROR; /* the IEC 61131-3 syntax guarantees that this value will never be negative! */ + + /* TODO: check that the total number of 'initial values' does not exceed the size of the array! */ + + return NULL; +} + + + + + + + + + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ +/*************************************/ +/* B 1.4.2 - Multi-element variables */ +/*************************************/ +void *array_range_check_c::visit(array_variable_c *symbol) { + check_dimension_count(symbol); + check_bounds(symbol); + return NULL; +} + + +/**************************************/ +/* B 1.5 - Program organisation units */ +/**************************************/ +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ +// SYM_REF4(function_declaration_c, derived_function_name, type_name, var_declarations_list, function_body) +void *array_range_check_c::visit(function_declaration_c *symbol) { + symbol->var_declarations_list->accept(*this); // required for visiting subrange_c + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + // search_var_instance_decl = new search_var_instance_decl_c(symbol); + symbol->function_body->accept(*this); + delete search_varfb_instance_type; + // delete search_var_instance_decl; + search_varfb_instance_type = NULL; + // search_var_instance_decl = NULL; + return NULL; +} + +/*****************************/ +/* B 1.5.2 - Function blocks */ +/*****************************/ +// SYM_REF3(function_block_declaration_c, fblock_name, var_declarations, fblock_body) +void *array_range_check_c::visit(function_block_declaration_c *symbol) { + symbol->var_declarations->accept(*this); // required for visiting subrange_c + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + // search_var_instance_decl = new search_var_instance_decl_c(symbol); + symbol->fblock_body->accept(*this); + delete search_varfb_instance_type; + // delete search_var_instance_decl; + search_varfb_instance_type = NULL; + // search_var_instance_decl = NULL; + return NULL; +} + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ +// SYM_REF3(program_declaration_c, program_type_name, var_declarations, function_block_body) +void *array_range_check_c::visit(program_declaration_c *symbol) { + symbol->var_declarations->accept(*this); // required for visiting subrange_c + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + // search_var_instance_decl = new search_var_instance_decl_c(symbol); + symbol->function_block_body->accept(*this); + delete search_varfb_instance_type; + // delete search_var_instance_decl; + search_varfb_instance_type = NULL; + // search_var_instance_decl = NULL; + return NULL; +} + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/array_range_check.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/array_range_check.hh Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,103 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (conti.ma@alice.it) + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +// #include +#include "../absyntax_utils/absyntax_utils.hh" +// #include "datatype_functions.hh" + + + +class array_range_check_c: public iterator_visitor_c { + + private: + search_varfb_instance_type_c *search_varfb_instance_type; + // search_var_instance_decl_c *search_var_instance_decl; + search_base_type_c search_base_type; + int error_count; + int current_display_error_level; + + void check_dimension_count(array_variable_c *symbol); + void check_bounds(array_variable_c *symbol); + + public: + array_range_check_c(symbol_c *ignore); + virtual ~array_range_check_c(void); + int get_error_count(); + + /*************************/ + /* B.1 - Common elements */ + /*************************/ + /**********************/ + /* B.1.3 - Data types */ + /**********************/ + /********************************/ + /* B 1.3.3 - Derived data types */ + /********************************/ + /* NOTE: we may later want to move the following 2 methods to a visitor that will focus on analysing the data type declarations! */ + void *visit(subrange_c *symbol); + void *visit(array_initial_elements_c *symbol); + + /*********************/ + /* B 1.4 - Variables */ + /*********************/ + /*************************************/ + /* B 1.4.2 - Multi-element variables */ + /*************************************/ + void *visit(array_variable_c *symbol); + + /**************************************/ + /* B 1.5 - Program organisation units */ + /**************************************/ + /***********************/ + /* B 1.5.1 - Functions */ + /***********************/ + void *visit(function_declaration_c *symbol); + + /*****************************/ + /* B 1.5.2 - Function blocks */ + /*****************************/ + void *visit(function_block_declaration_c *symbol); + + /**********************/ + /* B 1.5.3 - Programs */ + /**********************/ + void *visit(program_declaration_c *symbol); + +}; /* array_range_check_c */ + + + + + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/constant_folding.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/constant_folding.cc Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,1097 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (conti.ma@alice.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + + + + +/* Do constant folding... + * + * I.e., Determine the value of all expressions in which only constant values (i.e. literals) are used. + * The (constant) result of each operation is stored (annotated) in the respective operation symbol + * (e.g.: add_expression_c) in the abstract syntax tree, + * + * For example: + * 2 + 3 -> the constant value '5' is stored in the add_expression_c symbol. + * 22.2 - 5.0 -> the constant value '17.2' is stored in the add_expression_c symbol. + * etc... + * + * + * NOTE 1 + * Some operations and constants can have multiple data types. For example, + * 1 AND 0 + * may be either a BOOL, BYTE, WORD or LWORD. + * + * The same happens with + * 1 + 2 + * which may be signed (e.g. INT) or unsigned (UINT) + * + * For the above reason, instead of storing a single constant value, we actually store 4: + * - bool + * - uint64 + * - int64 + * - real64 + * + * Additionally, since the result of an operation may result in an overflow, we actually + * store the result inside a struct (defined in absyntax.hh) + * + * ** During stage 3 (semantic analysis/checking) we will be doing constant folding. + * * That algorithm will anotate the abstract syntax tree with the result of operations + * * on literals (i.e. 44 + 55 will store the result 99). + * * Since the same source code (e.g. 1 + 0) may actually be a BOOL or an ANY_INT, + * * or an ANY_BIT, we need to handle all possibilities, and determine the result of the + * * operation assuming each type. + * * For this reason, we have one entry for each possible type, with some expressions + * * having more than one entry filled in! + * ** + * typedef enum { cs_undefined, // not defined --> const_value is not valid! + * cs_const_value, // const value is valid + * cs_overflow // result produced overflow or underflow --> const_value is not valid! + * } const_status_t; + * + * typedef struct { + * const_status_t status; + * real64_t value; + * } const_value_real64_t; + * const_value_real64_t *const_value_real64; // when NULL --> UNDEFINED + * + * typedef struct { + * const_status_t status; + * int64_t value; + * } const_value_int64_t; + * const_value_int64_t *const_value_int64; // when NULL --> UNDEFINED + * + * typedef struct { + * const_status_t status; + * uint64_t value; + * } const_value_uint64_t; + * const_value_uint64_t *const_value_uint64; // when NULL --> UNDEFINED + * + * typedef struct { + * const_status_t status; + * bool value; + * } const_value_bool_t; + * const_value_bool_t *const_value_bool; // when NULL --> UNDEFINED + * + * + * + * NOTE 2 + * This file does not print out any error messages! + * We cannot really print out error messages when we find an overflow. Since each operation + * (symbol in the absract syntax tree for that operation) will have up to 4 constant results, + * it may happen that some of them overflow, while other do not. + * We must wait for data type checking to determine the exact data type of each expression + * before we can decide whether or not we should print out an overflow error message. + * + * For this reason, this visitor merely annotates the abstract syntax tree, and leaves the + * actuall printing of errors for the print_datatype_errors_c class! + */ + +#include "constant_folding.hh" +#include /* required for malloc() */ + +#include /* required for strlen() */ +// #include /* required for atoi() */ +#include /* required for errno */ + +#include "../main.hh" // required for uint8_t, real_64_t, ..., and the macros NAN, INFINITY, INT8_MAX, REAL32_MAX, ... */ + + + + + + +#define FIRST_(symbol1, symbol2) (((symbol1)->first_order < (symbol2)->first_order) ? (symbol1) : (symbol2)) +#define LAST_(symbol1, symbol2) (((symbol1)->last_order > (symbol2)->last_order) ? (symbol1) : (symbol2)) + +#define STAGE3_ERROR(error_level, symbol1, symbol2, ...) { \ + if (current_display_error_level >= error_level) { \ + fprintf(stderr, "%s:%d-%d..%d-%d: error: ", \ + FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ + LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + error_count++; \ + } \ +} + + +#define STAGE3_WARNING(symbol1, symbol2, ...) { \ + fprintf(stderr, "%s:%d-%d..%d-%d: warning: ", \ + FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ + LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + warning_found = true; \ +} + + + + + + + + + + + + +#define SET_CVALUE(dtype, symbol, new_value) ((symbol)->const_value._##dtype.value) = new_value; ((symbol)->const_value._##dtype.status) = symbol_c::cs_const_value; +#define GET_CVALUE(dtype, symbol) ((symbol)->const_value._##dtype.value) +#define SET_OVFLOW(dtype, symbol) ((symbol)->const_value._##dtype.status) = symbol_c::cs_overflow +#define SET_NONCONST(dtype, symbol) ((symbol)->const_value._##dtype.status) = symbol_c::cs_non_const + +#define VALID_CVALUE(dtype, symbol) (symbol_c::cs_const_value == (symbol)->const_value._##dtype.status) +#define ISZERO_CVALUE(dtype, symbol) ((VALID_CVALUE(dtype, symbol)) && (GET_CVALUE(dtype, symbol) == 0)) + +#define ISEQUAL_CVALUE(dtype, symbol1, symbol2) \ + (VALID_CVALUE(dtype, symbol1) && VALID_CVALUE(dtype, symbol2) && (GET_CVALUE(dtype, symbol1) == GET_CVALUE(dtype, symbol2))) + +#define DO_BINARY_OPER(dtype, oper, otype)\ + if (VALID_CVALUE(dtype, symbol->r_exp) && VALID_CVALUE(dtype, symbol->l_exp)) { \ + SET_CVALUE(otype, symbol, GET_CVALUE(dtype, symbol->l_exp) oper GET_CVALUE(dtype, symbol->r_exp)); \ + } + +#define DO_BINARY_OPER_(oper_type, operation, res_type, operand1, operand2)\ + if (VALID_CVALUE(oper_type, operand1) && VALID_CVALUE(oper_type, operand2)) { \ + SET_CVALUE(res_type, symbol, GET_CVALUE(oper_type, operand1) operation GET_CVALUE(oper_type, operand2)); \ + } + +#define DO_UNARY_OPER(dtype, operation, operand)\ + if (VALID_CVALUE(dtype, operand)) { \ + SET_CVALUE(dtype, symbol, operation GET_CVALUE(dtype, operand)); \ + } + + + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/*** convert string to numerical value ***/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + + + /* To allow the compiler to be portable, we cannot assume that int64_t is mapped onto long long int, + * so we cannot call strtoll() and strtoull() in extract_int64() and extract_uint64(). + * + * So, we create our own strtouint64() and strtoint64() functions. + * (We actually call them matiec_strtoint64() so they will not clash with any function + * that may be added to the standard library in the future). + * We actually create several of each, and let the compiler choose which is the correct one, + * by having it resolve the call to the overloaded function. For the C++ compiler to be able + * to resolve this ambiguity, we need to add a dummy parameter to each function! + * + * TODO: support platforms in which int64_t is mapped onto int !! Is this really needed? + */ +static int64_t matiec_strtoint64 ( long int *dummy, const char *nptr, char **endptr, int base) {return strtol (nptr, endptr, base);} +static int64_t matiec_strtoint64 ( long long int *dummy, const char *nptr, char **endptr, int base) {return strtoll (nptr, endptr, base);} + +static uint64_t matiec_strtouint64(unsigned long int *dummy, const char *nptr, char **endptr, int base) {return strtoul (nptr, endptr, base);} +static uint64_t matiec_strtouint64(unsigned long long int *dummy, const char *nptr, char **endptr, int base) {return strtoull(nptr, endptr, base);} + + +/* extract the value of an integer from an integer_c object !! */ +/* NOTE: it must ignore underscores! */ +/* NOTE: To follow the basic structure used throughout the compiler's code, we should really be + * writing this as a visitor_c (and do away with the dynamic casts!), but since we only have 3 distinct + * symbol class types to handle, it is probably easier to read if we write it as a standard function... + */ +int64_t extract_int64_value(symbol_c *sym, bool *overflow) { + int64_t ret; + std::string str = ""; + char *endptr; + const char *value; + int base; + integer_c *integer; + hex_integer_c *hex_integer; + octal_integer_c *octal_integer; + binary_integer_c *binary_integer; + + if ((integer = dynamic_cast(sym)) != NULL) {value = integer ->value + 0; base = 10;} + else if ((hex_integer = dynamic_cast(sym)) != NULL) {value = hex_integer ->value + 3; base = 16;} + else if ((octal_integer = dynamic_cast(sym)) != NULL) {value = octal_integer ->value + 2; base = 8;} + else if ((binary_integer = dynamic_cast(sym)) != NULL) {value = binary_integer->value + 2; base = 2;} + else ERROR; + + for(unsigned int i = 0; i < strlen(value); i++) + if (value[i] != '_') str += value[i]; + + errno = 0; // since strtoXX() may legally return 0, we must set errno to 0 to detect errors correctly! + ret = matiec_strtoint64((int64_t *)NULL, str.c_str(), &endptr, base); + if (overflow != NULL) + *overflow = (errno == ERANGE); + if (((errno != 0) && (errno != ERANGE)) || (*endptr != '\0')) + ERROR; + + return ret; +} + + + +uint64_t extract_uint64_value(symbol_c *sym, bool *overflow) { + uint64_t ret; + std::string str = ""; + char *endptr; + const char *value; + int base; + integer_c *integer; + hex_integer_c *hex_integer; + octal_integer_c *octal_integer; + binary_integer_c *binary_integer; + + if ((integer = dynamic_cast(sym)) != NULL) {value = integer ->value + 0; base = 10;} + else if ((hex_integer = dynamic_cast(sym)) != NULL) {value = hex_integer ->value + 3; base = 16;} + else if ((octal_integer = dynamic_cast(sym)) != NULL) {value = octal_integer ->value + 2; base = 8;} + else if ((binary_integer = dynamic_cast(sym)) != NULL) {value = binary_integer->value + 2; base = 2;} + else ERROR; + + for(unsigned int i = 0; i < strlen(value); i++) + if (value[i] != '_') str += value[i]; + + errno = 0; // since strtoXX() may legally return 0, we must set errno to 0 to detect errors correctly! + ret = matiec_strtouint64((uint64_t *)NULL, str.c_str(), &endptr, base); + if (overflow != NULL) + *overflow = (errno == ERANGE); + if (((errno != 0) && (errno != ERANGE)) || (*endptr != '\0')) + ERROR; + + return ret; +} + + + +/* extract the value of a real from an real_c object !! */ +/* NOTE: it must ignore underscores! */ +/* From iec_bison.yy + * real: + * real_token {$$ = new real_c($1, locloc(@$));} + * | fixed_point_token {$$ = new real_c($1, locloc(@$));} + * + * From iec_flex.ll + * {real} {yylval.ID=strdup(yytext); return real_token;} + * {fixed_point} {yylval.ID=strdup(yytext); return fixed_point_token;} + * + * real {integer}\.{integer}{exponent} + * fixed_point {integer}\.{integer} + * exponent [Ee]([+-]?){integer} + * integer {digit}((_?{digit})*) + */ +real64_t extract_real_value(symbol_c *sym, bool *overflow) { + std::string str = ""; + real_c *real_sym; + char *endptr; + real64_t ret; + + if ((real_sym = dynamic_cast(sym)) == NULL) ERROR; + for(unsigned int i = 0; i < strlen(real_sym->value); i++) + if (real_sym->value[i] != '_') str += real_sym->value[i]; + + errno = 0; // since strtoXX() may legally return 0, we must set errno to 0 to detect errors correctly! + #if (real64_t == float) + ret = strtof(str.c_str(), &endptr); + #elif (real64_t == double) + ret = strtod(str.c_str(), &endptr); + #elif (real64_t == long_double) + ret = strtold(str.c_str(), &endptr); + #else + #error Could not determine which data type is being used for real64_t (defined in absyntax.hh). Aborting! + #endif + if (overflow != NULL) + *overflow = (errno == ERANGE); + if (((errno != 0) && (errno != ERANGE)) || (*endptr != '\0')) + ERROR; + + return ret; +} + + + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/*** Functions to check for overflow situation ***/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + +/* NOTE: + * Most of the conditions to detect overflows on signed and unsigned integer operations were adapted from + * https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow?showComments=false + * https://www.securecoding.cert.org/confluence/display/seccode/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap + */ + +/* NOTE: If at all possible, all overflow tests are done by pre-condition tests, i.e. tests that + * can be run _before_ the operation is executed, and therefore without accessing the result! + * + * The exception is for real/floating point values, that simply test if the result is NaN (not a number). + */ + +/* res = a + b */ +static void CHECK_OVERFLOW_uint64_SUM(symbol_c *res, symbol_c *a, symbol_c *b) { + if (!VALID_CVALUE(uint64, res)) + return; + /* Test by post-condition: If sum is smaller than either operand => overflow! */ + // if (GET_CVALUE(uint64, res) < GET_CVALUE(uint64, a)) + /* Test by pre-condition: If (UINT64_MAX - a) < b => overflow! */ + if ((UINT64_MAX - GET_CVALUE(uint64, a)) < GET_CVALUE(uint64, b)) + SET_OVFLOW(uint64, res); +} + + +/* res = a - b */ +static void CHECK_OVERFLOW_uint64_SUB(symbol_c *res, symbol_c *a, symbol_c *b) { + if (!VALID_CVALUE(uint64, res)) + return; + /* Test by post-condition: If diference is larger than a => overflow! */ + // if (GET_CVALUE(uint64, res) > GET_CVALUE(uint64, a)) + /* Test by pre-condition: if b > a => overflow! */ + if (GET_CVALUE(uint64, b) > GET_CVALUE(uint64, a)) + SET_OVFLOW(uint64, res); +} + + +/* res = a * b */ +static void CHECK_OVERFLOW_uint64_MUL(symbol_c *res, symbol_c *a, symbol_c *b) { + if (!VALID_CVALUE(uint64, res)) + return; + /* Test by pre-condition: If (UINT64_MAX / a) < b => overflow! */ + if ((UINT64_MAX / GET_CVALUE(uint64, a)) < GET_CVALUE(uint64, b)) + SET_OVFLOW(uint64, res); +} + + +/* res = a / b */ +static void CHECK_OVERFLOW_uint64_DIV(symbol_c *res, symbol_c *a, symbol_c *b) { + if (!VALID_CVALUE(uint64, res)) + return; + if (GET_CVALUE(uint64, b) == 0) /* division by zero! */ + SET_OVFLOW(uint64, res); +} + + +/* res = a MOD b */ +static void CHECK_OVERFLOW_uint64_MOD(symbol_c *res, symbol_c *a, symbol_c *b) { + if (!VALID_CVALUE(uint64, res)) + return; + /* no overflow condition exists, including division by zero, which IEC 61131-3 considers legal for MOD operation! */ + if (false) + SET_OVFLOW(uint64, res); +} + + +/* res = a + b */ +static void CHECK_OVERFLOW_int64_SUM(symbol_c *res, symbol_c *a_ptr, symbol_c *b_ptr) { + if (!VALID_CVALUE(int64, res)) + return; + int64_t a = GET_CVALUE(int64, a_ptr); + int64_t b = GET_CVALUE(int64, b_ptr); + /* The following test is valid no matter what representation is being used (e.g. two's complement, etc...) */ + if (((b > 0) && (a > (INT64_MAX - b))) + || ((b < 0) && (a < (INT64_MIN - b)))) + SET_OVFLOW(int64, res); +} + + +/* res = a - b */ +static void CHECK_OVERFLOW_int64_SUB(symbol_c *res, symbol_c *a_ptr, symbol_c *b_ptr) { + if (!VALID_CVALUE(int64, res)) + return; + int64_t a = GET_CVALUE(int64, a_ptr); + int64_t b = GET_CVALUE(int64, b_ptr); + /* The following test is valid no matter what representation is being used (e.g. two's complement, etc...) */ + if (((b > 0) && (a < (INT64_MIN + b))) + || ((b < 0) && (a > (INT64_MAX + b)))) + SET_OVFLOW(int64, res); +} + + +/* res = a * b */ +static void CHECK_OVERFLOW_int64_MUL(symbol_c *res, symbol_c *a_ptr, symbol_c *b_ptr) { + if (!VALID_CVALUE(int64, res)) + return; + int64_t a = GET_CVALUE(int64, a_ptr); + int64_t b = GET_CVALUE(int64, b_ptr); + if ( ( (a > 0) && (b > 0) && (a > (INT64_MAX / b))) + || ( (a > 0) && !(b > 0) && (b < (INT64_MIN / a))) + || (!(a > 0) && (b > 0) && (a < (INT64_MIN / b))) + || (!(a > 0) && !(b > 0) && (a != 0) && (b < (INT64_MAX / a)))) + SET_OVFLOW(int64, res); +} + + +/* res = a / b */ +static void CHECK_OVERFLOW_int64_DIV(symbol_c *res, symbol_c *a_ptr, symbol_c *b_ptr) { + if (!VALID_CVALUE(int64, res)) + return; + int64_t a = GET_CVALUE(int64, a_ptr); + int64_t b = GET_CVALUE(int64, b_ptr); + if ((b == 0) || ((a == INT64_MIN) && (b == -1))) + SET_OVFLOW(int64, res); +} + + +/* res = a MOD b */ +static void CHECK_OVERFLOW_int64_MOD(symbol_c *res, symbol_c *a_ptr, symbol_c *b_ptr) { + if (!VALID_CVALUE(int64, res)) + return; + int64_t a = GET_CVALUE(int64, a_ptr); + int64_t b = GET_CVALUE(int64, b_ptr); + /* IEC 61131-3 standard says IN1 MOD IN2 must be equivalent to + * IF (IN2 = 0) THEN OUT:=0 ; ELSE OUT:=IN1 - (IN1/IN2)*IN2 ; END_IF + * + * Note that, when IN1 = INT64_MIN, and IN2 = -1, an overflow occurs in the division, + * so although the MOD operation should be OK, acording to the above definition, we actually have an overflow!! + * + * On the other hand, division by 0 is OK!! + */ + if ((a == INT64_MIN) && (b == -1)) + SET_OVFLOW(int64, res); +} + + +/* res = - a */ +static void CHECK_OVERFLOW_int64_NEG(symbol_c *res, symbol_c *a_ptr) { + if (!VALID_CVALUE(int64, res)) + return; + int64_t a = GET_CVALUE(int64, a_ptr); + if (a == INT64_MIN) + SET_OVFLOW(int64, res); +} + + + +static void CHECK_OVERFLOW_real64(symbol_c *res_ptr) { + if (!VALID_CVALUE(real64, res_ptr)) + return; + real64_t res = GET_CVALUE(real64, res_ptr); + /* NaN => underflow, overflow, number is a higher precision format, is a complex number (IEEE standard) */ + /* The IEC 61131-3 clearly states in section '2.5.1.5.2 Numerical functions': + * "It is an error if the result of evaluation of one of these [numerical] functions exceeds the range of values + * specified for the data type of the function output, or if division by zero is attempted." + * For this reason, any operation that has as a result a positive or negative inifinity, is also an error! + */ + if ((isnan(res)) || (res == INFINITY) || (res == -INFINITY)) + SET_OVFLOW(real64, res_ptr); +} + + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/*** Functions to execute operations on the const values ***/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + +/* static void *handle_cmp(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2, OPERATION) */ +#define handle_cmp(symbol, oper1, oper2, operation) { \ + if ((NULL == oper1) || (NULL == oper2)) return NULL; \ + DO_BINARY_OPER_( bool, operation, bool, oper1, oper2); \ + DO_BINARY_OPER_(uint64, operation, bool, oper1, oper2); \ + DO_BINARY_OPER_( int64, operation, bool, oper1, oper2); \ + DO_BINARY_OPER_(real64, operation, bool, oper1, oper2); \ + return NULL; \ +} + + +/* NOTE: the MOVE standard function is equivalent to the ':=' in ST syntax */ +static void *handle_move(symbol_c *to, symbol_c *from) { + if (NULL == from) return NULL; + to->const_value = from->const_value; + return NULL; +} + + +/* unary negation (multiply by -1) */ +static void *handle_neg(symbol_c *symbol, symbol_c *oper) { + DO_UNARY_OPER( int64, -, oper); CHECK_OVERFLOW_int64_NEG(symbol, oper); + DO_UNARY_OPER(real64, -, oper); CHECK_OVERFLOW_real64(symbol); + return NULL; +} + + +/* unary boolean negation (NOT) */ +static void *handle_not(symbol_c *symbol, symbol_c *oper) { + if (NULL == oper) return NULL; + DO_UNARY_OPER( bool, !, oper); + DO_UNARY_OPER(uint64, ~, oper); + return NULL; +} + + +static void *handle_or (symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { + if ((NULL == oper1) || (NULL == oper2)) return NULL; + DO_BINARY_OPER_( bool, ||, bool , oper1, oper2); + DO_BINARY_OPER_(uint64, | , uint64, oper1, oper2); + return NULL; +} + + +static void *handle_xor(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { + if ((NULL == oper1) || (NULL == oper2)) return NULL; + DO_BINARY_OPER_( bool, ^, bool , oper1, oper2); + DO_BINARY_OPER_(uint64, ^, uint64, oper1, oper2); + return NULL; +} + + +static void *handle_and(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { + if ((NULL == oper1) || (NULL == oper2)) return NULL; + DO_BINARY_OPER_( bool, &&, bool, oper1, oper2); + DO_BINARY_OPER_(uint64, & , uint64, oper1, oper2); + return NULL; +} + + +static void *handle_add(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { + if ((NULL == oper1) || (NULL == oper2)) return NULL; + DO_BINARY_OPER_(uint64, +, uint64, oper1, oper2); CHECK_OVERFLOW_uint64_SUM(symbol, oper1, oper2); + DO_BINARY_OPER_( int64, +, int64, oper1, oper2); CHECK_OVERFLOW_int64_SUM (symbol, oper1, oper2); + DO_BINARY_OPER_(real64, +, real64, oper1, oper2); CHECK_OVERFLOW_real64 (symbol); + return NULL; +} + + +static void *handle_sub(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { + if ((NULL == oper1) || (NULL == oper2)) return NULL; + DO_BINARY_OPER_(uint64, -, uint64, oper1, oper2); CHECK_OVERFLOW_uint64_SUB(symbol, oper1, oper2); + DO_BINARY_OPER_( int64, -, int64, oper1, oper2); CHECK_OVERFLOW_int64_SUB (symbol, oper1, oper2); + DO_BINARY_OPER_(real64, -, real64, oper1, oper2); CHECK_OVERFLOW_real64 (symbol); + return NULL; +} + + +static void *handle_mul(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { + if ((NULL == oper1) || (NULL == oper2)) return NULL; + DO_BINARY_OPER_(uint64, *, uint64, oper1, oper2); CHECK_OVERFLOW_uint64_MUL(symbol, oper1, oper2); + DO_BINARY_OPER_( int64, *, int64, oper1, oper2); CHECK_OVERFLOW_int64_MUL (symbol, oper1, oper2); + DO_BINARY_OPER_(real64, *, real64, oper1, oper2); CHECK_OVERFLOW_real64 (symbol); + return NULL; +} + + +static void *handle_div(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { + if ((NULL == oper1) || (NULL == oper2)) return NULL; + if (ISZERO_CVALUE(uint64, oper2)) {SET_OVFLOW(uint64, symbol);} else {DO_BINARY_OPER_(uint64, /, uint64, oper1, oper2); CHECK_OVERFLOW_uint64_DIV(symbol, oper1, oper2);}; + if (ISZERO_CVALUE( int64, oper2)) {SET_OVFLOW( int64, symbol);} else {DO_BINARY_OPER_( int64, /, int64, oper1, oper2); CHECK_OVERFLOW_int64_DIV (symbol, oper1, oper2);}; + if (ISZERO_CVALUE(real64, oper2)) {SET_OVFLOW(real64, symbol);} else {DO_BINARY_OPER_(real64, /, real64, oper1, oper2); CHECK_OVERFLOW_real64(symbol);}; + return NULL; +} + + +static void *handle_mod(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { + if ((NULL == oper1) || (NULL == oper2)) return NULL; + /* IEC 61131-3 standard says IN1 MOD IN2 must be equivalent to + * IF (IN2 = 0) THEN OUT:=0 ; ELSE OUT:=IN1 - (IN1/IN2)*IN2 ; END_IF + * + * Note that, when IN1 = INT64_MIN, and IN2 = -1, an overflow occurs in the division, + * so although the MOD operation should be OK, acording to the above definition, we actually have an overflow!! + */ + if (ISZERO_CVALUE(uint64, oper2)) {SET_CVALUE(uint64, symbol, 0);} else {DO_BINARY_OPER_(uint64, %, uint64, oper1, oper2); CHECK_OVERFLOW_uint64_MOD(symbol, oper1, oper2);}; + if (ISZERO_CVALUE( int64, oper2)) {SET_CVALUE( int64, symbol, 0);} else {DO_BINARY_OPER_( int64, %, int64, oper1, oper2); CHECK_OVERFLOW_int64_MOD (symbol, oper1, oper2);}; + return NULL; +} + + +static void *handle_pow(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { + /* NOTE: If the const_value in symbol->r_exp is within the limits of both int64 and uint64, then we do both operations. + * That is OK, as the result should be identicial (we do create an unnecessary CVALUE variable, but who cares?). + * If only one is valid, then that is the oper we will do! + */ + if (VALID_CVALUE(real64, oper1) && VALID_CVALUE( int64, oper2)) + SET_CVALUE(real64, symbol, pow(GET_CVALUE(real64, oper1), GET_CVALUE( int64, oper2))); + if (VALID_CVALUE(real64, oper1) && VALID_CVALUE(uint64, oper2)) + SET_CVALUE(real64, symbol, pow(GET_CVALUE(real64, oper1), GET_CVALUE(uint64, oper2))); + CHECK_OVERFLOW_real64(symbol); + return NULL; +} + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/*** Helper functions for handling IL instruction lists. ***/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + +/* If the cvalues of all the prev_il_intructions have the same VALID value, then set the local cvalue to that value, otherwise, set it to NONCONST! */ +#define intersect_prev_CVALUE_(dtype, symbol) { \ + symbol->const_value._##dtype = symbol->prev_il_instruction[0]->const_value._##dtype; \ + for (unsigned int i = 1; i < symbol->prev_il_instruction.size(); i++) { \ + if (!ISEQUAL_CVALUE(dtype, symbol, symbol->prev_il_instruction[i])) \ + {SET_NONCONST(dtype, symbol); break;} \ + } \ +} + +static void intersect_prev_cvalues(il_instruction_c *symbol) { + if (symbol->prev_il_instruction.empty()) + return; + intersect_prev_CVALUE_(real64, symbol); + intersect_prev_CVALUE_(uint64, symbol); + intersect_prev_CVALUE_( int64, symbol); + intersect_prev_CVALUE_( bool, symbol); +} + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/*** The constant_folding_c ***/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + + + + + +constant_folding_c::constant_folding_c(symbol_c *symbol) { + error_count = 0; + warning_found = false; + current_display_error_level = 0; + + /* check whether the platform on which the compiler is being run implements IEC 559 floating point data types. */ + symbol_c null_symbol; + if (! (std::numeric_limits::is_iec559) ) + STAGE3_WARNING(&null_symbol, &null_symbol, "The platform running the compiler does not implement IEC 60559 floating point numbers. " + "Any error and/or warning messages related to overflow/underflow of the result of operations on REAL/LREAL literals " + "(i.e. constant folding) may themselves be erroneous, although are most probably correct." + "However, more likely is the possible existance of overflow/underflow errors that are not detected."); +} + + +constant_folding_c::~constant_folding_c(void) { +} + + +int constant_folding_c::get_error_count() { + return error_count; +} + + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ +void *constant_folding_c::visit(real_c *symbol) { + bool overflow; + SET_CVALUE(real64, symbol, extract_real_value(symbol, &overflow)); + if (overflow) SET_OVFLOW(real64, symbol); + return NULL; +} + + +void *constant_folding_c::visit(integer_c *symbol) { + bool overflow; + SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); + if (overflow) SET_OVFLOW(int64, symbol); + SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); + if (overflow) SET_OVFLOW(uint64, symbol); + return NULL; +} + + +void *constant_folding_c::visit(neg_real_c *symbol) { + symbol->exp->accept(*this); + DO_UNARY_OPER(real64, -, symbol->exp); + CHECK_OVERFLOW_real64(symbol); + return NULL; +} + +/* | '-' integer {$$ = new neg_integer_c($2, locloc(@$));} */ +void *constant_folding_c::visit(neg_integer_c *symbol) { + symbol->exp->accept(*this); + DO_UNARY_OPER(int64, -, symbol->exp); + CHECK_OVERFLOW_int64_NEG(symbol, symbol->exp); + /* NOTE 1: INT64_MIN = -(INT64_MAX + 1) ---> assuming two's complement representation!!! + * NOTE 2: if the user happens to want INT_MIN, that value will first be parsed as a positive integer, before being negated here. + * However, the positive value cannot be stored inside an int64! So, in this case, we will get the value from the uint64 cvalue. + */ + // if (INT64_MIN == -INT64_MAX - 1) // We do not really need to check that the platform uses two's complement + if (VALID_CVALUE(uint64, symbol->exp) && (GET_CVALUE(uint64, symbol->exp) == (uint64_t)INT64_MAX+1)) { + SET_CVALUE(int64, symbol, INT64_MIN); + } + return NULL; +} + + +void *constant_folding_c::visit(binary_integer_c *symbol) { + bool overflow; + SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); + if (overflow) SET_OVFLOW(int64, symbol); + SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); + if (overflow) SET_OVFLOW(uint64, symbol); + return NULL; +} + + +void *constant_folding_c::visit(octal_integer_c *symbol) { + bool overflow; + SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); + if (overflow) SET_OVFLOW(int64, symbol); + SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); + if (overflow) SET_OVFLOW(uint64, symbol); + return NULL; +} + + +void *constant_folding_c::visit(hex_integer_c *symbol) { + bool overflow; + SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); + if (overflow) SET_OVFLOW(int64, symbol); + SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); + if (overflow) SET_OVFLOW(uint64, symbol); + return NULL; +} + + +/* +integer_literal: + integer_type_name '#' signed_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} +| integer_type_name '#' binary_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} +| integer_type_name '#' octal_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} +| integer_type_name '#' hex_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} +*/ +// SYM_REF2(integer_literal_c, type, value) +void *constant_folding_c::visit(integer_literal_c *symbol) { + symbol->value->accept(*this); + DO_UNARY_OPER( int64, /* none */, symbol->value); + DO_UNARY_OPER(uint64, /* none */, symbol->value); + return NULL; +} + + +void *constant_folding_c::visit(real_literal_c *symbol) { + symbol->value->accept(*this); + DO_UNARY_OPER(real64, /* none */, symbol->value); + return NULL; +} + + +void *constant_folding_c::visit(bit_string_literal_c *symbol) { + return NULL; +} + + +void *constant_folding_c::visit(boolean_literal_c *symbol) { + symbol->value->accept(*this); + DO_UNARY_OPER(bool, /* none */, symbol->value); + return NULL; +} + + +void *constant_folding_c::visit(boolean_true_c *symbol) { + SET_CVALUE(bool, symbol, true); + return NULL; +} + + +void *constant_folding_c::visit(boolean_false_c *symbol) { + SET_CVALUE(bool, symbol, false); + return NULL; +} + + + + + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ +/* Not needed, since we inherit from iterator_visitor_c */ +/*| instruction_list il_instruction */ +// SYM_LIST(instruction_list_c) +// void *constant_folding_c::visit(instruction_list_c *symbol) {} + +/* | label ':' [il_incomplete_instruction] eol_list */ +// SYM_REF2(il_instruction_c, label, il_instruction) +// void *visit(instruction_list_c *symbol); +void *constant_folding_c::visit(il_instruction_c *symbol) { + if (NULL == symbol->il_instruction) { + /* This empty/null il_instruction does not change the value of the current/default IL variable. + * So it inherits the candidate_datatypes from it's previous IL instructions! + */ + intersect_prev_cvalues(symbol); + } else { + il_instruction_c fake_prev_il_instruction = *symbol; + intersect_prev_cvalues(&fake_prev_il_instruction); + + if (symbol->prev_il_instruction.size() == 0) prev_il_instruction = NULL; + else prev_il_instruction = &fake_prev_il_instruction; + symbol->il_instruction->accept(*this); + prev_il_instruction = NULL; + + /* This object has (inherits) the same cvalues as the il_instruction */ + symbol->const_value = symbol->il_instruction->const_value; + } + + return NULL; +} + + +void *constant_folding_c::visit(il_simple_operation_c *symbol) { + /* determine the cvalue of the operand */ + if (NULL != symbol->il_operand) { + symbol->il_operand->accept(*this); + } + /* determine the cvalue resulting from executing the il_operator... */ + il_operand = symbol->il_operand; + symbol->il_simple_operator->accept(*this); + il_operand = NULL; + /* This object has (inherits) the same cvalues as the il_instruction */ + symbol->const_value = symbol->il_simple_operator->const_value; + return NULL; +} + + +/* TODO: handle function invocations... */ +/* | function_name [il_operand_list] */ +/* NOTE: The parameters 'called_function_declaration' and 'extensible_param_count' are used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;) +// void *constant_folding_c::visit(il_function_call_c *symbol) {} + + +/* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ +// SYM_REF3(il_expression_c, il_expr_operator, il_operand, simple_instr_list); +void *constant_folding_c::visit(il_expression_c *symbol) { + symbol_c *prev_il_instruction_backup = prev_il_instruction; + + if (NULL != symbol->il_operand) + symbol->il_operand->accept(*this); + + if(symbol->simple_instr_list != NULL) + symbol->simple_instr_list->accept(*this); + + /* Now do the operation, */ + il_operand = symbol->simple_instr_list; + prev_il_instruction = prev_il_instruction_backup; + symbol->il_expr_operator->accept(*this); + il_operand = NULL; + + /* This object has (inherits) the same cvalues as the il_instruction */ + symbol->const_value = symbol->il_expr_operator->const_value; + return NULL; +} + + + +void *constant_folding_c::visit(il_jump_operation_c *symbol) { + /* recursive call to fill const values... */ + il_operand = NULL; + symbol->il_jump_operator->accept(*this); + il_operand = NULL; + /* This object has (inherits) the same cvalues as the il_jump_operator */ + symbol->const_value = symbol->il_jump_operator->const_value; + return NULL; +} + + + +/* FB calls leave the value in the accumulator unchanged */ +/* il_call_operator prev_declared_fb_name + * | il_call_operator prev_declared_fb_name '(' ')' + * | il_call_operator prev_declared_fb_name '(' eol_list ')' + * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' + * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' + */ +/* NOTE: The parameter 'called_fb_declaration'is used to pass data between stage 3 and stage4 (although currently it is not used in stage 4 */ +// SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list, symbol_c *called_fb_declaration) +void *constant_folding_c::visit(il_fb_call_c *symbol) {return handle_move(symbol, prev_il_instruction);} + + +/* TODO: handle function invocations... */ +/* | function_name '(' eol_list [il_param_list] ')' */ +/* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;) +// void *constant_folding_c::visit(il_formal_funct_call_c *symbol) {return NULL;} + + + +/* Not needed, since we inherit from iterator_visitor_c */ +// void *constant_folding_c::visit(il_operand_list_c *symbol); + + + +/* | simple_instr_list il_simple_instruction */ +/* This object is referenced by il_expression_c objects */ +void *constant_folding_c::visit(simple_instr_list_c *symbol) { + if (symbol->n <= 0) + return NULL; /* List is empty! Nothing to do. */ + + for(int i = 0; i < symbol->n; i++) + symbol->elements[i]->accept(*this); + + /* This object has (inherits) the same cvalues as the il_jump_operator */ + symbol->const_value = symbol->elements[symbol->n-1]->const_value; + return NULL; +} + + + +// SYM_REF1(il_simple_instruction_c, il_simple_instruction, symbol_c *prev_il_instruction;) +void *constant_folding_c::visit(il_simple_instruction_c *symbol) { + if (symbol->prev_il_instruction.size() > 1) ERROR; /* There should be no labeled insructions inside an IL expression! */ + if (symbol->prev_il_instruction.size() == 0) prev_il_instruction = NULL; + else prev_il_instruction = symbol->prev_il_instruction[0]; + symbol->il_simple_instruction->accept(*this); + prev_il_instruction = NULL; + + /* This object has (inherits) the same cvalues as the il_jump_operator */ + symbol->const_value = symbol->il_simple_instruction->const_value; + return NULL; +} + + +/* + void *visit(il_param_list_c *symbol); + void *visit(il_param_assignment_c *symbol); + void *visit(il_param_out_assignment_c *symbol); +*/ + + +/*******************/ +/* B 2.2 Operators */ +/*******************/ +void *constant_folding_c::visit( LD_operator_c *symbol) {return handle_move(symbol, il_operand);} +void *constant_folding_c::visit( LDN_operator_c *symbol) {return handle_not (symbol, il_operand);} + +/* NOTE: we are implementing a constant folding algorithm, not a constant propagation algorithm. + * For the constant propagation algorithm, the correct implementation of ST(N)_operator_c would be... + */ +//void *constant_folding_c::visit( ST_operator_c *symbol) {return handle_move(il_operand, symbol);} +//void *constant_folding_c::visit( STN_operator_c *symbol) {return handle_not (il_operand, symbol);} +void *constant_folding_c::visit( ST_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( STN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} + +/* NOTE: the standard allows syntax in which the NOT operator is followed by an optional + * NOT [] + * However, it does not define the semantic of the NOT operation when the is specified. + * We therefore consider it an error if an il_operand is specified! This error will be caught elsewhere! + */ +void *constant_folding_c::visit( NOT_operator_c *symbol) {return handle_not(symbol, prev_il_instruction);} + +/* NOTE: Since we are only implementing a constant folding algorithm, and not a constant propagation algorithm, + * the following IL instructions do not change/set the value of the il_operand! + */ +void *constant_folding_c::visit( S_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( R_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} + +/* FB calls leave the value in the accumulator unchanged */ +void *constant_folding_c::visit( S1_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( R1_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( CLK_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( CU_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( CD_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( PV_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( IN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( PT_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} + +void *constant_folding_c::visit( AND_operator_c *symbol) {return handle_and (symbol, prev_il_instruction, il_operand);} +void *constant_folding_c::visit( OR_operator_c *symbol) {return handle_or (symbol, prev_il_instruction, il_operand);} +void *constant_folding_c::visit( XOR_operator_c *symbol) {return handle_xor (symbol, prev_il_instruction, il_operand);} +void *constant_folding_c::visit( ANDN_operator_c *symbol) { handle_and (symbol, prev_il_instruction, il_operand); return handle_not(symbol, symbol);} +void *constant_folding_c::visit( ORN_operator_c *symbol) { handle_or (symbol, prev_il_instruction, il_operand); return handle_not(symbol, symbol);} +void *constant_folding_c::visit( XORN_operator_c *symbol) { handle_xor (symbol, prev_il_instruction, il_operand); return handle_not(symbol, symbol);} + +void *constant_folding_c::visit( ADD_operator_c *symbol) {return handle_add (symbol, prev_il_instruction, il_operand);} +void *constant_folding_c::visit( SUB_operator_c *symbol) {return handle_sub (symbol, prev_il_instruction, il_operand);} +void *constant_folding_c::visit( MUL_operator_c *symbol) {return handle_mul (symbol, prev_il_instruction, il_operand);} +void *constant_folding_c::visit( DIV_operator_c *symbol) {return handle_div (symbol, prev_il_instruction, il_operand);} +void *constant_folding_c::visit( MOD_operator_c *symbol) {return handle_mod (symbol, prev_il_instruction, il_operand);} + +void *constant_folding_c::visit( GT_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, > );} +void *constant_folding_c::visit( GE_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, >=);} +void *constant_folding_c::visit( EQ_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, ==);} +void *constant_folding_c::visit( LT_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, < );} +void *constant_folding_c::visit( LE_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, <=);} +void *constant_folding_c::visit( NE_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, !=);} + +void *constant_folding_c::visit( CAL_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( RET_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( JMP_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( CALC_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit(CALCN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( RETC_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit(RETCN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit( JMPC_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} +void *constant_folding_c::visit(JMPCN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} + + + + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ +void *constant_folding_c::visit( or_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_or (symbol, symbol->l_exp, symbol->r_exp);} +void *constant_folding_c::visit( xor_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_xor(symbol, symbol->l_exp, symbol->r_exp);} +void *constant_folding_c::visit( and_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_and(symbol, symbol->l_exp, symbol->r_exp);} + +void *constant_folding_c::visit( equ_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, ==);} +void *constant_folding_c::visit(notequ_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, !=);} +void *constant_folding_c::visit( lt_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, < );} +void *constant_folding_c::visit( gt_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, > );} +void *constant_folding_c::visit( le_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, <=);} +void *constant_folding_c::visit( ge_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, >=);} + +void *constant_folding_c::visit( add_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_add(symbol, symbol->l_exp, symbol->r_exp);} +void *constant_folding_c::visit( sub_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_sub(symbol, symbol->l_exp, symbol->r_exp);} +void *constant_folding_c::visit( mul_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_mul(symbol, symbol->l_exp, symbol->r_exp);} +void *constant_folding_c::visit( div_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_div(symbol, symbol->l_exp, symbol->r_exp);} +void *constant_folding_c::visit( mod_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_mod(symbol, symbol->l_exp, symbol->r_exp);} +void *constant_folding_c::visit( power_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_pow(symbol, symbol->l_exp, symbol->r_exp);} + +void *constant_folding_c::visit( neg_expression_c *symbol) {symbol-> exp->accept(*this); return handle_neg(symbol, symbol->exp);} +void *constant_folding_c::visit( not_expression_c *symbol) {symbol-> exp->accept(*this); return handle_not(symbol, symbol->exp);} + +/* TODO: handle function invocations... */ +// void *fill_candidate_datatypes_c::visit(function_invocation_c *symbol) {} \ No newline at end of file diff -r aad38592bdde -r c0bda77b37a0 stage3/constant_folding.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/constant_folding.hh Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,179 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (conti.ma@alice.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* Determine the data type of an constant expression. + * A reference to the relevant type definition is returned. + * + * For example: + * 2 + 3 -> returns reference + * 22.2 - 5 -> returns reference + * etc... + */ + +#include +#include "../absyntax_utils/absyntax_utils.hh" + + + +class constant_folding_c : public iterator_visitor_c { + private: + search_varfb_instance_type_c *search_varfb_instance_type; + search_base_type_c search_base_type; + int error_count; + bool warning_found; + int current_display_error_level; + /* Pointer to the previous IL instruction, which contains the current cvalue of the data stored in the IL stack, i.e. the default variable, a.k.a. accumulator */ + symbol_c *prev_il_instruction; + /* the current IL operand being analyzed */ + symbol_c *il_operand; + + public: + constant_folding_c(symbol_c *symbol = NULL); + virtual ~constant_folding_c(void); + int get_error_count(); + + private: + /*********************/ + /* B 1.2 - Constants */ + /*********************/ + /******************************/ + /* B 1.2.1 - Numeric Literals */ + /******************************/ + void *visit(real_c *symbol); + void *visit(integer_c *symbol); + void *visit(neg_real_c *symbol); + void *visit(neg_integer_c *symbol); + void *visit(binary_integer_c *symbol); + void *visit(octal_integer_c *symbol); + void *visit(hex_integer_c *symbol); + void *visit(integer_literal_c *symbol); + void *visit(real_literal_c *symbol); + void *visit(bit_string_literal_c *symbol); + void *visit(boolean_literal_c *symbol); + void *visit(boolean_true_c *symbol); + void *visit(boolean_false_c *symbol); + + /****************************************/ + /* B.2 - Language IL (Instruction List) */ + /****************************************/ + /***********************************/ + /* B 2.1 Instructions and Operands */ + /***********************************/ + // void *visit(instruction_list_c *symbol); /* Not needed, since we inherit from iterator_visitor_c */ + void *visit(il_instruction_c *symbol); + void *visit(il_simple_operation_c *symbol); + //void *visit(il_function_call_c *symbol); /* TODO */ + void *visit(il_expression_c *symbol); + void *visit(il_jump_operation_c *symbol); + void *visit(il_fb_call_c *symbol); + //void *visit(il_formal_funct_call_c *symbol); /* TODO */ + //void *visit(il_operand_list_c *symbol); /* Not needed, since we inherit from iterator_visitor_c */ + void *visit(simple_instr_list_c *symbol); + void *visit(il_simple_instruction_c *symbol); + + + /*******************/ + /* B 2.2 Operators */ + /*******************/ + void *visit( LD_operator_c *symbol); + void *visit( LDN_operator_c *symbol); + void *visit( ST_operator_c *symbol); + void *visit( STN_operator_c *symbol); + void *visit( NOT_operator_c *symbol); + void *visit( S_operator_c *symbol); + void *visit( R_operator_c *symbol); + void *visit( S1_operator_c *symbol); + void *visit( R1_operator_c *symbol); + void *visit( CLK_operator_c *symbol); + void *visit( CU_operator_c *symbol); + void *visit( CD_operator_c *symbol); + void *visit( PV_operator_c *symbol); + void *visit( IN_operator_c *symbol); + void *visit( PT_operator_c *symbol); + void *visit( AND_operator_c *symbol); + void *visit( OR_operator_c *symbol); + void *visit( XOR_operator_c *symbol); + void *visit( ANDN_operator_c *symbol); + void *visit( ORN_operator_c *symbol); + void *visit( XORN_operator_c *symbol); + void *visit( ADD_operator_c *symbol); + void *visit( SUB_operator_c *symbol); + void *visit( MUL_operator_c *symbol); + void *visit( DIV_operator_c *symbol); + void *visit( MOD_operator_c *symbol); + void *visit( GT_operator_c *symbol); + void *visit( GE_operator_c *symbol); + void *visit( EQ_operator_c *symbol); + void *visit( LT_operator_c *symbol); + void *visit( LE_operator_c *symbol); + void *visit( NE_operator_c *symbol); + void *visit( CAL_operator_c *symbol); + void *visit( CALC_operator_c *symbol); + void *visit(CALCN_operator_c *symbol); + void *visit( RET_operator_c *symbol); + void *visit( RETC_operator_c *symbol); + void *visit(RETCN_operator_c *symbol); + void *visit( JMP_operator_c *symbol); + void *visit( JMPC_operator_c *symbol); + void *visit(JMPCN_operator_c *symbol); + /* Symbol class handled together with function call checks */ + // void *visit(il_assign_operator_c *symbol, variable_name); + /* Symbol class handled together with function call checks */ + // void *visit(il_assign_operator_c *symbol, option, variable_name); + + /***************************************/ + /* B.3 - Language ST (Structured Text) */ + /***************************************/ + /***********************/ + /* B 3.1 - Expressions */ + /***********************/ + void *visit( or_expression_c *symbol); + void *visit( xor_expression_c *symbol); + void *visit( and_expression_c *symbol); + void *visit( equ_expression_c *symbol); + void *visit(notequ_expression_c *symbol); + void *visit( lt_expression_c *symbol); + void *visit( gt_expression_c *symbol); + void *visit( le_expression_c *symbol); + void *visit( ge_expression_c *symbol); + void *visit( add_expression_c *symbol); + void *visit( sub_expression_c *symbol); + void *visit( mul_expression_c *symbol); + void *visit( div_expression_c *symbol); + void *visit( mod_expression_c *symbol); + void *visit( power_expression_c *symbol); + void *visit( neg_expression_c *symbol); + void *visit( not_expression_c *symbol); + //void *visit(function_invocation_c *symbol); /* TODO */ + +}; + diff -r aad38592bdde -r c0bda77b37a0 stage3/datatype_functions.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/datatype_functions.cc Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,806 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (conti.ma@alice.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +#include "datatype_functions.hh" +#include "../absyntax_utils/absyntax_utils.hh" +#include + + + + + + +elementary_type_c *elementary_type_c::singleton = NULL; + +const char *elementary_type_c::to_string(symbol_c *symbol) { + if (NULL == singleton) singleton = new elementary_type_c; + if (NULL == singleton) ERROR; + const char *res = (const char *)symbol->accept(*singleton); + if (NULL == res) ERROR; + return res; +} + + + + + +/* Macro that expand to subtypes */ +/* copied from matiec/lib/create_standard_functions_txt.sh */ +#define __ANY(DO) __ANY_DERIVED(DO) __ANY_ELEMENTARY(DO) +#define __ANY_DERIVED(DO) +#define __ANY_ELEMENTARY(DO) __ANY_MAGNITUDE(DO) __ANY_BIT(DO) __ANY_STRING(DO) __ANY_DATE(DO) +#define __ANY_MAGNITUDE(DO) __ANY_NUM(DO) DO(time) +#define __ANY_BIT(DO) __ANY_NBIT(DO) DO(bool) +#define __ANY_NBIT(DO) DO(byte) DO(word) DO(dword) DO(lword) +//#define __ANY_STRING(DO) DO(string) DO(wstring) +#define __ANY_STRING(DO) DO(string) +#define __ANY_DATE(DO) DO(date) DO(tod) DO(dt) +#define __ANY_NUM(DO) __ANY_REAL(DO) __ANY_INT(DO) +#define __ANY_REAL(DO) DO(real) DO(lreal) +#define __ANY_INT(DO) __ANY_SINT(DO) __ANY_UINT(DO) +#define __ANY_SINT(DO) DO(sint) DO(int) DO(dint) DO(lint) +#define __ANY_UINT(DO) DO(usint) DO(uint) DO(udint) DO(ulint) + +#define __ANY_1(DO,P1) __ANY_DERIVED_1(DO,P1) __ANY_ELEMENTARY_1(DO,P1) +#define __ANY_DERIVED_1(DO,P1) +#define __ANY_ELEMENTARY_1(DO,P1) __ANY_MAGNITUDE_1(DO,P1) __ANY_BIT_1(DO,P1) __ANY_STRING_1(DO,P1) __ANY_DATE_1(DO,P1) +#define __ANY_MAGNITUDE_1(DO,P1) __ANY_NUM_1(DO,P1) DO(time,P1) +#define __ANY_BIT_1(DO,P1) __ANY_NBIT_1(DO,P1) DO(bool,P1) +#define __ANY_NBIT_1(DO,P1) DO(byte,P1) DO(word,P1) DO(dword,P1) DO(lword,P1) +// #define __ANY_STRING_1(DO,P1) DO(string,P1) DO(wstring,P1) +#define __ANY_STRING_1(DO,P1) DO(string,P1) +#define __ANY_DATE_1(DO,P1) DO(date,P1) DO(tod,P1) DO(dt,P1) +#define __ANY_NUM_1(DO,P1) __ANY_REAL_1(DO,P1) __ANY_INT_1(DO,P1) +#define __ANY_REAL_1(DO,P1) DO(real,P1) DO(lreal,P1) +#define __ANY_INT_1(DO,P1) __ANY_SINT_1(DO,P1) __ANY_UINT_1(DO,P1) +#define __ANY_SINT_1(DO,P1) DO(sint,P1) DO(int,P1) DO(dint,P1) DO(lint,P1) +#define __ANY_UINT_1(DO,P1) DO(usint,P1) DO(uint,P1) DO(udint,P1) DO(ulint,P1) + + +/**************************************************************/ +/**************************************************************/ +/**************************************************************/ +/******* TABLE 24: Standard arithmetic functions *******/ +/******* merged with *******/ +/******* TABLE 30: Functions of time data types *******/ +/**************************************************************/ +/**************************************************************/ +/**************************************************************/ + +/* NOTE: IEC 61131-3 v2 declares that using implicit operations ('+', '-', '*', '/') on ANYTIME data types is + * valid, but deprecated, suposedly meaning that they will be removed in the following version of the standard. + * However, the current draft version of IEC 61131-3 v3 still allows this use, and no longer declares these + * implicit operations as deprecated. + * Because of this, and although we are implementing v2 of the standard, we will no longer mark these + * operations as deprecated. + */ + #define ANYTIME_OPER_DEPRECATION_STATUS widen_entry::ok +//#define ANYTIME_OPER_DEPRECATION_STATUS widen_entry::deprecated + + +const struct widen_entry widen_ADD_table[] = { +#define __add(TYPE) \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, widen_entry::ok }, + __ANY_NUM(__add) +#undef __add + + /*******************************************/ + /*******************************************/ + /*** Operations with TIME, DT and TOD... ***/ + /*******************************************/ + /*******************************************/ + { &search_constant_type_c::time_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::time_type_name, widen_entry::ok }, + { &search_constant_type_c::tod_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::tod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + /* NOTE: the standard des not explicitly support the following semantics. However, since 'addition' is supposed to be commutative, we add it anyway... */ + /* not currently supported by stage4, so it is best no tto add it for now... */ +// { &search_constant_type_c::time_type_name, &search_constant_type_c::tod_type_name, &search_constant_type_c::tod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::dt_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::dt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + /* NOTE: the standard des not explicitly support the following semantics. However, since 'addition' is supposed to be commutative, we add it anyway... */ + /* not currently supported by stage4, so it is best no tto add it for now... */ +// { &search_constant_type_c::time_type_name, &search_constant_type_c::dt_type_name, &search_constant_type_c::dt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + + /*******************************/ + /* SAFE version on the left... */ + /*******************************/ + { &search_constant_type_c::safetime_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::time_type_name, widen_entry::ok }, + { &search_constant_type_c::safetod_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::tod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + /* NOTE: the standard des not explicitly support the following semantics. However, since 'addition' is supposed to be commutative, we add it anyway... */ + /* not currently supported by stage4, so it is best no tto add it for now... */ +// { &search_constant_type_c::safetime_type_name, &search_constant_type_c::tod_type_name, &search_constant_type_c::tod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::safedt_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::dt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + /* NOTE: the standard des not explicitly support the following semantics. However, since 'addition' is supposed to be commutative, we add it anyway... */ + /* not currently supported by stage4, so it is best no tto add it for now... */ +// { &search_constant_type_c::safetime_type_name, &search_constant_type_c::dt_type_name, &search_constant_type_c::dt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + + /********************************/ + /* SAFE version on the right... */ + /********************************/ + { &search_constant_type_c::time_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::time_type_name, widen_entry::ok }, + { &search_constant_type_c::tod_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::tod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + /* NOTE: the standard des not explicitly support the following semantics. However, since 'addition' is supposed to be commutative, we add it anyway... */ + /* not currently supported by stage4, so it is best no tto add it for now... */ +// { &search_constant_type_c::time_type_name, &search_constant_type_c::safetod_type_name, &search_constant_type_c::tod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::dt_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::dt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + /* NOTE: the standard des not explicitly support the following semantics. However, since 'addition' is supposed to be commutative, we add it anyway... */ + /* not currently supported by stage4, so it is best no tto add it for now... */ +// { &search_constant_type_c::time_type_name, &search_constant_type_c::safedt_type_name, &search_constant_type_c::dt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + + /*************************************/ + /* SAFE version on left and right... */ + /*************************************/ + { &search_constant_type_c::safetime_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::safetime_type_name, widen_entry::ok }, + { &search_constant_type_c::safetod_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::safetod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + /* NOTE: the standard des not explicitly support the following semantics. However, since 'addition' is supposed to be commutative, we add it anyway... */ + /* not currently supported by stage4, so it is best no tto add it for now... */ +// { &search_constant_type_c::safetime_type_name, &search_constant_type_c::safetod_type_name, &search_constant_type_c::safetod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::safedt_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::safedt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + /* NOTE: the standard des not explicitly support the following semantics. However, since 'addition' is supposed to be commutative, we add it anyway... */ + /* not currently supported by stage4, so it is best no tto add it for now... */ +// { &search_constant_type_c::safetime_type_name, &search_constant_type_c::safedt_type_name, &search_constant_type_c::safedt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + + { NULL, NULL, NULL, widen_entry::ok }, +}; + + + + + + + +const struct widen_entry widen_SUB_table[] = { +#define __sub(TYPE) \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, widen_entry::ok }, + __ANY_NUM(__sub) +#undef __sub + + /*******************************************/ + /*******************************************/ + /*** Operations with TIME, DT and TOD... ***/ + /*******************************************/ + /*******************************************/ + { &search_constant_type_c::time_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::time_type_name, widen_entry::ok }, + { &search_constant_type_c::date_type_name, &search_constant_type_c::date_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::tod_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::tod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::tod_type_name, &search_constant_type_c::tod_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::dt_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::dt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::dt_type_name, &search_constant_type_c::dt_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + + /*******************************/ + /* SAFE version on the left... */ + /*******************************/ + { &search_constant_type_c::safetime_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::time_type_name, widen_entry::ok }, + { &search_constant_type_c::safedate_type_name, &search_constant_type_c::date_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::safetod_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::tod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::safetod_type_name, &search_constant_type_c::tod_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::safedt_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::dt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::safedt_type_name, &search_constant_type_c::dt_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + + /********************************/ + /* SAFE version on the right... */ + /********************************/ + { &search_constant_type_c::time_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::time_type_name, widen_entry::ok }, + { &search_constant_type_c::date_type_name, &search_constant_type_c::safedate_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::tod_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::tod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::tod_type_name, &search_constant_type_c::safetod_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::dt_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::dt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::dt_type_name, &search_constant_type_c::safedt_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + + /*************************************/ + /* SAFE version on left and right... */ + /*************************************/ + { &search_constant_type_c::safetime_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::safetime_type_name, widen_entry::ok }, + { &search_constant_type_c::safedate_type_name, &search_constant_type_c::safedate_type_name, &search_constant_type_c::safetime_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::safetod_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::safetod_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::safetod_type_name, &search_constant_type_c::safetod_type_name, &search_constant_type_c::safetime_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::safedt_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::safedt_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + { &search_constant_type_c::safedt_type_name, &search_constant_type_c::safedt_type_name, &search_constant_type_c::safetime_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + + { NULL, NULL, NULL, widen_entry::ok }, +}; + + + + + + + +const struct widen_entry widen_MUL_table[] = { +#define __mul(TYPE) \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, widen_entry::ok }, + __ANY_NUM(__mul) +#undef __mul + + /*******************************************/ + /*******************************************/ + /*** Operations with TIME, DT and TOD... ***/ + /*******************************************/ + /*******************************************/ +#define __multime(TYPE) \ + { &search_constant_type_c::time_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, \ + { &search_constant_type_c::safetime_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, \ + { &search_constant_type_c::time_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, \ + { &search_constant_type_c::safetime_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safetime_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, \ + /* NOTE: the standard des not explicitly support the following semantics. However, since 'multiplication' is supposed to be commutative, we add it anyway... */ \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::time_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safetime_type_name, &search_constant_type_c::safetime_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + __ANY_NUM(__multime) +#undef __multime + + { NULL, NULL, NULL, widen_entry::ok }, +}; + + + + + +const struct widen_entry widen_DIV_table[] = { +#define __div(TYPE) \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, widen_entry::ok }, + __ANY_NUM(__div) +#undef __div + + /*******************************************/ + /*******************************************/ + /*** Operations with TIME, DT and TOD... ***/ + /*******************************************/ + /*******************************************/ +#define __divtime(TYPE) \ + { &search_constant_type_c::time_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, \ + { &search_constant_type_c::safetime_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, \ + { &search_constant_type_c::time_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::time_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, \ + { &search_constant_type_c::safetime_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safetime_type_name, ANYTIME_OPER_DEPRECATION_STATUS }, + __ANY_NUM(__divtime) +#undef __divtime + + { NULL, NULL, NULL, widen_entry::ok }, + }; + + + + +const struct widen_entry widen_MOD_table[] = { +#define __mod(TYPE) \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, widen_entry::ok }, + __ANY_NUM(__mod) +#undef __mod + + { NULL, NULL, NULL, widen_entry::ok }, +}; + + + + +const struct widen_entry widen_EXPT_table[] = { +#define __expt(IN2TYPE, IN1TYPE) \ + { &search_constant_type_c::IN1TYPE##_type_name, &search_constant_type_c::IN2TYPE##_type_name, &search_constant_type_c::IN1TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##IN1TYPE##_type_name, &search_constant_type_c::IN2TYPE##_type_name, &search_constant_type_c::IN1TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::IN1TYPE##_type_name, &search_constant_type_c::safe##IN2TYPE##_type_name, &search_constant_type_c::IN1TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##IN1TYPE##_type_name, &search_constant_type_c::safe##IN2TYPE##_type_name, &search_constant_type_c::safe##IN1TYPE##_type_name, widen_entry::ok }, +#define __IN2_anynum_(IN1_TYPENAME) __ANY_NUM_1(__expt,IN1_TYPENAME) + __ANY_REAL(__IN2_anynum_) +#undef __expt +#undef __IN2_anynum_ + { NULL, NULL, NULL, widen_entry::ok }, +}; + + + +/**************************************************************/ +/**************************************************************/ +/**************************************************************/ +/******* *******/ +/******* TABLE 26: Standard bitwise Boolean functions *******/ +/******* *******/ +/**************************************************************/ +/**************************************************************/ +/**************************************************************/ +/* table used by AND and ANDN operators, and and_expression */ +const struct widen_entry widen_AND_table[] = { +#define __and(TYPE) \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, widen_entry::ok }, + __ANY_BIT(__and) +#undef __and + + { NULL, NULL, NULL, widen_entry::ok }, +}; + +/* table used by OR and ORN operators, and or_expression */ +const struct widen_entry widen_OR_table[] = { +#define __or(TYPE) \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, widen_entry::ok }, + __ANY_BIT(__or) +#undef __or + + { NULL, NULL, NULL, widen_entry::ok }, +}; + + +/* table used by XOR and XORN operators, and xor_expression */ +const struct widen_entry widen_XOR_table[] = { +#define __xor(TYPE) \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, widen_entry::ok }, + __ANY_BIT(__xor) +#undef __xor + + { NULL, NULL, NULL, widen_entry::ok }, +}; + +/**************************************************************/ +/**************************************************************/ +/**************************************************************/ +/******* *******/ +/******* TABLE 28: Standard comparison functions *******/ +/******* *******/ +/**************************************************************/ +/**************************************************************/ +/**************************************************************/ +/* table used by GT, GE, EQ, LE, LT, and NE operators, and equivalent ST expressions. */ +const struct widen_entry widen_CMP_table[] = { +#define __cmp(TYPE) \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::bool_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::bool_type_name, widen_entry::ok }, \ + { &search_constant_type_c::TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::bool_type_name, widen_entry::ok }, \ + { &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safe##TYPE##_type_name, &search_constant_type_c::safebool_type_name, widen_entry::ok }, + __ANY_ELEMENTARY(__cmp) +#undef __cmp + + { NULL, NULL, NULL, widen_entry::ok }, +}; + + +/* Search for a datatype inside a candidate_datatypes list. + * Returns: position of datatype in the list, or -1 if not found. + */ +int search_in_candidate_datatype_list(symbol_c *datatype, const std::vector &candidate_datatypes) { + if (NULL == datatype) + return -1; + + for(unsigned int i = 0; i < candidate_datatypes.size(); i++) + if (is_type_equal(datatype, candidate_datatypes[i])) + return i; + /* Not found ! */ + return -1; +} + +/* Remove a datatype inside a candidate_datatypes list. + * Returns: If successful it returns true, false otherwise. + */ +bool remove_from_candidate_datatype_list(symbol_c *datatype, std::vector &candidate_datatypes) { + int pos = search_in_candidate_datatype_list(datatype, candidate_datatypes); + if (pos < 0) + return false; + + candidate_datatypes.erase(candidate_datatypes.begin() + pos); + return true; +} + + + +/* Intersect two candidate_datatype_lists. + * Remove from list1 (origin, dest.) all elements that are not found in list2 (with). + * In essence, list1 will contain the result of the intersection of list1 with list2. + * In other words, modify list1 so it only contains the elelements that are simultaneously in list1 and list2! + */ +void intersect_candidate_datatype_list(symbol_c *list1 /*origin, dest.*/, symbol_c *list2 /*with*/) { + if ((NULL == list1) || (NULL == list2)) + /* In principle, we should never call it with NULL values. Best to abort the compiler just in case! */ + return; + + for(std::vector::iterator i = list1->candidate_datatypes.begin(); i < list1->candidate_datatypes.end(); ) { + /* Note that we do _not_ increment i in the for() loop! + * When we erase an element from position i, a new element will take it's place, that must also be tested! + */ + if (search_in_candidate_datatype_list(*i, list2->candidate_datatypes) < 0) + /* remove this element! This will change the value of candidate_datatypes.size() */ + list1->candidate_datatypes.erase(i); + else i++; + } +} + + + + +/* intersect the candidate_datatype lists of all prev_il_intructions, and set the local candidate_datatype list to the result! */ +void intersect_prev_candidate_datatype_lists(il_instruction_c *symbol) { + if (symbol->prev_il_instruction.empty()) + return; + + symbol->candidate_datatypes = symbol->prev_il_instruction[0]->candidate_datatypes; + for (unsigned int i = 1; i < symbol->prev_il_instruction.size(); i++) { + intersect_candidate_datatype_list(symbol /*origin, dest.*/, symbol->prev_il_instruction[i] /*with*/); + } +} + + + + +/* A helper function... */ +bool is_ANY_ELEMENTARY_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + return is_ANY_MAGNITUDE_type(type_symbol) + || is_ANY_BIT_type (type_symbol) + || is_ANY_STRING_type (type_symbol) + || is_ANY_DATE_type (type_symbol); +} + +/* A helper function... */ +bool is_ANY_SAFEELEMENTARY_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + return is_ANY_SAFEMAGNITUDE_type(type_symbol) + || is_ANY_SAFEBIT_type (type_symbol) + || is_ANY_SAFESTRING_type (type_symbol) + || is_ANY_SAFEDATE_type (type_symbol); +} + +/* A helper function... */ +bool is_ANY_ELEMENTARY_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + /* NOTE: doing + * return is_ANY_SAFEELEMENTARY_type() || is_ANY_ELEMENTARY_type() + * is incorrect, as the literals would never be considered compatible... + */ + return is_ANY_MAGNITUDE_compatible(type_symbol) + || is_ANY_BIT_compatible (type_symbol) + || is_ANY_STRING_compatible (type_symbol) + || is_ANY_DATE_compatible (type_symbol); +} + + +/* A helper function... */ +bool is_ANY_MAGNITUDE_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(time_type_name_c)) {return true;} + return is_ANY_NUM_type(type_symbol); +} + +/* A helper function... */ +bool is_ANY_signed_MAGNITUDE_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(time_type_name_c)) {return true;} + return is_ANY_signed_NUM_type(type_symbol); +} + +/* A helper function... */ +bool is_ANY_SAFEMAGNITUDE_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(safetime_type_name_c)) {return true;} + return is_ANY_SAFENUM_type(type_symbol); +} + +/* A helper function... */ +bool is_ANY_signed_SAFEMAGNITUDE_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(safetime_type_name_c)) {return true;} + return is_ANY_signed_SAFENUM_type(type_symbol); +} + +/* A helper function... */ +bool is_ANY_MAGNITUDE_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_MAGNITUDE_type (type_symbol)) {return true;} + if (is_ANY_SAFEMAGNITUDE_type(type_symbol)) {return true;} + return is_ANY_NUM_compatible(type_symbol); +} + +/* A helper function... */ +bool is_ANY_signed_MAGNITUDE_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_signed_MAGNITUDE_type (type_symbol)) {return true;} + if (is_ANY_signed_SAFEMAGNITUDE_type(type_symbol)) {return true;} + return is_ANY_signed_NUM_compatible(type_symbol); +} + +/* A helper function... */ +bool is_ANY_NUM_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_REAL_type(type_symbol)) {return true;} + if (is_ANY_INT_type(type_symbol)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_signed_NUM_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_REAL_type(type_symbol)) {return true;} + if (is_ANY_signed_INT_type(type_symbol)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_SAFENUM_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + return is_ANY_SAFEREAL_type(type_symbol) + || is_ANY_SAFEINT_type (type_symbol); +} + +/* A helper function... */ +bool is_ANY_signed_SAFENUM_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + return is_ANY_SAFEREAL_type(type_symbol) + || is_ANY_signed_SAFEINT_type (type_symbol); +} + +/* A helper function... */ +bool is_ANY_NUM_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_REAL_compatible(type_symbol)) {return true;} + if (is_ANY_INT_compatible(type_symbol)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_signed_NUM_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_REAL_compatible(type_symbol)) {return true;} + if (is_ANY_signed_INT_compatible(type_symbol)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_DATE_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(date_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(tod_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(dt_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_SAFEDATE_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(safedate_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safetod_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safedt_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_DATE_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_DATE_type (type_symbol)) {return true;} + if (is_ANY_SAFEDATE_type(type_symbol)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_STRING_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(string_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(wstring_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_SAFESTRING_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(safestring_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safewstring_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_STRING_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_STRING_type (type_symbol)) {return true;} + if (is_ANY_SAFESTRING_type(type_symbol)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_INT_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(sint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(int_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(dint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(lint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(usint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(uint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(udint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(ulint_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_signed_INT_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(sint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(int_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(dint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(lint_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_signed_SAFEINT_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(safesint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safeint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safedint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safelint_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_SAFEINT_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(safesint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safeint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safedint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safelint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safeusint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safeuint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safeudint_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safeulint_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_signed_INT_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_signed_INT_type (type_symbol)) {return true;} + if (is_ANY_signed_SAFEINT_type(type_symbol)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_INT_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_INT_type (type_symbol)) {return true;} + if (is_ANY_SAFEINT_type(type_symbol)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_REAL_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(real_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(lreal_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_SAFEREAL_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(safereal_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safelreal_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_REAL_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_REAL_type (type_symbol)) {return true;} + if (is_ANY_SAFEREAL_type(type_symbol)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_BIT_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(bool_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(byte_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(word_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(dword_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(lword_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_SAFEBIT_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(safebool_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safebyte_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safeword_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safedword_type_name_c)) {return true;} + if (typeid(*type_symbol) == typeid(safelword_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_BIT_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_ANY_BIT_type (type_symbol)) {return true;} + if (is_ANY_SAFEBIT_type(type_symbol)) {return true;} + return false; +} + +/* A helper function... */ +bool is_BOOL_type(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(bool_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_SAFEBOOL_type(symbol_c *type_symbol){ + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(safebool_type_name_c)) {return true;} + return false; +} + +/* A helper function... */ +bool is_ANY_BOOL_compatible(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (is_BOOL_type (type_symbol)) {return true;} + if (is_SAFEBOOL_type(type_symbol)) {return true;} + return false; +} + + + + + +bool is_type_equal(symbol_c *first_type, symbol_c *second_type) { + if ((NULL == first_type) || (NULL == second_type)) + return false; + if (typeid(* first_type) == typeid(invalid_type_name_c)) + return false; + if (typeid(*second_type) == typeid(invalid_type_name_c)) + return false; + + if (is_ANY_ELEMENTARY_type(first_type)) { + if (typeid(*first_type) == typeid(*second_type)) + return true; + } else /* ANY_DERIVED */ + return (first_type == second_type); + + return false; +} + + + +bool is_type_valid(symbol_c *type) { + if (NULL == type) + return false; + if (typeid(*type) == typeid(invalid_type_name_c)) + return false; + + return true; +} diff -r aad38592bdde -r c0bda77b37a0 stage3/datatype_functions.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/datatype_functions.hh Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,220 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (conti.ma@alice.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +#ifndef _HELPER_FUNCTIONS_HH_ +#define _HELPER_FUNCTIONS_HH_ + +#include "../absyntax/visitor.hh" +#include + + + + + +typedef struct { + symbol_c *function_name; + symbol_c *nonformal_operand_list; + symbol_c * formal_operand_list; + + enum {POU_FB, POU_function} POU_type; +//symbol_c &*datatype; +//std::vector &candidate_datatypes; + std::vector &candidate_functions; + symbol_c *&called_function_declaration; + int &extensible_param_count; +} generic_function_call_t; + + + +/* A small helper class, to transform elementary data type to string. + * this allows us to generate more relevant error messages... + */ + +class elementary_type_c: public null_visitor_c { + protected: + elementary_type_c(void) {}; + ~elementary_type_c(void) {}; + + private: + /* singleton class! */ + static elementary_type_c *singleton; + + public: + static const char * to_string(symbol_c *symbol); + + + /***********************************/ + /* B 1.3.1 - Elementary Data Types */ + /***********************************/ + void *visit(time_type_name_c *symbol) {return (void *)"TIME"; }; + void *visit(bool_type_name_c *symbol) {return (void *)"BOOL"; }; + void *visit(sint_type_name_c *symbol) {return (void *)"SINT"; }; + void *visit(int_type_name_c *symbol) {return (void *)"INT"; }; + void *visit(dint_type_name_c *symbol) {return (void *)"DINT"; }; + void *visit(lint_type_name_c *symbol) {return (void *)"LINT"; }; + void *visit(usint_type_name_c *symbol) {return (void *)"USINT"; }; + void *visit(uint_type_name_c *symbol) {return (void *)"UINT"; }; + void *visit(udint_type_name_c *symbol) {return (void *)"UDINT"; }; + void *visit(ulint_type_name_c *symbol) {return (void *)"ULINT"; }; + void *visit(real_type_name_c *symbol) {return (void *)"REAL"; }; + void *visit(lreal_type_name_c *symbol) {return (void *)"LREAL"; }; + void *visit(date_type_name_c *symbol) {return (void *)"DATE"; }; + void *visit(tod_type_name_c *symbol) {return (void *)"TOD"; }; + void *visit(dt_type_name_c *symbol) {return (void *)"DT"; }; + void *visit(byte_type_name_c *symbol) {return (void *)"BYTE"; }; + void *visit(word_type_name_c *symbol) {return (void *)"WORD"; }; + void *visit(lword_type_name_c *symbol) {return (void *)"LWORD"; }; + void *visit(dword_type_name_c *symbol) {return (void *)"DWORD"; }; + void *visit(string_type_name_c *symbol) {return (void *)"STRING"; }; + void *visit(wstring_type_name_c *symbol) {return (void *)"WSTRING"; }; + + void *visit(safetime_type_name_c *symbol) {return (void *)"SAFETIME"; }; + void *visit(safebool_type_name_c *symbol) {return (void *)"SAFEBOOL"; }; + void *visit(safesint_type_name_c *symbol) {return (void *)"SAFESINT"; }; + void *visit(safeint_type_name_c *symbol) {return (void *)"SAFEINT"; }; + void *visit(safedint_type_name_c *symbol) {return (void *)"SAFEDINT"; }; + void *visit(safelint_type_name_c *symbol) {return (void *)"SAFELINT"; }; + void *visit(safeusint_type_name_c *symbol) {return (void *)"SAFEUSINT"; }; + void *visit(safeuint_type_name_c *symbol) {return (void *)"SAFEUINT"; }; + void *visit(safeudint_type_name_c *symbol) {return (void *)"SAFEUDINT"; }; + void *visit(safeulint_type_name_c *symbol) {return (void *)"SAFEULINT"; }; + void *visit(safereal_type_name_c *symbol) {return (void *)"SAFEREAL"; }; + void *visit(safelreal_type_name_c *symbol) {return (void *)"SAFELREAL"; }; + void *visit(safedate_type_name_c *symbol) {return (void *)"SAFEDATE"; }; + void *visit(safetod_type_name_c *symbol) {return (void *)"SAFETOD"; }; + void *visit(safedt_type_name_c *symbol) {return (void *)"SAFEDT"; }; + void *visit(safebyte_type_name_c *symbol) {return (void *)"SAFEBYTE"; }; + void *visit(safeword_type_name_c *symbol) {return (void *)"SAFEWORD"; }; + void *visit(safelword_type_name_c *symbol) {return (void *)"SAFELWORD"; }; + void *visit(safedword_type_name_c *symbol) {return (void *)"SAFEDWORD"; }; + void *visit(safestring_type_name_c *symbol) {return (void *)"SAFESTRING"; }; + void *visit(safewstring_type_name_c *symbol) {return (void *)"SAFEWSTRING"; }; +}; + + + + + + +/* Widening Primitive Conversion */ +struct widen_entry { + symbol_c *left; + symbol_c *right; + symbol_c *result; + enum {ok, deprecated} status; +}; +/* + * 2.5.1.5.6 Functions of time data types + * Table 30 - page 64 + */ +extern const struct widen_entry widen_ADD_table[]; +extern const struct widen_entry widen_SUB_table[]; +extern const struct widen_entry widen_MUL_table[]; +extern const struct widen_entry widen_DIV_table[]; +extern const struct widen_entry widen_MOD_table[]; +extern const struct widen_entry widen_EXPT_table[]; +extern const struct widen_entry widen_AND_table[]; +extern const struct widen_entry widen_OR_table[]; +extern const struct widen_entry widen_XOR_table[]; +extern const struct widen_entry widen_CMP_table[]; + +/* Search for a datatype inside a candidate_datatypes list. + * Returns: position of datatype in the list, or -1 if not found. + */ +int search_in_candidate_datatype_list(symbol_c *datatype, const std::vector &candidate_datatypes); + +/* Remove a datatype inside a candidate_datatypes list. + * Returns: If successful it returns true, false otherwise. + */ +bool remove_from_candidate_datatype_list(symbol_c *datatype, std::vector &candidate_datatypes); + +/* Intersect two candidate_datatype_lists. + * Remove from list1 (origin, dest.) all elements that are not found in list2 (with). + * In essence, list1 will contain the result of the intersection of list1 with list2. + * In other words, modify list1 so it only contains the elelements that are simultaneously in list1 and list2! + */ +void intersect_candidate_datatype_list(symbol_c *list1 /*origin, dest.*/, symbol_c *list2 /*with*/); + +/* intersect the candidate_datatype lists of all prev_il_intructions, and set the local candidate_datatype list to the result! */ +void intersect_prev_candidate_datatype_lists(il_instruction_c *symbol); + + + +/* A helper function... */ +bool is_ANY_ELEMENTARY_type (symbol_c *type_symbol); +bool is_ANY_SAFEELEMENTARY_type (symbol_c *type_symbol); +bool is_ANY_ELEMENTARY_compatible (symbol_c *type_symbol); + +bool is_ANY_MAGNITUDE_type (symbol_c *type_symbol); +bool is_ANY_SAFEMAGNITUDE_type (symbol_c *type_symbol); +bool is_ANY_MAGNITUDE_compatible (symbol_c *type_symbol); + +bool is_ANY_signed_MAGNITUDE_type (symbol_c *type_symbol); +bool is_ANY_signed_SAFEMAGNITUDE_type (symbol_c *type_symbol); +bool is_ANY_signed_MAGNITUDE_compatible (symbol_c *type_symbol); + +bool is_ANY_DATE_type (symbol_c *type_symbol); +bool is_ANY_SAFEDATE_type (symbol_c *type_symbol); +bool is_ANY_DATE_compatible (symbol_c *type_symbol); + +bool is_ANY_STRING_type (symbol_c *type_symbol); +bool is_ANY_SAFESTRING_type (symbol_c *type_symbol); +bool is_ANY_STRING_compatible (symbol_c *type_symbol); + +bool is_ANY_INT_type (symbol_c *type_symbol); +bool is_ANY_SAFEINT_type (symbol_c *type_symbol); +bool is_ANY_INT_compatible (symbol_c *type_symbol); + +bool is_ANY_signed_INT_type (symbol_c *type_symbol); +bool is_ANY_signed_SAFEINT_type (symbol_c *type_symbol); +bool is_ANY_signed_INT_compatible (symbol_c *type_symbol); + +bool is_ANY_REAL_type (symbol_c *type_symbol); +bool is_ANY_SAFEREAL_type (symbol_c *type_symbol); +bool is_ANY_REAL_compatible (symbol_c *type_symbol); + +bool is_ANY_NUM_type (symbol_c *type_symbol); +bool is_ANY_SAFENUM_type (symbol_c *type_symbol); +bool is_ANY_NUM_compatible (symbol_c *type_symbol); + +bool is_ANY_signed_NUM_type (symbol_c *type_symbol); +bool is_ANY_signed_SAFENUM_type (symbol_c *type_symbol); +bool is_ANY_signed_NUM_compatible (symbol_c *type_symbol); + +bool is_ANY_BIT_type (symbol_c *type_symbol); +bool is_ANY_SAFEBIT_type (symbol_c *type_symbol); +bool is_ANY_BIT_compatible (symbol_c *type_symbol); + +bool is_BOOL_type (symbol_c *type_symbol); +bool is_SAFEBOOL_type (symbol_c *type_symbol); +bool is_ANY_BOOL_compatible (symbol_c *type_symbol); + + +bool is_type_equal(symbol_c *first_type, symbol_c *second_type); +bool is_type_valid(symbol_c *type); + + + + +#endif /* _HELPER_FUNCTIONS_HH_ */ diff -r aad38592bdde -r c0bda77b37a0 stage3/fill_candidate_datatypes.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/fill_candidate_datatypes.cc Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,1571 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (manuele.conti@sirius-es.it) + * Copyright (C) 2012 Matteo Facchinetti (matteo.facchinetti@sirius-es.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* TODO - things yet not checked by this data type checker... + * + * - check variable declarations + * - check data type declarations + * - check inside configurations (variable declarations) + * - check SFC code + * - must fix S and R IL functions (includes potientialy fixing stage4 code!) + */ + + +/* NOTE: The algorithm implemented here assumes that flow control analysis has already been completed! + * BEFORE running this visitor, be sure to CALL the flow_control_analysis_c visitor! + */ + + +/* + * Fill the candidate datatype list for all symbols that may legally 'have' a data type (e.g. variables, literals, function calls, expressions, etc.) + * + * The candidate datatype list will be filled with a list of all the data types that expression may legally take. + * For example, the very simple literal '0' (as in foo := 0), may represent a: + * BOOL, BYTE, WORD, DWORD, LWORD, USINT, SINT, UINT, INT, UDINT, DINT, ULINT, LINT (as well as the SAFE versions of these data tyes too!) + */ + +#include <../main.hh> /* required for UINT64_MAX, INT64_MAX, INT64_MIN, ... */ +#include "fill_candidate_datatypes.hh" +#include "datatype_functions.hh" +#include +#include +#include +#include +#include + +#define GET_CVALUE(dtype, symbol) ((symbol)->const_value._##dtype.value) +#define VALID_CVALUE(dtype, symbol) (symbol_c::cs_const_value == (symbol)->const_value._##dtype.status) +#define IS_OVERFLOW(dtype, symbol) (symbol_c::cs_overflow == (symbol)->const_value._##dtype.status) + +/* set to 1 to see debug info during execution */ +static int debug = 0; + +fill_candidate_datatypes_c::fill_candidate_datatypes_c(symbol_c *ignore) { +} + +fill_candidate_datatypes_c::~fill_candidate_datatypes_c(void) { +} + +symbol_c *fill_candidate_datatypes_c::widening_conversion(symbol_c *left_type, symbol_c *right_type, const struct widen_entry widen_table[]) { + int k; + /* find a widening table entry compatible */ + for (k = 0; NULL != widen_table[k].left; k++) + if ((typeid(*left_type) == typeid(*widen_table[k].left)) && (typeid(*right_type) == typeid(*widen_table[k].right))) + return widen_table[k].result; + return NULL; +} + + +/* add a data type to a candidate data type list, while guaranteeing no duplicate entries! */ +/* Returns true if it really did add the datatype to the list, or false if it was already present in the list! */ +bool fill_candidate_datatypes_c::add_datatype_to_candidate_list(symbol_c *symbol, symbol_c *datatype) { + /* If it is an invalid data type, do not insert! + * NOTE: it reduces overall code size to do this test here, instead of doing every time before calling the add_datatype_to_candidate_list() function. + */ + if (!is_type_valid(datatype)) /* checks for NULL and invalid_type_name_c */ + return false; + + if (search_in_candidate_datatype_list(datatype, symbol->candidate_datatypes) >= 0) + /* already in the list, Just return! */ + return false; + + /* not yet in the candidate data type list, so we insert it now! */ + symbol->candidate_datatypes.push_back(datatype); + return true; +} + + +bool fill_candidate_datatypes_c::add_2datatypes_to_candidate_list(symbol_c *symbol, symbol_c *datatype1, symbol_c *datatype2) { + add_datatype_to_candidate_list(symbol, datatype1); + add_datatype_to_candidate_list(symbol, datatype2); + return true; +} + + + +void fill_candidate_datatypes_c::remove_incompatible_datatypes(symbol_c *symbol) { + #ifdef __REMOVE__ + #error __REMOVE__ macro already exists. Choose another name! + #endif + #define __REMOVE__(datatype)\ + remove_from_candidate_datatype_list(&search_constant_type_c::datatype, symbol->candidate_datatypes);\ + remove_from_candidate_datatype_list(&search_constant_type_c::safe##datatype, symbol->candidate_datatypes); + + {/* Remove unsigned data types */ + uint64_t value = 0; + if (VALID_CVALUE( uint64, symbol)) value = GET_CVALUE(uint64, symbol); + if (IS_OVERFLOW ( uint64, symbol)) value = (uint64_t)UINT32_MAX + (uint64_t)1; + + if (value > 1 ) {__REMOVE__(bool_type_name);} + if (value > UINT8_MAX ) {__REMOVE__(usint_type_name); __REMOVE__( byte_type_name);} + if (value > UINT16_MAX ) {__REMOVE__( uint_type_name); __REMOVE__( word_type_name);} + if (value > UINT32_MAX ) {__REMOVE__(udint_type_name); __REMOVE__(dword_type_name);} + if (IS_OVERFLOW( uint64, symbol)) {__REMOVE__(ulint_type_name); __REMOVE__(lword_type_name);} + } + + {/* Remove signed data types */ + int64_t value = 0; + if (VALID_CVALUE( int64, symbol)) value = GET_CVALUE(int64, symbol); + if (IS_OVERFLOW ( int64, symbol)) value = (int64_t)INT32_MAX + (int64_t)1; + + if ((value < INT8_MIN) || (value > INT8_MAX)) {__REMOVE__(sint_type_name);} + if ((value < INT16_MIN) || (value > INT16_MAX)) {__REMOVE__( int_type_name);} + if ((value < INT32_MIN) || (value > INT32_MAX)) {__REMOVE__(dint_type_name);} + if (IS_OVERFLOW( int64, symbol)) {__REMOVE__(lint_type_name);} + } + + {/* Remove floating point data types */ + real64_t value = 0; + if (VALID_CVALUE( real64, symbol)) value = GET_CVALUE(real64, symbol); + if (IS_OVERFLOW ( real64, symbol)) value = (real64_t)REAL32_MAX + (real64_t)1; + if (value > REAL32_MAX ) {__REMOVE__( real_type_name);} + if (value < -REAL32_MAX ) {__REMOVE__( real_type_name);} + if (IS_OVERFLOW( real64, symbol)) {__REMOVE__(lreal_type_name);} + } + #undef __REMOVE__ +} + + +/* returns true if compatible function/FB invocation, otherwise returns false */ +/* Assumes that the candidate_datatype lists of all the parameters being passed haved already been filled in */ +/* + * All parameters being passed to the called function MUST be in the parameter list to which f_call points to! + * This means that, for non formal function calls in IL, de current (default value) must be artificially added to the + * beginning of the parameter list BEFORE calling handle_function_call(). + */ +bool fill_candidate_datatypes_c::match_nonformal_call(symbol_c *f_call, symbol_c *f_decl) { + symbol_c *call_param_value, *param_datatype; + identifier_c *param_name; + function_param_iterator_c fp_iterator(f_decl); + function_call_param_iterator_c fcp_iterator(f_call); + int extensible_parameter_highest_index = -1; + unsigned int i; + + /* Iterating through the non-formal parameters of the function call */ + while((call_param_value = fcp_iterator.next_nf()) != NULL) { + /* Iterate to the next parameter of the function being called. + * Get the name of that parameter, and ignore if EN or ENO. + */ + do { + param_name = fp_iterator.next(); + /* If there is no other parameter declared, then we are passing too many parameters... */ + if(param_name == NULL) return false; + } while ((strcmp(param_name->value, "EN") == 0) || (strcmp(param_name->value, "ENO") == 0)); + + /* TODO: verify if it is lvalue when INOUT or OUTPUT parameters! */ + /* Get the parameter type */ + param_datatype = base_type(fp_iterator.param_type()); + + /* check whether one of the candidate_data_types of the value being passed is the same as the param_type */ + if (search_in_candidate_datatype_list(param_datatype, call_param_value->candidate_datatypes) < 0) + return false; /* return false if param_type not in the list! */ + } + /* call is compatible! */ + return true; +} + + + +/* returns true if compatible function/FB invocation, otherwise returns false */ +/* Assumes that the candidate_datatype lists of all the parameters being passed haved already been filled in */ +bool fill_candidate_datatypes_c::match_formal_call(symbol_c *f_call, symbol_c *f_decl, symbol_c **first_param_datatype) { + symbol_c *call_param_value, *call_param_name, *param_datatype; + symbol_c *verify_duplicate_param; + identifier_c *param_name; + function_param_iterator_c fp_iterator(f_decl); + function_call_param_iterator_c fcp_iterator(f_call); + int extensible_parameter_highest_index = -1; + identifier_c *extensible_parameter_name; + unsigned int i; + bool is_first_param = true; + + /* Iterating through the formal parameters of the function call */ + while((call_param_name = fcp_iterator.next_f()) != NULL) { + /* Obtaining the value being passed in the function call */ + call_param_value = fcp_iterator.get_current_value(); + /* the following should never occur. If it does, then we have a bug in our code... */ + if (NULL == call_param_value) ERROR; + + /* Obtaining the assignment direction: := (assign_in) or => (assign_out) */ + function_call_param_iterator_c::assign_direction_t call_param_dir = fcp_iterator.get_assign_direction(); + + /* Checking if there are duplicated parameter values */ + verify_duplicate_param = fcp_iterator.search_f(call_param_name); + if(verify_duplicate_param != call_param_value) + return false; + + /* Obtaining the type of the value being passed in the function call */ + std::vector &call_param_types = call_param_value->candidate_datatypes; + + /* Find the corresponding parameter in function declaration */ + param_name = fp_iterator.search(call_param_name); + if(param_name == NULL) return false; + /* Get the parameter data type */ + param_datatype = base_type(fp_iterator.param_type()); + /* Get the parameter direction: IN, OUT, IN_OUT */ + function_param_iterator_c::param_direction_t param_dir = fp_iterator.param_direction(); + + /* check whether direction (IN, OUT, IN_OUT) and assignment types (:= , =>) are compatible !!! */ + if (function_call_param_iterator_c::assign_in == call_param_dir) { + if ((function_param_iterator_c::direction_in != param_dir) && + (function_param_iterator_c::direction_inout != param_dir)) + return false; + } else if (function_call_param_iterator_c::assign_out == call_param_dir) { + if ((function_param_iterator_c::direction_out != param_dir)) + return false; + } else ERROR; + + /* check whether one of the candidate_data_types of the value being passed is the same as the param_type */ + if (search_in_candidate_datatype_list(param_datatype, call_param_types) < 0) + return false; /* return false if param_type not in the list! */ + + /* If this is the first parameter, then copy the datatype to *first_param_datatype */ + if (is_first_param) + if (NULL != first_param_datatype) + *first_param_datatype = param_datatype; + is_first_param = false; + } + /* call is compatible! */ + return true; +} + + + + +/* Handle a generic function call! + * Assumes that the parameter_list containing the values being passed in this function invocation + * has already had all the candidate_datatype lists filled in! + * + * All parameters being passed to the called function MUST be in the parameter list to which f_call points to! + * This means that, for non formal function calls in IL, de current (default value) must be artificially added to the + * beginning of the parameter list BEFORE calling handle_function_call(). + */ +/* +typedef struct { + symbol_c *function_name, + symbol_c *nonformal_operand_list, + symbol_c * formal_operand_list, + + std::vector &candidate_functions, + symbol_c &*called_function_declaration, + int &extensible_param_count +} generic_function_call_t; +*/ +/* +void narrow_candidate_datatypes_c::narrow_function_invocation(symbol_c *fcall, generic_function_call_t fcall_data) { +void *fill_candidate_datatypes_c::handle_function_call(symbol_c *f_call, symbol_c *function_name, invocation_type_t invocation_type, + std::vector *candidate_datatypes, + std::vector *candidate_functions) { + */ +void fill_candidate_datatypes_c::handle_function_call(symbol_c *fcall, generic_function_call_t fcall_data) { + function_declaration_c *f_decl; + list_c *parameter_list; + list_c *parameter_candidate_datatypes; + symbol_c *returned_parameter_type; + + if (debug) std::cout << "function()\n"; + + function_symtable_t::iterator lower = function_symtable.lower_bound(fcall_data.function_name); + function_symtable_t::iterator upper = function_symtable.upper_bound(fcall_data.function_name); + /* If the name of the function being called is not found in the function symbol table, then this is an invalid call */ + /* Since the lexical parser already checks for this, then if this occurs then we have an internal compiler error. */ + if (lower == function_symtable.end()) ERROR; + + /* Look for all compatible function declarations, and add their return datatypes + * to the candidate_datatype list of this function invocation. + * + * If only one function exists, we add its return datatype to the candidate_datatype list, + * even if the parameters passed to it are invalid. + * This guarantees that the remainder of the expression in which the function call is inserted + * is treated as if the function call returns correctly, and therefore does not generate + * spurious error messages. + * Even if the parameters to the function call are invalid, doing this is still safe, as the + * expressions inside the function call will themselves have erros and will guarantee that + * compilation is aborted in stage3 (in print_datatypes_error_c). + */ + if (function_symtable.multiplicity(fcall_data.function_name) == 1) { + f_decl = function_symtable.get_value(lower); + returned_parameter_type = base_type(f_decl->type_name); + if (add_datatype_to_candidate_list(fcall, returned_parameter_type)) + /* we only add it to the function declaration list if this entry was not already present in the candidate datatype list! */ + fcall_data.candidate_functions.push_back(f_decl); + + } + for(; lower != upper; lower++) { + bool compatible = false; + + f_decl = function_symtable.get_value(lower); + /* Check if function declaration in symbol_table is compatible with parameters */ + if (NULL != fcall_data.nonformal_operand_list) compatible=match_nonformal_call(fcall, f_decl); + if (NULL != fcall_data. formal_operand_list) compatible= match_formal_call(fcall, f_decl); + if (compatible) { + /* Add the data type returned by the called functions. + * However, only do this if this data type is not already present in the candidate_datatypes list_c + */ + returned_parameter_type = base_type(f_decl->type_name); + if (add_datatype_to_candidate_list(fcall, returned_parameter_type)) + /* we only add it to the function declaration list if this entry was not already present in the candidate datatype list! */ + fcall_data.candidate_functions.push_back(f_decl); + } + } + if (debug) std::cout << "end_function() [" << fcall->candidate_datatypes.size() << "] result.\n"; + return; +} + + +/* handle implicit FB call in IL. + * e.g. CLK ton_var + * CU counter_var + */ +void *fill_candidate_datatypes_c::handle_implicit_il_fb_call(symbol_c *il_instruction, const char *param_name, symbol_c *&called_fb_declaration) { + symbol_c *fb_type_id = search_varfb_instance_type->get_basetype_id(il_operand); + /* Although a call to a non-declared FB is a semantic error, this is currently caught by stage 2! */ + if (NULL == fb_type_id) ERROR; + + function_block_declaration_c *fb_decl = function_block_type_symtable.find_value(fb_type_id); + if (function_block_type_symtable.end_value() == fb_decl) + /* The il_operand is not the name of a FB instance. Most probably it is the name of a variable of some other type. + * this is a semantic error. + */ + fb_decl = NULL; + + /* The narrow_candidate_datatypes_c does not rely on this called_fb_declaration pointer being == NULL to conclude that + * we have a datatype incompatibility error, so we set it to fb_decl to allow the print_datatype_error_c to print out + * more informative error messages! + */ + called_fb_declaration = fb_decl; + + /* This implicit FB call does not change the value stored in the current/default IL variable */ + /* It does, however, require that the datatype be compatible with the input parameter of the FB being called. + * If we were to follow the filling & narrowing algorithm correctly (implemented in fill_candidate_datatypes_c + * & narrow_candidate_datatypes_c respectively), we should be restricting the candidate_datatpes to the datatypes + * that are compatible to the FB call. + * However, doing the above will often result in some very confusing error messages for the user, especially in the case + * in which the FB call is wrong, so the resulting cadidate datatypes is an empty list. In this case, the user would see + * many error messages related to the IL instructions that follow the FB call, even though those IL instructions may be perfectly + * correct. + * For now, we will simply let the narrow_candidate_datatypes_c verify if the datatypes are compatible (something that should be done + * here). + */ + if (NULL != prev_il_instruction) + il_instruction->candidate_datatypes = prev_il_instruction->candidate_datatypes; + + if (debug) std::cout << "handle_implicit_il_fb_call() [" << prev_il_instruction->candidate_datatypes.size() << "] ==> " << il_instruction->candidate_datatypes.size() << " result.\n"; + return NULL; +} + + + + +/* handle a binary IL operator, like ADD, SUB, etc... */ +void *fill_candidate_datatypes_c::handle_binary_operator(const struct widen_entry widen_table[], symbol_c *symbol, symbol_c *l_expr, symbol_c *r_expr) { + if (NULL == l_expr) /* if no prev_il_instruction */ + return NULL; + + for(unsigned int i = 0; i < l_expr->candidate_datatypes.size(); i++) + for(unsigned int j = 0; j < r_expr->candidate_datatypes.size(); j++) + /* NOTE: add_datatype_to_candidate_list() will only really add the datatype if it is != NULL !!! */ + add_datatype_to_candidate_list(symbol, widening_conversion(l_expr->candidate_datatypes[i], r_expr->candidate_datatypes[j], widen_table)); + remove_incompatible_datatypes(symbol); + if (debug) std::cout << "[" << l_expr->candidate_datatypes.size() << "," << r_expr->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + + +/* handle a binary ST expression, like '+', '-', etc... */ +void *fill_candidate_datatypes_c::handle_binary_expression(const struct widen_entry widen_table[], symbol_c *symbol, symbol_c *l_expr, symbol_c *r_expr) { + l_expr->accept(*this); + r_expr->accept(*this); + return handle_binary_operator(widen_table, symbol, l_expr, r_expr); +} + + + + +/* a helper function... */ +symbol_c *fill_candidate_datatypes_c::base_type(symbol_c *symbol) { + /* NOTE: symbol == NULL is valid. It will occur when, for e.g., an undefined/undeclared symbolic_variable is used + * in the code. + */ + if (symbol == NULL) return NULL; + return (symbol_c *)symbol->accept(search_base_type); +} + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ +#define sizeoftype(symbol) get_sizeof_datatype_c::getsize(symbol) + +void *fill_candidate_datatypes_c::handle_any_integer(symbol_c *symbol) { + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::bool_type_name, &search_constant_type_c::safebool_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::byte_type_name, &search_constant_type_c::safebyte_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::word_type_name, &search_constant_type_c::safeword_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::dword_type_name, &search_constant_type_c::safedword_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::lword_type_name, &search_constant_type_c::safelword_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::sint_type_name, &search_constant_type_c::safesint_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::int_type_name, &search_constant_type_c::safeint_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::dint_type_name, &search_constant_type_c::safedint_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::lint_type_name, &search_constant_type_c::safelint_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::usint_type_name, &search_constant_type_c::safeusint_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::uint_type_name, &search_constant_type_c::safeuint_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::udint_type_name, &search_constant_type_c::safeudint_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::ulint_type_name, &search_constant_type_c::safeulint_type_name); + remove_incompatible_datatypes(symbol); + if (debug) std::cout << "ANY_INT [" << symbol->candidate_datatypes.size()<< "]" << std::endl; + return NULL; +} + + + +void *fill_candidate_datatypes_c::handle_any_real(symbol_c *symbol) { + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::real_type_name, &search_constant_type_c::safereal_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::lreal_type_name, &search_constant_type_c::safelreal_type_name); + remove_incompatible_datatypes(symbol); + if (debug) std::cout << "ANY_REAL [" << symbol->candidate_datatypes.size() << "]" << std::endl; + return NULL; +} + + + +void *fill_candidate_datatypes_c::handle_any_literal(symbol_c *symbol, symbol_c *symbol_value, symbol_c *symbol_type) { + symbol_value->accept(*this); + if (search_in_candidate_datatype_list(symbol_type, symbol_value->candidate_datatypes) >= 0) + add_datatype_to_candidate_list(symbol, symbol_type); + remove_incompatible_datatypes(symbol); + if (debug) std::cout << "XXX_LITERAL [" << symbol->candidate_datatypes.size() << "]\n"; + return NULL; +} + + + +void *fill_candidate_datatypes_c::visit( real_c *symbol) {return handle_any_real(symbol);} +void *fill_candidate_datatypes_c::visit(neg_real_c *symbol) {return handle_any_real(symbol);} + + + +void *fill_candidate_datatypes_c::visit(neg_integer_c *symbol) { + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::int_type_name, &search_constant_type_c::safeint_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::sint_type_name, &search_constant_type_c::safesint_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::dint_type_name, &search_constant_type_c::safedint_type_name); + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::lint_type_name, &search_constant_type_c::safelint_type_name); + remove_incompatible_datatypes(symbol); + if (debug) std::cout << "neg ANY_INT [" << symbol->candidate_datatypes.size() << "]" << std::endl; + return NULL; +} + + + +void *fill_candidate_datatypes_c::visit(integer_c *symbol) {return handle_any_integer(symbol);} +void *fill_candidate_datatypes_c::visit(binary_integer_c *symbol) {return handle_any_integer(symbol);} +void *fill_candidate_datatypes_c::visit(octal_integer_c *symbol) {return handle_any_integer(symbol);} +void *fill_candidate_datatypes_c::visit(hex_integer_c *symbol) {return handle_any_integer(symbol);} + + + +// SYM_REF2(integer_literal_c, type, value) +/* + * integer_literal: + * integer_type_name '#' signed_integer + * | integer_type_name '#' binary_integer + * | integer_type_name '#' octal_integer + * | integer_type_name '#' hex_integer + */ +void *fill_candidate_datatypes_c::visit( integer_literal_c *symbol) {return handle_any_literal(symbol, symbol->value, symbol->type);} +void *fill_candidate_datatypes_c::visit( real_literal_c *symbol) {return handle_any_literal(symbol, symbol->value, symbol->type);} +void *fill_candidate_datatypes_c::visit(bit_string_literal_c *symbol) {return handle_any_literal(symbol, symbol->value, symbol->type);} + +void *fill_candidate_datatypes_c::visit( boolean_literal_c *symbol) { + if (NULL != symbol->type) return handle_any_literal(symbol, symbol->value, symbol->type); + + symbol->value->accept(*this); + symbol->candidate_datatypes = symbol->value->candidate_datatypes; + return NULL; +} + + +void *fill_candidate_datatypes_c::visit(boolean_true_c *symbol) { + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::bool_type_name, &search_constant_type_c::safebool_type_name); + return NULL; +} + +void *fill_candidate_datatypes_c::visit(boolean_false_c *symbol) { + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::bool_type_name, &search_constant_type_c::safebool_type_name); + return NULL; +} + +/*******************************/ +/* B.1.2.2 Character Strings */ +/*******************************/ +void *fill_candidate_datatypes_c::visit(double_byte_character_string_c *symbol) { + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::wstring_type_name, &search_constant_type_c::safewstring_type_name); + return NULL; +} + +void *fill_candidate_datatypes_c::visit(single_byte_character_string_c *symbol) { + add_2datatypes_to_candidate_list(symbol, &search_constant_type_c::string_type_name, &search_constant_type_c::safestring_type_name); + return NULL; +} + +/***************************/ +/* B 1.2.3 - Time Literals */ +/***************************/ +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ +void *fill_candidate_datatypes_c::visit(duration_c *symbol) { + /* TODO: check whether the literal follows the rules specified in section '2.2.3.1 Duration' of the standard! */ + + add_datatype_to_candidate_list(symbol, symbol->type_name); + if (debug) std::cout << "TIME_LITERAL [" << symbol->candidate_datatypes.size() << "]\n"; + return NULL; +} + +/************************************/ +/* B 1.2.3.2 - Time of day and Date */ +/************************************/ +void *fill_candidate_datatypes_c::visit(time_of_day_c *symbol) {add_datatype_to_candidate_list(symbol, symbol->type_name); return NULL;} +void *fill_candidate_datatypes_c::visit(date_c *symbol) {add_datatype_to_candidate_list(symbol, symbol->type_name); return NULL;} +void *fill_candidate_datatypes_c::visit(date_and_time_c *symbol) {add_datatype_to_candidate_list(symbol, symbol->type_name); return NULL;} + +/**********************/ +/* B 1.3 - Data types */ +/**********************/ +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ + +/* simple_specification ASSIGN constant */ +// SYM_REF2(simple_spec_init_c, simple_specification, constant) +void *fill_candidate_datatypes_c::visit(simple_spec_init_c *symbol) { + if (NULL != symbol->constant) symbol->constant->accept(*this); + add_datatype_to_candidate_list(symbol->simple_specification, base_type(symbol->simple_specification)); + symbol->candidate_datatypes = symbol->simple_specification->candidate_datatypes; + /* NOTE: Even if the constant and the type are of incompatible data types, we let the + * simple_spec_init_c object inherit the data type of the type declaration (simple_specification) + * This will let us produce more informative error messages when checking data type compatibility + * with located variables (AT %QW3.4 : WORD). + */ + // if (NULL != symbol->constant) intersect_candidate_datatype_list(symbol /*origin, dest.*/, symbol->constant /*with*/); + return NULL; +} + +/* signed_integer DOTDOT signed_integer */ +// SYM_REF2(subrange_c, lower_limit, upper_limit) +void *fill_candidate_datatypes_c::visit(subrange_c *symbol) { + symbol->lower_limit->accept(*this); + symbol->upper_limit->accept(*this); + + for (unsigned int u = 0; u < symbol->upper_limit->candidate_datatypes.size(); u++) { + for(unsigned int l = 0; l < symbol->lower_limit->candidate_datatypes.size(); l++) { + if (is_type_equal(symbol->upper_limit->candidate_datatypes[u], symbol->lower_limit->candidate_datatypes[l])) + add_datatype_to_candidate_list(symbol, symbol->lower_limit->candidate_datatypes[l]); + } + } + return NULL; +} + +/* TYPE type_declaration_list END_TYPE */ +// SYM_REF1(data_type_declaration_c, type_declaration_list) +/* NOTE: Not required. already handled by iterator_visitor_c base class */ +/* +void *fill_candidate_datatypes_c::visit(data_type_declaration_c *symbol) { + symbol->type_declaration_list->accept(*this); + return NULL; +} +*/ + +void *fill_candidate_datatypes_c::visit(enumerated_value_c *symbol) { + symbol_c *enumerated_type; + + if (NULL != symbol->type) + enumerated_type = symbol->type; + else { + enumerated_type = enumerated_value_symtable.find_value(symbol->value); + if (enumerated_type == enumerated_value_symtable.end_value()) + enumerated_type = NULL; + } + enumerated_type = base_type(enumerated_type); + if (NULL != enumerated_type) + add_datatype_to_candidate_list(symbol, enumerated_type); + + if (debug) std::cout << "ENUMERATE [" << symbol->candidate_datatypes.size() << "]\n"; + return NULL; +} + + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ +void *fill_candidate_datatypes_c::visit(symbolic_variable_c *symbol) { + add_datatype_to_candidate_list(symbol, search_varfb_instance_type->get_basetype_decl(symbol)); /* will only add if non NULL */ + if (debug) std::cout << "VAR [" << symbol->candidate_datatypes.size() << "]\n"; + return NULL; +} + + +/********************************************/ +/* B 1.4.1 - Directly Represented Variables */ +/********************************************/ +void *fill_candidate_datatypes_c::visit(direct_variable_c *symbol) { + /* Comment added by mario: + * The following code is safe, actually, as the lexical parser guarantees the correct IEC61131-3 syntax was used. + */ + /* However, we should probably add an assertion in case we later change the lexical parser! */ + /* if (symbol->value == NULL) ERROR; + * if (symbol->value[0] == '\0') ERROR; + * if (symbol->value[1] == '\0') ERROR; + */ + switch (symbol->value[2]) { + case 'x': case 'X': /* bit - 1 bit */ add_datatype_to_candidate_list(symbol, &search_constant_type_c::bool_type_name); break; + case 'b': case 'B': /* byte - 8 bits */ add_datatype_to_candidate_list(symbol, &search_constant_type_c::byte_type_name); break; + case 'w': case 'W': /* word - 16 bits */ add_datatype_to_candidate_list(symbol, &search_constant_type_c::word_type_name); break; + case 'd': case 'D': /* dword - 32 bits */ add_datatype_to_candidate_list(symbol, &search_constant_type_c::dword_type_name); break; + case 'l': case 'L': /* lword - 64 bits */ add_datatype_to_candidate_list(symbol, &search_constant_type_c::lword_type_name); break; + /* if none of the above, then the empty string was used <=> boolean */ + default: add_datatype_to_candidate_list(symbol, &search_constant_type_c::bool_type_name); break; + } + return NULL; +} + +/*************************************/ +/* B 1.4.2 - Multi-element variables */ +/*************************************/ +/* subscripted_variable '[' subscript_list ']' */ +// SYM_REF2(array_variable_c, subscripted_variable, subscript_list) +void *fill_candidate_datatypes_c::visit(array_variable_c *symbol) { + /* get the declaration of the data type __stored__ in the array... */ + /* if we were to want the data type of the array itself, then we should call_param_name + * search_varfb_instance_type->get_basetype_decl(symbol->subscripted_variable) + */ + symbol_c *result = search_varfb_instance_type->get_basetype_decl(symbol); + if (NULL != result) add_datatype_to_candidate_list(symbol, result); + + /* recursively call the subscript list, so we can check the data types of the expressions used for the subscripts */ + symbol->subscript_list->accept(*this); + + if (debug) std::cout << "ARRAY_VAR [" << symbol->candidate_datatypes.size() << "]\n"; + return NULL; +} + + +/* subscript_list ',' subscript */ +// SYM_LIST(subscript_list_c) +/* NOTE: we inherit from iterator visitor, so we do not need to implement this method... */ +// void *fill_candidate_datatypes_c::visit(subscript_list_c *symbol) + + +/* record_variable '.' field_selector */ +/* WARNING: input and/or output variables of function blocks + * may be accessed as fields of a structured variable! + * Code handling a structured_variable_c must take + * this into account! + */ +// SYM_REF2(structured_variable_c, record_variable, field_selector) +/* NOTE: We do not need to recursively determine the data types of each field_selector, as the search_varfb_instance_type + * will do that for us. So we determine the candidate datatypes only for the full structured_variable. + */ +void *fill_candidate_datatypes_c::visit(structured_variable_c *symbol) { + add_datatype_to_candidate_list(symbol, search_varfb_instance_type->get_basetype_decl(symbol)); /* will only add if non NULL */ + return NULL; +} + + + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ + +void *fill_candidate_datatypes_c::visit(var1_list_c *symbol) { +#if 0 /* We don't really need to set the datatype of each variable. We just check the declaration itself! */ + for(int i = 0; i < symbol->n; i++) { + add_datatype_to_candidate_list(symbol->elements[i], search_varfb_instance_type->get_basetype_decl(symbol->elements[i])); /* will only add if non NULL */ + } +#endif + return NULL; +} + + +/* AT direct_variable */ +// SYM_REF1(location_c, direct_variable) +void *fill_candidate_datatypes_c::visit(location_c *symbol) { + /* This is a special situation. + * + * The reason is that a located variable may be declared to be of any data type, as long as the size + * matches the location (lines 1 3 and 4 of table 17). For example: + * var1 AT %MB42.0 : BYTE; + * var1 AT %MB42.1 : SINT; + * var1 AT %MB42.2 : USINT; + * var1 AT %MW64 : INT; + * var1 AT %MD56 : DINT; + * var1 AT %MD57 : REAL; + * are all valid!! + * + * However, when used inside an expression, the direct variable (uses the same syntax as the location + * of a located variable) is limited to the following (ANY_BIT) data types: + * %MX --> BOOL + * %MB --> BYTE + * %MW --> WORD + * %MD --> DWORD + * %ML --> LWORD + * + * So, in order to be able to analyse expressions with direct variables + * e.g: var1 := 66 OR %MW34 + * where the direct variable may only take the ANY_BIT data types, the fill_candidate_datatypes_c + * considers that only the ANY_BIT data types are allowed for a direct variable. + * However, it appears from the examples in the standard (lines 1 3 and 4 of table 17) + * a location may have any data type (presumably as long as the size in bits match). + * For this reason, a location_c may have more allowable data types than a direct_variable_c + */ + + symbol->direct_variable->accept(*this); + for (unsigned int i = 0; i < symbol->direct_variable->candidate_datatypes.size(); i++) { + switch (get_sizeof_datatype_c::getsize(symbol->direct_variable->candidate_datatypes[i])) { + case 1: /* bit - 1 bit */ + add_datatype_to_candidate_list(symbol, &search_constant_type_c::bool_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safebool_type_name); + break; + case 8: /* byte - 8 bits */ + add_datatype_to_candidate_list(symbol, &search_constant_type_c::byte_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safebyte_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::sint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safesint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::usint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safeusint_type_name); + break; + case 16: /* word - 16 bits */ + add_datatype_to_candidate_list(symbol, &search_constant_type_c::word_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safeword_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::int_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safeint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::uint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safeuint_type_name); + break; + case 32: /* dword - 32 bits */ + add_datatype_to_candidate_list(symbol, &search_constant_type_c::dword_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safedword_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::dint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safedint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::udint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safeudint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::real_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safereal_type_name); + break; + case 64: /* lword - 64 bits */ + add_datatype_to_candidate_list(symbol, &search_constant_type_c::lword_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safelword_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::lint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safelint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::ulint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safeulint_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::lreal_type_name); + add_datatype_to_candidate_list(symbol, &search_constant_type_c::safelreal_type_name); + break; + default: /* if none of the above, then no valid datatype allowed... */ + break; + } /* switch() */ + } /* for */ + + return NULL; +} + + +/* [variable_name] location ':' located_var_spec_init */ +/* variable_name -> may be NULL ! */ +// SYM_REF3(located_var_decl_c, variable_name, location, located_var_spec_init) +void *fill_candidate_datatypes_c::visit(located_var_decl_c *symbol) { + symbol->located_var_spec_init->accept(*this); + symbol->location->accept(*this); + if (NULL != symbol->variable_name) { + symbol->variable_name->candidate_datatypes = symbol->location->candidate_datatypes; + intersect_candidate_datatype_list(symbol->variable_name /*origin, dest.*/, symbol->located_var_spec_init /*with*/); + } + return NULL; +} + + + + + +/************************************/ +/* B 1.5 Program organization units */ +/************************************/ +/*********************/ +/* B 1.5.1 Functions */ +/*********************/ +void *fill_candidate_datatypes_c::visit(function_declaration_c *symbol) { + if (debug) printf("Filling candidate data types list of function %s\n", ((token_c *)(symbol->derived_function_name))->value); + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + symbol->var_declarations_list->accept(*this); + symbol->function_body->accept(*this); + delete search_varfb_instance_type; + search_varfb_instance_type = NULL; + return NULL; +} + +/***************************/ +/* B 1.5.2 Function blocks */ +/***************************/ +void *fill_candidate_datatypes_c::visit(function_block_declaration_c *symbol) { + if (debug) printf("Filling candidate data types list of FB %s\n", ((token_c *)(symbol->fblock_name))->value); + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + symbol->var_declarations->accept(*this); + symbol->fblock_body->accept(*this); + delete search_varfb_instance_type; + search_varfb_instance_type = NULL; + return NULL; +} + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ +void *fill_candidate_datatypes_c::visit(program_declaration_c *symbol) { + if (debug) printf("Filling candidate data types list in program %s\n", ((token_c *)(symbol->program_type_name))->value); + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + symbol->var_declarations->accept(*this); + symbol->function_block_body->accept(*this); + delete search_varfb_instance_type; + search_varfb_instance_type = NULL; + return NULL; +} + + + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ +void *fill_candidate_datatypes_c::visit(configuration_declaration_c *symbol) { + // TODO !!! + /* for the moment we must return NULL so semantic analysis of remaining code is not interrupted! */ + return NULL; +} + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ + +/*| instruction_list il_instruction */ +// SYM_LIST(instruction_list_c) +void *fill_candidate_datatypes_c::visit(instruction_list_c *symbol) { + /* In order to fill the data type candidates correctly + * in IL instruction lists containing JMPs to labels that come before the JMP instruction + * itself, we need to run the fill candidate datatypes algorithm twice on the Instruction List. + * e.g.: ... + * ld 23 + * label1:st byte_var + * ld 34 + * JMP label1 + * + * Note that the second time we run the algorithm, most of the candidate datatypes are already filled + * in, so it will be able to produce tha correct candidate datatypes for the IL instruction referenced + * by the label, as in the 2nd pass we already know the candidate datatypes of the JMP instruction! + */ + for(int j = 0; j < 2; j++) { + for(int i = 0; i < symbol->n; i++) { + symbol->elements[i]->accept(*this); + } + } + return NULL; +} + + + +/* | label ':' [il_incomplete_instruction] eol_list */ +// SYM_REF2(il_instruction_c, label, il_instruction) +// void *visit(instruction_list_c *symbol); +void *fill_candidate_datatypes_c::visit(il_instruction_c *symbol) { + if (NULL == symbol->il_instruction) { + /* This empty/null il_instruction does not change the value of the current/default IL variable. + * So it inherits the candidate_datatypes from it's previous IL instructions! + */ + intersect_prev_candidate_datatype_lists(symbol); + } else { + il_instruction_c fake_prev_il_instruction = *symbol; + intersect_prev_candidate_datatype_lists(&fake_prev_il_instruction); + + if (symbol->prev_il_instruction.size() == 0) prev_il_instruction = NULL; + else prev_il_instruction = &fake_prev_il_instruction; + symbol->il_instruction->accept(*this); + prev_il_instruction = NULL; + + /* This object has (inherits) the same candidate datatypes as the il_instruction */ + symbol->candidate_datatypes = symbol->il_instruction->candidate_datatypes; + } + + return NULL; +} + + + +void *fill_candidate_datatypes_c::visit(il_simple_operation_c *symbol) { + /* determine the data type of the operand */ + if (NULL != symbol->il_operand) { + symbol->il_operand->accept(*this); + } + /* recursive call to fill the candidate data types list */ + il_operand = symbol->il_operand; + symbol->il_simple_operator->accept(*this); + il_operand = NULL; + /* This object has (inherits) the same candidate datatypes as the il_simple_operator */ + symbol->candidate_datatypes = symbol->il_simple_operator->candidate_datatypes; + return NULL; +} + + +/* | function_name [il_operand_list] */ +/* NOTE: The parameters 'called_function_declaration' and 'extensible_param_count' are used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;) +void *fill_candidate_datatypes_c::visit(il_function_call_c *symbol) { + /* The first parameter of a non formal function call in IL will be the 'current value' (i.e. the prev_il_instruction) + * In order to be able to handle this without coding special cases, we will simply prepend that symbol + * to the il_operand_list, and remove it after calling handle_function_call(). + * + * However, if no further paramters are given, then il_operand_list will be NULL, and we will + * need to create a new object to hold the pointer to prev_il_instruction. + */ + if (NULL == symbol->il_operand_list) symbol->il_operand_list = new il_operand_list_c; + if (NULL == symbol->il_operand_list) ERROR; + + symbol->il_operand_list->accept(*this); + + if (NULL != prev_il_instruction) { + ((list_c *)symbol->il_operand_list)->insert_element(prev_il_instruction, 0); + + generic_function_call_t fcall_param = { + /* fcall_param.function_name = */ symbol->function_name, + /* fcall_param.nonformal_operand_list = */ symbol->il_operand_list, + /* fcall_param.formal_operand_list = */ NULL, + /* enum {POU_FB, POU_function} POU_type = */ generic_function_call_t::POU_function, + /* fcall_param.candidate_functions = */ symbol->candidate_functions, + /* fcall_param.called_function_declaration = */ symbol->called_function_declaration, + /* fcall_param.extensible_param_count = */ symbol->extensible_param_count + }; + handle_function_call(symbol, fcall_param); + + /* Undo the changes to the abstract syntax tree we made above... */ + ((list_c *)symbol->il_operand_list)->remove_element(0); + } + + /* Undo the changes to the abstract syntax tree we made above... */ + if (((list_c *)symbol->il_operand_list)->n == 0) { + /* if the list becomes empty, then that means that it did not exist before we made these changes, so we delete it! */ + delete symbol->il_operand_list; + symbol->il_operand_list = NULL; + } + + if (debug) std::cout << "il_function_call_c [" << symbol->candidate_datatypes.size() << "] result.\n"; + return NULL; +} + + +/* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ +// SYM_REF3(il_expression_c, il_expr_operator, il_operand, simple_instr_list); +void *fill_candidate_datatypes_c::visit(il_expression_c *symbol) { + symbol_c *prev_il_instruction_backup = prev_il_instruction; + + if (NULL != symbol->il_operand) + symbol->il_operand->accept(*this); + + if(symbol->simple_instr_list != NULL) + symbol->simple_instr_list->accept(*this); + + /* Now check the if the data type semantics of operation are correct, */ + il_operand = symbol->simple_instr_list; + prev_il_instruction = prev_il_instruction_backup; + symbol->il_expr_operator->accept(*this); + il_operand = NULL; + + /* This object has the same candidate datatypes as the il_expr_operator. */ + symbol->candidate_datatypes = symbol->il_expr_operator->candidate_datatypes; + return NULL; +} + + +void *fill_candidate_datatypes_c::visit(il_jump_operation_c *symbol) { + /* recursive call to fill the candidate data types list */ + il_operand = NULL; + symbol->il_jump_operator->accept(*this); + il_operand = NULL; + /* This object has the same candidate datatypes as the il_jump_operator. */ + symbol->candidate_datatypes = symbol->il_jump_operator->candidate_datatypes; + return NULL; +} + + +/* il_call_operator prev_declared_fb_name + * | il_call_operator prev_declared_fb_name '(' ')' + * | il_call_operator prev_declared_fb_name '(' eol_list ')' + * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' + * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' + */ +/* NOTE: The parameter 'called_fb_declaration'is used to pass data between stage 3 and stage4 (although currently it is not used in stage 4 */ +// SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list, symbol_c *called_fb_declaration) +void *fill_candidate_datatypes_c::visit(il_fb_call_c *symbol) { + /* We do not call + * fb_decl = search_varfb_instance_type->get_basetype_decl(symbol->fb_name); + * because we want to make sure it is a FB instance, and not some other data type... + */ + symbol_c *fb_type_id = search_varfb_instance_type->get_basetype_id(symbol->fb_name); + /* Although a call to a non-declared FB is a semantic error, this is currently caught by stage 2! */ + if (NULL == fb_type_id) ERROR; + + function_block_declaration_c *fb_decl = function_block_type_symtable.find_value(fb_type_id); + if (function_block_type_symtable.end_value() == fb_decl) + /* The fb_name not the name of a FB instance. Most probably it is the name of a variable of some other type. */ + fb_decl = NULL; + + /* Although a call to a non-declared FB is a semantic error, this is currently caught by stage 2! */ + if (NULL == fb_decl) ERROR; + + if (symbol-> il_param_list != NULL) symbol->il_param_list->accept(*this); + if (symbol->il_operand_list != NULL) symbol->il_operand_list->accept(*this); + + /* The print_datatypes_error_c does not rely on this called_fb_declaration pointer being != NULL to conclude that + * we have a datat type incompatibility error, so setting it to the correct fb_decl is actually safe, + * as the compiler will never reach the compilation stage! + */ + symbol->called_fb_declaration = fb_decl; + + /* Let the il_call_operator (CAL, CALC, or CALCN) determine the candidate datatypes of the il_fb_call_c... */ + /* NOTE: We ignore whether the call is 'compatible' or not when filling in the candidate datatypes list. + * Even if it is not compatible, we fill in the candidate datatypes list correctly so that the following + * IL instructions may be handled correctly and debuged. + * Doing this is actually safe, as the parameter_list will still contain errors that will be found by + * print_datatypes_error_c, so the code will never reach stage 4! + */ + symbol->il_call_operator->accept(*this); + symbol->candidate_datatypes = symbol->il_call_operator->candidate_datatypes; + + if (debug) std::cout << "FB [] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + + +/* | function_name '(' eol_list [il_param_list] ')' */ +/* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;) +void *fill_candidate_datatypes_c::visit(il_formal_funct_call_c *symbol) { + symbol->il_param_list->accept(*this); + + generic_function_call_t fcall_param = { + /* fcall_param.function_name = */ symbol->function_name, + /* fcall_param.nonformal_operand_list = */ NULL, + /* fcall_param.formal_operand_list = */ symbol->il_param_list, + /* enum {POU_FB, POU_function} POU_type = */ generic_function_call_t::POU_function, + /* fcall_param.candidate_functions = */ symbol->candidate_functions, + /* fcall_param.called_function_declaration = */ symbol->called_function_declaration, + /* fcall_param.extensible_param_count = */ symbol->extensible_param_count + }; + handle_function_call(symbol, fcall_param); + + if (debug) std::cout << "il_formal_funct_call_c [" << symbol->candidate_datatypes.size() << "] result.\n"; + return NULL; +} + + +// void *visit(il_operand_list_c *symbol); + + +/* | simple_instr_list il_simple_instruction */ +/* This object is referenced by il_expression_c objects */ +void *fill_candidate_datatypes_c::visit(simple_instr_list_c *symbol) { + if (symbol->n <= 0) + return NULL; /* List is empty! Nothing to do. */ + + for(int i = 0; i < symbol->n; i++) + symbol->elements[i]->accept(*this); + + /* This object has (inherits) the same candidate datatypes as the last il_instruction */ + symbol->candidate_datatypes = symbol->elements[symbol->n-1]->candidate_datatypes; + + if (debug) std::cout << "simple_instr_list_c [" << symbol->candidate_datatypes.size() << "] result.\n"; + return NULL; +} + + + + +// SYM_REF1(il_simple_instruction_c, il_simple_instruction, symbol_c *prev_il_instruction;) +void *fill_candidate_datatypes_c::visit(il_simple_instruction_c *symbol) { + if (symbol->prev_il_instruction.size() > 1) ERROR; /* There should be no labeled insructions inside an IL expression! */ + if (symbol->prev_il_instruction.size() == 0) prev_il_instruction = NULL; + else prev_il_instruction = symbol->prev_il_instruction[0]; + symbol->il_simple_instruction->accept(*this); + prev_il_instruction = NULL; + + /* This object has (inherits) the same candidate datatypes as the il_simple_instruction it points to */ + symbol->candidate_datatypes = symbol->il_simple_instruction->candidate_datatypes; + return NULL; +} + + +/* + void *visit(il_param_list_c *symbol); + void *visit(il_param_assignment_c *symbol); + void *visit(il_param_out_assignment_c *symbol); +*/ + +/*******************/ +/* B 2.2 Operators */ +/*******************/ +void *fill_candidate_datatypes_c::visit(LD_operator_c *symbol) { + for(unsigned int i = 0; i < il_operand->candidate_datatypes.size(); i++) { + add_datatype_to_candidate_list(symbol, il_operand->candidate_datatypes[i]); + } + if (debug) std::cout << "LD [" << il_operand->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + +void *fill_candidate_datatypes_c::visit(LDN_operator_c *symbol) { + for(unsigned int i = 0; i < il_operand->candidate_datatypes.size(); i++) { + if (is_ANY_BIT_compatible(il_operand->candidate_datatypes[i])) + add_datatype_to_candidate_list(symbol, il_operand->candidate_datatypes[i]); + } + if (debug) std::cout << "LDN [" << il_operand->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + +void *fill_candidate_datatypes_c::visit(ST_operator_c *symbol) { + symbol_c *prev_instruction_type, *operand_type; + + if (NULL == prev_il_instruction) return NULL; + for (unsigned int i = 0; i < prev_il_instruction->candidate_datatypes.size(); i++) { + for(unsigned int j = 0; j < il_operand->candidate_datatypes.size(); j++) { + prev_instruction_type = prev_il_instruction->candidate_datatypes[i]; + operand_type = il_operand->candidate_datatypes[j]; + if (is_type_equal(prev_instruction_type, operand_type)) + add_datatype_to_candidate_list(symbol, prev_instruction_type); + } + } + if (debug) std::cout << "ST [" << prev_il_instruction->candidate_datatypes.size() << "," << il_operand->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + +void *fill_candidate_datatypes_c::visit(STN_operator_c *symbol) { + symbol_c *prev_instruction_type, *operand_type; + + if (NULL == prev_il_instruction) return NULL; + for (unsigned int i = 0; i < prev_il_instruction->candidate_datatypes.size(); i++) { + for(unsigned int j = 0; j < il_operand->candidate_datatypes.size(); j++) { + prev_instruction_type = prev_il_instruction->candidate_datatypes[i]; + operand_type = il_operand->candidate_datatypes[j]; + if (is_type_equal(prev_instruction_type,operand_type) && is_ANY_BIT_compatible(operand_type)) + add_datatype_to_candidate_list(symbol, prev_instruction_type); + } + } + if (debug) std::cout << "STN [" << prev_il_instruction->candidate_datatypes.size() << "," << il_operand->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + +void *fill_candidate_datatypes_c::visit(NOT_operator_c *symbol) { + /* NOTE: the standard allows syntax in which the NOT operator is followed by an optional + * NOT [] + * However, it does not define the semantic of the NOT operation when the is specified. + * We therefore consider it an error if an il_operand is specified! + * We do not need to generate an error message. This error will be caught somewhere else! + */ + if (NULL == prev_il_instruction) return NULL; + for (unsigned int i = 0; i < prev_il_instruction->candidate_datatypes.size(); i++) { + if (is_ANY_BIT_compatible(prev_il_instruction->candidate_datatypes[i])) + add_datatype_to_candidate_list(symbol, prev_il_instruction->candidate_datatypes[i]); + } + if (debug) std::cout << "NOT_operator [" << prev_il_instruction->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + + +void *fill_candidate_datatypes_c::visit(S_operator_c *symbol) { + /* TODO: what if this is a FB call ?? */ + symbol_c *prev_instruction_type, *operand_type; + + if (NULL == prev_il_instruction) return NULL; + for (unsigned int i = 0; i < prev_il_instruction->candidate_datatypes.size(); i++) { + for(unsigned int j = 0; j < il_operand->candidate_datatypes.size(); j++) { + prev_instruction_type = prev_il_instruction->candidate_datatypes[i]; + operand_type = il_operand->candidate_datatypes[j]; + /* TODO: I believe the following is wrong! The data types of prev_instruction_type and operand_type DO NOT have to be equal. + * the prev_instruction_type MUST be BOOL compatible. + * I am not too sure about operand_type, does it have to be BOOL compatible, or can it be ANY_BIT compatible? Must check! + */ + if (is_type_equal(prev_instruction_type,operand_type) && is_ANY_BOOL_compatible(operand_type)) + add_datatype_to_candidate_list(symbol, prev_instruction_type); + } + } + if (debug) std::cout << "S [" << prev_il_instruction->candidate_datatypes.size() << "," << il_operand->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + + +void *fill_candidate_datatypes_c::visit(R_operator_c *symbol) { + /* TODO: what if this is a FB call ?? */ + symbol_c *prev_instruction_type, *operand_type; + + if (NULL == prev_il_instruction) return NULL; + for (unsigned int i = 0; i < prev_il_instruction->candidate_datatypes.size(); i++) { + for(unsigned int j = 0; j < il_operand->candidate_datatypes.size(); j++) { + prev_instruction_type = prev_il_instruction->candidate_datatypes[i]; + operand_type = il_operand->candidate_datatypes[j]; + /* TODO: I believe the following is wrong! The data types of prev_instruction_type and operand_type DO NOT have to be equal. + * the prev_instruction_type MUST be BOOL compatible. + * I am not too sure about operand_type, does it have to be BOOL compatible, or can it be ANY_BIT compatible? Must check! + */ + if (is_type_equal(prev_instruction_type,operand_type) && is_ANY_BOOL_compatible(operand_type)) + add_datatype_to_candidate_list(symbol, prev_instruction_type); + } + } + if (debug) std::cout << "R [" << prev_il_instruction->candidate_datatypes.size() << "," << il_operand->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + + +void *fill_candidate_datatypes_c::visit( S1_operator_c *symbol) {return handle_implicit_il_fb_call(symbol, "S1", symbol->called_fb_declaration);} +void *fill_candidate_datatypes_c::visit( R1_operator_c *symbol) {return handle_implicit_il_fb_call(symbol, "R1", symbol->called_fb_declaration);} +void *fill_candidate_datatypes_c::visit( CLK_operator_c *symbol) {return handle_implicit_il_fb_call(symbol, "CLK", symbol->called_fb_declaration);} +void *fill_candidate_datatypes_c::visit( CU_operator_c *symbol) {return handle_implicit_il_fb_call(symbol, "CU", symbol->called_fb_declaration);} +void *fill_candidate_datatypes_c::visit( CD_operator_c *symbol) {return handle_implicit_il_fb_call(symbol, "CD", symbol->called_fb_declaration);} +void *fill_candidate_datatypes_c::visit( PV_operator_c *symbol) {return handle_implicit_il_fb_call(symbol, "PV", symbol->called_fb_declaration);} +void *fill_candidate_datatypes_c::visit( IN_operator_c *symbol) {return handle_implicit_il_fb_call(symbol, "IN", symbol->called_fb_declaration);} +void *fill_candidate_datatypes_c::visit( PT_operator_c *symbol) {return handle_implicit_il_fb_call(symbol, "PT", symbol->called_fb_declaration);} + +void *fill_candidate_datatypes_c::visit( AND_operator_c *symbol) {return handle_binary_operator(widen_AND_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( OR_operator_c *symbol) {return handle_binary_operator( widen_OR_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( XOR_operator_c *symbol) {return handle_binary_operator(widen_XOR_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit(ANDN_operator_c *symbol) {return handle_binary_operator(widen_AND_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( ORN_operator_c *symbol) {return handle_binary_operator( widen_OR_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit(XORN_operator_c *symbol) {return handle_binary_operator(widen_XOR_table, symbol, prev_il_instruction, il_operand);} + +void *fill_candidate_datatypes_c::visit( ADD_operator_c *symbol) {return handle_binary_operator(widen_ADD_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( SUB_operator_c *symbol) {return handle_binary_operator(widen_SUB_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( MUL_operator_c *symbol) {return handle_binary_operator(widen_MUL_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( DIV_operator_c *symbol) {return handle_binary_operator(widen_DIV_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( MOD_operator_c *symbol) {return handle_binary_operator(widen_MOD_table, symbol, prev_il_instruction, il_operand);} + +void *fill_candidate_datatypes_c::visit( GT_operator_c *symbol) {return handle_binary_operator(widen_CMP_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( GE_operator_c *symbol) {return handle_binary_operator(widen_CMP_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( EQ_operator_c *symbol) {return handle_binary_operator(widen_CMP_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( LT_operator_c *symbol) {return handle_binary_operator(widen_CMP_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( LE_operator_c *symbol) {return handle_binary_operator(widen_CMP_table, symbol, prev_il_instruction, il_operand);} +void *fill_candidate_datatypes_c::visit( NE_operator_c *symbol) {return handle_binary_operator(widen_CMP_table, symbol, prev_il_instruction, il_operand);} + + + +void *fill_candidate_datatypes_c::handle_conditional_il_flow_control_operator(symbol_c *symbol) { + if (NULL == prev_il_instruction) return NULL; + for (unsigned int i = 0; i < prev_il_instruction->candidate_datatypes.size(); i++) { + if (is_ANY_BOOL_compatible(prev_il_instruction->candidate_datatypes[i])) + add_datatype_to_candidate_list(symbol, prev_il_instruction->candidate_datatypes[i]); + } + return NULL; +} + +void *fill_candidate_datatypes_c::visit( CAL_operator_c *symbol) {if (NULL != prev_il_instruction) symbol->candidate_datatypes = prev_il_instruction->candidate_datatypes; return NULL;} +void *fill_candidate_datatypes_c::visit( RET_operator_c *symbol) {if (NULL != prev_il_instruction) symbol->candidate_datatypes = prev_il_instruction->candidate_datatypes; return NULL;} +void *fill_candidate_datatypes_c::visit( JMP_operator_c *symbol) {if (NULL != prev_il_instruction) symbol->candidate_datatypes = prev_il_instruction->candidate_datatypes; return NULL;} +void *fill_candidate_datatypes_c::visit( CALC_operator_c *symbol) {return handle_conditional_il_flow_control_operator(symbol);} +void *fill_candidate_datatypes_c::visit(CALCN_operator_c *symbol) {return handle_conditional_il_flow_control_operator(symbol);} +void *fill_candidate_datatypes_c::visit( RETC_operator_c *symbol) {return handle_conditional_il_flow_control_operator(symbol);} +void *fill_candidate_datatypes_c::visit(RETCN_operator_c *symbol) {return handle_conditional_il_flow_control_operator(symbol);} +void *fill_candidate_datatypes_c::visit( JMPC_operator_c *symbol) {return handle_conditional_il_flow_control_operator(symbol);} +void *fill_candidate_datatypes_c::visit(JMPCN_operator_c *symbol) {return handle_conditional_il_flow_control_operator(symbol);} + + + + +/* Symbol class handled together with function call checks */ +// void *visit(il_assign_operator_c *symbol, variable_name); +/* Symbol class handled together with function call checks */ +// void *visit(il_assign_operator_c *symbol, option, variable_name); + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ +void *fill_candidate_datatypes_c::visit( or_expression_c *symbol) {return handle_binary_expression(widen_OR_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit( xor_expression_c *symbol) {return handle_binary_expression(widen_XOR_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit( and_expression_c *symbol) {return handle_binary_expression(widen_AND_table, symbol, symbol->l_exp, symbol->r_exp);} + +void *fill_candidate_datatypes_c::visit( equ_expression_c *symbol) {return handle_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit(notequ_expression_c *symbol) {return handle_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit( lt_expression_c *symbol) {return handle_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit( gt_expression_c *symbol) {return handle_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit( le_expression_c *symbol) {return handle_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit( ge_expression_c *symbol) {return handle_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} + + +/* The following code is correct when handling the addition of 2 symbolic_variables + * In this case, adding two variables (e.g. USINT_var1 + USINT_var2) will always yield + * the same data type, even if the result of the adition could not fit inside the same + * data type (due to overflowing) + * + * However, when adding two literals (e.g. USINT#42 + USINT#3) + * we should be able to detect overflows of the result, and therefore not consider + * that the result may be of type USINT. + * Currently we do not yet detect these overflows, and allow handling the sum of two USINTs + * as always resulting in an USINT, even in the following expression + * (USINT#65535 + USINT#2). + * + * In the future we can add some code to reduce + * all the expressions that are based on literals into the resulting literal + * value (maybe some visitor class that will run before or after data type + * checking). Since this class will have to be very careful to make sure it implements the same mathematical + * details (e.g. how to round and truncate numbers) as defined in IEC 61131-3, we will leave this to the future. + * Also, the question will arise if we should also replace calls to standard + * functions if the input parameters are all literals (e.g. ADD(42, 42)). This + * means this class will be more difficult than it appears at first. + */ +void *fill_candidate_datatypes_c::visit( add_expression_c *symbol) {return handle_binary_expression(widen_ADD_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit( sub_expression_c *symbol) {return handle_binary_expression(widen_SUB_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit( mul_expression_c *symbol) {return handle_binary_expression(widen_MUL_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit( div_expression_c *symbol) {return handle_binary_expression(widen_DIV_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit( mod_expression_c *symbol) {return handle_binary_expression(widen_MOD_table, symbol, symbol->l_exp, symbol->r_exp);} +void *fill_candidate_datatypes_c::visit(power_expression_c *symbol) {return handle_binary_expression(widen_EXPT_table, symbol, symbol->l_exp, symbol->r_exp);} + + +void *fill_candidate_datatypes_c::visit(neg_expression_c *symbol) { + /* NOTE: The standard defines the syntax for this 'negation' operation, but + * does not define the its semantics. + * + * We could be tempted to consider that the semantics of the + * 'negation' operation are similar/identical to the semantics of the + * SUB expression/operation. This would include assuming that the + * possible datatypes for the 'negation' operation is also + * the same as those for the SUB expression/operation, namely ANY_MAGNITUDE. + * + * However, this would then mean that the following ST code would be + * syntactically and semantically correct: + * uint_var := - (uint_var); + * + * According to the standard, the above code should result in a + * runtime error, when we try to apply a negative value to the + * UINT typed variable 'uint_var'. + * + * It is much easier for the compiler to detect this at compile time, + * and it is probably safer to the resulting code too. + * + * To detect these tyes of errors at compile time, the easisest solution + * is to only allow ANY_NUM datatytpes that are signed. + * So, that is what we do here! + */ + symbol->exp->accept(*this); + for (unsigned int i = 0; i < symbol->exp->candidate_datatypes.size(); i++) { + if (is_ANY_signed_MAGNITUDE_compatible(symbol->exp->candidate_datatypes[i])) + add_datatype_to_candidate_list(symbol, symbol->exp->candidate_datatypes[i]); + } + if (debug) std::cout << "neg [" << symbol->exp->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + + +void *fill_candidate_datatypes_c::visit(not_expression_c *symbol) { + symbol->exp->accept(*this); + for (unsigned int i = 0; i < symbol->exp->candidate_datatypes.size(); i++) { + if (is_ANY_BIT_compatible(symbol->exp->candidate_datatypes[i])) + add_datatype_to_candidate_list(symbol, symbol->exp->candidate_datatypes[i]); + } + if (debug) std::cout << "not [" << symbol->exp->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + + +void *fill_candidate_datatypes_c::visit(function_invocation_c *symbol) { + if (NULL != symbol->formal_param_list) symbol-> formal_param_list->accept(*this); + else if (NULL != symbol->nonformal_param_list) symbol->nonformal_param_list->accept(*this); + else ERROR; + + generic_function_call_t fcall_param = { + /* fcall_param.function_name = */ symbol->function_name, + /* fcall_param.nonformal_operand_list = */ symbol->nonformal_param_list, + /* fcall_param.formal_operand_list = */ symbol->formal_param_list, + /* enum {POU_FB, POU_function} POU_type = */ generic_function_call_t::POU_function, + /* fcall_param.candidate_functions = */ symbol->candidate_functions, + /* fcall_param.called_function_declaration = */ symbol->called_function_declaration, + /* fcall_param.extensible_param_count = */ symbol->extensible_param_count + }; + handle_function_call(symbol, fcall_param); + + if (debug) std::cout << "function_invocation_c [" << symbol->candidate_datatypes.size() << "] result.\n"; + return NULL; +} + + + +/********************/ +/* B 3.2 Statements */ +/********************/ +// SYM_LIST(statement_list_c) +/* The visitor of the base class search_visitor_c will handle calling each instruction in the list. + * We do not need to do anything here... + */ +// void *fill_candidate_datatypes_c::visit(statement_list_c *symbol) + + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ +void *fill_candidate_datatypes_c::visit(assignment_statement_c *symbol) { + symbol_c *left_type, *right_type; + + symbol->l_exp->accept(*this); + symbol->r_exp->accept(*this); + for (unsigned int i = 0; i < symbol->l_exp->candidate_datatypes.size(); i++) { + for(unsigned int j = 0; j < symbol->r_exp->candidate_datatypes.size(); j++) { + left_type = symbol->l_exp->candidate_datatypes[i]; + right_type = symbol->r_exp->candidate_datatypes[j]; + if (is_type_equal(left_type, right_type)) + add_datatype_to_candidate_list(symbol, left_type); + } + } + if (debug) std::cout << ":= [" << symbol->l_exp->candidate_datatypes.size() << "," << symbol->r_exp->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ +void *fill_candidate_datatypes_c::visit(fb_invocation_c *symbol) { + symbol_c *fb_type_id = search_varfb_instance_type->get_basetype_id(symbol->fb_name); + /* Although a call to a non-declared FB is a semantic error, this is currently caught by stage 2! */ + if (NULL == fb_type_id) ERROR; + + function_block_declaration_c *fb_decl = function_block_type_symtable.find_value(fb_type_id); + if (function_block_type_symtable.end_value() == fb_decl) + /* The fb_name not the name of a FB instance. Most probably it is the name of a variable of some other type. */ + fb_decl = NULL; + + /* Although a call to a non-declared FB is a semantic error, this is currently caught by stage 2! */ + if (NULL == fb_decl) ERROR; + + if (symbol-> formal_param_list != NULL) symbol->formal_param_list->accept(*this); + if (symbol->nonformal_param_list != NULL) symbol->nonformal_param_list->accept(*this); + + /* The print_datatypes_error_c does not rely on this called_fb_declaration pointer being != NULL to conclude that + * we have a datat type incompatibility error, so setting it to the correct fb_decl is actually safe, + * as the compiler will never reach the compilation stage! + */ + symbol->called_fb_declaration = fb_decl; + + if (debug) std::cout << "FB [] ==> " << symbol->candidate_datatypes.size() << " result.\n"; + return NULL; +} + + + +/********************************/ +/* B 3.2.3 Selection Statements */ +/********************************/ +void *fill_candidate_datatypes_c::visit(if_statement_c *symbol) { + symbol->expression->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + if (NULL != symbol->elseif_statement_list) + symbol->elseif_statement_list->accept(*this); + if (NULL != symbol->else_statement_list) + symbol->else_statement_list->accept(*this); + return NULL; +} + + +void *fill_candidate_datatypes_c::visit(elseif_statement_c *symbol) { + symbol->expression->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + +/* CASE expression OF case_element_list ELSE statement_list END_CASE */ +// SYM_REF3(case_statement_c, expression, case_element_list, statement_list) +void *fill_candidate_datatypes_c::visit(case_statement_c *symbol) { + symbol->expression->accept(*this); + if (NULL != symbol->case_element_list) + symbol->case_element_list->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + + +/* helper symbol for case_statement */ +// SYM_LIST(case_element_list_c) +/* NOTE: visitor method for case_element_list_c is not required since we inherit from iterator_visitor_c */ + +/* case_list ':' statement_list */ +// SYM_REF2(case_element_c, case_list, statement_list) +/* NOTE: visitor method for case_element_c is not required since we inherit from iterator_visitor_c */ + +// SYM_LIST(case_list_c) +/* NOTE: visitor method for case_list_c is not required since we inherit from iterator_visitor_c */ + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ + +void *fill_candidate_datatypes_c::visit(for_statement_c *symbol) { + symbol->control_variable->accept(*this); + symbol->beg_expression->accept(*this); + symbol->end_expression->accept(*this); + if (NULL != symbol->by_expression) + symbol->by_expression->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + + +void *fill_candidate_datatypes_c::visit(while_statement_c *symbol) { + symbol->expression->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + + +void *fill_candidate_datatypes_c::visit(repeat_statement_c *symbol) { + symbol->expression->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + + + + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/fill_candidate_datatypes.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/fill_candidate_datatypes.hh Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,357 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (manuele.conti@sirius-es.it) + * Copyright (C) 2012 Matteo Facchinetti (matteo.facchinetti@sirius-es.it) + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* NOTE: The algorithm implemented here assumes that flow control analysis has already been completed! + * BEFORE running this visitor, be sure to CALL the flow_control_analysis_c visitor! + */ + + +/* + * Fill the candidate datatype list for all symbols that may legally 'have' a data type (e.g. variables, literals, function calls, expressions, etc.) + * + * The candidate datatype list will be filled with a list of all the data types that expression may legally take. + * For example, the very simple literal '0' (as in foo := 0), may represent a: + * BOOL, BYTE, WORD, DWORD, LWORD, USINT, SINT, UINT, INT, UDINT, DINT, ULINT, LINT (as well as the SAFE versions of these data tyes too!) + */ + + +#include "../absyntax_utils/absyntax_utils.hh" +#include "datatype_functions.hh" + +class fill_candidate_datatypes_c: public iterator_visitor_c { + + private: + search_varfb_instance_type_c *search_varfb_instance_type; + search_base_type_c search_base_type; + /* When calling a function block, we must first find it's type, + * by searching through the declarations of the variables currently + * in scope. + * This class does just that... + * A new object instance is instantiated whenever we start checking semantics + * for a function block type declaration, or a program declaration. + * This object instance will then later be called while the + * function block's or the program's body is being handled. + * + * Note that functions cannot contain calls to function blocks, + * so we do not create an object instance when handling + * a function declaration. + */ + // search_var_instance_decl_c *search_var_instance_decl; + + /* This variable was created to pass information from + * fill_candidate_datatypes_c::visit(case_statement_c *symbol) function to + * fill_candidate_datatypes_c::visit(case_list_c *symbol) function. + */ +// symbol_c *case_expression_type; + + /* In IL code, once we find a type mismatch error, it is best to + * ignore any further errors until the end of the logical operation, + * i.e. until the next LD. + * However, we cannot clear the il_error flag on all LD operations, + * as these may also be used within parenthesis. LD operations + * within parenthesis may not clear the error flag. + * We therefore need a counter to know how deep inside a parenthesis + * structure we are. + */ +// int il_parenthesis_level; +// bool error_found; + + /* Pointer to the previous IL instruction, which contains the current data type (actually, the list of candidate data types) of the data stored in the IL stack, i.e. the default variable, a.k.a. accumulator */ + symbol_c *prev_il_instruction; + /* the current IL operand being analyzed */ + symbol_c *il_operand; + symbol_c *widening_conversion(symbol_c *left_type, symbol_c *right_type, const struct widen_entry widen_table[]); + + /* Match a function declaration with a function call through their parameters.*/ + /* returns true if compatible function/FB invocation, otherwise returns false */ + bool match_nonformal_call(symbol_c *f_call, symbol_c *f_decl); + bool match_formal_call (symbol_c *f_call, symbol_c *f_decl, symbol_c **first_param_datatype = NULL); + void handle_function_call(symbol_c *fcall, generic_function_call_t fcall_data); + void *handle_implicit_il_fb_call(symbol_c *il_instruction, const char *param_name, symbol_c *&called_fb_declaration); + void *handle_binary_expression(const struct widen_entry widen_table[], symbol_c *symbol, symbol_c *l_expr, symbol_c *r_expr); + void *handle_binary_operator (const struct widen_entry widen_table[], symbol_c *symbol, symbol_c *l_expr, symbol_c *r_expr); + void *handle_conditional_il_flow_control_operator(symbol_c *symbol); + + /* a helper function... */ + symbol_c *base_type(symbol_c *symbol); + + /* add a data type to a candidate data type list, while guaranteeing no duplicate entries! */ + /* Returns true if it really did add the datatype to the list, or false if it was already present in the list! */ + bool add_datatype_to_candidate_list (symbol_c *symbol, symbol_c *datatype); + bool add_2datatypes_to_candidate_list(symbol_c *symbol, symbol_c *datatype1, symbol_c *datatype2); + void remove_incompatible_datatypes(symbol_c *symbol); + + + public: + fill_candidate_datatypes_c(symbol_c *ignore); + virtual ~fill_candidate_datatypes_c(void); + + + /*********************/ + /* B 1.2 - Constants */ + /*********************/ + /******************************/ + /* B 1.2.1 - Numeric Literals */ + /******************************/ + void *handle_any_integer(symbol_c *symbol); + void *handle_any_real (symbol_c *symbol); + void *handle_any_literal(symbol_c *symbol, symbol_c *symbol_value, symbol_c *symbol_type); + + void *visit(real_c *symbol); + void *visit(integer_c *symbol); + void *visit(neg_real_c *symbol); + void *visit(neg_integer_c *symbol); + void *visit(binary_integer_c *symbol); + void *visit(octal_integer_c *symbol); + void *visit(hex_integer_c *symbol); + void *visit(integer_literal_c *symbol); + void *visit(real_literal_c *symbol); + void *visit(bit_string_literal_c *symbol); + void *visit(boolean_literal_c *symbol); + void *visit(boolean_true_c *symbol); + void *visit(boolean_false_c *symbol); + + /*******************************/ + /* B.1.2.2 Character Strings */ + /*******************************/ + void *visit(double_byte_character_string_c *symbol); + void *visit(single_byte_character_string_c *symbol); + + /***************************/ + /* B 1.2.3 - Time Literals */ + /***************************/ + /************************/ + /* B 1.2.3.1 - Duration */ + /************************/ + void *visit(duration_c *symbol); + + /************************************/ + /* B 1.2.3.2 - Time of day and Date */ + /************************************/ + void *visit(time_of_day_c *symbol); + void *visit(date_c *symbol); + void *visit(date_and_time_c *symbol); + + + /**********************/ + /* B 1.3 - Data types */ + /**********************/ + /********************************/ + /* B 1.3.3 - Derived data types */ + /********************************/ + void *visit(simple_spec_init_c *symbol); + void *visit(subrange_c *symbol); +// void *visit(data_type_declaration_c *symbol); + void *visit(enumerated_value_c *symbol); + + /*********************/ + /* B 1.4 - Variables */ + /*********************/ + void *visit(symbolic_variable_c *symbol); + + /********************************************/ + /* B 1.4.1 - Directly Represented Variables */ + /********************************************/ + void *visit(direct_variable_c *symbol); + + /*************************************/ + /* B 1.4.2 - Multi-element variables */ + /*************************************/ + void *visit(array_variable_c *symbol); + void *visit(structured_variable_c *symbol); + + /******************************************/ + /* B 1.4.3 - Declaration & Initialisation */ + /******************************************/ + void *visit(var1_list_c *symbol); + void *visit(location_c *symbol); + void *visit(located_var_decl_c *symbol); + + + /**************************************/ + /* B 1.5 - Program organization units */ + /**************************************/ + /***********************/ + /* B 1.5.1 - Functions */ + /***********************/ + void *visit(function_declaration_c *symbol); + + /*****************************/ + /* B 1.5.2 - Function blocks */ + /*****************************/ + void *visit(function_block_declaration_c *symbol); + + /**********************/ + /* B 1.5.3 - Programs */ + /**********************/ + void *visit(program_declaration_c *symbol); + + /********************************/ + /* B 1.7 Configuration elements */ + /********************************/ + void *visit(configuration_declaration_c *symbol); + + /****************************************/ + /* B.2 - Language IL (Instruction List) */ + /****************************************/ + /***********************************/ + /* B 2.1 Instructions and Operands */ + /***********************************/ + void *visit(instruction_list_c *symbol); + void *visit(il_instruction_c *symbol); + void *visit(il_simple_operation_c *symbol); + void *visit(il_function_call_c *symbol); + void *visit(il_expression_c *symbol); + void *visit(il_jump_operation_c *symbol); + void *visit(il_fb_call_c *symbol); + void *visit(il_formal_funct_call_c *symbol); +// void *visit(il_operand_list_c *symbol); + void *visit(simple_instr_list_c *symbol); + void *visit(il_simple_instruction_c*symbol); +// void *visit(il_param_list_c *symbol); +// void *visit(il_param_assignment_c *symbol); +// void *visit(il_param_out_assignment_c *symbol); + + /*******************/ + /* B 2.2 Operators */ + /*******************/ + void *visit(LD_operator_c *symbol); + void *visit(LDN_operator_c *symbol); + void *visit(ST_operator_c *symbol); + void *visit(STN_operator_c *symbol); + void *visit(NOT_operator_c *symbol); + void *visit(S_operator_c *symbol); + void *visit(R_operator_c *symbol); + void *visit(S1_operator_c *symbol); + void *visit(R1_operator_c *symbol); + void *visit(CLK_operator_c *symbol); + void *visit(CU_operator_c *symbol); + void *visit(CD_operator_c *symbol); + void *visit(PV_operator_c *symbol); + void *visit(IN_operator_c *symbol); + void *visit(PT_operator_c *symbol); + void *visit(AND_operator_c *symbol); + void *visit(OR_operator_c *symbol); + void *visit(XOR_operator_c *symbol); + void *visit(ANDN_operator_c *symbol); + void *visit(ORN_operator_c *symbol); + void *visit(XORN_operator_c *symbol); + void *visit(ADD_operator_c *symbol); + void *visit(SUB_operator_c *symbol); + void *visit(MUL_operator_c *symbol); + void *visit(DIV_operator_c *symbol); + void *visit(MOD_operator_c *symbol); + void *visit(GT_operator_c *symbol); + void *visit(GE_operator_c *symbol); + void *visit(EQ_operator_c *symbol); + void *visit(LT_operator_c *symbol); + void *visit(LE_operator_c *symbol); + void *visit(NE_operator_c *symbol); + void *visit(CAL_operator_c *symbol); + void *visit(CALC_operator_c *symbol); + void *visit(CALCN_operator_c *symbol); + void *visit(RET_operator_c *symbol); + void *visit(RETC_operator_c *symbol); + void *visit(RETCN_operator_c *symbol); + void *visit(JMP_operator_c *symbol); + void *visit(JMPC_operator_c *symbol); + void *visit(JMPCN_operator_c *symbol); + /* Symbol class handled together with function call checks */ + // void *visit(il_assign_operator_c *symbol, variable_name); + /* Symbol class handled together with function call checks */ + // void *visit(il_assign_operator_c *symbol, option, variable_name); + + + /***************************************/ + /* B.3 - Language ST (Structured Text) */ + /***************************************/ + /***********************/ + /* B 3.1 - Expressions */ + /***********************/ + void *visit(or_expression_c *symbol); + void *visit(xor_expression_c *symbol); + void *visit(and_expression_c *symbol); + void *visit(equ_expression_c *symbol); + void *visit(notequ_expression_c *symbol); + void *visit(lt_expression_c *symbol); + void *visit(gt_expression_c *symbol); + void *visit(le_expression_c *symbol); + void *visit(ge_expression_c *symbol); + void *visit(add_expression_c *symbol); + void *visit(sub_expression_c *symbol); + void *visit(mul_expression_c *symbol); + void *visit(div_expression_c *symbol); + void *visit(mod_expression_c *symbol); + void *visit(power_expression_c *symbol); + void *visit(neg_expression_c *symbol); + void *visit(not_expression_c *symbol); + void *visit(function_invocation_c *symbol); + + /*********************************/ + /* B 3.2.1 Assignment Statements */ + /*********************************/ + void *visit(assignment_statement_c *symbol); + + /*****************************************/ + /* B 3.2.2 Subprogram Control Statements */ + /*****************************************/ + void *visit(fb_invocation_c *symbol); + + /********************************/ + /* B 3.2.3 Selection Statements */ + /********************************/ + void *visit(if_statement_c *symbol); + // void *visit(elseif_statement_list_c *symbol); + void *visit(elseif_statement_c *symbol); + void *visit(case_statement_c *symbol); + + /********************************/ + /* B 3.2.4 Iteration Statements */ + /********************************/ + void *visit(for_statement_c *symbol); + void *visit(while_statement_c *symbol); + void *visit(repeat_statement_c *symbol); + +}; // fill_candidate_datatypes_c + + + + + + + + + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/flow_control_analysis.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/flow_control_analysis.cc Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,373 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2012 Mario de Sousa (msousa@fe.up.pt) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Do flow control analysis of the IEC 61131-3 code. + * + * We currently only do this for IL code. + * This class will annotate the abstract syntax tree, by filling in the + * prev_il_instruction variable in the il_instruction_c, so it points to + * the previous il_instruction_c object in the instruction list instruction_list_c. + * + * Since IL code can contain jumps (JMP), the same il_instruction may effectively have + * several previous il_instructions. In order to accommodate this, each il_instruction + * will maintain a vector (i..e an array) of pointers to all the previous il_instructions. + * We do however attempt to guarantee that the first element in the vector (array) will preferentially + * point to the il instruction that is right before / imediately preceding the current il instructions, + * i.e. the first element in the array will tend to point to the previous il_instruction + * that is not a jump JMP IL instruction! + * + * The result will essentially be a graph of il_instruction_c objects, each + * pointing to the previous il_instruction_c object. + * + * The reality is we will get several independent and isolated linked lists + * (actually, since we now process labels correctly, this is really a graph): + * one for each block of IL code (e.g. inside a Function, FB or Program). + * Additionally, when the IL code has an expression (expression_c object), we will actually + * have one more isolated linked list for the IL code inside that expression. + * + * e.g. + * line_1: LD 1 + * line_2: ADD (42 + * line_3: ADD B + * line_4: ADD C + * line_5: ) + * line_6: ADD D + * line_7: ST E + * + * will result in two independent linked lists: + * main list: line_7 -> line_6 -> line2 -> line_1 + * expr list: lin4_4 -> line_3 -> (operand of line_2, i.e. '42') + * + * + * In the main list, each: + * line_x: IL_operation IL_operand + * is encoded as + * il_instruction_c(label, il_incomplete_instruction) + * these il_instruction_c objects will point back to the previous il_instruction_c object. + * + * In the expr list, each + * line_x: IL_operation IL_operand + * is encoded as + * il_simple_instruction_c(il_simple_instruction) + * these il_simple_instruction_c objects will point back to the previous il_simple_instruction_c object, + * except the for the first il_simple_instruction_c object in the list, which will point back to + * the first il_operand (in the above example, '42'), or NULL is it does not exist. + * + * + * label: + * identifier_c + * + * il_incomplete_instruction: + * il_simple_operation (il_simple_operation_c, il_function_call_c) + * | il_expression (il_expression_c) + * | il_jump_operation (il_jump_operation_c) + * | il_fb_call (il_fb_call_c) + * | il_formal_funct_call (il_formal_funct_call_c) + * | il_return_operator (RET_operator_c, RETC_operator_c, RETCN_operator_c) + * + * + * il_expression_c(il_expr_operator, il_operand, simple_instr_list) + * + * il_operand: + * variable (symbolic_variable_c, direct_variable_c, array_variable_c, structured_variable_c) + * | enumerated_value (enumerated_value_c) + * | constant (lots of literal classes _c) + * + * simple_instr_list: + * list of il_simple_instruction + * + * il_simple_instruction: + * il_simple_operation (il_simple_operation_c, il_function_call_c) + * | il_expression (il_expression_c) + * | il_formal_funct_call (il_formal_funct_call_c) + * + */ + +#include "flow_control_analysis.hh" + + + +/* set to 1 to see debug info during execution */ +static int debug = 0; + +flow_control_analysis_c::flow_control_analysis_c(symbol_c *ignore) { + prev_il_instruction = NULL; + curr_il_instruction = NULL; +} + +flow_control_analysis_c::~flow_control_analysis_c(void) { +} + + + +/************************************/ +/* B 1.5 Program organization units */ +/************************************/ +/*********************/ +/* B 1.5.1 Functions */ +/*********************/ +void *flow_control_analysis_c::visit(function_declaration_c *symbol) { + search_il_label = new search_il_label_c(symbol); + if (debug) printf("Doing flow control analysis in body of function %s\n", ((token_c *)(symbol->derived_function_name))->value); + symbol->function_body->accept(*this); + delete search_il_label; + search_il_label = NULL; + return NULL; +} + +/***************************/ +/* B 1.5.2 Function blocks */ +/***************************/ +void *flow_control_analysis_c::visit(function_block_declaration_c *symbol) { + search_il_label = new search_il_label_c(symbol); + if (debug) printf("Doing flow control analysis in body of FB %s\n", ((token_c *)(symbol->fblock_name))->value); + symbol->fblock_body->accept(*this); + delete search_il_label; + search_il_label = NULL; + return NULL; +} + +/********************/ +/* B 1.5.3 Programs */ +/********************/ +void *flow_control_analysis_c::visit(program_declaration_c *symbol) { + search_il_label = new search_il_label_c(symbol); + if (debug) printf("Doing flow control analysis in body of program %s\n", ((token_c *)(symbol->program_type_name))->value); + symbol->function_block_body->accept(*this); + delete search_il_label; + search_il_label = NULL; + return NULL; +} + + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ +void *flow_control_analysis_c::visit(configuration_declaration_c *symbol) { + return NULL; +} + + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ + +/*| instruction_list il_instruction */ +// SYM_LIST(instruction_list_c) +void *flow_control_analysis_c::visit(instruction_list_c *symbol) { + prev_il_instruction_is_JMP_or_RET = false; + for(int i = 0; i < symbol->n; i++) { + prev_il_instruction = NULL; + if (i > 0) prev_il_instruction = symbol->elements[i-1]; + curr_il_instruction = symbol->elements[i]; + curr_il_instruction->accept(*this); + curr_il_instruction = NULL; + } + return NULL; +} + +/* | label ':' [il_incomplete_instruction] eol_list */ +// SYM_REF2(il_instruction_c, label, il_instruction) +// void *visit(instruction_list_c *symbol); +void *flow_control_analysis_c::visit(il_instruction_c *symbol) { + if ((NULL != prev_il_instruction) && (!prev_il_instruction_is_JMP_or_RET)) + /* We try to guarantee that the previous il instruction that is in the previous line, will occupy the first element of the vector. + * In order to do that, we use insert() instead of push_back() + */ + symbol->prev_il_instruction.insert(symbol->prev_il_instruction.begin() , prev_il_instruction); + + /* check if it is an il_expression_c, a JMP[C[N]], or a RET, and if so, handle it correctly */ + prev_il_instruction_is_JMP_or_RET = false; + if (NULL != symbol->il_instruction) + symbol->il_instruction->accept(*this); + return NULL; +} + + + +/* | il_simple_operator [il_operand] */ +// SYM_REF2(il_simple_operation_c, il_simple_operator, il_operand) +// void *flow_control_analysis_c::visit(il_simple_operation_c *symbol) + + + +/* | function_name [il_operand_list] */ +/* NOTE: The parameters 'called_function_declaration' and 'extensible_param_count' are used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;) +// void *flow_control_analysis_c::visit(il_function_call_c *symbol) + + +/* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ +// SYM_REF3(il_expression_c, il_expr_operator, il_operand, simple_instr_list); +void *flow_control_analysis_c::visit(il_expression_c *symbol) { + if(NULL == symbol->simple_instr_list) + /* nothing to do... */ + return NULL; + + symbol_c *save_prev_il_instruction = prev_il_instruction; + prev_il_instruction = symbol->il_operand; + symbol->simple_instr_list->accept(*this); + prev_il_instruction = save_prev_il_instruction; + return NULL; +} + + +/* il_jump_operator label */ +// SYM_REF2(il_jump_operation_c, il_jump_operator, label) +void *flow_control_analysis_c::visit(il_jump_operation_c *symbol) { + /* search for the il_instruction_c containing the label */ + il_instruction_c *destination = search_il_label->find_label(symbol->label); + + /* give the visit(JMP_operator *) an oportunity to set the prev_il_instruction_is_JMP_or_RET flag! */ + symbol->il_jump_operator->accept(*this); + /* add, to that il_instruction's list of prev_il_intsructions, the curr_il_instruction */ + if (NULL != destination) + destination->prev_il_instruction.push_back(curr_il_instruction); + return NULL; +} + + +/* il_call_operator prev_declared_fb_name + * | il_call_operator prev_declared_fb_name '(' ')' + * | il_call_operator prev_declared_fb_name '(' eol_list ')' + * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' + * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' + */ +/* NOTE: The parameter 'called_fb_declaration'is used to pass data between stage 3 and stage4 (although currently it is not used in stage 4 */ +// SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list, symbol_c *called_fb_declaration) +// void *flow_control_analysis_c::visit(il_fb_call_c *symbol) + + +/* | function_name '(' eol_list [il_param_list] ')' */ +/* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;) +// void *flow_control_analysis_c::visit(il_formal_funct_call_c *symbol) + + + +// void *visit(il_operand_list_c *symbol); +void *flow_control_analysis_c::visit(simple_instr_list_c *symbol) { + for(int i = 0; i < symbol->n; i++) { + /* The prev_il_instruction for element[0] was set in visit(il_expression_c *) */ + if (i>0) prev_il_instruction = symbol->elements[i-1]; + symbol->elements[i]->accept(*this); + } + return NULL; +} + + +// SYM_REF1(il_simple_instruction_c, il_simple_instruction, symbol_c *prev_il_instruction;) +void *flow_control_analysis_c::visit(il_simple_instruction_c*symbol) { + if (NULL != prev_il_instruction) + /* We try to guarantee that the previous il instruction that is in the previous line, will occupy the first element of the vector. + * In order to do that, we use insert() instead of push_back() + */ + symbol->prev_il_instruction.insert(symbol->prev_il_instruction.begin() , prev_il_instruction); + return NULL; +} + + +/* + void *visit(il_param_list_c *symbol); + void *visit(il_param_assignment_c *symbol); + void *visit(il_param_out_assignment_c *symbol); + */ + + + + +/*******************/ +/* B 2.2 Operators */ +/*******************/ +// void *visit( LD_operator_c *symbol); +// void *visit( LDN_operator_c *symbol); +// void *visit( ST_operator_c *symbol); +// void *visit( STN_operator_c *symbol); +// void *visit( NOT_operator_c *symbol); +// void *visit( S_operator_c *symbol); +// void *visit( R_operator_c *symbol); +// void *visit( S1_operator_c *symbol); +// void *visit( R1_operator_c *symbol); +// void *visit( CLK_operator_c *symbol); +// void *visit( CU_operator_c *symbol); +// void *visit( CD_operator_c *symbol); +// void *visit( PV_operator_c *symbol); +// void *visit( IN_operator_c *symbol); +// void *visit( PT_operator_c *symbol); +// void *visit( AND_operator_c *symbol); +// void *visit( OR_operator_c *symbol); +// void *visit( XOR_operator_c *symbol); +// void *visit( ANDN_operator_c *symbol); +// void *visit( ORN_operator_c *symbol); +// void *visit( XORN_operator_c *symbol); +// void *visit( ADD_operator_c *symbol); +// void *visit( SUB_operator_c *symbol); +// void *visit( MUL_operator_c *symbol); +// void *visit( DIV_operator_c *symbol); +// void *visit( MOD_operator_c *symbol); +// void *visit( GT_operator_c *symbol); +// void *visit( GE_operator_c *symbol); +// void *visit( EQ_operator_c *symbol); +// void *visit( LT_operator_c *symbol); +// void *visit( LE_operator_c *symbol); +// void *visit( NE_operator_c *symbol); +// void *visit( CAL_operator_c *symbol); +// void *visit( CALC_operator_c *symbol); +// void *visit(CALCN_operator_c *symbol); + +/* this next visit function will be called directly from visit(il_instruction_c *) */ +void *flow_control_analysis_c::visit( RET_operator_c *symbol) { + prev_il_instruction_is_JMP_or_RET = true; + return NULL; +} + +// void *visit( RETC_operator_c *symbol); +// void *visit(RETCN_operator_c *symbol); + +/* this next visit function will be called from visit(il_jump_operation_c *) */ +void *flow_control_analysis_c::visit( JMP_operator_c *symbol) { + prev_il_instruction_is_JMP_or_RET = true; + return NULL; +} + +// void *visit( JMPC_operator_c *symbol); +// void *visit(JMPCN_operator_c *symbol); + +/* Symbol class handled together with function call checks */ +// void *visit(il_assign_operator_c *symbol, variable_name); +/* Symbol class handled together with function call checks */ +// void *visit(il_assign_operator_c *symbol, option, variable_name); + diff -r aad38592bdde -r c0bda77b37a0 stage3/flow_control_analysis.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/flow_control_analysis.hh Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,162 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2012 Mario de Sousa (msousa@fe.up.pt) + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Do flow control analysis of the IEC 61131-3 code. + * + * We currently only do this for IL code. + * This class will annotate the abstract syntax tree, by filling in the + * prev_il_instruction variable in the il_instruction_c, so it points to + * the previous il_instruction_c object in the instruction list instruction_list_c. + */ + + + +#include "../absyntax_utils/absyntax_utils.hh" + + +class flow_control_analysis_c: public iterator_visitor_c { + + private: + search_il_label_c *search_il_label; + symbol_c *prev_il_instruction; + symbol_c *curr_il_instruction; + bool prev_il_instruction_is_JMP_or_RET; + + public: + flow_control_analysis_c(symbol_c *ignore); + virtual ~flow_control_analysis_c(void); + + /**************************************/ + /* B 1.5 - Program organization units */ + /**************************************/ + /***********************/ + /* B 1.5.1 - Functions */ + /***********************/ + void *visit(function_declaration_c *symbol); + + /*****************************/ + /* B 1.5.2 - Function blocks */ + /*****************************/ + void *visit(function_block_declaration_c *symbol); + + /**********************/ + /* B 1.5.3 - Programs */ + /**********************/ + void *visit(program_declaration_c *symbol); + + /********************************/ + /* B 1.7 Configuration elements */ + /********************************/ + void *visit(configuration_declaration_c *symbol); + + /****************************************/ + /* B.2 - Language IL (Instruction List) */ + /****************************************/ + /***********************************/ + /* B 2.1 Instructions and Operands */ + /***********************************/ + void *visit(instruction_list_c *symbol); + void *visit(il_instruction_c *symbol); +// void *visit(il_simple_operation_c *symbol); +// void *visit(il_function_call_c *symbol); + void *visit(il_expression_c *symbol); + void *visit(il_jump_operation_c *symbol); +// void *visit(il_fb_call_c *symbol); +// void *visit(il_formal_funct_call_c *symbol); +// void *visit(il_operand_list_c *symbol); + void *visit(simple_instr_list_c *symbol); + void *visit(il_simple_instruction_c*symbol); +// void *visit(il_param_list_c *symbol); +// void *visit(il_param_assignment_c *symbol); +// void *visit(il_param_out_assignment_c *symbol); + + /*******************/ + /* B 2.2 Operators */ + /*******************/ +// void *visit( LD_operator_c *symbol); +// void *visit( LDN_operator_c *symbol); +// void *visit( ST_operator_c *symbol); +// void *visit( STN_operator_c *symbol); +// void *visit( NOT_operator_c *symbol); +// void *visit( S_operator_c *symbol); +// void *visit( R_operator_c *symbol); +// void *visit( S1_operator_c *symbol); +// void *visit( R1_operator_c *symbol); +// void *visit( CLK_operator_c *symbol); +// void *visit( CU_operator_c *symbol); +// void *visit( CD_operator_c *symbol); +// void *visit( PV_operator_c *symbol); +// void *visit( IN_operator_c *symbol); +// void *visit( PT_operator_c *symbol); +// void *visit( AND_operator_c *symbol); +// void *visit( OR_operator_c *symbol); +// void *visit( XOR_operator_c *symbol); +// void *visit( ANDN_operator_c *symbol); +// void *visit( ORN_operator_c *symbol); +// void *visit( XORN_operator_c *symbol); +// void *visit( ADD_operator_c *symbol); +// void *visit( SUB_operator_c *symbol); +// void *visit( MUL_operator_c *symbol); +// void *visit( DIV_operator_c *symbol); +// void *visit( MOD_operator_c *symbol); +// void *visit( GT_operator_c *symbol); +// void *visit( GE_operator_c *symbol); +// void *visit( EQ_operator_c *symbol); +// void *visit( LT_operator_c *symbol); +// void *visit( LE_operator_c *symbol); +// void *visit( NE_operator_c *symbol); +// void *visit( CAL_operator_c *symbol); +// void *visit( CALC_operator_c *symbol); +// void *visit(CALCN_operator_c *symbol); + void *visit( RET_operator_c *symbol); +// void *visit( RETC_operator_c *symbol); +// void *visit(RETCN_operator_c *symbol); + void *visit( JMP_operator_c *symbol); +// void *visit( JMPC_operator_c *symbol); +// void *visit(JMPCN_operator_c *symbol); + + /* Symbol class handled together with function call checks */ + // void *visit(il_assign_operator_c *symbol, variable_name); + /* Symbol class handled together with function call checks */ + // void *visit(il_assign_operator_c *symbol, option, variable_name); + +}; // flow_control_analysis_c + + + + + + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/lvalue_check.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/lvalue_check.cc Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,559 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (conti.ma@alice.it) + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + + +/* Expressions on the left hand side of assignment statements have aditional restrictions on their datatype. + * For example, they cannot be literals, CONSTANT type variables, function invocations, etc... + * This class wil do those checks. + * + * Note that assignment may also be done when passing variables to OUTPUT or IN_OUT function parameters,so we check those too. + */ + + + +#include "lvalue_check.hh" + +#define FIRST_(symbol1, symbol2) (((symbol1)->first_order < (symbol2)->first_order) ? (symbol1) : (symbol2)) +#define LAST_(symbol1, symbol2) (((symbol1)->last_order > (symbol2)->last_order) ? (symbol1) : (symbol2)) + +#define STAGE3_ERROR(error_level, symbol1, symbol2, ...) { \ + if (current_display_error_level >= error_level) { \ + fprintf(stderr, "%s:%d-%d..%d-%d: error: ", \ + FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ + LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + error_count++; \ + } \ +} + + +#define STAGE3_WARNING(symbol1, symbol2, ...) { \ + fprintf(stderr, "%s:%d-%d..%d-%d: warning: ", \ + FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ + LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + warning_found = true; \ +} + + +lvalue_check_c::lvalue_check_c(symbol_c *ignore) { + error_count = 0; + current_display_error_level = 0; + current_il_operand = NULL; +} + +lvalue_check_c::~lvalue_check_c(void) { +} + +int lvalue_check_c::get_error_count() { + return error_count; +} + + +#include +/* No writing to iterator variables (used in FOR loops) inside the loop itself */ +void lvalue_check_c::check_assignment_to_controlvar(symbol_c *lvalue) { + for (unsigned int i = 0; i < control_variables.size(); i++) { + token_c *lvalue_name = get_var_name_c::get_name(lvalue); + if (compare_identifiers(lvalue_name, control_variables[i]) == 0) { + STAGE3_ERROR(0, lvalue, lvalue, "Assignment to FOR control variable is not allowed."); + break; + } + } +} + + +/* fb_instance.var := ... is not valid if var is output variable */ +/* NOTE, if a fb_instance1.fb_instance2.fb_instance3.var is used, we must iteratively check that none of the + * FB records are declared as OUTPUT variables!! + * This is the reason why we have the while() loop in this function! + * + * Note, however, that the first record (fb_instance1 in the above example) may be an output variable! + */ +void lvalue_check_c::check_assignment_to_output(symbol_c *lvalue) { + decompose_var_instance_name_c decompose_lvalue(lvalue); + search_base_type_c search_base_type; + + /* Get the first element/record of the potentially structured variable symbol */ + /* Note that if symbol is pointing to an expression (or simply a literal value), it will return a NULL. + * Once we have implemented the check_assignment_to_expression() method, and abort calling the other checks (including this one) + * when an expression is found, we may replace this check with an assertion... + * if (NULL == struct_elem) ERROR; + */ + symbol_c *struct_elem = decompose_lvalue.next_part(); + if (NULL == struct_elem) return; + + symbol_c *type_decl = search_var_instance_decl->get_decl(struct_elem); + // symbol_c *type_id = spec_init_sperator_c::get_spec(type_decl); /* this is not required! search_base_type_c can handle spec_init symbols! */ + symbol_c *basetype_id = search_base_type.get_basetype_id(/*type_id*/ type_decl); + /* If we can not determine the data type of the element, then the code must have a data type semantic error. + * This will have been caught by the data type semantic verifier, so we do not bother with this anymore! + */ + if (NULL == basetype_id) return; + + /* Determine if the record/structure element is of a FB type. */ + /* NOTE: If the structure element is not a FB type, then we can quit this check. + * Remember that the standard does not allow a STRUCT data type to have elements that are FB instances! + * Similarly, arrays of FB instances is also not allowed. + * So, as soon as we find one record/structure element that is not a FB, no other record/structure element + * will be of FB type, which means we can quit this check! + */ + function_block_declaration_c *fb_decl = function_block_type_symtable.find_value(basetype_id); + if (function_block_type_symtable.end_value() == fb_decl) return; + + while (NULL != (struct_elem = decompose_lvalue.next_part())) { + search_var_instance_decl_c fb_search_var_instance_decl(fb_decl); + if (search_var_instance_decl_c::output_vt == fb_search_var_instance_decl.get_vartype(struct_elem)) { + STAGE3_ERROR(0, struct_elem, struct_elem, "Assignment to FB output variable is not allowed."); + return; /* no need to carry on checking once the first error is found! */ + } + + /* prepare for any possible further record/structure elements */ + type_decl = fb_search_var_instance_decl.get_decl(struct_elem); + basetype_id = search_base_type.get_basetype_id(type_decl); + if (NULL == basetype_id) return; /* same comment as above... */ + fb_decl = function_block_type_symtable.find_value(basetype_id); + if (function_block_type_symtable.end_value() == fb_decl) return; /* same comment as above... */ + } +} + + +/* No writing to CONSTANTs */ +void lvalue_check_c::check_assignment_to_constant(symbol_c *lvalue) { + unsigned int option = search_var_instance_decl->get_option(lvalue); + if (option == search_var_instance_decl_c::constant_opt) { + STAGE3_ERROR(0, lvalue, lvalue, "Assignment to CONSTANT variables is not be allowed."); + } +} + + +/* No assigning values to expressions. */ +void lvalue_check_c::check_assignment_to_expression(symbol_c *lvalue) { + /* This may occur in function invocations, when passing values (possibly an expression) to one + * of the function's OUTPUT or IN_OUT parameters. + */ + /* This may occur in function invocations, when passing values (possibly an expression) to one + * of the function's OUTPUT or IN_OUT parameters. + */ + if ( + /*********************/ + /* B 1.2 - Constants */ + /*********************/ + /******************************/ + /* B 1.2.1 - Numeric Literals */ + /******************************/ + (typeid( *lvalue ) == typeid( real_c )) || + (typeid( *lvalue ) == typeid( integer_c )) || + (typeid( *lvalue ) == typeid( binary_integer_c )) || + (typeid( *lvalue ) == typeid( octal_integer_c )) || + (typeid( *lvalue ) == typeid( hex_integer_c )) || + (typeid( *lvalue ) == typeid( neg_real_c )) || + (typeid( *lvalue ) == typeid( neg_integer_c )) || + (typeid( *lvalue ) == typeid( integer_literal_c )) || + (typeid( *lvalue ) == typeid( real_literal_c )) || + (typeid( *lvalue ) == typeid( bit_string_literal_c )) || + (typeid( *lvalue ) == typeid( boolean_literal_c )) || + (typeid( *lvalue ) == typeid( boolean_true_c )) || /* should not really be needed */ + (typeid( *lvalue ) == typeid( boolean_false_c )) || /* should not really be needed */ + /*******************************/ + /* B.1.2.2 Character Strings */ + /*******************************/ + (typeid( *lvalue ) == typeid( double_byte_character_string_c )) || + (typeid( *lvalue ) == typeid( single_byte_character_string_c )) || + /***************************/ + /* B 1.2.3 - Time Literals */ + /***************************/ + /************************/ + /* B 1.2.3.1 - Duration */ + /************************/ + (typeid( *lvalue ) == typeid( duration_c )) || + /************************************/ + /* B 1.2.3.2 - Time of day and Date */ + /************************************/ + (typeid( *lvalue ) == typeid( time_of_day_c )) || + (typeid( *lvalue ) == typeid( daytime_c )) || /* should not really be needed */ + (typeid( *lvalue ) == typeid( date_c )) || /* should not really be needed */ + (typeid( *lvalue ) == typeid( date_literal_c )) || + (typeid( *lvalue ) == typeid( date_and_time_c )) || + /***************************************/ + /* B.3 - Language ST (Structured Text) */ + /***************************************/ + /***********************/ + /* B 3.1 - Expressions */ + /***********************/ + (typeid( *lvalue ) == typeid( or_expression_c )) || + (typeid( *lvalue ) == typeid( xor_expression_c )) || + (typeid( *lvalue ) == typeid( and_expression_c )) || + (typeid( *lvalue ) == typeid( equ_expression_c )) || + (typeid( *lvalue ) == typeid( notequ_expression_c )) || + (typeid( *lvalue ) == typeid( lt_expression_c )) || + (typeid( *lvalue ) == typeid( gt_expression_c )) || + (typeid( *lvalue ) == typeid( le_expression_c )) || + (typeid( *lvalue ) == typeid( ge_expression_c )) || + (typeid( *lvalue ) == typeid( add_expression_c )) || + (typeid( *lvalue ) == typeid( sub_expression_c )) || + (typeid( *lvalue ) == typeid( mul_expression_c )) || + (typeid( *lvalue ) == typeid( div_expression_c )) || + (typeid( *lvalue ) == typeid( mod_expression_c )) || + (typeid( *lvalue ) == typeid( power_expression_c )) || + (typeid( *lvalue ) == typeid( neg_expression_c )) || + (typeid( *lvalue ) == typeid( not_expression_c )) || + (typeid( *lvalue ) == typeid( function_invocation_c ))) + STAGE3_ERROR(0, lvalue, lvalue, "Assigning an expression to an OUT or IN_OUT parameter is not allowed."); +} + + + + + + + +void lvalue_check_c::verify_is_lvalue(symbol_c *lvalue) { + int init_error_count = error_count; /* stop the checks once an error has been found... */ + if (error_count == init_error_count) check_assignment_to_expression(lvalue); + if (error_count == init_error_count) check_assignment_to_controlvar(lvalue); + if (error_count == init_error_count) check_assignment_to_output(lvalue); + if (error_count == init_error_count) check_assignment_to_constant(lvalue); +} + + + + +/* check whether all values passed to OUT or IN_OUT parameters are legal lvalues. */ +/* + * All parameters being passed to the called function MUST be in the parameter list to which f_call points to! + * This means that, for non formal function calls in IL, de current (default value) must be artificially added to the + * beginning of the parameter list BEFORE calling handle_function_call(). + */ +#include /* required for strcmp() */ +void lvalue_check_c::check_nonformal_call(symbol_c *f_call, symbol_c *f_decl) { + symbol_c *call_param_value; + identifier_c *param_name; + function_param_iterator_c fp_iterator(f_decl); + function_call_param_iterator_c fcp_iterator(f_call); + + /* Iterating through the non-formal parameters of the function call */ + while((call_param_value = fcp_iterator.next_nf()) != NULL) { + /* Iterate to the next parameter of the function being called. + * Get the name of that parameter, and ignore if EN or ENO. + */ + do { + param_name = fp_iterator.next(); + /* If there is no other parameter declared, then we are passing too many parameters... */ + /* This error should have been caught in data type verification, so we simply abandon our check! */ + if(param_name == NULL) return; + } while ((strcmp(param_name->value, "EN") == 0) || (strcmp(param_name->value, "ENO") == 0)); + + /* Determine the direction (IN, OUT, IN_OUT) of the parameter... */ + function_param_iterator_c::param_direction_t param_direction = fp_iterator.param_direction(); + + /* We only process the parameter value if the paramater itself is valid... */ + if (param_name != NULL) { + /* If the parameter is either OUT or IN_OUT, we check if 'call_param_value' is a valid lvalue */ + if ((function_param_iterator_c::direction_out == param_direction) || (function_param_iterator_c::direction_inout == param_direction)) + verify_is_lvalue(call_param_value); + /* parameter values to IN parameters may be expressions with function invocations that must also be checked! */ + if (function_param_iterator_c::direction_in == param_direction) + call_param_value->accept(*this); + } + } +} + + + + + + +/* check whether all values passed to OUT or IN_OUT parameters are legal lvalues. */ +void lvalue_check_c::check_formal_call(symbol_c *f_call, symbol_c *f_decl) { + /* if data type semantic verification was unable to determine which function is being called, + * then it does not make sense to go ahead and check for lvalues to unknown parameters. + * We simply bug out! + */ + if (NULL == f_decl) return; + + symbol_c *call_param_name; + function_param_iterator_c fp_iterator(f_decl); + function_call_param_iterator_c fcp_iterator(f_call); + + /* Iterating through the formal parameters of the function call */ + while((call_param_name = fcp_iterator.next_f()) != NULL) { + + /* Obtaining the value being passed in the function call */ + symbol_c *call_param_value = fcp_iterator.get_current_value(); + if (NULL == call_param_value) ERROR; + + /* Find the corresponding parameter in function declaration, and it's direction (IN, OUT, IN_OUT) */ + identifier_c *param_name = fp_iterator.search(call_param_name); + function_param_iterator_c::param_direction_t param_direction = fp_iterator.param_direction(); + + /* We only process the parameter value if the paramater itself is valid... */ + if (param_name != NULL) { + /* If the parameter is either OUT or IN_OUT, we check if 'call_param_value' is a valid lvalue */ + if ((function_param_iterator_c::direction_out == param_direction) || (function_param_iterator_c::direction_inout == param_direction)) + verify_is_lvalue(call_param_value); + /* parameter values to IN parameters may be expressions with function invocations that must also be checked! */ + if (function_param_iterator_c::direction_in == param_direction) + call_param_value->accept(*this); + + } + } +} + + + + + + + +/**************************************/ +/* B 1.5 - Program organisation units */ +/**************************************/ +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ +void *lvalue_check_c::visit(function_declaration_c *symbol) { + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + search_var_instance_decl = new search_var_instance_decl_c(symbol); + symbol->function_body->accept(*this); + delete search_varfb_instance_type; + delete search_var_instance_decl; + search_varfb_instance_type = NULL; + search_var_instance_decl = NULL; + return NULL; +} + +/*****************************/ +/* B 1.5.2 - Function blocks */ +/*****************************/ +void *lvalue_check_c::visit(function_block_declaration_c *symbol) { + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + search_var_instance_decl = new search_var_instance_decl_c(symbol); + symbol->fblock_body->accept(*this); + delete search_varfb_instance_type; + delete search_var_instance_decl; + search_varfb_instance_type = NULL; + search_var_instance_decl = NULL; + return NULL; +} + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ +void *lvalue_check_c::visit(program_declaration_c *symbol) { + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + search_var_instance_decl = new search_var_instance_decl_c(symbol); + symbol->function_block_body->accept(*this); + delete search_varfb_instance_type; + delete search_var_instance_decl; + search_varfb_instance_type = NULL; + search_var_instance_decl = NULL; + return NULL; +} + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ +void *lvalue_check_c::visit(il_instruction_c *symbol) { + /* il_instruction will be NULL when parsing a label with no instruction + * e.g.: label1: <---- il_instruction = NULL! + * LD 33 + * ... + */ + if (NULL != symbol->il_instruction) + symbol->il_instruction->accept(*this); + return NULL; +} + +void *lvalue_check_c::visit(il_simple_operation_c *symbol) { + current_il_operand = symbol->il_operand; + symbol->il_simple_operator->accept(*this); + current_il_operand = NULL; + return NULL; +} + + + + +/* | function_name [il_operand_list] */ +/* NOTE: The parameters 'called_function_declaration' and 'extensible_param_count' are used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;) +void *lvalue_check_c::visit(il_function_call_c *symbol) { + /* The first parameter of a non formal function call in IL will be the 'current value' (i.e. the prev_il_instruction) + * In order to be able to handle this without coding special cases, we will simply prepend that symbol + * to the il_operand_list, and remove it after calling handle_function_call(). + * + * However, if no further paramters are given, then il_operand_list will be NULL, and we will + * need to create a new object to hold the pointer to prev_il_instruction. + * This change will also be undone at the end of this method. + */ + /* TODO: Copying the location data will result in confusing error message. + * We need to make this better, by inserting code to handle this special situation explicitly! + */ + /* NOTE: When calling a function, using the 'current value' as the first parameter of the function invocation + * implies that we can only call functions whose first parameter is IN. It would not do to pass + * the 'current value' to an OUT or IN_OUT parameter. + * In order to make sure that this will be caught by the check_nonformal_call() function, + * we add a symbol that cannot be an lvalue; in this case, a real_c (REAL literal). + */ + real_c param_value(NULL); + *((symbol_c *)(¶m_value)) = *((symbol_c *)symbol); /* copy the symbol location (file, line, offset) data */ + if (NULL == symbol->il_operand_list) symbol->il_operand_list = new il_operand_list_c; + if (NULL == symbol->il_operand_list) ERROR; + ((list_c *)symbol->il_operand_list)->insert_element(¶m_value, 0); + + check_nonformal_call(symbol, symbol->called_function_declaration); + + /* Undo the changes to the abstract syntax tree we made above... */ + ((list_c *)symbol->il_operand_list)->remove_element(0); + if (((list_c *)symbol->il_operand_list)->n == 0) { + /* if the list becomes empty, then that means that it did not exist before we made these changes, so we delete it! */ + delete symbol->il_operand_list; + symbol->il_operand_list = NULL; + } + + return NULL; +} + + + + + + +/* il_call_operator prev_declared_fb_name + * | il_call_operator prev_declared_fb_name '(' ')' + * | il_call_operator prev_declared_fb_name '(' eol_list ')' + * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' + * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' + */ +/* NOTE: The parameter 'called_fb_declaration'is used to pass data between stage 3 and stage4 (although currently it is not used in stage 4 */ +// SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list, symbol_c *called_fb_declaration) +void *lvalue_check_c::visit(il_fb_call_c *symbol) { + if (NULL != symbol->il_operand_list) check_nonformal_call(symbol, symbol->called_fb_declaration); + if (NULL != symbol-> il_param_list) check_formal_call(symbol, symbol->called_fb_declaration); + return NULL; +} + + +/* | function_name '(' eol_list [il_param_list] ')' */ +/* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;) +void *lvalue_check_c::visit(il_formal_funct_call_c *symbol) { + check_formal_call(symbol, symbol->called_function_declaration); + return NULL; +} + + + + + + +/*******************/ +/* B 2.2 Operators */ +/*******************/ +void *lvalue_check_c::visit(ST_operator_c *symbol) { + verify_is_lvalue(current_il_operand); + return NULL; +} + +void *lvalue_check_c::visit(STN_operator_c *symbol) { + verify_is_lvalue(current_il_operand); + return NULL; +} + +void *lvalue_check_c::visit(S_operator_c *symbol) { + verify_is_lvalue(current_il_operand); + return NULL; +} + +void *lvalue_check_c::visit(R_operator_c *symbol) { + verify_is_lvalue(current_il_operand); + return NULL; +} + + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ +// SYM_REF3(function_invocation_c, function_name, formal_param_list, nonformal_param_list, symbol_c *called_function_declaration; int extensible_param_count; std::vector candidate_functions;) +void *lvalue_check_c::visit(function_invocation_c *symbol) { + if (NULL != symbol->formal_param_list ) check_formal_call (symbol, symbol->called_function_declaration); + if (NULL != symbol->nonformal_param_list) check_nonformal_call(symbol, symbol->called_function_declaration); + return NULL; +} + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ +void *lvalue_check_c::visit(assignment_statement_c *symbol) { + verify_is_lvalue(symbol->l_exp); + /* We call visit r_exp to check function_call */ + symbol->r_exp->accept(*this); + return NULL; +} + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ +void *lvalue_check_c::visit(fb_invocation_c *symbol) { + if (NULL != symbol->formal_param_list ) check_formal_call (symbol, symbol->called_fb_declaration); + if (NULL != symbol->nonformal_param_list) check_nonformal_call(symbol, symbol->called_fb_declaration); + return NULL; +} + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ +void *lvalue_check_c::visit(for_statement_c *symbol) { + verify_is_lvalue(symbol->control_variable); + control_variables.push_back(get_var_name_c::get_name(symbol->control_variable)); + symbol->statement_list->accept(*this); + control_variables.pop_back(); + return NULL; +} + + + + + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/lvalue_check.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/lvalue_check.hh Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,141 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (conti.ma@alice.it) + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +#include +#include "../absyntax_utils/absyntax_utils.hh" +#include "datatype_functions.hh" + + +/* Expressions on the left hand side of assignment statements have aditional restrictions on their datatype. + * For example, they cannot be literals, CONSTANT type variables, function invocations, etc... + * This class wil do those checks. + * + * Note that assignment may also be done when passing variables to OUTPUT or IN_OUT function parameters,so we check those too. + */ + + + +class lvalue_check_c: public iterator_visitor_c { + + private: + search_varfb_instance_type_c *search_varfb_instance_type; + search_var_instance_decl_c *search_var_instance_decl; + search_base_type_c search_base_type; + int error_count; + int current_display_error_level; + std::vector control_variables; + symbol_c *current_il_operand; + + void verify_is_lvalue (symbol_c *lvalue); + void check_assignment_to_controlvar(symbol_c *lvalue); + void check_assignment_to_output (symbol_c *lvalue); + void check_assignment_to_constant (symbol_c *lvalue); + void check_assignment_to_expression(symbol_c *lvalue); + void check_formal_call (symbol_c *f_call, symbol_c *f_decl); + void check_nonformal_call(symbol_c *f_call, symbol_c *f_decl); + + + public: + lvalue_check_c(symbol_c *ignore); + virtual ~lvalue_check_c(void); + int get_error_count(); + + /**************************************/ + /* B 1.5 - Program organisation units */ + /**************************************/ + /***********************/ + /* B 1.5.1 - Functions */ + /***********************/ + void *visit(function_declaration_c *symbol); + + /*****************************/ + /* B 1.5.2 - Function blocks */ + /*****************************/ + void *visit(function_block_declaration_c *symbol); + + /**********************/ + /* B 1.5.3 - Programs */ + /**********************/ + void *visit(program_declaration_c *symbol); + + /****************************************/ + /* B.2 - Language IL (Instruction List) */ + /****************************************/ + /***********************************/ + /* B 2.1 Instructions and Operands */ + /***********************************/ + void *visit(il_instruction_c *symbol); + void *visit(il_simple_operation_c *symbol); + void *visit(il_function_call_c *symbol); + void *visit(il_fb_call_c *symbol); + void *visit(il_formal_funct_call_c *symbol); + + /*******************/ + /* B 2.2 Operators */ + /*******************/ + void *visit(ST_operator_c *symbol); + void *visit(STN_operator_c *symbol); + void *visit(S_operator_c *symbol); + void *visit(R_operator_c *symbol); + + /***************************************/ + /* B.3 - Language ST (Structured Text) */ + /***************************************/ + /***********************/ + /* B 3.1 - Expressions */ + /***********************/ + void *visit(function_invocation_c *symbol); + + /*********************************/ + /* B 3.2.1 Assignment Statements */ + /*********************************/ + void *visit(assignment_statement_c *symbol); + + /*****************************************/ + /* B 3.2.2 Subprogram Control Statements */ + /*****************************************/ + void *visit(fb_invocation_c *symbol); + + /********************************/ + /* B 3.2.4 Iteration Statements */ + /********************************/ + void *visit(for_statement_c *symbol); + +}; /* lvalue_check_c */ + + + + + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/narrow_candidate_datatypes.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/narrow_candidate_datatypes.cc Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,1328 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (manuele.conti@sirius-es.it) + * Copyright (C) 2012 Matteo Facchinetti (matteo.facchinetti@sirius-es.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* NOTE: The algorithm implemented here assumes that candidate datatype lists have already been filled! + * BEFORE running this visitor, be sure to CALL the fill_candidate_datatype_c visitor! + */ + + +/* + * Choose, from the list of all the possible datatypes each expression may take, the single datatype that it will in fact take. + * The resulting (chosen) datatype, will be stored in the symbol_c.datatype variable, leaving the candidate datatype list untouched! + * + * For rvalue expressions, this decision will be based on the datatype of the lvalue expression. + * For lvalue expressions, the candidate datatype list should have a single entry. + * + * For example, the very simple literal '0' in 'foo := 0', may represent a: + * BOOL, BYTE, WORD, DWORD, LWORD, USINT, SINT, UINT, INT, UDINT, DINT, ULINT, LINT (as well as the SAFE versions of these data tyes too!) + * + * In this class, the datatype of '0' will be set to the same datatype as the 'foo' variable. + * If the intersection of the candidate datatype lists of the left and right side expressions is empty, + * then a datatype error has been found, and the datatype is either left at NULL, or set to a pointer of an invalid_type_name_c object! + */ + + +#include "narrow_candidate_datatypes.hh" +#include "datatype_functions.hh" +#include +#include +#include +#include +#include + + +/* set to 1 to see debug info during execution */ +static int debug = 0; + +narrow_candidate_datatypes_c::narrow_candidate_datatypes_c(symbol_c *ignore) { +} + +narrow_candidate_datatypes_c::~narrow_candidate_datatypes_c(void) { +} + + +/* Only set the symbol's desired datatype to 'datatype' if that datatype is in the candidate_datatype list */ +static void set_datatype(symbol_c *datatype, symbol_c *symbol) { + + /* If we are trying to set to the undefined type, and the symbol's datatype has already been set to something else, + * we abort the compoiler as I don't think this should ever occur. + * NOTE: In order to handle JMPs to labels that come before the JMP itself, we run the narrow algorithm twice. + * This means that this situation may legally occur, so we cannot abort the compiler here! + */ +// if ((NULL == datatype) && (NULL != symbol->datatype)) ERROR; + if ((NULL == datatype) && (NULL != symbol->datatype)) return; + if ((NULL == datatype) && (NULL == symbol->datatype)) return; + + if (search_in_candidate_datatype_list(datatype, symbol->candidate_datatypes) < 0) + symbol->datatype = &(search_constant_type_c::invalid_type_name); + else { + if (NULL == symbol->datatype) + /* not yet set to anything, so we set it to the requested data type */ + symbol->datatype = datatype; + else { + /* had already been set previously to some data type. Let's check if they are the same! */ + if (!is_type_equal(symbol->datatype, datatype)) + symbol->datatype = &(search_constant_type_c::invalid_type_name); +// else + /* we leave it unchanged, as it is the same as the requested data type! */ + } + } +} + + + +/* Only set the symbol's desired datatype to 'datatype' if that datatype is in the candidate_datatype list */ +// static void set_datatype_in_prev_il_instructions(symbol_c *datatype, std::vector prev_il_instructions) { +static void set_datatype_in_prev_il_instructions(symbol_c *datatype, il_instruction_c *symbol) { + if (NULL == symbol) ERROR; + for (unsigned int i = 0; i < symbol->prev_il_instruction.size(); i++) + set_datatype(datatype, symbol->prev_il_instruction[i]); +} + + + +bool narrow_candidate_datatypes_c::is_widening_compatible(const struct widen_entry widen_table[], symbol_c *left_type, symbol_c *right_type, symbol_c *result_type, bool *deprecated_status) { + /* NOTE: According to our algorithm, left_type and right_type should never by NULL (if they are, we have an internal compiler error! + * However, result_type may be NULL if the code has a data type semantic error! + */ + if ((NULL == left_type) || (NULL == right_type) || (NULL == result_type)) + return false; + + for (int k = 0; NULL != widen_table[k].left; k++) { + if ((typeid(*left_type) == typeid(*widen_table[k].left)) + && (typeid(*right_type) == typeid(*widen_table[k].right)) + && (typeid(*result_type) == typeid(*widen_table[k].result))) { + if (NULL != deprecated_status) + *deprecated_status = (widen_table[k].status == widen_entry::deprecated); + return true; + } + } + return false; +} + +/* + * All parameters being passed to the called function MUST be in the parameter list to which f_call points to! + * This means that, for non formal function calls in IL, de current (default value) must be artificially added to the + * beginning of the parameter list BEFORE calling handle_function_call(). + */ +void narrow_candidate_datatypes_c::narrow_nonformal_call(symbol_c *f_call, symbol_c *f_decl, int *ext_parm_count) { + symbol_c *call_param_value, *param_type; + identifier_c *param_name; + function_param_iterator_c fp_iterator(f_decl); + function_call_param_iterator_c fcp_iterator(f_call); + int extensible_parameter_highest_index = -1; + unsigned int i; + + if (NULL != ext_parm_count) *ext_parm_count = -1; + + /* Iterating through the non-formal parameters of the function call */ + while((call_param_value = fcp_iterator.next_nf()) != NULL) { + /* Obtaining the type of the value being passed in the function call */ + /* Iterate to the next parameter of the function being called. + * Get the name of that parameter, and ignore if EN or ENO. + */ + do { + param_name = fp_iterator.next(); + /* If there is no other parameter declared, then we are passing too many parameters... */ + /* This error should have been caught in fill_candidate_datatypes_c, but may occur here again when we handle FB invocations! + * In this case, we carry on analysing the code in order to be able to provide relevant error messages + * for that code too! + */ + if(param_name == NULL) break; + } while ((strcmp(param_name->value, "EN") == 0) || (strcmp(param_name->value, "ENO") == 0)); + + /* Set the desired datatype for this parameter, and call it recursively. */ + /* Note that if the call has more parameters than those declared in the function/FB declaration, + * we may be setting this to NULL! + */ + symbol_c *desired_datatype = base_type(fp_iterator.param_type()); + if ((NULL != param_name) && (NULL == desired_datatype)) ERROR; + if ((NULL == param_name) && (NULL != desired_datatype)) ERROR; + + /* NOTE: When we are handling a nonformal function call made from IL, the first parameter is the 'default' or 'current' + * il value. However, a pointer to a copy of the prev_il_instruction is pre-pended into the operand list, so + * the call + * call_param_value->accept(*this); + * may actually be calling an object of the base symbol_c . + */ + set_datatype(desired_datatype, call_param_value); + call_param_value->accept(*this); + + if (NULL != param_name) + if (extensible_parameter_highest_index < fp_iterator.extensible_param_index()) + extensible_parameter_highest_index = fp_iterator.extensible_param_index(); + } + /* In the case of a call to an extensible function, we store the highest index + * of the extensible parameters this particular call uses, in the symbol_c object + * of the function call itself! + * In calls to non-extensible functions, this value will be set to -1. + * This information is later used in stage4 to correctly generate the + * output code. + */ + if ((NULL != ext_parm_count) && (extensible_parameter_highest_index >=0) /* if call to extensible function */) + *ext_parm_count = 1 + extensible_parameter_highest_index - fp_iterator.first_extensible_param_index(); +} + + + +void narrow_candidate_datatypes_c::narrow_formal_call(symbol_c *f_call, symbol_c *f_decl, int *ext_parm_count) { + symbol_c *call_param_value, *call_param_name, *param_type; + symbol_c *verify_duplicate_param; + identifier_c *param_name; + function_param_iterator_c fp_iterator(f_decl); + function_call_param_iterator_c fcp_iterator(f_call); + int extensible_parameter_highest_index = -1; + identifier_c *extensible_parameter_name; + unsigned int i; + + if (NULL != ext_parm_count) *ext_parm_count = -1; + /* Iterating through the formal parameters of the function call */ + while((call_param_name = fcp_iterator.next_f()) != NULL) { + + /* Obtaining the value being passed in the function call */ + call_param_value = fcp_iterator.get_current_value(); + /* the following should never occur. If it does, then we have a bug in our code... */ + if (NULL == call_param_value) ERROR; + + /* Find the corresponding parameter in function declaration */ + param_name = fp_iterator.search(call_param_name); + + /* Set the desired datatype for this parameter, and call it recursively. */ + /* NOTE: When handling a FB call, this narrow_formal_call() may be called to analyse + * an invalid FB call (call with parameters that do not exist on the FB declaration). + * For this reason, the param_name may come out as NULL! + */ + symbol_c *desired_datatype = base_type(fp_iterator.param_type()); + if ((NULL != param_name) && (NULL == desired_datatype)) ERROR; + if ((NULL == param_name) && (NULL != desired_datatype)) ERROR; + + /* set the desired data type for this parameter */ + set_datatype(desired_datatype, call_param_value); + /* And recursively call that parameter/expression, so it can propagate that info */ + call_param_value->accept(*this); + + /* set the extensible_parameter_highest_index, which will be needed in stage 4 */ + /* This value says how many extensible parameters are being passed to the standard function */ + if (NULL != param_name) + if (extensible_parameter_highest_index < fp_iterator.extensible_param_index()) + extensible_parameter_highest_index = fp_iterator.extensible_param_index(); + } + /* call is compatible! */ + + /* In the case of a call to an extensible function, we store the highest index + * of the extensible parameters this particular call uses, in the symbol_c object + * of the function call itself! + * In calls to non-extensible functions, this value will be set to -1. + * This information is later used in stage4 to correctly generate the + * output code. + */ + if ((NULL != ext_parm_count) && (extensible_parameter_highest_index >=0) /* if call to extensible function */) + *ext_parm_count = 1 + extensible_parameter_highest_index - fp_iterator.first_extensible_param_index(); +} + + +/* +typedef struct { + symbol_c *function_name, + symbol_c *nonformal_operand_list, + symbol_c * formal_operand_list, + + std::vector &candidate_functions, + symbol_c &*called_function_declaration, + int &extensible_param_count +} generic_function_call_t; +*/ +void narrow_candidate_datatypes_c::narrow_function_invocation(symbol_c *fcall, generic_function_call_t fcall_data) { + /* set the called_function_declaration. */ + fcall_data.called_function_declaration = NULL; + + /* set the called_function_declaration taking into account the datatype that we need to return */ + for(unsigned int i = 0; i < fcall->candidate_datatypes.size(); i++) { + if (is_type_equal(fcall->candidate_datatypes[i], fcall->datatype)) { + fcall_data.called_function_declaration = fcall_data.candidate_functions[i]; + break; + } + } + + /* NOTE: If we can't figure out the declaration of the function being called, this is not + * necessarily an internal compiler error. It could be because the symbol->datatype is NULL + * (because the ST code being analysed has an error _before_ this function invocation). + * However, we don't just give, up, we carry on recursivly analysing the code, so as to be + * able to print out any error messages related to the parameters being passed in this function + * invocation. + */ + /* if (NULL == symbol->called_function_declaration) ERROR; */ + if (fcall->candidate_datatypes.size() == 1) { + /* If only one function declaration, then we use that (even if symbol->datatypes == NULL) + * so we can check for errors in the expressions used to pass parameters in this + * function invocation. + */ + fcall_data.called_function_declaration = fcall_data.candidate_functions[0]; + } + + /* If an overloaded function is being invoked, and we cannot determine which version to use, + * then we can not meaningfully verify the expressions used inside that function invocation. + * We simply give up! + */ + if (NULL == fcall_data.called_function_declaration) + return; + + if (NULL != fcall_data.nonformal_operand_list) narrow_nonformal_call(fcall, fcall_data.called_function_declaration, &(fcall_data.extensible_param_count)); + if (NULL != fcall_data. formal_operand_list) narrow_formal_call(fcall, fcall_data.called_function_declaration, &(fcall_data.extensible_param_count)); + + return; +} + + + + +/* narrow implicit FB call in IL. + * e.g. CLK ton_var + * CU counter_var + * + * The algorithm will be to build a fake il_fb_call_c equivalent to the implicit IL FB call, and let + * the visit(il_fb_call_c *) method handle it! + */ +void *narrow_candidate_datatypes_c::narrow_implicit_il_fb_call(symbol_c *il_instruction, const char *param_name, symbol_c *&called_fb_declaration) { + + /* set the datatype of the il_operand, this is, the FB being called! */ + if (NULL != il_operand) { + /* only set it if it is in the candidate datatypes list! */ + set_datatype(called_fb_declaration, il_operand); + il_operand->accept(*this); + } + symbol_c *fb_decl = il_operand->datatype; + + if (0 == fake_prev_il_instruction->prev_il_instruction.size()) { + /* This IL implicit FB call (e.g. CLK ton_var) is not preceded by another IL instruction + * (or list of instructions) that will set the IL current/default value. + * We cannot proceed verifying type compatibility of something that does not exist. + */ + return NULL; + } + + if (NULL == fb_decl) { + /* the il_operand is a not FB instance */ + /* so we simply pass on the required datatype to the prev_il_instructions */ + /* The invalid FB invocation will be caught in the print_datatypes_error_c by analysing NULL value in il_operand->datatype! */ + set_datatype_in_prev_il_instructions(il_instruction->datatype, fake_prev_il_instruction); + return NULL; + } + + + /* The value being passed to the 'param_name' parameter is actually the prev_il_instruction. + * However, we do not place that object directly in the fake il_param_list_c that we will be + * creating, since the visit(il_fb_call_c *) method will recursively call every object in that list. + * The il_prev_intruction object will be visited once we have handled this implici IL FB call + * (called from the instruction_list_c for() loop that works backwards). We DO NOT want to visit it twice. + * (Anyway, if we let the visit(il_fb_call_c *) recursively visit the current prev_il_instruction, this pointer + * would be changed to the IL instruction coming before the current prev_il_instruction! => things would get all messed up!) + * The easiest way to work around this is to simply use a new object, and copy the relevant details to that object! + */ + symbol_c param_value = *fake_prev_il_instruction; /* copy the candidate_datatypes list ! */ + + identifier_c variable_name(param_name); + // SYM_REF1(il_assign_operator_c, variable_name) + il_assign_operator_c il_assign_operator(&variable_name); + // SYM_REF3(il_param_assignment_c, il_assign_operator, il_operand, simple_instr_list) + il_param_assignment_c il_param_assignment(&il_assign_operator, ¶m_value/*il_operand*/, NULL); + il_param_list_c il_param_list; + il_param_list.add_element(&il_param_assignment); + // SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list, symbol_c *called_fb_declaration) + CAL_operator_c CAL_operator; + il_fb_call_c il_fb_call(&CAL_operator, il_operand, NULL, &il_param_list); + + /* A FB call does not return any datatype, but the IL instructions that come after this + * FB call may require a specific datatype in the il current/default variable, + * so we must pass this information up to the IL instruction before the FB call, since it will + * be that IL instruction that will be required to produce the desired dtataype. + * + * The above will be done by the visit(il_fb_call_c *) method, so we must make sure to + * correctly set up the il_fb_call.datatype variable! + */ + il_fb_call.called_fb_declaration = called_fb_declaration; + il_fb_call.accept(*this); + + /* set the required datatype of the previous IL instruction! */ + /* NOTE: + * When handling these implicit IL calls, the parameter_value being passed to the FB parameter + * is actually the prev_il_instruction. + * + * However, since the FB call does not change the value in the current/default IL variable, this value + * must also be used ny the IL instruction coming after this FB call. + * + * This means that we have two consumers/users for the same value. + * We must therefore check whether the datatype required by the IL instructions following this FB call + * is the same as that required for the first parameter. If not, then we have a semantic error, + * and we set it to invalid_type_name. + * + * However, we only do that if: + * - The IL instruction that comes after this IL FB call actually asked this FB call for a specific + * datatype in the current/default vairable, once this IL FB call returns. + * However, sometimes, (for e.g., this FB call is the last in the IL list) the subsequent FB to not aks this + * FB call for any datatype. In that case, then the datatype required to pass to the first parameter of the + * FB call must be left unchanged! + */ + if ((NULL == il_instruction->datatype) || (is_type_equal(param_value.datatype, il_instruction->datatype))) { + set_datatype_in_prev_il_instructions(param_value.datatype, fake_prev_il_instruction); + } else { + set_datatype_in_prev_il_instructions(&search_constant_type_c::invalid_type_name, fake_prev_il_instruction); + } + return NULL; +} + + +/* a helper function... */ +symbol_c *narrow_candidate_datatypes_c::base_type(symbol_c *symbol) { + /* NOTE: symbol == NULL is valid. It will occur when, for e.g., an undefined/undeclared symbolic_variable is used + * in the code. + */ + if (symbol == NULL) return NULL; + return (symbol_c *)symbol->accept(search_base_type); +} + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ + +/**********************/ +/* B 1.3 - Data types */ +/**********************/ +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ +/* simple_specification ASSIGN constant */ +// SYM_REF2(simple_spec_init_c, simple_specification, constant) +void *narrow_candidate_datatypes_c::visit(simple_spec_init_c *symbol) { + if (symbol->candidate_datatypes.size() == 1) + symbol->datatype = symbol->candidate_datatypes[0]; + + if (symbol->simple_specification->candidate_datatypes.size() == 1) + symbol->simple_specification->datatype = symbol->simple_specification->candidate_datatypes[0]; + + if (NULL != symbol->constant) { + set_datatype(symbol->datatype, symbol->constant); + symbol->constant->accept(*this); + } + return NULL; +} + + + +/* signed_integer DOTDOT signed_integer */ +// SYM_REF2(subrange_c, lower_limit, upper_limit) +void *narrow_candidate_datatypes_c::visit(subrange_c *symbol) { + symbol->lower_limit->datatype = symbol->datatype; + symbol->lower_limit->accept(*this); + symbol->upper_limit->datatype = symbol->datatype; + symbol->upper_limit->accept(*this); + return NULL; +} + + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ + +/********************************************/ +/* B 1.4.1 - Directly Represented Variables */ +/********************************************/ + +/*************************************/ +/* B 1.4.2 - Multi-element variables */ +/*************************************/ +/* subscripted_variable '[' subscript_list ']' */ +// SYM_REF2(array_variable_c, subscripted_variable, subscript_list) +void *narrow_candidate_datatypes_c::visit(array_variable_c *symbol) { + /* we need to check the data types of the expressions used for the subscripts... */ + symbol->subscript_list->accept(*this); + return NULL; +} + + +/* subscript_list ',' subscript */ +// SYM_LIST(subscript_list_c) +void *narrow_candidate_datatypes_c::visit(subscript_list_c *symbol) { + for (int i = 0; i < symbol->n; i++) { + for (unsigned int k = 0; k < symbol->elements[i]->candidate_datatypes.size(); k++) { + if (is_ANY_INT_type(symbol->elements[i]->candidate_datatypes[k])) + symbol->elements[i]->datatype = symbol->elements[i]->candidate_datatypes[k]; + } + symbol->elements[i]->accept(*this); + } + return NULL; +} + + + + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ + +void *narrow_candidate_datatypes_c::visit(var1_list_c *symbol) { +#if 0 /* We don't really need to set the datatype of each variable. We just check the declaration itself! */ + for(int i = 0; i < symbol->n; i++) { + if (symbol->elements[i]->candidate_datatypes.size() == 1) + symbol->elements[i]->datatype = symbol->elements[i]->candidate_datatypes[0]; + } +#endif + return NULL; +} + + +/* AT direct_variable */ +// SYM_REF1(location_c, direct_variable) +void *narrow_candidate_datatypes_c::visit(location_c *symbol) { + set_datatype(symbol->datatype, symbol->direct_variable); + symbol->direct_variable->accept(*this); /* currently does nothing! */ + return NULL; +} + + +/* [variable_name] location ':' located_var_spec_init */ +/* variable_name -> may be NULL ! */ +// SYM_REF3(located_var_decl_c, variable_name, location, located_var_spec_init) +void *narrow_candidate_datatypes_c::visit(located_var_decl_c *symbol) { + /* let the var_spec_init set its own symbol->datatype value */ + symbol->located_var_spec_init->accept(*this); + + if (NULL != symbol->variable_name) + set_datatype(symbol->located_var_spec_init->datatype, symbol->variable_name); + + set_datatype(symbol->located_var_spec_init->datatype, symbol->location); + symbol->location->accept(*this); + return NULL; +} + + +/************************************/ +/* B 1.5 Program organization units */ +/************************************/ +/*********************/ +/* B 1.5.1 Functions */ +/*********************/ +void *narrow_candidate_datatypes_c::visit(function_declaration_c *symbol) { + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + symbol->var_declarations_list->accept(*this); + if (debug) printf("Narrowing candidate data types list in body of function %s\n", ((token_c *)(symbol->derived_function_name))->value); + symbol->function_body->accept(*this); + delete search_varfb_instance_type; + search_varfb_instance_type = NULL; + return NULL; +} + +/***************************/ +/* B 1.5.2 Function blocks */ +/***************************/ +void *narrow_candidate_datatypes_c::visit(function_block_declaration_c *symbol) { + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + symbol->var_declarations->accept(*this); + if (debug) printf("Narrowing candidate data types list in body of FB %s\n", ((token_c *)(symbol->fblock_name))->value); + symbol->fblock_body->accept(*this); + delete search_varfb_instance_type; + search_varfb_instance_type = NULL; + return NULL; +} + +/********************/ +/* B 1.5.3 Programs */ +/********************/ +void *narrow_candidate_datatypes_c::visit(program_declaration_c *symbol) { + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + symbol->var_declarations->accept(*this); + if (debug) printf("Narrowing candidate data types list in body of program %s\n", ((token_c *)(symbol->program_type_name))->value); + symbol->function_block_body->accept(*this); + delete search_varfb_instance_type; + search_varfb_instance_type = NULL; + return NULL; +} + + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ +void *narrow_candidate_datatypes_c::visit(configuration_declaration_c *symbol) { + // TODO !!! + /* for the moment we must return NULL so semantic analysis of remaining code is not interrupted! */ + return NULL; +} + + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ + +/*| instruction_list il_instruction */ +// SYM_LIST(instruction_list_c) +void *narrow_candidate_datatypes_c::visit(instruction_list_c *symbol) { + /* In order to execute the narrow algoritm correctly, we need to go through the instructions backwards, + * so we can not use the base class' visitor + */ + /* In order to execute the narrow algoritm correctly + * in IL instruction lists containing JMPs to labels that come before the JMP instruction + * itself, we need to run the narrow algorithm twice on the Instruction List. + * e.g.: ... + * ld 23 + * label1:st byte_var + * ld 34 + * JMP label1 + * + * Note that the second time we run the narrow, most of the datatypes are already filled + * in, so it will be able to produce tha correct datatypes for the IL instruction referenced + * by the label, as in the 2nd pass we already know the datatypes of the JMP instruction! + */ + for(int j = 0; j < 2; j++) { + for(int i = symbol->n-1; i >= 0; i--) { + symbol->elements[i]->accept(*this); + } + } + return NULL; +} + +/* | label ':' [il_incomplete_instruction] eol_list */ +// SYM_REF2(il_instruction_c, label, il_instruction) +// void *visit(instruction_list_c *symbol); +void *narrow_candidate_datatypes_c::visit(il_instruction_c *symbol) { + if (NULL == symbol->il_instruction) { + /* this empty/null il_instruction cannot generate the desired datatype. We pass on the request to the previous il instruction. */ + set_datatype_in_prev_il_instructions(symbol->datatype, symbol); + } else { + il_instruction_c tmp_prev_il_instruction(NULL, NULL); + /* the narrow algorithm will need access to the intersected candidate_datatype lists of all prev_il_instructions, as well as the + * list of the prev_il_instructions. + * Instead of creating two 'global' (within the class) variables, we create a single il_instruction_c variable (fake_prev_il_instruction), + * and shove that data into this single variable. + */ + tmp_prev_il_instruction.prev_il_instruction = symbol->prev_il_instruction; + intersect_prev_candidate_datatype_lists(&tmp_prev_il_instruction); + /* Tell the il_instruction the datatype that it must generate - this was chosen by the next il_instruction (remember: we are iterating backwards!) */ + fake_prev_il_instruction = &tmp_prev_il_instruction; + symbol->il_instruction->datatype = symbol->datatype; + symbol->il_instruction->accept(*this); + fake_prev_il_instruction = NULL; + } + return NULL; +} + + + + +// void *visit(instruction_list_c *symbol); +void *narrow_candidate_datatypes_c::visit(il_simple_operation_c *symbol) { + /* Tell the il_simple_operator the datatype that it must generate - this was chosen by the next il_instruction (we iterate backwards!) */ + symbol->il_simple_operator->datatype = symbol->datatype; + /* recursive call to see whether data types are compatible */ + il_operand = symbol->il_operand; + symbol->il_simple_operator->accept(*this); + il_operand = NULL; + return NULL; +} + +/* | function_name [il_operand_list] */ +/* NOTE: The parameters 'called_function_declaration' and 'extensible_param_count' are used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;) +void *narrow_candidate_datatypes_c::visit(il_function_call_c *symbol) { + /* The first parameter of a non formal function call in IL will be the 'current value' (i.e. the prev_il_instruction) + * In order to be able to handle this without coding special cases, we will simply prepend that symbol + * to the il_operand_list, and remove it after calling handle_function_call(). + * However, since handle_function_call() will be recursively calling all parameter, and we don't want + * to do that for the prev_il_instruction (since it has already been visited by the fill_candidate_datatypes_c) + * we create a new ____ symbol_c ____ object, and copy the relevant info to/from that object before/after + * the call to handle_function_call(). + * + * However, if no further paramters are given, then il_operand_list will be NULL, and we will + * need to create a new object to hold the pointer to prev_il_instruction. + * This change will also be undone at the end of this method. + */ + symbol_c param_value = *fake_prev_il_instruction; /* copy the candidate_datatypes list */ + if (NULL == symbol->il_operand_list) symbol->il_operand_list = new il_operand_list_c; + if (NULL == symbol->il_operand_list) ERROR; + + ((list_c *)symbol->il_operand_list)->insert_element(¶m_value, 0); + + generic_function_call_t fcall_param = { + /* fcall_param.function_name = */ symbol->function_name, + /* fcall_param.nonformal_operand_list = */ symbol->il_operand_list, + /* fcall_param.formal_operand_list = */ NULL, + /* enum {POU_FB, POU_function} POU_type = */ generic_function_call_t::POU_function, + /* fcall_param.candidate_functions = */ symbol->candidate_functions, + /* fcall_param.called_function_declaration = */ symbol->called_function_declaration, + /* fcall_param.extensible_param_count = */ symbol->extensible_param_count + }; + + narrow_function_invocation(symbol, fcall_param); + set_datatype_in_prev_il_instructions(param_value.datatype, fake_prev_il_instruction); + + /* Undo the changes to the abstract syntax tree we made above... */ + ((list_c *)symbol->il_operand_list)->remove_element(0); + if (((list_c *)symbol->il_operand_list)->n == 0) { + /* if the list becomes empty, then that means that it did not exist before we made these changes, so we delete it! */ + delete symbol->il_operand_list; + symbol->il_operand_list = NULL; + } + + return NULL; +} + + +/* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ +// SYM_REF3(il_expression_c, il_expr_operator, il_operand, simple_instr_list); +void *narrow_candidate_datatypes_c::visit(il_expression_c *symbol) { + /* first handle the operation (il_expr_operator) that will use the result coming from the parenthesised IL list (i.e. simple_instr_list) */ + symbol->il_expr_operator->datatype = symbol->datatype; + il_operand = symbol->simple_instr_list; /* This is not a bug! The parenthesised expression will be used as the operator! */ + symbol->il_expr_operator->accept(*this); + + /* now give the parenthesised IL list a chance to narrow the datatypes */ + /* The datatype that is must return was set by the call symbol->il_expr_operator->accept(*this) */ + il_instruction_c *save_fake_prev_il_instruction = fake_prev_il_instruction; /*this is not really necessary, but lets play it safe */ + symbol->simple_instr_list->accept(*this); + fake_prev_il_instruction = save_fake_prev_il_instruction; + return NULL; +} + + + + +/* il_jump_operator label */ +void *narrow_candidate_datatypes_c::visit(il_jump_operation_c *symbol) { + /* recursive call to fill the datatype */ + symbol->il_jump_operator->datatype = symbol->datatype; + symbol->il_jump_operator->accept(*this); + return NULL; +} + + + + + + + +/* il_call_operator prev_declared_fb_name + * | il_call_operator prev_declared_fb_name '(' ')' + * | il_call_operator prev_declared_fb_name '(' eol_list ')' + * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' + * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' + */ +/* NOTE: The parameter 'called_fb_declaration'is used to pass data between stage 3 and stage4 (although currently it is not used in stage 4 */ +// SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list, symbol_c *called_fb_declaration) +void *narrow_candidate_datatypes_c::visit(il_fb_call_c *symbol) { + symbol_c *fb_decl = symbol->called_fb_declaration; + + /* Although a call to a non-declared FB is a semantic error, this is currently caught by stage 2! */ + if (NULL == fb_decl) ERROR; + if (NULL != symbol->il_operand_list) narrow_nonformal_call(symbol, fb_decl); + if (NULL != symbol-> il_param_list) narrow_formal_call(symbol, fb_decl); + + /* Let the il_call_operator (CAL, CALC, or CALCN) set the datatype of prev_il_instruction... */ + symbol->il_call_operator->datatype = symbol->datatype; + symbol->il_call_operator->accept(*this); + return NULL; +} + + +/* | function_name '(' eol_list [il_param_list] ')' */ +/* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;) +void *narrow_candidate_datatypes_c::visit(il_formal_funct_call_c *symbol) { + generic_function_call_t fcall_param = { + /* fcall_param.function_name = */ symbol->function_name, + /* fcall_param.nonformal_operand_list = */ NULL, + /* fcall_param.formal_operand_list = */ symbol->il_param_list, + /* enum {POU_FB, POU_function} POU_type = */ generic_function_call_t::POU_function, + /* fcall_param.candidate_functions = */ symbol->candidate_functions, + /* fcall_param.called_function_declaration = */ symbol->called_function_declaration, + /* fcall_param.extensible_param_count = */ symbol->extensible_param_count + }; + + narrow_function_invocation(symbol, fcall_param); + /* The desired datatype of the previous il instruction was already set by narrow_function_invocation() */ + return NULL; +} + + +// void *visit(il_operand_list_c *symbol); + + +/* | simple_instr_list il_simple_instruction */ +/* This object is referenced by il_expression_c objects */ +void *narrow_candidate_datatypes_c::visit(simple_instr_list_c *symbol) { + if (symbol->n > 0) + symbol->elements[symbol->n - 1]->datatype = symbol->datatype; + + for(int i = symbol->n-1; i >= 0; i--) { + symbol->elements[i]->accept(*this); + } + return NULL; +} + + +// SYM_REF1(il_simple_instruction_c, il_simple_instruction, symbol_c *prev_il_instruction;) +void *narrow_candidate_datatypes_c::visit(il_simple_instruction_c *symbol) { + if (symbol->prev_il_instruction.size() > 1) ERROR; /* There should be no labeled insructions inside an IL expression! */ + + il_instruction_c tmp_prev_il_instruction(NULL, NULL); + /* the narrow algorithm will need access to the intersected candidate_datatype lists of all prev_il_instructions, as well as the + * list of the prev_il_instructions. + * Instead of creating two 'global' (within the class) variables, we create a single il_instruction_c variable (fake_prev_il_instruction), + * and shove that data into this single variable. + */ + if (symbol->prev_il_instruction.size() > 0) + tmp_prev_il_instruction.candidate_datatypes = symbol->prev_il_instruction[0]->candidate_datatypes; + tmp_prev_il_instruction.prev_il_instruction = symbol->prev_il_instruction; + + /* copy the candidate_datatypes list */ + fake_prev_il_instruction = &tmp_prev_il_instruction; + symbol->il_simple_instruction->datatype = symbol->datatype; + symbol->il_simple_instruction->accept(*this); + fake_prev_il_instruction = NULL; + return NULL; +} + +// void *visit(il_param_list_c *symbol); +// void *visit(il_param_assignment_c *symbol); +// void *visit(il_param_out_assignment_c *symbol); + + +/*******************/ +/* B 2.2 Operators */ +/*******************/ +void *narrow_candidate_datatypes_c::narrow_binary_operator(const struct widen_entry widen_table[], symbol_c *symbol, bool *deprecated_operation) { + symbol_c *prev_instruction_type, *operand_type; + int count = 0; + + if (NULL == symbol->datatype) + /* next IL instructions were unable to determine the datatype this instruction should produce */ + return NULL; + + if (NULL != deprecated_operation) + *deprecated_operation = false; + + /* NOTE 1: the il_operand __may__ be pointing to a parenthesized list of IL instructions. + * e.g. LD 33 + * AND ( 45 + * OR 56 + * ) + * When we handle the first 'AND' IL_operator, the il_operand will point to an simple_instr_list_c. + * In this case, when we call il_operand->accept(*this);, the prev_il_instruction pointer will be overwritten! + * + * We must therefore set the prev_il_instruction->datatype = symbol->datatype; + * __before__ calling il_operand->accept(*this) !! + * + * NOTE 2: We do not need to call prev_il_instruction->accept(*this), as the object to which prev_il_instruction + * is pointing to will be later narrowed by the call from the for() loop of the instruction_list_c + * (or simple_instr_list_c), which iterates backwards. + */ + for(unsigned int i = 0; i < fake_prev_il_instruction->candidate_datatypes.size(); i++) { + for(unsigned int j = 0; j < il_operand->candidate_datatypes.size(); j++) { + prev_instruction_type = fake_prev_il_instruction->candidate_datatypes[i]; + operand_type = il_operand->candidate_datatypes[j]; + if (is_widening_compatible(widen_table, prev_instruction_type, operand_type, symbol->datatype, deprecated_operation)) { + /* set the desired datatype of the previous il instruction */ + set_datatype_in_prev_il_instructions(prev_instruction_type, fake_prev_il_instruction); + /* set the datatype for the operand */ + il_operand->datatype = operand_type; + + count ++; + } + } + } +// if (count > 1) ERROR; /* Since we also support SAFE data types, this assertion is not necessarily always tru! */ + if (is_type_valid(symbol->datatype) && (count <= 0)) ERROR; + + il_operand->accept(*this); + return NULL; +} + + + + + +void *narrow_candidate_datatypes_c::handle_il_instruction(symbol_c *symbol) { + if (NULL == symbol->datatype) + /* next IL instructions were unable to determine the datatype this instruction should produce */ + return NULL; + /* NOTE 1: the il_operand __may__ be pointing to a parenthesized list of IL instructions. + * e.g. LD 33 + * AND ( 45 + * OR 56 + * ) + * When we handle the first 'AND' IL_operator, the il_operand will point to an simple_instr_list_c. + * In this case, when we call il_operand->accept(*this);, the prev_il_instruction pointer will be overwritten! + * + * We must therefore set the prev_il_instruction->datatype = symbol->datatype; + * __before__ calling il_operand->accept(*this) !! + * + * NOTE 2: We do not need to call prev_il_instruction->accept(*this), as the object to which prev_il_instruction + * is pointing to will be later narrowed by the call from the for() loop of the instruction_list_c + * (or simple_instr_list_c), which iterates backwards. + */ + /* set the desired datatype of the previous il instruction */ + set_datatype_in_prev_il_instructions(symbol->datatype, fake_prev_il_instruction); + + /* set the datatype for the operand */ + il_operand->datatype = symbol->datatype; + il_operand->accept(*this); + return NULL; +} + + + + +void *narrow_candidate_datatypes_c::visit(LD_operator_c *symbol) { + if (NULL == symbol->datatype) + /* next IL instructions were unable to determine the datatype this instruction should produce */ + return NULL; + /* set the datatype for the operand */ + il_operand->datatype = symbol->datatype; + il_operand->accept(*this); + return NULL; +} + + +void *narrow_candidate_datatypes_c::visit(LDN_operator_c *symbol) { + if (NULL == symbol->datatype) + /* next IL instructions were unable to determine the datatype this instruction should produce */ + return NULL; + /* set the datatype for the operand */ + il_operand->datatype = symbol->datatype; + il_operand->accept(*this); + return NULL; +} + +void *narrow_candidate_datatypes_c::visit(ST_operator_c *symbol) { + if (symbol->candidate_datatypes.size() != 1) + return NULL; + symbol->datatype = symbol->candidate_datatypes[0]; + /* set the datatype for the operand */ + il_operand->datatype = symbol->datatype; + il_operand->accept(*this); + /* set the desired datatype of the previous il instruction */ + set_datatype_in_prev_il_instructions(symbol->datatype, fake_prev_il_instruction); + return NULL; +} + +void *narrow_candidate_datatypes_c::visit(STN_operator_c *symbol) { + if (symbol->candidate_datatypes.size() != 1) + return NULL; + symbol->datatype = symbol->candidate_datatypes[0]; + /* set the datatype for the operand */ + il_operand->datatype = symbol->datatype; + il_operand->accept(*this); + /* set the desired datatype of the previous il instruction */ + set_datatype_in_prev_il_instructions(symbol->datatype, fake_prev_il_instruction); + return NULL; +} + +void *narrow_candidate_datatypes_c::visit(NOT_operator_c *symbol) { + /* NOTE: the standard allows syntax in which the NOT operator is followed by an optional + * NOT [] + * However, it does not define the semantic of the NOT operation when the is specified. + * We therefore consider it an error if an il_operand is specified! + */ + /* We do not change the data type, we simply invert the bits in bit types! */ + /* So, we set the desired datatype of the previous il instruction */ + set_datatype_in_prev_il_instructions(symbol->datatype, fake_prev_il_instruction); + return NULL; +} + +void *narrow_candidate_datatypes_c::visit(S_operator_c *symbol) { + /* TODO: what if this is a FB call? */ + return handle_il_instruction(symbol); +} +void *narrow_candidate_datatypes_c::visit(R_operator_c *symbol) { + /* TODO: what if this is a FB call? */ + return handle_il_instruction(symbol); +} + + +void *narrow_candidate_datatypes_c::visit( S1_operator_c *symbol) {return narrow_implicit_il_fb_call(symbol, "S1", symbol->called_fb_declaration);} +void *narrow_candidate_datatypes_c::visit( R1_operator_c *symbol) {return narrow_implicit_il_fb_call(symbol, "R1", symbol->called_fb_declaration);} +void *narrow_candidate_datatypes_c::visit( CLK_operator_c *symbol) {return narrow_implicit_il_fb_call(symbol, "CLK", symbol->called_fb_declaration);} +void *narrow_candidate_datatypes_c::visit( CU_operator_c *symbol) {return narrow_implicit_il_fb_call(symbol, "CU", symbol->called_fb_declaration);} +void *narrow_candidate_datatypes_c::visit( CD_operator_c *symbol) {return narrow_implicit_il_fb_call(symbol, "CD", symbol->called_fb_declaration);} +void *narrow_candidate_datatypes_c::visit( PV_operator_c *symbol) {return narrow_implicit_il_fb_call(symbol, "PV", symbol->called_fb_declaration);} +void *narrow_candidate_datatypes_c::visit( IN_operator_c *symbol) {return narrow_implicit_il_fb_call(symbol, "IN", symbol->called_fb_declaration);} +void *narrow_candidate_datatypes_c::visit( PT_operator_c *symbol) {return narrow_implicit_il_fb_call(symbol, "PT", symbol->called_fb_declaration);} + +void *narrow_candidate_datatypes_c::visit( AND_operator_c *symbol) {return narrow_binary_operator(widen_AND_table, symbol);} +void *narrow_candidate_datatypes_c::visit( OR_operator_c *symbol) {return narrow_binary_operator( widen_OR_table, symbol);} +void *narrow_candidate_datatypes_c::visit( XOR_operator_c *symbol) {return narrow_binary_operator(widen_XOR_table, symbol);} +void *narrow_candidate_datatypes_c::visit(ANDN_operator_c *symbol) {return narrow_binary_operator(widen_AND_table, symbol);} +void *narrow_candidate_datatypes_c::visit( ORN_operator_c *symbol) {return narrow_binary_operator( widen_OR_table, symbol);} +void *narrow_candidate_datatypes_c::visit(XORN_operator_c *symbol) {return narrow_binary_operator(widen_XOR_table, symbol);} +void *narrow_candidate_datatypes_c::visit( ADD_operator_c *symbol) {return narrow_binary_operator(widen_ADD_table, symbol, &(symbol->deprecated_operation));} +void *narrow_candidate_datatypes_c::visit( SUB_operator_c *symbol) {return narrow_binary_operator(widen_SUB_table, symbol, &(symbol->deprecated_operation));} +void *narrow_candidate_datatypes_c::visit( MUL_operator_c *symbol) {return narrow_binary_operator(widen_MUL_table, symbol, &(symbol->deprecated_operation));} +void *narrow_candidate_datatypes_c::visit( DIV_operator_c *symbol) {return narrow_binary_operator(widen_DIV_table, symbol, &(symbol->deprecated_operation));} +void *narrow_candidate_datatypes_c::visit( MOD_operator_c *symbol) {return narrow_binary_operator(widen_MOD_table, symbol);} +void *narrow_candidate_datatypes_c::visit( GT_operator_c *symbol) {return narrow_binary_operator(widen_CMP_table, symbol);} +void *narrow_candidate_datatypes_c::visit( GE_operator_c *symbol) {return narrow_binary_operator(widen_CMP_table, symbol);} +void *narrow_candidate_datatypes_c::visit( EQ_operator_c *symbol) {return narrow_binary_operator(widen_CMP_table, symbol);} +void *narrow_candidate_datatypes_c::visit( LT_operator_c *symbol) {return narrow_binary_operator(widen_CMP_table, symbol);} +void *narrow_candidate_datatypes_c::visit( LE_operator_c *symbol) {return narrow_binary_operator(widen_CMP_table, symbol);} +void *narrow_candidate_datatypes_c::visit( NE_operator_c *symbol) {return narrow_binary_operator(widen_CMP_table, symbol);} + + + + +void *narrow_candidate_datatypes_c::narrow_conditional_flow_control_IL_instruction(symbol_c *symbol) { + /* if the next IL instructions needs us to provide a datatype other than a bool, + * then we have an internal compiler error - most likely in fill_candidate_datatypes_c + */ + if ((NULL != symbol->datatype) && (!is_ANY_BOOL_compatible(symbol->datatype))) ERROR; + if (symbol->candidate_datatypes.size() > 1) ERROR; + + /* NOTE: If there is no IL instruction following this CALC, CALCN, JMPC, JMPC, ..., instruction, + * we must still provide a bool_type_name_c datatype (if possible, i.e. if it exists in the candidate datatype list). + * If it is not possible, we set it to NULL + */ + if (symbol->candidate_datatypes.size() == 0) symbol->datatype = NULL; + else symbol->datatype = symbol->candidate_datatypes[0]; /* i.e. a bool_type_name_c! */ + if ((NULL != symbol->datatype) && (!is_ANY_BOOL_compatible(symbol->datatype))) ERROR; + + /* set the required datatype of the previous IL instruction, i.e. a bool_type_name_c! */ + set_datatype_in_prev_il_instructions(symbol->datatype, fake_prev_il_instruction); + return NULL; +} + + +// SYM_REF0(CAL_operator_c) +// SYM_REF0(CALC_operator_c) +// SYM_REF0(CALCN_operator_c) +/* called from visit(il_fb_call_c *) {symbol->il_call_operator->accpet(*this)} */ +/* NOTE: The CAL, JMP and RET instructions simply set the desired datatype of the previous il instruction since they do not change the value in the current/default IL variable */ +/* called from il_fb_call_c (symbol->il_call_operator->accpet(*this) ) */ +void *narrow_candidate_datatypes_c::visit( CAL_operator_c *symbol) {set_datatype_in_prev_il_instructions(symbol->datatype, fake_prev_il_instruction); return NULL;} +void *narrow_candidate_datatypes_c::visit( RET_operator_c *symbol) {set_datatype_in_prev_il_instructions(symbol->datatype, fake_prev_il_instruction); return NULL;} +void *narrow_candidate_datatypes_c::visit( JMP_operator_c *symbol) {set_datatype_in_prev_il_instructions(symbol->datatype, fake_prev_il_instruction); return NULL;} +void *narrow_candidate_datatypes_c::visit( CALC_operator_c *symbol) {return narrow_conditional_flow_control_IL_instruction(symbol);} +void *narrow_candidate_datatypes_c::visit(CALCN_operator_c *symbol) {return narrow_conditional_flow_control_IL_instruction(symbol);} +void *narrow_candidate_datatypes_c::visit( RETC_operator_c *symbol) {return narrow_conditional_flow_control_IL_instruction(symbol);} +void *narrow_candidate_datatypes_c::visit(RETCN_operator_c *symbol) {return narrow_conditional_flow_control_IL_instruction(symbol);} +void *narrow_candidate_datatypes_c::visit( JMPC_operator_c *symbol) {return narrow_conditional_flow_control_IL_instruction(symbol);} +void *narrow_candidate_datatypes_c::visit(JMPCN_operator_c *symbol) {return narrow_conditional_flow_control_IL_instruction(symbol);} + +/* Symbol class handled together with function call checks */ +// void *visit(il_assign_operator_c *symbol, variable_name); +/* Symbol class handled together with function call checks */ +// void *visit(il_assign_operator_c *symbol, option, variable_name); + + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ +void *narrow_candidate_datatypes_c::narrow_binary_expression(const struct widen_entry widen_table[], symbol_c *symbol, symbol_c *l_expr, symbol_c *r_expr, bool *deprecated_operation) { + symbol_c *l_type, *r_type; + int count = 0; + + if (NULL != deprecated_operation) + *deprecated_operation = false; + + for(unsigned int i = 0; i < l_expr->candidate_datatypes.size(); i++) { + for(unsigned int j = 0; j < r_expr->candidate_datatypes.size(); j++) { + /* test widening compatibility */ + l_type = l_expr->candidate_datatypes[i]; + r_type = r_expr->candidate_datatypes[j]; + if (is_widening_compatible(widen_table, l_type, r_type, symbol->datatype, deprecated_operation)) { + l_expr->datatype = l_type; + r_expr->datatype = r_type; + count ++; + } + } + } +// if (count > 1) ERROR; /* Since we also support SAFE data types, this assertion is not necessarily always tru! */ + if (is_type_valid(symbol->datatype) && (count <= 0)) ERROR; + + l_expr->accept(*this); + r_expr->accept(*this); + return NULL; +} + + + +void *narrow_candidate_datatypes_c::visit( or_expression_c *symbol) {return narrow_binary_expression( widen_OR_table, symbol, symbol->l_exp, symbol->r_exp);} +void *narrow_candidate_datatypes_c::visit( xor_expression_c *symbol) {return narrow_binary_expression(widen_XOR_table, symbol, symbol->l_exp, symbol->r_exp);} +void *narrow_candidate_datatypes_c::visit( and_expression_c *symbol) {return narrow_binary_expression(widen_AND_table, symbol, symbol->l_exp, symbol->r_exp);} + +void *narrow_candidate_datatypes_c::visit( equ_expression_c *symbol) {return narrow_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} +void *narrow_candidate_datatypes_c::visit(notequ_expression_c *symbol) {return narrow_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} +void *narrow_candidate_datatypes_c::visit( lt_expression_c *symbol) {return narrow_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} +void *narrow_candidate_datatypes_c::visit( gt_expression_c *symbol) {return narrow_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} +void *narrow_candidate_datatypes_c::visit( le_expression_c *symbol) {return narrow_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} +void *narrow_candidate_datatypes_c::visit( ge_expression_c *symbol) {return narrow_binary_expression(widen_CMP_table, symbol, symbol->l_exp, symbol->r_exp);} + +void *narrow_candidate_datatypes_c::visit( add_expression_c *symbol) {return narrow_binary_expression(widen_ADD_table, symbol, symbol->l_exp, symbol->r_exp, &symbol->deprecated_operation);} +void *narrow_candidate_datatypes_c::visit( sub_expression_c *symbol) {return narrow_binary_expression(widen_SUB_table, symbol, symbol->l_exp, symbol->r_exp, &symbol->deprecated_operation);} +void *narrow_candidate_datatypes_c::visit( mul_expression_c *symbol) {return narrow_binary_expression(widen_MUL_table, symbol, symbol->l_exp, symbol->r_exp, &symbol->deprecated_operation);} +void *narrow_candidate_datatypes_c::visit( div_expression_c *symbol) {return narrow_binary_expression(widen_DIV_table, symbol, symbol->l_exp, symbol->r_exp, &symbol->deprecated_operation);} +void *narrow_candidate_datatypes_c::visit( mod_expression_c *symbol) {return narrow_binary_expression(widen_MOD_table, symbol, symbol->l_exp, symbol->r_exp);} +void *narrow_candidate_datatypes_c::visit( power_expression_c *symbol) {return narrow_binary_expression(widen_EXPT_table,symbol, symbol->l_exp, symbol->r_exp);} + + +void *narrow_candidate_datatypes_c::visit(neg_expression_c *symbol) { + symbol->exp->datatype = symbol->datatype; + symbol->exp->accept(*this); + return NULL; +} + + +void *narrow_candidate_datatypes_c::visit(not_expression_c *symbol) { + symbol->exp->datatype = symbol->datatype; + symbol->exp->accept(*this); + return NULL; +} + + + +/* NOTE: The parameter 'called_function_declaration', 'extensible_param_count' and 'candidate_functions' are used to pass data between the stage 3 and stage 4. */ +/* formal_param_list -> may be NULL ! */ +/* nonformal_param_list -> may be NULL ! */ +// SYM_REF3(function_invocation_c, function_name, formal_param_list, nonformal_param_list, symbol_c *called_function_declaration; int extensible_param_count; std::vector candidate_functions;) +void *narrow_candidate_datatypes_c::visit(function_invocation_c *symbol) { + generic_function_call_t fcall_param = { + /* fcall_param.function_name = */ symbol->function_name, + /* fcall_param.nonformal_operand_list = */ symbol->nonformal_param_list, + /* fcall_param.formal_operand_list = */ symbol->formal_param_list, + /* enum {POU_FB, POU_function} POU_type = */ generic_function_call_t::POU_function, + /* fcall_param.candidate_functions = */ symbol->candidate_functions, + /* fcall_param.called_function_declaration = */ symbol->called_function_declaration, + /* fcall_param.extensible_param_count = */ symbol->extensible_param_count + }; + + narrow_function_invocation(symbol, fcall_param); + return NULL; +} + +/********************/ +/* B 3.2 Statements */ +/********************/ + + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ + +void *narrow_candidate_datatypes_c::visit(assignment_statement_c *symbol) { + if (symbol->candidate_datatypes.size() != 1) + return NULL; + symbol->datatype = symbol->candidate_datatypes[0]; + symbol->l_exp->datatype = symbol->datatype; + symbol->l_exp->accept(*this); + symbol->r_exp->datatype = symbol->datatype; + symbol->r_exp->accept(*this); + return NULL; +} + + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ + +void *narrow_candidate_datatypes_c::visit(fb_invocation_c *symbol) { + /* Note: We do not use the symbol->called_fb_declaration value (set in fill_candidate_datatypes_c) + * because we try to identify any other datatype errors in the expressions used in the + * parameters to the FB call (e.g. fb_var(var1 * 56 + func(var * 43)) ) + * even it the call to the FB is invalid. + * This makes sense because it may be errors in those expressions which are + * making this an invalid call, so it makes sense to point them out to the user! + */ + symbol_c *fb_decl = search_varfb_instance_type->get_basetype_decl(symbol->fb_name); + + /* Although a call to a non-declared FB is a semantic error, this is currently caught by stage 2! */ + if (NULL == fb_decl) ERROR; + if (NULL != symbol->nonformal_param_list) narrow_nonformal_call(symbol, fb_decl); + if (NULL != symbol-> formal_param_list) narrow_formal_call(symbol, fb_decl); + + return NULL; +} + + +/********************************/ +/* B 3.2.3 Selection Statements */ +/********************************/ + +void *narrow_candidate_datatypes_c::visit(if_statement_c *symbol) { + for(unsigned int i = 0; i < symbol->expression->candidate_datatypes.size(); i++) { + if (is_ANY_BOOL_compatible(symbol->expression->candidate_datatypes[i])) + symbol->expression->datatype = symbol->expression->candidate_datatypes[i]; + } + symbol->expression->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + if (NULL != symbol->elseif_statement_list) + symbol->elseif_statement_list->accept(*this); + if (NULL != symbol->else_statement_list) + symbol->else_statement_list->accept(*this); + return NULL; +} + + +void *narrow_candidate_datatypes_c::visit(elseif_statement_c *symbol) { + for (unsigned int i = 0; i < symbol->expression->candidate_datatypes.size(); i++) { + if (is_ANY_BOOL_compatible(symbol->expression->candidate_datatypes[i])) + symbol->expression->datatype = symbol->expression->candidate_datatypes[i]; + } + symbol->expression->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + +/* CASE expression OF case_element_list ELSE statement_list END_CASE */ +// SYM_REF3(case_statement_c, expression, case_element_list, statement_list) +void *narrow_candidate_datatypes_c::visit(case_statement_c *symbol) { + for (unsigned int i = 0; i < symbol->expression->candidate_datatypes.size(); i++) { + if ((is_ANY_INT_type(symbol->expression->candidate_datatypes[i])) + || (search_base_type.type_is_enumerated(symbol->expression->candidate_datatypes[i]))) + symbol->expression->datatype = symbol->expression->candidate_datatypes[i]; + } + symbol->expression->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + if (NULL != symbol->case_element_list) { + symbol->case_element_list->datatype = symbol->expression->datatype; + symbol->case_element_list->accept(*this); + } + return NULL; +} + +/* helper symbol for case_statement */ +// SYM_LIST(case_element_list_c) +void *narrow_candidate_datatypes_c::visit(case_element_list_c *symbol) { + for (int i = 0; i < symbol->n; i++) { + symbol->elements[i]->datatype = symbol->datatype; + symbol->elements[i]->accept(*this); + } + return NULL; +} + +/* case_list ':' statement_list */ +// SYM_REF2(case_element_c, case_list, statement_list) +void *narrow_candidate_datatypes_c::visit(case_element_c *symbol) { + symbol->case_list->datatype = symbol->datatype; + symbol->case_list->accept(*this); + symbol->statement_list->accept(*this); + return NULL; +} + +// SYM_LIST(case_list_c) +void *narrow_candidate_datatypes_c::visit(case_list_c *symbol) { + for (int i = 0; i < symbol->n; i++) { + for (unsigned int k = 0; k < symbol->elements[i]->candidate_datatypes.size(); k++) { + if (is_type_equal(symbol->datatype, symbol->elements[i]->candidate_datatypes[k])) + symbol->elements[i]->datatype = symbol->elements[i]->candidate_datatypes[k]; + } + /* NOTE: this may be an integer, a subrange_c, or a enumerated value! */ + symbol->elements[i]->accept(*this); + } + return NULL; +} + + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ +void *narrow_candidate_datatypes_c::visit(for_statement_c *symbol) { + /* Control variable */ + for(unsigned int i = 0; i < symbol->control_variable->candidate_datatypes.size(); i++) { + if (is_ANY_INT_type(symbol->control_variable->candidate_datatypes[i])) { + symbol->control_variable->datatype = symbol->control_variable->candidate_datatypes[i]; + } + } + symbol->control_variable->accept(*this); + /* BEG expression */ + for(unsigned int i = 0; i < symbol->beg_expression->candidate_datatypes.size(); i++) { + if (is_type_equal(symbol->control_variable->datatype,symbol->beg_expression->candidate_datatypes[i]) && + is_ANY_INT_type(symbol->beg_expression->candidate_datatypes[i])) { + symbol->beg_expression->datatype = symbol->beg_expression->candidate_datatypes[i]; + } + } + symbol->beg_expression->accept(*this); + /* END expression */ + for(unsigned int i = 0; i < symbol->end_expression->candidate_datatypes.size(); i++) { + if (is_type_equal(symbol->control_variable->datatype,symbol->end_expression->candidate_datatypes[i]) && + is_ANY_INT_type(symbol->end_expression->candidate_datatypes[i])) { + symbol->end_expression->datatype = symbol->end_expression->candidate_datatypes[i]; + } + } + symbol->end_expression->accept(*this); + /* BY expression */ + if (NULL != symbol->by_expression) { + for(unsigned int i = 0; i < symbol->by_expression->candidate_datatypes.size(); i++) { + if (is_type_equal(symbol->control_variable->datatype,symbol->by_expression->candidate_datatypes[i]) && + is_ANY_INT_type(symbol->by_expression->candidate_datatypes[i])) { + symbol->by_expression->datatype = symbol->by_expression->candidate_datatypes[i]; + } + } + symbol->by_expression->accept(*this); + } + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + +void *narrow_candidate_datatypes_c::visit(while_statement_c *symbol) { + for (unsigned int i = 0; i < symbol->expression->candidate_datatypes.size(); i++) { + if(is_BOOL_type(symbol->expression->candidate_datatypes[i])) + symbol->expression->datatype = symbol->expression->candidate_datatypes[i]; + } + symbol->expression->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + +void *narrow_candidate_datatypes_c::visit(repeat_statement_c *symbol) { + for (unsigned int i = 0; i < symbol->expression->candidate_datatypes.size(); i++) { + if(is_BOOL_type(symbol->expression->candidate_datatypes[i])) + symbol->expression->datatype = symbol->expression->candidate_datatypes[i]; + } + symbol->expression->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + + + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/narrow_candidate_datatypes.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/narrow_candidate_datatypes.hh Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,270 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2012 Manuele Conti (manuele.conti@sirius-es.it) + * Copyright (C) 2012 Matteo Facchinetti (matteo.facchinetti@sirius-es.it) + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* NOTE: The algorithm implemented here assumes that candidate datatype lists have already been filled! + * BEFORE running this visitor, be sure to CALL the fill_candidate_datatype_c visitor! + */ + + +/* + * Choose, from the list of all the possible datatypes each expression may take, the single datatype that it will in fact take. + * The resulting (chosen) datatype, will be stored in the symbol_c.datatype variable, leaving the candidate datatype list untouched! + * + * For rvalue expressions, this decision will be based on the datatype of the lvalue expression. + * For lvalue expressions, the candidate datatype list should have a single entry. + * + * For example, the very simple literal '0' in 'foo := 0', may represent a: + * BOOL, BYTE, WORD, DWORD, LWORD, USINT, SINT, UINT, INT, UDINT, DINT, ULINT, LINT (as well as the SAFE versions of these data tyes too!) + * + * In this class, the datatype of '0' will be set to the same datatype as the 'foo' variable. + * If the intersection of the candidate datatype lists of the left and right side expressions is empty, + * then a datatype error has been found, and the datatype is either left at NULL, or set to a pointer of an invalid_type_name_c object! + */ + + +#include "../absyntax_utils/absyntax_utils.hh" +#include "datatype_functions.hh" + +class narrow_candidate_datatypes_c: public iterator_visitor_c { + + private: + search_varfb_instance_type_c *search_varfb_instance_type; + search_base_type_c search_base_type; + symbol_c *il_operand; + il_instruction_c *fake_prev_il_instruction; + std::vector *prev_il_instructions; + std::vector *prev_il_instructions_intersected_datatypes; + + bool is_widening_compatible(const struct widen_entry widen_table[], symbol_c *left_type, symbol_c *right_type, symbol_c *result_type, bool *deprecated_status = NULL); + + void narrow_function_invocation(symbol_c *f_call, generic_function_call_t fcall_data); + void narrow_nonformal_call(symbol_c *f_call, symbol_c *f_decl, int *ext_parm_count = NULL); + void narrow_formal_call(symbol_c *f_call, symbol_c *f_decl, int *ext_parm_count = NULL); + void *narrow_implicit_il_fb_call(symbol_c *il_instruction, const char *param_name, symbol_c *&called_fb_declaration); + + void *handle_il_instruction(symbol_c *symbol); + void *narrow_binary_operator (const struct widen_entry widen_table[], symbol_c *symbol, bool *deprecated_operation = NULL); + void *narrow_binary_expression(const struct widen_entry widen_table[], symbol_c *symbol, symbol_c *l_expr, symbol_c *r_expr, bool *deprecated_operation = NULL); + + void *narrow_conditional_flow_control_IL_instruction(symbol_c *symbol); + + + public: + narrow_candidate_datatypes_c(symbol_c *ignore); + virtual ~narrow_candidate_datatypes_c(void); + + symbol_c *base_type(symbol_c *symbol); + + /**********************/ + /* B 1.3 - Data types */ + /**********************/ + /********************************/ + /* B 1.3.3 - Derived data types */ + /********************************/ + void *visit(subrange_c *symbol); + void *visit(simple_spec_init_c *symbol); + + /*********************/ + /* B 1.4 - Variables */ + /*********************/ + /********************************************/ + /* B 1.4.1 - Directly Represented Variables */ + /********************************************/ + /*************************************/ + /* B 1.4.2 - Multi-element variables */ + /*************************************/ + void *visit(array_variable_c *symbol); + void *visit(subscript_list_c *symbol); + + /******************************************/ + /* B 1.4.3 - Declaration & Initialisation */ + /******************************************/ + void *visit(var1_list_c *symbol); + void *visit(location_c *symbol); + void *visit(located_var_decl_c *symbol); + + /**************************************/ + /* B 1.5 - Program organization units */ + /**************************************/ + /***********************/ + /* B 1.5.1 - Functions */ + /***********************/ + void *visit(function_declaration_c *symbol); + + /*****************************/ + /* B 1.5.2 - Function blocks */ + /*****************************/ + void *visit(function_block_declaration_c *symbol); + + /**********************/ + /* B 1.5.3 - Programs */ + /**********************/ + void *visit(program_declaration_c *symbol); + + /********************************/ + /* B 1.7 Configuration elements */ + /********************************/ + void *visit(configuration_declaration_c *symbol); + /****************************************/ + /* B.2 - Language IL (Instruction List) */ + /****************************************/ + /***********************************/ + /* B 2.1 Instructions and Operands */ + /***********************************/ + void *visit(instruction_list_c *symbol); + void *visit(il_instruction_c *symbol); + void *visit(il_simple_operation_c *symbol); + void *visit(il_function_call_c *symbol); + void *visit(il_expression_c *symbol); + void *visit(il_jump_operation_c *symbol); + void *visit(il_fb_call_c *symbol); + void *visit(il_formal_funct_call_c *symbol); +// void *visit(il_operand_list_c *symbol); + void *visit(simple_instr_list_c *symbol); + void *visit(il_simple_instruction_c*symbol); +// void *visit(il_param_list_c *symbol); +// void *visit(il_param_assignment_c *symbol); +// void *visit(il_param_out_assignment_c *symbol); + + /*******************/ + /* B 2.2 Operators */ + /*******************/ + void *visit(LD_operator_c *symbol); + void *visit(LDN_operator_c *symbol); + void *visit(ST_operator_c *symbol); + void *visit(STN_operator_c *symbol); + void *visit(NOT_operator_c *symbol); + void *visit(S_operator_c *symbol); + void *visit(R_operator_c *symbol); + void *visit(S1_operator_c *symbol); + void *visit(R1_operator_c *symbol); + void *visit(CLK_operator_c *symbol); + void *visit(CU_operator_c *symbol); + void *visit(CD_operator_c *symbol); + void *visit(PV_operator_c *symbol); + void *visit(IN_operator_c *symbol); + void *visit(PT_operator_c *symbol); + void *visit(AND_operator_c *symbol); + void *visit(OR_operator_c *symbol); + void *visit(XOR_operator_c *symbol); + void *visit(ANDN_operator_c *symbol); + void *visit(ORN_operator_c *symbol); + void *visit(XORN_operator_c *symbol); + void *visit(ADD_operator_c *symbol); + void *visit(SUB_operator_c *symbol); + void *visit(MUL_operator_c *symbol); + void *visit(DIV_operator_c *symbol); + void *visit(MOD_operator_c *symbol); + void *visit(GT_operator_c *symbol); + void *visit(GE_operator_c *symbol); + void *visit(EQ_operator_c *symbol); + void *visit(LT_operator_c *symbol); + void *visit(LE_operator_c *symbol); + void *visit(NE_operator_c *symbol); + void *visit(CAL_operator_c *symbol); + void *visit(CALC_operator_c *symbol); + void *visit(CALCN_operator_c *symbol); + void *visit(RET_operator_c *symbol); + void *visit(RETC_operator_c *symbol); + void *visit(RETCN_operator_c *symbol); + void *visit(JMP_operator_c *symbol); + void *visit(JMPC_operator_c *symbol); + void *visit(JMPCN_operator_c *symbol); + /* Symbol class handled together with function call checks */ + // void *visit(il_assign_operator_c *symbol, variable_name); + /* Symbol class handled together with function call checks */ + // void *visit(il_assign_operator_c *symbol, option, variable_name); + /***************************************/ + /* B.3 - Language ST (Structured Text) */ + /***************************************/ + /***********************/ + /* B 3.1 - Expressions */ + /***********************/ + void *visit(or_expression_c *symbol); + void *visit(xor_expression_c *symbol); + void *visit(and_expression_c *symbol); + void *visit(equ_expression_c *symbol); + void *visit(notequ_expression_c *symbol); + void *visit(lt_expression_c *symbol); + void *visit(gt_expression_c *symbol); + void *visit(le_expression_c *symbol); + void *visit(ge_expression_c *symbol); + void *visit(add_expression_c *symbol); + void *visit(sub_expression_c *symbol); + void *visit(mul_expression_c *symbol); + void *visit(div_expression_c *symbol); + void *visit(mod_expression_c *symbol); + void *visit(power_expression_c *symbol); + void *visit(neg_expression_c *symbol); + void *visit(not_expression_c *symbol); + + void *visit(function_invocation_c *symbol); + + /*********************************/ + /* B 3.2.1 Assignment Statements */ + /*********************************/ + void *visit(assignment_statement_c *symbol); + + /*****************************************/ + /* B 3.2.2 Subprogram Control Statements */ + /*****************************************/ + void *visit(fb_invocation_c *symbol); + + /********************************/ + /* B 3.2.3 Selection Statements */ + /********************************/ + void *visit(if_statement_c *symbol); + void *visit(elseif_statement_c *symbol); + void *visit(case_statement_c *symbol); + void *visit(case_element_list_c *symbol); + void *visit(case_element_c *symbol); + void *visit(case_list_c *symbol); + + /********************************/ + /* B 3.2.4 Iteration Statements */ + /********************************/ + void *visit(for_statement_c *symbol); + void *visit(while_statement_c *symbol); + void *visit(repeat_statement_c *symbol); + +}; // narrow_candidate_datatypes_c + + + + + + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/print_datatypes_error.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/print_datatypes_error.cc Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,1290 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2011 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2011-2012 Manuele Conti (manuele.conti@sirius-es.it) + * Copyright (C) 2011-2012 Matteo Facchinetti (matteo.facchinetti@sirius-es.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* NOTE: The algorithm implemented here assumes that the symbol_c.candidate_datatype, and the symbol_c.datatype + * annotations have already been apropriately filled in! + * BEFORE running this visitor, be sure to CALL the fill_candidate_datatypes_c, and the narrow_candidate_datatypes_c visitors! + */ + + +/* + * By analysing the candidate datatype lists, as well as the chosen datatype for each expression, determine + * if an datatype error has been found, and if so, print out an error message. + */ + + +#include "print_datatypes_error.hh" +#include "datatype_functions.hh" + +#include +#include +#include +#include +#include + + + + + + +#define FIRST_(symbol1, symbol2) (((symbol1)->first_order < (symbol2)->first_order) ? (symbol1) : (symbol2)) +#define LAST_(symbol1, symbol2) (((symbol1)->last_order > (symbol2)->last_order) ? (symbol1) : (symbol2)) + +#define STAGE3_ERROR(error_level, symbol1, symbol2, ...) { \ + if (current_display_error_level >= error_level) { \ + fprintf(stderr, "%s:%d-%d..%d-%d: error: ", \ + FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ + LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + il_error = true; \ + error_count++; \ + } \ +} + + +#define STAGE3_WARNING(symbol1, symbol2, ...) { \ + fprintf(stderr, "%s:%d-%d..%d-%d: warning: ", \ + FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ + LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + warning_found = true; \ +} + + +/* set to 1 to see debug info during execution */ +static int debug = 0; + +print_datatypes_error_c::print_datatypes_error_c(symbol_c *ignore) { + error_count = 0; + warning_found = false; + current_display_error_level = error_level_default; +} + +print_datatypes_error_c::~print_datatypes_error_c(void) { +} + +int print_datatypes_error_c::get_error_count() { + return error_count; +} + + + + + +/* Verify if the datatypes of all prev_il_instructions are valid and equal! */ +static bool are_all_datatypes_of_prev_il_instructions_datatypes_equal(il_instruction_c *symbol) { + if (NULL == symbol) ERROR; + bool res; + + if (symbol->prev_il_instruction.size() > 0) + res = is_type_valid(symbol->prev_il_instruction[0]->datatype); + + for (unsigned int i = 1; i < symbol->prev_il_instruction.size(); i++) + res &= is_type_equal(symbol->prev_il_instruction[i-1]->datatype, symbol->prev_il_instruction[i]->datatype); + + return res; +} + + + + +/* a helper function... */ +symbol_c *print_datatypes_error_c::base_type(symbol_c *symbol) { + /* NOTE: symbol == NULL is valid. It will occur when, for e.g., an undefined/undeclared symbolic_variable is used + * in the code. + */ + if (symbol == NULL) return NULL; + return (symbol_c *)symbol->accept(search_base_type); +} + + + +/* +typedef struct { + symbol_c *function_name, + symbol_c *nonformal_operand_list, + symbol_c * formal_operand_list, + + std::vector &candidate_functions, + symbol_c &*called_function_declaration, + int &extensible_param_count +} generic_function_call_t; +*/ +void print_datatypes_error_c::handle_function_invocation(symbol_c *fcall, generic_function_call_t fcall_data) { + symbol_c *param_value, *param_name; + function_call_param_iterator_c fcp_iterator(fcall); + bool function_invocation_error = false; + const char *POU_str = NULL; + + if (generic_function_call_t::POU_FB == fcall_data.POU_type) POU_str = "FB"; + if (generic_function_call_t::POU_function == fcall_data.POU_type) POU_str = "function"; + if (NULL == POU_str) ERROR; + + if ((NULL != fcall_data.formal_operand_list) && (NULL != fcall_data.nonformal_operand_list)) + ERROR; + + symbol_c *f_decl = fcall_data.called_function_declaration; + if ((NULL == f_decl) && (generic_function_call_t::POU_FB ==fcall_data.POU_type)) { + /* Due to the way the syntax analysis is buit (i.e. stage 2), this should never occur. */ + /* I.e., a FB invocation using an undefined FB variable is not possible in the current implementation of stage 2. */ + ERROR; + } + if (NULL == f_decl) { + /* we now try to find any function declaration with the same name, just so we can provide some relevant error messages */ + function_symtable_t::iterator lower = function_symtable.lower_bound(fcall_data.function_name); + if (lower == function_symtable.end()) ERROR; + f_decl = function_symtable.get_value(lower); + } + + if (NULL != fcall_data.formal_operand_list) { + fcall_data.formal_operand_list->accept(*this); + if (NULL != f_decl) { + function_param_iterator_c fp_iterator(f_decl); + while ((param_name = fcp_iterator.next_f()) != NULL) { + param_value = fcp_iterator.get_current_value(); + + /* Check if there are duplicate parameter values */ + if(fcp_iterator.search_f(param_name) != param_value) { + function_invocation_error = true; + STAGE3_ERROR(0, param_name, param_name, "Duplicate parameter '%s' when invoking %s '%s'", ((identifier_c *)param_name)->value, POU_str, ((identifier_c *)fcall_data.function_name)->value); + continue; /* jump to next parameter */ + } + + /* Find the corresponding parameter in function declaration */ + if (NULL == fp_iterator.search(param_name)) { + function_invocation_error = true; + STAGE3_ERROR(0, param_name, param_name, "Invalid parameter '%s' when invoking %s '%s'", ((identifier_c *)param_name)->value, POU_str, ((identifier_c *)fcall_data.function_name)->value); + continue; /* jump to next parameter */ + } + + /* check whether direction (IN, OUT, IN_OUT) and assignment types (:= , =>) are compatible !!! */ + /* Obtaining the assignment direction: := (assign_in) or => (assign_out) */ + function_call_param_iterator_c::assign_direction_t call_param_dir = fcp_iterator.get_assign_direction(); + /* Get the parameter direction: IN, OUT, IN_OUT */ + function_param_iterator_c::param_direction_t param_dir = fp_iterator.param_direction(); + if (function_call_param_iterator_c::assign_in == call_param_dir) { + if ((function_param_iterator_c::direction_in != param_dir) && + (function_param_iterator_c::direction_inout != param_dir)) { + function_invocation_error = true; + STAGE3_ERROR(0, param_name, param_name, "Invalid assignment syntax ':=' used for parameter '%s', when invoking %s '%s'", ((identifier_c *)param_name)->value, POU_str, ((identifier_c *)fcall_data.function_name)->value); + continue; /* jump to next parameter */ + } + } else if (function_call_param_iterator_c::assign_out == call_param_dir) { + if ((function_param_iterator_c::direction_out != param_dir)) { + function_invocation_error = true; + STAGE3_ERROR(0, param_name, param_name, "Invalid assignment syntax '=>' used for parameter '%s', when invoking %s '%s'", ((identifier_c *)param_name)->value, POU_str, ((identifier_c *)fcall_data.function_name)->value); + continue; /* jump to next parameter */ + } + } else ERROR; + + if (NULL == param_value->datatype) { + function_invocation_error = true; + STAGE3_ERROR(0, param_value, param_value, "Data type incompatibility between parameter '%s' and value being passed, when invoking %s '%s'", ((identifier_c *)param_name)->value, POU_str, ((identifier_c *)fcall_data.function_name)->value); + continue; /* jump to next parameter */ + } + } + } + } + if (NULL != fcall_data.nonformal_operand_list) { + fcall_data.nonformal_operand_list->accept(*this); + if (f_decl) + for (int i = 1; (param_value = fcp_iterator.next_nf()) != NULL; i++) { + /* TODO: verify if it is lvalue when INOUT or OUTPUT parameters! */ + + /* This handle_function_invocation() will be called to handle IL function calls, where the first parameter comes from the previous IL instruction. + * In this case, the previous IL instruction will be artifically (and temporarily) added to the begining ot the parameter list + * so we (in this function) can handle this situation like all the other function calls. + * However, + * a) if NO previous IL function exists, then we get a fake previous IL function, with no location data (i.e. not found anywhere in the source code. + * b) the function call may actually have several prev IL instructions (if several JMP instructions jump directly to the il function call). + * In order to handle these situations gracefully, we first check whether the first parameter is really an IL istruction! + */ + il_instruction_c *il_instruction_symbol = dynamic_cast(param_value); + if ((NULL != il_instruction_symbol) && (i == 1)) { + /* We are in a situation where an IL function call is passed the first parameter, which is actually the previous IL instruction */ + /* However, this is really a fake previous il instruction (see visit(il_instruction_c *) ) + * We will iterate through all the real previous IL instructions, and analyse each of them one by one */ + if (il_instruction_symbol->prev_il_instruction.size() == 0) { + function_invocation_error = true; + STAGE3_ERROR(0, fcall, fcall, "No available data to pass to first parameter of IL function %s. Missing a previous LD instruction?", ((identifier_c *)fcall_data.function_name)->value); + } +#if 0 + /* NOTE: We currently comment out this code... + * This does not currently work, since the narrow operation is currently done on the intersection + * of all the previous IL instructions, so we currently either accept them all, or none at all. + * In order to be able to produce these user freindly error messages, we will need to update the + * narrow algorithm. We leave this untill somebody aks for it... + * So, for now, we simply comment out this code. + */ + for (unsigned int p = 0; p < il_instruction_symbol->prev_il_instruction.size(); p++) { + symbol_c *value = il_instruction_symbol->prev_il_instruction[p]; + if (!is_type_valid(value->datatype)) { + function_invocation_error = true; + STAGE3_ERROR(0, fcall, fcall, "Data type incompatibility for value passed to first parameter when invoking function '%s'", ((identifier_c *)fcall_data.function_name)->value); + STAGE3_ERROR(0, value, value, "This is the IL instruction producing the incompatible data type to first parameter of function '%s'", ((identifier_c *)fcall_data.function_name)->value); + } + } +#else + if (!is_type_valid(il_instruction_symbol->datatype)) { + function_invocation_error = true; + STAGE3_ERROR(0, fcall, fcall, "Data type incompatibility between value in IL 'accumulator' and first parameter of function '%s'", ((identifier_c *)fcall_data.function_name)->value); + } +#endif + if (function_invocation_error) + /* when handling a IL function call, and an error is found in the first parameter, then we bug out and do not print out any more error messages. */ + return; + } + else if (!is_type_valid(param_value->datatype)) { + function_invocation_error = true; + STAGE3_ERROR(0, param_value, param_value, "Data type incompatibility for value passed in position %d when invoking %s '%s'", i, POU_str, ((identifier_c *)fcall_data.function_name)->value); + } + } + } + + if (NULL == fcall_data.called_function_declaration) { + function_invocation_error = true; + STAGE3_ERROR(0, fcall, fcall, "Unable to resolve which overloaded %s '%s' is being invoked.", POU_str, ((identifier_c *)fcall_data.function_name)->value); + } + + if (function_invocation_error) { + /* No compatible function exists */ + STAGE3_ERROR(2, fcall, fcall, "Invalid parameters when invoking %s '%s'", POU_str, ((identifier_c *)fcall_data.function_name)->value); + } + + return; +} + + + +void *print_datatypes_error_c::handle_implicit_il_fb_invocation(const char *param_name, symbol_c *il_operator, symbol_c *called_fb_declaration) { + if (NULL == il_operand) { + STAGE3_ERROR(0, il_operator, il_operator, "Missing operand for FB call operator '%s'.", param_name); + return NULL; + } + il_operand->accept(*this); + + if (NULL == called_fb_declaration) { + STAGE3_ERROR(0, il_operator, il_operand, "Invalid FB call: operand is not a FB instance."); + return NULL; + } + + if (fake_prev_il_instruction->prev_il_instruction.empty()) { + STAGE3_ERROR(0, il_operator, il_operand, "FB invocation operator '%s' must be preceded by a 'LD' (or equivalent) operator.", param_name); + return NULL; + } + + /* Find the corresponding parameter in function declaration */ + function_param_iterator_c fp_iterator(called_fb_declaration); + if (NULL == fp_iterator.search(param_name)) { + /* TODO: must also check whther it is an IN parameter!! */ + /* NOTE: although all standard FBs have the implicit FB calls defined as input parameters + * (i.e., for all standard FBs, CLK, PT, IN, CU, CD, S1, R1, etc... is always an input parameter) + * if a non-standard (i.e. a FB not defined in the standard library) FB is being called, then + * this (CLK, PT, IN, CU, ...) parameter may just have been defined as OUT or INOUT, + * which will not work for an implicit FB call! + */ + STAGE3_ERROR(0, il_operator, il_operand, "FB called by '%s' operator does not have a parameter named '%s'", param_name, param_name); + return NULL; + } + if (!are_all_datatypes_of_prev_il_instructions_datatypes_equal(fake_prev_il_instruction)) { + STAGE3_ERROR(0, il_operator, il_operand, "Data type incompatibility between parameter '%s' and value being passed.", param_name); + return NULL; + } + + + /* NOTE: The error_level currently being used for errors in variables/constants etc... is rather high. + * However, in the case of an implicit FB call, if the datatype of the operand == NULL, this may be + * the __only__ indication of an error! So we test it here again, to make sure thtis error will really + * be printed out! + */ + if (NULL == il_operand->datatype) { + /* Note: the case of (NULL == fb_declaration) was already caught above! */ +// if (NULL != fb_declaration) { + STAGE3_ERROR(0, il_operator, il_operator, "Invalid FB call: Datatype incompatibility between the FB's '%s' parameter and value being passed, or paramater '%s' is not a 'VAR_INPUT' parameter.", param_name, param_name); + return NULL; +// } + } + + return NULL; +} + + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ +void *print_datatypes_error_c::visit(real_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for ANY_REAL data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_REAL data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(integer_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for ANY_INT data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_INT data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(neg_real_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for ANY_REAL data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_REAL data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(neg_integer_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for ANY_INT data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_INT data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(binary_integer_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for ANY_INT data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_INT data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(octal_integer_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for ANY_INT data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_INT data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(hex_integer_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for ANY_INT data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_INT data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(integer_literal_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for %s data type.", elementary_type_c::to_string(symbol->type)); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_INT data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(real_literal_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for %s data type.", elementary_type_c::to_string(symbol->type)); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_REAL data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(bit_string_literal_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for %s data type.", elementary_type_c::to_string(symbol->type)); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_BIT data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(boolean_literal_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Value is not valid for %s data type.", elementary_type_c::to_string(symbol->type)); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_BOOL data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(boolean_true_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Value is not valid for ANY_BOOL data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_BOOL data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(boolean_false_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Value is not valid for ANY_BOOL data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "ANY_BOOL data type not valid in this location."); + } + return NULL; +} + +/*******************************/ +/* B.1.2.2 Character Strings */ +/*******************************/ +void *print_datatypes_error_c::visit(double_byte_character_string_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for WSTRING data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "WSTRING data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(single_byte_character_string_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Numerical value exceeds range for STRING data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "STRING data type not valid in this location."); + } + return NULL; +} + +/***************************/ +/* B 1.2.3 - Time Literals */ +/***************************/ +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ +void *print_datatypes_error_c::visit(duration_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Invalid syntax for TIME data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "TIME data type not valid in this location."); + } + return NULL; +} + +/************************************/ +/* B 1.2.3.2 - Time of day and Date */ +/************************************/ +void *print_datatypes_error_c::visit(time_of_day_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Invalid syntax for TOD data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "TOD data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(date_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Invalid syntax for DATE data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "DATE data type not valid in this location."); + } + return NULL; +} + +void *print_datatypes_error_c::visit(date_and_time_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) { + STAGE3_ERROR(0, symbol, symbol, "Invalid syntax for DT data type."); + } else if (NULL == symbol->datatype) { + STAGE3_ERROR(4, symbol, symbol, "DT data type not valid in this location."); + } + return NULL; +} + +/**********************/ +/* B 1.3 - Data types */ +/**********************/ +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ +void *print_datatypes_error_c::visit(simple_spec_init_c *symbol) { + if (!is_type_valid(symbol->simple_specification->datatype)) { + STAGE3_ERROR(0, symbol->simple_specification, symbol->simple_specification, "Invalid data type."); + } else if (NULL != symbol->constant) { + if (!is_type_valid(symbol->constant->datatype)) + STAGE3_ERROR(0, symbol->constant, symbol->constant, "Initial value has incompatible data type."); + } else if (!is_type_valid(symbol->datatype)) { + ERROR; /* If we have an error here, then we must also have an error in one of + * the two previous tests. If we reach this point, some strange error is ocurring! + */ + } + return NULL; +} + +void *print_datatypes_error_c::visit(data_type_declaration_c *symbol) { + // TODO !!! + /* for the moment we must return NULL so semantic analysis of remaining code is not interrupted! */ + return NULL; +} + +void *print_datatypes_error_c::visit(enumerated_value_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) + STAGE3_ERROR(0, symbol, symbol, "Ambiguous enumerate value or Variable not declared in this scope."); + return NULL; +} + + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ +void *print_datatypes_error_c::visit(symbolic_variable_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) + STAGE3_ERROR(0, symbol, symbol, "Variable not declared in this scope."); + return NULL; +} + +/********************************************/ +/* B 1.4.1 - Directly Represented Variables */ +/********************************************/ +void *print_datatypes_error_c::visit(direct_variable_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) ERROR; + if (!is_type_valid(symbol->datatype)) + STAGE3_ERROR(4, symbol, symbol, "Direct variable has incompatible data type with expression."); + return NULL; +} + +/*************************************/ +/* B 1.4.2 - Multi-element variables */ +/*************************************/ +/* subscripted_variable '[' subscript_list ']' */ +// SYM_REF2(array_variable_c, subscripted_variable, subscript_list) +void *print_datatypes_error_c::visit(array_variable_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) + STAGE3_ERROR(0, symbol, symbol, "Array variable not declared in this scope."); + + /* recursively call the subscript list to print any errors in the expressions used in the subscript...*/ + symbol->subscript_list->accept(*this); + return NULL; +} + +/* subscript_list ',' subscript */ +// SYM_LIST(subscript_list_c) +void *print_datatypes_error_c::visit(subscript_list_c *symbol) { + for (int i = 0; i < symbol->n; i++) { + int start_error_count = error_count; + symbol->elements[i]->accept(*this); + /* The following error message will only get printed if the current_display_error_level is set higher than 0! */ + if ((start_error_count == error_count) && (NULL == symbol->elements[i]->datatype)) + STAGE3_ERROR(0, symbol, symbol, "Invalid data type for array subscript field."); + } + return NULL; +} + + +/* record_variable '.' field_selector */ +/* WARNING: input and/or output variables of function blocks + * may be accessed as fields of a structured variable! + * Code handling a structured_variable_c must take + * this into account! + */ +// SYM_REF2(structured_variable_c, record_variable, field_selector) +/* NOTE: We do not recursively determine the data types of each field_selector in fill_candidate_datatypes_c, + * so it does not make sense to recursively visit all the field_selectors to print out error messages. + * Maybe in the future, if we find the need to print out more detailed error messages, we might do it that way. For now, we don't! + */ +void *print_datatypes_error_c::visit(structured_variable_c *symbol) { + if (symbol->candidate_datatypes.size() == 0) + STAGE3_ERROR(0, symbol, symbol, "Undeclared structured/FB variable."); + return NULL; +} + + + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ + +/* AT direct_variable */ +// SYM_REF1(location_c, direct_variable) +void *print_datatypes_error_c::visit(location_c *symbol) { + symbol->direct_variable->accept(*this); + return NULL; +} + + +/* [variable_name] location ':' located_var_spec_init */ +/* variable_name -> may be NULL ! */ +// SYM_REF3(located_var_decl_c, variable_name, location, located_var_spec_init) +void *print_datatypes_error_c::visit(located_var_decl_c *symbol) { + symbol->located_var_spec_init->accept(*this); + /* It does not make sense to call symbol->location->accept(*this). The check is done right here if the following if() */ + // symbol->location->accept(*this); + if ((is_type_valid(symbol->located_var_spec_init->datatype)) && (!is_type_valid(symbol->location->datatype))) + STAGE3_ERROR(0, symbol, symbol, "Bit size of data type is incompatible with bit size of location."); + return NULL; +} + + +/************************************/ +/* B 1.5 Program organization units */ +/************************************/ +/*********************/ +/* B 1.5.1 Functions */ +/*********************/ +void *print_datatypes_error_c::visit(function_declaration_c *symbol) { + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + /* We do not check for data type errors in variable declarations, Skip this for now... */ +// symbol->var_declarations_list->accept(*this); + if (debug) printf("Print error data types list in body of function %s\n", ((token_c *)(symbol->derived_function_name))->value); + il_parenthesis_level = 0; + il_error = false; + symbol->function_body->accept(*this); + delete search_varfb_instance_type; + search_varfb_instance_type = NULL; + return NULL; +} + +/***************************/ +/* B 1.5.2 Function blocks */ +/***************************/ +void *print_datatypes_error_c::visit(function_block_declaration_c *symbol) { + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + /* We do not check for data type errors in variable declarations, Skip this for now... */ +// symbol->var_declarations->accept(*this); + if (debug) printf("Print error data types list in body of FB %s\n", ((token_c *)(symbol->fblock_name))->value); + il_parenthesis_level = 0; + il_error = false; + symbol->fblock_body->accept(*this); + delete search_varfb_instance_type; + search_varfb_instance_type = NULL; + return NULL; +} + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ +void *print_datatypes_error_c::visit(program_declaration_c *symbol) { + search_varfb_instance_type = new search_varfb_instance_type_c(symbol); + /* We do not check for data type errors in variable declarations, Skip this for now... */ + symbol->var_declarations->accept(*this); + if (debug) printf("Print error data types list in body of program %s\n", ((token_c *)(symbol->program_type_name))->value); + il_parenthesis_level = 0; + il_error = false; + symbol->function_block_body->accept(*this); + delete search_varfb_instance_type; + search_varfb_instance_type = NULL; + return NULL; +} + + + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ +void *print_datatypes_error_c::visit(configuration_declaration_c *symbol) { + // TODO !!! + /* for the moment we must return NULL so semantic analysis of remaining code is not interrupted! */ + return NULL; +} + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ + +// void *visit(instruction_list_c *symbol); + +/* | label ':' [il_incomplete_instruction] eol_list */ +// SYM_REF2(il_instruction_c, label, il_instruction) +void *print_datatypes_error_c::visit(il_instruction_c *symbol) { + if (NULL != symbol->il_instruction) { + il_instruction_c tmp_prev_il_instruction(NULL, NULL); +#if 0 + /* NOTE: The following is currently no longer needed. Since the following code is actually cool, + * we don't delete it, but simply comment it out. It might just come in handy later on... + */ + /* When handling a il function call, this fake_prev_il_instruction may be used as a standard function call parameter, so it is important that + * it contain some valid location info so error messages make sense. + */ + if (symbol->prev_il_instruction.size() > 0) { + /* since we don't want to copy all that data one variable at a time, we copy it all at once */ + /* This has the advantage that, if we ever add some more data to the base symbol_c later on, we will not need to + * change the following line to guarantee that the data is copied correctly! + * However, it does have the drawback of copying more data than what we want! + * In order to only copy the data in the base class symbol_c, we use the tmp_symbol pointer! + * I (mario) have checked with a debugger, and it is working as intended! + */ + symbol_c *tmp_symbol1 = symbol->prev_il_instruction[0]; + symbol_c *tmp_symbol2 = &tmp_prev_il_instruction; + *tmp_symbol2 = *tmp_symbol1; + /* we do not want to copy the datatype variable, so we reset it to NULL */ + tmp_prev_il_instruction.datatype = NULL; + /* We don't need to worry about the candidate_datatype list (which we don't want to copy just yet), since that will + * be reset to the correct value when we call intersect_prev_candidate_datatype_lists() later on... + */ + } +#endif + /* the print error algorithm will need access to the intersected candidate_datatype lists of all prev_il_instructions, as well as the + * list of the prev_il_instructions. + * Instead of creating two 'global' (within the class) variables, we create a single il_instruction_c variable (fake_prev_il_instruction), + * and shove that data into this single variable. + */ + tmp_prev_il_instruction.prev_il_instruction = symbol->prev_il_instruction; + intersect_prev_candidate_datatype_lists(&tmp_prev_il_instruction); + if (are_all_datatypes_of_prev_il_instructions_datatypes_equal(symbol)) + if (symbol->prev_il_instruction.size() > 0) + tmp_prev_il_instruction.datatype = (symbol->prev_il_instruction[0])->datatype; + + /* Tell the il_instruction the datatype that it must generate - this was chosen by the next il_instruction (remember: we are iterating backwards!) */ + fake_prev_il_instruction = &tmp_prev_il_instruction; + symbol->il_instruction->accept(*this); + fake_prev_il_instruction = NULL; + } + + return NULL; +} + + + +void *print_datatypes_error_c::visit(il_simple_operation_c *symbol) { + il_operand = symbol->il_operand; + if (NULL != symbol->il_operand) { + symbol->il_operand->accept(*this); + } + /* recursive call to see whether data types are compatible */ + symbol->il_simple_operator->accept(*this); + il_operand = NULL; + return NULL; +} + +/* | function_name [il_operand_list] */ +/* NOTE: The parameters 'called_function_declaration' and 'extensible_param_count' are used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;) +void *print_datatypes_error_c::visit(il_function_call_c *symbol) { + /* The first parameter of a non formal function call in IL will be the 'current value' (i.e. the prev_il_instruction) + * In order to be able to handle this without coding special cases, we will simply prepend that symbol + * to the il_operand_list, and remove it after calling handle_function_call(). + * + * However, if no further paramters are given, then il_operand_list will be NULL, and we will + * need to create a new object to hold the pointer to prev_il_instruction. + * This change will also be undone later in print_datatypes_error_c. + */ + if (NULL == symbol->il_operand_list) symbol->il_operand_list = new il_operand_list_c; + if (NULL == symbol->il_operand_list) ERROR; + + ((list_c *)symbol->il_operand_list)->insert_element(fake_prev_il_instruction, 0); + + generic_function_call_t fcall_param = { + /* fcall_param.function_name = */ symbol->function_name, + /* fcall_param.nonformal_operand_list = */ symbol->il_operand_list, + /* fcall_param.formal_operand_list = */ NULL, + /* enum {POU_FB, POU_function} POU_type = */ generic_function_call_t::POU_function, + /* fcall_param.candidate_functions = */ symbol->candidate_functions, + /* fcall_param.called_function_declaration = */ symbol->called_function_declaration, + /* fcall_param.extensible_param_count = */ symbol->extensible_param_count + }; + +/* TODO: check what error message (if any) the compiler will give out if this function invocation + * is not preceded by a LD operator (or another equivalent operator or list of operators). + */ + handle_function_invocation(symbol, fcall_param); + + /* We now undo those changes to the abstract syntax tree made above! */ + ((list_c *)symbol->il_operand_list)->remove_element(0); + if (((list_c *)symbol->il_operand_list)->n == 0) { + /* if the list becomes empty, then that means that it did not exist before we made these changes, so we delete it! */ + delete symbol->il_operand_list; + symbol->il_operand_list = NULL; + } + + return NULL; +} + + +/* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ +// SYM_REF3(il_expression_c, il_expr_operator, il_operand, simple_instr_list); +void *print_datatypes_error_c::visit(il_expression_c *symbol) { + /* first give the parenthesised IL list a chance to print errors */ + il_instruction_c *save_fake_prev_il_instruction = fake_prev_il_instruction; + symbol->simple_instr_list->accept(*this); + fake_prev_il_instruction = save_fake_prev_il_instruction; + + /* Now handle the operation (il_expr_operator) that will use the result coming from the parenthesised IL list (i.e. simple_instr_list) */ + il_operand = symbol->simple_instr_list; /* This is not a bug! The parenthesised expression will be used as the operator! */ + symbol->il_expr_operator->accept(*this); + +return NULL; +} + + +/* il_call_operator prev_declared_fb_name + * | il_call_operator prev_declared_fb_name '(' ')' + * | il_call_operator prev_declared_fb_name '(' eol_list ')' + * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' + * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' + */ +/* NOTE: The parameter 'called_fb_declaration'is used to pass data between stage 3 and stage4 (although currently it is not used in stage 4 */ +// SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list, symbol_c *called_fb_declaration) +void *print_datatypes_error_c::visit(il_fb_call_c *symbol) { + int extensible_param_count; /* unused vairable! Needed for compilation only! */ + std::vector candidate_functions; /* unused vairable! Needed for compilation only! */ + generic_function_call_t fcall_param = { + /* fcall_param.function_name = */ symbol->fb_name, + /* fcall_param.nonformal_operand_list = */ symbol->il_operand_list, + /* fcall_param.formal_operand_list = */ symbol->il_param_list, + /* enum {POU_FB, POU_function} POU_type = */ generic_function_call_t::POU_FB, + /* fcall_param.candidate_functions = */ candidate_functions, /* will not be used, but must provide a reference to be able to compile */ + /* fcall_param.called_function_declaration = */ symbol->called_fb_declaration, + /* fcall_param.extensible_param_count = */ extensible_param_count /* will not be used, but must provide a reference to be able to compile */ + }; + + handle_function_invocation(symbol, fcall_param); + /* check the semantics of the CALC, CALCN operators! */ + symbol->il_call_operator->accept(*this); + return NULL; +} + +/* | function_name '(' eol_list [il_param_list] ')' */ +/* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4. */ +// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;) +void *print_datatypes_error_c::visit(il_formal_funct_call_c *symbol) { + generic_function_call_t fcall_param = { + /* fcall_param.function_name = */ symbol->function_name, + /* fcall_param.nonformal_operand_list = */ NULL, + /* fcall_param.formal_operand_list = */ symbol->il_param_list, + /* enum {POU_FB, POU_function} POU_type = */ generic_function_call_t::POU_function, + /* fcall_param.candidate_functions = */ symbol->candidate_functions, + /* fcall_param.called_function_declaration = */ symbol->called_function_declaration, + /* fcall_param.extensible_param_count = */ symbol->extensible_param_count + }; + + handle_function_invocation(symbol, fcall_param); + return NULL; +} + + +// void *visit(il_operand_list_c *symbol); +// void *visit(simple_instr_list_c *symbol); + +// SYM_REF1(il_simple_instruction_c, il_simple_instruction, symbol_c *prev_il_instruction;) +void *print_datatypes_error_c::visit(il_simple_instruction_c *symbol) { + if (symbol->prev_il_instruction.size() > 1) ERROR; /* There should be no labeled insructions inside an IL expression! */ + + il_instruction_c tmp_prev_il_instruction(NULL, NULL); + /* the print error algorithm will need access to the intersected candidate_datatype lists of all prev_il_instructions, as well as the + * list of the prev_il_instructions. + * Instead of creating two 'global' (within the class) variables, we create a single il_instruction_c variable (fake_prev_il_instruction), + * and shove that data into this single variable. + */ + if (symbol->prev_il_instruction.size() > 0) + tmp_prev_il_instruction.candidate_datatypes = symbol->prev_il_instruction[0]->candidate_datatypes; + tmp_prev_il_instruction.prev_il_instruction = symbol->prev_il_instruction; + + /* copy the candidate_datatypes list */ + fake_prev_il_instruction = &tmp_prev_il_instruction; + symbol->il_simple_instruction->accept(*this); + fake_prev_il_instruction = NULL; + return NULL; + + +// if (symbol->prev_il_instruction.size() == 0) prev_il_instruction = NULL; +// else prev_il_instruction = symbol->prev_il_instruction[0]; + +// symbol->il_simple_instruction->accept(*this); +// prev_il_instruction = NULL; +// return NULL; +} + + +// void *visit(il_param_list_c *symbol); +// void *visit(il_param_assignment_c *symbol); +// void *visit(il_param_out_assignment_c *symbol); + +/*******************/ +/* B 2.2 Operators */ +/*******************/ +void *print_datatypes_error_c::print_binary_operator_errors(const char *il_operator, symbol_c *symbol, bool deprecated_operation) { + if ((symbol->candidate_datatypes.size() == 0) && (il_operand->candidate_datatypes.size() > 0)) { + STAGE3_ERROR(0, symbol, symbol, "Data type mismatch for '%s' operator.", il_operator); + } else if (NULL == symbol->datatype) { + STAGE3_WARNING(symbol, symbol, "Result of '%s' operation is never used.", il_operator); + } else if (deprecated_operation) + STAGE3_WARNING(symbol, symbol, "Deprecated operation for '%s' operator.", il_operator); + return NULL; +} + + +void *print_datatypes_error_c::visit(LD_operator_c *symbol) { + return NULL; +} + +void *print_datatypes_error_c::visit(LDN_operator_c *symbol) { + if ((symbol->candidate_datatypes.size() == 0) && + (il_operand->candidate_datatypes.size() > 0)) + STAGE3_ERROR(0, symbol, symbol, "Data type mismatch for 'LDN' operator."); + return NULL; +} + +void *print_datatypes_error_c::visit(ST_operator_c *symbol) { + /* MANU: + * if prev_instruction is NULL we can print a message error or warning error like: + * we can't use a ST like first instruction. + * What do you think? + */ + if ((symbol->candidate_datatypes.size() == 0) && + (il_operand->candidate_datatypes.size() > 0)) + STAGE3_ERROR(0, symbol, symbol, "Data type mismatch for 'ST' operator."); + return NULL; +} + +void *print_datatypes_error_c::visit(STN_operator_c *symbol) { + /* MANU: + * if prev_instruction is NULL we can print a message error or warning error like: + * we can't use a ST like first instruction. + * What do you think? + */ + if ((symbol->candidate_datatypes.size() == 0) && + (il_operand->candidate_datatypes.size() > 0)) + STAGE3_ERROR(0, symbol, symbol, "Data type mismatch for 'STN' operator."); + return NULL; +} + +void *print_datatypes_error_c::visit(NOT_operator_c *symbol) { + /* NOTE: the standard allows syntax in which the NOT operator is followed by an optional + * NOT [] + * However, it does not define the semantic of the NOT operation when the is specified. + * We therefore consider it an error if an il_operand is specified! + */ + if (il_operand != NULL) + STAGE3_ERROR(0, symbol, symbol, "'NOT' operator may not have an operand."); + if (symbol->candidate_datatypes.size() == 0) + STAGE3_ERROR(0, symbol, symbol, "Data type mismatch for 'NOT' operator."); + return NULL; +} + +void *print_datatypes_error_c::visit(S_operator_c *symbol) { + /* TODO: what if this is a FB call ?? */ + if ((symbol->candidate_datatypes.size() == 0) && + (il_operand->candidate_datatypes.size() > 0)) + STAGE3_ERROR(0, symbol, symbol, "Data type mismatch for 'S' operator."); + return NULL; +} + +void *print_datatypes_error_c::visit(R_operator_c *symbol) { + /* TODO: what if this is a FB call ?? */ + if ((symbol->candidate_datatypes.size() == 0) && + (il_operand->candidate_datatypes.size() > 0)) + STAGE3_ERROR(0, symbol, symbol, "Data type mismatch for 'R' operator."); + return NULL; +} + +void *print_datatypes_error_c::visit( S1_operator_c *symbol) {return handle_implicit_il_fb_invocation( "S1", symbol, symbol->called_fb_declaration);} +void *print_datatypes_error_c::visit( R1_operator_c *symbol) {return handle_implicit_il_fb_invocation( "R1", symbol, symbol->called_fb_declaration);} +void *print_datatypes_error_c::visit(CLK_operator_c *symbol) {return handle_implicit_il_fb_invocation("CLK", symbol, symbol->called_fb_declaration);} +void *print_datatypes_error_c::visit( CU_operator_c *symbol) {return handle_implicit_il_fb_invocation( "CU", symbol, symbol->called_fb_declaration);} +void *print_datatypes_error_c::visit( CD_operator_c *symbol) {return handle_implicit_il_fb_invocation( "CD", symbol, symbol->called_fb_declaration);} +void *print_datatypes_error_c::visit( PV_operator_c *symbol) {return handle_implicit_il_fb_invocation( "PV", symbol, symbol->called_fb_declaration);} +void *print_datatypes_error_c::visit( IN_operator_c *symbol) {return handle_implicit_il_fb_invocation( "IN", symbol, symbol->called_fb_declaration);} +void *print_datatypes_error_c::visit( PT_operator_c *symbol) {return handle_implicit_il_fb_invocation( "PT", symbol, symbol->called_fb_declaration);} + +void *print_datatypes_error_c::visit( AND_operator_c *symbol) {return print_binary_operator_errors("AND" , symbol);} +void *print_datatypes_error_c::visit( OR_operator_c *symbol) {return print_binary_operator_errors( "OR" , symbol);} +void *print_datatypes_error_c::visit( XOR_operator_c *symbol) {return print_binary_operator_errors("XOR" , symbol);} +void *print_datatypes_error_c::visit(ANDN_operator_c *symbol) {return print_binary_operator_errors("ANDN", symbol);} +void *print_datatypes_error_c::visit( ORN_operator_c *symbol) {return print_binary_operator_errors( "ORN", symbol);} +void *print_datatypes_error_c::visit(XORN_operator_c *symbol) {return print_binary_operator_errors("XORN", symbol);} +void *print_datatypes_error_c::visit( ADD_operator_c *symbol) {return print_binary_operator_errors("ADD" , symbol, symbol->deprecated_operation);} +void *print_datatypes_error_c::visit( SUB_operator_c *symbol) {return print_binary_operator_errors("SUB" , symbol, symbol->deprecated_operation);} +void *print_datatypes_error_c::visit( MUL_operator_c *symbol) {return print_binary_operator_errors("MUL" , symbol, symbol->deprecated_operation);} +void *print_datatypes_error_c::visit( DIV_operator_c *symbol) {return print_binary_operator_errors("DIV" , symbol, symbol->deprecated_operation);} +void *print_datatypes_error_c::visit( MOD_operator_c *symbol) {return print_binary_operator_errors("MOD" , symbol);} + +void *print_datatypes_error_c::visit( GT_operator_c *symbol) {return print_binary_operator_errors( "GT" , symbol);} +void *print_datatypes_error_c::visit( GE_operator_c *symbol) {return print_binary_operator_errors( "GE" , symbol);} +void *print_datatypes_error_c::visit( EQ_operator_c *symbol) {return print_binary_operator_errors( "EQ" , symbol);} +void *print_datatypes_error_c::visit( LT_operator_c *symbol) {return print_binary_operator_errors( "LT" , symbol);} +void *print_datatypes_error_c::visit( LE_operator_c *symbol) {return print_binary_operator_errors( "LE" , symbol);} +void *print_datatypes_error_c::visit( NE_operator_c *symbol) {return print_binary_operator_errors( "NE" , symbol);} + + + + +void *print_datatypes_error_c::handle_conditional_flow_control_IL_instruction(symbol_c *symbol, const char *oper) { + if (NULL == symbol->datatype) + STAGE3_ERROR(0, symbol, symbol, "%s operator must be preceded by an IL instruction producing a BOOL value.", oper); + return NULL; +} + +void *print_datatypes_error_c::visit( CAL_operator_c *symbol) {return NULL;} +void *print_datatypes_error_c::visit( CALC_operator_c *symbol) {return handle_conditional_flow_control_IL_instruction(symbol, "CALC" );} +void *print_datatypes_error_c::visit(CALCN_operator_c *symbol) {return handle_conditional_flow_control_IL_instruction(symbol, "CALCN");} +void *print_datatypes_error_c::visit( RET_operator_c *symbol) {return NULL;} +void *print_datatypes_error_c::visit( RETC_operator_c *symbol) {return handle_conditional_flow_control_IL_instruction(symbol, "RETC" );} +void *print_datatypes_error_c::visit(RETCN_operator_c *symbol) {return handle_conditional_flow_control_IL_instruction(symbol, "RETCN");} +void *print_datatypes_error_c::visit( JMP_operator_c *symbol) {return NULL;} +void *print_datatypes_error_c::visit( JMPC_operator_c *symbol) {return handle_conditional_flow_control_IL_instruction(symbol, "JMPC" );} +void *print_datatypes_error_c::visit(JMPCN_operator_c *symbol) {return handle_conditional_flow_control_IL_instruction(symbol, "JMPCN");} + + + +/* Symbol class handled together with function call checks */ +// void *visit(il_assign_operator_c *symbol, variable_name); +/* Symbol class handled together with function call checks */ +// void *visit(il_assign_operator_c *symbol, option, variable_name); + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ + +void *print_datatypes_error_c::print_binary_expression_errors(const char *operation, symbol_c *symbol, symbol_c *l_expr, symbol_c *r_expr, bool deprecated_operation) { + l_expr->accept(*this); + r_expr->accept(*this); + if ((symbol->candidate_datatypes.size() == 0) && + (l_expr->candidate_datatypes.size() > 0) && + (r_expr->candidate_datatypes.size() > 0)) + STAGE3_ERROR(0, symbol, symbol, "Data type mismatch for '%s' expression.", operation); + if (deprecated_operation) + STAGE3_WARNING(symbol, symbol, "Deprecated operation for '%s' expression.", operation); + return NULL; +} + + +void *print_datatypes_error_c::visit( or_expression_c *symbol) {return print_binary_expression_errors( "OR", symbol, symbol->l_exp, symbol->r_exp);} +void *print_datatypes_error_c::visit( xor_expression_c *symbol) {return print_binary_expression_errors("XOR", symbol, symbol->l_exp, symbol->r_exp);} +void *print_datatypes_error_c::visit( and_expression_c *symbol) {return print_binary_expression_errors("AND", symbol, symbol->l_exp, symbol->r_exp);} +void *print_datatypes_error_c::visit( equ_expression_c *symbol) {return print_binary_expression_errors( "=" , symbol, symbol->l_exp, symbol->r_exp);} +void *print_datatypes_error_c::visit(notequ_expression_c *symbol) {return print_binary_expression_errors( "<>", symbol, symbol->l_exp, symbol->r_exp);} +void *print_datatypes_error_c::visit( lt_expression_c *symbol) {return print_binary_expression_errors( "<" , symbol, symbol->l_exp, symbol->r_exp);} +void *print_datatypes_error_c::visit( gt_expression_c *symbol) {return print_binary_expression_errors( ">" , symbol, symbol->l_exp, symbol->r_exp);} +void *print_datatypes_error_c::visit( le_expression_c *symbol) {return print_binary_expression_errors( "<=", symbol, symbol->l_exp, symbol->r_exp);} +void *print_datatypes_error_c::visit( ge_expression_c *symbol) {return print_binary_expression_errors( ">=", symbol, symbol->l_exp, symbol->r_exp);} +void *print_datatypes_error_c::visit( add_expression_c *symbol) {return print_binary_expression_errors( "+" , symbol, symbol->l_exp, symbol->r_exp, symbol->deprecated_operation);} +void *print_datatypes_error_c::visit( sub_expression_c *symbol) {return print_binary_expression_errors( "-" , symbol, symbol->l_exp, symbol->r_exp, symbol->deprecated_operation);} +void *print_datatypes_error_c::visit( mul_expression_c *symbol) {return print_binary_expression_errors( "*" , symbol, symbol->l_exp, symbol->r_exp, symbol->deprecated_operation);} +void *print_datatypes_error_c::visit( div_expression_c *symbol) {return print_binary_expression_errors( "/" , symbol, symbol->l_exp, symbol->r_exp, symbol->deprecated_operation);} +void *print_datatypes_error_c::visit( mod_expression_c *symbol) {return print_binary_expression_errors("MOD", symbol, symbol->l_exp, symbol->r_exp);} +void *print_datatypes_error_c::visit( power_expression_c *symbol) {return print_binary_expression_errors( "**", symbol, symbol->l_exp, symbol->r_exp);} + + +void *print_datatypes_error_c::visit(neg_expression_c *symbol) { + symbol->exp->accept(*this); + if ((symbol->candidate_datatypes.size() == 0) && + (symbol->exp->candidate_datatypes.size() > 0)) + STAGE3_ERROR(0, symbol, symbol, "Invalid data type for 'NEG' expression."); + return NULL; +} + + +void *print_datatypes_error_c::visit(not_expression_c *symbol) { + symbol->exp->accept(*this); + if ((symbol->candidate_datatypes.size() == 0) && + (symbol->exp->candidate_datatypes.size() > 0)) + STAGE3_ERROR(0, symbol, symbol, "Invalid data type for 'NOT' expression."); + return NULL; +} + +/* NOTE: The parameter 'called_function_declaration', 'extensible_param_count' and 'candidate_functions' are used to pass data between the stage 3 and stage 4. */ +/* formal_param_list -> may be NULL ! */ +/* nonformal_param_list -> may be NULL ! */ +// SYM_REF3(function_invocation_c, function_name, formal_param_list, nonformal_param_list, symbol_c *called_function_declaration; int extensible_param_count; std::vector candidate_functions;) +void *print_datatypes_error_c::visit(function_invocation_c *symbol) { + generic_function_call_t fcall_param = { + /* fcall_param.function_name = */ symbol->function_name, + /* fcall_param.nonformal_operand_list = */ symbol->nonformal_param_list, + /* fcall_param.formal_operand_list = */ symbol->formal_param_list, + /* enum {POU_FB, POU_function} POU_type = */ generic_function_call_t::POU_function, + /* fcall_param.candidate_functions = */ symbol->candidate_functions, + /* fcall_param.called_function_declaration = */ symbol->called_function_declaration, + /* fcall_param.extensible_param_count = */ symbol->extensible_param_count + }; + + handle_function_invocation(symbol, fcall_param); + return NULL; +} + + + +/********************/ +/* B 3.2 Statements */ +/********************/ + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ +void *print_datatypes_error_c::visit(assignment_statement_c *symbol) { + symbol->l_exp->accept(*this); + symbol->r_exp->accept(*this); + if ((NULL == symbol->l_exp->datatype) && + (NULL == symbol->r_exp->datatype) && + (symbol->l_exp->candidate_datatypes.size() > 0) && + (symbol->r_exp->candidate_datatypes.size() > 0)) + STAGE3_ERROR(0, symbol, symbol, "Incompatible data types for ':=' operation."); + return NULL; +} + + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ +/* fb_name '(' [param_assignment_list] ')' */ +/* formal_param_list -> may be NULL ! */ +/* nonformal_param_list -> may be NULL ! */ +/* NOTE: The parameter 'called_fb_declaration'is used to pass data between stage 3 and stage4 (although currently it is not used in stage 4 */ +// SYM_REF3(fb_invocation_c, fb_name, formal_param_list, nonformal_param_list, symbol_c *called_fb_declaration;) +void *print_datatypes_error_c::visit(fb_invocation_c *symbol) { + int extensible_param_count; /* unused vairable! Needed for compilation only! */ + std::vector candidate_functions; /* unused vairable! Needed for compilation only! */ + generic_function_call_t fcall_param = { + /* fcall_param.function_name = */ symbol->fb_name, + /* fcall_param.nonformal_operand_list = */ symbol->nonformal_param_list, + /* fcall_param.formal_operand_list = */ symbol->formal_param_list, + /* enum {POU_FB, POU_function} POU_type = */ generic_function_call_t::POU_FB, + /* fcall_param.candidate_functions = */ candidate_functions, /* will not be used, but must provide a reference to be able to compile */ + /* fcall_param.called_function_declaration = */ symbol->called_fb_declaration, + /* fcall_param.extensible_param_count = */ extensible_param_count /* will not be used, but must provide a reference to be able to compile */ + }; + + handle_function_invocation(symbol, fcall_param); + return NULL; +} + + +/********************************/ +/* B 3.2.3 Selection Statements */ +/********************************/ + +void *print_datatypes_error_c::visit(if_statement_c *symbol) { + symbol->expression->accept(*this); + if ((NULL == symbol->expression->datatype) && + (symbol->expression->candidate_datatypes.size() > 0)) { + STAGE3_ERROR(0, symbol, symbol, "Invalid data type for 'IF' condition."); + } + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + if (NULL != symbol->elseif_statement_list) + symbol->elseif_statement_list->accept(*this); + if (NULL != symbol->else_statement_list) + symbol->else_statement_list->accept(*this); + return NULL; +} + +void *print_datatypes_error_c::visit(elseif_statement_c *symbol) { + symbol->expression->accept(*this); + if ((NULL == symbol->expression->datatype) && + (symbol->expression->candidate_datatypes.size() > 0)) { + STAGE3_ERROR(0, symbol, symbol, "Invalid data type for 'ELSIF' condition."); + } + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + + +void *print_datatypes_error_c::visit(case_statement_c *symbol) { + symbol->expression->accept(*this); + if ((NULL == symbol->expression->datatype) && + (symbol->expression->candidate_datatypes.size() > 0)) { + STAGE3_ERROR(0, symbol, symbol, "'CASE' quantity not an integer or enumerated."); + } + symbol->case_element_list->accept(*this); + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ + +void *print_datatypes_error_c::visit(for_statement_c *symbol) { + symbol->control_variable->accept(*this); + symbol->beg_expression->accept(*this); + symbol->end_expression->accept(*this); + /* Control variable */ + if ((NULL == symbol->control_variable->datatype) && + (symbol->control_variable->candidate_datatypes.size() > 0)) { + STAGE3_ERROR(0, symbol, symbol, "Invalid data type for 'FOR' control variable."); + } + /* BEG expression */ + if ((NULL == symbol->beg_expression->datatype) && + (symbol->beg_expression->candidate_datatypes.size() > 0)) { + STAGE3_ERROR(0, symbol, symbol, "Invalid data type for 'FOR' begin expression."); + } + /* END expression */ + if ((NULL == symbol->end_expression->datatype) && + (symbol->end_expression->candidate_datatypes.size() > 0)) { + STAGE3_ERROR(0, symbol, symbol, "Invalid data type for 'FOR' end expression."); + } + /* BY expression */ + if ((NULL != symbol->by_expression) && + (NULL == symbol->by_expression->datatype) && + (symbol->end_expression->candidate_datatypes.size() > 0)) { + STAGE3_ERROR(0, symbol, symbol, "Invalid data type for 'FOR' by expression."); + } + /* DO statement */ + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + + return NULL; +} + +void *print_datatypes_error_c::visit(while_statement_c *symbol) { + symbol->expression->accept(*this); + if (!is_type_valid(symbol->expression->datatype)) { + STAGE3_ERROR(0, symbol, symbol, "Invalid data type for 'WHILE' condition."); + return NULL; + } + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + return NULL; +} + +void *print_datatypes_error_c::visit(repeat_statement_c *symbol) { + if (!is_type_valid(symbol->expression->datatype)) { + STAGE3_ERROR(0, symbol, symbol, "Invalid data type for 'REPEAT' condition."); + return NULL; + } + if (NULL != symbol->statement_list) + symbol->statement_list->accept(*this); + symbol->expression->accept(*this); + return NULL; +} + + + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/print_datatypes_error.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage3/print_datatypes_error.hh Wed Aug 22 16:46:17 2012 +0200 @@ -0,0 +1,342 @@ +/* + * matiec - a compiler for the programming languages defined in IEC 61131-3 + * + * Copyright (C) 2009-2011 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2011-2012 Manuele Conti (manuele.conti@sirius-es.it) + * Copyright (C) 2011-2012 Matteo Facchinetti (matteo.facchinetti@sirius-es.it) + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* NOTE: The algorithm implemented here assumes that the symbol_c.candidate_datatype, and the symbol_c.datatype + * annotations have already been apropriately filled in! + * BEFORE running this visitor, be sure to CALL the fill_candidate_datatypes_c, and the narrow_candidate_datatypes_c visitors! + */ + + +/* + * By analysing the candidate datatype lists, as well as the chosen datatype for each expression, determine + * if an datatype error has been found, and if so, print out an error message. + */ + + +#include "../absyntax_utils/absyntax_utils.hh" +#include "datatype_functions.hh" + + +class print_datatypes_error_c: public iterator_visitor_c { + + private: + /* The level of detail that the user wants us to display error messages. */ +// #define error_level_default (1) + #define error_level_default (1) + #define error_level_nagging (4) + unsigned int current_display_error_level; + + search_varfb_instance_type_c *search_varfb_instance_type; + search_base_type_c search_base_type; + /* When calling a function block, we must first find it's type, + * by searching through the declarations of the variables currently + * in scope. + * This class does just that... + * A new object instance is instantiated whenever we start checking semantics + * for a function block type declaration, or a program declaration. + * This object instance will then later be called while the + * function block's or the program's body is being handled. + * + * Note that functions cannot contain calls to function blocks, + * so we do not create an object instance when handling + * a function declaration. + */ + + /* In IL code, once we find a type mismatch error, it is best to + * ignore any further errors until the end of the logical operation, + * i.e. until the next LD. + * However, we cannot clear the il_error flag on all LD operations, + * as these may also be used within parenthesis. LD operations + * within parenthesis may not clear the error flag. + * We therefore need a counter to know how deep inside a parenthesis + * structure we are. + */ + int il_parenthesis_level; + bool il_error; + int error_count; + bool warning_found; + + /* the current data type of the data stored in the IL stack, i.e. the default variable */ + il_instruction_c *fake_prev_il_instruction; + /* the narrow algorithm will need access to the intersected candidate_datatype lists of all prev_il_instructions, as well as the + * list of the prev_il_instructions. + * Instead of creating two 'global' (within the class) variables, we create a single il_instruction_c variable (fake_prev_il_instruction), + * and shove that data into this single variable. + */ + symbol_c *il_operand_type; + symbol_c *il_operand; + + /* some helper functions... */ + symbol_c *base_type(symbol_c *symbol); + void handle_function_invocation(symbol_c *fcall, generic_function_call_t fcall_data); + void *handle_implicit_il_fb_invocation(const char *param_name, symbol_c *il_operator, symbol_c *called_fb_declaration); + void *handle_conditional_flow_control_IL_instruction(symbol_c *symbol, const char *oper); + + void *print_binary_operator_errors (const char *il_operator, symbol_c *symbol, bool deprecated_operation = false); + void *print_binary_expression_errors(const char *operation , symbol_c *symbol, symbol_c *l_expr, symbol_c *r_expr, bool deprecated_operation = false); + + + public: + print_datatypes_error_c(symbol_c *ignore); + virtual ~print_datatypes_error_c(void); + int get_error_count(); + + + /*********************/ + /* B 1.2 - Constants */ + /*********************/ + /******************************/ + /* B 1.2.1 - Numeric Literals */ + /******************************/ + void *visit(real_c *symbol); + void *visit(integer_c *symbol); + void *visit(neg_real_c *symbol); + void *visit(neg_integer_c *symbol); + void *visit(binary_integer_c *symbol); + void *visit(octal_integer_c *symbol); + void *visit(hex_integer_c *symbol); + void *visit(integer_literal_c *symbol); + void *visit(real_literal_c *symbol); + void *visit(bit_string_literal_c *symbol); + void *visit(boolean_literal_c *symbol); + void *visit(boolean_true_c *symbol); + void *visit(boolean_false_c *symbol); + + /*******************************/ + /* B.1.2.2 Character Strings */ + /*******************************/ + void *visit(double_byte_character_string_c *symbol); + void *visit(single_byte_character_string_c *symbol); + + /***************************/ + /* B 1.2.3 - Time Literals */ + /***************************/ + /************************/ + /* B 1.2.3.1 - Duration */ + /************************/ + void *visit(duration_c *symbol); + + /************************************/ + /* B 1.2.3.2 - Time of day and Date */ + /************************************/ + void *visit(time_of_day_c *symbol); + void *visit(date_c *symbol); + void *visit(date_and_time_c *symbol); + + /**********************/ + /* B 1.3 - Data types */ + /**********************/ + /********************************/ + /* B 1.3.3 - Derived data types */ + /********************************/ + void *visit(simple_spec_init_c *symbol); + void *visit(data_type_declaration_c *symbol); + void *visit(enumerated_value_c *symbol); + + /*********************/ + /* B 1.4 - Variables */ + /*********************/ + void *visit(symbolic_variable_c *symbol); + + /********************************************/ + /* B 1.4.1 - Directly Represented Variables */ + /********************************************/ + void *visit(direct_variable_c *symbol); + + /*************************************/ + /* B 1.4.2 - Multi-element variables */ + /*************************************/ + void *visit(array_variable_c *symbol); + void *visit(subscript_list_c *symbol); + void *visit(structured_variable_c *symbol); + + /******************************************/ + /* B 1.4.3 - Declaration & Initialisation */ + /******************************************/ + void *visit(location_c *symbol); + void *visit(located_var_decl_c *symbol); + + /**************************************/ + /* B 1.5 - Program organization units */ + /**************************************/ + /***********************/ + /* B 1.5.1 - Functions */ + /***********************/ + void *visit(function_declaration_c *symbol); + + /*****************************/ + /* B 1.5.2 - Function blocks */ + /*****************************/ + void *visit(function_block_declaration_c *symbol); + + /**********************/ + /* B 1.5.3 - Programs */ + /**********************/ + void *visit(program_declaration_c *symbol); + + /********************************/ + /* B 1.7 Configuration elements */ + /********************************/ + void *visit(configuration_declaration_c *symbol); + + /****************************************/ + /* B.2 - Language IL (Instruction List) */ + /****************************************/ + /***********************************/ + /* B 2.1 Instructions and Operands */ + /***********************************/ +// void *visit(instruction_list_c *symbol); + void *visit(il_instruction_c *symbol); + void *visit(il_simple_operation_c *symbol); + void *visit(il_function_call_c *symbol); + void *visit(il_expression_c *symbol); + void *visit(il_fb_call_c *symbol); + void *visit(il_formal_funct_call_c *symbol); +// void *visit(il_operand_list_c *symbol); +// void *visit(simple_instr_list_c *symbol); + void *visit(il_simple_instruction_c*symbol); +// void *visit(il_param_list_c *symbol); +// void *visit(il_param_assignment_c *symbol); +// void *visit(il_param_out_assignment_c *symbol); + + /*******************/ + /* B 2.2 Operators */ + /*******************/ + void *visit(LD_operator_c *symbol); + void *visit(LDN_operator_c *symbol); + void *visit(ST_operator_c *symbol); + void *visit(STN_operator_c *symbol); + void *visit(NOT_operator_c *symbol); + void *visit(S_operator_c *symbol); + void *visit(R_operator_c *symbol); + void *visit(S1_operator_c *symbol); + void *visit(R1_operator_c *symbol); + void *visit(CLK_operator_c *symbol); + void *visit(CU_operator_c *symbol); + void *visit(CD_operator_c *symbol); + void *visit(PV_operator_c *symbol); + void *visit(IN_operator_c *symbol); + void *visit(PT_operator_c *symbol); + void *visit(AND_operator_c *symbol); + void *visit(OR_operator_c *symbol); + void *visit(XOR_operator_c *symbol); + void *visit(ANDN_operator_c *symbol); + void *visit(ORN_operator_c *symbol); + void *visit(XORN_operator_c *symbol); + void *visit(ADD_operator_c *symbol); + void *visit(SUB_operator_c *symbol); + void *visit(MUL_operator_c *symbol); + void *visit(DIV_operator_c *symbol); + void *visit(MOD_operator_c *symbol); + void *visit(GT_operator_c *symbol); + void *visit(GE_operator_c *symbol); + void *visit(EQ_operator_c *symbol); + void *visit(LT_operator_c *symbol); + void *visit(LE_operator_c *symbol); + void *visit(NE_operator_c *symbol); + void *visit(CAL_operator_c *symbol); + void *visit(CALC_operator_c *symbol); + void *visit(CALCN_operator_c *symbol); + void *visit(RET_operator_c *symbol); + void *visit(RETC_operator_c *symbol); + void *visit(RETCN_operator_c *symbol); + void *visit(JMP_operator_c *symbol); + void *visit(JMPC_operator_c *symbol); + void *visit(JMPCN_operator_c *symbol); + /* Symbol class handled together with function call checks */ + // void *visit(il_assign_operator_c *symbol, variable_name); + /* Symbol class handled together with function call checks */ + // void *visit(il_assign_operator_c *symbol, option, variable_name); + + /***************************************/ + /* B.3 - Language ST (Structured Text) */ + /***************************************/ + /***********************/ + /* B 3.1 - Expressions */ + /***********************/ + void *visit(or_expression_c *symbol); + void *visit(xor_expression_c *symbol); + void *visit(and_expression_c *symbol); + void *visit(equ_expression_c *symbol); + void *visit(notequ_expression_c *symbol); + void *visit(lt_expression_c *symbol); + void *visit(gt_expression_c *symbol); + void *visit(le_expression_c *symbol); + void *visit(ge_expression_c *symbol); + void *visit(add_expression_c *symbol); + void *visit(sub_expression_c *symbol); + void *visit(mul_expression_c *symbol); + void *visit(div_expression_c *symbol); + void *visit(mod_expression_c *symbol); + void *visit(power_expression_c *symbol); + void *visit(neg_expression_c *symbol); + void *visit(not_expression_c *symbol); + void *visit(function_invocation_c *symbol); + + /*********************************/ + /* B 3.2.1 Assignment Statements */ + /*********************************/ + void *visit(assignment_statement_c *symbol); + + /*****************************************/ + /* B 3.2.2 Subprogram Control Statements */ + /*****************************************/ + void *visit(fb_invocation_c *symbol); + + /********************************/ + /* B 3.2.3 Selection Statements */ + /********************************/ + void *visit(if_statement_c *symbol); + // void *visit(elseif_statement_list_c *symbol); + void *visit(elseif_statement_c *symbol); + void *visit(case_statement_c *symbol); + // void *visit(case_element_list_c *symbol); + // void *visit(case_element_c *symbol); + // void *visit(case_list_c *symbol); + + /********************************/ + /* B 3.2.4 Iteration Statements */ + /********************************/ + void *visit(for_statement_c *symbol); + void *visit(while_statement_c *symbol); + void *visit(repeat_statement_c *symbol); + +}; // print_datatypes_error_c + + + + + + + diff -r aad38592bdde -r c0bda77b37a0 stage3/stage3.cc --- a/stage3/stage3.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage3/stage3.cc Wed Aug 22 16:46:17 2012 +0200 @@ -1,8 +1,10 @@ /* * matiec - a compiler for the programming languages defined in IEC 61131-3 * - * Copyright (C) 2009-2011 Mario de Sousa (msousa@fe.up.pt) + * Copyright (C) 2009-2012 Mario de Sousa (msousa@fe.up.pt) * Copyright (C) 2007-2011 Laurent Bessard and Edouard Tisserant + * Copyright (C) 2012 Manuele Conti (manuele.conti@sirius-es.it) + * Copyright (C) 2012 Matteo Facchinetti (matteo.facchinetti@sirius-es.it) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,17 +34,80 @@ #include "stage3.hh" -int type_safety(symbol_c *tree_root){ - visit_expression_type_c visit_expression_type(tree_root); +#include "flow_control_analysis.hh" +#include "fill_candidate_datatypes.hh" +#include "narrow_candidate_datatypes.hh" +#include "print_datatypes_error.hh" +#include "lvalue_check.hh" +#include "array_range_check.hh" +#include "constant_folding.hh" - (*tree_root).accept(visit_expression_type); - if (visit_expression_type.get_error_found()) - return -1; + +static int flow_control_analysis(symbol_c *tree_root){ + flow_control_analysis_c flow_control_analysis(tree_root); + tree_root->accept(flow_control_analysis); + return 0; +} + + +/* Constant folding assumes that flow control analysis has been completed! + * so be sure to call flow_control_analysis() before calling this function! + */ +static int constant_folding(symbol_c *tree_root){ + constant_folding_c constant_folding(tree_root); + tree_root->accept(constant_folding); + return constant_folding.get_error_count(); +} + + +/* Type safety analysis assumes that + * - flow control analysis + * - constant folding (constant check) + * has already been completed, so be sure to call those semantic checkers + * before calling this function + */ +static int type_safety(symbol_c *tree_root){ + fill_candidate_datatypes_c fill_candidate_datatypes(tree_root); + tree_root->accept(fill_candidate_datatypes); + narrow_candidate_datatypes_c narrow_candidate_datatypes(tree_root); + tree_root->accept(narrow_candidate_datatypes); + print_datatypes_error_c print_datatypes_error(tree_root); + tree_root->accept(print_datatypes_error); + return print_datatypes_error.get_error_count(); +} + + +/* Left value checking assumes that data type analysis has already been completed, + * so be sure to call type_safety() before calling this function + */ +static int lvalue_check(symbol_c *tree_root){ + lvalue_check_c lvalue_check(tree_root); + tree_root->accept(lvalue_check); + return lvalue_check.get_error_count(); +} + +/* Array range check assumes that constant folding has been completed! + * so be sure to call constant_folding() before calling this function! + */ +static int array_range_check(symbol_c *tree_root){ + array_range_check_c array_range_check(tree_root); + tree_root->accept(array_range_check); + return array_range_check.get_error_count(); +} + + +int stage3(symbol_c *tree_root){ + int error_count = 0; + error_count += flow_control_analysis(tree_root); + error_count += constant_folding(tree_root); + error_count += type_safety(tree_root); + error_count += lvalue_check(tree_root); + error_count += array_range_check(tree_root); + if (error_count > 0) { + fprintf(stderr, "%d error(s) found. Bailing out!\n", error_count); + return -1; + } return 0; } - -int stage3(symbol_c *tree_root){ - return type_safety(tree_root); -} diff -r aad38592bdde -r c0bda77b37a0 stage3/stage3.hh --- a/stage3/stage3.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/stage3/stage3.hh Wed Aug 22 16:46:17 2012 +0200 @@ -31,20 +31,8 @@ */ -// #include /* required for NULL */ -#include -#include -#include -#include -#include -#include - #include "../util/symtable.hh" -#include "../util/dsymtable.hh" -#include "../absyntax/visitor.hh" - -#include "visit_expression_type.hh" int stage3(symbol_c *tree_root); -int type_safety(symbol_c *tree_root); + diff -r aad38592bdde -r c0bda77b37a0 stage3/visit_expression_type.cc --- a/stage3/visit_expression_type.cc Tue Aug 14 19:40:01 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2456 +0,0 @@ -/* - * matiec - a compiler for the programming languages defined in IEC 61131-3 - * - * Copyright (C) 2009-2011 Mario de Sousa (msousa@fe.up.pt) - * Copyright (C) 2007-2011 Laurent Bessard and Edouard Tisserant - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - -/* - * An IEC 61131-3 compiler. - * - * Based on the - * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) - * - */ - - -/* Verify whether the semantic rules of data type compatibility are being followed. - * - * For example: - */ - -#include "visit_expression_type.hh" -#include -#include -#include -#include -#include - - -#define FIRST_(symbol1, symbol2) (((symbol1)->first_order < (symbol2)->first_order) ? (symbol1) : (symbol2)) -#define LAST_(symbol1, symbol2) (((symbol1)->last_order > (symbol2)->last_order) ? (symbol1) : (symbol2)) - -#define STAGE3_ERROR(symbol1, symbol2, ...) { \ - fprintf(stderr, "%s:%d-%d..%d-%d: error : ", \ - FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column, \ - LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "\n"); \ - il_error = true; \ - error_found = true; \ - } - - -/* set to 1 to see debug info during execution */ -static int debug = 0; - - -void *visit_expression_type_c::visit(program_declaration_c *symbol) { - search_varfb_instance_type = new search_varfb_instance_type_c(symbol); - symbol->var_declarations->accept(*this); - if (debug) printf("checking semantics in body of program %s\n", ((token_c *)(symbol->program_type_name))->value); - il_parenthesis_level = 0; - il_error = false; - il_default_variable_type = NULL; - symbol->function_block_body->accept(*this); - il_default_variable_type = NULL; - delete search_varfb_instance_type; - search_varfb_instance_type = NULL; - return NULL; -} - -void *visit_expression_type_c::visit(function_declaration_c *symbol) { - search_varfb_instance_type = new search_varfb_instance_type_c(symbol); - symbol->var_declarations_list->accept(*this); - if (debug) printf("checking semantics in body of function %s\n", ((token_c *)(symbol->derived_function_name))->value); - il_parenthesis_level = 0; - il_error = false; - il_default_variable_type = NULL; - symbol->function_body->accept(*this); - il_default_variable_type = NULL; - delete search_varfb_instance_type; - search_varfb_instance_type = NULL; - return NULL; -} - -void *visit_expression_type_c::visit(function_block_declaration_c *symbol) { - search_varfb_instance_type = new search_varfb_instance_type_c(symbol); - symbol->var_declarations->accept(*this); - if (debug) printf("checking semantics in body of FB %s\n", ((token_c *)(symbol->fblock_name))->value); - il_parenthesis_level = 0; - il_error = false; - il_default_variable_type = NULL; - symbol->fblock_body->accept(*this); - il_default_variable_type = NULL; - delete search_varfb_instance_type; - search_varfb_instance_type = NULL; - return NULL; -} - - - - - - - - - -visit_expression_type_c::visit_expression_type_c(symbol_c *ignore) { - error_found = false; -} - -visit_expression_type_c::~visit_expression_type_c(void) { -} - -bool visit_expression_type_c::get_error_found(void) { - return error_found; -} - - - -/* NOTE on data type handling and literals... - * ========================================== - * - * Literals that are explicitly type cast - * e.g.: BYTE#42 - * INT#65 - * TIME#45h23m - * etc... - * are NOT considered literals in the following code. - * Since they are type cast, and their data type is fixed and well known, - * they are treated as a variable of that data type (except when determining lvalues) - * In other words, when calling search_constant_type_c on these constants, it returns - * a xxxxx_type_name_c, and not one of the xxxx_literal_c ! - * - * When the following code handles a literal, it is really a literal of unknown data type. - * e.g. 42, may be considered an int, a byte, a word, etc... - * - * NOTE: type_symbol == NULL is valid! - * This will occur, for example, when and undefined/undeclared symbolic_variable is used in the program. - * This will not be of any type, so we always return false. - */ - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_ELEMENTARY_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - return is_ANY_MAGNITUDE_type(type_symbol) - || is_ANY_BIT_type (type_symbol) - || is_ANY_STRING_type (type_symbol) - || is_ANY_DATE_type (type_symbol); -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_SAFEELEMENTARY_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - return is_ANY_SAFEMAGNITUDE_type(type_symbol) - || is_ANY_SAFEBIT_type (type_symbol) - || is_ANY_SAFESTRING_type (type_symbol) - || is_ANY_SAFEDATE_type (type_symbol); -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_ELEMENTARY_compatible(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - /* NOTE: doing - * return is_ANY_SAFEELEMENTARY_type() || is_ANY_ELEMENTARY_type() - * is incorrect, as the literals would never be considered compatible... - */ - return is_ANY_MAGNITUDE_compatible(type_symbol) - || is_ANY_BIT_compatible (type_symbol) - || is_ANY_STRING_compatible (type_symbol) - || is_ANY_DATE_compatible (type_symbol); -} - - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_MAGNITUDE_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(time_type_name_c)) {return true;} - return is_ANY_NUM_type(type_symbol); -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_SAFEMAGNITUDE_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(safetime_type_name_c)) {return true;} - return is_ANY_SAFENUM_type(type_symbol); -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_MAGNITUDE_compatible(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (is_ANY_MAGNITUDE_type (type_symbol)) {return true;} - if (is_ANY_SAFEMAGNITUDE_type(type_symbol)) {return true;} - - return is_ANY_NUM_compatible(type_symbol); -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_NUM_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (is_ANY_REAL_type(type_symbol)) {return true;} - if (is_ANY_INT_type(type_symbol)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_SAFENUM_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - return is_ANY_SAFEREAL_type(type_symbol) - || is_ANY_SAFEINT_type (type_symbol); -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_NUM_compatible(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (is_ANY_REAL_compatible(type_symbol)) {return true;} - if (is_ANY_INT_compatible(type_symbol)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_DATE_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(date_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(tod_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(dt_type_name_c)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_SAFEDATE_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(safedate_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safetod_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safedt_type_name_c)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_DATE_compatible(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (is_ANY_DATE_type (type_symbol)) {return true;} - if (is_ANY_SAFEDATE_type(type_symbol)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_STRING_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(string_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(wstring_type_name_c)) {return true;} -// TODO literal_string ??? - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_SAFESTRING_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(safestring_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safewstring_type_name_c)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_STRING_compatible(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (is_ANY_STRING_type (type_symbol)) {return true;} - if (is_ANY_SAFESTRING_type(type_symbol)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_INT_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(sint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(int_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(dint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(lint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(usint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(uint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(udint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(ulint_type_name_c)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_SAFEINT_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(safesint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safeint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safedint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safelint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safeusint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safeuint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safeudint_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safeulint_type_name_c)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_INT_compatible(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (is_ANY_INT_type (type_symbol)) {return true;} - if (is_ANY_SAFEINT_type(type_symbol)) {return true;} - if (is_literal_integer_type(type_symbol)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_REAL_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(real_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(lreal_type_name_c)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_SAFEREAL_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(safereal_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safelreal_type_name_c)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_REAL_compatible(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (is_ANY_REAL_type (type_symbol)) {return true;} - if (is_ANY_SAFEREAL_type(type_symbol)) {return true;} - if (is_literal_real_type(type_symbol)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_BIT_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(bool_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(byte_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(word_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(dword_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(lword_type_name_c)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_SAFEBIT_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(safebool_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safebyte_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safeword_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safedword_type_name_c)) {return true;} - if (typeid(*type_symbol) == typeid(safelword_type_name_c)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_BIT_compatible(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (is_ANY_BIT_type (type_symbol)) {return true;} - if (is_ANY_SAFEBIT_type(type_symbol)) {return true;} - if (is_nonneg_literal_integer_type(type_symbol)) {return true;} - if (is_literal_bool_type(type_symbol)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_BOOL_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(bool_type_name_c)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_SAFEBOOL_type(symbol_c *type_symbol){ - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(safebool_type_name_c)) {return true;} - return false; -} - -/* A helper function... */ -bool visit_expression_type_c::is_ANY_BOOL_compatible(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (is_BOOL_type (type_symbol)) {return true;} - if (is_SAFEBOOL_type(type_symbol)) {return true;} - if (is_literal_bool_type(type_symbol)) {return true;} - return false; -} - - -#define is_type(type_name_symbol, type_name_class) ((type_name_symbol == NULL) ? false : (typeid(*type_name_symbol) == typeid(type_name_class))) - - -#define sizeoftype(symbol) get_sizeof_datatype_c::getsize(symbol) - - -/* A helper function... */ -bool visit_expression_type_c::is_literal_integer_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(neg_integer_c)) {return true;} - return is_nonneg_literal_integer_type(type_symbol); -} - - -/* A helper function... */ -bool visit_expression_type_c::is_nonneg_literal_integer_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(integer_c)) {return true;} - if (typeid(*type_symbol) == typeid(binary_integer_c)) {return true;} - if (typeid(*type_symbol) == typeid(octal_integer_c)) {return true;} - if (typeid(*type_symbol) == typeid(hex_integer_c)) {return true;} - return false; -} - - -/* A helper function... */ -bool visit_expression_type_c::is_literal_real_type(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(real_c)) {return true;} - if (typeid(*type_symbol) == typeid(neg_real_c)) {return true;} - return false; -} - - -/* A helper function... */ -bool visit_expression_type_c::is_literal_bool_type(symbol_c *type_symbol) { - bool_type_name_c bool_t; - - if (type_symbol == NULL) {return false;} - if (typeid(*type_symbol) == typeid(boolean_true_c)) {return true;} - if (typeid(*type_symbol) == typeid(boolean_false_c)) {return true;} - if (is_nonneg_literal_integer_type(type_symbol)) - if (sizeoftype(&bool_t) >= sizeoftype(type_symbol)) {return true;} - return false; -} - -bool visit_expression_type_c::is_ANY_ELEMENTARY_OR_ENUMERATED_compatible(symbol_c *type_symbol) { - if (type_symbol == NULL) {return false;} - if (search_base_type.type_is_enumerated(type_symbol)) {return true;} - return is_ANY_ELEMENTARY_compatible(type_symbol); -} - - -/* Determine the common data type between two data types. - * If no common data type found, return NULL. - * - * If data types are identical, return the first (actually any would do...). - * If any of the data types is a literal, we confirm that - * the literal uses less bits than the fixed size data type. - * e.g. BYTE and 1024 returns NULL - * BYTE and 255 returns BYTE - * - * If two literals, then return the literal that requires more bits... - */ - -symbol_c *visit_expression_type_c::common_type__(symbol_c *first_type, symbol_c *second_type) { - if (first_type == NULL && second_type == NULL) {return NULL;} - if (first_type == NULL) {return second_type;} - if (second_type == NULL) {return first_type;} - - if (is_literal_integer_type(first_type) && is_literal_integer_type(second_type)) - {return ((sizeoftype(first_type) > sizeoftype(second_type))? first_type:second_type);} - - if (is_literal_real_type(first_type) && is_literal_real_type(second_type)) - {return ((sizeoftype(first_type) > sizeoftype(second_type))? first_type:second_type);} - - if (is_literal_bool_type(first_type) && is_literal_bool_type(second_type)) - {return first_type;} - - /* The following check can only be made after the is_literal_XXXX checks */ - /* When two literals of the same type, with identical typeid's are checked, - * we must return the one that occupies more bits... This is done above. - */ - if (typeid(*first_type) == typeid(*second_type)) {return first_type;} - - /* NOTE Although a BOOL is also an ANY_BIT, we must check it explicitly since some - * literal bool values are not literal integers... - */ - if (is_BOOL_type(first_type) && is_literal_bool_type(second_type)) {return first_type;} - if (is_BOOL_type(second_type) && is_literal_bool_type(first_type)) {return second_type;} - - if (is_SAFEBOOL_type(first_type) && is_literal_bool_type(second_type)) {return first_type;} - if (is_SAFEBOOL_type(second_type) && is_literal_bool_type(first_type)) {return second_type;} - - if (is_SAFEBOOL_type(first_type) && is_BOOL_type(second_type)) {return second_type;} - if (is_SAFEBOOL_type(second_type) && is_BOOL_type(first_type)) {return first_type;} - - if (is_ANY_BIT_type(first_type) && is_nonneg_literal_integer_type(second_type)) - {return ((sizeoftype(first_type) >= sizeoftype(second_type))? first_type :NULL);} - if (is_ANY_BIT_type(second_type) && is_nonneg_literal_integer_type(first_type)) - {return ((sizeoftype(second_type) >= sizeoftype(first_type)) ? second_type:NULL);} - - if (is_ANY_SAFEBIT_type(first_type) && is_nonneg_literal_integer_type(second_type)) - {return ((sizeoftype(first_type) >= sizeoftype(second_type))? first_type :NULL);} - if (is_ANY_SAFEBIT_type(second_type) && is_nonneg_literal_integer_type(first_type)) - {return ((sizeoftype(second_type) >= sizeoftype(first_type)) ? second_type:NULL);} - - if (is_ANY_SAFEBIT_type(first_type) && is_ANY_BIT_type(second_type)) - {return ((sizeoftype(first_type) == sizeoftype(second_type))? second_type:NULL);} - if (is_ANY_SAFEBIT_type(second_type) && is_ANY_BIT_type(first_type)) - {return ((sizeoftype(first_type) == sizeoftype(second_type))? first_type :NULL);} - - if (is_ANY_INT_type(first_type) && is_literal_integer_type(second_type)) - {return ((sizeoftype(first_type) >= sizeoftype(second_type))? first_type :NULL);} - if (is_ANY_INT_type(second_type) && is_literal_integer_type(first_type)) - {return ((sizeoftype(second_type) >= sizeoftype(first_type)) ? second_type:NULL);} - - if (is_ANY_SAFEINT_type(first_type) && is_literal_integer_type(second_type)) - {return ((sizeoftype(first_type) >= sizeoftype(second_type))? first_type :NULL);} - if (is_ANY_SAFEINT_type(second_type) && is_literal_integer_type(first_type)) - {return ((sizeoftype(second_type) >= sizeoftype(first_type)) ? second_type:NULL);} - - if (is_ANY_SAFEINT_type(first_type) && is_ANY_INT_type(second_type)) - {return ((sizeoftype(first_type) == sizeoftype(second_type))? second_type:NULL);} - if (is_ANY_SAFEINT_type(second_type) && is_ANY_INT_type(first_type)) - {return ((sizeoftype(first_type) == sizeoftype(second_type))? first_type :NULL);} - - if (is_ANY_REAL_type(first_type) && is_literal_real_type(second_type)) - {return ((sizeoftype(first_type) >= sizeoftype(second_type))? first_type :NULL);} - if (is_ANY_REAL_type(second_type) && is_literal_real_type(first_type)) - {return ((sizeoftype(second_type) >= sizeoftype(first_type)) ? second_type:NULL);} - - if (is_ANY_SAFEREAL_type(first_type) && is_literal_real_type(second_type)) - {return ((sizeoftype(first_type) >= sizeoftype(second_type))? first_type :NULL);} - if (is_ANY_SAFEREAL_type(second_type) && is_literal_real_type(first_type)) - {return ((sizeoftype(second_type) >= sizeoftype(first_type)) ? second_type:NULL);} - - if (is_ANY_SAFEREAL_type(first_type) && is_ANY_REAL_type(second_type)) - {return ((sizeoftype(first_type) == sizeoftype(second_type))? second_type:NULL);} - if (is_ANY_SAFEREAL_type(second_type) && is_ANY_REAL_type(first_type)) - {return ((sizeoftype(first_type) == sizeoftype(second_type))? first_type :NULL);} - - /* the Time and Date types... */ - if (is_type(first_type, safetime_type_name_c) && is_type(second_type, time_type_name_c)) {return second_type;} - if (is_type(second_type, safetime_type_name_c) && is_type( first_type, time_type_name_c)) {return first_type;} - - if (is_type(first_type, safedate_type_name_c) && is_type(second_type, date_type_name_c)) {return second_type;} - if (is_type(second_type, safedate_type_name_c) && is_type( first_type, date_type_name_c)) {return first_type;} - - if (is_type(first_type, safedt_type_name_c) && is_type(second_type, dt_type_name_c)) {return second_type;} - if (is_type(second_type, safedt_type_name_c) && is_type( first_type, dt_type_name_c)) {return first_type;} - - if (is_type(first_type, safetod_type_name_c) && is_type(second_type, tod_type_name_c)) {return second_type;} - if (is_type(second_type, safetod_type_name_c) && is_type( first_type, tod_type_name_c)) {return first_type;} - - /* no common type */ - return NULL; -} - -/* Determine the common data type between two data types. - * Unlike the common_type__() function, we stop the compiler with an ERROR - * if no common data type is found. - */ -symbol_c *visit_expression_type_c::common_type(symbol_c *first_type, symbol_c *second_type) { -/* - symbol_c *res = common_type__(first_type, second_type); - if (NULL == res) ERROR; - return res; -*/ - return common_type__(first_type, second_type); -} - -symbol_c *visit_expression_type_c::common_literal(symbol_c *first_type, symbol_c *second_type) { - printf("common_literal: %d == %d, %d == %d, %d == %d\n", - (int)is_ANY_INT_compatible(first_type), - (int)is_ANY_INT_compatible(second_type), - (int)is_ANY_REAL_compatible(first_type), - (int)is_ANY_REAL_compatible(second_type), - (int)is_ANY_BIT_compatible(first_type), - (int)is_ANY_BIT_compatible(second_type)); - if ((is_ANY_INT_compatible(first_type) && is_ANY_INT_compatible(second_type)) || - (is_ANY_BIT_compatible(first_type) && is_ANY_BIT_compatible(second_type))) - return &search_constant_type_c::integer; - else if (is_ANY_REAL_compatible(first_type) && is_ANY_REAL_compatible(second_type)) - return &search_constant_type_c::real; - return NULL; -} - -symbol_c *visit_expression_type_c::overloaded_return_type(symbol_c *type) { - if (is_ANY_INT_compatible(type)) - return &search_constant_type_c::ulint_type_name; - else if (is_ANY_REAL_compatible(type)) - return &search_constant_type_c::lreal_type_name; - else if (is_ANY_BIT_compatible(type)) - return &search_constant_type_c::lword_type_name; - return NULL; -} - -/* Return TRUE if the second (value) data type may be assigned to a variable of the first (variable) data type - * such as: - * var_type value_type - * BOOL BYTE#7 -> returns false - * INT INT#7 -> returns true - * INT 7 -> returns true - * REAL 7.89 -> returns true - * REAL 7 -> returns true - * INT 7.89 -> returns false - * SAFEBOOL BOOL#1 -> returns false !!! - * etc... - * - * NOTE: It is assumed that the var_type is the data type of an lvalue - */ -bool visit_expression_type_c::is_valid_assignment(symbol_c *var_type, symbol_c *value_type) { - if (var_type == NULL) {/* STAGE3_ERROR(value_type, value_type, "Var_type == NULL"); */ return false;} - if (value_type == NULL) {/* STAGE3_ERROR(var_type, var_type, "Value_type == NULL"); */ return false;} - - symbol_c *common_type = common_type__(var_type, value_type); - if (NULL == common_type) - return false; - return (typeid(*var_type) == typeid(*common_type)); -} - - -/* Return TRUE if there is a common data type, otherwise return FALSE - * i.e., return TRUE if both data types may be used simultaneously in an expression - * such as: - * BOOL#0 AND BYTE#7 -> returns false - * 0 AND BYTE#7 -> returns true - * INT#10 AND INT#7 -> returns true - * INT#10 AND 7 -> returns true - * REAL#34.3 AND 7.89 -> returns true - * REAL#34.3 AND 7 -> returns true - * INT#10 AND 7.89 -> returns false - * SAFEBOOL#0 AND BOOL#1 -> returns true !!! - * etc... - */ -bool visit_expression_type_c::is_compatible_type(symbol_c *first_type, symbol_c *second_type) { - if (first_type == NULL || second_type == NULL) {return false;} - return (NULL != common_type__(first_type, second_type)); -} - - - - -/* A helper function... */ -/* -symbol_c *visit_expression_type_c::compute_boolean_expression(symbol_c *left_type, symbol_c *right_type, - is_data_type_t is_data_type) { -*/ -symbol_c *visit_expression_type_c::compute_expression(symbol_c *left_type, symbol_c *right_type, is_data_type_t is_data_type, - symbol_c *left_expr, symbol_c *right_expr) { - bool error = false; - - if (!(this->*is_data_type)(left_type)) { - if (debug) printf("visit_expression_type_c::compute_expression(): invalid left_type\n"); - if (left_expr != NULL) - STAGE3_ERROR(left_expr, left_expr, "Invalid data type of operand, or of data resulting from previous IL instructions."); - error = true; - } - if (!(this->*is_data_type)(right_type)) { - if (debug) printf("visit_expression_type_c::compute_expression(): invalid right_type\n"); - if (right_expr != NULL) - STAGE3_ERROR(right_expr, right_expr, "Invalid data type of operand."); - error = true; - } - if (!is_compatible_type(left_type, right_type)) { - if (debug) printf("visit_expression_type_c::compute_expression(): left_type & right_type are incompatible\n"); - if ((left_expr != NULL) && (right_expr != NULL)) - STAGE3_ERROR(left_expr, right_expr, "Type mismatch between operands."); - error = true; - } - - if (error) - return NULL; - else - return common_type(left_type, right_type); -} - - - - -/* A helper function... */ -/* check the semantics of a FB or Function non-formal call */ -/* e.g. foo(1, 2, 3, 4); */ -/* If error_count pointer is != NULL, we do not really print out the errors, - * but rather only count how many errors were found. - * This is used to support overloaded functions, where we have to check each possible - * function, one at a time, untill we find a function call without any errors. - */ -void visit_expression_type_c::check_nonformal_call(symbol_c *f_call, symbol_c *f_decl, bool use_il_defvar, int *error_count) { - symbol_c *call_param_value, *call_param_type, *param_type; - identifier_c *param_name; - function_param_iterator_c fp_iterator(f_decl); - function_call_param_iterator_c fcp_iterator(f_call); - int extensible_parameter_highest_index = -1; - - /* reset error counter */ - if (error_count != NULL) *error_count = 0; - /* if use_il_defvar, then the first parameter for the call comes from the il_default_variable */ - if (use_il_defvar) { - /* The first parameter of the function corresponds to the il_default_variable_type of the function call */ - do { - param_name = fp_iterator.next(); - if(param_name == NULL) break; - /* The EN and ENO parameters are default parameters. - * In the non-formal invocation of a function there can be no assignment of - * values to these parameters. Therefore, we ignore the parameters declared - * in the function. - */ - } while ((strcmp(param_name->value, "EN") == 0) || (strcmp(param_name->value, "ENO") == 0)); - /* If the function does not have any parameters (param_name == NULL) - * then we cannot compare its type with the il_default_variable_type. - * - * However, I (Mario) think this is invalid syntax, as it seems to me all functions must - * have at least one parameter. - * However, we will make this semantic verification consider it possible, as later - * versions of the standard may change that syntax. - * So, instead of generating a syntax error message, we simply check whether the call - * is passing any more parameters besides the default variable (the il default variable may be ignored - * in this case, and not consider it as being a parameter being passed to the function). - * If it does, then we have found a semantic error, otherwise the function call is - * correct, and we simply return. - */ - if(param_name == NULL) { - if (fcp_iterator.next_nf() != NULL) - STAGE3_ERROR(f_call, f_call, "Too many parameters in function/FB call."); - return; - } else { - /* param_name != NULL */ - param_type = fp_iterator.param_type(); - if(!is_valid_assignment(param_type, il_default_variable_type)) { - if (error_count != NULL) (*error_count)++; - else STAGE3_ERROR(f_call, f_call, "In function/FB call, first parameter has invalid data type."); - } - } - - /* the fisrt parameter (il_def_variable) is correct */ - if (extensible_parameter_highest_index < fp_iterator.extensible_param_index()) { - extensible_parameter_highest_index = fp_iterator.extensible_param_index(); - } - } // if (use_il_defvar) - - - - /* Iterating through the non-formal parameters of the function call */ - while((call_param_value = fcp_iterator.next_nf()) != NULL) { - /* Obtaining the type of the value being passed in the function call */ - call_param_type = base_type((symbol_c*)call_param_value->accept(*this)); - if (call_param_type == NULL) { - if (error_count != NULL) (*error_count)++; - /* the following error will usually occur when ST code uses an identifier, that could refer to an enumerated constant, - * but was not actually used as a constant in any definitions of an enumerated data type - */ - else STAGE3_ERROR(call_param_value, call_param_value, "Could not determine data type of value being passed in function/FB call."); - continue; - } - - /* Iterate to the next parameter of the function being called. - * Get the name of that parameter, and ignore if EN or ENO. - */ - do { - param_name = fp_iterator.next(); - /* If there is no other parameter declared, then we are passing too many parameters... */ - if(param_name == NULL) { - if (error_count != NULL) (*error_count)++; - /* Note: We don't want to print out the follwoing error message multiple times, so we return instead of continuing with 'break' */ - else STAGE3_ERROR(f_call, f_call, "Too many parameters in function/FB call."); return; - } - } while ((strcmp(param_name->value, "EN") == 0) || (strcmp(param_name->value, "ENO") == 0)); - - /* Get the parameter type */ - param_type = base_type(fp_iterator.param_type()); - /* If the declared parameter and the parameter from the function call do not have the same type */ - if(!is_valid_assignment(param_type, call_param_type)) { - if (error_count != NULL) (*error_count)++; - else STAGE3_ERROR(call_param_value, call_param_value, "Type mismatch in function/FB call parameter."); - } - - if (extensible_parameter_highest_index < fp_iterator.extensible_param_index()) { - extensible_parameter_highest_index = fp_iterator.extensible_param_index(); - } - } - - /* The function call may not have any errors! */ - /* In the case of a call to an extensible function, we store the highest index - * of the extensible parameters this particular call uses, in the symbol_c object - * of the function call itself! - * In calls to non-extensible functions, this value will be set to -1. - * This information is later used in stage4 to correctly generate the - * output code. - */ - int extensible_param_count = -1; - if (extensible_parameter_highest_index >=0) /* if call to extensible function */ - extensible_param_count = 1 + extensible_parameter_highest_index - fp_iterator.first_extensible_param_index(); - il_function_call_c *il_function_call = dynamic_cast(f_call); - function_invocation_c *function_invocation = dynamic_cast(f_call); - if (il_function_call != NULL) il_function_call ->extensible_param_count = extensible_param_count; - else if (function_invocation != NULL) function_invocation->extensible_param_count = extensible_param_count; - // else ERROR; /* this function is also called by Function Blocks, so this is not an error! */ -} - - -/* check semantics of FB call in the IL language using input operators */ -/* e.g. CU, CLK, IN, PT, SR, ... */ -void visit_expression_type_c::check_il_fbcall(symbol_c *il_operator, const char *il_operator_str) { - symbol_c *call_param_type = il_default_variable_type; - symbol_c *fb_decl = il_operand_type; - /* The following should never occur. The function block must be defined, - * and the FB type being called MUST be in the symtable... - * This was all already checked at stage 2! - */ - if (NULL == fb_decl) ERROR; - if (call_param_type == NULL) ERROR; - - /* We also create an identifier_c object, so we can later use it to find the equivalent FB parameter */ - /* Note however that this symbol does not have the correct location (file name and line numbers) - * so any error messages must use the il_operator symbol to generate the error location - */ - identifier_c call_param_name(il_operator_str); - - /* Obtaining the type of the value being passed in the function call */ - call_param_type = base_type(call_param_type); - if (call_param_type == NULL) STAGE3_ERROR(il_operator, il_operator, "Could not determine data type of value being passed in FB call."); - - /* Find the corresponding parameter of the function being called */ - function_param_iterator_c fp_iterator(fb_decl); - if(fp_iterator.search(&call_param_name) == NULL) { - STAGE3_ERROR(il_operand, il_operand, "Called FB does not have an input parameter named %s.", il_operator_str); - } else { - /* Get the parameter type */ - symbol_c *param_type = base_type(fp_iterator.param_type()); - /* If the declared parameter and the parameter from the function call have the same type */ - if(!is_valid_assignment(param_type, call_param_type)) STAGE3_ERROR(il_operator, il_operator, "Type mismatch in FB call parameter."); - } -} - - -/* A helper function... */ -/* check the semantics of a FB or Function formal call */ -/* e.g. foo(IN1 := 1, OUT1 =>x, EN := true); */ -/* If error_count pointer is != NULL, we do not really print out the errors, - * but rather only count how many errors were found. - * This is used to support overloaded functions, where we have to check each possible - * function, one at a time, untill we find a function call without any errors. - */ -void visit_expression_type_c::check_formal_call(symbol_c *f_call, symbol_c *f_decl, int *error_count) { - symbol_c *call_param_value, *call_param_type, *call_param_name, *param_type; - symbol_c *verify_duplicate_param; - identifier_c *param_name; - function_param_iterator_c fp_iterator(f_decl); - function_call_param_iterator_c fcp_iterator(f_call); - int extensible_parameter_highest_index = -1; - identifier_c *extensible_parameter_name; - - /* reset error counter */ - if (error_count != NULL) *error_count = 0; - - /* Iterating through the formal parameters of the function call */ - while((call_param_name = fcp_iterator.next_f()) != NULL) { - - /* Obtaining the value being passed in the function call */ - call_param_value = fcp_iterator.get_current_value(); - /* the following should never occur. If it does, then we have a bug in our code... */ - if (NULL == call_param_value) ERROR; - - /* Checking if there are duplicated parameter values */ - verify_duplicate_param = fcp_iterator.search_f(call_param_name); - if(verify_duplicate_param != call_param_value){ - if (error_count != NULL) (*error_count)++; - else STAGE3_ERROR(call_param_name, verify_duplicate_param, "Duplicated parameter values."); - } - - /* Obtaining the type of the value being passed in the function call */ - call_param_type = (symbol_c*)call_param_value->accept(*this); - if (call_param_type == NULL) { - if (error_count != NULL) (*error_count)++; - else STAGE3_ERROR(call_param_name, call_param_value, "Could not determine data type of value being passed in function/FB call."); - /* The data value being passed is possibly any enumerated type value. - * We do not yet handle semantic verification of enumerated types. - */ - ERROR; - } - call_param_type = base_type(call_param_type); - if (call_param_type == NULL) STAGE3_ERROR(call_param_name, call_param_value, "Could not determine data type of value being passed in function/FB call."); - - /* Find the corresponding parameter of the function being called */ - param_name = fp_iterator.search(call_param_name); - if(param_name == NULL) { - if (error_count != NULL) (*error_count)++; - else STAGE3_ERROR(call_param_name, call_param_name, "Invalid parameter in function/FB call."); - } else { - /* Get the parameter type */ - param_type = base_type(fp_iterator.param_type()); - /* If the declared parameter and the parameter from the function call have the same type */ - if(!is_valid_assignment(param_type, call_param_type)) { - if (error_count != NULL) (*error_count)++; - else STAGE3_ERROR(call_param_name, call_param_value, "Type mismatch function/FB call parameter."); - } - if (extensible_parameter_highest_index < fp_iterator.extensible_param_index()) { - extensible_parameter_highest_index = fp_iterator.extensible_param_index(); - extensible_parameter_name = param_name; - } - } - } - - /* In the case of a call to an extensible function, we store the highest index - * of the extensible parameters this particular call uses, in the symbol_c object - * of the function call itself! - * In calls to non-extensible functions, this value will be set to -1. - * This information is later used in stage4 to correctly generate the - * output code. - */ - int extensible_param_count = -1; - if (extensible_parameter_highest_index >=0) /* if call to extensible function */ - extensible_param_count = 1 + extensible_parameter_highest_index - fp_iterator.first_extensible_param_index(); - il_formal_funct_call_c *il_formal_funct_call = dynamic_cast(f_call); - function_invocation_c *function_invocation = dynamic_cast(f_call); - if (il_formal_funct_call != NULL) il_formal_funct_call->extensible_param_count = extensible_param_count; - else if (function_invocation != NULL) function_invocation->extensible_param_count = extensible_param_count; -// else ERROR; /* this function is also called by Function Blocks, so this is not an error! */ - - /* We have iterated through all the formal parameters of the function call, - * and everything seems fine. - * If the function being called in an extensible function, we now check - * whether the extensible paramters in the formal invocation do not skip - * any indexes... - * - * f(in1:=0, in2:=0, in4:=0) --> ERROR!! - */ - if (extensible_parameter_highest_index >=0) { /* if call to extensible function */ - for (int i=fp_iterator.first_extensible_param_index(); i < extensible_parameter_highest_index; i++) { - char tmp[256]; - if (snprintf(tmp, 256, "%s%d", extensible_parameter_name->value, i) >= 256) ERROR; - if (fcp_iterator.search_f(tmp) == NULL) { - /* error in invocation of extensible function */ - if (error_count != NULL) (*error_count)++; - else STAGE3_ERROR(f_call, f_call, "Missing extensible parameters in call to extensible function."); - } - } - } -} - - - - -/* a helper function... */ -symbol_c *visit_expression_type_c::base_type(symbol_c *symbol) { - /* NOTE: symbol == NULL is valid. It will occur when, for e.g., an undefined/undeclared symbolic_variable is used - * in the code. - */ - if (symbol == NULL) return NULL; - return (symbol_c *)symbol->accept(search_base_type); -} - - -/* a helper function... */ -void *visit_expression_type_c::verify_null(symbol_c *symbol){ - if(il_default_variable_type == NULL){ - STAGE3_ERROR(symbol, symbol, "Missing LD instruction (or equivalent) before this instruction."); - } - if(il_operand_type == NULL){ - STAGE3_ERROR(symbol, symbol, "This instruction requires an operand."); - } - return NULL; -} - - -/********************************/ -/* B 1.3.3 - Derived data types */ -/********************************/ -void *visit_expression_type_c::visit(data_type_declaration_c *symbol) { - // TODO !!! - /* for the moment we must return NULL so semantic analysis of remaining code is not interrupted! */ - return NULL; -} - - -/*********************/ -/* B 1.4 - Variables */ -/*********************/ - -void *visit_expression_type_c::visit(symbolic_variable_c *symbol) { - return search_varfb_instance_type->get_basetype_decl(symbol); -} - -/********************************************/ -/* B 1.4.1 - Directly Represented Variables */ -/********************************************/ -void *visit_expression_type_c::visit(direct_variable_c *symbol) { - switch (symbol->value[2]) { - case 'X': // bit - 1 bit - return (void *)&bool_type_name; - case 'B': // byte - 8 bits - return (void *)&byte_type_name; - case 'W': // word - 16 bits - return (void *)&word_type_name; - case 'D': // double word - 32 bits - return (void *)&dword_type_name; - case 'L': // long word - 64 bits - return (void *)&lword_type_name; - default: // if none of the above, then the empty string was used <=> boolean - return (void *)&bool_type_name; - } -} - -/*************************************/ -/* B 1.4.2 - Multi-element variables */ -/*************************************/ -void *visit_expression_type_c::visit(array_variable_c *symbol) { - return search_varfb_instance_type->get_basetype_decl(symbol); -} - -void *visit_expression_type_c::visit(structured_variable_c *symbol) { - return search_varfb_instance_type->get_basetype_decl(symbol); -} - - - -/********************************/ -/* B 1.7 Configuration elements */ -/********************************/ -void *visit_expression_type_c::visit(configuration_declaration_c *symbol) { - // TODO !!! - /* for the moment we must return NULL so semantic analysis of remaining code is not interrupted! */ - return NULL; -} - - -/****************************************/ -/* B.2 - Language IL (Instruction List) */ -/****************************************/ -/***********************************/ -/* B 2.1 Instructions and Operands */ -/***********************************/ -/*| instruction_list il_instruction */ -/* The visitor of the base class search_visitor_c will handle calling each instruction in the list. - * We do not need to do anything here... - */ -// void *visit_expression_type_c::visit(instruction_list_c *symbol) - -/* | label ':' [il_incomplete_instruction] eol_list */ -//SYM_REF2(il_instruction_c, label, il_instruction) -// void *visit_expression_type_c::visit(il_instruction_c *symbol); - - -/* | il_simple_operator [il_operand] */ -// SYM_REF2(il_simple_operation_c, il_simple_operator, il_operand) -void *visit_expression_type_c::visit(il_simple_operation_c *symbol) { - if (il_error) - return NULL; - - /* determine the data type of the operand */ - il_operand = symbol->il_operand; - if (symbol->il_operand != NULL){ - il_operand_type = base_type((symbol_c *)symbol->il_operand->accept(*this)); - } else { - il_operand_type = NULL; - } - /* recursive call to see whether data types are compatible */ - symbol->il_simple_operator->accept(*this); - - il_operand_type = NULL; - il_operand = NULL; - return NULL; -} - -// | function_name [il_operand_list] */ -//SYM_REF2(il_function_call_c, function_name, il_operand_list) -void *visit_expression_type_c::visit(il_function_call_c *symbol) { - if (il_error) - return NULL; - - symbol_c *return_data_type = NULL; - symbol_c* fdecl_return_type; - symbol_c* overloaded_data_type = NULL; - int extensible_param_count = -1; - symbol->called_function_declaration = NULL; - - /* First find the declaration of the function being called! */ - function_symtable_t::iterator lower = function_symtable.lower_bound(symbol->function_name); - function_symtable_t::iterator upper = function_symtable.upper_bound(symbol->function_name); - function_symtable_t::iterator current; - if (lower == function_symtable.end()) ERROR; - - int error_count = 0; - int *error_count_ptr = NULL; - - function_symtable_t::iterator second = lower; - second++; - if (second != upper) - /* This is a call to an overloaded function... */ - error_count_ptr = &error_count; - - for(current = lower; current != upper; current++) { - function_declaration_c *f_decl = function_symtable.get_value(current); - - check_nonformal_call(symbol, f_decl, true, error_count_ptr); - - if (0 == error_count) { - /* Either: - * (i) we have a call to a non-overloaded function (error_cnt_ptr is NULL!, so error_count won't change!) - * (ii) we have a call to an overloaded function, with no errors! - */ - - fdecl_return_type = base_type(f_decl->type_name); - - if (symbol->called_function_declaration == NULL) { - /* Store the pointer to the declaration of the function being called. - * This data will be used by stage 4 to call the correct function. - * Mostly needed to disambiguate overloaded functions... - * See comments in absyntax.def for more details - */ - symbol->called_function_declaration = f_decl; - extensible_param_count = symbol->extensible_param_count; - - /* determine the base data type returned by the function being called... */ - return_data_type = fdecl_return_type; - } - else if (typeid(*return_data_type) != typeid(*fdecl_return_type)){ - return_data_type = common_literal(return_data_type, fdecl_return_type); - overloaded_data_type = overloaded_return_type(return_data_type); - } - - if (NULL == return_data_type) ERROR; - } - } - - if (overloaded_data_type != NULL) { - for(current = lower; current != upper; current++) { - function_declaration_c *f_decl = function_symtable.get_value(current); - - /* check semantics of data passed in the function call... */ - check_nonformal_call(symbol, f_decl, true, error_count_ptr); - - if (0 == error_count) { - - fdecl_return_type = base_type(f_decl->type_name); - - if (typeid(*overloaded_data_type) == typeid(*fdecl_return_type)){ - /* Store the pointer to the declaration of the function being called. - * This data will be used by stage 4 to call the correct function. - * Mostly needed to disambiguate overloaded functions... - * See comments in absyntax.def for more details - */ - symbol->called_function_declaration = f_decl; - extensible_param_count = symbol->extensible_param_count; - } - } - } - } - - if (NULL == return_data_type) { - /* No compatible function was found for this function call */ - STAGE3_ERROR(symbol, symbol, "Call to an overloaded function with invalid parameter type."); - } - else { - symbol->extensible_param_count = extensible_param_count; - /* set the new data type of the default variable for the following verifications... */ - il_default_variable_type = return_data_type; - } - - return NULL; -} - - -/* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ -// SYM_REF3(il_expression_c, il_expr_operator, il_operand, simple_instr_list); -void *visit_expression_type_c::visit(il_expression_c *symbol) { - if (il_error) - return NULL; - - symbol_c *il_default_variable_type_back = il_default_variable_type; - - il_parenthesis_level++; - - if(symbol->il_operand != NULL) { - il_default_variable_type = base_type((symbol_c *)symbol->il_operand->accept(*this)); - } else { - il_default_variable_type = NULL; - } - - if(symbol->simple_instr_list != NULL) { - symbol->simple_instr_list->accept(*this); - } - - il_parenthesis_level--; - if (il_parenthesis_level < 0) ERROR; - - il_operand = symbol->simple_instr_list; - il_operand_type = il_default_variable_type; - il_default_variable_type = il_default_variable_type_back; - - /* Now check the if the data type semantics of operation are correct, - * but only if no previous error has been found... - */ - if (!il_error) - symbol->il_expr_operator->accept(*this); - - il_operand_type = NULL; - il_operand = NULL; - return NULL; -} - - -#if 0 -/* il_jump_operator label */ -SYM_REF2(il_jump_operation_c, il_jump_operator, label) -void *visit_expression_type_c::visit(il_jump_operation_c *symbol); -#endif - - -/* il_call_operator prev_declared_fb_name - * | il_call_operator prev_declared_fb_name '(' ')' - * | il_call_operator prev_declared_fb_name '(' eol_list ')' - * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' - * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' - */ -/* SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list) */ -void *visit_expression_type_c::visit(il_fb_call_c *symbol) { - if (il_error) - return NULL; - - /* first check whether the il_default_variable is of the correct type - * for the CAL / CALC / CALCN operator being used... - */ - symbol->il_call_operator->accept(*this); - - /* Now check the FB call itself... */ - - /* First we find the declaration of the FB type of the FB instance being called... */ - /* e.g. Function_block foo_fb_type - * ... - * End_Function_Block - * - * Program test - * var fb1 : foo_fb_type; end_var - * fb1(...) - * End_Program - * - * search_varfb_instance_type->get_basetype_decl( identifier_c("fb1") ) - * in the scope of Program 'test' - * will return the fb declaration of foo_fb_type !! - */ -#if 0 - symbol_c *fb_decl_symbol = search_varfb_instance_type->get_basetype_decl(symbol->fb_name); - /* The following should never occur. The function block must be defined, - * and the FB type being called MUST be in the symtable... - * This was all already checked at stage 2! - */ - if (NULL == fb_decl_symbol) ERROR; - - function_block_declaration_c *fb_decl = dynamic_cast(fb_decl_symbol); - /* should never occur. ... */ - if (NULL == fb_decl) ERROR; -#endif - symbol_c *fb_decl = search_varfb_instance_type->get_basetype_decl(symbol->fb_name); - /* The following should never occur. The function block must be defined, - * and the FB type being called MUST be in the symtable... - * This was all already checked at stage 2! - */ - if (NULL == fb_decl) ERROR; - - /* now check the semantics of the fb call... */ - /* If the syntax parser is working correctly, exactly one of the - * following two symbols will be NULL, while the other is != NULL. - */ - if (NULL != symbol->il_operand_list) check_nonformal_call(symbol, fb_decl); - if (NULL != symbol->il_param_list) check_formal_call (symbol, fb_decl); - - return NULL; -} - - - -/* | function_name '(' eol_list [il_param_list] ')' */ -/* SYM_REF2(il_formal_funct_call_c, function_name, il_param_list) */ -void *visit_expression_type_c::visit(il_formal_funct_call_c *symbol) { - if (il_error) - return NULL; - - symbol_c *return_data_type = NULL; - symbol_c* fdecl_return_type; - symbol_c *overloaded_data_type = NULL; - int extensible_param_count = -1; - symbol->called_function_declaration = NULL; - - function_symtable_t::iterator lower = function_symtable.lower_bound(symbol->function_name); - function_symtable_t::iterator upper = function_symtable.upper_bound(symbol->function_name); - function_symtable_t::iterator current; - - if (lower == function_symtable.end()) { - function_type_t current_function_type = get_function_type((identifier_c *)symbol->function_name); - if (current_function_type == function_none) ERROR; - return NULL; - } - - int error_count = 0; - int *error_count_ptr = NULL; - - function_symtable_t::iterator second = lower; - second++; - if (second != upper) - /* This is a call to an overloaded function... */ - error_count_ptr = &error_count; - - for(current = lower; current != upper; current++) { - function_declaration_c *f_decl = function_symtable.get_value(current); - - /* check semantics of data passed in the function call... */ - check_formal_call(symbol, f_decl, error_count_ptr); - - if (0 == error_count) { - /* Either: - * (i) we have a call to a non-overloaded function (error_cnt_ptr is NULL!, so error_count won't change!) - * (ii) we have a call to an overloaded function, with no errors! - */ - - fdecl_return_type = base_type(f_decl->type_name); - - if (symbol->called_function_declaration == NULL) { - /* Store the pointer to the declaration of the function being called. - * This data will be used by stage 4 to call the correct function. - * Mostly needed to disambiguate overloaded functions... - * See comments in absyntax.def for more details - */ - symbol->called_function_declaration = f_decl; - extensible_param_count = symbol->extensible_param_count; - - /* determine the base data type returned by the function being called... */ - return_data_type = fdecl_return_type; - } - else if (typeid(*return_data_type) != typeid(*fdecl_return_type)){ - return_data_type = common_literal(return_data_type, fdecl_return_type); - overloaded_data_type = overloaded_return_type(return_data_type); - } - - /* the following should never occur. If it does, then we have a bug in the syntax parser (stage 2)... */ - if (NULL == return_data_type) ERROR; - - } - } - - if (overloaded_data_type != NULL) { - for(current = lower; current != upper; current++) { - function_declaration_c *f_decl = function_symtable.get_value(current); - - /* check semantics of data passed in the function call... */ - check_formal_call(symbol, f_decl, error_count_ptr); - - if (0 == error_count) { - - fdecl_return_type = base_type(f_decl->type_name); - - if (typeid(*overloaded_data_type) == typeid(*fdecl_return_type)){ - /* Store the pointer to the declaration of the function being called. - * This data will be used by stage 4 to call the correct function. - * Mostly needed to disambiguate overloaded functions... - * See comments in absyntax.def for more details - */ - symbol->called_function_declaration = f_decl; - extensible_param_count = symbol->extensible_param_count; - } - } - } - } - - if (NULL == return_data_type) { - /* No compatible function was found for this function call */ - STAGE3_ERROR(symbol, symbol, "Call to an overloaded function with invalid parameter type."); - } - else { - symbol->extensible_param_count = extensible_param_count; - /* the data type of the data returned by the function, and stored in the il default variable... */ - il_default_variable_type = return_data_type; - } - - return NULL; -} - - -#if 0 -/* | il_operand_list ',' il_operand */ -SYM_LIST(il_operand_list_c) -void *visit_expression_type_c::visit(il_operand_list_c *symbol); - -/* | simple_instr_list il_simple_instruction */ -SYM_LIST(simple_instr_list_c) -void *visit_expression_type_c::visit(simple_instr_list_c *symbol); - -/* | il_initial_param_list il_param_instruction */ -SYM_LIST(il_param_list_c) -void *visit_expression_type_c::visit(il_param_list_c *symbol); - -/* il_assign_operator il_operand - * | il_assign_operator '(' eol_list simple_instr_list ')' - */ -SYM_REF3(il_param_assignment_c, il_assign_operator, il_operand, simple_instr_list) -void *visit_expression_type_c::visit(il_param_assignment_c *symbol); -/* il_assign_out_operator variable */ -SYM_REF2(il_param_out_assignment_c, il_assign_out_operator, variable) -void *visit_expression_type_c::visit(il_param_out_assignment_c *symbol); - -#endif - - -/*******************/ -/* B 2.2 Operators */ -/*******************/ - -//SYM_REF0(LD_operator_c) -void *visit_expression_type_c::visit(LD_operator_c *symbol) { - if (0 == il_parenthesis_level) - il_error = false; - - if(il_operand_type == NULL) - STAGE3_ERROR(symbol, symbol, "LD operator requires an operand."); - il_default_variable_type = il_operand_type; - return NULL; -} - -// SYM_REF0(LDN_operator_c) -void *visit_expression_type_c::visit(LDN_operator_c *symbol) { - if(il_operand_type == NULL) - STAGE3_ERROR(symbol, symbol, "LDN operator requires an operand."); - if(!is_ANY_BIT_compatible(il_operand_type)) - STAGE3_ERROR(symbol, il_operand, "invalid data type of LDN operand, should be of type ANY_BIT."); - il_default_variable_type = il_operand_type; - return NULL; -} - -// SYM_REF0(ST_operator_c) -void *visit_expression_type_c::visit(ST_operator_c *symbol) { - verify_null(symbol); - - if(!is_valid_assignment(il_operand_type, il_default_variable_type)) - STAGE3_ERROR(symbol, symbol, "Type mismatch in ST operation."); - /* TODO: check whether il_operand_type is an LVALUE !! */ - /* data type of il_default_variable_type is unchanged... */ - // il_default_variable_type = il_default_variable_type; - return NULL; -} - -// SYM_REF0(STN_operator_c) - void *visit_expression_type_c::visit(STN_operator_c *symbol) { - verify_null(symbol); - if(!is_valid_assignment(il_operand_type, il_default_variable_type)) - STAGE3_ERROR(symbol, symbol, "Type mismatch in ST operation."); - /* TODO: check whether il_operand_type is an LVALUE !! */ - if(!is_ANY_BIT_compatible(il_default_variable_type)) - STAGE3_ERROR(symbol, symbol, "invalid data type of il_default_variable for STN operand, should be of type ANY_BIT."); - if(!is_ANY_BIT_compatible(il_operand_type)) - STAGE3_ERROR(symbol, il_operand, "invalid data type of STN operand, should be of type ANY_BIT."); - /* data type of il_default_variable_type is unchanged... */ - // il_default_variable_type = il_default_variable_type; - return NULL; -} - -//SYM_REF0(NOT_operator_c) -void *visit_expression_type_c::visit(NOT_operator_c *symbol) { - if(il_operand_type != NULL){ - STAGE3_ERROR(symbol, il_operand, "NOT operator may not have an operand."); - return NULL; - } - if(il_default_variable_type == NULL) { - STAGE3_ERROR(symbol, symbol, "Il default variable should not be NULL."); - return NULL; - } - if(!is_ANY_BIT_compatible(il_default_variable_type)) { - STAGE3_ERROR(symbol, symbol, "Il default variable should be of type ANY_BIT."); - return NULL; - } - /* data type of il_default_variable_type is unchanged... */ - // il_default_variable_type = il_default_variable_type; - return NULL; -} - -// SYM_REF0(S_operator_c) -void *visit_expression_type_c::visit(S_operator_c *symbol) { - verify_null(symbol); - if (!is_BOOL_type(il_default_variable_type)) {STAGE3_ERROR(symbol, symbol, "IL default variable should be BOOL type.");} - if (!is_BOOL_type(il_operand_type)) {STAGE3_ERROR(symbol, il_operand, "operator S requires operand of type BOOL.");} - /* TODO: check whether il_operand_type is an LVALUE !! */ - /* data type of il_default_variable_type is unchanged... */ - // il_default_variable_type = il_default_variable_type; - return NULL; -} - -// SYM_REF0(R_operator_c) -void *visit_expression_type_c::visit(R_operator_c *symbol) { - verify_null(symbol); - if (!is_BOOL_type(il_default_variable_type)) {STAGE3_ERROR(symbol, symbol, "IL default variable should be BOOL type.");} - if (!is_BOOL_type(il_operand_type)) {STAGE3_ERROR(symbol, il_operand, "operator R requires operand of type BOOL.");} - /* TODO: check whether il_operand_type is an LVALUE !! */ - /* data type of il_default_variable_type is unchanged... */ - // il_default_variable_type = il_default_variable_type; - return NULL; -} - - -// SYM_REF0(S1_operator_c) -void *visit_expression_type_c::visit(S1_operator_c *symbol){ - check_il_fbcall(symbol, "S1"); - return NULL; -} - -// SYM_REF0(R1_operator_c) -void *visit_expression_type_c::visit(R1_operator_c *symbol) { - check_il_fbcall(symbol, "R1"); - return NULL; -} - -// SYM_REF0(CLK_operator_c) -void *visit_expression_type_c::visit(CLK_operator_c *symbol) { - check_il_fbcall(symbol, "CLK"); - return NULL; -} - -// SYM_REF0(CU_operator_c) -void *visit_expression_type_c::visit(CU_operator_c *symbol) { - check_il_fbcall(symbol, "CU"); - return NULL; -} - -// SYM_REF0(CD_operator_c) -void *visit_expression_type_c::visit(CD_operator_c *symbol) { - check_il_fbcall(symbol, "CD"); - return NULL; -} - -// SYM_REF0(PV_operator_c) -void *visit_expression_type_c::visit(PV_operator_c *symbol) { - check_il_fbcall(symbol, "PV"); - return NULL; -} - -// SYM_REF0(IN_operator_c) -void *visit_expression_type_c::visit(IN_operator_c *symbol) { - check_il_fbcall(symbol, "IN"); - return NULL; -} - -// SYM_REF0(PT_operator_c) -void *visit_expression_type_c::visit(PT_operator_c *symbol) { - check_il_fbcall(symbol, "PT"); - return NULL; -} - -//SYM_REF0(AND_operator_c) -void *visit_expression_type_c::visit(AND_operator_c *symbol) { - verify_null(symbol); - il_default_variable_type = compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_BIT_compatible, - symbol , il_operand); - return NULL; -} - -//SYM_REF0(OR_operator_c) -void *visit_expression_type_c::visit(OR_operator_c *symbol) { - verify_null(symbol); - il_default_variable_type = compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_BIT_compatible, - symbol , il_operand); - return NULL; -} - -//SYM_REF0(XOR_operator_c) -void *visit_expression_type_c::visit(XOR_operator_c *symbol) { - verify_null(symbol); - il_default_variable_type = compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_BIT_compatible, - symbol , il_operand); - return NULL; -} - -// SYM_REF0(ANDN_operator_c) -void *visit_expression_type_c::visit(ANDN_operator_c *symbol) { - verify_null(symbol); - il_default_variable_type = compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_BIT_compatible, - symbol , il_operand); - return NULL; -} - -// SYM_REF0(ORN_operator_c) -void *visit_expression_type_c::visit(ORN_operator_c *symbol) { - verify_null(symbol); - il_default_variable_type = compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_BIT_compatible, - symbol , il_operand); - return NULL; -} - -// SYM_REF0(XORN_operator_c) -void *visit_expression_type_c::visit(XORN_operator_c *symbol) { - verify_null(symbol); - il_default_variable_type = compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_BIT_compatible, - symbol , il_operand); - return NULL; -} - -// SYM_REF0(ADD_operator_c) -void *visit_expression_type_c::visit(ADD_operator_c *symbol) { - verify_null(symbol); - symbol_c *left_type = il_default_variable_type; - symbol_c *right_type = il_operand_type; - -/* The following is not required, it is already handled by compute_expression() ... */ -/* - if (is_type(left_type, time_type_name_c) && is_type(right_type, time_type_name_c)) - il_default_variable_type = &time_type_name; -*/ - - if (is_type(left_type, tod_type_name_c) && is_type(right_type, time_type_name_c)) - il_default_variable_type = &tod_type_name; - else if (is_type(left_type, safetod_type_name_c) && is_type(right_type, time_type_name_c)) - il_default_variable_type = &tod_type_name; - else if (is_type(left_type, tod_type_name_c) && is_type(right_type, safetime_type_name_c)) - il_default_variable_type = &tod_type_name; - else if (is_type(left_type, safetod_type_name_c) && is_type(right_type, safetime_type_name_c)) - il_default_variable_type = &safetod_type_name; - - else if (is_type(left_type, dt_type_name_c) && is_type(right_type, time_type_name_c)) - il_default_variable_type = &dt_type_name; - else if (is_type(left_type, safedt_type_name_c) && is_type(right_type, time_type_name_c)) - il_default_variable_type = &dt_type_name; - else if (is_type(left_type, dt_type_name_c) && is_type(right_type, safetime_type_name_c)) - il_default_variable_type = &dt_type_name; - else if (is_type(left_type, safedt_type_name_c) && is_type(right_type, safetime_type_name_c)) - il_default_variable_type = &safedt_type_name; - - else il_default_variable_type = compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_MAGNITUDE_compatible, - symbol , il_operand); - return NULL; -} - -// SYM_REF0(SUB_operator_c) -void *visit_expression_type_c::visit(SUB_operator_c *symbol) { - verify_null(symbol); - symbol_c *left_type = il_default_variable_type; - symbol_c *right_type = il_operand_type;; - -/* The following is not required, it is already handled by compute_expression() ... */ -/* - if (typeid(*left_type) == typeid(time_type_name_c) && typeid(*right_type) == typeid(time_type_name_c)) - il_default_variable_type = &time_type_name; -*/ - - if (is_type(left_type, tod_type_name_c) && is_type(right_type, time_type_name_c)) - il_default_variable_type = &tod_type_name; - else if (is_type(left_type, safetod_type_name_c) && is_type(right_type, time_type_name_c)) - il_default_variable_type = &tod_type_name; - else if (is_type(left_type, tod_type_name_c) && is_type(right_type, safetime_type_name_c)) - il_default_variable_type = &tod_type_name; - else if (is_type(left_type, safetod_type_name_c) && is_type(right_type, safetime_type_name_c)) - il_default_variable_type = &safetod_type_name; - - else if (is_type(left_type, dt_type_name_c) && is_type(right_type, time_type_name_c)) - il_default_variable_type = &dt_type_name; - else if (is_type(left_type, safedt_type_name_c) && is_type(right_type, time_type_name_c)) - il_default_variable_type = &dt_type_name; - else if (is_type(left_type, dt_type_name_c) && is_type(right_type, safetime_type_name_c)) - il_default_variable_type = &dt_type_name; - else if (is_type(left_type, safedt_type_name_c) && is_type(right_type, safetime_type_name_c)) - il_default_variable_type = &safedt_type_name; - - else if (is_type(left_type, date_type_name_c) && is_type(right_type, date_type_name_c)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, safedate_type_name_c) && is_type(right_type, date_type_name_c)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, date_type_name_c) && is_type(right_type, safedate_type_name_c)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, safedate_type_name_c) && is_type(right_type, safedate_type_name_c)) - il_default_variable_type = &safetime_type_name; - - else if (is_type(left_type, tod_type_name_c) && is_type(right_type, tod_type_name_c)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, safetod_type_name_c) && is_type(right_type, tod_type_name_c)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, tod_type_name_c) && is_type(right_type, safetod_type_name_c)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, safetod_type_name_c) && is_type(right_type, safetod_type_name_c)) - il_default_variable_type = &safetime_type_name; - - else if (is_type(left_type, dt_type_name_c) && is_type(right_type, dt_type_name_c)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, safedt_type_name_c) && is_type(right_type, dt_type_name_c)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, dt_type_name_c) && is_type(right_type, safedt_type_name_c)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, safedt_type_name_c) && is_type(right_type, safedt_type_name_c)) - il_default_variable_type = &safetime_type_name; - - else il_default_variable_type = compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_MAGNITUDE_compatible, - symbol , il_operand); - return NULL; -} - -// SYM_REF0(MUL_operator_c) -void *visit_expression_type_c::visit(MUL_operator_c *symbol) { - verify_null(symbol); - symbol_c *left_type = il_default_variable_type; - symbol_c *right_type = il_operand_type; - - if (is_type(left_type, time_type_name_c) && is_ANY_NUM_compatible(right_type)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, safetime_type_name_c) && is_ANY_NUM_type(right_type)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, safetime_type_name_c) && is_ANY_SAFENUM_type(right_type)) - il_default_variable_type = &safetime_type_name; - /* Since we have already checked for ANY_NUM_type and ANY_SAFENUM_type in the previous lines, - * this next line is really only to check for integers/reals of undefined type on 'right_type'... - */ - else if (is_type(left_type, safetime_type_name_c) && is_ANY_NUM_compatible(right_type)) - il_default_variable_type = &safetime_type_name; - - else il_default_variable_type = compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_NUM_compatible, - symbol , il_operand); - return NULL; -} - -// SYM_REF0(DIV_operator_c) -void *visit_expression_type_c::visit(DIV_operator_c *symbol) { - verify_null(symbol); - symbol_c *left_type = il_default_variable_type; - symbol_c *right_type = il_operand_type; - - if (is_type(left_type, time_type_name_c) && is_ANY_NUM_compatible(right_type)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, safetime_type_name_c) && is_ANY_NUM_type(right_type)) - il_default_variable_type = &time_type_name; - else if (is_type(left_type, safetime_type_name_c) && is_ANY_SAFENUM_type(right_type)) - il_default_variable_type = &safetime_type_name; - /* Since we have already checked for ANY_NUM_type and ANY_SAFENUM_type in the previous lines, - * this next line is really only to check for integers/reals of undefined type on 'right_type'... - */ - else if (is_type(left_type, safetime_type_name_c) && is_ANY_NUM_compatible(right_type)) - il_default_variable_type = &safetime_type_name; - - else il_default_variable_type = compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_NUM_compatible, - symbol , il_operand); - return NULL; -} - -// SYM_REF0(MOD_operator_c) -void *visit_expression_type_c::visit(MOD_operator_c *symbol) { - verify_null(symbol); - il_default_variable_type = compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_INT_compatible, - symbol , il_operand); - return NULL; -} - -// SYM_REF0(GT_operator_c) -void *visit_expression_type_c::visit(GT_operator_c *symbol) { - verify_null(symbol); - compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_ELEMENTARY_compatible, - symbol , il_operand); - il_default_variable_type = &search_expression_type_c::bool_type_name; - return NULL; -} - -//SYM_REF0(GE_operator_c) -void *visit_expression_type_c::visit(GE_operator_c *symbol) { - verify_null(symbol); - compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_ELEMENTARY_compatible, - symbol , il_operand); - il_default_variable_type = &search_expression_type_c::bool_type_name; - return NULL; -} - -//SYM_REF0(EQ_operator_c) -void *visit_expression_type_c::visit(EQ_operator_c *symbol) { - verify_null(symbol); - compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_ELEMENTARY_compatible, - symbol , il_operand); - il_default_variable_type = &search_expression_type_c::bool_type_name; - return NULL; -} - -//SYM_REF0(LT_operator_c) -void *visit_expression_type_c::visit(LT_operator_c *symbol) { - verify_null(symbol); - compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_ELEMENTARY_compatible, - symbol , il_operand); - il_default_variable_type = &search_expression_type_c::bool_type_name; - return NULL; -} - -//SYM_REF0(LE_operator_c) -void *visit_expression_type_c::visit(LE_operator_c *symbol) { - verify_null(symbol); - compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_ELEMENTARY_compatible, - symbol , il_operand); - il_default_variable_type = &search_expression_type_c::bool_type_name; - return NULL; -} - -//SYM_REF0(NE_operator_c) -void *visit_expression_type_c::visit(NE_operator_c *symbol) { - verify_null(symbol); - compute_expression(il_default_variable_type, il_operand_type, &visit_expression_type_c::is_ANY_ELEMENTARY_compatible, - symbol , il_operand); - il_default_variable_type = &search_expression_type_c::bool_type_name; - return NULL; -} - -// SYM_REF0(CAL_operator_c) -void *visit_expression_type_c::visit(CAL_operator_c *symbol) { - return NULL; -} - -// SYM_REF0(CALC_operator_c) -void *visit_expression_type_c::visit(CALC_operator_c *symbol) { - if(il_default_variable_type == NULL) - STAGE3_ERROR(symbol, symbol, "CALC: il default variable should not be NULL."); - if (!is_BOOL_type(il_default_variable_type)) - STAGE3_ERROR(symbol, symbol, "CALC operator requires il_default_variable to be of type BOOL."); - return NULL; -} - -// SYM_REF0(CALCN_operator_c) -void *visit_expression_type_c::visit(CALCN_operator_c *symbol) { - if(il_default_variable_type == NULL) - STAGE3_ERROR(symbol, symbol, "CALCN: il_default_variable should not be NULL."); - if (!is_BOOL_type(il_default_variable_type)) - STAGE3_ERROR(symbol, symbol, "CALCN operator requires il_default_variable to be of type BOOL."); - return NULL; -} - -// SYM_REF0(RET_operator_c) -void *visit_expression_type_c::visit(RET_operator_c *symbol) { - return NULL; -} - -// SYM_REF0(RETC_operator_c) -void *visit_expression_type_c::visit(RETC_operator_c *symbol) { - if(il_default_variable_type == NULL) - STAGE3_ERROR(symbol, symbol, "RETC: il default variable should not be NULL."); - if (!is_BOOL_type(il_default_variable_type)) - STAGE3_ERROR(symbol, symbol, "RETC operator requires il_default_variable to be of type BOOL."); - return NULL; -} - -// SYM_REF0(RETCN_operator_c) -void *visit_expression_type_c::visit(RETCN_operator_c *symbol) { - if(il_default_variable_type == NULL) - STAGE3_ERROR(symbol, symbol, "RETCN: il_default_variable should not be NULL."); - if (!is_BOOL_type(il_default_variable_type)) - STAGE3_ERROR(symbol, symbol, "RETCN operator requires il_default_variable to be of type BOOL."); - return NULL; -} - -// SYM_REF0(JMP_operator_c) -void *visit_expression_type_c::visit(JMP_operator_c *symbol){ - return NULL; -} - -// SYM_REF0(JMPC_operator_c) -void *visit_expression_type_c::visit(JMPC_operator_c *symbol) { - if(il_default_variable_type == NULL) - STAGE3_ERROR(symbol, symbol, "JMPC: il default variable should not be NULL."); - if (!is_BOOL_type(il_default_variable_type)) - STAGE3_ERROR(symbol, symbol, "JMPC operator requires il_default_variable to be of type BOOL."); - return NULL; -} - -// SYM_REF0(JMPCN_operator_c) -void *visit_expression_type_c::visit(JMPCN_operator_c *symbol) { - if(il_default_variable_type == NULL) - STAGE3_ERROR(symbol, symbol, "JMPCN: il_default_variable should not be NULL."); - if (!is_BOOL_type(il_default_variable_type)) - STAGE3_ERROR(symbol, symbol, "JMPCN operator requires il_default_variable to be of type BOOL."); - return NULL; -} - -/* Symbol class handled together with function call checks */ -/* any_identifier ASSIGN */ -// SYM_REF1(il_assign_operator_c, variable_name) -// void *visit_expression_type_c::visit(il_assign_operator_c *symbol, variable_name); - -/* Symbol class handled together with function call checks */ -/*| [NOT] any_identifier SENDTO */ -// SYM_REF2(il_assign_out_operator_c, option, variable_name) -// void *visit_expression_type_c::visit(il_assign_operator_c *symbol, option, variable_name); - - - - - -/***************************************/ -/* B.3 - Language ST (Structured Text) */ -/***************************************/ -/***********************/ -/* B 3.1 - Expressions */ -/***********************/ - -void *visit_expression_type_c::visit(or_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - return compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_BIT_compatible, symbol->l_exp, symbol->r_exp); -} - - -void *visit_expression_type_c::visit(xor_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - return compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_BIT_compatible, symbol->l_exp, symbol->r_exp); -} - - -void *visit_expression_type_c::visit(and_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - return compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_BIT_compatible, symbol->l_exp, symbol->r_exp); -} - - -void *visit_expression_type_c::visit(equ_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_ELEMENTARY_OR_ENUMERATED_compatible, symbol->l_exp, symbol->r_exp); - return &search_expression_type_c::bool_type_name; -} - - -void *visit_expression_type_c::visit(notequ_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_ELEMENTARY_OR_ENUMERATED_compatible, symbol->l_exp, symbol->r_exp); - return &search_expression_type_c::bool_type_name; -} - - -void *visit_expression_type_c::visit(lt_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_ELEMENTARY_compatible, symbol->l_exp, symbol->r_exp); - return &search_expression_type_c::bool_type_name; -} - - -void *visit_expression_type_c::visit(gt_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_ELEMENTARY_compatible, symbol->l_exp, symbol->r_exp); - return &search_expression_type_c::bool_type_name; -} - - -void *visit_expression_type_c::visit(le_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_ELEMENTARY_compatible, symbol->l_exp, symbol->r_exp); - return &search_expression_type_c::bool_type_name; -} - - -void *visit_expression_type_c::visit(ge_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_ELEMENTARY_compatible, symbol->l_exp, symbol->r_exp); - return &search_expression_type_c::bool_type_name; -} - - -void *visit_expression_type_c::visit(add_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - -/* The following is already checked in compute_expression */ -/* - if (is_type(left_type, time_type_name_c) && is_type(right_type, time_type_name_c)) - return (void *)&time_type_name; -*/ - - if (is_type(left_type, tod_type_name_c) && is_type(right_type, time_type_name_c)) - return (void *)&tod_type_name; - if (is_type(left_type, safetod_type_name_c) && is_type(right_type, time_type_name_c)) - return (void *)&tod_type_name; - if (is_type(left_type, tod_type_name_c) && is_type(right_type, safetime_type_name_c)) - return (void *)&tod_type_name; - if (is_type(left_type, safetod_type_name_c) && is_type(right_type, safetime_type_name_c)) - return (void *)&safetod_type_name; - - if (is_type(left_type, dt_type_name_c) && is_type(right_type, time_type_name_c)) - return (void *)&dt_type_name; - if (is_type(left_type, safedt_type_name_c) && is_type(right_type, time_type_name_c)) - return (void *)&dt_type_name; - if (is_type(left_type, dt_type_name_c) && is_type(right_type, safetime_type_name_c)) - return (void *)&dt_type_name; - if (is_type(left_type, safedt_type_name_c) && is_type(right_type, safetime_type_name_c)) - return (void *)&safedt_type_name; - - return compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_MAGNITUDE_compatible, symbol->l_exp, symbol->r_exp); -} - - -void *visit_expression_type_c::visit(sub_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - -/* The following is already checked in compute_expression */ -/* - if (is_type(left_type, time_type_name_c) && is_type(right_type, time_type_name_c)) - return (void *)&time_type_name; -*/ - - if (is_type(left_type, tod_type_name_c) && is_type(right_type, time_type_name_c)) - return (void *)&tod_type_name; - if (is_type(left_type, safetod_type_name_c) && is_type(right_type, time_type_name_c)) - return (void *)&tod_type_name; - if (is_type(left_type, tod_type_name_c) && is_type(right_type, safetime_type_name_c)) - return (void *)&tod_type_name; - if (is_type(left_type, safetod_type_name_c) && is_type(right_type, safetime_type_name_c)) - return (void *)&safetod_type_name; - - if (is_type(left_type, dt_type_name_c) && is_type(right_type, time_type_name_c)) - return (void *)&dt_type_name; - if (is_type(left_type, safedt_type_name_c) && is_type(right_type, time_type_name_c)) - return (void *)&dt_type_name; - if (is_type(left_type, dt_type_name_c) && is_type(right_type, safetime_type_name_c)) - return (void *)&dt_type_name; - if (is_type(left_type, safedt_type_name_c) && is_type(right_type, safetime_type_name_c)) - return (void *)&safedt_type_name; - - if (is_type(left_type, tod_type_name_c) && is_type(right_type, tod_type_name_c)) - return (void *)&time_type_name; - if (is_type(left_type, safetod_type_name_c) && is_type(right_type, tod_type_name_c)) - return (void *)&time_type_name; - if (is_type(left_type, tod_type_name_c) && is_type(right_type, safetod_type_name_c)) - return (void *)&time_type_name; - if (is_type(left_type, safetod_type_name_c) && is_type(right_type, safetod_type_name_c)) - return (void *)&safetime_type_name; - - if (is_type(left_type, date_type_name_c) && is_type(right_type, date_type_name_c)) - return (void *)&time_type_name; - if (is_type(left_type, safedate_type_name_c) && is_type(right_type, date_type_name_c)) - return (void *)&time_type_name; - if (is_type(left_type, date_type_name_c) && is_type(right_type, safedate_type_name_c)) - return (void *)&time_type_name; - if (is_type(left_type, safedate_type_name_c) && is_type(right_type, safedate_type_name_c)) - return (void *)&safetime_type_name; - - if (is_type(left_type, dt_type_name_c) && is_type(right_type, dt_type_name_c)) - return (void *)&time_type_name; - if (is_type(left_type, safedt_type_name_c) && is_type(right_type, dt_type_name_c)) - return (void *)&time_type_name; - if (is_type(left_type, dt_type_name_c) && is_type(right_type, safedt_type_name_c)) - return (void *)&time_type_name; - if (is_type(left_type, safedt_type_name_c) && is_type(right_type, safedt_type_name_c)) - return (void *)&safetime_type_name; - - return compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_MAGNITUDE_compatible, symbol->l_exp, symbol->r_exp); -} - - -void *visit_expression_type_c::visit(mul_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - - if (is_type(left_type, time_type_name_c) && is_ANY_NUM_compatible(right_type)) - return (void *)&time_type_name; - if (is_type(left_type, safetime_type_name_c) && is_ANY_NUM_type(right_type)) - return (void *)&time_type_name; - if (is_type(left_type, safetime_type_name_c) && is_ANY_SAFENUM_type(right_type)) - return (void *)&safetime_type_name; - /* Since we have already checked for ANY_NUM_type and ANY_SAFENUM_type in the previous lines, - * this next line is really only to check for integers/reals of undefined type on 'right_type'... - */ - if (is_type(left_type, safetime_type_name_c) && is_ANY_NUM_compatible(right_type)) - return (void *)&safetime_type_name; - - return compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_NUM_compatible, symbol->l_exp, symbol->r_exp); -} - - -void *visit_expression_type_c::visit(div_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - - if (is_type(left_type, time_type_name_c) && is_ANY_NUM_compatible(right_type)) - return (void *)&time_type_name; - if (is_type(left_type, safetime_type_name_c) && is_ANY_NUM_type(right_type)) - return (void *)&time_type_name; - if (is_type(left_type, safetime_type_name_c) && is_ANY_SAFENUM_type(right_type)) - return (void *)&safetime_type_name; - /* Since we have already checked for ANY_NUM_type and ANY_SAFENUM_type in the previous lines, - * this next line is really only to check for integers/reals of undefined type on 'right_type'... - */ - if (is_type(left_type, safetime_type_name_c) && is_ANY_NUM_compatible(right_type)) - return (void *)&safetime_type_name; - - return compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_NUM_compatible, symbol->l_exp, symbol->r_exp); -} - - -void *visit_expression_type_c::visit(mod_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - return compute_expression(left_type, right_type, &visit_expression_type_c::is_ANY_INT_compatible, symbol->l_exp, symbol->r_exp); -} - - -void *visit_expression_type_c::visit(power_expression_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - if (!is_ANY_REAL_compatible(left_type)) - STAGE3_ERROR(symbol->l_exp, symbol->l_exp, "first operand of ** operator has invalid data type, should be of type ANY_REAL."); - if (!is_ANY_NUM_compatible(right_type)) - STAGE3_ERROR(symbol->r_exp, symbol->r_exp, "second operand of ** operator has invalid data type, should be of type ANY_NUM."); - - return (void *)left_type; -} - - -void *visit_expression_type_c::visit(neg_expression_c *symbol) { - symbol_c *exp_type = base_type((symbol_c *)symbol->exp->accept(*this)); - if (!is_ANY_MAGNITUDE_compatible(exp_type)) - STAGE3_ERROR(symbol, symbol, "operand of negate expression '-' has invalid data type, should be of type ANY_MAGNITUDE."); - - return exp_type; -} - - -void *visit_expression_type_c::visit(not_expression_c *symbol) { - symbol_c *type = base_type((symbol_c *)symbol->exp->accept(*this)); - return compute_expression(type, type, &visit_expression_type_c::is_ANY_BIT_compatible, NULL, symbol->exp); -} - - -void *visit_expression_type_c::visit(function_invocation_c *symbol) { - function_symtable_t::iterator lower = function_symtable.lower_bound(symbol->function_name); - function_symtable_t::iterator upper = function_symtable.upper_bound(symbol->function_name); - function_symtable_t::iterator current; - if (lower == function_symtable.end()) ERROR; - - symbol_c* return_data_type; - symbol_c* fdecl_return_type; - symbol_c* overloaded_data_type = NULL; - int extensible_param_count = -1; - symbol->called_function_declaration = NULL; - - function_symtable_t::iterator second = lower; - second++; - if (second == upper) { - /* call to a function that is not overloaded. */ - /* now check the semantics of the function call... */ - /* If the syntax parser is working correctly, exactly one of the - * following two symbols will be NULL, while the other is != NULL. - */ - function_declaration_c *f_decl = function_symtable.get_value(lower); - if (symbol-> formal_param_list != NULL) check_formal_call (symbol, f_decl); - if (symbol->nonformal_param_list != NULL) check_nonformal_call(symbol, f_decl); - /* Store the pointer to the declaration of the function being called. - * This data will be used by stage 4 to call the correct function. - * Mostly needed to disambiguate overloaded functions... - * See comments in absyntax.def for more details - */ - symbol->called_function_declaration = f_decl; - return base_type(f_decl->type_name); - } - - /* This is a call to an overloaded function... */ - if (debug) printf("visit_expression_type_c::visit(function_invocation_c *symbol): FOUND CALL TO OVERLOADED FUNCTION!!\n"); - for(current = lower; current != upper; current++) { - if (debug) printf("visit_expression_type_c::visit(function_invocation_c *symbol): FOUND CALL TO OVERLOADED FUNCTION!! iterating...\n"); - int error_count = 0; - function_declaration_c *f_decl = function_symtable.get_value(current); - if (symbol-> formal_param_list != NULL) check_formal_call (symbol, f_decl, &error_count); - if (symbol->nonformal_param_list != NULL) check_nonformal_call(symbol, f_decl, false, &error_count); - if (0 == error_count) { - - fdecl_return_type = base_type(f_decl->type_name); - - if (symbol->called_function_declaration == NULL) { - /* Store the pointer to the declaration of the function being called. - * This data will be used by stage 4 to call the correct function. - * Mostly needed to disambiguate overloaded functions... - * See comments in absyntax.def for more details - */ - symbol->called_function_declaration = f_decl; - extensible_param_count = symbol->extensible_param_count; - - /* determine the base data type returned by the function being called... */ - return_data_type = fdecl_return_type; - } - else if (typeid(*return_data_type) != typeid(*fdecl_return_type)){ - return_data_type = common_literal(return_data_type, fdecl_return_type); - overloaded_data_type = overloaded_return_type(return_data_type); - } - - if (NULL == return_data_type) ERROR; - } - } - - if (overloaded_data_type != NULL) { - for(current = lower; current != upper; current++) { - function_declaration_c *f_decl = function_symtable.get_value(current); - int error_count = 0; - if (symbol-> formal_param_list != NULL) check_formal_call (symbol, f_decl, &error_count); - if (symbol->nonformal_param_list != NULL) check_nonformal_call(symbol, f_decl, false, &error_count); - if (0 == error_count) { - - fdecl_return_type = base_type(f_decl->type_name); - - if (typeid(*overloaded_data_type) == typeid(*fdecl_return_type)){ - /* Store the pointer to the declaration of the function being called. - * This data will be used by stage 4 to call the correct function. - * Mostly needed to disambiguate overloaded functions... - * See comments in absyntax.def for more details - */ - symbol->called_function_declaration = f_decl; - extensible_param_count = symbol->extensible_param_count; - } - } - } - } - - if (return_data_type != NULL) { - symbol->extensible_param_count = extensible_param_count; - return return_data_type; - } - - /* No compatible function was found for this function call */ - STAGE3_ERROR(symbol, symbol, "Call to an overloaded function with invalid parameter type."); - return NULL; -} - -/********************/ -/* B 3.2 Statements */ -/********************/ -// SYM_LIST(statement_list_c) -/* The visitor of the base class search_visitor_c will handle calling each instruction in the list. - * We do not need to do anything here... - */ -// void *visit_expression_type_c::visit(statement_list_c *symbol) - - -/*********************************/ -/* B 3.2.1 Assignment Statements */ -/*********************************/ - -void *visit_expression_type_c::visit(assignment_statement_c *symbol) { - symbol_c *left_type = base_type((symbol_c *)symbol->l_exp->accept(*this)); - symbol_c *right_type = base_type((symbol_c *)symbol->r_exp->accept(*this)); - - if (debug) { - printf("visit_expression_type_c::visit(assignment_statement_c) called. Checking --->"); - symbolic_variable_c *hi = dynamic_cast(symbol->l_exp); - if (hi != NULL) { - identifier_c *hi1 = dynamic_cast(hi->var_name); - if (hi1 != NULL) printf("%s", hi1->value); - } - printf(" := "); - hex_integer_c *hi2 = dynamic_cast(symbol->r_exp); - if (hi2 != NULL) printf("%s", hi2->value); - printf("\n"); - } // if (debug) - - if (NULL == left_type) { - STAGE3_ERROR(symbol->l_exp, symbol->l_exp, "Could not determine data type of expression (undefined variable, constant, or structure element?).\n"); - } else if (NULL == right_type) { - STAGE3_ERROR(symbol->r_exp, symbol->r_exp, "Could not determine data type of expression (undefined variable, constant, or structure element?).\n"); - } else if (!is_valid_assignment(left_type, right_type)) - STAGE3_ERROR(symbol, symbol, "data type mismatch in assignment statement!\n"); - - return NULL; -} - - - -/*****************************************/ -/* B 3.2.2 Subprogram Control Statements */ -/*****************************************/ - -/* RETURN */ -// SYM_REF0(return_statement_c) - - -/* fb_name '(' [param_assignment_list] ')' */ -/* param_assignment_list -> may be NULL ! */ -// SYM_REF3(fb_invocation_c, fb_name, formal_param_list, nonformal_param_list) -void *visit_expression_type_c::visit(fb_invocation_c *symbol) { - symbol_c *fb_decl = search_varfb_instance_type->get_basetype_decl(symbol->fb_name); - /* The following should never occur. The function block must be defined, - * and the FB type being called MUST be in the symtable... - * This was all already checked at stage 2! - */ - if (NULL == fb_decl) ERROR; - - /* now check the semantics of the fb call... */ - /* If the syntax parser is working correctly, exactly one of the - * following two symbols will be NULL, while the other is != NULL. - */ - if (symbol-> formal_param_list != NULL) check_formal_call (symbol, fb_decl); - if (symbol->nonformal_param_list != NULL) check_nonformal_call(symbol, fb_decl); - - return NULL; -} - - -#if 0 -/* helper symbol for fb_invocation */ -/* param_assignment_list ',' param_assignment */ -SYM_LIST(param_assignment_list_c) - -/* variable_name ASSIGN expression */ -SYM_REF2(input_variable_param_assignment_c, variable_name, expression) - -/* [NOT] variable_name '=>' variable */ -SYM_REF3(output_variable_param_assignment_c, not_param, variable_name, variable) - -/* helper CLASS for output_variable_param_assignment */ -SYM_REF0(not_paramassign_c) -#endif - -/********************************/ -/* B 3.2.3 Selection Statements */ -/********************************/ - -/* IF expression THEN statement_list elseif_statement_list ELSE statement_list END_IF */ -// SYM_REF4(if_statement_c, expression, statement_list, elseif_statement_list, else_statement_list) -void *visit_expression_type_c::visit(if_statement_c *symbol) { - symbol_c *expr_type = base_type((symbol_c*)symbol->expression->accept(*this)); - if (!is_BOOL_type(expr_type)) STAGE3_ERROR(symbol->expression,symbol->expression,"IF conditional expression is not of boolean type."); - if (NULL != symbol->statement_list) - symbol->statement_list->accept(*this); - if (NULL != symbol->elseif_statement_list) - symbol->elseif_statement_list->accept(*this); - if (NULL != symbol->else_statement_list) - symbol->else_statement_list->accept(*this); - return NULL; -} - -/* helper symbol for if_statement */ -// SYM_LIST(elseif_statement_list_c) -// void *visit_expression_type_c::visit(elseif_statement_list_c *symbol) { } - -/* helper symbol for elseif_statement_list */ -/* ELSIF expression THEN statement_list */ -// SYM_REF2(elseif_statement_c, expression, statement_list) -void *visit_expression_type_c::visit(elseif_statement_c *symbol) { - symbol_c *elseif_expr_type = base_type((symbol_c*)symbol->expression->accept(*this)); - if(!is_BOOL_type(elseif_expr_type)) STAGE3_ERROR(symbol->expression,symbol->expression,"ELSIF conditional expression is not of boolean type."); - if (NULL != symbol->statement_list) - symbol->statement_list->accept(*this); - return NULL; -} - - -/* CASE expression OF case_element_list ELSE statement_list END_CASE */ -// SYM_REF3(case_statement_c, expression, case_element_list, statement_list) -void *visit_expression_type_c::visit(case_statement_c *symbol) { - case_expression_type = base_type((symbol_c*)symbol->expression->accept(*this)); - if (NULL != case_expression_type) { - if (NULL != symbol->case_element_list) - symbol->case_element_list->accept(*this); - } - if (NULL != symbol->statement_list) - symbol->statement_list->accept(*this); - return NULL; -} - -#if 0 -/* helper symbol for case_statement */ -// SYM_LIST(case_element_list_c) -// void *visit_expression_type_c::visit(case_element_list_c *symbol); - -/* case_list ':' statement_list */ -// SYM_REF2(case_element_c, case_list, statement_list) -void *visit_expression_type_c::visit(case_element_c *symbol); -#endif - -// SYM_LIST(case_list_c) -void *visit_expression_type_c::visit(case_list_c *symbol) { - symbol_c *element_type; - for(int i = 0; i < symbol->n; i++) { - element_type = (symbol_c *)symbol->elements[i]->accept(*this); - if (NULL == element_type) { - STAGE3_ERROR(symbol->elements[i], symbol->elements[i], "Case list element has undefined data type."); - } else { - element_type = base_type(element_type); - if (NULL != element_type){ - /* The CASE value is only used for comparison (and not assingment), so we only check for compatibility! */ - if (!is_compatible_type(case_expression_type, element_type)) - STAGE3_ERROR(symbol->elements[i], symbol->elements[i], "Invalid data type of case list element."); - } - } - } - return NULL; -} - -/********************************/ -/* B 3.2.4 Iteration Statements */ -/********************************/ - -/* FOR control_variable ASSIGN expression TO expression [BY expression] DO statement_list END_FOR */ -// SYM_REF5(for_statement_c, control_variable, beg_expression, end_expression, by_expression, statement_list) -void *visit_expression_type_c::visit(for_statement_c *symbol) { - symbol_c *var_type = (symbol_c*)symbol->control_variable->accept(*this); - if (NULL == var_type) ERROR; - var_type = base_type(var_type); - if (NULL == var_type) ERROR; - // ASSIGN - symbol_c *beg_expr_type = base_type((symbol_c*)symbol->beg_expression->accept(*this)); - if (NULL != beg_expr_type) { - /* The BEG value is assigned to the variable, so we check for assignment validity! */ - if(!is_valid_assignment(var_type, beg_expr_type)) - STAGE3_ERROR(symbol->beg_expression, symbol->beg_expression, "Data type mismatch between control variable and initial value."); - } - // TO - symbol_c *end_expr_type = base_type((symbol_c*)symbol->end_expression->accept(*this)); - if (NULL != end_expr_type) { - /* The TO value is only used for comparison, so we only check for compatibility! */ - if(!is_compatible_type(var_type, end_expr_type)) - STAGE3_ERROR(symbol->end_expression, symbol->end_expression, "Data type mismatch between control variable and final value."); - } - // BY - if(symbol->by_expression != NULL) { - symbol_c *by_expr_type = base_type((symbol_c*)symbol->by_expression->accept(*this)); - if (NULL != end_expr_type) { - /* The BY value is used in an expression (add, sub, ...), so we only check for compatibility! */ - if(!is_compatible_type(var_type, by_expr_type)) - STAGE3_ERROR(symbol->by_expression, symbol->by_expression, "Data type mismatch between control variable and BY value."); - } - } - // DO - if (NULL != symbol->statement_list) - symbol->statement_list->accept(*this); - return NULL; -} - - -/* WHILE expression DO statement_list END_WHILE */ -// SYM_REF2(while_statement_c, expression, statement_list) -void *visit_expression_type_c::visit(while_statement_c *symbol) { - symbol_c *expr_type = base_type((symbol_c*)symbol->expression->accept(*this)); - if (NULL != expr_type) { - if(!is_BOOL_type(expr_type)) - STAGE3_ERROR(symbol->expression,symbol->expression,"WHILE conditional expression is not of boolean type."); - } - - if (NULL != symbol->statement_list) - symbol->statement_list->accept(*this); - return NULL; -} - -/* REPEAT statement_list UNTIL expression END_REPEAT */ -// SYM_REF2(repeat_statement_c, statement_list, expression) -void *visit_expression_type_c::visit(repeat_statement_c *symbol) { - if (NULL != symbol->statement_list) - symbol->statement_list->accept(*this); - - symbol_c *expr_type = base_type((symbol_c*)symbol->expression->accept(*this)); - if (NULL != expr_type) { - if(!is_BOOL_type(expr_type)) - STAGE3_ERROR(symbol->expression,symbol->expression,"REPEAT conditional expression is not of boolean type."); - } - return NULL; -} - -/* EXIT */ -// SYM_REF0(exit_statement_c) - - - diff -r aad38592bdde -r c0bda77b37a0 stage3/visit_expression_type.hh --- a/stage3/visit_expression_type.hh Tue Aug 14 19:40:01 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,393 +0,0 @@ -/* - * matiec - a compiler for the programming languages defined in IEC 61131-3 - * - * Copyright (C) 2009-2011 Mario de Sousa (msousa@fe.up.pt) - * Copyright (C) 2007-2011 Laurent Bessard and Edouard Tisserant - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - -/* - * An IEC 61131-3 compiler. - * - * Based on the - * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) - * - */ - -/* Verify whether the semantic rules of data type compatibility are being followed. - * - * For example: - */ - -#include "../absyntax_utils/absyntax_utils.hh" - -class visit_expression_type_c: public search_constant_type_c { - - private: - search_varfb_instance_type_c *search_varfb_instance_type; - search_base_type_c search_base_type; - - /* When calling a function block, we must first find it's type, - * by searching through the declarations of the variables currently - * in scope. - * This class does just that... - * A new bject instance is instantiated whenever we start checking semantics - * for a function block type declaration, or a program declaration. - * This object instance will then later be called while the - * function block's or the program's body is being handled. - * - * Note that functions cannot contain calls to function blocks, - * so we do not create an object instance when handling - * a function declaration. - */ -// search_var_instance_decl_c *search_var_instance_decl; - - /* This variable was created to pass information from - * visit_expression_type_c::visit(case_statement_c *symbol) function to - * visit_expression_type_c::visit(case_list_c *symbol) function. - */ - symbol_c *case_expression_type; - - /* In IL code, once we find a type mismatch error, it is best to - * ignore any further errors until the end of the logicl operation, - * i.e. until the next LD. - * However, we cannot clear the il_error flag on all LD operations, - * as these may also be used within parenthesis. LD operations - * within parenthesis may not clear the error flag. - * We therefore need a counter to know how deep inside a parenthesis - * structure we are. - */ - int il_parenthesis_level; - bool il_error; - bool error_found; - - /* the current data type of the data stored in the IL stack, i.e. the default variable */ - symbol_c *il_default_variable_type; - /* the current IL operand being analysed - its symbol and its data type */ - symbol_c *il_operand_type; - symbol_c *il_operand; - - - public: - visit_expression_type_c(symbol_c *ignore); - virtual ~visit_expression_type_c(void); - - bool get_error_found(void); - - typedef struct { - symbol_c *value; - symbol_c *type; - } value_and_type_t; - - /* A helper function... */ - bool is_ANY_ELEMENTARY_type (symbol_c *type_symbol); - bool is_ANY_SAFEELEMENTARY_type (symbol_c *type_symbol); - bool is_ANY_ELEMENTARY_compatible (symbol_c *type_symbol); - - bool is_ANY_MAGNITUDE_type (symbol_c *type_symbol); - bool is_ANY_SAFEMAGNITUDE_type (symbol_c *type_symbol); - bool is_ANY_MAGNITUDE_compatible (symbol_c *type_symbol); - - bool is_ANY_DATE_type (symbol_c *type_symbol); - bool is_ANY_SAFEDATE_type (symbol_c *type_symbol); - bool is_ANY_DATE_compatible (symbol_c *type_symbol); - - bool is_ANY_STRING_type (symbol_c *type_symbol); - bool is_ANY_SAFESTRING_type (symbol_c *type_symbol); - bool is_ANY_STRING_compatible (symbol_c *type_symbol); - - bool is_ANY_INT_type (symbol_c *type_symbol); - bool is_ANY_SAFEINT_type (symbol_c *type_symbol); - bool is_ANY_INT_compatible (symbol_c *type_symbol); - - bool is_ANY_REAL_type (symbol_c *type_symbol); - bool is_ANY_SAFEREAL_type (symbol_c *type_symbol); - bool is_ANY_REAL_compatible (symbol_c *type_symbol); - - bool is_ANY_NUM_type (symbol_c *type_symbol); - bool is_ANY_SAFENUM_type (symbol_c *type_symbol); - bool is_ANY_NUM_compatible (symbol_c *type_symbol); - - bool is_ANY_BIT_type (symbol_c *type_symbol); - bool is_ANY_SAFEBIT_type (symbol_c *type_symbol); - bool is_ANY_BIT_compatible (symbol_c *type_symbol); - - bool is_BOOL_type (symbol_c *type_symbol); - bool is_SAFEBOOL_type (symbol_c *type_symbol); - bool is_ANY_BOOL_compatible (symbol_c *type_symbol); - - bool is_nonneg_literal_integer_type (symbol_c *type_symbol); - bool is_literal_integer_type (symbol_c *type_symbol); - bool is_literal_real_type (symbol_c *type_symbol); - bool is_literal_bool_type (symbol_c *type_symbol); - - bool is_ANY_ELEMENTARY_OR_ENUMERATED_compatible (symbol_c *type_symbol); - - /* Determine the common data type between two data types. - * If no common data type found, return NULL. - * - * If data types are identical, return the first (any would do...). - * If any of the datat types is a literal, we confirm that - * the literal uses less bits than the fixed size data type. - * e.g. BYTE and 1024 returns NULL - * BYTE and 255 returns BYTE - * - * If two literals, then return the literal that requires more bits... - */ - symbol_c *common_type__(symbol_c *first_type, symbol_c *second_type); - /* Determine the common data type between two data types. - * Unlike the common_type__() function, we stop the compiler with an ERROR - * if no common data type is found. - */ - symbol_c *common_type(symbol_c *first_type, symbol_c *second_type); - - symbol_c *common_literal(symbol_c *first_type, symbol_c *second_type); - symbol_c *overloaded_return_type(symbol_c *type); - -/* Return TRUE if the second (value) data type may be assigned to a variable of the first (variable) data type - * such as: - * var_type value_type - * BOOL BYTE#7 -> returns false - * INT INT#7 -> returns true - * INT 7 -> returns true - * REAL 7.89 -> returns true - * REAL 7 -> returns true - * INT 7.89 -> returns false - * SAFEBOOL BOOL#1 -> returns false !!! - * etc... - * - * NOTE: It is assumed that the var_type is the data type of an lvalue - */ - bool is_valid_assignment(symbol_c *var_type, symbol_c *value_type); - -/* Return TRUE if there is a common data type, otherwise return FALSE - * i.e., return TRUE if both data types may be used simultaneously in an expression - * such as: - * BOOL#0 AND BYTE#7 -> returns false - * 0 AND BYTE#7 -> returns true - * INT#10 AND INT#7 -> returns true - * INT#10 AND 7 -> returns true - * REAL#34.3 AND 7.89 -> returns true - * REAL#34.3 AND 7 -> returns true - * INT#10 AND 7.89 -> returns false - * SAFEBOOL#0 AND BOOL#1 -> returns true !!! - * etc... - */ - bool is_compatible_type(symbol_c *first_type, symbol_c *second_type); - - /* check semantics of FB call in the IL language using input operators */ - /* e.g. CU, CLK, IN, PT, SR, ... */ - void check_il_fbcall(symbol_c *symbol, const char *input_operator); - /* check the semantics of a FB or Function non-formal call */ - /* e.g. foo(1, 2, 3, 4); */ - /* If error_count pointer is NULL, print out error messages. - * If error_count pointer is != NULL, do not print out error messages, but tally up - * how many errors were found. - */ - void check_nonformal_call(symbol_c *f_call, symbol_c *f_decl, bool use_il_defvar = false, int *error_count = NULL); - /* check the semantics of a FB or Function formal call */ - /* e.g. foo(IN1 := 1, OUT1 =>x, EN := true); */ - /* If error_count pointer is NULL, print out error messages. - * If error_count pointer is != NULL, do not print out error messages, but tally up - * how many errors were found. - */ - void check_formal_call(symbol_c *f_call, symbol_c *f_decl, int *error_count = NULL); - - - void *compute_standard_function_default(function_invocation_c *st_symbol, il_formal_funct_call_c *il_symbol); - void *compute_standard_function_il(il_function_call_c *symbol, symbol_c *param_data_type); - - - /* A helper function... */ - typedef bool (visit_expression_type_c::*is_data_type_t)(symbol_c *type_symbol); /* a pointer to a function! */ -// symbol_c *compute_boolean_expression(symbol_c *left_exp, symbol_c *right_exp, is_data_type_t is_data_type); -// symbol_c *compute_numeric_expression(symbol_c *left_exp, symbol_c *right_exp, is_data_type_t is_data_type); -// symbol_c *compute_expression(symbol_c *left_exp, symbol_c *right_exp, is_data_type_t is_data_type); - symbol_c *compute_expression(symbol_c *left_type, symbol_c *right_type, is_data_type_t is_data_type, - symbol_c *left_expr, symbol_c *right_expr); - - - /* a helper function... */ - symbol_c *base_type(symbol_c *symbol); - - /* a helper function... */ - void *verify_null(symbol_c *symbol); - - /********************************/ - /* B 1.3.3 - Derived data types */ - /********************************/ - void *visit(data_type_declaration_c *symbol); - - /*********************/ - /* B 1.4 - Variables */ - /*********************/ - void *visit(symbolic_variable_c *symbol); - - /********************************************/ - /* B 1.4.1 - Directly Represented Variables */ - /********************************************/ - void *visit(direct_variable_c *symbol); - - /*************************************/ - /* B 1.4.2 - Multi-element variables */ - /*************************************/ - - void *visit(array_variable_c *symbol); - void *visit(structured_variable_c *symbol); - - /********************************/ - /* B 1.7 Configuration elements */ - /********************************/ - void *visit(configuration_declaration_c *symbol); - -/****************************************/ - /* B.2 - Language IL (Instruction List) */ - /****************************************/ - /***********************************/ - /* B 2.1 Instructions and Operands */ - /***********************************/ - // void *visit(instruction_list_c *symbol); - void *visit(il_simple_operation_c *symbol); - void *visit(il_function_call_c *symbol); - void *visit(il_expression_c *symbol); -// void *visit(il_jump_operation_c *symbol); - void *visit(il_fb_call_c *symbol); - void *visit(il_formal_funct_call_c *symbol); - /* - void *visit(il_operand_list_c *symbol); - void *visit(simple_instr_list_c *symbol); - void *visit(il_param_list_c *symbol); - void *visit(il_param_assignment_c *symbol); - void *visit(il_param_out_assignment_c *symbol); - */ - - /*******************/ - /* B 2.2 Operators */ - /*******************/ - void *visit(LD_operator_c *symbol); - void *visit(LDN_operator_c *symbol); - void *visit(ST_operator_c *symbol); - void *visit(STN_operator_c *symbol); - void *visit(NOT_operator_c *symbol); - void *visit(S_operator_c *symbol); - void *visit(R_operator_c *symbol); - void *visit(S1_operator_c *symbol); - void *visit(R1_operator_c *symbol); - void *visit(CLK_operator_c *symbol); - void *visit(CU_operator_c *symbol); - void *visit(CD_operator_c *symbol); - void *visit(PV_operator_c *symbol); - void *visit(IN_operator_c *symbol); - void *visit(PT_operator_c *symbol); - void *visit(AND_operator_c *symbol); - void *visit(OR_operator_c *symbol); - void *visit(XOR_operator_c *symbol); - void *visit(ANDN_operator_c *symbol); - void *visit(ORN_operator_c *symbol); - void *visit(XORN_operator_c *symbol); - void *visit(ADD_operator_c *symbol); - void *visit(SUB_operator_c *symbol); - void *visit(MUL_operator_c *symbol); - void *visit(DIV_operator_c *symbol); - void *visit(MOD_operator_c *symbol); - void *visit(GT_operator_c *symbol); - void *visit(GE_operator_c *symbol); - void *visit(EQ_operator_c *symbol); - void *visit(LT_operator_c *symbol); - void *visit(LE_operator_c *symbol); - void *visit(NE_operator_c *symbol); - void *visit(CAL_operator_c *symbol); - void *visit(CALC_operator_c *symbol); - void *visit(CALCN_operator_c *symbol); - void *visit(RET_operator_c *symbol); - void *visit(RETC_operator_c *symbol); - void *visit(RETCN_operator_c *symbol); - void *visit(JMP_operator_c *symbol); - void *visit(JMPC_operator_c *symbol); - void *visit(JMPCN_operator_c *symbol); - /* Symbol class handled together with function call checks */ - // void *visit(il_assign_operator_c *symbol, variable_name); - /* Symbol class handled together with function call checks */ - // void *visit(il_assign_operator_c *symbol, option, variable_name); - - - - /***************************************/ - /* B.3 - Language ST (Structured Text) */ - /***************************************/ - /***********************/ - /* B 3.1 - Expressions */ - /***********************/ - void *visit(or_expression_c *symbol); - void *visit(xor_expression_c *symbol); - void *visit(and_expression_c *symbol); - void *visit(equ_expression_c *symbol); - void *visit(notequ_expression_c *symbol); - void *visit(lt_expression_c *symbol); - void *visit(gt_expression_c *symbol); - void *visit(le_expression_c *symbol); - void *visit(ge_expression_c *symbol); - void *visit(add_expression_c *symbol); - void *visit(sub_expression_c *symbol); - void *visit(mul_expression_c *symbol); - void *visit(div_expression_c *symbol); - void *visit(mod_expression_c *symbol); - void *visit(power_expression_c *symbol); - void *visit(neg_expression_c *symbol); - void *visit(not_expression_c *symbol); - void *visit(function_invocation_c *symbol); - - /*********************************/ - /* B 3.2.1 Assignment Statements */ - /*********************************/ - void *visit(assignment_statement_c *symbol); - - /*****************************************/ - /* B 3.2.2 Subprogram Control Statements */ - /*****************************************/ - void *visit(fb_invocation_c *symbol); - - /********************************/ - /* B 3.2.3 Selection Statements */ - /********************************/ - - void *visit(if_statement_c *symbol); -// void *visit(elseif_statement_list_c *symbol); - void *visit(elseif_statement_c *symbol); - void *visit(case_statement_c *symbol); -// void *visit(case_element_list_c *symbol); -// void *visit(case_element_c *symbol); - void *visit(case_list_c *symbol); - - /********************************/ - /* B 3.2.4 Iteration Statements */ - /********************************/ - - void *visit(for_statement_c *symbol); - void *visit(while_statement_c *symbol); - void *visit(repeat_statement_c *symbol); - - -//TODO: delete this functions. Why are they needed? -void *visit(program_declaration_c *symbol); -void *visit(function_declaration_c *symbol); -void *visit(function_block_declaration_c *symbol); - -}; // visit_expression_type_c - diff -r aad38592bdde -r c0bda77b37a0 stage4/generate_c/generate_c.cc --- a/stage4/generate_c/generate_c.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/generate_c/generate_c.cc Wed Aug 22 16:46:17 2012 +0200 @@ -31,10 +31,12 @@ #include #include + #include "../../util/symtable.hh" #include "../../util/dsymtable.hh" #include "../../absyntax/visitor.hh" #include "../../absyntax_utils/absyntax_utils.hh" +#include "../../main.hh" // required for ERROR() and ERROR_MSG() macros. #include "../stage4.hh" @@ -45,9 +47,16 @@ #define TRACE(classname) #endif -#define ERROR error_exit(__FILE__,__LINE__) -/* function defined in main.cc */ -extern void error_exit(const char *file_name, int line_no); + + +#define STAGE4_ERROR(symbol1, symbol2, ...) {stage4err("while generating C code", symbol1, symbol2, __VA_ARGS__); exit(EXIT_FAILURE);} + + +/* Macros to access the constant value of each expression (if it exists) from the annotation introduced to the symbol_c object by constant_folding_c in stage3! */ +#define VALID_CVALUE(dtype, symbol) (symbol_c::cs_const_value == (symbol)->const_value._##dtype.status) +#define GET_CVALUE(dtype, symbol) ((symbol)->const_value._##dtype.value) + + /***********************************************************************/ @@ -197,10 +206,9 @@ } public: - print_function_parameter_data_types_c(stage4out_c *s4o_ptr): - generate_c_base_c(s4o_ptr) { - current_type = NULL; - } + print_function_parameter_data_types_c(stage4out_c *s4o_ptr): + generate_c_base_c(s4o_ptr) + {current_type = NULL;} /**************************************/ /* B.1.5 - Program organization units */ @@ -211,8 +219,7 @@ /* FUNCTION derived_function_name ':' elementary_type_name io_OR_function_var_declarations_list function_body END_FUNCTION */ /* | FUNCTION derived_function_name ':' derived_type_name io_OR_function_var_declarations_list function_body END_FUNCTION */ void *visit(function_declaration_c *symbol) { - /* return type */ - symbol->type_name->accept(*this); + symbol->type_name->accept(*this); /* return type */ symbol->var_declarations_list->accept(*this); return NULL; } @@ -339,94 +346,75 @@ #define MILLISECOND 1000000 #define SECOND 1000 * MILLISECOND -/* A helper class that knows how to generate code for both the IL and ST languages... */ -class calculate_time_c: public iterator_visitor_c { - private: - unsigned long long time; - float current_value; +unsigned long long calculate_time(symbol_c *symbol) { + if (NULL == symbol) return 0; - public: - calculate_time_c(void){time = 0;}; - - unsigned long long get_time(void) {return time;}; - - void *get_integer_value(token_c *token) { - std::string str = ""; - for (unsigned int i = 0; i < strlen(token->value); i++) - if (token->value[i] != '_') - str += token->value[i]; - current_value = atof(str.c_str()); - return NULL; - } - - void *get_float_value(token_c *token) { - current_value = atof(token->value); - return NULL; - } - -/******************************/ -/* B 1.2.1 - Numeric Literals */ -/******************************/ - - void *visit(integer_c *symbol) {return get_integer_value(symbol);} - -/************************/ -/* B 1.2.3.1 - Duration */ -/************************/ + interval_c *interval = dynamic_cast(symbol); + duration_c *duration = dynamic_cast(symbol); + if ((NULL == interval) && (NULL == duration)) ERROR; + + if (NULL != duration) { /* SYM_REF2(duration_c, neg, interval) */ - void *visit(duration_c *symbol) { - if (symbol->neg != NULL) - ERROR; - symbol->interval->accept(*this); - return NULL; - } - - /* SYM_TOKEN(fixed_point_c) */ - void *visit(fixed_point_c *symbol) {return get_float_value(symbol);} - - /* SYM_REF2(days_c, days, hours) */ - void *visit(days_c *symbol) { - if (symbol->hours) - symbol->hours->accept(*this); - symbol->days->accept(*this); - time += (unsigned long long)(current_value * 24 * 3600 * SECOND); - return NULL; - } - - /* SYM_REF2(hours_c, hours, minutes) */ - void *visit(hours_c *symbol) { - if (symbol->minutes) - symbol->minutes->accept(*this); - symbol->hours->accept(*this); - time += (unsigned long long)(current_value * 3600 * SECOND); - return NULL; - } - - /* SYM_REF2(minutes_c, minutes, seconds) */ - void *visit(minutes_c *symbol) { - if (symbol->seconds) - symbol->seconds->accept(*this); - symbol->minutes->accept(*this); - time += (unsigned long long)(current_value * 60 * SECOND); - return NULL; - } - - /* SYM_REF2(seconds_c, seconds, milliseconds) */ - void *visit(seconds_c *symbol) { - if (symbol->milliseconds) - symbol->milliseconds->accept(*this); - symbol->seconds->accept(*this); - time += (unsigned long long)(current_value * SECOND); - return NULL; - } - - /* SYM_REF2(milliseconds_c, milliseconds, unused) */ - void *visit(milliseconds_c *symbol) { - symbol->milliseconds->accept(*this); - time += (unsigned long long)(current_value * MILLISECOND); - return NULL; - } + if (duration->neg != NULL) + {STAGE4_ERROR(duration, duration, "Negative TIME literals are not currently supported"); ERROR;} + return calculate_time(duration->interval); + } + + if (NULL != interval) { + /* SYM_REF5(interval_c, days, hours, minutes, seconds, milliseconds) */ + unsigned long long int time_ull = 0; + long double time_ld = 0; + /* + const unsigned long long int MILLISECOND = 1000000; + const unsigned long long int SECOND = 1000 * MILLISECOND + */ + + if (NULL != interval->milliseconds) { + if (VALID_CVALUE( int64, interval->milliseconds) && GET_CVALUE( int64, interval->milliseconds) < 0) ERROR; // interval elements should always be positive! + if (VALID_CVALUE( int64, interval->milliseconds)) time_ull += GET_CVALUE( int64, interval->milliseconds) * MILLISECOND; + else if (VALID_CVALUE(uint64, interval->milliseconds)) time_ull += GET_CVALUE(uint64, interval->milliseconds) * MILLISECOND; + else if (VALID_CVALUE(real64, interval->milliseconds)) time_ld += GET_CVALUE(real64, interval->milliseconds) * MILLISECOND; + else ERROR; // if (NULL != interval->milliseconds) is true, then it must have a valid constant value! + } + + if (NULL != interval->seconds ) { + if (VALID_CVALUE( int64, interval->seconds ) && GET_CVALUE( int64, interval->seconds ) < 0) ERROR; // interval elements should always be positive! + if (VALID_CVALUE( int64, interval->seconds )) time_ull += GET_CVALUE( int64, interval->seconds ) * SECOND; + else if (VALID_CVALUE(uint64, interval->seconds )) time_ull += GET_CVALUE(uint64, interval->seconds ) * SECOND; + else if (VALID_CVALUE(real64, interval->seconds )) time_ld += GET_CVALUE(real64, interval->seconds ) * SECOND; + else ERROR; // if (NULL != interval->seconds) is true, then it must have a valid constant value! + } + + if (NULL != interval->minutes ) { + if (VALID_CVALUE( int64, interval->minutes ) && GET_CVALUE( int64, interval->minutes ) < 0) ERROR; // interval elements should always be positive! + if (VALID_CVALUE( int64, interval->minutes )) time_ull += GET_CVALUE( int64, interval->minutes ) * SECOND * 60; + else if (VALID_CVALUE(uint64, interval->minutes )) time_ull += GET_CVALUE(uint64, interval->minutes ) * SECOND * 60; + else if (VALID_CVALUE(real64, interval->minutes )) time_ld += GET_CVALUE(real64, interval->minutes ) * SECOND * 60; + else ERROR; // if (NULL != interval->minutes) is true, then it must have a valid constant value! + } + + if (NULL != interval->hours ) { + if (VALID_CVALUE( int64, interval->hours ) && GET_CVALUE( int64, interval->hours ) < 0) ERROR; // interval elements should always be positive! + if (VALID_CVALUE( int64, interval->hours )) time_ull += GET_CVALUE( int64, interval->hours ) * SECOND * 60 * 60; + else if (VALID_CVALUE(uint64, interval->hours )) time_ull += GET_CVALUE(uint64, interval->hours ) * SECOND * 60 * 60; + else if (VALID_CVALUE(real64, interval->hours )) time_ld += GET_CVALUE(real64, interval->hours ) * SECOND * 60 * 60; + else ERROR; // if (NULL != interval->hours) is true, then it must have a valid constant value! + } + + if (NULL != interval->days ) { + if (VALID_CVALUE( int64, interval->days ) && GET_CVALUE( int64, interval->days ) < 0) ERROR; // interval elements should always be positive! + if (VALID_CVALUE( int64, interval->days )) time_ull += GET_CVALUE( int64, interval->days ) * SECOND * 60 * 60 * 24; + else if (VALID_CVALUE(uint64, interval->days )) time_ull += GET_CVALUE(uint64, interval->days ) * SECOND * 60 * 60 * 24; + else if (VALID_CVALUE(real64, interval->days )) time_ld += GET_CVALUE(real64, interval->days ) * SECOND * 60 * 60 * 24; + else ERROR; // if (NULL != interval->days) is true, then it must have a valid constant value! + } + + time_ull += time_ld; + return time_ull; + }; + ERROR; // should never reach this point! + return 0; // humour the compiler! }; /***********************************************************************/ @@ -480,14 +468,9 @@ /* TASK task_name task_initialization */ //SYM_REF2(task_configuration_c, task_name, task_initialization) void *visit(task_initialization_c *symbol) { - calculate_time_c calculate_time; - unsigned long long time = 0; - if (symbol->interval_data_source != NULL) { - symbol->interval_data_source->accept(calculate_time); - time = calculate_time.get_time(); - } - if (time > 0) - update_ticktime(time); + unsigned long long time = calculate_time(symbol->interval_data_source); + if (time < 0) ERROR; + else update_ticktime(time); return NULL; } }; @@ -788,19 +771,18 @@ /* signed_integer DOTDOT signed_integer */ //SYM_REF2(subrange_c, lower_limit, upper_limit) void *visit(subrange_c *symbol) { - int dimension = extract_integer(symbol->upper_limit) - extract_integer(symbol->lower_limit) + 1; switch (current_mode) { case arrayname_im: current_array_name += "_"; { std::stringstream ss; - ss << dimension; + ss << symbol->dimension; current_array_name += ss.str(); } break; case arraydeclaration_im: s4o_incl.print("["); - s4o_incl.print_integer(dimension); + s4o_incl.print(symbol->dimension); s4o_incl.print("]"); default: generate_c_typedecl_c::visit(symbol); @@ -809,11 +791,6 @@ return NULL; } - /* var1_list ':' structure_type_name */ - void *visit(structured_var_declaration_c *symbol) { - return NULL; - } - /* var1_list ':' initialized_structure */ // SYM_REF2(structured_var_init_decl_c, var1_list, initialized_structure) void *visit(structured_var_init_decl_c *symbol) { @@ -851,6 +828,12 @@ return NULL; } + /* var1_list ':' structure_type_name */ + //SYM_REF2(structured_var_declaration_c, var1_list, structure_type_name) + void *visit(structured_var_declaration_c *symbol) { + return NULL; + } + /* VAR [CONSTANT] var_init_decl_list END_VAR */ /* option -> may be NULL ! */ /* helper symbol for input_declarations */ @@ -1676,7 +1659,8 @@ s4o.print(s4o.indent_spaces); vardecl = new generate_c_vardecl_c(&s4o, generate_c_vardecl_c::constructorinit_vf, - generate_c_vardecl_c::global_vt); + generate_c_vardecl_c::global_vt, + symbol->configuration_name); vardecl->print(symbol); delete vardecl; s4o.print("\n"); @@ -1819,7 +1803,7 @@ declaretype_t wanted_declaretype; - unsigned long common_ticktime; + unsigned long long common_ticktime; const char *current_program_name; @@ -2196,12 +2180,10 @@ current_task_name->accept(*this); s4o.print(" = "); if (symbol->interval_data_source != NULL) { - calculate_time_c calculate_time; - symbol->interval_data_source->accept(calculate_time); - unsigned long time = calculate_time.get_time(); + unsigned long long int time = calculate_time(symbol->interval_data_source); if (time != 0) { s4o.print("!(tick % "); - s4o.print_integer((int)(time / common_ticktime)); + s4o.print(time / common_ticktime); s4o.print(")"); } else @@ -2231,10 +2213,10 @@ if (var_decl == NULL) ERROR; else - vartype = search_config_instance->get_vartype(); + vartype = search_config_instance->get_vartype(current_var_reference); } else - vartype = search_resource_instance->get_vartype(); + vartype = search_resource_instance->get_vartype(current_var_reference); s4o.print(s4o.indent_spaces + "{extern "); var_decl->accept(*this); @@ -2245,7 +2227,7 @@ s4o.print("."); symbol->symbolic_variable->accept(*this); s4o.print(" = "); - if (vartype || search_var_instance_decl_c::global_vt) + if (vartype == search_var_instance_decl_c::global_vt) s4o.print("*"); symbol->prog_data_source->accept(*this); s4o.print(";}\n"); @@ -2266,17 +2248,17 @@ if (var_decl == NULL) ERROR; else - vartype = search_config_instance->get_vartype(); + vartype = search_config_instance->get_vartype(current_var_reference); } else - vartype = search_resource_instance->get_vartype(); + vartype = search_resource_instance->get_vartype(current_var_reference); s4o.print(s4o.indent_spaces + "{extern "); var_decl->accept(*this); s4o.print(" *"); symbol->data_sink->accept(*this); s4o.print("; "); - if (vartype || search_var_instance_decl_c::global_vt) + if (vartype == search_var_instance_decl_c::global_vt) s4o.print("*"); symbol->data_sink->accept(*this); s4o.print(" = "); @@ -2491,6 +2473,7 @@ if (configuration_count++) { /* the first configuration is the one we will use!! */ + STAGE4_ERROR(symbol, symbol, "A previous CONFIGURATION has already been declared (C code generation currently only allows a single configuration)."); ERROR; } @@ -2501,7 +2484,7 @@ symbol->accept(calculate_common_ticktime); common_ticktime = calculate_common_ticktime.get_common_ticktime(); if (common_ticktime == 0) { - fprintf(stderr, "\nYou must at least define a periodic task to set cycle period!"); + STAGE4_ERROR(symbol, symbol, "You must define at least one periodic task (to set cycle period)!"); ERROR; } diff -r aad38592bdde -r c0bda77b37a0 stage4/generate_c/generate_c_base.cc --- a/stage4/generate_c/generate_c_base.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/generate_c/generate_c_base.cc Wed Aug 22 16:46:17 2012 +0200 @@ -385,7 +385,7 @@ str += '"'; s4o.print("__STRING_LITERAL("); - s4o.print_integer(count); + s4o.print(count); s4o.print(","); s4o.print(str); s4o.print(")"); @@ -410,18 +410,12 @@ void *visit(duration_c *symbol) { TRACE("duration_c"); s4o.print("__time_to_timespec("); - if (NULL == symbol->neg) - s4o.print("1"); /* positive time value */ - else - symbol->neg->accept(*this); /* this will print '-1' :-) */ - - s4o.print(", "); - + + if (NULL == symbol->neg) s4o.print("1"); /* positive time value */ + else symbol->neg->accept(*this); /* this will print '-1' :-) */ + + s4o.print(", "); symbol->interval->accept(*this); - if (typeid(*symbol->interval) == typeid(hours_c)) {s4o.print(", 0");} - if (typeid(*symbol->interval) == typeid(minutes_c)) {s4o.print(", 0, 0");} - if (typeid(*symbol->interval) == typeid(seconds_c)) {s4o.print(", 0, 0, 0");} - if (typeid(*symbol->interval) == typeid(milliseconds_c)) {s4o.print(", 0, 0, 0, 0");} s4o.print(")"); return NULL; } @@ -430,73 +424,32 @@ /* SYM_TOKEN(fixed_point_c) */ void *visit(fixed_point_c *symbol) {return print_striped_token(symbol);} - -/* SYM_REF2(days_c, days, hours) */ -void *visit(days_c *symbol) { - TRACE("days_c"); - if (NULL == symbol->hours) - s4o.print("0, 0, 0, 0"); /* milliseconds, seconds, minutes, hours */ - else - symbol->hours->accept(*this); - - s4o.print(", "); - - symbol->days->accept(*this); - return NULL; -} - - -/* SYM_REF2(hours_c, hours, minutes) */ -void *visit(hours_c *symbol) { - TRACE("hours_c"); - if (NULL == symbol->minutes) - s4o.print("0, 0, 0"); /* milliseconds, seconds, minutes */ - else - symbol->minutes->accept(*this); - - s4o.print(", "); - - symbol->hours->accept(*this); - return NULL; -} - - -/* SYM_REF2(minutes_c, minutes, seconds) */ -void *visit(minutes_c *symbol) { - TRACE("minutes_c"); - if (NULL == symbol->seconds) - s4o.print("0, 0"); /* milliseconds, seconds */ - else - symbol->seconds->accept(*this); - - s4o.print(", "); - - symbol->minutes->accept(*this); - return NULL; -} - - -/* SYM_REF2(seconds_c, seconds, milliseconds) */ -void *visit(seconds_c *symbol) { - TRACE("seconds_c"); - if (NULL == symbol->milliseconds) - s4o.print("0"); /* milliseconds */ - else - symbol->milliseconds->accept(*this); - - s4o.print(", "); - - symbol->seconds->accept(*this); - return NULL; -} - - -/* SYM_REF2(milliseconds_c, milliseconds, unused) */ -void *visit(milliseconds_c *symbol) { - TRACE("milliseconds_c"); - symbol->milliseconds->accept(*this); - return NULL; -} +/* SYM_REF5(interval_c, days, hours, minutes, seconds, milliseconds) */ +void *visit(interval_c *symbol) { + TRACE("interval_c"); + /* s4o.print("0, 0, 0, 0, 0"); // milliseconds, seconds, minutes, hours, days */ + if (NULL == symbol->milliseconds) s4o.print("0"); /* milliseconds */ + else symbol->milliseconds->accept(*this); + s4o.print(", "); + + if (NULL == symbol->seconds) s4o.print("0"); /* seconds */ + else symbol->seconds->accept(*this); + s4o.print(", "); + + if (NULL == symbol->minutes) s4o.print("0"); /* minutes */ + else symbol->minutes->accept(*this); + s4o.print(", "); + + if (NULL == symbol->hours) s4o.print("0"); /* hours */ + else symbol->hours->accept(*this); + s4o.print(", "); + + if (NULL == symbol->days) s4o.print("0"); /* days */ + else symbol->days->accept(*this); + + return NULL; +} + /************************************/ /* B 1.2.3.2 - Time of day and Date */ diff -r aad38592bdde -r c0bda77b37a0 stage4/generate_c/generate_c_il.cc --- a/stage4/generate_c/generate_c_il.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/generate_c/generate_c_il.cc Wed Aug 22 16:46:17 2012 +0200 @@ -232,6 +232,7 @@ search_fb_instance_decl_c *search_fb_instance_decl; search_varfb_instance_type_c *search_varfb_instance_type; + search_var_instance_decl_c *search_var_instance_decl; search_base_type_c search_base_type; @@ -252,6 +253,8 @@ search_expression_type = new search_expression_type_c(scope); search_fb_instance_decl = new search_fb_instance_decl_c(scope); search_varfb_instance_type = new search_varfb_instance_type_c(scope); + search_var_instance_decl = new search_var_instance_decl_c(scope); + current_operand = NULL; current_operand_type = NULL; il_default_variable_init_value = NULL; @@ -267,6 +270,7 @@ delete search_fb_instance_decl; delete search_expression_type; delete search_varfb_instance_type; + delete search_var_instance_decl; } void generate(instruction_list_c *il) { @@ -435,7 +439,7 @@ } void *print_getter(symbol_c *symbol) { - unsigned int vartype = search_varfb_instance_type->get_vartype(symbol); + unsigned int vartype = search_var_instance_decl->get_vartype(symbol); if (wanted_variablegeneration == fparam_output_vg) { if (vartype == search_var_instance_decl_c::external_vt) s4o.print(GET_EXTERNAL_BY_REF); @@ -457,7 +461,7 @@ variablegeneration_t old_wanted_variablegeneration = wanted_variablegeneration; wanted_variablegeneration = complextype_base_vg; symbol->accept(*this); - if (search_varfb_instance_type->type_is_complex()) + if (search_var_instance_decl->type_is_complex(symbol)) s4o.print(","); wanted_variablegeneration = complextype_suffix_vg; symbol->accept(*this); @@ -475,8 +479,8 @@ bool type_is_complex = false; if (fb_symbol == NULL) { - unsigned int vartype = search_varfb_instance_type->get_vartype(symbol); - type_is_complex = search_varfb_instance_type->type_is_complex(); + unsigned int vartype = search_var_instance_decl->get_vartype(symbol); + type_is_complex = search_var_instance_decl->type_is_complex(symbol); if (vartype == search_var_instance_decl_c::external_vt) s4o.print(SET_EXTERNAL); else if (vartype == search_var_instance_decl_c::located_vt) @@ -520,19 +524,15 @@ public: void *visit(il_default_variable_c *symbol) { - //s4o.print("il_default_variable_c VISITOR!!\n"); symbol->var_name->accept(*this); if (NULL != symbol->current_type) { s4o.print("."); - if (search_expression_type->is_literal_integer_type(symbol->current_type)) - this->lint_type.accept(*this); - else if (search_expression_type->is_literal_real_type(this->default_variable_name.current_type)) - this->lreal_type.accept(*this); - else - symbol->current_type->accept(*this); + if ( search_expression_type->is_literal_integer_type(symbol->current_type)) this->lint_type.accept(*this); + else if ( search_expression_type->is_literal_real_type(this->default_variable_name.current_type)) this->lreal_type.accept(*this); + else if ( search_expression_type->is_bool_type(this->default_variable_name.current_type)) this->bool_type.accept(*this); + else symbol->current_type->accept(*this); s4o.print("var"); - } - return NULL; + } return NULL; } @@ -591,7 +591,7 @@ break; default: if (this->is_variable_prefix_null()) { - vartype = search_varfb_instance_type->get_vartype(symbol); + vartype = search_var_instance_decl->get_vartype(symbol); if (wanted_variablegeneration == fparam_output_vg) { s4o.print("&("); generate_c_base_c::visit(symbol); @@ -649,8 +649,7 @@ // SYM_REF2(structured_variable_c, record_variable, field_selector) void *visit(structured_variable_c *symbol) { TRACE("structured_variable_c"); - unsigned int vartype = search_varfb_instance_type->get_vartype(symbol->record_variable); - bool type_is_complex = search_varfb_instance_type->type_is_complex(); + bool type_is_complex = search_var_instance_decl->type_is_complex(symbol->record_variable); switch (wanted_variablegeneration) { case complextype_base_vg: case complextype_base_assignment_vg: @@ -983,7 +982,7 @@ print_function_parameter_data_types_c overloaded_func_suf(&s4o); f_decl->accept(overloaded_func_suf); } - s4o.print_integer(fcall_number); + s4o.print(fcall_number); } else { if (function_name != NULL) { @@ -1391,7 +1390,7 @@ print_function_parameter_data_types_c overloaded_func_suf(&s4o); f_decl->accept(overloaded_func_suf); } - s4o.print_integer(fcall_number); + s4o.print(fcall_number); } else { if (function_name != NULL) { @@ -1574,6 +1573,12 @@ return NULL; } +// SYM_REF1(il_simple_instruction_c, il_simple_instruction, symbol_c *prev_il_instruction;) +void *visit(il_simple_instruction_c *symbol) { + return symbol->il_simple_instruction->accept(*this); +} + + /* | il_initial_param_list il_param_instruction */ // SYM_LIST(il_param_list_c) void *visit(il_param_list_c *symbol) {ERROR; return NULL;} // should never get called! @@ -1652,6 +1657,12 @@ } void *visit(NOT_operator_c *symbol) { + /* NOTE: the standard allows syntax in which the NOT operator is followed by an optional + * NOT [] + * However, it does not define the semantic of the NOT operation when the is specified. + * We therefore consider it an error if an il_operand is specified! + * The error is caught in stage 3! + */ if ((NULL != this->current_operand) || (NULL != this->current_operand_type)) ERROR; XXX_operator(&(this->default_variable_name), search_expression_type->is_bool_type(this->default_variable_name.current_type)?" = !":" = ~", diff -r aad38592bdde -r c0bda77b37a0 stage4/generate_c/generate_c_inlinefcall.cc --- a/stage4/generate_c/generate_c_inlinefcall.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/generate_c/generate_c_inlinefcall.cc Wed Aug 22 16:46:17 2012 +0200 @@ -106,6 +106,7 @@ search_expression_type_c *search_expression_type; search_varfb_instance_type_c *search_varfb_instance_type; + search_var_instance_decl_c *search_var_instance_decl; search_base_type_c search_base_type; @@ -119,6 +120,8 @@ { search_expression_type = new search_expression_type_c(scope); search_varfb_instance_type = new search_varfb_instance_type_c(scope); + search_var_instance_decl = new search_var_instance_decl_c(scope); + this->set_variable_prefix(variable_prefix); current_operand = NULL; current_operand_type = NULL; @@ -132,6 +135,7 @@ virtual ~generate_c_inlinefcall_c(void) { delete search_expression_type; delete search_varfb_instance_type; + delete search_var_instance_decl; } void print(symbol_c* symbol) { @@ -175,7 +179,7 @@ if (function_type_suffix) { function_type_suffix->accept(*this); } - s4o.print_integer(fcall_number); + s4o.print(fcall_number); s4o.print("("); s4o.indent_right(); @@ -309,7 +313,7 @@ } void *print_getter(symbol_c *symbol) { - unsigned int vartype = search_varfb_instance_type->get_vartype(symbol); + unsigned int vartype = search_var_instance_decl->get_vartype(symbol); if (vartype == search_var_instance_decl_c::external_vt) s4o.print(GET_EXTERNAL); else if (vartype == search_var_instance_decl_c::located_vt) @@ -320,7 +324,7 @@ wanted_variablegeneration = complextype_base_vg; symbol->accept(*this); - if (search_varfb_instance_type->type_is_complex()) + if (search_var_instance_decl->type_is_complex(symbol)) s4o.print(","); wanted_variablegeneration = complextype_suffix_vg; symbol->accept(*this); @@ -332,7 +336,7 @@ void *print_setter(symbol_c* symbol, symbol_c* type, symbol_c* value) { - unsigned int vartype = search_varfb_instance_type->get_vartype(symbol); + unsigned int vartype = search_var_instance_decl->get_vartype(symbol); if (vartype == search_var_instance_decl_c::external_vt) s4o.print(SET_EXTERNAL); else if (vartype == search_var_instance_decl_c::located_vt) @@ -346,7 +350,7 @@ s4o.print(","); wanted_variablegeneration = expression_vg; print_check_function(type, value, NULL, true); - if (search_varfb_instance_type->type_is_complex()) { + if (search_var_instance_decl->type_is_complex(symbol)) { s4o.print(","); wanted_variablegeneration = complextype_suffix_vg; symbol->accept(*this); @@ -397,8 +401,7 @@ // SYM_REF2(structured_variable_c, record_variable, field_selector) void *visit(structured_variable_c *symbol) { TRACE("structured_variable_c"); - unsigned int vartype = search_varfb_instance_type->get_vartype(symbol->record_variable); - bool type_is_complex = search_varfb_instance_type->type_is_complex(); + bool type_is_complex = search_var_instance_decl->type_is_complex(symbol->record_variable); if (generating_inlinefunction) { switch (wanted_variablegeneration) { case complextype_base_vg: @@ -834,6 +837,11 @@ this->default_variable_back_name.current_type = this->default_variable_name.current_type; return NULL; } + + // SYM_REF1(il_simple_instruction_c, il_simple_instruction, symbol_c *prev_il_instruction;) + void *visit(il_simple_instruction_c *symbol) { + return symbol->il_simple_instruction->accept(*this); + } void *visit(LD_operator_c *symbol) { /* the data type resulting from this operation... */ diff -r aad38592bdde -r c0bda77b37a0 stage4/generate_c/generate_c_sfc.cc --- a/stage4/generate_c/generate_c_sfc.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/generate_c/generate_c_sfc.cc Wed Aug 22 16:46:17 2012 +0200 @@ -117,7 +117,7 @@ } void print_transition_number(void) { - s4o.print_integer(transition_number); + s4o.print(transition_number); } void print_reset_step(symbol_c *step_name) { @@ -564,13 +564,17 @@ s4o.print(";\n"); } s4o.indent_left(); - s4o.print(s4o.indent_spaces + "}"); + s4o.print(s4o.indent_spaces + "}\n"); if (strcmp(qualifier, "DS") == 0) { + s4o.print(s4o.indent_spaces + "if ("); s4o.print("desactivated"); + s4o.print(") {\n"); s4o.indent_right(); s4o.print(s4o.indent_spaces); print_action_argument(current_action, "set_remaining_time"); s4o.print(" = __time_to_timespec(1, 0, 0, 0, 0, 0);\n"); + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "}\n"); } } break; @@ -844,7 +848,7 @@ for(pt = variable_list.begin(); pt != variable_list.end(); pt++) { symbol_c *var_decl = search_var_instance_decl->get_decl(pt->symbol); if (var_decl != NULL) { - unsigned int vartype = search_var_instance_decl->get_vartype(); + unsigned int vartype = search_var_instance_decl->get_vartype(pt->symbol); s4o.print(s4o.indent_spaces); if (vartype == search_var_instance_decl_c::external_vt) diff -r aad38592bdde -r c0bda77b37a0 stage4/generate_c/generate_c_sfcdecl.cc --- a/stage4/generate_c/generate_c_sfcdecl.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/generate_c/generate_c_sfcdecl.cc Wed Aug 22 16:46:17 2012 +0200 @@ -89,24 +89,24 @@ /* steps table declaration */ s4o.print(s4o.indent_spaces + "STEP __step_list["); - s4o.print_integer(step_number); + s4o.print(step_number); s4o.print("];\n"); s4o.print(s4o.indent_spaces + "UINT __nb_steps;\n"); /* actions table declaration */ s4o.print(s4o.indent_spaces + "ACTION __action_list["); - s4o.print_integer(action_number); + s4o.print(action_number); s4o.print("];\n"); s4o.print(s4o.indent_spaces + "UINT __nb_actions;\n"); /* transitions table declaration */ s4o.print(s4o.indent_spaces + "__IEC_BOOL_t __transition_list["); - s4o.print_integer(transition_number); + s4o.print(transition_number); s4o.print("];\n"); /* transitions debug table declaration */ s4o.print(s4o.indent_spaces + "__IEC_BOOL_t __debug_transition_list["); - s4o.print_integer(transition_number); + s4o.print(transition_number); s4o.print("];\n"); s4o.print(s4o.indent_spaces + "UINT __nb_transitions;\n"); @@ -124,7 +124,7 @@ s4o.print(s4o.indent_spaces); print_variable_prefix(); s4o.print("__nb_steps = "); - s4o.print_integer(step_number); + s4o.print(step_number); s4o.print(";\n"); step_number = 0; wanted_sfcdeclaration = sfcinit_sd; @@ -150,7 +150,7 @@ s4o.print(s4o.indent_spaces); print_variable_prefix(); s4o.print("__nb_actions = "); - s4o.print_integer(action_number); + s4o.print(action_number); s4o.print(";\n"); action_number = 0; wanted_sfcdeclaration = sfcinit_sd; @@ -174,7 +174,7 @@ s4o.print(s4o.indent_spaces); print_variable_prefix(); s4o.print("__nb_transitions = "); - s4o.print_integer(transition_number); + s4o.print(transition_number); s4o.print(";\n"); transition_number = 0; wanted_sfcdeclaration = sfcinit_sd; @@ -199,7 +199,7 @@ s4o.print(SFC_STEP_ACTION_PREFIX); pt->symbol->accept(*this); s4o.print(" "); - s4o.print_integer(action_number); + s4o.print(action_number); s4o.print("\n"); action_number++; } @@ -251,7 +251,7 @@ s4o.print("("); print_variable_prefix(); s4o.print(",__step_list["); - s4o.print_integer(step_number); + s4o.print(step_number); s4o.print("].state,1);\n"); step_number++; break; @@ -260,7 +260,7 @@ s4o.print(SFC_STEP_ACTION_PREFIX); symbol->step_name->accept(*this); s4o.print(" "); - s4o.print_integer(step_number); + s4o.print(step_number); s4o.print("\n"); step_number++; break; @@ -292,7 +292,7 @@ s4o.print(SFC_STEP_ACTION_PREFIX); symbol->step_name->accept(*this); s4o.print(" "); - s4o.print_integer(step_number); + s4o.print(step_number); s4o.print("\n"); step_number++; break; @@ -346,7 +346,7 @@ s4o.print(SFC_STEP_ACTION_PREFIX); symbol->action_name->accept(*this); s4o.print(" "); - s4o.print_integer(action_number); + s4o.print(action_number); s4o.print("\n"); action_number++; break; diff -r aad38592bdde -r c0bda77b37a0 stage4/generate_c/generate_c_st.cc --- a/stage4/generate_c/generate_c_st.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/generate_c/generate_c_st.cc Wed Aug 22 16:46:17 2012 +0200 @@ -89,6 +89,7 @@ search_expression_type_c *search_expression_type; search_varfb_instance_type_c *search_varfb_instance_type; + search_var_instance_decl_c *search_var_instance_decl; search_base_type_c search_base_type; @@ -109,6 +110,8 @@ search_fb_instance_decl = new search_fb_instance_decl_c(scope); search_expression_type = new search_expression_type_c(scope); search_varfb_instance_type = new search_varfb_instance_type_c(scope); + search_var_instance_decl = new search_var_instance_decl_c(scope); + this->set_variable_prefix(variable_prefix); current_array_type = NULL; current_param_type = NULL; @@ -122,6 +125,7 @@ delete search_fb_instance_decl; delete search_expression_type; delete search_varfb_instance_type; + delete search_var_instance_decl; } @@ -131,11 +135,16 @@ } private: + + + + + void *print_getter(symbol_c *symbol) { - unsigned int vartype = search_varfb_instance_type->get_vartype(symbol); + unsigned int vartype = search_var_instance_decl->get_vartype(symbol); if (wanted_variablegeneration == fparam_output_vg) { - if (vartype == search_var_instance_decl_c::external_vt) + if (vartype == search_var_instance_decl_c::external_vt) s4o.print(GET_EXTERNAL_BY_REF); else if (vartype == search_var_instance_decl_c::located_vt) s4o.print(GET_LOCATED_BY_REF); @@ -143,19 +152,19 @@ s4o.print(GET_VAR_BY_REF); } else { - if (vartype == search_var_instance_decl_c::external_vt) - s4o.print(GET_EXTERNAL); - else if (vartype == search_var_instance_decl_c::located_vt) - s4o.print(GET_LOCATED); - else - s4o.print(GET_VAR); + if (vartype == search_var_instance_decl_c::external_vt) + s4o.print(GET_EXTERNAL); + else if (vartype == search_var_instance_decl_c::located_vt) + s4o.print(GET_LOCATED); + else + s4o.print(GET_VAR); } s4o.print("("); variablegeneration_t old_wanted_variablegeneration = wanted_variablegeneration; wanted_variablegeneration = complextype_base_vg; symbol->accept(*this); - if (search_varfb_instance_type->type_is_complex()) + if (search_var_instance_decl->type_is_complex(symbol)) s4o.print(","); wanted_variablegeneration = complextype_suffix_vg; symbol->accept(*this); @@ -172,8 +181,8 @@ bool type_is_complex = false; if (fb_symbol == NULL) { - unsigned int vartype = search_varfb_instance_type->get_vartype(symbol); - type_is_complex = search_varfb_instance_type->type_is_complex(); + unsigned int vartype = search_var_instance_decl->get_vartype(symbol); + type_is_complex = search_var_instance_decl->type_is_complex(symbol); if (vartype == search_var_instance_decl_c::external_vt) s4o.print(SET_EXTERNAL); else if (vartype == search_var_instance_decl_c::located_vt) @@ -239,7 +248,6 @@ /* B 1.4 - Variables */ /*********************/ void *visit(symbolic_variable_c *symbol) { - unsigned int vartype; switch (wanted_variablegeneration) { case complextype_base_assignment_vg: case assignment_vg: @@ -254,7 +262,6 @@ break; default: if (this->is_variable_prefix_null()) { - vartype = search_varfb_instance_type->get_vartype(symbol); if (wanted_variablegeneration == fparam_output_vg) { s4o.print("&("); generate_c_base_c::visit(symbol); @@ -312,8 +319,7 @@ // SYM_REF2(structured_variable_c, record_variable, field_selector) void *visit(structured_variable_c *symbol) { TRACE("structured_variable_c"); - unsigned int vartype = search_varfb_instance_type->get_vartype(symbol->record_variable); - bool type_is_complex = search_varfb_instance_type->type_is_complex(); + bool type_is_complex = search_var_instance_decl->type_is_complex(symbol->record_variable); switch (wanted_variablegeneration) { case complextype_base_vg: case complextype_base_assignment_vg: @@ -359,7 +365,7 @@ case complextype_suffix_vg: symbol->subscripted_variable->accept(*this); - current_array_type = search_varfb_instance_type->get_type_id(symbol->subscripted_variable); + current_array_type = search_varfb_instance_type->get_basetype_decl(symbol->subscripted_variable); if (current_array_type == NULL) ERROR; s4o.print(".table"); @@ -373,7 +379,7 @@ if (this->is_variable_prefix_null()) { symbol->subscripted_variable->accept(*this); - current_array_type = search_varfb_instance_type->get_type_id(symbol->subscripted_variable); + current_array_type = search_varfb_instance_type->get_basetype_decl(symbol->subscripted_variable); if (current_array_type == NULL) ERROR; s4o.print(".table"); @@ -797,7 +803,7 @@ print_function_parameter_data_types_c overloaded_func_suf(&s4o); f_decl->accept(overloaded_func_suf); } - s4o.print_integer(fcall_number); + s4o.print(fcall_number); } else { function_name->accept(*this); diff -r aad38592bdde -r c0bda77b37a0 stage4/generate_c/generate_c_typedecl.cc --- a/stage4/generate_c/generate_c_typedecl.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/generate_c/generate_c_typedecl.cc Wed Aug 22 16:46:17 2012 +0200 @@ -75,18 +75,6 @@ basetypedeclaration_t current_basetypedeclaration; - void print_integer(unsigned int integer) { - char str[10]; - sprintf(str, "%d", integer); - s4o.print(str); - } - - void print_integer_incl(unsigned int integer) { - char str[10]; - sprintf(str, "%d", integer); - s4o_incl.print(str); - } - void *print_list_incl(list_c *list, std::string pre_elem_str = "", std::string inter_elem_str = "", @@ -255,8 +243,7 @@ case array_td: if (current_basetypedeclaration == arraysubrange_bd) { s4o_incl.print("["); - dimension = extract_integer(symbol->upper_limit) - extract_integer(symbol->lower_limit) + 1; - print_integer_incl(dimension); + s4o_incl.print(symbol->dimension); s4o_incl.print("]"); } else diff -r aad38592bdde -r c0bda77b37a0 stage4/generate_c/generate_c_vardecl.cc --- a/stage4/generate_c/generate_c_vardecl.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/generate_c/generate_c_vardecl.cc Wed Aug 22 16:46:17 2012 +0200 @@ -22,6 +22,8 @@ * used in safety-critical situations without a full and competent review. */ +#include // required for std::numeric_limits + class initialization_analyzer_c: public null_visitor_c { public: typedef enum { @@ -74,9 +76,9 @@ private: int current_dimension; - int array_size; - int defined_values_count; - int current_initialization_count; + unsigned long long int array_size; + unsigned long long int defined_values_count; + unsigned long long int current_initialization_count; public: generate_c_array_initialization_c(stage4out_c *s4o_ptr): generate_c_typedecl_c(s4o_ptr) {} @@ -124,7 +126,7 @@ if (array_default_initialization != NULL && defined_values_count < array_size) array_default_initialization->accept(*this); if (defined_values_count < array_size) { - for (int i = defined_values_count; i < array_size; i++) { + for (unsigned long long int i = defined_values_count; i < array_size; i++) { if (defined_values_count > 0) s4o.print(","); array_default_value->accept(*this); @@ -211,14 +213,17 @@ /* signed_integer DOTDOT signed_integer */ //SYM_REF2(subrange_c, lower_limit, upper_limit) void *visit(subrange_c *symbol) { - int dimension = extract_integer(symbol->upper_limit) - extract_integer(symbol->lower_limit) + 1; switch (current_mode) { case arraysize_am: - array_size *= dimension; + /* res = a * b; ---> Check for overflow by pre-condition: If (UINT_MAX / a) < b => overflow! */ + if ((std::numeric_limits< unsigned long long int >::max() / array_size) < symbol->dimension) + STAGE4_ERROR(symbol, symbol, "The array containing this subrange has a total number of elements larger than the maximum currently supported (%llu).", + std::numeric_limits< unsigned long long int >::max()); + array_size *= symbol->dimension; break; case typedecl_am: s4o.print("_"); - s4o.print_integer(dimension); + s4o.print(symbol->dimension); break; default: break; @@ -260,28 +265,37 @@ /* integer '(' [array_initial_element] ')' */ /* array_initial_element may be NULL ! */ void *visit(array_initial_elements_c *symbol) { - int initial_element_number; + unsigned long long int initial_element_count; + + /* This code assumes that unsigned long long int is >= uint64_t */ + if (std::numeric_limits< uint64_t >::max() > std::numeric_limits< unsigned long long int >::max()) + ERROR_MSG("Assertion (sizeof(uint64_t) > sizeof(unsigned long long int)) failed! Compiler cannot execute correctly on the current platform!"); switch (current_mode) { case initializationvalue_am: - initial_element_number = extract_integer(symbol->integer); + if (VALID_CVALUE( int64, symbol->integer) && (GET_CVALUE( int64, symbol->integer) >= 0)) + initial_element_count = GET_CVALUE( int64, symbol->integer); + else if (VALID_CVALUE(uint64, symbol->integer)) + initial_element_count = GET_CVALUE(uint64, symbol->integer); + else ERROR; + if (current_initialization_count < defined_values_count) { - int temp_element_number = 0; - int diff = defined_values_count - current_initialization_count; - if (diff <= initial_element_number) - temp_element_number = initial_element_number - diff; - current_initialization_count += initial_element_number - 1; - initial_element_number = temp_element_number; - if (initial_element_number > 0) { + unsigned long long int temp_element_number = 0; + unsigned long long int diff = defined_values_count - current_initialization_count; + if (diff <= initial_element_count) + temp_element_number = initial_element_count - diff; + current_initialization_count += initial_element_count - 1; + initial_element_count = temp_element_number; + if (initial_element_count > 0) { defined_values_count++; s4o.print(","); } } else - current_initialization_count += initial_element_number - 1; - if (defined_values_count + initial_element_number > array_size) + current_initialization_count += initial_element_count - 1; + if (defined_values_count + initial_element_count > array_size) ERROR; - for (int i = 0; i < initial_element_number; i++) { + for (unsigned long long int i = 0; i < initial_element_count; i++) { if (i > 0) s4o.print(","); if (symbol->array_initial_element != NULL) { @@ -291,8 +305,8 @@ array_default_value->accept(*this); } } - if (initial_element_number > 1) - defined_values_count += initial_element_number - 1; + if (initial_element_count > 1) + defined_values_count += initial_element_count - 1; break; default: break; @@ -1407,9 +1421,8 @@ /* signed_integer DOTDOT signed_integer */ //SYM_REF2(subrange_c, lower_limit, upper_limit) void *visit(subrange_c *symbol) { - int dimension = extract_integer(symbol->upper_limit) - extract_integer(symbol->lower_limit) + 1; s4o.print("_"); - print_integer(dimension); + s4o.print(symbol->dimension); return NULL; } @@ -1597,7 +1610,7 @@ } void *visit(structure_element_initialization_list_c *symbol) { - if (wanted_varformat == localinit_vf) { + if (wanted_varformat == localinit_vf || wanted_varformat == constructorinit_vf) { generate_c_structure_initialization_c *structure_initialization = new generate_c_structure_initialization_c(&s4o); structure_initialization->init_structure_default(this->current_var_type_symbol); structure_initialization->init_structure_values(this->current_var_init_symbol); diff -r aad38592bdde -r c0bda77b37a0 stage4/generate_iec/generate_iec.cc --- a/stage4/generate_iec/generate_iec.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/generate_iec/generate_iec.cc Wed Aug 22 16:46:17 2012 +0200 @@ -44,6 +44,8 @@ +/* Compile with the following option, to print out the const_value of each symbol, which was filled in by constant_folding_c during stage3 */ +// #define DEBUG_CONST_VALUE @@ -55,6 +57,8 @@ #include "generate_iec.hh" #include "../stage4.hh" +#include "../../main.hh" // required for ERROR() and ERROR_MSG() macros. + @@ -74,11 +78,63 @@ private: + +void print_const_value(symbol_c *symbol) { +#ifdef DEBUG_CONST_VALUE + if (NULL == symbol) return; + bool first = true; + + if (NULL != symbol->const_value_uint64) { + first?s4o.print("{"):s4o.print("; "); + first = false; + s4o.print("uint64:"); + if (symbol->const_value_uint64->status == symbol_c::cs_const_value) + s4o.print_uint64(symbol->const_value_uint64->value); + if (symbol->const_value_uint64->status == symbol_c::cs_overflow) + s4o.print("OVERFLOW"); + } + if (NULL != symbol->const_value_int64) { + first?s4o.print("{"):s4o.print("; "); + first = false; + s4o.print("int64:"); + if (symbol->const_value_int64->status == symbol_c::cs_const_value) + s4o.print_int64(symbol->const_value_int64->value); + if (symbol->const_value_int64->status == symbol_c::cs_overflow) + s4o.print("OVERFLOW"); + } + if (NULL != symbol->const_value_real64) { + first?s4o.print("{"):s4o.print("; "); + first = false; + s4o.print("real64:"); + if (symbol->const_value_real64->status == symbol_c::cs_const_value) + s4o.print_real64(symbol->const_value_real64->value); + if (symbol->const_value_real64->status == symbol_c::cs_overflow) + s4o.print("OVERFLOW"); + } + if (NULL != symbol->const_value_bool) { + first?s4o.print("{"):s4o.print("; "); + first = false; + s4o.print("bool:"); + if (symbol->const_value_bool->status == symbol_c::cs_const_value) + s4o.print((symbol->const_value_bool->value)?"true":"false"); + if (symbol->const_value_bool->status == symbol_c::cs_overflow) + s4o.print("OVERFLOW"); + } + if (!first) s4o.print("}"); +#endif + return; +} + + + void *print_token(token_c *token) { + print_const_value(token); return s4o.print(token->value); } + void *print_literal(symbol_c *type, symbol_c *value) { + print_const_value(value); if (NULL != type) { type->accept(*this); s4o.print("#"); @@ -108,9 +164,11 @@ } -void *print_binary_expression(symbol_c *l_exp, +void *print_binary_expression(symbol_c *symbol, + symbol_c *l_exp, symbol_c *r_exp, const char *operation) { + print_const_value(symbol); s4o.print("("); l_exp->accept(*this); s4o.print(operation); @@ -119,8 +177,10 @@ return NULL; } -void *print_unary_expression(symbol_c *exp, +void *print_unary_expression(symbol_c *symbol, + symbol_c *exp, const char *operation) { + print_const_value(symbol); s4o.print(operation); exp->accept(*this); return NULL; @@ -153,6 +213,13 @@ #endif +/* A class used to identify an entry (literal, variable, etc...) in the abstract syntax tree with an invalid data type */ +/* This is only used from stage3 onwards. Stages 1 and 2 will never create any instances of invalid_type_name_c */ +// SYM_REF0(invalid_type_name_c) +void *visit(invalid_type_name_c *symbol) { + ERROR; + return NULL; +} /******************/ @@ -181,9 +248,9 @@ /* B 1.2.1 - Numeric Literals */ /******************************/ void *visit(real_c *symbol) {return print_token(symbol);} -void *visit(neg_real_c *symbol) {return print_unary_expression(symbol->exp, "-");} +void *visit(neg_real_c *symbol) {return print_unary_expression(symbol, symbol->exp, "-");} void *visit(integer_c *symbol) {return print_token(symbol);} -void *visit(neg_integer_c *symbol) {return print_unary_expression(symbol->exp, "-");} +void *visit(neg_integer_c *symbol) {return print_unary_expression(symbol, symbol->exp, "-");} void *visit(binary_integer_c *symbol) {return print_token(symbol);} void *visit(octal_integer_c *symbol) {return print_token(symbol);} void *visit(hex_integer_c *symbol) {return print_token(symbol);} @@ -223,43 +290,37 @@ void *visit(fixed_point_c *symbol) {return print_token(symbol);} -void *visit(days_c *symbol) { - symbol->days->accept(*this); - s4o.print("d"); - if (symbol->hours != NULL) +/* SYM_REF5(interval_c, days, hours, minutes, seconds, milliseconds) */ +void *visit(interval_c *symbol) { + if (NULL != symbol->days) { + symbol->days->accept(*this); + s4o.print("d"); + } + + if (NULL != symbol->hours) { symbol->hours->accept(*this); - return NULL; -} - -void *visit(hours_c *symbol) { - symbol->hours->accept(*this); - s4o.print("h"); - if (symbol->minutes != NULL) + s4o.print("h"); + } + + if (NULL != symbol->minutes) { symbol->minutes->accept(*this); - return NULL; -} - -void *visit(minutes_c *symbol) { - symbol->minutes->accept(*this); - s4o.print("m"); - if (symbol->seconds != NULL) + s4o.print("m"); + } + + if (NULL != symbol->seconds) { symbol->seconds->accept(*this); - return NULL; -} - -void *visit(seconds_c *symbol) { - symbol->seconds->accept(*this); - s4o.print("s"); - if (symbol->milliseconds != NULL) + s4o.print("s"); + } + + if (NULL != symbol->milliseconds) { symbol->milliseconds->accept(*this); - return NULL; -} - -void *visit(milliseconds_c *symbol) { - symbol->milliseconds->accept(*this); - s4o.print("ms"); - return NULL; -} + s4o.print("ms"); + } + + return NULL; +} + + /************************************/ /* B 1.2.3.2 - Time of day and Date */ @@ -1670,6 +1731,17 @@ return print_list(symbol, s4o.indent_spaces, "\n" + s4o.indent_spaces, "\n"); } + +/* il_simple_instruction: + * il_simple_operation eol_list + * | il_expression eol_list + * | il_formal_funct_call eol_list + */ +void *visit(il_simple_instruction_c *symbol) { + return symbol->il_simple_instruction->accept(*this); +} + + /* | il_initial_param_list il_param_instruction */ void *visit(il_param_list_c *symbol) { // return print_list(symbol); @@ -1769,23 +1841,23 @@ /***********************/ /* B 3.1 - Expressions */ /***********************/ -void *visit(or_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " OR ");} -void *visit(xor_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " XOR ");} -void *visit(and_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " AND ");} -void *visit(equ_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " = ");} -void *visit(notequ_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " <> ");} -void *visit(lt_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " < ");} -void *visit(gt_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " > ");} -void *visit(le_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " <= ");} -void *visit(ge_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " >= ");} -void *visit(add_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " + ");} -void *visit(sub_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " - ");} -void *visit(mul_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " * ");} -void *visit(div_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " / ");} -void *visit(mod_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " MOD ");} -void *visit(power_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " ** ");} -void *visit(neg_expression_c *symbol) {return print_unary_expression(symbol->exp, "-");} -void *visit(not_expression_c *symbol) {return print_unary_expression(symbol->exp, "NOT ");} +void *visit(or_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " OR ");} +void *visit(xor_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " XOR ");} +void *visit(and_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " AND ");} +void *visit(equ_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " = ");} +void *visit(notequ_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " <> ");} +void *visit(lt_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " < ");} +void *visit(gt_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " > ");} +void *visit(le_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " <= ");} +void *visit(ge_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " >= ");} +void *visit(add_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " + ");} +void *visit(sub_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " - ");} +void *visit(mul_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " * ");} +void *visit(div_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " / ");} +void *visit(mod_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " MOD ");} +void *visit(power_expression_c *symbol) {return print_binary_expression(symbol, symbol->l_exp, symbol->r_exp, " ** ");} +void *visit(neg_expression_c *symbol) {return print_unary_expression(symbol, symbol->exp, "-");} +void *visit(not_expression_c *symbol) {return print_unary_expression(symbol, symbol->exp, "NOT ");} void *visit(function_invocation_c *symbol) { symbol->function_name->accept(*this); diff -r aad38592bdde -r c0bda77b37a0 stage4/stage4.cc --- a/stage4/stage4.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/stage4.cc Wed Aug 22 16:46:17 2012 +0200 @@ -46,9 +46,30 @@ #include #include "stage4.hh" - - - +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. + + + + +#define FIRST_(symbol1, symbol2) (((symbol1)->first_order < (symbol2)->first_order) ? (symbol1) : (symbol2)) +#define LAST_(symbol1, symbol2) (((symbol1)->last_order > (symbol2)->last_order) ? (symbol1) : (symbol2)) +#include + +void stage4err(const char *stage4_generator_id, symbol_c *symbol1, symbol_c *symbol2, const char *errmsg, ...) { + va_list argptr; + va_start(argptr, errmsg); /* second argument is last fixed pamater of stage4err() */ + + if ((symbol1 != NULL) && (symbol2 != NULL)) + fprintf(stderr, "%s:%d-%d..%d-%d: ", + FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column, + LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column); + + fprintf(stderr, "error %s: ", stage4_generator_id); + vfprintf(stderr, errmsg, argptr); + fprintf(stderr, "\n"); + // error_count++; + va_end(argptr); +} @@ -57,6 +78,7 @@ out = &std::cout; this->indent_level = indent_level; this->indent_spaces = ""; + allow_output = true; } stage4out_c::stage4out_c(const char *dir, const char *radix, const char *extension, std::string indent_level) { @@ -114,18 +136,18 @@ indent_spaces.erase(); } - -void *stage4out_c::print(const char *str) { - if (!allow_output) return NULL; - *out << str; - return NULL; -} - -void *stage4out_c::print_integer(int integer) { - if (!allow_output) return NULL; - *out << integer; - return NULL; -} +void *stage4out_c::print( std::string value) {if (!allow_output) return NULL; *out << value; return NULL;} +void *stage4out_c::print( const char *value) {if (!allow_output) return NULL; *out << value; return NULL;} +//void *stage4out_c::print( int64_t value) {if (!allow_output) return NULL; *out << value; return NULL;} +//void *stage4out_c::print( uint64_t value) {if (!allow_output) return NULL; *out << value; return NULL;} +void *stage4out_c::print( real64_t value) {if (!allow_output) return NULL; *out << value; return NULL;} +void *stage4out_c::print( int value) {if (!allow_output) return NULL; *out << value; return NULL;} +void *stage4out_c::print( long int value) {if (!allow_output) return NULL; *out << value; return NULL;} +void *stage4out_c::print( long long int value) {if (!allow_output) return NULL; *out << value; return NULL;} +void *stage4out_c::print(unsigned int value) {if (!allow_output) return NULL; *out << value; return NULL;} +void *stage4out_c::print(unsigned long int value) {if (!allow_output) return NULL; *out << value; return NULL;} +void *stage4out_c::print(unsigned long long int value) {if (!allow_output) return NULL; *out << value; return NULL;} + void *stage4out_c::print_long_integer(unsigned long l_integer, bool suffix) { if (!allow_output) return NULL; @@ -141,6 +163,7 @@ return NULL; } + void *stage4out_c::printupper(const char *str) { if (!allow_output) return NULL; for (int i = 0; str[i] != '\0'; i++) @@ -174,12 +197,6 @@ } -void *stage4out_c::print(std::string str) { - if (!allow_output) return NULL; - *out << str; - return NULL; -} - void *stage4out_c::printupper(std::string str) { if (!allow_output) return NULL; @@ -227,8 +244,7 @@ stage4out_c s4o; visitor_c *generate_code = new_code_generator(&s4o, builddir); - if (NULL == generate_code) - return -1; + if (NULL == generate_code) ERROR; tree_root->accept(*generate_code); diff -r aad38592bdde -r c0bda77b37a0 stage4/stage4.hh --- a/stage4/stage4.hh Tue Aug 14 19:40:01 2012 +0200 +++ b/stage4/stage4.hh Wed Aug 22 16:46:17 2012 +0200 @@ -39,6 +39,10 @@ #include "../absyntax/absyntax.hh" + +void stage4err(const char *stage4_generator_id, symbol_c *symbol1, symbol_c *symbol2, const char *errmsg, ...); + + class stage4out_c { public: std::string indent_level; @@ -57,13 +61,22 @@ void indent_right(void); void indent_left(void); - void *print(const char *str); - void *print(std::string str); + void *print( std::string value); + void *print( const char *value); + //void *print( int64_t value); // not required, since we have long long int, or similar + //void *print( uint64_t value); // not required, since we have long long int, or similar + void *print( real64_t value); + void *print( int value); + void *print( long int value); + void *print( long long int value); + void *print(unsigned int value); + void *print(unsigned long int value); + void *print(unsigned long long int value); - void *print_integer(int integer); void *print_long_integer(unsigned long l_integer, bool suffix=true); void *print_long_long_integer(unsigned long long ll_integer, bool suffix=true); + void *printupper(const char *str); void *printupper(std::string str); diff -r aad38592bdde -r c0bda77b37a0 util/dsymtable.cc --- a/util/dsymtable.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/util/dsymtable.cc Wed Aug 22 16:46:17 2012 +0200 @@ -33,14 +33,10 @@ #include #include "symtable.hh" +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. -/* 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); - diff -r aad38592bdde -r c0bda77b37a0 util/symtable.cc --- a/util/symtable.cc Tue Aug 14 19:40:01 2012 +0200 +++ b/util/symtable.cc Wed Aug 22 16:46:17 2012 +0200 @@ -34,14 +34,10 @@ #include #include "symtable.hh" +#include "../main.hh" // required for ERROR() and ERROR_MSG() macros. -/* 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); -