计算器DSL的设计:
根据那些基本的编译理论中最精华的部分,您可以得知一个语言处理器(包括解释器和编译器)的基本运算至少由两个阶段组成:
- 解析器,用于获取输入的文本并将其转换成 Abstract Syntax Tree(AST)抽象语法树。
- 代码生成器(在编译器的情况下),用于获取 AST 并从中生成所需字节码;或是求值器(在解释器的情况下),用于获取 AST 并计算它在 AST 里面所发现的内容。
什么是case类( 是通过工程方法发创建的):
package caseclass
case class Person(first: String, last: String, age: Int) <span style="background-color: rgb(255, 255, 255);">{
println(first);
println(last)
println(age)
}</span>
object App {
def main(args: Array[String]): Unit =
{
val ted = Person("Ted", "Neward", 37)
val amanda = Person("Amanda", "Laucher", 27)
System.out.println(process(ted))
System.out.println(process(amanda))
}
def process(p: Person) =
{
"Processing " + p + " reveals that" +
(p match {
<span style="background-color: rgb(192, 192, 192);">case Person(_, _, a) if a > 30 =>
" they're certainly old."
case Person(_, "Neward", _) =>
" they come from good genes...."
case Person(first, last, ageInYears) if ageInYears > 17 =>
first + " " + last + " is " + ageInYears + " years old."
case _ =>
" I have no idea what to do with this person"</span>
})
}
}
第一个 case
表达式里面有两个通配符(带下划线的字符就是通配符),这意味着该匹配将会为符合匹配的 Person
中那两个字段获取任何值,但是它引入了一个局部变量 a
,p.age
中的值会绑定在这个局部变量上。这个 case 只有在同时提供的起保护作用的表达式(跟在它后边的 if
表达式)成功时才会成功, 但只有第一个 Person
会这样,第二个就不会了。第二个 case
表达式 在 Person
的 firstName
部分使用了一个通配符,但在 lastName
部分使用常量字符串 Neward
来匹配,在 age
部分使用通配符来匹配。
由于第一个 Person
已经通过前面的 case
匹配了,而且第二个 Person
没有姓 Neward
,所以该匹配不会 为任何一个 Person
而被触发(但是,Person("Michael", "Neward", 15)
会由于第一个 case 中的 guard 子句失败而转到第二个 case)。
第三个示例展示了模式匹配的一个常见用途,有时称之为提取,在这个提取过程中,匹配对象 p
中的值为了能够在 case 块内使用而被提取到局部变量中(第一个、最后一个和 ageInYears
)。最后的 case 表达式是普通 case 的默认值,它只有在其他 case 表达式均未成功的情况下才会被触发。
简要了解了 case 类和模式匹配之后,接下来让我们回到创建计算器 AST 的任务上。