1 |
#include <Python.h> |
2 |
|
3 |
#include <iostream> |
4 |
#include <stdexcept> |
5 |
#include <sstream> |
6 |
using namespace std; |
7 |
|
8 |
#undef NDEBUG |
9 |
|
10 |
#include "config.h" |
11 |
|
12 |
extern "C"{ |
13 |
#include <ascend/general/platform.h> |
14 |
|
15 |
#include <ascend/general/list.h> |
16 |
#include <ascend/compiler/ascCompiler.h> |
17 |
|
18 |
/* #include <compiler/redirectFile.h> */ |
19 |
#include <ascend/compiler/prototype.h> |
20 |
#include <ascend/compiler/dump.h> |
21 |
#include <ascend/compiler/childio.h> |
22 |
#include <ascend/compiler/type_desc.h> |
23 |
#include <ascend/compiler/typedef.h> |
24 |
#include <ascend/compiler/library.h> |
25 |
#include <ascend/system/slv_types.h> |
26 |
#include <ascend/system/system.h> |
27 |
#include <ascend/utilities/ascEnvVar.h> |
28 |
#include <ascend/compiler/symtab.h> |
29 |
#include <ascend/general/table.h> |
30 |
#include <ascend/compiler/instance_enum.h> |
31 |
#include <ascend/compiler/notate.h> |
32 |
#include <ascend/compiler/simlist.h> |
33 |
#include <ascend/compiler/parser.h> |
34 |
#include <ascend/utilities/error.h> |
35 |
#include <ascend/general/env.h> |
36 |
#include <ascend/compiler/importhandler.h> |
37 |
} |
38 |
|
39 |
#include "library.h" |
40 |
#include "simulation.h" |
41 |
#include "solver.h" |
42 |
|
43 |
Library::Library(const char *defaultpath){ |
44 |
static int have_init; |
45 |
if(!have_init){ |
46 |
//cerr << "Initialising ASCEND library..." << endl; |
47 |
|
48 |
#ifdef REIMPLEMENT_STREAMS |
49 |
Asc_RedirectCompilerDefault(); // Ensure that error message reach stderr |
50 |
#endif |
51 |
|
52 |
Asc_CompilerInit(1); |
53 |
env_import(ASC_ENV_LIBRARY,getenv,Asc_PutEnv); |
54 |
env_import(ASC_ENV_SOLVERS,getenv,Asc_PutEnv); |
55 |
|
56 |
char *x = Asc_GetEnv(ASC_ENV_LIBRARY); |
57 |
if(x==NULL || strcmp(x,"")==0){ |
58 |
if(defaultpath==NULL){ |
59 |
ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"Using default " |
60 |
ASC_ENV_LIBRARY " = '" DEFAULT_ASCENDLIBRARY "'" |
61 |
); |
62 |
defaultpath = DEFAULT_ASCENDLIBRARY; |
63 |
} |
64 |
|
65 |
string s = string(ASC_ENV_LIBRARY "=") + defaultpath; |
66 |
//ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Setting %s",s.c_str());; |
67 |
Asc_PutEnv(s.c_str()); |
68 |
} |
69 |
Asc_ImportPathList(ASC_ENV_LIBRARY); |
70 |
//cerr << PATHENVIRONMENTVAR << " = " << x << endl; |
71 |
//cerr << "Created LIBRARY" << endl; |
72 |
//cerr << "Registering solvers..." << endl; |
73 |
registerStandardSolvers(); |
74 |
}/*else{ |
75 |
CONSOLE_DEBUG("Reusing LIBRARY"); |
76 |
}*/ |
77 |
have_init=1; |
78 |
} |
79 |
|
80 |
Library::~Library(){ |
81 |
//ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"DESTROYED LIBRARY!////////////////////////////////////////"); |
82 |
//DestroyLibrary(); |
83 |
// ... need to use some kind of reference counting before you can do that... |
84 |
} |
85 |
|
86 |
/** |
87 |
Load an ASCEND model file into the Library. It will be parsed such that |
88 |
its types will be visible to Library::findType. |
89 |
|
90 |
@param filename Filename, will be searched for relative to ASCENDLIBRARY environment |
91 |
variable, if necessary. |
92 |
*/ |
93 |
void |
94 |
Library::load(const char *filename){ |
95 |
|
96 |
//std::cerr << "Loading '" << filename << "'" << std::endl; |
97 |
|
98 |
int status; |
99 |
struct module_t *m=Asc_RequireModule(filename,&status); |
100 |
if(m!=NULL){ |
101 |
//std::cerr << "Loaded module '" << Asc_ModuleName(m) << "'" << std::endl; |
102 |
}else{ |
103 |
std::cerr << "Error: unable to load module '" << filename << "'." << std::endl; |
104 |
} |
105 |
|
106 |
const char *msg = getLoadErrorMessage(status); |
107 |
|
108 |
char msg1[100]; |
109 |
sprintf(msg1,msg,filename); |
110 |
|
111 |
if(status<0 || status>0){ |
112 |
throw std::runtime_error(msg1); |
113 |
}else{ |
114 |
std::cerr << "Note: Module " << Asc_ModuleName(m) << ": " << msg1 << std::endl; |
115 |
} |
116 |
|
117 |
CONSOLE_DEBUG("Beginning parse of %s",Asc_ModuleName(m)); |
118 |
error_reporter_tree_start(); |
119 |
status = zz_parse(); |
120 |
switch(status){ |
121 |
case 0: break; |
122 |
case 1: ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Parsing of %s was aborted",Asc_ModuleName(m)); break; |
123 |
case 2: ERROR_REPORTER_NOLINE(ASC_PROG_FATAL,"Out of memory when parsing %s",Asc_ModuleName(m)); break; |
124 |
default: ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"Invalid return from zz_parse"); break; |
125 |
} |
126 |
status = error_reporter_tree_has_error(); |
127 |
error_reporter_tree_end(); |
128 |
if(!status){ |
129 |
//CONSOLE_DEBUG("CLEARING TREE..."); |
130 |
error_reporter_tree_clear(); |
131 |
//CONSOLE_DEBUG("DONE CLEARING TREE..."); |
132 |
}else{ |
133 |
ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Error(s) when loading '%s'",filename); |
134 |
stringstream ss; |
135 |
ss << "Errors found in '" << filename << "'"; |
136 |
throw runtime_error(ss.str()); |
137 |
} |
138 |
|
139 |
|
140 |
struct gl_list_t *l = Asc_TypeByModule(m); |
141 |
CONSOLE_DEBUG("%lu library entries loaded from %s",gl_length(l), filename); |
142 |
} |
143 |
|
144 |
/** |
145 |
Load MODELs specified inside a text string into the Library. The string is |
146 |
parsed and its types will then be visible to Library::findType. |
147 |
|
148 |
@param str String containing the model, in the ASCEND modelling language. |
149 |
@param nameprefix Name to be used to refer tothe string module when eg using |
150 |
the Library::getModules methood. |
151 |
*/ |
152 |
void |
153 |
Library::loadString(const char *str, const char *nameprefix){ |
154 |
int status; |
155 |
struct module_t *m = Asc_OpenStringModule(str, &status, nameprefix); |
156 |
|
157 |
const char *msg = getLoadErrorMessage(status); |
158 |
|
159 |
char msg1[100]; |
160 |
sprintf(msg1,msg,nameprefix); |
161 |
|
162 |
if(status<0 || status>0){ |
163 |
throw std::runtime_error(msg1); |
164 |
}else{ |
165 |
std::cerr << "Note: Module " << Asc_ModuleName(m) << ": " << msg1 << std::endl; |
166 |
} |
167 |
|
168 |
CONSOLE_DEBUG("Beginning parse of %s",Asc_ModuleName(m)); |
169 |
#ifdef LOADSTRING_ERROR_TREE |
170 |
error_reporter_tree_start(); |
171 |
#endif |
172 |
status = zz_parse(); |
173 |
switch(status){ |
174 |
case 0: break; |
175 |
case 1: ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Parsing of %s was aborted",Asc_ModuleName(m)); break; |
176 |
case 2: ERROR_REPORTER_NOLINE(ASC_PROG_FATAL,"Out of memory when parsing %s",Asc_ModuleName(m)); break; |
177 |
default: ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"Invalid return from zz_parse"); break; |
178 |
} |
179 |
#ifdef LOADSTRING_ERROR_TREE |
180 |
status = error_reporter_tree_has_error(); |
181 |
error_reporter_tree_end(); |
182 |
if(!status){ |
183 |
CONSOLE_DEBUG("CLEARING TREE..."); |
184 |
error_reporter_tree_clear(); |
185 |
CONSOLE_DEBUG("DONE CLEARING TREE..."); |
186 |
}else{ |
187 |
ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Error(s) when loading '%s'",nameprefix); |
188 |
stringstream ss; |
189 |
ss << "Errors found in '" << nameprefix << "'"; |
190 |
throw runtime_error(ss.str()); |
191 |
} |
192 |
#endif |
193 |
|
194 |
struct gl_list_t *l = Asc_TypeByModule(m); |
195 |
CONSOLE_DEBUG("%lu library entries loaded from %s",gl_length(l), nameprefix); |
196 |
} |
197 |
|
198 |
const char * |
199 |
Library::getLoadErrorMessage(int status){ |
200 |
const char *msg = NULL; |
201 |
switch(status){ |
202 |
case 5: |
203 |
msg = "The module '%s' already exists. "; break; |
204 |
case 4: |
205 |
msg = "Caught an attempt to do a recursive require under '%s'."; break; |
206 |
case 3: |
207 |
msg = "A new module was created from '%s', overwriting a module's alias."; break; |
208 |
case 2: |
209 |
msg = "An existing module is being returned for '%s'." ; break; |
210 |
case 1: |
211 |
msg = "An new version of an existing module was created for '%s'."; break; |
212 |
case 0: |
213 |
msg = "Module for '%s' created OK."; break; |
214 |
case -1: |
215 |
msg = "File not found for '%s'. (-1)"; break; |
216 |
case -2: |
217 |
msg = "Unable to open '%s' for reading. (-2)";break; |
218 |
case -3: |
219 |
msg = "Insuffient memory to create module for '%s'. (-3)"; break; |
220 |
case -4: |
221 |
msg = "Bad input, null or zero length filename in '%s'. (-4)"; break; |
222 |
default: |
223 |
throw std::runtime_error("Invalid status code in library.cpp"); |
224 |
} |
225 |
return msg; |
226 |
} |
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
/** |
233 |
Return a vector of all the Modules which have been loaded into |
234 |
the current library. |
235 |
*/ |
236 |
vector<Module> |
237 |
Library::getModules(const int module_type){ |
238 |
if(module_type < 0 || module_type > 2){ |
239 |
throw std::runtime_error("Library::getModules: invalid module_type parameter"); |
240 |
} |
241 |
|
242 |
vector<Module> v; |
243 |
struct gl_list_t *l = Asc_ModuleList(module_type); |
244 |
if(l==NULL){ |
245 |
CONSOLE_DEBUG("list is empty"); |
246 |
return v; |
247 |
} |
248 |
for(int i=0, end=gl_length(l); i<end; ++i){ |
249 |
symchar *name = (symchar *)gl_fetch(l,i+1); |
250 |
if(AscFindSymbol(name)==NULL){ |
251 |
throw runtime_error("Library::getModules: invalid symchar *"); |
252 |
} |
253 |
//cerr << "GOT " << SCP( name ) << endl; |
254 |
const module_t *m = Asc_GetModuleByName((const char *)name); |
255 |
v.push_back(Module(m)); |
256 |
} |
257 |
/*cerr << "LENGTH OF V IS " << v.size() << endl; |
258 |
if(v.size()){ |
259 |
cerr << "MODULE 0's NAME IS " << v[0].getName() << endl; |
260 |
}*/ |
261 |
return v; |
262 |
} |
263 |
|
264 |
/** |
265 |
Output to stderr the names of the modules loaded into the current Library. |
266 |
*/ |
267 |
void |
268 |
Library::listModules(const int module_type){ |
269 |
if(module_type < 0 || module_type > 2){ |
270 |
throw std::runtime_error("Library::listModules: invalid module_type parameter"); |
271 |
} |
272 |
|
273 |
const char *type = NULL; |
274 |
switch(module_type){ |
275 |
case 0: type = "defined types"; break; |
276 |
case 1: type = "string definitions"; break; |
277 |
case 2: type = "statements"; break; |
278 |
} |
279 |
|
280 |
vector<Module> v = getModules(module_type); |
281 |
if(v.size()){ |
282 |
std::cerr << "Listing " << v.size() << " modules with " << type << std::endl; |
283 |
for(vector<Module>::const_iterator i=v.begin(); i < v.end(); ++i){ |
284 |
cerr << " - " << i->getName() << endl; |
285 |
vector<Type> tt = getModuleTypes(*i); |
286 |
for(vector<Type>::const_iterator j = tt.begin(); j < tt.end(); ++j){ |
287 |
cerr << " * " << j->getName() << endl; |
288 |
} |
289 |
} |
290 |
}else{ |
291 |
std::cerr << "Notice: No " << type << " found in module list." << std::endl; |
292 |
} |
293 |
} |
294 |
|
295 |
Type & |
296 |
Library::findType(const SymChar &sym){ |
297 |
TypeDescription *t = FindType(sym.getInternalType()); |
298 |
if(t==NULL){ |
299 |
stringstream ss; |
300 |
ss << "Library::findType: type '" << sym << "' not found in library"; |
301 |
throw runtime_error(ss.str()); |
302 |
}/*else{ |
303 |
cerr << "Found something for type " << sym << endl; |
304 |
}*/ |
305 |
Type *t2=new Type(t); |
306 |
return *t2; |
307 |
} |
308 |
|
309 |
static int module_sort_cmp(const void *a, const void *b){ |
310 |
return strcmp((char *)a, (char *)b); |
311 |
} |
312 |
|
313 |
/** |
314 |
This could be quite a bit more efficient if we could get a gl_list_t of TypeDescription rather than names |
315 |
*/ |
316 |
vector<Type> |
317 |
Library::getModuleTypes(const Module &m){ |
318 |
//cerr << "GET MODULE TYPES\n" << endl; |
319 |
vector<Type> v; |
320 |
struct gl_list_t *l = Asc_TypeByModule(m.getInternalType()); |
321 |
|
322 |
gl_sort(l, &module_sort_cmp); |
323 |
|
324 |
for(int i=0,end=gl_length(l); i<end; ++i){ |
325 |
char *name = (char *)gl_fetch(l,i+1); |
326 |
//CONSOLE_DEBUG("Found type %s",name); |
327 |
TypeDescription *t = FindType((const symchar *)name); |
328 |
v.push_back(Type(t)); |
329 |
} |
330 |
return v; |
331 |
} |
332 |
|
333 |
/** |
334 |
This function is kinda fighting against the Table implementation of the external function library. What we really need is some kind of iterator on the Table struct, but it doesn't seem to be implemented. Instead there is a C-style equivalent of the STL 'bind1st' function which we can use, but it's not exported from the current extfunc.h so we need to add it. |
335 |
*/ |
336 |
vector<ExtMethod> |
337 |
Library::getExtMethods(){ |
338 |
// Clear the vector |
339 |
extmethod_vector = vector<ExtMethod>(); |
340 |
|
341 |
// Traverse the vector |
342 |
TraverseExtFuncLibrary(Library::extMethodTraverse, (void *)this); |
343 |
|
344 |
return extmethod_vector; |
345 |
} |
346 |
|
347 |
/** |
348 |
This method exists only to allow the TraverseExtFuncLibrary function |
349 |
to make callbacks to the Library class from C. |
350 |
|
351 |
@NOTE there might be issues with C/C++ linking here? |
352 |
*/ |
353 |
void |
354 |
Library::extMethodTraverse(void *a1, void *a2){ |
355 |
Library *self = (Library *)a2; |
356 |
self->appendToExtMethodVector(a1); |
357 |
} |
358 |
|
359 |
void |
360 |
Library::appendToExtMethodVector(void *a1){ |
361 |
struct ExternalFunc *e = (struct ExternalFunc *)a1; |
362 |
extmethod_vector.push_back(ExtMethod(e)); |
363 |
} |
364 |
|
365 |
/** |
366 |
Clear the library: 'DESTROY TYPES' |
367 |
|
368 |
@TODO do this more efficiently, don't destroy the whole ASCEND compiler. |
369 |
*/ |
370 |
void |
371 |
Library::clear(){ |
372 |
/* Asc_CompilerDestroy(); |
373 |
cerr << "COMPLETED ASC_COMPILERDESTROY" << endl; |
374 |
Asc_CompilerInit(1); |
375 |
cerr << "... ASC_COMPILERINIT OK" << endl; |
376 |
Asc_ImportPathList(PATHENVIRONMENTVAR); |
377 |
registerStandardSolvers(); |
378 |
cerr << "... REGISTER_STANDARD_SOLVERS" << endl; |
379 |
DefineFundamentalTypes(); |
380 |
cerr << "... DEFINED FUND TYPES" << endl; |
381 |
\*SetUniversalProcedureList(NULL); |
382 |
*/ |
383 |
|
384 |
//CONSOLE_DEBUG("Displaying library modules and types..."); |
385 |
//listModules(); |
386 |
|
387 |
CONSOLE_DEBUG("Destroying simulations..."); |
388 |
Asc_DestroySimulations(); |
389 |
|
390 |
CONSOLE_DEBUG("Clearing library..."); |
391 |
DestroyNotesDatabase(LibraryNote()); |
392 |
SetUniversalProcedureList(NULL); |
393 |
DestroyLibrary(); |
394 |
DestroyPrototype(); |
395 |
EmptyTrash(); |
396 |
Asc_DestroyModules((DestroyFunc)DestroyStatementList); |
397 |
//importhandler_destroylibrary(); |
398 |
WriteChildMissing(NULL,NULL,NULL); |
399 |
//Asc_CompilerInit(1) |
400 |
DefineFundamentalTypes(); |
401 |
InitNotesDatabase(LibraryNote()); |
402 |
ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"LIBRARY CLEARED!"); |
403 |
} |
404 |
|
405 |
AnnotationDatabase |
406 |
Library::getAnnotationDatabase(){ |
407 |
return AnnotationDatabase(SCP(LibraryNote())); |
408 |
} |
409 |
|
410 |
vector<UnitsM> |
411 |
Library::getUnits() const{ |
412 |
vector<UnitsM> v; |
413 |
register unsigned long c; |
414 |
const struct Units *p; |
415 |
for(c = 0;c<UNITS_HASH_SIZE;c++) { |
416 |
for(p = g_units_hash_table[c];p!=NULL;p = p->next){ |
417 |
v.push_back(UnitsM(p)); |
418 |
} |
419 |
} |
420 |
return v; |
421 |
} |
422 |
|
423 |
set<Type> |
424 |
Library::getRealAtomTypes() const{ |
425 |
set<Type> s; |
426 |
struct gl_list_t *l = DefinitionList(); |
427 |
if(!l){ |
428 |
throw runtime_error("No types found in library (perhaps no files have been loaded?)"); |
429 |
} |
430 |
|
431 |
for(unsigned i = 1; i<=gl_length(l); ++i) { |
432 |
Type t((const struct TypeDescription *)gl_fetch(l,i)); |
433 |
if(t.isRefinedReal()){ |
434 |
Dimensions d = t.getDimensions(); |
435 |
if(d.isWild() || d.isDimensionless())continue; // skip this one |
436 |
// it's got some dimensions, add it to the list |
437 |
s.insert(t); |
438 |
} |
439 |
} |
440 |
gl_destroy(l); |
441 |
return s; |
442 |
} |
443 |
|