From c58441b29c3715f092e7f3747bb3ec65ae5a3d29 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Sun, 1 Nov 2020 11:36:48 +0000 Subject: [PATCH] Various SNBT parsing improvements Correctly handle: - Typed arrays ([I; 1, 2, 3]) - All suffixed numbers (1.2d) - Single-quoted strings Fixes #559 --- .../computercraft/lua/rom/apis/textutils.lua | 17 +++++++++++----- .../test-rom/spec/apis/textutils_spec.lua | 20 +++++++++++++++++-- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua index 576c6755e..e8e999677 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua @@ -453,13 +453,13 @@ do error_at(pos, "Unexpected %s, expected %s.", actual, exp) end - local function parse_string(str, pos) + local function parse_string(str, pos, terminate) local buf, n = {}, 1 while true do local c = sub(str, pos, pos) if c == "" then error_at(pos, "Unexpected end of input, expected '\"'.") end - if c == '"' then break end + if c == terminate then break end if c == '\\' then -- Handle the various escapes @@ -485,13 +485,13 @@ do return concat(buf, "", 1, n - 1), pos + 1 end - local valid = { b = true, B = true, s = true, S = true, l = true, L = true, f = true, F = true, d = true, D = true } + local num_types = { b = true, B = true, s = true, S = true, l = true, L = true, f = true, F = true, d = true, D = true } local function parse_number(str, pos, opts) local _, last, num_str = find(str, '^(-?%d+%.?%d*[eE]?[+-]?%d*)', pos) local val = tonumber(num_str) if not val then error_at(pos, "Malformed number %q.", num_str) end - if opts.nbt_style and valid[sub(str, pos + 1, pos + 1)] then return val, last + 2 end + if opts.nbt_style and num_types[sub(str, last + 1, last + 1)] then return val, last + 2 end return val, last + 1 end @@ -501,9 +501,11 @@ do return val, last + 1 end + local arr_types = { I = true, L = true, B = true } local function decode_impl(str, pos, opts) local c = sub(str, pos, pos) - if c == '"' then return parse_string(str, pos + 1) + if c == '"' then return parse_string(str, pos + 1, '"') + elseif c == "'" and opts.nbt_style then return parse_string(str, pos + 1, "\'") elseif c == "-" or c >= "0" and c <= "9" then return parse_number(str, pos, opts) elseif c == "t" then if sub(str, pos + 1, pos + 3) == "rue" then return true, pos + 4 end @@ -560,6 +562,11 @@ do pos = skip(str, pos + 1) c = sub(str, pos, pos) + if arr_types[c] and sub(str, pos + 1, pos + 1) == ";" and opts.nbt_style then + pos = skip(str, pos + 2) + c = sub(str, pos, pos) + end + if c == "" then return expected(pos, c, "']'") end if c == "]" then return empty_json_array, pos + 1 end diff --git a/src/test/resources/test-rom/spec/apis/textutils_spec.lua b/src/test/resources/test-rom/spec/apis/textutils_spec.lua index e83746ecf..486bf6118 100644 --- a/src/test/resources/test-rom/spec/apis/textutils_spec.lua +++ b/src/test/resources/test-rom/spec/apis/textutils_spec.lua @@ -126,12 +126,28 @@ describe("The textutils library", function() end) describe("parses using NBT-style syntax", function() + local function exp(x) + local res, err = textutils.unserializeJSON(x, { nbt_style = true }) + if not res then error(err, 2) end + return expect(res) + end it("basic objects", function() - expect(textutils.unserializeJSON([[{ a: 1, b:2 }]], { nbt_style = true })):same { a = 1, b = 2 } + exp([[{ a: 1, b:2 }]]):same { a = 1, b = 2 } end) it("suffixed numbers", function() - expect(textutils.unserializeJSON("1b", { nbt_style = true })):eq(1) + exp("1b"):eq(1) + exp("1.1d"):eq(1.1) + end) + + it("strings", function() + exp("'123'"):eq("123") + exp("\"123\""):eq("123") + end) + + it("typed arrays", function() + exp("[B; 1, 2, 3]"):same { 1, 2, 3 } + exp("[B;]"):same {} end) end)