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