/* * This is the lexer part of a parser for .bst files. * * I would like to be able to preserve comments, but it's unexpectedly * difficult to do in a comprehensive list of cases, so the following * simply discards comments at lexing time (I would like to preserve * comments so that I can round-trip a .bst file to bst-scm * (parse-tree) and back to .bst. Why? Because I'd like to be able * to edit the bst-scm intermediate and re-serialise, because that's a * better way of generating the urlbst outputs than hacking away at * the .bst source with Perl). Since it's probably _only_ me that * wants to preserve the comments, we can bear to lose them, at least * for the moment. * * To preserve comments, we need to have the `/%/` rule send * `scheme_trimmed_string(yytext)` to `*yylval`, and return a token `COMMENT`, * and make corresponding changes in parse-bst.y. * * This file is part of Beastie * SPDX-FileCopyrightText: 2023 Norman Gray * SPDX-License-Identifier: BSD-2-Clause */ %top{ #if __GNUC__ && !defined(__clang__) // for fileno #define _XOPEN_SOURCE 600 #endif } %{ #include "config.h" #include #include #if HAVE_ALLOCA_H #include #endif #include "beastie.h" #include "util.h" #include "unicode.h" #include "unicode-scm.h" #include "parse-bst.h" #include "parse-bst.tab.h" #ifndef WITH_MAIN #define WITH_MAIN 0 #endif #define LINENO s7_make_integer(S7, yyget_lineno(yyscanner)) %} /* optional white space */ OWS [[:space:]]* WS [[:space:]]+ %option prefix="bst" reentrant bison-bridge bison-locations %option noyywrap nounput yylineno %option extra-type="bst_extra_t" /* btxhak: Variable and function names may not begin with a * numeral and may not contain any of the ten restricted * characters on page 143 of the LaTeX book, but may otherwise * contain any printing characters. Also, BibTEX considers upper- * and lower-case equivalents to be the same. * * The following seems a useful subset; I should probably make a point * of expanding this list. */ NAME [A-Za-z][A-Za-z0-9.$_-]* %% {OWS}"%".* { input(yyscanner); // gobble a newline // ...but otherwise discard } /* tokens BO and BC are '{' and '}', but with a semantic value which is their line number */ {OWS}"{"{OWS} { *yylval = LINENO; return BO; } {OWS}"}"{OWS} { *yylval = LINENO; return BC; } {OWS}"'"{WS} { fprintf(stderr, "Space after quotes is not permitted (line %d)\n", yyget_lineno(yyscanner)); return QUOTE; } {OWS}"'" return QUOTE; {OWS}":="{OWS} return ASSIGNMENT; {OWS}"="{OWS} return '='; {OWS}"<"{OWS} return '<'; {OWS}">"{OWS} return '>'; {OWS}"*"{OWS} return '*'; {OWS}"+"{OWS} return '+'; {OWS}"-"{OWS} return '-'; {OWS}"#"-?[0-9]+{OWS} { const char* p = strchr(yytext, '#'); *yylval = s7_make_integer(S7, strtol(&p[1], NULL, 10)); return NUMBER; } {OWS}["][^"]*["]{OWS} { const char* startp = strchr(yytext, '"'); startp++; const char* endp = strrchr(yytext, '"'); const char* errmsg; ustring_t us = make_ustring(&errmsg); if (us == NULL) { return_beastie_error(S7, "bst-lex: can't create ustring: %s", *errmsg); } us = ustring_append_utf8_with_length(us, (const uint8_t*)startp, endp-startp, &errmsg); if (us == NULL) { return_beastie_error(S7, "bst-lex: can't append ustring: %s", *errmsg); } *yylval = make_ustring_obj(S7, us); return STRING; } /* spot commands, and return the appropriate token type, with the * 'value' of the command being its line number */ {OWS}[eE][nN][tT][rR][yY]{OWS} { *yylval = LINENO; return CMD_ENTRY; } {OWS}[eE][xX][eE][cC][uU][tT][eE]{OWS} { *yylval = LINENO; return CMD_EXECUTE; } {OWS}[fF][uU][nN][cC][tT][iI][oO][nN]{OWS} { *yylval = LINENO; return CMD_FUNCTION; } {OWS}[iI][nN][tT][eE][gG][eE][rR][sS]{OWS} { *yylval = LINENO; return CMD_INTEGERS; } {OWS}[iI][tT][eE][rR][aA][tT][eE]{OWS} { *yylval = LINENO; return CMD_ITERATE; } {OWS}[mM][aA][cC][rR][oO]{OWS} { *yylval = LINENO; return CMD_MACRO; } {OWS}[rR][eE][aA][dD]{OWS} { *yylval = LINENO; return CMD_READ; } {OWS}[rR][eE][vV][eE][rR][sS][eE]{OWS} { *yylval = LINENO; return CMD_REVERSE; } {OWS}[sS][oO][rR][tT]{OWS} { *yylval = LINENO; return CMD_SORT; } {OWS}[sS][tT][rR][iI][nN][gG][sS]{OWS} { *yylval = LINENO; return CMD_STRINGS; } /* Coerce all tokens to lowercase */ {NAME}{OWS} { char* b = alloca(yyleng+1); for (int i=0; iyyscanbuf = 0; if (path == NULL) { extra->path = NULL; } else { extra->path = strdup(path); } extra->f = f; if (f != NULL) { yyset_in(f, scanner); } return scanner; } yyscan_t parse_bst_setup_string(bst_extra_t extra, const char* s) { yyscan_t scanner; yylex_init_extra(extra, &scanner); extra->yyscanbuf = yy_scan_string(s, scanner); const int buflen = 20; const char fmt[] = ""; const size_t totlen = buflen + sizeof(fmt); char* source_legend = malloc(totlen+1); snprintf(source_legend, totlen, fmt, buflen, s); extra->path = source_legend; extra->f = NULL; yyset_lineno(1, scanner); return scanner; } void parse_bst_finish(bst_extra_t extra, yyscan_t scanner) { if (extra->yyscanbuf) { yy_delete_buffer((YY_BUFFER_STATE)extra->yyscanbuf, scanner); extra->yyscanbuf = NULL; } if (extra->path) free(extra->path); if (extra->f) fclose(extra->f); yylex_destroy(scanner); } #if WITH_MAIN #include #include "s7.h" #include "util.h" YYSTYPE one_value; YYLTYPE locp; s7_scheme* S7; static void display_lexemes(yyscan_t scanner) { int l; while ((l = bstlex(&one_value, &locp, scanner)) != 0) { //printf("l=%d\n", l); switch (l) { case '=': case '<': case '>': case '*': case '+': case '-': printf("%c\n", l); break; case NUMBER: s7w("number(", one_value, ")\n"); break; case QUOTE: printf("quote\n"); break; case TOKEN: s7w("token(", one_value, ")\n"); break; case STRING: s7w("string(", one_value, ")\n"); break; case ASSIGNMENT: printf(":=\n"); break; case BO: printf("{\n"); break; case BC: printf("}\n"); break; case CMD_ENTRY: printf("ENTRY\n"); break; case CMD_EXECUTE: printf("EXECUTE\n"); break; case CMD_FUNCTION: printf("FUNCTION\n"); break; case CMD_INTEGERS: printf("INTEGERS\n"); break; case CMD_ITERATE: printf("ITERATE\n"); break; case CMD_MACRO: printf("MACRO\n"); break; case CMD_READ: printf("READ\n"); break; case CMD_REVERSE: printf("REVERSE\n"); break; case CMD_SORT: printf("SORT\n"); break; case CMD_STRINGS: printf("STRINGS\n"); break; default: printf("Unexpected lexeme: %d\n", l); } } } const char* progname; void Usage(void) { fprintf(stderr, "Usage: %s [foo.bst]\n", progname); exit(1); } int main(int argc, char** argv) { const char* bstin = NULL; progname = argv[0]; switch (argc) { case 1: bstin = NULL; break; case 2: if (argv[1][0] == '-') Usage(); bstin = argv[1]; break; default: Usage(); } S7 = s7_init(); unicode_load_hook(S7, s7_nil(S7)); struct bst_extra_s S; yyscan_t scanner = parse_bst_setup_file(&S, bstin); if (scanner == NULL) { fprintf(stderr, "%s: can't open file %s to read\n", argv[0], (bstin == NULL ? "" : bstin)); exit(1); } display_lexemes(scanner); parse_bst_finish(&S, scanner); } #endif