/* 下記のコード部分で使用するために、先に定義しておく。 */
%x CSV_INITIAL
%x CSV_QUOTED_STRING
%x TAB_INITIAL
%x TAB_QUOTED_STRING

%{
//{{定型コード:lex.yy.l(定義部,%{〜%}内)=======================================
#include "app.h"
#pragma warning(default: 4131)	//{{2016/03/01追加:clipstd.hにてC4131の警告レベルを引き上げましたが、Bison/Flexの生成コードには旧スタイルの宣言が使われているので、Bison/Flexの生成コードのみ警告レベルを元に戻しました。}}
#include "y.tab.h"
//入力バッファサイズを定義します。
//デフォルトでは16384と定義されており、P/ECEには大きすぎます。
//P/ECEに適切な、小さなサイズ(256)に、再定義しました。
//入力バッファサイズが不足した場合は、自動的に拡張されるので、問題ありません。
//一つのトークンの大きさが、入力バッファサイズを超えたときに、拡張されるようです。
#undef  YY_BUF_SIZE
#define YY_BUF_SIZE 256
//入力ファイルの行を数えるための変数を定義します。
//「%option yylineno」を指定して、行を数えるコードを自動生成することもできますが、
//コンパイラが警告を出したりして煩わしくなるので、自分で数えることにしました。
int yylineno;
//スキャナの入力ファイルを初期化します。
//yyparse()を実行する都度、その直前に、本関数を呼び出してください。
void yylex_init(FILE* input_file) {
  if(!input_file) { DIE(); }	//入力ファイルが指定されていなければエラー停止する。
  switch(opt_FS) {
  default:DIE();//バグ
  case 'c': BEGIN CSV_INITIAL; break;	//スタート状態を初期化する。
  case 't': BEGIN TAB_INITIAL; break;	//スタート状態を初期化する。
  }
  yylineno = 1;			//入力ファイルの行を初期化する。
  yyrestart(input_file); }	//スキャナの実行を再開するようFlexに通知する。
//入力方法を変更するマクロを定義します。
//lex.yy.c内で、デフォルトのYY_INPUT()よりも先に展開されるので、#undefは不要です。
//自作CLiPライブラリの標準入出力は、CRLFをLFに変換しないので、明示的に変換します。
#define YY_INPUT(buf,result,max_size) {		\
  int i = 0; while(i < (max_size)) {		\
    int c = getc(yyin);				\
    if(c == EOF)  { break; }			\
    if(c == '\r') { continue; }			\
    (buf)[i++] = c;				\
    if(c == '\n') { yylineno++; break; }	\
  } (result) = i; }
//}}定型コード:lex.yy.l(定義部,%{〜%}内)=======================================
%}
/*{{定型コード:lex.yy.l(定義部,%{〜%}外)=====================================*/
/* Suppress the appearance of unneeded routines in the generated scanner. */
/*{{2014/02/16:当プログラム(dpcsvcc)特有の要件のための調整*/
/*■「Flexとのインターフェイス」(http://www.asahi-net.or.jp/~wg5k-ickw/html/online/flex-2.5.4/flex_6.html)より引用:*/
/*│Flexではinput()はyytextの内容を破壊しますが、Lexではyytextは変更されずそのまま残ります。これは将来のリリースで修正される予定です。*/
/*│input()と同様にunput()もyytextの内容を破壊します。*/
/*│Flex2.5では、`%array'が指定された場合は、unput()はyytextの内容を破壊しません。*/
/*%option noinput*/
/*%option nounput*/
%array
/*}}2014/02/16:当プログラム(dpcsvcc)特有の要件のための調整*/
/* Make the scanner not call yywrap() upon an end-of-file, but simply assume that there are no more files to scan. */
%option noyywrap
/*}}定型コード:lex.yy.l(定義部,%{〜%}外)=====================================*/

%%

	/*--- コマンドラインオプション'c'が指定された場合 ---*/
<CSV_INITIAL>{
\n			{ return T_RS; }			/* レコードセパレータトークンを返す。 */
,			{ return T_US; }			/* ユニットセパレータトークンを返す。 */
\"			{ BEGIN CSV_QUOTED_STRING; }		/* 引用符を含めず、引用符付きの文字列を開始する。 */
[^\n,"][^\n,]*		{ yylval = strdup(yytext);		/* 改行,',','"'以外で開始し、改行,','以外が0文字以上続く、文字列を複製する。文字列の途中では、'"'も有効な文字と見なす。 */	/* 【注意】否定文字クラスは、文字クラスのメンバ以外であれば、【'\n'も含めて】何にでもマッチします。従って、'\n'を含めたくない場合は、左記のように明示する必要があります。 */
			  if(!yylval) { DIE(); }		/* 文字列を複製するメモリが足りたことを確認する。 */
			  return T_UNIT; }			/* ユニットトークンを返す。 */
}

