xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/src/po-gram-gen.y (revision 4c3eb207d36f67d31994830c0a694161fc1ca39b)
1 /* GNU gettext - internationalization aids
2    Copyright (C) 1995-1996, 1998, 2000-2001, 2003, 2005-2006 Free Software Foundation, Inc.
3 
4    This file was written by Peter Miller <pmiller@agso.gov.au>
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software Foundation,
18    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19 
20 %{
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24 
25 /* Specification.  */
26 #include "po-gram.h"
27 
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "str-list.h"
34 #include "po-lex.h"
35 #include "po-charset.h"
36 #include "error.h"
37 #include "xalloc.h"
38 #include "gettext.h"
39 #include "read-catalog-abstract.h"
40 
41 #define _(str) gettext (str)
42 
43 /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
44    as well as gratuitiously global symbol names, so we can have multiple
45    yacc generated parsers in the same program.  Note that these are only
46    the variables produced by yacc.  If other parser generators (bison,
47    byacc, etc) produce additional global names that conflict at link time,
48    then those parser generators need to be fixed instead of adding those
49    names to this list. */
50 
51 #define yymaxdepth po_gram_maxdepth
52 #define yyparse po_gram_parse
53 #define yylex   po_gram_lex
54 #define yyerror po_gram_error
55 #define yylval  po_gram_lval
56 #define yychar  po_gram_char
57 #define yydebug po_gram_debug
58 #define yypact  po_gram_pact
59 #define yyr1    po_gram_r1
60 #define yyr2    po_gram_r2
61 #define yydef   po_gram_def
62 #define yychk   po_gram_chk
63 #define yypgo   po_gram_pgo
64 #define yyact   po_gram_act
65 #define yyexca  po_gram_exca
66 #define yyerrflag po_gram_errflag
67 #define yynerrs po_gram_nerrs
68 #define yyps    po_gram_ps
69 #define yypv    po_gram_pv
70 #define yys     po_gram_s
71 #define yy_yys  po_gram_yys
72 #define yystate po_gram_state
73 #define yytmp   po_gram_tmp
74 #define yyv     po_gram_v
75 #define yy_yyv  po_gram_yyv
76 #define yyval   po_gram_val
77 #define yylloc  po_gram_lloc
78 #define yyreds  po_gram_reds          /* With YYDEBUG defined */
79 #define yytoks  po_gram_toks          /* With YYDEBUG defined */
80 #define yylhs   po_gram_yylhs
81 #define yylen   po_gram_yylen
82 #define yydefred po_gram_yydefred
83 #define yydgoto po_gram_yydgoto
84 #define yysindex po_gram_yysindex
85 #define yyrindex po_gram_yyrindex
86 #define yygindex po_gram_yygindex
87 #define yytable  po_gram_yytable
88 #define yycheck  po_gram_yycheck
89 
90 static long plural_counter;
91 
92 #define check_obsolete(value1,value2) \
93   if ((value1).obsolete != (value2).obsolete) \
94     po_gram_error_at_line (&(value2).pos, _("inconsistent use of #~"));
95 
96 static inline void
97 do_callback_message (char *msgctxt,
98 		     char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
99 		     char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
100 		     char *prev_msgctxt,
101 		     char *prev_msgid, char *prev_msgid_plural,
102 		     bool obsolete)
103 {
104   /* Test for header entry.  Ignore fuzziness of the header entry.  */
105   if (msgctxt == NULL && msgid[0] == '\0' && !obsolete)
106     po_lex_charset_set (msgstr, gram_pos.file_name);
107 
108   po_callback_message (msgctxt,
109 		       msgid, msgid_pos, msgid_plural,
110 		       msgstr, msgstr_len, msgstr_pos,
111 		       prev_msgctxt, prev_msgid, prev_msgid_plural,
112 		       false, obsolete);
113 }
114 
115 #define free_message_intro(value) \
116   if ((value).prev_ctxt != NULL)	\
117     free ((value).prev_ctxt);		\
118   if ((value).prev_id != NULL)		\
119     free ((value).prev_id);		\
120   if ((value).prev_id_plural != NULL)	\
121     free ((value).prev_id_plural);	\
122   if ((value).ctxt != NULL)		\
123     free ((value).ctxt);
124 
125 %}
126 
127 %token	COMMENT
128 %token	DOMAIN
129 %token	JUNK
130 %token	PREV_MSGCTXT
131 %token	PREV_MSGID
132 %token	PREV_MSGID_PLURAL
133 %token	PREV_STRING
134 %token	MSGCTXT
135 %token	MSGID
136 %token	MSGID_PLURAL
137 %token	MSGSTR
138 %token	NAME
139 %token	'[' ']'
140 %token	NUMBER
141 %token	STRING
142 
143 %union
144 {
145   struct { char *string; lex_pos_ty pos; bool obsolete; } string;
146   struct { string_list_ty stringlist; lex_pos_ty pos; bool obsolete; } stringlist;
147   struct { long number; lex_pos_ty pos; bool obsolete; } number;
148   struct { lex_pos_ty pos; bool obsolete; } pos;
149   struct { char *ctxt; char *id; char *id_plural; lex_pos_ty pos; bool obsolete; } prev;
150   struct { char *prev_ctxt; char *prev_id; char *prev_id_plural; char *ctxt; lex_pos_ty pos; bool obsolete; } message_intro;
151   struct { struct msgstr_def rhs; lex_pos_ty pos; bool obsolete; } rhs;
152 }
153 
154 %type <string> STRING PREV_STRING COMMENT NAME
155                msg_intro prev_msg_intro msgid_pluralform prev_msgid_pluralform
156 %type <stringlist> string_list prev_string_list
157 %type <number> NUMBER
158 %type <pos> DOMAIN
159             PREV_MSGCTXT PREV_MSGID PREV_MSGID_PLURAL
160             MSGCTXT MSGID MSGID_PLURAL MSGSTR '[' ']'
161 %type <prev> prev
162 %type <message_intro> message_intro
163 %type <rhs> pluralform pluralform_list
164 
165 %right MSGSTR
166 
167 %%
168 
169 po_file
170 	: /* empty */
171 	| po_file comment
172 	| po_file domain
173 	| po_file message
174 	| po_file error
175 	;
176 
177 
178 comment
179 	: COMMENT
180 		{
181 		  po_callback_comment_dispatcher ($1.string);
182 		}
183 	;
184 
185 
186 domain
187 	: DOMAIN STRING
188 		{
189 		   po_callback_domain ($2.string);
190 		}
191 	;
192 
193 
194 message
195 	: message_intro string_list MSGSTR string_list
196 		{
197 		  char *string2 = string_list_concat_destroy (&$2.stringlist);
198 		  char *string4 = string_list_concat_destroy (&$4.stringlist);
199 
200 		  check_obsolete ($1, $2);
201 		  check_obsolete ($1, $3);
202 		  check_obsolete ($1, $4);
203 		  if (!$1.obsolete || pass_obsolete_entries)
204 		    do_callback_message ($1.ctxt, string2, &$1.pos, NULL,
205 					 string4, strlen (string4) + 1, &$3.pos,
206 					 $1.prev_ctxt,
207 					 $1.prev_id, $1.prev_id_plural,
208 					 $1.obsolete);
209 		  else
210 		    {
211 		      free_message_intro ($1);
212 		      free (string2);
213 		      free (string4);
214 		    }
215 		}
216 	| message_intro string_list msgid_pluralform pluralform_list
217 		{
218 		  char *string2 = string_list_concat_destroy (&$2.stringlist);
219 
220 		  check_obsolete ($1, $2);
221 		  check_obsolete ($1, $3);
222 		  check_obsolete ($1, $4);
223 		  if (!$1.obsolete || pass_obsolete_entries)
224 		    do_callback_message ($1.ctxt, string2, &$1.pos, $3.string,
225 					 $4.rhs.msgstr, $4.rhs.msgstr_len, &$4.pos,
226 					 $1.prev_ctxt,
227 					 $1.prev_id, $1.prev_id_plural,
228 					 $1.obsolete);
229 		  else
230 		    {
231 		      free_message_intro ($1);
232 		      free (string2);
233 		      free ($3.string);
234 		      free ($4.rhs.msgstr);
235 		    }
236 		}
237 	| message_intro string_list msgid_pluralform
238 		{
239 		  check_obsolete ($1, $2);
240 		  check_obsolete ($1, $3);
241 		  po_gram_error_at_line (&$1.pos, _("missing `msgstr[]' section"));
242 		  free_message_intro ($1);
243 		  string_list_destroy (&$2.stringlist);
244 		  free ($3.string);
245 		}
246 	| message_intro string_list pluralform_list
247 		{
248 		  check_obsolete ($1, $2);
249 		  check_obsolete ($1, $3);
250 		  po_gram_error_at_line (&$1.pos, _("missing `msgid_plural' section"));
251 		  free_message_intro ($1);
252 		  string_list_destroy (&$2.stringlist);
253 		  free ($3.rhs.msgstr);
254 		}
255 	| message_intro string_list
256 		{
257 		  check_obsolete ($1, $2);
258 		  po_gram_error_at_line (&$1.pos, _("missing `msgstr' section"));
259 		  free_message_intro ($1);
260 		  string_list_destroy (&$2.stringlist);
261 		}
262 	;
263 
264 
265 message_intro
266 	: msg_intro
267 		{
268 		  $$.prev_ctxt = NULL;
269 		  $$.prev_id = NULL;
270 		  $$.prev_id_plural = NULL;
271 		  $$.ctxt = $1.string;
272 		  $$.pos = $1.pos;
273 		  $$.obsolete = $1.obsolete;
274 		}
275 	| prev msg_intro
276 		{
277 		  check_obsolete ($1, $2);
278 		  $$.prev_ctxt = $1.ctxt;
279 		  $$.prev_id = $1.id;
280 		  $$.prev_id_plural = $1.id_plural;
281 		  $$.ctxt = $2.string;
282 		  $$.pos = $2.pos;
283 		  $$.obsolete = $2.obsolete;
284 		}
285 	;
286 
287 
288 prev
289 	: prev_msg_intro prev_string_list
290 		{
291 		  check_obsolete ($1, $2);
292 		  $$.ctxt = $1.string;
293 		  $$.id = string_list_concat_destroy (&$2.stringlist);
294 		  $$.id_plural = NULL;
295 		  $$.pos = $1.pos;
296 		  $$.obsolete = $1.obsolete;
297 		}
298 	| prev_msg_intro prev_string_list prev_msgid_pluralform
299 		{
300 		  check_obsolete ($1, $2);
301 		  check_obsolete ($1, $3);
302 		  $$.ctxt = $1.string;
303 		  $$.id = string_list_concat_destroy (&$2.stringlist);
304 		  $$.id_plural = $3.string;
305 		  $$.pos = $1.pos;
306 		  $$.obsolete = $1.obsolete;
307 		}
308 	;
309 
310 
311 msg_intro
312 	: MSGID
313 		{
314 		  $$.string = NULL;
315 		  $$.pos = $1.pos;
316 		  $$.obsolete = $1.obsolete;
317 		}
318 	| MSGCTXT string_list MSGID
319 		{
320 		  check_obsolete ($1, $2);
321 		  check_obsolete ($1, $3);
322 		  $$.string = string_list_concat_destroy (&$2.stringlist);
323 		  $$.pos = $3.pos;
324 		  $$.obsolete = $3.obsolete;
325 		}
326 	;
327 
328 prev_msg_intro
329 	: PREV_MSGID
330 		{
331 		  $$.string = NULL;
332 		  $$.pos = $1.pos;
333 		  $$.obsolete = $1.obsolete;
334 		}
335 	| PREV_MSGCTXT prev_string_list PREV_MSGID
336 		{
337 		  check_obsolete ($1, $2);
338 		  check_obsolete ($1, $3);
339 		  $$.string = string_list_concat_destroy (&$2.stringlist);
340 		  $$.pos = $3.pos;
341 		  $$.obsolete = $3.obsolete;
342 		}
343 	;
344 
345 
346 msgid_pluralform
347 	: MSGID_PLURAL string_list
348 		{
349 		  check_obsolete ($1, $2);
350 		  plural_counter = 0;
351 		  $$.string = string_list_concat_destroy (&$2.stringlist);
352 		  $$.pos = $1.pos;
353 		  $$.obsolete = $1.obsolete;
354 		}
355 	;
356 
357 prev_msgid_pluralform
358 	: PREV_MSGID_PLURAL prev_string_list
359 		{
360 		  check_obsolete ($1, $2);
361 		  $$.string = string_list_concat_destroy (&$2.stringlist);
362 		  $$.pos = $1.pos;
363 		  $$.obsolete = $1.obsolete;
364 		}
365 	;
366 
367 
368 pluralform_list
369 	: pluralform
370 		{
371 		  $$ = $1;
372 		}
373 	| pluralform_list pluralform
374 		{
375 		  check_obsolete ($1, $2);
376 		  $$.rhs.msgstr = (char *) xmalloc ($1.rhs.msgstr_len + $2.rhs.msgstr_len);
377 		  memcpy ($$.rhs.msgstr, $1.rhs.msgstr, $1.rhs.msgstr_len);
378 		  memcpy ($$.rhs.msgstr + $1.rhs.msgstr_len, $2.rhs.msgstr, $2.rhs.msgstr_len);
379 		  $$.rhs.msgstr_len = $1.rhs.msgstr_len + $2.rhs.msgstr_len;
380 		  free ($1.rhs.msgstr);
381 		  free ($2.rhs.msgstr);
382 		  $$.pos = $1.pos;
383 		  $$.obsolete = $1.obsolete;
384 		}
385 	;
386 
387 pluralform
388 	: MSGSTR '[' NUMBER ']' string_list
389 		{
390 		  check_obsolete ($1, $2);
391 		  check_obsolete ($1, $3);
392 		  check_obsolete ($1, $4);
393 		  check_obsolete ($1, $5);
394 		  if ($3.number != plural_counter)
395 		    {
396 		      if (plural_counter == 0)
397 			po_gram_error_at_line (&$1.pos, _("first plural form has nonzero index"));
398 		      else
399 			po_gram_error_at_line (&$1.pos, _("plural form has wrong index"));
400 		    }
401 		  plural_counter++;
402 		  $$.rhs.msgstr = string_list_concat_destroy (&$5.stringlist);
403 		  $$.rhs.msgstr_len = strlen ($$.rhs.msgstr) + 1;
404 		  $$.pos = $1.pos;
405 		  $$.obsolete = $1.obsolete;
406 		}
407 	;
408 
409 
410 string_list
411 	: STRING
412 		{
413 		  string_list_init (&$$.stringlist);
414 		  string_list_append (&$$.stringlist, $1.string);
415 		  $$.pos = $1.pos;
416 		  $$.obsolete = $1.obsolete;
417 		}
418 	| string_list STRING
419 		{
420 		  check_obsolete ($1, $2);
421 		  $$.stringlist = $1.stringlist;
422 		  string_list_append (&$$.stringlist, $2.string);
423 		  $$.pos = $1.pos;
424 		  $$.obsolete = $1.obsolete;
425 		}
426 	;
427 
428 prev_string_list
429 	: PREV_STRING
430 		{
431 		  string_list_init (&$$.stringlist);
432 		  string_list_append (&$$.stringlist, $1.string);
433 		  $$.pos = $1.pos;
434 		  $$.obsolete = $1.obsolete;
435 		}
436 	| prev_string_list PREV_STRING
437 		{
438 		  check_obsolete ($1, $2);
439 		  $$.stringlist = $1.stringlist;
440 		  string_list_append (&$$.stringlist, $2.string);
441 		  $$.pos = $1.pos;
442 		  $$.obsolete = $1.obsolete;
443 		}
444 	;
445