mario@181: /* msousa@265: * matiec - a compiler for the programming languages defined in IEC 61131-3 msousa@265: * msousa@265: * Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) Edouard@279: * Copyright (C) 2007-2011 Laurent Bessard and Edouard Tisserant msousa@265: * msousa@265: * This program is free software: you can redistribute it and/or modify msousa@265: * it under the terms of the GNU General Public License as published by msousa@265: * the Free Software Foundation, either version 3 of the License, or msousa@265: * (at your option) any later version. msousa@265: * msousa@265: * This program is distributed in the hope that it will be useful, msousa@265: * but WITHOUT ANY WARRANTY; without even the implied warranty of msousa@265: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the msousa@265: * GNU General Public License for more details. msousa@265: * msousa@265: * You should have received a copy of the GNU General Public License msousa@265: * along with this program. If not, see . msousa@265: * mario@181: * mario@181: * This code is made available on the understanding that it will not be mario@181: * used in safety-critical situations without a full and competent review. mario@181: */ mario@181: mario@181: /* msousa@265: * An IEC 61131-3 compiler. mario@181: * mario@181: * Based on the mario@181: * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) mario@181: * mario@181: */ mario@181: mario@181: /* mario@181: * Determine the default initial value of a type declaration. mario@181: * mario@181: * This is part of the 4th stage that generates mario@181: * a c++ source program equivalent to the IL and ST mario@181: * code. mario@181: */ mario@181: /* Given a type definition declration, determine its default mario@181: * initial value. Note that types based on other types mario@181: * may have to iterate through each type it is based on mario@181: * to determine the initial value. mario@181: * E.g. mario@181: * TYPE mario@181: * A_t : INT := 10; mario@181: * B_t : A_t := 20; mario@181: * C_t : B_t; mario@181: * D_t : C_t := 40; mario@181: * END_TYPE mario@181: * Where the default initial value for C_t is 20! mario@181: */ mario@181: /* NOTE: The main program only needs one instance of mario@181: * this class of object. This class mario@181: * is therefore a singleton. mario@181: */ mario@181: mario@181: mario@181: #include "absyntax_utils.hh" mario@181: mario@181: //#define DEBUG mario@181: #ifdef DEBUG mario@181: #define TRACE(classname) printf("\n____%s____\n",classname); mario@181: #else mario@181: #define TRACE(classname) mario@181: #endif mario@181: mario@181: mario@181: type_initial_value_c *type_initial_value_c::instance(void) { mario@181: if (_instance != NULL) mario@181: return _instance; mario@181: mario@181: _instance = new type_initial_value_c; mario@181: mjsousa@925: null_literal = new ref_value_null_literal_c(); mjsousa@925: real_0 = new real_c("0"); mjsousa@925: integer_0 = new integer_c("0"); mjsousa@925: integer_1 = new integer_c("1"); mjsousa@925: bool_0 = new boolean_literal_c(new bool_type_name_c(),new boolean_false_c()); mario@181: /* FIXME: Our current implementation only allows dates from 1970 onwards, mario@181: * but the standard defines the date 0001-01-01 as the default value mario@181: * for the DATE data type. Untill we fix our implementation, we use 1970-01-01 mario@181: * as our default value!! mario@181: */ msousa@762: //date_literal_0 = new date_literal_c(integer_1, integer_1, integer_1); mjsousa@925: date_literal_0 = new date_literal_c(new integer_c("1970"), integer_1, integer_1); mario@181: daytime_literal_0 = new daytime_c(integer_0, integer_0, real_0); mjsousa@925: time_0 = new duration_c (new time_type_name_c(), NULL, new interval_c(NULL, NULL, NULL, integer_0, NULL)); // T#0s mjsousa@925: date_0 = new date_c (new date_type_name_c(), date_literal_0); // D#0001-01-01 mjsousa@925: tod_0 = new time_of_day_c (new tod_type_name_c(), daytime_literal_0); // TOD#00:00:00 mjsousa@925: dt_0 = new date_and_time_c(new dt_type_name_c(), date_literal_0, daytime_literal_0); // DT#0001-01-01-00:00:00 mjsousa@925: string_0 = new single_byte_character_string_c("''"); mjsousa@925: wstring_0 = new double_byte_character_string_c("\"\""); mario@181: mario@181: return _instance; mario@181: } mario@181: mario@181: type_initial_value_c::type_initial_value_c(void) {} mario@181: mario@181: mario@181: msousa@762: symbol_c *type_initial_value_c::get(symbol_c *type) { mario@181: TRACE("type_initial_value_c::get(): called "); msousa@762: return (symbol_c *)type->accept(*type_initial_value_c::instance()); mario@181: } mario@181: mario@181: mario@181: mario@181: mario@181: void *type_initial_value_c::handle_type_spec(symbol_c *base_type_name, symbol_c *type_spec_init) { mario@181: if (type_spec_init != NULL) mario@181: return type_spec_init; mjsousa@958: /* no initial value specified, so we return the initial value of the type this type is based on... */ mario@181: return base_type_name->accept(*this); mario@181: } mario@181: mario@181: mjsousa@958: void *type_initial_value_c::handle_type_name(symbol_c *type_name) { mjsousa@945: /* look up the type declaration... */ mjsousa@971: type_symtable_t::iterator iter = type_symtable.find(type_name); mjsousa@945: /* Type declaration not found!! */ mjsousa@958: /* NOTE: Variables declared out of function block 'data types',for eg: VAR timer: TON; END_VAR mjsousa@958: * do not have a default value, so (TON) will never be found in the type symbol table. This means mjsousa@958: * we cannot simply consider this an error and abort, but must rather return a NULL. mjsousa@945: */ mjsousa@971: if (iter == type_symtable.end()) return NULL; mjsousa@971: mjsousa@971: return iter->second->accept(*this); // iter->second is the type_decl mjsousa@945: } mjsousa@945: mjsousa@958: /* visitor for identifier_c should no longer be necessary. All references to derived datatypes are now stored in then */ mjsousa@958: /* AST using either poutype_identifier_c or derived_datatype_identifier_c. In principe, the following should not be necesasry */ mjsousa@958: void *type_initial_value_c::visit( identifier_c *symbol) {return handle_type_name(symbol);} /* should never occur */ mjsousa@958: void *type_initial_value_c::visit( poutype_identifier_c *symbol) {return handle_type_name(symbol);} /* in practice it might never get called, as FB, Functions and Programs do not have initial value */ mjsousa@958: void *type_initial_value_c::visit(derived_datatype_identifier_c *symbol) {return handle_type_name(symbol);} mario@181: mario@181: /***********************************/ mario@181: /* B 1.3.1 - Elementary Data Types */ mario@181: /***********************************/ msousa@257: void *type_initial_value_c::visit(time_type_name_c *symbol) {return (void *)time_0;} msousa@257: void *type_initial_value_c::visit(bool_type_name_c *symbol) {return (void *)bool_0;} msousa@257: void *type_initial_value_c::visit(sint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(int_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(dint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(lint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(usint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(uint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(udint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(ulint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(real_type_name_c *symbol) {return (void *)real_0;} msousa@257: void *type_initial_value_c::visit(lreal_type_name_c *symbol) {return (void *)real_0;} msousa@257: void *type_initial_value_c::visit(date_type_name_c *symbol) {return (void *)date_0;} msousa@257: void *type_initial_value_c::visit(tod_type_name_c *symbol) {return (void *)tod_0;} msousa@257: void *type_initial_value_c::visit(dt_type_name_c *symbol) {return (void *)dt_0;} msousa@257: void *type_initial_value_c::visit(byte_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(word_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(dword_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(lword_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(string_type_name_c *symbol) {return (void *)string_0;} msousa@257: void *type_initial_value_c::visit(wstring_type_name_c *symbol) {return (void *)wstring_0;} msousa@257: msousa@257: void *type_initial_value_c::visit(safetime_type_name_c *symbol) {return (void *)time_0;} msousa@257: void *type_initial_value_c::visit(safebool_type_name_c *symbol) {return (void *)bool_0;} msousa@257: void *type_initial_value_c::visit(safesint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safeint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safedint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safelint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safeusint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safeuint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safeudint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safeulint_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safereal_type_name_c *symbol) {return (void *)real_0;} msousa@257: void *type_initial_value_c::visit(safelreal_type_name_c *symbol) {return (void *)real_0;} msousa@257: void *type_initial_value_c::visit(safedate_type_name_c *symbol) {return (void *)date_0;} msousa@257: void *type_initial_value_c::visit(safetod_type_name_c *symbol) {return (void *)tod_0;} msousa@257: void *type_initial_value_c::visit(safedt_type_name_c *symbol) {return (void *)dt_0;} msousa@257: void *type_initial_value_c::visit(safebyte_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safeword_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safedword_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safelword_type_name_c *symbol) {return (void *)integer_0;} msousa@257: void *type_initial_value_c::visit(safestring_type_name_c *symbol) {return (void *)string_0;} msousa@257: void *type_initial_value_c::visit(safewstring_type_name_c *symbol) {return (void *)wstring_0;} mario@181: mario@181: /********************************/ mario@181: /* B 1.3.3 - Derived data types */ mario@181: /********************************/ mario@181: /* simple_type_name ':' simple_spec_init */ mario@181: void *type_initial_value_c::visit(simple_type_declaration_c *symbol) { mario@181: return symbol->simple_spec_init->accept(*this); mario@181: } mario@181: /* simple_specification ASSIGN constant */ mario@181: void *type_initial_value_c::visit(simple_spec_init_c *symbol) { mario@181: return handle_type_spec(symbol->simple_specification, symbol->constant); mario@181: } mario@181: /* subrange_type_name ':' subrange_spec_init */ mario@181: void *type_initial_value_c::visit(subrange_type_declaration_c *symbol) { mario@181: return symbol->subrange_spec_init->accept(*this); mario@181: } mario@181: /* subrange_specification ASSIGN signed_integer */ mario@181: void *type_initial_value_c::visit(subrange_spec_init_c *symbol) { mario@181: return handle_type_spec(symbol->subrange_specification, symbol->signed_integer); mario@181: } mario@181: /* integer_type_name '(' subrange')' */ mario@181: void *type_initial_value_c::visit(subrange_specification_c *symbol) { mario@181: /* if no initial value explicitly given, then use the lowest value of the subrange */ mario@181: if (symbol->subrange != NULL) mario@181: return symbol->subrange->accept(*this); mario@181: else mario@181: return symbol->integer_type_name->accept(*this); mario@181: } mario@181: /* signed_integer DOTDOT signed_integer */ mario@181: void *type_initial_value_c::visit(subrange_c *symbol) {return symbol->lower_limit;} mario@181: /* enumerated_type_name ':' enumerated_spec_init */ mario@181: void *type_initial_value_c::visit(enumerated_type_declaration_c *symbol) { mario@181: return symbol->enumerated_spec_init->accept(*this); mario@181: } mario@181: /* enumerated_specification ASSIGN enumerated_value */ mario@181: void *type_initial_value_c::visit(enumerated_spec_init_c *symbol) { mario@181: return handle_type_spec(symbol->enumerated_specification, symbol->enumerated_value); mario@181: } mario@181: /* helper symbol for enumerated_specification->enumerated_spec_init */ mario@181: /* enumerated_value_list ',' enumerated_value */ mario@181: void *type_initial_value_c::visit(enumerated_value_list_c *symbol) { msousa@850: /* stage1_2 never creates an enumerated_value_list_c with no entries. If this occurs, then something must have changed! */ msousa@850: if (symbol->n <= 0) ERROR; mario@181: /* if no initial value explicitly given, then use the lowest value of the subrange */ msousa@1041: return (void *)symbol->get_element(0); mario@181: } mario@181: /* enumerated_type_name '#' identifier */ mario@181: // SYM_REF2(enumerated_value_c, type, value) mario@181: void *type_initial_value_c::visit(enumerated_value_c *symbol) {ERROR; return NULL;} mario@181: /* identifier ':' array_spec_init */ mario@181: void *type_initial_value_c::visit(array_type_declaration_c *symbol) { mario@181: return symbol->array_spec_init->accept(*this); mario@181: } mario@181: /* array_specification [ASSIGN array_initialization} */ mario@181: /* array_initialization may be NULL ! */ mario@181: void *type_initial_value_c::visit(array_spec_init_c *symbol) { mario@181: return handle_type_spec(symbol->array_specification, symbol->array_initialization); mario@181: } mario@181: /* ARRAY '[' array_subrange_list ']' OF non_generic_type_name */ mario@181: void *type_initial_value_c::visit(array_specification_c *symbol) { mario@181: //symbol_c *init_value = (symbol_c *)symbol->non_generic_type_name->accept(*this); mario@181: mario@181: /* Now build a array_initial_elements_list_c list, and populate it mario@181: * with 1 element of the array_initial_elements_c class mario@181: */ mario@181: /* The array_initial_elements_c will contain a reference to the init_value, mario@181: * and another constant representing the number of elements in the array. mario@181: * In essence, we are building the equivilant of the following ST/IL code: mario@181: * New_array_t : ARRAY [1..30, 51..60] of INT := [40(XXX)]; mario@181: * from the user given code mario@181: * New_array_t : ARRAY [1..30, 51..60] of INT; mario@181: * and replacing XXX with the default initial value of INT. mario@181: */ mario@181: /* now we need to determine the number of elements in the array... */ mario@181: /* Easier said than done, as the array may have a list of subranges, as in the mario@181: * example given above!! mario@181: */ mario@181: /* TODO !!!!!*/ mario@181: /* For now, just assume an array with 1 element. mario@181: * I (Mario) want to finish off this part of the code before getting boged down mario@181: * in something else... mario@181: */ mario@181: // NOTE: We are leaking memory, as the integer will never get free'd!! mario@181: //integer_c *integer = new integer_c("1"); mario@181: // NOTE: We are leaking memory, as the array_initial_elements will never get free'd!! mario@181: //array_initial_elements_c *array_initial_elements = new array_initial_elements_c(integer, init_value); mario@181: // NOTE: We are leaking memory, as the array_initial_elements_list will never get free'd!! mario@181: array_initial_elements_list_c *array_initial_elements_list = new array_initial_elements_list_c(); mario@181: //array_initial_elements_list->add_element(array_initial_elements); mario@181: return array_initial_elements_list; mario@181: } mario@181: /* helper symbol for array_specification */ mario@181: /* array_subrange_list ',' subrange */ mario@181: void *type_initial_value_c::visit(array_subrange_list_c *symbol) {ERROR; return NULL;} mario@181: /* array_initialization: '[' array_initial_elements_list ']' */ mario@181: /* helper symbol for array_initialization */ mario@181: /* array_initial_elements_list ',' array_initial_elements */ mario@181: void *type_initial_value_c::visit(array_initial_elements_list_c *symbol) {ERROR; return NULL;} mario@181: /* integer '(' [array_initial_element] ')' */ mario@181: /* array_initial_element may be NULL ! */ mario@181: void *type_initial_value_c::visit(array_initial_elements_c *symbol) {ERROR; return NULL;} mario@181: mario@181: mario@181: mario@181: /* TODO: from this point forward... */ mario@181: mario@181: /* structure_type_name ':' structure_specification */ mario@181: void *type_initial_value_c::visit(structure_type_declaration_c *symbol) {return NULL;} mario@181: /* structure_type_name ASSIGN structure_initialization */ mario@181: /* structure_initialization may be NULL ! */ mario@181: void *type_initial_value_c::visit(initialized_structure_c *symbol) { mario@181: return handle_type_spec(symbol->structure_type_name, symbol->structure_initialization); mario@181: } mario@181: /* helper symbol for structure_declaration */ mario@181: /* structure_declaration: STRUCT structure_element_declaration_list END_STRUCT */ mario@181: /* structure_element_declaration_list structure_element_declaration ';' */ mario@181: void *type_initial_value_c::visit(structure_element_declaration_list_c *symbol) { mario@181: structure_element_initialization_list_c *structure_element_initialization_list = new structure_element_initialization_list_c(); mario@181: return structure_element_initialization_list; mario@181: } mario@181: /* structure_element_name ':' *_spec_init */ mario@181: void *type_initial_value_c::visit(structure_element_declaration_c *symbol) {return NULL;} mario@181: /* helper symbol for structure_initialization */ mario@181: /* structure_initialization: '(' structure_element_initialization_list ')' */ mario@181: /* structure_element_initialization_list ',' structure_element_initialization */ mario@181: void *type_initial_value_c::visit(structure_element_initialization_list_c *symbol) {return NULL;} mario@181: /* structure_element_name ASSIGN value */ mario@181: void *type_initial_value_c::visit(structure_element_initialization_c *symbol) {return NULL;} mario@181: /* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ mario@181: /* mario@181: * NOTE: mario@181: * (Summary: Contrary to what is expected, the mario@181: * string_type_declaration_c is not used to store mario@181: * simple string type declarations that do not include mario@181: * size limits. mario@181: * For e.g.: mario@181: * str1_type: STRING := "hello!" mario@181: * will be stored in a simple_type_declaration_c mario@181: * instead of a string_type_declaration_c. mario@181: * The following: mario@181: * str2_type: STRING [64] := "hello!" mario@181: * will be stored in a sring_type_declaration_c mario@181: * mario@181: * Read on for why this is done... mario@181: * End Summary) mario@181: * mario@181: * According to the spec, the valid construct mario@181: * TYPE new_str_type : STRING := "hello!"; END_TYPE mario@181: * has two possible routes to type_declaration... mario@181: * mario@181: * Route 1: mario@181: * type_declaration: single_element_type_declaration mario@181: * single_element_type_declaration: simple_type_declaration mario@181: * simple_type_declaration: identifier ':' simple_spec_init mario@181: * simple_spec_init: simple_specification ASSIGN constant mario@181: * (shift: identifier <- 'new_str_type') mario@181: * simple_specification: elementary_type_name mario@181: * elementary_type_name: STRING mario@181: * (shift: elementary_type_name <- STRING) mario@181: * (reduce: simple_specification <- elementary_type_name) mario@181: * (shift: constant <- "hello!") mario@181: * (reduce: simple_spec_init: simple_specification ASSIGN constant) mario@181: * (reduce: ...) mario@181: * mario@181: * mario@181: * Route 2: mario@181: * type_declaration: string_type_declaration mario@181: * string_type_declaration: identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init mario@181: * (shift: identifier <- 'new_str_type') mario@181: * elementary_string_type_name: STRING mario@181: * (shift: elementary_string_type_name <- STRING) mario@181: * (shift: string_type_declaration_size <- empty ) mario@181: * string_type_declaration_init: ASSIGN character_string mario@181: * (shift: character_string <- "hello!") mario@181: * (reduce: string_type_declaration_init <- ASSIGN character_string) mario@181: * (reduce: string_type_declaration <- identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init ) mario@181: * (reduce: type_declaration <- string_type_declaration) mario@181: * mario@181: * mario@181: * At first glance it seems that removing route 1 would make mario@181: * the most sense. Unfortunately the construct 'simple_spec_init' mario@181: * shows up multiple times in other rules, so changing this construct mario@181: * would also mean changing all the rules in which it appears. mario@181: * I (Mario) therefore chose to remove route 2 instead. This means mario@181: * that the above declaration gets stored in a mario@181: * simple_type_declaration_c, and not in a string_type_declaration_c mario@181: * as would be expected! mario@181: */ mario@181: /* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ msousa@433: // SYM_REF4(string_type_declaration_c, string_type_name, msousa@433: // elementary_string_type_name, msousa@433: // string_type_declaration_size, msousa@433: // string_type_declaration_init) /* may be == NULL! */ msousa@432: void *type_initial_value_c::visit(string_type_declaration_c *symbol) { msousa@432: return handle_type_spec(symbol->elementary_string_type_name, symbol->string_type_declaration_init); msousa@432: } mario@181: mario@181: mjsousa@925: /* REF_TO (non_generic_type_name | function_block_type_name) */ mjsousa@925: void *type_initial_value_c::visit(ref_spec_c *symbol) { mjsousa@925: return null_literal; mjsousa@925: } mjsousa@925: mjsousa@925: mjsousa@925: /* ref_spec [ ASSIGN ref_initialization ]; */ mjsousa@925: /* NOTE: ref_initialization may be NULL!! */ mjsousa@925: void *type_initial_value_c::visit(ref_spec_init_c *symbol) { mjsousa@925: return handle_type_spec(symbol->ref_spec, symbol->ref_initialization); mjsousa@925: } mjsousa@925: /* identifier ':' ref_spec_init */ mjsousa@925: void *type_initial_value_c::visit(ref_type_decl_c *symbol) { mjsousa@925: return symbol->ref_spec_init->accept(*this); mjsousa@925: } mjsousa@925: mjsousa@925: mjsousa@925: mjsousa@925: mjsousa@925: mjsousa@925: mjsousa@925: mjsousa@925: mjsousa@925: type_initial_value_c *type_initial_value_c::_instance = NULL; mjsousa@925: ref_value_null_literal_c *type_initial_value_c::null_literal = NULL; mjsousa@925: real_c *type_initial_value_c::real_0 = NULL; mjsousa@925: integer_c *type_initial_value_c::integer_0 = NULL; mjsousa@925: integer_c *type_initial_value_c::integer_1 = NULL; mjsousa@925: boolean_literal_c *type_initial_value_c::bool_0 = NULL; mjsousa@925: date_literal_c *type_initial_value_c::date_literal_0 = NULL; mjsousa@925: daytime_c *type_initial_value_c::daytime_literal_0 = NULL; mjsousa@925: duration_c *type_initial_value_c::time_0 = NULL; mjsousa@925: date_c *type_initial_value_c::date_0 = NULL; mjsousa@925: time_of_day_c *type_initial_value_c::tod_0 = NULL; mjsousa@925: date_and_time_c *type_initial_value_c::dt_0 = NULL; mjsousa@925: single_byte_character_string_c *type_initial_value_c::string_0 = NULL; mjsousa@925: double_byte_character_string_c *type_initial_value_c::wstring_0 = NULL;