随笔-341  评论-2670  文章-0  trackbacks-0
    为了测试Kernel FP的健壮性以及进行一些bug的排除,一个四则运算式子的分析程序理所当然地就被实现了。代码如下:

    这里使用的方法是,现将字符串切开,然后变成一段一段的,最后直接遍历求值。譬如说输入(1+2)*(3+4)要得到21。程序可以识别出错误,不过main函数忽略一切错误(因为懒得写那么多if)。

    P.S. 没有语法糖写起来还真是惨啊……等测试得差不多的时候就把高级的语法糖添加进去。

    首先要为token建模。四则运算式子的token类型有括号、运算符和数字:
1 data token
2   = t_leftbrace
3   | t_rightbrace
4   | t_add
5   | t_sub
6   | t_mul
7   | t_div
8   | t_num float

    然后是词法分析的函数。token_split输入一个字符串,输出一个token数组和剩余的字符串。剩余的字符串如果存在的话,第一个字符是无法被识别的字符,譬如字母等:
 1 data token_stream = token_stream (list token) string
 2 
 3 def token_getnum input =
 4   let
 5     def _getnum output input =
 6       select input of
 7         case list x tail : if (and (cegt x '0') (celt x '9')) (_getnum (list x output) tail) (pair output input)
 8         case empty : pair output input
 9       end
10   in select _getnum "" input of
11     case pair output input : pair (reverse output) input
12   end
13 
14 def token_atof input = select atof input of
15   case success number : number
16 end
17 
18 def token_split input =
19   let
20     def _split stream = select stream of
21       case token_stream tokens remain : select remain of
22         case empty : stream
23         case list '(' tail : _split (token_stream (list t_leftbrace tokens) tail)
24         case list ')' tail : _split (token_stream (list t_rightbrace tokens) tail)
25         case list '+' tail : _split (token_stream (list t_add tokens) tail)
26         case list '-' tail : _split (token_stream (list t_sub tokens) tail)
27         case list '*' tail : _split (token_stream (list t_mul tokens) tail)
28         case list '/' tail : _split (token_stream (list t_div tokens) tail)
29         else : select token_getnum remain of
30           case pair num tail : select num of
31             case empty : stream
32             case list x xs : _split (token_stream (list (t_num (token_atof num)) tokens) tail)
33           end
34         end
35       end
36     end
37   in select _split (token_stream empty input) of
38     case token_stream tokens remain : token_stream (reverse tokens) remain
39   end

    接下来是语法分析。语法分析直接使用我们已经熟练到无法再熟练,连方法都可以倒着背出来的递归下降法进行分析:
 1 def token_toint token = select token of
 2   case t_leftbrace : 0
 3   case t_rightbrace : 1
 4   case t_add : 2
 5   case t_sub : 3
 6   case t_mul : 4
 7   case t_div : 5
 8   case t_num x : 6
 9 end
10 
11 def token_startwith token tokens = select tokens of
12   case empty : false
13   case list first remains : iequ (token_toint token) (token_toint first)
14 end
15 
16 data expression
17   = e_add expression expression
18   | e_sub expression expression
19   | e_mul expression expression
20   | e_div expression expression
21   | e_num float
22   | e_error string
23 
24 def exp_getfactor tokens = select head tokens of
25   case t_num x : pair (success x) (tail tokens)
26   case t_leftbrace : select exp_getexp (tail tokens) of
27     case pair first remains : select first of
28       case fail message : pair first remains
29       case success x :
30         if (token_startwith t_rightbrace remains)
31           (pair first (tail remains))
32           (pair (fail "此处需要右括号") remains)
33     end
34   end
35   else : pair (fail "此处需要表达式") tokens
36 end
37 
38 def exp_getterm tokens=
39   let
40     def _getterm current tokens ismul = select exp_getfactor tokens of
41       case pair result remains : select result of
42         case fail message : pair result remains
43         case success x :
44           let
45             def new_current=if ismul (fmul current x) (fdiv current x)
46           in if (isempty remains)
47             (pair (success new_current) remains)
48             select head remains of
49               case t_mul : _getterm new_current (tail remains) true
50               case t_div : _getterm new_current (tail remains) false
51               else : pair (success new_current) remains
52             end
53       end
54     end
55   in _getterm 1.0 tokens true
56 
57 def exp_getexp tokens=
58   let
59     def _getexp current tokens isadd = select exp_getterm tokens of
60       case pair result remains : select result of
61         case fail message : pair result remains
62         case success x :
63           let
64             def new_current=if isadd (fadd current x) (fsub current x)
65           in if (isempty remains)
66             (pair (success new_current) remains)
67             select head remains of
68               case t_add : _getexp new_current (tail remains) true
69               case t_sub : _getexp new_current (tail remains) false
70               else : pair (success new_current) remains
71             end
72       end
73     end
74   in _getexp 0.0 tokens true

    有了这些函数之后,我们写几个main来测试它们:
1 def main97 = token_getnum "123vczh"
2 def main98 = token_getnum "vczh123"
3 def main99 = token_split "(1+2)*(3+4)"
4 def main100 = select token_split "(1+2)*(3+4)" of
5   case token_stream tokens remains : exp_getexp tokens
6 end
7 def main101 = select token_split "(1+2)*(3+4)" of
8   case token_stream tokens remains : pairfirst (exp_getexp tokens)
9 end

    下面就是结果啦!
1 main97返回值:(sysutils.pair "123" "vczh")
2 main98返回值:(sysutils.pair "" "vczh123")
3 main99返回值:(startup.token_stream [startup.t_leftbrace , (startup.t_num 1.0) , startup.t_add , (startup.t_num 2.0) , startup.t_rightbrace , startup.t_mul , startup.t_leftbrace , (startup.t_num 3.0) , startup.t_add , (startup.t_num 4.0) ,startup.t_rightbrace] "")
4 main100返回值:(sysutils.pair (system.success 21.0"")
5 main101返回值:(system.success 21.0)

    返回system.success 21.0!娃哈哈……
posted on 2008-12-13 07:13 陈梓瀚(vczh) 阅读(3318) 评论(2)  编辑 收藏 引用 所属分类: 脚本技术

评论:
# re: Kernel FP 的四则运算式子分析程序 2008-12-14 17:55 | ckyap
没有语法糖写起来还真是惨啊。。。  回复  更多评论
  
# re: Kernel FP 的四则运算式子分析程序 2008-12-14 18:53 | 陈梓瀚(vczh)
这就是实验型语言与工程型语言的最大区别……Kernel FP的初衷是为了让我能够研究一下我自己对实现一门pure functional programming的想法究竟是能用的还是不能用的。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理