/*
 *	cliptcl.h
 *
 *	Tclインタプリタ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2018 Naoyuki Sawa
 *
 *	* Sun Oct 26 19:32:00 JST 2014 Naoyuki Sawa
 *	- 1st リリース。
 *	- 「keep/Tclｲﾝﾀﾌﾟﾘﾀ対応状況.xls」に、対応状況を記録して行きます。
 *	* Tue Nov 04 01:55:50 JST 2014 Naoyuki Sawa
 *	- バグ修正と、コードの整理を行いました。
 *	  cliptcl.hには最小限の定義を残し、アプリケーションから参照する必要の無い定義をcliptcl.cへ移しました。
 *	* Wed Nov 05 21:29:10 JST 2014 Naoyuki Sawa
 *	- Tcl_LreplaceCmd()関数に特例処理を追加しました。Tclのlreplaceコマンドの仕様に従うためです。
 *	* Thu Nov 06 01:04:00 JST 2014 Naoyuki Sawa
 *	- 'string reverse'コマンドを追加しました。'string reverse'は、Tcl8.5a6で追加されたコマンドです。
 *	* Thu Nov 06 21:20:13 JST 2014 Naoyuki Sawa
 *	- 'lsort'コマンドの、'-increasing','-decreasing'オプションを対応しました。
 *	* Sat Nov 08 23:09:13 JST 2014 Naoyuki Sawa
 *	- 'switch'コマンドの、'--'が正しく処理されていなかったバグを修正しました。
 *	  具体的には、'--'を発見したとき、'--'の次へ進める処理が不足していました。
 *	- 'unset'コマンドの、'--','-nocomplain'オプションを対応しました。
 *	- 'lindex'コマンドの、'lindex list ?index ...?'形式を対応しました。
 *	- 'lset'コマンドを追加しました。
 *	- 'lrepeat'コマンドを追加しました。'lrepeat'は、Tcl8.5で追加されたコマンドです。
 *	- 'string repeat'コマンドを追加しました。
 *	* Sun Nov 09 12:52:42 JST 2014 Naoyuki Sawa
 *	- 'array set'コマンドを追加しました。
 *	- 'array get'コマンドを追加しました。
 *	- 'array unset'コマンドを追加しました。
 *	- 'array names'コマンドの、'?pattern?'指定を対応しました。'?mode?'指定は未対応です。
 *	- 'lassign'コマンドを追加しました。'lassign'は、Tcl8.5で追加されたコマンドです。
 *	* Wed Nov 12 21:45:20 JST 2014 Naoyuki Sawa
 *	- セパレータ無しの文字列連結に、strjoin()⇒strconcat()を使用するよう、変更しました。
 *	- Tcl_GetsCmd()を、getline()による実装に変更しました。
 *	* Sat Nov 15 10:53:34 JST 2014 Naoyuki Sawa
 *	- 'lsearch'コマンドの、'-index','-subindices'オプションを対応しました。
 *	  これらのオプションを対応することにした理由は、以下の通りです。
 *	- 'dict'コマンドを実装したいと思いました。
 *	  しかし、'dict'は量が多く、当モジュール内で対応すると、容量が増える問題が生じます。
 *	  そこで、'dict'は、Tclスクリプトで実装し、必要な場合のみ"source"で読み込んで使おうと思います。⇒2014/11/16追記:実装しました。「clip/tclwin/dict.tcl.m4」を参照してください。⇒2014/11/30追記:やっぱり当モジュール内で実装しました。後述のコメントを参照してください。
 *	  'dict'をTclスクリプトで実装するには、'lsearch'コマンドの'-index','-subindices'オプションが使えると、効率が良いです。
 *	  '-index','-subindices'オプションだけならば、容量の増加も少ないので、当モジュール内で対応することにしました。
 *	* Mon Nov 17 23:53:17 JST 2014 Naoyuki Sawa
 *	- これまではTcl_EvalFile()がストリームを引数として受け取っていましたが、ストリームを受け取るTcl_EvalStream()とファイル名を受け取るTcl_EvalFile()に分離しました。
 *	  ファイル名を指定して実行したいことが良くあり、その都度、呼び出し側でfopen()を行うのは面倒なので、直接ファイル名を指定したいと思ったからです。
 *	  ストリームを指定したいことも引き続き有りますので、これまで通りストリームを指定する呼び出し形式の関数もTcl_EvalStream()という名前に変更して残すことにしました。
 *	* Sun Nov 30 13:38:06 JST 2014 Naoyuki Sawa
 *	- ビルトイン版'dict'コマンドを追加しました。
 *	  これまで、スクリプト版'dict'コマンドを使用してきましたが、速度が実用に耐えないぐらい遅く、高速化する必要が生じたためです。
 *	- ビルトイン版'dict'コマンドを追加したことにより、容量がかなり増大しました。
 *	  今後もし、やはりビルトイン版'dict'コマンドは不要だと判断した場合は、TCL_BUILTIN_DICTシンボルをコメントアウトしてください。
 *	- 高速化の効果と、容量増大の影響は、以下の通りです。
 *	  ┌──────┬────┬────┐
 *	  │　　　　　　│ サイズ │実行時間│(サイズ:cliptcl.oのバイト数,実行時間:「clip/tclwin/dict.tcl.m4」のdict_testの実行時間)
 *	  ├──────┼────┼────┤
 *	  │スクリプト版│   20672│  536255│
 *	  │ビルトイン版│   22700│    9905│
 *	  └──────┴────┴────┘
 *	  約2キロバイトの増加で、50倍以上高速化しました。
 *	- 尚、上表のスクリプト版のサイズは、「clip/tclwin/dict.tcl.m4」の'dict'のサイズを含んでいません。
 *	  「clip/tclwin/dict.tcl.m4」の'dict'のサイズは約5キロバイトで、sourceで読み込んだ時にRAM上に占める容量もほぼ同じサイズと思います。
 *	  従って、'dict'を使用する際のスクリプト版の合計容量は約25キロバイトであり、ビルトイン版の約22.17キロバイトよりも却って大きいです。
 *	  結論としては、'dict'の使用を前提とするならば、スクリプト版に較べてビルトイン版が不利な点は無く、ビルトイン版を使用するのが良いと思います。
 *	  'dict'は便利で、使用するケースは多いと思うので、ビルトイン版を有効にしてビルドしておくことにしました。
 *	* Tue Dec 09 21:44:51 JST 2014 Naoyuki Sawa
 *	- Tcl変数tcl_version,tcl_platform(platform)を追加しました。
 *	* Sun Dec 14 20:00:43 JST 2014 Naoyuki Sawa
 *	- 'string is'コマンドを追加しました。
 *	* Mon Dec 15 21:49:03 JST 2014 Naoyuki Sawa
 *	- 'string is'コマンドの、'-failindex'オプションを対応しました。
 *	* Tue Dec 16 21:33:25 JST 2014 Naoyuki Sawa
 *	- 'scan'コマンドの'%c'変換文字の動作を修正しました。
 *	  'scan'コマンドの'%c'は、文字ではなく、10進数文字列として格納するのが正しい。
 *	* Wed Feb 11 17:00:02 JST 2015 Naoyuki Sawa
 *	- 'lreverse'コマンドを追加しました。'lreverse'は、Tcl8.5a6で追加されたコマンドです。
 *	* Wed Feb 11 22:58:39 JST 2015 Naoyuki Sawa
 *	- 'lsort'コマンドの、'-ascii','-integer','-real','-nocase','-index'オプションを対応しました。
 *	* Thu Feb 12 21:46:47 JST 2015 Naoyuki Sawa
 *	- 'lsearch'コマンドの、'-exact','-glob'オプションを対応しました。
 *	* Fri Feb 13 20:30:58 JST 2015 Naoyuki Sawa
 *	- Tcl_IsEnclosedElement()を呼び出す処理が終わらなくなる問題を修正しました。
 *	- 当モジュールはガーベージコレクターとの併用が前提で明示的なfree()は不要なのに、明示的なfree()を行っている箇所が有ってコードサイズの無駄だったので削除しました。
 *	* Sat Feb 14 17:12:54 JST 2015 Naoyuki Sawa
 *	- Tcl_ExprLex()が失敗した時にエラーメッセージが適切でない問題を修正しました。
 *	- 'string first'コマンドの、'?startIndex?'指定を対応しました。
 *	- 'string last'コマンドの、'?lastIndex?'指定を対応しました。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'〜'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 *	* Fri Nov 03 23:05:28 JST 2017 Naoyuki Sawa
 *	- 'apply'コマンドを追加しました。'apply'は、Tcl8.5で追加されたコマンドです。
 *	- 既存のTcl_ProcCmd()関数から、プロシージャ情報を作成する処理を分離して、Tcl_CreateProc()関数を作成しました。
 *	  今回追加した'apply'コマンドと、既存の'proc'コマンドの、プロシージャ情報を作成する処理は共通なので、その処理をTcl_CreateProc()関数として共通化するためです。
 *	* Tue Nov 14 23:59:59 JST 2017 Naoyuki Sawa
 *	- 'regexp','regsub'コマンドを追加しました。switchesは'-all'のみ対応しています。
 *	  実装には、自作のBell V8 正規表現モジュール(/clip/regexp.c)を使用しているので、本物のTcl 8.x現在の正規表現のうち、一部の機能しか使用出来ません。
 *	  ただし、本物のTclも、6.7当時はBell V8 正規表現を利用していた形跡があり、それを元にして発展して来たと思われるので、おそらく8.xに対して下位互換だと思います。
 *	* Thu Nov 16 23:42:50 JST 2017 Naoyuki Sawa
 *	- 'regexp','regsub'コマンドの、switchesの'-nocase'を対応しました。
 *	* Sat Nov 18 23:37:58 JST 2017 Naoyuki Sawa
 *	- 'lsearch'コマンドの、'-regexp'オプションを対応しました。
 *	- 'switch'コマンドの、'-regexp'オプションを対応しました。
 *	* Sun Nov 19 22:01:55 JST 2017 Naoyuki Sawa
 *	- 'string replace'コマンドを追加しました。
 *	  'string replace'コマンドは、仕様が少し直観的ではないので、注意して下さい。
 *	  newstring引数を指定しない場合、'string range'コマンドの逆の動作となります。('string range'コマンドでは取り出していた部分を、逆に、取り除く動作となります。)
 *	  newstring引数を指定した場合、取り除く部分が有れば、その代わりに追加します。(取り除く部分が無ければ、newstring引数は無視されます。)
 *	* Wed Apr 04 23:59:59 JST 2018 Naoyuki Sawa
 *	- regexpコマンドの、'-indices'オプションを対応しました。
 */
