Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plpgsql: merge macroexpand into EVAL, add DEBUG-EVAL #705

Merged
merged 3 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion impls/plpgsql/core.sql
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ DECLARE
BEGIN
fname := types._valueToString(args[1]);
IF fname NOT LIKE '/%' THEN
fname := types._valueToString(envs.vget(0, '*PWD*')) || '/' || fname;
fname := types._valueToString(envs.get(0, '*PWD*')) || '/' || fname;
END IF;

tmp := CAST(round(random()*1000000) AS varchar);
Expand Down
38 changes: 8 additions & 30 deletions impls/plpgsql/envs.sql
Original file line number Diff line number Diff line change
Expand Up @@ -91,43 +91,21 @@ BEGIN
RETURN envs.vset(env, symkey, val);
END; $$ LANGUAGE plpgsql;

-- envs.find
CREATE FUNCTION envs.find(env integer, symkey varchar) RETURNS integer AS $$
-- envs.get
CREATE FUNCTION envs.get(env integer, symkey varchar) RETURNS integer AS $$
DECLARE
outer_id integer;
d hstore;
val integer;
BEGIN
LOOP
SELECT e.data, e.outer_id INTO d, outer_id FROM envs.env e
WHERE e.env_id = env;
IF d ? symkey THEN
RETURN env;
ELSIF outer_id IS NOT NULL THEN
RETURN envs.find(outer_id, symkey);
ELSE
RETURN NULL;
RETURN d -> symkey;
END IF;
END; $$ LANGUAGE plpgsql;


-- envs.vget
CREATE FUNCTION envs.vget(env integer, symkey varchar) RETURNS integer AS $$
DECLARE
result integer;
e integer;
BEGIN
e := envs.find(env, symkey);
--RAISE NOTICE 'envs.find env: %, symkey: % -> e: %', env, symkey, e;
IF e IS NULL THEN
RAISE EXCEPTION '''%'' not found', symkey;
ELSE
SELECT data -> symkey INTO result FROM envs.env WHERE env_id = e;
env := outer_id;
IF env IS NULL THEN
RETURN NULL;
END IF;
RETURN result;
END; $$ LANGUAGE plpgsql;

-- envs.get
CREATE FUNCTION envs.get(env integer, key integer) RETURNS integer AS $$
BEGIN
RETURN envs.vget(env, types._valueToString(key));
END LOOP;
END; $$ LANGUAGE plpgsql;
2 changes: 1 addition & 1 deletion impls/plpgsql/reader.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ CREATE SCHEMA reader;

CREATE FUNCTION reader.tokenize(str varchar) RETURNS varchar[] AS $$
DECLARE
re varchar = E'[[:space:] ,]*(~@|[\\[\\]{}()\'`~@]|"(?:[\\\\].|[^\\\\"])*"?|;[^\n]*|[^\\s \\[\\]{}()\'"`~@,;]*)';
re varchar = E'[[:space:] ,]*(~@|[\\[\\]{}()\'`~@^]|"(?:[\\\\].|[^\\\\"])*"?|;[^\n]*|[^\\s \\[\\]{}()\'"`~@,;^]*)';
BEGIN
RETURN ARRAY(SELECT tok FROM
(SELECT (regexp_matches(str, re, 'g'))[1] AS tok) AS x
Expand Down
84 changes: 46 additions & 38 deletions impls/plpgsql/step2_eval.sql
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,45 @@ BEGIN
END; $$ LANGUAGE plpgsql;

-- eval
CREATE FUNCTION mal.eval_ast(ast integer, env hstore) RETURNS integer AS $$
DECLARE
type integer;
symkey varchar;
seq integer[];
eseq integer[];
hash hstore;
ehash hstore;
kv RECORD;
e integer;
result integer;
BEGIN
SELECT type_id INTO type FROM types.value WHERE value_id = ast;
CASE
WHEN type = 7 THEN

CREATE FUNCTION mal.eval_symbol(ast integer, env hstore) RETURNS integer
AS $$
DECLARE
symkey constant varchar := types._valueToString(ast);
BEGIN
symkey := types._valueToString(ast);
IF env ? symkey THEN
result := env -> symkey;
RETURN env -> symkey;
ELSE
RAISE EXCEPTION '''%'' not found', symkey;
END IF;
END;
WHEN type IN (8, 9) THEN
$$ LANGUAGE plpgsql;

