From 652d8cf7c400b2f7a7cee49a0c8238bf71ff4f2c Mon Sep 17 00:00:00 2001 From: Werner Lemberg Date: Wed, 3 Sep 2014 12:48:44 +0200 Subject: [PATCH] Add a flex lexer and bison parser to handle delta exception descriptions. This is work in progress and not used yet. --- lib/tadeltas.l | 247 +++++++++++++++++++++++++ lib/tadeltas.y | 558 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 805 insertions(+) create mode 100644 lib/tadeltas.l create mode 100644 lib/tadeltas.y diff --git a/lib/tadeltas.l b/lib/tadeltas.l new file mode 100644 index 0000000..ecff687 --- /dev/null +++ b/lib/tadeltas.l @@ -0,0 +1,247 @@ +/* tadeltas.l */ + +/* + * Copyright (C) 2014 by Werner Lemberg. + * + * This file is part of the ttfautohint library, and may only be used, + * modified, and distributed under the terms given in `COPYING'. By + * continuing to use, modify, or distribute this file you indicate that you + * have read `COPYING' and understand and accept it fully. + * + * The file `COPYING' mentioned in the previous paragraph is distributed + * with the ttfautohint library. + */ + +/* lexical analyzer for parsing delta exceptions */ + +%option outfile="tadeltas-flex.c" +%option header-file="tadeltas-flex.h" + +%option batch +%option bison-bridge +%option bison-locations +%option never-interactive +%option noinput +%option nounput +%option noyywrap +%option reentrant + +%{ + +#include +#include + +#include "tadeltas-bison.h" + +#define YY_USER_ACTION \ + yylloc->first_line = yylineno; \ + yylloc->last_line = yylineno; \ + yylloc->first_column = yycolumn; \ + yylloc->lasl_column = yycolumn + yyleng - 1; \ + yycolumn += yyleng; + +#define YY_EXTRA_TYPE Deltas_Context* + +%} + + +%% + + +(?x: [ \t\f]+ +) { + /* skip whitespace */ +} + +(?x: ( "\r\n" + | "\n" + | "\r" + | ";" + )+ +) { + /* end of entry */ + return EOE; +} + +<> { + /* we want an EOE token at end of file, too */ + if (!yyextra->eof) + { + yyextra->eof = 1; + return EOE; + } + else + yyterminate(); +} + +(?x: ( "\\\r\n" + | "\\\n" + | "\\\r" + )+ +) { + /* skip escaped newline */ +} + +(?x: "#" [^\n]* +) { + /* skip line comment */ +} + + +(?x: [0+-] +) { + /* values `0' and `-' need special treatment for number ranges */ + return yytext[0]; +} + +(?x: [1-9] [0-9]* + | "0" [xX] [0-9a-fA-F]+ + | "0" [0-7]+ +) { + /* we don't support localized formats like a thousands separator */ + errno = 0; + yylval->integer = strtol(yytext, NULL, 0); + if (errno == ERANGE) + { + /* overflow or underflow */ + yyextra->error = TA_Err_Deltas_Overflow; + return BAD; + } + return INTEGER; +} + +(?x: [0-9]* "." [0-9]+ + | [0-9]+ "." [0-9]* +) { + /* we don't support exponents, */ + /* and we don't support localized formats like using `,' instead of `.' */ + errno = 0; + yylval->real = strtod(yytext, NULL); + if (yylval->real && errno == ERANGE) + { + /* overflow */ + yyextra->error = TA_Err_Deltas_Overflow; + return BAD; + } + return REAL; +} + + +(?x: [pxy@,] +) { + /* delimiters */ + return yytext[0]; +} + +(?x: [A-Za-z._] [A-Za-z0-9._]* +) { + yylval->name = strdup(yytext); + if (!yylval->name) + { + yyextra->error = TA_Err_Deltas_Allocation_Error; + return BAD; + } + return NAME; +} + + +(?x: . +) { + /* invalid input */ + yyextra->error = TA_Err_Deltas_Syntax_Error; + return BAD; +} + + +%% + + +TA_Error +TA_deltas_scanner_init(Deltas_Context* context, + const char* input) +{ + char* buf; + size_t buf_len; + int flex_error; + + yyscan_t scanner; + + + buf_len = strlen(input); + buf_len += 2; /* two bytes more at the end needed for flex */ + + buf = (char*)malloc(buf_len); + if (!buf) + return FT_Err_Out_Of_Memory; + strncpy(buf, input, buf_len - 2); + buf[buf_len - 2] = YY_END_OF_BUFFER_CHAR; + buf[buf_len - 1] = YY_END_OF_BUFFER_CHAR; + + /* this function sets `errno' in case of error */ + flex_error = yylex_init(&scanner); + if (flex_error && errno == ENOMEM) + return FT_Err_Out_Of_Memory; + + yyset_extra(context, scanner); + + /* XXX unfortunately, this flex function simply calls `exit' */ + /* (via YY_FATAL_ERROR) in case of a (more or less) fatal error -- */ + /* a possible error handling solution is using `longjmp', cf. */ + /* https://github.com/plusvic/yara/issues/45 */ + yy_scan_buffer(buf, buf_len, scanner); + + /* only initialize fields related to the lexer */ + context->error = TA_Err_Ok; + context->scanner = scanner; + context->buf = buf; + context->eof = 0; + + return TA_Err_Ok; +} + + +void +TA_deltas_scanner_done(Deltas_Context* context) +{ + yylex_destroy(context->scanner); + + free(context->buf); +} + + +#if 0 + +const char* input = + "# Test\n" + "\n" + "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n" + "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n" + "0 a p 2 x 0.5 @ 23-25"; + + +int +main(void) +{ + TA_Error error; + Deltas_Context context; + + YYSTYPE yylval_param; + YYLTYPE yylloc_param; + + + error = TA_deltas_scanner_init(&context, input); + if (error) + return 1; + + yyset_debug(1, context.scanner); + while (yylex(&yylval_param, &yylloc_param, context.scanner)) + ; + + TA_deltas_scanner_done(&context); + + return 0; +} + +#endif + +/* end of tadeltas.l */ diff --git a/lib/tadeltas.y b/lib/tadeltas.y new file mode 100644 index 0000000..25c3c47 --- /dev/null +++ b/lib/tadeltas.y @@ -0,0 +1,558 @@ +/* tadeltas.y */ + +/* + * Copyright (C) 2014 by Werner Lemberg. + * + * This file is part of the ttfautohint library, and may only be used, + * modified, and distributed under the terms given in `COPYING'. By + * continuing to use, modify, or distribute this file you indicate that you + * have read `COPYING' and understand and accept it fully. + * + * The file `COPYING' mentioned in the previous paragraph is distributed + * with the ttfautohint library. + */ + + +/* grammar for parsing delta exceptions */ + +/* + * Edsko de Vries's article `Writing a Reentrant Parser with Flex and Bison' + * (http://www.phpcompiler.org/articles/reentrantparser.html) + * was extremely helpful in writing this code. + */ + +%output "tadeltas-bison.c" +%defines "tadeltas-bison.h" + +%define api.pure +%error-verbose +%expect 2 +%glr-parser +%lex-param { void* scanner } +%locations +%name-prefix "TA_deltas_" +%parse-param { Deltas_Context* context } +%require "2.5" + +%code requires { +#include "ta.h" + +/* we don't change the name prefix of flex functions */ +#define TA_deltas_lex yylex +} + +%union { + long integer; + char* name; + number_range* range; + double real; + Deltas* deltas; +} + +%{ +#include "tadeltas-flex.h" + +void +TA_deltas_error(YYLTYPE *locp, + Deltas_Context* context, + char const* msg); + + +/* calls to `yylex' in the generated bison code use `scanner' directly */ +#define scanner context->scanner +%} + +%token BAD /* indicates a flex error */ +%token EOE +%token INTEGER +%token NAME +%token REAL + +%type entry +%type font_idx +%type glyph_idx +%type glyph_name +%type input +%type integer +%type left_limited +%type number_set +%type point_set +%type ppem_set +%type range +%type range_elem +%type range_elems +%type real +%type right_limited +%type shift +%type unlimited +%type x_shift +%type y_shift + +%destructor { TA_deltas_free($$); } +%destructor { number_set_free($$); } + + +%% + + +/* `number_range' list elements are stored in reversed order; */ +/* the call to `TA_deltas_new' fixes this */ +/* (`Deltas' list elements are stored in reversed order, too, */ +/* but they don't need to be sorted so we don't care */ + +start: + input + { context->result = $input; } +; + +input[result]: + /* empty */ + { $result = NULL; } +| input[left] entry + { $result = TA_deltas_prepend($left, $entry); } +; + +entry: + EOE + { $entry = NULL; } +| font_idx glyph_idx point_set x_shift y_shift ppem_set EOE + { + $entry = TA_deltas_new($font_idx, + $glyph_idx, + $point_set, + $x_shift, + $y_shift, + $ppem_set); + if (!$entry) + { + context->error = TA_Err_Deltas_Allocation_Error; + YYABORT; + } + } +; + +font_idx: + /* empty */ + { + $font_idx = 0; + context->font_idx = $font_idx; + } +| integer + { + $font_idx = $integer; + if ($font_idx >= context->font->num_sfnts) + { + context->error = TA_Err_Deltas_Invalid_Font_Index; + YYABORT; + } + context->font_idx = $font_idx; + } +; + +glyph_idx: + integer + { + FT_Face face = context->font->sfnts[context->font_idx].face; + + + $glyph_idx = $integer; + if ($glyph_idx >= face->num_glyphs) + { + context->error = TA_Err_Deltas_Invalid_Glyph_Index; + YYABORT; + } + context->glyph_idx = $glyph_idx; + } +| glyph_name + { + FT_Face face = context->font->sfnts[context->font_idx].face; + + + /* explicitly compare with `.notdef' */ + /* since `FT_Get_Name_Index' returns glyph index 0 */ + /* for both this glyph name and invalid ones */ + if (!strcmp($glyph_name, ".notdef")) + $glyph_idx = 0; + else + { + $glyph_idx = FT_Get_Name_Index(face, $glyph_name); + if ($glyph_idx == 0) + $glyph_idx = -1; + } + + free($glyph_name); + + if ($glyph_idx < 0) + { + context->error = TA_Err_Deltas_Invalid_Glyph_Name; + YYABORT; + } + context->glyph_idx = $glyph_idx; + } +; + +glyph_name: + 'p' + { + $glyph_name = strdup("p"); + if ($glyph_name) + { + context->error = TA_Err_Deltas_Allocation_Error; + YYABORT; + } + } +| 'x' + { + $glyph_name = strdup("x"); + if ($glyph_name) + { + context->error = TA_Err_Deltas_Allocation_Error; + YYABORT; + } + } +| 'y' + { + $glyph_name = strdup("y"); + if ($glyph_name) + { + context->error = TA_Err_Deltas_Allocation_Error; + YYABORT; + } + } +| NAME + { + /* `$NAME' was allocated in the lexer */ + $glyph_name = $NAME; + } +; + +point_set: + 'p' + { + FT_Error error; + FT_Face face = context->font->sfnts[context->font_idx].face; + int num_points; + + + error = FT_Load_Glyph(face, context->glyph_idx, FT_LOAD_NO_SCALE); + if (error) + { + context->error = TA_Err_Deltas_Invalid_Glyph; + YYABORT; + } + + num_points = face->glyph->outline.n_points; + + context->number_set_min = 0; + context->number_set_max = num_points - 1; + } + number_set + { $point_set = $number_set; } +; + +x_shift: + /* empty */ + { $x_shift = 0; } +| 'x' shift + { $x_shift = $shift; } +; + +y_shift: + /* empty */ + { $y_shift = 0; } +| 'y' shift + { $y_shift = $shift; } +; + +shift: + real + { + if ($real < DELTA_SHIFT_MIN || $real > DELTA_SHIFT_MAX) + { + context->error = TA_Err_Deltas_Invalid_Shift; + YYABORT; + } + $shift = $real; + } +; + +ppem_set: + '@' + { + context->number_set_min = DELTA_PPEM_MIN; + context->number_set_max = DELTA_PPEM_MAX; + } + number_set + { $ppem_set = $number_set; } +; + +integer: + '0' + { $integer = 0; } +| INTEGER + { $integer = $INTEGER; } +; + +real: + integer + { $real = $integer; } +| '+' integer + { $real = $integer; } +| '-' integer + { $real = -$integer; } +| REAL + { $real = $REAL; } +| '+' REAL + { $real = $REAL; } +| '-' REAL + { $real = -$REAL; } +; + +number_set: + unlimited + { $number_set = $unlimited; } +| right_limited + { $number_set = $right_limited; } +| left_limited + { $number_set = $left_limited; } +| range_elems + { $number_set = $range_elems; } +| right_limited ',' range_elems + { + $number_set = number_set_prepend($right_limited, $range_elems); + if ($number_set == NUMBERSET_NOT_ASCENDING) + { + context->error = TA_Err_Deltas_Ranges_Not_Ascending; + YYABORT; + } + if ($number_set == NUMBERSET_OVERLAPPING_RANGES) + { + context->error = TA_Err_Deltas_Overlapping_Ranges; + YYABORT; + } + } +| range_elems ',' left_limited + { + $number_set = number_set_prepend($range_elems, $left_limited); + if ($number_set == NUMBERSET_NOT_ASCENDING) + { + context->error = TA_Err_Deltas_Ranges_Not_Ascending; + YYABORT; + } + if ($number_set == NUMBERSET_OVERLAPPING_RANGES) + { + context->error = TA_Err_Deltas_Overlapping_Ranges; + YYABORT; + } + } +; + +unlimited: + '-' + { + $unlimited = number_set_new(context->number_set_min, + context->number_set_max, + context->number_set_min, + context->number_set_max); + /* range of `$unlimited' is always valid */ + if ($unlimited == NUMBERSET_ALLOCATION_ERROR) + { + context->error = TA_Err_Deltas_Allocation_Error; + YYABORT; + } + } +; + +right_limited: + '-' integer + { + $right_limited = number_set_new(context->number_set_min, + $integer, + context->number_set_min, + context->number_set_max); + if ($right_limited == NUMBERSET_INVALID_RANGE) + { + context->error = TA_Err_Deltas_Invalid_Range; + YYABORT; + } + if ($right_limited == NUMBERSET_ALLOCATION_ERROR) + { + context->error = TA_Err_Deltas_Allocation_Error; + YYABORT; + } + } +; + +left_limited: + integer '-' + { + $left_limited = number_set_new($integer, + context->number_set_max, + context->number_set_min, + context->number_set_max); + if ($left_limited == NUMBERSET_INVALID_RANGE) + { + context->error = TA_Err_Deltas_Invalid_Range; + YYABORT; + } + if ($left_limited == NUMBERSET_ALLOCATION_ERROR) + { + context->error = TA_Err_Deltas_Allocation_Error; + YYABORT; + } + } +; + +range_elems[result]: + range_elem + { $result = $range_elem; } +| range_elems[left] ',' range_elem + { + $result = number_set_prepend($left, $range_elem); + if ($result == NUMBERSET_NOT_ASCENDING) + { + context->error = TA_Err_Deltas_Ranges_Not_Ascending; + YYABORT; + } + if ($result == NUMBERSET_OVERLAPPING_RANGES) + { + context->error = TA_Err_Deltas_Overlapping_Ranges; + YYABORT; + } + } +; + +range_elem: + integer + { + $range_elem = number_set_new($integer, + $integer, + context->number_set_min, + context->number_set_max); + if ($range_elem == NUMBERSET_INVALID_RANGE) + { + context->error = TA_Err_Deltas_Invalid_Range; + YYABORT; + } + if ($range_elem == NUMBERSET_ALLOCATION_ERROR) + { + context->error = TA_Err_Deltas_Allocation_Error; + YYABORT; + } + } +| range + { $range_elem = $range; } +; + +range: + integer[left] '-' integer[right] + { + $range = number_set_new($left, + $right, + context->number_set_min, + context->number_set_max); + if ($range == NUMBERSET_INVALID_RANGE) + { + context->error = TA_Err_Deltas_Invalid_Range; + YYABORT; + } + if ($range == NUMBERSET_ALLOCATION_ERROR) + { + context->error = TA_Err_Deltas_Allocation_Error; + YYABORT; + } + } +; + + +%% + + +void +TA_deltas_error(YYLTYPE *locp, + Deltas_Context* context, + char const* msg) +{ + (void)locp; + (void)context; + + fprintf(stderr, "%s\n", msg); +} + + +#if 1 + +const char* input = + "# Test\n" + "\n" + "# 0 a p 1-3 x 0.3 y -0.2 @ 20-30; \\\n" + "0 exclam p 2-4 x 0.7 y -0.4 @ 6,7,9,10,11; \\\n" + "a p 12345678901234567890 x 0.5 @ 23-25"; + + +#undef scanner + +int +main(int argc, + char** argv) +{ + TA_Error error; + Deltas_Context context; + FONT font; + SFNT sfnts[1]; + FT_Library library; + FT_Face face; + const char* filename; + + + if (argc != 2) + { + fprintf(stderr, "need an outline font as an argument\n"); + exit(EXIT_FAILURE); + } + + filename = argv[1]; + + error = FT_Init_FreeType(&library); + if (error) + { + fprintf(stderr, "error while initializing FreeType library (0x%X)\n", + error); + exit(EXIT_FAILURE); + } + + error = FT_New_Face(library, filename, 0, &face); + if (error) + { + fprintf(stderr, "error while loading font `%s' (0x%X)\n", + filename, + error); + exit(EXIT_FAILURE); + } + + /* we construct a minumum Deltas_Context */ + sfnts[0].face = face; + + font.num_sfnts = 1; + font.sfnts = sfnts; + + context.font = &font; + + TA_deltas_debug = 1; + error = TA_deltas_scanner_init(&context, input); + if (error) + return 0; + TA_deltas_parse(&context); + TA_deltas_scanner_done(&context); + + TA_deltas_free(context.result); + + FT_Done_Face(face); + FT_Done_FreeType(library); + + return 0; +} + +#endif + +/* end of tadeltas.y */ -- 2.11.4.GIT