#!/bin/lua
--[[
# encoding: utf-8
#
# usage: ptt [-V] foo.tt
# -V: version
#
# ptt: coded by Kenji Arisawa
# version 4.1a
# date: 2015/07/02
# Email: arisawa@aichi-u.ac.jp
#
# 2015/02/17 V option for ptt
# 2015/02/17 e option in I tag
# 2015/01/21 n option in H1 tag
--]]
version="4.1" -- version No. used in comment of generated text
gen=require("gen")
argopt=gen.argopt
sub=string.sub
find=string.find
gsub=string.gsub
match=string.match
gmatch=string.gmatch
format=string.format
concat=table.concat
int=tonumber
dbg=print
push=table.insert
str=require("str")
trim=str.trim
xfind=str.xfind
rfind=str.rfind
split=str.split
xsplit=str.xsplit
trans=str.trans
-- constant values to make codes easy to read
-- don't change
dquote='"' -- double quote
tab="\t"
quof=false
-- our preference
encoding="utf-8" -- encoding in tt text
lang="ja" -- default language; japanese mode; not "jp"!
-- initial values
hdr={} -- HTML header
rdic={} -- unused
udic={[" & "]='&', [" < "]='<', [" > "]='>'} -- user's dictionary
ldic={} -- label dictionary
label=nil
ritmode=false
toc={n=0} -- table of contents
seccnt={0,0,0} -- section counter initial value
h1p=false -- H1 is printed
snum=false -- put section number
nline=0 -- next line to be read
dline=1 -- debug point. last block command
dd=nil -- controls DD+: part
-- end of initial values
-- start of some generic functions
--
function printf(fmt,...)
io.write(format(fmt,...))
end
function pline(fmt,...)
if dd == nil then
io.write(format(fmt.."\n",...))
else
dd = dd .. format(fmt,...) -- ???
end
end
function errf(fmt,...) -- error with format
io.stderr:write(format(fmt,...))
end
function merge(...) -- merge tables
t={}
u={...}
for n=1,#u do
for k,v in pairs(u[n]) do
t[k] = v
end
end
return t
end
-- end generic functions
-- start ptt5 special functions
--
function rquote(s) -- process single and double quote
-- replace double quote to "“" and "”" pair
s=gsub(s," \"([^\"]*)\""," “%1”")
-- replace single quote to "‘" and "’" pair
s=gsub(s," '([^\']*)'"," ‘%1’")
return s
end
function gline()
-- global nline
nline=nline+1
return lines[nline]
end
function ugline()
-- global nline
if nline>1 then
nline=nline-1
end
end
function secnum(s,n)
-- n is 2,3,4
-- translate id number to section number
-- the id number is "1.2.0" for example
-- to be translated "1.2."
a = "%.0%.0"
b = sub(a,3*n-5);
return gsub(s,"^([0-9.]+)"..b.."(.*)","%1. %2")
end
function subline(s)
-- global nline
lines[nline] = s
end
function logo()
---- change as you like ----
pline('')
pline('
')
---- end of change ------
end
function head(title)
local bo
if ritmode == true then
pline(hdr[1])
table.remove(hdr,1)
end
pline('')
pline('
') line=gline() while line ~= term do -- dbg(line) pline("%s", t2h(line)) line=gline() end pline("") end function ul(s) -- unordered list pline("
%s %s
%s
',repl(s)) end function bcent(s) -- center block o,term,a = opt(s) if a then block(term,format('\
| \n' ,im[3],fn,fn)
end
pline("
%s | ' , im[1]) end pline("
%s",repl(s)) end function bquote(s) -- block quote o,t,a=opt(s) if a then block(t,"
","") else block(t,"
","") end end function raw(s) pline(s) end function braw(term) line=gline() while line ~= term do pline("%s", line) line=gline() end end function tablth(line,cl,sep) local args,tr,hs,v,t,n,n1,n2,n3 --dbg("### tablth:",line) --dbg("## tablth:",sep) args=split(line,sep) -- header -- for k,v in pairs(args) do dbg("## tablth",k,v) end --[[ # format of table header: # name:<[width] # left align # name:>[width] # right align # name:[width] # center align # name:-[width] # hidden # name # center align # where [ ] is a meta symbol that denotes option # width must be in unit of pixel --]] tr={["<"]="left",[">"]="right",[""]="center",["-"]="hidden"} hs={} --dbg("## tablth",#args) for n=1,#args do v={} -- default v.align="center" -- alignment v.width="0" -- width, string t=trim(args[n]) v.name=t n1,n2,n3=match(t,"(.*):([<>-]?)([%d]*)") --dbg("## tablth",n,n1,n2,n3) if n1 then v.name=n1 v.align=tr[n2] v.width=n3 end hs[n]=v end -- count emty names and put them to colspan -- "hidden" must be skipped m=0 for n=1,#args do --dbg(n,hs[n].name,hs[n].align) if hs[n].name ~= "" then m = 1 break end end if m > 0 then pline("
%s', t2h(s)) line=gline() while line and sub(line,1,1)==tab do pline("%s", t2h(sub(line,2))) line=gline() end pline("") pline('
{
" and ...
-- which cannot be translated to
-- use “{
” and ...
function repl0(s)
if s == nil then
return s
end
s=trans(s,udic)
s = urll(s)
if quof then
s = rquote(s)
end
return s
end
function repl(s)
local t,s0,s1,u
u = ""
s0,t,s1 = match(s,"^([^<]*)(<[^ ][^>]*>)(.*)")
while s0 ~= nil do
s0 = repl0(s0)
u = u..s0..t
s = s1
s0,t,s1 = match(s,"^([^<]*)(<[^ ][^>]*>)(.*)")
end
return u..repl0(s)
end
--]=]
function t2h(s) -- text to html
-- order is matter !
local t={["&"]="&", ["<"]="<", [">"]=">"}
s=gsub(s,"[&<>]",t)
return s
end
function rit(line) -- rit block
while sub(line,-2) ~= "}$" do
pline("%s", line)
line=gline()
end
pline("%s", line)
end
function pmatch(line,dic)
-- dic: pattern dic
-- return: key,func,opt
if dic == nil then
return nil,nil,nil
end
for k,v in pairs(dic) do
if sub(line,1,#k) == k then
return k,v,sub(line,#k+1)
end
end
end
function bline(line,pdic,opt)
-- opt: for what?
local m
while not eol(line) do
pline("%s")
break
end
if ritmode==true and sub(line,1,2)=="${" then
rit(line)
break
end
line = gsub(line,refp,function (m)
return ldic[m]
end
)
subline(line)
m,f,o = pmatch(line,pat)
if m then
--dbg("# DEBUG:", line)
--dbg("# DEBUG:m:",m)
if f==obsolete then
f(line)
else
f(trim(o))
end
break
end
m = match(line,spec)
--dbg("DBG1:",m,line)
if m and spectagtab[m] then
--dbg("DBG2:",line)
pline("%s",line)
t = format("%s>",m)
braw(t) -- block raw output until t. t is the terminator
pline(t)
break
end
if match(line,uncook) then
if sub(line,-2) ~= "}" then
pline("%s",line)
end
break
end
if match(line,obrace) then
--dbg("DBG3:",line)
pline("%s",line)
line=gline()
while not match(line,cbrace) do
pline("%s",line)
line=gline()
end
pline("%s",line)
break
end
pline("%s ')
end
function mkdic(pat)
local pdic={}
for k,v in pairs(pat) do
pdic[k] = v
end
return pdic
end
-- pat0 is used elsewhere
-- pat1 is allowed in some block commands
pat1= { -- RE pattern
["- "]=item, -- special
["HR:"]=obsolete, -- hr,
["UL:"]=ul,
["OL:"]=ol,
["DL:"]=dl,
[tab]=pre, -- special
["I:"]=img,
["II:"]=bimg,
["C:"]=cent,
["CC:"]=bcent,
["#:"]=com,
["##:"]=bcom,
["N:"]=note,
["NN:"]=bnote,
["Q:"]=quote,
["QQ:"]=bquote,
["P:"]=code,
["Ta:"]=tabl,
["!:"]= raw,
["!!:"]=braw,
["-a:"]=obsolete, --actm,
["+a:"]=obsolete, --actp,
["A:"]=obsolete,
["AI:"]=obsolete,
["AI:"]=obsolete,
["B:"]=obsolete,
["T:"]=obsolete,
["R:"]=obsolete
}
-- outermost level tags
pat2= {
-- H1: is special
["H4:"]=h4,
["H3:"]=h3,
["H2:"]=h2,
["D:"]=define,
["DD:"]=bsdef,
["DD+:"]=bpdef,
}
-- pat3 is used for making toc.
-- then H1:,.. in comments etc must be skipped
pat3={
["!!:"]=bcom,
["P:"]=bcom,
["##:"]=bcom,
["NN:"]=bcom,
["QQ:"]=bcom,
["II:"]=bcom,
["CC:"]=bcom,
["DD:"]=bcom1,
["DD+:"]=bcom1,
}
pat4={ -- header level tags, they may be before "H1:"
["In:"]=inc, -- include
["KW:"]=kw,
["D:"]=define,
["DD:"]=bsdef,
["DD+:"]=bsdef,
}
pat0=merge(pat1,pat2) -- keep this order
-- we define some tags in ptt
ptth="^(\t|- |[^ ]:|[^ ][^ ][+]?:) *(.*[^ ]|) *$"
spectags="script|style|iframe|textarea|form|pre|select|table|ul|ol|dl"
t=split(spectags,"|")
spectagtab={}
for k,v in pairs(t) do
spectagtab[v]=true
end
spec="^<(%w+)[^<>]*>%s*$" -- special tags
obrace="^<[^>]*$" -- open brace
cbrace="^.*>.*$" -- close brace
uncook="^<.*>$" -- don't cook the line "< .... >"
inbrac=".*<[^>]*$" -- in < >
labp="
",repl(line))
break
end
line=gline()
--dbg("DBG4:",line)
end
end
function init(bob) -- make toc, label
-- global toc, label
local cnt,line,m,s,sid,key,r,k
-- we must read whole data below "H1:" to construct toc etc
--
nline=bob
line=gline()
while line do
--dbg(line)
-- we must skip pat3
k,m=pmatch(line,pat3)
if m then -- enter pat3
m(sub(line,#k+1))
else -- out of pat3
--dbg(line)
m,f,o = pmatch(line,pat2)
if m then
if match(m,"D:") then
-- o is "{ " for example
f(o)
end
if match(m,"H[234]:") then
sid=inc_seccnt(m) -- something like "1.1.2"
toc[sid]=trim(sub(line,4))
toc.n = toc.n + 1
label=sid -- ??? this result is not used
end
end
line = gsub(line,labp,function (m)
-- Let
")
t={}
for k,v in pairs(toc) do
push(t,k)
end
table.sort(t,compf)
for k,s in ipairs(t) do -- s is something like "1.1.2"
if match(s,"^%d+%.0+%.0+$") then
pline('
")
pline('