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