A	[a-zA-Z_]
N	[a-zA-Z0-9_.]
W	[ \t\n]*

%{
#undef	input
#undef	unput

#define input()	 	parser_input.f_input()
#define	unput(c)	parser_input.f_unput(c)

static char	      * buf_s;
static size_t		buf_n, buf_m;
static int		paren_lev = 0;

struct list * f(char const *, ...);

#define CHUNK		1024

%}

%start	CODE

%%

<INITIAL>\'([^\\\']|(\\.))\' {
		yylval.list = f("'", yytext);
		return ID;
	}

<INITIAL>(\'([^\\\']|(\\.))*\')|(\"([^\\\"]|(\\.))*\") {
		yylval.list = f("'", yytext);
		return CCODE;
	}

<INITIAL>"error"	{
		char * p = yytext, tmp;
		while (*p && !isspace(*p)) p++;
		tmp = *p; *p = 0;
		yylval.list = f("'", yytext);
		*p = tmp;
		yylval.list->next = f("w", p);
		return CCODE;
	}

<INITIAL>{A}{N}*{W}:	{
		char tmp, *p = yytext, *q, * r;

		for (q = p; *q && !isspace(*q) && *q != ':'; q++) ;
		tmp=*q, *q='\0', yylval.list = f("i", yytext), *q=tmp;

		for (r = q; *r && isspace(*r); r++) ;
		tmp=*r, *r='\0', yylval.list->next = f("ws", q, ":");
		*r = tmp;

		def_nonterminal(yylval.list);

		return PREFIX;
	}

<INITIAL>\<{W}{A}{N}*{W}\>	{
		char tmp, *p = yytext + 1, *q, * r;

		while (*p && isspace(*p)) p++;
		tmp=*p, *p=0, yylval.list=f("w", p), *p = tmp;

		for (q = p; *q && !isspace(*q) && *q != '>'; q++) ;
		tmp=*q, *q='\0', yylval.list->next = f("T", p), *q=tmp;

		for (r = q; *r && isspace(*r); r++) ;
		tmp=*r, *r='\0', yylval.list->next->next = f("w", q), *r=tmp;

		yylval.list = f("sls", "<", yylval.list, ">");
		return TAG;
	}

<INITIAL>{A}{N}* 	{ yylval.list = f("i", yytext); return ID; }

<INITIAL>%%	  	return MARK;

<INITIAL>\/\*		{ buf_n = 0;
			  put('/'); put('*');
			  skipahead('*', '/', "comment");
			  put('*'); put('/');
			  yylval.list = f("w", buf_s);
			  return WS;
			}

<INITIAL>%\{ 		{ buf_n = 0;
			  skipahead('%', '}', "%{...%}");
			  yylval.list = f("scs","%{", buf_s, "%}");
			  return CURLS;
			}
<INITIAL>%start			return START;
<INITIAL>%union 	{	
				return UNION;
			}
<INITIAL>%token		{
			  	yylval.list = f("s", yytext);
				return TOKEN;
			}
<INITIAL>%(right|left|nonassoc|type)	{
			  	yylval.list = f("s", yytext);
				return RWORD;
			}

<INITIAL>%prec	  		return PREC;

<INITIAL>[ \t\n]+	{
			  yylval.list = f("w", yytext);
			  return WS;
			}

<INITIAL>[0-9]+		{ yylval.list = f("c", yytext); return NUMBER;	}
<INITIAL>\{  		{ BEGIN CODE; paren_lev = 1; buf_n = 0; put('{'); }

<INITIAL>[:|<>;] 	return *yytext;

<INITIAL>.		{ yylval.list = f("s", yytext); return CCODE; }

<CODE>\"([^\\\"]|(\\.))*\" { append(yytext);				}
<CODE>\'([^\\\']|(\\.))*\' { append(yytext);				}
<CODE>\/\* 		{ put('/'); put('*');
			  skipahead('*', '/', "comment");
			  put('*'); put('/');				}
<CODE>\{ 		{ ++paren_lev; put('{');			}
<CODE>\}  		{ put('}');
			  if (!--paren_lev) {
				BEGIN INITIAL;
				yylval.list = f("c", buf_s);
				return CCODE;
			  } }
<CODE>(.|\n)		{ put(*yytext); 				}

%%

put(c) int c;
{
	if (!buf_m) buf_s = TMALLOC(char, buf_m = CHUNK);
	if (buf_n + 1 >= buf_m) buf_s = TREALLOC(char, buf_s, buf_m += CHUNK);

	buf_s[buf_n++] = c;
	buf_s[buf_n] = 0;
}

append(str) char const * str;
{
	while (*str) put(*str++);
}

skipahead(c1, c2, section) int c1, c2; char const * section;
{
	int c;

	assert(CHUNK > 1);

	while ((c = input()) && c != EOF) {
		if (c == c1) {
			if ((c = input()) == EOF || !c) goto eof_in;
			if (c == c2) {
				buf_s[buf_n] = 0; 
				return;
			}
			unput(c);
			c = c1;
		}
		put(c);
 	}
	if (c1 == EOF) {
		buf_s[buf_n] = 0;
		return;
	}
eof_in: parser_error("EOF in %s\n", section);
}