1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-21 10:37:58 +03:00
mal/plpgsql/step2_eval.sql
2016-05-02 23:37:15 -05:00

153 lines
5.0 KiB
PL/PgSQL

\i init.sql
\i types.sql
\i reader.sql
\i printer.sql
-- ---------------------------------------------------------
-- step1_read_print.sql
-- read
CREATE OR REPLACE FUNCTION READ(line varchar) RETURNS integer AS $$
BEGIN
RETURN read_str(line);
END; $$ LANGUAGE plpgsql;
-- eval
CREATE OR REPLACE FUNCTION just_add(args integer[]) RETURNS integer AS $$
BEGIN RETURN args[1] + args[2]; END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION eval_ast(ast integer, env integer) RETURNS integer AS $$
DECLARE
type integer;
symkey varchar;
vid integer;
i integer;
src_coll_id integer;
dst_coll_id integer = NULL;
e integer;
result integer;
BEGIN
SELECT type_id INTO type FROM value WHERE value_id = ast;
CASE
WHEN type = 7 THEN
BEGIN
symkey := _vstring(ast);
SELECT e.value_id FROM env e INTO result
WHERE e.env_id = env
AND e.key = symkey;
IF result IS NULL THEN
RAISE EXCEPTION '''%'' not found', symkey;
END IF;
END;
WHEN type = 8 OR type = 9 THEN
BEGIN
src_coll_id := (SELECT collection_id FROM value WHERE value_id = ast);
FOR vid, i IN (SELECT value_id, idx FROM collection
WHERE collection_id = src_coll_id)
LOOP
e := EVAL(vid, env);
IF dst_coll_id IS NULL THEN
dst_coll_id := COALESCE((SELECT Max(collection_id)
FROM collection)+1,0);
END IF;
-- Evaluated each entry
INSERT INTO collection (collection_id, idx, value_id)
VALUES (dst_coll_id, i, e);
END LOOP;
-- Create value entry pointing to new collection
INSERT INTO value (type_id, collection_id)
VALUES (type, dst_coll_id)
RETURNING value_id INTO result;
END;
ELSE
result := ast;
END CASE;
RETURN result;
END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION EVAL(ast integer, env integer) RETURNS integer AS $$
DECLARE
type integer;
el integer;
fname varchar;
args integer[];
result integer;
BEGIN
SELECT type_id INTO type FROM value WHERE value_id = ast;
IF type <> 8 THEN
RETURN eval_ast(ast, env);
END IF;
el := eval_ast(ast, env);
SELECT function_name INTO fname FROM value WHERE value_id = _first(el);
args := _restArray(el);
-- RAISE NOTICE 'fname: %, args: %', fname, args;
EXECUTE format('SELECT %s($1);', fname)
INTO result USING args;
RETURN result;
END; $$ LANGUAGE plpgsql;
-- print
CREATE OR REPLACE FUNCTION PRINT(exp integer) RETURNS varchar AS $$
BEGIN
RETURN pr_str(exp);
END; $$ LANGUAGE plpgsql;
-- repl
-- env table
CREATE TABLE env (
env_id integer NOT NULL,
key varchar NOT NULL,
value_id integer NOT NULL
);
CREATE OR REPLACE FUNCTION env_vset(env integer, name varchar, val integer)
RETURNS void AS $$
BEGIN
INSERT INTO env (env_id, key, value_id) VALUES (env, name, val);
END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION mal_intop(op varchar, args integer[]) RETURNS integer AS $$
DECLARE a integer; b integer; result integer;
BEGIN
SELECT val_int INTO a FROM value WHERE value_id = args[1];
SELECT val_int INTO b FROM value WHERE value_id = args[2];
EXECUTE format('INSERT INTO value (type_id, val_int) VALUES (3, $1 %s $2)
RETURNING value_id;', op) INTO result USING a, b;
RETURN result;
END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION mal_add(args integer[]) RETURNS integer AS $$
BEGIN RETURN mal_intop('+', args); END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION mal_subtract(args integer[]) RETURNS integer AS $$
BEGIN RETURN mal_intop('-', args); END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION mal_multiply(args integer[]) RETURNS integer AS $$
BEGIN RETURN mal_intop('*', args); END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION mal_divide(args integer[]) RETURNS integer AS $$
BEGIN RETURN mal_intop('/', args); END; $$ LANGUAGE plpgsql;
INSERT INTO value (type_id, function_name) VALUES (11, 'mal_add');
INSERT INTO value (type_id, function_name) VALUES (11, 'mal_subtract');
INSERT INTO value (type_id, function_name) VALUES (11, 'mal_multiply');
INSERT INTO value (type_id, function_name) VALUES (11, 'mal_divide');
-- repl_env is environment 0
SELECT env_vset(0, '+', (SELECT value_id FROM value WHERE function_name = 'mal_add'));
SELECT env_vset(0, '-', (SELECT value_id FROM value WHERE function_name = 'mal_subtract'));
SELECT env_vset(0, '*', (SELECT value_id FROM value WHERE function_name = 'mal_multiply'));
SELECT env_vset(0, '/', (SELECT value_id FROM value WHERE function_name = 'mal_divide'));
CREATE OR REPLACE FUNCTION REP(line varchar) RETURNS varchar AS $$
DECLARE
output varchar;
BEGIN
-- RAISE NOTICE 'line is %', line;
-- output := 'line: ' || line;
RETURN PRINT(EVAL(READ(line), 0));
END; $$ LANGUAGE plpgsql;