Porytiles
Loading...
Searching...
No Matches
c_parser_facade.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <fstream>
5#include <sstream>
6
11
12namespace porytiles {
13
14namespace {
15
16[[nodiscard]] std::unordered_map<std::string, std::int64_t> resolved_define_values(const TolerantDefineScan &scan)
17{
18 std::unordered_map<std::string, std::int64_t> values;
19 for (const DefineStatement &define : scan.defines) {
20 if (define.has_int_value()) {
21 values.insert_or_assign(define.name(), define.int_value());
22 }
23 }
24 return values;
25}
26
27[[nodiscard]] ChainableResult<TolerantDefineScan> scan_define_values_for_enums(
28 gsl::not_null<const TextFormatter *> format,
29 const std::string &content,
30 const CParserContext *context,
31 const std::unordered_map<std::string, std::int64_t> &seed_symbols)
32{
33 Lexer lexer{format, content, context};
34 auto lex_result = lexer.lex();
35 if (!lex_result.has_value()) {
36 return ChainableResult<TolerantDefineScan>{lex_result};
37 }
38
39 Parser parser{format, std::move(lex_result).value(), context};
40 parser.seed_symbols(seed_symbols);
41 TolerantDefineScan scan = parser.parse_defines_tolerant();
42 scan.ambiguous_values = parser.ambiguous_defines();
43 return scan;
44}
45
46} // namespace
47
48CParserFacade::CParserFacade(std::filesystem::path file_path, gsl::not_null<const TextFormatter *> format)
49 : file_path_{std::move(file_path)}, format_{format}
50{
51}
52
54 std::filesystem::path file_path,
55 gsl::not_null<const TextFormatter *> format,
56 std::unordered_map<std::string, std::int64_t> seed_symbols)
57 : file_path_{std::move(file_path)}, format_{format}, seed_symbols_{std::move(seed_symbols)}
58{
59}
60
61Parser CParserFacade::make_seeded_parser(std::vector<Token> tokens)
62{
63 Parser parser{format_, std::move(tokens), context_.get()};
64 if (!seed_symbols_.empty()) {
65 parser.seed_symbols(seed_symbols_);
66 }
67 return parser;
68}
69
70ChainableResult<void> CParserFacade::ensure_loaded()
71{
72 if (loaded_) {
73 return {};
74 }
75
76 if (load_failed_) {
77 return load_error_;
78 }
79
80 // Attempt to load the file
81 if (!std::filesystem::exists(file_path_)) {
82 load_failed_ = true;
83 load_error_ =
84 FormattableError{format_->format("{}: file not found", FormatParam{file_path_.string(), Style::bold})};
85 return load_error_;
86 }
87
88 std::ifstream file{file_path_};
89 if (!file.is_open()) {
90 load_failed_ = true;
91 load_error_ =
92 FormattableError{format_->format("{}: failed to open file", FormatParam{file_path_.string(), Style::bold})};
93 return load_error_;
94 }
95
96 // Read entire file content
97 std::ostringstream buffer;
98 buffer << file.rdbuf();
99 content_ = buffer.str();
100
101 // Split content into lines for FileHighlightPrinter
102 file_lines_.clear();
103 std::istringstream line_stream{content_};
104 std::string line;
105 while (std::getline(line_stream, line)) {
106 line = trim_line_ending(line);
107 file_lines_.push_back(line);
108 }
109
110 // Create context for rich error formatting
111 context_ = std::make_unique<CParserContext>(&file_lines_, format_, file_path_.string());
112
113 loaded_ = true;
114 return {};
115}
116
118{
119 auto load_result = ensure_loaded();
120 if (!load_result.has_value()) {
122 FormattableError{format_->format(
123 "{}: failed to parse #define statements", FormatParam{file_path_.string(), Style::bold})},
124 load_result};
125 }
126
127 // Lex the content
128 Lexer lexer{format_, content_, context_.get()};
129 auto lex_result = lexer.lex();
130 if (!lex_result.has_value()) {
132 FormattableError{format_->format(
133 "{}: failed to parse #define statements", FormatParam{file_path_.string(), Style::bold})},
134 lex_result};
135 }
136
137 // Parse the tokens
138 auto parser = make_seeded_parser(std::move(lex_result).value());
139 auto parse_result = parser.parse_defines();
140 if (!parse_result.has_value()) {
142 FormattableError{format_->format(
143 "{}: failed to parse #define statements", FormatParam{file_path_.string(), Style::bold})},
144 parse_result};
145 }
146
147 return std::move(parse_result).value();
148}
149
151{
152 auto load_result = ensure_loaded();
153 if (!load_result.has_value()) {
156 format_->format("{}: failed to parse enums", FormatParam{file_path_.string(), Style::bold})},
157 load_result};
158 }
159
160 // Enum values commonly refer to integer #defines in the same header. Use a tolerant define scan so unrelated
161 // function-like or otherwise unevaluable macros do not prevent the enum parser from receiving the values it needs.
162 auto defines_result = scan_define_values_for_enums(format_, content_, context_.get(), seed_symbols_);
163 if (!defines_result.has_value()) {
166 format_->format("{}: failed to parse enums", FormatParam{file_path_.string(), Style::bold})},
167 defines_result};
168 }
169
170 // Lex the content
171 Lexer lexer{format_, content_, context_.get()};
172 auto lex_result = lexer.lex();
173 if (!lex_result.has_value()) {
176 format_->format("{}: failed to parse enums", FormatParam{file_path_.string(), Style::bold})},
177 lex_result};
178 }
179
180 // Parse the tokens
181 auto parser = make_seeded_parser(std::move(lex_result).value());
182 parser.seed_values(resolved_define_values(defines_result.value()));
183 auto parse_result = parser.parse_enums();
184 if (!parse_result.has_value()) {
187 format_->format("{}: failed to parse enums", FormatParam{file_path_.string(), Style::bold})},
188 parse_result};
189 }
190
191 return std::move(parse_result).value();
192}
193
195{
196 auto load_result = ensure_loaded();
197 if (!load_result.has_value()) {
199 FormattableError{format_->format(
200 "{}: failed to parse #define statements", FormatParam{file_path_.string(), Style::bold})},
201 load_result};
202 }
203
204 Lexer lexer{format_, content_, context_.get()};
205 auto lex_result = lexer.lex();
206 if (!lex_result.has_value()) {
208 FormattableError{format_->format(
209 "{}: failed to parse #define statements", FormatParam{file_path_.string(), Style::bold})},
210 lex_result};
211 }
212
213 auto parser = make_seeded_parser(std::move(lex_result).value());
214 TolerantDefineScan scan = parser.parse_defines_tolerant();
215 scan.ambiguous_values = parser.ambiguous_defines();
216 scan_warnings_.insert(scan_warnings_.end(), parser.scan_warnings().begin(), parser.scan_warnings().end());
217 return scan;
218}
219
221{
222 auto load_result = ensure_loaded();
223 if (!load_result.has_value()) {
226 format_->format("{}: failed to parse enums", FormatParam{file_path_.string(), Style::bold})},
227 load_result};
228 }
229
230 auto defines_result = scan_define_values_for_enums(format_, content_, context_.get(), seed_symbols_);
231 if (!defines_result.has_value()) {
234 format_->format("{}: failed to parse enums", FormatParam{file_path_.string(), Style::bold})},
235 defines_result};
236 }
237
238 Lexer lexer{format_, content_, context_.get()};
239 auto lex_result = lexer.lex();
240 if (!lex_result.has_value()) {
243 format_->format("{}: failed to parse enums", FormatParam{file_path_.string(), Style::bold})},
244 lex_result};
245 }
246
247 auto parser = make_seeded_parser(std::move(lex_result).value());
248 parser.seed_values(resolved_define_values(defines_result.value()));
249 TolerantEnumScan scan = parser.parse_enums_tolerant();
250 scan_warnings_.insert(scan_warnings_.end(), parser.scan_warnings().begin(), parser.scan_warnings().end());
251 return scan;
252}
253
255CParserFacade::parse_pointer_arrays(const std::optional<std::string> &name_prefix)
256{
257 auto load_result = ensure_loaded();
258 if (!load_result.has_value()) {
261 format_->format("{}: failed to parse pointer arrays", FormatParam{file_path_.string(), Style::bold})},
262 load_result};
263 }
264
265 // Lex the content
266 Lexer lexer{format_, content_, context_.get()};
267 auto lex_result = lexer.lex();
268 if (!lex_result.has_value()) {
271 format_->format("{}: failed to parse pointer arrays", FormatParam{file_path_.string(), Style::bold})},
272 lex_result};
273 }
274
275 // Parse the tokens
276 auto parser = make_seeded_parser(std::move(lex_result).value());
277 auto parse_result = parser.parse_pointer_arrays();
278 if (!parse_result.has_value()) {
281 format_->format("{}: failed to parse pointer arrays", FormatParam{file_path_.string(), Style::bold})},
282 parse_result};
283 }
284
285 // Apply name prefix filter if provided
286 auto arrays = std::move(parse_result).value();
287 if (name_prefix.has_value()) {
288 std::erase_if(
289 arrays, [&](const ArrayDeclaration &arr) { return !arr.name().starts_with(name_prefix.value()); });
290 }
291
292 return arrays;
293}
294
296CParserFacade::parse_functions(const std::optional<std::string> &name_prefix)
297{
298 auto load_result = ensure_loaded();
299 if (!load_result.has_value()) {
302 format_->format("{}: failed to parse functions", FormatParam{file_path_.string(), Style::bold})},
303 load_result};
304 }
305
306 // Lex the content
307 Lexer lexer{format_, content_, context_.get()};
308 auto lex_result = lexer.lex();
309 if (!lex_result.has_value()) {
312 format_->format("{}: failed to parse functions", FormatParam{file_path_.string(), Style::bold})},
313 lex_result};
314 }
315
316 // Parse the tokens
317 auto parser = make_seeded_parser(std::move(lex_result).value());
318 auto parse_result = parser.parse_functions();
319 if (!parse_result.has_value()) {
322 format_->format("{}: failed to parse functions", FormatParam{file_path_.string(), Style::bold})},
323 parse_result};
324 }
325
326 // Apply name prefix filter if provided
327 auto functions = std::move(parse_result).value();
328 if (name_prefix.has_value()) {
329 std::erase_if(
330 functions, [&](const FunctionDefinition &func) { return !func.name().starts_with(name_prefix.value()); });
331 }
332
333 return functions;
334}
335
337CParserFacade::parse_struct_variables(const std::optional<std::string> &name_prefix)
338{
339 auto load_result = ensure_loaded();
340 if (!load_result.has_value()) {
343 format_->format("{}: failed to parse struct variables", FormatParam{file_path_.string(), Style::bold})},
344 load_result};
345 }
346
347 // Lex the content
348 Lexer lexer{format_, content_, context_.get()};
349 auto lex_result = lexer.lex();
350 if (!lex_result.has_value()) {
353 format_->format("{}: failed to parse struct variables", FormatParam{file_path_.string(), Style::bold})},
354 lex_result};
355 }
356
357 // Parse the tokens
358 auto parser = make_seeded_parser(std::move(lex_result).value());
359 auto parse_result = parser.parse_struct_variables();
360 if (!parse_result.has_value()) {
363 format_->format("{}: failed to parse struct variables", FormatParam{file_path_.string(), Style::bold})},
364 parse_result};
365 }
366
367 // Apply name prefix filter if provided
368 auto structs = std::move(parse_result).value();
369 if (name_prefix.has_value()) {
370 std::erase_if(structs, [&](const StructVariableDeclaration &s) {
371 return !s.variable_name().starts_with(name_prefix.value());
372 });
373 }
374
375 return structs;
376}
377
379CParserFacade::parse_struct_initializers(const std::optional<std::string> &name_prefix)
380{
381 auto load_result = ensure_loaded();
382 if (!load_result.has_value()) {
384 FormattableError{format_->format(
385 "{}: failed to parse struct initializers", FormatParam{file_path_.string(), Style::bold})},
386 load_result};
387 }
388
389 // Lex the content
390 Lexer lexer{format_, content_, context_.get()};
391 auto lex_result = lexer.lex();
392 if (!lex_result.has_value()) {
394 FormattableError{format_->format(
395 "{}: failed to parse struct initializers", FormatParam{file_path_.string(), Style::bold})},
396 lex_result};
397 }
398
399 // Parse the tokens
400 auto parser = make_seeded_parser(std::move(lex_result).value());
401 auto parse_result = parser.parse_struct_initializers();
402 if (!parse_result.has_value()) {
404 FormattableError{format_->format(
405 "{}: failed to parse struct initializers", FormatParam{file_path_.string(), Style::bold})},
406 parse_result};
407 }
408
409 // Apply name prefix filter if provided
410 auto structs = std::move(parse_result).value();
411 if (name_prefix.has_value()) {
412 std::erase_if(structs, [&](const StructInitializerDeclaration &s) {
413 return !s.variable_name().starts_with(name_prefix.value());
414 });
415 }
416
417 return structs;
418}
419
421CParserFacade::parse_struct_definitions(const std::optional<std::string> &name_filter)
422{
423 auto load_result = ensure_loaded();
424 if (!load_result.has_value()) {
426 FormattableError{format_->format(
427 "{}: failed to parse struct definitions", FormatParam{file_path_.string(), Style::bold})},
428 load_result};
429 }
430
431 // Lex the content
432 Lexer lexer{format_, content_, context_.get()};
433 auto lex_result = lexer.lex();
434 if (!lex_result.has_value()) {
436 FormattableError{format_->format(
437 "{}: failed to parse struct definitions", FormatParam{file_path_.string(), Style::bold})},
438 lex_result};
439 }
440
441 // Parse the tokens
442 auto parser = make_seeded_parser(std::move(lex_result).value());
443 auto parse_result = parser.parse_struct_definitions();
444 if (!parse_result.has_value()) {
446 FormattableError{format_->format(
447 "{}: failed to parse struct definitions", FormatParam{file_path_.string(), Style::bold})},
448 parse_result};
449 }
450
451 // Apply exact-name filter if provided
452 auto definitions = std::move(parse_result).value();
453 if (name_filter.has_value()) {
454 std::erase_if(definitions, [&](const StructDefinition &def) { return def.name != name_filter.value(); });
455 }
456
457 return definitions;
458}
459
461CParserFacade::parse_incbin_arrays(const std::optional<std::string> &name_prefix)
462{
463 auto load_result = ensure_loaded();
464 if (!load_result.has_value()) {
467 format_->format("{}: failed to parse INCBIN arrays", FormatParam{file_path_.string(), Style::bold})},
468 load_result};
469 }
470
471 // Lex the content
472 Lexer lexer{format_, content_, context_.get()};
473 auto lex_result = lexer.lex();
474 if (!lex_result.has_value()) {
477 format_->format("{}: failed to parse INCBIN arrays", FormatParam{file_path_.string(), Style::bold})},
478 lex_result};
479 }
480
481 // Parse the tokens
482 auto parser = make_seeded_parser(std::move(lex_result).value());
483 auto parse_result = parser.parse_incbin_arrays();
484 if (!parse_result.has_value()) {
487 format_->format("{}: failed to parse INCBIN arrays", FormatParam{file_path_.string(), Style::bold})},
488 parse_result};
489 }
490
491 // Apply name prefix filter if provided
492 auto incbins = std::move(parse_result).value();
493 if (name_prefix.has_value()) {
494 std::erase_if(incbins, [&](const IncbinDeclaration &inc) {
495 return !inc.variable_name().starts_with(name_prefix.value());
496 });
497 }
498
499 return incbins;
500}
501
503CParserFacade::parse_indexed_arrays(const std::optional<std::string> &name_prefix)
504{
505 auto load_result = ensure_loaded();
506 if (!load_result.has_value()) {
509 format_->format("{}: failed to parse indexed arrays", FormatParam{file_path_.string(), Style::bold})},
510 load_result};
511 }
512
513 Lexer lexer{format_, content_, context_.get()};
514 auto lex_result = lexer.lex();
515 if (!lex_result.has_value()) {
518 format_->format("{}: failed to parse indexed arrays", FormatParam{file_path_.string(), Style::bold})},
519 lex_result};
520 }
521
522 auto parser = make_seeded_parser(std::move(lex_result).value());
523 auto parse_result = parser.parse_indexed_arrays();
524 if (!parse_result.has_value()) {
527 format_->format("{}: failed to parse indexed arrays", FormatParam{file_path_.string(), Style::bold})},
528 parse_result};
529 }
530
531 // Apply name prefix filter if provided
532 auto arrays = std::move(parse_result).value();
533 if (name_prefix.has_value()) {
534 std::erase_if(
535 arrays, [&](const IndexedArrayDeclaration &arr) { return !arr.name.starts_with(name_prefix.value()); });
536 }
537
538 return arrays;
539}
540
542{
543 // Ensure defines are cached
544 if (!cached_defines_.has_value()) {
545 auto result = parse_defines();
546 if (!result.has_value()) {
548 FormattableError{format_->format(
549 "{}: failed to find #define '{}'",
550 FormatParam{file_path_.string(), Style::bold},
551 FormatParam{define_name, Style::bold})},
552 result};
553 }
554 cached_defines_ = std::move(result.value());
555 }
556
557 // Search for the define
558 for (const auto &define : cached_defines_.value()) {
559 if (define.name() == define_name) {
560 return std::optional{define};
561 }
562 }
563
564 // Not found - this is not an error, just return nullopt
565 return std::optional<DefineStatement>{std::nullopt};
566}
567
568const std::vector<std::string> &CParserFacade::file_lines() const
569{
570 return file_lines_;
571}
572
573} // namespace porytiles
Represents a parsed C pointer array declaration.
const std::string & name() const
Returns the array variable name.
const std::vector< std::string > & file_lines() const
Returns the cached file lines.
ChainableResult< std::vector< IncbinDeclaration > > parse_incbin_arrays(const std::optional< std::string > &name_prefix=std::nullopt)
Parses INCBIN array declarations from the file.
ChainableResult< std::vector< EnumDeclaration > > parse_enums()
Parses all enum declarations from the file.
ChainableResult< std::vector< StructVariableDeclaration > > parse_struct_variables(const std::optional< std::string > &name_prefix=std::nullopt)
Parses struct variable declarations from the file.
ChainableResult< std::vector< DefineStatement > > parse_defines()
Parses all #define statements from the file.
ChainableResult< std::optional< DefineStatement > > find_define(const std::string &define_name)
Finds a specific #define statement by name.
CParserFacade(std::filesystem::path file_path, gsl::not_null< const TextFormatter * > format)
Constructs a facade for parsing the specified file.
ChainableResult< std::vector< StructDefinition > > parse_struct_definitions(const std::optional< std::string > &name_filter=std::nullopt)
Parses struct type definitions from the file.
ChainableResult< TolerantDefineScan > parse_defines_tolerant()
Parses all #define statements, tolerating individual evaluation failures.
ChainableResult< std::vector< FunctionDefinition > > parse_functions(const std::optional< std::string > &name_prefix=std::nullopt)
Parses function definitions from the file.
ChainableResult< std::vector< IndexedArrayDeclaration > > parse_indexed_arrays(const std::optional< std::string > &name_prefix=std::nullopt)
Parses designated (indexed) array declarations from the file.
ChainableResult< std::vector< StructInitializerDeclaration > > parse_struct_initializers(const std::optional< std::string > &name_prefix=std::nullopt)
Parses struct variable declarations with their designated initializer fields.
ChainableResult< std::vector< ArrayDeclaration > > parse_pointer_arrays(const std::optional< std::string > &name_prefix=std::nullopt)
Parses all pointer array declarations from the file.
ChainableResult< TolerantEnumScan > parse_enums_tolerant()
Parses all enum declarations, tolerating individual member evaluation failures.
A result type that maintains a chainable sequence of errors for debugging and error reporting.
A text parameter with associated styling for formatted output.
General-purpose error implementation with formatted message support.
Definition error.hpp:57
Represents a parsed C function definition.
const std::string & name() const
Returns the function name.
Represents a parsed INCBIN array declaration from C source code.
const std::string & variable_name() const
Returns the variable name.
Lexical analyzer for C/C++ source code.
Definition lexer.hpp:42
Parser for C preprocessor constructs.
Definition parser.hpp:58
Represents a parsed C struct variable declaration with its initializer fields.
const std::string & variable_name() const
Returns the variable name.
Represents a parsed C struct variable declaration.
const std::string & variable_name() const
Returns the variable name.
static const Style bold
Bold text formatting.
virtual std::string format(const std::string &format_str, const std::vector< FormatParam > &params) const
Formats a string with styled parameters using fmtlib syntax.
std::string & trim_line_ending(std::string &line)
Removes line ending characters from a string in-place.
Utility functions for string manipulation and formatting.
A parsed C array declaration that uses designated initializers.
A parsed C struct type definition.
The result of a tolerant #define scan.
std::unordered_set< std::string > ambiguous_values
The result of a tolerant enum scan.