Insert the auto generated enum datatype conversion functions after the TYPE...END_TYPE, instead of at the end of input file.
authorMario de Sousa <msousa@fe.up.pt>
Tue, 27 Nov 2012 14:49:08 +0000
changeset 756 634f476cb60f
parent 755 7b90dd17f0ba
child 757 f1fc4aa6f0e3
child 758 1972c31c844d
Insert the auto generated enum datatype conversion functions after the TYPE...END_TYPE, instead of at the end of input file.
stage1_2/create_enumtype_conversion_functions.cc
stage1_2/create_enumtype_conversion_functions.hh
stage1_2/iec_bison.yy
stage1_2/iec_flex.ll
stage1_2/stage1_2.cc
stage1_2/stage1_2_priv.hh
--- a/stage1_2/create_enumtype_conversion_functions.cc	Mon Nov 26 16:38:15 2012 +0000
+++ b/stage1_2/create_enumtype_conversion_functions.cc	Tue Nov 27 14:49:08 2012 +0000
@@ -37,42 +37,48 @@
 /* set to 1 to see debug info during execution */
 static const int debug = 0;
 
-/*
- * functionDataType array contains all supported data type conversion.
- */
-const char *create_enumtype_conversion_functions_c::functionDataType[] = {
-		"STRING",
-		"SINT"  ,
-		"INT"   ,
-		"DINT"  ,
-		"LINT"  ,
-		"USINT" ,
-		"UINT"  ,
-		"UDINT" ,
-		"ULINT" ,
-		NULL
-};
-
-create_enumtype_conversion_functions_c::create_enumtype_conversion_functions_c(symbol_c *ignore) {
-
-}
-
-create_enumtype_conversion_functions_c::~create_enumtype_conversion_functions_c(void) {
-
-}
-
-std::string &create_enumtype_conversion_functions_c::get_declaration(symbol_c *root) {
-    text = "";
-    if (NULL != root) {
-        root->accept(*this);
-    }
-
-    return text;
-}
+
+/* 
+ * The create_enumtype_conversion_functions_c class generates ST source code!
+ * This code is in actual fact datatype conversion functions between user defined
+ * enumerated datatypes, and some basic datatypes.
+ *
+ * These conversion functions cannot be implemented the normal way (i.e. in the standard library)
+ * since they convert from/to a datatype that is defined by the user. So, we generate these conversions
+ * functions on the fly! 
+ * (to get an idea of what the generated code looks like, see the comments in create_enumtype_conversion_functions.cc)
+ *
+ * Currently, we support conversion between the user defined enumerated datatype and STRING,
+ * SINT, INT, DINT, LINT, USINT, UINT, UDINT, ULINT (basically the ANY_INT)
+ *
+ * ST source code is generated when the method get_declaration() is called. since the
+ * create_enumtype_conversion_functions_c inherits from the iterator visitor, this method may be 
+ * passed either the root of an abstract syntax tree, or sub-tree of the AST.
+ *
+ * This class will iterate through that AST, and for each enumerated type declaration, will 
+ * create the apropriate conversion functions.
+ */
+
+create_enumtype_conversion_functions_c *create_enumtype_conversion_functions_c::singleton = NULL;
+
+create_enumtype_conversion_functions_c:: create_enumtype_conversion_functions_c(symbol_c *ignore) {}
+create_enumtype_conversion_functions_c::~create_enumtype_conversion_functions_c(void)             {}
+
+
+std::string &create_enumtype_conversion_functions_c::get_declaration(symbol_c *symbol) {
+    if (NULL == singleton)  singleton = new create_enumtype_conversion_functions_c(NULL);
+    if (NULL == singleton)  ERROR_MSG("Out of memory. Bailing out!\n");
+    
+    singleton->text = "";
+    if (NULL != symbol) 
+      symbol->accept(*singleton);
+
+    return singleton->text;
+}
+
 
 void *create_enumtype_conversion_functions_c::visit(identifier_c *symbol) {
     currentToken = symbol->value;
-
     return NULL;
 }
 
