fix typo

CismonX 2020-05-04 00:03:33 +08:00
parent 1929f0e045
commit f57ca5bfb8
1 changed files with 3 additions and 3 deletions

@ -1,6 +1,6 @@
## 1. Compiling Unlambda
With a simple hand-written LL(0) parser, Unlambda source code can be trivially parsed into AST, which takes the form of a full binary tree, having "apply" operators as internal nodes and primitive functions as leaf nodes.
With a simple hand-written LL(1) parser, Unlambda source code can be trivially parsed into AST, which takes the form of a full binary tree, having "apply" operators as internal nodes and primitive functions as leaf nodes.
The following image shows the AST parsed from a `cat` program (code: ```` ```s`d`@|i`ci ````):
@ -28,7 +28,7 @@ After each (1), continuously pop instructions from the stack and append it to th
As you may have noticed, there's an obvious flaw in the said compilation rules - the special behaviour of function `d` wasn't taken into consideration. According to the Unlambda language specs, whenever an "operator" (the left child of an internal AST node) evaluates to function `d`, evaluation of the "operand" (its right sibling) is postponed and saved to a "promise", which may evaluate further in the future.
For rule (2), the compiler knows whether X is function `d`. When it is `d`, every instruction between the `NOP` and `APP d, acc` should be skipped, thus it compiles to `DEL offset`, where the "offset" points to the instruction next to the `APP d, acc`. The offset of the instruction next to `DEL offset` should be stored into the promise, so that the skipped instructions can execute in the future. Meanwhile, `APP d, acc` will never happen, so we change it into `LA` and introduce an internal function `f` ("finalize"), which applies the argument to the element popped from the stack, and restores the instruction pointed it saved. The function `f` is pushed to the runtime stack whenever the promise is applied to an argument, which should be pushed to the stack beforehand.
For rule (2), the compiler knows whether X is function `d`. When it is `d`, every instruction between the `NOP` and `APP d, acc` should be skipped, thus it compiles to `DEL offset`, where the "offset" points to the instruction next to the `APP d, acc`. The offset of the instruction next to `DEL offset` should be stored into the promise, so that the skipped instructions can execute in the future. Meanwhile, `APP d, acc` will never happen, so we change it into `LA` and introduce an internal function `f` ("finalize"), which applies the argument to the element popped from the stack, and restores the instruction pointer it saved. The function `f` is pushed to the runtime stack whenever the promise is applied to an argument, which should be pushed to the stack beforehand.
For rule (4), the left child may evaluate to `d`. If not, `SA` behaves normally (saves accumulator), otherwise, it behaves like `DEL`, which requires it to take an offset as operand as well.
@ -168,4 +168,4 @@ Notes:
* Anything before the magic number is omitted (allowing a shebang to be added).
* Integers are of network byte order.
* Anything after the last section is discarded.
* Anything after the last section is discarded.