Add support for the 'NULL' keyword, defined in version 3 of IEC 61131-3.
--- a/absyntax/absyntax.def Tue Jul 29 13:39:40 2014 +0100
+++ b/absyntax/absyntax.def Thu Jul 31 17:49:44 2014 +0100
@@ -130,6 +130,11 @@
/*********************/
/* B 1.2 - Constants */
/*********************/
+/*********************************/
+/* B 1.2.XX - Reference Literals */
+/*********************************/
+/* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */
+SYM_REF0(ref_value_null_literal_c)
/******************************/
/* B 1.2.1 - Numeric Literals */
--- a/absyntax_utils/get_datatype_info.cc Tue Jul 29 13:39:40 2014 +0100
+++ b/absyntax_utils/get_datatype_info.cc Thu Jul 31 17:49:44 2014 +0100
@@ -248,9 +248,13 @@
(typeid(*first_type) == typeid(*second_type))) {return true;}
/* ANY_DERIVED */
- if (is_ref_to(first_type) && is_ref_to(second_type))
- return is_type_equal(search_base_type_c::get_basetype_decl(get_ref_to(first_type )),
- search_base_type_c::get_basetype_decl(get_ref_to(second_type)));
+ if (is_ref_to(first_type) && is_ref_to(second_type)) {
+ /* if either of them is the constant 'NULL' then we consider them 'equal', as NULL is compatible to a REF_TO any datatype! */
+ if (typeid(* first_type) == typeid(ref_value_null_literal_c)) {return true;}
+ if (typeid(*second_type) == typeid(ref_value_null_literal_c)) {return true;}
+ return is_type_equal(search_base_type_c::get_basetype_decl(get_ref_to(first_type )),
+ search_base_type_c::get_basetype_decl(get_ref_to(second_type)));
+ }
return (first_type == second_type);
}
@@ -295,6 +299,7 @@
if (typeid(*type_decl) == typeid(ref_type_decl_c)) {return true;} /* identifier ':' ref_spec_init */
if (typeid(*type_decl) == typeid(ref_spec_init_c)) {return true;} /* ref_spec [ ASSIGN ref_initialization ]; */
if (typeid(*type_decl) == typeid(ref_spec_c)) {return true;} /* REF_TO (non_generic_type_name | function_block_type_name) */
+ if (typeid(*type_decl) == typeid(ref_value_null_literal_c)) {return true;} /* REF_TO (non_generic_type_name | function_block_type_name) */
return false;
}
--- a/absyntax_utils/search_base_type.cc Tue Jul 29 13:39:40 2014 +0100
+++ b/absyntax_utils/search_base_type.cc Thu Jul 31 17:49:44 2014 +0100
@@ -133,6 +133,12 @@
/*********************/
/* B 1.2 - Constants */
/*********************/
+/*********************************/
+/* B 1.2.XX - Reference Literals */
+/*********************************/
+/* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */
+/* See the comment in fill_candidate_datatypes_c::visit(ref_value_null_literal_c) for reason why we use this symbol as a datatype! */
+void *search_base_type_c::visit(ref_value_null_literal_c *symbol) {return (void *)symbol;}
/******************************/
/* B 1.2.1 - Numeric Literals */
--- a/absyntax_utils/search_base_type.hh Tue Jul 29 13:39:40 2014 +0100
+++ b/absyntax_utils/search_base_type.hh Thu Jul 31 17:49:44 2014 +0100
@@ -94,6 +94,12 @@
/*********************/
/* B 1.2 - Constants */
/*********************/
+ /*********************************/
+ /* B 1.2.XX - Reference Literals */
+ /*********************************/
+ /* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */
+ void *visit(ref_value_null_literal_c *symbol);
+
/******************************/
/* B 1.2.1 - Numeric Literals */
/******************************/
--- a/stage1_2/iec_bison.yy Tue Jul 29 13:39:40 2014 +0100
+++ b/stage1_2/iec_bison.yy Thu Jul 31 17:49:44 2014 +0100
@@ -414,9 +414,10 @@
%type <leaf> en_identifier
%type <leaf> eno_identifier
-/* Keyword in IEC 61131-3 v3 */
+/* Keywords in IEC 61131-3 v3 */
%token REF
%token REF_TO
+%token NULL_token /* cannot use simply 'NULL', as it conflicts with the NULL keyword in C++ */
@@ -445,6 +446,23 @@
%type <leaf> constant
%type <leaf> non_int_or_real_constant
+/*********************************/
+/* B 1.2.XX - Reference Literals */
+/*********************************/
+/* NOTE: The following syntax was added by MJS in order to add support for the NULL keyword, defined in IEC 61131-3 v3
+ * In v3 expressions that reduce to a reference datatype (REF_TO) are handled explicitly in the syntax
+ * (e.g., any variable that is a of reference datatpe falls under the 'ref_name' rule), which means
+ * that we would need to keep track of which variables are declared as REF_TO.
+ * In order to reduce the changes to the current IEC 61131-3 v2 syntax, I have opted not to do this,
+ * and simply let the ref_expressions (Ref_Assign, Ref_Compare) be interpreted as all other standard expressions
+ * in v2. However, ref_expressions allow the use of the 'NULL' constant, which is handled explicitly
+ * in the ref_expressions syntax of v3.
+ * To allow the use of the 'NULL' constant in this extended v2, I have opted to interpret this 'NULL' constant
+ * as a literal.
+ */
+%type <leaf> ref_value_null_literal /* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */
+
+
/******************************/
/* B 1.2.1 - Numeric Literals */
/******************************/
@@ -1777,6 +1795,7 @@
* rules, is not defined in the spec!
* We therefore replaced unsigned_integer as integer
*/
+| ref_value_null_literal /* defined in IEC 61131-3 v3. Basically the 'NULL' keyword! */
;
@@ -1808,6 +1827,16 @@
*/
;
+/*********************************/
+/* B 1.2.XX - Reference Literals */
+/*********************************/
+/* NOTE: The following syntax was added by MJS in order to add support for the NULL keyword, defined in IEC 61131-3 v3
+ * Please read the comment where the 'ref_value_null_literal' is declared as a <leaf>
+ */
+/* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */
+ref_value_null_literal:
+ NULL_token {$$ = new ref_value_null_literal_c(locloc(@$));}
+;
/******************************/
/* B 1.2.1 - Numeric Literals */
--- a/stage1_2/iec_flex.ll Tue Jul 29 13:39:40 2014 +0100
+++ b/stage1_2/iec_flex.ll Thu Jul 31 17:49:44 2014 +0100
@@ -1251,8 +1251,9 @@
/******************************************************/
-REF {if (get_opt_ref_operator()) return REF; else{REJECT;}} /* Keyword in IEC 61131-3 v3 */
-REF_TO {if (get_opt_ref_operator()) return REF_TO; else{REJECT;}} /* Keyword in IEC 61131-3 v3 */
+REF {if (get_opt_ref_operator()) return REF; else{REJECT;}} /* Keyword in IEC 61131-3 v3 */
+REF_TO {if (get_opt_ref_operator()) return REF_TO; else{REJECT;}} /* Keyword in IEC 61131-3 v3 */
+NULL {if (get_opt_ref_operator()) return NULL_token; else{REJECT;}} /* Keyword in IEC 61131-3 v3 */
EN return EN; /* Keyword */
ENO return ENO; /* Keyword */
--- a/stage3/fill_candidate_datatypes.cc Tue Jul 29 13:39:40 2014 +0100
+++ b/stage3/fill_candidate_datatypes.cc Thu Jul 31 17:49:44 2014 +0100
@@ -662,13 +662,15 @@
/* handle the two equality comparison operations, i.e. = (euqal) and != (not equal) */
/* This function is special, as it will also allow enumeration data types to be compared, with the result being a BOOL data type!
- * This possibility os not expressed in the 'widening' tables, so we need to hard code it here
+ * It will also allow to REF_TO datatypes to be compared.
+ * These possibilities are not expressed in the 'widening' tables, so we need to hard code it here
*/
void *fill_candidate_datatypes_c::handle_equality_comparison(const struct widen_entry widen_table[], symbol_c *symbol, symbol_c *l_expr, symbol_c *r_expr) {
handle_binary_expression(widen_table, symbol, l_expr, r_expr);
for(unsigned int i = 0; i < l_expr->candidate_datatypes.size(); i++)
for(unsigned int j = 0; j < r_expr->candidate_datatypes.size(); j++) {
- if ((l_expr->candidate_datatypes[i] == r_expr->candidate_datatypes[j]) && get_datatype_info_c::is_enumerated(l_expr->candidate_datatypes[i]))
+ if ( (get_datatype_info_c::is_enumerated(l_expr->candidate_datatypes[i]) && (l_expr->candidate_datatypes[i] == r_expr->candidate_datatypes[j]))
+ || (get_datatype_info_c::is_ref_to (l_expr->candidate_datatypes[i]) && get_datatype_info_c::is_type_equal(l_expr->candidate_datatypes[i], r_expr->candidate_datatypes[j])))
add_datatype_to_candidate_list(symbol, &get_datatype_info_c::bool_type_name);
}
return NULL;
@@ -689,15 +691,48 @@
/***************************/
/* main entry function! */
void *fill_candidate_datatypes_c::visit(library_c *symbol) {
- symbol->accept(populate_globalenumvalue_symtable);
- /* Now let the base class iterator_visitor_c iterate through all the library elements */
- return iterator_visitor_c::visit(symbol);
+ symbol->accept(populate_globalenumvalue_symtable);
+ /* Now let the base class iterator_visitor_c iterate through all the library elements */
+ return iterator_visitor_c::visit(symbol);
}
/*********************/
/* B 1.2 - Constants */
/*********************/
+/*********************************/
+/* B 1.2.XX - Reference Literals */
+/*********************************/
+/* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */
+void *fill_candidate_datatypes_c::visit(ref_value_null_literal_c *symbol) {
+ /* 'NULL' does not have any specific datatype. It is compatible with any reference, i.e. REF_TO <anything>
+ * The fill_candidate_datatypes / narrow_candidate_datatypes algorithm would require us to add
+ * as possible datatypes all the REF_TO <datatype>. To do this we would need to go through the list of all
+ * user declared datatypes, as well as all the elementary datatypes. This is easily done by using the
+ * type_symtable symbol table declared in absyntax_utils.hh.
+ *
+ * for(int i=0; i<type_symtable.n; i++) add_datatype_to_candidate_list(symbol, new ref_spec_c(type_symtable[i]));
+ * add_datatype_to_candidate_list(symbol, new ref_spec_c( ... SINT ...));
+ * add_datatype_to_candidate_list(symbol, new ref_spec_c( ... INT ...));
+ * add_datatype_to_candidate_list(symbol, new ref_spec_c( ... LINT ...));
+ * ...
+ *
+ * However, doing this for all NULL constants that may show up is probably a little too crazy, just for
+ * the 'pleasure' of following the standard fill/narrow algorithm.
+ *
+ * I have therefore opted to handle this as a special case: We use the ref_value_null_literal_c symbol itself as the NULL datatype!
+ * This implies the following changes:
+ * - We change the get_datatype_info_c::is_type_equal() to take the NULL datatype into account
+ * - We change the get_datatype_info_c::is_ref_to() to take the NULL datatype into account
+ * - We change the fill_candidate_datatypes_c::visit(assignment_statement_c) to make sure it uses the datatype of the lvalue
+ * as the datatype of the assignment statement
+ * - We search_base_type_c::get_basetype_decl
+ */
+ add_datatype_to_candidate_list(symbol, symbol);
+ return NULL;
+}
+
+
/******************************/
/* B 1.2.1 - Numeric Literals */
/******************************/
@@ -2001,7 +2036,6 @@
/*********************************/
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++) {
@@ -2009,7 +2043,7 @@
left_type = symbol->l_exp->candidate_datatypes[i];
right_type = symbol->r_exp->candidate_datatypes[j];
if (get_datatype_info_c::is_type_equal(left_type, right_type))
- add_datatype_to_candidate_list(symbol, left_type);
+ add_datatype_to_candidate_list(symbol, left_type); // NOTE: Must use left_type, as the right_type may be the 'NULL' reference! (see comment in visit(ref_value_null_literal_c)) */
}
}
if (debug) std::cout << ":= [" << symbol->l_exp->candidate_datatypes.size() << "," << symbol->r_exp->candidate_datatypes.size() << "] ==> " << symbol->candidate_datatypes.size() << " result.\n";
--- a/stage3/fill_candidate_datatypes.hh Tue Jul 29 13:39:40 2014 +0100
+++ b/stage3/fill_candidate_datatypes.hh Thu Jul 31 17:49:44 2014 +0100
@@ -121,6 +121,12 @@
/*********************/
/* B 1.2 - Constants */
/*********************/
+ /*********************************/
+ /* B 1.2.XX - Reference Literals */
+ /*********************************/
+ /* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */
+ void *visit(ref_value_null_literal_c *symbol);
+
/******************************/
/* B 1.2.1 - Numeric Literals */
/******************************/
--- a/stage3/narrow_candidate_datatypes.cc Tue Jul 29 13:39:40 2014 +0100
+++ b/stage3/narrow_candidate_datatypes.cc Thu Jul 31 17:49:44 2014 +0100
@@ -1412,7 +1412,10 @@
l_expr->datatype = l_type;
r_expr->datatype = r_type;
count ++;
- } else if ((l_type == r_type) && get_datatype_info_c::is_enumerated(l_type) && get_datatype_info_c::is_BOOL_compatible(symbol->datatype)) {
+ } else if ( /* handle the special case of enumerations */
+ (get_datatype_info_c::is_BOOL_compatible(symbol->datatype) && get_datatype_info_c::is_enumerated(l_type) && (l_type == r_type))
+ /* handle the special case of comparison between REF_TO datatypes */
+ || (get_datatype_info_c::is_BOOL_compatible(symbol->datatype) && get_datatype_info_c::is_ref_to (l_type) && get_datatype_info_c::is_type_equal(l_type, r_type))) {
if (NULL != deprecated_operation) *deprecated_operation = false;
l_expr->datatype = l_type;
r_expr->datatype = r_type;
--- a/stage4/generate_c/generate_c_base.cc Tue Jul 29 13:39:40 2014 +0100
+++ b/stage4/generate_c/generate_c_base.cc Thu Jul 31 17:49:44 2014 +0100
@@ -303,6 +303,12 @@
/*********************/
/* originally empty... */
+/*********************************/
+/* B 1.2.XX - Reference Literals */
+/*********************************/
+/* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */
+ void *visit(ref_value_null_literal_c *symbol) {s4o.print("NULL"); return NULL;}
+
/******************************/
/* B 1.2.1 - Numeric Literals */
/******************************/
--- a/stage4/generate_iec/generate_iec.cc Tue Jul 29 13:39:40 2014 +0100
+++ b/stage4/generate_iec/generate_iec.cc Thu Jul 31 17:49:44 2014 +0100
@@ -258,6 +258,11 @@
/*********************/
/* B 1.2 - Constants */
/*********************/
+/*********************************/
+/* B 1.2.XX - Reference Literals */
+/*********************************/
+/* defined in IEC 61131-3 v3 - Basically the 'NULL' keyword! */
+void *visit(ref_value_null_literal_c *symbol) {s4o.print("NULL"); return NULL;}
/******************************/
/* B 1.2.1 - Numeric Literals */
@@ -276,7 +281,7 @@
void *visit(boolean_literal_c *symbol) {return print_literal(symbol->type, symbol->value);}
/* helper class for boolean_literal_c */
-void *visit(boolean_true_c *symbol) {s4o.print(/*"TRUE"*/"1"); return NULL;}
+void *visit(boolean_true_c *symbol) {s4o.print(/*"TRUE" */"1"); return NULL;}
void *visit(boolean_false_c *symbol) {s4o.print(/*"FALSE"*/"0"); return NULL;}
/*******************************/