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