I have now finished writing up my templating engine and I'm pretty happy with it. There are still some rough edges but it works and it does what I need it to which is the most important part. I'm looking forward to my next attempt at this as I hope that will be my last but if it's not, I'll learn something else!
Now to lay out each of the things I've coded up and the behavior that I'm expecting. This is probably something I should have laid out at the very beginning but I wasn't sure exactly what I was going to implement.
The first and biggest different is the way data is passed into the template. In a previous version of this, I used the idea of passing in a context variable into the template and the template would then work off the context variable. I removed this idea and allowed my template to read directly from the database. This meant that instead of building a context variable, I could instead build up a work file. It's basically the same thing but this gives the templating language significantly more power.
I can do my reads directly from the templating language so if I need another variable from some other file, I can easily update just my template without having to recompile the BASIC code. This is probably a too smart to use function as now you have data coming from anywhere inside a template. You cannot know where a template is going to get its data from.
I've tried to keep my templating language very similar to BASIC with the goal being for it to work identically. The goal for this templating language is to be as close to BASIC as possible so as to leverage knowledge of BASIC to build a web page.
I wanted to be able to mix BASIC and html easily. I think one future avenue to explore things might be to remove the curly braces and to use context to figure otu what is code and what is html. I think that could be an interesting templating language but would probably be hell to implement.
The below is an example of the open statement. You could skip the else clause and then the template will fail during the compilation step. This way you can choose to test for the existence of a file versus hard exiting on an error.
{{ OPEN '','SITE-FILE' TO SITE.FILE ELSE SITE.FILE = '' }}
The read statement is similar to the open in that the else clause is optional for the same reason. Only the READ statement is available there is no readv and there is matread.
{{ READ SITE.ITEM FROM SITE.FILE,'MAIN' ELSE SITE.ITEM = '' }}
Variables can be defined like in BASIC. There is no special keyword and they go into a global scope. There is one shared scope.
{{ X = 1 }}
You can use variables like you can in BASIC.
<h1>{{ SITE<1,1> }}
Substrings work the same way as BASIC. This will give you the first 2 characters.
<h1>{{ SITE.ITEM<1,1>[1,2] }}</h1>
The basic FOR loop.
{{ FOR I = 1 TO 10 }}
{{ I }}
{{ NEXT I }}
The loop statement. This can be shortened into the LOOP UNTIL DO version.
{{ I = 0 }}
{{ LOOP }}
{{ I = I + 1 }}
{{ UNTIL I > 10 DO }}
{{ I }}
{{ REPEAT }}
The if statement. This can be expanded with END ELSE IFs. You can also use the various comparison operators as well as the logical operators.
{{ TEST = 0 }}
{{ IF TEST THEN }}
<div>True</div>
{{ END ELSE }}
<div>False</div>
{{ END }}
This print statement will during the compilation process and will not be part of the template. Handy when you need to debug things.
{{ PRINT "Hello, World!" }}
For now only 1 argument is allowed into the subroutine and that same variable can be modified in place. This is an arbitrary limit.
{{ CALL SAMPLE.CALL(P1) }}
All of these features can be mixed together to build out complex templates. The language is currently slow and so optimizations are needed. I'm also not convinced that the code is truly done the right way as there are parts of the code that I'm not happy with.
My next attempt will likely steal from this set of notes so I think it is worth writing everything down.
Below is the template for my blog. It doesn't use everything I've described but it serves as an example of a real template that I'm using as of 2023-06-19. It actually has stayed the same for the most part from what I defined at the beginning of the project surprisingly.
{{ OPEN '','SITE-FILE' TO SITE.FILE ELSE NULL }}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Posterity</title>
<link rel="icon" href="data:;base64,=">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<!--# include file="ssi/header.html" -->
<h1>for <span title="To You, 2,000 Years From Now">posterity</span></h1>
<div class="flex">
<div class="flex-1">
{{ READ SITE.ITEM FROM SITE.FILE,'MAIN' ELSE SITE.ITEM = '' }}
{{ FOR I = 1 TO DCOUNT(SITE.ITEM<1>,@VM) }}
<div>
<a href="{{SITE.ITEM<2,I>}}">{{SITE.ITEM<1,I>}}</a>
<br>
<small>{{ SITE.ITEM<3,I> }}</small>
</div>
<br>
{{ NEXT I }}
<h2>lists</h2>
{{ READ SITE.ITEM FROM SITE.FILE,'LISTS' ELSE SITE.ITEM = '' }}
{{ FOR I = 1 TO DCOUNT(SITE.ITEM<1>,@VM) }}
<div>
<a href="{{SITE.ITEM<2,I>}}">{{ SITE.ITEM<1,I> }}</a>
<br>
<small>{{ SITE.ITEM<3,I> }}</small>
</div>
<br>
{{ NEXT I }}
<h2>the war chest</h2>
{{ READ SITE.ITEM FROM SITE.FILE,'WARCHEST' ELSE SITE.ITEM = '' }}
{{ FOR I = 1 TO DCOUNT(SITE.ITEM<1>,@VM) }}
<div>
<a href="{{SITE.ITEM<2,I>}}">{{ SITE.ITEM<1,I> }}</a>
<br>
<small>{{ SITE.ITEM<3,I> }}</small>
</div>
<br>
{{ NEXT I }}
</div>
<div style="font-size:13px">
<div style="text-align: right;">
<img style="width:225px;border-radius:5px;" src="/images/seed_29_00002.png" alt="Cyberpunk Edgerunners, David sitting witha VR headset in public with his crew." title="the future we were promised">
</div>
<h3>latest</h3>
{{ FOR I = 1 TO DCOUNT(LATEST.POSTS<1>,@VM) }}
<div>
<a href="/devlog/{{LATEST.POSTS<2,I>}}">{{ LATEST.POSTS<1,I> }}</a>
</div>
{{ NEXT I }}
<h3>random</h3>
{{ FOR I = 1 TO DCOUNT(RANDOM.POSTS<1>,@VM) }}
<div>
<a href="/devlog/{{RANDOM.POSTS<2,I>}}">{{ RANDOM.POSTS<1,I> }}</a>
</div>
{{ NEXT I }}
</div>
</div>
</div>
<!--# include file="ssi/footer.html" -->
</body>
</html>