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 -- returns the whether value_id is integer or float type
213 CREATE FUNCTION types.
_number_Q(id integer) RETURNS boolean AS $$
215 RETURN types.
_tf((SELECT 1 FROM types.
value
216 WHERE (type_id
= 3 OR type_id
= 4)
218 END; $$
LANGUAGE plpgsql
;
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 $$
225 RETURN (SELECT val_string
FROM types.
value WHERE value_id
= sid
);
226 END; $$
LANGUAGE plpgsql
;
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 $$
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
)
243 RETURNING value_id
INTO result;
246 END; $$
LANGUAGE plpgsql
;
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 $$
253 RETURN types.
_stringish(str
, 5);
254 END; $$
LANGUAGE plpgsql
;
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 $$
261 RETURN types.
_stringish(chr(CAST(x
'7f' AS integer)) ||
name, 5);
262 END; $$
LANGUAGE plpgsql
;
266 -- returns the whether value_id is keyword type
267 CREATE FUNCTION types.
_keyword_Q(id integer) RETURNS boolean AS $$
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
279 END; $$
LANGUAGE plpgsql
;
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 $$
286 RETURN types.
_stringish(name, 7);
287 END; $$
LANGUAGE plpgsql
;
291 -- returns the whether value_id is symbol type
292 CREATE FUNCTION types.
_symbol_Q(id integer) RETURNS boolean AS $$
294 RETURN types.
_tf((SELECT 1 FROM types.
value
295 WHERE type_id
= 7 AND value_id
= id));
296 END; $$
LANGUAGE plpgsql
;
299 -- takes an bigint number
300 -- returns the value_id for the number
301 CREATE FUNCTION types.
_numToValue(num
bigint) RETURNS integer AS $$
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
)
311 RETURNING value_id
INTO result;
314 END; $$
LANGUAGE plpgsql
;
318 -- returns the whether value_id is a function
319 CREATE FUNCTION types.
_fn_Q(id integer) RETURNS boolean AS $$
321 RETURN types.
_tf((SELECT 1 FROM types.
value
322 WHERE (type_id
= 11 OR type_id
= 12)
325 END; $$
LANGUAGE plpgsql
;
329 -- returns the whether value_id is a macro
330 CREATE FUNCTION types.
_macro_Q(id integer) RETURNS boolean AS $$
332 RETURN types.
_tf((SELECT 1 FROM types.
value
336 END; $$
LANGUAGE plpgsql
;
338 -- ---------------------------------------------------------
339 -- sequence functions
342 -- return true if obj value_id is a list or vector
343 CREATE FUNCTION types.
_sequential_Q(obj
integer) RETURNS boolean AS $$
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
;
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 $$
356 IF type IN (8, 9) THEN
357 INSERT INTO types.
value (type_id
, val_seq
)
359 RETURNING value_id
INTO vid
;
361 IF (array_length(items
, 1) % 2) = 1 THEN
362 RAISE
EXCEPTION 'hash-map: odd number of arguments';
364 INSERT INTO types.
value (type_id
, val_hash
)
365 VALUES (type, hstore(CAST(items
AS varchar[])))
366 RETURNING value_id
INTO vid
;
369 END; $$
LANGUAGE plpgsql
;
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 $$
377 RETURN types.
_collection(items
, 8);
378 END; $$
LANGUAGE plpgsql
;
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 $$
385 RETURN types.
_collection(items
, 9);
386 END; $$
LANGUAGE plpgsql
;
389 -- return true if obj value_id is a list
390 CREATE FUNCTION types.
_list_Q(obj
integer) RETURNS boolean AS $$
392 RETURN types.
_tf((SELECT 1 FROM types.
value
393 WHERE value_id
= obj
and type_id
= 8));
394 END; $$
LANGUAGE plpgsql
;
397 -- return true if obj value_id is a list
398 CREATE FUNCTION types.
_vector_Q(obj
integer) RETURNS boolean AS $$
400 RETURN types.
_tf((SELECT 1 FROM types.
value
401 WHERE value_id
= obj
and type_id
= 9));
402 END; $$
LANGUAGE plpgsql
;
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 $$
412 result := (SELECT val_seq
FROM types.
value WHERE value_id
= seq
);
413 IF result IS NULL THEN
414 result := ARRAY[]::integer[];
417 END; $$
LANGUAGE plpgsql
;
419 -- From: https://wiki.postgresql.org/wiki/Array_reverse
420 CREATE FUNCTION types.
array_reverse(a
integer[]) RETURNS integer[] AS $$
423 FROM generate_subscripts(a
,1) AS s(i
)
426 $$
LANGUAGE 'sql' STRICT IMMUTABLE;
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 $$
436 RETURN (SELECT val_seq
[indx
+1] FROM types.
value WHERE value_id
= seq_id
);
437 END; $$
LANGUAGE plpgsql
;
441 -- returns the value_id of first element in list/vector
442 CREATE FUNCTION types.
_first(seq_id
integer) RETURNS integer AS $$
444 RETURN types.
_nth(seq_id
, 0);
445 END; $$
LANGUAGE plpgsql
;
450 -- returns the array of value_ids
451 CREATE FUNCTION types.
_restArray(seq_id
integer) RETURNS integer[] AS $$
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
;
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 $$
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;
475 END; $$
LANGUAGE plpgsql
;
479 -- returns the value_id of new list
480 CREATE FUNCTION types.
_rest(seq_id
integer) RETURNS integer AS $$
482 RETURN types.
_slice(seq_id
, 1, types.
_count(seq_id
));
483 END; $$
LANGUAGE plpgsql
;
487 -- returns a count (not value_id)
488 CREATE FUNCTION types.
_count(seq_id
integer) RETURNS integer AS $$
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
;
498 -- ---------------------------------------------------------
499 -- hash-map functions
502 -- return value_id of a new hash-map
503 CREATE FUNCTION types.
_hash_map(items
integer[]) RETURNS integer AS $$
505 RETURN types.
_collection(items
, 10);
506 END; $$
LANGUAGE plpgsql
;
509 -- return true if obj value_id is a list
510 CREATE FUNCTION types.
_hash_map_Q(obj
integer) RETURNS boolean AS $$
512 RETURN types.
_tf((SELECT 1 FROM types.
value
513 WHERE value_id
= obj
and type_id
= 10));
514 END; $$
LANGUAGE plpgsql
;
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 $$
522 IF (array_length(items
, 1) % 2) = 1 THEN
523 RAISE
EXCEPTION 'hash-map: odd number of arguments';
525 SELECT val_hash
INTO hash
FROM types.
value WHERE value_id
= hm
;
527 UPDATE types.
value SET val_hash
= hstore(CAST(items
AS varchar[]))
531 SET val_hash
= hash ||
hstore(CAST(items
AS varchar[]))
535 END; $$
LANGUAGE plpgsql
;
538 -- return value_id of the hash-map with elements removed
539 CREATE FUNCTION types.
_dissoc_BANG(hm
integer, items
integer[]) RETURNS integer AS $$
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[])
547 END; $$
LANGUAGE plpgsql
;
550 -- return value_id of the hash-map entry matching key
551 CREATE FUNCTION types.
_get(hm
integer, key varchar) RETURNS integer AS $$
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
;
560 -- return true if hash-map contains entry matching key
561 CREATE FUNCTION types.
_contains_Q(hm
integer, key varchar) RETURNS boolean AS $$
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
;
570 -- return array of key value_ids from hash-map
571 CREATE FUNCTION types.
_keys(hm
integer) RETURNS integer[] AS $$
575 SELECT val_hash
INTO hash
FROM types.
value WHERE value_id
= hm
;
576 RETURN CAST(akeys(hash
) AS integer[]);
577 END; $$
LANGUAGE plpgsql
;
580 -- return array of value value_ids from hash-map
581 CREATE FUNCTION types.
_vals(hm
integer) RETURNS integer[] AS $$
585 SELECT val_hash
INTO hash
FROM types.
value WHERE value_id
= hm
;
586 RETURN CAST(avals(hash
) AS integer[]);
587 END; $$
LANGUAGE plpgsql
;
590 -- ---------------------------------------------------------
591 -- function functions
594 -- takes a function name
595 -- returns the value_id of a new
596 CREATE FUNCTION types.
_function(fname
varchar)
597 RETURNS varchar AS $$
601 INSERT INTO types.
value (type_id
, val_string
)
603 RETURNING value_id
INTO result;
604 RETURN CAST(result AS varchar);
605 END; $$
LANGUAGE plpgsql
;
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 $$
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;
621 END; $$
LANGUAGE plpgsql
;
624 CREATE FUNCTION types.
_macro(func
integer) RETURNS integer AS $$
629 newfunc
:= types.
_clone(func
);
630 UPDATE types.
value SET macro
= true WHERE value_id
= newfunc
;
632 END; $$
LANGUAGE plpgsql
;
634 CREATE FUNCTION types.
_apply(func
integer, args
integer[]) RETURNS integer AS $$
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
;
648 EXECUTE format('SELECT %s($1);', fname
)
649 INTO result USING args
;
652 -- NOTE: forward reference to current step EVAL function
653 RETURN mal.
EVAL(fast
, envs.
new(fenv
, fparams
, args
));
655 RAISE
EXCEPTION 'Invalid function call';
657 END; $$
LANGUAGE plpgsql
;
659 -- ---------------------------------------------------------
663 -- takes an ast value_id
664 -- returns a new atom value_id
665 CREATE FUNCTION types.
_atom(val
integer) RETURNS integer AS $$
671 INSERT INTO types.
value (type_id
, val_seq
)
672 VALUES (13, ARRAY[val
])
673 RETURNING value_id
INTO result;
675 END; $$
LANGUAGE plpgsql
;
679 -- returns the whether value_id is an atom
680 CREATE FUNCTION types.
_atom_Q(id integer) RETURNS boolean AS $$
682 RETURN EXISTS(SELECT 1 FROM types.
value
683 WHERE type_id
= 13 AND value_id
= id);
684 END; $$
LANGUAGE plpgsql
;
687 -- takes an atom value_id
688 -- returns a atom value value_id
689 CREATE FUNCTION types.
_deref(atm
integer) RETURNS integer AS $$
693 RETURN (SELECT val_seq
[1] FROM types.
value WHERE value_id
= atm
);
694 END; $$
LANGUAGE plpgsql
;
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 $$
701 UPDATE types.
value SET val_seq
= ARRAY[newval
] WHERE value_id
= atm
;
703 END; $$
LANGUAGE plpgsql
;