#ifndef __CLIP_TCL_H__
#define __CLIP_TCL_H__

#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus

//┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
//┃【重要】当モジュールは、ガーベージコレクターとの併用を前提としています。　　　　　　　　　　　　　　　　　　　　┃
//┃・P/ECE環境では、Tcl_CreateInterp()を呼び出す前に、「gc_init(GC_LEVEL_CONSERVATIVE);」を実行しておいてください。┃
//┃・PC環境では、ガーベージコレクターのライブラリ(「Boehm GC」等)をリンクしてください。　　　　　　　　　　　　　　┃
//┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
//┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
//┃【重要】当モジュールを実行すると、小さいメモリブロックが増えて、処理速度が極端に低下します。　　　　　　　　　　┃
//┃・定期的にgc_run()を呼び出して、強制的にガーベージコレクションを行うことによって、処理速度の低下を緩和できます。┃
//┃・＜例＞アプリケーションプログラムの開始処理にて、「timer_start(500/*調整可*/, gc_run, NULL)」を行ってください。┃
//┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

/****************************************************************************
 *	定数
 ****************************************************************************/
/* 結果コード */
//When a TCL command returns, the string pointer interp->result points to a string containing return information from the command.
//In addition, the command procedure returns an integer value, which is one of the following:
#define TCL_OK				0				//Command completed normally; interp->result contains the command's result.
#define TCL_ERROR			1				//The command couldn't be completed successfully; interp->result describes what went wrong.
#define TCL_RETURN			2				//The command requests that the current procedure return; interp->result contains the procedure's return value.
#define TCL_BREAK			3				//The command requests that the innermost loop be exited; interp->result is meaningless.
#define TCL_CONTINUE			4				//Go on to the next iteration of the current loop; interp->result is meaninless.
/****************************************************************************
 *	構造体
 ****************************************************************************/
