это поле комментария делает меня сумасшедшим

вижу свою ошибку здесь .. это правило разбирает некоторые вещи в порядке, но последние два образца нет. Может кто-нибудь, пожалуйста, дайте мне подсказку ..

Цель - это синтаксический анализатор, который может идентифицировать доступ к свойству члена и вызовы функции-члена. Также в некотором роде

 a()
 a(para)
 x.a()
 x.a(para)
 x.a(para).g(para).j()
 x.y
 x.y.z
 x.y.z()    <---fail
 y.z.z(para) <--- fail
  lvalue =
         iter_pos >> name[_val = _1]
          >> *(lit('(') > paralistopt  > lit(')') >> iter_pos)[_val = construct<common_node>(type_cmd_fnc_call, LOCATION_NODE_ITER(_val, _2), key_this, construct<common_node>(_val), key_parameter, construct<std::vector<common_node> >(_1))]        
       >> *(lit('.') >> name_pure >> lit('(') > paralistopt > lit(')') >> iter_pos)[_val = construct<common_node>(type_cmd_fnc_call, LOCATION_NODE_ITER(_val, _3), key_this, construct<common_node>(_val), key_callname, construct<std::wstring>(_1), key_parameter, construct<std::vector<common_node> >(_2))]
       >> *(lit('.') >> name_pure >> iter_pos)[_val = construct<common_node>(type_cmd_dot_call, LOCATION_NODE_ITER(_val, _2), key_this, construct<common_node>(_val), key_propname, construct<std::wstring>(_1))]
    ;

спасибо Маркус

 sehe14 окт. 2017 г., 17:07
Кажется, в вашем примере кода отсутствуют соответствующие биты. Если вы включаете код, сделайте его автономным.
 sehe14 окт. 2017 г., 17:06
Можете ли вы описать то, что вы пытаетесь достичь? Что вы анализируете, в чем AST? Я уверен, что ваш код слишком сложен. (Видетьstackoverflow.com/questions/8259440/...)

Ответы на вопрос(2)

Попробуйте изменить

>> *(lit('.') >> name_pure >> lit('(') > paralistopt > lit(')'))

в

>> *(*(lit('.') >> name_pure) >> lit('(') > paralistopt > lit(')'))
 Markus13 окт. 2017 г., 14:51
это поле комментария делает меня сумасшедшим

чтобы пойти на. Позвольте мне развеселить вас своим вступлением в эту угадайку:

Давайте предположим, что вы хотите проанализировать простой «язык», который просто допускает выражения членов и вызовы функций, но в цепочке.

Теперь ваша грамматика ничего не говорит о параметрах (хотя ясно, что список параметров может быть пустым), поэтому позвольте мне пройти следующую милю и предположить, что вы хотите принять там выражения такого же типа (поэтомуfoo(a) это хорошо, но такжеbar(foo(a)) или жеbar(b.foo(a))).

Поскольку вы принимаете цепочку вызовов функций, кажется, что функции являются объектами первого класса (и функции могут возвращать функции), поэтомуfoo(a)(b, c, d) следует также принять.

Вы не упомянули об этом, но параметры часто включают литералы (sqrt(9) приходит на ум илиprintln("hello world")).

Другие предметы:

Вы не сказали, но, вероятно, вы хотите игнорировать пробелы в определенных местахизiter_pos (ab) используйте, кажется, вы заинтересованы в отслеживании исходного местоположения источника в результирующем AST.1. Определите АСТ

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

namespace Ast {
    using Identifier = boost::iterator_range<It>;

    struct MemberExpression;
    struct FunctionCall;

    using Expression = boost::variant<
                double,       // some literal types
                std::string,
                // non-literals
                Identifier,
                boost::recursive_wrapper<MemberExpression>,
                boost::recursive_wrapper<FunctionCall>
            >;

    struct MemberExpression {
        Expression object; // antecedent
        Identifier member; // function or field
    };

    using Parameter  = Expression;
    using Parameters = std::vector<Parameter>;

    struct FunctionCall {
        Expression function; // could be a member function
        Parameters parameters;
    };
}

НОТА Мы не собираемся сосредотачиваться на показе местоположений источника, но уже сделали одно условие, сохраняя идентификаторы как диапазон итераторов.

НОТА Fusion-адаптация единственных типов, не поддерживаемых непосредственно Spirit:

BOOST_FUSION_ADAPT_STRUCT(Ast::MemberExpression, object, member)
BOOST_FUSION_ADAPT_STRUCT(Ast::FunctionCall, function, parameters)

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

2. Соответствующая грамматика
Grammar() : Grammar::base_type(start) {
    using namespace qi;
    start = skip(space) [expression];

    identifier = raw [ (alpha|'_') >> *(alnum|'_') ];
    parameters = -(expression % ',');

    expression 
        = literal 
        | identifier  >> *(
                    ('.' >> identifier)        
                  | ('(' >> parameters >> ')') 
                );

    literal = double_ | string_;
    string_ = '"' >> *('\\' >> char_ | ~char_('"')) >> '"';

    BOOST_SPIRIT_DEBUG_NODES(
            (identifier)(start)(parameters)(expression)(literal)(string_)
        );
}

