Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
--   http://anggtwu.net/LUA/Dednat7.lua.html
--   http://anggtwu.net/LUA/Dednat7.lua
--          (find-angg "LUA/Dednat7.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
--
-- The core of dednat6, rewritten as a small number of classes with
-- nice __tostring metamethods and nice test blocks. Based on:
--   (find-angg "LATEX/dednat6/minimalcore.lua")
--   (find-dn6file "minimalcore.lua")
--
-- Typically loaded by:
--   (find-angg "LUA/dednat7load.lua")

-- «.Head»		(to "Head")
-- «.Head-tests»	(to "Head-tests")
-- «.Heads»		(to "Heads")
--   «.lua-head»	(to "lua-head")
-- «.Heads-tests»	(to "Heads-tests")
-- «.AllTeXFiles»	(to "AllTeXFiles")
-- «.AllTeXFiles-tests»	(to "AllTeXFiles-tests")
-- «.TeXFile»		(to "TeXFile")
-- «.TeXFile-tests»	(to "TeXFile-tests")
-- «.Block»		(to "Block")
--   «.Block-getblock»	(to "Block-getblock")
-- «.Block-tests»	(to "Block-tests")
-- «.Blocks»		(to "Blocks")
-- «.Blocks-tests»	(to "Blocks-tests")
-- «.texfile0»		(to "texfile0")
-- «.high-level-test»	(to "high-level-test")

require "Output1"    -- (find-angg "LUA/Output1.lua")



--  _   _                _ 
-- | | | | ___  __ _  __| |
-- | |_| |/ _ \/ _` |/ _` |
-- |  _  |  __/ (_| | (_| |
-- |_| |_|\___|\__,_|\__,_|
--                         
-- Typical usage: when we do something like this
--   heads:registerhead "%L" {name="lua", action=function () end}
-- this adds a new "Head" object to "heads".
-- See: (to "Heads")
--      (to "Heads-tests")
--      (find-dn6 "heads6.lua" "abbrev-head")
--      (find-dn6 "heads6.lua" "lua-head")
--
-- «Head»  (to ".Head")

Head = Class {
  type = "Head",
  __tostring = function (head) return mytostringv(head) end,
  __index = {
  },
}

-- «Head-tests»  (to ".Head-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Dednat7.lua"
= Head {name = "foo", action = function () end}

--]]




--  _   _                _     
-- | | | | ___  __ _  __| |___ 
-- | |_| |/ _ \/ _` |/ _` / __|
-- |  _  |  __/ (_| | (_| \__ \
-- |_| |_|\___|\__,_|\__,_|___/
--
-- Typical usage:
--   heads = Heads {}
--   heads:registerhead "%L" {name="lua",  action=function () end}
--   heads:registerhead "%:" {name="tree", action=function () end}
-- See: (find-dn6 "heads6.lua" "registerhead")
--
-- «Heads»  (to ".Heads")

Heads = Class {
  type    = "Heads",
  __tostring = function (heads)
      return "Registered heads: "..mapconcat(mytostring, sortedkeys(heads), ", ")
    end,
  __index = {
    headstrfor = function (heads, li)
        local p = function (len)
            local s = li:sub(1, len)
            return heads[s] and s
          end
        return p(5) or p(4) or p(3) or p(2) or p(1) or p(0)
      end,
    headfor = function (heads, li)
        local headstr = heads:headstrfor(li)
        return headstr and heads[headstr]
      end,
    registerhead = function (heads, headstr)
        return function (tbl)
            tbl.headstr = headstr
            heads[headstr] = Head(tbl)
            return heads
          end
      end,
  },
}


-- «lua-head»  (to ".lua-head")
-- Based on: (find-dn6 "heads6.lua" "lua-head")

heads = Heads {}
registerhead = function (...) return heads:registerhead(...) end
registerhead "%L" {
  name   = "lua",
  action = function ()
      local i,j,luacode = tf:getblockstr(3)
      local chunkname = tf.name..":%L:"..i.."-"..j
      assert(loadstring(luacode, chunkname))()
    end,
}

-- «Heads-tests»  (to ".Heads-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Dednat7.lua"
= heads
heads["%LL"] = "bar"
heads["%D"]  = 42
= heads
= heads:headstrfor "%LL bla"
= heads:headstrfor "%L  bla"
= heads:headstrfor "blop"
= heads:headfor    "%LL bla"
= heads:headfor    "%L  bla"
= heads:headfor    "blop"

heads:registerhead "%P" {
  name = "foo",
  action = function () end
}

= heads
= heads["%P"]
= heads:headstrfor "%P bla"
= heads:headfor    "%P bla"

--]]





--     _    _ _ _____   __  _______ _ _           
--    / \  | | |_   _|__\ \/ /  ___(_) | ___  ___ 
--   / _ \ | | | | |/ _ \\  /| |_  | | |/ _ \/ __|
--  / ___ \| | | | |  __//  \|  _| | | |  __/\__ \
-- /_/   \_\_|_| |_|\___/_/\_\_|   |_|_|\___||___/
--                                                
-- «AllTeXFiles»  (to ".AllTeXFiles")

AllTeXFiles = Class {
  type    = "AllTeXFiles",
  __tostring = function (atf)
      if #keys(atf) == 0 then return "(no TeXFiles objects registered)" end
      local f = function (k) return format("%q -> %d lines", k, #atf[k]) end
      local ks = sorted(keys(atf))
      return mapconcat(f, ks, "\n")
    end,
  __index = {
  },
}

alltexfiles = AllTeXFiles {}

-- «AllTeXFiles-tests»  (to ".AllTeXFiles-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Dednat7.lua"
= alltexfiles
alltexfiles["a.tex"]  =        {"a", "b", "c", "d", "e"}
alltexfiles["bb.tex"] = VTable {"a", "b", "c", name="bb.tex"}
= alltexfiles
= alltexfiles["a.tex"]
= alltexfiles["bb.tex"]

--]==]




--  _____   __  _______ _ _      
-- |_   _|__\ \/ /  ___(_) | ___ 
--   | |/ _ \\  /| |_  | | |/ _ \
--   | |  __//  \|  _| | | |  __/
--   |_|\___/_/\_\_|   |_|_|\___|
--                               
-- A TeXFile object is a .tex file split into lines and with a
-- "name" field. A texfile object also has a "blocks" field,
-- that is initially empty.
--
-- Based on: (find-dn6 "minimalcore.lua" "heads")
-- «TeXFile»  (to ".TeXFile")

TeXFile = Class {
  type = "TeXFile",
  read = function (name)
      return TeXFile.from(name, ee_readfile(name))
    end,
  from = function (name, lines)
      return TeXFile.from0(name):setlines(lines):register()
    end,
  from0 = function (name)
      return TeXFile {name=name, blocks=Blocks{}}
    end,
  __tostring = function (tf)
      return format("TeXFile: %q\n%s\n%s",
                    tf.name,
                    tf:linestostring(),
                    tostring(tf.blocks))
    end,
  __index = {
    line          = function (tf,k) return tf[k] end,
    linetostring  = function (tf,k) return format("%3d: %s", k, tf[k]) end,
    linestostring = function (tf,i,j)
        i,j = i or 1, j or #tf
        local f = function (k) return tf:linetostring(k) end
        return mapconcat(f, seq(i,j), "\n")
      end,
    register = function (tf)
        alltexfiles[tf.name] = tf
        return tf
      end,
    setlines = function (tf, lines)
        if type(lines) == "string" then
          lines = splitlines(unixnewlines(lines))
        end
        for i=1,#lines do tf[i] = lines[i] end
        return tf
      end,
    --
    headstr = function (tf,k) return heads:headstrfor(tf[k]) end,
    head    = function (tf,k) return heads:headfor(tf[k]) end,
    getj    = function (tf,i)
        local j,headstr = i,tf:headstr(i)
        while j<#tf and tf:headstr(j+1)==headstr do j = j+1 end
        return j
      end,
    buildblock = function (tf,i)
        return Block.from(i, tf:getj(i), tf.name, tf:headstr(i))
      end,
    --
    hasblocks    = function (tf)   return #tf.blocks > 0 end,
    lastblock    = function (tf,k) return tf.blocks[#tf.blocks + (k or 0)] end,
    lastj        = function (tf)   return tf:lastblock().j end,
    anotherblock = function (tf)
        if #tf == 0 then return end
        if tf:hasblocks() and tf:lastj() == #tf then return end
        local i  = tf:hasblocks() and tf:lastj()+1 or 1
        local bl = tf:buildblock(i)
        table.insert(tf.blocks, bl)
        return bl
      end,
    --
    lastj_or_0   = function (tf) return tf:hasblocks() and tf:lastj() or 0 end,
    processlast  = function (tf) return tf:lastblock():doaction() end,
    processnext  = function (tf)
        if tf:anotherblock() then
          -- if verbose then print(tf:lastblock())end
          tf:processlast()
        end
      end,
    processuntil = function (tf,puline,verbose)
        tf.puline = puline                      -- used by output:puinfo()
        if verbose then output:puinfo() end
        if puline > #tf then puline = #tf end   -- to avoid a loop
        while tf:lastj_or_0() < puline do tf:processnext() end
      end,
    --
    -- For compatibility with Dednat6:
    getblock     = function (tf,...) return thisblock:getblock(...) end,
    getblockstr  = function (tf,...) return thisblock:getblockstr(...) end,
    luachunkname = function (tf,...) return thisblock:luachunkname(...) end,
    hyperlink    = function (tf,...) return thisblock:hyperlink(...) end,
  },
}

pu = function (puline,verbose)
    tf:processuntil(puline or tex.inputlineno, verbose)
  end


-- «TeXFile-tests»  (to ".TeXFile-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Dednat7.lua"
lis = VTable {"1", "2", "%L print(3,", "%L 4)", "5", "6"}
tf  = TeXFile.from("a.tex", lis)
= alltexfiles
= alltexfiles["a.tex"]

heads = Heads {}
heads:registerhead "%:" {name="tree", action=function () end}
heads:registerhead "%L" {
    name   = "lua",
    action = function ()
        thisbody = thisblock:untabifys(3, "", "\n")
        print(thisbody)   -- global
      end,
  }
= heads

= tf:anotherblock()
= tf:anotherblock()
= tf:anotherblock()
= tf:anotherblock()
= tf
= tf.blocks
= tf.blocks[2]
= tf.blocks[2]:untabifys()
= tf.blocks[2]:untabifys(nil, nil, "\n")
= tf.blocks[2]:untabifys(3,   nil, "\n")
= tf.blocks[2]:untabifys(3,   "",  "\n")
= tf.blocks[2]:untabifys(3,   "---", "\n")

= tf.blocks[1]:head()
= tf.blocks[2]:head()
= tf.blocks[1]:action()
= tf.blocks[2]:action()
= tf.blocks[1]:doaction()
= tf.blocks[2]:doaction()

tf.blocks = Blocks {}  -- reset
= tf
= tf:lastj_or_0()
tf:processnext("verbose")
tf:processnext("verbose")
tf:processnext("verbose")

tf.blocks = Blocks {}  -- reset
tf:processuntil(200, "verbose")

--]==]





--  ____  _            _    
-- | __ )| | ___   ___| | __
-- |  _ \| |/ _ \ / __| |/ /
-- | |_) | | (_) | (__|   < 
-- |____/|_|\___/ \___|_|\_\
--                          
-- «Block»  (to ".Block")
--
Block = Class {
  type = "Block",
  from = function (i,j,name,headstr)
      return Block {i=i, j=j, name=name, headstr=headstr}
    end,
  __tostring = function (bl) return bl:tostring() end,
  __index = {
    ijtostring   = function (bl) return bl.i.."--"..bl.j end,
    luachunkname = function (bl) return bl.name..":%L:"..bl.i.."-"..bl.j end,
    tostring     = function (bl) return
        format("%7s in %s: %s", bl:ijtostring(), bl.name, mytostring(bl.headstr))
      end,
    --
    texlines = function (bl)    return alltexfiles[bl.name] end,
    line     = function (bl, k) return bl:texlines()[k] end,
    head     = function (bl)    return bl.headstr and heads[bl.headstr] end,
    action   = function (bl)    return bl:head()  and bl:head().action  end,
    doaction = function (bl)
        thisblock = bl              -- set a global
        lastheadblock = bl          -- same, for compatibility with dednat6
        local action = bl:action()
        if action then output:blockinfo(); return action() end
      end,
    --
    untabify = function (bl, k, cut, newprefix)
        local line = untabify8(bl:line(k))
        if cut then line = line:sub(cut+1) end
        if newprefix then line = newprefix..line end
        return line
      end,
    untabifys = function (bl, cut, newprefix, sep)
        local f = function (k) return bl:untabify(k, cut, newprefix) end
        local o = map(f, seq(bl.i, bl.j))
        if sep then o = table.concat(o, sep) end
        return o
      end,
    --
    -- «Block-getblock»  (to ".Block-getblock")
    -- (find-dn6 "minimalcore.lua" "Block" "getblock =")
    getblock = function (bl, cut)
        local i,j = bl.i,bl.j
        local lines = bl:untabifys(cut or #(thisblock.headstr)+1)
        return i,j,lines
      end,
    getblockstr = function (bl, cut)
        local i,j = bl.i,bl.j
        local blockstr = bl:untabifys(cut, nil, "\n")
        return i,j,blockstr
      end,
    --
    -- Old, recycle:
    hyperlink = function (bl)
        return format("In the \"%s\"-block in lines %d--%d",
                      lastheadblock.headstr, lastheadblock.i, lastheadblock.j)
      end,
  },
}

-- «Block-tests»  (to ".Block-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Dednat7.lua"
= Block.from(4,6, "a.tex", "%L")
= Block.from(4,6, "b.tex")

--]]



--  ____  _            _        
-- | __ )| | ___   ___| | _____ 
-- |  _ \| |/ _ \ / __| |/ / __|
-- | |_) | | (_) | (__|   <\__ \
-- |____/|_|\___/ \___|_|\_\___/
--                              
-- «Blocks»  (to ".Blocks")

Blocks = Class {
  type    = "Blocks",
  __tostring = function (bls)
      return "Blocks:\n"..mapconcat(tostring, bls, "\n")
    end,
  __index = {
  },
}

-- «Blocks-tests»  (to ".Blocks-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Dednat7.lua"
bls = Blocks {
  Block.from(1,4, "a.tex"),
  Block.from(5,6, "a.tex", "%L"),
  Block.from(7,8, "a.tex"),
}
= bls

--]]



