plsql: add step9 basics. Refactor arg list passing.
[jackhill/mal.git] / plsql / step7_quote.sql
CommitLineData
0fc03918
JM
1@io.sql
2@types.sql
3@reader.sql
4@printer.sql
5@env.sql
6@core.sql
7
8CREATE OR REPLACE PACKAGE mal IS
9
10FUNCTION MAIN(pwd varchar) RETURN integer;
11
12END mal;
13/
14
15CREATE OR REPLACE PACKAGE BODY mal IS
16
17FUNCTION MAIN(pwd varchar) RETURN integer IS
18 M mem_type;
19 env_mem env_mem_type;
20 repl_env integer;
21 x integer;
22 line varchar2(4000);
23 core_ns core_ns_type;
24 cidx integer;
25
26 -- read
27 FUNCTION READ(line varchar) RETURN integer IS
28 BEGIN
29 RETURN reader.read_str(M, line);
30 END;
31
32 -- eval
33
34 -- forward declarations
35 FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer;
150011e4 36 FUNCTION do_builtin(fn integer, args mal_seq_items_type) RETURN integer;
0fc03918
JM
37
38 FUNCTION is_pair(ast integer) RETURN BOOLEAN IS
39 BEGIN
40 RETURN M(ast).type_id IN (8,9) AND types.count(M, ast) > 0;
41 END;
42
43 FUNCTION quasiquote(ast integer) RETURN integer IS
44 a0 integer;
45 a00 integer;
46 BEGIN
47 IF NOT is_pair(ast) THEN
48 RETURN types.list(M, types.symbol(M, 'quote'), ast);
49 ELSE
50 a0 := types.nth(M, ast, 0);
51 IF M(a0).type_id = 7 AND
52 TREAT(m(a0) AS mal_str_type).val_str = 'unquote' THEN
53 RETURN types.nth(M, ast, 1);
54 ELSIF is_pair(a0) THEN
55 a00 := types.nth(M, a0, 0);
56 IF M(a00).type_id = 7 AND
57 TREAT(M(a00) AS mal_str_type).val_str = 'splice-unquote' THEN
58 RETURN types.list(M, types.symbol(M, 'concat'),
59 types.nth(M, a0, 1),
60 quasiquote(types.slice(M, ast, 1)));
61 END IF;
62 END IF;
63 RETURN types.list(M, types.symbol(M, 'cons'),
64 quasiquote(a0),
65 quasiquote(types.slice(M, ast, 1)));
66 END IF;
67 END;
68
69 FUNCTION eval_ast(ast integer, env integer) RETURN integer IS
70 i integer;
71 old_seq mal_seq_items_type;
72 new_seq mal_seq_items_type;
73 BEGIN
74 IF M(ast).type_id = 7 THEN
75 RETURN env_pkg.env_get(M, env_mem, env, ast);
76 ELSIF M(ast).type_id IN (8,9) THEN
77 old_seq := TREAT(M(ast) AS mal_seq_type).val_seq;
78 new_seq := mal_seq_items_type();
79 new_seq.EXTEND(old_seq.COUNT);
80 FOR i IN 1..old_seq.COUNT LOOP
81 new_seq(i) := EVAL(old_seq(i), env);
82 END LOOP;
83 RETURN types.seq(M, M(ast).type_id, new_seq);
84 ELSE
85 RETURN ast;
86 END IF;
87 END;
88
89 FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer IS
90 ast integer := orig_ast;
91 env integer := orig_env;
92 el integer;
93 a0 integer;
150011e4 94 a0sym varchar2(100);
0fc03918
JM
95 seq mal_seq_items_type;
96 let_env integer;
97 i integer;
98 f integer;
99 cond integer;
100 malfn malfunc_type;
150011e4 101 args mal_seq_items_type;
0fc03918
JM
102 BEGIN
103 WHILE TRUE LOOP
104 IF M(ast).type_id <> 8 THEN
105 RETURN eval_ast(ast, env);
106 END IF;
107
108 -- apply
109 a0 := types.first(M, ast);
110 if M(a0).type_id = 7 THEN -- symbol
111 a0sym := TREAT(M(a0) AS mal_str_type).val_str;
112 ELSE
113 a0sym := '__<*fn*>__';
114 END IF;
115
116 CASE
117 WHEN a0sym = 'def!' THEN
118 RETURN env_pkg.env_set(M, env_mem, env,
119 types.nth(M, ast, 1), EVAL(types.nth(M, ast, 2), env));
120 WHEN a0sym = 'let*' THEN
121 let_env := env_pkg.env_new(M, env_mem, env);
122 seq := TREAT(M(types.nth(M, ast, 1)) AS mal_seq_type).val_seq;
123 i := 1;
124 WHILE i <= seq.COUNT LOOP
125 x := env_pkg.env_set(M, env_mem, let_env,
126 seq(i), EVAL(seq(i+1), let_env));
127 i := i + 2;
128 END LOOP;
129 env := let_env;
130 ast := types.nth(M, ast, 2); -- TCO
131 WHEN a0sym = 'quote' THEN
132 RETURN types.nth(M, ast, 1);
133 WHEN a0sym = 'quasiquote' THEN
134 RETURN EVAL(quasiquote(types.nth(M, ast, 1)), env);
135 WHEN a0sym = 'do' THEN
136 x := types.slice(M, ast, 1, types.count(M, ast)-2);
137 x := eval_ast(x, env);
138 ast := types.nth(M, ast, types.count(M, ast)-1); -- TCO
139 WHEN a0sym = 'if' THEN
140 cond := EVAL(types.nth(M, ast, 1), env);
141 IF cond = 1 OR cond = 2 THEN -- nil or false
142 IF types.count(M, ast) > 3 THEN
143 ast := EVAL(types.nth(M, ast, 3), env); -- TCO
144 ELSE
145 RETURN 1; -- nil
146 END IF;
147 ELSE
148 ast := EVAL(types.nth(M, ast, 2), env); -- TCO
149 END IF;
150 WHEN a0sym = 'fn*' THEN
151 RETURN types.malfunc(M, types.nth(M, ast, 2),
152 types.nth(M, ast, 1),
153 env);
154 ELSE
155 el := eval_ast(ast, env);
156 f := types.first(M, el);
150011e4 157 args := TREAT(M(types.slice(M, el, 1)) AS mal_seq_type).val_seq;
0fc03918
JM
158 IF M(f).type_id = 12 THEN
159 malfn := TREAT(M(f) AS malfunc_type);
160 env := env_pkg.env_new(M, env_mem, malfn.env,
161 malfn.params, args);
162 ast := malfn.ast; -- TCO
163 ELSE
164 RETURN do_builtin(f, args);
165 END IF;
166 END CASE;
167
168 END LOOP;
169
170 END;
171
172 -- hack to get around lack of function references
173 -- functions that require special access to repl_env or EVAL
174 -- are implemented directly here, otherwise, core.do_core_fn
175 -- is called.
150011e4 176 FUNCTION do_builtin(fn integer, args mal_seq_items_type) RETURN integer IS
0fc03918 177 fname varchar2(100);
150011e4 178 val integer;
0fc03918
JM
179 f integer;
180 malfn malfunc_type;
181 fargs mal_seq_items_type;
182 fn_env integer;
183 BEGIN
184 fname := TREAT(M(fn) AS mal_str_type).val_str;
185 CASE
186 WHEN fname = 'do_eval' THEN
150011e4 187 RETURN EVAL(args(1), repl_env);
0fc03918 188 WHEN fname = 'swap!' THEN
150011e4
JM
189 val := TREAT(M(args(1)) AS mal_atom_type).val;
190 f := args(2);
0fc03918
JM
191 -- slice one extra at the beginning that will be changed
192 -- to the value of the atom
150011e4
JM
193 fargs := TREAT(M(types.slice(M, args, 1)) AS mal_seq_type).val_seq;
194 fargs(1) := val;
0fc03918
JM
195 IF M(f).type_id = 12 THEN
196 malfn := TREAT(M(f) AS malfunc_type);
197 fn_env := env_pkg.env_new(M, env_mem, malfn.env,
150011e4
JM
198 malfn.params, fargs);
199 val := EVAL(malfn.ast, fn_env);
0fc03918 200 ELSE
150011e4 201 val := do_builtin(f, fargs);
0fc03918 202 END IF;
150011e4
JM
203 M(args(1)) := mal_atom_type(13, val);
204 RETURN val;
0fc03918 205 ELSE
150011e4 206 RETURN core.do_core_func(M, fn, args);
0fc03918
JM
207 END CASE;
208 END;
209
210
211 -- print
212 FUNCTION PRINT(exp integer) RETURN varchar IS
213 BEGIN
214 RETURN printer.pr_str(M, exp);
215 END;
216
217 -- repl
218 FUNCTION REP(line varchar) RETURN varchar IS
219 BEGIN
220 RETURN PRINT(EVAL(READ(line), repl_env));
221 END;
222
223BEGIN
224 M := types.mem_new();
225 env_mem := env_mem_type();
226
227 repl_env := env_pkg.env_new(M, env_mem, NULL);
228
229 -- core.EXT: defined using PL/SQL
230 core_ns := core.get_core_ns();
231 FOR cidx IN 1..core_ns.COUNT LOOP
232 x := env_pkg.env_set(M, env_mem, repl_env,
233 types.symbol(M, core_ns(cidx)),
234 types.func(M, core_ns(cidx)));
235 END LOOP;
236 x := env_pkg.env_set(M, env_mem, repl_env,
237 types.symbol(M, 'eval'),
238 types.func(M, 'do_eval'));
239 x := env_pkg.env_set(M, env_mem, repl_env,
240 types.symbol(M, '*ARGV*'),
241 types.list(M));
242
243 -- core.mal: defined using the language itself
244 line := REP('(def! not (fn* (a) (if a false true)))');
245 line := REP('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))');
246
247 WHILE true LOOP
248 BEGIN
249 line := stream_readline('user> ', 0);
250 IF line IS NULL THEN CONTINUE; END IF;
251 IF line IS NOT NULL THEN
252 stream_writeline(REP(line));
253 END IF;
254
255 EXCEPTION WHEN OTHERS THEN
150011e4 256 IF SQLCODE = -20001 THEN -- io streams closed
0fc03918
JM
257 RETURN 0;
258 END IF;
259 stream_writeline('Error: ' || SQLERRM);
260 stream_writeline(dbms_utility.format_error_backtrace);
261 END;
262 END LOOP;
263END;
264
265END mal;
266/
267show errors;
268
269quit;