PlaceholderParser: Reduced code duplicity.

This commit is contained in:
Vojtech Bubnik 2023-03-23 15:00:36 +01:00
parent 11e22b7950
commit 3045884640

View File

@ -170,7 +170,7 @@ namespace client
template<typename Iterator> template<typename Iterator>
struct OptWithPos { struct OptWithPos {
OptWithPos() {} OptWithPos() {}
OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range<Iterator> it_range) : opt(opt), it_range(it_range) {} OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range<Iterator> it_range, bool writable = false) : opt(opt), it_range(it_range), writable(writable) {}
ConfigOptionConstPtr opt { nullptr }; ConfigOptionConstPtr opt { nullptr };
bool writable { false }; bool writable { false };
// -1 means it is a scalar variable, or it is a vector variable and index was not assigned yet or the whole vector is considered. // -1 means it is a scalar variable, or it is a vector variable and index was not assigned yet or the whole vector is considered.
@ -720,9 +720,14 @@ namespace client
} }
struct MyContext : public ConfigOptionResolver { struct MyContext : public ConfigOptionResolver {
// Config provided as a parameter to PlaceholderParser invocation, overriding PlaceholderParser stored config.
const DynamicConfig *external_config = nullptr; const DynamicConfig *external_config = nullptr;
// Config stored inside PlaceholderParser.
const DynamicConfig *config = nullptr; const DynamicConfig *config = nullptr;
// Config provided as a parameter to PlaceholderParser invocation, evaluated after the two configs above.
const DynamicConfig *config_override = nullptr; const DynamicConfig *config_override = nullptr;
// Config provided as a parameter to PlaceholderParser invocation, containing variables that will be read out
// and processed by the PlaceholderParser callee.
mutable DynamicConfig *config_outputs = nullptr; mutable DynamicConfig *config_outputs = nullptr;
// Local variables, read / write // Local variables, read / write
mutable DynamicConfig config_local; mutable DynamicConfig config_local;
@ -737,6 +742,7 @@ namespace client
// Table to translate symbol tag to a human readable error message. // Table to translate symbol tag to a human readable error message.
static std::map<std::string, std::string> tag_to_error_message; static std::map<std::string, std::string> tag_to_error_message;
// Should the parser consider the parsed string to be a macro or a boolean expression?
static bool evaluate_full_macro(const MyContext *ctx) { return ! ctx->just_boolean_expression; } static bool evaluate_full_macro(const MyContext *ctx) { return ! ctx->just_boolean_expression; }
const ConfigOption* optptr(const t_config_option_key &opt_key) const override const ConfigOption* optptr(const t_config_option_key &opt_key) const override
@ -762,13 +768,13 @@ namespace client
out = this->config_local.optptr(opt_key); out = this->config_local.optptr(opt_key);
return out; return out;
} }
void store_new_variable(const std::string &opt_key, ConfigOption *opt, bool global_variable) { void store_new_variable(const std::string &opt_key, std::unique_ptr<ConfigOption> &&opt, bool global_variable) {
assert(opt != nullptr); assert(opt);
if (global_variable) { if (global_variable) {
assert(this->context_data != nullptr && this->context_data->global_config); assert(this->context_data != nullptr && this->context_data->global_config);
this->context_data->global_config->set_key_value(opt_key, opt); this->context_data->global_config->set_key_value(opt_key, opt.release());
} else } else
this->config_local.set_key_value(opt_key ,opt); this->config_local.set_key_value(opt_key, opt.release());
} }
template <typename Iterator> template <typename Iterator>
@ -844,9 +850,10 @@ namespace client
boost::iterator_range<Iterator> &opt_key, boost::iterator_range<Iterator> &opt_key,
OptWithPos<Iterator> &output) OptWithPos<Iterator> &output)
{ {
const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end())); const std::string key{ opt_key.begin(), opt_key.end() };
const ConfigOption *opt = ctx->resolve_symbol(key);
if (opt == nullptr) { if (opt == nullptr) {
opt = ctx->resolve_output_symbol(std::string(opt_key.begin(), opt_key.end())); opt = ctx->resolve_output_symbol(key);
if (opt == nullptr) if (opt == nullptr)
ctx->throw_exception("Not a variable name", opt_key); ctx->throw_exception("Not a variable name", opt_key);
output.writable = true; output.writable = true;
@ -872,32 +879,16 @@ namespace client
output.it_range.end() = it_end; output.it_range.end() = it_end;
} }
// Evaluating a scalar variable into expr,
// all possible ConfigOption types are supported.
template <typename Iterator> template <typename Iterator>
static void variable_value( static void scalar_variable_to_expr(
const MyContext *ctx, const MyContext *ctx,
OptWithPos<Iterator> &opt, OptWithPos<Iterator> &opt,
expr<Iterator> &output) expr<Iterator> &output)
{ {
if (opt.opt->is_vector()) {
if (! opt.has_index())
ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range);
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt.opt);
if (vec->empty())
ctx->throw_exception("Indexing an empty vector variable", opt.it_range);
size_t idx = (opt.index < 0) ? 0 : (opt.index >= int(vec->size())) ? 0 : size_t(opt.index);
switch (opt.opt->type()) {
case coFloats: output.set_d(static_cast<const ConfigOptionFloats *>(opt.opt)->values[idx]); break;
case coInts: output.set_i(static_cast<const ConfigOptionInts *>(opt.opt)->values[idx]); break;
case coStrings: output.set_s(static_cast<const ConfigOptionStrings *>(opt.opt)->values[idx]); break;
case coPercents: output.set_d(static_cast<const ConfigOptionPercents*>(opt.opt)->values[idx]); break;
case coPoints: output.set_s(to_string(static_cast<const ConfigOptionPoints *>(opt.opt)->values[idx])); break;
case coBools: output.set_b(static_cast<const ConfigOptionBools *>(opt.opt)->values[idx] != 0); break;
//case coEnums: output.set_s(opt.opt->vserialize()[idx]); break;
default:
ctx->throw_exception("Unknown vector variable type", opt.it_range);
}
} else {
assert(opt.opt->is_scalar()); assert(opt.opt->is_scalar());
switch (opt.opt->type()) { switch (opt.opt->type()) {
case coFloat: output.set_d(opt.opt->getFloat()); break; case coFloat: output.set_d(opt.opt->getFloat()); break;
case coInt: output.set_i(opt.opt->getInt()); break; case coInt: output.set_i(opt.opt->getInt()); break;
@ -945,10 +936,176 @@ namespace client
break; break;
} }
default: default:
ctx->throw_exception("Unknown scalar variable type", opt.it_range); ctx->throw_exception("Unsupported scalar variable type", opt.it_range);
} }
} }
// Evaluating one element of a vector variable.
// all possible ConfigOption types are supported.
template <typename Iterator>
static void vector_element_to_expr(
const MyContext *ctx,
OptWithPos<Iterator> &opt,
expr<Iterator> &output)
{
assert(opt.opt->is_vector());
if (! opt.has_index())
ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range);
const ConfigOptionVectorBase* vec = static_cast<const ConfigOptionVectorBase*>(opt.opt);
if (vec->empty())
ctx->throw_exception("Indexing an empty vector variable", opt.it_range);
size_t idx = (opt.index < 0) ? 0 : (opt.index >= int(vec->size())) ? 0 : size_t(opt.index);
switch (opt.opt->type()) {
case coFloats: output.set_d(static_cast<const ConfigOptionFloats*>(opt.opt)->values[idx]); break;
case coInts: output.set_i(static_cast<const ConfigOptionInts*>(opt.opt)->values[idx]); break;
case coStrings: output.set_s(static_cast<const ConfigOptionStrings*>(opt.opt)->values[idx]); break;
case coPercents: output.set_d(static_cast<const ConfigOptionPercents*>(opt.opt)->values[idx]); break;
case coPoints: output.set_s(to_string(static_cast<const ConfigOptionPoints*>(opt.opt)->values[idx])); break;
case coBools: output.set_b(static_cast<const ConfigOptionBools*>(opt.opt)->values[idx] != 0); break;
//case coEnums: output.set_s(opt.opt->vserialize()[idx]); break;
default:
ctx->throw_exception("Unsupported vector variable type", opt.it_range);
}
}
template <typename Iterator>
static void check_writable(const MyContext *ctx, OptWithPos<Iterator> &opt) {
if (! opt.writable)
ctx->throw_exception("Cannot modify a read-only variable", opt.it_range);
}
template <typename Iterator>
static void check_numeric(const expr<Iterator> &param) {
if (! param.numeric_type())
param.throw_exception("Right side is not a numeric expression");
};
template <typename Iterator>
static size_t evaluate_count(const expr<Iterator> &expr_count) {
if (expr_count.type() != expr<Iterator>::TYPE_INT)
expr_count.throw_exception("Expected number of elements to fill a vector with.");
int count = expr_count.i();
if (count < 0)
expr_count.throw_exception("Negative number of elements specified.");
return size_t(count);
};
template <typename Iterator>
static void scalar_variable_assign_scalar(
const MyContext *ctx,
OptWithPos<Iterator> &lhs,
const expr<Iterator> &rhs)
{
assert(lhs.opt->is_scalar());
check_writable(ctx, lhs);
ConfigOption *wropt = const_cast<ConfigOption*>(lhs.opt);
switch (wropt->type()) {
case coFloat:
check_numeric(rhs);
static_cast<ConfigOptionFloat*>(wropt)->value = rhs.as_d();
break;
case coInt:
check_numeric(rhs);
static_cast<ConfigOptionInt*>(wropt)->value = rhs.as_i();
break;
case coString:
static_cast<ConfigOptionString*>(wropt)->value = rhs.to_string();
break;
case coPercent:
check_numeric(rhs);
static_cast<ConfigOptionPercent*>(wropt)->value = rhs.as_d();
break;
case coBool:
if (rhs.type() != expr<Iterator>::TYPE_BOOL)
ctx->throw_exception("Right side is not a boolean expression", rhs.it_range);
static_cast<ConfigOptionBool*>(wropt)->value = rhs.b();
break;
default:
ctx->throw_exception("Unsupported output scalar variable type", lhs.it_range);
}
}
template <typename Iterator>
static void vector_variable_element_assign_scalar(
const MyContext *ctx,
OptWithPos<Iterator> &lhs,
const expr<Iterator> &rhs)
{
assert(lhs.opt->is_vector());
check_writable(ctx, lhs);
if (! lhs.has_index())
ctx->throw_exception("Referencing an output vector variable when scalar is expected", lhs.it_range);
ConfigOptionVectorBase *vec = const_cast<ConfigOptionVectorBase*>(static_cast<const ConfigOptionVectorBase*>(lhs.opt));
if (vec->empty())
ctx->throw_exception("Indexing an empty vector variable", lhs.it_range);
if (lhs.index >= int(vec->size()))
ctx->throw_exception("Index out of range", lhs.it_range);
switch (lhs.opt->type()) {
case coFloats:
check_numeric(rhs);
static_cast<ConfigOptionFloats*>(vec)->values[lhs.index] = rhs.as_d();
break;
case coInts:
check_numeric(rhs);
static_cast<ConfigOptionInts*>(vec)->values[lhs.index] = rhs.as_i();
break;
case coStrings:
static_cast<ConfigOptionStrings*>(vec)->values[lhs.index] = rhs.to_string();
break;
case coPercents:
check_numeric(rhs);
static_cast<ConfigOptionPercents*>(vec)->values[lhs.index] = rhs.as_d();
break;
case coBools:
if (rhs.type() != expr<Iterator>::TYPE_BOOL)
ctx->throw_exception("Right side is not a boolean expression", rhs.it_range);
static_cast<ConfigOptionBools*>(vec)->values[lhs.index] = rhs.b();
break;
default:
ctx->throw_exception("Unsupported output vector variable type", lhs.it_range);
}
}
template <typename Iterator>
static void vector_variable_assign_expr_with_count(
const MyContext *ctx,
OptWithPos<Iterator> &lhs,
const expr<Iterator> &rhs_count,
const expr<Iterator> &rhs_value)
{
size_t count = evaluate_count(rhs_count);
auto *opt = const_cast<ConfigOption*>(lhs.opt);
switch (lhs.opt->type()) {
case coFloats:
check_numeric(rhs_value);
static_cast<ConfigOptionFloats*>(opt)->values.assign(count, rhs_value.as_d());
break;
case coInts:
check_numeric(rhs_value);
static_cast<ConfigOptionInts*>(opt)->values.assign(count, rhs_value.as_i());
break;
case coStrings:
static_cast<ConfigOptionStrings*>(opt)->values.assign(count, rhs_value.to_string());
break;
case coBools:
if (rhs_value.type() != expr<Iterator>::TYPE_BOOL)
rhs_value.throw_exception("Right side is not a boolean expression");
static_cast<ConfigOptionBools*>(opt)->values.assign(count, rhs_value.b());
break;
default: assert(false);
}
}
template <typename Iterator>
static void variable_value(
const MyContext *ctx,
OptWithPos<Iterator> &opt,
expr<Iterator> &output)
{
if (opt.opt->is_vector())
vector_element_to_expr(ctx, opt, output);
else
scalar_variable_to_expr(ctx, opt, output);
output.it_range = opt.it_range; output.it_range = opt.it_range;
} }
@ -974,6 +1131,7 @@ namespace client
output.it_range = opt.it_range; output.it_range = opt.it_range;
} }
// Reference to an existing symbol, or a name of a new symbol.
template<typename Iterator> template<typename Iterator>
struct NewOldVariable { struct NewOldVariable {
std::string name; std::string name;
@ -1010,240 +1168,148 @@ namespace client
out.it_range = it_range; out.it_range = it_range;
} }
template <typename Iterator>
static void new_scalar_variable(
const MyContext *ctx,
bool global_variable,
NewOldVariable<Iterator> &output_variable,
const expr<Iterator> &param)
{
auto check_numeric = [](const expr<Iterator> &param) {
if (! param.numeric_type())
param.throw_exception("Right side is not a numeric expression");
};
if (output_variable.opt) {
if (output_variable.opt->is_vector())
param.throw_exception("Cannot assign a scalar value to a vector variable.");
switch (output_variable.opt->type()) {
case coFloat:
check_numeric(param);
static_cast<ConfigOptionFloat*>(output_variable.opt)->value = param.as_d();
break;
case coInt:
check_numeric(param);
static_cast<ConfigOptionInt*>(output_variable.opt)->value = param.as_i();
break;
case coString:
static_cast<ConfigOptionString*>(output_variable.opt)->value = param.to_string();
break;
case coBool:
if (param.type() != expr<Iterator>::TYPE_BOOL)
param.throw_exception("Right side is not a boolean expression");
static_cast<ConfigOptionBool*>(output_variable.opt)->value = param.b();
break;
default: assert(false);
}
} else {
switch (param.type()) {
case expr<Iterator>::TYPE_BOOL: output_variable.opt = new ConfigOptionBool(param.b()); break;
case expr<Iterator>::TYPE_INT: output_variable.opt = new ConfigOptionInt(param.i()); break;
case expr<Iterator>::TYPE_DOUBLE: output_variable.opt = new ConfigOptionFloat(param.d()); break;
case expr<Iterator>::TYPE_STRING: output_variable.opt = new ConfigOptionString(param.s()); break;
default: assert(false);
}
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable);
}
}
template <typename Iterator>
static void check_writable(const MyContext *ctx, OptWithPos<Iterator> &opt) {
if (! opt.writable)
ctx->throw_exception("Cannot modify a read-only variable", opt.it_range);
}
// Decoding a scalar variable symbol "opt", assigning it a value of "param". // Decoding a scalar variable symbol "opt", assigning it a value of "param".
template <typename Iterator> template <typename Iterator>
static void assign_scalar_variable( static void scalar_variable_assign_scalar_expression(
const MyContext *ctx, const MyContext *ctx,
OptWithPos<Iterator> &opt, OptWithPos<Iterator> &opt,
expr<Iterator> &param) const expr<Iterator> &param)
{ {
check_writable(ctx, opt); check_writable(ctx, opt);
auto check_numeric = [](const expr<Iterator> &param) { if (opt.opt->is_vector())
if (! param.numeric_type()) vector_variable_element_assign_scalar(ctx, opt, param);
param.throw_exception("Right side is not a numeric expression"); else
}; scalar_variable_assign_scalar(ctx, opt, param);
if (opt.opt->is_vector()) {
if (! opt.has_index())
ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range);
ConfigOptionVectorBase *vec = const_cast<ConfigOptionVectorBase*>(static_cast<const ConfigOptionVectorBase*>(opt.opt));
if (vec->empty())
ctx->throw_exception("Indexing an empty vector variable", opt.it_range);
if (opt.index >= int(vec->size()))
ctx->throw_exception("Index out of range", opt.it_range);
switch (opt.opt->type()) {
case coFloats:
check_numeric(param);
static_cast<ConfigOptionFloats*>(vec)->values[opt.index] = param.as_d();
break;
case coInts:
check_numeric(param);
static_cast<ConfigOptionInts*>(vec)->values[opt.index] = param.as_i();
break;
case coStrings:
static_cast<ConfigOptionStrings*>(vec)->values[opt.index] = param.to_string();
break;
case coPercents:
check_numeric(param);
static_cast<ConfigOptionPercents*>(vec)->values[opt.index] = param.as_d();
break;
case coBools:
if (param.type() != expr<Iterator>::TYPE_BOOL)
ctx->throw_exception("Right side is not a boolean expression", param.it_range);
static_cast<ConfigOptionBools*>(vec)->values[opt.index] = param.b();
break;
default:
ctx->throw_exception("Unsupported output vector variable type", opt.it_range);
}
} else {
assert(opt.opt->is_scalar());
ConfigOption *wropt = const_cast<ConfigOption*>(opt.opt);
switch (wropt->type()) {
case coFloat:
check_numeric(param);
static_cast<ConfigOptionFloat*>(wropt)->value = param.as_d();
break;
case coInt:
check_numeric(param);
static_cast<ConfigOptionInt*>(wropt)->value = param.as_i();
break;
case coString:
static_cast<ConfigOptionString*>(wropt)->value = param.to_string();
break;
case coPercent:
check_numeric(param);
static_cast<ConfigOptionPercent*>(wropt)->value = param.as_d();
break;
case coBool:
if (param.type() != expr<Iterator>::TYPE_BOOL)
ctx->throw_exception("Right side is not a boolean expression", param.it_range);
static_cast<ConfigOptionBool*>(wropt)->value = param.b();
break;
default:
ctx->throw_exception("Unsupported output scalar variable type", opt.it_range);
}
}
} }
template <typename Iterator> template <typename Iterator>
static void new_vector_variable_array( static void scalar_variable_new_from_scalar_expression(
const MyContext *ctx, const MyContext *ctx,
bool global_variable, bool global_variable,
NewOldVariable<Iterator> &output_variable, NewOldVariable<Iterator> &lhs,
const expr<Iterator> &expr_count, const expr<Iterator> &rhs)
const expr<Iterator> &expr_value)
{ {
auto check_numeric = [](const expr<Iterator> &param) { if (lhs.opt) {
if (! param.numeric_type()) if (lhs.opt->is_vector())
param.throw_exception("Right side is not a numeric expression"); rhs.throw_exception("Cannot assign a scalar value to a vector variable.");
}; OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true };
auto evaluate_count = [](const expr<Iterator> &expr_count) -> size_t { scalar_variable_assign_scalar(ctx, lhs_opt, rhs);
if (expr_count.type() != expr<Iterator>::TYPE_INT)
expr_count.throw_exception("Expected number of elements to fill a vector with.");
int count = expr_count.i();
if (count < 0)
expr_count.throw_exception("Negative number of elements specified.");
return size_t(count);
};
if (output_variable.opt) {
if (output_variable.opt->is_scalar())
expr_value.throw_exception("Cannot assign a vector value to a scalar variable.");
size_t count = evaluate_count(expr_count);
switch (output_variable.opt->type()) {
case coFloats:
check_numeric(expr_value);
static_cast<ConfigOptionFloats*>(output_variable.opt)->values.assign(count, expr_value.as_d());
break;
case coInts:
check_numeric(expr_value);
static_cast<ConfigOptionInts*>(output_variable.opt)->values.assign(count, expr_value.as_i());
break;
case coStrings:
static_cast<ConfigOptionStrings*>(output_variable.opt)->values.assign(count, expr_value.to_string());
break;
case coBools:
if (expr_value.type() != expr<Iterator>::TYPE_BOOL)
expr_value.throw_exception("Right side is not a boolean expression");
static_cast<ConfigOptionBools*>(output_variable.opt)->values.assign(count, expr_value.b());
break;
default: assert(false);
}
} else { } else {
size_t count = evaluate_count(expr_count); std::unique_ptr<ConfigOption> opt_new;
switch (expr_value.type()) { switch (rhs.type()) {
case expr<Iterator>::TYPE_BOOL: output_variable.opt = new ConfigOptionBools(count, expr_value.b()); break; case expr<Iterator>::TYPE_BOOL: opt_new = std::make_unique<ConfigOptionBool>(rhs.b()); break;
case expr<Iterator>::TYPE_INT: output_variable.opt = new ConfigOptionInts(count, expr_value.i()); break; case expr<Iterator>::TYPE_INT: opt_new = std::make_unique<ConfigOptionInt>(rhs.i()); break;
case expr<Iterator>::TYPE_DOUBLE: output_variable.opt = new ConfigOptionFloats(count, expr_value.d()); break; case expr<Iterator>::TYPE_DOUBLE: opt_new = std::make_unique<ConfigOptionFloat>(rhs.d()); break;
case expr<Iterator>::TYPE_STRING: output_variable.opt = new ConfigOptionStrings(count, expr_value.s()); break; case expr<Iterator>::TYPE_STRING: opt_new = std::make_unique<ConfigOptionString>(rhs.s()); break;
default: assert(false); default: assert(false);
} }
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable); const_cast<MyContext*>(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable);
} }
} }
template <typename Iterator> template <typename Iterator>
static void assign_vector_variable_array( static void vector_variable_new_from_array(
const MyContext *ctx,
bool global_variable,
NewOldVariable<Iterator> &lhs,
const expr<Iterator> &rhs_count,
const expr<Iterator> &rhs_value)
{
if (lhs.opt) {
if (lhs.opt->is_scalar())
rhs_value.throw_exception("Cannot assign a vector value to a scalar variable.");
OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true };
vector_variable_assign_expr_with_count(ctx, lhs_opt, rhs_count, rhs_value);
} else {
size_t count = evaluate_count(rhs_count);
std::unique_ptr<ConfigOption> opt_new;
switch (rhs_value.type()) {
case expr<Iterator>::TYPE_BOOL: opt_new = std::make_unique<ConfigOptionBools>(count, rhs_value.b()); break;
case expr<Iterator>::TYPE_INT: opt_new = std::make_unique<ConfigOptionInts>(count, rhs_value.i()); break;
case expr<Iterator>::TYPE_DOUBLE: opt_new = std::make_unique<ConfigOptionFloats>(count, rhs_value.d()); break;
case expr<Iterator>::TYPE_STRING: opt_new = std::make_unique<ConfigOptionStrings>(count, rhs_value.s()); break;
default: assert(false);
}
const_cast<MyContext*>(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable);
}
}
template <typename Iterator>
static void vector_variable_assign_array(
const MyContext *ctx, const MyContext *ctx,
OptWithPos<Iterator> &lhs, OptWithPos<Iterator> &lhs,
const expr<Iterator> &expr_count, const expr<Iterator> &rhs_count,
const expr<Iterator> &expr_value) const expr<Iterator> &rhs_value)
{ {
check_writable(ctx, lhs); check_writable(ctx, lhs);
auto check_numeric = [](const expr<Iterator> &param) {
if (! param.numeric_type())
param.throw_exception("Right side is not a numeric expression");
};
auto evaluate_count = [](const expr<Iterator> &expr_count) -> size_t {
if (expr_count.type() != expr<Iterator>::TYPE_INT)
expr_count.throw_exception("Expected number of elements to fill a vector with.");
int count = expr_count.i();
if (count < 0)
expr_count.throw_exception("Negative number of elements specified.");
return size_t(count);
};
if (lhs.opt->is_scalar()) if (lhs.opt->is_scalar())
expr_value.throw_exception("Cannot assign a vector value to a scalar variable."); rhs_value.throw_exception("Cannot assign a vector value to a scalar variable.");
auto *opt = const_cast<ConfigOption*>(lhs.opt); vector_variable_assign_expr_with_count(ctx, lhs, rhs_count, rhs_value);
size_t count = evaluate_count(expr_count); }
template<typename ConfigOptionType, typename Iterator, typename RightValueEvaluate>
static void fill_vector_from_initializer_list(ConfigOption *opt, const std::vector<expr<Iterator>> &il, RightValueEvaluate rv_eval) {
auto& out = static_cast<ConfigOptionType*>(opt)->values;
out.clear();
out.reserve(il.size());
for (const expr<Iterator>& i : il)
out.emplace_back(rv_eval(i));
}
template <typename Iterator>
static void vector_variable_assign_initializer_list(
const MyContext *ctx,
OptWithPos<Iterator> &lhs,
const std::vector<expr<Iterator>> &il)
{
check_writable(ctx, lhs);
auto check_numeric_vector = [](const std::vector<expr<Iterator>> &il) {
for (auto &i : il)
if (! i.numeric_type())
i.throw_exception("Right side is not a numeric expression");
};
if (lhs.opt->is_scalar())
ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range);
ConfigOption *opt = const_cast<ConfigOption*>(lhs.opt);
switch (lhs.opt->type()) { switch (lhs.opt->type()) {
case coFloats: case coFloats:
check_numeric(expr_value); check_numeric_vector(il);
static_cast<ConfigOptionFloats*>(opt)->values.assign(count, expr_value.as_d()); fill_vector_from_initializer_list<ConfigOptionFloats>(opt, il, [](auto &v){ return v.as_d(); });
break; break;
case coInts: case coInts:
check_numeric(expr_value); check_numeric_vector(il);
static_cast<ConfigOptionInts*>(opt)->values.assign(count, expr_value.as_i()); fill_vector_from_initializer_list<ConfigOptionInts>(opt, il, [](auto &v){ return v.as_i(); });
break; break;
case coStrings: case coStrings:
static_cast<ConfigOptionStrings*>(opt)->values.assign(count, expr_value.to_string()); fill_vector_from_initializer_list<ConfigOptionStrings>(opt, il, [](auto &v){ return v.to_string(); });
break; break;
case coBools: case coBools:
if (expr_value.type() != expr<Iterator>::TYPE_BOOL) for (auto &i : il)
expr_value.throw_exception("Right side is not a boolean expression"); if (i.type() != expr<Iterator>::TYPE_BOOL)
static_cast<ConfigOptionBools*>(opt)->values.assign(count, expr_value.b()); i.throw_exception("Right side is not a boolean expression");
fill_vector_from_initializer_list<ConfigOptionBools>(opt, il, [](auto &v){ return v.b(); });
break; break;
default: assert(false); default: assert(false);
} }
} }
template <typename Iterator> template <typename Iterator>
static void new_vector_variable_initializer_list( static void vector_variable_new_from_initializer_list(
const MyContext *ctx, const MyContext *ctx,
bool global_variable, bool global_variable,
NewOldVariable<Iterator> &output_variable, NewOldVariable<Iterator> &lhs,
const std::vector<expr<Iterator>> &il) const std::vector<expr<Iterator>> &il)
{ {
if (! output_variable.opt) { if (lhs.opt) {
// Assign to an existing vector variable.
if (lhs.opt->is_scalar())
ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range);
OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true };
vector_variable_assign_initializer_list(ctx, lhs_opt, il);
} else {
// Allocate a new vector variable.
// First guesstimate type of the output vector. // First guesstimate type of the output vector.
size_t num_bool = 0; size_t num_bool = 0;
size_t num_int = 0; size_t num_int = 0;
@ -1257,186 +1323,53 @@ namespace client
case expr<Iterator>::TYPE_STRING: ++ num_string; break; case expr<Iterator>::TYPE_STRING: ++ num_string; break;
default: assert(false); default: assert(false);
} }
std::unique_ptr<ConfigOption> opt_new;
if (num_string > 0) if (num_string > 0)
// Convert everything to strings. // Convert everything to strings.
output_variable.opt = new ConfigOptionStrings(); opt_new = std::make_unique<ConfigOptionStrings>();
else if (num_bool > 0) { else if (num_bool > 0) {
if (num_double + num_int > 0) if (num_double + num_int > 0)
ctx->throw_exception("Right side is not valid: Mixing numeric and boolean types.", boost::iterator_range<Iterator>{ il.front().it_range.begin(), il.back().it_range.end() }); ctx->throw_exception("Right side is not valid: Mixing numeric and boolean types.", boost::iterator_range<Iterator>{ il.front().it_range.begin(), il.back().it_range.end() });
output_variable.opt = new ConfigOptionBools(); opt_new = std::make_unique<ConfigOptionBools>();
} else } else {
// Output is numeric. // Output is numeric.
output_variable.opt = num_double == 0 ? static_cast<ConfigOption*>(new ConfigOptionInts()) : static_cast<ConfigOption*>(new ConfigOptionFloats()); if (num_double == 0)
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable); opt_new = std::make_unique<ConfigOptionInts>();
}
auto check_numeric = [](const std::vector<expr<Iterator>> &il) {
for (auto& i : il)
if (!i.numeric_type())
i.throw_exception("Right side is not a numeric expression");
};
if (output_variable.opt->is_scalar())
ctx->throw_exception("Cannot assign a vector value to a scalar variable.", output_variable.it_range);
switch (output_variable.opt->type()) {
case coFloats:
{
check_numeric(il);
auto &out = static_cast<ConfigOptionFloats*>(output_variable.opt)->values;
out.clear();
out.reserve(il.size());
for (auto &i : il)
out.emplace_back(i.as_d());
break;
}
case coInts:
{
check_numeric(il);
auto &out = static_cast<ConfigOptionInts*>(output_variable.opt)->values;
out.clear();
out.reserve(il.size());
for (auto& i : il)
out.emplace_back(i.as_i());
break;
}
case coStrings:
{
auto &out = static_cast<ConfigOptionStrings*>(output_variable.opt)->values;
out.clear();
out.reserve(il.size());
for (auto &i : il)
out.emplace_back(i.to_string());
break;
}
case coBools:
{
auto &out = static_cast<ConfigOptionBools*>(output_variable.opt)->values;
out.clear();
out.reserve(il.size());
for (auto &i : il)
if (i.type() == expr<Iterator>::TYPE_BOOL)
out.emplace_back(i.b());
else else
i.throw_exception("Right side is not a boolean expression"); opt_new = std::make_unique<ConfigOptionFloats>();
break;
} }
default: OptWithPos lhs_opt{ opt_new.get(), lhs.it_range, true };
assert(false); vector_variable_assign_initializer_list(ctx, lhs_opt, il);
const_cast<MyContext*>(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable);
} }
} }
template <typename Iterator> template <typename Iterator>
static void assign_vector_variable_initializer_list( static void copy_vector_variable_to_vector_variable(
const MyContext *ctx, const MyContext *ctx,
OptWithPos<Iterator> &lhs, OptWithPos<Iterator> &lhs,
const std::vector<expr<Iterator>> &il) const OptWithPos<Iterator> &rhs)
{ {
check_writable(ctx, lhs); check_writable(ctx, lhs);
auto check_numeric = [](const std::vector<expr<Iterator>> &il) { assert(rhs.opt->is_vector());
for (auto &i : il) if (! lhs.opt->is_vector())
if (! i.numeric_type()) ctx->throw_exception("Cannot assign vector to a scalar", lhs.it_range);
i.throw_exception("Right side is not a numeric expression"); if (lhs.opt->type() != rhs.opt->type()) {
}; // Vector types are not compatible.
if (lhs.opt->is_scalar())
ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range);
ConfigOption *opt = const_cast<ConfigOption*>(lhs.opt);
switch (lhs.opt->type()) { switch (lhs.opt->type()) {
case coFloats: case coFloats:
{ ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", lhs.it_range);
check_numeric(il);
auto &out = static_cast<ConfigOptionFloats*>(opt)->values;
out.clear();
out.reserve(il.size());
for (auto &i : il)
out.emplace_back(i.as_d());
break;
}
case coInts: case coInts:
{ ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", lhs.it_range);
check_numeric(il);
auto &out = static_cast<ConfigOptionInts*>(opt)->values;
out.clear();
out.reserve(il.size());
for (auto& i : il)
out.emplace_back(i.as_i());
break;
}
case coStrings: case coStrings:
{ ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", lhs.it_range);
auto &out = static_cast<ConfigOptionStrings*>(opt)->values;
out.clear();
out.reserve(il.size());
for (auto &i : il)
out.emplace_back(i.to_string());
break;
}
case coBools: case coBools:
{ ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", lhs.it_range);
auto &out = static_cast<ConfigOptionBools*>(opt)->values;
out.clear();
out.reserve(il.size());
for (auto &i : il)
if (i.type() == expr<Iterator>::TYPE_BOOL)
out.emplace_back(i.b());
else
i.throw_exception("Right side is not a boolean expression");
break;
}
default: default:
assert(false); ctx->throw_exception("Left hand side / right hand side vectors are not compatible.", lhs.it_range);
} }
} }
const_cast<ConfigOption*>(lhs.opt)->set(rhs.opt);
template <typename Iterator>
static bool new_vector_variable_copy(
const MyContext *ctx,
bool global_variable,
NewOldVariable<Iterator> &output_variable,
const OptWithPos<Iterator> &src_variable)
{
if (! is_vector_variable_reference(src_variable))
// Skip parsing this branch, bactrack.
return false;
if (! output_variable.opt) {
if (one_of(src_variable.opt->type(), { coFloats, coInts, coStrings, coBools }))
output_variable.opt = src_variable.opt->clone();
else if (src_variable.opt->type() == coPercents)
output_variable.opt = new ConfigOptionFloats(static_cast<const ConfigOptionPercents*>(src_variable.opt)->values);
else
ctx->throw_exception("Duplicating this vector variable is not supported", src_variable.it_range);
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable);
}
switch (output_variable.opt->type()) {
case coFloats:
if (output_variable.opt->type() != coFloats)
ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
static_cast<ConfigOptionFloats*>(output_variable.opt)->values = static_cast<const ConfigOptionFloats*>(src_variable.opt)->values;
break;
case coInts:
if (output_variable.opt->type() != coInts)
ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
static_cast<ConfigOptionInts*>(output_variable.opt)->values = static_cast<const ConfigOptionInts*>(src_variable.opt)->values;
break;
case coStrings:
if (output_variable.opt->type() != coStrings)
ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
static_cast<ConfigOptionStrings*>(output_variable.opt)->values = static_cast<const ConfigOptionStrings*>(src_variable.opt)->values;
break;
case coBools:
if (output_variable.opt->type() != coBools)
ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
static_cast<ConfigOptionBools*>(output_variable.opt)->values = static_cast<const ConfigOptionBools*>(src_variable.opt)->values;
break;
default:
assert(false);
}
// Continue parsing.
return true;
} }
template <typename Iterator> template <typename Iterator>
@ -1445,49 +1378,53 @@ namespace client
} }
template <typename Iterator> template <typename Iterator>
static bool assign_vector_variable_copy( static bool vector_variable_new_from_copy(
const MyContext *ctx, const MyContext *ctx,
OptWithPos<Iterator> &lhs, bool global_variable,
const OptWithPos<Iterator> &src_variable) NewOldVariable<Iterator> &lhs,
const OptWithPos<Iterator> &rhs)
{ {
if (! is_vector_variable_reference(src_variable)) if (is_vector_variable_reference(rhs)) {
// Skip parsing this branch, bactrack. if (lhs.opt) {
return false; OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true };
copy_vector_variable_to_vector_variable(ctx, lhs_opt, rhs);
check_writable(ctx, lhs); } else {
// Clone the vector variable.
auto *opt = const_cast<ConfigOption*>(lhs.opt); std::unique_ptr<ConfigOption> opt_new;
switch (lhs.opt->type()) { if (one_of(rhs.opt->type(), { coFloats, coInts, coStrings, coBools }))
case coFloats: opt_new = std::unique_ptr<ConfigOption>(rhs.opt->clone());
if (lhs.opt->type() != coFloats) else if (rhs.opt->type() == coPercents)
ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", lhs.it_range); opt_new = std::make_unique<ConfigOptionFloats>(static_cast<const ConfigOptionPercents*>(rhs.opt)->values);
static_cast<ConfigOptionFloats*>(opt)->values = static_cast<const ConfigOptionFloats*>(src_variable.opt)->values; else
break; ctx->throw_exception("Duplicating this type of vector variable is not supported", rhs.it_range);
case coInts: const_cast<MyContext*>(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable);
if (lhs.opt->type() != coInts)
ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", lhs.it_range);
static_cast<ConfigOptionInts*>(opt)->values = static_cast<const ConfigOptionInts*>(src_variable.opt)->values;
break;
case coStrings:
if (lhs.opt->type() != coStrings)
ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", lhs.it_range);
static_cast<ConfigOptionStrings*>(opt)->values = static_cast<const ConfigOptionStrings*>(src_variable.opt)->values;
break;
case coBools:
if (lhs.opt->type() != coBools)
ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", lhs.it_range);
static_cast<ConfigOptionBools*>(opt)->values = static_cast<const ConfigOptionBools*>(src_variable.opt)->values;
break;
default:
assert(false);
} }
// Continue parsing. // Continue parsing.
return true; return true;
} else {
// Skip parsing this branch, bactrack.
return false;
}
} }
template <typename Iterator> template <typename Iterator>
static void new_vector_variable_initializer_list_append(std::vector<expr<Iterator>> &list, expr<Iterator> &expr) static bool vector_variable_assign_copy(
const MyContext *ctx,
OptWithPos<Iterator> &lhs,
const OptWithPos<Iterator> &rhs)
{
if (is_vector_variable_reference(rhs)) {
copy_vector_variable_to_vector_variable(ctx, lhs, rhs);
// Continue parsing.
return true;
} else {
// Skip parsing this branch, bactrack.
return false;
}
}
template <typename Iterator>
static void initializer_list_append(std::vector<expr<Iterator>> &list, expr<Iterator> &expr)
{ {
list.emplace_back(std::move(expr)); list.emplace_back(std::move(expr));
} }
@ -1910,36 +1847,36 @@ namespace client
variable_reference(_r1)[_a = _1] >> '=' > variable_reference(_r1)[_a = _1] >> '=' >
( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer.
(lit('(') > new_variable_initializer_list(_r1) > ')') (lit('(') > new_variable_initializer_list(_r1) > ')')
[px::bind(&MyContext::assign_vector_variable_initializer_list<Iterator>, _r1, _a, _1)] [px::bind(&MyContext::vector_variable_assign_initializer_list<Iterator>, _r1, _a, _1)]
// Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index.
// Only process such variable references, which return a naked vector variable. // Only process such variable references, which return a naked vector variable.
| variable_reference(_r1) | variable_reference(_r1)
[px::ref(qi::_pass) = px::bind(&MyContext::assign_vector_variable_copy<Iterator>, _r1, _a, _1)] [px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_assign_copy<Iterator>, _r1, _a, _1)]
// Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above. // Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above.
| conditional_expression(_r1) | conditional_expression(_r1)
[px::bind(&MyContext::assign_scalar_variable<Iterator>, _r1, _a, _1)] [px::bind(&MyContext::scalar_variable_assign_scalar_expression<Iterator>, _r1, _a, _1)]
| (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") | (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")")
[px::bind(&MyContext::assign_vector_variable_array<Iterator>, _r1, _a, _1, _2)] [px::bind(&MyContext::vector_variable_assign_array<Iterator>, _r1, _a, _1, _2)]
); );
new_variable_statement = new_variable_statement =
(kw["local"][_a = false] | kw["global"][_a = true]) > identifier[px::bind(&MyContext::new_old_variable<Iterator>, _r1, _a, _1, _b)] > lit('=') > (kw["local"][_a = false] | kw["global"][_a = true]) > identifier[px::bind(&MyContext::new_old_variable<Iterator>, _r1, _a, _1, _b)] > lit('=') >
( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer.
(lit('(') > new_variable_initializer_list(_r1) > ')') (lit('(') > new_variable_initializer_list(_r1) > ')')
[px::bind(&MyContext::new_vector_variable_initializer_list<Iterator>, _r1, _a, _b, _1)] [px::bind(&MyContext::vector_variable_new_from_initializer_list<Iterator>, _r1, _a, _b, _1)]
// Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index.
// Only process such variable references, which return a naked vector variable. // Only process such variable references, which return a naked vector variable.
| variable_reference(_r1) | variable_reference(_r1)
[px::ref(qi::_pass) = px::bind(&MyContext::new_vector_variable_copy<Iterator>, _r1, _a, _b, _1)] [px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_new_from_copy<Iterator>, _r1, _a, _b, _1)]
// Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above. // Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above.
| conditional_expression(_r1) | conditional_expression(_r1)
[px::bind(&MyContext::new_scalar_variable<Iterator>, _r1, _a, _b, _1)] [px::bind(&MyContext::scalar_variable_new_from_scalar_expression<Iterator>, _r1, _a, _b, _1)]
| (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") | (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")")
[px::bind(&MyContext::new_vector_variable_array<Iterator>, _r1, _a, _b, _1, _2)] [px::bind(&MyContext::vector_variable_new_from_array<Iterator>, _r1, _a, _b, _1, _2)]
); );
new_variable_initializer_list = new_variable_initializer_list =
conditional_expression(_r1)[px::bind(&MyContext::new_vector_variable_initializer_list_append<Iterator>, _val, _1)] >> conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append<Iterator>, _val, _1)] >>
*(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::new_vector_variable_initializer_list_append<Iterator>, _val, _1)]); *(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append<Iterator>, _val, _1)]);
struct FactorActions { struct FactorActions {
static void set_start_pos(Iterator &start_pos, expr<Iterator> &out) static void set_start_pos(Iterator &start_pos, expr<Iterator> &out)