Fix up compile in compiler blog part 6, and add more text.

This commit is contained in:
Danila Fedorin 2019-10-08 14:09:58 -07:00
parent d9c151d774
commit d3d73e0e9c
3 changed files with 79 additions and 12 deletions

View File

@ -29,7 +29,10 @@ type_ptr ast_lid::typecheck(type_mgr& mgr, const type_env& env) const {
}
void ast_lid::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
into.push_back(instruction_ptr(new instruction_pushglobal(id)));
into.push_back(instruction_ptr(
env->has_variable(id) ?
(instruction*) new instruction_push(env->get_offset(id)) :
(instruction*) new instruction_pushglobal(id)));
}
void ast_uid::print(int indent, std::ostream& to) const {
@ -42,10 +45,7 @@ type_ptr ast_uid::typecheck(type_mgr& mgr, const type_env& env) const {
}
void ast_uid::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
into.push_back(instruction_ptr(
env->has_variable(id) ?
(instruction*) new instruction_push(env->get_offset(id)) :
(instruction*) new instruction_pushglobal(id)));
into.push_back(instruction_ptr(new instruction_pushglobal(id)));
}
void ast_binop::print(int indent, std::ostream& to) const {
@ -70,8 +70,9 @@ type_ptr ast_binop::typecheck(type_mgr& mgr, const type_env& env) const {
}
void ast_binop::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
left->compile(env, into);
right->compile(env, into);
left->compile(env_ptr(new env_offset(1, env)), into);
into.push_back(instruction_ptr(new instruction_pushglobal(op_name(op))));
into.push_back(instruction_ptr(new instruction_mkapp()));
into.push_back(instruction_ptr(new instruction_mkapp()));
@ -95,8 +96,8 @@ type_ptr ast_app::typecheck(type_mgr& mgr, const type_env& env) const {
}
void ast_app::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
left->compile(env, into);
right->compile(env, into);
left->compile(env_ptr(new env_offset(1, env)), into);
into.push_back(instruction_ptr(new instruction_mkapp()));
}

View File

@ -11,7 +11,7 @@ struct env {
using env_ptr = std::shared_ptr<env>;
struct env_var {
struct env_var : public env {
std::string name;
env_ptr parent;
@ -22,7 +22,7 @@ struct env_var {
bool has_variable(const std::string& name) const;
};
struct env_offset {
struct env_offset : public env {
int offset;
env_ptr parent;

View File

@ -144,7 +144,7 @@ Now, it's time for compiling the whole case expression. We first want
to construct the graph for the expression we want to perform case analysis on.
Next, we want to evaluate it (since we need a packed value, not a graph,
to read the tag). Finally, we perform a jump depending on the tag. This
is capture by the following rule:
is captured by the following rule:
$$
\\mathcal{C} ⟦\\text{case} \\; e \\; \\text{of} \\; \\text{alt}_1 ... \\text{alt}_n⟧ \\; \\rho =
@ -196,7 +196,37 @@ And here's the source file:
{{< codeblock "C++" "compiler/06/env.cpp" >}}
{{< todo >}}Explain the code drops. {{< /todo >}}
There's not that much to see here, but let's go through it anyway.
We define an environment as a linked list, kind of like
we did with the type environment. This time, though,
we use shared pointers instead of raw pointers to reference the parent.
I decided on this because we will need to be using virtual methods
(since we have two subclasses of `env`), and thus will need to
be passing the `env` by pointer. At that point, we might as well
use the "proper" way!
I implemented the environment as a linked list because it is, in essence,
a stack. However, not every "offset" in a stack is introduced by
binding variables - for instance, when we create an application node,
we first build the argument value on the stack, and then,
with that value still on the stack, build the left hand side of the application.
Thus, all the variable positions are offset by the presence of the argument
on the stack, and we must account for that. Similarly, in cases when we will
allocate space on the stack (we will run into these cases later), we will
need to account for that change. Thus, since we can increment
the offset by two ways (binding a variable and building something on the stack),
we allow for two types of nodes in our `env` stack.
During recursion we will be tweaking the return value of `get_offset` to
calculate the final location of a variable on the stack (if the
parent of a node returned offset `1`, but the node itself is a variable
node and thus introduces another offset, we need to return `2`). Because
of this, we cannot reasonably return a constant like `-1` (it will quickly
be made positive on a long list), and thus we throw an exception. To
allow for a safe way to check for an offset, without try-catch,
we also add a `has_variable` method which checks if the lookup will succeed.
A better approach would be to use `std::optional`, but it's C++17, so
we'll shy away from it.
It will also help to move some of the functions on the `binop` enum
into a separate file. The new neader is pretty small:
@ -207,4 +237,40 @@ The new source file is not much longer:
{{< codeblock "C++" "compiler/06/binop.cpp" >}}
And now, we begin our implementation.
And now, we begin our implementation. Let's start with the easy ones:
`ast_int`, `ast_lid` and `ast_uid`. The code for `ast_int` involves just pushing
the integer into the stack:
{{< codelines "C++" "compiler/06/ast.cpp" 18 20 >}}
The code for `ast_lid` needs to check if the variable is global or local,
just like we discussed:
{{< codelines "C++" "compiler/06/ast.cpp" 31 36 >}}
We do not have to do this for `ast_uid`:
{{< codelines "C++" "compiler/06/ast.cpp" 47 49 >}}
On to `ast_binop`! This is the first time we have to change our environment.
Once we build the right operand on the stack, every offset that we counted
from the top of the stack will have been shifted by 1 (we see this
in our compilation scheme for function application). So,
we create a new environment with `env_offset`, and use that
when we compile the left child:
{{< codelines "C++" "compiler/06/ast.cpp" 72 79 >}}
`ast_binop` performs two applications: `(+) lhs rhs`.
We push `rhs`, then `lhs`, then `(+)`, and then use MkApp
twice. In `ast_app`, we only need to perform one application,
`lhs rhs`:
{{< codelines "C++" "compiler/06/ast.cpp" 98 102 >}}
Note that we also extend our environment in this one,
for the exact same reason as before.
Case expressions are the only thing left on the agenda. This
is the time during which we have to perform desugaring. Here,
though, we run into an issue: we don't have tags assigned to constructors!