Flex:如何将术语定义为行首的第一个(独家)

Flex: How to define a term to be the first one at the beginning of a line(exclusively)

我需要一些关于我在 flex 代码中遇到的问题的帮助。

我的任务:编写识别编程语言声明部分的 flex 代码,如下所述。

让编程语言PL。其变量定义部分说明如下:

开头我们得从关键字"var"开始。写完这个关键字后,我们要写变量名(一个或多个),用逗号“,”分隔。然后插入一个冒号“:”,之后我们必须写变量类型(在我的示例中是实数、布尔值、整数或字符),后跟一个分号“;”。完成前面的步骤后,有可能在新的一行中声明新变量(变量名用逗号“,”分隔,然后是冒号“:”,然后是变量类型,然后是分号“;”),但我们不能使用"var"关键字再次在新行的开头("var"关键字写了一次!!!)

例如

var number_of_attendants, sum: integer;
ticket_price: real;
symbols: char;

具体来说,我不知道如何定义每个声明部分必须仅以 'var' 关键字开头。到现在为止,如果我开始一个声明部分直接声明一个变量,比如 x(没有在行的开头写 "var"),那么就不会发生错误(不需要的状态)。

我当前的 flex 代码如下:

%{
#include <stdio.h>
%}
VAR_DEFINER "var"
VAR_NAME [a-zA-Z][a-zA-Z0-9_]*
VAR_TYPE "real"|"boolean"|"integer"|"char"
SUBEXPRESSION [{VAR_NAME}[","{VAR_NAME}]*":"[ \t\n]*{VAR_TYPE}";"]+
EXPRESSION {VAR_DEFINER}{SUBEXPRESSION}
%%
^{EXPRESSION}                 { 
                                  printf("This is not a well-syntaxed expression!\n"); 
                                  return 0;
                            }
{EXPRESSION}                        printf("This is a well-syntaxed expression!\n");
";"[ \t\n]*{VAR_DEFINER}    {
                                  printf("The keyword 'var' is defined once at the beginning of a new line. You can not use it again\n");
                                  return 0;
                            }
{VAR_DEFINER}                  printf("A keyword: %s\n", yytext);
^{VAR_DEFINER}                 printf("Each and every declaration part must start with the 'var' keyword.\n");
{VAR_TYPE}";"                     printf("The variable type is: %s\n", yytext);
{VAR_NAME}                        printf("A variable name: %s\n", yytext);
","/[ \t\n]*{VAR_NAME}            /* eat up commas */
":"/[ \t\n]*{VAR_TYPE}";"         /* eat up single colon */
[ \t\n]+                          /* eat up whitespace */
.                           {
                                  printf("Unrecognized character: %s\n", yytext);
                                  return 0;
                            }
%%
main(argc, argv)
int argc;
char** argv;
{
++argv, --argc;
if (argc > 0) 
yyin = fopen(argv[0],"r");
else 
yyin = stdin;
yylex();
}

我希望已经尽可能清楚了。

期待看到您的回答!

您似乎试图在扫描仪中做太多事情。您真的必须在 Flex 中做所有事情吗?换句话说,这是学习Flex高级用法的练习,还是可以使用更合适的工具来解决的问题?

我听说第一个 Fortran 编译器在 1950 年代花费了 18 个工作人员年的时间来创建。今天,"a substantial compiler can be implemented even as a student project in a one-semester compiler design course",正如 1986 年的 Dragon Book 所说。效率提高的主要原因之一是我们学会了如何将编译器划分为可以单独构建的模块。典型编译器的前两个这样的部分,或 phases,是 scannerparser

扫描器或词法分析器可以由 Flex 从规范文件生成,或者以其他方式构造。它的工作是读取由一系列字符组成的输入,并将其拆分为一系列 tokens。标记是输入语言中最小的有意义部分,例如分号、关键字 var、标识符 number_of_attendants 或运算符 <=。您不应该使用扫描仪做更多的事情。

以下是我将如何为您的令牌编写简化的 Flex 规范:

[ \t\n] { /* Ignore all whitespace */ }
var { return VAR; }
real { return REAL; }
boolean { return BOOLEAN; }
integer { return INTEGER; }
char { return CHAR; }
[a-zA-Z][a-zA-Z0-9_]* { return VAR_NAME; }
. { return yytext[0]; }

然后将标记序列传递给解析器或语法分析器。解析器将标记序列与该语言的语法进行比较。例如,输入 var number_of_attendants, sum : integer; 由关键字 var、逗号分隔的变量列表、冒号、数据类型关键字和分号组成。如果我理解你的输入应该是什么样子,也许这个语法是正确的:

program : VAR typedecls ;
typedecls : typedecl | typedecls typedecl ;
typedecl : varlist ':' var_type ';' ;
varlist : VAR_NAME | varlist ',' VAR_NAME ;
var_type : REAL | BOOLEAN | INTEGER | CHAR ;

这个语法恰好是用一种经常与Flex一起使用的解析器生成器Bison可以理解的格式编写的。

如果您将解决方案分成词汇部分(使用 Flex)和语法部分(使用 Bison),您的生活可能会更简单、更快乐。