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