1 // Scintilla source code edit control
2 /** @file LexPS.cxx
3  ** Lexer for PostScript
4  **
5  ** Written by Nigel Hathaway <nigel@bprj.co.uk>.
6  ** The License.txt file describes the conditions under which this software may be distributed.
7  **/
8 
9 // Previous releases of this lexer included support for marking token starts with
10 // a style byte indicator. This was used by the wxGhostscript IDE/debugger.
11 // Style byte indicators were removed in version 3.4.3.
12 // Anyone wanting to restore this functionality for wxGhostscript using 'modern'
13 // indicators can examine the earlier source in the Mercurial repository.
14 
15 #include <stdlib.h>
16 #include <string.h>
17 #include <stdio.h>
18 #include <stdarg.h>
19 #include <assert.h>
20 #include <ctype.h>
21 
22 #include "ILexer.h"
23 #include "Scintilla.h"
24 #include "SciLexer.h"
25 
26 #include "WordList.h"
27 #include "LexAccessor.h"
28 #include "Accessor.h"
29 #include "StyleContext.h"
30 #include "CharacterSet.h"
31 #include "LexerModule.h"
32 
33 using namespace Scintilla;
34 
IsASelfDelimitingChar(const int ch)35 static inline bool IsASelfDelimitingChar(const int ch) {
36     return (ch == '[' || ch == ']' || ch == '{' || ch == '}' ||
37             ch == '/' || ch == '<' || ch == '>' ||
38             ch == '(' || ch == ')' || ch == '%');
39 }
40 
IsAWhitespaceChar(const int ch)41 static inline bool IsAWhitespaceChar(const int ch) {
42     return (ch == ' '  || ch == '\t' || ch == '\r' ||
43             ch == '\n' || ch == '\f' || ch == '\0');
44 }
45 
IsABaseNDigit(const int ch,const int base)46 static bool IsABaseNDigit(const int ch, const int base) {
47     int maxdig = '9';
48     int letterext = -1;
49 
50     if (base <= 10)
51         maxdig = '0' + base - 1;
52     else
53         letterext = base - 11;
54 
55     return ((ch >= '0' && ch <= maxdig) ||
56             (ch >= 'A' && ch <= ('A' + letterext)) ||
57             (ch >= 'a' && ch <= ('a' + letterext)));
58 }
59 
IsABase85Char(const int ch)60 static inline bool IsABase85Char(const int ch) {
61     return ((ch >= '!' && ch <= 'u') || ch == 'z');
62 }
63 
ColourisePSDoc(Sci_PositionU startPos,Sci_Position length,int initStyle,WordList * keywordlists[],Accessor & styler)64 static void ColourisePSDoc(
65     Sci_PositionU startPos,
66     Sci_Position length,
67     int initStyle,
68     WordList *keywordlists[],
69     Accessor &styler) {
70 
71     WordList &keywords1 = *keywordlists[0];
72     WordList &keywords2 = *keywordlists[1];
73     WordList &keywords3 = *keywordlists[2];
74     WordList &keywords4 = *keywordlists[3];
75     WordList &keywords5 = *keywordlists[4];
76 
77     StyleContext sc(startPos, length, initStyle, styler);
78 
79     int pslevel = styler.GetPropertyInt("ps.level", 3);
80     Sci_Position lineCurrent = styler.GetLine(startPos);
81     int nestTextCurrent = 0;
82     if (lineCurrent > 0 && initStyle == SCE_PS_TEXT)
83         nestTextCurrent = styler.GetLineState(lineCurrent - 1);
84     int numRadix = 0;
85     bool numHasPoint = false;
86     bool numHasExponent = false;
87     bool numHasSign = false;
88 
89     for (; sc.More(); sc.Forward()) {
90         if (sc.atLineStart)
91             lineCurrent = styler.GetLine(sc.currentPos);
92 
93         // Determine if the current state should terminate.
94         if (sc.state == SCE_PS_COMMENT || sc.state == SCE_PS_DSC_VALUE) {
95             if (sc.atLineEnd) {
96                 sc.SetState(SCE_C_DEFAULT);
97             }
98         } else if (sc.state == SCE_PS_DSC_COMMENT) {
99             if (sc.ch == ':') {
100                 sc.Forward();
101                 if (!sc.atLineEnd)
102                     sc.SetState(SCE_PS_DSC_VALUE);
103                 else
104                     sc.SetState(SCE_C_DEFAULT);
105             } else if (sc.atLineEnd) {
106                 sc.SetState(SCE_C_DEFAULT);
107             } else if (IsAWhitespaceChar(sc.ch) && sc.ch != '\r') {
108                 sc.ChangeState(SCE_PS_COMMENT);
109             }
110         } else if (sc.state == SCE_PS_NUMBER) {
111             if (IsASelfDelimitingChar(sc.ch) || IsAWhitespaceChar(sc.ch)) {
112                 if ((sc.chPrev == '+' || sc.chPrev == '-' ||
113                      sc.chPrev == 'E' || sc.chPrev == 'e') && numRadix == 0)
114                     sc.ChangeState(SCE_PS_NAME);
115                 sc.SetState(SCE_C_DEFAULT);
116             } else if (sc.ch == '#') {
117                 if (numHasPoint || numHasExponent || numHasSign || numRadix != 0) {
118                     sc.ChangeState(SCE_PS_NAME);
119                 } else {
120                     char szradix[5];
121                     sc.GetCurrent(szradix, 4);
122                     numRadix = atoi(szradix);
123                     if (numRadix < 2 || numRadix > 36)
124                         sc.ChangeState(SCE_PS_NAME);
125                 }
126             } else if ((sc.ch == 'E' || sc.ch == 'e') && numRadix == 0) {
127                 if (numHasExponent) {
128                     sc.ChangeState(SCE_PS_NAME);
129                 } else {
130                     numHasExponent = true;
131                     if (sc.chNext == '+' || sc.chNext == '-')
132                         sc.Forward();
133                 }
134             } else if (sc.ch == '.') {
135                 if (numHasPoint || numHasExponent || numRadix != 0) {
136                     sc.ChangeState(SCE_PS_NAME);
137                 } else {
138                     numHasPoint = true;
139                 }
140             } else if (numRadix == 0) {
141                 if (!IsABaseNDigit(sc.ch, 10))
142                     sc.ChangeState(SCE_PS_NAME);
143             } else {
144                 if (!IsABaseNDigit(sc.ch, numRadix))
145                     sc.ChangeState(SCE_PS_NAME);
146             }
147         } else if (sc.state == SCE_PS_NAME || sc.state == SCE_PS_KEYWORD) {
148             if (IsASelfDelimitingChar(sc.ch) || IsAWhitespaceChar(sc.ch)) {
149                 char s[100];
150                 sc.GetCurrent(s, sizeof(s));
151                 if ((pslevel >= 1 && keywords1.InList(s)) ||
152                     (pslevel >= 2 && keywords2.InList(s)) ||
153                     (pslevel >= 3 && keywords3.InList(s)) ||
154                     keywords4.InList(s) || keywords5.InList(s)) {
155                     sc.ChangeState(SCE_PS_KEYWORD);
156                 }
157                 sc.SetState(SCE_C_DEFAULT);
158             }
159         } else if (sc.state == SCE_PS_LITERAL || sc.state == SCE_PS_IMMEVAL) {
160             if (IsASelfDelimitingChar(sc.ch) || IsAWhitespaceChar(sc.ch))
161                 sc.SetState(SCE_C_DEFAULT);
162         } else if (sc.state == SCE_PS_PAREN_ARRAY || sc.state == SCE_PS_PAREN_DICT ||
163                    sc.state == SCE_PS_PAREN_PROC) {
164             sc.SetState(SCE_C_DEFAULT);
165         } else if (sc.state == SCE_PS_TEXT) {
166             if (sc.ch == '(') {
167                 nestTextCurrent++;
168             } else if (sc.ch == ')') {
169                 if (--nestTextCurrent == 0)
170                    sc.ForwardSetState(SCE_PS_DEFAULT);
171             } else if (sc.ch == '\\') {
172                 sc.Forward();
173             }
174         } else if (sc.state == SCE_PS_HEXSTRING) {
175             if (sc.ch == '>') {
176                 sc.ForwardSetState(SCE_PS_DEFAULT);
177             } else if (!IsABaseNDigit(sc.ch, 16) && !IsAWhitespaceChar(sc.ch)) {
178                 sc.SetState(SCE_PS_HEXSTRING);
179                 styler.ColourTo(sc.currentPos, SCE_PS_BADSTRINGCHAR);
180             }
181         } else if (sc.state == SCE_PS_BASE85STRING) {
182             if (sc.Match('~', '>')) {
183                 sc.Forward();
184                 sc.ForwardSetState(SCE_PS_DEFAULT);
185             } else if (!IsABase85Char(sc.ch) && !IsAWhitespaceChar(sc.ch)) {
186                 sc.SetState(SCE_PS_BASE85STRING);
187                 styler.ColourTo(sc.currentPos, SCE_PS_BADSTRINGCHAR);
188             }
189         }
190 
191         // Determine if a new state should be entered.
192         if (sc.state == SCE_C_DEFAULT) {
193 
194             if (sc.ch == '[' || sc.ch == ']') {
195                 sc.SetState(SCE_PS_PAREN_ARRAY);
196             } else if (sc.ch == '{' || sc.ch == '}') {
197                 sc.SetState(SCE_PS_PAREN_PROC);
198             } else if (sc.ch == '/') {
199                 if (sc.chNext == '/') {
200                     sc.SetState(SCE_PS_IMMEVAL);
201                     sc.Forward();
202                 } else {
203                     sc.SetState(SCE_PS_LITERAL);
204                 }
205             } else if (sc.ch == '<') {
206                 if (sc.chNext == '<') {
207                     sc.SetState(SCE_PS_PAREN_DICT);
208                     sc.Forward();
209                 } else if (sc.chNext == '~') {
210                     sc.SetState(SCE_PS_BASE85STRING);
211                     sc.Forward();
212                 } else {
213                     sc.SetState(SCE_PS_HEXSTRING);
214                 }
215             } else if (sc.ch == '>' && sc.chNext == '>') {
216                     sc.SetState(SCE_PS_PAREN_DICT);
217                     sc.Forward();
218             } else if (sc.ch == '>' || sc.ch == ')') {
219                 sc.SetState(SCE_C_DEFAULT);
220                 styler.ColourTo(sc.currentPos, SCE_PS_BADSTRINGCHAR);
221             } else if (sc.ch == '(') {
222                 sc.SetState(SCE_PS_TEXT);
223                 nestTextCurrent = 1;
224             } else if (sc.ch == '%') {
225                 if (sc.chNext == '%' && sc.atLineStart) {
226                     sc.SetState(SCE_PS_DSC_COMMENT);
227                     sc.Forward();
228                     if (sc.chNext == '+') {
229                         sc.Forward();
230                         sc.ForwardSetState(SCE_PS_DSC_VALUE);
231                     }
232                 } else {
233                     sc.SetState(SCE_PS_COMMENT);
234                 }
235             } else if ((sc.ch == '+' || sc.ch == '-' || sc.ch == '.') &&
236                        IsABaseNDigit(sc.chNext, 10)) {
237                 sc.SetState(SCE_PS_NUMBER);
238                 numRadix = 0;
239                 numHasPoint = (sc.ch == '.');
240                 numHasExponent = false;
241                 numHasSign = (sc.ch == '+' || sc.ch == '-');
242             } else if ((sc.ch == '+' || sc.ch == '-') && sc.chNext == '.' &&
243                        IsABaseNDigit(sc.GetRelative(2), 10)) {
244                 sc.SetState(SCE_PS_NUMBER);
245                 numRadix = 0;
246                 numHasPoint = false;
247                 numHasExponent = false;
248                 numHasSign = true;
249             } else if (IsABaseNDigit(sc.ch, 10)) {
250                 sc.SetState(SCE_PS_NUMBER);
251                 numRadix = 0;
252                 numHasPoint = false;
253                 numHasExponent = false;
254                 numHasSign = false;
255             } else if (!IsAWhitespaceChar(sc.ch)) {
256                 sc.SetState(SCE_PS_NAME);
257             }
258         }
259 
260         if (sc.atLineEnd)
261             styler.SetLineState(lineCurrent, nestTextCurrent);
262     }
263 
264     sc.Complete();
265 }
266 
FoldPSDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)267 static void FoldPSDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[],
268                        Accessor &styler) {
269     bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
270     bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
271     Sci_PositionU endPos = startPos + length;
272     int visibleChars = 0;
273     Sci_Position lineCurrent = styler.GetLine(startPos);
274     int levelCurrent = SC_FOLDLEVELBASE;
275     if (lineCurrent > 0)
276         levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
277     int levelMinCurrent = levelCurrent;
278     int levelNext = levelCurrent;
279     char chNext = styler[startPos];
280     int styleNext = styler.StyleAt(startPos);
281     for (Sci_PositionU i = startPos; i < endPos; i++) {
282         char ch = chNext;
283         chNext = styler.SafeGetCharAt(i + 1);
284         int style = styleNext;
285         styleNext = styler.StyleAt(i + 1);
286         bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');  //mac??
287         if ((style & 31) == SCE_PS_PAREN_PROC) {
288             if (ch == '{') {
289                 // Measure the minimum before a '{' to allow
290                 // folding on "} {"
291                 if (levelMinCurrent > levelNext) {
292                     levelMinCurrent = levelNext;
293                 }
294                 levelNext++;
295             } else if (ch == '}') {
296                 levelNext--;
297             }
298         }
299         if (atEOL) {
300             int levelUse = levelCurrent;
301             if (foldAtElse) {
302                 levelUse = levelMinCurrent;
303             }
304             int lev = levelUse | levelNext << 16;
305             if (visibleChars == 0 && foldCompact)
306                 lev |= SC_FOLDLEVELWHITEFLAG;
307             if (levelUse < levelNext)
308                 lev |= SC_FOLDLEVELHEADERFLAG;
309             if (lev != styler.LevelAt(lineCurrent)) {
310                 styler.SetLevel(lineCurrent, lev);
311             }
312             lineCurrent++;
313             levelCurrent = levelNext;
314             levelMinCurrent = levelCurrent;
315             visibleChars = 0;
316         }
317         if (!isspacechar(ch))
318             visibleChars++;
319     }
320 }
321 
322 static const char * const psWordListDesc[] = {
323     "PS Level 1 operators",
324     "PS Level 2 operators",
325     "PS Level 3 operators",
326     "RIP-specific operators",
327     "User-defined operators",
328     0
329 };
330 
331 LexerModule lmPS(SCLEX_PS, ColourisePSDoc, "ps", FoldPSDoc, psWordListDesc);
332