Update "compiler: execution" to new math delimiters
Signed-off-by: Danila Fedorin <danila.fedorin@gmail.com>
This commit is contained in:
parent
96545a899f
commit
d3fa7336a2
|
@ -147,19 +147,19 @@ We will follow the same notation as Simon Peyton Jones in
|
||||||
[his book](https://www.microsoft.com/en-us/research/wp-content/uploads/1992/01/student.pdf)
|
[his book](https://www.microsoft.com/en-us/research/wp-content/uploads/1992/01/student.pdf)
|
||||||
, which was my source of truth when implementing my compiler. The machine
|
, which was my source of truth when implementing my compiler. The machine
|
||||||
will be executing instructions that we give it, and as such, it must have
|
will be executing instructions that we give it, and as such, it must have
|
||||||
an instruction queue, which we will reference as \\(i\\). We will write
|
an instruction queue, which we will reference as \(i\). We will write
|
||||||
\\(x:i\\) to mean "an instruction queue that starts with
|
\(x:i\) to mean "an instruction queue that starts with
|
||||||
an instruction x and ends with instructions \\(i\\)". A stack machine
|
an instruction x and ends with instructions \(i\)". A stack machine
|
||||||
obviously needs to have a stack - we will call it \\(s\\), and will
|
obviously needs to have a stack - we will call it \(s\), and will
|
||||||
adopt a similar notation to the instruction queue: \\(a\_1, a\_2, a\_3 : s\\)
|
adopt a similar notation to the instruction queue: \(a_1, a_2, a_3 : s\)
|
||||||
will mean "a stack with the top values \\(a\_1\\), \\(a\_2\\), and \\(a\_3\\),
|
will mean "a stack with the top values \(a_1\), \(a_2\), and \(a_3\),
|
||||||
and remaining instructions \\(s\\)". Finally, as we said, our stack
|
and remaining instructions \(s\)". Finally, as we said, our stack
|
||||||
machine has a dump, which we will write as \\(d\\). On this dump,
|
machine has a dump, which we will write as \(d\). On this dump,
|
||||||
we will push not only the current stack, but also the current
|
we will push not only the current stack, but also the current
|
||||||
instructions that we are executing, so we may resume execution
|
instructions that we are executing, so we may resume execution
|
||||||
later. We will write \\(\\langle i, s \\rangle : d\\) to mean
|
later. We will write \(\langle i, s \rangle : d\) to mean
|
||||||
"a dump with instructions \\(i\\) and stack \\(s\\) on top,
|
"a dump with instructions \(i\) and stack \(s\) on top,
|
||||||
followed by instructions and stacks in \\(d\\)".
|
followed by instructions and stacks in \(d\)".
|
||||||
|
|
||||||
There's one more thing the G-machine will have that we've not yet discussed at all,
|
There's one more thing the G-machine will have that we've not yet discussed at all,
|
||||||
and it's needed because of the following quip earlier in the post:
|
and it's needed because of the following quip earlier in the post:
|
||||||
|
@ -179,14 +179,14 @@ its address the same, but change the value on the heap.
|
||||||
This way, all trees that reference the node we change become updated,
|
This way, all trees that reference the node we change become updated,
|
||||||
without us having to change them - their child address remains the same,
|
without us having to change them - their child address remains the same,
|
||||||
but the child has now been updated. We represent the heap
|
but the child has now been updated. We represent the heap
|
||||||
using \\(h\\). We write \\(h[a : v]\\) to say "the address \\(a\\) points
|
using \(h\). We write \(h[a : v]\) to say "the address \(a\) points
|
||||||
to value \\(v\\) in the heap \\(h\\)". Now you also know why we used
|
to value \(v\) in the heap \(h\)". Now you also know why we used
|
||||||
the letter \\(a\\) when describing values on the stack - the stack contains
|
the letter \(a\) when describing values on the stack - the stack contains
|
||||||
addresses of (or pointers to) tree nodes.
|
addresses of (or pointers to) tree nodes.
|
||||||
|
|
||||||
_Compiling Functional Languages: a tutorial_ also keeps another component
|
_Compiling Functional Languages: a tutorial_ also keeps another component
|
||||||
of the G-machine, the __global map__, which maps function names to addresses of nodes
|
of the G-machine, the __global map__, which maps function names to addresses of nodes
|
||||||
that represent them. We'll stick with this, and call this global map \\(m\\).
|
that represent them. We'll stick with this, and call this global map \(m\).
|
||||||
|
|
||||||
Finally, let's talk about what kind of nodes our trees will be made of.
|
Finally, let's talk about what kind of nodes our trees will be made of.
|
||||||
We don't have to include every node that we've defined as a subclass of
|
We don't have to include every node that we've defined as a subclass of
|
||||||
|
@ -218,7 +218,7 @@ First up is __PushInt__:
|
||||||
|
|
||||||
Let's go through this. We start with an instruction queue
|
Let's go through this. We start with an instruction queue
|
||||||
with `PushInt n` on top. We allocate a new `NInt` with the
|
with `PushInt n` on top. We allocate a new `NInt` with the
|
||||||
number `n` on the heap at address \\(a\\). We then push
|
number `n` on the heap at address \(a\). We then push
|
||||||
the address of the `NInt` node on top of the stack. Next,
|
the address of the `NInt` node on top of the stack. Next,
|
||||||
__PushGlobal__:
|
__PushGlobal__:
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ __Push__:
|
||||||
{{< /gmachine >}}
|
{{< /gmachine >}}
|
||||||
|
|
||||||
We define this instruction to work if and only if there exists an address
|
We define this instruction to work if and only if there exists an address
|
||||||
on the stack at offset \\(n\\). We take the value at that offset, and
|
on the stack at offset \(n\). We take the value at that offset, and
|
||||||
push it onto the stack again. This can be helpful for something like
|
push it onto the stack again. This can be helpful for something like
|
||||||
`f x x`, where we use the same tree twice. Speaking of that - let's
|
`f x x`, where we use the same tree twice. Speaking of that - let's
|
||||||
define an instruction to combine two nodes into an application:
|
define an instruction to combine two nodes into an application:
|
||||||
|
@ -274,11 +274,11 @@ that is an `NApp` node, with its two children being the nodes we popped off.
|
||||||
Finally, we push it onto the stack.
|
Finally, we push it onto the stack.
|
||||||
|
|
||||||
Let's try use these instructions to get a feel for it. In
|
Let's try use these instructions to get a feel for it. In
|
||||||
order to conserve space, let's use \\(\\text{G}\\) for PushGlobal,
|
order to conserve space, let's use \(\text{G}\) for PushGlobal,
|
||||||
\\(\\text{I}\\) for PushInt, and \\(\\text{A}\\) for PushApp.
|
\(\text{I}\) for PushInt, and \(\text{A}\) for PushApp.
|
||||||
Let's say we want to construct a graph for `double 326`. We'll
|
Let's say we want to construct a graph for `double 326`. We'll
|
||||||
use the instructions \\(\\text{I} \; 326\\), \\(\\text{G} \; \\text{double}\\),
|
use the instructions \(\text{I} \; 326\), \(\text{G} \; \text{double}\),
|
||||||
and \\(\\text{A}\\). Let's watch these instructions play out:
|
and \(\text{A}\). Let's watch these instructions play out:
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
[\text{I} \; 326, \text{G} \; \text{double}, \text{A}] & \quad s \quad & d \quad & h \quad & m[\text{double} : a_d] \\
|
[\text{I} \; 326, \text{G} \; \text{double}, \text{A}] & \quad s \quad & d \quad & h \quad & m[\text{double} : a_d] \\
|
||||||
|
@ -346,13 +346,13 @@ code for the global function:
|
||||||
{{< /gmachine_inner >}}
|
{{< /gmachine_inner >}}
|
||||||
{{< /gmachine >}}
|
{{< /gmachine >}}
|
||||||
|
|
||||||
In this rule, we used a general rule for \\(a\_k\\), in which \\(k\\) is any number
|
In this rule, we used a general rule for \(a_k\), in which \(k\) is any number
|
||||||
between 1 and \\(n-1\\). We also expect the `NGlobal` node to contain two parameters,
|
between 1 and \(n-1\). We also expect the `NGlobal` node to contain two parameters,
|
||||||
\\(n\\) and \\(c\\). \\(n\\) is the arity of the function (the number of arguments
|
\(n\) and \(c\). \(n\) is the arity of the function (the number of arguments
|
||||||
it expects), and \\(c\\) are the instructions to construct the function's tree.
|
it expects), and \(c\) are the instructions to construct the function's tree.
|
||||||
|
|
||||||
The attentive reader will have noticed a catch: we kept \\(a\_{n-1}\\) on the stack!
|
The attentive reader will have noticed a catch: we kept \(a_{n-1}\) on the stack!
|
||||||
This once again goes back to replacing a node in-place. \\(a\_{n-1}\\) is the address of the "root" of the
|
This once again goes back to replacing a node in-place. \(a_{n-1}\) is the address of the "root" of the
|
||||||
whole expression we're simplifying. Thus, to replace the value at this address, we need to keep
|
whole expression we're simplifying. Thus, to replace the value at this address, we need to keep
|
||||||
the address until we have something to replace it with.
|
the address until we have something to replace it with.
|
||||||
|
|
||||||
|
@ -451,7 +451,7 @@ and define it to contain a mapping from tags to instructions
|
||||||
to be executed for a value of that tag. For instance,
|
to be executed for a value of that tag. For instance,
|
||||||
if the constructor `Nil` has tag 0, and `Cons` has tag 1,
|
if the constructor `Nil` has tag 0, and `Cons` has tag 1,
|
||||||
the mapping for the case expression of a length function
|
the mapping for the case expression of a length function
|
||||||
could be written as \\([0 \\rightarrow [\\text{PushInt} \; 0], 1 \\rightarrow [\\text{PushGlobal} \; \\text{length}, ...] ]\\).
|
could be written as \([0 \rightarrow [\text{PushInt} \; 0], 1 \rightarrow [\text{PushGlobal} \; \text{length}, ...] ]\).
|
||||||
Let's define the rule for it:
|
Let's define the rule for it:
|
||||||
|
|
||||||
{{< gmachine "Jump" >}}
|
{{< gmachine "Jump" >}}
|
||||||
|
@ -475,7 +475,7 @@ creating a final graph. We then continue to reduce this final
|
||||||
graph. But we've left the function parameters on the stack!
|
graph. But we've left the function parameters on the stack!
|
||||||
This is untidy. We define a __Slide__ instruction,
|
This is untidy. We define a __Slide__ instruction,
|
||||||
which keeps the address at the top of the stack, but gets
|
which keeps the address at the top of the stack, but gets
|
||||||
rid of the next \\(n\\) addresses:
|
rid of the next \(n\) addresses:
|
||||||
|
|
||||||
{{< gmachine "Slide" >}}
|
{{< gmachine "Slide" >}}
|
||||||
{{< gmachine_inner "Before">}}
|
{{< gmachine_inner "Before">}}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user