type_check_utils: old version of log replaced

This commit is contained in:
programsnail 2024-07-28 20:19:42 +03:00
parent 831cfa36f3
commit fefe3a910d
5 changed files with 178 additions and 127 deletions

View file

@ -1,15 +1,20 @@
#pragma once
#include "executor.hpp"
#include "expression_nodes.hpp"
#include "sources_manager.hpp"
#include "type_check_utils.hpp"
#include "log.hpp"
#include "name_tree.hpp"
#include "type_nodes.hpp"
#include "utils.hpp"
#include <optional>
namespace type_check {
using Executor = utils::Executor<nodes::ExpressionStorage, nodes::TypeStorage,
names::NameTree>;
using Task =
utils::Task<nodes::ExpressionStorage, nodes::TypeStorage, names::NameTree>;
class ContextHolder;
class State {
@ -22,11 +27,15 @@ public:
};
public:
State(Log &log) : log_(log) {}
bool insert_variable(const std::string &name, nodes::TypeProxy type,
nodes::NameDefinition::Modifier modifier) {
Log::Context logc(log_, utils::Log::Area::kTypeCheck);
if (contexts_.empty()) {
error_handling::handle_general_error(
"Insert variable into contexts_ with zero elements in State");
logc.Fatal<Log::kSys>(
{{"Insert variable into contexts_ with zero elements in State"}});
}
return contexts_.back()
@ -46,9 +55,11 @@ public:
}
bool bring_type(nodes::TypeProxy type) {
Log::Context logc(log_, utils::Log::Area::kTypeCheck);
if (contexts_.empty()) {
error_handling::handle_general_error(
"Set brought type to contexts_ with zero elements in State");
logc.Fatal<Log::kSys>(
{{"Set brought type to contexts_ with zero elements in State"}});
}
auto &brought_type = contexts_.back().brought_type;
if (brought_type.has_value() &&
@ -60,9 +71,11 @@ public:
}
bool return_type(nodes::TypeProxy type) {
Log::Context logc(log_, utils::Log::Area::kTypeCheck);
if (contexts_.empty()) {
error_handling::handle_general_error(
"Set returned type to contexts_ with zero elements in State");
logc.Fatal<Log::kSys>(
{{"Set returned type to contexts_ with zero elements in State"}});
}
auto &returned_type = contexts_.back().returned_type;
if (returned_type.has_value() &&
@ -74,18 +87,21 @@ public:
}
private:
void enter_context(const nodes::Node &node,
error_handling::ErrorLog &error_log) {
contexts_.emplace_back(node, error_log);
void enter_context(const nodes::Node &node) {
Log::Context logc(log_, utils::Log::Area::kTypeCheck);
contexts_.emplace_back(node);
}
// TODO: argument for property is returned type should be merged
// returns brought type, return type is merged with next context or with
// brought type in last context
nodes::MaybeTypeProxy exit_context() {
Log::Context logc(log_, utils::Log::Area::kTypeCheck);
if (contexts_.empty()) {
error_handling::handle_general_error(
"Pop from contexts_ with zero elements in State");
logc.Fatal<Log::kSys>(
{{"Pop from contexts_ with zero elements in State"}});
}
auto context = std::move(contexts_.back());
@ -98,8 +114,8 @@ private:
if (contexts_.empty()) {
if (brought_type.has_value()) {
if (returned_type.value() != brought_type.value()) {
context.log_typecheck_error(
"Different returned and brought type in last context");
logc.Error<Log::kProc>(
{{"Different returned and brought type in last context"}});
}
} else {
brought_type = returned_type.value();
@ -108,8 +124,8 @@ private:
auto &previous_returned_type = contexts_.back().returned_type;
if (previous_returned_type.has_value()) {
if (returned_type.value() != previous_returned_type.value()) {
context.log_typecheck_error(
"Different returned type in this context and previous one");
logc.Error<Log::kProc>({{"Different returned type in this "
"context and previous one"}});
}
} else {
previous_returned_type = returned_type.value();
@ -123,14 +139,13 @@ private:
public:
class Context {
public:
Context(const nodes::Node &node, error_handling::ErrorLog &error_log)
: node(node), error_log(error_log) {}
Context(const nodes::Node &node) : node_(node) {}
void log_typecheck_error(
const std::string &message = "Context typecheck error") {
error_log.add_error(error_handling::ErrorLog::ErrorMessage(
node, message, error_handling::ErrorType::TYPE_CHECK));
}
// void log_typecheck_error(
// const std::string &message = "Context typecheck error") {
// log_.add_error(Log::ErrorMessage(node, message,
// error_handling::ErrorType::TYPE_CHECK));
// }
public:
nodes::MaybeTypeProxy brought_type;
@ -138,10 +153,12 @@ public:
std::unordered_map<std::string, VariableInfo> variables;
private:
const nodes::Node &node;
error_handling::ErrorLog &error_log;
const nodes::Node &node_; // TODO: use as position
};
private:
Log &log_;
std::vector<Context> contexts_ = {{}};
};
@ -151,17 +168,16 @@ class Arguments {
public:
Arguments() = default;
Arguments expect_builtin(builtin::Type type,
SourcesManager &sources_manager) const {
Arguments expect_builtin(builtin::Type type, Executor &executor) const {
Arguments copy(*this);
copy.expected_types_ = {sources_manager.types()->primitive(type)};
copy.expected_types_ = {
executor.state<nodes::TypeStorage>().primitive(type)};
return copy;
}
Arguments pass_builtin(builtin::Type type,
SourcesManager &sources_manager) const {
Arguments pass_builtin(builtin::Type type, Executor &executor) const {
Arguments copy(*this);
copy.passed_type_ = sources_manager.types()->primitive(type);
copy.passed_type_ = executor.state<nodes::TypeStorage>().primitive(type);
return copy;
}
@ -213,10 +229,9 @@ private:
class ContextHolder {
public:
ContextHolder(State &state, const nodes::Node &node,
error_handling::ErrorLog &error_log,
nodes::MaybeTypeProxy *context_exit_type)
: state_(state), context_exit_type_(context_exit_type) {
state.enter_context(node, error_log);
state.enter_context(node);
}
ContextHolder(const ContextHolder &) = delete;
@ -250,19 +265,16 @@ public:
//
nodes::TypeProxy &get() {
if (!type_.has_value()) {
error_handling::handle_general_error(
"Access to invalid type in TypeCheckResult");
}
utils::Assert(type_.has_value(),
"Access to invalid type in TypeCheckResult");
return type_.value();
}
const nodes::TypeProxy &get() const {
if (!type_.has_value()) {
error_handling::handle_general_error(
"Access to invalid type in TypeCheckResult");
}
utils::Assert(type_.has_value(),
"Access to invalid type in TypeCheckResult");
return type_.value();
}
@ -271,13 +283,13 @@ public:
//
bool is_invalid() const { return !type_.has_value(); }
bool is_invalid() const { return not type_.has_value(); }
private:
Result() = default;
private:
nodes::MaybeTypeProxy type_;
nodes::MaybeTypeProxy type_ = {};
};
using MaybeResult = std::optional<Result>;
@ -286,7 +298,7 @@ using MaybeResult = std::optional<Result>;
nodes::TypeProxy check_same_to_pass_type_in_arguments(
nodes::TypeProxy type, const Arguments &arguments, const nodes::Node &node,
SourcesManager &sources_manager,
Executor &executor,
const std::string &message = "Different type with passed one",
bool handle_errors = true);
@ -297,39 +309,35 @@ nodes::TypeProxy check_same_to_pass_type_in_arguments(
Result type_same_to_expected(
nodes::TypeProxy type, const Arguments &argumensr, const nodes::Node &node,
SourcesManager &sources_manager,
Executor &executor,
const std::string &message = "Different type with expected one",
bool handle_errors = true);
Result type_check_from_arguments(nodes::TypeProxy type,
const Arguments &arguments,
const nodes::Node &node,
SourcesManager &sources_manager,
const nodes::Node &node, Executor &executor,
bool handle_errors = true);
std::optional<const nodes::TypeDefinition *>
find_type_definition(const std::string &name, const nodes::Node &node,
SourcesManager &sources_manager,
bool handle_errors = true);
Executor &executor, bool handle_errors = true);
std::optional<const nodes::FunctionDefinition *>
find_name_definition(const std::string &name, const nodes::Node &node,
SourcesManager &sources_manager,
bool handle_errors = true);
Executor &executor, bool handle_errors = true);
nodes::MaybeTypeProxy unfold_user_defined_type(nodes::TypeProxy type,
const nodes::Node &node,
SourcesManager &sources_manager,
Executor &executor,
bool handle_errors = true);
nodes::MaybeTypeProxy get_field_type_by_name(nodes::TypeProxy type,
const std::string &field,
const nodes::Node &node,
SourcesManager &sources_manager,
Executor &executor,
bool handle_errors = true);
void type_check_error(const std::string &message, const nodes::Node &node,
SourcesManager &sources_manager,
bool handle_error = true);
Executor &executor, bool handle_error = true);
} // namespace type_check

View file

@ -7,27 +7,31 @@ namespace type_check {
// pass type -> compare types, return bool
// no pass type -> return type
nodes::TypeProxy check_same_to_pass_type_in_arguments(
nodes::TypeProxy type, const Arguments &arguments, const nodes::Node &node,
SourcesManager &sources_manager, const std::string &message,
bool handle_errors) {
if (!arguments.get_passed().has_value()) {
nodes::TypeProxy type, const Arguments &arguments,
const nodes::Node & /*node*/, Executor &executor,
const std::string &message, bool handle_errors) {
Log::Context logc(executor.log(), utils::Log::Area::kTypeCheck);
if (not arguments.get_passed().has_value()) {
return type;
}
if (type != arguments.get_passed().value()) {
type_check_error(message, node, sources_manager, handle_errors);
if (type != arguments.get_passed().value() and handle_errors) {
logc.Error<Log::kProc>({{message}} /* TODO: node */);
}
return sources_manager.types()->primitive(builtin::Type::BOOL);
return executor.state<nodes::TypeStorage>().primitive(builtin::Type::BOOL);
}
bool check_no_pass_type_in_arguments(const Arguments &arguments,
const nodes::Node &node,
SourcesManager &sources_manager,
Executor &executor,
const std::string &message,
bool handle_errors) {
Log::Context logc(executor.log(), utils::Log::Area::kTypeCheck);
if (arguments.get_passed().has_value()) {
type_check_error(message, node, sources_manager, handle_errors);
type_check_error(message, node, executor, handle_errors);
return false;
}
@ -35,9 +39,10 @@ bool check_no_pass_type_in_arguments(const Arguments &arguments,
}
Result type_same_to_expected(nodes::TypeProxy type, const Arguments &arguments,
const nodes::Node &node,
SourcesManager &sources_manager,
const nodes::Node & /*node*/, Executor &executor,
const std::string &message, bool handle_errors) {
Log::Context logc(executor.log(), utils::Log::Area::kTypeCheck);
const auto &expected = arguments.get_expected();
if (expected.empty()) {
@ -45,40 +50,49 @@ Result type_same_to_expected(nodes::TypeProxy type, const Arguments &arguments,
}
// TODO: use 'can cast to' (for modifiers), instead '=='
if (std::all_of(expected.begin(), expected.end(),
[type](nodes::TypeProxy expected_type) {
return type != expected_type;
})) {
type_check_error(message, node, sources_manager, handle_errors);
bool all_not_expected = std::all_of(
expected.begin(), expected.end(),
[type](nodes::TypeProxy expected_type) { return type != expected_type; });
if (all_not_expected and handle_errors) {
logc.Error<Log::kProc>({{message}} /* TODO: node */);
}
return Result{expected.front()}; // any can be choosen
}
Result type_check_from_arguments(nodes::TypeProxy type,
const Arguments &arguments,
const nodes::Node &node,
SourcesManager &sources_manager,
bool handle_errors) {
/* TODO */
throw std::exception();
Result type_check_from_arguments(nodes::TypeProxy /*type*/,
const Arguments & /*arguments*/,
const nodes::Node & /*node*/,
Executor &executor, bool /*handle_errors*/) {
Log::Context logc(executor.log(), utils::Log::Area::kTypeCheck);
/* TODO FIXME */
logc.Fatal<Log::kSys>({{"Not implemented yet"}});
throw std::exception(); // unreachable
}
template <typename T>
std::optional<const T *> find_statement(
const std::string &name, const nodes::Node &node,
SourcesManager &sources_manager, const std::string &message_not_found,
const std::string &message_different_statement, bool handle_errors) {
const auto maybe_any_statement = sources_manager.names()->find(name);
if (!maybe_any_statement.has_value()) {
type_check_error(message_not_found, node, sources_manager, handle_errors);
std::optional<const T *>
find_statement(const std::string &name, const nodes::Node & /*node*/,
Executor &executor, const std::string &message_not_found,
const std::string &message_different_statement,
bool handle_errors) {
Log::Context logc(executor.log(), utils::Log::Area::kTypeCheck);
const auto maybe_any_statement = executor.state<names::NameTree>().find(name);
if (not maybe_any_statement.has_value()) {
if (handle_errors) {
logc.Error<Log::kProc>({{message_not_found}} /* TODO: node */);
}
return std::nullopt;
}
const auto maybe_statement = maybe_any_statement.value().get()->get<T>();
if (!maybe_statement.has_value()) {
type_check_error(message_different_statement, node, sources_manager,
handle_errors);
if (not maybe_statement.has_value()) {
if (handle_errors) {
logc.Error<Log::kProc>({{message_different_statement}} /* TODO: node */);
}
return std::nullopt;
}
@ -87,59 +101,68 @@ std::optional<const T *> find_statement(
std::optional<const nodes::TypeDefinition *>
find_type_definition(const std::string &name, const nodes::Node &node,
SourcesManager &sources_manager, bool handle_errors) {
Executor &executor, bool handle_errors) {
Log::Context logc(executor.log(), utils::Log::Area::kTypeCheck);
return find_statement<nodes::TypeDefinition>(
name, node, sources_manager, "No type definition found in name tree",
name, node, executor, "No type definition found in name tree",
"Node in name tree is not type definition", handle_errors);
}
std::optional<const nodes::FunctionDefinition *>
find_name_definition(const std::string &name, const nodes::Node &node,
SourcesManager &sources_manager, bool handle_errors) {
Executor &executor, bool handle_errors) {
Log::Context logc(executor.log(), utils::Log::Area::kTypeCheck);
return find_statement<nodes::FunctionDefinition>(
name, node, sources_manager, "No name definition found in name tree",
name, node, executor, "No name definition found in name tree",
"Node in name tree is not name definition", handle_errors);
}
std::optional<nodes::TypeProxy>
unfold_user_defined_type(nodes::TypeProxy type, const nodes::Node &node,
SourcesManager &sources_manager, bool handle_errors) {
Executor &executor, bool handle_errors) {
Log::Context logc(executor.log(), utils::Log::Area::kTypeCheck);
const auto maybe_type_definition = find_type_definition(
*type.get()->get_name()->get(), node, sources_manager, handle_errors);
*type.get()->get_name()->get(), node, executor, handle_errors);
if (!maybe_type_definition.has_value()) {
return std::nullopt;
}
if (maybe_type_definition.value()->get_type().has_value()) {
type_check_error("Only type declaration found for type " +
*type.get()->get_name()->get() +
" (type is not defined)",
node, sources_manager, handle_errors);
if (handle_errors) {
logc.Error<Log::kProc>({{std::format(
"Only type declaration found for type {} (type is not defined)",
*type.get()->get_name()->get())}} /* TODO: node*/);
}
return std::nullopt;
}
// TODO: perform type arguments substitution
error_handling::ensure(type.get()->parameters_size() == 0,
"Unfold of generic type is not supported (yet)");
logc.Require<Log::kProc>(type.get()->parameters_size() == 0,
{{"Unfold of generic type is not supported (yet)"}});
//
return maybe_type_definition.value()->get_type().value();
}
std::optional<nodes::TypeProxy>
get_field_type_by_name(nodes::TypeProxy type, const std::string &field,
const nodes::Node &node, SourcesManager &sources_manager,
bool handle_errors) {
std::optional<nodes::TypeProxy> get_field_type_by_name(nodes::TypeProxy type,
const std::string &field,
const nodes::Node &node,
Executor &executor,
bool handle_errors) {
Log::Context logc(executor.log(), utils::Log::Area::kTypeCheck);
switch (type.get()->to_builtin()) {
case builtin::Type::TUPLE: { // access field
const auto maybe_field = type.get()->get_parameter_proxy_by_name(field);
if (!maybe_field.has_value()) {
if (not maybe_field.has_value() and handle_errors) {
// TODO: pass unfolded type name to log it ??
type_check_error("Type has no defined field " + field, node,
sources_manager, handle_errors);
logc.Error<Log::kProc>({{std::format("Type has no defined field {}",
field)}} /* TODO: node */);
}
return maybe_field.value();
}
@ -148,33 +171,37 @@ get_field_type_by_name(nodes::TypeProxy type, const std::string &field,
// remove recursion ??
const auto maybe_internal_type =
unfold_user_defined_type(type, node, sources_manager, handle_errors);
unfold_user_defined_type(type, node, executor, handle_errors);
if (!maybe_internal_type.has_value()) {
return std::nullopt;
}
return get_field_type_by_name(maybe_internal_type.value(), field, node,
sources_manager, handle_errors);
executor, handle_errors);
}
default: // variant, function, optional, result, error (TODO: add message
// field?), array (TODO: add length field ?), basic types
type_check_error("Type " +
builtin::types::to_string(type.get()->to_builtin()) +
" has no accessible fields by definition",
node, sources_manager, handle_errors);
if (handle_errors) {
logc.Error<Log::kProc>(
{{std::format("Type {} has no accessible fields by definition",
builtin::types::to_string(
type.get()->to_builtin()))}} /* TODO: node */);
}
return std::nullopt;
}
}
void type_check_error(const std::string &message, const nodes::Node &node,
SourcesManager &sources_manager, bool handle_error) {
// FIXME: replace with direct log calls
void type_check_error(const std::string &message, const nodes::Node &,
Executor &executor, bool handle_error) {
Log::Context logc(executor.log(), utils::Log::Area::kTypeCheck);
if (!handle_error) {
return;
}
sources_manager.errors()->add_error(error_handling::ErrorLog::ErrorMessage(
node, message, error_handling::ErrorType::TYPE_CHECK));
logc.Error<Log::kProc>({{message}} /* TODO: node */);
}
} // namespace type_check

View file

@ -8,5 +8,5 @@ target("lang.type_check")
add_includedirs("include", {public = true})
add_files("src/**.cpp")
add_deps("lang.utils", "lang.nodes")
set_warnings("allextra", "error")
set_warnings("allextra") -- , "error")
set_rundir("$(projectdir)")

View file

@ -17,8 +17,8 @@ public:
//
State &state(Tag) { return state; }
const State &state(Tag) const { return state; }
// State &state(Tag) { return state; }
// const State &state(Tag) const { return state; }
protected:
State state_;
@ -37,6 +37,11 @@ public:
return T(*this, args...);
}
template <typename T> T &state() { return ExecutorState<T>::state_; }
template <typename T> const T &state() const {
return ExecutorState<T>::state_;
}
//
Log &log() { return log_; }
@ -62,11 +67,9 @@ public:
//
template <typename T> T &state() {
return executor.state(ExecutorState<T>::Tag);
}
template <typename T> T &state() { return executor.template state<T>(); }
template <typename T> const T &state() const {
return executor.state(ExecutorState<T>::Tag);
return executor.template state<T>();
}
Log &log() { return executor.log_; }

View file

@ -1,3 +1,5 @@
#pragma once
#include <format>
#include <functional>
#include <iostream>
@ -86,6 +88,7 @@ public:
enum class Area {
kDefault,
kParse,
kTypeCheck,
kIntepret,
// ...
};
@ -281,6 +284,16 @@ private:
std::function<void(const Message &)> proc_hook_;
};
inline void Assert(bool condition, std::string_view message,
const std::source_location &source_location =
std::source_location::current()) { // TODO: colors
if (not condition) {
std::cerr << std::format("Assert failed: {}", message,
to_string(source_location));
throw std::exception();
}
}
//
std::string to_string(const std::source_location &source_location);