Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/163.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 具有递归精神的分词错误.Qi语法_C++_Boost Spirit Qi - Fatal编程技术网

C++ 具有递归精神的分词错误.Qi语法

C++ 具有递归精神的分词错误.Qi语法,c++,boost-spirit-qi,C++,Boost Spirit Qi,我试图为一种只包含数字和数学表达式的非常简单的语言创建一个非常简单的解析器。最终,我计划扩展它,但直到我可以让这些基本版本工作 我已成功解析: 1 425 1 + 1 1 - 1 1 * 1 1 / 1 没问题。但我想让它递归,比方说,解析输入,如: 1 + 2 - 3 我开始犯分段错误。我在谷歌上搜索了一些递归语法和分段错误,但我似乎无法将我发现的任何东西应用到该语法中,使其正常工作。这要么是因为它们不适合我的情况,要么是因为我没有正确理解我的气语法 我的语法由以下结构组成,包括融合改编:

我试图为一种只包含数字和数学表达式的非常简单的语言创建一个非常简单的解析器。最终,我计划扩展它,但直到我可以让这些基本版本工作

我已成功解析:

1
425
1 + 1
1 - 1
1 * 1
1 / 1
没问题。但我想让它递归,比方说,解析输入,如:

1 + 2 - 3
我开始犯分段错误。我在谷歌上搜索了一些递归语法和分段错误,但我似乎无法将我发现的任何东西应用到该语法中,使其正常工作。这要么是因为它们不适合我的情况,要么是因为我没有正确理解我的气语法

我的语法由以下结构组成,包括融合改编:

namespace fun_lang {
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace phoenix = boost::phoenix;
    namespace fusion = boost::fusion;

    struct number_node {
        long value;
    };

    struct operation_node;

    typedef boost::variant<
        boost::recursive_wrapper<operation_node>,
        number_node
    > node;

    struct operation_node {
        node left, right;
        char op;
    };

    struct program {
        std::vector<node> nodes;
    };
}

BOOST_FUSION_ADAPT_STRUCT(fun_lang::program, (std::vector<fun_lang::node>, nodes));
BOOST_FUSION_ADAPT_STRUCT(fun_lang::number_node, (long, value));
BOOST_FUSION_ADAPT_STRUCT(fun_lang::operation_node, (fun_lang::node, left) (char, op) (fun_lang::node, right));

namespace fun_lang {
    template <typename Iterator, typename Skipper>
    struct fun_grammar : qi::grammar<Iterator, program(), Skipper> {
        fun_grammar() : fun_grammar::base_type(start) {
            using ascii::char_;
            using qi::ulong_;
            using qi::_val;
            using qi::_1;

            using phoenix::push_back;
            using phoenix::at_c;

            expression = (integer | operation)[_val = _1];

            oper = (char_('+') | char_('-') | char_('*') | char_('/'))[_val = _1];
            integer = ulong_[at_c<0>(_val) = _1];

            operation = expression[at_c<0>(_val) = _1] >> oper[at_c<1>(_val) = _1] >> expression[at_c<2>(_val) = _1];

            start = *expression[push_back(at_c<0>(_val), _1)];
        }

        qi::rule<Iterator, program(), Skipper> start;
        qi::rule<Iterator, number_node(), Skipper> integer;
        qi::rule<Iterator, char(), Skipper> oper;
        qi::rule<Iterator, node(), Skipper> expression;
        qi::rule<Iterator, operation_node(), Skipper> operation;
    };
}
一些规则结构基于我为另一种语言编写的yacc语法,我使用它作为构造这些规则的参考。我不确定是什么导致了分段错误,但我知道在运行此程序时,我收到的就是这个错误。我尝试过简化规则,删除一些中间规则,测试非递归方法。任何不是递归的东西似乎都能工作,但我已经看到了许多成功的使用递归规则的Spirit示例,所以我觉得我只是不太理解如何表达这些

编辑


为了帮助你解决这个问题,你可以在网上找到一份大致准确的副本。ideone版本与本地版本之间的唯一区别在于,它没有读取直接从标准输入中提取的文件。

有两种堆栈溢出源,最终导致分段错误。一个是操作节点和节点的构造函数。variant在默认构造时,使用其第一个模板参数的默认构造对象进行初始化。这是boost::recursive\u包装器,它构造一个操作节点,它构造两个节点,它构造一个boost::recursive\u包装器,这个过程一直持续到堆栈耗尽

在spirit语法中,给变体一个类似结构nil{}的nil类型是很常见的;作为防止这种情况发生的第一个参数,并且有一种方法可以识别未初始化的变体,所以

struct nil { };

typedef boost::variant<
    nil,
    boost::recursive_wrapper<operation_node>,
    number_node
> node;
也适用于您的情况,因为number_节点可以毫无问题地构造

另一个堆栈溢出是因为Boost.Spirit生成LLIF解析器,而yacc生成LALR1解析器,这意味着您得到的是递归下降解析器。规则

expression = (integer | operation)[_val = _1];
operation = expression[at_c<0>(_val) = _1] >> oper[at_c<1>(_val) = _1] >> expression[at_c<2>(_val) = _1];
这个问题消失了。此外,还必须将表达式规则重写为

为了让匹配像我认为您所期望的那样工作,否则整数部分将在找到操作之前成功匹配,并且解析器不会回溯,因为它有一个成功的部分匹配

还要注意,Spirit解析器是属性化的;您使用的解析器操作基本上是不必要的。可以像这样重写大部分语法:

expression = operation | integer;

oper = char_("-+*/");
integer = ulong_;

operation = integer >> oper >> expression;

嗯,这使得LL解析器在定义复杂语言时似乎较弱。但我对Spirit非常基本的经验让我认为它是针对更简单的解析问题而设计的,而不是复杂的问题。我不会这么说。虽然LALR解析器比LL解析器更强大、效率更高,而LR解析器更强大,但Boost.Spirit相当强大,具有无限的前瞻性和所有功能。你在野外能找到的大多数语言都可以用ll解析器进行解析,而且使用LLIF也很方便;不使用左递归语法实际上是使用LL解析器唯一需要注意的事情,而使用LALR则不需要注意。我需要花更多的时间区分这两种语法。当涉及到解析和语法以及所有这些好东西时,我是相当的新手。谢谢你的回答,它为输入工作。虽然我现在很难找到1+2+10的方向,但我会阅读更多内容并自己尝试。看看boost.spirit附带的示例,这里有几个算术表达式的计算器,你可以从中获得灵感。我看过它们的calc2示例,但我看不出它们与我做的有什么不同。当然,这是因为我对左递归规则以及如何避免由于它们而产生的无限循环缺乏了解。
operation = integer[at_c<0>(_val) = _1] >> oper[at_c<1>(_val) = _1] >> expression[at_c<2>(_val) = _1];
expression = (operation | integer)[_val = _1];
expression = operation | integer;

oper = char_("-+*/");
integer = ulong_;

operation = integer >> oper >> expression;