Browse Source

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".
Ali Mohammad Pur 4 years ago
parent
commit
dc68c765b7

+ 10 - 2
Userland/Libraries/LibCpp/Lexer.cpp

@@ -556,8 +556,16 @@ Vector<Token> Lexer::lex()
                     begin_token();
                     begin_token();
                 }
                 }
             } else {
             } 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);
                 commit_token(Token::Type::PreprocessorStatement);
             }
             }

+ 41 - 4
Userland/Libraries/LibCpp/Preprocessor.cpp

@@ -15,7 +15,25 @@ Preprocessor::Preprocessor(const String& filename, const StringView& program)
     : m_filename(filename)
     : m_filename(filename)
     , m_program(program)
     , 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()
 const String& Preprocessor::process()
@@ -45,9 +63,28 @@ const String& Preprocessor::process()
 
 
 static void consume_whitespace(GenericLexer& lexer)
 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)
 Preprocessor::PreprocessorKeyword Preprocessor::handle_preprocessor_line(const StringView& line)

+ 11 - 1
Userland/Utilities/cpp-parser.cpp

@@ -13,7 +13,9 @@ int main(int argc, char** argv)
     Core::ArgsParser args_parser;
     Core::ArgsParser args_parser;
     const char* path = nullptr;
     const char* path = nullptr;
     bool tokens_mode = false;
     bool tokens_mode = false;
+    bool preprocess_mode = false;
     args_parser.add_option(tokens_mode, "Print Tokens", "tokens", 'T');
     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.add_positional_argument(path, "Cpp File", "cpp-file", Core::ArgsParser::Required::No);
     args_parser.parse(argc, argv);
     args_parser.parse(argc, argv);
 
 
@@ -26,7 +28,15 @@ int main(int argc, char** argv)
     }
     }
     auto content = file->read_all();
     auto content = file->read_all();
     StringView content_view(content);
     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) {
     if (tokens_mode) {
         parser.print_tokens();
         parser.print_tokens();
         return 0;
         return 0;