Tarantool development patches archive
 help / color / mirror / Atom feed
From: Alexander Turenko <alexander.turenko@tarantool.org>
To: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
Cc: Alexander Turenko <alexander.turenko@tarantool.org>,
	tarantool-patches@freelists.org
Subject: [tarantool-patches] [PATCH v2 2/3] lua-yaml: fix boolean/null representation in yaml
Date: Tue, 22 Jan 2019 05:12:53 +0300	[thread overview]
Message-ID: <15b711cb55d34fff1f010fd6df1aa32248f65735.1548123025.git.alexander.turenko@tarantool.org> (raw)
In-Reply-To: <cover.1548123025.git.alexander.turenko@tarantool.org>

Encode changes:

Wrap the following string literals into single quotes: 'false', 'no',
'true', 'yes', '~', 'null', 'Null', 'NULL'. The content of these strings
is equals to null or boolean values representation in YAML.

Reverted the a2d7643c commit to use single quotes instead of multiline
encoding for 'true' and 'false' string literals.

'~', 'null', 'Null', 'NULL', 'no' and 'yes' were encoded w/o quotes
before, so '~', 'null', 'no', 'yes' were decoded with a wrong type then.
Multiline encoding was used for 'true' and 'false'.

Decode changes:

Treat 'Null', 'NULL' as null in addition to '~' and 'null'.

Note: this commit leaves an empty document/value treatment as an empty
string, which is not standard (should be treated as null). This is fixed
in the following commit.

Follows up #3476
Closes #3662
Closes #3583
---
 test/app-tap/console.test.lua | 23 +++++++---
 test/box/misc.result          |  2 +-
 third_party/lua-yaml/lyaml.cc | 80 ++++++++++++++++++++++++++---------
 3 files changed, 80 insertions(+), 25 deletions(-)

diff --git a/test/app-tap/console.test.lua b/test/app-tap/console.test.lua
index 4f915afd7..68f273234 100755
--- a/test/app-tap/console.test.lua
+++ b/test/app-tap/console.test.lua
@@ -21,7 +21,7 @@ local EOL = "\n...\n"
 
 test = tap.test("console")
 
-test:plan(59)
+test:plan(68)
 
 -- Start console and connect to it
 local server = console.listen(CONSOLE_SOCKET)
@@ -77,11 +77,24 @@ test:is(yaml.decode(client:read(EOL)), '', "clear delimiter")
 
 --
 -- gh-3476: yaml.encode encodes 'false' and 'true' incorrectly.
+-- gh-3662: yaml.encode encodes booleans with multiline format.
+-- gh-3583: yaml.encode encodes null incorrectly.
 --
