From 204676006c9787b2d5db4512105741a6c1a61a8d Mon Sep 17 00:00:00 2001 From: "Brandon C. Irizarry" Date: Thu, 26 Feb 2026 19:37:01 -0500 Subject: Track blog --- .gitignore | 1 + drafts/posts/apple-time.md | 21 +++ drafts/posts/orange.md | 6 + .../smoothing-over-more-markdown-pain-points.md | 85 +++++++++++ drafts/posts/understanding-pratt-parsing.md | 170 +++++++++++++++++++++ drafts/posts/writing-my-blog-with-eleventy.md | 104 +++++++++++++ index/index.md | 6 + 7 files changed, 393 insertions(+) create mode 100644 .gitignore create mode 100644 drafts/posts/apple-time.md create mode 100644 drafts/posts/orange.md create mode 100644 drafts/posts/smoothing-over-more-markdown-pain-points.md create mode 100644 drafts/posts/understanding-pratt-parsing.md create mode 100644 drafts/posts/writing-my-blog-with-eleventy.md create mode 100644 index/index.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3f03d2a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +published/ diff --git a/drafts/posts/apple-time.md b/drafts/posts/apple-time.md new file mode 100644 index 0000000..e012fc5 --- /dev/null +++ b/drafts/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/drafts/posts/orange.md b/drafts/posts/orange.md new file mode 100644 index 0000000..5890577 --- /dev/null +++ b/drafts/posts/orange.md @@ -0,0 +1,6 @@ ++++ +title = "My first post" +summary = "Stub post for buildablog" ++++ + +# A new post right away diff --git a/drafts/posts/smoothing-over-more-markdown-pain-points.md b/drafts/posts/smoothing-over-more-markdown-pain-points.md new file mode 100644 index 0000000..ab87edb --- /dev/null +++ b/drafts/posts/smoothing-over-more-markdown-pain-points.md @@ -0,0 +1,85 @@ ++++ +title = "Smoothing Over More Markdown Pain Points" +tags = ["blogging", "emacs"] +date = 2025-12-05 + +summary = """ + +A post I had written about a small Elisp helper library I wrote for \ +generating a table of contents for a Markdown file. + +""" + ++++ + +# Table of Contents + ++ [I Couldn't Keep It Together](#i-couldn't-keep-it-together) ++ [Elisp For The Win](#elisp-for-the-win) ++ [Yet Another Yasnippet Testimonial](#yet-another-yasnippet-testimonial) ++ [Now I Can Keep It Together!](#now-i-can-keep-it-together!) + + + +# 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](https://brandonirizarry.xyz/blog/writing_my_blog_with_eleventy/#introduction) 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 new file mode 100644 index 0000000..4c058fe --- /dev/null +++ b/drafts/posts/understanding-pratt-parsing.md @@ -0,0 +1,170 @@ ++++ +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. + +""" + ++++ + +# Table of Contents + ++ [Introduction](#introduction) ++ ["It's like a burrito"](#its-like-a-burrito) ++ [Down To Brass Tacks](#down-to-brass-tacks) ++ [Wanting More](#wanting-more) + + + +# 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 new file mode 100644 index 0000000..c822446 --- /dev/null +++ b/drafts/posts/writing-my-blog-with-eleventy.md @@ -0,0 +1,104 @@ ++++ +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. + +""" + ++++ + +# Table of Contents + ++ [Introduction](#introduction) ++ [Hugo](#hugo) ++ [Eleventy: The Soup Actually Tastes Good](#eleventy:-the-soup-actually-tastes-good) ++ [Painless Deployment](#painless-deployment) ++ [Conclusion](#conclusion) + + + +# 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/index/index.md b/index/index.md new file mode 100644 index 0000000..0be36fe --- /dev/null +++ b/index/index.md @@ -0,0 +1,6 @@ ++++ +title = "About Me" +summary = "The about me page." ++++ + +This is the site's front page. -- cgit v1.2.3