1 // Scintilla source code edit control
2 /** @file LexOpal.cxx
3  ** Lexer for OPAL (functional language similar to Haskell)
4  ** Written by Sebastian Pipping <webmaster@hartwork.org>
5  **/
6 
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <stdarg.h>
11 #include <assert.h>
12 #include <ctype.h>
13 
14 #include "ILexer.h"
15 #include "Scintilla.h"
16 #include "SciLexer.h"
17 
18 #include "WordList.h"
19 #include "LexAccessor.h"
20 #include "Accessor.h"
21 #include "StyleContext.h"
22 #include "CharacterSet.h"
23 #include "LexerModule.h"
24 
25 using namespace Scintilla;
26 
getRange(Sci_PositionU start,Sci_PositionU end,Accessor & styler,char * s,Sci_PositionU len)27 inline static void getRange( Sci_PositionU start, Sci_PositionU end, Accessor & styler, char * s, Sci_PositionU len )
28 {
29 	Sci_PositionU i = 0;
30 	while( ( i < end - start + 1 ) && ( i < len - 1 ) )
31 	{
32 		s[i] = static_cast<char>( styler[ start + i ] );
33 		i++;
34 	}
35 	s[ i ] = '\0';
36 }
37 
HandleString(Sci_PositionU & cur,Sci_PositionU one_too_much,Accessor & styler)38 inline bool HandleString( Sci_PositionU & cur, Sci_PositionU one_too_much, Accessor & styler )
39 {
40 	char ch;
41 
42 	// Wait for string to close
43 	bool even_backslash_count = true; // Without gaps in between
44 	cur++; // Skip initial quote
45 	for( ; ; )
46 	{
47 		if( cur >= one_too_much )
48 		{
49 			styler.ColourTo( cur - 1, SCE_OPAL_STRING );
50 			return false; // STOP
51 		}
52 
53 		ch = styler.SafeGetCharAt( cur );
54 		if( ( ch == '\015' ) || ( ch == '\012' ) ) // Deny multi-line strings
55 		{
56 			styler.ColourTo( cur - 1, SCE_OPAL_STRING );
57 			styler.StartSegment( cur );
58 			return true;
59 		}
60 		else
61 		{
62 			if( even_backslash_count )
63 			{
64 				if( ch == '"' )
65 				{
66 					styler.ColourTo( cur, SCE_OPAL_STRING );
67 					cur++;
68 					if( cur >= one_too_much )
69 					{
70 						return false; // STOP
71 					}
72 					else
73 					{
74 						styler.StartSegment( cur );
75 						return true;
76 					}
77 				}
78 				else if( ch == '\\' )
79 				{
80 					even_backslash_count = false;
81 				}
82 			}
83 			else
84 			{
85 				even_backslash_count = true;
86 			}
87 		}
88 
89 		cur++;
90 	}
91 }
92 
HandleCommentBlock(Sci_PositionU & cur,Sci_PositionU one_too_much,Accessor & styler,bool could_fail)93 inline bool HandleCommentBlock( Sci_PositionU & cur, Sci_PositionU one_too_much, Accessor & styler, bool could_fail )
94 {
95 	char ch;
96 
97 	if( could_fail )
98 	{
99 		cur++;
100 		if( cur >= one_too_much )
101 		{
102 			styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
103 			return false; // STOP
104 		}
105 
106 		ch = styler.SafeGetCharAt( cur );
107 		if( ch != '*' )
108 		{
109 			styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
110 			styler.StartSegment( cur );
111 			return true;
112 		}
113 	}
114 
115 	// Wait for comment close
116 	cur++;
117 	bool star_found = false;
118 	for( ; ; )
119 	{
120 		if( cur >= one_too_much )
121 		{
122 			styler.ColourTo( cur - 1, SCE_OPAL_COMMENT_BLOCK );
123 			return false; // STOP
124 		}
125 
126 		ch = styler.SafeGetCharAt( cur );
127 		if( star_found )
128 		{
129 			if( ch == '/' )
130 			{
131 				styler.ColourTo( cur, SCE_OPAL_COMMENT_BLOCK );
132 				cur++;
133 				if( cur >= one_too_much )
134 				{
135 					return false; // STOP
136 				}
137 				else
138 				{
139 					styler.StartSegment( cur );
140 					return true;
141 				}
142 			}
143 			else if( ch != '*' )
144 			{
145 				star_found = false;
146 			}
147 		}
148 		else if( ch == '*' )
149 		{
150 			star_found = true;
151 		}
152 		cur++;
153 	}
154 }
155 
HandleCommentLine(Sci_PositionU & cur,Sci_PositionU one_too_much,Accessor & styler,bool could_fail)156 inline bool HandleCommentLine( Sci_PositionU & cur, Sci_PositionU one_too_much, Accessor & styler, bool could_fail )
157 {
158 	char ch;
159 
160 	if( could_fail )
161 	{
162 		cur++;
163 		if( cur >= one_too_much )
164 		{
165 			styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
166 			return false; // STOP
167 		}
168 
169 		ch = styler.SafeGetCharAt( cur );
170 		if( ch != '-' )
171 		{
172 			styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
173 			styler.StartSegment( cur );
174 			return true;
175 		}
176 
177 		cur++;
178 		if( cur >= one_too_much )
179 		{
180 			styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
181 			return false; // STOP
182 		}
183 
184 		ch = styler.SafeGetCharAt( cur );
185 		if( ( ch != ' ' ) && ( ch != '\t' ) )
186 		{
187 			styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
188 			styler.StartSegment( cur );
189 			return true;
190 		}
191 	}
192 
193 	// Wait for end of line
194 	bool fifteen_found = false;
195 
196 	for( ; ; )
197 	{
198 		cur++;
199 
200 		if( cur >= one_too_much )
201 		{
202 			styler.ColourTo( cur - 1, SCE_OPAL_COMMENT_LINE );
203 			return false; // STOP
204 		}
205 
206 		ch = styler.SafeGetCharAt( cur );
207 		if( fifteen_found )
208 		{
209 /*
210 			if( ch == '\012' )
211 			{
212 				// One newline on Windows (015, 012)
213 			}
214 			else
215 			{
216 				// One newline on MAC (015) and another char
217 			}
218 */
219 			cur--;
220 			styler.ColourTo( cur - 1, SCE_OPAL_COMMENT_LINE );
221 			styler.StartSegment( cur );
222 			return true;
223 		}
224 		else
225 		{
226 			if( ch == '\015' )
227 			{
228 				fifteen_found = true;
229 			}
230 			else if( ch == '\012' )
231 			{
232 				// One newline on Linux (012)
233 				styler.ColourTo( cur - 1, SCE_OPAL_COMMENT_LINE );
234 				styler.StartSegment( cur );
235 				return true;
236 			}
237 		}
238 	}
239 }
240 
HandlePar(Sci_PositionU & cur,Accessor & styler)241 inline bool HandlePar( Sci_PositionU & cur, Accessor & styler )
242 {
243 	styler.ColourTo( cur, SCE_OPAL_PAR );
244 
245 	cur++;
246 
247 	styler.StartSegment( cur );
248 	return true;
249 }
250 
HandleSpace(Sci_PositionU & cur,Sci_PositionU one_too_much,Accessor & styler)251 inline bool HandleSpace( Sci_PositionU & cur, Sci_PositionU one_too_much, Accessor & styler )
252 {
253 	char ch;
254 
255 	cur++;
256 	for( ; ; )
257 	{
258 		if( cur >= one_too_much )
259 		{
260 			styler.ColourTo( cur - 1, SCE_OPAL_SPACE );
261 			return false;
262 		}
263 
264 		ch = styler.SafeGetCharAt( cur );
265 		switch( ch )
266 		{
267 		case ' ':
268 		case '\t':
269 		case '\015':
270 		case '\012':
271 			cur++;
272 			break;
273 
274 		default:
275 			styler.ColourTo( cur - 1, SCE_OPAL_SPACE );
276 			styler.StartSegment( cur );
277 			return true;
278 		}
279 	}
280 }
281 
HandleInteger(Sci_PositionU & cur,Sci_PositionU one_too_much,Accessor & styler)282 inline bool HandleInteger( Sci_PositionU & cur, Sci_PositionU one_too_much, Accessor & styler )
283 {
284 	char ch;
285 
286 	for( ; ; )
287 	{
288 		cur++;
289 		if( cur >= one_too_much )
290 		{
291 			styler.ColourTo( cur - 1, SCE_OPAL_INTEGER );
292 			return false; // STOP
293 		}
294 
295 		ch = styler.SafeGetCharAt( cur );
296 		if( !( IsASCII( ch ) && isdigit( ch ) ) )
297 		{
298 			styler.ColourTo( cur - 1, SCE_OPAL_INTEGER );
299 			styler.StartSegment( cur );
300 			return true;
301 		}
302 	}
303 }
304 
HandleWord(Sci_PositionU & cur,Sci_PositionU one_too_much,Accessor & styler,WordList * keywordlists[])305 inline bool HandleWord( Sci_PositionU & cur, Sci_PositionU one_too_much, Accessor & styler, WordList * keywordlists[] )
306 {
307 	char ch;
308 	const Sci_PositionU beg = cur;
309 
310 	cur++;
311 	for( ; ; )
312 	{
313 		ch = styler.SafeGetCharAt( cur );
314 		if( ( ch != '_' ) && ( ch != '-' ) &&
315 			!( IsASCII( ch ) && ( islower( ch ) || isupper( ch ) || isdigit( ch ) ) ) ) break;
316 
317 		cur++;
318 		if( cur >= one_too_much )
319 		{
320 			break;
321 		}
322 	}
323 
324 	const Sci_Position ide_len = cur - beg + 1;
325 	char * ide = new char[ ide_len ];
326 	getRange( beg, cur, styler, ide, ide_len );
327 
328 	WordList & keywords    = *keywordlists[ 0 ];
329 	WordList & classwords  = *keywordlists[ 1 ];
330 
331 	if( keywords.InList( ide ) ) // Keyword
332 	{
333 		delete [] ide;
334 
335 		styler.ColourTo( cur - 1, SCE_OPAL_KEYWORD );
336 		if( cur >= one_too_much )
337 		{
338 			return false; // STOP
339 		}
340 		else
341 		{
342 			styler.StartSegment( cur );
343 			return true;
344 		}
345 	}
346 	else if( classwords.InList( ide ) ) // Sort
347 	{
348 		delete [] ide;
349 
350 		styler.ColourTo( cur - 1, SCE_OPAL_SORT );
351 		if( cur >= one_too_much )
352 		{
353 			return false; // STOP
354 		}
355 		else
356 		{
357 			styler.StartSegment( cur );
358 			return true;
359 		}
360 	}
361 	else if( !strcmp( ide, "true" ) || !strcmp( ide, "false" ) ) // Bool const
362 	{
363 		delete [] ide;
364 
365 		styler.ColourTo( cur - 1, SCE_OPAL_BOOL_CONST );
366 		if( cur >= one_too_much )
367 		{
368 			return false; // STOP
369 		}
370 		else
371 		{
372 			styler.StartSegment( cur );
373 			return true;
374 		}
375 	}
376 	else // Unknown keyword
377 	{
378 		delete [] ide;
379 
380 		styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
381 		if( cur >= one_too_much )
382 		{
383 			return false; // STOP
384 		}
385 		else
386 		{
387 			styler.StartSegment( cur );
388 			return true;
389 		}
390 	}
391 
392 }
393 
HandleSkip(Sci_PositionU & cur,Sci_PositionU one_too_much,Accessor & styler)394 inline bool HandleSkip( Sci_PositionU & cur, Sci_PositionU one_too_much, Accessor & styler )
395 {
396 	cur++;
397 	styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
398 	if( cur >= one_too_much )
399 	{
400 		return false; // STOP
401 	}
402 	else
403 	{
404 		styler.StartSegment( cur );
405 		return true;
406 	}
407 }
408 
ColouriseOpalDoc(Sci_PositionU startPos,Sci_Position length,int initStyle,WordList * keywordlists[],Accessor & styler)409 static void ColouriseOpalDoc( Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], Accessor & styler )
410 {
411 	styler.StartAt( startPos );
412 	styler.StartSegment( startPos );
413 
414 	Sci_PositionU & cur = startPos;
415 	const Sci_PositionU one_too_much = startPos + length;
416 
417 	int state = initStyle;
418 
419 	for( ; ; )
420 	{
421 		switch( state )
422 		{
423 		case SCE_OPAL_KEYWORD:
424 		case SCE_OPAL_SORT:
425 			if( !HandleWord( cur, one_too_much, styler, keywordlists ) ) return;
426 			state = SCE_OPAL_DEFAULT;
427 			break;
428 
429 		case SCE_OPAL_INTEGER:
430 			if( !HandleInteger( cur, one_too_much, styler ) ) return;
431 			state = SCE_OPAL_DEFAULT;
432 			break;
433 
434 		case SCE_OPAL_COMMENT_BLOCK:
435 			if( !HandleCommentBlock( cur, one_too_much, styler, false ) ) return;
436 			state = SCE_OPAL_DEFAULT;
437 			break;
438 
439 		case SCE_OPAL_COMMENT_LINE:
440 			if( !HandleCommentLine( cur, one_too_much, styler, false ) ) return;
441 			state = SCE_OPAL_DEFAULT;
442 			break;
443 
444 		case SCE_OPAL_STRING:
445 			if( !HandleString( cur, one_too_much, styler ) ) return;
446 			state = SCE_OPAL_DEFAULT;
447 			break;
448 
449 		default: // SCE_OPAL_DEFAULT:
450 			{
451 				char ch = styler.SafeGetCharAt( cur );
452 
453 				switch( ch )
454 				{
455 				// String
456 				case '"':
457 					if( !HandleString( cur, one_too_much, styler ) ) return;
458 					break;
459 
460 				// Comment block
461 				case '/':
462 					if( !HandleCommentBlock( cur, one_too_much, styler, true ) ) return;
463 					break;
464 
465 				// Comment line
466 				case '-':
467 					if( !HandleCommentLine( cur, one_too_much, styler, true ) ) return;
468 					break;
469 
470 				// Par
471 				case '(':
472 				case ')':
473 				case '[':
474 				case ']':
475 				case '{':
476 				case '}':
477 					if( !HandlePar( cur, styler ) ) return;
478 					break;
479 
480 				// Whitespace
481 				case ' ':
482 				case '\t':
483 				case '\015':
484 				case '\012':
485 					if( !HandleSpace( cur, one_too_much, styler ) ) return;
486 					break;
487 
488 				default:
489 					{
490 						// Integer
491 						if( IsASCII( ch ) && isdigit( ch ) )
492 						{
493 							if( !HandleInteger( cur, one_too_much, styler ) ) return;
494 						}
495 
496 						// Keyword
497 						else if( IsASCII( ch ) && ( islower( ch ) || isupper( ch ) ) )
498 						{
499 							if( !HandleWord( cur, one_too_much, styler, keywordlists ) ) return;
500 
501 						}
502 
503 						// Skip
504 						else
505 						{
506 							if( !HandleSkip( cur, one_too_much, styler ) ) return;
507 						}
508 					}
509 				}
510 
511 				break;
512 			}
513 		}
514 	}
515 }
516 
517 static const char * const opalWordListDesc[] = {
518 	"Keywords",
519 	"Sorts",
520 	0
521 };
522 
523 LexerModule lmOpal(SCLEX_OPAL, ColouriseOpalDoc, "opal", NULL, opalWordListDesc);
524