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