#!/bin/lua
--[[
# encoding: utf-8
#
#	usage: ptt [-h] foo.tt
#
#	ptt: coded by Kenji Arisawa
#	version 4.0
#	date: 2014/09/18
#	Email: arisawa@aichi-u.ac.jp
#
--]]
version="4.0" -- version No. used in comment of generated text
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
h1opt={l=true,t=true} -- ptt option
--	l=true	-- logo print
--	t=true	-- toc print
--	e=false	-- english
--	q=false	-- quote

-- our preference
encoding="utf-8"   -- encoding in tt text
lang="ja" -- default language; japanese mode; not "jp"!

-- initial values
hdr={}		-- HTML header
rdic={} 
udic={[" & "]='&amp;', [" < "]='&lt;', [" > "]='&gt;'} -- 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

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 sysfatal(s)
	errf("error: %s\n",s)
	os.exit()
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) -- replace double quote to "&ldquo;" and "&rdquo;" pair
	-- s=gsub(s,"(.*) \"([^\"]*)\"","%1 &ldquo;%2&rdquo;")
	s=gsub(s," \"([^\"]*)\""," &ldquo;%1&rdquo;")
	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 subline(s)
	-- global nline
    lines[nline] = s
end

function logo()
	---- change as you like ---- 
	pline('<img src="/image/logo.png" alt="Logo" style="float:right;margin-right:0">')
	pline('<img src="/image/addr.png" alt="address" style="float:right;margin-left:0">')
	---- end of change ------
end

