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