1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
(*
* @LANG: ocaml
*)
let id x = x
let fail fmt = Printf.ksprintf failwith fmt
let pr fmt = Printf.ksprintf print_endline fmt
let failed fmt = Printf.ksprintf (fun s -> prerr_endline s; exit 1) fmt
let test' show f x y = if f x <> y then failed "FAILED: test %S" (show x)
let case = ref 0
let test f x y = incr case; if f x <> y then failed "FAILED: case %d" !case
let error f x = match try Some (f x) with _ -> None with Some _ -> failed "FAILED: fail %S" x | None -> ()
(*
// -*-go-*-
//
// Reverse Polish Notation Calculator
// Copyright (c) 2010 J.A. Roberts Tunney
// MIT License
//
// To compile:
//
// ragel -Z -G2 -o rpn.go rpn.rl
// 6g -o rpn.6 rpn.go
// 6l -o rpn rpn.6
// ./rpn
//
// To show a diagram of your state machine:
//
// ragel -V -G2 -p -o rpn.dot rpn.rl
// dot -Tpng -o rpn.png rpn.dot
// chrome rpn.png
//
*)
%% machine rpn;
%% write data;
let fail fmt = Printf.ksprintf failwith fmt
let rpn data =
let (cs, p, pe) = (ref 0, ref 0, ref (String.length data)) in
let mark = ref 0 in
let st = Stack.create () in
%%{
action mark { mark := !p }
action push { Stack.push (int_of_string (String.sub data !mark (!p - !mark))) st }
action add { let y = Stack.pop st in let x = Stack.pop st in Stack.push (x + y) st }
action sub { let y = Stack.pop st in let x = Stack.pop st in Stack.push (x - y) st }
action mul { let y = Stack.pop st in let x = Stack.pop st in Stack.push (x * y) st }
action div { let y = Stack.pop st in let x = Stack.pop st in Stack.push (x / y) st }
action abs { Stack.push (abs (Stack.pop st)) st }
action abba { Stack.push 666 st }
stuff = digit+ >mark %push
| '+' @add
| '-' @sub
| '*' @mul
| '/' @div
| 'abs' %abs
| 'add' %add
| 'abba' %abba
;
main := ( space | stuff space )* ;
write init;
write exec;
}%%
if !cs < rpn_first_final then
begin
if !p = !pe then
fail "unexpected eof"
else
fail "error at position %d" !p
end;
if Stack.is_empty st then
fail "rpn stack empty on result"
else
Stack.pop st
(* ////////////////////////////////////////////////////////////////////// *)
let rpnTests = [
("666\n", 666);
("666 111\n", 111);
("4 3 add\n", 7);
("4 3 +\n", 7);
("4 3 -\n", 1);
("4 3 *\n", 12);
("6 2 /\n", 3);
("0 3 -\n", -3);
("0 3 - abs\n", 3);
(" 2 2 + 3 - \n", 1);
("10 7 3 2 * - +\n", 11);
("abba abba add\n", 1332);
]
let rpnFailTests = [
("\n")
]
let () =
List.iter (fun (s,x) -> test rpn s x) rpnTests;
List.iter (fun s -> error rpn s) rpnFailTests
|