%{ /* mcparse.y -- parser for Windows mc files
  Copyright (C) 2007-2019 Free Software Foundation, Inc.

  Parser for Windows mc files
  Written by Kai Tietz, Onevision.

  This file is part of GNU Binutils.

  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 parser for Windows rc files.  It is based on the parser
   by Gunther Ebert <gunther.ebert@ixos-leipzig.de>.  */

#include "sysdep.h"
#include "bfd.h"
#include "bucomm.h"
#include "libiberty.h"
#include "windmc.h"
#include "safe-ctype.h"

static rc_uint_type mc_last_id = 0;
static rc_uint_type mc_sefa_val = 0;
static unichar *mc_last_symbol = NULL;
static const mc_keyword *mc_cur_severity = NULL;
static const mc_keyword *mc_cur_facility = NULL;
static mc_node *cur_node = NULL;

%}

%union
{
  rc_uint_type ival;
  unichar *ustr;
  const mc_keyword *tok;
  mc_node *nod;
};

%start input

%token NL
%token<ustr> MCIDENT MCFILENAME MCLINE MCCOMMENT
%token<tok> MCTOKEN
%token MCENDLINE
%token MCLANGUAGENAMES MCFACILITYNAMES MCSEVERITYNAMES MCOUTPUTBASE MCMESSAGEIDTYPEDEF
%token MCLANGUAGE MCMESSAGEID MCSEVERITY MCFACILITY MCSYMBOLICNAME
%token <ival> MCNUMBER

%type<ival> id vid sefasy_def
%type<ustr> alias_name token lines comments
%type<tok> lang

%%
input:	  entities
	;

entities:
	  /* empty */
	| entities entity
	;
entity:	  global_section
	| message
	| comments
	  {
	    cur_node = mc_add_node ();
	    cur_node->user_text = $1;
	  }
	| error	{ mc_fatal ("syntax error"); }
;

global_section:
	  MCSEVERITYNAMES '=' '(' severitymaps ')'
	| MCSEVERITYNAMES '=' '(' severitymaps error { mc_fatal ("missing ')' in SeverityNames"); }
	| MCSEVERITYNAMES '=' error { mc_fatal ("missing '(' in SeverityNames"); }
	| MCSEVERITYNAMES error { mc_fatal ("missing '=' for SeverityNames"); }
	| MCLANGUAGENAMES '=' '(' langmaps ')'
	| MCLANGUAGENAMES '=' '(' langmaps error { mc_fatal ("missing ')' in LanguageNames"); }
	| MCLANGUAGENAMES '=' error { mc_fatal ("missing '(' in LanguageNames"); }
	| MCLANGUAGENAMES error { mc_fatal ("missing '=' for LanguageNames"); }
	| MCFACILITYNAMES '=' '(' facilitymaps ')'
	| MCFACILITYNAMES '=' '(' facilitymaps error { mc_fatal ("missing ')' in FacilityNames"); }
	| MCFACILITYNAMES '=' error { mc_fatal ("missing '(' in FacilityNames"); }
	| MCFACILITYNAMES error { mc_fatal ("missing '=' for FacilityNames"); }
	| MCOUTPUTBASE '=' MCNUMBER
	  {
	    if ($3 != 10 && $3 != 16)
	      mc_fatal ("OutputBase allows 10 or 16 as value");
	    mcset_out_values_are_decimal = ($3 == 10 ? 1 : 0);
	  }
	| MCMESSAGEIDTYPEDEF '=' MCIDENT
	  {
	    mcset_msg_id_typedef = $3;
	  }
	| MCMESSAGEIDTYPEDEF '=' error
	  {
	    mc_fatal ("MessageIdTypedef expects an identifier");
	  }
	| MCMESSAGEIDTYPEDEF error
	  {
	    mc_fatal ("missing '=' for MessageIdTypedef");
	  }
;

severitymaps:
	  severitymap
	| severitymaps severitymap
	| error { mc_fatal ("severity ident missing"); }
;

severitymap:
	  token '=' MCNUMBER alias_name
	  {
	    mc_add_keyword ($1, MCTOKEN, "severity", $3, $4);
	  }
	| token '=' error { mc_fatal ("severity number missing"); }
	| token error { mc_fatal ("severity missing '='"); }
;

facilitymaps:
	  facilitymap
	| facilitymaps facilitymap
	| error { mc_fatal ("missing ident in FacilityNames"); }
;

facilitymap:
	  token '=' MCNUMBER alias_name
	  {
	    mc_add_keyword ($1, MCTOKEN, "facility", $3, $4);
	  }
	| token '=' error { mc_fatal ("facility number missing"); }
	| token error { mc_fatal ("facility missing '='"); }
;

langmaps:
	  langmap
	| langmaps langmap
	| error { mc_fatal ("missing ident in LanguageNames"); }
;

langmap:
	  token '=' MCNUMBER lex_want_filename ':' MCFILENAME
	  {
	    mc_add_keyword ($1, MCTOKEN, "language", $3, $6);
	  }
	| token '=' MCNUMBER lex_want_filename ':' error { mc_fatal ("missing filename in LanguageNames"); }
	| token '=' MCNUMBER error { mc_fatal ("missing ':' in LanguageNames"); }
	| token '=' error { mc_fatal ("missing language code in LanguageNames"); }
	| token error { mc_fatal ("missing '=' for LanguageNames"); }
;

alias_name:
	  /* empty */
	  {
	    $$ = NULL;
	  }
	| ':' MCIDENT
	  {
	    $$ = $2;
	  }
	| ':' error { mc_fatal ("illegal token in identifier"); $$ = NULL; }
