ERMParser.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. #include "ERMParser.h"
  2. #include <boost/spirit/include/qi.hpp>
  3. #include <boost/bind.hpp>
  4. #include <boost/spirit/include/phoenix_core.hpp>
  5. #include <boost/spirit/include/phoenix_operator.hpp>
  6. #include <boost/spirit/include/phoenix_fusion.hpp>
  7. #include <boost/spirit/include/phoenix_stl.hpp>
  8. #include <boost/spirit/include/phoenix_object.hpp>
  9. #include <boost/fusion/include/adapt_struct.hpp>
  10. #include <fstream>
  11. namespace spirit = boost::spirit;
  12. namespace qi = boost::spirit::qi;
  13. namespace ascii = spirit::ascii;
  14. namespace phoenix = boost::phoenix;
  15. //Greenspun's Tenth Rule of Programming:
  16. //Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified,
  17. //bug-ridden, slow implementation of half of Common Lisp.
  18. //actually these macros help in dealing with boost::variant
  19. #define BEGIN_TYPE_CASE(UN) struct UN : boost::static_visitor<> \
  20. {
  21. #define FOR_TYPE(TYPE, VAR) void operator()(TYPE const& VAR) const
  22. #define DO_TYPE_CASE(UN, VAR) } ___UN; boost::apply_visitor(___UN, VAR);
  23. ERMParser::ERMParser(std::string file)
  24. :srcFile(file)
  25. {}
  26. void ERMParser::parseFile()
  27. {
  28. std::ifstream file(srcFile.c_str());
  29. if(!file.is_open())
  30. {
  31. tlog1 << "File " << srcFile << " not found or unable to open\n";
  32. return;
  33. }
  34. //check header
  35. char header[5];
  36. file.getline(header, ARRAY_COUNT(header));
  37. if(std::string(header) != "ZVSE")
  38. {
  39. tlog1 << "File " << srcFile << " has wrong header\n";
  40. return;
  41. }
  42. //parse file
  43. char lineBuf[1024];
  44. parsedLine = 1;
  45. std::string wholeLine; //used for buffering multiline lines
  46. bool inString = false;
  47. while(file.good())
  48. {
  49. //reading line
  50. file.getline(lineBuf, ARRAY_COUNT(lineBuf));
  51. if(file.gcount() == ARRAY_COUNT(lineBuf))
  52. {
  53. tlog1 << "Encountered a problem during parsing " << srcFile << " too long line (" << parsedLine << ")\n";
  54. }
  55. switch(classifyLine(lineBuf, inString))
  56. {
  57. case ERMParser::COMMAND_FULL:
  58. case ERMParser::COMMENT:
  59. {
  60. parseLine(lineBuf);
  61. }
  62. break;
  63. case ERMParser::UNFINISHED_STRING:
  64. {
  65. if(!inString)
  66. wholeLine = "";
  67. inString = true;
  68. wholeLine += lineBuf;
  69. }
  70. break;
  71. case ERMParser::END_OF_STRING:
  72. {
  73. inString = false;
  74. wholeLine += lineBuf;
  75. parseLine(wholeLine);
  76. }
  77. break;
  78. }
  79. //loop end
  80. ++parsedLine;
  81. }
  82. }
  83. void callme(char const& i)
  84. {
  85. std::cout << "fd";
  86. }
  87. namespace ERM
  88. {
  89. typedef std::string TStringConstant;
  90. typedef std::string TMacroUsage;
  91. typedef std::string TMacroDef;
  92. typedef std::string TCmdName;
  93. struct TVarExpNotMacro
  94. {
  95. typedef boost::optional<int> valT;
  96. std::string varsym;
  97. valT val;
  98. };
  99. typedef boost::variant<TVarExpNotMacro, TMacroUsage> TVarExp;
  100. //write-only variable expression
  101. typedef TVarExp TVarpExp;
  102. //i-expression (identifier expression) - an integral constant, variable symbol or array symbol
  103. typedef boost::variant<TVarExp, int> iexpT;
  104. struct TArithmeticOp
  105. {
  106. iexpT lhs, rhs;
  107. char opcode;
  108. };
  109. struct TVRLogic
  110. {
  111. char opcode;
  112. iexpT var;
  113. };
  114. struct TVRArithmetic
  115. {
  116. char opcode;
  117. iexpT rhs;
  118. };
  119. struct TSemiCompare
  120. {
  121. std::string compSign;
  122. iexpT rhs;
  123. };
  124. struct TCurriedString
  125. {
  126. iexpT iexp;
  127. TStringConstant string;
  128. };
  129. struct TVarConcatString
  130. {
  131. TVarExp var;
  132. TStringConstant string;
  133. };
  134. typedef boost::variant<TVarConcatString, TStringConstant, TCurriedString, TSemiCompare, TMacroUsage, TMacroDef, iexpT, TVarpExp, qi::unused_type> TBodyOptionItem;
  135. typedef std::vector<TBodyOptionItem> TNormalBodyOptionList;
  136. struct TNormalBodyOption
  137. {
  138. char optionCode;
  139. TNormalBodyOptionList params;
  140. };
  141. typedef boost::variant<TVRLogic, TVRArithmetic, TNormalBodyOption> TBodyOption;
  142. typedef boost::variant<iexpT, TArithmeticOp > TIdentifierInternal;
  143. typedef std::vector< TIdentifierInternal > identifierT;
  144. struct TComparison
  145. {
  146. std::string compSign;
  147. iexpT lhs, rhs;
  148. };
  149. struct conditionT;
  150. typedef
  151. boost::optional<
  152. boost::recursive_wrapper<conditionT>
  153. >
  154. conditionNodeT;
  155. struct conditionT
  156. {
  157. typedef boost::variant<
  158. TComparison,
  159. int>
  160. Tcond; //comparison or condition flag
  161. char ctype;
  162. Tcond cond;
  163. conditionNodeT rhs;
  164. };
  165. struct triggerT
  166. {
  167. TCmdName name;
  168. boost::optional<identifierT> identifier;
  169. boost::optional<conditionT> condition;
  170. };
  171. //a dirty workaround for preprocessor magic that prevents the use types with comma in it in BOOST_FUSION_ADAPT_STRUCT
  172. //see http://comments.gmane.org/gmane.comp.lib.boost.user/62501 for some info
  173. //
  174. //moreover, I encountered a quite serious bug in boost: http://boost.2283326.n4.nabble.com/container-hpp-111-error-C2039-value-type-is-not-a-member-of-td3352328.html
  175. //not sure how serious it is...
  176. //typedef boost::variant<char, TStringConstant, TMacroUsage, TMacroDef> bodyItem;
  177. typedef std::vector<TBodyOption> bodyTbody;
  178. struct instructionT
  179. {
  180. TCmdName name;
  181. boost::optional<identifierT> identifier;
  182. boost::optional<conditionT> condition;
  183. bodyTbody body;
  184. };
  185. struct receiverT
  186. {
  187. TCmdName name;
  188. boost::optional<identifierT> identifier;
  189. boost::optional<conditionT> condition;
  190. bodyTbody body;
  191. };
  192. struct postOBtriggerT
  193. {
  194. boost::optional<identifierT> identifier;
  195. boost::optional<conditionT> condition;
  196. };
  197. typedef boost::variant<
  198. triggerT,
  199. instructionT,
  200. receiverT,
  201. postOBtriggerT
  202. >
  203. commandTcmd;
  204. struct commandT
  205. {
  206. commandTcmd cmd;
  207. std::string comment;
  208. };
  209. typedef boost::variant<commandT, std::string, qi::unused_type> lineT;
  210. //console printer
  211. struct VarPrinter : boost::static_visitor<>
  212. {
  213. void operator()(TVarExpNotMacro const& val) const
  214. {
  215. tlog2 << val.varsym;
  216. if(val.val.is_initialized())
  217. {
  218. tlog2 << val.val.get();
  219. }
  220. }
  221. void operator()(TMacroUsage const& val) const
  222. {
  223. tlog2 << "$" << val << "&";
  224. }
  225. };
  226. void varPrinter(const TVarExp & var)
  227. {
  228. boost::apply_visitor(VarPrinter(), var);
  229. }
  230. struct _IEP : boost::static_visitor<>
  231. {
  232. void operator()(int const & constant) const
  233. {
  234. tlog2 << constant;
  235. }
  236. void operator()(TVarExp const & var) const
  237. {
  238. varPrinter(var);
  239. }
  240. };
  241. void iexpPrinter(const iexpT & exp)
  242. {
  243. boost::apply_visitor(_IEP(), exp);
  244. }
  245. struct IdentifierVisitor : boost::static_visitor<>
  246. {
  247. void operator()(iexpT const& iexp) const
  248. {
  249. iexpPrinter(iexp);
  250. }
  251. void operator()(TArithmeticOp const& arop) const
  252. {
  253. iexpPrinter(arop.lhs);
  254. tlog2 << " " << arop.opcode << " ";
  255. iexpPrinter(arop.rhs);
  256. }
  257. };
  258. void identifierPrinter(const boost::optional<identifierT> & id)
  259. {
  260. if(id.is_initialized())
  261. {
  262. tlog2 << "identifier: ";
  263. BOOST_FOREACH(TIdentifierInternal x, id.get())
  264. {
  265. tlog2 << "\\";
  266. boost::apply_visitor(IdentifierVisitor(), x);
  267. }
  268. }
  269. }
  270. struct ConditionCondPrinter : boost::static_visitor<>
  271. {
  272. void operator()(TComparison const& cmp) const
  273. {
  274. iexpPrinter(cmp.lhs);
  275. tlog2 << " " << cmp.compSign << " ";
  276. iexpPrinter(cmp.rhs);
  277. }
  278. void operator()(int const& flag) const
  279. {
  280. tlog2 << "condflag " << flag;
  281. }
  282. };
  283. void conditionPrinter(const boost::optional<conditionT> & cond)
  284. {
  285. if(cond.is_initialized())
  286. {
  287. conditionT condp = cond.get();
  288. tlog2 << " condition: ";
  289. boost::apply_visitor(ConditionCondPrinter(), condp.cond);
  290. tlog2 << " cond type: " << condp.ctype << " rhs:";
  291. //recursive call
  292. if(condp.rhs.is_initialized())
  293. {
  294. boost::optional<conditionT> rhsc = condp.rhs.get().get();
  295. conditionPrinter(rhsc);
  296. }
  297. }
  298. }
  299. struct UN2 : boost::static_visitor<>
  300. {
  301. void operator()(triggerT const& trig) const
  302. {
  303. tlog2 << "trigger: " << trig.name;
  304. identifierPrinter(trig.identifier);
  305. conditionPrinter(trig.condition);
  306. }
  307. void operator()(instructionT const& trig) const
  308. {
  309. tlog2 << "instruction: " << trig.name;
  310. identifierPrinter(trig.identifier);
  311. conditionPrinter(trig.condition);
  312. tlog2 << " body items: ";
  313. // BOOST_FOREACH(bodyItem bi, trig.body)
  314. // {
  315. // tlog2 << " " << bi;
  316. // }
  317. }
  318. void operator()(receiverT const& trig) const
  319. {
  320. tlog2 << "receiver: " << trig.name;
  321. identifierPrinter(trig.identifier);
  322. conditionPrinter(trig.condition);
  323. }
  324. void operator()(postOBtriggerT const& trig) const
  325. {
  326. tlog2 << "post OB trigger; ";
  327. identifierPrinter(trig.identifier);
  328. conditionPrinter(trig.condition);
  329. }
  330. };
  331. struct UN : boost::static_visitor<>
  332. {
  333. void operator()(commandT const& cmd) const
  334. {
  335. UN2 un;
  336. boost::apply_visitor(un, cmd.cmd);
  337. std::cout << "Line comment: " << cmd.comment << std::endl;
  338. }
  339. void operator()(std::string const& comment) const
  340. {
  341. }
  342. void operator()(qi::unused_type const& nothing) const
  343. {
  344. }
  345. };
  346. void printLineAST(const lineT & ast)
  347. {
  348. tlog2 << "";
  349. UN zm = UN();
  350. boost::apply_visitor(zm, ast);
  351. }
  352. }
  353. BOOST_FUSION_ADAPT_STRUCT(
  354. ERM::TVarExpNotMacro,
  355. (std::string, varsym)
  356. (ERM::TVarExpNotMacro::valT, val)
  357. )
  358. BOOST_FUSION_ADAPT_STRUCT(
  359. ERM::TArithmeticOp,
  360. (ERM::iexpT, lhs)
  361. (char, opcode)
  362. (ERM::iexpT, rhs)
  363. )
  364. BOOST_FUSION_ADAPT_STRUCT(
  365. ERM::TVRLogic,
  366. (char, opcode)
  367. (ERM::iexpT, var)
  368. )
  369. BOOST_FUSION_ADAPT_STRUCT(
  370. ERM::TVRArithmetic,
  371. (char, opcode)
  372. (ERM::iexpT, rhs)
  373. )
  374. BOOST_FUSION_ADAPT_STRUCT(
  375. ERM::TNormalBodyOption,
  376. (char, optionCode)
  377. (ERM::TNormalBodyOptionList, params)
  378. )
  379. BOOST_FUSION_ADAPT_STRUCT(
  380. ERM::triggerT,
  381. (ERM::TCmdName, name)
  382. (boost::optional<ERM::identifierT>, identifier)
  383. (boost::optional<ERM::conditionT>, condition)
  384. )
  385. BOOST_FUSION_ADAPT_STRUCT(
  386. ERM::TComparison,
  387. (ERM::iexpT, lhs)
  388. (std::string, compSign)
  389. (ERM::iexpT, rhs)
  390. )
  391. BOOST_FUSION_ADAPT_STRUCT(
  392. ERM::TSemiCompare,
  393. (std::string, compSign)
  394. (ERM::iexpT, rhs)
  395. )
  396. BOOST_FUSION_ADAPT_STRUCT(
  397. ERM::TCurriedString,
  398. (ERM::iexpT, iexp)
  399. (ERM::TStringConstant, string)
  400. )
  401. BOOST_FUSION_ADAPT_STRUCT(
  402. ERM::TVarConcatString,
  403. (ERM::TVarExp, var)
  404. (ERM::TStringConstant, string)
  405. )
  406. BOOST_FUSION_ADAPT_STRUCT(
  407. ERM::conditionT,
  408. (char, ctype)
  409. (ERM::conditionT::Tcond, cond)
  410. (ERM::conditionNodeT, rhs)
  411. )
  412. BOOST_FUSION_ADAPT_STRUCT(
  413. ERM::instructionT,
  414. (ERM::TCmdName, name)
  415. (boost::optional<ERM::identifierT>, identifier)
  416. (boost::optional<ERM::conditionT>, condition)
  417. (ERM::bodyTbody, body)
  418. )
  419. BOOST_FUSION_ADAPT_STRUCT(
  420. ERM::receiverT,
  421. (ERM::TCmdName, name)
  422. (boost::optional<ERM::identifierT>, identifier)
  423. (boost::optional<ERM::conditionT>, condition)
  424. (ERM::bodyTbody, body)
  425. )
  426. BOOST_FUSION_ADAPT_STRUCT(
  427. ERM::postOBtriggerT,
  428. (boost::optional<ERM::identifierT>, identifier)
  429. (boost::optional<ERM::conditionT>, condition)
  430. )
  431. BOOST_FUSION_ADAPT_STRUCT(
  432. ERM::commandT,
  433. (ERM::commandTcmd, cmd)
  434. (std::string, comment)
  435. )
  436. namespace ERM
  437. {
  438. template<typename Iterator>
  439. struct ERM_grammar : qi::grammar<Iterator, lineT(), ascii::space_type>
  440. {
  441. ERM_grammar() : ERM_grammar::base_type(rline, "ERM script line")
  442. {
  443. //do not build too complicated expressions, e.g. (a >> b) | c, qi has problems with them
  444. macroUsage %= qi::lexeme[qi::lit('$') >> *(qi::char_ - '$') >> qi::lit('$')];
  445. macroDef %= qi::lexeme[qi::lit('@') >> *(qi::char_ - '@') >> qi::lit('@')];
  446. varExpNotMacro %= (+(qi::char_("?a-z") - 'u')) >> -qi::int_;
  447. varExp %= varExpNotMacro | macroUsage;
  448. iexp %= varExp | qi::int_;
  449. varp %= qi::char_("?") > varExp;
  450. comment %= *(qi::char_);
  451. commentLine %= (~qi::char_('!') >> comment | (qi::char_('!') >> (~qi::char_("?!#")) >> comment ));
  452. cmdName %= qi::lexeme[qi::repeat(2)[qi::char_]];
  453. arithmeticOp %= iexp >> qi::char_ >> iexp;
  454. //identifier is usually a vector of i-expressions but VR receiver performs arithmetic operations on it
  455. identifier %= (iexp | arithmeticOp) % qi::lit('/');
  456. comparison %= iexp >> (*qi::char_("<=>")) >> iexp;
  457. condition %= qi::char_("&|X/") >> (comparison | qi::int_) >> -condition;
  458. trigger %= cmdName >> -identifier >> -condition > qi::lit(";"); /////
  459. string %= qi::lexeme['^' >> *(qi::char_ - '^') >> '^'];
  460. VRLogic %= qi::char_("&|X") >> iexp;
  461. VRarithmetic %= qi::char_("+*:/%-") >> iexp;
  462. semiCompare %= *qi::char_("<=>") >> iexp;
  463. curStr %= iexp >> string;
  464. varConcatString %= varExp >> qi::lit("+") >> string;
  465. bodyOptionItem %= varConcatString | curStr | string | semiCompare | macroUsage | macroDef | iexp | varp | qi::eps;
  466. exactBodyOptionList %= (bodyOptionItem % qi::lit("/"));
  467. normalBodyOption = qi::char_("A-Z") > exactBodyOptionList;
  468. bodyOption %= VRLogic | VRarithmetic | normalBodyOption;
  469. body %= qi::lit(":") >> +(bodyOption) > qi::lit(";");
  470. instruction %= cmdName >> -identifier >> -condition >> body;
  471. receiver %= cmdName >> -identifier >> -condition >> body; //receiver without body exists... change needed
  472. postOBtrigger %= qi::lit("$OB") >> -identifier >> -condition > qi::lit(";");
  473. command %= (qi::lit("!") >>
  474. (
  475. (qi::lit("?") >> trigger) |
  476. (qi::lit("!") >> receiver) |
  477. (qi::lit("#") >> instruction) |
  478. postOBtrigger
  479. ) >> comment
  480. );
  481. rline %=
  482. (
  483. command | commentLine | spirit::eps
  484. ) > spirit::eoi;
  485. //error handling
  486. string.name("string constant");
  487. iexp.name("i-expression");
  488. comment.name("comment");
  489. commentLine.name("comment line");
  490. cmdName.name("name of a command");
  491. identifier.name("identifier");
  492. condition.name("condition");
  493. trigger.name("trigger");
  494. body.name("body");
  495. instruction.name("instruction");
  496. receiver.name("receiver");
  497. postOBtrigger.name("post OB trigger");
  498. command.name("command");
  499. rline.name("script line");
  500. qi::on_error<qi::fail>
  501. (
  502. rline
  503. , std::cout //or phoenix::ref(std::count), is there any difference?
  504. << phoenix::val("Error! Expecting ")
  505. << qi::_4 // what failed?
  506. << phoenix::val(" here: \"")
  507. << phoenix::construct<std::string>(qi::_3, qi::_2) // iterators to error-pos, end
  508. << phoenix::val("\"")
  509. << std::endl
  510. );
  511. }
  512. qi::rule<Iterator, TStringConstant(), ascii::space_type> string;
  513. qi::rule<Iterator, TMacroUsage(), ascii::space_type> macroUsage;
  514. qi::rule<Iterator, TMacroDef(), ascii::space_type> macroDef;
  515. qi::rule<Iterator, TVarExpNotMacro(), ascii::space_type> varExpNotMacro;
  516. qi::rule<Iterator, TVarExp(), ascii::space_type> varExp;
  517. qi::rule<Iterator, iexpT(), ascii::space_type> iexp;
  518. qi::rule<Iterator, TVarpExp(), ascii::space_type> varp;
  519. qi::rule<Iterator, TArithmeticOp(), ascii::space_type> arithmeticOp;
  520. qi::rule<Iterator, std::string(), ascii::space_type> comment;
  521. qi::rule<Iterator, std::string(), ascii::space_type> commentLine;
  522. qi::rule<Iterator, TCmdName(), ascii::space_type> cmdName;
  523. qi::rule<Iterator, identifierT(), ascii::space_type> identifier;
  524. qi::rule<Iterator, TComparison(), ascii::space_type> comparison;
  525. qi::rule<Iterator, conditionT(), ascii::space_type> condition;
  526. qi::rule<Iterator, TVRLogic(), ascii::space_type> VRLogic;
  527. qi::rule<Iterator, TVRArithmetic(), ascii::space_type> VRarithmetic;
  528. qi::rule<Iterator, TSemiCompare(), ascii::space_type> semiCompare;
  529. qi::rule<Iterator, TCurriedString(), ascii::space_type> curStr;
  530. qi::rule<Iterator, TVarConcatString(), ascii::space_type> varConcatString;
  531. qi::rule<Iterator, TBodyOptionItem(), ascii::space_type> bodyOptionItem;
  532. qi::rule<Iterator, TNormalBodyOptionList(), ascii::space_type> exactBodyOptionList;
  533. qi::rule<Iterator, TNormalBodyOption(), ascii::space_type> normalBodyOption;
  534. qi::rule<Iterator, TBodyOption(), ascii::space_type> bodyOption;
  535. qi::rule<Iterator, triggerT(), ascii::space_type> trigger;
  536. qi::rule<Iterator, bodyTbody(), ascii::space_type> body;
  537. qi::rule<Iterator, instructionT(), ascii::space_type> instruction;
  538. qi::rule<Iterator, receiverT(), ascii::space_type> receiver;
  539. qi::rule<Iterator, postOBtriggerT(), ascii::space_type> postOBtrigger;
  540. qi::rule<Iterator, commandT(), ascii::space_type> command;
  541. qi::rule<Iterator, lineT(), ascii::space_type> rline;
  542. };
  543. };
  544. void ERMParser::parseLine( const std::string & line )
  545. {
  546. std::string::const_iterator beg = line.begin(),
  547. end = line.end();
  548. ERM::ERM_grammar<std::string::const_iterator> ERMgrammar;
  549. ERM::lineT AST;
  550. // bool r = qi::phrase_parse(beg, end, ERMgrammar, ascii::space, AST);
  551. // if(!r || beg != end)
  552. // {
  553. // tlog1 << "Parse error for line (" << parsedLine << ") : " << line << std::endl;
  554. // tlog1 << "\tCannot parse: " << std::string(beg, end) << std::endl;
  555. // }
  556. // else
  557. // {
  558. // //parsing succeeded
  559. // //ERM::printLineAST(AST);
  560. // }
  561. }
  562. ERMParser::ELineType ERMParser::classifyLine( const std::string & line, bool inString ) const
  563. {
  564. ERMParser::ELineType ret;
  565. if(line[0] == '!')
  566. {
  567. if(countHatsBeforeSemicolon(line) % 2 == 1)
  568. {
  569. ret = ERMParser::UNFINISHED_STRING;
  570. }
  571. else
  572. {
  573. ret = ERMParser::COMMAND_FULL;
  574. }
  575. }
  576. else
  577. {
  578. if(inString)
  579. {
  580. if(countHatsBeforeSemicolon(line) % 2 == 1)
  581. {
  582. ret = ERMParser::END_OF_STRING;
  583. }
  584. else
  585. {
  586. ret = ERMParser::UNFINISHED_STRING;
  587. }
  588. }
  589. else
  590. {
  591. ret = ERMParser::COMMENT;
  592. }
  593. }
  594. return ret;
  595. }
  596. int ERMParser::countHatsBeforeSemicolon( const std::string & line ) const
  597. {
  598. //CHECK: omit macros? or anything else?
  599. int numOfHats = 0; //num of '^' before ';'
  600. //check for unmatched ^
  601. BOOST_FOREACH(char c, line)
  602. {
  603. if(c == ';')
  604. break;
  605. if(c == '^')
  606. ++numOfHats;
  607. }
  608. return numOfHats;
  609. }