Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
--   http://anggtwu.net/LUA/Indent1.lua.html
--   http://anggtwu.net/LUA/Indent1.lua
--          (find-angg "LUA/Indent1.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
--
-- This file is obsolete!
-- It was superseded by:
--   (find-angg "LUA/Indent2.lua")
--
--
-- My first implementation of "indent" only interpreted these kinds of
-- "indentation instructions":
--
--   "<+2>"     increase the indentation by 2
--   "<-3>"     decrease the indentation by 3
--   "\n"       is converted to something like "\n ", where the number
--              of spaces is given by the current indentation
--
-- then I added support for:
--
--   "<!>"      delete all whitespace - i.e., "[ \t\n]*" on both
--              sides of the "<!>"
--
-- and then I saw that 



-- This file implements a way to convert strings with "indentation
-- instructions" into, aham, "normal strings". The indentation
-- instructions are of these kinds:
--   "<+2>"     increase the indentation by 2
--   "<-3>"     decrease the indentation by 3
--   "\n"       is converted to something like "\n ", where the number
--              of spaces is given by the current indentation
--   "<!>"      delete all whitespace - i.e., "[ \t\n]*" on both
--              sides of the "<!>"
--   "<!L>"     capture the whitespace on both sides of the "<!L>"
--              and then return the whitespace that was at the left
--   "<!R>"     same, but at the right
--   "<!LR>"    same, but returns the left and right whitespaces
--              concatenated
--
--
-- (defun e1 () (interactive) (find-angg "LUA/ELpeg1.lua"))
-- (defun i1 () (interactive) (find-angg "LUA/Indent1.lua"))
-- (defun p1 () (interactive) (find-angg "LUA/Pict2e1.lua"))

-- «.flatten»		(to "flatten")
-- «.flatten-tests»	(to "flatten-tests")
-- «.nlify»		(to "nlify")
-- «.Ind»		(to "Ind")
-- «.Ind-tests»		(to "Ind-tests")
-- «.nlify-tests»	(to "nlify-tests")
-- «.pat_indent»	(to "pat_indent")
-- «.pat_indent-tests»	(to "pat_indent-tests")
-- «.Indent»		(to "Indent")
--   «.string.indent»	(to "string.indent")
-- «.Indent-tests»	(to "Indent-tests")

require "ELpeg1"  -- (find-angg "LUA/ELpeg1.lua" "Gram")
gr,V,VA,VE,PE = Gram.new()
_ = S(" ")^0


-- «flatten»  (to ".flatten")
flatten = function (o)
    local out = VTable {}
    local add   -- recursive, defined in the next line
    add = function (x)
        if type(x) == "table"
        then map(add, x)
        else table.insert(out, x)
        end
      end
    add(o)
    return out
  end

concattables = function (A, ...)
    A = HTable(copy(A))
    for _,B in ipairs({...}) do
      for _,v in ipairs(B) do
        table.insert(A, v)
      end
    end
    return A
  end

-- «flatten-tests»  (to ".flatten-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Indent1.lua"
= flatten {"a", 22, {33, 44, {print}, 55}}
= concattables({10, 20}, {30, 40}, {}, {50, 60})

--]]



--        _ _  __       
--  _ __ | (_)/ _|_   _ 
-- | '_ \| | | |_| | | |
-- | | | | | |  _| |_| |
-- |_| |_|_|_|_|  \__, |
--                |___/ 
--
-- Nlify splits a string and converts each "\n" in it to a {"nl"}.
-- For example,
--   nlify("a\nbc\n\nd")
-- returns:
--   {"a", {"nl"}, "bc", {"nl"}, {"nl"}, "d"}
--
-- «nlify»  (to ".nlify")

V.nl      = P"\n" / function () return {"nl"} end
V.nonnls  = Cs((1-P"\n")^1)
V.nlify   = Ct((V.nl + V.nonnls)^0)
pat_nlify = gr:compile("nlify")
nlify     = function (str) return pat_nlify:match(str) end

-- «nlify-tests»  (to ".nlify-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Indent1.lua"
PP(nlify("ab\n\ncd"))
PP(nlify("abcd"))
PP(nlify(""))

gr,V,VA,VE,PE = Gram.new()
V._ = Cs(P"_"^1)
V.m = Cs(S"abcd\n"^1) / nlify
V.all = V._ * V.m * V._
gr:cmp("all", "__ab\n\ncd__")

--]]





