| 710 | } |
| 711 | |
| 712 | void reshadefx::preprocessor::parse_include() |
| 713 | { |
| 714 | const location keyword_location = std::move(_token.location); |
| 715 | |
| 716 | while (accept(tokenid::identifier)) |
| 717 | { |
| 718 | if (!evaluate_identifier_as_macro()) |
| 719 | { |
| 720 | error(_token.location, "syntax error: unexpected identifier in #include"); |
| 721 | consume_until(tokenid::end_of_line); |
| 722 | return; |
| 723 | } |
| 724 | } |
| 725 | |
| 726 | if (!expect(tokenid::string_literal)) |
| 727 | { |
| 728 | consume_until(tokenid::end_of_line); |
| 729 | return; |
| 730 | } |
| 731 | |
| 732 | std::filesystem::path file_name = std::filesystem::u8path(_token.literal_as_string); |
| 733 | std::filesystem::path file_path = std::filesystem::u8path(_output_location.source); |
| 734 | file_path.replace_filename(file_name); |
| 735 | |
| 736 | std::error_code ec; |
| 737 | if (!std::filesystem::exists(file_path, ec)) |
| 738 | for (const std::filesystem::path &include_path : _include_paths) |
| 739 | if (std::filesystem::exists(file_path = include_path / file_name, ec)) |
| 740 | break; |
| 741 | |
| 742 | const std::string file_path_string = file_path.u8string(); |
| 743 | |
| 744 | // Detect recursive include and abort to avoid infinite loop |
| 745 | if (std::find_if(_input_stack.begin(), _input_stack.end(), |
| 746 | [&file_path_string](const input_level &level) { |
| 747 | return level.name == file_path_string; |
| 748 | }) != _input_stack.end()) |
| 749 | return error(_token.location, "recursive #include"); |
| 750 | |
| 751 | std::string input; |
| 752 | |
| 753 | if (const auto file_it = _file_cache.find(file_path_string); |
| 754 | file_it != _file_cache.end()) |
| 755 | { |
| 756 | input = file_it->second; |
| 757 | } |
| 758 | else |
| 759 | { |
| 760 | if (!read_file(file_path, input)) |
| 761 | return error(keyword_location, "could not open included file '" + file_name.u8string() + '\''); |
| 762 | |
| 763 | _file_cache.emplace(file_path_string, input); |
| 764 | } |
| 765 | |
| 766 | // Skip end of line character following the include statement before pushing, so that the line number is already pointing to the next line when popping out of it again |
| 767 | if (!expect(tokenid::end_of_line)) |
| 768 | consume_until(tokenid::end_of_line); |
| 769 | |