Use `Control_Type' to handle different segment directions.
[ttfautohint.git] / lib / tacontrol.flex
bloba60f3bd568f3f3b0660eafc3efaab1717f9abede
1 /* tacontrol.flex */
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 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 + 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 void
86 TA_control_scanner_fatal_error(const char* msg,
87                                yyscan_t yyscanner);
94 (?x: [ \t\f]+
95 ) {
96   /* skip whitespace */
99 (?x: ( "\n"
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: ( "\\\n"
119      )+
120 ) {
121   /* skip escaped newline */
124 (?x: "#" [^\n]*
125 ) {
126   /* skip line comment */
130 (?x: [0+-]
131 ) {
132   /* values `0' and `-' need special treatment for number ranges */
133   return yytext[0];
136 (?x:   [1-9] [0-9]*
137      | "0" [xX] [0-9a-fA-F]+
138      | "0" [0-7]+
139 ) {
140   /* we don't support localized formats like a thousands separator */
141   errno = 0;
142   yylval->integer = strtol(yytext, NULL, 0);
143   if (errno == ERANGE)
144   {
145     /* overflow or underflow */
146     yyextra->error = TA_Err_Control_Overflow;
147   }
148   return INTEGER;
151 (?x:   [0-9]* "." [0-9]+
152      | [0-9]+ "." [0-9]*
153 ) {
154   /* we don't support exponents, */
155   /* and we don't support localized formats like using `,' instead of `.' */
156   errno = 0;
157   yylval->real = strtod(yytext, NULL);
158   if (yylval->real && errno == ERANGE)
159   {
160     /* overflow */
161     yyextra->error = TA_Err_Control_Overflow;
162   }
163   return REAL;
167 (?x: [lnprxy@,]
168 ) {
169   /* delimiters */
170   return yytext[0];
173 (?x: [A-Za-z._] [A-Za-z0-9._]*
174 ) {
175   yylval->name = strdup(yytext);
176   if (!yylval->name)
177   {
178     /* allocation error */
179     yyextra->error = TA_Err_Control_Allocation_Error;
180   }
181   return NAME;
185 (?x: .
186 ) {
187   /* invalid input */
188   yylval->character = yytext[0];
189   return INVALID_CHARACTER;
196 /* the following two routines set `context->error' */
198 void*
199 yyalloc(yy_size_t size,
200         yyscan_t yyscanner)
202   YY_EXTRA_TYPE context;
205   void* p = malloc(size);
206   if (!p && yyscanner)
207   {
208     context = yyget_extra(yyscanner);
209     context->error = TA_Err_Control_Allocation_Error;
210   }
211   return p;
215 void*
216 yyrealloc(void* ptr,
217           yy_size_t size,
218           yyscan_t yyscanner)
220   YY_EXTRA_TYPE context;
223   void* p = realloc(ptr, size);
224   if (!p && yyscanner)
225   {
226     context = yyget_extra(yyscanner);
227     context->error = TA_Err_Control_Allocation_Error;
228   }
229   return p;
233 /* we reimplement this routine also to avoid a compiler warning, */
234 /* cf. https://sourceforge.net/p/flex/bugs/115 */
235 void
236 yyfree(void* ptr,
237        yyscan_t yyscanner)
239   (void)yyscanner;
240   free(ptr);
244 void
245 TA_control_scanner_fatal_error(const char* msg,
246                                yyscan_t yyscanner)
248   YY_EXTRA_TYPE context = yyget_extra(yyscanner);
251   /* allocation routines set a different error value */
252   if (!context->error)
253     context->error = TA_Err_Control_Flex_Error;
254   strncpy(context->errmsg, msg, sizeof (context->errmsg));
256   longjmp(context->jump_buffer, 1);
258   /* the next line, which we never reach, suppresses a warning about */
259   /* `yy_fatal_error' defined but not used */
260   yy_fatal_error(msg, yyscanner);
264 void
265 TA_control_scanner_init(Control_Context* context,
266                         FONT* font)
268   int flex_error;
270   yyscan_t scanner;
271   YY_BUFFER_STATE b;
274   /* this function sets `errno' in case of error */
275   flex_error = yylex_init(&scanner);
276   if (flex_error && errno == ENOMEM)
277   {
278     context->error = FT_Err_Out_Of_Memory;
279     context->errmsg[0] = '\0';
280     return;
281   }
283   /* initialize some context fields */
284   context->font = font;
285   context->error = TA_Err_Ok;
286   context->result = NULL;
287   context->scanner = scanner;
288   context->eof = 0;
290   yyset_extra(context, scanner);
292   /* by default, `yy_scan_bytes' simply calls `exit' */
293   /* (via YY_FATAL_ERROR) in case of a (more or less) fatal error -- */
294   /* this is bad for a library, thus we use `longjmp' to catch this, */
295   /* together with a proper handler for YY_FATAL_ERROR */
296   if (setjmp(context->jump_buffer) != 0)
297   {
298     /* error and error message in `context' already stored by */
299     /* `TA_control_scanner_fatal_error' */
300     return;
301   }
303   b = yy_scan_bytes(font->control_buf, font->control_len, scanner);
305   /* flex bug: these two fields are not initialized, */
306   /*           causing zillions of valgrind errors; see */
307   /*           https://sourceforge.net/p/flex/bugs/180 */
308   b->yy_bs_lineno = 0;
309   b->yy_bs_column = 0;
313 void
314 TA_control_scanner_done(Control_Context* context)
316   yylex_destroy(context->scanner);
320 #if 0
322 const char* input =
323   "# Test\n"
324   "\n"
325   "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
326   "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
327   "0 a p 2 x 0.5 @ 23-25";
331 main(void)
333   TA_Error error;
334   int retval = 1;
336   FONT font;
337   Control_Context context;
339   YYSTYPE yylval_param;
340   YYLTYPE yylloc_param;
343   /* we only need the control instructions buffer */
344   font.control_buf = (char*)input;
345   font.control_len = strlen(input);
347   TA_control_scanner_init(&context, &font);
348   if (context.error)
349     goto Exit;
351   yyset_debug(1, context.scanner);
352   while (yylex(&yylval_param, &yylloc_param, context.scanner))
353   {
354     if (context.error)
355       goto Exit;
356   }
358   retval = 0;
360 Exit:
361   TA_control_scanner_done(&context);
363   return retval;
366 #endif
368 /* end of tacontrol.flex */