diff options
author | Ali Mohammad Pur <ali.mpfard@gmail.com> | 2021-07-28 05:04:39 +0430 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-08-02 01:03:59 +0200 |
commit | dc68c765b7fc945e7bbd743b82113927cd49748e (patch) | |
tree | 10f5ea83a5f299137d9ac4a63391a7894557d916 | |
parent | 8fefbfd5ace73fb5a55565e9178a94c26b97cc06 (diff) | |
download | serenity-dc68c765b7fc945e7bbd743b82113927cd49748e.zip |
LibCpp: Correctly parse lines that end in '\'
Such lines should be considered to be joined into the next line.
This makes multiline preprocessor stuff "work".
-rw-r--r-- | Userland/Libraries/LibCpp/Lexer.cpp | 12 | ||||
-rw-r--r-- | Userland/Libraries/LibCpp/Preprocessor.cpp | 45 | ||||
-rw-r--r-- | Userland/Utilities/cpp-parser.cpp | 12 |
3 files changed, 62 insertions, 7 deletions
diff --git a/Userland/Libraries/LibCpp/Lexer.cpp b/Userland/Libraries/LibCpp/Lexer.cpp index 4f1cf49289..d3703db829 100644 --- a/Userland/Libraries/LibCpp/Lexer.cpp +++ b/Userland/Libraries/LibCpp/Lexer.cpp @@ -556,8 +556,16 @@ Vector<Token> Lexer::lex() begin_token(); } } else { - while (peek() && peek() != '\n') - consume(); + while (peek()) { + if (peek() == '\\' && peek(1) == '\n') { + consume(); + consume(); + } else if (peek() == '\n') { + break; + } else { + consume(); + } + } commit_token(Token::Type::PreprocessorStatement); } diff --git a/Userland/Libraries/LibCpp/Preprocessor.cpp b/Userland/Libraries/LibCpp/Preprocessor.cpp index 6dbb601201..9428f7ee0c 100644 --- a/Userland/Libraries/LibCpp/Preprocessor.cpp +++ b/Userland/Libraries/LibCpp/Preprocessor.cpp @@ -15,7 +15,25 @@ Preprocessor::Preprocessor(const String& filename, const StringView& program) : m_filename(filename) , m_program(program) { - m_lines = m_program.split_view('\n', true); + GenericLexer program_lexer { m_program }; + for (;;) { + if (program_lexer.is_eof()) + break; + auto line = program_lexer.consume_until('\n'); + bool has_multiline = false; + while (line.ends_with('\\') && !program_lexer.is_eof()) { + auto continuation = program_lexer.consume_until('\n'); + line = StringView { line.characters_without_null_termination(), line.length() + continuation.length() + 1 }; + // Append an empty line to keep the line count correct. + m_lines.append({}); + has_multiline = true; + } + + if (has_multiline) + m_lines.last() = line; + else + m_lines.append(line); + } } const String& Preprocessor::process() @@ -45,9 +63,28 @@ const String& Preprocessor::process() static void consume_whitespace(GenericLexer& lexer) { - lexer.ignore_while([](char ch) { return isspace(ch); }); - if (lexer.peek() == '/' && lexer.peek(1) == '/') - lexer.ignore_until([](char ch) { return ch == '\n'; }); + auto ignore_line = [&] { + for (;;) { + if (lexer.consume_specific("\\\n"sv)) { + lexer.ignore(2); + } else { + lexer.ignore_until('\n'); + break; + } + } + }; + for (;;) { + if (lexer.consume_specific("//"sv)) + ignore_line(); + else if (lexer.consume_specific("/*"sv)) + lexer.ignore_until("*/"); + else if (lexer.next_is("\\\n"sv)) + lexer.ignore(2); + else if (lexer.is_eof() || !lexer.next_is(isspace)) + break; + else + lexer.ignore(); + } } Preprocessor::PreprocessorKeyword Preprocessor::handle_preprocessor_line(const StringView& line) diff --git a/Userland/Utilities/cpp-parser.cpp b/Userland/Utilities/cpp-parser.cpp index 76bf57bdfa..471a2ef7d1 100644 --- a/Userland/Utilities/cpp-parser.cpp +++ b/Userland/Utilities/cpp-parser.cpp @@ -13,7 +13,9 @@ int main(int argc, char** argv) Core::ArgsParser args_parser; const char* path = nullptr; bool tokens_mode = false; + bool preprocess_mode = false; args_parser.add_option(tokens_mode, "Print Tokens", "tokens", 'T'); + args_parser.add_option(preprocess_mode, "Print Preprocessed source", "preprocessed", 'P'); args_parser.add_positional_argument(path, "Cpp File", "cpp-file", Core::ArgsParser::Required::No); args_parser.parse(argc, argv); @@ -26,7 +28,15 @@ int main(int argc, char** argv) } auto content = file->read_all(); StringView content_view(content); - ::Cpp::Parser parser(content_view, path); + + ::Cpp::Preprocessor processor(path, content_view); + auto preprocessed_content = processor.process(); + if (preprocess_mode) { + outln(preprocessed_content); + return 0; + } + + ::Cpp::Parser parser(preprocessed_content, path); if (tokens_mode) { parser.print_tokens(); return 0; |