%{
#include <iostream>
#include <cstdlib>
#include <string>
#include "dpuser_utils.h"
#include "dpComplex.h"
#include "fits.h"
#include "dpuserAST.h"

static void skip_until_eol(void);
static void skip_comment(void);

int getInput(char *buf, int max_size);
extern int include_stack_ptr;
#define MAX_INCLUDE_DEPTH 10
extern FILE* include_stack[MAX_INCLUDE_DEPTH];
extern FILE* rl_instream;
#define YY_INPUT(buf,result,max_size) result=getInput(buf,max_size)

extern bool idlmode;

extern int currentColumn;
#define YY_USER_ACTION currentColumn+=yyleng;

int yyparse();
void yyerror(const char *s);

#include "astParser.h"
%}

%array

%x STRINGCON
%x FITSCON
%x INCL
%x SCRIPT


dig [0-9]

A [aA]
B [bB]
D [dD]
E [eE]
F [fF]
G [gG]
H [hH]
I [iI]
L [lL]
N [nN]
O [oO]
Q [qQ]
R [rR]
T [tT]

asc [a-zA-Z0-9]
spa [ \t]
ascspa {asc}|{spa}

num1 {dig}+\.?([eEdD][-+]?{dig}+)?
num2 {dig}*\.{dig}+([eEdD][-+]?{dig}+)?
number {num1}|{num2}


%%

^"!"[a-zA-Z0-9]+[ \t]*.*";"    system(yytext + 1);

@           BEGIN INCL;

<INCL>[ \t]*  ;

<INCL>[^ \t\n;]+  {
/* got the include file name */
	if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
		dp_output("Includes nested too deeply\n");
		exit( 1 );
	}
	include_stack[include_stack_ptr++] = rl_instream;
	include_stack[include_stack_ptr] = fopen( yytext, "r" ); 
	if (!include_stack[include_stack_ptr]) {
		dp_output("Could not open file %s\n", yytext);
		include_stack_ptr--;
	}
	rl_instream = include_stack[include_stack_ptr];
	BEGIN 0;
} 

"EOF" { 
	if (include_stack_ptr) {
		fclose(include_stack[include_stack_ptr]);
		include_stack_ptr--;
		rl_instream = include_stack[include_stack_ptr];
	} else return EXIT;
}

[0-9]+{L}?      {
                yylval.iValue = atoi(yytext);
                return INTEGER;
            }

([0-9]+\.?([eEdD][-+]?[0-9]+)?)|([0-9]*\.[0-9]+([eEdD][-+]?[0-9]+)?)|([0-9]+\.?[0-9]*[eEdD])    {
								for (int _c_ = 0; _c_ < strlen(yytext); _c_++) {
									if (yytext[_c_] == 'd') yytext[_c_] = 'e';
									else if (yytext[_c_] == 'D') yytext[_c_] = 'E';
								}
								yylval.dValue = atof(yytext);
								return REAL;
						}

([0-9]+"i")|(([0-9]+\.?([eEdD][-+]?[0-9]+)?)|([0-9]*\.[0-9]+([eEdD][-+]?[0-9]+)?)"i") {
								yylval.cValue = atof(yytext);
								return COMPLEX;
						}

[-()<>=+*/:,{}\[\].^!?#%]             return *yytext;

">="|" "{G}{E}" "    return GE;
"<="|" "{L}{E}" "    return LE;
"=="|" "{E}{Q}" "    return EQ;
"!="|" "{N}{E}" "    return NE;
"&&"|" "{A}{N}{D}" " return AND;
"||"|" "{O}{R}" "    return OR;
" "{G}{T}" "         return '>';
" "{L}{T}" "         return '<';
{N}{O}{T}" "         return '!';

" "{D}{O}" "|" "{T}{H}{E}{N}" "	;
{B}{E}{G}{I}{N}      return '{';
{E}{N}{D}{F}{O}{R}   return '}';
{E}{N}{D}{I}{F}      return '}';
{E}{N}{D}            return '}';

"+="            return PlusE;
"-="            return MinusE;
"*="            return MulE;
"/="            return DivE;
"++"            return PlusP;
"--"            return MinusM;

","" "*"/"[a-zA-Z][a-zA-Z0-9_]* yylval.s = new std::string(strchr(yytext, '/') + 1); return OPTION;

"for"" "*"("	return FOR;
"for"" "+[_[:alnum:]]     unput(yytext[yyleng-1]); return FOR;
"while"	return WHILE;

"if"" "*"("	             unput(yytext[yyleng-1]); return IF;
"if"" "+[_[:alnum:]]     unput(yytext[yyleng-1]); return IF;
"else"                   return ELSE;
"else"" "+[_[:alnum:]]   unput(yytext[yyleng-1]); return ELSE;

"exit"" "*       return EXIT;
"quit"" "*       return EXIT;
"bye"" "*        return EXIT;

"where"" "*"("	return WHERE;

^"help"" "+[_[:alnum:]][_[:alnum:]]* yylval.s = new std::string(strrchr(yytext, ' ') + 1); return HELP;
^"help"" "*          yylval.s = new std::string(""); return HELP;

"procedure"" "+[_[:alpha:]][_[:alnum:]]* yylval.s = new std::string(strrchr(yytext, ' ') + 1); return USERPRO;
"function"" "+[_[:alpha:]][_[:alnum:]]* yylval.s = new std::string(strrchr(yytext, ' ') + 1); return USERFUNC;

[_[:alnum:]][_.[:alnum:]]*     yylval.s = new std::string(yytext);  return IDENTIFIER;

\"             { BEGIN STRINGCON; yylval.sValue = new std::string(); }
<STRINGCON>\\\\   { *yylval.sValue += '\\'; }
<STRINGCON>\\t    { *yylval.sValue += '\t'; }
<STRINGCON>\\n    { *yylval.sValue += '\n'; }
<STRINGCON>\\\"   { *yylval.sValue += '"'; }
<STRINGCON>\"     { BEGIN 0; return STRING; }
<STRINGCON>\n     {
                 BEGIN 0;
								 unput(';');
								 unput('\n');
								 dp_output("Warning: Unterminated string constant\n");
								 return STRING;
					  		}
<STRINGCON>.       { *yylval.sValue += *yytext; }

\'             { BEGIN FITSCON; yylval.sValue = new std::string(); }
<FITSCON>\\\'   { *yylval.sValue += '\''; }
<FITSCON>\'     { BEGIN 0; if (idlmode) return STRING; else return FITSFILE; }
<FITSCON>\n     {
                 BEGIN 0;
								 unput(';');
								 unput('\n');
								 dp_output("Warning: Unterminated FITS file name\n");
								 return FITSFILE;
					  		}
<FITSCON>.       { *yylval.sValue += *yytext; }

[;\n]                      return ENTER;

[_[:blank:]]       ;

"//"          { skip_until_eol(); }

"/*"            { skip_comment(); }

.               dp_output("%s", yytext); yyerror(": Unknown character");

%%
int yywrap(void) {
    return 1;
}

static void skip_comment(void)
{
	int c1, c2;

	c1 = yyinput();
	c2 = yyinput();

	while (c2 != EOF && !(c1 == '*' && c2 == '/')) {
//		if (c1 == '\n')
//			++lineno;
		c1 = c2;
		c2 = yyinput();
	}
}

static void skip_until_eol(void)
{
	int c;

	while ((c = yyinput()) != EOF && c != '\n')
		;
	unput(';');
	unput('\n');
}

