summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2021-07-28 05:04:39 +0430
committerAndreas Kling <kling@serenityos.org>2021-08-02 01:03:59 +0200
commitdc68c765b7fc945e7bbd743b82113927cd49748e (patch)
tree10f5ea83a5f299137d9ac4a63391a7894557d916 /Userland
parent8fefbfd5ace73fb5a55565e9178a94c26b97cc06 (diff)
downloadserenity-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".
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibCpp/Lexer.cpp12
-rw-r--r--Userland/Libraries/LibCpp/Preprocessor.cpp45
-rw-r--r--Userland/Utilities/cpp-parser.cpp12
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;