// vim:set ts=4 sw=4 fencs=euc-jp nomodified: /**@file *@brief Anthy の付属語グラフを全探索して表示する *@date Tue,05 May,2009 - Sat,16 May,2009 *@date Sat,20 Feb,2021 *@author Copyright(C)2009 G-HAL All rights reserved. * *@comment * * 使用例: * 「直すの」(なおすの)の付属語が * どの様に構成されているのかを調べる場合。 * 「直」(なお)の品詞を調べる。 * % egrep "^なお " alt-cannadic/gcanna.ctd * なお #F14*400 なお 尚 #R5*301 治 #S5*301 直 #CJ*300 なお 尚 #R5*300 直 #S5*300 なお 治 #CJ*200 猶 #F14*200 猶 #R5*200 なお #JN*151 なお 直 #JN*150 尚 奈緒 #JN*100 奈央 奈穂 #JN*50 奈保 * 「S5」だった。「S5」の wtype_t を調べる。 * % grep -w "S5" src-worddic/wtab.h * {"#S5",POS_V,COS_NONE,SCOS_NONE,CC_S5,CT_HEAD,WF_INDEP }, * ptab.h にて該当する wtype_t を探す。 * この時、*_NONE の項目と WF_* は無視する。 * % cat src-worddic/ptab.h | grep -w POS_V | grep -w CC_S5 | grep -w CT_HEAD * {"サ行5段活用動詞語幹",POS_V,COS_NONE,SCOS_NONE,CC_S5,CT_HEAD,WF_INDEP}, * "サ行5段活用動詞語幹" が根ノードとなる。 * % depgraphdump -n "サ行5段活用動詞語幹" -w "すの" -e * End: サ行5段活用動詞語幹|すの H SzC :0.0 @_サ行5段語幹後 "" @サ行5段終止形 "す" @_5段終止形(共通) "" @「の」終助 "の" Sz@ * End: サ行5段活用動詞語幹|すの HnSeC :0.0 @_サ行5段語幹後 "" @サ行5段連体形 "す" @_5段連体形(共通) "" Hn@「の」格助(準体) "の" Se@ * * * 引数: * * -p --path (path-name) * 付属語グラフのあるディレクトリ。 * 標準設定:カレントディレクトリ * * -i --indepword (file-name) * 自立語テーブルのファイル名(ファイル名のみ、もしくはフルパス)。 * 標準設定:indepword.txt * * -d --depword (file-name) * 付属語テーブルのファイル名(ファイル名のみ、もしくはフルパス)。 * 標準設定:master.depword * * -r --reverse * 後方検索(右→左方向の探索)。 * 標準設定:前方検索(左→右方向の探索) * * -n --start-node (string) * 検索を開始するノード名。 * 標準設定:無指定 * (前方検索時:根ノード全て、後方検索時:葉ノード全て) * * -w --word (string) * 探索する付属語。 * 先頭一致(前方検索時)/後方一致(後方検索時)した付属語のみが表示される。 * --max-len オプションが、指定した付属語の長さに、自動的に設定される。 * 標準設定:無指定 * * -m --max-len (int) * 探索する付属語の最大長を、所謂2byte文字の文字数で指定する。 * 0 を指定した場合、この条件での探索打ち切りをしない。 * 0文字を指定したい場合、-w "" -e と指定する。 * 標準設定:所謂2byte文字を4文字ぶん(-m 4)。 * * -S, --silent * 検索条件にヒットした数のみ表示する。 * 標準設定:否 * * -e, --exatct-match-only * --word オプションで指定した付属語に完全一致した物のみ表示する。 * 標準設定:否 * * -U, --unused-only * 既に表示した遷移リストに含まれているノード以下は探索しない。 * 標準設定:否 * * -s, --show-unused-node * 使用されなかったノードを表示する。 * 標準設定:否 * * -H, --help * 引数一覧を表示する。 * * -C, --credits * 権利表示を表示する。 * * --version * バージョン等を表示する。 * */ /**\mainpage  ソースコード形式かバイナリ形式か、変更するかしないかを問わず、 以下の条件を満たす場合に限り、再頒布および使用が許可されます。 ・ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、 および下記免責条項を含めること。 ・バイナリ形式で再頒布する場合、頒布物の付属のドキュメント等の資料に、 上記の著作権表示、本条件一覧、および下記免責条項を含めること。 ・書面による特別の許可なしに、本ソフトウェアから派生した製品の宣伝 または販売促進に、作者の名前またはコントリビューターの名前を 使用してはならない。 本ソフトウェアは、著作権者およびコントリビューターによって 「あるがまま」提供されており、明示黙示を問わず、商業的な使用可能性、 および特定の目的に対する適合性に関する暗黙の保証も含め、 またそれに限定されない、いかなる保証もありません。 著作権者もコントリビューターも、事由のいかんを問わず、 損害発生の原因いかんを問わず、かつ責任の根拠が契約であるか 厳格責任であるか(過失その他の)不法行為であるかを問わず、 仮にそのような損害が発生する可能性を知らされていたとしても、 本ソフトウェアの使用によって発生した (代替品または代用サービスの調達、使用の喪失、データの喪失、利益の喪失、 業務の中断も含め、またそれに限定されない) 直接損害、間接損害、偶発的な損害、特別損害、懲罰的損害、 または結果損害について、一切責任を負わないものとします。 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(HAVE_CONFIG_H) && !defined(__CONFIG_H__) # include "config.h" #else # define PACKAGE_NAME "depgraphdump" # define PACKAGE_VERSION "0.00" # define HAVE_STDINT_H # define HAVE_INTTYPES_H # define HAVE_TIME_H # define HAVE_SYS_TIME_H # define HAVE_LIMITS_H # define HAVE_STRING_H # define HAVE_ERRNO_H # define HAVE_STDARG_H # define HAVE_SYSLOG_H # define HAVE_ASSERT_H # define HAVE_NEW # define HAVE_MAP # define HAVE_STRING # define HAVE_GETOPT_H # define BUF_MAX 1024 # define BUFLONG_MAX 10240 #endif #include #include #if defined(HAVE_ERRNO_H) # include #endif #if defined(HAVE_STDINT_H) # if !defined(__STDC_LIMIT_MACROS) # define __STDC_LIMIT_MACROS # endif # include #endif #if defined(HAVE_INTTYPES_H) # if !defined(__STDC_FORMAT_MACROS) # define __STDC_FORMAT_MACROS # endif # include #endif #if defined(HAVE_STDARG_H) # include #endif #if defined(HAVE_STRING_H) # include #endif #if defined(HAVE_STRINGS_H) # include #endif #if defined(HAVE_ASSERT_H) # include #endif #if defined(HAVE_SYSLOG_H) # include #endif #if defined(HAVE_TIME_H) # include #endif #if defined(HAVE_SYS_TIME_H) # include #endif #if defined(HAVE_LIMITS_H) # include #endif #if defined(HAVE_NEW) # include #endif #if defined(HAVE_MAP) # include #endif #if defined(HAVE_STRING) # include #endif #if defined(HAVE_GETOPT_H) # include #endif ///@name macros マクロ //@{ ///@name define コンパイラ依存 //@{ #define SLASH() / #if defined(__GNUC__) # if (3 < __GNUC__) || ((3 == __GNUC__) && (1 <= __GNUC_MINOR__)) # define COMPILER_SUPPORT_NRVO ///< Named Return Value Optimization 対応 # define COMPILER_NRV(arg) # define COMPILER_NRVO(arg) arg # elif (2 == __GNUC__) # define COMPILER_SUPPORT_NRV ///< Named Return Value 対応 # define COMPILER_NRV(arg) arg # define COMPILER_NRVO(arg) # else # define COMPILER_NRV(arg) # define COMPILER_NRVO(arg) arg # endif # if !defined(__unused) # if !defined(HAVE_CONFIG_H) # if (3 < __GNUC__) || ((3 == __GNUC__) && (3 <= __GNUC_MINOR__)) # define __unused __attribute__((__unused__)) ///< __unused__ 対応 # else # define __unused ///< __unused__ 非対応 # endif # else # if defined(HAVE___ATTRIBUTE__UNUSED) # define __unused __attribute__((__unused__)) ///< __unused__ 対応 # else # define __unused ///< __unused__ 非対応 # endif # endif # endif # if !defined(_unused) # if !defined(HAVE_CONFIG_H) # if (3 < __GNUC__) || ((3 == __GNUC__) && (4 <= __GNUC_MINOR__)) # define _unused __attribute__((__unused__)) ///< 関数の引数の __unused__ 対応 # else # define _unused ///< 関数の引数の __unused__ 非対応 # endif # else # if defined(HAVE___ATTRIBUTE__UNUSED_ARG) # define _unused __attribute__((__unused__)) ///< 関数の引数の __unused__ 対応 # else # define _unused ///< 関数の引数の __unused__ 非対応 # endif # endif # endif #else /* defined(__GNUC__) */ # define COMPILER_NRV(arg) /* not supported */ # define COMPILER_NRVO(arg) arg # if !defined(HAVE_CONFIG_H) # if !defined(__attribute__) # define __attribute__(x) /* not supported */ # endif # if !defined(__unused) # define __unused /* not supported */ # endif # if !defined(_unused) # define _unused /* not supported */ # endif # else # if !defined(__attribute__) # if defined(HAVE___ATTRIBUTE__) # else # define __attribute__(x) /* not supported */ # endif # endif # if !defined(__unused) # if defined(HAVE___ATTRIBUTE__UNUSED) # define __unused __attribute__((__unused__)) ///< __unused__ 対応 # else # define __unused ///< __unused__ 非対応 # endif # endif # if !defined(_unused) # if defined(HAVE___ATTRIBUTE__UNUSED_ARG) # define _unused __attribute__((__unused__)) ///< 関数の引数の __unused__ 対応 # else # define _unused ///< 関数の引数の __unused__ 非対応 # endif # endif # endif #endif /* defined(__GNUC__) else */ # if !defined(__printflike) # if defined(HAVE___ATTRIBUTE____FORMAT____PRINTF__) # define __printflike(fmtarg, firstvararg) __attribute__((__format__ (__printf__, fmtarg, firstvararg))) # else # define __printflike(fmtarg, firstvararg) # endif # endif # if !defined(__scanflike) # if defined(HAVE___ATTRIBUTE____FORMAT____SCANF__) # define __scanflike(fmtarg, firstvararg) __attribute__((__format__ (__scanf__, fmtarg, firstvararg))) # else # define __scanflike(fmtarg, firstvararg) # endif # endif # if !defined(__bounded__) # if !defined(HAVE_CONFIG_H) # if defined(__OpenBSD__) && ((3 < __GNUC__) || ((3 == __GNUC__) && (2 < __GNUC_MINOR__)) || ((3 == __GNUC__) && (2 == __GNUC_MINOR__) && (3 <= __GNUC_PATCHLEVEL__))) // OpenBSD 3.4 以降で __attribute__((__bounded__(,,))) 対応。 // マクロで OpenBSD のバージョンを直接知る方法が見つからなかったので。 # else # define __bounded__(a,b,c) ///< バッファオーバー/アンダーフローの静的検査が無い # endif # else # if defined(HAVE___ATTRIBUTE__BOUNDED) # else # define __bounded__(a,b,c) ///< バッファオーバー/アンダーフローの静的検査が無い # endif # endif # endif //@} ///@name tips ちょっと便利かも知れないマクロ //@{ #if !defined(_number_) # define _number_(arr) (sizeof(arr)/sizeof((arr)[0])) #endif //@} ///@name log 諸事情で機能削減 //@{ void writeWtag( const char* const name, int line, const char* const func, int priority, const char* __restrict__ const msg, ... ) __printflike(5,6); /** ログ出力(可変長引数版) *@param[in] name ソースファイル名 *@param line ソース行番号 *@param[in] func ソース関数名 *@param priority 重要度 *@param[in] msg ログ出力メッセージ */ void writeWtag( const char* const name, int line, const char* const func, int priority, const char* __restrict__ const msg, ... ) { assert( NULL != name ); assert( NULL != func ); assert( NULL != msg ); if (LOG_DEBUG < priority) { return; } { FILE* output_fd = stderr; timeval tp; time_t current; struct tm local; gettimeofday( &tp, NULL ); current = tp.tv_sec; localtime_r( ¤t, &local ); fprintf( output_fd, "LOG: " "%04d-%02d-%02d %02d:%02d:%02d(%010" PRIdMAX ".%06" PRId32 ") %1d " "%s:%d:%s; ", static_cast(local.tm_year + 1900), static_cast(local.tm_mon + 1), static_cast(local.tm_mday), static_cast(local.tm_hour), static_cast(local.tm_min), static_cast(local.tm_sec), static_cast(tp.tv_sec), static_cast(tp.tv_usec), priority, name, line, func ); { va_list ap; va_start( ap, msg ); vfprintf( output_fd, msg,ap ); va_end( ap ); } fprintf( output_fd, "\n" ); //fflush(output_fd); } return; } #if (199901L <= (__STDC_VERSION__ +0L)) # define LOG(f,...) writeWtag(__FILE__,__LINE__,__FUNCTION__,f ,__VA_ARGS__ ) #elif (2 <= (__GNUC__ +0)) # define LOG(f,arg...) writeWtag(__FILE__,__LINE__,__func__,f ,##arg ) #else # define LOG(f,arg...) writeWtag(__FILE__,__LINE__,"",f ,##arg ) #endif //@} //@} ///@name prototype プロトタイプ //@{ /** 自立語テーブル/付属語グラフのデータ本体 */ class WordgraphTable { public: typedef std::map StringMap_t; /** bool はダミー */ typedef std::pair StringPair_t; /** bool はダミー */ static const StringMap_t::const_iterator NULL_StringMap; /**< NULL ポインタ */ typedef std::map StringStringMap_t; typedef std::pair StringStringPair_t; static const StringStringMap_t::const_iterator NULL_StringStringMap; /**< NULL ポインタ */ struct DataToken; typedef std::multimap DataTokenMap_t; typedef std::pair DataTokenPair_t; static const DataTokenMap_t::const_iterator NULL_DataTokenMap; /**< NULL ポインタ */ typedef std::map DataTokenPtrMap_t; typedef std::pair DataTokenPtrPair_t; static const DataTokenPtrMap_t::const_iterator NULL_DataTokenPtrMap; /**< NULL ポインタ */ /** データトークン */ struct DataToken { /** トークンの種別 */ enum TokenType { UNKNOWN, /**< 種別:未定義 */ INDEPWORD, /**< 種別:自立語 */ DEPWORD, /**< 種別:付属語 */ }; TokenType type; /**< 種別 */ bool used; /**< 未参照フラグ */ bool leaf; /**< 末端ノードとなりうるか */ StringMap_t depword; /**< 付属語リスト */ DataTokenPtrMap_t parent; /**< 接続元リスト(1:親ノード名 + 遷移, 2:親ノード) */ StringMap_t child; /**< 接続先リスト */ explicit DataToken( void ); DataToken( const DataToken& p ); virtual ~DataToken( void ); }; static const char TAGCHAR_NEXTSTATETAG; /**< 遷移先の識別符(?) */ static const char TAGCHAR_H; /**< 自立語部の品詞の識別符(?) */ static const char TAGCHAR_S; /**< 文節の属性の識別符(?) */ static const char TAGCHAR_C; /**< 活用形の識別符(?) */ static const char TAGCHAR_weak1; /**< 推移(:)の識別符(?) */ static const char TAGCHAR_weak2; /**< 推移(.)の識別符(?) */ static const char TAGCHAR_NUL; /**< 識別符(?)未指定 */ static const char TAGCHAR_DEPWORD; /**< 付属語の引用符(?) */ private: protected: DataTokenMap_t data; /**< データの実体。multimap。 */ public: void CheckSimplex( void ); void CheckConnection( void ); size_t ShowUnusedTokens( void ) const; explicit WordgraphTable( void ); virtual ~WordgraphTable( void ); }; /** 自立語テーブル/付属語グラフの遷移機械(基本部分) */ class WordgraphTransitionCommon : virtual public WordgraphTable { private: protected: /** 遷移ツリー */ struct TransitionTree { const struct TransitionTree* parent; /**< 親 */ DataTokenMap_t::const_iterator node; /**< 現ノード */ StringMap_t::const_iterator depword; /**< 使用した付属語 */ StringMap_t::const_iterator childf; /**< 使用した接続先(前方探索) */ DataTokenPtrMap_t::const_iterator childb; /**< 使用した接続先(後方探索) */ DataTokenMap_t::const_iterator child_node; /**< 接続先ノード */ char word[BUF_MAX]; /**< 現在地までの付属語を連結した物 */ size_t word_len; /**< word の長さ */ char H; /**< 自立語部の品詞 */ char S; /**< 文節の属性 */ char C; /**< 活用形 */ int weak1; /**< 推移(:) */ int weak2; /**< 推移(.) */ TransitionTree& operator =( const TransitionTree& p ); TransitionTree& operator =( int i ); explicit TransitionTree( void ); TransitionTree( int i ); TransitionTree( const TransitionTree& p ); virtual ~TransitionTree( void ); }; int verbose; /**< Verbose Level */ bool silent; /**< マッチしたノードを表示しない */ bool exact_match; /**< 完全一致したノードのみ表示 */ bool unused_only; /**< 未探索ノードのみ探索する */ char* matchstring; /**< 前方一致/後方一致の判定文字列 */ size_t matchstring_len; /**< matchstring の長さ */ size_t dump_len; /**< 検索する付属語の長さ */ public: void SetSearchConf_Verbose( int arg_verbose ); void SetSearchConf_Silent( bool arg_silent ); void SetSearchConf_ExactMatch( bool arg_exact_match ); void SetSearchConf_UnusedOnly( bool arg_unused_only ); void SetSearchConf_MatchString( const char* const str ); void SetSearchConf_DumpLength( size_t len ); explicit WordgraphTransitionCommon( void ); virtual ~WordgraphTransitionCommon( void ); }; /** 自立語テーブル/付属語グラフの遷移機械(前方検索用) */ class WordgraphTransitionForward : virtual public WordgraphTransitionCommon { private: protected: bool DumpToken_Forward( const char* const msg, const struct TransitionTree* const p ) const; size_t DumpTokenRecursionForward( const struct TransitionTree* const parent ); public: size_t DumpTokensForward( const char* const node_name ); size_t DumpAllTokensForward( void ); explicit WordgraphTransitionForward( void ); virtual ~WordgraphTransitionForward( void ); }; /** 自立語テーブル/付属語グラフの遷移機械(後方検索用) */ class WordgraphTransitionBackward : virtual public WordgraphTransitionCommon { private: protected: bool DumpToken_Backward( const char* const msg, const struct TransitionTree* const p ) const; size_t DumpTokenRecursionBackward( const struct TransitionTree* const parent ); size_t DumpTokenRecursionBackward_Start( const DataTokenMap_t::iterator p ); public: size_t DumpTokensBackward( const char* const node_name ); size_t DumpAllTokensBackward( void ); explicit WordgraphTransitionBackward( void ); virtual ~WordgraphTransitionBackward( void ); }; /** 自立語テーブル/付属語グラフのパーサ */ class WordgraphParser : virtual public WordgraphTable { private: static const char TAG_INCLUDE[]; /**< include タグ */ static const char TAGCHAR_COMMENT; /**< コメントタグ */ protected: public: char* get_one_string( char* buf, char** __restrict__ const next_ptr ) const; bool load( const char* const dir, const char* const filename ); explicit WordgraphParser( void ) { return; } virtual ~WordgraphParser( void ) { return; } }; /** 自立語テーブル/付属語グラフ */ class Wordgraph : virtual public WordgraphTable, virtual public WordgraphTransitionForward, virtual public WordgraphTransitionBackward, virtual public WordgraphParser { private: protected: public: explicit Wordgraph( void ) { return; } virtual ~Wordgraph( void ) { return; } }; /** 環境設定 */ class Settings { public: /** 環境設定の実体 */ struct Settings_t { char dir[PATH_MAX]; /**< 読み込み元ディレクトリ */ char indepword[PATH_MAX]; /**< 自立語ファイル */ char depword[PATH_MAX]; /**< 付属語ファイル */ bool reverse; /**< 逆順モード */ char start_node[BUF_MAX]; /**< 探索を開始するノード */ char word[BUF_MAX]; /**< 一致判定する文字列 */ size_t max_len; /**< 探索する最大長 */ bool silent; /**< Silent Mode */ bool exact_match; /**< 完全一致したノードのみ表示 */ bool unused_only; /**< 未探索ノードのみ探索する */ bool show_unused; /**< 未使用ノードの表示を行う */ int verbose_level; /**< Verbose Level */ }; private: struct Settings_t settings; /**< 環境設定の中身 */ protected: public: void print_credits( void ) const; void print_version( void ) const; void print_useage( void ) const; const struct Settings_t& get( void ) const { return settings; }; bool parse_argument( int argc, char* const* const argv ); explicit Settings( void ); virtual ~Settings( void ); }; //@} const WordgraphTable::StringMap_t::const_iterator WordgraphTable::NULL_StringMap; const WordgraphTable::StringStringMap_t::const_iterator WordgraphTable::NULL_StringStringMap; const WordgraphTable::DataTokenMap_t::const_iterator WordgraphTable::NULL_DataTokenMap; const WordgraphTable::DataTokenPtrMap_t::const_iterator WordgraphTable::NULL_DataTokenPtrMap; const char WordgraphTable::TAGCHAR_NEXTSTATETAG = '@'; const char WordgraphTable::TAGCHAR_H = 'H'; const char WordgraphTable::TAGCHAR_S = 'S'; const char WordgraphTable::TAGCHAR_C = 'C'; const char WordgraphTable::TAGCHAR_weak1 = ':'; const char WordgraphTable::TAGCHAR_weak2 = '.'; const char WordgraphTable::TAGCHAR_NUL = ' '; const char WordgraphTable::TAGCHAR_DEPWORD = '"'; const char WordgraphParser::TAG_INCLUDE[] = "\\\\include"; const char WordgraphParser::TAGCHAR_COMMENT = '#'; WordgraphTable::DataToken::DataToken( void ) { type = UNKNOWN; used = false; leaf = false; return; } WordgraphTable::DataToken::DataToken( const DataToken& p ) { if (NULL == &p) { type = UNKNOWN; used = false; leaf = false; } else { type = p.type; used = false; leaf = p.leaf; } return; } WordgraphTable::DataToken::~DataToken( void ) { return; } WordgraphTable::WordgraphTable( void ) { return; } WordgraphTable::~WordgraphTable( void ) { return; } WordgraphTransitionCommon::TransitionTree& WordgraphTransitionCommon::TransitionTree::operator =( const TransitionTree& p ) { parent = p.parent; node = p.node; depword = p.depword; childf = p.childf; childb = p.childb; child_node = p.child_node; strlcpy( word, p.word, _number_(word) ); word_len = p.word_len; H = p.H; S = p.S; C = p.C; weak1 = p.weak1; weak2 = p.weak2; return *this; } WordgraphTransitionCommon::TransitionTree::TransitionTree( void ) { LOG(LOG_DEBUG,"WordgraphTransitionCommon::TransitionTree::TransitionTree( void ), Not initialized." ); return; } WordgraphTransitionCommon::TransitionTree::TransitionTree( int i ) { assert( 0 == i ); parent = NULL; node = NULL_DataTokenMap; depword = NULL_StringMap; childf = NULL_StringMap; childb = NULL_DataTokenPtrMap; child_node = NULL_DataTokenMap; word[0] = '\0'; word_len = 0; H = TAGCHAR_NUL; S = TAGCHAR_NUL; C = TAGCHAR_NUL; weak1 = 0; weak2 = 0; return; } WordgraphTransitionCommon::TransitionTree& WordgraphTransitionCommon::TransitionTree::operator =( int i ) { assert( 0 == i ); parent = NULL; node = NULL_DataTokenMap; depword = NULL_StringMap; childf = NULL_StringMap; childb = NULL_DataTokenPtrMap; child_node = NULL_DataTokenMap; word[0] = '\0'; word_len = 0; H = TAGCHAR_NUL; S = TAGCHAR_NUL; C = TAGCHAR_NUL; weak1 = 0; weak2 = 0; return *this; } WordgraphTransitionCommon::TransitionTree::TransitionTree( const TransitionTree& p ) { parent = p.parent; node = p.node; depword = p.depword; childf = p.childf; childb = p.childb; child_node = p.child_node; strlcpy( word, p.word, _number_(word) ); word_len = p.word_len; H = p.H; S = p.S; C = p.C; weak1 = p.weak1; weak2 = p.weak2; return; } WordgraphTransitionCommon::TransitionTree::~TransitionTree( void ) { return; } /** ノード単体での整合性チェック */ void WordgraphTable::CheckSimplex( void ) { for (DataTokenMap_t::iterator p = data.begin(); p != data.end(); ++p) { const char* const tag = p->first.c_str(); const StringMap_t::const_iterator i_b = p->second.depword.begin(); const StringMap_t::const_iterator i_e = p->second.depword.end(); if (i_b == i_e) { if (DataToken::DEPWORD == p->second.type) { LOG(LOG_ERR,"No depword. '%s'.", tag ); } const std::pair n_ret = p->second.depword.insert( StringPair_t("",false) ); } const StringMap_t::const_iterator j_b = p->second.child.begin(); const StringMap_t::const_iterator j_e = p->second.child.end(); if (j_b == j_e) { LOG(LOG_ERR,"No child state. '%s'.", tag ); } } return; } /** 接続先の有無と重複のチェック */ void WordgraphTable::CheckConnection( void ) { char parent_idx[BUF_MAX]; for (DataTokenMap_t::iterator p = data.begin(); p != data.end(); ++p) { const char* const tag = p->first.c_str(); const StringMap_t::const_iterator j_b = p->second.child.begin(); const StringMap_t::const_iterator j_e = p->second.child.end(); for (StringMap_t::const_iterator j = j_b; j != j_e; ++j) { const char* const next_token = j->first.c_str(); const char* const next_key = strchr( next_token, TAGCHAR_NEXTSTATETAG ); if ((NULL == next_key) || ('\0' == next_key[0]) || ('\0' == next_key[1])) { /* 末端到達 */ p->second.leaf = true; continue; } snprintf(parent_idx,_number_(parent_idx), "%s %s", tag, next_token ); const DataTokenMap_t::iterator k_b = data.lower_bound( next_key ); const DataTokenMap_t::const_iterator k_e = data.upper_bound( next_key ); if (k_b == k_e) { LOG(LOG_ERR,"Unknown state. '%s' in '%s'.", next_token, tag ); } for (DataTokenMap_t::iterator k = k_b; k != k_e; ++k) { const std::pair n_ret = k->second.parent.insert( DataTokenPtrPair_t(parent_idx,p) ); if (!n_ret.second) { LOG(LOG_NOTICE,"Duplicated parent state. '%s' at '%s' in '%s'.", next_token, parent_idx, tag ); } } } } return; } /** Verbose Level を設定する *@param arg_verbose Verbose Level */ void WordgraphTransitionCommon::SetSearchConf_Verbose( int arg_verbose ) { verbose = arg_verbose; return; } /** Silent Mode を設定する *@param arg_silent Silent Mode */ void WordgraphTransitionCommon::SetSearchConf_Silent( bool arg_silent ) { silent = arg_silent; return; } /** Exact Match Mode を設定する *@param arg_exact_match Exact Match Mode */ void WordgraphTransitionCommon::SetSearchConf_ExactMatch( bool arg_exact_match ) { exact_match = arg_exact_match; return; } /** UnusedOnly を設定する *@param arg_unused_only UnusedOnly Mode */ void WordgraphTransitionCommon::SetSearchConf_UnusedOnly( bool arg_unused_only ) { unused_only = arg_unused_only; return; } /** 前方一致/後方一致の判定文字列を設定する *@param str 設定する文字列 */ void WordgraphTransitionCommon::SetSearchConf_MatchString( const char* const str ) { if (matchstring) { free( matchstring ); matchstring = NULL; matchstring_len = 0; } if (str) { matchstring = strdup( str ); matchstring_len = strlen( matchstring ); } return; } /** 検索する付属語の長さを設定する *@param len 設定する長さ */ void WordgraphTransitionCommon::SetSearchConf_DumpLength( size_t len ) { dump_len = len; return; } WordgraphTransitionCommon::WordgraphTransitionCommon( void ) { verbose = 0; silent = false; exact_match = false; unused_only = false; matchstring = NULL; matchstring_len = 0; dump_len = 0; return; } WordgraphTransitionCommon::~WordgraphTransitionCommon( void ) { if (matchstring) { free( matchstring ); matchstring = NULL; matchstring_len = 0; } return; } /** 指定されたトークンまでの接続を表示する *@param[in] msg 表示するメッセージ *@param[in] p 現在地までの遷移結果 *@retval true 検索条件に一致した。表示した。 *@retval false 検索条件に一致しなかった。表示しなかった。 */ bool WordgraphTransitionForward::DumpToken_Forward( const char* const msg, const struct TransitionTree* const p ) const { if (exact_match && (0 != strcmp(matchstring,p->word))) { return false; } if (silent) { return true; } const struct TransitionTree* top_node = NULL; char trans_graph_buf1[BUFLONG_MAX] = ""; char trans_graph_buf2[BUFLONG_MAX] = ""; char* trans_graph1 = trans_graph_buf1; char* trans_graph2 = trans_graph_buf2; char* trans_graph_tmp; for ( const struct TransitionTree* ptr = p; (NULL != ptr) && (NULL_DataTokenMap != ptr->node); ptr = ptr->parent) { top_node = ptr; const DataTokenMap_t::const_iterator node = ptr->node; const StringMap_t::const_iterator depword = ptr->depword; const StringMap_t::const_iterator child = ptr->childf; if (DataToken::DEPWORD == node->second.type) { snprintf( trans_graph2, _number_(trans_graph_buf1), " \"%s\" %s%s", (NULL_StringMap != depword) ? depword->first.c_str() : "broken", (NULL_StringMap != child) ? child->first.c_str() : "", trans_graph1 ); } else { snprintf( trans_graph2, _number_(trans_graph_buf1), " %s%s", (NULL_StringMap != child) ? child->first.c_str() : "", trans_graph1 ); } trans_graph_tmp = trans_graph2; trans_graph2 = trans_graph1; trans_graph1 = trans_graph_tmp; } char root_tag[BUF_MAX]; strlcpy( root_tag, top_node->node->first.c_str(), _number_(root_tag) ); fprintf(stdout, "%s%s|%s H%cS%cC%c :%d.%d%s\n", msg, root_tag, p->word, p->H, p->S, p->C, p->weak1, p->weak2, trans_graph1 ); return true; } /** 指定されたトークンの全接続結果を表示する *@param[in] parent 現在地までの遷移結果 *@return 検出されたパターン数 */ size_t WordgraphTransitionForward::DumpTokenRecursionForward( const struct TransitionTree* const parent ) { const DataTokenMap_t::const_iterator current_node = parent->child_node; const char* const tag = current_node->first.c_str(); struct TransitionTree node_default = *parent; node_default.parent = parent; node_default.node = current_node; node_default.depword = NULL_StringMap; node_default.childf = NULL_StringMap; node_default.child_node = NULL_DataTokenMap; struct TransitionTree node = node_default; size_t num = 0; const StringMap_t::const_iterator i_b = current_node->second.depword.begin(); const StringMap_t::const_iterator i_e = current_node->second.depword.end(); for (StringMap_t::const_iterator i = i_b; i != i_e; ++i) { node_default.depword = i; node_default.word_len = snprintf( node_default.word, _number_(node_default.word), "%s%s", parent->word, i->first.c_str() ); if (matchstring) { const size_t len = (node_default.word_len < matchstring_len) ? node_default.word_len : matchstring_len; const int ret = strncmp( node_default.word, matchstring, len ); if (0 != ret) { continue; } } if ((0 < dump_len) && (dump_len <= node_default.word_len)) { /* 強制枝切り */ DumpToken_Forward( "Cut: ", &node_default ); continue; } const StringMap_t::const_iterator j_b = current_node->second.child.begin(); const StringMap_t::const_iterator j_e = current_node->second.child.end(); for (StringMap_t::const_iterator j = j_b; j != j_e; ++j) { node = node_default; node.childf = j; const char* const next_token = j->first.c_str(); { /* 状態変化の抽出 */ const char* ptr = next_token; for (bool loop_break = false; !loop_break; ++ptr) { switch (*ptr) { case TAGCHAR_NEXTSTATETAG: case '\0': loop_break = true; break; case TAGCHAR_H: ++ptr; node.H = *ptr; break; case TAGCHAR_S: ++ptr; node.S = *ptr; break; case TAGCHAR_C: ++ptr; node.C = *ptr; break; case TAGCHAR_weak1: ++ node.weak1; break; case TAGCHAR_weak2: ++ node.weak2; break; default: LOG(LOG_ERR,"Unknown tag. '%c' at '%s' in '%s'.", ptr[0], next_token, tag ); break; } } } { /* 遷移の実行 */ const char* const next_key = strchr( next_token, TAGCHAR_NEXTSTATETAG ); if ((NULL == next_key) || ('\0' == next_key[0]) || ('\0' == next_key[1])) { /* 末端到達 */ num += DumpToken_Forward( "End: ", &node ) ? 1 : 0; continue; } const DataTokenMap_t::iterator k_b = data.lower_bound( next_key ); const DataTokenMap_t::const_iterator k_e = data.upper_bound( next_key ); for (DataTokenMap_t::iterator k = k_b; k != k_e; ++k) { if (unused_only && k->second.used) { DumpToken_Forward( "Already Traversed: ", &node ); continue; } k->second.used = true; node.child_node = k; bool loop_break = false; for ( const struct TransitionTree* ptr = parent; NULL != ptr; ptr = ptr->parent) { if (ptr->child_node == k) { loop_break = true; /* ループ検出 */ } } if (loop_break) { num += DumpToken_Forward( "Loop: ", &node ) ? 1 : 0; continue; } num += DumpTokenRecursionForward( &node ); } } } } return num; } /** 指定されたノードからの接続結果を表示する *@param[in] node_name 探索を開始するノード名 *@return 検出されたパターン数 */ size_t WordgraphTransitionForward::DumpTokensForward( const char* const node_name ) { size_t num = 0; struct TransitionTree root = 0; const DataTokenMap_t::iterator i_b = data.lower_bound( node_name ); const DataTokenMap_t::const_iterator i_e = data.upper_bound( node_name ); if (i_b == i_e) { fprintf(stdout,"No root token.\n" ); } for (DataTokenMap_t::iterator i = i_b; i != i_e; ++i) { root.child_node = i; i->second.used = true; num += DumpTokenRecursionForward( &root ); } return num; } /** 全トークンの接続結果を表示する *@return 検出されたパターン数 */ size_t WordgraphTransitionForward::DumpAllTokensForward( void ) { size_t num = 0; struct TransitionTree root = 0; const DataTokenMap_t::iterator i_b = data.begin(); const DataTokenMap_t::const_iterator i_e = data.end(); for (DataTokenMap_t::iterator i = i_b; i != i_e; ++i) { if (TAGCHAR_NEXTSTATETAG == i->first.c_str()[0]) { continue; } if (verbose) { LOG(LOG_DEBUG,"%s", i->first.c_str() ); } root.child_node = i; i->second.used = true; num += DumpTokenRecursionForward( &root ); } return num; } WordgraphTransitionForward::WordgraphTransitionForward( void ) { return; } WordgraphTransitionForward::~WordgraphTransitionForward( void ) { return; } /** 指定されたトークンまでの接続を表示する *@param[in] msg 表示するメッセージ *@param[in] p 現在地までの遷移結果 *@retval true 検索条件に一致した。表示した。 *@retval false 検索条件に一致しなかった。表示しなかった。 */ bool WordgraphTransitionBackward::DumpToken_Backward( const char* const msg, const struct TransitionTree* const p ) const { if (exact_match && (0 != strcmp(matchstring,p->word))) { return false; } if (silent) { return true; } const struct TransitionTree* last_node = NULL; char trans_graph_buf1[BUFLONG_MAX] = ""; char trans_graph_buf2[BUFLONG_MAX] = ""; char* trans_graph1 = trans_graph_buf1; char* trans_graph2 = trans_graph_buf2; char* trans_graph_tmp; last_node = p; for ( const struct TransitionTree* ptr = p->parent; (NULL != ptr) && (NULL_DataTokenMap != ptr->node); ptr = ptr->parent) { last_node = ptr; const DataTokenMap_t::const_iterator node = ptr->node; const StringMap_t::const_iterator depword = ptr->depword; const DataTokenPtrMap_t::const_iterator child = ptr->childb; const char* const child_state_tmp = (NULL_DataTokenPtrMap != child) ? strchr( child->first.c_str(), TAGCHAR_NUL ) : " broken"; if (child_state_tmp) { const char* const child_state = child_state_tmp + 1; if (DataToken::DEPWORD == node->second.type) { snprintf( trans_graph2, _number_(trans_graph_buf1), "%s %s \"%s\"", trans_graph1, child_state, (NULL_StringMap != depword) ? depword->first.c_str() : "broken" ); } else { snprintf( trans_graph2, _number_(trans_graph_buf1), "%s %s", trans_graph1, child_state ); } } else { LOG(LOG_ERR,"Unknown Error. '%s'.", child_state_tmp ); } trans_graph_tmp = trans_graph2; trans_graph2 = trans_graph1; trans_graph1 = trans_graph_tmp; } char root_tag[BUF_MAX]; strlcpy( root_tag, p->node->first.c_str(), _number_(root_tag) ); fprintf(stdout, "%s%s|%s H%cS%cC%c :%d.%d%s %s\n", msg, root_tag, p->word, p->H, p->S, p->C, p->weak1, p->weak2, trans_graph1, (NULL_StringMap != last_node->childf) ? last_node->childf->first.c_str() : "" ); return true; } /** 指定されたトークンの全接続結果を表示する *@param[in] parent 現在地までの遷移結果 *@return 検出されたパターン数 */ size_t WordgraphTransitionBackward::DumpTokenRecursionBackward( const struct TransitionTree* const parent ) { const DataTokenMap_t::const_iterator current_node = parent->child_node; const char* const tag = current_node->first.c_str(); struct TransitionTree node_default = *parent; node_default.parent = parent; node_default.node = current_node; node_default.depword = NULL_StringMap; node_default.childb = NULL_DataTokenPtrMap; node_default.child_node = NULL_DataTokenMap; struct TransitionTree node = node_default; size_t num = 0; const StringMap_t::const_iterator i_b = current_node->second.depword.begin(); const StringMap_t::const_iterator i_e = current_node->second.depword.end(); for (StringMap_t::const_iterator i = i_b; i != i_e; ++i) { if (DataToken::INDEPWORD == current_node->second.type) { strlcpy( node_default.word, parent->word, _number_(node_default.word) ); node_default.word_len = parent->word_len; } else { node_default.depword = i; node_default.word_len = snprintf( node_default.word, _number_(node_default.word), "%s%s", i->first.c_str(), parent->word ); if (matchstring) { const size_t len = (node_default.word_len < matchstring_len) ? node_default.word_len : matchstring_len; const int ret = strncmp( (node_default.word + node_default.word_len - len), (matchstring + matchstring_len - len), len ); if (0 != ret) { continue; } } if ((0 < dump_len) && (dump_len <= node_default.word_len)) { /* 強制枝切り */ DumpToken_Backward( "Cut: ", &node_default ); continue; } } const DataTokenPtrMap_t::const_iterator j_b = current_node->second.parent.begin(); const DataTokenPtrMap_t::const_iterator j_e = current_node->second.parent.end(); if (j_b == j_e) { /* 末端到達 */ num += DumpToken_Backward( "End: ", &node_default ) ? 1 : 0; return num; } for (DataTokenPtrMap_t::const_iterator j = j_b; j != j_e; ++j) { node = node_default; node.childb = j; { /* 状態変化の抽出 */ const char* const next_token = strchr( j->first.c_str(), TAGCHAR_NUL ); if (next_token) { const char* ptr = next_token + 1; for (bool loop_break = false; !loop_break; ++ptr) { switch (*ptr) { case TAGCHAR_NEXTSTATETAG: case '\0': loop_break = true; break; case TAGCHAR_H: ++ptr; if (TAGCHAR_NUL == node.H) { node.H = *ptr; } break; case TAGCHAR_S: ++ptr; if (TAGCHAR_NUL == node.S) { node.S = *ptr; } break; case TAGCHAR_C: ++ptr; if (TAGCHAR_NUL == node.C) { node.C = *ptr; } break; case TAGCHAR_weak1: ++ node.weak1; break; case TAGCHAR_weak2: ++ node.weak2; break; default: LOG(LOG_ERR,"Unknown tag. '%c' at '%s' in '%s'.", ptr[0], next_token, tag ); break; } } } else { LOG(LOG_ERR,"Unknown tag. '%s' in '%s'.", next_token, tag ); } } /* 遷移の実行 */ { const DataTokenMap_t::iterator k = j->second; if (unused_only && k->second.used) { DumpToken_Backward( "Already Traversed: ", &node ); continue; } k->second.used = true; node.child_node = k; bool loop_break = false; for ( const struct TransitionTree* ptr = parent; NULL != ptr; ptr = ptr->parent) { if (ptr->child_node == k) { loop_break = true; /* ループ検出 */ } } if (loop_break) { num += DumpToken_Backward( "Loop: ", &node ) ? 1 : 0; continue; } num += DumpTokenRecursionBackward( &node ); } } } return num; } /** 指定されたトークンの全接続結果を表示する *@param[in] p 探索開始ノード *@return 検出されたパターン数 */ size_t WordgraphTransitionBackward::DumpTokenRecursionBackward_Start( const DataTokenMap_t::iterator p ) { size_t num = 0; struct TransitionTree root = 0; struct TransitionTree node = 0; root.child_node = p; p->second.used = true; const StringMap_t::const_iterator i_b = p->second.child.begin(); const StringMap_t::const_iterator i_e = p->second.child.end(); if (i_b == i_e) { /* 末端到達 */ num += DumpToken_Backward( "End(Unterminated): ", &root ) ? 1 : 0; return num; } for (StringMap_t::const_iterator i = i_b; i != i_e; ++i) { node = root; node.childf = i; const char* const next_token = i->first.c_str(); { /* 状態変化の抽出 */ const char* ptr = next_token; for (bool loop_break = false; !loop_break; ++ptr) { switch (*ptr) { case TAGCHAR_NEXTSTATETAG: case '\0': loop_break = true; break; case TAGCHAR_H: ++ptr; if (TAGCHAR_NUL == node.H) { node.H = *ptr; } break; case TAGCHAR_S: ++ptr; if (TAGCHAR_NUL == node.S) { node.S = *ptr; } break; case TAGCHAR_C: ++ptr; if (TAGCHAR_NUL == node.C) { node.C = *ptr; } break; case TAGCHAR_weak1: ++ node.weak1; break; case TAGCHAR_weak2: ++ node.weak2; break; default: LOG(LOG_ERR,"Unknown tag. '%c' at '%s' in '%s'.", ptr[0], next_token, p->first.c_str() ); break; } } const char* const next_key = strchr( next_token, TAGCHAR_NEXTSTATETAG ); if ((NULL == next_key) || ('\0' == next_key[0]) || ('\0' == next_key[1])) { /* 末端到達 */ num += DumpTokenRecursionBackward( &node ); } } } return num; } /** 指定されたノードからの接続結果を表示する *@param[in] node_name 探索を開始するノード名 *@return 検出されたパターン数 */ size_t WordgraphTransitionBackward::DumpTokensBackward( const char* const node_name ) { size_t num = 0; struct TransitionTree root = 0; const DataTokenMap_t::iterator i_b = data.lower_bound( node_name ); const DataTokenMap_t::const_iterator i_e = data.upper_bound( node_name ); if (i_b == i_e) { fprintf(stdout,"No leaf token.\n" ); } for (DataTokenMap_t::iterator i = i_b; i != i_e; ++i) { root.child_node = i; i->second.used = true; num += DumpTokenRecursionBackward( &root ); } return num; } /** 全トークンの接続結果を表示する *@return 検出されたパターン数 */ size_t WordgraphTransitionBackward::DumpAllTokensBackward( void ) { size_t num = 0; const DataTokenMap_t::iterator i_b = data.begin(); const DataTokenMap_t::const_iterator i_e = data.end(); for (DataTokenMap_t::iterator i = i_b; i != i_e; ++i) { if (!i->second.leaf) { continue; } if (verbose) { LOG(LOG_DEBUG,"%s", i->first.c_str() ); } num += DumpTokenRecursionBackward_Start( i ); } return num; } WordgraphTransitionBackward::WordgraphTransitionBackward( void ) { return; } WordgraphTransitionBackward::~WordgraphTransitionBackward( void ) { return; } /** 未使用のトークンを表示する *@return 未使用だった数量 */ size_t WordgraphTable::ShowUnusedTokens( void ) const { size_t num = 0; const DataTokenMap_t::const_iterator i_b = data.begin(); const DataTokenMap_t::const_iterator i_e = data.end(); for (DataTokenMap_t::const_iterator i = i_b; i != i_e; ++i) { if (!i->second.used) { ++num; fprintf(stdout,"Unused token: '%s'\n", i->first.c_str() ); } } fprintf(stdout,"Total %zu unused token\n", num ); return num; } /** 文字列を1つ切り出す *@param[in,out] buf 解析開始位置 *@param[in,out] next_ptr 切り出した文字列の次。終端ならば NULL。 *@return 切り出した文字列。無ければ NULL。 * *@comment * buf中の、切り出した文字列の最後の位置は NUL に書き換えられる。 */ char* WordgraphParser::get_one_string( char* buf, char** __restrict__ const next_ptr ) const { assert( NULL != buf ); assert( NULL != next_ptr ); while (true) { switch (*buf) { case ' ': case '\t': case '\r': case '\n': ++buf; continue; } break; } char* const ret = buf; for (;true; ++buf) { switch (*buf) { case '\0': case TAGCHAR_COMMENT: (*next_ptr) = NULL; return NULL; case ' ': case '\t': case '\r': case '\n': (*buf) = '\0'; ++buf; (*next_ptr) = ('\0' != (*buf)) ? buf : NULL; return ret; } } } /** ファイル読み込み *@param[in] dir 読み込み元ディレクトリ *@param[in] filename 読み込みファイル名 *@retval true 読み込み成功 *@retval false 読み込み中にエラーが発生した */ bool WordgraphParser::load( const char* const dir, const char* const filename ) { assert( NULL != dir ); assert( NULL != filename ); FILE* fp = NULL; LOG(LOG_DEBUG,"File('%s') loading...", filename ); /* オープン */ { fp = fopen( filename, "r" ); if (NULL == fp) { char name[PATH_MAX]; snprintf( name, _number_(name), "%s%s", dir, filename ); fp = fopen( name, "r" ); if (NULL == fp) { LOG(LOG_CRIT,"File('%s','%s') open error.(%d:%s)", dir, filename, errno, strerror(errno) ); return false; } } } /* 読み込み & パース */ { char buf[BUF_MAX]; size_t line_num = 0; while (!feof(fp)) { fgets( buf, _number_(buf), fp ); ++line_num; char* ptr = buf; while (true) { const char* const tag = get_one_string( ptr, &ptr ); if (NULL == tag) { break; } if (0 == strncasecmp(TAG_INCLUDE, tag, strlen(TAG_INCLUDE))) { if (NULL == ptr) { LOG(LOG_ERR,"Parse error. No include filename. File:%s, Line:%zu, Tag:'%s'", filename, line_num, tag ); break; } const char* const name = get_one_string( ptr, &ptr ); load( dir, name ); break; } else { const DataTokenMap_t::iterator token = data.insert( DataTokenPair_t(tag,*(static_cast(NULL))) ); if (NULL == ptr) { LOG(LOG_ERR,"Parse error. No argument. File:%s, Line:%zu, Tag:'%s'", filename, line_num, tag ); break; } char* word = get_one_string( ptr, &ptr ); if (NULL == word) { LOG(LOG_ERR,"Parse error. No argument. File:%s, Line:%zu, Tag:'%s'", filename, line_num, tag ); break; } if (TAGCHAR_DEPWORD == word[0]) { if (WordgraphTable::DataToken::DEPWORD != token->second.type) { if (WordgraphTable::DataToken::UNKNOWN == token->second.type) { token->second.type = WordgraphTable::DataToken::DEPWORD; } else { LOG(LOG_ERR,"Parse error. Type was mismatch. File:%s, Line:%zu, Tag:'%s'", filename, line_num, tag ); break; } } while (true) { do { const size_t len = strlen( word ); if (TAGCHAR_DEPWORD != word[len-1]) { LOG(LOG_ERR,"Parse error. Depword was not quoted. File:%s, Line:%zu, Tag:'%s'", filename, line_num, tag ); break; } word[len-1] = '\0'; ++word; const std::pair s_ret = token->second.depword.insert( StringPair_t(word,false) ); if (!s_ret.second) { LOG(LOG_NOTICE,"Duplicated depword. File:%s, Line:%zu, Tag:'%s', Depword:'%s'", filename, line_num, tag, word ); } } while(false); if (NULL == ptr) { LOG(LOG_ERR,"Parse error. No argument. File:%s, Line:%zu, Tag:'%s'", filename, line_num, tag ); break; } word = get_one_string( ptr, &ptr ); if (NULL == word) { LOG(LOG_ERR,"Parse error. No argument. File:%s, Line:%zu, Tag:'%s'", filename, line_num, tag ); break; } if (TAGCHAR_DEPWORD != word[0]) { break; } } } else { if (WordgraphTable::DataToken::INDEPWORD != token->second.type) { if (WordgraphTable::DataToken::UNKNOWN == token->second.type) { token->second.type = WordgraphTable::DataToken::INDEPWORD; } else { LOG(LOG_ERR,"Parse error. Type was mismatch. File:%s, Line:%zu, Tag:'%s'", filename, line_num, tag ); break; } } } while (true) { do { const std::pair n_ret = token->second.child.insert( StringPair_t(word,false) ); if (!n_ret.second) { LOG(LOG_NOTICE,"Duplicated child state. File:%s, Line:%zu, Tag:'%s', NextState:'%s'", filename, line_num, tag, word ); } } while(false); if (NULL == ptr) { break; } word = get_one_string( ptr, &ptr ); if (NULL == word) { break; } } } break; } } } /* クローズ */ const int fc_ret = fclose( fp ); if (fc_ret) { LOG(LOG_CRIT,"File('%s','%s') close error.(%d:%s)", dir, filename, errno, strerror(errno) ); } return true; } /** credits の表示 */ void Settings::print_credits( void ) const { fprintf(stdout, "\tAS IS\tCopyright(C)2009 G-HAL\n" ); fprintf(stdout, "\n" ); return; } /** version の表示 */ void Settings::print_version( void ) const { fprintf(stdout, "%s: - %s - Dump depgraph table\n", PACKAGE_NAME, PACKAGE_VERSION ); fprintf(stdout, "\tCompile Options: " ); #if defined(DEBUG) fprintf(stdout, "DEBUG=%d ", static_cast(DEBUG) ); #endif fprintf(stdout, "\n" ); return; } /** usage の表示 */ void Settings::print_useage( void ) const { fprintf(stdout, "useage: %s [options]...\n\n", PACKAGE_NAME ); fprintf(stdout, " Options are:\n" " -p --path (path-name)\t" "Set a default path to load files.\n" " -i --indepword (file-name)\t" "Set a filename of indepword table.\n" " -d --depword (file-name)\t" "Set a filename of depword table.\n" " -r --reverse\t\t\t" "Backward output mode.\n" " -n --start-node (string)\t" "Set a starting node to searching.\n" " -w --word (string)\t\t" "String to retrieving while searching.\n" " -m --max-len (int)\t\t" "Set a maximum length of searching depwords. (Two is multiplied for EUC)\n" " -S, --silent\t\t\t" "Silent mode.\n" " -e, --exatct-match-only\t" "Exact match only.\n" " -U, --unused-only\t\t" "Traverse nodes only untraversed nodes.\n" " -s, --show-unused-node\t" "Show unused nodes.\n" " -H, --help\t\t\t" "Show these messages.\n" " -C, --credits\t\t\t" "Show credits.\n" " --version\t\t\t" "Show release version.\n" "\n" ); return; } /** 引数の解析 *@param argc 引数の数 *@param[in] argv 引数の中身 *@retval true 解析成功 *@retval false 解析中にエラーが発生した */ bool Settings::parse_argument( int argc, char* const* const argv ) { static const struct option long_options[] = { {"path", required_argument, NULL, 'p'}, {"indepword", required_argument, NULL, 'i'}, {"depword", required_argument, NULL, 'd'}, {"reverse", no_argument, NULL, 'r'}, {"start-node", required_argument, NULL, 'n'}, {"word", required_argument, NULL, 'w'}, {"max-len", required_argument, NULL, 'm'}, {"silent", no_argument, NULL, 'S'}, {"exact-match-only", no_argument, NULL, 'e'}, {"unused-only", no_argument, NULL, 'U'}, {"show-unused-node", no_argument, NULL, 's'}, {"verbose", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'H'}, {"version", no_argument, NULL, 0x80}, {"credits", no_argument, NULL, 'C'}, {0,no_argument,0,0} }; static const char short_options[] = "*" "p"":" "i"":" "d"":" "r" "n"":" "w"":" "m"":" "S" "e" "U" "s" "v" "H" "\x80" "C"; bool dontrun = false; bool someerr = false; bool opt_err = false; int opt; __unused int longidx = 0; while ((opt = getopt_long(argc, argv, short_options, long_options, &longidx)) != -1) { opt &= 0xFF; switch (opt) { case '*': break; case ':': someerr = true; LOG(LOG_ERR,"An argument is required."); break; case '?': someerr = true; LOG(LOG_ERR,"Unknown option(s)."); break; case 'p': strlcpy( settings.dir, optarg, _number_(settings.dir) ); break; case 'i': strlcpy( settings.indepword, optarg, _number_(settings.indepword) ); break; case 'd': strlcpy( settings.depword, optarg, _number_(settings.depword) ); break; case 'r': settings.reverse = true; break; case 'n': strlcpy( settings.start_node, optarg, _number_(settings.start_node) ); break; case 'w': strlcpy( settings.word, optarg, _number_(settings.word) ); settings.max_len = strlen( settings.word ) + 1; break; case 'm': { const long num = strtol( optarg, NULL, 0 ); if ((num < 0) || (BUF_MAX < num)) { LOG(LOG_ERR,"An argument of -m is out of range."); someerr = true; } else { settings.max_len = num * 2; } } break; case 'S': settings.silent = true; break; case 'e': settings.exact_match = true; break; case 'U': settings.unused_only = true; break; case 's': settings.show_unused = true; break; case 'v': ++ settings.verbose_level; break; case 'H': dontrun = true; print_version(); print_credits(); print_useage(); break; case 0x80: dontrun = true; print_version(); break; case 'C': dontrun = true; print_credits(); break; default: someerr = true; LOG(LOG_ERR,"Unknown option(s):%02X\n", opt ); break; } if (opt_err) { LOG(LOG_ERR, "Argument parameter error. %02X:'%s'.\n", opt, optarg ); } opt_err = false; } if (someerr) { LOG(LOG_ERR, "Argument parse error, or argument parameter error.\n" "Try `%s --help' for more information\n", argv[0] ); dontrun = true; } return !dontrun; } Settings::Settings( void ) { strlcpy( settings.dir, "./", _number_(settings.dir) ); strlcpy( settings.indepword, "indepword.txt", _number_(settings.indepword) ); strlcpy( settings.depword, "master.depword", _number_(settings.depword) ); settings.reverse = false; strlcpy( settings.start_node, "", _number_(settings.start_node) ); strlcpy( settings.word, "", _number_(settings.word) ); settings.max_len = 4 *2; settings.silent = false; settings.exact_match = false; settings.unused_only = false; settings.show_unused = false; settings.verbose_level = 0; return; } Settings::~Settings( void ) { return; } /** main() */ int main( int argc, char *argv[], _unused char *env[] ) { class Settings settings; { const bool ret = settings.parse_argument( argc, argv ); if (!ret) { exit(-1); } } { class Wordgraph wordgraph; /* 読み込み */ { const bool ret = wordgraph.load( settings.get().dir, settings.get().indepword ); if (!ret) { ; } } { const bool ret = wordgraph.load( settings.get().dir, settings.get().depword ); if (!ret) { ; } } /* 検査 */ wordgraph.CheckSimplex(); wordgraph.CheckConnection(); /* 検索 & 表示1 */ wordgraph.SetSearchConf_Verbose( settings.get().verbose_level ); wordgraph.SetSearchConf_Silent( settings.get().silent ); wordgraph.SetSearchConf_ExactMatch( settings.get().exact_match ); wordgraph.SetSearchConf_UnusedOnly( settings.get().unused_only ); wordgraph.SetSearchConf_MatchString( settings.get().word ); if ('\0' != settings.get().word[0]) { fprintf(stdout,"Searching depwords. Match string '%s'.\n", settings.get().word ); } wordgraph.SetSearchConf_DumpLength( settings.get().max_len ); if (0 < settings.get().max_len) { fprintf(stdout,"Searching depwords. Maximum length of depword is %zu.\n", settings.get().max_len / 2 ); } size_t match_num = 0; if (!settings.get().reverse) { if ('\0' == settings.get().start_node[0]) { match_num = wordgraph.DumpAllTokensForward(); } else { match_num = wordgraph.DumpTokensForward( settings.get().start_node ); } } else { if ('\0' == settings.get().start_node[0]) { match_num = wordgraph.DumpAllTokensBackward(); } else { match_num = wordgraph.DumpTokensBackward( settings.get().start_node ); } } fprintf(stdout,"Total %zu token.\n", match_num ); /* 表示2 */ if (settings.get().show_unused) { wordgraph.ShowUnusedTokens(); } } return 0; } /* [ End of File ] */