【问题标题】:lemon + re2c not getting correct rule resolution柠檬 + re2c 没有得到正确的规则解析
【发布时间】:2016-04-05 06:33:50
【问题描述】:

这是我的柠檬解析器语法

%nonassoc IMPLICATION.
%nonassoc PERIOD.
%nonassoc NEWLINE.
%nonassoc END.
%nonassoc STRING.

program ::= in END.
in ::= .
in ::= in rule NEWLINE.
in ::= in rule.
rule ::= STRING(A) IMPLICATION STRING(B) PERIOD. {cout<<A->token<<endl; cout<<B->token<<endl;}

我的输入字符串是

p<-body1.
q<-body3.

我希望输出是

p
body1
q
body3

但是我得到的输出是

q
q
\n (Empty line here)
\n (Empty line here)

我确定我以正确的顺序传递令牌,并且我已经验证了这一点,因为解析器会在输入错误时抛出语法/解析器错误。

这是我用来将令牌传递给解析器的代码

do
{
    token = lexer.scan(); // returns an int with the type of token 
    Token* t = new Token(lexer.getTokenValue().c_str());

    lpmlnParse(pParser, token, t);
}while(token != PARSE_TOKEN_END);

我不知道出了什么问题。有人可以指出我正确的方向。

【问题讨论】:

  • 没有足够的信息来解释结果,特别是因为所显示的程序无法产生声称的输出(例如,逗号来自哪里)。任何答案都只是猜测。请创建一个minimal reproducible example——记下minimal这个词

标签: c++ parsing lemon


【解决方案1】:

这仍然是一个猜测,因为没有迹象表明扫描器是如何工作的,或者lexer.getTokenValue() 的值是什么,或者Token 构造函数如何使用它的参数。

但是让我们假设lexer 对象包含一个私有std::string 成员,该成员在扫描每个标记后分配给匹配的文本:

struct lexer {
  // ...
  int scan() {
    int toke;
    const char* start = current_;
    /* re2c stuff */
    tstring_.assign(start, current_ - start);
    return toke;
  }
  const std::string& getTokenValue() const {
    return tstring_;
  }
  std::string tstring_;
  const char* current_;
};

假设Token 包含const char* 成员(而不是std::string):

struct Token {
  explicit Token(const char* s) : str_(s) {}
  const char* str_;
}

这至少可以解释观察到的行为。

lexer.scan() 的每次连续调用都会覆盖tstring_ 的内容。 (在一般情况下,std::string::assign 可能会重新分配内部字符数组,但由于现代 C++ 库使用短字符串优化并且示例代码中的所有标记都是短字符串,因此这里不会发生这种情况。)

由于std::string::c_strToken 构造函数都没有复制字符,最终结果是新创建的Token 有一个指向可变内部缓冲区的指针,该缓冲区将被覆盖(或更糟的是,删除) 随着扫描的进行。

因此,在减少操作中观察到 Token 的字符串值将与首次创建 Token 时不同。

这仍然不足以解释为什么q 是由可能减少p-&gt;body1. 的规则打印的。

bison 不同,lemon 解析器不会尝试优化前瞻。 bison-生成的解析器将在请求前瞻令牌之前执行缩减,如果不需要前瞻令牌来决定是减少还是移位。相比之下,lemon 生成的解析器仅在前瞻令牌可用时才减少。在这种情况下,产生式rule ::= STRING(A) IMPLICATION STRING(B) PERIOD. 的减少不依赖于PERIOD 之后的令牌,但柠檬解析器仍将等待下一个令牌。

根据语法,人们可能认为下一个标记是NEWLINE,但在这种情况下,输出应该显示两个换行符(或四个空行,因为语义操作也会打印一个换行符)。由于情况并非如此,我们可能会推测词法分析器正在跳过换行符而不是返回NEWLINE 标记。如果是这种情况,语法仍然有效,因为 NEWLINE 标记是可选的(in rulein rule NEWLINE 都是有效的右侧)。那么前瞻令牌将是以下STRING 令牌,即q。而q-&gt;body3. 之后的前瞻标记将是END,而不是NEWLINE,因此相应的标记字符串可能是空的,而不是换行符。

显然,如果上述所有推测均成立,则解决方案是制作令牌字符串的副本,例如将Token 对象中的const char* str_; 替换为std::string str_;。在这种情况下,将const char* 构造函数替换为const std::string&amp; 构造函数甚至是简单的std::string 构造函数是合理的,从而避免了使用std::string::c_str() 的必要性。

【讨论】:

  • 我根据您的回答根据我拥有的代码进行了一些更改,并且有效。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多