4 * Copyright (C) 2014-2022 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 */
38 %lex
-param
{ void* scanner
}
40 %name
-prefix
"TA_control_"
41 %parse
-param
{ Control_Context
* context
}
47 /* we don't change the name prefix of flex functions */
48 #define TA_control_lex yylex
63 #include "tacontrol-flex.h"
66 TA_control_error
(YYLTYPE *locp
,
67 Control_Context
* context
,
71 store_error_data
(const YYLTYPE *locp
,
72 Control_Context
* context
,
76 /* calls to `yylex' in the generated bison code use `scanner' directly */
77 #define scanner context->scanner
80 /* INVALID_CHARACTER and INTERNAL_FLEX_ERROR are flex errors */
82 %token
<integer
> INTEGER
"integer number"
83 %token INTERNAL_FLEX_ERROR
"internal flex error"
84 %token
<character
> INVALID_CHARACTER
"invalid character"
85 %token
<name
> LEFT
"left"
86 %token
<name
> NAME
"glyph name"
87 %token
<name
> NODIR
"no direction"
88 %token
<name
> POINT
"point"
89 %token
<real
> REAL
"real number"
90 %token
<name
> RIGHT
"right"
91 %token
<name
> TOUCH
"touch"
92 %token
<name
> WIDTH
"width"
93 %token
<name
> XSHIFT
"x shift"
94 %token
<name
> YSHIFT
"y shift"
97 %type
<integer
> font_idx
98 %type
<integer
> glyph_idx
99 %type
<range
> glyph_idx_range
100 %type
<range
> glyph_idx_range_elem
101 %type
<range
> glyph_idx_range_elems
102 %type
<range
> glyph_idx_set
103 %type
<name
> glyph_name
104 %type
<name
> glyph_name_
105 %type
<control
> input
106 %type
<integer
> integer
107 %type
<type
> left_right
108 %type
<type
> left_right_
109 %type
<type
> point_touch
110 %type
<type
> point_touch_
111 %type
<range
> left_limited
113 %type
<range
> number_set
114 %type
<integer
> offset
115 %type
<range
> ppem_set
117 %type
<range
> range_elem
118 %type
<range
> range_elems
120 %type
<range
> right_limited
121 %type
<integer
> script_feature
123 %type
<integer
> signed_integer
124 %type
<range
> unlimited
125 %type
<range
> width_elem
126 %type
<range
> width_elems
127 %type
<range
> width_set
128 %type
<integer
> wildcard_script_feature
132 %destructor
{ TA_control_free
($$
); } <control
>
133 %destructor
{ number_set_free
($$
); } <range
>
135 %printer
{ fprintf
(yyoutput
, "`%ld'", $$
); } <integer
>
136 %printer
{ fprintf
(yyoutput
, "`%s'", $$
); } <name
>
137 %printer
{ fprintf
(yyoutput
, "`%g'", $$
); } <real
>
143 nr
= number_set_reverse
($$
);
144 s
= number_set_show
(nr
, -1, -1);
145 (void)number_set_reverse
(nr
);
148 fprintf
(yyoutput
, "`%s'", s
);
152 fprintf
(yyoutput
, "allocation error");
154 %printer
{ fprintf
(yyoutput
, "`%c'", $$
); } INVALID_CHARACTER
160 /* `number_range' list elements are stored in reversed order; */
161 /* the call to `TA_control_new' fixes this */
163 /* `Control' list elements are stored in reversed order, too; */
164 /* this gets fixed by an explicit call to `TA_control_reverse' */
168 { context
->result
= TA_control_reverse
($input); }
175 { $result = TA_control_prepend
($left, $entry); }
181 | font_idx glyph_idx point_touch number_set x_shift y_shift ppem_set EOE
183 $entry = TA_control_new
($point_touch,
193 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
197 | font_idx glyph_idx left_right number_set EOE
199 $entry = TA_control_new
($left_right,
209 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
213 | font_idx glyph_idx left_right number_set
'(' offset
[left
] ',' offset
[right
] ')' EOE
215 $entry = TA_control_new
($left_right,
225 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
229 | font_idx glyph_idx no_dir number_set EOE
231 $entry = TA_control_new
($no_dir,
241 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
245 | font_idx script_feature glyph_idx_set EOE
247 $entry = TA_control_new
(Control_Script_Feature_Glyphs
,
257 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
261 | font_idx wildcard_script_feature width_set EOE
263 $entry = TA_control_new
(Control_Script_Feature_Widths
,
265 $wildcard_script_feature,
273 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
283 context
->font_idx
= $font_idx;
287 $font_idx = $integer;
288 if
($font_idx >= context
->font
->num_sfnts
)
290 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Font_Index
);
293 context
->font_idx
= $font_idx;
300 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
303 $glyph_idx = $integer;
304 if
($glyph_idx >= face
->num_glyphs
)
306 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph_Index
);
309 context
->glyph_idx
= $glyph_idx;
313 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
316 /* explicitly compare with `.notdef' */
317 /* since `FT_Get_Name_Index' returns glyph index 0 */
318 /* for both this glyph name and invalid ones */
319 if
(!strcmp
($glyph_name, ".notdef"))
323 $glyph_idx = (long)FT_Get_Name_Index
(face
, $glyph_name);
332 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph_Name
);
335 context
->glyph_idx
= $glyph_idx;
342 /* `$glyph_name_' was allocated in the lexer */
346 store_error_data
(&@$
, context
, context
->error);
350 $glyph_name = $glyph_name_;
370 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
374 error = FT_Load_Glyph
(face
,
375 (FT_UInt
)context
->glyph_idx
,
379 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
383 num_points
= face
->glyph
->outline.n_points
;
385 context
->number_set_min
= 0;
386 context
->number_set_max
= num_points
- 1;
388 $point_touch = $point_touch_;
395 $point_touch_ = Control_Delta_after_IUP
;
400 $point_touch_ = Control_Delta_before_IUP
;
409 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
413 error = FT_Load_Glyph
(face
,
414 (FT_UInt
)context
->glyph_idx
,
418 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
422 num_points
= face
->glyph
->outline.n_points
;
424 context
->number_set_min
= 0;
425 context
->number_set_max
= num_points
- 1;
427 $left_right = $left_right_;
434 $left_right_ = Control_Single_Point_Segment_Left
;
439 $left_right_ = Control_Single_Point_Segment_Right
;
448 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
452 error = FT_Load_Glyph
(face
,
453 (FT_UInt
)context
->glyph_idx
,
457 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
461 num_points
= face
->glyph
->outline.n_points
;
463 context
->number_set_min
= 0;
464 context
->number_set_max
= num_points
- 1;
466 $no_dir = Control_Single_Point_Segment_None
;
471 wildcard_script_feature:
473 { $wildcard_script_feature = $script_feature; }
474 |
'*' glyph_name
[feature
]
477 size_t feature_idx
= 0;
478 char feature_name
[5];
481 feature_name
[4] = '\0';
483 for
(i
= 0; i
< feature_tags_size
; i
++)
485 hb_tag_to_string
(feature_tags
[i
], feature_name
);
487 if
(!strcmp
($feature, feature_name
))
496 if
(i
== feature_tags_size
)
498 store_error_data
(&@
2, context
, TA_Err_Control_Invalid_Feature
);
502 /* simply use negative values for features applied to all scripts */
503 $wildcard_script_feature = -(long)feature_idx
;
507 glyph_name
[script
] glyph_name
[feature
]
511 size_t script_idx
= 0;
512 size_t feature_idx
= 0;
513 char feature_name
[5];
516 for
(i
= 0; i
< script_names_size
; i
++)
518 if
(!strcmp
($script, script_names
[i
]))
525 feature_name
[4] = '\0';
527 for
(j
= 0; j
< feature_tags_size
; j
++)
529 hb_tag_to_string
(feature_tags
[j
], feature_name
);
531 if
(!strcmp
($feature, feature_name
))
541 if
(i
== script_names_size
)
543 store_error_data
(&@
1, context
, TA_Err_Control_Invalid_Script
);
546 if
(j
== feature_tags_size
)
548 store_error_data
(&@
2, context
, TA_Err_Control_Invalid_Feature
);
552 for
(ss
= 0; ta_style_classes
[ss
]; ss
++)
554 TA_StyleClass style_class
= ta_style_classes
[ss
];
557 if
(script_idx
== style_class
->script
558 && feature_idx
== style_class
->coverage
)
560 $script_feature = ss
;
564 if
(!ta_style_classes
[ss
])
566 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Style
);
595 if
($real < CONTROL_DELTA_SHIFT_MIN ||
$real > CONTROL_DELTA_SHIFT_MAX
)
597 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Shift
);
607 if
($signed_integer < SHRT_MIN ||
$signed_integer > SHRT_MAX
)
609 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Offset
);
612 $offset = $signed_integer;
619 context
->number_set_min
= CONTROL_DELTA_PPEM_MIN
;
620 context
->number_set_max
= CONTROL_DELTA_PPEM_MAX
;
623 { $ppem_set = $number_set; }
634 store_error_data
(&@$
, context
, context
->error);
644 { $signed_integer = $integer; }
646 { $signed_integer = $integer; }
648 { $signed_integer = -$integer; }
653 { $real = $signed_integer; }
659 store_error_data
(&@$
, context
, context
->error);
673 { $number_set = $unlimited; }
675 { $number_set = $right_limited; }
677 { $number_set = $left_limited; }
679 { $number_set = $range_elems; }
680 | right_limited
',' range_elems
682 $number_set = number_set_prepend
($right_limited, $range_elems);
683 if
($number_set == NUMBERSET_NOT_ASCENDING
)
685 number_set_free
($right_limited);
686 number_set_free
($range_elems);
687 store_error_data
(&@
3, context
, TA_Err_Control_Ranges_Not_Ascending
);
690 if
($number_set == NUMBERSET_OVERLAPPING_RANGES
)
692 number_set_free
($right_limited);
693 number_set_free
($range_elems);
694 store_error_data
(&@
3, context
, TA_Err_Control_Overlapping_Ranges
);
698 | range_elems
',' left_limited
700 $number_set = number_set_prepend
($range_elems, $left_limited);
701 if
($number_set == NUMBERSET_NOT_ASCENDING
)
703 number_set_free
($range_elems);
704 number_set_free
($left_limited);
705 store_error_data
(&@
3, context
, TA_Err_Control_Ranges_Not_Ascending
);
708 if
($number_set == NUMBERSET_OVERLAPPING_RANGES
)
710 number_set_free
($range_elems);
711 number_set_free
($left_limited);
712 store_error_data
(&@
3, context
, TA_Err_Control_Overlapping_Ranges
);
721 $unlimited = number_set_new
(context
->number_set_min
,
722 context
->number_set_max
,
723 context
->number_set_min
,
724 context
->number_set_max
);
725 /* range of `$unlimited' is always valid */
726 if
($unlimited == NUMBERSET_ALLOCATION_ERROR
)
728 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
737 $right_limited = number_set_new
(context
->number_set_min
,
739 context
->number_set_min
,
740 context
->number_set_max
);
741 if
($right_limited == NUMBERSET_INVALID_RANGE
)
743 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
746 if
($right_limited == NUMBERSET_ALLOCATION_ERROR
)
748 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
757 $left_limited = number_set_new
((int)$integer,
758 context
->number_set_max
,
759 context
->number_set_min
,
760 context
->number_set_max
);
761 if
($left_limited == NUMBERSET_INVALID_RANGE
)
763 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
766 if
($left_limited == NUMBERSET_ALLOCATION_ERROR
)
768 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
776 { $result = $range_elem; }
777 | range_elems
[left
] ',' range_elem
779 $result = number_set_prepend
($left, $range_elem);
780 if
($result == NUMBERSET_NOT_ASCENDING
)
782 number_set_free
($left);
783 number_set_free
($range_elem);
784 store_error_data
(&@
3, context
, TA_Err_Control_Ranges_Not_Ascending
);
787 if
($result == NUMBERSET_OVERLAPPING_RANGES
)
789 number_set_free
($left);
790 number_set_free
($range_elem);
791 store_error_data
(&@
3, context
, TA_Err_Control_Overlapping_Ranges
);
800 $range_elem = number_set_new
((int)$integer,
802 context
->number_set_min
,
803 context
->number_set_max
);
804 if
($range_elem == NUMBERSET_INVALID_RANGE
)
806 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
809 if
($range_elem == NUMBERSET_ALLOCATION_ERROR
)
811 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
816 { $range_elem = $range; }
820 integer
[left
] '-' integer
[right
]
822 $range = number_set_new
((int)$left,
824 context
->number_set_min
,
825 context
->number_set_max
);
826 if
($range == NUMBERSET_INVALID_RANGE
)
828 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
831 if
($range == NUMBERSET_ALLOCATION_ERROR
)
833 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
842 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
845 context
->number_set_min
= 0;
846 context
->number_set_max
= (int)(face
->num_glyphs
- 1);
848 glyph_idx_range_elems
849 { $glyph_idx_set = $glyph_idx_range_elems; }
852 glyph_idx_range_elems
[result
]:
854 { $result = $glyph_idx_range_elem; }
855 | glyph_idx_range_elems
[left
] ',' glyph_idx_range_elem
857 /* for glyph_idx_set, ascending order is not enforced */
858 $result = number_set_insert
($left, $glyph_idx_range_elem);
859 if
($result == NUMBERSET_OVERLAPPING_RANGES
)
861 number_set_free
($left);
862 number_set_free
($glyph_idx_range_elem);
863 store_error_data
(&@
3, context
, TA_Err_Control_Overlapping_Ranges
);
869 glyph_idx_range_elem:
872 $glyph_idx_range_elem = number_set_new
((int)$glyph_idx,
874 context
->number_set_min
,
875 context
->number_set_max
);
876 /* glyph_idx is always valid */
877 /* since its value was already tested for validity */
878 if
($glyph_idx_range_elem == NUMBERSET_ALLOCATION_ERROR
)
880 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
885 { $glyph_idx_range_elem = $glyph_idx_range; }
889 glyph_idx
[left
] '-' glyph_idx
[right
]
891 $glyph_idx_range = number_set_new
((int)$left,
893 context
->number_set_min
,
894 context
->number_set_max
);
895 /* glyph range is always valid */
896 /* since both `glyph_idx' values were already tested for validity */
897 if
($glyph_idx_range == NUMBERSET_ALLOCATION_ERROR
)
899 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
908 context
->number_set_min
= 1;
909 context
->number_set_max
= 65535;
910 context
->number_set_num_elems
= 0;
913 { $width_set = $width_elems; }
919 context
->number_set_num_elems
++;
920 $result = $width_elem;
922 | width_elems
[left
] ',' width_elem
924 context
->number_set_num_elems
++;
925 if
(context
->number_set_num_elems
> TA_LATIN_MAX_WIDTHS
)
927 number_set_free
($left);
928 number_set_free
($width_elem);
929 store_error_data
(&@
3, context
, TA_Err_Control_Too_Much_Widths
);
933 /* for width_set, the order of entries is preserved */
934 $result = number_set_prepend_unsorted
($left, $width_elem);
941 $width_elem = number_set_new
($integer,
943 context
->number_set_min
,
944 context
->number_set_max
);
945 if
($width_elem == NUMBERSET_ALLOCATION_ERROR
)
947 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
958 TA_control_error
(YYLTYPE *locp
,
959 Control_Context
* context
,
962 /* if `error' is already set, we have a fatal flex error */
965 context
->error = TA_Err_Control_Syntax_Error
;
966 strncpy
(context
->errmsg
, msg
, sizeof
(context
->errmsg
));
969 context
->errline_num
= locp
->first_line
;
970 context
->errline_pos_left
= locp
->first_column
;
971 context
->errline_pos_right
= locp
->last_column
;
976 store_error_data
(const YYLTYPE *locp
,
977 Control_Context
* context
,
980 context
->error = error;
982 context
->errline_num
= locp
->first_line
;
983 context
->errline_pos_left
= locp
->first_column
;
984 context
->errline_pos_right
= locp
->last_column
;
986 context
->errmsg
[0] = '\0';
993 * compile this test program with
995 * make libnumberset.la
997 * flex -d tacontrol.flex \
998 * && bison -t tacontrol.bison \
1003 * -I/usr/local/include/freetype2 \
1004 * -I/usr/local/include/harfbuzz \
1006 * -o tacontrol-bison \
1007 * tacontrol-bison.c \
1008 * tacontrol-flex.c \
1017 "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
1018 "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
1019 "a p / 12 x 0.5 @ 23-25";
1032 Control_Context context
;
1037 const char* filename
;
1042 fprintf
(stderr
, "need an outline font as an argument\n");
1048 error = FT_Init_FreeType
(&library
);
1051 fprintf
(stderr
, "error while initializing FreeType library (0x%X)\n",
1056 error = FT_New_Face
(library
, filename
, 0, &face
);
1059 fprintf
(stderr
, "error while loading font `%s' (0x%X)\n",
1065 /* we construct a minumum `Font' object */
1066 sfnts
[0].face
= face
;
1070 font.control_buf
= (char*)input
;
1071 font.control_len
= strlen
(input
);
1073 TA_control_debug
= 1;
1075 TA_control_scanner_init
(&context
, &font
);
1079 bison_error
= TA_control_parse
(&context
);
1086 TA_control_scanner_done
(&context
);
1087 TA_control_free
(context.result
);
1093 FT_Done_FreeType
(library
);
1101 /* end of tacontrol.bison */