Preparation for including lexer and parser files into automake.
[ttfautohint.git] / lib / tadeltas.bison
blobff9462857e61ed737b3b784a93f1c64f4ac62c32
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.
17 * grammar for parsing delta exceptions
19 * Parsing errors that essentially belong to the lexing stage are handled
20 * with `store_error_data'; in case the lexer detects an error, it returns
21 * the right token type but sets `context->error'. Syntax errors and fatal
22 * lexer errors (token `INTERNAL_FLEX_ERROR') are handled with
23 * `TA_deltas_error'.
27 * Edsko de Vries's article `Writing a Reentrant Parser with Flex and Bison'
28 * (http://www.phpcompiler.org/articles/reentrantparser.html)
29 * was extremely helpful in writing this code.
32 %output "tadeltas-bison.c"
33 %defines "tadeltas-bison.h"
35 %define api.pure
36 %error-verbose
37 %expect 2
38 %glr-parser
39 %lex-param { void* scanner }
40 %locations
41 %name-prefix "TA_deltas_"
42 %parse-param { Deltas_Context* context }
43 %require "2.5"
45 %code requires {
46 #include "ta.h"
48 /* we don't change the name prefix of flex functions */
49 #define TA_deltas_lex yylex
52 %union {
53 char character;
54 long integer;
55 char* name;
56 number_range* range;
57 double real;
58 Deltas* deltas;
62 #include "tadeltas-flex.h"
64 void
65 TA_deltas_error(YYLTYPE *locp,
66 Deltas_Context* context,
67 char const* msg);
69 void
70 store_error_data(YYLTYPE *locp,
71 Deltas_Context* context,
72 TA_Error error);
75 /* calls to `yylex' in the generated bison code use `scanner' directly */
76 #define scanner context->scanner
79 /* INVALID_CHARACTER and INTERNAL_FLEX_ERROR are flex errors */
80 %token EOE
81 %token <integer> INTEGER
82 %token INTERNAL_FLEX_ERROR "internal flex error"
83 %token <character> INVALID_CHARACTER "invalid character"
84 %token <name> NAME
85 %token <real> REAL
87 %type <deltas> entry
88 %type <integer> font_idx
89 %type <integer> glyph_idx
90 %type <name> glyph_name
91 %type <deltas> input
92 %type <integer> integer
93 %type <range> left_limited
94 %type <range> number_set
95 %type <range> point_set
96 %type <range> ppem_set
97 %type <range> range
98 %type <range> range_elem
99 %type <range> range_elems
100 %type <real> real
101 %type <range> right_limited
102 %type <real> shift
103 %type <range> unlimited
104 %type <real> x_shift
105 %type <real> y_shift
107 %destructor { TA_deltas_free($$); } <deltas>
108 %destructor { number_set_free($$); } <range>
110 %printer { fprintf(yyoutput, "`%ld'", $$); } <integer>
111 %printer { fprintf(yyoutput, "`%s'", $$); } <name>
112 %printer { fprintf(yyoutput, "`%g'", $$); } <real>
113 %printer {
114 char* s;
115 number_range* nr;
118 nr = number_set_reverse($$);
119 s = number_set_show(nr, -1, -1);
120 (void)number_set_reverse(nr);
121 if (s)
123 fprintf(yyoutput, "`%s'", s);
124 free(s);
126 else
127 fprintf(yyoutput, "allocation error");
128 } <range>
129 %printer { fprintf(yyoutput, "`%c'", $$); } INVALID_CHARACTER
134 /* `number_range' list elements are stored in reversed order; */
135 /* the call to `TA_deltas_new' fixes this */
137 /* `Deltas' list elements are stored in reversed order, too; */
138 /* this gets fixed by an explicit call to `TA_deltas_reverse' */
140 start:
141 input
142 { context->result = TA_deltas_reverse($input); }
145 input[result]:
146 /* empty */
147 { $result = NULL; }
148 | input[left] entry
149 { $result = TA_deltas_prepend($left, $entry); }
152 entry:
154 { $entry = NULL; }
155 | font_idx glyph_idx point_set x_shift y_shift ppem_set EOE
157 $entry = TA_deltas_new($font_idx,
158 $glyph_idx,
159 $point_set,
160 $x_shift,
161 $y_shift,
162 $ppem_set);
163 if (!$entry)
165 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
166 YYABORT;
171 font_idx:
172 /* empty */
174 $font_idx = 0;
175 context->font_idx = $font_idx;
177 | integer
179 $font_idx = $integer;
180 if ($font_idx >= context->font->num_sfnts)
182 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Font_Index);
183 YYABORT;
185 context->font_idx = $font_idx;
189 glyph_idx:
190 integer
192 FT_Face face = context->font->sfnts[context->font_idx].face;
195 $glyph_idx = $integer;
196 if ($glyph_idx >= face->num_glyphs)
198 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Glyph_Index);
199 YYABORT;
201 context->glyph_idx = $glyph_idx;
203 | glyph_name
205 FT_Face face = context->font->sfnts[context->font_idx].face;
208 /* explicitly compare with `.notdef' */
209 /* since `FT_Get_Name_Index' returns glyph index 0 */
210 /* for both this glyph name and invalid ones */
211 if (!strcmp($glyph_name, ".notdef"))
212 $glyph_idx = 0;
213 else
215 $glyph_idx = FT_Get_Name_Index(face, $glyph_name);
216 if ($glyph_idx == 0)
217 $glyph_idx = -1;
220 free($glyph_name);
222 if ($glyph_idx < 0)
224 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Glyph_Name);
225 YYABORT;
227 context->glyph_idx = $glyph_idx;
231 glyph_name:
234 $glyph_name = strdup("p");
235 if ($glyph_name)
237 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
238 YYABORT;
241 | 'x'
243 $glyph_name = strdup("x");
244 if ($glyph_name)
246 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
247 YYABORT;
250 | 'y'
252 $glyph_name = strdup("y");
253 if ($glyph_name)
255 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
256 YYABORT;
259 | NAME
261 /* `$NAME' was allocated in the lexer */
262 if (context->error)
264 /* lexing error */
265 store_error_data(&@$, context, context->error);
266 YYABORT;
269 $glyph_name = $NAME;
273 point_set:
276 FT_Error error;
277 FT_Face face = context->font->sfnts[context->font_idx].face;
278 int num_points;
281 error = FT_Load_Glyph(face, context->glyph_idx, FT_LOAD_NO_SCALE);
282 if (error)
284 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Glyph);
285 YYABORT;
288 num_points = face->glyph->outline.n_points;
290 context->number_set_min = 0;
291 context->number_set_max = num_points - 1;
293 number_set
294 { $point_set = $number_set; }
297 x_shift:
298 /* empty */
299 { $x_shift = 0; }
300 | 'x' shift
301 { $x_shift = $shift; }
304 y_shift:
305 /* empty */
306 { $y_shift = 0; }
307 | 'y' shift
308 { $y_shift = $shift; }
311 shift:
312 real
314 if ($real < DELTA_SHIFT_MIN || $real > DELTA_SHIFT_MAX)
316 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Shift);
317 YYABORT;
319 $shift = $real;
323 ppem_set:
326 context->number_set_min = DELTA_PPEM_MIN;
327 context->number_set_max = DELTA_PPEM_MAX;
329 number_set
330 { $ppem_set = $number_set; }
333 integer:
335 { $integer = 0; }
336 | INTEGER
338 if (context->error)
340 /* lexing error */
341 store_error_data(&@$, context, context->error);
342 YYABORT;
345 $integer = $INTEGER;
349 real[result]:
350 integer
351 { $result = $integer; }
352 | REAL
354 if (context->error)
356 /* lexing error */
357 store_error_data(&@$, context, context->error);
358 YYABORT;
361 $result = $REAL;
363 | '+' real[unsigned]
364 { $result = $unsigned; }
365 | '-' real[unsigned]
366 { $result = -$unsigned; }
369 number_set:
370 unlimited
371 { $number_set = $unlimited; }
372 | right_limited
373 { $number_set = $right_limited; }
374 | left_limited
375 { $number_set = $left_limited; }
376 | range_elems
377 { $number_set = $range_elems; }
378 | right_limited ',' range_elems
380 $number_set = number_set_prepend($right_limited, $range_elems);
381 if ($number_set == NUMBERSET_NOT_ASCENDING)
383 number_set_free($right_limited);
384 number_set_free($range_elems);
385 store_error_data(&@$, context, TA_Err_Deltas_Ranges_Not_Ascending);
386 YYABORT;
388 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
390 number_set_free($right_limited);
391 number_set_free($range_elems);
392 store_error_data(&@$, context, TA_Err_Deltas_Overlapping_Ranges);
393 YYABORT;
396 | range_elems ',' left_limited
398 $number_set = number_set_prepend($range_elems, $left_limited);
399 if ($number_set == NUMBERSET_NOT_ASCENDING)
401 number_set_free($range_elems);
402 number_set_free($left_limited);
403 store_error_data(&@$, context, TA_Err_Deltas_Ranges_Not_Ascending);
404 YYABORT;
406 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
408 number_set_free($range_elems);
409 number_set_free($left_limited);
410 store_error_data(&@$, context, TA_Err_Deltas_Overlapping_Ranges);
411 YYABORT;
416 unlimited:
419 $unlimited = number_set_new(context->number_set_min,
420 context->number_set_max,
421 context->number_set_min,
422 context->number_set_max);
423 /* range of `$unlimited' is always valid */
424 if ($unlimited == NUMBERSET_ALLOCATION_ERROR)
426 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
427 YYABORT;
432 right_limited:
433 '-' integer
435 $right_limited = number_set_new(context->number_set_min,
436 $integer,
437 context->number_set_min,
438 context->number_set_max);
439 if ($right_limited == NUMBERSET_INVALID_RANGE)
441 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Range);
442 YYABORT;
444 if ($right_limited == NUMBERSET_ALLOCATION_ERROR)
446 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
447 YYABORT;
452 left_limited:
453 integer '-'
455 $left_limited = number_set_new($integer,
456 context->number_set_max,
457 context->number_set_min,
458 context->number_set_max);
459 if ($left_limited == NUMBERSET_INVALID_RANGE)
461 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Range);
462 YYABORT;
464 if ($left_limited == NUMBERSET_ALLOCATION_ERROR)
466 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
467 YYABORT;
472 range_elems[result]:
473 range_elem
474 { $result = $range_elem; }
475 | range_elems[left] ',' range_elem
477 $result = number_set_prepend($left, $range_elem);
478 if ($result == NUMBERSET_NOT_ASCENDING)
480 number_set_free($left);
481 number_set_free($range_elem);
482 store_error_data(&@$, context, TA_Err_Deltas_Ranges_Not_Ascending);
483 YYABORT;
485 if ($result == NUMBERSET_OVERLAPPING_RANGES)
487 number_set_free($left);
488 number_set_free($range_elem);
489 store_error_data(&@$, context, TA_Err_Deltas_Overlapping_Ranges);
490 YYABORT;
495 range_elem:
496 integer
498 $range_elem = number_set_new($integer,
499 $integer,
500 context->number_set_min,
501 context->number_set_max);
502 if ($range_elem == NUMBERSET_INVALID_RANGE)
504 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Range);
505 YYABORT;
507 if ($range_elem == NUMBERSET_ALLOCATION_ERROR)
509 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
510 YYABORT;
513 | range
514 { $range_elem = $range; }
517 range:
518 integer[left] '-' integer[right]
520 $range = number_set_new($left,
521 $right,
522 context->number_set_min,
523 context->number_set_max);
524 if ($range == NUMBERSET_INVALID_RANGE)
526 store_error_data(&@$, context, TA_Err_Deltas_Invalid_Range);
527 YYABORT;
529 if ($range == NUMBERSET_ALLOCATION_ERROR)
531 store_error_data(&@$, context, TA_Err_Deltas_Allocation_Error);
532 YYABORT;
541 void
542 TA_deltas_error(YYLTYPE *locp,
543 Deltas_Context* context,
544 char const* msg)
546 /* if `error' is already set, we have a fatal flex error */
547 if (!context->error)
549 context->error = TA_Err_Deltas_Syntax_Error;
550 strncpy(context->errmsg, msg, sizeof (context->errmsg));
553 context->errline_num = locp->first_line;
554 context->errline_pos_left = locp->first_column;
555 context->errline_pos_right = locp->last_column;
559 void
560 store_error_data(YYLTYPE *locp,
561 Deltas_Context* context,
562 TA_Error error)
564 context->error = error;
566 context->errline_num = locp->first_line;
567 context->errline_pos_left = locp->first_column;
568 context->errline_pos_right = locp->last_column;
570 context->errmsg[0] = '\0';
574 #if 1
576 const char* input =
577 "# Test\n"
578 "\n"
579 "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
580 "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
581 "a p / 12 x 0.5 @ 23-25";
584 #undef scanner
587 main(int argc,
588 char** argv)
590 TA_Error error;
591 int bison_error;
592 int retval = 1;
594 Deltas_Context context;
595 FONT font;
596 SFNT sfnts[1];
597 FT_Library library;
598 FT_Face face;
599 const char* filename;
602 if (argc != 2)
604 fprintf(stderr, "need an outline font as an argument\n");
605 goto Exit0;
608 filename = argv[1];
610 error = FT_Init_FreeType(&library);
611 if (error)
613 fprintf(stderr, "error while initializing FreeType library (0x%X)\n",
614 error);
615 goto Exit0;
618 error = FT_New_Face(library, filename, 0, &face);
619 if (error)
621 fprintf(stderr, "error while loading font `%s' (0x%X)\n",
622 filename,
623 error);
624 goto Exit1;
627 /* we construct a minumum `Font' object */
628 sfnts[0].face = face;
630 font.num_sfnts = 1;
631 font.sfnts = sfnts;
632 font.deltas_buf = (char*)input;
633 font.deltas_len = strlen(input);
635 TA_deltas_debug = 1;
637 TA_deltas_scanner_init(&context, &font);
638 if (context.error)
639 goto Exit2;
641 bison_error = TA_deltas_parse(&context);
642 if (bison_error)
643 goto Exit3;
645 retval = 0;
647 Exit3:
648 TA_deltas_scanner_done(&context);
649 TA_deltas_free(context.result);
651 Exit2:
652 FT_Done_Face(face);
654 Exit1:
655 FT_Done_FreeType(library);
657 Exit0:
658 return retval;
661 #endif
663 /* end of tadeltas.y */