1 // Scintilla source code edit control
2 /** @file LexInno.cxx
3  ** Lexer for Inno Setup scripts.
4  **/
5 // Written by Friedrich Vedder <fvedd@t-online.de>, using code from LexOthers.cxx.
6 // The License.txt file describes the conditions under which this software may be distributed.
7 
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <assert.h>
13 #include <ctype.h>
14 
15 #include "ILexer.h"
16 #include "Scintilla.h"
17 #include "SciLexer.h"
18 
19 #include "WordList.h"
20 #include "LexAccessor.h"
21 #include "Accessor.h"
22 #include "StyleContext.h"
23 #include "CharacterSet.h"
24 #include "LexerModule.h"
25 
26 using namespace Scintilla;
27 
ColouriseInnoDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * keywordLists[],Accessor & styler)28 static void ColouriseInnoDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *keywordLists[], Accessor &styler) {
29 	int state = SCE_INNO_DEFAULT;
30 	char chPrev;
31 	char ch = 0;
32 	char chNext = styler[startPos];
33 	Sci_Position lengthDoc = startPos + length;
34 	char *buffer = new char[length+1];
35 	Sci_Position bufferCount = 0;
36 	bool isBOL, isEOL, isWS, isBOLWS = 0;
37 	bool isCStyleComment = false;
38 
39 	WordList &sectionKeywords = *keywordLists[0];
40 	WordList &standardKeywords = *keywordLists[1];
41 	WordList &parameterKeywords = *keywordLists[2];
42 	WordList &preprocessorKeywords = *keywordLists[3];
43 	WordList &pascalKeywords = *keywordLists[4];
44 	WordList &userKeywords = *keywordLists[5];
45 
46 	Sci_Position curLine = styler.GetLine(startPos);
47 	int curLineState = curLine > 0 ? styler.GetLineState(curLine - 1) : 0;
48 	bool isCode = (curLineState == 1);
49 
50 	// Go through all provided text segment
51 	// using the hand-written state machine shown below
52 	styler.StartAt(startPos);
53 	styler.StartSegment(startPos);
54 	for (Sci_Position i = startPos; i < lengthDoc; i++) {
55 		chPrev = ch;
56 		ch = chNext;
57 		chNext = styler.SafeGetCharAt(i + 1);
58 
59 		if (styler.IsLeadByte(ch)) {
60 			chNext = styler.SafeGetCharAt(i + 2);
61 			i++;
62 			continue;
63 		}
64 
65 		isBOL = (chPrev == 0) || (chPrev == '\n') || (chPrev == '\r' && ch != '\n');
66 		isBOLWS = (isBOL) ? 1 : (isBOLWS && (chPrev == ' ' || chPrev == '\t'));
67 		isEOL = (ch == '\n' || ch == '\r');
68 		isWS = (ch == ' ' || ch == '\t');
69 
70 		if ((ch == '\r' && chNext != '\n') || (ch == '\n')) {
71 			// Remember the line state for future incremental lexing
72 			curLine = styler.GetLine(i);
73 			styler.SetLineState(curLine, (isCode ? 1 : 0));
74 		}
75 
76 		switch(state) {
77 			case SCE_INNO_DEFAULT:
78 				if (!isCode && ch == ';' && isBOLWS) {
79 					// Start of a comment
80 					state = SCE_INNO_COMMENT;
81 				} else if (ch == '[' && isBOLWS) {
82 					// Start of a section name
83 					bufferCount = 0;
84 					state = SCE_INNO_SECTION;
85 				} else if (ch == '#' && isBOLWS) {
86 					// Start of a preprocessor directive
87 					state = SCE_INNO_PREPROC;
88 				} else if (!isCode && ch == '{' && chNext != '{' && chPrev != '{') {
89 					// Start of an inline expansion
90 					state = SCE_INNO_INLINE_EXPANSION;
91 				} else if (isCode && (ch == '{' || (ch == '(' && chNext == '*'))) {
92 					// Start of a Pascal comment
93 					state = SCE_INNO_COMMENT_PASCAL;
94 					isCStyleComment = false;
95 				} else if (isCode && ch == '/' && chNext == '/') {
96 					// Apparently, C-style comments are legal, too
97 					state = SCE_INNO_COMMENT_PASCAL;
98 					isCStyleComment = true;
99 				} else if (ch == '"') {
100 					// Start of a double-quote string
101 					state = SCE_INNO_STRING_DOUBLE;
102 				} else if (ch == '\'') {
103 					// Start of a single-quote string
104 					state = SCE_INNO_STRING_SINGLE;
105 				} else if (IsASCII(ch) && (isalpha(ch) || (ch == '_'))) {
106 					// Start of an identifier
107 					bufferCount = 0;
108 					buffer[bufferCount++] = static_cast<char>(tolower(ch));
109 					state = SCE_INNO_IDENTIFIER;
110 				} else {
111 					// Style it the default style
112 					styler.ColourTo(i,SCE_INNO_DEFAULT);
113 				}
114 				break;
115 
116 			case SCE_INNO_COMMENT:
117 				if (isEOL) {
118 					state = SCE_INNO_DEFAULT;
119 					styler.ColourTo(i,SCE_INNO_COMMENT);
120 				}
121 				break;
122 
123 			case SCE_INNO_IDENTIFIER:
124 				if (IsASCII(ch) && (isalnum(ch) || (ch == '_'))) {
125 					buffer[bufferCount++] = static_cast<char>(tolower(ch));
126 				} else {
127 					state = SCE_INNO_DEFAULT;
128 					buffer[bufferCount] = '\0';
129 
130 					// Check if the buffer contains a keyword
131 					if (!isCode && standardKeywords.InList(buffer)) {
132 						styler.ColourTo(i-1,SCE_INNO_KEYWORD);
133 					} else if (!isCode && parameterKeywords.InList(buffer)) {
134 						styler.ColourTo(i-1,SCE_INNO_PARAMETER);
135 					} else if (isCode && pascalKeywords.InList(buffer)) {
136 						styler.ColourTo(i-1,SCE_INNO_KEYWORD_PASCAL);
137 					} else if (!isCode && userKeywords.InList(buffer)) {
138 						styler.ColourTo(i-1,SCE_INNO_KEYWORD_USER);
139 					} else {
140 						styler.ColourTo(i-1,SCE_INNO_DEFAULT);
141 					}
142 
143 					// Push back the faulty character
144 					chNext = styler[i--];
145 					ch = chPrev;
146 				}
147 				break;
148 
149 			case SCE_INNO_SECTION:
150 				if (ch == ']') {
151 					state = SCE_INNO_DEFAULT;
152 					buffer[bufferCount] = '\0';
153 
154 					// Check if the buffer contains a section name
155 					if (sectionKeywords.InList(buffer)) {
156 						styler.ColourTo(i,SCE_INNO_SECTION);
157 						isCode = !CompareCaseInsensitive(buffer, "code");
158 					} else {
159 						styler.ColourTo(i,SCE_INNO_DEFAULT);
160 					}
161 				} else if (IsASCII(ch) && (isalnum(ch) || (ch == '_'))) {
162 					buffer[bufferCount++] = static_cast<char>(tolower(ch));
163 				} else {
164 					state = SCE_INNO_DEFAULT;
165 					styler.ColourTo(i,SCE_INNO_DEFAULT);
166 				}
167 				break;
168 
169 			case SCE_INNO_PREPROC:
170 				if (isWS || isEOL) {
171 					if (IsASCII(chPrev) && isalpha(chPrev)) {
172 						state = SCE_INNO_DEFAULT;
173 						buffer[bufferCount] = '\0';
174 
175 						// Check if the buffer contains a preprocessor directive
176 						if (preprocessorKeywords.InList(buffer)) {
177 							styler.ColourTo(i-1,SCE_INNO_PREPROC);
178 						} else {
179 							styler.ColourTo(i-1,SCE_INNO_DEFAULT);
180 						}
181 
182 						// Push back the faulty character
183 						chNext = styler[i--];
184 						ch = chPrev;
185 					}
186 				} else if (IsASCII(ch) && isalpha(ch)) {
187 					if (chPrev == '#' || chPrev == ' ' || chPrev == '\t')
188 						bufferCount = 0;
189 					buffer[bufferCount++] = static_cast<char>(tolower(ch));
190 				}
191 				break;
192 
193 			case SCE_INNO_STRING_DOUBLE:
194 				if (ch == '"' || isEOL) {
195 					state = SCE_INNO_DEFAULT;
196 					styler.ColourTo(i,SCE_INNO_STRING_DOUBLE);
197 				}
198 				break;
199 
200 			case SCE_INNO_STRING_SINGLE:
201 				if (ch == '\'' || isEOL) {
202 					state = SCE_INNO_DEFAULT;
203 					styler.ColourTo(i,SCE_INNO_STRING_SINGLE);
204 				}
205 				break;
206 
207 			case SCE_INNO_INLINE_EXPANSION:
208 				if (ch == '}') {
209 					state = SCE_INNO_DEFAULT;
210 					styler.ColourTo(i,SCE_INNO_INLINE_EXPANSION);
211 				} else if (isEOL) {
212 					state = SCE_INNO_DEFAULT;
213 					styler.ColourTo(i,SCE_INNO_DEFAULT);
214 				}
215 				break;
216 
217 			case SCE_INNO_COMMENT_PASCAL:
218 				if (isCStyleComment) {
219 					if (isEOL) {
220 						state = SCE_INNO_DEFAULT;
221 						styler.ColourTo(i,SCE_INNO_COMMENT_PASCAL);
222 					}
223 				} else {
224 					if (ch == '}' || (ch == ')' && chPrev == '*')) {
225 						state = SCE_INNO_DEFAULT;
226 						styler.ColourTo(i,SCE_INNO_COMMENT_PASCAL);
227 					} else if (isEOL) {
228 						state = SCE_INNO_DEFAULT;
229 						styler.ColourTo(i,SCE_INNO_DEFAULT);
230 					}
231 				}
232 				break;
233 
234 		}
235 	}
236 	delete []buffer;
237 }
238 
239 static const char * const innoWordListDesc[] = {
240 	"Sections",
241 	"Keywords",
242 	"Parameters",
243 	"Preprocessor directives",
244 	"Pascal keywords",
245 	"User defined keywords",
246 	0
247 };
248 
FoldInnoDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)249 static void FoldInnoDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) {
250 	Sci_PositionU endPos = startPos + length;
251 	char chNext = styler[startPos];
252 
253 	Sci_Position lineCurrent = styler.GetLine(startPos);
254 
255 	bool sectionFlag = false;
256 	int levelPrev = lineCurrent > 0 ? styler.LevelAt(lineCurrent - 1) : SC_FOLDLEVELBASE;
257 	int level;
258 
259 	for (Sci_PositionU i = startPos; i < endPos; i++) {
260 		char ch = chNext;
261 		chNext = styler[i+1];
262 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
263 		int style = styler.StyleAt(i);
264 
265 		if (style == SCE_INNO_SECTION)
266 			sectionFlag = true;
267 
268 		if (atEOL || i == endPos - 1) {
269 			if (sectionFlag) {
270 				level = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
271 				if (level == levelPrev)
272 					styler.SetLevel(lineCurrent - 1, levelPrev & ~SC_FOLDLEVELHEADERFLAG);
273 			} else {
274 				level = levelPrev & SC_FOLDLEVELNUMBERMASK;
275 				if (levelPrev & SC_FOLDLEVELHEADERFLAG)
276 					level++;
277 			}
278 
279 			styler.SetLevel(lineCurrent, level);
280 
281 			levelPrev = level;
282 			lineCurrent++;
283 			sectionFlag = false;
284 		}
285 	}
286 }
287 
288 LexerModule lmInno(SCLEX_INNOSETUP, ColouriseInnoDoc, "inno", FoldInnoDoc, innoWordListDesc);
289