]> git.draconx.ca Git - cdecl99.git/blob - src/scan.l
Avoid warnings about unused scanner functions.
[cdecl99.git] / src / scan.l
1 %top{
2 /*
3  *  Scanner for C declarations.
4  *  Copyright © 2011, 2021, 2023 Nick Bowler
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 3 of the License, or
9  *  (at your option) 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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include "parse.h"
22
23 #define YY_NO_INPUT 1
24 #define YY_NO_UNPUT 1
25 }
26
27 %option nodefault noyywrap bison-locations reentrant never-interactive
28 %option extra-type="int"
29 %option prefix="cdecl__yy"
30
31 %{
32 #include <ctype.h>
33 #include "cdecl-internal.h"
34 #include "cdecl.h"
35 #include "errmsg.h"
36
37 #if HAVE_STRTOUMAX
38 /* Best case, implementation provides strtoumax. */
39 #  define STRTOUMAX strtoumax
40 #elif HAVE_STRTOULL
41 /* Fall back to strtoull, with possibly reduced range. */
42 #define STRTOUMAX strtoull
43 #elif HAVE___STRTOULL
44 /* HP-UX 11 has __strtoull in <inttypes.h> */
45 #define STRTOUMAX __strtoull
46 #else
47 /* Fall back to strtoul, with possibly reduced range. */
48 #define STRTOUMAX strtoul
49 #endif
50
51 #define dup_token() do { \
52         yylval->strval = malloc(yyleng+1); \
53         if (!yylval->strval) { \
54                 cdecl__errmsg(CDECL__ENOMEM); \
55                 return T_LEX_ERROR; \
56         } \
57         memcpy(yylval->strval, yytext, yyleng); \
58         yylval->strval[yyleng] = 0; \
59 } while(0)
60
61 static char *to_octal(char *dst, unsigned val)
62 {
63         unsigned i;
64
65         for (i = 0; i < 3; i++) {
66                 *dst++ = '0' + ((val >> 6) & 7u);
67                 val <<= 3;
68         }
69
70         return dst;
71 }
72
73 /*
74  * Convert a single character to a C-style character constant, including quote
75  * characters.  At most 7 bytes are written to the buffer for the longest
76  * octal encoding, e.g., '\177'
77  */
78 static void to_readable_ch(char *dst, char c)
79 {
80         unsigned char uc = c;
81         unsigned i;
82         char esc;
83
84         /*
85          * The 7 standard C control characters are contiguous in ASCII,
86          * permitting a simple and compact lookup table; separating their
87          * handling from backslash and quote characters hopefully allows
88          * the compiler to recognize that.
89          */
90         switch (c) {
91         case '\a': i = 0; break;
92         case '\b': i = 1; break;
93         case '\t': i = 2; break;
94         case '\n': i = 3; break;
95         case '\v': i = 4; break;
96         case '\f': i = 5; break;
97         case '\r': i = 6; break;
98         default:   i = 7; break;
99         }
100         esc = "abtnvfr"[i];
101
102         /* Otherwise printable characters that should still be escaped. */
103         switch (c) {
104         case '\\': case '\'': esc = c; break;
105         }
106
107         *dst++ = '\'';
108         if (esc) {
109                 *dst++ = '\\';
110                 *dst++ = esc;
111         } else if (isprint(uc)) {
112                 *dst++ = c;
113         } else {
114                 *dst++ = '\\';
115                 dst = to_octal(dst, uc);
116         }
117         *dst++ = '\'';
118         *dst++ = 0;
119 }
120
121 %}
122
123 IDENT [_[:alpha:]][-_[:alnum:]]*
124 INTEGER 0x[[:xdigit:]]+|0[0-7]+|[[:digit:]]+
125
126 %%
127
128 %{
129         char *c;
130 %}
131
132 "..."|[][;*(),] {
133         unsigned char *match;
134         static const unsigned char tab[2][8] = {
135                 "*[](),.;",
136                 {
137                         PACK_TOKEN(T_ASTERISK),
138                         PACK_TOKEN(T_LBRACKET),
139                         PACK_TOKEN(T_RBRACKET),
140                         PACK_TOKEN(T_LPAREN),
141                         PACK_TOKEN(T_RPAREN),
142                         PACK_TOKEN(T_COMMA),
143                         PACK_TOKEN(T_ELLIPSIS),
144                         PACK_TOKEN(T_SEMICOLON)
145                 }
146         };
147
148         match = memchr(&tab, yytext[0], sizeof tab[0]);
149         return UNPACK_TOKEN(match[sizeof tab[0]]);
150 }
151
152 {INTEGER} {
153         char *end;
154
155         errno = 0;
156         yylval->uintval = STRTOUMAX(yytext, &end, 0);
157         if (errno == ERANGE) {
158                 cdecl__errmsg(CDECL__ERANGE);
159                 return T_LEX_ERROR;
160         }
161         if (*end) {
162                 cdecl__errmsg(CDECL__EBADINT);
163                 return T_LEX_ERROR;
164         }
165
166         return T_UINT;
167 }
168
169 {IDENT} {
170         unsigned x = cdecl__to_keyword(yytext, yyleng, yyextra);
171         int tok;
172
173         yylval->spectype = UNPACK_SPEC(x & 0xff);
174         if ((tok = (x >> 8)) == PACK_TOKEN(T_IDENT)) {
175                 /*
176                  * Our IDENT pattern includes hyphens so we can match
177                  * "variable-length" as a keyword.  In all other cases a
178                  * hyphen is an error.
179                  *
180                  * We could use yyless to re-scan the hyphen and hit the
181                  * error catch-all, but jumping straight to the error code
182                  * seems to produce better results with gcc with no obvious
183                  * downsides.
184                  */
185 #if 1
186                 if ((c = strchr(yytext, '-')))
187                         goto invalid_char;
188 #else
189                 yyless(strcspn(yytext, "-"));
190 #endif
191                 dup_token();
192         }
193         return UNPACK_TOKEN(tok);
194 }
195
196 [[:space:]]+
197 . {
198         char buf[8];
199
200         c = yytext;
201 invalid_char:
202         to_readable_ch(buf, *c);
203         cdecl__err(CDECL_ENOPARSE, _("syntax error, unexpected %s"), buf);
204         return T_LEX_ERROR;
205 }