-- Old:
-- «texfile0»  (to ".texfile0")

texfile0 = function (fname)
    -- texlines = TexLines.read(fname)
    -- tf = texlines:toblock()
    tf = TeXFile.read(fname)
    texlines = tf
  end
texfile = function (fname)
    texfile0(fname..".tex")
  end




-- «high-level-test»  (to ".high-level-test")
--[[
-- (find-angg "LUA/Dednat7.lua" "lua-head" "i,j,luacode =")
-- (find-angg "LUA/Dednat7.lua" "Block" "untabifys =")

*  (ee-write-string "1\n2\n%L print(3,\n%L 4)\n5\n6\n" "/tmp/o.tex")
** (find-fline "/tmp/o.tex")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Dednat7.lua"
tf = TeXFile.read("/tmp/o.tex")
= tf
pu(#tf+1, "verbose")
PPV(tf)
pu(#tf+2, "verbose")
pu(#tf+3)

= tf
pu(6)
= tf
PP(tf)
= tf.blocks
= tf.blocks[2]

registerhead "%L" {
  name   = "lua",
  action = function ()
      local i,j,luacode = tf:getblockstr(3)
      local chunkname = tf:luachunkname()
      -- local i,j,luacode = thisblock:getblockstr(3)
      -- local chunkname = thisblock:luachunkname()
      print(thisblock)
      print(i,j,luacode,chunkname)
    end,
}






--]]









-- (defun e () (interactive) (find-angg "LUA/DedNat7.lua"))


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