OPALX (Object Oriented Parallel Accelerator Library for Exascal) MINIorX
OPALX
OpalParser.cpp
Go to the documentation of this file.
1
2// ------------------------------------------------------------------------
3// $RCSfile: OpalParser.cpp,v $
4// ------------------------------------------------------------------------
5// $Revision: 1.3 $
6// ------------------------------------------------------------------------
7// Copyright: see Copyright.readme
8// ------------------------------------------------------------------------
9//
10// Class OpalParser:
11// This is the default parser for OPAL statements.
12//
13// ------------------------------------------------------------------------
14//
15// $Date: 2001/08/13 15:17:27 $
16// $Author: jowett $
17//
18// ------------------------------------------------------------------------
19
31
32#include <boost/algorithm/string.hpp>
33#include <cmath>
34#include <ctime>
35#include <exception>
36#include <iostream>
37#include <memory>
38#include <new>
40#include "OpalParser/Token.h"
42#include "Utilities/Options.h"
44
45#include "Utility/Inform.h"
46#include "Utility/IpplInfo.h"
47
48using namespace Expressions;
49
50extern Inform* gmsg;
51
52// Class OpalParser
53// ------------------------------------------------------------------------
54
55std::vector<std::shared_ptr<TokenStream> > OpalParser::inputStack;
56
59
62
63void OpalParser::parse(Statement& stat) const {
64 if (stat.keyword("SHARED")) {
65 // "SHARED ...": Shared object definition.
66 parseDefine(stat);
67 } else if (
68 stat.keyword("CONSTANT") || stat.keyword("CONST") || stat.keyword("BOOL")
69 || stat.keyword("REAL") || stat.keyword("STRING") || stat.keyword("VECTOR")) {
70 // Keywords introducing variable definitions.
71 parseAssign(stat);
72 } else {
73 std::string name = parseString(stat, "Identifier or keyword expected.");
74
75 if (stat.delimiter('?')) {
76 // "<class>?": give help for class.
77 printHelp(name);
78 } else if (stat.delimiter(':')) {
79 // "<object>:<class>...": labeled command.
80 parseDefine(stat);
81 } else if (stat.delimiter('(')) {
82 // "<macro>(...)...": macro definition or call.
83 // We are positioned just after the '(' of the argument list.
84 parseMacro(name, stat);
85 } else if (stat.delimiter(',') || stat.delimiter(';') || stat.atEnd()) {
86 // "<class>" or "<class>,<attributes>": Executable command.
87 parseAction(stat);
88 } else {
89 // Assignment beginning with a name.
90 stat.mark();
91 stat.start();
92 std::string objName = parseString(stat, "Object name expected.");
93 if (OpalData::getInstance()->find(objName) == 0) {
94 Token tok = stat.getCurrent();
95 stat.restore();
96
97 std::string name = tok.getLex();
98 std::string hint = getHint(name);
99 unsigned int position = stat.position();
100 std::string positionIndicator = std::string(position, ' ') + "^\n";
101 std::ostringstream statStr;
102 stat.print(statStr);
103 if (!hint.empty()) {
104 throw ParseError(
105 "OpalParser::parse()",
106 statStr.str() + positionIndicator
107 + "Syntax error, either the keyword REAL is missing or\n" + hint);
108 } else {
109 throw ParseError(
110 "OpalParser::parse()", statStr.str() + positionIndicator
111 + "Syntax error, the keyword REAL is missing\n");
112 }
113 }
114 parseAssign(stat);
115 }
116 }
117}
118
119void OpalParser::execute(Object* object, const std::string& name) const {
120 // Trace execution.
121 if (Options::mtrace && object->shouldTrace()) {
122 double time = double(clock()) / double(CLOCKS_PER_SEC);
123 *gmsg << "\nBegin execution: \"" << name << "\", CPU time = " << time << " seconds.\n"
124 << endl;
125 }
126
127 // Force updating of all attributes which might have been changed.
128 if (object->shouldUpdate()) {
130 }
131
132 // Execute or check the command.
133 object->execute();
134
135 // Trace execution.
136 if (Options::mtrace && object->shouldTrace()) {
137 double time = double(clock()) / double(CLOCKS_PER_SEC);
138 *gmsg << "\nEnd execution: \"" << name << "\", CPU time = " << time << " seconds.\n"
139 << endl;
140 }
141}
142
143Object* OpalParser::find(const std::string& name) const {
144 return OpalData::getInstance()->find(name);
145}
146
148 stat.start();
149 std::string cmdName = parseString(stat, "Command name expected");
150
151 if (cmdName == "STOP") {
152 stopFlag = true;
153 } else if (cmdName == "QUIT") {
154 stopFlag = true;
155 } else if (cmdName == "HELP" && stat.delimiter(',')) {
156 cmdName = parseString(stat, "Object name expected");
157 printHelp(cmdName);
158 } else if (Object* object = find(cmdName)) {
159 Object* copy = 0;
160 try {
161 copy = object->clone("");
162 copy->parse(stat);
163 parseEnd(stat);
164 execute(copy, cmdName);
165 delete copy;
166 } catch (...) {
167 delete copy;
168 throw;
169 }
170 } else {
171 std::string hint = getHint(cmdName, "command");
172 if (!hint.empty()) {
173 throw ParseError("OpalParser::parseAction()", "Syntax error, " + hint);
174 }
175
176 throw ParseError("OpalParser::parseAction()", "Command \"" + cmdName + "\" is unknown.");
177 }
178}
179
181 stat.start();
182
183 // Find various model objects.
184 /*static*/
185 Object* boolConstant = OpalData::getInstance()->find("BOOL_CONSTANT");
186 /*static*/
187 Object* realConstant = OpalData::getInstance()->find("REAL_CONSTANT");
188 /*static*/
189 Object* realVariable = OpalData::getInstance()->find("REAL_VARIABLE");
190 /*static*/
191 Object* realVector = OpalData::getInstance()->find("REAL_VECTOR");
192 /*static*/
193 Object* stringConstant = OpalData::getInstance()->find("STRING_CONSTANT");
194
195 // Gobble up any prefix.
196 int code = 0x00;
197 while (true) {
198 if (stat.keyword("CONSTANT") || stat.keyword("CONST")) {
199 code |= 0x01;
200 } else if (stat.keyword("BOOL")) {
201 code |= 0x02;
202 } else if (stat.keyword("REAL")) {
203 code |= 0x04;
204 } else if (stat.keyword("STRING")) {
205 code |= 0x08;
206 } else if (stat.keyword("VECTOR")) {
207 code |= 0x10;
208 } else {
209 break;
210 }
211 }
212
213 std::string objName = parseString(stat, "Object name expected.");
214 // Test for attribute name.
215 Object* object = 0;
216 std::string attrName;
217
218 if (stat.delimiter("->")) {
219 // Assignment to object attribute.
220 attrName = parseString(stat, "Attribute name expected.");
221
222 if (code != 0) {
223 throw ParseError(
224 "OpalParser::parseAssign()", "Invalid type specification for this value.");
225 } else if ((object = OpalData::getInstance()->find(objName)) == 0) {
226 throw ParseError(
227 "OpalParser::parseAssign()", "The object \"" + objName + "\" is unknown.");
228 }
229 } else {
230 // Assignment to variable-like object.
231 if ((object = OpalData::getInstance()->find(objName)) == 0) {
232 Object* model = 0;
233 switch (code) {
234 case 0x01: // CONSTANT
235 case 0x05: // CONSTANT REAL
236 model = realConstant;
237 break;
238 case 0x02: // BOOL
239 case 0x03: // BOOL CONSTANT
240 model = boolConstant;
241 break;
242 case 0x00: // empty <type>.
243 case 0x04: // REAL
244 model = realVariable;
245 break;
246 case 0x10: // VECTOR
247 case 0x11: // CONSTANT VECTOR
248 case 0x14: // REAL VECTOR
249 case 0x15: // CONSTANT REAL VECTOR
250 model = realVector;
251 break;
252 case 0x08: // STRING
253 case 0x09: // STRING CONSTANT
254 model = stringConstant;
255 break;
256 default:
257 break;
258 }
259
260 if (model != 0) {
261 object = model->clone(objName);
262 OpalData::getInstance()->define(object);
263 } else {
264 throw ParseError("OpalParser::parseAssign()", "Invalid <type> field.");
265 }
266 } else if (object->isTreeMember(realConstant)) {
267 throw ParseError(
268 "OpalParser::parseAssign()",
269 "You cannot redefine the constant \"" + objName + "\".");
270 }
271
272 attrName = "VALUE";
273 }
274
275 // Test for index; it is evaluated immediately.
276 int index = 0;
277
278 if (stat.delimiter('[')) {
279 index = int(std::round(parseRealConst(stat)));
280 parseDelimiter(stat, ']');
281
282 if (index <= 0) {
283 throw ParseError("Expressions::parseReference()", "Index must be positive.");
284 }
285 }
286
287 if (object != 0) {
288 if (Attribute* attr = object->findAttribute(attrName)) {
289 if (stat.delimiter('=') || object->isTreeMember(realConstant)) {
290 if (index > 0) {
291 attr->parseComponent(stat, true, index);
292 } else {
293 attr->parse(stat, true);
294 }
295 } else if (stat.delimiter(":=")) {
296 if (index > 0) {
297 attr->parseComponent(stat, false, index);
298 } else {
299 attr->parse(stat, false);
300 }
301 }
302 } else {
303 throw ParseError(
304 "OpalParser::parseAssign()",
305 "Object \"" + objName + "\" has no attribute \"" + attrName + "\".");
306 }
307
308 parseEnd(stat);
310 }
311}
312
314 stat.start();
315 bool isShared = stat.keyword("SHARED");
316 std::string objName = parseString(stat, "Object name expected.");
317
318 if (stat.delimiter(':')) {
319 std::string clsName = parseString(stat, "Class name expected.");
320 Object* classObject = find(clsName);
321
322 if (classObject == 0) {
323 if (clsName == "SURFACEPHYSICS")
324 throw ParseError(
325 "OpalParser::parseDefine()",
326 "The object \"" + clsName + "\" is changed to \"PARTICLEMATTERINTERACTION\".");
327 else
328 throw ParseError(
329 "OpalParser::parseDefine()", "The object \"" + clsName + "\" is unknown.");
330 }
331
332 Object* copy = 0;
333 try {
334 if (stat.delimiter('(')) {
335 // Macro-like objects are always classes, instances never.
336 // There is no further check required.
337 copy = classObject->makeInstance(objName, stat, this);
338 } else {
339 copy = classObject->clone(objName);
340 copy->parse(stat);
341 copy->setShared(isShared);
342 }
343
344 parseEnd(stat);
345 execute(copy, clsName);
347 } catch (...) {
348 delete copy;
349 throw;
350 }
351 } else {
352 // Redefine an object to be a class.
353 Object* classObject = find(objName);
354 Object* copy = classObject->clone(objName);
355 copy->parse(stat);
356 copy->setShared(isShared);
357 }
358}
359
361 if (!stat.atEnd() && !stat.delimiter(';')) {
362 unsigned int position = stat.position();
363 std::string positionIndicator = std::string(position + 1, ' ') + "^\n";
364 std::ostringstream statStr;
365 stat.print(statStr);
366
367 throw ParseError(
368 "OpalParser::parseEnd()", statStr.str() + positionIndicator
369 + "Syntax error (maybe missing comma or semicolon ? )");
370 }
371}
372
373void OpalParser::parseMacro(const std::string& macName, Statement& stat) const {
374 // Record the position just after the '(' of the argument list.
375 stat.mark();
376
377 // Skip argument list.
378 int par_level = 1;
379 while (true) {
380 if (stat.delimiter('(')) {
381 ++par_level;
382 } else if (stat.delimiter(')')) {
383 if (--par_level == 0)
384 break;
385 } else {
386 stat.getCurrent();
387 }
388 }
389
390 if (stat.delimiter(':')) {
391 // Macro definition.
392 std::string className = parseString(stat, "Class name expected.");
393
394 if (Object* macro = OpalData::getInstance()->find(className)) {
395 // Backtrack to first argument.
396 stat.restore();
397
398 if (Object* copy = macro->makeTemplate(macName, *inputStack.back(), stat)) {
400 } else {
401 throw ParseError(
402 "OpalParser::parseMacro()",
403 "Command \"" + macName + "\" cannot be defined with arguments.");
404 }
405 } else {
406 throw ParseError(
407 "OpalParser::parseMacro()", "Object \"" + className + "\" is unknown.");
408 }
409 } else {
410 // Macro call.
411 if (Object* macro = OpalData::getInstance()->find(macName)) {
412 // Backtrack to first argument.
413 stat.restore();
414 Object* instance = 0;
415 try {
416 instance = macro->makeInstance(macName, stat, this);
417 execute(instance, macName);
418 } catch (...) {
419 delete instance;
420 throw;
421 }
422 } else {
423 throw ParseError("OpalParser::parseMacro()", "Macro \"" + macName + "\" is unknown.");
424 }
425 }
426}
427
428void OpalParser::printHelp(const std::string& cmdName) const {
429 Object* object = find(cmdName);
430
431 if (object == 0) {
432 *gmsg << "\nOpalParser::printHelp(): Unknown object \"" << cmdName << "\".\n" << endl;
433 } else {
434 object->printHelp(std::cerr);
435 }
436}
437
439 Token token = readToken();
440
441 while (!token.isEOF()) {
442 stat.append(token);
443
444 if (token.isDel('(')) {
445 parseBracketList(')', stat);
446 } else if (token.isDel('[')) {
447 parseBracketList(']', stat);
448 } else if (token.isDel('{')) {
449 parseBracketList('}', stat);
450 } else if (token.isDel(close)) {
451 return;
452 }
453
454 token = readToken();
455 }
456}
457
459 Token token = readToken();
460
461 while (!token.isEOF()) {
462 // End of list if semicolon occurs outside of brackets.
463 if (token.isDel(';'))
464 break;
465 stat.append(token);
466
467 if (token.isDel('(')) {
468 parseBracketList(')', stat);
469 } else if (token.isDel('[')) {
470 parseBracketList(']', stat);
471 } else if (token.isDel('{')) {
472 parseBracketList('}', stat);
473 }
474
475 token = readToken();
476 }
477}
478
480 if (inputStack.empty()) {
481 return Token("", 0, Token::IS_EOF, "End of input");
482 } else {
483 return inputStack.back()->readToken();
484 }
485}
486
488 Statement* stat = 0;
489 Token token = is->readToken();
490
491 try {
492 if (token.isDel('{')) {
493 // Compound statement.
494 inputStack.back()->putBack(token);
495 stat = new CompoundStatement(*inputStack.back());
496 } else if (token.isKey("IF")) {
497 // IF statement.
498 inputStack.back()->putBack(token);
499 stat = new IfStatement(*this, *inputStack.back());
500 } else if (token.isKey("WHILE")) {
501 // WHILE statement.
502 inputStack.back()->putBack(token);
503 stat = new WhileStatement(*this, *inputStack.back());
504 } else if (token.isWord() || token.isString()) {
505 // Simple statement or MACRO statement.
506 stat = new SimpleStatement(token.getFile(), token.getLine());
507 stat->append(token);
508 token = is->readToken();
509
510 if (!token.isEOF()) {
511 if (token.isDel('(')) {
512 // Macro statement; statement already contains initial word.
513 stat->append(token);
514 parseBracketList(')', *stat);
515 token = is->readToken();
516
517 if (!token.isEOF() && token.isDel(':')) {
518 // Macro definition.
519 stat->append(token);
520 token = is->readToken();
521
522 if (!token.isEOF()) {
523 stat->append(token);
524 if (token.isKey("MACRO")) {
525 token = is->readToken();
526
527 if (!token.isEOF() && token.isDel('{')) {
528 stat->append(token);
529 parseBracketList('}', *stat);
530 } else {
531 throw ParseError(
532 "OpalParser::readStatement()",
533 "MACRO definition lacks \"{...}\".");
534 }
535 } else {
536 parseTokenList(*stat);
537 }
538 }
539 } else if (!token.isDel(';')) {
540 throw ParseError(
541 "OpalParser::readStatement()", "MACRO call is not terminated by ';'.");
542 }
543 } else if (!token.isDel(';')) {
544 stat->append(token);
545 parseTokenList(*stat);
546 }
547 }
548 stat->start();
549 } else if (token.isDel(';')) {
550 // Skip empty statement.
551 stat = readStatement(is);
552 } else if (token.isDel('?')) {
553 // Give help.
554 *gmsg << "\ntry typing \"HELP\" for help.\n" << endl;
555 stat = readStatement(is);
556 } else if (!token.isEOF()) {
557 stat = new SimpleStatement(token.getFile(), token.getLine());
558 stat->append(token);
559 parseTokenList(*stat);
560 stat->start();
561 throw ParseError("OpalParser::readStatement()", "Command should begin with a <name>.");
562 }
563 } catch (ParseError& ex) {
564 *ippl::Error << "\n*** Parse error detected by function \""
565 << "OpalParser::readStatement()"
566 << "\"\n";
568
569 std::string what = ex.what();
570 boost::replace_all(what, "\n", "\n ");
571
572 *ippl::Error << " " << *stat << " a" << what << '\n' << endl;
573
574 stat = readStatement(is);
575 exit(1);
576 }
577
578 return stat;
579}
580
581void OpalParser::run() const {
582 stopFlag = false;
583 while (Statement* stat = readStatement(&*inputStack.back())) {
584 try {
585 // The dispatch via Statement::execute() allows a special
586 // treatment of structured statements.
587 stat->execute(*this);
588 } catch (ParseError& ex) {
589 Inform errorMsg("Error", std::cerr);
590 errorMsg << "\n*** Parse error detected by function \"" << ex.where() << "\"\n";
591 stat->printWhere(errorMsg, true);
592 std::string what = ex.what();
593 size_t pos = what.find_first_of('\n');
594 do {
595 errorMsg << " " << what.substr(0, pos) << endl;
596 what = what.substr(pos + 1, std::string::npos);
597 pos = what.find_first_of('\n');
598 } while (pos != std::string::npos);
599 errorMsg << " " << what << endl;
600
601 MPI_Abort(MPI_COMM_WORLD, -100);
602 }
603
604 delete stat;
605 if (stopFlag)
606 break;
607 }
608}
609
611 inputStack.push_back(std::shared_ptr<TokenStream>(is));
612 run();
613 inputStack.pop_back();
614}
615
616void OpalParser::stop() const {
617 stopFlag = true;
618}
619
620std::string OpalParser::getHint(const std::string& name, const std::string& type) {
621 auto owner = AttributeHandler::getOwner(name);
622 if (owner.empty()) {
623 return std::string();
624 }
625
626 std::string hint = "the " + type + " '" + name + "' could belong to\n";
627 {
628 std::string elements;
629 auto its = owner.equal_range(AttributeHandler::ELEMENT);
630 if (its.first != its.second) {
631 elements = (its.first)->second;
632 bool any = (its.first)->second == "Any";
633 for (auto it = std::next(its.first); it != its.second && !any; ++it) {
634 elements += ", " + it->second;
635 any = it->second == "Any";
636 }
637 if (any) {
638 hint += std::string(" - any element\n");
639 } else {
640 hint += std::string(" - the element")
641 + (std::distance(its.first, its.second) > 1 ? "s " : " ") + elements + "\n";
642 }
643 }
644 }
645 {
646 std::string commands;
647 auto its = owner.equal_range(AttributeHandler::COMMAND);
648 if (its.first != its.second) {
649 commands = (its.first)->second;
650 for (auto it = std::next(its.first); it != its.second; ++it) {
651 commands += ", " + it->second;
652 }
653 hint += std::string(" - the command")
654 + (std::distance(its.first, its.second) > 1 ? "s " : " ") + commands + "\n";
655 }
656 }
657 {
658 std::string sub_commands;
659 auto its = owner.equal_range(AttributeHandler::SUB_COMMAND);
660 if (its.first != its.second) {
661 sub_commands = (its.first)->second;
662 for (auto it = std::next(its.first); it != its.second; ++it) {
663 sub_commands += ", " + it->second;
664 }
665 hint += std::string(" - the sub-command")
666 + (std::distance(its.first, its.second) > 1 ? "s " : " ") + sub_commands + "\n";
667 }
668 }
669 {
670 std::string statements;
671 auto its = owner.equal_range(AttributeHandler::STATEMENT);
672 if (its.first != its.second) {
673 statements = (its.first)->second;
674 for (auto it = std::next(its.first); it != its.second; ++it) {
675 statements += ", " + it->second;
676 }
677 hint += std::string(" - the statement")
678 + (std::distance(its.first, its.second) > 1 ? "s " : " ") + statements + "\n";
679 }
680 }
681
682 hint += "but it's not present!";
683 return hint;
684}
Inform * gmsg
Definition changes.cpp:7
elements
Definition IndexMap.cpp:162
Representation objects and parsers for attribute expressions.
std::string parseString(Statement &, const char msg[])
Parse string value.
void parseDelimiter(Statement &stat, char delim)
Test for one-character delimiter.
double parseRealConst(Statement &)
Parse real constant.
bool mtrace
Trace flag.
Definition Options.cpp:31
A representation of an Object attribute.
Definition Attribute.h:52
static std::multimap< OwnerType, std::string > getOwner(const std::string &att)
The base class for all OPAL objects.
Definition Object.h:48
virtual bool shouldTrace() const =0
Trace flag.
virtual Object * makeTemplate(const std::string &, TokenStream &, Statement &)
Macro handler function.
Definition Object.cpp:83
virtual Object * clone(const std::string &name)=0
Return a clone.
virtual Object * makeInstance(const std::string &name, Statement &, const Parser *)
Macro handler function.
Definition Object.cpp:89
virtual void setShared(bool)
Set/reset shared flag.
Definition Object.cpp:276
virtual Attribute * findAttribute(const std::string &name)
Find an attribute by name.
Definition Object.cpp:64
virtual bool shouldUpdate() const =0
Update flag.
bool isTreeMember(const Object *subTree) const
Test for tree membership.
Definition Object.cpp:318
virtual void parse(Statement &)
Parse the object.
Definition Object.cpp:95
void makeDirty(Object *object)
Invalidate expressions.
Definition OpalData.cpp:571
void update()
Update all objects.
Definition OpalData.cpp:701
Object * find(const std::string &name)
Find entry.
Definition OpalData.cpp:563
static OpalData * getInstance()
Definition OpalData.cpp:195
void define(Object *newObject)
Define a new object.
Definition OpalData.cpp:486
Compound statement.
If statement.
Definition IfStatement.h:35
bool stopFlag
Definition OpalParser.h:116
virtual void parseAssign(Statement &) const
Parse assignment statement.
static Token readToken()
Return next input token.
static void parseTokenList(Statement &)
static std::vector< std::shared_ptr< TokenStream > > inputStack
Definition OpalParser.h:119
virtual void parseEnd(Statement &) const
Check for end of statement.
virtual void run() const
Read current stream.
virtual Object * find(const std::string &) const
Find object by name in the main directory.
virtual void parseMacro(const std::string &name, Statement &) const
Parse macro definition or call.
static void parseBracketList(char close, Statement &)
virtual Statement * readStatement(TokenStream *) const
Read complete statement from a token stream.
virtual void printHelp(const std::string &) const
Print help on named command.
void execute(Object *, const std::string &) const
Execute or check the current command.
static std::string getHint(const std::string &, const std::string &="attribute")
virtual void parse(Statement &) const
Parse and execute current statement.
void stop() const
Set stop flag.
virtual ~OpalParser()
virtual void parseDefine(Statement &) const
Parse definition.
virtual void parseAction(Statement &) const
Parse executable command.
A simple input statement in token form.
Interface for statements.
Definition Statement.h:38
Token & getCurrent()
Return current token and skip it.
Definition Statement.cpp:74
void append(const Token &)
Append a token.
Definition Statement.cpp:45
unsigned int position() const
Return current character number in line.
void restore()
Return to marked position.
bool keyword(const char *s)
Test for keyword.
void mark()
Mark position in command.
virtual void print(std::ostream &os) const
Print statement.
bool atEnd() const
Test for end of command.
Definition Statement.cpp:50
bool delimiter(char c)
Test for delimiter.
void start()
Return to start.
Representation of a single input token.
Definition Token.h:33
bool isString() const
Test for string.
Definition Token.cpp:132
bool isDel(char del) const
Test for delimiter.
Definition Token.cpp:92
bool isWord() const
Test for word.
Definition Token.cpp:127
bool isEOF() const
Test for end of file.
Definition Token.cpp:107
const std::string & getLex() const
Return the lexeme.
Definition Token.cpp:200
bool isKey(const char *key) const
Test for keyword.
Definition Token.cpp:137
int getLine() const
Return the token's line number.
Definition Token.cpp:215
@ IS_EOF
Definition Token.h:40
const std::string & getFile() const
Return the token's file name.
Definition Token.cpp:210
Abstract interface for a stream of input tokens.
Definition TokenStream.h:33
virtual Token readToken()=0
Read single token from stream.
While statement.
virtual const std::string & what() const
Return the message string for the exception.
virtual const std::string & where() const
Return the name of the method or function which detected the exception.
Parse exception.
Definition ParseError.h:32