Monday, June 16, 2008

Factor and Lisp, part three

Since last time I wrote about the state of my lisp-in-factor implementation, not much actual forward progress has been made. Instead, I was embroiled in a struggle to figure out how I was to properly implement the passing of arguments to lisp functions, specifically in the case of function calls.

The problem:

First, some background on my lisp converter: It essentially works by converting lisp forms to factor quotations in the following manner: (foo bar baz) ⇒ [ bar baz ] T{ lisp-symbol f "foo" } funcall (By the way, the T{ ... } is a literal tuple, which you may know as an object. So, T{ lisp-symbol f "foo" } is the literal (i.e. you could type that directly into a Factor listener and it would recognize it as a lisp-symbol object) for a lisp-symbol object whose delegate is f (which they usually are, since delegates are deprecated) and whose name field is "foo") In other words, we get a quotation containing the arguments to the function, which funcall will look up and then call appropriately. Certain primitive forms (e.g. quote, lambda, etc) are built-in primitives that are translated at parse time, but everything else will follow this general form.

Okay, that works...but what if we have something like this? (list 1 2 (list 3 (list 4) 5)) Hm...well, if we translate it the same why we did above, it ends up looking like this: [ 1 2 [ 3 [ 4 ] T{ lisp-symbol f "list" } funcall ] T{ lisp-symbol f "list" } funcall 5 ] T{ lisp-symbol f "list" } funcall Now, this is a problem...the argument list for that outer list is a literal quotation, which includes inner quotations, lisp-symbols and funcall words. This means that the arguments to list, rather than numbers and other lists (which is clearly what is intended here) will be quotations, symbols, and words...which is not what we want at all.

So, how to fix this? Well...I tried a few, fairly hackish methods, such as doing a pretty hideous reduce over the quotation inside funcall, to find occurences of the funcall word, then execute funcall on the last two elements of the quotation...blah, gross. I had been thinking...what I basically want to do is call the argument-list quotation, but somehow keep the result of calling it in a quotation. I toyed with the idea of making everything in the argument list into quotations, then doing a [ call ] map over the argument list (that is, evaluate each element of the quotation, putting the result into another list). However, doing this would require some fairly significant refactoring, and would make the rest of the code much less elegant, as the argument list shown above, rather than [ bar baz ], it would have to be [ [ bar ] [ baz ] ], or, even worse, [ [ 1 ] [ 2 ] [ [ 3 ] [ [ 4 ] [ ... ] ] ] ] get the idea. Very gross. However, while on #concatenative the other day, I asked slava about the possibility of writing a word that word that would take a quotation, call it, then put everything that calling the word put on the stack into a quotation...

Will that work?

It turns out that a superior version of the word I was looking for already exists: with-datastack. This word takes an array, which acts as the stack, a quotation, which it calls, and returns the array/stack, which now has the results of calling the quotation. For example: { 1 2 3 } [ + ] with-datastack . would output { 1 5 }. This can now be used to easily rectify my problems! In funcall, before looking up the variable name and calling the resultant quotation, it does 1array [ call ] with-datastack >quotation to the quotation which contains the argument list. Simple, easy, and clear!

What next?

Although I've said it before, it looks like I'm nearly done the factor side of this...All I have left to do is fix up my implementation of macros and provide facilites for loading and interpreting/compiling lisp files and I'll be just about done! At that point, I'll start bootstraping the lisp implementation in lisp itself, but the real "hard work" will be done. Wish me luck!

No comments: