├── LICENSE ├── README.md ├── test.tlp ├── testlib.tl ├── testrunpage.lua ├── testrunstring.lua ├── tlp.lua └── tlp_handler.lua /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Felipe Daragon, exluna.org 4 | Portions copyright (c) 2019 Hisham Muhammad 5 | Portions copyright (c) 2007 CGILua 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Teal Pages # 2 | 3 | This is the Teal Pages Template Preprocessor, a modification of the Lua Pages Template Preprocessor to enable the [Teal](https://github.com/teal-language/tl) language, a typed dialect of Lua, instead of Lua directly from within ` 8 | 9 | Hello World!!! 10 | 11 | 15 | -------------------------------------------------------------------------------- /testlib.tl: -------------------------------------------------------------------------------- 1 | function printnumber(n: number) 2 | print(n) 3 | end 4 | 5 | function printstring(s: string) 6 | print(s) 7 | end 8 | 9 | -------------------------------------------------------------------------------- /testrunpage.lua: -------------------------------------------------------------------------------- 1 | -- This demo script executes a Teal Pages script (test.tlp) with support for 2 | -- tags. 5 | -- Copyright (c) 2020 Felipe Daragon, exluna.org 6 | -- Portions copyright (c) 2019 Hisham Muhammad 7 | -- Portions copyright (c) 2007 CGILua 8 | -- License: MIT 9 | -- 10 | -- This is an experimental merge of slightly modified parts of tl's compiler 11 | -- (https://github.com/teal-language/tl/blob/master/tl) and a modified 12 | -- lp.lua (v1.12). Currently it can be used from within Lua scripts or Apache 13 | -- if you enable tlp_handler.lua through the LuaMapHandler directive 14 | ---------------------------------------------------------------------------- 15 | 16 | local assert, error, getfenv, loadstring, setfenv = assert, error, getfenv, loadstring, setfenv 17 | local find, format, gsub, strsub = string.find, string.format, string.gsub, string.sub 18 | local concat, tinsert = table.concat, table.insert 19 | local open = io.open 20 | local ipairs, unpack = ipairs, unpack 21 | local stderr = io.stderr 22 | local exit = os.exit 23 | local tl = require("tl") 24 | local tunpack = table.unpack 25 | 26 | module (...) 27 | 28 | local function htmlescape(text) 29 | local special = { ['<']='<', ['>']='>', ['&']='&', ['"']='"' } 30 | return text:gsub('[<>&"]', special) 31 | end 32 | 33 | ---------------------------------------------------------------------------- 34 | -- function to do output 35 | local outfunc = "io.write" 36 | -- accepts the old expression field: `$| |$' 37 | local compatmode = true 38 | 39 | -- 40 | -- Builds a piece of Lua code which outputs the (part of the) given string. 41 | -- @param s String. 42 | -- @param i Number with the initial position in the string. 43 | -- @param f Number with the final position in the string (default == -1). 44 | -- @return String with the correspondent Lua code which outputs the part of the string. 45 | -- 46 | local function out (s, i, f) 47 | s = strsub(s, i, f or -1) 48 | if s == "" then return s end 49 | -- we could use `%q' here, but this way we have better control 50 | s = gsub(s, "([\\\n\'])", "\\%1") 51 | -- substitute '\r' by '\'+'r' and let `loadstring' reconstruct it 52 | s = gsub(s, "\r", "\\r") 53 | return format(" %s('%s'); ", outfunc, s) 54 | end 55 | 56 | ---------------------------------------------------------------------------- 57 | -- Translate the template to Teal code. 58 | -- @param s String to translate. 59 | -- @return String with translated code. 60 | ---------------------------------------------------------------------------- 61 | function translate (s) 62 | if compatmode then 63 | s = gsub(s, "$|(.-)|%$", "") 64 | s = gsub(s, "", "") 65 | end 66 | s = gsub(s, "<%%(.-)%%>", "") 67 | local res = {} 68 | local start = 1 -- start of untranslated part in `s' 69 | while true do 70 | local ip, fp, target, exp, code = find(s, "<%?(%w*)[ \t]*(=?)(.-)%?>", start) 71 | if not ip then break end 72 | tinsert(res, out(s, start, ip-1)) 73 | if target ~= "" and target ~= "teal" then 74 | -- not for Teal; pass whole instruction to the output 75 | tinsert(res, out(s, ip, fp)) 76 | else 77 | if exp == "=" then -- expression? 78 | tinsert(res, format(" %s(%s);", outfunc, code)) 79 | else -- command 80 | tinsert(res, format(" %s ", code)) 81 | end 82 | end 83 | start = fp + 1 84 | end 85 | tinsert(res, out(s, start)) 86 | return concat(res) 87 | end 88 | 89 | 90 | ---------------------------------------------------------------------------- 91 | -- Defines the name of the output function. 92 | -- @param f String with the name of the function which produces output. 93 | 94 | function setoutfunc (f) 95 | outfunc = f 96 | end 97 | 98 | ---------------------------------------------------------------------------- 99 | -- Turns on or off the compatibility with old CGILua 3.X behavior. 100 | -- @param c Boolean indicating if the compatibility mode should be used. 101 | 102 | function setcompatmode (c) 103 | compatmode = c 104 | end 105 | 106 | ---------------------------------------------------------------------------- 107 | -- Internal compilation cache. 108 | 109 | local cache = {} 110 | 111 | ---------------------------------------------------------------------------- 112 | -- Translates a template into a Lua function. 113 | -- Does NOT execute the resulting function. 114 | -- Uses a cache of templates. 115 | -- @param string String with the template to be translated. 116 | -- @param chunkname String with the name of the chunk, for debugging purposes. 117 | -- @return Function with the resulting translation. 118 | 119 | function compile (string, chunkname) 120 | local f, err = cache[string] 121 | if f then return f end 122 | f, err = loadstring (translate (string), chunkname) 123 | if not f then error (err, 3) end 124 | cache[string] = f 125 | return f 126 | end 127 | 128 | ---------------------------------------------------------------------------- 129 | -- TEAL COMPILER -- 130 | ---------------------------------------------------------------------------- 131 | 132 | local function get_config() 133 | local config = { 134 | preload_modules = {}, 135 | include_dir = {}, 136 | quiet = false 137 | } 138 | return config 139 | end 140 | local tlconfig = get_config() 141 | 142 | local function printerr(s) 143 | stderr:write(s .. "\n") 144 | end 145 | 146 | local function trim(str) 147 | return str:gsub("^%s*(.-)%s*$", "%1") 148 | end 149 | 150 | local function die(msg) 151 | printerr(msg) 152 | exit(1) 153 | end 154 | 155 | local function report_errors(category, errors) 156 | if not errors then 157 | return false 158 | end 159 | if #errors > 0 then 160 | local n = #errors 161 | printerr("========================================") 162 | printerr(n .. " " .. category .. (n ~= 1 and "s" or "") .. ":") 163 | for _, err in ipairs(errors) do 164 | printerr(htmlescape(err.filename) .. ":" .. err.y .. ":" .. err.x .. ": " .. (err.msg or "")) 165 | end 166 | return true 167 | end 168 | return false 169 | end 170 | 171 | local function report_type_errors(result) 172 | local has_type_errors = report_errors("error", result.type_errors) 173 | report_errors("unknown variable", result.unknowns) 174 | 175 | return not has_type_errors 176 | end 177 | 178 | local env = nil 179 | 180 | 181 | 182 | local function type_check_and_loadstring(filename, input, islua, modules) 183 | local filename = "@"..filename 184 | local result, err = tl.process_string(input, islua, env, nil, modules, filename) 185 | if err then 186 | die(err) 187 | end 188 | env = result.env 189 | 190 | local has_syntax_errors = report_errors("syntax error", result.syntax_errors) 191 | if has_syntax_errors then 192 | exit(1) 193 | end 194 | if islua == false then 195 | local ok = report_type_errors(result) 196 | if not ok then 197 | exit(1) 198 | end 199 | end 200 | 201 | local chunk; chunk, err = (loadstring or load)(tl.pretty_print_ast(result.ast), filename) 202 | if err then 203 | die("Internal Compiler Error: Teal generator produced invalid Lua. Please report a bug at https://github.com/teal-language/tl") 204 | end 205 | return chunk 206 | end 207 | 208 | local function setup_env(filename) 209 | if not env then 210 | local basename, extension = filename:match("(.*)%.([a-z]+)$") 211 | extension = extension and extension:lower() 212 | 213 | local lax_mode 214 | if extension == "tl" then 215 | lax_mode = false 216 | elseif extension == "lua" then 217 | lax_mode = true 218 | else 219 | -- if we can't decide based on the file extension, default to strict mode 220 | lax_mode = false 221 | end 222 | 223 | local skip_compat53 = tlconfig["skip_compat53"] 224 | 225 | env = tl.init_env(lax_mode, skip_compat53) 226 | end 227 | end 228 | 229 | local function compilestring(input, filename) 230 | setup_env(filename) 231 | local chunk = type_check_and_loadstring(filename, input, false, modules) 232 | return chunk 233 | end 234 | 235 | function runstring(input, filename) 236 | local arg = {} 237 | filename = filename or "@teal" 238 | chunk = compilestring(input, filename) 239 | tl.loader() 240 | return chunk((unpack or table.unpack)(arg)) 241 | end 242 | 243 | 244 | ---------------------------------------------------------------------------- 245 | -- Translates and executes a template in a given file. 246 | -- The translation creates a Lua function which will be executed in an 247 | -- optionally given environment. 248 | -- @param filename String with the name of the file containing the template. 249 | -- @param env Table with the environment to run the resulting function. 250 | 251 | function include (filename, env) 252 | -- read the whole contents of the file 253 | local fh = assert (open (filename)) 254 | local src = fh:read("*a") 255 | src = translate (src) 256 | fh:close() 257 | -- translates the file into a function 258 | local prog = compilestring(src, filename) 259 | local arg = {} 260 | local _env 261 | if env then 262 | _env = getfenv (prog) 263 | setfenv (prog, env) 264 | end 265 | tl.loader() 266 | prog((unpack or tunpack)(arg)) 267 | end 268 | 269 | 270 | -------------------------------------------------------------------------------- /tlp_handler.lua: -------------------------------------------------------------------------------- 1 | -- This file is part of the Teal Pages project 2 | -- Copyright (c) 2020 Felipe Daragon 3 | -- License: MIT 4 | 5 | function handle_tlp(r) 6 | local tlp = require "tlp" 7 | r.content_type = "text/html" 8 | puts = function(s) r:write(s) end 9 | print = function(s) r:write(s) end 10 | tlp.setoutfunc "puts" 11 | tlp.include(r.filename) 12 | end 13 | 14 | --------------------------------------------------------------------------------