#BabelOfCode 2024
Week 6
Language: Nameless experimental LISP
Confidence level: High
PREV WEEK: mastodon.social/@mcc/113975448…
NEXT WEEK: mastodon.social/@mcc/114433465…
RULES: mastodon.social/@mcc/113676228…
Okay… here things get weird!
My project to, gradually over the course of 2025, do each puzzle from Advent of Code 2024 in a different programming language I've never used before, has stalled out for exactly two months now as I've instead been creating…
…the programming language I'm going to use this week!
#BabelOfCode 2024
Week 5
Language: TCLConfidence level: Medium high
PREV WEEK: mastodon.social/@mcc/113906616…
NEXT WEEK: mastodon.social/@mcc/114308850…
RULES: mastodon.social/@mcc/113676228…TCL is an odd language. I think anyone who's tried developing a programming language has probably at some point thought "what if I just didn't bother with types and did all operations as string transformations?". I think TCL is just that language.
Officially, TCL is supposed to be capitalized "Tcl". I will not be complying.
mcc (@mcc@mastodon.social)
#BabelOfCode 2024 Week 4 Language: FORTRAN Confidence level: High PREV WEEK: https://mastodon.social/@mcc/113867584791780280 NEXT WEEK: https://mastodon.social/@mcc/113975448813565537 RULES: https://mastodon.Mastodon
mcc
in reply to mcc • • •For "Week 5" I did TCL, and it kind of set my brain on fire with "wait… you can just DO that?".
In TCL, strings are anonymous functions and vice versa. In my old Emily language, "if" and "while" blocks were implemented by passing in lambdas; but in TCL you do the same thing by *passing in a string containing {the code to execute}*. I started trying to imagine what "hygienic" TCL would be; if TCL is a C macro, I want an ML or Rust macro. I tried to imagine something closer to LISP than TCL.
наб
in reply to mcc • • •mcc
in reply to наб • • •mcc
in reply to mcc • • •This is what I came up with; I think of it as "0-lisp". In LISP a "2-LISP" is a LISP where functions and variables live in different namespaces, and in a "1-LISP" they live in one namespace. Projecting backward, in my 0-LISP, the distinction itself disappears totally; functions are lists and *any list can be executed*. I wanted to know what this changed. Here's what I found:
It helps very little, and makes certain important things a huge pain. Oops! Still, it was very educational to learn this.
mcc
in reply to mcc • • •Anyway, now I have a LISP interpreter written in Rust.
github.com/mcclure/lisp0-exper…
It's a very minimal "MVP"; the language currently lacks:
- Variable scopes
- Loops
- Conditionals
- Floating point numbers
- Errors lack backtraces or line numbers (and cannot be recovered from)
It has
- One global scope
- Ints, strings, arrays and hashtables
- Tail recursion
- File I/O
But math and recursion means it's Turing complete. Which means it's sufficient to use it for a AOC problem!
GitHub - mcclure/lisp0-experiment at 2025-04-07
GitHubase
in reply to mcc • • •mcc
in reply to ase • • •mcc reshared this.
ase
in reply to mcc • • •mcc
in reply to ase • • •mcc
in reply to mcc • • •Now, since *I'm* the one writing this LISP, I do get to make some changes. The main thing I wanted to change, as it's the #1 thing that infuriates me writing LISP, is parentheses. This "LISP" has a syntax where each line (as separated by newlines or commas) implicitly has () around it. Meanwhile, {} and [] are shorthand, for
{x,y,z} => '((x)(y)(z))
[x,y,z] => (make-array [(x),(y),(z)])
In other words {} is functions (quoted lists) and [] is array literals.
Is this still a LISP? I don't care!
phooky
in reply to mcc • • •mcc
in reply to phooky • • •i've wondered about color-matching them. however, i have a better solution than your )^3, which I used in Emily: The colon
(print: + 2 3)
becomes
(print (+ 2 3))
I think this is similar to Haskell $ or ocaml |>, but it reads a lot more naturally.
aerique
in reply to mcc • • •Have you heard of Dylan?
en.wikipedia.org/wiki/Dylan_pr…
multi-paradigm programming language with support for functional and object-oriented programming
Contributors to Wikimedia projects (Wikimedia Foundation, Inc.)mcc
in reply to aerique • • •yuubi
in reply to mcc • • •any opinions on m-notation, which is definitely part of the lisp tradition, but has a but more texture than just identical parentheses everywhere? en.m.wikipedia.org/wiki/M-expr…
i feel like some of that punctuation should have been whitespace, but that feeling probably comes at least in part from familiarity with the syntax that won (insofar as any lisp syntax won).
Proposed syntax for the Lisp language
Contributors to Wikimedia projects (Wikimedia Foundation, Inc.)mcc
in reply to yuubi • • •@yuubi doesn't necessarily feel like an improvement to me.
I have done something similar in the past by treating ML syntax as a way of constructing s expressions tho.
mcc
in reply to mcc • • •Before I can start, I need to make some utilities. By "utilities" I mean "if" and "while" statements, which again, this language currently lacks. MVP, remember. Implementing these in userland is possible! It's *ugly*, though. For `if`, I simulate branching by putting the two possible outcomes (functions) into an array, casting the condition to a bool and then the bool to an int, and using that as an index to the array.
"while" is even more nightmarish. I don't… understand the use of make-quote.
mcc
in reply to mcc • • •Like, I wrote the code, but I don't understand it. I initially wrote it with all the `make-quote`s being `return`s, and that didn't work, so I experimentally replaced them with make-quotes and it did work. I need to reread the interpreter code to really understand why that was required.
If even the designer can't understand the model here, the model is probably too complicated for anyone ELSE to write self-modifying code either! That's a sign of a problem, or better abstractions needed.
mcc
in reply to mcc • • •Anyway, I like testing things before I use them, so after implementing my "if" and "while" I decide to write a simple Fizzbuzz program. This causes me to immediately realize—
I FORGOT TO IMPLEMENT > AND <
I just forgot!! Fortunately I *did* include bit arithmetic ops, so I implement <, >, <=, >= in userland as well, by testing bit (1<<63) as a proxy for 2's compliment negative. This is actually a little worrisome. I'm not sure if this code truly "works" or if I'm just leveraging UB in Rust.
mcc
in reply to mcc • • •So after a prelude containing reimplementations of if, while, int comparisons, and "noop", I am now ready to implement "fizzbuzz". I do not need an implementation of "fizzbuzz", I'm supposed to be doing AOC2024 day 6. But at least I know my helper routines work!
Fizzbuzz source:
github.com/mcclure/aoc2024/blo…
I was GOING to implement a `proc` that modified function code in-place to add local variable support, but debugging `while` took a lot out of me, so I think I will not do that for this project.
aoc2024/06-01-guard/src/test-fizzbuzz.l0 at 6d12e9cf65158b5f511da8545d263f4ed1dcad99 · mcclure/aoc2024
GitHubmcc
in reply to mcc • • •Got part 1 of the puzzle working this morning!
github.com/mcclure/aoc2024/blo…
App code "proper" starts around line 124 (43-123 are "library code" I didn't post above; I implemented "for" and "switch", and a very small vector math library.)
Writing this program was not at all hard (other than the library code… `for` was painful), but nor was it particularly *pleasant*; it didn't feel fluid. That might speak poorly for my minimal LISP, and the question of whether I use it again after this project.
aoc2024/06-01-guard/src/puzzle.l0 at 2656c75da28669ad11568fae6f5f3a9c24a311a6 · mcclure/aoc2024
GitHubmcc
in reply to mcc • • •mcc
in reply to mcc • • •Making this little LISP was *in part* a "can I do this?" for the Babel project. But there's another thing, which is I want a general purpose assembler. I've recently started wanting to target some unusual/retro systems (NES, N64, Saturn) and I forsee frustration with having no common toolchain among these projects for any language I like, nor a common macroassembler syntax I can take with me across different ISAs.
So I was hoping my tiny LISP could be reworked into a macroassembler.
mcc
in reply to mcc • • •I've realized something:
mov rax, r10
add r8, 1
jmp _main_loop_reset
Assembly looks a lot like LISP once you remove the parentheses! So I wanted to design an ASM syntax that exists as a data structure within a LISP, and write code as a LISP program that generates that data structure. In other words make a DSL similar to "Amaranth" or my old "No Compiler" blog posts msm.runhello.com/p/category/pl… . I may be just confusing you at this point— I'm not sure I can describe this idea clearly in 500 chars.
Run Hello » No Compiler
msm.runhello.comChris [list of emoji]
in reply to mcc • • •Did I already mention Honeylisp? It does exactly that, but for the Apple 2.
On the off chance that I'd planned to mention it but forgot, here it is:
git.information-superhighway.n…
honeylisp
git dot information dash superhighway dot netmcc reshared this.
mcc
in reply to Chris [list of emoji] • • •Chris [list of emoji]
in reply to mcc • • •JP
in reply to Chris [list of emoji] • • •technomancy
in reply to JP • • •mcc
in reply to technomancy • • •JP
in reply to mcc • • •Sensitive content
✧✦Catherine✦✧
in reply to mcc • • •or Forth
(cc @meithecatte who wrote a very cool boot sector forth)
mcc
in reply to ✧✦Catherine✦✧ • • •mcc
in reply to mcc • • •Anyway, before I move on to Week 6 Part 2, I'm considering going back and making changes to the interpreter. I told myself whatever I had written as of 04-07, I'd stick with it— I could fix bugs but not language design issues. Well, I hit some stuff straddling the line:
- lack of "fail" (exit with error message) is m i s e r a b l e, the second half of my program is all `if (!abort)` every few lines
- "the return problem"
…Oh geez. i can't describe the return problem in 500 characters either.
mcc
in reply to mcc • • •The point of my "no parentheses"/l0 syntax was each independent line has an implicit () around it— each line is a function call. This creates a problem: How do you form an expression that returns a value without executing code? I foresaw this during design and added a function named "return" that takes one argument and returns it. So a fn def looks like
{
set 'result
return result
}
Kludgy but readable. *But I forgot about lists!* So all lists turn out an ugly traffic jam of `return`s:
Fish Id Wardrobe
in reply to mcc • • •if a line is a syntactic unit – and since each line is executed I suppose it is – why not have the return keyword return *everything* on the rest of the line?
this is probably horrible to do, I suppose.
mcc
in reply to Fish Id Wardrobe • • •Fish Id Wardrobe
in reply to mcc • • •mcc
in reply to mcc • • •mcc
in reply to mcc • • •mcc
in reply to mcc • • •So that flows well into this next post…
I tried out "part 2" last night. Part 2 involves rerunning the simulation from part 1, but repeatedly stopping it and backing it up to test untaken paths.
That GC bug I was excited about? Yeah, it turns out my part 2 solution stresses my GC enough I get the GC bug *just running the basic program*. Every time I run, I get a random failure in a random place after memory corrupts (usually by randomly scrambing which object which pointer points to).
"LOL"
mcc
in reply to mcc • • •I mentioned before I was looking forward to fixing this GC bug, but that doesn't mean I want to fix it *now*.
Guessing the bug happens after a random number of GCs, I tried increasing the GC space size (by default it's 56 MB, and doubles when a collection ends with the space more than half full— which in this program never happens), to increase the time between GCs. Oddly, this *also* caused the program to survive for more GCs without crashing. But I couldn't find a size it didn't crash.
mcc
in reply to mcc • • •So yesterday got… a little silly.
Let me explain the puzzle.
The idea is a guard (▲) is walking a room with various obstructions (#) [📎 1]. They turn right every time they hit an obstruction, and continue until they reach the room border and "escape" [📎 2]. My goal in part two (SPOILERS!!!!) is to find all the places I can insert a new obstruction (X) which would cause the guard to enter a "loop" and prevent them from escaping [📎 3].
Interestingly, this problem is "embarrassingly parallel".
mcc
in reply to mcc • • •Chris L
in reply to mcc • • •наб
in reply to mcc • • •mcc
in reply to mcc • • •mcc
in reply to mcc • • •…then deduplicate the results using, of course, a small script written in my experimental LISP:
github.com/mcclure/aoc2024/blo…
(I could have just used UNIX uniq, but that's! Not! The point! Of this! Exercise!)
It's still too high!!! And now I'm in trouble. After double checking I conclude the problem isn't my weird multisegmented run process; there's either a very fundamental bug or I've misread the prompt.
My LISP isn't unpleasant to write, but it's not well instrumented for intricate debugging 🙁
aoc2024/06-02-guard/src/dedup.l0 at stable · mcclure/aoc2024
GitHubmcc
in reply to mcc • • •mcc
in reply to mcc • • •This might be the worst I've ever done on an AOC problem (grading by number of unsuccessful submissions)! The site is no longer giving me too high/too low hints, I guess to discourage binary search.
I think the only way I can proceed is to rewrite my code to be "dumber" ( mastodon.social/@mcc/114144927… ) — right now I have some clever optimizations, but those optimizations are potential sites for bugs. Increasingly worried about potential weasel wording in the problem statement mastodon.social/@mcc/114332477…
This might be the worst I've ever done on an AOC problem (grading by number of unsuccessful submissions)! The site is no longer giving me too high/too low hints, I guess to discourage binary search.
I think the only way I can proceed is to rewrite my code to be "dumber" ( mastodon.social/@mcc/114144927… ) — right now I have some clever optimizations, but those optimizations are potential sites for bugs. Increasingly worried about potential weasel wording in the problem statement mastodon.social/@mcc/114332477…
mcc
2025-04-13 20:12:32
mcc
in reply to mcc • • •Okay. So.
- I rewrote my search program to be less optimized in terms of time and more optimized in terms of memory (eg, creating less garbage).
github.com/mcclure/aoc2024/blo…
- In this less optimized state, it took nearly all day to run— like, six hours (but despite doing more work, only crashed and had to be restarted 8 times… so that part worked… sorta… I guess…)
- The "dumber" version produced the correct answer on the first try.
I'm not sure what, if anything, I've learned here.
aoc2024/06-02-guard/src/puzzle-exhaustive.l0 at 4fd6939cbac0a024ed0b5ea85670311688d3fb6a · mcclure/aoc2024
GitHubmcc
in reply to mcc • • •mcc
in reply to mcc • • •However I don't really get to go "there's a moral here!" because the brute force placement method only occurred to me *after* I'd implemented the "smart" time-rewinding method. So I didn't do it the smart/broken way because I prematurely optimized; I did it because I didn't want to rewrite the program. But then I had to do that anyway. Oh well!
Anyway, the second half of this was miserable, but I think I've got some useful feedback (from myself) about how to proceed with the "tiny LISP".
Allan Chow
in reply to mcc • • •mcc
in reply to mcc • • •mcc
in reply to mcc • • •This language may show up again in this post series, once I change it enough it constitutes a "different language". Before that though I want to do weeks with textual WASM (to get ideas about LISP-flavored assembly), Dylan & Haskell (to get ideas about multimethods) and Fennel (so I can try out HoneyLISP freeradical.zone/@suetanvil/11…). Alternately, the next version may just be a self-hosted macroassembler! I also hope to do an Emily 2 week, so my occasional "Emily" references won't just be incoherent.
Chris [list of emoji]
2025-04-10 17:45:28
rk: it’s hyphen-minus actually
in reply to mcc • • •mcc
in reply to mcc • • •As followup, I'm adding now to the language everything I missed while writing my test program.
This means adding > < >= <=, which means writing test cases for string comparison, which means an excuse to come up with cute Hanzi testcases…
Fun fact: Did you know that in Unicode the Chinese numerals are not actually sorted in lexicographic order?? Or any apparent order at all?
(一 < 四)
but
(四 > 五)
Elias Mårtenson
in reply to mcc • • •There is a specific Unicode character property for this, although I'm sure you already knew this.
Specifically, the
nv
property that is the numeric value of a character which represents a number: en.wikipedia.org/wiki/Unicode_…properties of Unicode characters
Contributors to Wikimedia projects (Wikimedia Foundation, Inc.)mcc
in reply to Elias Mårtenson • • •mcc
in reply to mcc • • •Elias Mårtenson
in reply to mcc • • •Yeah, I'd never actually suggest it's a good idea. In fact, I'm not sure the
nv
property can ever be really useful for much either.No "smart" ordering of Unicode characters is ever going to be correct (they can't be, because of conflicting expectations).
Brian Campbell
in reply to mcc • • •mcc
in reply to Brian Campbell • • •mcc
in reply to mcc • • •So one of the unusual solutions I have created to one of the unusual problems my new language has is that every array, every array ever created by the language, now remembers what line of code it was created on forever¹, because the language doesn't yet have an a priori way of knowing which arrays might later become functions and it *might* be needed for error messages
¹ Oddly, due to another questionable decision I made, this doesn't result in any additional memory usage
mcc
in reply to mcc • • •Post-field-test updates:
Finished:
- Line numbers in error messages
- do (call unary function) apply (call function given car element and cdr array) and if (if) builtins now exist
- Lone symbols on a line now return the value instead of calling it as a function
Fizzbuzz is now MUCH more readable and doesn't have to define any library functions except `while`:
github.com/mcclure/lisp0-exper…
TODO:
- `proc` (maybe `fun`) to create functions with named args and locals
- builtin `while`/`for`/`foreach`
lisp0-experiment/sample/fizzbuzz.l0 at 3b7a6676aca477fe529680fe3e008be24308c720 · mcclure/lisp0-experiment
GitHubmcc
in reply to mcc • • •Final update for this thread:
So based on the field test of this AOC, the Nameless Experimental LISP has been improved to a point I'm no longer embarrassed publishing it and "released", as open source, on Codeberg:
codeberg.org/mcc/nameless-expe…
I also documented it, pretty extensively, the documentation .md is now the longest single file in the project.
The main new thing is { } lambdas no longer accept arguments, and there's a `fn` and `set-fn` functions that upgrade a { } to have args/locals.
nameless-experimental-lisp
Codeberg.orgmcc
in reply to mcc • • •Some fun benefits of the refactor:
- "do" ("create a function and immediately call it") now can be used EITHER as a feral flow control construct OR to inject variables into a block's scope (my `for` impl uses this)
- I seem to have fixed the GC crash by accident?! That's terrifying?
The AOC answers, upgraded to the new variant (they're sample code!) are now part 1:
codeberg.org/mcc/nameless-expe…
And part 2, although I think there's a bug:
codeberg.org/mcc/nameless-expe…
And I find them much more readable!
nameless-experimental-lisp/sample/aoc/2024-day6-part1.l0 at 36435712e43afcde5bc85e74341928e5cbf3d12c
Codeberg.orgAmir Livne Bar-on
in reply to mcc • • •mcc
in reply to Amir Livne Bar-on • • •@Pashhur There are two problems with solving it with a macro:
- If I place any extra code at the end of the function-- for example, setting the locals to previous values, or to nil-- then I will break any tail recursion the function performs.
- I don't have any builtins for environment manipulation; if I did I'd want to choose them very carefully, because part of the point of this language is to be less aggressively dynamic than my previous language attempts, so I can easily write a compiler.
Owen Nelson
in reply to mcc • • •fabiosantoscode
in reply to mcc • • •if your problem was just variables, you could have solved it elegantly by making them all nullary functions.
So assigning X = 3 would be essentially creating a function called X that takes no arguments and returns 3.
But alas, the problem also affects primitives
mcc
in reply to fabiosantoscode • • •dave
in reply to mcc • • •mcc
in reply to dave • • •slembcke
in reply to mcc • • •Ölbaum
in reply to mcc • • •mcc
in reply to Ölbaum • • •Graham Sutherland / Polynomial
in reply to mcc • • •R
in reply to mcc • • •just saw this post -- Back In The Day there were a number of table-driven retargetable assemblers for systems like this, with "Telemark Assembler" being especially common in the circles we personally lurked around
they're all quite underpowered by modern devtool standards though, and they very much didn't have good implementations for architecture-specific fixups
it'll be interesting to see your take on this!
mcc
in reply to R • • •Bitbucket
bitbucket.orgRiley S. Faelan
in reply to mcc • • •Sensitive content
the Fennel programming language
fennel-lang.orgmcc
in reply to Riley S. Faelan • • •jcoglan
in reply to mcc • • •mcc
in reply to jcoglan • • •jcoglan
in reply to jcoglan • • •mcc
in reply to jcoglan • • •spv
in reply to mcc • • •Elias Mårtenson
in reply to mcc • • •Joe Groff
in reply to mcc • • •Ted Mielczarek
in reply to mcc • • •mcc
in reply to Ted Mielczarek • • •Ted Mielczarek
in reply to mcc • • •Ingvar
in reply to mcc • • •In a 1-lisp, there may or may not be a distinction between functions and variables. A worked example would be the really weird lisp "xlisp" (which was a lisp with very pre-CLtL object orientation on top), where something like
((lambda (a b) (+ a b) 17 42)
would evaluate to 59.
And "(defun sum (a b) (+ a b))" would cause the symbol "sum" to be bound to the list (lambda (a b) (+ a b))
So on that basis, if you have any way to bind symbols to values, it's probably not a 0-lisp.
mcc reshared this.
varx/social
in reply to mcc • • •mcc
Unknown parent • • •mcc
Unknown parent • • •@dysfun I'm not actually 100% sure WHAT I'm doing! I'd have to trace the number interpreter and the implementation of +.
Now that I think about it, it might be better to replace the positive (1<<63) with whatever negative number (1<<63) corresponds to, and have the reader reject number literals over ((1<<63)-1)?
Matthew Abbott
in reply to mcc • • •May I suggest the stand-in name NELS?
Nameless
Experimental
LISP
Sandbox (or Specification, Setup, Something)
mcc
in reply to Matthew Abbott • • •@matt I mean I was thinking NEL.
I am amused by languages that sound like someone's name. "Nel" for Nameless Experimental List, "Uli" for Unnamed Lisp Interpreter, "Uel" for Unnamed Experimental Lisp.
mcc
Unknown parent • • •mcc
Unknown parent • • •mcc
Unknown parent • • •mcc
Unknown parent • • •Dataline
Unknown parent • • •mcc
Unknown parent • • •mcc reshared this.
Adriano
in reply to mcc • • •Shar(yna)Tran/Shark(aeopteryx)
in reply to mcc • • •outerheaven.club/objects/e5706…
xrvs (@xarvos@outerheaven.club)
outerheaven.club