| /* yyscript.y -- linker script grammar for gold.  */ | 
 |  | 
 | /* Copyright (C) 2006-2025 Free Software Foundation, Inc. | 
 |    Written by Ian Lance Taylor <iant@google.com>. | 
 |  | 
 |    This file is part of gold. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program; if not, write to the Free Software | 
 |    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, | 
 |    MA 02110-1301, USA.  */ | 
 |  | 
 | /* This is a bison grammar to parse a subset of the original GNU ld | 
 |    linker script language.  */ | 
 |  | 
 | %{ | 
 |  | 
 | #include "config.h" | 
 | #include "diagnostics.h" | 
 |  | 
 | #include <stddef.h> | 
 | #include <stdint.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | #include "script-c.h" | 
 |  | 
 | DIAGNOSTIC_IGNORE_UNUSED_BUT_SET_VARIABLE | 
 |  | 
 | %} | 
 |  | 
 | /* We need to use a pure parser because we might be multi-threaded. | 
 |    We pass some arguments through the parser to the lexer.  */ | 
 |  | 
 | %pure-parser | 
 |  | 
 | %parse-param {void* closure} | 
 | %lex-param {void* closure} | 
 |  | 
 | /* Since we require bison anyhow, we take advantage of it.  */ | 
 |  | 
 | %error-verbose | 
 |  | 
 | /* The values associated with tokens.  */ | 
 |  | 
 | %union { | 
 |   /* A string.  */ | 
 |   struct Parser_string string; | 
 |   /* A number.  */ | 
 |   uint64_t integer; | 
 |   /* An expression.  */ | 
 |   Expression_ptr expr; | 
 |   /* An output section header.  */ | 
 |   struct Parser_output_section_header output_section_header; | 
 |   /* An output section trailer.  */ | 
 |   struct Parser_output_section_trailer output_section_trailer; | 
 |   /* A section constraint.  */ | 
 |   enum Section_constraint constraint; | 
 |   /* A complete input section specification.  */ | 
 |   struct Input_section_spec input_section_spec; | 
 |   /* A list of wildcard specifications, with exclusions.  */ | 
 |   struct Wildcard_sections wildcard_sections; | 
 |   /* A single wildcard specification.  */ | 
 |   struct Wildcard_section wildcard_section; | 
 |   /* A list of strings.  */ | 
 |   String_list_ptr string_list; | 
 |   /* Information for a program header.  */ | 
 |   struct Phdr_info phdr_info; | 
 |   /* Used for version scripts and within VERSION {}.  */ | 
 |   struct Version_dependency_list* deplist; | 
 |   struct Version_expression_list* versyms; | 
 |   struct Version_tree* versnode; | 
 |   enum Script_section_type section_type; | 
 | } | 
 |  | 
 | /* Operators, including a precedence table for expressions.  */ | 
 |  | 
 | %right PLUSEQ MINUSEQ MULTEQ DIVEQ '=' LSHIFTEQ RSHIFTEQ ANDEQ OREQ | 
 | %right '?' ':' | 
 | %left OROR | 
 | %left ANDAND | 
 | %left '|' | 
 | %left '^' | 
 | %left '&' | 
 | %left EQ NE | 
 | %left '<' '>' LE GE | 
 | %left LSHIFT RSHIFT | 
 | %left '+' '-' | 
 | %left '*' '/' '%' | 
 |  | 
 | /* A fake operator used to indicate unary operator precedence.  */ | 
 | %right UNARY | 
 |  | 
 | /* Constants.  */ | 
 |  | 
 | %token <string> STRING | 
 | %token <string> QUOTED_STRING | 
 | %token <integer> INTEGER | 
 |  | 
 | /* Keywords.  This list is taken from ldgram.y and ldlex.l in the old | 
 |    GNU linker, with the keywords which only appear in MRI mode | 
 |    removed.  Not all these keywords are actually used in this grammar. | 
 |    In most cases the keyword is recognized as the token name in upper | 
 |    case.  The comments indicate where this is not the case.  */ | 
 |  | 
 | %token ABSOLUTE | 
 | %token ADDR | 
 | %token ALIGN_K		/* ALIGN */ | 
 | %token ALIGNOF | 
 | %token ASSERT_K		/* ASSERT */ | 
 | %token AS_NEEDED | 
 | %token AT | 
 | %token BIND | 
 | %token BLOCK | 
 | %token BYTE | 
 | %token CONSTANT | 
 | %token CONSTRUCTORS | 
 | %token COPY | 
 | %token CREATE_OBJECT_SYMBOLS | 
 | %token DATA_SEGMENT_ALIGN | 
 | %token DATA_SEGMENT_END | 
 | %token DATA_SEGMENT_RELRO_END | 
 | %token DEFINED | 
 | %token DSECT | 
 | %token ENTRY | 
 | %token EXCLUDE_FILE | 
 | %token EXTERN | 
 | %token FILL | 
 | %token FLOAT | 
 | %token FORCE_COMMON_ALLOCATION | 
 | %token GLOBAL		/* global */ | 
 | %token GROUP | 
 | %token HIDDEN | 
 | %token HLL | 
 | %token INCLUDE | 
 | %token INHIBIT_COMMON_ALLOCATION | 
 | %token INFO | 
 | %token INPUT | 
 | %token KEEP | 
 | %token LEN | 
 | %token LENGTH		/* LENGTH, l, len */ | 
 | %token LOADADDR | 
 | %token LOCAL		/* local */ | 
 | %token LONG | 
 | %token MAP | 
 | %token MAX_K		/* MAX */ | 
 | %token MEMORY | 
 | %token MIN_K		/* MIN */ | 
 | %token NEXT | 
 | %token NOCROSSREFS | 
 | %token NOFLOAT | 
 | %token NOLOAD | 
 | %token ONLY_IF_RO | 
 | %token ONLY_IF_RW | 
 | %token ORG | 
 | %token ORIGIN		/* ORIGIN, o, org */ | 
 | %token OUTPUT | 
 | %token OUTPUT_ARCH | 
 | %token OUTPUT_FORMAT | 
 | %token OVERLAY | 
 | %token PHDRS | 
 | %token PROVIDE | 
 | %token PROVIDE_HIDDEN | 
 | %token QUAD | 
 | %token SEARCH_DIR | 
 | %token SECTIONS | 
 | %token SEGMENT_START | 
 | %token SHORT | 
 | %token SIZEOF | 
 | %token SIZEOF_HEADERS	/* SIZEOF_HEADERS, sizeof_headers */ | 
 | %token SORT_BY_ALIGNMENT | 
 | %token SORT_BY_INIT_PRIORITY | 
 | %token SORT_BY_NAME | 
 | %token SPECIAL | 
 | %token SQUAD | 
 | %token STARTUP | 
 | %token SUBALIGN | 
 | %token SYSLIB | 
 | %token TARGET_K		/* TARGET */ | 
 | %token TRUNCATE | 
 | %token VERSIONK		/* VERSION */ | 
 |  | 
 | /* Keywords, part 2.  These are keywords that are unique to gold, | 
 |    and not present in the old GNU linker.  As before, unless the | 
 |    comments say otherwise, the keyword is recognized as the token | 
 |    name in upper case. */ | 
 |  | 
 | %token OPTION | 
 |  | 
 | /* Special tokens used to tell the grammar what type of tokens we are | 
 |    parsing.  The token stream always begins with one of these tokens. | 
 |    We do this because version scripts can appear embedded within | 
 |    linker scripts, and because --defsym uses the expression | 
 |    parser.  */ | 
 | %token PARSING_LINKER_SCRIPT | 
 | %token PARSING_VERSION_SCRIPT | 
 | %token PARSING_DEFSYM | 
 | %token PARSING_DYNAMIC_LIST | 
 | %token PARSING_SECTIONS_BLOCK | 
 | %token PARSING_SECTION_COMMANDS | 
 | %token PARSING_MEMORY_DEF | 
 |  | 
 | /* Non-terminal types, where needed.  */ | 
 |  | 
 | %type <expr> parse_exp exp | 
 | %type <expr> opt_at opt_align opt_subalign opt_fill | 
 | %type <output_section_header> section_header opt_address_and_section_type | 
 | %type <section_type> section_type | 
 | %type <output_section_trailer> section_trailer | 
 | %type <constraint> opt_constraint | 
 | %type <string_list> opt_phdr | 
 | %type <integer> data_length | 
 | %type <input_section_spec> input_section_no_keep | 
 | %type <wildcard_sections> wildcard_sections | 
 | %type <wildcard_section> wildcard_file wildcard_section | 
 | %type <string_list> exclude_names | 
 | %type <string> wildcard_name | 
 | %type <integer> phdr_type memory_attr | 
 | %type <phdr_info> phdr_info | 
 | %type <versyms> vers_defns | 
 | %type <versnode> vers_tag | 
 | %type <deplist> verdep | 
 | %type <string> string | 
 |  | 
 | %% | 
 |  | 
 | /* Read the special token to see what to read next.  */ | 
 | top: | 
 | 	  PARSING_LINKER_SCRIPT linker_script | 
 | 	| PARSING_VERSION_SCRIPT version_script | 
 | 	| PARSING_DEFSYM defsym_expr | 
 |         | PARSING_DYNAMIC_LIST dynamic_list_expr | 
 |         | PARSING_SECTIONS_BLOCK sections_block | 
 |         | PARSING_SECTION_COMMANDS section_cmds | 
 |         | PARSING_MEMORY_DEF memory_defs | 
 | 	; | 
 |  | 
 | /* A file contains a list of commands.  */ | 
 | linker_script: | 
 | 	  linker_script file_cmd | 
 | 	| /* empty */ | 
 | 	; | 
 |  | 
 | /* A command which may appear at top level of a linker script.  */ | 
 | file_cmd: | 
 | 	  EXTERN '(' extern_name_list ')' | 
 | 	| FORCE_COMMON_ALLOCATION | 
 | 	    { script_set_common_allocation(closure, 1); } | 
 | 	| GROUP | 
 | 	    { script_start_group(closure); } | 
 | 	  '(' input_list ')' | 
 | 	    { script_end_group(closure); } | 
 | 	| INHIBIT_COMMON_ALLOCATION | 
 | 	    { script_set_common_allocation(closure, 0); } | 
 | 	| INPUT '(' input_list ')' | 
 | 	| MEMORY '{' memory_defs '}' | 
 |         | OPTION '(' string ')' | 
 | 	    { script_parse_option(closure, $3.value, $3.length); } | 
 | 	| OUTPUT_FORMAT '(' string ')' | 
 | 	    { | 
 | 	      if (!script_check_output_format(closure, $3.value, $3.length, | 
 | 					      NULL, 0, NULL, 0)) | 
 | 		YYABORT; | 
 | 	    } | 
 | 	| OUTPUT_FORMAT '(' string ',' string ',' string ')' | 
 | 	    { | 
 | 	      if (!script_check_output_format(closure, $3.value, $3.length, | 
 | 					      $5.value, $5.length, | 
 | 					      $7.value, $7.length)) | 
 | 		YYABORT; | 
 | 	    } | 
 | 	| PHDRS '{' phdrs_defs '}' | 
 | 	| SEARCH_DIR '(' string ')' | 
 | 	    { script_add_search_dir(closure, $3.value, $3.length); } | 
 | 	| SECTIONS '{' | 
 | 	    { script_start_sections(closure); } | 
 | 	  sections_block '}' | 
 | 	    { script_finish_sections(closure); } | 
 | 	| TARGET_K '(' string ')' | 
 | 	    { script_set_target(closure, $3.value, $3.length); } | 
 |         | VERSIONK '{' | 
 |             { script_push_lex_into_version_mode(closure); } | 
 |           version_script '}' | 
 |             { script_pop_lex_mode(closure); } | 
 | 	| ENTRY '(' string ')' | 
 | 	    { script_set_entry(closure, $3.value, $3.length); } | 
 | 	| assignment end | 
 | 	| ASSERT_K '(' parse_exp ',' string ')' | 
 | 	    { script_add_assertion(closure, $3, $5.value, $5.length); } | 
 | 	| INCLUDE string | 
 | 	    { script_include_directive(PARSING_LINKER_SCRIPT, closure, | 
 | 				       $2.value, $2.length); } | 
 | 	| ignore_cmd | 
 | 	| ';' | 
 | 	; | 
 |  | 
 | /* Top level commands which we ignore.  The GNU linker uses these to | 
 |    select the output format, but we don't offer a choice.  Ignoring | 
 |    these is more-or-less OK since most scripts simply explicitly | 
 |    choose the default.  */ | 
 | ignore_cmd: | 
 | 	  OUTPUT_ARCH '(' string ')' | 
 | 	; | 
 |  | 
 | /* A list of external undefined symbols.  We put the lexer into | 
 |    expression mode so that commas separate names; this is what the GNU | 
 |    linker does.  */ | 
 |  | 
 | extern_name_list: | 
 | 	    { script_push_lex_into_expression_mode(closure); } | 
 | 	  extern_name_list_body | 
 | 	    { script_pop_lex_mode(closure); } | 
 | 	; | 
 |  | 
 | extern_name_list_body: | 
 | 	  string | 
 | 	    { script_add_extern(closure, $1.value, $1.length); } | 
 | 	| extern_name_list_body string | 
 | 	    { script_add_extern(closure, $2.value, $2.length); } | 
 | 	| extern_name_list_body ',' string | 
 | 	    { script_add_extern(closure, $3.value, $3.length); } | 
 | 	; | 
 |  | 
 | /* A list of input file names.  */ | 
 | input_list: | 
 | 	  input_list_element | 
 | 	| input_list opt_comma input_list_element | 
 | 	; | 
 |  | 
 | /* An input file name.  */ | 
 | input_list_element: | 
 | 	  string | 
 | 	    { script_add_file(closure, $1.value, $1.length); } | 
 | 	| '-' STRING | 
 | 	    { script_add_library(closure, $2.value, $2.length); } | 
 | 	| AS_NEEDED | 
 | 	    { script_start_as_needed(closure); } | 
 | 	  '(' input_list ')' | 
 | 	    { script_end_as_needed(closure); } | 
 | 	; | 
 |  | 
 | /* Commands in a SECTIONS block.  */ | 
 | sections_block: | 
 | 	  sections_block section_block_cmd | 
 | 	| /* empty */ | 
 | 	; | 
 |  | 
 | /* A command which may appear within a SECTIONS block.  */ | 
 | section_block_cmd: | 
 | 	  ENTRY '(' string ')' | 
 | 	    { script_set_entry(closure, $3.value, $3.length); } | 
 | 	| assignment end | 
 | 	| ASSERT_K '(' parse_exp ',' string ')' | 
 | 	    { script_add_assertion(closure, $3, $5.value, $5.length); } | 
 | 	| INCLUDE string | 
 | 	    { script_include_directive(PARSING_SECTIONS_BLOCK, closure, | 
 | 				       $2.value, $2.length); } | 
 | 	| string section_header | 
 | 	    { script_start_output_section(closure, $1.value, $1.length, &$2); } | 
 | 	  '{' section_cmds '}' section_trailer | 
 | 	    { script_finish_output_section(closure, &$7); } | 
 | 	; | 
 |  | 
 | /* The header of an output section in a SECTIONS block--everything | 
 |    after the name.  */ | 
 | section_header: | 
 | 	    { script_push_lex_into_expression_mode(closure); } | 
 | 	  opt_address_and_section_type opt_at opt_align opt_subalign | 
 | 	    { script_pop_lex_mode(closure); } | 
 | 	  opt_constraint | 
 | 	    { | 
 | 	      $$.address = $2.address; | 
 | 	      $$.section_type = $2.section_type; | 
 | 	      $$.load_address = $3; | 
 | 	      $$.align = $4; | 
 | 	      $$.subalign = $5; | 
 | 	      $$.constraint = $7; | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* The optional address followed by the optional section type.  This | 
 |    is a separate nonterminal to avoid a shift/reduce conflict on | 
 |    '(' in section_header.  */ | 
 |  | 
 | opt_address_and_section_type: | 
 | 	':' | 
 | 	    { | 
 | 	      $$.address = NULL; | 
 | 	      $$.section_type = SCRIPT_SECTION_TYPE_NONE; | 
 | 	    } | 
 | 	| '(' ')' ':' | 
 | 	    { | 
 | 	      $$.address = NULL; | 
 | 	      $$.section_type = SCRIPT_SECTION_TYPE_NONE; | 
 | 	    } | 
 | 	| exp ':' | 
 | 	    { | 
 | 	      $$.address = $1; | 
 | 	      $$.section_type = SCRIPT_SECTION_TYPE_NONE; | 
 | 	    } | 
 | 	| exp '(' ')' ':' | 
 | 	    { | 
 | 	      $$.address = $1; | 
 | 	      $$.section_type = SCRIPT_SECTION_TYPE_NONE; | 
 | 	    } | 
 | 	| '(' section_type ')' ':' | 
 | 	    { | 
 | 	      $$.address = NULL; | 
 | 	      $$.section_type = $2; | 
 | 	    } | 
 | 	| exp '(' section_type ')' ':' | 
 | 	    { | 
 | 	      $$.address = $1; | 
 | 	      $$.section_type = $3; | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* We only support NOLOAD.  */ | 
 | section_type: | 
 | 	NOLOAD | 
 | 	    { $$ = SCRIPT_SECTION_TYPE_NOLOAD; } | 
 | 	| DSECT | 
 | 	    { | 
 | 	      yyerror(closure, "DSECT section type is unsupported"); | 
 | 	      $$ = SCRIPT_SECTION_TYPE_DSECT; | 
 | 	    } | 
 | 	| COPY | 
 | 	    { | 
 | 	      yyerror(closure, "COPY section type is unsupported"); | 
 | 	      $$ = SCRIPT_SECTION_TYPE_COPY; | 
 | 	    } | 
 | 	| INFO | 
 | 	    { | 
 | 	      yyerror(closure, "INFO section type is unsupported"); | 
 | 	      $$ = SCRIPT_SECTION_TYPE_INFO; | 
 | 	    } | 
 | 	| OVERLAY | 
 | 	    { | 
 | 	      yyerror(closure, "OVERLAY section type is unsupported"); | 
 | 	      $$ = SCRIPT_SECTION_TYPE_OVERLAY; | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* The address at which an output section should be loaded.  */ | 
 | opt_at: | 
 | 	  /* empty */ | 
 | 	    { $$ = NULL; } | 
 | 	| AT '(' exp ')' | 
 | 	    { $$ = $3; } | 
 | 	; | 
 |  | 
 | /* The alignment of an output section.  */ | 
 | opt_align: | 
 | 	  /* empty */ | 
 | 	    { $$ = NULL; } | 
 | 	| ALIGN_K '(' exp ')' | 
 | 	    { $$ = $3; } | 
 | 	; | 
 |  | 
 | /* The input section alignment within an output section.  */ | 
 | opt_subalign: | 
 | 	  /* empty */ | 
 | 	    { $$ = NULL; } | 
 | 	| SUBALIGN '(' exp ')' | 
 | 	    { $$ = $3; } | 
 | 	; | 
 |  | 
 | /* A section constraint.  */ | 
 | opt_constraint: | 
 | 	  /* empty */ | 
 | 	    { $$ = CONSTRAINT_NONE; } | 
 | 	| ONLY_IF_RO | 
 | 	    { $$ = CONSTRAINT_ONLY_IF_RO; } | 
 | 	| ONLY_IF_RW | 
 | 	    { $$ = CONSTRAINT_ONLY_IF_RW; } | 
 | 	| SPECIAL | 
 | 	    { $$ = CONSTRAINT_SPECIAL; } | 
 | 	; | 
 |  | 
 | /* The trailer of an output section in a SECTIONS block.  */ | 
 | section_trailer: | 
 | 	  opt_memspec opt_at_memspec opt_phdr opt_fill opt_comma | 
 | 	    { | 
 | 	      $$.fill = $4; | 
 | 	      $$.phdrs = $3; | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* A memory specification for an output section.  */ | 
 | opt_memspec: | 
 | 	  '>' string | 
 | 	    { script_set_section_region(closure, $2.value, $2.length, 1); } | 
 | 	| /* empty */ | 
 | 	; | 
 |  | 
 | /* A memory specification for where to load an output section.  */ | 
 | opt_at_memspec: | 
 | 	  AT '>' string | 
 | 	    { script_set_section_region(closure, $3.value, $3.length, 0); } | 
 | 	| /* empty */ | 
 | 	; | 
 |  | 
 | /* The program segment an output section should go into.  */ | 
 | opt_phdr: | 
 | 	  opt_phdr ':' string | 
 | 	    { $$ = script_string_list_push_back($1, $3.value, $3.length); } | 
 | 	| /* empty */ | 
 | 	    { $$ = NULL; } | 
 | 	; | 
 |  | 
 | /* The value to use to fill an output section.  FIXME: This does not | 
 |    handle a string of arbitrary length.  */ | 
 | opt_fill: | 
 | 	  '=' parse_exp | 
 | 	    { $$ = $2; } | 
 | 	| /* empty */ | 
 | 	    { $$ = NULL; } | 
 | 	; | 
 |  | 
 | /* Commands which may appear within the description of an output | 
 |    section in a SECTIONS block.  */ | 
 | section_cmds: | 
 | 	  /* empty */ | 
 | 	| section_cmds section_cmd | 
 | 	; | 
 |  | 
 | /* A command which may appear within the description of an output | 
 |    section in a SECTIONS block.  */ | 
 | section_cmd: | 
 | 	  assignment end | 
 | 	| input_section_spec | 
 | 	| data_length '(' parse_exp ')' | 
 | 	    { script_add_data(closure, $1, $3); } | 
 | 	| ASSERT_K '(' parse_exp ',' string ')' | 
 | 	    { script_add_assertion(closure, $3, $5.value, $5.length); } | 
 | 	| FILL '(' parse_exp ')' | 
 | 	    { script_add_fill(closure, $3); } | 
 | 	| CONSTRUCTORS | 
 | 	    { | 
 | 	      /* The GNU linker uses CONSTRUCTORS for the a.out object | 
 | 		 file format.  It does nothing when using ELF.  Since | 
 | 		 some ELF linker scripts use it although it does | 
 | 		 nothing, we accept it and ignore it.  */ | 
 | 	    } | 
 | 	| SORT_BY_NAME '(' CONSTRUCTORS ')' | 
 | 	| INCLUDE string | 
 | 	    { script_include_directive(PARSING_SECTION_COMMANDS, closure, | 
 | 				       $2.value, $2.length); } | 
 | 	| ';' | 
 | 	; | 
 |  | 
 | /* The length of data which may appear within the description of an | 
 |    output section in a SECTIONS block.  */ | 
 | data_length: | 
 | 	  QUAD | 
 | 	    { $$ = QUAD; } | 
 | 	| SQUAD | 
 | 	    { $$ = SQUAD; } | 
 | 	| LONG | 
 | 	    { $$ = LONG; } | 
 | 	| SHORT | 
 | 	    { $$ = SHORT; } | 
 | 	| BYTE | 
 | 	    { $$ = BYTE; } | 
 | 	; | 
 |  | 
 | /* An input section specification.  This may appear within the | 
 |    description of an output section in a SECTIONS block.  */ | 
 | input_section_spec: | 
 | 	  input_section_no_keep | 
 | 	    { script_add_input_section(closure, &$1, 0); } | 
 | 	| KEEP '(' input_section_no_keep ')' | 
 | 	    { script_add_input_section(closure, &$3, 1); } | 
 | 	; | 
 |  | 
 | /* An input section specification within a KEEP clause.  */ | 
 | input_section_no_keep: | 
 | 	  string | 
 | 	    { | 
 | 	      $$.file.name = $1; | 
 | 	      $$.file.sort = SORT_WILDCARD_NONE; | 
 | 	      $$.input_sections.sections = NULL; | 
 | 	      $$.input_sections.exclude = NULL; | 
 | 	    } | 
 | 	| wildcard_file '(' wildcard_sections ')' | 
 | 	    { | 
 | 	      $$.file = $1; | 
 | 	      $$.input_sections = $3; | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* A wildcard file specification.  */ | 
 | wildcard_file: | 
 | 	  wildcard_name | 
 | 	    { | 
 | 	      $$.name = $1; | 
 | 	      $$.sort = SORT_WILDCARD_NONE; | 
 | 	    } | 
 | 	| SORT_BY_NAME '(' wildcard_name ')' | 
 | 	    { | 
 | 	      $$.name = $3; | 
 | 	      $$.sort = SORT_WILDCARD_BY_NAME; | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* A list of wild card section specifications.  */ | 
 | wildcard_sections: | 
 | 	  wildcard_sections opt_comma wildcard_section | 
 | 	    { | 
 | 	      $$.sections = script_string_sort_list_add($1.sections, &$3); | 
 | 	      $$.exclude = $1.exclude; | 
 | 	    } | 
 | 	| wildcard_section | 
 | 	    { | 
 | 	      $$.sections = script_new_string_sort_list(&$1); | 
 | 	      $$.exclude = NULL; | 
 | 	    } | 
 | 	| wildcard_sections opt_comma EXCLUDE_FILE '(' exclude_names ')' | 
 | 	    { | 
 | 	      $$.sections = $1.sections; | 
 | 	      $$.exclude = script_string_list_append($1.exclude, $5); | 
 | 	    } | 
 | 	| EXCLUDE_FILE '(' exclude_names ')' | 
 | 	    { | 
 | 	      $$.sections = NULL; | 
 | 	      $$.exclude = $3; | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* A single wild card specification.  */ | 
 | wildcard_section: | 
 | 	  wildcard_name | 
 | 	    { | 
 | 	      $$.name = $1; | 
 | 	      $$.sort = SORT_WILDCARD_NONE; | 
 | 	    } | 
 | 	| SORT_BY_NAME '(' wildcard_section ')' | 
 | 	    { | 
 | 	      $$.name = $3.name; | 
 | 	      switch ($3.sort) | 
 | 		{ | 
 | 		case SORT_WILDCARD_NONE: | 
 | 		  $$.sort = SORT_WILDCARD_BY_NAME; | 
 | 		  break; | 
 | 		case SORT_WILDCARD_BY_NAME: | 
 | 		case SORT_WILDCARD_BY_NAME_BY_ALIGNMENT: | 
 | 		  break; | 
 | 		case SORT_WILDCARD_BY_ALIGNMENT: | 
 | 		case SORT_WILDCARD_BY_ALIGNMENT_BY_NAME: | 
 | 		  $$.sort = SORT_WILDCARD_BY_NAME_BY_ALIGNMENT; | 
 | 		  break; | 
 | 		default: | 
 | 		  abort(); | 
 | 		} | 
 | 	    } | 
 | 	| SORT_BY_ALIGNMENT '(' wildcard_section ')' | 
 | 	    { | 
 | 	      $$.name = $3.name; | 
 | 	      switch ($3.sort) | 
 | 		{ | 
 | 		case SORT_WILDCARD_NONE: | 
 | 		  $$.sort = SORT_WILDCARD_BY_ALIGNMENT; | 
 | 		  break; | 
 | 		case SORT_WILDCARD_BY_ALIGNMENT: | 
 | 		case SORT_WILDCARD_BY_ALIGNMENT_BY_NAME: | 
 | 		  break; | 
 | 		case SORT_WILDCARD_BY_NAME: | 
 | 		case SORT_WILDCARD_BY_NAME_BY_ALIGNMENT: | 
 | 		  $$.sort = SORT_WILDCARD_BY_ALIGNMENT_BY_NAME; | 
 | 		  break; | 
 | 		default: | 
 | 		  abort(); | 
 | 		} | 
 | 	    } | 
 | 	| SORT_BY_INIT_PRIORITY '(' wildcard_name ')' | 
 | 	    { | 
 | 	      $$.name = $3; | 
 | 	      $$.sort = SORT_WILDCARD_BY_INIT_PRIORITY; | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* A list of file names to exclude.  */ | 
 | exclude_names: | 
 | 	  exclude_names opt_comma wildcard_name | 
 | 	    { $$ = script_string_list_push_back($1, $3.value, $3.length); } | 
 | 	| wildcard_name | 
 | 	    { $$ = script_new_string_list($1.value, $1.length); } | 
 | 	; | 
 |  | 
 | /* A single wildcard name.  We recognize '*' and '?' specially since | 
 |    they are expression tokens.  */ | 
 | wildcard_name: | 
 | 	  string | 
 | 	    { $$ = $1; } | 
 | 	| '*' | 
 | 	    { | 
 | 	      $$.value = "*"; | 
 | 	      $$.length = 1; | 
 | 	    } | 
 | 	| '?' | 
 | 	    { | 
 | 	      $$.value = "?"; | 
 | 	      $$.length = 1; | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* A list of MEMORY definitions.  */ | 
 | memory_defs: | 
 | 	  memory_defs opt_comma memory_def | 
 | 	| /* empty */ | 
 | 	; | 
 |  | 
 | /* A single MEMORY definition.  */ | 
 | memory_def: | 
 | 	  string memory_attr ':' memory_origin '=' parse_exp opt_comma memory_length '=' parse_exp | 
 | 	  { script_add_memory(closure, $1.value, $1.length, $2, $6, $10); } | 
 | 	| | 
 | 	  INCLUDE string | 
 | 	  { script_include_directive(PARSING_MEMORY_DEF, closure, | 
 | 				     $2.value, $2.length); } | 
 | 	| | 
 | 	; | 
 |  | 
 | /* The (optional) attributes of a MEMORY region.  */ | 
 | memory_attr: | 
 | 	  '(' string ')' | 
 | 	  { $$ = script_parse_memory_attr(closure, $2.value, $2.length, 0); } | 
 |         | /* Inverted attributes. */ | 
 | 	  '(' '!' string ')' | 
 | 	  { $$ = script_parse_memory_attr(closure, $3.value, $3.length, 1); } | 
 | 	| /* empty */ | 
 | 	    { $$ = 0; } | 
 | 	; | 
 |  | 
 | memory_origin: | 
 |           ORIGIN | 
 | 	| | 
 | 	  ORG | 
 | 	| | 
 | 	  'o' | 
 | 	; | 
 |  | 
 | memory_length: | 
 |           LENGTH | 
 | 	| | 
 | 	  LEN | 
 | 	| | 
 | 	  'l' | 
 | 	; | 
 |  | 
 | /* A list of program header definitions.  */ | 
 | phdrs_defs: | 
 | 	  phdrs_defs phdr_def | 
 | 	| /* empty */ | 
 | 	; | 
 |  | 
 | /* A program header definition.  */ | 
 | phdr_def: | 
 | 	  string phdr_type phdr_info ';' | 
 | 	    { script_add_phdr(closure, $1.value, $1.length, $2, &$3); } | 
 | 	; | 
 |  | 
 | /* A program header type.  The GNU linker accepts a general expression | 
 |    here, but that would be a pain because we would have to dig into | 
 |    the expression structure.  It's unlikely that anybody uses anything | 
 |    other than a string or a number here, so that is all we expect.  */ | 
 | phdr_type: | 
 | 	  string | 
 | 	    { $$ = script_phdr_string_to_type(closure, $1.value, $1.length); } | 
 | 	| INTEGER | 
 | 	    { $$ = $1; } | 
 | 	; | 
 |  | 
 | /* Additional information for a program header.  */ | 
 | phdr_info: | 
 | 	  /* empty */ | 
 | 	    { memset(&$$, 0, sizeof(struct Phdr_info)); } | 
 | 	| string phdr_info | 
 | 	    { | 
 | 	      $$ = $2; | 
 | 	      if ($1.length == 7 && strncmp($1.value, "FILEHDR", 7) == 0) | 
 | 		$$.includes_filehdr = 1; | 
 | 	      else | 
 | 		yyerror(closure, "PHDRS syntax error"); | 
 | 	    } | 
 | 	| PHDRS phdr_info | 
 | 	    { | 
 | 	      $$ = $2; | 
 | 	      $$.includes_phdrs = 1; | 
 | 	    } | 
 | 	| string '(' INTEGER ')' phdr_info | 
 | 	    { | 
 | 	      $$ = $5; | 
 | 	      if ($1.length == 5 && strncmp($1.value, "FLAGS", 5) == 0) | 
 | 		{ | 
 | 		  $$.is_flags_valid = 1; | 
 | 		  $$.flags = $3; | 
 | 		} | 
 | 	      else | 
 | 		yyerror(closure, "PHDRS syntax error"); | 
 | 	    } | 
 | 	| AT '(' parse_exp ')' phdr_info | 
 | 	    { | 
 | 	      $$ = $5; | 
 | 	      $$.load_address = $3; | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* Set a symbol to a value.  */ | 
 | assignment: | 
 | 	  string '=' parse_exp | 
 | 	    { script_set_symbol(closure, $1.value, $1.length, $3, 0, 0); } | 
 | 	| string PLUSEQ parse_exp | 
 | 	    { | 
 | 	      Expression_ptr s = script_exp_string($1.value, $1.length); | 
 | 	      Expression_ptr e = script_exp_binary_add(s, $3); | 
 | 	      script_set_symbol(closure, $1.value, $1.length, e, 0, 0); | 
 | 	    } | 
 | 	| string MINUSEQ parse_exp | 
 | 	    { | 
 | 	      Expression_ptr s = script_exp_string($1.value, $1.length); | 
 | 	      Expression_ptr e = script_exp_binary_sub(s, $3); | 
 | 	      script_set_symbol(closure, $1.value, $1.length, e, 0, 0); | 
 | 	    } | 
 | 	| string MULTEQ parse_exp | 
 | 	    { | 
 | 	      Expression_ptr s = script_exp_string($1.value, $1.length); | 
 | 	      Expression_ptr e = script_exp_binary_mult(s, $3); | 
 | 	      script_set_symbol(closure, $1.value, $1.length, e, 0, 0); | 
 | 	    } | 
 | 	| string DIVEQ parse_exp | 
 | 	    { | 
 | 	      Expression_ptr s = script_exp_string($1.value, $1.length); | 
 | 	      Expression_ptr e = script_exp_binary_div(s, $3); | 
 | 	      script_set_symbol(closure, $1.value, $1.length, e, 0, 0); | 
 | 	    } | 
 | 	| string LSHIFTEQ parse_exp | 
 | 	    { | 
 | 	      Expression_ptr s = script_exp_string($1.value, $1.length); | 
 | 	      Expression_ptr e = script_exp_binary_lshift(s, $3); | 
 | 	      script_set_symbol(closure, $1.value, $1.length, e, 0, 0); | 
 | 	    } | 
 | 	| string RSHIFTEQ parse_exp | 
 | 	    { | 
 | 	      Expression_ptr s = script_exp_string($1.value, $1.length); | 
 | 	      Expression_ptr e = script_exp_binary_rshift(s, $3); | 
 | 	      script_set_symbol(closure, $1.value, $1.length, e, 0, 0); | 
 | 	    } | 
 | 	| string ANDEQ parse_exp | 
 | 	    { | 
 | 	      Expression_ptr s = script_exp_string($1.value, $1.length); | 
 | 	      Expression_ptr e = script_exp_binary_bitwise_and(s, $3); | 
 | 	      script_set_symbol(closure, $1.value, $1.length, e, 0, 0); | 
 | 	    } | 
 | 	| string OREQ parse_exp | 
 | 	    { | 
 | 	      Expression_ptr s = script_exp_string($1.value, $1.length); | 
 | 	      Expression_ptr e = script_exp_binary_bitwise_or(s, $3); | 
 | 	      script_set_symbol(closure, $1.value, $1.length, e, 0, 0); | 
 | 	    } | 
 | 	| HIDDEN '(' string '=' parse_exp ')' | 
 | 	    { script_set_symbol(closure, $3.value, $3.length, $5, 0, 1); } | 
 | 	| PROVIDE '(' string '=' parse_exp ')' | 
 | 	    { script_set_symbol(closure, $3.value, $3.length, $5, 1, 0); } | 
 | 	| PROVIDE_HIDDEN '(' string '=' parse_exp ')' | 
 | 	    { script_set_symbol(closure, $3.value, $3.length, $5, 1, 1); } | 
 | 	; | 
 |  | 
 | /* Parse an expression, putting the lexer into the right mode.  */ | 
 | parse_exp: | 
 | 	    { script_push_lex_into_expression_mode(closure); } | 
 | 	  exp | 
 | 	    { | 
 | 	      script_pop_lex_mode(closure); | 
 | 	      $$ = $2; | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* An expression.  */ | 
 | exp: | 
 | 	  '(' exp ')' | 
 | 	    { $$ = $2; } | 
 | 	| '-' exp %prec UNARY | 
 | 	    { $$ = script_exp_unary_minus($2); } | 
 | 	| '!' exp %prec UNARY | 
 | 	    { $$ = script_exp_unary_logical_not($2); } | 
 | 	| '~' exp %prec UNARY | 
 | 	    { $$ = script_exp_unary_bitwise_not($2); } | 
 | 	| '+' exp %prec UNARY | 
 | 	    { $$ = $2; } | 
 | 	| exp '*' exp | 
 | 	    { $$ = script_exp_binary_mult($1, $3); } | 
 | 	| exp '/' exp | 
 | 	    { $$ = script_exp_binary_div($1, $3); } | 
 | 	| exp '%' exp | 
 | 	    { $$ = script_exp_binary_mod($1, $3); } | 
 | 	| exp '+' exp | 
 | 	    { $$ = script_exp_binary_add($1, $3); } | 
 | 	| exp '-' exp | 
 | 	    { $$ = script_exp_binary_sub($1, $3); } | 
 | 	| exp LSHIFT exp | 
 | 	    { $$ = script_exp_binary_lshift($1, $3); } | 
 | 	| exp RSHIFT exp | 
 | 	    { $$ = script_exp_binary_rshift($1, $3); } | 
 | 	| exp EQ exp | 
 | 	    { $$ = script_exp_binary_eq($1, $3); } | 
 | 	| exp NE exp | 
 | 	    { $$ = script_exp_binary_ne($1, $3); } | 
 | 	| exp LE exp | 
 | 	    { $$ = script_exp_binary_le($1, $3); } | 
 | 	| exp GE exp | 
 | 	    { $$ = script_exp_binary_ge($1, $3); } | 
 | 	| exp '<' exp | 
 | 	    { $$ = script_exp_binary_lt($1, $3); } | 
 | 	| exp '>' exp | 
 | 	    { $$ = script_exp_binary_gt($1, $3); } | 
 | 	| exp '&' exp | 
 | 	    { $$ = script_exp_binary_bitwise_and($1, $3); } | 
 | 	| exp '^' exp | 
 | 	    { $$ = script_exp_binary_bitwise_xor($1, $3); } | 
 | 	| exp '|' exp | 
 | 	    { $$ = script_exp_binary_bitwise_or($1, $3); } | 
 | 	| exp ANDAND exp | 
 | 	    { $$ = script_exp_binary_logical_and($1, $3); } | 
 | 	| exp OROR exp | 
 | 	    { $$ = script_exp_binary_logical_or($1, $3); } | 
 | 	| exp '?' exp ':' exp | 
 | 	    { $$ = script_exp_trinary_cond($1, $3, $5); } | 
 | 	| INTEGER | 
 | 	    { $$ = script_exp_integer($1); } | 
 | 	| string | 
 | 	    { $$ = script_symbol(closure, $1.value, $1.length); } | 
 | 	| MAX_K '(' exp ',' exp ')' | 
 | 	    { $$ = script_exp_function_max($3, $5); } | 
 | 	| MIN_K '(' exp ',' exp ')' | 
 | 	    { $$ = script_exp_function_min($3, $5); } | 
 | 	| DEFINED '(' string ')' | 
 | 	    { $$ = script_exp_function_defined($3.value, $3.length); } | 
 | 	| SIZEOF_HEADERS | 
 | 	    { $$ = script_exp_function_sizeof_headers(); } | 
 | 	| ALIGNOF '(' string ')' | 
 | 	    { $$ = script_exp_function_alignof($3.value, $3.length); } | 
 | 	| SIZEOF '(' string ')' | 
 | 	    { $$ = script_exp_function_sizeof($3.value, $3.length); } | 
 | 	| ADDR '(' string ')' | 
 | 	    { $$ = script_exp_function_addr($3.value, $3.length); } | 
 | 	| LOADADDR '(' string ')' | 
 | 	    { $$ = script_exp_function_loadaddr($3.value, $3.length); } | 
 | 	| ORIGIN '(' string ')' | 
 | 	    { $$ = script_exp_function_origin(closure, $3.value, $3.length); } | 
 | 	| LENGTH '(' string ')' | 
 | 	    { $$ = script_exp_function_length(closure, $3.value, $3.length); } | 
 | 	| CONSTANT '(' string ')' | 
 | 	    { $$ = script_exp_function_constant($3.value, $3.length); } | 
 | 	| ABSOLUTE '(' exp ')' | 
 | 	    { $$ = script_exp_function_absolute($3); } | 
 | 	| ALIGN_K '(' exp ')' | 
 | 	    { $$ = script_exp_function_align(script_exp_string(".", 1), $3); } | 
 | 	| ALIGN_K '(' exp ',' exp ')' | 
 | 	    { $$ = script_exp_function_align($3, $5); } | 
 | 	| BLOCK '(' exp ')' | 
 | 	    { $$ = script_exp_function_align(script_exp_string(".", 1), $3); } | 
 | 	| DATA_SEGMENT_ALIGN '(' exp ',' exp ')' | 
 | 	    { | 
 | 	      script_data_segment_align(closure); | 
 | 	      $$ = script_exp_function_data_segment_align($3, $5); | 
 | 	    } | 
 | 	| DATA_SEGMENT_RELRO_END '(' exp ',' exp ')' | 
 | 	    { | 
 | 	      script_data_segment_relro_end(closure); | 
 | 	      $$ = script_exp_function_data_segment_relro_end($3, $5); | 
 | 	    } | 
 | 	| DATA_SEGMENT_END '(' exp ')' | 
 | 	    { $$ = script_exp_function_data_segment_end($3); } | 
 | 	| SEGMENT_START '(' string ',' exp ')' | 
 | 	    { | 
 | 	      $$ = script_exp_function_segment_start($3.value, $3.length, $5); | 
 | 	      /* We need to take note of any SEGMENT_START expressions | 
 | 		 because they change the behaviour of -Ttext, -Tdata and | 
 | 		 -Tbss options.  */ | 
 | 	      script_saw_segment_start_expression(closure); | 
 | 	    } | 
 | 	| ASSERT_K '(' exp ',' string ')' | 
 | 	    { $$ = script_exp_function_assert($3, $5.value, $5.length); } | 
 | 	; | 
 |  | 
 | /* Handle the --defsym option.  */ | 
 | defsym_expr: | 
 | 	  string '=' parse_exp | 
 | 	    { script_set_symbol(closure, $1.value, $1.length, $3, 0, 0); } | 
 | 	; | 
 |  | 
 | /* Handle the --dynamic-list option.  A dynamic list has the format | 
 |    { sym1; sym2; extern "C++" { namespace::sym3 }; }; | 
 |    We store the symbol we see in the "local" list; that is where | 
 |    Command_line::in_dynamic_list() will look to do its check. | 
 |    TODO(csilvers): More than one of these brace-lists can appear, and | 
 |    should just be merged and treated as a single list.  */ | 
 | dynamic_list_expr: dynamic_list_nodes ; | 
 |  | 
 | dynamic_list_nodes: | 
 | 	  dynamic_list_node | 
 | 	| dynamic_list_nodes dynamic_list_node | 
 |         ; | 
 |  | 
 | dynamic_list_node: | 
 |           '{' vers_defns ';' '}' ';' | 
 |             { script_new_vers_node (closure, NULL, $2); } | 
 |         ; | 
 |  | 
 | /* A version script.  */ | 
 | version_script: | 
 | 	  vers_nodes | 
 | 	; | 
 |  | 
 | vers_nodes: | 
 | 	  vers_node | 
 | 	| vers_nodes vers_node | 
 | 	; | 
 |  | 
 | vers_node: | 
 | 	  '{' vers_tag '}' ';' | 
 | 	    { | 
 | 	      script_register_vers_node (closure, NULL, 0, $2, NULL); | 
 | 	    } | 
 | 	| string '{' vers_tag '}' ';' | 
 | 	    { | 
 | 	      script_register_vers_node (closure, $1.value, $1.length, $3, | 
 | 					 NULL); | 
 | 	    } | 
 | 	| string '{' vers_tag '}' verdep ';' | 
 | 	    { | 
 | 	      script_register_vers_node (closure, $1.value, $1.length, $3, $5); | 
 | 	    } | 
 | 	; | 
 |  | 
 | verdep: | 
 | 	  string | 
 | 	    { | 
 | 	      $$ = script_add_vers_depend (closure, NULL, $1.value, $1.length); | 
 | 	    } | 
 | 	| verdep string | 
 | 	    { | 
 | 	      $$ = script_add_vers_depend (closure, $1, $2.value, $2.length); | 
 | 	    } | 
 | 	; | 
 |  | 
 | vers_tag: | 
 | 	  /* empty */ | 
 | 	    { $$ = script_new_vers_node (closure, NULL, NULL); } | 
 | 	| vers_defns ';' | 
 | 	    { $$ = script_new_vers_node (closure, $1, NULL); } | 
 | 	| GLOBAL ':' vers_defns ';' | 
 | 	    { $$ = script_new_vers_node (closure, $3, NULL); } | 
 | 	| LOCAL ':' vers_defns ';' | 
 | 	    { $$ = script_new_vers_node (closure, NULL, $3); } | 
 | 	| GLOBAL ':' vers_defns ';' LOCAL ':' vers_defns ';' | 
 | 	    { $$ = script_new_vers_node (closure, $3, $7); } | 
 | 	; | 
 |  | 
 | /* Here is one of the rare places we care about the distinction | 
 |    between STRING and QUOTED_STRING.  For QUOTED_STRING, we do exact | 
 |    matching on the pattern, so we pass in true for the exact_match | 
 |    parameter.  For STRING, we do glob matching and pass in false.  */ | 
 | vers_defns: | 
 | 	  STRING | 
 | 	    { | 
 | 	      $$ = script_new_vers_pattern (closure, NULL, $1.value, | 
 | 					    $1.length, 0); | 
 | 	    } | 
 | 	| QUOTED_STRING | 
 | 	    { | 
 | 	      $$ = script_new_vers_pattern (closure, NULL, $1.value, | 
 | 					    $1.length, 1); | 
 | 	    } | 
 | 	| vers_defns ';' STRING | 
 | 	    { | 
 | 	      $$ = script_new_vers_pattern (closure, $1, $3.value, | 
 |                                             $3.length, 0); | 
 | 	    } | 
 | 	| vers_defns ';' QUOTED_STRING | 
 | 	    { | 
 | 	      $$ = script_new_vers_pattern (closure, $1, $3.value, | 
 |                                             $3.length, 1); | 
 | 	    } | 
 |         | /* Push string on the language stack. */ | 
 |           EXTERN string '{' | 
 | 	    { version_script_push_lang (closure, $2.value, $2.length); } | 
 | 	  vers_defns opt_semicolon '}' | 
 | 	    { | 
 | 	      $$ = $5; | 
 | 	      version_script_pop_lang(closure); | 
 | 	    } | 
 |         | /* Push string on the language stack.  This is more complicated | 
 |              than the other cases because we need to merge the linked-list | 
 |              state from the pre-EXTERN defns and the post-EXTERN defns.  */ | 
 |           vers_defns ';' EXTERN string '{' | 
 | 	    { version_script_push_lang (closure, $4.value, $4.length); } | 
 | 	  vers_defns opt_semicolon '}' | 
 | 	    { | 
 | 	      $$ = script_merge_expressions ($1, $7); | 
 | 	      version_script_pop_lang(closure); | 
 | 	    } | 
 |         | EXTERN  // "extern" as a symbol name | 
 | 	    { | 
 | 	      $$ = script_new_vers_pattern (closure, NULL, "extern", | 
 | 					    sizeof("extern") - 1, 1); | 
 | 	    } | 
 | 	| vers_defns ';' EXTERN | 
 | 	    { | 
 | 	      $$ = script_new_vers_pattern (closure, $1, "extern", | 
 | 					    sizeof("extern") - 1, 1); | 
 | 	    } | 
 | 	; | 
 |  | 
 | /* A string can be either a STRING or a QUOTED_STRING.  Almost all the | 
 |    time we don't care, and we use this rule.  */ | 
 | string: | 
 |           STRING | 
 | 	    { $$ = $1; } | 
 | 	| QUOTED_STRING | 
 | 	    { $$ = $1; } | 
 | 	; | 
 |  | 
 | /* Some statements require a terminator, which may be a semicolon or a | 
 |    comma.  */ | 
 | end: | 
 | 	  ';' | 
 | 	| ',' | 
 | 	; | 
 |  | 
 | /* An optional semicolon.  */ | 
 | opt_semicolon: | 
 | 	  ';' | 
 | 	|  /* empty */ | 
 | 	; | 
 |  | 
 | /* An optional comma.  */ | 
 | opt_comma: | 
 | 	  ',' | 
 | 	| /* empty */ | 
 | 	; | 
 |  | 
 | %% |