--- a/stage1_2/create_enumtype_conversion_functions.hh	Mon Nov 26 16:38:15 2012 +0000
+++ b/stage1_2/create_enumtype_conversion_functions.hh	Tue Nov 27 14:49:08 2012 +0000
@@ -47,12 +47,13 @@
 
 
 class create_enumtype_conversion_functions_c: public iterator_visitor_c {
+  private:
+    static create_enumtype_conversion_functions_c *singleton;
+    
   public:
     explicit create_enumtype_conversion_functions_c(symbol_c *ignore);
     virtual ~create_enumtype_conversion_functions_c(void);
-    std::string &get_declaration(symbol_c *root);
-
-    const static char *functionDataType [];
+    static std::string &get_declaration(symbol_c *symbol);
 
     void *visit(identifier_c *symbol);
     /**********************/
--- a/stage1_2/iec_bison.yy	Mon Nov 26 16:38:15 2012 +0000
+++ b/stage1_2/iec_bison.yy	Tue Nov 27 14:49:08 2012 +0000
@@ -237,10 +237,7 @@
                    int last_column,
                    const char *last_filename,
                    long int last_order,
-                   const char *additional_error_msg);
-                   
-/* Create entry in symbol table for function conversion data type*/
-void add_enumtype_conversion_functions(const char * dname);
+                   const char *additional_error_msg);    
 %}
 
 
@@ -2509,7 +2506,7 @@
 
 data_type_declaration:
   TYPE type_declaration_list END_TYPE
-	{$$ = new data_type_declaration_c($2, locloc(@$));}
+	{$$ = new data_type_declaration_c($2, locloc(@$)); include_string((create_enumtype_conversion_functions_c::get_declaration($$)).c_str());}
 /* ERROR_CHECK_BEGIN */
 | TYPE END_TYPE
 	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "no data type declared in data type(s) declaration."); yynerrs++;}
@@ -2712,13 +2709,7 @@
  *       and include the library_element_symtable.insert(...) code in the rule actions!
  */
   identifier ':' enumerated_specification {library_element_symtable.insert($1, prev_declared_enumerated_type_name_token);}
-	{
-      $$ = new enumerated_type_declaration_c($1, new enumerated_spec_init_c($3, NULL, locloc(@3)), locloc(@$));
-      if (conversion_functions_) {
-        const char *name = ((identifier_c *)$1)->value;
-	    add_enumtype_conversion_functions(name);
-	  }
-    }
+	{$$ = new enumerated_type_declaration_c($1, new enumerated_spec_init_c($3, NULL, locloc(@3)), locloc(@$));}
 | identifier ':' enumerated_specification {library_element_symtable.insert($1, prev_declared_enumerated_type_name_token);} ASSIGN enumerated_value
 	{$$ = new enumerated_type_declaration_c($1, new enumerated_spec_init_c($3, $6, locf(@3), locl(@6)), locloc(@$));}
 /* ERROR_CHECK_BEGIN */
@@ -8338,8 +8329,8 @@
   {int res;
     if ((res = yyparse()) != 0) {
       fprintf (stderr, "\nParsing failed because of too many consecutive syntax errors. Bailing out!\n");
-  		exit(EXIT_FAILURE);
-  	}
+      exit(EXIT_FAILURE);
+    }
   }
 
   if (yynerrs > 0) {
@@ -8354,84 +8345,16 @@
   return 0;
 }
 
