1 // Scintilla source code edit control
2 /** @file LexProgress.cxx
3  **  Lexer for Progress 4GL.
4  ** Based on LexCPP.cxx of Neil Hodgson <neilh@scintilla.org>
5   **/
6 // Copyright 2006-2007 by Yuval Papish <Yuval@YuvCom.com>
7 // The License.txt file describes the conditions under which this software may be distributed.
8 
9 /** TODO:
10 WebSpeed support in html lexer
11 Support "end triggers" expression of the triggers phrase
12 Support more than 6 comments levels
13 **/
14 #include <stdlib.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <stdarg.h>
18 #include <assert.h>
19 #include <ctype.h>
20 
21 #include "ILexer.h"
22 #include "Scintilla.h"
23 #include "SciLexer.h"
24 
25 #include "WordList.h"
26 #include "LexAccessor.h"
27 #include "Accessor.h"
28 #include "StyleContext.h"
29 #include "CharacterSet.h"
30 #include "LexerModule.h"
31 
32 #ifdef SCI_NAMESPACE
33 using namespace Scintilla;
34 #endif
35 
IsAWordChar(int ch)36 static inline bool IsAWordChar(int ch) {
37 	return (ch < 0x80) && (isalnum(ch) || ch == '_');
38 }
39 
IsAWordStart(int ch)40 static inline bool IsAWordStart(int ch) {
41 	return (ch < 0x80) && (isalpha(ch) || ch == '_');
42 }
43 
44 enum SentenceStart { SetSentenceStart = 0xf, ResetSentenceStart = 0x10}; // true -> bit = 0
45 
Colourise4glDoc(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler)46 static void Colourise4glDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
47                             Accessor &styler) {
48 
49     WordList &keywords1 = *keywordlists[0];   // regular keywords
50     WordList &keywords2 = *keywordlists[1];   // block opening keywords, only when SentenceStart
51     WordList &keywords3 = *keywordlists[2];   // block opening keywords
52     //WordList &keywords4 = *keywordlists[3]; // preprocessor keywords. Not implemented
53 
54 
55 	int visibleChars = 0;
56 	int mask;
57 
58 	StyleContext sc(startPos, length, initStyle, styler);
59 
60 	for (; sc.More(); sc.Forward()) {
61 
62 		if (sc.atLineStart) {
63 			// Reset states to begining of colourise so no surprises
64 			// if different sets of lines lexed.
65 			visibleChars = 0;
66 		}
67 
68 		// Handle line continuation generically.
69 		if ((sc.state & 0xf) < SCE_4GL_COMMENT1) {
70 		if (sc.ch == '~') {
71 			if (sc.chNext > ' ') {
72 				// skip special char after ~
73 				sc.Forward();
74 				continue;
75 			}
76 			else {
77 				// Skip whitespace between ~ and EOL
78 				while (sc.More() && (sc.chNext == ' ' || sc.chNext == '\t') ) {
79 					sc.Forward();
80 				}
81 				if (sc.chNext == '\n' || sc.chNext == '\r') {
82 					sc.Forward();
83 					if (sc.ch == '\r' && sc.chNext == '\n') {
84 						sc.Forward();
85 					}
86 					sc.Forward();
87 					continue;
88 				}
89 			}
90 		}
91 		}
92 		// Determine if a new state should be terminated.
93 		mask = sc.state & 0x10;
94 		switch (sc.state & 0xf) {
95 			case SCE_4GL_OPERATOR:
96 				sc.SetState(SCE_4GL_DEFAULT | mask);
97 				break;
98 			case SCE_4GL_NUMBER:
99 				if (!(IsADigit(sc.ch))) {
100 					sc.SetState(SCE_4GL_DEFAULT | mask);
101 				}
102 				break;
103 			case SCE_4GL_IDENTIFIER:
104 				if (!IsAWordChar(sc.ch) && sc.ch != '-') {
105 					char s[1000];
106 					sc.GetCurrentLowered(s, sizeof(s));
107 					if ((((sc.state & 0x10) == 0) && keywords2.InList(s)) || keywords3.InList(s)) {
108 						sc.ChangeState(SCE_4GL_BLOCK | ResetSentenceStart);
109 					}
110 					else if (keywords1.InList(s)) {
111 						if ((s[0] == 'e' && s[1] =='n' && s[2] == 'd' && !isalnum(s[3]) && s[3] != '-') ||
112 							(s[0] == 'f' && s[1] =='o' && s[2] == 'r' && s[3] == 'w' && s[4] =='a' && s[5] == 'r' && s[6] == 'd'&& !isalnum(s[7]))) {
113 							sc.ChangeState(SCE_4GL_END | ResetSentenceStart);
114 						}
115 						else if	((s[0] == 'e' && s[1] =='l' && s[2] == 's' && s[3] == 'e') ||
116 								 (s[0] == 't' && s[1] =='h' && s[2] == 'e' && s[3] == 'n')) {
117 							sc.ChangeState(SCE_4GL_WORD & SetSentenceStart);
118 						}
119 						else {
120 							sc.ChangeState(SCE_4GL_WORD | ResetSentenceStart);
121 						}
122 					}
123 					sc.SetState(SCE_4GL_DEFAULT | (sc.state & 0x10));
124 				}
125 				break;
126 			case SCE_4GL_PREPROCESSOR:
127 				if (sc.atLineStart) {
128 					sc.SetState(SCE_4GL_DEFAULT & SetSentenceStart);
129 				}
130 				/* code removed to allow comments inside preprocessor
131 					else if (sc.ch == '*' && sc.chNext == '/') {
132 					sc.ForwardSetState(SCE_4GL_DEFAULT | sentenceStartState); } */
133 				break;
134 			case SCE_4GL_STRING:
135 				if (sc.ch == '\"') {
136 					sc.ForwardSetState(SCE_4GL_DEFAULT | mask);
137 				}
138 				break;
139 			case SCE_4GL_CHARACTER:
140 				if (sc.ch == '\'') {
141 					sc.ForwardSetState(SCE_4GL_DEFAULT | mask);
142 				}
143 				break;
144 			default:
145 				if ((sc.state & 0xf) >= SCE_4GL_COMMENT1) {
146 					if (sc.ch == '*' && sc.chNext == '/') {
147 						sc.Forward();
148 						if ((sc.state & 0xf) == SCE_4GL_COMMENT1) {
149 							sc.ForwardSetState(SCE_4GL_DEFAULT | mask);
150 						}
151 						else
152 							sc.SetState((sc.state & 0x1f) - 1);
153 					} else if (sc.ch == '/' && sc.chNext == '*') {
154 						sc.Forward();
155 						sc.SetState((sc.state & 0x1f) + 1);
156 					}
157 				}
158 		}
159 
160 		// Determine if a new state should be entered.
161 		mask = sc.state & 0x10;
162 		if ((sc.state & 0xf) == SCE_4GL_DEFAULT) {
163 			if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
164 				sc.SetState(SCE_4GL_NUMBER | ResetSentenceStart);
165 			} else if (IsAWordStart(sc.ch) || (sc.ch == '@')) {
166 				sc.SetState(SCE_4GL_IDENTIFIER | mask);
167 			} else if (sc.ch == '/' && sc.chNext == '*') {
168 				sc.SetState(SCE_4GL_COMMENT1 | mask);
169 				sc.Forward();
170 			} else if (sc.ch == '\"') {
171 				sc.SetState(SCE_4GL_STRING | ResetSentenceStart);
172 			} else if (sc.ch == '\'') {
173 				sc.SetState(SCE_4GL_CHARACTER | ResetSentenceStart);
174 			} else if (sc.ch == '&' && visibleChars == 0 && ((sc.state & 0x10) == 0)) {
175 				sc.SetState(SCE_4GL_PREPROCESSOR | ResetSentenceStart);
176 				// Skip whitespace between & and preprocessor word
177 				do {
178 					sc.Forward();
179 				} while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
180 			// Handle syntactical line termination
181 			} else if ((sc.ch == '.' || sc.ch == ':' || sc.ch == '}') && (sc.chNext == ' ' || sc.chNext == '\t' || sc.chNext == '\n' || sc.chNext == '\r')) {
182 				sc.SetState(sc.state & SetSentenceStart);
183 			} else if (isoperator(static_cast<char>(sc.ch))) {
184 		/* 	This code allows highlight of handles. Alas, it would cause the phrase "last-event:function"
185 			to be recognized as a BlockBegin */
186 
187 				if (sc.ch == ':')
188 					sc.SetState(SCE_4GL_OPERATOR & SetSentenceStart);
189 				/* else */
190 					sc.SetState(SCE_4GL_OPERATOR | ResetSentenceStart);
191 			}
192 		}
193 
194 		if (!IsASpace(sc.ch)) {
195 			visibleChars++;
196 		}
197 	}
198 	sc.Complete();
199 }
200 
IsStreamCommentStyle(int style)201 static bool IsStreamCommentStyle(int style) {
202 	return (style & 0xf) >= SCE_4GL_COMMENT1 ;
203 }
204 
205 // Store both the current line's fold level and the next lines in the
206 // level store to make it easy to pick up with each increment
207 // and to make it possible to fiddle the current level for "} else {".
FoldNoBox4glDoc(unsigned int startPos,int length,int initStyle,Accessor & styler)208 static void FoldNoBox4glDoc(unsigned int startPos, int length, int initStyle,
209                             Accessor &styler) {
210 	bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
211 	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
212 	bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
213 	unsigned int endPos = startPos + length;
214 	int visibleChars = 0;
215 	int lineCurrent = styler.GetLine(startPos);
216 	int levelCurrent = SC_FOLDLEVELBASE;
217 	if (lineCurrent > 0)
218 		levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
219 	int levelMinCurrent = levelCurrent;
220 	int levelNext = levelCurrent;
221 	char chNext = static_cast<char>(tolower(styler[startPos]));
222 	int styleNext = styler.StyleAt(startPos);
223 	int style = initStyle;
224 	for (unsigned int i = startPos; i < endPos; i++) {
225 		char ch = chNext;
226 		chNext = static_cast<char>(tolower(styler.SafeGetCharAt(i + 1)));
227 		int stylePrev = style;
228 		style = styleNext;
229 		styleNext = styler.StyleAt(i + 1);
230 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
231 		if (foldComment && IsStreamCommentStyle(style)) {
232 			if (!IsStreamCommentStyle(stylePrev)) {
233 				levelNext++;
234 			} else if (!IsStreamCommentStyle(styleNext)) { // && !atEOL) {
235 				// Comments don't end at end of line and the next character may be unstyled.
236 				levelNext--;
237 			}
238 		}
239 		else if ((style & 0xf) == SCE_4GL_BLOCK && !isalnum(chNext)) {
240 			levelNext++;
241 		}
242 		else if ((style & 0xf) == SCE_4GL_END  && (ch == 'e' || ch == 'f')) {
243 			levelNext--;
244 		}
245 		if (atEOL) {
246 			int levelUse = levelCurrent;
247 			if (foldAtElse) {
248 				levelUse = levelMinCurrent;
249 			}
250 			int lev = levelUse | levelNext << 16;
251 			if (visibleChars == 0 && foldCompact)
252 				lev |= SC_FOLDLEVELWHITEFLAG;
253 			if (levelUse < levelNext)
254 				lev |= SC_FOLDLEVELHEADERFLAG;
255 			if (lev != styler.LevelAt(lineCurrent)) {
256 				styler.SetLevel(lineCurrent, lev);
257 			}
258 			lineCurrent++;
259 			levelCurrent = levelNext;
260 			levelMinCurrent = levelCurrent;
261 			visibleChars = 0;
262 		}
263 		if (!isspacechar(ch))
264 			visibleChars++;
265 	}
266 }
267 
Fold4glDoc(unsigned int startPos,int length,int initStyle,WordList * [],Accessor & styler)268 static void Fold4glDoc(unsigned int startPos, int length, int initStyle, WordList *[],
269                        Accessor &styler) {
270 	FoldNoBox4glDoc(startPos, length, initStyle, styler);
271 }
272 
273 static const char * const FglWordLists[] = {
274             "Primary keywords and identifiers",
275             "Secondary keywords and identifiers",
276             "Documentation comment keywords",
277             "Unused",
278             "Global classes and typedefs",
279             0,
280         };
281 
282 LexerModule lmProgress(SCLEX_PROGRESS, Colourise4glDoc, "progress", Fold4glDoc, FglWordLists);
283