Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
--   http://anggtwu.net/LUA/BlogMe3.lua.html
--   http://anggtwu.net/LUA/BlogMe3.lua
--          (find-angg "LUA/BlogMe3.lua")
-- Author: Eduardo Ochs <[email protected]>
--
-- This is a rewrite of blogme3/brackets.lua.
-- The code is as similar to the original as possible,
-- but structured as classes, and with test blocks.
-- Version: 2023feb02
--
-- See: (find-blogme3 "brackets.lua")
--      (find-blogme4 "brackets.lua" "bracketstructure")
--      (find-blogme3 "definers.lua")
--      (find-blogme3 "definers.lua" "_AA")
--
-- (defun e () (interactive) (find-angg "LUA/BlogMe3.lua"))

-- «.BlogMe»			(to "BlogMe")
--   «.BlogMe-tostring»		(to "BlogMe-tostring")
--   «.BlogMe-lowlevel»		(to "BlogMe-lowlevel")
--   «.BlogMe-mygather»		(to "BlogMe-mygather")
--   «.BlogMe-midlevel»		(to "BlogMe-midlevel")
--   «.BlogMe-highlevel»	(to "BlogMe-highlevel")
--   «.BlogMe-evalblock»	(to "BlogMe-evalblock")
--   «.BlogMe-run»		(to "BlogMe-run")
-- «.BlogMe-highlevel-tests»	(to "BlogMe-highlevel-tests")
-- «.BlogMe-lowlevel-tests»	(to "BlogMe-lowlevel-tests")
-- «.BlogMe-midlevel-tests»	(to "BlogMe-midlevel-tests")
-- «._A-and-_B»			(to "_A-and-_B")
-- «._A-and-_B-tests»		(to "_A-and-_B-tests")
-- «.BlogMeShort»		(to "BlogMeShort")
-- «.BlogMeShort-tests»		(to "BlogMeShort-tests")
-- «.BlogMeDef»			(to "BlogMeDef")
-- «.BlogMeDef-tests»		(to "BlogMeDef-tests")
-- «.def»			(to "def")
-- «.def-tests»			(to "def-tests")


