Add a flex lexer and bison parser to handle delta exception descriptions.
[ttfautohint.git] / lib / tadeltas.y
blob25c3c479a1523dd4bb4b79f41ec945221cfc050a
1 /* tadeltas.y */
3 /*
4 * Copyright (C) 2014 by Werner Lemberg.
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.
11 * The file `COPYING' mentioned in the previous paragraph is distributed
12 * with the ttfautohint library.
16 /* grammar for parsing delta exceptions */
19 * Edsko de Vries's article `Writing a Reentrant Parser with Flex and Bison'
20 * (http://www.phpcompiler.org/articles/reentrantparser.html)
21 * was extremely helpful in writing this code.
24 %output "tadeltas-bison.c"
25 %defines "tadeltas-bison.h"
27 %define api.pure
28 %error-verbose
29 %expect 2
30 %glr-parser
31 %lex-param { void* scanner }
32 %locations
33 %name-prefix "TA_deltas_"
34 %parse-param { Deltas_Context* context }
35 %require "2.5"
37 %code requires {
38 #include "ta.h"
40 /* we don't change the name prefix of flex functions */
41 #define TA_deltas_lex yylex
44 %union {
45 long integer;
46 char* name;
47 number_range* range;
48 double real;
49 Deltas* deltas;
53 #include "tadeltas-flex.h"
55 void
56 TA_deltas_error(YYLTYPE *locp,
57 Deltas_Context* context,
58 char const* msg);
61 /* calls to `yylex' in the generated bison code use `scanner' directly */
62 #define scanner context->scanner
65 %token BAD /* indicates a flex error */
66 %token EOE
67 %token <integer> INTEGER
68 %token <name> NAME
69 %token <real> REAL
71 %type <deltas> entry
72 %type <integer> font_idx
73 %type <integer> glyph_idx
74 %type <name> glyph_name
75 %type <deltas> input
76 %type <integer> integer
77 %type <range> left_limited
78 %type <range> number_set
79 %type <range> point_set
80 %type <range> ppem_set
81 %type <range> range
82 %type <range> range_elem
83 %type <range> range_elems
84 %type <real> real
85 %type <range> right_limited
86 %type <real> shift
87 %type <range> unlimited
88 %type <real> x_shift
89 %type <real> y_shift
91 %destructor { TA_deltas_free($$); } <deltas>
92 %destructor { number_set_free($$); } <range>
98 /* `number_range' list elements are stored in reversed order; */
99 /* the call to `TA_deltas_new' fixes this */
100 /* (`Deltas' list elements are stored in reversed order, too, */
101 /* but they don't need to be sorted so we don't care */
103 start:
104 input
105 { context->result = $input; }
108 input[result]:
109 /* empty */
110 { $result = NULL; }
111 | input[left] entry
112 { $result = TA_deltas_prepend($left, $entry); }
115 entry:
117 { $entry = NULL; }
118 | font_idx glyph_idx point_set x_shift y_shift ppem_set EOE
120 $entry = TA_deltas_new($font_idx,
121 $glyph_idx,
122 $point_set,
123 $x_shift,
124 $y_shift,
125 $ppem_set);
126 if (!$entry)
128 context->error = TA_Err_Deltas_Allocation_Error;
129 YYABORT;
134 font_idx:
135 /* empty */
137 $font_idx = 0;
138 context->font_idx = $font_idx;
140 | integer
142 $font_idx = $integer;
143 if ($font_idx >= context->font->num_sfnts)
145 context->error = TA_Err_Deltas_Invalid_Font_Index;
146 YYABORT;
148 context->font_idx = $font_idx;
152 glyph_idx:
153 integer
155 FT_Face face = context->font->sfnts[context->font_idx].face;
158 $glyph_idx = $integer;
159 if ($glyph_idx >= face->num_glyphs)
161 context->error = TA_Err_Deltas_Invalid_Glyph_Index;
162 YYABORT;
164 context->glyph_idx = $glyph_idx;
166 | glyph_name
168 FT_Face face = context->font->sfnts[context->font_idx].face;
171 /* explicitly compare with `.notdef' */
172 /* since `FT_Get_Name_Index' returns glyph index 0 */
173 /* for both this glyph name and invalid ones */
174 if (!strcmp($glyph_name, ".notdef"))
175 $glyph_idx = 0;
176 else
178 $glyph_idx = FT_Get_Name_Index(face, $glyph_name);
179 if ($glyph_idx == 0)
180 $glyph_idx = -1;
183 free($glyph_name);
185 if ($glyph_idx < 0)
187 context->error = TA_Err_Deltas_Invalid_Glyph_Name;
188 YYABORT;
190 context->glyph_idx = $glyph_idx;
194 glyph_name:
197 $glyph_name = strdup("p");
198 if ($glyph_name)
200 context->error = TA_Err_Deltas_Allocation_Error;
201 YYABORT;
204 | 'x'
206 $glyph_name = strdup("x");
207 if ($glyph_name)
209 context->error = TA_Err_Deltas_Allocation_Error;
210 YYABORT;
213 | 'y'
215 $glyph_name = strdup("y");
216 if ($glyph_name)
218 context->error = TA_Err_Deltas_Allocation_Error;
219 YYABORT;
222 | NAME
224 /* `$NAME' was allocated in the lexer */
225 $glyph_name = $NAME;
229 point_set:
232 FT_Error error;
233 FT_Face face = context->font->sfnts[context->font_idx].face;
234 int num_points;
237 error = FT_Load_Glyph(face, context->glyph_idx, FT_LOAD_NO_SCALE);
238 if (error)
240 context->error = TA_Err_Deltas_Invalid_Glyph;
241 YYABORT;
244 num_points = face->glyph->outline.n_points;
246 context->number_set_min = 0;
247 context->number_set_max = num_points - 1;
249 number_set
250 { $point_set = $number_set; }
253 x_shift:
254 /* empty */
255 { $x_shift = 0; }
256 | 'x' shift
257 { $x_shift = $shift; }
260 y_shift:
261 /* empty */
262 { $y_shift = 0; }
263 | 'y' shift
264 { $y_shift = $shift; }
267 shift:
268 real
270 if ($real < DELTA_SHIFT_MIN || $real > DELTA_SHIFT_MAX)
272 context->error = TA_Err_Deltas_Invalid_Shift;
273 YYABORT;
275 $shift = $real;
279 ppem_set:
282 context->number_set_min = DELTA_PPEM_MIN;
283 context->number_set_max = DELTA_PPEM_MAX;
285 number_set
286 { $ppem_set = $number_set; }
289 integer:
291 { $integer = 0; }
292 | INTEGER
293 { $integer = $INTEGER; }
296 real:
297 integer
298 { $real = $integer; }
299 | '+' integer
300 { $real = $integer; }
301 | '-' integer
302 { $real = -$integer; }
303 | REAL
304 { $real = $REAL; }
305 | '+' REAL
306 { $real = $REAL; }
307 | '-' REAL
308 { $real = -$REAL; }
311 number_set:
312 unlimited
313 { $number_set = $unlimited; }
314 | right_limited
315 { $number_set = $right_limited; }
316 | left_limited
317 { $number_set = $left_limited; }
318 | range_elems
319 { $number_set = $range_elems; }
320 | right_limited ',' range_elems
322 $number_set = number_set_prepend($right_limited, $range_elems);
323 if ($number_set == NUMBERSET_NOT_ASCENDING)
325 context->error = TA_Err_Deltas_Ranges_Not_Ascending;
326 YYABORT;
328 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
330 context->error = TA_Err_Deltas_Overlapping_Ranges;
331 YYABORT;
334 | range_elems ',' left_limited
336 $number_set = number_set_prepend($range_elems, $left_limited);
337 if ($number_set == NUMBERSET_NOT_ASCENDING)
339 context->error = TA_Err_Deltas_Ranges_Not_Ascending;
340 YYABORT;
342 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
344 context->error = TA_Err_Deltas_Overlapping_Ranges;
345 YYABORT;
350 unlimited:
353 $unlimited = number_set_new(context->number_set_min,
354 context->number_set_max,
355 context->number_set_min,
356 context->number_set_max);
357 /* range of `$unlimited' is always valid */
358 if ($unlimited == NUMBERSET_ALLOCATION_ERROR)
360 context->error = TA_Err_Deltas_Allocation_Error;
361 YYABORT;
366 right_limited:
367 '-' integer
369 $right_limited = number_set_new(context->number_set_min,
370 $integer,
371 context->number_set_min,
372 context->number_set_max);
373 if ($right_limited == NUMBERSET_INVALID_RANGE)
375 context->error = TA_Err_Deltas_Invalid_Range;
376 YYABORT;
378 if ($right_limited == NUMBERSET_ALLOCATION_ERROR)
380 context->error = TA_Err_Deltas_Allocation_Error;
381 YYABORT;
386 left_limited:
387 integer '-'
389 $left_limited = number_set_new($integer,
390 context->number_set_max,
391 context->number_set_min,
392 context->number_set_max);
393 if ($left_limited == NUMBERSET_INVALID_RANGE)
395 context->error = TA_Err_Deltas_Invalid_Range;
396 YYABORT;
398 if ($left_limited == NUMBERSET_ALLOCATION_ERROR)
400 context->error = TA_Err_Deltas_Allocation_Error;
401 YYABORT;
406 range_elems[result]:
407 range_elem
408 { $result = $range_elem; }
409 | range_elems[left] ',' range_elem
411 $result = number_set_prepend($left, $range_elem);
412 if ($result == NUMBERSET_NOT_ASCENDING)
414 context->error = TA_Err_Deltas_Ranges_Not_Ascending;
415 YYABORT;
417 if ($result == NUMBERSET_OVERLAPPING_RANGES)
419 context->error = TA_Err_Deltas_Overlapping_Ranges;
420 YYABORT;
425 range_elem:
426 integer
428 $range_elem = number_set_new($integer,
429 $integer,
430 context->number_set_min,
431 context->number_set_max);
432 if ($range_elem == NUMBERSET_INVALID_RANGE)
434 context->error = TA_Err_Deltas_Invalid_Range;
435 YYABORT;
437 if ($range_elem == NUMBERSET_ALLOCATION_ERROR)
439 context->error = TA_Err_Deltas_Allocation_Error;
440 YYABORT;
443 | range
444 { $range_elem = $range; }
447 range:
448 integer[left] '-' integer[right]
450 $range = number_set_new($left,
451 $right,
452 context->number_set_min,
453 context->number_set_max);
454 if ($range == NUMBERSET_INVALID_RANGE)
456 context->error = TA_Err_Deltas_Invalid_Range;
457 YYABORT;
459 if ($range == NUMBERSET_ALLOCATION_ERROR)
461 context->error = TA_Err_Deltas_Allocation_Error;
462 YYABORT;
471 void
472 TA_deltas_error(YYLTYPE *locp,
473 Deltas_Context* context,
474 char const* msg)
476 (void)locp;
477 (void)context;
479 fprintf(stderr, "%s\n", msg);
483 #if 1
485 const char* input =
486 "# Test\n"
487 "\n"
488 "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
489 "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
490 "a p 12345678901234567890 x 0.5 @ 23-25";
493 #undef scanner
496 main(int argc,
497 char** argv)
499 TA_Error error;
500 Deltas_Context context;
501 FONT font;
502 SFNT sfnts[1];
503 FT_Library library;
504 FT_Face face;
505 const char* filename;
508 if (argc != 2)
510 fprintf(stderr, "need an outline font as an argument\n");
511 exit(EXIT_FAILURE);
514 filename = argv[1];
516 error = FT_Init_FreeType(&library);
517 if (error)
519 fprintf(stderr, "error while initializing FreeType library (0x%X)\n",
520 error);
521 exit(EXIT_FAILURE);
524 error = FT_New_Face(library, filename, 0, &face);
525 if (error)
527 fprintf(stderr, "error while loading font `%s' (0x%X)\n",
528 filename,
529 error);
530 exit(EXIT_FAILURE);
533 /* we construct a minumum Deltas_Context */
534 sfnts[0].face = face;
536 font.num_sfnts = 1;
537 font.sfnts = sfnts;
539 context.font = &font;
541 TA_deltas_debug = 1;
542 error = TA_deltas_scanner_init(&context, input);
543 if (error)
544 return 0;
545 TA_deltas_parse(&context);
546 TA_deltas_scanner_done(&context);
548 TA_deltas_free(context.result);
550 FT_Done_Face(face);
551 FT_Done_FreeType(library);
553 return 0;
556 #endif
558 /* end of tadeltas.y */