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