【问题标题】:I can't get the string value of a token我无法获取令牌的字符串值
【发布时间】:2011-12-07 13:55:12
【问题描述】:

我尝试使用 Boost Spirit 为一种小型编程语言实现 Lexer。

我必须得到一个令牌的值,我得到一个 bad_get 异常:

在抛出 'boost::bad_get' 实例后调用终止
what(): boost::bad_get: 使用 boost::get Aborted 获取失败的值

我在做的时候得到了这个异常:

std::string contents = "void";

base_iterator_type first = contents.begin();
base_iterator_type last = contents.end();

SimpleLexer<lexer_type> lexer;

iter = lexer.begin(first, last);
end = lexer.end();

std::cout << "Value = " << boost::get<std::string>(iter->value()) << std::endl;

我的词法分析器是这样定义的:

typedef std::string::iterator base_iterator_type;
typedef boost::spirit::lex::lexertl::token<base_iterator_type, boost::mpl::vector<unsigned int, std::string>> Tok;
typedef lex::lexertl::actor_lexer<Tok> lexer_type;

template<typename L>
class SimpleLexer : public lex::lexer<L> {
    private:

    public:
        SimpleLexer() {
            keyword_for = "for";
            keyword_while = "while";
            keyword_if = "if";
            keyword_else = "else";
            keyword_false = "false";
            keyword_true = "true";
            keyword_from = "from";
            keyword_to = "to";
            keyword_foreach = "foreach";

            word = "[a-zA-Z]+";
            integer = "[0-9]+";
            litteral = "...";

            left_parenth = '('; 
            right_parenth = ')'; 
            left_brace = '{'; 
            right_brace = '}'; 

            stop = ';';
            comma = ',';

            swap = "<>";
            assign = '=';
            addition = '+';
            subtraction = '-';
            multiplication = '*';
            division = '/';
            modulo = '%';

            equals = "==";
            not_equals = "!=";
            greater = '>';
            less = '<';
            greater_equals = ">=";
            less_equals = "<=";

            whitespaces = "[ \\t\\n]+";
            comments = "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/";

            //Add keywords
            this->self += keyword_for | keyword_while | keyword_true | keyword_false | keyword_if | keyword_else | keyword_from | keyword_to | keyword_foreach;
            this->self += integer | litteral | word;

            this->self += equals | not_equals | greater_equals | less_equals | greater | less ;
            this->self += left_parenth | right_parenth | left_brace | right_brace;
            this->self += comma | stop;
            this->self += assign | swap | addition | subtraction | multiplication | division | modulo;

            //Ignore whitespaces and comments
            this->self += whitespaces [lex::_pass = lex::pass_flags::pass_ignore];
            this->self += comments [lex::_pass = lex::pass_flags::pass_ignore]; 
        }

        lex::token_def<std::string> word, litteral, integer;

        lex::token_def<lex::omit> left_parenth, right_parenth, left_brace, right_brace;

        lex::token_def<lex::omit> stop, comma;

        lex::token_def<lex::omit> assign, swap, addition, subtraction, multiplication, division, modulo;
        lex::token_def<lex::omit> equals, not_equals, greater, less, greater_equals, less_equals;

        //Keywords
        lex::token_def<lex::omit> keyword_if, keyword_else, keyword_for, keyword_while, keyword_from, keyword_to, keyword_foreach;
        lex::token_def<lex::omit> keyword_true, keyword_false;

        //Ignored tokens
        lex::token_def<lex::omit> whitespaces;
        lex::token_def<lex::omit> comments;
};

还有其他方法可以获取 Token 的值吗?

【问题讨论】:

  • 再次阅读时,我注意到您将lex::omit 指定为令牌属性类型。这些标记不会暴露 任何 值数据(甚至不会暴露迭代器对)。这可能是你的问题。否则,我强烈建议在令牌迭代器之上使用 Qi 进行解析:两全其美。
  • 我已验证,遗憾的是这不是问题所在。我只在一个好的类型的令牌上使用 boost::get 并且应该具有价值。

标签: c++ boost boost-spirit boost-spirit-lex


【解决方案1】:

