]>
Commit | Line | Data |
---|---|---|
7e0c58e9 RD |
1 | // Scintilla source code edit control |
2 | /** @file LexGAP.cxx | |
3 | ** Lexer for the GAP language. (The GAP System for Computational Discrete Algebra) | |
4 | ** http://www.gap-system.org | |
5 | **/ | |
6 | // Copyright 2007 by Istvan Szollosi ( szteven <at> gmail <dot> com ) | |
7 | // The License.txt file describes the conditions under which this software may be distributed. | |
8 | ||
9 | #include <stdlib.h> | |
10 | #include <string.h> | |
7e0c58e9 RD |
11 | #include <stdio.h> |
12 | #include <stdarg.h> | |
1dcf666d RD |
13 | #include <assert.h> |
14 | #include <ctype.h> | |
7e0c58e9 | 15 | |
1dcf666d RD |
16 | #include "ILexer.h" |
17 | #include "Scintilla.h" | |
18 | #include "SciLexer.h" | |
7e0c58e9 | 19 | |
1dcf666d RD |
20 | #include "WordList.h" |
21 | #include "LexAccessor.h" | |
7e0c58e9 RD |
22 | #include "Accessor.h" |
23 | #include "StyleContext.h" | |
1dcf666d RD |
24 | #include "CharacterSet.h" |
25 | #include "LexerModule.h" | |
7e0c58e9 RD |
26 | |
27 | #ifdef SCI_NAMESPACE | |
28 | using namespace Scintilla; | |
29 | #endif | |
30 | ||
31 | static inline bool IsGAPOperator(char ch) { | |
1dcf666d | 32 | if (isascii(ch) && isalnum(ch)) return false; |
7e0c58e9 RD |
33 | if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || |
34 | ch == '^' || ch == ',' || ch == '!' || ch == '.' || | |
35 | ch == '=' || ch == '<' || ch == '>' || ch == '(' || | |
36 | ch == ')' || ch == ';' || ch == '[' || ch == ']' || | |
37 | ch == '{' || ch == '}' || ch == ':' ) | |
38 | return true; | |
39 | return false; | |
40 | } | |
41 | ||
42 | static void GetRange(unsigned int start, unsigned int end, Accessor &styler, char *s, unsigned int len) { | |
43 | unsigned int i = 0; | |
44 | while ((i < end - start + 1) && (i < len-1)) { | |
45 | s[i] = static_cast<char>(styler[start + i]); | |
46 | i++; | |
47 | } | |
48 | s[i] = '\0'; | |
49 | } | |
50 | ||
51 | static void ColouriseGAPDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], Accessor &styler) { | |
52 | ||
53 | WordList &keywords1 = *keywordlists[0]; | |
54 | WordList &keywords2 = *keywordlists[1]; | |
55 | WordList &keywords3 = *keywordlists[2]; | |
56 | WordList &keywords4 = *keywordlists[3]; | |
57 | ||
58 | // Do not leak onto next line | |
59 | if (initStyle == SCE_GAP_STRINGEOL) initStyle = SCE_GAP_DEFAULT; | |
60 | ||
61 | StyleContext sc(startPos, length, initStyle, styler); | |
62 | ||
63 | for (; sc.More(); sc.Forward()) { | |
64 | ||
65 | // Prevent SCE_GAP_STRINGEOL from leaking back to previous line | |
66 | if ( sc.atLineStart ) { | |
67 | if (sc.state == SCE_GAP_STRING) sc.SetState(SCE_GAP_STRING); | |
68 | if (sc.state == SCE_GAP_CHAR) sc.SetState(SCE_GAP_CHAR); | |
69 | } | |
70 | ||
71 | // Handle line continuation generically | |
72 | if (sc.ch == '\\' ) { | |
73 | if (sc.chNext == '\n' || sc.chNext == '\r') { | |
74 | sc.Forward(); | |
75 | if (sc.ch == '\r' && sc.chNext == '\n') { | |
76 | sc.Forward(); | |
77 | } | |
78 | continue; | |
79 | } | |
80 | } | |
81 | ||
82 | // Determine if the current state should terminate | |
83 | switch (sc.state) { | |
84 | case SCE_GAP_OPERATOR : | |
85 | sc.SetState(SCE_GAP_DEFAULT); | |
86 | break; | |
87 | ||
88 | case SCE_GAP_NUMBER : | |
89 | if (!IsADigit(sc.ch)) { | |
90 | if (sc.ch == '\\') { | |
91 | if (!sc.atLineEnd) { | |
92 | if (!IsADigit(sc.chNext)) { | |
93 | sc.Forward(); | |
94 | sc.ChangeState(SCE_GAP_IDENTIFIER); | |
95 | } | |
96 | } | |
97 | } else if (isalpha(sc.ch) || sc.ch == '_') { | |
98 | sc.ChangeState(SCE_GAP_IDENTIFIER); | |
99 | } | |
100 | else sc.SetState(SCE_GAP_DEFAULT); | |
101 | } | |
102 | break; | |
103 | ||
104 | case SCE_GAP_IDENTIFIER : | |
105 | if (!(iswordstart(static_cast<char>(sc.ch)) || sc.ch == '$')) { | |
106 | if (sc.ch == '\\') sc.Forward(); | |
107 | else { | |
108 | char s[1000]; | |
109 | sc.GetCurrent(s, sizeof(s)); | |
110 | if (keywords1.InList(s)) { | |
111 | sc.ChangeState(SCE_GAP_KEYWORD); | |
112 | } else if (keywords2.InList(s)) { | |
113 | sc.ChangeState(SCE_GAP_KEYWORD2); | |
114 | } else if (keywords3.InList(s)) { | |
115 | sc.ChangeState(SCE_GAP_KEYWORD3); | |
116 | } else if (keywords4.InList(s)) { | |
117 | sc.ChangeState(SCE_GAP_KEYWORD4); | |
118 | } | |
119 | sc.SetState(SCE_GAP_DEFAULT); | |
120 | } | |
121 | } | |
122 | break; | |
123 | ||
124 | case SCE_GAP_COMMENT : | |
125 | if (sc.atLineEnd) { | |
126 | sc.SetState(SCE_GAP_DEFAULT); | |
127 | } | |
128 | break; | |
129 | ||
130 | case SCE_GAP_STRING: | |
131 | if (sc.atLineEnd) { | |
132 | sc.ChangeState(SCE_GAP_STRINGEOL); | |
133 | } else if (sc.ch == '\\') { | |
134 | if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { | |
135 | sc.Forward(); | |
136 | } | |
137 | } else if (sc.ch == '\"') { | |
138 | sc.ForwardSetState(SCE_GAP_DEFAULT); | |
139 | } | |
140 | break; | |
141 | ||
142 | case SCE_GAP_CHAR: | |
143 | if (sc.atLineEnd) { | |
144 | sc.ChangeState(SCE_GAP_STRINGEOL); | |
145 | } else if (sc.ch == '\\') { | |
146 | if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { | |
147 | sc.Forward(); | |
148 | } | |
149 | } else if (sc.ch == '\'') { | |
150 | sc.ForwardSetState(SCE_GAP_DEFAULT); | |
151 | } | |
152 | break; | |
153 | ||
154 | case SCE_GAP_STRINGEOL: | |
155 | if (sc.atLineStart) { | |
156 | sc.SetState(SCE_GAP_DEFAULT); | |
157 | } | |
158 | break; | |
159 | } | |
160 | ||
161 | // Determine if a new state should be entered | |
162 | if (sc.state == SCE_GAP_DEFAULT) { | |
163 | if (IsGAPOperator(static_cast<char>(sc.ch))) { | |
164 | sc.SetState(SCE_GAP_OPERATOR); | |
165 | } | |
166 | else if (IsADigit(sc.ch)) { | |
167 | sc.SetState(SCE_GAP_NUMBER); | |
168 | } else if (isalpha(sc.ch) || sc.ch == '_' || sc.ch == '\\' || sc.ch == '$' || sc.ch == '~') { | |
169 | sc.SetState(SCE_GAP_IDENTIFIER); | |
170 | if (sc.ch == '\\') sc.Forward(); | |
171 | } else if (sc.ch == '#') { | |
172 | sc.SetState(SCE_GAP_COMMENT); | |
173 | } else if (sc.ch == '\"') { | |
174 | sc.SetState(SCE_GAP_STRING); | |
175 | } else if (sc.ch == '\'') { | |
176 | sc.SetState(SCE_GAP_CHAR); | |
177 | } | |
178 | } | |
179 | ||
180 | } | |
181 | sc.Complete(); | |
182 | } | |
183 | ||
184 | static int ClassifyFoldPointGAP(const char* s) { | |
185 | int level = 0; | |
186 | if (strcmp(s, "function") == 0 || | |
187 | strcmp(s, "do") == 0 || | |
188 | strcmp(s, "if") == 0 || | |
189 | strcmp(s, "repeat") == 0 ) { | |
190 | level = 1; | |
191 | } else if (strcmp(s, "end") == 0 || | |
192 | strcmp(s, "od") == 0 || | |
193 | strcmp(s, "fi") == 0 || | |
194 | strcmp(s, "until") == 0 ) { | |
195 | level = -1; | |
196 | } | |
197 | return level; | |
198 | } | |
199 | ||
200 | static void FoldGAPDoc( unsigned int startPos, int length, int initStyle, WordList** , Accessor &styler) { | |
201 | unsigned int endPos = startPos + length; | |
202 | int visibleChars = 0; | |
203 | int lineCurrent = styler.GetLine(startPos); | |
204 | int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; | |
205 | int levelCurrent = levelPrev; | |
206 | char chNext = styler[startPos]; | |
207 | int styleNext = styler.StyleAt(startPos); | |
208 | int style = initStyle; | |
209 | ||
210 | int lastStart = 0; | |
211 | ||
212 | for (unsigned int i = startPos; i < endPos; i++) { | |
213 | char ch = chNext; | |
214 | chNext = styler.SafeGetCharAt(i + 1); | |
215 | int stylePrev = style; | |
216 | style = styleNext; | |
217 | styleNext = styler.StyleAt(i + 1); | |
218 | bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); | |
219 | ||
220 | if (stylePrev != SCE_GAP_KEYWORD && style == SCE_GAP_KEYWORD) { | |
221 | // Store last word start point. | |
222 | lastStart = i; | |
223 | } | |
224 | ||
225 | if (stylePrev == SCE_GAP_KEYWORD) { | |
226 | if(iswordchar(ch) && !iswordchar(chNext)) { | |
227 | char s[100]; | |
228 | GetRange(lastStart, i, styler, s, sizeof(s)); | |
229 | levelCurrent += ClassifyFoldPointGAP(s); | |
230 | } | |
231 | } | |
232 | ||
233 | if (atEOL) { | |
234 | int lev = levelPrev; | |
235 | if ((levelCurrent > levelPrev) && (visibleChars > 0)) | |
236 | lev |= SC_FOLDLEVELHEADERFLAG; | |
237 | if (lev != styler.LevelAt(lineCurrent)) { | |
238 | styler.SetLevel(lineCurrent, lev); | |
239 | } | |
240 | lineCurrent++; | |
241 | levelPrev = levelCurrent; | |
242 | visibleChars = 0; | |
243 | } | |
244 | ||
245 | if (!isspacechar(ch)) | |
246 | visibleChars++; | |
247 | } | |
248 | ||
249 | int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; | |
250 | styler.SetLevel(lineCurrent, levelPrev | flagsNext); | |
251 | } | |
252 | ||
253 | static const char * const GAPWordListDesc[] = { | |
254 | "Keywords 1", | |
255 | "Keywords 2", | |
256 | "Keywords 3 (unused)", | |
257 | "Keywords 4 (unused)", | |
258 | 0 | |
259 | }; | |
260 | ||
261 | LexerModule lmGAP( | |
262 | SCLEX_GAP, | |
263 | ColouriseGAPDoc, | |
264 | "gap", | |
265 | FoldGAPDoc, | |
266 | GAPWordListDesc); |