1 /* $Author: hansonr $
2  * $Date: 2009-06-08 18:20:22 -0500 (Mon, 08 Jun 2009) $
3  * $Revision: 10975 $
4  *
5  * Copyright (C) 2002-2005  The Jmol Development Team
6  *
7  * Contact: jmol-developers@lists.sf.net
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 package org.jmol.script;
25 
26 import javajs.util.Lst;
27 import javajs.util.PT;
28 
29 import java.util.Map;
30 
31 import org.jmol.util.Edge;
32 import org.jmol.util.Logger;
33 import org.jmol.util.SimpleUnitCell;
34 
35 import javajs.util.P3;
36 
37 import org.jmol.viewer.Viewer;
38 import org.jmol.i18n.GT;
39 
40 
41 /**
42  * An abstract class subclassed by ScriptCompiler taking care of the second
43  * phase of syntax checking. After all the tokens are created, these methods
44  * ensure that they are in the proper order in terms of expressions, primarily.
45  *
46  * Here we are going from an "infix" to a "postfix" set of tokens and then back
47  * to infix for final storage.
48  *
49  */
50 
51 
52 abstract class ScriptTokenParser {
53 
54   protected Viewer vwr;
55 
56   protected Map<String, Boolean> htUserFunctions;
57 
58   protected String script;
59   protected boolean isStateScript;
60 
61   protected short lineCurrent;
62   protected int iCommand;
63 
64   protected int ichCurrentCommand, ichComment, ichEnd;
65   protected int ichToken;
66 
67   protected T theToken;
68   protected T lastFlowCommand;
69   protected T tokenCommand;
70   protected T lastToken;
71   protected T tokenAndEquals;
72 
73   protected int theTok;
74   protected int nTokens;
75   protected int tokCommand;
76 
77   protected int ptNewSetModifier;
78   protected boolean isNewSet;
79   protected boolean haveMacro;
80 
81 
82 //----------------------------------------------------------------------------------------
83 //----------------------------------------------------------------------------------------
84 //---------------PHASE II -- TOKEN-BASED COMPILING ---------------------------------------
85 //----------------------------------------------------------------------------------------
86 //----------------------------------------------------------------------------------------
87 
88   protected boolean logMessages = true;
89 
90   protected T[] atokenInfix;
91   protected int itokenInfix;
92 
93   protected boolean isSetBrace;
94   protected boolean isMathExpressionCommand;
95   protected boolean isSetOrDefine;
96 
97   private Lst<T> ltokenPostfix;
98 
99   protected boolean isEmbeddedExpression;
100   protected boolean isCommaAsOrAllowed;
101 
102   private Object theValue;
103 
compileExpressions()104   protected boolean compileExpressions() {
105 
106     boolean isScriptExpression = ((tokCommand == T.script || tokCommand == T.macro)
107         && tokAt(2) == T.leftparen);
108     isEmbeddedExpression = (isScriptExpression
109         || (tokCommand != T.nada
110              && (tokCommand != T.function
111                 && tokCommand != T.parallel
112                 && tokCommand != T.trycmd
113                 && tokCommand != T.catchcmd
114              || tokenCommand.intValue != Integer.MAX_VALUE)
115                 && tokCommand != T.end
116                 && !T.tokAttr(tokCommand, T.atomExpressionCommand)
117                 && (nTokens > 2 || !T.tokAttr(tokCommand, T.implicitStringCommand)
118                 )
119            )
120       );
121     isMathExpressionCommand = (tokCommand == T.identifier
122         || isScriptExpression
123         || T.tokAttr(tokCommand, T.mathExpressionCommand));
124 
125     boolean checkExpression = isEmbeddedExpression
126         || (T.tokAttr(tokCommand, T.atomExpressionCommand));
127 
128     // $ at beginning disallow expression checking for center, delete, hide, or
129     // display commands
130     if (tokAt(1) == T.dollarsign
131         && T.tokAttr(tokCommand, T.atomExpressionCommand))
132       checkExpression = false;
133     if (checkExpression && !compileExpression())
134       return false;
135 
136     // check statement length
137 
138     int size = atokenInfix.length;
139 
140     int nDefined = 0;
141     for (int i = 1; i < size; i++) {
142       if (tokAt(i) == T.define)
143         nDefined++;
144     }
145 
146     size -= nDefined;
147     if (isNewSet) {
148       if (size == 1) {
149         atokenInfix[0] = T.tv(T.function, 0, atokenInfix[0].value);
150         isNewSet = false;
151       }
152     }
153 
154     // must avoid roll-over to negative number in JavaScript
155     if ((isNewSet || isSetBrace) && ptNewSetModifier != Integer.MAX_VALUE && size < ptNewSetModifier + 2) {
156       if (!isNewSet || !haveMacro)
157         return commandExpected();
158       htUserFunctions.put((String) atokenInfix[0].value, Boolean.TRUE);
159     }
160     return(size == 1 || !T.tokAttr(tokCommand, T.noArgs) ? true
161             : error(ERROR_badArgumentCount));
162   }
163 
164 
compileExpression()165   protected boolean compileExpression() {
166     int firstToken = (isSetOrDefine && !isSetBrace ? 2 : 1);
167     ltokenPostfix = new  Lst<T>();
168     itokenInfix = 0;
169     T tokenBegin = null;
170     int tok = tokAt(1);
171     switch (tokCommand) {
172     case T.define:
173       int i = (tokAt(1) == T.define ? 2 : 1);
174       if (tokAt(i) == T.integer && tokAt(i + 1) == T.per && tokAt(i + 3) == T.opEQ) {
175         // @2.xxx =
176         // @@2.xxx =
177         tokCommand = T.set;
178         isSetBrace = true;
179         ptNewSetModifier = i + 3;
180         isMathExpressionCommand = true;
181         isEmbeddedExpression = true;
182         addTokenToPostfixToken(T.tokenSetProperty);
183         addTokenToPostfixToken(T.tokenExpressionBegin);
184         for (int j = 0; j++ <= i;)
185           addNextToken();
186         addTokenToPostfixToken(T.tokenExpressionEnd);
187         firstToken = 0;
188       }
189       break;
190     case T.restrict:
191       if (tok == T.bonds)
192         firstToken = 2;
193       break;
194     case T.select:
195       switch(tok) {
196       case T.on:
197       case T.off:
198         tok = tokAt(++firstToken);
199         break;
200       }
201       //$FALL-THROUGH$
202     case T.hide:
203     case T.display:
204       switch(tok) {
205       case T.add:
206       case T.remove:
207         tok = tokAt(++firstToken);
208         break;
209       }
210       if (tok == T.group  && !T.tokAttr(tokAt(firstToken + 1), T.mathop))
211         firstToken++;
212     }
213     for (int i = 0; i < firstToken && addNextToken(); i++) {
214     }
215     while (moreTokens()) {
216       if (isEmbeddedExpression) {
217         while (!isExpressionNext()) {
218           if (tokPeekIs(T.identifier) && !(tokCommand == T.load && itokenInfix == 1)) {
219             String name = (String) atokenInfix[itokenInfix].value;
220             T t = T.getTokenFromName(name);
221             if (t != null)
222               if (!isMathExpressionCommand && lastToken.tok != T.define
223                   || (lastToken.tok == T.per || tokAt(itokenInfix + 1) == T.leftparen)
224                         && !isUserFunction(name)) {
225                 // Checking here for known token masquerading as identifier due to VAR definition.
226                 // We reset it to its original mapping if it's a known token and:
227                 //    a) this isn't a math expression command, and not preceded by @, or
228                 //    b) it is preceded by "." or followed by "("
229                 //             and it isn't the name of a user function
230 
231                 atokenInfix[itokenInfix] = t;
232               }
233           }
234           if (!addNextToken())
235             break;
236         }
237         if (!moreTokens())
238           break;
239       }
240       if (lastToken.tok == T.define) {
241         if (!clauseDefine(true, false))
242           return false;
243         continue;
244       }
245       if (!isMathExpressionCommand)
246         addTokenToPostfixToken(tokenBegin = T.o(T.expressionBegin, "implicitExpressionBegin"));
247       if (!clauseOr(isCommaAsOrAllowed || !isMathExpressionCommand
248           && tokPeekIs(T.leftparen)))
249         return false;
250       if (!isMathExpressionCommand
251           && !(isEmbeddedExpression && lastToken == T.tokenCoordinateEnd)) {
252         addTokenToPostfixToken(T.tokenExpressionEnd);
253       }
254       if (moreTokens()) {
255         if (tokCommand != T.select && tokCommand != T.delete && !isEmbeddedExpression)
256           return error(ERROR_endOfExpressionExpected);
257         if (tokCommand == T.select) {
258           // advanced select, with two expressions, the first
259           // being an atom expression; the second being a property selector expression
260           tokenBegin.intValue = 0;
261           tokCommand = T.nada;
262           isEmbeddedExpression = true;
263           isMathExpressionCommand = true;
264           isCommaAsOrAllowed = false;
265         }
266       }
267     }
268     atokenInfix = ltokenPostfix.toArray(new T[ltokenPostfix.size()]);
269     return true;
270   }
271 
isUserFunction(String name)272   protected boolean isUserFunction(String name) {
273     name = name.toLowerCase();
274     return (!isStateScript && (vwr.isFunction(name) || htUserFunctions.containsKey(name)));
275   }
276 
isExpressionNext()277   private boolean isExpressionNext() {
278     return tokPeekIs(T.leftbrace)
279     && !(tokAt(itokenInfix + 1) == T.string
280          && tokAt(itokenInfix + 2) == T.colon)
281     || !isMathExpressionCommand && tokPeekIs(T.leftparen);
282   }
283 
tokenAttr(T token, int tok)284   protected static boolean tokenAttr(T token, int tok) {
285     return token != null && T.tokAttr(token.tok, tok);
286   }
287 
moreTokens()288   private boolean moreTokens() {
289     return (itokenInfix < atokenInfix.length);
290   }
291 
tokAt(int i)292   protected int tokAt(int i) {
293     return (i < atokenInfix.length ? atokenInfix[i].tok : T.nada);
294   }
295 
tokPeek()296   private int tokPeek() {
297     return (itokenInfix >= atokenInfix.length ? T.nada
298         : atokenInfix[itokenInfix].tok);
299   }
300 
tokPeekIs(int tok)301   private boolean tokPeekIs(int tok) {
302     return (tokAt(itokenInfix) == tok);
303   }
304 
intPeek()305   private int intPeek() {
306     return (itokenInfix >= atokenInfix.length ? Integer.MAX_VALUE
307         : atokenInfix[itokenInfix].intValue);
308   }
309 
valuePeek()310   private Object valuePeek() {
311     return (moreTokens() ? atokenInfix[itokenInfix].value : "");
312   }
313 
314   /**
315    * increments the pointer; does NOT set theToken or theValue
316    * @return the next token
317    */
tokenNext()318   private T tokenNext() {
319     return (itokenInfix >= atokenInfix.length ? null
320         : atokenInfix[itokenInfix++]);
321   }
322 
tokenNextTok(int tok)323   private boolean tokenNextTok(int tok) {
324     T token = tokenNext();
325     return (token != null && token.tok == tok);
326   }
327 
returnToken()328   private boolean returnToken() {
329     itokenInfix--;
330     return false;
331   }
332 
333   /**
334    * gets the next token and sets global theToken and theValue
335    * @return the next token
336    */
getToken()337   private T getToken() {
338     theValue = ((theToken = tokenNext()) == null ? null : theToken.value);
339     return theToken;
340   }
341 
getNumericalToken()342   private boolean getNumericalToken() {
343     return (getToken() != null
344         && (theToken.tok == T.integer || theToken.tok == T.decimal));
345   }
346 
floatValue()347   private float floatValue() {
348     switch (theToken.tok) {
349     case T.integer:
350       return theToken.intValue;
351     case T.decimal:
352       return ((Float) theValue).floatValue();
353     }
354     return 0;
355   }
356 
addTokenToPostfix(int tok, Object value)357   private boolean addTokenToPostfix(int tok, Object value) {
358     return addTokenToPostfixToken(T.o(tok, value));
359   }
360 
addTokenToPostfixInt(int tok, int intValue, Object value)361   private boolean addTokenToPostfixInt(int tok, int intValue, Object value) {
362     return addTokenToPostfixToken(T.tv(tok, intValue, value));
363   }
364 
addTokenToPostfixToken(T token)365   private boolean addTokenToPostfixToken(T token) {
366     if (token == null)
367       return false;
368     if (logMessages)
369         Logger.debug("addTokenToPostfix" + token);
370     if (token.tok == T.leftsquare && (lastToken.tok == T.per || lastToken.tok == T.perper)) {
371       // new notation
372       int ipt = ltokenPostfix.size() - 1;
373       ltokenPostfix.removeItemAt(ipt);
374       ltokenPostfix.addLast(T.tokenRightParen);
375       int pcount = 0;
376       int tok;
377       for (int i = ltokenPostfix.size(); --i >= 0 && pcount >= 0;) {
378         switch  (tok = ltokenPostfix.get(i).tok) {
379         case T.rightparen:
380         case T.rightsquare:
381           pcount++;
382           break;
383         case T.leftparen:
384         case T.leftsquare:
385           pcount--;
386           int tok2;
387           if (pcount == 1 && (tok2 = ltokenPostfix.get(i - 1).tok) != T.rightparen && tok2 != T.rightsquare) {
388             ipt = (tok == T.leftsquare ? i - 1 : i);
389             pcount = -10;
390           }
391           break;
392         default:
393           tok2 = (i == 0 ? T.nada : ltokenPostfix.get(i - 1).tok);
394           if (tok2 == T.per || tok2 == T.perper) {
395             ipt = i - 1;
396             break;
397           }
398           if (i == ipt - 1) {
399             ipt = i;
400             pcount = -10;
401           }
402           break;
403         }
404       }
405       if (pcount == -10) {
406         ltokenPostfix.add(ipt, T.tokenLeftParen);
407       }
408     }
409     ltokenPostfix.addLast(token);
410     lastToken = token;
411     return true;
412   }
413 
addNextToken()414   private boolean addNextToken() {
415     return addTokenToPostfixToken(tokenNext());
416   }
417 
addNextTokenIf(int tok)418   private boolean addNextTokenIf(int tok) {
419     return (tokPeekIs(tok) && addNextToken());
420   }
421 
addSubstituteTokenIf(int tok, T token)422   private boolean addSubstituteTokenIf(int tok, T token) {
423     if (!tokPeekIs(tok))
424       return false;
425     itokenInfix++;
426     return addTokenToPostfixToken(token);
427   }
428 
429   boolean haveString;
430 
clauseOr(boolean allowCommaAsOr)431   private boolean clauseOr(boolean allowCommaAsOr) {
432     haveString = false;
433     if (!clauseAnd())
434       return false;
435     if (isEmbeddedExpression && lastToken.tok == T.expressionEnd)
436       return true;
437 
438     //for simplicity, giving XOR (toggle) same precedence as OR
439     //OrNot: First OR, but if that makes no change, then NOT (special toggle)
440     int tok;
441     while ((tok = tokPeek())== T.opOr || tok == T.opXor
442         || tok==T.opToggle|| allowCommaAsOr && tok == T.comma) {
443       if (tok == T.comma && !haveString)
444         addSubstituteTokenIf(T.comma, T.tokenOr);
445       else
446         addNextToken();
447       if (!clauseAnd())
448         return false;
449       if (allowCommaAsOr && (lastToken.tok == T.rightbrace || lastToken.tok == T.bitset))
450         haveString = true;
451     }
452     return true;
453   }
454 
clauseAnd()455   private boolean clauseAnd() {
456     if (!clauseNot())
457       return false;
458     if (isEmbeddedExpression && lastToken.tok == T.expressionEnd)
459       return true;
460     while (tokPeekIs(T.opAnd)) {
461       addNextToken();
462       if (!clauseNot())
463         return false;
464     }
465     return true;
466   }
467 
468   // for RPN processor, not reversed
clauseNot()469   private boolean clauseNot() {
470     if (tokPeekIs(T.opNot)) {
471       addNextToken();
472       return clauseNot();
473     }
474     return (clausePrimitive());
475   }
476 
clausePrimitive()477   private boolean clausePrimitive() {
478     int tok = tokPeek();
479     switch (tok) {
480     case T.spacebeforesquare:
481       itokenInfix++;
482       return clausePrimitive();
483     case T.nada:
484       return error(ERROR_endOfCommandUnexpected);
485     case T.all:
486     case T.bitset:
487     case T.divide:
488     case T.helix:
489     case T.helix310:
490     case T.helixalpha:
491     case T.helixpi:
492     case T.isaromatic:
493     case T.none:
494     case T.sheet:
495       // nothing special
496       return addNextToken();
497     case T.string:
498       haveString = true;
499       return addNextToken();
500     case T.decimal:
501       // create a file_model integer as part of the token
502       return addTokenToPostfixInt(T.spec_model2, fixModelSpec(getToken()), theValue);
503     case T.cell:
504     case T.centroid:
505       return clauseCell(tok);
506     case T.connected:
507     case T.polyhedra:
508       return clauseConnected(tok == T.polyhedra);
509     case T.search:
510     case T.smiles:
511       return clauseSubstructure();
512     case T.within:
513     case T.contact:
514       return clauseWithin(tok == T.within);
515     case T.define:
516       return clauseDefine(false, false);
517     case T.bonds:
518     case T.measure:
519       addNextToken();
520       if (tokPeekIs(T.bitset))
521         addNextToken();
522       else if (tokPeekIs(T.define))
523         return clauseDefine(false, false);
524       return true;
525     case T.leftparen:
526       addNextToken();
527       if (!clauseOr(true))
528         return false;
529       if (!addNextTokenIf(T.rightparen))
530         return errorStr(ERROR_tokenExpected, ")");
531       return checkForItemSelector(true);
532     case T.leftbrace:
533       return checkForCoordinate(isMathExpressionCommand);
534     default:
535       // may be a residue specification
536       if (clauseResidueSpec())
537         return true;
538       if (isError())
539         return false;
540       if (T.tokAttr(tok, T.atomproperty)) {
541         int itemp = itokenInfix;
542         boolean isOK = clauseComparator(true);
543         if (isOK || itokenInfix != itemp)
544           return isOK;
545         if (tok == T.substructure) {
546           return clauseSubstructure();
547         }
548 
549       }
550       //if (tok != Token.integer && !Token.tokAttr(tok, Token.predefinedset))
551         //break;
552       return addNextToken();
553 
554     }
555 //    return error(ERROR_unrecognizedExpressionToken, "" + valuePeek());
556   }
557 
checkForCoordinate(boolean isImplicitExpression)558   private boolean checkForCoordinate(boolean isImplicitExpression) {
559     /*
560      * A bit tricky here: we have three contexts for braces --
561      *
562      * 1) expressionCommands SELECT, RESTRICT, DEFINE,
563      *    DISPLAY, HIDE, CENTER, and SUBSET
564      *
565      * 2) embeddedExpression commands such as DRAW and ISOSURFACE
566      *
567      * 3) IF and SET
568      *
569      * Then, within these, we have the possibility that we are
570      * looking at a coordinate {0 0 0} (with or without commas, and
571      * possibly fractional, {1/2 1/2 1}, and possibly a plane Point4f
572      * definition, {a b c d}) or an expression.
573      *
574      * We assume an expression initially and then adjust accordingly
575      * if it turns out this is a coordinate.
576      *
577      * Note that due to the nuances of how expressions such as (1-4) are
578      * reported as special codes, Eval must still interpret these
579      * carefully. This could be corrected for here, I think.
580      *
581      */
582     boolean isCoordinate = false;
583     int pt = ltokenPostfix.size();
584     if (isImplicitExpression) {
585       addTokenToPostfixToken(T.tokenExpressionBegin);
586       tokenNext();
587     } else if (isEmbeddedExpression) {
588       tokenNext();
589       pt--;
590     } else {
591       addNextToken();
592     }
593     boolean isHash = tokPeekIs(T.string);
594     if (isHash) {
595       isImplicitExpression = false;
596       returnToken();
597       ltokenPostfix.removeItemAt(ltokenPostfix.size() - 1);
598       addNextToken();
599       int nBrace = 1;
600       while (nBrace != 0) {
601         if (tokPeekIs(T.leftbrace)) {
602           if (isExpressionNext()) {
603             addTokenToPostfixToken(T.o(T.expressionBegin,
604                 "implicitExpressionBegin"));
605             if (!clauseOr(false)) // changed to FALSE 10/20 because  @({"center":{0.0 0.0 0.0}, "xxx"...}} failed
606               return false;
607             if (lastToken != T.tokenCoordinateEnd) {
608               addTokenToPostfixToken(T.tokenExpressionEnd);
609             }
610           } else {
611             nBrace++;
612           }
613         }
614         if (tokPeekIs(T.rightbrace))
615           nBrace--;
616         addNextToken();
617       }
618     } else {
619       if (!tokPeekIs(T.rightbrace) && !clauseOr(false))
620         return false;
621       int n = 1;
622       while (!tokPeekIs(T.rightbrace)) {
623         boolean haveComma = addNextTokenIf(T.comma);
624         if (!clauseOr(false))
625           return (haveComma || n < 3 ? false : errorStr(ERROR_tokenExpected, "}"));
626         n++;
627       }
628       isCoordinate = (n >= 2); // could be {1 -2 3}
629     }
630     if (isCoordinate && (isImplicitExpression || isEmbeddedExpression)) {
631       ltokenPostfix.set(pt, T.tokenCoordinateBegin);
632       addTokenToPostfixToken(T.tokenCoordinateEnd);
633       tokenNext();
634     } else if (isImplicitExpression) {
635       addTokenToPostfixToken(T.tokenExpressionEnd);
636       tokenNext();
637     } else if (isEmbeddedExpression) {
638       if (!isHash)
639         tokenNext();
640     } else {
641       addNextToken();
642     }
643     return checkForItemSelector(!isHash);
644   }
645 
checkForItemSelector(boolean allowNumeric)646   private boolean checkForItemSelector(boolean allowNumeric) {
647     // {x[1]}  @{x}[1][3]  (atomno=3)[2][5]
648     int tok;
649     if ((tok = tokAt(itokenInfix + 1)) == T.leftsquare
650         || allowNumeric && tok == T.leftbrace)
651       return true; // [[, as in a matrix or [{ ... not totally acceptable!
652 
653     // the real problem is that after an expression you can have
654     while (true) {//for (int i = 0; i < (allowNumeric ? 2 : 1); i++) {
655       if (!addNextTokenIf(T.leftsquare))
656         break;
657       if (!clauseItemSelector())
658         return false;
659       if (!addNextTokenIf(T.rightsquare))
660         return errorStr(ERROR_tokenExpected, "]");
661     }
662     return true;
663   }
664 
clauseWithin(boolean isWithin)665   private boolean clauseWithin(boolean isWithin) {
666 
667     //    // contact(distance, {}, {}) // default 100 for distance
668     // within ( plane, planeExpression)
669     // within ( hkl, hklExpression)
670     // within ( distance, plane, planeExpression)
671     // within ( distance, hkl, hklExpression)
672     // within ( distance, coord, point)
673     // within ( distance, point)
674     // within ( distance, $surfaceId)
675     // within ( distance, orClause)
676     // within ( group|branch|etc, ....)
677     // within ( distance, group, ....)
678     // within ( annotation, "xxxx")
679 
680     addNextToken();
681     if (!addNextTokenIf(T.leftparen))
682       return false;
683     if (getToken() == null)
684       return false;
685     float distance = Float.MAX_VALUE;
686     String key = null;
687     boolean allowComma = isWithin;
688     int tok;
689     int tok0 = theToken.tok;
690     if (!isWithin) {
691       tok = -1;
692       for (int i = itokenInfix; tok != T.nada; i++) {
693         switch (tok = tokAt(i)) {
694         case T.comma:
695           tok = T.nada;
696           break;
697         case T.leftbrace:
698         case T.leftparen:
699         case T.rightparen:
700           distance = 100;
701           returnToken();
702           tok0 = tok = T.nada;
703           break;
704         }
705       }
706     }
707     switch (tok0) {
708     case T.minus:
709       if (getToken() == null)
710         return false;
711       if (theToken.tok != T.integer)
712         return error(ERROR_numberExpected);
713       distance = -theToken.intValue;
714       break;
715     case T.integer:
716     case T.decimal:
717       distance = floatValue();
718       break;
719     case T.define:
720       addTokenToPostfixToken(theToken);
721       if (!clauseDefine(true, false))
722         return false;
723       key = "";
724       allowComma = false;
725       break;
726     }
727     if (isWithin && distance == Float.MAX_VALUE)
728       switch (tok0) {
729       case T.define:
730         break;
731       case T.centroid:
732       case T.cell:
733         addTokenToPostfix(T.string, theValue);
734         clauseCell(T.point3f);
735         key = "";
736         break;
737       case T.dssr:
738       case T.rna3d:
739       case T.search:
740       case T.smiles:
741       case T.substructure:
742       case T.domains:
743       case T.validation:
744         addTokenToPostfix(T.string, theValue);
745         if (!addNextTokenIf(T.comma))
746           return false;
747         allowComma = false;
748         tok = tokPeek();
749         switch (tok) {
750         case T.nada:
751           return false;
752         case T.string:
753           addNextToken();
754           key = "";
755           break;
756         case T.define:
757           if (!clauseDefine(false, true))
758             return false;
759           key = "";
760           break;
761         default:
762           return false;
763         }
764         break;
765       case T.branch:
766         allowComma = false;
767         //$FALL-THROUGH$
768       case T.atomtype:
769       case T.atomname:
770       case T.basepair:
771       case T.boundbox:
772       case T.chain:
773       case T.coord:
774       case T.element:
775       case T.group:
776       case T.unitcell:
777       case T.helix:
778       case T.model:
779       case T.molecule:
780       case T.plane:
781       case T.hkl:
782       case T.polymer:
783       case T.sequence:
784       case T.sheet:
785       case T.site:
786       case T.structure:
787       case T.string:
788       case T.vanderwaals:
789         key = (String) theValue;
790         break;
791       default:
792         key = ((String) theValue).toLowerCase();
793         break;
794       }
795     if (key == null)
796       addTokenToPostfix(T.decimal, Float.valueOf(distance));
797     else if (key.length() > 0)
798       addTokenToPostfix(T.string, key);
799     boolean done = false;
800     while (!done) {
801       if (tok0 != T.nada && !addNextTokenIf(T.comma))
802         break;
803       if (tok0 == T.nada)
804         tok0 = T.contact;
805       boolean isCoordOrPlane = false;
806       tok = tokPeek();
807       if (isWithin) {
808         switch (tok0) {
809         case T.integer:
810         case T.decimal:
811           if (tok == T.on || tok == T.off) {
812             addTokenToPostfixToken(getToken());
813             if (!addNextTokenIf(T.comma))
814               break;
815             tok = tokPeek();
816           }
817           break;
818         }
819         if (key == null) {
820           switch (tok) {
821           case T.hkl:
822           case T.coord:
823           case T.plane:
824             isCoordOrPlane = true;
825             addNextToken();
826             break;
827           case T.dollarsign:
828             getToken();
829             getToken();
830             addTokenToPostfix(T.string, "$" + theValue);
831             done = true;
832             break;
833           case T.group:
834           case T.vanderwaals:
835           case T.unitcell:
836             getToken();
837             addTokenToPostfix(T.string, T.nameOf(tok));
838             break;
839           case T.leftbrace:
840             returnToken();
841             isCoordOrPlane = true;
842             addTokenToPostfixToken(T
843                 .getTokenFromName(distance == Float.MAX_VALUE ? "plane"
844                     : "coord"));
845           }
846         if (!done)
847           addNextTokenIf(T.comma);
848         }
849       }
850       tok = tokPeek();
851       if (done)
852         break;
853       if (isCoordOrPlane) {
854         while (!tokPeekIs(T.rightparen)) {
855           switch (tokPeek()) {
856           case T.nada:
857             return error(ERROR_endOfCommandUnexpected);
858           case T.leftparen:
859             addTokenToPostfixToken(T.tokenExpressionBegin);
860             addNextToken();
861             if (!clauseOr(false))
862               return errorIntStr2(ERROR_unrecognizedParameter, "WITHIN", ": ?");
863             if (!addNextTokenIf(T.rightparen))
864               return errorStr(ERROR_tokenExpected, ", / )");
865             addTokenToPostfixToken(T.tokenExpressionEnd);
866             break;
867           case T.define:
868             if (!clauseDefine(false, false))
869               return false;
870             break;
871           default:
872             addTokenToPostfixToken(getToken());
873           }
874         }
875       } else if (!clauseOr(allowComma)) {// *expression*        return error(ERROR_badArgumentCount);
876       }
877     }
878     if (!addNextTokenIf(T.rightparen))
879       return errorStr(ERROR_tokenExpected, ")");
880     return true;
881   }
882 
clauseConnected(boolean isPolyhedra)883   private boolean clauseConnected(boolean isPolyhedra) {
884     addNextToken();
885     // connected (1,3, single, .....)
886     if (!addNextTokenIf(T.leftparen)) {
887       addTokenToPostfixToken(T.tokenLeftParen);
888       addTokenToPostfixToken(T.tokenRightParen);
889       return true;
890     }
891     while (true) {
892       if (addNextTokenIf(T.integer)) {
893         if (!addNextTokenIf(T.comma)) {
894           break;
895         }
896         if (isPolyhedra) {
897           returnToken();
898           break;
899         }
900       } else if (isPolyhedra &&
901           (addNextTokenIf(T.string) || addNextTokenIf(T.identifier))) {
902         break;
903       }
904 
905       if (addNextTokenIf(T.integer))
906         if (!addNextTokenIf(T.comma))
907           break;
908       if (addNextTokenIf(T.decimal))
909         if (!addNextTokenIf(T.comma))
910           break;
911       if (addNextTokenIf(T.decimal))
912         if (!addNextTokenIf(T.comma))
913           break;
914       Object o = getToken().value;
915       String strOrder = (o instanceof String ? (String) o : " ");
916       int intType = ScriptParam.getBondOrderFromString(strOrder);
917       if (intType == Edge.BOND_ORDER_NULL) {
918         returnToken();
919       } else {
920         addTokenToPostfix(T.string, strOrder);
921         if (!addNextTokenIf(T.comma))
922           break;
923       }
924       if (addNextTokenIf(T.rightparen))
925         return true;
926       if (!clauseOr(tokPeekIs(T.leftparen))) // *expression*
927         return false;
928       if (addNextTokenIf(T.rightparen))
929         return true;
930       if (!addNextTokenIf(T.comma))
931         return false;
932       if (!clauseOr(tokPeekIs(T.leftparen))) // *expression*
933         return false;
934 
935       break;
936     }
937     if (!addNextTokenIf(T.rightparen))
938       return errorStr(ERROR_tokenExpected, ")");
939     return true;
940   }
941 
clauseSubstructure()942   private boolean clauseSubstructure() {
943     addNextToken();
944     if (!addNextTokenIf(T.leftparen))
945       return false;
946     if (tokPeekIs(T.define)) {
947       if (!clauseDefine(false, true))
948         return false;
949     } else if (!addNextTokenIf(T.string)) {
950       return errorStr(ERROR_tokenExpected, "\"...\"");
951     }
952     if (addNextTokenIf(T.comma))
953       if (!clauseOr(tokPeekIs(T.leftparen))) // *expression*
954         return false;
955     if (!addNextTokenIf(T.rightparen))
956       return errorStr(ERROR_tokenExpected, ")");
957     return true;
958   }
959 
clauseItemSelector()960   private boolean clauseItemSelector() {
961     int tok;
962     int nparen = 0;
963     while ((tok = tokPeek()) != T.nada && tok != T.rightsquare) {
964       addNextToken();
965       if (tok == T.leftsquare)
966         nparen++;
967       if (tokPeek() == T.rightsquare && nparen-- > 0)
968         addNextToken();
969     }
970     return true;
971   }
972 
clauseComparator(boolean isOptional)973   private boolean clauseComparator(boolean isOptional) {
974     T tokenAtomProperty = tokenNext();
975     T tokenComparator = tokenNext();
976     if (!tokenAttr(tokenComparator, T.comparator)) {
977       if (!isOptional)
978         return errorStr(ERROR_tokenExpected, "== != < > <= >=");
979       if (tokenComparator != null)
980         returnToken();
981       returnToken();
982       return false;
983     }
984     if (tokenAttr(tokenAtomProperty, T.strproperty)
985         && tokenComparator.tok != T.opEQ
986         && tokenComparator.tok != T.opLIKE
987         && tokenComparator.tok != T.opNE)
988       return errorStr(ERROR_tokenExpected, "== !=");
989     if (tokPeek() == T.leftsquare) {
990       getToken();
991       addTokenToPostfixToken(T.tokenLeftParen);
992       while (true) {
993         if (!addCompare(tokenAtomProperty, tokenComparator))
994           return false;
995         if (tokPeek() == T.comma)
996           getToken();
997         else if (tokPeek() == T.rightsquare)
998           break;
999         addTokenToPostfixToken(tokenComparator.tok == T.opNE ? T.tokenAnd : T.tokenOr);
1000       }
1001       getToken();
1002       addTokenToPostfixToken(T.tokenRightParen);
1003       return true;
1004     }
1005     return addCompare(tokenAtomProperty, tokenComparator);
1006   }
1007 
addCompare(T tokenAtomProperty, T tokenComparator)1008   private boolean addCompare(T tokenAtomProperty, T tokenComparator) {
1009     if (getToken() == null)
1010       return errorStr(ERROR_unrecognizedExpressionToken, "" + valuePeek());
1011     boolean isNegative = (theToken.tok == T.minus);
1012     if (isNegative && getToken() == null)
1013       return error(ERROR_numberExpected);
1014     switch (theToken.tok) {
1015     case T.integer:
1016     case T.decimal:
1017     case T.identifier:
1018     case T.string:
1019     case T.leftbrace:
1020     case T.define:
1021       break;
1022     default:
1023       if (!T.tokAttr(theToken.tok, T.misc))
1024         return error(ERROR_numberOrVariableNameExpected);
1025     }
1026     addTokenToPostfixInt(tokenComparator.tok, tokenAtomProperty.tok,
1027         tokenComparator.value + (isNegative ? " -" : ""));
1028     if (tokenAtomProperty.tok == T.property)
1029       addTokenToPostfixToken(tokenAtomProperty);
1030     if (theToken.tok == T.leftbrace) {
1031       returnToken();
1032       return clausePrimitive();
1033     }
1034     addTokenToPostfixToken(theToken);
1035     if (theToken.tok == T.define)
1036       return clauseDefine(true, false);
1037     return true;
1038   }
1039 
clauseCell(int tok)1040   private boolean clauseCell(int tok) {
1041     P3 cell = new P3();
1042     tokenNext(); // CELL
1043     if (tok != T.point3f) {
1044       if (!tokenNextTok(T.opEQ)) // =
1045         return errorStr(ERROR_tokenExpected, "=");
1046     }
1047     if (getToken() == null)
1048       return error(ERROR_coordinateExpected);
1049     // 555 = {1 1 1}
1050     //Token coord = tokenNext(); // 555 == {1 1 1}
1051     if (theToken.tok == T.integer) {
1052       SimpleUnitCell.ijkToPoint3f(theToken.intValue, cell, 1, 0);
1053     } else {
1054       if (theToken.tok != T.leftbrace || !getNumericalToken())
1055         return error(ERROR_coordinateExpected); // i
1056       cell.x = floatValue();
1057       if (tokPeekIs(T.comma)) // ,
1058         tokenNext();
1059       if (!getNumericalToken())
1060         return error(ERROR_coordinateExpected); // j
1061       cell.y = floatValue();
1062       if (tokPeekIs(T.comma)) // ,
1063         tokenNext();
1064       if (!getNumericalToken() || !tokenNextTok(T.rightbrace))
1065         return error(ERROR_coordinateExpected); // k
1066       cell.z = floatValue();
1067     }
1068     return addTokenToPostfix(tok, cell);
1069   }
1070 
clauseDefine(boolean haveToken, boolean forceString)1071   private boolean clauseDefine(boolean haveToken, boolean forceString) {
1072     if (!haveToken) {
1073       T token = tokenNext();
1074       if (forceString) // we know it is @, this forces string type
1075         token = T.tokenDefineString;
1076       addTokenToPostfixToken(token);
1077     }
1078     if (tokPeekIs(T.nada))
1079       return error(ERROR_endOfCommandUnexpected);
1080     // we allow @x[1], which compiles as {@x}[1], not @{x[1]}
1081     // otherwise [1] gets read as a general atom name selector
1082     if (!addSubstituteTokenIf(T.leftbrace, T.tokenExpressionBegin)) {
1083       if (tokPeek() == T.define)
1084         addNextToken();
1085       return addNextToken() && checkForItemSelector(true);
1086     }
1087     while (moreTokens() && !tokPeekIs(T.rightbrace)) {
1088       if (tokPeekIs(T.leftbrace)) {
1089         if (!checkForCoordinate(true))
1090           return false;
1091       } else {
1092         addNextToken();
1093       }
1094     }
1095     return addSubstituteTokenIf(T.rightbrace, T.tokenExpressionEnd)
1096         && checkForItemSelector(true);
1097   }
1098 
1099   private boolean residueSpecCodeGenerated;
1100 
generateResidueSpecCode(T token)1101   private boolean generateResidueSpecCode(T token) {
1102     if (residueSpecCodeGenerated)
1103       addTokenToPostfixToken(T.tokenAndSpec);
1104     addTokenToPostfixToken(token);
1105     residueSpecCodeGenerated = true;
1106     return true;
1107   }
1108 
clauseResidueSpec()1109   private boolean clauseResidueSpec() {
1110     int tok = tokPeek();
1111     residueSpecCodeGenerated = false;
1112     boolean checkResNameSpec = false;
1113     switch (tok) {
1114     case T.nada:
1115       // terminal
1116     case T.dna:
1117     case T.rna:
1118       return false;
1119     case T.integer:
1120       // select 33
1121       // select 33-35
1122     case T.colon:
1123       // select :a
1124     case T.percent:
1125       // select %1
1126     case T.inscode:
1127       // sequence code precompiled
1128       break;
1129     case T.times:
1130       // all
1131     case T.leftsquare:
1132       // [ala]
1133     case T.identifier:
1134       // ala
1135       checkResNameSpec = true;
1136       break;
1137     default:
1138       if (T.tokAttr(tok, T.comparator))
1139         return false;
1140       String str = "" + valuePeek();
1141       checkResNameSpec = (str.length() == 2 || str.length() == 3);
1142       // note: there are many groups that could
1143       // in principle be here, for example:
1144       // "AND" "SET" "TO*"
1145       // these need to have attribute expression to be here
1146       // but then there are FX FY FZ UX UY UZ ..
1147       if (!checkResNameSpec)
1148         return false;
1149     }
1150     boolean specSeen = false;
1151     if (checkResNameSpec) {
1152       if (!clauseResNameSpec())
1153         return false;
1154       specSeen = true;
1155       tok = tokPeek();
1156     }
1157     if (tok == T.integer || tok == T.times || tok == T.inscode) {
1158       // [ala]33
1159       // ala33
1160       // [ala]*
1161       // [ala]<precompiled seqcode
1162       if (!clauseSequenceSpec())
1163         return false;
1164       specSeen = true;
1165       tok = tokPeek();
1166     }
1167     if (tok == T.colon
1168 // BH 1/19/15 reduced allowance here -- just too much possible
1169 // that is undocumented, especially with multiple-character chains
1170 //        || tok == T.times
1171 //        || theToken.tok ==  T.rightsquare &&
1172 //            (tok == T.identifier
1173 //              || tok == T.x || tok == T.y || tok == T.z || tok == T.w)
1174 //              || tok == T.integer && !wasInteger
1175               ) {
1176       if (!clauseChainSpec(tok))
1177         return false;
1178       specSeen = true;
1179       tok = tokPeek();
1180     }
1181     if (tok == T.per) {
1182       if (!clauseAtomSpec())
1183         return false;
1184       specSeen = true;
1185       tok = tokPeek();
1186     }
1187     if (tok == T.percent) {
1188       if (!clauseAlternateSpec())
1189         return false;
1190       specSeen = true;
1191       tok = tokPeek();
1192     }
1193     if (tok == T.divide) {
1194       // BH 1/19/2015: was || tok == T.colon here, but then that would
1195       // allow undocumented [ala]:a:1 to be the same as [ala]:a/1 ??
1196       if (!clauseModelSpec())
1197         return false;
1198       specSeen = true;
1199       tok = tokPeek();
1200     }
1201     if (!specSeen)
1202       return error(ERROR_residueSpecificationExpected);
1203     if (!residueSpecCodeGenerated) {
1204       // nobody generated any code, so everybody was a * (or equivalent)
1205       addTokenToPostfixToken(T.tokenAll);
1206     }
1207     return true;
1208   }
1209 
1210   /**
1211    * [a] or just a
1212    * @return true if handled
1213    */
clauseResNameSpec()1214   private boolean clauseResNameSpec() {
1215     getToken();
1216     int tok = tokPeek();
1217     switch (theToken.tok) {
1218     case T.times:
1219       return true;
1220     case T.leftsquare:
1221       String strSpec = "";
1222       while (getToken() != null && theToken.tok != T.rightsquare)
1223         strSpec += theValue;
1224       if (theToken == null)
1225         return false;
1226       if (strSpec == "")
1227         return true;
1228       int pt;
1229       return (strSpec.length() > 0 && (pt = strSpec.indexOf("*")) >= 0
1230           && pt != strSpec.length() - 1 ? error(ERROR_residueSpecificationExpected)
1231               : generateResidueSpecCode(T.o(T.spec_name_pattern, strSpec.toUpperCase())));
1232     default:
1233       if (T.tokAttr(tok, T.comparator)) {
1234         // a variable, not a name. For example:
1235         // a > 3
1236         // _e = 55
1237         returnToken();
1238         return false;
1239       }
1240       //check for a * in the next token, which
1241       //would indicate this must be a name with wildcard
1242       String res = (String) theValue;
1243       if (tok == T.times) {
1244         res = theValue + "*";
1245         getToken();
1246       }
1247       return generateResidueSpecCode(T.o(T.identifier, res));
1248     }
1249   }
1250 
clauseSequenceSpec()1251   private boolean clauseSequenceSpec() {
1252     if (tokPeek() == T.times)
1253       return  (getToken() != null); // true
1254     T seqToken = getSequenceCode(false);
1255     if (seqToken == null)
1256       return false;
1257     int tok = tokPeek();
1258     if (tok == T.minus || tok == T.integer && intPeek() < 0) {
1259       if (tok == T.minus) {
1260         tokenNext();
1261       } else {
1262          // hyphen masquerading as neg int
1263           int i = -intPeek();
1264           tokenNext().intValue = i;
1265           returnToken();
1266       }
1267       seqToken.tok = T.spec_seqcode_range;
1268       generateResidueSpecCode(seqToken);
1269       return addTokenToPostfixToken(getSequenceCode(true));
1270     }
1271     return generateResidueSpecCode(seqToken);
1272   }
1273 
getSequenceCode(boolean isSecond)1274   private T getSequenceCode(boolean isSecond) {
1275     int seqcode = Integer.MAX_VALUE;
1276     int seqvalue = Integer.MAX_VALUE;
1277     switch (tokPeek()) {
1278     case T.inscode:
1279       seqcode = tokenNext().intValue;
1280       break;
1281     case T.integer:
1282       seqvalue = tokenNext().intValue;
1283       break;
1284     default:
1285       if (!isSecond)
1286         return null;
1287       // can have open-ended range
1288       // select 3-
1289     }
1290     return T.tv(T.spec_seqcode, seqvalue, Integer.valueOf(seqcode));
1291   }
1292 
1293   /**
1294    * [:] [chars]
1295    *
1296    * [:] ["chars"]
1297    *
1298    * [:] [*]
1299    *
1300    * [:] [0-9]
1301    *
1302    * [:] [?]
1303    *
1304    * [:] (empty chain)
1305    *
1306    * @param tok
1307    * @return true if chain handled
1308    */
clauseChainSpec(int tok)1309   private boolean clauseChainSpec(int tok) {
1310     tokenNext();
1311     tok = tokPeek();
1312     String strChain;
1313     if (isTerminator(tok)) {
1314       strChain = " ";
1315     } else {
1316       switch (tok) {
1317       case T.times:
1318         return (getToken() != null); // true
1319       case T.integer:
1320         getToken();
1321         int val = theToken.intValue;
1322         if (val < 0 || val > 9999)
1323           return error(ERROR_invalidChainSpecification);
1324         strChain = "" + val;
1325         break;
1326       case T.string:
1327         vwr.getChainID("a", true); // forces chain case
1328         //$FALL-THROUGH$
1329       default:
1330         strChain = "" + getToken().value;
1331         break;
1332       }
1333       if (strChain.length() == 0)
1334         strChain = " ";
1335       else if (strChain.equals("?"))
1336         return true;
1337     }
1338     int chain = vwr.getChainID(strChain, false);
1339     return generateResidueSpecCode(T.tv(T.spec_chain, chain, "spec_chain"));
1340   }
1341 
1342   /**
1343    * check for %x or % (null alternate)
1344    *
1345    * @return true if no error
1346    */
clauseAlternateSpec()1347   private boolean clauseAlternateSpec() {
1348     tokenNext();
1349     // check for termination -- is this really the full list?
1350     if (isTerminator(tokPeek()))
1351       return generateResidueSpecCode(T.o(T.spec_alternate, null));
1352     switch (getToken().tok) {
1353     case T.times:
1354     case T.string:
1355     case T.integer:
1356     case T.identifier:
1357     case T.opIf:
1358     case T.x:
1359     case T.y:
1360     case T.z:
1361     case T.w:
1362       break;
1363     default:
1364       return error(ERROR_invalidModelSpecification);
1365     }
1366     return generateResidueSpecCode(T.o(T.spec_alternate, theToken.value));
1367   }
1368 
1369   /**
1370    * we allow : and % to have null values
1371    * @param tok
1372    * @return true if terminating
1373    */
isTerminator(int tok)1374   private boolean isTerminator(int tok) {
1375     switch (tok) {
1376     case T.nada:
1377     case T.divide:
1378     case T.opAnd:
1379     case T.opOr:
1380     case T.opNot:
1381     case T.comma:
1382     case T.rightparen:
1383     case T.rightbrace:
1384       return true;
1385     default:
1386       return false;
1387     }
1388   }
1389 
1390 
1391   /**
1392    * process /1   /1.1   / *  or just /
1393    *
1394    *
1395    * @return true if no error
1396    */
clauseModelSpec()1397   private boolean clauseModelSpec() {
1398     getToken();
1399     switch (tokPeek()) {
1400     case T.times:
1401       getToken();
1402       return true;
1403     case T.integer:
1404       return generateResidueSpecCode(T.o(T.spec_model, Integer
1405           .valueOf(getToken().intValue)));
1406     case T.decimal:
1407       return generateResidueSpecCode(T.tv(T.spec_model, fixModelSpec(getToken()), theValue));
1408     case T.comma:
1409     case T.rightbrace:
1410     case T.nada:
1411       // these are necessary to allow for {1 1 1/} or {1/,1/,1} in fractional coordinates
1412       return generateResidueSpecCode(T.o(T.spec_model, Integer
1413           .valueOf(1)));
1414     }
1415     return error(ERROR_invalidModelSpecification);
1416   }
1417 
fixModelSpec(T token)1418   private int fixModelSpec(T token) {
1419     int ival = token.intValue;
1420     if (ival == Integer.MAX_VALUE) {
1421       float f = ((Float) theValue).floatValue();
1422       if (f == (int) f)
1423         ival = ((int) f) * 1000000;
1424       if (ival < 0)
1425         ival = Integer.MAX_VALUE;
1426     }
1427     return ival;
1428   }
1429 
1430 
clauseAtomSpec()1431   private boolean clauseAtomSpec() {
1432     if (!tokenNextTok(T.per))
1433       return error(ERROR_invalidAtomSpecification);
1434     if (getToken() == null)
1435       return true;
1436     String atomSpec = "";
1437     if (theToken.tok == T.integer) {
1438       atomSpec += "" + theToken.intValue;
1439       if (getToken() == null)
1440         return error(ERROR_invalidAtomSpecification);
1441     }
1442     if (theToken.tok == T.times)
1443       return true;
1444     // here we cannot depend upon the atom spec being an identifier
1445     // in other words, not a known Jmol word. As long as the period
1446     // was there, we accept whatever is next
1447     atomSpec += "" + theToken.value;
1448     if (tokPeekIs(T.times)) {
1449       tokenNext();
1450       // this one is a '*' as a prime, not a wildcard
1451       atomSpec += "'";
1452     }
1453     return generateResidueSpecCode(T.tv(T.spec_atom, vwr.getJBR()
1454         .lookupSpecialAtomID(atomSpec.toUpperCase()), atomSpec));
1455   }
1456 
1457 //----------------------------------------------------------------------------------------
1458 //----------------------------------------------------------------------------------------
1459 //------------   ERROR HANDLING   --------------------------------------------------------
1460 //----------------------------------------------------------------------------------------
1461 //----------------------------------------------------------------------------------------
1462 
1463   protected String errorMessage;
1464   protected String errorMessageUntranslated;
1465   protected String errorLine;
1466   protected String errorType;
1467 
1468   protected final static int ERROR_badArgumentCount  = 0;
1469   protected final static int ERROR_badContext  = 1;
1470   protected final static int ERROR_commandExpected = 2;
1471   protected final static int ERROR_endOfCommandUnexpected  = 4;
1472   protected final static int ERROR_invalidExpressionToken  = 9;
1473   protected final static int ERROR_missingEnd  = 11;
1474   protected final static int ERROR_tokenExpected  = 15;
1475   protected final static int ERROR_tokenUnexpected  = 16;
1476   protected final static int ERROR_unrecognizedParameter  = 18;
1477   protected final static int ERROR_unrecognizedToken  = 19;
1478 
1479   private final static int ERROR_coordinateExpected  = 3;
1480   private final static int ERROR_endOfExpressionExpected  = 5;
1481   private final static int ERROR_identifierOrResidueSpecificationExpected  = 6;
1482   private final static int ERROR_invalidAtomSpecification  = 7;
1483   private final static int ERROR_invalidChainSpecification  = 8;
1484   private final static int ERROR_invalidModelSpecification  = 10;
1485   private final static int ERROR_numberExpected  = 12;
1486   private final static int ERROR_numberOrVariableNameExpected  = 13;
1487   private final static int ERROR_residueSpecificationExpected  = 14;
1488   private final static int ERROR_unrecognizedExpressionToken  = 17;
1489 
errorString(int iError, String value, String more, boolean translated)1490   static String errorString(int iError, String value, String more,
1491                             boolean translated) {
1492     boolean doTranslate = false;
1493     if (!translated && (doTranslate = GT.getDoTranslate()) == true)
1494       GT.setDoTranslate(false);
1495     String msg;
1496     switch (iError) {
1497     default:
1498       msg = "Unknown compiler error message number: " + iError;
1499       break;
1500     case ERROR_badArgumentCount: // 0;
1501       msg = GT.$("bad argument count"); // 0
1502       break;
1503     case ERROR_badContext: // 1;
1504       msg = GT.$("invalid context for {0}"); // 1
1505       break;
1506     case ERROR_commandExpected: // 2;
1507       msg = GT.$("command expected"); // 2
1508       break;
1509     case ERROR_coordinateExpected: // 3;
1510       msg = GT.$("{ number number number } expected"); // 3
1511       break;
1512     case ERROR_endOfCommandUnexpected: // 4;
1513       msg = GT.$("unexpected end of script command"); // 4
1514       break;
1515     case ERROR_endOfExpressionExpected: // 5;
1516       msg = GT.$("end of expression expected"); // 5
1517       break;
1518     case ERROR_identifierOrResidueSpecificationExpected: // 6;
1519       msg = GT.$("identifier or residue specification expected"); // 6
1520       break;
1521     case ERROR_invalidAtomSpecification: // 7;
1522       msg = GT.$("invalid atom specification"); // 7
1523       break;
1524     case ERROR_invalidChainSpecification: // 8;
1525       msg = GT.$("invalid chain specification"); // 8
1526       break;
1527     case ERROR_invalidExpressionToken: // 9;
1528       msg = GT.$("invalid expression token: {0}"); // 9
1529       break;
1530     case ERROR_invalidModelSpecification: // 10;
1531       msg = GT.$("invalid model specification"); // 10
1532       break;
1533     case ERROR_missingEnd: // 11;
1534       msg = GT.$("missing END for {0}"); // 11
1535       break;
1536     case ERROR_numberExpected: // 12;
1537       msg = GT.$("number expected"); // 12
1538       break;
1539     case ERROR_numberOrVariableNameExpected: // 13;
1540       msg = GT.$("number or variable name expected"); // 13
1541       break;
1542     case ERROR_residueSpecificationExpected: // 14;
1543       msg = GT.$("residue specification (ALA, AL?, A*) expected"); // 14
1544       break;
1545     case ERROR_tokenExpected: // 15;
1546       msg = GT.$("{0} expected"); // 15
1547       break;
1548     case ERROR_tokenUnexpected: // 16;
1549       msg = GT.$("{0} unexpected"); // 16
1550       break;
1551     case ERROR_unrecognizedExpressionToken: // 17;
1552       msg = GT.$("unrecognized expression token: {0}"); // 17
1553       break;
1554     case ERROR_unrecognizedParameter: // 18;
1555       msg = GT.$("unrecognized {0} parameter"); // 18
1556       break;
1557     case ERROR_unrecognizedToken: // 19;
1558       msg = GT.$("unrecognized token: {0}"); // 19
1559       break;
1560     }
1561     if (msg.indexOf("{0}") < 0) {
1562       if (value != null)
1563         msg += ": " + value;
1564     } else {
1565       msg = PT.rep(msg, "{0}", value);
1566       if (msg.indexOf("{1}") >= 0)
1567         msg = PT.rep(msg, "{1}", more);
1568       else if (more != null)
1569         msg += ": " + more;
1570     }
1571     if (!translated)
1572       GT.setDoTranslate(doTranslate);
1573     return msg;
1574   }
1575 
commandExpected()1576   protected boolean commandExpected() {
1577     ichToken = ichCurrentCommand;
1578     return error(ERROR_commandExpected);
1579   }
1580 
error(int error)1581   protected boolean error(int error) {
1582     return errorIntStr2(error, null, null);
1583   }
1584 
errorStr(int error, String value)1585   protected boolean errorStr(int error, String value) {
1586     return errorIntStr2(error, value, null);
1587   }
1588 
errorIntStr2(int iError, String value, String more)1589   protected boolean errorIntStr2(int iError, String value, String more) {
1590     String strError = errorString(iError, value, more, true);
1591     String strUntranslated = (GT.getDoTranslate() ? errorString(iError, value, more, false) : null);
1592     return errorStr2(strError, strUntranslated);
1593   }
1594 
isError()1595   private boolean isError() {
1596     return errorMessage != null;
1597   }
1598 
errorStr2(String errorMessage, String strUntranslated)1599   protected boolean errorStr2(String errorMessage, String strUntranslated) {
1600     this.errorMessage = errorMessage;
1601     errorMessageUntranslated = strUntranslated;
1602     return false;
1603   }
1604 
1605 }
1606