1 // Scintilla source code edit control
2 /** @file LexSpecman.cxx
3  ** Lexer for Specman E language.
4  ** Written by Avi Yegudin, based on C++ lexer by Neil Hodgson
5  **/
6 // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
7 // The License.txt file describes the conditions under which this software may be distributed.
8 
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <assert.h>
14 #include <ctype.h>
15 
16 #include "ILexer.h"
17 #include "Scintilla.h"
18 #include "SciLexer.h"
19 
20 #include "WordList.h"
21 #include "LexAccessor.h"
22 #include "Accessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
26 
27 #ifdef SCI_NAMESPACE
28 using namespace Scintilla;
29 #endif
30 
IsAWordChar(const int ch)31 static inline bool IsAWordChar(const int ch) {
32 	return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch == '\'');
33 }
34 
IsANumberChar(const int ch)35 static inline bool IsANumberChar(const int ch) {
36 	return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '\'');
37 }
38 
IsAWordStart(const int ch)39 static inline bool IsAWordStart(const int ch) {
40 	return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '`');
41 }
42 
ColouriseSpecmanDoc(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler,bool caseSensitive)43 static void ColouriseSpecmanDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
44                             Accessor &styler, bool caseSensitive) {
45 
46 	WordList &keywords = *keywordlists[0];
47 	WordList &keywords2 = *keywordlists[1];
48 	WordList &keywords3 = *keywordlists[2];
49 	WordList &keywords4 = *keywordlists[3];
50 
51 	// Do not leak onto next line
52 	if (initStyle == SCE_SN_STRINGEOL)
53 		initStyle = SCE_SN_CODE;
54 
55 	int visibleChars = 0;
56 
57 	StyleContext sc(startPos, length, initStyle, styler);
58 
59 	for (; sc.More(); sc.Forward()) {
60 
61 		if (sc.atLineStart && (sc.state == SCE_SN_STRING)) {
62 			// Prevent SCE_SN_STRINGEOL from leaking back to previous line
63 			sc.SetState(SCE_SN_STRING);
64 		}
65 
66 		// Handle line continuation generically.
67 		if (sc.ch == '\\') {
68 			if (sc.chNext == '\n' || sc.chNext == '\r') {
69 				sc.Forward();
70 				if (sc.ch == '\r' && sc.chNext == '\n') {
71 					sc.Forward();
72 				}
73 				continue;
74 			}
75 		}
76 
77 		// Determine if the current state should terminate.
78 		if (sc.state == SCE_SN_OPERATOR) {
79 			sc.SetState(SCE_SN_CODE);
80 		} else if (sc.state == SCE_SN_NUMBER) {
81 			if (!IsANumberChar(sc.ch)) {
82 				sc.SetState(SCE_SN_CODE);
83 			}
84 		} else if (sc.state == SCE_SN_IDENTIFIER) {
85 			if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
86 				char s[100];
87 				if (caseSensitive) {
88 					sc.GetCurrent(s, sizeof(s));
89 				} else {
90 					sc.GetCurrentLowered(s, sizeof(s));
91 				}
92 				if (keywords.InList(s)) {
93 					sc.ChangeState(SCE_SN_WORD);
94 				} else if (keywords2.InList(s)) {
95 					sc.ChangeState(SCE_SN_WORD2);
96 				} else if (keywords3.InList(s)) {
97                                         sc.ChangeState(SCE_SN_WORD3);
98 				} else if (keywords4.InList(s)) {
99 					sc.ChangeState(SCE_SN_USER);
100 				}
101 				sc.SetState(SCE_SN_CODE);
102 			}
103 		} else if (sc.state == SCE_SN_PREPROCESSOR) {
104                         if (IsASpace(sc.ch)) {
105                                 sc.SetState(SCE_SN_CODE);
106                         }
107 		} else if (sc.state == SCE_SN_DEFAULT) {
108 			if (sc.Match('<', '\'')) {
109 				sc.Forward();
110 				sc.ForwardSetState(SCE_SN_CODE);
111 			}
112 		} else if (sc.state == SCE_SN_COMMENTLINE || sc.state == SCE_SN_COMMENTLINEBANG) {
113 			if (sc.atLineEnd) {
114 				sc.SetState(SCE_SN_CODE);
115 				visibleChars = 0;
116 			}
117 		} else if (sc.state == SCE_SN_STRING) {
118 			if (sc.ch == '\\') {
119 				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
120 					sc.Forward();
121 				}
122 			} else if (sc.ch == '\"') {
123 				sc.ForwardSetState(SCE_SN_CODE);
124 			} else if (sc.atLineEnd) {
125 				sc.ChangeState(SCE_SN_STRINGEOL);
126 				sc.ForwardSetState(SCE_SN_CODE);
127 				visibleChars = 0;
128 			}
129 		} else if (sc.state == SCE_SN_SIGNAL) {
130 			if (sc.atLineEnd) {
131 				sc.ChangeState(SCE_SN_STRINGEOL);
132 				sc.ForwardSetState(SCE_SN_CODE);
133 				visibleChars = 0;
134 			} else if (sc.ch == '\\') {
135 				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
136 					sc.Forward();
137 				}
138 			} else if (sc.ch == '\'') {
139 				sc.ForwardSetState(SCE_SN_CODE);
140 			}
141 		} else if (sc.state == SCE_SN_REGEXTAG) {
142 			if (!IsADigit(sc.ch)) {
143 				sc.SetState(SCE_SN_CODE);
144 			}
145 		}
146 
147 		// Determine if a new state should be entered.
148 		if (sc.state == SCE_SN_CODE) {
149 			if (sc.ch == '$' && IsADigit(sc.chNext)) {
150 				sc.SetState(SCE_SN_REGEXTAG);
151                                 sc.Forward();
152 			} else if (IsADigit(sc.ch)) {
153                                 sc.SetState(SCE_SN_NUMBER);
154 			} else if (IsAWordStart(sc.ch)) {
155 				sc.SetState(SCE_SN_IDENTIFIER);
156 			} else if (sc.Match('\'', '>')) {
157                                 sc.SetState(SCE_SN_DEFAULT);
158 				sc.Forward();	// Eat the * so it isn't used for the end of the comment
159 			} else if (sc.Match('/', '/')) {
160 				if (sc.Match("//!"))	// Nice to have a different comment style
161 					sc.SetState(SCE_SN_COMMENTLINEBANG);
162 				else
163 					sc.SetState(SCE_SN_COMMENTLINE);
164 			} else if (sc.Match('-', '-')) {
165 				if (sc.Match("--!"))	// Nice to have a different comment style
166 					sc.SetState(SCE_SN_COMMENTLINEBANG);
167 				else
168 					sc.SetState(SCE_SN_COMMENTLINE);
169 			} else if (sc.ch == '\"') {
170 				sc.SetState(SCE_SN_STRING);
171 			} else if (sc.ch == '\'') {
172 				sc.SetState(SCE_SN_SIGNAL);
173 			} else if (sc.ch == '#' && visibleChars == 0) {
174 				// Preprocessor commands are alone on their line
175 				sc.SetState(SCE_SN_PREPROCESSOR);
176 				// Skip whitespace between # and preprocessor word
177 				do {
178 					sc.Forward();
179 				} while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
180 				if (sc.atLineEnd) {
181 					sc.SetState(SCE_SN_CODE);
182 				}
183 			} else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '@') {
184 				sc.SetState(SCE_SN_OPERATOR);
185 			}
186 		}
187 
188 		if (sc.atLineEnd) {
189 			// Reset states to begining of colourise so no surprises
190 			// if different sets of lines lexed.
191 			visibleChars = 0;
192 		}
193 		if (!IsASpace(sc.ch)) {
194 			visibleChars++;
195 		}
196 	}
197 	sc.Complete();
198 }
199 
200 // Store both the current line's fold level and the next lines in the
201 // level store to make it easy to pick up with each increment
202 // and to make it possible to fiddle the current level for "} else {".
FoldNoBoxSpecmanDoc(unsigned int startPos,int length,int,Accessor & styler)203 static void FoldNoBoxSpecmanDoc(unsigned int startPos, int length, int,
204                             Accessor &styler) {
205 	bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
206 	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
207 	bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
208 	unsigned int endPos = startPos + length;
209 	int visibleChars = 0;
210 	int lineCurrent = styler.GetLine(startPos);
211 	int levelCurrent = SC_FOLDLEVELBASE;
212 	if (lineCurrent > 0)
213 		levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
214 	int levelMinCurrent = levelCurrent;
215 	int levelNext = levelCurrent;
216 	char chNext = styler[startPos];
217 	int styleNext = styler.StyleAt(startPos);
218 	int style;
219 	for (unsigned int i = startPos; i < endPos; i++) {
220 		char ch = chNext;
221 		chNext = styler.SafeGetCharAt(i + 1);
222 		//int stylePrev = style;
223 		style = styleNext;
224 		styleNext = styler.StyleAt(i + 1);
225 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
226 		if (foldComment && (style == SCE_SN_COMMENTLINE)) {
227 			if (((ch == '/') && (chNext == '/')) ||
228                             ((ch == '-') && (chNext == '-'))) {
229 				char chNext2 = styler.SafeGetCharAt(i + 2);
230 				if (chNext2 == '{') {
231 					levelNext++;
232 				} else if (chNext2 == '}') {
233 					levelNext--;
234 				}
235 			}
236 		}
237 		if (style == SCE_SN_OPERATOR) {
238 			if (ch == '{') {
239 				// Measure the minimum before a '{' to allow
240 				// folding on "} else {"
241 				if (levelMinCurrent > levelNext) {
242 					levelMinCurrent = levelNext;
243 				}
244 				levelNext++;
245 			} else if (ch == '}') {
246 				levelNext--;
247 			}
248 		}
249 		if (atEOL) {
250 			int levelUse = levelCurrent;
251 			if (foldAtElse) {
252 				levelUse = levelMinCurrent;
253 			}
254 			int lev = levelUse | levelNext << 16;
255 			if (visibleChars == 0 && foldCompact)
256 				lev |= SC_FOLDLEVELWHITEFLAG;
257 			if (levelUse < levelNext)
258 				lev |= SC_FOLDLEVELHEADERFLAG;
259 			if (lev != styler.LevelAt(lineCurrent)) {
260 				styler.SetLevel(lineCurrent, lev);
261 			}
262 			lineCurrent++;
263 			levelCurrent = levelNext;
264 			levelMinCurrent = levelCurrent;
265 			visibleChars = 0;
266 		}
267 		if (!isspacechar(ch))
268 			visibleChars++;
269 	}
270 }
271 
FoldSpecmanDoc(unsigned int startPos,int length,int initStyle,WordList * [],Accessor & styler)272 static void FoldSpecmanDoc(unsigned int startPos, int length, int initStyle, WordList *[],
273                        Accessor &styler) {
274 	FoldNoBoxSpecmanDoc(startPos, length, initStyle, styler);
275 }
276 
277 static const char * const specmanWordLists[] = {
278             "Primary keywords and identifiers",
279             "Secondary keywords and identifiers",
280             "Sequence keywords and identifiers",
281             "User defined keywords and identifiers",
282             "Unused",
283             0,
284         };
285 
ColouriseSpecmanDocSensitive(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler)286 static void ColouriseSpecmanDocSensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
287                                      Accessor &styler) {
288 	ColouriseSpecmanDoc(startPos, length, initStyle, keywordlists, styler, true);
289 }
290 
291 
292 LexerModule lmSpecman(SCLEX_SPECMAN, ColouriseSpecmanDocSensitive, "specman", FoldSpecmanDoc, specmanWordLists);
293