Main Page Namespace List Class Hierarchy Alphabetical List Compound List File List Namespace Members Compound Members File Members Related Pages

ParseTree.C

Go to the documentation of this file.
00001 /***************************************************************************
00002 *cr 
00003 *cr (C) Copyright 1995-2019 The Board of Trustees of the 
00004 *cr University of Illinois 
00005 *cr All Rights Reserved 
00006 *cr 
00007 ***************************************************************************/
00008 
00009 /***************************************************************************
00010 * RCS INFORMATION:
00011 *
00012 * $RCSfile: ParseTree.C,v $
00013 * $Author: johns $ $Locker: $ $State: Exp $
00014 * $Revision: 1.154 $ $Date: 2024年03月01日 02:09:20 $
00015 *
00016 ***************************************************************************
00017 * DESCRIPTION:
00018 * Given the parse tree created by a SymbolTable, evaluate it and return
00019 * the selection
00020 *
00021 ***************************************************************************/
00022 
00023 #define NOMINMAX 1 // prevent MSVS from defining min/max macros
00024 
00025 #include <stdio.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <math.h> // for pow, fabs
00029 
00030 #include "AtomParser.h" // for atomparser_node definition
00031 #include "y.tab.h" // for FLOATVAL, INTVAL, and STRWORD
00032 #include "ParseTree.h"
00033 #include "Inform.h" // for printing error messages
00034 #include "JRegex.h" // for regular expression matching
00035 #include "AtomSel.h" // for atomsel_ctxt definition
00036 #include "Timestep.h" // for accessing coordinate data
00037 #include "DrawMolecule.h" // for drawmolecule multiple-frame selections
00038 #include "SpatialSearch.h" // for find_within()
00039 
00040 #include <vector> // for knearest implementation
00041 #include <algorithm> // for knearest implementation
00042 // #include <limits> // for knearest implementation
00043 
00044 #include "VMDApp.h" // CPU caps
00045 
00046 // do the string and numeric compares
00047 #define case_compare_numeric_macro(switchcase, symbol) \
00048 case switchcase: \
00049 l->convert(SymbolTableElement::IS_FLOAT); \
00050 r->convert(SymbolTableElement::IS_FLOAT); \
00051 ldval = l->dval; \
00052 rdval = r->dval; \
00053 flg = flgs; \
00054 for (i=num-1; i>=0; i--) { \
00055 *flg &= (*ldval symbol *rdval); \
00056 ldval += lincr; rdval += rincr; flg++; \
00057 } \
00058 break;
00059 
00060 #define case_compare_string_macro(switchcase, symbol) \
00061 case switchcase: \
00062 l->convert(SymbolTableElement::IS_STRING); \
00063 r->convert(SymbolTableElement::IS_STRING); \
00064 lsptr = l->sval; \
00065 rsptr = r->sval; \
00066 flg = flgs; \
00067 for (i=num-1; i>=0; i--) { \
00068 if (*flg) \
00069 *flg &= (strcmp(*lsptr, *rsptr) symbol 0); \
00070 lsptr += lincr; rsptr += rincr; flg++; \
00071 } \
00072 break;
00073 
00074 
00076 ParseTree::ParseTree(VMDApp *vmdapp, SymbolTable *parser, atomparser_node *parse_tree)
00077 {
00078 app = vmdapp;
00079 tree = parse_tree;
00080 table = parser;
00081 selected_array = NULL;
00082 num_selected = 0;
00083 context = NULL;
00084 }
00085 
00086 ParseTree::~ParseTree(void) {
00087 if (selected_array != NULL) 
00088 delete [] selected_array;
00089 delete tree;
00090 }
00091 
00092 void ParseTree::eval_compare(atomparser_node *node, int num, int *flgs) {
00093 int i;
00094 double *ldval, *rdval;
00095 char **lsptr, **rsptr;
00096 int lincr, rincr;
00097 int *flg;
00098 
00099 // get the data on the left and right
00100 symbol_data *l = eval(node->left, num, flgs);
00101 symbol_data *r = eval(node->right, num, flgs);
00102 
00103 // If the symbol data contains num elements, we need to check each one.
00104 // Otherwise, it contains exactly one element and we can just keep
00105 // reusing it. 
00106 lincr = l->num == num ? 1 : 0;
00107 rincr = r->num == num ? 1 : 0;
00108 
00109 switch (node->ival) {
00110 case_compare_numeric_macro(NLT, < )
00111 case_compare_numeric_macro(NLE, <= )
00112 case_compare_numeric_macro(NEQ, == )
00113 case_compare_numeric_macro(NGE, >= )
00114 case_compare_numeric_macro(NGT, > )
00115 case_compare_numeric_macro(NNE, != )
00116 
00117 case_compare_string_macro(SLT, < )
00118 case_compare_string_macro(SLE, <= )
00119 case_compare_string_macro(SEQ, == )
00120 case_compare_string_macro(SGE, >= )
00121 case_compare_string_macro(SGT, > )
00122 case_compare_string_macro(SNE, != )
00123 
00124 case MATCH: {
00125 l->convert(SymbolTableElement::IS_STRING);
00126 r->convert(SymbolTableElement::IS_STRING);
00127 lsptr = l->sval;
00128 rsptr = r->sval;
00129 flg = flgs;
00130 JRegex *rgx = NULL;
00131 const char *first = *rsptr;
00132 
00133 for (i=0; i<num; i++) {
00134 if (i==0 || strcmp(*rsptr, first)) {
00135 if (rgx) 
00136 delete rgx;
00137 rgx = new JRegex(*rsptr);
00138 first = *rsptr;
00139 }
00140 if (rgx) {
00141 if (*flg)
00142 *flg &= (rgx->match(*lsptr, strlen(*lsptr)) != -1);
00143 } else {
00144 *flg = 0;
00145 }
00146 lsptr += lincr; rsptr += rincr; flg++;
00147 }
00148 if (rgx) {
00149 delete rgx;
00150 }
00151 // done with match search
00152 break;
00153 }
00154 
00155 default:
00156 msgWarn << "ParseTree::eval_compare() missing operator!" << sendmsg;
00157 }
00158 
00159 delete l;
00160 delete r;
00161 }
00162 
00163 
00164 // place to do +, -, *, and /
00165 symbol_data * ParseTree::eval_mathop(atomparser_node *node, int num, int *flgs)
00166 {
00167 symbol_data *l = eval(node->left, num, flgs);
00168 symbol_data *r = eval(node->right, num, flgs);
00169 // since we can only have 1 or num elements, we'll either be using the
00170 // incrementing index value, or we'll only be using dval[0], so we set
00171 // the lincr/rincr value to 0 or all 1's and do binary AND against it.
00172 int lincr = l->num == num ? (~0) : 0;
00173 int rincr = r->num == num ? (~0) : 0;
00174 l->convert(SymbolTableElement::IS_FLOAT);
00175 r->convert(SymbolTableElement::IS_FLOAT);
00176 symbol_data *tmp = new symbol_data(SymbolTableElement::IS_FLOAT, num);
00177 int i;
00178 const double *lval = l->dval;
00179 const double *rval = r->dval;
00180 double *tmpval = tmp->dval;
00181 
00182 // XXX does it really pay to have tests on the flgs[] array
00183 // in loops that just do addition/subtraction? If the resulting
00184 // values are never referenced, we might get better performance 
00185 // by doing the math regardless, for these simple cases. For fmod()
00186 // it is definitely beneficial to performance to test before calling...
00187 int firstsel = 0;
00188 int lastsel = -1;
00189 if (!analyze_selection_aligned_dispatch(app->cpucaps, num, flgs, &firstsel, &lastsel, NULL)) {
00190 // XXX we should trim the loop ranges to the last selection prior to
00191 // entering the switch cases...
00192 switch (node->node_type) {
00193 case ADD:
00194 for (i=firstsel; i<=lastsel; i++) {
00195 if (flgs[i]) tmpval[i] = lval[lincr & i] + rval[rincr & i];
00196 }
00197 break;
00198 case SUB:
00199 for (i=firstsel; i<=lastsel; i++) {
00200 if (flgs[i]) tmpval[i] = lval[lincr & i] - rval[rincr & i];
00201 }
00202 break;
00203 case MULT:
00204 for (i=firstsel; i<=lastsel; i++) {
00205 if (flgs[i]) tmpval[i] = lval[lincr & i] * rval[rincr & i];
00206 }
00207 break;
00208 case DIV:
00209 for (i=firstsel; i<=lastsel; i++) {
00210 if (flgs[i]) tmpval[i] = lval[lincr & i] / rval[rincr & i];
00211 }
00212 break;
00213 case MOD: // fake mod
00214 for (i=firstsel; i<=lastsel; i++) {
00215 if (flgs[i]) tmpval[i] = fmod(lval[lincr & i], rval[rincr & i]);
00216 }
00217 break;
00218 case EXP:
00219 for (i=firstsel; i<=lastsel; i++) {
00220 if (flgs[i]) tmpval[i] = pow(lval[lincr & i], rval[rincr & i]);
00221 }
00222 break;
00223 }
00224 }
00225 
00226 delete l;
00227 delete r;
00228 return tmp;
00229 }
00230 
00231 
00232 // This puts the task of doing the selection inside the function
00233 // For example: sequence APW "T.*A"
00234 // The function converts the linked list into an array of const char *
00235 // and of fields, 0 == raw, 1 == single quote, 2 == double quote
00236 // if this is the start of a "to" then the fields are
00237 // and of fields, 3 == raw, 4 == single quote, 5 == double quote
00238 // the function modifies the selection as it pleases, 
00239 // so it _can_ override the current flags. Please be careful.
00240 void ParseTree::eval_stringfctn(atomparser_node *node, int num, int *flgs) {
00241 int count = 0;
00242 atomparser_node *left;
00243 
00244 // first count the number of elements in the linked list
00245 for (left = node->left; left != NULL; left = left->left) {
00246 count++;
00247 }
00248 if (count == 0) 
00249 return;
00250 
00251 // now populate the char ** pointers
00252 char **argv= (char **) calloc(1, count * sizeof(char *));
00253 int *types = new int[count];
00254 int i=0;
00255 for (left = node->left; left != NULL; left = left -> left, i++) {
00256 // get the string type (single quote, double quote, raw)
00257 switch (left->sele.st) {
00258 case RAW_STRING:
00259 types[i] = 0;
00260 argv[i] = (char *) ((const char *) left->sele.s);
00261 break;
00262 
00263 case SQ_STRING:
00264 types[i] = 1;
00265 argv[i] = (char *) ((const char *) left->sele.s);
00266 break;
00267 
00268 case DQ_STRING: 
00269 types[i] = 2;
00270 argv[i] = (char *) ((const char *) left->sele.s);
00271 break;
00272 }
00273 
00274 if (left->extra_type != -1) { // then it is a "through" search
00275 types[i] += 3;
00276 }
00277 }
00278 
00279 // Call the function. Functions can override flags, so they are copied first
00280 int *tmp_flgs = new int[num];
00281 memcpy(tmp_flgs, flgs, num * sizeof(int));
00282 SymbolTableElement *elem = table->fctns.data(node->extra_type);
00283 elem->keyword_stringfctn(context, count, (const char **)argv, types, num, tmp_flgs);
00284 
00285 // XXX candidate for a nice SSE loop
00286 for (i = num-1; i>=0; i--) {
00287 if (flgs[i]) flgs[i] = tmp_flgs[i];
00288 }
00289 delete [] tmp_flgs;
00290 delete [] types;
00291 free(argv);
00292 }
00293 
00294 static void same_string(symbol_data *tmp, symbol_data *tmp2, int num, 
00295 int *subselect, int *flgs) {
00296 hash_t hash;
00297 hash_init(&hash, num);
00298 
00299 // Hash all entries in the sublist
00300 int i;
00301 for (i=0; i<num; i++)
00302 if (subselect[i])
00303 hash_insert(&hash, tmp2->sval[i], 0);
00304 
00305 // Turn on flgs only if it's already on and its value is in the table.
00306 // Note: We cannot access string data for items that aren't on.
00307 // This is also much faster than calling hash_lookup() unnecessarily.
00308 for (i=0; i<num; i++) 
00309 if (flgs[i])
00310 flgs[i] = (hash_lookup(&hash, tmp->sval[i]) != HASH_FAIL);
00311 
00312 hash_destroy(&hash);
00313 }
00314 
00315 static void same_int(VMDApp *app, symbol_data *tmp, symbol_data *tmp2, int num, 
00316 int *subselect, int *flgs) {
00317 int firstsubsel = -1, lastsubsel = -1;
00318 
00319 if (analyze_selection_aligned_dispatch(app->cpucaps, num, subselect, &firstsubsel, &lastsubsel, NULL)) {
00320 // subselection is empty, so set all flags to zero.
00321 memset(flgs, 0, num*sizeof(int));
00322 return;
00323 }
00324 
00325 // Create a table of values found in subselect
00326 // XXX This could get to be very large, and therefore slow.
00327 // We should consider changing this to a hash table implementation
00328 // so that we don't soak up massive amounts of memory for cases where
00329 // we have an extremely sparse array of values
00330 int *int_table = NULL, int_min, int_max;
00331 
00332 // XXX Could use a minmax_1iv_aligned() SSE vectorized helper routine here...
00333 int_min = int_max = tmp2->ival[firstsubsel];
00334 int i;
00335 for (i=firstsubsel; i<=lastsubsel; i++) {
00336 if (subselect[i]) {
00337 int ival = tmp2->ival[i];
00338 if (ival > int_max)
00339 int_max = ival; 
00340 if (ival < int_min)
00341 int_min = ival; 
00342 }
00343 }
00344 
00345 int_table = (int *) calloc(1+int_max-int_min, sizeof(int)); 
00346 for (i=firstsubsel; i<=lastsubsel; i++) {
00347 if (subselect[i]) {
00348 int_table[tmp2->ival[i]-int_min] = 1;
00349 }
00350 }
00351 
00352 // Turn on flgs only if it's already on and its value is in the table.
00353 for (i=0; i<num; i++) {
00354 if (flgs[i]) {
00355 int ival = tmp->ival[i];
00356 if (ival >= int_min && ival <= int_max)
00357 flgs[i] = int_table[ival-int_min];
00358 else
00359 flgs[i] = 0;
00360 }
00361 }
00362 free(int_table);
00363 }
00364 
00365 static void same_double(VMDApp *app, symbol_data *tmp, symbol_data *tmp2, 
00366 int num, int *subselect, int *flgs) {
00367 int firstsubsel, lastsubsel, subselselected;
00368 if (analyze_selection_aligned_dispatch(app->cpucaps, num, subselect,
00369 &firstsubsel, &lastsubsel, &subselselected)) {
00370 return;
00371 }
00372 
00373 // Hash all the entries in the sublist, then check each flag against the
00374 // table. I have to convert doubles to strings.
00375 hash_t hash;
00376 // XXX doubles can't be longer than 25 chars, can they?
00377 char *doublestring = new char[25L*subselselected];
00378 char *istring = doublestring;
00379 hash_init(&hash, subselselected);
00380 int i;
00381 for (i=firstsubsel; i<=lastsubsel; i++) {
00382 if (subselect[i]) {
00383 sprintf(istring,"%f", (double) tmp2->dval[i]); 
00384 hash_insert(&hash, istring, 0);
00385 istring += 25;
00386 }
00387 }
00388 
00389 char tmpstring[25];
00390 for (i=0; i<num; i++) {
00391 sprintf(tmpstring,"%f", (double) tmp->dval[i]); 
00392 flgs[i] &= (hash_lookup(&hash, tmpstring) != HASH_FAIL);
00393 }
00394 hash_destroy(&hash);
00395 
00396 delete [] doublestring;
00397 }
00398 
00399 
00400 // this does things like: same resname as name CA 
00401 // 1) evalute the expression (m atoms)
00402 // 2) get the keyword information (n atoms)
00403 // 3) do an n*m search for the 'same' values
00404 void ParseTree::eval_same(atomparser_node *node, int num, int *flgs) {
00405 int i;
00406 int *subselect = new int[num];
00407 for (i=0; i<num; i++)
00408 subselect[i]=1; 
00409 
00410 // 1) evaluate the sub-selection
00411 if (eval(node->left, num, subselect)) {
00412 delete [] subselect;
00413 msgErr << "eval of a 'same' returned data when it shouldn't have" 
00414 << sendmsg;
00415 return;
00416 }
00417 
00418 // at this point, only the sub selection is defined
00419 // 2) get the keyword information
00420 // 2a) make space for the return type
00421 SymbolTableElement *elem = table->fctns.data(node->extra_type);
00422 SymbolTableElement::symtype has_type = elem->returns_a;
00423 symbol_data *tmp, *tmp2;
00424 tmp = new symbol_data(has_type, num);
00425 tmp2 = new symbol_data(has_type, num);
00426 
00427 // 2b) get the data (masked by the info passed by flgs)
00428 // and find the 'same' value
00429 switch (has_type) {
00430 case SymbolTableElement::IS_INT: 
00431 elem->keyword_int(context, num, tmp->ival, flgs);
00432 elem->keyword_int(context, num, tmp2->ival, subselect);
00433 same_int(app, tmp, tmp2, num, subselect, flgs);
00434 break;
00435 
00436 case SymbolTableElement::IS_FLOAT: 
00437 elem->keyword_double(context, num, tmp->dval, flgs);
00438 elem->keyword_double(context, num, tmp2->dval, subselect);
00439 same_double(app, tmp, tmp2, num, subselect, flgs);
00440 break;
00441 
00442 case SymbolTableElement::IS_STRING:
00443 elem->keyword_string(context, num, (const char **)tmp->sval, flgs);
00444 elem->keyword_string(context, num, (const char **)tmp2->sval, subselect);
00445 same_string(tmp, tmp2, num, subselect, flgs); 
00446 break;
00447 }
00448 
00449 delete tmp;
00450 delete tmp2;
00451 delete [] subselect;
00452 }
00453 
00454 
00455 // here's where I get things like: name CA N C O
00456 // and: mass
00457 symbol_data *ParseTree::eval_key(atomparser_node *node, int num, int *flgs) {
00458 // make space for the return type
00459 SymbolTableElement *elem = table->fctns.data(node->extra_type);
00460 SymbolTableElement::symtype has_type = elem->returns_a;
00461 symbol_data *tmp;
00462 tmp = new symbol_data(has_type, num);
00463 
00464 switch (has_type) {
00465 case SymbolTableElement::IS_INT:
00466 elem->keyword_int(context, num, tmp->ival, flgs);
00467 break;
00468 case SymbolTableElement::IS_FLOAT:
00469 elem->keyword_double(context, num, tmp->dval, flgs);
00470 break;
00471 case SymbolTableElement::IS_STRING:
00472 elem->keyword_string(context, num, (const char **)tmp->sval, flgs);
00473 break;
00474 }
00475 
00476 // If we're doing int's, set up a table to store all the values we find
00477 // in the list of values. 
00478 int *int_table = NULL;
00479 
00480 // XXX it should be possible for us to move the first/last selection
00481 // evaluation prior to the elem->keyword_xxx() calls so that we can
00482 // accelerate the inner loops within those operations too...
00483 int firstsel = 0, lastsel = -1;
00484 if (analyze_selection_aligned_dispatch(app->cpucaps, num, flgs, &firstsel, &lastsel, NULL)) {
00485 firstsel=0; // loops that test i<=lastsel will early-exit as they should
00486 }
00487 
00488 int int_min=0, int_max=0; 
00489 if (has_type == SymbolTableElement::IS_INT) {
00490 if (lastsel != -1) {
00491 int_min = int_max = tmp->ival[firstsel];
00492 } 
00493 // XXX what do we do if there was no selection?
00494 
00495 // find min/max values
00496 // XXX Could use a minmax_1iv_aligned() SSE vectorized helper routine here...
00497 for (int i=firstsel; i<=lastsel; i++) {
00498 if (flgs[i]) {
00499 const int ival = tmp->ival[i];
00500 if (ival > int_max)
00501 int_max = ival; 
00502 if (ival < int_min)
00503 int_min = ival; 
00504 }
00505 }
00506 int_table = (int *) calloc(1+int_max-int_min, sizeof(int)); 
00507 }
00508 
00509 // Now that I have the data, I can do one of two things
00510 // Either it is a list, in which case there is data off the
00511 // left, or it returns the data itself
00512 
00513 // if there is a list coming off the left, then I have
00514 // name CA N ===> (name='CA' and name='N')
00515 // chain 1 to 3 ===> (name>='1' and name<='3'
00516 
00517 // XXX call calloc() instead, and avoid extra clear operation
00518 int *newflgs = new int[num];
00519 // have to do this since selection parameters are 'OR'ed together
00520 memset(newflgs, 0, num*sizeof(int));
00521 
00522 if (node->left) {
00523 atomparser_node *left = node->left;
00524 while (left) {
00525 if (left->extra_type == -1) { 
00526 // then it is normal
00527 switch(has_type) {
00528 case SymbolTableElement::IS_INT:
00529 {
00530 int ival = atoi(left->sele.s);
00531 if (ival >= int_min && ival <= int_max) 
00532 int_table[ival-int_min] = 1;
00533 }
00534 break;
00535 
00536 case SymbolTableElement::IS_FLOAT:
00537 {
00538 // select atoms that are within .1% of dval
00539 double dval = atof(left->sele.s);
00540 double delta = fabs(dval / 1000);
00541 double maxval = dval+delta;
00542 double minval = dval-delta;
00543 for (int i=firstsel; i<=lastsel; i++) {
00544 if (flgs[i]) 
00545 newflgs[i] |= (minval <= tmp->dval[i] && maxval >= tmp->dval[i]);
00546 }
00547 }
00548 break;
00549 
00550 case SymbolTableElement::IS_STRING:
00551 {
00552 switch (left->sele.st) {
00553 case SQ_STRING: // doing string as single quotes
00554 case RAW_STRING:
00555 {
00556 for (int i=firstsel; i<=lastsel; i++) {
00557 // XXX we get NULL tmp->sval[i] when only coords
00558 // are loaded, without any structure/names, so
00559 // checking this prevents crashes
00560 // Short-circuiting the OR when newflgs is already set
00561 // speeds up selections involving several criteria by
00562 // avoiding needless further tests when already selected.
00563 #if 1
00564 if (flgs[i] && !newflgs[i] && (tmp->sval[i] != NULL)) {
00565 newflgs[i] = !strcmp(left->sele.s, tmp->sval[i]);
00566 #else
00567 if (flgs[i] && !newflgs[i] && (tmp->sval[i] != NULL)) {
00568 newflgs[i] |= !strcmp(left->sele.s, tmp->sval[i]);
00569 #endif
00570 }
00571 }
00572 }
00573 break;
00574 
00575 case DQ_STRING:
00576 default:
00577 {
00578 // A regex like "H" would match 'H', 'H21',
00579 // 'OH2', etc. I force the match to be
00580 // complete with the ^ and $. The parenthesis \(\)
00581 // are to avoid turning C\|O into ^C\|O$
00582 // and the double \ is to escape the string escape
00583 // mechanism. Ain't this grand?
00584 // Short-circuiting the OR when newflgs is already set
00585 // speeds up selections involving several criteria by
00586 // avoiding needless further tests when already selected.
00587 JString temps = "^("+left->sele.s+")$";
00588 JRegex r(temps, 1); // 1 for fast compile
00589 for (int i=firstsel; i<=lastsel; i++) {
00590 if (flgs[i] && !newflgs[i]) {
00591 newflgs[i] |= (r.match(tmp->sval[i], strlen(tmp->sval[i])) != -1);
00592 }
00593 } // end loop
00594 } // end check for DQ_STRING
00595 break;
00596 } // end based on string type
00597 } // end of IS_STRING
00598 } // end switch based on keyword type
00599 } else { // do a 'through' search
00600 switch(has_type) {
00601 case SymbolTableElement::IS_INT:
00602 {
00603 int ltval = atoi(left->sele.s);
00604 int gtval = atoi(left->left->sele.s);
00605 if (ltval < int_min) ltval = int_min;
00606 if (gtval > int_max) gtval = int_max;
00607 for (int i=ltval-int_min; i<= gtval-int_min; i++)
00608 int_table[i] = 1;
00609 }
00610 break;
00611 case SymbolTableElement::IS_FLOAT:
00612 {
00613 double ltval = atof(left->sele.s);
00614 double gtval = atof(left->left->sele.s);
00615 for (int i=firstsel; i<=lastsel; i++) {
00616 if (flgs[i])
00617 newflgs[i] |= ((ltval <= tmp->dval[i]) && (gtval >= tmp->dval[i]));
00618 }
00619 }
00620 break;
00621 default:
00622 {
00623 // no way to do regex with < or >, so do exact
00624 for (int i=firstsel; i<=lastsel; i++) {
00625 if (flgs[i]) 
00626 newflgs[i] |= (flgs[i] && strcmp(left->sele.s, tmp->sval[i]) <= 0
00627 && strcmp(left->left->sele.s, tmp->sval[i]) >= 0);
00628 
00629 }
00630 }
00631 } // end switch checking type
00632 left = left->left; // need to bypass that 2nd one
00633 } // end both possible ways
00634 left = left->left;
00635 } // end while loop going down the left side
00636 
00637 // get the flgs info back together
00638 if (has_type == SymbolTableElement::IS_INT) {
00639 for (int i=firstsel; i<=lastsel; i++) {
00640 if (flgs[i])
00641 flgs[i] = int_table[tmp->ival[i]-int_min]; 
00642 }
00643 free(int_table);
00644 } else {
00645 for (int i=firstsel; i<=lastsel; i++) {
00646 if (flgs[i]) 
00647 flgs[i] = newflgs[i];
00648 }
00649 }
00650 
00651 // first and last selections are now invalidated by the merge op
00652 firstsel=0;
00653 lastsel=num-1;
00654 delete [] newflgs;
00655 delete tmp;
00656 return NULL;
00657 } else {
00658 // if there isn't a list, then I have something like
00659 // mass + 5 < 7
00660 // so just return the data
00661 delete [] newflgs;
00662 if (int_table) free(int_table);
00663 return tmp;
00664 }
00665 }
00666 
00667 
00668 void ParseTree::eval_single(atomparser_node *node, int num, int *flgs) {
00669 // XXX Cast to atomsel_ctxt since we _know_ that only atom selections
00670 // use singlewords.
00671 atomsel_ctxt *ctxt = (atomsel_ctxt *)context;
00672 ctxt->singleword = table->fctns.name(node->extra_type);
00673 table->fctns.data(node->extra_type)->keyword_single(context, num, flgs);
00674 }
00675 
00676 
00677 void ParseTree::eval_exwithin(atomparser_node *node, int num, int *flgs) {
00678 eval_within(node, num, flgs);
00679 
00680 // add "and not others"
00681 int *others = new int[num];
00682 int i;
00683 for (i=0; i<num; others[i++] = 1);
00684 
00685 // XXX evaluates node->left twice
00686 if (eval(node->left, num, others)) {
00687 delete [] others;
00688 msgErr << "eval of a 'within' returned data when it shouldn't have." << sendmsg;
00689 return;
00690 }
00691 for (i=0; i<num; i++) {
00692 if (others[i]) flgs[i] = 0;
00693 }
00694 delete [] others;
00695 }
00696 
00697 
00698 // XXX copied from AtomSel
00699 static Timestep *selframe(DrawMolecule *atom_sel_mol, int which_frame) {
00700 switch (which_frame) {
00701 case AtomSel::TS_LAST: return atom_sel_mol->get_last_frame(); 
00702 case AtomSel::TS_NOW : return atom_sel_mol->current(); 
00703 default: {
00704 if (!atom_sel_mol->get_frame(which_frame)) {
00705 return atom_sel_mol->get_last_frame();
00706 
00707 } else {
00708 return atom_sel_mol->get_frame(which_frame);
00709 }
00710 }
00711 }
00712 return NULL;
00713 }
00714 
00715 void ParseTree::eval_pbwithin(atomparser_node *node, int num, int *flgs) {
00716 // for a zero valued distance, just return the "others" part
00717 // with no additional atoms selected.
00718 if ((float) node->dval <= 0.0f) {
00719 eval(node->left, num, flgs);
00720 return; // early exit
00721 }
00722 
00723 //
00724 // if we have a non-zero distance criteria, do the computation
00725 //
00726 int i;
00727 
00728 // coords holds original coordinates in first 3N entries
00729 ResizeArray<float> coords(3L*2L*num);
00730 
00731 // others holds the flags for others in the first N entries, and will
00732 // be padded with zeros, one for each replicated flg atom
00733 ResizeArray<int> others(2L*num);
00734 
00735 // repflgs holds a copy of flgs in the first N entries, and will be
00736 // extended with ones for each replicated flag atom. We store the index
00737 // of the replicated atom in repindexes.
00738 ResizeArray<int> repflgs(2L*num);
00739 
00740 // repindexes holds the indexes of the replicated flg atoms.
00741 ResizeArray<int> repindexes(num);
00742 
00743 // fetch coordinates
00744 atomsel_ctxt *ctxt = (atomsel_ctxt *)context;
00745 const Timestep *ts = selframe(ctxt->atom_sel_mol, ctxt->which_frame);
00746 if (!ts) {
00747 msgErr << "No timestep available for 'within' search!" << sendmsg;
00748 return;
00749 }
00750 others.appendN(1, num);
00751 if (eval(node->left, num, &others[0])) {
00752 msgErr << "eval of a 'within' returned data when it shouldn't have." << sendmsg;
00753 return;
00754 }
00755 
00756 // fill in start of coords and repflgs.
00757 const float * pos=ts->pos;
00758 for (i=0; i<num; i++) {
00759 coords.append3(pos);
00760 pos += 3;
00761 repflgs.append(flgs[i]);
00762 }
00763 
00764 // find bounding box on others
00765 float min[3], max[3];
00766 if (!find_minmax_selected(num, &others[0], ts->pos, 
00767 min[0], min[1], min[2], max[0], max[1], max[2])) {
00768 memset(flgs, 0, num*sizeof(int));
00769 return;
00770 }
00771 
00772 // extend bounding box by the cutoff distance.
00773 const float cutoff = (float)node->dval;
00774 for (i=0; i<3; i++) {
00775 min[i] -= cutoff;
00776 max[i] += cutoff;
00777 }
00778 
00779 // replicate flgs atoms as needed. 
00780 float A[3], B[3], C[3];
00781 ts->get_transform_vectors(A, B, C);
00782 for (i=-1; i<=1; i++) {
00783 float v1[3];
00784 vec_scale(v1, (float) i, A);
00785 for (int j=-1; j<=1; j++) {
00786 float v2[3];
00787 vec_scale(v2, (float) j, B);
00788 for (int k=-1; k<=1; k++) {
00789 // don't replicate the home cell
00790 if (!i && !j && !k) continue;
00791 float v3[3];
00792 vec_scale(v3, (float) k, C);
00793 float vx = v1[0] + v2[0] + v3[0];
00794 float vy = v1[1] + v2[1] + v3[1];
00795 float vz = v1[2] + v2[2] + v3[2];
00796 pos = ts->pos;
00797 for (int ind=0; ind<num; ind++) {
00798 if (flgs[ind]) {
00799 const float x = pos[0] + vx;
00800 const float y = pos[1] + vy;
00801 const float z = pos[2] + vz;
00802 if (x>min[0] && x<=max[0] &&
00803 y>min[1] && y<=max[1] &&
00804 z>min[2] && z<=max[2]) {
00805 repindexes.append(ind);
00806 coords.append3(x, y, z);
00807 repflgs.append(1);
00808 others.append(0);
00809 }
00810 }
00811 pos += 3;
00812 }
00813 }
00814 }
00815 }
00816 
00817 find_within(&coords[0], &repflgs[0], &others[0], others.num(), cutoff);
00818 
00819 // copy the flags for the unreplicated coordinates into the final result
00820 memcpy(flgs, &repflgs[0], num*sizeof(int));
00821 
00822 // OR the replicated atom flags into the unreplicated set.
00823 for (i=0; i<repindexes.num(); i++) {
00824 if (repflgs[num+i]) {
00825 flgs[repindexes[i]] = 1;
00826 }
00827 }
00828 }
00829 
00830 
00831 void ParseTree::eval_within(atomparser_node *node, int num, int *flgs) {
00832 // if we have a non-zero distance criteria, do the computation
00833 if ((float) node->dval > 0.0f) {
00834 // find the atoms in the rest of the selection
00835 int *others = new int[num];
00836 int i;
00837 for (i=0; i<num; ++i) 
00838 others[i] = 1;
00839 
00840 if (eval(node->left, num, others)) {
00841 delete [] others;
00842 msgErr << "eval of a 'within' returned data when it shouldn't have." << sendmsg;
00843 return;
00844 }
00845 
00846 // get the coordinates directly from the molecule.
00847 atomsel_ctxt *ctxt = (atomsel_ctxt *)context;
00848 Timestep *ts = selframe(ctxt->atom_sel_mol, ctxt->which_frame);
00849 if (!ts) {
00850 msgErr << "No timestep available for 'within' search!" << sendmsg;
00851 return;
00852 }
00853 
00854 find_within(ts->pos, flgs, others, num, (float) node->dval);
00855 
00856 delete [] others;
00857 } else {
00858 // for a zero valued distance, just return the "others" part
00859 // with no additional atoms selected.
00860 eval(node->left, num, flgs);
00861 }
00862 }
00863 
00864 
00865 void ParseTree::eval_within_bonds(atomparser_node *node, int num, int *flgs) {
00866 atomsel_ctxt *ctxt = (atomsel_ctxt *)context;
00867 int *others = new int[num];
00868 
00869 int i;
00870 for (i=0; i<num; ++i) 
00871 others[i] = 1;
00872 
00873 if (eval(node->left, num, others)) {
00874 delete [] others;
00875 msgErr << "eval of a 'within' returned data when it shouldn't have." << sendmsg;
00876 return;
00877 }
00878 
00879 // copy others to bondedsel
00880 int *bondedsel = new int[num];
00881 memcpy(bondedsel, others, num*sizeof(int));
00882 
00883 // grow selection by traversing N bonds...
00884 int bonddepth;
00885 for (bonddepth=0; bonddepth<node->ival; bonddepth++) {
00886 // if an atom is selected, select all of its bond partners
00887 for (i=0; i<num; i++) {
00888 if (bondedsel[i]) {
00889 MolAtom *atom = ctxt->atom_sel_mol->atom(i);
00890 int j;
00891 for (j=0; j<atom->bonds; j++) {
00892 others[atom->bondTo[j]] = 1;
00893 }
00894 } 
00895 } 
00896 
00897 // copy others to bondedsel 
00898 memcpy(bondedsel, others, num*sizeof(int));
00899 } 
00900 
00901 for (i=0; i<num; ++i) 
00902 if (bondedsel[i] && flgs[i]) 
00903 flgs[i] = 1;
00904 else
00905 flgs[i] = 0;
00906 
00907 delete [] bondedsel;
00908 delete [] others;
00909 }
00910 
00911 
00912 //
00913 // Find K atoms nearest to a selection
00914 //
00915 // XXX This uses the brute force approach rather than building a K-d tree.
00916 // This should be fine for small selections, but will not do very well 
00917 // on large structures. In reality, the small selections are the common
00918 // case so this is a low priority item for now.
00919 // 
00920 namespace {
00921 struct PointDistance {
00922 float o;
00923 int i;
00924 PointDistance() {}
00925 PointDistance(float o_, int i_) : o(o_), i(i_) {}
00926 bool operator<(const PointDistance& p) const {
00927 return o<p.o;
00928 }
00929 };
00930 }
00931 
00932 void ParseTree::eval_k_nearest(atomparser_node *node, int num, int *flgs) {
00933 atomsel_ctxt *ctxt = (atomsel_ctxt *)context;
00934 const Timestep *ts = selframe(ctxt->atom_sel_mol, ctxt->which_frame);
00935 if (!ts) {
00936 msgErr << "No timestep available for 'nearest' search!" << sendmsg;
00937 return;
00938 }
00939 const int N = node->ival;
00940 int i;
00941 
00942 /* evaluate subselection */
00943 std::vector<int> others(num);
00944 for (i=0; i<num; ++i) 
00945 others[i] = 1;
00946 
00947 if (eval(node->left, num, &others[0])) {
00948 msgErr << "eval of a 'within' returned data when it shouldn't have." << sendmsg;
00949 return;
00950 }
00951 
00952 /* make sure we have something in other */
00953 for (i=0; i<num; i++) {
00954 if (others[i]) 
00955 break;
00956 }
00957 if (i==num) {
00958 memset(flgs, 0, num*sizeof(*flgs));
00959 return;
00960 }
00961 
00962 std::vector<PointDistance> distances;
00963 int numdists=0;
00964 for (i=0; i<num; i++) {
00965 if (others[i] || !flgs[i]) 
00966 continue;
00967 #if 1
00968 float d2=1e37f;
00969 #else
00970 float d2=std::numeric_limits<float>::max();
00971 #endif
00972 for (int j=0; j<num; j++) {
00973 if (!others[j]) 
00974 continue;
00975 
00976 float d2_j=distance2(ts->pos+3L*i, ts->pos+3L*j);
00977 if (d2_j<d2) 
00978 d2=d2_j;
00979 }
00980 
00981 distances.push_back(PointDistance(d2,i));
00982 numdists++;
00983 }
00984 
00985 std::sort(distances.begin(), distances.end());
00986 int n=N;
00987 
00988 // XXX avoid signed vs. unsigned comparisons
00989 // if (n>distances.size()) n=distances.size();
00990 if (n>numdists) 
00991 n=numdists;
00992 memset(flgs, 0, num*sizeof(*flgs));
00993 for (i=0; i<n; i++) {
00994 flgs[distances[i].i]=1;
00995 }
00996 }
00997 
00998 
00999 //
01000 // Find ring structures
01001 //
01002 void ParseTree::find_rings(int num, int *flgs, int *others, 
01003 int minringsize, int maxringsize) {
01004 #ifdef VMDWITHCARBS
01005 int i;
01006 
01007 // XXX We're hijacking the ring list in BaseMolecule at present.
01008 // It might be better to build our own independent one, but
01009 // this way there's only one ring list in memory at a time.
01010 atomsel_ctxt *ctxt = (atomsel_ctxt *)context;
01011 ctxt->atom_sel_mol->find_small_rings_and_links(5, maxringsize);
01012 SmallRing *ring;
01013 memset(flgs, 0, num*sizeof(int));
01014 for (i=0; i < ctxt->atom_sel_mol->smallringList.num(); i++) {
01015 ring = ctxt->atom_sel_mol->smallringList[i];
01016 int N = ring->num();
01017 if (N >= minringsize && N <= maxringsize) {
01018 int j;
01019 for (j=0; j<N; j++) {
01020 int ind = (*ring)[j];
01021 flgs[ind] = others[ind];
01022 } 
01023 } 
01024 }
01025 #else
01026 memset(flgs, 0, num*sizeof(int));
01027 #endif 
01028 }
01029 
01030 
01031 void ParseTree::eval_maxringsize(atomparser_node *node, int num, int *flgs) {
01032 // find the atoms in the rest of the selection
01033 int *others = new int[num];
01034 int i;
01035 for (i=0; i<num; ++i) 
01036 others[i] = 1;
01037 
01038 if (eval(node->left, num, others)) {
01039 delete [] others;
01040 msgErr << "eval of a 'maxringsize' returned data when it shouldn't have." << sendmsg;
01041 return;
01042 }
01043 
01044 find_rings(num, flgs, others, 1, node->ival);
01045 
01046 delete [] others;
01047 }
01048 
01049 
01050 void ParseTree::eval_ringsize(atomparser_node *node, int num, int *flgs) {
01051 // find the atoms in the rest of the selection
01052 int *others = new int[num];
01053 int i;
01054 for (i=0; i<num; ++i) 
01055 others[i] = 1;
01056 
01057 if (eval(node->left, num, others)) {
01058 delete [] others;
01059 msgErr << "eval of a 'ringsize' returned data when it shouldn't have." << sendmsg;
01060 return;
01061 }
01062 
01063 find_rings(num, flgs, others, node->ival, node->ival);
01064 
01065 delete [] others;
01066 }
01067 
01068 
01069 // a node of the tree merges symbol_datas
01070 // a leaf of the tree produces symbol_datas
01071 symbol_data *ParseTree::eval(atomparser_node *node, int num, int *flgs) {
01072 int i;
01073 int *flg1, *flg2;
01074 symbol_data *tmp;
01075 switch(node->node_type) {
01076 case AND:
01077 eval(node->left, num, flgs); // implicit 'and'
01078 eval(node->right, num, flgs);
01079 return NULL;
01080 
01081 case NOT:
01082 flg1 = new int[num];
01083 memcpy(flg1, flgs, num*sizeof(int));
01084 // this gives: A and B
01085 eval(node->left, num, flg1);
01086 // I want A and (not B)
01087 for (i=num-1; i>=0; i--) {
01088 if (flgs[i]) 
01089 flgs[i] = !flg1[i];
01090 }
01091 delete [] flg1;
01092 break;
01093 
01094 case OR:
01095 flg1 = new int[num];
01096 memcpy(flg1, flgs, num*sizeof(int));
01097 eval(node->left, num, flg1);
01098 flg2 = new int[num];
01099 memcpy(flg2, flgs, num*sizeof(int));
01100 eval(node->right, num, flg2);
01101 for (i=num-1; i>=0; i--) {
01102 flgs[i] = flgs[i] && (flg1[i] || flg2[i]);
01103 }
01104 delete [] flg1;
01105 delete [] flg2;
01106 break;
01107 
01108 case FLOATVAL:
01109 tmp = new symbol_data(SymbolTableElement::IS_FLOAT, 1);
01110 tmp->dval[0] = node->dval;
01111 return tmp;
01112 
01113 case INTVAL:
01114 tmp = new symbol_data(SymbolTableElement::IS_INT, 1);
01115 tmp->ival[0] = node->ival;
01116 return tmp;
01117 
01118 case STRWORD:
01119 tmp = new symbol_data(SymbolTableElement::IS_STRING, 1);
01120 tmp->sval[0] = (char *)(const char *)node->sele.s;
01121 return tmp;
01122 
01123 case KEY: 
01124 return eval_key(node, num, flgs);
01125 
01126 case STRFCTN: 
01127 eval_stringfctn(node, num, flgs); 
01128 break;
01129 
01130 case FUNC:
01131 {
01132 // The only functions in the SymbolTable class are C functions 
01133 // that take a double and return a double. Hence we don't need
01134 // handle all 3x3=9 different cases. 
01135 symbol_data *inp = eval(node->left, num, flgs);
01136 inp->convert(SymbolTableElement::IS_FLOAT);
01137 
01138 // set up space for the return
01139 symbol_data *ret = new symbol_data(SymbolTableElement::IS_FLOAT, num);
01140 SymbolTableElement *elem = table->fctns.data(node->extra_type);
01141 
01142 // If inp came frame a node like INT or FLOAT, it will contain only
01143 // one value, but if it came from KEY, it will have a different value
01144 // for each atom. Check for the relevant case.
01145 if (inp->num == num) {
01146 for (i=0; i<num; i++) 
01147 ret->dval[i] = elem->fctn(inp->dval[i]);
01148 } else {
01149 // assumes that functions return the same value on the same input
01150 // (i.e. this would not work for functions like rand()...)
01151 double d = elem->fctn(inp->dval[0]);
01152 for (i=0; i<num; i++) 
01153 ret->dval[i] = d;
01154 }
01155 delete inp;
01156 return ret;
01157 }
01158 
01159 case ADD:
01160 case SUB:
01161 case MULT:
01162 case MOD:
01163 case EXP:
01164 case DIV: 
01165 return eval_mathop(node, num, flgs);
01166 
01167 case UMINUS:
01168 tmp = eval(node->left, num, flgs);
01169 tmp->convert(SymbolTableElement::IS_FLOAT);
01170 for (i=0; i<tmp->num; i++) {
01171 tmp->dval[i] = -tmp->dval[i];
01172 }
01173 return tmp;
01174 
01175 case COMPARE:
01176 eval_compare(node, num, flgs);
01177 break;
01178 
01179 case WITHIN: // this gets the coordinates from 'x', 'y', and 'z'
01180 eval_within(node, num, flgs);
01181 break;
01182 
01183 case EXWITHIN: // this gets the coordinates from 'x', 'y', and 'z'
01184 eval_exwithin(node, num, flgs);
01185 break;
01186 
01187 case PBWITHIN: // this gets the coordinates from 'x', 'y', and 'z'
01188 eval_pbwithin(node, num, flgs);
01189 break;
01190 
01191 #if defined(NEAREST)
01192 case NEAREST: // this gets the coordinates from 'x', 'y', and 'z'
01193 eval_k_nearest(node, num, flgs);
01194 break;
01195 #endif
01196 
01197 #if defined(WITHINBONDS)
01198 case WITHINBONDS:
01199 eval_within_bonds(node, num, flgs);
01200 break;
01201 #endif
01202 
01203 #if defined(MAXRINGSIZE)
01204 case MAXRINGSIZE:
01205 eval_maxringsize(node, num, flgs);
01206 break;
01207 #endif
01208 
01209 #if defined(RINGSIZE)
01210 case RINGSIZE:
01211 eval_ringsize(node, num, flgs);
01212 break;
01213 #endif
01214 
01215 case SAME:
01216 eval_same(node, num, flgs);
01217 break;
01218 
01219 case SINGLE:
01220 eval_single(node, num, flgs);
01221 break;
01222 
01223 default: 
01224 msgWarn << "ParseTree::eval() unknown node type: " << node->node_type << sendmsg;
01225 break;
01226 }
01227 
01228 return NULL;
01229 }
01230 
01231 
01232 // detect recursive atom selection macros
01233 void ParseTree::eval_find_recursion(atomparser_node *node, int *found,
01234 hash_t *hash) {
01235 // walk the parse tree just like in eval. If any new node types are
01236 // created whose operands can contain singlewords, they must be included
01237 // here.
01238 switch (node->node_type) {
01239 case AND:
01240 case OR:
01241 eval_find_recursion(node->left, found, hash);
01242 eval_find_recursion(node->right, found, hash);
01243 // we don't need to check for COMPARE because singlewords cannot be
01244 // part of the operands.
01245 break;
01246 
01247 case NOT:
01248 case UMINUS:
01249 case WITHIN:
01250 case EXWITHIN:
01251 case SAME:
01252 case FUNC:
01253 eval_find_recursion(node->left, found, hash);
01254 break;
01255 
01256 case SINGLE:
01257 {
01258 const char *thisword = table->fctns.name(node->extra_type);
01259 const char *macro = table->get_custom_singleword(thisword);
01260 if (macro) {
01261 if (hash_insert(hash, thisword, 0) != HASH_FAIL) {
01262 *found = 1;
01263 } else {
01264 ParseTree *subtree = table->parse(macro);
01265 if (subtree != NULL) {
01266 eval_find_recursion(subtree->tree, found, hash);
01267 delete subtree;
01268 } else {
01269 /* XXX prevent things like this from causing a crash:
01270 * atomselect macro A { segid A }
01271 * atomselect macro AB { A }
01272 */
01273 msgErr << "ParseTree) internal processing error, NULL "
01274 << "subtree value while checking recursion" << sendmsg;
01275 }
01276 hash_delete(hash, thisword);
01277 }
01278 }
01279 }
01280 break;
01281 }
01282 }
01283 
01284 
01285 // detect recursive atom selection macros 
01286 int ParseTree::find_recursion(const char *head) {
01287 hash_t hash;
01288 hash_init(&hash, 10);
01289 hash_insert(&hash, head, 0);
01290 int found = 0;
01291 eval_find_recursion(tree, &found, &hash);
01292 hash_destroy(&hash);
01293 return found;
01294 }
01295 
01296 
01297 // this will set a list of flags, then call eval on that array
01298 // it returns 0 if things went bad, 1 otherwise
01299 // the array either set to 1 (if selected) or 0.
01300 int ParseTree::evaluate(int num_atoms, int *flgs) {
01301 int num = num_atoms;
01302 if (!tree || num < 0 ) { // yes, I allow 0 atoms
01303 return 0;
01304 }
01305 
01306 // initialize flags array to true, eval() results are AND'd/OR'd in
01307 for (int i=0; i<num; i++) {
01308 flgs[i] = 1;
01309 }
01310 
01311 // things should never return data so complain if that happens
01312 symbol_data *retdat = eval(tree, num, flgs);
01313 if (retdat) {
01314 msgErr << "Atom selection returned data when it shouldn't\n" << sendmsg;
01315 delete retdat;
01316 }
01317 
01318 return 1;
01319 }
01320 
01321 
01322 // delete and recreate the data space
01323 void symbol_data::make_space(void) {
01324 free_space(); // delete any existing array first
01325 switch(type) {
01326 case SymbolTableElement::IS_FLOAT:
01327 dval = new double[num];
01328 break;
01329 
01330 case SymbolTableElement::IS_INT:
01331 ival = new int[num];
01332 break;
01333 
01334 case SymbolTableElement::IS_STRING:
01335 sval = new char *[num];
01336 memset(sval, 0, num*sizeof(char *)); // init pointers to NULL
01337 break;
01338 }
01339 }
01340 
01341 
01342 // just delete the space
01343 void symbol_data::free_space(void) {
01344 switch (type) {
01345 case SymbolTableElement::IS_FLOAT:
01346 if (dval) 
01347 delete [] dval;
01348 dval = NULL;
01349 break;
01350 
01351 case SymbolTableElement::IS_INT: 
01352 if (ival) 
01353 delete [] ival;
01354 ival = NULL;
01355 break;
01356 
01357 case SymbolTableElement::IS_STRING:
01358 if (sval) {
01359 // free individual strings if necessary
01360 if (free_sval) 
01361 for (int i=0; i<num; i++) free(sval[i]);
01362 
01363 delete [] sval;
01364 sval = NULL;
01365 }
01366 free_sval = 0;
01367 break;
01368 
01369 default:
01370 msgErr << "Unknown data type " << (int)type
01371 << " in symbol_data::free_space" << sendmsg;
01372 }
01373 }
01374 
01375 
01376 // given the new type and the number of elements, create space
01377 symbol_data::symbol_data(SymbolTableElement::symtype new_type, int new_num) {
01378 type = new_type;
01379 num = new_num;
01380 dval = NULL;
01381 ival = NULL;
01382 sval = NULL;
01383 free_sval = 0;
01384 make_space();
01385 }
01386 
01387 
01388 symbol_data::~symbol_data(void) {
01389 free_space();
01390 }
01391 
01392 
01393 void symbol_data::convert(SymbolTableElement::symtype totype) {
01394 // do nothing if types are the same
01395 if (totype == type) 
01396 return;
01397 
01398 // convert to floating point
01399 if (totype == SymbolTableElement::IS_FLOAT) {
01400 double *tmp = new double[num];
01401 if (type == SymbolTableElement::IS_INT) {
01402 for (int i=num-1; i>=0; i--) {
01403 tmp[i] = (double) ival[i];
01404 }
01405 } else { // SymbolTableElement::IS_STRING
01406 for (int i=num-1; i>=0; i--) {
01407 // XXX sval[i] should _never_ be NULL, but there's a bug somewhere
01408 // that allows a conversion from a residue name to a floating point
01409 // value, which occurs without setting the string value since it's
01410 // a built-in query rather than a user-provided string. When this
01411 // (extremely rare) situation occurs, the code could crash here.
01412 // e.g.: mol selection {(not resname SOD) and (segname % 11 == 0)}
01413 // This test will prevent the crash, but does not solve the root of
01414 // the problem.
01415 if (sval[i] != NULL) {
01416 tmp[i] = atof(sval[i]);
01417 } else {
01418 for (int j=num-1; j>=0; j--) {
01419 tmp[i] = 0.0f; 
01420 } 
01421 msgErr << "ParseTree) internal processing error, NULL string value " 
01422 << "while converting to floating point" << sendmsg;
01423 break;
01424 }
01425 }
01426 }
01427 free_space();
01428 type = totype;
01429 dval = tmp;
01430 return;
01431 }
01432 
01433 // convert to string
01434 if (totype == SymbolTableElement::IS_STRING) {
01435 char **tmp = new char*[num];
01436 memset(tmp, 0, num*sizeof(char *)); // init pointers to NULL
01437 char s[100];
01438 if (type == SymbolTableElement::IS_INT) {
01439 for (int i=num-1; i>=0; i--) {
01440 sprintf(s, "%ld", (long) ival[i]);
01441 tmp[i] = strdup(s);
01442 }
01443 } else { // SymbolTableElement::IS_FLOAT
01444 for (int i=num-1; i>=0; i--) {
01445 sprintf(s, "%f", (double) dval[i]);
01446 tmp[i] = strdup(s);
01447 }
01448 }
01449 free_space();
01450 type = totype;
01451 sval = tmp;
01452 free_sval = TRUE;
01453 return;
01454 }
01455 
01456 // convert to integer
01457 if (totype == SymbolTableElement::IS_INT) {
01458 int *tmp = new int[num];
01459 if (type == SymbolTableElement::IS_FLOAT) {
01460 for (int i=num-1; i>=0; i--) {
01461 tmp[i] = (int) dval[i];
01462 }
01463 } else { // SymbolTableElement::IS_STRING
01464 for (int i=num-1; i>=0; i--) {
01465 // XXX sval[i] should _never_ be NULL, but there's a bug somewhere
01466 // that allows a conversion from a residue name to a floating point
01467 // value, which occurs without setting the string value since it's
01468 // a built-in query rather than a user-provided string. When this
01469 // (extremely rare) situation occurs, the code could crash here.
01470 // e.g.: mol selection {(not resname SOD) and (segname % 11 == 0)}
01471 // This test will prevent the crash, but does not solve the root of
01472 // the problem.
01473 if (sval[i] != NULL) {
01474 tmp[i] = atoi(sval[i]);
01475 } else {
01476 for (int j=num-1; j>=0; j--) {
01477 tmp[i] = 0; 
01478 } 
01479 msgErr << "ParseTree) internal processing error, NULL string value " 
01480 << "while converting to integer" << sendmsg;
01481 break;
01482 }
01483 }
01484 }
01485 free_space();
01486 type = totype;
01487 ival = tmp;
01488 return;
01489 }
01490 }
01491 

Generated on Tue Nov 18 02:47:53 2025 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002

AltStyle によって変換されたページ (->オリジナル) /