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