/* Copyright 2002 Rene Rivera.
** Distributed under the Boost Software License, Version 1.0.
** (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

/*
# yyacc - yacc wrapper
#
# Allows tokens to be written as `literal` and then automatically 
# substituted with #defined tokens.
#
# Usage:
#    yyacc file.y filetab.h file.yy 
#
# inputs:
#    file.yy        yacc grammar with ` literals
#
# outputs:
#    file.y        yacc grammar
#    filetab.h    array of string <-> token mappings
#
# 3-13-93
#    Documented and p moved in sed command (for some reason,
#    s/x/y/p doesn't work).
# 10-12-93
#    Take basename as second argument.
# 12-31-96
#    reversed order of args to be compatible with GenFile rule
# 11-20-2002
#    Reimplemented as a C program for portability. (Rene Rivera)
*/

void print_usage();
char * copy_string(char * s, int l);
char * tokenize_string(char * s);
int cmp_literal(const void * a, const void * b);

typedef struct
{
    char * string;
    char * token;
} literal;

int main(int argc, char ** argv)
{
    int result = 0;
    if (argc != 4)
    {
        print_usage();
        result = 1;
    }
    else
    {
        FILE * token_output_f = 0;
        FILE * grammar_output_f = 0;
        FILE * grammar_source_f = 0;
        
        grammar_source_f = fopen(argv[3],"r");
        if (grammar_source_f == 0) { result = 1; }
        if (result == 0)
        {
            literal literals[1024];
            int t = 0;
            char l[2048];
            while (1)
            {
                if (fgets(l,2048,grammar_source_f) != 0)
                {
                    char * c = l;
                    while (1)
                    {
                        char * c1 = strchr(c,'`');
                        if (c1 != 0)
                        {
                            char * c2 = strchr(c1+1,'`');
                            if (c2 != 0)
                            {
                                literals[t].string = copy_string(c1+1,c2-c1-1);
                                literals[t].token = tokenize_string(literals[t].string);
                                t += 1;
                                c = c2+1;
                            }
                            else
                                break;
                        }
                        else
                            break;
                    }
                }
                else
                {
                    break;
                }
            }
            literals[t].string = 0;
            literals[t].token = 0;
            qsort(literals,t,sizeof(literal),cmp_literal);
            {
                int p = 1;
                int i = 1;
                while (literals[i].string != 0)
                {
                    if (strcmp(literals[p-1].string,literals[i].string) != 0)
                    {
                        literals[p] = literals[i];
                        p += 1;
                    }
                    i += 1;
                }
                literals[p].string = 0;
                literals[p].token = 0;
                t = p;
            }
            token_output_f = fopen(argv[2],"w");
            if (token_output_f != 0)
            {
                int i = 0;
                while (literals[i].string != 0)
                {
                    fprintf(token_output_f,"    { \"%s\", %s },\n",literals[i].string,literals[i].token);
                    i += 1;
                }
                fclose(token_output_f);
            }
            else
                result = 1;
            if (result == 0)
            {
                grammar_output_f = fopen(argv[1],"w");
                if (grammar_output_f != 0)
                {
                    int i = 0;
                    while (literals[i].string != 0)
                    {
                        fprintf(grammar_output_f,"%%token %s\n",literals[i].token);
                        i += 1;
                    }
                    rewind(grammar_source_f);
                    while (1)
                    {
                        if (fgets(l,2048,grammar_source_f) != 0)
                        {
                            char * c = l;
                            while (1)
                            {
                                char * c1 = strchr(c,'`');
                                if (c1 != 0)
                                {
                                    char * c2 = strchr(c1+1,'`');
                                    if (c2 != 0)
                                    {
                                        literal key;
                                        literal * replacement = 0;
                                        key.string = copy_string(c1+1,c2-c1-1);
                                        key.token = 0;
                                        replacement = (literal*)bsearch(
                                            &key,literals,t,sizeof(literal),cmp_literal);
                                        *c1 = 0;
                                        fprintf(grammar_output_f,"%s%s",c,replacement->token);
                                        c = c2+1;
                                    }
                                    else
                                    {
                                        fprintf(grammar_output_f,"%s",c);
                                        break;
                                    }
                                }
                                else
                                {
                                    fprintf(grammar_output_f,"%s",c);
                                    break;
                                }
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                    fclose(grammar_output_f);
                }
                else
                    result = 1;
            }
        }
        if (result != 0)
        {
            perror("yyacc");
        }
    }
    return result;
}

static char * usage[] = {
    "yyacc <grammar output.y> <token table output.h> <grammar source.yy>",
    0 };

void print_usage()
{
    char ** u;
    for (u = usage; *u != 0; ++u)
    {
        fputs(*u,stderr); putc('\n',stderr);
    }
}

char * copy_string(char * s, int l)
{
    char * result = (char*)malloc(l+1);
    strncpy(result,s,l);
    result[l] = 0;
    return result;
}

char * tokenize_string(char * s)
{
    char * result;
    char * literal = s;
    int l;
    int c;
    
    if (strcmp(s,":") == 0) literal = "_colon";
    else if (strcmp(s,"!") == 0) literal = "_bang";
    else if (strcmp(s,"!=") == 0) literal = "_bang_equals";
    else if (strcmp(s,"&&") == 0) literal = "_amperamper";
    else if (strcmp(s,"&") == 0) literal = "_amper";
    else if (strcmp(s,"+") == 0) literal = "_plus";
    else if (strcmp(s,"+=") == 0) literal = "_plus_equals";
    else if (strcmp(s,"||") == 0) literal = "_barbar";
    else if (strcmp(s,"|") == 0) literal = "_bar";
    else if (strcmp(s,";") == 0) literal = "_semic";
    else if (strcmp(s,"-") == 0) literal = "_minus";
    else if (strcmp(s,"<") == 0) literal = "_langle";
    else if (strcmp(s,"<=") == 0) literal = "_langle_equals";
    else if (strcmp(s,">") == 0) literal = "_rangle";
    else if (strcmp(s,">=") == 0) literal = "_rangle_equals";
    else if (strcmp(s,".") == 0) literal = "_period";
    else if (strcmp(s,"?") == 0) literal = "_question";
    else if (strcmp(s,"?=") == 0) literal = "_question_equals";
    else if (strcmp(s,"=") == 0) literal = "_equals";
    else if (strcmp(s,",") == 0) literal = "_comma";
    else if (strcmp(s,"[") == 0) literal = "_lbracket";
    else if (strcmp(s,"]") == 0) literal = "_rbracket";
    else if (strcmp(s,"{") == 0) literal = "_lbrace";
    else if (strcmp(s,"}") == 0) literal = "_rbrace";
    else if (strcmp(s,"(") == 0) literal = "_lparen";
    else if (strcmp(s,")") == 0) literal = "_rparen";
    l = strlen(literal)+2;
    result = (char*)malloc(l+1);
    for (c = 0; literal[c] != 0; ++c)
    {
        result[c] = toupper(literal[c]);
    }
    result[l-2] = '_';
    result[l-1] = 't';
    result[l] = 0;
    return result;
}

int cmp_literal(const void * a, const void * b)
{
    return strcmp(((const literal *)a)->string,((const literal *)b)->string);
}
