/*
 * Lexer for .aux files
 *
 * This file is part of Beastie <https://purl.org/nxg/dist/beastie>
 * SPDX-FileCopyrightText: 2023 Norman Gray <https://nxg.me.uk>
 * SPDX-License-Identifier: BSD-2-Clause
 */

%top{
#if __GNUC__
// for fileno
#define _XOPEN_SOURCE 600
#endif
}

%{
#include <ctype.h>
#include "beastie.h"
#include "util.h"

#include "parse-aux.h"
#include "parse-aux.tab.h"

#ifndef WITH_MAIN
#define WITH_MAIN 0
#endif

%}

%option prefix="aux" reentrant noyywrap nounput yylineno bison-bridge bison-locations
%option extra-type="aux_extra_t"

ALPHABETIC	[a-zA-Z@]
STRINGCONTENTS	[^%\\{} \t\n][^%\\{}]*
DIGIT		[0-9]
OWS		[[:space:]]*
WS		[[:space:]]+

%%

{OWS}"\\"{ALPHABETIC}+{OWS} 	{
    char *p = yytext;
    while (isspace(*p)) p++;
    char* start = p+1;
    while (*p != '\0' && !isspace(*p)) p++;

    *yylval = s7_make_string_with_length(S7, start, p-start);
    return CMD;
}

"{" {
    *yylval = scan_to_matching_char(&input, yyscanner, yyget_lineno(yyscanner), NULL,
                                    '}', 0);
    return BRACEDSTRING;
}

"%".*			return WHITESPACE;
{STRINGCONTENTS}+ 	{ *yylval = s7_make_string(S7, yytext); return STRING; }
{WS}			return WHITESPACE;

. { fprintf(stderr, "Unexpected character '%c' at line %d of .aux\n", yytext[0], yyget_lineno(yyscanner)); }

%%

yyscan_t parse_aux_setup_file(aux_extra_t extra, FILE* f)
{
    yyscan_t scanner;
    yylex_init_extra(extra, &scanner);
    extra->yyscanbuf = 0;

    if (f != NULL) {
        yyset_in(f, scanner);
    }

    return scanner;
}
yyscan_t parse_aux_setup_string(aux_extra_t extra, const char* s)
{
    yyscan_t scanner;
    yylex_init_extra(extra, &scanner);

    extra->yyscanbuf = (void*)yy_scan_string(s, scanner);
    yyset_lineno(1, scanner);

    return scanner;
}
void parse_aux_finish(aux_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 <stdio.h>
#include <unistd.h>
#include "util.h"

s7_scheme* S7;

static void display_lexemes(yyscan_t scanner)
{
    int l;
    YYSTYPE one_value;
    YYLTYPE locp;

    while ((l = auxlex(&one_value, &locp, scanner)) != 0) {
        switch (l) {
          case CMD:
            s7w("command: ", one_value, "\n");
            break;
          case STRING:
            s7w("string:<", one_value, ">\n");
            break;
          case BRACEDSTRING:
            s7w("bracedstring:<", one_value, ">\n");
            break;
          case WHITESPACE:
            printf("WHITESPACE\n");
            break;
          default:
            printf("Unexpected lexeme: %d\n", l);
        }
    }
}

const char* progname;
void Usage(void)
{
    fprintf(stderr, "Usage: %s [foo.aux]\n", progname);
    exit(1);
}

int main(int argc, char** argv)
{
    const char* infilename;
    progname = argv[0];

    if (argc > 0) {
        switch (argc) {
          case 1:
            infilename = NULL;
            break;
          case 2:
            if (argv[1][0] == '-') Usage();
            infilename = argv[1];
            break;
          default:
            Usage();
        }
    }

    S7 = s7_init();

    FILE* in;
    if (infilename) {
        in = fopen(argv[1], "r");
        if (in == NULL) {
            fprintf(stderr, "Can't open file %s to read\n", infilename);
            exit(1);
        }
    } else {
        in = stdin;
    }

    struct aux_extra_s S;
    yyscan_t scanner = parse_aux_setup_file(&S, in);
    display_lexemes(scanner);
    parse_aux_finish(&S, scanner);

    fclose(in);
}
#endif
