joyful.ml
· 7.5 KiB · OCaml
Eredeti
Playground
(*
1 2 + print-int
*)
let rec skip_whitespace chars =
let open Seq in
match chars () with
| Cons (' ', rest) | Cons ('\t', rest) | Cons ('\n', rest) ->
skip_whitespace rest
| _ ->
chars
let collect_symbol chars =
let rec helper chars acc =
let open Seq in
match chars () with
| Cons (' ', rest) | Cons ('\t', rest) | Cons ('\n', rest) ->
acc |> Buffer.contents, rest
| Cons (ch, rest) ->
Buffer.add_char acc ch ;
helper rest acc
| Nil ->
(acc |> Buffer.contents, Seq.empty) in
helper chars @@ Buffer.create 16
let scan_symbol chars =
let chars = skip_whitespace chars in
collect_symbol chars
let tokenize str =
let stream = String.to_seq str in
let rec helper stream acc =
let open Seq in
match scan_symbol stream with
| "", seq ->
acc |> List.rev
| str, more ->
helper more (str :: acc) in
helper stream []
type joyful_quote = [ `JoyfulQuotation of joyful_action list ]
and joyful_data =
[
| `JoyfulSymbol of string
| `JoyfulInt of int
| `JoyfulQuotation of joyful_action list
]
and joyful_action =
| Push of joyful_data
| Call of string
(* todo, implmenent stackless qoutation eval *)
exception Unclosed_quotation
let parse tokens =
let to_action token =
match int_of_string_opt token with
| Some n ->
Push (`JoyfulInt n)
| None when String.starts_with ~prefix:"`" token ->
Push (`JoyfulSymbol (String.sub token 1 (String.length token - 1)))
| None ->
Call token in
let rec parse_quotation ?(quotation=[]) tokens =
match tokens with
| [] ->
raise Unclosed_quotation
| "[" :: rest ->
let rest, inner_quotation = parse_quotation rest in
parse_quotation rest ~quotation:(Push inner_quotation :: quotation)
| "]" :: rest ->
rest, `JoyfulQuotation (List.rev quotation)
| token :: rest ->
let quotation = to_action token :: quotation in
parse_quotation rest ~quotation in
let rec helper tokens prog =
match tokens with
| [] ->
`JoyfulQuotation (List.rev prog)
| "[" :: rest ->
let rest, quotation = parse_quotation rest in
helper rest (Push (quotation) :: prog)
| token :: rest ->
to_action token :: prog |> helper rest in
helper tokens []
class runtime_class =
let transfer sa sb = Stack.push (Stack.pop sa) sb in
object(self)
val data_stack : joyful_data Stack.t = Stack.create ()
val retain_stack : joyful_data Stack.t = Stack.create ()
val call_stack : joyful_action Stack.t = Stack.create ()
method stash = transfer data_stack retain_stack
method unstash = transfer retain_stack data_stack
method push = Fun.flip Stack.push data_stack
method pop = Stack.pop data_stack
method pop_int =
match self#pop with
| `JoyfulInt i -> i
| _ -> raise @@ Invalid_argument "Expected int"
method pop_quote =
match self#pop with
| `JoyfulQuotation q -> q
| _ -> raise @@ Invalid_argument "Expected quote"
method pop_symbol =
match self#pop with
| `JoyfulSymbol s -> s
| _ -> raise @@ Invalid_argument "Expected symbol"
method dup = Stack.push (Stack.top data_stack) data_stack
method swap =
let tos = self#pop in
let nos = self#pop in
self#push tos ;
self#push nos
method drop = Stack.drop data_stack
method queue_quotation (quote : joyful_quote) =
match quote with
| `JoyfulQuotation actions -> self#queue_actions actions
method queue_actions actions =
actions |> List.rev |> List.to_seq |> Stack.add_seq call_stack
method poll =
Stack.pop_opt call_stack
method data_stack = data_stack
method retain_stack = retain_stack
method call_stack = call_stack
end
let dictionary =
object(self)
val words : (string, runtime_class -> unit) Hashtbl.t = Hashtbl.create 16
method add_word name f =
Hashtbl.add words name f
method remove_word name =
Hashtbl.remove words name
method call name runtime =
(Hashtbl.find words name) runtime
end
let () =
let new_math_primitives name op =
dictionary#add_word name (fun runtime ->
let y = runtime#pop_int in
let x = runtime#pop_int in
let z = `JoyfulInt (op x y) in
runtime#push z
) in
List.iter2 new_math_primitives
["+" ; "-" ; "/" ; "*" ]
[ (+) ; (-) ; (/) ; ( * ) ] ;
dictionary#add_word "print-int" (fun runtime -> runtime#pop_int |> print_int) ;
dictionary#add_word "dup" (fun runtime -> runtime#dup);
dictionary#add_word "swap" (fun runtime -> runtime#swap);
dictionary#add_word "drop" (fun runtime -> runtime#drop);
dictionary#add_word "call" (fun runtime -> runtime#pop_quote |> runtime#queue_actions) ;
dictionary#add_word "compose" (fun runtime ->
let p = runtime#pop_quote in
let q = runtime#pop_quote in
`JoyfulQuotation (q @ p) |> runtime#push
) ;
dictionary#add_word "quote" (fun runtime ->
`JoyfulQuotation [ Push runtime#pop ] |> runtime#push
) ;
dictionary#add_word "stash" (fun runtime -> runtime#stash) ;
dictionary#add_word "unstash" (fun runtime -> runtime#unstash) ;
dictionary#add_word "def" (fun runtime ->
let symbol = runtime#pop_symbol in
let actions = runtime#pop_quote in
dictionary#add_word symbol (fun runtime ->
actions |> runtime#queue_actions
)
) ;
dictionary#add_word "undef" (fun runtime ->
let symbol = runtime#pop_symbol in
dictionary#remove_word symbol) ;
dictionary#add_word "dip" (fun runtime ->
[ Call "unstash" ] |> runtime#queue_actions ;
runtime#pop_quote |> runtime#queue_actions ;
[ Call "stash" ] |> runtime#queue_actions
) ;
dictionary#add_word "sqrti" (fun runtime ->
`JoyfulInt (runtime#pop_int |> Float.of_int |> sqrt |> Int.of_float) |> runtime#push) ;
dictionary#add_word "nl" (fun _ -> print_newline ())
let eval ?(runtime = new runtime_class) quote =
runtime#queue_quotation quote ;
let rec helper () =
let action = runtime#poll in
match action with
| None ->
()
| Some (Push value) ->
runtime#push value ;
helper ()
| Some (Call name) ->
dictionary#call name runtime;
helper () in
helper ()
let program = "
[ quote [ call ] compose swap quote compose call ] `dip def
[ dup [ dip ] dip call ] `bi@ def
[ dup * ] `sq def
[ [ sq ] bi@ + sqrti ] `mag def
3 4 mag print-int nl
"
let spy_eval program =
let r = new runtime_class in
program |> tokenize |> parse |> eval ~runtime:r ;
( r#data_stack |> Stack.to_seq |> List.of_seq
, r#retain_stack |> Stack.to_seq |> List.of_seq
, r#call_stack |> Stack.to_seq |> List.of_seq
)
| 1 | (* |
| 2 | 1 2 + print-int |
| 3 | *) |
| 4 | |
| 5 | let 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 | |
| 13 | let 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 | |
| 26 | let scan_symbol chars = |
| 27 | let chars = skip_whitespace chars in |
| 28 | collect_symbol chars |
| 29 | |
| 30 | let 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 | |
| 41 | type joyful_quote = [ `JoyfulQuotation of joyful_action list ] |
| 42 | and joyful_data = |
| 43 | [ |
| 44 | | `JoyfulSymbol of string |
| 45 | | `JoyfulInt of int |
| 46 | | `JoyfulQuotation of joyful_action list |
| 47 | ] |
| 48 | and joyful_action = |
| 49 | | Push of joyful_data |
| 50 | | Call of string |
| 51 | |
| 52 | (* todo, implmenent stackless qoutation eval *) |
| 53 | exception Unclosed_quotation |
| 54 | |
| 55 | let 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 | |
| 90 | class 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 | |
| 142 | let 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 | |
| 157 | let () = |
| 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 | |
| 218 | let 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 | |
| 235 | let 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 | |
| 244 | let 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 | ) |