парсинг идентификаторов, кроме ключевых слов

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

struct keywords_t : x3::symbols<x3::unused_type> {
    keywords_t() {
        add("for", x3::unused)
                ("in", x3::unused)
                ("while", x3::unused);
    }
} const keywords;

и парсер для идентификатора должен быть таким:

auto const identifier_def =       
            x3::lexeme[
                (x3::alpha | '_') >> *(x3::alnum | '_')
            ];

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

auto const identifier_def =       
                x3::lexeme[
                    (x3::alpha | '_') >> *(x3::alnum | '_')
                ]-keywords;

и это:

auto const identifier_def =       
                x3::lexeme[
                    (x3::alpha | '_') >> *(x3::alnum | '_') - keywords
                ];

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


person Exagon    schedule 26.06.2016    source источник


Ответы (2)


Ваша проблема вызвана семантикой оператора разницы в Spirit. Когда у вас есть a - b дух, он делает следующее:

  • check whether b matches:
    • if it does, a - b fails and nothing is parsed.
    • if b fails then it checks whether a matches:
      • if a fails, a - b fails and nothing is parsed.
      • если a преуспевает, a - b преуспевает и анализирует все, что анализирует a.

В вашем случае (unchecked_identifier - keyword), если идентификатор начинается с ключевого слова, keyword будет совпадать, и ваш синтаксический анализатор не сработает. Таким образом, вам нужно заменить keyword на что-то, что соответствует всякий раз, когда передается отдельное ключевое слово, но терпит неудачу, когда за ключевым словом следует что-то еще. В этом могут помочь not predicate (!).

auto const distinct_keyword = x3::lexeme[ keyword >> !(x3::alnum | '_') ];

Полный пример (работает на Coliru):

//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/spirit/home/x3.hpp>

namespace parser {
    namespace x3 = boost::spirit::x3;

    struct keywords_t : x3::symbols<x3::unused_type> {
        keywords_t() {
            add("for", x3::unused)
                    ("in", x3::unused)
                    ("while", x3::unused);
        }
    } const keywords;

    x3::rule<struct identifier_tag,std::string>  const identifier ("identifier");

    auto const distinct_keyword = x3::lexeme[ keywords >> !(x3::alnum | '_') ];
    auto const unchecked_identifier = x3::lexeme[(x3::alpha | x3::char_('_')) >> *(x3::alnum | x3::char_('_'))];


    auto const identifier_def = unchecked_identifier - distinct_keyword;

    //This should also work:
    //auto const identifier_def = !distinct_keyword >> unchecked_identifier


    BOOST_SPIRIT_DEFINE(identifier);

    bool is_identifier(const std::string& input)
    {
        auto iter = std::begin(input), end= std::end(input);

        bool result = x3::phrase_parse(iter,end,identifier,x3::space);

        return result && iter==end;
    }
}



int main() {

    std::cout << parser::is_identifier("fortran") << std::endl;
    std::cout << parser::is_identifier("for") << std::endl;
    std::cout << parser::is_identifier("integer") << std::endl;
    std::cout << parser::is_identifier("in") << std::endl;
    std::cout << parser::is_identifier("whileechoyote") << std::endl;
    std::cout << parser::is_identifier("while") << std::endl;
}
person Community    schedule 26.06.2016

Проблема в том, что это работает без лексера, то есть если написать

keyword >> *char_

И введите whilefoo, он будет анализировать while как keyword и foo как *char_.

Вы можете предотвратить это двумя способами: либо потребовать пробел после ключевого слова, т.е.

auto keyword_rule = (keyword >> x3::space);
//or if you use phrase_parse
auto keyword_rule = x3::lexeme[keyword >> x3::space];

Другой способ, который вы описали, также возможен, т.е. явно удалить ключевое слово из строки (я бы сделал это так):

auto string = x3::lexeme[!keyword >> (x3::alpha | '_') >> *(x3::alnum | '_')];

Проблема с вашим определением заключается в том, что оно будет интерпретировать первый набор символов как ключевое слово, тем самым решив вообще не анализировать его. Оператор «x-y» означает анализировать x, но не y. Но если вы передадите 'whilefoo', он будет интерпретировать 'пока' как ключевое слово и, следовательно, вообще не будет анализировать.

person Klemens Morgenstern    schedule 26.06.2016