quux_STEP_TO_PROG = mylang/$($(1)).qx
```
-* Add a "run" script to you implementation directory that listens to
+* Add a "run" script to your implementation directory that listens to
the "STEP" environment variable for the implementation step to run
and defaults to "stepA_mal". Make sure the run script has the
executable file permission set (or else the test runner might fail with a
## The Make-A-Lisp Process
+Feel free to follow the guide as literally or as loosely as you
+like. You are here to learn; wandering off the beaten path may be the
+way you learn best. However, each step builds on the previous steps,
+so if you are new to Lisp or new to your implementation language then
+you may want to stick more closely to the guide your first time
+through to avoid frustration at later steps.
+
In the steps that follow the name of the target language is "quux" and
the file extension for that language is "qx".
* The default "apply"/invoke case of `EVAL` must now be changed to
account for the new object/structure returned by the `fn*` form.
- Continue to call `eval_ast` on `ast`. The first element is `f`.
+ Continue to call `eval_ast` on `ast`. The first element of the
+ result of `eval_ast` is `f` and the remaining elements are in `args`.
Switch on the type of `f`:
* regular function (not one defined by `fn*`): apply/invoke it as
before (in step 4).
* a `fn*` value: set `ast` to the `ast` attribute of `f`. Generate
a new environment using the `env` and `params` attributes of `f`
- as the `outer` and `binds` arguments and rest `ast` arguments
- (list elements 2 through the end) as the `exprs` argument. Set
- `env` to the new environment. Continue at the beginning of the loop.
+ as the `outer` and `binds` arguments and `args` as the `exprs`
+ argument. Set `env` to the new environment. Continue at the
+ beginning of the loop.
Run some manual tests from previous steps to make sure you have not
broken anything by adding TCO.
mean something within a quasiquoted list: `unquote` and
`splice-unquote`. These are perhaps best explained with some examples:
-* `(def! lst (quote (2 3)))` -> `(2 3)`
-* `(quasiquote (1 (unquote lst)))` -> `(1 (2 3))`
-* `(quasiquote (1 (splice-unquote lst)))` -> `(1 2 3)`
+* `(def! lst (quote (b c)))` -> `(b c)`
+* `(quasiquote (a lst d)` -> `(a lst d)`
+* `(quasiquote (a (unquote lst) d)` -> `(a (b c) d)`
+* `(quasiquote (a (splice-unquote lst)) d)` -> `(a b c d)`
The `unquote` form turns evaluation back on for its argument and the
result of evaluation is put in place into the quasiquoted list. The
* Add the `quote` special form. This form just returns its argument
(the second list element of `ast`).
-* Add the `quasiquote` special form. First implement a helper function
- `is_pair` that returns true if the parameter is a non-empty list.
- Then define a `quasiquote` function. This is called from `EVAL` with
- the first `ast` argument (second list element) and then `ast` is set
- to the result and execution continues at the top of the loop (TCO).
+* Add the `quasiquote` function.
The `quasiquote` function takes a parameter `ast` and has the
- following conditional:
- 1. if `is_pair` of `ast` is false: return a new list containing:
- a symbol named "quote" and `ast`.
- 2. else if the first element of `ast` is a symbol named "unquote":
- return the second element of `ast`.
- 3. if `is_pair` of the first element of `ast` is true and the first
- element of first element of `ast` (`ast[0][0]`) is a symbol named
- "splice-unquote": return a new list containing: a symbol named
- "concat", the second element of first element of `ast`
- (`ast[0][1]`), and the result of calling `quasiquote` with the
- second through last element of `ast`.
- 4. otherwise: return a new list containing: a symbol named "cons", the
- result of calling `quasiquote` on first element of `ast`
- (`ast[0]`), and the result of calling `quasiquote` with the second
- through last element of `ast`.
-
+ following conditional.
+ - If `ast` is a list starting with the "unquote" symbol, return its
+ second element.
+ - If `ast` is a list failing previous test, the result will be a
+ list populated by the following process.
+
+ The result is initially an empty list.
+ Iterate over each element `elt` of `ast` in reverse order:
+ - If `elt` is a list starting with the "splice-unquote" symbol,
+ replace the current result with a list containing:
+ the "concat" symbol,
+ the second element of `elt`,
+ then the previous result.
+ - Else replace the current result with a list containing:
+ the "cons" symbol,
+ the result of calling `quasiquote` with `elt` as argument,
+ then the previous result.
+
+ This process can also be described recursively:
+ - If `ast` is empty return it unchanged. else let `elt` be its
+ first element.
+ - If `elt` is a list starting with the "splice-unquote" symbol,
+ return a list containing:
+ the "concat" symbol,
+ the second element of `elt`,
+ then the result of processing the rest of `ast`.
+ - Else return a list containing:
+ the "cons" symbol,
+ the result of calling `quasiquote` with `elt` as argument,
+ then the result of processing the rest of `ast`.
+ - If `ast` is a map or a symbol, return a list containing:
+ the "quote" symbol,
+ then `ast`.
+ - Else return `ast` unchanged.
+ Such forms are not affected by evaluation, so you may quote them
+ as in the previous case if implementation is easyer.
+
+* Optionally, add a the `quasiquoteexpand` special form.
+ This form calls the `quasiquote` function using the first `ast`
+ argument (second list element) and returns the result.
+ It has no other practical purpose than testing your implementation
+ of the `quasiquote` internal function.
+
+* Add the `quasiquote` special form.
+ This form does the same than `quasiquoteexpand`,
+ but evaluates the result in the current environment before returning it,
+ either by recursively calling `EVAL` with the result and `env`,
+ or by assigning `ast` with the result and continuing execution at
+ the top of the loop (TCO).
Now go to the top level, run the step 7 tests:
```
the symbol "splice-unquote" and the result of reading the next
form (`read_form`).
-* Add support for quoting of vectors. The `is_pair` function should
- return true if the argument is a non-empty list or vector. `cons`
+* Add support for quoting of vectors. `cons`
should also accept a vector as the second argument. The return value
- is a list regardless. `concat` should support concatenation of
- lists, vectors, or a mix or both. The result is always a list.
+ is a list regardless. `concat` should support concatenation of
+ lists, vectors, or a mix of both. The result is always a list.
+
+ Implement a core function `vec` turning a list into a vector with
+ the same elements. If provided a vector, `vec` should return it
+ unchanged.
+ In the `quasiquote` function, when `ast` is a vector,
+ return a list containing:
+ the "vec" symbol,
+ then the result of processing `ast` as if it were a list not
+ starting with `quote`.
<a name="step8"></a>
and return the first element. If the list (or vector) is empty or
is `nil` then `nil` is returned.
* `rest`: this function takes a list (or vector) as its argument and
- returns a new list containing all the elements except the first.
+ returns a new list containing all the elements except the first. If
+ the list (or vector) is empty or is `nil` then `()` (empty list)
+ is returned.
* In the main program, call the `rep` function with the following
string argument to define a new control structure.