1 -- ---------------------------------------------------------
4 -- list of types for type_id
11 -- 6: keyword (not used, uses prefixed string)
22 CREATE SEQUENCE value_id_seq
START WITH 3 -- skip nil, false, true
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
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
);
48 CREATE INDEX ON types.
value (value_id
, type_id
);
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
55 -- ---------------------------------------------------------
58 CREATE FUNCTION types.
_wraptf(val
boolean) RETURNS integer AS $$
65 END; $$
LANGUAGE plpgsql
IMMUTABLE;
67 -- pun both NULL and false to false
68 CREATE FUNCTION types.
_tf(val
boolean) RETURNS boolean AS $$
70 IF val
IS NULL OR val
= false THEN
74 END; $$
LANGUAGE plpgsql
IMMUTABLE;
76 -- pun both NULL and 0 to false
77 CREATE FUNCTION types.
_tf(val
integer) RETURNS boolean AS $$
79 IF val
IS NULL OR val
= 0 THEN
83 END; $$
LANGUAGE plpgsql
IMMUTABLE;
85 -- return the type of the given value_id
86 CREATE FUNCTION types.
_type(obj
integer) RETURNS integer AS $$
88 RETURN (SELECT type_id
FROM types.
value WHERE value_id
= obj
);
89 END; $$
LANGUAGE plpgsql
;
92 CREATE FUNCTION types.
_equal_Q(a
integer, b
integer) RETURNS boolean AS $$
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
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
;
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
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
)
128 IF NOT types.
_equal_Q(aseq
[i
], bseq
[i
]) THEN
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
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
150 END; $$
LANGUAGE plpgsql
;
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 $$
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
166 RETURNING value_id
INTO result;
168 END; $$
LANGUAGE plpgsql
;
171 -- ---------------------------------------------------------
177 -- returns the whether value_id is nil
178 CREATE FUNCTION types.
_nil_Q(id integer) RETURNS boolean AS $$
181 END; $$
LANGUAGE plpgsql
IMMUTABLE;
185 -- returns the whether value_id is true
186 CREATE FUNCTION types.
_true_Q(id integer) RETURNS boolean AS $$
189 END; $$
LANGUAGE plpgsql
IMMUTABLE;
193 -- returns the whether value_id is false
194 CREATE FUNCTION types.
_false_Q(id integer) RETURNS boolean AS $$
197 END; $$
LANGUAGE plpgsql
IMMUTABLE;
201 -- returns the whether value_id is string type
202 CREATE FUNCTION types.
_string_Q(id integer) RETURNS boolean AS $$
204 IF (SELECT 1 FROM types.
value WHERE type_id
= 5 AND value_id
= id) THEN
205 RETURN NOT types.
_keyword_Q(id);
208 END; $$
LANGUAGE plpgsql
;
212 -- takes a value_id for a string
213 -- returns the varchar value of the string
214 CREATE FUNCTION types.
_valueToString(sid
integer) RETURNS varchar AS $$
216 RETURN (SELECT val_string
FROM types.
value WHERE value_id
= sid
);
217 END; $$
LANGUAGE plpgsql
;
220 -- takes a varchar string
221 -- returns the value_id of a stringish type (string, symbol, keyword)
222 CREATE FUNCTION types.
_stringish(str
varchar, type integer) RETURNS integer AS $$
226 -- TODO: share string data between string types
227 -- lookup if it exists
228 SELECT value_id
FROM types.
value INTO result
229 WHERE val_string
= str
AND type_id
= type;
230 IF result IS NULL THEN
231 -- Create string entry
232 INSERT INTO types.
value (type_id
, val_string
)
234 RETURNING value_id
INTO result;
237 END; $$
LANGUAGE plpgsql
;
240 -- takes a varchar string
241 -- returns the value_id of a string (new or existing)
242 CREATE FUNCTION types.
_stringv(str
varchar) RETURNS integer AS $$
244 RETURN types.
_stringish(str
, 5);
245 END; $$
LANGUAGE plpgsql
;
248 -- takes a varchar string
249 -- returns the value_id of a keyword (new or existing)
250 CREATE FUNCTION types.
_keywordv(name varchar) RETURNS integer AS $$
252 RETURN types.
_stringish(chr(CAST(x
'7f' AS integer)) ||
name, 5);
253 END; $$
LANGUAGE plpgsql
;
257 -- returns the whether value_id is keyword type
258 CREATE FUNCTION types.
_keyword_Q(id integer) RETURNS boolean AS $$
262 IF (SELECT 1 FROM types.
value WHERE type_id
= 5 AND value_id
= id) THEN
263 str
:= types.
_valueToString(id);
264 IF char_length(str
) > 0 AND
265 chr(CAST(x
'7f' AS integer)) = substring(str
FROM 1 FOR 1) THEN
270 END; $$
LANGUAGE plpgsql
;
273 -- takes a varchar string
274 -- returns the value_id of a symbol (new or existing)
275 CREATE FUNCTION types.
_symbolv(name varchar) RETURNS integer AS $$
277 RETURN types.
_stringish(name, 7);
278 END; $$
LANGUAGE plpgsql
;
282 -- returns the whether value_id is symbol type
283 CREATE FUNCTION types.
_symbol_Q(id integer) RETURNS boolean AS $$
285 RETURN types.
_tf((SELECT 1 FROM types.
value
286 WHERE type_id
= 7 AND value_id
= id));
287 END; $$
LANGUAGE plpgsql
;
290 -- takes an bigint number
291 -- returns the value_id for the number
292 CREATE FUNCTION types.
_numToValue(num
bigint) RETURNS integer AS $$
296 SELECT value_id
FROM types.
value INTO result
297 WHERE val_int
= num
AND type_id
= 3;
298 IF result IS NULL THEN
299 -- Create an integer entry
300 INSERT INTO types.
value (type_id
, val_int
)
302 RETURNING value_id
INTO result;
305 END; $$
LANGUAGE plpgsql
;
307 -- ---------------------------------------------------------
308 -- sequence functions
311 -- return true if obj value_id is a list or vector
312 CREATE FUNCTION types.
_sequential_Q(obj
integer) RETURNS boolean AS $$
314 RETURN types.
_tf((SELECT 1 FROM types.
value
315 WHERE value_id
= obj
AND (type_id
= 8 OR type_id
= 9)));
316 END; $$
LANGUAGE plpgsql
;
319 -- takes a array of value_id integers
320 -- returns the value_id of a new list (8), vector (9) or hash-map (10)
321 CREATE FUNCTION types.
_collection(items
integer[], type integer) RETURNS integer AS $$
325 IF type IN (8, 9) THEN
326 INSERT INTO types.
value (type_id
, val_seq
)
328 RETURNING value_id
INTO vid
;
330 IF (array_length(items
, 1) % 2) = 1 THEN
331 RAISE
EXCEPTION 'hash-map: odd number of arguments';
333 INSERT INTO types.
value (type_id
, val_hash
)
334 VALUES (type, hstore(CAST(items
AS varchar[])))
335 RETURNING value_id
INTO vid
;
338 END; $$
LANGUAGE plpgsql
;
342 -- takes a array of value_id integers
343 -- returns the value_id of a new list
344 CREATE FUNCTION types.
_list(items
integer[]) RETURNS integer AS $$
346 RETURN types.
_collection(items
, 8);
347 END; $$
LANGUAGE plpgsql
;
350 -- takes a array of value_id integers
351 -- returns the value_id of a new list
352 CREATE FUNCTION types.
_vector(items
integer[]) RETURNS integer AS $$
354 RETURN types.
_collection(items
, 9);
355 END; $$
LANGUAGE plpgsql
;
358 -- return true if obj value_id is a list
359 CREATE FUNCTION types.
_list_Q(obj
integer) RETURNS boolean AS $$
361 RETURN types.
_tf((SELECT 1 FROM types.
value
362 WHERE value_id
= obj
and type_id
= 8));
363 END; $$
LANGUAGE plpgsql
;
366 -- return true if obj value_id is a list
367 CREATE FUNCTION types.
_vector_Q(obj
integer) RETURNS boolean AS $$
369 RETURN types.
_tf((SELECT 1 FROM types.
value
370 WHERE value_id
= obj
and type_id
= 9));
371 END; $$
LANGUAGE plpgsql
;
375 -- takes an value_id referring to a list or vector
376 -- returns an array of the value_ids from the list/vector
377 CREATE FUNCTION types.
_valueToArray(seq
integer) RETURNS integer[] AS $$
381 result := (SELECT val_seq
FROM types.
value WHERE value_id
= seq
);
382 IF result IS NULL THEN
383 result := ARRAY[]::integer[];
386 END; $$
LANGUAGE plpgsql
;
388 -- From: https://wiki.postgresql.org/wiki/Array_reverse
389 CREATE FUNCTION types.
array_reverse(a
integer[]) RETURNS integer[] AS $$
392 FROM generate_subscripts(a
,1) AS s(i
)
395 $$
LANGUAGE 'sql' STRICT IMMUTABLE;
399 -- takes value_id and an index
400 -- returns the value_id of nth element in list/vector
401 CREATE FUNCTION types.
_nth(seq_id
integer, indx
integer) RETURNS integer AS $$
405 RETURN (SELECT val_seq
[indx
+1] FROM types.
value WHERE value_id
= seq_id
);
406 END; $$
LANGUAGE plpgsql
;
410 -- returns the value_id of first element in list/vector
411 CREATE FUNCTION types.
_first(seq_id
integer) RETURNS integer AS $$
413 RETURN types.
_nth(seq_id
, 0);
414 END; $$
LANGUAGE plpgsql
;
419 -- returns the array of value_ids
420 CREATE FUNCTION types.
_restArray(seq_id
integer) RETURNS integer[] AS $$
424 result := (SELECT val_seq
FROM types.
value WHERE value_id
= seq_id
);
425 RETURN result[2:array_length(result, 1)];
426 END; $$
LANGUAGE plpgsql
;
429 -- takes value_id, a first index and an last index
430 -- returns the value_id of new list from first (inclusive) to last (exclusive)
431 CREATE FUNCTION types.
_slice(seq_id
integer, first integer, last integer)
432 RETURNS integer AS $$
439 SELECT val_seq
INTO seq
FROM types.
value WHERE value_id
= seq_id
;
440 INSERT INTO types.
value (type_id
, val_seq
)
441 VALUES (8, seq
[first+1:last])
442 RETURNING value_id
INTO result;
444 END; $$
LANGUAGE plpgsql
;
448 -- returns the value_id of new list
449 CREATE FUNCTION types.
_rest(seq_id
integer) RETURNS integer AS $$
451 RETURN types.
_slice(seq_id
, 1, types.
_count(seq_id
));
452 END; $$
LANGUAGE plpgsql
;
456 -- returns a count (not value_id)
457 CREATE FUNCTION types.
_count(seq_id
integer) RETURNS integer AS $$
461 result := (SELECT val_seq
FROM types.
value
462 WHERE value_id
= seq_id
);
463 RETURN COALESCE(array_length(result, 1), 0);
464 END; $$
LANGUAGE plpgsql
;
467 -- ---------------------------------------------------------
468 -- hash-map functions
471 -- return value_id of a new hash-map
472 CREATE FUNCTION types.
_hash_map(items
integer[]) RETURNS integer AS $$
474 RETURN types.
_collection(items
, 10);
475 END; $$
LANGUAGE plpgsql
;
478 -- return true if obj value_id is a list
479 CREATE FUNCTION types.
_hash_map_Q(obj
integer) RETURNS boolean AS $$
481 RETURN types.
_tf((SELECT 1 FROM types.
value
482 WHERE value_id
= obj
and type_id
= 10));
483 END; $$
LANGUAGE plpgsql
;
486 -- return value_id of the hash-map with new elements appended
487 CREATE FUNCTION types.
_assoc_BANG(hm
integer, items
integer[]) RETURNS integer AS $$
491 IF (array_length(items
, 1) % 2) = 1 THEN
492 RAISE
EXCEPTION 'hash-map: odd number of arguments';
494 SELECT val_hash
INTO hash
FROM types.
value WHERE value_id
= hm
;
496 UPDATE types.
value SET val_hash
= hstore(CAST(items
AS varchar[]))
500 SET val_hash
= hash ||
hstore(CAST(items
AS varchar[]))
504 END; $$
LANGUAGE plpgsql
;
507 -- return value_id of the hash-map with elements removed
508 CREATE FUNCTION types.
_dissoc_BANG(hm
integer, items
integer[]) RETURNS integer AS $$
512 SELECT val_hash
INTO hash
FROM types.
value WHERE value_id
= hm
;
513 UPDATE types.
value SET val_hash
= hash
- CAST(items
AS varchar[])
516 END; $$
LANGUAGE plpgsql
;
519 -- return value_id of the hash-map entry matching key
520 CREATE FUNCTION types.
_get(hm
integer, key varchar) RETURNS integer AS $$
524 SELECT val_hash
INTO hash
FROM types.
value WHERE value_id
= hm
;
525 RETURN hash
-> CAST(types.
_stringv(key) AS varchar);
526 END; $$
LANGUAGE plpgsql
;
529 -- return true if hash-map contains entry matching key
530 CREATE FUNCTION types.
_contains_Q(hm
integer, key varchar) RETURNS boolean AS $$
534 SELECT val_hash
INTO hash
FROM types.
value WHERE value_id
= hm
;
535 RETURN types.
_tf(hash ?
CAST(types.
_stringv(key) AS varchar));
536 END; $$
LANGUAGE plpgsql
;
539 -- return array of key value_ids from hash-map
540 CREATE FUNCTION types.
_keys(hm
integer) RETURNS integer[] AS $$
544 SELECT val_hash
INTO hash
FROM types.
value WHERE value_id
= hm
;
545 RETURN CAST(akeys(hash
) AS integer[]);
546 END; $$
LANGUAGE plpgsql
;
549 -- return array of value value_ids from hash-map
550 CREATE FUNCTION types.
_vals(hm
integer) RETURNS integer[] AS $$
554 SELECT val_hash
INTO hash
FROM types.
value WHERE value_id
= hm
;
555 RETURN CAST(avals(hash
) AS integer[]);
556 END; $$
LANGUAGE plpgsql
;
559 -- ---------------------------------------------------------
560 -- function functions
563 -- takes a function name
564 -- returns the value_id of a new
565 CREATE FUNCTION types.
_function(fname
varchar)
566 RETURNS varchar AS $$
570 INSERT INTO types.
value (type_id
, val_string
)
572 RETURNING value_id
INTO result;
573 RETURN CAST(result AS varchar);
574 END; $$
LANGUAGE plpgsql
;
577 -- takes a ast value_id, params value_id and env_id
578 -- returns the value_id of a new function
579 CREATE FUNCTION types.
_malfunc(ast
integer, params
integer, env
integer)
580 RETURNS integer AS $$
585 -- Create function entry
586 INSERT INTO types.
value (type_id
, ast_id
, params_id
, env_id
)
587 VALUES (12, ast
, params
, env
)
588 RETURNING value_id
into result;
590 END; $$
LANGUAGE plpgsql
;
593 CREATE FUNCTION types.
_macro(func
integer) RETURNS integer AS $$
598 newfunc
:= types.
_clone(func
);
599 UPDATE types.
value SET macro
= true WHERE value_id
= newfunc
;
601 END; $$
LANGUAGE plpgsql
;
603 CREATE FUNCTION types.
_apply(func
integer, args
integer[]) RETURNS integer AS $$
613 SELECT type_id
, val_string
, ast_id
, params_id
, env_id
614 INTO type, fname
, fast
, fparams
, fenv
615 FROM types.
value WHERE value_id
= func
;
617 EXECUTE format('SELECT %s($1);', fname
)
618 INTO result USING args
;
621 -- NOTE: forward reference to current step EVAL function
622 RETURN mal.
EVAL(fast
, envs.
new(fenv
, fparams
, args
));
624 RAISE
EXCEPTION 'Invalid function call';
626 END; $$
LANGUAGE plpgsql
;
628 -- ---------------------------------------------------------
632 -- takes an ast value_id
633 -- returns a new atom value_id
634 CREATE FUNCTION types.
_atom(val
integer) RETURNS integer AS $$
640 INSERT INTO types.
value (type_id
, val_seq
)
641 VALUES (13, ARRAY[val
])
642 RETURNING value_id
INTO result;
644 END; $$
LANGUAGE plpgsql
;
648 -- returns the whether value_id is an atom
649 CREATE FUNCTION types.
_atom_Q(id integer) RETURNS boolean AS $$
651 RETURN EXISTS(SELECT 1 FROM types.
value
652 WHERE type_id
= 13 AND value_id
= id);
653 END; $$
LANGUAGE plpgsql
;
656 -- takes an atom value_id
657 -- returns a atom value value_id
658 CREATE FUNCTION types.
_deref(atm
integer) RETURNS integer AS $$
662 RETURN (SELECT val_seq
[1] FROM types.
value WHERE value_id
= atm
);
663 END; $$
LANGUAGE plpgsql
;
666 -- takes an atom value_id and new value value_id
667 -- returns a new value value_id
668 CREATE FUNCTION types.
_reset_BANG(atm
integer, newval
integer) RETURNS integer AS $$
670 UPDATE types.
value SET val_seq
= ARRAY[newval
] WHERE value_id
= atm
;
672 END; $$
LANGUAGE plpgsql
;