/* * A parser for the 'format string' component of the .bst language * format.names$ function. * * 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 "beastie.h" #include "util.h" #include "parse-fmtstring.h" #include "parse-fmtstring.tab.h" #define INCCHAR do { (yyextra->charno) += yyleng; } while (0) #ifndef WITH_MAIN #define WITH_MAIN 0 #endif %} %option prefix="fmtstring" reentrant bison-bridge bison-locations %option extra-type="fmtstring_extra_t" %option noyywrap %x BL1 /* the following, plus '{' and '}' account for all possible characters */ FMTLETTER [fvlj] TIECHAR [~] NONFMTLETTER [a-eghikm-uwxyz] OTHERCHAR [^{}~a-z] %% "{" { (yyextra->brace_level)++; INCCHAR; if ((yyextra->brace_level) == 1) { BEGIN(BL1); } else { BEGIN(INITIAL); } return '{'; } "}" { if ((yyextra->brace_level) == 0) { scheme_eval("print-warning", s7_make_string(S7, "more right braces than left at position ~a in format string ~a (ignored)"), s7_make_integer(S7, yyextra->charno), s7_make_string(S7, yyextra->current_string), NULL); } else { INCCHAR; (yyextra->brace_level)--; if ((yyextra->brace_level) == 1) { BEGIN(BL1); } else { BEGIN(INITIAL); } return '}'; } } /* all other characters, not at brace-level-1 */ [^{}]+ { INCCHAR; *yylval = s7_make_string(S7, yytext); return STRING; } "{" { (yyextra->brace_level)++; // from 1 INCCHAR; BEGIN(INITIAL); return '{'; } "}" { (yyextra->brace_level)--; // ...down to zero INCCHAR; BEGIN(INITIAL); return '}'; } {FMTLETTER} { int this_char = yytext[0]; int peek_char = input(yyscanner); int full_name; INCCHAR; // are we looking at (eg) 'f' or 'ff'? if (peek_char == this_char) { // the character is doubled INCCHAR; full_name = 1; } else { unput(peek_char); full_name = 0; } switch (this_char) { case 'f': *yylval = s7_make_symbol(S7, (full_name ? "first" : "first/i")); break; case 'v': *yylval = s7_make_symbol(S7, (full_name ? "von" : "von/i")); break; case 'l': *yylval = s7_make_symbol(S7, (full_name ? "last" : "last/i")); break; case 'j': *yylval = s7_make_symbol(S7, (full_name ? "junior" : "junior/i")); break; default: error_exit("Found character %c in fmtstring string (should be impossible)", this_char); } return FMTSYM; } {TIECHAR}+ { int nties = strlen(yytext); if (nties > 2) { #if WITH_MAIN fprintf(stderr, "unexpected multiple ties in <%s>\n", yyextra->current_string); #else scheme_eval("print-warning", s7_make_string(S7, "unexpected multiple ties in format ~s"), s7_make_string(S7, yyextra->current_string), NULL); #endif } if (nties == 1) { *yylval = s7_make_symbol(S7, "nbsp?"); return OPT_TIE; } else { *yylval = s7_make_symbol(S7, "nbsp"); return REQ_TIE; } } {OTHERCHAR}+ { INCCHAR; *yylval = s7_make_string(S7, yytext); return NONALPHA; // return STRING; } {NONFMTLETTER}+ { // Inside brace-level one, the only _letters_ we should see are the // format letters in FMTLETTER, so if we see one (or more) such letters, // that's an error INCCHAR; #if WITH_MAIN fprintf(stderr, "unexpected chararacters '%s' at brace-level 1 at position %d in format string %s (ignored)\n", yytext, yyextra->charno, yyextra->current_string); #else scheme_eval("print-warning", s7_make_string(S7, "unexpected chararacters '~a' at brace-level 1 at position ~a in format string ~a (ignored)"), s7_make_string(S7, yytext), s7_make_integer(S7, yyextra->charno), s7_make_string(S7, yyextra->current_string), NULL); #endif } /* fallback: if we get here, we've forgotten something * (we can't make the fallback '<*>.+' since that would longest-match others above) * This produces a flex warning 'rule cannot be matched', but that's * OK, since that's what we're expecting to be the case. */ /* <*>. { #if WITH_MAIN fprintf(stderr, "(very) unexpected chararacter '%c' at position %d in format string %s (ignored)\n", yytext[0], yyextra->charno, yyextra->current_string); #else scheme_eval("print-warning", s7_make_string(S7, "(very) unexpected chararacter ~a at position ~a in format string ~a (ignored)"), s7_make_character(S7, yytext[0]), s7_make_integer(S7, yyextra->charno), s7_make_string(S7, yyextra->current_string), NULL); #endif } */ %% yyscan_t parse_fmtstring_setup_string(fmtstring_extra_t extra, const char* s) { //fprintf(stderr, "fmtstring: %s\n", s); yyscan_t scanner; yylex_init_extra(extra, &scanner); extra->current_string = s; extra->brace_level = 0; extra->charno = 0; extra->yyscanbuf = yy_scan_string(s, scanner); yyset_lineno(1, scanner); return scanner; } void parse_fmtstring_finish(fmtstring_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 #include "util.h" YYSTYPE one_value; YYLTYPE locp; s7_scheme* S7; static void display_lexemes(yyscan_t scanner) { int l; while ((l = fmtstringlex(&one_value, &locp, scanner)) != 0) { switch (l) { case STRING: s7w("string: ", one_value, "\n"); break; case NONALPHA: s7w("non-alpha: ", one_value, "\n"); break; case FMTSYM: s7w("format specification: ", one_value, "\n"); break; case OPT_TIE: printf("optional-tie\n"); break; case REQ_TIE: printf("required-tie\n"); break; case '{': printf("{\n"); break; case '}': printf("}\n"); break; default: printf("Unexpected lexeme: %d\n", l); } } } const char* progname; void Usage(void) { fprintf(stderr, "Usage: %s \"fmtstring\"\n", progname); exit(1); } int main(int argc, char** argv) { progname = argv[0]; if (argc != 2 || argv[1][0] == '-') Usage(); S7 = s7_init(); struct fmtstring_extra_s S; yyscan_t scanner = parse_fmtstring_setup_string(&S, argv[1]); display_lexemes(scanner); parse_fmtstring_finish(&S, scanner); } #endif