当我有更好的规则时,为什么我的 lex 文件中的最后一个规则匹配?
Why the last rule matching in my lex file, when I have better rules?
我有一个 lex
文件,里面有我的规则,例如:
PROGRAM return Parser::PROGRAM;
PROGRAM_END return Parser::PROGRAM_END;
VARIABLES: return Parser::VARIABLES;
INSTRUCTIONS: return Parser::INSTRUCTIONS;
SKIP return Parser::SKIP;
. {
std::cerr << lineno() << ": ERROR." << std::endl;
exit(1);
}
并且当我尝试在测试文件上使用完全编译(带有 yacc
文件等)版本时,即使测试文件是正确的,也会使用最后的规则。
例如,这是这些规则的测试文件:
PROGRAM fst
INSTRUCTIONS:
SKIP
PROGRAM_END
对于这个文件我只得到:1: ERROR
.
这是为什么,我该如何解决?
如评论中所示,几乎可以肯定 PROGRAM
开始被识别为标记并传递给解析器。然而,几乎在所有情况下,解析器都会立即请求另一个标记,并且输入序列中的下一个字符是 space,它与最后一条规则相匹配。该规则打印错误消息并调用 exit()
,终止应用程序。 (这通常不是一个好主意,但我想这只是一个测试程序。)这就是您将获得的所有输出。
如果您在调用 (f)lex 时指定 -d
命令行参数,则会生成调试扫描器,报告扫描器工作时的进度。这是查看扫描仪中正在发生的事情的一种非常简单的方法。 Bison 也有调试模式,如 bison manual 中所述。这些工具使用起来非常简单,强烈推荐。
例如,这是一个快速测试装置:
%{
#include <iostream>
#include <cstdlib>
class Parser {
public:
enum Token {
PROGRAM = 257,
PROGRAM_END, VARIABLES, INSTRUCTIONS, SKIP
};
};
%}
%option batch noyywrap yylineno c++
%%
PROGRAM return Parser::PROGRAM;
PROGRAM_END return Parser::PROGRAM_END;
VARIABLES: return Parser::VARIABLES;
INSTRUCTIONS: return Parser::INSTRUCTIONS;
SKIP return Parser::SKIP;
. {
std::cerr << lineno() << ": ERROR." << std::endl;
exit(1);
}
%%
int main() {
yyFlexLexer lexer{};
lexer.set_debug(1);
while(lexer.yylex() != 0) { }
return 0;
}
和一个示例 运行:
$ g++ lex.yy.cc && ./a.out<<<"PROGRAM fst"
--(end of buffer or a NUL)
--accepting rule at line 14("PROGRAM")
--accepting rule at line 19(" ")
1: ERROR.
这清楚表明扫描器确实首先生成了 PROGRAM
标记,然后在 space 字符处退出。
我有一个 lex
文件,里面有我的规则,例如:
PROGRAM return Parser::PROGRAM;
PROGRAM_END return Parser::PROGRAM_END;
VARIABLES: return Parser::VARIABLES;
INSTRUCTIONS: return Parser::INSTRUCTIONS;
SKIP return Parser::SKIP;
. {
std::cerr << lineno() << ": ERROR." << std::endl;
exit(1);
}
并且当我尝试在测试文件上使用完全编译(带有 yacc
文件等)版本时,即使测试文件是正确的,也会使用最后的规则。
例如,这是这些规则的测试文件:
PROGRAM fst
INSTRUCTIONS:
SKIP
PROGRAM_END
对于这个文件我只得到:1: ERROR
.
这是为什么,我该如何解决?
如评论中所示,几乎可以肯定 PROGRAM
开始被识别为标记并传递给解析器。然而,几乎在所有情况下,解析器都会立即请求另一个标记,并且输入序列中的下一个字符是 space,它与最后一条规则相匹配。该规则打印错误消息并调用 exit()
,终止应用程序。 (这通常不是一个好主意,但我想这只是一个测试程序。)这就是您将获得的所有输出。
如果您在调用 (f)lex 时指定 -d
命令行参数,则会生成调试扫描器,报告扫描器工作时的进度。这是查看扫描仪中正在发生的事情的一种非常简单的方法。 Bison 也有调试模式,如 bison manual 中所述。这些工具使用起来非常简单,强烈推荐。
例如,这是一个快速测试装置:
%{
#include <iostream>
#include <cstdlib>
class Parser {
public:
enum Token {
PROGRAM = 257,
PROGRAM_END, VARIABLES, INSTRUCTIONS, SKIP
};
};
%}
%option batch noyywrap yylineno c++
%%
PROGRAM return Parser::PROGRAM;
PROGRAM_END return Parser::PROGRAM_END;
VARIABLES: return Parser::VARIABLES;
INSTRUCTIONS: return Parser::INSTRUCTIONS;
SKIP return Parser::SKIP;
. {
std::cerr << lineno() << ": ERROR." << std::endl;
exit(1);
}
%%
int main() {
yyFlexLexer lexer{};
lexer.set_debug(1);
while(lexer.yylex() != 0) { }
return 0;
}
和一个示例 运行:
$ g++ lex.yy.cc && ./a.out<<<"PROGRAM fst"
--(end of buffer or a NUL)
--accepting rule at line 14("PROGRAM")
--accepting rule at line 19(" ")
1: ERROR.
这清楚表明扫描器确实首先生成了 PROGRAM
标记,然后在 space 字符处退出。