/* lexer.l */

/*
 * The incl state is used for picking up the name of an include file
 */
%x incl

%x comment
%x str

%{
#include <stdio.h>
#include <string.h>
#include <err.h>
#include "types.h"
#include "y.tab.h"

#define MAX_INCLUDE_DEPTH 10
YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
int lineno_stack[MAX_INCLUDE_DEPTH];
int include_stack_ptr = 0;

#define MAX_STR_CONST 1024
char string_buf[MAX_STR_CONST];
char *string_buf_ptr;

%}

%option yylineno

%%

%include	BEGIN(incl);
"/*"		BEGIN(comment);

<comment>{
	[^*]*		/* eat anything that's not a '*' */
	"*"+[^*/]*	/* eat up '*'s not followed by '/'s */
	"*"+"/"		BEGIN(INITIAL);
}

\"	string_buf_ptr = string_buf; BEGIN(str);

<str>{
	\" {
		BEGIN(INITIAL);
		*string_buf_ptr = '\0';
		yylval.Tstring = strdup(string_buf);
		return STRING;
	}
	\n {
		fprintf(stderr, "Unterminated string at line %d\n", yylineno);
		exit(1);
	}
	\\[0-7]{1,3} {
		/* octal escape sequence */
		int result;
		(void) sscanf(yytext + 1, "%o", &result);
		if (result > 0xff) {
			fprintf(stderr,
			    "octal escape out of bounds at line %d\n",
			    yylineno);
			exit(1);
		}
	}
	\\[0-9]+ {
		/* bad escape sequence */
		fprintf(stderr, "bad escape sequence at line %d\n", yylineno);
		exit(1);
	}
	\\n	*string_buf_ptr++ = '\n';
	\\t	*string_buf_ptr++ = '\t';
	\\r	*string_buf_ptr++ = '\r';
	\\b	*string_buf_ptr++ = '\b';
	\\f	*string_buf_ptr++ = '\f';

	\\(.|\n)	*string_buf_ptr++ = yytext[1];

	[^\\\n\"]+ {
		char *yptr = yytext;

		while (*yptr)
			*string_buf_ptr++ = *yptr++;
	}
}

function	return TOKFUNCTION;
fndefint	return TOKFNDEFINT;
fndefext	return TOKFNDEFEXT;
constant	return TOKCONSTANT;
while		return TOKWHILE;
if		return TOKIF;
else		return TOKELSE;
switch		return TOKSWITCH;
case		return TOKCASE;
default		return TOKDEFAULT;
break		return TOKBREAK;
return		return TOKRETURN;
==		return TOKEQ;
!=		return TOKNE;
\<=		return TOKLE;
\>=		return TOKGE;
&&		return TOKAND;
\|\|		return TOKOR;

[0-9]+          yylval.Tinteger = atoi(yytext); return NUMBER;
-[0-9]+         yylval.Tinteger = atoi(yytext); return NUMBER;
[0-9]+\.[0-9]+	yylval.Treal = atof(yytext); return REAL;
-[0-9]+\.[0-9]+	yylval.Treal = atof(yytext); return REAL;
[_a-zA-Z0-9]+	yylval.Tstring = strdup(yytext); return IDENTIFIER;
%[_a-zA-Z0-9]+	yylval.Tstring = strdup(yytext); return IDENTIFIER;
$[_a-zA-Z0-9]+	yylval.Tstring = strdup(yytext); return IDENTIFIER;

[ \t\n]+	/* eat whitespace */

.		return (int) yytext[0];

<incl>[ \t]*	/* eat the whitespace */
<incl>[^ \t\n]+	{ /* got the include filename */
		if (include_stack_ptr >= MAX_INCLUDE_DEPTH) {
			fprintf(stderr, "Includes nested too deeply");
			exit(1);
		}
		include_stack[include_stack_ptr] = YY_CURRENT_BUFFER;
		lineno_stack[include_stack_ptr++] = yylineno;
		yyin = fopen(yytext, "r");
		yylineno = 1;
		if (!yyin)
			err(1, "%s", yytext);
		yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
		BEGIN(INITIAL);
	}

<<EOF>>		{
			if (--include_stack_ptr < 0) {
				yyterminate();
			} else {
				yy_delete_buffer(YY_CURRENT_BUFFER);
				yy_switch_to_buffer(
				    include_stack[include_stack_ptr]);
				yylineno = lineno_stack[include_stack_ptr];
			}
		}

%%

