ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/trunk/src/selection/SelectionCompiler.cpp
Revision: 1364
Committed: Wed Oct 7 20:49:50 2009 UTC (15 years, 6 months ago) by cli2
File size: 18307 byte(s)
Log Message:
Bug fixes in the SelectionEvaluator and SelectionCompiler
Added print option in Restraints

File Contents

# Content
1 /*
2 * Copyright (c) 2005 The University of Notre Dame. All Rights Reserved.
3 *
4 * The University of Notre Dame grants you ("Licensee") a
5 * non-exclusive, royalty free, license to use, modify and
6 * redistribute this software in source and binary code form, provided
7 * that the following conditions are met:
8 *
9 * 1. Acknowledgement of the program authors must be made in any
10 * publication of scientific results based in part on use of the
11 * program. An acceptable form of acknowledgement is citation of
12 * the article in which the program was described (Matthew
13 * A. Meineke, Charles F. Vardeman II, Teng Lin, Christopher
14 * J. Fennell and J. Daniel Gezelter, "OOPSE: An Object-Oriented
15 * Parallel Simulation Engine for Molecular Dynamics,"
16 * J. Comput. Chem. 26, pp. 252-271 (2005))
17 *
18 * 2. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 *
21 * 3. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the
24 * distribution.
25 *
26 * This software is provided "AS IS," without a warranty of any
27 * kind. All express or implied conditions, representations and
28 * warranties, including any implied warranty of merchantability,
29 * fitness for a particular purpose or non-infringement, are hereby
30 * excluded. The University of Notre Dame and its licensors shall not
31 * be liable for any damages suffered by licensee as a result of
32 * using, modifying or distributing the software or its
33 * derivatives. In no event will the University of Notre Dame or its
34 * licensors be liable for any lost revenue, profit or data, or for
35 * direct, indirect, special, consequential, incidental or punitive
36 * damages, however caused and regardless of the theory of liability,
37 * arising out of the use of or inability to use software, even if the
38 * University of Notre Dame has been advised of the possibility of
39 * such damages.
40 */
41
42 #include "selection/SelectionCompiler.hpp"
43 #include "utils/StringUtils.hpp"
44 namespace oopse {
45
46 bool SelectionCompiler::compile(const std::string& filename,
47 const std::string& script) {
48
49 this->filename = filename;
50 this->script = script;
51 lineNumbers.clear();
52 lineIndices.clear();
53 aatokenCompiled.clear();
54
55 if (internalCompile()) {
56 return true;
57 }
58
59 int icharEnd;
60 if ((icharEnd = script.find('\r', ichCurrentCommand)) == std::string::npos &&
61 (icharEnd = script.find('\n', ichCurrentCommand)) == std::string::npos) {
62 icharEnd = script.size();
63 }
64 errorLine = script.substr(ichCurrentCommand, icharEnd);
65 return false;
66 }
67
68 bool SelectionCompiler::internalCompile(){
69
70 cchScript = script.size();
71 ichToken = 0;
72 lineCurrent = 1;
73
74 error = false;
75
76 //std::vector<Token> lltoken;
77 aatokenCompiled.clear();
78 std::vector<Token> ltoken;
79
80 Token tokenCommand;
81 int tokCommand = Token::nada;
82
83 for ( ; true; ichToken += cchToken) {
84 if (lookingAtLeadingWhitespace())
85 continue;
86 //if (lookingAtComment())
87 // continue;
88 bool endOfLine = lookingAtEndOfLine();
89 if (endOfLine || lookingAtEndOfStatement()) {
90 if (tokCommand != Token::nada) {
91 if (! compileCommand(ltoken)) {
92 return false;
93 }
94 aatokenCompiled.push_back(atokenCommand);
95 lineNumbers.push_back(lineCurrent);
96 lineIndices.push_back(ichCurrentCommand);
97 ltoken.clear();
98 tokCommand = Token::nada;
99 }
100
101 if (ichToken < cchScript) {
102 if (endOfLine)
103 ++lineCurrent;
104 continue;
105 }
106 break;
107 }
108
109 if (tokCommand != Token::nada) {
110 if (lookingAtString()) {
111 std::string str = getUnescapedStringLiteral();
112 ltoken.push_back(Token(Token::string, str));
113 continue;
114 }
115 //if ((tokCommand & Token::specialstring) != 0 &&
116 // lookingAtSpecialString()) {
117 // std::string str = script.substr(ichToken, ichToken + cchToken);
118 // ltoken.push_back(Token(Token::string, str));
119 // continue;
120 //}
121 if (lookingAtDecimal((tokCommand & Token::negnums) != 0)) {
122 float value = lexi_cast<float>(script.substr(ichToken, cchToken));
123 ltoken.push_back(Token(Token::decimal, boost::any(value)));
124 continue;
125 }
126 if (lookingAtInteger((tokCommand & Token::negnums) != 0)) {
127
128 int val = lexi_cast<int>(script.substr(ichToken, cchToken));
129 ltoken.push_back(Token(Token::integer, boost::any(val)));
130 continue;
131 }
132 }
133
134 if (lookingAtLookupToken()) {
135 std::string ident = script.substr(ichToken, cchToken);
136 Token token;
137 Token* pToken = TokenMap::getInstance()->getToken(ident);
138 if (pToken != NULL) {
139 token = *pToken;
140 } else {
141 token = Token(Token::identifier, ident);
142 }
143
144 int tok = token.tok;
145
146 switch (tokCommand) {
147 case Token::nada:
148 ichCurrentCommand = ichToken;
149 //tokenCommand = token;
150 tokCommand = tok;
151 if ((tokCommand & Token::command) == 0)
152 return commandExpected();
153 break;
154
155 case Token::define:
156 if (ltoken.size() == 1) {
157 // we are looking at the variable name
158 if (tok != Token::identifier &&
159 (tok & Token::predefinedset) != Token::predefinedset)
160 return invalidExpressionToken(ident);
161 } else {
162 // we are looking at the expression
163 if (tok != Token::identifier &&
164 (tok & (Token::expression | Token::predefinedset)) == 0)
165 return invalidExpressionToken(ident);
166 }
167
168 break;
169
170 case Token::select:
171 if (tok != Token::identifier && (tok & Token::expression) == 0)
172 return invalidExpressionToken(ident);
173 break;
174 }
175 ltoken.push_back(token);
176 continue;
177 }
178
179 if (ltoken.size() == 0) {
180 return commandExpected();
181 }
182
183 return unrecognizedToken();
184 }
185
186 return true;
187 }
188
189
190 bool SelectionCompiler::lookingAtLeadingWhitespace() {
191
192 int ichT = ichToken;
193 while (ichT < cchScript && std::isspace(script[ichT])) {
194 ++ichT;
195 }
196 cchToken = ichT - ichToken;
197 return cchToken > 0;
198 }
199
200 bool SelectionCompiler::lookingAtEndOfLine() {
201 if (ichToken == cchScript)
202 return true;
203 int ichT = ichToken;
204 char ch = script[ichT];
205 if (ch == '\r') {
206 ++ichT;
207 if (ichT < cchScript && script[ichT] == '\n')
208 ++ichT;
209 } else if (ch == '\n') {
210 ++ichT;
211 } else {
212 return false;
213 }
214 cchToken = ichT - ichToken;
215 return true;
216 }
217
218 bool SelectionCompiler::lookingAtEndOfStatement() {
219 if (ichToken == cchScript || script[ichToken] != ';')
220 return false;
221 cchToken = 1;
222 return true;
223 }
224
225 bool SelectionCompiler::lookingAtString() {
226 if (ichToken == cchScript)
227 return false;
228 if (script[ichToken] != '"')
229 return false;
230 // remove support for single quote
231 // in order to use it in atom expressions
232 // char chFirst = script.charAt(ichToken);
233 // if (chFirst != '"' && chFirst != '\'')
234 // return false;
235 int ichT = ichToken + 1;
236 // while (ichT < cchScript && script.charAt(ichT++) != chFirst)
237 char ch;
238 bool previousCharBackslash = false;
239 while (ichT < cchScript) {
240 ch = script[ichT++];
241 if (ch == '"' && !previousCharBackslash)
242 break;
243 previousCharBackslash = ch == '\\' ? !previousCharBackslash : false;
244 }
245 cchToken = ichT - ichToken;
246
247 return true;
248 }
249
250
251 std::string SelectionCompiler::getUnescapedStringLiteral() {
252 /** @todo */
253 std::string sb(cchToken - 2, ' ');
254
255 int ichMax = ichToken + cchToken - 1;
256 int ich = ichToken + 1;
257
258 while (ich < ichMax) {
259 char ch = script[ich++];
260 if (ch == '\\' && ich < ichMax) {
261 ch = script[ich++];
262 switch (ch) {
263 case 'b':
264 ch = '\b';
265 break;
266 case 'n':
267 ch = '\n';
268 break;
269 case 't':
270 ch = '\t';
271 break;
272 case 'r':
273 ch = '\r';
274 // fall into
275 case '"':
276 case '\\':
277 case '\'':
278 break;
279 case 'x':
280 case 'u':
281 int digitCount = ch == 'x' ? 2 : 4;
282 if (ich < ichMax) {
283 int unicode = 0;
284 for (int k = digitCount; --k >= 0 && ich < ichMax; ) {
285 char chT = script[ich];
286 int hexit = getHexitValue(chT);
287 if (hexit < 0)
288 break;
289 unicode <<= 4;
290 unicode += hexit;
291 ++ich;
292 }
293 ch = (char)unicode;
294 }
295 }
296 }
297 sb.append(1, ch);
298 }
299
300 return sb;
301 }
302
303 int SelectionCompiler::getHexitValue(char ch) {
304 if (ch >= '0' && ch <= '9')
305 return ch - '0';
306 else if (ch >= 'a' && ch <= 'f')
307 return 10 + ch - 'a';
308 else if (ch >= 'A' && ch <= 'F')
309 return 10 + ch - 'A';
310 else
311 return -1;
312 }
313
314 bool SelectionCompiler::lookingAtSpecialString() {
315 int ichT = ichToken;
316 char ch = script[ichT];
317 while (ichT < cchScript && ch != ';' && ch != '\r' && ch != '\n') {
318 ++ichT;
319 }
320 cchToken = ichT - ichToken;
321 return cchToken > 0;
322 }
323
324 bool SelectionCompiler::lookingAtDecimal(bool allowNegative) {
325 if (ichToken == cchScript) {
326 return false;
327 }
328
329 int ichT = ichToken;
330 if (script[ichT] == '-') {
331 ++ichT;
332 }
333 bool digitSeen = false;
334 char ch = 'X';
335 while (ichT < cchScript && std::isdigit(ch = script[ichT])) {
336 ++ichT;
337 digitSeen = true;
338 }
339
340 if (ichT == cchScript || ch != '.') {
341 return false;
342 }
343
344 // to support DMPC.1, let's check the character before the dot
345 if (ch == '.' && (ichT > 0) && std::isalpha(script[ichT - 1])) {
346 return false;
347 }
348
349 ++ichT;
350 while (ichT < cchScript && std::isdigit(script[ichT])) {
351 ++ichT;
352 digitSeen = true;
353 }
354 cchToken = ichT - ichToken;
355 return digitSeen;
356 }
357
358 bool SelectionCompiler::lookingAtInteger(bool allowNegative) {
359 if (ichToken == cchScript) {
360 return false;
361 }
362 int ichT = ichToken;
363 if (allowNegative && script[ichToken] == '-') {
364 ++ichT;
365 }
366 int ichBeginDigits = ichT;
367 while (ichT < cchScript && std::isdigit(script[ichT])) {
368 ++ichT;
369 }
370 if (ichBeginDigits == ichT) {
371 return false;
372 }
373 cchToken = ichT - ichToken;
374 return true;
375 }
376
377 bool SelectionCompiler::lookingAtLookupToken() {
378 if (ichToken == cchScript) {
379 return false;
380 }
381
382 int ichT = ichToken;
383 char ch;
384 switch (ch = script[ichT++]) {
385 case '(':
386 case ')':
387 case ',':
388 case '[':
389 case ']':
390 break;
391 case '&':
392 case '|':
393 if (ichT < cchScript && script[ichT] == ch) {
394 ++ichT;
395 }
396 break;
397 case '<':
398 case '=':
399 case '>':
400 if (ichT < cchScript && ((ch = script[ichT]) == '<' || ch == '=' || ch == '>')) {
401 ++ichT;
402 }
403 break;
404 case '/':
405 case '!':
406 if (ichT < cchScript && script[ichT] == '=') {
407 ++ichT;
408 }
409 break;
410 default:
411 if ((ch < 'a' || ch > 'z') && (ch < 'A' && ch > 'Z') && ch != '_') {
412 return false;
413 }
414 case '*':
415 case '?': // include question marks in identifier for atom expressions
416 while (ichT < cchScript && !std::isspace(ch = script[ichT]) &&
417 (std::isalpha(ch) ||std::isdigit(ch) || ch == '_' || ch == '.' || ch == '*' || ch == '?' || ch == '+' || ch == '-' || ch == '[' || ch == ']') ){
418
419 ++ichT;
420 }
421 break;
422 }
423
424 cchToken = ichT - ichToken;
425
426 return true;
427 }
428
429 bool SelectionCompiler::compileCommand(const std::vector<Token>& ltoken) {
430 const Token& tokenCommand = ltoken[0];
431 int tokCommand = tokenCommand.tok;
432
433 atokenCommand = ltoken;
434 if ((tokCommand & Token::expressionCommand) != 0 && !compileExpression()) {
435 return false;
436 }
437
438 return true;
439 }
440
441 bool SelectionCompiler::compileExpression() {
442 /** todo */
443 int i = 1;
444 int tokCommand = atokenCommand[0].tok;
445 if (tokCommand == Token::define) {
446 i = 2;
447 } else if ((tokCommand & Token::embeddedExpression) != 0) {
448 // look for the open parenthesis
449 while (i < atokenCommand.size() &&
450 atokenCommand[i].tok != Token::leftparen)
451 ++i;
452 }
453
454 if (i >= atokenCommand.size()) {
455 return true;
456 }
457 return compileExpression(i);
458 }
459
460
461 bool SelectionCompiler::addTokenToPostfix(const Token& token) {
462 ltokenPostfix.push_back(token);
463 return true;
464 }
465
466 bool SelectionCompiler::compileExpression(int itoken) {
467 ltokenPostfix.clear();
468 for (int i = 0; i < itoken; ++i) {
469 addTokenToPostfix(atokenCommand[i]);
470 }
471
472 atokenInfix = atokenCommand;
473 itokenInfix = itoken;
474
475 addTokenToPostfix(Token::tokenExpressionBegin);
476 if (!clauseOr()) {
477 return false;
478 }
479
480 addTokenToPostfix(Token::tokenExpressionEnd);
481 if (itokenInfix != atokenInfix.size()) {
482 return endOfExpressionExpected();
483 }
484
485 atokenCommand = ltokenPostfix;
486 return true;
487 }
488
489 Token SelectionCompiler::tokenNext() {
490 if (itokenInfix == atokenInfix.size()) {
491 return Token();
492 }
493 return atokenInfix[itokenInfix++];
494 }
495
496 boost::any SelectionCompiler::valuePeek() {
497 if (itokenInfix == atokenInfix.size()) {
498 return boost::any();
499 } else {
500 return atokenInfix[itokenInfix].value;
501 }
502 }
503
504 int SelectionCompiler::tokPeek() {
505 if (itokenInfix == atokenInfix.size()) {
506 return 0;
507 }else {
508 return atokenInfix[itokenInfix].tok;
509 }
510 }
511
512 bool SelectionCompiler::clauseOr() {
513 if (!clauseAnd()) {
514 return false;
515 }
516
517 while (tokPeek() == Token::opOr) {
518 Token tokenOr = tokenNext();
519 if (!clauseAnd()) {
520 return false;
521 }
522 addTokenToPostfix(tokenOr);
523 }
524 return true;
525 }
526
527 bool SelectionCompiler::clauseAnd() {
528 if (!clauseNot()) {
529 return false;
530 }
531
532 while (tokPeek() == Token::opAnd) {
533 Token tokenAnd = tokenNext();
534 if (!clauseNot()) {
535 return false;
536 }
537 addTokenToPostfix(tokenAnd);
538 }
539 return true;
540 }
541
542 bool SelectionCompiler::clauseNot() {
543 if (tokPeek() == Token::opNot) {
544 Token tokenNot = tokenNext();
545 if (!clauseNot()) {
546 return false;
547 }
548 return addTokenToPostfix(tokenNot);
549 }
550 return clausePrimitive();
551 }
552
553 bool SelectionCompiler::clausePrimitive() {
554 int tok = tokPeek();
555 switch (tok) {
556 case Token::within:
557 return clauseWithin();
558
559 case Token::asterisk:
560 case Token::identifier:
561 return clauseChemObjName();
562
563 case Token::integer :
564 return clauseIndex();
565 default:
566 if ((tok & Token::atomproperty) == Token::atomproperty) {
567 return clauseComparator();
568 }
569 if ((tok & Token::predefinedset) != Token::predefinedset) {
570 break;
571 }
572 // fall into the code and below and just add the token
573 case Token::all:
574 case Token::none:
575 return addTokenToPostfix(tokenNext());
576 case Token::leftparen:
577 tokenNext();
578 if (!clauseOr()) {
579 return false;
580 }
581 if (tokenNext().tok != Token::rightparen) {
582 return rightParenthesisExpected();
583 }
584 return true;
585 }
586 return unrecognizedExpressionToken();
587 }
588
589 bool SelectionCompiler::clauseComparator() {
590 Token tokenAtomProperty = tokenNext();
591 Token tokenComparator = tokenNext();
592 if ((tokenComparator.tok & Token::comparator) == 0) {
593 return comparisonOperatorExpected();
594 }
595
596 Token tokenValue = tokenNext();
597 if (tokenValue.tok != Token::integer && tokenValue.tok != Token::decimal) {
598 return numberExpected();
599 }
600
601 float val;
602 if (tokenValue.value.type() == typeid(int)) {
603 val = boost::any_cast<int>(tokenValue.value);
604 } else if (tokenValue.value.type() == typeid(float)) {
605 val = boost::any_cast<float>(tokenValue.value);
606 } else {
607 return false;
608 }
609
610 boost::any floatVal;
611 floatVal = val;
612 return addTokenToPostfix(Token(tokenComparator.tok,
613 tokenAtomProperty.tok, floatVal));
614 }
615
616 bool SelectionCompiler::clauseWithin() {
617 tokenNext(); // WITHIN
618 if (tokenNext().tok != Token::leftparen) { // (
619 return leftParenthesisExpected();
620 }
621
622 boost::any distance;
623 Token tokenDistance = tokenNext(); // distance
624 switch(tokenDistance.tok) {
625 case Token::integer:
626 case Token::decimal:
627 distance = tokenDistance.value;
628 break;
629 default:
630 return numberOrKeywordExpected();
631 }
632
633 if (tokenNext().tok != Token::opOr) { // ,
634 return commaExpected();
635 }
636
637 if (! clauseOr()) { // *expression*
638 return false;
639 }
640
641 if (tokenNext().tok != Token::rightparen) { // )T
642 return rightParenthesisExpected();
643 }
644
645 return addTokenToPostfix(Token(Token::within, distance));
646 }
647
648 bool SelectionCompiler::clauseChemObjName() {
649 Token token = tokenNext();
650 if (token.tok == Token::identifier && token.value.type() == typeid(std::string)) {
651
652 std::string name = boost::any_cast<std::string>(token.value);
653 if (isNameValid(name)) {
654 return addTokenToPostfix(Token(Token::name, name));
655 } else {
656 return compileError("invalid name: " + name);
657 }
658 }
659
660 return false;
661
662 }
663
664 bool SelectionCompiler::isNameValid(const std::string& name) {
665 int nbracket = 0;
666 int ndot = 0;
667 for (int i =0 ; i < name.size(); ++i) {
668 switch(name[i]) {
669
670 case '[' :
671 ++nbracket;
672 break;
673 case ']' :
674 --nbracket;
675 break;
676 case '.' :
677 ++ndot;
678 break;
679 }
680 }
681
682 //only allow 3 dots at most
683 return (ndot <=3 && nbracket == 0) ? true : false;
684 }
685
686 bool SelectionCompiler::clauseIndex(){
687 Token token = tokenNext();
688 if (token.tok == Token::integer) {
689 int index = boost::any_cast<int>(token.value);
690 int tok = tokPeek();
691 std::cout << "Token::to is " << Token::to << ", tok = " << tok << std::endl;
692 if (tok == Token::to) {
693 tokenNext();
694 tok = tokPeek();
695 if (tok != Token::integer) {
696 return numberExpected();
697 }
698
699 boost::any intVal = tokenNext().value;
700 int first = index;
701 if (intVal.type() != typeid(int)){
702 return false;
703 }
704 int second = boost::any_cast<int>(intVal);
705
706 return addTokenToPostfix(Token(Token::index, boost::any(std::make_pair(first, second))));
707
708 }else {
709 return addTokenToPostfix(Token(Token::index, boost::any(index)));
710 }
711 } else {
712 return numberExpected();
713 }
714 }
715
716 }

Properties

Name Value
svn:executable *