在我展示我在这里所做的之前,我要做的是我尝试过的作业(我是新来的,所以我不确定我是否做得对)。
1. Implement lexical analyzer (using FLEX), as follows:
- Lexical analyzer supplies services next_token(), back_token()
- Lexical analyzer reads text from the input file and identifies tokens. This
happens when function next_token() is called.
- When a token is identified in the input text, it should be stored in a data
structure. For each token, the following attributes are saved:
* token type
* token lexeme
* number of the line in the input text in which this token was found.
- Blanks, tabs, new lines – are not tokens, and should be ignored
- For each token, print (on a separate line) its type (e.g. rel_op , number , etc.)
and lexeme
- Each operation, keyword, separation sign and each type of number should be
implemented as a token of a different kind
- Kinds of tokens are coded with integer numbers, for example:
# define ID_tok 1
# define COMMA_tok 2
使用Flex,我已经写下了:
%{
#include<stdio.h>
int line=1;
# define ID_tok 1
# define COMMA_tok 2
#define REL_OP_tok 3
#define NUMBER_tok 4
#define KEYWORDS_tok 5
%}
binary_ar_op "*"|"/"|"+"|"-"
rel_op "=="|"!="|">"|"<"|"<="|">="
id [a-z][a-z0-9]*
number ["+"|"-"]?[0-9]*("."[0-9]+)?
keywords "prabegin"|"parend"|"task"|"begin"|"end"|"integer"|"real"|"do"|"until"|"od"|"send"|"accept"
%%
\n {line++; printf("\n%d:",line);}
{binary_ar_op}+ {printf( "An binary_ar_op: %s (%d) at line(%d)\n", yytext,
atoi( yytext ),line);}
{rel_op}+ {printf( "An rel_op: %s (%d) at line(%d)\n", yytext,
atoi( yytext ),line);}
{id}+ {printf( "An id: %s (%d) at line(%d)\n", yytext,
atoi( yytext ),line);}
{number}+ {printf( "An number: %s (%d) at line(%d)\n", yytext,
atoi( yytext ),line);}
%%
int yywrap()
{
return 1;
}
main()
{
printf("Enter a string of data\n");
yylex();
}
正如你所看到的,我已经定义了我支持的所有类型,我不知道如何实现下一个和后一个,一些指南谁会是伟大的,我也保存了行号,但我想他们意味着其他,然后我写的,
我也不明白他们写的定义部分。
我知道这个问题看起来很奇怪,但我之前没有任何指导和解释就完成了这个作业,所以我是自己学习的,我并不完全理解(虽然我知道这个理论,谢谢!).
最佳答案
我在我们公司的项目中做了类似的事情。
关于令牌
我给他们做了个统计。。。
关于下一个令牌()
我的目的是将所有与令牌相关的信息存储到一个对象中:
acutal令牌(枚举值)
词素(std::string)
文件位置(由文件名指针、行和列组成)。
此外,我想使用这些生成的对象的智能指针,更不用说,它们应该是C++对象。
这就是我意识到的:
很容易重新定义yylex()
函数因此,您甚至可以重命名它并更改其签名。
很难(如果不是不可能的话)把这个和yacc/bison结合起来主要的问题是,数据是使用C联合从lex(生成代码)传递到yacc/bison(生成代码)的(如果我没记错的话,%union
)一个C组合和C++对象——不太好用。(C union中的一个对象可以工作,但多个对象肯定不行。)
对于我的运气来说,第二个问题实际上是不存在的,因为我使用Flex,但是写(同时生成)递归下降分析器(直接在C++中)。
那么,如何解决第一个问题呢这是我的代码:
/// redefines proto for generated yylex function.
#define YY_DECL \
RF::YAMS::Sim::ActionScript::RefToken \
RF::YAMS::Sim::ActionScript::Compiler::lex(yyscan_t yyscanner)
这里是flex man page您可以找到文档的地方要了解如何重新定义yylex函数的说明,请在本网站上搜索“YY_DECL”。
每当需要新的标记时,我的解析器都会调用
lex()
。笔记:
在我的例子中,我重命名了
yylex()
,甚至把它作为我的解析器类的一个方法(我这样做是为了简化lexer对私有解析器变量的访问。)我提供了丰富的范围运算符,因为生成的LeX代码不考虑我在C++代码中使用的任何命名空间。
必须有
yyscan_t yyscanner
参数,因为我生成了重新进入的扫描仪你必须决定它是否应该在那里(相反,您也可以提供其他参数。)返回的
RefToken
是指向生成的令牌的智能指针(智能指针使在不同的“位置”生成和使用令牌变得非常容易,而不存在内存泄漏的危险。)如果生成的lexer与bison生成的解析器相结合,可能就不那么容易了在本例中,我将使用静态变量并组织队列以将值从lexer传递到解析器这是可行的,但是,当然,并不像上面的方法那样优雅和省钱。
关于back_token()
一旦有了一个使用标记的解析器,您可以随心所欲地使用它们在我的案例中,其中一个要求是可以选择回溯因此,我必须不时地将令牌推回输入为此,我只需将它们堆栈到解析器中如果需要一个新的令牌,我首先检查这个堆栈是否为空如果没有弹出最上面的标记,则调用
lex()
方法以获取真正新的标记我想在您的案例中,可以使用类似的解决方案来实现back_token()
。关于空白
实际上,在我的lexer中有两种类型的规则(即规则操作):
以
return new Token(...);
结尾的操作以
break;
结尾的操作后者我用来使用分隔符(即空白等)甚至注释(解析器甚至看不到它们)这是因为lexer实际上只是一个封装在
switch()
循环中的for()
(我从flex医生那里学到了这个“诀窍”在某处被明确提及。)还有什么。。。
除了
YY_DECL
,我还重新定义了YY_INPUT
我这样做是为了用C++CX>(而不是std::stream
)使用Listor。IMHO flex提供了非常全面的手册然而,每当我有疑问时,我都会查看flex生成的C文件对于有限自动机有这些可怕的
yyin
数组,我通常忽略它们剩下的是它们周围的基础结构,你会发现你的C操作(写在lex规则中)嵌入在某个地方检查周围的代码可以使事情更清楚。
关于c - 使用词法进行词法分析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43088952/