-test:is(type(yaml.decode(yaml.encode('false'))), 'string')
-test:is(type(yaml.decode(yaml.encode('true'))), 'string')
-test:is(type(yaml.decode(yaml.encode({a = 'false'})).a), 'string')
-test:is(type(yaml.decode(yaml.encode({a = 'false'})).a), 'string')
+
+test:is(yaml.encode(false), "--- false\n...\n")
+test:is(yaml.encode(true), "--- true\n...\n")
+test:is(yaml.encode('false'), "--- 'false'\n...\n")
+test:is(yaml.encode('true'), "--- 'true'\n...\n")
+test:is(yaml.encode(nil), "--- null\n...\n")
+
+test:is(yaml.decode('false'), false)
+test:is(yaml.decode('no'), false)
+test:is(yaml.decode('true'), true)
+test:is(yaml.decode('yes'), true)
+test:is(yaml.decode('~'), nil)
+test:is(yaml.decode('null'), nil)
+test:is(yaml.decode('Null'), nil)
+test:is(yaml.decode('NULL'), nil)
 
 box.cfg{
     listen=IPROTO_SOCKET;
diff --git a/test/box/misc.result b/test/box/misc.result
index c3cabcc8a..94ee11b63 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -55,7 +55,7 @@ t = {} for n in pairs(box) do table.insert(t, tostring(n)) end table.sort(t)
 ...
 t
 ---
-- - NULL
+- - 'NULL'
   - atomic
   - backup
   - begin
diff --git a/third_party/lua-yaml/lyaml.cc b/third_party/lua-yaml/lyaml.cc
index 9b07992d8..73e5d2c31 100644
--- a/third_party/lua-yaml/lyaml.cc
+++ b/third_party/lua-yaml/lyaml.cc
@@ -94,6 +94,46 @@ struct lua_yaml_dumper {
    luaL_Buffer yamlbuf;
 };
 
+enum yaml_type {
+   YAML_NO_MATCH = 0,
+   YAML_FALSE,
+   YAML_TRUE,
+   YAML_NULL
+};
+
+/**
+ * Verify whether a string represents a boolean literal in YAML.
+ *
+ * Non-standard: only subset of YAML 1.1 boolean literals are
+ * treated as boolean values.
+ */
+static yaml_type
+yaml_get_bool(const char *str, const size_t len)
+{
+   if (len > 5)
+      return YAML_NO_MATCH;
+   if (strcmp(str, "false") == 0 || strcmp(str, "no") == 0)
+      return YAML_FALSE;
+   if (strcmp(str, "true") == 0 || strcmp(str, "yes") == 0)
+      return YAML_TRUE;
+   return YAML_NO_MATCH;
+}
+
+/**
+ * Verify whether a string represents a null literal in YAML.
+ *
+ * Non-standard: don't match an empty string as null.
+ */
+static yaml_type
+yaml_get_null(const char *str, const size_t len){
+   if (len == 1 && str[0] == '~')
+      return YAML_NULL;
+   if (len == 4 && (strcmp(str, "null") == 0 || strcmp(str, "Null") == 0 ||
+       strcmp(str, "NULL") == 0))
+      return YAML_NULL;
+   return YAML_NO_MATCH;
+}
+
 static void generate_error_message(struct lua_yaml_loader *loader) {
    char buf[256];
    luaL_Buffer b;
@@ -209,7 +249,7 @@ static void load_scalar(struct lua_yaml_loader *loader) {
          lua_pushnumber(loader->L, dval);
          return;
       } else if (!strcmp(tag, "bool")) {
-         lua_pushboolean(loader->L, !strcmp(str, "true") || !strcmp(str, "yes"));
+         lua_pushboolean(loader->L, yaml_get_bool(str, length) == YAML_TRUE);
          return;
       } else if (!strcmp(tag, "binary")) {
          frombase64(loader->L, (const unsigned char *)str, length);
@@ -218,20 +258,20 @@ static void load_scalar(struct lua_yaml_loader *loader) {
    }
 
    if (loader->event.data.scalar.style == YAML_PLAIN_SCALAR_STYLE) {
-      if (!strcmp(str, "~")) {
-         luaL_pushnull(loader->L);
-         return;
-      } else if (!strcmp(str, "true") || !strcmp(str, "yes")) {
-         lua_pushboolean(loader->L, 1);
-         return;
-      } else if (!strcmp(str, "false") || !strcmp(str, "no")) {
-         lua_pushboolean(loader->L, 0);
+      yaml_type type;
+      if (!length) {
+         /*
+          * Non-standard: an empty value/document is null
+          * according to the standard, but we decode it as an
+          * empty string.
+          */
+         lua_pushliteral(loader->L, "");
          return;
-      } else if (!strcmp(str, "null")) {
+      } else if (yaml_get_null(str, length) == YAML_NULL) {
          luaL_pushnull(loader->L);
          return;
-      } else if (!length) {
-         lua_pushliteral(loader->L, "");
+      } else if ((type = yaml_get_bool(str, length)) != YAML_NO_MATCH) {
+         lua_pushboolean(loader->L, type == YAML_TRUE);
          return;
       }
 
@@ -548,7 +588,6 @@ static int yaml_is_flow_mode(struct lua_yaml_dumper *dumper) {
                (evp->type == YAML_MAPPING_START_EVENT &&
                 evp->data.mapping_start.style == YAML_FLOW_MAPPING_STYLE)) {
             return 1;
-            break;
          }
       }
    }
@@ -597,8 +636,13 @@ static int dump_node(struct lua_yaml_dumper *dumper)
       return dump_table(dumper, &field);
    case MP_STR:
       str = lua_tolstring(dumper->L, -1, &len);
-      if (lua_isnumber(dumper->L, -1)) {
-         /* string is convertible to number, quote it to preserve type */
+      if ((yaml_get_null(str, len) == YAML_NULL)
+         || (yaml_get_bool(str, len) != YAML_NO_MATCH)
+         || (lua_isnumber(dumper->L, -1))) {
+         /*
+          * The string is convertible to a null, a boolean or
+          * a number, quote it to preserve its type.
+          */
          style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
          break;
       }
@@ -606,12 +650,10 @@ static int dump_node(struct lua_yaml_dumper *dumper)
       if (utf8_check_printable(str, len)) {
          if (yaml_is_flow_mode(dumper)) {
             style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
-         } else if (strstr(str, "\n\n") != NULL || strcmp(str, "true") == 0 ||
-		    strcmp(str, "false") == 0) {
+         } else if (strstr(str, "\n\n") != 0) {
             /*
              * Tarantool-specific: use literal style for string
-             * with empty lines and strings representing boolean
-             * types.
+             * with empty lines.
              * Useful for tutorial().
              */
             style = YAML_LITERAL_SCALAR_STYLE;
-- 
2.20.1

  parent reply	other threads:[~2019-01-22  2:13 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-22  2:12 [tarantool-patches] [PATCH v2 0/3] lua-yaml null/boolean fixes Alexander Turenko
2019-01-22  2:12 ` [tarantool-patches] [PATCH v2 1/3] lua-yaml: verify arguments count Alexander Turenko
2019-01-24 21:26   ` [tarantool-patches] " Vladislav Shpilevoy
2019-02-05  3:29     ` Alexander Turenko
2019-02-05 19:36       ` Vladislav Shpilevoy
2019-02-11 13:32         ` Alexander Turenko
2019-02-15 21:28           ` Vladislav Shpilevoy
2019-01-22  2:12 ` Alexander Turenko [this message]
2019-01-24 21:26   ` [tarantool-patches] Re: [PATCH v2 2/3] lua-yaml: fix boolean/null representation in yaml Vladislav Shpilevoy
2019-01-24 21:32     ` Vladislav Shpilevoy
2019-02-05  3:29     ` Alexander Turenko
2019-02-05 19:36       ` Vladislav Shpilevoy
2019-02-15 21:06         ` Vladislav Shpilevoy
2019-02-15 21:23           ` Alexander Turenko
2019-02-18 18:55         ` Alexander Turenko
2019-02-22 15:14           ` Vladislav Shpilevoy
2019-01-22  2:12 ` [tarantool-patches] [PATCH v2 3/3] lua-yaml: treat an empty document/value as null Alexander Turenko
2019-01-24 21:26   ` [tarantool-patches] " Vladislav Shpilevoy
2019-02-05  3:30     ` Alexander Turenko
2019-01-24 21:26 ` [tarantool-patches] Re: [PATCH v2 0/3] lua-yaml null/boolean fixes Vladislav Shpilevoy
2019-02-25 11:27 ` Kirill Yukhin
2019-03-05 16:40   ` Alexander Turenko
2019-03-06  7:21     ` Kirill Yukhin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=15b711cb55d34fff1f010fd6df1aa32248f65735.1548123025.git.alexander.turenko@tarantool.org \
    --to=alexander.turenko@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='Re: [tarantool-patches] [PATCH v2 2/3] lua-yaml: fix boolean/null representation in yaml' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox