DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / plpgsql / step2_eval.sql
1 -- ---------------------------------------------------------
2 -- step2_eval.sql
3
4 \i init.sql
5 \i io.sql
6 \i types.sql
7 \i reader.sql
8 \i printer.sql
9
10 -- ---------------------------------------------------------
11
12 CREATE SCHEMA mal;
13
14 -- read
15 CREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$
16 BEGIN
17 RETURN reader.read_str(line);
18 END; $$ LANGUAGE plpgsql;
19
20 -- eval
21 CREATE FUNCTION mal.eval_ast(ast integer, env hstore) RETURNS integer AS $$
22 DECLARE
23 type integer;
24 symkey varchar;
25 seq integer[];
26 eseq integer[];
27 hash hstore;
28 ehash hstore;
29 kv RECORD;
30 e integer;
31 result integer;
32 BEGIN
33 SELECT type_id INTO type FROM types.value WHERE value_id = ast;
34 CASE
35 WHEN type = 7 THEN
36 BEGIN
37 symkey := types._valueToString(ast);
38 IF env ? symkey THEN
39 result := env -> symkey;
40 ELSE
41 RAISE EXCEPTION '''%'' not found', symkey;
42 END IF;
43 END;
44 WHEN type IN (8, 9) THEN
45 BEGIN
46 SELECT val_seq INTO seq FROM types.value WHERE value_id = ast;
47 -- Evaluate each entry creating a new sequence
48 FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP
49 eseq[i] := mal.EVAL(seq[i], env);
50 END LOOP;
51 INSERT INTO types.value (type_id, val_seq) VALUES (type, eseq)
52 RETURNING value_id INTO result;
53 END;
54 WHEN type = 10 THEN
55 BEGIN
56 SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;
57 -- Evaluate each value for every key/value
58 FOR kv IN SELECT * FROM each(hash) LOOP
59 e := mal.EVAL(CAST(kv.value AS integer), env);
60 IF ehash IS NULL THEN
61 ehash := hstore(kv.key, CAST(e AS varchar));
62 ELSE
63 ehash := ehash || hstore(kv.key, CAST(e AS varchar));
64 END IF;
65 END LOOP;
66 INSERT INTO types.value (type_id, val_hash) VALUES (type, ehash)
67 RETURNING value_id INTO result;
68 END;
69 ELSE
70 result := ast;
71 END CASE;
72
73 RETURN result;
74 END; $$ LANGUAGE plpgsql;
75
76 CREATE FUNCTION mal.EVAL(ast integer, env hstore) RETURNS integer AS $$
77 DECLARE
78 type integer;
79 el integer;
80 fname varchar;
81 args integer[];
82 result integer;
83 BEGIN
84 SELECT type_id INTO type FROM types.value WHERE value_id = ast;
85 IF type <> 8 THEN
86 RETURN mal.eval_ast(ast, env);
87 END IF;
88 IF types._count(ast) = 0 THEN
89 RETURN ast;
90 END IF;
91
92 el := mal.eval_ast(ast, env);
93 SELECT val_string INTO fname FROM types.value
94 WHERE value_id = types._first(el);
95 args := types._restArray(el);
96 EXECUTE format('SELECT %s($1);', fname) INTO result USING args;
97 RETURN result;
98 END; $$ LANGUAGE plpgsql;
99
100 -- print
101 CREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$
102 BEGIN
103 RETURN printer.pr_str(exp);
104 END; $$ LANGUAGE plpgsql;
105
106
107 -- repl
108
109 CREATE FUNCTION mal.intop(op varchar, args integer[]) RETURNS integer AS $$
110 DECLARE a integer; b integer; result integer;
111 BEGIN
112 SELECT val_int INTO a FROM types.value WHERE value_id = args[1];
113 SELECT val_int INTO b FROM types.value WHERE value_id = args[2];
114 EXECUTE format('INSERT INTO types.value (type_id, val_int)
115 VALUES (3, $1 %s $2)
116 RETURNING value_id;', op) INTO result USING a, b;
117 RETURN result;
118 END; $$ LANGUAGE plpgsql;
119
120 CREATE FUNCTION mal.add(args integer[]) RETURNS integer AS $$
121 BEGIN RETURN mal.intop('+', args); END; $$ LANGUAGE plpgsql;
122 CREATE FUNCTION mal.subtract(args integer[]) RETURNS integer AS $$
123 BEGIN RETURN mal.intop('-', args); END; $$ LANGUAGE plpgsql;
124 CREATE FUNCTION mal.multiply(args integer[]) RETURNS integer AS $$
125 BEGIN RETURN mal.intop('*', args); END; $$ LANGUAGE plpgsql;
126 CREATE FUNCTION mal.divide(args integer[]) RETURNS integer AS $$
127 BEGIN RETURN mal.intop('/', args); END; $$ LANGUAGE plpgsql;
128
129
130 CREATE FUNCTION mal.REP(env hstore, line varchar) RETURNS varchar AS $$
131 BEGIN
132 RETURN mal.PRINT(mal.EVAL(mal.READ(line), env));
133 END; $$ LANGUAGE plpgsql;
134
135 CREATE FUNCTION mal.MAIN(pwd varchar) RETURNS integer AS $$
136 DECLARE
137 repl_env hstore;
138 line varchar;
139 output varchar;
140 BEGIN
141 repl_env := hstore(ARRAY[
142 '+', types._function('mal.add'),
143 '-', types._function('mal.subtract'),
144 '*', types._function('mal.multiply'),
145 '/', types._function('mal.divide')]);
146 WHILE true LOOP
147 BEGIN
148 line := io.readline('user> ', 0);
149 IF line IS NULL THEN
150 PERFORM io.close(1);
151 RETURN 0;
152 END IF;
153 IF line NOT IN ('', E'\n') THEN
154 output := mal.REP(repl_env, line);
155 PERFORM io.writeline(output);
156 END IF;
157
158 EXCEPTION WHEN OTHERS THEN
159 PERFORM io.writeline('Error: ' || SQLERRM);
160 END;
161 END LOOP;
162 END; $$ LANGUAGE plpgsql;