/* * Lexer for BibTeX author fields * * This file is part of Beastie * SPDX-FileCopyrightText: 2023 Norman Gray * SPDX-License-Identifier: BSD-2-Clause */ %top{ #if __GNUC__ // for fileno #define _XOPEN_SOURCE 600 #endif } %{ #include #include #include "beastie.h" #include "s7.h" #include "util.h" #include "parse-authors.h" #include "parse-authors.tab.h" #ifndef WITH_MAIN #define WITH_MAIN 0 #endif %} /* Flex doesn't do Unicode. There is a alternative lexer RE-flex * which does and which might * be of interest in the future, but for now, we have to do it by * hand. The following patterns were taken from a Stackoverflow * answer by ‘Kaz’, * * These patters work by spotting UTF-8 byte patterns. * They therefore cover only UTF-8. * That's mostly OK here, because s7's unicode support is actually * just UTF-8 support. and it handles the decoding of the UTF-8 * strings we lex here, so we don't have to. * * ASCN and UANYN are ASC and UANY minus newline. * UONLY is non-ASCII. */ ASC [\x00-\x7f] ASCN [\x00-\t\v-\x7f] U [\x80-\xbf] U2 [\xc2-\xdf] U3 [\xe0-\xef] U4 [\xf0-\xf4] UANY {ASC}|{U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U} UANYN {ASCN}|{U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U} UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U} /* We do currently accept dots in names, so that eg "Jr." is acceptable. * Is that right? * The Beebe syntax has for abbrev, entry, key, field name syntax, * NAME [A-Za-z][-A-Za-z0-9:.+/']* * so we should, too * Here, we allow a name to also include any non-ASCII Unicode character. */ CAPNAME [A-Z]([-A-Za-z0-9:.+/']|{UONLY})* /* btxdoc says (p.16): * * In general, it’s a von token if the first * letter at brace-level 0 is in lower case. * * There are a couple of following remarks about special cases * concerning 'special characters'. */ LCNAME [a-z]([-A-Za-z0-9:.+/']|{UONLY})* /* 'Unicode name' */ UNAME {UONLY}([-A-Za-z0-9:.+/']|{UONLY})* OWS [[:space:]]* WS [[:space:]]+ %option noyywrap nounput %option prefix="authors" reentrant bison-bridge bison-locations %option extra-type="authors_extra_t" %% ","{OWS} return ','; {WS} return ' '; "{" { *yylval = scan_to_matching_char(&input, yyscanner, yyget_lineno(yyscanner), "{", '}', SCAN_INCLUDE_LAST); return BRACED_STRING; } "\\"([[:alpha:]]+|[^[:space:]])[[:space:]]* { *yylval = s7_make_string(S7, yytext); return LCNAME; } /* Match "and" lowercase, only. This is a defection from BibTeX, which matches this case insensitively (which I think is a bit nuts). */ {WS}"and"{WS} { return AND; } {OWS}"others"{OWS} return OTHERS; {CAPNAME} { *yylval = s7_make_string(S7, yytext); return CAPNAME; } {LCNAME} { *yylval = s7_make_string(S7, yytext); return LCNAME; } /* We parse strings starting with an accented letter as uppercase. * This isn't quite right, but (a) fixing it would probably require a * lot of work to dig into Unicode uppercase coding, and (b) I'm not * aware of 'von' particles which start with an accented letter * (which of course means I'm going to learn about one tomorrow). * So... this'll do for now. */ {UNAME} { *yylval = s7_make_string(S7, yytext); return CAPNAME; } . { if (yyextra->location) { fprintf(stderr, "Unexpected character '%c' near %s, parsing authorlist: %s\n", yytext[0], yyextra->location, yyextra->current_string); } else { fprintf(stderr, "Unexpected character '%c', parsing authorlist: %s\n", yytext[0], yyextra->current_string); } } %% yyscan_t parse_authors_setup_string(authors_extra_t extra, const char* s) { yyscan_t scanner; yylex_init_extra(extra, &scanner); extra->current_string = s; extra->location = NULL; // we hope this will be set after setup and before use extra->yyscanbuf = (void*)yy_scan_string(s, scanner); yyset_lineno(1, scanner); return scanner; } void parse_authors_finish(authors_extra_t extra, yyscan_t scanner) { if (extra->yyscanbuf) { yy_delete_buffer((YY_BUFFER_STATE)extra->yyscanbuf, scanner); extra->yyscanbuf = NULL; } yylex_destroy(scanner); } #if WITH_MAIN #include YYSTYPE one_value; YYLTYPE locp; s7_scheme* S7; static void display_lexemes(yyscan_t scanner) { int l; while ((l = authorslex(&one_value, &locp, scanner)) != 0) { switch (l) { case CAPNAME: s7w("capname=", one_value, "\n"); break; case LCNAME: s7w("lcname=", one_value, "\n"); break; case AND: printf("AND\n"); break; case BRACED_STRING: s7w("braced_string=", one_value, "\n"); break; case ',': printf(",\n"); break; case ' ': printf("space\n"); break; case OTHERS: printf("...others\n"); break; default: printf("Unexpected lexeme: %d\n", l); } } } int main(int argc, char** argv) { S7 = s7_init(); if (argc != 2 || argv[1][0] == '-') { fprintf(stderr, "Usage: %s \"authorlist-string\"\n", argv[0]); exit(1); } struct authors_extra_s S; yyscan_t scanner = parse_authors_setup_string(&S, argv[1]); display_lexemes(scanner); parse_authors_finish(&S, scanner); } #endif