8 CREATE OR REPLACE PACKAGE mal
IS
10 FUNCTION MAIN(args
varchar DEFAULT '()') RETURN integer;
15 CREATE OR REPLACE PACKAGE BODY mal
IS
17 FUNCTION MAIN(args
varchar DEFAULT '()') RETURN integer IS
18 M types.mal_table
; -- general mal value memory pool
19 H types.map_entry_table
; -- hashmap memory pool
20 E env_pkg.env_entry_table
; -- mal env memory pool
30 FUNCTION READ(line
varchar) RETURN integer IS
32 RETURN reader.
read_str(M
, H
, line
);
37 -- forward declarations
38 FUNCTION EVAL(orig_ast
integer, orig_env
integer) RETURN integer;
39 FUNCTION do_builtin(fn
integer, args mal_vals
) RETURN integer;
41 FUNCTION is_pair(ast
integer) RETURN BOOLEAN IS
43 RETURN M(ast
).type_id
IN (8,9) AND types.
count(M
, ast
) > 0;
46 FUNCTION quasiquote(ast
integer) RETURN integer IS
50 IF NOT is_pair(ast
) THEN
51 RETURN types.
list(M
, types.
symbol(M
, 'quote'), ast
);
53 a0
:= types.
nth(M
, ast
, 0);
54 IF M(a0
).type_id
= 7 AND
55 TREAT(m(a0
) AS mal_str_T
).val_str
= 'unquote' THEN
56 RETURN types.
nth(M
, ast
, 1);
57 ELSIF
is_pair(a0
) THEN
58 a00
:= types.
nth(M
, a0
, 0);
59 IF M(a00
).type_id
= 7 AND
60 TREAT(M(a00
) AS mal_str_T
).val_str
= 'splice-unquote' THEN
61 RETURN types.
list(M
, types.
symbol(M
, 'concat'),
63 quasiquote(types.
slice(M
, ast
, 1)));
66 RETURN types.
list(M
, types.
symbol(M
, 'cons'),
68 quasiquote(types.
slice(M
, ast
, 1)));
73 FUNCTION is_macro_call(ast
integer, env
integer) RETURN BOOLEAN IS
77 IF M(ast
).type_id
= 8 THEN
78 a0
:= types.
nth(M
, ast
, 0);
79 IF M(a0
).type_id
= 7 AND
80 env_pkg.
env_find(M
, E
, env
, a0
) IS NOT NULL THEN
81 mac
:= env_pkg.
env_get(M
, E
, env
, a0
);
82 IF M(mac
).type_id
= 12 THEN
83 RETURN TREAT(M(mac
) AS mal_func_T
).is_macro
> 0;
90 FUNCTION macroexpand(orig_ast
integer, env
integer) RETURN integer IS
98 WHILE
is_macro_call(ast
, env
) LOOP
99 mac
:= env_pkg.
env_get(M
, E
, env
, types.
nth(M
, ast
, 0));
100 fargs
:= TREAT(M(types.
slice(M
, ast
, 1)) as mal_seq_T
).val_seq
;
101 if M(mac
).type_id
= 12 THEN
102 malfn
:= TREAT(M(mac
) AS mal_func_T
);
103 fn_env
:= env_pkg.
env_new(M
, E
, malfn.env
,
106 ast
:= EVAL(malfn.ast
, fn_env
);
108 ast
:= do_builtin(mac
, fargs
);
114 FUNCTION eval_ast(ast
integer, env
integer) RETURN integer IS
123 IF M(ast
).type_id
= 7 THEN
124 RETURN env_pkg.
env_get(M
, E
, env
, ast
);
125 ELSIF
M(ast
).type_id
IN (8,9) THEN
126 old_seq
:= TREAT(M(ast
) AS mal_seq_T
).val_seq
;
127 new_seq
:= mal_vals();
128 new_seq.
EXTEND(old_seq.
COUNT);
129 FOR i
IN 1..old_seq.
COUNT LOOP
130 new_seq(i
) := EVAL(old_seq(i
), env
);
132 RETURN types.
seq(M
, M(ast
).type_id
, new_seq
);
133 ELSIF
M(ast
).type_id
IN (10) THEN
134 new_hm
:= types.
hash_map(M
, H
, mal_vals());
135 old_midx
:= TREAT(M(ast
) AS mal_map_T
).map_idx
;
136 new_midx
:= TREAT(M(new_hm
) AS mal_map_T
).map_idx
;
138 k
:= H(old_midx
).
FIRST();
139 WHILE k
IS NOT NULL LOOP
140 H(new_midx
)(k
) := EVAL(H(old_midx
)(k
), env
);
141 k
:= H(old_midx
).
NEXT(k
);
149 FUNCTION EVAL(orig_ast
integer, orig_env
integer) RETURN integer IS
150 ast
integer := orig_ast
;
151 env
integer := orig_env
;
165 -- io.writeline('EVAL: ' || printer.pr_str(M, H, ast));
166 IF M(ast
).type_id
<> 8 THEN
167 RETURN eval_ast(ast
, env
);
171 ast
:= macroexpand(ast
, env
);
172 IF M(ast
).type_id
<> 8 THEN
173 RETURN eval_ast(ast
, env
);
175 IF types.
count(M
, ast
) = 0 THEN
176 RETURN ast
; -- empty list just returned
180 a0
:= types.
first(M
, ast
);
181 if M(a0
).type_id
= 7 THEN -- symbol
182 a0sym
:= TREAT(M(a0
) AS mal_str_T
).val_str
;
184 a0sym
:= '__<*fn*>__';
188 WHEN a0sym
= 'def!' THEN
189 RETURN env_pkg.
env_set(M
, E
, env
,
190 types.
nth(M
, ast
, 1), EVAL(types.
nth(M
, ast
, 2), env
));
191 WHEN a0sym
= 'let*' THEN
192 let_env
:= env_pkg.
env_new(M
, E
, env
);
193 seq
:= TREAT(M(types.
nth(M
, ast
, 1)) AS mal_seq_T
).val_seq
;
195 WHILE i
<= seq.
COUNT LOOP
196 x
:= env_pkg.
env_set(M
, E
, let_env
,
197 seq(i
), EVAL(seq(i
+1), let_env
));
201 ast
:= types.
nth(M
, ast
, 2); -- TCO
202 WHEN a0sym
= 'quote' THEN
203 RETURN types.
nth(M
, ast
, 1);
204 WHEN a0sym
= 'quasiquote' THEN
205 RETURN EVAL(quasiquote(types.
nth(M
, ast
, 1)), env
);
206 WHEN a0sym
= 'defmacro!' THEN
207 x
:= EVAL(types.
nth(M
, ast
, 2), env
);
208 malfn
:= TREAT(M(x
) as mal_func_T
);
211 RETURN env_pkg.
env_set(M
, E
, env
,
212 types.
nth(M
, ast
, 1), x
);
213 WHEN a0sym
= 'macroexpand' THEN
214 RETURN macroexpand(types.
nth(M
, ast
, 1), env
);
215 WHEN a0sym
= 'try*' THEN
220 a20sym
varchar2(100);
222 RETURN EVAL(types.
nth(M
, ast
, 1), env
);
224 EXCEPTION WHEN OTHERS THEN
225 IF types.
count(M
, ast
) > 2 THEN
226 a2
:= types.
nth(M
, ast
, 2);
227 IF M(a2
).type_id
= 8 THEN
228 a20
:= types.
nth(M
, a2
, 0);
229 IF M(a20
).type_id
= 7 THEN
230 a20sym
:= TREAT(M(a20
) AS mal_str_T
).val_str
;
234 IF a20sym
= 'catch*' THEN
235 IF SQLCODE <> -20000 THEN
236 IF SQLCODE < -20000 AND SQLCODE > -20100 THEN
237 exc
:= types.
string(M
,
238 REGEXP_REPLACE(SQLERRM
,
239 '^ORA-200[0-9][0-9]: '));
241 exc
:= types.
string(M
, SQLERRM
);
247 try_env
:= env_pkg.
env_new(M
, E
, env
,
248 types.
list(M
, types.
nth(M
, a2
, 1)),
250 RETURN EVAL(types.
nth(M
, a2
, 2), try_env
);
252 RAISE
; -- not handled, re-raise the exception
254 WHEN a0sym
= 'do' THEN
255 x
:= types.
slice(M
, ast
, 1, types.
count(M
, ast
)-2);
256 x
:= eval_ast(x
, env
);
257 ast
:= types.
nth(M
, ast
, types.
count(M
, ast
)-1); -- TCO
258 WHEN a0sym
= 'if' THEN
259 cond
:= EVAL(types.
nth(M
, ast
, 1), env
);
260 IF cond
= 1 OR cond
= 2 THEN -- nil or false
261 IF types.
count(M
, ast
) > 3 THEN
262 ast
:= types.
nth(M
, ast
, 3); -- TCO
267 ast
:= types.
nth(M
, ast
, 2); -- TCO
269 WHEN a0sym
= 'fn*' THEN
270 RETURN types.
malfunc(M
, types.
nth(M
, ast
, 2),
271 types.
nth(M
, ast
, 1),
274 el
:= eval_ast(ast
, env
);
275 f
:= types.
first(M
, el
);
276 args
:= TREAT(M(types.
slice(M
, el
, 1)) AS mal_seq_T
).val_seq
;
277 IF M(f
).type_id
= 12 THEN
278 malfn
:= TREAT(M(f
) AS mal_func_T
);
279 env
:= env_pkg.
env_new(M
, E
, malfn.env
,
281 ast
:= malfn.ast
; -- TCO
283 RETURN do_builtin(f
, args
);
291 -- hack to get around lack of function references
292 -- functions that require special access to repl_env or EVAL
293 -- are implemented directly here, otherwise, core.do_core_fn
295 FUNCTION do_builtin(fn
integer, args mal_vals
) RETURN integer IS
305 fname
:= TREAT(M(fn
) AS mal_str_T
).val_str
;
307 WHEN fname
= 'do_eval' THEN
308 RETURN EVAL(args(1), repl_env
);
309 WHEN fname
= 'swap!' THEN
310 val
:= TREAT(M(args(1)) AS mal_atom_T
).val
;
312 -- slice one extra at the beginning that will be changed
313 -- to the value of the atom
314 fargs
:= TREAT(M(types.
slice(M
, args
, 1)) AS mal_seq_T
).val_seq
;
316 IF M(f
).type_id
= 12 THEN
317 malfn
:= TREAT(M(f
) AS mal_func_T
);
318 fn_env
:= env_pkg.
env_new(M
, E
, malfn.env
,
319 malfn.params
, fargs
);
320 val
:= EVAL(malfn.ast
, fn_env
);
322 val
:= do_builtin(f
, fargs
);
324 RETURN types.
atom_reset(M
, args(1), val
);
325 WHEN fname
= 'apply' THEN
328 tseq
:= TREAT(M(args(args.
COUNT())) AS mal_seq_T
).val_seq
;
329 fargs.
EXTEND(args.
COUNT()-2 + tseq.
COUNT());
330 FOR i
IN 1..args.
COUNT()-2 LOOP
331 fargs(i
) := args(i
+1);
333 FOR i
IN 1..tseq.
COUNT() LOOP
334 fargs(args.
COUNT()-2 + i
) := tseq(i
);
336 IF M(f
).type_id
= 12 THEN
337 malfn
:= TREAT(M(f
) AS mal_func_T
);
338 fn_env
:= env_pkg.
env_new(M
, E
, malfn.env
,
339 malfn.params
, fargs
);
340 val
:= EVAL(malfn.ast
, fn_env
);
342 val
:= do_builtin(f
, fargs
);
345 WHEN fname
= 'map' THEN
347 fargs
:= TREAT(M(args(2)) AS mal_seq_T
).val_seq
;
349 tseq.
EXTEND(fargs.
COUNT());
350 IF M(f
).type_id
= 12 THEN
351 malfn
:= TREAT(M(f
) AS mal_func_T
);
352 FOR i
IN 1..fargs.
COUNT() LOOP
353 fn_env
:= env_pkg.
env_new(M
, E
, malfn.env
,
356 tseq(i
) := EVAL(malfn.ast
, fn_env
);
359 FOR i
IN 1..fargs.
COUNT() LOOP
360 tseq(i
) := do_builtin(f
,
364 RETURN types.
seq(M
, 8, tseq
);
365 WHEN fname
= 'throw' THEN
367 raise_application_error(-20000, 'MalException', TRUE);
369 RETURN core.
do_core_func(M
, H
, fn
, args
);
375 FUNCTION PRINT(exp integer) RETURN varchar IS
377 RETURN printer.
pr_str(M
, H
, exp);
381 FUNCTION REP(line
varchar) RETURN varchar IS
383 RETURN PRINT(EVAL(READ(line
), repl_env
));
387 -- initialize memory pools
388 M
:= types.
mem_new();
389 H
:= types.
map_entry_table();
390 E
:= env_pkg.
env_entry_table();
392 repl_env
:= env_pkg.
env_new(M
, E
, NULL);
394 argv
:= TREAT(M(reader.
read_str(M
, H
, args
)) AS mal_seq_T
).val_seq
;
396 -- core.EXT: defined using PL/SQL
397 core_ns
:= core.
get_core_ns();
398 FOR cidx
IN 1..core_ns.
COUNT LOOP
399 x
:= env_pkg.
env_set(M
, E
, repl_env
,
400 types.
symbol(M
, core_ns(cidx
)),
401 types.
func(M
, core_ns(cidx
)));
403 x
:= env_pkg.
env_set(M
, E
, repl_env
,
404 types.
symbol(M
, 'eval'),
405 types.
func(M
, 'do_eval'));
406 x
:= env_pkg.
env_set(M
, E
, repl_env
,
407 types.
symbol(M
, '*ARGV*'),
408 types.
slice(M
, argv
, 1));
410 -- core.mal: defined using the language itself
411 line
:= REP('(def! *host-language* "PL/SQL")');
412 line
:= REP('(def! not (fn* (a) (if a false true)))');
413 line
:= REP('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))');
414 line
:= REP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw "odd number of forms to cond")) (cons ''cond (rest (rest xs)))))))');
415 line
:= REP('(def! *gensym-counter* (atom 0))');
416 line
:= REP('(def! gensym (fn* [] (symbol (str "G__" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))');
417 line
:= REP('(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))');
419 IF argv.
COUNT() > 0 THEN
421 line
:= REP('(load-file "' ||
422 TREAT(M(argv(1)) AS mal_str_T
).val_str ||
424 io.
close(1); -- close output stream
426 EXCEPTION WHEN OTHERS THEN
427 io.
writeline('Error: ' || SQLERRM
);
428 io.
writeline(dbms_utility.format_error_backtrace
);
429 io.
close(1); -- close output stream
434 line
:= REP('(println (str "Mal [" *host-language* "]"))');
437 line
:= io.
readline('user> ', 0);
438 IF line
= EMPTY_CLOB() THEN CONTINUE; END IF;
439 IF line
IS NOT NULL THEN
440 io.
writeline(REP(line
));
443 EXCEPTION WHEN OTHERS THEN
444 IF SQLCODE = -20001 THEN -- io read stream closed
445 io.
close(1); -- close output stream
448 IF SQLCODE <> -20000 THEN
449 io.
writeline('Error: ' || SQLERRM
);
451 io.
writeline('Error: ' || printer.
pr_str(M
, H
, err_val
));
453 io.
writeline(dbms_utility.format_error_backtrace
);