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

Generated on Mon Oct 13 01:27:47 2008 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002