|
1 /* |
|
2 * matiec - a compiler for the programming languages defined in IEC 61131-3 |
|
3 * |
|
4 * Copyright (C) 2015 Mario de Sousa (msousa@fe.up.pt) |
|
5 * |
|
6 * |
|
7 * This program is free software: you can redistribute it and/or modify |
|
8 * it under the terms of the GNU General Public License as published by |
|
9 * the Free Software Foundation, either version 3 of the License, or |
|
10 * (at your option) any later version. |
|
11 * |
|
12 * This program is distributed in the hope that it will be useful, |
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
15 * GNU General Public License for more details. |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License |
|
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
19 * |
|
20 * |
|
21 * This code is made available on the understanding that it will not be |
|
22 * used in safety-critical situations without a full and competent review. |
|
23 */ |
|
24 |
|
25 /* |
|
26 * An IEC 61131-3 compiler. |
|
27 * |
|
28 * Based on the |
|
29 * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) |
|
30 * |
|
31 */ |
|
32 |
|
33 |
|
34 /* |
|
35 * Case Options Checking: |
|
36 * - Check whether the options in a case statement are repeated, either directly, or in a range. |
|
37 * For example: |
|
38 * case var of |
|
39 * 1: ... <- OK |
|
40 * 2: ... <- OK |
|
41 * 1: ... <- OK (not an error), but produce a warning due to repeated '1'! |
|
42 * 0..8: ...<- OK (not an error), but produce a warning cue to repeated '1' and '2'! |
|
43 */ |
|
44 |
|
45 |
|
46 #include "case_elements_check.hh" |
|
47 |
|
48 |
|
49 #define FIRST_(symbol1, symbol2) (((symbol1)->first_order < (symbol2)->first_order) ? (symbol1) : (symbol2)) |
|
50 #define LAST_(symbol1, symbol2) (((symbol1)->last_order > (symbol2)->last_order) ? (symbol1) : (symbol2)) |
|
51 |
|
52 #define STAGE3_ERROR(error_level, symbol1, symbol2, ...) { \ |
|
53 if (current_display_error_level >= error_level) { \ |
|
54 fprintf(stderr, "%s:%d-%d..%d-%d: error: ", \ |
|
55 FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ |
|
56 LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ |
|
57 fprintf(stderr, __VA_ARGS__); \ |
|
58 fprintf(stderr, "\n"); \ |
|
59 error_count++; \ |
|
60 } \ |
|
61 } |
|
62 |
|
63 |
|
64 #define STAGE3_WARNING(symbol1, symbol2, ...) { \ |
|
65 fprintf(stderr, "%s:%d-%d..%d-%d: warning: ", \ |
|
66 FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ |
|
67 LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ |
|
68 fprintf(stderr, __VA_ARGS__); \ |
|
69 fprintf(stderr, "\n"); \ |
|
70 warning_found = true; \ |
|
71 } |
|
72 |
|
73 |
|
74 #define GET_CVALUE(dtype, symbol) ((symbol)->const_value._##dtype.get()) |
|
75 #define VALID_CVALUE(dtype, symbol) ((symbol)->const_value._##dtype.is_valid()) |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 case_elements_check_c::case_elements_check_c(symbol_c *ignore) { |
|
81 warning_found = false; |
|
82 error_count = 0; |
|
83 current_display_error_level = 0; |
|
84 } |
|
85 |
|
86 |
|
87 |
|
88 case_elements_check_c::~case_elements_check_c(void) { |
|
89 } |
|
90 |
|
91 |
|
92 |
|
93 int case_elements_check_c::get_error_count() { |
|
94 return error_count; |
|
95 } |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 /* compare two integer constants, and determins if s1 < s2 */ |
|
101 static bool less_than(symbol_c *s1, symbol_c *s2) { |
|
102 if ( (VALID_CVALUE( int64, s1)) |
|
103 && (VALID_CVALUE( int64, s2)) |
|
104 && ( GET_CVALUE( int64, s1) < GET_CVALUE( int64, s2))) |
|
105 return true; |
|
106 |
|
107 if ( (VALID_CVALUE(uint64, s1)) |
|
108 && (VALID_CVALUE(uint64, s2)) |
|
109 && ( GET_CVALUE(uint64, s1) < GET_CVALUE(uint64, s2))) |
|
110 return true; |
|
111 |
|
112 if ( (VALID_CVALUE( int64, s1)) |
|
113 && (VALID_CVALUE(uint64, s2)) |
|
114 && ( GET_CVALUE( int64, s1) < 0)) |
|
115 return true; |
|
116 |
|
117 return false; |
|
118 } |
|
119 |
|
120 |
|
121 |
|
122 void case_elements_check_c::check_subr_subr(symbol_c *s1, symbol_c *s2) { |
|
123 subrange_c *sub1 = dynamic_cast<subrange_c *>(s1); |
|
124 subrange_c *sub2 = dynamic_cast<subrange_c *>(s2); |
|
125 |
|
126 if ((NULL == sub1) || (NULL == sub2)) return; |
|
127 symbol_c *l1 = sub1->lower_limit; |
|
128 symbol_c *u1 = sub1->upper_limit; |
|
129 symbol_c *l2 = sub2->lower_limit; |
|
130 symbol_c *u2 = sub2->upper_limit; |
|
131 |
|
132 if (less_than(u1, l2)) return; // no overlap! |
|
133 if (less_than(u2, l1)) return; // no overlap! |
|
134 |
|
135 if ( (VALID_CVALUE( int64, l1) || (VALID_CVALUE(uint64, l1))) |
|
136 && (VALID_CVALUE( int64, l2) || (VALID_CVALUE(uint64, l2))) |
|
137 && (VALID_CVALUE( int64, u1) || (VALID_CVALUE(uint64, u1))) |
|
138 && (VALID_CVALUE( int64, u2) || (VALID_CVALUE(uint64, u2)))) |
|
139 STAGE3_WARNING(s1, s2, "Elements in CASE options have overlapping ranges."); |
|
140 } |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 void case_elements_check_c::check_subr_symb(symbol_c *s1, symbol_c *s2) { |
|
146 subrange_c *subr = NULL; |
|
147 symbol_c *symb = NULL; |
|
148 if ((subr = dynamic_cast<subrange_c *>(s1)) != NULL) {symb = s2;} |
|
149 if ((subr = dynamic_cast<subrange_c *>(s2)) != NULL) {symb = s1;} |
|
150 |
|
151 if ((NULL == subr) || (NULL == symb)) return; |
|
152 symbol_c *lowl = subr->lower_limit; |
|
153 symbol_c *uppl = subr->upper_limit; |
|
154 |
|
155 if ( (VALID_CVALUE(int64, symb)) |
|
156 && (VALID_CVALUE(int64, lowl)) |
|
157 && (VALID_CVALUE(int64, uppl)) |
|
158 && ( GET_CVALUE(int64, symb) >= GET_CVALUE(int64, lowl)) |
|
159 && ( GET_CVALUE(int64, symb) <= GET_CVALUE(int64, uppl))) |
|
160 {STAGE3_WARNING(s1, s2, "Element in CASE option falls within range of another element."); return;} |
|
161 |
|
162 if ( (VALID_CVALUE(uint64, symb)) |
|
163 && (VALID_CVALUE( int64, lowl)) |
|
164 && (VALID_CVALUE(uint64, uppl)) |
|
165 && ( GET_CVALUE( int64, lowl) < 0) |
|
166 && ( GET_CVALUE(uint64, symb) <= GET_CVALUE(uint64, uppl))) |
|
167 {STAGE3_WARNING(s1, s2, "Element in CASE option falls within range of another element."); return;} |
|
168 |
|
169 if ( (VALID_CVALUE(uint64, symb)) |
|
170 && (VALID_CVALUE(uint64, lowl)) |
|
171 && (VALID_CVALUE(uint64, uppl)) |
|
172 && ( GET_CVALUE(uint64, symb) >= GET_CVALUE(uint64, lowl)) |
|
173 && ( GET_CVALUE(uint64, symb) <= GET_CVALUE(uint64, uppl))) |
|
174 {STAGE3_WARNING(s1, s2, "Element in CASE option falls within range of another element."); return;} |
|
175 } |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 #include <typeinfo> |
|
181 void case_elements_check_c::check_symb_symb(symbol_c *s1, symbol_c *s2) { |
|
182 if ( (dynamic_cast<subrange_c *>(s1) != NULL) |
|
183 || (dynamic_cast<subrange_c *>(s2) != NULL)) |
|
184 return; // only run this test if neither s1 nor s2 are subranges! |
|
185 |
|
186 if ( (s1->const_value.is_const() && s2->const_value.is_const() && (s1->const_value == s2->const_value)) // if const, then compare const values (using overloaded '==' operator!) |
|
187 || (compare_identifiers(s1, s2) == 0)) // if token_c, compare tokens! (compare_identifiers() returns 0 when equal tokens!, -1 when either is not token_c) |
|
188 STAGE3_WARNING(s1, s2, "Duplicate element found in CASE options."); |
|
189 } |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 /***************************************/ |
|
200 /* B.3 - Language ST (Structured Text) */ |
|
201 /***************************************/ |
|
202 /********************/ |
|
203 /* B 3.2 Statements */ |
|
204 /********************/ |
|
205 /********************************/ |
|
206 /* B 3.2.3 Selection Statements */ |
|
207 /********************************/ |
|
208 /* CASE expression OF case_element_list ELSE statement_list END_CASE */ |
|
209 // SYM_REF3(case_statement_c, expression, case_element_list, statement_list) |
|
210 void *case_elements_check_c::visit(case_statement_c *symbol) { |
|
211 std::vector<symbol_c *> case_elements_list_local = case_elements_list; // Required when source code contains CASE inside another CASE ! |
|
212 |
|
213 case_elements_list.clear(); |
|
214 symbol->case_element_list->accept(*this); // will fill up the case_elements_list with all the elements in the case! |
|
215 |
|
216 // OK, now check whether we have any overlappings... |
|
217 std::vector<symbol_c *>::iterator s1 = case_elements_list.begin(); |
|
218 for ( ; s1 != case_elements_list.end(); s1++) { |
|
219 std::vector<symbol_c *>::iterator s2 = s1; |
|
220 s2++; // do not compare the value with itself! |
|
221 for (; s2 != case_elements_list.end(); s2++) { |
|
222 // Check for overlapping elements |
|
223 check_subr_subr(*s1, *s2); |
|
224 check_subr_symb(*s1, *s2); |
|
225 check_symb_symb(*s2, *s1); |
|
226 } |
|
227 } |
|
228 |
|
229 case_elements_list = case_elements_list_local; |
|
230 return NULL; |
|
231 } |
|
232 |
|
233 /* helper symbol for case_statement */ |
|
234 // SYM_LIST(case_element_list_c) |
|
235 // void *case_elements_check_c::visit(case_element_list_c *symbol) // not needed! We inherit from iterator_visitor_c |
|
236 |
|
237 /* case_list ':' statement_list */ |
|
238 // SYM_REF2(case_element_c, case_list, statement_list) |
|
239 // void *case_elements_check_c::visit(case_element_c *symbol) // not needed! We inherit from iterator_visitor_c |
|
240 |
|
241 |
|
242 // SYM_LIST(case_list_c) |
|
243 void *case_elements_check_c::visit(case_list_c *symbol) { |
|
244 for (int i = 0; i < symbol->n; i++) |
|
245 case_elements_list.push_back(symbol->elements[i]); |
|
246 return NULL; |
|
247 } |
|
248 |
|
249 |