//------------------------------------------------------------
// $Id: lineParser.h,v 1.1.1.1 1999/11/25 06:26:12 yamauchi Exp $
//------------------------------------------------------------
//	(M\pi)^2 one line parser 
//	1997年11月19日(水) 解析したエントリのチェックを可能にした
//	1997年11月17日(月) lineParserElemClass を追加
//	1997年11月5日(水)  first version
// 
//	Copyright (C) 1997-1998 YAMAUCHI Hitoshi 山内 斉
//------------------------------------------------------------
#if 0 

 lineParserClass   (C) Hitoshi YAMAUCHI 山内 斉 

 設定ファイル等の解析を行うクラス

【簡略説明】
    このクラスは、

        setnum  IDENTIFIER = double_val [double_val double_val ... ]
	settype IDENTIFIER = STRING
	setname IDENTIFIER = STRING

    という形の行を解析します。それぞれの目的は

	setnum	   : 引数に要素を double とするベクトルをとる
		     主に数値パラメータ入力用

	settype	   : 複数の文字列の選択の中から一つを選ぶ
		     主にモードセレクタ用

	setname    : 文字列を一つ定義する
		     主にファイル名などの指定用

    です．settype について補足すると，たとえば，

		   { "FreeBSD", "Linux", "Solaris" }

    の一つを選択したいという場合に利用します．結果はこの配列の順番とし
    て int の値を返します．(つまり Linux ならば 1 が返る．(0 origin)) 
    場合に用います．setname は文字列そのものが得られ，たとえばファイル
    名をセットしたい場合に用います．

    この 3 つのものしか解析できませんが、通常の設定ファイルでは十分に
    使えるものだと思います。各行は fgets で読み込んだ時のように \n が
    付属していることを考えています。(おそらく \0 で終了しても大丈夫で
    す。)

【例】
    単純な例として

	setnum  define_space_size = 10.0 10.0 20.5
	settype OS_type	= FreeBSD
	setname InputFile = /home/usr/.cshrc

    の例を考えます．最初のものは数値データを入力としてとりたい場合，次
    のものはたとえば OS_type として

        "FreeBSD", "Linux", "SunOS5", "Windows95", "MacOS8" 

    のように 5 つの中から一つを選択するものと仮定します。文字列の中に
    は空白などを含めてはいけません。最後のものはとにかく引数の文字列そ
    のものをとりたい場合です．つまり 2 番目のものはある選択肢の何番目
    が選択されたかの番号を得たい場合，Option などの解析に使い，3 番目
    のものは文字列を知りたい場合，入力ファイル名の指定などに利用します． 

    これを lineParser.cc の main 関数に示します。コンパイルは，
    lineparser/ の下で xmkmf -a; make です．
    
    以下に利用法のあらすじを記します。完全なものは lineParser.cc の 
    main 中に示してありますので，そちらを参照して下さい．

    // lineParser 作成 : entrynum = 解析する Entry の数 : 何種類のもの
    // を解析したいか 
    LineParser lp(entrynum);  

    // 解析したい各要素を作成する
    // この要素は型 LineParserNumberElem や LineParserTypeElem も良い
    // が，これらは LineParserElemIF から派生されており，LineParser 自
    // 体は LineParserElemIF を受けるのでこうしておけば一つの配列で扱
    // うことができる．まあ，Object 指向の Interface に対するプログラ
    // ミングという基本中の基本の考えなのでわからなければ Object 指向
    // の本を読んで下さい．
    LineParserElemIF pLpElem[entrynum]; 

    // define space size は数のベクトルで要素は 3 つ
    pLpElem[Defspace] = new LineParserNumberElem;
    ((LineParserNumberElem *)pLpElem[Defspace])->set("define_space_size", 3);

    // OS_type は 5 つの選択肢の中の一つ
    const char *pTypeArgs[] = { 
	"SunOS5", "Windows95", "MacOS8", "FreeBSD", "Linux", 
    };
    pLpElem[OSTYPE] = new LineParserTypeElem;
    ((LineParserTypeElem *)pLpElem[OSTYPE])->set("OS_type", 5, pTypeArgs);

    // input file
    pLpElem[InputFile] = new LineParserNameElem;    
    ((LineParserNameElem *)pLpElem[InputFile])->set("InputFile");

    // 各 entry を lp に登録する
    for(int i = 0; i < entrynum; i++)
    {
	lp.addEntry(pLpElem[i]);
    }

    // for DEBUG 登録内容の確認
    lp.print();

    // 評価します 
    // 解析したい入力行が pInput_line_buf に入っている
    // pMatchLpElemIF には登録した要素に値がセットされて返ってくる
    LineParserElemIF *pMatchLpElemIF = NULL;
    if(lp.evalOneLine(pInputs[i], &pMatchLpElemIF) == YES) 
    {
	// YES ならその行は登録したものに一致 : 実際の利用には各クラス
	// の get 関係のメンバによって解析した値を得る．
	pMatchLpElemIF->print();
	pMatchLpElemIF->printContents();
    }

    // 全エントリを一度は解析したかを調べる場合には以下のメソッドを呼ぶ
    // 未解析エントリ数
    cout << "Number of unparsed entries = " << lp.getNumberofUnparsedEntries()
	<< endl;

    // 解析していないエントリの名前を取得
    // (pUnparsed は後に解放する必要あり。後述の printUnparsedEntries 
    // の方が簡易 
    myString *pUnparsed = lp.newNgetUnparsedEntries();  
    delete [] pUnparsed;

    // 解析していないエントリの名前を出力する
    lp.printUnparsedEntries();  

