RIP, Vernon...
[ttfautohint.git] / lib / tacontrol.flex
blob5528937ba39d40c1737c258b1a126d253f3770d4
1 /* tacontrol.flex */
3 /*
4  * Copyright (C) 2014-2016 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 ttfautohint control instructions
17  *
18  * Lexing errors are indicated by setting `context->error' to an appropriate
19  * value; fatal lexer errors return token `INTERNAL_FLEX_ERROR'.
20  */
22 /* you should use flex version >= 2.5.39 to avoid various buglets */
23 /* that don't have work-arounds */
25 %option outfile="tacontrol-flex.c"
26 %option header-file="tacontrol-flex.h"
28 %option batch
29 %option bison-bridge
30 %option bison-locations
31 %option never-interactive
32 %option noinput
33 %option nounput
34 %option noyyalloc
35 %option noyyfree
36 %option noyyrealloc
37 %option noyywrap
38 %option reentrant
39 %option yylineno
43 #include <stdio.h>
44 #include <string.h>
46 #include "ta.h"
47 #include "tacontrol-bison.h"
49 /* option `yylineno' resets `yycolumn' to 0 after a newline */
50 /* (since version 2.5.30, March 2003) before the next token gets read; */
51 /* note we want both line number and column start with value 1 */
53 #define YY_USER_ACTION \
54   yylloc->first_line = yylineno + 1; \
55   yylloc->last_line = yylineno + 1; \
56   yylloc->first_column = yycolumn; \
57   yylloc->last_column = yycolumn + (int)yyleng - 1; \
58   yycolumn += yyleng;
60 #define YY_EXTRA_TYPE Control_Context*
62 #define YY_FATAL_ERROR(msg) TA_control_scanner_fatal_error(msg, yyscanner)
64 /* by default, `yylex' simply calls `exit' (via YY_FATAL_ERROR) */
65 /* in case of a (more or less) fatal error -- */
66 /* this is bad for a library, thus we use `longjmp' to catch this, */
67 /* together with a proper handler for YY_FATAL_ERROR */
69 #define YY_USER_INIT \
70           do \
71           { \
72             if (setjmp(yyextra->jump_buffer) != 0) \
73             { \
74               /* error and error message in `context' already stored by */ \
75               /* `TA_control_scanner_fatal_error' */ \
76               return INTERNAL_FLEX_ERROR; \
77             } \
78           } while (0)
80 /* this macro works around flex bug to suppress a compilation warning, */
81 /* cf. https://sourceforge.net/p/flex/bugs/115 */
83 #define YY_EXIT_FAILURE ((void)yyscanner, 2)
85 #define NAME_ASSIGN \
86           do \
87           { \
88             yylval->name = strdup(yytext); \
89             if (!yylval->name) \
90               yyextra->error = TA_Err_Control_Allocation_Error; \
91           } while (0)
93 void
94 TA_control_scanner_fatal_error(const char* msg,
95                                yyscan_t yyscanner);
102 (?x: [ \t\f]+
103 ) {
104   /* skip whitespace */
107 (?x: ( "\n"
108      | ";"
109      )+
110 ) {
111   /* end of entry */
112   return EOE;
115 <<EOF>> {
116   /* we want an EOE token at end of file, too */
117   if (!yyextra->eof)
118   {
119     yyextra->eof = 1;
120     return EOE;
121   }
122   else
123     yyterminate();
126 (?x: ( "\\\n"
127      )+
128 ) {
129   /* skip escaped newline */
132 (?x: "#" [^\n]*
133 ) {
134   /* skip line comment */
138 (?x: [0+-]
139 ) {
140   /* values `0' and `-' need special treatment for number ranges */
141   return yytext[0];
144 (?x:   [1-9] [0-9]*
145      | "0" [xX] [0-9a-fA-F]+
146      | "0" [0-7]+
147 ) {
148   /* we don't support localized formats like a thousands separator */
149   errno = 0;
150   yylval->integer = strtol(yytext, NULL, 0);
151   if (errno == ERANGE)
152   {
153     /* overflow or underflow */
154     yyextra->error = TA_Err_Control_Overflow;
155   }
156   return INTEGER;
159 (?x:   [0-9]* "." [0-9]+
160      | [0-9]+ "." [0-9]*
161 ) {
162   /* we don't support exponents, */
163   /* and we don't support localized formats like using `,' instead of `.' */
164   errno = 0;
165   yylval->real = strtod(yytext, NULL);
166   if (yylval->real && errno == ERANGE)
167   {
168     /* overflow */
169     yyextra->error = TA_Err_Control_Overflow;
170   }
171   return REAL;
175 (?x: [@,()]
176 ) {
177   /* delimiters */
178   return yytext[0];
181 (?x:   "point"
182      | "p"
183 ) {
184   NAME_ASSIGN;
185   return POINT;
188 (?x:   "touch"
189      | "t"
190 ) {
191   NAME_ASSIGN;
192   return TOUCH;
195 (?x:   "xshift"
196      | "x"
197 ) {
198   NAME_ASSIGN;
199   return XSHIFT;
202 (?x:   "yshift"
203      | "y"
204 ) {
205   NAME_ASSIGN;
206   return YSHIFT;
209 (?x:   "left"
210      | "l"
211 ) {
212   NAME_ASSIGN;
213   return LEFT;
216 (?x:   "right"
217      | "r"
218 ) {
219   NAME_ASSIGN;
220   return RIGHT;
223 (?x:   "nodir"
224      | "n"
225 ) {
226   NAME_ASSIGN;
227   return NODIR;
230 (?x: [A-Za-z._] [A-Za-z0-9._]*
231 ) {
232   NAME_ASSIGN;
233   return NAME;
237 (?x: .
238 ) {
239   /* invalid input */
240   yylval->character = yytext[0];
241   return INVALID_CHARACTER;
248 /* the following two routines set `context->error' */
250 void*
251 yyalloc(yy_size_t size,
252         yyscan_t yyscanner)
254   YY_EXTRA_TYPE context;
257   void* p = malloc(size);
258   if (!p && yyscanner)
259   {
260     context = yyget_extra(yyscanner);
261     context->error = TA_Err_Control_Allocation_Error;
262   }
263   return p;
267 void*
268 yyrealloc(void* ptr,
269           yy_size_t size,
270           yyscan_t yyscanner)
272   YY_EXTRA_TYPE context;
275   void* p = realloc(ptr, size);
276   if (!p && yyscanner)
277   {
278     context = yyget_extra(yyscanner);
279     context->error = TA_Err_Control_Allocation_Error;
280   }
281   return p;
285 /* we reimplement this routine also to avoid a compiler warning, */
286 /* cf. https://sourceforge.net/p/flex/bugs/115 */
287 void
288 yyfree(void* ptr,
289        yyscan_t yyscanner)
291   (void)yyscanner;
292   free(ptr);
296 void
297 TA_control_scanner_fatal_error(const char* msg,
298                                yyscan_t yyscanner)
300   YY_EXTRA_TYPE context = yyget_extra(yyscanner);
303   /* allocation routines set a different error value */
304   if (!context->error)
305     context->error = TA_Err_Control_Flex_Error;
306   strncpy(context->errmsg, msg, sizeof (context->errmsg));
308   longjmp(context->jump_buffer, 1);
310   /* the next line, which we never reach, suppresses a warning about */
311   /* `yy_fatal_error' defined but not used */
312   yy_fatal_error(msg, yyscanner);
316 void
317 TA_control_scanner_init(Control_Context* context,
318                         FONT* font)
320   int flex_error;
322   yyscan_t scanner;
323   YY_BUFFER_STATE b;
326   /* this function sets `errno' in case of error */
327   flex_error = yylex_init(&scanner);
328   if (flex_error && errno == ENOMEM)
329   {
330     context->error = FT_Err_Out_Of_Memory;
331     context->errmsg[0] = '\0';
332     return;
333   }
335   /* initialize some context fields */
336   context->font = font;
337   context->error = TA_Err_Ok;
338   context->result = NULL;
339   context->scanner = scanner;
340   context->eof = 0;
342   yyset_extra(context, scanner);
344   /* by default, `yy_scan_bytes' simply calls `exit' */
345   /* (via YY_FATAL_ERROR) in case of a (more or less) fatal error -- */
346   /* this is bad for a library, thus we use `longjmp' to catch this, */
347   /* together with a proper handler for YY_FATAL_ERROR */
348   if (setjmp(context->jump_buffer) != 0)
349   {
350     /* error and error message in `context' already stored by */
351     /* `TA_control_scanner_fatal_error' */
352     return;
353   }
355   b = yy_scan_bytes(font->control_buf, font->control_len, scanner);
357   /* flex bug: these two fields are not initialized, */
358   /*           causing zillions of valgrind errors; see */
359   /*           https://sourceforge.net/p/flex/bugs/180 */
360   b->yy_bs_lineno = 0;
361   b->yy_bs_column = 0;
365 void
366 TA_control_scanner_done(Control_Context* context)
368   yylex_destroy(context->scanner);
372 #if 0
374 const char* input =
375   "# Test\n"
376   "\n"
377   "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
378   "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
379   "0 a p 2 x 0.5 @ 23-25";
383 main(void)
385   TA_Error error;
386   int retval = 1;
388   FONT font;
389   Control_Context context;
391   YYSTYPE yylval_param;
392   YYLTYPE yylloc_param;
395   /* we only need the control instructions buffer */
396   font.control_buf = (char*)input;
397   font.control_len = strlen(input);
399   TA_control_scanner_init(&context, &font);
400   if (context.error)
401     goto Exit;
403   yyset_debug(1, context.scanner);
404   while (yylex(&yylval_param, &yylloc_param, context.scanner))
405   {
406     if (context.error)
407       goto Exit;
408   }
410   retval = 0;
412 Exit:
413   TA_control_scanner_done(&context);
415   return retval;
418 #endif
420 /* end of tacontrol.flex */