effect_preprocessor.hpp (5898B)
1 /* 2 * Copyright (C) 2014 Patrick Mours 3 * SPDX-License-Identifier: BSD-3-Clause 4 */ 5 6 #pragma once 7 8 #include "effect_token.hpp" 9 #include <cstdint> 10 #include <memory> // std::unique_ptr 11 #include <filesystem> 12 #include <unordered_map> 13 #include <unordered_set> 14 15 namespace reshadefx 16 { 17 /// <summary> 18 /// A C-style preprocessor implementation. 19 /// </summary> 20 class preprocessor 21 { 22 public: 23 struct macro 24 { 25 std::string replacement_list; 26 std::vector<std::string> parameters; 27 bool is_predefined = false; 28 bool is_variadic = false; 29 bool is_function_like = false; 30 }; 31 32 // Callbacks for manual file reading. 33 using include_file_exists_callback = bool(*)(const std::string &path); 34 using include_read_file_callback = bool(*)(const std::string &path, std::string &data); 35 static bool stdfs_read_file_callback(const std::string &path, std::string& data); 36 static bool stdfs_file_exists_callback(const std::string &path); 37 38 // Define constructor explicitly because lexer class is not included here 39 preprocessor(); 40 ~preprocessor(); 41 42 /// <summary> 43 /// Sets callbacks to use for reading files. If this is not called, std::filesystem will be used. 44 /// </summary> 45 void set_include_callbacks(include_file_exists_callback file_exists, include_read_file_callback read_file); 46 47 /// <summary> 48 /// Adds an include directory to the list of search paths used when resolving #include directives. 49 /// </summary> 50 /// <param name="path">Path to the directory to add.</param> 51 void add_include_path(const std::string &path); 52 53 /// <summary> 54 /// Adds a new macro definition. This is equal to appending '#define name macro' to this preprocessor instance. 55 /// </summary> 56 /// <param name="name">Name of the macro to define.</param> 57 /// <param name="macro">Definition of the macro function or value.</param> 58 /// <returns></returns> 59 bool add_macro_definition(const std::string &name, const macro ¯o); 60 /// <summary> 61 /// Adds a new macro value definition. This is equal to appending '#define name macro' to this preprocessor instance. 62 /// </summary> 63 /// <param name="name">Name of the macro to define.</param> 64 /// <param name="value">Value to define that macro to.</param> 65 /// <returns></returns> 66 bool add_macro_definition(const std::string &name, std::string value = "1") 67 { 68 return add_macro_definition(name, macro { std::move(value), {}, true }); 69 } 70 71 /// <summary> 72 /// Opens the specified file, parses its contents and appends them to the output. 73 /// </summary> 74 /// <param name="path">Path to the file to parse.</param> 75 /// <returns><see langword="true"/> if parsing was successful, <see langword="false"/> otherwise.</returns> 76 bool append_file(const std::string &path); 77 /// <summary> 78 /// Parses the specified string and appends it to the output. 79 /// </summary> 80 /// <param name="source_code">String to parse.</param> 81 /// <param name="path">Optional file path to identify this string with.</param> 82 /// <returns><see langword="true"/> if parsing was successful, <see langword="false"/> otherwise.</returns> 83 bool append_string(std::string source_code, const std::string &path = std::string()); 84 85 /// <summary> 86 /// Gets the list of error messages. 87 /// </summary> 88 const std::string &errors() const { return _errors; } 89 /// <summary> 90 /// Gets the current pre-processed output string. 91 /// </summary> 92 const std::string &output() const { return _output; } 93 94 /// <summary> 95 /// Gets a list of all included files. 96 /// </summary> 97 std::vector<std::filesystem::path> included_files() const; 98 99 /// <summary> 100 /// Gets a list of all defines that were used in #ifdef and #ifndef lines. 101 /// </summary> 102 std::vector<std::pair<std::string, std::string>> used_macro_definitions() const; 103 104 /// <summary> 105 /// Gets a list of pragma directives that occured. 106 /// </summary> 107 std::vector<std::pair<std::string, std::string>> used_pragma_directives() const { return _used_pragmas; } 108 109 private: 110 struct if_level 111 { 112 bool value; 113 bool skipping; 114 token pp_token; 115 size_t input_index; 116 }; 117 struct input_level 118 { 119 std::string name; 120 std::unique_ptr<class lexer> lexer; 121 token next_token; 122 std::unordered_set<std::string> hidden_macros; 123 }; 124 125 void error(const location &location, const std::string &message); 126 void warning(const location &location, const std::string &message); 127 128 void push(std::string input, const std::string &name = std::string()); 129 130 bool peek(tokenid tokid) const; 131 void consume(); 132 void consume_until(tokenid tokid); 133 bool accept(tokenid tokid, bool ignore_whitespace = true); 134 bool expect(tokenid tokid); 135 136 void parse(); 137 void parse_def(); 138 void parse_undef(); 139 void parse_if(); 140 void parse_ifdef(); 141 void parse_ifndef(); 142 void parse_elif(); 143 void parse_else(); 144 void parse_endif(); 145 void parse_error(); 146 void parse_warning(); 147 void parse_pragma(); 148 void parse_include(); 149 150 bool evaluate_expression(); 151 bool evaluate_identifier_as_macro(); 152 153 bool is_defined(const std::string &name) const; 154 void expand_macro(const std::string &name, const macro ¯o, const std::vector<std::string> &arguments); 155 void create_macro_replacement_list(macro ¯o); 156 157 bool _success = true; 158 include_file_exists_callback _file_exists_cb; 159 include_read_file_callback _read_file_cb; 160 std::string _output, _errors; 161 162 std::string _current_token_raw_data; 163 reshadefx::token _token; 164 location _output_location; 165 std::vector<input_level> _input_stack; 166 size_t _next_input_index = 0; 167 size_t _current_input_index = 0; 168 169 std::vector<if_level> _if_stack; 170 171 unsigned short _recursion_count = 0; 172 std::unordered_set<std::string> _used_macros; 173 std::unordered_map<std::string, macro> _macros; 174 175 std::vector<std::filesystem::path> _include_paths; 176 std::unordered_map<std::string, std::string> _file_cache; 177 178 std::vector<std::pair<std::string, std::string>> _used_pragmas; 179 }; 180 }