ML-Lex入门

ML-Lex是Lex的ML语言实现。

在我使用的SML/NJ中,已经包含了ML-Lex。你可以通过链接下载符合你需要的SML/NJ版本。

ML-Lex是Lex的ML语言实现,所以它的用法与Lex类似:你需要完成一个.lex文件,然后运行lex,这样就能得到文件的词法序列。

Lex文件分成三个区块, 均以一个只有两个百分比符号(%)的单行来分隔,它具有如下形式:

用户声明 %% ML-Lex 定义 %% 规则

在我实现Tig语言的语法分析时,利用了虎书的一些辅助代码,你可以在这里找到他们,辅助文件结构如下:

.
├── driver.sml
├── errormsg.sml
├── sources.cm
├── tiger.lex
├── tokens.sig
└── tokens.sml

###tiger.lex
其中,tiger.lex是最重要的文件,你需要在这里完成词法分析的主要部分。

#####声明部分
在这里你可以定义一些变量,例如定义行号以及列号等,方便下面的规则中使用。

#####ML-Lex 定义
这里和Lex一样,需要你定义一些正则表达式,如

1
digit = [0-9];

就定义了数字字符。

#####规则
这里是整个Lex文件中最重要的部分。

规则的语法如下:

1
模式 => 动作

这里的模式可以是一个字符,如“+”,也可以是定义中规定的正则表达式,注意,使用正则表达式时,需要使用{}

在语法分析中,我们需要做的动作就是打印出所有的词法单元,所以可以这样写:

1
{digit}+ => (Tokens.INT(valOf(Int.toString yytext), yypos, yypos+size yytext))

这样,就会调用tokens.sml中的函数,输出“INT(数字) 该数字在文件中出现的位置”。

###其他文件说明
sources.cm类似于Makefile文件,可以在sml中使用CM.make “souces.cm”;编译文件,编译完成后将生成tiger.lex.sml,利用Mlexr即可对输入的文件进行处理。

tokens.sml以及tokens.sig则是对应的辅助函数,里面已经定义了一部分可能使用到的功能,如下面这句:

1
fun TYPE(i,j) = "TYPE   " ^ Int.toString(i)

将会打印出字符串“TYPE [数字]”。在lex文件的规则部分,对匹配使用对应的函数,就可以打印出词法单元。

errormsg.sml的作用与tokens.*类似,不过它定义的是错误而非词法单元的相关动作。

driver.sml定义了Parse结构,在这个结构中调用已经编译完成的tiger.lex.sml对输入的文件处理,将词法单元输出至控制台。我重新编写了该文件,可以将处理的词法文件输出至.out文件,方便观察。

##运行
在分析文件构成,完成tiger.lex后,让我们来实际测试一下吧。

首先打开sml

shell
1
sml

然后编译

sml
1
CM.make “sources.cm”;

调用Parse处理输入

shell
1
Parse.parse "a.tig" "a.tig.out";

这样就完成了。

最后贴一下a.tig以及a.tig.out

a.tig
1
/*test*/
let 
     var N := 8.5
     var M := 9
  in print("Success\n")
end

a.tig.out
1
COMMENT(/*test*/)     2
ID(let)     11
ID(var)     21
ID(N)     25
ASSIGN   27
REAL(8.5)  30
ID(var)     39
ID(M)     43
ASSIGN   45
INT(9)   48
ID(in)     52
ID(print)     55
LPAREN   60
STRING("Success\n")     61
RPAREN   72
ID(end)     74
EOF   78