160 using uint64_t = std::uint64_t;
161 using report_callback = std::function<void(
162 int error_line,
int error_char, std::string error_line_string,
const char *error_str)>;
169 std::vector<SharedVar> shared_vars_;
188 if (filename.find(
".msl") != std::string::npos) {
191 if (filename.find(
".glsl") != std::string::npos) {
194 if (filename.find(
".hh") != std::string::npos) {
203 const std::string &filename,
204 bool do_parse_function,
205 bool do_small_type_linting,
206 report_callback report_error,
210 report_error(0, 0,
"",
"Unknown file type");
213 str = remove_comments(
str, report_error);
214 threadgroup_variables_parsing(
str);
216 str = disabled_code_mutation(
str, report_error);
218 parse_builtins(
str, filename);
220 if (do_parse_function) {
221 parse_library_functions(
str);
224 pragma_runtime_generated_parsing(
str);
225 pragma_once_linting(
str, filename, report_error);
227 str = include_parse_and_remove(
str, report_error);
228 str = pragmas_mutation(
str, report_error);
229 str = swizzle_function_mutation(
str, report_error);
230 str = enum_macro_injection(
str, language ==
CPP, report_error);
233 using_mutation(
parser, report_error);
235 namespace_mutation(
parser, report_error);
236 template_struct_mutation(
parser, report_error);
237 struct_method_mutation(
parser, report_error);
238 empty_struct_mutation(
parser, report_error);
239 method_call_mutation(
parser, report_error);
240 stage_function_mutation(
parser, report_error);
241 resource_guard_mutation(
parser, report_error);
242 loop_unroll(
parser, report_error);
243 assert_processing(
parser, filename, report_error);
244 static_strings_merging(
parser, report_error);
245 static_strings_parsing_and_mutation(
parser, report_error);
247 str = printf_processing(
str, report_error);
248 quote_linting(
str, report_error);
252 global_scope_constant_linting(
parser, report_error);
253 if (do_small_type_linting) {
254 small_type_linting(
parser, report_error);
256 remove_quotes(
parser, report_error);
257 argument_reference_mutation(
parser, report_error);
258 default_argument_mutation(
parser, report_error);
261 str = variable_reference_mutation(
str, report_error);
262 str = template_definition_mutation(
str, report_error);
264 str = namespace_separator_mutation(
str);
266 str = template_call_mutation(
str, report_error);
268 else if (language ==
MSL) {
269 pragma_runtime_generated_parsing(
str);
270 str = include_parse_and_remove(
str, report_error);
271 str = pragmas_mutation(
str, report_error);
274 if (language ==
GLSL) {
275 str = matrix_constructor_mutation(
str);
278 str = argument_decorator_macro_injection(
str);
279 str = array_constructor_macro_injection(
str);
280 r_metadata = metadata;
281 return line_directive_prefix(filename) +
str + threadgroup_variables_suffix();
287 auto no_err_report = [](int, int, std::string,
const char *) {};
289 return process(
GLSL,
str,
"",
false,
false, no_err_report, unused);
293 using regex_callback = std::function<void(
const std::smatch &)>;
294 using regex_callback_with_line_count = std::function<void(
const std::smatch &,
int64_t)>;
297 void regex_global_search(
const std::string &
str,
298 const std::regex ®ex,
299 regex_callback callback)
302 string::const_iterator it =
str.begin();
303 for (smatch match; regex_search(it,
str.end(), match, regex); it = match.suffix().first) {
308 void regex_global_search(
const std::string &
str,
309 const std::regex ®ex,
310 regex_callback_with_line_count callback)
314 regex_global_search(
str, regex, [&line, &callback](
const std::smatch &match) {
316 callback(match, line);
321 template<
typename ReportErrorF>
322 std::string remove_comments(
const std::string &
str,
const ReportErrorF &report_error)
324 std::string out_str =
str;
327 size_t start, end = 0;
328 while ((start = out_str.find(
"/*", end)) != std::string::npos) {
329 end = out_str.find(
"*/", start + 2);
330 if (end == std::string::npos) {
333 for (
size_t i = start;
i < end + 2; ++
i) {
334 if (out_str[
i] !=
'\n') {
340 if (end == std::string::npos) {
344 "Malformed multi-line comment.");
350 size_t start, end = 0;
351 while ((start = out_str.find(
"//", end)) != std::string::npos) {
352 end = out_str.find(
'\n', start + 2);
353 if (end == std::string::npos) {
356 for (
size_t i = start;
i < end; ++
i) {
361 if (end == std::string::npos) {
365 "Malformed single line comment, missing newline.");
370 std::regex regex(R
"((\ )*?\n)");
371 return std::regex_replace(out_str, regex,
"\n");
374 static std::string template_arguments_mangle(
const shader::parser::Scope template_args)
377 using namespace shader::parser;
379 template_args.foreach_scope(ScopeType::TemplateArg, [&](
const Scope &scope) {
380 args_concat +=
'T' + scope.start().str();
385 void template_struct_mutation(Parser &parser, report_callback &report_error)
388 using namespace shader::parser;
390 parser.foreach_match(
"w<..>(..)", [&](
const vector<Token> &tokens) {
391 const Scope template_args = tokens[1].scope();
392 template_args.foreach_match(
"w<..>", [&parser](
const vector<Token> &tokens) {
393 parser.replace(tokens[1].scope(), template_arguments_mangle(tokens[1].scope()),
true);
396 parser.apply_mutations();
399 parser.foreach_match(
"t<>sw<..>", [&](
const std::vector<Token> &tokens) {
400 parser.erase(tokens[0], tokens[2]);
401 parser.replace(tokens[5].scope(), template_arguments_mangle(tokens[5].scope()),
true);
403 parser.apply_mutations();
406 parser.foreach_scope(ScopeType::Template, [&](Scope temp) {
408 Token struct_start = temp.end().next();
409 if (struct_start != Struct) {
412 Token struct_name = struct_start.next();
413 Scope struct_body = struct_name.next().scope();
416 temp.foreach_match(
"=", [&](
const std::vector<Token> &tokens) {
418 "Default arguments are not supported inside template declaration");
426 vector<string> arg_list;
427 temp.foreach_scope(ScopeType::TemplateArg, [&](Scope arg) {
428 const Token type = arg.start();
429 const Token
name = type.next();
430 const string name_str =
name.str();
431 const string type_str = type.str();
433 arg_list.emplace_back(name_str);
435 if (type_str ==
"typename") {
438 else if (type_str ==
"enum" || type_str ==
"bool") {
441 else if (type_str ==
"int" || type_str ==
"uint") {
445 report_error(
ERROR_TOK(type),
"Invalid template argument type");
449 Token struct_end = struct_body.end();
450 const string fn_decl = parser.substr_range_inclusive(struct_start.str_index_start(),
451 struct_end.str_index_last());
454 Token template_keyword = temp.start().prev();
455 parser.erase(template_keyword.str_index_start(), struct_end.line_end());
458 Scope parent_scope = temp.scope();
459 string specialization_pattern =
"tsw<" + arg_pattern.substr(1) +
">";
460 parent_scope.foreach_match(specialization_pattern, [&](
const std::vector<Token> &tokens) {
461 if (struct_name.str() != tokens[2].str()) {
465 vector<pair<string, string>> arg_name_value_pairs;
466 for (
int i = 0;
i < arg_list.size();
i++) {
467 arg_name_value_pairs.emplace_back(arg_list[
i], tokens[4 + 2 *
i].
str());
470 Parser instance_parser(fn_decl, report_error,
true);
471 instance_parser.foreach_match(
"w", [&](
const std::vector<Token> &tokens) {
472 string token_str = tokens[0].str();
473 for (
const auto &arg_name_value : arg_name_value_pairs) {
474 if (token_str == arg_name_value.first) {
475 instance_parser.replace(tokens[0], arg_name_value.second);
480 const string template_args = parser.substr_range_inclusive(
481 tokens[3], tokens[3 + arg_pattern.size()]);
482 size_t pos = fn_decl.find(
" " + struct_name.str());
483 instance_parser.insert_after(
pos + struct_name.str().size(), template_args);
485 Token end_of_instantiation = tokens.back();
486 string instance = instance_parser.result_get();
487 parser.insert_line_number(tokens.front().str_index_start() - 1,
488 struct_start.line_number());
489 parser.replace(tokens.front().str_index_start(),
490 end_of_instantiation.str_index_last_no_whitespace(),
492 parser.insert_line_number(end_of_instantiation.line_end() + 1,
493 end_of_instantiation.line_number() + 1);
496 parser.apply_mutations();
501 parser.foreach_match(
"sw<..>", [&](
const std::vector<Token> &tokens) {
502 parser.replace(tokens[2].scope(), template_arguments_mangle(tokens[2].scope()),
true);
504 parser.apply_mutations();
508 std::string template_definition_mutation(
const std::string &
str, report_callback &report_error)
510 if (
str.find(
"template") == std::string::npos) {
515 using namespace shader::parser;
517 std::string out_str =
str;
519 Parser parser(out_str, report_error);
521 auto process_specialization = [&](
const Token specialization_start,
522 const Scope template_args) {
523 parser.erase(specialization_start, specialization_start.next().next());
524 parser.replace(template_args, template_arguments_mangle(template_args),
true);
527 parser.foreach_scope(ScopeType::Global, [&](Scope scope) {
529 scope.foreach_match(
"t<>ww<", [&](
const std::vector<Token> &tokens) {
530 process_specialization(tokens[0], tokens[5].scope());
532 scope.foreach_match(
"t<>ww::w<", [&](
const std::vector<Token> &tokens) {
533 process_specialization(tokens[0], tokens[8].scope());
537 parser.apply_mutations();
539 auto process_template = [&](
const Token fn_start,
540 const string &fn_name,
543 const Token fn_end) {
545 temp.foreach_match(
"=", [&](
const std::vector<Token> &tokens) {
549 "Default arguments are not supported inside template declaration");
557 vector<string> arg_list;
558 bool all_template_args_in_function_signature =
true;
559 temp.foreach_scope(ScopeType::TemplateArg, [&](Scope arg) {
560 const Token type = arg.start();
561 const Token
name = type.next();
562 const string name_str =
name.str();
563 const string type_str = type.str();
565 arg_list.emplace_back(name_str);
567 if (type_str ==
"typename") {
572 fn_args.foreach_match(
"ww", [&](
const std::vector<Token> &tokens) {
573 if (tokens[0].
str() == name_str) {
577 all_template_args_in_function_signature &= found;
579 else if (type_str ==
"enum" || type_str ==
"bool") {
582 all_template_args_in_function_signature =
false;
584 else if (type_str ==
"int" || type_str ==
"uint") {
587 all_template_args_in_function_signature =
false;
590 report_error(
ERROR_TOK(type),
"Invalid template argument type");
594 const string fn_decl = parser.substr_range_inclusive(fn_start.str_index_start(),
598 Token template_keyword = temp.start().prev();
599 parser.erase(template_keyword.str_index_start(), fn_end.line_end());
601 auto process_instantiation = [&](
const string &inst_name,
602 const Token inst_start,
603 const Token inst_end,
604 const Scope &inst_args) {
605 if (fn_name != inst_name) {
609 vector<pair<string, string>> arg_name_value_pairs;
610 for (
int i = 0;
i < arg_list.size();
i++) {
611 arg_name_value_pairs.emplace_back(arg_list[
i], inst_args[1 + 2 *
i].
str());
614 Parser instance_parser(fn_decl, report_error,
true);
615 instance_parser.foreach_token(Word, [&](
const Token &word) {
616 string token_str = word.str();
617 for (
const auto &arg_name_value : arg_name_value_pairs) {
618 if (token_str == arg_name_value.first) {
619 instance_parser.replace(word, arg_name_value.second);
624 if (!all_template_args_in_function_signature) {
627 size_t pos = fn_decl.find(
" " + fn_name);
628 instance_parser.insert_after(
pos + fn_name.size(), inst_args.str());
631 string instance = instance_parser.result_get();
632 parser.insert_line_number(inst_start.str_index_start() - 1, fn_start.line_number());
634 inst_start.str_index_start(), inst_end.str_index_last_no_whitespace(), instance);
635 parser.insert_line_number(inst_end.line_end() + 1, inst_end.line_number() + 1);
639 Scope parent_scope = temp.scope();
641 string specialization_pattern =
"tww<" + arg_pattern.substr(1) +
">(..);";
642 parent_scope.foreach_match(specialization_pattern, [&](
const vector<Token> &tokens) {
643 process_instantiation(tokens[2].
str(), tokens.front(), tokens.back(), tokens[3].scope());
647 string specialization_pattern =
"tww::w<" + arg_pattern.substr(1) +
">(..);";
648 parent_scope.foreach_match(specialization_pattern, [&](
const vector<Token> &tokens) {
649 const string inst_name = parser.substr_range_inclusive(tokens[2], tokens[5]);
650 process_instantiation(inst_name, tokens.front(), tokens.back(), tokens[6].scope());
655 parser.foreach_match(
"t<..>ww(..)c?{..}", [&](
const vector<Token> &tokens) {
657 tokens[5], tokens[6].
str(), tokens[7].scope(), tokens[1].scope(), tokens[16]);
660 parser.foreach_match(
"t<..>ww::w(..)c?{..}", [&](
const vector<Token> &tokens) {
661 const string fn_name = parser.substr_range_inclusive(tokens[6], tokens[9]);
662 process_template(tokens[5], fn_name, tokens[10].scope(), tokens[1].scope(), tokens[19]);
665 out_str = parser.result_get();
670 if ((error_pos = out_str.find(
"template<")) != std::string::npos) {
674 "Template declaration unsupported syntax");
676 if ((error_pos = out_str.find(
"template ")) != std::string::npos) {
680 "Template instantiation unsupported syntax");
686 std::string template_call_mutation(
const std::string &
str, report_callback &report_error)
689 using namespace shader::parser;
691 Parser parser(
str, report_error);
692 parser.foreach_match(
"w<..>", [&](
const std::vector<Token> &tokens) {
693 parser.replace(tokens[1].scope(), template_arguments_mangle(tokens[1].scope()),
true);
695 return parser.result_get();
699 void remove_quotes(Parser &parser, report_callback )
702 using namespace shader::parser;
704 parser.foreach_token(TokenType::String, [&](
const Token token) { parser.erase(token); });
705 parser.apply_mutations();
708 std::string include_parse_and_remove(
const std::string &
str, report_callback report_error)
711 using namespace shader::parser;
713 Parser parser(
str, report_error);
715 parser.foreach_match(
"#w_", [&](
const std::vector<Token> &tokens) {
716 if (tokens[1].
str() !=
"include") {
719 string dependency_name = tokens[2].str_exclusive();
721 if (dependency_name ==
"gpu_shader_compat.hh") {
723 parser.erase(tokens.front(), tokens.back());
726 if (dependency_name.find(
"infos.hh") != std::string::npos) {
728 parser.erase(tokens.front(), tokens.back());
731 if (dependency_name.find(
"gpu_shader_create_info.hh") != std::string::npos) {
733 parser.erase(tokens.front(), tokens.back());
736 metadata.dependencies.emplace_back(dependency_name);
737 parser.erase(tokens.front(), tokens.back());
740 return parser.result_get();
743 void pragma_runtime_generated_parsing(
const std::string &
str)
745 if (
str.find(
"\n#pragma runtime_generated") != std::string::npos) {
750 void pragma_once_linting(
const std::string &
str,
751 const std::string &filename,
752 report_callback report_error)
754 if (filename.find(
"_lib.") == std::string::npos && filename.find(
".hh") == std::string::npos) {
757 if (
str.find(
"\n#pragma once") == std::string::npos) {
758 report_error(0, 0,
"",
"Header files must contain #pragma once directive.");
762 void loop_unroll(Parser &parser, report_callback report_error)
765 using namespace shader::parser;
767 auto parse_for_args =
768 [&](
const Scope loop_args, Scope &r_init, Scope &r_condition, Scope &r_iter) {
769 r_init = r_condition = r_iter = Scope::invalid();
770 loop_args.foreach_scope(ScopeType::LoopArg, [&](
const Scope arg) {
771 if (arg.start().prev() ==
'(' && arg.end().next() ==
';') {
774 else if (arg.start().prev() ==
';' && arg.end().next() ==
';') {
777 else if (arg.start().prev() ==
';' && arg.end().next() ==
')') {
781 report_error(
ERROR_TOK(arg.start()),
"Invalid loop declaration.");
786 auto process_loop = [&](
const Token loop_start,
787 const int iter_count,
790 const bool condition_is_trivial,
791 const bool iteration_is_trivial,
796 const string body_prefix =
"",
797 const string body_suffix =
"") {
801 body.foreach_token(Continue, [&](
const Token token) {
802 if (token.first_containing_scope_of_type(ScopeType::LoopBody) == body) {
803 report_error(
ERROR_TOK(token),
"Unrolled loop cannot contain \"continue\" statement.");
808 body.foreach_token(Break, [&](
const Token token) {
809 if (token.first_containing_scope_of_type(ScopeType::LoopBody) == body) {
810 const Scope switch_scope = token.first_containing_scope_of_type(ScopeType::SwitchBody);
811 if (switch_scope.is_invalid() || !body.contains(switch_scope)) {
812 report_error(
ERROR_TOK(token),
"Unrolled loop cannot contain \"break\" statement.");
821 if (!parser.replace_try(loop_start, body.end(),
"",
true)) {
826 string indent_init, indent_cond, indent_iter;
827 if (
init.is_valid()) {
828 indent_init = string(
init.start().char_number() - 1,
' ');
830 if (cond.is_valid()) {
831 indent_cond = string(cond.start().char_number() - 3,
' ');
833 if (iter.is_valid()) {
834 indent_iter = string(iter.start().char_number(),
' ');
836 string indent_body = string(body.start().char_number(),
' ');
837 string indent_end = string(body.end().char_number(),
' ');
840 auto replace_index = [&](
const string &
str,
int loop_index) {
841 if (iter.is_invalid() || !iteration_is_trivial ||
str.empty()) {
844 Parser str_parser(
str, report_error);
845 str_parser.foreach_token(Word, [&](
const Token tok) {
846 if (tok.str() == iter[0].str()) {
847 str_parser.replace(tok, std::to_string(loop_index),
true);
850 return str_parser.result_get();
853 parser.insert_after(body.end(),
"\n");
854 if (
init.is_valid() && !iteration_is_trivial) {
855 parser.insert_line_number(body.end(),
init.start().line_number());
856 parser.insert_after(body.end(), indent_init +
"{" +
init.str() +
";\n");
859 parser.insert_after(body.end(),
"{\n");
861 for (
int64_t i = 0, value = iter_init;
i < iter_count;
i++, value += iter_incr) {
862 if (cond.is_valid() && !condition_is_trivial) {
863 parser.insert_line_number(body.end(), cond.start().line_number());
864 parser.insert_after(body.end(), indent_cond +
"if(" + cond.str() +
")\n");
866 parser.insert_after(body.end(), replace_index(body_prefix, value));
867 parser.insert_line_number(body.end(), body.start().line_number());
868 parser.insert_after(body.end(), indent_body + replace_index(body.str(), value) +
"\n");
869 parser.insert_after(body.end(), body_suffix);
870 if (iter.is_valid() && !iteration_is_trivial) {
871 parser.insert_line_number(body.end(), iter.start().line_number());
872 parser.insert_after(body.end(), indent_iter + iter.str() +
";\n");
875 parser.insert_line_number(body.end(), body.end().line_number());
876 parser.insert_after(body.end(), indent_end + body.end().str_with_whitespace());
881 parser.foreach_match(
"[[w::w]]f(..){..}", [&](
const std::vector<Token> tokens) {
882 if (tokens[1].scope().
str() !=
"[gpu::unroll]") {
885 const Token for_tok = tokens[8];
886 const Scope loop_args = tokens[9].scope();
887 const Scope loop_body = tokens[13].scope();
889 Scope
init, cond, iter;
890 parse_for_args(loop_args,
init, cond, iter);
893 const Token var_type =
init[0];
894 const Token var_name =
init[1];
895 const Token var_init =
init[2];
896 if (var_type.str() !=
"int" && var_type.str() !=
"uint") {
897 report_error(
ERROR_TOK(var_init),
"Can only unroll integer based loop.");
900 if (var_init !=
'=') {
901 report_error(
ERROR_TOK(var_init),
"Expecting assignment here.");
904 if (
init[3] !=
'0' &&
init[3] !=
'-') {
905 report_error(
ERROR_TOK(
init[3]),
"Expecting integer literal here.");
910 const Token cond_var = cond[0];
911 const Token cond_type = cond[1];
912 const Token cond_sign = (cond[2] ==
'+' || cond[2] ==
'-') ? cond[2] : Token::invalid();
913 const Token cond_end = cond_sign.is_valid() ? cond[3] : cond[2];
914 if (cond_var.str() != var_name.str()) {
915 report_error(ERROR_TOK(cond_var),
"Non matching loop counter variable.");
918 if (cond_end !=
'0') {
919 report_error(
ERROR_TOK(cond_end),
"Expecting integer literal here.");
924 const Token iter_var = iter[0];
925 const Token iter_type = iter[1];
926 const Token iter_end = iter[1];
928 if (iter_var.str() != var_name.str()) {
929 report_error(ERROR_TOK(iter_var),
"Non matching loop counter variable.");
932 if (iter_type == Increment) {
934 if (cond_type ==
'>') {
935 report_error(
ERROR_TOK(for_tok),
"Unsupported condition in unrolled loop.");
939 else if (iter_type == Decrement) {
941 if (cond_type ==
'<') {
942 report_error(
ERROR_TOK(for_tok),
"Unsupported condition in unrolled loop.");
947 report_error(
ERROR_TOK(iter_type),
"Unsupported loop expression. Expecting ++ or --.");
951 int64_t init_value = std::stol(
952 parser.substr_range_inclusive(var_init.next(), var_init.scope().end()));
954 parser.substr_range_inclusive(cond_sign.is_valid() ? cond_sign : cond_end, cond_end));
956 int iter_count = std::abs(end_value - init_value);
957 if (cond_type == GEqual || cond_type == LEqual) {
961 bool condition_is_trivial = (cond_end == cond.end());
962 bool iteration_is_trivial = (iter_end == iter.end());
964 process_loop(tokens[0],
968 condition_is_trivial,
969 iteration_is_trivial,
977 parser.foreach_match(
"[[w::w(0)]]f(..){..}", [&](
const std::vector<Token> tokens) {
978 if (tokens[5].
str() !=
"unroll") {
981 const Scope loop_args = tokens[12].scope();
982 const Scope loop_body = tokens[16].scope();
984 Scope
init, cond, iter;
985 parse_for_args(loop_args,
init, cond, iter);
987 int iter_count = std::stol(tokens[7].
str());
989 process_loop(tokens[0], iter_count, 0, 0,
false,
false,
init, cond, iter, loop_body);
993 parser.foreach_match(
"[[w::w(0)]]f(..){..}", [&](
const std::vector<Token> tokens) {
994 if (tokens[5].
str() !=
"unroll_define") {
997 const Scope loop_args = tokens[12].scope();
998 const Scope loop_body = tokens[16].scope();
1001 Token define_name = Token::invalid();
1002 Token iter_var = Token::invalid();
1003 loop_args.foreach_match(
"ww=0;w<w;wP", [&](
const std::vector<Token> tokens) {
1004 if (tokens[1].
str() != tokens[5].
str() || tokens[5].
str() != tokens[9].
str()) {
1007 iter_var = tokens[1];
1008 define_name = tokens[7];
1011 if (define_name.is_invalid()) {
1012 report_error(
ERROR_TOK(loop_args.start()),
1013 "Incompatible loop format for [[gpu::unroll_define(max_n)]], expected "
1014 "'(int i = 0; i < DEFINE; i++)'");
1018 Scope
init, cond, iter;
1019 parse_for_args(loop_args,
init, cond, iter);
1021 int iter_count = std::stol(tokens[7].
str());
1023 string body_prefix =
"#if " + define_name.str() +
" > " + iter_var.str() +
"\n";
1025 process_loop(tokens[0],
1038 }
while (parser.apply_mutations());
1041 parser.foreach_match(
"[[w::w", [&](
const std::vector<Token> tokens) {
1042 if (tokens[2].
str() ==
"gpu" && tokens[5].
str() ==
"unroll") {
1043 report_error(
ERROR_TOK(tokens[0]),
"Incompatible loop format for [[gpu::unroll]].");
1048 void namespace_mutation(Parser &parser, report_callback report_error)
1050 using namespace std;
1051 using namespace shader::parser;
1054 parser.foreach_scope(ScopeType::Namespace, [&](
const Scope &scope) {
1056 scope.foreach_match(
"n", [&](
const std::vector<Token> &tokens) {
1057 report_error(
ERROR_TOK(tokens[0]),
"Nested namespaces are unsupported.");
1060 string namespace_prefix = namespace_separator_mutation(
1061 scope.start().prev().full_symbol_name() +
"::");
1062 auto process_symbol = [&](
const Token &symbol) {
1063 if (symbol.next() ==
'<') {
1068 scope.foreach_token(Word, [&](
const Token &token) {
1069 if (token.str() != symbol.str()) {
1073 if (token.namespace_start() != token) {
1077 if (token.prev() ==
'.') {
1080 parser.replace(token, namespace_prefix + token.str(),
true);
1084 unordered_set<string> processed_functions;
1086 scope.foreach_function([&](
bool, Token, Token fn_name, Scope,
bool, Scope) {
1088 if (fn_name.scope().type() == ScopeType::Local) {
1093 if (processed_functions.count(fn_name.str())) {
1097 processed_functions.emplace(fn_name.str());
1098 process_symbol(fn_name);
1100 scope.foreach_struct([&](Token, Token struct_name, Scope) { process_symbol(struct_name); });
1102 Token namespace_tok = scope.start().prev().namespace_start().prev();
1103 if (namespace_tok == Namespace) {
1104 parser.erase(namespace_tok, scope.start());
1105 parser.erase(scope.end());
1108 report_error(
ERROR_TOK(namespace_tok),
"Expected namespace token.");
1112 parser.apply_mutations();
1116 void using_mutation(Parser &parser, report_callback report_error)
1118 using namespace std;
1119 using namespace shader::parser;
1121 parser.foreach_match(
"un", [&](
const std::vector<Token> &tokens) {
1123 "Unsupported `using namespace`. "
1124 "Add individual `using` directives for each needed symbol.");
1127 auto process_using = [&](
const Token &using_tok,
1129 const Token &to_start,
1130 const Token &to_end,
1131 const Token &end_tok) {
1132 string to = parser.substr_range_inclusive(to_start, to_end);
1133 string namespace_prefix = parser.substr_range_inclusive(to_start,
1134 to_end.prev().prev().prev());
1135 Scope scope = from.scope();
1138 if (scope.type() == ScopeType::Global) {
1139 report_error(
ERROR_TOK(using_tok),
"The `using` keyword is not allowed in global scope.");
1142 if (scope.type() == ScopeType::Namespace) {
1145 string namespace_name = scope.start().prev().full_symbol_name();
1146 if (namespace_name != namespace_prefix) {
1149 "The `using` keyword is only allowed in namespace scope to make visible symbols "
1150 "from the same namespace declared in another scope, potentially from another "
1156 to = namespace_separator_mutation(to);
1159 const bool use_alias = from.str() != to_end.str();
1160 const bool replace_fn = !use_alias;
1166 scope.foreach_token(Word, [&](
const Token &token) {
1168 if (token.index <= to_end.index) {
1172 if (token.prev() ==
':') {
1175 if (!replace_fn && token.next() ==
'(') {
1178 if (token.str() != from.str()) {
1181 parser.replace(token, to,
true);
1184 parser.erase(using_tok, end_tok);
1187 parser.foreach_match(
"uw::w", [&](
const std::vector<Token> &tokens) {
1188 Token end = tokens.back().find_next(SemiColon);
1189 process_using(tokens[0], end.prev(), tokens[1], end.prev(), end);
1192 parser.foreach_match(
"uw=w::w", [&](
const std::vector<Token> &tokens) {
1193 Token end = tokens.back().find_next(SemiColon);
1194 process_using(tokens[0], tokens[1], tokens[3], end.prev(), end);
1197 parser.apply_mutations();
1200 parser.foreach_token(Using, [&](
const Token &token) {
1201 report_error(
ERROR_TOK(token),
"Unsupported `using` keyword usage.");
1205 std::string namespace_separator_mutation(
const std::string &
str)
1217 std::string disabled_code_mutation(
const std::string &
str, report_callback &report_error)
1219 using namespace std;
1220 using namespace shader::parser;
1222 Parser parser(
str, report_error);
1224 auto process_disabled_scope = [&](Token start_tok) {
1226 string end_str = start_tok.str_with_whitespace() +
"endif";
1227 size_t scope_end = parser.data_get().str.find(end_str, start_tok.str_index_start());
1228 if (scope_end == string::npos) {
1229 report_error(
ERROR_TOK(start_tok),
"Couldn't find end of disabled scope.");
1233 string else_str = start_tok.str_with_whitespace() +
"el";
1234 size_t scope_else = parser.data_get().str.find(else_str, start_tok.str_index_start());
1235 if (scope_else != string::npos && scope_else < scope_end) {
1237 parser.erase(start_tok.line_end() + 1, scope_else - 1);
1241 parser.erase(start_tok.str_index_start(), scope_end + end_str.size());
1245 parser.foreach_match(
"#ww", [&](
const std::vector<Token> &tokens) {
1246 if (tokens[1].
str() ==
"ifndef" && tokens[2].
str() ==
"GPU_SHADER") {
1247 process_disabled_scope(tokens[0]);
1250 parser.foreach_match(
"#i!w(w)", [&](
const std::vector<Token> &tokens) {
1251 if (tokens[1].
str() ==
"if" && tokens[3].
str() ==
"defined" &&
1252 tokens[5].
str() ==
"GPU_SHADER")
1254 process_disabled_scope(tokens[0]);
1257 parser.foreach_match(
"#i0", [&](
const std::vector<Token> &tokens) {
1258 if (tokens[1].
str() ==
"if" && tokens[2].
str() ==
"0") {
1259 process_disabled_scope(tokens[0]);
1262 return parser.result_get();
1265 std::string pragmas_mutation(
const std::string &
str, report_callback &report_error)
1268 using namespace std;
1269 using namespace shader::parser;
1271 Parser parser(
str, report_error);
1272 parser.foreach_match(
"#ww", [&](
const std::vector<Token> &tokens) {
1273 if (tokens[1].
str() ==
"pragma") {
1274 if (tokens[2].
str() ==
"once") {
1275 parser.erase(tokens.front(), tokens.back());
1277 else if (tokens[2].
str() ==
"runtime_generated") {
1278 parser.erase(tokens.front(), tokens.back());
1282 return parser.result_get();
1285 std::string swizzle_function_mutation(
const std::string &
str, report_callback &report_error)
1287 using namespace std;
1288 using namespace shader::parser;
1290 Parser parser(
str, report_error);
1292 parser.foreach_scope(ScopeType::Global, [&](Scope scope) {
1295 scope.foreach_match(
".w()", [&](
const std::vector<Token> &tokens) {
1296 string method_name = tokens[1].str();
1297 if (method_name.length() > 1 && method_name.length() <= 4 &&
1298 (method_name.find_first_not_of(
"xyzw") == string::npos ||
1299 method_name.find_first_not_of(
"rgba") == string::npos))
1303 parser.replace(tokens[2], tokens[3],
" ");
1307 return parser.result_get();
1310 void threadgroup_variables_parsing(
const std::string &
str)
1312 std::regex regex(R
"(shared\s+(\w+)\s+(\w+)([^;]*);)");
1313 regex_global_search(str, regex, [&](const std::smatch &match) {
1314 shared_vars_.push_back({match[1].str(), match[2].str(), match[3].str()});
1318 void parse_library_functions(
const std::string &
str)
1320 using namespace metadata;
1321 std::regex regex_func(R
"(void\s+(\w+)\s*\(([^)]+\))\s*\{)");
1322 regex_global_search(str, regex_func, [&](const std::smatch &match) {
1323 std::string
name = match[1].str();
1324 std::string args = match[2].str();
1329 std::regex regex_arg(R
"((?:(const|in|out|inout)\s)?(\w+)\s([\w\[\]]+)(?:,|\)))");
1330 regex_global_search(args, regex_arg, [&](const std::smatch &arg) {
1331 std::string qualifier = arg[1].str();
1332 std::string type = arg[2].str();
1333 if (qualifier.empty() || qualifier ==
"const") {
1336 fn.arguments.emplace_back(
1339 metadata.functions.emplace_back(fn);
1343 void parse_builtins(
const std::string &
str,
const std::string &filename)
1345 const bool skip_drw_debug = filename.find(
"draw_debug_draw_lib.glsl") != std::string::npos ||
1346 filename.find(
"draw_debug_draw_display_vert.glsl") !=
1347 std::string::npos ||
1348 filename.find(
"draw_shader_shared.hh") != std::string::npos;
1349 using namespace metadata;
1351 std::string tokens[] = {
"gl_FragCoord",
1352 "gl_FragStencilRefARB",
1354 "gl_GlobalInvocationID",
1356 "gl_LocalInvocationID",
1357 "gl_LocalInvocationIndex",
1366#ifdef WITH_GPU_SHADER_ASSERT
1370 for (
auto &token : tokens) {
1371 if (skip_drw_debug && token ==
"drw_debug_") {
1374 if (
str.find(token) != std::string::npos) {
1375 metadata.builtins.emplace_back(
Builtin(
hash(token)));
1380 template<
typename ReportErrorF>
1381 std::string printf_processing(
const std::string &
str,
const ReportErrorF &report_error)
1383 std::string out_str =
str;
1386 size_t start, end = 0;
1387 while ((start = out_str.find(
"printf(", end)) != std::string::npos) {
1388 end = out_str.find(
';', start);
1389 if (end == std::string::npos) {
1393 int bracket_depth = 0;
1395 for (
size_t i = start;
i < end; ++
i) {
1396 if (out_str[
i] ==
'(') {
1399 else if (out_str[
i] ==
')') {
1402 else if (bracket_depth == 1 && out_str[
i] ==
',') {
1411 "Too many parameters in printf. Max is 99.");
1415 out_str[start +
sizeof(
"printf") - 4] =
'$';
1416 out_str[start +
sizeof(
"printf") - 3] = ((arg_len / 10) > 0) ? (
'0' + arg_len / 10) :
'$';
1417 out_str[start +
sizeof(
"printf") - 2] =
'0' + arg_len % 10;
1426 std::regex regex(R
"(pri\$\$?(\d{1,2})\()");
1427 out_str = std::regex_replace(out_str, regex, "{uint c_ = print_header($1u, ");
1430 std::regex regex(R
"(\@)");
1431 out_str = std::regex_replace(out_str, regex, "); c_ = print_data(c_,");
1434 std::regex regex(R
"(\$)");
1435 out_str = std::regex_replace(out_str, regex, "; }");
1440 void assert_processing(Parser &parser,
const std::string &filepath, report_callback report_error)
1442 std::string filename = std::regex_replace(filepath, std::regex(R
"((?:.*)\/(.*))"), "$1");
1444 using namespace std;
1445 using namespace shader::parser;
1448 parser.foreach_match(
"w(..)", [&](
const vector<Token> &tokens) {
1449 if (tokens[0].
str() !=
"assert") {
1453#ifdef WITH_GPU_SHADER_ASSERT
1454 string condition = tokens[1].scope().str();
1455 replacement +=
"if (!" + condition +
") ";
1457 replacement +=
" printf(\"";
1458 replacement +=
"Assertion failed: " + condition +
", ";
1459 replacement +=
"file " + filename +
", ";
1460 replacement +=
"line %d, ";
1461 replacement +=
"thread (%u,%u,%u).\\n";
1462 replacement +=
"\"";
1463 replacement +=
", __LINE__, GPU_THREAD.x, GPU_THREAD.y, GPU_THREAD.z); ";
1466 parser.replace(tokens[0], tokens[4], replacement);
1468#ifndef WITH_GPU_SHADER_ASSERT
1472 parser.apply_mutations();
1476 static uint32_t hash_string(
const std::string &
str)
1483 void static_strings_merging(Parser &parser, report_callback )
1485 using namespace std;
1486 using namespace shader::parser;
1489 parser.foreach_match(
"__", [&](
const std::vector<Token> &tokens) {
1490 string first = tokens[0].str();
1491 string second = tokens[1].str();
1492 string between = parser.substr_range_inclusive(
1493 tokens[0].str_index_last_no_whitespace() + 1, tokens[1].str_index_start() - 1);
1494 string trailing = parser.substr_range_inclusive(
1495 tokens[1].str_index_last_no_whitespace() + 1, tokens[1].str_index_last());
1496 string merged = first.substr(0, first.length() - 1) + second.substr(1) + between +
1498 parser.replace_try(tokens[0], tokens[1], merged);
1500 }
while (parser.apply_mutations());
1503 void static_strings_parsing_and_mutation(Parser &parser, report_callback )
1505 using namespace std;
1506 using namespace shader::parser;
1508 parser.foreach_token(
String, [&](
const Token &token) {
1510 metadata::PrintfFormat
format = {
hash, token.str()};
1511 metadata.printf_formats.emplace_back(
format);
1512 parser.replace(token, std::to_string(
hash) +
'u',
true);
1514 parser.apply_mutations();
1518 void struct_method_mutation(Parser &parser, report_callback report_error)
1520 using namespace std;
1521 using namespace shader::parser;
1523 parser.foreach_scope(ScopeType::Global, [&](Scope scope) {
1525 scope.foreach_match(
"S", [&](
const std::vector<Token> &tokens) {
1526 parser.replace(tokens[0], tokens[0],
"struct ");
1530 parser.apply_mutations();
1532 parser.foreach_scope(ScopeType::Global, [&](Scope scope) {
1533 scope.foreach_match(
"sw", [&](
const std::vector<Token> &tokens) {
1534 const Token struct_name = tokens[1];
1536 if (struct_name.next() ==
':') {
1537 report_error(struct_name.next().line_number(),
1538 struct_name.next().char_number(),
1539 struct_name.next().line_str(),
1540 "class inheritance is not supported");
1543 if (struct_name.next() !=
'{') {
1544 report_error(struct_name.line_number(),
1545 struct_name.char_number(),
1546 struct_name.line_str(),
1551 const Scope struct_scope = struct_name.next().scope();
1552 const Token struct_end = struct_scope.end().next();
1555 struct_scope.foreach_match(
"v:", [&](
const std::vector<Token> &tokens) {
1556 parser.erase(tokens[0].line_start(), tokens[1].line_end());
1558 struct_scope.foreach_match(
"V:", [&](
const std::vector<Token> &tokens) {
1559 parser.erase(tokens[0].line_start(), tokens[1].line_end());
1562 struct_scope.foreach_match(
"ww(", [&](
const std::vector<Token> &tokens) {
1563 if (tokens[0].
prev() == Const) {
1567 "function return type is marked `const` but it makes no sense for values "
1568 "and returning reference is not supported");
1572 const bool is_static = tokens[0].prev() ==
Static;
1573 const Token fn_start = is_static ? tokens[0].prev() : tokens[0];
1574 const Scope fn_args = tokens[2].scope();
1575 const Token after_args = fn_args.end().next();
1576 const bool is_const = after_args ==
Const;
1577 const Scope fn_body = (is_const ? after_args.next() : after_args).scope();
1579 string fn_content = parser.substr_range_inclusive(fn_start.line_start(),
1580 fn_body.end().line_end() + 1);
1582 Parser fn_parser(fn_content, report_error);
1583 fn_parser.foreach_scope(ScopeType::Global, [&](Scope scope) {
1585 scope.foreach_match(
"mww(", [&](
const std::vector<Token> &tokens) {
1586 const Token fn_name = tokens[2];
1587 fn_parser.replace(fn_name, fn_name, struct_name.str() +
"::" + fn_name.str());
1590 fn_parser.erase(tokens[0]);
1594 scope.foreach_match(
"ww(", [&](
const std::vector<Token> &tokens) {
1595 const Scope args = tokens[2].scope();
1596 const bool has_no_args = args.token_count() == 2;
1597 const char *suffix = (has_no_args ?
"" :
", ");
1600 fn_parser.erase(args.end().next());
1601 fn_parser.insert_after(args.start(),
1602 "const " + struct_name.str() +
" this_" + suffix);
1605 fn_parser.insert_after(args.start(), struct_name.str() +
" &this_" + suffix);
1611 scope.foreach_match(
"*T", [&](
const std::vector<Token> &tokens) {
1612 fn_parser.replace(tokens[0], tokens[1],
"this_");
1615 scope.foreach_match(
"TD", [&](
const std::vector<Token> &tokens) {
1616 fn_parser.replace(tokens[0], tokens[1],
"this_.");
1620 string line_directive =
"#line " + std::to_string(fn_start.line_number()) +
'\n';
1621 parser.erase(fn_start.line_start(), fn_body.end().line_end());
1622 parser.insert_after(struct_end.line_end() + 1,
line_directive + fn_parser.result_get());
1625 string line_directive =
"#line " + std::to_string(struct_end.line_number() + 1) +
'\n';
1630 parser.apply_mutations();
1635 void empty_struct_mutation(Parser &parser, report_callback )
1637 using namespace std;
1638 using namespace shader::parser;
1640 parser.foreach_scope(ScopeType::Global, [&](Scope scope) {
1641 scope.foreach_match(
"sw{};", [&](
const std::vector<Token> &tokens) {
1642 parser.insert_after(tokens[2],
"int _pad;");
1645 parser.apply_mutations();
1649 void method_call_mutation(Parser &parser, report_callback report_error)
1651 using namespace std;
1652 using namespace shader::parser;
1655 parser.foreach_scope(ScopeType::Function, [&](Scope scope) {
1656 scope.foreach_match(
".w(", [&](
const std::vector<Token> &tokens) {
1657 const Token
dot = tokens[0];
1658 const Token func = tokens[1];
1659 const Token par_open = tokens[2];
1660 const Token end_of_this =
dot.prev();
1661 Token start_of_this = end_of_this;
1663 if (start_of_this ==
')') {
1665 start_of_this = start_of_this.scope().start().prev();
1668 if (start_of_this ==
']') {
1670 start_of_this = start_of_this.scope().start().prev();
1673 if (start_of_this == Word) {
1675 if (start_of_this.prev() ==
'.') {
1676 start_of_this = start_of_this.prev().prev();
1683 report_error(start_of_this.line_number(),
1684 start_of_this.char_number(),
1685 start_of_this.line_str(),
1686 "method_call_mutation parsing error");
1689 string this_str = parser.substr_range_inclusive(start_of_this, end_of_this);
1690 string func_str = func.str();
1691 const bool has_no_arg = par_open.next() ==
')';
1694 start_of_this, par_open, func_str +
"(" + this_str + (has_no_arg ?
"" :
", "));
1697 }
while (parser.apply_mutations());
1700 void stage_function_mutation(Parser &parser, report_callback )
1702 using namespace std;
1703 using namespace shader::parser;
1705 parser.foreach_function([&](
bool is_static, Token fn_type, Token, Scope,
bool, Scope fn_body) {
1706 Token attr_tok = (is_static) ? fn_type.prev().prev() : fn_type.prev();
1707 if (attr_tok.is_invalid() || attr_tok !=
']' || attr_tok.prev() !=
']') {
1710 Scope attribute = attr_tok.prev().scope();
1711 if (attribute.type() != ScopeType::Subscript) {
1715 const string attr = attribute.str_exclusive();
1716 parser.erase(attribute.scope());
1718 string condition =
"defined(";
1719 if (attr ==
"gpu::vertex_function") {
1720 condition +=
"GPU_VERTEX_SHADER";
1722 else if (attr ==
"gpu::fragment_function") {
1723 condition +=
"GPU_FRAGMENT_SHADER";
1725 else if (attr ==
"gpu::compute_function") {
1726 condition +=
"GPU_COMPUTE_SHADER";
1733 guarded_scope_mutation(parser, fn_body, condition);
1735 parser.apply_mutations();
1738 void resource_guard_mutation(Parser &parser, report_callback )
1740 using namespace std;
1741 using namespace shader::parser;
1743 parser.foreach_function([&](
bool, Token fn_type, Token, Scope,
bool, Scope fn_body) {
1744 fn_body.foreach_match(
"w(w,", [&](
const std::vector<Token> &tokens) {
1745 string func_name = tokens[0].str();
1746 if (func_name !=
"specialization_constant_get" && func_name !=
"shared_variable_get" &&
1747 func_name !=
"push_constant_get" && func_name !=
"interface_get" &&
1748 func_name !=
"attribute_get" && func_name !=
"buffer_get" &&
1749 func_name !=
"sampler_get" && func_name !=
"image_get")
1753 string info_name = tokens[2].str();
1754 Scope scope = tokens[0].scope();
1756 while (scope.type() != ScopeType::Function && scope.type() != ScopeType::Local) {
1757 scope = scope.scope();
1760 string condition =
"defined(CREATE_INFO_" + info_name +
")";
1762 if (scope.type() == ScopeType::Function) {
1763 guarded_scope_mutation(parser, scope, condition, fn_type);
1766 guarded_scope_mutation(parser, scope, condition);
1771 parser.apply_mutations();
1774 void guarded_scope_mutation(parser::Parser &parser,
1775 parser::Scope scope,
1776 const std::string &condition,
1779 using namespace std;
1780 using namespace shader::parser;
1782 string line_start =
"#line " + std::to_string(scope.start().next().line_number()) +
"\n";
1783 string line_end =
"#line " + std::to_string(scope.end().line_number()) +
"\n";
1785 string guard_start =
"#if " + condition +
"\n";
1787 if (fn_type.is_valid() && fn_type.str() !=
"void") {
1788 string type = fn_type.str();
1789 bool is_trivial =
false;
1790 if (type ==
"float" || type ==
"float2" || type ==
"float3" || type ==
"float4" ||
1792 type ==
"int" || type ==
"int2" || type ==
"int3" || type ==
"int4" ||
1794 type ==
"uint" || type ==
"uint2" || type ==
"uint3" || type ==
"uint4" ||
1796 type ==
"float2x2" || type ==
"float2x3" || type ==
"float2x4" ||
1798 type ==
"float3x2" || type ==
"float3x3" || type ==
"float3x4" ||
1800 type ==
"float4x2" || type ==
"float4x3" || type ==
"float4x4")
1804 guard_else +=
"#else\n";
1805 guard_else += line_start;
1806 guard_else +=
" return " + type + (is_trivial ?
"(0)" :
"::zero()") +
";\n";
1808 string guard_end =
"#endif\n";
1810 parser.insert_after(scope.start().line_end() + 1, guard_start + line_start);
1811 parser.insert_before(scope.end().line_start(), guard_else + guard_end + line_end);
1814 std::string guarded_scope_mutation(std::string content,
int64_t line_start, std::string check)
1817 std::string guarded_cope;
1818 guarded_cope +=
"#if " + check +
"\n";
1819 guarded_cope +=
"#line " + std::to_string(line_start) +
"\n";
1820 guarded_cope += content;
1821 guarded_cope +=
"#endif\n";
1822 guarded_cope +=
"#line " + std::to_string(line_end) +
"\n";
1823 return guarded_cope;
1826 std::string enum_macro_injection(
const std::string &
str,
1827 bool is_shared_file,
1828 report_callback &report_error)
1859 using namespace std;
1860 using namespace shader::parser;
1862 Parser parser(
str, report_error);
1864 auto missing_underlying_type = [&](vector<Token> tokens) {
1868 "enum declaration must explicitly use an underlying type");
1871 parser.foreach_match(
"Mw{", missing_underlying_type);
1872 parser.foreach_match(
"MSw{", missing_underlying_type);
1875 [&](Token enum_tok, Token class_tok, Token enum_name, Token enum_type, Scope enum_scope) {
1876 string type_str = enum_type.str();
1878 if (is_shared_file) {
1879 if (type_str !=
"uint32_t" && type_str !=
"int32_t") {
1881 enum_type.line_number(),
1882 enum_type.char_number(),
1883 enum_type.line_str(),
1884 "enum declaration must use uint32_t or int32_t underlying type for interface "
1890 size_t insert_at = enum_scope.end().line_end();
1891 parser.erase(enum_tok.str_index_start(), insert_at);
1892 parser.insert_line_number(insert_at + 1, enum_tok.line_number());
1893 parser.insert_after(insert_at + 1,
1894 "#define " + enum_name.str() +
" " + enum_type.str() +
"\n");
1896 enum_scope.foreach_scope(ScopeType::Assignment, [&](Scope scope) {
1897 string name = scope.start().prev().str();
1898 string value = scope.str();
1899 if (class_tok.is_valid()) {
1900 name = enum_name.str() +
"::" +
name;
1902 string decl =
"constant static constexpr " + type_str +
" " +
name +
" " + value +
1904 parser.insert_line_number(insert_at + 1, scope.start().line_number());
1905 parser.insert_after(insert_at + 1, decl);
1907 parser.insert_line_number(insert_at + 1, enum_scope.end().line_number() + 1);
1910 parser.foreach_match(
"MSw:w{", [&](vector<Token> tokens) {
1911 process_enum(tokens[0], tokens[1], tokens[2], tokens[4], tokens[5].scope());
1913 parser.foreach_match(
"Mw:w{", [&](vector<Token> tokens) {
1914 process_enum(tokens[0], Token::invalid(), tokens[1], tokens[3], tokens[4].scope());
1917 parser.apply_mutations();
1919 parser.foreach_match(
"M", [&](vector<Token> tokens) {
1923 "invalid enum declaration");
1925 return parser.result_get();
1928 std::string strip_whitespace(
const std::string &
str)
const
1930 return str.substr(0,
str.find_last_not_of(
" \n") + 1);
1937 void default_argument_mutation(Parser &parser, report_callback )
1939 using namespace std;
1940 using namespace shader::parser;
1942 parser.foreach_function(
1943 [&](
bool, Token fn_type, Token fn_name, Scope fn_args,
bool, Scope fn_body) {
1944 if (!fn_args.contains_token(
'=')) {
1948 const bool has_non_void_return_type = fn_type.str() !=
"void";
1953 vector<string> fn_overloads;
1955 fn_args.foreach_scope(ScopeType::FunctionArg, [&](Scope arg) {
1956 Token
equal = arg.find_token(
'=');
1957 const char *comma = (args_decl.empty() ?
"" :
", ");
1958 if (
equal.is_invalid()) {
1959 args_decl += comma + arg.str();
1960 args_names += comma + arg.end().str();
1963 string arg_name =
equal.prev().str();
1964 string value = parser.substr_range_inclusive(
equal.next(), arg.end());
1965 string decl = parser.substr_range_inclusive(arg.start(),
equal.prev());
1967 string fn_call = fn_name.str() +
'(' + args_names + comma + value +
");";
1968 if (has_non_void_return_type) {
1969 fn_call =
"return " + fn_call;
1972 overload += fn_type.str() +
" ";
1973 overload += fn_name.str() +
'(' + args_decl +
")\n";
1975 overload +=
"#line " + std::to_string(fn_type.line_number()) +
"\n";
1976 overload +=
" " + fn_call +
"\n}\n";
1977 fn_overloads.emplace_back(overload);
1979 args_decl += comma + strip_whitespace(decl);
1980 args_names += comma + arg_name;
1982 parser.erase(
equal.scope());
1985 size_t end_of_fn_char = fn_body.end().line_end() + 1;
1987 for (
auto it = fn_overloads.rbegin(); it != fn_overloads.rend(); ++it) {
1988 parser.insert_line_number(end_of_fn_char, fn_type.line_number());
1989 parser.insert_after(end_of_fn_char, *it);
1991 parser.insert_line_number(end_of_fn_char, fn_body.end().line_number() + 1);
1994 parser.apply_mutations();
1999 std::string matrix_constructor_mutation(
const std::string &
str)
2001 if (
str.find(
"mat") == std::string::npos) {
2005 std::regex regex_parenthesis(R
"(\bmat([234])\()");
2006 std::string out = std::regex_replace(str, regex_parenthesis, "mat$1x$1(");
2009 std::regex regex(R
"(\bmat(2x2|3x3|4x4)\()");
2010 return std::regex_replace(
out, regex,
"__mat$1(");
2014 void argument_reference_mutation(Parser &parser, report_callback )
2016 using namespace std;
2017 using namespace shader::parser;
2019 auto add_mutation = [&](Token type, Token arg_name, Token last_tok) {
2020 if (type.prev() == Const) {
2021 parser.replace(type.prev(), last_tok, type.str() +
" " + arg_name.str());
2024 parser.replace(type, last_tok,
"inout " + type.str() +
" " + arg_name.str());
2028 parser.foreach_scope(ScopeType::FunctionArgs, [&](
const Scope scope) {
2029 scope.foreach_match(
2030 "w(&w)", [&](
const vector<Token> toks) { add_mutation(toks[0], toks[3], toks[4]); });
2031 scope.foreach_match(
2032 "w&w", [&](
const vector<Token> toks) { add_mutation(toks[0], toks[2], toks[2]); });
2033 scope.foreach_match(
2034 "w&T", [&](
const vector<Token> toks) { add_mutation(toks[0], toks[2], toks[2]); });
2036 parser.apply_mutations();
2040 std::string variable_reference_mutation(
const std::string &
str, report_callback report_error)
2042 using namespace std;
2044 bool valid_match =
false;
2045 string next_str =
str;
2048 if (parenthesis_depth == 0) {
2061 regex regex_ref(R
"(\ ?(?:const)?\s*\w+\s+\@(\w+) =\s*([^;]+);)");
2064 while (regex_search(next_str, match, regex_ref)) {
2065 const string definition = match[0].str();
2066 const string name = match[1].str();
2067 const string value = match[2].str();
2068 const string prefix = match.prefix().str();
2069 const string suffix = match.suffix().str();
2074 if (value.find(
"++") != string::npos || value.find(
"--") != string::npos) {
2078 "Reference definitions cannot have side effects.");
2081 if (value.find(
"(") != string::npos) {
2082 if (value.find(
"specialization_constant_get(") == string::npos &&
2083 value.find(
"push_constant_get(") == string::npos &&
2084 value.find(
"interface_get(") == string::npos &&
2085 value.find(
"attribute_get(") == string::npos &&
2086 value.find(
"buffer_get(") == string::npos &&
2087 value.find(
"sampler_get(") == string::npos && value.find(
"image_get(") == string::npos)
2092 "Reference definitions cannot contain function calls.");
2096 if (value.find(
"[") != string::npos) {
2099 if (index_var.find(
' ') != string::npos) {
2103 "Array subscript inside reference declaration must be a single variable or "
2104 "a constant, not an expression.");
2109 string scope_depth =
" }";
2110 bool found_var =
false;
2111 while (!found_var) {
2115 if (scope.empty()) {
2119 scope = regex_replace(scope, regex(R
"(\{[^\}]*\})"), "{}");
2121 regex regex_definition(R
"((const)? \w+ )" + index_var + " =");
2122 smatch match_definition;
2123 if (regex_search(scope, match_definition, regex_definition)) {
2125 if (match_definition[1].matched ==
false) {
2129 "Array subscript variable must be declared as const qualified.");
2138 "Cannot locate array subscript variable declaration. "
2139 "If it is a global variable, assign it to a temporary const variable for "
2140 "indexing inside the reference.");
2147 if (scope.empty()) {
2151 "Reference is defined inside a global or unterminated scope.");
2154 string original = definition + scope;
2155 string modified = original;
2158 string newlines(
line_count(definition),
'\n');
2162 modified = regex_replace(
2163 modified, regex(R
"(([^\.])\b)" + name + R"(\b([^(]))"), "$1" + value +
"$2");
2166 next_str = definition + suffix;
2171 out_str += next_str;
2175 std::string argument_decorator_macro_injection(
const std::string &
str)
2178 std::regex regex(R
"((out|inout|in|shared)\s+(\w+)\s+(\w+))");
2179 return std::regex_replace(
str, regex,
"$1 $2 _$1_sta $3 _$1_end");
2182 std::string array_constructor_macro_injection(
const std::string &
str)
2185 std::regex regex(R
"(=\s*(\w+)\s*\[[^\]]*\]\s*\()");
2186 return std::regex_replace(
str, regex,
"= ARRAY_T($1) ARRAY_V(");
2190 void global_scope_constant_linting(Parser &parser, report_callback report_error)
2192 using namespace std;
2193 using namespace shader::parser;
2196 parser.foreach_match(
"cww=", [&](
const vector<Token> &tokens) {
2197 if (tokens[0].scope().type() == ScopeType::Global) {
2200 "Global scope constant expression found. These get allocated per-thread in MSL. "
2201 "Use Macro's or uniforms instead.");
2206 void quote_linting(
const std::string &
str, report_callback report_error)
2208 using namespace std;
2209 using namespace shader::parser;
2211 Parser parser(
str, report_error);
2213 parser.foreach_token(TokenType::String, [&](
const Token token) {
2215 "Unprocessed string literal. "
2216 "Strings are forbidden in GLSL.");
2220 void small_type_linting(Parser &parser, report_callback report_error)
2222 using namespace std;
2223 using namespace shader::parser;
2225 parser.foreach_scope(ScopeType::Struct, [&](
const Scope scope) {
2226 scope.foreach_match(
"ww;", [&](
const vector<Token> tokens) {
2227 string type = tokens[0].str();
2228 if (type.find(
"char") != string::npos || type.find(
"short") != string::npos ||
2229 type.find(
"half") != string::npos)
2231 report_error(
ERROR_TOK(tokens[0]),
"Small types are forbidden in shader interfaces.");
2237 std::string threadgroup_variables_suffix()
2239 if (shared_vars_.empty()) {
2243 std::stringstream suffix;
2259 suffix <<
"#undef MSL_SHARED_VARS_ARGS\n";
2261 suffix <<
"#undef MSL_SHARED_VARS_ASSIGN\n";
2263 suffix <<
"#undef MSL_SHARED_VARS_DECLARE\n";
2265 suffix <<
"#undef MSL_SHARED_VARS_PASS\n";
2305 std::stringstream args, assign, declare, pass;
2308 for (SharedVar &var : shared_vars_) {
2309 char sep = first ?
' ' :
',';
2311 args << sep <<
"threadgroup " << var.type <<
"(&_" << var.name <<
")" << var.array;
2312 assign << (first ?
':' :
',') << var.name <<
"(_" << var.name <<
")";
2313 declare <<
"threadgroup " << var.type <<
' ' << var.name << var.array <<
";";
2314 pass << sep << var.name;
2318 suffix <<
"#define MSL_SHARED_VARS_ARGS " << args.str() <<
"\n";
2319 suffix <<
"#define MSL_SHARED_VARS_ASSIGN " << assign.str() <<
"\n";
2320 suffix <<
"#define MSL_SHARED_VARS_DECLARE " << declare.str() <<
"\n";
2321 suffix <<
"#define MSL_SHARED_VARS_PASS (" << pass.str() <<
")\n";
2324 return suffix.str();
2327 std::string line_directive_prefix(
const std::string &filepath)
2329 std::string filename = std::regex_replace(filepath, std::regex(R
"((?:.*)\/(.*))"), "$1");
2331 std::stringstream suffix;
2334 suffix <<
"#line 1 \"" << filename <<
"\"\n";
2335 return suffix.str();
2341 char start_delimiter,
2343 const bool backwards =
false)
2346 size_t start = std::string::npos;
2347 size_t end = std::string::npos;
2350 std::swap(start_delimiter, end_delimiter);
2353 for (
size_t i = 0;
i <
input.length(); ++
i) {
2354 size_t idx = backwards ? (
input.length() - 1) -
i :
i;
2355 if (
input[idx] == start_delimiter) {
2361 else if (
input[idx] == end_delimiter) {
2363 if (
balance == 0 && start != std::string::npos) {
2366 std::swap(start, end);
2368 return input.substr(start + 1, end - start - 1);
2378 const char start_delimiter,
2379 const char end_delimiter,
2386 for (
char &string_char :
str) {
2387 if (string_char == start_delimiter) {
2390 else if (string_char == end_delimiter) {
2393 else if (depth > 0 && string_char == from) {
2401 static std::vector<std::string>
split_string(
const std::string &
str,
const char delimiter)
2403 std::vector<std::string> substrings;
2404 std::stringstream ss(
str);
2407 while (std::getline(ss, item, delimiter)) {
2408 substrings.push_back(item);
2416 const char delimiter,
2417 const char pair_start,
2418 const char pair_end)
2420 const char safe_char =
'@';
2422 str, pair_start, pair_end, delimiter, safe_char);
2430 static void replace_all(std::string &
str,
const std::string &from,
const std::string &to)
2435 size_t start_pos = 0;
2436 while ((start_pos =
str.find(from, start_pos)) != std::string::npos) {
2437 str.replace(start_pos, from.length(), to);
2438 start_pos += to.length();
2444 for (
char &string_char :
str) {
2445 if (string_char == from) {
2453 return std::count(
str.begin(),
str.end(), c);
2467 str,
'&', [&](
size_t pos,
int parenthesis_depth,
int bracket_depth,
char &c) {
2468 if (
pos > 0 &&
pos <=
str.length() - 2) {
2470 char prev_char =
str[
pos - 1];
2471 char next_char =
str[
pos + 1];
2473 if (prev_char ==
' ' || prev_char ==
'(') {
2474 if (next_char !=
' ' && next_char !=
'\n' && next_char !=
'&' && next_char !=
'=') {
2475 callback(parenthesis_depth, bracket_depth, c);
2486 std::function<
void(
int,
int,
char &)> callback)
2489 str,
'=', [&](
size_t pos,
int parenthesis_depth,
int bracket_depth,
char &c) {
2490 if (
pos > 0 &&
pos <=
str.length() - 2) {
2492 char prev_char =
str[
pos - 1];
2493 char next_char =
str[
pos + 1];
2495 if (prev_char ==
' ' && next_char ==
' ') {
2496 if (parenthesis_depth == 1 && bracket_depth == 0) {
2497 callback(parenthesis_depth, bracket_depth, c);
2508 std::function<
void(
size_t,
int,
int,
char &)> callback)
2511 int parenthesis_depth = 0;
2512 int bracket_depth = 0;
2513 for (
char &c :
str) {
2514 if (c == search_char) {
2515 callback(
pos, parenthesis_depth, bracket_depth, c);
2517 else if (c ==
'(') {
2518 parenthesis_depth++;
2520 else if (c ==
')') {
2521 parenthesis_depth--;
2523 else if (c ==
'{') {
2526 else if (c ==
'}') {
2536 std::string sub_str = file_str.substr(0,
pos);
2537 std::string directive =
"#line ";
2538 size_t nearest_line_directive = sub_str.rfind(directive);
2540 if (nearest_line_directive != std::string::npos) {
2541 sub_str = sub_str.substr(nearest_line_directive + directive.size());
2544 return line_count + std::count(sub_str.begin(), sub_str.end(),
'\n');
2548 std::string whole_file = smatch.prefix().str() + smatch[0].str() + smatch.suffix().str();
2549 return line_number(whole_file, smatch.prefix().str().size());
2555 std::string sub_str = file_str.substr(0,
pos);
2556 size_t nearest_line_directive = sub_str.find_last_of(
"\n");
2557 return (nearest_line_directive == std::string::npos) ?
2558 (sub_str.size() - 1) :
2559 (sub_str.size() - nearest_line_directive);
2563 std::string whole_file = smatch.prefix().str() + smatch[0].str() + smatch.suffix().str();
2564 return char_number(whole_file, smatch.prefix().str().size());
2570 size_t start = file_str.rfind(
'\n',
pos);
2571 size_t end = file_str.find(
'\n',
pos);
2572 if (start == std::string::npos) {
2575 return file_str.substr(start, end - start);
2579 std::string whole_file = smatch.prefix().str() + smatch[0].str() + smatch.suffix().str();
2580 return line_str(whole_file, smatch.prefix().str().size());