-/* Create a tmp file from a char buffer. */
-FILE *ftmpopen (void *buf, size_t size, const char *opentype)
-{
-  FILE *f;
-  f = tmpfile();
-  fwrite(buf, 1, size, f);
-  rewind(f);
-  return f;
-}
-
-/*  sstage2__ function allow to parse a ST code inside a string.
- *  We use this function to add into AST auto generated code like enum conversion functions.
- *  This appoach allow us to write future changes code indipendetly and to check generate code
- *  during developing.
- */
-int sstage2__(const char *text, 
-              symbol_c **tree_root_ref,
-              bool full_token_loc_        /* error messages specify full token location */
-             ) {
-
-  FILE *in_file = NULL;
-    
-  if((in_file = ftmpopen((void *)text, strlen(text), "r")) == NULL) {
-    perror("Error creating temp file.");
-    return -1;
-  }
-
-  /* now parse the input file... */
-  #if YYDEBUG
-    yydebug = 1;
-  #endif
-  yyin = in_file;
-  
-  /* We turn on "allow_function_overloading" flag to disable some checks. 
-   * In detail when we add to symboltable a symbol already processed we
-   * don't want to get any error. 
-   */  
-   
-  allow_function_overloading = true;
-  allow_extensible_function_parameters = false;
-  full_token_loc = full_token_loc_;
-  current_filename = "built-in";
-  current_tracking = GetNewTracking(yyin);
-  {int res;
-    if ((res = yyparse()) != 0) {
-      fprintf (stderr, "\nParsing failed because of too many consecutive syntax errors. Bailing out!\n");
-        exit(EXIT_FAILURE);
-    }
-  }
-
-  if (yynerrs > 0) {
-    fprintf (stderr, "\n%d error(s) found. Bailing out!\n", yynerrs /* global variable */);
-    exit(EXIT_FAILURE);
-  }
-  
-  if (tree_root_ref != NULL)
-    *tree_root_ref = tree_root;
-
-  fclose(in_file);
-  return 0;
-}
-
-
-/* Create entry in symbol table for function conversion data type*/
-void add_enumtype_conversion_functions(const char * dname) {
-  std::string strname;
-  std::string tmp;
-  
-  strname = dname; 
-  for (int i = 0; create_enumtype_conversion_functions_c::functionDataType[i] != NULL; i++) {
-    tmp = strname + std::string("_TO_") + create_enumtype_conversion_functions_c::functionDataType[i];
-    library_element_symtable.insert(tmp.c_str(), prev_declared_derived_function_name_token);
-    
-    tmp = create_enumtype_conversion_functions_c::functionDataType[i] + std::string("_TO_") + strname;
-    library_element_symtable.insert(tmp.c_str(), prev_declared_derived_function_name_token);
-  }  
-}
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
--- a/stage1_2/iec_flex.ll	Mon Nov 26 16:38:15 2012 +0000
+++ b/stage1_2/iec_flex.ll	Tue Nov 27 14:49:08 2012 +0000
@@ -247,6 +247,8 @@
  * but first return to the stream an additional character to mark the end of the token. 
  */
 void unput_and_mark(const char c);
+
+void include_file(const char *include_filename);
 %}
 
 
@@ -867,38 +869,8 @@
 <include_beg>{file_include_pragma_beg}	BEGIN(include_filename);
 
 <include_filename>{file_include_pragma_filename}	{
-			  /* got the include file name */
-			  int i;
-
-			  if (include_stack_ptr >= MAX_INCLUDE_DEPTH) {
-			    fprintf(stderr, "Includes nested too deeply\n");
-			    exit( 1 );
-			  }
-			  include_stack[include_stack_ptr].buffer_state = YY_CURRENT_BUFFER;
-			  include_stack[include_stack_ptr].env = current_tracking;
-			  include_stack[include_stack_ptr].filename = current_filename;
-			  
-			  for (i = 0, yyin = NULL; (INCLUDE_DIRECTORIES[i] != NULL) && (yyin == NULL); i++) {
-			    char *full_name = strdup3(INCLUDE_DIRECTORIES[i], "/", yytext);
-			    if (full_name == NULL) {
-			      fprintf(stderr, "Out of memory!\n");
-			      exit( 1 );
-			    }
-			    yyin = fopen(full_name, "r");
-			    free(full_name);
-			  }
-
-			  if (!yyin) {
-			    fprintf(stderr, "Error opening included file %s\n", yytext);
-			    exit( 1 );
-			  }
-
-			  current_filename = strdup(yytext);
-			  current_tracking = GetNewTracking(yyin);
-			  include_stack_ptr++;
-
-			  /* switch input buffer to new file... */
-			  yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+			  /* set the internal state variables of lexical analyser to process a new include file */
+			  include_file(yytext);
 			  /* switch to whatever state was active before the include file */
 			  yy_pop_state();
 			  /* now process the new file... */
@@ -947,6 +919,8 @@
 			}
 
 <include_end>{file_include_pragma_end}	yy_pop_state();
+	/* handle the artificial file includes created by include_string(), which do not end with a '}' */
+<include_end>.				unput_text(0); yy_pop_state(); 
 
 
 	/*********************************/
@@ -1690,6 +1664,79 @@
 }
 
 
