flex-lexer - 写我想要的,但必须在某些标记之前或之后
问题描述
所以我有这个 lex 文件:
%{
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "node.h"
#include "y.tab.h"
char *dupstr(const char *s);
void yyerror(char *s);
int octal(char *s);
%}
%%
\$\$.* ; /* comment */
\$(.|\n)*\$ ; /* comment */
">=" return GE;
"<=" return LE;
":=" return AT;
"~=" return NEQ;
"if" return IF;
"else" return ELSE;
"then" return THEN;
"elif" return ELIF;
"fi" return FI;
"for" return FOR;
"until" return UNTIL;
"step" return STEP;
"do" return DO;
"done" return DONE;
"repeat" return REP;
"stop" return STOP;
"return" return RET;
^"program" return PROG;
^"module" return MOD;
"start" return ST;
^"end" return END;
"void" return VD;
"const" return CT;
"number" return NB;
"array" return ARR;
"string" return SG;
"function" return FC;
"public" return PB;
"forward" return FW;
0|[1-9][0-9]* { errno = 0; yylval.i = strtol(yytext, 0, 10); if (errno == ERANGE)
yyerror("overflow in decimal constant"); return INTEGER; }
0[0-7]+ { yylval.i = octal(yytext); return INTEGER; }
0x[0-9a-fA-F]+ { yylval.i = strtol(yytext, 0, 16); return INTEGER; }
0b[01]+ { errno = 0; yylval.i = strtol(yytext+2, 0, 2); if (errno == ERANGE)
yyerror("overflow in binary constant"); return INTEGER; }
\'[^\\\']\'|\'\\[nrt\\\']\'|\'\\[a-fA-F0-9]\' { yytext[yyleng-1] = 0; yylval.s =
dupstr(yytext+1); return STRING; }
[A-Za-z][A-Za-z0-9_]* { yylval.s = dupstr(yytext+1); return ID; }
\"[^"]*\" { yytext[yyleng-1] = 0; yylval.s = dupstr(yytext+1); return STRING; }
[-+*/%^:=<>~|&?#<\[\]();!,] return *yytext;
[ \t\n\r]+ ; /* ignore whitespace */
. yyerror("Unknown character");
%%
char *getyytext() { return yytext; }
int yywrap(void) {
return 1;
}
int octal(char *s)
{
int i, a = 0, b = 0;
for (i = 0; i < strlen(s); i++) {
if (s[i] < '0' || s[i] > '7') break;
b = b * 8 + s[i] - '0';
if (b < a) {
yyerror("octal overflow");
break;
}
a = b;
}
return a;
}
而且我想要一个限制,允许我写任何我想要的东西,但前提是我在令牌程序和模块之前或令牌结束之后编写它,这可能吗?我在相应的 yacc 文件上尝试了一些选项,但做不到,我也认为这是 lex 的问题,提前抱歉,这是我第一次使用这种语言,我在研究中没有找到任何可以帮助解决这个问题的东西问题。
解决方案
您将需要一个启动条件,但这是一个非常简单的应用程序。每个开始条件适用于不同的词法环境。在您的情况下,您基本上有两个这样的环境:一个对应于不应解析的文本,另一个对应于您要分析的文本部分。
这通常被称为“孤岛解析”,因为您试图在非结构化文本的海洋中解析结构化信息的孤岛。
基于 Lex 的扫描器生成器有一个名为 的默认启动条件<INITIAL>
,它在词法分析器第一次启动时处于活动状态。<INITIAL>
不必用明确的开始条件编写规则;其他规则可以。在孤岛解析的情况下,这很烦人,因为大多数规则都处于孤岛开始条件中,这意味着条件名称必须预先存在于所有规则中。
但是您几乎可以肯定实际上是在使用 flex,如果是这样,您可以使用有用的 flex 扩展,它允许将规则块分配给开始条件。这就是我写这个答案的方式,如果它适合你,那么你应该更改任何引用“lex”的构建规则,以便它们正确命名你正在使用的扫描仪生成器(因为如果你使用 flex 扩展,你将需要用 flex 处理文件)。
正确编写解析器需要在输入规范中具有很高的精度。您的简短问题中有许多未指明的案例;我首先列出我看到的那些,以及我选择的解决方案(通常是最省力的解决方案)。
在外部
<INITIAL>
开始条件中,任何不以单词开头的文本行program
或module
非结构化文本。您的问题并未表明您希望如何处理。您可以将其传递给解析器、忽略它、将其复制到yyout
或任何数量的其他替代方案。在这里,我忽略它,因为这是最简单的。应该清楚其他替代方案需要更改的内容。单词
program
或module
必须是唯一的东西才能被识别?如果没有,可以遵循什么?例如,这条线是否符合条件:program"FOO"{
(我不知道你的语言的语法是什么;我只是在这里提出假设。)最简单的解决方案是要求单词单独成一行,但这不是一个很可能的要求:我们经常想要将评论之类的内容与此类标记放在同一行。另一方面,如果这条线
programming is complicated because we're not using to thinking precisely
将被视为已解析块的开始。所以我猜测,重要的是
program
(或模块)恰好位于行首的行,紧随其后的是空格(或行尾,这也是一个空格字符)。这将无法识别以下任何一项:program$$ This is a comment program;
但它会认
program $$ This is a comment program MyProgram
因此,可能需要根据您的需要进行一些调整。
我也对岛后文字的精确处理产生了怀疑。你期望只有一个岛吗?或者你可以:
非结构化文本 非结构化文本程序 ... 结束非结构化文本模块 ... 结束非结构化文本
以下假设您将要处理两个岛,因为它是最简单的。相反,如果您想忽略后面的所有
end
文本,则需要添加第三个开始条件,它只是忽略所有文本。(或者,如果您不想对岛后面的文本做任何事情,您可以在读取end
令牌后发送重置输入流。)一旦遇到or关键字,标记是否真的需要
end
位于行首?如果您需要,则扫描仪会将错误或无意的缩进转换为。在我看来这不太可能,所以我忽略了限制。我也在假设以非结构化文本开头的行仍然是非结构化文本;也就是说,规则甚至不需要尝试检测它。program
module
end
ID
end
<INITIAL>
同样,我不清楚岛内是否
program
以及是否是合法令牌,或者它们是否应该被视为标识符。module
如果它们是合法标记,是否有充分的理由限制它们出现在一行的开头?我认为不是,所以我忽略了限制。
也就是说,这是一个示例实现。我们首先声明开始条件(您可以阅读链接的 flex 文档以详细解释我为什么%x
要声明它),它必须进入 flex 输入的第一部分,在%%
%x ISLAND
%%
在<INITIAL>
状态中,我们只关心以program
或开头的行module
。如上所述,我们还需要确保目标词后跟空格。这实际上有点棘手,因为负匹配(“不以program
或module
”开头的行)很难写成正则表达式(没有负前瞻断言, (f)lex 不提供)。我们没有尝试这样做,而是分别识别行中的第一个单词和行的其余部分,这允许我们使用最长匹配规则。但首先,我们需要识别我们的特殊情况,即使用BEGIN
特殊动作切换开始条件。这里我们使用 flex 的“尾随上下文”/
^program/[[:space:]] { BEGIN(ISLAND); return PROG; }
^module/[[:space:]] { BEGIN(ISLAND); return MOD; }
[[:alpha:]]+ ; /* Any other word (at the beginning of a line) */
[^[:alpha:]\n].* ; /* See below */
\n ; /* The newline at the end of the line */
第三条规则匹配行首的字母词。[注 1] 第四条规则匹配单词后的其余行和不以单词开头的任何行。我们必须注意不要\n
在一行的开头匹配 a;如果不排除\n
负字符类中的 ,则该模式将匹配\n
空行的 ,然后匹配整个下一行,因此如果它跟在空行之后,它将跳过program
。(如果不清楚,您可能想尝试一下。)
<ISLAND>
开始条件本质上是您已经编写的规则,包装在开始条件块内。出于这个原因,我没有重复所有的规则。只有我改变的那些。请注意,在开始条件块内,flex 取消了规则必须从行首开始的限制。另请注意,无需引用仅由字母和数字组成的模式。只有带有元字符的模式需要被引用。
<ISLAND>{ /* Open the block */
[[:space:]]+ ; /* Ignore whitespace */
end { BEGIN(INITIAL); return END; }
program { return PROG; }
module { return MOD; }
/* And all the rest of the rules. */
}
笔记:
理论上,第三条规则可以匹配任何地方的字母词,因为它没有锚定
^
. 实际上,除了在行首之外不可能触发此规则,因为第四条规则总是延伸到行尾。但理论上,某些动作可能会BEGIN(INITIAL)
在下一个要读取的字符是字母而不是在行首的时刻调用。仔细检查代码会发现这是不可能的,但 flex 无法进行这种分析;从 flex 的角度来看,这是一种可能性,如果发生这种情况,则需要第三条规则。我知道这一点,因为我总是
%option nodefault
在我的 flex 文件中使用,这会导致 flex 警告我是否有可能没有规则适用于输入。而且由于我最初使用锚点编写规则 3,因此 flex 有义务警告我可以匹配默认规则。所以我不得不移除锚点以移除该警告。但是尽管有烦恼,但我认为警告很有用,因为在将来的某个时候,肯定有可能有人会引入一个BEGIN
动作,该动作创造了一个条件,在这种条件下,字母词的非锚定匹配是必要的。
推荐阅读
- postgresql - 使用 flyway、Access FE 和 Postgresql BE 跟踪 DDL 和 DML 更改
- java - 如何使用 Apache POI 将 .ppt 文件转换为 html?
- r - 计算 R 中 2 个日期时间 ("%Y-%m-%dT%H:%M:%OS-05:00") 列之间经过的时间
- python - Tensorflow tf.train.shuffle_batch() 无法创建内容大于 2GB 的张量原型
- powershell - 来自远程 GCI 命令的过滤结果问题
- r - 如何在一行中获取矩阵的所有值?
- c# - 如何使用 cookie 容器 c# 执行多个发布请求
- intellij-idea - 将光标放在方法/变量名称上不会突然显示它在 intellij 中的所有外观
- php - 根据用户权限在下拉菜单中显示/隐藏选项
- mercurial - Mercurial - 记录文件的第一次修订