diff options
| author | frosty <gabriel@bwaaa.monster> | 2026-09-19 23:10:06 -0400 |
|---|---|---|
| committer | frosty <gabriel@bwaaa.monster> | 2026-09-19 23:10:06 -0400 |
| commit | ca4732f7c6e3d0c735cb0977c505db52b99ff688 (patch) | |
| tree | 250b36e991a24835b50cfa209523b0935ee2a381 | |
| parent | 9b34d3cb8ea7d7651fc65d264b0dcfd867cc044b (diff) | |
feat: support variadic html.element children
| -rw-r--r-- | examples/site/build.lua | 11 | ||||
| -rw-r--r-- | src/Dom/Dom.c | 78 |
2 files changed, 76 insertions, 13 deletions
diff --git a/examples/site/build.lua b/examples/site/build.lua index f3b07e1..e18124e 100644 --- a/examples/site/build.lua +++ b/examples/site/build.lua @@ -11,7 +11,7 @@ local features = { local feature_list = assert(html.find_by_class(page, "feature-list")) for _, feature in ipairs(features) do - table.insert(feature_list.children, html.element("li", nil, { feature })) + table.insert(feature_list.children, html.element("li", nil, feature)) end local entries = {} @@ -25,10 +25,9 @@ summary.text = tostring(#entries) .. " entries" local file_list = assert(html.find_by_class(page, "file-list")) for _, entry in ipairs(entries) do - table.insert(file_list.children, html.element("li", nil, { - html.element("code", nil, { entry.path }), - " (", entry.type, ")" - })) + table.insert(file_list.children, html.element("li", nil, + html.element("code", nil, entry.path), + " (", entry.type, ")")) end local build_summary = assert(html.find_by_class(page, "build-summary")) @@ -38,7 +37,7 @@ local mixed = assert(html.find_by_class(page, "mixed-content")) mixed.children = { { kind = "comment", text = "generated by Lua" }, "Text with ", - html.element("strong", nil, { "an element" }), + html.element("strong", nil, "an element"), "." } diff --git a/src/Dom/Dom.c b/src/Dom/Dom.c index f0d4f36..4eb3ad4 100644 --- a/src/Dom/Dom.c +++ b/src/Dom/Dom.c @@ -10,8 +10,75 @@ static int absolute_index(lua_State *lua, int index) { return index < 0 ? lua_gettop(lua) + index + 1 : index; } +static int table_is_node(lua_State *lua, int table_index) { + int is_node; + + table_index = absolute_index(lua, table_index); + lua_getfield(lua, table_index, "tag"); + is_node = !lua_isnil(lua, -1); + lua_pop(lua, 1); + if (!is_node) { + lua_getfield(lua, table_index, "kind"); + is_node = !lua_isnil(lua, -1); + lua_pop(lua, 1); + } + return is_node; +} + +static void append_element_child(lua_State *lua, int children_index, + int child_index, size_t *child_count, + unsigned int depth) { + size_t array_length; + size_t index; + int child_type; + + if (!lua_checkstack(lua, 2)) { + luaL_error(lua, "html.element children are nested too deeply"); + } + children_index = absolute_index(lua, children_index); + child_index = absolute_index(lua, child_index); + child_type = lua_type(lua, child_index); + + if (child_type == LUA_TNIL) { + return; + } + if (child_type == LUA_TSTRING || + (child_type == LUA_TTABLE && table_is_node(lua, child_index))) { + if (*child_count == INT_MAX) { + luaL_error(lua, "html.element has too many children"); + } + ++*child_count; + lua_pushvalue(lua, child_index); + lua_rawseti(lua, children_index, (int)*child_count); + return; + } + if (child_type != LUA_TTABLE) { + luaL_error(lua, + "html.element child must be a string, node, child array, or " + "nil (got %s)", + lua_typename(lua, child_type)); + } + if (depth == 100) { + luaL_error(lua, "html.element children are nested too deeply"); + } + + array_length = lua_objlen(lua, child_index); + if (array_length > INT_MAX) { + luaL_error(lua, "html.element child array is too large"); + } + for (index = 1; index <= array_length; ++index) { + lua_rawgeti(lua, child_index, (int)index); + append_element_child(lua, children_index, -1, child_count, depth + 1); + lua_pop(lua, 1); + } +} + static int html_element(lua_State *lua) { const char *tag; + size_t child_count = 0; + int argument_count = lua_gettop(lua); + int children_index; + int index; luaL_checktype(lua, 1, LUA_TSTRING); tag = lua_tostring(lua, 1); @@ -21,9 +88,6 @@ static int html_element(lua_State *lua) { if (!lua_isnoneornil(lua, 2)) { luaL_checktype(lua, 2, LUA_TTABLE); } - if (!lua_isnoneornil(lua, 3)) { - luaL_checktype(lua, 3, LUA_TTABLE); - } lua_newtable(lua); @@ -37,10 +101,10 @@ static int html_element(lua_State *lua) { } lua_setfield(lua, -2, "attributes"); - if (lua_istable(lua, 3)) { - lua_pushvalue(lua, 3); - } else { - lua_newtable(lua); + lua_newtable(lua); + children_index = lua_gettop(lua); + for (index = 3; index <= argument_count; ++index) { + append_element_child(lua, children_index, index, &child_count, 0); } lua_setfield(lua, -2, "children"); |
