Compare commits
256 Commits
64f4abb8d6
...
table-of-c
| Author | SHA1 | Date | |
|---|---|---|---|
| e7d56dd4bd | |||
| a4fedb276d | |||
| 277c0a2ce6 | |||
| ef3c61e9e6 | |||
| 1908126607 | |||
| 2d77f8489f | |||
| 0371651fdd | |||
| 01734d24f7 | |||
| 71fc0546e0 | |||
| 871a745702 | |||
| 3f0df8ae0d | |||
| 1746011c16 | |||
| 7c4cfbf3d4 | |||
| 8524e098a8 | |||
| 971f58da9b | |||
| c496be1031 | |||
| 21851e3a9c | |||
| 600d5b91ea | |||
| 09b90c3bbc | |||
| f6ca13d6dc | |||
| 9c4d7c514f | |||
| ad1946e9fb | |||
| 68910458e8 | |||
| 240e87eca4 | |||
| 6b5f7e25b7 | |||
| e7229e644f | |||
| 08c8aca144 | |||
| 7f8dae74ac | |||
| 08503116ff | |||
| a1d679a59d | |||
| 4586bd0188 | |||
| a97b50f497 | |||
| c84ff11d0d | |||
| e966e74487 | |||
| 3865abfb4d | |||
| 1905601aaa | |||
| aacb9e2874 | |||
| 78f3b18969 | |||
| 9f73ca2950 | |||
| 035b98a602 | |||
| 17f4ebc297 | |||
| 906e15674e | |||
| 85bd0b6c9c | |||
| b19e8713e0 | |||
| 68fb78e765 | |||
| be8a0a4a3a | |||
| e883e3c60e | |||
| 4ede62b39a | |||
| 7d9f487a78 | |||
| 9da584ded4 | |||
| 9452c90cf3 | |||
| a80064f40a | |||
| 49691803cc | |||
| ee4738b245 | |||
| b270fa78da | |||
| 18339d7e4d | |||
| 78563448fb | |||
| 144d5f3324 | |||
| 0fb315ec47 | |||
| 1ff67341a1 | |||
| a441280812 | |||
| eda9bbb191 | |||
| 2d9da2899f | |||
| a95490d9d4 | |||
| 44135b1824 | |||
| 4a0367b401 | |||
| c1f0104edb | |||
| c9a7fbf6dd | |||
| 1f00b6a3f8 | |||
| acb22c4119 | |||
| be2b855ffe | |||
| 88c9418350 | |||
| 2255543d94 | |||
| b4c91d2dd4 | |||
| 98c1b5a3b2 | |||
| 122a1d73d3 | |||
| 74e6dba914 | |||
| d7846e0b32 | |||
| 902ffc0bc0 | |||
| 8c1168d818 | |||
| fd77fe078a | |||
| 7f3883fb39 | |||
| 6a2698e911 | |||
| 569fea74a7 | |||
| b04d82f0b3 | |||
| 33cd4f5f68 | |||
| 49cf8e3e08 | |||
| c53a8ba68e | |||
| 5cccb97ede | |||
| 493419f324 | |||
| 577e0ad930 | |||
| 2a12f7f31e | |||
| ae3e661d7a | |||
| 0efa05142f | |||
| 6714e18e7c | |||
| 5d53678e83 | |||
| 3cb66a606d | |||
| 074db07275 | |||
| e3834ed6ea | |||
| 1bdb4a650e | |||
| 6966973497 | |||
| 8ee016e189 | |||
| fa0a96f057 | |||
| a2c84f5c40 | |||
| 768c43df2d | |||
| 579d988f4a | |||
| 45bc113e3f | |||
| 1abc13b20f | |||
| cdc9e28c90 | |||
| 8a48a110ff | |||
| 0eb1abd26d | |||
| a7a6d7ff13 | |||
| fb544e0545 | |||
| 84029fbc5b | |||
| 8039e459fa | |||
| f202c8ea44 | |||
| d8d1aa66e6 | |||
| 79ef221820 | |||
| 67ecc741d0 | |||
| 80d722568e | |||
| b9fcac974d | |||
| 31e9e58304 | |||
| 8f09b518ba | |||
| 2d6aab6b71 | |||
| 6712c0064a | |||
| 5e6d97ab36 | |||
| ea753fdfe7 | |||
| 1db8a24b4d | |||
| f8adac8b76 | |||
| 4bae586e36 | |||
| 3522c34adf | |||
| 96fc519b3c | |||
| 28f686eb80 | |||
| 6f0c95e49c | |||
| 130086db00 | |||
| 2a1ad171c0 | |||
| db8a050bdf | |||
| 3ff5ce4dec | |||
| 9f8855a4d3 | |||
| d3515d3fa5 | |||
| 277427af57 | |||
| f6c1079bda | |||
| 252d82469c | |||
| 1879ba2c2b | |||
| fc444c1986 | |||
| ae9805e4f2 | |||
| 33b1457e91 | |||
| 9e399ebe3c | |||
| eac1151616 | |||
| f7a7100fea | |||
| c207d1dfcf | |||
| df051fd643 | |||
| 419ab937b6 | |||
| 7ff919c31b | |||
| ee90351c17 | |||
| fbdbf67ce3 | |||
| a7e32d300a | |||
| 56387cb936 | |||
| df965816ac | |||
| c7341c9b15 | |||
| 00322d7e9f | |||
| ef93632130 | |||
| 0f744888ef | |||
| a5b84bab69 | |||
| 12725500a8 | |||
| 1917c08e51 | |||
| b304057560 | |||
| e5a39d8dfb | |||
| 54ccef9c72 | |||
| c103c6acbf | |||
| d6f53076c0 | |||
| b07ea8fe9c | |||
| 9a7441779f | |||
| a6f27e446d | |||
| e7f0ccfa16 | |||
| e5d01a4e19 | |||
| b7d72f2fbf | |||
| 281dbbd174 | |||
| 153349f3d5 | |||
| 8d22acfe78 | |||
| c1b030ee97 | |||
| 803f52b2d0 | |||
| 2f96abeef6 | |||
| 163fcd2b2e | |||
| 9ddcb1b3f2 | |||
| 133979218a | |||
| ef545be03c | |||
| c534dc7508 | |||
| 263ffe2b8c | |||
| 67181fb033 | |||
| a026e67a3b | |||
| d9544398b9 | |||
| 1c4bb29fdd | |||
| 765d497724 | |||
| 80410c9200 | |||
| 4e918db5cb | |||
| 382102f071 | |||
| 6e88780f8b | |||
| e3035b9d66 | |||
| 8765626898 | |||
| c38247df9e | |||
| baf44f8627 | |||
| 19aa126025 | |||
| a406fb0846 | |||
| 75664e90bb | |||
| f74209c970 | |||
| c7ce8a3107 | |||
| b3b906dd90 | |||
| b8e0e0b4ce | |||
| eb02e1e6b0 | |||
| b2fc6ea5a8 | |||
| f75a47e273 | |||
| 9eae560cae | |||
| b0529a9124 | |||
| 3df9c57482 | |||
| cb5163e1d9 | |||
| c309ac4c14 | |||
| 58c9d5f982 | |||
| dc9a68ad10 | |||
| db16dbda18 | |||
| 172630c2ee | |||
| 6dc7734c70 | |||
| 19a1ffbc98 | |||
| 2cce2859bb | |||
| 654239e29f | |||
| 50fbe3e196 | |||
| 1a8a1c3052 | |||
| 2994f8983d | |||
| 64227f2873 | |||
| 9aef499deb | |||
| c79b5a4120 | |||
| 81ee50d0d4 | |||
| 43b140285f | |||
| adb894869e | |||
| 1f6032a30e | |||
| 9531f4d8e3 | |||
| 37097d3a40 | |||
| 3aa468c2f6 | |||
| c704187012 | |||
| a834fd578e | |||
| 4b5e2f4454 | |||
| 7812b1064b | |||
| 65b9f385cf | |||
| ed88d54aa6 | |||
| d1b515ec5b | |||
| 1ffc43af98 | |||
| b27dc19e57 | |||
| df0b819b0e | |||
| 21f90d85c5 | |||
| 18e3f2af55 | |||
| 3901c9b115 | |||
| d9486d08ae | |||
| d90993a93c | |||
| 7e9bd95846 | |||
| d3d73e0e9c | |||
| d9c151d774 |
38
.drone.yml
Normal file
38
.drone.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
volumes:
|
||||
- name: live-output
|
||||
temp: {}
|
||||
|
||||
steps:
|
||||
- name: test-compiler
|
||||
image: archlinux
|
||||
commands:
|
||||
- pacman -Sy cmake gcc make llvm bison flex gettext libffi --noconfirm
|
||||
- cd code/compiler
|
||||
- ./test.sh
|
||||
- name: build-live
|
||||
image: klakegg/hugo:ext-alpine
|
||||
commands:
|
||||
- hugo -D --baseUrl "http://danilafe.com:8080"
|
||||
volumes:
|
||||
- name: live-output
|
||||
path: /live-output
|
||||
environment:
|
||||
HUGO_DESTINATION: /live-output
|
||||
# - name: upload-live
|
||||
# image: eeacms/rsync
|
||||
# commands:
|
||||
# - eval `ssh-agent -s`
|
||||
# - echo "$CUSTOM_KEY" | ssh-add -
|
||||
# - mkdir -p ~/.ssh
|
||||
# - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
|
||||
# - rsync -rv -e "ssh -p 22" /live-output/ blog-live@danilafe.com:/var/www/blog-live/ --checksum
|
||||
# environment:
|
||||
# CUSTOM_KEY:
|
||||
# from_secret: live_ssh_key
|
||||
# volumes:
|
||||
# - name: live-output
|
||||
# path: /live-output
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
**/build/*
|
||||
@@ -1,14 +1,14 @@
|
||||
$basic-border: 1px solid #bfbfbf;
|
||||
@import "variables.scss";
|
||||
@import "mixins.scss";
|
||||
|
||||
.gmachine-instruction {
|
||||
display: flex;
|
||||
border: $basic-border;
|
||||
border-radius: 2px;
|
||||
@include bordered-block;
|
||||
}
|
||||
|
||||
.gmachine-instruction-name {
|
||||
padding: 10px;
|
||||
border-right: $basic-border;
|
||||
padding: .8rem;
|
||||
border-right: $standard-border;
|
||||
flex-grow: 1;
|
||||
flex-basis: 20%;
|
||||
text-align: center;
|
||||
@@ -20,7 +20,7 @@ $basic-border: 1px solid #bfbfbf;
|
||||
}
|
||||
|
||||
.gmachine-inner {
|
||||
border-bottom: $basic-border;
|
||||
border-bottom: $standard-border;
|
||||
width: 100%;
|
||||
|
||||
&:last-child {
|
||||
@@ -29,12 +29,12 @@ $basic-border: 1px solid #bfbfbf;
|
||||
}
|
||||
|
||||
.gmachine-inner-label {
|
||||
padding: 10px;
|
||||
padding: .8rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.gmachine-inner-text {
|
||||
padding: 10px;
|
||||
padding: .8rem;
|
||||
text-align: right;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
19
assets/scss/stack.scss
Normal file
19
assets/scss/stack.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
@import "variables.scss";
|
||||
@import "mixins.scss";
|
||||
|
||||
.stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 10rem;
|
||||
margin: auto;
|
||||
@include bordered-block;
|
||||
}
|
||||
|
||||
.stack-element {
|
||||
text-align: center;
|
||||
min-height: 1.5rem;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: $standard-border;
|
||||
}
|
||||
}
|
||||
@@ -97,10 +97,6 @@ type_ptr ast_case::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_ptr case_type = mgr.resolve(of->typecheck(mgr, env), var);
|
||||
type_ptr branch_type = mgr.new_type();
|
||||
|
||||
if(!dynamic_cast<type_base*>(case_type.get())) {
|
||||
throw type_error("attempting case analysis of non-data type");
|
||||
}
|
||||
|
||||
for(auto& branch : branches) {
|
||||
type_env new_env = env.scope();
|
||||
branch->pat->match(case_type, mgr, new_env);
|
||||
@@ -108,6 +104,11 @@ type_ptr ast_case::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
mgr.unify(branch_type, curr_branch_type);
|
||||
}
|
||||
|
||||
case_type = mgr.resolve(case_type, var);
|
||||
if(!dynamic_cast<type_base*>(case_type.get())) {
|
||||
throw type_error("attempting case analysis of non-data type");
|
||||
}
|
||||
|
||||
return branch_type;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ add_executable(compiler
|
||||
env.cpp env.hpp
|
||||
type.cpp type.hpp
|
||||
error.cpp error.hpp
|
||||
binop.cpp binop.hpp
|
||||
instruction.cpp instruction.hpp
|
||||
${BISON_parser_OUTPUTS}
|
||||
${FLEX_scanner_OUTPUTS}
|
||||
main.cpp
|
||||
|
||||
@@ -2,18 +2,22 @@
|
||||
#include <ostream>
|
||||
#include "error.hpp"
|
||||
|
||||
std::string op_name(binop op) {
|
||||
switch(op) {
|
||||
case PLUS: return "+";
|
||||
case MINUS: return "-";
|
||||
case TIMES: return "*";
|
||||
case DIVIDE: return "/";
|
||||
}
|
||||
return "??";
|
||||
static void print_indent(int n, std::ostream& to) {
|
||||
while(n--) to << " ";
|
||||
}
|
||||
|
||||
void print_indent(int n, std::ostream& to) {
|
||||
while(n--) to << " ";
|
||||
type_ptr ast::typecheck_common(type_mgr& mgr, const type_env& env) {
|
||||
node_type = typecheck(mgr, env);
|
||||
return node_type;
|
||||
}
|
||||
|
||||
void ast::resolve_common(const type_mgr& mgr) {
|
||||
type_var* var;
|
||||
type_ptr resolved_type = mgr.resolve(node_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
|
||||
resolve(mgr);
|
||||
node_type = std::move(resolved_type);
|
||||
}
|
||||
|
||||
void ast_int::print(int indent, std::ostream& to) const {
|
||||
@@ -25,6 +29,14 @@ type_ptr ast_int::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return type_ptr(new type_base("Int"));
|
||||
}
|
||||
|
||||
void ast_int::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_int::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
into.push_back(instruction_ptr(new instruction_pushint(value)));
|
||||
}
|
||||
|
||||
void ast_lid::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "LID: " << id << std::endl;
|
||||
@@ -34,6 +46,17 @@ type_ptr ast_lid::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return env.lookup(id);
|
||||
}
|
||||
|
||||
void ast_lid::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_lid::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)));
|
||||
}
|
||||
|
||||
void ast_uid::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "UID: " << id << std::endl;
|
||||
@@ -43,6 +66,14 @@ type_ptr ast_uid::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return env.lookup(id);
|
||||
}
|
||||
|
||||
void ast_uid::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_uid::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
into.push_back(instruction_ptr(new instruction_pushglobal(id)));
|
||||
}
|
||||
|
||||
void ast_binop::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "BINOP: " << op_name(op) << std::endl;
|
||||
@@ -51,8 +82,8 @@ void ast_binop::print(int indent, std::ostream& to) const {
|
||||
}
|
||||
|
||||
type_ptr ast_binop::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_ptr ltype = left->typecheck(mgr, env);
|
||||
type_ptr rtype = right->typecheck(mgr, env);
|
||||
type_ptr ltype = left->typecheck_common(mgr, env);
|
||||
type_ptr rtype = right->typecheck_common(mgr, env);
|
||||
type_ptr ftype = env.lookup(op_name(op));
|
||||
if(!ftype) throw type_error(std::string("unknown binary operator ") + op_name(op));
|
||||
|
||||
@@ -64,6 +95,20 @@ type_ptr ast_binop::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return return_type;
|
||||
}
|
||||
|
||||
void ast_binop::resolve(const type_mgr& mgr) const {
|
||||
left->resolve_common(mgr);
|
||||
right->resolve_common(mgr);
|
||||
}
|
||||
|
||||
void ast_binop::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
right->compile(env, into);
|
||||
left->compile(env_ptr(new env_offset(1, env)), into);
|
||||
|
||||
into.push_back(instruction_ptr(new instruction_pushglobal(op_action(op))));
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
}
|
||||
|
||||
void ast_app::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "APP:" << std::endl;
|
||||
@@ -72,8 +117,8 @@ void ast_app::print(int indent, std::ostream& to) const {
|
||||
}
|
||||
|
||||
type_ptr ast_app::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_ptr ltype = left->typecheck(mgr, env);
|
||||
type_ptr rtype = right->typecheck(mgr, env);
|
||||
type_ptr ltype = left->typecheck_common(mgr, env);
|
||||
type_ptr rtype = right->typecheck_common(mgr, env);
|
||||
|
||||
type_ptr return_type = mgr.new_type();
|
||||
type_ptr arrow = type_ptr(new type_arr(rtype, return_type));
|
||||
@@ -81,6 +126,17 @@ type_ptr ast_app::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return return_type;
|
||||
}
|
||||
|
||||
void ast_app::resolve(const type_mgr& mgr) const {
|
||||
left->resolve_common(mgr);
|
||||
right->resolve_common(mgr);
|
||||
}
|
||||
|
||||
void ast_app::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
right->compile(env, into);
|
||||
left->compile(env_ptr(new env_offset(1, env)), into);
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
}
|
||||
|
||||
void ast_case::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "CASE: " << std::endl;
|
||||
@@ -94,23 +150,85 @@ void ast_case::print(int indent, std::ostream& to) const {
|
||||
|
||||
type_ptr ast_case::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_var* var;
|
||||
type_ptr case_type = mgr.resolve(of->typecheck(mgr, env), var);
|
||||
type_ptr case_type = mgr.resolve(of->typecheck_common(mgr, env), var);
|
||||
type_ptr branch_type = mgr.new_type();
|
||||
|
||||
if(!dynamic_cast<type_base*>(case_type.get())) {
|
||||
throw type_error("attempting case analysis of non-data type");
|
||||
}
|
||||
|
||||
for(auto& branch : branches) {
|
||||
type_env new_env = env.scope();
|
||||
branch->pat->match(case_type, mgr, new_env);
|
||||
type_ptr curr_branch_type = branch->expr->typecheck(mgr, new_env);
|
||||
type_ptr curr_branch_type = branch->expr->typecheck_common(mgr, new_env);
|
||||
mgr.unify(branch_type, curr_branch_type);
|
||||
}
|
||||
|
||||
case_type = mgr.resolve(case_type, var);
|
||||
if(!dynamic_cast<type_data*>(case_type.get())) {
|
||||
throw type_error("attempting case analysis of non-data type");
|
||||
}
|
||||
|
||||
return branch_type;
|
||||
}
|
||||
|
||||
void ast_case::resolve(const type_mgr& mgr) const {
|
||||
of->resolve_common(mgr);
|
||||
for(auto& branch : branches) {
|
||||
branch->expr->resolve_common(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
void ast_case::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
type_data* type = dynamic_cast<type_data*>(of->node_type.get());
|
||||
|
||||
of->compile(env, into);
|
||||
into.push_back(instruction_ptr(new instruction_eval()));
|
||||
|
||||
instruction_jump* jump_instruction = new instruction_jump();
|
||||
into.push_back(instruction_ptr(jump_instruction));
|
||||
for(auto& branch : branches) {
|
||||
std::vector<instruction_ptr> branch_instructions;
|
||||
pattern_var* vpat;
|
||||
pattern_constr* cpat;
|
||||
|
||||
if((vpat = dynamic_cast<pattern_var*>(branch->pat.get()))) {
|
||||
branch->expr->compile(env_ptr(new env_offset(1, env)), branch_instructions);
|
||||
|
||||
for(auto& constr_pair : type->constructors) {
|
||||
if(jump_instruction->tag_mappings.find(constr_pair.second.tag) !=
|
||||
jump_instruction->tag_mappings.end())
|
||||
break;
|
||||
|
||||
jump_instruction->tag_mappings[constr_pair.second.tag] =
|
||||
jump_instruction->branches.size();
|
||||
}
|
||||
jump_instruction->branches.push_back(std::move(branch_instructions));
|
||||
} 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->expr->compile(new_env, branch_instructions);
|
||||
branch_instructions.push_back(instruction_ptr(new instruction_slide(
|
||||
cpat->params.size())));
|
||||
|
||||
int new_tag = type->constructors[cpat->constr].tag;
|
||||
if(jump_instruction->tag_mappings.find(new_tag) !=
|
||||
jump_instruction->tag_mappings.end())
|
||||
throw type_error("technically not a type error: duplicate pattern");
|
||||
|
||||
jump_instruction->tag_mappings[new_tag] =
|
||||
jump_instruction->branches.size();
|
||||
jump_instruction->branches.push_back(std::move(branch_instructions));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& constr_pair : type->constructors) {
|
||||
if(jump_instruction->tag_mappings.find(constr_pair.second.tag) ==
|
||||
jump_instruction->tag_mappings.end())
|
||||
throw type_error("non-total pattern");
|
||||
}
|
||||
}
|
||||
|
||||
void pattern_var::print(std::ostream& to) const {
|
||||
to << var;
|
||||
}
|
||||
|
||||
@@ -8,12 +8,18 @@
|
||||
#include "env.hpp"
|
||||
|
||||
struct ast {
|
||||
type_ptr node_type;
|
||||
|
||||
virtual ~ast() = default;
|
||||
|
||||
virtual void print(int indent, std::ostream& to) const = 0;
|
||||
virtual type_ptr typecheck(type_mgr& mgr, const type_env& env) const = 0;
|
||||
virtual void compile(const env_ptr env,
|
||||
std::vector<instruction>& into) const;
|
||||
virtual void resolve(const type_mgr& mgr) const = 0;
|
||||
virtual void compile(const env_ptr& env,
|
||||
std::vector<instruction_ptr>& into) const = 0;
|
||||
|
||||
type_ptr typecheck_common(type_mgr& mgr, const type_env& env);
|
||||
void resolve_common(const type_mgr& mgr);
|
||||
};
|
||||
|
||||
using ast_ptr = std::unique_ptr<ast>;
|
||||
@@ -40,6 +46,7 @@ using branch_ptr = std::unique_ptr<branch>;
|
||||
struct constructor {
|
||||
std::string name;
|
||||
std::vector<std::string> types;
|
||||
int8_t tag;
|
||||
|
||||
constructor(std::string n, std::vector<std::string> ts)
|
||||
: name(std::move(n)), types(std::move(ts)) {}
|
||||
@@ -52,6 +59,8 @@ struct definition {
|
||||
|
||||
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 resolve(const type_mgr& mgr) = 0;
|
||||
virtual void compile() = 0;
|
||||
};
|
||||
|
||||
using definition_ptr = std::unique_ptr<definition>;
|
||||
@@ -64,6 +73,8 @@ struct ast_int : public ast {
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_lid : public ast {
|
||||
@@ -74,6 +85,8 @@ struct ast_lid : public ast {
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_uid : public ast {
|
||||
@@ -84,6 +97,8 @@ struct ast_uid : public ast {
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_binop : public ast {
|
||||
@@ -96,6 +111,8 @@ struct ast_binop : public ast {
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_app : public ast {
|
||||
@@ -107,6 +124,8 @@ struct ast_app : public ast {
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_case : public ast {
|
||||
@@ -118,6 +137,8 @@ struct ast_case : public ast {
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct pattern_var : public pattern {
|
||||
@@ -149,6 +170,8 @@ struct definition_defn : public definition {
|
||||
type_ptr return_type;
|
||||
std::vector<type_ptr> param_types;
|
||||
|
||||
std::vector<instruction_ptr> instructions;
|
||||
|
||||
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)) {
|
||||
|
||||
@@ -156,6 +179,8 @@ struct definition_defn : public definition {
|
||||
|
||||
void typecheck_first(type_mgr& mgr, type_env& env);
|
||||
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr);
|
||||
void compile();
|
||||
};
|
||||
|
||||
struct definition_data : public definition {
|
||||
@@ -167,4 +192,6 @@ struct definition_data : public definition {
|
||||
|
||||
void typecheck_first(type_mgr& mgr, type_env& env);
|
||||
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr);
|
||||
void compile();
|
||||
};
|
||||
|
||||
21
code/compiler/06/binop.cpp
Normal file
21
code/compiler/06/binop.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "binop.hpp"
|
||||
|
||||
std::string op_name(binop op) {
|
||||
switch(op) {
|
||||
case PLUS: return "+";
|
||||
case MINUS: return "-";
|
||||
case TIMES: return "*";
|
||||
case DIVIDE: return "/";
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
|
||||
std::string op_action(binop op) {
|
||||
switch(op) {
|
||||
case PLUS: return "plus";
|
||||
case MINUS: return "minus";
|
||||
case TIMES: return "times";
|
||||
case DIVIDE: return "divide";
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
enum binop {
|
||||
PLUS,
|
||||
@@ -7,3 +8,5 @@ enum binop {
|
||||
DIVIDE
|
||||
};
|
||||
|
||||
std::string op_name(binop op);
|
||||
std::string op_action(binop op);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ast.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
void definition_defn::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||
return_type = mgr.new_type();
|
||||
@@ -24,16 +25,42 @@ void definition_defn::typecheck_second(type_mgr& mgr, const type_env& env) const
|
||||
type_it++;
|
||||
}
|
||||
|
||||
type_ptr body_type = body->typecheck(mgr, new_env);
|
||||
type_ptr body_type = body->typecheck_common(mgr, new_env);
|
||||
mgr.unify(return_type, body_type);
|
||||
}
|
||||
|
||||
void definition_defn::resolve(const type_mgr& mgr) {
|
||||
type_var* var;
|
||||
body->resolve_common(mgr);
|
||||
|
||||
return_type = mgr.resolve(return_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
for(auto& param_type : param_types) {
|
||||
param_type = mgr.resolve(param_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
}
|
||||
}
|
||||
|
||||
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())));
|
||||
instructions.push_back(instruction_ptr(new instruction_pop(params.size())));
|
||||
}
|
||||
|
||||
void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||
type_ptr return_type = type_ptr(new type_base(name));
|
||||
type_data* this_type = new type_data(name);
|
||||
type_ptr return_type = type_ptr(this_type);
|
||||
int next_tag = 0;
|
||||
|
||||
for(auto& constructor : constructors) {
|
||||
type_ptr full_type = return_type;
|
||||
constructor->tag = next_tag;
|
||||
this_type->constructors[constructor->name] = { next_tag++ };
|
||||
|
||||
type_ptr full_type = return_type;
|
||||
for(auto it = constructor->types.rbegin(); it != constructor->types.rend(); it++) {
|
||||
type_ptr type = type_ptr(new type_base(*it));
|
||||
full_type = type_ptr(new type_arr(type, full_type));
|
||||
@@ -46,3 +73,11 @@ void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||
void definition_data::typecheck_second(type_mgr& mgr, const type_env& env) const {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
void definition_data::resolve(const type_mgr& mgr) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
void definition_data::compile() {
|
||||
|
||||
}
|
||||
|
||||
23
code/compiler/06/env.cpp
Normal file
23
code/compiler/06/env.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "env.hpp"
|
||||
|
||||
int env_var::get_offset(const std::string& name) const {
|
||||
if(name == this->name) return 0;
|
||||
if(parent) return parent->get_offset(name) + 1;
|
||||
throw 0;
|
||||
}
|
||||
|
||||
bool env_var::has_variable(const std::string& name) const {
|
||||
if(name == this->name) return true;
|
||||
if(parent) return parent->has_variable(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
int env_offset::get_offset(const std::string& name) const {
|
||||
if(parent) return parent->get_offset(name) + offset;
|
||||
throw 0;
|
||||
}
|
||||
|
||||
bool env_offset::has_variable(const std::string& name) const {
|
||||
if(parent) return parent->has_variable(name);
|
||||
return false;
|
||||
}
|
||||
@@ -6,26 +6,29 @@ struct env {
|
||||
virtual ~env() = default;
|
||||
|
||||
virtual int get_offset(const std::string& name) const = 0;
|
||||
virtual bool has_variable(const std::string& name) const = 0;
|
||||
};
|
||||
|
||||
using env_ptr = std::shared_ptr<env>;
|
||||
|
||||
struct env_var {
|
||||
struct env_var : public env {
|
||||
std::string name;
|
||||
env_ptr parent;
|
||||
|
||||
env_var(std::string& n, env_ptr p)
|
||||
: name(std::move(n)), parent(std::move(p)) {}
|
||||
|
||||
virtual int get_offset(const std::string& name) const;
|
||||
int get_offset(const std::string& name) const;
|
||||
bool has_variable(const std::string& name) const;
|
||||
};
|
||||
|
||||
struct env_offset {
|
||||
struct env_offset : public env {
|
||||
int offset;
|
||||
env_ptr parent;
|
||||
|
||||
env_offset(int o, env_ptr p)
|
||||
: offset(o), parent(std::move(p)) {}
|
||||
|
||||
virtual int get_offset(const std::string& name) const;
|
||||
int get_offset(const std::string& name) const;
|
||||
bool has_variable(const std::string& name) const;
|
||||
};
|
||||
|
||||
83
code/compiler/06/instruction.cpp
Normal file
83
code/compiler/06/instruction.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "instruction.hpp"
|
||||
|
||||
static void print_indent(int n, std::ostream& to) {
|
||||
while(n--) to << " ";
|
||||
}
|
||||
|
||||
void instruction_pushint::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "PushInt(" << value << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_pushglobal::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "PushGlobal(" << name << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_push::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Push(" << offset << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_pop::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Pop(" << count << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_mkapp::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "MkApp()" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_update::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Update(" << offset << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_pack::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Pack(" << tag << ", " << size << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_split::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Split()" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_jump::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Jump(" << std::endl;
|
||||
for(auto& instruction_set : branches) {
|
||||
for(auto& instruction : instruction_set) {
|
||||
instruction->print(indent + 2, to);
|
||||
}
|
||||
to << std::endl;
|
||||
}
|
||||
print_indent(indent, to);
|
||||
to << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_slide::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Slide(" << offset << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_binop::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "BinOp(" << op_action(op) << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_eval::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Eval()" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_alloc::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Alloc(" << amount << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_unwind::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Unwind()" << std::endl;
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include "binop.hpp"
|
||||
|
||||
struct instruction {
|
||||
virtual ~instruction() = default;
|
||||
|
||||
virtual void print(int indent, std::ostream& to) const = 0;
|
||||
};
|
||||
|
||||
using instruction_ptr = std::unique_ptr<instruction>;
|
||||
@@ -14,6 +19,8 @@ struct instruction_pushint : public instruction {
|
||||
|
||||
instruction_pushint(int v)
|
||||
: value(v) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_pushglobal : public instruction {
|
||||
@@ -21,6 +28,8 @@ struct instruction_pushglobal : public instruction {
|
||||
|
||||
instruction_pushglobal(std::string n)
|
||||
: name(std::move(n)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_push : public instruction {
|
||||
@@ -28,10 +37,21 @@ struct instruction_push : public instruction {
|
||||
|
||||
instruction_push(int o)
|
||||
: offset(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_pop : public instruction {
|
||||
int count;
|
||||
|
||||
instruction_pop(int c)
|
||||
: count(c) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_mkapp : public instruction {
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_update : public instruction {
|
||||
@@ -39,6 +59,8 @@ struct instruction_update : public instruction {
|
||||
|
||||
instruction_update(int o)
|
||||
: offset(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_pack : public instruction {
|
||||
@@ -47,10 +69,19 @@ struct instruction_pack : public instruction {
|
||||
|
||||
instruction_pack(int t, int s)
|
||||
: tag(t), size(s) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_split : public instruction {
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_jump : public instruction {
|
||||
std::vector<std::vector<instruction_ptr>> branches;
|
||||
std::map<int, int> tag_mappings;
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_slide : public instruction {
|
||||
@@ -58,6 +89,8 @@ struct instruction_slide : public instruction {
|
||||
|
||||
instruction_slide(int o)
|
||||
: offset(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_binop : public instruction {
|
||||
@@ -65,10 +98,12 @@ struct instruction_binop : public instruction {
|
||||
|
||||
instruction_binop(binop o)
|
||||
: op(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_eval : public instruction {
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_alloc : public instruction {
|
||||
@@ -76,8 +111,10 @@ struct instruction_alloc : public instruction {
|
||||
|
||||
instruction_alloc(int a)
|
||||
: amount(a) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_unwind : public instruction {
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
@@ -36,6 +36,23 @@ void typecheck_program(
|
||||
pair.second->print(mgr, std::cout);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
for(auto& def : prog) {
|
||||
def->resolve(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -56,6 +73,7 @@ int main() {
|
||||
}
|
||||
try {
|
||||
typecheck_program(program, mgr, env);
|
||||
compile_program(program);
|
||||
} catch(unification_error& err) {
|
||||
std::cout << "failed to unify types: " << std::endl;
|
||||
std::cout << " (1) \033[34m";
|
||||
|
||||
@@ -44,7 +44,7 @@ type_ptr type_mgr::new_arrow_type() {
|
||||
return type_ptr(new type_arr(new_type(), new_type()));
|
||||
}
|
||||
|
||||
type_ptr type_mgr::resolve(type_ptr t, type_var*& var) {
|
||||
type_ptr type_mgr::resolve(type_ptr t, type_var*& var) const {
|
||||
type_var* cast;
|
||||
|
||||
var = nullptr;
|
||||
|
||||
@@ -30,6 +30,17 @@ struct type_base : public type {
|
||||
void print(const type_mgr& mgr, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct type_data : public type_base {
|
||||
struct constructor {
|
||||
int tag;
|
||||
};
|
||||
|
||||
std::map<std::string, constructor> constructors;
|
||||
|
||||
type_data(std::string n)
|
||||
: type_base(std::move(n)) {}
|
||||
};
|
||||
|
||||
struct type_arr : public type {
|
||||
type_ptr left;
|
||||
type_ptr right;
|
||||
@@ -49,6 +60,6 @@ struct type_mgr {
|
||||
type_ptr new_arrow_type();
|
||||
|
||||
void unify(type_ptr l, type_ptr r);
|
||||
type_ptr resolve(type_ptr t, type_var*& var);
|
||||
type_ptr resolve(type_ptr t, type_var*& var) const;
|
||||
void bind(const std::string& s, type_ptr t);
|
||||
};
|
||||
|
||||
28
code/compiler/07/CMakeLists.txt
Normal file
28
code/compiler/07/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(compiler)
|
||||
|
||||
find_package(BISON)
|
||||
find_package(FLEX)
|
||||
bison_target(parser
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/parser.y
|
||||
${CMAKE_CURRENT_BINARY_DIR}/parser.cpp
|
||||
COMPILE_FLAGS "-d")
|
||||
flex_target(scanner
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/scanner.l
|
||||
${CMAKE_CURRENT_BINARY_DIR}/scanner.cpp)
|
||||
add_flex_bison_dependency(scanner parser)
|
||||
|
||||
add_executable(compiler
|
||||
ast.cpp ast.hpp definition.cpp
|
||||
type_env.cpp type_env.hpp
|
||||
env.cpp env.hpp
|
||||
type.cpp type.hpp
|
||||
error.cpp error.hpp
|
||||
binop.cpp binop.hpp
|
||||
instruction.cpp instruction.hpp
|
||||
${BISON_parser_OUTPUTS}
|
||||
${FLEX_scanner_OUTPUTS}
|
||||
main.cpp
|
||||
)
|
||||
target_include_directories(compiler PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(compiler PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
||||
262
code/compiler/07/ast.cpp
Normal file
262
code/compiler/07/ast.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
#include "ast.hpp"
|
||||
#include <ostream>
|
||||
#include "error.hpp"
|
||||
|
||||
static void print_indent(int n, std::ostream& to) {
|
||||
while(n--) to << " ";
|
||||
}
|
||||
|
||||
type_ptr ast::typecheck_common(type_mgr& mgr, const type_env& env) {
|
||||
node_type = typecheck(mgr, env);
|
||||
return node_type;
|
||||
}
|
||||
|
||||
void ast::resolve_common(const type_mgr& mgr) {
|
||||
type_var* var;
|
||||
type_ptr resolved_type = mgr.resolve(node_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
|
||||
resolve(mgr);
|
||||
node_type = std::move(resolved_type);
|
||||
}
|
||||
|
||||
void ast_int::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "INT: " << value << std::endl;
|
||||
}
|
||||
|
||||
type_ptr ast_int::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return type_ptr(new type_base("Int"));
|
||||
}
|
||||
|
||||
void ast_int::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_int::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
into.push_back(instruction_ptr(new instruction_pushint(value)));
|
||||
}
|
||||
|
||||
void ast_lid::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "LID: " << id << std::endl;
|
||||
}
|
||||
|
||||
type_ptr ast_lid::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return env.lookup(id);
|
||||
}
|
||||
|
||||
void ast_lid::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_lid::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)));
|
||||
}
|
||||
|
||||
void ast_uid::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "UID: " << id << std::endl;
|
||||
}
|
||||
|
||||
type_ptr ast_uid::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return env.lookup(id);
|
||||
}
|
||||
|
||||
void ast_uid::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_uid::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
into.push_back(instruction_ptr(new instruction_pushglobal(id)));
|
||||
}
|
||||
|
||||
void ast_binop::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "BINOP: " << op_name(op) << std::endl;
|
||||
left->print(indent + 1, to);
|
||||
right->print(indent + 1, to);
|
||||
}
|
||||
|
||||
type_ptr ast_binop::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_ptr ltype = left->typecheck_common(mgr, env);
|
||||
type_ptr rtype = right->typecheck_common(mgr, env);
|
||||
type_ptr ftype = env.lookup(op_name(op));
|
||||
if(!ftype) throw type_error(std::string("unknown binary operator ") + op_name(op));
|
||||
|
||||
type_ptr return_type = mgr.new_type();
|
||||
type_ptr arrow_one = type_ptr(new type_arr(rtype, return_type));
|
||||
type_ptr arrow_two = type_ptr(new type_arr(ltype, arrow_one));
|
||||
|
||||
mgr.unify(arrow_two, ftype);
|
||||
return return_type;
|
||||
}
|
||||
|
||||
void ast_binop::resolve(const type_mgr& mgr) const {
|
||||
left->resolve_common(mgr);
|
||||
right->resolve_common(mgr);
|
||||
}
|
||||
|
||||
void ast_binop::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
right->compile(env, into);
|
||||
left->compile(env_ptr(new env_offset(1, env)), into);
|
||||
|
||||
into.push_back(instruction_ptr(new instruction_pushglobal(op_action(op))));
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
}
|
||||
|
||||
void ast_app::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "APP:" << std::endl;
|
||||
left->print(indent + 1, to);
|
||||
right->print(indent + 1, to);
|
||||
}
|
||||
|
||||
type_ptr ast_app::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_ptr ltype = left->typecheck_common(mgr, env);
|
||||
type_ptr rtype = right->typecheck_common(mgr, env);
|
||||
|
||||
type_ptr return_type = mgr.new_type();
|
||||
type_ptr arrow = type_ptr(new type_arr(rtype, return_type));
|
||||
mgr.unify(arrow, ltype);
|
||||
return return_type;
|
||||
}
|
||||
|
||||
void ast_app::resolve(const type_mgr& mgr) const {
|
||||
left->resolve_common(mgr);
|
||||
right->resolve_common(mgr);
|
||||
}
|
||||
|
||||
void ast_app::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
right->compile(env, into);
|
||||
left->compile(env_ptr(new env_offset(1, env)), into);
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
}
|
||||
|
||||
void ast_case::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "CASE: " << std::endl;
|
||||
for(auto& branch : branches) {
|
||||
print_indent(indent + 1, to);
|
||||
branch->pat->print(to);
|
||||
to << std::endl;
|
||||
branch->expr->print(indent + 2, to);
|
||||
}
|
||||
}
|
||||
|
||||
type_ptr ast_case::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_var* var;
|
||||
type_ptr case_type = mgr.resolve(of->typecheck_common(mgr, env), var);
|
||||
type_ptr branch_type = mgr.new_type();
|
||||
|
||||
for(auto& branch : branches) {
|
||||
type_env new_env = env.scope();
|
||||
branch->pat->match(case_type, mgr, new_env);
|
||||
type_ptr curr_branch_type = branch->expr->typecheck_common(mgr, new_env);
|
||||
mgr.unify(branch_type, curr_branch_type);
|
||||
}
|
||||
|
||||
case_type = mgr.resolve(case_type, var);
|
||||
if(!dynamic_cast<type_data*>(case_type.get())) {
|
||||
throw type_error("attempting case analysis of non-data type");
|
||||
}
|
||||
|
||||
return branch_type;
|
||||
}
|
||||
|
||||
void ast_case::resolve(const type_mgr& mgr) const {
|
||||
of->resolve_common(mgr);
|
||||
for(auto& branch : branches) {
|
||||
branch->expr->resolve_common(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
void ast_case::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
type_data* type = dynamic_cast<type_data*>(of->node_type.get());
|
||||
|
||||
of->compile(env, into);
|
||||
into.push_back(instruction_ptr(new instruction_eval()));
|
||||
|
||||
instruction_jump* jump_instruction = new instruction_jump();
|
||||
into.push_back(instruction_ptr(jump_instruction));
|
||||
for(auto& branch : branches) {
|
||||
std::vector<instruction_ptr> branch_instructions;
|
||||
pattern_var* vpat;
|
||||
pattern_constr* cpat;
|
||||
|
||||
if((vpat = dynamic_cast<pattern_var*>(branch->pat.get()))) {
|
||||
branch->expr->compile(env_ptr(new env_offset(1, env)), branch_instructions);
|
||||
|
||||
for(auto& constr_pair : type->constructors) {
|
||||
if(jump_instruction->tag_mappings.find(constr_pair.second.tag) !=
|
||||
jump_instruction->tag_mappings.end())
|
||||
break;
|
||||
|
||||
jump_instruction->tag_mappings[constr_pair.second.tag] =
|
||||
jump_instruction->branches.size();
|
||||
}
|
||||
jump_instruction->branches.push_back(std::move(branch_instructions));
|
||||
} 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->expr->compile(new_env, branch_instructions);
|
||||
branch_instructions.push_back(instruction_ptr(new instruction_slide(
|
||||
cpat->params.size())));
|
||||
|
||||
int new_tag = type->constructors[cpat->constr].tag;
|
||||
if(jump_instruction->tag_mappings.find(new_tag) !=
|
||||
jump_instruction->tag_mappings.end())
|
||||
throw type_error("technically not a type error: duplicate pattern");
|
||||
|
||||
jump_instruction->tag_mappings[new_tag] =
|
||||
jump_instruction->branches.size();
|
||||
jump_instruction->branches.push_back(std::move(branch_instructions));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& constr_pair : type->constructors) {
|
||||
if(jump_instruction->tag_mappings.find(constr_pair.second.tag) ==
|
||||
jump_instruction->tag_mappings.end())
|
||||
throw type_error("non-total pattern");
|
||||
}
|
||||
}
|
||||
|
||||
void pattern_var::print(std::ostream& to) const {
|
||||
to << var;
|
||||
}
|
||||
|
||||
void pattern_var::match(type_ptr t, type_mgr& mgr, type_env& env) const {
|
||||
env.bind(var, t);
|
||||
}
|
||||
|
||||
void pattern_constr::print(std::ostream& to) const {
|
||||
to << constr;
|
||||
for(auto& param : params) {
|
||||
to << " " << param;
|
||||
}
|
||||
}
|
||||
|
||||
void pattern_constr::match(type_ptr t, type_mgr& mgr, type_env& env) const {
|
||||
type_ptr constructor_type = env.lookup(constr);
|
||||
if(!constructor_type) {
|
||||
throw type_error(std::string("pattern using unknown constructor ") + constr);
|
||||
}
|
||||
|
||||
for(int i = 0; i < params.size(); i++) {
|
||||
type_arr* arr = dynamic_cast<type_arr*>(constructor_type.get());
|
||||
if(!arr) throw type_error("too many parameters in constructor pattern");
|
||||
|
||||
env.bind(params[i], arr->left);
|
||||
constructor_type = arr->right;
|
||||
}
|
||||
|
||||
mgr.unify(t, constructor_type);
|
||||
}
|
||||
197
code/compiler/07/ast.hpp
Normal file
197
code/compiler/07/ast.hpp
Normal file
@@ -0,0 +1,197 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "type.hpp"
|
||||
#include "type_env.hpp"
|
||||
#include "binop.hpp"
|
||||
#include "instruction.hpp"
|
||||
#include "env.hpp"
|
||||
|
||||
struct ast {
|
||||
type_ptr node_type;
|
||||
|
||||
virtual ~ast() = default;
|
||||
|
||||
virtual void print(int indent, std::ostream& to) const = 0;
|
||||
virtual type_ptr typecheck(type_mgr& mgr, const type_env& env) const = 0;
|
||||
virtual void resolve(const type_mgr& mgr) const = 0;
|
||||
virtual void compile(const env_ptr& env,
|
||||
std::vector<instruction_ptr>& into) const = 0;
|
||||
|
||||
type_ptr typecheck_common(type_mgr& mgr, const type_env& env);
|
||||
void resolve_common(const type_mgr& mgr);
|
||||
};
|
||||
|
||||
using ast_ptr = std::unique_ptr<ast>;
|
||||
|
||||
struct pattern {
|
||||
virtual ~pattern() = default;
|
||||
|
||||
virtual void print(std::ostream& to) const = 0;
|
||||
virtual void match(type_ptr t, type_mgr& mgr, type_env& env) const = 0;
|
||||
};
|
||||
|
||||
using pattern_ptr = std::unique_ptr<pattern>;
|
||||
|
||||
struct branch {
|
||||
pattern_ptr pat;
|
||||
ast_ptr expr;
|
||||
|
||||
branch(pattern_ptr p, ast_ptr a)
|
||||
: pat(std::move(p)), expr(std::move(a)) {}
|
||||
};
|
||||
|
||||
using branch_ptr = std::unique_ptr<branch>;
|
||||
|
||||
struct constructor {
|
||||
std::string name;
|
||||
std::vector<std::string> types;
|
||||
int8_t tag;
|
||||
|
||||
constructor(std::string n, std::vector<std::string> ts)
|
||||
: name(std::move(n)), types(std::move(ts)) {}
|
||||
};
|
||||
|
||||
using constructor_ptr = std::unique_ptr<constructor>;
|
||||
|
||||
struct definition {
|
||||
virtual ~definition() = default;
|
||||
|
||||
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 resolve(const type_mgr& mgr) = 0;
|
||||
virtual void compile() = 0;
|
||||
};
|
||||
|
||||
using definition_ptr = std::unique_ptr<definition>;
|
||||
|
||||
struct ast_int : public ast {
|
||||
int value;
|
||||
|
||||
explicit ast_int(int v)
|
||||
: value(v) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_lid : public ast {
|
||||
std::string id;
|
||||
|
||||
explicit ast_lid(std::string i)
|
||||
: id(std::move(i)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_uid : public ast {
|
||||
std::string id;
|
||||
|
||||
explicit ast_uid(std::string i)
|
||||
: id(std::move(i)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_binop : public ast {
|
||||
binop op;
|
||||
ast_ptr left;
|
||||
ast_ptr right;
|
||||
|
||||
ast_binop(binop o, ast_ptr l, ast_ptr r)
|
||||
: op(o), left(std::move(l)), right(std::move(r)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_app : public ast {
|
||||
ast_ptr left;
|
||||
ast_ptr right;
|
||||
|
||||
ast_app(ast_ptr l, ast_ptr r)
|
||||
: left(std::move(l)), right(std::move(r)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_case : public ast {
|
||||
ast_ptr of;
|
||||
std::vector<branch_ptr> branches;
|
||||
|
||||
ast_case(ast_ptr o, std::vector<branch_ptr> b)
|
||||
: of(std::move(o)), branches(std::move(b)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct pattern_var : public pattern {
|
||||
std::string var;
|
||||
|
||||
pattern_var(std::string v)
|
||||
: var(std::move(v)) {}
|
||||
|
||||
void print(std::ostream &to) const;
|
||||
void match(type_ptr t, type_mgr& mgr, type_env& env) const;
|
||||
};
|
||||
|
||||
struct pattern_constr : public pattern {
|
||||
std::string constr;
|
||||
std::vector<std::string> params;
|
||||
|
||||
pattern_constr(std::string c, std::vector<std::string> p)
|
||||
: constr(std::move(c)), params(std::move(p)) {}
|
||||
|
||||
void print(std::ostream &to) const;
|
||||
void match(type_ptr t, type_mgr&, type_env& env) const;
|
||||
};
|
||||
|
||||
struct definition_defn : public definition {
|
||||
std::string name;
|
||||
std::vector<std::string> params;
|
||||
ast_ptr body;
|
||||
|
||||
type_ptr return_type;
|
||||
std::vector<type_ptr> param_types;
|
||||
|
||||
std::vector<instruction_ptr> instructions;
|
||||
|
||||
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)) {
|
||||
|
||||
}
|
||||
|
||||
void typecheck_first(type_mgr& mgr, type_env& env);
|
||||
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr);
|
||||
void compile();
|
||||
};
|
||||
|
||||
struct definition_data : public definition {
|
||||
std::string name;
|
||||
std::vector<constructor_ptr> constructors;
|
||||
|
||||
definition_data(std::string n, std::vector<constructor_ptr> cs)
|
||||
: name(std::move(n)), constructors(std::move(cs)) {}
|
||||
|
||||
void typecheck_first(type_mgr& mgr, type_env& env);
|
||||
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr);
|
||||
void compile();
|
||||
};
|
||||
21
code/compiler/07/binop.cpp
Normal file
21
code/compiler/07/binop.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "binop.hpp"
|
||||
|
||||
std::string op_name(binop op) {
|
||||
switch(op) {
|
||||
case PLUS: return "+";
|
||||
case MINUS: return "-";
|
||||
case TIMES: return "*";
|
||||
case DIVIDE: return "/";
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
|
||||
std::string op_action(binop op) {
|
||||
switch(op) {
|
||||
case PLUS: return "plus";
|
||||
case MINUS: return "minus";
|
||||
case TIMES: return "times";
|
||||
case DIVIDE: return "divide";
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
12
code/compiler/07/binop.hpp
Normal file
12
code/compiler/07/binop.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
enum binop {
|
||||
PLUS,
|
||||
MINUS,
|
||||
TIMES,
|
||||
DIVIDE
|
||||
};
|
||||
|
||||
std::string op_name(binop op);
|
||||
std::string op_action(binop op);
|
||||
83
code/compiler/07/definition.cpp
Normal file
83
code/compiler/07/definition.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "ast.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
void definition_defn::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||
return_type = mgr.new_type();
|
||||
type_ptr full_type = return_type;
|
||||
|
||||
for(auto it = params.rbegin(); it != params.rend(); it++) {
|
||||
type_ptr param_type = mgr.new_type();
|
||||
full_type = type_ptr(new type_arr(param_type, full_type));
|
||||
param_types.push_back(param_type);
|
||||
}
|
||||
|
||||
env.bind(name, full_type);
|
||||
}
|
||||
|
||||
void definition_defn::typecheck_second(type_mgr& mgr, const type_env& env) const {
|
||||
type_env new_env = env.scope();
|
||||
auto param_it = params.begin();
|
||||
auto type_it = param_types.rbegin();
|
||||
|
||||
while(param_it != params.end() && type_it != param_types.rend()) {
|
||||
new_env.bind(*param_it, *type_it);
|
||||
param_it++;
|
||||
type_it++;
|
||||
}
|
||||
|
||||
type_ptr body_type = body->typecheck_common(mgr, new_env);
|
||||
mgr.unify(return_type, body_type);
|
||||
}
|
||||
|
||||
void definition_defn::resolve(const type_mgr& mgr) {
|
||||
type_var* var;
|
||||
body->resolve_common(mgr);
|
||||
|
||||
return_type = mgr.resolve(return_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
for(auto& param_type : param_types) {
|
||||
param_type = mgr.resolve(param_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
}
|
||||
}
|
||||
|
||||
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())));
|
||||
instructions.push_back(instruction_ptr(new instruction_pop(params.size())));
|
||||
}
|
||||
|
||||
void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||
type_data* this_type = new type_data(name);
|
||||
type_ptr return_type = type_ptr(this_type);
|
||||
int next_tag = 0;
|
||||
|
||||
for(auto& constructor : constructors) {
|
||||
constructor->tag = next_tag;
|
||||
this_type->constructors[constructor->name] = { next_tag++ };
|
||||
|
||||
type_ptr full_type = return_type;
|
||||
for(auto it = constructor->types.rbegin(); it != constructor->types.rend(); it++) {
|
||||
type_ptr type = type_ptr(new type_base(*it));
|
||||
full_type = type_ptr(new type_arr(type, full_type));
|
||||
}
|
||||
|
||||
env.bind(constructor->name, full_type);
|
||||
}
|
||||
}
|
||||
|
||||
void definition_data::typecheck_second(type_mgr& mgr, const type_env& env) const {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
void definition_data::resolve(const type_mgr& mgr) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
void definition_data::compile() {
|
||||
|
||||
}
|
||||
23
code/compiler/07/env.cpp
Normal file
23
code/compiler/07/env.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "env.hpp"
|
||||
|
||||
int env_var::get_offset(const std::string& name) const {
|
||||
if(name == this->name) return 0;
|
||||
if(parent) return parent->get_offset(name) + 1;
|
||||
throw 0;
|
||||
}
|
||||
|
||||
bool env_var::has_variable(const std::string& name) const {
|
||||
if(name == this->name) return true;
|
||||
if(parent) return parent->has_variable(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
int env_offset::get_offset(const std::string& name) const {
|
||||
if(parent) return parent->get_offset(name) + offset;
|
||||
throw 0;
|
||||
}
|
||||
|
||||
bool env_offset::has_variable(const std::string& name) const {
|
||||
if(parent) return parent->has_variable(name);
|
||||
return false;
|
||||
}
|
||||
34
code/compiler/07/env.hpp
Normal file
34
code/compiler/07/env.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
struct env {
|
||||
virtual ~env() = default;
|
||||
|
||||
virtual int get_offset(const std::string& name) const = 0;
|
||||
virtual bool has_variable(const std::string& name) const = 0;
|
||||
};
|
||||
|
||||
using env_ptr = std::shared_ptr<env>;
|
||||
|
||||
struct env_var : public env {
|
||||
std::string name;
|
||||
env_ptr parent;
|
||||
|
||||
env_var(std::string& n, env_ptr p)
|
||||
: name(std::move(n)), parent(std::move(p)) {}
|
||||
|
||||
int get_offset(const std::string& name) const;
|
||||
bool has_variable(const std::string& name) const;
|
||||
};
|
||||
|
||||
struct env_offset : public env {
|
||||
int offset;
|
||||
env_ptr parent;
|
||||
|
||||
env_offset(int o, env_ptr p)
|
||||
: offset(o), parent(std::move(p)) {}
|
||||
|
||||
int get_offset(const std::string& name) const;
|
||||
bool has_variable(const std::string& name) const;
|
||||
};
|
||||
5
code/compiler/07/error.cpp
Normal file
5
code/compiler/07/error.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "error.hpp"
|
||||
|
||||
const char* type_error::what() const noexcept {
|
||||
return "an error occured while checking the types of the program";
|
||||
}
|
||||
21
code/compiler/07/error.hpp
Normal file
21
code/compiler/07/error.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <exception>
|
||||
#include "type.hpp"
|
||||
|
||||
struct type_error : std::exception {
|
||||
std::string description;
|
||||
|
||||
type_error(std::string d)
|
||||
: description(std::move(d)) {}
|
||||
|
||||
const char* what() const noexcept override;
|
||||
};
|
||||
|
||||
struct unification_error : public type_error {
|
||||
type_ptr left;
|
||||
type_ptr right;
|
||||
|
||||
unification_error(type_ptr l, type_ptr r)
|
||||
: left(std::move(l)), right(std::move(r)),
|
||||
type_error("failed to unify types") {}
|
||||
};
|
||||
2
code/compiler/07/examples/bad1.txt
Normal file
2
code/compiler/07/examples/bad1.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
data Bool = { True, False }
|
||||
defn main = { 3 + True }
|
||||
1
code/compiler/07/examples/bad2.txt
Normal file
1
code/compiler/07/examples/bad2.txt
Normal file
@@ -0,0 +1 @@
|
||||
defn main = { 1 2 3 4 5 }
|
||||
8
code/compiler/07/examples/bad3.txt
Normal file
8
code/compiler/07/examples/bad3.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
data List = { Nil, Cons Int List }
|
||||
|
||||
defn head l = {
|
||||
case l of {
|
||||
Nil -> { 0 }
|
||||
Cons x y z -> { x }
|
||||
}
|
||||
}
|
||||
31
code/compiler/07/examples/runtime1.c
Normal file
31
code/compiler/07/examples/runtime1.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "../runtime.h"
|
||||
|
||||
void f_add(struct stack* s) {
|
||||
struct node_num* left = (struct node_num*) eval(stack_peek(s, 0));
|
||||
struct node_num* right = (struct node_num*) eval(stack_peek(s, 1));
|
||||
stack_push(s, (struct node_base*) alloc_num(left->value + right->value));
|
||||
}
|
||||
|
||||
void f_main(struct stack* s) {
|
||||
// PushInt 320
|
||||
stack_push(s, (struct node_base*) alloc_num(320));
|
||||
|
||||
// PushInt 6
|
||||
stack_push(s, (struct node_base*) alloc_num(6));
|
||||
|
||||
// PushGlobal f_add (the function for +)
|
||||
stack_push(s, (struct node_base*) alloc_global(f_add, 2));
|
||||
|
||||
struct node_base* left;
|
||||
struct node_base* right;
|
||||
|
||||
// MkApp
|
||||
left = stack_pop(s);
|
||||
right = stack_pop(s);
|
||||
stack_push(s, (struct node_base*) alloc_app(left, right));
|
||||
|
||||
// MkApp
|
||||
left = stack_pop(s);
|
||||
right = stack_pop(s);
|
||||
stack_push(s, (struct node_base*) alloc_app(left, right));
|
||||
}
|
||||
2
code/compiler/07/examples/works1.txt
Normal file
2
code/compiler/07/examples/works1.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
defn main = { plus 320 6 }
|
||||
defn plus x y = { x + y }
|
||||
3
code/compiler/07/examples/works2.txt
Normal file
3
code/compiler/07/examples/works2.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
defn add x y = { x + y }
|
||||
defn double x = { add x x }
|
||||
defn main = { double 163 }
|
||||
7
code/compiler/07/examples/works3.txt
Normal file
7
code/compiler/07/examples/works3.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
data List = { Nil, Cons Int List }
|
||||
defn length l = {
|
||||
case l of {
|
||||
Nil -> { 0 }
|
||||
Cons x xs -> { 1 + length xs }
|
||||
}
|
||||
}
|
||||
83
code/compiler/07/instruction.cpp
Normal file
83
code/compiler/07/instruction.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "instruction.hpp"
|
||||
|
||||
static void print_indent(int n, std::ostream& to) {
|
||||
while(n--) to << " ";
|
||||
}
|
||||
|
||||
void instruction_pushint::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "PushInt(" << value << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_pushglobal::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "PushGlobal(" << name << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_push::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Push(" << offset << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_pop::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Pop(" << count << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_mkapp::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "MkApp()" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_update::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Update(" << offset << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_pack::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Pack(" << tag << ", " << size << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_split::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Split()" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_jump::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Jump(" << std::endl;
|
||||
for(auto& instruction_set : branches) {
|
||||
for(auto& instruction : instruction_set) {
|
||||
instruction->print(indent + 2, to);
|
||||
}
|
||||
to << std::endl;
|
||||
}
|
||||
print_indent(indent, to);
|
||||
to << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_slide::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Slide(" << offset << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_binop::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "BinOp(" << op_action(op) << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_eval::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Eval()" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_alloc::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Alloc(" << amount << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_unwind::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Unwind()" << std::endl;
|
||||
}
|
||||
120
code/compiler/07/instruction.hpp
Normal file
120
code/compiler/07/instruction.hpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include "binop.hpp"
|
||||
|
||||
struct instruction {
|
||||
virtual ~instruction() = default;
|
||||
|
||||
virtual void print(int indent, std::ostream& to) const = 0;
|
||||
};
|
||||
|
||||
using instruction_ptr = std::unique_ptr<instruction>;
|
||||
|
||||
struct instruction_pushint : public instruction {
|
||||
int value;
|
||||
|
||||
instruction_pushint(int v)
|
||||
: value(v) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_pushglobal : public instruction {
|
||||
std::string name;
|
||||
|
||||
instruction_pushglobal(std::string n)
|
||||
: name(std::move(n)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_push : public instruction {
|
||||
int offset;
|
||||
|
||||
instruction_push(int o)
|
||||
: offset(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_pop : public instruction {
|
||||
int count;
|
||||
|
||||
instruction_pop(int c)
|
||||
: count(c) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_mkapp : public instruction {
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_update : public instruction {
|
||||
int offset;
|
||||
|
||||
instruction_update(int o)
|
||||
: offset(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_pack : public instruction {
|
||||
int tag;
|
||||
int size;
|
||||
|
||||
instruction_pack(int t, int s)
|
||||
: tag(t), size(s) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_split : public instruction {
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_jump : public instruction {
|
||||
std::vector<std::vector<instruction_ptr>> branches;
|
||||
std::map<int, int> tag_mappings;
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_slide : public instruction {
|
||||
int offset;
|
||||
|
||||
instruction_slide(int o)
|
||||
: offset(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_binop : public instruction {
|
||||
binop op;
|
||||
|
||||
instruction_binop(binop o)
|
||||
: op(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_eval : public instruction {
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_alloc : public instruction {
|
||||
int amount;
|
||||
|
||||
instruction_alloc(int a)
|
||||
: amount(a) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct instruction_unwind : public instruction {
|
||||
void print(int indent, std::ostream& to) const;
|
||||
};
|
||||
88
code/compiler/07/main.cpp
Normal file
88
code/compiler/07/main.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "ast.hpp"
|
||||
#include <iostream>
|
||||
#include "parser.hpp"
|
||||
#include "error.hpp"
|
||||
#include "type.hpp"
|
||||
|
||||
void yy::parser::error(const std::string& msg) {
|
||||
std::cout << "An error occured: " << msg << std::endl;
|
||||
}
|
||||
|
||||
extern std::vector<definition_ptr> program;
|
||||
|
||||
void typecheck_program(
|
||||
const std::vector<definition_ptr>& prog,
|
||||
type_mgr& mgr, type_env& env) {
|
||||
type_ptr int_type = type_ptr(new type_base("Int"));
|
||||
type_ptr binop_type = type_ptr(new type_arr(
|
||||
int_type,
|
||||
type_ptr(new type_arr(int_type, int_type))));
|
||||
|
||||
env.bind("+", binop_type);
|
||||
env.bind("-", binop_type);
|
||||
env.bind("*", binop_type);
|
||||
env.bind("/", binop_type);
|
||||
|
||||
for(auto& def : prog) {
|
||||
def->typecheck_first(mgr, env);
|
||||
}
|
||||
|
||||
for(auto& def : prog) {
|
||||
def->typecheck_second(mgr, env);
|
||||
}
|
||||
|
||||
for(auto& pair : env.names) {
|
||||
std::cout << pair.first << ": ";
|
||||
pair.second->print(mgr, std::cout);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
for(auto& def : prog) {
|
||||
def->resolve(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
yy::parser parser;
|
||||
type_mgr mgr;
|
||||
type_env env;
|
||||
|
||||
parser.parse();
|
||||
for(auto& definition : program) {
|
||||
definition_defn* def = dynamic_cast<definition_defn*>(definition.get());
|
||||
if(!def) continue;
|
||||
|
||||
std::cout << def->name;
|
||||
for(auto& param : def->params) std::cout << " " << param;
|
||||
std::cout << ":" << std::endl;
|
||||
|
||||
def->body->print(1, std::cout);
|
||||
}
|
||||
try {
|
||||
typecheck_program(program, mgr, env);
|
||||
compile_program(program);
|
||||
} catch(unification_error& err) {
|
||||
std::cout << "failed to unify types: " << std::endl;
|
||||
std::cout << " (1) \033[34m";
|
||||
err.left->print(mgr, std::cout);
|
||||
std::cout << "\033[0m" << std::endl;
|
||||
std::cout << " (2) \033[32m";
|
||||
err.right->print(mgr, std::cout);
|
||||
std::cout << "\033[0m" << std::endl;
|
||||
} catch(type_error& err) {
|
||||
std::cout << "failed to type check program: " << err.description << std::endl;
|
||||
}
|
||||
}
|
||||
140
code/compiler/07/parser.y
Normal file
140
code/compiler/07/parser.y
Normal file
@@ -0,0 +1,140 @@
|
||||
%{
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "ast.hpp"
|
||||
#include "parser.hpp"
|
||||
|
||||
std::vector<definition_ptr> program;
|
||||
extern yy::parser::symbol_type yylex();
|
||||
|
||||
%}
|
||||
|
||||
%token PLUS
|
||||
%token TIMES
|
||||
%token MINUS
|
||||
%token DIVIDE
|
||||
%token <int> INT
|
||||
%token DEFN
|
||||
%token DATA
|
||||
%token CASE
|
||||
%token OF
|
||||
%token OCURLY
|
||||
%token CCURLY
|
||||
%token OPAREN
|
||||
%token CPAREN
|
||||
%token COMMA
|
||||
%token ARROW
|
||||
%token EQUAL
|
||||
%token <std::string> LID
|
||||
%token <std::string> UID
|
||||
|
||||
%language "c++"
|
||||
%define api.value.type variant
|
||||
%define api.token.constructor
|
||||
|
||||
%type <std::vector<std::string>> lowercaseParams uppercaseParams
|
||||
%type <std::vector<definition_ptr>> program definitions
|
||||
%type <std::vector<branch_ptr>> branches
|
||||
%type <std::vector<constructor_ptr>> constructors
|
||||
%type <ast_ptr> aAdd aMul case app appBase
|
||||
%type <definition_ptr> definition defn data
|
||||
%type <branch_ptr> branch
|
||||
%type <pattern_ptr> pattern
|
||||
%type <constructor_ptr> constructor
|
||||
|
||||
%start program
|
||||
|
||||
%%
|
||||
|
||||
program
|
||||
: definitions { program = std::move($1); }
|
||||
;
|
||||
|
||||
definitions
|
||||
: definitions definition { $$ = std::move($1); $$.push_back(std::move($2)); }
|
||||
| definition { $$ = std::vector<definition_ptr>(); $$.push_back(std::move($1)); }
|
||||
;
|
||||
|
||||
definition
|
||||
: defn { $$ = std::move($1); }
|
||||
| data { $$ = std::move($1); }
|
||||
;
|
||||
|
||||
defn
|
||||
: DEFN LID lowercaseParams EQUAL OCURLY aAdd CCURLY
|
||||
{ $$ = definition_ptr(
|
||||
new definition_defn(std::move($2), std::move($3), std::move($6))); }
|
||||
;
|
||||
|
||||
lowercaseParams
|
||||
: %empty { $$ = std::vector<std::string>(); }
|
||||
| lowercaseParams LID { $$ = std::move($1); $$.push_back(std::move($2)); }
|
||||
;
|
||||
|
||||
uppercaseParams
|
||||
: %empty { $$ = std::vector<std::string>(); }
|
||||
| uppercaseParams UID { $$ = std::move($1); $$.push_back(std::move($2)); }
|
||||
;
|
||||
|
||||
aAdd
|
||||
: aAdd PLUS aMul { $$ = ast_ptr(new ast_binop(PLUS, std::move($1), std::move($3))); }
|
||||
| aAdd MINUS aMul { $$ = ast_ptr(new ast_binop(MINUS, std::move($1), std::move($3))); }
|
||||
| aMul { $$ = std::move($1); }
|
||||
;
|
||||
|
||||
aMul
|
||||
: aMul TIMES app { $$ = ast_ptr(new ast_binop(TIMES, std::move($1), std::move($3))); }
|
||||
| aMul DIVIDE app { $$ = ast_ptr(new ast_binop(DIVIDE, std::move($1), std::move($3))); }
|
||||
| app { $$ = std::move($1); }
|
||||
;
|
||||
|
||||
app
|
||||
: app appBase { $$ = ast_ptr(new ast_app(std::move($1), std::move($2))); }
|
||||
| appBase { $$ = std::move($1); }
|
||||
;
|
||||
|
||||
appBase
|
||||
: INT { $$ = ast_ptr(new ast_int($1)); }
|
||||
| LID { $$ = ast_ptr(new ast_lid(std::move($1))); }
|
||||
| UID { $$ = ast_ptr(new ast_uid(std::move($1))); }
|
||||
| OPAREN aAdd CPAREN { $$ = std::move($2); }
|
||||
| case { $$ = std::move($1); }
|
||||
;
|
||||
|
||||
case
|
||||
: CASE aAdd OF OCURLY branches CCURLY
|
||||
{ $$ = ast_ptr(new ast_case(std::move($2), std::move($5))); }
|
||||
;
|
||||
|
||||
branches
|
||||
: branches branch { $$ = std::move($1); $$.push_back(std::move($2)); }
|
||||
| branch { $$ = std::vector<branch_ptr>(); $$.push_back(std::move($1));}
|
||||
;
|
||||
|
||||
branch
|
||||
: pattern ARROW OCURLY aAdd CCURLY
|
||||
{ $$ = branch_ptr(new branch(std::move($1), std::move($4))); }
|
||||
;
|
||||
|
||||
pattern
|
||||
: LID { $$ = pattern_ptr(new pattern_var(std::move($1))); }
|
||||
| UID lowercaseParams
|
||||
{ $$ = pattern_ptr(new pattern_constr(std::move($1), std::move($2))); }
|
||||
;
|
||||
|
||||
data
|
||||
: DATA UID EQUAL OCURLY constructors CCURLY
|
||||
{ $$ = definition_ptr(new definition_data(std::move($2), std::move($5))); }
|
||||
;
|
||||
|
||||
constructors
|
||||
: constructors COMMA constructor { $$ = std::move($1); $$.push_back(std::move($3)); }
|
||||
| constructor
|
||||
{ $$ = std::vector<constructor_ptr>(); $$.push_back(std::move($1)); }
|
||||
;
|
||||
|
||||
constructor
|
||||
: UID uppercaseParams
|
||||
{ $$ = constructor_ptr(new constructor(std::move($1), std::move($2))); }
|
||||
;
|
||||
|
||||
159
code/compiler/07/runtime.c
Normal file
159
code/compiler/07/runtime.c
Normal file
@@ -0,0 +1,159 @@
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
#include "runtime.h"
|
||||
|
||||
struct node_base* alloc_node() {
|
||||
struct node_base* new_node = malloc(sizeof(struct node_app));
|
||||
assert(new_node != NULL);
|
||||
return new_node;
|
||||
}
|
||||
|
||||
struct node_app* alloc_app(struct node_base* l, struct node_base* r) {
|
||||
struct node_app* node = (struct node_app*) alloc_node();
|
||||
node->base.tag = NODE_APP;
|
||||
node->left = l;
|
||||
node->right = r;
|
||||
return node;
|
||||
}
|
||||
|
||||
struct node_num* alloc_num(int32_t n) {
|
||||
struct node_num* node = (struct node_num*) alloc_node();
|
||||
node->base.tag = NODE_NUM;
|
||||
node->value = n;
|
||||
return node;
|
||||
}
|
||||
|
||||
struct node_global* alloc_global(void (*f)(struct stack*), int32_t a) {
|
||||
struct node_global* node = (struct node_global*) alloc_node();
|
||||
node->base.tag = NODE_GLOBAL;
|
||||
node->arity = a;
|
||||
node->function = f;
|
||||
return node;
|
||||
}
|
||||
|
||||
struct node_ind* alloc_ind(struct node_base* n) {
|
||||
struct node_ind* node = (struct node_ind*) alloc_node();
|
||||
node->base.tag = NODE_IND;
|
||||
node->next = n;
|
||||
return node;
|
||||
}
|
||||
|
||||
void stack_init(struct stack* s) {
|
||||
s->size = 4;
|
||||
s->count = 0;
|
||||
s->data = malloc(sizeof(*s->data) * s->size);
|
||||
assert(s->data != NULL);
|
||||
}
|
||||
|
||||
void stack_free(struct stack* s) {
|
||||
free(s->data);
|
||||
}
|
||||
|
||||
void stack_push(struct stack* s, struct node_base* n) {
|
||||
while(s->count >= s->size) {
|
||||
s->data = realloc(s->data, sizeof(*s->data) * (s->size *= 2));
|
||||
assert(s->data != NULL);
|
||||
}
|
||||
s->data[s->count++] = n;
|
||||
}
|
||||
|
||||
struct node_base* stack_pop(struct stack* s) {
|
||||
assert(s->count > 0);
|
||||
return s->data[--s->count];
|
||||
}
|
||||
|
||||
struct node_base* stack_peek(struct stack* s, size_t o) {
|
||||
assert(s->count > o);
|
||||
return s->data[s->count - o - 1];
|
||||
}
|
||||
|
||||
void stack_popn(struct stack* s, size_t n) {
|
||||
assert(s->count >= n);
|
||||
s->count -= n;
|
||||
}
|
||||
|
||||
void stack_slide(struct stack* s, size_t n) {
|
||||
assert(s->count > n);
|
||||
s->data[s->count - n - 1] = s->data[s->count - 1];
|
||||
s->count -= n;
|
||||
}
|
||||
|
||||
void stack_update(struct stack* s, size_t o) {
|
||||
assert(s->count > o + 1);
|
||||
struct node_ind* ind = (struct node_ind*) s->data[s->count - o - 2];
|
||||
ind->base.tag = NODE_IND;
|
||||
ind->next = s->data[s->count -= 1];
|
||||
}
|
||||
|
||||
void stack_alloc(struct stack* s, size_t o) {
|
||||
while(o--) {
|
||||
stack_push(s, (struct node_base*) alloc_ind(NULL));
|
||||
}
|
||||
}
|
||||
|
||||
void stack_pack(struct stack* s, size_t n, int8_t t) {
|
||||
assert(s->count >= n);
|
||||
|
||||
struct node_base** data = malloc(sizeof(*data) * n);
|
||||
assert(data != NULL);
|
||||
memcpy(data, &s->data[s->count - 1 - n], n * sizeof(*data));
|
||||
|
||||
struct node_data* new_node = (struct node_data*) alloc_node();
|
||||
new_node->array = data;
|
||||
new_node->base.tag = NODE_DATA;
|
||||
new_node->tag = t;
|
||||
|
||||
stack_popn(s, n);
|
||||
stack_push(s, (struct node_base*) new_node);
|
||||
}
|
||||
|
||||
void stack_split(struct stack* s, size_t n) {
|
||||
struct node_data* node = (struct node_data*) stack_pop(s);
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
stack_push(s, node->array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void unwind(struct stack* s) {
|
||||
while(1) {
|
||||
struct node_base* peek = stack_peek(s, 0);
|
||||
if(peek->tag == NODE_APP) {
|
||||
struct node_app* n = (struct node_app*) peek;
|
||||
stack_push(s, n->left);
|
||||
} else if(peek->tag == NODE_GLOBAL) {
|
||||
struct node_global* n = (struct node_global*) peek;
|
||||
assert(s->count > n->arity);
|
||||
|
||||
for(size_t i = 1; i <= n->arity; i++) {
|
||||
s->data[s->count - i]
|
||||
= ((struct node_app*) s->data[s->count - i - 1])->right;
|
||||
}
|
||||
|
||||
n->function(s);
|
||||
} else if(peek->tag == NODE_IND) {
|
||||
struct node_ind* n = (struct node_ind*) peek;
|
||||
stack_pop(s);
|
||||
stack_push(s, n->next);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct node_base* eval(struct node_base* n) {
|
||||
struct stack program_stack;
|
||||
stack_init(&program_stack);
|
||||
stack_push(&program_stack, n);
|
||||
unwind(&program_stack);
|
||||
struct node_base* result = stack_pop(&program_stack);
|
||||
stack_free(&program_stack);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern void f_main(struct stack* s);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
struct node_global* first_node = alloc_global(f_main, 0);
|
||||
struct node_base* result = eval((struct node_base*) first_node);
|
||||
}
|
||||
70
code/compiler/07/runtime.h
Normal file
70
code/compiler/07/runtime.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#include <stdlib.h>
|
||||
|
||||
struct stack;
|
||||
|
||||
enum node_tag {
|
||||
NODE_APP,
|
||||
NODE_NUM,
|
||||
NODE_GLOBAL,
|
||||
NODE_IND,
|
||||
NODE_DATA
|
||||
};
|
||||
|
||||
struct node_base {
|
||||
enum node_tag tag;
|
||||
};
|
||||
|
||||
struct node_app {
|
||||
struct node_base base;
|
||||
struct node_base* left;
|
||||
struct node_base* right;
|
||||
};
|
||||
|
||||
struct node_num {
|
||||
struct node_base base;
|
||||
int32_t value;
|
||||
};
|
||||
|
||||
struct node_global {
|
||||
struct node_base base;
|
||||
int32_t arity;
|
||||
void (*function)(struct stack*);
|
||||
};
|
||||
|
||||
struct node_ind {
|
||||
struct node_base base;
|
||||
struct node_base* next;
|
||||
};
|
||||
|
||||
struct node_data {
|
||||
struct node_base base;
|
||||
int8_t tag;
|
||||
struct node_base** array;
|
||||
};
|
||||
|
||||
struct node_base* alloc_node();
|
||||
struct node_app* alloc_app(struct node_base* l, struct node_base* r);
|
||||
struct node_num* alloc_num(int32_t n);
|
||||
struct node_global* alloc_global(void (*f)(struct stack*), int32_t a);
|
||||
struct node_ind* alloc_ind(struct node_base* n);
|
||||
|
||||
struct stack {
|
||||
size_t size;
|
||||
size_t count;
|
||||
struct node_base** data;
|
||||
};
|
||||
|
||||
void stack_init(struct stack* s);
|
||||
void stack_free(struct stack* s);
|
||||
void stack_push(struct stack* s, struct node_base* n);
|
||||
struct node_base* stack_pop(struct stack* s);
|
||||
struct node_base* stack_peek(struct stack* s, size_t o);
|
||||
void stack_popn(struct stack* s, size_t n);
|
||||
void stack_slide(struct stack* s, size_t n);
|
||||
void stack_update(struct stack* s, size_t o);
|
||||
void stack_alloc(struct stack* s, size_t o);
|
||||
void stack_pack(struct stack* s, size_t n, int8_t t);
|
||||
void stack_split(struct stack* s, size_t n);
|
||||
|
||||
struct node_base* eval(struct node_base* n);
|
||||
34
code/compiler/07/scanner.l
Normal file
34
code/compiler/07/scanner.l
Normal file
@@ -0,0 +1,34 @@
|
||||
%option noyywrap
|
||||
|
||||
%{
|
||||
#include <iostream>
|
||||
#include "ast.hpp"
|
||||
#include "parser.hpp"
|
||||
|
||||
#define YY_DECL yy::parser::symbol_type yylex()
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
|
||||
[ \n]+ {}
|
||||
\+ { return yy::parser::make_PLUS(); }
|
||||
\* { return yy::parser::make_TIMES(); }
|
||||
- { return yy::parser::make_MINUS(); }
|
||||
\/ { return yy::parser::make_DIVIDE(); }
|
||||
[0-9]+ { return yy::parser::make_INT(atoi(yytext)); }
|
||||
defn { return yy::parser::make_DEFN(); }
|
||||
data { return yy::parser::make_DATA(); }
|
||||
case { return yy::parser::make_CASE(); }
|
||||
of { return yy::parser::make_OF(); }
|
||||
\{ { return yy::parser::make_OCURLY(); }
|
||||
\} { return yy::parser::make_CCURLY(); }
|
||||
\( { return yy::parser::make_OPAREN(); }
|
||||
\) { return yy::parser::make_CPAREN(); }
|
||||
, { return yy::parser::make_COMMA(); }
|
||||
-> { return yy::parser::make_ARROW(); }
|
||||
= { return yy::parser::make_EQUAL(); }
|
||||
[a-z][a-zA-Z]* { return yy::parser::make_LID(std::string(yytext)); }
|
||||
[A-Z][a-zA-Z]* { return yy::parser::make_UID(std::string(yytext)); }
|
||||
|
||||
%%
|
||||
99
code/compiler/07/type.cpp
Normal file
99
code/compiler/07/type.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "type.hpp"
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include "error.hpp"
|
||||
|
||||
void type_var::print(const type_mgr& mgr, std::ostream& to) const {
|
||||
auto it = mgr.types.find(name);
|
||||
if(it != mgr.types.end()) {
|
||||
it->second->print(mgr, to);
|
||||
} else {
|
||||
to << name;
|
||||
}
|
||||
}
|
||||
|
||||
void type_base::print(const type_mgr& mgr, std::ostream& to) const {
|
||||
to << name;
|
||||
}
|
||||
|
||||
void type_arr::print(const type_mgr& mgr, std::ostream& to) const {
|
||||
left->print(mgr, to);
|
||||
to << " -> (";
|
||||
right->print(mgr, to);
|
||||
to << ")";
|
||||
}
|
||||
|
||||
std::string type_mgr::new_type_name() {
|
||||
int temp = last_id++;
|
||||
std::string str = "";
|
||||
|
||||
while(temp != -1) {
|
||||
str += (char) ('a' + (temp % 26));
|
||||
temp = temp / 26 - 1;
|
||||
}
|
||||
|
||||
std::reverse(str.begin(), str.end());
|
||||
return str;
|
||||
}
|
||||
|
||||
type_ptr type_mgr::new_type() {
|
||||
return type_ptr(new type_var(new_type_name()));
|
||||
}
|
||||
|
||||
type_ptr type_mgr::new_arrow_type() {
|
||||
return type_ptr(new type_arr(new_type(), new_type()));
|
||||
}
|
||||
|
||||
type_ptr type_mgr::resolve(type_ptr t, type_var*& var) const {
|
||||
type_var* cast;
|
||||
|
||||
var = nullptr;
|
||||
while((cast = dynamic_cast<type_var*>(t.get()))) {
|
||||
auto it = types.find(cast->name);
|
||||
|
||||
if(it == types.end()) {
|
||||
var = cast;
|
||||
break;
|
||||
}
|
||||
t = it->second;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void type_mgr::unify(type_ptr l, type_ptr r) {
|
||||
type_var* lvar;
|
||||
type_var* rvar;
|
||||
type_arr* larr;
|
||||
type_arr* rarr;
|
||||
type_base* lid;
|
||||
type_base* rid;
|
||||
|
||||
l = resolve(l, lvar);
|
||||
r = resolve(r, rvar);
|
||||
|
||||
if(lvar) {
|
||||
bind(lvar->name, r);
|
||||
return;
|
||||
} else if(rvar) {
|
||||
bind(rvar->name, l);
|
||||
return;
|
||||
} else if((larr = dynamic_cast<type_arr*>(l.get())) &&
|
||||
(rarr = dynamic_cast<type_arr*>(r.get()))) {
|
||||
unify(larr->left, rarr->left);
|
||||
unify(larr->right, rarr->right);
|
||||
return;
|
||||
} else if((lid = dynamic_cast<type_base*>(l.get())) &&
|
||||
(rid = dynamic_cast<type_base*>(r.get()))) {
|
||||
if(lid->name == rid->name) return;
|
||||
}
|
||||
|
||||
throw unification_error(l, r);
|
||||
}
|
||||
|
||||
void type_mgr::bind(const std::string& s, type_ptr t) {
|
||||
type_var* other = dynamic_cast<type_var*>(t.get());
|
||||
|
||||
if(other && other->name == s) return;
|
||||
types[s] = t;
|
||||
}
|
||||
65
code/compiler/07/type.hpp
Normal file
65
code/compiler/07/type.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
struct type_mgr;
|
||||
|
||||
struct type {
|
||||
virtual ~type() = default;
|
||||
|
||||
virtual void print(const type_mgr& mgr, std::ostream& to) const = 0;
|
||||
};
|
||||
|
||||
using type_ptr = std::shared_ptr<type>;
|
||||
|
||||
struct type_var : public type {
|
||||
std::string name;
|
||||
|
||||
type_var(std::string n)
|
||||
: name(std::move(n)) {}
|
||||
|
||||
void print(const type_mgr& mgr, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct type_base : public type {
|
||||
std::string name;
|
||||
|
||||
type_base(std::string n)
|
||||
: name(std::move(n)) {}
|
||||
|
||||
void print(const type_mgr& mgr, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct type_data : public type_base {
|
||||
struct constructor {
|
||||
int tag;
|
||||
};
|
||||
|
||||
std::map<std::string, constructor> constructors;
|
||||
|
||||
type_data(std::string n)
|
||||
: type_base(std::move(n)) {}
|
||||
};
|
||||
|
||||
struct type_arr : public type {
|
||||
type_ptr left;
|
||||
type_ptr right;
|
||||
|
||||
type_arr(type_ptr l, type_ptr r)
|
||||
: left(std::move(l)), right(std::move(r)) {}
|
||||
|
||||
void print(const type_mgr& mgr, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct type_mgr {
|
||||
int last_id = 0;
|
||||
std::map<std::string, type_ptr> types;
|
||||
|
||||
std::string new_type_name();
|
||||
type_ptr new_type();
|
||||
type_ptr new_arrow_type();
|
||||
|
||||
void unify(type_ptr l, type_ptr r);
|
||||
type_ptr resolve(type_ptr t, type_var*& var) const;
|
||||
void bind(const std::string& s, type_ptr t);
|
||||
};
|
||||
16
code/compiler/07/type_env.cpp
Normal file
16
code/compiler/07/type_env.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "type_env.hpp"
|
||||
|
||||
type_ptr type_env::lookup(const std::string& name) const {
|
||||
auto it = names.find(name);
|
||||
if(it != names.end()) return it->second;
|
||||
if(parent) return parent->lookup(name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void type_env::bind(const std::string& name, type_ptr t) {
|
||||
names[name] = t;
|
||||
}
|
||||
|
||||
type_env type_env::scope() const {
|
||||
return type_env(this);
|
||||
}
|
||||
16
code/compiler/07/type_env.hpp
Normal file
16
code/compiler/07/type_env.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include "type.hpp"
|
||||
|
||||
struct type_env {
|
||||
std::map<std::string, type_ptr> names;
|
||||
type_env const* parent = nullptr;
|
||||
|
||||
type_env(type_env const* p)
|
||||
: parent(p) {}
|
||||
type_env() : type_env(nullptr) {}
|
||||
|
||||
type_ptr lookup(const std::string& name) const;
|
||||
void bind(const std::string& name, type_ptr t);
|
||||
type_env scope() const;
|
||||
};
|
||||
42
code/compiler/08/CMakeLists.txt
Normal file
42
code/compiler/08/CMakeLists.txt
Normal file
@@ -0,0 +1,42 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(compiler)
|
||||
|
||||
# Find all the required packages
|
||||
find_package(BISON)
|
||||
find_package(FLEX)
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
|
||||
# Set up the flex and bison targets
|
||||
bison_target(parser
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/parser.y
|
||||
${CMAKE_CURRENT_BINARY_DIR}/parser.cpp
|
||||
COMPILE_FLAGS "-d")
|
||||
flex_target(scanner
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/scanner.l
|
||||
${CMAKE_CURRENT_BINARY_DIR}/scanner.cpp)
|
||||
add_flex_bison_dependency(scanner parser)
|
||||
|
||||
# Find all the relevant LLVM components
|
||||
llvm_map_components_to_libnames(LLVM_LIBS core x86asmparser x86codegen)
|
||||
|
||||
# Create compiler executable
|
||||
add_executable(compiler
|
||||
ast.cpp ast.hpp definition.cpp
|
||||
llvm_context.cpp llvm_context.hpp
|
||||
type_env.cpp type_env.hpp
|
||||
env.cpp env.hpp
|
||||
type.cpp type.hpp
|
||||
error.cpp error.hpp
|
||||
binop.cpp binop.hpp
|
||||
instruction.cpp instruction.hpp
|
||||
${BISON_parser_OUTPUTS}
|
||||
${FLEX_scanner_OUTPUTS}
|
||||
main.cpp
|
||||
)
|
||||
|
||||
# Configure compiler executable
|
||||
target_include_directories(compiler PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(compiler PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_include_directories(compiler PUBLIC ${LLVM_INCLUDE_DIRS})
|
||||
target_compile_definitions(compiler PUBLIC ${LLVM_DEFINITIONS})
|
||||
target_link_libraries(compiler ${LLVM_LIBS})
|
||||
264
code/compiler/08/ast.cpp
Normal file
264
code/compiler/08/ast.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
#include "ast.hpp"
|
||||
#include <ostream>
|
||||
#include "binop.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
static void print_indent(int n, std::ostream& to) {
|
||||
while(n--) to << " ";
|
||||
}
|
||||
|
||||
type_ptr ast::typecheck_common(type_mgr& mgr, const type_env& env) {
|
||||
node_type = typecheck(mgr, env);
|
||||
return node_type;
|
||||
}
|
||||
|
||||
void ast::resolve_common(const type_mgr& mgr) {
|
||||
type_var* var;
|
||||
type_ptr resolved_type = mgr.resolve(node_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
|
||||
resolve(mgr);
|
||||
node_type = std::move(resolved_type);
|
||||
}
|
||||
|
||||
void ast_int::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "INT: " << value << std::endl;
|
||||
}
|
||||
|
||||
type_ptr ast_int::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return type_ptr(new type_base("Int"));
|
||||
}
|
||||
|
||||
void ast_int::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_int::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
into.push_back(instruction_ptr(new instruction_pushint(value)));
|
||||
}
|
||||
|
||||
void ast_lid::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "LID: " << id << std::endl;
|
||||
}
|
||||
|
||||
type_ptr ast_lid::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return env.lookup(id);
|
||||
}
|
||||
|
||||
void ast_lid::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_lid::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)));
|
||||
}
|
||||
|
||||
void ast_uid::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "UID: " << id << std::endl;
|
||||
}
|
||||
|
||||
type_ptr ast_uid::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return env.lookup(id);
|
||||
}
|
||||
|
||||
void ast_uid::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_uid::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
into.push_back(instruction_ptr(new instruction_pushglobal(id)));
|
||||
}
|
||||
|
||||
void ast_binop::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "BINOP: " << op_name(op) << std::endl;
|
||||
left->print(indent + 1, to);
|
||||
right->print(indent + 1, to);
|
||||
}
|
||||
|
||||
type_ptr ast_binop::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_ptr ltype = left->typecheck_common(mgr, env);
|
||||
type_ptr rtype = right->typecheck_common(mgr, env);
|
||||
type_ptr ftype = env.lookup(op_name(op));
|
||||
if(!ftype) throw type_error(std::string("unknown binary operator ") + op_name(op));
|
||||
|
||||
type_ptr return_type = mgr.new_type();
|
||||
type_ptr arrow_one = type_ptr(new type_arr(rtype, return_type));
|
||||
type_ptr arrow_two = type_ptr(new type_arr(ltype, arrow_one));
|
||||
|
||||
mgr.unify(arrow_two, ftype);
|
||||
return return_type;
|
||||
}
|
||||
|
||||
void ast_binop::resolve(const type_mgr& mgr) const {
|
||||
left->resolve_common(mgr);
|
||||
right->resolve_common(mgr);
|
||||
}
|
||||
|
||||
void ast_binop::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
right->compile(env, into);
|
||||
left->compile(env_ptr(new env_offset(1, env)), into);
|
||||
|
||||
into.push_back(instruction_ptr(new instruction_pushglobal(op_action(op))));
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
}
|
||||
|
||||
void ast_app::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "APP:" << std::endl;
|
||||
left->print(indent + 1, to);
|
||||
right->print(indent + 1, to);
|
||||
}
|
||||
|
||||
type_ptr ast_app::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_ptr ltype = left->typecheck_common(mgr, env);
|
||||
type_ptr rtype = right->typecheck_common(mgr, env);
|
||||
|
||||
type_ptr return_type = mgr.new_type();
|
||||
type_ptr arrow = type_ptr(new type_arr(rtype, return_type));
|
||||
mgr.unify(arrow, ltype);
|
||||
return return_type;
|
||||
}
|
||||
|
||||
void ast_app::resolve(const type_mgr& mgr) const {
|
||||
left->resolve_common(mgr);
|
||||
right->resolve_common(mgr);
|
||||
}
|
||||
|
||||
void ast_app::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
right->compile(env, into);
|
||||
left->compile(env_ptr(new env_offset(1, env)), into);
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
}
|
||||
|
||||
void ast_case::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "CASE: " << std::endl;
|
||||
for(auto& branch : branches) {
|
||||
print_indent(indent + 1, to);
|
||||
branch->pat->print(to);
|
||||
to << std::endl;
|
||||
branch->expr->print(indent + 2, to);
|
||||
}
|
||||
}
|
||||
|
||||
type_ptr ast_case::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_var* var;
|
||||
type_ptr case_type = mgr.resolve(of->typecheck_common(mgr, env), var);
|
||||
type_ptr branch_type = mgr.new_type();
|
||||
|
||||
for(auto& branch : branches) {
|
||||
type_env new_env = env.scope();
|
||||
branch->pat->match(case_type, mgr, new_env);
|
||||
type_ptr curr_branch_type = branch->expr->typecheck_common(mgr, new_env);
|
||||
mgr.unify(branch_type, curr_branch_type);
|
||||
}
|
||||
|
||||
case_type = mgr.resolve(case_type, var);
|
||||
if(!dynamic_cast<type_data*>(case_type.get())) {
|
||||
throw type_error("attempting case analysis of non-data type");
|
||||
}
|
||||
|
||||
return branch_type;
|
||||
}
|
||||
|
||||
void ast_case::resolve(const type_mgr& mgr) const {
|
||||
of->resolve_common(mgr);
|
||||
for(auto& branch : branches) {
|
||||
branch->expr->resolve_common(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
void ast_case::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
type_data* type = dynamic_cast<type_data*>(of->node_type.get());
|
||||
|
||||
of->compile(env, into);
|
||||
into.push_back(instruction_ptr(new instruction_eval()));
|
||||
|
||||
instruction_jump* jump_instruction = new instruction_jump();
|
||||
into.push_back(instruction_ptr(jump_instruction));
|
||||
for(auto& branch : branches) {
|
||||
std::vector<instruction_ptr> branch_instructions;
|
||||
pattern_var* vpat;
|
||||
pattern_constr* cpat;
|
||||
|
||||
if((vpat = dynamic_cast<pattern_var*>(branch->pat.get()))) {
|
||||
branch->expr->compile(env_ptr(new env_offset(1, env)), branch_instructions);
|
||||
|
||||
for(auto& constr_pair : type->constructors) {
|
||||
if(jump_instruction->tag_mappings.find(constr_pair.second.tag) !=
|
||||
jump_instruction->tag_mappings.end())
|
||||
break;
|
||||
|
||||
jump_instruction->tag_mappings[constr_pair.second.tag] =
|
||||
jump_instruction->branches.size();
|
||||
}
|
||||
jump_instruction->branches.push_back(std::move(branch_instructions));
|
||||
} 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(
|
||||
cpat->params.size())));
|
||||
branch->expr->compile(new_env, branch_instructions);
|
||||
branch_instructions.push_back(instruction_ptr(new instruction_slide(
|
||||
cpat->params.size())));
|
||||
|
||||
int new_tag = type->constructors[cpat->constr].tag;
|
||||
if(jump_instruction->tag_mappings.find(new_tag) !=
|
||||
jump_instruction->tag_mappings.end())
|
||||
throw type_error("technically not a type error: duplicate pattern");
|
||||
|
||||
jump_instruction->tag_mappings[new_tag] =
|
||||
jump_instruction->branches.size();
|
||||
jump_instruction->branches.push_back(std::move(branch_instructions));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& constr_pair : type->constructors) {
|
||||
if(jump_instruction->tag_mappings.find(constr_pair.second.tag) ==
|
||||
jump_instruction->tag_mappings.end())
|
||||
throw type_error("non-total pattern");
|
||||
}
|
||||
}
|
||||
|
||||
void pattern_var::print(std::ostream& to) const {
|
||||
to << var;
|
||||
}
|
||||
|
||||
void pattern_var::match(type_ptr t, type_mgr& mgr, type_env& env) const {
|
||||
env.bind(var, t);
|
||||
}
|
||||
|
||||
void pattern_constr::print(std::ostream& to) const {
|
||||
to << constr;
|
||||
for(auto& param : params) {
|
||||
to << " " << param;
|
||||
}
|
||||
}
|
||||
|
||||
void pattern_constr::match(type_ptr t, type_mgr& mgr, type_env& env) const {
|
||||
type_ptr constructor_type = env.lookup(constr);
|
||||
if(!constructor_type) {
|
||||
throw type_error(std::string("pattern using unknown constructor ") + constr);
|
||||
}
|
||||
|
||||
for(int i = 0; i < params.size(); i++) {
|
||||
type_arr* arr = dynamic_cast<type_arr*>(constructor_type.get());
|
||||
if(!arr) throw type_error("too many parameters in constructor pattern");
|
||||
|
||||
env.bind(params[i], arr->left);
|
||||
constructor_type = arr->right;
|
||||
}
|
||||
|
||||
mgr.unify(t, constructor_type);
|
||||
}
|
||||
141
code/compiler/08/ast.hpp
Normal file
141
code/compiler/08/ast.hpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "type.hpp"
|
||||
#include "type_env.hpp"
|
||||
#include "binop.hpp"
|
||||
#include "instruction.hpp"
|
||||
#include "env.hpp"
|
||||
|
||||
struct ast {
|
||||
type_ptr node_type;
|
||||
|
||||
virtual ~ast() = default;
|
||||
|
||||
virtual void print(int indent, std::ostream& to) const = 0;
|
||||
virtual type_ptr typecheck(type_mgr& mgr, const type_env& env) const = 0;
|
||||
virtual void resolve(const type_mgr& mgr) const = 0;
|
||||
virtual void compile(const env_ptr& env,
|
||||
std::vector<instruction_ptr>& into) const = 0;
|
||||
|
||||
type_ptr typecheck_common(type_mgr& mgr, const type_env& env);
|
||||
void resolve_common(const type_mgr& mgr);
|
||||
};
|
||||
|
||||
using ast_ptr = std::unique_ptr<ast>;
|
||||
|
||||
struct pattern {
|
||||
virtual ~pattern() = default;
|
||||
|
||||
virtual void print(std::ostream& to) const = 0;
|
||||
virtual void match(type_ptr t, type_mgr& mgr, type_env& env) const = 0;
|
||||
};
|
||||
|
||||
using pattern_ptr = std::unique_ptr<pattern>;
|
||||
|
||||
struct branch {
|
||||
pattern_ptr pat;
|
||||
ast_ptr expr;
|
||||
|
||||
branch(pattern_ptr p, ast_ptr a)
|
||||
: pat(std::move(p)), expr(std::move(a)) {}
|
||||
};
|
||||
|
||||
using branch_ptr = std::unique_ptr<branch>;
|
||||
|
||||
struct ast_int : public ast {
|
||||
int value;
|
||||
|
||||
explicit ast_int(int v)
|
||||
: value(v) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_lid : public ast {
|
||||
std::string id;
|
||||
|
||||
explicit ast_lid(std::string i)
|
||||
: id(std::move(i)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_uid : public ast {
|
||||
std::string id;
|
||||
|
||||
explicit ast_uid(std::string i)
|
||||
: id(std::move(i)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_binop : public ast {
|
||||
binop op;
|
||||
ast_ptr left;
|
||||
ast_ptr right;
|
||||
|
||||
ast_binop(binop o, ast_ptr l, ast_ptr r)
|
||||
: op(o), left(std::move(l)), right(std::move(r)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_app : public ast {
|
||||
ast_ptr left;
|
||||
ast_ptr right;
|
||||
|
||||
ast_app(ast_ptr l, ast_ptr r)
|
||||
: left(std::move(l)), right(std::move(r)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_case : public ast {
|
||||
ast_ptr of;
|
||||
std::vector<branch_ptr> branches;
|
||||
|
||||
ast_case(ast_ptr o, std::vector<branch_ptr> b)
|
||||
: of(std::move(o)), branches(std::move(b)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct pattern_var : public pattern {
|
||||
std::string var;
|
||||
|
||||
pattern_var(std::string v)
|
||||
: var(std::move(v)) {}
|
||||
|
||||
void print(std::ostream &to) const;
|
||||
void match(type_ptr t, type_mgr& mgr, type_env& env) const;
|
||||
};
|
||||
|
||||
struct pattern_constr : public pattern {
|
||||
std::string constr;
|
||||
std::vector<std::string> params;
|
||||
|
||||
pattern_constr(std::string c, std::vector<std::string> p)
|
||||
: constr(std::move(c)), params(std::move(p)) {}
|
||||
|
||||
void print(std::ostream &to) const;
|
||||
void match(type_ptr t, type_mgr&, type_env& env) const;
|
||||
};
|
||||
21
code/compiler/08/binop.cpp
Normal file
21
code/compiler/08/binop.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "binop.hpp"
|
||||
|
||||
std::string op_name(binop op) {
|
||||
switch(op) {
|
||||
case PLUS: return "+";
|
||||
case MINUS: return "-";
|
||||
case TIMES: return "*";
|
||||
case DIVIDE: return "/";
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
|
||||
std::string op_action(binop op) {
|
||||
switch(op) {
|
||||
case PLUS: return "plus";
|
||||
case MINUS: return "minus";
|
||||
case TIMES: return "times";
|
||||
case DIVIDE: return "divide";
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
12
code/compiler/08/binop.hpp
Normal file
12
code/compiler/08/binop.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
enum binop {
|
||||
PLUS,
|
||||
MINUS,
|
||||
TIMES,
|
||||
DIVIDE
|
||||
};
|
||||
|
||||
std::string op_name(binop op);
|
||||
std::string op_action(binop op);
|
||||
116
code/compiler/08/definition.cpp
Normal file
116
code/compiler/08/definition.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "definition.hpp"
|
||||
#include "error.hpp"
|
||||
#include "ast.hpp"
|
||||
#include "llvm_context.hpp"
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/Function.h>
|
||||
#include <llvm/IR/Type.h>
|
||||
|
||||
void definition_defn::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||
return_type = mgr.new_type();
|
||||
type_ptr full_type = return_type;
|
||||
|
||||
for(auto it = params.rbegin(); it != params.rend(); it++) {
|
||||
type_ptr param_type = mgr.new_type();
|
||||
full_type = type_ptr(new type_arr(param_type, full_type));
|
||||
param_types.push_back(param_type);
|
||||
}
|
||||
|
||||
env.bind(name, full_type);
|
||||
}
|
||||
|
||||
void definition_defn::typecheck_second(type_mgr& mgr, const type_env& env) const {
|
||||
type_env new_env = env.scope();
|
||||
auto param_it = params.begin();
|
||||
auto type_it = param_types.rbegin();
|
||||
|
||||
while(param_it != params.end() && type_it != param_types.rend()) {
|
||||
new_env.bind(*param_it, *type_it);
|
||||
param_it++;
|
||||
type_it++;
|
||||
}
|
||||
|
||||
type_ptr body_type = body->typecheck_common(mgr, new_env);
|
||||
mgr.unify(return_type, body_type);
|
||||
}
|
||||
|
||||
void definition_defn::resolve(const type_mgr& mgr) {
|
||||
type_var* var;
|
||||
body->resolve_common(mgr);
|
||||
|
||||
return_type = mgr.resolve(return_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
for(auto& param_type : param_types) {
|
||||
param_type = mgr.resolve(param_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
}
|
||||
}
|
||||
|
||||
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())));
|
||||
instructions.push_back(instruction_ptr(new instruction_pop(params.size())));
|
||||
}
|
||||
void definition_defn::gen_llvm_first(llvm_context& ctx) {
|
||||
generated_function = ctx.create_custom_function(name, params.size());
|
||||
}
|
||||
|
||||
void definition_defn::gen_llvm_second(llvm_context& ctx) {
|
||||
ctx.builder.SetInsertPoint(&generated_function->getEntryBlock());
|
||||
for(auto& instruction : instructions) {
|
||||
instruction->gen_llvm(ctx, generated_function);
|
||||
}
|
||||
ctx.builder.CreateRetVoid();
|
||||
}
|
||||
|
||||
void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||
type_data* this_type = new type_data(name);
|
||||
type_ptr return_type = type_ptr(this_type);
|
||||
int next_tag = 0;
|
||||
|
||||
for(auto& constructor : constructors) {
|
||||
constructor->tag = next_tag;
|
||||
this_type->constructors[constructor->name] = { next_tag++ };
|
||||
|
||||
type_ptr full_type = return_type;
|
||||
for(auto it = constructor->types.rbegin(); it != constructor->types.rend(); it++) {
|
||||
type_ptr type = type_ptr(new type_base(*it));
|
||||
full_type = type_ptr(new type_arr(type, full_type));
|
||||
}
|
||||
|
||||
env.bind(constructor->name, full_type);
|
||||
}
|
||||
}
|
||||
|
||||
void definition_data::typecheck_second(type_mgr& mgr, const type_env& env) const {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
void definition_data::resolve(const type_mgr& mgr) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
void definition_data::compile() {
|
||||
|
||||
}
|
||||
|
||||
void definition_data::gen_llvm_first(llvm_context& ctx) {
|
||||
for(auto& constructor : constructors) {
|
||||
auto new_function =
|
||||
ctx.create_custom_function(constructor->name, constructor->types.size());
|
||||
ctx.builder.SetInsertPoint(&new_function->getEntryBlock());
|
||||
ctx.create_pack(new_function,
|
||||
ctx.create_size(constructor->types.size()),
|
||||
ctx.create_i8(constructor->tag)
|
||||
);
|
||||
ctx.builder.CreateRetVoid();
|
||||
}
|
||||
}
|
||||
|
||||
void definition_data::gen_llvm_second(llvm_context& ctx) {
|
||||
// Nothing
|
||||
}
|
||||
73
code/compiler/08/definition.hpp
Normal file
73
code/compiler/08/definition.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "instruction.hpp"
|
||||
#include "llvm_context.hpp"
|
||||
#include "type_env.hpp"
|
||||
|
||||
struct ast;
|
||||
using ast_ptr = std::unique_ptr<ast>;
|
||||
|
||||
struct definition {
|
||||
virtual ~definition() = default;
|
||||
|
||||
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 resolve(const type_mgr& mgr) = 0;
|
||||
virtual void compile() = 0;
|
||||
virtual void gen_llvm_first(llvm_context& ctx) = 0;
|
||||
virtual void gen_llvm_second(llvm_context& ctx) = 0;
|
||||
};
|
||||
|
||||
using definition_ptr = std::unique_ptr<definition>;
|
||||
|
||||
struct constructor {
|
||||
std::string name;
|
||||
std::vector<std::string> types;
|
||||
int8_t tag;
|
||||
|
||||
constructor(std::string n, std::vector<std::string> ts)
|
||||
: name(std::move(n)), types(std::move(ts)) {}
|
||||
};
|
||||
|
||||
using constructor_ptr = std::unique_ptr<constructor>;
|
||||
|
||||
struct definition_defn : public definition {
|
||||
std::string name;
|
||||
std::vector<std::string> params;
|
||||
ast_ptr body;
|
||||
|
||||
type_ptr return_type;
|
||||
std::vector<type_ptr> param_types;
|
||||
|
||||
std::vector<instruction_ptr> instructions;
|
||||
|
||||
llvm::Function* generated_function;
|
||||
|
||||
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)) {
|
||||
|
||||
}
|
||||
|
||||
void typecheck_first(type_mgr& mgr, type_env& env);
|
||||
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr);
|
||||
void compile();
|
||||
void gen_llvm_first(llvm_context& ctx);
|
||||
void gen_llvm_second(llvm_context& ctx);
|
||||
};
|
||||
|
||||
struct definition_data : public definition {
|
||||
std::string name;
|
||||
std::vector<constructor_ptr> constructors;
|
||||
|
||||
definition_data(std::string n, std::vector<constructor_ptr> cs)
|
||||
: name(std::move(n)), constructors(std::move(cs)) {}
|
||||
|
||||
void typecheck_first(type_mgr& mgr, type_env& env);
|
||||
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr);
|
||||
void compile();
|
||||
void gen_llvm_first(llvm_context& ctx);
|
||||
void gen_llvm_second(llvm_context& ctx);
|
||||
};
|
||||
23
code/compiler/08/env.cpp
Normal file
23
code/compiler/08/env.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "env.hpp"
|
||||
|
||||
int env_var::get_offset(const std::string& name) const {
|
||||
if(name == this->name) return 0;
|
||||
if(parent) return parent->get_offset(name) + 1;
|
||||
throw 0;
|
||||
}
|
||||
|
||||
bool env_var::has_variable(const std::string& name) const {
|
||||
if(name == this->name) return true;
|
||||
if(parent) return parent->has_variable(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
int env_offset::get_offset(const std::string& name) const {
|
||||
if(parent) return parent->get_offset(name) + offset;
|
||||
throw 0;
|
||||
}
|
||||
|
||||
bool env_offset::has_variable(const std::string& name) const {
|
||||
if(parent) return parent->has_variable(name);
|
||||
return false;
|
||||
}
|
||||
34
code/compiler/08/env.hpp
Normal file
34
code/compiler/08/env.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
struct env {
|
||||
virtual ~env() = default;
|
||||
|
||||
virtual int get_offset(const std::string& name) const = 0;
|
||||
virtual bool has_variable(const std::string& name) const = 0;
|
||||
};
|
||||
|
||||
using env_ptr = std::shared_ptr<env>;
|
||||
|
||||
struct env_var : public env {
|
||||
std::string name;
|
||||
env_ptr parent;
|
||||
|
||||
env_var(std::string& n, env_ptr p)
|
||||
: name(std::move(n)), parent(std::move(p)) {}
|
||||
|
||||
int get_offset(const std::string& name) const;
|
||||
bool has_variable(const std::string& name) const;
|
||||
};
|
||||
|
||||
struct env_offset : public env {
|
||||
int offset;
|
||||
env_ptr parent;
|
||||
|
||||
env_offset(int o, env_ptr p)
|
||||
: offset(o), parent(std::move(p)) {}
|
||||
|
||||
int get_offset(const std::string& name) const;
|
||||
bool has_variable(const std::string& name) const;
|
||||
};
|
||||
5
code/compiler/08/error.cpp
Normal file
5
code/compiler/08/error.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "error.hpp"
|
||||
|
||||
const char* type_error::what() const noexcept {
|
||||
return "an error occured while checking the types of the program";
|
||||
}
|
||||
21
code/compiler/08/error.hpp
Normal file
21
code/compiler/08/error.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <exception>
|
||||
#include "type.hpp"
|
||||
|
||||
struct type_error : std::exception {
|
||||
std::string description;
|
||||
|
||||
type_error(std::string d)
|
||||
: description(std::move(d)) {}
|
||||
|
||||
const char* what() const noexcept override;
|
||||
};
|
||||
|
||||
struct unification_error : public type_error {
|
||||
type_ptr left;
|
||||
type_ptr right;
|
||||
|
||||
unification_error(type_ptr l, type_ptr r)
|
||||
: left(std::move(l)), right(std::move(r)),
|
||||
type_error("failed to unify types") {}
|
||||
};
|
||||
2
code/compiler/08/examples/bad1.txt
Normal file
2
code/compiler/08/examples/bad1.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
data Bool = { True, False }
|
||||
defn main = { 3 + True }
|
||||
1
code/compiler/08/examples/bad2.txt
Normal file
1
code/compiler/08/examples/bad2.txt
Normal file
@@ -0,0 +1 @@
|
||||
defn main = { 1 2 3 4 5 }
|
||||
8
code/compiler/08/examples/bad3.txt
Normal file
8
code/compiler/08/examples/bad3.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
data List = { Nil, Cons Int List }
|
||||
|
||||
defn head l = {
|
||||
case l of {
|
||||
Nil -> { 0 }
|
||||
Cons x y z -> { x }
|
||||
}
|
||||
}
|
||||
31
code/compiler/08/examples/runtime1.c
Normal file
31
code/compiler/08/examples/runtime1.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "../runtime.h"
|
||||
|
||||
void f_add(struct stack* s) {
|
||||
struct node_num* left = (struct node_num*) eval(stack_peek(s, 0));
|
||||
struct node_num* right = (struct node_num*) eval(stack_peek(s, 1));
|
||||
stack_push(s, (struct node_base*) alloc_num(left->value + right->value));
|
||||
}
|
||||
|
||||
void f_main(struct stack* s) {
|
||||
// PushInt 320
|
||||
stack_push(s, (struct node_base*) alloc_num(320));
|
||||
|
||||
// PushInt 6
|
||||
stack_push(s, (struct node_base*) alloc_num(6));
|
||||
|
||||
// PushGlobal f_add (the function for +)
|
||||
stack_push(s, (struct node_base*) alloc_global(f_add, 2));
|
||||
|
||||
struct node_base* left;
|
||||
struct node_base* right;
|
||||
|
||||
// MkApp
|
||||
left = stack_pop(s);
|
||||
right = stack_pop(s);
|
||||
stack_push(s, (struct node_base*) alloc_app(left, right));
|
||||
|
||||
// MkApp
|
||||
left = stack_pop(s);
|
||||
right = stack_pop(s);
|
||||
stack_push(s, (struct node_base*) alloc_app(left, right));
|
||||
}
|
||||
2
code/compiler/08/examples/works1.txt
Normal file
2
code/compiler/08/examples/works1.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
defn main = { sum 320 6 }
|
||||
defn sum x y = { x + y }
|
||||
3
code/compiler/08/examples/works2.txt
Normal file
3
code/compiler/08/examples/works2.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
defn add x y = { x + y }
|
||||
defn double x = { add x x }
|
||||
defn main = { double 163 }
|
||||
8
code/compiler/08/examples/works3.txt
Normal file
8
code/compiler/08/examples/works3.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
data List = { Nil, Cons Int List }
|
||||
defn length l = {
|
||||
case l of {
|
||||
Nil -> { 0 }
|
||||
Cons x xs -> { 1 + length xs }
|
||||
}
|
||||
}
|
||||
defn main = { length (Cons 1 (Cons 2 (Cons 3 Nil))) }
|
||||
16
code/compiler/08/examples/works4.txt
Normal file
16
code/compiler/08/examples/works4.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
data List = { Nil, Cons Int List }
|
||||
|
||||
defn add x y = { x + y }
|
||||
defn mul x y = { x * y }
|
||||
|
||||
defn foldr f b l = {
|
||||
case l of {
|
||||
Nil -> { b }
|
||||
Cons x xs -> { f x (foldr f b xs) }
|
||||
}
|
||||
}
|
||||
|
||||
defn main = {
|
||||
foldr add 0 (Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)))) +
|
||||
foldr mul 1 (Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))))
|
||||
}
|
||||
17
code/compiler/08/examples/works5.txt
Normal file
17
code/compiler/08/examples/works5.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
data List = { Nil, Cons Int List }
|
||||
|
||||
defn sumZip l m = {
|
||||
case l of {
|
||||
Nil -> { 0 }
|
||||
Cons x xs -> {
|
||||
case m of {
|
||||
Nil -> { 0 }
|
||||
Cons y ys -> { x + y + sumZip xs ys }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defn ones = { Cons 1 ones }
|
||||
|
||||
defn main = { sumZip ones (Cons 1 (Cons 2 (Cons 3 Nil))) }
|
||||
177
code/compiler/08/instruction.cpp
Normal file
177
code/compiler/08/instruction.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include "instruction.hpp"
|
||||
#include "llvm_context.hpp"
|
||||
#include <llvm/IR/BasicBlock.h>
|
||||
#include <llvm/IR/Function.h>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
static void print_indent(int n, std::ostream& to) {
|
||||
while(n--) to << " ";
|
||||
}
|
||||
|
||||
void instruction_pushint::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "PushInt(" << value << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_pushint::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
ctx.create_push(f, ctx.create_num(ctx.create_i32(value)));
|
||||
}
|
||||
|
||||
void instruction_pushglobal::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "PushGlobal(" << name << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_pushglobal::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
auto& global_f = ctx.custom_functions.at("f_" + name);
|
||||
auto arity = ctx.create_i32(global_f->arity);
|
||||
ctx.create_push(f, ctx.create_global(global_f->function, arity));
|
||||
}
|
||||
|
||||
void instruction_push::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Push(" << offset << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_push::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
ctx.create_push(f, ctx.create_peek(f, ctx.create_size(offset)));
|
||||
}
|
||||
|
||||
void instruction_pop::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Pop(" << count << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_pop::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
ctx.create_popn(f, ctx.create_size(count));
|
||||
}
|
||||
|
||||
void instruction_mkapp::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "MkApp()" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_mkapp::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
auto left = ctx.create_pop(f);
|
||||
auto right = ctx.create_pop(f);
|
||||
ctx.create_push(f, ctx.create_app(left, right));
|
||||
}
|
||||
|
||||
void instruction_update::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Update(" << offset << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_update::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
ctx.create_update(f, ctx.create_size(offset));
|
||||
}
|
||||
|
||||
void instruction_pack::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Pack(" << tag << ", " << size << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_pack::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
ctx.create_pack(f, ctx.create_size(size), ctx.create_i8(tag));
|
||||
}
|
||||
|
||||
void instruction_split::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Split()" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_split::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
ctx.create_split(f, ctx.create_size(size));
|
||||
}
|
||||
|
||||
void instruction_jump::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Jump(" << std::endl;
|
||||
for(auto& instruction_set : branches) {
|
||||
for(auto& instruction : instruction_set) {
|
||||
instruction->print(indent + 2, to);
|
||||
}
|
||||
to << std::endl;
|
||||
}
|
||||
print_indent(indent, to);
|
||||
to << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_jump::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
auto top_node = ctx.create_peek(f, ctx.create_size(0));
|
||||
auto tag = ctx.unwrap_data_tag(top_node);
|
||||
auto safety_block = BasicBlock::Create(ctx.ctx, "safety", f);
|
||||
auto switch_op = ctx.builder.CreateSwitch(tag, safety_block, tag_mappings.size());
|
||||
std::vector<BasicBlock*> blocks;
|
||||
|
||||
for(auto& branch : branches) {
|
||||
auto branch_block = BasicBlock::Create(ctx.ctx, "branch", f);
|
||||
ctx.builder.SetInsertPoint(branch_block);
|
||||
for(auto& instruction : branch) {
|
||||
instruction->gen_llvm(ctx, f);
|
||||
}
|
||||
ctx.builder.CreateBr(safety_block);
|
||||
blocks.push_back(branch_block);
|
||||
}
|
||||
|
||||
for(auto& mapping : tag_mappings) {
|
||||
switch_op->addCase(ctx.create_i8(mapping.first), blocks[mapping.second]);
|
||||
}
|
||||
|
||||
ctx.builder.SetInsertPoint(safety_block);
|
||||
}
|
||||
|
||||
void instruction_slide::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Slide(" << offset << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_slide::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
ctx.create_slide(f, ctx.create_size(offset));
|
||||
}
|
||||
|
||||
void instruction_binop::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "BinOp(" << op_action(op) << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_binop::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
auto left_int = ctx.unwrap_num(ctx.create_pop(f));
|
||||
auto right_int = ctx.unwrap_num(ctx.create_pop(f));
|
||||
llvm::Value* result;
|
||||
switch(op) {
|
||||
case PLUS: result = ctx.builder.CreateAdd(left_int, right_int); break;
|
||||
case MINUS: result = ctx.builder.CreateSub(left_int, right_int); break;
|
||||
case TIMES: result = ctx.builder.CreateMul(left_int, right_int); break;
|
||||
case DIVIDE: result = ctx.builder.CreateSDiv(left_int, right_int); break;
|
||||
}
|
||||
ctx.create_push(f, ctx.create_num(result));
|
||||
}
|
||||
|
||||
void instruction_eval::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Eval()" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_eval::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
ctx.create_push(f, ctx.create_eval(ctx.create_pop(f)));
|
||||
}
|
||||
|
||||
void instruction_alloc::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Alloc(" << amount << ")" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_alloc::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
ctx.create_alloc(f, ctx.create_size(amount));
|
||||
}
|
||||
|
||||
void instruction_unwind::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "Unwind()" << std::endl;
|
||||
}
|
||||
|
||||
void instruction_unwind::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||
// Nothing
|
||||
}
|
||||
142
code/compiler/08/instruction.hpp
Normal file
142
code/compiler/08/instruction.hpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#pragma once
|
||||
#include <llvm/IR/Function.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include "binop.hpp"
|
||||
#include "llvm_context.hpp"
|
||||
|
||||
struct instruction {
|
||||
virtual ~instruction() = default;
|
||||
|
||||
virtual void print(int indent, std::ostream& to) const = 0;
|
||||
virtual void gen_llvm(llvm_context& ctx, llvm::Function* f) const = 0;
|
||||
};
|
||||
|
||||
using instruction_ptr = std::unique_ptr<instruction>;
|
||||
|
||||
struct instruction_pushint : public instruction {
|
||||
int value;
|
||||
|
||||
instruction_pushint(int v)
|
||||
: value(v) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_pushglobal : public instruction {
|
||||
std::string name;
|
||||
|
||||
instruction_pushglobal(std::string n)
|
||||
: name(std::move(n)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_push : public instruction {
|
||||
int offset;
|
||||
|
||||
instruction_push(int o)
|
||||
: offset(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_pop : public instruction {
|
||||
int count;
|
||||
|
||||
instruction_pop(int c)
|
||||
: count(c) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_mkapp : public instruction {
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_update : public instruction {
|
||||
int offset;
|
||||
|
||||
instruction_update(int o)
|
||||
: offset(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_pack : public instruction {
|
||||
int tag;
|
||||
int size;
|
||||
|
||||
instruction_pack(int t, int s)
|
||||
: tag(t), size(s) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_split : public instruction {
|
||||
int size;
|
||||
|
||||
instruction_split(int s)
|
||||
: size(s) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_jump : public instruction {
|
||||
std::vector<std::vector<instruction_ptr>> branches;
|
||||
std::map<int, int> tag_mappings;
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_slide : public instruction {
|
||||
int offset;
|
||||
|
||||
instruction_slide(int o)
|
||||
: offset(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_binop : public instruction {
|
||||
binop op;
|
||||
|
||||
instruction_binop(binop o)
|
||||
: op(o) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_eval : public instruction {
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_alloc : public instruction {
|
||||
int amount;
|
||||
|
||||
instruction_alloc(int a)
|
||||
: amount(a) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
|
||||
struct instruction_unwind : public instruction {
|
||||
void print(int indent, std::ostream& to) const;
|
||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||
};
|
||||
252
code/compiler/08/llvm_context.cpp
Normal file
252
code/compiler/08/llvm_context.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
#include "llvm_context.hpp"
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
void llvm_context::create_types() {
|
||||
stack_type = StructType::create(ctx, "stack");
|
||||
stack_ptr_type = PointerType::getUnqual(stack_type);
|
||||
tag_type = IntegerType::getInt8Ty(ctx);
|
||||
struct_types["node_base"] = StructType::create(ctx, "node_base");
|
||||
struct_types["node_app"] = StructType::create(ctx, "node_app");
|
||||
struct_types["node_num"] = StructType::create(ctx, "node_num");
|
||||
struct_types["node_global"] = StructType::create(ctx, "node_global");
|
||||
struct_types["node_ind"] = StructType::create(ctx, "node_ind");
|
||||
struct_types["node_data"] = StructType::create(ctx, "node_data");
|
||||
node_ptr_type = PointerType::getUnqual(struct_types.at("node_base"));
|
||||
function_type = FunctionType::get(Type::getVoidTy(ctx), { stack_ptr_type }, false);
|
||||
|
||||
struct_types.at("node_base")->setBody(
|
||||
IntegerType::getInt32Ty(ctx)
|
||||
);
|
||||
struct_types.at("node_app")->setBody(
|
||||
struct_types.at("node_base"),
|
||||
node_ptr_type,
|
||||
node_ptr_type
|
||||
);
|
||||
struct_types.at("node_num")->setBody(
|
||||
struct_types.at("node_base"),
|
||||
IntegerType::getInt32Ty(ctx)
|
||||
);
|
||||
struct_types.at("node_global")->setBody(
|
||||
struct_types.at("node_base"),
|
||||
FunctionType::get(Type::getVoidTy(ctx), { stack_ptr_type }, false)
|
||||
);
|
||||
struct_types.at("node_ind")->setBody(
|
||||
struct_types.at("node_base"),
|
||||
node_ptr_type
|
||||
);
|
||||
struct_types.at("node_data")->setBody(
|
||||
struct_types.at("node_base"),
|
||||
IntegerType::getInt8Ty(ctx),
|
||||
PointerType::getUnqual(node_ptr_type)
|
||||
);
|
||||
}
|
||||
|
||||
void llvm_context::create_functions() {
|
||||
auto void_type = Type::getVoidTy(ctx);
|
||||
auto sizet_type = IntegerType::get(ctx, sizeof(size_t) * 8);
|
||||
functions["stack_init"] = Function::Create(
|
||||
FunctionType::get(void_type, { stack_ptr_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"stack_init",
|
||||
&module
|
||||
);
|
||||
functions["stack_free"] = Function::Create(
|
||||
FunctionType::get(void_type, { stack_ptr_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"stack_free",
|
||||
&module
|
||||
);
|
||||
functions["stack_push"] = Function::Create(
|
||||
FunctionType::get(void_type, { stack_ptr_type, node_ptr_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"stack_push",
|
||||
&module
|
||||
);
|
||||
functions["stack_pop"] = Function::Create(
|
||||
FunctionType::get(node_ptr_type, { stack_ptr_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"stack_pop",
|
||||
&module
|
||||
);
|
||||
functions["stack_peek"] = Function::Create(
|
||||
FunctionType::get(node_ptr_type, { stack_ptr_type, sizet_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"stack_peek",
|
||||
&module
|
||||
);
|
||||
functions["stack_popn"] = Function::Create(
|
||||
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"stack_popn",
|
||||
&module
|
||||
);
|
||||
functions["stack_slide"] = Function::Create(
|
||||
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"stack_slide",
|
||||
&module
|
||||
);
|
||||
functions["stack_update"] = Function::Create(
|
||||
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"stack_update",
|
||||
&module
|
||||
);
|
||||
functions["stack_alloc"] = Function::Create(
|
||||
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"stack_alloc",
|
||||
&module
|
||||
);
|
||||
functions["stack_pack"] = Function::Create(
|
||||
FunctionType::get(void_type, { stack_ptr_type, sizet_type, tag_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"stack_pack",
|
||||
&module
|
||||
);
|
||||
functions["stack_split"] = Function::Create(
|
||||
FunctionType::get(node_ptr_type, { stack_ptr_type, sizet_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"stack_split",
|
||||
&module
|
||||
);
|
||||
|
||||
auto int32_type = IntegerType::getInt32Ty(ctx);
|
||||
functions["alloc_app"] = Function::Create(
|
||||
FunctionType::get(node_ptr_type, { node_ptr_type, node_ptr_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"alloc_app",
|
||||
&module
|
||||
);
|
||||
functions["alloc_num"] = Function::Create(
|
||||
FunctionType::get(node_ptr_type, { int32_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"alloc_num",
|
||||
&module
|
||||
);
|
||||
functions["alloc_global"] = Function::Create(
|
||||
FunctionType::get(node_ptr_type, { function_type, int32_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"alloc_global",
|
||||
&module
|
||||
);
|
||||
functions["alloc_ind"] = Function::Create(
|
||||
FunctionType::get(node_ptr_type, { node_ptr_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"alloc_ind",
|
||||
&module
|
||||
);
|
||||
|
||||
functions["eval"] = Function::Create(
|
||||
FunctionType::get(node_ptr_type, { node_ptr_type }, false),
|
||||
Function::LinkageTypes::ExternalLinkage,
|
||||
"eval",
|
||||
&module
|
||||
);
|
||||
}
|
||||
|
||||
ConstantInt* llvm_context::create_i8(int8_t i) {
|
||||
return ConstantInt::get(ctx, APInt(8, i));
|
||||
}
|
||||
ConstantInt* llvm_context::create_i32(int32_t i) {
|
||||
return ConstantInt::get(ctx, APInt(32, i));
|
||||
}
|
||||
ConstantInt* llvm_context::create_size(size_t i) {
|
||||
return ConstantInt::get(ctx, APInt(sizeof(size_t) * 8, i));
|
||||
}
|
||||
|
||||
Value* llvm_context::create_pop(Function* f) {
|
||||
auto pop_f = functions.at("stack_pop");
|
||||
return builder.CreateCall(pop_f, { f->arg_begin() });
|
||||
}
|
||||
Value* llvm_context::create_peek(Function* f, Value* off) {
|
||||
auto peek_f = functions.at("stack_peek");
|
||||
return builder.CreateCall(peek_f, { f->arg_begin(), off });
|
||||
}
|
||||
void llvm_context::create_push(Function* f, Value* v) {
|
||||
auto push_f = functions.at("stack_push");
|
||||
builder.CreateCall(push_f, { f->arg_begin(), v });
|
||||
}
|
||||
void llvm_context::create_popn(Function* f, Value* off) {
|
||||
auto popn_f = functions.at("stack_popn");
|
||||
builder.CreateCall(popn_f, { f->arg_begin(), off });
|
||||
}
|
||||
void llvm_context::create_update(Function* f, Value* off) {
|
||||
auto update_f = functions.at("stack_update");
|
||||
builder.CreateCall(update_f, { f->arg_begin(), off });
|
||||
}
|
||||
void llvm_context::create_pack(Function* f, Value* c, Value* t) {
|
||||
auto pack_f = functions.at("stack_pack");
|
||||
builder.CreateCall(pack_f, { f->arg_begin(), c, t });
|
||||
}
|
||||
void llvm_context::create_split(Function* f, Value* c) {
|
||||
auto split_f = functions.at("stack_split");
|
||||
builder.CreateCall(split_f, { f->arg_begin(), c });
|
||||
}
|
||||
void llvm_context::create_slide(Function* f, Value* off) {
|
||||
auto slide_f = functions.at("stack_slide");
|
||||
builder.CreateCall(slide_f, { f->arg_begin(), off });
|
||||
}
|
||||
void llvm_context::create_alloc(Function* f, Value* n) {
|
||||
auto alloc_f = functions.at("stack_alloc");
|
||||
builder.CreateCall(alloc_f, { f->arg_begin(), n });
|
||||
}
|
||||
|
||||
Value* llvm_context::create_eval(Value* e) {
|
||||
auto eval_f = functions.at("eval");
|
||||
return builder.CreateCall(eval_f, { e });
|
||||
}
|
||||
|
||||
Value* llvm_context::unwrap_num(Value* v) {
|
||||
auto num_ptr_type = PointerType::getUnqual(struct_types.at("node_num"));
|
||||
auto cast = builder.CreatePointerCast(v, num_ptr_type);
|
||||
auto offset_0 = create_i32(0);
|
||||
auto offset_1 = create_i32(1);
|
||||
auto int_ptr = builder.CreateGEP(cast, { offset_0, offset_1 });
|
||||
return builder.CreateLoad(int_ptr);
|
||||
}
|
||||
Value* llvm_context::create_num(Value* v) {
|
||||
auto alloc_num_f = functions.at("alloc_num");
|
||||
return builder.CreateCall(alloc_num_f, { v });
|
||||
}
|
||||
|
||||
Value* llvm_context::unwrap_data_tag(Value* v) {
|
||||
auto data_ptr_type = PointerType::getUnqual(struct_types.at("node_data"));
|
||||
auto cast = builder.CreatePointerCast(v, data_ptr_type);
|
||||
auto offset_0 = create_i32(0);
|
||||
auto offset_1 = create_i32(1);
|
||||
auto tag_ptr = builder.CreateGEP(cast, { offset_0, offset_1 });
|
||||
return builder.CreateLoad(tag_ptr);
|
||||
}
|
||||
|
||||
Value* llvm_context::create_global(Value* f, Value* a) {
|
||||
auto alloc_global_f = functions.at("alloc_global");
|
||||
return builder.CreateCall(alloc_global_f, { f, a });
|
||||
}
|
||||
|
||||
Value* llvm_context::create_app(Value* l, Value* r) {
|
||||
auto alloc_app_f = functions.at("alloc_app");
|
||||
return builder.CreateCall(alloc_app_f, { l, r });
|
||||
}
|
||||
|
||||
llvm::Function* llvm_context::create_custom_function(std::string name, int32_t arity) {
|
||||
auto void_type = llvm::Type::getVoidTy(ctx);
|
||||
auto function_type =
|
||||
llvm::FunctionType::get(void_type, { stack_ptr_type }, false);
|
||||
auto new_function = llvm::Function::Create(
|
||||
function_type,
|
||||
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||
"f_" + name,
|
||||
&module
|
||||
);
|
||||
auto start_block = llvm::BasicBlock::Create(ctx, "entry", new_function);
|
||||
|
||||
auto new_custom_f = custom_function_ptr(new custom_function());
|
||||
new_custom_f->arity = arity;
|
||||
new_custom_f->function = new_function;
|
||||
custom_functions["f_" + name] = std::move(new_custom_f);
|
||||
|
||||
return new_function;
|
||||
}
|
||||
66
code/compiler/08/llvm_context.hpp
Normal file
66
code/compiler/08/llvm_context.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/Function.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <map>
|
||||
|
||||
struct llvm_context {
|
||||
struct custom_function {
|
||||
llvm::Function* function;
|
||||
int32_t arity;
|
||||
};
|
||||
|
||||
using custom_function_ptr = std::unique_ptr<custom_function>;
|
||||
|
||||
llvm::LLVMContext ctx;
|
||||
llvm::IRBuilder<> builder;
|
||||
llvm::Module module;
|
||||
|
||||
std::map<std::string, custom_function_ptr> custom_functions;
|
||||
std::map<std::string, llvm::Function*> functions;
|
||||
std::map<std::string, llvm::StructType*> struct_types;
|
||||
|
||||
llvm::StructType* stack_type;
|
||||
llvm::PointerType* stack_ptr_type;
|
||||
llvm::PointerType* node_ptr_type;
|
||||
llvm::IntegerType* tag_type;
|
||||
llvm::FunctionType* function_type;
|
||||
|
||||
llvm_context()
|
||||
: builder(ctx), module("bloglang", ctx) {
|
||||
create_types();
|
||||
create_functions();
|
||||
}
|
||||
|
||||
void create_types();
|
||||
void create_functions();
|
||||
|
||||
llvm::ConstantInt* create_i8(int8_t);
|
||||
llvm::ConstantInt* create_i32(int32_t);
|
||||
llvm::ConstantInt* create_size(size_t);
|
||||
|
||||
llvm::Value* create_pop(llvm::Function*);
|
||||
llvm::Value* create_peek(llvm::Function*, llvm::Value*);
|
||||
void create_push(llvm::Function*, llvm::Value*);
|
||||
void create_popn(llvm::Function*, llvm::Value*);
|
||||
void create_update(llvm::Function*, llvm::Value*);
|
||||
void create_pack(llvm::Function*, llvm::Value*, llvm::Value*);
|
||||
void create_split(llvm::Function*, llvm::Value*);
|
||||
void create_slide(llvm::Function*, llvm::Value*);
|
||||
void create_alloc(llvm::Function*, llvm::Value*);
|
||||
|
||||
llvm::Value* create_eval(llvm::Value*);
|
||||
|
||||
llvm::Value* unwrap_num(llvm::Value*);
|
||||
llvm::Value* create_num(llvm::Value*);
|
||||
|
||||
llvm::Value* unwrap_data_tag(llvm::Value*);
|
||||
|
||||
llvm::Value* create_global(llvm::Value*, llvm::Value*);
|
||||
|
||||
llvm::Value* create_app(llvm::Value*, llvm::Value*);
|
||||
|
||||
llvm::Function* create_custom_function(std::string name, int32_t arity);
|
||||
};
|
||||
174
code/compiler/08/main.cpp
Normal file
174
code/compiler/08/main.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#include "ast.hpp"
|
||||
#include <iostream>
|
||||
#include "binop.hpp"
|
||||
#include "definition.hpp"
|
||||
#include "instruction.hpp"
|
||||
#include "llvm_context.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "error.hpp"
|
||||
#include "type.hpp"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
|
||||
void yy::parser::error(const std::string& msg) {
|
||||
std::cout << "An error occured: " << msg << std::endl;
|
||||
}
|
||||
|
||||
extern std::vector<definition_ptr> program;
|
||||
|
||||
void typecheck_program(
|
||||
const std::vector<definition_ptr>& prog,
|
||||
type_mgr& mgr, type_env& env) {
|
||||
type_ptr int_type = type_ptr(new type_base("Int"));
|
||||
type_ptr binop_type = type_ptr(new type_arr(
|
||||
int_type,
|
||||
type_ptr(new type_arr(int_type, int_type))));
|
||||
|
||||
env.bind("+", binop_type);
|
||||
env.bind("-", binop_type);
|
||||
env.bind("*", binop_type);
|
||||
env.bind("/", binop_type);
|
||||
|
||||
for(auto& def : prog) {
|
||||
def->typecheck_first(mgr, env);
|
||||
}
|
||||
|
||||
for(auto& def : prog) {
|
||||
def->typecheck_second(mgr, env);
|
||||
}
|
||||
|
||||
for(auto& pair : env.names) {
|
||||
std::cout << pair.first << ": ";
|
||||
pair.second->print(mgr, std::cout);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
for(auto& def : prog) {
|
||||
def->resolve(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void gen_llvm_internal_op(llvm_context& ctx, binop op) {
|
||||
auto new_function = ctx.create_custom_function(op_action(op), 2);
|
||||
std::vector<instruction_ptr> instructions;
|
||||
instructions.push_back(instruction_ptr(new instruction_push(1)));
|
||||
instructions.push_back(instruction_ptr(new instruction_eval()));
|
||||
instructions.push_back(instruction_ptr(new instruction_push(1)));
|
||||
instructions.push_back(instruction_ptr(new instruction_eval()));
|
||||
instructions.push_back(instruction_ptr(new instruction_binop(op)));
|
||||
ctx.builder.SetInsertPoint(&new_function->getEntryBlock());
|
||||
for(auto& instruction : instructions) {
|
||||
instruction->gen_llvm(ctx, new_function);
|
||||
}
|
||||
ctx.builder.CreateRetVoid();
|
||||
}
|
||||
|
||||
void output_llvm(llvm_context& ctx, const std::string& filename) {
|
||||
std::string targetTriple = llvm::sys::getDefaultTargetTriple();
|
||||
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmParser();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
|
||||
std::string error;
|
||||
const llvm::Target* target =
|
||||
llvm::TargetRegistry::lookupTarget(targetTriple, error);
|
||||
if (!target) {
|
||||
std::cerr << error << std::endl;
|
||||
} else {
|
||||
std::string cpu = "generic";
|
||||
std::string features = "";
|
||||
llvm::TargetOptions options;
|
||||
llvm::TargetMachine* targetMachine =
|
||||
target->createTargetMachine(targetTriple, cpu, features,
|
||||
options, llvm::Optional<llvm::Reloc::Model>());
|
||||
|
||||
ctx.module.setDataLayout(targetMachine->createDataLayout());
|
||||
ctx.module.setTargetTriple(targetTriple);
|
||||
|
||||
std::error_code ec;
|
||||
llvm::raw_fd_ostream file(filename, ec, llvm::sys::fs::F_None);
|
||||
if (ec) {
|
||||
throw 0;
|
||||
} else {
|
||||
llvm::TargetMachine::CodeGenFileType type = llvm::TargetMachine::CGFT_ObjectFile;
|
||||
llvm::legacy::PassManager pm;
|
||||
if (targetMachine->addPassesToEmitFile(pm, file, NULL, type)) {
|
||||
throw 0;
|
||||
} else {
|
||||
pm.run(ctx.module);
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gen_llvm(const std::vector<definition_ptr>& prog) {
|
||||
llvm_context ctx;
|
||||
gen_llvm_internal_op(ctx, PLUS);
|
||||
gen_llvm_internal_op(ctx, MINUS);
|
||||
gen_llvm_internal_op(ctx, TIMES);
|
||||
gen_llvm_internal_op(ctx, DIVIDE);
|
||||
|
||||
for(auto& definition : prog) {
|
||||
definition->gen_llvm_first(ctx);
|
||||
}
|
||||
|
||||
for(auto& definition : prog) {
|
||||
definition->gen_llvm_second(ctx);
|
||||
}
|
||||
ctx.module.print(llvm::outs(), nullptr);
|
||||
output_llvm(ctx, "program.o");
|
||||
}
|
||||
|
||||
int main() {
|
||||
yy::parser parser;
|
||||
type_mgr mgr;
|
||||
type_env env;
|
||||
|
||||
parser.parse();
|
||||
for(auto& definition : program) {
|
||||
definition_defn* def = dynamic_cast<definition_defn*>(definition.get());
|
||||
if(!def) continue;
|
||||
|
||||
std::cout << def->name;
|
||||
for(auto& param : def->params) std::cout << " " << param;
|
||||
std::cout << ":" << std::endl;
|
||||
|
||||
def->body->print(1, std::cout);
|
||||
}
|
||||
try {
|
||||
typecheck_program(program, mgr, env);
|
||||
compile_program(program);
|
||||
gen_llvm(program);
|
||||
} catch(unification_error& err) {
|
||||
std::cout << "failed to unify types: " << std::endl;
|
||||
std::cout << " (1) \033[34m";
|
||||
err.left->print(mgr, std::cout);
|
||||
std::cout << "\033[0m" << std::endl;
|
||||
std::cout << " (2) \033[32m";
|
||||
err.right->print(mgr, std::cout);
|
||||
std::cout << "\033[0m" << std::endl;
|
||||
} catch(type_error& err) {
|
||||
std::cout << "failed to type check program: " << err.description << std::endl;
|
||||
}
|
||||
}
|
||||
141
code/compiler/08/parser.y
Normal file
141
code/compiler/08/parser.y
Normal file
@@ -0,0 +1,141 @@
|
||||
%{
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "ast.hpp"
|
||||
#include "definition.hpp"
|
||||
#include "parser.hpp"
|
||||
|
||||
std::vector<definition_ptr> program;
|
||||
extern yy::parser::symbol_type yylex();
|
||||
|
||||
%}
|
||||
|
||||
%token PLUS
|
||||
%token TIMES
|
||||
%token MINUS
|
||||
%token DIVIDE
|
||||
%token <int> INT
|
||||
%token DEFN
|
||||
%token DATA
|
||||
%token CASE
|
||||
%token OF
|
||||
%token OCURLY
|
||||
%token CCURLY
|
||||
%token OPAREN
|
||||
%token CPAREN
|
||||
%token COMMA
|
||||
%token ARROW
|
||||
%token EQUAL
|
||||
%token <std::string> LID
|
||||
%token <std::string> UID
|
||||
|
||||
%language "c++"
|
||||
%define api.value.type variant
|
||||
%define api.token.constructor
|
||||
|
||||
%type <std::vector<std::string>> lowercaseParams uppercaseParams
|
||||
%type <std::vector<definition_ptr>> program definitions
|
||||
%type <std::vector<branch_ptr>> branches
|
||||
%type <std::vector<constructor_ptr>> constructors
|
||||
%type <ast_ptr> aAdd aMul case app appBase
|
||||
%type <definition_ptr> definition defn data
|
||||
%type <branch_ptr> branch
|
||||
%type <pattern_ptr> pattern
|
||||
%type <constructor_ptr> constructor
|
||||
|
||||
%start program
|
||||
|
||||
%%
|
||||
|
||||
program
|
||||
: definitions { program = std::move($1); }
|
||||
;
|
||||
|
||||
definitions
|
||||
: definitions definition { $$ = std::move($1); $$.push_back(std::move($2)); }
|
||||
| definition { $$ = std::vector<definition_ptr>(); $$.push_back(std::move($1)); }
|
||||
;
|
||||
|
||||
definition
|
||||
: defn { $$ = std::move($1); }
|
||||
| data { $$ = std::move($1); }
|
||||
;
|
||||
|
||||
defn
|
||||
: DEFN LID lowercaseParams EQUAL OCURLY aAdd CCURLY
|
||||
{ $$ = definition_ptr(
|
||||
new definition_defn(std::move($2), std::move($3), std::move($6))); }
|
||||
;
|
||||
|
||||
lowercaseParams
|
||||
: %empty { $$ = std::vector<std::string>(); }
|
||||
| lowercaseParams LID { $$ = std::move($1); $$.push_back(std::move($2)); }
|
||||
;
|
||||
|
||||
uppercaseParams
|
||||
: %empty { $$ = std::vector<std::string>(); }
|
||||
| uppercaseParams UID { $$ = std::move($1); $$.push_back(std::move($2)); }
|
||||
;
|
||||
|
||||
aAdd
|
||||
: aAdd PLUS aMul { $$ = ast_ptr(new ast_binop(PLUS, std::move($1), std::move($3))); }
|
||||
| aAdd MINUS aMul { $$ = ast_ptr(new ast_binop(MINUS, std::move($1), std::move($3))); }
|
||||
| aMul { $$ = std::move($1); }
|
||||
;
|
||||
|
||||
aMul
|
||||
: aMul TIMES app { $$ = ast_ptr(new ast_binop(TIMES, std::move($1), std::move($3))); }
|
||||
| aMul DIVIDE app { $$ = ast_ptr(new ast_binop(DIVIDE, std::move($1), std::move($3))); }
|
||||
| app { $$ = std::move($1); }
|
||||
;
|
||||
|
||||
app
|
||||
: app appBase { $$ = ast_ptr(new ast_app(std::move($1), std::move($2))); }
|
||||
| appBase { $$ = std::move($1); }
|
||||
;
|
||||
|
||||
appBase
|
||||
: INT { $$ = ast_ptr(new ast_int($1)); }
|
||||
| LID { $$ = ast_ptr(new ast_lid(std::move($1))); }
|
||||
| UID { $$ = ast_ptr(new ast_uid(std::move($1))); }
|
||||
| OPAREN aAdd CPAREN { $$ = std::move($2); }
|
||||
| case { $$ = std::move($1); }
|
||||
;
|
||||
|
||||
case
|
||||
: CASE aAdd OF OCURLY branches CCURLY
|
||||
{ $$ = ast_ptr(new ast_case(std::move($2), std::move($5))); }
|
||||
;
|
||||
|
||||
branches
|
||||
: branches branch { $$ = std::move($1); $$.push_back(std::move($2)); }
|
||||
| branch { $$ = std::vector<branch_ptr>(); $$.push_back(std::move($1));}
|
||||
;
|
||||
|
||||
branch
|
||||
: pattern ARROW OCURLY aAdd CCURLY
|
||||
{ $$ = branch_ptr(new branch(std::move($1), std::move($4))); }
|
||||
;
|
||||
|
||||
pattern
|
||||
: LID { $$ = pattern_ptr(new pattern_var(std::move($1))); }
|
||||
| UID lowercaseParams
|
||||
{ $$ = pattern_ptr(new pattern_constr(std::move($1), std::move($2))); }
|
||||
;
|
||||
|
||||
data
|
||||
: DATA UID EQUAL OCURLY constructors CCURLY
|
||||
{ $$ = definition_ptr(new definition_data(std::move($2), std::move($5))); }
|
||||
;
|
||||
|
||||
constructors
|
||||
: constructors COMMA constructor { $$ = std::move($1); $$.push_back(std::move($3)); }
|
||||
| constructor
|
||||
{ $$ = std::vector<constructor_ptr>(); $$.push_back(std::move($1)); }
|
||||
;
|
||||
|
||||
constructor
|
||||
: UID uppercaseParams
|
||||
{ $$ = constructor_ptr(new constructor(std::move($1), std::move($2))); }
|
||||
;
|
||||
|
||||
183
code/compiler/08/runtime.c
Normal file
183
code/compiler/08/runtime.c
Normal file
@@ -0,0 +1,183 @@
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
#include <stdio.h>
|
||||
#include "runtime.h"
|
||||
|
||||
struct node_base* alloc_node() {
|
||||
struct node_base* new_node = malloc(sizeof(struct node_app));
|
||||
assert(new_node != NULL);
|
||||
return new_node;
|
||||
}
|
||||
|
||||
struct node_app* alloc_app(struct node_base* l, struct node_base* r) {
|
||||
struct node_app* node = (struct node_app*) alloc_node();
|
||||
node->base.tag = NODE_APP;
|
||||
node->left = l;
|
||||
node->right = r;
|
||||
return node;
|
||||
}
|
||||
|
||||
struct node_num* alloc_num(int32_t n) {
|
||||
struct node_num* node = (struct node_num*) alloc_node();
|
||||
node->base.tag = NODE_NUM;
|
||||
node->value = n;
|
||||
return node;
|
||||
}
|
||||
|
||||
struct node_global* alloc_global(void (*f)(struct stack*), int32_t a) {
|
||||
struct node_global* node = (struct node_global*) alloc_node();
|
||||
node->base.tag = NODE_GLOBAL;
|
||||
node->arity = a;
|
||||
node->function = f;
|
||||
return node;
|
||||
}
|
||||
|
||||
struct node_ind* alloc_ind(struct node_base* n) {
|
||||
struct node_ind* node = (struct node_ind*) alloc_node();
|
||||
node->base.tag = NODE_IND;
|
||||
node->next = n;
|
||||
return node;
|
||||
}
|
||||
|
||||
void stack_init(struct stack* s) {
|
||||
s->size = 4;
|
||||
s->count = 0;
|
||||
s->data = malloc(sizeof(*s->data) * s->size);
|
||||
assert(s->data != NULL);
|
||||
}
|
||||
|
||||
void stack_free(struct stack* s) {
|
||||
free(s->data);
|
||||
}
|
||||
|
||||
void stack_push(struct stack* s, struct node_base* n) {
|
||||
while(s->count >= s->size) {
|
||||
s->data = realloc(s->data, sizeof(*s->data) * (s->size *= 2));
|
||||
assert(s->data != NULL);
|
||||
}
|
||||
s->data[s->count++] = n;
|
||||
}
|
||||
|
||||
struct node_base* stack_pop(struct stack* s) {
|
||||
assert(s->count > 0);
|
||||
return s->data[--s->count];
|
||||
}
|
||||
|
||||
struct node_base* stack_peek(struct stack* s, size_t o) {
|
||||
assert(s->count > o);
|
||||
return s->data[s->count - o - 1];
|
||||
}
|
||||
|
||||
void stack_popn(struct stack* s, size_t n) {
|
||||
assert(s->count >= n);
|
||||
s->count -= n;
|
||||
}
|
||||
|
||||
void stack_slide(struct stack* s, size_t n) {
|
||||
assert(s->count > n);
|
||||
s->data[s->count - n - 1] = s->data[s->count - 1];
|
||||
s->count -= n;
|
||||
}
|
||||
|
||||
void stack_update(struct stack* s, size_t o) {
|
||||
assert(s->count > o + 1);
|
||||
struct node_ind* ind = (struct node_ind*) s->data[s->count - o - 2];
|
||||
ind->base.tag = NODE_IND;
|
||||
ind->next = s->data[s->count -= 1];
|
||||
}
|
||||
|
||||
void stack_alloc(struct stack* s, size_t o) {
|
||||
while(o--) {
|
||||
stack_push(s, (struct node_base*) alloc_ind(NULL));
|
||||
}
|
||||
}
|
||||
|
||||
void stack_pack(struct stack* s, size_t n, int8_t t) {
|
||||
assert(s->count >= n);
|
||||
|
||||
struct node_base** data = malloc(sizeof(*data) * n);
|
||||
assert(data != NULL);
|
||||
memcpy(data, &s->data[s->count - n], n * sizeof(*data));
|
||||
|
||||
struct node_data* new_node = (struct node_data*) alloc_node();
|
||||
new_node->array = data;
|
||||
new_node->base.tag = NODE_DATA;
|
||||
new_node->tag = t;
|
||||
|
||||
stack_popn(s, n);
|
||||
stack_push(s, (struct node_base*) new_node);
|
||||
}
|
||||
|
||||
void stack_split(struct stack* s, size_t n) {
|
||||
struct node_data* node = (struct node_data*) stack_pop(s);
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
stack_push(s, node->array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void unwind(struct stack* s) {
|
||||
while(1) {
|
||||
struct node_base* peek = stack_peek(s, 0);
|
||||
if(peek->tag == NODE_APP) {
|
||||
struct node_app* n = (struct node_app*) peek;
|
||||
stack_push(s, n->left);
|
||||
} else if(peek->tag == NODE_GLOBAL) {
|
||||
struct node_global* n = (struct node_global*) peek;
|
||||
assert(s->count > n->arity);
|
||||
|
||||
for(size_t i = 1; i <= n->arity; i++) {
|
||||
s->data[s->count - i]
|
||||
= ((struct node_app*) s->data[s->count - i - 1])->right;
|
||||
}
|
||||
|
||||
n->function(s);
|
||||
} else if(peek->tag == NODE_IND) {
|
||||
struct node_ind* n = (struct node_ind*) peek;
|
||||
stack_pop(s);
|
||||
stack_push(s, n->next);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct node_base* eval(struct node_base* n) {
|
||||
struct stack program_stack;
|
||||
stack_init(&program_stack);
|
||||
stack_push(&program_stack, n);
|
||||
unwind(&program_stack);
|
||||
struct node_base* result = stack_pop(&program_stack);
|
||||
stack_free(&program_stack);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern void f_main(struct stack* s);
|
||||
|
||||
void print_node(struct node_base* n) {
|
||||
if(n->tag == NODE_APP) {
|
||||
struct node_app* app = (struct node_app*) n;
|
||||
print_node(app->left);
|
||||
putchar(' ');
|
||||
print_node(app->right);
|
||||
} else if(n->tag == NODE_DATA) {
|
||||
printf("(Packed)");
|
||||
} else if(n->tag == NODE_GLOBAL) {
|
||||
struct node_global* global = (struct node_global*) n;
|
||||
printf("(Global: %p)", global->function);
|
||||
} else if(n->tag == NODE_IND) {
|
||||
print_node(((struct node_ind*) n)->next);
|
||||
} else if(n->tag == NODE_NUM) {
|
||||
struct node_num* num = (struct node_num*) n;
|
||||
printf("%d", num->value);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
struct node_global* first_node = alloc_global(f_main, 0);
|
||||
struct node_base* result = eval((struct node_base*) first_node);
|
||||
|
||||
printf("Result: ");
|
||||
print_node(result);
|
||||
putchar('\n');
|
||||
}
|
||||
70
code/compiler/08/runtime.h
Normal file
70
code/compiler/08/runtime.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#include <stdlib.h>
|
||||
|
||||
struct stack;
|
||||
|
||||
enum node_tag {
|
||||
NODE_APP,
|
||||
NODE_NUM,
|
||||
NODE_GLOBAL,
|
||||
NODE_IND,
|
||||
NODE_DATA
|
||||
};
|
||||
|
||||
struct node_base {
|
||||
enum node_tag tag;
|
||||
};
|
||||
|
||||
struct node_app {
|
||||
struct node_base base;
|
||||
struct node_base* left;
|
||||
struct node_base* right;
|
||||
};
|
||||
|
||||
struct node_num {
|
||||
struct node_base base;
|
||||
int32_t value;
|
||||
};
|
||||
|
||||
struct node_global {
|
||||
struct node_base base;
|
||||
int32_t arity;
|
||||
void (*function)(struct stack*);
|
||||
};
|
||||
|
||||
struct node_ind {
|
||||
struct node_base base;
|
||||
struct node_base* next;
|
||||
};
|
||||
|
||||
struct node_data {
|
||||
struct node_base base;
|
||||
int8_t tag;
|
||||
struct node_base** array;
|
||||
};
|
||||
|
||||
struct node_base* alloc_node();
|
||||
struct node_app* alloc_app(struct node_base* l, struct node_base* r);
|
||||
struct node_num* alloc_num(int32_t n);
|
||||
struct node_global* alloc_global(void (*f)(struct stack*), int32_t a);
|
||||
struct node_ind* alloc_ind(struct node_base* n);
|
||||
|
||||
struct stack {
|
||||
size_t size;
|
||||
size_t count;
|
||||
struct node_base** data;
|
||||
};
|
||||
|
||||
void stack_init(struct stack* s);
|
||||
void stack_free(struct stack* s);
|
||||
void stack_push(struct stack* s, struct node_base* n);
|
||||
struct node_base* stack_pop(struct stack* s);
|
||||
struct node_base* stack_peek(struct stack* s, size_t o);
|
||||
void stack_popn(struct stack* s, size_t n);
|
||||
void stack_slide(struct stack* s, size_t n);
|
||||
void stack_update(struct stack* s, size_t o);
|
||||
void stack_alloc(struct stack* s, size_t o);
|
||||
void stack_pack(struct stack* s, size_t n, int8_t t);
|
||||
void stack_split(struct stack* s, size_t n);
|
||||
|
||||
struct node_base* eval(struct node_base* n);
|
||||
35
code/compiler/08/scanner.l
Normal file
35
code/compiler/08/scanner.l
Normal file
@@ -0,0 +1,35 @@
|
||||
%option noyywrap
|
||||
|
||||
%{
|
||||
#include <iostream>
|
||||
#include "ast.hpp"
|
||||
#include "definition.hpp"
|
||||
#include "parser.hpp"
|
||||
|
||||
#define YY_DECL yy::parser::symbol_type yylex()
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
|
||||
[ \n]+ {}
|
||||
\+ { return yy::parser::make_PLUS(); }
|
||||
\* { return yy::parser::make_TIMES(); }
|
||||
- { return yy::parser::make_MINUS(); }
|
||||
\/ { return yy::parser::make_DIVIDE(); }
|
||||
[0-9]+ { return yy::parser::make_INT(atoi(yytext)); }
|
||||
defn { return yy::parser::make_DEFN(); }
|
||||
data { return yy::parser::make_DATA(); }
|
||||
case { return yy::parser::make_CASE(); }
|
||||
of { return yy::parser::make_OF(); }
|
||||
\{ { return yy::parser::make_OCURLY(); }
|
||||
\} { return yy::parser::make_CCURLY(); }
|
||||
\( { return yy::parser::make_OPAREN(); }
|
||||
\) { return yy::parser::make_CPAREN(); }
|
||||
, { return yy::parser::make_COMMA(); }
|
||||
-> { return yy::parser::make_ARROW(); }
|
||||
= { return yy::parser::make_EQUAL(); }
|
||||
[a-z][a-zA-Z]* { return yy::parser::make_LID(std::string(yytext)); }
|
||||
[A-Z][a-zA-Z]* { return yy::parser::make_UID(std::string(yytext)); }
|
||||
|
||||
%%
|
||||
99
code/compiler/08/type.cpp
Normal file
99
code/compiler/08/type.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "type.hpp"
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include "error.hpp"
|
||||
|
||||
void type_var::print(const type_mgr& mgr, std::ostream& to) const {
|
||||
auto it = mgr.types.find(name);
|
||||
if(it != mgr.types.end()) {
|
||||
it->second->print(mgr, to);
|
||||
} else {
|
||||
to << name;
|
||||
}
|
||||
}
|
||||
|
||||
void type_base::print(const type_mgr& mgr, std::ostream& to) const {
|
||||
to << name;
|
||||
}
|
||||
|
||||
void type_arr::print(const type_mgr& mgr, std::ostream& to) const {
|
||||
left->print(mgr, to);
|
||||
to << " -> (";
|
||||
right->print(mgr, to);
|
||||
to << ")";
|
||||
}
|
||||
|
||||
std::string type_mgr::new_type_name() {
|
||||
int temp = last_id++;
|
||||
std::string str = "";
|
||||
|
||||
while(temp != -1) {
|
||||
str += (char) ('a' + (temp % 26));
|
||||
temp = temp / 26 - 1;
|
||||
}
|
||||
|
||||
std::reverse(str.begin(), str.end());
|
||||
return str;
|
||||
}
|
||||
|
||||
type_ptr type_mgr::new_type() {
|
||||
return type_ptr(new type_var(new_type_name()));
|
||||
}
|
||||
|
||||
type_ptr type_mgr::new_arrow_type() {
|
||||
return type_ptr(new type_arr(new_type(), new_type()));
|
||||
}
|
||||
|
||||
type_ptr type_mgr::resolve(type_ptr t, type_var*& var) const {
|
||||
type_var* cast;
|
||||
|
||||
var = nullptr;
|
||||
while((cast = dynamic_cast<type_var*>(t.get()))) {
|
||||
auto it = types.find(cast->name);
|
||||
|
||||
if(it == types.end()) {
|
||||
var = cast;
|
||||
break;
|
||||
}
|
||||
t = it->second;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void type_mgr::unify(type_ptr l, type_ptr r) {
|
||||
type_var* lvar;
|
||||
type_var* rvar;
|
||||
type_arr* larr;
|
||||
type_arr* rarr;
|
||||
type_base* lid;
|
||||
type_base* rid;
|
||||
|
||||
l = resolve(l, lvar);
|
||||
r = resolve(r, rvar);
|
||||
|
||||
if(lvar) {
|
||||
bind(lvar->name, r);
|
||||
return;
|
||||
} else if(rvar) {
|
||||
bind(rvar->name, l);
|
||||
return;
|
||||
} else if((larr = dynamic_cast<type_arr*>(l.get())) &&
|
||||
(rarr = dynamic_cast<type_arr*>(r.get()))) {
|
||||
unify(larr->left, rarr->left);
|
||||
unify(larr->right, rarr->right);
|
||||
return;
|
||||
} else if((lid = dynamic_cast<type_base*>(l.get())) &&
|
||||
(rid = dynamic_cast<type_base*>(r.get()))) {
|
||||
if(lid->name == rid->name) return;
|
||||
}
|
||||
|
||||
throw unification_error(l, r);
|
||||
}
|
||||
|
||||
void type_mgr::bind(const std::string& s, type_ptr t) {
|
||||
type_var* other = dynamic_cast<type_var*>(t.get());
|
||||
|
||||
if(other && other->name == s) return;
|
||||
types[s] = t;
|
||||
}
|
||||
65
code/compiler/08/type.hpp
Normal file
65
code/compiler/08/type.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
struct type_mgr;
|
||||
|
||||
struct type {
|
||||
virtual ~type() = default;
|
||||
|
||||
virtual void print(const type_mgr& mgr, std::ostream& to) const = 0;
|
||||
};
|
||||
|
||||
using type_ptr = std::shared_ptr<type>;
|
||||
|
||||
struct type_var : public type {
|
||||
std::string name;
|
||||
|
||||
type_var(std::string n)
|
||||
: name(std::move(n)) {}
|
||||
|
||||
void print(const type_mgr& mgr, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct type_base : public type {
|
||||
std::string name;
|
||||
|
||||
type_base(std::string n)
|
||||
: name(std::move(n)) {}
|
||||
|
||||
void print(const type_mgr& mgr, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct type_data : public type_base {
|
||||
struct constructor {
|
||||
int tag;
|
||||
};
|
||||
|
||||
std::map<std::string, constructor> constructors;
|
||||
|
||||
type_data(std::string n)
|
||||
: type_base(std::move(n)) {}
|
||||
};
|
||||
|
||||
struct type_arr : public type {
|
||||
type_ptr left;
|
||||
type_ptr right;
|
||||
|
||||
type_arr(type_ptr l, type_ptr r)
|
||||
: left(std::move(l)), right(std::move(r)) {}
|
||||
|
||||
void print(const type_mgr& mgr, std::ostream& to) const;
|
||||
};
|
||||
|
||||
struct type_mgr {
|
||||
int last_id = 0;
|
||||
std::map<std::string, type_ptr> types;
|
||||
|
||||
std::string new_type_name();
|
||||
type_ptr new_type();
|
||||
type_ptr new_arrow_type();
|
||||
|
||||
void unify(type_ptr l, type_ptr r);
|
||||
type_ptr resolve(type_ptr t, type_var*& var) const;
|
||||
void bind(const std::string& s, type_ptr t);
|
||||
};
|
||||
16
code/compiler/08/type_env.cpp
Normal file
16
code/compiler/08/type_env.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "type_env.hpp"
|
||||
|
||||
type_ptr type_env::lookup(const std::string& name) const {
|
||||
auto it = names.find(name);
|
||||
if(it != names.end()) return it->second;
|
||||
if(parent) return parent->lookup(name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void type_env::bind(const std::string& name, type_ptr t) {
|
||||
names[name] = t;
|
||||
}
|
||||
|
||||
type_env type_env::scope() const {
|
||||
return type_env(this);
|
||||
}
|
||||
16
code/compiler/08/type_env.hpp
Normal file
16
code/compiler/08/type_env.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include "type.hpp"
|
||||
|
||||
struct type_env {
|
||||
std::map<std::string, type_ptr> names;
|
||||
type_env const* parent = nullptr;
|
||||
|
||||
type_env(type_env const* p)
|
||||
: parent(p) {}
|
||||
type_env() : type_env(nullptr) {}
|
||||
|
||||
type_ptr lookup(const std::string& name) const;
|
||||
void bind(const std::string& name, type_ptr t);
|
||||
type_env scope() const;
|
||||
};
|
||||
42
code/compiler/09/CMakeLists.txt
Normal file
42
code/compiler/09/CMakeLists.txt
Normal file
@@ -0,0 +1,42 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(compiler)
|
||||
|
||||
# Find all the required packages
|
||||
find_package(BISON)
|
||||
find_package(FLEX)
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
|
||||
# Set up the flex and bison targets
|
||||
bison_target(parser
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/parser.y
|
||||
${CMAKE_CURRENT_BINARY_DIR}/parser.cpp
|
||||
COMPILE_FLAGS "-d")
|
||||
flex_target(scanner
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/scanner.l
|
||||
${CMAKE_CURRENT_BINARY_DIR}/scanner.cpp)
|
||||
add_flex_bison_dependency(scanner parser)
|
||||
|
||||
# Find all the relevant LLVM components
|
||||
llvm_map_components_to_libnames(LLVM_LIBS core x86asmparser x86codegen)
|
||||
|
||||
# Create compiler executable
|
||||
add_executable(compiler
|
||||
ast.cpp ast.hpp definition.cpp
|
||||
llvm_context.cpp llvm_context.hpp
|
||||
type_env.cpp type_env.hpp
|
||||
env.cpp env.hpp
|
||||
type.cpp type.hpp
|
||||
error.cpp error.hpp
|
||||
binop.cpp binop.hpp
|
||||
instruction.cpp instruction.hpp
|
||||
${BISON_parser_OUTPUTS}
|
||||
${FLEX_scanner_OUTPUTS}
|
||||
main.cpp
|
||||
)
|
||||
|
||||
# Configure compiler executable
|
||||
target_include_directories(compiler PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(compiler PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_include_directories(compiler PUBLIC ${LLVM_INCLUDE_DIRS})
|
||||
target_compile_definitions(compiler PUBLIC ${LLVM_DEFINITIONS})
|
||||
target_link_libraries(compiler ${LLVM_LIBS})
|
||||
264
code/compiler/09/ast.cpp
Normal file
264
code/compiler/09/ast.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
#include "ast.hpp"
|
||||
#include <ostream>
|
||||
#include "binop.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
static void print_indent(int n, std::ostream& to) {
|
||||
while(n--) to << " ";
|
||||
}
|
||||
|
||||
type_ptr ast::typecheck_common(type_mgr& mgr, const type_env& env) {
|
||||
node_type = typecheck(mgr, env);
|
||||
return node_type;
|
||||
}
|
||||
|
||||
void ast::resolve_common(const type_mgr& mgr) {
|
||||
type_var* var;
|
||||
type_ptr resolved_type = mgr.resolve(node_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
|
||||
resolve(mgr);
|
||||
node_type = std::move(resolved_type);
|
||||
}
|
||||
|
||||
void ast_int::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "INT: " << value << std::endl;
|
||||
}
|
||||
|
||||
type_ptr ast_int::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return type_ptr(new type_base("Int"));
|
||||
}
|
||||
|
||||
void ast_int::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_int::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
into.push_back(instruction_ptr(new instruction_pushint(value)));
|
||||
}
|
||||
|
||||
void ast_lid::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "LID: " << id << std::endl;
|
||||
}
|
||||
|
||||
type_ptr ast_lid::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return env.lookup(id);
|
||||
}
|
||||
|
||||
void ast_lid::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_lid::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)));
|
||||
}
|
||||
|
||||
void ast_uid::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "UID: " << id << std::endl;
|
||||
}
|
||||
|
||||
type_ptr ast_uid::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
return env.lookup(id);
|
||||
}
|
||||
|
||||
void ast_uid::resolve(const type_mgr& mgr) const {
|
||||
|
||||
}
|
||||
|
||||
void ast_uid::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
into.push_back(instruction_ptr(new instruction_pushglobal(id)));
|
||||
}
|
||||
|
||||
void ast_binop::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "BINOP: " << op_name(op) << std::endl;
|
||||
left->print(indent + 1, to);
|
||||
right->print(indent + 1, to);
|
||||
}
|
||||
|
||||
type_ptr ast_binop::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_ptr ltype = left->typecheck_common(mgr, env);
|
||||
type_ptr rtype = right->typecheck_common(mgr, env);
|
||||
type_ptr ftype = env.lookup(op_name(op));
|
||||
if(!ftype) throw type_error(std::string("unknown binary operator ") + op_name(op));
|
||||
|
||||
type_ptr return_type = mgr.new_type();
|
||||
type_ptr arrow_one = type_ptr(new type_arr(rtype, return_type));
|
||||
type_ptr arrow_two = type_ptr(new type_arr(ltype, arrow_one));
|
||||
|
||||
mgr.unify(arrow_two, ftype);
|
||||
return return_type;
|
||||
}
|
||||
|
||||
void ast_binop::resolve(const type_mgr& mgr) const {
|
||||
left->resolve_common(mgr);
|
||||
right->resolve_common(mgr);
|
||||
}
|
||||
|
||||
void ast_binop::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
right->compile(env, into);
|
||||
left->compile(env_ptr(new env_offset(1, env)), into);
|
||||
|
||||
into.push_back(instruction_ptr(new instruction_pushglobal(op_action(op))));
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
}
|
||||
|
||||
void ast_app::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "APP:" << std::endl;
|
||||
left->print(indent + 1, to);
|
||||
right->print(indent + 1, to);
|
||||
}
|
||||
|
||||
type_ptr ast_app::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_ptr ltype = left->typecheck_common(mgr, env);
|
||||
type_ptr rtype = right->typecheck_common(mgr, env);
|
||||
|
||||
type_ptr return_type = mgr.new_type();
|
||||
type_ptr arrow = type_ptr(new type_arr(rtype, return_type));
|
||||
mgr.unify(arrow, ltype);
|
||||
return return_type;
|
||||
}
|
||||
|
||||
void ast_app::resolve(const type_mgr& mgr) const {
|
||||
left->resolve_common(mgr);
|
||||
right->resolve_common(mgr);
|
||||
}
|
||||
|
||||
void ast_app::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
right->compile(env, into);
|
||||
left->compile(env_ptr(new env_offset(1, env)), into);
|
||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||
}
|
||||
|
||||
void ast_case::print(int indent, std::ostream& to) const {
|
||||
print_indent(indent, to);
|
||||
to << "CASE: " << std::endl;
|
||||
for(auto& branch : branches) {
|
||||
print_indent(indent + 1, to);
|
||||
branch->pat->print(to);
|
||||
to << std::endl;
|
||||
branch->expr->print(indent + 2, to);
|
||||
}
|
||||
}
|
||||
|
||||
type_ptr ast_case::typecheck(type_mgr& mgr, const type_env& env) const {
|
||||
type_var* var;
|
||||
type_ptr case_type = mgr.resolve(of->typecheck_common(mgr, env), var);
|
||||
type_ptr branch_type = mgr.new_type();
|
||||
|
||||
for(auto& branch : branches) {
|
||||
type_env new_env = env.scope();
|
||||
branch->pat->match(case_type, mgr, new_env);
|
||||
type_ptr curr_branch_type = branch->expr->typecheck_common(mgr, new_env);
|
||||
mgr.unify(branch_type, curr_branch_type);
|
||||
}
|
||||
|
||||
case_type = mgr.resolve(case_type, var);
|
||||
if(!dynamic_cast<type_data*>(case_type.get())) {
|
||||
throw type_error("attempting case analysis of non-data type");
|
||||
}
|
||||
|
||||
return branch_type;
|
||||
}
|
||||
|
||||
void ast_case::resolve(const type_mgr& mgr) const {
|
||||
of->resolve_common(mgr);
|
||||
for(auto& branch : branches) {
|
||||
branch->expr->resolve_common(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
void ast_case::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const {
|
||||
type_data* type = dynamic_cast<type_data*>(of->node_type.get());
|
||||
|
||||
of->compile(env, into);
|
||||
into.push_back(instruction_ptr(new instruction_eval()));
|
||||
|
||||
instruction_jump* jump_instruction = new instruction_jump();
|
||||
into.push_back(instruction_ptr(jump_instruction));
|
||||
for(auto& branch : branches) {
|
||||
std::vector<instruction_ptr> branch_instructions;
|
||||
pattern_var* vpat;
|
||||
pattern_constr* cpat;
|
||||
|
||||
if((vpat = dynamic_cast<pattern_var*>(branch->pat.get()))) {
|
||||
branch->expr->compile(env_ptr(new env_offset(1, env)), branch_instructions);
|
||||
|
||||
for(auto& constr_pair : type->constructors) {
|
||||
if(jump_instruction->tag_mappings.find(constr_pair.second.tag) !=
|
||||
jump_instruction->tag_mappings.end())
|
||||
break;
|
||||
|
||||
jump_instruction->tag_mappings[constr_pair.second.tag] =
|
||||
jump_instruction->branches.size();
|
||||
}
|
||||
jump_instruction->branches.push_back(std::move(branch_instructions));
|
||||
} 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(
|
||||
cpat->params.size())));
|
||||
branch->expr->compile(new_env, branch_instructions);
|
||||
branch_instructions.push_back(instruction_ptr(new instruction_slide(
|
||||
cpat->params.size())));
|
||||
|
||||
int new_tag = type->constructors[cpat->constr].tag;
|
||||
if(jump_instruction->tag_mappings.find(new_tag) !=
|
||||
jump_instruction->tag_mappings.end())
|
||||
throw type_error("technically not a type error: duplicate pattern");
|
||||
|
||||
jump_instruction->tag_mappings[new_tag] =
|
||||
jump_instruction->branches.size();
|
||||
jump_instruction->branches.push_back(std::move(branch_instructions));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& constr_pair : type->constructors) {
|
||||
if(jump_instruction->tag_mappings.find(constr_pair.second.tag) ==
|
||||
jump_instruction->tag_mappings.end())
|
||||
throw type_error("non-total pattern");
|
||||
}
|
||||
}
|
||||
|
||||
void pattern_var::print(std::ostream& to) const {
|
||||
to << var;
|
||||
}
|
||||
|
||||
void pattern_var::match(type_ptr t, type_mgr& mgr, type_env& env) const {
|
||||
env.bind(var, t);
|
||||
}
|
||||
|
||||
void pattern_constr::print(std::ostream& to) const {
|
||||
to << constr;
|
||||
for(auto& param : params) {
|
||||
to << " " << param;
|
||||
}
|
||||
}
|
||||
|
||||
void pattern_constr::match(type_ptr t, type_mgr& mgr, type_env& env) const {
|
||||
type_ptr constructor_type = env.lookup(constr);
|
||||
if(!constructor_type) {
|
||||
throw type_error(std::string("pattern using unknown constructor ") + constr);
|
||||
}
|
||||
|
||||
for(int i = 0; i < params.size(); i++) {
|
||||
type_arr* arr = dynamic_cast<type_arr*>(constructor_type.get());
|
||||
if(!arr) throw type_error("too many parameters in constructor pattern");
|
||||
|
||||
env.bind(params[i], arr->left);
|
||||
constructor_type = arr->right;
|
||||
}
|
||||
|
||||
mgr.unify(t, constructor_type);
|
||||
}
|
||||
141
code/compiler/09/ast.hpp
Normal file
141
code/compiler/09/ast.hpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "type.hpp"
|
||||
#include "type_env.hpp"
|
||||
#include "binop.hpp"
|
||||
#include "instruction.hpp"
|
||||
#include "env.hpp"
|
||||
|
||||
struct ast {
|
||||
type_ptr node_type;
|
||||
|
||||
virtual ~ast() = default;
|
||||
|
||||
virtual void print(int indent, std::ostream& to) const = 0;
|
||||
virtual type_ptr typecheck(type_mgr& mgr, const type_env& env) const = 0;
|
||||
virtual void resolve(const type_mgr& mgr) const = 0;
|
||||
virtual void compile(const env_ptr& env,
|
||||
std::vector<instruction_ptr>& into) const = 0;
|
||||
|
||||
type_ptr typecheck_common(type_mgr& mgr, const type_env& env);
|
||||
void resolve_common(const type_mgr& mgr);
|
||||
};
|
||||
|
||||
using ast_ptr = std::unique_ptr<ast>;
|
||||
|
||||
struct pattern {
|
||||
virtual ~pattern() = default;
|
||||
|
||||
virtual void print(std::ostream& to) const = 0;
|
||||
virtual void match(type_ptr t, type_mgr& mgr, type_env& env) const = 0;
|
||||
};
|
||||
|
||||
using pattern_ptr = std::unique_ptr<pattern>;
|
||||
|
||||
struct branch {
|
||||
pattern_ptr pat;
|
||||
ast_ptr expr;
|
||||
|
||||
branch(pattern_ptr p, ast_ptr a)
|
||||
: pat(std::move(p)), expr(std::move(a)) {}
|
||||
};
|
||||
|
||||
using branch_ptr = std::unique_ptr<branch>;
|
||||
|
||||
struct ast_int : public ast {
|
||||
int value;
|
||||
|
||||
explicit ast_int(int v)
|
||||
: value(v) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_lid : public ast {
|
||||
std::string id;
|
||||
|
||||
explicit ast_lid(std::string i)
|
||||
: id(std::move(i)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_uid : public ast {
|
||||
std::string id;
|
||||
|
||||
explicit ast_uid(std::string i)
|
||||
: id(std::move(i)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_binop : public ast {
|
||||
binop op;
|
||||
ast_ptr left;
|
||||
ast_ptr right;
|
||||
|
||||
ast_binop(binop o, ast_ptr l, ast_ptr r)
|
||||
: op(o), left(std::move(l)), right(std::move(r)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_app : public ast {
|
||||
ast_ptr left;
|
||||
ast_ptr right;
|
||||
|
||||
ast_app(ast_ptr l, ast_ptr r)
|
||||
: left(std::move(l)), right(std::move(r)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct ast_case : public ast {
|
||||
ast_ptr of;
|
||||
std::vector<branch_ptr> branches;
|
||||
|
||||
ast_case(ast_ptr o, std::vector<branch_ptr> b)
|
||||
: of(std::move(o)), branches(std::move(b)) {}
|
||||
|
||||
void print(int indent, std::ostream& to) const;
|
||||
type_ptr typecheck(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr) const;
|
||||
void compile(const env_ptr& env, std::vector<instruction_ptr>& into) const;
|
||||
};
|
||||
|
||||
struct pattern_var : public pattern {
|
||||
std::string var;
|
||||
|
||||
pattern_var(std::string v)
|
||||
: var(std::move(v)) {}
|
||||
|
||||
void print(std::ostream &to) const;
|
||||
void match(type_ptr t, type_mgr& mgr, type_env& env) const;
|
||||
};
|
||||
|
||||
struct pattern_constr : public pattern {
|
||||
std::string constr;
|
||||
std::vector<std::string> params;
|
||||
|
||||
pattern_constr(std::string c, std::vector<std::string> p)
|
||||
: constr(std::move(c)), params(std::move(p)) {}
|
||||
|
||||
void print(std::ostream &to) const;
|
||||
void match(type_ptr t, type_mgr&, type_env& env) const;
|
||||
};
|
||||
21
code/compiler/09/binop.cpp
Normal file
21
code/compiler/09/binop.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "binop.hpp"
|
||||
|
||||
std::string op_name(binop op) {
|
||||
switch(op) {
|
||||
case PLUS: return "+";
|
||||
case MINUS: return "-";
|
||||
case TIMES: return "*";
|
||||
case DIVIDE: return "/";
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
|
||||
std::string op_action(binop op) {
|
||||
switch(op) {
|
||||
case PLUS: return "plus";
|
||||
case MINUS: return "minus";
|
||||
case TIMES: return "times";
|
||||
case DIVIDE: return "divide";
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
12
code/compiler/09/binop.hpp
Normal file
12
code/compiler/09/binop.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
enum binop {
|
||||
PLUS,
|
||||
MINUS,
|
||||
TIMES,
|
||||
DIVIDE
|
||||
};
|
||||
|
||||
std::string op_name(binop op);
|
||||
std::string op_action(binop op);
|
||||
121
code/compiler/09/definition.cpp
Normal file
121
code/compiler/09/definition.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "definition.hpp"
|
||||
#include "error.hpp"
|
||||
#include "ast.hpp"
|
||||
#include "instruction.hpp"
|
||||
#include "llvm_context.hpp"
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/Function.h>
|
||||
#include <llvm/IR/Type.h>
|
||||
|
||||
void definition_defn::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||
return_type = mgr.new_type();
|
||||
type_ptr full_type = return_type;
|
||||
|
||||
for(auto it = params.rbegin(); it != params.rend(); it++) {
|
||||
type_ptr param_type = mgr.new_type();
|
||||
full_type = type_ptr(new type_arr(param_type, full_type));
|
||||
param_types.push_back(param_type);
|
||||
}
|
||||
|
||||
env.bind(name, full_type);
|
||||
}
|
||||
|
||||
void definition_defn::typecheck_second(type_mgr& mgr, const type_env& env) const {
|
||||
type_env new_env = env.scope();
|
||||
auto param_it = params.begin();
|
||||
auto type_it = param_types.rbegin();
|
||||
|
||||
while(param_it != params.end() && type_it != param_types.rend()) {
|
||||
new_env.bind(*param_it, *type_it);
|
||||
param_it++;
|
||||
type_it++;
|
||||
}
|
||||
|
||||
type_ptr body_type = body->typecheck_common(mgr, new_env);
|
||||
mgr.unify(return_type, body_type);
|
||||
}
|
||||
|
||||
void definition_defn::resolve(const type_mgr& mgr) {
|
||||
type_var* var;
|
||||
body->resolve_common(mgr);
|
||||
|
||||
return_type = mgr.resolve(return_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
for(auto& param_type : param_types) {
|
||||
param_type = mgr.resolve(param_type, var);
|
||||
if(var) throw type_error("ambiguously typed program");
|
||||
}
|
||||
}
|
||||
|
||||
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())));
|
||||
instructions.push_back(instruction_ptr(new instruction_pop(params.size())));
|
||||
}
|
||||
void definition_defn::gen_llvm_first(llvm_context& ctx) {
|
||||
generated_function = ctx.create_custom_function(name, params.size());
|
||||
}
|
||||
|
||||
void definition_defn::gen_llvm_second(llvm_context& ctx) {
|
||||
ctx.builder.SetInsertPoint(&generated_function->getEntryBlock());
|
||||
for(auto& instruction : instructions) {
|
||||
instruction->gen_llvm(ctx, generated_function);
|
||||
}
|
||||
ctx.builder.CreateRetVoid();
|
||||
}
|
||||
|
||||
void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||
type_data* this_type = new type_data(name);
|
||||
type_ptr return_type = type_ptr(this_type);
|
||||
int next_tag = 0;
|
||||
|
||||
for(auto& constructor : constructors) {
|
||||
constructor->tag = next_tag;
|
||||
this_type->constructors[constructor->name] = { next_tag++ };
|
||||
|
||||
type_ptr full_type = return_type;
|
||||
for(auto it = constructor->types.rbegin(); it != constructor->types.rend(); it++) {
|
||||
type_ptr type = type_ptr(new type_base(*it));
|
||||
full_type = type_ptr(new type_arr(type, full_type));
|
||||
}
|
||||
|
||||
env.bind(constructor->name, full_type);
|
||||
}
|
||||
}
|
||||
|
||||
void definition_data::typecheck_second(type_mgr& mgr, const type_env& env) const {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
void definition_data::resolve(const type_mgr& mgr) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
void definition_data::compile() {
|
||||
|
||||
}
|
||||
|
||||
void definition_data::gen_llvm_first(llvm_context& ctx) {
|
||||
for(auto& constructor : constructors) {
|
||||
auto new_function =
|
||||
ctx.create_custom_function(constructor->name, constructor->types.size());
|
||||
std::vector<instruction_ptr> instructions;
|
||||
instructions.push_back(instruction_ptr(
|
||||
new instruction_pack(constructor->tag, constructor->types.size())
|
||||
));
|
||||
instructions.push_back(instruction_ptr(new instruction_update(0)));
|
||||
ctx.builder.SetInsertPoint(&new_function->getEntryBlock());
|
||||
for (auto& instruction : instructions) {
|
||||
instruction->gen_llvm(ctx, new_function);
|
||||
}
|
||||
ctx.builder.CreateRetVoid();
|
||||
}
|
||||
}
|
||||
|
||||
void definition_data::gen_llvm_second(llvm_context& ctx) {
|
||||
// Nothing
|
||||
}
|
||||
73
code/compiler/09/definition.hpp
Normal file
73
code/compiler/09/definition.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "instruction.hpp"
|
||||
#include "llvm_context.hpp"
|
||||
#include "type_env.hpp"
|
||||
|
||||
struct ast;
|
||||
using ast_ptr = std::unique_ptr<ast>;
|
||||
|
||||
struct definition {
|
||||
virtual ~definition() = default;
|
||||
|
||||
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 resolve(const type_mgr& mgr) = 0;
|
||||
virtual void compile() = 0;
|
||||
virtual void gen_llvm_first(llvm_context& ctx) = 0;
|
||||
virtual void gen_llvm_second(llvm_context& ctx) = 0;
|
||||
};
|
||||
|
||||
using definition_ptr = std::unique_ptr<definition>;
|
||||
|
||||
struct constructor {
|
||||
std::string name;
|
||||
std::vector<std::string> types;
|
||||
int8_t tag;
|
||||
|
||||
constructor(std::string n, std::vector<std::string> ts)
|
||||
: name(std::move(n)), types(std::move(ts)) {}
|
||||
};
|
||||
|
||||
using constructor_ptr = std::unique_ptr<constructor>;
|
||||
|
||||
struct definition_defn : public definition {
|
||||
std::string name;
|
||||
std::vector<std::string> params;
|
||||
ast_ptr body;
|
||||
|
||||
type_ptr return_type;
|
||||
std::vector<type_ptr> param_types;
|
||||
|
||||
std::vector<instruction_ptr> instructions;
|
||||
|
||||
llvm::Function* generated_function;
|
||||
|
||||
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)) {
|
||||
|
||||
}
|
||||
|
||||
void typecheck_first(type_mgr& mgr, type_env& env);
|
||||
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr);
|
||||
void compile();
|
||||
void gen_llvm_first(llvm_context& ctx);
|
||||
void gen_llvm_second(llvm_context& ctx);
|
||||
};
|
||||
|
||||
struct definition_data : public definition {
|
||||
std::string name;
|
||||
std::vector<constructor_ptr> constructors;
|
||||
|
||||
definition_data(std::string n, std::vector<constructor_ptr> cs)
|
||||
: name(std::move(n)), constructors(std::move(cs)) {}
|
||||
|
||||
void typecheck_first(type_mgr& mgr, type_env& env);
|
||||
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
||||
void resolve(const type_mgr& mgr);
|
||||
void compile();
|
||||
void gen_llvm_first(llvm_context& ctx);
|
||||
void gen_llvm_second(llvm_context& ctx);
|
||||
};
|
||||
23
code/compiler/09/env.cpp
Normal file
23
code/compiler/09/env.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "env.hpp"
|
||||
|
||||
int env_var::get_offset(const std::string& name) const {
|
||||
if(name == this->name) return 0;
|
||||
if(parent) return parent->get_offset(name) + 1;
|
||||
throw 0;
|
||||
}
|
||||
|
||||
bool env_var::has_variable(const std::string& name) const {
|
||||
if(name == this->name) return true;
|
||||
if(parent) return parent->has_variable(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
int env_offset::get_offset(const std::string& name) const {
|
||||
if(parent) return parent->get_offset(name) + offset;
|
||||
throw 0;
|
||||
}
|
||||
|
||||
bool env_offset::has_variable(const std::string& name) const {
|
||||
if(parent) return parent->has_variable(name);
|
||||
return false;
|
||||
}
|
||||
34
code/compiler/09/env.hpp
Normal file
34
code/compiler/09/env.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
struct env {
|
||||
virtual ~env() = default;
|
||||
|
||||
virtual int get_offset(const std::string& name) const = 0;
|
||||
virtual bool has_variable(const std::string& name) const = 0;
|
||||
};
|
||||
|
||||
using env_ptr = std::shared_ptr<env>;
|
||||
|
||||
struct env_var : public env {
|
||||
std::string name;
|
||||
env_ptr parent;
|
||||
|
||||
env_var(std::string& n, env_ptr p)
|
||||
: name(std::move(n)), parent(std::move(p)) {}
|
||||
|
||||
int get_offset(const std::string& name) const;
|
||||
bool has_variable(const std::string& name) const;
|
||||
};
|
||||
|
||||
struct env_offset : public env {
|
||||
int offset;
|
||||
env_ptr parent;
|
||||
|
||||
env_offset(int o, env_ptr p)
|
||||
: offset(o), parent(std::move(p)) {}
|
||||
|
||||
int get_offset(const std::string& name) const;
|
||||
bool has_variable(const std::string& name) const;
|
||||
};
|
||||
5
code/compiler/09/error.cpp
Normal file
5
code/compiler/09/error.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "error.hpp"
|
||||
|
||||
const char* type_error::what() const noexcept {
|
||||
return "an error occured while checking the types of the program";
|
||||
}
|
||||
21
code/compiler/09/error.hpp
Normal file
21
code/compiler/09/error.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <exception>
|
||||
#include "type.hpp"
|
||||
|
||||
struct type_error : std::exception {
|
||||
std::string description;
|
||||
|
||||
type_error(std::string d)
|
||||
: description(std::move(d)) {}
|
||||
|
||||
const char* what() const noexcept override;
|
||||
};
|
||||
|
||||
struct unification_error : public type_error {
|
||||
type_ptr left;
|
||||
type_ptr right;
|
||||
|
||||
unification_error(type_ptr l, type_ptr r)
|
||||
: left(std::move(l)), right(std::move(r)),
|
||||
type_error("failed to unify types") {}
|
||||
};
|
||||
2
code/compiler/09/examples/bad1.txt
Normal file
2
code/compiler/09/examples/bad1.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
data Bool = { True, False }
|
||||
defn main = { 3 + True }
|
||||
1
code/compiler/09/examples/bad2.txt
Normal file
1
code/compiler/09/examples/bad2.txt
Normal file
@@ -0,0 +1 @@
|
||||
defn main = { 1 2 3 4 5 }
|
||||
8
code/compiler/09/examples/bad3.txt
Normal file
8
code/compiler/09/examples/bad3.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
data List = { Nil, Cons Int List }
|
||||
|
||||
defn head l = {
|
||||
case l of {
|
||||
Nil -> { 0 }
|
||||
Cons x y z -> { x }
|
||||
}
|
||||
}
|
||||
129
code/compiler/09/examples/primes.txt
Normal file
129
code/compiler/09/examples/primes.txt
Normal file
@@ -0,0 +1,129 @@
|
||||
data List = { Nil, Cons Nat List }
|
||||
data Bool = { True, False }
|
||||
data Nat = { O, S Nat }
|
||||
|
||||
defn ifN c t e = {
|
||||
case c of {
|
||||
True -> { t }
|
||||
False -> { e }
|
||||
}
|
||||
}
|
||||
|
||||
defn ifL c t e = {
|
||||
case c of {
|
||||
True -> { t }
|
||||
False -> { e }
|
||||
}
|
||||
}
|
||||
|
||||
defn toInt n = {
|
||||
case n of {
|
||||
O -> { 0 }
|
||||
S np -> { 1 + toInt np }
|
||||
}
|
||||
}
|
||||
|
||||
defn lte n m = {
|
||||
case m of {
|
||||
O -> {
|
||||
case n of {
|
||||
O -> { True }
|
||||
S np -> { False }
|
||||
}
|
||||
}
|
||||
S mp -> {
|
||||
case n of {
|
||||
O -> { True }
|
||||
S np -> { lte np mp }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defn minus n m = {
|
||||
case m of {
|
||||
O -> { n }
|
||||
S mp -> {
|
||||
case n of {
|
||||
O -> { O }
|
||||
S np -> {
|
||||
minus np mp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defn mod n m = {
|
||||
ifN (lte m n) (mod (minus n m) m) n
|
||||
}
|
||||
|
||||
defn notDivisibleBy n m = {
|
||||
case (mod m n) of {
|
||||
O -> { False }
|
||||
S mp -> { True }
|
||||
}
|
||||
}
|
||||
|
||||
defn filter f l = {
|
||||
case l of {
|
||||
Nil -> { Nil }
|
||||
Cons x xs -> { ifL (f x) (Cons x (filter f xs)) (filter f xs) }
|
||||
}
|
||||
}
|
||||
|
||||
defn map f l = {
|
||||
case l of {
|
||||
Nil -> { Nil }
|
||||
Cons x xs -> { Cons (f x) (map f xs) }
|
||||
}
|
||||
}
|
||||
|
||||
defn nats = {
|
||||
Cons (S (S O)) (map S nats)
|
||||
}
|
||||
|
||||
defn primesRec l = {
|
||||
case l of {
|
||||
Nil -> { Nil }
|
||||
Cons p xs -> { Cons p (primesRec (filter (notDivisibleBy p) xs)) }
|
||||
}
|
||||
}
|
||||
|
||||
defn primes = {
|
||||
primesRec nats
|
||||
}
|
||||
|
||||
defn take n l = {
|
||||
case l of {
|
||||
Nil -> { Nil }
|
||||
Cons x xs -> {
|
||||
case n of {
|
||||
O -> { Nil }
|
||||
S np -> { Cons x (take np xs) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defn head l = {
|
||||
case l of {
|
||||
Nil -> { O }
|
||||
Cons x xs -> { x }
|
||||
}
|
||||
}
|
||||
|
||||
defn reverseAcc a l = {
|
||||
case l of {
|
||||
Nil -> { a }
|
||||
Cons x xs -> { reverseAcc (Cons x a) xs }
|
||||
}
|
||||
}
|
||||
|
||||
defn reverse l = {
|
||||
reverseAcc Nil l
|
||||
}
|
||||
|
||||
defn main = {
|
||||
toInt (head (reverse (take ((S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S O))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) primes)))
|
||||
}
|
||||
31
code/compiler/09/examples/runtime1.c
Normal file
31
code/compiler/09/examples/runtime1.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "../runtime.h"
|
||||
|
||||
void f_add(struct stack* s) {
|
||||
struct node_num* left = (struct node_num*) eval(stack_peek(s, 0));
|
||||
struct node_num* right = (struct node_num*) eval(stack_peek(s, 1));
|
||||
stack_push(s, (struct node_base*) alloc_num(left->value + right->value));
|
||||
}
|
||||
|
||||
void f_main(struct stack* s) {
|
||||
// PushInt 320
|
||||
stack_push(s, (struct node_base*) alloc_num(320));
|
||||
|
||||
// PushInt 6
|
||||
stack_push(s, (struct node_base*) alloc_num(6));
|
||||
|
||||
// PushGlobal f_add (the function for +)
|
||||
stack_push(s, (struct node_base*) alloc_global(f_add, 2));
|
||||
|
||||
struct node_base* left;
|
||||
struct node_base* right;
|
||||
|
||||
// MkApp
|
||||
left = stack_pop(s);
|
||||
right = stack_pop(s);
|
||||
stack_push(s, (struct node_base*) alloc_app(left, right));
|
||||
|
||||
// MkApp
|
||||
left = stack_pop(s);
|
||||
right = stack_pop(s);
|
||||
stack_push(s, (struct node_base*) alloc_app(left, right));
|
||||
}
|
||||
2
code/compiler/09/examples/works1.txt
Normal file
2
code/compiler/09/examples/works1.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
defn main = { sum 320 6 }
|
||||
defn sum x y = { x + y }
|
||||
3
code/compiler/09/examples/works2.txt
Normal file
3
code/compiler/09/examples/works2.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
defn add x y = { x + y }
|
||||
defn double x = { add x x }
|
||||
defn main = { double 163 }
|
||||
8
code/compiler/09/examples/works3.txt
Normal file
8
code/compiler/09/examples/works3.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
data List = { Nil, Cons Int List }
|
||||
defn length l = {
|
||||
case l of {
|
||||
Nil -> { 0 }
|
||||
Cons x xs -> { 1 + length xs }
|
||||
}
|
||||
}
|
||||
defn main = { length (Cons 1 (Cons 2 (Cons 3 Nil))) }
|
||||
16
code/compiler/09/examples/works4.txt
Normal file
16
code/compiler/09/examples/works4.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
data List = { Nil, Cons Int List }
|
||||
|
||||
defn add x y = { x + y }
|
||||
defn mul x y = { x * y }
|
||||
|
||||
defn foldr f b l = {
|
||||
case l of {
|
||||
Nil -> { b }
|
||||
Cons x xs -> { f x (foldr f b xs) }
|
||||
}
|
||||
}
|
||||
|
||||
defn main = {
|
||||
foldr add 0 (Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)))) +
|
||||
foldr mul 1 (Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))))
|
||||
}
|
||||
17
code/compiler/09/examples/works5.txt
Normal file
17
code/compiler/09/examples/works5.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
data List = { Nil, Cons Int List }
|
||||
|
||||
defn sumZip l m = {
|
||||
case l of {
|
||||
Nil -> { 0 }
|
||||
Cons x xs -> {
|
||||
case m of {
|
||||
Nil -> { 0 }
|
||||
Cons y ys -> { x + y + sumZip xs ys }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defn ones = { Cons 1 ones }
|
||||
|
||||
defn main = { sumZip ones (Cons 1 (Cons 2 (Cons 3 Nil))) }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user