blob: 5f6b2e4091a7a1f5c3c6523d2c2caf0999070494 [file] [log] [blame]
/* GNU gettext - internationalization aids
Copyright (C) 1995-1996, 1998, 2000-2001, 2003, 2005-2006, 2012-2013, 2016, 2020
Free Software Foundation, Inc.
This file was written by Peter Miller <pmiller@agso.gov.au>
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, see <https://www.gnu.org/licenses/>. */
%{
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
/* Specification. */
#include "po-gram.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "str-list.h"
#include "po-lex.h"
#include "po-charset.h"
#include "error.h"
#include "xalloc.h"
#include "gettext.h"
#include "read-catalog-abstract.h"
#define _(str) gettext (str)
static long plural_counter;
#define check_obsolete(value1,value2) \
if ((value1).obsolete != (value2).obsolete) \
po_gram_error_at_line (&(value2).pos, _("inconsistent use of #~"));
static inline void
do_callback_message (char *msgctxt,
char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
char *prev_msgctxt,
char *prev_msgid, char *prev_msgid_plural,
bool obsolete)
{
/* Test for header entry. Ignore fuzziness of the header entry. */
if (msgctxt == NULL && msgid[0] == '\0' && !obsolete)
po_lex_charset_set (msgstr, gram_pos.file_name);
po_callback_message (msgctxt,
msgid, msgid_pos, msgid_plural,
msgstr, msgstr_len, msgstr_pos,
prev_msgctxt, prev_msgid, prev_msgid_plural,
false, obsolete);
}
#define free_message_intro(value) \
if ((value).prev_ctxt != NULL) \
free ((value).prev_ctxt); \
if ((value).prev_id != NULL) \
free ((value).prev_id); \
if ((value).prev_id_plural != NULL) \
free ((value).prev_id_plural); \
if ((value).ctxt != NULL) \
free ((value).ctxt);
%}
%require "3.0"
/* Remap parser interface names, so we can have multiple Bison
generated parsers in the same program. */
%define api.prefix {po_gram_}
%token COMMENT
%token DOMAIN
%token JUNK
%token PREV_MSGCTXT
%token PREV_MSGID
%token PREV_MSGID_PLURAL
%token PREV_STRING
%token MSGCTXT
%token MSGID
%token MSGID_PLURAL
%token MSGSTR
%token NAME
%token '[' ']'
%token NUMBER
%token STRING
%union
{
struct { char *string; lex_pos_ty pos; bool obsolete; } string;
struct { string_list_ty stringlist; lex_pos_ty pos; bool obsolete; } stringlist;
struct { long number; lex_pos_ty pos; bool obsolete; } number;
struct { lex_pos_ty pos; bool obsolete; } pos;
struct { char *ctxt; char *id; char *id_plural; lex_pos_ty pos; bool obsolete; } prev;
struct { char *prev_ctxt; char *prev_id; char *prev_id_plural; char *ctxt; lex_pos_ty pos; bool obsolete; } message_intro;
struct { struct msgstr_def rhs; lex_pos_ty pos; bool obsolete; } rhs;
}
%type <string> STRING PREV_STRING COMMENT NAME
msg_intro prev_msg_intro msgid_pluralform prev_msgid_pluralform
%type <stringlist> string_list prev_string_list
%type <number> NUMBER
%type <pos> DOMAIN
PREV_MSGCTXT PREV_MSGID PREV_MSGID_PLURAL
MSGCTXT MSGID MSGID_PLURAL MSGSTR '[' ']'
%type <prev> prev
%type <message_intro> message_intro
%type <rhs> pluralform pluralform_list
%right MSGSTR
%%
po_file
: /* empty */
| po_file comment
| po_file domain
| po_file message
| po_file error
;
comment
: COMMENT
{
po_callback_comment_dispatcher ($1.string);
}
;
domain
: DOMAIN STRING
{
po_callback_domain ($2.string);
}
;
message
: message_intro string_list MSGSTR string_list
{
char *string2 = string_list_concat_destroy (&$2.stringlist);
char *string4 = string_list_concat_destroy (&$4.stringlist);
check_obsolete ($1, $2);
check_obsolete ($1, $3);
check_obsolete ($1, $4);
if (!$1.obsolete || pass_obsolete_entries)
do_callback_message ($1.ctxt, string2, &$1.pos, NULL,
string4, strlen (string4) + 1, &$3.pos,
$1.prev_ctxt,
$1.prev_id, $1.prev_id_plural,
$1.obsolete);
else
{
free_message_intro ($1);
free (string2);
free (string4);
}
}
| message_intro string_list msgid_pluralform pluralform_list
{
char *string2 = string_list_concat_destroy (&$2.stringlist);
check_obsolete ($1, $2);
check_obsolete ($1, $3);
check_obsolete ($1, $4);
if (!$1.obsolete || pass_obsolete_entries)
do_callback_message ($1.ctxt, string2, &$1.pos, $3.string,
$4.rhs.msgstr, $4.rhs.msgstr_len, &$4.pos,
$1.prev_ctxt,
$1.prev_id, $1.prev_id_plural,
$1.obsolete);
else
{
free_message_intro ($1);
free (string2);
free ($3.string);
free ($4.rhs.msgstr);
}
}
| message_intro string_list msgid_pluralform
{
check_obsolete ($1, $2);
check_obsolete ($1, $3);
po_gram_error_at_line (&$1.pos, _("missing 'msgstr[]' section"));
free_message_intro ($1);
string_list_destroy (&$2.stringlist);
free ($3.string);
}
| message_intro string_list pluralform_list
{
check_obsolete ($1, $2);
check_obsolete ($1, $3);
po_gram_error_at_line (&$1.pos, _("missing 'msgid_plural' section"));
free_message_intro ($1);
string_list_destroy (&$2.stringlist);
free ($3.rhs.msgstr);
}
| message_intro string_list
{
check_obsolete ($1, $2);
po_gram_error_at_line (&$1.pos, _("missing 'msgstr' section"));
free_message_intro ($1);
string_list_destroy (&$2.stringlist);
}
;
message_intro
: msg_intro
{
$$.prev_ctxt = NULL;
$$.prev_id = NULL;
$$.prev_id_plural = NULL;
$$.ctxt = $1.string;
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
| prev msg_intro
{
check_obsolete ($1, $2);
$$.prev_ctxt = $1.ctxt;
$$.prev_id = $1.id;
$$.prev_id_plural = $1.id_plural;
$$.ctxt = $2.string;
$$.pos = $2.pos;
$$.obsolete = $2.obsolete;
}
;
prev
: prev_msg_intro prev_string_list
{
check_obsolete ($1, $2);
$$.ctxt = $1.string;
$$.id = string_list_concat_destroy (&$2.stringlist);
$$.id_plural = NULL;
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
| prev_msg_intro prev_string_list prev_msgid_pluralform
{
check_obsolete ($1, $2);
check_obsolete ($1, $3);
$$.ctxt = $1.string;
$$.id = string_list_concat_destroy (&$2.stringlist);
$$.id_plural = $3.string;
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
;
msg_intro
: MSGID
{
$$.string = NULL;
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
| MSGCTXT string_list MSGID
{
check_obsolete ($1, $2);
check_obsolete ($1, $3);
$$.string = string_list_concat_destroy (&$2.stringlist);
$$.pos = $3.pos;
$$.obsolete = $3.obsolete;
}
;
prev_msg_intro
: PREV_MSGID
{
$$.string = NULL;
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
| PREV_MSGCTXT prev_string_list PREV_MSGID
{
check_obsolete ($1, $2);
check_obsolete ($1, $3);
$$.string = string_list_concat_destroy (&$2.stringlist);
$$.pos = $3.pos;
$$.obsolete = $3.obsolete;
}
;
msgid_pluralform
: MSGID_PLURAL string_list
{
check_obsolete ($1, $2);
plural_counter = 0;
$$.string = string_list_concat_destroy (&$2.stringlist);
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
;
prev_msgid_pluralform
: PREV_MSGID_PLURAL prev_string_list
{
check_obsolete ($1, $2);
$$.string = string_list_concat_destroy (&$2.stringlist);
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
;
pluralform_list
: pluralform
{
$$ = $1;
}
| pluralform_list pluralform
{
check_obsolete ($1, $2);
$$.rhs.msgstr = XNMALLOC ($1.rhs.msgstr_len + $2.rhs.msgstr_len, char);
memcpy ($$.rhs.msgstr, $1.rhs.msgstr, $1.rhs.msgstr_len);
memcpy ($$.rhs.msgstr + $1.rhs.msgstr_len, $2.rhs.msgstr, $2.rhs.msgstr_len);
$$.rhs.msgstr_len = $1.rhs.msgstr_len + $2.rhs.msgstr_len;
free ($1.rhs.msgstr);
free ($2.rhs.msgstr);
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
;
pluralform
: MSGSTR '[' NUMBER ']' string_list
{
check_obsolete ($1, $2);
check_obsolete ($1, $3);
check_obsolete ($1, $4);
check_obsolete ($1, $5);
if ($3.number != plural_counter)
{
if (plural_counter == 0)
po_gram_error_at_line (&$1.pos, _("first plural form has nonzero index"));
else
po_gram_error_at_line (&$1.pos, _("plural form has wrong index"));
}
plural_counter++;
$$.rhs.msgstr = string_list_concat_destroy (&$5.stringlist);
$$.rhs.msgstr_len = strlen ($$.rhs.msgstr) + 1;
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
;
string_list
: STRING
{
string_list_init (&$$.stringlist);
string_list_append (&$$.stringlist, $1.string);
free ($1.string);
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
| string_list STRING
{
check_obsolete ($1, $2);
$$.stringlist = $1.stringlist;
string_list_append (&$$.stringlist, $2.string);
free ($2.string);
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
;
prev_string_list
: PREV_STRING
{
string_list_init (&$$.stringlist);
string_list_append (&$$.stringlist, $1.string);
free ($1.string);
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
| prev_string_list PREV_STRING
{
check_obsolete ($1, $2);
$$.stringlist = $1.stringlist;
string_list_append (&$$.stringlist, $2.string);
free ($2.string);
$$.pos = $1.pos;
$$.obsolete = $1.obsolete;
}
;