mirror of
https://codeberg.org/ProgramSnail/lang.git
synced 2025-12-06 06:58:46 +00:00
func name fix, move name expr and constructor type checks to separated files
This commit is contained in:
parent
00bf9705a9
commit
361a267054
8 changed files with 325 additions and 484 deletions
|
|
@ -168,13 +168,14 @@ struct FunctionDefinition {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
public:
|
||||||
SymbolDocs docs;
|
SymbolDocs docs;
|
||||||
std::vector<Constraint> constraints;
|
std::vector<Constraint> constraints;
|
||||||
Modifier return_modifier;
|
Modifier return_modifier;
|
||||||
bool is_method;
|
bool is_method;
|
||||||
Identifier name;
|
Identifier name;
|
||||||
Identifier full_name;
|
Identifier full_name;
|
||||||
std::vector<Argument> args;
|
std::vector<Argument> args; // including returned values
|
||||||
bool are_annotations_same_to_names; // needed for easier prinitng process
|
bool are_annotations_same_to_names; // needed for easier prinitng process
|
||||||
std::optional<NodeId> expr;
|
std::optional<NodeId> expr;
|
||||||
}; // refactor ??
|
}; // refactor ??
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "type_check_utils.hpp"
|
||||||
|
|
||||||
|
// IN PROGRESS
|
||||||
|
|
||||||
|
namespace type_check {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct CheckTask<nodes::Constructor>
|
||||||
|
: public CheckTaskBase<nodes::Constructor> {
|
||||||
|
using CheckTaskBase<nodes::Constructor>::CheckTaskBase;
|
||||||
|
|
||||||
|
Result operator()(const nodes::Constructor &expr, const Args &args) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace type_check
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
#include "expression_nodes.hpp"
|
#include "expression_nodes.hpp"
|
||||||
#include "type_check_utils.hpp"
|
#include "type_check_utils.hpp"
|
||||||
|
|
||||||
|
#include "constructor_expression_type_check.hpp"
|
||||||
|
#include "name_expression_type_check.hpp"
|
||||||
|
|
||||||
// IN PROGRESS
|
// IN PROGRESS
|
||||||
|
|
||||||
namespace type_check {
|
namespace type_check {
|
||||||
|
|
@ -104,22 +107,9 @@ struct CheckTask<nodes::ModifierExpression>
|
||||||
|
|
||||||
// --- other
|
// --- other
|
||||||
|
|
||||||
template <>
|
// NameExpression -> other file
|
||||||
struct CheckTask<nodes::NameExpression>
|
|
||||||
: public CheckTaskBase<nodes::NameExpression> {
|
|
||||||
using CheckTaskBase<nodes::NameExpression>::CheckTaskBase;
|
|
||||||
|
|
||||||
Result operator()(const nodes::NameExpression &expr,
|
// Constructor -> other file
|
||||||
const Args &args) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct CheckTask<nodes::Constructor>
|
|
||||||
: public CheckTaskBase<nodes::Constructor> {
|
|
||||||
using CheckTaskBase<nodes::Constructor>::CheckTaskBase;
|
|
||||||
|
|
||||||
Result operator()(const nodes::Constructor &expr, const Args &args) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct CheckTask<nodes::Lambda> : public CheckTaskBase<nodes::Lambda> {
|
struct CheckTask<nodes::Lambda> : public CheckTaskBase<nodes::Lambda> {
|
||||||
|
|
|
||||||
18
lang/type_check/include/name_expression_type_check.hpp
Normal file
18
lang/type_check/include/name_expression_type_check.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "type_check_utils.hpp"
|
||||||
|
|
||||||
|
// IN PROGRESS
|
||||||
|
|
||||||
|
namespace type_check {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct CheckTask<nodes::NameExpression>
|
||||||
|
: public CheckTaskBase<nodes::NameExpression> {
|
||||||
|
using CheckTaskBase<nodes::NameExpression>::CheckTaskBase;
|
||||||
|
|
||||||
|
Result operator()(const nodes::NameExpression &expr,
|
||||||
|
const Args &args) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace type_check
|
||||||
|
|
@ -358,7 +358,7 @@ nodes::MaybeType FieldTypeByName(nodes::Type type, const std::string &field,
|
||||||
const utils::Pos &pos, Executor &executor,
|
const utils::Pos &pos, Executor &executor,
|
||||||
bool handle_errors = true);
|
bool handle_errors = true);
|
||||||
|
|
||||||
inline nodes::MaybeType Curry(MaybeResult result) {
|
inline nodes::MaybeType Bind(MaybeResult result) {
|
||||||
return result.has_value() ? result->Get() : nodes::MaybeType{};
|
return result.has_value() ? result->Get() : nodes::MaybeType{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include "constructor_expression_type_check.hpp"
|
||||||
|
|
||||||
|
#include "expression_type_check.hpp"
|
||||||
|
|
||||||
|
namespace type_check {} // namespace type_check
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
// TODO: typecheck pass in all functions
|
// TODO: typecheck pass in all functions
|
||||||
// TODO: assign types in nodes
|
// TODO: assign types in nodes
|
||||||
|
// TODO: pass passet type into subexpressions ??
|
||||||
|
|
||||||
namespace type_check {
|
namespace type_check {
|
||||||
|
|
||||||
|
|
@ -85,7 +86,7 @@ Result CheckTask<nodes::Match>::operator()(const nodes::Match &expr,
|
||||||
const auto &expr = current_case.expr.value();
|
const auto &expr = current_case.expr.value();
|
||||||
at_least_one_case_with_expression = true;
|
at_least_one_case_with_expression = true;
|
||||||
|
|
||||||
Result case_result = Run(expr, Args{expr}.Expect(Curry(expr_result)));
|
Result case_result = Run(expr, Args{expr}.Expect(Bind(expr_result)));
|
||||||
|
|
||||||
if (not expr_result.has_value() and not case_result.is_invalid()) {
|
if (not expr_result.has_value() and not case_result.is_invalid()) {
|
||||||
expr_result = std::move(case_result);
|
expr_result = std::move(case_result);
|
||||||
|
|
@ -127,7 +128,7 @@ Result CheckTask<nodes::Condition>::operator()(const nodes::Condition &expr,
|
||||||
// expression
|
// expression
|
||||||
Result case_result =
|
Result case_result =
|
||||||
Run(current_case.second,
|
Run(current_case.second,
|
||||||
Args{current_case.second}.Expect(Curry(expr_result)));
|
Args{current_case.second}.Expect(Bind(expr_result)));
|
||||||
|
|
||||||
if (not expr_result.has_value() and not case_result.is_invalid()) {
|
if (not expr_result.has_value() and not case_result.is_invalid()) {
|
||||||
expr_result = std::move(case_result);
|
expr_result = std::move(case_result);
|
||||||
|
|
@ -136,7 +137,7 @@ Result CheckTask<nodes::Condition>::operator()(const nodes::Condition &expr,
|
||||||
|
|
||||||
if (expr.else_case.has_value()) {
|
if (expr.else_case.has_value()) {
|
||||||
Run(expr.else_case.value(),
|
Run(expr.else_case.value(),
|
||||||
Args{expr.else_case.value()}.Expect(Curry(expr_result)));
|
Args{expr.else_case.value()}.Expect(Bind(expr_result)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!expr_result.has_value()) {
|
if (!expr_result.has_value()) {
|
||||||
|
|
@ -308,17 +309,15 @@ Result CheckTask<nodes::Return>::operator()(const nodes::Return &expr,
|
||||||
current_pos);
|
current_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// <------ NOTE: CHECK ENDED THERE
|
|
||||||
|
|
||||||
// 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) {
|
||||||
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
||||||
|
|
||||||
const auto current_pos = PosOf(args.current_id);
|
const auto current_pos = PosOf(args.current_id);
|
||||||
|
|
||||||
if (!args.passed().has_value()) {
|
//
|
||||||
|
|
||||||
|
if (not args.passed().has_value()) {
|
||||||
TypeCheckError("Can't deduce type of new variable from context",
|
TypeCheckError("Can't deduce type of new variable from context",
|
||||||
current_pos);
|
current_pos);
|
||||||
}
|
}
|
||||||
|
|
@ -326,26 +325,26 @@ CheckTask<nodes::NameDefinition>::operator()(const nodes::NameDefinition &expr,
|
||||||
// assigned type shold be one of <-, <>, -- (can't be ->)
|
// assigned type shold be one of <-, <>, -- (can't be ->)
|
||||||
const auto variable_type = args.passed().value();
|
const auto variable_type = args.passed().value();
|
||||||
if (nodes::utils::modifier_contains_OUT(
|
if (nodes::utils::modifier_contains_OUT(
|
||||||
variable_type.get()
|
variable_type.get()->get_modifier())) {
|
||||||
->get_modifier())) { // TODO: utils::modifier_contains_OUT
|
// TODO: modifiers are nto fully implemented yet
|
||||||
TypeCheckError("Variable can't be assigned from out (->) value",
|
TypeCheckError("Variable can't be assigned from out (->) value",
|
||||||
current_pos);
|
current_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// variable accessible by reference by default ??
|
// variable is 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>().InsertVariable(expr.name.value, variable_type,
|
// TODO: warning if name is same to package prefix, function prefix, etc. ??
|
||||||
expr.kind)) {
|
if (not state<State>().InsertVariable(expr.name.value, variable_type,
|
||||||
|
expr.kind)) {
|
||||||
TypeCheckError("Variable is already defined in this context", current_pos);
|
TypeCheckError("Variable is already defined in this context", current_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return BOOL as any := / =: expression
|
// Return BOOL (as any := / =: expression) == true
|
||||||
return TypeCheckFromArgs(state<Types>().primitive(builtin::Type::BOOL), args,
|
return TypeCheckFromArgs(state<Types>().primitive(builtin::Type::BOOL), args,
|
||||||
current_pos);
|
current_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: CHECKED
|
|
||||||
Result CheckTask<nodes::Access>::CheckArrayAccess(const nodes::Access &expr,
|
Result CheckTask<nodes::Access>::CheckArrayAccess(const nodes::Access &expr,
|
||||||
const Args &args) {
|
const Args &args) {
|
||||||
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
||||||
|
|
@ -375,7 +374,6 @@ Result CheckTask<nodes::Access>::CheckArrayAccess(const nodes::Access &expr,
|
||||||
args, current_pos);
|
args, current_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: CHECKED
|
|
||||||
Result CheckTask<nodes::Access>::CheckTupleAccess(const nodes::Access &expr,
|
Result CheckTask<nodes::Access>::CheckTupleAccess(const nodes::Access &expr,
|
||||||
const Args &args) {
|
const Args &args) {
|
||||||
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
||||||
|
|
@ -383,7 +381,7 @@ Result CheckTask<nodes::Access>::CheckTupleAccess(const nodes::Access &expr,
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
// TODO: check expect for parametrized type without parametears
|
// TODO: check expect for parametrized type without parameters
|
||||||
auto value_result =
|
auto value_result =
|
||||||
Run(expr.value,
|
Run(expr.value,
|
||||||
Args{expr.value}.ExpectBuiltin(builtin::Type::TUPLE, executor));
|
Args{expr.value}.ExpectBuiltin(builtin::Type::TUPLE, executor));
|
||||||
|
|
@ -401,7 +399,6 @@ Result CheckTask<nodes::Access>::CheckTupleAccess(const nodes::Access &expr,
|
||||||
args, current_pos);
|
args, current_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: CHECKED
|
|
||||||
Result CheckTask<nodes::Access>::operator()(const nodes::Access &expr,
|
Result CheckTask<nodes::Access>::operator()(const nodes::Access &expr,
|
||||||
const Args &args) {
|
const Args &args) {
|
||||||
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
||||||
|
|
@ -414,7 +411,6 @@ Result CheckTask<nodes::Access>::operator()(const nodes::Access &expr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: CHECKED
|
|
||||||
Result CheckTask<nodes::LoopControl>::operator()(const nodes::LoopControl &expr,
|
Result CheckTask<nodes::LoopControl>::operator()(const nodes::LoopControl &expr,
|
||||||
const Args &args) {
|
const Args &args) {
|
||||||
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
||||||
|
|
@ -433,25 +429,46 @@ Result CheckTask<nodes::ModifierExpression>::operator()(
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
// TODO: expext Optional or Result ??
|
// TODO: check expect for parametrized type without parameters
|
||||||
auto modified_result = Run(expr.expr, {expr.expr});
|
auto given_args = nodes::utils::is_suffix_modifier(expr.modifier)
|
||||||
|
? Args{expr.expr}.Expect({
|
||||||
|
state<Types>().primitive(builtin::Type::OPTIONAL),
|
||||||
|
state<Types>().primitive(builtin::Type::RESULT),
|
||||||
|
})
|
||||||
|
: Args{expr.expr};
|
||||||
|
|
||||||
|
auto modified_result = Run(expr.expr, given_args);
|
||||||
|
|
||||||
if (modified_result.is_invalid()) {
|
if (modified_result.is_invalid()) {
|
||||||
return Result::invalid();
|
return Result::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodes::utils::is_suffix_modifier(expr.modifier)) { // optional, result
|
if (nodes::utils::is_suffix_modifier(expr.modifier)) { // optional, result
|
||||||
|
|
||||||
// '?' - open optional / result in -> (execute or not execute pattern
|
|
||||||
// matching expression) / (value / return) (TODO: alternative for bring)
|
|
||||||
|
|
||||||
// '!' - open optional / result -> value / panic
|
|
||||||
|
|
||||||
switch (modified_result.Get().get()->to_builtin()) {
|
switch (modified_result.Get().get()->to_builtin()) {
|
||||||
case builtin::Type::OPTIONAL:
|
case builtin::Type::OPTIONAL: { // '?' - open optional / result in ->
|
||||||
case builtin::Type::RESULT:
|
// (execute
|
||||||
modified_result.Set(modified_result.Get().get()->get_parameter_proxy(0));
|
// or not execute pattern matching expression)
|
||||||
|
// / (value / return)
|
||||||
|
|
||||||
|
const auto underlying_type =
|
||||||
|
modified_result.Get().get()->get_parameter_proxy(0);
|
||||||
|
|
||||||
|
// TODO: alternative for bring in future
|
||||||
|
if (not state<State>().ReturnType(underlying_type)) {
|
||||||
|
TypeCheckError("Different returned type to current one", current_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
modified_result.Set(underlying_type);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
case builtin::Type::RESULT: { // '!' - open optional / result -> value /
|
||||||
|
// panic
|
||||||
|
const auto underlying_type =
|
||||||
|
modified_result.Get().get()->get_parameter_proxy(0);
|
||||||
|
|
||||||
|
modified_result.Set(underlying_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
TypeCheckError("Can unwrap only Optional or Result", current_pos);
|
TypeCheckError("Can unwrap only Optional or Result", current_pos);
|
||||||
return Result::invalid();
|
return Result::invalid();
|
||||||
|
|
@ -459,440 +476,35 @@ Result CheckTask<nodes::ModifierExpression>::operator()(
|
||||||
} else {
|
} else {
|
||||||
// NOTE: other modifiers applied instead of current one
|
// NOTE: other modifiers applied instead of current one
|
||||||
|
|
||||||
// TODO: check thatmodification is possible
|
// TODO: check that modification is possible (not all casts are possible)
|
||||||
modified_result.Set(state<Types>().add_modification_of(
|
modified_result.Set(state<Types>().add_modification_of(
|
||||||
modified_result.Get(), expr.modifier));
|
modified_result.Get(), expr.modifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
return TypeCheckFromArgs(modified_result.Get(), args, current_pos);
|
return TypeCheckFromArgs(modified_result.Get(), args, current_pos);
|
||||||
} // IN PROGRESS
|
}
|
||||||
|
|
||||||
// --- other
|
// --- other
|
||||||
|
|
||||||
// TODO
|
// <------ NOTE: CHECK ENDED THERE
|
||||||
Result
|
|
||||||
CheckTask<nodes::NameExpression>::operator()(const nodes::NameExpression &expr,
|
|
||||||
const Args &args) {
|
|
||||||
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
|
||||||
|
|
||||||
const auto current_pos = PosOf(args.current_id);
|
|
||||||
|
|
||||||
// TODO: constraints ??
|
|
||||||
|
|
||||||
const auto name = expr.name;
|
|
||||||
|
|
||||||
{
|
|
||||||
const auto fragments = name.get_fragments();
|
|
||||||
nodes::Identifier current_prefix = fragments.front();
|
|
||||||
std::optional<State::VariableInfo> maybe_variable;
|
|
||||||
size_t i = 0;
|
|
||||||
for (; i < fragments.size(); ++i) {
|
|
||||||
// go in prefixes and try to find matching var namei
|
|
||||||
if (i > 0) {
|
|
||||||
current_prefix.append_after(fragments[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
maybe_variable = state<State>().FindVariable(current_prefix.value);
|
|
||||||
|
|
||||||
if (maybe_variable.has_value()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maybe_variable.has_value()) {
|
|
||||||
auto &type = maybe_variable.value().type;
|
|
||||||
for (size_t j = i + 1; j < fragments.size(); ++i) {
|
|
||||||
if (j + 1 ==
|
|
||||||
fragments
|
|
||||||
.size()) { // then this can be method call
|
|
||||||
// TODO: try to find method or local function
|
|
||||||
// if found, search to field (without error handling)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: fields from several fragments ? (not acceptable fors methods /
|
|
||||||
// functions)
|
|
||||||
|
|
||||||
// <- in typecheck utils method
|
|
||||||
// TODO: switch by type types: Variant, Tuple, ...
|
|
||||||
// Tuple -> try to find field
|
|
||||||
// Others -> try to open / builtin fields ?
|
|
||||||
const auto maybe_field_type =
|
|
||||||
FieldTypeByName(type, fragments[i].value, current_pos, executor);
|
|
||||||
|
|
||||||
if (maybe_field_type.has_value()) {
|
|
||||||
type = maybe_field_type.value();
|
|
||||||
// TODO
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
// fields for the names should be found
|
|
||||||
// last segment can be function name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- TODO --- deal with passed type --- TODO --- (additional argument)
|
|
||||||
// TODO: deal with given ->(out) Args (type not expected, but passed into)
|
|
||||||
|
|
||||||
// TODO: check, if there is variable with this name
|
|
||||||
// TODO: check var + fields
|
|
||||||
const auto maybe_function_definition =
|
|
||||||
FindNameDefinition(name.value, current_pos, executor);
|
|
||||||
if (!maybe_function_definition.has_value()) {
|
|
||||||
return Result::invalid();
|
|
||||||
}
|
|
||||||
const nodes::FunctionDefinition *function_definition =
|
|
||||||
maybe_function_definition.value();
|
|
||||||
|
|
||||||
// TODO: count passed type, if needed
|
|
||||||
// TODO: manage situation with one out type at any position
|
|
||||||
// TODO + 1 - returned type - somtimes (can be ==)
|
|
||||||
const auto args_given = expr.args.size();
|
|
||||||
const auto args_defined = function_definition->args.size();
|
|
||||||
if (args_given + 1 < args_defined ||
|
|
||||||
args_given > args_defined) { // other, when there is passed type
|
|
||||||
TypeCheckError(std::format("Number of function args is different from "
|
|
||||||
"expected ({} instead of {}{})",
|
|
||||||
args_given, args_defined,
|
|
||||||
args_defined > 0
|
|
||||||
? (" or " + std::to_string(args_defined - 1))
|
|
||||||
: ""),
|
|
||||||
current_pos);
|
|
||||||
return Result::invalid();
|
|
||||||
// TODO: try return correct type (function return type), when possible
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: define types for generic function
|
|
||||||
|
|
||||||
std::vector<Result> function_argument_results;
|
|
||||||
for (size_t i = 0; i < args_given;
|
|
||||||
++i) { // TODO: pass types with oud modifier
|
|
||||||
const nodes::FunctionDefinition::Argument &argument =
|
|
||||||
function_definition->args.at(i);
|
|
||||||
|
|
||||||
if (!argument.type.has_value()) {
|
|
||||||
TypeCheckError(
|
|
||||||
std::format("Function argument type is not defined for argument {}",
|
|
||||||
i),
|
|
||||||
current_pos);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto annotation = expr.args.at(i).first;
|
|
||||||
const auto expected_annotation = argument.annotation;
|
|
||||||
|
|
||||||
if (annotation.has_value() != expected_annotation.has_value()) {
|
|
||||||
TypeCheckError("Wrong function argument annotation: should be " +
|
|
||||||
std::string{expected_annotation.has_value()
|
|
||||||
? expected_annotation.value()
|
|
||||||
: "[none]"},
|
|
||||||
PosOf(expr.args.at(i).second));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (annotation.has_value() &&
|
|
||||||
annotation.value() != expected_annotation.value()) {
|
|
||||||
TypeCheckError(
|
|
||||||
std::format(
|
|
||||||
"Wrong function argument type annotation: {} instead of {}",
|
|
||||||
annotation.value(), expected_annotation.value()),
|
|
||||||
PosOf(expr.args.at(i).second));
|
|
||||||
}
|
|
||||||
|
|
||||||
function_argument_results.push_back(
|
|
||||||
Run(expr.args.at(i).second,
|
|
||||||
Args{expr.args.at(i).second}.Expect(argument.type.value())));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (function_definition->args.size() == 0) {
|
|
||||||
TypeCheckError(
|
|
||||||
"Function arguments size is zero. Returned type is not defined",
|
|
||||||
current_pos);
|
|
||||||
return Result::invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: check condition
|
|
||||||
if (args_given + 1 == args_defined) {
|
|
||||||
// returned type
|
|
||||||
const nodes::FunctionDefinition::Argument &returned =
|
|
||||||
function_definition->args.back();
|
|
||||||
|
|
||||||
// TODO: invert modifier ??
|
|
||||||
if (!returned.type.has_value()) {
|
|
||||||
TypeCheckError("Function argument type is not defined for returned type",
|
|
||||||
current_pos);
|
|
||||||
return Result::invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: invert modifier ??
|
|
||||||
// TODO: generic types should be deduced from args
|
|
||||||
return TypeCheckFromArgs(returned.type.value(), args, current_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// checks for universal call syntax ??
|
|
||||||
|
|
||||||
// TODO: return result
|
|
||||||
} // IN PROGRESS
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
Result CheckTask<nodes::Constructor>::operator()(const nodes::Constructor &expr,
|
|
||||||
const Args &args) {
|
|
||||||
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
|
||||||
|
|
||||||
const auto current_pos = PosOf(args.current_id);
|
|
||||||
|
|
||||||
// TODO: constraints ??
|
|
||||||
|
|
||||||
// TODO: use pass type
|
|
||||||
const auto maybe_type_definition = FindTypeDefinition(
|
|
||||||
expr.type.get()->get_name()->value, current_pos, executor);
|
|
||||||
if (!maybe_type_definition.has_value()) {
|
|
||||||
return Result::invalid();
|
|
||||||
}
|
|
||||||
const nodes::TypeDefinition *type_definition = maybe_type_definition.value();
|
|
||||||
|
|
||||||
if (!type_definition->type.has_value()) {
|
|
||||||
TypeCheckError(
|
|
||||||
"Type defenition for constructor type not found (declaration only)",
|
|
||||||
current_pos);
|
|
||||||
return Result::invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: deal with anniotations, recursive annotations
|
|
||||||
|
|
||||||
// TODO: check that is not typeclass ??
|
|
||||||
|
|
||||||
nodes::Type type = type_definition->type.value();
|
|
||||||
|
|
||||||
// TODO: work with different parametric types: tuple, variant, ...
|
|
||||||
|
|
||||||
if (expr.args.size() == 0) {
|
|
||||||
TypeCheckError("Number of type constructor arguments should be > 0",
|
|
||||||
current_pos);
|
|
||||||
return Result::invalid();
|
|
||||||
// TODO: try return correct type (constructor's type), when possible (not
|
|
||||||
// generic)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: work with generics (type_definition->args, ...)
|
|
||||||
|
|
||||||
const auto builtin_type = type.get()->to_builtin();
|
|
||||||
|
|
||||||
{ // check args size, ets.
|
|
||||||
switch (builtin_type) {
|
|
||||||
case builtin::Type::TUPLE:
|
|
||||||
if (expr.args.size() != type.get()->parameters_size()) {
|
|
||||||
TypeCheckError(
|
|
||||||
"Number of type constructor arguments is different from expected "
|
|
||||||
"(" +
|
|
||||||
std::to_string(expr.args.size()) + " instead of " +
|
|
||||||
std::to_string(type.get()->parameters_size()) + ")",
|
|
||||||
current_pos);
|
|
||||||
return Result::invalid();
|
|
||||||
// TODO: try return correct type (constructor's type), when possible
|
|
||||||
// (not generic)
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case builtin::Type::VARIANT:
|
|
||||||
case builtin::Type::OPTIONAL:
|
|
||||||
case builtin::Type::RESULT:
|
|
||||||
case builtin::Type::ERROR:
|
|
||||||
case builtin::Type::FUNCTION:
|
|
||||||
case builtin::Type::NONE:
|
|
||||||
if (expr.args.size() != 1) { // TODO: better to_string
|
|
||||||
TypeCheckError(std::format("Number of type constructor arguments "
|
|
||||||
"should be = 1 (builtin type {})",
|
|
||||||
uint(builtin_type)),
|
|
||||||
current_pos);
|
|
||||||
return Result::invalid();
|
|
||||||
// TODO: try return correct type (constructor's type), when possible
|
|
||||||
// (not generic)
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default: // array, basic types
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (builtin::types::get_parameters_count(builtin_type).has_value() &&
|
|
||||||
type.get()->parameters_size() !=
|
|
||||||
builtin::types::get_parameters_count(builtin_type).value()) {
|
|
||||||
TypeCheckError("Wrong amount of parametars for builtin type",
|
|
||||||
current_pos);
|
|
||||||
|
|
||||||
return Result::invalid();
|
|
||||||
// TODO: try return correct type (constructor's type), when possible (not
|
|
||||||
// generic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<size_t> chosen_variant_option; // for VARIANT
|
|
||||||
|
|
||||||
{ // check annotations
|
|
||||||
const auto check_same_annotation =
|
|
||||||
[&expr, this](size_t i,
|
|
||||||
std::optional<const std::string *> expected_annotation,
|
|
||||||
bool log_errors) {
|
|
||||||
bool is_same = true;
|
|
||||||
|
|
||||||
const auto annotation = expr.args.at(i).first;
|
|
||||||
|
|
||||||
if (annotation.has_value() != expected_annotation.has_value()) {
|
|
||||||
if (log_errors) {
|
|
||||||
TypeCheckError(
|
|
||||||
"Wrong type constructor argument annotation: should be " +
|
|
||||||
std::string{expected_annotation.has_value()
|
|
||||||
? *expected_annotation.value()
|
|
||||||
: "[none]"},
|
|
||||||
PosOf(expr.args.at(i).second));
|
|
||||||
}
|
|
||||||
is_same = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (annotation.has_value() &&
|
|
||||||
annotation.value() != *expected_annotation.value()) {
|
|
||||||
if (log_errors) {
|
|
||||||
TypeCheckError("Wrong function argument type annotation: " +
|
|
||||||
annotation.value() + " instead of " +
|
|
||||||
*expected_annotation.value(),
|
|
||||||
PosOf(expr.args.at(i).second));
|
|
||||||
}
|
|
||||||
|
|
||||||
is_same = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return is_same;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto check_no_annotation = [&expr, this](size_t i, bool log_errors) {
|
|
||||||
if (expr.args.at(i).first.has_value()) {
|
|
||||||
if (log_errors) {
|
|
||||||
TypeCheckError(
|
|
||||||
"Type constructor argument annotation not expected there",
|
|
||||||
PosOf(expr.args.at(i).second));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (builtin_type) {
|
|
||||||
case builtin::Type::TUPLE:
|
|
||||||
for (size_t i = 0; i < expr.args.size(); ++i) {
|
|
||||||
check_same_annotation(i, type.get()->get_parameter(i)->get_annotation(),
|
|
||||||
true /*log errors*/);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case builtin::Type::VARIANT:
|
|
||||||
// more then one same annotation ??
|
|
||||||
for (size_t i = 0; i < type.get()->parameters_size(); ++i) {
|
|
||||||
if (check_same_annotation(
|
|
||||||
0, type.get()->get_parameter(i)->get_annotation(),
|
|
||||||
false /*do not log errors*/)) {
|
|
||||||
chosen_variant_option = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chosen_variant_option.has_value() &&
|
|
||||||
!check_no_annotation(0, false /*do not log errors*/)) {
|
|
||||||
TypeCheckError("Wrong type constructor argument annotation in "
|
|
||||||
"constructor of variant type",
|
|
||||||
PosOf(expr.args.front().second));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case builtin::Type::ERROR: // no anotations ??
|
|
||||||
check_same_annotation(0, type.get()->get_parameter(0)->get_annotation(),
|
|
||||||
true /*log errors*/);
|
|
||||||
break;
|
|
||||||
case builtin::Type::OPTIONAL:
|
|
||||||
case builtin::Type::RESULT:
|
|
||||||
case builtin::Type::FUNCTION:
|
|
||||||
case builtin::Type::NONE:
|
|
||||||
check_no_annotation(0, true /*log errors*/);
|
|
||||||
break;
|
|
||||||
default: // array, basic types
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // type check args
|
|
||||||
switch (builtin_type) {
|
|
||||||
case builtin::Type::TUPLE:
|
|
||||||
for (size_t i = 0; i < expr.args.size(); ++i) {
|
|
||||||
Run(expr.args.at(i).second, Args{expr.args.at(i).second}.Expect(
|
|
||||||
type.get()->get_parameter_proxy(i)));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case builtin::Type::VARIANT:
|
|
||||||
if (chosen_variant_option.has_value()) {
|
|
||||||
Run(expr.args.front().second, Args{expr.args.front().second}.Expect(
|
|
||||||
type.get()->get_parameter_proxy(
|
|
||||||
chosen_variant_option.value())));
|
|
||||||
} else { // TODO: error, if there is more then one possible variant in
|
|
||||||
// answer
|
|
||||||
nodes::Types possible_options;
|
|
||||||
for (size_t i = 0; i < type.get()->parameters_size(); ++i) {
|
|
||||||
possible_options.push_back(type.get()->get_parameter_proxy(i));
|
|
||||||
}
|
|
||||||
Run(expr.args.front().second,
|
|
||||||
Args{expr.args.front().second}.Expect(possible_options));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case builtin::Type::OPTIONAL:
|
|
||||||
// first parameter or NULL
|
|
||||||
Run(expr.args.front().second,
|
|
||||||
Args{expr.args.front().second}.Expect(
|
|
||||||
{type.get()->get_parameter_proxy(0),
|
|
||||||
state<Types>().primitive(builtin::Type::NULL_OPTION)}));
|
|
||||||
break;
|
|
||||||
case builtin::Type::RESULT:
|
|
||||||
// first parameter or ERROR[second parameter]
|
|
||||||
Run(expr.args.front().second,
|
|
||||||
Args{expr.args.front().second}.Expect(
|
|
||||||
{type.get()->get_parameter_proxy(0),
|
|
||||||
state<Types>().add_error_of(
|
|
||||||
type.get()->get_parameter_proxy(1))}));
|
|
||||||
break;
|
|
||||||
case builtin::Type::ERROR:
|
|
||||||
// first parameter
|
|
||||||
Run(expr.args.front().second, Args{expr.args.front().second}.Expect(
|
|
||||||
type.get()->get_parameter_proxy(0)));
|
|
||||||
break;
|
|
||||||
case builtin::Type::FUNCTION:
|
|
||||||
case builtin::Type::NONE:
|
|
||||||
// type itself
|
|
||||||
Run(expr.args.front().second,
|
|
||||||
Args{expr.args.front().second}.Expect(type));
|
|
||||||
break;
|
|
||||||
default: // array, basic types
|
|
||||||
TypeCheckError("Type can't be constructed", current_pos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: deduce generic parts in type
|
|
||||||
return TypeCheckFromArgs(expr.type, args, current_pos);
|
|
||||||
// TODO: add <- modifiier to type ??
|
|
||||||
|
|
||||||
} // IN PROGRESS
|
|
||||||
|
|
||||||
// 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) {
|
||||||
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
||||||
|
|
||||||
const auto current_pos = PosOf(args.current_id);
|
const auto current_pos = PosOf(args.current_id);
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
// TODO: maybe expected type is not always required (type deduction) ??
|
||||||
if (args.expected().empty()) {
|
if (args.expected().empty()) {
|
||||||
TypeCheckError("Can't deduce type of lambda function from context: no "
|
TypeCheckError("Can't deduce type of lambda function from context: no "
|
||||||
"one type expected",
|
"type is expected",
|
||||||
current_pos);
|
current_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.expected().size() !=
|
if (args.expected().size() > 1) { // TODO: check if only one function argument
|
||||||
1) { // TODO: check if only one function argument
|
TypeCheckError("Can't deduce type of lambda function from context: too "
|
||||||
TypeCheckError("Can't deduce type of lambda function from context; too "
|
|
||||||
"much possible types",
|
"much possible types",
|
||||||
current_pos);
|
current_pos);
|
||||||
}
|
}
|
||||||
|
|
@ -902,37 +514,61 @@ Result CheckTask<nodes::Lambda>::operator()(const nodes::Lambda &expr,
|
||||||
TypeCheckError("Type of lambda function should be function", current_pos);
|
TypeCheckError("Type of lambda function should be function", current_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- maybe use new type and then unify instead ?? ---
|
||||||
|
|
||||||
// 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) {
|
||||||
TypeCheckError(std::format("Number of function arguments is different from "
|
// TypeCheckError(std::format("Number of function arguments is different
|
||||||
"expected ({} istead of {}{})",
|
// from "
|
||||||
args_given, args_defined,
|
// "expected ({} istead of {}{})",
|
||||||
args_defined > 0
|
// args_given, args_defined,
|
||||||
? (" or " + std::to_string(args_defined - 1))
|
// args_defined > 0
|
||||||
: ""),
|
// ? (" or " + std::to_string(args_defined -
|
||||||
current_pos);
|
// 1)) : ""),
|
||||||
}
|
// current_pos);
|
||||||
|
// }
|
||||||
|
|
||||||
// TODO: set another context (for expression typecheck and vars)
|
// TODO: set another context (for expression typecheck and vars)
|
||||||
|
nodes::MaybeType context_exit_type;
|
||||||
|
|
||||||
for (size_t i = 0; i < args_given; ++i) {
|
{
|
||||||
if (!state<State>().InsertVariable(
|
ContextHolder<nodes::Return::Kind::RETURN> context_holder(
|
||||||
expr.args.at(i).value, expected_type.get()->get_parameter_proxy(i),
|
state<State>(), current_pos, &context_exit_type);
|
||||||
nodes::NameDefinition::Kind::LET)) {
|
|
||||||
// TODO: which modifier ??
|
for (size_t i = 0; i < expr.args.size(); ++i) {
|
||||||
TypeCheckError("Variable is already defined in this context",
|
if (not state<State>().InsertVariable(expr.args.at(i).value,
|
||||||
current_pos);
|
state<Types>().add_generic_type(),
|
||||||
|
nodes::NameDefinition::Kind::LET)) {
|
||||||
|
// TODO: choose LET / CONST
|
||||||
|
TypeCheckError("Variable is already defined in this context",
|
||||||
|
current_pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: replace with defined args ??
|
||||||
|
// for (size_t i = 0; i < args_given; ++i) {
|
||||||
|
// if (not state<State>().InsertVariable(
|
||||||
|
// expr.args.at(i).value,
|
||||||
|
// expected_type.get()->get_parameter_proxy(i),
|
||||||
|
// nodes::NameDefinition::Kind::LET)) {
|
||||||
|
// // TODO: choose LET / CONST
|
||||||
|
// TypeCheckError("Variable is already defined in this context",
|
||||||
|
// current_pos);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO: run args exprs
|
||||||
|
// TODO: run lambda expr itself, get returned type
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.expr,
|
// Run(expr.expr,
|
||||||
Args{expr.expr}.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 TypeCheckFromArgs(expected_type, args, current_pos);
|
return TypeCheckFromArgs(expected_type, args, current_pos);
|
||||||
|
|
|
||||||
174
lang/type_check/src/name_expression_type_check.cpp
Normal file
174
lang/type_check/src/name_expression_type_check.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
#include "name_expression_type_check.hpp"
|
||||||
|
|
||||||
|
#include "expression_type_check.hpp"
|
||||||
|
|
||||||
|
namespace type_check {
|
||||||
|
|
||||||
|
// TODO: add constraints evaluation
|
||||||
|
Result
|
||||||
|
CheckTask<nodes::NameExpression>::operator()(const nodes::NameExpression &expr,
|
||||||
|
const Args &args) {
|
||||||
|
Log::Context logc(executor.log(), Log::Area::kTypeCheck);
|
||||||
|
const auto current_pos = PosOf(args.current_id);
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
const auto name = expr.name;
|
||||||
|
|
||||||
|
{ // find variable name equal to expression name prefix
|
||||||
|
const auto fragments = name.get_fragments();
|
||||||
|
nodes::Identifier current_prefix = fragments.front();
|
||||||
|
std::optional<State::VariableInfo> maybe_variable;
|
||||||
|
size_t i = 0;
|
||||||
|
for (; i < fragments.size(); ++i) {
|
||||||
|
// go in prefixes and try to find matching var namei
|
||||||
|
if (i > 0) {
|
||||||
|
current_prefix.append_after(fragments[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
maybe_variable = state<State>().FindVariable(current_prefix.value);
|
||||||
|
|
||||||
|
if (maybe_variable.has_value()) {
|
||||||
|
// possible variable name found
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
if (maybe_variable.has_value()) {
|
||||||
|
auto &type = maybe_variable.value().type;
|
||||||
|
for (size_t j = i + 1; j < fragments.size(); ++i) {
|
||||||
|
if (j + 1 == fragments.size()) {
|
||||||
|
// then this can be method call
|
||||||
|
// TODO: try to find method or local function
|
||||||
|
// if found, search to field (without error handling)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: fields from several fragments ? (not acceptable fors methods /
|
||||||
|
// functions)
|
||||||
|
|
||||||
|
// <- in typecheck utils method
|
||||||
|
// TODO: switch by type types: Variant, Tuple, ...
|
||||||
|
// Tuple -> try to find field
|
||||||
|
// Others -> try to open / builtin fields ?
|
||||||
|
const auto maybe_field_type =
|
||||||
|
FieldTypeByName(type, fragments[i].value, current_pos, executor);
|
||||||
|
|
||||||
|
if (maybe_field_type.has_value()) {
|
||||||
|
type = maybe_field_type.value();
|
||||||
|
// TODO
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// fields for the names should be found
|
||||||
|
// last segment can be function name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- TODO --- deal with passed type --- TODO --- (additional argument)
|
||||||
|
// TODO: deal with given ->(out) Args (type not expected, but passed into)
|
||||||
|
|
||||||
|
// TODO: check, if there is variable with this name
|
||||||
|
// TODO: check var + fields
|
||||||
|
const auto maybe_function_definition =
|
||||||
|
FindNameDefinition(name.value, current_pos, executor);
|
||||||
|
if (!maybe_function_definition.has_value()) {
|
||||||
|
return Result::invalid();
|
||||||
|
}
|
||||||
|
const nodes::FunctionDefinition *function_definition =
|
||||||
|
maybe_function_definition.value();
|
||||||
|
|
||||||
|
// TODO: count passed type, if needed
|
||||||
|
// TODO: manage situation with one out type at any position
|
||||||
|
// TODO + 1 - returned type - somtimes (can be ==)
|
||||||
|
const auto args_given = expr.args.size();
|
||||||
|
const auto args_defined = function_definition->args.size();
|
||||||
|
if (args_given + 1 < args_defined ||
|
||||||
|
args_given > args_defined) { // other, when there is passed type
|
||||||
|
TypeCheckError(std::format("Number of function args is different from "
|
||||||
|
"expected ({} instead of {}{})",
|
||||||
|
args_given, args_defined,
|
||||||
|
args_defined > 0
|
||||||
|
? (" or " + std::to_string(args_defined - 1))
|
||||||
|
: ""),
|
||||||
|
current_pos);
|
||||||
|
return Result::invalid();
|
||||||
|
// TODO: try return correct type (function return type), when possible
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: define types for generic function
|
||||||
|
|
||||||
|
std::vector<Result> function_argument_results;
|
||||||
|
for (size_t i = 0; i < args_given;
|
||||||
|
++i) { // TODO: pass types with oud modifier
|
||||||
|
const nodes::FunctionDefinition::Argument &argument =
|
||||||
|
function_definition->args.at(i);
|
||||||
|
|
||||||
|
if (!argument.type.has_value()) {
|
||||||
|
TypeCheckError(
|
||||||
|
std::format("Function argument type is not defined for argument {}",
|
||||||
|
i),
|
||||||
|
current_pos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto annotation = expr.args.at(i).first;
|
||||||
|
const auto expected_annotation = argument.annotation;
|
||||||
|
|
||||||
|
if (annotation.has_value() != expected_annotation.has_value()) {
|
||||||
|
TypeCheckError("Wrong function argument annotation: should be " +
|
||||||
|
std::string{expected_annotation.has_value()
|
||||||
|
? expected_annotation.value()
|
||||||
|
: "[none]"},
|
||||||
|
PosOf(expr.args.at(i).second));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (annotation.has_value() &&
|
||||||
|
annotation.value() != expected_annotation.value()) {
|
||||||
|
TypeCheckError(
|
||||||
|
std::format(
|
||||||
|
"Wrong function argument type annotation: {} instead of {}",
|
||||||
|
annotation.value(), expected_annotation.value()),
|
||||||
|
PosOf(expr.args.at(i).second));
|
||||||
|
}
|
||||||
|
|
||||||
|
function_argument_results.push_back(
|
||||||
|
Run(expr.args.at(i).second,
|
||||||
|
Args{expr.args.at(i).second}.Expect(argument.type.value())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function_definition->args.size() == 0) {
|
||||||
|
TypeCheckError(
|
||||||
|
"Function arguments size is zero. Returned type is not defined",
|
||||||
|
current_pos);
|
||||||
|
return Result::invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check condition
|
||||||
|
if (args_given + 1 == args_defined) {
|
||||||
|
// returned type
|
||||||
|
const nodes::FunctionDefinition::Argument &returned =
|
||||||
|
function_definition->args.back();
|
||||||
|
|
||||||
|
// TODO: invert modifier ??
|
||||||
|
if (!returned.type.has_value()) {
|
||||||
|
TypeCheckError("Function argument type is not defined for returned type",
|
||||||
|
current_pos);
|
||||||
|
return Result::invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: invert modifier ??
|
||||||
|
// TODO: generic types should be deduced from args
|
||||||
|
return TypeCheckFromArgs(returned.type.value(), args, current_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks for universal call syntax ??
|
||||||
|
|
||||||
|
// TODO: return result
|
||||||
|
} // IN PROGRESS
|
||||||
|
|
||||||
|
} // namespace type_check
|
||||||
Loading…
Add table
Add a link
Reference in a new issue