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_yyparse(void*);
32int mzn_yylex_destroy(void* scanner);
33
34namespace {
35// fastest way to read a file into a string (especially big files)
36// see: http://insanecoding.blogspot.be/2011/11/how-to-read-in-file-in-c.html
37std::string get_file_contents(std::ifstream& in) {
38 if (in) {
39 std::string contents;
40 in.seekg(0, std::ios::end);
41 contents.resize(static_cast<unsigned int>(in.tellg()));
42 in.seekg(0, std::ios::beg);
43 in.read(&contents[0], contents.size());
44 in.close();
45 if (contents.size() > 0 && contents[0] == '@') {
46 contents = MiniZinc::FileUtils::decodeBase64(contents);
47 MiniZinc::FileUtils::inflateString(contents);
48 }
49 return (contents);
50 }
51 throw(errno);
52}
53} // namespace
54
55namespace MiniZinc {
56
57void parse(Env& env, Model*& model, const vector<string>& filenames,
58 const vector<string>& datafiles, const std::string& modelString,
59 const std::string& modelStringName, const vector<string>& ip, bool ignoreStdlib,
60 bool parseDocComments, bool verbose, ostream& err,
61 std::vector<SyntaxError>& syntaxErrors) {
62 vector<string> includePaths;
63 for (unsigned int i = 0; i < ip.size(); i++) includePaths.push_back(ip[i]);
64
65 vector<ParseWorkItem> files;
66 map<string, Model*> seenModels;
67
68 string workingDir = FileUtils::working_directory();
69
70 if (filenames.size() > 0) {
71 GCLock lock;
72 model->setFilename(FileUtils::base_name(filenames[0]));
73 if (FileUtils::is_absolute(filenames[0])) {
74 files.push_back(ParseWorkItem(model, NULL, "", filenames[0]));
75 } else {
76 files.push_back(ParseWorkItem(model, NULL, "", workingDir + "/" + filenames[0]));
77 }
78
79 for (unsigned int i = 1; i < filenames.size(); i++) {
80 GCLock lock;
81 string fullName = filenames[i];
82 string baseName = FileUtils::base_name(filenames[i]);
83 if (!FileUtils::is_absolute(fullName)) fullName = workingDir + "/" + fullName;
84 bool isFzn = (baseName.compare(baseName.length() - 4, 4, ".fzn") == 0);
85 if (isFzn) {
86 files.push_back(ParseWorkItem(model, NULL, "", fullName));
87 } else {
88 Model* includedModel = new Model;
89 includedModel->setFilename(baseName);
90 files.push_back(ParseWorkItem(includedModel, NULL, "", fullName));
91 seenModels.insert(pair<string, Model*>(baseName, includedModel));
92 Location loc(ASTString(filenames[i]), 0, 0, 0, 0);
93 IncludeI* inc = new IncludeI(loc, includedModel->filename());
94 inc->m(includedModel, true);
95 model->addItem(inc);
96 }
97 }
98 if (!modelString.empty()) {
99 Model* includedModel = new Model;
100 includedModel->setFilename(modelStringName);
101 files.push_back(ParseWorkItem(includedModel, NULL, modelString, modelStringName, true));
102 seenModels.insert(pair<string, Model*>(modelStringName, includedModel));
103 Location loc(ASTString(modelStringName), 0, 0, 0, 0);
104 IncludeI* inc = new IncludeI(loc, includedModel->filename());
105 inc->m(includedModel, true);
106 model->addItem(inc);
107 }
108 } else if (!modelString.empty()) {
109 GCLock lock;
110 model->setFilename(modelStringName);
111 files.push_back(ParseWorkItem(model, NULL, modelString, modelStringName, true));
112 }
113
114 if (!ignoreStdlib) {
115 GCLock lock;
116 Model* stdlib = new Model;
117 stdlib->setFilename("stdlib.mzn");
118 files.push_back(ParseWorkItem(stdlib, NULL, "./", "stdlib.mzn"));
119 seenModels.insert(pair<string, Model*>("stdlib.mzn", stdlib));
120 Location stdlibloc(ASTString(model->filename()), 0, 0, 0, 0);
121 IncludeI* stdlibinc = new IncludeI(stdlibloc, stdlib->filename());
122 stdlibinc->m(stdlib, true);
123 model->addItem(stdlibinc);
124 }
125
126 while (!files.empty()) {
127 GCLock lock;
128 ParseWorkItem& np = files.back();
129 string parentPath = np.dirName;
130 Model* m = np.m;
131 bool isModelString = np.isModelString;
132 IncludeI* np_ii = np.ii;
133 string f(np.fileName);
134 files.pop_back();
135
136 std::string s;
137 std::string fullname;
138 bool isFzn;
139 if (!isModelString) {
140 for (Model* p = m->parent(); p; p = p->parent()) {
141 if (f == p->filename().c_str()) {
142 err << "Error: cyclic includes: " << std::endl;
143 for (Model* pe = m; pe; pe = pe->parent()) {
144 err << " " << pe->filename() << std::endl;
145 }
146 goto error;
147 }
148 }
149 ifstream file;
150 if (FileUtils::is_absolute(f) || parentPath == "") {
151 if (filenames.size() == 0) {
152 err << "Internal error." << endl;
153 goto error;
154 }
155 fullname = f;
156 if (FileUtils::file_exists(fullname)) {
157 file.open(fullname.c_str(), std::ios::binary);
158 }
159 } else {
160 includePaths.push_back(parentPath);
161 unsigned int i = 0;
162 for (; i < includePaths.size(); i++) {
163 fullname = includePaths[i] + "/" + f;
164 if (FileUtils::file_exists(fullname)) {
165 file.open(fullname.c_str(), std::ios::binary);
166 if (file.is_open()) break;
167 }
168 }
169 if (file.is_open() && i < includePaths.size() - 1 && parentPath == workingDir &&
170 FileUtils::file_path(includePaths[i], workingDir) != FileUtils::file_path(workingDir) &&
171 FileUtils::file_exists(workingDir + "/" + f)) {
172 err << "Warning: file " << f
173 << " included from library, but also exists in current working directory" << endl;
174 }
175 includePaths.pop_back();
176 }
177 if (!file.is_open()) {
178 if (np_ii) {
179 err << np_ii->loc().toString() << ":\n";
180 err << "MiniZinc: error in include item, cannot open file '" << f << "'." << endl;
181 } else {
182 err << "Error: cannot open file '" << f << "'." << endl;
183 }
184 goto error;
185 }
186 if (verbose) std::cerr << "processing file '" << fullname << "'" << endl;
187 s = get_file_contents(file);
188
189 if (m->filepath().size() == 0) m->setFilepath(fullname);
190 isFzn = (fullname.compare(fullname.length() - 4, 4, ".fzn") == 0);
191 isFzn |= (fullname.compare(fullname.length() - 4, 4, ".ozn") == 0);
192 isFzn |= (fullname.compare(fullname.length() - 4, 4, ".szn") == 0);
193 isFzn |= (fullname.compare(fullname.length() - 4, 4, ".mzc") == 0);
194 } else {
195 isFzn = false;
196 fullname = f;
197 s = parentPath;
198 }
199 ParserState pp(fullname, s, err, files, seenModels, m, false, isFzn, parseDocComments);
200 mzn_yylex_init(&pp.yyscanner);
201 mzn_yyset_extra(&pp, pp.yyscanner);
202 mzn_yyparse(&pp);
203 if (pp.yyscanner) mzn_yylex_destroy(pp.yyscanner);
204 if (pp.hadError) {
205 for (unsigned int i = 0; i < pp.syntaxErrors.size(); i++)
206 syntaxErrors.push_back(pp.syntaxErrors[i]);
207 goto error;
208 }
209 }
210
211 for (unsigned int i = 0; i < datafiles.size(); i++) {
212 GCLock lock;
213 string f = datafiles[i];
214 if (f.size() >= 6 && f.substr(f.size() - 5, string::npos) == ".json") {
215 JSONParser jp(env.envi());
216 jp.parse(model, f);
217 } else {
218 string s;
219 if (f.size() > 5 && f.substr(0, 5) == "cmd:/") {
220 s = f.substr(5);
221 } else {
222 std::ifstream file;
223 file.open(f.c_str(), std::ios::binary);
224 if (!FileUtils::file_exists(f) || !file.is_open()) {
225 err << "Error: cannot open data file '" << f << "'." << endl;
226 goto error;
227 }
228 if (verbose) std::cerr << "processing data file '" << f << "'" << endl;
229 s = get_file_contents(file);
230 }
231
232 ParserState pp(f, s, err, files, seenModels, model, true, false, parseDocComments);
233 mzn_yylex_init(&pp.yyscanner);
234 mzn_yyset_extra(&pp, pp.yyscanner);
235 mzn_yyparse(&pp);
236 if (pp.yyscanner) mzn_yylex_destroy(pp.yyscanner);
237 if (pp.hadError) {
238 for (unsigned int i = 0; i < pp.syntaxErrors.size(); i++)
239 syntaxErrors.push_back(pp.syntaxErrors[i]);
240 goto error;
241 }
242 }
243 }
244
245 return;
246error:
247 delete model;
248 model = NULL;
249}
250
251Model* parse(Env& env, const vector<string>& filenames, const vector<string>& datafiles,
252 const string& textModel, const string& textModelName, const vector<string>& ip,
253 bool ignoreStdlib, bool parseDocComments, bool verbose, ostream& err) {
254 if (filenames.empty() && textModel.empty()) {
255 err << "Error: no model given" << std::endl;
256 return NULL;
257 }
258
259 Model* model;
260 {
261 GCLock lock;
262 model = new Model();
263 }
264 std::vector<SyntaxError> se;
265 parse(env, model, filenames, datafiles, textModel, textModelName, ip, ignoreStdlib,
266 parseDocComments, verbose, err, se);
267 return model;
268}
269
270Model* parseData(Env& env, Model* model, const vector<string>& datafiles,
271 const vector<string>& includePaths, bool ignoreStdlib, bool parseDocComments,
272 bool verbose, ostream& err) {
273 vector<string> filenames;
274 std::vector<SyntaxError> se;
275 parse(env, model, filenames, datafiles, "", "", includePaths, ignoreStdlib, parseDocComments,
276 verbose, err, se);
277 return model;
278}
279
280Model* parseFromString(Env& env, const string& text, const string& filename,
281 const vector<string>& ip, bool ignoreStdlib, bool parseDocComments,
282 bool verbose, ostream& err, std::vector<SyntaxError>& syntaxErrors) {
283 vector<string> filenames;
284 vector<string> datafiles;
285 Model* model;
286 {
287 GCLock lock;
288 model = new Model();
289 }
290 parse(env, model, filenames, datafiles, text, filename, ip, ignoreStdlib, parseDocComments,
291 verbose, err, syntaxErrors);
292 return model;
293}
294
295} // namespace MiniZinc