Skip to content

Commit 42d1beb

Browse files
committed
Restore variant parsing for JS/python bindings
1 parent 5ff1f4d commit 42d1beb

File tree

4 files changed

+88
-23
lines changed

4 files changed

+88
-23
lines changed

src/ffishjs.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,9 +480,11 @@ namespace ffish {
480480
}
481481

482482
void load_variant_config(std::string variantInitContent) {
483-
(void)variantInitContent;
483+
std::stringstream ss(variantInitContent);
484484
if (!Board::sfInitialized)
485485
initialize_stockfish();
486+
variants.parse_istream<false>(ss);
487+
Options["UCI_Variant"].set_combo(variants.get_keys());
486488
Board::sfInitialized = true;
487489
}
488490

src/pyffish.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ extern "C" PyObject* pyffish_loadVariantConfig(PyObject* self, PyObject *args) {
100100
const char *config;
101101
if (!PyArg_ParseTuple(args, "s", &config))
102102
return NULL;
103-
(void)config;
103+
std::stringstream ss(config);
104+
variants.parse_istream<false>(ss);
105+
Options["UCI_Variant"].set_combo(variants.get_keys());
104106
Py_RETURN_NONE;
105107
}
106108

src/variant.cpp

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <sstream>
2323

2424
#include "piece.h"
25+
#include "parser.h"
2526
#include "variant.h"
2627

2728
using std::string;
@@ -86,6 +87,83 @@ void VariantMap::init() {
8687
add("spell-chess", spell_chess_variant());
8788
}
8889

90+
/// VariantMap::parse_istream reads variants from an INI-style configuration input stream.
91+
92+
template <bool DoCheck>
93+
void VariantMap::parse_istream(std::istream& file) {
94+
std::string variant, variant_template, key, value, input;
95+
while (file.peek() != '[' && std::getline(file, input)) {}
96+
97+
std::vector<std::string> varsToErase = {};
98+
while (file.get() && std::getline(std::getline(file, variant, ']'), input))
99+
{
100+
// Extract variant template, if specified
101+
if (!std::getline(std::getline(std::stringstream(variant), variant, ':'), variant_template))
102+
variant_template = "";
103+
104+
// Read variant rules
105+
Config attribs = {};
106+
while (file.peek() != '[' && std::getline(file, input))
107+
{
108+
if (!input.empty() && input.back() == '\r')
109+
input.pop_back();
110+
std::stringstream ss(input);
111+
if (ss.peek() != ';' && ss.peek() != '#')
112+
{
113+
if (DoCheck && !input.empty() && input.find('=') == std::string::npos)
114+
std::cerr << "Invalid syntax: '" << input << "'." << std::endl;
115+
if (std::getline(std::getline(ss, key, '=') >> std::ws, value) && !key.empty())
116+
attribs[key.erase(key.find_last_not_of(" ") + 1)] = value;
117+
}
118+
}
119+
120+
// Create variant
121+
if (variants.find(variant) != variants.end())
122+
std::cerr << "Variant '" << variant << "' already exists." << std::endl;
123+
else if (!variant_template.empty() && variants.find(variant_template) == variants.end())
124+
std::cerr << "Variant template '" << variant_template << "' does not exist." << std::endl;
125+
else
126+
{
127+
if (DoCheck)
128+
std::cerr << "Parsing variant: " << variant << std::endl;
129+
Variant* v = !variant_template.empty() ? VariantParser<DoCheck>(attribs).parse((new Variant(*variants.find(variant_template)->second))->init())
130+
: VariantParser<DoCheck>(attribs).parse();
131+
if (v->maxFile <= FILE_MAX && v->maxRank <= RANK_MAX)
132+
{
133+
add(variant, v);
134+
// In order to allow inheritance, we need to temporarily add configured variants
135+
// even when only checking them, but we remove them later after parsing is finished.
136+
if (DoCheck)
137+
varsToErase.push_back(variant);
138+
}
139+
else
140+
delete v;
141+
}
142+
}
143+
// Clean up temporary variants
144+
for (std::string tempVar : varsToErase)
145+
{
146+
delete variants[tempVar];
147+
variants.erase(tempVar);
148+
}
149+
}
150+
151+
/// VariantMap::parse reads variants from an INI-style configuration file.
152+
153+
template <bool DoCheck>
154+
void VariantMap::parse(std::string path) {
155+
if (path.empty() || path == "<empty>")
156+
return;
157+
std::ifstream file(path);
158+
if (!file.is_open())
159+
{
160+
std::cerr << "Unable to open file " << path << std::endl;
161+
return;
162+
}
163+
parse_istream<DoCheck>(file);
164+
file.close();
165+
}
166+
89167
// Pre-calculate derived properties
90168
Variant* Variant::conclude() {
91169
// Enforce consistency to allow runtime optimizations
@@ -300,7 +378,7 @@ std::vector<std::string> VariantMap::get_keys() {
300378
// VariantPath parsing entry points.
301379
template void VariantMap::parse_istream<true>(std::istream&);
302380
template void VariantMap::parse_istream<false>(std::istream&);
303-
template void VariantMap::parse<true>(const std::string&);
304-
template void VariantMap::parse<false>(const std::string&);
381+
template void VariantMap::parse<true>(std::string);
382+
template void VariantMap::parse<false>(std::string);
305383

306384
} // namespace Stockfish

src/variant.h

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -249,32 +249,15 @@ struct Variant {
249249
class VariantMap : public std::map<std::string, const Variant*> {
250250
public:
251251
void init();
252-
// Spell-only build: keep parsing entry points as no-ops for link compatibility
253-
// with UCI VariantPath handling in other build configurations.
254-
template <bool DoCheck>
255-
void parse_istream(std::istream& file);
256-
template <bool DoCheck>
257-
void parse(const std::string& data);
252+
template <bool DoCheck> void parse(std::string path);
253+
template <bool DoCheck> void parse_istream(std::istream& file);
258254
void clear_all();
259255
std::vector<std::string> get_keys();
260256

261257
private:
262258
void add(std::string s, Variant* v);
263259
};
264260

265-
template <bool DoCheck>
266-
inline void VariantMap::parse_istream(std::istream& file) {
267-
(void)file;
268-
// No-op: variants are hardcoded in this build.
269-
// If DoCheck is true, callers expect validation side effects; none apply here.
270-
}
271-
272-
template <bool DoCheck>
273-
inline void VariantMap::parse(const std::string& data) {
274-
std::istringstream ss(data);
275-
parse_istream<DoCheck>(ss);
276-
}
277-
278261
extern VariantMap variants;
279262

280263
} // namespace Stockfish

0 commit comments

Comments
 (0)