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 Fri Apr 19 02:45:09 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002