Fix C code generation when forward references (-p command line option) is on.
--- a/main.cc Sat Dec 06 19:11:32 2014 +0000
+++ b/main.cc Sat Dec 06 19:14:23 2014 +0000
@@ -134,7 +134,7 @@
int main(int argc, char **argv) {
- symbol_c *tree_root;
+ symbol_c *tree_root, *ordered_tree_root;
char * builddir = NULL;
int optres, errflg = 0;
int path_len;
@@ -252,11 +252,11 @@
//add_en_eno_param_decl_c::add_to(tree_root);
/* Do semantic verification of code */
- if (stage3(tree_root) < 0)
+ if (stage3(tree_root, &ordered_tree_root) < 0)
return EXIT_FAILURE;
/* 3rd Pass */
- if (stage4(tree_root, builddir) < 0)
+ if (stage4(ordered_tree_root, builddir) < 0)
return EXIT_FAILURE;
/* 4th Pass */
--- a/stage3/Makefile.am Sat Dec 06 19:11:32 2014 +0000
+++ b/stage3/Makefile.am Sat Dec 06 19:14:23 2014 +0000
@@ -14,5 +14,6 @@
array_range_check.cc \
constant_folding.cc \
declaration_check.cc \
- enum_declaration_check.cc
+ enum_declaration_check.cc \
+ remove_forward_dependencies.cc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stage3/remove_forward_dependencies.cc Sat Dec 06 19:14:23 2014 +0000
@@ -0,0 +1,303 @@
+/*
+ * matiec - a compiler for the programming languages defined in IEC 61131-3
+ *
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ *
+ *
+ * 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)
+ *
+ */
+
+/*
+ * Re-oder the POUs in te library so that no forward references occur.
+ *
+ * Since stage1_2 now suppport POUs that contain references to POUS that are only declared later,
+ * (e.g. a variable of FB1_t is declared, before the FB1_T function block is itself declared!)
+ * we may need to re-order all the POUs in the library so that these forward references do not occur.
+ *
+ * This utility function will do just that. However, it does not destroy the original abstract syntax
+ * tree (AST). It instead creates a new re-ordered AST, by instantiating a new library_c object.
+ * This new library_c object will however point to the *same* objects of the original AST, just in
+ * a new order.
+ * This means that the new and original AST share all the object instances, and only use a distinct
+ * library_c object!
+ */
+
+#include "remove_forward_dependencies.hh"
+#include "../main.hh" // required for ERROR() and ERROR_MSG() macros.
+#include "../absyntax_utils/absyntax_utils.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 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; \
+}
+
+
+
+/* NOTE: We create an independent visitor for this task instead of having this done by the remove_forward_dependencies_c
+ * because we do not want to handle the ***_pragma_c classes while doing this search.
+ * (remove_forward_dependencies_c needs to visit those classes when handling all the possible entries in
+ * library_c, and must have those visitors)
+ *
+ * NOTE:
+ * This class could have been written by visiting all the AST objects that could _reference_ a
+ * - FB type
+ * - Program type
+ * - Function type
+ * and by checking whether those references are in the declared_identifiers list.
+ * However, one of those objects, the ref_spec_c, may reference an FB type, or any other datatype, so we must have a way
+ * of knowing what is being referenced in this case. I have opted to introduce a new object type in the AST, the
+ * poutype_identifier_c, that will be used anywhere in the AST that references either a PROGRAM name or a FB type name
+ * or a FUNCTION name (previously a simple identifier_c was used!).
+ * This means that we merely need to visit the new poutype_identifier_c object in this visitor.
+ */
+class find_forward_dependencies_c: public search_visitor_c {
+ private:
+ identifiers_symbtable_t *declared_identifiers; // list of identifiers already declared by the symbols in the new tree
+ public:
+ find_forward_dependencies_c(identifiers_symbtable_t *declared_identifiers_) {declared_identifiers = declared_identifiers_;}
+ /*******************************************/
+ /* B 1.1 - Letters, digits and identifiers */
+ /*******************************************/
+ // return NULL if the symbol is already in the declared_identifiers symbol table, otherwise return the missing symbol!
+ void *visit( poutype_identifier_c *symbol)
+ {if (declared_identifiers->find_value(symbol) != declared_identifiers->end_value()) return NULL; else return symbol;}
+}; /* class find_forward_dependencies_c */
+
+
+
+
+/* A class to count the number of POUs (Function, FBs Programs and Configurations) in a library.
+ * This will be used to make sure whether we have copied all the POUs from the original AST (abstract
+ * syntax tree) to the new AST.
+ * Note that we can't simply use the number of 'elements' in the AST, as it may contain unknown/unsupported pragmas.
+ */
+class pou_count_c: public search_visitor_c {
+ private:
+ static pou_count_c *singleton;
+ long long int count;
+
+ public:
+ static long long int get_count(library_c *library) {
+ if (NULL == singleton) singleton = new pou_count_c;
+ if (NULL == singleton) ERROR;
+ singleton->count = 0;
+ library->accept(*singleton);
+ return singleton->count;
+ }
+
+ /**************************************/
+ /* B.1.5 - Program organization units */
+ /**************************************/
+ void *visit( function_declaration_c *symbol) {count++; return NULL;}
+ void *visit(function_block_declaration_c *symbol) {count++; return NULL;}
+ void *visit( program_declaration_c *symbol) {count++; return NULL;}
+ void *visit( configuration_declaration_c *symbol) {count++; return NULL;}
+}; /* class pou_count_c */
+
+pou_count_c *pou_count_c::singleton = NULL;
+symbol_c remove_forward_dependencies_c_null_symbol;
+
+
+
+
+
+
+
+
+/************************************************************/
+/************************************************************/
+/****** The main class: Remove Forward Depencies *******/
+/************************************************************/
+/************************************************************/
+
+// constructor & destructor
+remove_forward_dependencies_c:: remove_forward_dependencies_c(void) {
+ find_forward_dependencies = new find_forward_dependencies_c(&declared_identifiers);
+ current_display_error_level = error_level_default;
+ error_count = 0;
+}
+
+remove_forward_dependencies_c::~remove_forward_dependencies_c(void) {
+ delete find_forward_dependencies;
+}
+
+
+int remove_forward_dependencies_c::get_error_count(void) {
+ return error_count;
+}
+
+
+
+library_c *remove_forward_dependencies_c::create_new_tree(symbol_c *tree) {
+ library_c *old_tree = dynamic_cast<library_c *>(tree);
+ if (NULL == old_tree) ERROR;
+ new_tree = new library_c;
+ *((symbol_c *)new_tree) = *((symbol_c *)tree); // copy any annotations from tree to new_tree;
+ new_tree->clear(); // remove all elements from list.
+ tree->accept(*this);
+ return new_tree;
+}
+
+
+
+void *remove_forward_dependencies_c::handle_library_symbol(symbol_c *symbol, symbol_c *name, symbol_c *search1, symbol_c *search2, symbol_c *search3) {
+ if (inserted_symbols.find(symbol) != inserted_symbols.end()) return NULL; // already previously inserted into new_tree and declared_identifiers. Do not handle again!
+ if ((search1 != NULL) && (search1->accept(*find_forward_dependencies) != NULL)) return NULL; // A forward depency has not yet been satisfied. Wait for a later iteration to try again!
+ if ((search2 != NULL) && (search2->accept(*find_forward_dependencies) != NULL)) return NULL; // A forward depency has not yet been satisfied. Wait for a later iteration to try again!
+ if ((search3 != NULL) && (search3->accept(*find_forward_dependencies) != NULL)) return NULL; // A forward depency has not yet been satisfied. Wait for a later iteration to try again!
+ /* no forward dependencies found => insert into new AST, and add to the 'defined identifiers' and 'inserted symbol' lists */
+ if (declared_identifiers.find_value(name) == declared_identifiers.end_value())
+ declared_identifiers.insert(name, NULL); // only add if not yet in the symbol table (an overloaded version of this same POU could have been inderted previously!)
+ inserted_symbols.insert(symbol);
+ new_tree->add_element(current_code_generation_pragma);
+ new_tree->add_element(symbol);
+ return NULL;
+}
+
+
+/* Tell the user that the source code contains a circular dependency */
+void remove_forward_dependencies_c::print_circ_error(library_c *symbol) {
+ /* Note that we only print Functions and FBs, as Programs and Configurations cannot contain circular references due to syntax rules */
+ /* Note too that circular references in derived datatypes is also not possible due to sytax! */
+ int initial_error_count = error_count;
+ for (int i = 0; i < symbol->n; i++)
+ if ( (inserted_symbols.find(symbol->elements[i]) == inserted_symbols.end()) // if not copied to new AST
+ &&( (NULL != dynamic_cast <function_block_declaration_c *>(symbol->elements[i])) // and (is a FB
+ ||(NULL != dynamic_cast < function_declaration_c *>(symbol->elements[i])))) // or a Function)
+ STAGE3_ERROR(0, symbol->elements[i], symbol->elements[i], "POU (%s) contains a self-reference and/or belongs in a circular referencing loop", get_datatype_info_c::get_id_str(symbol->elements[i]));
+ if (error_count == initial_error_count) ERROR; // We were unable to determine which POUs contain the circular references!!
+}
+
+
+/***************************/
+/* B 0 - Programming Model */
+/***************************/
+/* enumvalue_symtable is filled in by enum_declaration_check_c, during stage3 semantic verification, with a list of all enumerated constants declared inside this POU */
+// SYM_LIST(library_c, enumvalue_symtable_t enumvalue_symtable;)
+void *remove_forward_dependencies_c::visit(library_c *symbol) {
+ /* this method is the expected entry point for this visitor, and implements the main algorithm of the visitor */
+
+ /* first insert all the derived datatype declarations, in the same order by which they are delcared in the original AST */
+ /* Since IEC 61131-3 does not allow FBs in arrays or structures, it is actually safe to place all the datatypes before all the POUs! */
+ for (int i = 0; i < symbol->n; i++)
+ if (NULL != dynamic_cast <data_type_declaration_c *>(symbol->elements[i]))
+ new_tree->add_element(symbol->elements[i]);
+
+ /* now do the POUs, in whatever order is necessary to guarantee no forward references. */
+ long long int old_tree_pou_count = pou_count_c::get_count(symbol);
+ // if no code generation pragma exists before the first entry in the library, the default is to enable code generation.
+ enable_code_generation_pragma_c *default_code_generation_pragma = new enable_code_generation_pragma_c;
+ int prev_n;
+ cycle_count = 0;
+ do {
+ cycle_count++;
+ prev_n = new_tree->n;
+ current_code_generation_pragma = default_code_generation_pragma;
+ for (int i = 0; i < symbol->n; i++) symbol->elements[i]->accept(*this);
+ } while (prev_n != new_tree->n); // repeat while new elementns are still being added to the new AST
+
+ if (old_tree_pou_count != pou_count_c::get_count(new_tree))
+ print_circ_error(symbol);
+
+ return NULL;
+}
+
+
+/**************************************/
+/* B.1.5 - Program organization units */
+/**************************************/
+/***********************/
+/* B 1.5.1 - Functions */
+/***********************/
+// SYM_REF4(function_declaration_c, derived_function_name, type_name, var_declarations_list, function_body, enumvalue_symtable_t enumvalue_symtable;)
+void *remove_forward_dependencies_c::visit(function_declaration_c *symbol)
+ {return handle_library_symbol(symbol, symbol->derived_function_name, symbol->type_name, symbol->var_declarations_list, symbol->function_body);}
+/*****************************/
+/* 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, enumvalue_symtable_t enumvalue_symtable;)
+void *remove_forward_dependencies_c::visit(function_block_declaration_c *symbol)
+ {return handle_library_symbol(symbol, symbol->fblock_name, symbol->var_declarations, symbol->fblock_body);}
+/**********************/
+/* 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, enumvalue_symtable_t enumvalue_symtable;)
+void *remove_forward_dependencies_c::visit(program_declaration_c *symbol)
+ {return handle_library_symbol(symbol, symbol->program_type_name, symbol->var_declarations, symbol->function_block_body);}
+/********************************/
+/* B 1.7 Configuration elements */
+/********************************/
+/* CONFIGURATION configuration_name (...) END_CONFIGURATION */
+// SYM_REF5(configuration_declaration_c, configuration_name, global_var_declarations, resource_declarations, access_declarations, instance_specific_initializations, enumvalue_symtable_t enumvalue_symtable;)
+void *remove_forward_dependencies_c::visit(configuration_declaration_c *symbol)
+ {return handle_library_symbol(symbol, symbol->configuration_name, symbol->global_var_declarations, symbol->resource_declarations, symbol->access_declarations);}
+/********************/
+/* 2.1.6 - Pragmas */
+/********************/
+void *remove_forward_dependencies_c::visit(disable_code_generation_pragma_c *symbol) {current_code_generation_pragma = symbol; return NULL;}
+void *remove_forward_dependencies_c::visit( enable_code_generation_pragma_c *symbol) {current_code_generation_pragma = symbol; return NULL;}
+/* I have no ideia what this pragma is. Where should we place it in the re-ordered tree?
+ * Without knowing the semantics of the pragma, it is not possible to hande it correctly.
+ * We therefore print out an error message, and abort!
+ */
+// TODO: print error message!
+void *remove_forward_dependencies_c::visit(pragma_c *symbol) {
+ if (1 != cycle_count) return NULL; // only handle unknown pragmas in the first cycle!
+ STAGE3_WARNING(symbol, symbol, "Unrecognized pragma. Including the pragma when using the '-p' command line option for 'allow use of forward references' may result in unwanted behaviour.");
+ new_tree->add_element(symbol);
+ return NULL;
+}
+
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stage3/remove_forward_dependencies.hh Sat Dec 06 19:14:23 2014 +0000
@@ -0,0 +1,115 @@
+/*
+ * matiec - a compiler for the programming languages defined in IEC 61131-3
+ *
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ *
+ *
+ * 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)
+ *
+ */
+
+
+/*
+ * Re-oder the POUs in te library so that no forward references occur.
+ *
+ * Since stage1_2 now suppport POUs that contain references to POUS that are only declared later,
+ * (e.g. a variable of FB1_t is declared, before the FB1_T function block is itself declared!)
+ * we may need to re-order all the POUs in the library so that these forward references do not occur.
+ *
+ * This utility class will do just that. However, it does not destroy the original abstract syntax
+ * tree (AST). It instead creates a new re-ordered AST, by instantiating a new library_c object.
+ * This new library_c object will however point to the *same* objects of the original AST, just in
+ * a new order.
+ * This means that the new and original AST share all the object instances, and only use a distinct
+ * library_c object!
+ */
+
+#include "../absyntax/absyntax.hh"
+#include "../absyntax/visitor.hh"
+#include "../util/symtable.hh"
+#include <set>
+
+
+class find_forward_dependencies_c;
+extern symbol_c remove_forward_dependencies_c_null_symbol;
+typedef symtable_c<symbol_c *, &remove_forward_dependencies_c_null_symbol> identifiers_symbtable_t;
+
+
+
+
+
+
+
+class remove_forward_dependencies_c: public search_visitor_c {
+
+ private:
+ /* The level of detail that the user wants us to display error messages. */
+ #define error_level_default (1)
+ #define error_level_nagging (4)
+ unsigned int current_display_error_level;
+ int error_count;
+ bool warning_found;
+ library_c *new_tree;
+ int cycle_count; // main algorithm runs in a loop. The nuber of the current cycle...
+ // NOTE: we need two lists in order to correctly handle overloaded functions
+ identifiers_symbtable_t declared_identifiers; // list of identifiers already declared by the symbols in the new tree
+ std::set <symbol_c *> inserted_symbols; // list of symbols already inserted in the new tree
+ symbol_c *current_code_generation_pragma; // points to any currently 'active' enable_code_generation_pragma_c
+ find_forward_dependencies_c *find_forward_dependencies;
+
+ public:
+ remove_forward_dependencies_c(void);
+ ~remove_forward_dependencies_c(void);
+ library_c *create_new_tree(symbol_c *old_tree); // create a new tree with POUs ordered so it does not contain forward dependencies...
+ int get_error_count(void);
+
+ private:
+ void *handle_library_symbol(symbol_c *symbol, symbol_c *name, symbol_c *search1, symbol_c *search2 = NULL, symbol_c *search3 = NULL);
+ void print_circ_error(library_c *symbol);
+
+ /***************************/
+ /* B 0 - Programming Model */
+ /***************************/
+ void *visit(library_c *symbol);
+ /**************************************/
+ /* B.1.5 - Program organization units */
+ /**************************************/
+ void *visit(function_declaration_c *symbol);
+ void *visit(function_block_declaration_c *symbol);
+ void *visit(program_declaration_c *symbol);
+ /********************************/
+ /* B 1.7 Configuration elements */
+ /********************************/
+ void *visit(configuration_declaration_c *symbol);
+ /********************/
+ /* 2.1.6 - Pragmas */
+ /********************/
+ void *visit(disable_code_generation_pragma_c *symbol);
+ void *visit(enable_code_generation_pragma_c *symbol);
+ void *visit(pragma_c *symbol);
+
+}; /* class remove_forward_dependencies_c */
+
+
+
--- a/stage3/stage3.cc Sat Dec 06 19:11:32 2014 +0000
+++ b/stage3/stage3.cc Sat Dec 06 19:14:23 2014 +0000
@@ -44,6 +44,8 @@
#include "constant_folding.hh"
#include "declaration_check.hh"
#include "enum_declaration_check.hh"
+#include "remove_forward_dependencies.hh"
+
static int enum_declaration_check(symbol_c *tree_root){
@@ -114,7 +116,26 @@
}
-int stage3(symbol_c *tree_root){
+
+/* Removing forward dependencies only makes sense when stage1_2 is run with the pre-parsing option.
+ * This algorithm has no dependencies on other stage 3 algorithms.
+ * Typically this is run last, just to show that the remaining algorithms also do not depend on the fact that
+ * the library_c (i.e. the source code) does not contain forward dependencies.
+ */
+static int remove_forward_dependencies(symbol_c *tree_root, symbol_c **ordered_tree_root) {
+ if (NULL != ordered_tree_root) *ordered_tree_root = tree_root; // by default, consider tree_root already ordered
+ if (!runtime_options.pre_parsing) return 0; // No re-ordering necessary, just return
+
+ /* We need to re-order the elements in the library, to fix any forward references! */
+ remove_forward_dependencies_c remove_forward_dependencies;
+ symbol_c *new_tree_root = remove_forward_dependencies.create_new_tree(tree_root);
+ if (NULL == new_tree_root) ERROR;
+ if (NULL != ordered_tree_root) *ordered_tree_root = new_tree_root;
+ return remove_forward_dependencies.get_error_count();
+}
+
+
+int stage3(symbol_c *tree_root, symbol_c **ordered_tree_root) {
int error_count = 0;
error_count += enum_declaration_check(tree_root);
error_count += declaration_safety(tree_root);
@@ -123,6 +144,7 @@
error_count += type_safety(tree_root);
error_count += lvalue_check(tree_root);
error_count += array_range_check(tree_root);
+ error_count += remove_forward_dependencies(tree_root, ordered_tree_root);
if (error_count > 0) {
fprintf(stderr, "%d error(s) found. Bailing out!\n", error_count);
--- a/stage3/stage3.hh Sat Dec 06 19:11:32 2014 +0000
+++ b/stage3/stage3.hh Sat Dec 06 19:14:23 2014 +0000
@@ -36,6 +36,6 @@
#include "../util/symtable.hh"
-int stage3(symbol_c *tree_root);
+int stage3(symbol_c *tree_root, symbol_c **ordered_tree_root);
#endif /* _STAGE3_HH */