;

message:
	  id sefasy_def
	  {
	    cur_node = mc_add_node ();
	    cur_node->symbol = mc_last_symbol;
	    cur_node->facility = mc_cur_facility;
	    cur_node->severity = mc_cur_severity;
	    cur_node->id = ($1 & 0xffffUL);
	    cur_node->vid = ($1 & 0xffffUL) | mc_sefa_val;
	    mc_last_id = $1;
	  }
	  lang_entities
;

id:	  MCMESSAGEID '=' vid { $$ = $3; }
	| MCMESSAGEID '=' error { mc_fatal ("missing number in MessageId"); $$ = 0; }
	| MCMESSAGEID error { mc_fatal ("missing '=' for MessageId"); $$ = 0; }
;

vid:	  /* empty */
	  {
	    $$ = ++mc_last_id;
	  }
	| MCNUMBER
	  {
	    $$ = $1;
	  }
	| '+' MCNUMBER
	  {
	    $$ = mc_last_id + $2;
	  }
	| '+' error { mc_fatal ("missing number after MessageId '+'"); }
;

sefasy_def:
	  /* empty */
	  {
	    $$ = 0;
	    mc_sefa_val = (mcset_custom_bit ? 1 : 0) << 29;
	    mc_last_symbol = NULL;
	    mc_cur_severity = NULL;
	    mc_cur_facility = NULL;
	  }
	| sefasy_def severity
	  {
	    if ($1 & 1)
	      mc_warn (_("duplicate definition of Severity"));
	    $$ = $1 | 1;
	  }
	| sefasy_def facility
	  {
	    if ($1 & 2)
	      mc_warn (_("duplicate definition of Facility"));
	    $$ = $1 | 2;
	  }
	| sefasy_def symbol
	  {
	    if ($1 & 4)
	      mc_warn (_("duplicate definition of SymbolicName"));
	    $$ = $1 | 4;
	  }
;

severity: MCSEVERITY '=' MCTOKEN
	  {
	    mc_sefa_val &= ~ (0x3UL << 30);
	    mc_sefa_val |= (($3->nval & 0x3UL) << 30);
	    mc_cur_severity = $3;
	  }
;

facility: MCFACILITY '=' MCTOKEN
	  {
	    mc_sefa_val &= ~ (0xfffUL << 16);
	    mc_sefa_val |= (($3->nval & 0xfffUL) << 16);
	    mc_cur_facility = $3;
	  }
;

symbol: MCSYMBOLICNAME '=' MCIDENT
	{
	  mc_last_symbol = $3;
	}
;

lang_entities:
	  lang_entity
	| lang_entities lang_entity
;

lang_entity:
	  lang lex_want_line lines MCENDLINE
	  {
	    mc_node_lang *h;
	    h = mc_add_node_lang (cur_node, $1, cur_node->vid);
	    h->message = $3;
	    if (mcset_max_message_length != 0 && unichar_len (h->message) > mcset_max_message_length)
	      mc_warn ("message length to long");
	  }
;

lines:	  MCLINE
	  {
	    $$ = $1;
	  }
	| lines MCLINE
	  {
	    unichar *h;
	    rc_uint_type l1,l2;
	    l1 = unichar_len ($1);
	    l2 = unichar_len ($2);
	    h = (unichar *) res_alloc ((l1 + l2 + 1) * sizeof (unichar));
	    if (l1) memcpy (h, $1, l1 * sizeof (unichar));
	    if (l2) memcpy (&h[l1], $2, l2 * sizeof (unichar));
	    h[l1 + l2] = 0;
	    $$ = h;
	  }
	| error { mc_fatal ("missing end of message text"); $$ = NULL; }
	| lines error { mc_fatal ("missing end of message text"); $$ = $1; }
;

comments: MCCOMMENT { $$ = $1; }
	| comments MCCOMMENT
	  {
	    unichar *h;
	    rc_uint_type l1,l2;
	    l1 = unichar_len ($1);
	    l2 = unichar_len ($2);
	    h = (unichar *) res_alloc ((l1 + l2 + 1) * sizeof (unichar));
	    if (l1) memcpy (h, $1, l1 * sizeof (unichar));
	    if (l2) memcpy (&h[l1], $2, l2 * sizeof (unichar));
	    h[l1 + l2] = 0;
	    $$ = h;
	  }
;

lang:	  MCLANGUAGE lex_want_nl '=' MCTOKEN NL
	  {
	    $$ = $4;
	  }
	| MCLANGUAGE lex_want_nl '=' MCIDENT NL
	  {
	    $$ = NULL;
	    mc_fatal (_("undeclared language identifier"));
	  }
	| MCLANGUAGE lex_want_nl '=' token error
	  {
	    $$ = NULL;
	    mc_fatal ("missing newline after Language");
	  }
	| MCLANGUAGE lex_want_nl '=' error
	  {
	    $$ = NULL;
	    mc_fatal ("missing ident for Language");
	  }
	| MCLANGUAGE error
	  {
	    $$ = NULL;
	    mc_fatal ("missing '=' for Language");
	  }
;

token: 	MCIDENT { $$ = $1; }
	|  MCTOKEN { $$ = $1->usz; }
;

lex_want_nl:
	  /* Empty */	{ mclex_want_nl = 1; }
;

lex_want_line:
	  /* Empty */	{ mclex_want_line = 1; }
;

lex_want_filename:
	  /* Empty */	{ mclex_want_filename = 1; }
;

%%

/* Something else.  */