4 * Copyright (C) 2014-2019 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
27 * Edsko de Vries's article `Writing a Reentrant Parser with Flex and Bison'
28 * (https://web.archive.org/web/20160130155657/http://www.phpcompiler.org:80/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"
41 %lex
-param
{ void* scanner
}
43 %name
-prefix
"TA_control_"
44 %parse
-param
{ Control_Context
* context
}
50 /* we don't change the name prefix of flex functions */
51 #define TA_control_lex yylex
66 #include "tacontrol-flex.h"
69 TA_control_error
(YYLTYPE *locp
,
70 Control_Context
* context
,
74 store_error_data
(const YYLTYPE *locp
,
75 Control_Context
* context
,
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 */
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
> WIDTH
"width"
96 %token
<name
> XSHIFT
"x shift"
97 %token
<name
> YSHIFT
"y shift"
100 %type
<integer
> font_idx
101 %type
<integer
> glyph_idx
102 %type
<range
> glyph_idx_range
103 %type
<range
> glyph_idx_range_elem
104 %type
<range
> glyph_idx_range_elems
105 %type
<range
> glyph_idx_set
106 %type
<name
> glyph_name
107 %type
<name
> glyph_name_
108 %type
<control
> input
109 %type
<integer
> integer
110 %type
<type
> left_right
111 %type
<type
> left_right_
112 %type
<type
> point_touch
113 %type
<type
> point_touch_
114 %type
<range
> left_limited
116 %type
<range
> number_set
117 %type
<integer
> offset
118 %type
<range
> ppem_set
120 %type
<range
> range_elem
121 %type
<range
> range_elems
123 %type
<range
> right_limited
124 %type
<integer
> script_feature
126 %type
<integer
> signed_integer
127 %type
<range
> unlimited
128 %type
<range
> width_elem
129 %type
<range
> width_elems
130 %type
<range
> width_set
131 %type
<integer
> wildcard_script_feature
135 %destructor
{ TA_control_free
($$
); } <control
>
136 %destructor
{ number_set_free
($$
); } <range
>
138 %printer
{ fprintf
(yyoutput
, "`%ld'", $$
); } <integer
>
139 %printer
{ fprintf
(yyoutput
, "`%s'", $$
); } <name
>
140 %printer
{ fprintf
(yyoutput
, "`%g'", $$
); } <real
>
146 nr
= number_set_reverse
($$
);
147 s
= number_set_show
(nr
, -1, -1);
148 (void)number_set_reverse
(nr
);
151 fprintf
(yyoutput
, "`%s'", s
);
155 fprintf
(yyoutput
, "allocation error");
157 %printer
{ fprintf
(yyoutput
, "`%c'", $$
); } INVALID_CHARACTER
163 /* `number_range' list elements are stored in reversed order; */
164 /* the call to `TA_control_new' fixes this */
166 /* `Control' list elements are stored in reversed order, too; */
167 /* this gets fixed by an explicit call to `TA_control_reverse' */
171 { context
->result
= TA_control_reverse
($input); }
178 { $result = TA_control_prepend
($left, $entry); }
184 | font_idx glyph_idx point_touch number_set x_shift y_shift ppem_set EOE
186 $entry = TA_control_new
($point_touch,
196 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
200 | font_idx glyph_idx left_right number_set EOE
202 $entry = TA_control_new
($left_right,
212 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
216 | font_idx glyph_idx left_right number_set
'(' offset
[left
] ',' offset
[right
] ')' EOE
218 $entry = TA_control_new
($left_right,
228 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
232 | font_idx glyph_idx no_dir number_set EOE
234 $entry = TA_control_new
($no_dir,
244 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
248 | font_idx script_feature glyph_idx_set EOE
250 $entry = TA_control_new
(Control_Script_Feature_Glyphs
,
260 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
264 | font_idx wildcard_script_feature width_set EOE
266 $entry = TA_control_new
(Control_Script_Feature_Widths
,
268 $wildcard_script_feature,
276 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
286 context
->font_idx
= $font_idx;
290 $font_idx = $integer;
291 if
($font_idx >= context
->font
->num_sfnts
)
293 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Font_Index
);
296 context
->font_idx
= $font_idx;
303 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
306 $glyph_idx = $integer;
307 if
($glyph_idx >= face
->num_glyphs
)
309 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph_Index
);
312 context
->glyph_idx
= $glyph_idx;
316 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
319 /* explicitly compare with `.notdef' */
320 /* since `FT_Get_Name_Index' returns glyph index 0 */
321 /* for both this glyph name and invalid ones */
322 if
(!strcmp
($glyph_name, ".notdef"))
326 $glyph_idx = (long)FT_Get_Name_Index
(face
, $glyph_name);
335 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph_Name
);
338 context
->glyph_idx
= $glyph_idx;
345 /* `$glyph_name_' was allocated in the lexer */
349 store_error_data
(&@$
, context
, context
->error);
353 $glyph_name = $glyph_name_;
373 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
377 error = FT_Load_Glyph
(face
,
378 (FT_UInt
)context
->glyph_idx
,
382 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
386 num_points
= face
->glyph
->outline.n_points
;
388 context
->number_set_min
= 0;
389 context
->number_set_max
= num_points
- 1;
391 $point_touch = $point_touch_;
398 $point_touch_ = Control_Delta_after_IUP
;
403 $point_touch_ = Control_Delta_before_IUP
;
412 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
416 error = FT_Load_Glyph
(face
,
417 (FT_UInt
)context
->glyph_idx
,
421 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
425 num_points
= face
->glyph
->outline.n_points
;
427 context
->number_set_min
= 0;
428 context
->number_set_max
= num_points
- 1;
430 $left_right = $left_right_;
437 $left_right_ = Control_Single_Point_Segment_Left
;
442 $left_right_ = Control_Single_Point_Segment_Right
;
451 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
455 error = FT_Load_Glyph
(face
,
456 (FT_UInt
)context
->glyph_idx
,
460 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
464 num_points
= face
->glyph
->outline.n_points
;
466 context
->number_set_min
= 0;
467 context
->number_set_max
= num_points
- 1;
469 $no_dir = Control_Single_Point_Segment_None
;
474 wildcard_script_feature:
476 { $wildcard_script_feature = $script_feature; }
477 |
'*' glyph_name
[feature
]
480 size_t feature_idx
= 0;
481 char feature_name
[5];
484 feature_name
[4] = '\0';
486 for
(i
= 0; i
< feature_tags_size
; i
++)
488 hb_tag_to_string
(feature_tags
[i
], feature_name
);
490 if
(!strcmp
($feature, feature_name
))
499 if
(i
== feature_tags_size
)
501 store_error_data
(&@
2, context
, TA_Err_Control_Invalid_Feature
);
505 /* simply use negative values for features applied to all scripts */
506 $wildcard_script_feature = -(long)feature_idx
;
510 glyph_name
[script
] glyph_name
[feature
]
514 size_t script_idx
= 0;
515 size_t feature_idx
= 0;
516 char feature_name
[5];
519 for
(i
= 0; i
< script_names_size
; i
++)
521 if
(!strcmp
($script, script_names
[i
]))
528 feature_name
[4] = '\0';
530 for
(j
= 0; j
< feature_tags_size
; j
++)
532 hb_tag_to_string
(feature_tags
[j
], feature_name
);
534 if
(!strcmp
($feature, feature_name
))
544 if
(i
== script_names_size
)
546 store_error_data
(&@
1, context
, TA_Err_Control_Invalid_Script
);
549 if
(j
== feature_tags_size
)
551 store_error_data
(&@
2, context
, TA_Err_Control_Invalid_Feature
);
555 for
(ss
= 0; ta_style_classes
[ss
]; ss
++)
557 TA_StyleClass style_class
= ta_style_classes
[ss
];
560 if
(script_idx
== style_class
->script
561 && feature_idx
== style_class
->coverage
)
563 $script_feature = ss
;
567 if
(!ta_style_classes
[ss
])
569 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Style
);
598 if
($real < CONTROL_DELTA_SHIFT_MIN ||
$real > CONTROL_DELTA_SHIFT_MAX
)
600 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Shift
);
610 if
($signed_integer < SHRT_MIN ||
$signed_integer > SHRT_MAX
)
612 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Offset
);
615 $offset = $signed_integer;
622 context
->number_set_min
= CONTROL_DELTA_PPEM_MIN
;
623 context
->number_set_max
= CONTROL_DELTA_PPEM_MAX
;
626 { $ppem_set = $number_set; }
637 store_error_data
(&@$
, context
, context
->error);
647 { $signed_integer = $integer; }
649 { $signed_integer = $integer; }
651 { $signed_integer = -$integer; }
656 { $real = $signed_integer; }
662 store_error_data
(&@$
, context
, context
->error);
676 { $number_set = $unlimited; }
678 { $number_set = $right_limited; }
680 { $number_set = $left_limited; }
682 { $number_set = $range_elems; }
683 | right_limited
',' range_elems
685 $number_set = number_set_prepend
($right_limited, $range_elems);
686 if
($number_set == NUMBERSET_NOT_ASCENDING
)
688 number_set_free
($right_limited);
689 number_set_free
($range_elems);
690 store_error_data
(&@
3, context
, TA_Err_Control_Ranges_Not_Ascending
);
693 if
($number_set == NUMBERSET_OVERLAPPING_RANGES
)
695 number_set_free
($right_limited);
696 number_set_free
($range_elems);
697 store_error_data
(&@
3, context
, TA_Err_Control_Overlapping_Ranges
);
701 | range_elems
',' left_limited
703 $number_set = number_set_prepend
($range_elems, $left_limited);
704 if
($number_set == NUMBERSET_NOT_ASCENDING
)
706 number_set_free
($range_elems);
707 number_set_free
($left_limited);
708 store_error_data
(&@
3, context
, TA_Err_Control_Ranges_Not_Ascending
);
711 if
($number_set == NUMBERSET_OVERLAPPING_RANGES
)
713 number_set_free
($range_elems);
714 number_set_free
($left_limited);
715 store_error_data
(&@
3, context
, TA_Err_Control_Overlapping_Ranges
);
724 $unlimited = number_set_new
(context
->number_set_min
,
725 context
->number_set_max
,
726 context
->number_set_min
,
727 context
->number_set_max
);
728 /* range of `$unlimited' is always valid */
729 if
($unlimited == NUMBERSET_ALLOCATION_ERROR
)
731 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
740 $right_limited = number_set_new
(context
->number_set_min
,
742 context
->number_set_min
,
743 context
->number_set_max
);
744 if
($right_limited == NUMBERSET_INVALID_RANGE
)
746 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
749 if
($right_limited == NUMBERSET_ALLOCATION_ERROR
)
751 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
760 $left_limited = number_set_new
((int)$integer,
761 context
->number_set_max
,
762 context
->number_set_min
,
763 context
->number_set_max
);
764 if
($left_limited == NUMBERSET_INVALID_RANGE
)
766 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
769 if
($left_limited == NUMBERSET_ALLOCATION_ERROR
)
771 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
779 { $result = $range_elem; }
780 | range_elems
[left
] ',' range_elem
782 $result = number_set_prepend
($left, $range_elem);
783 if
($result == NUMBERSET_NOT_ASCENDING
)
785 number_set_free
($left);
786 number_set_free
($range_elem);
787 store_error_data
(&@
3, context
, TA_Err_Control_Ranges_Not_Ascending
);
790 if
($result == NUMBERSET_OVERLAPPING_RANGES
)
792 number_set_free
($left);
793 number_set_free
($range_elem);
794 store_error_data
(&@
3, context
, TA_Err_Control_Overlapping_Ranges
);
803 $range_elem = number_set_new
((int)$integer,
805 context
->number_set_min
,
806 context
->number_set_max
);
807 if
($range_elem == NUMBERSET_INVALID_RANGE
)
809 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
812 if
($range_elem == NUMBERSET_ALLOCATION_ERROR
)
814 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
819 { $range_elem = $range; }
823 integer
[left
] '-' integer
[right
]
825 $range = number_set_new
((int)$left,
827 context
->number_set_min
,
828 context
->number_set_max
);
829 if
($range == NUMBERSET_INVALID_RANGE
)
831 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
834 if
($range == NUMBERSET_ALLOCATION_ERROR
)
836 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
845 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
848 context
->number_set_min
= 0;
849 context
->number_set_max
= (int)(face
->num_glyphs
- 1);
851 glyph_idx_range_elems
852 { $glyph_idx_set = $glyph_idx_range_elems; }
855 glyph_idx_range_elems
[result
]:
857 { $result = $glyph_idx_range_elem; }
858 | glyph_idx_range_elems
[left
] ',' glyph_idx_range_elem
860 /* for glyph_idx_set, ascending order is not enforced */
861 $result = number_set_insert
($left, $glyph_idx_range_elem);
862 if
($result == NUMBERSET_OVERLAPPING_RANGES
)
864 number_set_free
($left);
865 number_set_free
($glyph_idx_range_elem);
866 store_error_data
(&@
3, context
, TA_Err_Control_Overlapping_Ranges
);
872 glyph_idx_range_elem:
875 $glyph_idx_range_elem = number_set_new
((int)$glyph_idx,
877 context
->number_set_min
,
878 context
->number_set_max
);
879 /* glyph_idx is always valid */
880 /* since its value was already tested for validity */
881 if
($glyph_idx_range_elem == NUMBERSET_ALLOCATION_ERROR
)
883 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
888 { $glyph_idx_range_elem = $glyph_idx_range; }
892 glyph_idx
[left
] '-' glyph_idx
[right
]
894 $glyph_idx_range = number_set_new
((int)$left,
896 context
->number_set_min
,
897 context
->number_set_max
);
898 /* glyph range is always valid */
899 /* since both `glyph_idx' values were already tested for validity */
900 if
($glyph_idx_range == NUMBERSET_ALLOCATION_ERROR
)
902 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
911 context
->number_set_min
= 1;
912 context
->number_set_max
= 65535;
913 context
->number_set_num_elems
= 0;
916 { $width_set = $width_elems; }
922 context
->number_set_num_elems
++;
923 $result = $width_elem;
925 | width_elems
[left
] ',' width_elem
927 context
->number_set_num_elems
++;
928 if
(context
->number_set_num_elems
> TA_LATIN_MAX_WIDTHS
)
930 number_set_free
($left);
931 number_set_free
($width_elem);
932 store_error_data
(&@
3, context
, TA_Err_Control_Too_Much_Widths
);
936 /* for width_set, the order of entries is preserved */
937 $result = number_set_prepend_unsorted
($left, $width_elem);
944 $width_elem = number_set_new
($integer,
946 context
->number_set_min
,
947 context
->number_set_max
);
948 if
($width_elem == NUMBERSET_ALLOCATION_ERROR
)
950 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
961 TA_control_error
(YYLTYPE *locp
,
962 Control_Context
* context
,
965 /* if `error' is already set, we have a fatal flex error */
968 context
->error = TA_Err_Control_Syntax_Error
;
969 strncpy
(context
->errmsg
, msg
, sizeof
(context
->errmsg
));
972 context
->errline_num
= locp
->first_line
;
973 context
->errline_pos_left
= locp
->first_column
;
974 context
->errline_pos_right
= locp
->last_column
;
979 store_error_data
(const YYLTYPE *locp
,
980 Control_Context
* context
,
983 context
->error = error;
985 context
->errline_num
= locp
->first_line
;
986 context
->errline_pos_left
= locp
->first_column
;
987 context
->errline_pos_right
= locp
->last_column
;
989 context
->errmsg
[0] = '\0';
996 * compile this test program with
998 * make libnumberset.la
1000 * flex -d tacontrol.flex \
1001 * && bison -t tacontrol.bison \
1006 * -I/usr/local/include/freetype2 \
1007 * -I/usr/local/include/harfbuzz \
1009 * -o tacontrol-bison \
1010 * tacontrol-bison.c \
1011 * tacontrol-flex.c \
1020 "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
1021 "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
1022 "a p / 12 x 0.5 @ 23-25";
1035 Control_Context context
;
1040 const char* filename
;
1045 fprintf
(stderr
, "need an outline font as an argument\n");
1051 error = FT_Init_FreeType
(&library
);
1054 fprintf
(stderr
, "error while initializing FreeType library (0x%X)\n",
1059 error = FT_New_Face
(library
, filename
, 0, &face
);
1062 fprintf
(stderr
, "error while loading font `%s' (0x%X)\n",
1068 /* we construct a minumum `Font' object */
1069 sfnts
[0].face
= face
;
1073 font.control_buf
= (char*)input
;
1074 font.control_len
= strlen
(input
);
1076 TA_control_debug
= 1;
1078 TA_control_scanner_init
(&context
, &font
);
1082 bison_error
= TA_control_parse
(&context
);
1089 TA_control_scanner_done
(&context
);
1090 TA_control_free
(context.result
);
1096 FT_Done_FreeType
(library
);
1104 /* end of tacontrol.bison */