this repo has no description
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 56void parse(Env& env, Model*& model, const vector<string>& filenames, 57 const vector<string>& datafiles, const std::string& modelString, 58 const std::string& modelStringName, const vector<string>& ip, bool isFlatZinc, 59 bool ignoreStdlib, bool parseDocComments, bool verbose, ostream& err, 60 std::vector<SyntaxError>& syntaxErrors) { 61 vector<string> includePaths; 62 for (const auto& i : ip) { 63 includePaths.push_back(i); 64 } 65 66 vector<ParseWorkItem> files; 67 map<string, Model*> seenModels; 68 69 string workingDir = FileUtils::working_directory(); 70 71 if (!filenames.empty()) { 72 GCLock lock; 73 model->setFilename(FileUtils::base_name(filenames[0])); 74 if (FileUtils::is_absolute(filenames[0])) { 75 files.emplace_back(model, nullptr, "", filenames[0]); 76 } else { 77 files.emplace_back(model, nullptr, "", workingDir + "/" + filenames[0]); 78 } 79 80 for (unsigned int i = 1; i < filenames.size(); i++) { 81 GCLock lock; 82 string fullName = filenames[i]; 83 string baseName = FileUtils::base_name(filenames[i]); 84 if (!FileUtils::is_absolute(fullName)) { 85 fullName = workingDir + "/" + fullName; 86 } 87 bool isFzn = (baseName.compare(baseName.length() - 4, 4, ".fzn") == 0); 88 if (isFzn) { 89 files.emplace_back(model, nullptr, "", fullName); 90 } else { 91 auto* includedModel = new Model; 92 includedModel->setFilename(baseName); 93 files.emplace_back(includedModel, nullptr, "", fullName); 94 seenModels.insert(pair<string, Model*>(baseName, includedModel)); 95 Location loc(ASTString(filenames[i]), 0, 0, 0, 0); 96 auto* inc = new IncludeI(loc, includedModel->filename()); 97 inc->m(includedModel, true); 98 model->addItem(inc); 99 } 100 } 101 if (!modelString.empty()) { 102 auto* includedModel = new Model; 103 includedModel->setFilename(modelStringName); 104 files.emplace_back(includedModel, nullptr, modelString, modelStringName, false, true); 105 seenModels.insert(pair<string, Model*>(modelStringName, includedModel)); 106 Location loc(ASTString(modelStringName), 0, 0, 0, 0); 107 auto* inc = new IncludeI(loc, includedModel->filename()); 108 inc->m(includedModel, true); 109 model->addItem(inc); 110 } 111 } else if (!modelString.empty()) { 112 GCLock lock; 113 model->setFilename(modelStringName); 114 files.emplace_back(model, nullptr, modelString, modelStringName, false, true); 115 } 116 117 auto include_file = [&](const std::string& libname, bool builtin) { 118 GCLock lock; 119 auto* lib = new Model; 120 lib->setFilename(libname); 121 files.emplace_back(lib, nullptr, "./", libname, builtin); 122 seenModels.insert(pair<string, Model*>(libname, lib)); 123 Location libloc(ASTString(model->filename()), 0, 0, 0, 0); 124 auto* libinc = new IncludeI(libloc, lib->filename()); 125 libinc->m(lib, true); 126 model->addItem(libinc); 127 }; 128 129 // TODO: It should be possible to use just flatzinc builtins instead of stdlib when parsing 130 // FlatZinc if (!isFlatZinc) { 131 if (!ignoreStdlib) { 132 include_file("solver_redefinitions.mzn", false); 133 include_file("stdlib.mzn", true); // Added last, so it is processed first 134 } 135 // } else { 136 // include_file("flatzincbuiltins.mzn", true); 137 // } 138 139 while (!files.empty()) { 140 GCLock lock; 141 ParseWorkItem& np = files.back(); 142 string parentPath = np.dirName; 143 Model* m = np.m; 144 bool isModelString = np.isModelString; 145 bool isSTDLib = np.isSTDLib; 146 IncludeI* np_ii = np.ii; 147 string f(np.fileName); 148 files.pop_back(); 149 150 std::string s; 151 std::string fullname; 152 bool isFzn; 153 if (!isModelString) { 154 for (Model* p = m->parent(); p != nullptr; p = p->parent()) { 155 if (p->filename() == f) { 156 err << "Error: cyclic includes: " << std::endl; 157 for (Model* pe = m; pe != nullptr; pe = pe->parent()) { 158 err << " " << pe->filename() << std::endl; 159 } 160 goto error; 161 } 162 } 163 ifstream file; 164 if (FileUtils::is_absolute(f) || parentPath.empty()) { 165 fullname = f; 166 if (FileUtils::file_exists(fullname)) { 167 file.open(FILE_PATH(fullname), std::ios::binary); 168 } 169 } else { 170 includePaths.push_back(parentPath); 171 unsigned int i = 0; 172 for (; i < includePaths.size(); i++) { 173 fullname = includePaths[i] + "/" + f; 174 if (FileUtils::file_exists(fullname)) { 175 file.open(FILE_PATH(fullname), std::ios::binary); 176 if (file.is_open()) { 177 break; 178 } 179 } 180 } 181 if (file.is_open() && i < includePaths.size() - 1 && parentPath == workingDir && 182 FileUtils::file_path(includePaths[i], workingDir) != FileUtils::file_path(workingDir) && 183 FileUtils::file_exists(workingDir + "/" + f)) { 184 err << "Warning: file " << f 185 << " included from library, but also exists in current working directory" << endl; 186 } 187 for (; i < includePaths.size(); i++) { 188 std::string deprecatedName = includePaths[i] + "/" + f + ".deprecated.mzn"; 189 if (FileUtils::file_exists(deprecatedName)) { 190 string deprecatedBaseName = FileUtils::base_name(deprecatedName); 191 auto* includedModel = new Model; 192 includedModel->setFilename(deprecatedBaseName); 193 files.emplace_back(includedModel, nullptr, "", deprecatedName, isSTDLib, false); 194 seenModels.insert(pair<string, Model*>(deprecatedBaseName, includedModel)); 195 Location loc(ASTString(deprecatedName), 0, 0, 0, 0); 196 auto* inc = new IncludeI(loc, includedModel->filename()); 197 inc->m(includedModel, true); 198 m->addItem(inc); 199 files.emplace_back(includedModel, inc, deprecatedName, deprecatedBaseName, isSTDLib, 200 false); 201 } 202 } 203 includePaths.pop_back(); 204 } 205 if (!file.is_open()) { 206 if (np_ii != nullptr) { 207 err << np_ii->loc().toString() << ":\n"; 208 err << "MiniZinc: error in include item, cannot open file '" << f << "'." << endl; 209 } else { 210 err << "Error: cannot open file '" << f << "'." << endl; 211 } 212 goto error; 213 } 214 if (verbose) { 215 std::cerr << "processing file '" << fullname << "'" << endl; 216 } 217 s = get_file_contents(file); 218 219 if (m->filepath().size() == 0) { 220 m->setFilepath(fullname); 221 } 222 isFzn = (fullname.compare(fullname.length() - 4, 4, ".fzn") == 0); 223 isFzn |= (fullname.compare(fullname.length() - 4, 4, ".ozn") == 0); 224 isFzn |= (fullname.compare(fullname.length() - 4, 4, ".szn") == 0); 225 isFzn |= (fullname.compare(fullname.length() - 4, 4, ".mzc") == 0); 226 } else { 227 isFzn = false; 228 fullname = f; 229 s = parentPath; 230 } 231 ParserState pp(fullname, s, err, files, seenModels, m, false, isFzn, isSTDLib, 232 parseDocComments); 233 mzn_yylex_init(&pp.yyscanner); 234 mzn_yyset_extra(&pp, pp.yyscanner); 235 mzn_yyparse(&pp); 236 if (pp.yyscanner != nullptr) { 237 mzn_yylex_destroy(pp.yyscanner); 238 } 239 if (pp.hadError) { 240 for (const auto& syntaxError : pp.syntaxErrors) { 241 syntaxErrors.push_back(syntaxError); 242 } 243 goto error; 244 } 245 } 246 247 for (const auto& f : datafiles) { 248 GCLock lock; 249 if (f.size() >= 6 && f.substr(f.size() - 5, string::npos) == ".json") { 250 JSONParser jp(env.envi()); 251 jp.parse(model, f, true); 252 } else { 253 string s; 254 if (f.size() > 5 && f.substr(0, 5) == "cmd:/") { 255 s = f.substr(5); 256 } else { 257 std::ifstream file(FILE_PATH(f), std::ios::binary); 258 if (!FileUtils::file_exists(f) || !file.is_open()) { 259 err << "Error: cannot open data file '" << f << "'." << endl; 260 goto error; 261 } 262 if (verbose) { 263 std::cerr << "processing data file '" << f << "'" << endl; 264 } 265 s = get_file_contents(file); 266 } 267 268 ParserState pp(f, s, err, files, seenModels, model, true, false, false, parseDocComments); 269 mzn_yylex_init(&pp.yyscanner); 270 mzn_yyset_extra(&pp, pp.yyscanner); 271 mzn_yyparse(&pp); 272 if (pp.yyscanner != nullptr) { 273 mzn_yylex_destroy(pp.yyscanner); 274 } 275 if (pp.hadError) { 276 for (const auto& syntaxError : pp.syntaxErrors) { 277 syntaxErrors.push_back(syntaxError); 278 } 279 goto error; 280 } 281 } 282 } 283 284 return; 285error: 286 delete model; 287 model = nullptr; 288} 289 290Model* parse(Env& env, const vector<string>& filenames, const vector<string>& datafiles, 291 const string& textModel, const string& textModelName, 292 const vector<string>& includePaths, bool isFlatZinc, bool ignoreStdlib, 293 bool parseDocComments, bool verbose, ostream& err) { 294 if (filenames.empty() && textModel.empty()) { 295 err << "Error: no model given" << std::endl; 296 return nullptr; 297 } 298 299 Model* model; 300 { 301 GCLock lock; 302 model = new Model(); 303 } 304 std::vector<SyntaxError> se; 305 parse(env, model, filenames, datafiles, textModel, textModelName, includePaths, isFlatZinc, 306 ignoreStdlib, parseDocComments, verbose, err, se); 307 return model; 308} 309 310Model* parse_data(Env& env, Model* model, const vector<string>& datafiles, 311 const vector<string>& includePaths, bool isFlatZinc, bool ignoreStdlib, 312 bool parseDocComments, bool verbose, ostream& err) { 313 vector<string> filenames; 314 std::vector<SyntaxError> se; 315 parse(env, model, filenames, datafiles, "", "", includePaths, isFlatZinc, ignoreStdlib, 316 parseDocComments, verbose, err, se); 317 return model; 318} 319 320Model* parse_from_string(Env& env, const string& text, const string& filename, 321 const vector<string>& includePaths, bool isFlatZinc, bool ignoreStdlib, 322 bool parseDocComments, bool verbose, ostream& err, 323 std::vector<SyntaxError>& syntaxErrors) { 324 vector<string> filenames; 325 vector<string> datafiles; 326 Model* model; 327 { 328 GCLock lock; 329 model = new Model(); 330 } 331 parse(env, model, filenames, datafiles, text, filename, includePaths, isFlatZinc, ignoreStdlib, 332 parseDocComments, verbose, err, syntaxErrors); 333 return model; 334} 335 336} // namespace MiniZinc