1 -- ---------------------------------------------------------
5 type_id
integer NOT NULL,
8 ALTER TABLE type ADD CONSTRAINT pk_types_id
11 INSERT INTO type VALUES (0, 'nil');
12 INSERT INTO type VALUES (1, 'false');
13 INSERT INTO type VALUES (2, 'true');
14 INSERT INTO type VALUES (3, 'integer');
15 INSERT INTO type VALUES (4, 'float');
16 INSERT INTO type VALUES (5, 'string');
17 --INSERT INTO type VALUES (6, 'keyword');
18 INSERT INTO type VALUES (7, 'symbol');
19 INSERT INTO type VALUES (8, 'list');
20 INSERT INTO type VALUES (9, 'vector');
21 INSERT INTO type VALUES (10, 'hashmap');
22 INSERT INTO type VALUES (11, 'function');
23 INSERT INTO type VALUES (12, 'malfunc');
24 INSERT INTO type VALUES (13, 'atom');
27 -- ---------------------------------------------------------
28 -- collections/groupings
30 CREATE TABLE collection (
31 collection_id
integer NOT NULL, -- same for items of a collection
32 idx
integer, -- set for list and vector items
33 key_string
varchar, -- set for hashmap items
34 value_id
integer, -- set for all items (ast for functions)
35 params_id
integer, -- set for functions
36 env_id
integer, -- set for functions
37 macro
boolean -- set for macro functions
39 -- ALTER TABLE collection ADD CONSTRAINT pk_collection
40 -- PRIMARY KEY (collection_id, idx, key_string);
41 -- value_id, params_id foreign keys are after value table
42 CREATE INDEX ON collection (collection_id
);
43 CREATE INDEX ON collection (collection_id
, idx
);
44 CREATE INDEX ON collection (collection_id
, key_string
);
47 -- ---------------------------------------------------------
50 CREATE SEQUENCE value_id_seq
START WITH 3; -- skip nil, false, true
52 value_id
integer NOT NULL DEFAULT nextval('value_id_seq'),
53 type_id
integer NOT NULL,
54 val_int
bigint, -- set for integers
55 val_string
varchar, -- set for strings, keywords, and symbols
56 collection_id
integer, -- set for lists, vectors and hashmaps
57 -- (NULL for empty collection)
58 function_name
varchar, -- set for native function types
59 meta_id
integer -- can be set for any collection
61 ALTER TABLE value ADD CONSTRAINT pk_value_id
62 PRIMARY KEY (value_id
);
63 -- drop sequence when table dropped
64 ALTER SEQUENCE value_id_seq
OWNED BY value.value_id
;
65 ALTER TABLE value ADD CONSTRAINT fk_type_id
66 FOREIGN KEY (type_id
) REFERENCES type(type_id
);
67 ALTER TABLE value ADD CONSTRAINT fk_meta_id
68 FOREIGN KEY (meta_id
) REFERENCES value(value_id
);
69 -- ALTER TABLE value ADD CONSTRAINT fk_collection_id
70 -- FOREIGN KEY (collection_id) REFERENCES collection(collection_id, idx, key_string);
71 -- References from collection back to value
72 ALTER TABLE collection
ADD CONSTRAINT fk_value_id
73 FOREIGN KEY (value_id
) REFERENCES value(value_id
);
74 ALTER TABLE collection
ADD CONSTRAINT fk_params_id
75 FOREIGN KEY (params_id
) REFERENCES value(value_id
);
77 CREATE INDEX ON value (value_id
, type_id
);
79 INSERT INTO value (value_id
, type_id
) VALUES (0, 0); -- nil
80 INSERT INTO value (value_id
, type_id
) VALUES (1, 1); -- false
81 INSERT INTO value (value_id
, type_id
) VALUES (2, 2); -- true
84 -- ---------------------------------------------------------
87 CREATE FUNCTION _wraptf(val
boolean) RETURNS integer AS $$
94 END; $$
LANGUAGE plpgsql
;
96 -- pun both NULL and false to false
97 CREATE FUNCTION _tf(val
boolean) RETURNS boolean AS $$
99 IF val
IS NULL OR val
= false THEN
103 END; $$
LANGUAGE plpgsql
;
105 -- pun both NULL and 0 to false
106 CREATE FUNCTION _tf(val
integer) RETURNS boolean AS $$
108 IF val
IS NULL OR val
= 0 THEN
112 END; $$
LANGUAGE plpgsql
;
114 -- return the type of the given value_id
115 CREATE FUNCTION _type(obj
integer) RETURNS integer AS $$
117 RETURN (SELECT type_id
FROM value WHERE value_id
= obj
);
118 END; $$
LANGUAGE plpgsql
;
123 CREATE FUNCTION _equal_Q(a
integer, b
integer) RETURNS boolean AS $$
137 IF NOT ((atype
= btype
) OR (_sequential_Q(a
) AND _sequential_Q(b
))) THEN
141 WHEN atype
= 3 THEN -- integer
142 SELECT val_int
FROM value INTO avid
WHERE value_id
= a
;
143 SELECT val_int
FROM value INTO bvid
WHERE value_id
= b
;
145 WHEN atype
= 5 OR atype
= 7 THEN -- string/symbol
146 RETURN _valueToString(a
) = _valueToString(b
);
147 WHEN atype
IN (8, 9, 10) THEN -- list/vector/hash-map
148 IF _count(a
) <> _count(b
) THEN
151 SELECT collection_id
FROM value INTO acid
WHERE value_id
= a
;
152 SELECT collection_id
FROM value INTO bcid
WHERE value_id
= b
;
153 IF atype
= 10 THEN -- hash-map
154 FOR akey
, avid
IN (SELECT key_string
, value_id
FROM collection
155 WHERE collection_id
= acid
)
157 SELECT key_string
, value_id
INTO bkey
, bvid
FROM collection
158 WHERE collection_id
= bcid
AND key_string
= akey
;
159 IF bvid
IS NULL OR NOT _equal_Q(avid
, bvid
) THEN
164 FOR i
IN 0 ..
_count(a
)-1
166 SELECT value_id
INTO avid
FROM collection
167 WHERE collection_id
= acid
AND idx
= i
;
168 SELECT value_id
INTO bvid
FROM collection
169 WHERE collection_id
= bcid
AND idx
= i
;
170 IF NOT _equal_Q(avid
, bvid
) THEN
179 END; $$
LANGUAGE plpgsql
;
183 -- take a value_id of a collection
184 -- returns a new value_id of a cloned collection
185 CREATE FUNCTION _clone(id integer) RETURNS integer AS $$
191 SELECT collection_id
FROM value INTO src_coll_id
194 -- copy value and change collection_id to new value
195 INSERT INTO value (type_id
,collection_id
)
196 (SELECT type_id
,COALESCE((SELECT Max(collection_id
) FROM value)+1,0)
199 RETURNING value_id
, collection_id
INTO result, dst_coll_id
;
201 -- copy collection and change collection_id
202 INSERT INTO collection
203 (collection_id
,idx
,key_string
,value_id
,params_id
,env_id
,macro
)
204 (SELECT dst_coll_id
,idx
,key_string
,value_id
,params_id
,env_id
,macro
206 WHERE collection_id
= src_coll_id
);
209 END; $$
LANGUAGE plpgsql
;
212 -- ---------------------------------------------------------
218 -- returns the whether value_id is nil
219 CREATE FUNCTION _nil_Q(id integer) RETURNS boolean AS $$
222 END; $$
LANGUAGE plpgsql
;
226 -- returns the whether value_id is true
227 CREATE FUNCTION _true_Q(id integer) RETURNS boolean AS $$
230 END; $$
LANGUAGE plpgsql
;
234 -- returns the whether value_id is false
235 CREATE FUNCTION _false_Q(id integer) RETURNS boolean AS $$
238 END; $$
LANGUAGE plpgsql
;
242 -- returns the whether value_id is string type
243 CREATE FUNCTION _string_Q(id integer) RETURNS boolean AS $$
245 IF (SELECT 1 FROM value WHERE type_id
= 5 AND value_id
= id) THEN
246 RETURN NOT _keyword_Q(id);
249 END; $$
LANGUAGE plpgsql
;
253 -- takes a value_id for a string
254 -- returns the varchar value of the string
255 CREATE FUNCTION _valueToString(sid
integer) RETURNS varchar AS $$
257 RETURN (SELECT val_string
FROM value WHERE value_id
= sid
);
258 END; $$
LANGUAGE plpgsql
;
261 -- takes a varchar string
262 -- returns the value_id of a stringish type (string, symbol, keyword)
263 CREATE FUNCTION _stringish(str
varchar, type integer) RETURNS integer AS $$
267 -- TODO: share string data between string types
268 -- lookup if it exists
269 SELECT value_id
FROM value INTO result
270 WHERE val_string
= str
AND type_id
= type;
271 IF result IS NULL THEN
272 -- Create string entry
273 INSERT INTO value (type_id
, val_string
)
275 RETURNING value_id
INTO result;
278 END; $$
LANGUAGE plpgsql
;
281 -- takes a varchar string
282 -- returns the value_id of a string (new or existing)
283 CREATE FUNCTION _stringv(str
varchar) RETURNS integer AS $$
285 RETURN _stringish(str
, 5);
286 END; $$
LANGUAGE plpgsql
;
289 -- takes a varchar string
290 -- returns the value_id of a keyword (new or existing)
291 CREATE FUNCTION _keywordv(name varchar) RETURNS integer AS $$
293 RETURN _stringish(chr(CAST(x
'29e' AS integer)) ||
name, 5);
294 END; $$
LANGUAGE plpgsql
;
298 -- returns the whether value_id is keyword type
299 CREATE FUNCTION _keyword_Q(id integer) RETURNS boolean AS $$
303 IF (SELECT 1 FROM value WHERE type_id
= 5 AND value_id
= id) THEN
304 str
:= _valueToString(id);
305 IF char_length(str
) > 0 AND
306 chr(CAST(x
'29e' AS integer)) = substring(str
FROM 1 FOR 1) THEN
311 END; $$
LANGUAGE plpgsql
;
314 -- takes a varchar string
315 -- returns the value_id of a symbol (new or existing)
316 CREATE FUNCTION _symbolv(name varchar) RETURNS integer AS $$
318 RETURN _stringish(name, 7);
319 END; $$
LANGUAGE plpgsql
;
323 -- returns the whether value_id is symbol type
324 CREATE FUNCTION _symbol_Q(id integer) RETURNS boolean AS $$
326 RETURN _tf((SELECT 1 FROM value WHERE type_id
= 7 AND value_id
= id));
327 END; $$
LANGUAGE plpgsql
;
330 ---- takes an integer number
331 ---- returns the value_id for the number
332 --CREATE FUNCTION _numToValue(num integer) RETURNS integer AS $$
336 -- SELECT value_id FROM value INTO result
337 -- WHERE val_int = CAST(num AS bigint) AND type_id = 3;
338 -- IF result IS NULL THEN
339 -- -- Create an integer entry
340 -- INSERT INTO value (type_id, val_int)
341 -- VALUES (3, CAST(num AS bigint))
342 -- RETURNING value_id INTO result;
345 --END; $$ LANGUAGE plpgsql;
348 -- takes an bigint number
349 -- returns the value_id for the number
350 CREATE FUNCTION _numToValue(num
bigint) RETURNS integer AS $$
354 SELECT value_id
FROM value INTO result
355 WHERE val_int
= num
AND type_id
= 3;
356 IF result IS NULL THEN
357 -- Create an integer entry
358 INSERT INTO value (type_id
, val_int
)
360 RETURNING value_id
INTO result;
363 END; $$
LANGUAGE plpgsql
;
365 -- ---------------------------------------------------------
366 -- sequence functions
369 -- return true if obj value_id is a list or vector
370 CREATE FUNCTION _sequential_Q(obj
integer) RETURNS boolean AS $$
372 RETURN _tf((SELECT 1 FROM value
373 WHERE value_id
= obj
AND (type_id
= 8 OR type_id
= 9)));
374 END; $$
LANGUAGE plpgsql
;
377 -- takes a array of value_id integers
378 -- returns the value_id of a new list (8), vector (9) or hash-map (10)
379 CREATE FUNCTION _collection(items
integer[], type integer) RETURNS integer AS $$
386 IF type = 10 AND (array_length(items
, 1) % 2) = 1 THEN
387 RAISE
EXCEPTION 'hash-map: odd number of arguments';
390 -- Create value entry pointing to collection (or NULL)
391 INSERT INTO value (type_id
, collection_id
)
392 VALUES (type, COALESCE((SELECT Max(collection_id
) FROM value)+1,0))
393 RETURNING value_id
, collection_id
INTO vid
, cid
;
395 IF array_length(items
, 1) > 0 THEN
398 IF idx
> array_length(items
, 1) THEN EXIT
; END IF;
399 IF type = 10 THEN -- hash-map
400 key := _valueToString(items
[idx
]);
404 INSERT INTO collection (collection_id
, idx
, key_string
, value_id
)
405 VALUES (cid
, idx
-1, key, items
[idx
]);
410 END; $$
LANGUAGE plpgsql
;
413 -- return value_id of the collection with new elements appended
414 CREATE FUNCTION _append(coll
integer, items
integer[]) RETURNS integer AS $$
422 SELECT type_id
, COALESCE(collection_id
,
423 (SELECT Max(collection_id
) FROM value)+1)
424 FROM value INTO type, cid
WHERE value_id
= coll
;
425 IF type = 10 AND (array_length(items
, 1) % 2) = 1 THEN
426 RAISE
EXCEPTION 'hash-map: odd number of arguments';
428 SELECT COALESCE(Max(idx
)+1, 0)
429 FROM collection
INTO start_idx
WHERE collection_id
= cid
;
432 IF cur_idx
> array_length(items
, 1) THEN EXIT
; END IF;
433 IF type = 10 THEN -- hash-map
434 key := _valueToString(items
[cur_idx
]);
435 cur_idx
:= cur_idx
+ 1;
438 INSERT INTO collection (collection_id
, idx
, key_string
, value_id
)
439 VALUES (cid
, cur_idx
+start_idx
-1, key, items
[cur_idx
]);
440 cur_idx
:= cur_idx
+ 1;
443 END; $$
LANGUAGE plpgsql
;
447 -- takes a array of value_id integers
448 -- returns the value_id of a new list
449 CREATE FUNCTION _list(items
integer[]) RETURNS integer AS $$
451 RETURN _collection(items
, 8);
452 END; $$
LANGUAGE plpgsql
;
455 -- takes a array of value_id integers
456 -- returns the value_id of a new list
457 CREATE FUNCTION _vector(items
integer[]) RETURNS integer AS $$
459 RETURN _collection(items
, 9);
460 END; $$
LANGUAGE plpgsql
;
463 -- return true if obj value_id is a list
464 CREATE FUNCTION _list_Q(obj
integer) RETURNS boolean AS $$
466 RETURN _tf((SELECT 1 FROM value WHERE value_id
= obj
and type_id
= 8));
467 END; $$
LANGUAGE plpgsql
;
470 -- return true if obj value_id is a list
471 CREATE FUNCTION _vector_Q(obj
integer) RETURNS boolean AS $$
473 RETURN _tf((SELECT 1 FROM value WHERE value_id
= obj
and type_id
= 9));
474 END; $$
LANGUAGE plpgsql
;
478 -- takes an value_id referring to a list or vector
479 -- returns an array of the value_ids from the list/vector
480 CREATE FUNCTION _valueToArray(seq
integer) RETURNS integer[] AS $$
482 RETURN ARRAY(SELECT value_id
FROM collection
483 WHERE collection_id
= (SELECT collection_id
FROM value
484 WHERE value_id
= seq
));
485 END; $$
LANGUAGE plpgsql
;
489 -- takes value_id and an index
490 -- returns the value_id of nth element in list/vector
491 CREATE FUNCTION _nth(seq_id
integer, indx
integer) RETURNS integer AS $$
495 SELECT value_id
INTO result FROM collection
496 WHERE collection_id
= (SELECT collection_id
FROM value
497 WHERE value_id
= seq_id
)
500 END; $$
LANGUAGE plpgsql
;
504 -- returns the value_id of first element in list/vector
505 CREATE FUNCTION _first(seq_id
integer) RETURNS integer AS $$
507 RETURN _nth(seq_id
, 0);
508 END; $$
LANGUAGE plpgsql
;
513 -- returns the array of value_ids
514 CREATE FUNCTION _restArray(seq_id
integer) RETURNS integer[] AS $$
516 RETURN ARRAY(SELECT value_id
FROM collection
517 WHERE collection_id
= (SELECT collection_id
FROM value
518 WHERE value_id
= seq_id
)
520 END; $$
LANGUAGE plpgsql
;
523 -- takes value_id, a first index and an last index
524 -- returns the value_id of new list from first (inclusive) to last (exclusive)
525 CREATE FUNCTION _slice(seq_id
integer, first integer, last integer)
526 RETURNS integer AS $$
528 dst_coll_id
integer = NULL;
533 FOR vid
, i
IN (SELECT value_id
, idx
FROM collection
534 WHERE collection_id
= (SELECT collection_id
FROM value
535 WHERE value_id
= seq_id
)
536 AND idx
>= first AND idx
< last
539 IF dst_coll_id
IS NULL THEN
540 INSERT INTO collection (collection_id
, idx
, value_id
)
541 VALUES (COALESCE((SELECT Max(collection_id
) FROM collection
)+1,0), i
-1, vid
)
542 RETURNING collection_id
INTO dst_coll_id
;
544 INSERT INTO collection (collection_id
, idx
, value_id
)
545 VALUES (dst_coll_id
, i
-1, vid
);
548 INSERT INTO value (type_id
, collection_id
)
549 VALUES (8, dst_coll_id
)
550 RETURNING value_id
INTO result;
552 END; $$
LANGUAGE plpgsql
;
556 -- returns the value_id of new list
557 CREATE FUNCTION _rest(seq_id
integer) RETURNS integer AS $$
559 RETURN _slice(seq_id
, 1, _count(seq_id
));
560 END; $$
LANGUAGE plpgsql
;
564 -- returns a count (not value_id)
565 CREATE FUNCTION _count(seq_id
integer) RETURNS integer AS $$
569 SELECT count(*) INTO result FROM collection
570 WHERE collection_id
= (SELECT collection_id
FROM value
571 WHERE value_id
= seq_id
);
573 END; $$
LANGUAGE plpgsql
;
576 -- ---------------------------------------------------------
577 -- hash-map functions
580 -- return value_id of a new hash-map
581 CREATE FUNCTION _hash_map(items
integer[]) RETURNS integer AS $$
583 RETURN _collection(items
, 10);
584 END; $$
LANGUAGE plpgsql
;
587 -- return true if obj value_id is a list
588 CREATE FUNCTION _hash_map_Q(obj
integer) RETURNS boolean AS $$
590 RETURN _tf((SELECT 1 FROM value WHERE value_id
= obj
and type_id
= 10));
591 END; $$
LANGUAGE plpgsql
;
594 -- return value_id of the hash-map with elements removed
595 CREATE FUNCTION _dissoc(hm
integer, items
integer[]) RETURNS integer AS $$
599 SELECT collection_id
FROM value INTO cid
WHERE value_id
= hm
;
600 FOR i
IN 1 ..
array_length(items
, 1)
602 -- Delete matching entries
603 DELETE FROM collection
604 WHERE collection_id
= cid
605 AND key_string
= _valueToString(items
[i
]);
608 END; $$
LANGUAGE plpgsql
;
611 -- return value_id of the hash-map entry matching key
612 CREATE FUNCTION _get(hm
integer, key varchar) RETURNS integer AS $$
614 RETURN (SELECT value_id
FROM collection
615 WHERE collection_id
= (SELECT collection_id
FROM value
617 AND key_string
= key);
618 END; $$
LANGUAGE plpgsql
;
621 -- return true if hash-map contains entry matching key
622 CREATE FUNCTION _contains_Q(hm
integer, key varchar) RETURNS boolean AS $$
624 RETURN _tf((SELECT 1 FROM collection
625 WHERE collection_id
= (SELECT collection_id
FROM value
627 AND key_string
= key));
628 END; $$
LANGUAGE plpgsql
;
631 -- return array of key value_ids from hash-map
632 CREATE FUNCTION _keys(hm
integer) RETURNS integer[] AS $$
634 RETURN ARRAY(SELECT _stringv(key_string
) FROM collection
635 WHERE collection_id
= (SELECT collection_id
FROM value
636 WHERE value_id
= hm
));
637 END; $$
LANGUAGE plpgsql
;
640 -- return array of value value_ids from hash-map
641 CREATE FUNCTION _vals(hm
integer) RETURNS integer[] AS $$
643 RETURN ARRAY(SELECT value_id
FROM collection
644 WHERE collection_id
= (SELECT collection_id
FROM value
645 WHERE value_id
= hm
));
646 END; $$
LANGUAGE plpgsql
;
649 -- ---------------------------------------------------------
650 -- function functions
653 -- takes a ast value_id, params value_id and env_id
654 -- returns the value_id of a new function
655 CREATE FUNCTION _function(ast
integer, params
integer, env
integer)
656 RETURNS integer AS $$
661 -- Create function entry
662 INSERT INTO collection (collection_id
, value_id
, params_id
, env_id
)
663 VALUES (COALESCE((SELECT Max(collection_id
) FROM collection
)+1,0),
665 RETURNING collection_id
INTO cid
;
666 INSERT INTO value (type_id
, collection_id
)
668 RETURNING value_id
into result;
670 END; $$
LANGUAGE plpgsql
;
673 CREATE FUNCTION _macro(func
integer) RETURNS integer AS $$
678 newfunc
:= _clone(func
);
679 SELECT collection_id
FROM value INTO cid
WHERE value_id
= newfunc
;
680 UPDATE collection
SET macro
= true
681 WHERE collection_id
= cid
;
683 END; $$
LANGUAGE plpgsql
;
685 CREATE FUNCTION _apply(func
integer, args
integer[]) RETURNS integer AS $$
695 SELECT type_id
, collection_id
, function_name
696 INTO type, fcid
, fname
697 FROM value WHERE value_id
= func
;
699 EXECUTE format('SELECT %s($1);', fname
)
700 INTO result USING args
;
703 SELECT value_id
, params_id
, env_id
704 INTO fast
, fparams
, fenv
706 WHERE collection_id
= fcid
;
707 -- NOTE: forward reference to current step EVAL function
708 RETURN EVAL(fast
, env_new_bindings(fenv
, fparams
, args
));
710 RAISE
EXCEPTION 'Invalid function call';
712 END; $$
LANGUAGE plpgsql
;
714 -- ---------------------------------------------------------
718 -- takes an ast value_id
719 -- returns a new atom value_id
720 CREATE FUNCTION _atom(val
integer) RETURNS integer AS $$
725 -- Create function entry
726 INSERT INTO collection (collection_id
, value_id
)
727 VALUES (COALESCE((SELECT Max(collection_id
) FROM collection
)+1,0), val
)
728 RETURNING collection_id
INTO cid
;
729 INSERT INTO value (type_id
, collection_id
) VALUES (13, cid
)
730 RETURNING value_id
INTO result;
732 END; $$
LANGUAGE plpgsql
;
736 -- returns the whether value_id is an atom
737 CREATE FUNCTION _atom_Q(id integer) RETURNS boolean AS $$
739 RETURN EXISTS(SELECT 1 FROM value WHERE type_id
= 13 AND value_id
= id);
740 END; $$
LANGUAGE plpgsql
;
743 -- takes an atom value_id
744 -- returns a atom value value_id
745 CREATE FUNCTION _deref(atm
integer) RETURNS integer AS $$
749 SELECT value_id
INTO result FROM collection
750 WHERE collection_id
= (SELECT collection_id
FROM value
751 WHERE value_id
= atm
);
753 END; $$
LANGUAGE plpgsql
;
756 -- takes an atom value_id and new value value_id
757 -- returns a new value value_id
758 CREATE FUNCTION _reset_BANG(atm
integer, newval
integer) RETURNS integer AS $$
760 UPDATE collection
SET value_id
= newval
761 WHERE collection_id
= (SELECT collection_id
FROM value
762 WHERE value_id
= atm
);
764 END; $$
LANGUAGE plpgsql
;