c++ - 柠檬 + re2c 没有得到正确的规则解析

标签 c++ parsing lemon

这是我的柠檬解析器语法

%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);

我不知道出了什么问题。有人能指出我正确的方向吗?

最佳答案

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

但让我们想象一下,lexer 对象包含一个私有(private)的 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->body1. 的规则打印的。

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

从语法来看,人们可能期望下一个标记是 NEWLINE,但在这种情况下,输出应该显示两个换行符(或四个空行,因为语义操作也会打印一个换行符) .由于情况并非如此,我们可以推测词法分析器正在跳过换行符而不是返回 NEWLINE 标记。如果是这种情况,语法仍然有效,因为 NEWLINE 标记是可选的(in rulein rule NEWLINE 都是有效的,正确的-手边)。然后,先行标记将是以下 STRING 标记,即 q。而 q->body3. 之后的前瞻标记将是 END,而不是 NEWLINE,因此相应的标记字符串似乎是合理的将是空的,而不是换行符。

显然,如果上面的所有推测都是有效的,那么解决方案就是复制 token 字符串,例如将 const char* str_; 替换为 std::Token 对象中的字符串 str_;。在这种情况下,将 const char* 构造函数替换为 const std::string& 构造函数甚至是简单的 std::string 是合理的 构造函数,从而避免使用 std::string::c_str() 的必要性。

关于c++ - 柠檬 + re2c 没有得到正确的规则解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36418772/

相关文章:

c - 如何将 GNU readline 与 flex-lexer 一起使用?

c++ - 如何使用Lua5.1 柠檬语法?

grammar - 为什么会出现语法冲突呢?

c++ - shared_ptr 对容器的意义是什么?

c++ - 使用 libssl/libcrypto 的段错误

c++ - 使用 smtp 发送电子邮件

linq - 当字段需要首先解析时,如何在 linq 查询中进行过滤?

c++ - QNetworkConfigurationManager::allConfigurations() 没有列出 WLAN

java - java I/O 的字符串数组逻辑问题

python - 解析几乎是 Python 的语言的最佳方法?