Remove gensym, inc and or from step files.
[jackhill/mal.git] / plpgsql / types.sql
1 -- ---------------------------------------------------------
2 -- persistent values
3
4 -- list of types for type_id
5 -- 0: nil
6 -- 1: false
7 -- 2: true
8 -- 3: integer
9 -- 4: float
10 -- 5: string
11 -- 6: keyword (not used, uses prefixed string)
12 -- 7: symbol
13 -- 8: list
14 -- 9: vector
15 -- 10: hashmap
16 -- 11: function
17 -- 12: malfunc
18 -- 13: atom
19
20 CREATE SCHEMA types
21
22 CREATE SEQUENCE value_id_seq START WITH 3 -- skip nil, false, true
23
24 CREATE TABLE value (
25 value_id integer NOT NULL DEFAULT nextval('value_id_seq'),
26 type_id integer NOT NULL,
27 val_int bigint, -- set for integers
28 val_string varchar, -- set for strings, keywords, symbols,
29 -- and native functions (function name)
30 val_seq integer[], -- set for lists and vectors
31 val_hash hstore, -- set for hash-maps
32 ast_id integer, -- set for malfunc
33 params_id integer, -- set for malfunc
34 env_id integer, -- set for malfunc
35 macro boolean, -- set for malfunc
36 meta_id integer -- can be set for any collection
37 );
38
39 ALTER TABLE types.value ADD CONSTRAINT pk_value_id
40 PRIMARY KEY (value_id);
41 -- drop sequence when table dropped
42 ALTER SEQUENCE types.value_id_seq OWNED BY types.value.value_id;
43 ALTER TABLE types.value ADD CONSTRAINT fk_meta_id
44 FOREIGN KEY (meta_id) REFERENCES types.value(value_id);
45 ALTER TABLE types.value ADD CONSTRAINT fk_params_id
46 FOREIGN KEY (params_id) REFERENCES types.value(value_id);
47
48 CREATE INDEX ON types.value (value_id, type_id);
49
50 INSERT INTO types.value (value_id, type_id) VALUES (0, 0); -- nil
51 INSERT INTO types.value (value_id, type_id) VALUES (1, 1); -- false
52 INSERT INTO types.value (value_id, type_id) VALUES (2, 2); -- true
53
54
55 -- ---------------------------------------------------------
56 -- general functions
57
58 CREATE FUNCTION types._wraptf(val boolean) RETURNS integer AS $$
59 BEGIN
60 IF val THEN
61 RETURN 2;
62 ELSE
63 RETURN 1;
64 END IF;
65 END; $$ LANGUAGE plpgsql IMMUTABLE;
66
67 -- pun both NULL and false to false
68 CREATE FUNCTION types._tf(val boolean) RETURNS boolean AS $$
69 BEGIN
70 IF val IS NULL OR val = false THEN
71 RETURN false;
72 END IF;
73 RETURN true;
74 END; $$ LANGUAGE plpgsql IMMUTABLE;
75
76 -- pun both NULL and 0 to false
77 CREATE FUNCTION types._tf(val integer) RETURNS boolean AS $$
78 BEGIN
79 IF val IS NULL OR val = 0 THEN
80 RETURN false;
81 END IF;
82 RETURN true;
83 END; $$ LANGUAGE plpgsql IMMUTABLE;
84
85 -- return the type of the given value_id
86 CREATE FUNCTION types._type(obj integer) RETURNS integer AS $$
87 BEGIN
88 RETURN (SELECT type_id FROM types.value WHERE value_id = obj);
89 END; $$ LANGUAGE plpgsql;
90
91
92 CREATE FUNCTION types._equal_Q(a integer, b integer) RETURNS boolean AS $$
93 DECLARE
94 atype integer;
95 btype integer;
96 anum bigint;
97 bnum bigint;
98 avid integer;
99 bvid integer;
100 aseq integer[];
101 bseq integer[];
102 ahash hstore;
103 bhash hstore;
104 kv RECORD;
105 i integer;
106 BEGIN
107 atype := types._type(a);
108 btype := types._type(b);
109 IF NOT ((atype = btype) OR
110 (types._sequential_Q(a) AND types._sequential_Q(b))) THEN
111 RETURN false;
112 END IF;
113 CASE
114 WHEN atype = 3 THEN -- integer
115 SELECT val_int FROM types.value INTO anum WHERE value_id = a;
116 SELECT val_int FROM types.value INTO bnum WHERE value_id = b;
117 RETURN anum = bnum;
118 WHEN atype = 5 OR atype = 7 THEN -- string/symbol
119 RETURN types._valueToString(a) = types._valueToString(b);
120 WHEN atype IN (8, 9) THEN -- list/vector
121 IF types._count(a) <> types._count(b) THEN
122 RETURN false;
123 END IF;
124 SELECT val_seq INTO aseq FROM types.value WHERE value_id = a;
125 SELECT val_seq INTO bseq FROM types.value WHERE value_id = b;
126 FOR i IN 1 .. types._count(a)
127 LOOP
128 IF NOT types._equal_Q(aseq[i], bseq[i]) THEN
129 return false;
130 END IF;
131 END LOOP;
132 RETURN true;
133 WHEN atype = 10 THEN -- hash-map
134 SELECT val_hash INTO ahash FROM types.value WHERE value_id = a;
135 SELECT val_hash INTO bhash FROM types.value WHERE value_id = b;
136 IF array_length(akeys(ahash), 1) <> array_length(akeys(bhash), 1) THEN
137 RETURN false;
138 END IF;
139 FOR kv IN SELECT * FROM each(ahash) LOOP
140 avid := CAST((ahash -> kv.key) AS integer);
141 bvid := CAST((bhash -> kv.key) AS integer);
142 IF bvid IS NULL OR NOT types._equal_Q(avid, bvid) THEN
143 return false;
144 END IF;
145 END LOOP;
146 RETURN true;
147 ELSE
148 RETURN a = b;
149 END CASE;
150 END; $$ LANGUAGE plpgsql;
151
152
153 -- _clone:
154 -- take a value_id of a collection
155 -- returns a new value_id of a cloned collection
156 CREATE FUNCTION types._clone(id integer) RETURNS integer AS $$
157 DECLARE
158 result integer;
159 BEGIN
160 INSERT INTO types.value (type_id,val_int,val_string,val_seq,val_hash,
161 ast_id,params_id,env_id,meta_id)
162 (SELECT type_id,val_int,val_string,val_seq,val_hash,
163 ast_id,params_id,env_id,meta_id
164 FROM types.value
165 WHERE value_id = id)
166 RETURNING value_id INTO result;
167 RETURN result;
168 END; $$ LANGUAGE plpgsql;
169
170
171 -- ---------------------------------------------------------
172 -- scalar functions
173
174
175 -- _nil_Q:
176 -- takes a value_id
177 -- returns the whether value_id is nil
178 CREATE FUNCTION types._nil_Q(id integer) RETURNS boolean AS $$
179 BEGIN
180 RETURN id = 0;
181 END; $$ LANGUAGE plpgsql IMMUTABLE;
182
183 -- _true_Q:
184 -- takes a value_id
185 -- returns the whether value_id is true
186 CREATE FUNCTION types._true_Q(id integer) RETURNS boolean AS $$
187 BEGIN
188 RETURN id = 2;
189 END; $$ LANGUAGE plpgsql IMMUTABLE;
190
191 -- _false_Q:
192 -- takes a value_id
193 -- returns the whether value_id is false
194 CREATE FUNCTION types._false_Q(id integer) RETURNS boolean AS $$
195 BEGIN
196 RETURN id = 1;
197 END; $$ LANGUAGE plpgsql IMMUTABLE;
198
199 -- _string_Q:
200 -- takes a value_id
201 -- returns the whether value_id is string type
202 CREATE FUNCTION types._string_Q(id integer) RETURNS boolean AS $$
203 BEGIN
204 IF (SELECT 1 FROM types.value WHERE type_id = 5 AND value_id = id) THEN
205 RETURN NOT types._keyword_Q(id);
206 END IF;
207 RETURN false;
208 END; $$ LANGUAGE plpgsql;
209
210 -- _number_Q:
211 -- takes a value_id
212 -- returns the whether value_id is integer or float type
213 CREATE FUNCTION types._number_Q(id integer) RETURNS boolean AS $$
214 BEGIN
215 RETURN types._tf((SELECT 1 FROM types.value
216 WHERE (type_id = 3 OR type_id = 4)
217 AND value_id = id));
218 END; $$ LANGUAGE plpgsql;
219
220 -- _valueToString:
221 -- takes a value_id for a string
222 -- returns the varchar value of the string
223 CREATE FUNCTION types._valueToString(sid integer) RETURNS varchar AS $$
224 BEGIN
225 RETURN (SELECT val_string FROM types.value WHERE value_id = sid);
226 END; $$ LANGUAGE plpgsql;
227
228 -- _stringish:
229 -- takes a varchar string
230 -- returns the value_id of a stringish type (string, symbol, keyword)
231 CREATE FUNCTION types._stringish(str varchar, type integer) RETURNS integer AS $$
232 DECLARE
233 result integer;
234 BEGIN
235 -- TODO: share string data between string types
236 -- lookup if it exists
237 SELECT value_id FROM types.value INTO result
238 WHERE val_string = str AND type_id = type;
239 IF result IS NULL THEN
240 -- Create string entry
241 INSERT INTO types.value (type_id, val_string)
242 VALUES (type, str)
243 RETURNING value_id INTO result;
244 END IF;
245 RETURN result;
246 END; $$ LANGUAGE plpgsql;
247
248 -- _stringv:
249 -- takes a varchar string
250 -- returns the value_id of a string (new or existing)
251 CREATE FUNCTION types._stringv(str varchar) RETURNS integer AS $$
252 BEGIN
253 RETURN types._stringish(str, 5);
254 END; $$ LANGUAGE plpgsql;
255
256 -- _keywordv:
257 -- takes a varchar string
258 -- returns the value_id of a keyword (new or existing)
259 CREATE FUNCTION types._keywordv(name varchar) RETURNS integer AS $$
260 BEGIN
261 RETURN types._stringish(chr(CAST(x'7f' AS integer)) || name, 5);
262 END; $$ LANGUAGE plpgsql;
263
264 -- _keyword_Q:
265 -- takes a value_id
266 -- returns the whether value_id is keyword type
267 CREATE FUNCTION types._keyword_Q(id integer) RETURNS boolean AS $$
268 DECLARE
269 str varchar;
270 BEGIN
271 IF (SELECT 1 FROM types.value WHERE type_id = 5 AND value_id = id) THEN
272 str := types._valueToString(id);
273 IF char_length(str) > 0 AND
274 chr(CAST(x'7f' AS integer)) = substring(str FROM 1 FOR 1) THEN
275 RETURN true;
276 END IF;
277 END IF;
278 RETURN false;
279 END; $$ LANGUAGE plpgsql;
280
281 -- _symbolv:
282 -- takes a varchar string
283 -- returns the value_id of a symbol (new or existing)
284 CREATE FUNCTION types._symbolv(name varchar) RETURNS integer AS $$
285 BEGIN
286 RETURN types._stringish(name, 7);
287 END; $$ LANGUAGE plpgsql;
288
289 -- _symbol_Q:
290 -- takes a value_id
291 -- returns the whether value_id is symbol type
292 CREATE FUNCTION types._symbol_Q(id integer) RETURNS boolean AS $$
293 BEGIN
294 RETURN types._tf((SELECT 1 FROM types.value
295 WHERE type_id = 7 AND value_id = id));
296 END; $$ LANGUAGE plpgsql;
297
298 -- _numToValue:
299 -- takes an bigint number
300 -- returns the value_id for the number
301 CREATE FUNCTION types._numToValue(num bigint) RETURNS integer AS $$
302 DECLARE
303 result integer;
304 BEGIN
305 SELECT value_id FROM types.value INTO result
306 WHERE val_int = num AND type_id = 3;
307 IF result IS NULL THEN
308 -- Create an integer entry
309 INSERT INTO types.value (type_id, val_int)
310 VALUES (3, num)
311 RETURNING value_id INTO result;
312 END IF;
313 RETURN result;
314 END; $$ LANGUAGE plpgsql;
315
316 -- _fn_Q:
317 -- takes a value_id
318 -- returns the whether value_id is a function
319 CREATE FUNCTION types._fn_Q(id integer) RETURNS boolean AS $$
320 BEGIN
321 RETURN types._tf((SELECT 1 FROM types.value
322 WHERE (type_id = 11 OR type_id = 12)
323 AND macro IS NULL
324 AND value_id = id));
325 END; $$ LANGUAGE plpgsql;
326
327 -- _macro_Q:
328 -- takes a value_id
329 -- returns the whether value_id is a macro
330 CREATE FUNCTION types._macro_Q(id integer) RETURNS boolean AS $$
331 BEGIN
332 RETURN types._tf((SELECT 1 FROM types.value
333 WHERE type_id = 12
334 AND macro IS TRUE
335 AND value_id = id));
336 END; $$ LANGUAGE plpgsql;
337
338 -- ---------------------------------------------------------
339 -- sequence functions
340
341 -- _sequential_Q:
342 -- return true if obj value_id is a list or vector
343 CREATE FUNCTION types._sequential_Q(obj integer) RETURNS boolean AS $$
344 BEGIN
345 RETURN types._tf((SELECT 1 FROM types.value
346 WHERE value_id = obj AND (type_id = 8 OR type_id = 9)));
347 END; $$ LANGUAGE plpgsql;
348
349 -- _collection:
350 -- takes a array of value_id integers
351 -- returns the value_id of a new list (8), vector (9) or hash-map (10)
352 CREATE FUNCTION types._collection(items integer[], type integer) RETURNS integer AS $$
353 DECLARE
354 vid integer;
355 BEGIN
356 IF type IN (8, 9) THEN
357 INSERT INTO types.value (type_id, val_seq)
358 VALUES (type, items)
359 RETURNING value_id INTO vid;
360 ELSIF type = 10 THEN
361 IF (array_length(items, 1) % 2) = 1 THEN
362 RAISE EXCEPTION 'hash-map: odd number of arguments';
363 END IF;
364 INSERT INTO types.value (type_id, val_hash)
365 VALUES (type, hstore(CAST(items AS varchar[])))
366 RETURNING value_id INTO vid;
367 END IF;
368 RETURN vid;
369 END; $$ LANGUAGE plpgsql;
370
371
372 -- _list:
373 -- takes a array of value_id integers
374 -- returns the value_id of a new list
375 CREATE FUNCTION types._list(items integer[]) RETURNS integer AS $$
376 BEGIN
377 RETURN types._collection(items, 8);
378 END; $$ LANGUAGE plpgsql;
379
380 -- _vector:
381 -- takes a array of value_id integers
382 -- returns the value_id of a new list
383 CREATE FUNCTION types._vector(items integer[]) RETURNS integer AS $$
384 BEGIN
385 RETURN types._collection(items, 9);
386 END; $$ LANGUAGE plpgsql;
387
388 -- _list_Q:
389 -- return true if obj value_id is a list
390 CREATE FUNCTION types._list_Q(obj integer) RETURNS boolean AS $$
391 BEGIN
392 RETURN types._tf((SELECT 1 FROM types.value
393 WHERE value_id = obj and type_id = 8));
394 END; $$ LANGUAGE plpgsql;
395
396 -- _vector_Q:
397 -- return true if obj value_id is a list
398 CREATE FUNCTION types._vector_Q(obj integer) RETURNS boolean AS $$
399 BEGIN
400 RETURN types._tf((SELECT 1 FROM types.value
401 WHERE value_id = obj and type_id = 9));
402 END; $$ LANGUAGE plpgsql;
403
404
405 -- _valueToArray:
406 -- takes an value_id referring to a list or vector
407 -- returns an array of the value_ids from the list/vector
408 CREATE FUNCTION types._valueToArray(seq integer) RETURNS integer[] AS $$
409 DECLARE
410 result integer[];
411 BEGIN
412 result := (SELECT val_seq FROM types.value WHERE value_id = seq);
413 IF result IS NULL THEN
414 result := ARRAY[]::integer[];
415 END IF;
416 RETURN result;
417 END; $$ LANGUAGE plpgsql;
418
419 -- From: https://wiki.postgresql.org/wiki/Array_reverse
420 CREATE FUNCTION types.array_reverse(a integer[]) RETURNS integer[] AS $$
421 SELECT ARRAY(
422 SELECT a[i]
423 FROM generate_subscripts(a,1) AS s(i)
424 ORDER BY i DESC
425 );
426 $$ LANGUAGE 'sql' STRICT IMMUTABLE;
427
428
429 -- _nth:
430 -- takes value_id and an index
431 -- returns the value_id of nth element in list/vector
432 CREATE FUNCTION types._nth(seq_id integer, indx integer) RETURNS integer AS $$
433 DECLARE
434 result integer;
435 BEGIN
436 RETURN (SELECT val_seq[indx+1] FROM types.value WHERE value_id = seq_id);
437 END; $$ LANGUAGE plpgsql;
438
439 -- _first:
440 -- takes value_id
441 -- returns the value_id of first element in list/vector
442 CREATE FUNCTION types._first(seq_id integer) RETURNS integer AS $$
443 BEGIN
444 RETURN types._nth(seq_id, 0);
445 END; $$ LANGUAGE plpgsql;
446
447
448 -- _restArray:
449 -- takes value_id
450 -- returns the array of value_ids
451 CREATE FUNCTION types._restArray(seq_id integer) RETURNS integer[] AS $$
452 DECLARE
453 result integer[];
454 BEGIN
455 result := (SELECT val_seq FROM types.value WHERE value_id = seq_id);
456 RETURN result[2:array_length(result, 1)];
457 END; $$ LANGUAGE plpgsql;
458
459 -- _slice:
460 -- takes value_id, a first index and an last index
461 -- returns the value_id of new list from first (inclusive) to last (exclusive)
462 CREATE FUNCTION types._slice(seq_id integer, first integer, last integer)
463 RETURNS integer AS $$
464 DECLARE
465 seq integer[];
466 vid integer;
467 i integer;
468 result integer;
469 BEGIN
470 SELECT val_seq INTO seq FROM types.value WHERE value_id = seq_id;
471 INSERT INTO types.value (type_id, val_seq)
472 VALUES (8, seq[first+1:last])
473 RETURNING value_id INTO result;
474 RETURN result;
475 END; $$ LANGUAGE plpgsql;
476
477 -- _rest:
478 -- takes value_id
479 -- returns the value_id of new list
480 CREATE FUNCTION types._rest(seq_id integer) RETURNS integer AS $$
481 BEGIN
482 RETURN types._slice(seq_id, 1, types._count(seq_id));
483 END; $$ LANGUAGE plpgsql;
484
485 -- _count:
486 -- takes value_id
487 -- returns a count (not value_id)
488 CREATE FUNCTION types._count(seq_id integer) RETURNS integer AS $$
489 DECLARE
490 result integer[];
491 BEGIN
492 result := (SELECT val_seq FROM types.value
493 WHERE value_id = seq_id);
494 RETURN COALESCE(array_length(result, 1), 0);
495 END; $$ LANGUAGE plpgsql;
496
497
498 -- ---------------------------------------------------------
499 -- hash-map functions
500
501 -- _hash_map:
502 -- return value_id of a new hash-map
503 CREATE FUNCTION types._hash_map(items integer[]) RETURNS integer AS $$
504 BEGIN
505 RETURN types._collection(items, 10);
506 END; $$ LANGUAGE plpgsql;
507
508 -- _hash_map_Q:
509 -- return true if obj value_id is a list
510 CREATE FUNCTION types._hash_map_Q(obj integer) RETURNS boolean AS $$
511 BEGIN
512 RETURN types._tf((SELECT 1 FROM types.value
513 WHERE value_id = obj and type_id = 10));
514 END; $$ LANGUAGE plpgsql;
515
516 -- _assoc_BANG:
517 -- return value_id of the hash-map with new elements appended
518 CREATE FUNCTION types._assoc_BANG(hm integer, items integer[]) RETURNS integer AS $$
519 DECLARE
520 hash hstore;
521 BEGIN
522 IF (array_length(items, 1) % 2) = 1 THEN
523 RAISE EXCEPTION 'hash-map: odd number of arguments';
524 END IF;
525 SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;
526 IF hash IS NULL THEN
527 UPDATE types.value SET val_hash = hstore(CAST(items AS varchar[]))
528 WHERE value_id = hm;
529 ELSE
530 UPDATE types.value
531 SET val_hash = hash || hstore(CAST(items AS varchar[]))
532 WHERE value_id = hm;
533 END IF;
534 RETURN hm;
535 END; $$ LANGUAGE plpgsql;
536
537 -- _dissoc_BANG:
538 -- return value_id of the hash-map with elements removed
539 CREATE FUNCTION types._dissoc_BANG(hm integer, items integer[]) RETURNS integer AS $$
540 DECLARE
541 hash hstore;
542 BEGIN
543 SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;
544 UPDATE types.value SET val_hash = hash - CAST(items AS varchar[])
545 WHERE value_id = hm;
546 RETURN hm;
547 END; $$ LANGUAGE plpgsql;
548
549 -- _get:
550 -- return value_id of the hash-map entry matching key
551 CREATE FUNCTION types._get(hm integer, key varchar) RETURNS integer AS $$
552 DECLARE
553 hash hstore;
554 BEGIN
555 SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;
556 RETURN hash -> CAST(types._stringv(key) AS varchar);
557 END; $$ LANGUAGE plpgsql;
558
559 -- _contains_Q:
560 -- return true if hash-map contains entry matching key
561 CREATE FUNCTION types._contains_Q(hm integer, key varchar) RETURNS boolean AS $$
562 DECLARE
563 hash hstore;
564 BEGIN
565 SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;
566 RETURN types._tf(hash ? CAST(types._stringv(key) AS varchar));
567 END; $$ LANGUAGE plpgsql;
568
569 -- _keys:
570 -- return array of key value_ids from hash-map
571 CREATE FUNCTION types._keys(hm integer) RETURNS integer[] AS $$
572 DECLARE
573 hash hstore;
574 BEGIN
575 SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;
576 RETURN CAST(akeys(hash) AS integer[]);
577 END; $$ LANGUAGE plpgsql;
578
579 -- _vals:
580 -- return array of value value_ids from hash-map
581 CREATE FUNCTION types._vals(hm integer) RETURNS integer[] AS $$
582 DECLARE
583 hash hstore;
584 BEGIN
585 SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;
586 RETURN CAST(avals(hash) AS integer[]);
587 END; $$ LANGUAGE plpgsql;
588
589
590 -- ---------------------------------------------------------
591 -- function functions
592
593 -- _function:
594 -- takes a function name
595 -- returns the value_id of a new
596 CREATE FUNCTION types._function(fname varchar)
597 RETURNS varchar AS $$
598 DECLARE
599 result integer;
600 BEGIN
601 INSERT INTO types.value (type_id, val_string)
602 VALUES (11, fname)
603 RETURNING value_id INTO result;
604 RETURN CAST(result AS varchar);
605 END; $$ LANGUAGE plpgsql;
606
607 -- _malfunc:
608 -- takes a ast value_id, params value_id and env_id
609 -- returns the value_id of a new function
610 CREATE FUNCTION types._malfunc(ast integer, params integer, env integer)
611 RETURNS integer AS $$
612 DECLARE
613 cid integer = NULL;
614 result integer;
615 BEGIN
616 -- Create function entry
617 INSERT INTO types.value (type_id, ast_id, params_id, env_id)
618 VALUES (12, ast, params, env)
619 RETURNING value_id into result;
620 RETURN result;
621 END; $$ LANGUAGE plpgsql;
622
623 -- _macro:
624 CREATE FUNCTION types._macro(func integer) RETURNS integer AS $$
625 DECLARE
626 newfunc integer;
627 cid integer;
628 BEGIN
629 newfunc := types._clone(func);
630 UPDATE types.value SET macro = true WHERE value_id = newfunc;
631 RETURN newfunc;
632 END; $$ LANGUAGE plpgsql;
633
634 CREATE FUNCTION types._apply(func integer, args integer[]) RETURNS integer AS $$
635 DECLARE
636 type integer;
637 fcid integer;
638 fname varchar;
639 fast integer;
640 fparams integer;
641 fenv integer;
642 result integer;
643 BEGIN
644 SELECT type_id, val_string, ast_id, params_id, env_id
645 INTO type, fname, fast, fparams, fenv
646 FROM types.value WHERE value_id = func;
647 IF type = 11 THEN
648 EXECUTE format('SELECT %s($1);', fname)
649 INTO result USING args;
650 RETURN result;
651 ELSIF type = 12 THEN
652 -- NOTE: forward reference to current step EVAL function
653 RETURN mal.EVAL(fast, envs.new(fenv, fparams, args));
654 ELSE
655 RAISE EXCEPTION 'Invalid function call';
656 END IF;
657 END; $$ LANGUAGE plpgsql;
658
659 -- ---------------------------------------------------------
660 -- atom functions
661
662 -- _atom:
663 -- takes an ast value_id
664 -- returns a new atom value_id
665 CREATE FUNCTION types._atom(val integer) RETURNS integer AS $$
666 DECLARE
667 cid integer = NULL;
668 result integer;
669 BEGIN
670 -- Create atom
671 INSERT INTO types.value (type_id, val_seq)
672 VALUES (13, ARRAY[val])
673 RETURNING value_id INTO result;
674 RETURN result;
675 END; $$ LANGUAGE plpgsql;
676
677 -- _atom_Q:
678 -- takes a value_id
679 -- returns the whether value_id is an atom
680 CREATE FUNCTION types._atom_Q(id integer) RETURNS boolean AS $$
681 BEGIN
682 RETURN EXISTS(SELECT 1 FROM types.value
683 WHERE type_id = 13 AND value_id = id);
684 END; $$ LANGUAGE plpgsql;
685
686 -- _deref:
687 -- takes an atom value_id
688 -- returns a atom value value_id
689 CREATE FUNCTION types._deref(atm integer) RETURNS integer AS $$
690 DECLARE
691 result integer;
692 BEGIN
693 RETURN (SELECT val_seq[1] FROM types.value WHERE value_id = atm);
694 END; $$ LANGUAGE plpgsql;
695
696 -- _reset_BANG:
697 -- takes an atom value_id and new value value_id
698 -- returns a new value value_id
699 CREATE FUNCTION types._reset_BANG(atm integer, newval integer) RETURNS integer AS $$
700 BEGIN
701 UPDATE types.value SET val_seq = ARRAY[newval] WHERE value_id = atm;
702 RETURN newval;
703 END; $$ LANGUAGE plpgsql;