--- ---------------------------------------------------------
--- persistent values
-
-PROMPT "types.sql start";
-
-BEGIN
- EXECUTE IMMEDIATE 'DROP TYPE mal_type FORCE';
-EXCEPTION
- WHEN OTHERS THEN IF SQLCODE != -4043 THEN RAISE; END IF;
-END;
-/
-
--- list of types for type_id
--- 0: nil
--- 1: false
--- 2: true
--- 3: integer
--- 4: float
--- 5: string
--- 6: keyword (not used, uses prefixed string)
--- 7: symbol
--- 8: list
--- 9: vector
--- 10: hashmap
--- 11: function
--- 12: malfunc
--- 13: atom
-
--- nil (0), false (1), true (2)
-CREATE OR REPLACE TYPE mal_type FORCE AS OBJECT (
- type_id integer
-) NOT FINAL;
-/
-
--- integer (3)
-CREATE OR REPLACE TYPE mal_int_type FORCE UNDER mal_type (
- val_int integer
-) FINAL;
-/
-
--- string/keyword (5), symbol (7)
-CREATE OR REPLACE TYPE mal_str_type FORCE UNDER mal_type (
- val_str varchar2(4000)
-) FINAL;
-/
-
--- list (8), vector (9)
-CREATE OR REPLACE TYPE mal_seq_items_type FORCE AS TABLE OF mal_type;
-/
-
-CREATE OR REPLACE TYPE mal_seq_type FORCE UNDER mal_type (
- val_seq mal_seq_items_type
-) FINAL;
-/
-
--- malfunc (12)
-CREATE OR REPLACE TYPE malfunc_type FORCE UNDER mal_type (
- ast mal_type,
- params mal_type,
- env integer
-) FINAL;
-/
-
-
-
--- ---------------------------------------------------------
-
-CREATE OR REPLACE PACKAGE types_pkg IS
- -- general functions
- FUNCTION wraptf(val boolean) RETURN mal_type;
- FUNCTION equal_Q(a mal_type, b mal_type) RETURN boolean;
-
- -- scalar functions
- FUNCTION symbol(name varchar) RETURN mal_type;
-
- -- sequence functions
- FUNCTION list RETURN mal_type;
- FUNCTION list(a mal_type) RETURN mal_type;
- FUNCTION list(a mal_type, b mal_type) RETURN mal_type;
- FUNCTION list(a mal_type, b mal_type, c mal_type) RETURN mal_type;
- FUNCTION list(items mal_seq_items_type) RETURN mal_type;
-
- FUNCTION first(seq mal_type) RETURN mal_type;
- FUNCTION slice(seq mal_type, idx integer) RETURN mal_type;
- FUNCTION slice(items mal_seq_items_type, idx integer) RETURN mal_type;
- FUNCTION nth(seq mal_type, idx integer) RETURN mal_type;
-
- FUNCTION count(seq mal_type) RETURN integer;
-END types_pkg;
-/
-
-CREATE OR REPLACE PACKAGE BODY types_pkg IS
-
--- ---------------------------------------------------------
--- general functions
-
-
-FUNCTION wraptf(val boolean) RETURN mal_type IS
-BEGIN
- IF val THEN
- RETURN mal_type(2);
- ELSE
- RETURN mal_type(1);
- END IF;
-END;
-
-FUNCTION equal_Q(a mal_type, b mal_type) RETURN boolean IS
- aseq mal_seq_items_type;
- bseq mal_seq_items_type;
- i integer;
-BEGIN
- if NOT (a.type_id = b.type_id OR
- (a.type_id IN (8,9) AND b.type_id IN (8,9))) THEN
- RETURN FALSE;
- END IF;
-
- CASE
- WHEN a.type_id IN (0,1,2) THEN
- RETURN TRUE;
- WHEN a.type_id = 3 THEN
- RETURN TREAT(a AS mal_int_type).val_int =
- TREAT(b AS mal_int_type).val_int;
- WHEN a.type_id IN (5,7) THEN
- IF TREAT(a AS mal_str_type).val_str IS NULL AND
- TREAT(b AS mal_str_type).val_str IS NULL THEN
- RETURN TRUE;
- ELSE
- RETURN TREAT(a AS mal_str_type).val_str =
- TREAT(b AS mal_str_type).val_str;
- END IF;
- WHEN a.type_id IN (8,9) THEN
- aseq := TREAT(a AS mal_seq_type).val_seq;
- bseq := TREAT(b AS mal_seq_type).val_seq;
- IF aseq.COUNT <> bseq.COUNT THEN
- RETURN FALSE;
- END IF;
- FOR i IN 1..aseq.COUNT LOOP
- IF NOT equal_Q(aseq(i), bseq(i)) THEN
- RETURN FALSE;
- END IF;
- END LOOP;
- RETURN TRUE;
- ELSE
- RETURN FALSE;
- END CASE;
-END;
-
--- -- pun both NULL and false to false
--- CREATE OR REPLACE FUNCTION _tf(val boolean) RETURNS boolean AS $$
--- BEGIN
--- IF val IS NULL OR val = false THEN
--- RETURN false;
--- END IF;
--- RETURN true;
--- END; $$ LANGUAGE plpgsql IMMUTABLE;
---
--- -- pun both NULL and 0 to false
--- CREATE OR REPLACE FUNCTION _tf(val integer) RETURNS boolean AS $$
--- BEGIN
--- IF val IS NULL OR val = 0 THEN
--- RETURN false;
--- END IF;
--- RETURN true;
--- END; $$ LANGUAGE plpgsql IMMUTABLE;
---
--- -- return the type of the given value_id
--- CREATE OR REPLACE FUNCTION _type(obj integer) RETURNS integer AS $$
--- BEGIN
--- RETURN (SELECT type_id FROM value WHERE value_id = obj);
--- END; $$ LANGUAGE plpgsql;
---
---
--- CREATE OR REPLACE FUNCTION _equal_Q(a integer, b integer) RETURNS boolean AS $$
--- DECLARE
--- atype integer;
--- btype integer;
--- anum bigint;
--- bnum bigint;
--- avid integer;
--- bvid integer;
--- aseq integer[];
--- bseq integer[];
--- ahash hstore;
--- bhash hstore;
--- kv RECORD;
--- i integer;
--- BEGIN
--- atype := _type(a);
--- btype := _type(b);
--- IF NOT ((atype = btype) OR (_sequential_Q(a) AND _sequential_Q(b))) THEN
--- RETURN false;
--- END IF;
--- CASE
--- WHEN atype = 3 THEN -- integer
--- SELECT val_int FROM value INTO anum WHERE value_id = a;
--- SELECT val_int FROM value INTO bnum WHERE value_id = b;
--- RETURN anum = bnum;
--- WHEN atype = 5 OR atype = 7 THEN -- string/symbol
--- RETURN _valueToString(a) = _valueToString(b);
--- WHEN atype IN (8, 9) THEN -- list/vector
--- IF _count(a) <> _count(b) THEN
--- RETURN false;
--- END IF;
--- SELECT val_seq INTO aseq FROM value WHERE value_id = a;
--- SELECT val_seq INTO bseq FROM value WHERE value_id = b;
--- FOR i IN 1 .. _count(a)
--- LOOP
--- IF NOT _equal_Q(aseq[i], bseq[i]) THEN
--- return false;
--- END IF;
--- END LOOP;
--- RETURN true;
--- WHEN atype = 10 THEN -- hash-map
--- SELECT val_hash INTO ahash FROM value WHERE value_id = a;
--- SELECT val_hash INTO bhash FROM value WHERE value_id = b;
--- IF array_length(akeys(ahash), 1) <> array_length(akeys(bhash), 1) THEN
--- RETURN false;
--- END IF;
--- FOR kv IN SELECT * FROM each(ahash) LOOP
--- avid := CAST((ahash -> kv.key) AS integer);
--- bvid := CAST((bhash -> kv.key) AS integer);
--- IF bvid IS NULL OR NOT _equal_Q(avid, bvid) THEN
--- return false;
--- END IF;
--- END LOOP;
--- RETURN true;
--- ELSE
--- RETURN a = b;
--- END CASE;
--- END; $$ LANGUAGE plpgsql;
---
---
--- -- _clone:
--- -- take a value_id of a collection
--- -- returns a new value_id of a cloned collection
--- CREATE OR REPLACE FUNCTION _clone(id integer) RETURNS integer AS $$
--- DECLARE
--- result integer;
--- BEGIN
--- INSERT INTO value (type_id,val_int,val_string,val_seq,val_hash,
--- ast_id,params_id,env_id,meta_id)
--- (SELECT type_id,val_int,val_string,val_seq,val_hash,
--- ast_id,params_id,env_id,meta_id
--- FROM value
--- WHERE value_id = id)
--- RETURNING value_id INTO result;
--- RETURN result;
--- END; $$ LANGUAGE plpgsql;
-
-
--- ---------------------------------------------------------
--- scalar functions
-
-
-FUNCTION symbol(name varchar) RETURN mal_type IS
-BEGIN
- RETURN mal_str_type(7, name);
-END;
-
-
--- -- _nil_Q:
--- -- takes a value_id
--- -- returns the whether value_id is nil
--- CREATE OR REPLACE FUNCTION _nil_Q(id integer) RETURNS boolean AS $$
--- BEGIN
--- RETURN id = 0;
--- END; $$ LANGUAGE plpgsql IMMUTABLE;
---
--- -- _true_Q:
--- -- takes a value_id
--- -- returns the whether value_id is true
--- CREATE OR REPLACE FUNCTION _true_Q(id integer) RETURNS boolean AS $$
--- BEGIN
--- RETURN id = 2;
--- END; $$ LANGUAGE plpgsql IMMUTABLE;
---
--- -- _false_Q:
--- -- takes a value_id
--- -- returns the whether value_id is false
--- CREATE OR REPLACE FUNCTION _false_Q(id integer) RETURNS boolean AS $$
--- BEGIN
--- RETURN id = 1;
--- END; $$ LANGUAGE plpgsql IMMUTABLE;
---
--- -- _string_Q:
--- -- takes a value_id
--- -- returns the whether value_id is string type
--- CREATE OR REPLACE FUNCTION _string_Q(id integer) RETURNS boolean AS $$
--- BEGIN
--- IF (SELECT 1 FROM value WHERE type_id = 5 AND value_id = id) THEN
--- RETURN NOT _keyword_Q(id);
--- END IF;
--- RETURN false;
--- END; $$ LANGUAGE plpgsql;
---
---
--- -- _valueToString:
--- -- takes a value_id for a string
--- -- returns the varchar value of the string
--- CREATE OR REPLACE FUNCTION _valueToString(sid integer) RETURNS varchar AS $$
--- BEGIN
--- RETURN (SELECT val_string FROM value WHERE value_id = sid);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _stringish:
--- -- takes a varchar string
--- -- returns the value_id of a stringish type (string, symbol, keyword)
--- CREATE OR REPLACE FUNCTION _stringish(str varchar, type integer) RETURNS integer AS $$
--- DECLARE
--- result integer;
--- BEGIN
--- -- TODO: share string data between string types
--- -- lookup if it exists
--- SELECT value_id FROM value INTO result
--- WHERE val_string = str AND type_id = type;
--- IF result IS NULL THEN
--- -- Create string entry
--- INSERT INTO value (type_id, val_string)
--- VALUES (type, str)
--- RETURNING value_id INTO result;
--- END IF;
--- RETURN result;
--- END; $$ LANGUAGE plpgsql;
---
--- -- _stringv:
--- -- takes a varchar string
--- -- returns the value_id of a string (new or existing)
--- CREATE OR REPLACE FUNCTION _stringv(str varchar) RETURNS integer AS $$
--- BEGIN
--- RETURN _stringish(str, 5);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _keywordv:
--- -- takes a varchar string
--- -- returns the value_id of a keyword (new or existing)
--- CREATE OR REPLACE FUNCTION _keywordv(name varchar) RETURNS integer AS $$
--- BEGIN
--- RETURN _stringish(chr(CAST(x'7f' AS integer)) || name, 5);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _keyword_Q:
--- -- takes a value_id
--- -- returns the whether value_id is keyword type
--- CREATE OR REPLACE FUNCTION _keyword_Q(id integer) RETURNS boolean AS $$
--- DECLARE
--- str varchar;
--- BEGIN
--- IF (SELECT 1 FROM value WHERE type_id = 5 AND value_id = id) THEN
--- str := _valueToString(id);
--- IF char_length(str) > 0 AND
--- chr(CAST(x'7f' AS integer)) = substring(str FROM 1 FOR 1) THEN
--- RETURN true;
--- END IF;
--- END IF;
--- RETURN false;
--- END; $$ LANGUAGE plpgsql;
---
--- -- _symbolv:
--- -- takes a varchar string
--- -- returns the value_id of a symbol (new or existing)
--- CREATE OR REPLACE FUNCTION _symbolv(name varchar) RETURNS integer AS $$
--- BEGIN
--- RETURN _stringish(name, 7);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _symbol_Q:
--- -- takes a value_id
--- -- returns the whether value_id is symbol type
--- CREATE OR REPLACE FUNCTION _symbol_Q(id integer) RETURNS boolean AS $$
--- BEGIN
--- RETURN _tf((SELECT 1 FROM value WHERE type_id = 7 AND value_id = id));
--- END; $$ LANGUAGE plpgsql;
---
--- -- _numToValue:
--- -- takes an bigint number
--- -- returns the value_id for the number
--- CREATE OR REPLACE FUNCTION _numToValue(num bigint) RETURNS integer AS $$
--- DECLARE
--- result integer;
--- BEGIN
--- SELECT value_id FROM value INTO result
--- WHERE val_int = num AND type_id = 3;
--- IF result IS NULL THEN
--- -- Create an integer entry
--- INSERT INTO value (type_id, val_int)
--- VALUES (3, num)
--- RETURNING value_id INTO result;
--- END IF;
--- RETURN result;
--- END; $$ LANGUAGE plpgsql;
---
--- -- ---------------------------------------------------------
--- -- sequence functions
---
--- -- _sequential_Q:
--- -- return true if obj value_id is a list or vector
--- CREATE OR REPLACE FUNCTION _sequential_Q(obj integer) RETURNS boolean AS $$
--- BEGIN
--- RETURN _tf((SELECT 1 FROM value
--- WHERE value_id = obj AND (type_id = 8 OR type_id = 9)));
--- END; $$ LANGUAGE plpgsql;
---
--- -- _collection:
--- -- takes a array of value_id integers
--- -- returns the value_id of a new list (8), vector (9) or hash-map (10)
--- CREATE OR REPLACE FUNCTION _collection(items integer[], type integer) RETURNS integer AS $$
--- DECLARE
--- vid integer;
--- BEGIN
--- IF type IN (8, 9) THEN
--- INSERT INTO value (type_id, val_seq)
--- VALUES (type, items)
--- RETURNING value_id INTO vid;
--- ELSIF type = 10 THEN
--- IF (array_length(items, 1) % 2) = 1 THEN
--- RAISE EXCEPTION 'hash-map: odd number of arguments';
--- END IF;
--- INSERT INTO value (type_id, val_hash)
--- VALUES (type, hstore(CAST(items AS varchar[])))
--- RETURNING value_id INTO vid;
--- END IF;
--- RETURN vid;
--- END; $$ LANGUAGE plpgsql;
---
-
--- ---------------------------------------------------------
--- sequence functions
-
--- list:
--- return a mal list
-FUNCTION list RETURN mal_type IS
-BEGIN
- RETURN mal_seq_type(8, mal_seq_items_type());
-END;
-
-FUNCTION list(a mal_type) RETURN mal_type IS
-BEGIN
- RETURN mal_seq_type(8, mal_seq_items_type(a));
-END;
-
-FUNCTION list(a mal_type, b mal_type) RETURN mal_type IS
-BEGIN
- RETURN mal_seq_type(8, mal_seq_items_type(a, b));
-END;
-
-FUNCTION list(a mal_type, b mal_type, c mal_type) RETURN mal_type IS
-BEGIN
- RETURN mal_seq_type(8, mal_seq_items_type(a, b, c));
-END;
-
-FUNCTION list(items mal_seq_items_type) RETURN mal_type IS
-BEGIN
- RETURN mal_seq_type(8, items);
-END;
-
-FUNCTION first(seq mal_type) RETURN mal_type IS
-BEGIN
- RETURN TREAT(seq AS mal_seq_type).val_seq(1);
-END;
-
-FUNCTION slice(seq mal_type, idx integer) RETURN mal_type IS
- old_items mal_seq_items_type;
- new_items mal_seq_items_type;
- i integer;
-BEGIN
- old_items := TREAT(seq AS mal_seq_type).val_seq;
- new_items := mal_seq_items_type();
- new_items.EXTEND(old_items.COUNT - idx);
- FOR i IN idx+1..old_items.COUNT LOOP
- new_items(i-idx) := old_items(i);
- END LOOP;
- RETURN mal_seq_type(8, new_items);
-END;
-
-FUNCTION slice(items mal_seq_items_type, idx integer) RETURN mal_type IS
- new_items mal_seq_items_type;
- i integer;
-BEGIN
- new_items := mal_seq_items_type();
- new_items.EXTEND(items.COUNT - idx);
- FOR i IN idx+1..items.COUNT LOOP
- new_items(i-idx) := items(i);
- END LOOP;
- RETURN mal_seq_type(8, new_items);
-END;
-
-FUNCTION nth(seq mal_type, idx integer) RETURN mal_type IS
-BEGIN
- RETURN TREAT(seq AS mal_seq_type).val_seq(idx+1);
-END;
-
-FUNCTION count(seq mal_type) RETURN integer IS
-BEGIN
- RETURN TREAT(seq AS mal_seq_type).val_seq.COUNT;
-END;
-
--- -- _vector:
--- -- takes a array of value_id integers
--- -- returns the value_id of a new list
--- CREATE OR REPLACE FUNCTION _vector(items integer[]) RETURNS integer AS $$
--- BEGIN
--- RETURN _collection(items, 9);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _list_Q:
--- -- return true if obj value_id is a list
--- CREATE OR REPLACE FUNCTION _list_Q(obj integer) RETURNS boolean AS $$
--- BEGIN
--- RETURN _tf((SELECT 1 FROM value WHERE value_id = obj and type_id = 8));
--- END; $$ LANGUAGE plpgsql;
---
--- -- _vector_Q:
--- -- return true if obj value_id is a list
--- CREATE OR REPLACE FUNCTION _vector_Q(obj integer) RETURNS boolean AS $$
--- BEGIN
--- RETURN _tf((SELECT 1 FROM value WHERE value_id = obj and type_id = 9));
--- END; $$ LANGUAGE plpgsql;
---
---
--- -- _valueToArray:
--- -- takes an value_id referring to a list or vector
--- -- returns an array of the value_ids from the list/vector
--- CREATE OR REPLACE FUNCTION _valueToArray(seq integer) RETURNS integer[] AS $$
--- DECLARE
--- result integer[];
--- BEGIN
--- result := (SELECT val_seq FROM value WHERE value_id = seq);
--- IF result IS NULL THEN
--- result := ARRAY[]::integer[];
--- END IF;
--- RETURN result;
--- END; $$ LANGUAGE plpgsql;
---
--- -- From: https://wiki.postgresql.org/wiki/Array_reverse
--- CREATE OR REPLACE FUNCTION array_reverse(a integer[]) RETURNS integer[] AS $$
--- SELECT ARRAY(
--- SELECT a[i]
--- FROM generate_subscripts(a,1) AS s(i)
--- ORDER BY i DESC
--- );
--- $$ LANGUAGE 'sql' STRICT IMMUTABLE;
---
---
--- -- _nth:
--- -- takes value_id and an index
--- -- returns the value_id of nth element in list/vector
--- CREATE OR REPLACE FUNCTION _nth(seq_id integer, indx integer) RETURNS integer AS $$
--- DECLARE
--- result integer;
--- BEGIN
--- RETURN (SELECT val_seq[indx+1] FROM value WHERE value_id = seq_id);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _first:
--- -- takes value_id
--- -- returns the value_id of first element in list/vector
--- CREATE OR REPLACE FUNCTION _first(seq_id integer) RETURNS integer AS $$
--- BEGIN
--- RETURN _nth(seq_id, 0);
--- END; $$ LANGUAGE plpgsql;
---
---
--- -- _restArray:
--- -- takes value_id
--- -- returns the array of value_ids
--- CREATE OR REPLACE FUNCTION _restArray(seq_id integer) RETURNS integer[] AS $$
--- DECLARE
--- result integer[];
--- BEGIN
--- result := (SELECT val_seq FROM value WHERE value_id = seq_id);
--- RETURN result[2:array_length(result, 1)];
--- END; $$ LANGUAGE plpgsql;
---
--- -- _slice:
--- -- takes value_id, a first index and an last index
--- -- returns the value_id of new list from first (inclusive) to last (exclusive)
--- CREATE OR REPLACE FUNCTION _slice(seq_id integer, first integer, last integer)
--- RETURNS integer AS $$
--- DECLARE
--- seq integer[];
--- vid integer;
--- i integer;
--- result integer;
--- BEGIN
--- SELECT val_seq INTO seq FROM value WHERE value_id = seq_id;
--- INSERT INTO value (type_id, val_seq)
--- VALUES (8, seq[first+1:last])
--- RETURNING value_id INTO result;
--- RETURN result;
--- END; $$ LANGUAGE plpgsql;
---
--- -- _rest:
--- -- takes value_id
--- -- returns the value_id of new list
--- CREATE OR REPLACE FUNCTION _rest(seq_id integer) RETURNS integer AS $$
--- BEGIN
--- RETURN _slice(seq_id, 1, _count(seq_id));
--- END; $$ LANGUAGE plpgsql;
---
--- -- _count:
--- -- takes value_id
--- -- returns a count (not value_id)
--- CREATE OR REPLACE FUNCTION _count(seq_id integer) RETURNS integer AS $$
--- DECLARE
--- result integer[];
--- BEGIN
--- result := (SELECT val_seq FROM value
--- WHERE value_id = seq_id);
--- RETURN COALESCE(array_length(result, 1), 0);
--- END; $$ LANGUAGE plpgsql;
---
---
--- -- ---------------------------------------------------------
--- -- hash-map functions
---
--- -- _hash_map:
--- -- return value_id of a new hash-map
--- CREATE OR REPLACE FUNCTION _hash_map(items integer[]) RETURNS integer AS $$
--- BEGIN
--- RETURN _collection(items, 10);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _hash_map_Q:
--- -- return true if obj value_id is a list
--- CREATE OR REPLACE FUNCTION _hash_map_Q(obj integer) RETURNS boolean AS $$
--- BEGIN
--- RETURN _tf((SELECT 1 FROM value WHERE value_id = obj and type_id = 10));
--- END; $$ LANGUAGE plpgsql;
---
--- -- _assoc_BANG:
--- -- return value_id of the hash-map with new elements appended
--- CREATE OR REPLACE FUNCTION _assoc_BANG(hm integer, items integer[]) RETURNS integer AS $$
--- DECLARE
--- hash hstore;
--- BEGIN
--- IF (array_length(items, 1) % 2) = 1 THEN
--- RAISE EXCEPTION 'hash-map: odd number of arguments';
--- END IF;
--- SELECT val_hash INTO hash FROM value WHERE value_id = hm;
--- IF hash IS NULL THEN
--- UPDATE value SET val_hash = hstore(CAST(items AS varchar[]))
--- WHERE value_id = hm;
--- ELSE
--- UPDATE value SET val_hash = hash || hstore(CAST(items AS varchar[]))
--- WHERE value_id = hm;
--- END IF;
--- RETURN hm;
--- END; $$ LANGUAGE plpgsql;
---
--- -- _dissoc_BANG:
--- -- return value_id of the hash-map with elements removed
--- CREATE OR REPLACE FUNCTION _dissoc_BANG(hm integer, items integer[]) RETURNS integer AS $$
--- DECLARE
--- hash hstore;
--- BEGIN
--- SELECT val_hash INTO hash FROM value WHERE value_id = hm;
--- UPDATE value SET val_hash = hash - CAST(items AS varchar[])
--- WHERE value_id = hm;
--- RETURN hm;
--- END; $$ LANGUAGE plpgsql;
---
--- -- _get:
--- -- return value_id of the hash-map entry matching key
--- CREATE OR REPLACE FUNCTION _get(hm integer, key varchar) RETURNS integer AS $$
--- DECLARE
--- hash hstore;
--- BEGIN
--- SELECT val_hash INTO hash FROM value WHERE value_id = hm;
--- RETURN hash -> CAST(_stringv(key) AS varchar);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _contains_Q:
--- -- return true if hash-map contains entry matching key
--- CREATE OR REPLACE FUNCTION _contains_Q(hm integer, key varchar) RETURNS boolean AS $$
--- DECLARE
--- hash hstore;
--- BEGIN
--- SELECT val_hash INTO hash FROM value WHERE value_id = hm;
--- RETURN _tf(hash ? CAST(_stringv(key) AS varchar));
--- END; $$ LANGUAGE plpgsql;
---
--- -- _keys:
--- -- return array of key value_ids from hash-map
--- CREATE OR REPLACE FUNCTION _keys(hm integer) RETURNS integer[] AS $$
--- DECLARE
--- hash hstore;
--- BEGIN
--- SELECT val_hash INTO hash FROM value WHERE value_id = hm;
--- RETURN CAST(akeys(hash) AS integer[]);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _vals:
--- -- return array of value value_ids from hash-map
--- CREATE OR REPLACE FUNCTION _vals(hm integer) RETURNS integer[] AS $$
--- DECLARE
--- hash hstore;
--- BEGIN
--- SELECT val_hash INTO hash FROM value WHERE value_id = hm;
--- RETURN CAST(avals(hash) AS integer[]);
--- END; $$ LANGUAGE plpgsql;
---
---
--- -- ---------------------------------------------------------
--- -- function functions
---
--- -- _function:
--- -- takes a function name
--- -- returns the value_id of a new
--- CREATE OR REPLACE FUNCTION _function(fname varchar)
--- RETURNS varchar AS $$
--- DECLARE
--- result integer;
--- BEGIN
--- INSERT INTO value (type_id, val_string)
--- VALUES (11, fname)
--- RETURNING value_id INTO result;
--- RETURN CAST(result AS varchar);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _malfunc:
--- -- takes a ast value_id, params value_id and env_id
--- -- returns the value_id of a new function
--- CREATE OR REPLACE FUNCTION _malfunc(ast integer, params integer, env integer)
--- RETURNS integer AS $$
--- DECLARE
--- cid integer = NULL;
--- result integer;
--- BEGIN
--- -- Create function entry
--- INSERT INTO value (type_id, ast_id, params_id, env_id)
--- VALUES (12, ast, params, env)
--- RETURNING value_id into result;
--- RETURN result;
--- END; $$ LANGUAGE plpgsql;
---
--- -- _macro:
--- CREATE OR REPLACE FUNCTION _macro(func integer) RETURNS integer AS $$
--- DECLARE
--- newfunc integer;
--- cid integer;
--- BEGIN
--- newfunc := _clone(func);
--- UPDATE value SET macro = true WHERE value_id = newfunc;
--- RETURN newfunc;
--- END; $$ LANGUAGE plpgsql;
---
--- CREATE OR REPLACE FUNCTION _apply(func integer, args integer[]) RETURNS integer AS $$
--- DECLARE
--- type integer;
--- fcid integer;
--- fname varchar;
--- fast integer;
--- fparams integer;
--- fenv integer;
--- result integer;
--- BEGIN
--- SELECT type_id, val_string, ast_id, params_id, env_id
--- INTO type, fname, fast, fparams, fenv
--- FROM value WHERE value_id = func;
--- IF type = 11 THEN
--- EXECUTE format('SELECT %s($1);', fname)
--- INTO result USING args;
--- RETURN result;
--- ELSIF type = 12 THEN
--- -- NOTE: forward reference to current step EVAL function
--- RETURN EVAL(fast, env_new_bindings(fenv, fparams, args));
--- ELSE
--- RAISE EXCEPTION 'Invalid function call';
--- END IF;
--- END; $$ LANGUAGE plpgsql;
---
--- -- ---------------------------------------------------------
--- -- atom functions
---
--- -- _atom:
--- -- takes an ast value_id
--- -- returns a new atom value_id
--- CREATE OR REPLACE FUNCTION _atom(val integer) RETURNS integer AS $$
--- DECLARE
--- cid integer = NULL;
--- result integer;
--- BEGIN
--- -- Create atom
--- INSERT INTO value (type_id, val_seq)
--- VALUES (13, ARRAY[val])
--- RETURNING value_id INTO result;
--- RETURN result;
--- END; $$ LANGUAGE plpgsql;
---
--- -- _atom_Q:
--- -- takes a value_id
--- -- returns the whether value_id is an atom
--- CREATE OR REPLACE FUNCTION _atom_Q(id integer) RETURNS boolean AS $$
--- BEGIN
--- RETURN EXISTS(SELECT 1 FROM value WHERE type_id = 13 AND value_id = id);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _deref:
--- -- takes an atom value_id
--- -- returns a atom value value_id
--- CREATE OR REPLACE FUNCTION _deref(atm integer) RETURNS integer AS $$
--- DECLARE
--- result integer;
--- BEGIN
--- RETURN (SELECT val_seq[1] FROM value WHERE value_id = atm);
--- END; $$ LANGUAGE plpgsql;
---
--- -- _reset_BANG:
--- -- takes an atom value_id and new value value_id
--- -- returns a new value value_id
--- CREATE OR REPLACE FUNCTION _reset_BANG(atm integer, newval integer) RETURNS integer AS $$
--- BEGIN
--- UPDATE value SET val_seq = ARRAY[newval] WHERE value_id = atm;
--- RETURN newval;
--- END; $$ LANGUAGE plpgsql;
-
-END types_pkg;
-/
-show errors;
-
-PROMPT "types.sql finished";
+-- ---------------------------------------------------------
+-- persistent values
+
+BEGIN
+ EXECUTE IMMEDIATE 'DROP TYPE mal_T FORCE';
+EXCEPTION
+ WHEN OTHERS THEN IF SQLCODE != -4043 THEN RAISE; END IF;
+END;
+/
+
+-- list of types for type_id
+-- 0: nil
+-- 1: false
+-- 2: true
+-- 3: integer
+-- 4: float
+-- 5: string
+-- 6: long string (CLOB)
+-- 7: symbol
+-- 8: list
+-- 9: vector
+-- 10: hashmap
+-- 11: function
+-- 12: malfunc
+-- 13: atom
+
+-- nil (0), false (1), true (2)
+CREATE OR REPLACE TYPE mal_T FORCE AS OBJECT (
+ type_id integer
+) NOT FINAL;
+/
+
+-- general nested table of mal values (integers)
+-- used frequently for argument passing
+CREATE OR REPLACE TYPE mal_vals FORCE AS TABLE OF integer;
+/
+
+
+-- integer (3)
+CREATE OR REPLACE TYPE mal_int_T FORCE UNDER mal_T (
+ val_int integer
+) FINAL;
+/
+
+-- string/keyword (5,6), symbol (7)
+CREATE OR REPLACE TYPE mal_str_T FORCE UNDER mal_T (
+ val_str varchar2(4000)
+) NOT FINAL;
+/
+
+CREATE OR REPLACE TYPE mal_long_str_T FORCE UNDER mal_str_T (
+ val_long_str CLOB -- long character object (for larger than 4000 chars)
+) FINAL;
+/
+show errors;
+
+-- list (8), vector (9)
+CREATE OR REPLACE TYPE mal_seq_T FORCE UNDER mal_T (
+ val_seq mal_vals,
+ meta integer
+) FINAL;
+/
+
+CREATE OR REPLACE TYPE mal_map_T FORCE UNDER mal_T (
+ map_idx integer, -- index into map entry table
+ meta integer
+) FINAL;
+/
+
+-- malfunc (12)
+CREATE OR REPLACE TYPE mal_func_T FORCE UNDER mal_T (
+ ast integer,
+ params integer,
+ env integer,
+ is_macro integer,
+ meta integer
+) FINAL;
+/
+
+-- atom (13)
+CREATE OR REPLACE TYPE mal_atom_T FORCE UNDER mal_T (
+ val integer -- index into mal_table
+);
+/
+
+
+-- ---------------------------------------------------------
+
+CREATE OR REPLACE PACKAGE types IS
+ -- memory pool for mal_objects (non-hash-map)
+ TYPE mal_table IS TABLE OF mal_T;
+
+ -- memory pool for hash-map objects
+ TYPE map_entry IS TABLE OF integer INDEX BY varchar2(256);
+ TYPE map_entry_table IS TABLE OF map_entry;
+
+ -- general functions
+ FUNCTION mem_new RETURN mal_table;
+
+ FUNCTION tf(val boolean) RETURN integer;
+ FUNCTION equal_Q(M IN OUT NOCOPY mal_table,
+ H IN OUT NOCOPY map_entry_table,
+ a integer, b integer) RETURN boolean;
+
+ FUNCTION clone(M IN OUT NOCOPY mal_table,
+ H IN OUT NOCOPY map_entry_table,
+ obj integer,
+ meta integer DEFAULT 1) RETURN integer;
+
+ -- scalar functions
+ FUNCTION int(M IN OUT NOCOPY mal_table, num integer) RETURN integer;
+ FUNCTION string(M IN OUT NOCOPY mal_table, name varchar) RETURN integer;
+ FUNCTION string_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean;
+ FUNCTION symbol(M IN OUT NOCOPY mal_table, name varchar) RETURN integer;
+ FUNCTION keyword(M IN OUT NOCOPY mal_table, name varchar) RETURN integer;
+ FUNCTION keyword_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean;
+ FUNCTION number_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean;
+ FUNCTION function_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean;
+ FUNCTION macro_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean;
+
+ -- sequence functions
+ FUNCTION seq(M IN OUT NOCOPY mal_table,
+ type_id integer,
+ items mal_vals,
+ meta integer DEFAULT 1) RETURN integer;
+ FUNCTION list(M IN OUT NOCOPY mal_table) RETURN integer;
+ FUNCTION list(M IN OUT NOCOPY mal_table,
+ a integer) RETURN integer;
+ FUNCTION list(M IN OUT NOCOPY mal_table,
+ a integer, b integer) RETURN integer;
+ FUNCTION list(M IN OUT NOCOPY mal_table,
+ a integer, b integer, c integer) RETURN integer;
+
+ FUNCTION first(M IN OUT NOCOPY mal_table,
+ seq integer) RETURN integer;
+ FUNCTION slice(M IN OUT NOCOPY mal_table,
+ seq integer,
+ idx integer,
+ last integer DEFAULT NULL) RETURN integer;
+ FUNCTION slice(M IN OUT NOCOPY mal_table,
+ items mal_vals,
+ idx integer) RETURN integer;
+ FUNCTION islice(items mal_vals,
+ idx integer) RETURN mal_vals;
+ FUNCTION nth(M IN OUT NOCOPY mal_table,
+ seq integer, idx integer) RETURN integer;
+
+ FUNCTION count(M IN OUT NOCOPY mal_table,
+ seq integer) RETURN integer;
+
+ FUNCTION atom_new(M IN OUT NOCOPY mal_table,
+ val integer) RETURN integer;
+ FUNCTION atom_reset(M IN OUT NOCOPY mal_table,
+ atm integer,
+ val integer) RETURN integer;
+
+ -- hash-map functions
+ FUNCTION assoc_BANG(M IN OUT NOCOPY mal_table,
+ H IN OUT NOCOPY map_entry_table,
+ midx integer,
+ kvs mal_vals) RETURN integer;
+ FUNCTION dissoc_BANG(M IN OUT NOCOPY mal_table,
+ H IN OUT NOCOPY map_entry_table,
+ midx integer,
+ ks mal_vals) RETURN integer;
+ FUNCTION hash_map(M IN OUT NOCOPY mal_table,
+ H IN OUT NOCOPY map_entry_table,
+ kvs mal_vals,
+ meta integer DEFAULT 1) RETURN integer;
+
+ -- function functions
+ FUNCTION func(M IN OUT NOCOPY mal_table, name varchar) RETURN integer;
+ FUNCTION malfunc(M IN OUT NOCOPY mal_table,
+ ast integer,
+ params integer,
+ env integer,
+ is_macro integer DEFAULT 0,
+ meta integer DEFAULT 1) RETURN integer;
+END types;
+/
+show errors;
+
+
+CREATE OR REPLACE PACKAGE BODY types IS
+
+-- ---------------------------------------------------------
+-- general functions
+
+FUNCTION mem_new RETURN mal_table IS
+BEGIN
+ -- initialize mal type memory pool
+ -- 1 -> nil
+ -- 2 -> false
+ -- 3 -> true
+ RETURN mal_table(mal_T(0), mal_T(1), mal_T(2));
+END;
+
+FUNCTION tf(val boolean) RETURN integer IS
+BEGIN
+ IF val THEN
+ RETURN 3; -- true
+ ELSE
+ RETURN 2; -- false
+ END IF;
+END;
+
+FUNCTION equal_Q(M IN OUT NOCOPY mal_table,
+ H IN OUT NOCOPY map_entry_table,
+ a integer, b integer) RETURN boolean IS
+ atyp integer;
+ btyp integer;
+ aseq mal_vals;
+ bseq mal_vals;
+ amidx integer;
+ bmidx integer;
+ i integer;
+ k varchar2(256);
+BEGIN
+ atyp := M(a).type_id;
+ btyp := M(b).type_id;
+ IF NOT (atyp = btyp OR (atyp IN (8,9) AND btyp IN (8,9))) THEN
+ RETURN FALSE;
+ END IF;
+
+ CASE
+ WHEN atyp IN (0,1,2) THEN
+ RETURN TRUE;
+ WHEN atyp = 3 THEN
+ RETURN TREAT(M(a) AS mal_int_T).val_int =
+ TREAT(M(b) AS mal_int_T).val_int;
+ WHEN atyp IN (5,6,7) THEN
+ IF TREAT(M(a) AS mal_str_T).val_str IS NULL AND
+ TREAT(M(b) AS mal_str_T).val_str IS NULL THEN
+ RETURN TRUE;
+ ELSE
+ RETURN TREAT(M(a) AS mal_str_T).val_str =
+ TREAT(M(b) AS mal_str_T).val_str;
+ END IF;
+ WHEN atyp IN (8,9) THEN
+ aseq := TREAT(M(a) AS mal_seq_T).val_seq;
+ bseq := TREAT(M(b) AS mal_seq_T).val_seq;
+ IF aseq.COUNT <> bseq.COUNT THEN
+ RETURN FALSE;
+ END IF;
+ FOR i IN 1..aseq.COUNT LOOP
+ IF NOT equal_Q(M, H, aseq(i), bseq(i)) THEN
+ RETURN FALSE;
+ END IF;
+ END LOOP;
+ RETURN TRUE;
+ WHEN atyp = 10 THEN
+ amidx := TREAT(M(a) AS mal_map_T).map_idx;
+ bmidx := TREAT(M(b) AS mal_map_T).map_idx;
+ IF H(amidx).COUNT() <> H(bmidx).COUNT() THEN
+ RETURN FALSE;
+ END IF;
+
+ k := H(amidx).FIRST();
+ WHILE k IS NOT NULL LOOP
+ IF H(amidx)(k) IS NULL OR H(bmidx)(k) IS NULL THEN
+ RETURN FALSE;
+ END IF;
+ IF NOT equal_Q(M, H, H(amidx)(k), H(bmidx)(k)) THEN
+ RETURN FALSE;
+ END IF;
+ k := H(amidx).NEXT(k);
+ END LOOP;
+ RETURN TRUE;
+ ELSE
+ RETURN FALSE;
+ END CASE;
+END;
+
+FUNCTION clone(M IN OUT NOCOPY mal_table,
+ H IN OUT NOCOPY map_entry_table,
+ obj integer,
+ meta integer DEFAULT 1) RETURN integer IS
+ type_id integer;
+ new_hm integer;
+ old_midx integer;
+ new_midx integer;
+ k varchar2(256);
+ malfn mal_func_T;
+BEGIN
+ type_id := M(obj).type_id;
+ CASE
+ WHEN type_id IN (8,9) THEN -- list/vector
+ RETURN seq(M, type_id,
+ TREAT(M(obj) AS mal_seq_T).val_seq,
+ meta);
+ WHEN type_id = 10 THEN -- hash-map
+ new_hm := types.hash_map(M, H, mal_vals(), meta);
+ old_midx := TREAT(M(obj) AS mal_map_T).map_idx;
+ new_midx := TREAT(M(new_hm) AS mal_map_T).map_idx;
+
+ k := H(old_midx).FIRST();
+ WHILE k IS NOT NULL LOOP
+ H(new_midx)(k) := H(old_midx)(k);
+ k := H(old_midx).NEXT(k);
+ END LOOP;
+
+ RETURN new_hm;
+ WHEN type_id = 12 THEN -- mal function
+ malfn := TREAT(M(obj) AS mal_func_T);
+ RETURN types.malfunc(M,
+ malfn.ast,
+ malfn.params,
+ malfn.env,
+ malfn.is_macro,
+ meta);
+ ELSE
+ raise_application_error(-20008,
+ 'clone not supported for type ' || type_id, TRUE);
+ END CASE;
+END;
+
+
+-- ---------------------------------------------------------
+-- scalar functions
+
+
+FUNCTION int(M IN OUT NOCOPY mal_table, num integer) RETURN integer IS
+BEGIN
+ M.EXTEND();
+ M(M.COUNT()) := mal_int_T(3, num);
+ RETURN M.COUNT();
+END;
+
+FUNCTION string(M IN OUT NOCOPY mal_table, name varchar) RETURN integer IS
+BEGIN
+ M.EXTEND();
+ IF LENGTH(name) <= 4000 THEN
+ M(M.COUNT()) := mal_str_T(5, name);
+ ELSE
+ M(M.COUNT()) := mal_long_str_T(6, NULL, name);
+ END IF;
+ RETURN M.COUNT();
+END;
+
+FUNCTION string_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean IS
+ str CLOB;
+BEGIN
+ IF M(val).type_id IN (5,6) THEN
+ IF M(val).type_id = 5 THEN
+ str := TREAT(M(val) AS mal_str_T).val_str;
+ ELSE
+ str := TREAT(M(val) AS mal_long_str_T).val_long_str;
+ END IF;
+ IF str IS NULL OR
+ str = EMPTY_CLOB() OR
+ SUBSTR(str, 1, 1) <> chr(127) THEN
+ RETURN TRUE;
+ ELSE
+ RETURN FALSE;
+ END IF;
+ ELSE
+ RETURN FALSE;
+ END IF;
+END;
+
+FUNCTION symbol(M IN OUT NOCOPY mal_table, name varchar) RETURN integer IS
+BEGIN
+ M.EXTEND();
+ M(M.COUNT()) := mal_str_T(7, name);
+ RETURN M.COUNT();
+END;
+
+FUNCTION keyword(M IN OUT NOCOPY mal_table, name varchar) RETURN integer IS
+BEGIN
+ M.EXTEND();
+ M(M.COUNT()) := mal_str_T(5, chr(127) || name);
+ RETURN M.COUNT();
+END;
+
+FUNCTION keyword_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean IS
+ str CLOB;
+BEGIN
+ IF M(val).type_id = 5 THEN
+ str := TREAT(M(val) AS mal_str_T).val_str;
+ IF LENGTH(str) > 0 AND SUBSTR(str, 1, 1) = chr(127) THEN
+ RETURN TRUE;
+ ELSE
+ RETURN FALSE;
+ END IF;
+ ELSE
+ RETURN FALSE;
+ END IF;
+END;
+
+FUNCTION number_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean IS
+ str CLOB;
+BEGIN
+ IF M(val).type_id IN (3,4) THEN
+ RETURN TRUE;
+ ELSE
+ RETURN FALSE;
+ END IF;
+END;
+
+FUNCTION function_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean IS
+ str CLOB;
+BEGIN
+ IF M(val).type_id = 11 THEN
+ RETURN TRUE;
+ ELSIF M(val).type_id = 12 THEN
+ RETURN TREAT(M(val) AS mal_func_T).is_macro = 0;
+ ELSE
+ RETURN FALSE;
+ END IF;
+END;
+
+FUNCTION macro_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean IS
+ str CLOB;
+BEGIN
+ IF M(val).type_id = 12 THEN
+ RETURN TREAT(M(val) AS mal_func_T).is_macro > 0;
+ ELSE
+ RETURN FALSE;
+ END IF;
+END;
+
+
+-- ---------------------------------------------------------
+-- sequence functions
+
+FUNCTION seq(M IN OUT NOCOPY mal_table,
+ type_id integer,
+ items mal_vals,
+ meta integer DEFAULT 1) RETURN integer IS
+BEGIN
+ M.EXTEND();
+ M(M.COUNT()) := mal_seq_T(type_id, items, meta);
+ RETURN M.COUNT();
+END;
+
+-- list:
+-- return a mal list
+FUNCTION list(M IN OUT NOCOPY mal_table) RETURN integer IS
+BEGIN
+ M.EXTEND();
+ M(M.COUNT()) := mal_seq_T(8, mal_vals(), 1);
+ RETURN M.COUNT();
+END;
+
+FUNCTION list(M IN OUT NOCOPY mal_table,
+ a integer) RETURN integer IS
+BEGIN
+ M.EXTEND();
+ M(M.COUNT()) := mal_seq_T(8, mal_vals(a), 1);
+ RETURN M.COUNT();
+END;
+
+FUNCTION list(M IN OUT NOCOPY mal_table,
+ a integer, b integer) RETURN integer IS
+BEGIN
+ M.EXTEND();
+ M(M.COUNT()) := mal_seq_T(8, mal_vals(a, b), 1);
+ RETURN M.COUNT();
+END;
+
+FUNCTION list(M IN OUT NOCOPY mal_table,
+ a integer, b integer, c integer) RETURN integer IS
+BEGIN
+ M.EXTEND();
+ M(M.COUNT()) := mal_seq_T(8, mal_vals(a, b, c), 1);
+ RETURN M.COUNT();
+END;
+
+FUNCTION first(M IN OUT NOCOPY mal_table,
+ seq integer) RETURN integer IS
+BEGIN
+ RETURN TREAT(M(seq) AS mal_seq_T).val_seq(1);
+END;
+
+FUNCTION slice(M IN OUT NOCOPY mal_table,
+ seq integer,
+ idx integer,
+ last integer DEFAULT NULL) RETURN integer IS
+ old_items mal_vals;
+ new_items mal_vals;
+ i integer;
+ final_idx integer;
+BEGIN
+ old_items := TREAT(M(seq) AS mal_seq_T).val_seq;
+ new_items := mal_vals();
+ IF last IS NULL THEN
+ final_idx := old_items.COUNT();
+ ELSE
+ final_idx := last + 1;
+ END IF;
+ IF final_idx > idx THEN
+ new_items.EXTEND(final_idx - idx);
+ FOR i IN idx+1..final_idx LOOP
+ new_items(i-idx) := old_items(i);
+ END LOOP;
+ END IF;
+ M.EXTEND();
+ M(M.COUNT()) := mal_seq_T(8, new_items, 1);
+ RETURN M.COUNT();
+END;
+
+FUNCTION slice(M IN OUT NOCOPY mal_table,
+ items mal_vals,
+ idx integer) RETURN integer IS
+ new_items mal_vals;
+BEGIN
+ new_items := islice(items, idx);
+ M.EXTEND();
+ M(M.COUNT()) := mal_seq_T(8, new_items, 1);
+ RETURN M.COUNT();
+END;
+
+FUNCTION islice(items mal_vals,
+ idx integer) RETURN mal_vals IS
+ new_items mal_vals;
+ i integer;
+BEGIN
+ new_items := mal_vals();
+ IF items.COUNT > idx THEN
+ new_items.EXTEND(items.COUNT - idx);
+ FOR i IN idx+1..items.COUNT LOOP
+ new_items(i-idx) := items(i);
+ END LOOP;
+ END IF;
+ RETURN new_items;
+END;
+
+
+FUNCTION nth(M IN OUT NOCOPY mal_table,
+ seq integer, idx integer) RETURN integer IS
+BEGIN
+ RETURN TREAT(M(seq) AS mal_seq_T).val_seq(idx+1);
+END;
+
+FUNCTION count(M IN OUT NOCOPY mal_table,
+ seq integer) RETURN integer IS
+BEGIN
+ RETURN TREAT(M(seq) AS mal_seq_T).val_seq.COUNT;
+END;
+
+-- ---------------------------------------------------------
+-- hash-map functions
+
+FUNCTION assoc_BANG(M IN OUT NOCOPY mal_table,
+ H IN OUT NOCOPY map_entry_table,
+ midx integer,
+ kvs mal_vals) RETURN integer IS
+ i integer;
+BEGIN
+ IF MOD(kvs.COUNT(), 2) = 1 THEN
+ raise_application_error(-20007,
+ 'odd number of arguments to assoc', TRUE);
+ END IF;
+
+ i := 1;
+ WHILE i <= kvs.COUNT() LOOP
+ H(midx)(TREAT(M(kvs(i)) AS mal_str_T).val_str) := kvs(i+1);
+ i := i + 2;
+ END LOOP;
+ RETURN midx;
+END;
+
+FUNCTION dissoc_BANG(M IN OUT NOCOPY mal_table,
+ H IN OUT NOCOPY map_entry_table,
+ midx integer,
+ ks mal_vals) RETURN integer IS
+ i integer;
+BEGIN
+ FOR i IN 1..ks.COUNT() LOOP
+ H(midx).DELETE(TREAT(M(ks(i)) AS mal_str_T).val_str);
+ END LOOP;
+ RETURN midx;
+END;
+
+FUNCTION hash_map(M IN OUT NOCOPY mal_table,
+ H IN OUT NOCOPY map_entry_table,
+ kvs mal_vals,
+ meta integer DEFAULT 1) RETURN integer IS
+ midx integer;
+BEGIN
+ H.EXTEND();
+ midx := H.COUNT();
+ midx := assoc_BANG(M, H, midx, kvs);
+
+ M.EXTEND();
+ M(M.COUNT()) := mal_map_T(10, midx, meta);
+ RETURN M.COUNT();
+END;
+
+
+-- ---------------------------------------------------------
+-- function functions
+
+FUNCTION func(M IN OUT NOCOPY mal_table, name varchar) RETURN integer IS
+BEGIN
+ M.EXTEND();
+ M(M.COUNT()) := mal_str_T(11, name);
+ RETURN M.COUNT();
+END;
+
+FUNCTION malfunc(M IN OUT NOCOPY mal_table,
+ ast integer,
+ params integer,
+ env integer,
+ is_macro integer DEFAULT 0,
+ meta integer DEFAULT 1) RETURN integer IS
+BEGIN
+ M.EXTEND();
+ M(M.COUNT()) := mal_func_T(12, ast, params, env, is_macro, meta);
+ RETURN M.COUNT();
+END;
+
+
+-- ---------------------------------------------------------
+-- atom functions
+
+FUNCTION atom_new(M IN OUT NOCOPY mal_table,
+ val integer) RETURN integer IS
+ aidx integer;
+BEGIN
+ M.EXTEND();
+ M(M.COUNT()) := mal_atom_T(13, val);
+ RETURN M.COUNT();
+END;
+
+FUNCTION atom_reset(M IN OUT NOCOPY mal_table,
+ atm integer,
+ val integer) RETURN integer IS
+BEGIN
+ M(atm) := mal_atom_T(13, val);
+ RETURN val;
+END;
+
+
+
+END types;
+/
+show errors;