【制限】

    数字の場合のベクトルのサイズと選択肢の種類とには上限があります。こ
    れは一つのエントリに対しての上限です。LineParser は一行づつ解析を
    行いますが、その際に split する単語数分のバッファを作成します．そ
    のためにそのサイズを超えての解析はできません。(例えば上記の例では 
    define_space_size に指定できるベクトルの要素数) この大きさは、 
	
	(数字:ベクトルサイズ)	MAX_LINEPARSER_NUM_ARGNUM
	(文字列:選択肢)		MAX_LINEPARSER_TYPE_ARGNUM

    で定義されています。通常どちらも 8 になっています。ただし、この定
    数を lineParser.h を読み込む前に define すればその値に変更可能です。
    たとえば、文字列での選択肢の数を 12 にしたい場合には、コンパイル時に

        g++ -c -DMAX_LINEPARSER_TYPE_ARGNUM=12 lineParse.cc 

    のように指定すると、最大の選択肢の数は 12 になります。両方指定する
    ことも可能ですが、通常は大丈夫でしょう。

#endif				// #if 0 
//------------------------------------------------------------
#ifndef _lineParser_h 
#define _lineParser_h 1
#ifndef YorN
enum YorN {
    NO  = 0,
    YES = 1
};
#define YorN YorN
#endif
#include "myString.h"

//------------------------------------------------------------
// defines
//------------------------------------------------------------
// 引数の数がある値より大きければ異常を検知するための任意の値
//	数値: ベクトルサイズ
//------------------------------
#ifndef MAX_LINEPARSER_TYPE_ARGNUM
static const int MAX_LINEPARSER_TYPE_ARGNUM 	= 8; 
#endif
//------------------------------
//	文字列: 選択肢の数
//------------------------------
#ifndef MAX_LINEPARSER_NUM_ARGNUM
static const int MAX_LINEPARSER_NUM_ARGNUM 	= 8; 
#endif

//------------------------------------------------------------
// one line parser
//------------------------------------------------------------
// エントリのセット用
//------------------------------
enum lineparser_type {
    LP_NUMBER_e,
    LP_TYPE_e,
    LP_NAME_e,
    LP_ELSE_e
};

//------------------------------
// element of lineparser interface 
//------------------------------
class LineParserElemIF {
private:
    const LineParserElemIF& operator=(const LineParserElemIF& rhs);
    LineParserElemIF(const LineParserElemIF& rhs);

public:
    LineParserElemIF(void){};
    virtual ~LineParserElemIF(void){};

    // このエントリの型を返す
    // 	{ LP_NUMBER_e, LP_TYPE_e, LP_NAME_e, LP_ELSE_e } のいずれか
    virtual int getType(void) const		= 0;

    // 識別子を返す
    virtual myString getIdentifier(void) const 	= 0;
    virtual YorN isOK(void) const		= 0;    
    virtual void print(void) const		= 0;    
    virtual void printContents(void) const	= 0;        
};


//------------------------------
// 数値用
//------------------------------
class LineParserNumberElem : public LineParserElemIF {
public:
    myString  mID;			// 識別子
    int       mNumVecOfArgs;		// ベクトルの要素数
    double   *mpVec;			// 読み込んだ要素の値

protected:
    void copy(const LineParserNumberElem& rhs);
    void init(void);

public:
    LineParserNumberElem(void);  
    virtual ~LineParserNumberElem(void);
    LineParserNumberElem(const LineParserNumberElem& rhs);
    const LineParserNumberElem& operator=(const LineParserNumberElem& rhs);
    LineParserNumberElem(const char *pID, int numberOfVector);

    // virtual functions of LineParserElemIF
    virtual int getType(void) const 		{ return(LP_NUMBER_e);	}
    virtual myString getIdentifier(void) const	{ return(this->mID);	}
    virtual YorN isOK(void) const;
    virtual void print(void) const;    
    virtual void printContents(void) const ;        

    void   set(const char *pID, int numberOfVector); 	 // Number 候補用
    void   setVectorItem(int idx, double val);
    int    getVectorLen(void) const   { return(this->mNumVecOfArgs);	}
    double getVecValue(int idx) const;
};

