Add replacements for pbc enumerations
[gromacs.git] / src / gromacs / selection / parser.y
blob5f8197db4741d9d3dff591e471f518618331f1b7
1 %code requires {
2 /*
3 * This file is part of the GROMACS molecular simulation package.
5 * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
37 /*! \internal \file
38 * \brief Grammar description and parser for the selection language.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_selection
43 %code top {
44 /*! \internal \file parser.cpp
45 * \brief Generated (from parser.y by Bison) parser for the selection language.
47 * \ingroup module_selection
49 /*! \internal \file parser.h
50 * \brief Generated (from parser.y by Bison) parser include file.
52 * \ingroup module_selection
54 #include "gmxpre.h"
57 #include "gromacs/utility/unique_cptr.h"
59 #include "parser_internal.h"
61 using gmx::sfree_guard;
62 using gmx::SelectionParserValue;
63 using gmx::SelectionParserValueList;
64 using gmx::SelectionParserValueListPointer;
65 using gmx::SelectionParserParameter;
66 using gmx::SelectionParserParameterList;
67 using gmx::SelectionParserParameterListPointer;
68 using gmx::SelectionTreeElement;
69 using gmx::SelectionTreeElementPointer;
71 #ifdef _MSC_VER
72 #pragma warning(disable: 4065)
73 #endif
76 %code requires{
77 #include "parsetree.h"
78 #include "selelem.h"
80 #define YYLTYPE ::gmx::SelectionLocation
83 %union{
84 int i;
85 real r;
86 char *str;
87 struct gmx_ana_selmethod_t *meth;
89 gmx::SelectionStringMatchType smt;
91 gmx::SelectionTreeElementPointer *sel;
92 gmx::SelectionParserValue *val;
93 gmx::SelectionParserValueListPointer *vlist;
94 gmx::SelectionParserParameter *param;
95 gmx::SelectionParserParameterListPointer *plist;
98 /* Invalid token to report lexer errors */
99 %token INVALID
101 /* Simple input tokens */
102 %token <i> TOK_INT
103 %token <r> TOK_REAL
104 %token <str> STR
105 %token <str> IDENTIFIER
106 %token CMD_SEP
108 /* Simple keyword tokens */
109 %token GROUP
110 %token TO
112 /* Variable tokens */
113 %token <sel> VARIABLE_NUMERIC
114 %token <sel> VARIABLE_GROUP
115 %token <sel> VARIABLE_POS
117 /* Selection method tokens */
118 %token <meth> KEYWORD_NUMERIC
119 %token <meth> KEYWORD_STR
120 %token <str> KEYWORD_POS
121 %token <meth> KEYWORD_GROUP
122 %token <meth> METHOD_NUMERIC
123 %token <meth> METHOD_GROUP
124 %token <meth> METHOD_POS
125 %token <meth> MODIFIER
126 /* Empty token that should precede any non-position KEYWORD/METHOD token that
127 * is not preceded by KEYWORD_POS. This is used to work around reduce/reduce
128 * conflicts that appear when a lookahead token would require a reduction of
129 * a rule with empty RHS before shifting, and there is an alternative reduction
130 * available. Replacing the empty RHS with a dummy token makes these conflicts
131 * only shift/reduce conflicts. Another alternative would be to remove the
132 * pos_mod non-terminal completely and split each rule that uses it into two,
133 * but this would require duplicating six rules in the grammar. */
134 %token EMPTY_POSMOD
136 %token <str> PARAM
137 %token END_OF_METHOD
139 %token OF
140 /* Comparison operators have lower precedence than parameter reduction
141 * to make it possible to parse, e.g., "mindist from resnr 1 < 2" without
142 * parenthesis. */
143 %nonassoc <str> CMP_OP
144 /* A dummy token that determines the precedence of parameter reduction */
145 %nonassoc PARAM_REDUCT
146 /* Boolean operator tokens */
147 %left OR XOR
148 %left AND
149 %left NOT
150 /* Arithmetic operator tokens */
151 %left '+' '-'
152 %left '*' '/'
153 %right UNARY_NEG /* Dummy token for unary negation precedence */
154 %right '^'
155 %nonassoc NUM_REDUCT /* Dummy token for numerical keyword reduction precedence */
157 /* Simple non-terminals */
158 %type <i> integer_number
159 %type <r> real_number number
160 %type <str> string
161 %type <str> pos_mod
162 %type <smt> str_match_type
164 /* Expression non-terminals */
165 %type <sel> commands command cmd_plain
166 %type <sel> selection
167 %type <sel> sel_expr
168 %type <sel> num_expr
169 %type <sel> str_expr
170 %type <sel> pos_expr
172 /* Parameter/value non-terminals */
173 %type <plist> method_params method_param_list
174 %type <param> method_param
175 %type <vlist> value_list value_list_contents basic_value_list basic_value_list_contents
176 %type <val> value_item value_item_range basic_value_item
178 %destructor { free($$); } STR IDENTIFIER KEYWORD_POS CMP_OP string
179 %destructor { if($$) free($$); } PARAM pos_mod
180 %destructor { delete $$; } commands command cmd_plain selection
181 %destructor { delete $$; } sel_expr num_expr str_expr pos_expr
182 %destructor { delete $$; } method_params method_param_list method_param
183 %destructor { delete $$; } value_list value_list_contents basic_value_list basic_value_list_contents
184 %destructor { delete $$; } value_item value_item_range basic_value_item
186 %expect 35
187 %debug
188 %pure-parser
189 %define api.push-pull push
190 %locations
192 %name-prefix "_gmx_sel_yy"
193 %parse-param { void *scanner }
197 /* The start rule: allow one or more commands */
198 commands: /* empty */
200 BEGIN_ACTION;
201 set_empty($$);
202 END_ACTION_TOPLEVEL;
204 | commands command
206 BEGIN_ACTION;
207 set($$, _gmx_sel_append_selection(get($2), get($1), scanner));
208 if (_gmx_sel_parser_should_finish(scanner)) {
209 delete $$;
210 YYACCEPT;
212 END_ACTION_TOPLEVEL;
216 /* A command is formed from an actual command and a separator */
217 command: cmd_plain CMD_SEP { $$ = $1; }
218 | error CMD_SEP
220 BEGIN_ACTION;
221 _gmx_sel_lexer_clear_method_stack(scanner);
222 if (_gmx_selparser_handle_error(scanner))
224 yyerrok;
226 else
228 YYABORT;
230 _gmx_sel_lexer_clear_pselstr(scanner);
231 set_empty($$);
232 END_ACTION_TOPLEVEL;
236 /* Commands can be selections or variable assignments */
237 cmd_plain: /* empty */
239 BEGIN_ACTION;
240 set_empty($$);
241 END_ACTION;
243 | TOK_INT
245 BEGIN_ACTION;
246 SelectionTreeElementPointer s
247 = _gmx_sel_init_group_by_id($1, scanner);
248 SelectionTreeElementPointer p
249 = _gmx_sel_init_position(s, NULL, scanner);
250 if (!p) YYERROR;
251 set($$, _gmx_sel_init_selection(NULL, p, scanner));
252 END_ACTION;
254 | string
256 BEGIN_ACTION;
257 const sfree_guard nameGuard($1);
258 SelectionTreeElementPointer s
259 = _gmx_sel_init_group_by_name($1, scanner);
260 SelectionTreeElementPointer p
261 = _gmx_sel_init_position(s, NULL, scanner);
262 if (!p) YYERROR;
263 set($$, _gmx_sel_init_selection(NULL, p, scanner));
264 END_ACTION;
266 | selection
268 BEGIN_ACTION;
269 set($$, _gmx_sel_init_selection(NULL, get($1), scanner));
270 END_ACTION;
272 | STR selection
274 BEGIN_ACTION;
275 const sfree_guard nameGuard($1);
276 set($$, _gmx_sel_init_selection($1, get($2), scanner));
277 END_ACTION;
279 | IDENTIFIER '=' sel_expr
281 BEGIN_ACTION;
282 const sfree_guard nameGuard($1);
283 set($$, _gmx_sel_assign_variable($1, get($3), scanner));
284 END_ACTION;
286 | IDENTIFIER '=' num_expr
288 BEGIN_ACTION;
289 const sfree_guard nameGuard($1);
290 set($$, _gmx_sel_assign_variable($1, get($3), scanner));
291 END_ACTION;
293 | IDENTIFIER '=' pos_expr
295 BEGIN_ACTION;
296 const sfree_guard nameGuard($1);
297 set($$, _gmx_sel_assign_variable($1, get($3), scanner));
298 END_ACTION;
302 /* Selection is made of an expression and zero or more modifiers */
303 selection: pos_expr { $$ = $1; }
304 | sel_expr
306 BEGIN_ACTION;
307 set($$, _gmx_sel_init_position(get($1), NULL, scanner));
308 CHECK_SEL($$);
309 END_ACTION;
311 | '(' selection ')' { $$ = $2; }
312 | selection MODIFIER method_params
314 BEGIN_ACTION;
315 set($$, _gmx_sel_init_modifier($2, get($3), get($1), scanner));
316 CHECK_SEL($$);
317 END_ACTION;
321 /********************************************************************
322 * BASIC NON-TERMINAL SYMBOLS
323 ********************************************************************/
325 integer_number:
326 TOK_INT { $$ = $1; }
327 | '-' TOK_INT { $$ = -$2; }
330 real_number:
331 TOK_REAL { $$ = $1; }
332 | '-' TOK_REAL { $$ = -$2; }
335 number: integer_number { $$ = $1; }
336 | real_number { $$ = $1; }
339 string: STR { $$ = $1; }
340 | IDENTIFIER { $$ = $1; }
343 /********************************************************************
344 * ATOM SELECTION EXPRESSIONS
345 ********************************************************************/
347 /* Boolean expressions and grouping */
348 sel_expr: NOT sel_expr
350 BEGIN_ACTION;
351 SelectionTreeElementPointer arg(get($2));
352 SelectionTreeElementPointer sel(
353 new SelectionTreeElement(SEL_BOOLEAN, @$));
354 sel->u.boolt = BOOL_NOT;
355 sel->child = arg;
356 set($$, sel);
357 END_ACTION;
359 | sel_expr AND sel_expr
361 BEGIN_ACTION;
362 SelectionTreeElementPointer arg1(get($1)), arg2(get($3));
363 SelectionTreeElementPointer sel(
364 new SelectionTreeElement(SEL_BOOLEAN, @$));
365 sel->u.boolt = BOOL_AND;
366 sel->child = arg1; sel->child->next = arg2;
367 set($$, sel);
368 END_ACTION;
370 | sel_expr OR sel_expr
372 BEGIN_ACTION;
373 SelectionTreeElementPointer arg1(get($1)), arg2(get($3));
374 SelectionTreeElementPointer sel(
375 new SelectionTreeElement(SEL_BOOLEAN, @$));
376 sel->u.boolt = BOOL_OR;
377 sel->child = arg1; sel->child->next = arg2;
378 set($$, sel);
379 END_ACTION;
381 | '(' sel_expr ')' { $$ = $2; }
384 /* Numeric comparisons */
385 sel_expr: num_expr CMP_OP num_expr
387 BEGIN_ACTION;
388 const sfree_guard opGuard($2);
389 set($$, _gmx_sel_init_comparison(get($1), get($3), $2, scanner));
390 CHECK_SEL($$);
391 END_ACTION;
395 /* External groups */
396 sel_expr: GROUP string
398 BEGIN_ACTION;
399 const sfree_guard nameGuard($2);
400 set($$, _gmx_sel_init_group_by_name($2, scanner));
401 END_ACTION;
403 | GROUP TOK_INT
405 BEGIN_ACTION;
406 set($$, _gmx_sel_init_group_by_id($2, scanner));
407 END_ACTION;
411 /* Position modifiers for selection methods */
412 pos_mod: EMPTY_POSMOD { $$ = NULL; }
413 | KEYWORD_POS { $$ = $1; }
416 /* Matching mode forcing for keyword matching */
417 str_match_type:
418 '~' { $$ = gmx::eStringMatchType_RegularExpression; }
419 | '?' { $$ = gmx::eStringMatchType_Wildcard; }
420 | '=' { $$ = gmx::eStringMatchType_Exact; }
423 /* Keyword selections */
424 sel_expr: pos_mod KEYWORD_GROUP
426 BEGIN_ACTION;
427 const sfree_guard posmodGuard($1);
428 set($$, _gmx_sel_init_keyword($2, SelectionParserValueListPointer(), $1, scanner));
429 CHECK_SEL($$);
430 END_ACTION;
432 | pos_mod KEYWORD_STR basic_value_list
434 BEGIN_ACTION;
435 const sfree_guard posmodGuard($1);
436 set($$, _gmx_sel_init_keyword_strmatch($2, gmx::eStringMatchType_Auto, get($3), $1, scanner));
437 CHECK_SEL($$);
438 END_ACTION;
440 | pos_mod KEYWORD_STR str_match_type basic_value_list
442 BEGIN_ACTION;
443 const sfree_guard posmodGuard($1);
444 set($$, _gmx_sel_init_keyword_strmatch($2, $3, get($4), $1, scanner));
445 CHECK_SEL($$);
446 END_ACTION;
448 | pos_mod KEYWORD_NUMERIC basic_value_list
450 BEGIN_ACTION;
451 const sfree_guard posmodGuard($1);
452 set($$, _gmx_sel_init_keyword($2, get($3), $1, scanner));
453 CHECK_SEL($$);
454 END_ACTION;
458 /* Custom selection methods */
459 sel_expr: pos_mod METHOD_GROUP method_params
461 BEGIN_ACTION;
462 const sfree_guard posmodGuard($1);
463 set($$, _gmx_sel_init_method($2, get($3), $1, scanner));
464 CHECK_SEL($$);
465 END_ACTION;
469 /********************************************************************
470 * NUMERICAL EXPRESSIONS
471 ********************************************************************/
473 /* Basic numerical values */
474 num_expr: TOK_INT
476 BEGIN_ACTION;
477 SelectionTreeElementPointer sel(
478 new SelectionTreeElement(SEL_CONST, @$));
479 _gmx_selelem_set_vtype(sel, INT_VALUE);
480 _gmx_selvalue_reserve(&sel->v, 1);
481 sel->v.u.i[0] = $1;
482 set($$, sel);
483 END_ACTION;
485 | TOK_REAL
487 BEGIN_ACTION;
488 SelectionTreeElementPointer sel(
489 new SelectionTreeElement(SEL_CONST, @$));
490 _gmx_selelem_set_vtype(sel, REAL_VALUE);
491 _gmx_selvalue_reserve(&sel->v, 1);
492 sel->v.u.r[0] = $1;
493 set($$, sel);
494 END_ACTION;
498 /* Numeric selection methods */
499 num_expr: pos_mod KEYWORD_NUMERIC %prec NUM_REDUCT
501 BEGIN_ACTION;
502 const sfree_guard posmodGuard($1);
503 set($$, _gmx_sel_init_keyword($2, SelectionParserValueListPointer(), $1, scanner));
504 CHECK_SEL($$);
505 END_ACTION;
507 | pos_mod KEYWORD_NUMERIC OF pos_expr
509 BEGIN_ACTION;
510 const sfree_guard posmodGuard($1);
511 set($$, _gmx_sel_init_keyword_of($2, get($4), $1, scanner));
512 CHECK_SEL($$);
513 END_ACTION;
515 | pos_mod METHOD_NUMERIC method_params
517 BEGIN_ACTION;
518 const sfree_guard posmodGuard($1);
519 set($$, _gmx_sel_init_method($2, get($3), $1, scanner));
520 CHECK_SEL($$);
521 END_ACTION;
525 /* Arithmetic evaluation and grouping */
526 num_expr: num_expr '+' num_expr
528 BEGIN_ACTION;
529 set($$, _gmx_sel_init_arithmetic(get($1), get($3), '+', scanner));
530 END_ACTION;
532 | num_expr '-' num_expr
534 BEGIN_ACTION;
535 set($$, _gmx_sel_init_arithmetic(get($1), get($3), '-', scanner));
536 END_ACTION;
538 | num_expr '*' num_expr
540 BEGIN_ACTION;
541 set($$, _gmx_sel_init_arithmetic(get($1), get($3), '*', scanner));
542 END_ACTION;
544 | num_expr '/' num_expr
546 BEGIN_ACTION;
547 set($$, _gmx_sel_init_arithmetic(get($1), get($3), '/', scanner));
548 END_ACTION;
550 | '-' num_expr %prec UNARY_NEG
552 BEGIN_ACTION;
553 set($$, _gmx_sel_init_arithmetic(get($2), SelectionTreeElementPointer(), '-', scanner));
554 END_ACTION;
556 | num_expr '^' num_expr
558 BEGIN_ACTION;
559 set($$, _gmx_sel_init_arithmetic(get($1), get($3), '^', scanner));
560 END_ACTION;
562 | '(' num_expr ')' { $$ = $2; }
565 /********************************************************************
566 * STRING EXPRESSIONS
567 ********************************************************************/
569 str_expr: string
571 BEGIN_ACTION;
572 SelectionTreeElementPointer sel(
573 new SelectionTreeElement(SEL_CONST, @$));
574 _gmx_selelem_set_vtype(sel, STR_VALUE);
575 _gmx_selvalue_reserve(&sel->v, 1);
576 sel->v.u.s[0] = $1;
577 set($$, sel);
578 END_ACTION;
580 | pos_mod KEYWORD_STR
582 BEGIN_ACTION;
583 const sfree_guard posmodGuard($1);
584 set($$, _gmx_sel_init_keyword($2, SelectionParserValueListPointer(), $1, scanner));
585 CHECK_SEL($$);
586 END_ACTION;
590 /********************************************************************
591 * POSITION EXPRESSIONS
592 ********************************************************************/
594 /* Constant position expressions */
595 pos_expr: '[' number ',' number ',' number ']'
597 BEGIN_ACTION;
598 set($$, _gmx_sel_init_const_position($2, $4, $6, scanner));
599 END_ACTION;
603 /* Grouping of position expressions */
604 pos_expr: '(' pos_expr ')' { $$ = $2; }
607 /* Expressions with a position value */
608 pos_expr: METHOD_POS method_params
610 BEGIN_ACTION;
611 set($$, _gmx_sel_init_method($1, get($2), NULL, scanner));
612 CHECK_SEL($$);
613 END_ACTION;
617 /* Evaluation of positions using a keyword */
618 pos_expr: KEYWORD_POS OF sel_expr %prec PARAM_REDUCT
620 BEGIN_ACTION;
621 const sfree_guard keywordGuard($1);
622 set($$, _gmx_sel_init_position(get($3), $1, scanner));
623 CHECK_SEL($$);
624 END_ACTION;
628 /********************************************************************
629 * VARIABLES
630 ********************************************************************/
632 sel_expr: VARIABLE_GROUP
634 BEGIN_ACTION;
635 set($$, _gmx_sel_init_variable_ref(get($1), scanner));
636 END_ACTION;
640 num_expr: VARIABLE_NUMERIC
642 BEGIN_ACTION;
643 set($$, _gmx_sel_init_variable_ref(get($1), scanner));
644 END_ACTION;
648 pos_expr: VARIABLE_POS
650 BEGIN_ACTION;
651 set($$, _gmx_sel_init_variable_ref(get($1), scanner));
652 END_ACTION;
656 /********************************************************************
657 * METHOD PARAMETERS
658 ********************************************************************/
660 method_params:
661 method_param_list
662 { $$ = $1; }
663 | method_param_list END_OF_METHOD
664 { $$ = $1; }
667 method_param_list:
668 /* empty */
670 BEGIN_ACTION;
671 set($$, SelectionParserParameter::createList());
672 END_ACTION;
674 | method_param_list method_param
676 BEGIN_ACTION;
677 SelectionParserParameterListPointer list(get($1));
678 list->push_back(get($2));
679 set($$, std::move(list));
680 END_ACTION;
684 method_param:
685 PARAM value_list
687 BEGIN_ACTION;
688 const sfree_guard nameGuard($1);
689 set($$, SelectionParserParameter::create($1, get($2), @$));
690 END_ACTION;
694 value_list: value_list_contents { $$ = $1; }
695 | '{' value_list_contents '}' { $$ = $2; }
698 value_list_contents:
699 /* empty */
701 BEGIN_ACTION;
702 set($$, SelectionParserValue::createList());
703 END_ACTION;
705 | value_list_contents value_item
707 BEGIN_ACTION;
708 SelectionParserValueListPointer list(get($1));
709 list->push_back(get($2));
710 set($$, std::move(list));
711 END_ACTION;
713 | value_list_contents ',' value_item
715 BEGIN_ACTION;
716 SelectionParserValueListPointer list(get($1));
717 list->push_back(get($3));
718 set($$, std::move(list));
719 END_ACTION;
723 basic_value_list:
724 basic_value_list_contents { $$ = $1; }
725 | '{' basic_value_list_contents '}' { $$ = $2; }
728 basic_value_list_contents:
729 basic_value_item
731 BEGIN_ACTION;
732 set($$, SelectionParserValue::createList(get($1)));
733 END_ACTION;
735 | basic_value_list_contents basic_value_item
737 BEGIN_ACTION;
738 SelectionParserValueListPointer list(get($1));
739 list->push_back(get($2));
740 set($$, std::move(list));
741 END_ACTION;
743 | basic_value_list_contents ',' basic_value_item
745 BEGIN_ACTION;
746 SelectionParserValueListPointer list(get($1));
747 list->push_back(get($3));
748 set($$, std::move(list));
749 END_ACTION;
753 value_item: sel_expr %prec PARAM_REDUCT
755 BEGIN_ACTION;
756 set($$, SelectionParserValue::createExpr(get($1)));
757 END_ACTION;
759 | pos_expr %prec PARAM_REDUCT
761 BEGIN_ACTION;
762 set($$, SelectionParserValue::createExpr(get($1)));
763 END_ACTION;
765 | num_expr %prec PARAM_REDUCT
767 BEGIN_ACTION;
768 set($$, SelectionParserValue::createExpr(get($1)));
769 END_ACTION;
771 | str_expr %prec PARAM_REDUCT
773 BEGIN_ACTION;
774 set($$, SelectionParserValue::createExpr(get($1)));
775 END_ACTION;
777 | value_item_range { $$ = $1; }
780 basic_value_item:
781 integer_number %prec PARAM_REDUCT
783 BEGIN_ACTION;
784 set($$, SelectionParserValue::createInteger($1, @$));
785 END_ACTION;
787 | real_number %prec PARAM_REDUCT
789 BEGIN_ACTION;
790 set($$, SelectionParserValue::createReal($1, @$));
791 END_ACTION;
793 | string %prec PARAM_REDUCT
795 BEGIN_ACTION;
796 const sfree_guard stringGuard($1);
797 set($$, SelectionParserValue::createString($1, @$));
798 END_ACTION;
800 | value_item_range { $$ = $1; }
803 value_item_range:
804 integer_number TO integer_number
806 BEGIN_ACTION;
807 set($$, SelectionParserValue::createIntegerRange($1, $3, @$));
808 END_ACTION;
810 | integer_number TO real_number
812 BEGIN_ACTION;
813 set($$, SelectionParserValue::createRealRange($1, $3, @$));
814 END_ACTION;
816 | real_number TO number
818 BEGIN_ACTION;
819 set($$, SelectionParserValue::createRealRange($1, $3, @$));
820 END_ACTION;