Commit | Line | Data |
---|---|---|
970ed795 EL |
1 | /////////////////////////////////////////////////////////////////////////////// |
2 | // Copyright (c) 2000-2014 Ericsson Telecom AB | |
3 | // All rights reserved. This program and the accompanying materials | |
4 | // are made available under the terms of the Eclipse Public License v1.0 | |
5 | // which accompanies this distribution, and is available at | |
6 | // http://www.eclipse.org/legal/epl-v10.html | |
7 | /////////////////////////////////////////////////////////////////////////////// | |
8 | #include "PatternString.hh" | |
9 | #include "../../common/pattern.hh" | |
10 | #include "../CompilerError.hh" | |
11 | #include "../Code.hh" | |
12 | ||
13 | #include "TtcnTemplate.hh" | |
14 | ||
15 | namespace Ttcn { | |
16 | ||
17 | // ================================= | |
18 | // ===== PatternString::ps_elem_t | |
19 | // ================================= | |
20 | ||
21 | struct PatternString::ps_elem_t { | |
22 | enum kind_t { | |
23 | PSE_STR, | |
24 | PSE_REF, | |
25 | PSE_REFDSET | |
26 | } kind; | |
27 | union { | |
28 | string *str; | |
29 | Ttcn::Reference *ref; | |
30 | }; | |
31 | ps_elem_t(kind_t p_kind, const string& p_str); | |
32 | ps_elem_t(kind_t p_kind, Ttcn::Reference *p_ref); | |
33 | ps_elem_t(const ps_elem_t& p); | |
34 | ~ps_elem_t(); | |
35 | ps_elem_t* clone() const; | |
36 | void set_fullname(const string& p_fullname); | |
37 | void set_my_scope(Scope *p_scope); | |
38 | void chk_ref(PatternString::pstr_type_t pstr_type, Type::expected_value_t expected_value); | |
39 | void set_code_section(GovernedSimple::code_section_t p_code_section); | |
40 | }; | |
41 | ||
42 | PatternString::ps_elem_t::ps_elem_t(kind_t p_kind, const string& p_str) | |
43 | : kind(p_kind) | |
44 | { | |
45 | str = new string(p_str); | |
46 | } | |
47 | ||
48 | PatternString::ps_elem_t::ps_elem_t(kind_t p_kind, Ttcn::Reference *p_ref) | |
49 | : kind(p_kind) | |
50 | { | |
51 | if (!p_ref) FATAL_ERROR("PatternString::ps_elem_t::ps_elem_t()"); | |
52 | ref = p_ref; | |
53 | } | |
54 | ||
55 | PatternString::ps_elem_t::~ps_elem_t() | |
56 | { | |
57 | switch(kind) { | |
58 | case PSE_STR: | |
59 | delete str; | |
60 | break; | |
61 | case PSE_REF: | |
62 | case PSE_REFDSET: | |
63 | delete ref; | |
64 | break; | |
65 | } // switch kind | |
66 | } | |
67 | ||
68 | PatternString::ps_elem_t* PatternString::ps_elem_t::clone() const | |
69 | { | |
70 | FATAL_ERROR("PatternString::ps_elem_t::clone"); | |
71 | } | |
72 | ||
73 | void PatternString::ps_elem_t::set_fullname(const string& p_fullname) | |
74 | { | |
75 | switch(kind) { | |
76 | case PSE_REF: | |
77 | case PSE_REFDSET: | |
78 | ref->set_fullname(p_fullname); | |
79 | break; | |
80 | default: | |
81 | ; | |
82 | } // switch kind | |
83 | } | |
84 | ||
85 | void PatternString::ps_elem_t::set_my_scope(Scope *p_scope) | |
86 | { | |
87 | switch(kind) { | |
88 | case PSE_REF: | |
89 | case PSE_REFDSET: | |
90 | ref->set_my_scope(p_scope); | |
91 | break; | |
92 | default: | |
93 | ; | |
94 | } // switch kind | |
95 | } | |
96 | ||
97 | void PatternString::ps_elem_t::chk_ref(PatternString::pstr_type_t pstr_type, Type::expected_value_t expected_value) | |
98 | { | |
99 | if (kind != PSE_REF) FATAL_ERROR("PatternString::ps_elem_t::chk_ref()"); | |
100 | Value* v = 0; | |
101 | Value* v_last = 0; | |
102 | Common::Assignment* ass = ref->get_refd_assignment(); | |
103 | if (!ass) | |
104 | return; | |
105 | Ttcn::FieldOrArrayRefs* t_subrefs = ref->get_subrefs(); | |
106 | Type* ref_type = ass->get_Type()->get_type_refd_last()->get_field_type( | |
107 | t_subrefs, expected_value); | |
108 | Type::typetype_t tt; | |
109 | switch (pstr_type) { | |
110 | case PatternString::CSTR_PATTERN: | |
111 | tt = Type::T_CSTR; | |
112 | if (ref_type->get_typetype() != Type::T_CSTR) | |
113 | TTCN_pattern_error("Type of the referenced %s '%s' should be " | |
114 | "'charstring'", ass->get_assname(), ref->get_dispname().c_str()); | |
115 | break; | |
116 | case PatternString::USTR_PATTERN: | |
117 | tt = ref_type->get_typetype(); | |
118 | if (tt != Type::T_CSTR && tt != Type::T_USTR) | |
119 | TTCN_pattern_error("Type of the referenced %s '%s' should be either " | |
120 | "'charstring' or 'universal charstring'", ass->get_assname(), | |
121 | ref->get_dispname().c_str()); | |
122 | break; | |
123 | default: | |
124 | FATAL_ERROR("Unknown pattern string type"); | |
125 | } | |
126 | Type* refcheckertype = Type::get_pooltype(tt); | |
127 | switch (ass->get_asstype()) { | |
128 | case Common::Assignment::A_MODULEPAR_TEMP: | |
129 | case Common::Assignment::A_VAR_TEMPLATE: | |
130 | // error reporting moved up | |
131 | break; | |
132 | case Common::Assignment::A_TEMPLATE: { | |
133 | Template* templ = ass->get_Template(); | |
134 | refcheckertype->chk_this_template_ref(templ); | |
135 | refcheckertype->chk_this_template_generic(templ, INCOMPLETE_ALLOWED, | |
136 | OMIT_ALLOWED, ANY_OR_OMIT_ALLOWED, SUB_CHK, NOT_IMPLICIT_OMIT, 0); | |
137 | switch (templ->get_templatetype()) { | |
138 | case Template::SPECIFIC_VALUE: | |
139 | v_last = templ->get_specific_value(); | |
140 | break; | |
141 | case Template::CSTR_PATTERN: { | |
142 | Ttcn::PatternString* ps = templ->get_cstr_pattern(); | |
143 | if (!ps->has_refs()) | |
144 | v_last = ps->get_value(); | |
145 | break; } | |
146 | case Template::USTR_PATTERN: { | |
147 | Ttcn::PatternString* ps = templ->get_ustr_pattern(); | |
148 | if (!ps->has_refs()) | |
149 | v_last = ps->get_value(); | |
150 | break; } | |
151 | default: | |
152 | TTCN_pattern_error("Unable to resolve referenced '%s' to character " | |
153 | "string type. '%s' template cannot be used.", | |
154 | ref->get_dispname().c_str(), templ->get_templatetype_str()); | |
155 | break; | |
156 | } | |
157 | break; } | |
158 | default: { | |
159 | Reference *t_ref = ref->clone(); | |
160 | t_ref->set_location(*ref); | |
161 | v = new Value(Value::V_REFD, t_ref); | |
162 | v->set_my_governor(refcheckertype); | |
163 | v->set_my_scope(ref->get_my_scope()); | |
164 | v->set_location(*ref); | |
165 | refcheckertype->chk_this_value(v, 0, expected_value, | |
166 | INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK); | |
167 | v_last = v->get_value_refd_last(); | |
168 | } | |
169 | } | |
170 | if (v_last && (v_last->get_valuetype() == Value::V_CSTR || | |
171 | v_last->get_valuetype() == Value::V_USTR)) { | |
172 | // the reference points to a constant | |
173 | // substitute the reference with the known value | |
174 | delete ref; | |
175 | kind = PSE_STR; | |
176 | if (v_last->get_valuetype() == Value::V_CSTR) | |
177 | str = new string(v_last->get_val_str()); | |
178 | else | |
179 | str = new string(v_last->get_val_ustr().get_stringRepr_for_pattern()); | |
180 | } | |
181 | delete v; | |
182 | } | |
183 | ||
184 | void PatternString::ps_elem_t::set_code_section | |
185 | (GovernedSimple::code_section_t p_code_section) | |
186 | { | |
187 | switch(kind) { | |
188 | case PSE_REF: | |
189 | case PSE_REFDSET: | |
190 | ref->set_code_section(p_code_section); | |
191 | break; | |
192 | default: | |
193 | ; | |
194 | } // switch kind | |
195 | } | |
196 | ||
197 | // ================================= | |
198 | // ===== PatternString | |
199 | // ================================= | |
200 | ||
201 | PatternString::PatternString(const PatternString& p) | |
202 | : Node(p), my_scope(0), pattern_type(p.pattern_type) | |
203 | { | |
204 | size_t nof_elems = p.elems.size(); | |
205 | for (size_t i = 0; i < nof_elems; i++) elems.add(p.elems[i]->clone()); | |
206 | } | |
207 | ||
208 | PatternString::ps_elem_t *PatternString::get_last_elem() const | |
209 | { | |
210 | if (elems.empty()) return 0; | |
211 | ps_elem_t *last_elem = elems[elems.size() - 1]; | |
212 | if (last_elem->kind == ps_elem_t::PSE_STR) return last_elem; | |
213 | else return 0; | |
214 | } | |
215 | ||
216 | PatternString::~PatternString() | |
217 | { | |
218 | size_t nof_elems = elems.size(); | |
219 | for (size_t i = 0; i < nof_elems; i++) delete elems[i]; | |
220 | elems.clear(); | |
221 | delete cstr_value; | |
222 | } | |
223 | ||
224 | PatternString *PatternString::clone() const | |
225 | { | |
226 | return new PatternString(*this); | |
227 | } | |
228 | ||
229 | void PatternString::set_fullname(const string& p_fullname) | |
230 | { | |
231 | Node::set_fullname(p_fullname); | |
232 | size_t nof_elems = elems.size(); | |
233 | for(size_t i = 0; i < nof_elems; i++) elems[i]->set_fullname(p_fullname); | |
234 | } | |
235 | ||
236 | void PatternString::set_my_scope(Scope *p_scope) | |
237 | { | |
238 | my_scope = p_scope; | |
239 | size_t nof_elems = elems.size(); | |
240 | for (size_t i = 0; i < nof_elems; i++) elems[i]->set_my_scope(p_scope); | |
241 | } | |
242 | ||
243 | void PatternString::set_code_section | |
244 | (GovernedSimple::code_section_t p_code_section) | |
245 | { | |
246 | size_t nof_elems = elems.size(); | |
247 | for (size_t i = 0; i < nof_elems; i++) | |
248 | elems[i]->set_code_section(p_code_section); | |
249 | } | |
250 | ||
251 | void PatternString::addChar(char c) | |
252 | { | |
253 | ps_elem_t *last_elem = get_last_elem(); | |
254 | if (last_elem) *last_elem->str += c; | |
255 | else elems.add(new ps_elem_t(ps_elem_t::PSE_STR, string(c))); | |
256 | } | |
257 | ||
258 | void PatternString::addString(const char *p_str) | |
259 | { | |
260 | ps_elem_t *last_elem = get_last_elem(); | |
261 | if (last_elem) *last_elem->str += p_str; | |
262 | else elems.add(new ps_elem_t(ps_elem_t::PSE_STR, string(p_str))); | |
263 | } | |
264 | ||
265 | void PatternString::addString(const string& p_str) | |
266 | { | |
267 | ps_elem_t *last_elem = get_last_elem(); | |
268 | if (last_elem) *last_elem->str += p_str; | |
269 | else elems.add(new ps_elem_t(ps_elem_t::PSE_STR, p_str)); | |
270 | } | |
271 | ||
272 | void PatternString::addRef(Ttcn::Reference *p_ref) | |
273 | { | |
274 | elems.add(new ps_elem_t(ps_elem_t::PSE_REF, p_ref)); | |
275 | } | |
276 | ||
277 | void PatternString::addRefdCharSet(Ttcn::Reference *p_ref) | |
278 | { | |
279 | elems.add(new ps_elem_t(ps_elem_t::PSE_REFDSET, p_ref)); | |
280 | } | |
281 | ||
282 | string PatternString::get_full_str() const | |
283 | { | |
284 | string s; | |
285 | for(size_t i=0; i<elems.size(); i++) { | |
286 | ps_elem_t *pse=elems[i]; | |
287 | switch(pse->kind) { | |
288 | case ps_elem_t::PSE_STR: | |
289 | s+=*pse->str; | |
290 | break; | |
291 | case ps_elem_t::PSE_REFDSET: | |
292 | s+="\\N"; | |
293 | /* no break */ | |
294 | case ps_elem_t::PSE_REF: | |
295 | s+='{'; | |
296 | s+=pse->ref->get_dispname(); | |
297 | s+='}'; | |
298 | } // switch kind | |
299 | } // for | |
300 | return s; | |
301 | } | |
302 | ||
303 | void PatternString::set_pattern_type(pstr_type_t p_type) { | |
304 | pattern_type = p_type; | |
305 | } | |
306 | ||
307 | PatternString::pstr_type_t PatternString::get_pattern_type() const { | |
308 | return pattern_type; | |
309 | } | |
310 | ||
311 | bool PatternString::has_refs() const | |
312 | { | |
313 | for (size_t i = 0; i < elems.size(); i++) { | |
314 | switch (elems[i]->kind) { | |
315 | case ps_elem_t::PSE_REF: | |
316 | case ps_elem_t::PSE_REFDSET: | |
317 | return true; | |
318 | default: | |
319 | break; | |
320 | } | |
321 | } | |
322 | return false; | |
323 | } | |
324 | ||
325 | void PatternString::chk_refs(Type::expected_value_t expected_value) | |
326 | { | |
327 | for(size_t i=0; i<elems.size(); i++) { | |
328 | ps_elem_t *pse=elems[i]; | |
329 | switch(pse->kind) { | |
330 | case ps_elem_t::PSE_STR: | |
331 | break; | |
332 | case ps_elem_t::PSE_REFDSET: | |
333 | /* actually, not supported */ | |
334 | break; | |
335 | case ps_elem_t::PSE_REF: | |
336 | pse->chk_ref(pattern_type, expected_value); | |
337 | break; | |
338 | } // switch kind | |
339 | } // for | |
340 | } | |
341 | ||
342 | /** \todo implement */ | |
343 | void PatternString::chk_recursions(ReferenceChain&) | |
344 | { | |
345 | ||
346 | } | |
347 | ||
348 | void PatternString::chk_pattern() | |
349 | { | |
350 | string str; | |
351 | for (size_t i = 0; i < elems.size(); i++) { | |
352 | ps_elem_t *pse = elems[i]; | |
353 | if (pse->kind != ps_elem_t::PSE_STR) | |
354 | FATAL_ERROR("PatternString::chk_pattern()"); | |
355 | str += *pse->str; | |
356 | } | |
357 | char* posix_str = 0; | |
358 | switch (pattern_type) { | |
359 | case CSTR_PATTERN: | |
360 | posix_str = TTCN_pattern_to_regexp(str.c_str()); | |
361 | break; | |
362 | case USTR_PATTERN: | |
363 | posix_str = TTCN_pattern_to_regexp_uni(str.c_str()); | |
364 | } | |
365 | Free(posix_str); | |
366 | } | |
367 | ||
368 | bool PatternString::chk_self_ref(Common::Assignment *lhs) | |
369 | { | |
370 | for (size_t i = 0, e = elems.size(); i < e; ++i) { | |
371 | ps_elem_t *pse = elems[i]; | |
372 | switch (pse->kind) { | |
373 | case ps_elem_t::PSE_STR: | |
374 | break; | |
375 | case ps_elem_t::PSE_REFDSET: | |
376 | /* actually, not supported */ | |
377 | break; | |
378 | case ps_elem_t::PSE_REF: { | |
379 | Ttcn::Assignment *ass = pse->ref->get_refd_assignment(); | |
380 | if (ass == lhs) return true; | |
381 | break; } | |
382 | } // switch | |
383 | } | |
384 | return false; | |
385 | } | |
386 | ||
387 | void PatternString::join_strings() | |
388 | { | |
389 | // points to the previous string element otherwise it is NULL | |
390 | ps_elem_t *prev_str = 0; | |
391 | for (size_t i = 0; i < elems.size(); ) { | |
392 | ps_elem_t *pse = elems[i]; | |
393 | if (pse->kind == ps_elem_t::PSE_STR) { | |
394 | const string& str = *pse->str; | |
395 | if (str.size() > 0) { | |
396 | // the current element is a non-empty string | |
397 | if (prev_str) { | |
398 | // append str to prev_str and drop pse | |
399 | *prev_str->str += str; | |
400 | delete pse; | |
401 | elems.replace(i, 1); | |
402 | // don't increment i | |
403 | } else { | |
404 | // keep pse for the next iteration | |
405 | prev_str = pse; | |
406 | i++; | |
407 | } | |
408 | } else { | |
409 | // the current element is an empty string | |
410 | // simply drop it | |
411 | delete pse; | |
412 | elems.replace(i, 1); | |
413 | // don't increment i | |
414 | } | |
415 | } else { | |
416 | // pse is not a string | |
417 | // forget prev_str | |
418 | prev_str = 0; | |
419 | i++; | |
420 | } | |
421 | } | |
422 | } | |
423 | ||
424 | string PatternString::create_charstring_literals(Common::Module *p_mod) | |
425 | { | |
426 | /* The cast is there for the benefit of OPTIONAL<CHARSTRING>, because | |
427 | * it doesn't have operator+(). Only the first member needs the cast | |
428 | * (the others will be automagically converted to satisfy | |
429 | * CHARSTRING::operator+(const CHARSTRING&) ) */ | |
430 | string s; | |
431 | if (pattern_type == CSTR_PATTERN) | |
432 | s = "CHARSTRING_template(STRING_PATTERN, (CHARSTRING)"; | |
433 | else | |
434 | s = "UNIVERSAL_CHARSTRING_template(STRING_PATTERN, (CHARSTRING)"; | |
435 | size_t nof_elems = elems.size(); | |
436 | if (nof_elems > 0) { | |
437 | // the pattern is not empty | |
438 | for (size_t i = 0; i < nof_elems; i++) { | |
439 | if (i > 0) s += " + "; | |
440 | ps_elem_t *pse = elems[i]; | |
441 | switch (pse->kind) { | |
442 | case ps_elem_t::PSE_STR: | |
443 | s += p_mod->add_charstring_literal(*pse->str); | |
444 | break; | |
445 | case ps_elem_t::PSE_REFDSET: | |
446 | /* actually, not supported */ | |
447 | FATAL_ERROR("PatternString::create_charstring_literals()"); | |
448 | break; | |
449 | case ps_elem_t::PSE_REF: { | |
450 | expression_struct expr; | |
451 | Code::init_expr(&expr); | |
452 | pse->ref->generate_code(&expr); | |
453 | if (expr.preamble || expr.postamble) | |
454 | FATAL_ERROR("PatternString::create_charstring_literals()"); | |
455 | s += expr.expr; | |
456 | Common::Assignment* assign = pse->ref->get_refd_assignment(); | |
457 | ||
458 | if ((assign->get_asstype() == Common::Assignment::A_TEMPLATE | |
459 | || assign->get_asstype() == Common::Assignment::A_MODULEPAR_TEMP | |
460 | || assign->get_asstype() == Common::Assignment::A_VAR_TEMPLATE)) | |
461 | { | |
462 | if ((assign->get_Type()->get_typetype() == Type::T_CSTR | |
463 | || assign->get_Type()->get_typetype() == Type::T_USTR)) { | |
464 | s += ".get_single_value()"; | |
465 | } | |
466 | else { | |
467 | s += ".valueof()"; | |
468 | } | |
469 | } | |
470 | ||
471 | Code::free_expr(&expr); | |
472 | break; } | |
473 | } // switch kind | |
474 | } // for | |
475 | } else { | |
476 | // empty pattern: create an empty string literal for it | |
477 | s += p_mod->add_charstring_literal(string()); | |
478 | } | |
479 | s += ')'; | |
480 | return s; | |
481 | } | |
482 | ||
483 | void PatternString::dump(unsigned level) const | |
484 | { | |
485 | DEBUG(level, "%s", get_full_str().c_str()); | |
486 | } | |
487 | ||
488 | Common::Value* PatternString::get_value() { | |
489 | if (!cstr_value && !has_refs()) | |
490 | cstr_value = new Common::Value(Common::Value::V_CSTR, | |
491 | new string(get_full_str())); | |
492 | return cstr_value; | |
493 | } | |
494 | ||
495 | } // namespace Ttcn | |
496 | ||
497 | // ================================= | |
498 | // ===== TTCN_pattern_XXXX | |
499 | // ================================= | |
500 | ||
501 | /* These functions are used by common charstring pattern parser. */ | |
502 | ||
503 | void TTCN_pattern_error(const char *fmt, ...) | |
504 | { | |
505 | char *msg=mcopystr("Charstring pattern: "); | |
506 | msg=mputstr(msg, fmt); | |
507 | va_list args; | |
508 | va_start(args, fmt); | |
509 | Common::Error_Context::report_error(0, msg, args); | |
510 | va_end(args); | |
511 | Free(msg); | |
512 | } | |
513 | ||
514 | void TTCN_pattern_warning(const char *fmt, ...) | |
515 | { | |
516 | char *msg=mcopystr("Charstring pattern: "); | |
517 | msg=mputstr(msg, fmt); | |
518 | va_list args; | |
519 | va_start(args, fmt); | |
520 | Common::Error_Context::report_warning(0, msg, args); | |
521 | va_end(args); | |
522 | Free(msg); | |
523 | } |