new builders fixes, print fix

This commit is contained in:
ProgramSnail 2024-08-25 15:41:20 +03:00
parent 3446d599d7
commit edf7db284c
12 changed files with 90 additions and 127 deletions

View file

@ -11,7 +11,7 @@ int main(int argc, char **argv) {
//
SourcesManager sources_manager(Log({}, {}));
SourcesManager sources_manager;
sources_manager.AddFile(filename);
sources_manager.Print(std::cout);

View file

@ -1,7 +1,6 @@
#pragma once
#include "basic_printers.hpp"
#include "error_handling.hpp"
#include "expression_nodes.hpp"
#include "log.hpp"
#include "name_tree.hpp"
@ -17,10 +16,8 @@
class SourcesManager {
public:
SourcesManager(Log &&log) : log_(std::move(log)) {}
void AddFile(const std::string &filename) {
Log::Context logc(log_, utils::Log::Area::kParse);
Log::Context logc(executor_.log(), utils::Log::Area::kParse);
std::ifstream in;
in.open(filename);
@ -40,8 +37,9 @@ public:
{{"There are some parsing errors in file"}} /*,parse_tree.get_root()*/);
}
auto new_statements = builders::build_source_file(
parse_tree.get_root(), expression_storage_, type_storage_, name_tree_);
builders::BuilderTask<nodes::Statements> statements_builder(executor_);
auto new_statements = statements_builder(parse_tree.get_root(), {});
statements_.reserve(statements_.size() + new_statements.size());
for (auto &new_statement : new_statements) {
@ -56,34 +54,6 @@ public:
printers::print(statements_, printer);
}
//
nodes::ExpressionStorage &expressions() { return expression_storage_; }
const nodes::ExpressionStorage &expressions() const {
return expression_storage_;
}
//
nodes::TypeStorage &types() { return type_storage_; }
const nodes::TypeStorage &types() const { return type_storage_; }
//
names::NameTree &names() { return name_tree_; }
const names::NameTree &names() const { return name_tree_; }
//
utils::Log &log() { return log_; }
const utils::Log &log() const { return log_; }
//
size_t statements_size() const { return statements_.size(); }
nodes::Statement *statement(size_t id) { return &statements_.at(id); }
@ -93,9 +63,7 @@ public:
}
private:
Log log_;
nodes::ExpressionStorage expression_storage_ = {};
nodes::TypeStorage type_storage_ = {};
names::NameTree name_tree_ = {};
builders::Executor executor_ =
builders::Executor(utils::BuildPrintLog(std::cout), {}, {}, {}, {});
std::vector<nodes::Statement> statements_ = {};
};

View file

