More lexer and parser improvements.
[ttfautohint.git] / lib / tadeltas.y
blob3559aa6627acaa969936097234ba620088a20ba8
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 3
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);
60 void
61 store_error_data(YYLTYPE *locp,
62 Deltas_Context* context,
63 TA_Error error);
66 /* calls to `yylex' in the generated bison code use `scanner' directly */
67 #define scanner context->scanner
70 /* BAD_XXX and INVALID_CHARACTER indicate flex errors */
71 %token INVALID_CHARACTER
72 %token EOE
73 %token <integer> BAD_INTEGER
74 %token <name> BAD_NAME
75 %token <real> BAD_REAL
76 %token <integer> INTEGER
77 %token <name> NAME
78 %token <real> REAL
80 %type <deltas> entry
81 %type <integer> font_idx
82 %type <integer> glyph_idx
83 %type <name> glyph_name
84 %type <deltas> input
85 %type <integer> integer
86 %type <range> left_limited
87 %type <range> number_set
88 %type <range> point_set
89 %type <range> ppem_set
90 %type <range> range
91 %type <range> range_elem
92 %type <range> range_elems
93 %type <real> real
94 %type <range> right_limited
95 %type <real> shift
96 %type <range> unlimited
97 %type <real> x_shift
98 %type <real> y_shift
100 %destructor { TA_deltas_free($$); } <deltas>
101 %destructor { number_set_free($$); } <range>
107 /* `number_range' list elements are stored in reversed order; */
108 /* the call to `TA_deltas_new' fixes this */
109 /* (`Deltas' list elements are stored in reversed order, too, */
110 /* but they don't need to be sorted so we don't care */
112 start:
113 input
114 { context->result = $input; }
117 input[result]:
118 /* empty */
119 { $result = NULL; }
120 | input[left] entry
121 { $result = TA_deltas_prepend($left, $entry); }
124 entry:
126 { $entry = NULL; }
127 | font_idx glyph_idx point_set x_shift y_shift ppem_set EOE
129 $entry = TA_deltas_new($font_idx,
130 $glyph_idx,
131 $point_set,
132 $x_shift,
133 $y_shift,
134 $ppem_set);
135 if (!$entry)
137 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
138 YYABORT;
143 font_idx:
144 /* empty */
146 $font_idx = 0;
147 context->font_idx = $font_idx;
149 | integer
151 $font_idx = $integer;
152 if ($font_idx >= context->font->num_sfnts)
154 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Font_Index);
155 YYABORT;
157 context->font_idx = $font_idx;
161 glyph_idx:
162 integer
164 FT_Face face = context->font->sfnts[context->font_idx].face;
167 $glyph_idx = $integer;
168 if ($glyph_idx >= face->num_glyphs)
170 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Glyph_Index);
171 YYABORT;
173 context->glyph_idx = $glyph_idx;
175 | glyph_name
177 FT_Face face = context->font->sfnts[context->font_idx].face;
180 /* explicitly compare with `.notdef' */
181 /* since `FT_Get_Name_Index' returns glyph index 0 */
182 /* for both this glyph name and invalid ones */
183 if (!strcmp($glyph_name, ".notdef"))
184 $glyph_idx = 0;
185 else
187 $glyph_idx = FT_Get_Name_Index(face, $glyph_name);
188 if ($glyph_idx == 0)
189 $glyph_idx = -1;
192 free($glyph_name);
194 if ($glyph_idx < 0)
196 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Glyph_Name);
197 YYABORT;
199 context->glyph_idx = $glyph_idx;
203 glyph_name:
206 $glyph_name = strdup("p");
207 if ($glyph_name)
209 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
210 YYABORT;
213 | 'x'
215 $glyph_name = strdup("x");
216 if ($glyph_name)
218 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
219 YYABORT;
222 | 'y'
224 $glyph_name = strdup("y");
225 if ($glyph_name)
227 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
228 YYABORT;
231 | NAME
233 /* `$NAME' was allocated in the lexer */
234 $glyph_name = $NAME;
236 | BAD_NAME
238 $glyph_name = $BAD_NAME;
239 YYABORT;
243 point_set:
246 FT_Error error;
247 FT_Face face = context->font->sfnts[context->font_idx].face;
248 int num_points;
251 error = FT_Load_Glyph(face, context->glyph_idx, FT_LOAD_NO_SCALE);
252 if (error)
254 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Glyph);
255 YYABORT;
258 num_points = face->glyph->outline.n_points;
260 context->number_set_min = 0;
261 context->number_set_max = num_points - 1;
263 number_set
264 { $point_set = $number_set; }
267 x_shift:
268 /* empty */
269 { $x_shift = 0; }
270 | 'x' shift
271 { $x_shift = $shift; }
274 y_shift:
275 /* empty */
276 { $y_shift = 0; }
277 | 'y' shift
278 { $y_shift = $shift; }
281 shift:
282 real
284 if ($real < DELTA_SHIFT_MIN || $real > DELTA_SHIFT_MAX)
286 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Shift);
287 YYABORT;
289 $shift = $real;
293 ppem_set:
296 context->number_set_min = DELTA_PPEM_MIN;
297 context->number_set_max = DELTA_PPEM_MAX;
299 number_set
300 { $ppem_set = $number_set; }
303 integer:
305 { $integer = 0; }
306 | INTEGER
307 { $integer = $INTEGER; }
308 | BAD_INTEGER
310 $integer = $BAD_INTEGER;
311 YYABORT;
315 real:
316 integer
317 { $real = $integer; }
318 | '+' integer
319 { $real = $integer; }
320 | '-' integer
321 { $real = -$integer; }
322 | REAL
323 { $real = $REAL; }
324 | '+' REAL
325 { $real = $REAL; }
326 | '-' REAL
327 { $real = -$REAL; }
328 | BAD_REAL
330 $real = $BAD_REAL;
331 YYABORT;
333 | '+' BAD_REAL
335 $real = $BAD_REAL;
336 YYABORT;
338 | '-' BAD_REAL
340 $real = -$BAD_REAL;
341 YYABORT;
345 number_set:
346 unlimited
347 { $number_set = $unlimited; }
348 | right_limited
349 { $number_set = $right_limited; }
350 | left_limited
351 { $number_set = $left_limited; }
352 | range_elems
353 { $number_set = $range_elems; }
354 | right_limited ',' range_elems
356 $number_set = number_set_prepend($right_limited, $range_elems);
357 if ($number_set == NUMBERSET_NOT_ASCENDING)
359 number_set_free($right_limited);
360 number_set_free($range_elems);
361 store_error_data(&@$, context, TA_Err_Deltas_Ranges_Not_Ascending);
362 YYABORT;
364 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
366 number_set_free($right_limited);
367 number_set_free($range_elems);
368 store_error_data(&@$, context, TA_Err_Deltas_Overlapping_Ranges);
369 YYABORT;
372 | range_elems ',' left_limited
374 $number_set = number_set_prepend($range_elems, $left_limited);
375 if ($number_set == NUMBERSET_NOT_ASCENDING)
377 number_set_free($range_elems);
378 number_set_free($left_limited);
379 store_error_data(&@$, context, TA_Err_Deltas_Ranges_Not_Ascending);
380 YYABORT;
382 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
384 number_set_free($range_elems);
385 number_set_free($left_limited);
386 store_error_data(&@$, context, TA_Err_Deltas_Overlapping_Ranges);
387 YYABORT;
392 unlimited:
395 $unlimited = number_set_new(context->number_set_min,
396 context->number_set_max,
397 context->number_set_min,
398 context->number_set_max);
399 /* range of `$unlimited' is always valid */
400 if ($unlimited == NUMBERSET_ALLOCATION_ERROR)
402 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
403 YYABORT;
408 right_limited:
409 '-' integer
411 $right_limited = number_set_new(context->number_set_min,
412 $integer,
413 context->number_set_min,
414 context->number_set_max);
415 if ($right_limited == NUMBERSET_INVALID_RANGE)
417 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Range);
418 YYABORT;
420 if ($right_limited == NUMBERSET_ALLOCATION_ERROR)
422 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
423 YYABORT;
428 left_limited:
429 integer '-'
431 $left_limited = number_set_new($integer,
432 context->number_set_max,
433 context->number_set_min,
434 context->number_set_max);
435 if ($left_limited == NUMBERSET_INVALID_RANGE)
437 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Range);
438 YYABORT;
440 if ($left_limited == NUMBERSET_ALLOCATION_ERROR)
442 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
443 YYABORT;
448 range_elems[result]:
449 range_elem
450 { $result = $range_elem; }
451 | range_elems[left] ',' range_elem
453 $result = number_set_prepend($left, $range_elem);
454 if ($result == NUMBERSET_NOT_ASCENDING)
456 number_set_free($left);
457 number_set_free($range_elem);
458 store_error_data(&@$, context, TA_Err_Deltas_Ranges_Not_Ascending);
459 YYABORT;
461 if ($result == NUMBERSET_OVERLAPPING_RANGES)
463 number_set_free($left);
464 number_set_free($range_elem);
465 store_error_data(&@$, context, TA_Err_Deltas_Overlapping_Ranges);
466 YYABORT;
471 range_elem:
472 integer
474 $range_elem = number_set_new($integer,
475 $integer,
476 context->number_set_min,
477 context->number_set_max);
478 if ($range_elem == NUMBERSET_INVALID_RANGE)
480 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Range);
481 YYABORT;
483 if ($range_elem == NUMBERSET_ALLOCATION_ERROR)
485 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
486 YYABORT;
489 | range
490 { $range_elem = $range; }
493 range:
494 integer[left] '-' integer[right]
496 $range = number_set_new($left,
497 $right,
498 context->number_set_min,
499 context->number_set_max);
500 if ($range == NUMBERSET_INVALID_RANGE)
502 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Range);
503 YYABORT;
505 if ($range == NUMBERSET_ALLOCATION_ERROR)
507 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
508 YYABORT;
517 void
518 TA_deltas_error(YYLTYPE *locp,
519 Deltas_Context* context,
520 char const* msg)
522 context->error = TA_Err_Deltas_Syntax_Error;
524 context->errline_num = locp->first_line;
525 context->errline_pos_left = locp->first_column;
526 context->errline_pos_right = locp->last_column;
528 strncpy(context->errmsg, msg, sizeof (context->errmsg));
532 void
533 store_error_data(YYLTYPE *locp,
534 Deltas_Context* context,
535 TA_Error error)
537 context->error = error;
539 context->errline_num = locp->first_line;
540 context->errline_pos_left = locp->first_column;
541 context->errline_pos_right = locp->last_column;
543 context->errmsg[0] = '\0';
547 #if 1
549 const char* input =
550 "# Test\n"
551 "\n"
552 "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
553 "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
554 "a p 12345678901234567890 x 0.5 @ 23-25";
557 #undef scanner
560 main(int argc,
561 char** argv)
563 TA_Error error;
564 Deltas_Context context;
565 FONT font;
566 SFNT sfnts[1];
567 FT_Library library;
568 FT_Face face;
569 const char* filename;
572 if (argc != 2)
574 fprintf(stderr, "need an outline font as an argument\n");
575 exit(EXIT_FAILURE);
578 filename = argv[1];
580 error = FT_Init_FreeType(&library);
581 if (error)
583 fprintf(stderr, "error while initializing FreeType library (0x%X)\n",
584 error);
585 exit(EXIT_FAILURE);
588 error = FT_New_Face(library, filename, 0, &face);
589 if (error)
591 fprintf(stderr, "error while loading font `%s' (0x%X)\n",
592 filename,
593 error);
594 exit(EXIT_FAILURE);
597 /* we construct a minumum Deltas_Context */
598 sfnts[0].face = face;
600 font.num_sfnts = 1;
601 font.sfnts = sfnts;
603 context.font = &font;
605 TA_deltas_debug = 1;
606 error = TA_deltas_scanner_init(&context, input);
607 if (error)
608 return 0;
609 TA_deltas_parse(&context);
610 TA_deltas_scanner_done(&context);
612 TA_deltas_free(context.result);
614 FT_Done_Face(face);
615 FT_Done_FreeType(library);
617 return 0;
620 #endif
622 /* end of tadeltas.y */