@q Copyright 2012-2022 Alexander Shibakov@> @q Copyright 2002-2014 Free Software Foundation, Inc.@> @q This file is part of SPLinT@> @q SPLinT is free software: you can redistribute it and/or modify@> @q it under the terms of the GNU General Public License as published by@> @q the Free Software Foundation, either version 3 of the License, or@> @q (at your option) any later version.@> @q SPLinT is distributed in the hope that it will be useful,@> @q but WITHOUT ANY WARRANTY; without even the implied warranty of@> @q MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the@> @q GNU General Public License for more details.@> @q You should have received a copy of the GNU General Public License@> @q along with SPLinT. If not, see .@> @** The parser. \ifbootstrapmode \def\tokendeffile{ldp.tok}% \input ldman.sty \def\bstrapparser{dyytab.tex}% only the full or preamble parser would know how to % parse grammar sections that mix \prodstyle{\%token} % declarations with other \bison\ syntax \def\bstraptokens{bo.tok}% token equivalences for the full lexer \modebootstrap \def\MRI{} \def\ld{} \fi \immediate\openout\exampletable=\jobname.exl\relax% The outline of the grammar file below does not reveal anything unusual in the general layout of \ld\ grammar. The first section lists all the token definitions, \prodstyle{\%union} styles, and some \Cee\ code. The original comments that come with the grammar file of the linker have been mostly left intact. They are typeset in {\it italics\/} to make them easy to recognize. @s TeX_ TeX @s TeXa TeX @s TeXb TeX @s TeXf TeX @s TeXfo TeX @s TeXao TeX @(ldp.yy@>= @G Switch to generic mode. %{@> @<\ld\ parser \Cee\ preamble@> @=%} @> @<\ld\ parser \bison\ options@> @= %union {@> @ @=} %{@> @<\ld\ parser \Cee\ postamble@> @=%} @> @ @= %% @> @<\ld\ parser productions@> @= %% @g @ Among the options listed in this section, \prodstyle{\%token-table} is the most critical for the proper operation of the parser and must be enabled to supply the token information to the lexer (the traditional way of passing this information along is to use a \Cee\ header file with the appropriate definitions). The start symbol does not have to be given explicitly and can be indicated by listing the appropriate rules at the beginning. Most other sections of the grammar file, with the exception of the rules are either empty or hold placeholder values. The functionality provided by the code in these sections in the case of a \Cee\ parser is supplied by the \TeX\ macros in \.{ldman.sty}. @<\ld\ parser \bison\ options@>= @G %token-table %debug %start script_file @g @ @<\ld\ parser \Cee\ preamble@>= @ @= @ @<\ld\ parser \Cee\ postamble@>= @ @<\ld\ parser productions@>= @<\GNU\ \ld\ script rules@>@; @@; @ The tokens are declared first. This section is also used to supply numerical token values to the lexer by the original parser, as well as the bootstrapping phase of the typesetting parser. Unlike the native (\Cee) parser for \ld\ the typesetting parser has no need for the type of each token (rather, the type consistency is based on the weak dynamic type system coded in \.{yyunion.sty} and \.{ldunion.sy}). Thus all the tokens used by the \ld\ parser are put in a single list. @= @G %token INT %token NAME LNAME %token END %token ALIGN_K BLOCK BIND QUAD SQUAD LONG SHORT BYTE %token SECTIONS PHDRS INSERT_K AFTER BEFORE %token DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END DATA_SEGMENT_END %token SORT_BY_NAME SORT_BY_ALIGNMENT SORT_NONE %token SORT_BY_INIT_PRIORITY %token '{' '}' %token SIZEOF_HEADERS OUTPUT_FORMAT FORCE_COMMON_ALLOCATION OUTPUT_ARCH %token INHIBIT_COMMON_ALLOCATION %token SEGMENT_START %token INCLUDE %token MEMORY %token REGION_ALIAS %token LD_FEATURE %token NOLOAD DSECT COPY INFO OVERLAY %token DEFINED TARGET_K SEARCH_DIR MAP ENTRY %token NEXT %token SIZEOF ALIGNOF ADDR LOADADDR MAX_K MIN_K %token STARTUP HLL SYSLIB FLOAT NOFLOAT NOCROSSREFS %token ORIGIN FILL %token LENGTH CREATE_OBJECT_SYMBOLS INPUT GROUP OUTPUT CONSTRUCTORS %token ALIGNMOD AT SUBALIGN HIDDEN PROVIDE PROVIDE_HIDDEN AS_NEEDED %token CHIP LIST SECT ABSOLUTE LOAD NEWLINE ENDWORD ORDER NAMEWORD ASSERT_K %token LOG2CEIL FORMAT PUBLIC DEFSYMEND BASE ALIAS TRUNCATE REL %token INPUT_SCRIPT INPUT_MRI_SCRIPT INPUT_DEFSYM CASE EXTERN START %token VERS_TAG VERS_IDENTIFIER %token GLOBAL LOCAL VERSIONK INPUT_VERSION_SCRIPT %token KEEP ONLY_IF_RO ONLY_IF_RW SPECIAL INPUT_SECTION_FLAGS ALIGN_WITH_INPUT %token EXCLUDE_FILE %token CONSTANT %token INPUT_DYNAMIC_LIST %right PLUSEQ MINUSEQ MULTEQ DIVEQ '=' LSHIFTEQ RSHIFTEQ ANDEQ OREQ '?' ':' UNARY %left OROR ANDAND '|' '^' '&' EQ NE '<' '>' LE GE LSHIFT RSHIFT '+' '-' '*' '/' '%' '(' @g @*1 Grammar rules, an overview. The first natural step in transforming an existing parser into a `parser stack' for pretty printing is to understand the `anatomy' of the grammar. Not every grammar is suitable for such a transformation and in almost every case, some modifications are needed. The parser and lexer implementation for \ld\ is not terrible although it does have some idiosynchrasies that could have been eliminated by a careful grammar redesign. Instead of invasive rewriting of significant portions of the grammar, the approach taken here merely omits some rules and partitions the grammar into several subsets, each of which is supposed to handle a well defined logical section of an \ld\ script file. One example of a trick used by the \ld\ parser that is not appropriate for a pretty printing grammar is the way the original parser handles the choice of the format of an input file. After a command line option that selects the input format has been read (or the format has been determined using some other method), the first token output by the lexer branches the parser to the appropriate portion of the full grammar. Since the token never appears as part of the input file there is no need to include this part of the main grammar for the purposes of typesetting. \traceparserstatestrue \tracestackstrue \tracerulestrue \traceactionstrue \tracelookaheadtrue \traceparseresultstrue \tracebadcharstrue \yyflexdebugtrue %\checktabletrue % \traceparserstatesfalse \tracestacksfalse \tracerulesfalse \traceactionsfalse \tracelookaheadfalse \traceparseresultsfalse \tracebadcharsfalse \yyflexdebugfalse \checktablefalse % \saveparseoutputfalse @= @G file: INPUT_SCRIPT script_file | INPUT_MRI_SCRIPT mri_script_file | INPUT_VERSION_SCRIPT version_script_file | INPUT_DYNAMIC_LIST dynamic_list_file | INPUT_DEFSYM defsym_expr ; @g @ @= @G @t}\vb{\inline\flatten}{@> filename: NAME {@>@[TeX_( "/yy0{/noexpand/ldfilename{/the/yy(1)}}" );@]@=} ; @g @ The simplest parser subset is intended to parse symbol definitions given in the command line that invokes the linker. Creating a parser for it involves almost no extra effort so we leave it in. Note that the simpliciy is somewhat deceptive as the syntax of \prodstyle{exp} is rather complex. That part of the grammar is needed elsewhere, however, so symbol definitions parsing costs almost nothing on top of the already required effort. The only practical use for this part of the \ld\ grammar is presenting examples in text. The\namedspot{pingpong} \TeX\ macro \.{\\ldlex@@defsym} switches the lexer state to \.{DEFSYMEXP} (see \locallink{stateswitchers}all the state switching macros\endlink\ in the chapter about the lexer implementation below). Switching lexer states from the parser presents some difficulties which can be overcome by careful design. For example, the state switching macros can be invoked before the lexer is called and initialized (when the parser performs a {\it default action\/}). @= @G defsym_expr: {@>@[TeX_( "/ldlex@@defsym" );@]@=} NAME '=' exp {@>@[TeX_( "/ldlex@@popstate" );@]@=} ; @g @ {\it Syntax within an \MRI\ script file}\footnote{As explained at the beginning of this chapter, the text in {\it italics\/} was taken from the original comments by the \ld\ parser and lexer programmers.}. The parser for typesetting is only intended to process \GNU\ \ld\ scripts and does not concern itself with any additional compatibility modes. For this reason, all support for \MRI\ style scripts has been omitted. One use for the section below is a small demonstration of the formatting tools that change the output of the \bison\ parser. %\checktabletrue %\tracingliststrue @<\MRI\ style script rules@>= @G @t}\vb{\inline\flatten}{@> mri_script_file: {@>@[TeX_( "/ldlex@@mri@@script" );@]@=} mri_script_lines {@>@[TeX_( "/ldlex@@popstate" );@]@=} ; mri_script_lines: mri_script_lines mri_script_command NEWLINE | ; @t}\vb{\resetf}{@> mri_script_command: CHIP exp | CHIP exp ',' exp | NAME {} | LIST {} | ORDER ordernamelist {} | ENDWORD {} @t}\vb{\flatten}{@> | PUBLIC NAME '=' exp {} | PUBLIC NAME ',' exp {} | PUBLIC NAME exp @t}\vb{\resetf}{@> {} | FORMAT NAME {} @t}\vb{\flatten}{@> | SECT NAME ',' exp {} | SECT NAME exp {} | SECT NAME '=' exp @t}\vb{\resetf}{@> {} @t}\vb{\flatten}{@> | ALIGN_K NAME '=' exp {} | ALIGN_K NAME ',' exp @t}\vb{\resetf}{@> {} @t}\vb{\flatten}{@> | ALIGNMOD NAME '=' exp {} | ALIGNMOD NAME ',' exp {} @t}\vb{\resetf}{@> {} | ABSOLUTE mri_abs_name_list | LOAD mri_load_name_list | NAMEWORD NAME {} @t}\vb{\flatten}{@> | ALIAS NAME ',' NAME {} | ALIAS NAME ',' INT {} @t}\vb{\resetf}{@> {} | BASE exp {} | TRUNCATE INT {} | CASE casesymlist | EXTERN extern_name_list @t}\vb{\flatten}{@> | INCLUDE filename {@>@@=} mri_script_lines END @t}\vb{\resetf}{@> {@>@@=} | START NAME {} | ; @t}\vb{\inline\flatten}{@> ordernamelist: ordernamelist ',' NAME {} | ordernamelist NAME {} | ; mri_load_name_list: NAME {} | mri_load_name_list ',' NAME {} ; mri_abs_name_list: NAME {} | mri_abs_name_list ',' NAME {} ; casesymlist: {} | NAME | casesymlist ',' NAME ; @g @ {\it Parsed as expressions so that commas separate entries.} The core of the parser consists of productions describing \GNU\ \ld\ linker scripts. The first rule is common to both \MRI\ and \GNU\ formats. \checktablefalse \tracinglistsfalse @<\GNU\ \ld\ script rules@>= @G extern_name_list: {@>@[TeX_( "/ldlex@@expression" );@]@=} extern_name_list_body {@>@[TeX_( "/ldlex@@popstate" );@]@=} ; extern_name_list_body: NAME {} | extern_name_list_body NAME {} | extern_name_list_body ',' NAME {} ; @ The top level productions simply define a script file as a list of script commands. @<\GNU\ \ld\ script rules@>= @G script_file: {@>@[TeX_( "/ldlex@@both" );@]@=} ifile_list {@>@[TeX_( "/getfifth{/yy(2)}/to/ldcmds/ldlex@@popstate" );@]@=} ; ifile_list: ifile_list ifile_p1 {@>@@=} | {@>@[TeX_( "/yy0{/nx/ldinsertcweb{}{}{}{}}" );@]@=} ; @g @ @= @[TeX_( "/getsecond{/yy(1)}/to/toksa/getthird{/yy(1)}/to/toksb" );@]@; @[TeX_( "/getfourth{/yy(1)}/to/toksc/getfifth{/yy(1)}/to/toksd" );@]@; @[TeX_( "/getsecond{/yy(2)}/to/tokse/getthird{/yy(2)}/to/toksf" );@]@; @[TeX_( "/getfourth{/yy(2)}/to/toksg/getfifth{/yy(2)}/to/toksh" );@]@; @[TeXb( "/yytoksempty{/toksh}{/yy0{/the/yy(1)}}{/yytoksempty{/toksd}{/yy0{/the/yy(2)}}" );@]@; @[TeXf( " {/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{/the/toksg}{/the/toksd" );@]@; @[TeXfo( " /nx/ldcommandseparator{/the/tokse}{/the/toksf}{/the/toksc}{/the/toksg}/the/toksh}}}}" );@]@; @*1 Script internals. There are a number of different commands. For typesetting purposes, the handling of most of these can be significantly simplified. In the \prodstyle{GROUP} command there is no need to perform any actions upon entering the group, for instance. \prodstyle{INCLUDE} presents a special challenge. In the original grammar this command is followed by a general list of script commands (the contents of the included file) terminated by \prodstyle{END}. The `magic' of opening the file and inserting its contents into the stream being parsed is performed by the lexer and the parser in the background. The typesetting parser, on the other hand, only has to typeset the \prodstyle{INCLUDE} command itself and has no need for opening and parsing the file being included. We can simply change the grammar rule to omit the follow up script commands but that would require altering the existing grammar. \namedspot{pretendbuffersw}Since the command list (\prodstyle{ifile\_list}) is allowed to be empty, we simply \locallink{pretendbufferswlex}fake\endlink\ the inclusion of the file in the lexer by immediately outputting \prodstyle{END} upon entering the appropriate lexer state. One advantage in using this approach is the ability, when desired, to examine the included file for possible cross-referencing information. Each command is packaged with a qualifier that records its type for the rule that adds the fragment to the script file. @<\GNU\ \ld\ script rules@>= @G ifile_p1: memory {@>@@=} | sections {@>@@=} | phdrs | startup | high_level_library | low_level_library | floating_point_support | statement_anywhere {@>@@=} | version | ';' {@>@[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){none}{}}" );@]@=} | TARGET_K '(' NAME ')' {} | SEARCH_DIR '(' filename ')' {} | OUTPUT '(' filename ')' {} | OUTPUT_FORMAT '(' NAME ')' {} | OUTPUT_FORMAT '(' NAME ',' NAME ',' NAME ')' {} | OUTPUT_ARCH '(' NAME ')' {} | FORCE_COMMON_ALLOCATION {} | INHIBIT_COMMON_ALLOCATION {} | INPUT '(' input_list ')' | GROUP {} '(' input_list ')' {} | MAP '(' filename ')' {} | INCLUDE filename {@>@@=} ifile_list END {@>@@=} | NOCROSSREFS '(' nocrossref_list ')' {} | EXTERN '(' extern_name_list ')' | INSERT_K AFTER NAME {} | INSERT_K BEFORE NAME {} | REGION_ALIAS '(' NAME ',' NAME ')' {} | LD_FEATURE '(' NAME ')' {} ; input_list: NAME {} | input_list ',' NAME {} | input_list NAME {} | LNAME {} | input_list ',' LNAME {} | input_list LNAME {} | AS_NEEDED '(' {} input_list ')' {} | input_list ',' AS_NEEDED '(' {} input_list ')' {} | input_list AS_NEEDED '(' {} input_list ')' {} ; sections: SECTIONS '{' sec_or_group_p1 '}' {@>@
@=} ; sec_or_group_p1: sec_or_group_p1 section {@>@@=} | sec_or_group_p1 statement_anywhere {@>@@=} | {@>@[TeX_( "/yy0{}" );@]@=} ; statement_anywhere: ENTRY '(' NAME ')' {@>@@=} | assignment end {@>@@=} | ASSERT_K {@>@[TeX_( "/ldlex@@expression" );@]@=} '(' exp ',' NAME ')' {@>@[TeX_( "/ldlex@@popstate" );@]@=} ; @g @ @= @[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){sect}{/nx/ldsections{/the/yy(3)/nx/ldsectionstash/the/yy(4)}}}" );@] @ @= @[TeX_( "/yytoksempty{/yy(2)}{/yy0{/the/yy(1)}}{" );@]@; @[TeX_( " /getsecond{/yy(2)}/to/tokse/getthird{/yy(2)}/to/toksf" );@]@; @[TeX_( " /getfourth{/yy(2)}/to/toksg/getfifth{/yy(2)}/to/toksh" );@]@; @[TeXb( " /yytoksempty{/yy(1)}{/yy0{/nx/ldsectionstash{/the/tokse}{/the/toksf}/the/toksh}}" );@]@; @[TeXa( " {/yy0{/the/yy(1)/nx/ldsectionseparator{/the/tokse}{/the/toksf}/the/toksh}}" );@]@; @[TeXfo( "}" );@]@; @ @= @[TeX_( "/yytoksempty{/yy(2)}{/yy0{/the/yy(1)}}{" );@]@; @[TeX_( " /getsecond{/yy(2)}/to/tokse/getthird{/yy(2)}/to/toksf" );@]@; @[TeX_( " /getfourth{/yy(2)}/to/toksg/getfifth{/yy(2)}/to/toksh" );@]@; @[TeXb( " /yytoksempty{/yy(1)}{/yy0{/nx/ldsectionstash{/the/tokse}{/the/toksf}/nx/ldfreestmt{/the/toksh}}}" );@]@; @[TeXa( " {/yy0{/the/yy(1)/nx/ldsectionseparator{/the/tokse}{/the/toksf}/nx/ldfreestmt{/the/toksh}}}" );@]@; @[TeXfo( "}" );@]@; @ @= @[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){stmt}{/nx/ldstatement{/nx/ldentry{/nx/ldregexp{/the/yy(3)}}}}}" );@] @ @= @[TeXb( "/getsecond{/yy(1)}/to/toksa/getthird{/yy(1)}/to/toksb" ); @]@; @[TeXf( "/getfourth{/yy(1)}/to/toksc/getfifth{/yy(1)}/to/toksd" );@]@; @[TeXfo( "/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{stmt}{/nx/ldstatement{/the/toksd}}}" );@] @ This is the default action performed by the parser when the parser writer does not supply one. For a minor gain in efficiency, this definition can be made empty. @= @[TeX_( "/yy0{/the/yy(1)}" );@]@; @ @= @[TeX_( "/ldlex@@script" );@]@; @[TeX_( "/ldfile@@open@@command@@file{/yy(2)}" );@]@; @ @= @[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){inc}{/nx/ldinclude{/the/yy(2)}}}/ldlex@@popstate" );@]@; @ \tracebadcharstrue {\it \setrulecontext{wildcard_name}The \prodstyle{'*'} and \prodstyle{'?'} cases are there because the lexer returns them as separate tokens rather than as \prodstyle{NAME}.} \tracebadcharsfalse @= @G wildcard_name: NAME {@>@@=} | '*' {@>@[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){wld}{/nx/ldregop{{*}{*}/the/yy(1)}}}" );@]@=} | '?' {@>@[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){wld}{/nx/ldregop{{?}{?}/the/yy(1)}}}" );@]@=} ; @g @ @= @G wildcard_spec: wildcard_name {@>@@=} | EXCLUDE_FILE '(' exclude_name_list ')' wildcard_name {} | SORT_BY_NAME '(' wildcard_name ')' {} | SORT_BY_ALIGNMENT '(' wildcard_name ')' {} | SORT_NONE '(' wildcard_name ')' {} | SORT_BY_NAME '(' SORT_BY_ALIGNMENT '(' wildcard_name ')' ')' {} | SORT_BY_NAME '(' SORT_BY_NAME '(' wildcard_name ')' ')' {} | SORT_BY_ALIGNMENT '(' SORT_BY_NAME '(' wildcard_name ')' ')' {} | SORT_BY_ALIGNMENT @t}\vb{\breakline}{@> '(' SORT_BY_ALIGNMENT '(' wildcard_name ')' ')' {} | SORT_BY_NAME @t}\vb{\breakline}{@> '(' EXCLUDE_FILE '(' exclude_name_list ')' wildcard_name ')' {} | SORT_BY_INIT_PRIORITY '(' wildcard_name ')' {} ; sect_flag_list: NAME {} | sect_flag_list '&' NAME {} ; sect_flags: INPUT_SECTION_FLAGS '(' sect_flag_list ')' {} ; exclude_name_list: exclude_name_list wildcard_name {} | wildcard_name {} ; file_NAME_list: file_NAME_list opt_comma wildcard_spec {@>@@=} | wildcard_spec {@>@@=} ; input_section_spec_no_keep: NAME {} | sect_flags NAME {} | '[' file_NAME_list ']' {} | sect_flags '[' file_NAME_list ']' {} | wildcard_spec '(' file_NAME_list ')' {@>@@=} | sect_flags wildcard_spec '(' file_NAME_list ')' {} ; @g @ @= @[TeXf( "/getthird{/yy(1)}/to/toksa/getfourth{/yy(1)}/to/toksb" );@]@; @[TeX_( "/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{wld}{/nx/ldregexp{/the/yy(1)}}}" );@] @ @= @[TeX_( "/getsecond{/yy(1)}/to/toksa/getthird{/yy(1)}/to/toksb" );@]@; @[TeX_( "/getfourth{/yy(1)}/to/toksc/getfifth{/yy(1)}/to/toksd" );@]@; @[TeX_( "/getsecond{/yy(2)}/to/tokse/getthird{/yy(2)}/to/toksf" );@]@; @[TeX_( "/getfourth{/yy(2)}/to/toksg/getfifth{/yy(2)}/to/toksh" );@]@; @[TeX_( "/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{flst}{/the/toksd/nx/ldspace/the/toksh}}" );@] @ @= @@; @ @= @[TeX_( "/getsecond{/yy(1)}/to/toksa/getthird{/yy(1)}/to/toksb" );@]@; @[TeX_( "/getfourth{/yy(1)}/to/toksc/getfifth{/yy(1)}/to/toksd" );@]@; @[TeX_( "/getfifth{/yy(3)}/to/toksh" );@]@; @[TeX_( "/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{sspec}{/the/toksd(/the/toksh)}}" );@] @ @= @G input_section_spec: input_section_spec_no_keep {@>@@=} | KEEP '(' {} input_section_spec_no_keep ')' {@>@@=} ; statement: assignment end {@>@@=} | CREATE_OBJECT_SYMBOLS {} | ';' {@>@[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){stmt}{}}" );@]@=} | CONSTRUCTORS {} | SORT_BY_NAME '(' CONSTRUCTORS ')' {} | input_section_spec {@>@@=} | length '(' mustbe_exp ')' {} | FILL '(' fill_exp ')' {} | ASSERT_K {@>@[TeX_( "/ldlex@@expression" );@]@=} '(' exp ',' NAME ')' end {@>@[TeX_( "/ldlex@@popstate" );@]@=} | INCLUDE filename {@>@@=} statement_list_opt END {@>@@>@=} ; statement_list: statement_list statement {@>@@=} | statement {@>@@=} ; statement_list_opt: {@>@[TeX_( "/yy0{/nx/insertcweb{}{}{stmt}{}}" );@]@=} | statement_list {@>@@=} ; @g @ @= @[TeXb( "/getsecond{/yy(1)}/to/toksa/getthird{/yy(1)}/to/toksb" ); @]@; @[TeXf( "/getfourth{/yy(1)}/to/toksc/getfifth{/yy(1)}/to/toksd" );@]@; @[TeXfo( "/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{stmt}{/nx/ldsecspec{/the/toksd}}}" );@] @ @= @[TeX_( "/getfifth{/yy(4)}/to/toksa" );@]@; @[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){stmt}{/nx/ldkeep{/the/toksa}}}" );@] @ @= @[TeX_( "/getsecond{/yy(1)}/to/toksa/getthird{/yy(1)}/to/toksb" );@]@; @[TeX_( "/getfourth{/yy(1)}/to/toksc/getfifth{/yy(1)}/to/toksd" );@]@; @[TeX_( "/getsecond{/yy(2)}/to/tokse/getthird{/yy(2)}/to/toksf" );@]@; @[TeX_( "/getfourth{/yy(2)}/to/toksg/getfifth{/yy(2)}/to/toksh" );@]@; @[TeXb( "/yytoksempty{/toksd}{/yy0{/the/yy(2)}}" );@]@; @[TeXf( " {/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{/the/toksg}{/the/toksd" );@]@; @[TeXfo( " /yytoksempty{/toksh}{}{/yytoksempty{/toksd}{}{/nx/ldor}/the/toksh}}}}" );@]@; @ @= @G @t}\vb{\inline\flatten}{@> length: QUAD {} | SQUAD {} | LONG {} | SHORT {} | BYTE {} ; @t}\vb{\resetf\inline}{@> fill_exp: mustbe_exp {@>@@=} ; @t}\vb{\flatten}{@> fill_opt: {@>@[TeX_( "/yy0{}" );@]@=} | '=' fill_exp {@>@[TeX_( "/yy0{/noexpand/ldfill{/the/yy(2)}}" );@]@=} ; @t}\vb{\resetf}{@> assign_op: PLUSEQ {@>@[TeX_( "/yy(0)={/MRL{+{/K}}}" );@]@=} | MINUSEQ {@>@[TeX_( "/yy(0)={/MRL{-{/K}}}" );@]@=} @t}\vb{\inline\flatten}{@> | MULTEQ {@>@[TeX_( "/yy(0)={/MRL*{/K}}" );@]@=} | DIVEQ {@>@[TeX_( "/yy(0)={/MRL{{/div}{/K}}}" );@]@=} | LSHIFTEQ {@>@[TeX_( "/yy(0)={/MRL{{/ll}{/K}}}" );@]@=} | RSHIFTEQ {@>@[TeX_( "/yy(0)={/MRL{{/gg}{/K}}}" );@]@=} | ANDEQ {@>@[TeX_( "/yy(0)={/Xandxeq}" );@]@=} | OREQ {@>@[TeX_( "/yy(0)={/Xorxeq}" );@]@=} ; end: ';' | ',' ; opt_comma: ',' | ; @t}\vb{\resetf}{@> @g @ Assignments are not expressions as in \Cee. @= @G assignment: NAME '=' mustbe_exp {@>@@=} | NAME assign_op mustbe_exp {@>@@=} | HIDDEN '(' NAME '=' mustbe_exp ')' {@>@@=} | PROVIDE '(' NAME '=' mustbe_exp ')' {@>@@=} | PROVIDE_HIDDEN '(' NAME '=' mustbe_exp ')' {@>@@=} ; @g @ @= @[TeXb( "/getthird{/yy(1)}/to/toksa/getfourth{/yy(1)}/to/toksb" );@]@; @[TeXfo( "/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{asgnm}{/nx/ldassignment{/nx/ldregexp{/the/yy(1)}}{/K}{/the/yy(3)}}}" );@] @ @= @[TeXb( "/getthird{/yy(1)}/to/toksa/getfourth{/yy(1)}/to/toksb" );@]@; @[TeXfo( "/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{asgnm}{/nx/ldassignment{/nx/ldregexp{/the/yy(1)}}{/the/yy(2)}{/the/yy(3)}}}" );@] @ @= @[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){hiddn}{/nx/ldhidden{/nx/ldregexp{/the/yy(3)}}{/the/yy(5)}}}" );@] @ @= @[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){prvde}{/nx/ldprovide{/nx/ldregexp{/the/yy(3)}}{/the/yy(5)}}}" );@] @ @= @[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){prhid}{/nx/ldprovidehid{/nx/ldregexp{/the/yy(3)}}{/the/yy(5)}}}" );@] @ @= @G memory: MEMORY '{' memory_spec_list_opt '}' {@>@@=} ; memory_spec_list_opt: memory_spec_list {@>@@=} | {@>@[TeX_( "/yy0{}" );@]@=} ; memory_spec_list: memory_spec_list opt_comma memory_spec {@>@@=} | memory_spec {@>@@=} ; memory_spec: NAME {} attributes_opt ':' origin_spec opt_comma length_spec {@>@@=} | INCLUDE filename {@>@@=} memory_spec_list_opt END {@>@@=} ; @g @ @= @[TeX_( "/yy0{/nx/ldinsertcweb/the/yy(1){mem}{/nx/ldmemory{/the/yy(3)/nx/ldmemspecstash/the/yy(4)}}}" );@] @ @= @[TeXb( "/getsecond{/yy(1)}/to/toksa/getthird{/yy(1)}/to/toksb" ); @]@; @[TeXf( "/getfifth{/yy(1)}/to/toksc" );@]@; @[TeXfo( "/yy0{/nx/ldmemspecstash{/the/toksa}{/the/toksb}/the/toksc}" );@]@; @ @= @[TeXb( "/getsecond{/yy(3)}/to/toksa/getthird{/yy(3)}/to/toksb" ); @]@; @[TeXf( "/getfifth{/yy(3)}/to/toksc" );@]@; @[TeXfo( "/yy0{/the/yy(1)/nx/ldmemspecseparator{/the/toksa}{/the/toksb}/the/toksc}" );@]@; @ @= @[TeXb( "/getthird{/yy(1)}/to/toksa/getfourth{/yy(1)}/to/toksb" );@]@; @[TeXf( "/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{mreg}" );@]@; @[TeXfo( " {/nx/ldmemoryspec{/the/yy(1)}{/the/yy(3)}{/the/yy(5)}{/the/yy(7)}}}" );@] @ @= @G origin_spec: ORIGIN '=' mustbe_exp {@>@[TeX_( "/yy0{/noexpand/ldoriginspec{/the/yy(3)}}" );@]@=} ; length_spec: LENGTH '=' mustbe_exp {@>@[TeX_( "/yy0{/noexpand/ldlengthspec{/the/yy(3)}}" );@]@=} ; attributes_opt: {@>@[TeX_( "/yy0{}" );@]@=} | '(' attributes_list ')' {@>@[TeX_( "/yy0{/the/yy(2)}" );@]@=} ; attributes_list: attributes_string {@>@[TeX_( "/yy0{/the/yy(1)}" );@]@=} | attributes_list attributes_string {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/ldspace/the/yy(2)}" );@]@=} ; attributes_string: NAME {@>@[TeX_( "/yy0{/noexpand/ldattrstring/the/yy(1)}" );@]@=} | '!' NAME {@>@[TeX_( "/yy0{/noexpand/ldattrstringneg/the/yy(2)}" );@]@=} ; startup: STARTUP '(' filename ')' {} ; high_level_library: HLL '(' high_level_library_NAME_list ')' | HLL '(' ')' {} ; high_level_library_NAME_list: high_level_library_NAME_list opt_comma filename {} | filename {} ; low_level_library: SYSLIB '(' low_level_library_NAME_list ')' {} ; low_level_library_NAME_list: low_level_library_NAME_list opt_comma filename {} | {} ; floating_point_support: FLOAT {} | NOFLOAT {} ; nocrossref_list: {} | NAME nocrossref_list {} | NAME ',' nocrossref_list {} ; mustbe_exp: {@>@[TeX_( "/ldlex@@expression" );@]@=} exp {@>@[TeX_( "/ldlex@@popstate/yy0{/the/yy(2)}" );@]@=} ; @g @*1 {\ifheader\ninepoint\fi\prodstylens{SECTIONS}{\ldnamespace}} and expressions. The linker supports an extensive range of expressions. The precedence mechanism provided by \bison\ is used to present the composition of expressions out of simpler chunks and basic building blocks tied together by algebraic operations. @= @G exp: '-' exp %prec UNARY {@>@[TeX_( "/yy0{{-/the/yy(2)}}" );@]@=} | '(' exp ')' {@>@[TeX_( "/yy0{(/the/yy(2))}" );@]@=} | NEXT '(' exp ')' %prec UNARY {@>@[TeX_( "/yy0{/hbox{/nx/ssf next}(/the/yy(3))}" );@]@=} | '!' exp %prec UNARY {@>@[TeX_( "/yy0{{/noexpand/CM/the/yy(2)}}" );@]@=} | '+' exp %prec UNARY {@>@[TeX_( "/yy0{{+/the/yy(2)}}" );@]@=} | '~' exp %prec UNARY {@>@[TeX_( "/yy0{{/noexpand/R/the/yy(2)}}" );@]@=} | exp '*' exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/times/the/yy(3)}" );@]@=} | exp '/' exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand///the/yy(3)}" );@]@=} | exp '%' exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/div/the/yy(3)}" );@]@=} | exp '+' exp {@>@[TeX_( "/yy0{/the/yy(1)+/the/yy(3)}" );@]@=} | exp '-' exp {@>@[TeX_( "/yy0{/the/yy(1)-/the/yy(3)}" );@]@=} | exp LSHIFT exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/ll/the/yy(3)}" );@]@=} | exp RSHIFT exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/gg/the/yy(3)}" );@]@=} | exp EQ exp {@>@[TeX_( "/yy0{/the/yy(1)=/the/yy(3)}" );@]@=} | exp NE exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/not=/the/yy(3)}" );@]@=} | exp LE exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/leq/the/yy(3)}" );@]@=} | exp GE exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/geq/the/yy(3)}" );@]@=} | exp '<' exp {@>@[TeX_( "/yy0{/the/yy(1)' exp {@>@[TeX_( "/yy0{/the/yy(1)>/the/yy(3)}" );@]@=} | exp '&' exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/AND/the/yy(3)}" );@]@=} | exp '^' exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/XOR/the/yy(3)}" );@]@=} | exp '|' exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/OR/the/yy(3)}" );@]@=} | exp '?' exp ':' exp {@>@@=} | exp ANDAND exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/V/the/yy(3)}" );@]@=} | exp OROR exp {@>@[TeX_( "/yy0{/the/yy(1)/noexpand/W/the/yy(3)}" );@]@=} @g @ More atomic expression types specific to the linker's function. @= @G exp : DEFINED '(' NAME ')' {@>@[TeX_( "/def/ldfuncname{defined}" );@]@@=} | INT {@>@@=} | SIZEOF_HEADERS {} | ALIGNOF '(' NAME ')' {@>@[TeX_( "/def/ldfuncname{alignof}" );@]@@=} | SIZEOF '(' NAME ')' {@>@[TeX_( "/def/ldfuncname{sizeof}" );@]@@=} | ADDR '(' NAME ')' {@>@[TeX_( "/def/ldfuncname{addr}" );@]@@=} | LOADADDR '(' NAME ')' {@>@[TeX_( "/def/ldfuncname{loadaddr}" );@]@@=} | CONSTANT '(' NAME ')' {@>@[TeX_( "/def/ldfuncname{constant}" );@]@@=} | ABSOLUTE '(' exp ')' {@>@[TeX_( "/yy0{/nx/mathop{/hbox{/nx/ssf absolute}}(/the/yy(3))}" );@]@=} | ALIGN_K '(' exp ')' {@>@[TeX_( "/yy0{/nx/mathop{/hbox{/nx/ssf align}}(/the/yy(3))}" );@]@=} | ALIGN_K '(' exp ',' exp ')' {} | DATA_SEGMENT_ALIGN '(' exp ',' exp ')' {} | DATA_SEGMENT_RELRO_END '(' exp ',' exp ')' {} | DATA_SEGMENT_END '(' exp ')' {} | SEGMENT_START '(' NAME ',' exp ')' {} | BLOCK '(' exp ')' {@>@[TeX_( "/yy0{/nx/mathop{/hbox{/nx/ssf block}}(/the/yy(3))}" );@]@=} | NAME {@>@[TeX_( "/yy0{/nx/ldregexp{/the/yy(1)}}" );@]@=} | MAX_K '(' exp ',' exp ')' {} | MIN_K '(' exp ',' exp ')' {} | ASSERT_K '(' exp ',' NAME ')' {} | ORIGIN '(' NAME ')' {@>@[TeX_( "/def/ldfuncname{origin}" );@]@@=} | LENGTH '(' NAME ')' {@>@[TeX_( "/def/ldfuncname{length}" );@]@@=} | LOG2CEIL '(' exp ')' {} ; @g @ @= @q TeX_( "/yy0{/hbox{/nx/ttl let }/nx/xi(0)=/the/yy(5): /nx/xi(/nx/CM0)=/the/yy(3)/hbox{ /nx/ttl do }/xi(/the/yy(1))}" );@> @[TeXb( "/yy0{/hbox{/nx/ttl do }/xi(/the/yy(1))/hbox{ /nx/ttl where }" );@]@; @[TeXfo( " {/let/nx/{/nx/bigbracedel/nx/xi(x)=/nx/cases{/the/yy(5)& if /inmath{x=0}/cr/the/yy(3)& if /inmath{x/nx/not=0}}}}" );@]@; @ @= @[TeX_( "/yy0{/nx/mathop{/hbox{/nx/ssf /ldfuncname}}(/nx/ldregexp{/the/yy(3))}}" );@] @ @= @G memspec_at_opt: AT '>' NAME {@>@[TeX_( "/yy0{/the/yy(3)}" );@]@=} | {@>@[TeX_( "/yy0{}" );@]@=} ; opt_at: AT '(' exp ')' {@>@[TeX_( "/yy0{/the/yy(3)}" );@]@=} | {@>@[TeX_( "/yy0{}" );@]@=} ; opt_align: ALIGN_K '(' exp ')' {@>@[TeX_( "/yy0{/the/yy(3)}" );@]@=} | {@>@[TeX_( "/yy0{}" );@]@=} ; opt_align_with_input: ALIGN_WITH_INPUT {@>@[TeX_( "/yy0{align with input}" );@]@=} | {@>@[TeX_( "/yy0{}" );@]@=} ; opt_subalign: SUBALIGN '(' exp ')' {@>@[TeX_( "/yy0{/the/yy(3)}" );@]@=} | {@>@[TeX_( "/yy0{}" );@]@=} ; sect_constraint: ONLY_IF_RO {@>@[TeX_( "/yy0{only/_if/_ro}" );@]@=} | ONLY_IF_RW {@>@[TeX_( "/yy0{only/_if/_rw}" );@]@=} | SPECIAL {@>@[TeX_( "/yy0{special}" );@]@=} | {@>@[TeX_( "/yy0{}" );@]@=} ; @g @ {\it The \prodstyle{GROUP} case is just enough to support the \gcc\ |svr3.ifile| script. It is not intended to be full support. I'm not even sure what \prodstyle{GROUP} is supposed to mean.} A careful analysis of the productions below reveals some pitfalls in the parser/lexer interaction setup that uses the state switching macros (or functions in the case of the original parser). The switch to the \.{EXPRESSION} state at the end of the production for \prodstyle{section} is invoked before \prodstyle{opt\_comma} which can be empty. This means that the next (lookahead) token (which could be a \prodstyle{NAME} in a different context) might be read before the lexer is in the appropriate state. In practice, the names of the sections and other \prodstyle{NAME}s are usually pretty straightforward so this parser idiosynchrasy is unlikely to lead to a genuine problem. Since the goal was to keep the original grammar intact as much as possible, it was decided to leave this production unchanged. @= @G section: NAME {@>@[TeX_( "/ldlex@@expression" );@]@=} opt_exp_with_type opt_at opt_align opt_align_with_input @t}\vb{\breakline}{@> opt_subalign {@>@[TeX_( "/ldlex@@popstate/ldlex@@script" );@]@=} sect_constraint '{' {} statement_list_opt '}' {@>@[TeX_( "/ldlex@@popstate/ldlex@@expression" );@]@=} memspec_opt memspec_at_opt phdr_opt fill_opt {@>@[TeX_( "/ldlex@@popstate" );@]@=} opt_comma {@>@@=} | OVERLAY {@>@[TeX_( "/ldlex@@expression" );@]@=} opt_exp_without_type opt_nocrossrefs opt_at opt_subalign {@>@[TeX_( "/ldlex@@popstate/ldlex@@script" );@]@=} '{' {} overlay_section '}' {@>@[TeX_( "/ldlex@@popstate/ldlex@@expression" );@]@=} memspec_opt memspec_at_opt phdr_opt fill_opt {@>@[TeX_( "/ldlex@@popstate" );@]@=} opt_comma {@>@@=} | GROUP {@>@[TeX_( "/ldlex@@expression" );@]@=} opt_exp_with_type {@>@[TeX_( "/ldlex@@popstate" );@]@=} '{' sec_or_group_p1 '}' {} | INCLUDE filename {@>@@=} sec_or_group_p1 END {@>@@=} ; @g @ @= @[TeXb( "/getthird{/yy(1)}/to/toksa/getfourth{/yy(1)}/to/toksb" );@]@; @[TeXf( "/getfifth{/yy(12)}/to/toksc" );@]@;/* \prodstylens{statement\_list\_opt}{\ldnamespace} contents */ @[TeXf( "/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{osect}{/nx/ldnamedsection{/the/yy(1)}{/the/yy(3)}{/the/yy(4)}" );@]@; @[TeXf( " {{/the/yy(5)}{/the/yy(6)}{/the/yy(7)}}" );@]@;/* alignment */ @[TeXf( " {/the/yy(9)}{/the/toksc}" );@]@; @[TeXfo( " {{/the/yy(15)}{/the/yy(16)}{/the/yy(17)}{/the/yy(18)}}}}" );@]@; /*memory specifiers */ @ @= @[TeXb( "/getfirst{/yy(1)}/to/toksa/getsecond{/yy(1)}/to/toksb" );@]@; @[TeXf( "/yy0{/nx/ldinsertcweb{/the/toksa}{/the/toksb}{osect}{/nx/ldoverlay{/the/yy(3)}{/the/yy(4)}{/the/yy(5)}{/the/yy(6)}" );@]@; @[TeXf( " {/the/yy(10)}" );@]@;/* sections */ @[TeXfo( " {{/the/yy(13)}{/the/yy(14)}{/the/yy(15)}{/the/yy(16)}}}}" );@]@; /*memory specifiers, etc.*/ @ @= @G type: NOLOAD {@>@[TeX_( "/yy0{noload}" );@]@=} | DSECT {@>@[TeX_( "/yy0{dsect}" );@]@=} | COPY {@>@[TeX_( "/yy0{copy}" );@]@=} | INFO {@>@[TeX_( "/yy0{info}" );@]@=} | OVERLAY {@>@[TeX_( "/yy0{overlay}" );@]@=} ; atype: '(' type ')' {@>@[TeX_( "/yy0{/noexpand/ldtype{/the/yy(2)}}" );@]@=} | '(' ')' {@>@[TeX_( "/yy0{/noexpand/ldtype{}}" );@]@=} | {@>@[TeX_( "/yy0{}" );@]@=} ; @g @ {\it The \prodstyle{BIND} cases are to support the \gcc\ |svr3.ifile| script. They aren't intended to implement full support for the \prodstyle{BIND} keyword. I'm not even sure what \prodstyle{BIND} is supposed to mean.} @= @G opt_exp_with_type: exp atype ':' {@>@[TeX_( "/yy0{{}{/the/yy(1)}{}{}{/the/yy(2)}}" );@]@=} | atype ':' {@>@[TeX_( "/yy0{{}{}{}{}{/the/yy(1)}}" );@]@=} | BIND '(' exp ')' atype ':' {@>@[TeX_( "/yy0{{bind}{/the/yy(3)}{}{}{/the/yy(5)}}" );@]@=} | BIND '(' exp ')' BLOCK '(' exp ')' atype ':' {@>@[TeX_( "/yy0{{bind}{/the/yy(3)}{block}{/the/yy(7)}{/the/yy(9)}}" );@]@=} ; opt_exp_without_type: exp ':' {@>@[TeX_( "/yy0{/the/yy(1)}" );@]@=} | ':' {@>@[TeX_( "/yy0{}" );@]@=} ; opt_nocrossrefs: {} | NOCROSSREFS {} ; memspec_opt: '>' NAME {@>@[TeX_( "/yy0{/the/yy(2)}" );@]@=} | {@>@[TeX_( "/yy0{}" );@]@=} ; phdr_opt: {@>@[TeX_( "/yy0{}" );@]@=} | phdr_opt ':' NAME {@>@@=} ; overlay_section: {@>@[TeX_( "/yy0{}" );@]@=} | overlay_section NAME {@>@[TeX_( "/ldlex@@script" );@]@=} '{' statement_list_opt '}' {@>@[TeX_( "/ldlex@@popstate/ldlex@@expression" );@]@=} phdr_opt fill_opt {@>@[TeX_( "/ldlex@@popstate" );@]@=} opt_comma {@>@@=} ; @g @ @= @[TeXb( "/yytoksempty{/yy(1)}{/yy0{{/the/yy(3)}}}" );@]@; @[TeXao( "{/yy0{/the/yy(1)/noexpand/ldor{/the/yy(3)}}}" );@]@; @ @= @[TeXb( "/getthird{/yy(2)}/to/toksa/getfourth{/yy(2)}/to/toksb" );@]@;/* \prodstylens{name}{\ldnamespace} pointers */ @[TeXf( "/getfifth{/yy(5)}/to/toksc" );@]@;/* \prodstylens{statement\_list\_opt}{\ldnamespace} contents */ @[TeXf( "/yytoksempty{/yy(1)}{" );@]@; @[TeXf( " /yy0{/nx/ldoverlaysection{/the/yy(2)}{/the/toksc}{/the/yy(8)}{/the/yy(9)}}" );@]@; @[TeXf( "}{" );@]@; @[TeXf( " /yy0{/the/yy(1)/nx/ldoverlaysection{/the/yy(2)}{/the/toksc}{/the/yy(8)}{/the/yy(9)}}" );@]@; @[TeXfo( "}" );@]@; @ @= @G phdrs: PHDRS '{' phdr_list '}' ; phdr_list: | phdr_list phdr ; phdr: NAME {@>@[TeX_( "/ldlex@@expression" );@]@=} phdr_type phdr_qualifiers {@>@[TeX_( "/ldlex@@popstate" );@]@=} ';' {} ; phdr_type: exp {} ; phdr_qualifiers: {} | NAME phdr_val phdr_qualifiers {} | AT '(' exp ')' phdr_qualifiers {} ; phdr_val: {} | '(' exp ')' {} ; @g @*1 Other types of script files. At present time other script types are ignored, although some of the rules are used in linker scripts that are processed by the parser. @= @G dynamic_list_file: {@>@[TeX_( "/ldlex@@version@@file" );@]@=} dynamic_list_nodes {@>@[TeX_( "/ldlex@@popstate" );@]@=} ; dynamic_list_nodes: dynamic_list_node | dynamic_list_nodes dynamic_list_node ; dynamic_list_node: '{' dynamic_list_tag '}' ';' ; dynamic_list_tag: vers_defns ';' {} ; @g @ {\it This syntax is used within an external version script file.} @= @G version_script_file: {@>@[TeX_( "/ldlex@@version@@file" );@]@=} vers_nodes {@>@[TeX_( "/ldlex@@popstate" );@]@=} ; @g @ {\it This is used within a normal linker script file.} @= @G version: {@>@[TeX_( "/ldlex@@version@@script" );@]@=} VERSIONK '{' vers_nodes '}' {@>@[TeX_( "/ldlex@@popstate" );@]@=} ; vers_nodes: vers_node | vers_nodes vers_node ; vers_node: '{' vers_tag '}' ';' {} | VERS_TAG '{' vers_tag '}' ';' {} | VERS_TAG '{' vers_tag '}' verdep ';' {} ; verdep: VERS_TAG {} | verdep VERS_TAG {} ; vers_tag: {} | vers_defns ';' {} | GLOBAL ':' vers_defns ';' {} | LOCAL ':' vers_defns ';' {} | GLOBAL ':' vers_defns ';' LOCAL ':' vers_defns ';' {} ; vers_defns: VERS_IDENTIFIER {} | NAME {} | vers_defns ';' VERS_IDENTIFIER {} | vers_defns ';' NAME {} | vers_defns ';' EXTERN NAME '{' {} vers_defns opt_semicolon '}' {} | EXTERN NAME '{' {} vers_defns opt_semicolon '}' {} | GLOBAL {} | vers_defns ';' GLOBAL {} | LOCAL {} | vers_defns ';' LOCAL {} | EXTERN {} | vers_defns ';' EXTERN {} ; @t}\vb{\inline\flatten}{@> opt_semicolon: | ';' ; @g