+
+/* set the internal state variables of lexical analyser to process a new include file */
+void handle_include_file_(FILE *filehandle, const char *filename) {
+  if (include_stack_ptr >= MAX_INCLUDE_DEPTH) {
+    fprintf(stderr, "Includes nested too deeply\n");
+    exit( 1 );
+  }
+  
+  yyin = filehandle;
+  
+  include_stack[include_stack_ptr].buffer_state = YY_CURRENT_BUFFER;
+  include_stack[include_stack_ptr].env = current_tracking;
+  include_stack[include_stack_ptr].filename = current_filename;
+  
+  current_filename = strdup(filename);
+  current_tracking = GetNewTracking(yyin);
+  include_stack_ptr++;
+
+  /* switch input buffer to new file... */
+  yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+}
+
+
+
+/* insert the code (in <source_code>) into the source code we are parsing.
+ * This is done by creating an artificial file with that new source code, and then 'including' the file
+ */
+void include_string(const char *source_code) {
+  FILE *tmp_file = tmpfile();
+  
+  if(tmp_file == NULL) {
+    perror("Error creating temp file.");
+    exit(EXIT_FAILURE);
+  }
+
+  fwrite((void *)source_code, 1, strlen(source_code), tmp_file);
+  rewind(tmp_file);
+
+  /* now parse the tmp file, by asking flex to handle it as if it had been included with the (*#include ... *) pragma... */
+  handle_include_file_(tmp_file, "");
+//fclose(tmp_file);  /* do NOT close file. It must only be closed when we finish reading from it! */
+}
+
+
+
+/* Open an include file, and set the internal state variables of lexical analyser to process a new include file */
+void include_file(const char *filename) {
+  FILE *filehandle = NULL;
+  
+  for (int i = 0; (INCLUDE_DIRECTORIES[i] != NULL) && (filehandle == NULL); i++) {
+    char *full_name;
+    full_name = strdup3(INCLUDE_DIRECTORIES[i], "/", filename);
+    if (full_name == NULL) {
+      fprintf(stderr, "Out of memory!\n");
+      exit( 1 );
+    }
+    filehandle = fopen(full_name, "r");
+    free(full_name);
+  }
+
+  if (NULL == filehandle) {
+    fprintf(stderr, "Error opening included file %s\n", filename);
+    exit( 1 );
+  }
+
+  /* now process the new file... */
+  handle_include_file_(filehandle, filename);
+}
+
+
+
+
+
 /* return all the text in the current token back to the input stream, except the first n chars. */
 void unput_text(unsigned int n) {
   /* it seems that flex has a bug in that it will not correctly count the line numbers
--- a/stage1_2/stage1_2.cc	Mon Nov 26 16:38:15 2012 +0000
+++ b/stage1_2/stage1_2.cc	Tue Nov 27 14:49:08 2012 +0000
@@ -292,11 +292,6 @@
              bool full_token_loc         /* error messages specify full token location */
             );
 
-int sstage2__(const char *text,
-              symbol_c **tree_root_ref,
-              bool full_token_loc         /* error messages specify full token location */
-             );
-
 
 int stage1_2(const char *filename, symbol_c **tree_root_ref, stage1_2_options_t options) {
       /* NOTE: we only call stage2 (bison - syntax analysis) directly, as stage 2 will itself call stage1 (flex - lexical analysis)
@@ -312,13 +307,6 @@
 
   safe_extensions_ = options.safe_extensions;
   conversion_functions_ = options.conversion_functions;
-  int ret = stage2__(filename, options.includedir, tree_root_ref, options.full_token_loc);
-
-  if (conversion_functions_) {
-	  create_enumtype_conversion_functions_c create_enumtype_conversion_functions_c(*tree_root_ref);
-	  std::string source_code = create_enumtype_conversion_functions_c.get_declaration(*tree_root_ref);
-	  ret = sstage2__(source_code.c_str(), tree_root_ref, false);
-  }
-  return ret;
-}
-
+  return stage2__(filename, options.includedir, tree_root_ref, options.full_token_loc);
+}
+
--- a/stage1_2/stage1_2_priv.hh	Mon Nov 26 16:38:15 2012 +0000
+++ b/stage1_2/stage1_2_priv.hh	Tue Nov 27 14:49:08 2012 +0000
@@ -105,6 +105,18 @@
 /* This is a service that flex provides to bison... */
 void print_include_stack(void);
 
+/*****************************************************/
+/* Ask flex to include the source code in the string */
+/*****************************************************/
+/* This is a service that flex provides to bison... */
+/* The string should contain valid IEC 61131-3 source code. Bison will ask flex to insert source 
+ * code into the input stream of IEC code being parsed. The code to be inserted is typically
+ * generated automatically.
+ * Currently this is used to insert conversion functions ***_TO_*** (as defined by the standard)
+ * between user defined (i.e. derived) enumerated datatypes, and some basic datatypes 
+ * (e.g. INT, STRING, etc...)
+ */
+void include_string(const char *source_code);
 
 /**************************************/
 /* The name of the file being parsed. */