--  ____  _             __  __      
-- | __ )| | ___   __ _|  \/  | ___ 
-- |  _ \| |/ _ \ / _` | |\/| |/ _ \
-- | |_) | | (_) | (_| | |  | |  __/
-- |____/|_|\___/ \__, |_|  |_|\___|
--                |___/             
--
-- «BlogMe»  (to ".BlogMe")
BlogMe = Class {
  type = "BlogMe",
  from = function (subj)
      local n2pos,pos2pos,pos2n = BlogMe.bracketstructure(subj)
      return BlogMe {
        subj    = subj,
        pos     = 1,
        n2pos   = HTable(n2pos),
        pos2n   = HTable(pos2n),
        pos2pos = HTable(pos2pos),
      }
    end,
  --
  -- See: (find-blogme3 "brackets.lua" "bracketstructure")
  bracketstructure = function (subj)
      local pos2n, n2pos, pos2pos = {}, {}, {}
      local opens = {}
      local n = 0
      for pos,bracket in string.gmatch(subj, "()([%[%]])") do
        n = n + 1
        pos2n[pos] = n
        n2pos[n] = pos
        if bracket == "[" then 
          table.insert(opens, pos)
          pos2pos[pos] = "?"
        else    -- bracket == "]"
          if #opens > 0 then
            local openpos = table.remove(opens)
            pos2pos[openpos] = pos
            pos2pos[pos] = openpos
  	  else
            error("Extra closing bracket at pos " .. pos)
          end
        end
      end
      if #opens > 0 then
        error("Extra opening bracket at pos " .. opens[#opens])
      end
      return n2pos, pos2pos, pos2n
    end,
  __tostring = function (b) return b:tostring() end,
  __index = {
    --
    -- See: (find-es "lua5" "posnumbers")
    -- «BlogMe-tostring»  (to ".BlogMe-tostring")
    tostring = function(b)
        local line2,line1 = b:posnumbers(1, #b.subj)
        local caret = b.pos and (string.rep(" ", b.pos-1).."^") or ""
        local fmt = "          %s\n" ..
                    "          %s\n" ..
                    "subj    = %s\n" ..
                    "          %s\n" ..
                    "pos     = %s\n" ..
                    "n2pos   = %s\n" ..
                    "pos2n   = %s\n" ..
                    "pos2pos = %s\n"
        return format(fmt, line1, line2, b.subj,
                      caret, tostring(b.pos),
                      tostring(b.n2pos), tostring(b.pos2n),
                      tostring(b.pos2pos))
      end,
    posnumbers = function (b,i,j)
        local fmt = "%2d"
        local hnum = function (k) return format(fmt, k) end
        local digit = function (k, pos) return hnum(k):sub(pos,pos) end
        local line = function (pos)
            local f = function (k) return digit(k, pos) end
            return mapconcat(f, seq(i,j), "")
          end
        return line(2), line(1)
      end,
    --
    -- «BlogMe-lowlevel»  (to ".BlogMe-lowlevel")
    -- See: (find-blogme3 "brackets.lua" "parsers")
    -- "wchars" means "word chars",
    -- "rchars" means "regular chars".
    parsebypattern = function (b, pat)
        local capture, newpos = string.match(b.subj, pat, b.pos)
        if newpos then b.pos = newpos; return capture end
      end,
    parsespaces = function (b) return b:parsebypattern("^([ \t\n]+)()") end,
    parsewchars = function (b) return b:parsebypattern("^([^ \t\n%[%]]+)()") end,
    parserchars = function (b) return b:parsebypattern("^([^%[%]]+)()") end,
    parseblock  = function (b)
        if b.pos2pos[b.pos] and b.pos < b.pos2pos[b.pos] then
          local inside = b.pos + 1
          b.pos = b.pos2pos[b.pos] + 1
          return inside
        end
      end,
    --
    -- «BlogMe-mygather»  (to ".BlogMe-mygather")
    -- (find-blogme3 "brackets.lua" "myconcat")
    -- (find-blogme3 "brackets.lua" "mygather")
    mygather = function (b, methodname)
        local T = {}
        while true do
          local val = b[methodname](b)
          if val then table.insert(T, val) else return T end
        end
      end,
    myconcat = function (b, T)
        if #T >  1 then return table.concat(T, "") end
        if #T == 1 then return T[1] end
      end,
    myconcatgather = function (b, methodname)
        return b:myconcat(b:mygather(methodname))
      end,
    --
    -- «BlogMe-midlevel»  (to ".BlogMe-midlevel")
    -- See: (find-blogme3 "brackets.lua" "parsers_")
    -- "q" means "quoted".
    -- "v" means "value", or "evaluated".
    -- The method evalblock is defined at the end of the class.
    --
    readvblock_ = function (b)
        local blockstart = b:parseblock()
        if blockstart then return b:evalblock(blockstart) or "" end
      end,
    readqblock_ = function (b)
        local blockstart = b:parseblock()
        if blockstart then return b.subj:sub(blockstart - 1, b.pos - 1) end
      end,
    readwcharsorqblock_ = function (b) return b:parsewchars() or b:readqblock_() end,
    readwcharsorvblock_ = function (b) return b:parsewchars() or b:readvblock_() end,
    readrcharsorqblock_ = function (b) return b:parserchars() or b:readqblock_() end,
    readrcharsorvblock_ = function (b) return b:parserchars() or b:readvblock_() end,
    readqword__ = function (b) return b:myconcatgather("readwcharsorqblock_") end,
    readvword__ = function (b) return b:myconcatgather("readwcharsorvblock_") end,
    readqrest__ = function (b) return b:myconcatgather("readrcharsorqblock_") end,
    readvrest__ = function (b) return b:myconcatgather("readrcharsorvblock_") end,
    readqword_  = function (b) b:parsespaces(); return b:readqword__() end,
    readvword_  = function (b) b:parsespaces(); return b:readvword__() end,
    readqrest_  = function (b) b:parsespaces(); return b:readqrest__() end,
    readvrest_  = function (b) b:parsespaces(); return b:readvrest__() end,
    --
    -- «BlogMe-highlevel»  (to ".BlogMe-highlevel")
    -- See: (find-blogme3 "brackets.lua" "readvword")
    readqword   = function (b) return b:readqword_() or "" end,
    readvword   = function (b) return b:readvword_() or "" end,
    readqrest   = function (b) return b:readqrest_() or "" end,
    readvrest   = function (b) return b:readvrest_() or "" end,
    readqlist   = function (b) return b:mygather("readqword_") end,
    readvlist   = function (b) return b:mygather("readvword_") end,
    readqargs   = function (b) return unpack(b:readqlist()) end,
    readvargs   = function (b) return unpack(b:readvlist()) end,
    --    
    readqqrest    = function (b) return b:readqword(), b:readqrest()    end,
    readqqqrest   = function (b) return b:readqword(), b:readqqrest()   end,
    readqqqqrest  = function (b) return b:readqword(), b:readqqqrest()  end,
    readqqqqqrest = function (b) return b:readqword(), b:readqqqqrest() end,
    readvvrest    = function (b) return b:readvword(), b:readvrest()    end,
    readvvvrest   = function (b) return b:readvword(), b:readvvrest()   end,
    readvvvvrest  = function (b) return b:readvword(), b:readvvvrest()  end,
    readvvvvvrest = function (b) return b:readvword(), b:readvvvvrest() end,
    --
    readqqlist    = function (b) return b:readqword(), b:readqlist()    end,
    readqqqlist   = function (b) return b:readqword(), b:readqqlist()   end,
    readqqqqlist  = function (b) return b:readqword(), b:readqqqlist()  end,
    readqqqqqlist = function (b) return b:readqword(), b:readqqqqlist() end,
    readvvlist    = function (b) return b:readvword(), b:readvlist()    end,
    readvvvlist   = function (b) return b:readvword(), b:readvvlist()   end,
    readvvvvlist  = function (b) return b:readvword(), b:readvvvlist()  end,
    readvvvvvlist = function (b) return b:readvword(), b:readvvvvlist() end,
    --
    nop           = function (b) return end,
    --
    -- «BlogMe-evalblock»  (to ".BlogMe-evalblock")
    -- See: (find-blogme3 "brackets.lua" "evalblock")
    readeval = function (b)
        b:parsespaces()
        local word = b:parsewchars()     -- should this be global (for debugging)?
        local headfun = _B[word] or _G[word] or error("Not in _B or _G: " .. word)
        local argsfun = _A[word]             or error("Not in _A: "       .. word)
        return headfun(b[argsfun](b))
      end,
    evalblock = function (b, start)
        local oldpos = b.pos
        b.pos = start
        local result = b:readeval()
        b.pos = oldpos
        return result
      end,
    --
    -- «BlogMe-run»  (to ".BlogMe-run")
    runat = function (b, pos, parsername)
        if pos then b.pos = pos end
        PP(b[parsername](b))
      end,
    runats = function (b, pos, parsernames)
        for _,parsername in ipairs(split(parsernames)) do
          b.pos = pos
          printf("%-21s", parsername..":")
          PP(b[parsername](b))
        end
      end,
    run = function (b)
        b.pos = 1
        return b["readvrest"](b)
      end,
  },
}


-- «BlogMe-highlevel-tests»  (to ".BlogMe-highlevel-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "BlogMe3.lua"
def [[ IT  1 str  "<i>$str</i>"    ]]
def [[ EM  1 str  "<em>$str</em>"  ]]

= IT("foo")
bm = BlogMe.from [[ abc[IT de]fg  hi ]]
bm:runats(1, [[ readqlist
                readqrest
                readqqrest
                readqqqrest
                readqword   ]])
bm:runats(1, [[ readvlist
                readvrest
                readvvrest
                readvvvrest
                readvword   ]])

--]==]




-- «BlogMe-lowlevel-tests»  (to ".BlogMe-lowlevel-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "BlogMe.lua"
b = BlogMe.from [=[ a[b[c]d[e]]f ]=]
= b
PP(b:parsespaces(),
   b:parsewchars(),
   b:parseblock(),
   b:parsewchars(),
   b.pos)

-- «BlogMe-midlevel-tests»  (to ".BlogMe-midlevel-tests")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "BlogMe.lua"
prb = function () print(b) end

b = BlogMe.from [=[  a b[c]d e[f [g h]][i j]k l m ]=]
b.pos = 12
= b
= b:readqblock_()

b.pos = 12
= b
PP(b:readqblock_(),
   b:readqblock_(),
   b:readqblock_())

b.pos = 11
= b
PP(b:readwcharsorqblock_(),
   b:readwcharsorqblock_(),
   b:readwcharsorqblock_(),
   b:readwcharsorqblock_(),
   b:readwcharsorqblock_())

b.pos = 12
= b
PP(b:mygather("readqblock_"))
b.pos = 12
PP(b:myconcatgather("readqblock_"))

b.pos = 11
= b
PP(b:readqword_())
b.pos = 11; PP(b:readqrest_())

b.pos = 11; PP(b:readqword())
b.pos = 11; PP(b:readqrest())
b.pos = 11; PP(b:readqqrest())
b.pos = 11; PP(b:readqqqrest())
b.pos = 11; PP(b:readqqqqrest())

b.pos = 11; PP(b:readqlist())
b.pos = 11; PP(b:readqqlist())
b.pos = 11; PP(b:readqqqlist())
b.pos = 11; PP(b:readqqqqlist())

--]==]



--------[ Parsers and Evaluators ]--------
-- This is the most twisted part of blogme3...
-- That's because it involves some recursions.
-- The problem: evaluating a "block" usually involve "reading vwords",
-- where a vword is the "value of a (big) word"; and to obtain the
-- value of a big word we need to evaluate all the blocks in it.
--
-- Something like "[print foo[+ 22 33]bar]" is a "block", and parsing
-- it with parseblock() just returns the position of the "[" (as a
-- number!) and advances pos past the "]"... But that's just the
-- "syntactical level", and that's the easy part; above that there's a
-- "semantical level", where blocks can have "values", obtained by
-- evaluation. To understand how evaluation works we need to
-- understand a function, "evalblock(start)" - the argument "start" is
-- the position of a "[", as a number -, and two tables, _A and _B,
-- whose keys are strings and whose values are functions:
--
--   _A["print"] is the "argument parser" for "print";
--   _B["print"] is the "blogme code" for "print".
--
-- They are similar to Lua's "_G": _G["print"] is the "Lua code" for
-- "print".
--
-- So: if we run "evalblock(start)" after the "parseblock()" then
-- blogme tries to execute the "print": it first parses "print", then
-- uses the function in _A["print"] to parse the argument list, then
-- runs the code in _B["print"] with those arguments:
-- _B["print"]("foo55bar").
--
-- _A["print"] knows that the arguments are a series of "vwords" -
-- "values of (big) words". A similar idea is that of "qwords" -
-- "quoted (big) words". Parsing "foo[+ 22 33]bar" as a qword (by
-- calling getqword() with pos at the "f") would return this, as a
-- string: "foo[+ 22 33]bar"; but parsing "foo[+ 22 33]bar" as vword
-- (by calling readqword() with pos at the "f") involves evaluating the
-- blocks in the way - and "[+ 22 33]" evaluates to 55 (a number), and
-- myconcat {"foo", 55, "bar"} returns "foo55bar".
--
-- _A["print"] is set to `readvargs' - a function that returns a
-- variable number of results. In "[HREF http://foo/bar Foo bar]" the
-- function in _A["HREF"] is `readvvrest', that returns exactly two
-- results: first a vword, then a "vrest" - and "vrests" are like
-- vwords, but whitespace chars are treated as regular chars, not as
-- separators; the result of running readvrest() with pos at the "F"
-- is "Foo bar".
--
-- "readvrest" returns the "rest of the arguments" as a string;
-- "readvlist" returns it as an array of vwords; and "readvargs" is
-- like "readvlist" but varargs-ish - it returns the vwords that it
-- can read as several values, like in "return v1, v2, v3". (Note: the
-- choice of terms is not very good - "list" could become "array", and
-- maybe "args" should become "list"...)
--
-- "Parsers" return positions, as numbers; "readers" return "values",
-- that are usually strings. Readers are divided into two classes:
-- "quoters", that don't call evalblock and always return strings, and
-- "evaluators", that call evalblock on blocks; all readers use
-- myconcat and mygather to build their results.
--
-- "Parsers" return nil - and don't advance pos - when they "fail";
-- that is, when they can't parse what they expected. "Readers" return
-- the empty string.
--
-- Readers whose names have the suffix "_" (meaning "low-level") don't
-- advance pos when they fail; readers without the "_" in their names
-- are higher-level versions that call "parsespaces" at some places -
-- high-level readers may advance pos past some whitespace then fail,
-- and when that happens pos is not returned to before the whitespace.
--
-- Char classes:   Basic parsers:   Quoters:     Evaluators:
-- wordchar        parsewchars                   evalblock
-- regularchar     parserchars      readqblock   readvblock
-- spacechar       parsespaces      readqword    readvword   
--                 parseblock       readqrest    readvrest    
--                                  readqqrest   readvvrest    
--                                  readqqqrest  readvvvrest    
--                                  readqlist    readvlist    
--                                  readqqlist   readvvlist    
--                                  readqqqlist  readvvvlist    
--                                  readqargs    readvargs

-- (find-blogmefile "blogme2-inner.lua" "-- run_head:")
-- (find-blogmefile "blogme2-middle.lua")


--         _                      _       ____  
--        / \      __ _ _ __   __| |     | __ ) 
--       / _ \    / _` | '_ \ / _` |     |  _ \ 
--      / ___ \  | (_| | | | | (_| |     | |_) |
--  ___/_/   \_\  \__,_|_| |_|\__,_|  ___|____/ 
-- |____|                            |____|     
--
-- «_A-and-_B»  (to "._A-and-_B")
-- The low-level way to define blogme words is with _A and _B.
-- The high-level way is 

-- See: (find-blogme3 "brackets.lua" "evalblock")

_A = VTable {}  -- arglist parser functions for blogme words
_B = VTable {}  -- like _G, but for blogme words

_A["PP"]  = "readvlist"
_B["PP"]  = PP
_A["lua"] = "readqrest"
_B["lua"] = expr

-- «_A-and-_B-tests»  (to "._A-and-_B-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "BlogMe.lua"
b = BlogMe.from [=[ a[lua 2+3]b ]=]
b.pos = 1
= b
= b:readqword()

b.pos = 1
= b
= b:readvword()

--]==]




