From 5f1744c5b0f9761db402dfbca9cc85e1542ddb5f Mon Sep 17 00:00:00 2001 From: the lemons Date: Tue, 31 Aug 2021 23:22:36 -0500 Subject: create the --- README.md | 60 +++++++++++++++++++++++++++++ example.lua | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++ html.lua | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 289 insertions(+) create mode 100644 README.md create mode 100644 example.lua create mode 100644 html.lua diff --git a/README.md b/README.md new file mode 100644 index 0000000..8700e8b --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# simple HTML DSL for lua 5.3 + +this library returns a table which, when indexed with the name of an HTML tag, provides a function which generates a data structure representing an HTML element. when this data structure is converted to a string, the corresponding HTML is generated. + + local h = require "html" + +calling a tag's function with no arguments produces a self-closing tag. + +### source + print(h.hr()) +### output +
+ +calling a tag's function with a single array or value argument produces a tag with the children provided in the array. non-table values may appear in the list of children or in its stead. they are converted to a string an escaped. + +### source + print(h.section{h.h1"foo",h.p"bar"}) +### output +

foo

bar

+ +calling a tag's function with a single set of key/value pairs as the argument produces a self-closing tag with attributes corresponding to the key/value pairs + +### source + print(h.meta{charset = "utf-8"}) +### output + + +calling a tag's function with a set of key/value pairs and then an array or value as the arguments produces a tag with attributes corresponding to the key/value pairs. + +### source + print(h.a({href = "https://example.com"}, "foobar")) +### output + foobar + +a table used in a list of children must either be generated from the HTML functions or have a `tohtml` function or a `render` function, returning HTML structures or HTML source respectively. a table that is not a set of attributes or an array of element children may not be used as arguments to an HTML function. + +### source + local db_record = {name = "foobar", id = 256} + setmetatable(db_record, { + tohtml = function(x) + return h.a( + {href = "https://example.com/" .. x.id}, x.name) + end + }) + + print(h.p{"search result: ", db_record}) +### output +

search result: foobar

+ +the `raw` function will insert text verbatim into the HTML, unescaped. + +### source + local html_source = "hello" + print(h.p{html_source}) + print(h.p{h.raw(html_source)}) +### output +

<marquee>hello</marquee>

+

hello

