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,
190 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
194 | font_idx glyph_idx left_right number_set EOE
196 $entry = TA_control_new
($left_right,
206 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
210 | font_idx glyph_idx left_right number_set
'(' offset
[left
] ',' offset
[right
] ')' EOE
212 $entry = TA_control_new
($left_right,
222 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
226 | font_idx glyph_idx no_dir number_set EOE
228 $entry = TA_control_new
($no_dir,
238 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
242 | font_idx script_feature glyph_idx_set EOE
244 $entry = TA_control_new
(Control_Script_Feature
,
254 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
264 context
->font_idx
= $font_idx;
268 $font_idx = $integer;
269 if
($font_idx >= context
->font
->num_sfnts
)
271 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Font_Index
);
274 context
->font_idx
= $font_idx;
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
);
290 context
->glyph_idx
= $glyph_idx;
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"))
304 $glyph_idx = (long)FT_Get_Name_Index
(face
, $glyph_name);
313 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph_Name
);
316 context
->glyph_idx
= $glyph_idx;
323 /* `$glyph_name_' was allocated in the lexer */
327 store_error_data
(&@$
, context
, context
->error);
331 $glyph_name = $glyph_name_;
350 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
354 error = FT_Load_Glyph
(face
,
355 (FT_UInt
)context
->glyph_idx
,
359 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
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_;
375 $point_touch_ = Control_Delta_after_IUP
;
380 $point_touch_ = Control_Delta_before_IUP
;
389 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
393 error = FT_Load_Glyph
(face
,
394 (FT_UInt
)context
->glyph_idx
,
398 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
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_;
414 $left_right_ = Control_Single_Point_Segment_Left
;
419 $left_right_ = Control_Single_Point_Segment_Right
;
428 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
432 error = FT_Load_Glyph
(face
,
433 (FT_UInt
)context
->glyph_idx
,
437 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
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
;
452 glyph_name
[script
] glyph_name
[feature
]
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
]))
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
))
486 if
(i
== script_names_size
)
488 store_error_data
(&@
1, context
, TA_Err_Control_Invalid_Script
);
491 if
(j
== feature_tags_size
)
493 store_error_data
(&@
2, context
, TA_Err_Control_Invalid_Feature
);
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
;
509 if
(!ta_style_classes
[ss
])
511 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Style
);
541 if
($real < CONTROL_DELTA_SHIFT_MIN ||
$real > CONTROL_DELTA_SHIFT_MAX
)
543 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Shift
);
553 if
($signed_integer < SHRT_MIN ||
$signed_integer > SHRT_MAX
)
555 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Offset
);
558 $offset = $signed_integer;
565 context
->number_set_min
= CONTROL_DELTA_PPEM_MIN
;
566 context
->number_set_max
= CONTROL_DELTA_PPEM_MAX
;
569 { $ppem_set = $number_set; }
580 store_error_data
(&@$
, context
, context
->error);
590 { $signed_integer = $integer; }
592 { $signed_integer = $integer; }
594 { $signed_integer = -$integer; }
599 { $real = $signed_integer; }
605 store_error_data
(&@$
, context
, context
->error);
619 { $number_set = $unlimited; }
621 { $number_set = $right_limited; }
623 { $number_set = $left_limited; }
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
);
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
);
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
);
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
);
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
);
683 $right_limited = number_set_new
(context
->number_set_min
,
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
);
692 if
($right_limited == NUMBERSET_ALLOCATION_ERROR
)
694 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
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
);
712 if
($left_limited == NUMBERSET_ALLOCATION_ERROR
)
714 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
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
);
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
);
746 $range_elem = number_set_new
((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
);
755 if
($range_elem == NUMBERSET_ALLOCATION_ERROR
)
757 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
762 { $range_elem = $range; }
766 integer
[left
] '-' integer
[right
]
768 $range = number_set_new
((int)$left,
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
);
777 if
($range == NUMBERSET_ALLOCATION_ERROR
)
779 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
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
]:
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
);
815 glyph_idx_range_elem:
818 $glyph_idx_range_elem = number_set_new
((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
);
831 { $glyph_idx_range_elem = $glyph_idx_range; }
835 glyph_idx
[left
] '-' glyph_idx
[right
]
837 $glyph_idx_range = number_set_new
((int)$left,
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
);
856 TA_control_error
(YYLTYPE *locp
,
857 Control_Context
* context
,
860 /* if `error' is already set, we have a fatal flex 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
;
874 store_error_data
(const YYLTYPE *locp
,
875 Control_Context
* context
,
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';
891 * compile this test program with
893 * make libnumberset.la
895 * flex -d tacontrol.flex \
896 * && bison -t tacontrol.bison \
901 * -I/usr/local/include/freetype2 \
902 * -I/usr/local/include/harfbuzz \
904 * -o tacontrol-bison \
905 * tacontrol-bison.c \
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";
930 Control_Context context
;
935 const char* filename
;
940 fprintf
(stderr
, "need an outline font as an argument\n");
946 error = FT_Init_FreeType
(&library
);
949 fprintf
(stderr
, "error while initializing FreeType library (0x%X)\n",
954 error = FT_New_Face
(library
, filename
, 0, &face
);
957 fprintf
(stderr
, "error while loading font `%s' (0x%X)\n",
963 /* we construct a minumum `Font' object */
964 sfnts
[0].face
= face
;
968 font.control_buf
= (char*)input
;
969 font.control_len
= strlen
(input
);
971 TA_control_debug
= 1;
973 TA_control_scanner_init
(&context
, &font
);
977 bison_error
= TA_control_parse
(&context
);
984 TA_control_scanner_done
(&context
);
985 TA_control_free
(context.result
);
991 FT_Done_FreeType
(library
);
999 /* end of tacontrol.bison */