Solver settings

An EXS solver can carry application data alongside its operator registry and evaluation steps. Derive a settings type from snt::exs::BaseSettings and pass it as the second template argument to Solver. The solver stores a copy of this object and passes a pointer to it when it parses an atom or applies an operator.

The SettingsSolver example uses settings for a symbolic value and an indexed list of options:

#include <array>
#include <string>
#include <utility>
#include <snt/exs/solver.h>

struct Settings : snt::exs::BaseSettings {
    std::string symbol;
    int value;
    std::array<int, 5> options;

    Settings(std::string s, int v, std::array<int, 5> o)
        : symbol(std::move(s)), value(v), options(o) {}
};

Settings settings{"?", 5, {1, 2, 3, 4, 5}};
snt::exs::Solver<CustomAtom, Settings> solver(operators, steps, settings);
auto result = solver.eval("2 + ? - {3}");  // 3

Here CustomAtom, operators, and steps are defined by the example. During tokenization, CustomAtom::from_string receives the settings pointer. It turns ? into the configured value 5 and parses other operands as integers:

int CustomAtom::from_string(std::string& text,
                            snt::exs::BaseSettings* base) {
    auto* settings = static_cast<Settings*>(base);
    return text == settings->symbol ? settings->value : std::stoi(text);
}

The example’s OperatorSelect implements the two-argument operate_group callback. It reads the same settings object and replaces {3} with options.at(3), which is 4. The expression therefore evaluates to 2 + 5 - 4 = 3.

void OperatorSelect::operate_group(snt::exs::TokenListBase* tokens,
                                   snt::exs::BaseSettings* base) {
    auto* settings = static_cast<Settings*>(base);
    auto argument = tokens->get_left();
    auto* atom = static_cast<CustomAtom*>(argument.atom);
    atom->value = settings->options.at(atom->value);
    tokens->put_left(argument);
}

The Solver<CustomAtom, Settings> template argument ensures that these callbacks receive the intended settings type. Use solver.set_settings(new_settings) to replace the stored settings before a later evaluation. For the complete operator registry, evaluation steps, and atom implementation, see the SettingsSolver source.