Interesting. I would have had an easier time looking for interesting things to try if you’d included arithmetic. (Yes, I know, all you need is lambda. That doesn’t mean it’s fun with only lambda!)
I would be less inclined to wrap NEW around everything you return. If you ever add list mutation, you’d want to preserve the identity of values. (Oh, and a little related quibble, the function you call EQ should be called EQUAL?; EQ tests identity, the same location in memory, not just equal values.)
I found it hard to read your code because of all the complicated OOP stuff you use. I’m not convinced that actually buys you much.
I’m not sure at what level to comment, because I don’t know what you already know. For example, have you read SICP?
(Have you seen the Lisp interpreter someone wrote in Scratch? Is it still on their site? What a tour de force! Old Scratch, no user-defined procedures.)
Good idea, will add that. Although, of course, this is really more of a exercise than a real dialect of LISP.
I only use NEW in EVIL to replace cons, and to act as quote for atoms. If I do add list mutation, I might think about it.
Because this exercise mostly deals with implementing EVIL in a LISP-like way, I didn’t want to add memory stuff like this. However, because of this, the only easy way I could add EQ would then be to pretend that garbage collecting existed, and therefore all atoms are equal but not lists. EQUAL? would probably be simpler to implement (=).
In hindsight, it’s only two uses were to 1. make things easier to read (If you see the definition of the cons block, you can tell this was not my goal) and 2. to make implementing the c_r block easier.
Before I started caring about, then only background I had of it was that its syntax had a bunch of parentheses. I’m still a beginner at LISP, but I do find it fascinating. I haven’t read SICP, but I’ve read some history on Scheme.
Huh, I see there are several Lisp interpreter projects on Scratch now! There was only one, back in the Scratch 1.4 days, and it was really complete and featureful, amazing when you consider that you couldn’t create custom blocks back then. Its author never published any other projects and I don’t think anyone knows who he or she is.
About CAR and CDR: The basic underlying data structure in Lisp is the pair, which is just a (Snap!) list of length 2. So CAR is ITEM 1 OF, and CDR is ITEM 2 OF. The “(Snap!)” above is because Lisp lists are made out of pairs, so even though Lisp pairs are made of Snap! lists, Lisp lists aren’t just Snap! lists, and a pair isn’t a Lisp list unless its cdr is a Lisp list.
If you read SICP, Chapter 4 is about a Scheme interpreter written in Scheme. It’s amazingly little code.
If I were going to do Lisp in Snap! myself, I think I’d try to inherit the primitive functions (not the special forms such as COND) from Snap!. That is, if the user says (+ 2 3) I’d look for a Snap! block whose title text is +, and I’d find a block labelled _ + _ so that’s what I’d use. Then I could focus my efforts on parsing and on the process of calling a function.
But the main thing I want to say is that the result of parsing a Lisp expression shouldn’t have any parentheses in it! It should be a list structure, e.g., you’d parse (car (cons 'a 'b))
into
I will just reply to the last 2 paragraphs for now.
While I could do that…it is, for me, a little boring. Again, I would consider the crux of the project to be the EVIL block.
I think you might be confused. the lex block is what separates the input into tokens. It’s so simple I thought about whether to just not make it a block. the parse block turns this into a Lisp expression, which then gets evaluated by EVIL. Does this make EVIL a meta-circular evaluator? I’m not sure.
you should use singular “they” instead. it is shorter, clearer, and includes the possibility of the original author of the interpreter being non-binary (more likely than you’d think!)