--  ___           _ 
-- |_ _|_ __   __| |
--  | || '_ \ / _` |
--  | || | | | (_| |
-- |___|_| |_|\__,_|
--                  
-- This is used by one of the patterns in pat_indent.
-- (find-es "lpeg" "lpeg-matchtime")
--
-- «Ind»  (to ".Ind")

Ind = Class {
  type = "Ind",
  from     = function (_) return Ind{_=_, out={}} end,
  captures = function (_) return Ind.from(_):captures() end,
--captures = function (_) PP(_); return PP(Ind.from(_):captures()) end,
  mtcaptures = function (s,p,_) return p,Ind.captures(_) end,
  test     = function (_) PP(Ind.captures(_)) end,
  __tostring = function (i) return mytostring(i) end,
  __index = {
    sign   = function (i) return (i._.sign == "-") and -1 or 1 end,
    uint   = function (i) return tonumber(i._.digits) end,
    int    = function (i) return i:uint() and i:sign()*i:uint() end,
    indent = function (i) return i:int() and i:int()~=0 end,
    itable = function (i) return i:indent() and {{"indent", i:int()}} or {} end,
    --
    l      = function (i) return i._.l or "" end,
    r      = function (i) return i._.r or "" end,
    LR     = function (i) return i._.LR or "" end,
    keepl  = function (i) return i:LR()=="L" or i:LR()=="LR" end,
    keepr  = function (i) return i:LR()=="R" or i:LR()=="LR" end,
    ltable = function (i) return i:keepl() and nlify(i:l()) or {} end,
    rtable = function (i) return i:keepr() and nlify(i:r()) or {} end,
    --
    captures = function (i)
        return unpack(concattables(i:ltable(), i:itable(), i:rtable()))
      end,
  },
}

-- «Ind-tests»  (to ".Ind-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Indent1.lua"
Ind.test {sign="-", digits="42"}
Ind.test {sign="-", digits="42", l=" ", r=" ", LR="LR"}
Ind.test {sign="-", digits="42", l=" \n ", r=" \n\n ", LR="LR"}
Ind.test {sign="-", digits="42", l=" \n ", r=" \n\n ", LR="L"}
Ind.test {sign="-", digits="42", l=" \n ", r=" \n\n ", LR="R"}
Ind.test {                       l=" \n ", r=" \n\n ", LR="R"}
PP(Ind.captures {sign="-", digits="42"})

gr,V,VA,VE,PE = Gram.new()
V._ = Cs(P"_"^1)
V.m  = P"a" * Cc {sign="-", digits="42", l=" \n ", r=" \n\n ", LR="LR"}
V.mc = P"a" * Cc {sign="-", digits="42", l=" \n ", r=" \n\n ", LR="LR"}
V.m  = V.mc / Ind.captures
V.all = V._ * V.m * V._
gr:cmp("all", "__a__")

--]]



