summaryrefslogtreecommitdiff
path: root/test/ragel.d/rpn1.rl
blob: 4d63daa551f785b3f5144e720c6d6cf6ad20ffab (plain)
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