В этом скелете большинство правил выигрывают от автоматического распространения атрибутов. Тот, который не являетсяexpression:

qi::rule<It, Expression()> start;

using Skipper = qi::space_type;
qi::rule<It, Expression(), Skipper> expression, literal;
qi::rule<It, Parameters(), Skipper> parameters;
// lexemes
qi::rule<It, Identifier()> identifier;
qi::rule<It, std::string()> string_;

Итак, давайте создадим несколько помощников для семантических действий.

НОТА Важный вывод здесь состоит в том, чтобы создавать свои собственные строительные блоки более высокого уровня вместо того, чтобы тратить время наboost::phoenix::construct<> и т.п.

Определите две простые функции построения:

struct mme_f { MemberExpression operator()(Expression lhs, Identifier rhs) const { return { lhs, rhs }; } };
struct mfc_f { FunctionCall operator()(Expression f, Parameters params) const { return { f, params }; } };
phx::function<mme_f> make_member_expression;
phx::function<mfc_f> make_function_call;

Тогда используйте их:

expression 
    = literal [_val=_1]
    | identifier [_val=_1] >> *(
                ('.' >> identifier)        [ _val = make_member_expression(_val, _1)]
              | ('(' >> parameters >> ')') [ _val = make_function_call(_val, _1) ]
            );

Вот и все. Мы готовы к работе!

3. ДЕМО

Жить на Колиру

Я создал тестовый стенд, похожий на этот:

int main() {
    using It = std::string::const_iterator;
    Parser::Grammar<It> const g;

    for (std::string const input : {
             "a()", "a(para)", "x.a()", "x.a(para)", "x.a(para).g(para).j()", "x.y", "x.y.z",
             "x.y.z()",
             "y.z.z(para)",
             // now let's add some funkyness that you didn't mention
             "bar(foo(a))",
             "bar(b.foo(a))",
             "foo(a)(b, c, d)", // first class functions
             "sqrt(9)",
             "println(\"hello world\")",
             "allocate(strlen(\"aaaaa\"))",
             "3.14",
             "object.rotate(180)",
             "object.rotate(event.getAngle(), \"torque\")",
             "app.mainwindow().find_child(\"InputBox\").font().size(12)",
             "app.mainwindow().find_child(\"InputBox\").font(config().preferences.baseFont(style.PROPORTIONAL))"
         }) {
        std::cout << " =========== '" << input << "' ========================\n";
        It f(input.begin()), l(input.end());

        Ast::Expression parsed;
        bool ok = parse(f, l, g, parsed);
        if (ok) {
            std::cout << "Parsed: " << parsed << "\n";
        }
        else
            std::cout << "Parse failed\n";

        if (f != l)
            std::cout << "Remaining unparsed input: '" << std::string(f, l) << "'\n";
    }
}

Невероятно, как это может показаться, это уже анализирует все тесты и печатает:

 =========== 'a()' ========================
Parsed: a()
 =========== 'a(para)' ========================
Parsed: a(para)
 =========== 'x.a()' ========================
Parsed: x.a()
 =========== 'x.a(para)' ========================
Parsed: x.a(para)
 =========== 'x.a(para).g(para).j()' ========================
Parsed: x.a(para).g(para).j()
 =========== 'x.y' ========================
Parsed: x.y
 =========== 'x.y.z' ========================
Parsed: x.y.z
 =========== 'x.y.z()' ========================
Parsed: x.y.z()
 =========== 'y.z.z(para)' ========================
Parsed: y.z.z(para)
 =========== 'bar(foo(a))' ========================
Parsed: bar(foo(a))
 =========== 'bar(b.foo(a))' ========================
Parsed: bar(b.foo(a))
 =========== 'foo(a)(b, c, d)' ========================
Parsed: foo(a)(b, c, d)
 =========== 'sqrt(9)' ========================
Parsed: sqrt(9)
 =========== 'println("hello world")' ========================
Parsed: println(hello world)
 =========== 'allocate(strlen("aaaaa"))' ========================
Parsed: allocate(strlen(aaaaa))
 =========== '3.14' ========================
Parsed: 3.14
 =========== 'object.rotate(180)' ========================
Parsed: object.rotate(180)
 =========== 'object.rotate(event.getAngle(), "torque")' ========================
Parsed: object.rotate(event.getAngle(), torque)
 =========== 'app.mainwindow().find_child("InputBox").font().size(12)' ========================
Parsed: app.mainwindow().find_child(InputBox).font().size(12)
 =========== 'app.mainwindow().find_child("InputBox").font(config().preferences.baseFont(style.PROPORTIONAL))' ========================