//------------------------------
// Type 用 (選択型)
//------------------------------
class LineParserTypeElem : public LineParserElemIF {
public:
    myString  mID;			// 識別子
    int       mNumTypeOfCandidates;	// type の引数候補数 
    myString *mpArgsCandidates;		// type の引数候補群
    int       mSelected;

protected:
    void copy(const LineParserTypeElem& rhs);

public:
    LineParserTypeElem(void);  
    virtual ~LineParserTypeElem(void);
    LineParserTypeElem(const LineParserTypeElem& rhs);
    const LineParserTypeElem& operator=(const LineParserTypeElem& rhs);
    LineParserTypeElem(const char *pID, int numberOfArgsCandidate, 
		       const char **ppArgsCandidate);

    // virtual functions of LineParserElemIF
    virtual int getType(void) const 		{ return(LP_TYPE_e);	}
    virtual myString getIdentifier(void) const	{ return(this->mID);	}
    virtual YorN isOK(void) const;
    virtual void print(void) const;
    virtual void printContents(void) const;

    void set(const char *pID, int numberOfArgsCandidate, // Type 候補用
 	     const char **ppArgsCandidate);  
    void setTypeNumber(int selected);
    int  getTypeNumber(void) const 		{ return(this->mSelected);}
    int  getVectorLen(void) const   { return(this->mNumTypeOfCandidates); }
    const myString& getArgCandidate(int idx) const;    
};

//------------------------------
// 名前用
//------------------------------
class LineParserNameElem : public LineParserElemIF {
public:
    myString  mID;			// 識別子
    myString  mName;

protected:
    void copy(const LineParserNameElem& rhs);

public:
    LineParserNameElem(void){};  
    virtual ~LineParserNameElem(void){};
    LineParserNameElem(const LineParserNameElem& rhs);
    const LineParserNameElem& operator=(const LineParserNameElem& rhs);
    LineParserNameElem(const char *pID);

    // virtual functions of LineParserElemIF
    virtual int getType(void) const 		{ return(LP_NAME_e);	}    
    virtual myString getIdentifier(void) const	{ return(this->mID);	}
    virtual YorN isOK(void)  const;
    virtual void print(void) const;
    virtual void printContents(void) const;

    void set(const char *pID)	       	{ this->mID = myString(pID);	}
    void setName(const myString& name)	{ this->mName = name;		}
    const myString& getName(void) const { return(this->mName);		}
};

//------------------------------------------------------------
// line parser class
//------------------------------------------------------------
class LineParser {
private:
    LineParser(void);	// 必ず登録数の最大を指定してオブジェクト作成
    LineParser(const LineParser& rhs);
    const LineParser& operator=(const LineParser& rhs);

protected:
    int 	mMaxEntryNum;	// identifier の最大数
    int 	mCurEntryNum;	// 現在登録されている identifier の数
    YorN       *mpIsParsed;	    // 各 entry が解析されたか
    LineParserElemIF **mppEntries;    // 保持する entry の配列
    
protected:
    void init(int maxEntryNum);	// 最大いくつのエントリを用いるか
    void freemem(void);		// メモリ解放用
    
    // evalOneLine の下位関数 : それぞれのエントリを解析する
    int  getNumberEntry(myString words[]);
    int  getTypeEntry(myString words[]);
    int  getNameEntry(myString words[]);

public:
    ~LineParser(void);

    //------------------------------------------------------------
    // このクラスではユーザは以下の関数を利用する
    //------------------------------------------------------------
public:
    //------------------------------
    // 解析する種類の最大数を登録して construct する
    //------------------------------
    LineParser(int maxEntryNum);

    //------------------------------
    // エントリを登録する
    //	ret: 登録番号が返る
    //------------------------------
    int  addEntry(const LineParserElemIF* apLpEntry);

    //------------------------------
    // 一行解析する
    //   @param pLine 	解析する行
    //   @param pRetRefLPIF 解析の結果マッチした内部の line parser
    //   	element へのポインタ : 解放してはいけない．
    //------------------------------
    YorN evalOneLine(const char *pLine,	    // 解析する行
 		     LineParserElemIF **ppRetRefLPIF);  //内部の elem への ptr

    //------------------------------
    // 登録されたエントリのうち未解析のものの数を返す
    //------------------------------
    int getNumberofUnparsedEntries(void);

    //------------------------------
    // 未解析のエントリの identifier を返す
    //   後にここで確保された myString は解放しなくてはいけない 
    //------------------------------
    myString *newNgetUnparsedEntries(void);  

    //------------------------------
    // 未解析のエントリの identifier を出力する
    //   名前を特に取り出す必要がない場合には上記よりメモリ管理がわず
    //   らわしくない
    //------------------------------
    void printUnparsedEntries(void);  

    //------------------------------
    // 内部状態の print (for debug) 現在登録されているものなどの表示
    //------------------------------
    void print(void);
};

#endif				// #ifndef _lineParser_h 
