2#pragma warning (disable:4996)
8#include <cgv/utils/file.h>
9#include <cgv/utils/dir.h>
10#include <cgv/utils/tokenizer.h>
12#include "ph_processor.h"
13#include "expression_processor.h"
17#define reflect_version_1 true
18#define reflect_version_2 false
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)
29 generate_output =
false;
33 content_is_external =
false;
34 inserted_shader_file_names = 0;
36 debug_reflection =
false;
38 debug_reflection =
false;
43 ph_processor::~ph_processor()
48 void ph_processor::configure_insert_to_shader(std::vector<std::string>* _inserted_shader_file_names)
50 inserted_shader_file_names = _inserted_shader_file_names;
53 void ph_processor::close()
55 if (!content_is_external && content) {
58 content_is_external =
false;
62 bool ph_processor::parse_string(
const std::string& text)
66 content_is_external =
true;
70 bool ph_processor::parse_file(
const std::string& _file_name)
74 std::string* file_content =
new std::string();
75 content_is_external =
false;
76 if (!cgv::utils::file::read(_file_name, *file_content,
true)) {
78 (*es) <<
"could not read input file " << _file_name.c_str() << std::endl;
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) {
89 if (carriage_return_found && (*file_content)[i] == 10) {
97 carriage_return_found = (*file_content)[i] == 13;
99 if (carriage_return_found) (*new_file_content) += 10;
101 else (*new_file_content) += (*file_content)[i];
105 file_content = new_file_content;
109 content = file_content;
110 file_name = _file_name;
114 bool ph_processor::process_without_output()
116 generate_output =
false;
117 bool result = process(0, (
unsigned int)commands.size());
127 void ph_processor::swap_output(ph_processor& pp)
129 std::swap(generate_output, pp.generate_output);
130 std::swap(os, pp.os);
133 bool ph_processor::process_to_string(std::string& output)
135 std::stringstream str_os;
137 generate_output =
true;
139 bool result = process(0, (
unsigned int)commands.size());
146 output = str_os.str();
150 int ph_processor::process_to_file(
const std::string& _file_name,
long long last_write_time)
169 if (!process_to_string(output))
172 bool do_write =
true;
173 if (cgv::utils::file::exists(_file_name)) {
175 if (cgv::utils::file::read(_file_name, content,
true)) {
176 if (output == content) {
178 do_write = cgv::utils::file::get_last_write_time(_file_name) < last_write_time;
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;
197 void ph_processor::set_error_stream(std::ostream& error_stream)
202 unsigned int ph_processor::get_line_number(
const token& loc)
const
206 for (li = 0; li < lines.size(); ++li)
207 if (lines[li].begin > loc.begin)
211 void ph_processor::error(
const std::string& text,
const token& loc,
212 unsigned int error_number)
216 unsigned int li = get_line_number(loc);
217 unsigned prev_line = li;
220 const char* lb = loc.begin;
222 lb = lines[li].begin;
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() <<
" \\_ ";
228 token l(loc.begin, loc.begin);
229 while (l.end < loc.end && !
is_space(*l.end))
231 (*es) <<
to_string(l).c_str() <<
" _/" << std::endl;
236 std::string ph_processor::my_getenv(
const char* name)
238 if (ref_variable(name).is_str())
239 return ref_variable(name).
get_str();
241 const char* var = getenv(name);
243 return std::string();
244 return std::string(var);
247 void my_append(std::string& p,
const std::string& o)
259 std::string ph_processor::find_include_file(
const std::string& fn,
bool local)
const
261 token path_token(0, 0);
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;
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))
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);
282 if (!file_path.empty() && cgv::utils::file::exists(file_path)) {
289 bool ph_processor::process_include(
unsigned int i,
bool is_cinclude,
bool insert)
292 if (commands[i].expressions.size() > 0) {
294 commands[i].expressions[0].evaluate(v0,
this);
297 if (!check_evaluation_error(i, 0))
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]);
307 ph_processor* fp =
new ph_processor(additional_include_path,
false);
308 bool success =
false;
309 if ((success = fp->parse_file(file_path))) {
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);
320 (*os) << output.c_str() << std::endl;
323 success = fp->process_without_output();
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()
333 << commands[i].get_close_parenthesis();
335 if (fp->exit_code != 0)
336 exit_code = fp->exit_code;
337 if (fp->nr_functions == 0)
342 bool ph_processor::check_evaluation_error(
unsigned int i,
unsigned int ei)
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());
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)
367 bool bite_digits(
token& t,
int& n)
371 while (!t.empty() &&
is_digit(*t.begin)) {
372 n = 10 * n + (*t.begin -
'0');
379 bool ph_processor::process_list(
unsigned int i)
382 commands[i].expressions[0].evaluate(v0,
this);
385 if (!check_evaluation_error(i, 0))
388 commands[i].expressions[1].evaluate(v1,
this);
391 if (!check_evaluation_error(i, 1))
394 commands[i].expressions[2].evaluate(v2,
this);
397 if (!check_evaluation_error(i, 2))
399 if (!generate_output)
402 std::string s0 = v0.get_str();
404 std::string s1 = v1.get_str();
405 std::string s2 = v2.get_str();
407 std::vector<token> toks;
408 const char* ptr_base = 0;
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])) {
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]);
427 if (t0.empty() || t2.empty()) {
428 if (t0.empty() && t2.empty())
431 error(std::string(
"error in list [") + s0 +
";" + s1 +
";" + s2 +
"]\n 1st and 3rd expression split into different number of terms", commands[i]);
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]);
442 toks.push_back(
token(ptr_base + n0, ptr_base + n2));
444 }
while (!(t0.empty() && t2.empty()));
450 if (toks.size() > 1) {
451 n = (int)((toks[1].end - ptr_base) - (toks[1].begin - ptr_base));
455 for (j = 3; j < toks.size(); j += 2) {
456 int nn = (int)((toks[j].end - ptr_base) - (toks[j].begin - ptr_base));
459 error(std::string(
"error in list [") + s0 +
";" + s1 +
";" + s2 +
"]\n several number ranges of different size not allowed", commands[i]);
463 for (
int k = 0; k < n; ++k) {
466 for (j = 0; j < toks.size(); j += 2) {
468 if (j + 1 < toks.size())
469 (*os) << (int)(toks[j + 1].begin - ptr_base) + k;
475 void ph_processor::ensure_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());
489 reflect_info->ref_element(
"includes") = variant(variant::list_type());
493 variant ph_processor::construct_namespace_list()
const
495 variant list = variant(variant::list_type());
496 if (!compound_stack.empty())
498 for (
unsigned int i = 0; i < namespace_stack.size(); ++i)
499 list.append_to_list(namespace_stack[i]);
503 std::string ph_processor::construct_namespace_sequence()
const
506 if (!compound_stack.empty())
508 for (
unsigned int i = 0; i < namespace_stack.size(); ++i)
509 seq += namespace_stack[i] +
"::";
514 bool ph_processor::reflect(
const token& code,
const token& comment)
519 std::vector<token> toks;
520 t.skip_whitespaces();
521 t.reverse_skip_whitespaces();
526 error(
"no code to be reflected here", code);
534 if (rp_stack.empty()) {
535 error(
"reflect end of something without starting something", code);
540 if (rp_stack.back() == RP_NAMESPACE) {
541 if (namespace_stack.empty()) {
542 error(
"reflect end of namespace without a namespace started", code);
545 if (debug_reflection)
546 std::cout <<
"reflect end of namespace " << namespace_stack.back().c_str() << std::endl;
547 namespace_stack.pop_back();
551 if (compound_stack.empty()) {
552 error(
"reflect end of compound without a compound started", code);
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();
564 std::string new_access_type;
566 new_access_type =
"private";
568 new_access_type =
"protected";
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]);
576 if (compound_access_type_stack.empty()) {
577 error(
"compound_access_type_stack empty", toks[0]);
580 compound_access_type_stack.back() = new_access_type;
585 if (toks.size() < 2) {
586 error(
"code splits only into one token and cannot be reflected", code);
592 if (toks.size() < 3) {
593 error(
"typedef with only two tokens cannot be reflected", code);
596 token name_tok = toks.back();
597 token type_tok(toks[1].begin, toks[toks.size() - 2].end);
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));
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);
615 reflect_info->ref_element(
"elements").append_to_list(type_def);
617 if (reflect_version_1) {
618 if (compound_stack.size() > 0)
619 compound_stack.back()->ref_element(
"typedefs").append_to_list(type_def);
621 reflect_info->ref_element(
"typedefs").append_to_list(type_def);
624 if (debug_reflection)
625 std::cout <<
"reflect typedef " << type_def << std::endl;
628 else if (
to_string(toks[0]) ==
"#include") {
630 if (toks.size() < 2) {
631 error(
"cannot reflect include without file name", code);
635 ensure_reflect_info();
636 reflect_info->ref_element(
"includes").append_to_list(variant(
to_string(toks[1])));
638 if (debug_reflection)
639 std::cout <<
"reflect include " <<
to_string(toks[1]).c_str() << std::endl;
644 variant enum_entries = variant(variant::list_type());
646 bool contains_cgv_api =
false;
648 if (
to_string(toks[1]) ==
"/*CGV_API*/") {
649 contains_cgv_api =
true;
653 token name_tok = toks[idx];
658 bool define_name =
false;
659 while (!t.skip_ws_check_empty()) {
660 token tok = t.bite();
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);
685 error(
"enum value expected", tok);
688 enum_entries.ref_element(enum_entries.get_size() - 1).ref_element(1).set_int(w);
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);
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);
710 reflect_info->ref_element(
"enums").append_to_list(enum_info);
712 if (reflect_version_2) {
713 if (compound_stack.size() > 0)
714 compound_stack.back()->ref_element(
"elements").append_to_list(enum_info);
716 reflect_info->ref_element(
"elements").append_to_list(enum_info);
719 if (debug_reflection)
720 std::cout <<
"reflect enum " << enum_info << std::endl;
725 variant base_list = variant(variant::list_type());
727 bool contains_cgv_api =
false;
730 contains_cgv_api =
true;
737 tmp.set_sep(
":").set_ws(
"").bite();
741 (!tmp.empty() &&
to_string(tmp.bite()) ==
":")) {
743 base_tok =
token(tmp.bite().begin, toks.back().end);
746 if (!base_tok.empty()) {
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);
755 std::string access_type =
"public";
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;
763 if (toks.size() == 0) {
764 error(
"found invalid base definition", base_toks[i]);
768 for (
unsigned int k = 0; k < 2; ++k, ++j) {
769 if (toks.size() > j) {
772 else if (
to_string(toks[j]) ==
"private")
773 access_type =
"private";
774 else if (
to_string(toks[j]) ==
"protected")
775 access_type =
"protected";
777 access_type =
"public";
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);
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();
805 if (reflect_version_2) {
806 compound.ref_element(
"name").set_str(
to_string(name_tok));
807 compound.ref_element(
"elements").set_list();
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);
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));
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));
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));
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));
837 if (debug_reflection)
838 std::cout <<
"reflect compound " << compound << std::endl;
841 compound_access_type_stack.push_back(
"public");
843 compound_access_type_stack.back() =
"private";
844 rp_stack.push_back(RP_COMPOUND);
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);
862 error(
"method reflection not supported yet", toks.back());
866 error(
"array reflection not supported yet", toks.back());
872 bool is_static =
false;
877 token type_tok(toks[idx].begin, toks[toks.size() - 2].end);
878 token name_tok = toks.back();
880 if (reflect_version_1) {
881 if (!compound_stack.empty()) {
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));
891 compound_stack.back()->ref_element(
"members").append_to_list(member_info);
894 if (debug_reflection)
895 std::cout <<
"reflect member " << member_info << std::endl;
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));
907 ensure_reflect_info();
908 reflect_info->ref_element(
"variables").append_to_list(variable_info);
911 if (debug_reflection)
912 std::cout <<
"reflect variable " << variable_info << std::endl;
915 if (reflect_version_2) {
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();
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);
933 ensure_reflect_info();
934 reflect_info->ref_element(
"elements").append_to_list(variable_info);
937 if (debug_reflection)
938 std::cout <<
"reflect variable " << variable_info << std::endl;
942 error(
"could not handle reflection", code);
947 using namespace cgv::ppp;
948 using namespace cgv::utils::file;
950 void remove_comments(
string& text)
954 for (i = 0; i < text.length(); ++i) {
955 if (text[i] ==
'/') {
956 if (i + 1 < text.length()) {
957 if (text[i + 1] ==
'/') {
959 while (i < text.length()) {
960 if (text[i] ==
'\n') {
966 else if (text[i + 1] ==
'*') {
968 while (i < text.length()) {
969 if (text[i - 1] ==
'*' && text[i] ==
'/') {
978 if (i < text.length())
984 void remove_strings(
string& text)
988 for (i = 0; i < text.length(); ++i) {
989 bool was_string =
false;
990 if (text[i] ==
'"') {
992 while (i < text.length()) {
995 else if (text[i] ==
'"') {
1003 new_text += text[i];
1008 void filter_out_preprocessor(
string& text)
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')
1019 if (!new_text.empty())
1021 new_text += string(toks[i].begin, p - toks[i].begin);
1022 while (i + 1 < toks.size() && toks[i + 1].begin < p)
1029 void filter_out_local_includes(
string& text)
1035 for (i = 0; i < lines.size(); ++i) {
1037 size_t n = l.length();
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)
1051 if (!new_text.empty())
1070 void filter_out_empty_blocks(
string& text)
1075 vector<LineType> line_types;
1076 line_types.resize(lines.size());
1078 for (i = 0; i < lines.size(); ++i) {
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;
1094 line_types[i] = LT_OTHER;
1099 while (i < line_types.size()) {
1100 switch (line_types[i]) {
1120 if (line_types[j] != LT_OTHER) {
1121 if (!new_text.empty())
1138 bool scan_includes(
const std::string& fn, std::string& s)
1140 std::string content;
1141 if (!cgv::utils::file::read(fn, content,
true))
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);
1149 if (!content.empty()) {
1160 bool ph_processor::process(
unsigned int i,
unsigned int j)
1163 switch (commands[i].ct) {
1165 case CT_IMPLICIT_TEXT:
1166 if (generate_output) {
1167 (*os) <<
to_string(commands[i]).c_str();
1174 commands[i].expressions[0].evaluate(v,
this);
1177 if (!check_evaluation_error(i, 0))
1183 variant file_name, str, ascii;
1184 commands[i].expressions[0].evaluate(file_name,
this);
1187 if (!check_evaluation_error(i, 0))
1189 commands[i].expressions[2].evaluate(ascii,
this);
1192 if (!check_evaluation_error(i, 2))
1194 if (!cgv::utils::file::exists(file_name.
get_str())) {
1195 error(std::string(
"could not find file '") + file_name.
get_str() +
"'", commands[i]);
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]);
1203 commands[i].expressions[1].evaluate(str,
this);
1206 if (!check_evaluation_error(i, 1))
1209 error(std::string(
"file '") + file_name.
get_str() +
"' can only be read into a variable given by name or string ", commands[i]);
1217 variant file_name, str, ascii;
1218 commands[i].expressions[0].evaluate(file_name,
this);
1221 if (!check_evaluation_error(i, 0))
1223 commands[i].expressions[1].evaluate(str,
this);
1226 if (!check_evaluation_error(i, 1))
1228 commands[i].expressions[2].evaluate(ascii,
this);
1231 if (!check_evaluation_error(i, 2))
1234 error(std::string(
"file '") + file_name.
get_str() +
"' can only be read into a variable given by name or string ", commands[i]);
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]);
1246 variant minval, maxval, str, seed;
1247 commands[i].expressions[0].evaluate(minval,
this);
1250 if (!check_evaluation_error(i, 0))
1252 commands[i].expressions[1].evaluate(maxval,
this);
1255 if (!check_evaluation_error(i, 1))
1257 commands[i].expressions[2].evaluate(str,
this);
1260 if (!check_evaluation_error(i, 2))
1263 error(std::string(
"rand can only be generated into a variable given by name or string"), commands[i]);
1266 if (commands[i].expressions.size() == 4) {
1267 commands[i].expressions[3].evaluate(seed,
this);
1270 if (!check_evaluation_error(i, 3))
1273 static std::default_random_engine R;
1274 if (commands[i].expressions.size() == 4) {
1283 std::uniform_int_distribution<int> D(minval.
get_int(), maxval.
get_int());
1293 commands[i].expressions[0].evaluate(v,
this);
1296 if (!check_evaluation_error(i, 0))
1298 std::string var_name;
1304 error(
"namespace argument must evaluate to name or string", commands[i]);
1307 variant* ns = find_variable(var_name,
true);
1309 ns = &ref_variable(var_name,
true);
1310 *ns =
variant(variant::map_type());
1313 error(
"namespace cannot overwrite previously defined variable", commands[i]);
1317 if (!process(i + 1, commands[i].block_end))
1320 i = commands[i].block_end;
1327 commands[i].expressions[0].evaluate(v,
this);
1330 if (!check_evaluation_error(i, 0))
1333 commands[i].expressions[1].evaluate(v,
this);
1336 if (!check_evaluation_error(i, 1))
1340 if (!process(i + 1, commands[i].block_end))
1342 commands[i].expressions[2].evaluate(v,
this);
1345 if (!check_evaluation_error(i, 2))
1348 i = commands[i].block_end;
1355 commands[i].expressions[0].evaluate(v,
this);
1358 if (!check_evaluation_error(i, 0))
1361 if (!process(i + 1, commands[i].block_end))
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;
1370 i = commands[i].block_end;
1371 bool success =
false;
1372 while (i < j && commands[i].ct == CT_ELIF) {
1374 commands[i].expressions[0].evaluate(v,
this);
1377 if (!check_evaluation_error(i, 0))
1381 if (!process(i + 1, commands[i].block_end))
1385 i = commands[i].block_end;
1387 if (i < j && commands[i].ct == CT_ELSE) {
1389 if (!process(i + 1, commands[i].block_end))
1392 i = commands[i].block_end;
1402 commands[i].expressions[0].evaluate(v,
this);
1405 if (!check_evaluation_error(i, 0))
1407 if (generate_output) {
1408 if (commands[i].ct == CT_STRING)
1409 (*os) <<
"\"" << v.
get_str().c_str() <<
"\"";
1416 if (!process_list(i))
1423 if (!process_include(i))
1429 if (!process_include(i,
true))
1435 if (!process_include(i,
false,
true))
1441 error(
"elif without if", commands[i]);
1445 error(
"else without if", commands[i]);
1448 case CT_REFLECT_NEXT_LINE:
1452 error(
"next line reflection command without a line to follow", commands[i]);
1455 token comment_tok = t.set_sep(
"\n").set_ws(
"").bite();
1457 comment_tok =
token();
1460 error(
"next line reflection command without a line to follow", commands[i]);
1466 error(
"next line reflection command without a line to follow", commands[i]);
1470 if (!reflect(t.bite(), comment_tok))
1474 case CT_REFLECT_PREV_LINE:
1479 comment_tok = t.set_sep(
"\n").set_ws(
"").bite();
1481 comment_tok =
token();
1483 t =
tokenizer(
token(&(*content->begin()), commands[i].begin - 2));
1484 t.set_sep(
"\n").set_ws(
"");
1486 error(
"previous line reflection command without a line to preceed", commands[i]);
1489 if (!reflect(t.reverse_bite(), comment_tok))
1496 commands[i].expressions[0].evaluate(v,
this);
1499 if (!check_evaluation_error(i, 0))
1508 commands[i].expressions[0].evaluate(v,
this);
1511 if (!check_evaluation_error(i, 0))
1514 error(
"cin target expression must evaluate to reference", commands[i]);
1518 std::cin.getline(buffer, 1000);
1519 std::string s(buffer);
1531 commands[i].expressions[0].evaluate(v1,
this);
1534 if (!check_evaluation_error(i, 0))
1536 commands[i].expressions[1].evaluate(v2,
this);
1539 if (!check_evaluation_error(i, 1))
1541 std::cout << file_name.c_str() <<
"(" << get_line_number(commands[i]) <<
") : ";
1542 if (commands[i].ct == CT_ERROR)
1543 std::cout <<
"error ";
1545 std::cout <<
"warning ";
1546 std::cout << v1.
get_int() <<
": " << v2.
get_str().c_str() << std::endl;
1552 commands[i].expressions[0].evaluate(v1,
this);
1555 if (!check_evaluation_error(i, 0))
1558 commands[i].expressions[1].evaluate(v2,
this);
1561 if (!check_evaluation_error(i, 1))
1564 error(
"system result expression must evaluate to reference", commands[i]);
1573 commands[i].expressions[0].evaluate(v1,
this);
1576 check_evaluation_error(i, 0);
1583 commands[i].expressions[0].evaluate(v1,
this);
1586 if (!check_evaluation_error(i, 0))
1589 error(
"first parameter of func() has to be a name, string or reference", commands[i]);
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());
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;
1607 commands[i].expressions[0].evaluate(v1,
this);
1610 if (!check_evaluation_error(i, 0))
1613 error(
"first parameter of dir() has to be a directory as string", commands[i]);
1617 commands[i].expressions[1].evaluate(v2,
this);
1620 if (!check_evaluation_error(i, 0))
1623 error(
"second parameter of dir() has to be a filter string", commands[i]);
1627 commands[i].expressions[2].evaluate(v3,
this);
1630 if (!check_evaluation_error(i, 0))
1633 error(
"third parameter of dir() has to be a int", commands[i]);
1637 commands[i].expressions[3].evaluate(v4,
this);
1640 if (!check_evaluation_error(i, 0))
1643 error(
"fourth parameter of dir() has to be a list", commands[i]);
1647 std::string directory = v1.
get_str();
1652 if(!cgv::utils::dir::exists(directory)) {
1653 error(
"path \"" + directory +
"\" does not exist", commands[i]);
1656 std::string filter = v2.
get_str();
1657 bool recursive = v3.
get_int() > 0;
1660 if (!(directory[directory.size() - 1] ==
'/'))
1661 directory = directory +
"/";
1663 std::string actual_directory =
"";
1665 bool rel_path =
true;
1666 if (actual_directory == directory)
1669 bool success = recursive_dir(directory, actual_directory, filter, rel_path, v4, recursive);
1670 if (!success)
return false;
1677 commands[i].expressions[0].evaluate(v1,
this);
1680 if (!check_evaluation_error(i, 0))
1683 error(
"first parameter of transform() must evaluate to a file name as string", commands[i]);
1687 commands[i].expressions[1].evaluate(v2,
this);
1690 if (!check_evaluation_error(i, 0))
1693 error(
"second parameter of transform() must evaluate to a file name as string", commands[i]);
1696 std::string inp = find_include_file(v1.
get_str(),
true);
1698 error(
"could not find input file to transform() ", commands[i]);
1702 bool success =
false;
1703 if ((success = fp->parse_file(inp))) {
1705 success = fp->process_to_file(v2.
get_str(), cgv::utils::file::get_last_write_time(inp)) != 0;
1707 if (fp->exit_code != 0)
1708 exit_code = fp->exit_code;
1714 case CT_SCAN_INCLUDES:
1717 commands[i].expressions[0].evaluate(v1,
this);
1720 if (!check_evaluation_error(i, 0))
1723 error(
"first parameter of scan_includes() must evaluate to a file name as string", commands[i]);
1727 commands[i].expressions[1].evaluate(v2,
this);
1730 if (!check_evaluation_error(i, 0))
1733 error(
"second parameter of scan_includes() must evaluate to a variable name", commands[i]);
1738 error(
"second parameter of scan_includes() must evaluate to the name of variable of STRING type", commands[i]);
1741 std::string inp = find_include_file(v1.
get_str(),
true);
1743 error(
"could not find input file to scan_includes() ", commands[i]);
1746 scan_includes(inp, v_str.ref_str());
1750 error(std::string(
"token ") +
1751 commands[i].get_keyword() +
1752 " not expected", commands[i]);
1761 bool ph_processor::recursive_dir(
1762 std::string rel_directory,
1763 std::string actual_directory,
1769 bool skip_files =
false;
1770 bool list_dirs =
false;
1772 if (filter ==
".") {
1774 fh = cgv::utils::file::find_first(rel_directory +
"*");
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 +
"*");
1790 not_finished =
false;
1792 std::string fn = cgv::utils::file::find_name(fh);
1793 if (fn ==
"." || fn ==
".." || fn ==
".svn")
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;
1799 file_desc.push_back(
new variant(fn));
1801 file_desc.push_back(
new variant(rel_directory + fn));
1804 file_desc.push_back(
new variant(actual_directory + fn));
1806 file_desc.push_back(
new variant(rel_directory + fn));
1808 file_desc.push_back(
new variant(is_dir));
1810 file_desc.push_back(
new variant((
int)cgv::utils::file::find_size(fh)));
1812 std::vector<variant> sub_dirs;
1813 std::vector<token> toks;
1815 for (
unsigned int i = 0; i < toks.size(); ++i)
1817 file_desc.push_back(
new variant(sub_dirs));
1819 desc_list.append_to_list(
new variant(file_desc));
1822 if (recursive&&is_dir) {
1823 if (!recursive_dir(rel_directory + fn +
"/",
1824 actual_directory + fn +
"/",
1826 rel_path, desc_list, recursive))
1829 }
while ((fh = cgv::utils::file::find_next(fh)) != 0);
1830 if (!skip_files && recursive) {
1831 fh = cgv::utils::file::find_first(rel_directory +
"*");
1834 not_finished =
true;
1836 }
while (not_finished);
1840 bool ph_processor::parse()
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)))
1848 special = content->at(2);
1853 return parse_without_special(nr_skip);
1855 found_error =
false;
1856 bool block_begin_follows =
false;
1860 std::stack<unsigned int> block_stack;
1864 toker.begin += nr_skip;
1865 toker.set_sep(std::string(1, special)).set_skip(
"'\"",
"'\"").set_ws(
"");
1866 while (!toker.empty()) {
1870 token tok = toker.bite();
1873 if (*tok.begin != special)
1876 else if (!toker.empty() && *toker.begin ==
'/') {
1878 while (!toker.empty()) {
1880 if (toker.begin[-1] ==
'\n')
1888 if (toker.begin < toker.end && *toker.begin == special) {
1893 error(ct.get_last_error(), ct.get_last_error_token());
1899 commands.back().is_empty())
1900 commands.pop_back();
1902 if (block_begin_follows) {
1903 if (ct.
ct == CT_BEGIN) {
1904 block_begin_follows =
false;
1909 error(std::string(
"expected ") + special +
'{', ct);
1910 block_begin_follows =
false;
1913 if (ct.
ct == CT_END) {
1914 if (block_stack.empty()) {
1915 error(std::string() + special +
" unexpected", ct);
1918 commands[block_stack.top()].block_end = (
unsigned int)commands.size();
1924 if (block_begin_follows)
1925 block_stack.push((
unsigned int)commands.size());
1926 commands.push_back(ct);
1928 if (!block_stack.empty()) {
1929 error(
"block begin not matched", commands[block_stack.top()]);
1932 return !found_error;
1935 bool ph_processor::parse_without_special(
unsigned nr_skip)
1937 found_error =
false;
1938 bool block_begin_follows =
false;
1943 std::stack<unsigned int> block_stack;
1955 if (*T.begin ==
'/') {
1957 while (!T.empty()) {
1959 if (T.begin[-1] ==
'\n')
1966 const char* old_begin = T.begin;
1968 if (!success && ct.get_last_error() !=
"could not determine command") {
1969 error(ct.get_last_error(), ct.get_last_error_token());
1972 if (success && (ct.
ct == CT_EVAL || ct.
ct == CT_STRING || ct.
ct == CT_LIST)) {
1973 T.begin = old_begin;
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] ==
';')
1985 if (!ep.parse(exp_tok)) {
1986 error(ep.get_last_error(), ep.get_last_error_token());
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());
1997 ct.
begin = exp_tok.begin;
1998 ct.end = exp_tok.end;
2002 error(
"could not extract expression with balanced parantheses", old_T);
2008 if (block_begin_follows) {
2009 if (ct.
ct == CT_BEGIN) {
2010 block_begin_follows =
false;
2015 error(
"expected {", ct);
2016 block_begin_follows =
false;
2019 if (ct.
ct == CT_END) {
2020 if (block_stack.empty()) {
2021 error(
"} unexpected", ct);
2024 commands[block_stack.top()].block_end = (
unsigned int)commands.size();
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()]);
2038 return !found_error;
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.
tokenizer & set_sep(const std::string &sep, bool merge)
set the list of separators and specify whether succeeding separators are merged into single tokens
tokenizer & set_ws(const std::string &ws)
set the list of white spaces, that separate tokens and are skipped
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
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...
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
char to_upper(char c)
convert char to upper case
bool is_space(char c)
check if char is a whitespace
const char * skip_spaces(const char *begin, const char *end)
return new start pointer by skipping spaces at begin
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
const char * begin
pointers that define the range of characters