Next round of lexer and parser refinements.
[ttfautohint.git] / lib / tadeltas.l
blobcff061fc035eeef9f4021f00c3026433c0a2b8ee
1 /* tadeltas.l */
3 /*
4  * Copyright (C) 2014 by Werner Lemberg.
5  *
6  * This file is part of the ttfautohint library, and may only be used,
7  * modified, and distributed under the terms given in `COPYING'.  By
8  * continuing to use, modify, or distribute this file you indicate that you
9  * have read `COPYING' and understand and accept it fully.
10  *
11  * The file `COPYING' mentioned in the previous paragraph is distributed
12  * with the ttfautohint library.
13  */
16  * lexical analyzer for parsing delta exceptions
17  *
18  * Lexing errors are indicated with tokens like `BAD_INTEGER'; fatal lexer
19  * errors return token `INTERNAL_FLEX_ERROR' and set `context->error' to an
20  * appropriate value.
21  */
23 %option outfile="tadeltas-flex.c"
24 %option header-file="tadeltas-flex.h"
26 %option batch
27 %option bison-bridge
28 %option bison-locations
29 %option never-interactive
30 %option noinput
31 %option nounput
32 %option noyyalloc
33 %option noyyfree
34 %option noyyrealloc
35 %option noyywrap
36 %option reentrant
37 %option yylineno
41 #include <stdio.h>
42 #include <string.h>
44 #include "ta.h"
45 #include "tadeltas-bison.h"
47 /* option `yylineno' resets `yycolumn' to 0 after a newline */
48 /* (since version 2.5.30, March 2003) before the next token gets read; */
49 /* note we want both line number and column start with value 1 */
51 #define YY_USER_ACTION \
52   yylloc->first_line = yylineno + 1; \
53   yylloc->last_line = yylineno + 1; \
54   yylloc->first_column = yycolumn; \
55   yylloc->last_column = yycolumn + yyleng - 1; \
56   yycolumn += yyleng;
58 #define YY_EXTRA_TYPE Deltas_Context*
60 #define YY_FATAL_ERROR(msg) TA_deltas_scanner_fatal_error(msg, yyscanner)
62 /* by default, `yylex' simply calls `exit' (via YY_FATAL_ERROR) */
63 /* in case of a (more or less) fatal error -- */
64 /* this is bad for a library, thus we use `longjmp' to catch this, */
65 /* together with a proper handler for YY_FATAL_ERROR */
67 #define YY_USER_INIT \
68           do \
69           { \
70             if (setjmp(yyextra->jump_buffer) != 0) \
71             { \
72               /* error and error message in `context' already stored by */ \
73               /* `TA_deltas_scanner_fatal_error' */ \
74               return INTERNAL_FLEX_ERROR; \
75             } \
76           } while (0)
78 /* this macro works around flex bug to suppress a compilation warning, */
79 /* cf. https://sourceforge.net/p/flex/bugs/115 */
81 #define YY_EXIT_FAILURE ((void)yyscanner, 2)
83 void
84 TA_deltas_scanner_fatal_error(const char* msg,
85                               yyscan_t yyscanner);
92 (?x: [ \t\f]+
93 ) {
94   /* skip whitespace */
97 (?x: ( "\r\n"
98      | "\n"
99      | "\r"
100      | ";"
101      )+
102 ) {
103   /* end of entry */
104   return EOE;
107 <<EOF>> {
108   /* we want an EOE token at end of file, too */
109   if (!yyextra->eof)
110   {
111     yyextra->eof = 1;
112     return EOE;
113   }
114   else
115     yyterminate();
118 (?x: ( "\\\r\n"
119      | "\\\n"
120      | "\\\r"
121      )+
122 ) {
123   /* skip escaped newline */
126 (?x: "#" [^\n]*
127 ) {
128   /* skip line comment */
132 (?x: [0+-]
133 ) {
134   /* values `0' and `-' need special treatment for number ranges */
135   return yytext[0];
138 (?x:   [1-9] [0-9]*
139      | "0" [xX] [0-9a-fA-F]+
140      | "0" [0-7]+
141 ) {
142   /* we don't support localized formats like a thousands separator */
143   errno = 0;
144   yylval->integer = strtol(yytext, NULL, 0);
145   if (errno == ERANGE)
146   {
147     /* overflow or underflow */
148     return BAD_INTEGER;
149   }
150   return INTEGER;
153 (?x:   [0-9]* "." [0-9]+
154      | [0-9]+ "." [0-9]*
155 ) {
156   /* we don't support exponents, */
157   /* and we don't support localized formats like using `,' instead of `.' */
158   errno = 0;
159   yylval->real = strtod(yytext, NULL);
160   if (yylval->real && errno == ERANGE)
161   {
162     /* overflow */
163     return BAD_REAL;
164   }
165   return REAL;
169 (?x: [pxy@,]
170 ) {
171   /* delimiters */
172   return yytext[0];
175 (?x: [A-Za-z._] [A-Za-z0-9._]*
176 ) {
177   yylval->name = strdup(yytext);
178   if (!yylval->name)
179   {
180     /* allocation error */
181     return BAD_NAME;
182   }
183   return NAME;
187 (?x: .
188 ) {
189   /* invalid input */
190   return INVALID_CHARACTER;
197 /* the following two routines set `context->error' */
199 void*
200 yyalloc(yy_size_t size,
201         yyscan_t yyscanner)
203   YY_EXTRA_TYPE context;
206   void* p = malloc(size);
207   if (!p && yyscanner)
208   {
209     context = yyget_extra(yyscanner);
210     context->error = TA_Err_Deltas_Allocation_Error;
211   }
212   return p;
216 void*
217 yyrealloc(void* ptr,
218           yy_size_t size,
219           yyscan_t yyscanner)
221   YY_EXTRA_TYPE context;
224   void* p = realloc(ptr, size);
225   if (!p && yyscanner)
226   {
227     context = yyget_extra(yyscanner);
228     context->error = TA_Err_Deltas_Allocation_Error;
229   }
230   return p;
234 /* we reimplement this routine also to avoid a compiler warning, */
235 /* cf. https://sourceforge.net/p/flex/bugs/115 */
236 void
237 yyfree(void* ptr,
238        yyscan_t yyscanner)
240   (void)yyscanner;
241   free(ptr);
245 void
246 TA_deltas_scanner_fatal_error(const char* msg,
247                               yyscan_t yyscanner)
249   YY_EXTRA_TYPE context = yyget_extra(yyscanner);
252   /* allocation routines set a different error value */
253   if (!context->error)
254     context->error = TA_Err_Deltas_Flex_Error;
255   strncpy(context->errmsg, msg, sizeof (context->errmsg));
257   longjmp(context->jump_buffer, 1);
259   /* the next line, which we never reach, suppresses a warning about */
260   /* `yy_fatal_error' defined but not used */
261   yy_fatal_error(msg, yyscanner);
265 void
266 TA_deltas_scanner_init(Deltas_Context* context,
267                        FONT* font)
269   int flex_error;
271   yyscan_t scanner;
272   YY_BUFFER_STATE b;
275   /* this function sets `errno' in case of error */
276   flex_error = yylex_init(&scanner);
277   if (flex_error && errno == ENOMEM)
278   {
279     context->error = FT_Err_Out_Of_Memory;
280     context->errmsg[0] = '\0';
281     return;
282   }
284   /* initialize some context fields */
285   context->font = font;
286   context->error = TA_Err_Ok;
287   context->result = NULL;
288   context->scanner = scanner;
289   context->eof = 0;
291   yyset_extra(context, scanner);
293   /* by default, `yy_scan_bytes' simply calls `exit' */
294   /* (via YY_FATAL_ERROR) in case of a (more or less) fatal error -- */
295   /* this is bad for a library, thus we use `longjmp' to catch this, */
296   /* together with a proper handler for YY_FATAL_ERROR */
297   if (setjmp(context->jump_buffer) != 0)
298   {
299     /* error and error message in `context' already stored by */
300     /* `TA_deltas_scanner_fatal_error' */
301     return;
302   }
304   b = yy_scan_bytes(font->deltas_buf, font->deltas_len, scanner);
306   /* flex bug: these two fields are not initialized, */
307   /*           causing zillions of valgrind errors; see */
308   /*           https://sourceforge.net/p/flex/bugs/180 */
309   b->yy_bs_lineno = 0;
310   b->yy_bs_column = 0;
314 void
315 TA_deltas_scanner_done(Deltas_Context* context)
317   yylex_destroy(context->scanner);
321 #if 0
323 const char* input =
324   "# Test\n"
325   "\n"
326   "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
327   "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
328   "0 a p 2 x 0.5 @ 23-25";
332 main(void)
334   TA_Error error;
335   int retval = 1;
337   FONT font;
338   Deltas_Context context;
340   YYSTYPE yylval_param;
341   YYLTYPE yylloc_param;
344   /* we only need the delta exceptions buffer */
345   font.deltas_buf = (char*)input;
346   font.deltas_len = strlen(input);
348   TA_deltas_scanner_init(&context, &font);
349   if (context.error)
350     goto Exit;
352   yyset_debug(1, context.scanner);
353   while (yylex(&yylval_param, &yylloc_param, context.scanner))
354   {
355     if (context.error)
356       goto Exit;
357   }
359   retval = 0;
361 Exit:
362   TA_deltas_scanner_done(&context);
364   return retval;
367 #endif
369 /* end of tadeltas.l */