4 * Copyright (C) 2014-2015 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
}
49 /* we don't change the name prefix of flex functions */
50 #define TA_control_lex yylex
65 #include "tacontrol-flex.h"
68 TA_control_error
(YYLTYPE *locp
,
69 Control_Context
* context
,
73 store_error_data
(const YYLTYPE *locp
,
74 Control_Context
* context
,
78 /* calls to `yylex' in the generated bison code use `scanner' directly */
79 #define scanner context->scanner
82 /* INVALID_CHARACTER and INTERNAL_FLEX_ERROR are flex errors */
84 %token
<integer
> INTEGER
"integer number"
85 %token INTERNAL_FLEX_ERROR
"internal flex error"
86 %token
<character
> INVALID_CHARACTER
"invalid character"
87 %token
<name
> LEFT
"left"
88 %token
<name
> NAME
"glyph name"
89 %token
<name
> NODIR
"no direction"
90 %token
<name
> POINT
"point"
91 %token
<real
> REAL
"real number"
92 %token
<name
> RIGHT
"right"
93 %token
<name
> TOUCH
"touch"
94 %token
<name
> XSHIFT
"x shift"
95 %token
<name
> YSHIFT
"y shift"
98 %type
<integer
> font_idx
99 %type
<integer
> glyph_idx
100 %type
<name
> glyph_name
101 %type
<name
> glyph_name_
102 %type
<control
> input
103 %type
<integer
> integer
104 %type
<type
> left_right
105 %type
<type
> left_right_
106 %type
<type
> point_touch
107 %type
<type
> point_touch_
108 %type
<range
> left_limited
110 %type
<range
> number_set
111 %type
<integer
> offset
112 %type
<range
> ppem_set
114 %type
<range
> range_elem
115 %type
<range
> range_elems
117 %type
<range
> right_limited
119 %type
<integer
> signed_integer
120 %type
<range
> unlimited
124 %destructor
{ TA_control_free
($$
); } <control
>
125 %destructor
{ number_set_free
($$
); } <range
>
127 %printer
{ fprintf
(yyoutput
, "`%ld'", $$
); } <integer
>
128 %printer
{ fprintf
(yyoutput
, "`%s'", $$
); } <name
>
129 %printer
{ fprintf
(yyoutput
, "`%g'", $$
); } <real
>
135 nr
= number_set_reverse
($$
);
136 s
= number_set_show
(nr
, -1, -1);
137 (void)number_set_reverse
(nr
);
140 fprintf
(yyoutput
, "`%s'", s
);
144 fprintf
(yyoutput
, "allocation error");
146 %printer
{ fprintf
(yyoutput
, "`%c'", $$
); } INVALID_CHARACTER
151 /* `number_range' list elements are stored in reversed order; */
152 /* the call to `TA_control_new' fixes this */
154 /* `Control' list elements are stored in reversed order, too; */
155 /* this gets fixed by an explicit call to `TA_control_reverse' */
159 { context
->result
= TA_control_reverse
($input); }
166 { $result = TA_control_prepend
($left, $entry); }
172 | font_idx glyph_idx point_touch number_set x_shift y_shift ppem_set EOE
174 $entry = TA_control_new
($point_touch,
183 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
187 | font_idx glyph_idx left_right number_set EOE
189 $entry = TA_control_new
($left_right,
198 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
202 | font_idx glyph_idx left_right number_set
'(' offset
[left
] ',' offset
[right
] ')' EOE
204 $entry = TA_control_new
($left_right,
213 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
217 | font_idx glyph_idx no_dir number_set EOE
219 $entry = TA_control_new
($no_dir,
228 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
238 context
->font_idx
= $font_idx;
242 $font_idx = $integer;
243 if
($font_idx >= context
->font
->num_sfnts
)
245 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Font_Index
);
248 context
->font_idx
= $font_idx;
255 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
258 $glyph_idx = $integer;
259 if
($glyph_idx >= face
->num_glyphs
)
261 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph_Index
);
264 context
->glyph_idx
= $glyph_idx;
268 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
271 /* explicitly compare with `.notdef' */
272 /* since `FT_Get_Name_Index' returns glyph index 0 */
273 /* for both this glyph name and invalid ones */
274 if
(!strcmp
($glyph_name, ".notdef"))
278 $glyph_idx = FT_Get_Name_Index
(face
, $glyph_name);
287 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph_Name
);
290 context
->glyph_idx
= $glyph_idx;
297 /* `$glyph_name_' was allocated in the lexer */
301 store_error_data
(&@$
, context
, context
->error);
305 $glyph_name = $glyph_name_;
324 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
328 error = FT_Load_Glyph
(face
, context
->glyph_idx
, FT_LOAD_NO_SCALE
);
331 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
335 num_points
= face
->glyph
->outline.n_points
;
337 context
->number_set_min
= 0;
338 context
->number_set_max
= num_points
- 1;
340 $point_touch = $point_touch_;
347 $point_touch_ = Control_Delta_after_IUP
;
352 $point_touch_ = Control_Delta_before_IUP
;
361 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
365 error = FT_Load_Glyph
(face
, context
->glyph_idx
, FT_LOAD_NO_SCALE
);
368 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
372 num_points
= face
->glyph
->outline.n_points
;
374 context
->number_set_min
= 0;
375 context
->number_set_max
= num_points
- 1;
377 $left_right = $left_right_;
384 $left_right_ = Control_Segment_Left
;
389 $left_right_ = Control_Segment_Right
;
398 FT_Face face
= context
->font
->sfnts
[context
->font_idx
].face
;
402 error = FT_Load_Glyph
(face
, context
->glyph_idx
, FT_LOAD_NO_SCALE
);
405 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Glyph
);
409 num_points
= face
->glyph
->outline.n_points
;
411 context
->number_set_min
= 0;
412 context
->number_set_max
= num_points
- 1;
414 $no_dir = Control_Segment_None
;
443 if
($real < CONTROL_DELTA_SHIFT_MIN ||
$real > CONTROL_DELTA_SHIFT_MAX
)
445 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Shift
);
455 if
($signed_integer < SHRT_MIN ||
$signed_integer > SHRT_MAX
)
457 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Offset
);
460 $offset = $signed_integer;
467 context
->number_set_min
= CONTROL_DELTA_PPEM_MIN
;
468 context
->number_set_max
= CONTROL_DELTA_PPEM_MAX
;
471 { $ppem_set = $number_set; }
482 store_error_data
(&@$
, context
, context
->error);
492 { $signed_integer = $integer; }
494 { $signed_integer = $integer; }
496 { $signed_integer = -$integer; }
501 { $real = $signed_integer; }
507 store_error_data
(&@$
, context
, context
->error);
521 { $number_set = $unlimited; }
523 { $number_set = $right_limited; }
525 { $number_set = $left_limited; }
527 { $number_set = $range_elems; }
528 | right_limited
',' range_elems
530 $number_set = number_set_prepend
($right_limited, $range_elems);
531 if
($number_set == NUMBERSET_NOT_ASCENDING
)
533 number_set_free
($right_limited);
534 number_set_free
($range_elems);
535 store_error_data
(&@
3, context
, TA_Err_Control_Ranges_Not_Ascending
);
538 if
($number_set == NUMBERSET_OVERLAPPING_RANGES
)
540 number_set_free
($right_limited);
541 number_set_free
($range_elems);
542 store_error_data
(&@
3, context
, TA_Err_Control_Overlapping_Ranges
);
546 | range_elems
',' left_limited
548 $number_set = number_set_prepend
($range_elems, $left_limited);
549 if
($number_set == NUMBERSET_NOT_ASCENDING
)
551 number_set_free
($range_elems);
552 number_set_free
($left_limited);
553 store_error_data
(&@
3, context
, TA_Err_Control_Ranges_Not_Ascending
);
556 if
($number_set == NUMBERSET_OVERLAPPING_RANGES
)
558 number_set_free
($range_elems);
559 number_set_free
($left_limited);
560 store_error_data
(&@
3, context
, TA_Err_Control_Overlapping_Ranges
);
569 $unlimited = number_set_new
(context
->number_set_min
,
570 context
->number_set_max
,
571 context
->number_set_min
,
572 context
->number_set_max
);
573 /* range of `$unlimited' is always valid */
574 if
($unlimited == NUMBERSET_ALLOCATION_ERROR
)
576 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
585 $right_limited = number_set_new
(context
->number_set_min
,
587 context
->number_set_min
,
588 context
->number_set_max
);
589 if
($right_limited == NUMBERSET_INVALID_RANGE
)
591 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
594 if
($right_limited == NUMBERSET_ALLOCATION_ERROR
)
596 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
605 $left_limited = number_set_new
($integer,
606 context
->number_set_max
,
607 context
->number_set_min
,
608 context
->number_set_max
);
609 if
($left_limited == NUMBERSET_INVALID_RANGE
)
611 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
614 if
($left_limited == NUMBERSET_ALLOCATION_ERROR
)
616 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
624 { $result = $range_elem; }
625 | range_elems
[left
] ',' range_elem
627 $result = number_set_prepend
($left, $range_elem);
628 if
($result == NUMBERSET_NOT_ASCENDING
)
630 number_set_free
($left);
631 number_set_free
($range_elem);
632 store_error_data
(&@
3, context
, TA_Err_Control_Ranges_Not_Ascending
);
635 if
($result == NUMBERSET_OVERLAPPING_RANGES
)
637 number_set_free
($left);
638 number_set_free
($range_elem);
639 store_error_data
(&@
3, context
, TA_Err_Control_Overlapping_Ranges
);
648 $range_elem = number_set_new
($integer,
650 context
->number_set_min
,
651 context
->number_set_max
);
652 if
($range_elem == NUMBERSET_INVALID_RANGE
)
654 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
657 if
($range_elem == NUMBERSET_ALLOCATION_ERROR
)
659 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
664 { $range_elem = $range; }
668 integer
[left
] '-' integer
[right
]
670 $range = number_set_new
($left,
672 context
->number_set_min
,
673 context
->number_set_max
);
674 if
($range == NUMBERSET_INVALID_RANGE
)
676 store_error_data
(&@$
, context
, TA_Err_Control_Invalid_Range
);
679 if
($range == NUMBERSET_ALLOCATION_ERROR
)
681 store_error_data
(&@$
, context
, TA_Err_Control_Allocation_Error
);
692 TA_control_error
(YYLTYPE *locp
,
693 Control_Context
* context
,
696 /* if `error' is already set, we have a fatal flex error */
699 context
->error = TA_Err_Control_Syntax_Error
;
700 strncpy
(context
->errmsg
, msg
, sizeof
(context
->errmsg
));
703 context
->errline_num
= locp
->first_line
;
704 context
->errline_pos_left
= locp
->first_column
;
705 context
->errline_pos_right
= locp
->last_column
;
710 store_error_data
(const YYLTYPE *locp
,
711 Control_Context
* context
,
714 context
->error = error;
716 context
->errline_num
= locp
->first_line
;
717 context
->errline_pos_left
= locp
->first_column
;
718 context
->errline_pos_right
= locp
->last_column
;
720 context
->errmsg
[0] = '\0';
727 * compile this test program with
729 * make libnumberset.la
731 * flex -d tacontrol.flex \
732 * && bison -t tacontrol.bison \
737 * -I/usr/local/include/freetype2 \
738 * -I/usr/local/include/harfbuzz \
740 * -o tacontrol-bison \
741 * tacontrol-bison.c \
751 "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n"
752 "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n"
753 "a p / 12 x 0.5 @ 23-25";
766 Control_Context context
;
771 const char* filename
;
776 fprintf
(stderr
, "need an outline font as an argument\n");
782 error = FT_Init_FreeType
(&library
);
785 fprintf
(stderr
, "error while initializing FreeType library (0x%X)\n",
790 error = FT_New_Face
(library
, filename
, 0, &face
);
793 fprintf
(stderr
, "error while loading font `%s' (0x%X)\n",
799 /* we construct a minumum `Font' object */
800 sfnts
[0].face
= face
;
804 font.control_buf
= (char*)input
;
805 font.control_len
= strlen
(input
);
807 TA_control_debug
= 1;
809 TA_control_scanner_init
(&context
, &font
);
813 bison_error
= TA_control_parse
(&context
);
820 TA_control_scanner_done
(&context
);
821 TA_control_free
(context.result
);
827 FT_Done_FreeType
(library
);
835 /* end of tacontrol.bison */