1 /* $Header: /var/cvs/mbdyn/mbdyn/mbdyn-1.0/libraries/libmbutil/parser.cc,v 1.90 2017/01/12 14:44:05 masarati Exp $ */
2 /*
3  * MBDyn (C) is a multibody analysis code.
4  * http://www.mbdyn.org
5  *
6  * Copyright (C) 1996-2017
7  *
8  * Pierangelo Masarati	<masarati@aero.polimi.it>
9  * Paolo Mantegazza	<mantegazza@aero.polimi.it>
10  *
11  * Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
12  * via La Masa, 34 - 20156 Milano, Italy
13  * http://www.aero.polimi.it
14  *
15  * Changing this copyright notice is forbidden.
16  *
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation (version 2 of the License).
20  *
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30  */
31 
32 /* parser */
33 
34 #include "mbconfig.h"           /* This goes first in every *.c,*.cc file */
35 
36 #include <cstring>
37 #include <stdlib.h>
38 #include <stack>
39 #include <map>
40 
41 #include "mathtyp.h"
42 #include "parser.h"
43 #include "filename.h"
44 #include "Rot.hh"
45 
46 /* LowParser - begin */
47 
48 static int
skip_remarks(HighParser & HP,InputStream & In,char & cIn)49 skip_remarks(HighParser& HP, InputStream& In, char &cIn)
50 {
51 skip_again:;
52 	for (cIn = In.get(); isspace(cIn); cIn = In.get()) {
53 		// this is here in case in some implementation isspace returns success for EOF
54 		if (In.eof()) {
55 			return -1;
56 		}
57 	}
58 
59 	if (In.eof()) {
60 		return -1;
61 	}
62 
63 	switch (cIn) {
64 	case -1: // should be EOF!
65 		return -1;
66 
67 	case MathParser::ONE_LINE_REMARK:
68 		for (cIn = In.get(); cIn != '\n'; cIn = In.get()) {
69 			if (cIn == '\\') {
70 				cIn = In.get();
71 				if (In.eof()) {
72 					return -1;
73 				}
74 				if (cIn == '\r') {
75 					/* if input file was prepared
76 					 * under DOS/Windows */
77 					cIn = In.get();
78 					if (In.eof()) {
79 						return -1;
80 					}
81 				}
82 			}
83 			if (In.eof()) {
84 				return -1;
85 			}
86 		}
87 		goto skip_again;
88 
89 	case '/':
90 		cIn = In.get();
91 		if (In.eof()) {
92 			return -1;
93 
94 		} else if (cIn == '*') {
95 			for (cIn = In.get(); !In.eof(); cIn = In.get()) {
96 				if (cIn == '*') {
97 end_of_comment:;
98 					cIn = In.get();
99 					if (In.eof()) {
100 						return -1;
101 					}
102 					if (cIn == '/') {
103 						goto skip_again;
104 					}
105 
106 				} else if (cIn == '/') {
107 					cIn = In.get();
108 					if (In.eof()) {
109 						return -1;
110 					}
111 					if (cIn == '*') {
112 						silent_cerr("warning: '/*' inside a comment "
113 							"at line " << HP.GetLineData()
114 							<< std::endl);
115 						goto end_of_comment;
116 					}
117 				}
118 			}
119 			if (In.eof()) {
120 				return -1;
121 			}
122 
123 		} else {
124 			In.putback(cIn);
125 			return 0;
126 		}
127 	}
128 
129 	return 0;
130 }
131 
LowParser(HighParser & hp)132 LowParser::LowParser(HighParser& hp)
133 : HP(hp), sCurrWordBuf(0), iBufSize(iDefaultBufSize)
134 {
135 	SAFENEWARR(sCurrWordBuf, char, iBufSize);
136 }
137 
~LowParser(void)138 LowParser::~LowParser(void)
139 {
140 	if (sCurrWordBuf) {
141 		SAFEDELETEARR(sCurrWordBuf);
142 	}
143 }
144 
145 void
PackWords(InputStream & In)146 LowParser::PackWords(InputStream& In)
147 {
148 	unsigned iCur = 0;
149 	char cIn;
150 
151 	/* note: no remarks allowed inside words */
152 	for (cIn = In.get(); !In.eof(); cIn = In.get()) {
153 		switch (cIn) {
154 		case COLON:
155 		case COMMA:
156 		case SEMICOLON:
157 			goto end_of_word;
158 
159 		default:
160       			if (!isspace(cIn)) {
161 				sCurrWordBuf[iCur] = cIn;
162 				iCur++;
163 				 if (iCur == iBufSize - 1) {
164 					 char *s = NULL;
165 					 unsigned i = 2*iBufSize;
166 
167 					 /* FIXME: no limit on max size? */
168 
169 					 SAFENEWARR(s, char, i);
170 					 memcpy(s, sCurrWordBuf, iBufSize);
171 					 SAFEDELETEARR(sCurrWordBuf);
172 					 sCurrWordBuf = s;
173 					 iBufSize = i;
174 				 }
175 			}
176 		}
177 	}
178 
179 	throw EndOfFile(MBDYN_EXCEPT_ARGS);
180 
181 end_of_word:;
182 
183 	sCurrWordBuf[iCur] = '\0';
184 	In.putback(cIn);
185 }
186 
187 
188 LowParser::Token
GetToken(InputStream & In)189 LowParser::GetToken(InputStream& In)
190 {
191 	/* toglie gli spazi iniziali e tutti i commenti */
192 	char cIn;
193 	if (skip_remarks(HP, In, cIn)) {
194 		return CurrToken = LowParser::ENDOFFILE;
195 	}
196 
197 	if (isalpha(cIn) || cIn == '_') {
198 		PackWords(In.putback(cIn));
199 		return CurrToken = LowParser::WORD;
200 	}
201 
202 	switch (cIn) {
203 	case ',':
204 		return CurrToken = LowParser::COMMA;
205 
206 	case ':':
207 		return CurrToken = LowParser::COLON;
208 
209 	case ';':
210 		return CurrToken = LowParser::SEMICOLON;
211 
212 	case '.':
213 	case '-':
214 	case '+':
215 is_digit:;
216 		In.putback(cIn) >> dCurrNumber;
217 		return CurrToken = LowParser::NUMBER;
218 
219 	default:
220 		if (isdigit(cIn)) {
221 			goto is_digit;
222 		}
223 		In.putback(cIn);
224 		return CurrToken = LowParser::UNKNOWN;
225 	}
226 }
227 
228 
229 doublereal
dGetReal(void) const230 LowParser::dGetReal(void) const
231 {
232 	return dCurrNumber;
233 }
234 
235 
236 integer
iGetInt(void) const237 LowParser::iGetInt(void) const
238 {
239 	return integer(dCurrNumber);
240 }
241 
242 
243 char*
sGetWord(void)244 LowParser::sGetWord(void)
245 {
246 	return sCurrWordBuf;
247 }
248 
249 /* LowParser - end */
250 
251 
252 /* KeyTable - begin */
253 
KeyTable(HighParser & hp,const char * const sTable[])254 KeyTable::KeyTable(HighParser& hp, const char* const sTable[])
255 : sKeyWords(0), oldKey(0), HP(hp)
256 {
257 	sKeyWords = (char* const*)sTable;
258 	oldKey = HP.PutKeyTable(*this);
259 }
260 
261 
~KeyTable(void)262 KeyTable::~KeyTable(void)
263 {
264 	if (oldKey) {
265 		(void)HP.PutKeyTable(*oldKey);
266 	}
267 }
268 
269 
270 int
Find(const char * sToFind) const271 KeyTable::Find(const char* sToFind) const
272 {
273 	for (int iCnt = 0; sKeyWords[iCnt]; iCnt++) {
274 		if (strcasecmp(sKeyWords[iCnt], sToFind) == 0) {
275 			return iCnt;
276 		}
277 	}
278 
279 	return -1;
280 }
281 
282 /* KeyTable - end */
283 
284 
285 /* DescRead - begin */
286 
287 /* bag that contains functions to parse descriptions */
288 
289 typedef std::map<std::string, DescRead *, ltstrcase> DescFuncMapType;
290 static DescFuncMapType DescFuncMap;
291 
292 struct DescWordSetType : public HighParser::WordSet {
IsWordDescWordSetType293 	bool IsWord(const std::string& s) const {
294 		return DescFuncMap.find(s) != DescFuncMap.end();
295 	};
296 };
297 
298 static DescWordSetType DescWordSet;
299 
300 bool
SetDescData(const std::string & name,DescRead * rf)301 SetDescData(const std::string& name, DescRead *rf)
302 {
303 	pedantic_cout("registering description \"" << name << "\"" << std::endl);
304 	return DescFuncMap.insert(DescFuncMapType::value_type(name, rf)).second;
305 }
306 
307 /* Reads descriptions */
308 
309 bool
ReadDescription(HighParser & HP,const std::string & desc)310 ReadDescription(HighParser& HP, const std::string& desc)
311 {
312 	DEBUGCOUTFNAME("ReadDescription()");
313 
314 	bool bRC(false);
315 	DescFuncMapType::iterator func = DescFuncMap.find(desc);
316 	if (func != DescFuncMap.end()) {
317 		HP.GotDescription();
318 		if (!HP.IsArg() && !HP.IsDescription()) {
319 			silent_cerr("Parser error in ReadDescription(),"
320 				" colon or semicolon expected after description at line "
321 				<< HP.GetLineData() << std::endl);
322 			throw HighParser::ErrColonExpected(MBDYN_EXCEPT_ARGS);
323 		}
324 
325 		bRC = func->second->Read(HP);
326 
327 		if (HP.IsArg()) {
328 			silent_cerr("semicolon expected at line " << HP.GetLineData() << std::endl);
329 			throw HighParser::ErrSemicolonExpected(MBDYN_EXCEPT_ARGS);
330 		}
331 	}
332 
333 	return bRC;
334 }
335 
~DescRead(void)336 DescRead::~DescRead(void)
337 {
338 	NO_OP;
339 }
340 
341 struct RemarkDR : public DescRead {
342 public:
343 	bool Read(HighParser& HP);
344 };
345 
346 bool
Read(HighParser & HP)347 RemarkDR::Read(HighParser& HP)
348 {
349 	silent_cout("line " << HP.GetLineData());
350 
351 	char prefix = ':';
352 	while (HP.IsArg()) {
353 		TypedValue v;
354 		v = HP.GetValue(v);
355 		silent_cout(prefix << ' ' << v);
356 
357 		if (prefix == ':') {
358 			prefix = ',';
359 		}
360 	}
361 
362 	silent_cout(std::endl);
363 
364 	return true;
365 }
366 
367 struct PrintSymbolTableDR : public DescRead {
368 public:
369 	bool Read(HighParser& HP);
370 };
371 
372 bool
Read(HighParser & HP)373 PrintSymbolTableDR::Read(HighParser& HP)
374 {
375 	if (!HP.IsArg()) {
376 		silent_cout( "math parser symbol table at line "
377 			<< HP.GetLineData() << ":" << std::endl
378 			<< HP.GetMathParser().GetSymbolTable() << std::endl);
379 		return true;
380 	}
381 
382 	if (HP.IsKeyWord("all")) {
383 		const MathParser::NameSpaceMap& ns = HP.GetMathParser().GetNameSpaceMap();
384 		for (MathParser::NameSpaceMap::const_iterator i = ns.begin(); i != ns.end(); ++i) {
385 			const std::string& sName = i->second->sGetName();
386 			const Table *pT = i->second->GetTable();
387 			if (pT != 0) {
388 				silent_cout( "namespace \"" << sName << "\" symbol table at line "
389 					<< HP.GetLineData() << ":" << std::endl
390 					<< *pT << std::endl);
391 			}
392 		}
393 
394 		return true;
395 	}
396 
397 	while (HP.IsArg()) {
398 		const char *sName = HP.GetString();
399 		MathParser::NameSpace *pN = HP.GetMathParser().GetNameSpace(sName);
400 		if (pN == 0) {
401 			silent_cerr("PrintSymbolTableDR::Read(): warning, unable to find namespace \"" << sName << "\" at line "
402 				<< HP.GetLineData() << std::endl);
403 
404 		} else {
405 			Table *pT = pN->GetTable();
406 			if (pT == 0) {
407 				silent_cerr("PrintSymbolTableDR::Read(): warning, namespace \"" << sName << "\" "
408 					"has no symbol table at line " << HP.GetLineData() << std::endl);
409 
410 			} else {
411 				silent_cout( "namespace \"" << sName << "\" symbol table at line "
412 					<< HP.GetLineData() << ":" << std::endl
413 					<< *pT << std::endl);
414 			}
415 		}
416 	}
417 
418 	return true;
419 }
420 
421 struct SetDR : public DescRead {
422 public:
423 	bool Read(HighParser& HP);
424 };
425 
426 bool
Read(HighParser & HP)427 SetDR::Read(HighParser& HP)
428 {
429 	if (!HP.IsArg()) {
430      		silent_cerr("Parser error in SetDR::Read(), "
431      			"arg expected at line "
432      			<< HP.GetLineData() << std::endl);
433      		throw HighParser::ErrColonExpected(MBDYN_EXCEPT_ARGS);
434 	}
435 
436 	TypedValue v;
437 	HP.GetValue(v);
438 
439 	return true;
440 }
441 
442 struct SetEnvDR : public DescRead {
443 public:
444 	bool Read(HighParser& HP);
445 };
446 
447 bool
Read(HighParser & HP)448 SetEnvDR::Read(HighParser& HP)
449 {
450 #ifdef HAVE_SETENV
451 	if (!HP.IsArg()) {
452      		silent_cerr("Parser error in SetEnvDR::Read(), "
453      			"arg(s) expected at line "
454      			<< HP.GetLineData() << std::endl);
455      		throw HighParser::ErrColonExpected(MBDYN_EXCEPT_ARGS);
456 	}
457 
458 	int overwrite = 0;
459 	if (HP.IsKeyWord("overwrite")) {
460 		bool b = HP.GetYesNoOrBool();
461 		overwrite = b ? 1 : 0;
462 	}
463 
464 	const char *ava = HP.GetStringWithDelims();
465 	if (ava == NULL) {
466 		silent_cerr("unable to get AVA for \"setenv\" at line "
467 				<< HP.GetLineData() << std::endl);
468 		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
469 	}
470 
471 	char *avasep = std::strchr(const_cast<char *>(ava), '=');
472 	if (avasep == NULL) {
473 #ifdef HAVE_UNSETENV
474 		unsetenv(ava);
475 #elif defined(HAVE_PUTENV)
476 		if (putenv(ava)) {
477 			silent_cerr("unable to unset the environment variable "
478 					"\"" << ava << "\" at line "
479 					<< HP.GetLineData() << std::endl);
480 			throw ErrGeneric(MBDYN_EXCEPT_ARGS);
481 		}
482 #endif	/* !HAVE_UNSETENV && !HAVE_PUTENV */
483 
484 	} else {
485 		if (avasep == ava) {
486 			silent_cerr("illegal AVA \"" << ava
487 					<< "\" at line "
488 					<< HP.GetLineData() << std::endl);
489 			throw ErrGeneric(MBDYN_EXCEPT_ARGS);
490 		}
491 
492 		avasep[0] = '\0';
493 		avasep++;
494 		bool bPresent(getenv(ava) != NULL);
495 		int rc = setenv(ava, avasep, overwrite);
496 		if (rc) {
497 			silent_cerr("unable to set the environment variable \""
498 					<< ava << "\" to \"" << avasep
499 					<< "\" at line " << HP.GetLineData()
500 					<< std::endl);
501 			throw ErrGeneric(MBDYN_EXCEPT_ARGS);
502 		}
503 
504 		if (bPresent && overwrite == 0) {
505 			silent_cout("Environment variable \"" << ava
506 				<< "\" _not_ overwritten with \"" << avasep
507 				<< "\" (current value is \"" << getenv(ava)
508 				<< "\") at line " << HP.GetLineData()
509 				<< std::endl);
510 
511 		} else if (!bPresent) {
512 			silent_cout("Environment variable \"" << ava
513 				<< "\" set to \"" << avasep
514 				<< "\" at line " << HP.GetLineData()
515 				<< std::endl);
516 
517 		} else {
518 			silent_cout("Environment variable \"" << ava
519 				<< "\" overwritten to \"" << avasep
520 				<< "\" at line " << HP.GetLineData()
521 				<< std::endl);
522 		}
523 	}
524 #else // ! HAVE_SETENV
525 	silent_cerr("SetEnvDR::Read(): warning, setenv() not available; ignored at line "
526 		<< HP.GetLineData() << std::endl);
527 #endif // !HAVE_SETENV
528 	return true;
529 }
530 
531 struct ExitDR : public DescRead {
532 public:
533 	bool Read(HighParser& HP);
534 };
535 
536 bool
Read(HighParser & HP)537 ExitDR::Read(HighParser& HP)
538 {
539 	if (!HP.IsDescription()) {
540 		silent_cerr("Parser error in ExitDR::Read(),"
541 			" semicolon expected at line "
542 			<< HP.GetLineData() << std::endl);
543 		throw HighParser::ErrSemicolonExpected(MBDYN_EXCEPT_ARGS);
544 	}
545 
546 	/* exits with no error */
547 	throw NoErr(MBDYN_EXCEPT_ARGS);
548 }
549 
550 static unsigned desc_done;
551 
552 static void
InitDescData(void)553 InitDescData(void)
554 {
555 	if (::desc_done++ > 0) {
556 		return;
557 	}
558 
559 	SetDescData("remark", new RemarkDR);
560 	SetDescData("print" "symbol" "table", new PrintSymbolTableDR);
561 	SetDescData("set", new SetDR);
562 	SetDescData("setenv", new SetEnvDR);
563 	SetDescData("exit", new ExitDR);
564 
565 	/* NOTE: add here initialization of new built-in descriptions;
566 	 * alternative ways to register new custom descriptions are:
567 	 * - call SetDescData() from anywhere in the code
568 	 * - write a module that calls SetDescData() from inside a function
569 	 *   called module_init(), and run-time load it using "module load"
570 	 *   in the input file.
571 	 */
572 }
573 
574 static void
DestroyDescData(void)575 DestroyDescData(void)
576 {
577 	if (::desc_done == 0) {
578 		silent_cerr("DestroyDescData() called once too many" << std::endl);
579 		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
580 	}
581 
582 	if (--::desc_done > 0) {
583 		return;
584 	}
585 
586 	/* free stuff */
587 	for (DescFuncMapType::iterator i = DescFuncMap.begin(); i != DescFuncMap.end(); ++i) {
588 		delete i->second;
589 	}
590 	DescFuncMap.clear();
591 }
592 
593 /* DescRead - end */
594 
595 
596 /* HighParser - begin */
597 
598 static std::stack<const HighParser *> pHP;
599 
600 static const HighParser::ErrOut unknownErr = { "(unknown)", "(unknown)", 0 };
601 
602 HighParser::ErrOut
mbdyn_get_line_data(void)603 mbdyn_get_line_data(void)
604 {
605 	if (!pHP.empty()) {
606 		return pHP.top()->GetLineData();
607 	}
608 
609 	return unknownErr;
610 }
611 
612 std::ostream&
mbdyn_print_line_data(std::ostream & out)613 mbdyn_print_line_data(std::ostream& out)
614 {
615 	if (!pHP.empty()) {
616 		out << pHP.top()->GetLineData();
617 	}
618 
619 	return out;
620 }
621 
HighParser(MathParser & MP,InputStream & streamIn)622 HighParser::HighParser(MathParser& MP, InputStream& streamIn)
623 : ESCAPE_CHAR('\\'),
624 LowP(*this),
625 pIn(&streamIn),
626 pf(NULL),
627 MathP(MP),
628 KeyT(0)
629 {
630 	DEBUGCOUTFNAME("HighParser::HighParser");
631 	CurrToken = HighParser::DESCRIPTION;
632 
633 	InitDescData();
634 
635 	pHP.push(this);
636 }
637 
638 
~HighParser(void)639 HighParser::~HighParser(void)
640 {
641 	DEBUGCOUTFNAME("HighParser::~HighParser");
642 	Close();
643 	ASSERT(pHP.top() == this);
644 	pHP.pop();
645 
646 	DestroyDescData();
647 }
648 
649 
650 void
Close(void)651 HighParser::Close(void)
652 {
653 	NO_OP;
654 }
655 
656 
657 const KeyTable*
PutKeyTable(const KeyTable & KT)658 HighParser::PutKeyTable(const KeyTable& KT)
659 {
660 	const KeyTable* oldKey = KeyT;
661 
662 	KeyT = &KT;
663 
664 	return oldKey;
665 }
666 
667 MathParser&
GetMathParser(void)668 HighParser::GetMathParser(void)
669 {
670 	return MathP;
671 }
672 
673 int
GetLineNumber(void) const674 HighParser::GetLineNumber(void) const
675 {
676 	return const_cast<InputStream *>(pIn)->GetLineNumber();
677 }
678 
679 
680 HighParser::ErrOut
GetLineData(void) const681 HighParser::GetLineData(void) const
682 {
683 	ErrOut LineData;
684 	LineData.iLineNumber = GetLineNumber();
685 	LineData.sFileName = NULL;
686 	LineData.sPathName = NULL;
687 	return LineData;
688 }
689 
690 
691 bool
IsDescription(void) const692 HighParser::IsDescription(void) const
693 {
694      	return (CurrToken == HighParser::DESCRIPTION);
695 }
696 
697 HighParser::Token
GotDescription(void)698 HighParser::GotDescription(void)
699 {
700 	return FirstToken();
701 }
702 
703 int
iGetDescription_int(const char * const s)704 HighParser::iGetDescription_int(const char* const s)
705 {
706 	int i = -1;
707 
708 	if (KeyT) {
709 		i = KeyT->Find(s);
710 	}
711 
712      	if (FirstToken() == HighParser::UNKNOWN) {
713 		silent_cerr("Parser error in HighParser::iGetDescription_int(), "
714 			"semicolon expected at line "
715 			<< GetLineData() << std::endl);
716 	  	throw HighParser::ErrSemicolonExpected(MBDYN_EXCEPT_ARGS);
717      	}
718 
719      	return i;
720 }
721 
722 
723 void
Eof(void)724 HighParser::Eof(void)
725 {
726 	 throw EndOfFile(MBDYN_EXCEPT_ARGS);
727 }
728 
729 int
GetDescription(void)730 HighParser::GetDescription(void)
731 {
732 	/* Checks if current token is a description */
733 	if (!IsDescription()) {
734 		silent_cerr("Parser error in HighParser::GetDescription, "
735 			"invalid call to GetDescription at line "
736 			<< GetLineData() << std::endl);
737 		throw HighParser::ErrInvalidCallToGetDescription(MBDYN_EXCEPT_ARGS);
738 	}
739 
740 restart_parsing:;
741 
742 	CurrLowToken = LowP.GetToken(*pIn);
743 	if (CurrLowToken != LowParser::WORD) {
744 		if (pIn->eof()) {
745 			Eof();
746 			goto restart_parsing;
747 		}
748 
749 		silent_cerr("Parser error in HighParser::GetDescription, "
750 			<< "keyword expected at line "
751 			<< GetLineData() << std::endl);
752 		throw HighParser::ErrKeyWordExpected(MBDYN_EXCEPT_ARGS);
753 	}
754 
755 	/* Description corrente */
756 	char* s = LowP.sGetWord();
757 
758  	if (ReadDescription(*this, s)) {
759 		goto restart_parsing;
760 	}
761 
762 	return iGetDescription_int(s);
763 }
764 
765 
766 HighParser::Token
FirstToken(void)767 HighParser::FirstToken(void)
768 {
769 	CurrLowToken = LowP.GetToken(*pIn);
770 
771 	switch (CurrLowToken) {
772 	case LowParser::COLON:
773 		CurrToken = HighParser::ARG;
774 		break;
775 
776 	case LowParser::SEMICOLON:
777 		CurrToken = HighParser::DESCRIPTION;
778 		break;
779 
780 	default:
781 		CurrToken = HighParser::UNKNOWN;
782 		break;
783 	}
784 
785 	return CurrToken;
786 }
787 
788 void
ExpectDescription(void)789 HighParser::ExpectDescription(void)
790 {
791 	/* forces the next expected token to be a "description"
792 	 * e.g. a keyword followed by a colon (deprecated) */
793 	CurrToken = HighParser::DESCRIPTION;
794 }
795 
796 
797 void
ExpectArg(void)798 HighParser::ExpectArg(void)
799 {
800 	/* forces the next expected token to be an argument
801 	 * e.g. a keyword followed by a separator (deprecated) */
802 	CurrToken = HighParser::ARG;
803 }
804 
805 
806 bool
IsArg(void)807 HighParser::IsArg(void)
808 {
809 	return (CurrToken == ARG);
810 }
811 
812 void
PutBackSemicolon(void)813 HighParser::PutBackSemicolon(void)
814 {
815 	if (CurrLowToken == LowParser::SEMICOLON) {
816 		pIn->putback(';');
817 	}
818 }
819 
820 
821 void
NextToken(const char * sFuncName)822 HighParser::NextToken(const char* sFuncName)
823 {
824 	CurrLowToken = LowP.GetToken(*pIn);
825 	switch (CurrLowToken) {
826 	case LowParser::COMMA:
827 		CurrToken = HighParser::ARG;
828 		break;
829 
830 	case LowParser::SEMICOLON:
831 		CurrToken = HighParser::DESCRIPTION;
832 		break;
833 
834 	default:
835 		silent_cerr("Parser error in "
836 			<< sFuncName << ", missing separator at line "
837 			<< GetLineData() << std::endl);
838 		throw HighParser::ErrMissingSeparator(MBDYN_EXCEPT_ARGS);
839 	}
840 }
841 
842 int
ParseWord(unsigned flags)843 HighParser::ParseWord(unsigned flags)
844 {
845 	char* sBuf = sStringBuf;
846 	char* sBufWithSpaces = sStringBufWithSpaces;
847 
848 	char cIn;
849 	if (skip_remarks(*this, *pIn, cIn)) {
850 		return CurrToken = HighParser::ENDOFFILE;
851 	}
852 
853 	if (!isalpha(cIn) && cIn != '_') {
854 		pIn->putback(cIn);
855 		return -1;
856 	}
857 
858 	*sBufWithSpaces++ = cIn;
859 
860 	if (flags & LOWER) {
861 		*sBuf++ = tolower(cIn);
862 
863 	} else if (flags & UPPER) {
864 		*sBuf++ = toupper(cIn);
865 
866 	} else {
867 		*sBuf++ = cIn;
868 	}
869 
870 	for (cIn = pIn->get(); isalnum(cIn) || cIn == '_' || isspace(cIn); cIn = pIn->get()) {
871 		*sBufWithSpaces++ = cIn;
872 		if (sBufWithSpaces >= sStringBufWithSpaces + iDefaultBufSize - 1) {
873 			break;
874 		}
875 
876 		if (isspace(cIn)) {
877 			continue;
878 		}
879 
880 		if (flags & LOWER) {
881 			*sBuf++ = tolower(cIn);
882 
883 		} else if (flags & UPPER) {
884 			*sBuf++ = toupper(cIn);
885 
886 		} else {
887 			*sBuf++ = cIn;
888 		}
889 	}
890 	pIn->putback(cIn);
891 
892 	*sBuf = '\0';
893 	*sBufWithSpaces = '\0';
894 
895 	return 0;
896 }
897 
898 void
PutbackWord(void)899 HighParser::PutbackWord(void)
900 {
901 	char* sBufWithSpaces = sStringBufWithSpaces + strlen(sStringBufWithSpaces);
902 
903 
904 	while (sBufWithSpaces > sStringBufWithSpaces) {
905 		pIn->putback(*--sBufWithSpaces);
906 	}
907 }
908 
909 bool
IsKeyWord(const char * sKeyWord)910 HighParser::IsKeyWord(const char* sKeyWord)
911 {
912 	const char sFuncName[] = "HighParser::IsKeyWord()";
913 
914 	if (CurrToken != HighParser::ARG) {
915 		return false;
916 	}
917 
918 	switch (ParseWord()) {
919 	case 0:
920 		break;
921 
922 	case HighParser::ENDOFFILE:
923 		return true;
924 
925 	default:
926 		return false;
927 	}
928 
929 	if (!strcasecmp(sStringBuf, sKeyWord)) {
930 		NextToken(sFuncName);
931 		return true;
932 	}
933 
934 	PutbackWord();
935 
936 	return false;
937 }
938 
939 int
IsKeyWord(void)940 HighParser::IsKeyWord(void)
941 {
942 	const char sFuncName[] = "HighParser::IsKeyWord()";
943 
944 	if (CurrToken != HighParser::ARG) {
945 		return -1;
946 	}
947 
948 	switch (ParseWord()) {
949 	case 0:
950 		break;
951 
952 	case HighParser::ENDOFFILE:
953 		return HighParser::ENDOFFILE;
954 
955 	default:
956 		return -1;
957 	}
958 
959 	int iKW = -1;
960 
961 	if (KeyT) {
962 		iKW = KeyT->Find(sStringBuf);
963 	}
964 
965 	if (iKW >= 0) {
966 		NextToken(sFuncName);
967 		return iKW;
968 	}
969 
970 	PutbackWord();
971 
972 	return -1;
973 }
974 
975 /* 1 se l'argomento successivo e' una parola in un WordSet */
976 const char *
IsWord(const HighParser::WordSet & ws)977 HighParser::IsWord(const HighParser::WordSet& ws)
978 {
979 	const char sFuncName[] = "HighParser::IsWord()";
980 
981 	if (CurrToken != HighParser::ARG) {
982 		return 0;
983 	}
984 
985 	switch (ParseWord()) {
986 	case 0:
987 		break;
988 
989 	default:
990 		return 0;
991 	}
992 
993 	if (ws.IsWord(std::string(sStringBuf))) {
994 		NextToken(sFuncName);
995 		return sStringBuf;
996 	}
997 
998 	PutbackWord();
999 
1000 	return 0;
1001 }
1002 
1003 TypedValue
GetValue(const TypedValue & vDefVal)1004 HighParser::GetValue(const TypedValue& vDefVal)
1005 {
1006 	return GetValue<range_any<TypedValue> >(vDefVal, range_any<TypedValue>());
1007 }
1008 
1009 bool
GetBool(bool bDefVal)1010 HighParser::GetBool(bool bDefVal)
1011 {
1012 	TypedValue v(bDefVal);
1013 	v = GetValue(v);
1014 	return v.GetBool();
1015 }
1016 
1017 /*
1018  * Read a boolean as "yes" or "no" and put the result in bRet
1019  * return true in case of success, false otherwise (bRet undefined)
1020  */
1021 bool
GetYesNo(bool & bRet)1022 HighParser::GetYesNo(bool& bRet)
1023 {
1024 	if (IsKeyWord("yes")) {
1025 		bRet = true;
1026 
1027 	} else if (IsKeyWord("no")) {
1028 		bRet = false;
1029 
1030 	} else {
1031 		return false;
1032 	}
1033 
1034 	return true;
1035 }
1036 
1037 bool
GetYesNoOrBool(bool bDefval)1038 HighParser::GetYesNoOrBool(bool bDefval)
1039 {
1040 	bool bRet;
1041 
1042 	if (!GetYesNo(bRet)) {
1043 		bRet = GetBool(bDefval);
1044 	}
1045 
1046 	return bRet;
1047 }
1048 
1049 integer
GetInt(integer iDefVal)1050 HighParser::GetInt(integer iDefVal)
1051 {
1052 	return GetInt<range_any<integer> >(iDefVal, range_any<integer>());
1053 }
1054 
1055 doublereal
GetReal(const doublereal & dDefVal)1056 HighParser::GetReal(const doublereal& dDefVal)
1057 {
1058 	return GetReal<range_any<doublereal> >(dDefVal, range_any<doublereal>());
1059 }
1060 
1061 mbsleep_t
GetTimeout(const mbsleep_t & DefVal)1062 HighParser::GetTimeout(const mbsleep_t& DefVal)
1063 {
1064 	doublereal d;
1065 	mbsleep_sleep2real(&DefVal, &d);
1066 	TypedValue v(d);
1067 	v = GetValue(v);
1068 	mbsleep_t newval;
1069 	mbsleep_real2sleep(v.GetReal(), &newval);
1070 	return newval;
1071 }
1072 
1073 std::string
GetString(const std::string & sDefVal)1074 HighParser::GetString(const std::string& sDefVal)
1075 {
1076 	TypedValue v(sDefVal);
1077 	v = GetValue(v);
1078 	return v.GetString();
1079 }
1080 
1081 
1082 int
GetWord(void)1083 HighParser::GetWord(void)
1084 {
1085 	const char sFuncName[] = "HighParser::GetWord()";
1086 
1087 	if (CurrToken != HighParser::ARG) {
1088 		silent_cerr("Parser error in "
1089 			<< sFuncName << ", keyword arg expected at line "
1090 			<< GetLineData() << std::endl);
1091 		throw HighParser::ErrKeyWordExpected(MBDYN_EXCEPT_ARGS);
1092 	}
1093 
1094 	CurrLowToken = LowP.GetToken(*pIn);
1095 	if (CurrLowToken != LowParser::WORD) {
1096 		silent_cerr("Parser error in "
1097 			<< sFuncName << ", keyword expected at line "
1098 			<< GetLineData() << std::endl);
1099 		throw HighParser::ErrKeyWordExpected(MBDYN_EXCEPT_ARGS);
1100 	}
1101 
1102 	int i = -1;
1103 	if (KeyT) {
1104 		i = KeyT->Find(LowP.sGetWord());
1105 	}
1106 
1107 	NextToken(sFuncName);
1108 
1109 	return i;
1110 }
1111 
1112 const char*
GetString(unsigned flags)1113 HighParser::GetString(unsigned flags)
1114 {
1115 	const char sFuncName[] = "HighParser::GetString()";
1116 
1117 	pedantic_cout("use of deprecated method \"GetString\" at line"
1118 		<< GetLineData() << std::endl);
1119 
1120 	if (CurrToken != HighParser::ARG) {
1121 		silent_cerr("Parser error in "
1122 			<< sFuncName << ", string arg expected at line "
1123 			<< GetLineData() << std::endl);
1124 		throw HighParser::ErrStringExpected(MBDYN_EXCEPT_ARGS);
1125 	}
1126 
1127 	char* s = sStringBuf;
1128 	char* sTmp = s;
1129 
1130 	char cIn = '\0';
1131 
1132 	while (isspace(cIn = pIn->get())) {
1133 		NO_OP;
1134 	}
1135 
1136 	if (pIn->eof()) {
1137 		CurrToken = HighParser::ENDOFFILE;
1138 		return NULL;
1139 	}
1140 
1141 	pIn->putback(cIn);
1142 	for (cIn = pIn->get(); cIn != ',' && cIn != ';'; cIn = pIn->get()) {
1143 		/* Attenzione! cosi' la legge tutta,
1144 		 * ma ne tiene solo iBufSize-1 caratteri */
1145 		if (pIn->eof()) {
1146 			CurrToken = HighParser::ENDOFFILE;
1147 			*sTmp = '\0';
1148 			return s;
1149 
1150 		} else if (sTmp < s + iDefaultBufSize - 1) {
1151 			if (!(flags & HighParser::EATSPACES) || !isspace(cIn)) {
1152 				if (flags & HighParser::LOWER) {
1153 					cIn = tolower(cIn);
1154 
1155 				} else if (flags & HighParser::UPPER) {
1156 					cIn = toupper(cIn);
1157 				}
1158 
1159 				*sTmp++ = cIn;
1160 			}
1161 		}
1162 	}
1163 
1164 	pIn->putback(cIn);
1165 	*sTmp = '\0';
1166 
1167 	NextToken(sFuncName);
1168 
1169 	return s;
1170 }
1171 
1172 void
SetDelims(enum Delims Del,char & cLdelim,char & cRdelim) const1173 HighParser::SetDelims(enum Delims Del, char &cLdelim, char &cRdelim) const
1174 {
1175 	cLdelim = '\0';
1176 	cRdelim = '\0';
1177 
1178 	switch (Del) {
1179 	case PLAINBRACKETS:
1180 		cLdelim = '(';
1181 		cRdelim = ')';
1182 		break;
1183 
1184 	case SQUAREBRACKETS:
1185 		cLdelim = '[';
1186 		cRdelim = ']';
1187 		break;
1188 
1189 	case CURLYBRACKETS:
1190 		cLdelim = '{';
1191 		cRdelim = '}';
1192 		break;
1193 
1194 	case SINGLEQUOTE:
1195 		cLdelim = '`';
1196 		cRdelim = '\'';
1197 		break;
1198 
1199 	default:
1200 	case UNKNOWNDELIM:
1201 	case DEFAULTDELIM:
1202 	case DOUBLEQUOTE:
1203 		cLdelim = '"';
1204 		cRdelim = '"';
1205 		break;
1206 	}
1207 }
1208 
1209 bool
IsStringWithDelims(enum Delims Del)1210 HighParser::IsStringWithDelims(enum Delims Del)
1211 {
1212 	char cLdelim, cRdelim;
1213 	SetDelims(Del, cLdelim, cRdelim);
1214 
1215 	char cIn;
1216 	if (skip_remarks(*this, *pIn, cIn)) {
1217 		return false;
1218 	}
1219 
1220 	/* put back the first non-remark char */
1221 	pIn->putback(cIn);
1222 
1223 	/* if the left delimiter is found, true */
1224 	return (cIn == cLdelim);
1225 }
1226 
1227 const char*
GetStringWithDelims(enum Delims Del,bool escape)1228 HighParser::GetStringWithDelims(enum Delims Del, bool escape)
1229 {
1230 	const char sFuncName[] = "HighParser::GetStringWithDelims()";
1231 
1232 	if (CurrToken != HighParser::ARG) {
1233 		silent_cerr("Parser error in "
1234 			<< sFuncName << ", string arg expected at line "
1235 			<< GetLineData() << std::endl);
1236 		throw HighParser::ErrStringExpected(MBDYN_EXCEPT_ARGS);
1237 	}
1238 
1239 	char* s = sStringBuf;
1240 	char* sTmp = s;
1241 
1242 	char cLdelim, cRdelim;
1243 	SetDelims(Del, cLdelim, cRdelim);
1244 
1245 	char cIn;
1246 	if (skip_remarks(*this, *pIn, cIn)) {
1247 		return NULL;
1248 	}
1249 
1250 	/* Se trova il delimitatore sinistro, legge la stringa */
1251 	if (cIn == cLdelim) {
1252 		for (cIn = pIn->get(); cIn != cRdelim; cIn = pIn->get()) {
1253 			/* Attenzione! cosi' la legge tutta,
1254 			 * ma ne tiene solo iBufSize-1 caratteri */
1255 			if (pIn->eof()) {
1256 				/* FIXME: this should be an error ... */
1257 				sTmp[0] = '\0';
1258 				return s;
1259 
1260 			} else if (sTmp < s + iDefaultBufSize - 1) {
1261 				if (cIn == ESCAPE_CHAR) {
1262 					cIn = pIn->get();
1263 					if (cIn == '\n') {
1264 
1265 						/*
1266 						 * eat the newline as well, so that
1267 
1268 							"first line\
1269 							second line"
1270 
1271 						 * actually results in "first linesecond line"
1272 						 */
1273 
1274 						cIn = pIn->get();
1275 
1276 					} else if (cIn == '\r') {
1277 						cIn = pIn->get();
1278 						if (cIn != '\n') {
1279 							pIn->putback(cIn);
1280 							goto escaped_generic;
1281 						}
1282 						cIn = pIn->get();
1283 
1284 					} else if ((cIn == ESCAPE_CHAR) || (cIn == cRdelim)) {
1285 						if (!escape) {
1286 							sTmp[0] = ESCAPE_CHAR;
1287 							++sTmp;
1288 						}
1289 
1290 					} else {
1291 escaped_generic:;
1292 						if (escape) {
1293 							int i, c = 0;
1294 							char hex[3];
1295 
1296 							/*
1297 							 * allow non-printable chars in the form "\<hexpair>",
1298 							 * so that "\78" is equivalent to "x";
1299 							 * "\<non-hexpair>" is treated as an error.
1300 							 */
1301 
1302 							hex[0] = cIn;
1303 							hex[1] = pIn->get();
1304 							hex[2] = '\0';
1305 
1306 							for (i = 0; i < 2; i++) {
1307 								int shift = 4*(1 - i), h = 0;
1308 
1309 								/* NOTE: this conversion relies
1310 								 * on 0-9, a-f, A-F being consecutive,
1311 								 * which is true for ASCII, but might
1312 								 * not be for other encodings;
1313 								 * bah, not critical right now */
1314 								if (hex[i] >= '0' && hex[i]  <= '9') {
1315 									h = hex[i] - '0';
1316 								} else if (hex[i] >= 'a' && hex[i] <= 'f') {
1317 									h = hex[i] - 'a';
1318 								} else if (hex[i] >= 'A' && hex[i] <= 'F') {
1319 									h = hex[i] - 'A';
1320 								} else {
1321 									silent_cerr("invalid escape sequence "
1322 										"\"\\" << hex << "\" "
1323 										"at line " << GetLineData()
1324 										<< std::endl);
1325 									throw ErrGeneric(MBDYN_EXCEPT_ARGS);
1326 								}
1327 
1328 								c += (h << shift);
1329 							}
1330 							cIn = c;
1331 
1332 						} else {
1333 							sTmp[0] = ESCAPE_CHAR;
1334 							++sTmp;
1335 						}
1336 					}
1337 				}
1338 				sTmp[0] = cIn;
1339 				++sTmp;
1340 			}
1341 		}
1342 
1343 		/* Se trova una virgola o un punto e virgola, le rimette nello stream
1344 		 * e passa oltre, restituendo un puntatore nullo. Il chiamante deve
1345 		 * occuparsi della gestione del valore di default */
1346 	} else if (cIn == ',' || cIn == ';') {
1347 		pIn->putback(cIn);
1348 		goto nullstring;
1349 
1350 		/* Altrimenti c'e' qualcosa senza delimitatore. Adesso da' errore,
1351 		 * forse e' piu' corretto fargli ritornare lo stream intatto */
1352 	} else {
1353 		silent_cerr("Parser error in "
1354 			<< sFuncName << std::endl
1355 			<< "first non-blank char at line "
1356 			<< GetLineData() << " isn't a valid left-delimiter"
1357 			<< std::endl);
1358 		throw HighParser::ErrIllegalDelimiter(MBDYN_EXCEPT_ARGS);
1359 	}
1360 
1361 	/* Mette zero al termine della stringa */
1362 	*sTmp = '\0';
1363 
1364 nullstring:;
1365 	NextToken(sFuncName);
1366 	return s;
1367 }
1368 
1369 /* HighParser - end */
1370 
1371 std::ostream&
operator <<(std::ostream & out,const HighParser::ErrOut & err)1372 operator << (std::ostream& out, const HighParser::ErrOut& err)
1373 {
1374 	out << err.iLineNumber;
1375 
1376 	if (err.sFileName != 0) {
1377 		out << ", file <";
1378 		if (err.sPathName != 0) {
1379 			out << err.sPathName << DIR_SEP;
1380 		}
1381 		out << err.sFileName << '>';
1382 	}
1383 
1384 	return out;
1385 }
1386 
1387 /* HighParser - end */
1388 
1389