teg! - a web building block for the desperate
code: codeberg

What's teg?

teg is a small (~770 LOC by cloc, ~1200 lines total) portable web "anti-framework" foundation made in AWK, compatible with pretty much all AWKs out there, so you can even use teg on Plan 9.

teg is meant to be a minimal building block for your full website; it only acts as a page generator. You can piece together teg with your own scripts and programs to, for example, make a full-on CGI system. teg can be used with a Makefile to generate a static site from a list of .teg files.

Why's teg?

I wanted a simple framework for my website to not write everything in HTML by hand. Everything I looked at didn't fit me in one way or another, so I decided to just make something of my own.

This project is to not be taken seriously. Heck, all of this is a giant shitpost. I know this part is not even worth adding, but some people took this whole thing as something serious like I'm going to pitch it to a company. Sorry, no I won't.

Let's have fun and more.

Naming

teg is always typed lowercase (okay, all uppercase in the code for library use)
teg is named as a reference to the Russian name for graffiti tags (тег, теггинг) and their crudeness, just like teg itself.

Syntax

The syntax of teg is all over the place. It is a mix of a dialect of Markdown, calls, variables and comments.

Comments

Comments are defined with two equals signs at the start of a line:
== This is a comment
   == This is NOT a comment
All comments are skipped.

Calls

Calls are defined with an exclamation mark at the start of a line:
!exec_raw echo 'Hello, World!'
If the call exists, it will be executed, and the line replaced with the call's return.
The calls can be categorized into a couple of groups:

Basics

!var variable=value

Set variable to value
If value is not set but an equals sign exists, clear variable
The var call also supports using a heredoc-like syntax to input long multiline variables:

!var variable<delim
...
delim

The variable is being filled with the contents of all the following lines until a line consisting of just "delim" is found.

!start

Declare the start of the webpage.
You cannot use element-related and exec calls (for the exception of !exec_inc) before calling !start

!abort exit code

Cleanly abort execution, return exit code

!inc file

Include a file

!log text

Print text to stderr

Exec

!exec_raw command

Run a command in your system's command interpreter and give its output to the Markdown processor

!exec_fmt command

Run a command and paste its output as a code block. Disables the Markdown processor for the contents of the code block

!exec_inc command

Run a command and include its output as teg code

Elements / Tags

!e element class options

Open an HTML tag on first call, close the tag on the second call for the same class
If class is "_", the tag does not receive a class (useful for setting options without giving it one)
If class starts with a #, assign an HTML ID instead. If you try to create two elements with the same ID, an error is thrown
You can specify multiple classes / IDs by separating them with semicolons like so: !e div #some-id;some-class1;some-class2
The options get passed to the tag like so: !e div _ style="font-size: 90%" -> <div style="font-size: 90%">
If you call !e without any arguments, the last opened tag will be closed
Example:
!e pre #container;containers;borders; style="padding: 1em; border: 2px dotted black; font-family: monospace"
Hello, world!
This is a demo of the teg element call.
!e

!eo element class options

Same as !e, just don't close the tag on a second call for the same class
Useful for self-closing tags!

!eoc element class options

Same as !eo, just immediately close the tag

Inline calls

The following syntax can be used to include a call or a variable without splitting a line: Here's an example of using inline calls to create a page that displays your distro name:
!var osname={!exec_raw . /etc/os-release&&echo $PRETTY_NAME||echo unknown!}
!start
My current distro is `{$osname$}`
Inline calls can be nested:
!var ls_args=-lF
!start
ls output: {!exec_fmt ls {$ls_args$}!}
... but please keep in mind that nesting can sometimes be very broken and cause unexpected issues.

Inline calls get expanded inside codeblocks, so if you want to display one, you need to escape it.
Example:
hello  {$who$} <- NO!
hello \{$who$} <- Yes!

Builtin variables

teg has some built-in variables that you can read for information or set to change behavior. They can be put into several groups similarly to calls:

Basics

title=file

Page title

description

Page description for OpenGraph embeds

lang=en-US

Page locale

status

Return an HTTP status for use with CGI
Must be set before !start

ctype=text/html

