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>
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
IsGAPOperator(char ch)31 static inline bool IsGAPOperator(char ch) {
32 if (isascii(ch) && isalnum(ch)) return false;
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
GetRange(unsigned int start,unsigned int end,Accessor & styler,char * s,unsigned int len)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
ColouriseGAPDoc(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler)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
ClassifyFoldPointGAP(const char * s)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
FoldGAPDoc(unsigned int startPos,int length,int initStyle,WordList **,Accessor & styler)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);
267