Skalpel APL-alike
I wanted to implement an APL interpreter as the next phase of my APL journey. I'm not arrogant enough to think that I somehow can do "better" than the incumbents; instead tackling this solely for my own amusement and as a means to gain insight into how things actually hang together 'under the hood'.
It's a work in progress, and lacking huge swathes of central chunks of what makes APL APL.
So is it like an open-source version of Dyalog?
LOL.
But why?
The general advice to someone wanting to make their own programming language is typically don't. If you want to make your own array language, you need special permission from the Ministry of Vector Processing. But here we are.
Python?
It's slow, right? Undeniably, but fast enough for this exercise.
Prior art
There are actually quite a few open source APL-alikes around, and the intrepid neophyte APL implementer would do well to learn from them. Here's an abridged resource list:
Install
If you really want to go there, you'll need poetry. You also need a bang-up-to-date version of Python (I use 3.10). At the moment, it only has one dependency, bitarray. skalpel
is type-annotated. Haters will hate.
git clone git@github.com:xpqz/skalpel.git
cd skalpel; poetry install; poetry shell
python apl/repl.py
Repl
The repl behaves like a typical readline()
unixy repl thing, rather than the Dyalog APL repl. Quit with C-d. Step through history with arrows. It has some basic introspection capabilities, mainly for my own use when debugging. Starting a line with ]py
will show the internal representation of the evaluated expression. ]cmpx "expr1" "expr2"
will compare the execution times of the two expressions similar to what Dyalog's cmpx does. Skalpel is a bytecode compiler, not a tree-walking interpreter. Even in the repl, it parses, emits bytecode and then interprets this. You can inspect the generated bytecode using the ]compile
command in the repl.
Differences
skalpel
makes a few concessions to the established APL grammar in order to make it context-free, along the lines of what BQN does, most crucially, requiring that function names must start with a capital letter:
Add ← {⍺+⍵} ⍝ right
add ← {⍺+⍵} ⍝ wrong
When skalpel
eventually grows direct operator support, it will adopt BQN's naming approach there, too.
What's implemented so far?
Current built-ins can be seen in the file skalpel.py in the Voc
class towards the bottom. Basic support for dfns (no guards yet, and only local scope). Barely any operators (currently /⌿¨⍥⍨
). Only numeric arrays!
Are there tests?
As a matter of fact, yes. We use pytest
for unit tests, so you need that if you intend to run them. Tests are found in the suitably named tests
directory. There is also the file t.py
which runs the test suite for ngn/apl
. At the moment, it only passes a minute fraction of those (about 10% 25% 38%), but it's aspirational. Many of the source files also contain doctests.