首页 > 代码库 > 数据结构成为小语言
数据结构成为小语言
数据结构成为小语言
面向语言的开发并不一定意味着,一定要自己开发解析器或编译器。这就是说,我们将在下一章学习创建解析器,然后,把这样的解析器和本章介绍的方法照结合起来,去构建一个简单的编译器。可以通过创建数据结构和函数或模块,就能够做很多事,数据结构描述了准备做什么,而函数或模块定义了如何解释结构。
几乎可以用任何语言创建数据结构来表示一个程序,但是 F# 更适合。F# 的文字列表和数组很容易定义,不要求庞大的类型注解;它的联合类型能够创建结构,来表达相关的概念,但却不一定要包含相同类型的数据,可以用它来创建树型结构,这在创建语言时是非常有用的;最后,由于函数可以看作是值,因此,我们可以很容易把函数嵌入到数据结构中,这样,F# 的表达式就能成为语言的一部分,通常作为一个动作,来响应语言的特定条件。
我们先来看一下在FSharp.PowerPack.dll 中预定义的 DSL,通过 Arg(参数)模块能够快速构建命令行参数解析器。它是通过 F# 的联合和列表类型而实现的,先创建小语言,然后,用参数模块中的大量函数进行解释。
参数模块公开了一个元组类型argspec,它由两个字符串和一个联合类型 spec 组成。元组中的第一个字符串指定了命令行参数的名字,元组中的第二项是联合类型 spec,它指定了参数的内容,例如,它可以指定后面跟着的是字符串值,或者只是一个标志;它还可以指定如果发现命令行标记,应该做什么。元组中最后的字符串是关于这个标志做什么的文本描述,当命令行参数错误时会打印到控制台,对程序员来说也是一个有用的注释。
参数模块公开了两个函数用于解析参数:parse,它解析在命令行中传入的命令;和parse_argv,它要求直接传递参数给它。我们应该传递描述命令行参数的 argspec 类型列表的函数,所有被传递给函数的命令行参数都没有前缀 -,最后,是描述用法的字符串。
这个模块还公开了第三个函数,可以传递argspec 类型的列表,并直接写出用法。
下面的例子演示了如何以这种方法构建参数解析器,把从命令行收集的参数保存到标识符中准备以后使用,这里,是把它们定到控制台:
let myFlag = ref true
let myString = ref ""
let myInt = ref 0
let myFloat = ref 0.0
let (myStringList : string list ref) = ref []
let argList =
[ ("-set", Arg.Set myFlag, "Sets the value myFlag");
("-clear", Arg.Clear myFlag,"Clears the value myFlag");
("-str_val", Arg.String(fun x-> myString:= x), "Sets the value myString");
("-int_val", Arg.Int(fun x-> myInt :=x), "Sets the value myInt");
("-float_val", Arg.Float(fun x-> myFloat:= x), "Sets the value myFloat") ]
ifSystem.Environment.GetCommandLineArgs().Length <> 1 then
Arg.parse
argList
(funx -> myStringList := x :: !myStringList)
"Argmodule demo"
else
Arg.usage
argList
"Argmodule demo"
exit1
printfn "myFlag: %b" !myFlag
printfn "myString: %s" !myString
printfn "myInt: %i" !myInt
printfn "myFloat: %f" !myFloat
printfn "myStringList: %A"!myStringList
当运行前面的代码,没有命令行参数,或者命令行参数错误时,程序会输出下面的结果:
Arg module demo
-set:Sets the value my_flag
-clear:Clears the value my_flag
-str_val<string>: Sets the value my_string
-int_val<int>: Sets the value my_int
-float_val<float>: Sets the value my_float
--help:display this list of options
-help:display this list of options
当用下面的命令行运行前面的示例时:
args.exe -clear -str_val "helloworld" -int_val 10 -float_val 3.14 "file1" "file2""file3"::
输出如下:
myFlag: false
myString: hello world
myInt: 10
myFloat: 3.140000
myStringList: ["file3";"file2"; "file1"]
我特别喜欢这样的 DSL,因为它使问题更清晰,程序需要什么参数,如果接收到参数,应该进行什么样的处理。帮助文本也保存在这个结构中,有两个目的:如果命令行参数有错误,函数会自动输出一个帮助消息;当我们忘记什么参数时,可以提醒。我也喜欢用这种方式创建命令行解析器,因为,我用命令语言写过几个命令行解析器,那可不是令人满意的体验,最终必须写大量的代码,详细说明如何拆分命令行。如果你用.NET 写过代码,那么,通常会花太多的时间在调用字符串类型的IndexOf 和Substring 方法。