Warning: this is an htmlized version!
The original is across this link,
and the conversion rules are here.
-- definers.lua: blogme3's def, DEF and friends, rewritten.
-- This file:
--   http://angg.twu.net/blogme4/definers.lua.html
--   http://angg.twu.net/blogme4/definers.lua
--                (find-blogme4 "definers.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2011feb17
-- License: GPL3
--
-- See: (find-angg "LUA/canvas2.lua" "Class")
--      (find-blogme3 "definers.lua")
--      (find-blogme3 "definers.lua" "def")


-- «.undollar»		(to "undollar")
-- «.BlogmeWord»	(to "BlogmeWord")
-- «.def»		(to "def")
-- «.test-def»		(to "test-def")



require "eoo"  -- (find-blogme4 "eoo.lua")


-- «undollar»  (to ".undollar")
-- 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 = code:gsub("%$([a-z]+)", "\"..%1..\"")
    code = code:gsub("%$(%b())",   "\"..%1..\"")
    code = code:gsub("%$(%b[])", function (s)
        return "]]..("..s:sub(2, -2)..")..[["
      end)
    return code
  end
wrap_f = function (arglist, statements)
    return "function ("..arglist..")\n"..statements.."\nend"
  end
wrap_fr = function (arglist, expr)
    return "function ("..arglist..")\nreturn "..expr.."\nend"
  end
wrap_rf = function (arglist, statements)
    return "return function ("..arglist..")\n"..statements.."\nend"
  end
wrap_rfr = function (arglist, expr)
    return "return function ("..arglist..")\nreturn "..expr.."\nend"
  end
-- wrap_fu  = function (argl, body) return wrap_f (argl, undollar(body)) end
-- wrap_fru = function (argl, body) return wrap_fr(argl, undollar(body)) end



-- «BlogmeWord»  (to ".BlogmeWord")
-- The class of blogme words,
-- and a way to create a bword from a string.
-- See: (find-blogme4 "eoo.lua")
--
BlogmeWord = Class {
  type    = "BlogmeWord",
  __index = {
    fu  = function (bw) return wrap_f (bw.arglist, undollar(bw.body)) end,
    fru = function (bw) return wrap_fr(bw.arglist, undollar(bw.body)) end,
  },
  __call = function (bw, ...) return bw.fun(...) end,
}
BlogmeWordFrom = function (defspec)
    local name, argp, arglist, body =
      string.match(defspec, "^%s*(%S+)%s+(%S+)%s+(%S+)%s(.*)")
    assert(name, "defspec is not of the form \"name argp arglist body\"")
    local bw = BlogmeWord {name=name, argp=argp, arglist=arglist, body=body}
    return bw
  end



-- «def»  (to ".def")
-- Old version:       (find-blogme3 "definers.lua" "def")
-- Examples of usage: (find-blogme4 "anggdefs.lua")
-- Here we define Def_, Def, def_ and def, where:
--   Def_ and def_ store a BW in _B,
--   Def  and def  store a BW in _B and in _G.
--   Def_ and Def expect a series of statements,
--   def_ and def expect an expression.
-- and:
--   _G is the table of global symbols (usually lua functions),
--   _B is the table of blogme words (instances of BlogmeWord).
-- Here's the trick for remembering which function does what.
-- In "Def_" and "Def" everything is bigger: they start with an
--   uppercase letter (yeah!) and they expect a longer, more serious
--   piece of code, made of several statements, possibly even starting
--   with declarations of local variables, and usually ending with a
--   "return". In contrast, "def_" and "def" are for smaller things,
--   and they expect an expression (because internally they prepend a
--   "return " to their code).
-- An "_" always suggests "low-level", so "Def_" and "def_" do less
--   than "Def" and "def". "Def_" and "def_" install the newly-defined
--   word only in _B, while "Def" and "def" also copy it to _G.
_B = {}
Def_ = function (defspec, prefix)
    local bw = BlogmeWordFrom(defspec)
    local arglist = bw.arglist
    local body    = bw.body
    local code    = wrap_rf(arglist, (prefix or "")..undollar(body))
    -- PP(bw.name, code)
    bw.fun   = assert(loadstring(code, bw.name))()
    _B[bw.name] = bw
    return bw
  end
Def = function (defspec, prefix)
    local bw = Def_(defspec, prefix)
    _G[bw.name] = bw
    return bw
  end
def_ = function (defspec) return Def_(defspec, "return ") end
def  = function (defspec) return Def (defspec, "return ") end



-- dump-to: tests
--[==[
-- «test-def»  (to ".test-def")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
-- userocks()
-- chdir "~/blogme4/"
require "def"

_B = {}
def [[ H2   1   str    "<h2>$str</h2>\n" ]]
PP(_B)
--> {"H2"={"arglist"="str", "body"="   \"<h2>$str</h2>\\n\" ", "fun"=<function: 0x9763268>, "name"="H2", "nargs"="1"}}
PP(_B.H2)
-->       {"arglist"="str", "body"="   \"<h2>$str</h2>\\n\" ", "fun"=<function: 0x9763268>, "name"="H2", "nargs"="1"}
= _B.H2:fru()
--> function (str)
--  return    "<h2>"..str.."</h2>\n" 
--  end
= _B.H2 "foo"
-->  <h2>foo</h2>
=    H2 "foo"
-->  <h2>foo</h2>
= undollar [[ "foo $bar plic" ]]
-->  "foo "..bar.." plic" 
= undollar [[ "foo$(bar)plic" ]]
-->  "foo"..(bar).."plic" 
= undollar " [[foo$[1+2]bar]] "
-->  [[foo]]..(1+2)..[[bar]] 

--]==]




-- Local Variables:
-- coding:             raw-text-unix
-- ee-anchor-format:   "«%s»"
-- End: