最終更新 1 day ago

修正履歴 9cf47f82640f7fe7268900178538aff482af3bbf

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 rec parse_quotation ?(quotation=[]) tokens =
57 match tokens with
58 | [] ->
59 raise Unclosed_quotation
60 | "[" :: rest ->
61 let rest, inner_quotation = parse_quotation rest in
62 parse_quotation rest ~quotation:(Push inner_quotation :: quotation)
63 | "]" :: rest ->
64 rest, `JoyfulQuotation (List.rev quotation)
65 | x :: rest ->
66 match int_of_string_opt x with
67 | Some n ->
68 let quotation = Push (`JoyfulInt n) :: quotation in
69 parse_quotation rest ~quotation
70 | None ->
71 let quotation = Call x :: quotation in
72 parse_quotation rest ~quotation in
73
74 let rec helper tokens prog =
75 match tokens with
76 | [] ->
77 `JoyfulQuotation (List.rev prog)
78 | "[" :: rest ->
79 let rest, quotation = parse_quotation rest in
80 helper rest (Push (quotation) :: prog)
81 | x :: rest ->
82 match int_of_string_opt x with
83 | Some n ->
84 Push (`JoyfulInt n) :: prog |> helper rest
85 | None when String.starts_with ~prefix:"`" x ->
86 Push (`JoyfulSymbol (String.sub x 1 (String.length x - 1))) :: prog |> helper rest
87 | None ->
88 Call x :: prog |> helper rest in
89 helper tokens []
90
91class runtime_class =
92 let transfer sa sb = Stack.push (Stack.pop sa) sb in
93 object(self)
94 val data_stack : joyful_data Stack.t = Stack.create ()
95 val retain_stack : joyful_data Stack.t = Stack.create ()
96 val call_stack : joyful_action Stack.t = Stack.create ()
97
98 method stash = transfer data_stack retain_stack
99 method unstash = transfer retain_stack data_stack
100 method push = Fun.flip Stack.push data_stack
101 method pop = Stack.pop data_stack
102
103 method pop_int =
104 match self#pop with
105 | `JoyfulInt i -> i
106 | _ -> raise @@ Invalid_argument "Expected int"
107
108 method pop_quote =
109 match self#pop with
110 | `JoyfulQuotation q -> q
111 | _ -> raise @@ Invalid_argument "Expected quote"
112
113 method pop_symbol =
114 match self#pop with
115 | `JoyfulSymbol s -> s
116 | _ -> raise @@ Invalid_argument "Expected symbol"
117
118 method dup = Stack.push (Stack.top data_stack) data_stack
119
120 method swap =
121 let tos = self#pop in
122 let nos = self#pop in
123 self#push tos ;
124 self#push nos
125
126 method drop = Stack.drop data_stack
127
128 method queue_quotation (quote : joyful_quote) =
129 match quote with
130 | `JoyfulQuotation actions -> self#queue_actions actions
131
132 method queue_actions actions =
133 actions |> List.rev |> List.to_seq |> Stack.add_seq call_stack
134
135 method poll =
136 Stack.pop_opt call_stack
137 end
138
139let dictionary =
140 object(self)
141 val words : (string, runtime_class -> unit) Hashtbl.t = Hashtbl.create 16
142
143 method new_primitive name f =
144 Hashtbl.add words name f
145
146
147 method call name runtime =
148 (Hashtbl.find words name) runtime
149 end
150
151
152let () =
153 let new_math_primitives name op =
154 dictionary#new_primitive name (fun runtime ->
155 let y = runtime#pop_int in
156 let x = runtime#pop_int in
157 let z = `JoyfulInt (op x y) in
158 runtime#push z
159 ) in
160
161 List.iter2 new_math_primitives
162 ["+" ; "-" ; "/" ; "*" ]
163 [ (+) ; (-) ; (/) ; ( * ) ] ;
164
165 dictionary#new_primitive "print-int" (fun runtime -> runtime#pop_int |> print_int) ;
166
167 dictionary#new_primitive "dup" (fun runtime -> runtime#dup);
168
169 dictionary#new_primitive "swap" (fun runtime -> runtime#swap);
170
171 dictionary#new_primitive "drop" (fun runtime -> runtime#drop);
172
173 dictionary#new_primitive "call" (fun runtime -> runtime#pop_quote |> runtime#queue_actions) ;
174
175 dictionary#new_primitive "stash" (fun runtime -> runtime#stash) ;
176
177 dictionary#new_primitive "unstash" (fun runtime -> runtime#unstash) ;
178
179 dictionary#new_primitive "def" (fun runtime ->
180 let symbol = runtime#pop_symbol in
181 let actions = runtime#pop_quote in
182 dictionary#new_primitive symbol (fun runtime ->
183 actions |> runtime#queue_actions
184 )
185 ) ;
186
187
188 dictionary#new_primitive "dip" (fun runtime ->
189 [ Call "unstash" ] |> runtime#queue_actions ;
190 runtime#pop_quote |> runtime#queue_actions ;
191 [ Call "stash" ] |> runtime#queue_actions
192 ) ;
193
194 dictionary#new_primitive "sqrti" (fun runtime ->
195 `JoyfulInt (runtime#pop_int |> Float.of_int |> sqrt |> Int.of_float) |> runtime#push) ;
196
197 dictionary#new_primitive "nl" (fun _ -> print_newline ())
198
199let eval quote =
200 let runtime = new runtime_class in
201 runtime#queue_quotation quote ;
202 let rec helper () =
203 let action = runtime#poll in
204 match action with
205 | None ->
206 ()
207
208 | Some (Push value) ->
209 runtime#push value ;
210 helper ()
211
212 | Some (Call name) ->
213 dictionary#call name runtime;
214 helper () in
215 helper ()
216
217let mag_program = "
218[ dup [ dip ] dip call ] `bi@ def
219[ dup * ] `sq def
2203 4 [ sq ] bi@ + sqrti print-int nl
221"
222
223let run_mag () =
224 print_endline mag_program ;
225 mag_program |> tokenize |> parse |> eval
226