1 -- ---------------------------------------------------------
12 -- ---------------------------------------------------------
17 CREATE FUNCTION mal.
READ(line
varchar) RETURNS integer AS $$
19 RETURN reader.
read_str(line
);
20 END; $$
LANGUAGE plpgsql
;
23 CREATE FUNCTION mal.
is_pair(ast
integer) RETURNS boolean AS $$
25 RETURN types.
_sequential_Q(ast
) AND types.
_count(ast
) > 0;
26 END; $$
LANGUAGE plpgsql
;
28 CREATE FUNCTION mal.
quasiquote(ast
integer) RETURNS integer AS $$
33 IF NOT mal.
is_pair(ast
) THEN
34 RETURN types.
_list(ARRAY[types.
_symbolv('quote'), ast
]);
36 a0
:= types.
_nth(ast
, 0);
37 IF types.
_symbol_Q(a0
) AND a0
= types.
_symbolv('unquote') THEN
38 RETURN types.
_nth(ast
, 1);
40 a00
:= types.
_nth(a0
, 0);
41 IF types.
_symbol_Q(a00
) AND
42 a00
= types.
_symbolv('splice-unquote') THEN
43 RETURN types.
_list(ARRAY[types.
_symbolv('concat'),
45 mal.
quasiquote(types.
_rest(ast
))]);
48 RETURN types.
_list(ARRAY[types.
_symbolv('cons'),
49 mal.
quasiquote(types.
_first(ast
)),
50 mal.
quasiquote(types.
_rest(ast
))]);
52 END; $$
LANGUAGE plpgsql
;
54 CREATE FUNCTION mal.
eval_ast(ast
integer, env
integer) RETURNS integer AS $$
65 SELECT type_id
INTO type FROM types.
value WHERE value_id
= ast
;
69 result := envs.
get(env
, ast
);
71 WHEN type IN (8, 9) THEN
73 SELECT val_seq
INTO seq
FROM types.
value WHERE value_id
= ast
;
74 -- Evaluate each entry creating a new sequence
75 FOR i
IN 1 ..
COALESCE(array_length(seq
, 1), 0) LOOP
76 eseq
[i
] := mal.
EVAL(seq
[i
], env
);
78 INSERT INTO types.
value (type_id
, val_seq
) VALUES (type, eseq
)
79 RETURNING value_id
INTO result;
83 SELECT val_hash
INTO hash
FROM types.
value WHERE value_id
= ast
;
84 -- Evaluate each value for every key/value
85 FOR kv
IN SELECT * FROM each(hash
) LOOP
86 e
:= mal.
EVAL(CAST(kv.
value AS integer), env
);
88 ehash
:= hstore(kv.
key, CAST(e
AS varchar));
90 ehash
:= ehash ||
hstore(kv.
key, CAST(e
AS varchar));
93 INSERT INTO types.
value (type_id
, val_hash
) VALUES (type, ehash
)
94 RETURNING value_id
INTO result;
101 END; $$
LANGUAGE plpgsql
;
103 CREATE FUNCTION mal.
EVAL(ast
integer, env
integer) RETURNS integer AS $$
123 -- PERFORM writeline(format('EVAL: %s [%s]', pr_str(ast), ast));
124 SELECT type_id
INTO type FROM types.
value WHERE value_id
= ast
;
126 RETURN mal.
eval_ast(ast
, env
);
128 IF types.
_count(ast
) = 0 THEN
132 a0
:= types.
_first(ast
);
133 IF types.
_symbol_Q(a0
) THEN
134 a0sym
:= (SELECT val_string
FROM types.
value WHERE value_id
= a0
);
136 a0sym
:= '__<*fn*>__';
140 WHEN a0sym
= 'def!' THEN
142 RETURN envs.
set(env
, types.
_nth(ast
, 1),
143 mal.
EVAL(types.
_nth(ast
, 2), env
));
145 WHEN a0sym
= 'let*' THEN
147 let_env
:= envs.
new(env
);
148 a1
:= types.
_nth(ast
, 1);
149 binds
:= (SELECT val_seq
FROM types.
value WHERE value_id
= a1
);
151 WHILE idx
< array_length(binds
, 1) LOOP
152 PERFORM envs.
set(let_env
, binds
[idx
],
153 mal.
EVAL(binds
[idx
+1], let_env
));
157 ast
:= types.
_nth(ast
, 2);
160 WHEN a0sym
= 'quote' THEN
162 RETURN types.
_nth(ast
, 1);
164 WHEN a0sym
= 'quasiquote' THEN
166 ast
:= mal.
quasiquote(types.
_nth(ast
, 1));
169 WHEN a0sym
= 'do' THEN
171 PERFORM mal.
eval_ast(types.
_slice(ast
, 1, types.
_count(ast
)-1), env
);
172 ast
:= types.
_nth(ast
, types.
_count(ast
)-1);
175 WHEN a0sym
= 'if' THEN
177 cond
:= mal.
EVAL(types.
_nth(ast
, 1), env
);
178 SELECT type_id
INTO type FROM types.
value WHERE value_id
= cond
;
179 IF type = 0 OR type = 1 THEN -- nil or false
180 IF types.
_count(ast
) > 3 THEN
181 ast
:= types.
_nth(ast
, 3);
187 ast
:= types.
_nth(ast
, 2);
191 WHEN a0sym
= 'fn*' THEN
193 RETURN types.
_malfunc(types.
_nth(ast
, 2), types.
_nth(ast
, 1), env
);
197 el
:= mal.
eval_ast(ast
, env
);
198 SELECT type_id
, val_string
, ast_id
, params_id
, env_id
199 INTO type, fname
, fast
, fparams
, fenv
200 FROM types.
value WHERE value_id
= types.
_first(el
);
201 args
:= types.
_restArray(el
);
203 EXECUTE format('SELECT %s($1);', fname
)
204 INTO result USING args
;
207 env
:= envs.
new(fenv
, fparams
, args
);
211 RAISE
EXCEPTION 'Invalid function call';
216 END; $$
LANGUAGE plpgsql
;
219 CREATE FUNCTION mal.
PRINT(exp integer) RETURNS varchar AS $$
221 RETURN printer.
pr_str(exp);
222 END; $$
LANGUAGE plpgsql
;
227 -- repl_env is environment 0
229 CREATE FUNCTION mal.
REP(line
varchar) RETURNS varchar AS $$
231 RETURN mal.
PRINT(mal.
EVAL(mal.
READ(line
), 0));
232 END; $$
LANGUAGE plpgsql
;
234 -- core.sql: defined using SQL (in core.sql)
235 -- repl_env is created and populated with core functions in by core.sql
236 CREATE FUNCTION mal.
mal_eval(args
integer[]) RETURNS integer AS $$
238 RETURN mal.
EVAL(args
[1], 0);
239 END; $$
LANGUAGE plpgsql
;
240 INSERT INTO types.
value (type_id
, val_string
) VALUES (11, 'mal.mal_eval');
242 SELECT envs.
vset(0, 'eval',
243 (SELECT value_id
FROM types.
value
244 WHERE val_string
= 'mal.mal_eval')) \g
'/dev/null'
245 -- *ARGV* values are set by RUN
246 SELECT envs.
vset(0, '*ARGV*', mal.
READ('()')) \g
'/dev/null'
249 -- core.mal: defined using the language itself
250 SELECT mal.
REP('(def! not (fn* (a) (if a false true)))') \g
'/dev/null'
251 SELECT mal.
REP('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\nnil)")))))') \g
'/dev/null'
253 CREATE FUNCTION mal.
MAIN(pwd
varchar, argstring
varchar DEFAULT NULL)
254 RETURNS integer AS $$
260 PERFORM envs.
vset(0, '*PWD*', types.
_stringv(pwd
));
262 IF argstring
IS NOT NULL THEN
263 allargs
:= mal.
READ(argstring
);
264 PERFORM envs.
vset(0, '*ARGV*', types.
_rest(allargs
));
265 PERFORM mal.
REP('(load-file ' ||
266 printer.
pr_str(types.
_first(allargs
)) ||
')');
268 PERFORM io.
wait_flushed(1);
275 line
:= io.
readline('user> ', 0);
280 IF line
NOT IN ('', E
'\n') THEN
281 output := mal.
REP(line
);
282 PERFORM io.
writeline(output);
285 EXCEPTION WHEN OTHERS THEN
286 PERFORM io.
writeline('Error: ' || SQLERRM
);
289 END; $$
LANGUAGE plpgsql
;