shp: hypertext processor

This commit is contained in:
yosh 2023-11-20 18:39:34 -05:00
parent 31f6b8f005
commit f47ff90e72
5 changed files with 69 additions and 18 deletions

View File

@ -7,16 +7,13 @@ export _SRCDIR="${_SRCDIR:-"${BASE}/src"}"
__NL="
"
__cleanup() {
[ -f "${__FILE_TMP:-}" ] && rm "$__FILE_TMP"
true
}
# process an html formatted file from stdin and output to stdout
# process an html formatted file either given or from stdin and output to stdout
# if a file is given, stdin is free to be used by any shp files it encounters
__process_shp() {
__SHLVL=$((__SHLVL + 1))
eval '__SH_FLAG'$__SHLVL'=""
__LINE_EXEC_'$__SHLVL'=""
__TMP_FILE'$__SHLVL'="${1:-/dev/stdin}"
while read -r __LINE'$__SHLVL'; do
if [ -z "$__SH_FLAG'$__SHLVL'" ]; then
case "$__LINE'$__SHLVL'" in
@ -40,10 +37,11 @@ __process_shp() {
*) ;;
esac
fi
done
done < "$__TMP_FILE'$__SHLVL'"
[ $__SH_FLAG'$__SHLVL' ] && eval "$__LINE_EXEC_'$__SHLVL'"'
__SHLVL=$((__SHLVL - 1))
}
__SHLVL=0
__lowdown() { lowdown --html-no-skiphtml --html-no-escapehtml "$@"; }
@ -51,16 +49,11 @@ __lowdown() { lowdown --html-no-skiphtml --html-no-escapehtml "$@"; }
# uses a special file in _INCDIR, `markdown-template.sh` to create the html
__process_md() {
[ -f mdmeta ] && set_md_metadata "$1"
. "$_INCDIR"/_markdown-template.sh <<-EOF
__process_shp "$_INCDIR"/_markdown-template.shp <<-EOF
$(__lowdown "$1")
EOF
}
trap '__cleanup' INT HUP QUIT EXIT
__FILE_TMP="$(mktemp -u)"
find "$_SRCDIR" -type d -exec sh -c 'for d; do mkdir -p "$_BUILDDIR"/"${d#"$_SRCDIR"}"; done' sh {} +
# standard library
include() {
# if the path contains a slash, then treat it as-is
@ -68,12 +61,12 @@ include() {
case "$1" in
*/*.md) __lowdown "$1" ;;
*.md) __lowdown "$_INCDIR"/"$1" ;;
*/*) __process_shp < "$1" ;;
*) __process_shp < "$_INCDIR"/"$1" ;;
*/*) __process_shp "$1" ;;
*) __process_shp "$_INCDIR"/"$1" ;;
esac
}
set_md_metadata() {
set_md_metadata() {
__MD_BASE="${1##*/}"
while read -r __MD_LINE; do
case "$__MD_LINE" in
@ -96,12 +89,21 @@ escape() {
fi
}
__SHLVL=0
__cleanup() {
[ -f "${__FILE_TMP:-}" ] && rm "$__FILE_TMP"
true
}
trap '__cleanup' INT HUP QUIT EXIT
__FILE_TMP="$(mktemp -u)"
find "$_SRCDIR" -type d -exec sh -c 'for d; do mkdir -p "$_BUILDDIR"/"${d#"$_SRCDIR"}"; done' sh {} +
while read -r __FILE; do
cd "${__FILE%/*}"
__OUTFILE="$_BUILDDIR/${__FILE#"$_SRCDIR"}"
case "${__FILE##*/}" in
*.[hH][tT][mM][lL]) ( __process_shp ) < "$__FILE" > "$__FILE_TMP" ;;
*.[hH][tT][mM][lL]) ( __process_shp "$__FILE" ) > "$__FILE_TMP" ;;
*.[mM][dD]) ( __process_md "$__FILE" ) > "$__FILE_TMP"; __OUTFILE="${__OUTFILE%.*}.html" ;;
mdmeta) continue ;;
*) cp -p "$__FILE" "$__FILE_TMP" ;;

View File

@ -1,3 +1,4 @@
<?shp
include header-unified.shp
echo "<main>"

