source: abnfparser/bap/parser.y @ 504

Last change on this file since 504 was 407, checked in by ylafon@…, 14 years ago

unixism

File size: 10.6 KB
Line 
1%{
2/*
3 * Bill's ABNF parser.
4 * Copyright 2002-2004 William C. Fenner <fenner@fenron.com>
5 *  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the author nor the names of contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY WILLIAM C. FENNER ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WILLIAM C. FENNER OR HIS
23 * BROTHER B1FF BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
29 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30 * DAMAGE.
31 */
32
33#include "config.h"
34#include <stdio.h>
35#include <stdlib.h>
36#include <stdarg.h>
37#include <string.h>
38
39#include "common.h"
40
41static const char rcsid[] =
42 "$Id: parser.y,v 1.1 2008-05-28 12:01:58 jre Exp $";
43
44extern int yylineno, yycolumn, yyerrors;
45
46int defline;
47extern struct rule *rules;
48extern char *input_file;
49
50#if defined(YYERROR_VERBOSE) && defined(YYBISON)
51#define MYERROR_VERBOSE
52#endif
53#ifdef MYERROR_VERBOSE
54/* HACK-O-ROONIE for yyerror verbosity */
55int *yystatep = NULL;
56int *yychar1p = NULL;
57#endif
58
59int pipewarn = 0;
60
61object *newobj(int);
62int yyerror(char *);
63int yylex(void);
64%}
65
66%union {
67        char *string;
68        struct range range;
69        object *object;
70        int retval;
71}
72
73%token <string> CHARVAL PROSEVAL BINVAL DECVAL HEXVAL RULENAME
74%token <range> BINVALRANGE DECVALRANGE HEXVALRANGE REPEAT LIST
75%token CWSP EQSLASH CRLF
76
77%type <string> numval
78%type <range> numvalrange
79%type <object> element group option repetition list elements
80%type <object> rulerest
81%type <retval> definedas
82
83%%
84
85begin:  {
86#ifdef MYERROR_VERBOSE
87        /* HACK-O-RAMA */ yystatep = &yystate; yychar1p = &yychar1;
88#endif
89                } rulelist
90        ;
91
92rulelist: rules
93        | rulelist rules
94        ;
95
96rules:    rule
97        | starcwsp CRLF
98        | cwsp RULENAME { mywarn(MYERROR, "Indented rules are Right Out."); YYERROR; }
99        ;
100
101recover:
102        | error CRLF
103        ;
104
105rule:   recover RULENAME { defline = yylineno; } definedas rulerest {
106                struct rule *r;
107
108                r = findrule($2);
109
110                if ($4 == 0 || r->name == NULL || r->rule == NULL) {    /* = */
111                        if ($4) {
112                                mywarn(MYERROR, "Rule %s does not yet exist; treating /= as =", $2);
113                        }
114                        if (r->name && strcmp(r->name, $2))
115                                if (r->rule)
116                                        mywarn(MYERROR, "Rule %s previously defined as %s on line %d",
117                                                $2, r->name, r->line);
118                                else
119                                        mywarn(MYWARNING, "rule %s previously referred to as %s",
120                                                $2, r->name);
121                        if (r->rule)
122                                mywarn(MYERROR, "Rule %s was already defined on line %d of %s", $2,
123               r->line, (r->file? r->file : "stdin"));
124                        else {
125                                r->name = $2;
126                                r->line = defline;
127        r->file = input_file;
128                                r->rule = $5;
129                                if (r->next != rules) {
130                                        /* unlink r from where it is and move to the end */
131                                        r->prev->next = r->next;
132                                        r->next->prev = r->prev;
133                                        if (rules == r)
134                                                rules = r->next;
135                                        r->prev = rules->prev;
136                                        rules->prev = r;
137                                        r->prev->next = r;
138                                        r->next = rules;
139                                }
140                        }
141                } else {        /* =/ */
142                        object *tmp;
143
144                        tmp = newobj(T_ALTERNATION);
145                        tmp->u.alternation.left = r->rule;
146                        tmp->u.alternation.right = $5;
147                        r->rule = tmp;
148                }
149                }
150        ;
151
152definedas: starcwsp EQSLASH starcwsp    { $$ = 1; }
153        | starcwsp '=' starcwsp         { $$ = 0; }
154        | starcwsp '=' starcwsp CRLF { mywarn(MYERROR, "Empty rule"); YYERROR; /* XXX this CRLF may be the one required to recover from the error, but we've already used it... */}
155        | starcwsp repetition { mywarn(MYERROR, "Got definitions, expecting '=' or '=/'"); YYERROR; }
156        | starcwsp CRLF { mywarn(MYERROR, "Got EOL, expecting '=' or '=/'"); YYERROR; /* XXX this CRLF may be the one required to recover from the error, but we've already used it... */}
157        ;
158
159cwsp:     CWSP
160        ;
161
162starcwsp:
163        | CWSP
164        /* | CRLF       { mywarn(MYERROR, "Blank lines are not permitted inside rules"); YYERROR; } */
165        ;
166
167rulerest: elements starcwsp CRLF        { $$ = $1; }
168        | elements ')'  { mywarn(MYERROR, "Extra ')'?  Missing '('?"); YYERROR; }
169        | elements ']'  { mywarn(MYERROR, "Extra ']'?  Missing '['?"); YYERROR; }
170        ;
171
172elements:
173          repetition
174  | list
175        | elements cwsp repetition                      {
176                /* concatenation */
177                object *o = $1;
178
179                $$ = $1;
180                if (o->type == T_ALTERNATION)
181                        o = o->u.alternation.right;
182                while (o->next) /* n**2, do this better */
183                        o = o->next;
184                o->next = $3;
185                }
186        | elements cwsp list                    {
187                /* concatenation */
188                object *o = $1;
189
190                $$ = $1;
191                if (o->type == T_ALTERNATION)
192                        o = o->u.alternation.right;
193                while (o->next) /* n**2, do this better */
194                        o = o->next;
195                o->next = $3;
196                }
197        | elements starcwsp '/' starcwsp repetition     {
198                $$ = newobj(T_ALTERNATION);
199                $$->u.alternation.left = $1;
200                $$->u.alternation.right = $5;
201                }
202        | elements starcwsp '|' starcwsp repetition     {
203                if (!pipewarn) {
204                        mywarn(MYERROR, "'/' is the alternation character in ABNF");
205                        pipewarn = 1;
206                }
207                $$ = newobj(T_ALTERNATION);
208                $$->u.alternation.left = $1;
209                $$->u.alternation.right = $5;
210                }
211        | elements starcwsp '/' starcwsp list   {
212                $$ = newobj(T_ALTERNATION);
213                $$->u.alternation.left = $1;
214                $$->u.alternation.right = $5;
215                }
216        | elements starcwsp '|' starcwsp list   {
217                if (!pipewarn) {
218                        mywarn(MYERROR, "'/' is the alternation character in ABNF");
219                        pipewarn = 1;
220                }
221                $$ = newobj(T_ALTERNATION);
222                $$->u.alternation.left = $1;
223                $$->u.alternation.right = $5;
224                }
225        | elements repetition {
226                object *o = $1;
227                mywarn(MYERROR, "Concatenation of adjacent elements is not allowed (missing whitespace?)");
228                printf("; line %d ... trying to concatenate ", yylineno);
229                if (o->type == T_ALTERNATION)
230                        o = o->u.alternation.right;
231                while (o->next) /* n**2, do this better */
232                        o = o->next;
233                printobj(o, 1);
234                printf("\n; ... with ");
235                printobj($2, 1);
236                printf("\n");
237                YYERROR;
238                }
239        | elements starcwsp '=' {
240                mywarn(MYERROR, "Encountered definition while parsing rule (Indented rule?)");
241                YYERROR;
242                }
243        | elements starcwsp EQSLASH {
244                mywarn(MYERROR, "Encountered definition while parsing rule (Indented rule?)");
245                YYERROR;
246                }
247        ;
248
249repetition:
250          element
251        | REPEAT element        {
252                                $$ = $2;
253                                /* 5*10[foo] is really *10(foo), so leave
254                                 * the zero that [ put there. */
255                                if ($$->u.e.repetition.lo != 0)
256                                        $$->u.e.repetition.lo = $1.lo;
257                                $$->u.e.repetition.hi = $1.hi;
258                                if ($1.hi < $1.lo && $1.hi != -1)
259                                        mywarn(MYERROR, "Repeat range swapped, should be min*max");
260                                if ($1.hi == 0)
261                                        mywarn(MYFYI, "absolute repeat count of zero means this element may not occur at all");
262                                }
263        | REPEAT cwsp           {
264                                mywarn(MYERROR, "No whitespace allowed between repeat and element.");
265                                YYERROR;
266                                }
267        ;
268
269list:
270        LIST element    {
271                                $$ = $2;
272                                if ($$->u.e.repetition.lo != 0)
273                                        $$->u.e.repetition.lo = $1.lo;
274                                $$->u.e.repetition.hi = $1.hi;
275                                if ($1.hi < $1.lo && $1.hi != -1)
276                                        mywarn(MYERROR, "List range swapped, should be min*max");
277                                if ($1.hi == 0)
278                                        mywarn(MYFYI, "absolute list count of zero means this element may not occur at all");
279        if ($1.lo != 0 && $1.lo != 1 && $1.lo != -1)
280                                        mywarn(MYERROR, "List range min must be 0 or 1");
281        if ($1.hi != -1)
282                                        mywarn(MYERROR, "List range max must be infinity");
283        $$->u.e.islist = 1;
284                                }
285        | LIST cwsp             {
286                                mywarn(MYERROR, "No whitespace allowed between list and element.");
287                                YYERROR;
288                                }
289        ;
290
291numval:   BINVAL
292        | DECVAL
293        | HEXVAL
294        ;
295
296numvalrange:
297          BINVALRANGE
298        | DECVALRANGE
299        | HEXVALRANGE
300        ;
301
302element:
303          RULENAME              {
304                                $$ = newobj(T_RULE);
305                                $$->u.e.e.rule.name = $1;
306                                $$->u.e.e.rule.rule = findrule($1);
307                                if (strcmp($1, $$->u.e.e.rule.rule->name))
308                                        mywarn(MYWARNING, "rule %s defined on line %d referred to as %s", $$->u.e.e.rule.rule->name, $$->u.e.e.rule.rule->line, $1);
309                                }
310        | group
311        | option
312        | CHARVAL               {
313                                char *p = $1;
314                                if (*$1)
315                                        p += strlen($1) - 1;
316                                if (*p == '\n' || *p == '\r') {
317                                        mywarn(MYERROR, "unterminated quoted-string");
318                                        YYERROR;
319                                }
320                                $$ = newobj(T_TERMSTR);
321                                $$->u.e.e.termstr.str = $1;
322                                $$->u.e.e.termstr.flags = 0;
323                                }
324        | numval                {
325                                $$ = newobj(T_TERMSTR);
326                                $$->u.e.e.termstr.str = $1;
327                                $$->u.e.e.termstr.flags = F_CASESENSITIVE;
328                                }
329        | numvalrange           {
330                                $$ = newobj(T_TERMRANGE);
331                                $$->u.e.e.termrange.lo = $1.lo;
332                                $$->u.e.e.termrange.hi = $1.hi;
333                                }
334        | PROSEVAL              {
335                                $$ = newobj(T_PROSE);
336                                $$->u.e.e.proseval = $1;
337                                if (strcmp($1, "\"") == 0) {
338                                        mywarn(MYFYI, "suggest DQUOTE or %%x22 instead of <\">.");
339                                }
340                                }
341        ;
342
343group:    '(' starcwsp elements starcwsp ')'    {
344                                                $$ = newobj(T_GROUP);
345                                                $$->u.e.e.group = $3;
346                                                }
347        | '(' starcwsp elements starcwsp CRLF {
348                mywarn(MYERROR, "Missing ')'?  Extra '('?");
349                YYERROR;
350                }
351        ;
352
353option:   '[' starcwsp elements starcwsp ']'    {
354                                                $$ = newobj(T_GROUP);
355                                                $$->u.e.e.group = $3;
356                                                $$->u.e.repetition.lo = 0;
357                                                }
358        | '[' starcwsp elements starcwsp CRLF {
359                mywarn(MYERROR, "Missing ']'?  Extra '['?");
360                YYERROR;
361                }
362        ;
363
364%%
365
366void
367mywarn(int level, const char *fmt, ...)
368{
369        va_list ap;
370
371        /* file name */
372        fprintf(stderr, "%s(%d:%d): ", input_file, yylineno, yycolumn);
373        switch (level) {
374                case MYFYI: fprintf(stderr, "fyi: "); break;
375                case MYWARNING: fprintf(stderr, "warning: "); break;
376                case MYERROR: ++yyerrors; /* fall through */
377                default: fprintf(stderr, "error: "); break;
378        }
379        va_start(ap, fmt);
380        vfprintf(stderr, fmt, ap);
381        fprintf(stderr, "\n");
382}
383
384int
385yyerror(char *s)
386{
387#ifdef MYERROR_VERBOSE
388        mywarn(MYERROR, "state %d, token %s: %s",
389                *yystatep,
390                (yychar1p && (*yychar1p >= 0 && *yychar1p <= (sizeof(yytname)/sizeof(yytname[0])))) ? yytname[*yychar1p] : "?",
391                s);
392#else
393        mywarn(MYERROR, "%s\n", s);
394#endif
395        return 0;
396}
397
398object *
399newobj(int type)
400{
401        object *o;
402
403        o = calloc(sizeof(object), 1);
404        if (o == NULL) {
405                mywarn(MYERROR, "out of memory");
406                exit(1);
407        }
408        o->type = type;
409        o->next = NULL;
410        switch (type) {
411                case T_RULE:
412                case T_GROUP:
413                case T_TERMSTR:
414                case T_TERMRANGE:
415                case T_PROSE:
416                        o->u.e.repetition.lo = 1;
417                        o->u.e.repetition.hi = 1;
418                        break;
419        }
420        return o;
421}
Note: See TracBrowser for help on using the repository browser.