从Java走进Scala构建计算器DSL文档格式.docx
《从Java走进Scala构建计算器DSL文档格式.docx》由会员分享,可在线阅读,更多相关《从Java走进Scala构建计算器DSL文档格式.docx(11页珍藏版)》请在冰豆网上搜索。
8.private[calcdsl]caseclassUnaryOp(operator:
String,arg:
Expr)extendsExpr
9.private[calcdsl]caseclassBinaryOp(operator:
String,left:
Expr,right:
Expr)
10.extendsExpr
11.
12.}
……对此我们可以提供类似解释器的行为,它能最大限度地简化数学表达式……
清单2.后端(解释器)
5.objectCalc
6.{
7.defsimplify(e:
Expr):
Expr={
8.//firstsimplifythesubexpressions
9.valsimpSubs=ematch{
10.//Askeachsidetosimplify
11.caseBinaryOp(op,left,right)=>
BinaryOp(op,simplify(left),simplify(right))
12.//Asktheoperandtosimplify
13.caseUnaryOp(op,operand)=>
UnaryOp(op,simplify(operand))
14.//Anythingelsedoesn'
thavecomplexity(nooperandstosimplify)
15.case_=>
e
16.}
17.
18.//nowsimplifyatthetop,assumingthecomponentsarealreadysimplified
19.defsimplifyTop(x:
Expr)=xmatch{
20.//Doublenegationreturnstheoriginalvalue
21.caseUnaryOp("
-"
UnaryOp("
x))=>
x
22.
23.//Positivereturnstheoriginalvalue
24.caseUnaryOp("
+"
x)=>
25.
26.//Multiplyingxby1returnstheoriginalvalue
27.caseBinaryOp("
*"
x,Number
(1))=>
28.
29.//Multiplying1byxreturnstheoriginalvalue
30.caseBinaryOp("
Number
(1),x)=>
31.
32.//Multiplyingxby0returnszero
33.caseBinaryOp("
x,Number(0))=>
Number(0)
34.
35.//Multiplying0byxreturnszero
36.caseBinaryOp("
Number(0),x)=>
37.
38.//Dividingxby1returnstheoriginalvalue
39.caseBinaryOp("
/"
40.
41.//Dividingxbyxreturns1
42.caseBinaryOp("
x1,x2)ifx1==x2=>
Number
(1)
43.
44.//Addingxto0returnstheoriginalvalue
45.caseBinaryOp("
46.
47.//Adding0toxreturnstheoriginalvalue
48.caseBinaryOp("
49.
50.//Anythingelsecannot(yet)besimplified
51.casee=>
52.}
53.simplifyTop(simpSubs)
54.}
55.
56.defevaluate(e:
Expr):
Double=
57.{
58.simplify(e)match{
59.caseNumber(x)=>
60.caseUnaryOp("
-(evaluate(x))
61.caseBinaryOp("
x1,x2)=>
(evaluate(x1)+evaluate(x2))
62.caseBinaryOp("
(evaluate(x1)-evaluate(x2))
63.caseBinaryOp("
(evaluate(x1)*evaluate(x2))
64.caseBinaryOp("
(evaluate(x1)/evaluate(x2))
65.}
66.}
67.}
68.}
……我们使用了一个由Scala解析器组合子构建的文本解析器,用于解析简单的数学表达式……
清单3.前端
7.objectArithParserextendsJavaTokenParsers
8.{
9.defexpr:
Parser[Any]=term~rep("
~term|"
~term)
10.defterm:
Parser[Any]=factor~rep("
~factor|"
~factor)
11.deffactor:
Parser[Any]=floatingPointNumber|"
("
~expr~"
)"
12.
13.defparse(text:
String)=
14.{
15.parseAll(expr,text)
17.}
18.
19.//...
20.}
21.}
……但在进行解析时,由于解析器组合子当前被编写为返回Parser[Any]类型,所以会生成String和List集合,实际上应该让解析器返回它需要的任意类型(我们可以看到,此时是一个String和List集合)。
要让DSL成功,解析器需要返回AST中的对象,以便在解析完成时,执行引擎可以捕获该树并对它执行evaluate()。
对于该前端,我们需要更改解析器组合子实现,以便在解析期间生成不同的对象。
清理语法
对解析器做的第一个更改是修改其中一个语法。
在原来的解析器中,可以接受像“5+5+5”这样的表达式,因为语法中为表达式(expr)和术语(term)定义了rep()组合子。
但如果考虑扩展,这可能会引起一些关联性和操作符优先级问题。
以后的运算可能会要求使用括号来显式给出优先级,以避免这类问题。
因此第一个更改是将语法改为要求在所有表达式中加“()”。
回想一下,这应该是我一开始就需要做的事情;
事实上,放宽限制通常比在以后添加限制容易(如果最后不需要这些限制),但是解决运算符优先级和关联性问题比这要困难得多。
如果您不清楚运算符的优先级和关联性;
那么让我大致概述一下我们所处的环境将有多复杂。
考虑Java语言本身和它支持的各种运算符(如Java语言规范中所示)或一些关联性难题(来自Bloch和Gafter提供的JavaPuzzlers),您将发现情况不容乐观。
因此,我们需要逐步解决问题。
首先是再次测试语法:
清单4.采用括号
7.//...
8.
9.objectOldAnyParserextendsJavaTokenParsers
10.{
11.defexpr:
12.defterm:
13.deffactor:
14.
15.defparse(text:
16.{
17.parseAll(expr,text)
18.}
19.}
20.objectAnyParserextendsJavaTokenParsers
21.{
22.defexpr:
Parser[Any]=(term~"
~term)|(term~"
~term)|term
23.defterm:
Parser[Any]=(factor~"
~factor)|(factor~"
~factor)|factor
24.deffactor:
Parser[Any]="
~>
expr<
~"
|floatingPointNumber
26.defparse(text:
27.{
28.parseAll(expr,text)
29.}
30.}