Merge release-4-6 into master
[alexxy/gromacs.git] / src / gromacs / selection / scanner_internal.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief Helper functions for the selection tokenizer.
33  *
34  * This file implements the functions in the headers scanner.h and
35  * scanner_internal.h.
36  *
37  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
38  * \ingroup module_selection
39  */
40 /*! \cond
41  * \internal \file scanner_flex.h
42  * \brief Generated (from scanner.l) header file by Flex.
43  *
44  * This file contains definitions of functions that are needed in
45  * scanner_internal.cpp.
46  *
47  * \ingroup module_selection
48  * \endcond
49  */
50 #include <stdlib.h>
51 #include <string.h>
52
53 #include <string>
54
55 #include "gromacs/legacyheaders/typedefs.h"
56 #include "gromacs/legacyheaders/smalloc.h"
57 #include "gromacs/legacyheaders/string2.h"
58
59 #include "gromacs/utility/exceptions.h"
60 #include "gromacs/utility/gmxassert.h"
61 #include "gromacs/utility/messagestringcollector.h"
62 #include "gromacs/utility/stringutil.h"
63
64 #include "parsetree.h"
65 #include "selectioncollection-impl.h"
66 #include "selelem.h"
67 #include "selmethod.h"
68 #include "symrec.h"
69
70 #include "parser.h"
71 #include "scanner.h"
72 #include "scanner_internal.h"
73
74 //! Step in which the allocated memory for pretty-printed input is incremented.
75 #define STRSTORE_ALLOCSTEP 1000
76
77 /* These are defined as macros in the generated scanner_flex.h.
78  * We undefine them here to have them as variable names in the subroutines.
79  * There are other ways of doing this, but this is probably the easiest. */
80 #undef yylval
81 #undef yytext
82 #undef yyleng
83
84 /*! \brief
85  * Handles initialization of method parameter token.
86  */
87 static int
88 init_param_token(YYSTYPE *yylval, gmx_ana_selparam_t *param, bool bBoolNo)
89 {
90     if (bBoolNo)
91     {
92         GMX_RELEASE_ASSERT(param->name != NULL,
93                 "bBoolNo should only be set for a parameters with a name");
94         snew(yylval->str, strlen(param->name) + 3);
95         yylval->str[0] = 'n';
96         yylval->str[1] = 'o';
97         strcpy(yylval->str+2, param->name);
98     }
99     else
100     {
101         yylval->str = param->name ? strdup(param->name) : NULL;
102     }
103     return PARAM;
104 }
105
106 /*! \brief
107  * Processes a selection method token.
108  */
109 static int
110 init_method_token(YYSTYPE *yylval, gmx_ana_selmethod_t *method, bool bPosMod,
111                   gmx_sel_lexer_t *state)
112 {
113     /* If the previous token was not KEYWORD_POS, return EMPTY_POSMOD
114      * before the actual method to work around a limitation in Bison. */
115     if (!bPosMod && method->type != POS_VALUE)
116     {
117         state->nextmethod = method;
118         return EMPTY_POSMOD;
119     }
120     yylval->meth = method;
121     if (!(method->flags & SMETH_MODIFIER) && method->nparams == 0)
122     {
123         /* Keyword */
124         switch (method->type)
125         {
126             case INT_VALUE:   return KEYWORD_NUMERIC;
127             case REAL_VALUE:  return KEYWORD_NUMERIC;
128             case STR_VALUE:   return KEYWORD_STR;
129             case GROUP_VALUE: return KEYWORD_GROUP;
130             default:
131                 GMX_THROW(gmx::InternalError("Unsupported keyword type"));
132         }
133     }
134     else
135     {
136         /* Method with parameters or a modifier */
137         if (method->flags & SMETH_MODIFIER)
138         {
139             /* Remove all methods from the stack */
140             state->msp = -1;
141             if (method->param[1].name == NULL)
142             {
143                 state->nextparam = &method->param[1];
144             }
145         }
146         else
147         {
148             if (method->param[0].name == NULL)
149             {
150                 state->nextparam = &method->param[0];
151             }
152         }
153         ++state->msp;
154         if (state->msp >= state->mstack_alloc)
155         {
156             state->mstack_alloc += 10;
157             srenew(state->mstack, state->mstack_alloc);
158         }
159         state->mstack[state->msp] = method;
160         if (method->flags & SMETH_MODIFIER)
161         {
162             return MODIFIER;
163         }
164         switch (method->type)
165         {
166             case INT_VALUE:   return METHOD_NUMERIC;
167             case REAL_VALUE:  return METHOD_NUMERIC;
168             case POS_VALUE:   return METHOD_POS;
169             case GROUP_VALUE: return METHOD_GROUP;
170             default:
171                 --state->msp;
172                 GMX_THROW(gmx::InternalError("Unsupported method type"));
173         }
174     }
175     return INVALID; /* Should not be reached */
176 }
177
178 int
179 _gmx_sel_lexer_process_pending(YYSTYPE *yylval, gmx_sel_lexer_t *state)
180 {
181     if (state->nextparam)
182     {
183         gmx_ana_selparam_t *param = state->nextparam;
184         bool                bBoolNo = state->bBoolNo;
185
186         if (state->neom > 0)
187         {
188             --state->neom;
189             return END_OF_METHOD;
190         }
191         state->nextparam = NULL;
192         state->bBoolNo   = false;
193         _gmx_sel_lexer_add_token(param->name, -1, state);
194         return init_param_token(yylval, param, bBoolNo);
195     }
196     if (state->prev_pos_kw > 0)
197     {
198         --state->prev_pos_kw;
199     }
200     if (state->nextmethod)
201     {
202         gmx_ana_selmethod_t *method = state->nextmethod;
203
204         state->nextmethod = NULL;
205         return init_method_token(yylval, method, true, state);
206     }
207     return 0;
208 }
209
210 int
211 _gmx_sel_lexer_process_identifier(YYSTYPE *yylval, char *yytext, size_t yyleng,
212                                   gmx_sel_lexer_t *state)
213 {
214     /* Check if the identifier matches with a parameter name */
215     if (state->msp >= 0)
216     {
217         gmx_ana_selparam_t *param = NULL;
218         bool                bBoolNo = false;
219         int                 sp = state->msp;
220         while (!param && sp >= 0)
221         {
222             int             i;
223             for (i = 0; i < state->mstack[sp]->nparams; ++i)
224             {
225                 /* Skip NULL parameters and too long parameters */
226                 if (state->mstack[sp]->param[i].name == NULL
227                     || strlen(state->mstack[sp]->param[i].name) > yyleng)
228                 {
229                     continue;
230                 }
231                 if (!strncmp(state->mstack[sp]->param[i].name, yytext, yyleng))
232                 {
233                     param = &state->mstack[sp]->param[i];
234                     break;
235                 }
236                 /* Check separately for a 'no' prefix on boolean parameters */
237                 if (state->mstack[sp]->param[i].val.type == NO_VALUE
238                     && yyleng > 2 && yytext[0] == 'n' && yytext[1] == 'o'
239                     && !strncmp(state->mstack[sp]->param[i].name, yytext+2, yyleng-2))
240                 {
241                     param = &state->mstack[sp]->param[i];
242                     bBoolNo = true;
243                     break;
244                 }
245             }
246             if (!param)
247             {
248                 --sp;
249             }
250         }
251         if (param)
252         {
253             if (param->val.type == NO_VALUE && !bBoolNo)
254             {
255                 state->bMatchBool = true;
256             }
257             if (sp < state->msp)
258             {
259                 state->neom = state->msp - sp - 1;
260                 state->nextparam = param;
261                 state->bBoolNo   = bBoolNo;
262                 return END_OF_METHOD;
263             }
264             _gmx_sel_lexer_add_token(param->name, -1, state);
265             return init_param_token(yylval, param, bBoolNo);
266         }
267     }
268
269     /* Check if the identifier matches with a symbol */
270     const gmx::SelectionParserSymbol *symbol
271         = state->sc->symtab->findSymbol(std::string(yytext, yyleng), false);
272     /* If there is no match, return the token as a string */
273     if (!symbol)
274     {
275         yylval->str = gmx_strndup(yytext, yyleng);
276         _gmx_sel_lexer_add_token(yytext, yyleng, state);
277         return IDENTIFIER;
278     }
279     _gmx_sel_lexer_add_token(symbol->name().c_str(), -1, state);
280     gmx::SelectionParserSymbol::SymbolType symtype = symbol->type();
281     /* Reserved symbols should have been caught earlier */
282     if (symtype == gmx::SelectionParserSymbol::ReservedSymbol)
283     {
284         GMX_THROW(gmx::InternalError(gmx::formatString(
285                         "Mismatch between tokenizer and reserved symbol table (for '%s')",
286                         symbol->name().c_str())));
287     }
288     /* For variable symbols, return the type of the variable value */
289     if (symtype == gmx::SelectionParserSymbol::VariableSymbol)
290     {
291         gmx::SelectionTreeElementPointer var = symbol->variableValue();
292         /* Return simple tokens for constant variables */
293         if (var->type == SEL_CONST)
294         {
295             switch (var->v.type)
296             {
297                 case INT_VALUE:
298                     yylval->i = var->v.u.i[0];
299                     return TOK_INT;
300                 case REAL_VALUE:
301                     yylval->r = var->v.u.r[0];
302                     return TOK_REAL;
303                 case POS_VALUE:
304                     break;
305                 default:
306                     GMX_THROW(gmx::InternalError("Unsupported variable type"));
307             }
308         }
309         yylval->sel = new gmx::SelectionTreeElementPointer(var);
310         switch (var->v.type)
311         {
312             case INT_VALUE:   return VARIABLE_NUMERIC;
313             case REAL_VALUE:  return VARIABLE_NUMERIC;
314             case POS_VALUE:   return VARIABLE_POS;
315             case GROUP_VALUE: return VARIABLE_GROUP;
316             default:
317                 delete yylval->sel;
318                 GMX_THROW(gmx::InternalError("Unsupported variable type"));
319                 return INVALID;
320         }
321         delete yylval->sel;
322         return INVALID; /* Should not be reached. */
323     }
324     /* For method symbols, return the correct type */
325     if (symtype == gmx::SelectionParserSymbol::MethodSymbol)
326     {
327         gmx_ana_selmethod_t *method = symbol->methodValue();
328         return init_method_token(yylval, method, state->prev_pos_kw > 0, state);
329     }
330     /* For position symbols, we need to return KEYWORD_POS, but we also need
331      * some additional handling. */
332     if (symtype == gmx::SelectionParserSymbol::PositionSymbol)
333     {
334         state->bMatchOf = true;
335         yylval->str = strdup(symbol->name().c_str());
336         state->prev_pos_kw = 2;
337         return KEYWORD_POS;
338     }
339     /* Should not be reached */
340     return INVALID;
341 }
342
343 void
344 _gmx_sel_lexer_add_token(const char *str, int len, gmx_sel_lexer_t *state)
345 {
346     /* Do nothing if the string is empty, or if it is a space and there is
347      * no other text yet, or if there already is a space. */
348     if (!str || len == 0 || strlen(str) == 0
349         || (str[0] == ' ' && str[1] == 0
350             && (state->pslen == 0 || state->pselstr[state->pslen - 1] == ' ')))
351     {
352         return;
353     }
354     if (len < 0)
355     {
356         len = strlen(str);
357     }
358     /* Allocate more memory if necessary */
359     if (state->nalloc_psel - state->pslen < len)
360     {
361         int incr = STRSTORE_ALLOCSTEP < len ? len : STRSTORE_ALLOCSTEP;
362         state->nalloc_psel += incr;
363         srenew(state->pselstr, state->nalloc_psel);
364     }
365     /* Append the token to the stored string */
366     strncpy(state->pselstr + state->pslen, str, len);
367     state->pslen += len;
368     state->pselstr[state->pslen] = 0;
369 }
370
371 void
372 _gmx_sel_init_lexer(yyscan_t *scannerp, struct gmx_ana_selcollection_t *sc,
373                     bool bInteractive, int maxnr, bool bGroups,
374                     struct gmx_ana_indexgrps_t *grps)
375 {
376     int rc = _gmx_sel_yylex_init(scannerp);
377     if (rc != 0)
378     {
379         // TODO: Throw a more representative exception.
380         GMX_THROW(gmx::InternalError("Lexer initialization failed"));
381     }
382
383     gmx_sel_lexer_t *state = new gmx_sel_lexer_t;
384
385     state->sc        = sc;
386     state->errors    = NULL;
387     state->bGroups   = bGroups;
388     state->grps      = grps;
389     state->nexpsel   = (maxnr > 0 ? static_cast<int>(sc->sel.size()) + maxnr : -1);
390
391     state->bInteractive = bInteractive;
392
393     snew(state->pselstr, STRSTORE_ALLOCSTEP);
394     state->pselstr[0]   = 0;
395     state->pslen        = 0;
396     state->nalloc_psel  = STRSTORE_ALLOCSTEP;
397
398     snew(state->mstack, 20);
399     state->mstack_alloc = 20;
400     state->msp          = -1;
401     state->neom         = 0;
402     state->nextparam    = NULL;
403     state->nextmethod   = NULL;
404     state->prev_pos_kw  = 0;
405     state->bBoolNo      = false;
406     state->bMatchOf     = false;
407     state->bMatchBool   = false;
408     state->bCmdStart    = true;
409     state->bBuffer      = false;
410
411     _gmx_sel_yyset_extra(state, *scannerp);
412 }
413
414 void
415 _gmx_sel_free_lexer(yyscan_t scanner)
416 {
417     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
418
419     sfree(state->pselstr);
420     sfree(state->mstack);
421     if (state->bBuffer)
422     {
423         _gmx_sel_yy_delete_buffer(state->buffer, scanner);
424     }
425     delete state;
426     _gmx_sel_yylex_destroy(scanner);
427 }
428
429 void
430 _gmx_sel_set_lexer_error_reporter(yyscan_t scanner,
431                                   gmx::MessageStringCollector *errors)
432 {
433     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
434     state->errors = errors;
435 }
436
437 void
438 _gmx_sel_lexer_set_exception(yyscan_t scanner,
439                              const boost::exception_ptr &ex)
440 {
441     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
442     state->exception = ex;
443 }
444
445 void
446 _gmx_sel_lexer_rethrow_exception_if_occurred(yyscan_t scanner)
447 {
448     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
449     if (state->exception)
450     {
451         boost::exception_ptr ex = state->exception;
452         state->exception = boost::exception_ptr();
453         rethrow_exception(ex);
454     }
455 }
456
457 bool
458 _gmx_sel_is_lexer_interactive(yyscan_t scanner)
459 {
460     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
461     return state->bInteractive;
462 }
463
464 struct gmx_ana_selcollection_t *
465 _gmx_sel_lexer_selcollection(yyscan_t scanner)
466 {
467     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
468     return state->sc;
469 }
470
471 gmx::MessageStringCollector *
472 _gmx_sel_lexer_error_reporter(yyscan_t scanner)
473 {
474     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
475     GMX_RELEASE_ASSERT(state->errors != NULL, "Error reporter not set");
476     return state->errors;
477 }
478
479 bool
480 _gmx_sel_lexer_has_groups_set(yyscan_t scanner)
481 {
482     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
483     return state->bGroups;
484 }
485
486 struct gmx_ana_indexgrps_t *
487 _gmx_sel_lexer_indexgrps(yyscan_t scanner)
488 {
489     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
490     return state->grps;
491 }
492
493 int
494 _gmx_sel_lexer_exp_selcount(yyscan_t scanner)
495 {
496     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
497     return state->nexpsel;
498 }
499
500 const char *
501 _gmx_sel_lexer_pselstr(yyscan_t scanner)
502 {
503     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
504     return state->pselstr;
505 }
506
507 void
508 _gmx_sel_lexer_clear_pselstr(yyscan_t scanner)
509 {
510     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
511     state->pselstr[0] = 0;
512     state->pslen      = 0;
513 }
514
515 void
516 _gmx_sel_lexer_clear_method_stack(yyscan_t scanner)
517 {
518     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
519
520     state->msp = -1;
521 }
522
523 void
524 _gmx_sel_finish_method(yyscan_t scanner)
525 {
526     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
527
528     if (state->msp >= 0)
529     {
530         --state->msp;
531     }
532 }
533
534 void
535 _gmx_sel_set_lex_input_file(yyscan_t scanner, FILE *fp)
536 {
537     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
538
539     state->bBuffer = true;
540     state->buffer  = _gmx_sel_yy_create_buffer(fp, YY_BUF_SIZE, scanner);
541     _gmx_sel_yy_switch_to_buffer(state->buffer, scanner);
542 }
543
544 void
545 _gmx_sel_set_lex_input_str(yyscan_t scanner, const char *str)
546 {
547     gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
548
549     if (state->bBuffer)
550     {
551         _gmx_sel_yy_delete_buffer(state->buffer, scanner);
552     }
553     state->bBuffer = true;
554     state->buffer  = _gmx_sel_yy_scan_string(str, scanner);
555 }