Трудности разбора комментариев, которые следуют после;в линии - PullRequest
0 голосов
/ 04 февраля 2019

Я анализирую файл с определениями функций.Поскольку функции могут быть написаны в несколько строк, я выполняю синтаксический анализ до тех пор, пока не обнаружу ;:

#include <iostream>
#include <string>

void removeLineBreaks(std::string &str)
{
    auto pos = str.find('\n');
    while (pos != std::string::npos)
    {
        str.replace(pos, 1, "");
        pos = str.find('\n', pos);
    }
}

int main()
{
    std::ifstream ifStream("a.pr");
    std::string sLine;
    const char sDelim(';');

    while (std::getline(ifStream, sLine, sDelim))
    {
        sLine += sDelim;
        removeLineBreaks(sLine);
        // process further
    }
}

Текст может выглядеть примерно так:

a=f(b,c); // comment
d=f(e,f);

Поскольку ячтение до ;, здесь я получаю две части:

a=f(b,c); и

// comment \n d=f(e,f);.

Если я позвоню removeLineBreaks на второй части,Станет // comment d=f(e,f);, поэтому мой парсер обработает комментарий.

Какие опции у меня есть, чтобы эта работа работала правильно?Я мог бы подумать об этом - перед вызовом removeLineBreaks в строке, получить строку до \n, и, если она начинается с //, вырезать эту часть из строки, и только затем вызвать removeLineBreaks.

Есть еще идеи?

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019

Это хак, но препроцессор C ++ удаляет комментарии, так что вы можете просто вызвать его перед анализом определений.Для gcc это g++ -E input.cpp.Препроцессор gcc оставит после обработки комментарии, с которыми вам придется иметь дело, но с ними гораздо проще работать, чем с комментариями в целом.

@ NathanOliver написал в комментариях:

Прочитайте всю строку, а затем работайте в обратном направлении, пока не нажмете;

Если вы знаете всех васдолжны иметь дело с объявлениями функций и комментариями, это, вероятно, самый простой и простой вариант.

Если вы хотите что-то более серьезное (т. е. реальный формат на самом деле может быть более сложным), то правильная абстракция - это посмотретьна это как серия жетонов.Вам придется взглянуть на первые несколько глав любой хорошей книги компиляторов для полного изложения, но если мы можем предположить, что ваш файл имеет только объявления и комментарии, вот простой способ начать.

ТамВот четыре категории токенов, которые вас интересуют: пробелы, комментарии, код и точка с запятой.По первым двум символам вы можете определить, какой из них у вас есть.

Например, если это ваш ввод:

a=f(b, 
   c); //comment
d=f(e,f);

Тогда мы бы хотели разбить это на

a=f(b,    // Code
\n        // Whitespace
c)        // Code
;         // Semicolon
          // Whitespace
//comment\n //Comment
d=f(e,f) // Code
;        // Semicolon

(Возможны и другие способы его разбить. Например, ["a=f(b,", "c)", ";", "d=f(e,f)", ";"] и просто пропустить некодовые вещи.)

Вы можете написать функцию, которая будет проходить символ за символом и возвращатьсамый большой кусок текста в этой категории вы можете найти.Тогда ваш «парсер» может просто несколько раз вызывать это, отбрасывать комментарии и пробелы и объединять объявления.Такие инструменты, как lex, могут сделать это за вас, хотя в этом случае, вероятно, так же легко освоить lex, как и самостоятельно.

0 голосов
/ 04 февраля 2019

Сначала необходимо удалить комментарии // из вашего ввода, и только после этого вы можете разделить их точкой с запятой.

Рассмотрим следующий ввод:

a=f(b,c); // Functions comments are not functions; a=F(b,c);

Если сначала разбить точку с запятой, а затем удалить комментарии, то вы получите две функции:

a=f(b,c);
a=F(b,c);

Но вы только хотите иметь первый.

Решение заключается в следующем:

  1. Чтение файла построчно (строки, разделенные LF).
  2. При этом удаляются строки на основе // комментариева также все разрывы строк.
  3. Объединить весь ввод обратно в одну строку.
  4. Разделить строку на точки с запятой, чтобы извлечь все функции, которые находятся за пределами комментариев.

Шаги не должны быть сделаны последовательно.Вы можете делать все эти шаги одновременно на входном потоке символов, и в конце выдавать поток функций.Фактически, это то, что будут делать настоящие парсеры.

По сути, вы пишете простой парсер.Поскольку ваш язык становится все более и более сложным, вам будет все труднее разбирать файл таким образом.Например, при описанном выше подходе будет трудно генерировать сообщения об ошибках с информацией о номере строки.

Если вы хотите написать правильный анализатор, я бы порекомендовал синтаксический анализатор с рекурсивным спуском вместе с PEG(грамматика выражения синтаксического анализатора).Этот подход прост в освоении, имеет меньше подводных камней, чем другие подходы, и все же очень эффективен для компьютерных языков.См. Здесь: https://en.wikipedia.org/wiki/Parsing_expression_grammar

Предупреждение. Если вы слышите людей, предлагающих flex и bison (или lex и yacc), я настоятельно рекомендую не использовать их.Они сложны в использовании и очень ограничены в том, что они могут анализировать и как это нужно указывать.Я бы предпочел использовать легкую и современную структуру синтаксического анализа, такую ​​как PEGTL: https://github.com/taocpp/PEGTL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...