duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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 &macro);
     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 &macro, const std::vector<std::string> &arguments);
    155 		void create_macro_replacement_list(macro &macro);
    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 }