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

ParseTree.C

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

Generated on Tue May 22 01:48:03 2012 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002