Fix unescaping in c, coffee, crystal, haxe, plpgsql and r.
[jackhill/mal.git] / plpgsql / reader.sql
index dbe599a..2368df2 100644 (file)
@@ -1,21 +1,22 @@
 -- ---------------------------------------------------------
 -- reader.sql
 
-CREATE OR REPLACE FUNCTION
-    tokenize(str varchar) RETURNS varchar[] AS $$
+CREATE SCHEMA reader;
+
+CREATE FUNCTION reader.tokenize(str varchar) RETURNS varchar[] AS $$
 DECLARE
     re varchar = E'[[:space:] ,]*(~@|[\\[\\]{}()\'`~@]|"(?:[\\\\].|[^\\\\"])*"|;[^\n]*|[^\\s \\[\\]{}()\'"`~@,;]*)';
 BEGIN
     RETURN ARRAY(SELECT tok FROM
         (SELECT (regexp_matches(str, re, 'g'))[1] AS tok) AS x
         WHERE tok <> '' AND tok NOT LIKE ';%');
-END; $$ LANGUAGE plpgsql;
+END; $$ LANGUAGE plpgsql IMMUTABLE;
 
 -- read_atom:
 -- takes a tokens array and position
 -- returns new position and value_id
-CREATE OR REPLACE FUNCTION
-    read_atom(tokens varchar[], INOUT pos integer, OUT result integer) AS $$
+CREATE FUNCTION reader.read_atom(tokens varchar[],
+    INOUT pos integer, OUT result integer) AS $$
 DECLARE
     str_id  integer;
     str     varchar;
@@ -24,74 +25,70 @@ BEGIN
     token := tokens[pos];
     pos := pos + 1;
     -- RAISE NOTICE 'read_atom: %', token;
-    IF token = 'nil' THEN
+    IF token = 'nil' THEN       -- nil
         result := 0;
-    ELSIF token = 'false' THEN
+    ELSIF token = 'false' THEN  -- false
         result := 1;
-    ELSIF token = 'true' THEN
+    ELSIF token = 'true' THEN   -- true
         result := 2;
-    ELSIF token ~ '^-?[0-9][0-9]*$' THEN
+    ELSIF token ~ '^-?[0-9][0-9]*$' THEN  -- integer
         -- integer
-        INSERT INTO value (type_id, val_int)
+        INSERT INTO types.value (type_id, val_int)
             VALUES (3, CAST(token AS integer))
             RETURNING value_id INTO result;
-    ELSIF token ~ '^".*"' THEN
+    ELSIF token ~ '^".*"' THEN  -- string
         -- string
         str := substring(token FROM 2 FOR (char_length(token)-2));
+        str := replace(str, '\\', chr(CAST(x'7f' AS integer)));
         str := replace(str, '\"', '"');
         str := replace(str, '\n', E'\n');
-        str := replace(str, '\\', E'\\');
-        result := _stringv(str);
+        str := replace(str, chr(CAST(x'7f' AS integer)), E'\\');
+        result := types._stringv(str);
+    ELSIF token ~ '^:.*' THEN  -- keyword
+        -- keyword
+        result := types._keywordv(substring(token FROM 2 FOR (char_length(token)-1)));
     ELSE
         -- symbol
-        result := _symbolv(token);
+        result := types._symbolv(token);
     END IF;
 END; $$ LANGUAGE plpgsql;
 
 -- read_seq:
--- takes a tokens array, type (8,9), first and last characters and position
--- returns new position and value_id (or a list)
-CREATE OR REPLACE FUNCTION
-    read_seq(tokens varchar[], type integer, first varchar, last varchar,
-             INOUT pos integer, OUT result integer) AS $$
+-- takes a tokens array, type (8, 9, 10), first and last characters
+-- and position
+-- returns new position and value_id for a list (8), vector (9) or
+-- hash-map (10)
+CREATE FUNCTION reader.read_seq(tokens varchar[], first varchar, last varchar,
+    INOUT p integer, OUT items integer[]) AS $$
 DECLARE
-    list_id   integer = NULL;
     token     varchar;
-    idx       integer = 0;
+    key       varchar = NULL;
     item_id   integer;
 BEGIN
-    token := tokens[pos];
-    pos := pos + 1;
+    token := tokens[p];
+    p := p + 1;
     IF token <> first THEN
         RAISE EXCEPTION 'expected ''%''', first;
     END IF;
+    items := ARRAY[]::integer[];
     LOOP
-        IF pos > array_length(tokens, 1) THEN
+        IF p > array_length(tokens, 1) THEN
             RAISE EXCEPTION 'expected ''%''', last;
         END IF;
-        token := tokens[pos];
+        token := tokens[p];
         IF token = last THEN EXIT; END IF;
-        SELECT * FROM read_form(tokens, pos) INTO pos, item_id;
-        IF list_id IS NULL THEN
-            list_id := (SELECT COALESCE(Max(collection_id), 0) FROM collection)+1;
-        END IF;
-        INSERT INTO collection (collection_id, idx, value_id)
-            VALUES (list_id, idx, item_id);
-        idx := idx + 1;
+        SELECT * FROM reader.read_form(tokens, p) INTO p, item_id;
+        items := array_append(items, item_id);
     END LOOP;
 
