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