function head(title)
	local bo
	if ritmode == true then
		pline(hdr[1])
		table.remove(hdr,1)
	end
	pline('<!DOCTYPE html>')
	pline('<html lang="%s"><head>',lang)
	pline("<!-- automatically generated by Ptt %s -->\n",version)
	pline('<meta charset="%s">',encoding)
	pline("<title>%s</title>",title)
	-- additional header
	for i,v in ipairs(hdr) do
		m,f,o = pmatch(v,pat4)
		if m then
			f(trim(o))
		else
			pline("%s",v)
		end
	end
	pline("</head>")
	--errf(#hdr,lines[#hdr])
	if bopt then
		pline("<body %s>",bopt)
	else
		pline("<body>")
	end
end

function inc_seccnt(h)
	-- increase section counter seccnt, h is one of "H2:","H3:","H4:"
	-- global seccnt
	local n,k
	n=int(sub(h,2,2))
	if n<2 then
		return nil
	end
	n=n-1
	seccnt[n]= seccnt[n]+1
	for k=n+1,3 do
		seccnt[k]=0
	end
	return format("%d.%d.%d",seccnt[1],seccnt[2],seccnt[3])
end

function opt(s)
	-- s is : [opt] [term] [class]
	local v,o,u,a,t,c
	a={}
	c=nil
	o=nil
	t=nil
	o,u=match(s,"^-([^%s]+)%s*(.*)$")
	--dbg("## opt:",s,o,t,c)
	s=u or s
	--dbg("## opt:",s)
	t,c=match(s,"^([^%s]+)%s*(.*)$")
	--dbg("## opt:",s,o,t,c)
	if c=="" then
		c=nil
	end
	--dbg("## opt:",s,o,t,c)
	return o,t,c
end
--[[
dbg(opt(""))
dbg(opt("-abc"))
dbg(opt("!!"))
dbg(opt('class="solid"'))
dbg(opt("-abc !!"))
dbg(opt('-abc class="solid"'))
dbg(opt('!! class="solid"'))
dbg(opt('-abc !! class="solid"'))
--]]



function h1(s)
	-- s should be trimmed
	-- global seccnt, h1p, quof, lang
	local lp,tp,v,title,n,k
	local op={} -- options in H1
	--dbg(s)
	-- H1: [-xx] title
	-- options:
	--		e: english
	--		l: not show lego
	--		q: quote
	--		t: not print table of contents
	if sub(s,1,1)=="-" then
		m1,m2=match(s,"^-([lteq]+)%s*(.-)$")
		for n=1,#m1 do
			op[sub(m1,n,n)]=true
		end
		title=m2
	else
		title=s
	end
	if op.e then
		lang = "en"
	end

	--dbg(title)
	head(title)
	pline('<header>');
	if not op.l then
		logo()
	end
	if op.q then
		quof = true
	end
	pline('<h1>%s</h1>',title)
	t="目次"
	if op.e then
		t="Contents"
	end
	if not op.t then
		ptoc(t)
	end
	pline('</header>');
	seccnt={0,0,0}
	h1p=true
end

function h2(s)
	local sid
	sid=inc_seccnt("H2:")
	pline('<h2 id="id.%s">%s</h2>',sid,repl(s))
end

function h3(s)
	sid=inc_seccnt("H3:")
	pline('<h3 id="id.%s">%s</h3>',sid,repl(s))
end

function h4(s)
	sid=inc_seccnt("H4:")
	pline('<h4 id="id.%s">%s</h4>',sid,repl(s))
end

function hr(s)
	pline('<hr %s>',s)
end

function code(s) -- code block
	-- P: !
	local o,term,a,t,line
	o,term,a=opt(s)
	printf('<pre class="code">')
	line=gline()
	while line ~= term do
		-- print(line)
		pline("%s", t2h(line))
		line=gline()
	end
	pline("</pre>")
end

function ul(s) -- unordered list
	pline("<ul>")
	list(s)
	pline("</ul>")
end

function ol(s) -- ordered list
	pline( "<ol>")
	list(s)
	pline( "</ol>")
end

function dl(s) -- definition list
	pline( "<dl>")
	deflist()
	pline( "</dl>")
end

function eol(s) -- end of list
	if s==nil or s=="" or s=="-" then
		return true
	end
	return false
end

--[[
# rditem reads data of the following syntax
# - item
# lines
# ...
--]]

function rditem()
	local m,line
	local t={}
	line=gline()
	--dbg(line)
	-- this line should begin with "- ", simple "-" is end of list
	if line==nil then
		return nil
	end
	m=match(line,"^- %s*(.*)$")
	if m==nil then
		--ugline()
		return nil
	end
	push(t,m)
	line = gline()
	while line~=nil and line ~= "" and sub(line,1,1) ~= "-" do
		push(t,trim(line))
		line = gline()
	end
	ugline()
	return t
end

function list(o) -- o is option
	local line,s,t
	--dbg("### list o:",o)
	if o ~= "" then -- o is "-a" or "+a"
		actlist(o)
		return
	end
	t=rditem()
	while t do
		printf("<li>")
		for n,k in ipairs(t) do
			pline("%s<br>",repl(k))
		end
		t=rditem()
	end
end

function actlist(o) -- action list
	local s,h,t
	t=rditem()
	while t do
		h,s=match(t[1],"^([^%s]+)%s+(.*)$")
		s=repl(s)
		if o=="-a" then
			r=format('<a href="%s">%s</a>',h,s)
		else -- +a
			r=format('<a href="%s">%s</a> %s',h,h,s)
		end
		pline("<li>%s",r)
		for n=2,#t do
			pline("%s<br>",repl(t[n]))
		end
		t=rditem()
	end
end

function deflist() -- definition list
	local line,d,t
	t=rditem()
	while t do
		pline("<dt>%s\n<dd>",repl(t[1]))
		for n=2,#t do
			pline("%s<br>",t[n])
		end
		t=rditem()
	end
end

function actm(s) -- -a:
	local v
	v=split(s)
	if len(v)==0 then
		sysfatal('no term in "-a:"')
	end
	h=v[0]  -- the first field in s
	t=join(sub(v,2))
	t=repl(t)
	pline( '<p><a href="%s">%s</a><br></p>' , h,t)
end

function actp(s) -- +a:
	local v
	v=split(s)
	if #v==0 then
		sysfatal('# no term in "-a:"')
	end
	h=v[1]  -- the first field in s
	t=concat(v," ",2)
	t=repl(t)
	pline('<p><a href="%s">%s</a> %s<br></p>' , h,h,t)
end

function item(s)
	ugline()
	ul("")
end

function cent(s)
	pline('<p style="text-align:center">%s</p>',repl(s))
end

function bcent(s) -- center block
	o,term,a = opt(s)
	if a then
		block(term,format('<div style="text-align:center" %s>',a),'</div>')
	else
		block(term,'<div style="text-align:center">','</div>')	
	end
end

function kw(s)
	pline('<meta name="keywords" content="%s">',s)
end

function inc(f) -- include, f is file name or path, already trimmed
	-- include style sheet, javascript
	local b	-- base
	local e -- extention
	b,e=match(f,"^(.+)%.([^.]+)$")
	if b==nil then
		sysfatal("no file name after 'In:'") 
	end
	if e=="css" then
		pline('<link rel="stylesheet" type="text/css" href="'..f..'">')
		return
	end
	if e=="js" then
		pline('<script type="text/javascript" src="'..f..'"></script>')
		return
	end
end

function img(s) -- image inclusion,
	-- I: foo.jpg [options]
	-- we asume, s is trimmed
	-- file [alt [size]]  # ptt3
	-- file [size [alt]]
	--    size is 640 or 66% for example
	local f	-- file
	local r -- rest, alt="...", style="..." etc
	f,r=match(s,"^([^%s]+)(.*)$")
	if f==nil then
		sysfatal("no file name after 'I:'") 
	end
	pline("<figure>")
	-- svg file
	if match(f,"^[^.]+%.svg") then
		if r=="" then
			pline('<object type="image/svg+xml" data="%s"></object>', f)
		else
			pline('<object type="image/svg+xml" data="%s" %s ></object>', f, r)
		end
	else
		if r=="" then
			pline('<img src="%s" />', f)
		else
			pline('<img src="%s" %s />', f,r)
		end
	end
	line = gline()
	if line and sub(line,1,2) == "C:" then
		pline("<figcaption>")
		print(sub(line,3))
		pline("</figcaption>")
		pline("</figure>")
	else
		pline("</figure>")
		ugline()
	end
end

function bimg(s) -- image array
	local o,term,a,line,cap
	o,term,a=opt(s)
	line=gline()
	cap=nil
	if sub(line,1,3)=="Ca:" then
		cap = '<caption align="bottom">'..sub(line,3)..'</caption>\n'
		line=gline()
	end
	imglist={}
	while line ~= term do
		v=xsplit(line)
		w=nil
		if #v>2 then
			w=concat(v," ",3)
		end
		--dbg(v[1],v[2],w)
		push(imglist,{v[1],v[2],w})
		line=gline()
	end
	n=#imglist
	if a then
		pline('<table style="border:0" %s>',a)
	else
		if n == 1 then
			pline('<table style="border:0" width="60%%">')
		else
			pline('<table style="border:0" width="90%%">')
		end
	end
	if cap then
		pline("%s",cap)
	end
	w=format('width:%d%%',(100/n))
	for key,im in pairs(imglist) do
		--dbg(im[1],im[2],im[3])
		if im[3]==nil then im[3]=w end
	end
	pline("<tr>\n")
	for key,im in pairs(imglist) do -- im[1]: name, im[2]:file, im[3]:option
		fn=im[2]
		n=rfind(fn,".",true)
		bn=sub(fn,1,n-1)
		pline('<td style="border:0;vertical-align:bottom;%s">\
		<img src="%s" alt="%s" style="width:100%%">\
		</td>\n' ,im[3],fn,fn)
	end
	pline("</tr><tr>")
	for key,im in pairs(imglist) do
		pline('<td style="border:0">%s</td>' , im[1])
	end
	pline("</tr></table>")
end

function com(s) -- comment
	-- pass
end

function bcom(s) -- block comment
	-- global dline
	dline=nline-1
	o,term,a=opt(s)
	line=gline()
	while line ~= term do
		line=gline()
	end
end


function bcom1(s) -- skip for DD:, DD+:
	-- global line,dline
	local d,term
	dline=nline-1
	d = split(s)
	term = d[2]
	line=gline()
	while line ~= term do
		-- dbg("bcom1:",line)
		line=gline()
	end
end


function block(term,b,e)
	local line
	pline("%s", b)
	parse(term,pat1)
	pline("%s<br>\n", e)
end

function note(s) -- note
	-- <note>...</note> isn't defined in HTML4.0
	-- but I want to use because this is a note
	pline('<div class="note">%s</div>',repl(s))
end

function bnote(s) -- block note
	o,term,a=opt(s)
	if a then
		block(term,format('<div %s>\n',a),'</div>')
	else
		block(term,'<div class="note">\n','</div>')
	end
end

function quote(s)
	pline("<blockquote>%s</blockquote>",repl(s))
end

function bquote(s) -- block quote
	o,t,a=opt(s)
	if a then
		block(t,"<blockquote "..a..">\n","</blockquote>")
	else
		block(t,"<blockquote>\n","</blockquote>")
	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("<tr>")
		for n=1,#args do
			--dbg(n,hs[n].name,hs[n].align)
			if hs[n].align ~= "hidden" then
				pline('<th%s>%s</th>',cl,hs[n].name)
			end
		end
		pline("</tr>")
	end
	return hs,v
end


function tabltd(cl,sep,hs,v)
	local args,r
	line=gline()
	--dbg("### tabltd:",line)
	while not eol(line) do
		--dbg("### tabltd",sep,line)
		args=split(line,sep)
		--dbg("### args:",#args,args[1],args[2],args[3],args[4])
		pline("<tr>")
		r=repl(args[1])
		if hs[1].align ~= "hidden" then
			pline('<th%s width="%s" style="text-align:%s">%s</th>',
				cl,hs[1].width,hs[1].align,r)
		end
		for n=2,#args do
			--dbg(args[n],hs[n].width,hs[n].align)
			if hs[n].align ~= "hidden" then
				r=repl(args[n])
				pline('<td%s width="%s" style="text-align:%s">%s</td>',
					cl,hs[n].width,hs[n].align,r)
			end
		end
		pline("</tr>")
		line=gline()	
	end
end

function tabl(s)
	-- format of Ta tag
	--	Ta: [-x] [sep] [class]
    --  Ta: | class=noborder	# example
	-- 	
	local m1,m2,cl,args,hs,n,sep,v,o
	--dbg("## tabl",s)
	o,sep,cl=opt(s)
	if cl==nil then
		cl="class=solid"
	end
    cl=" "..cl
	pline("<table%s>",cl)
	line=gline()
	if sub(line,1,3)=="Ca:" then
		pline('<caption align="bottom">%s</caption>', sub(line,4))
		line=gline()
	end
	pline("<tbody>")
	hs,v=tablth(line,cl,sep)
	tabltd(cl,sep,hs,v)
	pline("</tbody></table>")
end


function define(s) -- simple definition
	local a,b
	a,b=match(s,"([^%s]+)%s+(.*)")
	--dbg(s,a,b)
	if a==nil then
		a=match(s,"([^%s]+)")
		udic[a] = nil
	else
		udic[a] = b
	end
end

function bpdef(s) -- block plus defintion
	-- DD+: key term
	local a
	a=split(s)
	if #a==1 then
		udic[a[1]] = nil
		return
	end
	term=a[2]
	dd=""
	parse(term,pdic1)
	udic[a[1]]=dd
	dd=nil
end

function bsdef(s) -- block simple defintion
	-- DD: key term
	local k,t,d
	k,t = match(s,"^([^%s]+)%s*(.*)$")
	d=''
	line=gline()
	while line ~= t do
		d=d.."\n" .. line
		line=gline()
	end
	udic[k]=d
end

function pre(s) -- preformat; s is "" or others
	pline('<pre>\t%s', t2h(s))
	line=gline()
	while line and sub(line,1,1)==tab do
		pline("\t%s", t2h(sub(line,2)))
		line=gline()
	end
	pline("</pre>")
	ugline()
end

function urll(s) -- url link
	-- translate http://HOST to:
	--   <a href="http://HOST">http://HOST</a>
	-- http://HOST must be at the beginning of line
	-- Lua does not have '|', so '(%s|^)' is not allowed
	s = gsub(s,'%s(https?://[^%s"{}()%[%]\\|^`]+)',
		' <a href="%1" target="other">%1</a>')
	s = gsub(s,'^(https?://[^%s"{}()%[%]\\|^`]+)',
		'<a href="%1" target="other">%1</a>')
	return s
end

function repl(s)
	if s == nil then
		return s
	end
	s=trans(s,udic)
	-- s=gsub(s,rdic)
	-- s=replace(s,k,udic[k])
	s = urll(s)
	if quof then
		s = rquote(s)
	end
	return s
end

function t2h(s) -- text to html
	-- order is matter !
	local t={["&"]="&amp;", ["<"]="&lt;", [">"]="&gt;"}
	s=gsub(s,"[&<>]",t)
	return s
end

function rit(line) -- rit block
	while line ~= "}$" 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<br>",repl(line))
		line=gline()
		if sub(line,1,1)=="-" then
			ugline()
			return
		end
	end
end

function parse(term,pat,opt)
	-- term: terminator; pat: pattern list
	local m,f,o,line
	line=gline()
	while line and line ~= term do
		--dbg("DBG parse:",nline,line)
		while true do -- case statement
			--dbg("DBG:",line, term)
			if line=="" then
				pline("<p>")
				break
			end
			if ritmode==true and sub(line,1,2)=="${" then
				rit(line)
				break
			end
			line = gsub(line,labp,function (m)
				-- Let <lab(tab:hogehoge)>
				-- then m is "tab:hogehoge"
				-- ldic is label dictionary
				m1 = match(m,"^([^:]+):")	-- m1 is "tab"
				if ldic[m1] == nil then
					ldic[m1] = 1
				else
					ldic[m1] = ldic[m1] + 1
				end
				ldic[m] = ldic[m1]
				return ldic[m]
			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<br>",repl(line))
			break
		end
		line=gline()
		--dbg("DBG4:",line)
	end
end

function init(bob) -- make toc, ref index
	-- 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 "{ <code>" 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
				end
			end
			subline(line)
		end
		line=gline()
	end
	nline=bob
end

function compf(x,y)
	local x1 = split(x,".")
	local y1 = split(y,".")
	local x2 = format("%4s%4s%4s",x1[1],x1[2],x1[3])
	local y2 = format("%4s%4s%4s",y1[1],y1[2],y1[3])
	return x2 < y2
end

function ptoc(title)	-- print toc
	local n,t
	--dbg(toc.n)
	if toc.n < 2 then
		return
	end
	n = toc.n
	toc.n = nil
	-- print toc
	pline("<b>%s</b>",title)
	pline("<ul>")
	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('<li style="font-weight:bold"><a href="#id.%s">%s</a> %s',s,s,repl(toc[s]))
		elseif match(s,"^%d+%.%d+%.0+$") then
			pline('<li><a href="#id.%s">%s</a> %s',s,s,repl(toc[s]))
		else
			pline('<li><a href="#id.%s">%s</a>&nbsp;&nbsp;<span style="font-size:small">%s</span>',s,s,repl(toc[s]))
		end
	end
	pline("</ul>")
	pline('<p style="margin-top:2em;">')
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,
	["RD:"]=redef
}

-- 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="^<[^>]*$"
cbrace="^.*>.*$"
uncook="^<.*>$"   -- don't cook the line "< .... >"
inbrac=".*<[^>]*$" -- in < >
labp="<lab[({]([^)}]+)[)}]>"
refp="<ref[({]([^)}]+)[)}]>"

if #arg == 0 then
	sysfatal("usage: ptt [-h] foo.tt")
end

fn=arg[1]
lines={}
-- read whole data to lines and trim the trailing spaces
for line in io.lines(fn) do
	line=gsub(line,"([%s]*)$","")
	push(lines,line)
end

pdic1=mkdic(pat1)
pdic3=mkdic(pat3)


nline=0
line=gline()
if sub(line,1,3)=="#!/" then -- then rit mode
	push(hdr,line)
	ritmode=true
	line=gline()
end
while sub(line,1,3) ~= "H1:" do
	push(hdr,line)
	line = gline()
end
bob=#hdr+1	-- beginning of body
if sub(lines[bob+1],1,3)=="BO:" then -- body option
	bopt=sub(lines[#hdr+2],4)
	bob=bob+1
end
hline=line
init(bob)	-- init() and then h1(), keep this order
h1(trim(sub(hline,4)))
nline=bob
parse(nil,pat0)
pline("</body>\n</html>")
