00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <sys/types.h>
00022 #include <sys/stat.h>
00023 #if defined(__digital__)
00024 #include <sys/mode.h>
00025 #endif
00026 #include <strings.h>
00027 #include <errno.h>
00028
00029 #include <Basics.H>
00030 #include <Sequence.H>
00031 #include <FS.H>
00032 #include "ParseImports.H"
00033
00034 using std::ostream;
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044 struct Char_Buff
00045 {
00046
00047
00048 static const unsigned int len = 100;
00049 char chars[len+1];
00050
00051
00052 unsigned int pos;
00053
00054
00055
00056 Text content;
00057
00058 inline Char_Buff()
00059 : pos(0)
00060 {
00061 }
00062
00063
00064
00065 inline void flush()
00066 {
00067 if(pos > 0)
00068 {
00069 chars[pos] = 0;
00070 content += chars;
00071 pos = 0;
00072 }
00073 }
00074
00075
00076 inline void reset()
00077 {
00078 content = "";
00079 pos = 0;
00080 }
00081
00082
00083
00084 inline void expect(unsigned int n)
00085 {
00086
00087
00088 if((pos + n) >= len)
00089 {
00090
00091 flush();
00092 }
00093 }
00094
00095
00096
00097 inline unsigned int length()
00098 {
00099 return pos + (content.Empty() ? 0 : content.Length());
00100 }
00101
00102
00103 inline Char_Buff &operator +=(char c)
00104 {
00105 expect(1);
00106 chars[pos++] = c;
00107 return *this;
00108 }
00109
00110
00111 inline char pop()
00112 {
00113
00114
00115 if(pos > 0)
00116 {
00117 return chars[--pos];
00118 }
00119
00120 else if(content.Empty())
00121 {
00122
00123 return 0;
00124 }
00125
00126
00127 else
00128 {
00129 unsigned int clen = content.Length();
00130 char c = content[clen-1];
00131 content = content.Sub(0, clen-1);
00132 return c;
00133 }
00134 }
00135
00136
00137
00138
00139 inline bool operator ==(const char *text)
00140 {
00141 if(content.Empty())
00142 {
00143 chars[pos] = 0;
00144 return (strcmp(text, chars) == 0);
00145 }
00146 else
00147 {
00148 flush();
00149 return (content == text);
00150 }
00151 }
00152 inline bool operator !=(const char *text)
00153 {
00154 if(content.Empty())
00155 {
00156 chars[pos] = 0;
00157 return (strcmp(text, chars) != 0);
00158 }
00159 else
00160 {
00161 flush();
00162 return (content != text);
00163 }
00164 }
00165
00166
00167
00168 inline char first() const throw()
00169 {
00170 return (content.Empty() ? chars[0] : content[0]);
00171 }
00172 };
00173
00174 ostream&
00175 operator<<(ostream &os, const ParseImports::Error &err) throw ()
00176 {
00177 return (os << err.msg);
00178 }
00179
00180
00181
00182 char
00183 ParseImports::LocalModelSpace::getC()
00184 throw (ParseImports::Error, FS::EndOfFile)
00185
00186
00187 {
00188 int c = ifs.get();
00189 if (c == EOF || ifs.eof()) throw FS::EndOfFile();
00190 if (ifs.fail()) throw ParseImports::Error("failure reading model");
00191 return (char)c;
00192 }
00193
00194 ParseImports::ModelSpace*
00195 ParseImports::LocalModelSpace::open(const Text &modelname) const
00196 throw(ParseImports::Error, FS::Failure, FS::DoesNotExist)
00197 {
00198 return NEW_CONSTR(LocalModelSpace, (modelname));
00199 }
00200
00201 ParseImports::LocalModelSpace::LocalModelSpace(const Text &modelname)
00202 throw(FS::Failure, FS::DoesNotExist)
00203 {
00204 try
00205 {
00206 FS::OpenReadOnly(modelname, this->ifs);
00207 }
00208 catch (FS::DoesNotExist)
00209 {
00210 errno = ENOENT;
00211 throw FS::DoesNotExist();
00212 }
00213 }
00214
00215 ParseImports::ModelSpace::type
00216 ParseImports::LocalModelSpace::getType(const Text &name) const throw()
00217 {
00218 struct stat stat_buff;
00219 int stat_res = stat(name.cchars(), &stat_buff);
00220 if (stat_res == 0) {
00221 if (S_ISDIR(stat_buff.st_mode)) {
00222 return ParseImports::ModelSpace::directory;
00223 }
00224 if (S_ISREG(stat_buff.st_mode)) {
00225 return ParseImports::ModelSpace::file;
00226 }
00227 }
00228 return ParseImports::ModelSpace::none;
00229 }
00230
00231
00232
00233 static void
00234 SkipOneLineComment(ParseImports::ModelSpace &model) throw (ParseImports::Error)
00235
00236 {
00237 try {
00238 while (model.getC() != '\n') ;
00239 } catch (FS::EndOfFile) {
00240 throw ParseImports::Error("premature EOF in // comment");
00241 }
00242 }
00243
00244 static Text
00245 SkipBracketedComment(ParseImports::ModelSpace &model)
00246 throw (ParseImports::Error)
00247
00248
00249
00250
00251 {
00252 try {
00253 bool inPragma = false;
00254 Char_Buff pragma;
00255 char last = model.getC(), c;
00256 if (last == '*') inPragma = true;
00257 while ((c = model.getC()) != '/' || last != '*') {
00258 if (inPragma) {
00259 pragma += c;
00260 }
00261 last = c;
00262 }
00263 if (inPragma) {
00264 if (pragma.length() < 1) {
00265
00266 inPragma = false;
00267 } else {
00268 pragma.flush();
00269 int len = pragma.content.Length();
00270 int i = len;
00271 while (i > 0 && pragma.content[i-1] == '*') i--;
00272 assert(i < len);
00273 pragma.content = pragma.content.Sub(0, i);
00274 }
00275 }
00276
00277
00278 return (inPragma ? pragma.content : Text(""));
00279 }
00280 catch (FS::EndOfFile) {
00281 throw ParseImports::Error("premature EOF in /*...*/ comment");
00282 }
00283 }
00284
00285 static Text
00286 SkipWhite(ParseImports::ModelSpace &model, Char_Buff &buff)
00287 throw (ParseImports::Error, FS::EndOfFile)
00288
00289
00290
00291
00292 {
00293 Text res("");
00294 char c;
00295 while (true) {
00296 while ((c = model.getC()) == ' ' || c == '\n' || c == '\t') ;
00297 buff.reset();
00298 buff += c;
00299 if (c != '/') break;
00300 c = model.getC();
00301 buff += c;
00302 switch (c) {
00303 case '/':
00304 SkipOneLineComment(model);
00305 res = "";
00306 break;
00307 case '*':
00308 res = SkipBracketedComment(model);
00309 break;
00310 default:
00311 model.ungetC(buff.pop());
00312 return res;
00313 }
00314 }
00315 return res;
00316 }
00317
00318 static void
00319 Scan(ParseImports::ModelSpace &model, Char_Buff &buff,
00320 const char *stopChars) throw (ParseImports::Error)
00321
00322
00323
00324
00325
00326 {
00327 char c;
00328 try {
00329 while (true) {
00330 c = model.getC();
00331 if (index(stopChars, c) != (char *)NULL) {
00332 model.ungetC(c);
00333 break;
00334 }
00335 buff += c;
00336 }
00337 }
00338 catch (FS::EndOfFile) {
00339
00340 }
00341 }
00342
00343
00344 class Intv {
00345 public:
00346 int start, end;
00347 Text pragma;
00348 };
00349
00350 static Intv
00351 Lex(ParseImports::ModelSpace &model, Char_Buff &buff)
00352 throw (ParseImports::Error, FS::EndOfFile)
00353
00354
00355
00356
00357
00358
00359 {
00360 Intv res;
00361 res.pragma = SkipWhite(model, buff);
00362 assert(buff.length() == 1);
00363 try {
00364 res.start = model.tell() - 1;
00365 if (index("=;,{}[]", buff.first()) == (char *)NULL) {
00366
00367 Scan(model, buff, "=;,[]{} \t\n");
00368 }
00369 buff.flush();
00370 res.end = model.tell();
00371 }
00372 catch (FS::Failure) {
00373 throw ParseImports::Error("failure reading model");
00374 }
00375 return res;
00376 }
00377
00378
00379
00380
00381 static Text StripQuotes(const Text &in)
00382 {
00383 Text result = in;
00384
00385 int start = 0, match;
00386 while ((match = result.FindChar('"', start)) >= 0) {
00387 result = result.Sub(0, match) + result.Sub(match+1);
00388 start = match;
00389 }
00390
00391 return result;
00392 }
00393
00394 Text
00395 ParseImports::ResolvePath(const Text &path, const Text &wd,
00396 const ParseImports::ModelSpace *ms)
00397 throw (ParseImports::Error, FS::Failure, SRPC::failure)
00398 {
00399 if (ms == NULL) {
00400 ms = NEW(LocalModelSpace);
00401 }
00402 Text fullPath(path);
00403
00404
00405 fullPath = StripQuotes(fullPath);
00406
00407
00408 if (fullPath[0] != '/') {
00409 fullPath = wd + fullPath;
00410 }
00411
00412 switch (ms->getType(fullPath)) {
00413 case ParseImports::ModelSpace::directory:
00414
00415 fullPath += (fullPath[fullPath.Length()-1] == '/')
00416 ? "build.ves" : "/build.ves";
00417 break;
00418
00419 case ParseImports::ModelSpace::file:
00420 default:
00421 break;
00422
00423 case ParseImports::ModelSpace::none:
00424
00425 if (fullPath.Sub(fullPath.Length()-4) != Text(".ves")) {
00426 Text fullPath2(fullPath); fullPath2 += ".ves";
00427 if (ms->getType(fullPath2) == ParseImports::ModelSpace::file) {
00428 fullPath = fullPath2;
00429 }
00430 }
00431 break;
00432 }
00433
00434 return fullPath;
00435 }
00436
00437 static void
00438 AddPath( ImportSeq &seq, const Text &orig,
00439 const Text &path, const ImportID &id, const Text &wd, const Intv &bounds,
00440 const ParseImports::ModelSpace &ms, bool fromForm=false)
00441 throw (ParseImports::Error, FS::Failure, SRPC::failure)
00442 {
00443
00444
00445
00446 bool local = !((path[0] == '"' && path[1] == '/') || (path[0] == '/'));
00447 Text fullPath(ParseImports::ResolvePath(path, wd, &ms));
00448
00449
00450 bool noUpdate = ((bounds.pragma == "NOUPDATE") ||
00451 (bounds.pragma == "noupdate"));
00452 Import *imp = NEW_CONSTR(Import, (fullPath, orig, id, bounds.start, bounds.end,
00453 local, fromForm, noUpdate));
00454
00455
00456 seq.addhi(imp);
00457 }
00458
00459 static void
00460 AddFromPath( ImportSeq &seq, const Text &orig, const Text &path,
00461 const ImportID &id, const Text &wd, const Intv &bounds,
00462 const ParseImports::ModelSpace &ms)
00463 throw (ParseImports::Error, FS::Failure, SRPC::failure)
00464 {
00465 AddPath( seq, orig, path, id, wd, bounds, ms, true);
00466 }
00467
00468 static void
00469 ExpectToken(const char *expected, Char_Buff &found)
00470 throw (ParseImports::Error)
00471 {
00472 if (found != expected)
00473 {
00474 Text msg("expecting '");
00475 msg += expected;
00476 msg += "'; found '";
00477
00478 found.flush();
00479 msg += found.content;
00480 msg += "'";
00481 throw ParseImports::Error(msg);
00482 }
00483 }
00484
00485 static void
00486 ReadIncIdReq(ParseImports::ModelSpace &model, Char_Buff &buff,
00487 const Text &wd, ImportSeq &seq)
00488 throw (ParseImports::Error, FS::EndOfFile, FS::Failure, SRPC::failure)
00489
00490
00491
00492
00493
00494
00495 {
00496
00497 ExpectToken("import", buff);
00498 (void)Lex(model, buff);
00499 buff.flush();
00500 Text id(StripQuotes(buff.content));
00501 ImportID import_id;
00502 import_id.addhi(id);
00503
00504
00505 while ((buff != "import") && (buff != "from") &&
00506 (buff != "{")) {
00507
00508 (void)Lex(model, buff);
00509
00510
00511 ExpectToken("=", buff);
00512 Intv bounds = Lex(model, buff);
00513
00514
00515 if (buff != "[") {
00516
00517 buff.flush();
00518 Text orig(buff.content);
00519 AddPath( seq, orig, orig, import_id, wd, bounds, model);
00520 } else {
00521
00522 (void)Lex(model, buff);
00523 buff.flush();
00524 id = StripQuotes(buff.content);
00525 import_id.addhi(id);
00526 while (buff != "]") {
00527
00528 (void)Lex(model, buff);
00529
00530
00531 ExpectToken("=", buff);
00532 Intv bounds = Lex(model, buff);
00533
00534
00535 buff.flush();
00536 Text orig(buff.content);
00537 AddPath( seq, orig, orig, import_id, wd, bounds, model);
00538 (void)Lex(model, buff);
00539
00540
00541 if (buff == ",") {
00542 (void)Lex(model, buff);
00543 buff.flush();
00544 import_id.remhi();
00545 id = StripQuotes(buff.content);
00546 import_id.addhi(id);
00547 } else {
00548 ExpectToken("]", buff);
00549 }
00550 }
00551 import_id.remhi();
00552 }
00553 (void)Lex(model, buff);
00554
00555
00556 if (buff == ";") {
00557 (void)Lex(model, buff);
00558 buff.flush();
00559 import_id.remhi();
00560 id = StripQuotes(buff.content);
00561 import_id.addhi(id);
00562 } else {
00563
00564 break;
00565 }
00566 }
00567 import_id.remhi();
00568 }
00569
00570 static void
00571 ReadIncIdOpt(ParseImports::ModelSpace &model, Char_Buff &buff,
00572 const Text &wd, ImportSeq &seq)
00573 throw (ParseImports::Error, FS::EndOfFile)
00574
00575
00576
00577
00578
00579
00580 {
00581
00582 ExpectToken("from", buff);
00583 (void)Lex(model, buff);
00584
00585
00586 buff.flush();
00587 Text rootPath(buff.content);
00588 if (rootPath[rootPath.Length()-1] != '/') {
00589 rootPath += '/';
00590 }
00591 (void)Lex(model, buff);
00592
00593
00594 ExpectToken("import", buff);
00595 Intv bounds = Lex(model, buff);
00596
00597 ImportID import_id;
00598
00599 while ((buff != "import") && (buff != "from") &&
00600 (buff != "{")) {
00601
00602
00603 buff.flush();
00604 Text path(buff.content);
00605 (void)Lex(model, buff);
00606
00607
00608 if (buff != "=") {
00609
00610
00611 int first_slash = path.FindChar('/');
00612 Text id = StripQuotes((first_slash>0)
00613 ?path.Sub(0, first_slash)
00614 :path);
00615 import_id.addhi(id);
00616
00617 AddFromPath( seq, path, rootPath+path, import_id,
00618 wd, bounds, model);
00619 } else {
00620
00621 bounds = Lex(model, buff);
00622 if (buff != "[") {
00623 Text id(StripQuotes(path));
00624 import_id.addhi(id);
00625
00626 buff.flush();
00627 Text path(buff.content);
00628 AddFromPath( seq, path, rootPath+path, import_id,
00629 wd, bounds, model);
00630 } else {
00631
00632 Text id(StripQuotes(path));
00633 import_id.addhi(id);
00634 bounds = Lex(model, buff);
00635 while (buff != "]") {
00636
00637 buff.flush();
00638 Text path(buff.content);
00639 (void)Lex(model, buff);
00640
00641
00642 if (buff != "=") {
00643 int first_slash = path.FindChar('/');
00644 Text id = StripQuotes((first_slash>0)
00645 ?path.Sub(0, first_slash)
00646 :path);
00647 import_id.addhi(id);
00648 AddFromPath( seq, path, rootPath+path, import_id,
00649 wd, bounds, model);
00650 } else {
00651 Text id(StripQuotes(path));
00652 import_id.addhi(id);
00653
00654 bounds = Lex(model, buff);
00655 buff.flush();
00656 Text path(buff.content);
00657 AddFromPath( seq, path, rootPath+path, import_id,
00658 wd, bounds, model);
00659 (void)Lex(model, buff);
00660 }
00661
00662 import_id.remhi();
00663
00664 if (buff == ",") {
00665 bounds = Lex(model, buff);
00666 } else {
00667 ExpectToken("]", buff);
00668 }
00669 }
00670 import_id.remhi();
00671 }
00672 (void)Lex(model, buff);
00673 }
00674
00675
00676 if (buff == ";") {
00677 bounds = Lex(model, buff);
00678 } else {
00679
00680 break;
00681 }
00682 }
00683 }
00684
00685 static void
00686 ScanImports(ParseImports::ModelSpace &model, const Text &wd,
00687 ImportSeq &seq) throw (ParseImports::Error, FS::Failure, SRPC::failure)
00688
00689
00690
00691 {
00692 Char_Buff buff;
00693 try {
00694 Lex(model, buff);
00695 while (true) {
00696 if (buff == "{") {
00697
00698 break;
00699 } else if (buff == "import") {
00700
00701 ReadIncIdReq(model, buff, wd, seq);
00702 } else if (buff == "from") {
00703
00704 ReadIncIdOpt(model, buff, wd, seq);
00705 } else {
00706
00707 Lex(model, buff);
00708 }
00709 }
00710 }
00711 catch (FS::EndOfFile) {
00712 throw ParseImports::Error("premature end-of-file");
00713 }
00714 }
00715
00716 void
00717 ParseImports::P(const Text &modelname, ImportSeq &seq,
00718 const ParseImports::ModelSpace* ms)
00719 throw(FS::DoesNotExist, FS::Failure, ParseImports::Error, SRPC::failure)
00720
00721
00722 {
00723
00724 ParseImports::ModelSpace *model;
00725 if (ms == NULL) {
00726 model = NEW_CONSTR(LocalModelSpace, (modelname));
00727 } else {
00728 model = ms->open(modelname);
00729 }
00730
00731
00732 int last_slash = modelname.FindCharR('/'); assert(last_slash >= 0);
00733 Text wd = modelname.Sub(0, last_slash+1);
00734
00735
00736 ScanImports(*model, wd, seq);
00737
00738
00739 delete(model);
00740 }