mjsousa@959: /*
mjsousa@959: * matiec - a compiler for the programming languages defined in IEC 61131-3
mjsousa@959: *
mjsousa@959: * Copyright (C) 2014 Mario de Sousa (msousa@fe.up.pt)
mjsousa@959: *
mjsousa@959: * This program is free software: you can redistribute it and/or modify
mjsousa@959: * it under the terms of the GNU General Public License as published by
mjsousa@959: * the Free Software Foundation, either version 3 of the License, or
mjsousa@959: * (at your option) any later version.
mjsousa@959: *
mjsousa@959: * This program is distributed in the hope that it will be useful,
mjsousa@959: * but WITHOUT ANY WARRANTY; without even the implied warranty of
mjsousa@959: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
mjsousa@959: * GNU General Public License for more details.
mjsousa@959: *
mjsousa@959: * You should have received a copy of the GNU General Public License
mjsousa@959: * along with this program. If not, see .
mjsousa@959: *
mjsousa@959: *
mjsousa@959: * This code is made available on the understanding that it will not be
mjsousa@959: * used in safety-critical situations without a full and competent review.
mjsousa@959: */
mjsousa@959:
mjsousa@959: /*
mjsousa@959: * An IEC 61131-3 compiler.
mjsousa@959: *
mjsousa@959: * Based on the
mjsousa@959: * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10)
mjsousa@959: *
mjsousa@959: */
mjsousa@959:
mjsousa@959: /*
mjsousa@959: * Re-oder the POUs in te library so that no forward references occur.
mjsousa@959: *
mjsousa@959: * Since stage1_2 now suppport POUs that contain references to POUS that are only declared later,
mjsousa@959: * (e.g. a variable of FB1_t is declared, before the FB1_T function block is itself declared!)
mjsousa@959: * we may need to re-order all the POUs in the library so that these forward references do not occur.
mjsousa@959: *
mjsousa@959: * This utility function will do just that. However, it does not destroy the original abstract syntax
mjsousa@959: * tree (AST). It instead creates a new re-ordered AST, by instantiating a new library_c object.
mjsousa@959: * This new library_c object will however point to the *same* objects of the original AST, just in
mjsousa@959: * a new order.
mjsousa@959: * This means that the new and original AST share all the object instances, and only use a distinct
mjsousa@959: * library_c object!
mjsousa@959: */
mjsousa@959:
mjsousa@959: #include "remove_forward_dependencies.hh"
mjsousa@959: #include "../main.hh" // required for ERROR() and ERROR_MSG() macros.
mjsousa@959: #include "../absyntax_utils/absyntax_utils.hh"
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959: #define FIRST_(symbol1, symbol2) (((symbol1)->first_order < (symbol2)->first_order) ? (symbol1) : (symbol2))
mjsousa@959: #define LAST_(symbol1, symbol2) (((symbol1)->last_order > (symbol2)->last_order) ? (symbol1) : (symbol2))
mjsousa@959:
mjsousa@959: #define FIRST_(symbol1, symbol2) (((symbol1)->first_order < (symbol2)->first_order) ? (symbol1) : (symbol2))
mjsousa@959: #define LAST_(symbol1, symbol2) (((symbol1)->last_order > (symbol2)->last_order) ? (symbol1) : (symbol2))
mjsousa@959:
mjsousa@959: #define STAGE3_ERROR(error_level, symbol1, symbol2, ...) { \
mjsousa@959: if (current_display_error_level >= error_level) { \
mjsousa@959: fprintf(stderr, "%s:%d-%d..%d-%d: error: ", \
mjsousa@959: FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\
mjsousa@959: LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\
mjsousa@959: fprintf(stderr, __VA_ARGS__); \
mjsousa@959: fprintf(stderr, "\n"); \
mjsousa@959: error_count++; \
mjsousa@959: } \
mjsousa@959: }
mjsousa@959:
mjsousa@959:
mjsousa@959: #define STAGE3_WARNING(symbol1, symbol2, ...) { \
mjsousa@959: fprintf(stderr, "%s:%d-%d..%d-%d: warning: ", \
mjsousa@959: FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\
mjsousa@959: LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\
mjsousa@959: fprintf(stderr, __VA_ARGS__); \
mjsousa@959: fprintf(stderr, "\n"); \
mjsousa@959: warning_found = true; \
mjsousa@959: }
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959: /* NOTE: We create an independent visitor for this task instead of having this done by the remove_forward_dependencies_c
mjsousa@959: * because we do not want to handle the ***_pragma_c classes while doing this search.
mjsousa@959: * (remove_forward_dependencies_c needs to visit those classes when handling all the possible entries in
mjsousa@959: * library_c, and must have those visitors)
mjsousa@959: *
mjsousa@959: * NOTE:
mjsousa@959: * This class could have been written by visiting all the AST objects that could _reference_ a
mjsousa@959: * - FB type
mjsousa@959: * - Program type
mjsousa@959: * - Function type
mjsousa@959: * and by checking whether those references are in the declared_identifiers list.
mjsousa@959: * However, one of those objects, the ref_spec_c, may reference an FB type, or any other datatype, so we must have a way
mjsousa@959: * of knowing what is being referenced in this case. I have opted to introduce a new object type in the AST, the
mjsousa@959: * poutype_identifier_c, that will be used anywhere in the AST that references either a PROGRAM name or a FB type name
mjsousa@959: * or a FUNCTION name (previously a simple identifier_c was used!).
mjsousa@959: * This means that we merely need to visit the new poutype_identifier_c object in this visitor.
mjsousa@959: */
mjsousa@959: class find_forward_dependencies_c: public search_visitor_c {
mjsousa@959: private:
mjsousa@959: identifiers_symbtable_t *declared_identifiers; // list of identifiers already declared by the symbols in the new tree
mjsousa@959: public:
mjsousa@959: find_forward_dependencies_c(identifiers_symbtable_t *declared_identifiers_) {declared_identifiers = declared_identifiers_;}
mjsousa@959: /*******************************************/
mjsousa@959: /* B 1.1 - Letters, digits and identifiers */
mjsousa@959: /*******************************************/
mjsousa@959: // return NULL if the symbol is already in the declared_identifiers symbol table, otherwise return the missing symbol!
mjsousa@959: void *visit( poutype_identifier_c *symbol)
mjsousa@971: {if (declared_identifiers->find(symbol) != declared_identifiers->end()) return NULL; else return symbol;}
mjsousa@959: }; /* class find_forward_dependencies_c */
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959: /* A class to count the number of POUs (Function, FBs Programs and Configurations) in a library.
mjsousa@959: * This will be used to make sure whether we have copied all the POUs from the original AST (abstract
mjsousa@959: * syntax tree) to the new AST.
mjsousa@959: * Note that we can't simply use the number of 'elements' in the AST, as it may contain unknown/unsupported pragmas.
mjsousa@959: */
mjsousa@959: class pou_count_c: public search_visitor_c {
mjsousa@959: private:
mjsousa@959: static pou_count_c *singleton;
mjsousa@959: long long int count;
mjsousa@959:
mjsousa@959: public:
mjsousa@959: static long long int get_count(library_c *library) {
mjsousa@959: if (NULL == singleton) singleton = new pou_count_c;
mjsousa@959: if (NULL == singleton) ERROR;
mjsousa@959: singleton->count = 0;
mjsousa@959: library->accept(*singleton);
mjsousa@959: return singleton->count;
mjsousa@959: }
mjsousa@959:
mjsousa@959: /**************************************/
mjsousa@959: /* B.1.5 - Program organization units */
mjsousa@959: /**************************************/
mjsousa@959: void *visit( function_declaration_c *symbol) {count++; return NULL;}
mjsousa@959: void *visit(function_block_declaration_c *symbol) {count++; return NULL;}
mjsousa@959: void *visit( program_declaration_c *symbol) {count++; return NULL;}
mjsousa@959: void *visit( configuration_declaration_c *symbol) {count++; return NULL;}
mjsousa@959: }; /* class pou_count_c */
mjsousa@959:
mjsousa@959: pou_count_c *pou_count_c::singleton = NULL;
mjsousa@959: symbol_c remove_forward_dependencies_c_null_symbol;
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959: /************************************************************/
mjsousa@959: /************************************************************/
mjsousa@959: /****** The main class: Remove Forward Depencies *******/
mjsousa@959: /************************************************************/
mjsousa@959: /************************************************************/
mjsousa@959:
mjsousa@959: // constructor & destructor
mjsousa@959: remove_forward_dependencies_c:: remove_forward_dependencies_c(void) {
mjsousa@959: find_forward_dependencies = new find_forward_dependencies_c(&declared_identifiers);
mjsousa@959: current_display_error_level = error_level_default;
mjsousa@959: error_count = 0;
mjsousa@959: }
mjsousa@959:
mjsousa@959: remove_forward_dependencies_c::~remove_forward_dependencies_c(void) {
mjsousa@959: delete find_forward_dependencies;
mjsousa@959: }
mjsousa@959:
mjsousa@959:
mjsousa@959: int remove_forward_dependencies_c::get_error_count(void) {
mjsousa@959: return error_count;
mjsousa@959: }
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959: library_c *remove_forward_dependencies_c::create_new_tree(symbol_c *tree) {
mjsousa@959: library_c *old_tree = dynamic_cast(tree);
mjsousa@959: if (NULL == old_tree) ERROR;
mjsousa@959: new_tree = new library_c;
mjsousa@959: *((symbol_c *)new_tree) = *((symbol_c *)tree); // copy any annotations from tree to new_tree;
mjsousa@959: new_tree->clear(); // remove all elements from list.
mjsousa@959: tree->accept(*this);
mjsousa@959: return new_tree;
mjsousa@959: }
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959: void *remove_forward_dependencies_c::handle_library_symbol(symbol_c *symbol, symbol_c *name, symbol_c *search1, symbol_c *search2, symbol_c *search3) {
mjsousa@959: if (inserted_symbols.find(symbol) != inserted_symbols.end()) return NULL; // already previously inserted into new_tree and declared_identifiers. Do not handle again!
mjsousa@959: 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!
mjsousa@959: 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!
mjsousa@959: 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!
mjsousa@959: /* no forward dependencies found => insert into new AST, and add to the 'defined identifiers' and 'inserted symbol' lists */
mjsousa@971: if (declared_identifiers.find(name) == declared_identifiers.end())
mjsousa@959: 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!)
mjsousa@959: inserted_symbols.insert(symbol);
mjsousa@959: new_tree->add_element(current_code_generation_pragma);
mjsousa@959: new_tree->add_element(symbol);
mjsousa@959: return NULL;
mjsousa@959: }
mjsousa@959:
mjsousa@959:
mjsousa@959: /* Tell the user that the source code contains a circular dependency */
mjsousa@959: void remove_forward_dependencies_c::print_circ_error(library_c *symbol) {
mjsousa@959: /* Note that we only print Functions and FBs, as Programs and Configurations cannot contain circular references due to syntax rules */
mjsousa@959: /* Note too that circular references in derived datatypes is also not possible due to sytax! */
mjsousa@959: int initial_error_count = error_count;
mjsousa@959: for (int i = 0; i < symbol->n; i++)
msousa@1041: if ( (inserted_symbols.find(symbol->get_element(i)) == inserted_symbols.end()) // if not copied to new AST
msousa@1041: &&( (NULL != dynamic_cast (symbol->get_element(i))) // and (is a FB
msousa@1041: ||(NULL != dynamic_cast < function_declaration_c *>(symbol->get_element(i))))) // or a Function)
msousa@1041: STAGE3_ERROR(0, symbol->get_element(i), symbol->get_element(i), "POU (%s) contains a self-reference and/or belongs in a circular referencing loop", get_datatype_info_c::get_id_str(symbol->get_element(i)));
mjsousa@959: if (error_count == initial_error_count) ERROR; // We were unable to determine which POUs contain the circular references!!
mjsousa@959: }
mjsousa@959:
mjsousa@959:
mjsousa@959: /***************************/
mjsousa@959: /* B 0 - Programming Model */
mjsousa@959: /***************************/
mjsousa@959: /* 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 */
mjsousa@959: // SYM_LIST(library_c, enumvalue_symtable_t enumvalue_symtable;)
mjsousa@959: void *remove_forward_dependencies_c::visit(library_c *symbol) {
mjsousa@959: /* this method is the expected entry point for this visitor, and implements the main algorithm of the visitor */
mjsousa@959:
mjsousa@959: /* first insert all the derived datatype declarations, in the same order by which they are delcared in the original AST */
mjsousa@959: /* 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! */
mjsousa@959: for (int i = 0; i < symbol->n; i++)
msousa@1041: if (NULL != dynamic_cast (symbol->get_element(i)))
msousa@1041: new_tree->add_element(symbol->get_element(i));
mjsousa@959:
mjsousa@959: /* now do the POUs, in whatever order is necessary to guarantee no forward references. */
mjsousa@959: long long int old_tree_pou_count = pou_count_c::get_count(symbol);
mjsousa@959: // if no code generation pragma exists before the first entry in the library, the default is to enable code generation.
mjsousa@959: enable_code_generation_pragma_c *default_code_generation_pragma = new enable_code_generation_pragma_c;
mjsousa@959: int prev_n;
mjsousa@959: cycle_count = 0;
mjsousa@959: do {
mjsousa@959: cycle_count++;
mjsousa@959: prev_n = new_tree->n;
mjsousa@959: current_code_generation_pragma = default_code_generation_pragma;
msousa@1041: for (int i = 0; i < symbol->n; i++) symbol->get_element(i)->accept(*this);
mjsousa@959: } while (prev_n != new_tree->n); // repeat while new elementns are still being added to the new AST
mjsousa@959:
mjsousa@959: if (old_tree_pou_count != pou_count_c::get_count(new_tree))
mjsousa@959: print_circ_error(symbol);
mjsousa@959:
mjsousa@959: return NULL;
mjsousa@959: }
mjsousa@959:
mjsousa@959:
mjsousa@959: /**************************************/
mjsousa@959: /* B.1.5 - Program organization units */
mjsousa@959: /**************************************/
mjsousa@959: /***********************/
mjsousa@959: /* B 1.5.1 - Functions */
mjsousa@959: /***********************/
mjsousa@959: // SYM_REF4(function_declaration_c, derived_function_name, type_name, var_declarations_list, function_body, enumvalue_symtable_t enumvalue_symtable;)
mjsousa@959: void *remove_forward_dependencies_c::visit(function_declaration_c *symbol)
mjsousa@959: {return handle_library_symbol(symbol, symbol->derived_function_name, symbol->type_name, symbol->var_declarations_list, symbol->function_body);}
mjsousa@959: /*****************************/
mjsousa@959: /* B 1.5.2 - Function Blocks */
mjsousa@959: /*****************************/
mjsousa@959: /* FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */
mjsousa@959: // SYM_REF3(function_block_declaration_c, fblock_name, var_declarations, fblock_body, enumvalue_symtable_t enumvalue_symtable;)
mjsousa@959: void *remove_forward_dependencies_c::visit(function_block_declaration_c *symbol)
mjsousa@959: {return handle_library_symbol(symbol, symbol->fblock_name, symbol->var_declarations, symbol->fblock_body);}
mjsousa@959: /**********************/
mjsousa@959: /* B 1.5.3 - Programs */
mjsousa@959: /**********************/
mjsousa@959: /* PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM */
mjsousa@959: // SYM_REF3(program_declaration_c, program_type_name, var_declarations, function_block_body, enumvalue_symtable_t enumvalue_symtable;)
mjsousa@959: void *remove_forward_dependencies_c::visit(program_declaration_c *symbol)
mjsousa@959: {return handle_library_symbol(symbol, symbol->program_type_name, symbol->var_declarations, symbol->function_block_body);}
mjsousa@959: /********************************/
mjsousa@959: /* B 1.7 Configuration elements */
mjsousa@959: /********************************/
mjsousa@959: /* CONFIGURATION configuration_name (...) END_CONFIGURATION */
mjsousa@959: // SYM_REF5(configuration_declaration_c, configuration_name, global_var_declarations, resource_declarations, access_declarations, instance_specific_initializations, enumvalue_symtable_t enumvalue_symtable;)
mjsousa@959: void *remove_forward_dependencies_c::visit(configuration_declaration_c *symbol)
mjsousa@959: {return handle_library_symbol(symbol, symbol->configuration_name, symbol->global_var_declarations, symbol->resource_declarations, symbol->access_declarations);}
mjsousa@959: /********************/
mjsousa@959: /* 2.1.6 - Pragmas */
mjsousa@959: /********************/
mjsousa@959: void *remove_forward_dependencies_c::visit(disable_code_generation_pragma_c *symbol) {current_code_generation_pragma = symbol; return NULL;}
mjsousa@959: void *remove_forward_dependencies_c::visit( enable_code_generation_pragma_c *symbol) {current_code_generation_pragma = symbol; return NULL;}
mjsousa@959: /* I have no ideia what this pragma is. Where should we place it in the re-ordered tree?
mjsousa@959: * Without knowing the semantics of the pragma, it is not possible to hande it correctly.
mjsousa@959: * We therefore print out an error message, and abort!
mjsousa@959: */
mjsousa@959: // TODO: print error message!
mjsousa@959: void *remove_forward_dependencies_c::visit(pragma_c *symbol) {
mjsousa@959: if (1 != cycle_count) return NULL; // only handle unknown pragmas in the first cycle!
mjsousa@959: 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.");
mjsousa@959: new_tree->add_element(symbol);
mjsousa@959: return NULL;
mjsousa@959: }
mjsousa@959:
mjsousa@959:
mjsousa@959:
mjsousa@959: