|
Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- definers.lua - code to define blogme words.
-- This is part of blogme3.
-- The semantics for a blogme word FOO is determined by an "argument
-- parsing" function for FOO, stored in _A["FOO"], and by the "blogme
-- code" for FOO, a function stored in _B["FOO"]; see
-- (find-blogme3 "brackets.lua")
-- for the details.
--
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2007apr16
-- <http://anggtwu.net/blogme3/definers.lua>
-- <http://anggtwu.net/blogme3/definers.lua.html>
-- License: GPL.
--
-- (find-a2ps (buffer-file-name))
-- «._AA» (to "_AA")
-- «.undollar» (to "undollar")
-- «.tools» (to "tools")
-- «.def» (to "def")
-- «.odef» (to "odef")
-- «_AA» (to "._AA")
--------[ Short names for arglist parsers ]--------
_AA = {}
for short,long in each2(split [[
1 readvrest 1L readvlist 1Q readqrest 0 nop
2 readvvrest 2L readvvlist 2Q readqqrest * readvargs
3 readvvvrest 3L readvvvlist 3Q readqqqrest
4 readvvvvrest 4L readvvvvlist 4Q readqqqqrest
5 readvvvvvrest 5L readvvvvvlist 5Q readqqqqqrest
]]) do
_AA[short] = _G[long] or
error(format("Not in _G (to fill _AA[%q]): %s", short, long))
end
-- «undollar» (to ".undollar")
--------[ Functions to transform code ]--------
wrap_f = function (arglist, body) -- here body is made of statements
return "function ("..arglist..")\n"..body.."\nend"
end
wrap_fr = function (arglist, body) -- here body is an expression
return "function ("..arglist..")\nreturn "..body.."\nend"
end
-- undollar: apply three kinds of "string interpolations" on code.
-- This is very hard to understand without examples, so:
--
-- undollar [[ "foo $bar plic" ]] --> [[ "foo "..bar.." plic" ]]
-- undollar [[ "foo$(bar)plic" ]] --> [[ "foo"..(bar).."plic" ]]
-- undollar " [[foo$[1+2]bar]] " --> " [[foo]]..(1+2)..[[bar]] "
--
undollar = function (code)
code = string.gsub(code, "%$([a-z]+)", "\"..%1..\"")
code = string.gsub(code, "%$(%b())", "\"..%1..\"")
code = string.gsub(code, "%$(%b[])", function (s)
return "]]..("..strsub(s, 2, -2)..")..[["
end)
return code
end
-- «tools» (to ".tools")
--------[ Tool functions for def ]--------
-- Note: it is possible to define blogme words that are "private to
-- the blogme vocabulary" by putting their code in _B["wordname"], but
-- it is also possible to make blogme words accessible as lua globals.
-- The evaluator, evalblock(startpos), searches for the blogme code of
-- a word, say, "foo", first in _B["foo"], then in _G["foo"]; and if
-- it is stored in _G["foo"] then we can call foo from Lua as
-- foo(arg1, arg2, ...).
--
-- Some of the functions below come in pairs - one variant stores the
-- blogme code in _G, another one in _B.
_S = _S or {}
def_split = function (defstr)
return string.match(defstr, "^%s*(%S+)%s+(%S+)%s+(%S+)%s(.*)")
end
def_set_A = function (name, apspec)
_A[name] = _AA[apspec] or _G[apspec] or
error(format("Not in _AA or _G (to fill _A[%q]): %q", name, apspec))
end
def_set_B = function (name, funtext)
_B[name] = assert(loadstring("return " .. funtext, name))()
_S[name] = funtext
end
def_set_G = function (name, funtext)
_G[name] = assert(loadstring("return " .. funtext, name))()
_S[name] = funtext
end
--------[ More tools for def, with support for precompilation ]--------
_defs = _defs or {}
def_set_AB = function (name, apspec, funtext)
def_set_A(name, apspec)
def_set_B(name, funtext)
tinsert(_defs, {name, apspec, "_B", funtext})
end
def_set_AG = function (name, apspec, funtext)
def_set_A(name, apspec)
def_set_G(name, funtext)
tinsert(_defs, {name, apspec, "_G", funtext})
end
def_as_lua = function (navc, bdelim, edelim)
local name, apspec, var, code = unpack(navc)
return format([[
_A[%q] = _AA[%q] or _G[%q]
%s[%q] = %s
_S[%q] = %s%s%s
]], name, apspec, apspec,
var, name, code,
name, (bdelim or "[=["), code, (edelim or "]=]"))
end
defs_as_lua = function ()
return table.concat(map(def_as_lua, _defs), "\n")
end
-- «def» (to ".def")
-- New version: (find-blogme4 "def.lua" "def")
--------[ def itself, plus variations ]--------
-- In def and bdef a "return" is added; the "body" is an expression.
-- In def_ and bdef_ no "return" is added; the body is made of statements.
-- In bdef and bdef_ the new word is stored in _B and it does not
-- become a lua global.
def = function (defstr)
local name, apspec, arglist, body = def_split(defstr)
def_set_AG(name, apspec, wrap_fr(arglist, undollar(body)))
end
bdef = function (defstr)
local name, apspec, arglist, body = def_split(defstr)
def_set_AB(name, apspec, wrap_fr(arglist, undollar(body)))
end
def_ = function (defstr)
local name, apspec, arglist, body = def_split(defstr)
def_set_AG(name, apspec, wrap_f(arglist, undollar(body)))
end
bdef_ = function (defstr)
local name, apspec, arglist, body = def_split(defstr)
def_set_AB(name, apspec, wrap_f(arglist, undollar(body)))
end
--------[ odef ]--------
-- «odef» (to ".odef")
-- 2014jan25, from (find-blogme3 "youtube.lua")
-- See: (find-blogme3 "options.lua")
lua_eval = function (str) return assert(loadstring(str))() end
lua_expr = function (str) return assert(loadstring("return "..str))() end
odef_split = function (odefstr)
return string.match(odefstr, "^%s*(%S+)%s+(%S+)%s+(.*)")
end
odef_ = function (odefstr)
local optname, argstr, body = odef_split(odefstr)
return format('_O["-%s"] = function (%s)\n%s\ndooptions(...)\nend\n'..
'dooption_%s = _O["-%s"]\n',
optname, argstr, body,
optname, optname)
end
odef = function (odefstr)
lua_eval(odef_(odefstr))
end
--------[ DEF ]--------
-- DEF: a quick hack to define Blogme words from inside Blogme code,
-- without having execute any Lua code directly. Here's an example:
-- the BlogMe block below
--
-- [DEF DEFED-HREF 2 url,text <a href="[$ url]">[$ text]</a>]
--
-- defines a word DEFED-HREF by running this, in Lua:
--
-- _A["DEFED-HREF"] = _AA["2"] or _G["2"]
-- _G["DEFED-HREF"] = function (url,text)
-- return runwithvars(splitatcommas("url,text"), {url,text},
-- doblogme,
-- "<a href=\"[$ url]\">[$ text]</a>")
-- end
--
-- But hey, beware!!! DEF passes arguments (and restores them later)
-- using global variables... This makes a big buggy mess when we start
-- to call one DEF-defined word from inside another one, or when we
-- choose bad names for the arguments. Welcome to the worst side of
-- the macro languages of the 70's!... :-)
-- See: (find-elnode "Scope")
-- (find-blogme3file "definers.lua" "def =")
-- (find-blogmefile "blogme2-middle.lua" "`[DEF ...]'")
-- (find-blogmefile "blogme2-middle.lua" "withvars =")
-- (find-anggfile "TH/index.blogme3" "DEF BOX")
runwithvars = function (varnames, values, f, ...)
local backups = {}
for i,varname in ipairs(varnames) do -- for each varname,
backups[i] = _G[i] -- backup global variable,
_G[varname] = values[i] -- then set global variable
end
local results = pack(f(unpack(arg))) -- run f (in the changed environment)
for i,varname in ipairs(varnames) do -- for each varname,
_G[varname] = backups[i] -- restore global variable
end
return unpack(results)
end
splitatcommas = function (str) return split(str, "([^,]+)") end
wrap_rwvdoblogme = function (vars, body)
return format("runwithvars(splitatcommas(%q), {%s},\
doblogme,\
%q)", vars, vars, body)
end
def [[ DEF 4Q name,apspec,arglist,body
def_set_AG(name, apspec, wrap_fr(arglist, wrap_rwvdoblogme(arglist, body)))
]]
--------[ DDEF ]--------
-- DDEF: another quick hack to define blogme words in pure blogme.
-- The name means "Dollar(-centric) DEF". Calls to this word look like
-- calls to DEF, but without the quotes to convert the "body" into a
-- Lua expression; the body is expanded immediately using `doblogme',
-- then the result is converted to a constant Lua string expression
-- with format("%q", ...), and then the dollar-constructs - $varname
-- and $(varname) - in that are transformed by `undollar' into code to
-- concatenate the value of the variable "varname" at the string at thet
-- point. No error-checking is performed at all!
-- This is experimental and has not been adequately tested.
--
-- An example (not checked):
--
-- [DDEF FOOHREF 2 url,text foo[HREF $url $text]bar ]
-- --> body = [[foo<a href="$url">$text</a>bar ]]
-- --> qbody = [["foo<a href=\"$url\">$text</a>bar "]]
-- --> eqbody = [["foo<a href=\""..url.."\">"..text.."</a>bar "]]
--
-- def [[FOOHREF 2 url,text "foo<a href=\""..url.."\">"..text.."</a>bar "]]
--
-- def_set_AG("FOOHREF", "2", "url,text",
-- wrap_fr [["foo<a href=\""..url.."\">"..text.."</a>bar "]])
--
-- Note that the arglist parser specifier is "4", not "4Q" - this
-- makes the "body" be expanded by the argument parser before becoming
-- the argument for a Lua function.
def [[ DDEF 4 name,apspec,arglist,body
def_set_AG(name, apspec,
wrap_fr(arglist, undollar(format("%q", body)))) ]]
---- Untested:
-- ddef = function (defstr)
-- local name, apspec, arglist, body = def_split(defstr)
-- def_set_AG(name, apspec, wrap_fr(arglist, undollar(format "%s", body)))
-- end
--[=[
* (eepitch-lua51)
dofile "brackets.lua"
dofile "definers.lua"
PP(_AA)
--]=]
-- (find-lua51file "src/lstrlib.c" "case 's' : res = isspace(c)")
-- (find-node "(libc)Classification of Characters" "int isspace")
-- Local Variables:
-- coding: raw-text-unix
-- ee-anchor-format: "«%s»"
-- End: