RIP, Vernon...
[ttfautohint.git] / lib / tacontrol.bison
blobf52a6364151c18f95c946f80770fd9b30f47cfb1
1 /* tacontrol.bison */
3 /*
4 * Copyright (C) 2014-2016 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 7
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"
48 #include "tashaper.h"
50 /* we don't change the name prefix of flex functions */
51 #define TA_control_lex yylex
54 %union {
55 char character;
56 Control_Type type;
57 long integer;
58 char* name;
59 number_range* range;
60 double real;
61 Control* control;
65 #include <limits.h>
66 #include "tacontrol-flex.h"
68 void
69 TA_control_error(YYLTYPE *locp,
70 Control_Context* context,
71 char const* msg);
73 void
74 store_error_data(const YYLTYPE *locp,
75 Control_Context* context,
76 TA_Error error);
79 /* calls to `yylex' in the generated bison code use `scanner' directly */
80 #define scanner context->scanner
83 /* INVALID_CHARACTER and INTERNAL_FLEX_ERROR are flex errors */
84 %token EOE
85 %token <integer> INTEGER "integer number"
86 %token INTERNAL_FLEX_ERROR "internal flex error"
87 %token <character> INVALID_CHARACTER "invalid character"
88 %token <name> LEFT "left"
89 %token <name> NAME "glyph name"
90 %token <name> NODIR "no direction"
91 %token <name> POINT "point"
92 %token <real> REAL "real number"
93 %token <name> RIGHT "right"
94 %token <name> TOUCH "touch"
95 %token <name> XSHIFT "x shift"
96 %token <name> YSHIFT "y shift"
98 %type <control> entry
99 %type <integer> font_idx
100 %type <integer> glyph_idx
101 %type <range> glyph_idx_range
102 %type <range> glyph_idx_range_elem
103 %type <range> glyph_idx_range_elems
104 %type <range> glyph_idx_set
105 %type <name> glyph_name
106 %type <name> glyph_name_
107 %type <control> input
108 %type <integer> integer
109 %type <type> left_right
110 %type <type> left_right_
111 %type <type> point_touch
112 %type <type> point_touch_
113 %type <range> left_limited
114 %type <type> no_dir
115 %type <range> number_set
116 %type <integer> offset
117 %type <range> ppem_set
118 %type <range> range
119 %type <range> range_elem
120 %type <range> range_elems
121 %type <real> real
122 %type <range> right_limited
123 %type <integer> script_feature
124 %type <real> shift
125 %type <integer> signed_integer
126 %type <range> unlimited
127 %type <real> x_shift
128 %type <real> y_shift
130 %destructor { TA_control_free($$); } <control>
131 %destructor { number_set_free($$); } <range>
133 %printer { fprintf(yyoutput, "`%ld'", $$); } <integer>
134 %printer { fprintf(yyoutput, "`%s'", $$); } <name>
135 %printer { fprintf(yyoutput, "`%g'", $$); } <real>
136 %printer {
137 char* s;
138 number_range* nr;
141 nr = number_set_reverse($$);
142 s = number_set_show(nr, -1, -1);
143 (void)number_set_reverse(nr);
144 if (s)
146 fprintf(yyoutput, "`%s'", s);
147 free(s);
149 else
150 fprintf(yyoutput, "allocation error");
151 } <range>
152 %printer { fprintf(yyoutput, "`%c'", $$); } INVALID_CHARACTER
157 /* `number_range' list elements are stored in reversed order; */
158 /* the call to `TA_control_new' fixes this */
160 /* `Control' list elements are stored in reversed order, too; */
161 /* this gets fixed by an explicit call to `TA_control_reverse' */
163 start:
164 input
165 { context->result = TA_control_reverse($input); }
168 input[result]:
169 /* empty */
170 { $result = NULL; }
171 | input[left] entry
172 { $result = TA_control_prepend($left, $entry); }
175 entry:
177 { $entry = NULL; }
178 | font_idx glyph_idx point_touch number_set x_shift y_shift ppem_set EOE
180 $entry = TA_control_new($point_touch,
181 $font_idx,
182 $glyph_idx,
183 $number_set,
184 $x_shift,
185 $y_shift,
186 $ppem_set);
187 if (!$entry)
189 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
190 YYABORT;
193 | font_idx glyph_idx left_right number_set EOE
195 $entry = TA_control_new($left_right,
196 $font_idx,
197 $glyph_idx,
198 $number_set,
201 NULL);
202 if (!$entry)
204 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
205 YYABORT;
208 | font_idx glyph_idx left_right number_set '(' offset[left] ',' offset[right] ')' EOE
210 $entry = TA_control_new($left_right,
211 $font_idx,
212 $glyph_idx,
213 $number_set,
214 $left,
215 $right,
216 NULL);
217 if (!$entry)
219 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
220 YYABORT;
223 | font_idx glyph_idx no_dir number_set EOE
225 $entry = TA_control_new($no_dir,
226 $font_idx,
227 $glyph_idx,
228 $number_set,
231 NULL);
232 if (!$entry)
234 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
235 YYABORT;
238 | font_idx script_feature glyph_idx_set EOE
240 $entry = TA_control_new(Control_Script_Feature,
241 $font_idx,
242 $script_feature,
243 $glyph_idx_set,
246 NULL);
247 if (!$entry)
249 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
250 YYABORT;
255 font_idx:
256 /* empty */
258 $font_idx = 0;
259 context->font_idx = $font_idx;
261 | integer
263 $font_idx = $integer;
264 if ($font_idx >= context->font->num_sfnts)
266 store_error_data(&@$, context, TA_Err_Control_Invalid_Font_Index);
267 YYABORT;
269 context->font_idx = $font_idx;
273 glyph_idx:
274 integer
276 FT_Face face = context->font->sfnts[context->font_idx].face;
279 $glyph_idx = $integer;
280 if ($glyph_idx >= face->num_glyphs)
282 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph_Index);
283 YYABORT;
285 context->glyph_idx = $glyph_idx;
287 | glyph_name
289 FT_Face face = context->font->sfnts[context->font_idx].face;
292 /* explicitly compare with `.notdef' */
293 /* since `FT_Get_Name_Index' returns glyph index 0 */
294 /* for both this glyph name and invalid ones */
295 if (!strcmp($glyph_name, ".notdef"))
296 $glyph_idx = 0;
297 else
299 $glyph_idx = (long)FT_Get_Name_Index(face, $glyph_name);
300 if ($glyph_idx == 0)
301 $glyph_idx = -1;
304 free($glyph_name);
306 if ($glyph_idx < 0)
308 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph_Name);
309 YYABORT;
311 context->glyph_idx = $glyph_idx;
315 glyph_name:
316 glyph_name_
318 /* `$glyph_name_' was allocated in the lexer */
319 if (context->error)
321 /* lexing error */
322 store_error_data(&@$, context, context->error);
323 YYABORT;
326 $glyph_name = $glyph_name_;
330 glyph_name_:
331 LEFT
332 | NAME
333 | NODIR
334 | POINT
335 | RIGHT
336 | TOUCH
337 | XSHIFT
338 | YSHIFT
341 point_touch:
342 point_touch_
344 FT_Error error;
345 FT_Face face = context->font->sfnts[context->font_idx].face;
346 int num_points;
349 error = FT_Load_Glyph(face,
350 (FT_UInt)context->glyph_idx,
351 FT_LOAD_NO_SCALE);
352 if (error)
354 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph);
355 YYABORT;
358 num_points = face->glyph->outline.n_points;
360 context->number_set_min = 0;
361 context->number_set_max = num_points - 1;
363 $point_touch = $point_touch_;
367 point_touch_:
368 POINT
370 $point_touch_ = Control_Delta_after_IUP;
371 free($POINT);
373 | TOUCH
375 $point_touch_ = Control_Delta_before_IUP;
376 free($TOUCH);
380 left_right:
381 left_right_
383 FT_Error error;
384 FT_Face face = context->font->sfnts[context->font_idx].face;
385 int num_points;
388 error = FT_Load_Glyph(face,
389 (FT_UInt)context->glyph_idx,
390 FT_LOAD_NO_SCALE);
391 if (error)
393 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph);
394 YYABORT;
397 num_points = face->glyph->outline.n_points;
399 context->number_set_min = 0;
400 context->number_set_max = num_points - 1;
402 $left_right = $left_right_;
406 left_right_:
407 LEFT
409 $left_right_ = Control_Segment_Left;
410 free($LEFT);
412 | RIGHT
414 $left_right_ = Control_Segment_Right;
415 free($RIGHT);
419 no_dir:
420 NODIR
422 FT_Error error;
423 FT_Face face = context->font->sfnts[context->font_idx].face;
424 int num_points;
427 error = FT_Load_Glyph(face,
428 (FT_UInt)context->glyph_idx,
429 FT_LOAD_NO_SCALE);
430 if (error)
432 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph);
433 YYABORT;
436 num_points = face->glyph->outline.n_points;
438 context->number_set_min = 0;
439 context->number_set_max = num_points - 1;
441 $no_dir = Control_Segment_None;
442 free($NODIR);
446 script_feature:
447 glyph_name[script] glyph_name[feature]
449 long ss;
450 size_t i, j;
451 size_t script_idx = 0;
452 size_t feature_idx = 0;
453 char feature_name[5];
456 for (i = 0; i < script_names_size; i++)
458 if (!strcmp($script, script_names[i]))
460 script_idx = i;
461 break;
465 feature_name[4] = '\0';
467 for (j = 0; j < feature_tags_size; j++)
469 hb_tag_to_string(feature_tags[j], feature_name);
471 if (!strcmp($feature, feature_name))
473 feature_idx = j;
474 break;
478 free($script);
479 free($feature);
481 if (i == script_names_size)
483 store_error_data(&@1, context, TA_Err_Control_Invalid_Script);
484 YYABORT;
486 if (j == feature_tags_size)
488 store_error_data(&@2, context, TA_Err_Control_Invalid_Feature);
489 YYABORT;
492 for (ss = 0; ta_style_classes[ss]; ss++)
494 TA_StyleClass style_class = ta_style_classes[ss];
497 if (script_idx == style_class->script
498 && feature_idx == style_class->coverage)
500 $script_feature = ss;
501 break;
504 if (!ta_style_classes[ss])
506 store_error_data(&@$, context, TA_Err_Control_Invalid_Style);
507 YYABORT;
513 x_shift:
514 /* empty */
515 { $x_shift = 0; }
516 | XSHIFT shift
518 $x_shift = $shift;
519 free($XSHIFT);
523 y_shift:
524 /* empty */
525 { $y_shift = 0; }
526 | YSHIFT shift
528 $y_shift = $shift;
529 free($YSHIFT);
533 shift:
534 real
536 if ($real < CONTROL_DELTA_SHIFT_MIN || $real > CONTROL_DELTA_SHIFT_MAX)
538 store_error_data(&@$, context, TA_Err_Control_Invalid_Shift);
539 YYABORT;
541 $shift = $real;
545 offset:
546 signed_integer
548 if ($signed_integer < SHRT_MIN || $signed_integer > SHRT_MAX)
550 store_error_data(&@$, context, TA_Err_Control_Invalid_Offset);
551 YYABORT;
553 $offset = $signed_integer;
557 ppem_set:
560 context->number_set_min = CONTROL_DELTA_PPEM_MIN;
561 context->number_set_max = CONTROL_DELTA_PPEM_MAX;
563 number_set
564 { $ppem_set = $number_set; }
567 integer:
569 { $integer = 0; }
570 | INTEGER
572 if (context->error)
574 /* lexing error */
575 store_error_data(&@$, context, context->error);
576 YYABORT;
579 $integer = $INTEGER;
583 signed_integer:
584 integer
585 { $signed_integer = $integer; }
586 | '+' integer
587 { $signed_integer = $integer; }
588 | '-' integer
589 { $signed_integer = -$integer; }
592 real:
593 signed_integer
594 { $real = $signed_integer; }
595 | REAL
597 if (context->error)
599 /* lexing error */
600 store_error_data(&@$, context, context->error);
601 YYABORT;
604 $real = $REAL;
606 | '+' REAL
607 { $real = $REAL; }
608 | '-' REAL
609 { $real = -$REAL; }
612 number_set:
613 unlimited
614 { $number_set = $unlimited; }
615 | right_limited
616 { $number_set = $right_limited; }
617 | left_limited
618 { $number_set = $left_limited; }
619 | range_elems
620 { $number_set = $range_elems; }
621 | right_limited ',' range_elems
623 $number_set = number_set_prepend($right_limited, $range_elems);
624 if ($number_set == NUMBERSET_NOT_ASCENDING)
626 number_set_free($right_limited);
627 number_set_free($range_elems);
628 store_error_data(&@3, context, TA_Err_Control_Ranges_Not_Ascending);
629 YYABORT;
631 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
633 number_set_free($right_limited);
634 number_set_free($range_elems);
635 store_error_data(&@3, context, TA_Err_Control_Overlapping_Ranges);
636 YYABORT;
639 | range_elems ',' left_limited
641 $number_set = number_set_prepend($range_elems, $left_limited);
642 if ($number_set == NUMBERSET_NOT_ASCENDING)
644 number_set_free($range_elems);
645 number_set_free($left_limited);
646 store_error_data(&@3, context, TA_Err_Control_Ranges_Not_Ascending);
647 YYABORT;
649 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
651 number_set_free($range_elems);
652 number_set_free($left_limited);
653 store_error_data(&@3, context, TA_Err_Control_Overlapping_Ranges);
654 YYABORT;
659 unlimited:
662 $unlimited = number_set_new(context->number_set_min,
663 context->number_set_max,
664 context->number_set_min,
665 context->number_set_max);
666 /* range of `$unlimited' is always valid */
667 if ($unlimited == NUMBERSET_ALLOCATION_ERROR)
669 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
670 YYABORT;
675 right_limited:
676 '-' integer
678 $right_limited = number_set_new(context->number_set_min,
679 $integer,
680 context->number_set_min,
681 context->number_set_max);
682 if ($right_limited == NUMBERSET_INVALID_RANGE)
684 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
685 YYABORT;
687 if ($right_limited == NUMBERSET_ALLOCATION_ERROR)
689 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
690 YYABORT;
695 left_limited:
696 integer '-'
698 $left_limited = number_set_new($integer,
699 context->number_set_max,
700 context->number_set_min,
701 context->number_set_max);
702 if ($left_limited == NUMBERSET_INVALID_RANGE)
704 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
705 YYABORT;
707 if ($left_limited == NUMBERSET_ALLOCATION_ERROR)
709 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
710 YYABORT;
715 range_elems[result]:
716 range_elem
717 { $result = $range_elem; }
718 | range_elems[left] ',' range_elem
720 $result = number_set_prepend($left, $range_elem);
721 if ($result == NUMBERSET_NOT_ASCENDING)
723 number_set_free($left);
724 number_set_free($range_elem);
725 store_error_data(&@3, context, TA_Err_Control_Ranges_Not_Ascending);
726 YYABORT;
728 if ($result == NUMBERSET_OVERLAPPING_RANGES)
730 number_set_free($left);
731 number_set_free($range_elem);
732 store_error_data(&@3, context, TA_Err_Control_Overlapping_Ranges);
733 YYABORT;
738 range_elem:
739 integer
741 $range_elem = number_set_new($integer,
742 $integer,
743 context->number_set_min,
744 context->number_set_max);
745 if ($range_elem == NUMBERSET_INVALID_RANGE)
747 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
748 YYABORT;
750 if ($range_elem == NUMBERSET_ALLOCATION_ERROR)
752 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
753 YYABORT;
756 | range
757 { $range_elem = $range; }
760 range:
761 integer[left] '-' integer[right]
763 $range = number_set_new($left,
764 $right,
765 context->number_set_min,
766 context->number_set_max);
767 if ($range == NUMBERSET_INVALID_RANGE)
769 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
770 YYABORT;
772 if ($range == NUMBERSET_ALLOCATION_ERROR)
774 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
775 YYABORT;
780 glyph_idx_set:
783 FT_Face face = context->font->sfnts[context->font_idx].face;
786 context->number_set_min = 0;
787 context->number_set_max = face->num_glyphs - 1;
789 glyph_idx_range_elems
790 { $glyph_idx_set = $glyph_idx_range_elems; }
793 glyph_idx_range_elems[result]:
794 glyph_idx_range_elem
795 { $result = $glyph_idx_range_elem; }
796 | glyph_idx_range_elems[left] ',' glyph_idx_range_elem
798 /* for glyph_idx_set, ascending order is not enforced */
799 $result = number_set_insert($left, $glyph_idx_range_elem);
800 if ($result == NUMBERSET_OVERLAPPING_RANGES)
802 number_set_free($left);
803 number_set_free($glyph_idx_range_elem);
804 store_error_data(&@3, context, TA_Err_Control_Overlapping_Ranges);
805 YYABORT;
810 glyph_idx_range_elem:
811 glyph_idx
813 $glyph_idx_range_elem = number_set_new($glyph_idx,
814 $glyph_idx,
815 context->number_set_min,
816 context->number_set_max);
817 /* glyph_idx is always valid */
818 /* since its value was already tested for validity */
819 if ($glyph_idx_range_elem == NUMBERSET_ALLOCATION_ERROR)
821 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
822 YYABORT;
825 | glyph_idx_range
826 { $glyph_idx_range_elem = $glyph_idx_range; }
829 glyph_idx_range:
830 glyph_idx[left] '-' glyph_idx[right]
832 $glyph_idx_range = number_set_new($left,
833 $right,
834 context->number_set_min,
835 context->number_set_max);
836 /* glyph range is always valid */
837 /* since both `glyph_idx' values were already tested for validity */
838 if ($glyph_idx_range == NUMBERSET_ALLOCATION_ERROR)
840 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
841 YYABORT;
850 void
851 TA_control_error(YYLTYPE *locp,
852 Control_Context* context,
853 char const* msg)
855 /* if `error' is already set, we have a fatal flex error */
856 if (!context->error)
858 context->error = TA_Err_Control_Syntax_Error;
859 strncpy(context->errmsg, msg, sizeof (context->errmsg));
862 context->errline_num = locp->first_line;
863 context->errline_pos_left = locp->first_column;
864 context->errline_pos_right = locp->last_column;
868 void
869 store_error_data(const YYLTYPE *locp,
870 Control_Context* context,
871 TA_Error error)
873 context->error = error;
875 context->errline_num = locp->first_line;
876 context->errline_pos_left = locp->first_column;
877 context->errline_pos_right = locp->last_column;
879 context->errmsg[0] = '\0';
883 #if 0
886 * compile this test program with
888 * make libnumberset.la
890 * flex -d tacontrol.flex \
891 * && bison -t tacontrol.bison \
892 * && gcc -g3 -O0 \
893 * -Wall -W \
894 * -I.. \
895 * -I. \
896 * -I/usr/local/include/freetype2 \
897 * -I/usr/local/include/harfbuzz \
898 * -L.libs \
899 * -o tacontrol-bison \
900 * tacontrol-bison.c \
901 * tacontrol-flex.c \
902 * tacontrol.c \
903 * -lfreetype \
904 * -lnumberset
907 const char* input =
908 "# Test\n"
909 "\n"
910 "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
911 "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
912 "a p / 12 x 0.5 @ 23-25";
915 #undef scanner
918 main(int argc,
919 char** argv)
921 TA_Error error;
922 int bison_error;
923 int retval = 1;
925 Control_Context context;
926 FONT font;
927 SFNT sfnts[1];
928 FT_Library library;
929 FT_Face face;
930 const char* filename;
933 if (argc != 2)
935 fprintf(stderr, "need an outline font as an argument\n");
936 goto Exit0;
939 filename = argv[1];
941 error = FT_Init_FreeType(&library);
942 if (error)
944 fprintf(stderr, "error while initializing FreeType library (0x%X)\n",
945 error);
946 goto Exit0;
949 error = FT_New_Face(library, filename, 0, &face);
950 if (error)
952 fprintf(stderr, "error while loading font `%s' (0x%X)\n",
953 filename,
954 error);
955 goto Exit1;
958 /* we construct a minumum `Font' object */
959 sfnts[0].face = face;
961 font.num_sfnts = 1;
962 font.sfnts = sfnts;
963 font.control_buf = (char*)input;
964 font.control_len = strlen(input);
966 TA_control_debug = 1;
968 TA_control_scanner_init(&context, &font);
969 if (context.error)
970 goto Exit2;
972 bison_error = TA_control_parse(&context);
973 if (bison_error)
974 goto Exit3;
976 retval = 0;
978 Exit3:
979 TA_control_scanner_done(&context);
980 TA_control_free(context.result);
982 Exit2:
983 FT_Done_Face(face);
985 Exit1:
986 FT_Done_FreeType(library);
988 Exit0:
989 return retval;
992 #endif
994 /* end of tacontrol.bison */