From 9a958355ca7f111a8b7e2a38f48582b19ce24073 Mon Sep 17 00:00:00 2001 From: luk3yx Date: Mon, 7 Sep 2020 17:48:51 +1200 Subject: [PATCH] Add scroll_container[] (minor compatibility break) --- README.md | 13 +++- core.lua | 14 ++-- elements.lua | 2 +- helpers.lua | 2 +- init.lua | 1 + make_elements.py | 22 +++++- tests.lua | 181 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 223 insertions(+), 12 deletions(-) create mode 100644 tests.lua diff --git a/README.md b/README.md index 8f51f97..2ce652f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,8 @@ A Minetest mod library to make modifying formspecs easier. - `formspec_ast.apply_offset(tree, x, y)`: Shifts all elements in `tree`. Similar to `container`. - `formspec_ast.flatten(tree)`: Removes all containers and offsets elements - that were in containers accordingly. + that were in containers accordingly. **The use of this function is + discouraged as `scroll_container[]` elements are not flattened.** - `formspec_ast.show_formspec(player_or_name, formname, formspec)`: Similar to `minetest.show_formspec`, however also accepts player objects and will pass `formspec` through `formspec_ast.interpret` first. @@ -41,6 +42,12 @@ all attributes are lowercase. [the formspec element list]: https://github.com/minetest/minetest/blob/dee2210/doc/lua_api.txt#L1959 +### Recent backwards incompatibilities + + - The `style[]` element has a `selectors` field instead of `name`. Using + `name` when unparsing formspecs still works, however parsed formspecs + always use `selectors`. + ### Special cases - `formspec_version` (provided it is the first element) is moved to @@ -73,7 +80,7 @@ readability.* }, { type = "style", - name = "name", + selectors = {"name"}, props = { bgcolor = "blue", textcolor = "yellow", @@ -135,7 +142,7 @@ readability.* }, { type = "style", - name = "name", + selectors = {"name"}, props = { bgcolor = "blue", textcolor = "yellow", diff --git a/core.lua b/core.lua index 8159b57..87f9f51 100644 --- a/core.lua +++ b/core.lua @@ -156,7 +156,7 @@ end local function parse_value(elems, template) local elems_l, template_l = #elems, #template if elems_l < template_l or (elems_l > template_l and - template[template_l][2] ~= '...') then + template_l > 0 and template[template_l][2] ~= '...') then while #elems > #template and elems[#elems]:trim() == '' do elems[#elems] = nil end @@ -320,10 +320,12 @@ function formspec_ast.parse(spec, custom_handlers) return nil, err end table.insert(container, ast_elem) - if ast_elem.type == 'container' then + if ast_elem.type == 'container' or + ast_elem.type == 'scroll_container' then table.insert(containers, container) container = ast_elem - elseif ast_elem.type == 'container_end' then + elseif ast_elem.type == 'end' or ast_elem.type == 'container_end' or + ast_elem.type == 'scroll_container_end' then container[#container] = nil container = table.remove(containers) if not container then @@ -348,6 +350,7 @@ local function unparse_ellipsis(elem, obj1, res, inner) end elseif type(obj1[2]) == 'string' then local value = elem[obj1[1]] + if value == nil then return end for k, v in ipairs(value) do table.insert(res, tostring(v)) end @@ -413,14 +416,15 @@ do end local function unparse_elem(elem, res, force) - if elem.type == 'container' and not force then + if (elem.type == 'container' or + elem.type == 'scroll_container') and not force then local err = unparse_elem(elem, res, true) if err then return err end for _, e in ipairs(elem) do local err = unparse_elem(e, res) if err then return err end end - return unparse_elem({type='container_end'}, res, true) + return unparse_elem({type=elem.type .. '_end'}, res, true) end local data = elements[elem.type] diff --git a/elements.lua b/elements.lua index 06010d1..28a18bf 100644 --- a/elements.lua +++ b/elements.lua @@ -3,4 +3,4 @@ -- by make_elements.py. -- -return {["formspec_version"] = {{{"version", "number"}}}, ["size"] = {{{{"w", "number"}, {"h", "number"}}}, {{{"w", "number"}, {"h", "number"}, {"", "null"}}}, {{{"w", "number"}, {"h", "number"}, {"fixed_size", "boolean"}}}}, ["position"] = {{{{"x", "number"}, {"y", "number"}}}}, ["anchor"] = {{{{"x", "number"}, {"y", "number"}}}}, ["no_prepend"] = {{}}, ["real_coordinates"] = {{{"bool", "boolean"}}}, ["container"] = {{{{"x", "number"}, {"y", "number"}}}}, ["container_end"] = {{}}, ["list"] = {{{"inventory_location", "string"}, {"list_name", "string"}, {{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"", "null"}}, {{"inventory_location", "string"}, {"list_name", "string"}, {{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"starting_item_index", "number"}}}, ["listring"] = {{{"inventory_location", "string"}, {"list_name", "string"}}, {}}, ["listcolors"] = {{{"slot_bg_normal", "string"}, {"slot_bg_hover", "string"}, {"slot_border", "string"}, {"tooltip_bgcolor", "string"}, {"tooltip_fontcolor", "string"}}, {{"slot_bg_normal", "string"}, {"slot_bg_hover", "string"}, {"slot_border", "string"}}, {{"slot_bg_normal", "string"}, {"slot_bg_hover", "string"}}}, ["tooltip"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"tooltip_text", "string"}, {"bgcolor", "string"}, {"fontcolor", "string"}}, {{"gui_element_name", "string"}, {"tooltip_text", "string"}, {"bgcolor", "string"}, {"fontcolor", "string"}}}, ["image"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}}}, ["item_image"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"item_name", "string"}}}, ["bgcolor"] = {{{"bgcolor", "string"}, {"fullscreen", "boolean"}, {"fbgcolor", "string"}}, {{"bgcolor", "string"}, {"fullscreen", "boolean"}}, {{"bgcolor", "string"}}}, ["background"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"auto_clip", "boolean"}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}}}, ["background9"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"auto_clip", "boolean"}, {{"middle_x", "number"}, {"middle_y", "number"}, {"middle_x2", "number"}, {"middle_y2", "number"}}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"auto_clip", "boolean"}, {{"middle_x", "number"}, {"middle_y", "number"}}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"auto_clip", "boolean"}, {{"middle_x", "number"}}}}, ["pwdfield"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"label", "string"}}}, ["field"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"label", "string"}, {"default", "string"}}, {{"name", "string"}, {"label", "string"}, {"default", "string"}}}, ["field_close_on_enter"] = {{{"name", "string"}, {"close_on_enter", "string"}}}, ["textarea"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"label", "string"}, {"default", "string"}}}, ["label"] = {{{{"x", "number"}, {"y", "number"}}, {"label", "string"}}}, ["hypertext"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"text", "string"}}}, ["vertlabel"] = {{{{"x", "number"}, {"y", "number"}}, {"label", "string"}}}, ["button"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"label", "string"}}}, ["image_button"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"name", "string"}, {"label", "string"}, {"noclip", "boolean"}, {"drawborder", "boolean"}, {"pressed_texture_name", "string"}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"name", "string"}, {"label", "string"}}}, ["item_image_button"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"item_name", "string"}, {"name", "string"}, {"label", "string"}}}, ["button_exit"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"label", "string"}}}, ["image_button_exit"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"name", "string"}, {"label", "string"}}}, ["textlist"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {{{"listelem", "string"}, "..."}}, {"selected_idx", "number"}, {"transparent", "boolean"}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {{{"listelem", "string"}, "..."}}}}, ["tabheader"] = {{{{"x", "number"}, {"y", "number"}}, {"h", "number"}, {"name", "string"}, {{{"caption", "string"}, "..."}}, {"current_tab", "string"}, {"transparent", "boolean"}, {"draw_border", "boolean"}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {{{"caption", "string"}, "..."}}, {"current_tab", "string"}, {"transparent", "boolean"}, {"draw_border", "boolean"}}, {{{"x", "number"}, {"y", "number"}}, {"name", "string"}, {{{"caption", "string"}, "..."}}, {"current_tab", "string"}, {"transparent", "boolean"}, {"draw_border", "boolean"}}}, ["box"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"color", "string"}}}, ["dropdown"] = {{{{"x", "number"}, {"y", "number"}}, {"w", "number"}, {"name", "string"}, {{{"item", "string"}, "..."}}, {"selected_idx", "number"}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {{{"item", "string"}, "..."}}, {"selected_idx", "number"}}}, ["checkbox"] = {{{{"x", "number"}, {"y", "number"}}, {"name", "string"}, {"label", "string"}, {"selected", "boolean"}}}, ["scrollbar"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"orientation", "string"}, {"name", "string"}, {"value", "string"}}}, ["scrollbaroptions"] = {{{{"opt", "table"}, "..."}}}, ["table"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {{{"cells", "string"}, "..."}}, {"selected_idx", "number"}}}, ["tableoptions"] = {{{{"opt", "table"}, "..."}}}, ["tablecolumns"] = {{{{{"type", "string"}, {{"opt", "table"}, "..."}}, "..."}}}, ["style"] = {{{"name", "string"}, {{"prop", "table"}, "..."}}}, ["style_type"] = {{{"elem_type", "string"}, {{"prop", "table"}, "..."}}}} \ No newline at end of file +return {["formspec_version"] = {{{"version", "number"}}}, ["size"] = {{{{"w", "number"}, {"h", "number"}}}, {{{"w", "number"}, {"h", "number"}, {"fixed_size", "boolean"}}}}, ["position"] = {{{{"x", "number"}, {"y", "number"}}}}, ["anchor"] = {{{{"x", "number"}, {"y", "number"}}}}, ["no_prepend"] = {{}}, ["real_coordinates"] = {{{"bool", "boolean"}}}, ["container"] = {{{{"x", "number"}, {"y", "number"}}}}, ["container_end"] = {{}}, ["scroll_container"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"scrollbar_name", "string"}, {"orientation", "string"}, {"scroll_factor", "number"}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"scrollbar_name", "string"}, {"orientation", "string"}}}, ["scroll_container_end"] = {{}}, ["list"] = {{{"inventory_location", "string"}, {"list_name", "string"}, {{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"", "null"}}, {{"inventory_location", "string"}, {"list_name", "string"}, {{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"starting_item_index", "number"}}}, ["listring"] = {{{"inventory_location", "string"}, {"list_name", "string"}}, {}}, ["listcolors"] = {{{"slot_bg_normal", "string"}, {"slot_bg_hover", "string"}, {"slot_border", "string"}, {"tooltip_bgcolor", "string"}, {"tooltip_fontcolor", "string"}}, {{"slot_bg_normal", "string"}, {"slot_bg_hover", "string"}, {"slot_border", "string"}}, {{"slot_bg_normal", "string"}, {"slot_bg_hover", "string"}}}, ["tooltip"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"tooltip_text", "string"}, {"bgcolor", "string"}, {"fontcolor", "string"}}, {{"gui_element_name", "string"}, {"tooltip_text", "string"}, {"bgcolor", "string"}, {"fontcolor", "string"}}}, ["image"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}}}, ["animated_image"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"texture_name", "string"}, {"frame_count", "string"}, {"frame_duration", "string"}, {"frame_start", "string"}}}, ["item_image"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"item_name", "string"}}}, ["bgcolor"] = {{{"bgcolor", "string"}, {"fullscreen", "boolean"}, {"fbgcolor", "string"}}, {{"bgcolor", "string"}, {"fullscreen", "boolean"}}, {{"bgcolor", "string"}}}, ["background"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"auto_clip", "boolean"}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}}}, ["background9"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"auto_clip", "boolean"}, {{"middle_x", "number"}, {"middle_y", "number"}, {"middle_x2", "number"}, {"middle_y2", "number"}}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"auto_clip", "boolean"}, {{"middle_x", "number"}, {"middle_y", "number"}}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"auto_clip", "boolean"}, {{"middle_x", "number"}}}}, ["pwdfield"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"label", "string"}}}, ["field"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"label", "string"}, {"default", "string"}}, {{"name", "string"}, {"label", "string"}, {"default", "string"}}}, ["field_close_on_enter"] = {{{"name", "string"}, {"close_on_enter", "string"}}}, ["textarea"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"label", "string"}, {"default", "string"}}}, ["label"] = {{{{"x", "number"}, {"y", "number"}}, {"label", "string"}}}, ["hypertext"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"text", "string"}}}, ["vertlabel"] = {{{{"x", "number"}, {"y", "number"}}, {"label", "string"}}}, ["button"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"label", "string"}}}, ["image_button"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"name", "string"}, {"label", "string"}, {"noclip", "boolean"}, {"drawborder", "boolean"}, {"pressed_texture_name", "string"}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"name", "string"}, {"label", "string"}}}, ["item_image_button"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"item_name", "string"}, {"name", "string"}, {"label", "string"}}}, ["button_exit"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {"label", "string"}}}, ["image_button_exit"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"texture_name", "string"}, {"name", "string"}, {"label", "string"}}}, ["textlist"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {{{"listelem", "string"}, "..."}}, {"selected_idx", "number"}, {"transparent", "boolean"}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {{{"listelem", "string"}, "..."}}}}, ["tabheader"] = {{{{"x", "number"}, {"y", "number"}}, {"h", "number"}, {"name", "string"}, {{{"caption", "string"}, "..."}}, {"current_tab", "string"}, {"transparent", "boolean"}, {"draw_border", "boolean"}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {{{"caption", "string"}, "..."}}, {"current_tab", "string"}, {"transparent", "boolean"}, {"draw_border", "boolean"}}, {{{"x", "number"}, {"y", "number"}}, {"name", "string"}, {{{"caption", "string"}, "..."}}, {"current_tab", "string"}, {"transparent", "boolean"}, {"draw_border", "boolean"}}}, ["box"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"color", "string"}}}, ["dropdown"] = {{{{"x", "number"}, {"y", "number"}}, {"w", "number"}, {"name", "string"}, {{{"item", "string"}, "..."}}, {"selected_idx", "number"}, {"index_event", "string"}}, {{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {{{"item", "string"}, "..."}}, {"selected_idx", "number"}, {"index_event", "string"}}}, ["checkbox"] = {{{{"x", "number"}, {"y", "number"}}, {"name", "string"}, {"label", "string"}, {"selected", "boolean"}}}, ["scrollbar"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"orientation", "string"}, {"name", "string"}, {"value", "string"}}}, ["scrollbaroptions"] = {{{{"opt", "table"}, "..."}}}, ["table"] = {{{{"x", "number"}, {"y", "number"}}, {{"w", "number"}, {"h", "number"}}, {"name", "string"}, {{{"cells", "string"}, "..."}}, {"selected_idx", "number"}}}, ["tableoptions"] = {{{{"opt", "table"}, "..."}}}, ["tablecolumns"] = {{{{{"type", "string"}, {{"opt", "table"}, "..."}}, "..."}}}, ["style"] = {{{{{"selectors", "string"}, "..."}}, {{"prop", "table"}, "..."}}, {{{"name", "string"}}, {{"prop", "table"}, "..."}}}, ["style_type"] = {{{{{"selectors", "string"}, "..."}}, {{"prop", "table"}, "..."}}, {{{"name", "string"}}, {{"prop", "table"}, "..."}}}, ["set_focus"] = {{{"name", "string"}, {"force", "boolean"}}}} \ No newline at end of file diff --git a/helpers.lua b/helpers.lua index e6f0d9e..7a7fb29 100644 --- a/helpers.lua +++ b/helpers.lua @@ -59,7 +59,7 @@ function formspec_ast.walk(tree) end i = i + 1 - if res.type == 'container' then + if res.type == 'container' or res.type == 'scroll_container' then table.insert(parents, {tree, i}) tree = res i = 1 diff --git a/init.lua b/init.lua index abe568c..11f1b7d 100644 --- a/init.lua +++ b/init.lua @@ -54,6 +54,7 @@ else end return text end + minetest.log = print function string.trim(str) return str:gsub("^%s*(.-)%s*$", "%1") end diff --git a/make_elements.py b/make_elements.py index 2fd5ac5..b97be87 100755 --- a/make_elements.py +++ b/make_elements.py @@ -14,9 +14,9 @@ def _make_known(**kwargs): _known = _make_known( number=('x', 'y', 'w', 'h', 'selected_idx', 'version', - 'starting_item_index'), + 'starting_item_index', 'scroll_factor'), boolean=('auto_clip', 'fixed_size', 'transparent', 'draw_border', 'bool', - 'fullscreen', 'noclip', 'drawborder', 'selected'), + 'fullscreen', 'noclip', 'drawborder', 'selected', 'force'), table=('param', 'opt', 'prop'), null=('',), ) @@ -99,6 +99,24 @@ def _size_hook(params): yield params yield [[('w', 'number'), ('h', 'number')]] +# Fix style and style_type +@hook('style') +@hook('style_type') +def _style_hook(params): + # This is not used when parsing but keeps backwards compatibility when + # unparsing. + params[0] = [('name', 'string')] + yield params + + params[0] = [(('selectors', 'string'), '...')] + yield params + +# Fix scroll_container +@hook('scroll_container') +def _scroll_container_hook(params): + yield params + yield params[:4] + def _raw_parse(data): data = data.split('\nElements\n--------\n', 1)[-1].split('\n----', 1)[0] for line in data.split('\n'): diff --git a/tests.lua b/tests.lua new file mode 100644 index 0000000..32161c1 --- /dev/null +++ b/tests.lua @@ -0,0 +1,181 @@ +-- dofile('init.lua') +dofile('test.lua') + +local function equal(t1, t2) + if type(t1) ~= 'table' or type(t2) ~= 'table' then + return t1 == t2 + end + for k, v in pairs(t1) do + if not equal(v, t2[k]) then + print(k, v, dump(t1), dump(t2)) + return false + end + end + for k, v in pairs(t2) do + if t1[k] == nil then + return false + end + end + return true +end + +local function assert_equal(obj1, ...) + for i = 1, select('#', ...) do + objn = select(i, ...) + if not equal(obj1, objn) then + error(('%s ~= %s'):format(obj1, objn)) + end + end +end + +local function test_parse(fs, expected_tree) + -- Make single elements lists and add formspec_version + if expected_tree.type then + expected_tree = {expected_tree} + end + if not expected_tree.formspec_version then + expected_tree.formspec_version = 1 + end + + local tree = assert(formspec_ast.parse(fs)) + assert_equal(tree, expected_tree) +end + +local function test_parse_unparse(fs, expected_tree) + test_parse(fs, expected_tree) + local unparsed_fs = assert(formspec_ast.unparse(expected_tree)) + assert_equal(fs, unparsed_fs) +end + +local fs = [[ + formspec_version[2] + size[5,2] + container[1,1] + label[0,0;Containers are fun] + container[-1,-1] + button[0.5,0;4,1;name;Label] + container_end[] + label[0,1;Nested containers work too.] + scroll_container[0,2;1,1;scrollbar;vertical] + button[0.5,0;4,1;name;Label] + scroll_container_end[] + container_end[] + image[0,1;1,1;air.png] + set_focus[name;true] +]] +fs = ('\n' .. fs):gsub('\n[ \n]*', '') + +test_parse_unparse(fs, { + formspec_version = 2, + { + type = "size", + w = 5, + h = 2, + }, + { + type = "container", + x = 1, + y = 1, + { + type = "label", + x = 0, + y = 0, + label = "Containers are fun", + }, + { + type = "container", + x = -1, + y = -1, + { + type = "button", + x = 0.5, + y = 0, + w = 4, + h = 1, + name = "name", + label = "Label", + }, + }, + { + type = "label", + x = 0, + y = 1, + label = "Nested containers work too.", + }, + { + type = "scroll_container", + x = 0, + y = 2, + w = 1, + h = 1, + scrollbar_name = "scrollbar", + orientation = "vertical", + -- scroll_factor = nil, + { + h = 1, + y = 0, + label = "Label", + w = 4, + name = "name", + x = 0.5, + type = "button" + }, + }, + }, + { + type = "image", + x = 0, + y = 1, + w = 1, + h = 1, + texture_name = "air.png", + }, + { + type = "set_focus", + name = "name", + force = true, + } +}) + + +-- Make sure style[] (un)parses correctly +local s = 'style[test1,test2;def=ghi]style_type[test;abc=def]' +assert_equal(s, assert(formspec_ast.interpret(s))) +test_parse('style[name,name2;bgcolor=blue;textcolor=yellow]', { + type = "style", + selectors = { + "name", + "name2", + }, + props = { + bgcolor = "blue", + textcolor = "yellow", + }, +}) + +-- Ensure the style[] unparse compatibility works correctly +assert_equal( + 'style_type[test;abc=def]', + assert(formspec_ast.unparse({ + { + type = 'style_type', + name = 'test', + props = { + abc = 'def', + }, + } + })), + assert(formspec_ast.unparse({ + { + type = 'style_type', + selectors = { + 'test', + }, + props = { + abc = 'def', + }, + } + })) +) + +print('Tests pass')