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