+ +included with this documentation is `example.lua` which produces this documenation in HTML via the DSL. diff --git a/example.lua b/example.lua new file mode 100644 index 0000000..042425e --- /dev/null +++ b/example.lua @@ -0,0 +1,105 @@ +local h = require "html" + +function code_example(source, output) + return h.div { + h.h3 "source", + h.code {h.pre{source}}, + h.h3 "output", + h.code {h.pre{output}} + } +end + + +print(h.html { + h.head { + h.meta{charset = "utf-8"}, + h.style{h.raw[[ + body { + max-width: 65ch; font-family: sans-serif; margin:auto; + } + ]]} + }, + h.body { + h.h1[[simple HTML DSL for lua 5.3]], + h.p[[ + this library returns a table which, when indexed with the name of + an HTML tag, provides a function which generates a data structure + representing an HTML element. when this data structure is converted + to a string, the corresponding HTML is generated. + ]], + h.code[[local h = require "html"]], + h.p[[ + calling a tag's function with no arguments produces a self-closing + tag. + ]], + code_example([[print(h.hr())]], [[
]]), + h.p[[ + calling a tag's function with a single array or value argument + produces a tag with the children provided in the array. non-table + values may appear in the list of children or in its stead. they are + converted to a string an escaped. + ]], + code_example( + [[print(h.section{h.h1"foo",h.p"bar"})]], + [[

foo

bar

]] + ), + h.p[[ + calling a tag's function with a single set of key/value pairs as + the argument produces a self-closing tag with attributes + corresponding to the key/value pairs + ]], + code_example( + [[print(h.meta{charset = "utf-8"})]], + [[]] + ), + h.p[[ + calling a tag's function with a set of key/value pairs and then an + array or value as the arguments produces a tag with attributes + corresponding to the key/value pairs. + ]], + code_example( + [[print(h.a({href = "https://example.com"}, "foobar"))]], + [[foobar]] + ), + h.p[[ + a table used in a list of children must either be generated from + the HTML functions or have a "tohtml" function or a "render" + function, returning HTML structures or HTML source respectively. a + table that is not a set of attributes or an array of element + children may not be used as arguments to an HTML function. + ]], + code_example( + [[ +local db_record = {name = "foobar", id = 256} +setmetatable(db_record, { + tohtml = function(x) + return h.a( + {href = "https://example.com/" .. x.id}, x.name) + end +}) + +print(h.p{"search result: ", db_record}) + ]], + '

search result: foobar

' + ), + h.p[[ + the `raw` function will insert text verbatim into the HTML, + unescaped. + ]], + code_example( + [[ +local html_source = "hello" +print(h.p{html_source}) +print(h.p{h.raw(html_source)}) + ]], + [[ +

<marquee>hello</marquee>

+

hello

+ ]] + ), + h.p[[ + included with this documentation is "example.lua" which produces + this documenation in HTML via the DSL. + ]] + } +}) diff --git a/html.lua b/html.lua new file mode 100644 index 0000000..454eae8 --- /dev/null +++ b/html.lua @@ -0,0 +1,124 @@ +-- HTML generator DSL for lua 5.3 + +-- copyright © 2021 citrons +-- arbitrary copying, distribution, modification, and use is permitted as long +-- as this copyright notice is retained, and you get in the hole immediately. +-- go on, get in. + + +local h = {} + +local esc_sequences = { + ["<"] = "<", + [">"] = ">", + ['"'] = """ +} +local function escape(x) + local escaped = tostring(x) + escaped = escaped:gsub("&", "&") + + for char,esc in pairs(esc_sequences) do + escaped = string.gsub(escaped, char, esc) + end + return escaped +end + +local function render(x) + local mt = getmetatable(x) + if mt.render then return mt.render(x) + else return escape(x) end +end + +local function verify_tag(x) +end + +local meta = {} +function meta.render(self) + local attr_strings = {} + local i = 1 + for k,v in pairs(self.attrs) do + verify_tag(k) + attr_strings[i] = string.format(' %s="%s"', k, escape(v)) + i = i + 1 + end + local attrs = table.concat(attr_strings) + + local str + if self.children then + local child_strings = {} + for i,c in ipairs(self.children) do + child_strings[i] = render(c) + end + local children = table.concat(child_strings) + + str = string.format('<%s%s>%s', + self.tag, attrs, children, self.tag) + else + str = string.format('<%s%s />', self.tag, attrs) + end + + if self.tag == "html" then + str = "\n" .. str + end + return str +end +meta.__tostring = meta.render + +setmetatable(h, { + __index = function(self, tag) + verify_tag(tag) + return function(...) + local attrs, children + local args = {...} + + if #args == 2 then + attrs = args[1] + if type(args[2]) ~= "table" then + children = {args[2]} + else + children = args[2] + end + elseif #args == 1 then + if type(args[1]) ~= "table" then + attrs = {} children = {args[1]} + elseif args[1][1] ~= nil then + attrs = {} children = args[1] + else + children = {} + attrs = {} + -- if args[1] is key/value pairs, use it as attributes + for _,_ in pairs(args[1]) do + children = nil attrs = args[1] break + end + end + else attrs = {} end + + if children then + for i,v in ipairs(children) do + local mt = getmetatable(v) + if mt.tohtml then + children[i] = mt.tohtml(v) + end + end + end + + local element = {tag=tag, attrs=attrs, children=children} + setmetatable(element, meta) + return element + end + end +}) + +local raw_meta = { + render = function(self) return self.str end, + __tostring = function(self) return self.str end +} + +function h.raw(str) + local raw = {str=str} + setmetatable(raw, raw_meta) + return raw +end + +return h + -- cgit v1.2.3