OSDN Git Service

Fix osdn.net #40488: C language block comments are not highlighted correctly (2)
[winmerge-jp/winmerge-jp.git] / Externals / crystaledit / editlib / parsers / php.cpp
1 ///////////////////////////////////////////////////////////////////////////
2 //  File:    php.cpp
3 //  Version: 1.1.0.4
4 //  Updated: 19-Jul-1998
5 //
6 //  Copyright:  Ferdinand Prantl, portions by Stcherbatchenko Andrei
7 //  E-mail:     prantl@ff.cuni.cz
8 //
9 //  PHP syntax highlighing definition
10 //
11 //  You are free to use or modify this code to the following restrictions:
12 //  - Acknowledge me somewhere in your about box, simple "Parts of code by.."
13 //  will be enough. If you can't (or don't want to), contact me personally.
14 //  - LEAVE THIS HEADER INTACT
15 ////////////////////////////////////////////////////////////////////////////
16
17 #include "StdAfx.h"
18 #include "crystallineparser.h"
19 #include "../SyntaxColors.h"
20 #include "../utils/string_util.h"
21
22 #ifdef _DEBUG
23 #define new DEBUG_NEW
24 #endif
25
26 static LPCTSTR s_apszPhpKeywordList[] =
27   {
28     _T ("array"),
29     _T ("as"),
30     _T ("break"),
31     _T ("case"),
32     _T ("cfunction"),
33     _T ("class"),
34     _T ("const"),
35     _T ("continue"),
36     _T ("declare"),
37     _T ("default"),
38     _T ("die"),
39     _T ("do"),
40     _T ("echo"),
41     _T ("else"),
42     _T ("elseif"),
43     _T ("empty"),
44     _T ("enddeclare"),
45     _T ("endfor"),
46     _T ("endforeach"),
47     _T ("endif"),
48     _T ("endswitch"),
49     _T ("endwhile"),
50     _T ("eval"),
51     _T ("exception"),
52     _T ("exit"),
53     _T ("extends"),
54     _T ("for"),
55     _T ("foreach"),
56     _T ("function"),
57     _T ("global"),
58     _T ("if"),
59     _T ("include"),
60     _T ("include_once"),
61     _T ("isset"),
62     _T ("list"),
63     _T ("new"),
64     _T ("old_function"),
65     _T ("php_user_filter"),
66     _T ("print"),
67     _T ("require"),
68     _T ("require_once"),
69     _T ("return"),
70     _T ("static"),
71     _T ("switch"),
72     _T ("unset"),
73     _T ("use"),
74     _T ("var"),
75     _T ("while"),
76   };
77
78 static LPCTSTR s_apszPhp1KeywordList[] =
79   {
80     _T ("AND"),
81     _T ("OR"),
82     _T ("XOR"),
83   };
84
85 static LPCTSTR s_apszPhp2KeywordList[] =
86   {
87     _T ("__CLASS__"),
88     _T ("__FILE__"),
89     _T ("__FUNCTION__"),
90     _T ("__LINE__"),
91     _T ("__METHOD__"),
92   };
93
94 static bool
95 IsPhpKeyword (LPCTSTR pszChars, int nLength)
96 {
97   return ISXKEYWORDI (s_apszPhpKeywordList, pszChars, nLength);
98 }
99
100 static bool
101 IsPhp1Keyword (LPCTSTR pszChars, int nLength)
102 {
103   return ISXKEYWORDI (s_apszPhp1KeywordList, pszChars, nLength);
104 }
105
106 static bool
107 IsPhp2Keyword (LPCTSTR pszChars, int nLength)
108 {
109   return ISXKEYWORDI (s_apszPhp2KeywordList, pszChars, nLength);
110 }
111
112 DWORD
113 CrystalLineParser::ParseLinePhp (DWORD dwCookie, const TCHAR *pszChars, int nLength, TEXTBLOCK * pBuf, int &nActualItems)
114 {
115   if (nLength == 0)
116     return dwCookie & (COOKIE_EXT_COMMENT|COOKIE_EXT_USER1);
117
118   bool bFirstChar = (dwCookie & ~(COOKIE_EXT_COMMENT|COOKIE_EXT_USER1)) == 0;
119   LPCTSTR pszCommentBegin = nullptr;
120   LPCTSTR pszCommentEnd = nullptr;
121   bool bRedefineBlock = true;
122   bool bDecIndex = false;
123   int nIdentBegin = -1;
124   int nPrevI = -1;
125   int I=0;
126   for (I = 0;; nPrevI = I, I = static_cast<int>(::CharNext(pszChars+I) - pszChars))
127     {
128       if (I == nPrevI)
129         {
130           // CharNext did not advance, so we're at the end of the string
131           // and we already handled this character, so stop
132           break;
133         }
134
135       if (bRedefineBlock)
136         {
137           int nPos = I;
138           if (bDecIndex)
139             nPos = nPrevI;
140           if (dwCookie & (COOKIE_COMMENT | COOKIE_EXT_COMMENT))
141             {
142               DEFINE_BLOCK (nPos, COLORINDEX_COMMENT);
143             }
144           else if (dwCookie & (COOKIE_CHAR | COOKIE_STRING))
145             {
146               DEFINE_BLOCK (nPos, COLORINDEX_STRING);
147             }
148           else if (dwCookie & COOKIE_PREPROCESSOR)
149             {
150               DEFINE_BLOCK (nPos, COLORINDEX_PREPROCESSOR);
151             }
152           else if (dwCookie & COOKIE_EXT_USER1)
153             {
154               DEFINE_BLOCK (nPos, COLORINDEX_NORMALTEXT);
155             }
156           else
157             {
158               if (xisalnum (pszChars[nPos]) || pszChars[nPos] == '.')
159                 {
160                   DEFINE_BLOCK (nPos, COLORINDEX_NORMALTEXT);
161                 }
162               else
163                 {
164                   DEFINE_BLOCK (nPos, COLORINDEX_OPERATOR);
165                   bRedefineBlock = true;
166                   bDecIndex = true;
167                   goto out;
168                 }
169             }
170           bRedefineBlock = false;
171           bDecIndex = false;
172         }
173 out:
174
175       // Can be bigger than length if there is binary data
176       // See bug #1474782 Crash when comparing SQL with with binary data
177       if (I >= nLength || pszChars[I] == 0)
178         break;
179
180       if (dwCookie & COOKIE_COMMENT)
181         {
182           DEFINE_BLOCK (I, COLORINDEX_COMMENT);
183           dwCookie |= COOKIE_COMMENT;
184           break;
185         }
186
187       //  String constant "...."
188       if (dwCookie & COOKIE_STRING)
189         {
190           if (pszChars[I] == '"' && (I == 0 || I == 1 && pszChars[nPrevI] != '\\' || I >= 2 && (pszChars[nPrevI] != '\\' || *::CharPrev(pszChars, pszChars + nPrevI) == '\\')))
191             {
192               dwCookie &= ~COOKIE_STRING;
193               bRedefineBlock = true;
194             }
195           continue;
196         }
197
198       //  Char constant '..'
199       if (dwCookie & COOKIE_CHAR)
200         {
201           if (pszChars[I] == '\'' && (I == 0 || I == 1 && pszChars[nPrevI] != '\\' || I >= 2 && (pszChars[nPrevI] != '\\' || *::CharPrev(pszChars, pszChars + nPrevI) == '\\')))
202             {
203               dwCookie &= ~COOKIE_CHAR;
204               bRedefineBlock = true;
205             }
206           continue;
207         }
208
209       //  Extended comment <!--....-->
210       if (dwCookie & COOKIE_EXT_COMMENT)
211         {
212           if (dwCookie & COOKIE_EXT_USER1)
213             {
214               if ((pszCommentBegin < pszChars + I) && (I > 0 && pszChars[I] == '/' && pszChars[nPrevI] == '*'))
215                 {
216                   dwCookie &= ~COOKIE_EXT_COMMENT;
217                   bRedefineBlock = true;
218                   pszCommentEnd = pszChars + I + 1;
219                 }
220             }
221           else
222             {
223               if (I > 1 && pszChars[I] == '>' && pszChars[nPrevI] == '-' && *::CharPrev(pszChars, pszChars + nPrevI) == '-')
224                 {
225                   dwCookie &= ~COOKIE_EXT_COMMENT;
226                   bRedefineBlock = true;
227                 }
228             }
229           continue;
230         }
231
232       if ((dwCookie & COOKIE_EXT_USER1) && (pszCommentEnd < pszChars + I) && (I > 0 && pszChars[I] == '/' && pszChars[nPrevI] == '/'))
233         {
234           DEFINE_BLOCK (nPrevI, COLORINDEX_COMMENT);
235           dwCookie |= COOKIE_COMMENT;
236           break;
237         }
238
239       if ((dwCookie & COOKIE_EXT_USER1) && pszChars[I] == '#')
240         {
241           DEFINE_BLOCK (I, COLORINDEX_COMMENT);
242           dwCookie |= COOKIE_COMMENT;
243           break;
244         }
245
246       //  Extended comment <?....?>
247       if (dwCookie & COOKIE_EXT_USER1)
248         {
249           if (I > 0 && pszChars[I] == '>' && (pszChars[nPrevI] == '?' || pszChars[nPrevI] == '%'))
250             {
251               dwCookie &= ~COOKIE_EXT_USER1;
252               bRedefineBlock = true;
253               continue;
254             }
255         }
256
257       //  Normal text
258       if ((dwCookie & (COOKIE_PREPROCESSOR|COOKIE_EXT_USER1)) && pszChars[I] == '"')
259         {
260           DEFINE_BLOCK (I, COLORINDEX_STRING);
261           dwCookie |= COOKIE_STRING;
262           continue;
263         }
264
265       if ((dwCookie & (COOKIE_PREPROCESSOR|COOKIE_EXT_USER1)) && pszChars[I] == '\'')
266         {
267           // if (I + 1 < nLength && pszChars[I + 1] == '\'' || I + 2 < nLength && pszChars[I + 1] != '\\' && pszChars[I + 2] == '\'' || I + 3 < nLength && pszChars[I + 1] == '\\' && pszChars[I + 3] == '\'')
268           if (!I || !xisalnum (pszChars[nPrevI]))
269             {
270               DEFINE_BLOCK (I, COLORINDEX_STRING);
271               dwCookie |= COOKIE_CHAR;
272               continue;
273             }
274         }
275
276       if (dwCookie & COOKIE_EXT_USER1)
277         {
278           if ((pszCommentEnd < pszChars + I) && (I > 0 && pszChars[I] == '*' && pszChars[nPrevI] == '/'))
279             {
280               DEFINE_BLOCK (nPrevI, COLORINDEX_COMMENT);
281               dwCookie |= COOKIE_EXT_COMMENT;
282               pszCommentBegin = pszChars + I + 1;
283               continue;
284             }
285         }
286       else
287         {
288           if (!(dwCookie & COOKIE_EXT_USER1) && I < nLength - 3 && pszChars[I] == '<' && pszChars[I + 1] == '!' && pszChars[I + 2] == '-' && pszChars[I + 3] == '-')
289             {
290               DEFINE_BLOCK (I, COLORINDEX_COMMENT);
291               I += 3;
292               dwCookie |= COOKIE_EXT_COMMENT;
293               dwCookie &= ~COOKIE_PREPROCESSOR;
294               continue;
295             }
296         }
297
298       if (bFirstChar)
299         {
300           if (!xisspace (pszChars[I]))
301             bFirstChar = false;
302         }
303
304       //  User1 start: <?
305       if (I < nLength && pszChars[I] == '<' && I < nLength - 1 && (pszChars[I + 1] == '?' || pszChars[I + 1] == '%'))
306         {
307           DEFINE_BLOCK (I, COLORINDEX_NORMALTEXT);
308           dwCookie |= COOKIE_EXT_USER1;
309           nIdentBegin = -1;
310           continue;
311         }
312
313       if (pBuf == nullptr)
314         continue;               //  We don't need to extract keywords,
315       //  for faster parsing skip the rest of loop
316
317       if (xisalnum (pszChars[I]) || pszChars[I] == '.')
318         {
319           if (nIdentBegin == -1)
320             nIdentBegin = I;
321         }
322       else
323         {
324           if (nIdentBegin >= 0)
325             {
326               if (dwCookie & COOKIE_PREPROCESSOR)
327                 {
328                   if (IsHtmlKeyword (pszChars + nIdentBegin, I - nIdentBegin) && (pszChars[nIdentBegin - 1] == _T ('<') || pszChars[nIdentBegin - 1] == _T ('/')))
329                     {
330                       DEFINE_BLOCK (nIdentBegin, COLORINDEX_KEYWORD);
331                     }
332                   else if (IsHtmlUser1Keyword (pszChars + nIdentBegin, I - nIdentBegin))
333                     {
334                       DEFINE_BLOCK (nIdentBegin, COLORINDEX_USER1);
335                     }
336                   else if (IsXNumber (pszChars + nIdentBegin, I - nIdentBegin))
337                     {
338                       DEFINE_BLOCK (nIdentBegin, COLORINDEX_NUMBER);
339                     }
340                   else
341                     {
342                       goto next;
343                     }
344                 }
345               else if (dwCookie & COOKIE_EXT_USER1)
346                 {
347                   if (dwCookie & COOKIE_USER2)
348                     {
349                       DEFINE_BLOCK (nIdentBegin, COLORINDEX_USER1);
350                     }
351                   if (IsPhpKeyword (pszChars + nIdentBegin, I - nIdentBegin))
352                     {
353                       DEFINE_BLOCK (nIdentBegin, COLORINDEX_KEYWORD);
354                     }
355                   else if (IsPhp1Keyword (pszChars + nIdentBegin, I - nIdentBegin))
356                     {
357                       DEFINE_BLOCK (nIdentBegin, COLORINDEX_OPERATOR);
358                     }
359                   else if (IsPhp2Keyword (pszChars + nIdentBegin, I - nIdentBegin))
360                     {
361                       DEFINE_BLOCK (nIdentBegin, COLORINDEX_USER2);
362                     }
363                   else if (IsXNumber (pszChars + nIdentBegin, I - nIdentBegin))
364                     {
365                       DEFINE_BLOCK (nIdentBegin, COLORINDEX_NUMBER);
366                     }
367                   else
368                     {
369                       bool bFunction = false;
370
371                       for (int j = I; j < nLength; j++)
372                         {
373                           if (!xisspace (pszChars[j]))
374                             {
375                               if (pszChars[j] == '(')
376                                 {
377                                   bFunction = true;
378                                 }
379                               break;
380                             }
381                         }
382                       if (bFunction)
383                         {
384                           DEFINE_BLOCK (nIdentBegin, COLORINDEX_FUNCNAME);
385                         }
386                       else
387                         {
388                           goto next;
389                         }
390                     }
391                 }
392               else if (dwCookie & COOKIE_USER1)
393                 {
394                   if (IsHtmlUser2Keyword (pszChars + nIdentBegin, I - nIdentBegin))
395                     {
396                       DEFINE_BLOCK (nIdentBegin, COLORINDEX_USER2);
397                     }
398                   else
399                     {
400                       goto next;
401                     }
402                 }
403               bRedefineBlock = true;
404               bDecIndex = true;
405               nIdentBegin = -1;
406 next:
407               ;
408             }
409
410           //  Preprocessor start: < or bracket
411           if (!(dwCookie & COOKIE_EXT_USER1) && I < nLength && pszChars[I] == '<' && !(I < nLength - 3 && pszChars[I + 1] == '!' && pszChars[I + 2] == '-' && pszChars[I + 3] == '-'))
412             {
413               DEFINE_BLOCK (I, COLORINDEX_OPERATOR);
414               DEFINE_BLOCK (I + 1, COLORINDEX_PREPROCESSOR);
415               dwCookie |= COOKIE_PREPROCESSOR;
416               nIdentBegin = -1;
417               continue;
418             }
419
420           //  User1 end: ?>
421           if (dwCookie & COOKIE_EXT_USER1)
422             {
423               if (I > 0 && pszChars[I] == '>' && (pszChars[nPrevI] == '?' || pszChars[nPrevI] == '%'))
424                 {
425                   dwCookie &= ~COOKIE_EXT_USER1;
426                   nIdentBegin = -1;
427                   bRedefineBlock = true;
428                   bDecIndex = true;
429                   continue;
430                 }
431             }
432
433           //  Preprocessor end: > or bracket
434           if (dwCookie & COOKIE_PREPROCESSOR)
435             {
436               if (pszChars[I] == '>')
437                 {
438                   dwCookie &= ~COOKIE_PREPROCESSOR;
439                   nIdentBegin = -1;
440                   bRedefineBlock = true;
441                   bDecIndex = true;
442                   continue;
443                 }
444             }
445
446           //  Preprocessor start: &
447           if (!(dwCookie & COOKIE_EXT_USER1) && pszChars[I] == '&')
448             {
449               dwCookie |= COOKIE_USER1;
450               nIdentBegin = -1;
451               continue;
452             }
453
454           //  Preprocessor end: ;
455           if (dwCookie & COOKIE_USER1)
456             {
457               if (pszChars[I] == ';')
458                 {
459                   dwCookie &= ~COOKIE_USER1;
460                   nIdentBegin = -1;
461                   continue;
462                 }
463             }
464
465           //  Preprocessor start: $
466           if ((dwCookie & COOKIE_EXT_USER1) && pszChars[I] == '$')
467             {
468               dwCookie |= COOKIE_USER2;
469               nIdentBegin = -1;
470               continue;
471             }
472
473           //  Preprocessor end: ...
474           if (dwCookie & COOKIE_USER2)
475             {
476               if (!xisalnum (pszChars[I]))
477                 {
478                   dwCookie &= ~COOKIE_USER2;
479                   nIdentBegin = -1;
480                   continue;
481                 }
482             }
483         }
484     }
485
486   if (nIdentBegin >= 0 && (dwCookie & COOKIE_PREPROCESSOR))
487     {
488       if (IsHtmlKeyword (pszChars + nIdentBegin, I - nIdentBegin) && (pszChars[nIdentBegin - 1] == _T ('<') || pszChars[nIdentBegin - 1] == _T ('/')))
489         {
490           DEFINE_BLOCK (nIdentBegin, COLORINDEX_KEYWORD);
491         }
492       else if (IsHtmlUser1Keyword (pszChars + nIdentBegin, I - nIdentBegin))
493         {
494           DEFINE_BLOCK (nIdentBegin, COLORINDEX_USER1);
495         }
496       else if (IsHtmlUser2Keyword (pszChars + nIdentBegin, I - nIdentBegin))
497         {
498           DEFINE_BLOCK (nIdentBegin, COLORINDEX_USER2);
499         }
500       else if (IsXNumber (pszChars + nIdentBegin, I - nIdentBegin))
501         {
502           DEFINE_BLOCK (nIdentBegin, COLORINDEX_NUMBER);
503         }
504       else
505         {
506           bool bFunction = false;
507
508           for (int j = I; j < nLength; j++)
509             {
510               if (!xisspace (pszChars[j]))
511                 {
512                   if (pszChars[j] == '(')
513                     {
514                       bFunction = true;
515                     }
516                   break;
517                 }
518             }
519           if (bFunction)
520             {
521               DEFINE_BLOCK (nIdentBegin, COLORINDEX_FUNCNAME);
522             }
523         }
524     }
525   else if (nIdentBegin >= 0 && (dwCookie & COOKIE_EXT_USER1))
526     {
527       if (IsPhpKeyword (pszChars + nIdentBegin, I - nIdentBegin))
528         {
529           DEFINE_BLOCK (nIdentBegin, COLORINDEX_KEYWORD);
530         }
531       else if (IsPhp1Keyword (pszChars + nIdentBegin, I - nIdentBegin))
532         {
533           DEFINE_BLOCK (nIdentBegin, COLORINDEX_OPERATOR);
534         }
535       else if (IsPhp2Keyword (pszChars + nIdentBegin, I - nIdentBegin))
536         {
537           DEFINE_BLOCK (nIdentBegin, COLORINDEX_USER2);
538         }
539       else if (IsXNumber (pszChars + nIdentBegin, I - nIdentBegin))
540         {
541           DEFINE_BLOCK (nIdentBegin, COLORINDEX_NUMBER);
542         }
543       else
544         {
545           bool bFunction = false;
546
547           for (int j = I; j < nLength; j++)
548             {
549               if (!xisspace (pszChars[j]))
550                 {
551                   if (pszChars[j] == '(')
552                     {
553                       bFunction = true;
554                     }
555                   break;
556                 }
557             }
558           if (bFunction)
559             {
560               DEFINE_BLOCK (nIdentBegin, COLORINDEX_FUNCNAME);
561             }
562         }
563     }
564
565   dwCookie &= (COOKIE_EXT_COMMENT | COOKIE_STRING | COOKIE_PREPROCESSOR | COOKIE_EXT_USER1);
566   return dwCookie;
567 }