]>
git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/lexers/LexBasic.cxx
1 // Scintilla source code edit control
3 ** Lexer for BlitzBasic and PureBasic.
4 ** Converted to lexer object and added further folding features/properties by "Udo Lechner" <dlchnr(at)gmx(dot)net>
6 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
7 // The License.txt file describes the conditions under which this software may be distributed.
9 // This tries to be a unified Lexer/Folder for all the BlitzBasic/BlitzMax/PurBasic basics
10 // and derivatives. Once they diverge enough, might want to split it into multiple
11 // lexers for more code clearity.
13 // Mail me (elias <at> users <dot> sf <dot> net) for any bugs.
15 // Folding only works for simple things like functions or types.
17 // You may want to have a look at my ctags lexer as well, if you additionally to coloring
18 // and folding need to extract things like label tags in your editor.
31 #include "Scintilla.h"
35 #include "LexAccessor.h"
36 #include "StyleContext.h"
37 #include "CharacterSet.h"
38 #include "LexerModule.h"
39 #include "OptionSet.h"
42 using namespace Scintilla
;
53 static int character_classification
[128] =
55 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
56 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
57 1, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 2,
58 60, 60, 28, 28, 28, 28, 28, 28, 28, 28, 2, 2, 2, 2, 2, 2,
59 2, 20, 20, 20, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4,
60 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 4,
61 2, 20, 20, 20, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4,
62 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 0
65 static bool IsSpace(int c
) {
66 return c
< 128 && (character_classification
[c
] & 1);
69 static bool IsOperator(int c
) {
70 return c
< 128 && (character_classification
[c
] & 2);
73 static bool IsIdentifier(int c
) {
74 return c
< 128 && (character_classification
[c
] & 4);
77 static bool IsDigit(int c
) {
78 return c
< 128 && (character_classification
[c
] & 8);
81 static bool IsHexDigit(int c
) {
82 return c
< 128 && (character_classification
[c
] & 16);
85 static bool IsBinDigit(int c
) {
86 return c
< 128 && (character_classification
[c
] & 32);
89 static int LowerCase(int c
)
91 if (c
>= 'A' && c
<= 'Z')
96 static int CheckBlitzFoldPoint(char const *token
, int &level
) {
97 if (!strcmp(token
, "function") ||
98 !strcmp(token
, "type")) {
99 level
|= SC_FOLDLEVELHEADERFLAG
;
102 if (!strcmp(token
, "end function") ||
103 !strcmp(token
, "end type")) {
109 static int CheckPureFoldPoint(char const *token
, int &level
) {
110 if (!strcmp(token
, "procedure") ||
111 !strcmp(token
, "enumeration") ||
112 !strcmp(token
, "interface") ||
113 !strcmp(token
, "structure")) {
114 level
|= SC_FOLDLEVELHEADERFLAG
;
117 if (!strcmp(token
, "endprocedure") ||
118 !strcmp(token
, "endenumeration") ||
119 !strcmp(token
, "endinterface") ||
120 !strcmp(token
, "endstructure")) {
126 static int CheckFreeFoldPoint(char const *token
, int &level
) {
127 if (!strcmp(token
, "function") ||
128 !strcmp(token
, "sub") ||
129 !strcmp(token
, "type")) {
130 level
|= SC_FOLDLEVELHEADERFLAG
;
133 if (!strcmp(token
, "end function") ||
134 !strcmp(token
, "end sub") ||
135 !strcmp(token
, "end type")) {
141 // An individual named option for use in an OptionSet
143 // Options used for LexerBasic
144 struct OptionsBasic
{
146 bool foldSyntaxBased
;
147 bool foldCommentExplicit
;
148 std::string foldExplicitStart
;
149 std::string foldExplicitEnd
;
150 bool foldExplicitAnywhere
;
154 foldSyntaxBased
= true;
155 foldCommentExplicit
= false;
156 foldExplicitStart
= "";
157 foldExplicitEnd
= "";
158 foldExplicitAnywhere
= false;
163 static const char * const blitzbasicWordListDesc
[] = {
164 "BlitzBasic Keywords",
171 static const char * const purebasicWordListDesc
[] = {
172 "PureBasic Keywords",
173 "PureBasic PreProcessor Keywords",
179 static const char * const freebasicWordListDesc
[] = {
180 "FreeBasic Keywords",
181 "FreeBasic PreProcessor Keywords",
187 struct OptionSetBasic
: public OptionSet
<OptionsBasic
> {
188 OptionSetBasic(const char * const wordListDescriptions
[]) {
189 DefineProperty("fold", &OptionsBasic::fold
);
191 DefineProperty("fold.basic.syntax.based", &OptionsBasic::foldSyntaxBased
,
192 "Set this property to 0 to disable syntax based folding.");
194 DefineProperty("fold.basic.comment.explicit", &OptionsBasic::foldCommentExplicit
,
195 "This option enables folding explicit fold points when using the Basic lexer. "
196 "Explicit fold points allows adding extra folding by placing a ;{ (BB/PB) or '{ (FB) comment at the start "
197 "and a ;} (BB/PB) or '} (FB) at the end of a section that should be folded.");
199 DefineProperty("fold.basic.explicit.start", &OptionsBasic::foldExplicitStart
,
200 "The string to use for explicit fold start points, replacing the standard ;{ (BB/PB) or '{ (FB).");
202 DefineProperty("fold.basic.explicit.end", &OptionsBasic::foldExplicitEnd
,
203 "The string to use for explicit fold end points, replacing the standard ;} (BB/PB) or '} (FB).");
205 DefineProperty("fold.basic.explicit.anywhere", &OptionsBasic::foldExplicitAnywhere
,
206 "Set this property to 1 to enable explicit fold points anywhere, not just in line comments.");
208 DefineProperty("fold.compact", &OptionsBasic::foldCompact
);
210 DefineWordListSets(wordListDescriptions
);
214 class LexerBasic
: public ILexer
{
216 int (*CheckFoldPoint
)(char const *, int &);
217 WordList keywordlists
[4];
218 OptionsBasic options
;
219 OptionSetBasic osBasic
;
221 LexerBasic(char comment_char_
, int (*CheckFoldPoint_
)(char const *, int &), const char * const wordListDescriptions
[]) :
222 comment_char(comment_char_
),
223 CheckFoldPoint(CheckFoldPoint_
),
224 osBasic(wordListDescriptions
) {
226 virtual ~LexerBasic() {
228 void SCI_METHOD
Release() {
231 int SCI_METHOD
Version() const {
234 const char * SCI_METHOD
PropertyNames() {
235 return osBasic
.PropertyNames();
237 int SCI_METHOD
PropertyType(const char *name
) {
238 return osBasic
.PropertyType(name
);
240 const char * SCI_METHOD
DescribeProperty(const char *name
) {
241 return osBasic
.DescribeProperty(name
);
243 int SCI_METHOD
PropertySet(const char *key
, const char *val
);
244 const char * SCI_METHOD
DescribeWordListSets() {
245 return osBasic
.DescribeWordListSets();
247 int SCI_METHOD
WordListSet(int n
, const char *wl
);
248 void SCI_METHOD
Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
249 void SCI_METHOD
Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
251 void * SCI_METHOD
PrivateCall(int, void *) {
254 static ILexer
*LexerFactoryBlitzBasic() {
255 return new LexerBasic(';', CheckBlitzFoldPoint
, blitzbasicWordListDesc
);
257 static ILexer
*LexerFactoryPureBasic() {
258 return new LexerBasic(';', CheckPureFoldPoint
, purebasicWordListDesc
);
260 static ILexer
*LexerFactoryFreeBasic() {
261 return new LexerBasic('\'', CheckFreeFoldPoint
, freebasicWordListDesc
);
265 int SCI_METHOD
LexerBasic::PropertySet(const char *key
, const char *val
) {
266 if (osBasic
.PropertySet(&options
, key
, val
)) {
272 int SCI_METHOD
LexerBasic::WordListSet(int n
, const char *wl
) {
273 WordList
*wordListN
= 0;
276 wordListN
= &keywordlists
[0];
279 wordListN
= &keywordlists
[1];
282 wordListN
= &keywordlists
[2];
285 wordListN
= &keywordlists
[3];
288 int firstModification
= -1;
292 if (*wordListN
!= wlNew
) {
294 firstModification
= 0;
297 return firstModification
;
300 void SCI_METHOD
LexerBasic::Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
301 LexAccessor
styler(pAccess
);
303 bool wasfirst
= true, isfirst
= true; // true if first token in a line
304 styler
.StartAt(startPos
);
306 StyleContext
sc(startPos
, length
, initStyle
, styler
);
308 // Can't use sc.More() here else we miss the last character
309 for (; ; sc
.Forward()) {
310 if (sc
.state
== SCE_B_IDENTIFIER
) {
311 if (!IsIdentifier(sc
.ch
)) {
313 if (wasfirst
&& sc
.Match(':')) {
314 sc
.ChangeState(SCE_B_LABEL
);
315 sc
.ForwardSetState(SCE_B_DEFAULT
);
324 sc
.GetCurrentLowered(s
, sizeof(s
));
325 for (int i
= 0; i
< 4; i
++) {
326 if (keywordlists
[i
].InList(s
)) {
327 sc
.ChangeState(kstates
[i
]);
330 // Types, must set them as operator else they will be
331 // matched as number/constant
332 if (sc
.Match('.') || sc
.Match('$') || sc
.Match('%') ||
334 sc
.SetState(SCE_B_OPERATOR
);
336 sc
.SetState(SCE_B_DEFAULT
);
340 } else if (sc
.state
== SCE_B_OPERATOR
) {
341 if (!IsOperator(sc
.ch
) || sc
.Match('#'))
342 sc
.SetState(SCE_B_DEFAULT
);
343 } else if (sc
.state
== SCE_B_LABEL
) {
344 if (!IsIdentifier(sc
.ch
))
345 sc
.SetState(SCE_B_DEFAULT
);
346 } else if (sc
.state
== SCE_B_CONSTANT
) {
347 if (!IsIdentifier(sc
.ch
))
348 sc
.SetState(SCE_B_DEFAULT
);
349 } else if (sc
.state
== SCE_B_NUMBER
) {
351 sc
.SetState(SCE_B_DEFAULT
);
352 } else if (sc
.state
== SCE_B_HEXNUMBER
) {
353 if (!IsHexDigit(sc
.ch
))
354 sc
.SetState(SCE_B_DEFAULT
);
355 } else if (sc
.state
== SCE_B_BINNUMBER
) {
356 if (!IsBinDigit(sc
.ch
))
357 sc
.SetState(SCE_B_DEFAULT
);
358 } else if (sc
.state
== SCE_B_STRING
) {
360 sc
.ForwardSetState(SCE_B_DEFAULT
);
363 sc
.ChangeState(SCE_B_ERROR
);
364 sc
.SetState(SCE_B_DEFAULT
);
366 } else if (sc
.state
== SCE_B_COMMENT
|| sc
.state
== SCE_B_PREPROCESSOR
) {
368 sc
.SetState(SCE_B_DEFAULT
);
375 if (sc
.state
== SCE_B_DEFAULT
|| sc
.state
== SCE_B_ERROR
) {
376 if (isfirst
&& sc
.Match('.')) {
377 sc
.SetState(SCE_B_LABEL
);
378 } else if (isfirst
&& sc
.Match('#')) {
380 sc
.SetState(SCE_B_IDENTIFIER
);
381 } else if (sc
.Match(comment_char
)) {
382 // Hack to make deprecated QBASIC '$Include show
383 // up in freebasic with SCE_B_PREPROCESSOR.
384 if (comment_char
== '\'' && sc
.Match(comment_char
, '$'))
385 sc
.SetState(SCE_B_PREPROCESSOR
);
387 sc
.SetState(SCE_B_COMMENT
);
388 } else if (sc
.Match('"')) {
389 sc
.SetState(SCE_B_STRING
);
390 } else if (IsDigit(sc
.ch
)) {
391 sc
.SetState(SCE_B_NUMBER
);
392 } else if (sc
.Match('$')) {
393 sc
.SetState(SCE_B_HEXNUMBER
);
394 } else if (sc
.Match('%')) {
395 sc
.SetState(SCE_B_BINNUMBER
);
396 } else if (sc
.Match('#')) {
397 sc
.SetState(SCE_B_CONSTANT
);
398 } else if (IsOperator(sc
.ch
)) {
399 sc
.SetState(SCE_B_OPERATOR
);
400 } else if (IsIdentifier(sc
.ch
)) {
402 sc
.SetState(SCE_B_IDENTIFIER
);
403 } else if (!IsSpace(sc
.ch
)) {
404 sc
.SetState(SCE_B_ERROR
);
418 void SCI_METHOD
LexerBasic::Fold(unsigned int startPos
, int length
, int /* initStyle */, IDocument
*pAccess
) {
423 LexAccessor
styler(pAccess
);
425 int line
= styler
.GetLine(startPos
);
426 int level
= styler
.LevelAt(line
);
427 int go
= 0, done
= 0;
428 int endPos
= startPos
+ length
;
431 const bool userDefinedFoldMarkers
= !options
.foldExplicitStart
.empty() && !options
.foldExplicitEnd
.empty();
432 int cNext
= styler
[startPos
];
434 // Scan for tokens at the start of the line (they may include
435 // whitespace, for tokens like "End Function"
436 for (int i
= startPos
; i
< endPos
; i
++) {
438 cNext
= styler
.SafeGetCharAt(i
+ 1);
439 bool atEOL
= (c
== '\r' && cNext
!= '\n') || (c
== '\n');
440 if (options
.foldSyntaxBased
&& !done
&& !go
) {
441 if (wordlen
) { // are we scanning a token already?
442 word
[wordlen
] = static_cast<char>(LowerCase(c
));
443 if (!IsIdentifier(c
)) { // done with token
444 word
[wordlen
] = '\0';
445 go
= CheckFoldPoint(word
, level
);
447 // Treat any whitespace as single blank, for
448 // things like "End Function".
449 if (IsSpace(c
) && IsIdentifier(word
[wordlen
- 1])) {
454 else // done with this line
457 } else if (wordlen
< 255) {
460 } else { // start scanning at first non-whitespace character
462 if (IsIdentifier(c
)) {
463 word
[0] = static_cast<char>(LowerCase(c
));
465 } else // done with this line
470 if (options
.foldCommentExplicit
&& ((styler
.StyleAt(i
) == SCE_B_COMMENT
) || options
.foldExplicitAnywhere
)) {
471 if (userDefinedFoldMarkers
) {
472 if (styler
.Match(i
, options
.foldExplicitStart
.c_str())) {
473 level
|= SC_FOLDLEVELHEADERFLAG
;
475 } else if (styler
.Match(i
, options
.foldExplicitEnd
.c_str())) {
479 if (c
== comment_char
) {
481 level
|= SC_FOLDLEVELHEADERFLAG
;
483 } else if (cNext
== '}') {
489 if (atEOL
) { // line end
490 if (!done
&& wordlen
== 0 && options
.foldCompact
) // line was only space
491 level
|= SC_FOLDLEVELWHITEFLAG
;
492 if (level
!= styler
.LevelAt(line
))
493 styler
.SetLevel(line
, level
);
498 level
&= ~SC_FOLDLEVELHEADERFLAG
;
499 level
&= ~SC_FOLDLEVELWHITEFLAG
;
506 LexerModule
lmBlitzBasic(SCLEX_BLITZBASIC
, LexerBasic::LexerFactoryBlitzBasic
, "blitzbasic", blitzbasicWordListDesc
);
508 LexerModule
lmPureBasic(SCLEX_PUREBASIC
, LexerBasic::LexerFactoryPureBasic
, "purebasic", purebasicWordListDesc
);
510 LexerModule
lmFreeBasic(SCLEX_FREEBASIC
, LexerBasic::LexerFactoryFreeBasic
, "freebasic", freebasicWordListDesc
);