Content type for use with CGI
Only works together with status

icon

Favicon path

Embeds and media

color_chrome

Browser style color (only for mobile)

embed_img

Add an image embed
Must be a link to a website

embed_og=0

Enable OpenGraph embeds (<meta property="og:.." ...>)

embed_twt=0

Enable Twitter embeds (<meta name="twitter:.." ...>)
Setting this to 0 doesn't disable embeds on Twitter (i think) as it also uses OpenGraph
This is used by Discord for large image embeds

Styling

style

External CSS stylesheet to reference, specified as a list of files separated by a semicolon
Example: !var style=/data/main.css;custom.css

style_inline

CSS stylesheet files to include in HTML
Supports the same semicolon separated syntax

Can be also a stylesheet directly inside teg by using a heredoc variable declaration, determined to be one if it's more than one line in length.
Example:
!var style_inline<eol
body {
	font: monospace;
}
eol

JS

script

JS file to reference in <head>
Supports the same semicolon separated syntax as style

script_inline

JS files to include at the start of <body>
Supports the same semicolon separated syntax as script

Supports the same heredoc declaration as style_inline

Internals and debug

debug=0

Enable / disable debug logging

exit_on_error=1

Abort processing and exit on error

no_br=0

Do not put line breaks for the next N lines

no_proc=0

Halt processing for the next N lines

file

File that is currently being processed

Markdown

teg's markdown is relatively close to the one you are familiar with, except for things like altered newline behavior, styles inside single quote code and some missing complex features like tables.

Spoilers

||[Click me]Boo!||
Click meBoo!

Modified newline behavior

Line 1
Same line

Line 2


Line 4?
formats as
Line 1 Same line
Line 2

Line 4?

Strikethrough text

~~strikethrough!~~
strikethrough!

Small text

-# I am tiny!
I am tiny!

Library usage

teg can be used as a library in GAWK. For that, you need to first define TEG_AS_LIBRARY = 1, then include teg.awk like so:
BEGIN { TEG_AS_LIBRARY = 1 }
@include "teg.awk"
Some of the more useful functions you get access to are:

TEG_init()

Initialize teg

TEG_proc(src)

Process a line, returns formatted line

TEG_end()

Finalize processing, returns end of HTML

TEG_md_fmt(str)

Convert markdown to HTML, returns converted HTML
If you use this alone, remember to provide it an empty string at the end of processing to close all lists and codeblocks

TEG_callproc(str, explicit)

Process a teg call, return its output
Set explicit to 1 to run the processing even if we've not reached !start yet

TEG_read_list(list, delim)

Read each file from a list delimited by delim and return all of their contents

TEG_escape_html(str)

Escape some symbols in string for inclusion in HTML, returns escaped string

TEG_MARK(optional_str)

Print a marker to stderr with a number incrementing each call, useful for debugging

TEG_explode_arr(array)

Pretty print for arrays, useful for debugging

TEG_logt(str, type)

Debug, warning and error logging

type = 1 or unset: Debug log
type 2: Warning
type = 3: Error

If TEG_c_vars["exit_on_error"] is set to 1 and an error is logged, the script will exit

TEG_relpath(path)

Complete the input path with currently running/included teg script's path and return a relative path to the file
Returns the completed path

TEG_exists(file)

Check if a file exists, returns 1 if yes and 0 if no

TEG_is_null(str)

Check if a string is empty or made of purely whitespace, returns 1 if yes and 0 if not

Some of the useful variables you get access to are:

TEG_c_vars[]

The variables you can set with !var

TEG_reglist[]

An array of various REGEX for matching various things

TEG_list_type[0,1]

Current and previous markdown list types

1 - Unordered
2 - Ordered

TEG_list_lvl[0,1]

Current and previous markdown list levels

TEG_blockquote_lvl[0,1]

Current and previous markdown blockquote levels

You can find an example in the teg repository at extra/library_use.awk.

Before using any of the more complex functions like TEG_md_fmt() or TEG_proc() make sure to run TEG_init() to prepare all variables they expect.
After you're done processing a chunk of text with TEG_proc(), run TEG_end() (and print / append its return) to put the finishing HTML.