1 | // Scintilla source code edit control |
2 | /** @file LexSmalltalk.cxx |
3 | ** Lexer for Smalltalk language. |
4 | ** Written by Sergey Philippov, sphilippov-at-gmail-dot-com |
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 | /* |
33 | | lexTable classificationBlock charClasses | |
34 | charClasses := #(#DecDigit #Letter #Special #Upper #BinSel). |
35 | lexTable := ByteArray new: 128. |
36 | classificationBlock := [ :charClass :chars | |
37 | | flag | |
38 | flag := 1 bitShift: (charClasses indexOf: charClass) - 1. |
39 | chars do: [ :char | lexTable at: char codePoint + 1 put: ((lexTable at: char codePoint + 1) bitOr: flag)]]. |
40 | |
41 | classificationBlock |
42 | value: #DecDigit value: '0123456789'; |
43 | value: #Letter value: '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
44 | value: #Special value: '()[]{};.^:'; |
45 | value: #BinSel value: '~@%&*-+=|\/,<>?!'; |
46 | value: #Upper value: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. |
47 | |
48 | ((String new: 500) streamContents: [ :stream | |
49 | stream crLf; nextPutAll: 'static int ClassificationTable[256] = {'. |
50 | lexTable keysAndValuesDo: [ :index :value | |
51 | ((index - 1) rem: 16) == 0 ifTrue: [ |
52 | stream crLf; tab] |
53 | ifFalse: [ |
54 | stream space]. |
55 | stream print: value. |
56 | index ~= 256 ifTrue: [ |
57 | stream nextPut: $,]]. |
58 | stream crLf; nextPutAll: '};'; crLf. |
59 | |
60 | charClasses keysAndValuesDo: [ :index :name | |
61 | stream |
62 | crLf; |
63 | nextPutAll: ( |
64 | ('static inline bool is<1s>(int ch) {return (ch > 0) && (ch %< 0x80) && ((ClassificationTable[ch] & <2p>) != 0);}') |
65 | expandMacrosWith: name with: (1 bitShift: (index - 1))) |
66 | ]]) edit |
67 | */ |
68 | |
69 | // autogenerated {{{{ |
70 | |
71 | static int ClassificationTable[256] = { |
72 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
73 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
74 | 0, 16, 0, 0, 0, 16, 16, 0, 4, 4, 16, 16, 16, 16, 4, 16, |
75 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 16, 16, 16, 16, |
76 | 16, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, |
77 | 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 16, 4, 4, 2, |
78 | 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |
79 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 16, 4, 16, 0, |
80 | }; |
81 | |
82 | static inline bool isDecDigit(int ch) {return (ch > 0) && (ch < 0x80) && ((ClassificationTable[ch] & 1) != 0);} |
83 | static inline bool isLetter(int ch) {return (ch > 0) && (ch < 0x80) && ((ClassificationTable[ch] & 2) != 0);} |
84 | static inline bool isSpecial(int ch) {return (ch > 0) && (ch < 0x80) && ((ClassificationTable[ch] & 4) != 0);} |
85 | static inline bool isUpper(int ch) {return (ch > 0) && (ch < 0x80) && ((ClassificationTable[ch] & 8) != 0);} |
86 | static inline bool isBinSel(int ch) {return (ch > 0) && (ch < 0x80) && ((ClassificationTable[ch] & 16) != 0);} |
87 | // autogenerated }}}} |
88 | |
89 | static inline bool isAlphaNumeric(int ch) { |
90 | return isDecDigit(ch) || isLetter(ch); |
91 | } |
92 | |
93 | static inline bool isDigitOfRadix(int ch, int radix) |
94 | { |
95 | if (isDecDigit(ch)) |
96 | return (ch - '0') < radix; |
97 | else if (!isUpper(ch)) |
98 | return false; |
99 | else |
100 | return (ch - 'A' + 10) < radix; |
101 | } |
102 | |
103 | static inline void (StyleContext& sc) |
104 | { |
105 | while (sc.More() && sc.ch != '\"') |
106 | sc.Forward(); |
107 | } |
108 | |
109 | static inline void skipString(StyleContext& sc) |
110 | { |
111 | while (sc.More()) { |
112 | if (sc.ch == '\'') { |
113 | if (sc.chNext != '\'') |
114 | return; |
115 | sc.Forward(); |
116 | } |
117 | sc.Forward(); |
118 | } |
119 | } |
120 | |
121 | static void handleHash(StyleContext& sc) |
122 | { |
123 | if (isSpecial(sc.chNext)) { |
124 | sc.SetState(SCE_ST_SPECIAL); |
125 | return; |
126 | } |
127 | |
128 | sc.SetState(SCE_ST_SYMBOL); |
129 | sc.Forward(); |
130 | if (sc.ch == '\'') { |
131 | sc.Forward(); |
132 | skipString(sc); |
133 | } |
134 | else { |
135 | if (isLetter(sc.ch)) { |
136 | while (isAlphaNumeric(sc.chNext) || sc.chNext == ':') |
137 | sc.Forward(); |
138 | } |
139 | else if (isBinSel(sc.ch)) { |
140 | while (isBinSel(sc.chNext)) |
141 | sc.Forward(); |
142 | } |
143 | } |
144 | } |
145 | |
146 | static inline void handleSpecial(StyleContext& sc) |
147 | { |
148 | if (sc.ch == ':' && sc.chNext == '=') { |
149 | sc.SetState(SCE_ST_ASSIGN); |
150 | sc.Forward(); |
151 | } |
152 | else { |
153 | if (sc.ch == '^') |
154 | sc.SetState(SCE_ST_RETURN); |
155 | else |
156 | sc.SetState(SCE_ST_SPECIAL); |
157 | } |
158 | } |
159 | |
160 | static inline void skipInt(StyleContext& sc, int radix) |
161 | { |
162 | while (isDigitOfRadix(sc.chNext, radix)) |
163 | sc.Forward(); |
164 | } |
165 | |
166 | static void handleNumeric(StyleContext& sc) |
167 | { |
168 | char num[256]; |
169 | int nl; |
170 | int radix; |
171 | |
172 | sc.SetState(SCE_ST_NUMBER); |
173 | num[0] = static_cast<char>(sc.ch); |
174 | nl = 1; |
175 | while (isDecDigit(sc.chNext)) { |
176 | num[nl++] = static_cast<char>(sc.chNext); |
177 | sc.Forward(); |
178 | if (nl+1 == sizeof(num)/sizeof(num[0])) // overrun check |
179 | break; |
180 | } |
181 | if (sc.chNext == 'r') { |
182 | num[nl] = 0; |
183 | if (num[0] == '-') |
184 | radix = atoi(num + 1); |
185 | else |
186 | radix = atoi(num); |
187 | sc.Forward(); |
188 | if (sc.chNext == '-') |
189 | sc.Forward(); |
190 | skipInt(sc, radix); |
191 | } |
192 | else |
193 | radix = 10; |
194 | if (sc.chNext != '.' || !isDigitOfRadix(sc.GetRelative(2), radix)) |
195 | return; |
196 | sc.Forward(); |
197 | skipInt(sc, radix); |
198 | if (sc.chNext == 's') { |
199 | // ScaledDecimal |
200 | sc.Forward(); |
201 | while (isDecDigit(sc.chNext)) |
202 | sc.Forward(); |
203 | return; |
204 | } |
205 | else if (sc.chNext != 'e' && sc.chNext != 'd' && sc.chNext != 'q') |
206 | return; |
207 | sc.Forward(); |
208 | if (sc.chNext == '+' || sc.chNext == '-') |
209 | sc.Forward(); |
210 | skipInt(sc, radix); |
211 | } |
212 | |
213 | static inline void handleBinSel(StyleContext& sc) |
214 | { |
215 | sc.SetState(SCE_ST_BINARY); |
216 | while (isBinSel(sc.chNext)) |
217 | sc.Forward(); |
218 | } |
219 | |
220 | static void handleLetter(StyleContext& sc, WordList* specialSelectorList) |
221 | { |
222 | char ident[256]; |
223 | int il; |
224 | int state; |
225 | bool doubleColonPresent; |
226 | |
227 | sc.SetState(SCE_ST_DEFAULT); |
228 | |
229 | ident[0] = static_cast<char>(sc.ch); |
230 | il = 1; |
231 | while (isAlphaNumeric(sc.chNext)) { |
232 | ident[il++] = static_cast<char>(sc.chNext); |
233 | sc.Forward(); |
234 | if (il+2 == sizeof(ident)/sizeof(ident[0])) // overrun check |
235 | break; |
236 | } |
237 | |
238 | if (sc.chNext == ':') { |
239 | doubleColonPresent = true; |
240 | ident[il++] = ':'; |
241 | sc.Forward(); |
242 | } |
243 | else |
244 | doubleColonPresent = false; |
245 | ident[il] = 0; |
246 | |
247 | if (specialSelectorList->InList(ident)) |
248 | state = SCE_ST_SPEC_SEL; |
249 | else if (doubleColonPresent) |
250 | state = SCE_ST_KWSEND; |
251 | else if (isUpper(ident[0])) |
252 | state = SCE_ST_GLOBAL; |
253 | else { |
254 | if (!strcmp(ident, "self" )) |
255 | state = SCE_ST_SELF; |
256 | else if (!strcmp(ident, "super" )) |
257 | state = SCE_ST_SUPER; |
258 | else if (!strcmp(ident, "nil" )) |
259 | state = SCE_ST_NIL; |
260 | else if (!strcmp(ident, "true" ) || !strcmp(ident, "false" )) |
261 | state = SCE_ST_BOOL; |
262 | else |
263 | state = SCE_ST_DEFAULT; |
264 | } |
265 | |
266 | sc.ChangeState(state); |
267 | } |
268 | |
269 | static void colorizeSmalltalkDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *wordLists[], Accessor &styler) |
270 | { |
271 | StyleContext sc(startPos, length, initStyle, styler); |
272 | |
273 | if (initStyle == SCE_ST_COMMENT) { |
274 | skipComment(sc); |
275 | if (sc.More()) |
276 | sc.Forward(); |
277 | } |
278 | else if (initStyle == SCE_ST_STRING) { |
279 | skipString(sc); |
280 | if (sc.More()) |
281 | sc.Forward(); |
282 | } |
283 | |
284 | for (; sc.More(); sc.Forward()) { |
285 | int ch; |
286 | |
287 | ch = sc.ch; |
288 | if (ch == '\"') { |
289 | sc.SetState(SCE_ST_COMMENT); |
290 | sc.Forward(); |
291 | skipComment(sc); |
292 | } |
293 | else if (ch == '\'') { |
294 | sc.SetState(SCE_ST_STRING); |
295 | sc.Forward(); |
296 | skipString(sc); |
297 | } |
298 | else if (ch == '#') |
299 | handleHash(sc); |
300 | else if (ch == '$') { |
301 | sc.SetState(SCE_ST_CHARACTER); |
302 | sc.Forward(); |
303 | } |
304 | else if (isSpecial(ch)) |
305 | handleSpecial(sc); |
306 | else if (isDecDigit(ch)) |
307 | handleNumeric(sc); |
308 | else if (isLetter(ch)) |
309 | handleLetter(sc, wordLists[0]); |
310 | else if (isBinSel(ch)) { |
311 | if (ch == '-' && isDecDigit(sc.chNext)) |
312 | handleNumeric(sc); |
313 | else |
314 | handleBinSel(sc); |
315 | } |
316 | else |
317 | sc.SetState(SCE_ST_DEFAULT); |
318 | } |
319 | sc.Complete(); |
320 | } |
321 | |
322 | static const char* const smalltalkWordListDesc[] = { |
323 | "Special selectors" , |
324 | 0 |
325 | }; |
326 | |
327 | LexerModule lmSmalltalk(SCLEX_SMALLTALK, colorizeSmalltalkDoc, "smalltalk" , NULL, smalltalkWordListDesc); |
328 | |