mirror of
https://github.com/ProgramSnail/lang_modes_check.git
synced 2025-12-06 00:58:42 +00:00
typecheck (without modes), unique mode
This commit is contained in:
parent
7055b353a9
commit
9448e2ac19
12 changed files with 602 additions and 279 deletions
|
|
@ -1,2 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include "parsing_tree.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <source_location>
|
||||
|
||||
namespace mode_check {
|
||||
|
||||
using namespace types;
|
||||
|
||||
struct VarState {
|
||||
VarState(Mode mode, size_t count = 0) : mode(mode), count(count) {}
|
||||
|
||||
Mode mode;
|
||||
size_t count = 0;
|
||||
};
|
||||
|
||||
struct State {
|
||||
friend struct Context;
|
||||
|
||||
State() { vars_stack.emplace_back(); }
|
||||
|
||||
std::optional<VarState *> get_var_state(const std::string &name,
|
||||
bool last_context_only = false) {
|
||||
for (auto vars_it = vars_stack.rbegin(); vars_it != vars_stack.rend();
|
||||
++vars_it) {
|
||||
auto var_it = vars_it->find(name);
|
||||
if (var_it == vars_it->end()) {
|
||||
if (last_context_only) {
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
return &var_it->second;
|
||||
}
|
||||
|
||||
utils::throw_error("NO_VAR");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void add_var(std::string name, Mode mode = Mode()) {
|
||||
vars_stack.back().insert({std::move(name), VarState{mode}});
|
||||
// TODO: check existance
|
||||
}
|
||||
|
||||
private:
|
||||
void enter_context() { vars_stack.emplace_back(); }
|
||||
|
||||
void exit_context() { vars_stack.pop_back(); }
|
||||
|
||||
private:
|
||||
vector<map<string, VarState>> vars_stack;
|
||||
};
|
||||
|
||||
struct Context {
|
||||
Context(State &state) : state_(state) { state_.enter_context(); }
|
||||
|
||||
~Context() { state_.exit_context(); }
|
||||
|
||||
private:
|
||||
State &state_;
|
||||
};
|
||||
|
||||
// struct ExclVarScope {
|
||||
// ExclVarScope(std::string name, State &state)
|
||||
// : name_(std::move(name)), state_(state) {}
|
||||
|
||||
// ~ExclVarScope() {}
|
||||
|
||||
// private:
|
||||
// std::string name_;
|
||||
// State &state_;
|
||||
// };
|
||||
|
||||
void check_expr(nodes::ExprPtr expr, State &state);
|
||||
|
||||
} // mode_check
|
||||
|
|
|
|||
|
|
@ -6,81 +6,68 @@
|
|||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace types {
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct Type;
|
||||
using TypePtr = shared_ptr<Type>;
|
||||
|
||||
struct ArrowType {
|
||||
vector<TypePtr> types;
|
||||
};
|
||||
|
||||
struct BoolType {};
|
||||
struct IntType {};
|
||||
// struct UnitType {};
|
||||
|
||||
struct AnyType {};
|
||||
|
||||
struct Type {
|
||||
static constexpr size_t ARROW_TYPE_INDEX = 0;
|
||||
variant<ArrowType, BoolType, IntType, AnyType> type;
|
||||
|
||||
enum class Loc { GLOBAL, LOCAL } loc = Loc::GLOBAL;
|
||||
enum class Uniq { SHARED, UNIQUE, EXCL } uniq = Uniq::SHARED;
|
||||
enum class Lin { MANY, ONCE, SEP } lin = Lin::MANY;
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
Type make_type(Args&&... args) {
|
||||
return Type{T{std::forward<Args>(args)...}};
|
||||
}
|
||||
|
||||
} // namespace types
|
||||
#include "utils.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
namespace nodes {
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct Node {
|
||||
optional<types::Type> type = std::nullopt;
|
||||
struct NodeInfo {
|
||||
optional<types::TypeID> type = std::nullopt;
|
||||
optional<types::Mode> mode = std::nullopt;
|
||||
};
|
||||
|
||||
struct Expr;
|
||||
using ExprPtr = shared_ptr<Expr>;
|
||||
using ExprPtrV = std::vector<ExprPtr>;
|
||||
|
||||
struct Arg : public Node {
|
||||
struct Arg : public NodeInfo {
|
||||
Arg(string name) : name(std::move(name)) {}
|
||||
|
||||
string name;
|
||||
};
|
||||
|
||||
struct Const : public Node {
|
||||
struct Const : public NodeInfo {
|
||||
Const(int value) : value(value) {}
|
||||
|
||||
int value;
|
||||
};
|
||||
|
||||
struct Var : public Node {
|
||||
struct Var : public NodeInfo {
|
||||
Var(string name) : name(std::move(name)) {}
|
||||
|
||||
string name;
|
||||
};
|
||||
|
||||
struct Let : public Node {
|
||||
struct Let : public NodeInfo {
|
||||
Let(Arg name, ExprPtr body, ExprPtr where)
|
||||
: name(std::move(name)), body(body), where(where) {}
|
||||
|
||||
Arg name;
|
||||
ExprPtr body;
|
||||
ExprPtr where;
|
||||
};
|
||||
|
||||
struct Lambda : public Node {
|
||||
struct Lambda : public NodeInfo {
|
||||
Lambda(vector<Arg> args, ExprPtr expr) : args(std::move(args)), expr(expr) {}
|
||||
|
||||
vector<Arg> args;
|
||||
ExprPtr expr;
|
||||
};
|
||||
|
||||
struct Call : public Node {
|
||||
struct Call : public NodeInfo {
|
||||
Call(ExprPtr func, vector<ExprPtr> args)
|
||||
: func(func), args(std::move(args)) {}
|
||||
|
||||
ExprPtr func;
|
||||
vector<ExprPtr> args;
|
||||
};
|
||||
|
||||
struct Condition : public Node {
|
||||
struct Condition : public NodeInfo {
|
||||
Condition(ExprPtr condition, ExprPtr then_case, ExprPtr else_case)
|
||||
: condition(condition), then_case(then_case), else_case(else_case) {}
|
||||
|
||||
ExprPtr condition;
|
||||
ExprPtr then_case;
|
||||
ExprPtr else_case;
|
||||
|
|
@ -96,17 +83,39 @@ struct Expr {
|
|||
variant<Const, Var, Let, Lambda, Call, Condition> value;
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
ExprPtr make_expr(Args&&... args) {
|
||||
return std::make_shared<Expr>(T{std::forward<Args>(args)...});
|
||||
template <typename T, typename... Args> ExprPtr make_expr(Args &&...args) {
|
||||
return std::make_shared<Expr>(T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
static ExprPtr lambda1(string name, ExprPtr expr) {
|
||||
return make_expr<Lambda>(vector<Arg>{{name}}, std::move(expr));
|
||||
template <typename T> inline T with_type(T node, types::Type type) {
|
||||
node.type = std::move(type);
|
||||
return node;
|
||||
}
|
||||
|
||||
ExprPtr operator_call(string name, ExprPtr left, ExprPtr right) {
|
||||
return make_expr<Call>(make_expr<Var>(name), ExprPtrV{left, right});
|
||||
template <typename T> inline T with_mode(T node, types::Mode mode) {
|
||||
node.mode = std::move(mode);
|
||||
return node;
|
||||
}
|
||||
|
||||
template <typename T> inline T with_unique(T node) {
|
||||
return with_mode(node, types::Mode(types::Mode::Uniq::UNIQUE));
|
||||
}
|
||||
|
||||
inline ExprPtr make_var(std::string name, types::Mode mode = types::Mode()) {
|
||||
return make_expr<Var>(with_mode(Var(std::move(name)), mode));
|
||||
}
|
||||
|
||||
inline ExprPtr lambda1(string var, ExprPtr expr) {
|
||||
return make_expr<Lambda>(vector<Arg>{Arg(var)}, std::move(expr));
|
||||
}
|
||||
|
||||
inline ExprPtr lambda1(Arg var, ExprPtr expr) {
|
||||
return make_expr<Lambda>(vector<Arg>{var}, std::move(expr));
|
||||
}
|
||||
|
||||
inline ExprPtr operator_call(string name, ExprPtr left, ExprPtr right,
|
||||
types::Mode mode = types::Mode()) {
|
||||
return make_expr<Call>(make_var(name, mode), ExprPtrV{left, right});
|
||||
}
|
||||
|
||||
// TODO: all constructors
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
#pragma once
|
||||
|
||||
#include "parsing_tree.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <source_location>
|
||||
|
||||
namespace type_check {
|
||||
|
||||
using namespace types;
|
||||
|
||||
struct VarManager {
|
||||
friend struct Context;
|
||||
|
||||
VarManager() { vars_stack.emplace_back(); }
|
||||
|
||||
optional<TypeID> get_var_type(const std::string &name,
|
||||
bool last_context_only = false) {
|
||||
for (auto vars_it = vars_stack.rbegin(); vars_it != vars_stack.rend();
|
||||
++vars_it) {
|
||||
auto var_it = vars_it->find(name);
|
||||
if (var_it == vars_it->end()) {
|
||||
if (last_context_only) {
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
return var_it->second;
|
||||
}
|
||||
|
||||
utils::throw_error("NO_VAR");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void add_var(std::string name, TypeID type) {
|
||||
vars_stack.back().insert({std::move(name), type});
|
||||
// TODO: check existance
|
||||
}
|
||||
|
||||
private:
|
||||
void enter_context() { vars_stack.emplace_back(); }
|
||||
|
||||
void exit_context() { vars_stack.pop_back(); }
|
||||
|
||||
private:
|
||||
vector<map<string, TypeID>> vars_stack;
|
||||
};
|
||||
|
||||
struct Context {
|
||||
Context(VarManager &manager) : manager_(manager) { manager_.enter_context(); }
|
||||
|
||||
~Context() { manager_.exit_context(); }
|
||||
|
||||
private:
|
||||
VarManager &manager_;
|
||||
};
|
||||
|
||||
// ---------------
|
||||
|
||||
struct State {
|
||||
types::Storage type_storage;
|
||||
VarManager manager;
|
||||
};
|
||||
|
||||
// struct GenericVarContext {
|
||||
// GenericVarContext() { /*introduce generic*/ }
|
||||
// ~GenericVarContext() { /*resolve generic (two ways: as let, or as func
|
||||
// arg)*/ }
|
||||
// };
|
||||
|
||||
types::TypeID check_expr(nodes::ExprPtr expr, State &state);
|
||||
|
||||
} // namespace type_check
|
||||
165
include/types.hpp
Normal file
165
include/types.hpp
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
|
||||
namespace types {
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct Mode {
|
||||
enum class Loc { GLOBAL, LOCAL } loc = Loc::GLOBAL;
|
||||
enum class Uniq { SHARED, UNIQUE, EXCL } uniq = Uniq::SHARED;
|
||||
enum class Lin { MANY, ONCE, SEP } lin = Lin::MANY;
|
||||
|
||||
Mode with(Loc mode) const {
|
||||
Mode copy = *this;
|
||||
copy.loc = mode;
|
||||
return copy;
|
||||
}
|
||||
Mode with(Uniq mode) const {
|
||||
Mode copy = *this;
|
||||
copy.uniq = mode;
|
||||
return copy;
|
||||
}
|
||||
Mode with(Lin mode) const {
|
||||
Mode copy = *this;
|
||||
copy.lin = mode;
|
||||
return copy;
|
||||
}
|
||||
|
||||
Mode() = default;
|
||||
Mode(Loc mode) : loc(mode) {}
|
||||
Mode(Uniq mode) : uniq(mode) {}
|
||||
Mode(Lin mode) : lin(mode) {}
|
||||
};
|
||||
using ModePtr = shared_ptr<Mode>;
|
||||
|
||||
struct Storage;
|
||||
struct Type;
|
||||
struct TypeID {
|
||||
TypeID(size_t id, Storage *storage) : id(id), storage(storage) {}
|
||||
|
||||
const Type &get() const;
|
||||
Type &get();
|
||||
|
||||
private:
|
||||
size_t id;
|
||||
Storage *storage = nullptr;
|
||||
};
|
||||
using TypeIDV = vector<TypeID>;
|
||||
|
||||
struct ArrowType {
|
||||
vector<TypeID> types;
|
||||
};
|
||||
|
||||
struct BoolType {};
|
||||
struct IntType {};
|
||||
// struct UnitType {};
|
||||
|
||||
struct GenericType {
|
||||
size_t id;
|
||||
};
|
||||
|
||||
struct Type {
|
||||
variant<ArrowType, BoolType, IntType, GenericType> type;
|
||||
};
|
||||
|
||||
template <typename T, typename... Args> Type make_type(Args &&...args) {
|
||||
return Type{T{std::forward<Args>(args)...}};
|
||||
}
|
||||
|
||||
template <typename T, typename... Args> Type make_func1(TypeID in, TypeID ret) {
|
||||
return make_type<ArrowType>(TypeIDV{in, ret});
|
||||
}
|
||||
|
||||
inline Type make_operator(TypeID left, TypeID right, TypeID ret) {
|
||||
return make_type<ArrowType>(TypeIDV{left, right, ret});
|
||||
}
|
||||
|
||||
struct Storage {
|
||||
Storage()
|
||||
: int_type(add(make_type<IntType>())),
|
||||
bool_type(add(make_type<BoolType>())) {}
|
||||
|
||||
TypeID get_int_type() { return int_type; }
|
||||
TypeID get_bool_type() { return bool_type; }
|
||||
|
||||
Type &get_type(size_t id) { return types[id]; }
|
||||
|
||||
const Type &get_type(size_t id) const { return types[id]; }
|
||||
|
||||
TypeID introduce_new_generic() {
|
||||
return add(make_type<GenericType>(first_unused_generic_id++));
|
||||
}
|
||||
|
||||
TypeID add(Type type) {
|
||||
types.push_back(std::move(type));
|
||||
return TypeID(types.size() - 1, this);
|
||||
}
|
||||
|
||||
// TODO: add modes ??
|
||||
bool unify(TypeID left_id, TypeID right_id) {
|
||||
Type &left = left_id.get();
|
||||
Type &right = right_id.get();
|
||||
|
||||
if (const auto *left_generic = get_if<GenericType>(&left.type);
|
||||
left_generic != nullptr) {
|
||||
// TODO: check if other type contins generic
|
||||
resolve(*left_generic, right);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (const auto *right_generic = get_if<GenericType>(&right.type);
|
||||
right_generic != nullptr) {
|
||||
// TODO: check if other type contins generic
|
||||
resolve(*right_generic, left);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (left.type.index() != right.type.index()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (holds_alternative<ArrowType>(left.type)) {
|
||||
const auto &left_types = std::get<ArrowType>(left.type).types;
|
||||
const auto &right_types = std::get<ArrowType>(right.type).types;
|
||||
|
||||
if (left_types.size() != right_types.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool all_unify_passed = true;
|
||||
for (size_t i = 0; i < left_types.size(); ++i) {
|
||||
if (not unify(left_types[i], right_types[i])) {
|
||||
all_unify_passed = false;
|
||||
}
|
||||
}
|
||||
|
||||
return all_unify_passed;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void resolve(GenericType generic, const Type &replacement) {
|
||||
for (auto &type : types) {
|
||||
if (const auto *generic_type = get_if<GenericType>(&type.type);
|
||||
generic_type != nullptr and generic_type->id == generic.id) {
|
||||
type = replacement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t first_unused_generic_id = 0;
|
||||
|
||||
vector<Type> types;
|
||||
|
||||
TypeID int_type;
|
||||
TypeID bool_type;
|
||||
};
|
||||
|
||||
} // namespace types
|
||||
|
||||
39
include/utils.hpp
Normal file
39
include/utils.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <source_location>
|
||||
#include <string>
|
||||
|
||||
namespace utils {
|
||||
|
||||
using namespace std;
|
||||
|
||||
// C++ 23
|
||||
[[noreturn]] inline void unreachable() {
|
||||
// Uses compiler specific extensions if possible.
|
||||
// Even if no extension is used, undefined behavior is still raised by
|
||||
// an empty function body and the noreturn attribute.
|
||||
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
|
||||
__assume(false);
|
||||
#else // GCC, Clang
|
||||
__builtin_unreachable();
|
||||
#endif
|
||||
}
|
||||
|
||||
// -----------------
|
||||
|
||||
// visitor helper
|
||||
template <typename... Ts> struct overloaded : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
struct Error {
|
||||
string message;
|
||||
source_location location;
|
||||
};
|
||||
|
||||
inline void throw_error(string message,
|
||||
source_location location = source_location::current()) {
|
||||
throw Error{std::move(message), location};
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
Loading…
Add table
Add a link
Reference in a new issue