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