-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwhitespace.ml
More file actions
351 lines (312 loc) · 11.3 KB
/
whitespace.ml
File metadata and controls
351 lines (312 loc) · 11.3 KB
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
module Whitespace = struct
type label = string
type io = ReadChar | ReadNumber | OutputChar | OutputNumber
type stack_manipulation =
Push of int
| Dup
| Swap
| Drop
| Copy of int
| Slide of int
type arithmetic = Add | Sub | Mul | Div | Mod
type flow_control =
Mark of label
| End
| Call of label
| Jmp of label
| Jz of label
| Jneg of label
| EndProg
type heap_access = Store | Retrive
type instruction =
IO of io
| StackManipulaton of stack_manipulation
| Arithmetic of arithmetic
| FlowControl of flow_control
| HeapAccess of heap_access
type t = instruction list
end
module Lexer = struct
type t = Space | Tab | LineFeed
type tokens = t list
let tokenize : string -> tokens =
fun source ->
let len = String.length source in
let rec aux idx =
if idx = len then []
else
match source.[idx] with
| ' ' -> Space :: aux (idx + 1)
| '\t' -> Tab :: aux (idx + 1)
| '\n' -> LineFeed :: aux (idx + 1)
| _ -> aux (idx + 1)
in
aux 0
let to_char = function Space -> ' ' | Tab -> '\t' | LineFeed -> '\n'
end
module Parser = struct
type t = Whitespace.t
let rec power x = function 0 -> 1 | n -> x * power x (n - 1)
let rec parse_number : Lexer.tokens -> int * Lexer.tokens =
fun tokens ->
let open Lexer in
match tokens with
| Space :: tokens -> parse_bits tokens
| Tab :: tokens ->
let n, tokens = parse_bits tokens in
-n, tokens
| _ -> failwith "ParsingError: [parse_number] Invalid sign"
and parse_bits : Lexer.tokens -> int * Lexer.tokens =
fun tokens ->
let open Lexer in
let rec aux tokens =
match tokens with
| LineFeed :: tokens -> [], tokens
| Space :: tokens ->
let bs, tokens = aux tokens in
0 :: bs, tokens
| Tab :: tokens ->
let bs, tokens = aux tokens in
1 :: bs, tokens
| [] -> failwith "ParseError: [parse_bits] Empty list"
in
let bs, tokens = aux tokens in
let num = bs
|> List.rev
|> List.mapi (fun i b -> b * power 2 i)
|> List.fold_left (+) 0
in
num, tokens
and parse_label : Lexer.tokens -> string * Lexer.tokens =
fun tokens ->
let open Lexer in
let rec aux tokens =
match tokens with
| LineFeed :: tokens -> Seq.empty, tokens
| c :: tokens ->
let s, tokens = aux tokens in
Seq.cons (Lexer.to_char c) s, tokens
| [] -> failwith "ParseError: [parse_label] Empty list"
in
let s, tokens = aux tokens in
String.of_seq s, tokens
and parse_io : Lexer.tokens -> Whitespace.io * Lexer.tokens =
fun tokens ->
let open Lexer in
match tokens with
| Tab :: Space :: tokens -> ReadChar , tokens
| Tab :: Tab :: tokens -> ReadNumber , tokens
| Space :: Space :: tokens -> OutputChar , tokens
| Space :: Tab :: tokens -> OutputNumber, tokens
| _ -> failwith "ParseError: [parse_io] Invalid case"
and parse_stack_manipulation
: Lexer.tokens -> Whitespace.stack_manipulation * Lexer.tokens
= fun tokens ->
let open Lexer in
match tokens with
| LineFeed :: Space :: tokens -> Dup, tokens
| LineFeed :: Tab :: tokens -> Swap, tokens
| LineFeed :: LineFeed :: tokens -> Drop, tokens
| Tab :: Space :: tokens ->
let n, tokens = parse_number tokens in
Copy n, tokens
| Tab :: LineFeed :: tokens ->
let n, tokens = parse_number tokens in
Slide n, tokens
| Space :: tokens ->
let num, tokens = parse_number tokens in
Push num, tokens
| _ -> failwith "ParseError: [parse_stack_manipulation] Invalid case"
and parse_arithmetic
: Lexer.tokens -> Whitespace.arithmetic * Lexer.tokens
= fun tokens ->
let open Lexer in
match tokens with
| Space :: Space :: tokens -> Add, tokens
| Space :: Tab :: tokens -> Sub, tokens
| Space :: LineFeed :: tokens -> Mul, tokens
| Tab :: Space :: tokens -> Div, tokens
| Tab :: Tab :: tokens -> Mod, tokens
| _ -> failwith "ParseError: [parse_arithmetic] Invalid case"
and parse_flow_control
: Lexer.tokens -> Whitespace.flow_control * Lexer.tokens
= fun tokens ->
let open Lexer in
match tokens with
| Space :: Space :: tokens ->
let label, tokens = parse_label tokens in
Mark label, tokens
| Space :: Tab :: tokens ->
let label, tokens = parse_label tokens in
Call label, tokens
| Space :: LineFeed :: tokens ->
let label, tokens = parse_label tokens in
Jmp label, tokens
| Tab :: Space :: tokens ->
let label, tokens = parse_label tokens in
Jz label, tokens
| Tab :: Tab :: tokens ->
let label, tokens = parse_label tokens in
Jneg label, tokens
| Tab :: LineFeed :: tokens -> End, tokens
| LineFeed :: LineFeed :: tokens -> EndProg, tokens
| _ -> failwith "ParseError: [parse_flow_control] Invalid case"
and parse_heap_access
: Lexer.tokens -> Whitespace.heap_access * Lexer.tokens
= fun tokens ->
let open Lexer in
match tokens with
| Space :: tokens -> Store, tokens
| Tab :: tokens -> Retrive, tokens
| _ -> failwith "ParseError: [parse_heap_access] Invalid case"
and parse : Lexer.tokens -> Whitespace.t =
fun tokens ->
let open Lexer in
match tokens with
| [] -> []
| Tab :: LineFeed :: tokens ->
let io, tokens = parse_io tokens in
IO io :: parse tokens
| Tab :: Space :: tokens ->
let ar, tokens = parse_arithmetic tokens in
Arithmetic ar :: parse tokens
| Tab :: Tab :: tokens ->
let ha, tokens = parse_heap_access tokens in
HeapAccess ha :: parse tokens
| Space :: tokens ->
let sm, tokens = parse_stack_manipulation tokens in
StackManipulaton sm :: parse tokens
| LineFeed :: tokens ->
let fc, tokens = parse_flow_control tokens in
FlowControl fc :: parse tokens
| _ -> failwith "ParseError: [parse] Invalid case"
end
module Interpreter = struct
let stack = Stack.create ()
let heap = Hashtbl.create 512
let jmp_tbl = Hashtbl.create 512
let call_stack = Stack.create ()
let eval_arith op l r =
let open Whitespace in
match op with
| Add -> l + r
| Sub -> l - r
| Mul -> l * r
| Div -> l / r
| Mod -> l mod r
let rec record_jmps : Whitespace.t -> unit =
fun instructions ->
let open Whitespace in
match instructions with
| [] -> ()
| FlowControl Mark label :: instructions ->
let () = Hashtbl.add jmp_tbl label instructions in
record_jmps instructions
| _::instructions -> record_jmps instructions
let rec eval : Whitespace.t -> unit =
fun instructions ->
let open Whitespace in
match instructions with
(* Flow Control *)
| FlowControl EndProg :: _ | [] -> ()
| FlowControl Mark label :: instructions ->
(* let () = Hashtbl.add jmp_tbl label instructions in *)
eval instructions
| FlowControl Call label :: instructions ->
let () = Stack.push instructions call_stack in
let instructions = Hashtbl.find jmp_tbl label in
eval instructions
| FlowControl Jmp label :: instructions ->
let instructions = Hashtbl.find jmp_tbl label in
eval instructions
| FlowControl Jz label :: instructions ->
if Stack.pop stack = 0
then let instructions = Hashtbl.find jmp_tbl label in
eval instructions
else eval instructions
| FlowControl Jneg label :: instructions ->
if Stack.pop stack < 0
then let instructions = Hashtbl.find jmp_tbl label in
eval instructions
else eval instructions
| FlowControl End :: instructions ->
let instructions = Stack.pop call_stack in
eval instructions
(* IO *)
| IO OutputChar :: instructions ->
let top = Stack.pop stack in
let ch = Char.unsafe_chr top in
let () = Printf.printf "%c" ch in
eval instructions
| IO OutputNumber :: instructions ->
let top = Stack.pop stack in
let () = Printf.printf "%d" top in
eval instructions
| IO ReadChar :: instructions ->
let addr = Stack.pop stack in
let char = input_char stdin in
let () = Hashtbl.replace heap addr (Char.code char) in
eval instructions
| IO ReadNumber :: instructions ->
let addr = Stack.pop stack in
let int = int_of_string (input_line stdin) in
let () = Hashtbl.replace heap addr int in
eval instructions
(* Stack Manipulation *)
| StackManipulaton Push n :: instructions ->
let () = Stack.push n stack in
eval instructions
| StackManipulaton Dup :: instructions ->
let top = Stack.top stack in
let () = Stack.push top stack in
eval instructions
| StackManipulaton Swap :: instructions ->
let fst = Stack.pop stack in
let snd = Stack.pop stack in
let () = Stack.push fst stack in
let () = Stack.push snd stack in
eval instructions
| StackManipulaton Drop :: instructions ->
let _ = Stack.pop stack in
eval instructions
(* | StackManipulaton Copy n :: instructions -> *)
(* | StackManipulaton Slide n :: instructions -> *)
(* Arithmetic *)
| Arithmetic op :: instructions ->
let r = Stack.pop stack in
let l = Stack.pop stack in
let () = Stack.push (eval_arith op l r) stack in
eval instructions
(* Heap Access *)
| HeapAccess Store :: instructions ->
let value = Stack.pop stack in
let addr = Stack.pop stack in
let () = Hashtbl.replace heap addr value in
eval instructions
| HeapAccess Retrive :: instructions ->
let addr = Stack.pop stack in
let value = Hashtbl.find heap addr in
let () = Stack.push value stack in
eval instructions
| _ -> failwith "EvalError: [eval] Not implemented"
end
let hello_world = " \t \t \n\t\n \t\t \t \t\n\t\n \t\t \t\t \n\t\n \t\t \t\t \n\t\n \t\t \t\t\t\t\n\t\n \t \t\t \n\t\n \t \n\t\n \t\t\t \t\t\t\n\t\n \t\t \t\t\t\t\n\t\n \t\t\t \t \n\t\n \t\t \t\t \n\t\n \t\t \t \n\t\n \t \t\n\t\n \t \t\n\t\n \t \t \n\t\n \n\n\n"
let count = " \t\n\n \t \t\t\n \n \t\n \t \t \t \n\t\n \t\n\t \n \t \t\t\n\t \t\n\t \t \t \t\n\n \n \t \t\t\n\n \t \t \t\n \n\n\n\n\n"
let read_file file =
let inc = open_in file in
let rec read_lines s =
try
let line = input_line inc in
read_lines (s ^line ^ "\n")
with _ -> close_in inc ; s
in
read_lines ""
let ws prg = prg
|> Lexer.tokenize
|> Parser.parse
|> fun ws -> Interpreter.record_jmps ws ; ws
|> Interpreter.eval
let () = ws hello_world
let () = ws count
let () = ws (read_file "fact.ws")
let () = ws (read_file "fibonacci.ws")