essmann.xyz

Web Programming with Hexiscript

Posted September 29, 2026

At time of writing I have two websites: The one you are on right now and my old Wordpress blog robinessmann.de. This Wordpress blog has been languishing for a few years now, showcasing a list of my projects and a blog post. So, paying the hoster and maintaining the Wordpress install with its constant plugin updates has become less and less reasonable. I've been planning to migrate it to my static web page here for a while now. However, I quickly found nuisances in the process. Creating separate pages would require copying my header and footer to every page and potentially adjusting it depending on context. What happens if I want to change the header or footer? As a matter of fact, the footer already changes every day as the background image of my website changes. To change them for a handful of pages might be feasible, but as more pages get added this will quickly become a nightmare. So what do I do?

One solution could be to generate the pages based on templates ahead of deployment. However, I found this to be suboptimal. I want to be able to iteratively build the website, and if I have to generate and redeploy everything after every minor change, I'll never be done with it. So I turned to the internet to see what options it had to offer. To little surprise there were two main options: PHP & JavaScript. Admittedly I've grown to like PHP recently and I've built some web apps with it, but I find it to be overkill for the slight amount of templating I need to do here. JavaScript was not an option for me. I want this to be as close to a static web page as I can. I prefer for my websites to remain functional in minimal and privacy respecting environments (no JavaScript or cookies). There are also Server Side Includes which I did consider. But for the most part I've simply kicked the can down the road.

With the expiry of my Wordpress hosting subscription looming ever closer, I've started to make peace with the fact that I would have to spin up PHP on this server and do some rewriting. However, while doing some more research I came across CGI. A piece of now practically ancient web history that I wasn't aware of. By using this interface it is possible to dynamically produce web content via any executable. Anything relevant to the request is fed into the script via stdin and environment variables, and the script will then produce the resulting headers and content via stdout. The idea of running a dynamic web site using something like C or Bash seemed amusing, but it also brought another idea of mine back to life. Using my own programming language for web development!

I first created hexiscript in early 2019 as an exercise to brush up on my functional programming skills with OCaml. I've always liked the idea of making my own programming language and I learned that functional programming languages like OCaml are particularly well suited for this. I can attest to that as it was a very pleasant and fun experience to build a programming language from scratch. I still enjoy playing around with hexiscript from time to time, but it is admittedly not very fast or powerful. Things were cobbled together with relatively little planning. And debugging the scripts can be kind of a pain, as the parser has no concept of line numbers (plenty of fun debugging sessions were had over the years). Nonetheless, it's something that's mine, and I've always liked the idea of working with it and building on it.

One thing I have shied away from is extending the language itself. For example I've repeatedly considered adding socket support, so I could build more powerful tools, maybe even a web server in hexiscript. But I enjoy the relative simplicity of the language, and I'd rather keep it as it is. Therefore using hexiscript for web development remained a pipe dream. Until my discovery of CGI that is. Now hexiscript can generate the web pages on the fly while the lower level work of handling sockets, connections, etc. can continue to be performed by a more mature web server. No adjustment to the language necessary! So I've been working on supporting CGI using hexiscript. I've built a JSON parser, Base64 encoder and decoder, a URL encoder and decoder, and a query string parser. Funny that I didn't actually end up needing any of this for this website, but it will make it possible to actually build some more sophisticated web apps using hexiscript with CGI in the future.

The final roadblock was that I normally use nginx as my web server of choice. It's the server I'm most comfortable with. Unfortunately nginx doesn't actually support CGI. After all it's considered an antiquated technology nowadays. Thankfully Apache and Lighttpd do support CGI. Anyway, as you can see, I got it working! Let me conclude by showing part of my hexiscript library for CGI, all dynamically pulled from the server on request of course:

if catch HTTP_HEXI
define HTTP_HEXI true

fun read_file name
    let f openin name
    let output ""
    while !(catch (let s read str f))
        let output output + s + "\n"
    endwhile
    return output
endfun

fun html_special_chars s
    let output ""
    for let i 0; i < len s; i++
        if s[i] = '<'; let output output + "&lt;"
        elif s[i] = '>'; let output output + "&gt;"
        elif s[i] = '\''; let output output + "&#039;"
        elif s[i] = '\"'; let output output + "&quot;"
        elif s[i] = '&'; let output output + "&amp;"
        else let output output + s[i]; endif
    endfor
    return output
endfun

# Parse query string into dict
# Strings remain url encoded!
fun parse_query_string s
    let output dict 10
    let value false
    let key ""
    for let i 0; i < len s; i++
        if value && s[i] = '='; throw "Errant equals sign in query string!"
        elif s[i] = '=' && len key = 0; throw "Empty key in query string!"
        elif s[i] = '='; let value true; let output[key] ""; continue
        elif !value && s[i] = '&'; throw "Errant ampersand in query string!"
        elif s[i] = '&'; let value false; let key ""; continue
        elif value; let output[key] output[key] + s[i]
        else let key key + s[i]; endif
    endfor
    return output
endfun

fun unreserved_char c
    return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' || c = '-' || c = '.' || c = '_' || c = '~'
endfun

fun char_to_hex c
    let c tonum c; let lower c % 16; let c c / 16
    if lower >= 10; let lower lower - 10 + 'A'
    else let lower lower + '0'; endif
    if c >= 10; let c c - 10 + 'A'
    else let c c + '0'; endif
    return "" + tochar c + tochar lower
endfun

fun rawurlencode s
    let output ""
    for let i 0; i < len s; i++
        if unreserved_char s[i]; let output output + s[i]
        else let output output + "%" + char_to_hex s[i]; endif
    endfor
    return output
endfun

fun urlencode s
    let output ""
    for let i 0; i < len s; i++
        if s[i] = ' '; let output output + "+"
        elif unreserved_char s[i]; let output output + s[i]
        else let output output + "%" + char_to_hex s[i]; endif
    endfor
    return output
endfun

fun rawurldecode s
    let output ""
    for let i 0; i < len s; i++
        if s[i] = '%'; let output output + tochar (tonum ("0x" + s[++i] + s[++i]))
        else let output output + s[i]; endif
    endfor
    return output
endfun

fun urldecode s
    let output ""
    for let i 0; i < len s; i++
        if s[i] = '+'; let output output + " "
        elif s[i] = '%'; let output output + tochar (tonum ("0x" + s[++i] + s[++i]))
        else let output output + s[i]; endif
    endfor
    return output
endfun

fun html_page h b
    return "<!DOCTYPE html><html><head>" + h + "</head><body>" + b + "</body></html>"
endfun

fun html_response r
    println "Content-Type: text/html; charset=UTF-8\n"
    println r
endfun

fun text_response t
    println "Content-Type: text/plain; charset=UTF-8\n"
    println t
endfun

fun json_response j
    println "Content-Type: application/json; charset=UTF-8\n"
    println j
endfun

endif