diff --git a/content/articles/blaze-lisp.md b/content/articles/blaze-lisp.md index 801df8d..36604f4 100644 --- a/content/articles/blaze-lisp.md +++ b/content/articles/blaze-lisp.md @@ -3,7 +3,7 @@ title: "blaze lisp" description: "An implementation of the MAL (Make a Lisp) project." navigation: false date: "2023-03-18" -img: "/img/.png" +img: "/img/mal-steps.png" tags: - C++20 - CMake @@ -17,4 +17,77 @@ Repository at [Gitea](https://git.riyyi.com/riyyi/blaze){target="_blank"}. -TODO +After having implemented the JSON serializer for my [utility +library](/articles/utility-library), I became interested in interpreters. The +utility library just has the `tokenization` step of an interpreter, so my tought +was, what's next? That's right, `parsing`. + +Having used Emacs quite a bit, I wasn't shy of Lisp. Then, I came across the MAL +([Make a Lisp](https://github.com/kanaka/mal){target="_blank"}) project, and +decided to look into it. The project lays out the implementation steps of making +a Lisp programming language in 10 or so steps and also provides you with unit +tests that have to pass to proceed to the next step. There are also a bunch of +reference implementations to refer to, in case you get stuck. + +Feeling like implementing a fully interpreted language was a bit of an +undertaking and realizing that in a Lisp `parsing` and `interpreting` is +basically combined into one step, it seemed achievable and I decided to give the +MAL project a go. + +The final architechture that has to be implemented is pictured below. + +
+
+ +![](/img/mal-steps.png "") + +
+
+ +I managed to do a full implementation, including `variables`, `if` statements, +`loops`, `functions`, `try/catch` exceptions, `lists`, `arrays`, `hash-maps` and +even `macros`. + +With the REPL you can try things out and play around easily. + + +``` +./repl +Blaze [C++] +user> +``` + +```elisp +user> 123 +123 +user> (+ 1 2.5) +3.5 +user> (- 10 (- 5 3)) +8 +user> "hello world" +"hello world" +user> (= 2 3) +true +user> (if (> 2 4) "yes" "no") +"no" +user> (count (list 3 6 2 9)) +4 +user> (count '(3 6 2 9)) ; list shorthand +4 +user> (list? [2.1 nil "text"]) ; array / vector +false +user> {:a (+ 7 8)} ; hash-map +{"a" 15} +user> (def! mul (fn* [x y] (* x y))) ; defining a function +#(0x5f35f38815d0) +user> (mul 4 5) +20 +``` + +This project was really interesting and the language is quite powerful, powerful +enough to accomplish self-hosting! This means that an interpreter (or rather, +eval+apply) _for_ this language, can be written _in_ the language. + +If you're curious about reading more examples, a good place is probably the +testsuite for the project, those have a bunch of unit tests and are available +[here](https://github.com/kanaka/mal/tree/master/impls/tests). diff --git a/nuxt.config.ts b/nuxt.config.ts index d6e4206..18ad859 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -15,7 +15,7 @@ export default defineNuxtConfig({ }, langs: [ // https://github.com/shikijs/shiki/blob/main/packages/langs/package.json - "c", "cpp", "css", "html", "js", "json", "lua", "md", "mdc", "php", "python", "shell", "ts", "vue", "yaml" + "c", "cpp", "css", "elisp", "html", "js", "json", "lua", "md", "mdc", "php", "python", "shell", "ts", "vue", "yaml" ] }, toc: { diff --git a/public/img/mal-steps.png b/public/img/mal-steps.png new file mode 100644 index 0000000..1a300d5 Binary files /dev/null and b/public/img/mal-steps.png differ