Ultima attività 1 day ago

joyful.ml Raw Playground
1(*
2 1 2 + print-int
3*)
4
5let rec skip_whitespace chars =
6 let open Seq in
7 match chars () with
8 | Cons (' ', rest) | Cons ('\t', rest) | Cons ('\n', rest) ->
9 skip_whitespace rest
10 | _ ->
11 chars
12
13let collect_symbol chars =
14 let rec helper chars acc =
15 let open Seq in
16 match chars () with
17 | Cons (' ', rest) | Cons ('\t', rest) | Cons ('\n', rest) ->
18 acc |> Buffer.contents, rest
19 | Cons (ch, rest) ->
20 Buffer.add_char acc ch ;
21 helper rest acc
22 | Nil ->
23 (acc |> Buffer.contents, Seq.empty) in
24 helper chars @@ Buffer.create 16
25
26let scan_symbol chars =
27 let chars = skip_whitespace chars in
28 collect_symbol chars
29
30let tokenize str =
31 let stream = String.to_seq str in
32 let rec helper stream acc =
33 let open Seq in
34 match scan_symbol stream with
35 | "", seq ->
36 acc |> List.rev
37 | str, more ->
38 helper more (str :: acc) in
39 helper stream []
40
41type joyful_quote = [ `JoyfulQuotation of joyful_action list ]
42and joyful_data =
43 [
44 | `JoyfulSymbol of string
45 | `JoyfulInt of int
46 | `JoyfulQuotation of joyful_action list
47 ]
48and joyful_action =
49 | Push of joyful_data
50 | Call of string
51
52(* todo, implmenent stackless qoutation eval *)
53exception Unclosed_quotation
54
55let parse tokens =
56 let to_action token =
57 match int_of_string_opt token with
58 | Some n ->
59 Push (`JoyfulInt n)
60 | None when String.starts_with ~prefix:"`" token ->
61 Push (`JoyfulSymbol (String.sub token 1 (String.length token - 1)))
62 | None ->
63 Call token in
64
65 let rec parse_quotation ?(quotation=[]) tokens =
66 match tokens with
67 | [] ->
68 raise Unclosed_quotation
69 | "[" :: rest ->
70 let rest, inner_quotation = parse_quotation rest in
71 parse_quotation rest ~quotation:(Push inner_quotation :: quotation)
72 | "]" :: rest ->
73 rest, `JoyfulQuotation (List.rev quotation)
74 | token :: rest ->
75 let quotation = to_action token :: quotation in
76 parse_quotation rest ~quotation in
77
78 let rec helper tokens prog =
79 match tokens with
80 | [] ->
81 `JoyfulQuotation (List.rev prog)
82 | "[" :: rest ->
83 let rest, quotation = parse_quotation rest in
84 helper rest (Push (quotation) :: prog)
85 | token :: rest ->
86 to_action token :: prog |> helper rest in
87
88 helper tokens []
89
90class runtime_class =
91 let transfer sa sb = Stack.push (Stack.pop sa) sb in
92 object(self)
93 val data_stack : joyful_data Stack.t = Stack.create ()
94 val retain_stack : joyful_data Stack.t = Stack.create ()
95 val call_stack : joyful_action Stack.t = Stack.create ()
96
97 method stash = transfer data_stack retain_stack
98 method unstash = transfer retain_stack data_stack
99 method push = Fun.flip Stack.push data_stack
100 method pop = Stack.pop data_stack
101
102 method pop_int =
103 match self#pop with
104 | `JoyfulInt i -> i
105 | _ -> raise @@ Invalid_argument "Expected int"
106
107 method pop_quote =
108 match self#pop with
109 | `JoyfulQuotation q -> q
110 | _ -> raise @@ Invalid_argument "Expected quote"
111
112 method pop_symbol =
113 match self#pop with
114 | `JoyfulSymbol s -> s
115 | _ -> raise @@ Invalid_argument "Expected symbol"
116
117 method dup = Stack.push (Stack.top data_stack) data_stack
118
119 method swap =
120 let tos = self#pop in
121 let nos = self#pop in
122 self#push tos ;
123 self#push nos
124
125 method drop = Stack.drop data_stack
126
127 method queue_quotation (quote : joyful_quote) =
128 match quote with
129 | `JoyfulQuotation actions -> self#queue_actions actions
130
131 method queue_actions actions =
132 actions |> List.rev |> List.to_seq |> Stack.add_seq call_stack
133
134 method poll =
135 Stack.pop_opt call_stack
136
137 method data_stack = data_stack
138 method retain_stack = retain_stack
139 method call_stack = call_stack
140 end
141
142let dictionary =
143 object(self)
144 val words : (string, runtime_class -> unit) Hashtbl.t = Hashtbl.create 16
145
146 method add_word name f =
147 Hashtbl.add words name f
148
149 method remove_word name =
150 Hashtbl.remove words name
151
152 method call name runtime =
153 (Hashtbl.find words name) runtime
154 end
155
156
157let () =
158 let new_math_primitives name op =
159 dictionary#add_word name (fun runtime ->
160 let y = runtime#pop_int in
161 let x = runtime#pop_int in
162 let z = `JoyfulInt (op x y) in
163 runtime#push z
164 ) in
165
166 List.iter2 new_math_primitives
167 ["+" ; "-" ; "/" ; "*" ]
168 [ (+) ; (-) ; (/) ; ( * ) ] ;
169
170 dictionary#add_word "print-int" (fun runtime -> runtime#pop_int |> print_int) ;
171
172 dictionary#add_word "dup" (fun runtime -> runtime#dup);
173
174 dictionary#add_word "swap" (fun runtime -> runtime#swap);
175
176 dictionary#add_word "drop" (fun runtime -> runtime#drop);
177
178 dictionary#add_word "call" (fun runtime -> runtime#pop_quote |> runtime#queue_actions) ;
179
180 dictionary#add_word "compose" (fun runtime ->
181 let p = runtime#pop_quote in
182 let q = runtime#pop_quote in
183 `JoyfulQuotation (q @ p) |> runtime#push
184 ) ;
185
186 dictionary#add_word "quote" (fun runtime ->
187 `JoyfulQuotation [ Push runtime#pop ] |> runtime#push
188 ) ;
189
190 dictionary#add_word "stash" (fun runtime -> runtime#stash) ;
191
192 dictionary#add_word "unstash" (fun runtime -> runtime#unstash) ;
193
194 dictionary#add_word "def" (fun runtime ->
195 let symbol = runtime#pop_symbol in
196 let actions = runtime#pop_quote in
197 dictionary#add_word symbol (fun runtime ->
198 actions |> runtime#queue_actions
199 )
200 ) ;
201
202 dictionary#add_word "undef" (fun runtime ->
203 let symbol = runtime#pop_symbol in
204 dictionary#remove_word symbol) ;
205
206 dictionary#add_word "dip" (fun runtime ->
207 [ Call "unstash" ] |> runtime#queue_actions ;
208 runtime#pop_quote |> runtime#queue_actions ;
209 [ Call "stash" ] |> runtime#queue_actions
210 ) ;
211
212 dictionary#add_word "sqrti" (fun runtime ->
213 `JoyfulInt (runtime#pop_int |> Float.of_int |> sqrt |> Int.of_float) |> runtime#push) ;
214
215 dictionary#add_word "nl" (fun _ -> print_newline ())
216
217
218let eval ?(runtime = new runtime_class) quote =
219 runtime#queue_quotation quote ;
220 let rec helper () =
221 let action = runtime#poll in
222 match action with
223 | None ->
224 ()
225
226 | Some (Push value) ->
227 runtime#push value ;
228 helper ()
229
230 | Some (Call name) ->
231 dictionary#call name runtime;
232 helper () in
233 helper ()
234
235let program = "
236 [ quote [ call ] compose swap quote compose call ] `dip def
237 [ dup [ dip ] dip call ] `bi@ def
238 [ dup * ] `sq def
239 [ [ sq ] bi@ + sqrti ] `mag def
240 3 4 mag print-int nl
241
242"
243
244let spy_eval program =
245 let r = new runtime_class in
246 program |> tokenize |> parse |> eval ~runtime:r ;
247 ( r#data_stack |> Stack.to_seq |> List.of_seq
248 , r#retain_stack |> Stack.to_seq |> List.of_seq
249 , r#call_stack |> Stack.to_seq |> List.of_seq
250 )