The models, scripts, and results of the benchmarks performed for a Half Reification Journal paper
at develop 357 lines 13 kB view raw
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* 4 * Main authors: 5 * Guido Tack <guido.tack@monash.edu> 6 */ 7 8/* This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12/* This (main) file coordinates flattening and solving. 13 * The corresponding modules are flexibly plugged in 14 * as derived classes, prospectively from DLLs. 15 * A flattening module should provide MinZinc::GetFlattener() 16 * A solving module should provide an object of a class derived from SolverFactory. 17 * Need to get more flexible for multi-pass & multi-solving stuff TODO 18 */ 19 20#include <minizinc/file_utils.hh> 21#include <minizinc/json_parser.hh> 22#include <minizinc/parser.hh> 23#include <minizinc/prettyprinter.hh> 24 25#include <fstream> 26 27using namespace std; 28 29int mzn_yylex_init(void** scanner); 30void mzn_yyset_extra(void* user_defined, void* yyscanner); 31int mzn_yylex_destroy(void* scanner); 32 33namespace { 34// fastest way to read a file into a string (especially big files) 35// see: http://insanecoding.blogspot.be/2011/11/how-to-read-in-file-in-c.html 36std::string get_file_contents(std::ifstream& in) { 37 if (in) { 38 std::string contents; 39 in.seekg(0, std::ios::end); 40 contents.resize(static_cast<unsigned int>(in.tellg())); 41 in.seekg(0, std::ios::beg); 42 in.read(&contents[0], contents.size()); 43 in.close(); 44 if (!contents.empty() && contents[0] == '@') { 45 contents = MiniZinc::FileUtils::decode_base64(contents); 46 MiniZinc::FileUtils::inflate_string(contents); 47 } 48 return (contents); 49 } 50 throw(errno); 51} 52} // namespace 53 54namespace MiniZinc { 55 56std::string ParserState::canonicalFilename(const std::string& f) const { 57 if (FileUtils::is_absolute(f) || std::string(filename).empty()) { 58 return f; 59 } 60 for (const auto& ip : includePaths) { 61 std::string fullname = FileUtils::file_path(ip + "/" + f); 62 if (FileUtils::file_exists(fullname)) { 63 return fullname; 64 } 65 } 66 std::string parentPath = FileUtils::dir_name(filename); 67 if (parentPath.empty()) { 68 parentPath = "."; 69 } 70 std::string fullname = FileUtils::file_path(parentPath + "/" + f); 71 if (FileUtils::file_exists(fullname)) { 72 return fullname; 73 } 74 return f; 75} 76 77void parse(Env& env, Model*& model, const vector<string>& filenames, 78 const vector<string>& datafiles, const std::string& modelString, 79 const std::string& modelStringName, const vector<string>& ip, bool isFlatZinc, 80 bool ignoreStdlib, bool parseDocComments, bool verbose, ostream& err, 81 std::vector<SyntaxError>& syntaxErrors) { 82 vector<string> includePaths; 83 for (const auto& i : ip) { 84 includePaths.push_back(i); 85 } 86 87 vector<ParseWorkItem> files; 88 map<string, Model*> seenModels; 89 90 string workingDir = FileUtils::working_directory(); 91 92 if (!filenames.empty()) { 93 GCLock lock; 94 model->setFilename(FileUtils::base_name(filenames[0])); 95 if (FileUtils::is_absolute(filenames[0])) { 96 files.emplace_back(model, nullptr, "", filenames[0]); 97 } else { 98 files.emplace_back(model, nullptr, "", workingDir + "/" + filenames[0]); 99 } 100 101 for (unsigned int i = 1; i < filenames.size(); i++) { 102 GCLock lock; 103 string fullName = filenames[i]; 104 string baseName = FileUtils::base_name(filenames[i]); 105 if (!FileUtils::is_absolute(fullName)) { 106 fullName = FileUtils::file_path(workingDir + "/" + fullName); 107 } 108 bool isFzn = (baseName.compare(baseName.length() - 4, 4, ".fzn") == 0); 109 if (isFzn) { 110 files.emplace_back(model, nullptr, "", fullName); 111 } else { 112 auto* includedModel = new Model; 113 includedModel->setFilename(baseName); 114 files.emplace_back(includedModel, nullptr, "", fullName); 115 seenModels.insert(pair<string, Model*>(fullName, includedModel)); 116 Location loc(ASTString(filenames[i]), 0, 0, 0, 0); 117 auto* inc = new IncludeI(loc, includedModel->filename()); 118 inc->m(includedModel, true); 119 model->addItem(inc); 120 } 121 } 122 if (!modelString.empty()) { 123 auto* includedModel = new Model; 124 includedModel->setFilename(modelStringName); 125 files.emplace_back(includedModel, nullptr, modelString, modelStringName, false, true); 126 seenModels.insert(pair<string, Model*>(modelStringName, includedModel)); 127 Location loc(ASTString(modelStringName), 0, 0, 0, 0); 128 auto* inc = new IncludeI(loc, includedModel->filename()); 129 inc->m(includedModel, true); 130 model->addItem(inc); 131 } 132 } else if (!modelString.empty()) { 133 GCLock lock; 134 model->setFilename(modelStringName); 135 files.emplace_back(model, nullptr, modelString, modelStringName, false, true); 136 } 137 138 auto include_file = [&](const std::string& libname, bool builtin) { 139 GCLock lock; 140 auto* lib = new Model; 141 std::string fullname; 142 for (const auto& ip : includePaths) { 143 std::string n = FileUtils::file_path(ip + "/" + libname); 144 if (FileUtils::file_exists(n)) { 145 fullname = n; 146 break; 147 } 148 } 149 lib->setFilename(fullname); 150 files.emplace_back(lib, nullptr, "./", fullname, builtin); 151 seenModels.insert(pair<string, Model*>(fullname, lib)); 152 Location libloc(ASTString(model->filename()), 0, 0, 0, 0); 153 auto* libinc = new IncludeI(libloc, libname); 154 libinc->m(lib, true); 155 model->addItem(libinc); 156 }; 157 158 // TODO: It should be possible to use just flatzinc builtins instead of stdlib when parsing 159 // FlatZinc if (!isFlatZinc) { 160 if (!ignoreStdlib) { 161 include_file("solver_redefinitions.mzn", false); 162 include_file("stdlib.mzn", true); // Added last, so it is processed first 163 } 164 // } else { 165 // include_file("flatzincbuiltins.mzn", true); 166 // } 167 168 while (!files.empty()) { 169 GCLock lock; 170 ParseWorkItem& np = files.back(); 171 string parentPath = np.dirName; 172 Model* m = np.m; 173 bool isModelString = np.isModelString; 174 bool isSTDLib = np.isSTDLib; 175 IncludeI* np_ii = np.ii; 176 string f(np.fileName); 177 files.pop_back(); 178 179 std::string s; 180 std::string fullname; 181 std::string basename; 182 bool isFzn; 183 if (!isModelString) { 184 for (Model* p = m->parent(); p != nullptr; p = p->parent()) { 185 if (p->filename() == f) { 186 err << "Error: cyclic includes: " << std::endl; 187 for (Model* pe = m; pe != nullptr; pe = pe->parent()) { 188 err << " " << pe->filename() << std::endl; 189 } 190 goto error; 191 } 192 } 193 ifstream file; 194 if (FileUtils::is_absolute(f)) { 195 fullname = f; 196 basename = FileUtils::base_name(fullname); 197 if (FileUtils::file_exists(fullname)) { 198 file.open(FILE_PATH(fullname), std::ios::binary); 199 } 200 } 201 if (file.is_open() && 202 FileUtils::file_path(FileUtils::dir_name(fullname)) != FileUtils::file_path(workingDir) && 203 FileUtils::file_exists(workingDir + "/" + basename)) { 204 err << "Warning: file " << basename 205 << " included from library, but also exists in current working directory" << endl; 206 } 207 for (const auto& includePath : includePaths) { 208 std::string deprecatedName = includePath + "/" + basename + ".deprecated.mzn"; 209 if (FileUtils::file_exists(deprecatedName)) { 210 string deprecatedFullPath = FileUtils::file_path(deprecatedName); 211 string deprecatedBaseName = FileUtils::base_name(deprecatedFullPath); 212 string deprecatedDirName = FileUtils::dir_name(deprecatedFullPath); 213 auto* includedModel = new Model; 214 includedModel->setFilename(deprecatedName); 215 files.emplace_back(includedModel, nullptr, "", deprecatedName, isSTDLib, false); 216 seenModels.insert(pair<string, Model*>(deprecatedName, includedModel)); 217 Location loc(ASTString(deprecatedName), 0, 0, 0, 0); 218 auto* inc = new IncludeI(loc, includedModel->filename()); 219 inc->m(includedModel, true); 220 m->addItem(inc); 221 files.emplace_back(includedModel, inc, deprecatedDirName, deprecatedFullPath, isSTDLib, 222 false); 223 } 224 } 225 if (!file.is_open()) { 226 if (np_ii != nullptr) { 227 err << np_ii->loc().toString() << ":\n"; 228 err << "MiniZinc: error in include item, cannot open file '" << f << "'." << endl; 229 } else { 230 err << "Error: cannot open file '" << f << "'." << endl; 231 } 232 goto error; 233 } 234 if (verbose) { 235 std::cerr << "processing file '" << fullname << "'" << endl; 236 } 237 s = get_file_contents(file); 238 239 if (m->filepath().size() == 0) { 240 m->setFilepath(fullname); 241 } 242 isFzn = (fullname.compare(fullname.length() - 4, 4, ".fzn") == 0); 243 isFzn |= (fullname.compare(fullname.length() - 4, 4, ".ozn") == 0); 244 isFzn |= (fullname.compare(fullname.length() - 4, 4, ".szn") == 0); 245 isFzn |= (fullname.compare(fullname.length() - 4, 4, ".mzc") == 0); 246 } else { 247 isFzn = false; 248 fullname = f; 249 s = parentPath; 250 } 251 ParserState pp(fullname, s, err, includePaths, files, seenModels, m, false, isFzn, isSTDLib, 252 parseDocComments); 253 mzn_yylex_init(&pp.yyscanner); 254 mzn_yyset_extra(&pp, pp.yyscanner); 255 mzn_yyparse(&pp); 256 if (pp.yyscanner != nullptr) { 257 mzn_yylex_destroy(pp.yyscanner); 258 } 259 if (pp.hadError) { 260 for (const auto& syntaxError : pp.syntaxErrors) { 261 syntaxErrors.push_back(syntaxError); 262 } 263 goto error; 264 } 265 } 266 267 for (const auto& f : datafiles) { 268 GCLock lock; 269 if (f.size() >= 6 && f.substr(f.size() - 5, string::npos) == ".json") { 270 JSONParser jp(env.envi()); 271 jp.parse(model, f, true); 272 } else { 273 string s; 274 if (f.size() > 5 && f.substr(0, 5) == "cmd:/") { 275 s = f.substr(5); 276 } else { 277 std::ifstream file(FILE_PATH(f), std::ios::binary); 278 if (!FileUtils::file_exists(f) || !file.is_open()) { 279 err << "Error: cannot open data file '" << f << "'." << endl; 280 goto error; 281 } 282 if (verbose) { 283 std::cerr << "processing data file '" << f << "'" << endl; 284 } 285 s = get_file_contents(file); 286 } 287 288 ParserState pp(f, s, err, includePaths, files, seenModels, model, true, false, false, 289 parseDocComments); 290 mzn_yylex_init(&pp.yyscanner); 291 mzn_yyset_extra(&pp, pp.yyscanner); 292 mzn_yyparse(&pp); 293 if (pp.yyscanner != nullptr) { 294 mzn_yylex_destroy(pp.yyscanner); 295 } 296 if (pp.hadError) { 297 for (const auto& syntaxError : pp.syntaxErrors) { 298 syntaxErrors.push_back(syntaxError); 299 } 300 goto error; 301 } 302 } 303 } 304 305 return; 306error: 307 delete model; 308 model = nullptr; 309} 310 311Model* parse(Env& env, const vector<string>& filenames, const vector<string>& datafiles, 312 const string& textModel, const string& textModelName, 313 const vector<string>& includePaths, bool isFlatZinc, bool ignoreStdlib, 314 bool parseDocComments, bool verbose, ostream& err) { 315 if (filenames.empty() && textModel.empty()) { 316 err << "Error: no model given" << std::endl; 317 return nullptr; 318 } 319 320 Model* model; 321 { 322 GCLock lock; 323 model = new Model(); 324 } 325 std::vector<SyntaxError> se; 326 parse(env, model, filenames, datafiles, textModel, textModelName, includePaths, isFlatZinc, 327 ignoreStdlib, parseDocComments, verbose, err, se); 328 return model; 329} 330 331Model* parse_data(Env& env, Model* model, const vector<string>& datafiles, 332 const vector<string>& includePaths, bool isFlatZinc, bool ignoreStdlib, 333 bool parseDocComments, bool verbose, ostream& err) { 334 vector<string> filenames; 335 std::vector<SyntaxError> se; 336 parse(env, model, filenames, datafiles, "", "", includePaths, isFlatZinc, ignoreStdlib, 337 parseDocComments, verbose, err, se); 338 return model; 339} 340 341Model* parse_from_string(Env& env, const string& text, const string& filename, 342 const vector<string>& includePaths, bool isFlatZinc, bool ignoreStdlib, 343 bool parseDocComments, bool verbose, ostream& err, 344 std::vector<SyntaxError>& syntaxErrors) { 345 vector<string> filenames; 346 vector<string> datafiles; 347 Model* model; 348 { 349 GCLock lock; 350 model = new Model(); 351 } 352 parse(env, model, filenames, datafiles, text, filename, includePaths, isFlatZinc, ignoreStdlib, 353 parseDocComments, verbose, err, syntaxErrors); 354 return model; 355} 356 357} // namespace MiniZinc