CREATE FUNCTION mal.eval_vector(ast integer, env hstore) RETURNS integer
AS $$
DECLARE
seq constant integer[] := types._valueToArray(ast);
eseq integer[];
result integer;
BEGIN
SELECT val_seq INTO seq FROM types.value WHERE value_id = ast;
-- Evaluate each entry creating a new sequence
FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP
eseq[i] := mal.EVAL(seq[i], env);
END LOOP;
INSERT INTO types.value (type_id, val_seq) VALUES (type, eseq)
INSERT INTO types.value (type_id, val_seq) VALUES (9, eseq)
RETURNING value_id INTO result;
RETURN result;
END;
WHEN type = 10 THEN
$$ LANGUAGE plpgsql;

CREATE FUNCTION mal.eval_map(ast integer, env hstore) RETURNS integer
AS $$
DECLARE
hash hstore;
ehash hstore;
kv RECORD;
e integer;
result integer;
BEGIN
SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;
-- Evaluate each value for every key/value
Expand All @@ -63,36 +68,39 @@ BEGIN
ehash := ehash || hstore(kv.key, CAST(e AS varchar));
END IF;
END LOOP;
INSERT INTO types.value (type_id, val_hash) VALUES (type, ehash)
INSERT INTO types.value (type_id, val_hash) VALUES (10, ehash)
RETURNING value_id INTO result;
RETURN result;
END;
ELSE
result := ast;
END CASE;

RETURN result;
END; $$ LANGUAGE plpgsql;
$$ LANGUAGE plpgsql;

CREATE FUNCTION mal.EVAL(ast integer, env hstore) RETURNS integer AS $$
DECLARE
type integer;
el integer;
a0 integer;
fname varchar;
args integer[];
args integer[] := ARRAY[]::integer[];
evda0 integer;
result integer;
BEGIN
SELECT type_id INTO type FROM types.value WHERE value_id = ast;
IF type <> 8 THEN
RETURN mal.eval_ast(ast, env);
END IF;
CASE type_id FROM types.value WHERE value_id = ast
WHEN 7 THEN RETURN mal.eval_symbol(ast, env);
WHEN 8 THEN NULL; -- List, proceed after this case statement.
WHEN 9 THEN RETURN mal.eval_vector(ast, env);
WHEN 10 THEN RETURN mal.eval_map(ast, env);
ELSE RETURN ast;
END CASE;

IF types._count(ast) = 0 THEN
RETURN ast;
END IF;

el := mal.eval_ast(ast, env);
a0 := types._first(ast);
evda0 := mal.EVAL(a0, env);
SELECT val_string INTO fname FROM types.value
WHERE value_id = types._first(el);
args := types._restArray(el);
WHERE value_id = evda0;
FOR i in 1 .. types._count(ast) - 1 LOOP
args[i] := mal.EVAL(types._nth(ast, i), env);
END LOOP;
EXECUTE format('SELECT %s($1);', fname) INTO result USING args;
RETURN result;
END; $$ LANGUAGE plpgsql;
Expand Down
133 changes: 75 additions & 58 deletions impls/plpgsql/step3_env.sql
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,57 @@ BEGIN
END; $$ LANGUAGE plpgsql;

-- eval
CREATE FUNCTION mal.eval_ast(ast integer, env integer) RETURNS integer AS $$

CREATE FUNCTION mal.eval_debug(ast integer, env integer) RETURNS void AS $$
DECLARE
type integer;
seq integer[];
eseq integer[];
hash hstore;
ehash hstore;
kv RECORD;
e integer;
result integer;
val constant integer := envs.get(env, 'DEBUG-EVAL');
BEGIN
SELECT type_id INTO type FROM types.value WHERE value_id = ast;
CASE
WHEN type = 7 THEN
IF val IS NOT NULL THEN
IF (SELECT type_id FROM types.value WHERE value_id = val) NOT IN (0, 1)
THEN
PERFORM io.writeline(format('EVAL: %s [%s]', mal.PRINT(ast), ast));
END IF;
END IF;
END; $$ LANGUAGE plpgsql;

