如何切换打开的文件,例如由另一台扫描仪读取到下一台扫描仪 - 并将其提供给解析器?
答案 0 :(得分:4)
Flex缓冲区无法轻松地从一台扫描仪传输到另一台扫描仪。许多细节都是扫描仪专用的,需要进行逆向工程,从而导致可维护性降低。
但是,只要语义类型兼容,将两个(或更多)扫描仪定义组合到单个扫描仪中并不困难。只需要给他们不同的开始条件。由于可以在扫描仪动作之外设置启动条件,因此从一个扫描仪定义切换到另一个扫描仪定义是微不足道的。
由于Flex扫描仪是基于表格的,因此组合两个扫描仪并没有真正的低效率;实际上,不重复代码可能有一些价值。组合表可能略大于各个表的总和,因为可能有更多的字符等价类,但另一方面,较大的表可能允许更好的表压缩。这些影响都不会引人注意。
这是一个简单但可能有用的例子。此解析器读取文件并将${arithmetic expressions}
替换为已计算的表达式。 (因为它只是一个例子,只允许非常基本的表达式,但它应该很容易扩展。)
由于词汇扫描程序需要在启动条件SC_ECHO
中启动,因此需要对其进行初始化。就个人而言,我更喜欢在INITIAL
中启动以避免在这种简单情况下进行初始化,但有时扫描程序需要能够处理各种启动条件,因此我将代码保留在内。错误处理可能是改进了,但它有用。
解析器使用非常简单的error
规则来重新同步并跟踪替换错误。非终端subst
,file
和start
的语义值是文件的错误计数; expr
的语义值是表达式的值。在这个简单的例子中,它们都只是整数,因此yylval
的默认类型可以工作。
未终止的替换未得到妥善处理;特别是,如果在词法扫描期间读取EOF以进行替换,则不会在输出中插入任何指示。我把它当作练习。 :)
这是lexer:
%{
#include "xsub.tab.h"
%}
%option noinput nounput noyywrap nodefault
%option yylineno
%x SC_ECHO
%%
/* In a reentrant lexer, this would go into the state object */
static int braces;
/* This start condition just echos until it finds ${... */
<SC_ECHO>{
"${" braces = 0; BEGIN(INITIAL);
[^$\n]+ ECHO;
"$" ECHO;
\n ECHO;
}
/* We need to figure out where the substitution ends, which is why we can't
* just use a standard calculator. Here we deal with terminations.
*/
"{" ++braces; return '{';
"}" { if (braces) { --braces; return '}'; }
else { BEGIN(SC_ECHO); return FIN; }
}
/* The rest is just a normal calculator */
[0-9]+ yylval = strtol(yytext, NULL, 10); return NUMBER;
[[:blank:]]+ /* Ignore white space */
\n /* Ignore newlines, too (but could also be an error) */
. return yytext[0];
%%
void initialize_scanner(void) {
BEGIN(SC_ECHO);
}
解析器导出单个接口:
int parseFile(FILE *in, *out);
如果一切顺利,则返回0,否则返回错误的数量(以未确定的替换为模的上述问题的模数)。这是文件:
%{
#include <stdio.h>
int yylex(void);
void yyerror(const char* msg);
void initialize_scanner(void);
extern int yylineno;
extern FILE *yyin, *yyout;
%}
%token NUMBER FIN UNOP
%left '+' '-'
%left '*' '/' '%'
%nonassoc UNOP
%define parse.lac full
%define parse.error verbose
%%
start: file { if ($1) YYABORT; else YYACCEPT; }
file : { $$ = 0; }
| file subst { $$ = $1 + $2; }
subst: expr FIN { fprintf(yyout, "%d", $1); $$ = 0; }
| error FIN { fputs("${ BAD SUBSTITUTION }", yyout); $$ = 1; }
expr : NUMBER
| '-' expr %prec UNOP { $$ = -$2; }
| '(' expr ')' { $$ = $2; }
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr { $$ = $1 / $3; }
| expr '%' expr { $$ = $1 % $3; }
%%
void yyerror(const char* msg) {
fprintf(stderr, "%d: %s\n", yylineno, msg);
}
int parseFile(FILE* in, FILE* out) {
initialize_scanner();
yyin = in;
yyout = out;
return yyparse();
}
一个简单的驱动程序:
#include <stdio.h>
int parseFile(FILE* in, FILE* out);
int main() {
return parseFile(stdin, stdout);
}