-- Lua HTMLifier. v0.5 -- Copyright 2004 by Rici Lake. Permission is granted to use this code under -- the same terms and conditions as found in the Lua copyright notice at -- http://www.lua.org/license.html. -- -- I'd appreciate a credit if you use this code, but I don't insist. -- -- This is prerelease software, no interfaces are guaranteed. -- Use at your own risk, etc. -- TODO -- Repackage this as a package table, taking into account the suggestion -- about stylesheets. -- -- See the TODO in lparse for the possibility of changing lparse to a -- coroutine-based generator. -- This file returns an object constructor, but the object is a -- function. This is probably not a great idea, but it works for now. -- It was designed for the case where the basic style sheet is unlikely -- to change between calls, which saves a lot of setup. Probably the -- solution is to abstract a stylesheet object, and attach that as -- a parameter, which would need to get passed through the case vectors. -- Constructor: -- highlighter = new "lua2html" (ncolour, tagmap) -- -- ncolour is the number of scope depth colours in the style sheet. -- tagmap is a mapping from particular tags to html, which can be used to -- insert mathematical symbols or whatever. Every entry in the tag map -- must be fully html escaped. -- -- Usage: -- err, warn, ntokens = highlighter(file, tag, tagdata) -- -- file is an output file, or at least an object with a :write method which -- takes a string argument. (I think I've removed all the multiple argument -- calls.) -- -- tag, tagData the result of calling lparse.parse -- -- err, warn error / warning encountered (booleans) -- -- ntokens number of tokens in the input vector -- -- The actual output is presented through repeated calls to file:write. -- Only the html'ised body is sent; you must previously send the -- header (including stylesheet) and subsequently send the trailer. local string = import "string" local strfind, format = string.find, string.format local table = import "table" local tinsert, tconcat = table.insert, table.concat local math = import "math" local mod = math.mod local lparse = import "lparse" local query = import "libquery" local entify = query.entify local util = import "libutil" local tableFrom, setFrom = util.tableFrom, util.setFrom -- Here is an example overdone tagmap: -- This is used to translate content. Do not put anything in here for which -- there is not a clear link between tag (as opposed to tagData) and content. -- Anything which is in here needs to be fully escaped. --[[ local fancyTag2content = tableFrom [[ - => − * => × / => ÷ ^ => ↑ .. => ‥ ... => … <= => ≤ >= => ≥ ~= => ≠ == => ≡ = => ← < => < > => > and => ∧ not => ≁ or => ∨ nil => ∅ true => ✓ false => ✗ ]] ]] -- Map tags to classes. local tag2class = tableFrom[[ + => op - => op * => op / => op ^ => op .. => op ~= => comp <= => comp >= => comp < => comp > => comp == => comp ( => paren ) => paren [ => brack ] => brack { => brace } => brace = => assign . => index : => index , => punc ; => punc ... => ellip and => wordop not => wordop or => wordop false => const nil => const true => const do => block end => block for => block if => block repeat => block until => block while => block function => block doclause => clause else => clause elseif => clause in => clause then => clause break => stmt local => stmt return => stmt ]] -- error and warning tags local tag2error = setFrom[[missing skip invalid]] local tag2warn = setFrom[[badGlobal unused]] return function(ncolour, tagmap) -- set defaults local tag2content = tableFrom [[ doclause => do > => > >= => >= < => < <= => <= ]] if tagmap then for k, v in tagmap do tag2content[k] = v end end local function formatVar(tag, var, idx) if tag == "varset" then tag = "var def" end -- ugly local htmltag, href, class = "span", "", {tag, n = 1} if var.scope.depth ~= 0 then tinsert(class, "depth"..mod(var.scope.depth, ncolour)) end if var.def == "STDLIB" then tinsert(class, "stdlib") elseif var.def == idx then htmltag, href = "a", format(" name='a%i' id='a%i'", idx, idx) elseif var.def then htmltag, href = "a", format(" href='#a%i'", var.def) end if var.badGlobal then tinsert(class, "badGlobal") elseif not var.used and var.name ~= "_" and not var.global then tinsert(class, "unused") elseif var.closed then if var.set then tinsert(class, "vclosure") else tinsert(class, "cclosure") end end return format("<%s%s class='%s'>%s</%s>", htmltag, href, tconcat(class, " "), var.name, htmltag) end local function formatKeydef(t, td, idx) return format("<a name='a%i' id='a%i' class='key def'>%s</a>", idx, idx, lparse.textFor(t, td)) end local function formatMeantequal() return "<span class='comp'>=</span><span class='missing'>=</span>" end local function formatMissing(_, tagdata) return format("%s<span class='missing'>%s</span>", strfind(tagdata, "^[.()]") and "" or " ", entify(tagdata)) end local function formatEscape(_, tagdata) local _, _, f, r = strfind(tagdata, "(.)(.*)") return format("<span class='escape1'>%s</span><span class='escape'>%s</span>", entify(f), entify(r)) end local function defaultFormat(tag, tagdata) local scope, class, text = lparse.scopeFor(tag, tagdata), tag2class[tag] or tag, tag2content[tag] or entify(lparse.textFor(tag, tagdata)) if scope then return format("<span class='%s depth%i'>%s</span>", class, mod(scope.depth, ncolour), text) else return format("<span class='%s'>%s</span>", class, text) end end local actionTab = setmetatable({ missing = formatMissing, var = formatVar, varset = formatVar, keydef = formatKeydef, meantequal = formatMeantequal, escape = formatEscape, whitespace = function(_, w) return w end, EOF = function() return "" end }, { __index = function() return defaultFormat end }) -- returns (boolean)err, (boolean)warn, (int)#tokens return function(outf, tagVec, tagData) outf:write "<pre>" local i, n = 1, tagVec.n local err, warn while i <= n do local tag = tagVec[i] err = err or tag2error[tag] warn = warn or tag2warn[tag] if tag == "skip" then outf:write "<span class='skip'><span class='skipped'>" for j = i + 1, n do if tagVec[j] == "endskip" then i = j outf:write "</span></span>" break end outf:write(tagData[j]) end else outf:write(actionTab[tagVec[i]](tagVec[i], tagData[i], i)) end i = i + 1 end outf:write "</pre>\n" return err, warn, n end end
Produced by TNT, the Lua-linter. TNT/0.5 Copyright (C) 2004 Rici Lake