[numberset] Add `wrap_range_prepend'.
[ttfautohint.git] / lib / tacontrol.bison
blob38d2ddb77a7822e888adacf5548b555c782d422c
1 /* tacontrol.bison */
3 /*
4 * Copyright (C) 2014-2017 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 @$.first_line);
188 if (!$entry)
190 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
191 YYABORT;
194 | font_idx glyph_idx left_right number_set EOE
196 $entry = TA_control_new($left_right,
197 $font_idx,
198 $glyph_idx,
199 $number_set,
202 NULL,
203 @$.first_line);
204 if (!$entry)
206 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
207 YYABORT;
210 | font_idx glyph_idx left_right number_set '(' offset[left] ',' offset[right] ')' EOE
212 $entry = TA_control_new($left_right,
213 $font_idx,
214 $glyph_idx,
215 $number_set,
216 $left,
217 $right,
218 NULL,
219 @$.first_line);
220 if (!$entry)
222 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
223 YYABORT;
226 | font_idx glyph_idx no_dir number_set EOE
228 $entry = TA_control_new($no_dir,
229 $font_idx,
230 $glyph_idx,
231 $number_set,
234 NULL,
235 @$.first_line);
236 if (!$entry)
238 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
239 YYABORT;
242 | font_idx script_feature glyph_idx_set EOE
244 $entry = TA_control_new(Control_Script_Feature,
245 $font_idx,
246 $script_feature,
247 $glyph_idx_set,
250 NULL,
251 @$.first_line);
252 if (!$entry)
254 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
255 YYABORT;
260 font_idx:
261 /* empty */
263 $font_idx = 0;
264 context->font_idx = $font_idx;
266 | integer
268 $font_idx = $integer;
269 if ($font_idx >= context->font->num_sfnts)
271 store_error_data(&@$, context, TA_Err_Control_Invalid_Font_Index);
272 YYABORT;
274 context->font_idx = $font_idx;
278 glyph_idx:
279 integer
281 FT_Face face = context->font->sfnts[context->font_idx].face;
284 $glyph_idx = $integer;
285 if ($glyph_idx >= face->num_glyphs)
287 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph_Index);
288 YYABORT;
290 context->glyph_idx = $glyph_idx;
292 | glyph_name
294 FT_Face face = context->font->sfnts[context->font_idx].face;
297 /* explicitly compare with `.notdef' */
298 /* since `FT_Get_Name_Index' returns glyph index 0 */
299 /* for both this glyph name and invalid ones */
300 if (!strcmp($glyph_name, ".notdef"))
301 $glyph_idx = 0;
302 else
304 $glyph_idx = (long)FT_Get_Name_Index(face, $glyph_name);
305 if ($glyph_idx == 0)
306 $glyph_idx = -1;
309 free($glyph_name);
311 if ($glyph_idx < 0)
313 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph_Name);
314 YYABORT;
316 context->glyph_idx = $glyph_idx;
320 glyph_name:
321 glyph_name_
323 /* `$glyph_name_' was allocated in the lexer */
324 if (context->error)
326 /* lexing error */
327 store_error_data(&@$, context, context->error);
328 YYABORT;
331 $glyph_name = $glyph_name_;
335 glyph_name_:
336 LEFT
337 | NAME
338 | NODIR
339 | POINT
340 | RIGHT
341 | TOUCH
342 | XSHIFT
343 | YSHIFT
346 point_touch:
347 point_touch_
349 FT_Error error;
350 FT_Face face = context->font->sfnts[context->font_idx].face;
351 int num_points;
354 error = FT_Load_Glyph(face,
355 (FT_UInt)context->glyph_idx,
356 FT_LOAD_NO_SCALE);
357 if (error)
359 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph);
360 YYABORT;
363 num_points = face->glyph->outline.n_points;
365 context->number_set_min = 0;
366 context->number_set_max = num_points - 1;
368 $point_touch = $point_touch_;
372 point_touch_:
373 POINT
375 $point_touch_ = Control_Delta_after_IUP;
376 free($POINT);
378 | TOUCH
380 $point_touch_ = Control_Delta_before_IUP;
381 free($TOUCH);
385 left_right:
386 left_right_
388 FT_Error error;
389 FT_Face face = context->font->sfnts[context->font_idx].face;
390 int num_points;
393 error = FT_Load_Glyph(face,
394 (FT_UInt)context->glyph_idx,
395 FT_LOAD_NO_SCALE);
396 if (error)
398 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph);
399 YYABORT;
402 num_points = face->glyph->outline.n_points;
404 context->number_set_min = 0;
405 context->number_set_max = num_points - 1;
407 $left_right = $left_right_;
411 left_right_:
412 LEFT
414 $left_right_ = Control_Single_Point_Segment_Left;
415 free($LEFT);
417 | RIGHT
419 $left_right_ = Control_Single_Point_Segment_Right;
420 free($RIGHT);
424 no_dir:
425 NODIR
427 FT_Error error;
428 FT_Face face = context->font->sfnts[context->font_idx].face;
429 int num_points;
432 error = FT_Load_Glyph(face,
433 (FT_UInt)context->glyph_idx,
434 FT_LOAD_NO_SCALE);
435 if (error)
437 store_error_data(&@$, context, TA_Err_Control_Invalid_Glyph);
438 YYABORT;
441 num_points = face->glyph->outline.n_points;
443 context->number_set_min = 0;
444 context->number_set_max = num_points - 1;
446 $no_dir = Control_Single_Point_Segment_None;
447 free($NODIR);
451 script_feature:
452 glyph_name[script] glyph_name[feature]
454 long ss;
455 size_t i, j;
456 size_t script_idx = 0;
457 size_t feature_idx = 0;
458 char feature_name[5];
461 for (i = 0; i < script_names_size; i++)
463 if (!strcmp($script, script_names[i]))
465 script_idx = i;
466 break;
470 feature_name[4] = '\0';
472 for (j = 0; j < feature_tags_size; j++)
474 hb_tag_to_string(feature_tags[j], feature_name);
476 if (!strcmp($feature, feature_name))
478 feature_idx = j;
479 break;
483 free($script);
484 free($feature);
486 if (i == script_names_size)
488 store_error_data(&@1, context, TA_Err_Control_Invalid_Script);
489 YYABORT;
491 if (j == feature_tags_size)
493 store_error_data(&@2, context, TA_Err_Control_Invalid_Feature);
494 YYABORT;
497 for (ss = 0; ta_style_classes[ss]; ss++)
499 TA_StyleClass style_class = ta_style_classes[ss];
502 if (script_idx == style_class->script
503 && feature_idx == style_class->coverage)
505 $script_feature = ss;
506 break;
509 if (!ta_style_classes[ss])
511 store_error_data(&@$, context, TA_Err_Control_Invalid_Style);
512 YYABORT;
518 x_shift:
519 /* empty */
520 { $x_shift = 0; }
521 | XSHIFT shift
523 $x_shift = $shift;
524 free($XSHIFT);
528 y_shift:
529 /* empty */
530 { $y_shift = 0; }
531 | YSHIFT shift
533 $y_shift = $shift;
534 free($YSHIFT);
538 shift:
539 real
541 if ($real < CONTROL_DELTA_SHIFT_MIN || $real > CONTROL_DELTA_SHIFT_MAX)
543 store_error_data(&@$, context, TA_Err_Control_Invalid_Shift);
544 YYABORT;
546 $shift = $real;
550 offset:
551 signed_integer
553 if ($signed_integer < SHRT_MIN || $signed_integer > SHRT_MAX)
555 store_error_data(&@$, context, TA_Err_Control_Invalid_Offset);
556 YYABORT;
558 $offset = $signed_integer;
562 ppem_set:
565 context->number_set_min = CONTROL_DELTA_PPEM_MIN;
566 context->number_set_max = CONTROL_DELTA_PPEM_MAX;
568 number_set
569 { $ppem_set = $number_set; }
572 integer:
574 { $integer = 0; }
575 | INTEGER
577 if (context->error)
579 /* lexing error */
580 store_error_data(&@$, context, context->error);
581 YYABORT;
584 $integer = $INTEGER;
588 signed_integer:
589 integer
590 { $signed_integer = $integer; }
591 | '+' integer
592 { $signed_integer = $integer; }
593 | '-' integer
594 { $signed_integer = -$integer; }
597 real:
598 signed_integer
599 { $real = $signed_integer; }
600 | REAL
602 if (context->error)
604 /* lexing error */
605 store_error_data(&@$, context, context->error);
606 YYABORT;
609 $real = $REAL;
611 | '+' REAL
612 { $real = $REAL; }
613 | '-' REAL
614 { $real = -$REAL; }
617 number_set:
618 unlimited
619 { $number_set = $unlimited; }
620 | right_limited
621 { $number_set = $right_limited; }
622 | left_limited
623 { $number_set = $left_limited; }
624 | range_elems
625 { $number_set = $range_elems; }
626 | right_limited ',' range_elems
628 $number_set = number_set_prepend($right_limited, $range_elems);
629 if ($number_set == NUMBERSET_NOT_ASCENDING)
631 number_set_free($right_limited);
632 number_set_free($range_elems);
633 store_error_data(&@3, context, TA_Err_Control_Ranges_Not_Ascending);
634 YYABORT;
636 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
638 number_set_free($right_limited);
639 number_set_free($range_elems);
640 store_error_data(&@3, context, TA_Err_Control_Overlapping_Ranges);
641 YYABORT;
644 | range_elems ',' left_limited
646 $number_set = number_set_prepend($range_elems, $left_limited);
647 if ($number_set == NUMBERSET_NOT_ASCENDING)
649 number_set_free($range_elems);
650 number_set_free($left_limited);
651 store_error_data(&@3, context, TA_Err_Control_Ranges_Not_Ascending);
652 YYABORT;
654 if ($number_set == NUMBERSET_OVERLAPPING_RANGES)
656 number_set_free($range_elems);
657 number_set_free($left_limited);
658 store_error_data(&@3, context, TA_Err_Control_Overlapping_Ranges);
659 YYABORT;
664 unlimited:
667 $unlimited = number_set_new(context->number_set_min,
668 context->number_set_max,
669 context->number_set_min,
670 context->number_set_max);
671 /* range of `$unlimited' is always valid */
672 if ($unlimited == NUMBERSET_ALLOCATION_ERROR)
674 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
675 YYABORT;
680 right_limited:
681 '-' integer
683 $right_limited = number_set_new(context->number_set_min,
684 (int)$integer,
685 context->number_set_min,
686 context->number_set_max);
687 if ($right_limited == NUMBERSET_INVALID_RANGE)
689 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
690 YYABORT;
692 if ($right_limited == NUMBERSET_ALLOCATION_ERROR)
694 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
695 YYABORT;
700 left_limited:
701 integer '-'
703 $left_limited = number_set_new((int)$integer,
704 context->number_set_max,
705 context->number_set_min,
706 context->number_set_max);
707 if ($left_limited == NUMBERSET_INVALID_RANGE)
709 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
710 YYABORT;
712 if ($left_limited == NUMBERSET_ALLOCATION_ERROR)
714 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
715 YYABORT;
720 range_elems[result]:
721 range_elem
722 { $result = $range_elem; }
723 | range_elems[left] ',' range_elem
725 $result = number_set_prepend($left, $range_elem);
726 if ($result == NUMBERSET_NOT_ASCENDING)
728 number_set_free($left);
729 number_set_free($range_elem);
730 store_error_data(&@3, context, TA_Err_Control_Ranges_Not_Ascending);
731 YYABORT;
733 if ($result == NUMBERSET_OVERLAPPING_RANGES)
735 number_set_free($left);
736 number_set_free($range_elem);
737 store_error_data(&@3, context, TA_Err_Control_Overlapping_Ranges);
738 YYABORT;
743 range_elem:
744 integer
746 $range_elem = number_set_new((int)$integer,
747 (int)$integer,
748 context->number_set_min,
749 context->number_set_max);
750 if ($range_elem == NUMBERSET_INVALID_RANGE)
752 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
753 YYABORT;
755 if ($range_elem == NUMBERSET_ALLOCATION_ERROR)
757 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
758 YYABORT;
761 | range
762 { $range_elem = $range; }
765 range:
766 integer[left] '-' integer[right]
768 $range = number_set_new((int)$left,
769 (int)$right,
770 context->number_set_min,
771 context->number_set_max);
772 if ($range == NUMBERSET_INVALID_RANGE)
774 store_error_data(&@$, context, TA_Err_Control_Invalid_Range);
775 YYABORT;
777 if ($range == NUMBERSET_ALLOCATION_ERROR)
779 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
780 YYABORT;
785 glyph_idx_set:
788 FT_Face face = context->font->sfnts[context->font_idx].face;
791 context->number_set_min = 0;
792 context->number_set_max = (int)(face->num_glyphs - 1);
794 glyph_idx_range_elems
795 { $glyph_idx_set = $glyph_idx_range_elems; }
798 glyph_idx_range_elems[result]:
799 glyph_idx_range_elem
800 { $result = $glyph_idx_range_elem; }
801 | glyph_idx_range_elems[left] ',' glyph_idx_range_elem
803 /* for glyph_idx_set, ascending order is not enforced */
804 $result = number_set_insert($left, $glyph_idx_range_elem);
805 if ($result == NUMBERSET_OVERLAPPING_RANGES)
807 number_set_free($left);
808 number_set_free($glyph_idx_range_elem);
809 store_error_data(&@3, context, TA_Err_Control_Overlapping_Ranges);
810 YYABORT;
815 glyph_idx_range_elem:
816 glyph_idx
818 $glyph_idx_range_elem = number_set_new((int)$glyph_idx,
819 (int)$glyph_idx,
820 context->number_set_min,
821 context->number_set_max);
822 /* glyph_idx is always valid */
823 /* since its value was already tested for validity */
824 if ($glyph_idx_range_elem == NUMBERSET_ALLOCATION_ERROR)
826 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
827 YYABORT;
830 | glyph_idx_range
831 { $glyph_idx_range_elem = $glyph_idx_range; }
834 glyph_idx_range:
835 glyph_idx[left] '-' glyph_idx[right]
837 $glyph_idx_range = number_set_new((int)$left,
838 (int)$right,
839 context->number_set_min,
840 context->number_set_max);
841 /* glyph range is always valid */
842 /* since both `glyph_idx' values were already tested for validity */
843 if ($glyph_idx_range == NUMBERSET_ALLOCATION_ERROR)
845 store_error_data(&@$, context, TA_Err_Control_Allocation_Error);
846 YYABORT;
855 void
856 TA_control_error(YYLTYPE *locp,
857 Control_Context* context,
858 char const* msg)
860 /* if `error' is already set, we have a fatal flex error */
861 if (!context->error)
863 context->error = TA_Err_Control_Syntax_Error;
864 strncpy(context->errmsg, msg, sizeof (context->errmsg));
867 context->errline_num = locp->first_line;
868 context->errline_pos_left = locp->first_column;
869 context->errline_pos_right = locp->last_column;
873 void
874 store_error_data(const YYLTYPE *locp,
875 Control_Context* context,
876 TA_Error error)
878 context->error = error;
880 context->errline_num = locp->first_line;
881 context->errline_pos_left = locp->first_column;
882 context->errline_pos_right = locp->last_column;
884 context->errmsg[0] = '\0';
888 #if 0
891 * compile this test program with
893 * make libnumberset.la
895 * flex -d tacontrol.flex \
896 * && bison -t tacontrol.bison \
897 * && gcc -g3 -O0 \
898 * -Wall -W \
899 * -I.. \
900 * -I. \
901 * -I/usr/local/include/freetype2 \
902 * -I/usr/local/include/harfbuzz \
903 * -L.libs \
904 * -o tacontrol-bison \
905 * tacontrol-bison.c \
906 * tacontrol-flex.c \
907 * tacontrol.c \
908 * -lfreetype \
909 * -lnumberset
912 const char* input =
913 "# Test\n"
914 "\n"
915 "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
916 "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
917 "a p / 12 x 0.5 @ 23-25";
920 #undef scanner
923 main(int argc,
924 char** argv)
926 TA_Error error;
927 int bison_error;
928 int retval = 1;
930 Control_Context context;
931 FONT font;
932 SFNT sfnts[1];
933 FT_Library library;
934 FT_Face face;
935 const char* filename;
938 if (argc != 2)
940 fprintf(stderr, "need an outline font as an argument\n");
941 goto Exit0;
944 filename = argv[1];
946 error = FT_Init_FreeType(&library);
947 if (error)
949 fprintf(stderr, "error while initializing FreeType library (0x%X)\n",
950 error);
951 goto Exit0;
954 error = FT_New_Face(library, filename, 0, &face);
955 if (error)
957 fprintf(stderr, "error while loading font `%s' (0x%X)\n",
958 filename,
959 error);
960 goto Exit1;
963 /* we construct a minumum `Font' object */
964 sfnts[0].face = face;
966 font.num_sfnts = 1;
967 font.sfnts = sfnts;
968 font.control_buf = (char*)input;
969 font.control_len = strlen(input);
971 TA_control_debug = 1;
973 TA_control_scanner_init(&context, &font);
974 if (context.error)
975 goto Exit2;
977 bison_error = TA_control_parse(&context);
978 if (bison_error)
979 goto Exit3;
981 retval = 0;
983 Exit3:
984 TA_control_scanner_done(&context);
985 TA_control_free(context.result);
987 Exit2:
988 FT_Done_Face(face);
990 Exit1:
991 FT_Done_FreeType(library);
993 Exit0:
994 return retval;
997 #endif
999 /* end of tacontrol.bison */