Refactor to use run scripts, remove *_RUNSTEP
[jackhill/mal.git] / plpgsql / step4_if_fn_do.sql
CommitLineData
494792ab
JM
1-- ---------------------------------------------------------
2-- step4_if_fn_do.sql
3
5340418b 4\i init.sql
53105a77 5\i io.sql
5340418b
JM
6\i types.sql
7\i reader.sql
8\i printer.sql
494792ab 9\i envs.sql
5340418b
JM
10\i core.sql
11
12-- ---------------------------------------------------------
494792ab
JM
13
14CREATE SCHEMA mal;
5340418b
JM
15
16-- read
494792ab 17CREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$
5340418b 18BEGIN
494792ab 19 RETURN reader.read_str(line);
5340418b
JM
20END; $$ LANGUAGE plpgsql;
21
22-- eval
494792ab 23CREATE FUNCTION mal.eval_ast(ast integer, env integer) RETURNS integer AS $$
5340418b
JM
24DECLARE
25 type integer;
b642c0db
JM
26 seq integer[];
27 eseq integer[];
28 hash hstore;
29 ehash hstore;
30 kv RECORD;
5340418b
JM
31 e integer;
32 result integer;
33BEGIN
494792ab 34 SELECT type_id INTO type FROM types.value WHERE value_id = ast;
5340418b
JM
35 CASE
36 WHEN type = 7 THEN
37 BEGIN
494792ab 38 result := envs.get(env, ast);
5340418b 39 END;
b642c0db 40 WHEN type IN (8, 9) THEN
5340418b 41 BEGIN
494792ab 42 SELECT val_seq INTO seq FROM types.value WHERE value_id = ast;
b642c0db
JM
43 -- Evaluate each entry creating a new sequence
44 FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP
494792ab 45 eseq[i] := mal.EVAL(seq[i], env);
b642c0db 46 END LOOP;
494792ab 47 INSERT INTO types.value (type_id, val_seq) VALUES (type, eseq)
97c0256d 48 RETURNING value_id INTO result;
b642c0db
JM
49 END;
50 WHEN type = 10 THEN
51 BEGIN
494792ab 52 SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;
b642c0db
JM
53 -- Evaluate each value for every key/value
54 FOR kv IN SELECT * FROM each(hash) LOOP
494792ab 55 e := mal.EVAL(CAST(kv.value AS integer), env);
b642c0db
JM
56 IF ehash IS NULL THEN
57 ehash := hstore(kv.key, CAST(e AS varchar));
58 ELSE
59 ehash := ehash || hstore(kv.key, CAST(e AS varchar));
60 END IF;
5340418b 61 END LOOP;
494792ab 62 INSERT INTO types.value (type_id, val_hash) VALUES (type, ehash)
b642c0db 63 RETURNING value_id INTO result;
5340418b
JM
64 END;
65 ELSE
66 result := ast;
67 END CASE;
68
69 RETURN result;
70END; $$ LANGUAGE plpgsql;
71
494792ab 72CREATE FUNCTION mal.EVAL(ast integer, env integer) RETURNS integer AS $$
5340418b
JM
73DECLARE
74 type integer;
75 a0 integer;
76 a0sym varchar;
77 a1 integer;
78 let_env integer;
b642c0db 79 idx integer;
5340418b 80 binds integer[];
5340418b
JM
81 el integer;
82 fn integer;
83 fname varchar;
84 args integer[];
85 cond integer;
86 fast integer;
87 fparams integer;
88 fenv integer;
89 result integer;
90BEGIN
494792ab
JM
91 -- PERFORM writeline(format('EVAL: %s [%s]', pr_str(ast), ast));
92 SELECT type_id INTO type FROM types.value WHERE value_id = ast;
5340418b 93 IF type <> 8 THEN
494792ab
JM
94 RETURN mal.eval_ast(ast, env);
95 END IF;
96 IF types._count(ast) = 0 THEN
97 RETURN ast;
5340418b
JM
98 END IF;
99
494792ab
JM
100 a0 := types._first(ast);
101 IF types._symbol_Q(a0) THEN
102 a0sym := (SELECT val_string FROM types.value WHERE value_id = a0);
5340418b
JM
103 ELSE
104 a0sym := '__<*fn*>__';
105 END IF;
106
5340418b
JM
107 CASE
108 WHEN a0sym = 'def!' THEN
109 BEGIN
494792ab
JM
110 RETURN envs.set(env, types._nth(ast, 1),
111 mal.EVAL(types._nth(ast, 2), env));
5340418b
JM
112 END;
113 WHEN a0sym = 'let*' THEN
114 BEGIN
494792ab
JM
115 let_env := envs.new(env);
116 a1 := types._nth(ast, 1);
117 binds := (SELECT val_seq FROM types.value WHERE value_id = a1);
b642c0db
JM
118 idx := 1;
119 WHILE idx < array_length(binds, 1) LOOP
494792ab
JM
120 PERFORM envs.set(let_env, binds[idx],
121 mal.EVAL(binds[idx+1], let_env));
b642c0db 122 idx := idx + 2;
5340418b 123 END LOOP;
494792ab 124 RETURN mal.EVAL(types._nth(ast, 2), let_env);
5340418b
JM
125 END;
126 WHEN a0sym = 'do' THEN
127 BEGIN
494792ab
JM
128 el := mal.eval_ast(types._rest(ast), env);
129 RETURN types._nth(el, types._count(el)-1);
5340418b
JM
130 END;
131 WHEN a0sym = 'if' THEN
132 BEGIN
494792ab
JM
133 cond := mal.EVAL(types._nth(ast, 1), env);
134 SELECT type_id INTO type FROM types.value WHERE value_id = cond;
5340418b 135 IF type = 0 OR type = 1 THEN -- nil or false
494792ab
JM
136 IF types._count(ast) > 3 THEN
137 RETURN mal.EVAL(types._nth(ast, 3), env);
5340418b
JM
138 ELSE
139 RETURN 0; -- nil
140 END IF;
141 ELSE
494792ab 142 RETURN mal.EVAL(types._nth(ast, 2), env);
5340418b
JM
143 END IF;
144 END;
145 WHEN a0sym = 'fn*' THEN
146 BEGIN
494792ab 147 RETURN types._malfunc(types._nth(ast, 2), types._nth(ast, 1), env);
5340418b
JM
148 END;
149 ELSE
150 BEGIN
494792ab 151 el := mal.eval_ast(ast, env);
4facce82 152 SELECT type_id, val_string, ast_id, params_id, env_id
b642c0db 153 INTO type, fname, fast, fparams, fenv
494792ab
JM
154 FROM types.value WHERE value_id = types._first(el);
155 args := types._restArray(el);
5340418b
JM
156 IF type = 11 THEN
157 EXECUTE format('SELECT %s($1);', fname)
158 INTO result USING args;
159 RETURN result;
160 ELSIF type = 12 THEN
494792ab 161 RETURN mal.EVAL(fast, envs.new(fenv, fparams, args));
5340418b
JM
162 ELSE
163 RAISE EXCEPTION 'Invalid function call';
164 END IF;
165 END;
166 END CASE;
167END; $$ LANGUAGE plpgsql;
168
169-- print
494792ab 170CREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$
5340418b 171BEGIN
494792ab 172 RETURN printer.pr_str(exp);
5340418b
JM
173END; $$ LANGUAGE plpgsql;
174
175
176-- repl
177
178-- repl_env is environment 0
179
494792ab 180CREATE FUNCTION mal.REP(line varchar) RETURNS varchar AS $$
5340418b 181BEGIN
494792ab 182 RETURN mal.PRINT(mal.EVAL(mal.READ(line), 0));
5340418b
JM
183END; $$ LANGUAGE plpgsql;
184
185-- core.sql: defined using SQL (in core.sql)
186-- repl_env is created and populated with core functions in by core.sql
187
188-- core.mal: defined using the language itself
494792ab 189SELECT mal.REP('(def! not (fn* (a) (if a false true)))') \g '/dev/null'
5340418b 190
494792ab 191CREATE FUNCTION mal.MAIN(pwd varchar) RETURNS integer AS $$
53105a77 192DECLARE
494792ab
JM
193 line varchar;
194 output varchar;
53105a77
JM
195BEGIN
196 WHILE true
197 LOOP
198 BEGIN
494792ab
JM
199 line := io.readline('user> ', 0);
200 IF line IS NULL THEN
201 PERFORM io.close(1);
202 RETURN 0;
203 END IF;
204 IF line NOT IN ('', E'\n') THEN
205 output := mal.REP(line);
206 PERFORM io.writeline(output);
53105a77
JM
207 END IF;
208
209 EXCEPTION WHEN OTHERS THEN
494792ab 210 PERFORM io.writeline('Error: ' || SQLERRM);
53105a77
JM
211 END;
212 END LOOP;
213END; $$ LANGUAGE plpgsql;