--              _       _           _            _   
--  _ __   __ _| |_    (_)_ __   __| | ___ _ __ | |_ 
-- | '_ \ / _` | __|   | | '_ \ / _` |/ _ \ '_ \| __|
-- | |_) | (_| | |_    | | | | | (_| |  __/ | | | |_ 
-- | .__/ \__,_|\__|___|_|_| |_|\__,_|\___|_| |_|\__|
-- |_|            |_____|                            
--
-- This block defines the lpeg pattern used by the Indent class.
-- For example, that pattern "converts"
--   "<+23>a\n<-4><!>\nb"
-- to:
--   {1="indent", 2=23} "a" {1="nl"} {1="indent", 2=-4} "" "b"
--
-- These substrings or the original string are treated specially:
--   "<+23>"   increase the indentation by 23
--   "<-4>"    decrease the indentation by 4
--   "\n"      print a newline plus the current indentation
--   "<!>\n"   <!> discards all the newlines immediately after it
-- At this moment the delimiters "<" and ">" are hardcoded.
--
-- See the classes Indent:
--   (to "Indent")
--
-- «pat_indent»  (to ".pat_indent")

V.o    = P"<"
V.c    = P">"

Cin      = function (tag, pat) return Cs(pat):Cg(tag) end
V.sign   = Cin("sign",   S"+-")
V.n      = Cin("digits", R"09"^1)
V.l      = Cin("l",      S" \t\n"^0)
V.r      = Cin("r",      S" \t\n"^0)
V.LR     = Cin("LR",     P"LR" + P"L" + P"R" + P"")
V.bang0  = (V.sign*V.n + P"!") * V.LR
V.bangt  = Ct(V.l*V.o* V.bang0 *V.c*V.r)
V.bang   = V.bangt / Ind.captures

V.spaces = Cs(S" \t"^1)
V.nl     = P"\n" / function () return {"nl"} end

V.nonnls = Cs((1-P"\n")^1)

V.special   = V.bang + V.spaces + V.nl
V.text      = Cs((-V.special * P(1))^1)
V.all       = (V.text + V.special)^0
V.tall      = Ct(V.all)
pat_indent0 = gr:compile("all")    -- returns several values
pat_indent  = gr:compile("tall")   -- returns a table

-- «pat_indent-tests»  (to ".pat_indent-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Indent1.lua"
    gr:cmp("bangt",  "<+23>")
o = gr:cm0("bangt",  "<+23>")
PP(o)
PP(Ind.captures(o))

    gr:cmp("bang",   "<+23>")
    gr:cmp( "all",   "<+23>a\n<-4>\nb")
    gr:cmp( "all",   "<+23>a\nb\n<-4>\nc")
    gr:cmp("tall",   "<+23>a\nb\n<-4L>\nc")
    gr:cmp("tall",   "<+23>a\nb\n<-4R>\nc")
    gr:cmp("tall",   "<+23>a\n<-4LR>\nb")
    gr:cmp("tall",   "<+23>a\n<-4><!>b")
    gr:cmp("tall",   "<+23>a\n<-4><!>\nb")
    gr:cmp("tall",   "<+23>a\n<-4> <!>  b")
    gr:cmp("tall",   "<+23>a\n<-4> <!L>  b")
    gr:cmp("tall",   "<+23>a\n<-4> <!R>  b")
    gr:cmp("tall",   "<+23>a\n<-4> <!LR>  b")
PP(pat_indent0:match "<+23>a\n<-4><!>\nb")
PP(pat_indent :match "<+23>a\n<-4><!>\nb")

--]]

--  ___           _            _   
-- |_ _|_ __   __| | ___ _ __ | |_ 
--  | || '_ \ / _` |/ _ \ '_ \| __|
--  | || | | | (_| |  __/ | | | |_ 
-- |___|_| |_|\__,_|\___|_| |_|\__|
--                                 
-- The class Indent implements the machinery to convert a string like:
--   "foo\nbar<+1>\nplic\nploc<-1>\nbof"
-- to:
--   foo
--   bar
--    plic
--    ploc
--   bof
-- and it lets us test each step of the conversion separately.
--
-- «Indent»  (to ".Indent")

Indent = Class {
  type = "Indent",
  new  = function (bigstr) return Indent {bigstr = bigstr} end,
  indent = function (bigstr, ind)
      return Indent.new(bigstr):parse():map(ind or 0):concat()
    end,
  flatten = function (o)
      return Indent.indent(table.concat(flatten(o), "\n"))
    end,
  __index = {
    parse = function (ind)
        ind.A = VTable(pat_indent:match(ind.bigstr))
        return ind
      end,
    map = function (ind, r)
        ind.B = VTable {}
        for _,o in ipairs(ind.A) do
          if type(o) == "string"  then table.insert(ind.B, o)
          elseif o[1] == "nl"     then table.insert(ind.B, "\n"..(" "):rep(r))
          elseif o[1] == "indent" then r = r + o[2]
          else error()
          end
        end
        return ind
      end,
    concat = function (ind) return table.concat(ind.B) end,
  },
}

-- «string.indent»  (to ".string.indent")
string.indent = Indent.indent   -- works only on strings
indent        = Indent.flatten  -- works on strings and tables

-- «Indent-tests»  (to ".Indent-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Indent1.lua"
bigstr = "foo\nbar<+1>\nplic\nploc<-1>\nbof"
o = {"foo", {"bar<+1>", "plic", "ploc<-1>"}, "bof"}
= indent(bigstr)
= indent(o)
= bigstr:indent()
= bigstr:indent(4)

i = Indent.new(bigstr)
= i:parse().A
= i:parse():map(0).B
= i:parse():map(0):concat()
=               bigstr
= Indent.indent(bigstr)
= bigstr:indent()

= indent "foo\nbar<+1>\nplic\nploc<-1>\nbof"
= indent "foo\nbar<+1LR>\nplic\nploc<-1>\nbof"
= indent "foo\nbar<+1LR>\nplic\nploc<-1LR>\nbof"

--]]


-- «Ind-tests»  (to ".Ind-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Indent1.lua"

a = Ind {
  "% Grid",
  "% Horizontal lines:",
  "\\Line(-1.2,0)(3.2,0)",
  "\\Line(-1.2,1)(3.2,1)",
  "% Vertical lines:",
  "\\Line(0,-2.2)(0,5.2)",
  "\\Line(1,-2.2)(1,5.2)",
  }
b = Ind { "\\linethickness{0.5pt}", a }
c = Ind { "\\color{gray}", b }
d = Ind { "{<+1><!>", c, "}<-1>" }

= d:tostring0()
= d:tostring()
= d:flatten()
= d:flatten():indent()

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Indent1.lua"

a = { "%hlines", "%vlines" }
b = { "thick", a }
c = { "gray", b }
d = { "{<+1><!>", c, "}<-1>" }
= d
PP(d)
= Ind.flatten(d)
= Ind.flatten(d, "\n")
= Ind.flatten(d, "\n", 0)
= Ind.tostring (d)
= Ind.tostring0(d)

e = Ind.fromcopy(d)
PP(e)
= e
= e[2]
= e[2][2]

--]]




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