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
19 E env_pkg.env_entry_table
;
25 argv mal_seq_items_type
;
29 FUNCTION READ(line
varchar) RETURN integer IS
31 RETURN reader.
read_str(M
, line
);
36 -- forward declarations
37 FUNCTION EVAL(orig_ast
integer, orig_env
integer) RETURN integer;
38 FUNCTION do_builtin(fn
integer, args mal_seq_items_type
) RETURN integer;
40 FUNCTION is_pair(ast
integer) RETURN BOOLEAN IS
42 RETURN M(ast
).type_id
IN (8,9) AND types.
count(M
, ast
) > 0;
45 FUNCTION quasiquote(ast
integer) RETURN integer IS
49 IF NOT is_pair(ast
) THEN
50 RETURN types.
list(M
, types.
symbol(M
, 'quote'), ast
);
52 a0
:= types.
nth(M
, ast
, 0);
53 IF M(a0
).type_id
= 7 AND
54 TREAT(m(a0
) AS mal_str_type
).val_str
= 'unquote' THEN
55 RETURN types.
nth(M
, ast
, 1);
56 ELSIF
is_pair(a0
) THEN
57 a00
:= types.
nth(M
, a0
, 0);
58 IF M(a00
).type_id
= 7 AND
59 TREAT(M(a00
) AS mal_str_type
).val_str
= 'splice-unquote' THEN
60 RETURN types.
list(M
, types.
symbol(M
, 'concat'),
62 quasiquote(types.
slice(M
, ast
, 1)));
65 RETURN types.
list(M
, types.
symbol(M
, 'cons'),
67 quasiquote(types.
slice(M
, ast
, 1)));
72 FUNCTION is_macro_call(ast
integer, env
integer) RETURN BOOLEAN IS
76 IF M(ast
).type_id
= 8 THEN
77 a0
:= types.
nth(M
, ast
, 0);
78 IF M(a0
).type_id
= 7 AND
79 env_pkg.
env_find(M
, E
, env
, a0
) IS NOT NULL THEN
80 mac
:= env_pkg.
env_get(M
, E
, env
, a0
);
81 IF M(mac
).type_id
= 12 THEN
82 RETURN TREAT(M(mac
) AS malfunc_type
).is_macro
> 0;
89 FUNCTION macroexpand(orig_ast
integer, env
integer) RETURN integer IS
93 fargs mal_seq_items_type
;
97 WHILE
is_macro_call(ast
, env
) LOOP
98 mac
:= env_pkg.
env_get(M
, E
, env
, types.
nth(M
, ast
, 0));
99 fargs
:= TREAT(M(types.
slice(M
, ast
, 1)) as mal_seq_type
).val_seq
;
100 if M(mac
).type_id
= 12 THEN
101 malfn
:= TREAT(M(mac
) AS malfunc_type
);
102 fn_env
:= env_pkg.
env_new(M
, E
, malfn.env
,
105 ast
:= EVAL(malfn.ast
, fn_env
);
107 ast
:= do_builtin(mac
, fargs
);
113 FUNCTION eval_ast(ast
integer, env
integer) RETURN integer IS
115 old_seq mal_seq_items_type
;
116 new_seq mal_seq_items_type
;
118 IF M(ast
).type_id
= 7 THEN
119 RETURN env_pkg.
env_get(M
, E
, env
, ast
);
120 ELSIF
M(ast
).type_id
IN (8,9) THEN
121 old_seq
:= TREAT(M(ast
) AS mal_seq_type
).val_seq
;
122 new_seq
:= mal_seq_items_type();
123 new_seq.
EXTEND(old_seq.
COUNT);
124 FOR i
IN 1..old_seq.
COUNT LOOP
125 new_seq(i
) := EVAL(old_seq(i
), env
);
127 RETURN types.
seq(M
, M(ast
).type_id
, new_seq
);
133 FUNCTION EVAL(orig_ast
integer, orig_env
integer) RETURN integer IS
134 ast
integer := orig_ast
;
135 env
integer := orig_env
;
139 seq mal_seq_items_type
;
146 args mal_seq_items_type
;
149 IF M(ast
).type_id
<> 8 THEN
150 RETURN eval_ast(ast
, env
);
154 ast
:= macroexpand(ast
, env
);
155 IF M(ast
).type_id
<> 8 THEN
156 RETURN eval_ast(ast
, env
);
158 IF types.
count(M
, ast
) = 0 THEN
162 a0
:= types.
first(M
, ast
);
163 if M(a0
).type_id
= 7 THEN -- symbol
164 a0sym
:= TREAT(M(a0
) AS mal_str_type
).val_str
;
166 a0sym
:= '__<*fn*>__';
170 WHEN a0sym
= 'def!' THEN
171 RETURN env_pkg.
env_set(M
, E
, env
,
172 types.
nth(M
, ast
, 1), EVAL(types.
nth(M
, ast
, 2), env
));
173 WHEN a0sym
= 'let*' THEN
174 let_env
:= env_pkg.
env_new(M
, E
, env
);
175 seq
:= TREAT(M(types.
nth(M
, ast
, 1)) AS mal_seq_type
).val_seq
;
177 WHILE i
<= seq.
COUNT LOOP
178 x
:= env_pkg.
env_set(M
, E
, let_env
,
179 seq(i
), EVAL(seq(i
+1), let_env
));
183 ast
:= types.
nth(M
, ast
, 2); -- TCO
184 WHEN a0sym
= 'quote' THEN
185 RETURN types.
nth(M
, ast
, 1);
186 WHEN a0sym
= 'quasiquote' THEN
187 RETURN EVAL(quasiquote(types.
nth(M
, ast
, 1)), env
);
188 WHEN a0sym
= 'defmacro!' THEN
189 x
:= EVAL(types.
nth(M
, ast
, 2), env
);
190 malfn
:= TREAT(M(x
) as malfunc_type
);
193 RETURN env_pkg.
env_set(M
, E
, env
,
194 types.
nth(M
, ast
, 1), x
);
195 WHEN a0sym
= 'macroexpand' THEN
196 RETURN macroexpand(types.
nth(M
, ast
, 1), env
);
197 WHEN a0sym
= 'try*' THEN
202 a20sym
varchar2(100);
204 RETURN EVAL(types.
nth(M
, ast
, 1), env
);
206 EXCEPTION WHEN OTHERS THEN
207 IF types.
count(M
, ast
) > 2 THEN
208 a2
:= types.
nth(M
, ast
, 2);
209 IF M(a2
).type_id
= 8 THEN
210 a20
:= types.
nth(M
, a2
, 0);
211 IF M(a20
).type_id
= 7 THEN
212 a20sym
:= TREAT(M(a20
) AS mal_str_type
).val_str
;
216 IF a20sym
= 'catch*' THEN
217 IF SQLCODE <> -20000 THEN
218 IF SQLCODE < -20000 AND SQLCODE > -20100 THEN
219 exc
:= types.
string(M
,
220 REGEXP_REPLACE(SQLERRM
,
221 '^ORA-200[0-9][0-9]: '));
223 exc
:= types.
string(M
, SQLERRM
);
229 try_env
:= env_pkg.
env_new(M
, E
, env
,
230 types.
list(M
, types.
nth(M
, a2
, 1)),
231 mal_seq_items_type(exc
));
232 RETURN EVAL(types.
nth(M
, a2
, 2), try_env
);
234 RAISE
; -- not handled, re-raise the exception
236 WHEN a0sym
= 'do' THEN
237 x
:= types.
slice(M
, ast
, 1, types.
count(M
, ast
)-2);
238 x
:= eval_ast(x
, env
);
239 ast
:= types.
nth(M
, ast
, types.
count(M
, ast
)-1); -- TCO
240 WHEN a0sym
= 'if' THEN
241 cond
:= EVAL(types.
nth(M
, ast
, 1), env
);
242 IF cond
= 1 OR cond
= 2 THEN -- nil or false
243 IF types.
count(M
, ast
) > 3 THEN
244 ast
:= EVAL(types.
nth(M
, ast
, 3), env
); -- TCO
249 ast
:= EVAL(types.
nth(M
, ast
, 2), env
); -- TCO
251 WHEN a0sym
= 'fn*' THEN
252 RETURN types.
malfunc(M
, types.
nth(M
, ast
, 2),
253 types.
nth(M
, ast
, 1),
256 el
:= eval_ast(ast
, env
);
257 f
:= types.
first(M
, el
);
258 args
:= TREAT(M(types.
slice(M
, el
, 1)) AS mal_seq_type
).val_seq
;
259 IF M(f
).type_id
= 12 THEN
260 malfn
:= TREAT(M(f
) AS malfunc_type
);
261 env
:= env_pkg.
env_new(M
, E
, malfn.env
,
263 ast
:= malfn.ast
; -- TCO
265 RETURN do_builtin(f
, args
);
273 -- hack to get around lack of function references
274 -- functions that require special access to repl_env or EVAL
275 -- are implemented directly here, otherwise, core.do_core_fn
277 FUNCTION do_builtin(fn
integer, args mal_seq_items_type
) RETURN integer IS
282 fargs mal_seq_items_type
;
285 tseq mal_seq_items_type
;
287 fname
:= TREAT(M(fn
) AS mal_str_type
).val_str
;
289 WHEN fname
= 'do_eval' THEN
290 RETURN EVAL(args(1), repl_env
);
291 WHEN fname
= 'swap!' THEN
292 val
:= TREAT(M(args(1)) AS mal_atom_type
).val
;
294 -- slice one extra at the beginning that will be changed
295 -- to the value of the atom
296 fargs
:= TREAT(M(types.
slice(M
, args
, 1)) AS mal_seq_type
).val_seq
;
298 IF M(f
).type_id
= 12 THEN
299 malfn
:= TREAT(M(f
) AS malfunc_type
);
300 fn_env
:= env_pkg.
env_new(M
, E
, malfn.env
,
301 malfn.params
, fargs
);
302 val
:= EVAL(malfn.ast
, fn_env
);
304 val
:= do_builtin(f
, fargs
);
306 M(args(1)) := mal_atom_type(13, val
);
308 WHEN fname
= 'apply' THEN
310 fargs
:= mal_seq_items_type();
311 tseq
:= TREAT(M(args(args.
COUNT())) AS mal_seq_type
).val_seq
;
312 fargs.
EXTEND(args.
COUNT()-2 + tseq.
COUNT());
313 FOR i
IN 1..args.
COUNT()-2 LOOP
314 fargs(i
) := args(i
+1);
316 FOR i
IN 1..tseq.
COUNT() LOOP
317 fargs(args.
COUNT()-2 + i
) := tseq(i
);
319 IF M(f
).type_id
= 12 THEN
320 malfn
:= TREAT(M(f
) AS malfunc_type
);
321 fn_env
:= env_pkg.
env_new(M
, E
, malfn.env
,
322 malfn.params
, fargs
);
323 val
:= EVAL(malfn.ast
, fn_env
);
325 val
:= do_builtin(f
, fargs
);
328 WHEN fname
= 'map' THEN
330 fargs
:= TREAT(M(args(2)) AS mal_seq_type
).val_seq
;
331 tseq
:= mal_seq_items_type();
332 tseq.
EXTEND(fargs.
COUNT());
333 IF M(f
).type_id
= 12 THEN
334 malfn
:= TREAT(M(f
) AS malfunc_type
);
335 FOR i
IN 1..fargs.
COUNT() LOOP
336 fn_env
:= env_pkg.
env_new(M
, E
, malfn.env
,
338 mal_seq_items_type(fargs(i
)));
339 tseq(i
) := EVAL(malfn.ast
, fn_env
);
342 FOR i
IN 1..fargs.
COUNT() LOOP
343 tseq(i
) := do_builtin(f
,
344 mal_seq_items_type(fargs(i
)));
347 RETURN types.
seq(M
, 8, tseq
);
348 WHEN fname
= 'throw' THEN
350 raise_application_error(-20000, 'MalException', TRUE);
352 RETURN core.
do_core_func(M
, fn
, args
);
358 FUNCTION PRINT(exp integer) RETURN varchar IS
360 RETURN printer.
pr_str(M
, exp);
364 FUNCTION REP(line
varchar) RETURN varchar IS
366 RETURN PRINT(EVAL(READ(line
), repl_env
));
370 M
:= types.
mem_new();
371 E
:= env_pkg.
env_entry_table();
373 repl_env
:= env_pkg.
env_new(M
, E
, NULL);
375 argv
:= TREAT(M(reader.
read_str(M
, args
)) AS mal_seq_type
).val_seq
;
377 -- core.EXT: defined using PL/SQL
378 core_ns
:= core.
get_core_ns();
379 FOR cidx
IN 1..core_ns.
COUNT LOOP
380 x
:= env_pkg.
env_set(M
, E
, repl_env
,
381 types.
symbol(M
, core_ns(cidx
)),
382 types.
func(M
, core_ns(cidx
)));
384 x
:= env_pkg.
env_set(M
, E
, repl_env
,
385 types.
symbol(M
, 'eval'),
386 types.
func(M
, 'do_eval'));
387 x
:= env_pkg.
env_set(M
, E
, repl_env
,
388 types.
symbol(M
, '*ARGV*'),
389 types.
slice(M
, argv
, 1));
391 -- core.mal: defined using the language itself
392 line
:= REP('(def! *host-language* "PL/SQL")');
393 line
:= REP('(def! not (fn* (a) (if a false true)))');
394 line
:= REP('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))');
395 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)))))))');
396 line
:= REP('(def! *gensym-counter* (atom 0))');
397 line
:= REP('(def! gensym (fn* [] (symbol (str \"G__\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))');
398 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)))))))))');
400 IF argv.
COUNT() > 0 THEN
401 line
:= REP('(load-file "' ||
402 TREAT(M(argv(1)) AS mal_str_type
).val_str ||
407 line
:= REP('(println (str "Mal [" *host-language* "]"))');
410 line
:= stream_readline('user> ', 0);
411 IF line
IS NULL THEN CONTINUE; END IF;
412 IF line
IS NOT NULL THEN
413 stream_writeline(REP(line
));
416 EXCEPTION WHEN OTHERS THEN
417 IF SQLCODE = -20001 THEN -- io streams closed
420 stream_writeline('Error: ' || SQLERRM
);
421 stream_writeline(dbms_utility.format_error_backtrace
);