View File

@ -0,0 +1,41 @@
# shell: hypertext processor
that name might ring a few alarm bells to anyone with a sense of morality. alas, necessity is the mother of invention, and I have willed this into existence
in normal speak, I have created probably the most cursed static site generator ever, by combining the inlining of php with the language of shell. I have spent too much time to refine this to a state where it's basically the same style of programming as php that I have looped back to being proud of it
## but why
this site you're reading right now is hosted on [unix.dog](https://unix.dog), a pubnix. when I got a unix.dog account, there was no templating software such as php, hugo, etc. due to this, I was [making most of my pages manually](https://git.unix.dog/yosh/website/src/commit/62a41d9c17460dd1f445063f4f9aec8200891c52) to start off, which I knew very well wasn't going to scale. I can't remember if between this time and when I set up my first draft that php was added, but either way, I *thought* there wasn't any templating software still. I had two options: create my own using resources already on the system, or compile one
I chose to make my own. I've already had a bunch of experience writing shell, so I wanted to take a stab at another "real" project in it
[the complete end result](https://git.unix.dog/yosh/website/src/commit/68a17eb2afce3eeeca336a7d30d80b75d586fd5c/build.sh) fucking sucked in retrospect. how it'd work was that you'd have to make a single line comment following the form of `<!--RES f=file;VAR=val;VAR2=val2 -->` etc. to bring in a template in the `BUILD_RESOURCES` folder, replacing any strings matching `var-VAR` with `val`, `var-VAR2` with `val2`, etc.
certainly, it was slow as shit as well. I can't remember the exact specifics, but it'd take almost 2 seconds to go through my (very small, mind you) site on my hardware, let alone how long it took on unix.dog's hardware (I want to say around 5-6 seconds?). clearly, I needed something better
again, I still thought no templating software was available, so I set out to make a *better* way to update my site with a shell script
and that's how I landed on shp
## the process
from the get-go, I had the idea of making a php-like templating method but with shell. the initial draft had the bright idea of using the html comments `<!--sh` and `-->` to represent the blocks of shell that would be run. when it came to detecting the boundaries and substituting the output of shell commands, I [used `awk` and temporary files](https://git.unix.dog/yosh/website/src/commit/b20f843c47a7adb4f5cdd4e31ad7b498b227ee24/build.sh) to perform the actual replacement, as well as grep to see if I still needed to replace stuff
of course, this was slow as shit, as well as unreliable. It'd take over a second to build the site on my machine--which keep in mind, still wasn't using markdown for anything. this [was optimized](https://git.unix.dog/yosh/website/src/commit/ab5c46d14f0a46dd39be0b4432a1896d826bffa4/build.sh) to just about a second by doing a single run of `grep` for each file, storing the counted lines, then iterating over that. even still, the unreliability persisted, because in the even that the shell block outputted `<!--sh <stuff> -->`, the awk would fuck up and flag it. of course, any reasonable person *wouldn't* do this, but reasonable people *would* put `-->` for arrows in their webpage, and managing that proved to be a much harder task than one would think in awk. another refactor had to be done
[by this point](https://git.unix.dog/yosh/website/src/commit/b20f843c47a7adb4f5cdd4e31ad7b498b227ee24/build.sh), I dropped the idea of using normal html comments and went full-on using `<?shp <shell> ?>` syntax (well, `<?sh` here, but that'll change in the next commit). this didn't exactly simplify parsing relatively--you'll see why later--but it *did* play in to the idea of making this more "php-like", which I find funny. instead of using the super slow method of `grep` + `awk` for every single file, I made a `while read -r` loop for the processing function, which fed into a `case` glob parser for each line, setting a flag depending on if the line contained `<?shp` or not. if the flag was null, then echo the line. if it was just set, echo everything before the `<?shp` and set it. if it is active (including just set), `eval` the line. if the line contains `?>`, then print everything after it and unset the shell flag. this significantly sped up the process, given how it wasn't iterating over the entire file multiple times--only once now
by this point, it was looking pretty good! my site was building much faster now--about 0.3 seconds on my machine. for this being in shell, that's pretty good! this wasn't without its own set of issues, though. for one, calling `eval` separately for each line was both slow and worked weird. if I needed multiline commands, such as a here-document or simply wrapping a long command across multiple lines, it'd break. the major issue though is that I didn't have a proper way of "including" other files. how I worked around this was by [directly calling other shell scripts](https://git.unix.dog/yosh/website/src/commit/9d7a7fd02cf4709c2f4ee456726b9948f39737bb/src/index.html), where the global working directory would always be the `include` directory. this was a bit clunky, and something that nagged at me. sure, it *worked*, but it wasn't really "including", and I was moreso just wrangling shell scripts together without a proper "include" function. one last time, I went back to the drawing board
the major thing I noticed with attempting to make a proper `include` function was shell variables. if you didn't know, variables in shell are *always* global unless set in a subshell/subprocess. this is actually why I made the variables that are used for the build script all caps and prefixed with two underscores--something to clearly indicate that these variables are only meant to be used by the build script, and you probably shouldn't be using variables that follow the same convention. anyway, I realized that making an include function that worked like php, where variables persist, would mean processing that file in the same shell environment. however, doing so would override crucial variables such as `__LINE` and `__SH_FLAG`, causing who knows what to break.
realizing this made me come to the most cursed part of [my final script](https://git.unix.dog/yosh/website/src/commit/358459a67872a46e76569b4564faccce24623591/build.sh). I wrapped the *entire* file processing part in an `eval`, single-quoted as to not double-evaluate any parts. whenever it came to the important variables like `__LINE`, `__SH_FLAG`, and `__LINE_EXEC` (a variable I made to work around the "`eval` every line" issue), I would go out of the single quotes briefly to add a number to the end, defined by the variable `__SHLVL`. this variable is incremented by 1 each time `__process_shp` is run, then decremented when the function ends. this causes includes to use a different variable for their own contexts without influencing the parent's special variables, while keeping the benefit of making included variables work like php.
by now, it was basically perfect. implementing markdown support was very simple--passing the markdown file through `lowdown` and sending that to the stdin of a special script in the include directory, `_markdown-template.sh`. as I was writing this, I changed this to be a proper `shp` file by making `__process_shp()` take an argument rather than consuming stdin for itself. personally, I never needed like, a bunch of html outside of `include` for markdown--just using shell's `echo` with commands was enough for me--but oh well! at least it's consistent now, and it doesn't incur a big speed detriment
## the result
as it stands, I feel as if I am completely done with this. the "library" gives you only a few basic functions--`include`, `set_md_metadata`, and `escape`--and the rest is up for you to figure out. this site builds in ~0.4 seconds as it stands, which while slower than one of the previous iterations, makes up for it with like 10x more functionality. it scales slightly worse than linearly, which is to be expected, but build times shouldn't get too bad. oh well!
the only caveat that I have seen that remains in the final script is the fact that you can't start a `shp` block on the same line as one ends. for the sake of keeping the parsing code simple, I am not changing this. any reasonable person wouldn't do this.
the result is [bundled with my website repo](https://git.unix.dog/yosh/website/src/branch/master/build.sh). it's a shell script. I don't care enough to give it the whole 9 yards. be reasonable
[back](index.html)

View File

@ -35,3 +35,9 @@ TITLE="thoughts on yash | ~yosh"
TB_TITLE="yash"
POST_TIME=2023-11-18T01:47:00Z
ENTRY_UUID=d6659f13-4ed1-4894-8fd5-4d3a931ce45d
2023-11-20_shp-hypertext-processor.md
TITLE="shp: hypertext processor | ~yosh"
TB_TITLE="shp"
POST_TIME=2023-11-20T23:40:00Z
ENTRY_UUID=14f1934b-76d1-4dd4-ae1c-65bf0e305eea

View File

@ -3,3 +3,4 @@ I want to *eventually* make my own fursuit. I also know there's 30 million tiny
## non-electronic
- [tiny silicone spatulas](https://twitter.com/ApricityHats/status/1673413767885910023) made for kitchen use spread hot glue very well (reportedly better than popsicle sticks!). [amazon link](https://www.amazon.com/dp/B08SL4N33F)
- [matrices.net](https://matrices.net/) - the website of a costume designer and furry that contains lots of little resources