mirror of
https://codeberg.org/ProgramSnail/lang_2023.git
synced 2025-12-06 06:58:45 +00:00
const -> let
This commit is contained in:
parent
3ae6ed079d
commit
189306df26
33 changed files with 406 additions and 285 deletions
|
|
@ -91,6 +91,8 @@ private:
|
||||||
void Visit(TypeConstructorParameter* node) override;
|
void Visit(TypeConstructorParameter* node) override;
|
||||||
void Visit(TypeConstructor* node) override;
|
void Visit(TypeConstructor* node) override;
|
||||||
void Visit(LambdaFunction* node) override;
|
void Visit(LambdaFunction* node) override;
|
||||||
|
void Visit(AndExpression* node) override;
|
||||||
|
void Visit(OrExpression* node) override;
|
||||||
void Visit(ArrayExpression* node) override;
|
void Visit(ArrayExpression* node) override;
|
||||||
|
|
||||||
void Visit(LoopControlExpression& node) override; // enum
|
void Visit(LoopControlExpression& node) override; // enum
|
||||||
|
|
|
||||||
|
|
@ -22,4 +22,12 @@ inline T Read() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void Error(const T& value) { // only for strings ??
|
||||||
|
std::cout << "\x1b[1;33mError:\x1b[0m ";
|
||||||
|
std::cout << value;
|
||||||
|
std::cout << '\n';
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace info
|
} // namespace info
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,8 @@ private:
|
||||||
void Visit(TypeConstructorParameter* node) override;
|
void Visit(TypeConstructorParameter* node) override;
|
||||||
void Visit(TypeConstructor* node) override;
|
void Visit(TypeConstructor* node) override;
|
||||||
void Visit(LambdaFunction* node) override;
|
void Visit(LambdaFunction* node) override;
|
||||||
|
void Visit(AndExpression* node) override;
|
||||||
|
void Visit(OrExpression* node) override;
|
||||||
void Visit(ArrayExpression* node) override;
|
void Visit(ArrayExpression* node) override;
|
||||||
|
|
||||||
void Visit(LoopControlExpression& node) override; // enum
|
void Visit(LoopControlExpression& node) override; // enum
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@ private:
|
||||||
// // void Visit(TypeConstructorParameter* node) override;
|
// // void Visit(TypeConstructorParameter* node) override;
|
||||||
// // void Visit(TypeConstructor* node) override;
|
// // void Visit(TypeConstructor* node) override;
|
||||||
// // void Visit(LambdaFunction* node) override;
|
// // void Visit(LambdaFunction* node) override;
|
||||||
|
// // void Visit(AndExpression* node) override;
|
||||||
|
// // void Visit(OrExpression* node) override;
|
||||||
// // void Visit(ArrayExpression* node) override;
|
// // void Visit(ArrayExpression* node) override;
|
||||||
|
|
||||||
// // void Visit(LoopControlExpression& node) override; // enum
|
// // void Visit(LoopControlExpression& node) override; // enum
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,6 @@ using SubExpressionToken = std::variant<
|
||||||
std::unique_ptr<ReferenceExpression>>;
|
std::unique_ptr<ReferenceExpression>>;
|
||||||
//
|
//
|
||||||
struct FunctionCallExpression;
|
struct FunctionCallExpression;
|
||||||
struct ArrayExpression;
|
|
||||||
using SubExpression = std::variant< // BiaryOperatorExpression is FunctionCallExpression
|
using SubExpression = std::variant< // BiaryOperatorExpression is FunctionCallExpression
|
||||||
std::unique_ptr<FunctionCallExpression>,
|
std::unique_ptr<FunctionCallExpression>,
|
||||||
std::unique_ptr<SubExpressionToken>>;
|
std::unique_ptr<SubExpressionToken>>;
|
||||||
|
|
@ -149,11 +148,17 @@ using PrefixedExpression = std::variant<
|
||||||
//
|
//
|
||||||
struct LambdaFunction;
|
struct LambdaFunction;
|
||||||
struct TypeConstructor;
|
struct TypeConstructor;
|
||||||
|
struct AndExpression;
|
||||||
|
struct OrExpression;
|
||||||
|
struct ArrayExpression;
|
||||||
using Expression = std::variant<
|
using Expression = std::variant<
|
||||||
std::unique_ptr<LambdaFunction>,
|
std::unique_ptr<LambdaFunction>,
|
||||||
std::unique_ptr<TypeConstructor>,
|
std::unique_ptr<TypeConstructor>,
|
||||||
std::unique_ptr<PrefixedExpression>,
|
std::unique_ptr<PrefixedExpression>,
|
||||||
std::unique_ptr<SubExpression>>;
|
std::unique_ptr<SubExpression>,
|
||||||
|
std::unique_ptr<AndExpression>,
|
||||||
|
std::unique_ptr<OrExpression>,
|
||||||
|
std::unique_ptr<ArrayExpression>>;
|
||||||
//
|
//
|
||||||
struct TupleExpression;
|
struct TupleExpression;
|
||||||
struct VariantExpression;
|
struct VariantExpression;
|
||||||
|
|
@ -161,7 +166,6 @@ using SuperExpression = std::variant<
|
||||||
std::unique_ptr<FlowControl>,
|
std::unique_ptr<FlowControl>,
|
||||||
std::unique_ptr<TupleExpression>,
|
std::unique_ptr<TupleExpression>,
|
||||||
std::unique_ptr<VariantExpression>,
|
std::unique_ptr<VariantExpression>,
|
||||||
std::unique_ptr<ArrayExpression>,
|
|
||||||
std::unique_ptr<Expression>>;
|
std::unique_ptr<Expression>>;
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
@ -181,6 +185,8 @@ struct ReturnExpression;
|
||||||
struct TypeConstructorParameter;
|
struct TypeConstructorParameter;
|
||||||
struct TypeConstructor;
|
struct TypeConstructor;
|
||||||
struct LambdaFunction;
|
struct LambdaFunction;
|
||||||
|
struct AndExpression;
|
||||||
|
struct OrExpression;
|
||||||
struct ArrayExpression;
|
struct ArrayExpression;
|
||||||
|
|
||||||
// Name
|
// Name
|
||||||
|
|
@ -525,7 +531,7 @@ struct FunctionCallExpression {
|
||||||
std::unique_ptr<WrappedTypeExpression>>> prefix;
|
std::unique_ptr<WrappedTypeExpression>>> prefix;
|
||||||
NameOrOperatorIdentifier name;
|
NameOrOperatorIdentifier name;
|
||||||
std::vector<std::unique_ptr<TypeExpression>> parameters;
|
std::vector<std::unique_ptr<TypeExpression>> parameters;
|
||||||
std::vector<SubExpression> arguments;
|
std::vector<std::pair<std::optional<NameIdentifier>, SubExpression>> arguments;
|
||||||
|
|
||||||
std::optional<size_t> precedence; // for operators
|
std::optional<size_t> precedence; // for operators
|
||||||
bool is_binary_operator_expression = false; // for operators
|
bool is_binary_operator_expression = false; // for operators
|
||||||
|
|
@ -587,6 +593,18 @@ struct LambdaFunction {
|
||||||
utils::IdType return_type_graph_id_ = 0;
|
utils::IdType return_type_graph_id_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AndExpression {
|
||||||
|
BaseNode base;
|
||||||
|
|
||||||
|
std::vector<SubExpression> expressions;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrExpression {
|
||||||
|
BaseNode base;
|
||||||
|
|
||||||
|
std::vector<SubExpression> expressions;
|
||||||
|
};
|
||||||
|
|
||||||
struct ArrayExpression {
|
struct ArrayExpression {
|
||||||
BaseNode base;
|
BaseNode base;
|
||||||
|
|
||||||
|
|
@ -632,7 +650,7 @@ struct Name {
|
||||||
struct FunctionType {
|
struct FunctionType {
|
||||||
BaseNode base;
|
BaseNode base;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<ExtendedScopedAnyType>> types;
|
std::vector<std::pair<std::optional<NameIdentifier>, std::unique_ptr<ExtendedScopedAnyType>>> types;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TupleType {
|
struct TupleType {
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,8 @@ private:
|
||||||
// // void Visit(TypeConstructorParameter* node) override;
|
// // void Visit(TypeConstructorParameter* node) override;
|
||||||
// // void Visit(TypeConstructor* node) override;
|
// // void Visit(TypeConstructor* node) override;
|
||||||
// // void Visit(LambdaFunction* node) override;
|
// // void Visit(LambdaFunction* node) override;
|
||||||
|
// // void Visit(AndExpression* node) override;
|
||||||
|
// // void Visit(OrExpression* node) override;
|
||||||
// // void Visit(ArrayExpression* node) override;
|
// // void Visit(ArrayExpression* node) override;
|
||||||
|
|
||||||
// // void Visit(LoopControlExpression& node) override; // enum
|
// // void Visit(LoopControlExpression& node) override; // enum
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,8 @@ const std::string ReturnExpression = "return_expression";
|
||||||
const std::string TypeConstructorParameter = "type_constructor_parameter";
|
const std::string TypeConstructorParameter = "type_constructor_parameter";
|
||||||
const std::string TypeConstructor = "type_constructor";
|
const std::string TypeConstructor = "type_constructor";
|
||||||
const std::string LambdaFunction = "lambda_function";
|
const std::string LambdaFunction = "lambda_function";
|
||||||
|
const std::string AndExpression = "and_expression";
|
||||||
|
const std::string OrExpression = "or_expression";
|
||||||
const std::string ArrayExpression = "array_expression";
|
const std::string ArrayExpression = "array_expression";
|
||||||
const std::string LoopControlExpression = "loop_control_expression";
|
const std::string LoopControlExpression = "loop_control_expression";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ private:
|
||||||
void Visit(TypeConstructorParameter* node) override;
|
void Visit(TypeConstructorParameter* node) override;
|
||||||
void Visit(TypeConstructor* node) override;
|
void Visit(TypeConstructor* node) override;
|
||||||
void Visit(LambdaFunction* node) override;
|
void Visit(LambdaFunction* node) override;
|
||||||
|
void Visit(AndExpression* node) override;
|
||||||
|
void Visit(OrExpression* node) override;
|
||||||
void Visit(ArrayExpression* node) override;
|
void Visit(ArrayExpression* node) override;
|
||||||
|
|
||||||
void Visit(LoopControlExpression& node) override; // enum
|
void Visit(LoopControlExpression& node) override; // enum
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,8 @@ private:
|
||||||
void Visit(TypeConstructorParameter* node) override;
|
void Visit(TypeConstructorParameter* node) override;
|
||||||
void Visit(TypeConstructor* node) override;
|
void Visit(TypeConstructor* node) override;
|
||||||
void Visit(LambdaFunction* node) override;
|
void Visit(LambdaFunction* node) override;
|
||||||
|
void Visit(AndExpression* node) override;
|
||||||
|
void Visit(OrExpression* node) override;
|
||||||
void Visit(ArrayExpression* node) override;
|
void Visit(ArrayExpression* node) override;
|
||||||
|
|
||||||
void Visit(LoopControlExpression& node) override; // enum
|
void Visit(LoopControlExpression& node) override; // enum
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@ private:
|
||||||
void Visit(TypeConstructorParameter* node) override;
|
void Visit(TypeConstructorParameter* node) override;
|
||||||
void Visit(TypeConstructor* node) override;
|
void Visit(TypeConstructor* node) override;
|
||||||
void Visit(LambdaFunction* node) override;
|
void Visit(LambdaFunction* node) override;
|
||||||
|
void Visit(AndExpression* node) override;
|
||||||
|
void Visit(OrExpression* node) override;
|
||||||
void Visit(ArrayExpression* node) override;
|
void Visit(ArrayExpression* node) override;
|
||||||
|
|
||||||
void Visit(LoopControlExpression& node) override; // enum
|
void Visit(LoopControlExpression& node) override; // enum
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ enum class ClassModifier { Struct = 0, Class = 1 };
|
||||||
enum class AssignmentModifier { Assign = 0, Move = 1 };
|
enum class AssignmentModifier { Assign = 0, Move = 1 };
|
||||||
enum class AliasModifier { Alias = 0, Type = 1, Let = 2 };
|
enum class AliasModifier { Alias = 0, Type = 1, Let = 2 };
|
||||||
enum class AbstractTypeModifier { Basic = 0, Abstract = 1 };
|
enum class AbstractTypeModifier { Basic = 0, Abstract = 1 };
|
||||||
enum class PartitionModifier { Exec = 0, Test = 1 };
|
enum class PartitionModifier { Exec = 0, Test = 1, Example = 2 };
|
||||||
|
|
||||||
enum class ValueType { Const = 0, Var = 1, Tmp = 2 };
|
enum class ValueType { Const = 0, Var = 1, Tmp = 2 };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ protected:
|
||||||
virtual void Visit(TypeConstructorParameter* node);
|
virtual void Visit(TypeConstructorParameter* node);
|
||||||
virtual void Visit(TypeConstructor* node);
|
virtual void Visit(TypeConstructor* node);
|
||||||
virtual void Visit(LambdaFunction* node);
|
virtual void Visit(LambdaFunction* node);
|
||||||
|
virtual void Visit(AndExpression* node);
|
||||||
|
virtual void Visit(OrExpression* node);
|
||||||
virtual void Visit(ArrayExpression* node);
|
virtual void Visit(ArrayExpression* node);
|
||||||
|
|
||||||
virtual void Visit(LoopControlExpression& node); // enum
|
virtual void Visit(LoopControlExpression& node); // enum
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 25f8750ab2a99222f61b4702419f8b4533ed8cf4
|
Subproject commit 8e69e96520290fab76a98e620d58a4478e9d0861
|
||||||
|
|
@ -315,6 +315,8 @@ void BuildVisitor::Visit(PartitionStatement* node) {
|
||||||
node->modifier = utils::PartitionModifier::Exec;
|
node->modifier = utils::PartitionModifier::Exec;
|
||||||
} else if (partition_modifier == "test") {
|
} else if (partition_modifier == "test") {
|
||||||
node->modifier = utils::PartitionModifier::Test;
|
node->modifier = utils::PartitionModifier::Test;
|
||||||
|
} else if (partition_modifier == "example") {
|
||||||
|
node->modifier = utils::PartitionModifier::Example;
|
||||||
} else {
|
} else {
|
||||||
// error
|
// error
|
||||||
}
|
}
|
||||||
|
|
@ -826,6 +828,15 @@ void BuildVisitor::Visit(Expression& node) {
|
||||||
} else if (current_node_type == parser::tokens::SubExpression) {
|
} else if (current_node_type == parser::tokens::SubExpression) {
|
||||||
node = std::make_unique<SubExpression>();
|
node = std::make_unique<SubExpression>();
|
||||||
Visit(*std::get<std::unique_ptr<SubExpression>>(node));
|
Visit(*std::get<std::unique_ptr<SubExpression>>(node));
|
||||||
|
} else if (current_node_type == parser::tokens::AndExpression) {
|
||||||
|
node = std::make_unique<AndExpression>();
|
||||||
|
Visit(std::get<std::unique_ptr<AndExpression>>(node).get());
|
||||||
|
} else if (current_node_type == parser::tokens::OrExpression) {
|
||||||
|
node = std::make_unique<OrExpression>();
|
||||||
|
Visit(std::get<std::unique_ptr<OrExpression>>(node).get());
|
||||||
|
} else if (current_node_type == parser::tokens::ArrayExpression) {
|
||||||
|
node = std::make_unique<ArrayExpression>();
|
||||||
|
Visit(std::get<std::unique_ptr<ArrayExpression>>(node).get());
|
||||||
} else {
|
} else {
|
||||||
// error
|
// error
|
||||||
}
|
}
|
||||||
|
|
@ -849,9 +860,6 @@ void BuildVisitor::Visit(SuperExpression& node) {
|
||||||
} else if (current_node_type == parser::tokens::VariantExpression) {
|
} else if (current_node_type == parser::tokens::VariantExpression) {
|
||||||
node = std::make_unique<VariantExpression>();
|
node = std::make_unique<VariantExpression>();
|
||||||
Visit(std::get<std::unique_ptr<VariantExpression>>(node).get());
|
Visit(std::get<std::unique_ptr<VariantExpression>>(node).get());
|
||||||
} else if (current_node_type == parser::tokens::ArrayExpression) {
|
|
||||||
node = std::make_unique<ArrayExpression>();
|
|
||||||
Visit(std::get<std::unique_ptr<ArrayExpression>>(node).get());
|
|
||||||
} else if (current_node_type == parser::tokens::Expression) {
|
} else if (current_node_type == parser::tokens::Expression) {
|
||||||
node = std::make_unique<Expression>();
|
node = std::make_unique<Expression>();
|
||||||
Visit(*std::get<std::unique_ptr<Expression>>(node));
|
Visit(*std::get<std::unique_ptr<Expression>>(node));
|
||||||
|
|
@ -884,7 +892,7 @@ void BuildVisitor::VisitBinaryOperatorExpression(FunctionCallExpression* node) {
|
||||||
node->arguments.resize(2);
|
node->arguments.resize(2);
|
||||||
|
|
||||||
current_node_ = parse_node.ChildByFieldName("left_expression");
|
current_node_ = parse_node.ChildByFieldName("left_expression");
|
||||||
Visit(node->arguments[0]);
|
Visit(node->arguments[0].second);
|
||||||
|
|
||||||
node->name = parse_node.ChildByFieldName("operator_name").GetValue();
|
node->name = parse_node.ChildByFieldName("operator_name").GetValue();
|
||||||
|
|
||||||
|
|
@ -896,15 +904,16 @@ void BuildVisitor::VisitBinaryOperatorExpression(FunctionCallExpression* node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
current_node_ = parse_node.ChildByFieldName("right_expression");
|
current_node_ = parse_node.ChildByFieldName("right_expression");
|
||||||
Visit(node->arguments[1]);
|
Visit(node->arguments[1].second);
|
||||||
|
|
||||||
// ??
|
// ??
|
||||||
for (size_t i = 0; i < node->arguments.size(); ++i) {
|
for (size_t i = 0; i < node->arguments.size(); ++i) {
|
||||||
if (std::holds_alternative<std::unique_ptr<FunctionCallExpression>>(node->arguments[i])) {
|
if (std::holds_alternative<std::unique_ptr<FunctionCallExpression>>(node->arguments[i].second)) {
|
||||||
FunctionCallExpression* argument_node = std::get<std::unique_ptr<FunctionCallExpression>>(node->arguments[i]).get();
|
FunctionCallExpression* argument_node = std::get<std::unique_ptr<FunctionCallExpression>>(node->arguments[i].second).get();
|
||||||
if (argument_node->is_binary_operator_expression
|
if (argument_node->is_binary_operator_expression
|
||||||
&& argument_node->precedence.has_value()
|
&& argument_node->precedence.has_value()
|
||||||
&& argument_node->precedence.value() == node->precedence.value()) {
|
&& argument_node->precedence.value() == node->precedence.value()
|
||||||
|
&& node->name != argument_node->name) { // same operators can be chained
|
||||||
error_handling::HandleParsingError("Operators can't be chained (left argument)", node->base.start_position, node->base.end_position);
|
error_handling::HandleParsingError("Operators can't be chained (left argument)", node->base.start_position, node->base.end_position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -986,6 +995,7 @@ void BuildVisitor::Visit(FunctionCallExpression* node) {
|
||||||
|
|
||||||
if (child_count > excluded_child_count) {
|
if (child_count > excluded_child_count) {
|
||||||
bool parameters_ended = false;
|
bool parameters_ended = false;
|
||||||
|
bool last_child_is_annotation = false;
|
||||||
|
|
||||||
for (size_t i = 0; i + excluded_child_count < child_count; ++i) {
|
for (size_t i = 0; i + excluded_child_count < child_count; ++i) {
|
||||||
current_node_ = parse_node.NthNamedChild(i + excluded_child_count);
|
current_node_ = parse_node.NthNamedChild(i + excluded_child_count);
|
||||||
|
|
@ -998,10 +1008,26 @@ void BuildVisitor::Visit(FunctionCallExpression* node) {
|
||||||
node->parameters.push_back(std::make_unique<TypeExpression>());
|
node->parameters.push_back(std::make_unique<TypeExpression>());
|
||||||
Visit(node->parameters.back().get());
|
Visit(node->parameters.back().get());
|
||||||
} else {
|
} else {
|
||||||
node->arguments.push_back(std::make_unique<SubExpressionToken>());
|
if (!current_node_.PreviousSibling().IsNull() && current_node_.PreviousSibling().GetValue() == "::") { // annotation
|
||||||
Visit(*std::get<std::unique_ptr<SubExpressionToken>>(node->arguments.back()));
|
node->arguments.push_back({current_node_.GetValue(), SubExpression()});
|
||||||
|
last_child_is_annotation = true;
|
||||||
|
} else if (last_child_is_annotation) { // argument after annotation
|
||||||
|
node->arguments.back().second = std::make_unique<SubExpressionToken>();
|
||||||
|
Visit(*std::get<std::unique_ptr<SubExpressionToken>>(node->arguments.back().second));
|
||||||
|
last_child_is_annotation = false;
|
||||||
|
} else { // argument without annotation
|
||||||
|
node->arguments.push_back({std::nullopt, std::make_unique<SubExpressionToken>()});
|
||||||
|
Visit(*std::get<std::unique_ptr<SubExpressionToken>>(node->arguments.back().second));
|
||||||
|
last_child_is_annotation = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (last_child_is_annotation) {
|
||||||
|
error_handling::HandleInternalError("Last child is annotation",
|
||||||
|
"BuildVisitor.FunctionCallExpression",
|
||||||
|
&node->base);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
current_node_ = parse_node;
|
current_node_ = parse_node;
|
||||||
|
|
@ -1137,6 +1163,40 @@ void BuildVisitor::Visit(LambdaFunction* node) {
|
||||||
current_node_ = parse_node;
|
current_node_ = parse_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BuildVisitor::Visit(AndExpression* node) {
|
||||||
|
SetPosition(node->base, current_node_);
|
||||||
|
|
||||||
|
auto parse_node = current_node_;
|
||||||
|
|
||||||
|
size_t expressions_count = parse_node.NamedChildCount();
|
||||||
|
|
||||||
|
node->expressions.resize(expressions_count);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < expressions_count; ++i) {
|
||||||
|
current_node_ = parse_node.NthNamedChild(i);
|
||||||
|
Visit(node->expressions[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_node_ = parse_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildVisitor::Visit(OrExpression* node) {
|
||||||
|
SetPosition(node->base, current_node_);
|
||||||
|
|
||||||
|
auto parse_node = current_node_;
|
||||||
|
|
||||||
|
size_t expressions_count = parse_node.NamedChildCount();
|
||||||
|
|
||||||
|
node->expressions.resize(expressions_count);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < expressions_count; ++i) {
|
||||||
|
current_node_ = parse_node.NthNamedChild(i);
|
||||||
|
Visit(node->expressions[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_node_ = parse_node;
|
||||||
|
}
|
||||||
|
|
||||||
void BuildVisitor::Visit(ArrayExpression* node) {
|
void BuildVisitor::Visit(ArrayExpression* node) {
|
||||||
SetPosition(node->base, current_node_);
|
SetPosition(node->base, current_node_);
|
||||||
|
|
||||||
|
|
@ -1296,12 +1356,31 @@ void BuildVisitor::Visit(FunctionType* node) {
|
||||||
|
|
||||||
size_t types_count = parse_node.NamedChildCount();
|
size_t types_count = parse_node.NamedChildCount();
|
||||||
|
|
||||||
node->types.resize(types_count);
|
node->types.reserve(types_count);
|
||||||
|
|
||||||
|
bool last_child_is_annotation = false;
|
||||||
|
|
||||||
for (size_t i = 0; i < types_count; ++i) {
|
for (size_t i = 0; i < types_count; ++i) {
|
||||||
current_node_ = parse_node.NthNamedChild(i);
|
current_node_ = parse_node.NthNamedChild(i);
|
||||||
node->types[i] = std::make_unique<ExtendedScopedAnyType>();
|
|
||||||
Visit(node->types[i].get());
|
if (!current_node_.PreviousSibling().IsNull() && current_node_.PreviousSibling().GetValue() == "::") { // annotation
|
||||||
|
node->types.push_back({current_node_.GetValue(), nullptr});
|
||||||
|
last_child_is_annotation = true;
|
||||||
|
} else if (last_child_is_annotation) { // argument after annotation
|
||||||
|
node->types.back().second = std::make_unique<ExtendedScopedAnyType>();
|
||||||
|
Visit(node->types.back().second.get());
|
||||||
|
last_child_is_annotation = false;
|
||||||
|
} else { // argument without annotation
|
||||||
|
node->types.push_back({std::nullopt, std::make_unique<ExtendedScopedAnyType>()});
|
||||||
|
Visit(node->types.back().second.get());
|
||||||
|
last_child_is_annotation = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_child_is_annotation) {
|
||||||
|
error_handling::HandleInternalError("Last child is annotation",
|
||||||
|
"BuildVisitor.FunctionType",
|
||||||
|
&node->base);
|
||||||
}
|
}
|
||||||
|
|
||||||
current_node_ = parse_node;
|
current_node_ = parse_node;
|
||||||
|
|
|
||||||
|
|
@ -477,7 +477,7 @@ void ExecuteVisitor::Visit(FunctionCallExpression* node) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (node->is_method_of_first_argument_) {
|
if (node->is_method_of_first_argument_) {
|
||||||
Visitor::Visit(node->arguments[0]);
|
Visitor::Visit(node->arguments[0].second);
|
||||||
|
|
||||||
if (context_manager_.GetValueType(current_value_) == utils::ValueType::Tmp) {
|
if (context_manager_.GetValueType(current_value_) == utils::ValueType::Tmp) {
|
||||||
// temporary value can't be modified inside
|
// temporary value can't be modified inside
|
||||||
|
|
@ -598,7 +598,7 @@ void ExecuteVisitor::Visit(FunctionCallExpression* node) {
|
||||||
|
|
||||||
// handle arguments
|
// handle arguments
|
||||||
for (size_t i = index_shift; i < node->arguments.size(); ++i) {
|
for (size_t i = index_shift; i < node->arguments.size(); ++i) {
|
||||||
Visitor::Visit(node->arguments[i]);
|
Visitor::Visit(node->arguments[i].second);
|
||||||
|
|
||||||
// function arguments can't be changed inside function
|
// function arguments can't be changed inside function
|
||||||
current_value_ = context_manager_.ToModifiedValue(current_value_, utils::ValueType::Const);
|
current_value_ = context_manager_.ToModifiedValue(current_value_, utils::ValueType::Const);
|
||||||
|
|
@ -711,6 +711,38 @@ void ExecuteVisitor::Visit(LambdaFunction* node) {
|
||||||
&node->base);
|
&node->base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExecuteVisitor::Visit(AndExpression* node) {
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
for (auto& expression : node->expressions) {
|
||||||
|
Visitor::Visit(expression);
|
||||||
|
if (!*ExtractInternalValue<bool>(current_value_, node->base)) {
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_value_ = context_manager_.AddValue(
|
||||||
|
info::value::InternalValue(result),
|
||||||
|
utils::ValueType::Tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecuteVisitor::Visit(OrExpression* node) {
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
for (auto& expression : node->expressions) {
|
||||||
|
Visitor::Visit(expression);
|
||||||
|
if (*ExtractInternalValue<bool>(current_value_, node->base)) {
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_value_ = context_manager_.AddValue(
|
||||||
|
info::value::InternalValue(result),
|
||||||
|
utils::ValueType::Tmp);
|
||||||
|
}
|
||||||
|
|
||||||
void ExecuteVisitor::Visit(ArrayExpression* node) {
|
void ExecuteVisitor::Visit(ArrayExpression* node) {
|
||||||
std::vector<utils::IdType> elements;
|
std::vector<utils::IdType> elements;
|
||||||
|
|
||||||
|
|
@ -1014,7 +1046,7 @@ void ExecuteVisitor::CheckPattern(Pattern& node, const BaseNode& base_node) {
|
||||||
|
|
||||||
bool ExecuteVisitor::HandleBuiltinFunctionCall(FunctionCallExpression* node) {
|
bool ExecuteVisitor::HandleBuiltinFunctionCall(FunctionCallExpression* node) {
|
||||||
if (node->name == "print") {
|
if (node->name == "print") {
|
||||||
Visitor::Visit(node->arguments[0]);
|
Visitor::Visit(node->arguments[0].second);
|
||||||
info::builtin::Print(*ExtractInternalValue<std::string>(current_value_, node->base));
|
info::builtin::Print(*ExtractInternalValue<std::string>(current_value_, node->base));
|
||||||
|
|
||||||
current_value_ = context_manager_.AddValue(info::value::InternalValue(info::value::Unit()),
|
current_value_ = context_manager_.AddValue(info::value::InternalValue(info::value::Unit()),
|
||||||
|
|
@ -1025,6 +1057,13 @@ bool ExecuteVisitor::HandleBuiltinFunctionCall(FunctionCallExpression* node) {
|
||||||
} else if (node->name == "random") { // TODO: different types, better random, seed, etc.
|
} else if (node->name == "random") { // TODO: different types, better random, seed, etc.
|
||||||
current_value_ = context_manager_.AddValue(info::value::InternalValue(rand()),
|
current_value_ = context_manager_.AddValue(info::value::InternalValue(rand()),
|
||||||
utils::ValueType::Tmp);
|
utils::ValueType::Tmp);
|
||||||
|
} else if (node->name == "error") {
|
||||||
|
Visitor::Visit(node->arguments[0].second);
|
||||||
|
info::builtin::Error(*ExtractInternalValue<std::string>(current_value_, node->base));
|
||||||
|
|
||||||
|
error_handling::HandleInternalError("Error function finished",
|
||||||
|
"ExecuteVisitor.HandleBuiltinFunctionCall",
|
||||||
|
&node->base);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -1046,7 +1085,7 @@ bool ExecuteVisitor::HandleBuiltinTypeFunctionCall(FunctionCallExpression* node,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& argument : node->arguments) {
|
for (auto& argument : node->arguments) {
|
||||||
Visitor::Visit(argument);
|
Visitor::Visit(argument.second);
|
||||||
arguments.push_back(ExtractValue<info::value::InternalValue>(current_value_, node->base));
|
arguments.push_back(ExtractValue<info::value::InternalValue>(current_value_, node->base));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ void FindSymbolsVisitor::Visit(FunctionDeclaration* node) {
|
||||||
|
|
||||||
info.argument_types.resize(node->type->types.size());
|
info.argument_types.resize(node->type->types.size());
|
||||||
for (size_t i = 0; i < node->type->types.size(); ++i) {
|
for (size_t i = 0; i < node->type->types.size(); ++i) {
|
||||||
info.argument_types[i] = node->type->types[i].get();
|
info.argument_types[i] = node->type->types[i].second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
info.node = node;
|
info.node = node;
|
||||||
|
|
@ -179,6 +179,7 @@ void FindSymbolsVisitor::Visit(TypeclassDefinitionStatement* node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindSymbolsVisitor::Visit(PartitionStatement* node) {
|
void FindSymbolsVisitor::Visit(PartitionStatement* node) {
|
||||||
|
// TODO: difference between partitions ??
|
||||||
node->executable_id_ = namespace_visitor_.AddPartition(node->name.path, node, node->base);
|
node->executable_id_ = namespace_visitor_.AddPartition(node->name.path, node, node->base);
|
||||||
|
|
||||||
Visitor::Visit(node->value); // to visit all tree
|
Visitor::Visit(node->value); // to visit all tree
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,9 @@ void PrintVisitor::Visit(PartitionStatement* node) {
|
||||||
case utils::PartitionModifier::Test:
|
case utils::PartitionModifier::Test:
|
||||||
out_ << "test ";
|
out_ << "test ";
|
||||||
break;
|
break;
|
||||||
|
case utils::PartitionModifier::Example:
|
||||||
|
out_ << "example ";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
Visit(&node->name);
|
Visit(&node->name);
|
||||||
out_ << "] = (";
|
out_ << "] = (";
|
||||||
|
|
@ -460,7 +463,13 @@ void PrintVisitor::Visit(FunctionCallExpression* node) {
|
||||||
}
|
}
|
||||||
is_first = false;
|
is_first = false;
|
||||||
|
|
||||||
Visitor::Visit(argument);
|
if (argument.first.has_value()) {
|
||||||
|
out_ << "::";
|
||||||
|
Visit(&argument.first.value());
|
||||||
|
out_ << ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
Visitor::Visit(argument.second);
|
||||||
}
|
}
|
||||||
out_ << ")";
|
out_ << ")";
|
||||||
}
|
}
|
||||||
|
|
@ -544,6 +553,24 @@ void PrintVisitor::Visit(LambdaFunction* node) {
|
||||||
out_ << ")\n";
|
out_ << ")\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrintVisitor::Visit(AndExpression* node) {
|
||||||
|
out_ << "[AndExpression] (";
|
||||||
|
for (auto& expression : node->expressions) {
|
||||||
|
out_ << "&&";
|
||||||
|
Visitor::Visit(expression);
|
||||||
|
}
|
||||||
|
out_ << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintVisitor::Visit(OrExpression* node) {
|
||||||
|
out_ << "[OrExpression] (";
|
||||||
|
for (auto& expression : node->expressions) {
|
||||||
|
out_ << "||";
|
||||||
|
Visitor::Visit(expression);
|
||||||
|
}
|
||||||
|
out_ << ")";
|
||||||
|
}
|
||||||
|
|
||||||
void PrintVisitor::Visit(ArrayExpression* node) {
|
void PrintVisitor::Visit(ArrayExpression* node) {
|
||||||
out_ << "[ArrayExpression] ( ,";
|
out_ << "[ArrayExpression] ( ,";
|
||||||
for (auto& element : node->elements) {
|
for (auto& element : node->elements) {
|
||||||
|
|
@ -619,7 +646,14 @@ void PrintVisitor::Visit(FunctionType* node) {
|
||||||
out_ << " -> ";
|
out_ << " -> ";
|
||||||
}
|
}
|
||||||
is_first = false;
|
is_first = false;
|
||||||
Visit(type.get());
|
|
||||||
|
if (type.first.has_value()) {
|
||||||
|
out_ << "::";
|
||||||
|
Visit(&type.first.value());
|
||||||
|
out_ << ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
Visit(type.second.get());
|
||||||
}
|
}
|
||||||
out_ << ')';
|
out_ << ')';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -239,14 +239,14 @@ void TypeCheckVisitor::Visit(FunctionDefinitionStatement* node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < node->definition->arguments.size(); ++i) {
|
for (size_t i = 0; i < node->definition->arguments.size(); ++i) {
|
||||||
Visit(declaration->type->types[i].get());
|
Visit(declaration->type->types[i].second.get());
|
||||||
current_type_ = context_manager_.ToModifiedValue(current_type_, utils::ValueType::Const);
|
current_type_ = context_manager_.ToModifiedValue(current_type_, utils::ValueType::Const);
|
||||||
if (!context_manager_.DefineVariable(node->definition->arguments[i], current_type_)) {
|
if (!context_manager_.DefineVariable(node->definition->arguments[i], current_type_)) {
|
||||||
error_handling::HandleTypecheckError("Can't define function argument variable: name redefinition", node->base);
|
error_handling::HandleTypecheckError("Can't define function argument variable: name redefinition", node->base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Visit(declaration->type->types.back().get());
|
Visit(declaration->type->types.back().second.get());
|
||||||
utils::IdType returned_type = current_type_;
|
utils::IdType returned_type = current_type_;
|
||||||
|
|
||||||
returned_type_ = std::nullopt;
|
returned_type_ = std::nullopt;
|
||||||
|
|
@ -828,10 +828,18 @@ void TypeCheckVisitor::Visit(FunctionCallExpression* node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = index_shift; i < node->arguments.size(); ++i) {
|
for (size_t i = index_shift; i < node->arguments.size(); ++i) {
|
||||||
Visit(function_declaration->type->types[i - index_shift].get());
|
Visit(function_declaration->type->types[i - index_shift].second.get());
|
||||||
utils::IdType argument_type = TypeInContext(current_type_, context);
|
utils::IdType argument_type = TypeInContext(current_type_, context);
|
||||||
|
|
||||||
Visitor::Visit(node->arguments[i]);
|
if (node->arguments[i].first.has_value()) {
|
||||||
|
if (!function_declaration->type->types[i - index_shift].first.has_value()) {
|
||||||
|
error_handling::HandleTypecheckError("Declared argument has no annotation (argument " + std::to_string(i - index_shift + 1) + ")", node->base);
|
||||||
|
} else if (node->arguments[i].first.value() != function_declaration->type->types[i - index_shift].first.value()) {
|
||||||
|
error_handling::HandleTypecheckError("Wrong argument annotation (argument " + std::to_string(i - index_shift + 1) + ")", node->base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Visitor::Visit(node->arguments[i].second);
|
||||||
if (!context_manager_.AddValueRequirement(current_type_, argument_type)) {
|
if (!context_manager_.AddValueRequirement(current_type_, argument_type)) {
|
||||||
error_handling::HandleTypecheckError("Wrong argument type (argument " + std::to_string(i - index_shift + 1) + ")", node->base);
|
error_handling::HandleTypecheckError("Wrong argument type (argument " + std::to_string(i - index_shift + 1) + ")", node->base);
|
||||||
}
|
}
|
||||||
|
|
@ -863,7 +871,7 @@ void TypeCheckVisitor::Visit(FunctionCallExpression* node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Visit(function_declaration->type->types.back().get());
|
Visit(function_declaration->type->types.back().second.get());
|
||||||
current_type_ = TypeInContext(current_type_, context);
|
current_type_ = TypeInContext(current_type_, context);
|
||||||
|
|
||||||
context_manager_.ExitContext();
|
context_manager_.ExitContext();
|
||||||
|
|
@ -1052,6 +1060,32 @@ void TypeCheckVisitor::Visit(LambdaFunction* node) {
|
||||||
node->base.type_ = current_type_;
|
node->base.type_ = current_type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TypeCheckVisitor::Visit(AndExpression* node) {
|
||||||
|
for (auto& expression : node->expressions) {
|
||||||
|
Visitor::Visit(expression);
|
||||||
|
if (!context_manager_.EqualValues(internal_to_abstract_type_.at(info::type::InternalType::Bool), current_type_)) {
|
||||||
|
error_handling::HandleTypecheckError("And expression element is not bool expression", node->base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_type_ = internal_to_abstract_type_.at(info::type::InternalType::Bool);
|
||||||
|
|
||||||
|
node->base.type_ = current_type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypeCheckVisitor::Visit(OrExpression* node) {
|
||||||
|
for (auto& expression : node->expressions) {
|
||||||
|
Visitor::Visit(expression);
|
||||||
|
if (!context_manager_.EqualValues(internal_to_abstract_type_.at(info::type::InternalType::Bool), current_type_)) {
|
||||||
|
error_handling::HandleTypecheckError("Or expression element is not bool expression", node->base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_type_ = internal_to_abstract_type_.at(info::type::InternalType::Bool);
|
||||||
|
|
||||||
|
node->base.type_ = current_type_;
|
||||||
|
}
|
||||||
|
|
||||||
void TypeCheckVisitor::Visit(ArrayExpression* node) {
|
void TypeCheckVisitor::Visit(ArrayExpression* node) {
|
||||||
utils::IdType elements_type = 0;
|
utils::IdType elements_type = 0;
|
||||||
|
|
||||||
|
|
@ -1282,7 +1316,7 @@ void TypeCheckVisitor::Visit(FunctionType* node) {
|
||||||
std::vector<utils::IdType> argument_types(node->types.size());
|
std::vector<utils::IdType> argument_types(node->types.size());
|
||||||
|
|
||||||
for (auto& argument_type : node->types) {
|
for (auto& argument_type : node->types) {
|
||||||
Visit(argument_type.get());
|
Visit(argument_type.second.get());
|
||||||
argument_types.push_back(current_type_);
|
argument_types.push_back(current_type_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1677,8 +1711,8 @@ std::optional<FunctionDeclaration*> TypeCheckVisitor::FindFunctionAndUpdate(Func
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!maybe_function_declaration.has_value() && node->is_binary_operator_expression && node->arguments.size() > 0) {
|
if (!maybe_function_declaration.has_value() && node->is_binary_operator_expression && node->arguments.size() > 0 && !node->arguments[0].first.has_value()) { // ... , no annotation
|
||||||
Visitor::Visit(node->arguments[0]);
|
Visitor::Visit(node->arguments[0].second);
|
||||||
maybe_function_declaration = FindExpressionMethodAndUpdate(node, current_type_);
|
maybe_function_declaration = FindExpressionMethodAndUpdate(node, current_type_);
|
||||||
if (maybe_function_declaration.has_value()) {
|
if (maybe_function_declaration.has_value()) {
|
||||||
node->is_method_of_first_argument_ = true;
|
node->is_method_of_first_argument_ = true;
|
||||||
|
|
|
||||||
|
|
@ -260,6 +260,9 @@ void TypedPrintVisitor::Visit(PartitionStatement* node) {
|
||||||
case utils::PartitionModifier::Test:
|
case utils::PartitionModifier::Test:
|
||||||
out_ << "test ";
|
out_ << "test ";
|
||||||
break;
|
break;
|
||||||
|
case utils::PartitionModifier::Example:
|
||||||
|
out_ << "example ";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
Visit(&node->name);
|
Visit(&node->name);
|
||||||
out_ << "] = (";
|
out_ << "] = (";
|
||||||
|
|
@ -631,7 +634,13 @@ void TypedPrintVisitor::Visit(FunctionCallExpression* node) {
|
||||||
}
|
}
|
||||||
is_first = false;
|
is_first = false;
|
||||||
|
|
||||||
Visitor::Visit(argument);
|
if (argument.first.has_value()) {
|
||||||
|
out_ << "::";
|
||||||
|
Visit(&argument.first.value());
|
||||||
|
out_ << ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
Visitor::Visit(argument.second);
|
||||||
}
|
}
|
||||||
out_ << ")";
|
out_ << ")";
|
||||||
}
|
}
|
||||||
|
|
@ -750,6 +759,36 @@ void TypedPrintVisitor::Visit(LambdaFunction* node) {
|
||||||
out_ << ")\n";
|
out_ << ")\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TypedPrintVisitor::Visit(AndExpression* node) {
|
||||||
|
out_ << "[AndExpression : ";
|
||||||
|
|
||||||
|
if (node->base.type_.has_value()) {
|
||||||
|
out_ << context_manager_.GetAnyValue(node->base.type_.value())->GetTypeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
out_ << "] (";
|
||||||
|
for (auto& expression : node->expressions) {
|
||||||
|
out_ << "&&";
|
||||||
|
Visitor::Visit(expression);
|
||||||
|
}
|
||||||
|
out_ << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypedPrintVisitor::Visit(OrExpression* node) {
|
||||||
|
out_ << "[OrExpression : ";
|
||||||
|
|
||||||
|
if (node->base.type_.has_value()) {
|
||||||
|
out_ << context_manager_.GetAnyValue(node->base.type_.value())->GetTypeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
out_ << "] (";
|
||||||
|
for (auto& expression : node->expressions) {
|
||||||
|
out_ << "||";
|
||||||
|
Visitor::Visit(expression);
|
||||||
|
}
|
||||||
|
out_ << ")";
|
||||||
|
}
|
||||||
|
|
||||||
void TypedPrintVisitor::Visit(ArrayExpression* node) {
|
void TypedPrintVisitor::Visit(ArrayExpression* node) {
|
||||||
out_ << "[ArrayExpression : ";
|
out_ << "[ArrayExpression : ";
|
||||||
|
|
||||||
|
|
@ -873,7 +912,14 @@ void TypedPrintVisitor::Visit(FunctionType* node) {
|
||||||
out_ << " -> ";
|
out_ << " -> ";
|
||||||
}
|
}
|
||||||
is_first = false;
|
is_first = false;
|
||||||
Visit(type.get());
|
|
||||||
|
if (type.first.has_value()) {
|
||||||
|
out_ << "::";
|
||||||
|
Visit(&type.first.value());
|
||||||
|
out_ << ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
Visit(type.second.get());
|
||||||
}
|
}
|
||||||
out_ << ')';
|
out_ << ')';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,8 @@ std::optional<char> ToEscapeSymbol(char symbol) {
|
||||||
return '\t';
|
return '\t';
|
||||||
case 'v':
|
case 'v':
|
||||||
return '\v';
|
return '\v';
|
||||||
|
case 's':
|
||||||
|
return ' ';
|
||||||
default:
|
default:
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +84,7 @@ bool IsBuiltinFunction(const std::string& name) { // optimize ??
|
||||||
builtin_functions.insert("one");
|
builtin_functions.insert("one");
|
||||||
builtin_functions.insert("show");
|
builtin_functions.insert("show");
|
||||||
builtin_functions.insert("read");
|
builtin_functions.insert("read");
|
||||||
|
builtin_functions.insert("error");
|
||||||
// builtin_functions.insert("debug_show"); // TODO
|
// builtin_functions.insert("debug_show"); // TODO
|
||||||
|
|
||||||
return builtin_functions.count(name) != 0;
|
return builtin_functions.count(name) != 0;
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,15 @@ void Visitor::Visit(Expression& node) {
|
||||||
case 3:
|
case 3:
|
||||||
Visit(*std::get<std::unique_ptr<SubExpression>>(node));
|
Visit(*std::get<std::unique_ptr<SubExpression>>(node));
|
||||||
break;
|
break;
|
||||||
|
case 4:
|
||||||
|
Visit(std::get<std::unique_ptr<AndExpression>>(node).get());
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
Visit(std::get<std::unique_ptr<OrExpression>>(node).get());
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
Visit(std::get<std::unique_ptr<ArrayExpression>>(node).get());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// error
|
// error
|
||||||
break;
|
break;
|
||||||
|
|
@ -206,9 +215,6 @@ void Visitor::Visit(SuperExpression& node) {
|
||||||
Visit(std::get<std::unique_ptr<VariantExpression>>(node).get());
|
Visit(std::get<std::unique_ptr<VariantExpression>>(node).get());
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
Visit(std::get<std::unique_ptr<ArrayExpression>>(node).get());
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
Visit(*std::get<std::unique_ptr<Expression>>(node));
|
Visit(*std::get<std::unique_ptr<Expression>>(node));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -500,7 +506,10 @@ void Visitor::Visit(FunctionCallExpression* node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& argument : node->arguments) {
|
for (auto& argument : node->arguments) {
|
||||||
Visit(argument);
|
if (argument.first.has_value()) {
|
||||||
|
Visit(&argument.first.value());
|
||||||
|
}
|
||||||
|
Visit(argument.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -544,6 +553,18 @@ void Visitor::Visit(LambdaFunction* node) {
|
||||||
Visit(node->expression);
|
Visit(node->expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Visitor::Visit(AndExpression* node) {
|
||||||
|
for (auto& expression : node->expressions) {
|
||||||
|
Visit(expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Visitor::Visit(OrExpression* node) {
|
||||||
|
for (auto& expression : node->expressions) {
|
||||||
|
Visit(expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Visitor::Visit(ArrayExpression* node) {
|
void Visitor::Visit(ArrayExpression* node) {
|
||||||
for (auto& element : node->elements) {
|
for (auto& element : node->elements) {
|
||||||
Visit(element);
|
Visit(element);
|
||||||
|
|
@ -590,7 +611,10 @@ void Visitor::Visit(Name* node) {
|
||||||
|
|
||||||
void Visitor::Visit(FunctionType* node) {
|
void Visitor::Visit(FunctionType* node) {
|
||||||
for (auto& type : node->types) {
|
for (auto& type : node->types) {
|
||||||
Visit(type.get());
|
if (type.first.has_value()) {
|
||||||
|
Visit(&type.first.value());
|
||||||
|
}
|
||||||
|
Visit(type.second.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
decl test-arrays : -> \unit
|
decl test-arrays : -> \unit
|
||||||
def test-arrays = {
|
def test-arrays = {
|
||||||
var arr1 = ,1 ,2 ,3
|
var arr1 = ,1 ,2 ,3
|
||||||
const arr2 = \int..array: 32
|
let arr2 = \int..array: 32
|
||||||
var arr3 = \string..array: 11
|
var arr3 = \string..array: 11
|
||||||
const arr4 = ''a--''z
|
let arr4 = ''a--''z
|
||||||
const n = 100
|
let n = 100
|
||||||
var arr5 <- \int..new-array: 10
|
var arr5 <- \int..new-array: 10
|
||||||
|
|
||||||
var arr6 <- \string..new-array: 10
|
var arr6 <- \string..new-array: 10
|
||||||
var arr6-reference = ^arr6
|
var arr6-reference = ^arr6
|
||||||
|
|
||||||
const elem1 = arr1`0
|
let elem1 = arr1`0
|
||||||
var elem2 = arr1`2
|
var elem2 = arr1`2
|
||||||
const ref1 = ^arr1`1
|
let ref1 = ^arr1`1
|
||||||
var ref2 = ^arr1`3
|
var ref2 = ^arr1`3
|
||||||
; arr1`1 = 123
|
; arr1`1 = 123
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
decl flow-control-test : -> \unit
|
decl flow-control-test : -> \unit
|
||||||
def flow-control-test = {
|
def flow-control-test = {
|
||||||
if (a < b ||. a == b) && (b < c) then \io..print: x
|
if && ( || a < b
|
||||||
elif x < 0 then {
|
|| a == b )
|
||||||
|
&& b < c
|
||||||
|
then \io..print: x
|
||||||
|
elif x < 0
|
||||||
|
then {
|
||||||
; x += 1
|
; x += 1
|
||||||
; \io..print: y
|
; \io..print: y
|
||||||
} else {
|
} else {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (a > 0) && not: (array..is-empty:) do {
|
while && a > 0
|
||||||
|
&& not: (array..is-empty:)
|
||||||
|
do {
|
||||||
; a -= 1
|
; a -= 1
|
||||||
; array..pop:
|
; array..pop:
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,15 @@ def find-prefix-hashes : str = {
|
||||||
|
|
||||||
alias \hash = \acc-hash[char]
|
alias \hash = \acc-hash[char]
|
||||||
|
|
||||||
decl find-substring : \string -> \string -> \array[index]
|
decl find-substring : ::str \string -> ::substr \string -> \array[index]
|
||||||
def find-substring : str substr = {
|
def find-substring : str substr = {
|
||||||
var result = \array[index]..empty:
|
var result = \array[index]..empty:
|
||||||
|
|
||||||
const str-hashes = find-prefix-hashes:[hash] str
|
let str-hashes = find-prefix-hashes:[hash] str
|
||||||
const substr-hash = \hash..of: substr
|
let substr-hash = \hash..of: substr
|
||||||
|
|
||||||
for i in 0--(str-hashes..size: - substr..size:) do {
|
for i in 0--(str-hashes..size: - substr..size:) do {
|
||||||
const part-hash = hash..diff: str-hashes`(i + substr..size:) str-hashes`i
|
let part-hash = hash..diff: str-hashes`(i + substr..size:) str-hashes`i
|
||||||
|
|
||||||
if part-hash == substr-hash then {
|
if part-hash == substr-hash then {
|
||||||
; result..push: i
|
; result..push: i
|
||||||
|
|
@ -60,3 +60,9 @@ def mul : x y = x * y
|
||||||
|
|
||||||
decl mul-10 : \int -> \int
|
decl mul-10 : \int -> \int
|
||||||
def mul-10 = mul: 10
|
def mul-10 = mul: 10
|
||||||
|
|
||||||
|
exec main {
|
||||||
|
; find-substring:
|
||||||
|
::str "abacaba"
|
||||||
|
::substr "bac"
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
decl test-lambdas : -> \unit
|
decl test-lambdas : -> \unit
|
||||||
def test-lambdas = {
|
def test-lambdas = {
|
||||||
const lambda1 = \\x -> x * x
|
let lambda1 = \\x -> x * x
|
||||||
const lambda2 = \\x -> x..hash:
|
let lambda2 = \\x -> x..hash:
|
||||||
const lambda3 = \\x y -> x + y
|
let lambda3 = \\x y -> x + y
|
||||||
|
|
||||||
const lambda4 = \\x -> {
|
let lambda4 = \\x -> {
|
||||||
; \io..print: x
|
; \io..print: x
|
||||||
const y = x + x
|
let y = x + x
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ def fruit-cost : fruit = {
|
||||||
|
|
||||||
decl amount-to-string : \int -> \bool -> \string
|
decl amount-to-string : \int -> \bool -> \string
|
||||||
def amount-to-string : x is-zero-separated = {
|
def amount-to-string : x is-zero-separated = {
|
||||||
const ans = match x with
|
let ans = match x with
|
||||||
| 0 ?? is-zero-separated -> "Zero"
|
| 0 ?? is-zero-separated -> "Zero"
|
||||||
| 0 | 1 | 2 | 3 | 4 -> "Few"
|
| 0 | 1 | 2 | 3 | 4 -> "Few"
|
||||||
| x ?? (5--9)..contains: x -> "Several"
|
| x ?? (5--9)..contains: x -> "Several"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,6 @@ struct \struct-with-ref =
|
||||||
|
|
||||||
decl test-memory : -> \unit
|
decl test-memory : -> \unit
|
||||||
def test-memory = {
|
def test-memory = {
|
||||||
const unique-ref1 <- \int..new: 5
|
let unique-ref1 <- \int..new: 5
|
||||||
var unique-ref2 <- \array..of: 1 2 3
|
var unique-ref2 <- \array..of: 1 2 3
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,6 @@ namespace var \array {
|
||||||
decl something : -> \unit
|
decl something : -> \unit
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace const \array {
|
namespace let \array {
|
||||||
decl something : -> \unit
|
decl something : -> \unit
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
test all.dev.syntax.testing {
|
test all.dev.syntax.testing {
|
||||||
const a = 31
|
let a = 31
|
||||||
; do-something: a
|
; do-something: a
|
||||||
}
|
}
|
||||||
|
|
||||||
exec app.exe {
|
exec app.exe {
|
||||||
const b = true
|
let b = true
|
||||||
const c = false
|
let c = false
|
||||||
; do-something-different: b b c
|
; do-something-different: b b c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
example func.basic-example {
|
||||||
|
; func: a b c // func executed
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,189 +0,0 @@
|
||||||
basic \float[#ord #div #str]
|
|
||||||
basic \int[#ord #idiv #str]
|
|
||||||
basic \string[#ord #str #char-container #copy]
|
|
||||||
basic \char[#ord #str #copy]
|
|
||||||
basic \bool[#ord #str #copy]
|
|
||||||
basic \unit[#str #copy]
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
decl not : \bool -> \bool
|
|
||||||
def not : x =
|
|
||||||
(match x with
|
|
||||||
| true -> false
|
|
||||||
| false -> true)
|
|
||||||
|
|
||||||
decl ( && ) : \bool -> \bool -> \bool
|
|
||||||
def ( && ) : x y =
|
|
||||||
match x with
|
|
||||||
| true -> (
|
|
||||||
match y with
|
|
||||||
| true -> true
|
|
||||||
| false -> false
|
|
||||||
)
|
|
||||||
| false -> false
|
|
||||||
|
|
||||||
decl ( || ) : \bool -> \bool -> \bool
|
|
||||||
def ( || ) : x y =
|
|
||||||
match x with
|
|
||||||
| true -> true
|
|
||||||
| false -> (
|
|
||||||
match y with
|
|
||||||
| true -> true
|
|
||||||
| false -> false
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
typeclass \char-container =
|
|
||||||
& var size : -> \int
|
|
||||||
& var at : \int -> \char
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
typeclass \move = // TODO
|
|
||||||
& var ( <- ) : \move -> \unit
|
|
||||||
|
|
||||||
typeclass \copy =
|
|
||||||
& var ( = ) : \copy -> \unit
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
typeclass \sum[#copy] =
|
|
||||||
& var ( += ) : \sum -> \unit
|
|
||||||
& var ( -= ) : \sum -> \unit
|
|
||||||
& var ( + ) : \sum -> \sum
|
|
||||||
& var ( - ) : \sum -> \sum
|
|
||||||
& zero : -> \sum
|
|
||||||
|
|
||||||
namespace var \sum {
|
|
||||||
def ( + ) : x = {
|
|
||||||
var ans = self
|
|
||||||
; ans += x
|
|
||||||
return ans
|
|
||||||
}
|
|
||||||
|
|
||||||
def ( - ) : x = {
|
|
||||||
var ans = self
|
|
||||||
; ans -= x
|
|
||||||
return ans
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typeclass \mult[#sum] =
|
|
||||||
& var ( *= ) : \mult -> \unit
|
|
||||||
& var ( * ) : \mult -> \mult
|
|
||||||
|
|
||||||
namespace var \mult {
|
|
||||||
def ( * ) : x = {
|
|
||||||
var ans = self
|
|
||||||
; ans *= x
|
|
||||||
return ans
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typeclass \idiv[#mult] =
|
|
||||||
& var div : \idiv -> \idiv
|
|
||||||
& var mod : \idiv -> \idiv
|
|
||||||
|
|
||||||
namespace var \idiv {
|
|
||||||
def mod : x = self -. x * self..div: x
|
|
||||||
}
|
|
||||||
|
|
||||||
typeclass \div[#mult] =
|
|
||||||
& var ( /= ) : \div -> \unit
|
|
||||||
& var ( / ) : \div -> \div
|
|
||||||
|
|
||||||
namespace var \div {
|
|
||||||
def ( / ) : x = {
|
|
||||||
var ans = self
|
|
||||||
; ans /= x
|
|
||||||
return ans
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
typeclass \eq =
|
|
||||||
& var ( == ) : \eq -> \bool
|
|
||||||
& var ( != ) : \eq -> \bool
|
|
||||||
|
|
||||||
namespace var \eq {
|
|
||||||
def ( != ) : x = not: (self == x)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
struct \order =
|
|
||||||
| $eq
|
|
||||||
| $lt
|
|
||||||
| $gt
|
|
||||||
|
|
||||||
typeclass \ord[#eq] =
|
|
||||||
& var compare : \ord -> \order
|
|
||||||
& var ( < ) : \ord -> \bool
|
|
||||||
& var ( >= ) : \ord -> \bool
|
|
||||||
& var ( > ) : \ord -> \bool
|
|
||||||
& var ( <= ) : \ord -> \bool
|
|
||||||
|
|
||||||
decl min 'a[#ord] : 'a -> 'a -> 'a
|
|
||||||
def min : x y = if x < y then x else y
|
|
||||||
|
|
||||||
decl max 'a[#ord] : 'a -> 'a -> 'a
|
|
||||||
def max : x y = if x < y then y else x
|
|
||||||
|
|
||||||
namespace var \ord {
|
|
||||||
def compare : x =
|
|
||||||
if self == x then $eq
|
|
||||||
elif self < x then $lt
|
|
||||||
else $gt
|
|
||||||
|
|
||||||
def ( >= ) : x = not: (self < x)
|
|
||||||
def ( > ) : x = x < self
|
|
||||||
def ( <= ) : x = not: (x < self)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
typeclass \show =
|
|
||||||
& var show : -> \string
|
|
||||||
|
|
||||||
typeclass \read =
|
|
||||||
& read : \string -> \read
|
|
||||||
|
|
||||||
typeclass \str[#show #read]
|
|
||||||
|
|
||||||
// typeclass debug-show = // TODO
|
|
||||||
// & debugdshow : -> \string
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
typeclass \default =
|
|
||||||
& default : -> \default
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
typeclass \bounded =
|
|
||||||
& min-bound : -> \bounded
|
|
||||||
& max-bound : -> \bounded
|
|
||||||
& var is-max-bound : -> \bool
|
|
||||||
& var is-min-bound : -> \bool
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
typeclass \enum =
|
|
||||||
& var succ : -> \optional[enum]
|
|
||||||
& var pred : -> \optional[enum]
|
|
||||||
& to-enum : \int -> \enum
|
|
||||||
& var from-enum : -> \int
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
namespace io {
|
|
||||||
decl print : \string -> \unit
|
|
||||||
decl scan : -> \string
|
|
||||||
}
|
|
||||||
|
|
||||||
decl random : -> \int // TODO
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
@ -7,32 +7,23 @@ basic \unit[#str #copy]
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
namespace io {
|
||||||
|
decl print : \string -> \unit
|
||||||
|
decl scan : -> \string
|
||||||
|
}
|
||||||
|
|
||||||
|
decl random : -> \int // TODO
|
||||||
|
|
||||||
|
decl error : \string -> \unit
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
decl not : \bool -> \bool
|
decl not : \bool -> \bool
|
||||||
def not : x =
|
def not : x =
|
||||||
(match x with
|
(match x with
|
||||||
| true -> false
|
| true -> false
|
||||||
| false -> true)
|
| false -> true)
|
||||||
|
|
||||||
decl ( && ) : \bool -> \bool -> \bool
|
|
||||||
def ( && ) : x y =
|
|
||||||
match x with
|
|
||||||
| true -> (
|
|
||||||
match y with
|
|
||||||
| true -> true
|
|
||||||
| false -> false
|
|
||||||
)
|
|
||||||
| false -> false
|
|
||||||
|
|
||||||
decl ( || ) : \bool -> \bool -> \bool
|
|
||||||
def ( || ) : x y =
|
|
||||||
match x with
|
|
||||||
| true -> true
|
|
||||||
| false -> (
|
|
||||||
match y with
|
|
||||||
| true -> true
|
|
||||||
| false -> false
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
typeclass \char-container =
|
typeclass \char-container =
|
||||||
|
|
@ -179,15 +170,6 @@ typeclass \enum =
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
namespace io {
|
|
||||||
decl print : \string -> \unit
|
|
||||||
decl scan : -> \string
|
|
||||||
}
|
|
||||||
|
|
||||||
decl random : -> \int // TODO
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
// // // bad
|
// // // bad
|
||||||
// // typeclass \functor 'a =
|
// // typeclass \functor 'a =
|
||||||
// // & fmap 'b ('f[#functor['b]]) : ('a -> 'b) -> \functor -> 'f
|
// // & fmap 'b ('f[#functor['b]]) : ('a -> 'b) -> \functor -> 'f
|
||||||
|
|
@ -231,7 +213,7 @@ def ( -- ) : begin end = {
|
||||||
decl scan-int : -> \int
|
decl scan-int : -> \int
|
||||||
def scan-int = \int..read: (\io..scan:)
|
def scan-int = \int..read: (\io..scan:)
|
||||||
|
|
||||||
decl print-int : \int -> \unit
|
decl print-int : ::x \int -> \unit
|
||||||
def print-int : x = \io..print: (x..show:)
|
def print-int : x = \io..print: (x..show:)
|
||||||
|
|
||||||
decl scan-anything 'a[#read] : -> 'a
|
decl scan-anything 'a[#read] : -> 'a
|
||||||
|
|
@ -277,6 +259,11 @@ def scan-three = & \io..scan: & \io..scan: & \io..scan:
|
||||||
|
|
||||||
exec main {
|
exec main {
|
||||||
var n = scan-anything:[int]
|
var n = scan-anything:[int]
|
||||||
|
|
||||||
|
; print-anything:[bool] (n < 2) // TODO: fix exception (arguments not present ??)
|
||||||
|
|
||||||
|
if n > 1 then print-anything:[string] "aaa" // not work ??
|
||||||
|
|
||||||
var x = (for _ in 0--n do scan-int:) // $array[int] & 0
|
var x = (for _ in 0--n do scan-int:) // $array[int] & 0
|
||||||
|
|
||||||
var k? = if n < 2 then n * 2 +. 3 in
|
var k? = if n < 2 then n * 2 +. 3 in
|
||||||
|
|
@ -285,6 +272,8 @@ exec main {
|
||||||
|
|
||||||
; print-anything:[int] n
|
; print-anything:[int] n
|
||||||
|
|
||||||
|
; print-int: ::x 123
|
||||||
|
|
||||||
var & a & b & c = scan-three-t:
|
var & a & b & c = scan-three-t:
|
||||||
; \io..print: b
|
; \io..print: b
|
||||||
var & d & e & f = scan-three:
|
var & d & e & f = scan-three:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
decl test-tuples : -> \unit
|
decl test-tuples : -> \unit
|
||||||
def test-tuples = {
|
def test-tuples = {
|
||||||
var tuple1 = & ''a & 2 & "hello"
|
var tuple1 = & ''a & 2 & "hello"
|
||||||
const & t1 & t2 & t3 = f: x
|
let & t1 & t2 & t3 = f: x
|
||||||
|
|
||||||
; tuple1`0 = ''b
|
; tuple1`0 = ''b
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,4 @@ def test-variants = {
|
||||||
| "hello" -> "nothing"
|
| "hello" -> "nothing"
|
||||||
| 11 -> "nothing"
|
| 11 -> "nothing"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue