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