cgv
Loading...
Searching...
No Matches
ph_processor.cxx
1#ifdef WIN32
2#pragma warning (disable:4996)
3#endif
4
5#include <iostream>
6#include <sstream>
7#include <stdlib.h>
8#include <cgv/utils/file.h>
9#include <cgv/utils/dir.h>
10#include <cgv/utils/tokenizer.h>
11#include <random>
12#include "ph_processor.h"
13#include "expression_processor.h"
14
15using namespace cgv::utils;
16
17#define reflect_version_1 true
18#define reflect_version_2 false
19
20namespace cgv {
21 namespace ppp {
22
23 ph_processor::ph_processor(const std::string& _additional_include_path, bool _search_recursive, char _special) :
24 additional_include_path(_additional_include_path), search_recursive(_search_recursive), special(_special)
25 {
26 exit_code = 0;
27 nr_functions = 0;
28 found_error = false;
29 generate_output = false;
30 es = &std::cerr;
31 os = 0;
32 content = 0;
33 content_is_external = false;
34 inserted_shader_file_names = 0;
35#ifdef _DEBUG
36 debug_reflection = false;
37#else
38 debug_reflection = false;
39#endif
40 reflect_info = 0;
41 }
42
43 ph_processor::~ph_processor()
44 {
45 close();
46 }
47
48 void ph_processor::configure_insert_to_shader(std::vector<std::string>* _inserted_shader_file_names)
49 {
50 inserted_shader_file_names = _inserted_shader_file_names;
51 }
52
53 void ph_processor::close()
54 {
55 if (!content_is_external && content) {
56 delete content;
57 content = 0;
58 content_is_external = false;
59 }
60 }
61
62 bool ph_processor::parse_string(const std::string& text)
63 {
64 close();
65 content = &text;
66 content_is_external = true;
67 return parse();
68 }
69
70 bool ph_processor::parse_file(const std::string& _file_name)
71 {
72 // read the file
73 close();
74 std::string* file_content = new std::string();
75 content_is_external = false;
76 if (!cgv::utils::file::read(_file_name, *file_content, true)) {
77 if (es) {
78 (*es) << "could not read input file " << _file_name.c_str() << std::endl;
79 }
80 return false;
81 }
82
83#ifndef WIN32
84 //correct linefeeds
85 std::string* new_file_content = new std::string();
86 bool carriage_return_found = false;
87 for (unsigned int i = 0; i < file_content->size(); ++i) {
88
89 if (carriage_return_found && (*file_content)[i] == 10) {
90
91 //linefeed found
92 //printf("§\n");
93 continue;
94
95 }//printf("%c",(*file_content)[i]);
96
97 carriage_return_found = (*file_content)[i] == 13;
98
99 if (carriage_return_found) (*new_file_content) += 10;
100
101 else (*new_file_content) += (*file_content)[i];
102
103 }
104 delete file_content;
105 file_content = new_file_content;
106
107#endif
108
109 content = file_content;
110 file_name = _file_name;
111 return parse();
112 }
113
114 bool ph_processor::process_without_output()
115 {
116 generate_output = false;
117 bool result = process(0, (unsigned int)commands.size());
118 /*
119 if (debug_reflection) {
120 ensure_reflect_info();
121 std::cout << "reflect_info = " << ref_variable("reflect_info") << std::endl;
122 }
123 */
124 return result;
125 }
126
127 void ph_processor::swap_output(ph_processor& pp)
128 {
129 std::swap(generate_output, pp.generate_output);
130 std::swap(os, pp.os);
131 }
132
133 bool ph_processor::process_to_string(std::string& output)
134 {
135 std::stringstream str_os;
136 os = &str_os;
137 generate_output = true;
138
139 bool result = process(0, (unsigned int)commands.size());
140 /*
141 if (debug_reflection) {
142 ensure_reflect_info();
143 std::cout << "reflect_info = " << ref_variable("reflect_info") << std::endl;
144 }
145 */
146 output = str_os.str();
147 return result;
148 }
149
150 int ph_processor::process_to_file(const std::string& _file_name, long long last_write_time)
151 {
152 /*
153 if (last_write_time == 0) {
154 std::ofstream fos(_file_name.c_str());
155 if (fos.fail()) {
156 if (es)
157 (*es) << "could not open output file " << _file_name.c_str() << " for writing" << std::endl;
158 return false;
159 }
160 os = &fos;
161 generate_output = true;
162 bool result = process(0, (unsigned int) commands.size());
163 fos.close();
164 return result ? 1 : 0;
165 }
166 else {
167 */
168 std::string output;
169 if (!process_to_string(output))
170 return 0;
171 bool changed = true;
172 bool do_write = true;
173 if (cgv::utils::file::exists(_file_name)) {
174 std::string content;
175 if (cgv::utils::file::read(_file_name, content, true)) {
176 if (output == content) {
177 changed = false;
178 do_write = cgv::utils::file::get_last_write_time(_file_name) < last_write_time;
179 }
180 }
181 }
182 if (do_write) {
183 if (!cgv::utils::file::write(_file_name, output.c_str(), output.size(), true)) {
184 std::cerr << "could not write to file " << _file_name << std::endl;
185 return 0;
186 }
187 else
188 if (changed)
189 return 2;
190 else
191 return 3;
192 }
193 return 1;
194 //}
195 }
196
197 void ph_processor::set_error_stream(std::ostream& error_stream)
198 {
199 es = &error_stream;
200 }
201
202 unsigned int ph_processor::get_line_number(const token& loc) const
203 {
204 // determine line number
205 unsigned int li;
206 for (li = 0; li < lines.size(); ++li)
207 if (lines[li].begin > loc.begin)
208 break;
209 return li;
210 }
211 void ph_processor::error(const std::string& text, const token& loc,
212 unsigned int error_number)
213 {
214 found_error = true;
215 // determine line number
216 unsigned int li = get_line_number(loc);
217 unsigned prev_line = li;
218 if (li > 0)
219 --prev_line;
220 const char* lb = loc.begin;
221 if (!lb)
222 lb = lines[li].begin;
223 if (es) {
224 (*es) << file_name.c_str() << "(" << li << ") : error "
225 << error_number << ": " << text.c_str() << "\n "
226 << to_string(token(lines[prev_line].begin, lb)).c_str() << " \\_ ";
227 if (loc.begin) {
228 token l(loc.begin, loc.begin);
229 while (l.end < loc.end && !is_space(*l.end))
230 ++l.end;
231 (*es) << to_string(l).c_str() << " _/" << std::endl;
232 }
233 }
234 }
235
236 std::string ph_processor::my_getenv(const char* name)
237 {
238 if (ref_variable(name).is_str())
239 return ref_variable(name).get_str();
240
241 const char* var = getenv(name);
242 if (!var)
243 return std::string();
244 return std::string(var);
245 }
246
247 void my_append(std::string& p, const std::string& o)
248 {
249 if (o.empty())
250 return;
251 if (p.empty())
252 p = o;
253 else {
254 p += ";";
255 p += o;
256 }
257 }
258
259 std::string ph_processor::find_include_file(const std::string& fn, bool local) const
260 {
261 token path_token(0, 0);
262
263 std::string include = my_getenv("INCLUDE");
264 std::string cgv_dir = my_getenv("CGV_DIR");
265 std::string cgv_root = my_getenv("CGV_ROOT");
266 std::string include_paths = additional_include_path;
267 if (local)
268 include_paths = ref_variable("input_dir").get_str();
269 my_append(include_paths, cgv_dir);
270 my_append(include_paths, cgv_root);
271 my_append(include_paths, include);
272 std::vector<token> path_list;
273 split_to_tokens(include_paths, path_list, "", false, "\"'", "\"'", ";");
274 if (cgv::utils::file::exists(fn))
275 return fn;
276 for (unsigned int j = 0; j < path_list.size(); ++j) {
277 std::string file_path = to_string(path_list[j]) + "/" + fn;
278 if (!cgv::utils::file::exists(file_path) && search_recursive) {
279 std::string path = to_string(path_list[j]);
280 file_path = cgv::utils::file::find_in_paths(fn, path);
281 }
282 if (!file_path.empty() && cgv::utils::file::exists(file_path)) {
283 return file_path;
284 }
285 }
286 return "";
287 }
288
289 bool ph_processor::process_include(unsigned int i, bool is_cinclude, bool insert)
290 {
291 std::string fn = to_string(commands[i]);
292 if (commands[i].expressions.size() > 0) {
293 variant v0;
294 commands[i].expressions[0].evaluate(v0, this);
295 if (exit_code != 0)
296 return true;
297 if (!check_evaluation_error(i, 0))
298 return false;
299 fn = v0.get_str();
300 }
301 std::string file_path = find_include_file(fn, commands[i].expressions.size() == 0 && commands[i].begin[-1] == '"');
302 if (file_path.empty()) {
303 error(std::string("could not open file ") + fn, commands[i]);
304 return false;
305 }
306
307 ph_processor* fp = new ph_processor(additional_include_path, false);
308 bool success = false;
309 if ((success = fp->parse_file(file_path))) {
310 if (insert) {
311 std::string output;
312 success = fp->process_to_string(output);
313 if (inserted_shader_file_names) {
314 (*os) << "#line 0 " << 1000 + inserted_shader_file_names->size() << std::endl;
315 (*os) << output.c_str() << std::endl;
316 (*os) << "#line " << (int)get_line_number(commands[i]) - 1 << " 0" << std::endl;
317 inserted_shader_file_names->push_back(file_path);
318 }
319 else
320 (*os) << output.c_str() << std::endl;
321 }
322 else {
323 success = fp->process_without_output();
324 }
325 }
326 if (is_cinclude && generate_output) {
327 token path_token(commands[i]);
328 std::size_t pos = to_string(path_token).find_last_of('.');
329 if (pos != std::string::npos)
330 path_token.end = path_token.begin + pos;
331 (*os) << "#include " << commands[i].get_open_parenthesis()
332 << to_string(path_token).c_str() << ".h"
333 << commands[i].get_close_parenthesis();
334 }
335 if (fp->exit_code != 0)
336 exit_code = fp->exit_code;
337 if (fp->nr_functions == 0)
338 delete fp;
339 return success;
340 }
341
342 bool ph_processor::check_evaluation_error(unsigned int i, unsigned int ei)
343 {
344 if (commands[i].expressions[ei].found_error) {
345 if (!commands[i].expressions[ei].issued_error) {
346 error(std::string("evaluate expression: ") +
347 commands[i].expressions[ei].get_last_error(), commands[i].expressions[ei].get_last_error_token());
348 }
349 return false;
350 }
351 return true;
352 }
353
354
355 void bite_common_prefix(token& t0, token& t1, token& p0, token& p1)
356 {
357 p0 = token(t0.begin, t0.begin);
358 p1 = token(t1.begin, t1.begin);
359 while (!t0.empty() && !t1.empty()) {
360 if (*t0.begin != *t1.begin)
361 break;
362 p0.end = ++t0.begin;
363 p1.end = ++t1.begin;
364 }
365 }
366
367 bool bite_digits(token& t, int& n)
368 {
369 bool result = false;
370 n = 0;
371 while (!t.empty() && is_digit(*t.begin)) {
372 n = 10 * n + (*t.begin - '0');
373 ++t.begin;
374 result = true;
375 }
376 return result;
377 }
378
379 bool ph_processor::process_list(unsigned int i)
380 {
381 variant v0;
382 commands[i].expressions[0].evaluate(v0, this);
383 if (exit_code != 0)
384 return true;
385 if (!check_evaluation_error(i, 0))
386 return false;
387 variant v1;
388 commands[i].expressions[1].evaluate(v1, this);
389 if (exit_code != 0)
390 return true;
391 if (!check_evaluation_error(i, 1))
392 return false;
393 variant v2;
394 commands[i].expressions[2].evaluate(v2, this);
395 if (exit_code != 0)
396 return true;
397 if (!check_evaluation_error(i, 2))
398 return false;
399 if (!generate_output)
400 return true;
401
402 std::string s0 = v0.get_str();
403 token t0(s0);
404 std::string s1 = v1.get_str();
405 std::string s2 = v2.get_str();
406 token t2(s2);
407 std::vector<token> toks;
408 const char* ptr_base = 0;
409 do {
410 token p0, p2;
411 // std::cout << "tokens = " << to_string(t0).c_str() << " <-> "
412 // << to_string(t2).c_str() << std::endl;
413
414 bite_common_prefix(t0, t2, p0, p2);
415 if (t0.empty() || !is_digit(*t0.begin) ||
416 t2.empty() || !is_digit(*t2.begin)) {
417 if (!p0.empty() && is_digit(p0.end[-1])) {
418 t0.begin = --p0.end;
419 t2.begin = --p2.end;
420 }
421 else if (!(t0.empty() && t2.empty())) {
422 error(std::string("error in list [") + s0 + ";" + s1 + ";" + s2 + "]\n can only iterate between two numbers", commands[i]);
423 return false;
424 }
425 }
426 toks.push_back(p0);
427 if (t0.empty() || t2.empty()) {
428 if (t0.empty() && t2.empty())
429 break;
430 else {
431 error(std::string("error in list [") + s0 + ";" + s1 + ";" + s2 + "]\n 1st and 3rd expression split into different number of terms", commands[i]);
432 return false;
433 }
434 }
435 // std::cout << "bite off prefix " << p0.str().c_str() << " = "
436 // << p2.str().c_str() << std::endl;
437 int n0, n2;
438 if (!bite_digits(t0, n0) || !bite_digits(t2, n2)) {
439 error(std::string("error in list [") + s0 + ";" + s1 + ";" + s2 + "]\n can only iterate between two numbers (2)", commands[i]);
440 return false;
441 }
442 toks.push_back(token(ptr_base + n0, ptr_base + n2));
443 // std::cout << "bite off numbers " << n0 << " and " << n2 << std::endl;
444 } while (!(t0.empty() && t2.empty()));
445
446 if (toks.empty())
447 return true;
448
449 int n = 1;
450 if (toks.size() > 1) {
451 n = (int)((toks[1].end - ptr_base) - (toks[1].begin - ptr_base));
452 n += 1;
453 }
454 unsigned int j;
455 for (j = 3; j < toks.size(); j += 2) {
456 int nn = (int)((toks[j].end - ptr_base) - (toks[j].begin - ptr_base));
457 nn += 1;
458 if (nn != n) {
459 error(std::string("error in list [") + s0 + ";" + s1 + ";" + s2 + "]\n several number ranges of different size not allowed", commands[i]);
460 return false;
461 }
462 }
463 for (int k = 0; k < n; ++k) {
464 if (k > 0)
465 (*os) << s1.c_str();
466 for (j = 0; j < toks.size(); j += 2) {
467 (*os) << to_string(toks[j]).c_str();
468 if (j + 1 < toks.size())
469 (*os) << (int)(toks[j + 1].begin - ptr_base) + k;
470 }
471 }
472 return true;
473 }
474
475 void ph_processor::ensure_reflect_info()
476 {
477 if (!reflect_info) {
478 reflect_info = &ref_variable("reflect_info");
479 *reflect_info = variant(variant::map_type());
480 if (reflect_version_2)
481 reflect_info->ref_element("elements") = variant(variant::list_type());
482 if (reflect_version_1) {
483 reflect_info->ref_element("typedefs") = variant(variant::list_type());
484 reflect_info->ref_element("enums") = variant(variant::list_type());
485 reflect_info->ref_element("compounds") = variant(variant::list_type());
486 reflect_info->ref_element("variables") = variant(variant::list_type());
487 reflect_info->ref_element("functions") = variant(variant::list_type());
488 }
489 reflect_info->ref_element("includes") = variant(variant::list_type());
490 }
491 }
492
493 variant ph_processor::construct_namespace_list() const
494 {
495 variant list = variant(variant::list_type());
496 if (!compound_stack.empty())
497 return list;
498 for (unsigned int i = 0; i < namespace_stack.size(); ++i)
499 list.append_to_list(namespace_stack[i]);
500 return list;
501 }
502
503 std::string ph_processor::construct_namespace_sequence() const
504 {
505 std::string seq;
506 if (!compound_stack.empty())
507 return seq;
508 for (unsigned int i = 0; i < namespace_stack.size(); ++i)
509 seq += namespace_stack[i] + "::";
510 return seq;
511 }
512
513
514 bool ph_processor::reflect(const token& code, const token& comment)
515 {
516 // split code fragment into tokens
517 tokenizer t(code);
518 t.set_sep("()[];");
519 std::vector<token> toks;
520 t.skip_whitespaces();
521 t.reverse_skip_whitespaces();
522 bite_all(t, toks);
523
524 // check that at least one token is present
525 if (toks.empty()) {
526 error("no code to be reflected here", code);
527 return false;
528 }
529
530 // check for closing code parenthesis
531 if (to_string(toks[0]) == "}") {
532
533 // these are only valid if the parenthesis stack is not empty
534 if (rp_stack.empty()) {
535 error("reflect end of something without starting something", code);
536 return false;
537 }
538
539 // either close the top most namespace
540 if (rp_stack.back() == RP_NAMESPACE) {
541 if (namespace_stack.empty()) {
542 error("reflect end of namespace without a namespace started", code);
543 return false;
544 }
545 if (debug_reflection)
546 std::cout << "reflect end of namespace " << namespace_stack.back().c_str() << std::endl;
547 namespace_stack.pop_back();
548 }
549 // or the top most compound definition
550 else {
551 if (compound_stack.empty()) {
552 error("reflect end of compound without a compound started", code);
553 return false;
554 }
555 if (debug_reflection)
556 std::cout << "reflect end of compound " << compound_stack.back()->get_element(2).get_str().c_str() << std::endl;
557 compound_stack.pop_back();
558 compound_access_type_stack.pop_back();
559 }
560 rp_stack.pop_back();
561 return true;
562 }
563 // check for access type declaration
564 std::string new_access_type;
565 if (to_string(toks[0]) == "private" || to_string(toks[0]) == "private:")
566 new_access_type = "private";
567 else if (to_string(toks[0]) == "protected" || to_string(toks[0]) == "protected:")
568 new_access_type = "protected";
569 else if (to_string(toks[0]) == "public" || to_string(toks[0]) == "public:")
570 new_access_type = "public";
571 if (!new_access_type.empty()) {
572 if (rp_stack.empty() || rp_stack.back() != RP_COMPOUND) {
573 error("access type definition only valid inside compound declaration", toks[0]);
574 return false;
575 }
576 if (compound_access_type_stack.empty()) {
577 error("compound_access_type_stack empty", toks[0]);
578 return false;
579 }
580 compound_access_type_stack.back() = new_access_type;
581 return true;
582 }
583
584 // in all other cases there must be at least two tokens
585 if (toks.size() < 2) {
586 error("code splits only into one token and cannot be reflected", code);
587 return false;
588 }
589
590 if (to_string(toks[0]) == "typedef") {
591 // parse typedef declaration
592 if (toks.size() < 3) {
593 error("typedef with only two tokens cannot be reflected", code);
594 return false;
595 }
596 token name_tok = toks.back();
597 token type_tok(toks[1].begin, toks[toks.size() - 2].end);
598
599 // construct reflection info
600 variant type_def = variant(variant::map_type());
601 if (reflect_version_2)
602 type_def.ref_element("reflect").set_str("typedef");
603 type_def.ref_element("namespaces") = construct_namespace_list();
604 type_def.ref_element("namespace_prefix") = construct_namespace_sequence();
605 type_def.ref_element("name") = variant(to_string(name_tok));
606 type_def.ref_element("type") = variant(to_string(type_tok));
607 type_def.ref_element("comment") = variant(to_string(comment));
608
609 // store reflection info
610 ensure_reflect_info();
611 if (reflect_version_2) {
612 if (compound_stack.size() > 0)
613 compound_stack.back()->ref_element("elements").append_to_list(type_def);
614 else
615 reflect_info->ref_element("elements").append_to_list(type_def);
616 }
617 if (reflect_version_1) {
618 if (compound_stack.size() > 0)
619 compound_stack.back()->ref_element("typedefs").append_to_list(type_def);
620 else
621 reflect_info->ref_element("typedefs").append_to_list(type_def);
622 }
623 // generate debug info
624 if (debug_reflection)
625 std::cout << "reflect typedef " << type_def << std::endl;
626 return true;
627 }
628 else if (to_string(toks[0]) == "#include") {
629 // parse include declaration
630 if (toks.size() < 2) {
631 error("cannot reflect include without file name", code);
632 return false;
633 }
634 // store reflection info
635 ensure_reflect_info();
636 reflect_info->ref_element("includes").append_to_list(variant(to_string(toks[1])));
637 // generate debug info
638 if (debug_reflection)
639 std::cout << "reflect include " << to_string(toks[1]).c_str() << std::endl;
640 return true;
641 }
642 else if (to_string(toks[0]) == "enum") {
643 // parse enum declaration and generate list of enum entries
644 variant enum_entries = variant(variant::list_type());
645 // skip CGV_API macro
646 bool contains_cgv_api = false;
647 int idx = 1;
648 if (to_string(toks[1]) == "/*CGV_API*/") {
649 contains_cgv_api = true;
650 idx = 2;
651 }
652 // extract token of class name
653 token name_tok = toks[idx];
654 tokenizer t(token(name_tok.end, commands.back().end));
655 t.set_sep(",={}");
656
657 int v = 0;
658 bool define_name = false;
659 while (!t.skip_ws_check_empty()) {
660 token tok = t.bite();
661 if (to_string(tok) == "{") {
662 define_name = true;
663 continue;
664 }
665 if (to_string(tok) == "}")
666 break;
667 if (to_string(tok) == ",") {
668 define_name = true;
669 continue;
670 }
671 if (to_string(tok) == "=") {
672 define_name = false;
673 continue;
674 }
675 if (define_name) {
676 variant enum_entry = variant(variant::map_type());
677 enum_entry.ref_element("name").set_str(to_string(tok));
678 enum_entry.ref_element("value").set_int(v);
679 enum_entries.append_to_list(enum_entry);
680 ++v;
681 }
682 else {
683 int w;
684 if (!is_integer(to_string(tok), w)) {
685 error("enum value expected", tok);
686 return false;
687 }
688 enum_entries.ref_element(enum_entries.get_size() - 1).ref_element(1).set_int(w);
689 v = w + 1;
690 }
691 }
692
693 // prepate enum_info
694 variant enum_info = variant(variant::map_type());
695 if (reflect_version_2)
696 enum_info.ref_element("reflect").set_str("enum");
697 enum_info.ref_element("namespaces") = construct_namespace_list();
698 enum_info.ref_element("namespace_prefix") = construct_namespace_sequence();
699 enum_info.ref_element("name").set_str(to_string(name_tok));
700 enum_info.ref_element("entries") = enum_entries;
701 enum_info.ref_element("comment").set_str(to_string(comment));
702 enum_info.ref_element("contains_CGV_API").set_bool(contains_cgv_api);
703
704 // store reflection info
705 ensure_reflect_info();
706 if (reflect_version_1) {
707 if (compound_stack.size() > 0)
708 compound_stack.back()->ref_element("enums").append_to_list(enum_info);
709 else
710 reflect_info->ref_element("enums").append_to_list(enum_info);
711 }
712 if (reflect_version_2) {
713 if (compound_stack.size() > 0)
714 compound_stack.back()->ref_element("elements").append_to_list(enum_info);
715 else
716 reflect_info->ref_element("elements").append_to_list(enum_info);
717 }
718 // generate debug info
719 if (debug_reflection)
720 std::cout << "reflect enum " << enum_info << std::endl;
721 return true;
722 }
723 else if (to_string(toks[0]) == "struct" || to_string(toks[0]) == "class" || to_string(toks[0]) == "union") {
724 // parse code and generate base list
725 variant base_list = variant(variant::list_type());
726 // skip CGV_API macro
727 bool contains_cgv_api = false;
728 int idx = 1;
729 if (to_string(toks[1]) == "CGV_API") {
730 contains_cgv_api = true;
731 idx = 2;
732 }
733 // extract token of class name
734 token name_tok = tokenizer(toks[idx]).set_sep(":").bite();
735 // tokenize remaining text again to find first colon
736 tokenizer tmp(token(name_tok.end, toks.back().end));
737 tmp.set_sep(":").set_ws("").bite();
738 token base_tok;
739 if (!tmp.empty()) {
740 if (to_string(tmp.bite()) == ":" ||
741 (!tmp.empty() && to_string(tmp.bite()) == ":")) {
742 if (!tmp.empty())
743 base_tok = token(tmp.bite().begin, toks.back().end);
744 }
745 }
746 if (!base_tok.empty()) {
747 tmp = tokenizer(base_tok);
748 std::vector<token> base_toks;
749 bite_all(tmp.set_sep(",").set_ws("").set_skip("<", ">"), base_toks);
750 if (base_toks.size() % 2 == 0 ||
751 (base_toks.size() > 0 && to_string(base_toks[0]) == ",")) {
752 error("found invalid base list", base_tok);
753 return false;
754 }
755 std::string access_type = "public";
756 if (to_string(toks[0]) == "class")
757 access_type = "private";
758 for (unsigned int i = 0; i < base_toks.size(); i += 2) {
759 bool is_virtual = false;
760 std::vector<token> toks;
761 tokenizer ttt(base_toks[i]);
762 bite_all(ttt, toks);
763 if (toks.size() == 0) {
764 error("found invalid base definition", base_toks[i]);
765 return false;
766 }
767 unsigned int j = 0;
768 for (unsigned int k = 0; k < 2; ++k, ++j) {
769 if (toks.size() > j) {
770 if (to_string(toks[j]) == "virtual")
771 is_virtual = true;
772 else if (to_string(toks[j]) == "private")
773 access_type = "private";
774 else if (to_string(toks[j]) == "protected")
775 access_type = "protected";
776 else if (to_string(toks[j]) == "public")
777 access_type = "public";
778 else
779 break;
780 }
781 }
782 token base_name_tok(toks[j].begin, toks.back().end);
783 variant base = variant(variant::map_type());
784 base.ref_element("type_name").set_str(to_string(base_name_tok));
785 base.ref_element("is_virtual").set_bool(is_virtual);
786 base.ref_element("access_type").set_str(access_type);
787 base_list.append_to_list(base);
788 }
789 }
790
791 // prepate compound reflection info
792 variant compound = variant(variant::map_type());
793 if (reflect_version_2)
794 compound.ref_element("reflect").set_str("compound");
795 compound.ref_element("namespaces") = construct_namespace_list();
796 compound.ref_element("namespace_prefix") = construct_namespace_sequence();
797 if (reflect_version_1) {
798 compound.ref_element("type_name").set_str(to_string(name_tok));
799 compound.ref_element("typedefs").set_list();
800 compound.ref_element("enums").set_list();
801 compound.ref_element("compounds").set_list();
802 compound.ref_element("members").set_list();
803 compound.ref_element("methods").set_list();
804 }
805 if (reflect_version_2) {
806 compound.ref_element("name").set_str(to_string(name_tok));
807 compound.ref_element("elements").set_list();
808 }
809 compound.ref_element("bases") = base_list;
810 compound.ref_element("kind").set_str(to_string(toks[0]));
811 compound.ref_element("comment").set_str(to_string(comment));
812 compound.ref_element("contains_CGV_API").set_bool(contains_cgv_api);
813
814 // store reflection info
815 ensure_reflect_info();
816 if (reflect_version_1) {
817 if (compound_stack.size() > 0) {
818 compound_stack.back()->ref_element("compounds").append_to_list(compound);
819 compound_stack.push_back(&compound_stack.back()->ref_element("compounds").ref_element(compound_stack.back()->ref_element("compounds").get_size() - 1));
820 }
821 else {
822 reflect_info->ref_element("compounds").append_to_list(compound);
823 compound_stack.push_back(&reflect_info->ref_element("compounds").ref_element(reflect_info->get_element("compounds").get_size() - 1));
824 }
825 }
826 if (reflect_version_2) {
827 if (compound_stack.size() > 0) {
828 compound_stack.back()->ref_element("elements").append_to_list(compound);
829 compound_stack.push_back(&compound_stack.back()->ref_element("elements").ref_element(compound_stack.back()->ref_element("elements").get_size() - 1));
830 }
831 else {
832 reflect_info->ref_element("elements").append_to_list(compound);
833 compound_stack.push_back(&reflect_info->ref_element("elements").ref_element(reflect_info->get_element("elements").get_size() - 1));
834 }
835 }
836 // generate debug info
837 if (debug_reflection)
838 std::cout << "reflect compound " << compound << std::endl;
839
840 // start definition of compound and define default access type
841 compound_access_type_stack.push_back("public");
842 if (to_string(toks[0]) == "class")
843 compound_access_type_stack.back() = "private";
844 rp_stack.push_back(RP_COMPOUND);
845
846 return true;
847 }
848 else if (to_string(toks[0]) == "namespace") {
849 token name_tok = toks[1];
850 if (debug_reflection)
851 std::cout << "reflect start namespace " << to_string(name_tok).c_str() << " // "
852 << to_string(comment).c_str() << std::endl;
853 namespace_stack.push_back(to_string(name_tok));
854 rp_stack.push_back(RP_NAMESPACE);
855 return true;
856 }
857 else {
858 // remaining declarations must be variable / member, function / method, constructor or operator
859
860 // only variable or member declarations supported yet
861 if (to_string(toks.back()) == "const" || to_string(toks.back()) == ")") {
862 error("method reflection not supported yet", toks.back());
863 return false;
864 }
865 if (to_string(toks.back()) == "]") {
866 error("array reflection not supported yet", toks.back());
867 return false;
868 }
869
870 // parse into static, type and name
871 int idx = 0;
872 bool is_static = false;
873 if (to_string(toks[0]) == "static") {
874 is_static = true;
875 idx = 1;
876 }
877 token type_tok(toks[idx].begin, toks[toks.size() - 2].end);
878 token name_tok = toks.back();
879
880 if (reflect_version_1) {
881 if (!compound_stack.empty()) {
882 // construct member info
883 variant member_info = variant(variant::map_type());
884 member_info.ref_element("name").set_str(to_string(name_tok));
885 member_info.ref_element("type").set_str(to_string(type_tok));
886 member_info.ref_element("access_type").set_str(compound_access_type_stack.back());
887 member_info.ref_element("is_static").set_bool(is_static);
888 member_info.ref_element("comment").set_str(to_string(comment));
889
890 // store member info
891 compound_stack.back()->ref_element("members").append_to_list(member_info);
892
893 // generate debug info
894 if (debug_reflection)
895 std::cout << "reflect member " << member_info << std::endl;
896 }
897 else {
898 // construct variable info
899 variant variable_info = variant(variant::map_type());
900 variable_info.ref_element("namespaces") = construct_namespace_list();
901 variable_info.ref_element("namespace_prefix") = construct_namespace_sequence();
902 variable_info.ref_element("name").set_str(to_string(name_tok));
903 variable_info.ref_element("type").set_str(to_string(type_tok));
904 variable_info.ref_element("comment").set_str(to_string(comment));
905
906 // store variable info
907 ensure_reflect_info();
908 reflect_info->ref_element("variables").append_to_list(variable_info);
909
910 // generate debug info
911 if (debug_reflection)
912 std::cout << "reflect variable " << variable_info << std::endl;
913 }
914 }
915 if (reflect_version_2) {
916 // construct variable info
917 variant variable_info = variant(variant::map_type());
918 variable_info.ref_element("reflect").set_str("variable");
919 variable_info.ref_element("name").set_str(to_string(name_tok));
920 variable_info.ref_element("type").set_str(to_string(type_tok));
921 if (compound_stack.empty()) {
922 variable_info.ref_element("namespaces") = construct_namespace_list();
923 variable_info.ref_element("namespace_prefix") = construct_namespace_sequence();
924 }
925 else
926 variable_info.ref_element("access_type").set_str(compound_access_type_stack.back());
927 variable_info.ref_element("is_static").set_bool(is_static);
928 variable_info.ref_element("comment").set_str(to_string(comment));
929 if (!compound_stack.empty()) {
930 compound_stack.back()->ref_element("elements").append_to_list(variable_info);
931 }
932 else {
933 ensure_reflect_info();
934 reflect_info->ref_element("elements").append_to_list(variable_info);
935 }
936 // generate debug info
937 if (debug_reflection)
938 std::cout << "reflect variable " << variable_info << std::endl;
939 }
940 return true;
941 }
942 error("could not handle reflection", code);
943 return false;
944 }
945
946 using namespace std;
947 using namespace cgv::ppp;
948 using namespace cgv::utils::file;
949
950 void remove_comments(string& text)
951 {
952 string new_text;
953 unsigned i;
954 for (i = 0; i < text.length(); ++i) {
955 if (text[i] == '/') {
956 if (i + 1 < text.length()) {
957 if (text[i + 1] == '/') {
958 i += 2;
959 while (i < text.length()) {
960 if (text[i] == '\n') {
961 break;
962 }
963 ++i;
964 }
965 }
966 else if (text[i + 1] == '*') {
967 i += 3;
968 while (i < text.length()) {
969 if (text[i - 1] == '*' && text[i] == '/') {
970 break;
971 }
972 ++i;
973 }
974 ++i;
975 }
976 }
977 }
978 if (i < text.length())
979 new_text += text[i];
980 }
981 text = new_text;
982 }
983
984 void remove_strings(string& text)
985 {
986 string new_text;
987 unsigned i;
988 for (i = 0; i < text.length(); ++i) {
989 bool was_string = false;
990 if (text[i] == '"') {
991 ++i;
992 while (i < text.length()) {
993 if (text[i] == '\\')
994 ++i;
995 else if (text[i] == '"') {
996 was_string = true;
997 break;
998 }
999 ++i;
1000 }
1001 }
1002 if (!was_string)
1003 new_text += text[i];
1004 }
1005 text = new_text;
1006 }
1007
1008 void filter_out_preprocessor(string& text)
1009 {
1010 string new_text;
1011 vector<token> toks;
1012 tokenizer(text).set_ws("").set_sep("#").bite_all(toks);
1013 for (unsigned i = 0; i < toks.size(); ++i) {
1014 if (toks[i] == "#") {
1015 const char* p = toks[i].begin;
1016 const char* e = toks.back().end;
1017 while (p < e && *p != '\n')
1018 ++p;
1019 if (!new_text.empty())
1020 new_text += '\n';
1021 new_text += string(toks[i].begin, p - toks[i].begin);
1022 while (i + 1 < toks.size() && toks[i + 1].begin < p)
1023 ++i;
1024 }
1025 }
1026 text = new_text;
1027 }
1028
1029 void filter_out_local_includes(string& text)
1030 {
1031 string new_text;
1032 vector<line> lines;
1033 split_to_lines(text, lines, true);
1034 size_t i;
1035 for (i = 0; i < lines.size(); ++i) {
1036 string l = to_upper(to_string(token(skip_spaces(lines[i].begin + 1, lines[i].end), lines[i].end)));
1037 size_t n = l.length();
1038 bool keep = true;
1039 if (l.substr(0, 7) == "INCLUDE") {
1040 bool is_local = true;
1041 if (l.find_first_of('"') == string::npos) {
1042 size_t p0 = l.find_first_of('<');
1043 size_t p1 = l.find_first_of('>', p0);
1044 if (p0 != string::npos && p1 != string::npos)
1045 is_local = false;
1046 }
1047 if (is_local)
1048 keep = false;
1049 }
1050 if (keep) {
1051 if (!new_text.empty())
1052 new_text += '\n';
1053 new_text += to_string(lines[i]);
1054 }
1055 }
1056 text = new_text;
1057 }
1058
1059 enum LineType {
1060 LT_IF,
1061 LT_ELIF,
1062 LT_ELSE,
1063 LT_ENDIF,
1064 LT_INCLUDE,
1065 LT_DEFINE,
1066 LT_OTHER
1067 };
1068
1069
1070 void filter_out_empty_blocks(string& text)
1071 {
1072 string new_text;
1073 vector<line> lines;
1074 split_to_lines(text, lines, true);
1075 vector<LineType> line_types;
1076 line_types.resize(lines.size());
1077 unsigned i, j;
1078 for (i = 0; i < lines.size(); ++i) {
1079 string l = to_upper(to_string(token(skip_spaces(lines[i].begin + 1, lines[i].end), lines[i].end)));
1080 size_t n = l.length();
1081 if (l.substr(0, 2) == "IF")
1082 line_types[i] = LT_IF;
1083 else if (l.substr(0, 4) == "ELIF")
1084 line_types[i] = LT_ELIF;
1085 else if (l.substr(0, 4) == "ELSE")
1086 line_types[i] = LT_ELSE;
1087 else if (l.substr(0, 5) == "ENDIF")
1088 line_types[i] = LT_ENDIF;
1089 else if (l.substr(0, 7) == "INCLUDE")
1090 line_types[i] = LT_INCLUDE;
1091 else if (l.substr(0, 6) == "DEFINE")
1092 line_types[i] = LT_DEFINE;
1093 else
1094 line_types[i] = LT_OTHER;
1095 }
1096 j = i = 0;
1097 bool empty = true;
1098 unsigned d = 0;
1099 while (i < line_types.size()) {
1100 switch (line_types[i]) {
1101 case LT_IF:
1102 ++d;
1103 break;
1104 case LT_ELIF:
1105 case LT_ELSE:
1106 break;
1107 case LT_ENDIF:
1108 --d;
1109 break;
1110 case LT_INCLUDE:
1111 case LT_DEFINE:
1112 empty = false;
1113 break;
1114 case LT_OTHER:
1115 break;
1116 }
1117 if (d == 0) {
1118 if (!empty) {
1119 while (j <= i) {
1120 if (line_types[j] != LT_OTHER) {
1121 if (!new_text.empty())
1122 new_text += '\n';
1123 new_text += to_string(lines[j]);
1124 }
1125 ++j;
1126 }
1127 empty = true;
1128 }
1129 else
1130 j = i + 1;
1131 }
1132 ++i;
1133 }
1134 text = new_text;
1135 }
1136
1137
1138 bool scan_includes(const std::string& fn, std::string& s)
1139 {
1140 std::string content;
1141 if (!cgv::utils::file::read(fn, content, true))
1142 return false;
1143 remove_comments(content);
1144 remove_strings(content);
1145 filter_out_preprocessor(content);
1146 filter_out_local_includes(content);
1147 filter_out_empty_blocks(content);
1148
1149 if (!content.empty()) {
1150 if (!s.empty())
1151 s = s + '\n';
1152 s += "// ";
1153 s += fn;
1154 s += "\n";
1155 s = s + content;
1156 }
1157 return true;
1158 }
1159
1160 bool ph_processor::process(unsigned int i, unsigned int j)
1161 {
1162 while (i < j) {
1163 switch (commands[i].ct) {
1164 case CT_TEXT:
1165 case CT_IMPLICIT_TEXT:
1166 if (generate_output) {
1167 (*os) << to_string(commands[i]).c_str();
1168 }
1169 break;
1170 case CT_DEFINE:
1171 case CT_SKIP:
1172 {
1173 variant v;
1174 commands[i].expressions[0].evaluate(v, this);
1175 if (exit_code != 0)
1176 return true;
1177 if (!check_evaluation_error(i, 0))
1178 return false;
1179 }
1180 break;
1181 case CT_READ:
1182 {
1183 variant file_name, str, ascii;
1184 commands[i].expressions[0].evaluate(file_name, this);
1185 if (exit_code != 0)
1186 return true;
1187 if (!check_evaluation_error(i, 0))
1188 return false;
1189 commands[i].expressions[2].evaluate(ascii, this);
1190 if (exit_code != 0)
1191 return true;
1192 if (!check_evaluation_error(i, 2))
1193 return false;
1194 if (!cgv::utils::file::exists(file_name.get_str())) {
1195 error(std::string("could not find file '") + file_name.get_str() + "'", commands[i]);
1196 return false;
1197 }
1198 std::string content;
1199 if (!cgv::utils::file::read(file_name.get_str(), content, ascii.get_bool())) {
1200 error(std::string("could not read file '") + file_name.get_str() + "'", commands[i]);
1201 return false;
1202 }
1203 commands[i].expressions[1].evaluate(str, this);
1204 if (exit_code != 0)
1205 return true;
1206 if (!check_evaluation_error(i, 1))
1207 return false;
1208 if (!(str.is_name() || str.is_str())) {
1209 error(std::string("file '") + file_name.get_str() + "' can only be read into a variable given by name or string ", commands[i]);
1210 return false;
1211 }
1212 ref_variable(str.is_name() ? str.get_name() : str.get_str()) = variant(content);
1213 }
1214 break;
1215 case CT_WRITE:
1216 {
1217 variant file_name, str, ascii;
1218 commands[i].expressions[0].evaluate(file_name, this);
1219 if (exit_code != 0)
1220 return true;
1221 if (!check_evaluation_error(i, 0))
1222 return false;
1223 commands[i].expressions[1].evaluate(str, this);
1224 if (exit_code != 0)
1225 return true;
1226 if (!check_evaluation_error(i, 1))
1227 return false;
1228 commands[i].expressions[2].evaluate(ascii, this);
1229 if (exit_code != 0)
1230 return true;
1231 if (!check_evaluation_error(i, 2))
1232 return false;
1233 if (!(str.is_name() || str.is_str())) {
1234 error(std::string("file '") + file_name.get_str() + "' can only be read into a variable given by name or string ", commands[i]);
1235 return false;
1236 }
1237 std::string content = ref_variable(str.is_name() ? str.get_name() : str.get_str()).get_str();
1238 if (!cgv::utils::file::write(file_name.get_str(), &content[0], content.size(), ascii.get_bool())) {
1239 error(std::string("could not write to file '") + file_name.get_str() + "'", commands[i]);
1240 return false;
1241 }
1242 }
1243 break;
1244 case CT_RAND:
1245 {
1246 variant minval, maxval, str, seed;
1247 commands[i].expressions[0].evaluate(minval, this);
1248 if (exit_code != 0)
1249 return true;
1250 if (!check_evaluation_error(i, 0))
1251 return false;
1252 commands[i].expressions[1].evaluate(maxval, this);
1253 if (exit_code != 0)
1254 return true;
1255 if (!check_evaluation_error(i, 1))
1256 return false;
1257 commands[i].expressions[2].evaluate(str, this);
1258 if (exit_code != 0)
1259 return true;
1260 if (!check_evaluation_error(i, 2))
1261 return false;
1262 if (!(str.is_name() || str.is_str())) {
1263 error(std::string("rand can only be generated into a variable given by name or string"), commands[i]);
1264 return false;
1265 }
1266 if (commands[i].expressions.size() == 4) {
1267 commands[i].expressions[3].evaluate(seed, this);
1268 if (exit_code != 0)
1269 return true;
1270 if (!check_evaluation_error(i, 3))
1271 return false;
1272 }
1273 static std::default_random_engine R;
1274 if (commands[i].expressions.size() == 4) {
1275 R.seed(seed.get_int());
1276 }
1277 if (minval.is_double() || maxval.is_double()) {
1278 std::uniform_real_distribution<double> D(minval.get_double(), maxval.get_double());
1279 double d = D(R);
1280 ref_variable(str.is_name() ? str.get_name() : str.get_str()) = variant(d);
1281 }
1282 else {
1283 std::uniform_int_distribution<int> D(minval.get_int(), maxval.get_int());
1284 int i = D(R);
1285 ref_variable(str.is_name() ? str.get_name() : str.get_str()) =
1286 variant(i);
1287 }
1288 }
1289 break;
1290 case CT_NAMESPACE:
1291 {
1292 variant v;
1293 commands[i].expressions[0].evaluate(v, this);
1294 if (exit_code != 0)
1295 return true;
1296 if (!check_evaluation_error(i, 0))
1297 return false;
1298 std::string var_name;
1299 if (v.is_name())
1300 var_name = v.get_name();
1301 else if (v.is_str())
1302 var_name = v.get_str();
1303 else {
1304 error("namespace argument must evaluate to name or string", commands[i]);
1305 return false;
1306 }
1307 variant* ns = find_variable(var_name, true);
1308 if (!ns) {
1309 ns = &ref_variable(var_name, true);
1310 *ns = variant(variant::map_type());
1311 }
1312 else if (ns->get_value_type() != MAP_VALUE) {
1313 error("namespace cannot overwrite previously defined variable", commands[i]);
1314 return false;
1315 }
1316 push_namespace(ns);
1317 if (!process(i + 1, commands[i].block_end))
1318 return false;
1319 pop_namespace();
1320 i = commands[i].block_end;
1321 }
1322 --i;
1323 break;
1324 case CT_FOR:
1325 {
1326 variant v;
1327 commands[i].expressions[0].evaluate(v, this);
1328 if (exit_code != 0)
1329 return true;
1330 if (!check_evaluation_error(i, 0))
1331 return false;
1332 do {
1333 commands[i].expressions[1].evaluate(v, this);
1334 if (exit_code != 0)
1335 return true;
1336 if (!check_evaluation_error(i, 1))
1337 return false;
1338 if (!v.get_bool())
1339 break;
1340 if (!process(i + 1, commands[i].block_end))
1341 return false;
1342 commands[i].expressions[2].evaluate(v, this);
1343 if (exit_code != 0)
1344 return true;
1345 if (!check_evaluation_error(i, 2))
1346 return false;
1347 } while (true);
1348 i = commands[i].block_end;
1349 }
1350 --i;
1351 break;
1352 case CT_IF:
1353 {
1354 variant v;
1355 commands[i].expressions[0].evaluate(v, this);
1356 if (exit_code != 0)
1357 return true;
1358 if (!check_evaluation_error(i, 0))
1359 return false;
1360 if (v.get_bool()) {
1361 if (!process(i + 1, commands[i].block_end))
1362 return false;
1363 i = commands[i].block_end;
1364 while (i < j && commands[i].ct == CT_ELIF)
1365 i = commands[i].block_end;
1366 if (i < j && commands[i].ct == CT_ELSE)
1367 i = commands[i].block_end;
1368 }
1369 else {
1370 i = commands[i].block_end;
1371 bool success = false;
1372 while (i < j && commands[i].ct == CT_ELIF) {
1373 if (!success) {
1374 commands[i].expressions[0].evaluate(v, this);
1375 if (exit_code != 0)
1376 return true;
1377 if (!check_evaluation_error(i, 0))
1378 return false;
1379 if (v.get_bool()) {
1380 success = true;
1381 if (!process(i + 1, commands[i].block_end))
1382 return false;
1383 }
1384 }
1385 i = commands[i].block_end;
1386 }
1387 if (i < j && commands[i].ct == CT_ELSE) {
1388 if (!success) {
1389 if (!process(i + 1, commands[i].block_end))
1390 return false;
1391 }
1392 i = commands[i].block_end;
1393 }
1394 }
1395 }
1396 --i;
1397 break;
1398 case CT_EVAL:
1399 case CT_STRING:
1400 {
1401 variant v;
1402 commands[i].expressions[0].evaluate(v, this);
1403 if (exit_code != 0)
1404 return true;
1405 if (!check_evaluation_error(i, 0))
1406 return false;
1407 if (generate_output) {
1408 if (commands[i].ct == CT_STRING)
1409 (*os) << "\"" << v.get_str().c_str() << "\"";
1410 else
1411 (*os) << v.get_str().c_str();
1412 }
1413 }
1414 break;
1415 case CT_LIST:
1416 if (!process_list(i))
1417 return false;
1418 if (exit_code != 0)
1419 return true;
1420 break;
1421 case CT_EXCLUDE:
1422 case CT_INCLUDE:
1423 if (!process_include(i))
1424 return false;
1425 if (exit_code != 0)
1426 return true;
1427 break;
1428 case CT_CINCLUDE:
1429 if (!process_include(i, true))
1430 return false;
1431 if (exit_code != 0)
1432 return true;
1433 break;
1434 case CT_INSERT:
1435 if (!process_include(i, false, true))
1436 return false;
1437 if (exit_code != 0)
1438 return true;
1439 break;
1440 case CT_ELIF:
1441 error("elif without if", commands[i]);
1442 return false;
1443 break;
1444 case CT_ELSE:
1445 error("else without if", commands[i]);
1446 return false;
1447 break;
1448 case CT_REFLECT_NEXT_LINE:
1449 {
1450 tokenizer t(token(commands[i].end, &(*content->rbegin()) + 1));
1451 if (t.empty()) {
1452 error("next line reflection command without a line to follow", commands[i]);
1453 return false;
1454 }
1455 token comment_tok = t.set_sep("\n").set_ws("").bite();
1456 if (to_string(comment_tok) == "\n")
1457 comment_tok = token();
1458 else {
1459 if (t.empty()) {
1460 error("next line reflection command without a line to follow", commands[i]);
1461 return false;
1462 }
1463 t.bite();
1464 }
1465 if (t.empty()) {
1466 error("next line reflection command without a line to follow", commands[i]);
1467 return false;
1468 }
1469 t.set_sep(";{");
1470 if (!reflect(t.bite(), comment_tok))
1471 return false;
1472 }
1473 break;
1474 case CT_REFLECT_PREV_LINE:
1475 {
1476 tokenizer t(token(commands[i].end, &(*content->rbegin()) + 1));
1477 token comment_tok;
1478 if (!t.empty()) {
1479 comment_tok = t.set_sep("\n").set_ws("").bite();
1480 if (to_string(comment_tok) == "\n")
1481 comment_tok = token();
1482 }
1483 t = tokenizer(token(&(*content->begin()), commands[i].begin - 2));
1484 t.set_sep("\n").set_ws("");
1485 if (t.empty()) {
1486 error("previous line reflection command without a line to preceed", commands[i]);
1487 return false;
1488 }
1489 if (!reflect(t.reverse_bite(), comment_tok))
1490 return false;
1491 }
1492 break;
1493 case CT_COUT:
1494 {
1495 variant v;
1496 commands[i].expressions[0].evaluate(v, this);
1497 if (exit_code != 0)
1498 return true;
1499 if (!check_evaluation_error(i, 0))
1500 return false;
1501 std::cout << v.get_value();
1502 std::cout.flush();
1503 break;
1504 }
1505 case CT_CIN:
1506 {
1507 variant v;
1508 commands[i].expressions[0].evaluate(v, this);
1509 if (exit_code != 0)
1510 return true;
1511 if (!check_evaluation_error(i, 0))
1512 return false;
1513 if (!v.is_reference()) {
1514 error("cin target expression must evaluate to reference", commands[i]);
1515 return false;
1516 }
1517 char buffer[1000];
1518 std::cin.getline(buffer, 1000);
1519 std::string s(buffer);
1520 int i;
1521 if (is_integer(s, i))
1522 v.ref_value().set_int(i);
1523 else
1524 v.ref_value().set_str(s);
1525 break;
1526 }
1527 case CT_ERROR:
1528 case CT_WARNING:
1529 {
1530 variant v1, v2;
1531 commands[i].expressions[0].evaluate(v1, this);
1532 if (exit_code != 0)
1533 return true;
1534 if (!check_evaluation_error(i, 0))
1535 return false;
1536 commands[i].expressions[1].evaluate(v2, this);
1537 if (exit_code != 0)
1538 return true;
1539 if (!check_evaluation_error(i, 1))
1540 return false;
1541 std::cout << file_name.c_str() << "(" << get_line_number(commands[i]) << ") : ";
1542 if (commands[i].ct == CT_ERROR)
1543 std::cout << "error ";
1544 else
1545 std::cout << "warning ";
1546 std::cout << v1.get_int() << ": " << v2.get_str().c_str() << std::endl;
1547 break;
1548 }
1549 case CT_SYSTEM:
1550 {
1551 variant v1;
1552 commands[i].expressions[0].evaluate(v1, this);
1553 if (exit_code != 0)
1554 return true;
1555 if (!check_evaluation_error(i, 0))
1556 return false;
1557 variant v2;
1558 commands[i].expressions[1].evaluate(v2, this);
1559 if (exit_code != 0)
1560 return true;
1561 if (!check_evaluation_error(i, 1))
1562 return false;
1563 if (!v2.is_reference()) {
1564 error("system result expression must evaluate to reference", commands[i]);
1565 return false;
1566 }
1567 v2.ref_value().set_int(system(v1.get_str().c_str()));
1568 break;
1569 }
1570 case CT_EXIT:
1571 {
1572 variant v1;
1573 commands[i].expressions[0].evaluate(v1, this);
1574 if (exit_code != 0)
1575 return true;
1576 check_evaluation_error(i, 0);
1577 exit_code = v1.get_int();
1578 return true;
1579 }
1580 case CT_FUNC:
1581 {
1582 variant v1;
1583 commands[i].expressions[0].evaluate(v1, this);
1584 if (exit_code != 0)
1585 return true;
1586 if (!check_evaluation_error(i, 0))
1587 return false;
1588 if (!(v1.is_str() || v1.is_name() || v1.is_reference())) {
1589 error("first parameter of func() has to be a name, string or reference", commands[i]);
1590 return false;
1591 }
1592 if (commands[i].expressions.size() == 2 && !commands[i].expressions[1].is_func_decl()) {
1593 error(commands[i].expressions[1].get_last_error(), commands[i].expressions[1].get_last_error_token());
1594 return false;
1595 }
1596 ++nr_functions;
1597 if (v1.is_reference())
1598 v1.ref_value() = variant(func_type(i + 1, commands[i].block_end, this, get_current_namespace()->get_environment()));
1599 else
1600 ref_variable(v1.is_str() ? v1.get_str() : v1.get_name()) = variant(func_type(i + 1, commands[i].block_end, this, get_current_namespace()->get_environment()));
1601 i = commands[i].block_end - 1;
1602 break;
1603 }
1604 case CT_DIR:
1605 {
1606 variant v1;
1607 commands[i].expressions[0].evaluate(v1, this);
1608 if (exit_code != 0)
1609 return true;
1610 if (!check_evaluation_error(i, 0))
1611 return false;
1612 if (!v1.is_str()) {
1613 error("first parameter of dir() has to be a directory as string", commands[i]);
1614 return false;
1615 }
1616 variant v2;
1617 commands[i].expressions[1].evaluate(v2, this);
1618 if (exit_code != 0)
1619 return true;
1620 if (!check_evaluation_error(i, 0))
1621 return false;
1622 if (!v2.is_str()) {
1623 error("second parameter of dir() has to be a filter string", commands[i]);
1624 return false;
1625 }
1626 variant v3;
1627 commands[i].expressions[2].evaluate(v3, this);
1628 if (exit_code != 0)
1629 return true;
1630 if (!check_evaluation_error(i, 0))
1631 return false;
1632 if (!v3.is_int()) {
1633 error("third parameter of dir() has to be a int", commands[i]);
1634 return false;
1635 }
1636 variant v4;
1637 commands[i].expressions[3].evaluate(v4, this);
1638 if (exit_code != 0)
1639 return true;
1640 if (!check_evaluation_error(i, 0))
1641 return false;
1642 if (!v4.is_list()) {
1643 error("fourth parameter of dir() has to be a list", commands[i]);
1644 return false;
1645 }
1646
1647 std::string directory = v1.get_str();
1648 //void* fh = cgv::utils::file::find_first(directory + "/*");
1649 //if (fh == 0) {
1650 // error("path \"" + directory + "\" does not exist", commands[i]);
1651 //}
1652 if(!cgv::utils::dir::exists(directory)) {
1653 error("path \"" + directory + "\" does not exist", commands[i]);
1654 }
1655
1656 std::string filter = v2.get_str();
1657 bool recursive = v3.get_int() > 0;
1658
1659
1660 if (!(directory[directory.size() - 1] == '/'))
1661 directory = directory + "/";
1662
1663 std::string actual_directory = "";//TODO
1664
1665 bool rel_path = true;
1666 if (actual_directory == directory)
1667 rel_path = false;
1668
1669 bool success = recursive_dir(directory, actual_directory, filter, rel_path, v4, recursive);
1670 if (!success) return false;
1671
1672 break;
1673 }
1674 case CT_TRANSFORM:
1675 {
1676 variant v1;
1677 commands[i].expressions[0].evaluate(v1, this);
1678 if (exit_code != 0)
1679 return true;
1680 if (!check_evaluation_error(i, 0))
1681 return false;
1682 if (!v1.is_str()) {
1683 error("first parameter of transform() must evaluate to a file name as string", commands[i]);
1684 return false;
1685 }
1686 variant v2;
1687 commands[i].expressions[1].evaluate(v2, this);
1688 if (exit_code != 0)
1689 return true;
1690 if (!check_evaluation_error(i, 0))
1691 return false;
1692 if (!v2.is_str()) {
1693 error("second parameter of transform() must evaluate to a file name as string", commands[i]);
1694 return false;
1695 }
1696 std::string inp = find_include_file(v1.get_str(), true);
1697 if (inp.empty()) {
1698 error("could not find input file to transform() ", commands[i]);
1699 return false;
1700 }
1701 ph_processor* fp = new ph_processor(additional_include_path, false);
1702 bool success = false;
1703 if ((success = fp->parse_file(inp))) {
1704 std::string output;
1705 success = fp->process_to_file(v2.get_str(), cgv::utils::file::get_last_write_time(inp)) != 0;
1706 }
1707 if (fp->exit_code != 0)
1708 exit_code = fp->exit_code;
1709 delete fp;
1710 if (!success)
1711 return false;
1712 break;
1713 }
1714 case CT_SCAN_INCLUDES:
1715 {
1716 variant v1;
1717 commands[i].expressions[0].evaluate(v1, this);
1718 if (exit_code != 0)
1719 return true;
1720 if (!check_evaluation_error(i, 0))
1721 return false;
1722 if (!v1.is_str()) {
1723 error("first parameter of scan_includes() must evaluate to a file name as string", commands[i]);
1724 return false;
1725 }
1726 variant v2;
1727 commands[i].expressions[1].evaluate(v2, this);
1728 if (exit_code != 0)
1729 return true;
1730 if (!check_evaluation_error(i, 0))
1731 return false;
1732 if (!v2.is_name()) {
1733 error("second parameter of scan_includes() must evaluate to a variable name", commands[i]);
1734 return false;
1735 }
1736 variant& v_str = ref_variable(v2.get_name());
1737 if (!v_str.is_str()) {
1738 error("second parameter of scan_includes() must evaluate to the name of variable of STRING type", commands[i]);
1739 return false;
1740 }
1741 std::string inp = find_include_file(v1.get_str(), true);
1742 if (inp.empty()) {
1743 error("could not find input file to scan_includes() ", commands[i]);
1744 return false;
1745 }
1746 scan_includes(inp, v_str.ref_str());
1747 }
1748 break;
1749 default:
1750 error(std::string("token ") +
1751 commands[i].get_keyword() +
1752 " not expected", commands[i]);
1753 return false;
1754 break;
1755 }
1756 ++i;
1757 }
1758 return true;
1759 }
1760
1761 bool ph_processor::recursive_dir(
1762 std::string rel_directory,
1763 std::string actual_directory,
1764 std::string filter,
1765 bool rel_path,
1766 variant& desc_list,
1767 bool recursive)
1768 {
1769 bool skip_files = false;
1770 bool list_dirs = false;
1771 void* fh;
1772 if (filter == ".") {
1773 // std::cout << "directories in :" << (rel_directory+"*").c_str() << std::endl;
1774 fh = cgv::utils::file::find_first(rel_directory + "*");
1775 skip_files = true;
1776 list_dirs = true;
1777 }
1778 else {
1779 std::string mask = rel_directory + filter;
1780 fh = cgv::utils::file::find_first(mask);
1781 if (fh == 0 && recursive) {
1782 fh = cgv::utils::file::find_first(rel_directory + "*");
1783 skip_files = true;
1784 }
1785 }
1786 if (fh == 0)
1787 return true;
1788 bool not_finished;
1789 do {
1790 not_finished = false;
1791 do {
1792 std::string fn = cgv::utils::file::find_name(fh);
1793 if (fn == "." || fn == ".." || fn == ".svn")
1794 continue;
1795 bool is_dir = cgv::utils::file::find_directory(fh);
1796 if ((!is_dir && !skip_files) || (is_dir && list_dirs)) {
1797 std::vector<variant> file_desc;
1798 //filename
1799 file_desc.push_back(new variant(fn));
1800 //relative path
1801 file_desc.push_back(new variant(rel_directory + fn));
1802 //absolute path
1803 if (rel_path)
1804 file_desc.push_back(new variant(actual_directory + fn));
1805 else
1806 file_desc.push_back(new variant(rel_directory + fn));
1807 //is directory?
1808 file_desc.push_back(new variant(is_dir));
1809 //size of file
1810 file_desc.push_back(new variant((int)cgv::utils::file::find_size(fh)));
1811 //list of sub dirs
1812 std::vector<variant> sub_dirs;
1813 std::vector<token> toks;
1814 tokenizer(actual_directory).set_ws("/\\").bite_all(toks);
1815 for (unsigned int i = 0; i < toks.size(); ++i)
1816 sub_dirs.push_back(to_string(toks[i]));
1817 file_desc.push_back(new variant(sub_dirs));
1818
1819 desc_list.append_to_list(new variant(file_desc));
1820 }
1821
1822 if (recursive&&is_dir) {
1823 if (!recursive_dir(rel_directory + fn + "/",
1824 actual_directory + fn + "/",
1825 filter,
1826 rel_path, desc_list, recursive))
1827 return false;
1828 }
1829 } while ((fh = cgv::utils::file::find_next(fh)) != 0);
1830 if (!skip_files && recursive) {
1831 fh = cgv::utils::file::find_first(rel_directory + "*");
1832 skip_files = true;
1833 if (fh)
1834 not_finished = true;
1835 }
1836 } while (not_finished);
1837 return true;
1838 }
1839
1840 bool ph_processor::parse()
1841 {
1842 unsigned nr_skip = 0;
1843 if (content->size() > 1) {
1844 if (content->at(0) == '@' && content->at(1) == '=') {
1845 if (content->size() == 2 || is_space(content->at(2)))
1846 special = 0;
1847 else
1848 special = content->at(2);
1849 nr_skip = 3;
1850 }
1851 }
1852 if (special == 0)
1853 return parse_without_special(nr_skip);
1854
1855 found_error = false;
1856 bool block_begin_follows = false;
1857 lines.clear();
1858 commands.clear();
1859 // iterate all lines and @-tokens on each line
1860 std::stack<unsigned int> block_stack;
1861 split_to_lines(*content, lines, false);
1862
1863 tokenizer toker(*content);
1864 toker.begin += nr_skip;
1865 toker.set_sep(std::string(1, special)).set_skip("'\"", "'\"").set_ws("");
1866 while (!toker.empty()) {
1867 // construct command token
1868 command_token ct;
1869 // in inter woven mode each token not starting with @ is a text token
1870 token tok = toker.bite();
1871 std::string s = to_string(tok);
1872 // either as a text token
1873 if (*tok.begin != special)
1874 ct = command_token(tok);
1875 // if equal to @, first check if a ppp comment is following
1876 else if (!toker.empty() && *toker.begin == '/') {
1877 // skip rest of current line
1878 while (!toker.empty()) {
1879 ++toker.begin;
1880 if (toker.begin[-1] == '\n')
1881 break;
1882 }
1883 continue;
1884 }
1885 // finally split off command token needs to be split off from next token
1886 else {
1887 // if special character repeats, interpret first as escape character
1888 if (toker.begin < toker.end && *toker.begin == special) {
1889 ++toker.begin;
1890 ct = command_token(tok);
1891 }
1892 else if (!ct.split_off_from(toker)) {
1893 error(ct.get_last_error(), ct.get_last_error_token());
1894 continue;
1895 }
1896 }
1897 // eliminate empty tokens before else and elif commands
1898 if (ct.remove_preceeding_empty_text_token() && !commands.empty() &&
1899 commands.back().is_empty())
1900 commands.pop_back();
1901 // handle the beginning of blocks
1902 if (block_begin_follows) {
1903 if (ct.ct == CT_BEGIN) {
1904 block_begin_follows = false;
1905 continue;
1906 }
1907 if (ct.is_empty())
1908 continue;
1909 error(std::string("expected ") + special + '{', ct);
1910 block_begin_follows = false;
1911 }
1912 // handle the end of blocks
1913 if (ct.ct == CT_END) {
1914 if (block_stack.empty()) {
1915 error(std::string() + special + " unexpected", ct);
1916 continue;
1917 }
1918 commands[block_stack.top()].block_end = (unsigned int)commands.size();
1919 block_stack.pop();
1920 continue;
1921 }
1922 // handle blocks that are to follow
1923 block_begin_follows = ct.block_follows();
1924 if (block_begin_follows)
1925 block_stack.push((unsigned int)commands.size());
1926 commands.push_back(ct);
1927 }
1928 if (!block_stack.empty()) {
1929 error("block begin not matched", commands[block_stack.top()]);
1930 }
1931 //std::cout << (unsigned int) lines.size() << " lines and " << commands.size() << " attributed line tokens" << std::endl;
1932 return !found_error;
1933 }
1934
1935 bool ph_processor::parse_without_special(unsigned nr_skip)
1936 {
1937 found_error = false;
1938 bool block_begin_follows = false;
1939 lines.clear();
1940 commands.clear();
1941
1942 // iterate all lines and @-tokens on each line
1943 std::stack<unsigned int> block_stack;
1944 split_to_lines(*content, lines, false);
1945
1946 token T(*content);
1947 T.begin += nr_skip;
1948 do {
1949 T.skip(" \t\n");
1950 if (T.empty())
1951 break;
1952 // construct command token
1953 command_token ct;
1954 // if equal to @, first check if a ppp comment is following
1955 if (*T.begin == '/') {
1956 // skip rest of current line
1957 while (!T.empty()) {
1958 ++T.begin;
1959 if (T.begin[-1] == '\n')
1960 break;
1961 }
1962 continue;
1963 }
1964 // finally split off command token needs to be split off from next token
1965 else {
1966 const char* old_begin = T.begin;
1967 bool success = ct.split_off_from(T);
1968 if (!success && ct.get_last_error() != "could not determine command") {
1969 error(ct.get_last_error(), ct.get_last_error_token());
1970 return false;
1971 }
1972 if (success && (ct.ct == CT_EVAL || ct.ct == CT_STRING || ct.ct == CT_LIST)) {
1973 T.begin = old_begin;
1974 success = false;
1975 }
1976 if (!success) {
1977 // try to interpret as expression
1978 token old_T = T, exp_tok;
1979 if (tokenizer(T).set_sep(";{}").set_ws("").set_skip("'\"", "'\"").balanced_bite(exp_tok, "([", ")]", true)) {
1980 T.begin = exp_tok.end;
1981 if (!T.empty() && T[0] == ';')
1982 ++T.begin;
1983
1985 if (!ep.parse(exp_tok)) {
1986 error(ep.get_last_error(), ep.get_last_error_token());
1987 return false;
1988 }
1989 if (!ep.validate(true)) {
1990 error(std::string("could not validate expression: ") + ep.get_last_error(),
1991 ep.get_last_error_token().empty() ? exp_tok : ep.get_last_error_token());
1992 return false;
1993 }
1994 ct.ct = CT_DEFINE;
1995 ct.parenthesis_index = 0;
1996 ct.expressions.clear();
1997 ct.begin = exp_tok.begin;
1998 ct.end = exp_tok.end;
1999 ct.expressions.push_back(ep);
2000 }
2001 else {
2002 error("could not extract expression with balanced parantheses", old_T);
2003 return false;
2004 }
2005 }
2006 }
2007 // handle the beginning of blocks
2008 if (block_begin_follows) {
2009 if (ct.ct == CT_BEGIN) {
2010 block_begin_follows = false;
2011 continue;
2012 }
2013 if (ct.is_empty())
2014 continue;
2015 error("expected {", ct);
2016 block_begin_follows = false;
2017 }
2018 // handle the end of blocks
2019 if (ct.ct == CT_END) {
2020 if (block_stack.empty()) {
2021 error("} unexpected", ct);
2022 continue;
2023 }
2024 commands[block_stack.top()].block_end = (unsigned int)commands.size();
2025 block_stack.pop();
2026 continue;
2027 }
2028 // handle blocks that are to follow
2029 block_begin_follows = ct.block_follows();
2030 if (block_begin_follows)
2031 block_stack.push((unsigned int)commands.size());
2032 commands.push_back(ct);
2033 } while (!T.empty());
2034 if (!block_stack.empty()) {
2035 error("block begin not matched", commands[block_stack.top()]);
2036 }
2037 //std::cout << (unsigned int) lines.size() << " lines and " << commands.size() << " attributed line tokens" << std::endl;
2038 return !found_error;
2039 }
2040 }
2041}
bool validate(bool allow_several_values=false)
the pre header processor parses a pre header file and converts it to a header file or uses the inform...
std::string get_str() const
lookup names and follow references and convert to string
ValueType get_value_type() const
lookup names and follow references and return value type
const variant & get_value() const
lookup names and follow references and return the reached variant
double get_double() const
lookup names and follow references and convert to double: undef ... -1, bool ... 0 or 1,...
bool is_int() const
lookup names and follow references and return whether variant is int
int get_int() const
lookup names and follow references and convert to int: undef ... -1, bool ... 0 or 1,...
const std::string & get_name() const
return the name of a name value
bool is_reference() const
name and reference type return true
bool is_name() const
only a name returns true
bool get_bool() const
lookup names and follow references and convert to bool: undef ... false, int ... compares unequal zer...
variant & ref_value()
lookup names and follow references and return reference to the reached variant
bool is_list() const
lookup names and follow references and return whether variant is list
bool is_str() const
lookup names and follow references and return whether variant is string
bool is_double() const
lookup names and follow references and return whether variant is double
the tokenizer allows to split text into tokens in a convenient way.
Definition tokenizer.h:68
tokenizer & set_sep(const std::string &sep, bool merge)
set the list of separators and specify whether succeeding separators are merged into single tokens
Definition tokenizer.cxx:59
tokenizer & set_ws(const std::string &ws)
set the list of white spaces, that separate tokens and are skipped
Definition tokenizer.cxx:38
token bite()
bite away a single token from the front
namespace that holds tools that dont fit any other namespace
bool is_digit(char c)
check if char is a digit
Definition scan.cxx:20
void split_to_tokens(const char *begin, const char *end, std::vector< token > &tokens, const std::string &separators, bool merge_separators, const std::string &open_parenthesis, const std::string &close_parenthesis, const std::string &whitespaces, unsigned int max_nr_tokens)
this function splits a text range into tokens.
std::string to_string(const std::string &v, unsigned int w, unsigned int p, bool)
specialization of conversion from string to strings
bool is_integer(const char *begin, const char *end, int &value)
check if the text range (begin,end( defines an integer value. If yes, store the value in the passed r...
Definition scan.cxx:367
void split_to_lines(const char *global_begin, const char *global_end, std::vector< line > &lines, bool truncate_trailing_spaces)
this function splits a text range at the newline characters into single lines.
void bite_all(tokenizer &t, std::vector< token > &result)
bite all tokens into a token vector
Definition tokenizer.h:121
char to_upper(char c)
convert char to upper case
Definition scan.cxx:106
bool is_space(char c)
check if char is a whitespace
Definition scan.cxx:12
const char * skip_spaces(const char *begin, const char *end)
return new start pointer by skipping spaces at begin
Definition scan.cxx:683
the cgv namespace
Definition print.h:11
bool remove_preceeding_empty_text_token() const
return whether empty text tokens before this command token should be deleted
bool block_follows() const
return whether the command token must be followed by a block
bool is_empty() const
check whether the token only contains white spaces
bool split_off_from(token &t)
splits a command token from the front of a given token
unsigned int parenthesis_index
store index of parenthesis that enclosed the expressions
CommandType ct
store command type
std::vector< expression_processor > expressions
vector of parsed expressions
bool empty() const
return whether the token is empty
Definition token.h:56
const char * begin
pointers that define the range of characters
Definition token.h:20