Typecheck: build working

This commit is contained in:
ProgramSnail 2024-09-08 23:40:51 +03:00
parent 5891066414
commit 7be3722824
7 changed files with 287 additions and 216 deletions

View file

@ -82,12 +82,12 @@ public:
// //
template <typename T> T &part() { return NodePart<T>::data_[id_]; } // template <typename T> T &part() { return NodePart<T>::data_[id_]; }
template <typename T> const T &part() const { template <typename T> const T &part() const {
return NodePart<T>::data_[id_]; return NodePart<T>::data_[id_];
} }
template <typename T> T &get() { return std::get<T>(data_[id_].value); } // template <typename T> T &get() { return std::get<T>(data_[id_].value); }
template <typename T> const T &get() const { template <typename T> const T &get() const {
return std::get<T>(data_[id_].value); return std::get<T>(data_[id_].value);
} }

View file

@ -8,10 +8,17 @@
namespace type_check { namespace type_check {
template <> template <>
struct CheckTask<nodes::ExprData> : public CheckTaskBase<nodes::ExprData> { struct CheckTask<nodes::NodeId> : public CheckTaskBase<nodes::NodeId> {
using CheckTaskBase<nodes::ExprData>::CheckTaskBase; using CheckTaskBase<nodes::NodeId>::CheckTaskBase;
Result operator()(const nodes::ExprData &expr, const Args &args) override; Result operator()(const nodes::NodeId &expr, const Args &args) override;
};
template <>
struct CheckTask<nodes::NodeData> : public CheckTaskBase<nodes::NodeData> {
using CheckTaskBase<nodes::NodeData>::CheckTaskBase;
Result operator()(const nodes::NodeData &expr, const Args &args) override;
}; };
// --- flow control // --- flow control

View file

@ -163,14 +163,17 @@ private:
using Exprs = nodes::NodeStorage; using Exprs = nodes::NodeStorage;
using Types = nodes::TypeStorage; using Types = nodes::TypeStorage;
using Names = names::NameTree; using Names = names::NameTree;
using Positions = core::DependentStorage<utils::Pos>;
using Executor = utils::Executor<Exprs, Types, Names, State>; using Executor = utils::Executor<Exprs, Types, Names, State, Positions>;
using Node = nodes::Node_<nodes::NodePart<utils::Pos>>;
// //
class Args { class Args {
public: public:
Args() = default; Args(storage::Id current_id) : current_id(current_id) {};
Args expect_builtin(builtin::Type type, Executor &executor) const { Args expect_builtin(builtin::Type type, Executor &executor) const {
Args copy(*this); Args copy(*this);
@ -222,6 +225,9 @@ public:
// TODO: add check, that there is no passed type for some nodes ?? // TODO: add check, that there is no passed type for some nodes ??
// TODO: args builder ?? // TODO: args builder ??
public:
storage::Id current_id;
private: private:
nodes::Types expected_types_; nodes::Types expected_types_;
nodes::MaybeType passed_type_; nodes::MaybeType passed_type_;
@ -357,6 +363,15 @@ template <typename N> struct CheckTaskBase : public Task<N> {
CheckTask<OtherN> task(this->executor); CheckTask<OtherN> task(this->executor);
return task(node, args); return task(node, args);
} }
Node Ext(storage::Id id) {
return Node(id, this->template state<Exprs>(),
this->template state<Positions>());
}
utils::Pos PosOf(storage::Id id) {
return Ext(id).template part<utils::Pos>();
}
}; };
} // namespace type_check } // namespace type_check

View file

@ -40,7 +40,8 @@ nodes::Type get_literal_type(const nodes::Literal &literal,
Result CheckTask<nodes::Literal>::operator()(const nodes::Literal &literal, Result CheckTask<nodes::Literal>::operator()(const nodes::Literal &literal,
const Args &args) { const Args &args) {
auto const type = get_literal_type(literal, this->executor); auto const type = get_literal_type(literal, this->executor);
return type_same_to_expected(type, args, literal, this->executor); return type_same_to_expected(type, args, {},
this->executor); // TODO: add pos ??
} }
} // namespace type_check } // namespace type_check

View file

@ -11,22 +11,37 @@
namespace type_check { namespace type_check {
Result CheckTask<nodes::ExprData>::operator()(const nodes::ExprData &expr, Result CheckTask<nodes::NodeId>::operator()(const nodes::NodeId &expr,
const Args &args) {
return Run(*Ext(expr), args);
}
Result CheckTask<nodes::NodeData>::operator()(const nodes::NodeData &expr,
const Args &args) { const Args &args) {
return std::visit( return std::visit(
[this, &args](const auto &node) -> Result { return Run(node, args); }, [this, &args](const auto &node) -> Result {
expr.get_any()); using T = std::decay_t<decltype(node)>;
if constexpr (std::is_same_v<T, nodes::Match::Case>) {
utils::Assert(false, "No type check for nodes::Match::Case");
utils::unreachable();
} else {
return Run(node, args);
}
},
expr.value);
} }
// --- flow control // --- flow control
Result CheckTask<nodes::Match>::operator()(const nodes::Match &expr, Result CheckTask<nodes::Match>::operator()(const nodes::Match &expr,
const Args &args) { const Args &args) {
Result value_result = Run(*expr.get_value(), {}); const auto current_pos = PosOf(args.current_id);
Result value_result = Run(expr.value, {expr.value});
// x :=/=: ... // x :=/=: ...
if (value_result.is_invalid()) { if (value_result.is_invalid()) {
type_check_error("Match value is invalid", expr, executor); type_check_error("Match value is invalid", current_pos, executor);
} }
MaybeResult expression_result; MaybeResult expression_result;
@ -34,31 +49,33 @@ Result CheckTask<nodes::Match>::operator()(const nodes::Match &expr,
bool at_least_one_case_with_expression = false; bool at_least_one_case_with_expression = false;
bool at_least_one_case_without_expression = false; bool at_least_one_case_without_expression = false;
for (size_t i = 0; i < expr.cases_size(); ++i) { for (const auto &current_case_id : expr.cases) {
const nodes::Match::Case *current_case = expr.get_case(i); const nodes::Match::Case &current_case =
Ext(current_case_id).get<nodes::Match::Case>();
// :=/=: x ... // :=/=: x ...
Run(*current_case->get_value(), Run(current_case.value,
Args{} Args{current_case.value}
.expect_builtin(builtin::Type::BOOL, executor) .expect_builtin(builtin::Type::BOOL, executor)
.pass(value_result.is_invalid() ? nodes::MaybeType{} .pass(value_result.is_invalid() ? nodes::MaybeType{}
: expression_result.value().get())); : expression_result.value().get()));
// TODO: use type modifiers ?? // TODO: use type modifiers ??
// ... ?? x ... // ... ?? x ...
if (current_case->get_condition().has_value()) { if (current_case.condition.has_value()) {
Run(*current_case->get_condition().value(), Run(current_case.condition.value(),
Args{}.expect_builtin(builtin::Type::BOOL, executor)); Args{current_case.condition.value()}.expect_builtin(
builtin::Type::BOOL, executor));
} }
// ... -> x // ... -> x
if (current_case->get_expression().has_value()) { if (current_case.expr.has_value()) {
at_least_one_case_with_expression = true; at_least_one_case_with_expression = true;
Result case_result = Result case_result = Run(current_case.expr.value(),
Run(*current_case->get_condition().value(), Args{current_case.expr.value()}.expect(
Args{}.expect(expression_result.has_value() expression_result.has_value()
? expression_result.value().get() ? expression_result.value().get()
: nodes::MaybeType{})); : nodes::MaybeType{}));
if (!expression_result.has_value() && !case_result.is_invalid()) { if (!expression_result.has_value() && !case_result.is_invalid()) {
expression_result = std::move(case_result); expression_result = std::move(case_result);
@ -71,8 +88,8 @@ Result CheckTask<nodes::Match>::operator()(const nodes::Match &expr,
if (at_least_one_case_with_expression && if (at_least_one_case_with_expression &&
at_least_one_case_without_expression) { at_least_one_case_without_expression) {
type_check_error( type_check_error(
"All cases should be with or without expression at the same time", expr, "All cases should be with or without expression at the same time",
executor); current_pos, executor);
expression_result = Result::invalid(); expression_result = Result::invalid();
} }
@ -81,62 +98,67 @@ Result CheckTask<nodes::Match>::operator()(const nodes::Match &expr,
} }
return type_check_from_args( return type_check_from_args(
state<Types>().add_array_of(expression_result.value().get()), args, expr, state<Types>().add_array_of(expression_result.value().get()), args,
executor); current_pos, executor);
} }
Result CheckTask<nodes::Condition>::operator()(const nodes::Condition &expr, Result CheckTask<nodes::Condition>::operator()(const nodes::Condition &expr,
const Args &args) { const Args &args) {
const auto current_pos = PosOf(args.current_id);
MaybeResult expression_result; MaybeResult expression_result;
for (size_t i = 0; i < expr.cases_size(); ++i) { for (const auto &current_case : expr.cases) {
Run(*expr.get_case(i).first, Run(current_case.first,
Args{}.expect_builtin(builtin::Type::BOOL, executor)); Args{current_case.first}.expect_builtin(builtin::Type::BOOL, executor));
Result case_result = Run(*expr.get_case(i).first, Result case_result =
Args{}.expect(expression_result.has_value() Run(current_case.second,
? expression_result.value().get() Args{current_case.second}.expect(
: nodes::MaybeType{})); expression_result.has_value() ? expression_result.value().get()
: nodes::MaybeType{}));
if (!expression_result.has_value() && !case_result.is_invalid()) { if (!expression_result.has_value() && !case_result.is_invalid()) {
expression_result = std::move(case_result); expression_result = std::move(case_result);
} }
} }
if (expr.get_else_case().has_value()) { if (expr.else_case.has_value()) {
Run(*expr.get_else_case().value(), Run(expr.else_case.value(),
Args{}.expect(expression_result.has_value() Args{expr.else_case.value()}.expect(
? expression_result.value().get() expression_result.has_value() ? expression_result.value().get()
: nodes::MaybeType{})); : nodes::MaybeType{}));
} }
if (!expression_result.has_value()) { if (!expression_result.has_value()) {
type_check_error("There should be at least one case in if statement", expr, type_check_error("There should be at least one case in if statement",
executor); current_pos, executor);
expression_result = Result::invalid(); expression_result = Result::invalid();
} }
return type_check_from_args( return type_check_from_args(
state<Types>().add_array_of(expression_result.value().get()), args, expr, state<Types>().add_array_of(expression_result.value().get()), args,
executor); current_pos, executor);
} }
Result CheckTask<nodes::Loop>::operator()(const nodes::Loop &expr, Result CheckTask<nodes::Loop>::operator()(const nodes::Loop &expr,
const Args &args) { const Args &args) {
const auto current_pos = PosOf(args.current_id);
// TODO: ranges ?? // TODO: ranges ??
MaybeResult interval_result; MaybeResult interval_result;
MaybeResult variable_result; MaybeResult variable_result;
Result expression_result = Run(*expr.get_expression(), {}); Result expression_result = Run(expr.expr, {expr.expr});
switch (expr.get_type()) { switch (expr.kind) {
case nodes::Loop::LOOP: // infinity loop, no params case nodes::Loop::LOOP: // infinity loop, no params
break; break;
case nodes::Loop::WHILE: case nodes::Loop::WHILE:
Run(*expr.get_condition().value(), Run(expr.condition.value(), Args{expr.condition.value()}.expect_builtin(
Args{}.expect_builtin(builtin::Type::BOOL, executor)); builtin::Type::BOOL, executor));
// --- type check is independent from loop itself --- // --- type check is independent from loop itself ---
// if (condition_result.value().is_invalid()) { // if (condition_result.value().is_invalid()) {
@ -147,8 +169,8 @@ Result CheckTask<nodes::Loop>::operator()(const nodes::Loop &expr,
case nodes::Loop::FOR: case nodes::Loop::FOR:
// TODO: expect range ?? // TODO: expect range ??
interval_result = interval_result =
Run(*expr.get_interval().value(), Run(expr.interval.value(), Args{expr.interval.value()}.expect_builtin(
Args{}.expect_builtin(builtin::Type::ARRAY, executor)); builtin::Type::ARRAY, executor));
if (interval_result.value().is_invalid()) { if (interval_result.value().is_invalid()) {
// --- type check is independent from loop itself --- // --- type check is independent from loop itself ---
@ -157,8 +179,8 @@ Result CheckTask<nodes::Loop>::operator()(const nodes::Loop &expr,
} }
variable_result = variable_result =
Run(*expr.get_variable().value(), Run(expr.variable.value(),
Args{}.expect( Args{expr.variable.value()}.expect(
interval_result.value().get().get()->get_parameter_proxy(0))); interval_result.value().get().get()->get_parameter_proxy(0)));
// --- type check is independent from loop itself --- // --- type check is independent from loop itself ---
@ -175,7 +197,7 @@ Result CheckTask<nodes::Loop>::operator()(const nodes::Loop &expr,
// TODO: modifier checks ??, modifiers ?? // TODO: modifier checks ??, modifiers ??
return type_check_from_args( return type_check_from_args(
state<Types>().add_array_of(expression_result.get()), args, expr, state<Types>().add_array_of(expression_result.get()), args, current_pos,
executor); executor);
} // IN PROGRESS } // IN PROGRESS
@ -183,46 +205,50 @@ Result CheckTask<nodes::Loop>::operator()(const nodes::Loop &expr,
Result CheckTask<nodes::Container>::CheckArray(const nodes::Container &expr, Result CheckTask<nodes::Container>::CheckArray(const nodes::Container &expr,
const Args &args) { const Args &args) {
MaybeResult last_expression_result; const auto current_pos = PosOf(args.current_id);
for (size_t i = 0; i < expr.expressions_size(); ++i) { MaybeResult last_expr_result;
for (const auto &elem_expr : expr.exprs) {
// elements should have same type, but type is not expected // elements should have same type, but type is not expected
auto expression_result = Run(*expr.get_expression(i), {}); auto expr_result = Run(elem_expr, {elem_expr});
if (!last_expression_result.has_value()) { if (!last_expr_result.has_value()) {
last_expression_result = expression_result; last_expr_result = expr_result;
} else { } else {
if (last_expression_result.value().get() != expression_result.get()) { if (last_expr_result.value().get() != expr_result.get()) {
type_check_error("Elements in array should have same type", type_check_error("Elements in array should have same type",
*expr.get_expression(i), executor); PosOf(args.current_id), executor);
// return TypeCheckResult::construct_invalid_result(); // max // return TypeCheckResult::construct_invalid_result(); // max
// possible checks, so no return // possible checks, so no return
} }
} }
} }
if (!last_expression_result.has_value()) { if (!last_expr_result.has_value()) {
type_check_error("Array with zero elements", expr, executor); type_check_error("Array with zero elements", current_pos, executor);
return Result::invalid(); return Result::invalid();
} }
return type_check_from_args( return type_check_from_args(
state<Types>().add_array_of(last_expression_result.value().get()), args, state<Types>().add_array_of(last_expr_result.value().get()), args,
expr, executor); current_pos, executor);
} }
Result CheckTask<nodes::Container>::CheckBlock(const nodes::Container &expr, Result CheckTask<nodes::Container>::CheckBlock(const nodes::Container &expr,
const Args &args) { const Args &args) {
const auto current_pos = PosOf(args.current_id);
nodes::MaybeType context_exit_type; nodes::MaybeType context_exit_type;
{ {
ContextHolder context_holder( ContextHolder context_holder(
state<State>(), expr, state<State>(), current_pos,
&context_exit_type); // TODO: is brought type returned &context_exit_type); // TODO: is brought type returned
for (size_t i = 0; i < expr.expressions_size(); ++i) { for (const auto &elem_expr : expr.exprs) {
// result types in block are discarded // result types in block are discarded
Run(*expr.get_expression(i), {}); Run(elem_expr, {elem_expr});
} }
} }
@ -232,13 +258,13 @@ Result CheckTask<nodes::Container>::CheckBlock(const nodes::Container &expr,
: Result(state<Types>().primitive(builtin::Type::UNIT)); : Result(state<Types>().primitive(builtin::Type::UNIT));
return type_check_from_args( return type_check_from_args(
state<Types>().add_array_of(block_brought_type.get()), args, expr, state<Types>().add_array_of(block_brought_type.get()), args, current_pos,
executor); executor);
} }
Result CheckTask<nodes::Container>::operator()(const nodes::Container &expr, Result CheckTask<nodes::Container>::operator()(const nodes::Container &expr,
const Args &args) { const Args &args) {
switch (expr.get_type()) { switch (expr.kind) {
case nodes::Container::ARRAY: case nodes::Container::ARRAY:
return CheckArray(expr, args); return CheckArray(expr, args);
case nodes::Container::BLOCK: case nodes::Container::BLOCK:
@ -250,22 +276,25 @@ Result CheckTask<nodes::Container>::operator()(const nodes::Container &expr,
Result CheckTask<nodes::Return>::operator()(const nodes::Return &expr, Result CheckTask<nodes::Return>::operator()(const nodes::Return &expr,
const Args &args) { const Args &args) {
auto returned_result = Run(*expr.get_expression(), {}); const auto current_pos = PosOf(args.current_id);
auto returned_result = Run(expr.expr, {expr.expr});
if (returned_result.is_invalid()) { if (returned_result.is_invalid()) {
return returned_result; return returned_result;
} }
switch (expr.get_type()) { switch (expr.kind) {
case nodes::Return::BRING: case nodes::Return::BRING:
if (state<State>().bring_type(returned_result.get())) { if (state<State>().bring_type(returned_result.get())) {
type_check_error("Different brought type to current one", expr, executor); type_check_error("Different brought type to current one", current_pos,
executor);
return Result::invalid(); return Result::invalid();
} }
break; break;
case nodes::Return::RETURN: case nodes::Return::RETURN:
if (!state<State>().return_type(returned_result.get())) { if (!state<State>().return_type(returned_result.get())) {
type_check_error("Different returned type to current one", expr, type_check_error("Different returned type to current one", current_pos,
executor); executor);
return Result::invalid(); return Result::invalid();
} }
@ -273,16 +302,18 @@ Result CheckTask<nodes::Return>::operator()(const nodes::Return &expr,
} }
return type_check_from_args(state<Types>().primitive(builtin::Type::UNIT), return type_check_from_args(state<Types>().primitive(builtin::Type::UNIT),
args, expr, executor); args, current_pos, executor);
} }
// TODO: warning if name is same to package prefix, function prefix, etc. ?? // TODO: warning if name is same to package prefix, function prefix, etc. ??
Result Result
CheckTask<nodes::NameDefinition>::operator()(const nodes::NameDefinition &expr, CheckTask<nodes::NameDefinition>::operator()(const nodes::NameDefinition &expr,
const Args &args) { const Args &args) {
const auto current_pos = PosOf(args.current_id);
if (!args.get_passed().has_value()) { if (!args.get_passed().has_value()) {
type_check_error("Can't deduce type of new variable from context", expr, type_check_error("Can't deduce type of new variable from context",
executor); current_pos, executor);
} }
// assigned type shold be one of <-, <>, -- (can't be ->) // assigned type shold be one of <-, <>, -- (can't be ->)
@ -290,31 +321,35 @@ CheckTask<nodes::NameDefinition>::operator()(const nodes::NameDefinition &expr,
if (nodes::utils::modifier_contains_OUT( if (nodes::utils::modifier_contains_OUT(
variable_type.get() variable_type.get()
->get_modifier())) { // TODO: utils::modifier_contains_OUT ->get_modifier())) { // TODO: utils::modifier_contains_OUT
type_check_error("Variable can't be assigned from out (->) value", expr, type_check_error("Variable can't be assigned from out (->) value",
executor); current_pos, executor);
} }
// variable accessible by reference by default ?? // variable accessible by reference by default ??
state<Types>().add_modification_of(variable_type, nodes::Modifier::REF); state<Types>().add_modification_of(variable_type, nodes::Modifier::REF);
if (!state<State>().insert_variable(*expr.get_name()->get(), variable_type, if (!state<State>().insert_variable(expr.name.value, variable_type,
expr.get_modifier())) { expr.kind)) {
type_check_error("Variable is already defined in this context", expr, type_check_error("Variable is already defined in this context", current_pos,
executor); executor);
} }
// Return BOOL as any := / =: expression // Return BOOL as any := / =: expression
return type_check_from_args(state<Types>().primitive(builtin::Type::BOOL), return type_check_from_args(state<Types>().primitive(builtin::Type::BOOL),
args, expr, executor); args, current_pos, executor);
} }
Result CheckTask<nodes::Access>::CheckArrayAccess(const nodes::Access &expr, Result CheckTask<nodes::Access>::CheckArrayAccess(const nodes::Access &expr,
const Args &args) { const Args &args) {
auto index_result = Run( const auto current_pos = PosOf(args.current_id);
*expr.get_index(), Args{}.expect_builtin(builtin::Type::INDEX, executor));
auto value_result = Run( auto index_result =
*expr.get_value(), Args{}.expect_builtin(builtin::Type::ARRAY, executor)); Run(expr.index,
Args{expr.index}.expect_builtin(builtin::Type::INDEX, executor));
auto value_result =
Run(expr.value,
Args{expr.value}.expect_builtin(builtin::Type::ARRAY, executor));
if (index_result.is_invalid()) { if (index_result.is_invalid()) {
return index_result; return index_result;
@ -327,34 +362,36 @@ Result CheckTask<nodes::Access>::CheckArrayAccess(const nodes::Access &expr,
// TODO: modifier checks ?? // TODO: modifier checks ??
return type_check_from_args(value_result.get().get()->get_parameter_proxy(0), return type_check_from_args(value_result.get().get()->get_parameter_proxy(0),
args, expr, executor); args, current_pos, executor);
} }
Result CheckTask<nodes::Access>::CheckTupleAccess(const nodes::Access &expr, Result CheckTask<nodes::Access>::CheckTupleAccess(const nodes::Access &expr,
const Args &args) { const Args &args) {
auto value_result = Run( const auto current_pos = PosOf(args.current_id);
*expr.get_value(), Args{}.expect_builtin(builtin::Type::TUPLE, executor));
auto value_result =
Run(expr.value,
Args{expr.value}.expect_builtin(builtin::Type::TUPLE, executor));
if (value_result.is_invalid()) { if (value_result.is_invalid()) {
return value_result; return value_result;
} }
size_t index = *expr.get_index() size_t index = *Ext(expr.index)
->get<nodes::Literal>() .get<nodes::Literal>()
.value() .get<size_t>() // Index type
->get<size_t>() // Index type
.value(); .value();
// TODO: modifier checks ?? // TODO: modifier checks ??
return type_check_from_args( return type_check_from_args(
value_result.get().get()->get_parameter_proxy(index), args, expr, value_result.get().get()->get_parameter_proxy(index), args, current_pos,
executor); executor);
} }
Result CheckTask<nodes::Access>::operator()(const nodes::Access &expr, Result CheckTask<nodes::Access>::operator()(const nodes::Access &expr,
const Args &args) { const Args &args) {
switch (expr.get_type()) { switch (expr.kind) {
case nodes::Access::ARRAY: case nodes::Access::ARRAY:
return CheckArrayAccess(expr, args); return CheckArrayAccess(expr, args);
case nodes::Access::TUPLE: case nodes::Access::TUPLE:
@ -364,24 +401,28 @@ Result CheckTask<nodes::Access>::operator()(const nodes::Access &expr,
Result CheckTask<nodes::LoopControl>::operator()(const nodes::LoopControl &expr, Result CheckTask<nodes::LoopControl>::operator()(const nodes::LoopControl &expr,
const Args &args) { const Args &args) {
const auto current_pos = PosOf(args.current_id);
return type_check_from_args(state<Types>().primitive(builtin::Type::UNIT), return type_check_from_args(state<Types>().primitive(builtin::Type::UNIT),
args, expr, executor); args, current_pos, executor);
} }
Result CheckTask<nodes::ModifierExpression>::operator()( Result CheckTask<nodes::ModifierExpression>::operator()(
const nodes::ModifierExpression &expr, const Args &args) { const nodes::ModifierExpression &expr, const Args &args) {
auto modified_result = Run(*expr.get_expression(), {}); const auto current_pos = PosOf(args.current_id);
auto modified_result = Run(expr.expr, {expr.expr});
if (modified_result.is_invalid()) { if (modified_result.is_invalid()) {
return Result::invalid(); return Result::invalid();
} }
if (nodes::utils::is_suffix_modifier( if (nodes::utils::is_suffix_modifier(
expr.get_modifier())) { // optional, result expr.modifier)) { // optional, result
// '?' - open optional / result in -> // '?' - open optional / result in ->
// (execute or not execute pattern // (execute or not execute pattern
// matching expression) / (value / // matching expression) / (value /
// return) (TODO: alternative for bring) // return) (TODO: alternative for bring)
// '!' - open optional / result -> value / panic // '!' - open optional / result -> value / panic
@ -392,16 +433,18 @@ Result CheckTask<nodes::ModifierExpression>::operator()(
modified_result.set(modified_result.get().get()->get_parameter_proxy(0)); modified_result.set(modified_result.get().get()->get_parameter_proxy(0));
break; break;
default: default:
type_check_error("Can unwrap only Optional or Result", expr, executor); type_check_error("Can unwrap only Optional or Result", current_pos,
executor);
return Result::invalid(); return Result::invalid();
} }
} else { } else {
// TODO: check that modifier can be applied // TODO: check that modifier can be applied
modified_result.set(state<Types>().add_modification_of( modified_result.set(state<Types>().add_modification_of(
modified_result.get(), expr.get_modifier())); modified_result.get(), expr.modifier));
} }
return type_check_from_args(modified_result.get(), args, expr, executor); return type_check_from_args(modified_result.get(), args, current_pos,
executor);
} // IN PROGRESS } // IN PROGRESS
// --- other // --- other
@ -410,12 +453,14 @@ Result CheckTask<nodes::ModifierExpression>::operator()(
Result Result
CheckTask<nodes::NameExpression>::operator()(const nodes::NameExpression &expr, CheckTask<nodes::NameExpression>::operator()(const nodes::NameExpression &expr,
const Args &args) { const Args &args) {
const auto current_pos = PosOf(args.current_id);
// TODO: constraints ?? // TODO: constraints ??
const auto name = expr.get_name(); const auto name = expr.name;
{ {
const auto fragments = name->get_fragments(); const auto fragments = name.get_fragments();
nodes::Identifier current_prefix = fragments.front(); nodes::Identifier current_prefix = fragments.front();
std::optional<State::VariableInfo> maybe_variable; std::optional<State::VariableInfo> maybe_variable;
size_t i = 0; size_t i = 0;
@ -425,7 +470,7 @@ CheckTask<nodes::NameExpression>::operator()(const nodes::NameExpression &expr,
current_prefix.append_after(fragments[i]); current_prefix.append_after(fragments[i]);
} }
maybe_variable = state<State>().find_variable(*current_prefix.get()); maybe_variable = state<State>().find_variable(current_prefix.value);
if (maybe_variable.has_value()) { if (maybe_variable.has_value()) {
break; break;
@ -449,8 +494,8 @@ CheckTask<nodes::NameExpression>::operator()(const nodes::NameExpression &expr,
// TODO: switch by type types: Variant, Tuple, ... // TODO: switch by type types: Variant, Tuple, ...
// Tuple -> try to find field // Tuple -> try to find field
// Others -> try to open / builtin fields ? // Others -> try to open / builtin fields ?
const auto maybe_field_type = const auto maybe_field_type = get_field_type_by_name(
get_field_type_by_name(type, *fragments[i].get(), expr, executor); type, fragments[i].value, current_pos, executor);
if (maybe_field_type.has_value()) { if (maybe_field_type.has_value()) {
type = maybe_field_type.value(); type = maybe_field_type.value();
@ -472,7 +517,7 @@ CheckTask<nodes::NameExpression>::operator()(const nodes::NameExpression &expr,
// TODO: check, if there is variable with this name // TODO: check, if there is variable with this name
// TODO: check var + fields // TODO: check var + fields
const auto maybe_function_definition = const auto maybe_function_definition =
find_name_definition(*name->get(), expr, executor); find_name_definition(name.value, current_pos, executor);
if (!maybe_function_definition.has_value()) { if (!maybe_function_definition.has_value()) {
return Result::invalid(); return Result::invalid();
} }
@ -482,8 +527,8 @@ CheckTask<nodes::NameExpression>::operator()(const nodes::NameExpression &expr,
// TODO: count passed type, if needed // TODO: count passed type, if needed
// TODO: manage situation with one out type at any position // TODO: manage situation with one out type at any position
// TODO + 1 - returned type - somtimes (can be ==) // TODO + 1 - returned type - somtimes (can be ==)
const auto args_given = expr.args_size(); const auto args_given = expr.args.size();
const auto args_defined = function_definition->args_size(); const auto args_defined = function_definition->args.size();
if (args_given + 1 < args_defined || if (args_given + 1 < args_defined ||
args_given > args_defined) { // other, when there is passed type args_given > args_defined) { // other, when there is passed type
type_check_error( type_check_error(
@ -494,7 +539,7 @@ CheckTask<nodes::NameExpression>::operator()(const nodes::NameExpression &expr,
? (" or " + std::to_string(args_defined - 1)) ? (" or " + std::to_string(args_defined - 1))
: ""} + : ""} +
")", ")",
expr, executor); current_pos, executor);
return Result::invalid(); return Result::invalid();
// TODO: try return correct type (function return type), when possible // TODO: try return correct type (function return type), when possible
} }
@ -504,64 +549,64 @@ CheckTask<nodes::NameExpression>::operator()(const nodes::NameExpression &expr,
std::vector<Result> function_argument_results; std::vector<Result> function_argument_results;
for (size_t i = 0; i < args_given; for (size_t i = 0; i < args_given;
++i) { // TODO: pass types with oud modifier ++i) { // TODO: pass types with oud modifier
const nodes::FunctionDefinition::Argument *argument = const nodes::FunctionDefinition::Argument &argument =
function_definition->get_argument(i); function_definition->args.at(i);
if (!argument->get_type().has_value()) { if (!argument.type.has_value()) {
type_check_error("Function argument type is not defined for argument " + type_check_error("Function argument type is not defined for argument " +
std::to_string(i), std::to_string(i),
expr, executor); current_pos, executor);
continue; continue;
} }
const auto annotation = expr.get_argument_annotation(i); const auto annotation = expr.args.at(i).first;
const auto expected_annotation = argument->get_annotation(); const auto expected_annotation = argument.annotation;
if (annotation.has_value() != expected_annotation.has_value()) { if (annotation.has_value() != expected_annotation.has_value()) {
type_check_error("Wrong function argument annotation: should be " + type_check_error("Wrong function argument annotation: should be " +
std::string{expected_annotation.has_value() std::string{expected_annotation.has_value()
? *expected_annotation.value() ? expected_annotation.value()
: "[none]"}, : "[none]"},
*expr.get_argument_value(i), executor); PosOf(expr.args.at(i).second), executor);
} }
if (annotation.has_value() && if (annotation.has_value() &&
*annotation.value() != *expected_annotation.value()) { annotation.value() != expected_annotation.value()) {
type_check_error( type_check_error(
"Wrong function argument type annotation: " + *annotation.value() + "Wrong function argument type annotation: " + annotation.value() +
" instead of " + *expected_annotation.value(), " instead of " + expected_annotation.value(),
*expr.get_argument_value(i), executor); PosOf(expr.args.at(i).second), executor);
} }
function_argument_results.push_back( function_argument_results.push_back(
Run(*expr.get_argument_value(i), Run(expr.args.at(i).second,
Args{}.expect(argument->get_type_proxy().value()))); Args{expr.args.at(i).second}.expect(argument.type.value())));
} }
if (function_definition->args_size() == 0) { if (function_definition->args.size() == 0) {
type_check_error( type_check_error(
"Function arguments size is zero. Returned type is not defined", expr, "Function arguments size is zero. Returned type is not defined",
executor); current_pos, executor);
return Result::invalid(); return Result::invalid();
} }
// TODO: check condition // TODO: check condition
if (args_given + 1 == args_defined) { if (args_given + 1 == args_defined) {
// returned type // returned type
const nodes::FunctionDefinition::Argument *returned = const nodes::FunctionDefinition::Argument &returned =
function_definition->get_argument(function_definition->args_size() - 1); function_definition->args.back();
// TODO: invert modifier ?? // TODO: invert modifier ??
if (!returned->get_type().has_value()) { if (!returned.type.has_value()) {
type_check_error( type_check_error(
"Function argument type is not defined for returned type", expr, "Function argument type is not defined for returned type",
executor); current_pos, executor);
return Result::invalid(); return Result::invalid();
} }
// TODO: invert modifier ?? // TODO: invert modifier ??
// TODO: generic types should be deduced from args // TODO: generic types should be deduced from args
return type_check_from_args(returned->get_type_proxy().value(), args, expr, return type_check_from_args(returned.type.value(), args, current_pos,
executor); executor);
} }
@ -573,20 +618,22 @@ CheckTask<nodes::NameExpression>::operator()(const nodes::NameExpression &expr,
// TODO // TODO
Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr, Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
const Args &args) { const Args &args) {
const auto current_pos = PosOf(args.current_id);
// TODO: constraints ?? // TODO: constraints ??
// TODO: use pass type // TODO: use pass type
const auto maybe_type_definition = const auto maybe_type_definition = find_type_definition(
find_type_definition(*expr.get_type()->get_name()->get(), expr, executor); expr.type.get()->get_name()->value, current_pos, executor);
if (!maybe_type_definition.has_value()) { if (!maybe_type_definition.has_value()) {
return Result::invalid(); return Result::invalid();
} }
const nodes::TypeDefinition *type_definition = maybe_type_definition.value(); const nodes::TypeDefinition *type_definition = maybe_type_definition.value();
if (!type_definition->get_type().has_value()) { if (!type_definition->type.has_value()) {
type_check_error( type_check_error(
"Type defenition for constructor type not found (declaration only)", "Type defenition for constructor type not found (declaration only)",
expr, executor); current_pos, executor);
return Result::invalid(); return Result::invalid();
} }
@ -594,13 +641,13 @@ Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
// TODO: check that is not typeclass ?? // TODO: check that is not typeclass ??
nodes::Type type = type_definition->get_type().value(); nodes::Type type = type_definition->type.value();
// TODO: work with different parametric types: tuple, variant, ... // TODO: work with different parametric types: tuple, variant, ...
if (expr.args_size() == 0) { if (expr.args.size() == 0) {
type_check_error("Number of type constructor arguments should be > 0", expr, type_check_error("Number of type constructor arguments should be > 0",
executor); current_pos, executor);
return Result::invalid(); return Result::invalid();
// TODO: try return correct type (constructor's type), when possible (not // TODO: try return correct type (constructor's type), when possible (not
// generic) // generic)
@ -613,13 +660,13 @@ Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
{ // check args size, ets. { // check args size, ets.
switch (builtin_type) { switch (builtin_type) {
case builtin::Type::TUPLE: case builtin::Type::TUPLE:
if (expr.args_size() != type.get()->parameters_size()) { if (expr.args.size() != type.get()->parameters_size()) {
type_check_error( type_check_error(
"Number of type constructor arguments is different from expected " "Number of type constructor arguments is different from expected "
"(" + "(" +
std::to_string(expr.args_size()) + " instead of " + std::to_string(expr.args.size()) + " instead of " +
std::to_string(type.get()->parameters_size()) + ")", std::to_string(type.get()->parameters_size()) + ")",
expr, executor); current_pos, executor);
return Result::invalid(); return Result::invalid();
// TODO: try return correct type (constructor's type), when possible // TODO: try return correct type (constructor's type), when possible
// (not generic) // (not generic)
@ -631,11 +678,11 @@ Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
case builtin::Type::ERROR: case builtin::Type::ERROR:
case builtin::Type::FUNCTION: case builtin::Type::FUNCTION:
case builtin::Type::NONE: case builtin::Type::NONE:
if (expr.args_size() != 1) { // TODO: better to_string if (expr.args.size() != 1) { // TODO: better to_string
type_check_error("Number of type constructor arguments should be = 1 " type_check_error("Number of type constructor arguments should be = 1 "
"(builtin type " + "(builtin type " +
std::to_string(uint(builtin_type)) + ")", std::to_string(uint(builtin_type)) + ")",
expr, executor); current_pos, executor);
return Result::invalid(); return Result::invalid();
// TODO: try return correct type (constructor's type), when possible // TODO: try return correct type (constructor's type), when possible
// (not generic) // (not generic)
@ -648,8 +695,8 @@ Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
if (builtin::types::get_parameters_count(builtin_type).has_value() && if (builtin::types::get_parameters_count(builtin_type).has_value() &&
type.get()->parameters_size() != type.get()->parameters_size() !=
builtin::types::get_parameters_count(builtin_type).value()) { builtin::types::get_parameters_count(builtin_type).value()) {
type_check_error("Wrong amount of parametars for builtin type", expr, type_check_error("Wrong amount of parametars for builtin type",
executor); current_pos, executor);
return Result::invalid(); return Result::invalid();
// TODO: try return correct type (constructor's type), when possible (not // TODO: try return correct type (constructor's type), when possible (not
@ -661,12 +708,12 @@ Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
{ // check annotations { // check annotations
const auto check_same_annotation = const auto check_same_annotation =
[&expr, &executor = this->executor]( [&expr, this](size_t i,
size_t i, std::optional<const std::string *> expected_annotation, std::optional<const std::string *> expected_annotation,
bool log_errors) { bool log_errors) {
bool is_same = true; bool is_same = true;
const auto annotation = expr.get_argument_annotation(i); const auto annotation = expr.args.at(i).first;
if (annotation.has_value() != expected_annotation.has_value()) { if (annotation.has_value() != expected_annotation.has_value()) {
if (log_errors) { if (log_errors) {
@ -675,18 +722,18 @@ Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
std::string{expected_annotation.has_value() std::string{expected_annotation.has_value()
? *expected_annotation.value() ? *expected_annotation.value()
: "[none]"}, : "[none]"},
*expr.get_argument_value(i), executor); PosOf(expr.args.at(i).second), executor);
} }
is_same = false; is_same = false;
} }
if (annotation.has_value() && if (annotation.has_value() &&
*annotation.value() != *expected_annotation.value()) { annotation.value() != *expected_annotation.value()) {
if (log_errors) { if (log_errors) {
type_check_error("Wrong function argument type annotation: " + type_check_error("Wrong function argument type annotation: " +
*annotation.value() + " instead of " + annotation.value() + " instead of " +
*expected_annotation.value(), *expected_annotation.value(),
*expr.get_argument_value(i), executor); PosOf(expr.args.at(i).second), executor);
} }
is_same = false; is_same = false;
@ -695,22 +742,21 @@ Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
return is_same; return is_same;
}; };
const auto check_no_annotation = const auto check_no_annotation = [&expr, this](size_t i, bool log_errors) {
[&expr, &executor = this->executor](size_t i, bool log_errors) { if (expr.args.at(i).first.has_value()) {
if (expr.get_argument_annotation(i).has_value()) { if (log_errors) {
if (log_errors) { type_check_error(
type_check_error( "Type constructor argument annotation not expected there",
"Type constructor argument annotation not expected there", PosOf(expr.args.at(i).second), executor);
*expr.get_argument_value(i), executor); }
} return false;
return false; }
} return true;
return true; };
};
switch (builtin_type) { switch (builtin_type) {
case builtin::Type::TUPLE: case builtin::Type::TUPLE:
for (size_t i = 0; i < expr.args_size(); ++i) { for (size_t i = 0; i < expr.args.size(); ++i) {
check_same_annotation(i, type.get()->get_parameter(i)->get_annotation(), check_same_annotation(i, type.get()->get_parameter(i)->get_annotation(),
true /*log errors*/); true /*log errors*/);
} }
@ -730,7 +776,7 @@ Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
!check_no_annotation(0, false /*do not log errors*/)) { !check_no_annotation(0, false /*do not log errors*/)) {
type_check_error("Wrong type constructor argument annotation in " type_check_error("Wrong type constructor argument annotation in "
"constructor of variant type", "constructor of variant type",
*expr.get_argument_value(0), executor); PosOf(expr.args.front().second), executor);
} }
break; break;
case builtin::Type::ERROR: // no anotations ?? case builtin::Type::ERROR: // no anotations ??
@ -751,57 +797,60 @@ Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
{ // type check args { // type check args
switch (builtin_type) { switch (builtin_type) {
case builtin::Type::TUPLE: case builtin::Type::TUPLE:
for (size_t i = 0; i < expr.args_size(); ++i) { for (size_t i = 0; i < expr.args.size(); ++i) {
Run(*expr.get_argument_value(i), Run(expr.args.at(i).second, Args{expr.args.at(i).second}.expect(
Args{}.expect(type.get()->get_parameter_proxy(i))); type.get()->get_parameter_proxy(i)));
} }
break; break;
case builtin::Type::VARIANT: case builtin::Type::VARIANT:
if (chosen_variant_option.has_value()) { if (chosen_variant_option.has_value()) {
Run(*expr.get_argument_value(0), Run(expr.args.front().second, Args{expr.args.front().second}.expect(
Args{}.expect(type.get()->get_parameter_proxy( type.get()->get_parameter_proxy(
chosen_variant_option.value()))); chosen_variant_option.value())));
} else { // TODO: error, if there is more then one possible variant in } else { // TODO: error, if there is more then one possible variant in
// answer // answer
nodes::Types possible_options; nodes::Types possible_options;
for (size_t i = 0; i < type.get()->parameters_size(); ++i) { for (size_t i = 0; i < type.get()->parameters_size(); ++i) {
possible_options.push_back(type.get()->get_parameter_proxy(i)); possible_options.push_back(type.get()->get_parameter_proxy(i));
} }
Run(*expr.get_argument_value(0), Args{}.expect(possible_options)); Run(expr.args.front().second,
Args{expr.args.front().second}.expect(possible_options));
} }
break; break;
case builtin::Type::OPTIONAL: case builtin::Type::OPTIONAL:
// first parameter or NULL // first parameter or NULL
Run(*expr.get_argument_value(0), Run(expr.args.front().second,
Args{}.expect( Args{expr.args.front().second}.expect(
{type.get()->get_parameter_proxy(0), {type.get()->get_parameter_proxy(0),
state<Types>().primitive(builtin::Type::NULL_OPTION)})); state<Types>().primitive(builtin::Type::NULL_OPTION)}));
break; break;
case builtin::Type::RESULT: case builtin::Type::RESULT:
// first parameter or ERROR[second parameter] // first parameter or ERROR[second parameter]
Run(*expr.get_argument_value(0), Run(expr.args.front().second,
Args{}.expect({type.get()->get_parameter_proxy(0), Args{expr.args.front().second}.expect(
state<Types>().add_error_of( {type.get()->get_parameter_proxy(0),
type.get()->get_parameter_proxy(1))})); state<Types>().add_error_of(
type.get()->get_parameter_proxy(1))}));
break; break;
case builtin::Type::ERROR: case builtin::Type::ERROR:
// first parameter // first parameter
Run(*expr.get_argument_value(0), Run(expr.args.front().second, Args{expr.args.front().second}.expect(
Args{}.expect(type.get()->get_parameter_proxy(0))); type.get()->get_parameter_proxy(0)));
break; break;
case builtin::Type::FUNCTION: case builtin::Type::FUNCTION:
case builtin::Type::NONE: case builtin::Type::NONE:
// type itself // type itself
Run(*expr.get_argument_value(0), Args{}.expect(type)); Run(expr.args.front().second,
Args{expr.args.front().second}.expect(type));
break; break;
default: // array, basic types default: // array, basic types
type_check_error("Type can't be constructed", expr, executor); type_check_error("Type can't be constructed", current_pos, executor);
break; break;
} }
} }
// TODO: deduce generic parts in type // TODO: deduce generic parts in type
return type_check_from_args(expr.get_type_proxy(), args, expr, executor); return type_check_from_args(expr.type, args, current_pos, executor);
// TODO: add <- modifiier to type ?? // TODO: add <- modifiier to type ??
} // IN PROGRESS } // IN PROGRESS
@ -809,27 +858,29 @@ Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
// TODO // TODO
Result CheckTask<nodes::Lambda>::operator()(const nodes::Lambda &expr, Result CheckTask<nodes::Lambda>::operator()(const nodes::Lambda &expr,
const Args &args) { const Args &args) {
const auto current_pos = PosOf(args.current_id);
if (args.get_expected().empty()) { if (args.get_expected().empty()) {
type_check_error("Can't deduce type of lambda function from context: no " type_check_error("Can't deduce type of lambda function from context: no "
"one type expected", "one type expected",
expr, executor); current_pos, executor);
} }
if (args.get_expected().size() != if (args.get_expected().size() !=
1) { // TODO: check if only one function argument 1) { // TODO: check if only one function argument
type_check_error("Can't deduce type of lambda function from context; too " type_check_error("Can't deduce type of lambda function from context; too "
"much possible types", "much possible types",
expr, executor); current_pos, executor);
} }
const auto expected_type = args.get_expected().front(); const auto expected_type = args.get_expected().front();
if (!expected_type.get()->is_builtin(builtin::Type::FUNCTION)) { if (!expected_type.get()->is_builtin(builtin::Type::FUNCTION)) {
type_check_error("Type of lambda function should be function", expr, type_check_error("Type of lambda function should be function", current_pos,
executor); executor);
} }
// TODO: deal with return type (+1 sometimes), etc // TODO: deal with return type (+1 sometimes), etc
const auto args_given = expr.args_size(); const auto args_given = expr.args.size();
const auto args_defined = expected_type.get()->parameters_size(); const auto args_defined = expected_type.get()->parameters_size();
if (args_given != args_defined) { if (args_given != args_defined) {
type_check_error( type_check_error(
@ -840,41 +891,38 @@ Result CheckTask<nodes::Lambda>::operator()(const nodes::Lambda &expr,
? (" or " + std::to_string(args_defined - 1)) ? (" or " + std::to_string(args_defined - 1))
: ""} + : ""} +
")", ")",
expr, executor); current_pos, executor);
} }
// TODO: set another context (for expression typecheck and vars) // TODO: set another context (for expression typecheck and vars)
for (size_t i = 0; i < args_given; ++i) { for (size_t i = 0; i < args_given; ++i) {
if (!state<State>().insert_variable( if (!state<State>().insert_variable(
*expr.get_argument(i)->get(), expr.args.at(i).value, expected_type.get()->get_parameter_proxy(i),
expected_type.get()->get_parameter_proxy(i),
nodes::NameDefinition::Kind::LET)) { nodes::NameDefinition::Kind::LET)) {
// TODO: which modifier ?? // TODO: which modifier ??
type_check_error("Variable is already defined in this context", expr, type_check_error("Variable is already defined in this context",
executor); current_pos, executor);
} }
} }
// TODO: out type is can be not last // TODO: out type is can be not last
if (args_given + 1 == args_defined) { if (args_given + 1 == args_defined) {
Run(*expr.get_expression(), Run(expr.expr,
Args{}.expect( Args{expr.expr}.expect(
expected_type.get()->get_parameter_proxy(args_defined - 1))); expected_type.get()->get_parameter_proxy(args_defined - 1)));
} }
// TODO: needed ?? (only passed type check required ??) // TODO: needed ?? (only passed type check required ??)
return type_check_from_args(expected_type, args, expr, executor); return type_check_from_args(expected_type, args, current_pos, executor);
} // IN PROGRESS } // IN PROGRESS
Result CheckTask<nodes::Extra>::operator()(const nodes::Extra &, const Args &) { Result CheckTask<nodes::Extra>::operator()(const nodes::Extra &, const Args &) {
return Result(state<Types>().primitive(builtin::Type::UNIT)); return Result(state<Types>().primitive(builtin::Type::UNIT));
} }
Result CheckTask<nodes::EmptyLines>::operator()(const nodes::EmptyLines &, Result CheckTask<nodes::EmptyLines>::operator()(const nodes::EmptyLines &,
const Args &) { const Args &) {
return Result(state<Types>().primitive(builtin::Type::UNIT)); return Result(state<Types>().primitive(builtin::Type::UNIT));
} }

View file

@ -200,7 +200,7 @@ void type_check_error(const std::string &message, const utils::Pos &,
return; return;
} }
logc.Error<Log::kProc>({{message}} /* TODO: node */); logc.Error<Log::kProc>({{message}} /* TODO: use pos */);
} }
} // namespace type_check } // namespace type_check

View file

@ -3,10 +3,10 @@ includes("../nodes/xmake.lua")
set_languages("c++20") set_languages("c++20")
-- target("lang.type_check") target("lang.type_check")
-- set_kind("static") set_kind("static")
-- add_includedirs("include", {public = true}) add_includedirs("include", {public = true})
-- add_files("src/**.cpp") add_files("src/**.cpp")
-- add_deps("lang.utils", "lang.nodes") add_deps("lang.utils", "lang.nodes")
-- set_warnings("allextra") -- , "error") set_warnings("allextra") -- , "error")
-- set_rundir("$(projectdir)") set_rundir("$(projectdir)")