import os
import html
import markdown
os.path.dirname(os.path.abspath(__file__))
def dictGet(d, path):
for k in str(path).split('.'):
if type(d) is list or type(d) is tuple:
d = d[int(k)]
continue
elif type(d) is dict:
if (not k in d) and ('ex' in d):
print("query ex for field: %s (%s)" % (path, k))
d = d['ex'](d)
if k in d:
d = d[k]
continue
print("variable not found: %s (%s)" % (path, k))
return None
return d
def tostr(s):
return '' if s is None else str(s)
def text2html(t):
return html.escape(tostr(t)).replace("\n", "<br />")
def md2html(t):
return markdown.markdown(tostr(t))
tabsym = ' -- '
class TemplateText:
def __init__(self, text = ''):
self.text = text
def write(self, writer, context):
writer.write(self.text)
def optimize(self):
return self if self.text else None
def dbgPrint(self, tab):
print("%stext(%s)" % (tab, self.text.replace("\n", "\\n")))
class TemplateVarHtml:
def __init__(self, varpath = None):
self.varpath = varpath
def write(self, writer, context):
writer.write(tostr(dictGet( context, self.varpath )))
def optimize(self):
return self
def dbgPrint(self, tab):
print("%svarHtml(%s)" % (tab, self.varpath))
class TemplateVarMd:
def __init__(self, varpath = None):
self.varpath = varpath
def write(self, writer, context):
writer.write(md2html(dictGet( context, self.varpath )))
def optimize(self):
return self
def dbgPrint(self, tab):
print("%svarMd(%s)" % (tab, self.varpath))
class TemplateVarText:
def __init__(self, varpath = None):
self.varpath = varpath
def write(self, writer, context):
writer.write(text2html(dictGet( context, self.varpath )))
def optimize(self):
return self
def dbgPrint(self, tab):
print("%svarText(%s)" % (tab, self.varpath))
class TemplateVarTextMultiline:
def __init__(self, varpath = None):
self.varpath = varpath
def write(self, writer, context):
writer.write(text2html(dictGet( context, self.varpath )).replace("\n", "<br />"))
def optimize(self):
return self
def dbgPrint(self, tab):
print("%svarTextMultiline(%s)" % (tab, self.varpath))
class TemplateVarCount:
def __init__(self, varpath = None):
self.varpath = varpath
def write(self, writer, context):
v = dictGet( context, self.varpath )
writer.write(str(len(v) if hasattr(v, '__len__') else 0))
def optimize(self):
return self
def dbgPrint(self, tab):
print("%svarCount(%s)" % (tab, self.varpath))
class TemplateVarField:
def __init__(self, varpath = None):
self.varpath = varpath
def write(self, writer, context):
f = self.varpath.split('.')[-1]
writer.write("<p><b>%s:</b> %s</p>" % (
text2html( f ),
text2html( dictGet(context, self.varpath) ) ))
def optimize(self):
return self
def dbgPrint(self, tab):
print("%svarField(%s)" % (tab, self.varpath))
class TemplateIf:
def __init__(self, varpath = None, sub = None, alt = None):
self.varpath = varpath
self.sub = sub
self.alt = alt
def write(self, writer, context):
if dictGet(context, self.varpath):
if self.sub: self.sub.write(writer, context)
else:
if self.alt: self.alt.write(writer, context)
def optimize(self):
self.sub = self.sub.optimize() if self.sub else None
self.alt = self.alt.optimize() if self.alt else None
return self if self.sub or self.alt else None
def dbgPrint(self, tab):
print("%sif(%s):" % (tab, self.varpath))
if self.sub:
self.sub.dbgPrint(tab + tabsym)
if self.alt:
print("%selse:" % tab)
self.alt.dbgPrint(tab + tabsym)
class TemplateWith:
def __init__(self, varpath = None, varname = None, sub = None):
self.varpath = varpath
self.varname = varname
self.sub = sub
def write(self, writer, context):
if self.sub:
context = dict(context)
context[self.varname] = dictGet(context, self.varpath)
self.sub.write(writer, context)
def optimize(self):
self.sub = self.sub.optimize() if self.sub else None
return self if self.sub else None
def dbgPrint(self, tab):
print("%swith(%s:%s):" % (tab, self.varpath, self.varname))
if self.sub: self.sub.dbgPrint(tab + tabsym)
class TemplateComment:
def __init__(self, text = None):
self.text = text
def write(self, writer, context):
pass
def optimize(self):
return None
def dbgPrint(self, tab):
print("%scomment(%s)" % (texttab, self.text))
class TemplateLoop:
def __init__(self, varpath = None, keyvarname = None, varname = None, sub = None, sep = None):
self.varpath = varpath
self.keyvarname = keyvarname
self.varname = varname
self.sub = sub
self.sep = sep
def writeItem(self, writer, context, first, k, v):
if self.sep and not first:
self.sep.write(writer, context)
if self.sub:
ctx = dict(context)
ctx[self.keyvarname] = k
ctx[self.varname] = v
self.sub.write(writer, ctx)
def write(self, writer, context):
if self.sep or self.sub:
vv = dictGet(context, self.varpath)
if type(vv) is dict:
f = True
keys = list(vv.keys())
keys.sort()
for k in keys:
self.writeItem(writer, context, f, k, vv[k])
f = False
elif hasattr(vv, '__iter__'):
idx = 0
for v in vv:
self.writeItem(writer, context, not idx, idx, v)
idx = idx + 1
def optimize(self):
self.sub = self.sub.optimize() if self.sub else None
self.sep = self.sep.optimize() if self.sep else None
return self if self.sub or self.sep else None
def dbgPrint(self, tab):
print("%sloop(%s:%s:%s):" % (tab, self.varpath, self.keyvarname, self.varname))
if self.sub:
self.sub.dbgPrint(tab + tabsym)
if self.sep:
print("%ssep:" % tab)
self.sep.dbgPrint(tab + tabsym)
class Template:
def __init__(self):
self.items = []
def write(self, writer, context):
for i in self.items:
i.write(writer, context)
def optimize(self):
it = []
for i in self.items:
ii = i.optimize()
if ii:
if it and type(ii) is TemplateText and type(it[-1]) is TemplateText:
it[-1].text = it[-1].text + ii.text
else:
it.append(ii)
self.items = it
if len(self.items) == 1: return self.items[0]
return self if self.items else None
def dbgPrint(self, tab):
print("%stemplate:" % tab)
for i in self.items:
i.dbgPrint(tab + tabsym)
class TplLoader:
@staticmethod
def loadtext(filename):
filename = os.path.abspath(filename)
path = os.path.dirname(filename)
res = ''
text = ''
with open(filename, 'r') as f: text = f.read()
ii = 0
i = 0
while i < len(text):
if text[i:i+9] == '{include:':
res = res + text[ii:i]
a = i+9
b = text.find('}', a)
if b<0: raise Exception("include parse error: %s:%d" % (filename, a))
fn = text[a:b]
res = res + TplLoader.loadtext(path +'/' + fn)
i = b+1
ii = i
else:
i = i + 1
return res + text[ii:i]
def __init__(self, filename):
self.filename = os.path.abspath(filename)
self.text = TplLoader.loadtext(self.filename)
self.ptr = 0
def error(self):
with open('tplerr.txt', 'w') as f: f.write(self.text)
raise Exception("parse error: %s:%d" % (self.filename, self.ptr))
def readKey(self, k):
if self.text[self.ptr:self.ptr + len(k)] == k:
self.ptr = self.ptr + len(k)
return True
return False
def readVarname(self):
i = self.ptr
while i < len(self.text) and (self.text[i].isalnum() or self.text[i] in '_'):
i = i + 1
if i == self.ptr: return None
i, self.ptr = self.ptr, i
return self.text[i:self.ptr]
def readVarpath(self):
i = self.ptr
varpath = None
while True:
varname = self.readVarname()
if not varname:
if varpath: self.error()
break
varpath = varpath + '.' + varname if varpath else varname
if not self.readKey('.'): break
if not varpath:
self.ptr = i
return varpath
def loadVarText(self):
if not self.readKey("{:"): return None
varpath = self.readVarpath()
if not varpath or not self.readKey("}"): self.error()
return TemplateVarText(varpath)
def loadVarTextMultiline(self):
if not self.readKey("{t:"): return None
varpath = self.readVarpath()
if not varpath or not self.readKey("}"): self.error()
return TemplateVarTextMultiline(varpath)
def loadVarHtml(self):
if not self.readKey("{html:"): return None
varpath = self.readVarpath()
if not varpath or not self.readKey("}"): self.error()
return TemplateVarHtml(varpath)
def loadVarMd(self):
if not self.readKey("{md:"): return None
varpath = self.readVarpath()
if not varpath or not self.readKey("}"): self.error()
return TemplateVarMd(varpath)
def loadVarCount(self):
if not self.readKey("{count:"): return None
varpath = self.readVarpath()
if not varpath or not self.readKey("}"): self.error()
return TemplateVarCount(varpath)
def loadVarField(self):
if not self.readKey("{f:"): return None
varpath = self.readVarpath()
if not varpath or not self.readKey("}"): self.error()
return TemplateVarField(varpath)
def loadIf(self):
if not self.readKey("{if:"): return None
varpath = self.readVarpath()
if not varpath or not self.readKey("}"): self.error()
res = TemplateIf(varpath, Template(), Template())
e = False
while True:
if self.readKey("{endif}"): break
if self.readKey("{else}"):
if e: self.error()
e = True
continue
item = self.loadTemplate()
if not item: self.error()
if e:
res.alt.items.append(item)
else:
res.sub.items.append(item)
return res
def loadWith(self):
if not self.readKey("{with:"): return None
varpath = self.readVarpath()
if not varpath or not self.readKey(":"): self.error()
varname = self.readVarname()
if not varname or not self.readKey("}"): self.error()
res = TemplateWith(varpath, varname, Template())
while True:
if self.readKey("{endwith}"): break
item = self.loadTemplate()
if not item: self.error()
res.sub.items.append(item)
return res
def loadComment(self):
if not self.readKey("{comment}"): return None
res = TemplateComment()
i = self.text.find('{endcomment}', self.ptr)
if i < 0:
i = len(self.text)
res = TemplateComment(self.text[self.ptr:i])
self.ptr = i
if not self.readKey("{endcomment}"): self.error()
return res
def loadLoop(self):
if not self.readKey("{loop:"): return None
varpath = self.readVarpath()
if not varpath or not self.readKey(":"): self.error()
keyvarname = self.readVarname()
if not self.readKey(":"): self.error()
varname = self.readVarname()
if not varname or not self.readKey("}"): self.error()
res = TemplateLoop(varpath, keyvarname, varname, Template(), Template())
s = False
while True:
if self.readKey("{endloop}"): break
if self.readKey("{sep}"):
if s: self.error()
s = True
continue
item = self.loadTemplate()
if not item: self.error()
if s:
res.sep.items.append(item)
else:
res.sub.items.append(item)
return res
def loadTemplate(self):
t = self.loadVarText()
if t: return t
t = self.loadVarTextMultiline()
if t: return t
t = self.loadVarHtml()
if t: return t
t = self.loadVarMd()
if t: return t
t = self.loadVarCount()
if t: return t
t = self.loadVarField()
if t: return t
t = self.loadIf()
if t: return t
t = self.loadWith()
if t: return t
t = self.loadComment()
if t: return t
t = self.loadLoop()
if t: return t
i = self.text.find('{', self.ptr)
if i == self.ptr:
i = self.text.find('{', self.ptr+1)
if i < 0:
i = len(self.text)
if i <= self.ptr:
return None
i, self.ptr = self.ptr, i
return TemplateText(self.text[i:self.ptr])
@staticmethod
def load(filename):
l = TplLoader(filename)
t = Template()
while True:
tt = l.loadTemplate()
if not tt: break
t.items.append(tt)
if l.ptr < len(l.text): l.error()
t = t.optimize()
return t if t else TemplateText()