Synchronize with FreeType [4/4].
[ttfautohint.git] / lib / tacontrol.bison
blobb36fea47800b96ece9f06175de687180559a785a
1 /* tacontrol.bison */
3 /*
4 * Copyright (C) 2014-2015 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 ttfautohint control instructions
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_control_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 %require "2.5" /* we use named references */
34 %output "tacontrol-bison.c"
35 %defines "tacontrol-bison.h"
37 %define api.pure
38 %error-verbose
39 %expect 2
40 %glr-parser
41 %lex-param { void* scanner }
42 %locations
43 %name-prefix "TA_control_"
44 %parse-param { Control_Context* context }
46 %code requires {
47 #include "ta.h"
49 /* we don't change the name prefix of flex functions */
50 #define TA_control_lex yylex
53 %union {
54 char character;
55 Control_Type type;
56 long integer;
57 char* name;
58 number_range* range;
59 double real;
60 Control* control;
64 #include <limits.h>
65 #include "tacontrol-flex.h"
67 void
68 TA_control_error(YYLTYPE *locp,
69 Control_Context* context,
70 char const* msg);
72 void
73 store_error_data(const YYLTYPE *locp,
74 Control_Context* context,
75 TA_Error error);
78 /* calls to `yylex' in the generated bison code use `scanner' directly */
79 #define scanner context->scanner
82 /* INVALID_CHARACTER and INTERNAL_FLEX_ERROR are flex errors */
83 %token EOE
84 %token <integer> INTEGER "integer number"
85 %token INTERNAL_FLEX_ERROR "internal flex error"
86 %token <character> INVALID_CHARACTER "invalid character"
87 %token <name> LEFT "left"
88 %token <name> NAME "glyph name"
89 %token <name> NODIR "no direction"
90 %token <name> POINT "point"
91 %token <real> REAL "real number"
92 %token <name> RIGHT "right"
93 %token <name> TOUCH "touch"
94 %token <name> XSHIFT "x shift"
95 %token <name> YSHIFT "y shift"
97 %type <control> entry
98 %type <integer> font_idx
99 %type <integer> glyph_idx
100 %type <name> glyph_name
101 %type <name> glyph_name_
102 %type <control> input
103 %type <integer> integer
104 %type <type> left_right
105 %type <type> left_right_
106 %type <type> point_touch
107 %type <type> point_touch_
108 %type <range> left_limited
109 %type <type> no_dir
110 %type <range> number_set
111 %type <integer> offset
112 %type <range> ppem_set
113 %type <range> range
114 %type <range> range_elem
115 %type <range> range_elems
116 %type <real> real
117 %type <range> right_limited
118 %type <real> shift
119 %type <integer> signed_integer
120 %type <range> unlimited
121 %type <real> x_shift
122 %type <real> y_shift
124 %destructor { TA_control_free($$); } <control>
125 %destructor { number_set_free($$); } <range>
127 %printer { fprintf(yyoutput, "`%ld'", $$); } <integer>
128 %printer { fprintf(yyoutput, "`%s'", $$); } <name>
129 %printer { fprintf(yyoutput, "`%g'", $$); } <real>
130 %printer {
131 char* s;
132 number_range* nr;
135 nr = number_set_reverse($$);
136 s = number_set_show(nr, -1, -1);
137 (void)number_set_reverse(nr);
138 if (s)
140 fprintf(yyoutput, "`%s'", s);
141 free(s);
143 else
144 fprintf(yyoutput, "allocation error");
145 } <range>
146 %printer { fprintf(yyoutput, "`%c'", $$); } INVALID_CHARACTER
151 /* `number_range' list elements are stored in reversed order; */
152 /* the call to `TA_control_new' fixes this */
154 /* `Control' list elements are stored in reversed order, too; */
155 /* this gets fixed by an explicit call to `TA_control_reverse' */
157 start:
158 input
159 { context->result = TA_control_reverse($input); }
162 input[result]:
163 /* empty */
164 { $result = NULL; }
165 | input[left] entry
166 { $result = TA_control_prepend($left, $entry); }
169 entry:
171 { $entry = NULL; }
172 | font_idx glyph_idx point_touch number_set x_shift y_shift ppem_set EOE
174 $entry = TA_control_new($point_touch,
175 $font_idx,
176 $glyph_idx,
177 $number_set,
178 $x_shift,
179 $y_shift,
180 $ppem_set);
181 if (!$entry)
183 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
184 YYABORT;
187 | font_idx glyph_idx left_right number_set EOE
189 $entry = TA_control_new($left_right,
190 $font_idx,
191 $glyph_idx,
192 $number_set,
195 NULL);
196 if (!$entry)
198 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
199 YYABORT;
202 | font_idx glyph_idx left_right number_set '(' offset[left] ',' offset[right] ')' EOE
204 $entry = TA_control_new($left_right,
205 $font_idx,
206 $glyph_idx,
207 $number_set,
208 $left,
209 $right,
210 NULL);
211 if (!$entry)
213 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
214 YYABORT;
217 | font_idx glyph_idx no_dir number_set EOE
219 $entry = TA_control_new($no_dir,
220 $font_idx,
221 $glyph_idx,
222 $number_set,
225 NULL);
226 if (!$entry)
228 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
229 YYABORT;
234 font_idx:
235 /* empty */
237 $font_idx = 0;
238 context->font_idx = $font_idx;
240 | integer
242 $font_idx = $integer;
243 if ($font_idx >= context->font->num_sfnts)
245 store_error_data(&@$, context, TA_Err_Control_Invalid_Font_Index);
246 YYABORT;
248 context->font_idx = $font_idx;
252 glyph_idx:
253 integer
255 FT_Face face = context->font->sfnts[context->font_idx].face;
258 $glyph_idx = $integer;
259 if ($glyph_idx >= face->num_glyphs)
261 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph_Index);
262 YYABORT;
264 context->glyph_idx = $glyph_idx;
266 | glyph_name
268 FT_Face face = context->font->sfnts[context->font_idx].face;
271 /* explicitly compare with `.notdef' */
272 /* since `FT_Get_Name_Index' returns glyph index 0 */
273 /* for both this glyph name and invalid ones */
274 if (!strcmp($glyph_name, ".notdef"))
275 $glyph_idx = 0;
276 else
278 $glyph_idx = FT_Get_Name_Index(face, $glyph_name);
279 if ($glyph_idx == 0)
280 $glyph_idx = -1;
283 free($glyph_name);
285 if ($glyph_idx < 0)
287 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph_Name);
288 YYABORT;
290 context->glyph_idx = $glyph_idx;
294 glyph_name:
295 glyph_name_
297 /* `$glyph_name_' was allocated in the lexer */
298 if (context->error)
300 /* lexing error */
301 store_error_data(&@$, context, context->error);
302 YYABORT;
305 $glyph_name = $glyph_name_;
309 glyph_name_:
310 LEFT
311 | NAME
312 | NODIR
313 | POINT
314 | RIGHT
315 | TOUCH
316 | XSHIFT
317 | YSHIFT
320 point_touch:
321 point_touch_
323 FT_Error error;
324 FT_Face face = context->font->sfnts[context->font_idx].face;
325 int num_points;
328 error = FT_Load_Glyph(face, context->glyph_idx, FT_LOAD_NO_SCALE);
329 if (error)
331 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph);
332 YYABORT;
335 num_points = face->glyph->outline.n_points;
337 context->number_set_min = 0;
338 context->number_set_max = num_points - 1;
340 $point_touch = $point_touch_;
344 point_touch_:
345 POINT
347 $point_touch_ = Control_Delta_after_IUP;
348 free($POINT);
350 | TOUCH
352 $point_touch_ = Control_Delta_before_IUP;
353 free($TOUCH);
357 left_right:
358 left_right_
360 FT_Error error;
361 FT_Face face = context->font->sfnts[context->font_idx].face;
362 int num_points;
365 error = FT_Load_Glyph(face, context->glyph_idx, FT_LOAD_NO_SCALE);
366 if (error)
368 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph);
369 YYABORT;
372 num_points = face->glyph->outline.n_points;
374 context->number_set_min = 0;
375 context->number_set_max = num_points - 1;
377 $left_right = $left_right_;
381 left_right_:
382 LEFT
384 $left_right_ = Control_Segment_Left;
385 free($LEFT);
387 | RIGHT
389 $left_right_ = Control_Segment_Right;
390 free($RIGHT);
394 no_dir:
395 NODIR
397 FT_Error error;
398 FT_Face face = context->font->sfnts[context->font_idx].face;
399 int num_points;
402 error = FT_Load_Glyph(face, context->glyph_idx, FT_LOAD_NO_SCALE);
403 if (error)
405 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph);
406 YYABORT;
409 num_points = face->glyph->outline.n_points;
411 context->number_set_min = 0;
412 context->number_set_max = num_points - 1;
414 $no_dir = Control_Segment_None;
415 free($NODIR);
420 x_shift:
421 /* empty */
422 { $x_shift = 0; }
423 | XSHIFT shift
425 $x_shift = $shift;
426 free($XSHIFT);
430 y_shift:
431 /* empty */
432 { $y_shift = 0; }
433 | YSHIFT shift
435 $y_shift = $shift;
436 free($YSHIFT);
440 shift:
441 real
443 if ($real < CONTROL_DELTA_SHIFT_MIN || $real > CONTROL_DELTA_SHIFT_MAX)
445 store_error_data(&@$, context, TA_Err_Control_Invalid_Shift);
446 YYABORT;
448 $shift = $real;
452 offset:
453 signed_integer
455 if ($signed_integer < SHRT_MIN || $signed_integer > SHRT_MAX)
457 store_error_data(&@$, context, TA_Err_Control_Invalid_Offset);
458 YYABORT;
460 $offset = $signed_integer;
464 ppem_set:
467 context->number_set_min = CONTROL_DELTA_PPEM_MIN;
468 context->number_set_max = CONTROL_DELTA_PPEM_MAX;
470 number_set
471 { $ppem_set = $number_set; }
474 integer:
476 { $integer = 0; }
477 | INTEGER
479 if (context->error)
481 /* lexing error */
482 store_error_data(&@$, context, context->error);
483 YYABORT;
486 $integer = $INTEGER;
490 signed_integer:
491 integer
492 { $signed_integer = $integer; }
493 | '+' integer
494 { $signed_integer = $integer; }
495 | '-' integer
496 { $signed_integer = -$integer; }
499 real:
500 signed_integer
501 { $real = $signed_integer; }
502 | REAL
504 if (context->error)
506 /* lexing error */
507 store_error_data(&@$, context, context->error);
508 YYABORT;
511 $real = $REAL;
513 | '+' REAL
514 { $real = $REAL; }
515 | '-' REAL
516 { $real = -$REAL; }
519 number_set:
520 unlimited
521 { $number_set = $unlimited; }
522 | right_limited
523 { $number_set = $right_limited; }
524 | left_limited
525 { $number_set = $left_limited; }
526 | range_elems
527 { $number_set = $range_elems; }
528 | right_limited ',' range_elems
530 $number_set = number_set_prepend($right_limited, $range_elems);
531 if ($number_set == NUMBERSET_NOT_ASCENDING)
533 number_set_free($right_limited);
534 number_set_free($range_elems);
535 store_error_data(&@3, context, TA_Err_Control_Ranges_Not_Ascending);
536 YYABORT;
538 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
540 number_set_free($right_limited);
541 number_set_free($range_elems);
542 store_error_data(&@3, context, TA_Err_Control_Overlapping_Ranges);
543 YYABORT;
546 | range_elems ',' left_limited
548 $number_set = number_set_prepend($range_elems, $left_limited);
549 if ($number_set == NUMBERSET_NOT_ASCENDING)
551 number_set_free($range_elems);
552 number_set_free($left_limited);
553 store_error_data(&@3, context, TA_Err_Control_Ranges_Not_Ascending);
554 YYABORT;
556 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
558 number_set_free($range_elems);
559 number_set_free($left_limited);
560 store_error_data(&@3, context, TA_Err_Control_Overlapping_Ranges);
561 YYABORT;
566 unlimited:
569 $unlimited = number_set_new(context->number_set_min,
570 context->number_set_max,
571 context->number_set_min,
572 context->number_set_max);
573 /* range of `$unlimited' is always valid */
574 if ($unlimited == NUMBERSET_ALLOCATION_ERROR)
576 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
577 YYABORT;
582 right_limited:
583 '-' integer
585 $right_limited = number_set_new(context->number_set_min,
586 $integer,
587 context->number_set_min,
588 context->number_set_max);
589 if ($right_limited == NUMBERSET_INVALID_RANGE)
591 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
592 YYABORT;
594 if ($right_limited == NUMBERSET_ALLOCATION_ERROR)
596 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
597 YYABORT;
602 left_limited:
603 integer '-'
605 $left_limited = number_set_new($integer,
606 context->number_set_max,
607 context->number_set_min,
608 context->number_set_max);
609 if ($left_limited == NUMBERSET_INVALID_RANGE)
611 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
612 YYABORT;
614 if ($left_limited == NUMBERSET_ALLOCATION_ERROR)
616 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
617 YYABORT;
622 range_elems[result]:
623 range_elem
624 { $result = $range_elem; }
625 | range_elems[left] ',' range_elem
627 $result = number_set_prepend($left, $range_elem);
628 if ($result == NUMBERSET_NOT_ASCENDING)
630 number_set_free($left);
631 number_set_free($range_elem);
632 store_error_data(&@3, context, TA_Err_Control_Ranges_Not_Ascending);
633 YYABORT;
635 if ($result == NUMBERSET_OVERLAPPING_RANGES)
637 number_set_free($left);
638 number_set_free($range_elem);
639 store_error_data(&@3, context, TA_Err_Control_Overlapping_Ranges);
640 YYABORT;
645 range_elem:
646 integer
648 $range_elem = number_set_new($integer,
649 $integer,
650 context->number_set_min,
651 context->number_set_max);
652 if ($range_elem == NUMBERSET_INVALID_RANGE)
654 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
655 YYABORT;
657 if ($range_elem == NUMBERSET_ALLOCATION_ERROR)
659 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
660 YYABORT;
663 | range
664 { $range_elem = $range; }
667 range:
668 integer[left] '-' integer[right]
670 $range = number_set_new($left,
671 $right,
672 context->number_set_min,
673 context->number_set_max);
674 if ($range == NUMBERSET_INVALID_RANGE)
676 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
677 YYABORT;
679 if ($range == NUMBERSET_ALLOCATION_ERROR)
681 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
682 YYABORT;
691 void
692 TA_control_error(YYLTYPE *locp,
693 Control_Context* context,
694 char const* msg)
696 /* if `error' is already set, we have a fatal flex error */
697 if (!context->error)
699 context->error = TA_Err_Control_Syntax_Error;
700 strncpy(context->errmsg, msg, sizeof (context->errmsg));
703 context->errline_num = locp->first_line;
704 context->errline_pos_left = locp->first_column;
705 context->errline_pos_right = locp->last_column;
709 void
710 store_error_data(const YYLTYPE *locp,
711 Control_Context* context,
712 TA_Error error)
714 context->error = error;
716 context->errline_num = locp->first_line;
717 context->errline_pos_left = locp->first_column;
718 context->errline_pos_right = locp->last_column;
720 context->errmsg[0] = '\0';
724 #if 0
727 * compile this test program with
729 * make libnumberset.la
731 * flex -d tacontrol.flex \
732 * && bison -t tacontrol.bison \
733 * && gcc -g3 -O0 \
734 * -Wall -W \
735 * -I.. \
736 * -I. \
737 * -I/usr/local/include/freetype2 \
738 * -I/usr/local/include/harfbuzz \
739 * -L.libs \
740 * -o tacontrol-bison \
741 * tacontrol-bison.c \
742 * tacontrol-flex.c \
743 * tacontrol.c \
744 * -lfreetype \
745 * -lnumberset
748 const char* input =
749 "# Test\n"
750 "\n"
751 "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
752 "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
753 "a p / 12 x 0.5 @ 23-25";
756 #undef scanner
759 main(int argc,
760 char** argv)
762 TA_Error error;
763 int bison_error;
764 int retval = 1;
766 Control_Context context;
767 FONT font;
768 SFNT sfnts[1];
769 FT_Library library;
770 FT_Face face;
771 const char* filename;
774 if (argc != 2)
776 fprintf(stderr, "need an outline font as an argument\n");
777 goto Exit0;
780 filename = argv[1];
782 error = FT_Init_FreeType(&library);
783 if (error)
785 fprintf(stderr, "error while initializing FreeType library (0x%X)\n",
786 error);
787 goto Exit0;
790 error = FT_New_Face(library, filename, 0, &face);
791 if (error)
793 fprintf(stderr, "error while loading font `%s' (0x%X)\n",
794 filename,
795 error);
796 goto Exit1;
799 /* we construct a minumum `Font' object */
800 sfnts[0].face = face;
802 font.num_sfnts = 1;
803 font.sfnts = sfnts;
804 font.control_buf = (char*)input;
805 font.control_len = strlen(input);
807 TA_control_debug = 1;
809 TA_control_scanner_init(&context, &font);
810 if (context.error)
811 goto Exit2;
813 bison_error = TA_control_parse(&context);
814 if (bison_error)
815 goto Exit3;
817 retval = 0;
819 Exit3:
820 TA_control_scanner_done(&context);
821 TA_control_free(context.result);
823 Exit2:
824 FT_Done_Face(face);
826 Exit1:
827 FT_Done_FreeType(library);
829 Exit0:
830 return retval;
833 #endif
835 /* end of tacontrol.bison */