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
14CParserFacade::CParserFacade(std::filesystem::path file_path, gsl::not_null<const TextFormatter *> format)
15 : file_path_{std::move(file_path)}, format_{format}
16{
17}
18
19ChainableResult<void> CParserFacade::ensure_loaded()
20{
21 if (loaded_) {
22 return {};
23 }
24
25 if (load_failed_) {
26 return load_error_;
27 }
28
29 // Attempt to load the file
30 if (!std::filesystem::exists(file_path_)) {
31 load_failed_ = true;
32 load_error_ =
33 FormattableError{format_->format("{}: file not found", FormatParam{file_path_.string(), Style::bold})};
34 return load_error_;
35 }
36
37 std::ifstream file{file_path_};
38 if (!file.is_open()) {
39 load_failed_ = true;
40 load_error_ =
41 FormattableError{format_->format("{}: failed to open file", FormatParam{file_path_.string(), Style::bold})};
42 return load_error_;
43 }
44
45 // Read entire file content
46 std::ostringstream buffer;
47 buffer << file.rdbuf();
48 content_ = buffer.str();
49
50 // Split content into lines for FileHighlightPrinter
51 file_lines_.clear();
52 std::istringstream line_stream{content_};
53 std::string line;
54 while (std::getline(line_stream, line)) {
55 line = trim_line_ending(line);
56 file_lines_.push_back(line);
57 }
58
59 // Create context for rich error formatting
60 context_ = std::make_unique<CParserContext>(&file_lines_, format_, file_path_.string());
61
62 loaded_ = true;
63 return {};
64}
65
67{
68 auto load_result = ensure_loaded();
69 if (!load_result.has_value()) {
71 FormattableError{format_->format(
72 "{}: failed to parse #define statements", FormatParam{file_path_.string(), Style::bold})},
73 load_result};
74 }
75
76 // Lex the content
77 Lexer lexer{format_, content_, context_.get()};
78 auto lex_result = lexer.lex();
79 if (!lex_result.has_value()) {
81 FormattableError{format_->format(
82 "{}: failed to parse #define statements", FormatParam{file_path_.string(), Style::bold})},
83 lex_result};
84 }
85
86 // Parse the tokens
87 Parser parser{format_, std::move(lex_result).value(), context_.get()};
88 auto parse_result = parser.parse_defines();
89 if (!parse_result.has_value()) {
91 FormattableError{format_->format(
92 "{}: failed to parse #define statements", FormatParam{file_path_.string(), Style::bold})},
93 parse_result};
94 }
95
96 return std::move(parse_result).value();
97}
98
100{
101 auto load_result = ensure_loaded();
102 if (!load_result.has_value()) {
105 format_->format("{}: failed to parse enums", FormatParam{file_path_.string(), Style::bold})},
106 load_result};
107 }
108
109 // Lex the content
110 Lexer lexer{format_, content_, context_.get()};
111 auto lex_result = lexer.lex();
112 if (!lex_result.has_value()) {
115 format_->format("{}: failed to parse enums", FormatParam{file_path_.string(), Style::bold})},
116 lex_result};
117 }
118
119 // Parse the tokens
120 Parser parser{format_, std::move(lex_result).value(), context_.get()};
121 auto parse_result = parser.parse_enums();
122 if (!parse_result.has_value()) {
125 format_->format("{}: failed to parse enums", FormatParam{file_path_.string(), Style::bold})},
126 parse_result};
127 }
128
129 return std::move(parse_result).value();
130}
131
133CParserFacade::parse_pointer_arrays(const std::optional<std::string> &name_prefix)
134{
135 auto load_result = ensure_loaded();
136 if (!load_result.has_value()) {
139 format_->format("{}: failed to parse pointer arrays", FormatParam{file_path_.string(), Style::bold})},
140 load_result};
141 }
142
143 // Lex the content
144 Lexer lexer{format_, content_, context_.get()};
145 auto lex_result = lexer.lex();
146 if (!lex_result.has_value()) {
149 format_->format("{}: failed to parse pointer arrays", FormatParam{file_path_.string(), Style::bold})},
150 lex_result};
151 }
152
153 // Parse the tokens
154 Parser parser{format_, std::move(lex_result).value(), context_.get()};
155 auto parse_result = parser.parse_pointer_arrays();
156 if (!parse_result.has_value()) {
159 format_->format("{}: failed to parse pointer arrays", FormatParam{file_path_.string(), Style::bold})},
160 parse_result};
161 }
162
163 // Apply name prefix filter if provided
164 auto arrays = std::move(parse_result).value();
165 if (name_prefix.has_value()) {
166 std::erase_if(
167 arrays, [&](const ArrayDeclaration &arr) { return !arr.name().starts_with(name_prefix.value()); });
168 }
169
170 return arrays;
171}
172
174CParserFacade::parse_functions(const std::optional<std::string> &name_prefix)
175{
176 auto load_result = ensure_loaded();
177 if (!load_result.has_value()) {
180 format_->format("{}: failed to parse functions", FormatParam{file_path_.string(), Style::bold})},
181 load_result};
182 }
183
184 // Lex the content
185 Lexer lexer{format_, content_, context_.get()};
186 auto lex_result = lexer.lex();
187 if (!lex_result.has_value()) {
190 format_->format("{}: failed to parse functions", FormatParam{file_path_.string(), Style::bold})},
191 lex_result};
192 }
193
194 // Parse the tokens
195 Parser parser{format_, std::move(lex_result).value(), context_.get()};
196 auto parse_result = parser.parse_functions();
197 if (!parse_result.has_value()) {
200 format_->format("{}: failed to parse functions", FormatParam{file_path_.string(), Style::bold})},
201 parse_result};
202 }
203
204 // Apply name prefix filter if provided
205 auto functions = std::move(parse_result).value();
206 if (name_prefix.has_value()) {
207 std::erase_if(
208 functions, [&](const FunctionDefinition &func) { return !func.name().starts_with(name_prefix.value()); });
209 }
210
211 return functions;
212}
213
215CParserFacade::parse_struct_variables(const std::optional<std::string> &name_prefix)
216{
217 auto load_result = ensure_loaded();
218 if (!load_result.has_value()) {
221 format_->format("{}: failed to parse struct variables", FormatParam{file_path_.string(), Style::bold})},
222 load_result};
223 }
224
225 // Lex the content
226 Lexer lexer{format_, content_, context_.get()};
227 auto lex_result = lexer.lex();
228 if (!lex_result.has_value()) {
231 format_->format("{}: failed to parse struct variables", FormatParam{file_path_.string(), Style::bold})},
232 lex_result};
233 }
234
235 // Parse the tokens
236 Parser parser{format_, std::move(lex_result).value(), context_.get()};
237 auto parse_result = parser.parse_struct_variables();
238 if (!parse_result.has_value()) {
241 format_->format("{}: failed to parse struct variables", FormatParam{file_path_.string(), Style::bold})},
242 parse_result};
243 }
244
245 // Apply name prefix filter if provided
246 auto structs = std::move(parse_result).value();
247 if (name_prefix.has_value()) {
248 std::erase_if(structs, [&](const StructVariableDeclaration &s) {
249 return !s.variable_name().starts_with(name_prefix.value());
250 });
251 }
252
253 return structs;
254}
255
257CParserFacade::parse_struct_initializers(const std::optional<std::string> &name_prefix)
258{
259 auto load_result = ensure_loaded();
260 if (!load_result.has_value()) {
262 FormattableError{format_->format(
263 "{}: failed to parse struct initializers", FormatParam{file_path_.string(), Style::bold})},
264 load_result};
265 }
266
267 // Lex the content
268 Lexer lexer{format_, content_, context_.get()};
269 auto lex_result = lexer.lex();
270 if (!lex_result.has_value()) {
272 FormattableError{format_->format(
273 "{}: failed to parse struct initializers", FormatParam{file_path_.string(), Style::bold})},
274 lex_result};
275 }
276
277 // Parse the tokens
278 Parser parser{format_, std::move(lex_result).value(), context_.get()};
279 auto parse_result = parser.parse_struct_initializers();
280 if (!parse_result.has_value()) {
282 FormattableError{format_->format(
283 "{}: failed to parse struct initializers", FormatParam{file_path_.string(), Style::bold})},
284 parse_result};
285 }
286
287 // Apply name prefix filter if provided
288 auto structs = std::move(parse_result).value();
289 if (name_prefix.has_value()) {
290 std::erase_if(structs, [&](const StructInitializerDeclaration &s) {
291 return !s.variable_name().starts_with(name_prefix.value());
292 });
293 }
294
295 return structs;
296}
297
299CParserFacade::parse_incbin_arrays(const std::optional<std::string> &name_prefix)
300{
301 auto load_result = ensure_loaded();
302 if (!load_result.has_value()) {
305 format_->format("{}: failed to parse INCBIN arrays", FormatParam{file_path_.string(), Style::bold})},
306 load_result};
307 }
308
309 // Lex the content
310 Lexer lexer{format_, content_, context_.get()};
311 auto lex_result = lexer.lex();
312 if (!lex_result.has_value()) {
315 format_->format("{}: failed to parse INCBIN arrays", FormatParam{file_path_.string(), Style::bold})},
316 lex_result};
317 }
318
319 // Parse the tokens
320 Parser parser{format_, std::move(lex_result).value(), context_.get()};
321 auto parse_result = parser.parse_incbin_arrays();
322 if (!parse_result.has_value()) {
325 format_->format("{}: failed to parse INCBIN arrays", FormatParam{file_path_.string(), Style::bold})},
326 parse_result};
327 }
328
329 // Apply name prefix filter if provided
330 auto incbins = std::move(parse_result).value();
331 if (name_prefix.has_value()) {
332 std::erase_if(incbins, [&](const IncbinDeclaration &inc) {
333 return !inc.variable_name().starts_with(name_prefix.value());
334 });
335 }
336
337 return incbins;
338}
339
341{
342 // Ensure defines are cached
343 if (!cached_defines_.has_value()) {
344 auto result = parse_defines();
345 if (!result.has_value()) {
347 FormattableError{format_->format(
348 "{}: failed to find #define '{}'",
349 FormatParam{file_path_.string(), Style::bold},
350 FormatParam{define_name, Style::bold})},
351 result};
352 }
353 cached_defines_ = std::move(result.value());
354 }
355
356 // Search for the define
357 for (const auto &define : cached_defines_.value()) {
358 if (define.name() == define_name) {
359 return std::optional{define};
360 }
361 }
362
363 // Not found - this is not an error, just return nullopt
364 return std::optional<DefineStatement>{std::nullopt};
365}
366
367const std::vector<std::string> &CParserFacade::file_lines() const
368{
369 return file_lines_;
370}
371
372} // 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< FunctionDefinition > > parse_functions(const std::optional< std::string > &name_prefix=std::nullopt)
Parses function definitions 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.
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:63
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:44
Parser for C preprocessor constructs.
Definition parser.hpp:54
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.