Add file missing in previous commit.
[ttfautohint.git] / lib / tacontrol.flex
bloba9039c23f47d0d29b12a1a55f7169731a2ff842f
1 /* tacontrol.flex */
3 /*
4  * Copyright (C) 2014-2017 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
42 %top {
43 #include "config.h"
49 #include <stdio.h>
50 #include <string.h>
52 #include "ta.h"
53 #include "tacontrol-bison.h"
55 /* option `yylineno' resets `yycolumn' to 0 after a newline */
56 /* (since version 2.5.30, March 2003) before the next token gets read; */
57 /* note we want both line number and column start with value 1 */
59 #define YY_USER_ACTION \
60           yylloc->first_line = yylineno + 1; \
61           yylloc->last_line = yylineno + 1; \
62           yylloc->first_column = yycolumn; \
63           yylloc->last_column = yycolumn + (int)yyleng - 1; \
64           yycolumn += yyleng;
66 #define YY_EXTRA_TYPE Control_Context*
68 #define YY_FATAL_ERROR(msg) TA_control_scanner_fatal_error(msg, yyscanner)
70 /* by default, `yylex' simply calls `exit' (via YY_FATAL_ERROR) */
71 /* in case of a (more or less) fatal error -- */
72 /* this is bad for a library, thus we use `longjmp' to catch this, */
73 /* together with a proper handler for YY_FATAL_ERROR */
75 #define YY_USER_INIT \
76           do \
77           { \
78             if (setjmp(yyextra->jump_buffer) != 0) \
79             { \
80               /* error and error message in `context' already stored by */ \
81               /* `TA_control_scanner_fatal_error' */ \
82               return INTERNAL_FLEX_ERROR; \
83             } \
84           } while (0)
86 /* this macro works around flex bug to suppress a compilation warning, */
87 /* cf. https://sourceforge.net/p/flex/bugs/115 */
89 #define YY_EXIT_FAILURE ((void)yyscanner, 2)
91 #define NAME_ASSIGN \
92           do \
93           { \
94             yylval->name = strdup(yytext); \
95             if (!yylval->name) \
96               yyextra->error = TA_Err_Control_Allocation_Error; \
97           } while (0)
99 void
100 TA_control_scanner_fatal_error(const char* msg,
101                                yyscan_t yyscanner);
108 (?x: [ \t\f]+
109 ) {
110   /* skip whitespace */
113 (?x: ( "\n"
114      | ";"
115      )+
116 ) {
117   /* end of entry */
118   return EOE;
121 <<EOF>> {
122   /* we want an EOE token at end of file, too */
123   if (!yyextra->eof)
124   {
125     yyextra->eof = 1;
126     return EOE;
127   }
128   else
129     yyterminate();
132 (?x: ( "\\\n"
133      )+
134 ) {
135   /* skip escaped newline */
138 (?x: "#" [^\n]*
139 ) {
140   /* skip line comment */
144 (?x: [0+-]
145 ) {
146   /* values `0' and `-' need special treatment for number ranges */
147   return yytext[0];
150 (?x:   [1-9] [0-9]*
151      | "0" [xX] [0-9a-fA-F]+
152      | "0" [0-7]+
153 ) {
154   /* we don't support localized formats like a thousands separator */
155   errno = 0;
156   yylval->integer = strtol(yytext, NULL, 0);
157   if (errno == ERANGE)
158   {
159     /* overflow or underflow */
160     yyextra->error = TA_Err_Control_Overflow;
161   }
162   return INTEGER;
165 (?x:   [0-9]* "." [0-9]+
166      | [0-9]+ "." [0-9]*
167 ) {
168   /* we don't support exponents, */
169   /* and we don't support localized formats like using `,' instead of `.' */
170   errno = 0;
171   yylval->real = strtod(yytext, NULL);
172   if (yylval->real != 0 && errno == ERANGE)
173   {
174     /* overflow */
175     yyextra->error = TA_Err_Control_Overflow;
176   }
177   return REAL;
181 (?x: [@,()*]
182 ) {
183   /* delimiters, wildcard */
184   return yytext[0];
187 (?x:   "point"
188      | "p"
189 ) {
190   NAME_ASSIGN;
191   return POINT;
194 (?x:   "touch"
195      | "t"
196 ) {
197   NAME_ASSIGN;
198   return TOUCH;
201 (?x:   "xshift"
202      | "x"
203 ) {
204   NAME_ASSIGN;
205   return XSHIFT;
208 (?x:   "yshift"
209      | "y"
210 ) {
211   NAME_ASSIGN;
212   return YSHIFT;
215 (?x:   "left"
216      | "l"
217 ) {
218   NAME_ASSIGN;
219   return LEFT;
222 (?x:   "right"
223      | "r"
224 ) {
225   NAME_ASSIGN;
226   return RIGHT;
229 (?x:   "nodir"
230      | "n"
231 ) {
232   NAME_ASSIGN;
233   return NODIR;
236 (?x:   "width"
237      | "w"
238 ) {
239   NAME_ASSIGN;
240   return WIDTH;
243 (?x: [A-Za-z._] [A-Za-z0-9._]*
244 ) {
245   NAME_ASSIGN;
246   return NAME;
250 (?x: .
251 ) {
252   /* invalid input */
253   yylval->character = yytext[0];
254   return INVALID_CHARACTER;
261 /* the following two routines set `context->error' */
263 void*
264 yyalloc(yy_size_t size,
265         yyscan_t yyscanner)
267   YY_EXTRA_TYPE context;
270   void* p = malloc(size);
271   if (!p && yyscanner)
272   {
273     context = yyget_extra(yyscanner);
274     context->error = TA_Err_Control_Allocation_Error;
275   }
276   return p;
280 void*
281 yyrealloc(void* ptr,
282           yy_size_t size,
283           yyscan_t yyscanner)
285   YY_EXTRA_TYPE context;
288   void* p = realloc(ptr, size);
289   if (!p && yyscanner)
290   {
291     context = yyget_extra(yyscanner);
292     context->error = TA_Err_Control_Allocation_Error;
293   }
294   return p;
298 /* we reimplement this routine also to avoid a compiler warning, */
299 /* cf. https://sourceforge.net/p/flex/bugs/115 */
300 void
301 yyfree(void* ptr,
302        yyscan_t yyscanner)
304   (void)yyscanner;
305   free(ptr);
309 void
310 TA_control_scanner_fatal_error(const char* msg,
311                                yyscan_t yyscanner)
313   YY_EXTRA_TYPE context = yyget_extra(yyscanner);
316   /* allocation routines set a different error value */
317   if (!context->error)
318     context->error = TA_Err_Control_Flex_Error;
319   strncpy(context->errmsg, msg, sizeof (context->errmsg));
321   longjmp(context->jump_buffer, 1);
323   /* the next line, which we never reach, suppresses a warning about */
324   /* `yy_fatal_error' defined but not used */
325   yy_fatal_error(msg, yyscanner);
329 void
330 TA_control_scanner_init(Control_Context* context,
331                         FONT* font)
333   int flex_error;
335   yyscan_t scanner;
336   YY_BUFFER_STATE b;
339   /* this function sets `errno' in case of error */
340   flex_error = yylex_init(&scanner);
341   if (flex_error && errno == ENOMEM)
342   {
343     context->error = FT_Err_Out_Of_Memory;
344     context->errmsg[0] = '\0';
345     return;
346   }
348   /* initialize some context fields */
349   context->font = font;
350   context->error = TA_Err_Ok;
351   context->result = NULL;
352   context->scanner = scanner;
353   context->eof = 0;
355   yyset_extra(context, scanner);
357   /* by default, `yy_scan_bytes' simply calls `exit' */
358   /* (via YY_FATAL_ERROR) in case of a (more or less) fatal error -- */
359   /* this is bad for a library, thus we use `longjmp' to catch this, */
360   /* together with a proper handler for YY_FATAL_ERROR */
361   if (setjmp(context->jump_buffer) != 0)
362   {
363     /* error and error message in `context' already stored by */
364     /* `TA_control_scanner_fatal_error' */
365     return;
366   }
368   b = yy_scan_bytes(font->control_buf, font->control_len, scanner);
370   /* flex bug: these two fields are not initialized, */
371   /*           causing zillions of valgrind errors; see */
372   /*           https://sourceforge.net/p/flex/bugs/180 */
373   b->yy_bs_lineno = 0;
374   b->yy_bs_column = 0;
378 void
379 TA_control_scanner_done(Control_Context* context)
381   yylex_destroy(context->scanner);
385 #if 0
387 const char* input =
388   "# Test\n"
389   "\n"
390   "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
391   "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
392   "0 a p 2 x 0.5 @ 23-25";
396 main(void)
398   TA_Error error;
399   int retval = 1;
401   FONT font;
402   Control_Context context;
404   YYSTYPE yylval_param;
405   YYLTYPE yylloc_param;
408   /* we only need the control instructions buffer */
409   font.control_buf = (char*)input;
410   font.control_len = strlen(input);
412   TA_control_scanner_init(&context, &font);
413   if (context.error)
414     goto Exit;
416   yyset_debug(1, context.scanner);
417   while (yylex(&yylval_param, &yylloc_param, context.scanner))
418   {
419     if (context.error)
420       goto Exit;
421   }
423   retval = 0;
425 Exit:
426   TA_control_scanner_done(&context);
428   return retval;
431 #endif
433 /* end of tacontrol.flex */