2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2009,2010,2011,2012,2014,2015,2016,2017, 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.
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.
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.
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.
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.
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.
36 * \brief Helper functions for the selection tokenizer.
38 * This file implements the functions in the headers scanner.h and
41 * \author Teemu Murtola <teemu.murtola@gmail.com>
42 * \ingroup module_selection
45 * \internal \file scanner_flex.h
46 * \brief Generated (from scanner.l) header file by Flex.
48 * This file contains definitions of functions that are needed in
49 * scanner_internal.cpp.
51 * \ingroup module_selection
56 #include "scanner_internal.h"
63 #include "gromacs/utility/cstringutil.h"
64 #include "gromacs/utility/exceptions.h"
65 #include "gromacs/utility/gmxassert.h"
66 #include "gromacs/utility/smalloc.h"
67 #include "gromacs/utility/stringutil.h"
70 #include "parsetree.h"
72 #include "selectioncollection-impl.h"
74 #include "selmethod.h"
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. */
85 * Handles initialization of method parameter token.
88 init_param_token(YYSTYPE
*yylval
, gmx_ana_selparam_t
*param
, bool bBoolNo
)
92 GMX_RELEASE_ASSERT(param
->name
!= nullptr,
93 "bBoolNo should only be set for a parameters with a name");
94 snew(yylval
->str
, strlen(param
->name
) + 3);
97 strcpy(yylval
->str
+2, param
->name
);
101 yylval
->str
= param
->name
? gmx_strdup(param
->name
) : nullptr;
107 * Processes a selection method token.
110 init_method_token(YYSTYPE
*yylval
, YYLTYPE
*yylloc
,
111 const gmx::SelectionParserSymbol
*symbol
,
112 bool bPosMod
, gmx_sel_lexer_t
*state
)
114 gmx_ana_selmethod_t
*method
= symbol
->methodValue();
115 /* If the previous token was not KEYWORD_POS, return EMPTY_POSMOD
116 * before the actual method to work around a limitation in Bison. */
117 if (!bPosMod
&& method
->type
!= POS_VALUE
)
119 state
->nextMethodSymbol
= symbol
;
120 _gmx_sel_lexer_add_token(yylloc
, nullptr, 0, state
);
123 _gmx_sel_lexer_add_token(yylloc
, symbol
->name().c_str(), -1, state
);
124 yylval
->meth
= method
;
125 if (!(method
->flags
& SMETH_MODIFIER
) && method
->nparams
== 0)
128 switch (method
->type
)
132 state
->bMatchOf
= true;
133 return KEYWORD_NUMERIC
;
134 case STR_VALUE
: return KEYWORD_STR
;
135 case GROUP_VALUE
: return KEYWORD_GROUP
;
137 GMX_THROW(gmx::InternalError("Unsupported keyword type"));
142 /* Method with parameters or a modifier */
143 if (method
->flags
& SMETH_MODIFIER
)
145 /* Remove all methods from the stack */
147 if (method
->param
[1].name
== nullptr)
149 state
->nextparam
= &method
->param
[1];
154 if (method
->param
[0].name
== nullptr)
156 state
->nextparam
= &method
->param
[0];
160 if (state
->msp
>= state
->mstack_alloc
)
162 state
->mstack_alloc
+= 10;
163 srenew(state
->mstack
, state
->mstack_alloc
);
165 state
->mstack
[state
->msp
] = method
;
166 if (method
->flags
& SMETH_MODIFIER
)
170 switch (method
->type
)
172 case INT_VALUE
: return METHOD_NUMERIC
;
173 case REAL_VALUE
: return METHOD_NUMERIC
;
174 case POS_VALUE
: return METHOD_POS
;
175 case GROUP_VALUE
: return METHOD_GROUP
;
178 GMX_THROW(gmx::InternalError("Unsupported method type"));
181 return INVALID
; /* Should not be reached */
185 _gmx_sel_lexer_process_pending(YYSTYPE
*yylval
, YYLTYPE
*yylloc
,
186 gmx_sel_lexer_t
*state
)
188 if (state
->nextparam
)
190 gmx_ana_selparam_t
*param
= state
->nextparam
;
191 bool bBoolNo
= state
->bBoolNo
;
196 _gmx_sel_lexer_add_token(yylloc
, nullptr, 0, state
);
197 return END_OF_METHOD
;
199 state
->nextparam
= nullptr;
200 state
->bBoolNo
= false;
201 _gmx_sel_lexer_add_token(yylloc
, param
->name
, -1, state
);
202 return init_param_token(yylval
, param
, bBoolNo
);
204 if (state
->prev_pos_kw
> 0)
206 --state
->prev_pos_kw
;
208 if (state
->nextMethodSymbol
)
210 const gmx::SelectionParserSymbol
*symbol
= state
->nextMethodSymbol
;
211 state
->nextMethodSymbol
= nullptr;
212 return init_method_token(yylval
, yylloc
, symbol
, true, state
);
218 _gmx_sel_lexer_process_identifier(YYSTYPE
*yylval
, YYLTYPE
*yylloc
,
219 char *yytext
, size_t yyleng
,
220 gmx_sel_lexer_t
*state
)
222 /* Check if the identifier matches with a parameter name */
225 gmx_ana_selparam_t
*param
= nullptr;
226 bool bBoolNo
= false;
228 while (!param
&& sp
>= 0)
231 for (i
= 0; i
< state
->mstack
[sp
]->nparams
; ++i
)
233 /* Skip NULL parameters and too long parameters */
234 if (state
->mstack
[sp
]->param
[i
].name
== nullptr
235 || strlen(state
->mstack
[sp
]->param
[i
].name
) > yyleng
)
239 if (!strncmp(state
->mstack
[sp
]->param
[i
].name
, yytext
, yyleng
))
241 param
= &state
->mstack
[sp
]->param
[i
];
244 /* Check separately for a 'no' prefix on boolean parameters */
245 if (state
->mstack
[sp
]->param
[i
].val
.type
== NO_VALUE
246 && yyleng
> 2 && yytext
[0] == 'n' && yytext
[1] == 'o'
247 && !strncmp(state
->mstack
[sp
]->param
[i
].name
, yytext
+2, yyleng
-2))
249 param
= &state
->mstack
[sp
]->param
[i
];
261 if (param
->val
.type
== NO_VALUE
&& !bBoolNo
)
263 state
->bMatchBool
= true;
267 state
->neom
= state
->msp
- sp
- 1;
268 state
->nextparam
= param
;
269 state
->bBoolNo
= bBoolNo
;
270 return END_OF_METHOD
;
272 _gmx_sel_lexer_add_token(yylloc
, param
->name
, -1, state
);
273 return init_param_token(yylval
, param
, bBoolNo
);
277 /* Check if the identifier matches with a symbol */
278 const gmx::SelectionParserSymbol
*symbol
279 = state
->sc
->symtab
->findSymbol(std::string(yytext
, yyleng
));
280 /* If there is no match, return the token as a string */
283 yylval
->str
= gmx_strndup(yytext
, yyleng
);
284 _gmx_sel_lexer_add_token(yylloc
, yytext
, yyleng
, state
);
287 gmx::SelectionParserSymbol::SymbolType symtype
= symbol
->type();
288 /* For method symbols, we need some extra processing. */
289 if (symtype
== gmx::SelectionParserSymbol::MethodSymbol
)
291 return init_method_token(yylval
, yylloc
, symbol
, state
->prev_pos_kw
> 0, state
);
293 _gmx_sel_lexer_add_token(yylloc
, symbol
->name().c_str(), -1, state
);
294 /* Reserved symbols should have been caught earlier */
295 if (symtype
== gmx::SelectionParserSymbol::ReservedSymbol
)
297 GMX_THROW(gmx::InternalError(gmx::formatString(
298 "Mismatch between tokenizer and reserved symbol table (for '%s')",
299 symbol
->name().c_str())));
301 /* For variable symbols, return the type of the variable value */
302 if (symtype
== gmx::SelectionParserSymbol::VariableSymbol
)
304 gmx::SelectionTreeElementPointer var
= symbol
->variableValue();
305 /* Return simple tokens for constant variables */
306 if (var
->type
== SEL_CONST
)
311 yylval
->i
= var
->v
.u
.i
[0];
314 yylval
->r
= var
->v
.u
.r
[0];
319 GMX_THROW(gmx::InternalError("Unsupported variable type"));
322 yylval
->sel
= new gmx::SelectionTreeElementPointer(var
);
325 case INT_VALUE
: return VARIABLE_NUMERIC
;
326 case REAL_VALUE
: return VARIABLE_NUMERIC
;
327 case POS_VALUE
: return VARIABLE_POS
;
328 case GROUP_VALUE
: return VARIABLE_GROUP
;
331 GMX_THROW(gmx::InternalError("Unsupported variable type"));
334 /* This position should not be reached. */
336 /* For position symbols, we need to return KEYWORD_POS, but we also need
337 * some additional handling. */
338 if (symtype
== gmx::SelectionParserSymbol::PositionSymbol
)
340 state
->bMatchOf
= true;
341 yylval
->str
= gmx_strdup(symbol
->name().c_str());
342 state
->prev_pos_kw
= 2;
345 /* Should not be reached */
350 _gmx_sel_lexer_add_token(YYLTYPE
*yylloc
, const char *str
, int len
,
351 gmx_sel_lexer_t
*state
)
353 yylloc
->startIndex
= yylloc
->endIndex
= state
->pselstr
.size();
354 /* Do nothing if the string is empty, or if it is a space and there is
355 * no other text yet, or if there already is a space. */
356 if (!str
|| len
== 0 || strlen(str
) == 0
357 || (str
[0] == ' ' && str
[1] == 0
358 && (state
->pselstr
.empty() || state
->pselstr
.back() == ' ')))
366 /* Append the token to the stored string */
367 state
->pselstr
.append(str
, len
);
368 yylloc
->endIndex
= state
->pselstr
.size();
372 _gmx_sel_init_lexer(yyscan_t
*scannerp
, struct gmx_ana_selcollection_t
*sc
,
373 gmx::TextWriter
*statusWriter
, int maxnr
,
374 bool bGroups
, struct gmx_ana_indexgrps_t
*grps
)
376 int rc
= _gmx_sel_yylex_init(scannerp
);
379 // TODO: Throw a more representative exception.
380 GMX_THROW(gmx::InternalError("Lexer initialization failed"));
383 gmx_sel_lexer_t
*state
= new gmx_sel_lexer_t
;
385 // cppcheck-suppress uninitdata
387 // cppcheck-suppress uninitdata
388 state
->bGroups
= bGroups
;
389 // cppcheck-suppress uninitdata
391 // cppcheck-suppress uninitdata
392 state
->nexpsel
= (maxnr
> 0 ? static_cast<int>(sc
->sel
.size()) + maxnr
: -1);
394 state
->statusWriter
= statusWriter
;
396 state
->currentLocation
.startIndex
= 0;
397 state
->currentLocation
.endIndex
= 0;
399 snew(state
->mstack
, 20);
400 state
->mstack_alloc
= 20;
403 state
->nextparam
= nullptr;
404 state
->nextMethodSymbol
= nullptr;
405 state
->prev_pos_kw
= 0;
406 state
->bBoolNo
= false;
407 state
->bMatchOf
= false;
408 state
->bMatchBool
= false;
409 state
->bCmdStart
= true;
410 state
->bBuffer
= false;
412 _gmx_sel_yyset_extra(state
, *scannerp
);
416 _gmx_sel_free_lexer(yyscan_t scanner
)
418 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
420 sfree(state
->mstack
);
423 _gmx_sel_yy_delete_buffer(state
->buffer
, scanner
);
426 _gmx_sel_yylex_destroy(scanner
);
430 _gmx_sel_lexer_set_exception(yyscan_t scanner
,
431 const std::exception_ptr
&ex
)
433 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
434 state
->exception
= ex
;
438 _gmx_sel_lexer_rethrow_exception_if_occurred(yyscan_t scanner
)
440 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
441 if (state
->exception
)
443 std::exception_ptr ex
= state
->exception
;
444 state
->exception
= std::exception_ptr();
445 std::rethrow_exception(ex
);
450 _gmx_sel_lexer_get_status_writer(yyscan_t scanner
)
452 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
453 return state
->statusWriter
;
456 struct gmx_ana_selcollection_t
*
457 _gmx_sel_lexer_selcollection(yyscan_t scanner
)
459 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
464 _gmx_sel_lexer_has_groups_set(yyscan_t scanner
)
466 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
467 return state
->bGroups
;
470 struct gmx_ana_indexgrps_t
*
471 _gmx_sel_lexer_indexgrps(yyscan_t scanner
)
473 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
478 _gmx_sel_lexer_exp_selcount(yyscan_t scanner
)
480 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
481 return state
->nexpsel
;
485 _gmx_sel_lexer_pselstr(yyscan_t scanner
)
487 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
488 return state
->pselstr
.c_str();
492 _gmx_sel_lexer_set_current_location(yyscan_t scanner
,
493 const gmx::SelectionLocation
&location
)
495 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
496 state
->currentLocation
= location
;
499 const gmx::SelectionLocation
&
500 _gmx_sel_lexer_get_current_location(yyscan_t scanner
)
502 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
503 return state
->currentLocation
;
507 _gmx_sel_lexer_get_current_text(yyscan_t scanner
)
509 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
510 return _gmx_sel_lexer_get_text(scanner
, state
->currentLocation
);
514 _gmx_sel_lexer_get_text(yyscan_t scanner
,
515 const gmx::SelectionLocation
&location
)
517 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
518 const int startIndex
= location
.startIndex
;
519 const int endIndex
= location
.endIndex
;
520 if (startIndex
>= endIndex
)
522 return std::string();
524 return state
->pselstr
.substr(startIndex
, endIndex
- startIndex
);
528 _gmx_sel_lexer_clear_pselstr(yyscan_t scanner
)
530 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
531 state
->pselstr
.clear();
535 _gmx_sel_lexer_clear_method_stack(yyscan_t scanner
)
537 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
543 _gmx_sel_finish_method(yyscan_t scanner
)
545 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
554 _gmx_sel_set_lex_input_file(yyscan_t scanner
, FILE *fp
)
556 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
558 state
->bBuffer
= true;
559 state
->buffer
= _gmx_sel_yy_create_buffer(fp
, YY_BUF_SIZE
, scanner
);
560 _gmx_sel_yy_switch_to_buffer(state
->buffer
, scanner
);
564 _gmx_sel_set_lex_input_str(yyscan_t scanner
, const char *str
)
566 gmx_sel_lexer_t
*state
= _gmx_sel_yyget_extra(scanner
);
570 _gmx_sel_yy_delete_buffer(state
->buffer
, scanner
);
572 state
->bBuffer
= true;
573 state
->buffer
= _gmx_sel_yy_scan_string(str
, scanner
);