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