%{
#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
#include "yaccdef.h"

#define	GLOBAL
#include "global.h"

#define	CHUNK_SIZE	(64 * 1204L)

void *malloc(size_t);
char *copy_strip(char *s);
void position(void);
void yyerror(char *);

%}

CID		([a-zA-Z_][a-zA-Z0-9_]*)
CLID		(\'[A-Za-z*/\-+%$][A-Za-z0-9%$_]*\')
LITCHAR		([*();,])
SPACE		([ \t\n]+)

%s COMMENT
%%

<INITIAL>"foreign"	{
	position();
	return(SYM_FOREIGN);
}

<INITIAL>"module"	{
	position();
	return(SYM_MODULE);
}

<INITIAL>"void"		{
	position();
	return(SYM_VOID);
}

<INITIAL>"int"		{
	position();
	return(SYM_INT);
}

<INITIAL>"BOOL" {
	position();
	return SYM_BOOL;
}

<INITIAL>"double"	{
	position();
	return(SYM_DOUBLE);
}

<INITIAL>"CLEAN_STRING"	{
	position();
	return(SYM_CLEAN_STRING);
}

<INITIAL>"CLEAN_FILE"	{
	position();
	return(SYM_CLEAN_FILE);
}

<INITIAL>"*int"		{
	position();
	return(SYM_UNQ_INT);
}

<INITIAL>"*BOOL" {
	position();
	return SYM_UNQ_BOOL;
}

<INITIAL>"*double"	{
	position();
	return(SYM_UNQ_DOUBLE);
}

<INITIAL>"*CLEAN_STRING"	{
	position();
	return(SYM_UNQ_CLEAN_STRING);
}

<INITIAL>"*CLEAN_FILE"	{
	position();
	return(SYM_UNQ_CLEAN_FILE);
}

<INITIAL>{CID}		{
	yylval.string = concat(yytext,CEND);
	position();
	return(C_IDENT);
}

<INITIAL>{CLID}		{
	yylval.string = copy_strip(yytext);
	position();
	return(CLEAN_IDENT);
}

<INITIAL>{LITCHAR}	{
	position();
	return(yytext[0]);
}

<INITIAL>"/*"		{
	position();
	BEGIN COMMENT;
}

<INITIAL>"||".*\n	{
	position();
}

<COMMENT>"*/"		{
	position();
	BEGIN INITIAL;
}

<COMMENT>.		{
	position();
}

{SPACE}			{
	position();
}

<INITIAL>.		{
	position();
	fprintf(stderr,"Bad character: \'%s\'\n", yytext);
	return(BAD_CHAR);
}

%%

static size_t size_chunk = 0;
static char *chunk;

char *(alloc) (size_t size)
{
    char *res;
    size_t t;
    static size_t chunk_size = CHUNK_SIZE;

    if (size > size_chunk) {
        t = size * 3;
	if (t > chunk_size)
	    chunk_size = t;
	chunk = malloc (chunk_size);
	if (chunk == NULL) {
	    fprintf (stderr, "No more memory\n");
	    exit (1);
	};
	size_chunk = chunk_size;
    };
    res = chunk;
    chunk += size;
    size_chunk -= size;
    return (res);
}

char *copy_strip (char *s)
{
    char *new;
    size_t l = 1;
    char *t;

    s++;
    for (t = s; *t++;)
	l++;
    new = alloc (l);
    t = new;
    while (*t++ = *s++);
    t[-2] = '\0';
    return (new);
}

char *concat (char *s1,...)
{
    size_t len = 1;
    char *s, *t;
    char *new;
    va_list v;

    s = s1;
    va_start (v, s1);
    while (s != CEND) {
	while (*s++)
	    len++;
	s = va_arg (v, char *);
    };
    va_end (v);
    t = new = alloc (len);
    s = s1;
    va_start (v, s1);
    while (s != CEND) {
	while (*t++ = *s++);
	t--;
	s = va_arg (v, char *);
    };
    va_end (v);
    return (new);
}

static unsigned long cpos = 0;
static unsigned long lpos = 1;
static unsigned long prevcpos, prevlpos;

void position (void)
{
    char *s = yytext, c;

    prevcpos = cpos;
    prevlpos = lpos;
    while (c = *s++) {
	switch (c) {
	case '\n':
	    cpos = 0;
	    lpos++;
	    break;
	case '\t':
	    cpos = (cpos + 8) & ~(unsigned) 7;
	    break;
	default:
	    cpos++;
	}
    }
}

static int errcnt = 0;

void yyerror (char *msg)
{
    fprintf (stderr, "error at line %lu char %lu: %s\n",
	     prevlpos, prevcpos, msg);
    errcnt++;
}

char *basename (char *path)
{
    char *base = path, c;

    while (c = *path++)
	if (c == '/')
	    base = path;
    return (base);
}

char *newextension (char *name, char *extension)
{
    char *p = concat (name, CEND);
    char *dot, *q, *end = p;

    while (*end)
	end++;
    dot = end;
    for (q = p; *q; q++)
	switch (*q) {
	case '.':
	    dot = q;
	    break;
	case '/':
	    dot = end;
	    break;
	};
    *dot = '\0';
    return (concat (p, extension, CEND));
}

void main (int argc, char *argv[])
{
    char *new;

    if (argc != 2)
        goto help;
    yyin = fopen (argv[1], "r");
    if (yyin == NULL) {
	    perror (argv[1]);
        exit (1);
    };

    new = newextension (argv[1], ".dcl");
    definition = fopen (new, "w");
    if (definition == NULL) {
	    perror (new);
	    exit (1);
    };

    new = newextension (argv[1], ".icl");
    impl = fopen (new, "w");
    if (impl == NULL) {
        perror (new);
        exit (1);
    };

    if (yyparse () != 0 || errcnt > 0) {
        fprintf (stderr, "Translation aborted\n");
        exit (1);
    };

    fclose (definition);
    fclose (impl);
    exit (0);

help:
    fprintf (stderr, "Usage: %s <input>\n", basename (argv[0]));
    exit (1);
}

int yywrap()
{
	return (1);
}