<CSV_QUOTED_STRING>{
\"			{ int c = input();			/*{{2014/02/16追加:引用符付きの文字列内で、二連続の引用符は、一個の引用符を表す。(例:「"abc""def"」は「abc"def」という文字列を表す。)}}*/
			  if(c == '"') { yymore(); break; }	/*{{2014/02/16追加:引用符付きの文字列内で、二連続の引用符は、一個の引用符を表す。(例:「"abc""def"」は「abc"def」という文字列を表す。)}}*/
			  if(c != EOF) { unput(c); }		/*{{2014/02/16追加:引用符付きの文字列内で、二連続の引用符は、一個の引用符を表す。(例:「"abc""def"」は「abc"def」という文字列を表す。)}}*/
			  BEGIN CSV_INITIAL;			/* 引用符付きの文字列を終了する。 */
			  if(yyleng < 1) { DIE(); }		/* 引用符を含めて、一文字以上有ることを確認する。 */
			  yytext[yyleng - 1] = '\0';		/* 引用符を削除する。 */
			  yylval = strdup(yytext);		/* 文字列を複製する。 */
			  if(!yylval) { DIE(); }		/* 文字列を複製するメモリが足りたことを確認する。 */
			  return T_UNIT; }			/* ユニットトークンを返す。 */
\n|.			{ yymore(); }				/* 引用符以外の文字を読み込む。引用符付きの文字列の中では、改行,','も有効な文字と見なす。 */
<<EOF>>			{ return T_EOF; }			/* 引用符付きの文字列の途中で、ファイル終端に達してはいけない。引用符付きの文字列の途中で、ファイル終端に達したことを示すトークンを返す。 */
}

	/*--- コマンドラインオプション't'が指定された場合 ---*/
<TAB_INITIAL>{
\n			{ return T_RS; }			/* レコードセパレータトークンを返す。 */
\t			{ return T_US; }			/* ユニットセパレータトークンを返す。 */
\"			{ BEGIN TAB_QUOTED_STRING; }		/* 引用符を含めず、引用符付きの文字列を開始する。 */
[^\n\t"][^\n\t]*	{ yylval = strdup(yytext);		/* 改行,'\t','"'以外で開始し、改行,'\t'以外が0文字以上続く、文字列を複製する。文字列の途中では、'"'も有効な文字と見なす。 */	/* 【注意】否定文字クラスは、文字クラスのメンバ以外であれば、【'\n'も含めて】何にでもマッチします。従って、'\n'を含めたくない場合は、左記のように明示する必要があります。 */
			  if(!yylval) { DIE(); }		/* 文字列を複製するメモリが足りたことを確認する。 */
			  return T_UNIT; }			/* ユニットトークンを返す。 */
}

<TAB_QUOTED_STRING>{
\"			{ int c = input();			/*{{2014/02/16追加:引用符付きの文字列内で、二連続の引用符は、一個の引用符を表す。(例:「"abc""def"」は「abc"def」という文字列を表す。)}}*/
			  if(c == '"') { yymore(); break; }	/*{{2014/02/16追加:引用符付きの文字列内で、二連続の引用符は、一個の引用符を表す。(例:「"abc""def"」は「abc"def」という文字列を表す。)}}*/
			  if(c != EOF) { unput(c); }		/*{{2014/02/16追加:引用符付きの文字列内で、二連続の引用符は、一個の引用符を表す。(例:「"abc""def"」は「abc"def」という文字列を表す。)}}*/
			  BEGIN TAB_INITIAL;			/* 引用符付きの文字列を終了する。 */
			  if(yyleng < 1) { DIE(); }		/* 引用符を含めて、一文字以上有ることを確認する。 */
			  yytext[yyleng - 1] = '\0';		/* 引用符を削除する。 */
			  yylval = strdup(yytext);		/* 文字列を複製する。 */
			  if(!yylval) { DIE(); }		/* 文字列を複製するメモリが足りたことを確認する。 */
			  return T_UNIT; }			/* ユニットトークンを返す。 */
\n|.			{ yymore(); }				/* 引用符以外の文字を読み込む。引用符付きの文字列の中では、改行,'\t'も有効な文字と見なす。 */
<<EOF>>			{ return T_EOF; }			/* 引用符付きの文字列の途中で、ファイル終端に達してはいけない。引用符付きの文字列の途中で、ファイル終端に達したことを示すトークンを返す。 */
}

	/*--- 当アプリは、INITIAL状態に遷移することは無い ---*/
\n|.			{ DIE(); }				/* 警告抑制 */

%%
