joyful.ml
· 6.9 KiB · OCaml
原始文件
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 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)
| x :: rest ->
match int_of_string_opt x with
| Some n ->
let quotation = Push (`JoyfulInt n) :: quotation in
parse_quotation rest ~quotation
| None ->
let quotation = Call x :: 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)
| x :: rest ->
match int_of_string_opt x with
| Some n ->
Push (`JoyfulInt n) :: prog |> helper rest
| None when String.starts_with ~prefix:"`" x ->
Push (`JoyfulSymbol (String.sub x 1 (String.length x - 1))) :: prog |> helper rest
| None ->
Call x :: 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
end
let dictionary =
object(self)
val words : (string, runtime_class -> unit) Hashtbl.t = Hashtbl.create 16
method new_primitive name f =
Hashtbl.add words name f
method call name runtime =
(Hashtbl.find words name) runtime
end
let () =
let new_math_primitives name op =
dictionary#new_primitive 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#new_primitive "print-int" (fun runtime -> runtime#pop_int |> print_int) ;
dictionary#new_primitive "dup" (fun runtime -> runtime#dup);
dictionary#new_primitive "swap" (fun runtime -> runtime#swap);
dictionary#new_primitive "drop" (fun runtime -> runtime#drop);
dictionary#new_primitive "call" (fun runtime -> runtime#pop_quote |> runtime#queue_actions) ;
dictionary#new_primitive "stash" (fun runtime -> runtime#stash) ;
dictionary#new_primitive "unstash" (fun runtime -> runtime#unstash) ;
dictionary#new_primitive "def" (fun runtime ->
let symbol = runtime#pop_symbol in
let actions = runtime#pop_quote in
dictionary#new_primitive symbol (fun runtime ->
actions |> runtime#queue_actions
)
) ;
dictionary#new_primitive "dip" (fun runtime ->
[ Call "unstash" ] |> runtime#queue_actions ;
runtime#pop_quote |> runtime#queue_actions ;
[ Call "stash" ] |> runtime#queue_actions
) ;
dictionary#new_primitive "sqrti" (fun runtime ->
`JoyfulInt (runtime#pop_int |> Float.of_int |> sqrt |> Int.of_float) |> runtime#push) ;
dictionary#new_primitive "nl" (fun _ -> print_newline ())
let eval quote =
let runtime = new runtime_class in
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 mag_program = "
[ dup [ dip ] dip call ] `bi@ def
[ dup * ] `sq def
3 4 [ sq ] bi@ + sqrti print-int nl
"
let run_mag () =
print_endline mag_program ;
mag_program |> tokenize |> parse |> eval
| 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 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 | |
| 91 | class 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 | |
| 139 | let 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 | |
| 152 | let () = |
| 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 | |
| 199 | let 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 | |
| 217 | let mag_program = " |
| 218 | [ dup [ dip ] dip call ] `bi@ def |
| 219 | [ dup * ] `sq def |
| 220 | 3 4 [ sq ] bi@ + sqrti print-int nl |
| 221 | " |
| 222 | |
| 223 | let run_mag () = |
| 224 | print_endline mag_program ; |
| 225 | mag_program |> tokenize |> parse |> eval |
| 226 |