您始终可以使用“默认”令牌数据(即源迭代器类型的 iterator_range)。

std::string tokenvalue(iter->value().begin(), iter->value().end());

在研究了boost存储库中的测试用例之后,我发现了一些东西:

  • 这是设计使然
  • 有一种更简单的方法
  • 更简单的方式在 Lex 语义操作(例如使用 _1)和 Qi 中使用词法分析器标记时自动实现;赋值会自动转换为 Qi 属性类型
  • 这(确实)获得了文档中提到的“惰性、一次性、评估”语义

关键是令牌数据是可变的,它从原始输入迭代器范围开始。只有在 'a' 强制赋值后,转换后的属性才会缓存在变体中。你可以见证这个转变:

lexer_type::iterator_type iter = lexer.begin(first, last);
lexer_type::iterator_type end = lexer.end();

assert(0 == iter->value().which());
std::cout << "Value = " << boost::get<boost::iterator_range<base_iterator_type> >(iter->value()) << std::endl;

std::string s;
boost::spirit::traits::assign_to(*iter, s);
assert(1 == iter->value().which());
std::cout << "Value = " << s << std::endl;

如你所见,这里的属性赋值是强制的,直接使用assign_to trait 实现。

完整的工作演示:

#include <boost/spirit/include/lex_lexertl.hpp>

#include <iostream>
#include <string>

namespace lex = boost::spirit::lex;

typedef std::string::iterator base_iterator_type;
typedef boost::spirit::lex::lexertl::token<base_iterator_type, boost::mpl::vector<int, std::string>> Tok;
typedef lex::lexertl::actor_lexer<Tok> lexer_type;

template<typename L>
class SimpleLexer : public lex::lexer<L> {
    private:

    public:
        SimpleLexer() {
            word = "[a-zA-Z]+";
            integer = "[0-9]+";
            literal = "...";

            this->self += integer | literal | word;
        }

        lex::token_def<std::string> word, literal;
        lex::token_def<int> integer;
};

int main(int argc, const char* argv[]) {
    SimpleLexer<lexer_type> lexer;

    std::string contents = "void";

    base_iterator_type first = contents.begin();
    base_iterator_type last = contents.end();

    lexer_type::iterator_type iter = lexer.begin(first, last);
    lexer_type::iterator_type end = lexer.end();

    assert(0 == iter->value().which());
    std::cout << "Value = " << boost::get<boost::iterator_range<base_iterator_type> >(iter->value()) << std::endl;

    std::string s;
    boost::spirit::traits::assign_to(*iter, s);
    assert(2 == iter->value().which());
    std::cout << "Value = " << s << std::endl;

    return 0;
}

【讨论】:

  • Spirit 应该做的事情看起来有点过于复杂。在我的情况下,输入令牌以获取它们的值,因此我从 value() 而不是直接获取迭代器获取变体。我也有一个 int 令牌。使用您的技术,您没有利用 value() 提供的变体,不是吗?
  • 哪部分过于复杂?它说std::string(iter-&gt;value().begin(), iter-&gt;value().end())的部分?我没有把它拼出来(你希望我们读入你的 Looooong 样本以“get”你的意思,而你不想阅读 7 行 showtoken 来了解它的含义完成了吗?嗯。)我的示例可能看起来过于复杂,因为它是如何在现实生活中的解析器中使用它来实现的完整示例,例如错误报告。很抱歉展示了你不感兴趣的东西:)
  • 我觉得过于复杂的是我们手动解析了 Boost Spirit 提供给我们的东西。如果我有 float、int、string 和 bool 标记并且我想获取它们的原始值,我必须创建 4 个解析函数,不是吗?通常这些值存储在 boost::variant 中。或者我不明白 value() 函数的返回值。
  • 我怀疑您可能误解了 Lexer 的使用。 Lexer 解析成标记(根据定义,它们只是源迭代器范围)。如果您想要简单且自动的值提取,请使用 Spirit Qi(尽管,Lexer 令牌可以给您attribute values directly。稍后会更新答案。
  • 我已经确定了评估发生的条件。我希望这些信息对您有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-23
  • 2019-11-11
  • 2016-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多