[英]Lexical analysis using lex
在我展示我在这里所做的工作之前,是我尝试做的作业(我是新来的,所以我不太确定我是否做得很好)。
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();
}
如您所见,我已经定义了所有我要支持的类型,我不知道如何实现下一个和后面的内容,有些指导性可能很棒,我也保存了行号,但是我想它们的含义与我写的不一样,我也不理解他们写的定义部分。
我知道这个问题看起来很奇怪,但是我之前没有任何指导或解释就得到了这个作业,所以我是自己学习的,我并不太了解(尽管我知道理论,谢谢!)。
我在公司项目中做了非常相似的事情。
关于代币
我为他们列举了...
关于next_token()
我的意图是通过以下方式将与令牌相关的全部信息存储到一个对象中:
另外,我想对这些生成的对象使用智能指针,更不用说,它们应该是C ++对象。
这是我意识到的:
重新定义yylex()
函数很容易。 因此,您甚至可以重命名它并更改其签名。
很难(如果不是不可能)将其与yacc /野牛放在一起。 主要问题是数据使用C联合( %union
如果我没记错的话)从lex(生成的代码)传递到yacc / bison(生成的代码)。 AC union和C ++对象-效果不佳。 (C联合中的一个对象可能有效,但多个绝对不能。)
幸运的是,第二个问题实际上对我来说并不存在,因为我使用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手册页 ,您可以在其中找到文档。 要找到有关如何重新定义yylex函数的说明,请在此网站上搜索“ YY_DECL”。
每当需要新令牌时,我的解析器就会调用lex()
。
笔记:
就我而言,我重命名了yylex()
,甚至使其成为了我的解析器类的方法。 (我这样做是为了简化词法分析器对私有解析器变量的访问。)
我提供了有效的作用域运算符,因为生成的lex代码不考虑我在C ++代码中使用的任何名称空间。
yyscan_t yyscanner
参数必须存在,因为我生成了可重入的扫描器。 您必须决定是否应该在那里。 (相反,您也可以提供其他参数。)
返回的RefToken
是指向产生的令牌的智能指针。 (通过智能指针,可以很容易地在不同的“位置”产生和使用令牌,而没有内存泄漏的危险。)
如果将生成的词法分析器与bison生成的解析器结合使用,则可能不那么容易。 在这种情况下,我将使用静态变量并组织队列以将值从lexer传递到解析器。 这将起作用,但是当然不如上面的方法那样优雅且省钱。
关于back_token()
一旦拥有使用令牌的解析器,就可以随便使用它们。 就我而言,要求之一是回溯的选项。 因此,我不得不不时地推回令牌来输入。 为此,我只是将它们堆叠在解析器中。 如果需要新令牌,请检查1st此堆栈是否为空。 如果没有弹出最上面的令牌,则调用lex()
方法以获得一个真正的新令牌。 我猜在您的情况下,可以使用类似的解决方案来实现back_token()
。
关于空白
我的词法分析器实际上有两种类型的规则(即规则操作):
最终return new Token(...);
以失败告终的行动break;
我使用后者来消耗分隔符(例如,空格等),甚至注释(解析器甚至看不到它们)。 之所以可行,是因为该词法分析器实际上只是包裹在for()
循环中的switch()
而已。 (我从flex文档中学到了这个“技巧”,在某处明确提到了该技巧)。
还有什么...
除了YY_DECL
,我还重新定义了YY_INPUT
。 我这样做是为了将词法分析器与C ++ std::stream
(而不是yyin
)一起使用。
IMHO flex确实提供了非常全面的手册。 但是,每当我有疑问时,我都会查看flex生成的C文件。 我通常简单地忽略有限自动机的这些可怕的int
数组。 剩下的就是它们周围的基础结构,您会发现C操作(用lex规则编写)嵌入在某个位置。 检查周围的代码可以使事情变得更清楚。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.