etisserant@0: /* msousa@261: * matiec - a compiler for the programming languages defined in IEC 61131-3 msousa@261: * Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) Edouard@279: * Copyright (C) 2007-2011 Laurent Bessard and Edouard Tisserant msousa@261: * msousa@261: * This program is free software: you can redistribute it and/or modify msousa@261: * it under the terms of the GNU General Public License as published by msousa@261: * the Free Software Foundation, either version 3 of the License, or msousa@261: * (at your option) any later version. msousa@261: * msousa@261: * This program is distributed in the hope that it will be useful, msousa@261: * but WITHOUT ANY WARRANTY; without even the implied warranty of msousa@261: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the msousa@261: * GNU General Public License for more details. msousa@261: * msousa@261: * You should have received a copy of the GNU General Public License msousa@261: * along with this program. If not, see . msousa@261: * etisserant@0: * etisserant@0: * This code is made available on the understanding that it will not be etisserant@0: * used in safety-critical situations without a full and competent review. etisserant@0: */ etisserant@0: etisserant@0: /* msousa@261: * An IEC 61131-3 compiler. etisserant@0: * etisserant@0: * Based on the etisserant@0: * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) etisserant@0: * etisserant@0: */ etisserant@0: etisserant@0: /* etisserant@0: * Declaration of the Abstract Syntax data structure components etisserant@0: */ etisserant@0: etisserant@0: /* etisserant@0: * ABSYNTAX.H etisserant@0: * etisserant@0: * This generates the parse tree structure used to bind the components etisserant@0: * identified by Bison in the correct syntax order. At the end of the etisserant@0: * Bison analysis the tree is walked in a sequential fashion generating etisserant@0: * the relavent code. etisserant@0: */ etisserant@0: etisserant@0: #ifndef _ABSYNTAX_HH etisserant@0: #define _ABSYNTAX_HH etisserant@0: etisserant@0: etisserant@0: #include // required for NULL msousa@417: #include msousa@738: #include conti@564: #include msousa@565: #include // required for uint64_t, etc... msousa@607: #include "../main.hh" // required for uint8_t, real_64_t, ..., and the macros INT8_MAX, REAL32_MAX, ... */ msousa@607: msousa@607: msousa@568: msousa@568: etisserant@0: /* Forward declaration of the visitor interface conti@564: * declared in the visitor.hh file etisserant@0: * We cannot include the visitor.hh file, as it will etisserant@0: * include this same file first, as it too requires references etisserant@0: * to the abstract syntax classes defined here. etisserant@0: */ etisserant@0: class visitor_c; // forward declaration etisserant@0: etisserant@0: etisserant@0: class symbol_c; // forward declaration etisserant@0: etisserant@0: msousa@569: msousa@569: msousa@569: msousa@569: msousa@569: msousa@738: /* Case insensitive string compare */ msousa@738: /* Case insensitive string compare copied from msousa@738: * "The C++ Programming Language" - 3rd Edition msousa@738: * by Bjarne Stroustrup, ISBN 0201889544. msousa@738: */ msousa@738: class nocasecmp_c { msousa@738: public: msousa@738: bool operator() (const std::string& x, const std::string& y) const { msousa@738: std::string::const_iterator ix = x.begin(); msousa@738: std::string::const_iterator iy = y.begin(); msousa@738: msousa@738: for(; (ix != x.end()) && (iy != y.end()) && (toupper(*ix) == toupper(*iy)); ++ix, ++iy); msousa@738: if (ix == x.end()) return (iy != y.end()); msousa@738: if (iy == y.end()) return false; msousa@738: return (toupper(*ix) < toupper(*iy)); msousa@738: }; msousa@738: }; msousa@738: msousa@738: msousa@738: mjsousa@965: /*** constant folding ***/ mjsousa@965: /* During stage 3 (semantic analysis/checking) we will be doing constant folding. mjsousa@965: * That algorithm will anotate the abstract syntax tree with the result of operations mjsousa@965: * on literals (i.e. 44 + 55 will store the result 99). mjsousa@965: * Since the same source code (e.g. 1 + 0) may actually be a BOOL or an ANY_INT, mjsousa@965: * or an ANY_BIT, we need to handle all possibilities, and determine the result of the mjsousa@965: * operation assuming each type. mjsousa@965: * For this reason, we have one entry for each possible type, with some expressions mjsousa@965: * having more than one entry filled in! mjsousa@965: */ mjsousa@965: class const_value_c { mjsousa@965: public: mjsousa@965: typedef enum { cs_undefined, /* not defined/not yet evaluated --> const_value is not valid! */ mjsousa@965: cs_non_const, /* we have determined that expression is not a const value --> const_value is not valid! */ mjsousa@965: cs_const_value, /* const value is valid */ mjsousa@965: cs_overflow /* result produced overflow or underflow --> const_value is not valid! */ mjsousa@965: } const_status_t; mjsousa@965: mjsousa@965: template class const_value__ { mjsousa@965: const_status_t status; mjsousa@965: value_type value; mjsousa@965: mjsousa@965: public: mjsousa@965: const_value__(void): status(cs_undefined), value(0) {}; mjsousa@965: mjsousa@965: value_type get(void) {return value;} mjsousa@965: void set(value_type value_) {status = cs_const_value; value = value_;} mjsousa@965: void set_overflow(void) {status = cs_overflow ;} mjsousa@965: void set_nonconst(void) {status = cs_non_const ;} mjsousa@965: bool is_valid (void) {return (status == cs_const_value);} mjsousa@965: bool is_overflow (void) {return (status == cs_overflow );} mjsousa@965: bool is_nonconst (void) {return (status == cs_non_const );} mjsousa@978: bool is_undefined(void) {return (status == cs_undefined );} mjsousa@965: bool is_zero (void) {return (is_valid() && (get() == 0));} mjsousa@983: mjsousa@983: /* comparison operator */ mjsousa@983: bool operator==(const const_value__ cv) { mjsousa@983: return ( ((status!=cs_const_value) && (status==cv.status)) mjsousa@983: || ((status==cs_const_value) && (value ==cv.value ))); mjsousa@983: } mjsousa@983: mjsousa@965: }; mjsousa@965: mjsousa@965: const_value__< int64_t> _int64; /* status is initialised to UNDEFINED */ mjsousa@965: const_value__ _uint64; /* status is initialised to UNDEFINED */ mjsousa@965: const_value__ _real64; /* status is initialised to UNDEFINED */ mjsousa@965: const_value__ _bool; /* status is initialised to UNDEFINED */ mjsousa@965: mjsousa@965: /* default constructor and destructor */ mjsousa@965: const_value_c(void) {}; mjsousa@965: ~const_value_c(void) {}; mjsousa@983: mjsousa@983: /* comparison operator */ mjsousa@983: bool operator==(const const_value_c cv) mjsousa@983: {return ((_int64==cv._int64) && (_uint64==cv._uint64) && (_real64==cv._real64) && (_bool==cv._bool));} mjsousa@1000: mjsousa@1000: /* return true if at least one of the const values (int, real, ...) is a valid const value */ mjsousa@1000: bool is_const(void) mjsousa@1000: {return (_int64.is_valid() || _uint64.is_valid() || _real64.is_valid() || _bool.is_valid());} mjsousa@965: }; msousa@569: msousa@569: msousa@569: etisserant@0: /* The base class of all symbols */ etisserant@0: class symbol_c { etisserant@0: etisserant@0: public: msousa@659: /* WARNING: only use this method for debugging purposes!! */ msousa@659: virtual const char *absyntax_cname(void) {return "symbol_c";}; msousa@659: etisserant@0: /* mjsousa@913: * Annotations produced during stage 1_2 mjsousa@913: */ mjsousa@936: /* Points to the parent symbol in the AST, i.e. the symbol in the AST that will contain the current symbol */ mjsousa@936: symbol_c *parent; mjsousa@936: mjsousa@913: /* Line number for the purposes of error checking. */ mario@69: int first_line; mario@69: int first_column; msousa@286: const char *first_file; /* filename referenced by first line/column */ msousa@287: long int first_order; /* relative order in which it is read by lexcial analyser */ mario@69: int last_line; mario@69: int last_column; msousa@286: const char *last_file; /* filename referenced by last line/column */ msousa@287: long int last_order; /* relative order in which it is read by lexcial analyser */ msousa@569: msousa@569: msousa@569: /* msousa@569: * Annotations produced during stage 3 msousa@738: */ msousa@569: /*** Data type analysis ***/ msousa@417: std::vector candidate_datatypes; /* All possible data types the expression/literal/etc. may take. Filled in stage3 by fill_candidate_datatypes_c class */ msousa@459: /* Data type of the expression/literal/etc. Filled in stage3 by narrow_candidate_datatypes_c msousa@459: * If set to NULL, it means it has not yet been evaluated. msousa@459: * If it points to an object of type invalid_type_name_c, it means it is invalid. msousa@459: * Otherwise, it points to an object of the apropriate data type (e.g. int_type_name_c, bool_type_name_c, ...) msousa@459: */ conti@564: symbol_c *datatype; mjsousa@889: /* The POU in which the symbolic variable (or structured variable, or array variable, or located variable, - any more?) mjsousa@889: * was declared. This will point to a Configuration, Resource, Program, FB, or Function. mjsousa@938: * This is set in stage 3 by the datatype analyser algorithm (fill/narrow) for the symbols: mjsousa@938: * symbolic_variable_c, array_variable_c, structured_variable_c mjsousa@889: */ mjsousa@889: symbol_c *scope; conti@564: msousa@569: /*** constant folding ***/ mjsousa@965: /* If the symbol has a constant numerical value, this will be set to that value by constant_folding_c */ mjsousa@965: const_value_c const_value; msousa@569: msousa@738: /*** Enumeration datatype checking ***/ msousa@738: /* Not all symbols will contain the following anotations, which is why they are not declared here in symbol_c msousa@738: * They will be declared only inside the symbols that require them (have a look at absyntax.def) msousa@738: */ msousa@738: typedef std::multimap enumvalue_symtable_t; msousa@738: mjsousa@913: /* mjsousa@913: * Annotations produced during stage 4 mjsousa@913: */ mjsousa@913: /* Since we support several distinct stage_4 implementations, having explicit entries for each mjsousa@913: * possible use would quickly get out of hand. mjsousa@913: * We therefore simply add a map, that each stage 4 may use for all its needs. mjsousa@913: */ mjsousa@913: typedef std::map anotations_map_t; mjsousa@913: anotations_map_t anotations_map; mjsousa@913: etisserant@0: etisserant@0: public: etisserant@0: /* default constructor */ msousa@287: symbol_c(int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, /* order in which it is read by lexcial analyser */ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0 /* order in which it is read by lexcial analyser */ mario@69: ); etisserant@0: etisserant@0: /* default destructor */ etisserant@0: /* must be virtual so compiler does not complain... */ etisserant@0: virtual ~symbol_c(void) {return;}; etisserant@0: etisserant@0: virtual void *accept(visitor_c &visitor) {return NULL;}; etisserant@0: }; etisserant@0: etisserant@0: msousa@738: msousa@738: etisserant@0: class token_c: public symbol_c { etisserant@0: public: msousa@659: /* WARNING: only use this method for debugging purposes!! */ msousa@659: virtual const char *absyntax_cname(void) {return "token_c";}; msousa@659: etisserant@0: /* the value of the symbol. */ etisserant@0: const char *value; etisserant@0: etisserant@0: public: msousa@286: token_c(const char *value, msousa@287: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, /* order in which it is read by lexcial analyser */ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0 /* order in which it is read by lexcial analyser */ msousa@286: ); etisserant@0: }; etisserant@0: etisserant@0: msousa@738: msousa@738: etisserant@0: /* a list of symbols... */ etisserant@0: class list_c: public symbol_c { etisserant@0: public: msousa@659: /* WARNING: only use this method for debugging purposes!! */ msousa@659: virtual const char *absyntax_cname(void) {return "list_c";}; msousa@659: msousa@655: int c,n; /* c: current capacity of list (malloc'd memory); n: current number of elements in list */ etisserant@0: symbol_c **elements; etisserant@0: etisserant@0: public: msousa@287: list_c(int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, /* order in which it is read by lexcial analyser */ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0 /* order in which it is read by lexcial analyser */ msousa@286: ); msousa@286: msousa@286: list_c(symbol_c *elem, msousa@287: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, /* order in which it is read by lexcial analyser */ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0 /* order in which it is read by lexcial analyser */ msousa@286: ); msousa@350: /* append a new element to the end of the list */ etisserant@0: virtual void add_element(symbol_c *elem); msousa@350: /* insert a new element before position pos. */ msousa@350: /* To insert into the begining of list, call with pos=0 */ msousa@350: /* To insert into the end of list, call with pos=list->n */ msousa@350: virtual void insert_element(symbol_c *elem, int pos = 0); msousa@438: /* remove element at position pos. */ msousa@438: virtual void remove_element(int pos = 0); mjsousa@958: /* remove all elements from list. Does not delete the elements in the list! */ mjsousa@958: virtual void clear(void); msousa@350: }; msousa@350: msousa@350: msousa@350: msousa@350: msousa@738: msousa@738: msousa@738: msousa@738: msousa@738: msousa@568: #define SYM_LIST(class_name_c, ...) \ msousa@286: class class_name_c: public list_c { \ msousa@286: public: \ msousa@350: __VA_ARGS__ \ msousa@350: public: \ msousa@286: class_name_c( \ msousa@287: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, \ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0); \ msousa@286: class_name_c(symbol_c *elem, \ msousa@287: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, \ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0); \ msousa@286: virtual void *accept(visitor_c &visitor); \ msousa@659: /* WARNING: only use this method for debugging purposes!! */ \ msousa@659: virtual const char *absyntax_cname(void) {return #class_name_c;}; \ msousa@286: }; msousa@286: msousa@286: msousa@568: #define SYM_TOKEN(class_name_c, ...) \ msousa@286: class class_name_c: public token_c { \ msousa@286: public: \ msousa@350: __VA_ARGS__ \ msousa@350: public: \ msousa@286: class_name_c(const char *value, \ msousa@287: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, \ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0); \ msousa@286: virtual void *accept(visitor_c &visitor); \ msousa@659: /* WARNING: only use this method for debugging purposes!! */ \ msousa@659: virtual const char *absyntax_cname(void) {return #class_name_c;}; \ msousa@286: }; msousa@286: msousa@286: msousa@568: #define SYM_REF0(class_name_c, ...) \ msousa@350: class class_name_c: public symbol_c { \ msousa@350: public: \ msousa@350: __VA_ARGS__ \ msousa@286: public: \ msousa@286: class_name_c( \ msousa@287: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, \ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0); \ msousa@286: virtual void *accept(visitor_c &visitor); \ msousa@659: /* WARNING: only use this method for debugging purposes!! */ \ msousa@659: virtual const char *absyntax_cname(void) {return #class_name_c;}; \ msousa@286: }; msousa@286: msousa@286: msousa@350: #define SYM_REF1(class_name_c, ref1, ...) \ msousa@350: class class_name_c: public symbol_c { \ msousa@350: public: \ msousa@350: symbol_c *ref1; \ msousa@350: __VA_ARGS__ \ msousa@350: public: \ msousa@350: class_name_c(symbol_c *ref1, \ msousa@350: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, \ msousa@350: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0); \ msousa@350: virtual void *accept(visitor_c &visitor); \ msousa@659: /* WARNING: only use this method for debugging purposes!! */ \ msousa@659: virtual const char *absyntax_cname(void) {return #class_name_c;}; \ msousa@350: }; msousa@350: msousa@350: msousa@350: #define SYM_REF2(class_name_c, ref1, ref2, ...) \ msousa@350: class class_name_c: public symbol_c { \ msousa@350: public: \ msousa@350: symbol_c *ref1; \ msousa@350: symbol_c *ref2; \ msousa@350: __VA_ARGS__ \ msousa@286: public: \ msousa@286: class_name_c(symbol_c *ref1, \ msousa@286: symbol_c *ref2 = NULL, \ msousa@287: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, \ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0); \ msousa@286: virtual void *accept(visitor_c &visitor); \ msousa@659: /* WARNING: only use this method for debugging purposes!! */ \ msousa@659: virtual const char *absyntax_cname(void) {return #class_name_c;}; \ msousa@286: }; msousa@286: msousa@286: msousa@350: #define SYM_REF3(class_name_c, ref1, ref2, ref3, ...) \ msousa@350: class class_name_c: public symbol_c { \ msousa@350: public: \ msousa@350: symbol_c *ref1; \ msousa@350: symbol_c *ref2; \ msousa@350: symbol_c *ref3; \ msousa@350: __VA_ARGS__ \ msousa@350: public: \ msousa@350: class_name_c(symbol_c *ref1, \ msousa@350: symbol_c *ref2, \ msousa@350: symbol_c *ref3, \ msousa@350: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, \ msousa@350: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0); \ msousa@350: virtual void *accept(visitor_c &visitor); \ msousa@659: /* WARNING: only use this method for debugging purposes!! */ \ msousa@659: virtual const char *absyntax_cname(void) {return #class_name_c;}; \ msousa@350: }; msousa@350: msousa@350: msousa@350: #define SYM_REF4(class_name_c, ref1, ref2, ref3, ref4, ...) \ msousa@286: class class_name_c: public symbol_c { \ msousa@286: public: \ msousa@286: symbol_c *ref1; \ msousa@286: symbol_c *ref2; \ msousa@286: symbol_c *ref3; \ msousa@286: symbol_c *ref4; \ msousa@350: __VA_ARGS__ \ msousa@286: public: \ msousa@286: class_name_c(symbol_c *ref1, \ msousa@286: symbol_c *ref2, \ msousa@286: symbol_c *ref3, \ msousa@286: symbol_c *ref4 = NULL, \ msousa@287: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, \ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0); \ msousa@286: virtual void *accept(visitor_c &visitor); \ msousa@659: /* WARNING: only use this method for debugging purposes!! */ \ msousa@659: virtual const char *absyntax_cname(void) {return #class_name_c;}; \ msousa@286: }; msousa@286: msousa@286: msousa@568: #define SYM_REF5(class_name_c, ref1, ref2, ref3, ref4, ref5, ...) \ msousa@286: class class_name_c: public symbol_c { \ msousa@286: public: \ msousa@286: symbol_c *ref1; \ msousa@286: symbol_c *ref2; \ msousa@286: symbol_c *ref3; \ msousa@286: symbol_c *ref4; \ msousa@286: symbol_c *ref5; \ msousa@350: __VA_ARGS__ \ msousa@286: public: \ msousa@286: class_name_c(symbol_c *ref1, \ msousa@286: symbol_c *ref2, \ msousa@286: symbol_c *ref3, \ msousa@286: symbol_c *ref4, \ msousa@286: symbol_c *ref5, \ msousa@287: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, \ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0); \ msousa@286: virtual void *accept(visitor_c &visitor); \ msousa@659: /* WARNING: only use this method for debugging purposes!! */ \ msousa@659: virtual const char *absyntax_cname(void) {return #class_name_c;}; \ msousa@286: }; msousa@286: msousa@286: msousa@350: #define SYM_REF6(class_name_c, ref1, ref2, ref3, ref4, ref5, ref6, ...) \ msousa@286: class class_name_c: public symbol_c { \ msousa@286: public: \ msousa@286: symbol_c *ref1; \ msousa@286: symbol_c *ref2; \ msousa@286: symbol_c *ref3; \ msousa@286: symbol_c *ref4; \ msousa@286: symbol_c *ref5; \ msousa@286: symbol_c *ref6; \ msousa@350: __VA_ARGS__ \ msousa@286: public: \ msousa@286: class_name_c(symbol_c *ref1, \ msousa@286: symbol_c *ref2, \ msousa@286: symbol_c *ref3, \ msousa@286: symbol_c *ref4, \ msousa@286: symbol_c *ref5, \ msousa@286: symbol_c *ref6 = NULL, \ msousa@287: int fl = 0, int fc = 0, const char *ffile = NULL /* filename */, long int forder=0, \ msousa@287: int ll = 0, int lc = 0, const char *lfile = NULL /* filename */, long int lorder=0); \ msousa@286: virtual void *accept(visitor_c &visitor); \ msousa@659: /* WARNING: only use this method for debugging purposes!! */ \ msousa@659: virtual const char *absyntax_cname(void) {return #class_name_c;}; \ etisserant@0: }; etisserant@0: etisserant@0: etisserant@0: #include "absyntax.def" etisserant@0: etisserant@0: etisserant@0: etisserant@0: #undef SYM_LIST etisserant@0: #undef SYM_TOKEN etisserant@0: #undef SYM_REF0 mario@69: #undef SYM_REF1 etisserant@0: #undef SYM_REF2 mario@69: #undef SYM_REF3 etisserant@0: #undef SYM_REF4 mario@69: #undef SYM_REF5 etisserant@0: #undef SYM_REF6 etisserant@0: etisserant@0: #endif /* _ABSYNTAX_HH */