diff --git a/core/builtin.lua b/core/builtin.lua index 41b1f0f..26321f8 100644 --- a/core/builtin.lua +++ b/core/builtin.lua @@ -398,242 +398,14 @@ function M:custom(str) return self end -function M:h1(str) - str = str or "" - self:custom(std.h1(str)) - return self -end - -function M:h2(str) - str = str or "" - self:custom(std.h2(str)) - return self -end - -function M:h3(str) - str = str or "" - self:custom(std.h3(str)) - return self -end - -function M:h4(str) - str = str or "" - self:custom(std.h4(str)) - return self -end - -function M:h5(str) - str = str or "" - self:custom(std.h5(str)) - return self -end - -function M:h6(str) - str = str or "" - self:custom(std.h6(str)) - return self -end - -function M:p(str) - str = str or "" - table.insert(self.parts, "

" .. str .. "

") - return self -end - -function M:note(content) - content = content or "" - self:custom('
' .. content .. '
') - return self -end - -function M:muted(content) - content = content or "" - self:custom('
' .. content .. '
') - return self -end - -function M:a(link, str) - link = link or "example.com" - str = str or link - table.insert(self.parts, "" .. str .. "") - return self -end - -function M:ha(link, str) - link = link or "example.com" - str = str or link - table.insert(self.parts, "" .. str .. "") - return self -end - -function M:external(link, str) - link = link or "example.com" - str = str or link - table.insert(self.parts, "" .. str .. "") - return self -end - -function M:version() - return self.version -end - -function M:ul(items) - items = items or {} - local html = "" - self:custom(html) - return self -end - -function M:ol(items) - items = items or {} - local html = "
    " - for _, item in ipairs(items) do - html = html .. "
  1. " .. tostring(item) .. "
  2. " - end - html = html .. "
" - self:custom(html) - return self -end - -function M:li(str) - str = str or "" - self:custom("
  • " .. str .. "
  • ") - return self -end - -function M:code(str) - str = str or "" - self:custom("
    " .. str .. "
    ") - return self -end - -function M:blockquote(str) - str = str or "" - self:custom("
    " .. str .. "
    ") - return self -end - -function M:hr() - self:custom("
    ") - return self -end - -function M:divider() - self:custom('
    ') - return self -end - -function M:img(src, alt) - src = src or "" - alt = alt or "" - self:custom('' .. alt .. '') - return self -end - -function M:table(headers, rows) - headers = headers or {} - rows = rows or {} - - local html = "" - for _, header in ipairs(headers) do - html = html .. "" - end - html = html .. "" - - for _, row in ipairs(rows) do - html = html .. "" - for _, cell in ipairs(row) do - html = html .. "" - end - html = html .. "" - end - - html = html .. "
    " .. tostring(header) .. "
    " .. tostring(cell) .. "
    " - self:custom(html) - return self -end - -function M:div(content, class) - content = content or "" - class = class or "" - local class_attr = class ~= "" and (' class="' .. class .. '"') or "" - self:custom("" .. content .. "") - return self -end - -function M:span(content, class) - content = content or "" - class = class or "" - local class_attr = class ~= "" and (' class="' .. class .. '"') or "" - self:custom("" .. content .. "") - return self -end - -function M:strong(str) - str = str or "" - self:custom("" .. str .. "") - return self -end - -function M:em(str) - str = str or "" - self:custom("" .. str .. "") - return self -end - -function M:br() - self:custom("
    ") - return self -end - -function M:section(content) - content = content or "" - self:custom('
    ' .. content .. '
    ') - return self -end - -function M:links(link_list) - link_list = link_list or {} - local html = '' - self:custom(html) - return self -end - -function M:lead(str) - str = str or "" - self:custom(std.small(str)) - return self -end - -function M:small(str) - str = str or "" - self:custom(std.small(str)) - return self -end - -function M:highlight(str) - str = str or "" - self:custom(std.highlight(str)) - return self -end - -function M:banner(str) - self:custom(std.banner(str)) end function M:build() diff --git a/core/log.lua b/core/log.lua deleted file mode 100644 index 0d3c011..0000000 --- a/core/log.lua +++ /dev/null @@ -1,7 +0,0 @@ -local M = {} - -function M.printl(fmt, ...) - print(string.format(fmt, ...)) -end - -return M diff --git a/core/middleware.lua b/core/middleware.lua new file mode 100644 index 0000000..7f8679b --- /dev/null +++ b/core/middleware.lua @@ -0,0 +1,169 @@ +local M = {} + +function M.json_decode(json) + if type(json) ~= "string" then + return nil, "input must be a string" + end + + local pos = 1 + local len = #json + + local function skip_ws() + while pos <= len and json:sub(pos,pos):match("%s") do + pos = pos + 1 + end + end + + local function parse_value() + skip_ws() + local c = json:sub(pos,pos) + if c == "{" then return parse_object() + elseif c == "[" then return parse_array() + elseif c == '"' then return parse_string() + elseif c:match("[%d%-]") then return parse_number() + elseif json:sub(pos,pos+3) == "true" then pos=pos+4; return true + elseif json:sub(pos,pos+4) == "false" then pos=pos+5; return false + elseif json:sub(pos,pos+3) == "null" then pos=pos+4; return nil + else return nil, "invalid value at position "..pos + end + end + + function parse_string() + pos = pos + 1 + local start_pos = pos + local str = "" + while pos <= len do + local c = json:sub(pos,pos) + if c == '"' then + str = str .. json:sub(start_pos,pos-1) + pos = pos + 1 + return str + elseif c == "\\" then + str = str .. json:sub(start_pos,pos-1) + pos = pos + 1 + local esc = json:sub(pos,pos) + local map = {b="\b", f="\f", n="\n", r="\r", t="\t", ['"']='"', ["\\"]="\\", ["/"]="/"} + str = str .. (map[esc] or esc) + pos = pos + 1 + start_pos = pos + else + pos = pos + 1 + end + end + return nil, "unterminated string" + end + + function parse_number() + local start_pos = pos + while pos <= len and json:sub(pos,pos):match("[%d%+%-eE%.]") do + pos = pos + 1 + end + local n = tonumber(json:sub(start_pos,pos-1)) + if not n then return nil, "invalid number at position "..start_pos end + return n + end + + function parse_array() + pos = pos + 1 + local arr = {} + skip_ws() + if json:sub(pos,pos) == "]" then pos=pos+1; return arr end + while true do + local val, err = parse_value() + if err then return nil, err end + table.insert(arr,val) + skip_ws() + local c = json:sub(pos,pos) + if c == "]" then pos=pos+1; break + elseif c == "," then pos=pos+1 + else return nil, "expected ',' or ']' at position "..pos + end + end + return arr + end + + function parse_object() + pos = pos + 1 + local obj = {} + skip_ws() + if json:sub(pos,pos) == "}" then pos=pos+1; return obj end + while true do + skip_ws() + if json:sub(pos,pos) ~= '"' then return nil, "expected string key at "..pos end + local key, err = parse_string() + if err then return nil, err end + skip_ws() + if json:sub(pos,pos) ~= ":" then return nil, "expected ':' at "..pos end + pos = pos + 1 + local val, err = parse_value() + if err then return nil, err end + obj[key] = val + skip_ws() + local c = json:sub(pos,pos) + if c == "}" then pos=pos+1; break + elseif c == "," then pos=pos+1 + else return nil, "expected ',' or '}' at "..pos + end + end + return obj + end + + local result, err = parse_value() + if err then return nil, err end + skip_ws() + if pos <= len then return nil, "trailing characters at "..pos end + return result +end + +function M.json_encode(value) + local t = type(value) + if t == "nil" then + return "null" + elseif t == "boolean" then + return tostring(value) + elseif t == "number" then + return tostring(value) + elseif t == "string" then + return '"' .. value:gsub('[%z\1-\31\\"]', { + ['\\'] = '\\\\', + ['"'] = '\\"', + ['\b'] = '\\b', + ['\f'] = '\\f', + ['\n'] = '\\n', + ['\r'] = '\\r', + ['\t'] = '\\t' + }):gsub("[%z\1-\31]", function(c) + return string.format("\\u%04x", c:byte()) + end) .. '"' + elseif t == "table" then + local is_array = true + local max_index = 0 + for k,v in pairs(value) do + if type(k) ~= "number" then + is_array = false + else + if k > max_index then max_index = k end + end + end + + local items = {} + if is_array then + for i = 1, max_index do + table.insert(items, M.json_encode(value[i])) + end + return "[" .. table.concat(items,",") .. "]" + else + for k,v in pairs(value) do + if type(k) ~= "string" then + return nil, "object keys must be strings" + end + table.insert(items, M.json_encode(k) .. ":" .. M.json_encode(v)) + end + return "{" .. table.concat(items,",") .. "}" + end + else + return nil, "unsupported type: " .. t + end +end + +return M diff --git a/core/std.lua b/core/std.lua index c7462d7..38131e4 100644 --- a/core/std.lua +++ b/core/std.lua @@ -55,9 +55,7 @@ end function M.h3(str) return "

    " .. (str or "") .. "

    " end - -function M.h4(str) - return "

    " .. (str or "") .. "

    " +function M.h4(str) return "

    " .. (str or "") .. "

    " end function M.h5(str) @@ -72,11 +70,11 @@ function M.p(str) return "

    " .. (str or "") .. "

    " end -function M.code(str) - return "" .. (str or "") .. "" +function M.pre(str) + return "
    " .. (str or "") .. "
    " end -function M.pre(str) +function M.code(str) return "
    " .. (str or "") .. "
    " end @@ -228,23 +226,16 @@ function M.table(headers, rows) return html end -function M.copyright() - return "©" -end - function M.highlight(str) - str = str or "" - return '' .. str .. "" + return '' .. (str or "") .. "" end function M.banner(str) - str = str or "" - return '" + return '" end function M.center(str) - str = str or "" - return '
    ' .. str .. "
    " + return '
    ' .. (str or "") .. "
    " end function M.nav(link, str) diff --git a/core/symbol.lua b/core/symbol.lua new file mode 100644 index 0000000..b3fac61 --- /dev/null +++ b/core/symbol.lua @@ -0,0 +1,7 @@ +local M = {} + +M.copyright = "©" +M.registered_trademark = "®" +M.trademark = "™" + +return M diff --git a/core/util.lua b/core/util.lua new file mode 100644 index 0000000..39c6a97 --- /dev/null +++ b/core/util.lua @@ -0,0 +1,14 @@ +local std = require("core.std") +local symbol = require("core.symbol") + +local M = {} + +function M.cc(tbl) + return table.concat(tbl) +end + +function M.copyright(link, holder) + return symbol.copyright .. " " .. std.external(link, holder) +end + +return M diff --git a/examples/canonical/www/index.lua b/examples/canonical/www/index.lua index 3cddd3c..480d637 100644 --- a/examples/canonical/www/index.lua +++ b/examples/canonical/www/index.lua @@ -4,7 +4,7 @@ local std = fes.std local site = fes.fes() site.title = "Canonical" -site.copyright = std.copyright() .. " " .. std.external("https://git.vxserver.dev/fSD", "fSD") +site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD") site:banner(fes.app.header.render(std)) diff --git a/examples/hello-world/www/index.lua b/examples/hello-world/www/index.lua index b8adf7e..4720737 100644 --- a/examples/hello-world/www/index.lua +++ b/examples/hello-world/www/index.lua @@ -2,7 +2,7 @@ local fes = require("fes") local site = fes.fes() site.title = "Hello, World!" -site.copyright = fes.std.copyright() .. " " .. fes.std.external("https://git.vxserver.dev/fSD", "fSD") +site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD") site:h1("Hello, World!") diff --git a/examples/json/Fes.toml b/examples/json/Fes.toml new file mode 100644 index 0000000..e519140 --- /dev/null +++ b/examples/json/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "json" +version = "0.0.1" +authors = ["vx-clutch"] \ No newline at end of file diff --git a/examples/json/www/index.lua b/examples/json/www/index.lua new file mode 100644 index 0000000..980e6d7 --- /dev/null +++ b/examples/json/www/index.lua @@ -0,0 +1,34 @@ +local fes = require("fes") +local site = fes.fes() + +site.title = "JSON" +site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD") + +local json_pre = [[ +{ + "userId": 1, + "id": 1, + "title": "delectus aut autem", + "completed": false +} +]] + +local json = fes.middleware.json_decode(json_pre) + +site:h1("JSON") + +site:note(fes.util.cc({ + fes.std.h2("Before"), + fes.std.code(json_pre), +})) +site:note(fes.util.cc({ + fes.std.h2("After"), + fes.std.ul({ + json["userId"], + json["id"], + json["title"], + json["completed"], + }) +})) + +return site diff --git a/examples/multi-page/www/index.lua b/examples/multi-page/www/index.lua index 41ee99e..e95b94b 100644 --- a/examples/multi-page/www/index.lua +++ b/examples/multi-page/www/index.lua @@ -2,7 +2,7 @@ local fes = require("fes") local site = fes.fes() site.title = "Home" -site.copyright = fes.std.copyright() .. " " .. fes.std.external("https://git.vxserver.dev/fSD", "fSD") +site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD") site:h1("Home") site:note( diff --git a/examples/multi-page/www/page1.lua b/examples/multi-page/www/page1.lua index 7072077..3b2c7c6 100644 --- a/examples/multi-page/www/page1.lua +++ b/examples/multi-page/www/page1.lua @@ -2,7 +2,7 @@ local fes = require("fes") local site = fes.fes() site.title = "Page 1" -site.copyright = fes.std.copyright() .. " " .. fes.std.external("https://git.vxserver.dev/fSD", "fSD") +site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD") site:h1("Page 1") site:note( diff --git a/examples/multi-page/www/page2.lua b/examples/multi-page/www/page2.lua index 1a97048..535bf68 100644 --- a/examples/multi-page/www/page2.lua +++ b/examples/multi-page/www/page2.lua @@ -2,7 +2,7 @@ local fes = require("fes") local site = fes.fes() site.title = "Page 2" -site.copyright = fes.std.copyright() .. " " .. fes.std.external("https://git.vxserver.dev/fSD", "fSD") +site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD") site:h1("Page 2") site:note( diff --git a/examples/simple/Fes.toml b/examples/simple/Fes.toml new file mode 100644 index 0000000..8027800 --- /dev/null +++ b/examples/simple/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "simple" +version = "0.0.1" +authors = ["vx-clutch"] diff --git a/examples/simple/www/index.lua b/examples/simple/www/index.lua new file mode 100644 index 0000000..5e0506d --- /dev/null +++ b/examples/simple/www/index.lua @@ -0,0 +1 @@ +return "Hello, World!" diff --git a/src/new/new.go b/src/new/new.go index 0c534f8..caea2d5 100644 --- a/src/new/new.go +++ b/src/new/new.go @@ -30,13 +30,15 @@ func Project(dir string) error { } indexLua := filepath.Join(dir, "www", "index.lua") if _, err := os.Stat(indexLua); os.IsNotExist(err) { - content := `local fes = require("fes") + content := fmt.Sprintf(`local fes = require("fes") local site = fes.fes() +site.title = "%s" + site:h1("Hello, World!") return site -` +`, dir) if err := os.WriteFile(indexLua, []byte(content), 0644); err != nil { return err } diff --git a/src/server/server.go b/src/server/server.go index 522aa05..d0d443d 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -108,6 +108,18 @@ func loadLua(luaDir string, entry string, cfg *config.MyConfig) (string, error) return 1 }) + L.PreloadModule("core.symbol", func(L *lua.LState) int { + data, err := config.Core.ReadFile("core/symbol.lua") + if err != nil { + panic(err) + } + if err := L.DoString(string(data)); err != nil { + panic(err) + } + L.Push(L.Get(-1)) + return 1 + }) + L.PreloadModule("fes", func(L *lua.LState) int { mod := L.NewTable() coreModules := []string{}