📄 scanner.l
字号:
%{
/******************************************************************************
*******************************************************************************
scanner.l
词法分析lex源文件
本文件定义了VSL语言的词法,及词汇处理函数。
文件修改时间
============
2005年5月6日: Lex源文件 (C) hfwang
*******************************************************************************
******************************************************************************/
/* 定义部分 */
/* 以下是词法分析器需要的头文件和函数的引用说明,mkxxx()将识别的词形放入符号表
中,全局变量lineno记录当前行号,在词法和语法分析出错时,可输出错误所在的行,
以方便对程序的修改 */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "vc.h"
#include "parser.h" /* 由yacc生成的词汇的宏定义其语义值的数据类型 */
void mkname( void ) ;
void mkval( void ) ;
void mktext( void ) ; /* 函数的定义在本文件的代码部分 */
int lineno = 1;
/* 以下定义一组正规表达式,以方便对各种词汇的处理。由于是通过换行符"\n"来记录
行号,我们对它单独处理,所以分界符delimiter和空白字符whitespace中没有"\n"。
VSL语言的词法规定关键字一律用大写字母表示,而标识符一律用小写字母表示,在
此定义un_letter表示大写字母,lc_letter表示小写字母,VSL的注释由双斜杠//直
到第一个环行符\n之前结束;VSL的常数只有正数常数,在此由integer定义;VSL的
标识符是以小写字母开始并由小写字母和数字组成的字符串,其长度没有限制,在此
由variable定义,VSL的字符串中允许有转义字符"\""和"\n"(定义为espaced_char),
分别表示字符双引号"和换行,在此由text定义。 */
%}
comment "//".*
delimiter [ \t]
whitespace {delimiter}+
uc_letter [A-Z]
lc_letter [a-z]
letter {lc_letter}|{uc_letter}
ascii_char [^\"\n]
escaped_char \\n|\\\"
digit [0-9]
variable {lc_letter}({lc_letter}|{digit})*
integer {digit}+
text \"({ascii_char}|{escaped_char})*\"
%%
%{
/* 规则部分 */
/* 在规则部分定义了VSL的词汇的模式及再识别词汇后对应的C操作,对注释和空白字符
yylex不做任何操作,即相当于吃掉这些字符,继续执行yylex,对换行符"\n",其唯
一的操作是行计数器加1,对标识符,常正数和字符串常量,我们首先将识别的词形或
对应的数值放入符号表中,并定义其语义值yylval为在符号表的指针,再返回其对应
的词汇编码给调用它的语法分析函数yyparse()。对关键字,只是简单地返回其对应的
词汇码,对于单个的字符,由于在语义分析中其词汇码就是该字符对应的ASCII码,所
以返回该字符yytext[0]即可。在匹配词形后的操作中如果有return语句,则表示结束
yylex()函数,但是,yylex()的环境保持不变(如扫描文件的文件指针),下次再调
用时,yylex()继续扫描输入文件还未扫描的字符。
*/
%}
{comment} ; { }
{whitespace} ; { }
\n { lineno++; }
{variable} { mkname();
return VARIABLE; }
{integer} { mkval();
return INTEGER;}
{text} { mktext();
return TEXT;}
":=" { return ASSIGN_SYMBOL;}
FUNC { return FUNC;}
PRINT { return PRINT;}
RETURN { return RETURN;}
CONTINUE { return CONTINUE;}
IF { return IF;}
THEN { return THEN;}
ELSE { return ELSE;}
FI { return FI;}
WHILE { return WHILE;}
DO { return DO;}
DONE { return DONE;}
VAR { return VAR;}
. { return yytext[0]; }
%%
/* 代码部分 */
/* 在识别一个标识符、正数和字符串常量后,通过以下的mkxxx()将其装入符号表中,并
将符号指针作为该终结符的语义值(全局变量yylval)。*/
void mkname ( void )
/* 创建标识符函数,在词法分析程序匹配一个标识符之后,虽然指向该标识符的指针
yytext是全局变量,但是保存该标识符的字符数组是局部变量,即在yylex函数返回
之后不再可使用,因此在保存标识符时,必须通过动态申请同该标识符长度相同的
内存来存放该标识符,只有这样该标识符才能被整个编译器使用。*/
{
struct symb *t;
char *s;
/* 首先查看符号表,如果该标识符已在符号表中,则表明当前标识符不是首
次出现,我们将yylval设为该标识符在符号表的指针并返回即可 */
if((t = lookup(yytext)) != NULL)
{
yylval.symb = t;
return;
}
s = (char *) safe_malloc(yyleng + 1); /* 申请备份的内存,全局变量
yyleng是当前词形的长度 */
strncpy(s, yytext, yyleng); /* 安全字符串考拷贝 */
s[yyleng] = EOS;
t = get_symb(); /* 创建符号内存单元 */
t->type = T_UNDEF; /* 定义符号单元 */
t->TEXT1 = s;
insert(t); /* 插入符号表 */
yylval.symb = t; /* 设置语义值 */
} /* void mkname ( void ) 结束 */
void mkval( void )
/* 创建正数常量:通过库函数atoi()将识别的正数字符串转换为对应的正数,调用mkconst()
对该整数创建一个符号内存单元并将之放入符号表中,设置语义值为该符号指针。 */
{
yylval.symb = mkconst( atoi( yytext ));
} /* void mkval( void ) 结束 */
void mktext( void )
/* 创建字符串常量:用mkname()对识别的字符串创建一个符号内存单元(注意:该字符串的首尾
是双引号",并有转义字符,因此,在代码生成中对字符串分配内存时,一定要将双引号除掉,
并将转义字符转换成相应的ASCII码),设置字符串的符号属性,在此,我们使用标号来记录
字符串的地址,标号计数器next_label分配一个标号后加1。*/
{
mkname();
yylval.symb->type = T_TEXT;
yylval.symb->VAL2 = next_label++;
} /* void mktext( void ) 结束 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -