From cdecb8191a791b8db66effe61de16cb3e3904238 Mon Sep 17 00:00:00 2001 From: "Brandon C. Irizarry" Date: Sun, 8 Mar 2026 19:16:04 -0400 Subject: Reorder posts according to new publishing algorithm --- .../adding-cgit-subdomain-to-personal-site.md | 195 --------------------- drafts/posts/apple-time.md | 21 --- drafts/posts/making-cgit-go-gettable.md | 101 ----------- .../smoothing-over-more-markdown-pain-points.md | 73 -------- drafts/posts/understanding-pratt-parsing.md | 155 ---------------- drafts/posts/writing-my-blog-with-eleventy.md | 90 ---------- drafts/projects/buildablog.md | 109 ------------ index.md | 9 - index/index.md | 9 + posts/adding-cgit-subdomain-to-personal-site.md | 195 +++++++++++++++++++++ posts/apple-time.md | 21 +++ posts/making-cgit-go-gettable.md | 101 +++++++++++ posts/smoothing-over-more-markdown-pain-points.md | 73 ++++++++ posts/understanding-pratt-parsing.md | 155 ++++++++++++++++ posts/writing-my-blog-with-eleventy.md | 90 ++++++++++ projects/buildablog.md | 109 ++++++++++++ 16 files changed, 753 insertions(+), 753 deletions(-) delete mode 100644 drafts/posts/adding-cgit-subdomain-to-personal-site.md delete mode 100644 drafts/posts/apple-time.md delete mode 100644 drafts/posts/making-cgit-go-gettable.md delete mode 100644 drafts/posts/smoothing-over-more-markdown-pain-points.md delete mode 100644 drafts/posts/understanding-pratt-parsing.md delete mode 100644 drafts/posts/writing-my-blog-with-eleventy.md delete mode 100644 drafts/projects/buildablog.md delete mode 100644 index.md create mode 100644 index/index.md create mode 100644 posts/adding-cgit-subdomain-to-personal-site.md create mode 100644 posts/apple-time.md create mode 100644 posts/making-cgit-go-gettable.md create mode 100644 posts/smoothing-over-more-markdown-pain-points.md create mode 100644 posts/understanding-pratt-parsing.md create mode 100644 posts/writing-my-blog-with-eleventy.md create mode 100644 projects/buildablog.md diff --git a/drafts/posts/adding-cgit-subdomain-to-personal-site.md b/drafts/posts/adding-cgit-subdomain-to-personal-site.md deleted file mode 100644 index c62fde8..0000000 --- a/drafts/posts/adding-cgit-subdomain-to-personal-site.md +++ /dev/null @@ -1,195 +0,0 @@ -+++ -title = "Adding a CGit Subdomain To My Site" -tags = ["linux", "nginx", "certbot", "cgit"] -summary = "Setting up CGit on my VPS." -date = 2026-03-06 -+++ - -# Motivation - -To update the content of my blog, I have to do something of a -dance. My blog's content started out life as a subdirectory of my SSG -project, but now lives in its own separate Git repo. This is super -convenient since I now can host my blog wherever I want on my VPS's -filesystem, for example in a folder called `brandons_blog`. So right -now what I'm doing is this: - -1. Commit all changes. -2. Push the changes to GitHub, where it's currently hosted in a - somewhat centralized manner. -3. Log in via SSH into my VPS. -4. Perform a `cd` into the `brandons_blog` directory, and run `git - pull`. - -However, I thought, "wouldn't it be nice if I could push **directly** -to the VPS repo?" And so I started working on that idea in the obvious -manner: add the VPS repo as a remote, such that I would be pushing to -`vps/main` alongside of `origin/main` (where `origin` points to -GitHub). - -It turns out that this alone is a shade more complicated that it would -seem: I had to first add a new `git` user (see [this tutorial](https://landchad.net/git/)), -and then adjust my SSH configuration appropriately to allow for this -pseudo-user to log in via SSH (since I would be pushing into a -directory now owned by it.) - -I quickly learned, to my dismay, that I couldn't push to the VPS -remote if it's not a bare repo. A bare repo is one initialized with -`git init --bare`, so that it doesn't have a working directory -populated with files. However, the buildablog server expects to see a -working directory with blog files (not just blobs, for example), so -this doesn't solve my problem. - -# Cgit - -What I ended up doing in the end didn't solve this problem, but it -ended up becoming an interesting rabbit hole in its own right. - -I ended up adding the Cgit web interface to my site, available via -. I checked out a [tutorial](https://landchad.net/cgit/) on -how to do it, but their suggested Nginx setup was off in some -parts. - -After a ton of false starts, I ended up doing slightly different. Note -that this assumes that you've already followed the aforementioned -tutorial on setting up your `git` user and its home directory. As a -quick addon to that, I suggest adjusting the permissions for the -`/var/git` directory to 775 (I had initially found they were set to -770.) This allows the Cgit web interface to actually read and display -your hosted repos, which is after all the point. - -Here's what I did. I'm phrasing these in the imperative mood since it -reads better than beginning everything with "I" + past-tense, and the -steps themselves are also suitable as a potential HOWTO for my future -self: - -1. Add two new *external records* for `git.brandonirizarry.xyz` to my - site's DNS configuration over on Epik: the A (IPv4) and AAAA (IPv6) - records, per usual if you're already somewhat familiar with this - thing. - -2. Start out by adding the Nginx configuration of the `http` version - of the site. Not only does this make adding the TLS certificate - later on painless, you can immediately verify that your new - subdomain is, in fact, being hosted. Add the following server block - to your published Nginx configuration, and then reload Nginx - (e.g. `sudo systemctl restart nginx.service`): - -```nginx -server { - listen 80; - listen [::]:80; - - # Replace this with your actual site. - server_name git.example.org; - - root /usr/share/cgit ; - try_files $uri @cgit ; - - location ~ /.+/(info/refs|git-upload-pack) { - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend; - fastcgi_param PATH_INFO $uri; - fastcgi_param GIT_HTTP_EXPORT_ALL 1; - - # This part assumes your git user's home directory is /var/git. - fastcgi_param GIT_PROJECT_ROOT /var/git; - fastcgi_param HOME /var/git; - fastcgi_pass unix:/run/fcgiwrap.socket; - } - - location @cgit { - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; - fastcgi_param PATH_INFO $uri; - fastcgi_param QUERY_STRING $args; - fastcgi_param HTTP_HOST $server_name; - fastcgi_pass unix:/run/fcgiwrap.socket; - } -} -``` - -3. Add a TLS certificate for your subdomain. I admit that I took a - somewhat nonlinear path in achieving my setup, but this should be - as simple as running `sudo certbot --nginx`, and then selecting - your subdomain from the menu options. Here I'm assuming you've - already gotten a certificate for your main site, hence you need a - certificate only for your new subdomain. - -From there you shouldn't even have to restart Nginx: you should see -that your subdomain is available over `https`. - -# Five Strikes and You're Out - -I learned, through banging my head against various misconfigurations -(both from the DNS and Nginx sides), that Let's Encrypt (what Certbot -uses to issue the certificate) [imposes a rate limit](https://letsencrypt.org/docs/rate-limits/#authorization-failures-per-identifier-per-account) on -certificate issues per identifier (five per hour), which doesn't -forgive botched attempts at certificate registration. The solution -here is to run Certbot with the `--test-cert` flag, which uses Let's -Encrypt's staging area, which has a much more forgiving rate limit. - -In digging a bit through the Let's Encrypt [forums](https://community.letsencrypt.org/), I learned -about two super helpful sites for debugging DNS and certificate -issues: - -1. - - Super helpful for figuring out issues with bad certificates. - -2. - - For debugging a site's DNS config, which I was messing up since I - wasn't sure in the beginning how to properly add a subdomain to my - DNS records (somewhat confusingly, Epik places the word - "subdomain" alongside the CNAME section, making me think CNAME had - something to do with it, which it doesn't.) - -# A Ghost in the Machine? - -I finally managed to [host](https://git.brandonirizarry.xyz) my Cgit dashboard on my site, which -currently contains only my blog repo. I even managed to share the link -with a friend of mine, who was successfully able to view it from their -end. - -However, when going through some exercises in *The Go Programming -Language* (a story for another time), I happened to cavalierly make a -GET request to that subdomain, which then reported a TLS error. In my -mind this seemed somewhat bonkers, since, after all, everything was -already up and running, no? So late that evening I had to jump back -onto the VPS and do some bespoke troubleshooting. - -It looked like there were some redundant server blocks in my Nginx -config file that were added while I was throwing everything and the -kitchen sink at getting a valid TLS certificate. So what I did in the -end was remove everything Certbot had added concerning my `git` -subdomain, essentially reverting back to just the server block shown -just above, and repeating those exact steps — including first -verifying service over `http`. This part of the process for me was the -most satisfying, since it proves that the mere act of publishing a -website on the Web is, at its core, not all that difficult. One thing -different this time though was that, per the options Cerbot presents -you, it sufficed to reinstall the existing certificate, as opposed to -applying for a new one.) - -After that, everything was in order! I even checked the site the next -morning just to make sure it had stayed that way. To date, everything -looks good. - -# In the End... - -In the end, I didn't actually solve my initial problem, but still went -down an interesting rabbit hole, and now have a convenient tool at my -disposal — my own poor-man's GitHub — for personal use. For now, I may -well only use it for throwaway Go packages, in case I don't feel like -using workspaces. - - - - - - - - - - diff --git a/drafts/posts/apple-time.md b/drafts/posts/apple-time.md deleted file mode 100644 index e012fc5..0000000 --- a/drafts/posts/apple-time.md +++ /dev/null @@ -1,21 +0,0 @@ -+++ -title = "My first post" -summary = "Stub post for buildablog" -+++ - -# Roses are red - -Violets are blue, etc. -
-```go -package main - -import "fmt" - -func main() { - fmt.Println("Hello world!") -} -``` -
- -[This is a link](https://example.com) diff --git a/drafts/posts/making-cgit-go-gettable.md b/drafts/posts/making-cgit-go-gettable.md deleted file mode 100644 index 0c197ca..0000000 --- a/drafts/posts/making-cgit-go-gettable.md +++ /dev/null @@ -1,101 +0,0 @@ -+++ -title = "Making Cgit Repos Installable as Go Packages " -summary = "Steps I took to configure my private Cgit repo hub to host Go packages." -tags = ["nginx", "cgit", "go"] -date = 2026-03-07 -+++ - -# Putting my private Git server to good use - -In my quest to learn the real ins and outs of Go by going through *The -Go Programming Language*, I decided to use my newly minted private -repo [hub](https://git.brandonirizarry.xyz) to store exercises from the book as packages I can reuse -for later exercises. For example, I decided to make the lissajous -example from Chapter 1 into a separate installable [package](https://git.brandonirizarry.xyz/lissajous). The -lissajous package is used to create a GIF of a [Lissajous curve](https://en.wikipedia.org/wiki/Lissajous_curve), -which was a staple visual effect in old sci-fi movies. - -However, simply "go-getting" from the repo's URL, as you would in the -case of GitHub, isn't that simple. There were multiple hiccups along -the way which I had to overcome. What follows is my best attempt to -piece together what I did to finally enable Go package installation -from my private Git server. Hopefully this account will serve two -purposes: - -1. Set people straight who are looking for answers to this same - question. - -2. Serve as a reference for my future self in case I have to do this - again. - -# Steps - -## Inject the appropriate HTML `meta` tag from Nginx - -This [excellent](https://anirudh.fi/go-get-cgit ) blog post by Anirudh -Oppiliappan set me on the right path for fixing an error I encountered -early on involving a missing `go-import` something-or-other: - - -I ended up putting the `sub_filter` stuff inside the `location @cgit` -block: - -```nginx - location @cgit { - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; - fastcgi_param PATH_INFO $uri; - fastcgi_param QUERY_STRING $args; - fastcgi_param HTTP_HOST $server_name; - fastcgi_pass unix:/run/fcgiwrap.socket; - - # Make our repos go-gettable. - sub_filter '' - ''; - - sub_filter_once on ; - } -``` - -Remembering the semicolons here is important. You can always use `sudo -nginx -t` to test whether your current config is valid. - - -## Configure Git to use SSH for the Git server - -Add this block to your home directory's `.gitconfig` file, making the -appropriate substitution for "example.com": - -```ini -[url "git@example.com:"] - insteadOf = https://git.example.com/ -``` - -Alternatively, you can issue the equivalent command line invocation: - -``` -git config --global url."git@example.com".insteadOf "https://git.example.com/" -``` - -Now, whenever you invoke `go get`, you'll be prompted for your SSH -password. - -Note that, in this case, the `git` in `git@example.com` refers to the -Linux *user* on your remote server that owns the directory containing -all your Git repos. This syntax mirrors the one used when logging into -the same server via SSH, viz. `ssh git@example.com`. - -## Set GOPRIVATE (optional?) - -I'm not sure I need to set this in my case, since AFAICT this has more -to do with close-sourcing code, which isn't my intention here. But I -threw it in just in case. - -Since I want **all** my repos to be potentially installable as Go -packages for now, so I use a glob to indicate that: - -`go env -w GOPRIVATE=git.brandonirizarry.xyz/*` - -Initially, I had set `GOPRIVATE` to point to the `lissajous` repo -only, though this glob technique should work also (and be way easier -to maintain.) diff --git a/drafts/posts/smoothing-over-more-markdown-pain-points.md b/drafts/posts/smoothing-over-more-markdown-pain-points.md deleted file mode 100644 index d9432ac..0000000 --- a/drafts/posts/smoothing-over-more-markdown-pain-points.md +++ /dev/null @@ -1,73 +0,0 @@ -+++ -title = "Smoothing Over More Markdown Pain Points" -tags = ["blogging", "emacs"] -date = 2025-12-05 - -summary = """ - -A post from my old blog about a small Elisp helper library I wrote for \ -generating a table of contents for a Markdown file. - -""" - -+++ - -# I Couldn't Keep It Together - -As I go about editing these blogs as Markdown buffers inside Emacs, -I've been running into a snag of sorts. Previously, I had been -exporting Org to Markdown one way or another. I observed how the -Markdown output inserts an anchor tag above a given section as a way -to link to it from the table of contents. I decided to continue this -practice in my now hand-wrought Markdown. However, manually keeping -the table of contents in sync with changes in the outlining of the -content itself—adding and removing sections, renaming sections, and so -on—is a pain. And so I came up with a way to sync the two, using -Emacs Lisp. Emacs Lisp, or Elisp for short, is the Emacs editor's -extension language: the language you use to write Emacs plugins. - -# Elisp For The Win -[Having written](2025-12-03) about my zany Elisp-based Java build system made -me recall those times: I could once again rise to the challenge, and -solve this new problem with Elisp. That's exactly what I did. I wrote -two functions, `bcimd-generate-toc` and `bcimd-remove-toc`. The first -one regenerates the table of contents based on the current set of -level-1 headings. The second one erases the existing table of -contents, along with the connected anchor tags. It's used by the first -function to start out with a clean slate before defining the new table -of contents. - -I decided to collect these functions into an installable package. It's -currently available through Emacs' version-control installation -mechanisms (for example, `package-vc-install`.) See the [project -README](https://github.com/BrandonIrizarry/bcimd) for more details. - -I find Emacs' VC-based package installation facilities extremely -convenient for writing my own bespoke stuff which I otherwise have to -manage locally. I store it remotely, and install it as an *official* -package, much like how Go packages work. In this way, I can even share -my work with the community. - -# Yet Another Yasnippet Testimonial - -I also decided to go the extra mile and use a [Yasnippet](Yasnippet) snippet -that generates some stock front matter. In particular, the title of a -given blog post is ripped directly from the name of the file itself, -which first undergoes some on-the-fly formatting. I got this idea from -[another blog](https://weblog.masukomi.org/2024/07/19/using-org-mode-with-hugo/) where the author runs with the whole Yasnippet idea -to set up her `ox-hugo` front matter. In fact, this is what turned me -on to the idea of Yasnippet as a useful tool in general; that is, it -isn't just a lazy man's way of inserting a for-loop into source code. - -# Now I Can Keep It Together! - -I now use table-of-contents regeneration frequently: writing the -package was a worthwhile investment of time.The only minor hiccup is -that I have to remember to leave two spaces in between headers, so -that the anchor tag doesn't eliminate all whitespace between sections, -an effect which looks aesthetically jarring. I may address this in the -future, but I first need to see how this package interacts with, for -example, level-2 headers. Other ideas include running -table-of-contents generation as an `after-save-hook`, and eventually -writing a full-blown minor-mode. But for now, I'm taking it easy on -this project: I still have to work on other things. diff --git a/drafts/posts/understanding-pratt-parsing.md b/drafts/posts/understanding-pratt-parsing.md deleted file mode 100644 index d88ba88..0000000 --- a/drafts/posts/understanding-pratt-parsing.md +++ /dev/null @@ -1,155 +0,0 @@ -+++ -title = "Understanding Pratt Parsing" -tags = ["programming languages"] -date = 2025-12-02 - -summary = """ - -A post I had written about Pratt parsing, in the context of a \ -programming language I was designing at the time. - -""" - -+++ - -# Introduction - -I've forgotten how I came across Pratt parsing specifically. I had -been working on an interpreter for a programming language based on the -one loosely described in Greg Michaelson's *An Introduction to -Functional Programming Through Lambda Calculus*. I had managed to -implement simple arithmetic, and even extended the basic lambda -calculus spec with assignment expressions (a feat which I was very -proud of.) - -However, some parts of my implementation felt a bit hacky (for -example, how I had implemented `letrec`), and my implementation of -lazy evaluation, while mostly complete, ultimately turned out to be -buggy. - -At first, I decided to rewrite the project from scratch. One major -guiding factor was to narrow the scope of the project, borrowing some -advice from [Zed Shaw](https://learncodethehardway.com/blog/32-very-deep-not-boring-beginner-projects/). I got around to writing a new tokenizer, -and then I was on to writing the parser. My initial parser used the -recursive descent technique, taking advantage of the simplified lambda -calculus grammar used in Michaelson's text (for example, parentheses -are always used for application terms there.) - -This time though, I wanted to try something different. And so, -rummaging through the internets, I stumbled across Pratt parsing. - -# "It's like a burrito" - -Understanding Pratt parsing ended up being much harder than I -expected. I ended up searching through a bunch of examples online: - -1. [Alex Kladov's](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html) Rust-based tutorial. -2. [Eli Bendersky's](https://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing) Python-based tutorial. -3. Bob Nystrom's [intro](https://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/) to the subject, as well as the relevant - chapter in his [Crafting Interpreters](https://craftinginterpreters.com/compiling-expressions.html). -4. Vaughan Pratt's [original paper](https://tdop.github.io/). Shout-out to the legend who - put this up as a GitHub Pages site! -5. Douglas Crockford's celebrated [article](https://crockford.com/javascript/tdop/tdop.html) on the subject deserves - honorable mention, though my JavaScript is currently rusty and so I - didn't look into it in any depth. - -Alex Kladov in his post calls Pratt parsing the "monad tutorial of -syntactic analysis". As I had recently become familiar with the -concept of "monad" in a [philosophical](https://en.wikipedia.org/wiki/Monad_(philosophy)) sense, I aksed ChatGPT -where the reference comes from: it turns out that it's an inside joke -involving *Haskell* monads. I'm not an expert, but my readings have -given me enough of an inkling to see the connection: Pratt parsing -isn't a discrete "thing" with exactly one shape: it's more of a -technique, if you will—a design pattern—which can assume various -manifestations. - -A good example of this is iteration, because we all recognize it when -we see it, even when we don't know the language—but no one syntactic -construction sufficiently defines it. For-loops, while-loops, Python -generator functions, and [optimized tail-recursive functions](https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/full-text/book/book-Z-H-11.html#%_sec_1.2.1 ) all -count as iteration. - -After struggling for some time, I finally managed to distill the -essence of the algorithm, which I present here as pseudocode: - -``` -parse(level): - t ← next(stream) - acc ← nud_dispatch(t) - - while level < precedence(peek(stream)): - t ← next(stream) - acc ← led_dispatch(t, acc) - - return acc -``` - -Unwrapping what this does exactly is a surprisingly nuanced task, -precisely because the algorithm is more about technique than -structure. Because of this, I won't pretend to be up to the task -here. Nevertheless, a brief synopsis is warranted. - -There is a global `stream` of tokens, such that `next(stream)` -consumes and returns the next token, and `peek(stream)` returns the -next token without consuming it. The function `nud_dispatch` -interprets `t` as a *null denotation*, or "nud" for short, and -initializes `acc`. The function `led_dispatch` interprets `t` as a -*left denotation*, or "led" for short. It accumulates a value into -`acc` using the existing value of `acc` (hence the choice of name.) -Both dispatch functions call `parse` recursively in all but the most -trivial cases. - -When `peek`ing the stream reveals a token with higher precedence than -the current `level`, the while loop exits and `acc` is returned. - -The algorithm is initialized by calling `parse(0)`. - -# Down To Brass Tacks - -My approach was to take Eli Bendersky's full source code at the bottom -of his post, and start chiseling away at it. What I ended up with was -the same simple arithmetic calculator, only with a different -architecture: I moved away from Eli's object-oriented approach towards -something closer to the formulation given in the previous section. - -In the end, I was amazed at how simple and robust the actual -implementation turned out to be! I feel that what I came up with (at -this stage, anyway) is arguably simpler than even many of the examples -I initially came across: for example, it isn't necessary to add space -between precedence levels (10, 20, etc.), since you can use an enum to -take care of any ordering needed. Also, using even and odd precedence -levels (for handling right associativity) is unnecessary. For example, -say you have precedence levels `MULTIPLICATION = 2` and -`EXPONENTIATION = 3`. The algorithm cleverly avoids clashing -`EXPONENTIATION-1` with `MULTIPLICATION` when enforcing right -associativity for exponentiation. I found this to be one of the more -remarkable aspects of the algorithm. - -# Wanting More - -To be fair, my calculator app technically doesn't parse arithmetic -expressions: it evaluates them wholesale. This is OK: instead of -accumulating an AST, I'm accumulating an arithmetic result. - -Because of how compelling the calculator app turned out to be, I -decided to stop work on the lambda calculus project, and instead work -on expanding the calculator into a full-blown programming language, -albeit a simple one. I've already made progress in this direction: in -addition to arithmetic (including trig functions!), the application -currently supports variable assignment. - -Ideally, I'd like something with -

- -+ Booleans -+ Conditionals -+ Loops -+ Functions -+ Proper lexical scoping, even for conditional and loop blocks -

-I'll see how many of these I manage. - - - - - diff --git a/drafts/posts/writing-my-blog-with-eleventy.md b/drafts/posts/writing-my-blog-with-eleventy.md deleted file mode 100644 index dc78e51..0000000 --- a/drafts/posts/writing-my-blog-with-eleventy.md +++ /dev/null @@ -1,90 +0,0 @@ -+++ -title = "Writing My Blog With Eleventy" -tags = ["blogging"] -date = 2025-12-03 - -summary = """ - -This is a reproduction of a post I had on my old blog, which I've \ -since migrated to a custom engine. - -""" - -+++ - -# Introduction - -This is *at least* my third time trying to start a blog. - -First, I experimented with using Org Mode's HTML exporting feature to -create posts; unfortunately, that didn't get me far, though there are -some [interesting attempts](https://one.tonyaldon.com/) by others to this end. I might've -published this material at some point, but at any rate it didn't stay -up long. An early topic from this time include a post about a [Java -build system](https://github.com/BrandonIrizarry/Hydraulic-Make) I once wrote that scanned a `.java` file for its -dependencies (defined by things like package imports and code syntax), -so that those would get passed into `javac` along with the target -file. - -# Hugo -I then started writing a blog using Hugo. Hugo was my first encounter -with an SSG. Because of this, I was a bit impatient with Hugo, and hit -a wall every time I came across any sort of complexity. I also got -frustrated with how themes never follow a consistent template; each -does something different, with different elements, and so each one -effectively has different rules. In the end, I published a blog post -or two on GitHub pages using this setup. It was passable, but in the -end configuring it still felt wonky and cargo-culted. - -Another reason for why I didn't have success with Hugo was my use of -`ox-hugo`. It's a fun package, and you can tell the author put a *lot* -of love into it. However, using Org Mode as a middleman between you -and Hugo obfuscates the nature of Hugo, something I'm realizing now as -I go deeper into using Eleventy. - - -# Eleventy: The Soup Actually Tastes Good - -I went ahead and did a little bit of "shopping" for SSGs. I ran into -[Eleventy](https://www.11ty.dev). I watched the author's [intro video](https://www.youtube.com/watch?v=kzf9A9tkkl4), and -immediately took a liking to it. After a few false starts, I cloned -their [official starter project](https://github.com/11ty/eleventy-base-blog), tweaked it here and there, and -the rest is what you're currently looking at. - -A huge shift in my thinking which made the leap from Hugo to Eleventy -possible occurred when I learned to stop worrying and love the -Markdown. - -I used to think of Markdown as an icky, second-rate version of Org -Mode. Then, I eventually got the hang of writing Markdown using Emacs' -`markdown-mode` package, which is a [masterpiece](https://jblevins.org/projects/markdown-mode/) of a plugin: it -makes the experience of writing Markdown rival that of using Org, and -smoothes out a lot of Markdown's pain points (significant whitespace, -noisy links, etc.) And so I slowly let go of the attachment of using -Org Mode in all the things, and embraced the idea of writing blog -posts directly in Markdown; this also alleviated the complexity of -sundry issues arising from exporting from Org to Markdown. - -At first, Eleventy looks like a deceptively complex pile of language -soup: JS, Markdown, templating languages, HTML, and CSS—at times all -occurring within the same file—all somehow live under one -roof. However, tweaking the starter project ended up being a -relatively easy, even pleasant experience. - -# Painless Deployment - -Even deployment is simple. This site's content is version-controlled -locally. I then build the site, then simply `scp` the `_site` -directory to the appropriate directory in my VPS, where this blog is -hosted. The previous remote `_site` directory is simply overwritten -with the new files. I don't need a GitHub workflow, as I did when -using Hugo with GitHub pages; I don't even need to push to a remote -repo. Copying the files suffices. - -# Conclusion - -On the one hand, I'm nowhere near able to make something like the -starter project from scratch. On the other hand, neither am I -daunted. Eleventy in a sense reminds me of Emacs, in that there's a -certain joy to be found in its eclectic complexity. I look forward to -continue using Eleventy as I grow this blog. diff --git a/drafts/projects/buildablog.md b/drafts/projects/buildablog.md deleted file mode 100644 index f8d534a..0000000 --- a/drafts/projects/buildablog.md +++ /dev/null @@ -1,109 +0,0 @@ -+++ -name = "buildablog" -title = "Building My Own SSG" -host_url = "https://github.com/BrandonIrizarry/buildablog" -synopsis = "The SSG used to build this site." -stack = ["Go", "HTML", "CSS"] -thumbnail = "assets/github-white.svg" -date = 2026-03-01 -+++ - -# Motivation - -While looking for options to host my blog, and having tried Hugo and -Eleventy, I decided to write my own SSG. - -I had completed the [Build a Static Site Generator in Python](https://www.boot.dev/courses/build-static-site-generator-python) -course on boot.dev, and so already had some inkling of what's involved -here. That course requires the learner to manually parse Markdown into -HTML; in my project (which uses Go for the backend), Markdown parsing -is forwarded over to the [frontmatter](https://github.com/adrg/frontmatter) and [goldmark](https://github.com/yuin/goldmark) libraries -(with help from the [goldmark-highlighting](https://github.com/yuin/goldmark-highlighting/v2) extension library for -prettifying code listings.) - -Basically, writing my own SSG from scratch felt like it was going to -be at most not that much harder than learning the ins and outs of -configuring an existing SSG like Hugo or Eleventy. It would also in -the end allow me to configure my site at the atomic level, so to -speak - I write and maintain the server endpoints, the HTML Go -templates, the CSS styling, you get the idea. - -Using Go templates can be daunting at first, but Section 4.6 of of -*The Go Programming Language* — Text and HTML Templates — helps. Jon -Calhoun has a good [mini course](https://www.calhoun.io/intro-to-templates/) on templates, which I had already -completed in its entirety when I needed knowledge of templates for a -previous project I completed, called [Juices](https://github.com/BrandonIrizarry/Juices). Another thing I -learned while working on my SSG is that template parsing should not be -done when serving an endpoint, since this creates a significant -performance bottleneck. - -Generics also came in handy when I wanted to reuse the exact same code -for differing frontmatter layouts (e.g., project posts like this one, -versus ordinary blog posts.) - -# RSS - -Setting up RSS for my site (once you know the tricks) turns out to be -surprisingly straightforward, especially with Go's ability to marshal -all things structured (XML, TOML, JSON, etc.) to and from itself. At -one point I was torn between using either Atom or RSS for my feed, -since the former is [touted](https://nullprogram.com/blog/2013/09/23/) as strictly superior to the latter; in -the end I decided that RSS is good enough to get things started for -now. - -I did face a last-minute curveball when I realized I had to integrate -the *projects* listing to the RSS feed. To this end, I needed a -reliable way to sort RSS item fields by date, which necessitated some -beefing up of the rss package itself. - -Two notable resources for learning about interfacing RSS with Go code -are: - -1. [Create an RSS Feed (WikiHow)](https://www.wikihow.com/Create-an-RSS-Feed) - Super helpful for getting the details of the layout right. - -2. [Build Your Own RSS Feed Generator in Go (YouTube)](https://www.youtube.com/watch?v=b2E1JpC38Pg ) - Helpful for understanding the gory details of what the acutal - marshalling logic looks like. - -With regard to RSS, I feel *all* blogs should have an RSS/Atom -feed. There are some fantastic ones out there that don't! - -When setting up RSS, I finally grokked something while reading a [blog -post](https://pluralistic.net/2024/10/16/keep-it-really-simple-stupid/) by Cory Doctorow where he advocates for RSS: - -I realized that **RSS is a genre of social media.** - -# Sources of Inspiration - -I took (and continue to take) inspiration from blogs I've seen in the -wild, such as: - -1. - - I ripped off a lot of CSS (and general design decisions) from this - site. 😁 The great thing about this site is that it proves that you - can be both a highly intelligent and original thinker without being - a techie conformist or all-around hipster with regard to things - like site aesthetic, opinions, and so on. Such concerns create - performance anxiety which get in the way of you actually making - something *good*. - - As a shout out, the author even gives some quick lessons on setting - up a blog and writing the HTML and CSS for a simple website (which - I also took pointers from! This guy probably did a better job of - explaining what `width: 100%;` means than a lot of resources I've - seen online.) - -2. - - For pointers on how to establish a convention for blog post - slugs. This author uses the date-based scheme, which I adopted in - the end. - -3. - - For general compactness of aesthetic. While I don't plan on going - that route, the nod to S-expression syntax is nice. - - diff --git a/index.md b/index.md deleted file mode 100644 index f9b5850..0000000 --- a/index.md +++ /dev/null @@ -1,9 +0,0 @@ -+++ -title = "Brandon's Blog" -summary = "My website's front page." -+++ - -Welcome to my site! - -I hope to document my progress in various technical fields and hobbies -in a meaningful and edifying way. diff --git a/index/index.md b/index/index.md new file mode 100644 index 0000000..f9b5850 --- /dev/null +++ b/index/index.md @@ -0,0 +1,9 @@ ++++ +title = "Brandon's Blog" +summary = "My website's front page." ++++ + +Welcome to my site! + +I hope to document my progress in various technical fields and hobbies +in a meaningful and edifying way. diff --git a/posts/adding-cgit-subdomain-to-personal-site.md b/posts/adding-cgit-subdomain-to-personal-site.md new file mode 100644 index 0000000..c62fde8 --- /dev/null +++ b/posts/adding-cgit-subdomain-to-personal-site.md @@ -0,0 +1,195 @@ ++++ +title = "Adding a CGit Subdomain To My Site" +tags = ["linux", "nginx", "certbot", "cgit"] +summary = "Setting up CGit on my VPS." +date = 2026-03-06 ++++ + +# Motivation + +To update the content of my blog, I have to do something of a +dance. My blog's content started out life as a subdirectory of my SSG +project, but now lives in its own separate Git repo. This is super +convenient since I now can host my blog wherever I want on my VPS's +filesystem, for example in a folder called `brandons_blog`. So right +now what I'm doing is this: + +1. Commit all changes. +2. Push the changes to GitHub, where it's currently hosted in a + somewhat centralized manner. +3. Log in via SSH into my VPS. +4. Perform a `cd` into the `brandons_blog` directory, and run `git + pull`. + +However, I thought, "wouldn't it be nice if I could push **directly** +to the VPS repo?" And so I started working on that idea in the obvious +manner: add the VPS repo as a remote, such that I would be pushing to +`vps/main` alongside of `origin/main` (where `origin` points to +GitHub). + +It turns out that this alone is a shade more complicated that it would +seem: I had to first add a new `git` user (see [this tutorial](https://landchad.net/git/)), +and then adjust my SSH configuration appropriately to allow for this +pseudo-user to log in via SSH (since I would be pushing into a +directory now owned by it.) + +I quickly learned, to my dismay, that I couldn't push to the VPS +remote if it's not a bare repo. A bare repo is one initialized with +`git init --bare`, so that it doesn't have a working directory +populated with files. However, the buildablog server expects to see a +working directory with blog files (not just blobs, for example), so +this doesn't solve my problem. + +# Cgit + +What I ended up doing in the end didn't solve this problem, but it +ended up becoming an interesting rabbit hole in its own right. + +I ended up adding the Cgit web interface to my site, available via +. I checked out a [tutorial](https://landchad.net/cgit/) on +how to do it, but their suggested Nginx setup was off in some +parts. + +After a ton of false starts, I ended up doing slightly different. Note +that this assumes that you've already followed the aforementioned +tutorial on setting up your `git` user and its home directory. As a +quick addon to that, I suggest adjusting the permissions for the +`/var/git` directory to 775 (I had initially found they were set to +770.) This allows the Cgit web interface to actually read and display +your hosted repos, which is after all the point. + +Here's what I did. I'm phrasing these in the imperative mood since it +reads better than beginning everything with "I" + past-tense, and the +steps themselves are also suitable as a potential HOWTO for my future +self: + +1. Add two new *external records* for `git.brandonirizarry.xyz` to my + site's DNS configuration over on Epik: the A (IPv4) and AAAA (IPv6) + records, per usual if you're already somewhat familiar with this + thing. + +2. Start out by adding the Nginx configuration of the `http` version + of the site. Not only does this make adding the TLS certificate + later on painless, you can immediately verify that your new + subdomain is, in fact, being hosted. Add the following server block + to your published Nginx configuration, and then reload Nginx + (e.g. `sudo systemctl restart nginx.service`): + +```nginx +server { + listen 80; + listen [::]:80; + + # Replace this with your actual site. + server_name git.example.org; + + root /usr/share/cgit ; + try_files $uri @cgit ; + + location ~ /.+/(info/refs|git-upload-pack) { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend; + fastcgi_param PATH_INFO $uri; + fastcgi_param GIT_HTTP_EXPORT_ALL 1; + + # This part assumes your git user's home directory is /var/git. + fastcgi_param GIT_PROJECT_ROOT /var/git; + fastcgi_param HOME /var/git; + fastcgi_pass unix:/run/fcgiwrap.socket; + } + + location @cgit { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; + fastcgi_param PATH_INFO $uri; + fastcgi_param QUERY_STRING $args; + fastcgi_param HTTP_HOST $server_name; + fastcgi_pass unix:/run/fcgiwrap.socket; + } +} +``` + +3. Add a TLS certificate for your subdomain. I admit that I took a + somewhat nonlinear path in achieving my setup, but this should be + as simple as running `sudo certbot --nginx`, and then selecting + your subdomain from the menu options. Here I'm assuming you've + already gotten a certificate for your main site, hence you need a + certificate only for your new subdomain. + +From there you shouldn't even have to restart Nginx: you should see +that your subdomain is available over `https`. + +# Five Strikes and You're Out + +I learned, through banging my head against various misconfigurations +(both from the DNS and Nginx sides), that Let's Encrypt (what Certbot +uses to issue the certificate) [imposes a rate limit](https://letsencrypt.org/docs/rate-limits/#authorization-failures-per-identifier-per-account) on +certificate issues per identifier (five per hour), which doesn't +forgive botched attempts at certificate registration. The solution +here is to run Certbot with the `--test-cert` flag, which uses Let's +Encrypt's staging area, which has a much more forgiving rate limit. + +In digging a bit through the Let's Encrypt [forums](https://community.letsencrypt.org/), I learned +about two super helpful sites for debugging DNS and certificate +issues: + +1. + + Super helpful for figuring out issues with bad certificates. + +2. + + For debugging a site's DNS config, which I was messing up since I + wasn't sure in the beginning how to properly add a subdomain to my + DNS records (somewhat confusingly, Epik places the word + "subdomain" alongside the CNAME section, making me think CNAME had + something to do with it, which it doesn't.) + +# A Ghost in the Machine? + +I finally managed to [host](https://git.brandonirizarry.xyz) my Cgit dashboard on my site, which +currently contains only my blog repo. I even managed to share the link +with a friend of mine, who was successfully able to view it from their +end. + +However, when going through some exercises in *The Go Programming +Language* (a story for another time), I happened to cavalierly make a +GET request to that subdomain, which then reported a TLS error. In my +mind this seemed somewhat bonkers, since, after all, everything was +already up and running, no? So late that evening I had to jump back +onto the VPS and do some bespoke troubleshooting. + +It looked like there were some redundant server blocks in my Nginx +config file that were added while I was throwing everything and the +kitchen sink at getting a valid TLS certificate. So what I did in the +end was remove everything Certbot had added concerning my `git` +subdomain, essentially reverting back to just the server block shown +just above, and repeating those exact steps — including first +verifying service over `http`. This part of the process for me was the +most satisfying, since it proves that the mere act of publishing a +website on the Web is, at its core, not all that difficult. One thing +different this time though was that, per the options Cerbot presents +you, it sufficed to reinstall the existing certificate, as opposed to +applying for a new one.) + +After that, everything was in order! I even checked the site the next +morning just to make sure it had stayed that way. To date, everything +looks good. + +# In the End... + +In the end, I didn't actually solve my initial problem, but still went +down an interesting rabbit hole, and now have a convenient tool at my +disposal — my own poor-man's GitHub — for personal use. For now, I may +well only use it for throwaway Go packages, in case I don't feel like +using workspaces. + + + + + + + + + + diff --git a/posts/apple-time.md b/posts/apple-time.md new file mode 100644 index 0000000..e012fc5 --- /dev/null +++ b/posts/apple-time.md @@ -0,0 +1,21 @@ ++++ +title = "My first post" +summary = "Stub post for buildablog" ++++ + +# Roses are red + +Violets are blue, etc. +
+```go +package main + +import "fmt" + +func main() { + fmt.Println("Hello world!") +} +``` +
+ +[This is a link](https://example.com) diff --git a/posts/making-cgit-go-gettable.md b/posts/making-cgit-go-gettable.md new file mode 100644 index 0000000..0c197ca --- /dev/null +++ b/posts/making-cgit-go-gettable.md @@ -0,0 +1,101 @@ ++++ +title = "Making Cgit Repos Installable as Go Packages " +summary = "Steps I took to configure my private Cgit repo hub to host Go packages." +tags = ["nginx", "cgit", "go"] +date = 2026-03-07 ++++ + +# Putting my private Git server to good use + +In my quest to learn the real ins and outs of Go by going through *The +Go Programming Language*, I decided to use my newly minted private +repo [hub](https://git.brandonirizarry.xyz) to store exercises from the book as packages I can reuse +for later exercises. For example, I decided to make the lissajous +example from Chapter 1 into a separate installable [package](https://git.brandonirizarry.xyz/lissajous). The +lissajous package is used to create a GIF of a [Lissajous curve](https://en.wikipedia.org/wiki/Lissajous_curve), +which was a staple visual effect in old sci-fi movies. + +However, simply "go-getting" from the repo's URL, as you would in the +case of GitHub, isn't that simple. There were multiple hiccups along +the way which I had to overcome. What follows is my best attempt to +piece together what I did to finally enable Go package installation +from my private Git server. Hopefully this account will serve two +purposes: + +1. Set people straight who are looking for answers to this same + question. + +2. Serve as a reference for my future self in case I have to do this + again. + +# Steps + +## Inject the appropriate HTML `meta` tag from Nginx + +This [excellent](https://anirudh.fi/go-get-cgit ) blog post by Anirudh +Oppiliappan set me on the right path for fixing an error I encountered +early on involving a missing `go-import` something-or-other: + + +I ended up putting the `sub_filter` stuff inside the `location @cgit` +block: + +```nginx + location @cgit { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; + fastcgi_param PATH_INFO $uri; + fastcgi_param QUERY_STRING $args; + fastcgi_param HTTP_HOST $server_name; + fastcgi_pass unix:/run/fcgiwrap.socket; + + # Make our repos go-gettable. + sub_filter '' + ''; + + sub_filter_once on ; + } +``` + +Remembering the semicolons here is important. You can always use `sudo +nginx -t` to test whether your current config is valid. + + +## Configure Git to use SSH for the Git server + +Add this block to your home directory's `.gitconfig` file, making the +appropriate substitution for "example.com": + +```ini +[url "git@example.com:"] + insteadOf = https://git.example.com/ +``` + +Alternatively, you can issue the equivalent command line invocation: + +``` +git config --global url."git@example.com".insteadOf "https://git.example.com/" +``` + +Now, whenever you invoke `go get`, you'll be prompted for your SSH +password. + +Note that, in this case, the `git` in `git@example.com` refers to the +Linux *user* on your remote server that owns the directory containing +all your Git repos. This syntax mirrors the one used when logging into +the same server via SSH, viz. `ssh git@example.com`. + +## Set GOPRIVATE (optional?) + +I'm not sure I need to set this in my case, since AFAICT this has more +to do with close-sourcing code, which isn't my intention here. But I +threw it in just in case. + +Since I want **all** my repos to be potentially installable as Go +packages for now, so I use a glob to indicate that: + +`go env -w GOPRIVATE=git.brandonirizarry.xyz/*` + +Initially, I had set `GOPRIVATE` to point to the `lissajous` repo +only, though this glob technique should work also (and be way easier +to maintain.) diff --git a/posts/smoothing-over-more-markdown-pain-points.md b/posts/smoothing-over-more-markdown-pain-points.md new file mode 100644 index 0000000..d9432ac --- /dev/null +++ b/posts/smoothing-over-more-markdown-pain-points.md @@ -0,0 +1,73 @@ ++++ +title = "Smoothing Over More Markdown Pain Points" +tags = ["blogging", "emacs"] +date = 2025-12-05 + +summary = """ + +A post from my old blog about a small Elisp helper library I wrote for \ +generating a table of contents for a Markdown file. + +""" + ++++ + +# I Couldn't Keep It Together + +As I go about editing these blogs as Markdown buffers inside Emacs, +I've been running into a snag of sorts. Previously, I had been +exporting Org to Markdown one way or another. I observed how the +Markdown output inserts an anchor tag above a given section as a way +to link to it from the table of contents. I decided to continue this +practice in my now hand-wrought Markdown. However, manually keeping +the table of contents in sync with changes in the outlining of the +content itself—adding and removing sections, renaming sections, and so +on—is a pain. And so I came up with a way to sync the two, using +Emacs Lisp. Emacs Lisp, or Elisp for short, is the Emacs editor's +extension language: the language you use to write Emacs plugins. + +# Elisp For The Win +[Having written](2025-12-03) about my zany Elisp-based Java build system made +me recall those times: I could once again rise to the challenge, and +solve this new problem with Elisp. That's exactly what I did. I wrote +two functions, `bcimd-generate-toc` and `bcimd-remove-toc`. The first +one regenerates the table of contents based on the current set of +level-1 headings. The second one erases the existing table of +contents, along with the connected anchor tags. It's used by the first +function to start out with a clean slate before defining the new table +of contents. + +I decided to collect these functions into an installable package. It's +currently available through Emacs' version-control installation +mechanisms (for example, `package-vc-install`.) See the [project +README](https://github.com/BrandonIrizarry/bcimd) for more details. + +I find Emacs' VC-based package installation facilities extremely +convenient for writing my own bespoke stuff which I otherwise have to +manage locally. I store it remotely, and install it as an *official* +package, much like how Go packages work. In this way, I can even share +my work with the community. + +# Yet Another Yasnippet Testimonial + +I also decided to go the extra mile and use a [Yasnippet](Yasnippet) snippet +that generates some stock front matter. In particular, the title of a +given blog post is ripped directly from the name of the file itself, +which first undergoes some on-the-fly formatting. I got this idea from +[another blog](https://weblog.masukomi.org/2024/07/19/using-org-mode-with-hugo/) where the author runs with the whole Yasnippet idea +to set up her `ox-hugo` front matter. In fact, this is what turned me +on to the idea of Yasnippet as a useful tool in general; that is, it +isn't just a lazy man's way of inserting a for-loop into source code. + +# Now I Can Keep It Together! + +I now use table-of-contents regeneration frequently: writing the +package was a worthwhile investment of time.The only minor hiccup is +that I have to remember to leave two spaces in between headers, so +that the anchor tag doesn't eliminate all whitespace between sections, +an effect which looks aesthetically jarring. I may address this in the +future, but I first need to see how this package interacts with, for +example, level-2 headers. Other ideas include running +table-of-contents generation as an `after-save-hook`, and eventually +writing a full-blown minor-mode. But for now, I'm taking it easy on +this project: I still have to work on other things. diff --git a/posts/understanding-pratt-parsing.md b/posts/understanding-pratt-parsing.md new file mode 100644 index 0000000..d88ba88 --- /dev/null +++ b/posts/understanding-pratt-parsing.md @@ -0,0 +1,155 @@ ++++ +title = "Understanding Pratt Parsing" +tags = ["programming languages"] +date = 2025-12-02 + +summary = """ + +A post I had written about Pratt parsing, in the context of a \ +programming language I was designing at the time. + +""" + ++++ + +# Introduction + +I've forgotten how I came across Pratt parsing specifically. I had +been working on an interpreter for a programming language based on the +one loosely described in Greg Michaelson's *An Introduction to +Functional Programming Through Lambda Calculus*. I had managed to +implement simple arithmetic, and even extended the basic lambda +calculus spec with assignment expressions (a feat which I was very +proud of.) + +However, some parts of my implementation felt a bit hacky (for +example, how I had implemented `letrec`), and my implementation of +lazy evaluation, while mostly complete, ultimately turned out to be +buggy. + +At first, I decided to rewrite the project from scratch. One major +guiding factor was to narrow the scope of the project, borrowing some +advice from [Zed Shaw](https://learncodethehardway.com/blog/32-very-deep-not-boring-beginner-projects/). I got around to writing a new tokenizer, +and then I was on to writing the parser. My initial parser used the +recursive descent technique, taking advantage of the simplified lambda +calculus grammar used in Michaelson's text (for example, parentheses +are always used for application terms there.) + +This time though, I wanted to try something different. And so, +rummaging through the internets, I stumbled across Pratt parsing. + +# "It's like a burrito" + +Understanding Pratt parsing ended up being much harder than I +expected. I ended up searching through a bunch of examples online: + +1. [Alex Kladov's](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html) Rust-based tutorial. +2. [Eli Bendersky's](https://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing) Python-based tutorial. +3. Bob Nystrom's [intro](https://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/) to the subject, as well as the relevant + chapter in his [Crafting Interpreters](https://craftinginterpreters.com/compiling-expressions.html). +4. Vaughan Pratt's [original paper](https://tdop.github.io/). Shout-out to the legend who + put this up as a GitHub Pages site! +5. Douglas Crockford's celebrated [article](https://crockford.com/javascript/tdop/tdop.html) on the subject deserves + honorable mention, though my JavaScript is currently rusty and so I + didn't look into it in any depth. + +Alex Kladov in his post calls Pratt parsing the "monad tutorial of +syntactic analysis". As I had recently become familiar with the +concept of "monad" in a [philosophical](https://en.wikipedia.org/wiki/Monad_(philosophy)) sense, I aksed ChatGPT +where the reference comes from: it turns out that it's an inside joke +involving *Haskell* monads. I'm not an expert, but my readings have +given me enough of an inkling to see the connection: Pratt parsing +isn't a discrete "thing" with exactly one shape: it's more of a +technique, if you will—a design pattern—which can assume various +manifestations. + +A good example of this is iteration, because we all recognize it when +we see it, even when we don't know the language—but no one syntactic +construction sufficiently defines it. For-loops, while-loops, Python +generator functions, and [optimized tail-recursive functions](https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/full-text/book/book-Z-H-11.html#%_sec_1.2.1 ) all +count as iteration. + +After struggling for some time, I finally managed to distill the +essence of the algorithm, which I present here as pseudocode: + +``` +parse(level): + t ← next(stream) + acc ← nud_dispatch(t) + + while level < precedence(peek(stream)): + t ← next(stream) + acc ← led_dispatch(t, acc) + + return acc +``` + +Unwrapping what this does exactly is a surprisingly nuanced task, +precisely because the algorithm is more about technique than +structure. Because of this, I won't pretend to be up to the task +here. Nevertheless, a brief synopsis is warranted. + +There is a global `stream` of tokens, such that `next(stream)` +consumes and returns the next token, and `peek(stream)` returns the +next token without consuming it. The function `nud_dispatch` +interprets `t` as a *null denotation*, or "nud" for short, and +initializes `acc`. The function `led_dispatch` interprets `t` as a +*left denotation*, or "led" for short. It accumulates a value into +`acc` using the existing value of `acc` (hence the choice of name.) +Both dispatch functions call `parse` recursively in all but the most +trivial cases. + +When `peek`ing the stream reveals a token with higher precedence than +the current `level`, the while loop exits and `acc` is returned. + +The algorithm is initialized by calling `parse(0)`. + +# Down To Brass Tacks + +My approach was to take Eli Bendersky's full source code at the bottom +of his post, and start chiseling away at it. What I ended up with was +the same simple arithmetic calculator, only with a different +architecture: I moved away from Eli's object-oriented approach towards +something closer to the formulation given in the previous section. + +In the end, I was amazed at how simple and robust the actual +implementation turned out to be! I feel that what I came up with (at +this stage, anyway) is arguably simpler than even many of the examples +I initially came across: for example, it isn't necessary to add space +between precedence levels (10, 20, etc.), since you can use an enum to +take care of any ordering needed. Also, using even and odd precedence +levels (for handling right associativity) is unnecessary. For example, +say you have precedence levels `MULTIPLICATION = 2` and +`EXPONENTIATION = 3`. The algorithm cleverly avoids clashing +`EXPONENTIATION-1` with `MULTIPLICATION` when enforcing right +associativity for exponentiation. I found this to be one of the more +remarkable aspects of the algorithm. + +# Wanting More + +To be fair, my calculator app technically doesn't parse arithmetic +expressions: it evaluates them wholesale. This is OK: instead of +accumulating an AST, I'm accumulating an arithmetic result. + +Because of how compelling the calculator app turned out to be, I +decided to stop work on the lambda calculus project, and instead work +on expanding the calculator into a full-blown programming language, +albeit a simple one. I've already made progress in this direction: in +addition to arithmetic (including trig functions!), the application +currently supports variable assignment. + +Ideally, I'd like something with +

+ ++ Booleans ++ Conditionals ++ Loops ++ Functions ++ Proper lexical scoping, even for conditional and loop blocks +

+I'll see how many of these I manage. + + + + + diff --git a/posts/writing-my-blog-with-eleventy.md b/posts/writing-my-blog-with-eleventy.md new file mode 100644 index 0000000..dc78e51 --- /dev/null +++ b/posts/writing-my-blog-with-eleventy.md @@ -0,0 +1,90 @@ ++++ +title = "Writing My Blog With Eleventy" +tags = ["blogging"] +date = 2025-12-03 + +summary = """ + +This is a reproduction of a post I had on my old blog, which I've \ +since migrated to a custom engine. + +""" + ++++ + +# Introduction + +This is *at least* my third time trying to start a blog. + +First, I experimented with using Org Mode's HTML exporting feature to +create posts; unfortunately, that didn't get me far, though there are +some [interesting attempts](https://one.tonyaldon.com/) by others to this end. I might've +published this material at some point, but at any rate it didn't stay +up long. An early topic from this time include a post about a [Java +build system](https://github.com/BrandonIrizarry/Hydraulic-Make) I once wrote that scanned a `.java` file for its +dependencies (defined by things like package imports and code syntax), +so that those would get passed into `javac` along with the target +file. + +# Hugo +I then started writing a blog using Hugo. Hugo was my first encounter +with an SSG. Because of this, I was a bit impatient with Hugo, and hit +a wall every time I came across any sort of complexity. I also got +frustrated with how themes never follow a consistent template; each +does something different, with different elements, and so each one +effectively has different rules. In the end, I published a blog post +or two on GitHub pages using this setup. It was passable, but in the +end configuring it still felt wonky and cargo-culted. + +Another reason for why I didn't have success with Hugo was my use of +`ox-hugo`. It's a fun package, and you can tell the author put a *lot* +of love into it. However, using Org Mode as a middleman between you +and Hugo obfuscates the nature of Hugo, something I'm realizing now as +I go deeper into using Eleventy. + + +# Eleventy: The Soup Actually Tastes Good + +I went ahead and did a little bit of "shopping" for SSGs. I ran into +[Eleventy](https://www.11ty.dev). I watched the author's [intro video](https://www.youtube.com/watch?v=kzf9A9tkkl4), and +immediately took a liking to it. After a few false starts, I cloned +their [official starter project](https://github.com/11ty/eleventy-base-blog), tweaked it here and there, and +the rest is what you're currently looking at. + +A huge shift in my thinking which made the leap from Hugo to Eleventy +possible occurred when I learned to stop worrying and love the +Markdown. + +I used to think of Markdown as an icky, second-rate version of Org +Mode. Then, I eventually got the hang of writing Markdown using Emacs' +`markdown-mode` package, which is a [masterpiece](https://jblevins.org/projects/markdown-mode/) of a plugin: it +makes the experience of writing Markdown rival that of using Org, and +smoothes out a lot of Markdown's pain points (significant whitespace, +noisy links, etc.) And so I slowly let go of the attachment of using +Org Mode in all the things, and embraced the idea of writing blog +posts directly in Markdown; this also alleviated the complexity of +sundry issues arising from exporting from Org to Markdown. + +At first, Eleventy looks like a deceptively complex pile of language +soup: JS, Markdown, templating languages, HTML, and CSS—at times all +occurring within the same file—all somehow live under one +roof. However, tweaking the starter project ended up being a +relatively easy, even pleasant experience. + +# Painless Deployment + +Even deployment is simple. This site's content is version-controlled +locally. I then build the site, then simply `scp` the `_site` +directory to the appropriate directory in my VPS, where this blog is +hosted. The previous remote `_site` directory is simply overwritten +with the new files. I don't need a GitHub workflow, as I did when +using Hugo with GitHub pages; I don't even need to push to a remote +repo. Copying the files suffices. + +# Conclusion + +On the one hand, I'm nowhere near able to make something like the +starter project from scratch. On the other hand, neither am I +daunted. Eleventy in a sense reminds me of Emacs, in that there's a +certain joy to be found in its eclectic complexity. I look forward to +continue using Eleventy as I grow this blog. diff --git a/projects/buildablog.md b/projects/buildablog.md new file mode 100644 index 0000000..f8d534a --- /dev/null +++ b/projects/buildablog.md @@ -0,0 +1,109 @@ ++++ +name = "buildablog" +title = "Building My Own SSG" +host_url = "https://github.com/BrandonIrizarry/buildablog" +synopsis = "The SSG used to build this site." +stack = ["Go", "HTML", "CSS"] +thumbnail = "assets/github-white.svg" +date = 2026-03-01 ++++ + +# Motivation + +While looking for options to host my blog, and having tried Hugo and +Eleventy, I decided to write my own SSG. + +I had completed the [Build a Static Site Generator in Python](https://www.boot.dev/courses/build-static-site-generator-python) +course on boot.dev, and so already had some inkling of what's involved +here. That course requires the learner to manually parse Markdown into +HTML; in my project (which uses Go for the backend), Markdown parsing +is forwarded over to the [frontmatter](https://github.com/adrg/frontmatter) and [goldmark](https://github.com/yuin/goldmark) libraries +(with help from the [goldmark-highlighting](https://github.com/yuin/goldmark-highlighting/v2) extension library for +prettifying code listings.) + +Basically, writing my own SSG from scratch felt like it was going to +be at most not that much harder than learning the ins and outs of +configuring an existing SSG like Hugo or Eleventy. It would also in +the end allow me to configure my site at the atomic level, so to +speak - I write and maintain the server endpoints, the HTML Go +templates, the CSS styling, you get the idea. + +Using Go templates can be daunting at first, but Section 4.6 of of +*The Go Programming Language* — Text and HTML Templates — helps. Jon +Calhoun has a good [mini course](https://www.calhoun.io/intro-to-templates/) on templates, which I had already +completed in its entirety when I needed knowledge of templates for a +previous project I completed, called [Juices](https://github.com/BrandonIrizarry/Juices). Another thing I +learned while working on my SSG is that template parsing should not be +done when serving an endpoint, since this creates a significant +performance bottleneck. + +Generics also came in handy when I wanted to reuse the exact same code +for differing frontmatter layouts (e.g., project posts like this one, +versus ordinary blog posts.) + +# RSS + +Setting up RSS for my site (once you know the tricks) turns out to be +surprisingly straightforward, especially with Go's ability to marshal +all things structured (XML, TOML, JSON, etc.) to and from itself. At +one point I was torn between using either Atom or RSS for my feed, +since the former is [touted](https://nullprogram.com/blog/2013/09/23/) as strictly superior to the latter; in +the end I decided that RSS is good enough to get things started for +now. + +I did face a last-minute curveball when I realized I had to integrate +the *projects* listing to the RSS feed. To this end, I needed a +reliable way to sort RSS item fields by date, which necessitated some +beefing up of the rss package itself. + +Two notable resources for learning about interfacing RSS with Go code +are: + +1. [Create an RSS Feed (WikiHow)](https://www.wikihow.com/Create-an-RSS-Feed) + Super helpful for getting the details of the layout right. + +2. [Build Your Own RSS Feed Generator in Go (YouTube)](https://www.youtube.com/watch?v=b2E1JpC38Pg ) + Helpful for understanding the gory details of what the acutal + marshalling logic looks like. + +With regard to RSS, I feel *all* blogs should have an RSS/Atom +feed. There are some fantastic ones out there that don't! + +When setting up RSS, I finally grokked something while reading a [blog +post](https://pluralistic.net/2024/10/16/keep-it-really-simple-stupid/) by Cory Doctorow where he advocates for RSS: + +I realized that **RSS is a genre of social media.** + +# Sources of Inspiration + +I took (and continue to take) inspiration from blogs I've seen in the +wild, such as: + +1. + + I ripped off a lot of CSS (and general design decisions) from this + site. 😁 The great thing about this site is that it proves that you + can be both a highly intelligent and original thinker without being + a techie conformist or all-around hipster with regard to things + like site aesthetic, opinions, and so on. Such concerns create + performance anxiety which get in the way of you actually making + something *good*. + + As a shout out, the author even gives some quick lessons on setting + up a blog and writing the HTML and CSS for a simple website (which + I also took pointers from! This guy probably did a better job of + explaining what `width: 100%;` means than a lot of resources I've + seen online.) + +2. + + For pointers on how to establish a convention for blog post + slugs. This author uses the date-based scheme, which I adopted in + the end. + +3. + + For general compactness of aesthetic. While I don't plan on going + that route, the nod to S-expression syntax is nice. + + -- cgit v1.2.3