CREATE FUNCTION mal.eval_symbol(ast integer, env integer) RETURNS integer
AS $$
DECLARE
symkey constant varchar := types._valueToString(ast);
result constant integer := envs.get(env, symkey);
BEGIN
result := envs.get(env, ast);
IF result IS NULL THEN
RAISE EXCEPTION '''%'' not found', symkey;
END IF;
RETURN result;
END;
WHEN type IN (8, 9) THEN
$$ LANGUAGE plpgsql;

CREATE FUNCTION mal.eval_vector(ast integer, env integer) RETURNS integer
AS $$
DECLARE
seq constant integer[] := types._valueToArray(ast);
eseq integer[];
result integer;
BEGIN
SELECT val_seq INTO seq FROM types.value WHERE value_id = ast;
-- Evaluate each entry creating a new sequence
FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP
eseq[i] := mal.EVAL(seq[i], env);
END LOOP;
INSERT INTO types.value (type_id, val_seq) VALUES (type, eseq)
INSERT INTO types.value (type_id, val_seq) VALUES (9, eseq)
RETURNING value_id INTO result;
RETURN result;
END;
WHEN type = 10 THEN
$$ LANGUAGE plpgsql;

CREATE FUNCTION mal.eval_map(ast integer, env integer) RETURNS integer
AS $$
DECLARE
hash hstore;
ehash hstore;
kv RECORD;
e integer;
result integer;
BEGIN
SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;
-- Evaluate each value for every key/value
Expand All @@ -58,76 +81,70 @@ BEGIN
ehash := ehash || hstore(kv.key, CAST(e AS varchar));
END IF;
END LOOP;
INSERT INTO types.value (type_id, val_hash) VALUES (type, ehash)
INSERT INTO types.value (type_id, val_hash) VALUES (10, ehash)
RETURNING value_id INTO result;
RETURN result;
END;
ELSE
result := ast;
END CASE;

RETURN result;
END; $$ LANGUAGE plpgsql;
$$ LANGUAGE plpgsql;

CREATE FUNCTION mal.EVAL(ast integer, env integer) RETURNS integer AS $$
DECLARE
type integer;
a0 integer;
a0sym varchar;
a1 integer;
let_env integer;
idx integer;
binds integer[];
el integer;
fname varchar;
args integer[];
result integer;
BEGIN
-- PERFORM writeline(format('EVAL: %s [%s]', pr_str(ast), ast));
SELECT type_id INTO type FROM types.value WHERE value_id = ast;
IF type <> 8 THEN
RETURN mal.eval_ast(ast, env);
END IF;
PERFORM mal.eval_debug(ast, env);

CASE type_id FROM types.value WHERE value_id = ast
WHEN 7 THEN RETURN mal.eval_symbol(ast, env);
WHEN 8 THEN NULL; -- List, proceed after this case statement.
WHEN 9 THEN RETURN mal.eval_vector(ast, env);
WHEN 10 THEN RETURN mal.eval_map(ast, env);
ELSE RETURN ast;
END CASE;

IF types._count(ast) = 0 THEN
RETURN ast;
END IF;

a0 := types._first(ast);
IF types._symbol_Q(a0) THEN
a0sym := (SELECT val_string FROM types.value WHERE value_id = a0);
ELSE
a0sym := '__<*fn*>__';
END IF;

CASE
WHEN a0sym = 'def!' THEN
BEGIN
CASE val_string FROM types.value WHERE value_id = a0

WHEN 'def!' THEN
RETURN envs.set(env, types._nth(ast, 1),
mal.EVAL(types._nth(ast, 2), env));
END;
WHEN a0sym = 'let*' THEN

WHEN 'let*' THEN
DECLARE
let_env constant integer := envs.new(env);
binds constant integer[] := types._valueToArray(types._nth(ast, 1));
BEGIN
let_env := envs.new(env);
a1 := types._nth(ast, 1);
binds := (SELECT val_seq FROM types.value WHERE value_id = a1);
idx := 1;
WHILE idx < array_length(binds, 1) LOOP
FOR idx IN 1 .. array_length(binds, 1) BY 2 LOOP
PERFORM envs.set(let_env, binds[idx],
mal.EVAL(binds[idx+1], let_env));
idx := idx + 2;
END LOOP;
RETURN mal.EVAL(types._nth(ast, 2), let_env);
END;
ELSE
NULL;
END CASE;
END IF;
-- Apply phase.
DECLARE
fname varchar;
args integer[] := ARRAY[]::integer[];
result integer;
evda0 constant integer := mal.EVAL(a0, env);
BEGIN
el := mal.eval_ast(ast, env);
SELECT val_string INTO fname FROM types.value
WHERE value_id = types._first(el);
args := types._restArray(el);
WHERE value_id = evda0;
FOR i in 1 .. types._count(ast) - 1 LOOP
args[i] := mal.EVAL(types._nth(ast, i), env);
END LOOP;
EXECUTE format('SELECT %s($1);', fname)
INTO result USING args;
RETURN result;
END;
END CASE;
END; $$ LANGUAGE plpgsql;

-- print
Expand Down
Loading
Loading