%{
//{{定型コード:lex.yy.l(定義部,%{〜%}内)=======================================
#ifdef  PIECE
#include "app.h"
#else //PIECE
#include "winapp.h"
#pragma warning(default: 4131)	//{{2016/03/01追加:clipstd.hにてC4131の警告レベルを引き上げましたが、Bison/Flexの生成コードには旧スタイルの宣言が使われているので、Bison/Flexの生成コードのみ警告レベルを元に戻しました。}}
#endif//PIECE
#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(); }	//入力ファイルが指定されていなければエラー停止する。
  BEGIN INITIAL;		//スタート状態を初期化する。
  yylineno = 1;			//入力ファイルの行を初期化する。
  yyrestart(input_file); }	//スキャナの実行を再開するようFlexに通知する。
//入力方法を変更するマクロを定義します。
//lex.yy.c内で、デフォルトのYY_INPUT()よりも先に展開されるので、#undefは不要です。
//自作CLiPライブラリの標準入出力は、CRLFをLFに変換しないので、明示的に変換します。
//'popen'キーワードによる入力バッファ切り替え中ならば、入力ファイルの行を増やさない事にしました。	⇒//{{2016/09/24変更:エラーメッセージに現在のファイル名も表示する変更に伴いロケーションスタックを追加したので、'popen'キーワードによる入力バッファ切り替え中も入力ファイルの行を増やせるようになった。}}
// * Wed Jul 01 21:52:52 JST 2015 Naoyuki Sawa
// - 行末のバックスラッシュで行継続する機能を追加しました。
//   有限状態機械マップスクリプトはCプリプロセッサと相性が悪く、当ツールにかける前にCプリプロセッサを通してCプリプロセッサによる行継続を適用する事が難しい事が判ったからです。
//   有限状態機械マップスクリプト内の'%{〜%}'の中でCプリプロセッサ疑似命令を使用していると、Cソースファイルに出力する前に展開されてしまい、予期しない結果になるからです。
//   上記の問題を避けるため、当ツール自体に、行末のバックスラッシュで行継続する機能を追加する事にしました。
// - '%{〜%}','[`〜`]','{`〜`}'の中は、字句解析によって行継続しますので、行末のバックスラッシュ指定は不要です。
#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; }							\
    if(c == '\\') { int d = getc(yyin);						\
                    if(d == '\r') { d = getc(yyin); }				\
                    if(d == '\n') { c = ' '; } else { ungetc(d, yyin); } }	\
    (buf)[i++] = c;								\
    if(c == '\n') { /*if(!popen_stack_ptr)*/ { yylineno++; } break; }		\
  } (result) = i; }
//'popen'キーワードで入力バッファを切り替えた時に、切り替え前の入力バッファを記憶しておくスタックです。
//「flex version 2.5.4」にはyypush_buffer_state()とyypop_buffer_state()が無いので、自前で処理しました。
//参考資料「11 Multiple Input Buffers」(http://flex.sourceforge.net/manual/Multiple-Input-Buffers.html)
static YY_BUFFER_STATE popen_stack[16/*調整可*/];
static int popen_stack_ptr;
//P/ECEでは'popen'キーワードによる入力バッファ切り替え処理をダミーとします。
#ifdef  PIECE
#define _popen(command,type) (NULL)
#define _pclose(stream) (-1)
#endif//PIECE
//}}定型コード:lex.yy.l(定義部,%{〜%}内)=======================================
%}
/*{{定型コード:lex.yy.l(定義部,%{〜%}外)=====================================*/
/* Suppress the appearance of unneeded routines in the generated scanner. */
%option noinput
%option nounput
/* 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(定義部,%{〜%}外)=====================================*/

%x POPEN
%x COMMENT
%x SOURCE
%x PROPERTY
%x CODE

mbblead		([\x81-\x9F]|[\xE0-\xFC])
mbbtrail	([\x40-\x7E]|[\x80-\xFC])

%%

