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
|
|
@ -17,5 +17,6 @@ include_directories(
|
||||||
|
|
||||||
add_executable(lang src/main.cpp
|
add_executable(lang src/main.cpp
|
||||||
src/parsing_tree.cpp
|
src/parsing_tree.cpp
|
||||||
|
src/types.cpp
|
||||||
src/type_check.cpp
|
src/type_check.cpp
|
||||||
src/mode_check.cpp)
|
src/mode_check.cpp)
|
||||||
|
|
|
||||||
11
README.md
11
README.md
|
|
@ -3,6 +3,7 @@
|
||||||
## Info
|
## Info
|
||||||
|
|
||||||
https://blog.janestreet.com/oxidizing-ocaml-locality/
|
https://blog.janestreet.com/oxidizing-ocaml-locality/
|
||||||
|
|
||||||
https://blog.janestreet.com/oxidizing-ocaml-ownership/
|
https://blog.janestreet.com/oxidizing-ocaml-ownership/
|
||||||
|
|
||||||
- **locality:** *global* (default) or *local* (value should not be passed out of context, can be allocated on stach)
|
- **locality:** *global* (default) or *local* (value should not be passed out of context, can be allocated on stach)
|
||||||
|
|
@ -23,3 +24,13 @@ https://blog.janestreet.com/oxidizing-ocaml-ownership/
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
- *unique:* let f (unique x) = x * x in f;; -> error
|
- *unique:* let f (unique x) = x * x in f;; -> error
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**bad design decisions:**
|
||||||
|
|
||||||
|
- shared_ptr instead of unique_ptr
|
||||||
|
- using namespace std
|
||||||
|
- use of indicies instead of visitor for std::variant
|
||||||
|
|
||||||
|
going to fix later (?)
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,79 @@
|
||||||
#pragma once
|
#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 <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace types {
|
#include "utils.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
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
|
|
||||||
|
|
||||||
namespace nodes {
|
namespace nodes {
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
struct Node {
|
struct NodeInfo {
|
||||||
optional<types::Type> type = std::nullopt;
|
optional<types::TypeID> type = std::nullopt;
|
||||||
|
optional<types::Mode> mode = std::nullopt;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Expr;
|
struct Expr;
|
||||||
using ExprPtr = shared_ptr<Expr>;
|
using ExprPtr = shared_ptr<Expr>;
|
||||||
using ExprPtrV = std::vector<ExprPtr>;
|
using ExprPtrV = std::vector<ExprPtr>;
|
||||||
|
|
||||||
struct Arg : public Node {
|
struct Arg : public NodeInfo {
|
||||||
|
Arg(string name) : name(std::move(name)) {}
|
||||||
|
|
||||||
string name;
|
string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Const : public Node {
|
struct Const : public NodeInfo {
|
||||||
|
Const(int value) : value(value) {}
|
||||||
|
|
||||||
int value;
|
int value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Var : public Node {
|
struct Var : public NodeInfo {
|
||||||
|
Var(string name) : name(std::move(name)) {}
|
||||||
|
|
||||||
string 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;
|
Arg name;
|
||||||
ExprPtr body;
|
ExprPtr body;
|
||||||
ExprPtr where;
|
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;
|
vector<Arg> args;
|
||||||
ExprPtr expr;
|
ExprPtr expr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Call : public Node {
|
struct Call : public NodeInfo {
|
||||||
|
Call(ExprPtr func, vector<ExprPtr> args)
|
||||||
|
: func(func), args(std::move(args)) {}
|
||||||
|
|
||||||
ExprPtr func;
|
ExprPtr func;
|
||||||
vector<ExprPtr> args;
|
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 condition;
|
||||||
ExprPtr then_case;
|
ExprPtr then_case;
|
||||||
ExprPtr else_case;
|
ExprPtr else_case;
|
||||||
|
|
@ -96,17 +83,39 @@ struct Expr {
|
||||||
variant<Const, Var, Let, Lambda, Call, Condition> value;
|
variant<Const, Var, Let, Lambda, Call, Condition> value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template <typename T, typename... Args> ExprPtr make_expr(Args &&...args) {
|
||||||
ExprPtr make_expr(Args&&... args) {
|
return std::make_shared<Expr>(T(std::forward<Args>(args)...));
|
||||||
return std::make_shared<Expr>(T{std::forward<Args>(args)...});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ExprPtr lambda1(string name, ExprPtr expr) {
|
template <typename T> inline T with_type(T node, types::Type type) {
|
||||||
return make_expr<Lambda>(vector<Arg>{{name}}, std::move(expr));
|
node.type = std::move(type);
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprPtr operator_call(string name, ExprPtr left, ExprPtr right) {
|
template <typename T> inline T with_mode(T node, types::Mode mode) {
|
||||||
return make_expr<Call>(make_expr<Var>(name), ExprPtrV{left, right});
|
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
|
// 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
|
||||||
56
src/main.cpp
56
src/main.cpp
|
|
@ -1,6 +1,58 @@
|
||||||
|
#include "mode_check.hpp"
|
||||||
#include "parsing_tree.hpp"
|
#include "parsing_tree.hpp"
|
||||||
#include "typechecker.hpp"
|
#include "type_check.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
auto make_program() {
|
||||||
|
using namespace nodes;
|
||||||
|
return make_expr<Let>(Arg("f"),
|
||||||
|
lambda1(with_unique(Arg("x")),
|
||||||
|
operator_call("+", make_var("x", types::Mode::Uniq::UNIQUE),
|
||||||
|
make_var("x", types::Mode::Uniq::UNIQUE))),
|
||||||
|
make_var("f"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_builtin_functions_types(type_check::State &state) {
|
||||||
|
auto sum_type = state.type_storage.add(types::make_operator(
|
||||||
|
state.type_storage.get_int_type(), state.type_storage.get_int_type(),
|
||||||
|
state.type_storage.get_int_type()));
|
||||||
|
state.manager.add_var("+", sum_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_builtin_functions_modes(mode_check::State &state) {
|
||||||
|
state.add_var("+");
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_error(const std::string &general_message,
|
||||||
|
const utils::Error &error) {
|
||||||
|
std::cerr << general_message << " "
|
||||||
|
<< "file: " << error.location.file_name() << "("
|
||||||
|
<< error.location.line() << ":" << error.location.column() << ") `"
|
||||||
|
<< error.location.function_name() << "`: " << error.message;
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
return 0;
|
const auto program = make_program();
|
||||||
|
|
||||||
|
try {
|
||||||
|
type_check::State state;
|
||||||
|
|
||||||
|
add_builtin_functions_types(state);
|
||||||
|
|
||||||
|
type_check::check_expr(program, state);
|
||||||
|
|
||||||
|
} catch (utils::Error error) {
|
||||||
|
print_error("TYPE CHECK ERROR:", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
mode_check::State state;
|
||||||
|
|
||||||
|
add_builtin_functions_modes(state);
|
||||||
|
|
||||||
|
mode_check::check_expr(program, state);
|
||||||
|
} catch (utils::Error error) {
|
||||||
|
print_error("MODE CHECK ERROR:", error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,140 +1,24 @@
|
||||||
#include "mode_check.hpp"
|
#include "mode_check.hpp"
|
||||||
|
|
||||||
#include "parsing_tree.hpp"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <source_location>
|
|
||||||
|
|
||||||
namespace mode_check {
|
namespace mode_check {
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
using namespace types;
|
|
||||||
|
|
||||||
struct VarState {
|
|
||||||
Type type;
|
|
||||||
size_t count = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ModeError {
|
|
||||||
enum Error {
|
|
||||||
UNKNOWN,
|
|
||||||
NO_VAR,
|
|
||||||
NO_VAR_TYPE,
|
|
||||||
NO_TYPE,
|
|
||||||
WRONG_TYPE,
|
|
||||||
LOCAL,
|
|
||||||
UNIQUE,
|
|
||||||
EXCL,
|
|
||||||
ONCE,
|
|
||||||
SEP
|
|
||||||
} error = UNKNOWN;
|
|
||||||
|
|
||||||
source_location location;
|
|
||||||
|
|
||||||
ModeError(Error error, source_location location)
|
|
||||||
: type(type), location(location) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct State {
|
|
||||||
friend struct Context;
|
|
||||||
|
|
||||||
State() { vars_stack.emplace_back(); }
|
|
||||||
|
|
||||||
void set_error(ModeError::Error error,
|
|
||||||
source_location location = source_location::current()) {
|
|
||||||
if (first_error.has_value()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
first_error = ModeError(std::move(error), location);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_error(ModeError::NO_VAR);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_var(std::string name, Type type) {
|
|
||||||
vars_stack.back().insert({std::move(name), VarState{std::move(type)}});
|
|
||||||
// TODO: check existance
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<ModeError> get_first_error() { return first_error; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void enter_context() { vars_stack.emplace_back(); }
|
|
||||||
|
|
||||||
void exit_context() { vars_stack.pop_back(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<map<string, VarState>> vars_stack;
|
|
||||||
std::optional<ModeError> first_error;
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
void check_const(const nodes::Const &, State &) {}
|
void check_const(const nodes::Const &, State &) {}
|
||||||
|
|
||||||
void check_var(const nodes::Var &expr, State &state) {
|
void check_var(const nodes::Var &expr, State &state) {
|
||||||
if (not expr.type.has_value()) {
|
if (not expr.mode.has_value()) {
|
||||||
state.set_error(ModeError::NO_TYPE);
|
utils::throw_error("NO_MODE for " + expr.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto type = expr.type.value();
|
auto mode = expr.mode.value();
|
||||||
|
|
||||||
if (auto maybe_var_state = state.get_var_state(expr.name);
|
if (auto maybe_var_state = state.get_var_state(expr.name);
|
||||||
maybe_var_state.has_value()) {
|
maybe_var_state.has_value()) {
|
||||||
auto &var_state = *maybe_var_state.value();
|
auto &var_state = *maybe_var_state.value();
|
||||||
|
|
||||||
if (var_state.type.uniq == Type::Uniq::UNIQUE) {
|
if (var_state.mode.uniq == Mode::Uniq::UNIQUE) {
|
||||||
++var_state.count;
|
++var_state.count;
|
||||||
if (var_state.count > 1 || type.uniq != Type::Uniq::UNIQUE) {
|
if (var_state.count > 1 || mode.uniq != Mode::Uniq::UNIQUE) {
|
||||||
state.set_error(ModeError::UNIQUE);
|
utils::throw_error("UNIQUE for " + expr.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -151,10 +35,10 @@ void check_let(const nodes::Let &expr, State &state) {
|
||||||
{
|
{
|
||||||
Context context(state);
|
Context context(state);
|
||||||
|
|
||||||
if (not expr.name.type.has_value()) {
|
if (not expr.name.mode.has_value()) {
|
||||||
state.set_error(ModeError::NO_VAR_TYPE);
|
utils::throw_error("NO_VAR_MODE");
|
||||||
}
|
}
|
||||||
state.add_var(expr.name.name, expr.name.type.value());
|
state.add_var(expr.name.name, expr.name.mode.value());
|
||||||
|
|
||||||
check_expr(expr.where, state);
|
check_expr(expr.where, state);
|
||||||
}
|
}
|
||||||
|
|
@ -164,11 +48,11 @@ void check_lambda(const nodes::Lambda &expr, State &state) {
|
||||||
Context context(state);
|
Context context(state);
|
||||||
|
|
||||||
for (const auto &arg : expr.args) {
|
for (const auto &arg : expr.args) {
|
||||||
if (not arg.type.has_value()) {
|
if (not arg.mode.has_value()) {
|
||||||
state.set_error(ModeError::NO_VAR_TYPE);
|
utils::throw_error("NO_VAR_MODE");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
state.add_var(arg.name, arg.type.value());
|
state.add_var(arg.name, arg.mode.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
check_expr(expr.expr, state);
|
check_expr(expr.expr, state);
|
||||||
|
|
@ -176,20 +60,20 @@ void check_lambda(const nodes::Lambda &expr, State &state) {
|
||||||
|
|
||||||
void check_call(const nodes::Call &expr, State &state) {
|
void check_call(const nodes::Call &expr, State &state) {
|
||||||
// if (not expr.type.has_value()) {
|
// if (not expr.type.has_value()) {
|
||||||
// state.set_error(ModeError::NO_TYPE);
|
// utils::throw_error("NO_TYPE");
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// auto type = expr.type.value();
|
// auto type = expr.type.value();
|
||||||
|
|
||||||
// if (not holds_alternative<types::ArrowType>(type.type)) {
|
// if (not holds_alternative<types::ArrowType>(type.type)) {
|
||||||
// state.set_error(ModeError::WRONG_TYPE);
|
// utils::throw_error("WRONG_TYPE");
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// const auto &arrow_type = get<types::ArrowType>(type.type);
|
// const auto &arrow_type = get<types::ArrowType>(type.type);
|
||||||
|
|
||||||
// if (arrow_type.types.size() != expr.args.size() + 1) {
|
// if (arrow_type.types.size() != expr.args.size() + 1) {
|
||||||
// state.set_error(ModeError::WRONG_TYPE);
|
// utils::throw_error("WRONG_TYPE");
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
@ -229,7 +113,7 @@ void check_expr(nodes::ExprPtr expr, State &state) {
|
||||||
check_condition(std::get<5>(expr->value), state);
|
check_condition(std::get<5>(expr->value), state);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
unreachable();
|
utils::unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,112 +1,107 @@
|
||||||
#include "type_check.hpp"
|
#include "type_check.hpp"
|
||||||
|
|
||||||
#include "parsing_tree.hpp"
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
namespace type_check {
|
namespace type_check {
|
||||||
|
|
||||||
using namespace types;
|
types::TypeID check_const(nodes::Const &expr, State &state) {
|
||||||
|
return (expr.type = state.type_storage.get_int_type()).value();
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bool eq(Type x, Type y) {
|
types::TypeID check_var(nodes::Var &expr, State &state) {
|
||||||
// if (x.type.index() != y.type.index()) {
|
if (auto maybe_var_type = state.manager.get_var_type(expr.name);
|
||||||
// return false;
|
maybe_var_type.has_value()) {
|
||||||
// }
|
return (expr.type = maybe_var_type).value();
|
||||||
|
}
|
||||||
// if (x.type.index() != Type::ARROW_TYPE_INDEX) {
|
utils::unreachable();
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const auto &x_types = std::get<Type::ARROW_TYPE_INDEX>(x.type);
|
|
||||||
// const auto &y_types = std::get<Type::ARROW_TYPE_INDEX>(y.type);
|
|
||||||
|
|
||||||
// if (x_types.size() != )
|
|
||||||
// }
|
|
||||||
|
|
||||||
// -----------------
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using Typechecked = std::pair<T /*expr*/, types::Type /*type*/>;
|
|
||||||
|
|
||||||
Typechecked<nodes::ExprPtr> typecheck_expr(nodes::ExprPtr expr);
|
|
||||||
|
|
||||||
Typechecked<nodes::Const> typecheck_const(nodes::Const expr) {
|
|
||||||
// TODO
|
|
||||||
return {std::move(expr), make_type<AnyType>()};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Typechecked<nodes::Var> typecheck_var(nodes::Var expr) {
|
types::TypeID check_let(nodes::Let &expr, State &state) {
|
||||||
// TODO
|
Context context(state.manager);
|
||||||
return {std::move(expr), make_type<AnyType>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
Typechecked<nodes::Let> typecheck_let(nodes::Let expr) {
|
types::TypeID new_type = state.type_storage.introduce_new_generic();
|
||||||
// TODO
|
|
||||||
return {std::move(expr), make_type<AnyType>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
Typechecked<nodes::Lambda> typecheck_lambda(nodes::Lambda expr) {
|
state.manager.add_var(expr.name.name, new_type);
|
||||||
// TODO
|
|
||||||
return {std::move(expr), make_type<AnyType>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
Typechecked<nodes::Call> typecheck_call(nodes::Call expr) {
|
types::TypeID body_type = check_expr(expr.body, state);
|
||||||
// TODO
|
|
||||||
return {std::move(expr), make_type<AnyType>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
Typechecked<nodes::Condition> typecheck_condition(nodes::Condition expr) {
|
if (not state.type_storage.unify(new_type, body_type)) {
|
||||||
// const auto [condition_expr, condition_type] = typecheck_expr(expr.condition);
|
utils::throw_error("DIFFERENT_TYPES");
|
||||||
// expr.condition = std::move(condition_expr);
|
|
||||||
|
|
||||||
// const auto [then_case_expr, then_case_type] = typecheck_expr(expr.then_case);
|
|
||||||
// expr.then_case = std::move(then_case_expr);
|
|
||||||
|
|
||||||
// const auto [else_case_expr, else_type_type] = typecheck_expr(expr.else_case);
|
|
||||||
// expr.else_case = std::move(else_case_expr);
|
|
||||||
|
|
||||||
return {std::move(expr), make_type<AnyType>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
Typechecked<nodes::ExprPtr> typecheck_expr(nodes::ExprPtr expr) {
|
|
||||||
types::Type type;
|
|
||||||
|
|
||||||
switch (expr->value.index()) {
|
|
||||||
case 0: // Const
|
|
||||||
std::tie(expr->value, type) = typecheck_const(std::get<0>(expr->value));
|
|
||||||
break;
|
|
||||||
case 1: // Var
|
|
||||||
std::tie(expr->value, type) = typecheck_var(std::get<1>(expr->value));
|
|
||||||
break;
|
|
||||||
case 2: // Let
|
|
||||||
std::tie(expr->value, type) = typecheck_let(std::get<2>(expr->value));
|
|
||||||
break;
|
|
||||||
case 3: // Lambda
|
|
||||||
std::tie(expr->value, type) = typecheck_lambda(std::get<3>(expr->value));
|
|
||||||
break;
|
|
||||||
case 4: // Call
|
|
||||||
std::tie(expr->value, type) = typecheck_call(std::get<4>(expr->value));
|
|
||||||
break;
|
|
||||||
case 5: // Condition
|
|
||||||
std::tie(expr->value, type) = typecheck_condition(std::get<5>(expr->value));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
unreachable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {std::move(expr), std::move(type)};
|
types::TypeID where_type = check_expr(expr.where, state);
|
||||||
|
return (expr.type = where_type).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
types::TypeID check_lambda(nodes::Lambda &expr, State &state) {
|
||||||
|
Context context(state.manager);
|
||||||
|
|
||||||
|
for (const auto &arg : expr.args) {
|
||||||
|
types::TypeID new_type = state.type_storage.introduce_new_generic();
|
||||||
|
state.manager.add_var(arg.name, new_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
types::TypeID lambda_type = check_expr(expr.expr, state);
|
||||||
|
return (expr.type = lambda_type).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
types::TypeID check_call(nodes::Call &expr, State &state) {
|
||||||
|
types::TypeID func_type = check_expr(expr.func, state);
|
||||||
|
|
||||||
|
if (auto *arrow_func_type = get_if<types::ArrowType>(&func_type.get().type);
|
||||||
|
arrow_func_type != nullptr) {
|
||||||
|
|
||||||
|
if (arrow_func_type->types.size() != expr.args.size() + 1) {
|
||||||
|
utils::throw_error("ARG_COUNT_MISMATCH");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < expr.args.size(); ++i) {
|
||||||
|
types::TypeID arg_type = check_expr(expr.args[i], state);
|
||||||
|
if (not state.type_storage.unify(arrow_func_type->types[i], arg_type)) {
|
||||||
|
utils::throw_error("DIFFERENT_TYPES");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (expr.type = arrow_func_type->types.back()).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::throw_error("FUNC_IS_NOT_ARROW_TYPE");
|
||||||
|
utils::unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
types::TypeID check_condition(nodes::Condition &expr, State &state) {
|
||||||
|
types::TypeID condition_type = check_expr(expr.condition, state);
|
||||||
|
|
||||||
|
if (not state.type_storage.unify(condition_type,
|
||||||
|
state.type_storage.get_bool_type())) {
|
||||||
|
utils::throw_error("DIFFERENT_TYPES");
|
||||||
|
}
|
||||||
|
|
||||||
|
types::TypeID then_type = check_expr(expr.then_case, state);
|
||||||
|
types::TypeID else_type = check_expr(expr.else_case, state);
|
||||||
|
|
||||||
|
if (not state.type_storage.unify(then_type, else_type)) {
|
||||||
|
utils::throw_error("DIFFERENT_TYPES");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (expr.type = then_type).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
types::TypeID check_expr(nodes::ExprPtr expr, State &state) {
|
||||||
|
switch (expr->value.index()) {
|
||||||
|
case 0: // Const
|
||||||
|
return check_const(std::get<0>(expr->value), state);
|
||||||
|
case 1: // Var
|
||||||
|
return check_var(std::get<1>(expr->value), state);
|
||||||
|
case 2: // Let
|
||||||
|
return check_let(std::get<2>(expr->value), state);
|
||||||
|
case 3: // Lambda
|
||||||
|
return check_lambda(std::get<3>(expr->value), state);
|
||||||
|
case 4: // Call
|
||||||
|
return check_call(std::get<4>(expr->value), state);
|
||||||
|
case 5: // Condition
|
||||||
|
return check_condition(std::get<5>(expr->value), state);
|
||||||
|
default:
|
||||||
|
utils::unreachable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace type_check
|
} // namespace type_check
|
||||||
|
|
|
||||||
13
src/types.cpp
Normal file
13
src/types.cpp
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
namespace types {
|
||||||
|
|
||||||
|
const Type& TypeID::get() const {
|
||||||
|
return storage->get_type(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type& TypeID::get() {
|
||||||
|
return storage->get_type(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace types
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
#include "parsing_tree.hpp"
|
#include "parsing_tree.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
using namespace nodes;
|
using namespace nodes;
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
const auto program =
|
const auto program =
|
||||||
Expr{Let{Arg{"f", {}},
|
Expr(Let(Arg("f"),
|
||||||
lambda1("x", operator_call("+", make_expr<Var>("x"),
|
lambda1("x", operator_call("+", make_expr<Var>("x"),
|
||||||
make_expr<Var>("x"))),
|
make_expr<Var>("x"))),
|
||||||
make_expr<Var>("f")}};
|
make_expr<Var>("f")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue