1754 explicit Impl(std::string source) : source(std::move(source)) {}
1758 std::vector<Token> body_tokens = tokenizer.
tokenize();
1760 std::erase_if(body_tokens, [](
const Token& t) {
1761 return t.
type == TokenType::EndOfFile;
1765 macro.
name = std::move(name);
1766 macro.is_function_like =
false;
1767 macro.body = std::move(body_tokens);
1769 macros.define(std::move(macro));
1773 output_lines.clear();
1774 line_mappings.clear();
1776 conditional_stack.clear();
1777 current_output_line = 1;
1778 included_libraries.clear();
1779 library_line_count = 0;
1782 std::vector<Token> tokens = tokenizer.
tokenize();
1784 processTokens(tokens);
1786 if (!conditional_stack.empty()) {
1787 addError(std::format(
1788 "Unclosed @ifdef/@ifndef directive started at line {}",
1789 conditional_stack.back().start_line),
1790 tokens.empty() ? 0 : tokens.back().line);
1794 result.
success = errors.empty();
1799 std::ostringstream oss;
1800 for (
size_t i = 0; i < output_lines.size(); ++i) {
1801 oss << output_lines[i];
1802 if (i < output_lines.size() - 1) {
1806 result.
source = oss.str();
1812 struct ConditionalBlock {
1815 bool had_true_branch;
1820 std::vector<std::string> output_lines;
1821 std::vector<LineMapping> line_mappings;
1822 std::vector<std::string> errors;
1823 std::vector<ConditionalBlock> conditional_stack;
1824 int current_output_line = 1;
1825 std::set<std::string_view, std::less<>> included_libraries;
1826 int library_line_count = 0;
1828 void processTokens(std::vector<Token>& tokens) {
1829 std::vector<Token> current_line_tokens;
1830 int current_line_number = 1;
1832 for (
const Token& tok : tokens) {
1833 if (tok.
type == TokenType::Newline) {
1834 processLineTokens(current_line_tokens, current_line_number);
1835 current_line_tokens.clear();
1836 current_line_number = tok.line + 1;
1840 if (tok.
type != TokenType::EndOfFile) {
1841 current_line_tokens.push_back(tok);
1847 if (!current_line_tokens.empty()) {
1848 processLineTokens(current_line_tokens, current_line_number);
1852 void processLineTokens(std::vector<Token>& line_tokens,
int line_number) {
1853 if (line_tokens.empty()) {
1854 addOutputLine(
"", line_number);
1858 size_t first_non_ws = 0;
1859 while (first_non_ws < line_tokens.size() &&
1860 (line_tokens[first_non_ws].type == TokenType::Whitespace ||
1861 line_tokens[first_non_ws].type == TokenType::Comment)) {
1865 if (first_non_ws >= line_tokens.size()) {
1866 addOutputLine(
"", line_number);
1870 const Token& first_tok = line_tokens[first_non_ws];
1871 if (first_tok.
type >= TokenType::AtDefine &&
1872 first_tok.
type <= TokenType::AtRequires) {
1873 handleDirective(line_tokens, line_number);
1874 addOutputLine(
"", line_number);
1875 }
else if (!isCurrentBlockActive()) {
1876 addOutputLine(
"", line_number);
1879 preprocessor::Expander expander(macros);
1880 std::vector<Token> expanded = expander.expand(line_tokens);
1881 auto expansions = expander.getExpansions();
1883 std::string line_text;
1884 int current_column = 1;
1885 for (
const auto& tok : expanded) {
1886 if (tok.
type == TokenType::BeginMacroExpansion) {
1887 if (tok.expansion_idx < expansions.size()) {
1888 expansions[tok.expansion_idx]
1889 .preprocessed_start_column = current_column;
1891 }
else if (tok.
type == TokenType::EndMacroExpansion) {
1892 if (tok.expansion_idx < expansions.size()) {
1893 expansions[tok.expansion_idx]
1894 .preprocessed_end_column = current_column;
1897 line_text += tok.
text;
1898 current_column +=
static_cast<int>(tok.
text.length());
1902 addOutputLine(line_text, line_number);
1904 if (!line_mappings.empty()) {
1905 line_mappings.back().expansions.insert(
1906 line_mappings.back().expansions.end(),
1907 expansions.begin(), expansions.end());
1909 }
catch (
const PreprocessorError& e) {
1910 addError(e.what(), line_number);
1911 addOutputLine(
"", line_number);
1916 void handleDirective(std::vector<Token>& line_tokens,
int line_number) {
1917 preprocessor::TokenStream stream(line_tokens);
1919 stream.skipWhitespace();
1920 if (stream.is_eof()) {
1924 const Token& directive_tok = stream.consume();
1926 switch (directive_tok.
type) {
1927 case TokenType::AtDefine:
1928 if (isCurrentBlockActive()) {
1929 handleDefine(stream, line_number);
1932 case TokenType::AtUndef:
1933 if (isCurrentBlockActive()) {
1934 handleUndef(stream, line_number);
1937 case TokenType::AtIfdef:
1938 handleIfdef(stream, line_number,
true);
1940 case TokenType::AtIfndef:
1941 handleIfdef(stream, line_number,
false);
1943 case TokenType::AtIf:
1944 handleIf(stream, line_number);
1946 case TokenType::AtElse:
1947 handleElse(line_number);
1949 case TokenType::AtEndif:
1950 handleEndif(line_number);
1952 case TokenType::AtError:
1953 if (isCurrentBlockActive()) {
1954 handleError(stream, line_number);
1957 case TokenType::AtRequires:
1958 if (isCurrentBlockActive()) {
1959 handleRequires(stream, line_number);
1963 if (isCurrentBlockActive()) {
1965 std::format(
"Unknown directive '{}'", directive_tok.
text),
1972 void handleDefine(preprocessor::TokenStream& stream,
int line_number) {
1973 stream.skipWhitespace();
1975 if (stream.is_eof() || stream.peek().type != TokenType::Identifier) {
1976 addError(
"@define requires a macro name", line_number);
1980 std::string name = stream.consume().
text;
1982 preprocessor::Macro macro;
1984 macro.is_function_like =
false;
1986 if (!stream.is_eof() && stream.peek().type == TokenType::Lparen) {
1987 macro.is_function_like =
true;
1990 stream.skipWhitespace();
1991 while (!stream.is_eof() &&
1992 stream.peek().type != TokenType::Rparen) {
1993 stream.skipWhitespace();
1995 if (stream.peek().type != TokenType::Identifier) {
1996 addError(
"Expected parameter name in macro definition",
2001 std::string param = stream.consume().text;
2003 if (std::ranges::contains(macro.params, param)) {
2005 std::format(
"Duplicate parameter name '{}' in macro "
2012 macro.params.push_back(param);
2013 stream.skipWhitespace();
2015 if (stream.is_eof()) {
2016 addError(
"Unterminated parameter list in macro definition",
2021 if (stream.peek().type == TokenType::Comma) {
2023 }
else if (stream.peek().type != TokenType::Rparen) {
2024 addError(
"Unterminated parameter list in macro definition",
2030 if (stream.is_eof() || stream.peek().type != TokenType::Rparen) {
2031 addError(
"Unterminated parameter list in macro definition",
2039 stream.skipWhitespace();
2040 std::vector<Token> body_tokens;
2041 while (!stream.is_eof()) {
2042 body_tokens.push_back(stream.consume());
2047 if (!macro.is_function_like && !body_tokens.empty()) {
2049 preprocessor::Expander expander(macros);
2050 body_tokens = expander.expand(body_tokens);
2052 preprocessor::Evaluator evaluator(body_tokens);
2053 auto evaluated = evaluator.tryEvaluate();
2057 body_tokens.clear();
2059 line_number, 0, *evaluated);
2065 macro.body = std::move(body_tokens);
2066 macros.define(std::move(macro));
2069 void handleUndef(preprocessor::TokenStream& stream,
int line_number) {
2070 stream.skipWhitespace();
2072 if (stream.is_eof() || stream.peek().type != TokenType::Identifier) {
2073 addError(
"@undef requires a macro name", line_number);
2077 std::string name = stream.consume().text;
2081 void handleIfdef(preprocessor::TokenStream& stream,
int line_number,
2082 bool check_defined) {
2083 stream.skipWhitespace();
2085 if (stream.is_eof() || stream.peek().type != TokenType::Identifier) {
2086 const char* directive = check_defined ?
"@ifdef" :
"@ifndef";
2087 addError(std::format(
"{} requires a macro name", directive),
2089 conditional_stack.push_back({line_number,
false,
true});
2093 std::string name = stream.consume().text;
2095 bool parent_active = isCurrentBlockActive();
2096 bool macro_defined = macros.contains(name);
2097 bool condition_met = check_defined ? macro_defined : !macro_defined;
2098 bool is_active = parent_active && condition_met;
2100 conditional_stack.push_back({line_number, is_active, condition_met});
2103 void handleIf(preprocessor::TokenStream& stream,
int line_number) {
2104 stream.skipWhitespace();
2106 std::vector<Token> expr_tokens;
2107 while (!stream.is_eof()) {
2108 expr_tokens.push_back(stream.consume());
2111 if (expr_tokens.empty()) {
2112 addError(
"@if requires an expression", line_number);
2113 conditional_stack.push_back({line_number,
false,
false});
2117 bool parent_active = isCurrentBlockActive();
2118 bool condition_met =
false;
2120 if (parent_active) {
2122 preprocessor::Expander expander(macros);
2123 expr_tokens = expander.expand(expr_tokens);
2125 preprocessor::Evaluator evaluator(expr_tokens);
2126 auto result = evaluator.evaluate();
2128 }
catch (
const PreprocessorError& e) {
2129 addError(e.what(), line_number);
2130 }
catch (
const std::runtime_error& e) {
2131 addError(std::format(
"Failed to evaluate @if expression: {}",
2137 bool is_active = parent_active && condition_met;
2138 conditional_stack.push_back({line_number, is_active, condition_met});
2141 void handleElse(
int line_number) {
2142 if (conditional_stack.empty()) {
2143 addError(
"@else without matching @ifdef/@ifndef", line_number);
2147 auto& block = conditional_stack.back();
2149 bool parent_active =
true;
2150 if (conditional_stack.size() > 1) {
2152 conditional_stack[conditional_stack.size() - 2].is_active;
2155 block.is_active = parent_active && !block.had_true_branch;
2158 void handleEndif(
int line_number) {
2159 if (conditional_stack.empty()) {
2160 addError(
"@endif without matching @ifdef/@ifndef", line_number);
2164 conditional_stack.pop_back();
2167 void handleError(preprocessor::TokenStream& stream,
int line_number) {
2168 stream.skipWhitespace();
2170 std::vector<Token> message_tokens;
2171 while (!stream.is_eof()) {
2172 message_tokens.push_back(stream.consume());
2176 std::string message =
2179 addError(message.empty() ?
"@error directive encountered"
2180 : std::format(
"@error: {}", message),
2184 void handleRequires(preprocessor::TokenStream& stream,
int line_number) {
2185 stream.skipWhitespace();
2187 if (stream.is_eof() || stream.peek().type != TokenType::Identifier) {
2188 addError(
"@requires requires a library name", line_number);
2192 std::string lib_name = stream.consume().text;
2194 std::vector<std::string_view> libraries_to_include;
2196 libraries_to_include =
2198 }
catch (
const std::exception& e) {
2199 addError(std::format(
"Failed to resolve library '{}': {}", lib_name,
2205 std::string_view explicitly_requested_lib = lib_name;
2207 for (
const auto& lib : libraries_to_include) {
2208 if (included_libraries.contains(lib)) {
2213 if (!lib_code_opt) {
2215 std::format(
"Library '{}' not found", std::string(lib)),
2220 std::string lib_code = std::string(lib_code_opt.value());
2223 for (
const auto& [name, macro] : macros) {
2224 lib_preprocessor.impl->macros.define(macro);
2226 auto lib_result = lib_preprocessor.process();
2228 if (!lib_result.success) {
2229 addError(std::format(
"Failed to preprocess library '{}': {}",
2231 lib_result.errors.empty()
2233 : lib_result.errors[0]),
2238 for (
const auto& [name, macro] : lib_preprocessor.impl->macros) {
2239 macros.define(macro);
2242 std::vector<std::string> lib_lines;
2243 std::istringstream lib_stream(lib_result.source);
2244 std::string lib_line;
2245 while (std::getline(lib_stream, lib_line)) {
2246 lib_lines.push_back(lib_line);
2249 if (lib == explicitly_requested_lib) {
2252 std::string lib_name_upper;
2253 for (
char c : lib) {
2254 lib_name_upper +=
static_cast<char>(std::toupper(c));
2257 bool is_expr = macros.contains(
"__EXPR__");
2258 bool is_single_expr = macros.contains(
"__SINGLEEXPR__");
2260 for (
const auto& exported : *exports_opt) {
2270 preprocessor::Macro alias_macro;
2271 alias_macro.name = std::string(exported.name);
2273 if (exported.param_count == 0) {
2274 std::string body_str;
2275 if (!exported.internal_name_override.empty()) {
2276 body_str = std::string(
2277 exported.internal_name_override);
2279 body_str = std::format(
2280 "___STDLIB_{}_{}", lib_name_upper,
2281 std::string(exported.name));
2284 alias_macro.body = tokenizer.tokenize();
2285 std::erase_if(alias_macro.body, [](
const Token& t) {
2286 return t.type == TokenType::EndOfFile;
2288 alias_macro.is_function_like =
false;
2290 std::string internal_name;
2291 if (!exported.internal_name_override.empty()) {
2292 internal_name = std::string(
2293 exported.internal_name_override);
2295 internal_name = std::format(
2296 "___stdlib_{}_{}", std::string(lib),
2297 std::string(exported.name));
2300 alias_macro.is_function_like =
true;
2301 for (
int i = 0; i < exported.param_count; ++i) {
2302 alias_macro.params.push_back(
2303 std::format(
"__arg{}", i));
2306 std::string body_str = internal_name +
"(";
2307 for (
int i = 0; i < exported.param_count; ++i) {
2311 body_str += std::format(
"__arg{}", i);
2316 alias_macro.body = tokenizer.tokenize();
2317 std::erase_if(alias_macro.body, [](
const Token& t) {
2318 return t.type == TokenType::EndOfFile;
2322 macros.define(std::move(alias_macro));
2327 for (
const auto& lib_line : lib_lines | std::views::reverse) {
2328 output_lines.insert(output_lines.begin(), lib_line);
2330 LineMapping mapping;
2331 mapping.preprocessed_line = 1;
2332 mapping.original_line = -1;
2333 line_mappings.insert(line_mappings.begin(), mapping);
2336 library_line_count +=
static_cast<int>(lib_lines.size());
2338 for (
size_t i = lib_lines.size(); i < line_mappings.size(); ++i) {
2339 if (line_mappings[i].original_line > 0) {
2340 line_mappings[i].preprocessed_line +=
2341 static_cast<int>(lib_lines.size());
2345 for (
size_t i = 0; i < lib_lines.size() && i < line_mappings.size();
2347 line_mappings[i].preprocessed_line =
static_cast<int>(i + 1);
2350 current_output_line +=
static_cast<int>(lib_lines.size());
2352 included_libraries.insert(lib);
2356 [[nodiscard]]
bool isCurrentBlockActive()
const {
2357 return std::ranges::all_of(conditional_stack, [](
const auto& block) {
2358 return block.is_active;
2362 void addError(
const std::string& message,
int line) {
2363 errors.push_back(std::format(
"Line {}: {}", line, message));
2366 void addOutputLine(
const std::string& line,
int original_line) {
2367 output_lines.push_back(line);
2369 LineMapping mapping;
2370 mapping.preprocessed_line = current_output_line;
2371 mapping.original_line = original_line;
2373 line_mappings.push_back(mapping);
2374 current_output_line++;