diff --git a/src/lua/zencode_data.lua b/src/lua/zencode_data.lua index 96d025c0c..45bf6f508 100644 --- a/src/lua/zencode_data.lua +++ b/src/lua/zencode_data.lua @@ -672,3 +672,102 @@ function apply_encoding(src_name, src_enc, dest_enc) if not f_dest_enc then error("Destination encoding format not found: "..dest_enc, 2) end return deepmap(f_dest_enc.fun, encoded_src) end + +-- READ and WRITE to ACK memory + +-- extract an element from deep down +-- works both in IN and ACK memory +-- @param path path to variable separated by points +-- @return element found following the path +-- @return name of the destination +function read_from_path(path, no_dest) + local path_array = strtok(uscore(path), CONF.path.separator) + local root = path_array[1] + table.remove(path_array, 1) + local dest = path_array[#path_array] + if not no_dest then empty(dest) end + -- should works both in given and when phase + -- IN is checked firstly since in the When phase IN will be empty + local res = IN[root] or ACK[root] + for _,v in pairs(path_array) do + zencode_assert(luatype(res) == 'table', "Object is not a table: "..root) + if res[v] == nil then + local v_number = tonumber(v) + zencode_assert(v_number and res[v_number] ~= nil, "Key "..v.." not found in "..root) + res = res[v_number] + else + res = res[v] + end + root = v + end + return res, dest +end + +-- set an element deep down +-- @param path to dest separated by points +-- @param value to set in path +function write_to_path(path, value) + local path_array = strtok(uscore(path), CONF.path.separator) + if #path_array == 0 then + error("write_to_path path input is empty", 2) + end + local current_path = "" + local current = ACK + + for i = 1, #path_array - 1 do + local raw_key = path_array[i] + local key = tonumber(raw_key) or raw_key + current_path = fif(i == 1, "", current_path .. ".") .. raw_key + + -- Current must always be a table + if luatype(current) ~= "table" then + error("Path error at segment '" .. current_path .. "': not a table") + end + -- Handle array case + if luatype(key) == "number" then + local arr_length = isarray(current) + if not arr_length then + error("Path error at '" .. current_path .. "': expected array, found " .. luatype(current[key])) + end + if current[key] == nil then + if key ~= arr_length + 1 then + error("Invalid array index at '" .. current_path .. "': expected position between 1 and " .. (arr_length + 1)) + end + current[key] = {} + end + else + if current[key] ~= nil then + local ltc = luatype(current[key]) + if ltc ~= "table" then + error("Path conflict at '" .. current_path .. "': expected table, found " .. ltc) + end + else + current[key] = {} + end + end + current = current[key] + end + + local final_raw = path_array[#path_array] + local final_key = tonumber(final_raw) or final_raw + current_path = current_path.."."..final_raw + + if luatype(current) ~= "table" then + error("Cannot assign at path '" .. current_path .. "': parent is not a table") + end + -- Make sure to not overwrite existing value + if current[final_key] ~= nil then + error("Cannot overwrite existing value at path '" .. current_path .. "'") + end + -- Final value: if it's an array index, make sure it's next free position + if luatype(final_key) == "number" then + local arr_length = isarray(current) + if not arr_length then + error("Path error at '" .. current_path .. "': expected array, found " .. luatype(current)) + end + if final_key ~= arr_length + 1 then + error("Invalid array index at '" .. current_path .. "': expected position " .. (arr_length + 1)) + end + end + current[final_key] = value +end diff --git a/src/lua/zencode_given.lua b/src/lua/zencode_given.lua index c944a0019..2c5b0dea6 100644 --- a/src/lua/zencode_given.lua +++ b/src/lua/zencode_given.lua @@ -445,7 +445,7 @@ Given("'' part of '' before string suffix ''", function(enc, src, sfx_name) end) Given("'' in path ''", function(enc, path) - local ele_from_path, dest = pick_from_path(path) + local ele_from_path, dest = read_from_path(path) ZEN.TMP = guess_conversion(ele_from_path, enc) ZEN.TMP.name = dest ack(dest) @@ -453,7 +453,7 @@ Given("'' in path ''", function(enc, path) end) Given("'' part of path '' after string prefix ''", function(enc, path, pfx_name) - local ele_from_path, dest = pick_from_path(path) + local ele_from_path, dest = read_from_path(path) local pfx = IN[pfx_name] or pfx_name local plen = #pfx elelen = #ele_from_path @@ -470,7 +470,7 @@ end) Given("'' part of path '' before string suffix ''", function(enc, path, sfx_name) - local ele_from_path, dest = pick_from_path(path) + local ele_from_path, dest = read_from_path(path) local sfx = IN[sfx_name] or sfx_name local slen = #sfx elelen = #ele_from_path diff --git a/src/lua/zencode_math.lua b/src/lua/zencode_math.lua index ba7d43d68..4c8bcab11 100644 --- a/src/lua/zencode_math.lua +++ b/src/lua/zencode_math.lua @@ -150,7 +150,7 @@ local function _rpn_eval(rpn) res = v else -- handle path divided by conf separator in value name - res = pick_from_path(v, true) + res = read_from_path(v, true) end end insert(values, res) diff --git a/src/lua/zencode_merkle.lua b/src/lua/zencode_merkle.lua index aca50f4ec..e00485859 100644 --- a/src/lua/zencode_merkle.lua +++ b/src/lua/zencode_merkle.lua @@ -23,7 +23,7 @@ local MT = require'crypto_merkle' local function _zencode_merkle_root(name, hashtype) - local data = pick_from_path(name, true) + local data = read_from_path(name, true) if not data or type(data) ~= 'table' then error("Table not found in path: "..name, 2) end @@ -39,7 +39,7 @@ When("create merkle root of dictionary path '' using hash ''", _zencode_merkle_r -- Function to verify the integrity of a Merkle root local function _verify_merkle_root(root, name) local merkle_root = have(root) - local data_table = pick_from_path(name, true) + local data_table = read_from_path(name, true) if not data_table or type(data_table) ~= 'table' then error("Table not found in path: "..name, 2) end diff --git a/src/lua/zencode_table.lua b/src/lua/zencode_table.lua index 39fcdf23a..443bc3369 100644 --- a/src/lua/zencode_table.lua +++ b/src/lua/zencode_table.lua @@ -255,7 +255,7 @@ end) local function take_out_f(path, dest, format) if dest then path = path..CONF.path.separator..dest end - local ele , dest = pick_from_path(path) + local ele , dest = read_from_path(path) ACK[dest] = ele if format then new_codec(dest, guess_conversion(ACK[dest], format)) diff --git a/src/lua/zencode_when.lua b/src/lua/zencode_when.lua index c56521f80..40d81704d 100644 --- a/src/lua/zencode_when.lua +++ b/src/lua/zencode_when.lua @@ -116,6 +116,13 @@ When("write string '' in ''", function(content, dest) zentype = 'e' }) end) +When("write string '' in path ''", function(content, dest_path) + local maybe = mayhave(dest_path) + local path = maybe and maybe:octet():string() or dest_path + have(string.match(path, "^[^%.]+")) + write_to_path(path, O.from_string(content)) +end) + -- ... and from a number When("write number '' in ''", function(content, dest) empty(dest) diff --git a/src/lua/zenroom_common.lua b/src/lua/zenroom_common.lua index c49b66893..935e11c15 100644 --- a/src/lua/zenroom_common.lua +++ b/src/lua/zenroom_common.lua @@ -420,34 +420,6 @@ function zip(...) end end --- extract an element from deep down --- works both in IN and ACK memory --- @param path path to variable separated by points --- @return element found following the path --- @return name of the destination -function pick_from_path(path, no_dest) - local path_array = strtok(uscore(path), CONF.path.separator) - local root = path_array[1] - table.remove(path_array, 1) - local dest = path_array[#path_array] - if not no_dest then empty(dest) end - -- should works both in given and when phase - -- IN is checked firstly since in the When phase IN will be empty - local res = IN[root] or ACK[root] - for _,v in pairs(path_array) do - zencode_assert(luatype(res) == 'table', "Object is not a table: "..root) - if res[v] == nil then - local v_number = tonumber(v) - zencode_assert(v_number and res[v_number] ~= nil, "Key "..v.." not found in "..root) - res = res[v_number] - else - res = res[v] - end - root = v - end - return res, dest -end - -- -- MULTIBASE -- Unicode, character, encoding, description, status diff --git a/test/zencode/cookbook_when.bats b/test/zencode/cookbook_when.bats index f3ab2ebc9..cb2f89efc 100644 --- a/test/zencode/cookbook_when.bats +++ b/test/zencode/cookbook_when.bats @@ -1847,7 +1847,7 @@ Then print 'random dictionary' EOF save_output when_random_from_table.out.json assert_output '{"random_array":[1,3],"random_dictionary":{"str3":"world","str4":"!"},"random_pick":"world"}' -} +} @test "hash to point on a curve" { cat <