@ -11,14 +11,25 @@ using Exprs = nodes::ExpressionStorage;
using Types = nodes::TypeStorage;
using Names = names::NameTree;
using Executor = utils::Executor<Exprs, Types, Names>;
//
struct State {
std::optional<nodes::Identifier> last_defined_type_name;
};
//
using Executor = utils::Executor<Exprs, Types, Names, State>;
using ParserNode = parser::ParseTree::Node;
using Node = nodes::Node_<nodes::NodePart<utils::Pos>>; // TODO
// using Node = nodes::Node_<nodes::NodePart<utils::Pos>>; // TODO
using Arguments = utils::None;
template <typename N>
using Task = utils::Task<Executor, N, utils::None, parser::ParseTree::Node>;
using Task = utils::Task<Executor, N, Arguments, parser::ParseTree::Node>;
template <typename N, typename Tag = utils::None> struct BuilderTask {
static_assert(false);
@ -28,8 +39,7 @@ template <typename N> struct BuilderTaskBase : public Task<N> {
using Task<N>::Task;
template <typename OtherN, typename OtherTag = utils::None>
OtherN Run(const parser::ParseTree::Node &node,
const utils::None &args = {}) {
OtherN Run(const parser::ParseTree::Node &node, const Arguments &args = {}) {
BuilderTask<OtherN, OtherTag> task(this->executor);
return task(node, args);
}

View file

@ -29,7 +29,7 @@ struct BuilderTask<nodes::ExpressionProxy>
using BuilderTaskBase<nodes::ExpressionProxy>::BuilderTaskBase;
nodes::ExpressionProxy operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -38,7 +38,7 @@ struct BuilderTask<nodes::Match::Case>
using BuilderTaskBase<nodes::Match::Case>::BuilderTaskBase;
nodes::Match::Case operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -46,7 +46,7 @@ struct BuilderTask<nodes::Match> : public BuilderTaskBase<nodes::Match> {
using BuilderTaskBase<nodes::Match>::BuilderTaskBase;
nodes::Match operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -55,7 +55,7 @@ struct BuilderTask<nodes::Condition>
using BuilderTaskBase<nodes::Condition>::BuilderTaskBase;
nodes::Condition operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -63,7 +63,7 @@ struct BuilderTask<nodes::Loop> : public BuilderTaskBase<nodes::Loop> {
using BuilderTaskBase<nodes::Loop>::BuilderTaskBase;
nodes::Loop operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
// --- operators
@ -74,7 +74,7 @@ struct BuilderTask<nodes::NameExpression, utils::CommaTag>
using BuilderTaskBase<nodes::NameExpression>::BuilderTaskBase;
nodes::NameExpression operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -83,7 +83,7 @@ struct BuilderTask<nodes::NameExpression, utils::OperatorTag>
using BuilderTaskBase<nodes::NameExpression>::BuilderTaskBase;
nodes::NameExpression operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
// --- continers
@ -99,7 +99,7 @@ struct BuilderTask<nodes::Container, T>
// or
// '[[' expression+ ']]'
nodes::Container operator()(const ParserNode &parser_node,
const utils::None &) override {
const Arguments &) override {
const auto container_type = std::is_same_v<T, utils::BlockTag>
? nodes::Container::BLOCK
: nodes::Container::ARRAY;
@ -125,7 +125,7 @@ struct BuilderTask<nodes::Return> : public BuilderTaskBase<nodes::Return> {
using BuilderTaskBase<nodes::Return>::BuilderTaskBase;
nodes::Return operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -134,7 +134,7 @@ struct BuilderTask<nodes::NameDefinition>
using BuilderTaskBase<nodes::NameDefinition>::BuilderTaskBase;
nodes::NameDefinition operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -143,7 +143,7 @@ struct BuilderTask<nodes::Access, utils::ArrayAccessTag>
using BuilderTaskBase<nodes::Access>::BuilderTaskBase;
nodes::Access operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -152,7 +152,7 @@ struct BuilderTask<nodes::Access, utils::TupleAccessTag>
using BuilderTaskBase<nodes::Access>::BuilderTaskBase;
nodes::Access operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -161,25 +161,7 @@ struct BuilderTask<nodes::LoopControl>
using BuilderTaskBase<nodes::LoopControl>::BuilderTaskBase;
nodes::LoopControl operator()(const ParserNode &parser_node,
const utils::None &args) override;
};
template <>
struct BuilderTask<nodes::ModifierExpression, utils::RefTag>
: public BuilderTaskBase<nodes::ModifierExpression> {
using BuilderTaskBase<nodes::ModifierExpression>::BuilderTaskBase;
nodes::ModifierExpression operator()(const ParserNode &parser_node,
const utils::None &args) override;
};
template <>
struct BuilderTask<nodes::ModifierExpression, utils::SuffixTag>
: public BuilderTaskBase<nodes::ModifierExpression> {
using BuilderTaskBase<nodes::ModifierExpression>::BuilderTaskBase;
nodes::ModifierExpression operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <typename T>
@ -193,7 +175,7 @@ struct BuilderTask<nodes::ModifierExpression, T>
// or
// expression ('?' | '!')
nodes::ModifierExpression operator()(const ParserNode &parser_node,
const utils::None &) override {
const Arguments &) override {
const size_t modifier_pos =
std::is_same_v<T, utils::RefTag> ? 0 : parser_node.child_count() - 1;
@ -212,7 +194,7 @@ struct BuilderTask<nodes::NameExpression, utils::FuncCallTag>
using BuilderTaskBase<nodes::NameExpression>::BuilderTaskBase;
nodes::NameExpression operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -221,7 +203,7 @@ struct BuilderTask<nodes::Constructor>
using BuilderTaskBase<nodes::Constructor>::BuilderTaskBase;
nodes::Constructor operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -229,7 +211,7 @@ struct BuilderTask<nodes::Lambda> : public BuilderTaskBase<nodes::Lambda> {
using BuilderTaskBase<nodes::Lambda>::BuilderTaskBase;
nodes::Lambda operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
} // namespace builders

View file

@ -20,7 +20,7 @@ struct BuilderTask<nodes::Statements>
using BuilderTaskBase<nodes::Statements>::BuilderTaskBase;
nodes::Statements operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
// copy of statement inserted into name_tree
@ -30,7 +30,7 @@ struct BuilderTask<nodes::Statement>
using BuilderTaskBase<nodes::Statement>::BuilderTaskBase;
nodes::Statement operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -38,7 +38,7 @@ struct BuilderTask<nodes::Import> : public BuilderTaskBase<nodes::Import> {
using BuilderTaskBase<nodes::Import>::BuilderTaskBase;
nodes::Import operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -47,7 +47,7 @@ struct BuilderTask<nodes::Constraint>
using BuilderTaskBase<nodes::Constraint>::BuilderTaskBase;
nodes::Constraint operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -56,7 +56,7 @@ struct BuilderTask<nodes::TypeDefinition>
using BuilderTaskBase<nodes::TypeDefinition>::BuilderTaskBase;
nodes::TypeDefinition operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
template <>
@ -65,7 +65,7 @@ struct BuilderTask<nodes::FunctionDefinition>
using BuilderTaskBase<nodes::FunctionDefinition>::BuilderTaskBase;
nodes::FunctionDefinition operator()(const ParserNode &parser_node,
const utils::None &args) override;
const Arguments &args) override;
};
// const std::optional<nodes::Identifier> &previous_defined_type_name, // TODO

View file

@ -12,7 +12,7 @@ namespace builders {
nodes::ExpressionProxy
BuilderTask<nodes::ExpressionProxy>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
tokens::Type type = tokens::string_to_type(parser_node.get_type());
auto maybe_parenthesis = parser_node.previous_sibling();
@ -150,7 +150,7 @@ BuilderTask<nodes::ExpressionProxy>::operator()(const ParserNode &parser_node,
// (':=' | '=:') expression (('??' | 'if') expression)? (_do_ expression)?
nodes::Match::Case
BuilderTask<nodes::Match::Case>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
std::string case_type = parser_node.nth_child(0).get_value();
std::optional<ParserNode> condition_node;
@ -186,7 +186,7 @@ BuilderTask<nodes::Match::Case>::operator()(const ParserNode &parser_node,
// expression case+
nodes::Match
BuilderTask<nodes::Match>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
std::vector<nodes::Match::Case> cases;
auto current_node = parser_node.nth_named_child(1);
@ -205,7 +205,7 @@ BuilderTask<nodes::Match>::operator()(const ParserNode &parser_node,
// expression)* (('!!=>', 'else') expression)?
nodes::Condition
BuilderTask<nodes::Condition>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
size_t named_child_count = parser_node.named_child_count();
std::vector<std::pair<nodes::ExpressionProxy, nodes::ExpressionProxy>> cases;
@ -233,7 +233,7 @@ BuilderTask<nodes::Condition>::operator()(const ParserNode &parser_node,
// ('@' | 'for') (expression | expression ':' expression)? _do_ expression
nodes::Loop BuilderTask<nodes::Loop>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
size_t named_child_count = parser_node.named_child_count();
if (named_child_count == 1) { // body
@ -282,7 +282,7 @@ nodes::Loop BuilderTask<nodes::Loop>::operator()(const ParserNode &parser_node,
// expression ',' expression
nodes::NameExpression
BuilderTask<nodes::NameExpression, utils::CommaTag>::operator()(
const ParserNode &parser_node, const utils::None &) {
const ParserNode &parser_node, const Arguments &) {
std::vector<std::pair<std::optional<std::string>, nodes::ExpressionProxy>>
arguments;
@ -303,7 +303,7 @@ BuilderTask<nodes::NameExpression, utils::CommaTag>::operator()(
// expression operator expression
nodes::NameExpression
BuilderTask<nodes::NameExpression, utils::OperatorTag>::operator()(
const ParserNode &parser_node, const utils::None &) {
const ParserNode &parser_node, const Arguments &) {
auto name_node = parser_node.child_by_field_name("na"
"m"
"e");
@ -331,7 +331,7 @@ BuilderTask<nodes::NameExpression, utils::OperatorTag>::operator()(
// ('return' | 'bring') expression
nodes::Return
BuilderTask<nodes::Return>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
std::string modifier = parser_node.nth_child(0).get_value();
return nodes::Return(
@ -347,7 +347,7 @@ BuilderTask<nodes::Return>::operator()(const ParserNode &parser_node,
// _var_let_ (simple_name_identifier | placeholder)
nodes::NameDefinition
BuilderTask<nodes::NameDefinition>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
std::string modifier = parser_node.nth_child(0).get_value();
auto name_node = parser_node.nth_named_child(0);
@ -361,7 +361,7 @@ BuilderTask<nodes::NameDefinition>::operator()(const ParserNode &parser_node,
// expression '[' expression ']'
nodes::Access BuilderTask<nodes::Access, utils::ArrayAccessTag>::operator()(
const ParserNode &parser_node, const utils::None &) {
const ParserNode &parser_node, const Arguments &) {
return nodes::Access(
build_node(parser_node), nodes::Access::ARRAY,
Run<nodes::ExpressionProxy>(parser_node.nth_named_child(0)),
@ -370,7 +370,7 @@ nodes::Access BuilderTask<nodes::Access, utils::ArrayAccessTag>::operator()(
// expression '.' number_literal
nodes::Access BuilderTask<nodes::Access, utils::TupleAccessTag>::operator()(
const ParserNode &parser_node, const utils::None &) {
const ParserNode &parser_node, const Arguments &) {
return nodes::Access(
build_node(parser_node), nodes::Access::TUPLE,
Run<nodes::ExpressionProxy>(parser_node.nth_named_child(0)),
@ -382,7 +382,7 @@ nodes::Access BuilderTask<nodes::Access, utils::TupleAccessTag>::operator()(
// 'break' | 'continue'
nodes::LoopControl
BuilderTask<nodes::LoopControl>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
return nodes::LoopControl(build_node(parser_node),
parser_node.get_value() == "br"
"ea"
@ -408,7 +408,7 @@ void build_arguments_until_end(
} else {
arguments.emplace_back(
std::move(last_annotation),
executor.Run<nodes::ExpressionProxy>(current_node));
BuilderTask<nodes::ExpressionProxy>{executor}(current_node, {}));
last_annotation = std::nullopt;
}
current_node = current_node.next_named_sibling();
@ -419,7 +419,7 @@ void build_arguments_until_end(
// ')') (annotation? expression)*
nodes::NameExpression
BuilderTask<nodes::NameExpression, utils::FuncCallTag>::operator()(
const ParserNode &parser_node, const utils::None &) {
const ParserNode &parser_node, const Arguments &) {
std::vector<nodes::AnnotatedArgument> arguments;
std::optional<nodes::Type> prefix;
@ -466,7 +466,7 @@ BuilderTask<nodes::NameExpression, utils::FuncCallTag>::operator()(
// type (annotation? expression)*
nodes::Constructor
BuilderTask<nodes::Constructor>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
std::vector<nodes::AnnotatedArgument> arguments;
build_arguments_until_end(parser_node
@ -485,7 +485,7 @@ BuilderTask<nodes::Constructor>::operator()(const ParserNode &parser_node,
// '\\' argument_name* _do_ expression
nodes::Lambda
BuilderTask<nodes::Lambda>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
std::vector<nodes::Identifier> arguments;
auto current_node =

View file

@ -14,7 +14,6 @@
#include "utils.hpp"
#include <optional>
#include <type_traits>
#include <vector>
namespace builders {
@ -22,16 +21,14 @@ namespace builders {
// statement+
nodes::Statements
BuilderTask<nodes::Statements>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
std::vector<nodes::Statement> statements;
std::optional<nodes::Identifier> last_defined_type_name;
auto current_node = parser_node.nth_named_child(0);
while (!current_node.is_null()) {
statements.push_back(
Run<nodes::Statement>(current_node /* TODO , last_defined_type_name*/
));
statements.push_back(Run<nodes::Statement>(current_node));
current_node = current_node.next_named_sibling();
}
@ -41,8 +38,7 @@ BuilderTask<nodes::Statements>::operator()(const ParserNode &parser_node,
// import | type_definition | function_definition | typeclass_definition
nodes::Statement
BuilderTask<nodes::Statement>::operator()(const ParserNode &parser_node,
const utils::None &) {
// std::optional<nodes::Identifier> &last_defined_type_name, // TODO
const Arguments &) {
tokens::Type type = tokens::string_to_type(parser_node.get_type());
std::optional<std::string> statement_name;
@ -61,7 +57,7 @@ BuilderTask<nodes::Statement>::operator()(const ParserNode &parser_node,
case tokens::Type::TYPE_DEFINITION:
statement = nodes::Statement(build_node(parser_node),
Run<nodes::TypeDefinition>(parser_node));
last_defined_type_name =
state<State>().last_defined_type_name =
*statement.value().get<nodes::TypeDefinition>().value()->get_name();
statement_name = *statement.value()
.get<nodes::TypeDefinition>()
@ -70,11 +66,8 @@ BuilderTask<nodes::Statement>::operator()(const ParserNode &parser_node,
->get();
break;
case tokens::Type::FUNCTION_DEFINITION:
statement =
nodes::Statement(build_node(parser_node),
Run<nodes::FunctionDefinition>(
parser_node /*, TODO last_defined_type_name*/
));
statement = nodes::Statement(build_node(parser_node),
Run<nodes::FunctionDefinition>(parser_node));
statement_name = *statement.value()
.get<nodes::FunctionDefinition>()
.value()
@ -115,7 +108,7 @@ BuilderTask<nodes::Statement>::operator()(const ParserNode &parser_node,
// ('::' | 'import') simple_name ('=' simple_name)? (':' identifier*)?
nodes::Import
BuilderTask<nodes::Import>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
auto name_node = parser_node.child_by_field_name("name");
auto module_node = parser_node.child_by_field_name("module");
@ -138,7 +131,7 @@ BuilderTask<nodes::Import>::operator()(const ParserNode &parser_node,
// '?' expression
nodes::Constraint
BuilderTask<nodes::Constraint>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
return nodes::Constraint(
build_node(parser_node),
Run<nodes::ExpressionProxy>(parser_node.nth_named_child(0)));
@ -177,7 +170,7 @@ parser::ParseTree::Node collect_symbol_doc_nodes(
// (argument_type* '=' type)? ';'
nodes::TypeDefinition
BuilderTask<nodes::TypeDefinition>::operator()(const ParserNode &parser_node,
const utils::None &) {
const Arguments &) {
std::optional<parser::ParseTree::Node> description_node;
std::vector<parser::ParseTree::Node> annotation_nodes;
@ -275,7 +268,7 @@ BuilderTask<nodes::TypeDefinition>::operator()(const ParserNode &parser_node,
// (annotation? _reference_ type)+)?
// (((block | array) | '=' expression ';') | ';')
nodes::FunctionDefinition BuilderTask<nodes::FunctionDefinition>::operator()(
const ParserNode &parser_node, const utils::None &) {
const ParserNode &parser_node, const Arguments &) {
// const std::optional<nodes::Identifier> &last_defined_type_name, // TODO
std::optional<parser::ParseTree::Node> description_node;
@ -303,12 +296,12 @@ nodes::FunctionDefinition BuilderTask<nodes::FunctionDefinition>::operator()(
current_node.get_value() == ".") {
is_method = true;
if (!last_defined_type_name.has_value()) {
if (!state<State>().last_defined_type_name.has_value()) {
error_handling::handle_parsing_error(
"Can't define method without associated type", parser_node);
}
name_prefix = last_defined_type_name.value();
name_prefix = state<State>().last_defined_type_name.value();
}
nodes::Modifier return_modifier = nodes::Modifier::NONE;

View file

@ -16,7 +16,7 @@ class NodeStorage : public core::Storage<NodeData> {
public:
Id Insert(NodeData &&elem) {
const auto id = NewId();
utils::Assert(data_.insert({id, std::move(elem)}).second,
::utils::Assert(data_.insert({id, std::move(elem)}).second,
std::format("insert failed, id={}", *id));
return id;
}

View file

@ -20,6 +20,10 @@ namespace printers {
void print(const nodes::Statement &statement, Printer &printer) {
std::visit([&printer](const auto &arg) -> void { print(arg, printer); },
*statement.get_any());
if (not statement.get<nodes::EmptyLines>().has_value()) {
printer.new_indent_line();
}
}
void print(const nodes::Import &statement, Printer &printer) {

View file

@ -23,9 +23,9 @@ public:
//
template <typename T, typename... Args> T Run(Args... args) {
return T(*this, args...);
}
// template <typename T, typename... Args> T Run(Args &&...args) {
// return T(*this, std::forward<Args>(args)...);
// }
template <typename T> T &state() { return ExecutorState<T>::state_; }
template <typename T> const T &state() const {

View file

@ -22,6 +22,12 @@ struct Id {
} // namespace storage
template <> struct std::hash<storage::Id> {
std::size_t operator()(const storage::Id &id) const noexcept {
return std::hash<size_t>{}(id.id);
}
};
namespace core { // TODO: move all important to core
template <typename T> class Storage {

View file

@ -42,10 +42,10 @@ std::string to_string(Log::Message message) {
//
inline Log BuildPrintLog(std::ostream &out, Log::Level min_level) {
Log BuildPrintLog(std::ostream &out, Log::Level min_level) {
auto const print = [&out, min_level](const Log::Message &message) {
if (message.level >= min_level) {
out << to_string(message);
out << to_string(message) << std::endl;
}
};
return Log(print, print);