Add finishing touches to code for part 6 of compiler series

This commit is contained in:
Danila Fedorin 2019-10-10 13:14:00 -07:00
parent 18e3f2af55
commit 21f90d85c5
6 changed files with 98 additions and 8 deletions

View File

@ -176,7 +176,7 @@ void ast_case::resolve(const type_mgr& mgr) const {
} }
void ast_case::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const { void ast_case::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
type_data* type = dynamic_cast<type_data*>(node_type.get()); type_data* type = dynamic_cast<type_data*>(of->node_type.get());
of->compile(env, into); of->compile(env, into);
into.push_back(instruction_ptr(new instruction_eval())); into.push_back(instruction_ptr(new instruction_eval()));
@ -201,9 +201,15 @@ void ast_case::compile(const env_ptr& env, std::vector<instruction_ptr>& into) c
} }
jump_instruction->branches.push_back(std::move(branch_instructions)); jump_instruction->branches.push_back(std::move(branch_instructions));
} else if((cpat = dynamic_cast<pattern_constr*>(branch->pat.get()))) { } else if((cpat = dynamic_cast<pattern_constr*>(branch->pat.get()))) {
env_ptr new_env = env;
for(auto it = cpat->params.rbegin(); it != cpat->params.rend(); it++) {
new_env = env_ptr(new env_var(*it, new_env));
}
branch_instructions.push_back(instruction_ptr(new instruction_split())); branch_instructions.push_back(instruction_ptr(new instruction_split()));
branch->expr->compile(env_ptr(new env_offset(cpat->params.size(), env)), branch->expr->compile(new_env, branch_instructions);
branch_instructions); branch_instructions.push_back(instruction_ptr(new instruction_slide(
cpat->params.size())));
int new_tag = type->constructors[cpat->constr].tag; int new_tag = type->constructors[cpat->constr].tag;
if(jump_instruction->tag_mappings.find(new_tag) != if(jump_instruction->tag_mappings.find(new_tag) !=

View File

@ -59,6 +59,7 @@ struct definition {
virtual void typecheck_first(type_mgr& mgr, type_env& env) = 0; virtual void typecheck_first(type_mgr& mgr, type_env& env) = 0;
virtual void typecheck_second(type_mgr& mgr, const type_env& env) const = 0; virtual void typecheck_second(type_mgr& mgr, const type_env& env) const = 0;
virtual void resolve(const type_mgr& mgr) = 0; virtual void resolve(const type_mgr& mgr) = 0;
virtual void compile() = 0;
}; };
using definition_ptr = std::unique_ptr<definition>; using definition_ptr = std::unique_ptr<definition>;
@ -168,6 +169,8 @@ struct definition_defn : public definition {
type_ptr return_type; type_ptr return_type;
std::vector<type_ptr> param_types; std::vector<type_ptr> param_types;
std::vector<instruction_ptr> instructions;
definition_defn(std::string n, std::vector<std::string> p, ast_ptr b) definition_defn(std::string n, std::vector<std::string> p, ast_ptr b)
: name(std::move(n)), params(std::move(p)), body(std::move(b)) { : name(std::move(n)), params(std::move(p)), body(std::move(b)) {
@ -176,6 +179,7 @@ struct definition_defn : public definition {
void typecheck_first(type_mgr& mgr, type_env& env); void typecheck_first(type_mgr& mgr, type_env& env);
void typecheck_second(type_mgr& mgr, const type_env& env) const; void typecheck_second(type_mgr& mgr, const type_env& env) const;
void resolve(const type_mgr& mgr); void resolve(const type_mgr& mgr);
void compile();
}; };
struct definition_data : public definition { struct definition_data : public definition {
@ -188,4 +192,5 @@ struct definition_data : public definition {
void typecheck_first(type_mgr& mgr, type_env& env); void typecheck_first(type_mgr& mgr, type_env& env);
void typecheck_second(type_mgr& mgr, const type_env& env) const; void typecheck_second(type_mgr& mgr, const type_env& env) const;
void resolve(const type_mgr& mgr); void resolve(const type_mgr& mgr);
void compile();
}; };

View File

@ -41,6 +41,15 @@ void definition_defn::resolve(const type_mgr& mgr) {
} }
} }
void definition_defn::compile() {
env_ptr new_env = env_ptr(new env_offset(0, nullptr));
for(auto it = params.rbegin(); it != params.rend(); it++) {
new_env = env_ptr(new env_var(*it, new_env));
}
body->compile(new_env, instructions);
instructions.push_back(instruction_ptr(new instruction_update(params.size())));
}
void definition_data::typecheck_first(type_mgr& mgr, type_env& env) { void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
type_data* this_type = new type_data(name); type_data* this_type = new type_data(name);
type_ptr return_type = type_ptr(this_type); type_ptr return_type = type_ptr(this_type);
@ -67,3 +76,6 @@ void definition_data::resolve(const type_mgr& mgr) {
// Nothing // Nothing
} }
void definition_data::compile() {
}

View File

@ -21,12 +21,12 @@ void instruction_push::print(int indent, std::ostream& to) const {
void instruction_mkapp::print(int indent, std::ostream& to) const { void instruction_mkapp::print(int indent, std::ostream& to) const {
print_indent(indent, to); print_indent(indent, to);
to << "Push()" << std::endl; to << "MkApp()" << std::endl;
} }
void instruction_update::print(int indent, std::ostream& to) const { void instruction_update::print(int indent, std::ostream& to) const {
print_indent(indent, to); print_indent(indent, to);
to << "Offset(" << offset << ")" << std::endl; to << "Update(" << offset << ")" << std::endl;
} }
void instruction_pack::print(int indent, std::ostream& to) const { void instruction_pack::print(int indent, std::ostream& to) const {
@ -48,6 +48,8 @@ void instruction_jump::print(int indent, std::ostream& to) const {
} }
to << std::endl; to << std::endl;
} }
print_indent(indent, to);
to << ")" << std::endl;
} }
void instruction_slide::print(int indent, std::ostream& to) const { void instruction_slide::print(int indent, std::ostream& to) const {

View File

@ -42,6 +42,19 @@ void typecheck_program(
} }
} }
void compile_program(const std::vector<definition_ptr>& prog) {
for(auto& def : prog) {
def->compile();
definition_defn* defn = dynamic_cast<definition_defn*>(def.get());
if(!defn) continue;
for(auto& instruction : defn->instructions) {
instruction->print(0, std::cout);
}
std::cout << std::endl;
}
}
int main() { int main() {
yy::parser parser; yy::parser parser;
type_mgr mgr; type_mgr mgr;
@ -60,6 +73,7 @@ int main() {
} }
try { try {
typecheck_program(program, mgr, env); typecheck_program(program, mgr, env);
compile_program(program);
} catch(unification_error& err) { } catch(unification_error& err) {
std::cout << "failed to unify types: " << std::endl; std::cout << "failed to unify types: " << std::endl;
std::cout << " (1) \033[34m"; std::cout << " (1) \033[34m";

View File

@ -168,6 +168,10 @@ first define C++ structs for the instructions of the G-machine:
{{< codeblock "C++" "compiler/06/instruction.hpp" >}} {{< codeblock "C++" "compiler/06/instruction.hpp" >}}
I omit the implementation of the various (trivial) `print` methods in this post;
as always, you can look at the full project source code, which is
freely available for each post in the series.
We can now envision a method on the `ast` struct that takes an environment We can now envision a method on the `ast` struct that takes an environment
(just like our compilation scheme takes the environment \\(\\rho\\\)), (just like our compilation scheme takes the environment \\(\\rho\\\)),
and compiles the `ast`. Rather than returning a vector and compiles the `ast`. Rather than returning a vector
@ -282,7 +286,7 @@ struct, called `type_data`:
When we create types from `definition_data`, we tag the corresponding constructors: When we create types from `definition_data`, we tag the corresponding constructors:
{{< codelines "C++" "compiler/06/definition.cpp" 35 51 >}} {{< codelines "C++" "compiler/06/definition.cpp" 53 69 >}}
Ah, but adding constructor info to the type doesn't solve the problem. Ah, but adding constructor info to the type doesn't solve the problem.
Once we performed type checking, we don't keep Once we performed type checking, we don't keep
@ -339,7 +343,7 @@ of a node. Here's a sample implementation from `ast_binop`:
And here's the implementation of `definition::resolve` on `definition_defn`: And here's the implementation of `definition::resolve` on `definition_defn`:
{{< codelines "C++" "compiler/06/definition.cpp" 31 33 >}} {{< codelines "C++" "compiler/06/definition.cpp" 32 42 >}}
Finally, we call `resolve` at the end `typecheck_program` in `main.cpp`: Finally, we call `resolve` at the end `typecheck_program` in `main.cpp`:
@ -348,7 +352,7 @@ Finally, we call `resolve` at the end `typecheck_program` in `main.cpp`:
At last, we're ready to implement the code for compiling `ast_case`. At last, we're ready to implement the code for compiling `ast_case`.
Here it is, in all its glory: Here it is, in all its glory:
{{< codelines "C++" "compiler/06/ast.cpp" 178 224 >}} {{< codelines "C++" "compiler/06/ast.cpp" 178 230 >}}
There's a lot to unpack here. First of all, just like we said in the compilation There's a lot to unpack here. First of all, just like we said in the compilation
scheme, we want to build and evaluate the expression that's being analyzed. scheme, we want to build and evaluate the expression that's being analyzed.
@ -399,4 +403,51 @@ After we're done with all the branches, we also check for non-exhaustive pattern
since otherwise we could run into runtime errors. With this, the case expression, since otherwise we could run into runtime errors. With this, the case expression,
and the last of the AST nodes, can be compiled. and the last of the AST nodes, can be compiled.
We also add a `compile` method to definitions, since they contain
our AST nodes. The method is empty for `defn_data`, and
looks as follows for `definition_defn`:
{{< codelines "C++" "compiler/06/definition.cpp" 44 51 >}}
Finally, we make a function in our `main.cpp` file to compile
all the definitions:
{{< codelines "C++" "compiler/06/main.cpp" 45 56 >}}
In this method, we also include some extra
output to help us see the result of our compilation. Since
at the moment, only the `definition_defn` program has to
be compiled, we try cast all definitions to it, and if
we succeed, we print them out.
Let's try it all out! For the below sample program:
{{< rawblock "compiler/06/examples/works1.txt" >}}
Our compiler produces the following new output:
```
PushInt(6)
PushInt(320)
PushGlobal(plus)
MkApp()
MkApp()
Push(1)
Push(1)
PushGlobal(+)
MkApp()
MkApp()
```
The first sequence of instructions is clearly `main`. It creates
an application of `plus` to `320`, and then applies that to
`6`, which results in `plus 320 6`, which is correct. The
second sequence of instruction pushes the parameter that
sits on offset 1 from the top of the stack (`y`). It then
pushes a parameter from the same offset again, but this time,
since `y` was previously pushed on the stack, `x` is now
in that position, so `x` is pushed onto the stack.
Finally, `+` is pushed, and the application
`(+) x y` is created, which is equivalent to `x+y`.
{{< todo >}}Backport bugfix in case's typecheck{{< /todo >}} {{< todo >}}Backport bugfix in case's typecheck{{< /todo >}}