/* インタプリタ情報 */
typedef struct _Tcl_Interp {
	/*--- 公開フィールド ---*/
	const char*			result;				//Points to result returned by last command.	//←←←←←■アプリケーションがTcl_Eval*()の結果を得るためにこのフィールドを参照することができる。アプリケーション定義のコマンド関数がこのフィールドに結果を格納することができる。■
	int				argc;				//┐
	char**				argv;				//├Tcl_CmdProc()への引数			//←←←←←■アプリケーションがTcl_CreateCommand()でコマンド関数を登録し、コマンド関数はこのフィールドを参照せよ。具体的にはビルトインコマンドの各Tcl_*Cmd()関数の実装を参照せよ。■
	void*				data;				//┘
	/*--- これ以下は、アプリケーションや、アプリケーション定義のコマンド関数から、直接参照してはいけない。 ---*/
	int				noEval;				//短絡評価中にコマンドの実行を抑制するためのカウンタ。具体的にはTcl_ExprGetValue()の実装を参照せよ。
	int				numLevels;			//Keeps track of how many nested calls to Tcl_Eval are in progress for this interpreter. It's used to delay deletion of the table until all Tcl_Eval invocations are completed.
	struct _Tcl_CallFrame*		framePtr;			//Points to top-most in stack of all nested procedure invocations. NULL means there are no active procedures.
	struct _Tcl_CallFrame*		varFramePtr;			//Points to the call frame whose variables are currently in use (same as framePtr unless an "uplevel" command is being executed). NULL means no procedure is active or "uplevel 0" is being exec'ed.
	struct _Tcl_Table*		commandTablePtr;		//Contains all of the commands currently registered in this interpreter. Indexed by strings; values have type (Tcl_Command*).
	struct _Tcl_Table*		globalTablePtr;			//Contains all global variables for interpreter. Indexed by strings; values have type (Tcl_Var*).
	struct _Tcl_Table*		arraySearchTablePtr;		//Contains all array searches currently active in this interpreter. Indexed by strings; values have type (Tcl_ArraySearch*).
	struct _Tcl_Table*		fileTablePtr;			//Contains all files opened in this interpreter. Indexed by strings; values have type (FILE*).
} Tcl_Interp;
/****************************************************************************
 *	グローバル関数
 ****************************************************************************/