--  ____  _                            ____  _                _       
-- | __ )| | ___   __ _ _ __ ___   ___/ ___|| |__   ___  _ __| |_ ___ 
-- |  _ \| |/ _ \ / _` | '_ ` _ \ / _ \___ \| '_ \ / _ \| '__| __/ __|
-- | |_) | | (_) | (_| | | | | | |  __/___) | | | | (_) | |  | |_\__ \
-- |____/|_|\___/ \__, |_| |_| |_|\___|____/|_| |_|\___/|_|   \__|___/
--                |___/                                               
--
-- «BlogMeShort»  (to ".BlogMeShort")
-- The global variable "_AA" holds a list of shorthands for "_A" that
-- is used by "def" and friends. This class helps me to initialize and
-- maintain _AA and to avoid certain common errors.
-- See: (find-blogme3 "definers.lua" "_AA")
--
BlogMeShort = Class {
  type      = "BlogMeShort",
  --
  isshort = function (short)
      if not _AA[short] then error("Not in _AA: %S", short) end
      return short
    end,
  islong = function (long)
      local method = BlogMe.__index[long]
      if not method then error(format("%q: argparser method not found", long)) end
      return long
    end,
  expand = function (short)
      return BlogMeShort.isshort(short) and _AA[short]
    end,
  defshort = function (short, long)
      _AA[short] = BlogMeShort.islong(long)
    end,
  defshorts = function (bigstr)
      for short,long in bigstr:gmatch("(%S+)%s+(%S+)") do
        BlogMeShort.defshort(short, long)
      end
    end,
  --
  shorthands = [[
        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
    ]],
  init = function ()
      local bigstr = BlogMeShort.shorthands
      BlogMeShort.defshorts(bigstr)
    end,
}