-    -- Create new list referencing list_id
-    INSERT INTO value (type_id, collection_id)
-        VALUES (type, list_id)
-        RETURNING value_id INTO result;
-    pos := pos + 1;
+    p := p + 1;
 END; $$ LANGUAGE plpgsql;
 
 -- read_form:
 -- takes a tokens array and position
 -- returns new position and value_id
-CREATE OR REPLACE FUNCTION
-    read_form(tokens varchar[], INOUT pos integer, OUT result integer) AS $$
+CREATE FUNCTION reader.read_form(tokens varchar[],
+    INOUT pos integer, OUT result integer) AS $$
 DECLARE
     vid     integer;
     meta    integer;
@@ -102,39 +99,39 @@ BEGIN
     WHEN token = '''' THEN
     BEGIN
         pos := pos + 1;
-        SELECT * FROM read_form(tokens, pos) INTO pos, vid;
-        result := _list(ARRAY[_symbolv('quote'), vid]);
+        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;
+        result := types._list(ARRAY[types._symbolv('quote'), vid]);
     END;
     WHEN token = '`' THEN
     BEGIN
         pos := pos + 1;
-        SELECT * FROM read_form(tokens, pos) INTO pos, vid;
-        result := _list(ARRAY[_symbolv('quasiquote'), vid]);
+        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;
+        result := types._list(ARRAY[types._symbolv('quasiquote'), vid]);
     END;
     WHEN token = '~' THEN
     BEGIN
         pos := pos + 1;
-        SELECT * FROM read_form(tokens, pos) INTO pos, vid;
-        result := _list(ARRAY[_symbolv('unquote'), vid]);
+        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;
+        result := types._list(ARRAY[types._symbolv('unquote'), vid]);
     END;
     WHEN token = '~@' THEN
     BEGIN
         pos := pos + 1;
-        SELECT * FROM read_form(tokens, pos) INTO pos, vid;
-        result := _list(ARRAY[_symbolv('splice-unquote'), vid]);
+        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;
+        result := types._list(ARRAY[types._symbolv('splice-unquote'), vid]);
     END;
     WHEN token = '^' THEN
     BEGIN
         pos := pos + 1;
-        SELECT * FROM read_form(tokens, pos) INTO pos, meta;
-        SELECT * FROM read_form(tokens, pos) INTO pos, vid;
-        result := _list(ARRAY[_symbolv('with-meta'), vid, meta]);
+        SELECT * FROM reader.read_form(tokens, pos) INTO pos, meta;
+        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;
+        result := types._list(ARRAY[types._symbolv('with-meta'), vid, meta]);
     END;
     WHEN token = '@' THEN
     BEGIN
         pos := pos + 1;
-        SELECT * FROM read_form(tokens, pos) INTO pos, vid;
-        result := _list(ARRAY[_symbolv('deref'), vid]);
+        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;
+        result := types._list(ARRAY[types._symbolv('deref'), vid]);
     END;
 
     -- list
@@ -142,7 +139,8 @@ BEGIN
         RAISE EXCEPTION 'unexpected '')''';
     WHEN token = '(' THEN
     BEGIN
-        SELECT * FROM read_seq(tokens, 8, '(', ')', pos) INTO pos, result;
+        SELECT p, types._list(items)
+            FROM reader.read_seq(tokens, '(', ')', pos) INTO pos, result;
     END;
 
     -- vector
@@ -150,29 +148,38 @@ BEGIN
         RAISE EXCEPTION 'unexpected '']''';
     WHEN token = '[' THEN
     BEGIN
-        SELECT * FROM read_seq(tokens, 9, '[', ']', pos) INTO pos, result;
+        SELECT p, types._vector(items)
+            FROM reader.read_seq(tokens, '[', ']', pos) INTO pos, result;
+    END;
+
+    -- hash-map
+    WHEN token = '}' THEN
+        RAISE EXCEPTION 'unexpected ''}''';
+    WHEN token = '{' THEN
+    BEGIN
+        SELECT p, types._hash_map(items)
+            FROM reader.read_seq(tokens, '{', '}', pos) INTO pos, result;
     END;
 
     --
     ELSE
-        SELECT * FROM read_atom(tokens, pos) INTO pos, result;
+        SELECT * FROM reader.read_atom(tokens, pos) INTO pos, result;
     END CASE;
 END; $$ LANGUAGE plpgsql;
 
 -- read_str:
 -- takes a string
 -- returns a new value_id
-CREATE OR REPLACE FUNCTION
-    read_str(str varchar) RETURNS integer AS $$
+CREATE FUNCTION reader.read_str(str varchar) RETURNS integer AS $$
 DECLARE
     tokens varchar[];
     pos    integer;
     ast    integer;
 BEGIN
-    tokens := tokenize(str);
+    tokens := reader.tokenize(str);
     -- RAISE NOTICE 'read_str first: %', tokens[1];
     pos := 1;
-    SELECT * FROM read_form(tokens, pos) INTO pos, ast;
+    SELECT * FROM reader.read_form(tokens, pos) INTO pos, ast;
     -- RAISE NOTICE 'pos after read_atom: %', pos;
     RETURN ast;
 END; $$ LANGUAGE plpgsql;