Tcl_Interp* Tcl_CreateInterp();							//インタプリタを作成する。開放はガーベージコレクタが行うので、明示的な開放は不要。(Tcl_Interp*)が指している各メモリブロックを、早くガーベージコレクションの対象にしたい場合は、(Tcl_Interp*)をfree()で開放しても良い。(必須ではない)
/*--------------------------------------------------------------------------*/
typedef int Tcl_CmdProc(Tcl_Interp* iPtr);
void Tcl_CreateCommand(Tcl_Interp* iPtr, const char* name, Tcl_CmdProc* proc, void* data);	//C言語で実装したコマンドを登録する。
int Tcl_DeleteCommand(Tcl_Interp* iPtr, char* name);						//C言語で実装したコマンドを登録解除する。
int Tcl_EvalString(Tcl_Interp* iPtr, const char* s);				//Tclスクリプトの文字列を指定して実行する。
int Tcl_EvalStream(Tcl_Interp* iPtr, FILE* fp);					//ストリームを指定して実行する。ファイルストリーム,又は,stdinも指定可能。
int Tcl_EvalFile(Tcl_Interp* iPtr, const char* fileName);			//ファイル名を指定して実行する。
/*--------------------------------------------------------------------------*/
char* Tcl_GetVar(Tcl_Interp* iPtr, const char* name);				//現在のコールフレームの変数を取得する。失敗ならばNULLが返る。							┐
char* Tcl_SetVar(Tcl_Interp* iPtr, const char* name, const char* value);	//現在のコールフレームの変数を設定する。失敗ならばNULLが返る。成功時に返される文字列は、valueの複製である。	├Tclプロシージャを経由してC関数が呼び出された場合は、C関数がグローバル変数にアクセスできないことに注意せよ。どうしてもC関数がグローバル変数にアクセスしたい場合は、C関数内でiPtr->varFramePtrを一時的に変更して当関数を呼び出せ。
int Tcl_UnsetVar(Tcl_Interp* iPtr, const char* name);				//現在のコールフレームの変数を削除する。成功ならば0,失敗ならば0以外の値が返る。					┘
/****************************************************************************
 *	
 ****************************************************************************/

#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus

#endif/*__CLIP_TCL_H__*/
