为参数,将解析的结果作为一个参数传递给该匿名函数。
如果您和许多 Java 开发人员一样,那么就要花一点时间进行解析,让我们查看一下实际效果:
清单 6. 产品组合子
package com.tedneward.calcdsl
{
// ...
object Calc
{
object ExprParser extends JavaTokenParsers
{
def expr: Parser[Expr] =
(term ~ "+" ~ term) ^^ { case lhs~plus~rhs => BinaryOp("+", lhs, rhs) } |
(term ~ "-" ~ term) ^^ { case lhs~minus~rhs => BinaryOp("-", lhs, rhs) } |
term
def term: Parser[Expr] =
(factor ~ "*" ~ factor) ^^ { case lhs~times~rhs => BinaryOp("*", lhs, rhs) } |
(factor ~ "/" ~ factor) ^^ { case lhs~div~rhs => BinaryOp("/", lhs, rhs) } |
factor
def factor : Parser[Expr] =
"(" ~> expr <~ ")" |
floatingPointNumber ^^ {x => Number(x.toFloat) }
def parse(text : String) = parseAll(expr, text)
}
def parse(text : String) =
ExprParser.parse(text).get
// ...
}
// ...
}
^^ 组合子接收一个匿名函数,其解析结果(例如,假设输入的是 5 + 5,那么解析结果将是 ((5~+)~5))将会被单独传递并得到一个对象 — 在本例中,是一个适当类型的 BinaryObject。请再次注意模式匹配的强大功能;我将表达式的左边部分与 lhs 实例绑定在一起,将 + 部分与(未使用的)plus 实例绑定在一起,该表达式的右边则与 rhs 绑定,然后我分别使用 lhs 和 rhs 填充 BinaryOp 构造函数的左边和右边。
现在运行代码(记得注释掉临时测试),单元测试集会再次产生所有正确的结果:我们以前尝试的各种表达式不会再失败,因为现在解析器生成了派生 Expr 对象。前面已经说过,不进一步测试解析器是不负责任的,所以让我们添加更多的测试(包括我之前在解析器中使用的非正式测试):
清单 7. 测试解析器(这次是正式的)
package com.tedneward.calcdsl.test
{
class CalcTest
{
import org.junit._, Assert._
// ...
@Test def parseAnExpr1 =
assertEquals(
Number(5),
Calc.parse("5")
)
@Test def parseAnExpr2 =
assertEquals(
Number(5),
Calc.parse("(5)")
)
@Test def parseAnExpr3 =
assertEquals(
BinaryOp("+", Number(5), Number(5)),
Calc.parse("5 + 5")
)
@Test def parseAnExpr4 =
assertEquals(
BinaryOp("+", Number(5), Number(5)),
Calc.parse("(5 + 5)")
)
@Test def parseAnExpr5 =
assertEquals(
BinaryOp("+", BinaryOp("+", Number(5), Number(5)), Number(5)),
Calc.parse("(5 + 5) + 5")
)
@Test def parseAnExpr6 =
assertEquals(
BinaryOp("+", BinaryOp("+", Number(5), Number(5)), BinaryOp("+", Number(5),
Number(5))),
Calc.parse("(5 + 5) + (5 + 5)")
)
// other tests elided for brevity
}
}
面向Java开发人员的Scala指南 - 构建计算器,第3部分(5)
时间:2 |