Add support for the 'NULL' keyword, defined in version 3 of IEC 61131-3.
authormjsousa
Thu, 31 Jul 2014 17:49:44 +0100
changeset 919 8da635655f37
parent 918 e9bde0aa93ed
child 920 4369ce5e687f
Add support for the 'NULL' keyword, defined in version 3 of IEC 61131-3.
absyntax/absyntax.def
absyntax_utils/get_datatype_info.cc
absyntax_utils/search_base_type.cc
absyntax_utils/search_base_type.hh
stage1_2/iec_bison.yy
stage1_2/iec_flex.ll
stage3/fill_candidate_datatypes.cc
stage3/fill_candidate_datatypes.hh
stage3/narrow_candidate_datatypes.cc
stage4/generate_c/generate_c_base.cc
stage4/generate_iec/generate_iec.cc
--- 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;}
 
 /*******************************/