Parsed: app.mainwindow().find_child(InputBox).font(config().preferences.baseFont(style.PROPORTIONAL))
4. Слишком хорошо, чтобы быть правдой?

Вы правы. Я смухлевал. Я не показывал вам этот код, необходимый для отладки распечатанного AST:

namespace Ast {
    static inline std::ostream& operator<<(std::ostream& os, MemberExpression const& me) {
        return os << me.object << "." << me.member;
    }

    static inline std::ostream& operator<<(std::ostream& os, FunctionCall const& fc) {
        os << fc.function << "(";
        bool first = true;
        for (auto& p : fc.parameters) { if (!first) os << ", "; first = false; os << p; }
        return os << ")";
    }
}

Это только отладочная печать, поскольку строковые литералы неправильно переворачиваются. Но это всего лишь 10 строк кода, это бонус.

5. Полный Монти: Местонахождение источника

Это вас заинтересовало, так что давайте покажем, что это работает. Давайте добавим простой цикл для печати всех местоположений идентификаторов:

using IOManip::showpos;

for (auto& id : all_identifiers(parsed)) {
    std::cout << " - " << id << " at " << showpos(id, input) << "\n";
}

Конечно, возникает вопрос, каковыshowpos а такжеall_identifiers?

namespace IOManip {
    struct showpos_t {
        boost::iterator_range<It> fragment;
        std::string const& source;

        friend std::ostream& operator<<(std::ostream& os, showpos_t const& manip) {
            auto ofs = [&](It it) { return it - manip.source.begin(); };
            return os << "[" << ofs(manip.fragment.begin()) << ".." << ofs(manip.fragment.end()) << ")";
        }
    };

    showpos_t showpos(boost::iterator_range<It> fragment, std::string const& source) {
        return {fragment, source};
    }
}

Что касается извлечения идентификатора:

std::vector<Identifier> all_identifiers(Expression const& expr) {
    std::vector<Identifier> result;
    struct Harvest {
        using result_type = void;
        std::back_insert_iterator<std::vector<Identifier> > out;
        void operator()(Identifier const& id)       { *out++ = id; }
        void operator()(MemberExpression const& me) { apply_visitor(*this, me.object); *out++ = me.member; }
        void operator()(FunctionCall const& fc)     {
            apply_visitor(*this, fc.function); 
            for (auto& p : fc.parameters) apply_visitor(*this, p);
        }
        // non-identifier expressions
        void operator()(std::string const&) { }
        void operator()(double) { }
    } harvest { back_inserter(result) };
    boost::apply_visitor(harvest, expr);

    return result;
}

Это посетитель дерева, который рекурсивно собирает все идентификаторы, вставляя их в конец контейнера.

Жить на Колиру

Где выглядит вывод (отрывок):

 =========== 'app.mainwindow().find_child("InputBox").font(config().preferences.baseFont(style.PROPORTIONAL))' ========================
Parsed: app.mainwindow().find_child(InputBox).font(config().preferences.baseFont(style.PROPORTIONAL))
 - app at [0..3)
 - mainwindow at [4..14)
 - find_child at [17..27)
 - font at [40..44)
 - config at [45..51)
 - preferences at [54..65)
 - baseFont at [66..74)
 - style at [75..80)
 - PROPORTIONAL at [81..93)
 sehe18 окт. 2017 г., 02:54
@Markus У меня было больше веселья с этимcoliru.stacked-crooked.com/a/3f354e40fb4da907 - Есть несколько тонкостей, которые прокомментированы. Обратите внимание, как вы можете раскомментироватьAnnotateTag базовый класс дляString и получить «магически» аннотированные строковые узлы (в настоящее время только родительскийExpression узел аннотирован).
 Markus16 окт. 2017 г., 12:50
Привет, большое спасибо за эту подсказку. Ваше решение работает очень хорошо, и я смог сделать все звонки, а также цепочки, работая на моем парсере! Великая помощь! Мне нужно некоторое время, чтобы просмотреть материал с помощью iter_pos, ваше решение выглядит более нативно, но у меня есть несколько более сложных элементов для отслеживания позиций, а не только идентификатор. Поэтому я должен проверить, как ваше решение будет работать здесь Markus
 Markus17 окт. 2017 г., 07:42
получил это ... хороший рабочий процесс и больше нет iter_pos внутри парсера! Еще раз спасибо
 sehe16 окт. 2017 г., 15:55
Я вспомнил наполовину правильно: используются учебники calc [78], заклинание [123] и mini_cqi::on_success с "annotation_function". Вам может быть интересно посмотреть на эти
 sehe16 окт. 2017 г., 15:39
Я рад взглянуть на более крупный проект. На самом деле, я бы посоветовал аннотировать узлы AST, используя грамматику, ортогонально (во IIRC была демонстрационная версия компилятора, выполняющая такие вещи с использованиемqi::on_error<success,....>)

Ваш ответ на вопрос