llkeywords.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:15k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llkeywords.cpp
  3.  * @brief Keyword list for LSL
  4.  *
  5.  * $LicenseInfo:firstyear=2000&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2000-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #include "linden_common.h"
  33. #include <iostream>
  34. #include <fstream>
  35. #include "llkeywords.h"
  36. #include "lltexteditor.h"
  37. #include "llstl.h"
  38. #include <boost/tokenizer.hpp>
  39. const U32 KEYWORD_FILE_CURRENT_VERSION = 2;
  40. inline BOOL LLKeywordToken::isHead(const llwchar* s) const
  41. {
  42. // strncmp is much faster than string compare
  43. BOOL res = TRUE;
  44. const llwchar* t = mToken.c_str();
  45. S32 len = mToken.size();
  46. for (S32 i=0; i<len; i++)
  47. {
  48. if (s[i] != t[i])
  49. {
  50. res = FALSE;
  51. break;
  52. }
  53. }
  54. return res;
  55. }
  56. LLKeywords::LLKeywords() : mLoaded(FALSE)
  57. {
  58. }
  59. LLKeywords::~LLKeywords()
  60. {
  61. std::for_each(mWordTokenMap.begin(), mWordTokenMap.end(), DeletePairedPointer());
  62. std::for_each(mLineTokenList.begin(), mLineTokenList.end(), DeletePointer());
  63. std::for_each(mDelimiterTokenList.begin(), mDelimiterTokenList.end(), DeletePointer());
  64. }
  65. BOOL LLKeywords::loadFromFile( const std::string& filename )
  66. {
  67. mLoaded = FALSE;
  68. ////////////////////////////////////////////////////////////
  69. // File header
  70. const S32 BUFFER_SIZE = 1024;
  71. char buffer[BUFFER_SIZE]; /* Flawfinder: ignore */
  72. llifstream file;
  73. file.open(filename); /* Flawfinder: ignore */
  74. if( file.fail() )
  75. {
  76. llinfos << "LLKeywords::loadFromFile()  Unable to open file: " << filename << llendl;
  77. return mLoaded;
  78. }
  79. // Identifying string
  80. file >> buffer;
  81. if( strcmp( buffer, "llkeywords" ) )
  82. {
  83. llinfos << filename << " does not appear to be a keyword file" << llendl;
  84. return mLoaded;
  85. }
  86. // Check file version
  87. file >> buffer;
  88. U32 version_num;
  89. file >> version_num;
  90. if( strcmp(buffer, "version") || version_num != (U32)KEYWORD_FILE_CURRENT_VERSION )
  91. {
  92. llinfos << filename << " does not appear to be a version " << KEYWORD_FILE_CURRENT_VERSION << " keyword file" << llendl;
  93. return mLoaded;
  94. }
  95. // start of line (SOL)
  96. std::string SOL_COMMENT("#");
  97. std::string SOL_WORD("[word ");
  98. std::string SOL_LINE("[line ");
  99. std::string SOL_ONE_SIDED_DELIMITER("[one_sided_delimiter ");
  100. std::string SOL_TWO_SIDED_DELIMITER("[two_sided_delimiter ");
  101. LLColor3 cur_color( 1, 0, 0 );
  102. LLKeywordToken::TOKEN_TYPE cur_type = LLKeywordToken::WORD;
  103. while (!file.eof())
  104. {
  105. buffer[0] = 0;
  106. file.getline( buffer, BUFFER_SIZE );
  107. std::string line(buffer);
  108. if( line.find(SOL_COMMENT) == 0 )
  109. {
  110. continue;
  111. }
  112. else if( line.find(SOL_WORD) == 0 )
  113. {
  114. cur_color = readColor( line.substr(SOL_WORD.size()) );
  115. cur_type = LLKeywordToken::WORD;
  116. continue;
  117. }
  118. else if( line.find(SOL_LINE) == 0 )
  119. {
  120. cur_color = readColor( line.substr(SOL_LINE.size()) );
  121. cur_type = LLKeywordToken::LINE;
  122. continue;
  123. }
  124. else if( line.find(SOL_TWO_SIDED_DELIMITER) == 0 )
  125. {
  126. cur_color = readColor( line.substr(SOL_TWO_SIDED_DELIMITER.size()) );
  127. cur_type = LLKeywordToken::TWO_SIDED_DELIMITER;
  128. continue;
  129. }
  130. else if( line.find(SOL_ONE_SIDED_DELIMITER) == 0 )
  131. {
  132. cur_color = readColor( line.substr(SOL_ONE_SIDED_DELIMITER.size()) );
  133. cur_type = LLKeywordToken::ONE_SIDED_DELIMITER;
  134. continue;
  135. }
  136. std::string token_buffer( line );
  137. LLStringUtil::trim(token_buffer);
  138. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  139. boost::char_separator<char> sep_word("", " t");
  140. tokenizer word_tokens(token_buffer, sep_word);
  141. tokenizer::iterator token_word_iter = word_tokens.begin();
  142. if( !token_buffer.empty() && token_word_iter != word_tokens.end() )
  143. {
  144. // first word is keyword
  145. std::string keyword = (*token_word_iter);
  146. LLStringUtil::trim(keyword);
  147. // following words are tooltip
  148. std::string tool_tip;
  149. while (++token_word_iter != word_tokens.end())
  150. {
  151. tool_tip += (*token_word_iter);
  152. }
  153. LLStringUtil::trim(tool_tip);
  154. if( !tool_tip.empty() )
  155. {
  156. // Replace : with n for multi-line tool tips.
  157. LLStringUtil::replaceChar( tool_tip, ':', 'n' );
  158. addToken(cur_type, keyword, cur_color, tool_tip );
  159. }
  160. else
  161. {
  162. addToken(cur_type, keyword, cur_color, LLStringUtil::null );
  163. }
  164. }
  165. }
  166. file.close();
  167. mLoaded = TRUE;
  168. return mLoaded;
  169. }
  170. // Add the token as described
  171. void LLKeywords::addToken(LLKeywordToken::TOKEN_TYPE type,
  172.   const std::string& key_in,
  173.   const LLColor3& color,
  174.   const std::string& tool_tip_in )
  175. {
  176. LLWString key = utf8str_to_wstring(key_in);
  177. LLWString tool_tip = utf8str_to_wstring(tool_tip_in);
  178. switch(type)
  179. {
  180. case LLKeywordToken::WORD:
  181. mWordTokenMap[key] = new LLKeywordToken(type, color, key, tool_tip);
  182. break;
  183. case LLKeywordToken::LINE:
  184. mLineTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip));
  185. break;
  186. case LLKeywordToken::TWO_SIDED_DELIMITER:
  187. case LLKeywordToken::ONE_SIDED_DELIMITER:
  188. mDelimiterTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip));
  189. break;
  190. default:
  191. llassert(0);
  192. }
  193. }
  194. LLKeywords::WStringMapIndex::WStringMapIndex(const WStringMapIndex& other)
  195. {
  196. if(other.mOwner)
  197. {
  198. copyData(other.mData, other.mLength);
  199. }
  200. else
  201. {
  202. mOwner = false;
  203. mLength = other.mLength;
  204. mData = other.mData;
  205. }
  206. }
  207. LLKeywords::WStringMapIndex::WStringMapIndex(const LLWString& str)
  208. {
  209. copyData(str.data(), str.size());
  210. }
  211. LLKeywords::WStringMapIndex::WStringMapIndex(const llwchar *start, size_t length):
  212. mData(start), mLength(length), mOwner(false)
  213. {
  214. }
  215. LLKeywords::WStringMapIndex::~WStringMapIndex()
  216. {
  217. if(mOwner)
  218. delete[] mData;
  219. }
  220. void LLKeywords::WStringMapIndex::copyData(const llwchar *start, size_t length)
  221. {
  222. llwchar *data = new llwchar[length];
  223. memcpy((void*)data, (const void*)start, length * sizeof(llwchar));
  224. mOwner = true;
  225. mLength = length;
  226. mData = data;
  227. }
  228. bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &other) const
  229. {
  230. // NOTE: Since this is only used to organize a std::map, it doesn't matter if it uses correct collate order or not.
  231. // The comparison only needs to strictly order all possible strings, and be stable.
  232. bool result = false;
  233. const llwchar* self_iter = mData;
  234. const llwchar* self_end = mData + mLength;
  235. const llwchar* other_iter = other.mData;
  236. const llwchar* other_end = other.mData + other.mLength;
  237. while(true)
  238. {
  239. if(other_iter >= other_end)
  240. {
  241. // We've hit the end of other.
  242. // This covers two cases: other being shorter than self, or the strings being equal.
  243. // In either case, we want to return false.
  244. result = false;
  245. break;
  246. }
  247. else if(self_iter >= self_end)
  248. {
  249. // self is shorter than other.
  250. result = true;
  251. break; 
  252. }
  253. else if(*self_iter != *other_iter)
  254. {
  255. // The current character differs.  The strings are not equal.
  256. result = *self_iter < *other_iter;
  257. break;
  258. }
  259. self_iter++;
  260. other_iter++;
  261. }
  262. return result;
  263. }
  264. LLColor3 LLKeywords::readColor( const std::string& s )
  265. {
  266. F32 r, g, b;
  267. r = g = b = 0.0f;
  268. S32 values_read = sscanf(s.c_str(), "%f, %f, %f]", &r, &g, &b );
  269. if( values_read != 3 )
  270. {
  271. llinfos << " poorly formed color in keyword file" << llendl;
  272. }
  273. return LLColor3( r, g, b );
  274. }
  275. LLFastTimer::DeclareTimer FTM_SYNTAX_COLORING("Syntax Coloring");
  276. // Walk through a string, applying the rules specified by the keyword token list and
  277. // create a list of color segments.
  278. void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLWString& wtext, const LLColor4 &defaultColor, LLTextEditor& editor)
  279. {
  280. LLFastTimer ft(FTM_SYNTAX_COLORING);
  281. seg_list->clear();
  282. if( wtext.empty() )
  283. {
  284. return;
  285. }
  286. S32 text_len = wtext.size() + 1;
  287. seg_list->push_back( new LLNormalTextSegment( defaultColor, 0, text_len, editor ) ); 
  288. const llwchar* base = wtext.c_str();
  289. const llwchar* cur = base;
  290. const llwchar* line = NULL;
  291. while( *cur )
  292. {
  293. if( *cur == 'n' || cur == base )
  294. {
  295. if( *cur == 'n' )
  296. {
  297. cur++;
  298. if( !*cur || *cur == 'n' )
  299. {
  300. continue;
  301. }
  302. }
  303. // Start of a new line
  304. line = cur;
  305. // Skip white space
  306. while( *cur && isspace(*cur) && (*cur != 'n')  )
  307. {
  308. cur++;
  309. }
  310. if( !*cur || *cur == 'n' )
  311. {
  312. continue;
  313. }
  314. // cur is now at the first non-whitespace character of a new line
  315. // Line start tokens
  316. {
  317. BOOL line_done = FALSE;
  318. for (token_list_t::iterator iter = mLineTokenList.begin();
  319.  iter != mLineTokenList.end(); ++iter)
  320. {
  321. LLKeywordToken* cur_token = *iter;
  322. if( cur_token->isHead( cur ) )
  323. {
  324. S32 seg_start = cur - base;
  325. while( *cur && *cur != 'n' )
  326. {
  327. // skip the rest of the line
  328. cur++;
  329. }
  330. S32 seg_end = cur - base;
  331. LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, seg_end, editor );
  332. text_segment->setToken( cur_token );
  333. insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
  334. line_done = TRUE; // to break out of second loop.
  335. break;
  336. }
  337. }
  338. if( line_done )
  339. {
  340. continue;
  341. }
  342. }
  343. }
  344. // Skip white space
  345. while( *cur && isspace(*cur) && (*cur != 'n')  )
  346. {
  347. cur++;
  348. }
  349. while( *cur && *cur != 'n' )
  350. {
  351. // Check against delimiters
  352. {
  353. S32 seg_start = 0;
  354. LLKeywordToken* cur_delimiter = NULL;
  355. for (token_list_t::iterator iter = mDelimiterTokenList.begin();
  356.  iter != mDelimiterTokenList.end(); ++iter)
  357. {
  358. LLKeywordToken* delimiter = *iter;
  359. if( delimiter->isHead( cur ) )
  360. {
  361. cur_delimiter = delimiter;
  362. break;
  363. }
  364. }
  365. if( cur_delimiter )
  366. {
  367. S32 between_delimiters = 0;
  368. S32 seg_end = 0;
  369. seg_start = cur - base;
  370. cur += cur_delimiter->getLength();
  371. if( cur_delimiter->getType() == LLKeywordToken::TWO_SIDED_DELIMITER )
  372. {
  373. while( *cur && !cur_delimiter->isHead(cur))
  374. {
  375. // Check for an escape sequence.
  376. if (*cur == '\')
  377. {
  378. // Count the number of backslashes.
  379. S32 num_backslashes = 0;
  380. while (*cur == '\')
  381. {
  382. num_backslashes++;
  383. between_delimiters++;
  384. cur++;
  385. }
  386. // Is the next character the end delimiter?
  387. if (cur_delimiter->isHead(cur))
  388. {
  389. // Is there was an odd number of backslashes, then this delimiter
  390. // does not end the sequence.
  391. if (num_backslashes % 2 == 1)
  392. {
  393. between_delimiters++;
  394. cur++;
  395. }
  396. else
  397. {
  398. // This is an end delimiter.
  399. break;
  400. }
  401. }
  402. }
  403. else
  404. {
  405. between_delimiters++;
  406. cur++;
  407. }
  408. }
  409. if( *cur )
  410. {
  411. cur += cur_delimiter->getLength();
  412. seg_end = seg_start + between_delimiters + 2 * cur_delimiter->getLength();
  413. }
  414. else
  415. {
  416. // eof
  417. seg_end = seg_start + between_delimiters + cur_delimiter->getLength();
  418. }
  419. }
  420. else
  421. {
  422. llassert( cur_delimiter->getType() == LLKeywordToken::ONE_SIDED_DELIMITER );
  423. // Left side is the delimiter.  Right side is eol or eof.
  424. while( *cur && ('n' != *cur) )
  425. {
  426. between_delimiters++;
  427. cur++;
  428. }
  429. seg_end = seg_start + between_delimiters + cur_delimiter->getLength();
  430. }
  431. LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor );
  432. text_segment->setToken( cur_delimiter );
  433. insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
  434. // Note: we don't increment cur, since the end of one delimited seg may be immediately
  435. // followed by the start of another one.
  436. continue;
  437. }
  438. }
  439. // check against words
  440. llwchar prev = cur > base ? *(cur-1) : 0;
  441. if( !isalnum( prev ) && (prev != '_') )
  442. {
  443. const llwchar* p = cur;
  444. while( isalnum( *p ) || (*p == '_') )
  445. {
  446. p++;
  447. }
  448. S32 seg_len = p - cur;
  449. if( seg_len > 0 )
  450. {
  451. WStringMapIndex word( cur, seg_len );
  452. word_token_map_t::iterator map_iter = mWordTokenMap.find(word);
  453. if( map_iter != mWordTokenMap.end() )
  454. {
  455. LLKeywordToken* cur_token = map_iter->second;
  456. S32 seg_start = cur - base;
  457. S32 seg_end = seg_start + seg_len;
  458. // llinfos << "Seg: [" << word.c_str() << "]" << llendl;
  459. LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, seg_end, editor );
  460. text_segment->setToken( cur_token );
  461. insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
  462. }
  463. cur += seg_len; 
  464. continue;
  465. }
  466. }
  467. if( *cur && *cur != 'n' )
  468. {
  469. cur++;
  470. }
  471. }
  472. }
  473. }
  474. void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>* seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor )
  475. {
  476. LLTextSegmentPtr last = seg_list->back();
  477. S32 new_seg_end = new_segment->getEnd();
  478. if( new_segment->getStart() == last->getStart() )
  479. {
  480. seg_list->pop_back();
  481. }
  482. else
  483. {
  484. last->setEnd( new_segment->getStart() );
  485. }
  486. seg_list->push_back( new_segment );
  487. if( new_seg_end < text_len )
  488. {
  489. seg_list->push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) );
  490. }
  491. }
  492. #ifdef _DEBUG
  493. void LLKeywords::dump()
  494. {
  495. llinfos << "LLKeywords" << llendl;
  496. llinfos << "LLKeywords::sWordTokenMap" << llendl;
  497. word_token_map_t::iterator word_token_iter = mWordTokenMap.begin();
  498. while( word_token_iter != mWordTokenMap.end() )
  499. {
  500. LLKeywordToken* word_token = word_token_iter->second;
  501. word_token->dump();
  502. ++word_token_iter;
  503. }
  504. llinfos << "LLKeywords::sLineTokenList" << llendl;
  505. for (token_list_t::iterator iter = mLineTokenList.begin();
  506.  iter != mLineTokenList.end(); ++iter)
  507. {
  508. LLKeywordToken* line_token = *iter;
  509. line_token->dump();
  510. }
  511. llinfos << "LLKeywords::sDelimiterTokenList" << llendl;
  512. for (token_list_t::iterator iter = mDelimiterTokenList.begin();
  513.  iter != mDelimiterTokenList.end(); ++iter)
  514. {
  515. LLKeywordToken* delimiter_token = *iter;
  516. delimiter_token->dump();
  517. }
  518. }
  519. void LLKeywordToken::dump()
  520. {
  521. llinfos << "[" << 
  522. mColor.mV[VX] << ", " <<
  523. mColor.mV[VY] << ", " <<
  524. mColor.mV[VZ] << "] [" <<
  525. wstring_to_utf8str(mToken) << "]" <<
  526. llendl;
  527. }
  528. #endif  // DEBUG