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
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"
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
> XSHIFT
"x shift"
96 %token
<name
> YSHIFT
"y shift"
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
115 %type
<range
> number_set
116 %type
<integer
> offset
117 %type
<range
> ppem_set
119 %type
<range
> range_elem
120 %type
<range
> range_elems
122 %type
<range
> right_limited
123 %type
<integer
> script_feature
125 %type
<integer
> signed_integer
126 %type
<range
> unlimited
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
>
141 nr
= number_set_reverse
($$
);
142 s
= number_set_show
(nr
, -1, -1);
143 (void)number_set_reverse
(nr
);
146 fprintf
(yyoutput
, "`%s'", s
);
150 fprintf
(yyoutput
, "allocation error");
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' */
165 { context
->result
= TA_control_reverse
($input); }
172 { $result = TA_control_prepend
($left, $entry); }
178 | font_idx glyph_idx point_touch number_set x_shift y_shift ppem_set EOE
180 $entry = TA_control_new
($point_touch,
189 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
193 | font_idx glyph_idx left_right number_set EOE
195 $entry = TA_control_new
($left_right,
204 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
208 | font_idx glyph_idx left_right number_set
'(' offset
[left
] ',' offset
[right
] ')' EOE
210 $entry = TA_control_new
($left_right,
219 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
223 | font_idx glyph_idx no_dir number_set EOE
225 $entry = TA_control_new
($no_dir,
234 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
238 | font_idx script_feature glyph_idx_set EOE
240 $entry = TA_control_new
(Control_Script_Feature
,
249 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
259 context
->font_idx
= $font_idx;
263 $font_idx = $integer;
264 if
($font_idx >= context
->font
->num_sfnts
)
266 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Font_Index
);
269 context
->font_idx
= $font_idx;
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
);
285 context
->glyph_idx
= $glyph_idx;
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"))
299 $glyph_idx = (long)FT_Get_Name_Index
(face
, $glyph_name);
308 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph_Name
);
311 context
->glyph_idx
= $glyph_idx;
318 /* `$glyph_name_' was allocated in the lexer */
322 store_error_data
(&@$
, context
, context
->error);
326 $glyph_name = $glyph_name_;
345 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
349 error = FT_Load_Glyph
(face
,
350 (FT_UInt
)context
->glyph_idx
,
354 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
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_;
370 $point_touch_ = Control_Delta_after_IUP
;
375 $point_touch_ = Control_Delta_before_IUP
;
384 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
388 error = FT_Load_Glyph
(face
,
389 (FT_UInt
)context
->glyph_idx
,
393 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
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_;
409 $left_right_ = Control_Segment_Left
;
414 $left_right_ = Control_Segment_Right
;
423 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
427 error = FT_Load_Glyph
(face
,
428 (FT_UInt
)context
->glyph_idx
,
432 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
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
;
447 glyph_name
[script
] glyph_name
[feature
]
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
]))
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
))
481 if
(i
== script_names_size
)
483 store_error_data
(&@
1, context
, TA_Err_Control_Invalid_Script
);
486 if
(j
== feature_tags_size
)
488 store_error_data
(&@
2, context
, TA_Err_Control_Invalid_Feature
);
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
;
504 if
(!ta_style_classes
[ss
])
506 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Style
);
536 if
($real < CONTROL_DELTA_SHIFT_MIN ||
$real > CONTROL_DELTA_SHIFT_MAX
)
538 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Shift
);
548 if
($signed_integer < SHRT_MIN ||
$signed_integer > SHRT_MAX
)
550 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Offset
);
553 $offset = $signed_integer;
560 context
->number_set_min
= CONTROL_DELTA_PPEM_MIN
;
561 context
->number_set_max
= CONTROL_DELTA_PPEM_MAX
;
564 { $ppem_set = $number_set; }
575 store_error_data
(&@$
, context
, context
->error);
585 { $signed_integer = $integer; }
587 { $signed_integer = $integer; }
589 { $signed_integer = -$integer; }
594 { $real = $signed_integer; }
600 store_error_data
(&@$
, context
, context
->error);
614 { $number_set = $unlimited; }
616 { $number_set = $right_limited; }
618 { $number_set = $left_limited; }
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
);
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
);
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
);
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
);
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
);
678 $right_limited = number_set_new
(context
->number_set_min
,
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
);
687 if
($right_limited == NUMBERSET_ALLOCATION_ERROR
)
689 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
698 $left_limited = number_set_new
((int)$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
);
707 if
($left_limited == NUMBERSET_ALLOCATION_ERROR
)
709 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
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
);
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
);
741 $range_elem = number_set_new
((int)$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
);
750 if
($range_elem == NUMBERSET_ALLOCATION_ERROR
)
752 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
757 { $range_elem = $range; }
761 integer
[left
] '-' integer
[right
]
763 $range = number_set_new
((int)$left,
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
);
772 if
($range == NUMBERSET_ALLOCATION_ERROR
)
774 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
783 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
786 context
->number_set_min
= 0;
787 context
->number_set_max
= (int)(face
->num_glyphs
- 1);
789 glyph_idx_range_elems
790 { $glyph_idx_set = $glyph_idx_range_elems; }
793 glyph_idx_range_elems
[result
]:
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
);
810 glyph_idx_range_elem:
813 $glyph_idx_range_elem = number_set_new
((int)$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
);
826 { $glyph_idx_range_elem = $glyph_idx_range; }
830 glyph_idx
[left
] '-' glyph_idx
[right
]
832 $glyph_idx_range = number_set_new
((int)$left,
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
);
851 TA_control_error
(YYLTYPE *locp
,
852 Control_Context
* context
,
855 /* if `error' is already set, we have a fatal flex 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
;
869 store_error_data
(const YYLTYPE *locp
,
870 Control_Context
* context
,
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';
886 * compile this test program with
888 * make libnumberset.la
890 * flex -d tacontrol.flex \
891 * && bison -t tacontrol.bison \
896 * -I/usr/local/include/freetype2 \
897 * -I/usr/local/include/harfbuzz \
899 * -o tacontrol-bison \
900 * tacontrol-bison.c \
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";
925 Control_Context context
;
930 const char* filename
;
935 fprintf
(stderr
, "need an outline font as an argument\n");
941 error = FT_Init_FreeType
(&library
);
944 fprintf
(stderr
, "error while initializing FreeType library (0x%X)\n",
949 error = FT_New_Face
(library
, filename
, 0, &face
);
952 fprintf
(stderr
, "error while loading font `%s' (0x%X)\n",
958 /* we construct a minumum `Font' object */
959 sfnts
[0].face
= face
;
963 font.control_buf
= (char*)input
;
964 font.control_len
= strlen
(input
);
966 TA_control_debug
= 1;
968 TA_control_scanner_init
(&context
, &font
);
972 bison_error
= TA_control_parse
(&context
);
979 TA_control_scanner_done
(&context
);
980 TA_control_free
(context.result
);
986 FT_Done_FreeType
(library
);
994 /* end of tacontrol.bison */