1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2009-06-12 07:58:28 -0500 (Fri, 12 Jun 2009) $ 4 * $Revision: 11009 $ 5 * 6 * Copyright (C) 2003-2006 Miguel, Jmol Development, www.jmol.org 7 * 8 * Contact: jmol-developers@lists.sf.net 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 23 */ 24 package org.jmol.script; 25 26 import java.util.Hashtable; 27 import java.util.Map; 28 29 import javajs.util.A4; 30 import javajs.util.AU; 31 import javajs.util.CU; 32 import javajs.util.DF; 33 import javajs.util.Lst; 34 import javajs.util.M3; 35 import javajs.util.M4; 36 import javajs.util.P3; 37 import javajs.util.P4; 38 import javajs.util.PT; 39 import javajs.util.Quat; 40 import javajs.util.T3; 41 import javajs.util.V3; 42 43 import javajs.util.BS; 44 import org.jmol.modelset.BondSet; 45 import org.jmol.util.BSUtil; 46 import org.jmol.util.BoxInfo; 47 import org.jmol.util.Escape; 48 import org.jmol.util.Logger; 49 import org.jmol.viewer.Viewer; 50 51 /** 52 * Reverse Polish Notation Engine for IF, SET, and @{...} 53 * 54 * Just a (not so simple?) RPN processor that can handle boolean, int, float, 55 * String, Point3f, BitSet, Array, Hashtable, Matrix3f, Matrix4f 56 * 57 * -- Bob Hanson 2/16/2007 58 * 59 * @author hansonr 60 * 61 */ 62 public class ScriptMathProcessor { 63 64 public boolean wasX; 65 public boolean asBitSet; 66 public int oPt = -1; // used in asynchronous load to mark which file is being loaded 67 68 private boolean chk; 69 private boolean wasSyntaxCheck; 70 private boolean debugHigh; 71 private ScriptExpr eval; 72 private Viewer vwr; 73 74 private T[] oStack = new T[8]; 75 private SV[] xStack = new SV[8]; 76 private char[] ifStack = new char[8]; 77 private int ifPt = -1; 78 private int xPt = -1; 79 private int parenCount; 80 private int squareCount; 81 private int braceCount; 82 private boolean isArrayItem; 83 private boolean asVector; 84 85 private boolean haveSpaceBeforeSquare; 86 private int equalCount; 87 88 private int ptid = 0; 89 private int ptx = Integer.MAX_VALUE; 90 private int pto = Integer.MAX_VALUE; 91 private boolean isSpecialAssignment; 92 private boolean doSelections = true; 93 private boolean assignLeft; 94 private boolean allowUnderflow; 95 private boolean isAssignment; 96 97 /** 98 * 99 * @param eval 100 * @param isSpecialAssignment 101 * x[n] = ... 102 * @param isArrayItem 103 * @param asVector 104 * return a Lst(SV) from getResult() 105 * @param asBitSet 106 * return a (SV)bitset 107 * @param allowUnderflow 108 * expression can terminate prior to end of statement 109 * 110 * @param key 111 */ ScriptMathProcessor(ScriptExpr eval, boolean isSpecialAssignment, boolean isArrayItem, boolean asVector, boolean asBitSet, boolean allowUnderflow, String key)112 ScriptMathProcessor(ScriptExpr eval, boolean isSpecialAssignment, boolean isArrayItem, 113 boolean asVector, boolean asBitSet, boolean allowUnderflow, String key) { 114 this.eval = eval; 115 this.isSpecialAssignment = assignLeft = isSpecialAssignment; 116 this.isAssignment = (isSpecialAssignment || key != null); 117 this.vwr = eval.vwr; 118 this.debugHigh = eval.debugHigh; 119 this.chk = wasSyntaxCheck = eval.chk; 120 this.isArrayItem = isArrayItem; 121 this.asVector = asVector || isArrayItem; 122 this.asBitSet = asBitSet; 123 this.allowUnderflow = allowUnderflow; // atom expressions only 124 wasX = isArrayItem; 125 if (debugHigh) 126 Logger.debug("initialize RPN"); 127 } 128 endAssignment()129 public boolean endAssignment() { 130 assignLeft = false; 131 return (doSelections = false); 132 } 133 134 @SuppressWarnings("unchecked") getResult()135 SV getResult() throws ScriptException { 136 boolean isOK = true; 137 while (isOK && oPt >= 0 && oStack[oPt] != null) 138 isOK = operate(); 139 if (isOK) { 140 if (asVector) { 141 // check for y = x x or y = x + ; 142 if (isAssignment && (xPt > 0 && oPt < 0 || oPt >= 0 && (oStack[oPt] != null))) 143 eval.invArg(); 144 Lst<SV> result = new Lst<SV>(); 145 for (int i = 0; i <= xPt; i++) 146 result.addLast(isSpecialAssignment ? xStack[i] : SV.selectItemVar(xStack[i])); 147 if (lastAssignedString != null) { 148 result.removeItemAt(0); 149 result.add(0, lastAssignedString); 150 lastAssignedString.intValue = xStack[0].intValue; 151 } 152 return SV.newV(T.vector, result); 153 } 154 if (xPt == 0) { 155 SV x = xStack[0]; 156 if (chk) { 157 if (asBitSet) 158 return SV.newV(T.bitset, new BS()); 159 return x; 160 } 161 if (x.tok == T.bitset 162 || x.tok == T.varray || x.tok == T.barray 163 || x.tok == T.string 164 || x.tok == T.matrix3f || x.tok == T.matrix4f) 165 x = SV.selectItemVar(x); 166 if (asBitSet && x.tok == T.varray) 167 x = SV.newV(T.bitset, 168 SV.unEscapeBitSetArray((Lst<SV>) x.value, false)); 169 return x; 170 } 171 } 172 if (!allowUnderflow && (xPt >= 0 || oPt >= 0)) 173 eval.invArg(); 174 return null; 175 } 176 putX(SV x)177 private void putX(SV x) { 178 if (skipping) 179 return; 180 if (wasX) { 181 try { 182 addOp(T.tokenComma); 183 } catch (ScriptException e) { 184 // System.out.println("Error adding comma"); 185 } 186 } 187 if (++xPt == xStack.length) 188 xStack = (SV[]) AU.doubleLength(xStack); 189 if (xPt < 0) 190 System.out.println("testing scriptemaafe"); 191 xStack[xPt] = x; 192 ptx = ++ptid; 193 if (debugHigh) { 194 Logger.debug("\nputx= " + x + " ptx=" + ptid); 195 } 196 } 197 putOp(T op)198 private void putOp(T op) { 199 if (++oPt >= oStack.length) 200 oStack = (T[]) AU.doubleLength(oStack); 201 oStack[oPt] = op; 202 pto = ++ptid; 203 if (debugHigh) { 204 Logger.debug("\nputop=" + op + " pto=" + ptid); 205 } 206 } 207 putIf(char c)208 private void putIf(char c) { 209 if (++ifPt >= ifStack.length) 210 ifStack = (char[]) AU.doubleLength(ifStack); 211 ifStack[ifPt] = c; 212 } 213 addXCopy(SV x)214 public boolean addXCopy(SV x) { 215 // copying int and decimal in case of ++ or -- 216 switch (x.tok){ 217 case T.integer: 218 x = SV.newI(x.intValue); 219 break; 220 case T.decimal: 221 x = SV.newV(T.decimal, x.value); 222 break; 223 } 224 return addX(x); 225 } 226 addX(SV x)227 public boolean addX(SV x) { 228 // the standard entry point 229 putX(x); 230 return wasX = true; 231 } 232 addXObj(Object x)233 public boolean addXObj(Object x) { 234 // the generic, slower, entry point 235 SV v = SV.getVariable(x); 236 if (v == null) 237 return false; 238 putX(v); 239 return wasX = true; 240 } 241 addXStr(String x)242 public boolean addXStr(String x) { 243 putX(SV.newS(x)); 244 return wasX = true; 245 } 246 addXBool(boolean x)247 public boolean addXBool(boolean x) { 248 putX(SV.getBoolean(x)); 249 return wasX = true; 250 } 251 addXInt(int x)252 public boolean addXInt(int x) { 253 // no check for unary minus 254 putX(SV.newI(x)); 255 return wasX = true; 256 } 257 addXList(Lst<?> x)258 public boolean addXList(Lst<?> x) { 259 putX(SV.getVariableList(x)); 260 return wasX = true; 261 } 262 addXMap(Map<String, ?> x)263 public boolean addXMap(Map<String, ?> x) { 264 putX(SV.getVariableMap(x)); 265 return wasX = true; 266 } 267 addXM3(M3 x)268 public boolean addXM3(M3 x) { 269 putX(SV.newV(T.matrix3f, x)); 270 return wasX = true; 271 } 272 addXM4(M4 x)273 public boolean addXM4(M4 x) { 274 putX(SV.newV(T.matrix4f, x)); 275 return wasX = true; 276 } 277 addXFloat(float x)278 public boolean addXFloat(float x) { 279 if (Float.isNaN(x)) 280 return addXStr("NaN"); 281 putX(SV.newF(x)); 282 return wasX = true; 283 } 284 addXBs(BS bs)285 public boolean addXBs(BS bs) { 286 // the standard entry point for bit sets 287 putX(SV.newV(T.bitset, bs)); 288 return wasX = true; 289 } 290 addXPt(P3 pt)291 public boolean addXPt(P3 pt) { 292 putX(SV.newV(T.point3f, pt)); 293 return wasX = true; 294 } 295 addXPt4(P4 pt)296 public boolean addXPt4(P4 pt) { 297 putX(SV.newV(T.point4f, pt)); 298 return wasX = true; 299 } 300 addXNum(T x)301 public boolean addXNum(T x) throws ScriptException { 302 // corrects for x-3 being x - 3 303 // only when coming from expression() or parameterExpression() 304 // and only when number is not flagged as forced negative 305 // as when x -3 306 SV v; 307 if (x instanceof SV) { 308 v = (SV) x; 309 } else { 310 switch (x.tok) { 311 case T.decimal: 312 if (wasX) { 313 float f = ((Float) x.value).floatValue(); 314 if (f < 0 || f == 0 && 1 / f == Float.NEGATIVE_INFINITY) { 315 addOp(T.tokenMinus); 316 v = SV.newF(-f); 317 break; 318 } 319 } 320 v = SV.newV(T.decimal, x.value); 321 break; 322 default: 323 int iv = x.intValue; 324 if (wasX && iv < 0) { 325 addOp(T.tokenMinus); 326 iv = -iv; 327 } 328 v = SV.newI(iv); 329 break; 330 } 331 } 332 putX(v); 333 return wasX = true; 334 } 335 addXAV(SV[] x)336 public boolean addXAV(SV[] x) { 337 putX(SV.getVariableAV(x)); 338 return wasX = true; 339 } 340 addXAD(double[] x)341 public boolean addXAD(double[] x) { 342 putX(SV.getVariableAD(x)); 343 return wasX = true; 344 } 345 addXAS(String[] x)346 public boolean addXAS(String[] x) { 347 putX(SV.getVariableAS(x)); 348 return wasX = true; 349 } 350 addXAI(int[] x)351 public boolean addXAI(int[] x) { 352 putX(SV.getVariableAI(x)); 353 return wasX = true; 354 } 355 addXAII(int[][] x)356 public boolean addXAII(int[][] x) { 357 putX(SV.getVariableAII(x)); 358 return wasX = true; 359 } 360 addXAF(float[] x)361 public boolean addXAF(float[] x) { 362 putX(SV.getVariableAF(x)); 363 return wasX = true; 364 } 365 addXAFF(float[][] x)366 public boolean addXAFF(float[][] x) { 367 putX(SV.getVariableAFF(x)); 368 return wasX = true; 369 } 370 isOpFunc(T op)371 private static boolean isOpFunc(T op) { 372 return (op != null && (T.tokAttr(op.tok, T.mathfunc) && op != T.tokenArraySquare 373 || op.tok == T.propselector && T.tokAttr(op.intValue, T.mathfunc))); 374 } 375 376 private boolean skipping; 377 private SV lastAssignedString; 378 379 /** 380 * addOp The primary driver of the Reverse Polish Notation evaluation engine. 381 * 382 * This method loads operators onto the oStack[] and processes them based on a 383 * precedence system. Operands are added by addX() onto the xStack[]. 384 * 385 * We check here for syntax issues that were not caught in the compiler. I 386 * suppose that should be done at compilation stage, but this is how it is for 387 * now. 388 * 389 * The processing of functional arguments and (___?___:___) constructs is 390 * carried out by pushing markers onto the stacks that later can be used to 391 * fill argument lists or turn "skipping" on or off. Note that in the case of 392 * skipped sections of ( ? : ) no attempt is made to do syntax checking. 393 * [That's not entirely true -- when syntaxChecking is true, that is, when the 394 * user is typing at the Jmol application console, then this code is being 395 * traversed with dummy variables. That could be improved, for sure. 396 * 397 * Actually, there's plenty of room for improvement here. I did this based on 398 * what I learned in High School in 1974 -- 35 years ago! -- when I managed to 399 * build a mini FORTRAN compiler from scratch in machine code. That was fun. 400 * (This was fun, too.) 401 * 402 * -- Bob Hanson, hansonr@stolaf.edu 6/9/2009 403 * 404 * 405 * @param op 406 * @return false if an error condition arises 407 * @throws ScriptException 408 */ addOp(T op)409 public boolean addOp(T op) throws ScriptException { 410 return addOpAllowMath(op, true, T.nada); 411 } 412 addOpAllowMath(T op, boolean allowMathFunc, int tokNext)413 boolean addOpAllowMath(T op, boolean allowMathFunc, int tokNext) throws ScriptException { 414 415 if (debugHigh) { 416 dumpStacks("adding " + op + " wasx=" + wasX); 417 } 418 419 // are we skipping due to a ( ? : ) construct? 420 int tok0 = (oPt >= 0 && oStack[oPt] != null ? oStack[oPt].tok : T.nada); 421 skipping = (ifPt >= 0 && (ifStack[ifPt] == 'F' || ifStack[ifPt] == 'X')); 422 if (skipping) 423 return checkSkip(op, tok0); 424 425 // Do we have the appropriate context for this operator? 426 427 int tok; 428 boolean isDotSelector = (op.tok == T.propselector); 429 430 if (isDotSelector && !wasX) 431 return false; 432 433 boolean isMathFunc = (allowMathFunc && isOpFunc(op)); 434 435 // the word "plane" can also appear alone, not as a function 436 if (oPt >= 1 && op.tok != T.leftparen && tok0 == T.plane) 437 tok0 = oStack[--oPt].tok; 438 439 // math functions as arguments appear without a prefixing operator 440 441 // check context, and for some cases, handle them here 442 T newOp = null; 443 boolean isLeftOp = false; 444 switch (op.tok) { 445 case T.spacebeforesquare: 446 haveSpaceBeforeSquare = true; 447 return true; 448 case T.comma: 449 if (!wasX) 450 return false; 451 break; 452 case T.minusMinus: 453 case T.plusPlus: 454 // check for [a ++b] 455 if (wasX && op.intValue == -1 && addOp(T.tokenComma)) 456 return addOp(op); 457 break; 458 case T.rightsquare: 459 break; 460 case T.rightparen: // () without argument allowed only for math funcs 461 if (!wasX && oPt >= 1 && tok0 == T.leftparen 462 && !isOpFunc(oStack[oPt - 1])) 463 return false; 464 break; 465 case T.minus: 466 if (!wasX) 467 op = SV.newV(T.unaryMinus, "-"); 468 break; 469 case T.min: 470 case T.max: 471 case T.average: 472 case T.sum: 473 case T.sum2: 474 case T.stddev: 475 case T.minmaxmask: // ALL 476 tok = (oPt < 0 ? T.nada : tok0); 477 if (!wasX || !(tok == T.propselector || tok == T.bonds || tok == T.atoms)) 478 return false; 479 oStack[oPt].intValue |= op.tok; 480 return true; 481 case T.leftsquare: // {....}[n][m] 482 isLeftOp = true; 483 if (!wasX || haveSpaceBeforeSquare) { 484 squareCount++; 485 op = newOp = T.tokenArraySquare; 486 haveSpaceBeforeSquare = false; 487 } 488 break; 489 case T.opNot: 490 case T.leftparen: 491 isLeftOp = true; 492 //$FALL-THROUGH$ 493 default: 494 if (isMathFunc) { 495 boolean isArgument = (oPt >= 1 && tok0 == T.leftparen); 496 if (isDotSelector) { 497 if (tokNext == T.leftparen) { 498 // check for {hash}.x(), which is not allowed 499 // if this is desired, one needs to use {hash}..x() 500 if (xStack[xPt].tok == T.hash) 501 return false; 502 } 503 } else if (wasX && !isArgument) { 504 return false; 505 } 506 newOp = op; 507 isLeftOp = true; 508 break; 509 } 510 if (wasX == isLeftOp && tok0 != T.propselector) { 511 // for now, because we have .label and .label() 512 if (!wasX || !allowMathFunc) 513 return false; 514 if (addOp(T.tokenComma)) 515 return addOp(op); 516 } 517 break; 518 } 519 520 // what is left are standard operators 521 522 // Q: Do we need to operate? 523 // A: Well, we must have an operator if... 524 while (oPt >= 0 525 // ...that operator is not :, 526 // because that's part of an array definition 527 && tok0 != T.colon 528 // ... and we don't have ++ or -- coming our way 529 // with no X on hand or definitely left-operator 530 && (op.tok != T.minusMinus && op.tok != T.plusPlus || wasX) 531 // ...and we do not have x( or x[ or func(.... 532 // because the function must come first 533 // unless we have x.y.z( or x.y.z[ 534 // in which case we DO need to do that selector first 535 && (!isLeftOp || tok0 == T.propselector 536 && (op.tok == T.propselector || op.tok == T.leftsquare)) 537 // ...and previous operator has equal or higher precedence 538 && T.getPrecedence(tok0) >= T.getPrecedence(op.tok) 539 // ...and this is not x - - y, because unary minus operates from 540 // right to left. 541 && (tok0 != T.unaryMinus || op.tok != T.unaryMinus)) { 542 543 // ) and ] must wait until matching ( or [ is found 544 if (op.tok == T.rightparen && tok0 == T.leftparen) { 545 // (x[2]) finalizes the selection 546 if (xPt >= 0) 547 xStack[xPt] = SV.selectItemVar(xStack[xPt]); 548 wasX = true; 549 break; 550 } 551 if (op.tok == T.rightsquare && tok0 == T.array) { 552 // we are done; just leave the array on the stack 553 break; 554 } 555 if (op.tok == T.rightsquare && tok0 == T.leftsquare) { 556 // this must be a selector 557 if (isArrayItem && squareCount == 1 && equalCount == 0) { 558 // x[3] = .... ; add a special flag for this, 559 // waiting until the very end to apply it. 560 wasX = false; 561 addX(SV.newT(T.tokenArrayOpen)); 562 break; 563 } 564 if (!doSelection()) 565 return false; 566 wasX = true; 567 break; 568 } 569 // if not, it's time to operate 570 571 if (!operate()) 572 return false; 573 tok0 = (oPt >= 0 && oStack[oPt] != null ? oStack[oPt].tok : 0); 574 } 575 576 // now add a marker on the xStack if necessary 577 578 if (newOp != null) { 579 wasX = false; 580 addX(SV.newV(T.opEQ, newOp)); 581 } 582 583 // fix up counts and operand flag 584 // right ) and ] are not added to the stack 585 586 switch (op.tok) { 587 case T.leftparen: 588 //System.out.println("----------(----------"); 589 parenCount++; 590 wasX = false; 591 break; 592 case T.opIf: 593 //System.out.println("---------IF---------"); 594 boolean isFirst = getX().asBoolean(); 595 if (tok0 == T.colon) 596 ifPt--; 597 else 598 putOp(T.tokenColon); 599 putIf(isFirst ? 'T' : 'F'); 600 skipping = !isFirst; 601 wasX = false; 602 // dumpStacks("(.." + isFirst + "...?<--here ... :...skip...) "); 603 return true; 604 case T.colon: 605 //System.out.println("----------:----------"); 606 if (tok0 != T.colon) 607 return false; 608 if (ifPt < 0) 609 return false; 610 ifStack[ifPt] = 'X'; 611 wasX = false; 612 skipping = true; 613 // dumpStacks("(..True...? ... :<--here ...skip...) "); 614 return true; 615 case T.rightparen: 616 //System.out.println("----------)----------"); 617 wasX = true; 618 if (parenCount-- <= 0) 619 return false; 620 if (tok0 == T.colon) { 621 // remove markers 622 ifPt--; 623 oPt--; 624 // dumpStacks("(..False...? ...skip... : ...)<--here "); 625 } 626 oPt--; 627 if (oPt < 0) 628 return true; 629 if (isOpFunc(oStack[oPt])) { 630 wasX = false; 631 if(!evaluateFunction(T.nada)) 632 return false; 633 } 634 skipping = (ifPt >= 0 && ifStack[ifPt] == 'X'); 635 return true; 636 case T.comma: 637 wasX = false; 638 return true; 639 case T.leftsquare: 640 squareCount++; 641 wasX = false; 642 break; 643 case T.rightsquare: 644 wasX = true; 645 if (squareCount-- <= 0 || oPt < 0 || !doSelections) 646 return !doSelections; 647 if (oStack[oPt].tok == T.array) 648 return evaluateFunction(T.leftsquare); 649 oPt--; 650 return true; 651 case T.propselector: 652 wasX = (!allowMathFunc || !T.tokAttr(op.intValue, T.mathfunc)); 653 break; 654 case T.leftbrace: 655 braceCount++; 656 wasX = false; 657 break; 658 case T.rightbrace: 659 if (braceCount-- <= 0) 660 return false; 661 wasX = false; 662 break; 663 case T.opAnd: 664 case T.opOr: 665 if (!wasSyntaxCheck && xPt < 0) 666 return false; 667 if (!wasSyntaxCheck && xStack[xPt].tok != T.bitset 668 && xStack[xPt].tok != T.varray) { 669 // check to see if we need to evaluate the second operand or not 670 // if not, then set this to syntax check in order to skip :) 671 // Jmol 12.0.4, Jmol 12.1.2 672 boolean tf = getX().asBoolean(); 673 addX(SV.getBoolean(tf)); 674 if (tf == (op.tok == T.opOr)) { // TRUE or.. FALSE and... 675 chk = true; 676 op = (op.tok == T.opOr ? T.tokenOrTRUE : T.tokenAndFALSE); 677 } 678 } 679 wasX = false; 680 break; 681 case T.plusPlus: 682 case T.minusMinus: 683 break; 684 case T.opEQ: 685 if (squareCount == 0) { 686 doSelections = true; 687 assignLeft = false; 688 equalCount++; 689 } 690 wasX = false; 691 break; 692 default: 693 wasX = false; 694 } 695 696 // add the operator if possible 697 698 putOp(op); 699 700 // immediate operation check: 701 switch (op.tok) { 702 case T.propselector: 703 return (((op.intValue & ~T.minmaxmask) == T.function 704 && op.intValue != T.function)? evaluateFunction(T.nada) : true); 705 case T.plusPlus: 706 case T.minusMinus: 707 return (wasX ? operate() : true); 708 } 709 710 return true; 711 } 712 checkSkip(T op, int tok0)713 private boolean checkSkip(T op, int tok0) { 714 switch (op.tok) { 715 case T.leftparen: 716 putOp(op); 717 break; 718 case T.colon: 719 // dumpStacks("skipping -- :"); 720 if (tok0 != T.colon || ifStack[ifPt] == 'X') 721 break; // ignore if not a clean opstack or T already processed 722 // no object here because we were skipping 723 // set to flag end of this parens 724 ifStack[ifPt] = 'T'; 725 wasX = false; 726 // dumpStacks("(..False...? .skip.. :<--here.... )"); 727 skipping = false; 728 break; 729 case T.rightparen: 730 if (tok0 == T.leftparen) { 731 oPt--; // clear opstack 732 break; 733 } 734 // dumpStacks("skipping -- )"); 735 if (tok0 != T.colon) { 736 putOp(op); 737 break; 738 } 739 wasX = true; 740 // and remove markers 741 ifPt--; 742 oPt -= 2; 743 skipping = false; 744 // dumpStacks("(..True...? ... : ...skip...)<--here "); 745 break; 746 } 747 return true; 748 } 749 doSelection()750 private boolean doSelection() { 751 if (xPt < 0 || xPt == 0 && !isArrayItem) { 752 return false; 753 } 754 SV var1 = xStack[xPt--]; 755 SV var = xStack[xPt]; 756 if ((var.tok == T.varray || var.tok == T.barray) && var.intValue != Integer.MAX_VALUE) 757 758 if (var1.tok == T.string || assignLeft && squareCount == 1) { 759 // immediate drill-down 760 // allow for x[1]["test"][1]["here"] 761 // common in getproperty business 762 // also x[1][2][3] = .... 763 // prior to 12.2/3.18, x[1]["id"] was misread as x[1][0] 764 xStack[xPt] = var = (SV) SV.selectItemTok(var, Integer.MIN_VALUE); 765 } 766 if (assignLeft && var.tok != T.string) 767 lastAssignedString = null; 768 switch (var.tok) { 769 case T.hash: 770 case T.context: 771 if (doSelections) { 772 SV v = var.mapValue(SV.sValue(var1)); 773 xStack[xPt] = (v == null ? SV.newS("") : v); 774 } else { 775 xPt++; 776 putOp(null); // final operations terminator 777 } 778 return true; 779 default: 780 var = SV.newS(SV.sValue(var)); 781 //$FALL-THROUGH$ 782 case T.bitset: 783 case T.barray: 784 case T.varray: 785 case T.string: 786 case T.matrix3f: 787 case T.matrix4f: 788 if (doSelections || var.tok == T.varray 789 && var.intValue == Integer.MAX_VALUE) { 790 xStack[xPt] = (SV) SV.selectItemTok(var, var1.asInt()); 791 if (assignLeft && var.tok == T.string && squareCount == 1) 792 lastAssignedString = var; 793 } else { 794 xPt++; 795 } 796 if (!doSelections) 797 putOp(null); // final operations terminator 798 799 break; 800 } 801 return true; 802 } 803 dumpStacks(String message)804 void dumpStacks(String message) { 805 Logger.debug("\n\n------------------\nRPN stacks: " + message + "\n"); 806 for (int i = 0; i <= xPt; i++) 807 Logger.debug("x[" + i + "]: " + xStack[i]); 808 Logger.debug("\n"); 809 for (int i = 0; i <= oPt; i++) 810 Logger.debug("o[" + i + "]: " + oStack[i] + " prec=" 811 + (oStack[i] == null ? "--" : "" + T.getPrecedence(oStack[i].tok))); 812 Logger.debug(" ifStack = " + (new String(ifStack)).substring(0, ifPt + 1)); 813 } 814 getX()815 public SV getX() throws ScriptException { 816 if (xPt < 0) 817 eval.error(ScriptError.ERROR_endOfStatementUnexpected); 818 SV v = SV.selectItemVar(xStack[xPt]); 819 xStack[xPt--] = null; 820 wasX = false; 821 return v; 822 } 823 getXTok()824 public int getXTok() { 825 return (xPt < 0 ? T.nada : xStack[xPt].tok); 826 } 827 evaluateFunction(int tok)828 private boolean evaluateFunction(int tok) throws ScriptException { 829 T op = oStack[oPt--]; 830 // for .xxx or .xxx() functions 831 // we store the token in the intValue field of the propselector token 832 if (tok == T.nada) 833 tok = (op.tok == T.propselector ? op.intValue & ~T.minmaxmask : op.tok); 834 835 int nParamMax = T.getMaxMathParams(tok); // note - this is NINE for 836 // dot-operators 837 int nParam = 0; 838 int pt = xPt; 839 while (pt >= 0 && xStack[pt--].value != op) 840 nParam++; 841 if (nParamMax > 0 && nParam > nParamMax) 842 return false; 843 SV[] args = new SV[nParam]; 844 for (int i = nParam; --i >= 0;) 845 args[i] = getX(); 846 xPt--; 847 // no script checking of functions because 848 // we cannot know what variables are real 849 // if this is a property selector, as in x.func(), then we 850 // just exit; otherwise we add a new TRUE to xStack 851 852 if (!chk) 853 return eval.getMathExt().evaluate(this, op, args, tok); 854 if (op.tok == T.propselector) 855 xPt--; // pop x in "x.func(...)" 856 if (xPt < 0) 857 xPt = 0; 858 switch (tok) { 859 case T.connected: 860 case T.polyhedra: 861 case T.search: 862 case T.smiles: 863 case T.within: 864 case T.contact: 865 return addXBs(new BS()); 866 } 867 return addXBool(true); 868 } 869 870 operate()871 private boolean operate() throws ScriptException { 872 T op = oStack[oPt--]; 873 P3 pt; 874 M3 m; 875 M4 m4; 876 String s; 877 SV x1; 878 if (debugHigh) { 879 dumpStacks("operate: " + op); 880 } 881 882 // check for a[3][2] 883 if (op.tok == T.opEQ 884 && (isArrayItem && squareCount == 0 && equalCount == 1 && oPt < 0 || oPt >= 0 885 && oStack[oPt] == null)) 886 return true; 887 888 SV x2; 889 switch (op.tok) { 890 case T.minusMinus: 891 case T.plusPlus: 892 if (xPt >= 0 && xStack[xPt].canIncrement()) { 893 x2 = xStack[xPt--]; 894 wasX = false; 895 break; 896 } 897 //$FALL-THROUGH$ 898 default: 899 x2 = getX(); 900 break; 901 } 902 if (x2 == T.tokenArrayOpen) 903 return false; 904 905 // unary: 906 907 switch (op.tok) { 908 case T.minusMinus: 909 case T.plusPlus: 910 // we are looking out for an array selection here 911 x1 = x2; 912 if (!chk) { 913 //System.out.println("ptx="+ ptx + " " + pto); 914 if (ptx < pto) { 915 // x++ must make a copy first 916 x1 = SV.newS("").setv(x2); 917 } 918 if (!x2.increment(op.tok == T.plusPlus ? 1 : -1)) 919 return false; 920 if (ptx > pto) { 921 // ++x must make a copy after 922 x1 = SV.newS("").setv(x2); 923 } 924 } 925 wasX = false; 926 putX(x1); // reverse getX() 927 wasX = true; 928 return true; 929 case T.unaryMinus: 930 switch (x2.tok) { 931 case T.integer: 932 return addXInt(-x2.asInt()); 933 case T.point3f: 934 pt = P3.newP((P3) x2.value); 935 pt.scale(-1f); 936 return addXPt(pt); 937 case T.point4f: 938 P4 pt4 = P4.newPt((P4) x2.value); 939 pt4.scale4(-1f); 940 return addXPt4(pt4); 941 case T.matrix3f: 942 m = M3.newM3((M3) x2.value); 943 m.invert(); 944 return addXM3(m); 945 case T.matrix4f: 946 m4 = M4.newM4((M4) x2.value); 947 m4.invert(); 948 return addXM4(m4); 949 case T.bitset: 950 return addXBs(BSUtil.copyInvert((BS) x2.value, 951 (x2.value instanceof BondSet ? vwr.ms.bondCount : vwr.ms.ac))); 952 } 953 return addXFloat(-x2.asFloat()); 954 case T.opNot: 955 if (chk) 956 return addXBool(true); 957 switch (x2.tok) { 958 case T.point4f: // quaternion 959 return addXPt4((Quat.newP4((P4) x2.value)).inv().toPoint4f()); 960 case T.matrix3f: 961 m = M3.newM3((M3) x2.value); 962 m.invert(); 963 return addXM3(m); 964 case T.matrix4f: 965 return addXM4(M4.newM4((M4) x2.value).invert()); 966 case T.bitset: 967 return addXBs(BSUtil.copyInvert((BS) x2.value, 968 (x2.value instanceof BondSet ? vwr.ms.bondCount : vwr.ms.ac))); 969 default: 970 return addXBool(!x2.asBoolean()); 971 } 972 case T.propselector: 973 int iv = (op.intValue == T.opIf ? T.opIf : op.intValue & ~T.minmaxmask); 974 if (chk) 975 return addXObj(SV.newS("")); 976 if (vwr.allowArrayDotNotation) 977 switch (x2.tok) { 978 case T.hash: 979 case T.context: 980 switch (iv) { 981 // reserved words XXXX for x.XXXX 982 case T.array: 983 case T.keys: 984 case T.size: 985 case T.type: 986 break; 987 //$FALL-THROUGH$ 988 default: 989 SV ret = x2.mapValue((String) op.value); 990 return addXObj(ret == null ? SV.newS("") : ret); 991 } 992 break; 993 } 994 switch (iv) { 995 case T.array: 996 return addX(x2.toArray()); 997 case T.opIf: // '?' 998 case T.identifier: 999 // special flag to get all properties. 1000 return (x2.tok == T.bitset && (chk ? addXStr("") : getAllProperties(x2, 1001 (String) op.value))); 1002 case T.type: 1003 return addXStr(typeOf(x2)); 1004 case T.keys: 1005 String[] keys = x2.getKeys((op.intValue & T.minmaxmask) == T.minmaxmask); 1006 return (keys == null ? addXStr("") : addXAS(keys)); 1007 case T.length: 1008 case T.count: 1009 case T.size: 1010 if (iv == T.length && x2.value instanceof BondSet) 1011 break; 1012 return addXInt(SV.sizeOf(x2)); 1013 case T.lines: 1014 switch (x2.tok) { 1015 case T.matrix3f: 1016 case T.matrix4f: 1017 s = SV.sValue(x2); 1018 s = PT.rep(s.substring(1, s.length() - 1), "],[", "]\n["); 1019 break; 1020 case T.string: 1021 s = (String) x2.value; 1022 break; 1023 default: 1024 s = SV.sValue(x2); 1025 } 1026 s = PT.rep(s, "\n\r", "\n").replace('\r', '\n'); 1027 return addXAS(PT.split(s, "\n")); 1028 case T.color: 1029 switch (x2.tok) { 1030 case T.string: 1031 case T.varray: 1032 return addXPt(CU.colorPtFromString(SV.sValue(x2))); 1033 case T.integer: 1034 case T.decimal: 1035 return addXPt(vwr.getColorPointForPropertyValue(SV.fValue(x2))); 1036 case T.point3f: 1037 return addXStr(Escape.escapeColor(CU.colorPtToFFRGB((P3) x2.value))); 1038 default: 1039 // handle bitset later 1040 } 1041 break; 1042 case T.boundbox: 1043 return (chk ? addXStr("x") : getBoundBox(x2)); 1044 } 1045 if (chk) 1046 return addXStr(SV.sValue(x2)); 1047 if (x2.tok == T.string) { 1048 Object v = SV.unescapePointOrBitsetAsVariable(SV.sValue(x2)); 1049 if (!(v instanceof SV)) 1050 return false; 1051 x2 = (SV) v; 1052 } 1053 if (op.tok == x2.tok) 1054 x2 = getX(); 1055 return getPointOrBitsetOperation(op, x2); 1056 } 1057 1058 // binary: 1059 x1 = getX(); 1060 if (chk) { 1061 if (op == T.tokenAndFALSE || op == T.tokenOrTRUE) 1062 chk = false; 1063 return addX(SV.newT(x1)); 1064 } 1065 1066 return binaryOp(op, x1, x2); 1067 } 1068 1069 private static final String qMods = " w:0 x:1 y:2 z:3 normal:4 eulerzxz:5 eulerzyz:6 vector:-1 theta:-2 axisx:-3 axisy:-4 axisz:-5 axisangle:-6 matrix:-9"; 1070 1071 @SuppressWarnings("unchecked") binaryOp(T op, SV x1, SV x2)1072 public boolean binaryOp(T op, SV x1, SV x2) throws ScriptException { 1073 P3 pt; 1074 P4 pt4; 1075 M3 m; 1076 String s; 1077 float f; 1078 1079 switch (op.tok) { 1080 case T.opAND: 1081 case T.opAnd: 1082 switch (x1.tok) { 1083 case T.bitset: 1084 BS bs = (BS) x1.value; 1085 switch (x2.tok) { 1086 case T.integer: 1087 int x = x2.asInt(); 1088 return (addXBool(x < 0 ? false : bs.get(x))); 1089 case T.bitset: 1090 bs = BSUtil.copy(bs); 1091 bs.and((BS) x2.value); 1092 return addXBs(bs); 1093 } 1094 break; 1095 } 1096 return addXBool(x1.asBoolean() && x2.asBoolean()); 1097 case T.opOr: 1098 switch (x1.tok) { 1099 case T.bitset: 1100 BS bs = BSUtil.copy((BS) x1.value); 1101 switch (x2.tok) { 1102 case T.bitset: 1103 bs.or((BS) x2.value); 1104 return addXBs(bs); 1105 case T.integer: 1106 int x = x2.asInt(); 1107 if (x < 0) 1108 break; 1109 bs.set(x); 1110 return addXBs(bs); 1111 case T.varray: 1112 Lst<SV> sv = (Lst<SV>) x2.value; 1113 for (int i = sv.size(); --i >= 0;) { 1114 int b = sv.get(i).asInt(); 1115 if (b >= 0) 1116 bs.set(b); 1117 } 1118 return addXBs(bs); 1119 } 1120 break; 1121 case T.varray: 1122 return addX(SV.concatList(x1, x2, false)); 1123 } 1124 return addXBool(x1.asBoolean() || x2.asBoolean()); 1125 case T.opXor: 1126 if (x1.tok == T.bitset && x2.tok == T.bitset) { 1127 BS bs = BSUtil.copy((BS) x1.value); 1128 bs.xor((BS) x2.value); 1129 return addXBs(bs); 1130 } 1131 boolean a = x1.asBoolean(); 1132 boolean b = x2.asBoolean(); 1133 return addXBool(a && !b || b && !a); 1134 case T.opToggle: 1135 if (x1.tok != T.bitset || x2.tok != T.bitset) 1136 return false; 1137 return addXBs( 1138 BSUtil.toggleInPlace(BSUtil.copy((BS) x1.value), (BS) x2.value)); 1139 case T.opLE: 1140 return addXBool(x1.tok == T.integer && x2.tok == T.integer 1141 ? x1.intValue <= x2.intValue 1142 : x1.asFloat() <= x2.asFloat()); 1143 case T.opGE: 1144 return addXBool(x1.tok == T.integer && x2.tok == T.integer 1145 ? x1.intValue >= x2.intValue 1146 : x1.asFloat() >= x2.asFloat()); 1147 case T.opGT: 1148 return addXBool( 1149 x1.tok == T.integer && x2.tok == T.integer ? x1.intValue > x2.intValue 1150 : x1.asFloat() > x2.asFloat()); 1151 case T.opLT: 1152 return addXBool( 1153 x1.tok == T.integer && x2.tok == T.integer ? x1.intValue < x2.intValue 1154 : x1.asFloat() < x2.asFloat()); 1155 case T.opEQ: 1156 return addXBool(SV.areEqual(x1, x2)); 1157 case T.opNE: 1158 return addXBool(!SV.areEqual(x1, x2)); 1159 case T.opLIKE: 1160 return addXBool(SV.isLike(x1, x2)); 1161 case T.plus: 1162 switch (x1.tok) { 1163 case T.hash: 1164 Map<String, SV> ht = new Hashtable<String, SV>( 1165 (Map<String, SV>) x1.value); 1166 Map<String, SV> map = x2.getMap(); 1167 if (map != null) 1168 ht.putAll(map); // new Jmol 14.18 1169 return addX(SV.getVariableMap(ht)); 1170 case T.integer: 1171 if (!isDecimal(x2)) 1172 return addXInt(x1.intValue + x2.asInt()); 1173 break; 1174 case T.string: 1175 return addX(SV.newS(SV.sValue(x1) + SV.sValue(x2))); 1176 case T.point3f: 1177 pt = P3.newP((P3) x1.value); 1178 switch (x2.tok) { 1179 case T.point3f: 1180 pt.add((P3) x2.value); 1181 return addXPt(pt); 1182 case T.point4f: 1183 // extract {xyz} 1184 pt4 = (P4) x2.value; 1185 pt.add(P3.new3(pt4.x, pt4.y, pt4.z)); 1186 return addXPt(pt); 1187 default: 1188 f = x2.asFloat(); 1189 return addXPt(P3.new3(pt.x + f, pt.y + f, pt.z + f)); 1190 } 1191 case T.matrix3f: 1192 switch (x2.tok) { 1193 case T.matrix3f: 1194 m = M3.newM3((M3) x1.value); 1195 m.add((M3) x2.value); 1196 return addXM3(m); 1197 case T.point3f: 1198 return addXM4(getMatrix4f((M3) x1.value, (P3) x2.value)); 1199 } 1200 break; 1201 case T.matrix4f: 1202 switch (x2.tok) { 1203 case T.point3f: 1204 M4 m4b = M4.newM4((M4) x1.value); 1205 m4b.add((T3) x2.value); 1206 return addXM4(m4b); 1207 } 1208 break; 1209 case T.point4f: 1210 Quat q1 = Quat.newP4((P4) x1.value); 1211 switch (x2.tok) { 1212 default: 1213 return addXPt4(q1.add(x2.asFloat()).toPoint4f()); 1214 case T.point4f: 1215 return addXPt4(q1.mulQ(Quat.newP4((P4) x2.value)).toPoint4f()); 1216 } 1217 case T.varray: 1218 return addX(SV.concatList(x1, x2, true)); 1219 } 1220 return addXFloat(x1.asFloat() + x2.asFloat()); 1221 case T.minus: 1222 switch (x1.tok) { 1223 case T.integer: 1224 if (!isDecimal(x2)) 1225 return addXInt(x1.intValue - x2.asInt()); 1226 break; 1227 case T.string: 1228 if (!isDecimal(x2) && !isDecimal(x1)) 1229 return addXInt(x1.asInt() - x2.asInt()); 1230 break; 1231 case T.hash: 1232 Map<String, SV> ht = new Hashtable<String, SV>( 1233 (Map<String, SV>) x1.value); 1234 ht.remove(SV.sValue(x2)); 1235 return addX(SV.getVariableMap(ht)); 1236 case T.matrix3f: 1237 if (x2.tok != T.matrix3f) 1238 break; 1239 m = M3.newM3((M3) x1.value); 1240 m.sub((M3) x2.value); 1241 return addXM3(m); 1242 case T.matrix4f: 1243 if (x2.tok != T.matrix4f) 1244 break; 1245 M4 m4 = M4.newM4((M4) x1.value); 1246 m4.sub((M4) x2.value); 1247 return addXM4(m4); 1248 case T.point3f: 1249 pt = P3.newP((P3) x1.value); 1250 switch (x2.tok) { 1251 case T.point3f: 1252 pt.sub((P3) x2.value); 1253 return addXPt(pt); 1254 case T.point4f: 1255 // extract {xyz} 1256 pt4 = (P4) x2.value; 1257 pt.sub(P3.new3(pt4.x, pt4.y, pt4.z)); 1258 return addXPt(pt); 1259 } 1260 f = x2.asFloat(); 1261 return addXPt(P3.new3(pt.x - f, pt.y - f, pt.z - f)); 1262 case T.point4f: 1263 Quat q1 = Quat.newP4((P4) x1.value); 1264 if (x2.tok == T.point4f) { 1265 Quat q2 = Quat.newP4((P4) x2.value); 1266 return addXPt4(q2.mulQ(q1.inv()).toPoint4f()); 1267 } 1268 return addXPt4(q1.add(-x2.asFloat()).toPoint4f()); 1269 } 1270 return addXFloat(x1.asFloat() - x2.asFloat()); 1271 case T.mul3: 1272 if (x1.tok == T.point3f && x2.tok == T.point3f) { 1273 pt = (P3) x1.value; 1274 P3 pt2 = (P3) x2.value; 1275 return addXPt(P3.new3(pt.x * pt2.x, pt.y * pt2.y, pt.z * pt2.z)); 1276 } 1277 //$FALL-THROUGH$ 1278 case T.times: 1279 switch (x1.tok) { 1280 case T.integer: 1281 return (isDecimal(x2) ? addXFloat(x1.intValue * x2.asFloat()) 1282 : addXInt(x1.intValue * x2.asInt())); 1283 case T.string: 1284 return (isDecimal(x2) || isDecimal(x1) 1285 ? addXFloat(x1.asFloat() * x2.asFloat()) 1286 : addXInt(x1.asInt() * x2.asInt())); 1287 } 1288 pt = (x1.tok == T.matrix3f || x1.tok == T.matrix4f ? ptValue(x2, null) 1289 : x2.tok == T.matrix3f ? ptValue(x1, null) : null); 1290 pt4 = (x1.tok == T.matrix4f ? planeValue(x2) 1291 : x2.tok == T.matrix4f ? planeValue(x1) : null); 1292 // checking here to make sure arrays remain arrays and 1293 // points remain points with matrix operations. 1294 // we check x2, because x1 could be many things. 1295 switch (x2.tok) { 1296 case T.matrix3f: 1297 if (pt != null) { 1298 // pt * m 1299 M3 m3b = M3.newM3((M3) x2.value); 1300 m3b.transpose(); 1301 P3 pt1 = P3.newP(pt); 1302 m3b.rotate(pt1); 1303 return (x1.tok == T.varray 1304 ? addX(SV.getVariableAF(new float[] { pt1.x, pt1.y, pt1.z })) 1305 : addXPt(pt1)); 1306 } 1307 if (pt4 != null) 1308 // q * m --> q 1309 return addXPt4( 1310 (Quat.newP4(pt4).mulQ(Quat.newM((M3) x2.value))).toPoint4f()); 1311 break; 1312 case T.matrix4f: 1313 // pt4 * m4 1314 // [a b c d] * m4 1315 if (pt4 != null) { 1316 M4 m4b = M4.newM4((M4) x2.value); 1317 m4b.transpose(); 1318 P4 pt41 = P4.newPt(pt4); 1319 m4b.transform(pt41); 1320 return (x1.tok == T.varray 1321 ? addX(SV.getVariableAF( 1322 new float[] { pt41.x, pt41.y, pt41.z, pt41.w })) 1323 : addXPt4(pt41)); 1324 } 1325 break; 1326 } 1327 switch (x1.tok) { 1328 case T.matrix3f: 1329 M3 m3 = (M3) x1.value; 1330 if (pt != null) { 1331 P3 pt1 = P3.newP(pt); 1332 m3.rotate(pt1); 1333 return (x2.tok == T.varray 1334 ? addX(SV.getVariableAF(new float[] { pt1.x, pt1.y, pt1.z })) 1335 : addXPt(pt1)); 1336 } 1337 switch (x2.tok) { 1338 case T.matrix3f: 1339 m = M3.newM3((M3) x2.value); 1340 m.mul2(m3, m); 1341 return addXM3(m); 1342 case T.point4f: 1343 // m * q 1344 return addXM3( 1345 Quat.newM(m3).mulQ(Quat.newP4((P4) x2.value)).getMatrix()); 1346 case T.varray: 1347 Lst<SV> l = x2.getList(); 1348 Lst<P3> lnew = new Lst<P3>(); 1349 for (int i = l.size(); --i >= 0;) { 1350 P3 pt1 = P3.newP(SV.ptValue(l.get(i))); 1351 m3.rotate(pt1); 1352 lnew.add(pt1); 1353 } 1354 return addXList(lnew); 1355 } 1356 f = x2.asFloat(); 1357 A4 aa = new A4(); 1358 aa.setM(m3); 1359 aa.angle *= f; 1360 return addXM3(new M3().setAA(aa)); 1361 case T.matrix4f: 1362 M4 m4 = (M4) x1.value; 1363 if (pt != null) { 1364 P3 pt1 = P3.newP(pt); 1365 m4.rotTrans(pt1); 1366 return (x2.tok == T.varray 1367 ? addX(SV.getVariableAF(new float[] { pt1.x, pt1.y, pt1.z })) 1368 : addXPt(pt1)); 1369 } 1370 if (pt4 != null) { 1371 m4.transform(pt4); 1372 return (x2.tok == T.varray 1373 ? addX( 1374 SV.getVariableAF(new float[] { pt4.x, pt4.y, pt4.z, pt4.w })) 1375 : addXPt4(pt4)); 1376 } 1377 switch (x2.tok) { 1378 case T.matrix4f: 1379 M4 m4b = M4.newM4((M4) x2.value); 1380 m4b.mul2(m4, m4b); 1381 return addXM4(m4b); 1382 case T.varray: 1383 Lst<SV> l = x2.getList(); 1384 Lst<P3> lnew = new Lst<P3>(); 1385 for (int i = l.size(); --i >= 0;) { 1386 P3 pt1 = P3.newP(SV.ptValue(l.get(i))); 1387 m4.rotTrans(pt1); 1388 lnew.add(pt1); 1389 } 1390 return addXList(lnew); 1391 } 1392 return addXStr("NaN"); 1393 case T.point3f: 1394 pt = P3.newP((P3) x1.value); 1395 switch (x2.tok) { 1396 case T.point3f: 1397 P3 pt2 = ((P3) x2.value); 1398 return addXFloat(pt.x * pt2.x + pt.y * pt2.y + pt.z * pt2.z); 1399 } 1400 f = x2.asFloat(); 1401 return addXPt(P3.new3(pt.x * f, pt.y * f, pt.z * f)); 1402 case T.point4f: 1403 if (x2.tok == T.point4f) 1404 // quaternion multiplication 1405 // note that Point4f is {x,y,z,w} so we use that for 1406 // quaternion notation as well here. 1407 return addXPt4(Quat.newP4((P4) x1.value) 1408 .mulQ(Quat.newP4((P4) x2.value)).toPoint4f()); 1409 return addXPt4(Quat.newP4((P4) x1.value).mul(x2.asFloat()).toPoint4f()); 1410 } 1411 return addXFloat(x1.asFloat() * x2.asFloat()); 1412 case T.divide: 1413 float f2; 1414 switch (x1.tok) { 1415 case T.integer: 1416 if (x2.tok == T.integer && x2.intValue != 0) 1417 return addXInt(x1.intValue / x2.intValue); 1418 int n = (isDecimal(x2) ? 0 : x2.asInt()); 1419 if (n != 0) 1420 return addXInt(x1.intValue / n); 1421 break; 1422 case T.string: 1423 int i2; 1424 if (!isDecimal(x1) && !isDecimal(x2) && (i2 = x2.asInt()) != 0) 1425 return addXInt(x1.asInt() / i2); 1426 break; 1427 case T.point3f: 1428 pt = P3.newP((P3) x1.value); 1429 return addXPt( 1430 (f2 = x2.asFloat()) == 0 ? P3.new3(Float.NaN, Float.NaN, Float.NaN) 1431 : P3.new3(pt.x / f2, pt.y / f2, pt.z / f2)); 1432 case T.point4f: 1433 return addXPt4(x2.tok == T.point4f 1434 ? Quat.newP4((P4) x1.value).div(Quat.newP4((P4) x2.value)) 1435 .toPoint4f() 1436 : (f2 = x2.asFloat()) == 0 1437 ? P4.new4(Float.NaN, Float.NaN, Float.NaN, Float.NaN) 1438 : Quat.newP4((P4) x1.value).mul(1 / f2).toPoint4f()); 1439 } 1440 return addXFloat(x1.asFloat() / x2.asFloat()); 1441 case T.leftdivide: 1442 f = x2.asFloat(); 1443 if (x1.tok == T.point4f) { 1444 return (f == 0 1445 ? addXPt4(P4.new4(Float.NaN, Float.NaN, Float.NaN, Float.NaN)) 1446 : x2.tok == T.point4f 1447 ? addXPt4(Quat.newP4((P4) x1.value) 1448 .divLeft(Quat.newP4((P4) x2.value)).toPoint4f()) 1449 : addXPt4(Quat.newP4((P4) x1.value).mul(1 / f).toPoint4f())); 1450 } 1451 return addXInt( 1452 f == 0 ? 0 : (int) Math.floor(x1.asFloat() / x2.asFloat())); 1453 case T.timestimes: 1454 f = (float) Math.pow(x1.asFloat(), x2.asFloat()); 1455 return (x1.tok == T.integer && x2.tok == T.integer ? addXInt((int) f) 1456 : addXFloat(f)); 1457 case T.percent: 1458 // more than just modulus 1459 1460 // float % n round to n digits; n = 0 does "nice" rounding 1461 // String % -n trim to width n; left justify 1462 // String % n trim to width n; right justify 1463 // Point3f % n ah... sets to multiple of unit cell! 1464 // bitset % n 1465 // Point3f * Point3f does dot product 1466 // Point3f / Point3f divides by magnitude 1467 // float * Point3f gets magnitude 1468 // Point4f % n returns q0, q1, q2, q3, or theta 1469 // Point4f % Point4f 1470 s = null; 1471 int n = x2.asInt(); 1472 switch (x1.tok) { 1473 case T.on: 1474 case T.off: 1475 case T.integer: 1476 default: 1477 break; 1478 case T.decimal: 1479 f = x1.asFloat(); 1480 // neg is scientific notation 1481 if (n == 0) 1482 return addXInt(Math.round(f)); 1483 s = DF.formatDecimal(f, n); 1484 return addXStr(s); 1485 case T.string: 1486 s = (String) x1.value; 1487 return addXStr(n == 0 ? PT.trim(s, "\n\t ") 1488 : n == 9999 ? s.toUpperCase() 1489 : n == -9999 ? s.toLowerCase() 1490 : n > 0 ? PT.formatS(s, n, n, false, false) 1491 : PT.formatS(s, n, n - 1, true, false)); 1492 case T.varray: 1493 String[] list = SV.strListValue(x1); 1494 for (int i = 0; i < list.length; i++) { 1495 if (n == 0) 1496 list[i] = list[i].trim(); 1497 else if (n > 0) 1498 list[i] = PT.formatS(list[i], n, n, true, false); 1499 else 1500 list[i] = PT.formatS(s, -n, n, false, false); 1501 } 1502 return addXAS(list); 1503 case T.point3f: 1504 pt = P3.newP((P3) x1.value); 1505 vwr.toUnitCell(pt, P3.new3(n, n, n)); 1506 return addXPt(pt); 1507 case T.point4f: 1508 pt4 = (P4) x1.value; 1509 if (x2.tok == T.point3f) 1510 return addXPt( 1511 (P3) (Quat.newP4(pt4)).transform2((P3) x2.value, new P3())); 1512 if (x2.tok == T.point4f) { 1513 P4 v4 = P4.newPt((P4) x2.value); 1514 (Quat.newP4(pt4)).getThetaDirected(v4); 1515 return addXPt4(v4); 1516 } 1517 if (n == 0 && x2.tok == T.string) { 1518 s = " " + x2.value.toString().trim().toLowerCase() + ":"; 1519 int i = qMods.indexOf(s); 1520 n = (i >= 0 ? PT.parseInt(qMods.substring(i + s.length())) : -99); 1521 } 1522 switch (n) { 1523 // q%0 w 1524 // q%1 x 1525 // q%2 y 1526 // q%3 z 1527 // q%4 normal 1528 // q%5 EulerZXZ (degrees) 1529 // q%6 EulerZYZ (degrees) 1530 // q%-1 vector(1) 1531 // q%-2 theta 1532 // q%-3 Matrix column 0 1533 // q%-4 Matrix column 1 1534 // q%-5 Matrix column 2 1535 // q%-6 AxisAngle format 1536 // q%-9 Matrix format 1537 case 0: 1538 return addXFloat(pt4.w); 1539 case 1: 1540 return addXFloat(pt4.x); 1541 case 2: 1542 return addXFloat(pt4.y); 1543 case 3: 1544 return addXFloat(pt4.z); 1545 } 1546 Quat q = Quat.newP4(pt4); 1547 switch (n) { 1548 case 4: 1549 return addXPt(P3.newP(q.getNormal())); 1550 case 5: 1551 return addXAF(q.getEulerZXZ()); 1552 case 6: 1553 return addXAF(q.getEulerZYZ()); 1554 case -1: 1555 return addXPt(P3.newP(q.getVector(-1))); 1556 case -2: 1557 return addXFloat(q.getTheta()); 1558 case -3: 1559 return addXPt(P3.newP(q.getVector(0))); 1560 case -4: 1561 return addXPt(P3.newP(q.getVector(1))); 1562 case -5: 1563 return addXPt(P3.newP(q.getVector(2))); 1564 case -6: 1565 A4 ax = q.toAxisAngle4f(); 1566 return addXPt4( 1567 P4.new4(ax.x, ax.y, ax.z, (float) (ax.angle * 180 / Math.PI))); 1568 case -9: 1569 return addXM3(q.getMatrix()); 1570 default: 1571 return addXStr("NaN"); 1572 } 1573 case T.matrix4f: 1574 M4 m4 = (M4) x1.value; 1575 switch (n) { 1576 case 1: 1577 M3 m3 = new M3(); 1578 m4.getRotationScale(m3); 1579 return addXM3(m3); 1580 case 2: 1581 V3 v3 = new V3(); 1582 m4.getTranslation(v3); 1583 return addXPt(P3.newP(v3)); 1584 default: 1585 return false; 1586 } 1587 case T.bitset: 1588 return addXBs(SV.bsSelectRange(x1, n)); 1589 } 1590 return addXInt(n == 0 ? x1.asInt() : x1.asInt() % n); 1591 } 1592 return true; 1593 } 1594 isDecimal(SV x)1595 private boolean isDecimal(SV x) { 1596 String s; 1597 return (x.tok == T.decimal || x.tok == T.string 1598 && ((s = SV.sValue(x).trim()).indexOf(".") >= 0 || s.indexOf("+") > 0 || s 1599 .lastIndexOf("-") > 0)); 1600 } 1601 ptValue(SV x, BS bsRestrict)1602 public P3 ptValue(SV x, BS bsRestrict) throws ScriptException { 1603 Object pt; 1604 switch (x.tok) { 1605 case T.point3f: 1606 return (P3) x.value; 1607 case T.bitset: 1608 BS bs = (BS) x.value; 1609 if (bs.isEmpty()) 1610 break; 1611 if (bsRestrict != null) { 1612 bs = BSUtil.copy(bs); 1613 bs.and(bsRestrict); 1614 } 1615 return (P3) eval.getBitsetProperty(bs, null, T.xyz, null, 1616 null, x.value, null, false, Integer.MAX_VALUE, false); 1617 case T.string: 1618 pt = Escape.uP(SV.sValue(x)); 1619 if (pt instanceof P3) 1620 return (P3) pt; 1621 break; 1622 case T.varray: 1623 pt = Escape.uP("{" + SV.sValue(x).replace(']',' ').replace('[',' ') + "}"); 1624 if (pt instanceof P3) 1625 return (P3) pt; 1626 break; 1627 } 1628 return null; 1629 } 1630 planeValue(T x)1631 public static P4 planeValue(T x) { 1632 Object pt; 1633 switch (x.tok) { 1634 case T.point4f: 1635 return (P4) x.value; 1636 case T.varray: 1637 break; 1638 case T.string: 1639 String s = (String) x.value; 1640 boolean isMinus = s.startsWith("-"); 1641 float f = (isMinus ? -1 : 1); 1642 if (isMinus) 1643 s = s.substring(1); 1644 P4 p4 = null; 1645 switch (s.length() < 2 ? -1 1646 : "xy yz xz x= y= z=".indexOf(s.substring(0, 2))) { 1647 case 0: 1648 return P4.new4(1, 1, 0, f); 1649 case 3: 1650 return P4.new4(0, 1, 1, f); 1651 case 6: 1652 return P4.new4(1, 0, 1, f); 1653 case 9: 1654 p4 = P4.new4(1, 0, 0, -f * PT.parseFloat(s.substring(2))); 1655 break; 1656 case 12: 1657 p4 = P4.new4(0, 1, 0, -f * PT.parseFloat(s.substring(2))); 1658 break; 1659 case 15: 1660 p4 = P4.new4(0, 0, 1, -f * PT.parseFloat(s.substring(2))); 1661 break; 1662 } 1663 if (p4 != null && !Float.isNaN(p4.w)) 1664 return p4; 1665 break; 1666 default: 1667 return null; 1668 } 1669 pt = Escape.uP(SV.sValue(x)); 1670 return (pt instanceof P4 ? (P4) pt : null); 1671 } 1672 typeOf(SV x)1673 static private String typeOf(SV x) { 1674 int tok = (x == null ? T.nada : x.tok); 1675 switch (tok) { 1676 case T.on: 1677 case T.off: 1678 return "boolean"; 1679 case T.bitset: 1680 return (x.value instanceof BondSet ? "bondset" : "bitset"); 1681 case T.integer: 1682 case T.decimal: 1683 case T.point3f: 1684 case T.point4f: 1685 case T.string: 1686 case T.varray: 1687 case T.hash: 1688 case T.barray: 1689 case T.matrix3f: 1690 case T.matrix4f: 1691 case T.context: 1692 return T.astrType[tok]; 1693 } 1694 return "?"; 1695 } 1696 getAllProperties(SV x2, String abbr)1697 private boolean getAllProperties(SV x2, String abbr) 1698 throws ScriptException { 1699 BS bs = (BS) x2.value; 1700 Lst<T> tokens; 1701 int n = bs.cardinality(); 1702 if (n == 0 || !abbr.endsWith("?") 1703 || (tokens = T.getAtomPropertiesLike(abbr.substring(0, abbr 1704 .length() - 1))) == null) 1705 return addXStr(""); 1706 Map<String, Object> ht = new Hashtable<String, Object>(); 1707 int index = (n == 1 ? bs.nextSetBit(0) : Integer.MAX_VALUE); 1708 for (int i = tokens.size(); --i >= 0;) { 1709 T t = tokens.get(i); 1710 int tok = t.tok; 1711 switch (tok) { 1712 case T.configuration: 1713 case T.cell: 1714 continue; 1715 default: 1716 if (index == Integer.MAX_VALUE) 1717 tok |= T.minmaxmask; 1718 ht.put((String) t.value, SV.getVariable( 1719 eval.getBitsetProperty(bs, null, tok, null, null, null, null, false, index, true))); 1720 } 1721 } 1722 return addXMap(ht); 1723 } 1724 getMatrix4f(M3 matRotate, T3 vTranslate)1725 public static M4 getMatrix4f(M3 matRotate, T3 vTranslate) { 1726 return M4.newMV(matRotate, vTranslate == null ? new V3() : V3.newV(vTranslate)); 1727 } 1728 getBoundBox(SV x2)1729 private boolean getBoundBox(SV x2) { 1730 if (x2.tok != T.bitset) 1731 return false; 1732 BoxInfo b = vwr.ms.getBoxInfo((BS) x2.value, 1); 1733 P3[] pts = b.getBoundBoxPoints(true); 1734 Lst<P3> list = new Lst<P3>(); 1735 for (int i = 0; i < 4; i++) 1736 list.addLast(pts[i]); 1737 return addXList(list); 1738 } 1739 getPointOrBitsetOperation(T op, SV x2)1740 private boolean getPointOrBitsetOperation(T op, SV x2) 1741 throws ScriptException { 1742 switch (x2.tok) { 1743 case T.varray: 1744 switch (op.intValue) { 1745 case T.min: 1746 case T.max: 1747 case T.average: 1748 case T.stddev: 1749 case T.sum: 1750 case T.sum2: 1751 case T.pivot: 1752 return addXObj(eval.getMathExt().getMinMax(x2.getList(), op.intValue)); 1753 case T.pop: 1754 return addX(x2.pushPop(null, null)); 1755 case T.sort: 1756 case T.reverse: 1757 return addX(x2 1758 .sortOrReverse(op.intValue == T.reverse ? Integer.MIN_VALUE : 1)); 1759 } 1760 SV[] list2 = new SV[x2.getList().size()]; 1761 for (int i = 0; i < list2.length; i++) { 1762 Object v = SV.unescapePointOrBitsetAsVariable(x2.getList() 1763 .get(i)); 1764 if (!(v instanceof SV) 1765 || !getPointOrBitsetOperation(op, (SV) v)) 1766 return false; 1767 list2[i] = xStack[xPt--]; 1768 } 1769 return addXAV(list2); 1770 case T.point3f: 1771 switch (op.intValue) { 1772 case T.atomx: 1773 case T.x: 1774 return addXFloat(((P3) x2.value).x); 1775 case T.atomy: 1776 case T.y: 1777 return addXFloat(((P3) x2.value).y); 1778 case T.atomz: 1779 case T.z: 1780 return addXFloat(((P3) x2.value).z); 1781 case T.xyz: 1782 P3 pt = P3.newP((P3) x2.value); 1783 // assumes a fractional coordinate 1784 vwr.toCartesian(pt, false); 1785 return addXPt(pt); 1786 case T.fracx: 1787 case T.fracy: 1788 case T.fracz: 1789 case T.fracxyz: 1790 P3 ptf = P3.newP((P3) x2.value); 1791 vwr.toFractional(ptf, false); 1792 return (op.intValue == T.fracxyz ? addXPt(ptf) 1793 : addXFloat(op.intValue == T.fracx ? ptf.x 1794 : op.intValue == T.fracy ? ptf.y : ptf.z)); 1795 case T.fux: 1796 case T.fuy: 1797 case T.fuz: 1798 case T.fuxyz: 1799 P3 ptfu = P3.newP((P3) x2.value); 1800 vwr.toFractional(ptfu, true); 1801 return (op.intValue == T.fuxyz ? addXPt(ptfu) 1802 : addXFloat(op.intValue == T.fux ? ptfu.x 1803 : op.intValue == T.fuy ? ptfu.y : ptfu.z)); 1804 case T.unitx: 1805 case T.unity: 1806 case T.unitz: 1807 case T.unitxyz: 1808 P3 ptu = P3.newP((P3) x2.value); 1809 vwr.toUnitCell(ptu, null); 1810 vwr.toFractional(ptu, false); 1811 return (op.intValue == T.unitxyz ? addXPt(ptu) 1812 : addXFloat(op.intValue == T.unitx ? ptu.x 1813 : op.intValue == T.unity ? ptu.y : ptu.z)); 1814 } 1815 break; 1816 case T.point4f: 1817 switch (op.intValue) { 1818 case T.atomx: 1819 case T.x: 1820 return addXFloat(((P4) x2.value).x); 1821 case T.atomy: 1822 case T.y: 1823 return addXFloat(((P4) x2.value).y); 1824 case T.atomz: 1825 case T.z: 1826 return addXFloat(((P4) x2.value).z); 1827 case T.w: 1828 return addXFloat(((P4) x2.value).w); 1829 } 1830 break; 1831 case T.bitset: 1832 boolean isAtoms = (op.intValue != T.bonds); 1833 if (!isAtoms && x2.value instanceof BondSet) 1834 return addX(x2); 1835 BS bs = (BS) x2.value; 1836 if (isAtoms && bs.cardinality() == 1 && (op.intValue & T.minmaxmask) == 0) 1837 op.intValue |= T.min; 1838 Object val = eval.getBitsetProperty(bs, null, op.intValue, null, 1839 null, null, op.value, false, x2.index, true); 1840 return (isAtoms ? addXObj(val) : addX(SV.newV(T.bitset, BondSet.newBS( 1841 (BS) val, vwr.ms.getAtomIndices(bs))))); 1842 } 1843 return false; 1844 } 1845 1846 1847 } 1848