The language is coming along nicely. I refactored the evaluation function, moving out all of the function call logic into more digestible functions. This led to a few nice advances.
Bug Fixes
I think I got scoping to work well enough, fixing that closure example from last time.
Feature Additions
I added the ability to specify functions with variable numbers of arguments.
To do this, add ...
to the end of the last variable declaration. Then,
when the function is called, the last argument will be set to a list containing
all of the unnamed arguments.
That is,
'(1 2 3)
.
Also, I added very rudimentary error messages, which you can mess up because they’re basically strings. You’ll encounter one of my new errors if you run through all of the examples here. Also, I added a cap to the stack size. It’s currently 40, because I kept making things that recursed forever and wanted them to stop quickly. If you overflow the stack there is an error for that.
Some Quirks
Internally, functions are just a list of two elements: an argument list, and the function expression. When a user-defined function is called, the function arguments are evaluated, and mapped to each element of the argument list inside a frame. This frame is then pushed onto the stack, along with the function’s context, that is, the frame that was at the top of the stack when the function was defined.
This led to an interesting realization - lists in this language can be called as functions, and they will just return their second element. Additionally, they can take in arguments, as listed in their first element!
As an example, consider the following:
But, I lied a little. Lists are not functions. My first paragraph hinted
at a key difference - functions store context. When a function is created
using fun
, the current stack frame is stored in the function node in the
syntax tree. This does not happen with lists. So what if we do our little
function-as-list example using lists-as-functions-as-lists?
If you notice, it doesn’t successfully run, which is to be expected. Fret not, however, as there is something interesting about this lack of context that can give the illusion that it is there.
Finally, I realized that I didn’t make a map properly in JavaScript.
I had assumed the familiar {“key”: “value”} syntax was good enough, not
realizing that that creates an Object with a prototype that defines
things such as toString
. Well, because of that, um, you can’t
define an identifier named toString
and expect it to work, at least
not yet.
So
I plan on fixing this, but need to decide on which course is best (aka easiest but still compatible with enough browsers).