string Satori::UnKakko(const char* p,bool for_calc,bool for_non_talk) { assert(p!=NULL); string result; while ( p[0] != '\0' ) { string c=get_a_chr(p); result += (c=="(") ? KakkoSection(p,for_calc,for_non_talk) : c; } return result; }
bool Satori::Translate(string& ioScript) { if ( ioScript.empty() ) return false; const bool is_OnTranslate = (mRequestID=="OnTranslate"); // さくらスクリプトとそれ以外を分割して処理を加える vector<string> vec; string acum; bool content=false; // 文に中身があるのか bool is_first_question = true; // 選択分岐記録の消去処理用。 int last_speaker=0; const char* p = ioScript.c_str(); while (*p) { string c=get_a_chr(p); // 全角半角問わず一文字取得し、pを一文字すすめる if ( c=="\\" || c=="%" ) { if (*p=='\\'||*p=='%') { // エスケープされた\, % acum += c + *p++; continue; } const char* start=p; string cmd="",opt=""; while (!_ismbblead(*p) && (isalpha(*p)||isdigit(*p)||*p=='!'||*p=='*'||*p=='&'||*p=='?'||*p=='_')) ++p; cmd.assign(start, p-start); if (*p=='[') { const char* opt_start = ++p; while (*p!=']') { if (p[0]=='\\' && p[1]==']') // エスケープされた] ++p; p += _ismbblead(*p) ? 2 : 1; } opt.assign(opt_start, p++ -opt_start); } // 選択分岐ラベルに対する特殊処理 if ( !is_OnTranslate && cmd=="q" && opt!="" && count(opt, ",")>0 && mRequestID!="OnHeadlinesense.OnFind") { // 選択分岐があるスクリプトであれば、その初回で選択分岐記録をクリア if ( is_first_question ) { question_record.clear(); is_first_question = false; } // 選択分岐を記録 { strvec vec; split(opt, ",", vec); if ( vec.size()==1 ) // countとsplitの罠。 vec.push_back(""); // string label=vec[0], id=vec[1]; if ( false == compare_head(id, "On") ) { // Onで始まるものはOnChoiceSelectを経由されないため、対象外とする int count = question_record.size()+1; question_record[id] = pair<int,string>(count, label); // ラベルIDに書き戻し opt = label+","+id+byte1_dlmt+label+byte1_dlmt+itos(count); for (int i=2;i<vec.size();++i) opt += string(",") + vec[i]; } } } else if ( cmd=="0" || cmd=="h" ) { last_speaker=0; } else if ( cmd=="1" || cmd=="u" ) { last_speaker=1; } else if ( cmd=="p" && aredigits(opt) ) { last_speaker=stoi(opt); if ( mIsMateria || last_speaker<=1 ) { cmd = (opt=="0") ? "0" : "1"; opt = ""; } } else if ( cmd=="s" && !opt.empty() ) { last_talk_exiting_surface[last_speaker]=stoi(opt); } else if ( cmd.size()==2 && cmd[0]=='s' && isdigit(cmd[1]) ) { last_talk_exiting_surface[last_speaker]=cmd[1]-'0'; } if ( !acum.empty() ) vec.push_back(acum); if ( opt=="" ) vec.push_back(c + cmd); else vec.push_back(c + cmd + "[" + opt + "]"); acum=""; static set<string> nc_cmd; // 有効と数えないさくらスクリプト群 static bool initialized=false; if (!initialized) { initialized=true; nc_cmd.insert("0"); nc_cmd.insert("1"); nc_cmd.insert("h"); nc_cmd.insert("u"); nc_cmd.insert("p"); nc_cmd.insert("n"); nc_cmd.insert("w"); nc_cmd.insert("_w"); nc_cmd.insert("e"); } if ( nc_cmd.find(cmd)==nc_cmd.end() ) content = true; } else { content = true; acum += c; } } if ( !acum.empty() ) vec.push_back(acum); if (!content) return false; // 中身の無いスクリプト(実行してもしなくても一緒)と判断。 ioScript=""; for (vector<string>::iterator i=vec.begin() ; i!=vec.end() ; ++i) { if ( i->at(0)!='\\' && i->at(0)!='%' ) { // さくらスクリプト以外の文への処理 // アンカー挿入 if ( !is_OnTranslate ) for ( set<string>::iterator j=anchors.begin() ; j!=anchors.end() ; ++j ) replace(*i, *j, string("\\_a[")+*j+"]"+*j+"\\_a"); } ioScript += *i; } // 事後置き換え辞書を適用 if ( !is_OnTranslate ) for ( strmap::iterator di=replace_after_dic.begin() ; di!=replace_after_dic.end() ; ++di ) replace(ioScript, di->first, di->second); diet_script(ioScript); // ラストダイエット // エスケープしてあった文字を戻す m_escaper.unescape(ioScript); return true; }
// 文章の中で ( を見つけた場合、pが ( の次の位置まで進められた上でこれが実行される。 // pはこの内部で ) の次の位置まで進められる。 // 返値はカッコの解釈結果。 string Satori::KakkoSection(const char*& p,bool for_calc,bool for_non_talk) { string thePluginName = ""; string theDelimiter = ""; bool specialFlag = false; const char *pp=0; strvec theArguments; string kakko_str; if ( for_calc ) { for_non_talk = true; } for ( set<string>::iterator it = special_commands.begin(); it != special_commands.end(); ++it) { if ( strncmp(it->c_str(), p, it->size()) == 0 ) { pp = p + it->size(); string c = get_a_chr(pp); //引数がない場合はスペシャルフォームにする必要はない。 if ( mDelimiters.find(c) != mDelimiters.end() ){ specialFlag = true; theDelimiter = c; thePluginName = it->c_str(); break; } } } if( specialFlag ) { assert(pp); int level = 0; const char *p_start = pp; while( true ){ if ( *pp == '\0' ){ return string("("); // 閉じカッコが無かった } string c = get_a_chr(pp); if ( c == "(" ) { level++; } if ( c == ")" ) { level--; } if ( level < 0 ) { theArguments.push_back( string(p_start, pp-p_start-2) ); break; } if ( level == 0 ) { if ( c == theDelimiter ) { theArguments.push_back( string(p_start, pp-p_start-c.size()) ); p_start = (char *)pp; } } } p = pp; return special_call(thePluginName, theArguments, for_calc, for_non_talk, secure_flag); } else { while (true) { if ( p[0] == '\0' ) return string("(") + kakko_str; // 閉じカッコが無かった string c = get_a_chr(p); if ( c==")" ) break; else if ( c=="(" ) { kakko_str += KakkoSection(p,false,for_non_talk); //内側の括弧は0に置き換えしない } else kakko_str += c; } string result; if ( Call(kakko_str, result, for_calc, for_non_talk) ) return result; if ( for_calc ) return string("0"); else return string("(") + kakko_str + ")"; } }
int Satori::SentenceToSakuraScriptInternal(const strvec &vec,string &result,string &jump_to,ptrdiff_t &ip) { // 再帰管理 static int nest_count=0; ++nest_count; //DBG(sender << "enter SentenceToSakuraScriptInternal, nest-count: " << nest_count << ", vector_size: " << vec.size() << endl); if ( m_nest_limit > 0 && nest_count > m_nest_limit ) { sender << "呼び出し回数超過" << endl; --nest_count; return 0; } static const int basewait=3; strvec::const_iterator it = vec.begin(); std::advance(it,ip); for ( ; it != vec.end() ; ++it) { const char* p = it->c_str(); //DBG(sender << nest_count << " '" << p << "'" << endl); if ( it==vec.begin() && strncmp(p, "→", 2)==0 ) { p+=2; updateGhostsInfo(); // ゴースト情報を更新 if ( ghosts_info.size()>=2 ) { // そもそも自分以外にゴーストはいるのか。 string temp = p; vector<strmap>::iterator i=ghosts_info.begin(); ++i; // 自分は飛ばす for ( ; i!=ghosts_info.end() ; ++i ) { string name = (*i)["name"]; sender << "ghost: " << name <<endl; if ( compare_head(temp, name) ) {// 相手を特定 mCommunicateFor = name; p += mCommunicateFor.size(); break; } } if ( i==ghosts_info.end() ) { // 特定しなかった場合 // ランダム //int n = random(ghosts_info.size()-1))+1; //assert( n>=1 && n < ghosts_info.size()); mCommunicateFor = (ghosts_info[1])["name"]; // あかん、隣で起動している〜〜にならん } } } // 選択肢 \q?[id,string] if ( strncmp(p, "_", 2)==0 ) { if ( strlen(p)>1023 ) continue; char buf[1024]; strncpy(buf, p+2, sizeof(buf) / sizeof(buf[0])); char* choiced = buf; char* id = (char*)strstr_hz(buf, "\t"); // 選択肢ラベルとジャンプ先の区切り result += append_at_choice_start; if ( id == NULL ) { string str=UnKakko(choiced); //result += string("\\q")+itos(question_num++)+"["+str+"]["+str+"]"; result += "\\q["+str+","+str+"]"; } else { *id++='\0'; while ( *id=='\t' ) ++id; // 選択肢ラベルとジャンプ先の区切り //result += string("\\q")+itos(question_num++)+"["+UnKakko(id)+"]["+UnKakko(choiced)+"]"; result += "\\q["+UnKakko(choiced)+","+UnKakko(id)+"]"; } result += append_at_choice_end; continue; } // ちょっと微妙な存在意義。 if ( strncmp(p, "\\s", 2)==0 ) { if ( !is_speaked(speaker) ) { if ( surface_changed_before_speak.find(speaker) == surface_changed_before_speak.end() ) { surface_changed_before_speak.insert(map<int,bool>::value_type(speaker,is_speaked_anybody()) ); } } } // ジャンプ if ( strncmp(p, ">", 2)==0 || strncmp(p, "≫", 2)==0 ) { strvec words; split(p+2, "\t", words, 2); // ジャンプ先とジャンプ条件の区切り if ( words.size()>=2 ) { string r; if ( !calculate(words[1], r) ) break; if ( zen2int(r) == 0 ) { sender << "*計算結果が0だったため、続行します。" << endl; continue; } } if ( words.size() >= 1 ) { jump_to = UnKakko(words[0].c_str(),false,true); } else { jump_to.erase(); } if ( strncmp(p, "≫", 2)==0 ) { ip = std::distance(vec.begin(),it) + 1; --nest_count; return 2; } else { ip = std::distance(vec.begin(),it) + 1; --nest_count; return 1; } } // 変数を設定 if ( strncmp(p, "$", 2)==0 ) { const char* v; string value; bool do_calc=false; p+=2; if ( (v=strstr_hz(p, "\t"))!=NULL ) { // 変数名と変数に設定する内容の区切り value = UnKakko(v+1,false,true); } else if ( (v=strstr_hz(p, "="))!=NULL || (v=strstr_hz(p, "="))!=NULL ) { value = UnKakko(v+((*v=='=') ? 1 : 2),false,true); do_calc=true; } else { //v = p+strlen(p); //value=""; result += "\\n※ $による変数代入文には、タブによる区切りか、=による計算式が必要です ※\\n\\n[half]'" + value +"'"; break; } string key(p, v-p); key = UnKakko(key.c_str(),false,true); if ( key=="" ) { result += "$"; // $そのまま表示 speaked_speaker.insert(speaker); } else if ( aredigits(zen2han(key)) ) { sender << "$" << key << " 数字のみの変数名は扱えません." << endl; erase_var(key); // 存在抹消 } else if ( value=="" ) { sender << "$" << key << "/cleared." << endl; erase_var(key); // 存在抹消 system_variable_operation(key, "", &result);//存在抹消したものがシステム変数かも! } else { bool isOverwritten; bool isSysValue; // "0"は代入先を先に参照する時、エラーを返さないように。 string *pstr = GetValue(key,isSysValue,true,&isOverwritten,"0"); if ( do_calc ) { if ( !calculate(value, value) ) break; if ( aredigits(value) ) { value = int2zen(stoi(value)); } } sender << "$" << key << "=" << value << "/" << (isOverwritten ? "written." : "overwritten.")<< endl; if ( pstr ) { *pstr = value; } system_variable_operation(key, value, &result); } continue; } // 通常処理 while ( p[0] != '\0' ) { string c=get_a_chr(p); // 全角半角問わず一文字取得し、pを一文字すすめる if ( c=="(" ) { // 何かを取得・挿入 character_wait_exec; result += KakkoSection(p); } else if ( c=="\xff" ) { //内部特殊表現 c=get_a_chr(p); if ( c=="\x01" ) { //0xff0x01=スコープ切り替え 後に半角数値が1文字続く c=get_a_chr(p); int speaker_tmp = stoi(c.c_str()); if ( is_speaked(speaker) && speaker != speaker_tmp ) { result += append_at_scope_change; chars_spoken += 2; } speaker = speaker_tmp; character_wait_clear(2); if ( speaker == 0 ) { result += "\\0"; } else if ( speaker == 1 ) { result += "\\1"; } else { result += "\\p[" + c + "]"; } } } else if ( c==":" ) { // スコープ切り替え - ここは二人を想定。 if ( is_speaked(speaker) ) { result += append_at_scope_change; chars_spoken += 2; } speaker = (speaker==0) ? 1 : 0; character_wait_clear(2); result += (speaker ? "\\1" : "\\0"); } else if ( c=="\\" || c=="%" ) { // さくらスクリプトの解釈、というか解釈のスキップ。 if (*p=='\\'||*p=='%') { // エスケープされた\, % result += c + *p++; continue; } const char* start=p; string cmd="",opt=""; while (!_ismbblead(*p) && (isalpha(*p)||isdigit(*p)||*p=='!'||*p=='-'||*p=='*'||*p=='&'||*p=='?'||*p=='_')) ++p; cmd.assign(start, p-start); if ( cmd == "_?" || cmd == "_!" ) { //エスケープ処理 この間に自動タグ挿入はしない const char* e1 = strstr(p,"\\_?"); if ( ! e1 ) { e1 = strstr(p,"\\_!"); } if ( e1 ) { character_wait_exec; result += c; result += cmd; opt.assign(p,e1-p+3); opt = UnKakko(opt.c_str()); p = e1 + 3; //endtag result += opt; continue; } } if (*p=='[') { const char* opt_start = ++p; while (*p!=']') { if (p[0]=='\\' && p[1]==']') // エスケープされた] ++p; p += _ismbblead(*p) ? 2 : 1; } opt.assign(opt_start, p++ -opt_start); opt=UnKakko(opt.c_str()); } if ( cmd=="n" ) { // 改行 character_wait_clear(2); } else if ( ( (cmd=="0" || cmd=="h") && speaker==1) || ( (cmd=="1" || cmd=="u") && speaker==0) ) { // スコープ切り替え if ( is_speaked(speaker) ) { result += append_at_scope_change_with_sakura_script; chars_spoken += 2; } speaker = stoi(cmd); character_wait_clear(2); } else if ( cmd=="p" && aredigits(opt) ) { int spktmp = stoi(opt); if ( speaker != spktmp ) { // スコープ切り替えonSSP/CROW if ( is_speaked(speaker) ) { result += append_at_scope_change_with_sakura_script; chars_spoken += 2; } speaker = spktmp; character_wait_clear(2); } } else if ( cmd=="s" ) { //サーフィス切り替えの前にウェイトは済ませておくこと character_wait_exec; } else if ( cmd=="_q" ) { if ( ! is_quick_section ) { //これからクイックセクションなのでウエイトを全部消化 character_wait_exec; } is_quick_section = ! is_quick_section; } if ( opt!="" ) { //sender << "ss_cmd: " << c << "," << cmd << "," << opt << endl; result += c + cmd + "[" + opt + "]"; } else { //sender << "ss_cmd: " << c << "," << cmd << endl; result += c + cmd; } //result += string(start, p-start); } else { // 通常の一文字 character_wait_exec; speaked_speaker.insert(speaker); result += c; chars_spoken += c.size(); if ( c=="。" || c=="、" ) { character_wait_clear(c=="、"?1:2); } } } character_wait_clear(2); result += "\\n"; } //DBG(sender << "leave SentenceToSakuraScriptInternal, nest-count: " << nest_count << endl); --nest_count; return 0; }