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: