1 // Scintilla source code edit control
2 /** @file LexLisp.cxx
3  ** Lexer for Lisp.
4  ** Written by Alexey Yutkin.
5  **/
6 // Copyright 1998-2001 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 
31 #define SCE_LISP_CHARACTER 29
32 #define SCE_LISP_MACRO 30
33 #define SCE_LISP_MACRO_DISPATCH 31
34 
isLispoperator(char ch)35 static inline bool isLispoperator(char ch) {
36 	if (IsASCII(ch) && isalnum(ch))
37 		return false;
38 	if (ch == '\'' || ch == '`' || ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == '{' || ch == '}')
39 		return true;
40 	return false;
41 }
42 
isLispwordstart(char ch)43 static inline bool isLispwordstart(char ch) {
44 	return IsASCII(ch) && ch != ';'  && !isspacechar(ch) && !isLispoperator(ch) &&
45 		ch != '\n' && ch != '\r' &&  ch != '\"';
46 }
47 
48 
classifyWordLisp(unsigned int start,unsigned int end,WordList & keywords,WordList & keywords_kw,Accessor & styler)49 static void classifyWordLisp(unsigned int start, unsigned int end, WordList &keywords, WordList &keywords_kw, Accessor &styler) {
50 	assert(end >= start);
51 	char s[100];
52 	unsigned int i;
53 	bool digit_flag = true;
54 	for (i = 0; (i < end - start + 1) && (i < 99); i++) {
55 		s[i] = styler[start + i];
56 		s[i + 1] = '\0';
57 		if (!isdigit(s[i]) && (s[i] != '.')) digit_flag = false;
58 	}
59 	char chAttr = SCE_LISP_IDENTIFIER;
60 
61 	if(digit_flag) chAttr = SCE_LISP_NUMBER;
62 	else {
63 		if (keywords.InList(s)) {
64 			chAttr = SCE_LISP_KEYWORD;
65 		} else if (keywords_kw.InList(s)) {
66 			chAttr = SCE_LISP_KEYWORD_KW;
67 		} else if ((s[0] == '*' && s[i-1] == '*') ||
68 			   (s[0] == '+' && s[i-1] == '+')) {
69 			chAttr = SCE_LISP_SPECIAL;
70 		}
71 	}
72 	styler.ColourTo(end, chAttr);
73 	return;
74 }
75 
76 
ColouriseLispDoc(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler)77 static void ColouriseLispDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
78                             Accessor &styler) {
79 
80 	WordList &keywords = *keywordlists[0];
81 	WordList &keywords_kw = *keywordlists[1];
82 
83 	styler.StartAt(startPos);
84 
85 	int state = initStyle, radix = -1;
86 	char chNext = styler[startPos];
87 	unsigned int lengthDoc = startPos + length;
88 	styler.StartSegment(startPos);
89 	for (unsigned int i = startPos; i < lengthDoc; i++) {
90 		char ch = chNext;
91 		chNext = styler.SafeGetCharAt(i + 1);
92 
93 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
94 
95 		if (styler.IsLeadByte(ch)) {
96 			chNext = styler.SafeGetCharAt(i + 2);
97 			i += 1;
98 			continue;
99 		}
100 
101 		if (state == SCE_LISP_DEFAULT) {
102 			if (ch == '#') {
103 				styler.ColourTo(i - 1, state);
104 				radix = -1;
105 				state = SCE_LISP_MACRO_DISPATCH;
106 			} else if (ch == ':' && isLispwordstart(chNext)) {
107 				styler.ColourTo(i - 1, state);
108 				state = SCE_LISP_SYMBOL;
109 			} else if (isLispwordstart(ch)) {
110 				styler.ColourTo(i - 1, state);
111 				state = SCE_LISP_IDENTIFIER;
112 			}
113 			else if (ch == ';') {
114 				styler.ColourTo(i - 1, state);
115 				state = SCE_LISP_COMMENT;
116 			}
117 			else if (isLispoperator(ch) || ch=='\'') {
118 				styler.ColourTo(i - 1, state);
119 				styler.ColourTo(i, SCE_LISP_OPERATOR);
120 				if (ch=='\'' && isLispwordstart(chNext)) {
121 					state = SCE_LISP_SYMBOL;
122 				}
123 			}
124 			else if (ch == '\"') {
125 				styler.ColourTo(i - 1, state);
126 				state = SCE_LISP_STRING;
127 			}
128 		} else if (state == SCE_LISP_IDENTIFIER || state == SCE_LISP_SYMBOL) {
129 			if (!isLispwordstart(ch)) {
130 				if (state == SCE_LISP_IDENTIFIER) {
131 					classifyWordLisp(styler.GetStartSegment(), i - 1, keywords, keywords_kw, styler);
132 				} else {
133 					styler.ColourTo(i - 1, state);
134 				}
135 				state = SCE_LISP_DEFAULT;
136 			} /*else*/
137 			if (isLispoperator(ch) || ch=='\'') {
138 				styler.ColourTo(i - 1, state);
139 				styler.ColourTo(i, SCE_LISP_OPERATOR);
140 				if (ch=='\'' && isLispwordstart(chNext)) {
141 					state = SCE_LISP_SYMBOL;
142 				}
143 			}
144 		} else if (state == SCE_LISP_MACRO_DISPATCH) {
145 			if (!(IsASCII(ch) && isdigit(ch))) {
146 				if (ch != 'r' && ch != 'R' && (i - styler.GetStartSegment()) > 1) {
147 					state = SCE_LISP_DEFAULT;
148 				} else {
149 					switch (ch) {
150 						case '|': state = SCE_LISP_MULTI_COMMENT; break;
151 						case 'o':
152 						case 'O': radix = 8; state = SCE_LISP_MACRO; break;
153 						case 'x':
154 						case 'X': radix = 16; state = SCE_LISP_MACRO; break;
155 						case 'b':
156 						case 'B': radix = 2; state = SCE_LISP_MACRO; break;
157 						case '\\': state = SCE_LISP_CHARACTER; break;
158 						case ':':
159 						case '-':
160 						case '+': state = SCE_LISP_MACRO; break;
161 						case '\'': if (isLispwordstart(chNext)) {
162 								   state = SCE_LISP_SPECIAL;
163 							   } else {
164 								   styler.ColourTo(i - 1, SCE_LISP_DEFAULT);
165 								   styler.ColourTo(i, SCE_LISP_OPERATOR);
166 								   state = SCE_LISP_DEFAULT;
167 							   }
168 							   break;
169 						default: if (isLispoperator(ch)) {
170 								 styler.ColourTo(i - 1, SCE_LISP_DEFAULT);
171 								 styler.ColourTo(i, SCE_LISP_OPERATOR);
172 							 }
173 							 state = SCE_LISP_DEFAULT;
174 							 break;
175 					}
176 				}
177 			}
178 		} else if (state == SCE_LISP_MACRO) {
179 			if (isLispwordstart(ch) && (radix == -1 || IsADigit(ch, radix))) {
180 				state = SCE_LISP_SPECIAL;
181 			} else {
182 				state = SCE_LISP_DEFAULT;
183 			}
184 		} else if (state == SCE_LISP_CHARACTER) {
185 			if (isLispoperator(ch)) {
186 				styler.ColourTo(i, SCE_LISP_SPECIAL);
187 				state = SCE_LISP_DEFAULT;
188 			} else if (isLispwordstart(ch)) {
189 				styler.ColourTo(i, SCE_LISP_SPECIAL);
190 				state = SCE_LISP_SPECIAL;
191 			} else {
192 				state = SCE_LISP_DEFAULT;
193 			}
194 		} else if (state == SCE_LISP_SPECIAL) {
195 			if (!isLispwordstart(ch) || (radix != -1 && !IsADigit(ch, radix))) {
196 				styler.ColourTo(i - 1, state);
197 				state = SCE_LISP_DEFAULT;
198 			}
199 			if (isLispoperator(ch) || ch=='\'') {
200 				styler.ColourTo(i - 1, state);
201 				styler.ColourTo(i, SCE_LISP_OPERATOR);
202 				if (ch=='\'' && isLispwordstart(chNext)) {
203 					state = SCE_LISP_SYMBOL;
204 				}
205 			}
206 		} else {
207 			if (state == SCE_LISP_COMMENT) {
208 				if (atEOL) {
209 					styler.ColourTo(i - 1, state);
210 					state = SCE_LISP_DEFAULT;
211 				}
212 			} else if (state == SCE_LISP_MULTI_COMMENT) {
213 				if (ch == '|' && chNext == '#') {
214 					i++;
215 					chNext = styler.SafeGetCharAt(i + 1);
216 					styler.ColourTo(i, state);
217 					state = SCE_LISP_DEFAULT;
218 				}
219 			} else if (state == SCE_LISP_STRING) {
220 				if (ch == '\\') {
221 					if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
222 						i++;
223 						chNext = styler.SafeGetCharAt(i + 1);
224 					}
225 				} else if (ch == '\"') {
226 					styler.ColourTo(i, state);
227 					state = SCE_LISP_DEFAULT;
228 				}
229 			}
230 		}
231 
232 	}
233 	styler.ColourTo(lengthDoc - 1, state);
234 }
235 
FoldLispDoc(unsigned int startPos,int length,int,WordList * [],Accessor & styler)236 static void FoldLispDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
237                             Accessor &styler) {
238 	unsigned int lengthDoc = startPos + length;
239 	int visibleChars = 0;
240 	int lineCurrent = styler.GetLine(startPos);
241 	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
242 	int levelCurrent = levelPrev;
243 	char chNext = styler[startPos];
244 	int styleNext = styler.StyleAt(startPos);
245 	for (unsigned int i = startPos; i < lengthDoc; i++) {
246 		char ch = chNext;
247 		chNext = styler.SafeGetCharAt(i + 1);
248 		int style = styleNext;
249 		styleNext = styler.StyleAt(i + 1);
250 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
251 		if (style == SCE_LISP_OPERATOR) {
252 			if (ch == '(' || ch == '[' || ch == '{') {
253 				levelCurrent++;
254 			} else if (ch == ')' || ch == ']' || ch == '}') {
255 				levelCurrent--;
256 			}
257 		}
258 		if (atEOL) {
259 			int lev = levelPrev;
260 			if (visibleChars == 0)
261 				lev |= SC_FOLDLEVELWHITEFLAG;
262 			if ((levelCurrent > levelPrev) && (visibleChars > 0))
263 				lev |= SC_FOLDLEVELHEADERFLAG;
264 			if (lev != styler.LevelAt(lineCurrent)) {
265 				styler.SetLevel(lineCurrent, lev);
266 			}
267 			lineCurrent++;
268 			levelPrev = levelCurrent;
269 			visibleChars = 0;
270 		}
271 		if (!isspacechar(ch))
272 			visibleChars++;
273 	}
274 	// Fill in the real level of the next line, keeping the current flags as they will be filled in later
275 	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
276 	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
277 }
278 
279 static const char * const lispWordListDesc[] = {
280 	"Functions and special operators",
281 	"Keywords",
282 	0
283 };
284 
285 LexerModule lmLISP(SCLEX_LISP, ColouriseLispDoc, "lisp", FoldLispDoc, lispWordListDesc);
286