首页 > 代码库 > 文本符号化:Fslex

文本符号化:Fslex

文本符号化:Fslex

 

文本符号化(Tokenizing,有时也叫词法分析,lexical analysis 或 lexing),其基本意思是把文本分成可管理的块(lump),或符号(token)。要用到工具fslex.exe,它本身就是创建词法分析器(有时也叫扫描器,scanner),进行文本符号化的程序或模块的特定域语言。fslex.exe 是命令行程序,接收参数是表示词法分析器的文本文件,转换成 F# 文件,实现分析器。

fslex.exe 文件的扩展名 .fsl。文件有一个可选的头,放在大括号({}),是纯 F# 代码,通常用于引用模块,或者可能是定义辅助函数(helper function);其余部分定义正则表达式,组成词法分析器。可以用关键字let 把正则表达式绑定到标识符,如:

 

let digit = [‘0‘-‘9‘]

 

或者把正则表达式定义为规则的一部分。规则(rule)是正则表达式的集合,它以竞争方式匹配文本部分。规则的定义,使用关键字rule,加规则名,加等号,加关键字parse;接下来是正则表达式的定义,应该加一个动作,用大括号括起来的 F# 表达式。每个规则用竖线(|)隔开。每个规则根据文本流的匹配情况,将会变成一个函数。如果找到一个匹配,就触发这个规则,并执行F# 表达式;如果有几个正则表达式都匹配这个规则,那么,使用最长的匹配。函数的返回值也就是动作的返回值,因此,每个动作的类型必须相同;如果没有找到匹配,就会引发异常。

虽然动作可以是任何有效的 F# 表达式,但是,通常应该返回将用在fsyacc 文件中的符号声明,详细内容看下一节“生成解析器:Fsyacc”。如果想使用自身的词法分析器,可以在这儿放任意想发生的逻辑,例如,把符号写到控制台,或者把找到的符号保存在列表中。

下面的例子就显示了这样的文件,它能将小语言符号化。通常做动作中两件事的其中之一。如果对匹配感兴趣,那么就返回在解析器文件中定义的符号,都是大写字母的标识符,比如 RPAREN or MULTI;如果不感兴趣,就用专门的值 lexbuf 去调用token 函数,以再次启动解析。lexbuf 值会自动放在解析器定义中,表示文本流已经处理过了,它的类型是Microsoft.FSharp.Tools.FsLex.LexBuffer。还要注意,如果真正感兴趣的是发现值的位置,而不是找到的值,那么,可以用函数curLexeme,获取表示 lexbuf 匹配的字符串。

 

{

module Strangelights.ExpressionParser.Lexer

 

open System

open System.Text

open Strangelights.ExpressionParser.Parser

open Microsoft.FSharp.Text.Lexing

 

let curLexeme (lb: LexBuffer<byte>) =

 Encoding.ASCII.GetString(lb.Lexeme, 0, lb.Lexeme.Length)

}

 

let digit = [‘0‘-‘9‘]

let whitespace = [‘ ‘‘\t‘ ]

let newline = (‘\n‘ |‘\r‘ ‘\n‘)

 

rule token = parse

| whitespace { token lexbuf }

| newline { token lexbuf }

| "(" {LPAREN }

| ")" {RPAREN }

| "*" {MULTI }

| "/" {DIV }

| "+" {PLUS }

| "-" {MINUS }

| [‘a‘-‘z‘ ‘A‘-‘Z‘ ‘_‘]+ { ID(curLexeme(lexbuf)) }

| [‘-‘]?digit+(‘.‘digit+)?([‘e‘‘E‘]digit+)?

    { FLOAT(Double.Parse(curLexeme(lexbuf))) }

| eof { EOF }

 

一个词法分析器可以包含几个规则,更多的规则之间用关键字and 分隔,加规则名,加等号,加关键字parse;之后,是组成这个规则的正则表达式定义。这通常用于在语言中实现注释。注释常常会产生词法解析器错误,因为注释中可以包含任意文本。为了解决这个问题,当检测到注释开始的符号时,通常就切换到另外的规则,只查找注释结束的符号,而忽略其他所有的输入。

下面的例子是一个简单的解析器,既可以找出像 F# 标识符的字符串,也能舍弃 C# 风格的多行注释。注意,当找到注释开始时,调用comment 函数以跳转到comment 规则,找到注释的结束,返回空,跳出。

 

{

open Microsoft.FSharp.TextLexing

}

rule token = parse

| "/*" {comment lexbuf; token lexbuf }

| [‘_‘‘a‘-‘z‘‘A‘-‘Z‘][‘_‘‘a‘-‘z‘‘A‘-‘Z‘‘0‘-‘9‘]*

    { lexemelexbuf }

and comment = parse

| "*/" |eof { () }

| _ { comment lexbuf }