|
|
|
@ -1,5 +1,6 @@ |
|
|
|
|
#include "ast.hpp" |
|
|
|
|
#include <ostream> |
|
|
|
|
#include <type_traits> |
|
|
|
|
#include "binop.hpp" |
|
|
|
|
#include "error.hpp" |
|
|
|
|
#include "type_env.hpp" |
|
|
|
@ -218,60 +219,184 @@ void ast_case::translate(global_scope& scope) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void ast_case::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const { |
|
|
|
|
type_app* app_type = dynamic_cast<type_app*>(input_type.get()); |
|
|
|
|
type_data* type = dynamic_cast<type_data*>(app_type->constructor.get()); |
|
|
|
|
template <typename T> |
|
|
|
|
struct case_mappings { |
|
|
|
|
using tag_type = typename T::tag_type; |
|
|
|
|
std::map<tag_type, std::vector<instruction_ptr>> defined_cases; |
|
|
|
|
std::optional<std::vector<instruction_ptr>> default_case; |
|
|
|
|
|
|
|
|
|
of->compile(env, into); |
|
|
|
|
into.push_back(instruction_ptr(new instruction_eval())); |
|
|
|
|
std::vector<instruction_ptr>& make_case_for(tag_type tag) { |
|
|
|
|
auto existing_case = defined_cases.find(tag); |
|
|
|
|
if(existing_case != defined_cases.end()) return existing_case->second; |
|
|
|
|
if(default_case) |
|
|
|
|
throw type_error("attempted pattern match after catch-all"); |
|
|
|
|
return defined_cases[tag]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
std::vector<instruction_ptr>& make_default_case() { |
|
|
|
|
if(default_case) |
|
|
|
|
throw type_error("attempted repeated use of catch-all"); |
|
|
|
|
default_case.emplace(std::vector<instruction_ptr>()); |
|
|
|
|
return *default_case; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::vector<instruction_ptr>& get_specific_case_for(tag_type tag) { |
|
|
|
|
auto existing_case = defined_cases.find(tag); |
|
|
|
|
assert(existing_case != defined_cases.end()); |
|
|
|
|
return existing_case->second; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::vector<instruction_ptr>& get_default_case() { |
|
|
|
|
assert(default_case); |
|
|
|
|
return *default_case; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool case_defined_for(tag_type tag) { |
|
|
|
|
return defined_cases.find(tag) != defined_cases.end(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool default_case_defined() { return default_case.has_value(); } |
|
|
|
|
|
|
|
|
|
size_t defined_cases_count() { return defined_cases.size(); } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct case_strategy_bool { |
|
|
|
|
using tag_type = bool; |
|
|
|
|
using repr_type = bool; |
|
|
|
|
|
|
|
|
|
tag_type tag_from_repr(repr_type b) { return b; } |
|
|
|
|
|
|
|
|
|
repr_type from_typed_pattern(const pattern_ptr& pt, const type* type) { |
|
|
|
|
pattern_constr* cpat; |
|
|
|
|
if(!(cpat = dynamic_cast<pattern_constr*>(pt.get())) || |
|
|
|
|
(cpat->constr != "True" && cpat->constr != "False") || |
|
|
|
|
cpat->params.size() != 0) |
|
|
|
|
throw type_error("pattern cannot be converted to a boolean"); |
|
|
|
|
return cpat->constr == "True"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if((vpat = dynamic_cast<pattern_var*>(branch->pat.get()))) { |
|
|
|
|
branch->expr->compile(env_ptr(new env_offset(1, env)), branch_instructions); |
|
|
|
|
void compile_branch( |
|
|
|
|
const branch_ptr& branch, |
|
|
|
|
const env_ptr& env, |
|
|
|
|
repr_type repr, |
|
|
|
|
std::vector<instruction_ptr>& into) { |
|
|
|
|
branch->expr->compile(env_ptr(new env_offset(1, env)), into); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
size_t case_count(const type* type) { |
|
|
|
|
return 2; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
instruction_ptr into_instruction(const type* type, case_mappings<case_strategy_bool>& ms) { |
|
|
|
|
throw std::runtime_error("boolean case unimplemented!"); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct case_strategy_data { |
|
|
|
|
using tag_type = int; |
|
|
|
|
using repr_type = std::pair<const type_data::constructor*, const std::vector<std::string>*>; |
|
|
|
|
|
|
|
|
|
for(auto& constr_pair : type->constructors) { |
|
|
|
|
if(jump_instruction->tag_mappings.find(constr_pair.second.tag) != |
|
|
|
|
jump_instruction->tag_mappings.end()) |
|
|
|
|
break; |
|
|
|
|
tag_type tag_from_repr(const repr_type& repr) { return repr.first->tag; } |
|
|
|
|
|
|
|
|
|
jump_instruction->tag_mappings[constr_pair.second.tag] = |
|
|
|
|
repr_type from_typed_pattern(const pattern_ptr& pt, const type* type) { |
|
|
|
|
pattern_constr* cpat; |
|
|
|
|
if(!(cpat = dynamic_cast<pattern_constr*>(pt.get()))) |
|
|
|
|
throw type_error("pattern cannot be interpreted as constructor."); |
|
|
|
|
return std::make_pair( |
|
|
|
|
&static_cast<const type_data*>(type)->constructors.find(cpat->constr)->second, |
|
|
|
|
&cpat->params); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void compile_branch( |
|
|
|
|
const branch_ptr& branch, |
|
|
|
|
const env_ptr& env, |
|
|
|
|
const repr_type& repr, |
|
|
|
|
std::vector<instruction_ptr>& into) { |
|
|
|
|
env_ptr new_env = env; |
|
|
|
|
for(auto it = repr.second->rbegin(); it != repr.second->rend(); it++) { |
|
|
|
|
new_env = env_ptr(new env_var(branch->expr->env->get_mangled_name(*it), new_env)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
into.push_back(instruction_ptr(new instruction_split(repr.second->size()))); |
|
|
|
|
branch->expr->compile(new_env, into); |
|
|
|
|
into.push_back(instruction_ptr(new instruction_slide(repr.second->size()))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
size_t case_count(const type* type) { |
|
|
|
|
return static_cast<const type_data*>(type)->constructors.size(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
instruction_ptr into_instruction(const type* type, case_mappings<case_strategy_data>& ms) { |
|
|
|
|
instruction_jump* jump_instruction = new instruction_jump(); |
|
|
|
|
instruction_ptr inst(jump_instruction); |
|
|
|
|
|
|
|
|
|
auto data_type = static_cast<const type_data*>(type); |
|
|
|
|
for(auto& constr : data_type->constructors) { |
|
|
|
|
if(!ms.case_defined_for(constr.second.tag)) continue; |
|
|
|
|
jump_instruction->branches.push_back( |
|
|
|
|
std::move(ms.get_specific_case_for(constr.second.tag))); |
|
|
|
|
jump_instruction->tag_mappings[constr.second.tag] = |
|
|
|
|
jump_instruction->branches.size() - 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(ms.default_case_defined()) { |
|
|
|
|
jump_instruction->branches.push_back( |
|
|
|
|
std::move(ms.get_default_case())); |
|
|
|
|
for(auto& constr : data_type->constructors) { |
|
|
|
|
if(ms.case_defined_for(constr.second.tag)) continue; |
|
|
|
|
jump_instruction->tag_mappings[constr.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(branch->expr->env->get_mangled_name(*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", cpat->loc); |
|
|
|
|
return std::move(inst); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
jump_instruction->tag_mappings[new_tag] = |
|
|
|
|
jump_instruction->branches.size(); |
|
|
|
|
jump_instruction->branches.push_back(std::move(branch_instructions)); |
|
|
|
|
template <typename T> |
|
|
|
|
void compile_case(const ast_case& node, const env_ptr& env, const type* type, std::vector<instruction_ptr>& into) { |
|
|
|
|
T strategy; |
|
|
|
|
case_mappings<T> cases; |
|
|
|
|
for(auto& branch : node.branches) { |
|
|
|
|
pattern_var* vpat; |
|
|
|
|
if((vpat = dynamic_cast<pattern_var*>(branch->pat.get()))) { |
|
|
|
|
auto& branch_into = cases.make_default_case(); |
|
|
|
|
env_ptr new_env(new env_var(branch->expr->env->get_mangled_name(vpat->var), env)); |
|
|
|
|
branch->expr->compile(new_env, branch_into); |
|
|
|
|
} else { |
|
|
|
|
auto repr = strategy.from_typed_pattern(branch->pat, type); |
|
|
|
|
auto& branch_into = cases.make_case_for(strategy.tag_from_repr(repr)); |
|
|
|
|
strategy.compile_branch(branch, env, repr, branch_into); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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", loc); |
|
|
|
|
if(!(cases.defined_cases_count() == strategy.case_count(type) || |
|
|
|
|
cases.default_case_defined())) |
|
|
|
|
throw type_error("incomplete patterns", node.loc); |
|
|
|
|
|
|
|
|
|
into.push_back(strategy.into_instruction(type, cases)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void ast_case::compile(const env_ptr& env, std::vector<instruction_ptr>& into) const { |
|
|
|
|
type_app* app_type = dynamic_cast<type_app*>(input_type.get()); |
|
|
|
|
type_data* data; |
|
|
|
|
type_internal* internal; |
|
|
|
|
|
|
|
|
|
of->compile(env, into); |
|
|
|
|
into.push_back(instruction_ptr(new instruction_eval())); |
|
|
|
|
|
|
|
|
|
if((data = dynamic_cast<type_data*>(app_type->constructor.get()))) { |
|
|
|
|
compile_case<case_strategy_data>(*this, env, data, into); |
|
|
|
|
return; |
|
|
|
|
} else if((internal = dynamic_cast<type_internal*>(app_type->constructor.get()))) { |
|
|
|
|
if(internal->name == "Bool") { |
|
|
|
|
compile_case<case_strategy_bool>(*this, env, data, into); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
throw std::runtime_error("no known way to compile case expression"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void ast_let::print(int indent, std::ostream& to) const { |
|
|
|
|