]> git.draconx.ca Git - cdecl99.git/blob - src/scan.l
Release 1.3.
[cdecl99.git] / src / scan.l
1 %top{
2 /*
3  *  Scanner for C declarations.
4  *  Copyright © 2011, 2021, 2023-2024 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 <stdio.h>
22 #include "parse.h"
23
24 /* Disable various generated code we don't use */
25 #define YY_INPUT(a, b, c) do {} while (0)
26 #define YY_NO_INPUT 1
27 #define YY_NO_UNPUT 1
28
29 /*
30  * The flex-generated scanner defines a huge pile of external functions of
31  * which we use almost none elsewhere.  Explicitly declare any unneeded
32  * functions static, which allows better optimization (especially wrt.
33  * dead code elimination).
34  */
35 #if !cdecl__yyIN_HEADER
36
37 #if __GNUC__
38 #  define static __attribute__((__unused__)) static
39 #endif
40
41 static struct yy_buffer_state *cdecl__yy_create_buffer(FILE *, int, void *);
42 static struct yy_buffer_state *cdecl__yy_scan_bytes(const char *, int, void *);
43 static struct yy_buffer_state *cdecl__yy_scan_buffer(char *, size_t, void *);
44 static void cdecl__yy_switch_to_buffer(struct yy_buffer_state *, void *);
45 static void cdecl__yy_flush_buffer(struct yy_buffer_state *, void *);
46 static void cdecl__yypush_buffer_state(struct yy_buffer_state *, void *);
47 static void cdecl__yypop_buffer_state(void *);
48 static void cdecl__yyrestart(FILE *, void *);
49 static int cdecl__yylex_init(void **);
50
51 static int cdecl__yyget_extra(void *);
52 static YYLTYPE *cdecl__yyget_lloc(void *);
53 static YYSTYPE *cdecl__yyget_lval(void *);
54 static char *cdecl__yyget_text(void *);
55 static FILE *cdecl__yyget_out(void *);
56 static FILE *cdecl__yyget_in(void *);
57 static int cdecl__yyget_debug(void *);
58 static int cdecl__yyget_lineno(void *);
59 static int cdecl__yyget_column(void *);
60 static int cdecl__yyget_leng(void *);
61
62 static void cdecl__yyset_extra(int, void *);
63 static void cdecl__yyset_lloc(YYLTYPE *, void *);
64 static void cdecl__yyset_lval(YYSTYPE *, void *);
65 static void cdecl__yyset_in(FILE *, void *);
66 static void cdecl__yyset_out(FILE *, void *);
67 static void cdecl__yyset_debug(int, void *);
68 static void cdecl__yyset_lineno(int, void *);
69 static void cdecl__yyset_column(int, void *);
70
71 static void *cdecl__yyrealloc(void *, size_t, void *);
72 static void *cdecl__yyalloc(size_t, void *);
73 static void cdecl__yyfree(void *, void *);
74
75 #undef static
76
77 #endif
78 }
79
80 %option nodefault noyywrap bison-locations reentrant never-interactive
81 %option extra-type="int"
82 %option prefix="cdecl__yy"
83
84 %{
85 #include <ctype.h>
86 #include "cdecl-internal.h"
87 #include "cdecl.h"
88 #include "errmsg.h"
89 #include "intconv.h"
90
91 static char *to_octal(char *dst, unsigned val)
92 {
93         unsigned i;
94
95         for (i = 0; i < 3; i++) {
96                 *dst++ = '0' + ((val >> 6) & 7u);
97                 val <<= 3;
98         }
99
100         return dst;
101 }
102
103 /*
104  * Convert a single character to a C-style character constant, including quote
105  * characters.  At most 7 bytes are written to the buffer for the longest
106  * octal encoding, e.g., '\177'
107  */
108 static void to_readable_ch(char *dst, char c)
109 {
110         unsigned char uc = c;
111         unsigned i;
112         char esc;
113
114         /*
115          * The 7 standard C control characters are contiguous in ASCII,
116          * permitting a simple and compact lookup table; separating their
117          * handling from backslash and quote characters hopefully allows
118          * the compiler to recognize that.
119          */
120         switch (c) {
121         case '\a': i = 0; break;
122         case '\b': i = 1; break;
123         case '\t': i = 2; break;
124         case '\n': i = 3; break;
125         case '\v': i = 4; break;
126         case '\f': i = 5; break;
127         case '\r': i = 6; break;
128         default:   i = 7; break;
129         }
130         esc = "abtnvfr"[i];
131
132         /* Otherwise printable characters that should still be escaped. */
133         switch (c) {
134         case '\\': case '\'': esc = c; break;
135         }
136
137         *dst++ = '\'';
138         if (esc) {
139                 *dst++ = '\\';
140                 *dst++ = esc;
141         } else if (isprint(uc)) {
142                 *dst++ = c;
143         } else {
144                 *dst++ = '\\';
145                 dst = to_octal(dst, uc);
146         }
147         *dst++ = '\'';
148         *dst++ = 0;
149 }
150
151 %}
152
153 IDENT [_[:alpha:]][-_[:alnum:]]*
154
155 %%
156
157 %{
158         int intconv_base;
159         char *c;
160 %}
161
162 "..."|[][;*(),] {
163         unsigned char *match;
164         static const unsigned char tab[2][8] = {
165                 "*[](),.;",
166                 {
167                         PACK_TOKEN(T_ASTERISK),
168                         PACK_TOKEN(T_LBRACKET),
169                         PACK_TOKEN(T_RBRACKET),
170                         PACK_TOKEN(T_LPAREN),
171                         PACK_TOKEN(T_RPAREN),
172                         PACK_TOKEN(T_COMMA),
173                         PACK_TOKEN(T_ELLIPSIS),
174                         PACK_TOKEN(T_SEMICOLON)
175                 }
176         };
177
178         match = memchr(&tab, yytext[0], sizeof tab[0]);
179         return UNPACK_TOKEN(match[sizeof tab[0]]);
180 }
181
182 0[0-7]* { intconv_base = INTCONV_OCTAL; goto int_parse; }
183 [1-9][0-9]* { intconv_base = INTCONV_DECIMAL; goto int_parse; }
184 0[Xx][[:xdigit:]]+ {
185         unsigned char d;
186         cdecl_uintmax v;
187
188         yytext += 2;
189         intconv_base = INTCONV_HEXADECIMAL;
190 int_parse:
191         for (v = 0; (d = *yytext++);) {
192                 if (!intconv_shift(&v, intconv_base, intconv_digit(d))) {
193                         cdecl__errmsg(CDECL__ERANGE);
194                         return T_LEX_ERROR;
195                 }
196         }
197
198         yylval->uintval = v;
199         return T_UINT;
200 }
201 0[Xx]|[0-9]+ {
202         cdecl__errmsg(CDECL__EBADINT);
203         return T_LEX_ERROR;
204 }
205
206 {IDENT} {
207         int len = yyleng, tok;
208         unsigned x;
209
210         x = cdecl__to_keyword(yytext, len, yyextra);
211         yylval->spectype = UNPACK_SPEC(x & 0xff);
212         if ((tok = (x >> 8)) == PACK_TOKEN(T_IDENT)) {
213                 /*
214                  * Our IDENT pattern includes hyphens so we can match
215                  * "variable-length" as a keyword.  In all other cases a
216                  * hyphen is an error.
217                  *
218                  * We could use yyless to re-scan the hyphen and hit the
219                  * error catch-all, but jumping straight to the error code
220                  * seems to produce better results with gcc with no obvious
221                  * downsides.
222                  */
223 #if 1
224                 if ((c = memchr(yytext, '-', len)))
225                         goto invalid_char;
226 #else
227                 yyless(strcspn(yytext, "-"));
228 #endif
229                 if (!(yylval->item = cdecl__alloc_item(len+1)))
230                         return T_LEX_ERROR;
231                 memcpy(yylval->item->s, yytext, len+1);
232         }
233         return UNPACK_TOKEN(tok);
234 }
235
236 [[:space:]]+
237 . {
238         char buf[8];
239
240         c = yytext;
241 invalid_char:
242         to_readable_ch(buf, *c);
243         cdecl__err(_("syntax error, unexpected %s"), buf);
244         return T_LEX_ERROR;
245 }