/* * Copyright (C) 2014 Patrick Mours * SPDX-License-Identifier: BSD-3-Clause */ #pragma once #include "effect_token.hpp" #include #include // std::unique_ptr #include #include #include namespace reshadefx { /// /// A C-style preprocessor implementation. /// class preprocessor { public: struct macro { std::string replacement_list; std::vector parameters; bool is_predefined = false; bool is_variadic = false; bool is_function_like = false; }; // Define constructor explicitly because lexer class is not included here preprocessor(); ~preprocessor(); /// /// Adds an include directory to the list of search paths used when resolving #include directives. /// /// Path to the directory to add. void add_include_path(const std::filesystem::path &path); /// /// Adds a new macro definition. This is equal to appending '#define name macro' to this preprocessor instance. /// /// Name of the macro to define. /// Definition of the macro function or value. /// bool add_macro_definition(const std::string &name, const macro ¯o); /// /// Adds a new macro value definition. This is equal to appending '#define name macro' to this preprocessor instance. /// /// Name of the macro to define. /// Value to define that macro to. /// bool add_macro_definition(const std::string &name, std::string value = "1") { return add_macro_definition(name, macro { std::move(value), {}, true }); } /// /// Opens the specified file, parses its contents and appends them to the output. /// /// Path to the file to parse. /// if parsing was successful, otherwise. bool append_file(const std::filesystem::path &path); /// /// Parses the specified string and appends it to the output. /// /// String to parse. /// Optional file path to identify this string with. /// if parsing was successful, otherwise. bool append_string(std::string source_code, const std::filesystem::path &path = std::filesystem::path()); /// /// Gets the list of error messages. /// const std::string &errors() const { return _errors; } /// /// Gets the current pre-processed output string. /// const std::string &output() const { return _output; } /// /// Gets a list of all included files. /// std::vector included_files() const; /// /// Gets a list of all defines that were used in #ifdef and #ifndef lines. /// std::vector> used_macro_definitions() const; /// /// Gets a list of pragma directives that occured. /// std::vector> used_pragma_directives() const { return _used_pragmas; } private: struct if_level { bool value; bool skipping; token pp_token; size_t input_index; }; struct input_level { std::string name; std::unique_ptr lexer; token next_token; std::unordered_set hidden_macros; }; void error(const location &location, const std::string &message); void warning(const location &location, const std::string &message); void push(std::string input, const std::string &name = std::string()); bool peek(tokenid tokid) const; void consume(); void consume_until(tokenid tokid); bool accept(tokenid tokid, bool ignore_whitespace = true); bool expect(tokenid tokid); void parse(); void parse_def(); void parse_undef(); void parse_if(); void parse_ifdef(); void parse_ifndef(); void parse_elif(); void parse_else(); void parse_endif(); void parse_error(); void parse_warning(); void parse_pragma(); void parse_include(); bool evaluate_expression(); bool evaluate_identifier_as_macro(); bool is_defined(const std::string &name) const; void expand_macro(const std::string &name, const macro ¯o, const std::vector &arguments); void create_macro_replacement_list(macro ¯o); bool _success = true; std::string _output, _errors; std::string _current_token_raw_data; reshadefx::token _token; location _output_location; std::vector _input_stack; size_t _next_input_index = 0; size_t _current_input_index = 0; std::vector _if_stack; unsigned short _recursion_count = 0; std::unordered_set _used_macros; std::unordered_map _macros; std::vector _include_paths; std::unordered_map _file_cache; std::vector> _used_pragmas; }; }