About

This is a simple templating thingy. Basically like the C pre-processor, but for HTML.

Usage

Run this with the .template.html files you want to "compile" as the arguments.

Code

--[[
An HTML pre-processor

BSD Zero Clause License

Copyright (c) 2023 icri.neocities.org

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
]]
local DEBUG = false
local compile
local commands

commands = {
	template = function(scope, params)
		return compile(params .. ".template.html", setmetatable({}, { __index = scope }))
	end,
	---Set a variable
	---@param params string
	set = function(scope, params)
		local name, value = params:match("^(%S+)%s+(.*)$")
		if value then
			if DEBUG then print(("Set: %s to %s"):format(name, value)) end
			scope[name] = value
			return ""
		end
		if DEBUG then
			print(("Warning: \"%s\" failed to match for set"):format(params))
		end
	end,
	---Set only if not already set
	default = function(scope, params)
		local name, value = params:match("^(%S+)%s+(.*)$")
		if value then
			if scope[name] ~= nil then return "" end
			if DEBUG then
				print(("Set: %s to %s"):format(name, value))
			end
			scope[name] = value
			return ""
		end
		if DEBUG then
			print(("Warning: \"%s\" failed to match for set"):format(params))
		end
	end,
	get = function(scope, params)
		local value = scope[params]
		if value then return value end
		if DEBUG then
			print(("Warning: \"%s\" failed to get value"):format(params))
		end
	end,
	---Obtains the verbatim text content
	---@param scope table
	---@param params string
	---@return string
	include = function(scope, params)
		local file = io.open(params) or error(("Invalid file for include %s"):format(params))
		return file:read("*a") ---@as string
	end,
	includequot = function(scope, params)
		local result = commands.include(scope, params)
		result = result:gsub("&", "&amp;"):gsub("<", "&lt;"):gsub(">", "&gt;")
		return result
	end,
	---Gets output of pygmentize
	---@param scope table
	---@param params string
	pygmentize = function(scope, params)
		local filename = params:gsub("'", "\\'")
		local proc = io.popen(("pygmentize -f html '%s'"):format(filename)) or error("Failed to run pygmentize")
		local result = proc:read("*a")
		proc:close()
		return result
	end,
}

---Compile the template file
---@param template string
function compile(template, scope)
	scope = scope or {}
	print("Compiling ", template)
	local file = io.open(template) or error(("Could not open file %s"):format(template))
	local data = file:read("*a") --[[@as string]]
	local result = data:gsub("<!%-%-(%S+)%s+(.-)%-%->", function(command, params)
		if DEBUG then print(command, params) end
		local executor = commands[command]
		if executor then
			return executor(scope, params)
		end
	end)
	return result
end

for i = 1, #arg do
	local template = arg[i]
	local dest = template:match("^(.+).template.html$")
	if not dest then
		print(("Warn: Invalid template specified %s"):format(template))
	end
	local destinationFile = io.open(dest .. ".html", "w") or error("Failed to open output")
	destinationFile:write(compile(template))
end