在Jflex如何比较好的识别字符串

前言

本文专注于解决在梅努斯国际工程学院中的CS310FZ编译原理这节课的某一次实验
在Lab4中,我们被要求写flex文件以识别:
字符串文字——由双引号括起来的字符序列。您可以假设包含的字符串不包含任何引号或其他可转义字符
因此本文将基于这个目标进行任务的完成————如果您是学习研究词法编辑器而看到了本文,建议阅读Jflex手册的第3-4章,作者的思路十分清晰

储备

上官方手册:https://www.jflex.de/manual.html#content
如果您想比较好的明白这篇文章的内容,您可以先完整阅读以下部分:
1.A simple Example: How to work with JFlex
2.Lexical Specifications

如果您懒得阅读,请直接看下面的部分:

完成该任务的知识

flex文件的组成

JFlex 的词法规范文件由三个部分组成,由以 %% 开头的单行分隔,简而言之就是这样:

1
2
3
4
5
6
7
8
A.用户代码部分:这部分会写在生成的Java文件的类的最开头,一般而言会写一些ipmort在这  

%%
B.设置和声明:可以在这里设置你生成的Java文件,最重要的是可以在这里进行变量的声明

%%
C.Lexical rules词法规则,就是识别啥

JFlex中的Lexical rules的语法

即在C部分的语法,这部分语法可以被概括为:

1
识别的东西   +   {额外的Java代码}   

举个例子,我们写下

1
"float"           {return new Token (Token.FLOAT_WORD, yytext());}

在生成的Java文件中就会有如下的代码:

1
2
3
case 36:
{ return new Token (Token.FLOAT_WORD, yytext());
}

其中怎么到这个case,就是你写的“识别的东西”决定的,一般就是写一个正则表达式

简单来说,这部分的语法:前面的负责找东西,大括号里面的可以指定找到之后干啥

JFlex中的状态

JFlex中是有状态来进行匹配的,状态用<XXXXX>来标识
默认的状态是<YYINITIAL>
书接上文,如果我们在Lexical rules部分写下:

1
<AAAA> rex   +   {额外的Java代码} 

就代表只有在A状态下,才进行rex的识别匹配,识别匹配成功了才能执行大括号里面的任务

YYINITIAL是隐含的默认状态,其实可以省略

用手册的例子说明:
image.png
1.默认情况(YYINITIAL)会识别expr1,然后识别成功后,因为代码yybegin(A),会让Jflex进入到A状态
2.在YYINITIAL和A状态下,如果识别到了expr2,采取action(手册里很喜欢把这部分叫action)
3.在A状态下,如果识别到expr3则进入对应action,而状态也可以内联,这里就不多说了

状态的声明

一定要在B.设置和声明这部分声明你的状态
使用Jflex的语法%state 名字

主要,用state和xstate声明是不一样的。手册里有相关介绍,但是这里我们用不上

开始干活吧!

0.手册是怎么实现的

手册利用了一个包import java_cup.runtime.*;实现,但是我们不想导包(而且你提交的时候老师助教也不一定有这个包)
所以我们不这么干————有探究精神的话可以导并且参考手册的A simple Example: How to work with JFlex部分

1.存储String

我们用一个StringBuffer来存储字符串的内容,这部分要先写在B.设置和声明里面

1
2
3
%{
StringBuffer string = new StringBuffer();
%}

2.声明STRING状态

同样的,在B.设置和声明里面写入:

1
%state STRING

3.在Lexical rules部分写下相关逻辑

C.Lexical rules中写下:

1
<YYINITIAL>\"                { string.setLength(0); yybegin(STRING); }

这个语句的意思是,在默认状态下,如果检测到了双引号,那么:
1.将StrinBuffer清空
2.进入STRING状态

然后我们继续写:

1
2
3
4
5
6
7
8
9
10
11
12
<STRING> {
\" { yybegin(YYINITIAL); }
[^\n\r\"\\]+ { return new Token (Token.STRING, yytext());}

\\t { string.append('\t'); }
\\n { string.append('\n'); }

\\r { string.append('\r'); }
\\\" { string.append('\"'); }
\\ { string.append('\\'); }

}

这个就是Jflex进入STRING后会识别并采用的操作,后面几行不重要,意思就是各种转义的处理
重要的是[^\n\r\"\\]+ { return new Token (Token.STRING, yytext());}
它意味着,如果检测到字符串结束,就执行你自定义的action,并且开始下一次识别

FINAL

至此大功告成
当然,这是我晚上做实验的时候(好不容易)模仿原定思路修改的,也许会有bug,请多包涵
另外,关于注释的识别,也可以按照这个思路完成