<INITIAL>{
<<EOF>>				{ if(!popen_stack_ptr) { yyterminate(); }
				  pop_location();/*_pclose()が失敗する原因はpopenで開こうとしたファイルが無い場合がほとんどなので元ファイル側のエラーとするのが適切だ。従って_pclose()する前にロケーションを復元しておく事にした。*/
				  { int retval = _pclose(yyin); if(retval) { yyerror("pcloseが失敗しました。(%d)", retval); yynerrs++; }
				    yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(popen_stack[--popen_stack_ptr]); } }
popen[[:blank:]]+\"		{ BEGIN POPEN; }
"#"				{ BEGIN COMMENT; }
"%{"				{ BEGIN SOURCE; }
"@{"				{ BEGIN PROPERTY; }
\`				{ BEGIN CODE; }

"["				|
"]"				|
"{"				|
"}"				|
"("				|
")"				{ return yytext[0]; }

"entry"				{ return T_ENTRY; }
"exit"				{ return T_EXIT; }
"suspend"			{ return T_SUSPEND; }
"resume"			{ return T_RESUME; }
"push"				{ return T_PUSH; }
"pop"				{ return T_POP; }
"end"				{ return T_END; }
"no-self-transition"		{ return T_NO_SELF_TRANSITION; }			/*{{2017/01/15追加:自己遷移を抑止するGuard関数の省略記法を追加しました。}}*/

[_[:alnum:]]+			{ yylval.v_str = strdup(yytext); return T_LAB; }	/* 当ツールは数値リテラルを使用しないので、数字から始まるラベルも「`」無しで書ける事にした。「`」は別の用途でコードを括るために使用する。 */

[[:blank:]]+			{ /** no job **/ }
";"				|
\n				{ return T_EOL; }
.				{ yyerror("scan error"); return yytext[0]; }
}

<COMMENT>{
\n				{ BEGIN INITIAL; return T_EOL; }
.				{ /** no job **/ }
}

<SOURCE>{
<<EOF>>				{ BEGIN INITIAL; yyerror("EOF in source."); yynerrs++; }
"%}"				{ BEGIN INITIAL; yytext[yyleng - 2] = '\0'; yylval.v_str = strdup(yytext); return T_SOURCE; }
\n|.				{ yymore(); }
}

<PROPERTY>{
<<EOF>>				{ BEGIN INITIAL; yyerror("EOF in property."); yynerrs++; }
"@}"				{ BEGIN INITIAL; yytext[yyleng - 2] = '\0'; yylval.v_str = strdup(yytext); return T_PROPERTY; }
\n|.				{ yymore(); }
}

<CODE>{
<<EOF>>				{ BEGIN INITIAL; yyerror("EOF in code."); yynerrs++; }
\`				{ BEGIN INITIAL; yytext[yyleng - 1] = '\0'; yylval.v_str = strdup(yytext); return T_CODE; }
{mbblead}{mbbtrail}		|	/* `…`の中ではシフトJISを意識したスキャンを行う。最長一致のルールにより上の行の「\`」よりもこの行が優先される。 */
\n|.				{ yymore(); }
}

<POPEN>{
<<EOF>>				{ BEGIN INITIAL; yyerror("EOF in popen."); yynerrs++; }
\n				{ BEGIN INITIAL; yyerror("Unterminated popen."); yynerrs++; }
\"				{ BEGIN INITIAL; yytext[yyleng - 1] = '\0';
				  { FILE* fp = _popen(yytext, "r"); if(!fp) { yyerror("popenが失敗しました。(%s)", yytext); yynerrs++; YY_BREAK; }
				    if(popen_stack_ptr >= ARRAY_SIZE(popen_stack)) { yyerror("popenの階層が深過ぎます。(%s)", yytext); yynerrs++; YY_BREAK; }
				    push_location(yytext);/*yy_switch_to_buffer()を呼ぶとyytextが破壊されるようなのでyy_switch_to_buffer()よりも先に行う必要が有る。*/
				    popen_stack[popen_stack_ptr++] = YY_CURRENT_BUFFER; yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); } }
.				{ yymore(); }
}

%%
