One thing that annoyed me endlessly is that all existing systems for merging data with templates to output text are all stupid and complicated and confusing.
This is not gonna change that, but at least it's yet another alternative in the world of stupid ass templating engines. At least it's relatively agnostic to output languages.
This code is ugly. It is written in python but barely, as I wanted it to be trivial to port to some random assembler language for no good reason.
The program works with 31/2 files.
First of all is the template
file, next is the data
file, then comes where it's normally output
to and last there is access to a second output stream that acts as an extension on your data stream.
The format for the data files is really simple: It's dollar sign ($), followed by section name, then a whitespace character and then whatever this section outputs.
$section output
Of course, given that a newline is also a whitespace you can also write this:
$section
A somewhat longer output.
You can of course use multiple lines.
The template system is similar but a little more complex.
First of all you can just write the section name followed by a newline. The system will then repeat that section as many times as it comes across it, then move on to the next text.
$section
However, say you need to surround it with more text on every entry. Then you can use the following syntax:
$section before$after
Each dollar after the first will be replaced by the contents of $section
, yet the line will only repeat after the new-line.
So for instance:
$section <a href="$">$</a>
with the data file
$section test1.html
$section test2.html
$section test3.html
<a href="test1.html">test1.html</a>
<a href="test2.html">test2.html</a>
<a href="test3.html">test3.html</a>
You can also output one of multiple variables by using the pipe (|), in which case order in the data file will be preserved. So:
$test1|test2 Variable $ yay
with data
$test1 val1
$test2 val2
$test1 val3
Variable val1 yay
Variable val2 yay
Variable val3 yay
More advanced multiline blocks are as of yet not supported. It was really meant for those repetitive tasks, not for advanced templating!
Finally, there is a slight system present to make it slightly more recursive, slightly more compact. This is what the temp files are for.
So if you start your block in the template with %section
and end it with %
, then everything in between those percentages gets "added" to the input data.
%section
$test Testing $ test
%
Outside
$section
More
$section
End
$test test1
$test test2
$test test3
Outside
Testing test1 test
Testing test2 test
Testing test3 test
More
Testing test1 test
Testing test2 test
Testing test3 test
End
import os
def give_keyword(line):
return line.rstrip(b"\n").split(b" ")[0]
def parse(template, data, output_file, temp_file=".tmp.txt"):
if os.path.exists(temp_file):
os.remove(temp_file)
with open(template, "rb") as template:
output = open(output_file, "wb")
while 1:
line = template.readline()
if len(line)==0:
if os.path.exists(temp_file):
os.remove(temp_file)
return
ident_char = bytes([line[0]])
stripped_line = line.lstrip()
indent = len(line)-len(stripped_line)
if stripped_line.startswith(b"%"):
output.close()
if not any(ord("A") 0:
output.write(ident_char*indent)
data_out = None
first = True
with open(data, "rb") as data_file:
meta = None
while 1:
data_byte = data_file.read(1)
if len(data_byte)==0 or data_byte == b"$":
if data_out is not None and len(data_out) != 0:
#two modes of operation: replace the whole line, or replace $ with snippets
if b"$" in stripped_line:
if not first:
output.write(ident_char*indent)
output.write(stripped_line.replace(b"$",data_out.rstrip(b"\n")))
first = False
else:
output.write(data_out.replace(b"\n",b"\n"+ident_char*indent))
if len(data_byte)==0:
break
data_out = None
meta = b""
elif data_byte.isspace() and meta is not None:
#print(meta)
if meta == keyword or (b"|" in keyword and meta in keyword.split(b"|")):
data_out = b""
else:
data_out = None
meta = None
elif meta is not None:
meta += data_byte
elif data_out is not None:
data_out += data_byte
data_out = None
first = True
if os.path.exists(temp_file):
#repeat of the above but now with our temporary file
with open(temp_file, "rb") as data_file:
meta = None
while 1:
data_byte = data_file.read(1)
if len(data_byte)==0 or data_byte == b"$":
if data_out is not None and len(data_out) != 0:
if b"$" in stripped_line:
if not first:
output.write(ident_char*indent)
output.write(stripped_line.replace(b"$",data_out.rstrip(b"\n")))
first = False
else:
output.write(data_out.replace(b"\n",b"\n"+ident_char*indent))
if len(data_byte)==0:
break
data_out = None
meta = b""
elif data_byte.isspace() and meta is not None:
if meta == keyword or (b"|" in keyword and meta in keyword.split(b"|")):
data_out = b""
else:
data_out = None
meta = None
elif meta is not None:
meta += data_byte
elif data_out is not None:
data_out += data_byte
if __name__=="__main__":
import sys
parse(*sys.argv[1:])
For in the future, I want in the template for a $
followed by a newline to have the special behaviour to not repeat that section until the first of the variables within that section gets repeated in the data file. Or something idk. It needs something for more complex data output, this is piss poor but for what i made it for it has served it's purpose.