SIP4 python bindings for TQt
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

456 lines
15KB

  1. /**********************************************************************
  2. ** Copyright (C) 2002 Detlev Offenbach <detlev@die-offenbachs.de>
  3. **
  4. ** This is a modified version of lupdate. The original is part of TQt-Linguist.
  5. ** The copyright of the original file can be found below.
  6. **
  7. ** This version is modified to handle python sources.
  8. **
  9. ** The file is provided AS IS with NO WARRANTY OF ANY KIND,
  10. ** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
  11. ** A PARTICULAR PURPOSE.
  12. **
  13. **********************************************************************/
  14. /**********************************************************************
  15. ** Copyright (C) 2000 Trolltech AS. All rights reserved.
  16. **
  17. ** fetchtr.cpp
  18. **
  19. ** This file is part of TQt Linguist.
  20. **
  21. ** See the file LICENSE included in the distribution for the usage
  22. ** and distribution terms.
  23. **
  24. ** The file is provided AS IS with NO WARRANTY OF ANY KIND,
  25. ** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
  26. ** A PARTICULAR PURPOSE.
  27. **
  28. **********************************************************************/
  29. #include <qfile.h>
  30. #include <qregexp.h>
  31. #include <qstring.h>
  32. #include <qtextstream.h>
  33. #include <ctype.h>
  34. #include <errno.h>
  35. #include <metatranslator.h>
  36. #include <stdio.h>
  37. #include <string.h>
  38. /*#include <qxml.h>*/
  39. static const char MagicComment[] = "TRANSLATOR ";
  40. static TQMap<TQCString, int> needs_Q_OBJECT;
  41. static TQMap<TQCString, int> lacks_Q_OBJECT;
  42. /*
  43. The first part of this source file is the python tokenizer. We skip
  44. most of python; the only tokens that interest us are defined here.
  45. */
  46. enum { Tok_Eof, Tok_class, Tok_return, Tok_tr,
  47. Tok_trUtf8, Tok_translate, Tok_Ident,
  48. Tok_Comment, Tok_Dot, Tok_String,
  49. Tok_LeftParen, Tok_RightParen,
  50. Tok_Comma};
  51. /*
  52. The tokenizer maintains the following global variables. The names
  53. should be self-explanatory.
  54. */
  55. static TQCString yyFileName;
  56. static int yyCh;
  57. static char yyIdent[128];
  58. static size_t yyIdentLen;
  59. static char yyComment[65536];
  60. static size_t yyCommentLen;
  61. static char yyString[16384];
  62. static size_t yyStringLen;
  63. static int yyParenDepth;
  64. static int yyLineNo;
  65. static int yyCurLineNo;
  66. // the file to read from (if reading from a file)
  67. static FILE *yyInFile;
  68. // the string to read from and current position in the string (otherwise)
  69. static TQString yyInStr;
  70. static int yyInPos;
  71. static int buf;
  72. static int (*getChar)();
  73. static int (*peekChar)();
  74. static int getCharFromFile()
  75. {
  76. int c;
  77. if ( buf < 0 )
  78. c = getc( yyInFile );
  79. else {
  80. c = buf;
  81. buf = -1;
  82. }
  83. if ( c == '\n' )
  84. yyCurLineNo++;
  85. return c;
  86. }
  87. static int peekCharFromFile()
  88. {
  89. int c = getc( yyInFile );
  90. buf = c;
  91. return c;
  92. }
  93. static void startTokenizer( const char *fileName, int (*getCharFunc)(),
  94. int (*peekCharFunc)() )
  95. {
  96. yyInPos = 0;
  97. buf = -1;
  98. getChar = getCharFunc;
  99. peekChar = peekCharFunc;
  100. yyFileName = fileName;
  101. yyCh = getChar();
  102. yyParenDepth = 0;
  103. yyCurLineNo = 1;
  104. }
  105. static int getToken()
  106. {
  107. const char tab[] = "abfnrtv";
  108. const char backTab[] = "\a\b\f\n\r\t\v";
  109. uint n;
  110. yyIdentLen = 0;
  111. yyCommentLen = 0;
  112. yyStringLen = 0;
  113. while ( yyCh != EOF ) {
  114. yyLineNo = yyCurLineNo;
  115. if ( isalpha(yyCh) || yyCh == '_' ) {
  116. do {
  117. if ( yyIdentLen < sizeof(yyIdent) - 1 )
  118. yyIdent[yyIdentLen++] = (char) yyCh;
  119. yyCh = getChar();
  120. } while ( isalnum(yyCh) || yyCh == '_' );
  121. yyIdent[yyIdentLen] = '\0';
  122. switch ( yyIdent[0] ) {
  123. case 'Q':
  124. if ( strcmp(yyIdent + 1, "T_TR_NOOP") == 0 ) {
  125. return Tok_tr;
  126. } else if ( strcmp(yyIdent + 1, "T_TRANSLATE_NOOP") == 0 ) {
  127. return Tok_translate;
  128. }
  129. break;
  130. case 'c':
  131. if ( strcmp(yyIdent + 1, "lass") == 0 )
  132. return Tok_class;
  133. break;
  134. case 'r':
  135. if ( strcmp(yyIdent + 1, "eturn") == 0 )
  136. return Tok_return;
  137. break;
  138. case 't':
  139. if ( strcmp(yyIdent + 1, "r") == 0 )
  140. return Tok_tr;
  141. else if ( strcmp(yyIdent + 1, "rUtf8") == 0 )
  142. return Tok_trUtf8;
  143. else if ( strcmp(yyIdent + 1, "ranslate") == 0 )
  144. return Tok_translate;
  145. case '_':
  146. if ( strcmp(yyIdent + 1, "_tr") == 0 )
  147. return Tok_tr;
  148. else if ( strcmp(yyIdent + 1, "_trUtf8") == 0 )
  149. return Tok_trUtf8;
  150. }
  151. return Tok_Ident;
  152. } else {
  153. switch ( yyCh ) {
  154. case '#':
  155. yyCh = getChar();
  156. do {
  157. yyCh = getChar();
  158. } while ( yyCh != EOF && yyCh != '\n' );
  159. break;
  160. case '"':
  161. case '\'':
  162. int quoteChar;
  163. int trippelQuote, singleQuote;
  164. int in;
  165. quoteChar = yyCh;
  166. trippelQuote = 0;
  167. singleQuote = 1;
  168. in = 0;
  169. yyCh = getChar();
  170. while ( yyCh != EOF ) {
  171. if ( singleQuote && (yyCh == '\n' || (in && yyCh == quoteChar)) )
  172. break;
  173. if ( yyCh == quoteChar ) {
  174. if (peekChar() == quoteChar) {
  175. yyCh = getChar();
  176. if (!trippelQuote) {
  177. trippelQuote = 1;
  178. singleQuote = 0;
  179. in = 1;
  180. yyCh = getChar();
  181. } else {
  182. yyCh = getChar();
  183. if (yyCh == quoteChar) {
  184. trippelQuote = 0;
  185. break;
  186. }
  187. }
  188. } else if (trippelQuote) {
  189. if ( yyStringLen < sizeof(yyString) - 1 )
  190. yyString[yyStringLen++] = (char) yyCh;
  191. yyCh = getChar();
  192. continue;
  193. } else
  194. break;
  195. } else
  196. in = 1;
  197. if ( yyCh == '\\' ) {
  198. yyCh = getChar();
  199. if ( yyCh == 'x' ) {
  200. TQCString hex = "0";
  201. yyCh = getChar();
  202. while ( isxdigit(yyCh) ) {
  203. hex += (char) yyCh;
  204. yyCh = getChar();
  205. }
  206. sscanf( hex, "%x", &n );
  207. if ( yyStringLen < sizeof(yyString) - 1 )
  208. yyString[yyStringLen++] = (char) n;
  209. } else if ( yyCh >= '0' && yyCh < '8' ) {
  210. TQCString oct = "";
  211. do {
  212. oct += (char) yyCh;
  213. yyCh = getChar();
  214. } while ( yyCh >= '0' && yyCh < '8' );
  215. sscanf( oct, "%o", &n );
  216. if ( yyStringLen < sizeof(yyString) - 1 )
  217. yyString[yyStringLen++] = (char) n;
  218. } else {
  219. const char *p = strchr( tab, yyCh );
  220. if ( yyStringLen < sizeof(yyString) - 1 )
  221. yyString[yyStringLen++] = ( p == 0 ) ?
  222. (char) yyCh : backTab[p - tab];
  223. yyCh = getChar();
  224. }
  225. } else {
  226. if ( yyStringLen < sizeof(yyString) - 1 )
  227. yyString[yyStringLen++] = (char) yyCh;
  228. yyCh = getChar();
  229. }
  230. }
  231. yyString[yyStringLen] = '\0';
  232. if ( yyCh != quoteChar ) {
  233. printf("%c\n", yyCh);
  234. qWarning( "%s:%d: Unterminated string",
  235. (const char *) yyFileName, yyLineNo );
  236. }
  237. if ( yyCh == EOF ) {
  238. return Tok_Eof;
  239. } else {
  240. yyCh = getChar();
  241. return Tok_String;
  242. }
  243. break;
  244. case '(':
  245. yyParenDepth++;
  246. yyCh = getChar();
  247. return Tok_LeftParen;
  248. case ')':
  249. yyParenDepth--;
  250. yyCh = getChar();
  251. return Tok_RightParen;
  252. case ',':
  253. yyCh = getChar();
  254. return Tok_Comma;
  255. case '.':
  256. yyCh = getChar();
  257. return Tok_Dot;
  258. default:
  259. yyCh = getChar();
  260. }
  261. }
  262. }
  263. return Tok_Eof;
  264. }
  265. /*
  266. The second part of this source file is the parser. It accomplishes
  267. a very easy task: It finds all strings inside a tr() or translate()
  268. call, and possibly finds out the context of the call. It supports
  269. three cases:
  270. (1) the context is specified, as in FunnyDialog.tr("Hello") or
  271. translate("FunnyDialog", "Hello");
  272. (2) the call appears within an inlined function;
  273. (3) the call appears within a function defined outside the class definition.
  274. */
  275. static int yyTok;
  276. static bool match( int t )
  277. {
  278. bool matches = ( yyTok == t );
  279. if ( matches )
  280. yyTok = getToken();
  281. return matches;
  282. }
  283. static bool matchString( TQCString *s )
  284. {
  285. bool matches = ( yyTok == Tok_String );
  286. *s = "";
  287. while ( yyTok == Tok_String ) {
  288. *s += yyString;
  289. yyTok = getToken();
  290. }
  291. return matches;
  292. }
  293. static bool matchEncoding( bool *utf8 )
  294. {
  295. if ( yyTok == Tok_Ident ) {
  296. if ( strcmp(yyIdent, "TQApplication") == 0 ) {
  297. yyTok = getToken();
  298. }
  299. *utf8 = TQString( yyIdent ).endsWith( TQString("UTF8") );
  300. yyTok = getToken();
  301. return TRUE;
  302. } else {
  303. return FALSE;
  304. }
  305. }
  306. static void parse( MetaTranslator *tor, const char *initialContext,
  307. const char *defaultContext )
  308. {
  309. TQMap<TQCString, TQCString> qualifiedContexts;
  310. TQCString context;
  311. TQCString text;
  312. TQCString com;
  313. TQCString functionContext = initialContext;
  314. TQCString prefix;
  315. bool utf8 = FALSE;
  316. yyTok = getToken();
  317. while ( yyTok != Tok_Eof ) {
  318. switch ( yyTok ) {
  319. case Tok_class:
  320. yyTok = getToken();
  321. functionContext = yyIdent;
  322. yyTok = getToken();
  323. break;
  324. case Tok_tr:
  325. case Tok_trUtf8:
  326. utf8 = ( yyTok == Tok_trUtf8 );
  327. yyTok = getToken();
  328. if ( match(Tok_LeftParen) && matchString(&text) ) {
  329. com = "";
  330. if ( match(Tok_RightParen) || (match(Tok_Comma) &&
  331. matchString(&com) && match(Tok_RightParen)) ) {
  332. if ( prefix.isNull() ) {
  333. context = defaultContext;
  334. } else if ( qstrcmp(prefix, "self") == 0 ) {
  335. context = functionContext;
  336. } else {
  337. context = prefix;
  338. }
  339. prefix = (const char *) 0;
  340. if ( qualifiedContexts.contains(context) )
  341. context = qualifiedContexts[context];
  342. tor->insert( MetaTranslatorMessage(context, text, com,
  343. TQString::null, utf8) );
  344. }
  345. }
  346. break;
  347. case Tok_translate:
  348. utf8 = FALSE;
  349. yyTok = getToken();
  350. if ( match(Tok_LeftParen) &&
  351. matchString(&context) &&
  352. match(Tok_Comma) &&
  353. matchString(&text) ) {
  354. com = "";
  355. if ( match(Tok_RightParen) ||
  356. (match(Tok_Comma) &&
  357. matchString(&com) &&
  358. (match(Tok_RightParen) ||
  359. match(Tok_Comma) &&
  360. matchEncoding(&utf8) &&
  361. match(Tok_RightParen))) )
  362. tor->insert( MetaTranslatorMessage(context, text, com,
  363. TQString::null, utf8) );
  364. }
  365. break;
  366. case Tok_Ident:
  367. if ( !prefix.isNull() )
  368. prefix += ".";
  369. prefix += yyIdent;
  370. yyTok = getToken();
  371. if ( yyTok != Tok_Dot )
  372. prefix = (const char *) 0;
  373. break;
  374. case Tok_Comment:
  375. com = yyComment;
  376. com = com.simplifyWhiteSpace();
  377. if ( com.left(sizeof(MagicComment) - 1) == MagicComment ) {
  378. com.remove( 0, sizeof(MagicComment) - 1 );
  379. int k = com.find( ' ' );
  380. if ( k == -1 ) {
  381. context = com;
  382. } else {
  383. context = com.left( k );
  384. com.remove( 0, k + 1 );
  385. tor->insert( MetaTranslatorMessage(context, "", com,
  386. TQString::null, FALSE) );
  387. }
  388. }
  389. yyTok = getToken();
  390. break;
  391. default:
  392. yyTok = getToken();
  393. }
  394. }
  395. if ( yyParenDepth != 0 )
  396. qWarning( "%s: Unbalanced parentheses in Python code",
  397. (const char *) yyFileName );
  398. }
  399. void fetchtr_py( const char *fileName, MetaTranslator *tor,
  400. const char *defaultContext, bool mustExist )
  401. {
  402. yyInFile = fopen( fileName, "r" );
  403. if ( yyInFile == 0 ) {
  404. if ( mustExist )
  405. qWarning( "pylupdate error: cannot open Python source file '%s': %s",
  406. fileName, strerror(errno) );
  407. return;
  408. }
  409. startTokenizer( fileName, getCharFromFile, peekCharFromFile );
  410. parse( tor, 0, defaultContext );
  411. fclose( yyInFile );
  412. }