_AA = VTable {}
BlogMeShort.init()

-- «BlogMeShort-tests»  (to ".BlogMeShort-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "BlogMe3.lua"
= _AA
= BlogMeShort.expand("1L")
= BlogMeShort.expand("99")
= BlogMeShort.defshort("99", "readqrest")
= BlogMeShort.expand("99")
= BlogMeShort.defshort("99", "foo")
= _AA

--]]




--  ____  _             __  __      ____        __ 
-- | __ )| | ___   __ _|  \/  | ___|  _ \  ___ / _|
-- |  _ \| |/ _ \ / _` | |\/| |/ _ \ | | |/ _ \ |_ 
-- | |_) | | (_) | (_| | |  | |  __/ |_| |  __/  _|
-- |____/|_|\___/ \__, |_|  |_|\___|____/ \___|_|  
--                |___/                            
--
-- «BlogMeDef»  (to ".BlogMeDef")
-- The function "def" is defined using this class.
-- The variants "def_", "bdef", and "bdef_" too,
-- and new variants are easy to define.
-- The suffix "_" means: don't add a "return".
-- The prefix "b" means: store in _B, not in _G.
--
-- See: (find-blogme3 "anggdefs.lua" "basic-html")
--      (find-blogme3 "definers.lua" "undollar")
--      (find-blogme3 "definers.lua" "def")
--
BlogMeDef = Class {
  type    = "BlogMeDef",
  fromsplit = function (defstr)
      local pat = "^%s*(%S+)%s+(%S+)%s+(%S+)%s(.*)"
      local name,short,arglist,body0 = defstr:match(pat)
      return BlogMeDef { name=name, short=short, arglist=arglist,
                         body0=body0, body1=body0 }
    end,
  run0 = function (definer, defstr)
      local bd = BlogMeDef.fromsplit(defstr)
      bd:doword(definer)
      return bd
    end,
  run = function (definer, defstr)
      local bd = BlogMeDef.run0(definer, defstr)
      eval(bd.body1)
      return bd
    end,
  --
  __tostring = function (bd)
      return bd:tostring()
    end,
  --
  __index = {
    tostring = function (bd)
        local f = function (o) return tostring(o) end
        return "name:    " .. f(bd.name)
           .."\nshort:   " .. f(bd.short)
           .."\nlong:    " .. f(bd.long)
           .."\narglist: " .. f(bd.arglist)
           .."\nstorein: " .. f(bd.storein)
           .."\nbody0:   " .. f(bd.body0)
           .."\nbody1:   " .. f(bd.body1)
      end,
    --
    doword   = function (bd, word) bd["do_"..word](bd) end,
    dowords  = function (bd, words)
        for _,word in ipairs(split(words)) do bd:doword(word) end
      end,
    --
    do_def   = function (bd) bd:dowords("undollar ret f _G set _A") end,
    do_def_  = function (bd) bd:dowords("undollar     f _G set _A") end,
    do_bdef  = function (bd) bd:dowords("undollar ret f _B set _A") end,
    do_bdef_ = function (bd) bd:dowords("undollar     f _B set _A") end,
    --
    do__G    = function (bd) bd.storein = "_G" end,
    do__B    = function (bd) bd.storein = "_B" end,
    do_ret   = function (bd) bd.body1 = "return "..bd.body1 end,
    --
    do_set   = function (bd)
        local fmt = "%s[%q] = %s"
        bd.body1 = format(fmt, bd.storein, bd.name, bd.body1)
      end,
    do_f     = function (bd)
        local fmt = "function (%s)\n    %s\n  end"
        bd.body1 = format(fmt, bd.arglist, bd.body1)
      end,
    do__A    = function (bd)
        local fmt = '\n_A["%s"] = "%s"\n%s'
        bd.long = BlogMeShort.expand(bd.short)
        bd.body1 = format(fmt, bd.name, bd.long, bd.body1)
      end,
    do_undollar = function (bd)
        bd.body1 = bd.body1:gsub("%$([a-z]+)", "\"..%1..\"")
        bd.body1 = bd.body1:gsub("%$(%b())",   "\"..%1..\"")
        bd.body1 = bd.body1:gsub("%$(%b[])", function (s)
            return "]]..("..strsub(s, 2, -2)..")..[["
          end)
        return bd.body1
      end,
  },
}

-- «BlogMeDef-tests»  (to ".BlogMeDef-tests")
-- See: (find-blogme3 "anggdefs.lua" "basic-html")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "BlogMe3.lua"
bd = BlogMeDef.fromsplit [[ H1 1 str "<h1>$str</h1>\n" ]]
= bd
bd:do_def()
= bd

= BlogMeDef.run0("def",   [[ foo 2 a,b        "<foo>$a:$b</foo>" ]])
= BlogMeDef.run0("def_",  [[ foo 2 a,b return "<foo>$a:$b</foo>" ]])
= BlogMeDef.run0("bdef",  [[ foo 2 a,b        "<foo>$a:$b</foo>" ]])
= BlogMeDef.run0("bdef_", [[ foo 2 a,b return "<foo>$a:$b</foo>" ]])
  BlogMeDef.run ("def",   [[ foo 2 a,b        "<foo>$a:$b</foo>" ]])

= foo("aaa", "bbb")

--]==]


--      _       __ 
--   __| | ___ / _|
--  / _` |/ _ \ |_ 
-- | (_| |  __/  _|
--  \__,_|\___|_|  
--                 
-- «def»  (to ".def")
-- "def" and friends.

def   = function (defstr) return BlogMeDef.run("def",   defstr) end
def_  = function (defstr) return BlogMeDef.run("def_",  defstr) end
bdef  = function (defstr) return BlogMeDef.run("bdef",  defstr) end
bdef_ = function (defstr) return BlogMeDef.run("bdef_", defstr) end

-- «def-tests»  (to ".def-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "BlogMe3.lua"
= def  [[ FOO   2   a,b       "($a:$b)"                    ]]
= def  [[ HREF  2   tgt,body  "<a href=\"$tgt\">$body</a>" ]]
= def  [[ lua:  1Q  code      eval(code)                   ]]
= bdef [[ P     1Q  code      P(code)                      ]]

PP(FOO("xy","zw"))

bm = BlogMe.from [[ ab[FOO c d e]fg ]]
PP(bm:run())

--]==]











-- Local Variables:
-- coding:  utf-8-unix
-- End: