1
// Scintilla source code edit control
3
** Lexer for BlitzBasic and PureBasic.
5
// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6
// The License.txt file describes the conditions under which this software may be distributed.
8
// This tries to be a unified Lexer/Folder for all the BlitzBasic/BlitzMax/PurBasic basics
9
// and derivatives. Once they diverge enough, might want to split it into multiple
10
// lexers for more code clearity.
12
// Mail me (elias <at> users <dot> sf <dot> net) for any bugs.
14
// Folding only works for simple things like functions or types.
16
// You may want to have a look at my ctags lexer as well, if you additionally to coloring
17
// and folding need to extract things like label tags in your editor.
26
#include "Scintilla.h"
29
#include "PropSetSimple.h"
31
#include "LexAccessor.h"
33
#include "StyleContext.h"
34
#include "CharacterSet.h"
35
#include "LexerModule.h"
38
using namespace Scintilla;
49
static int character_classification[128] =
51
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
52
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53
1, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 2,
54
60, 60, 28, 28, 28, 28, 28, 28, 28, 28, 2, 2, 2, 2, 2, 2,
55
2, 20, 20, 20, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4,
56
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 4,
57
2, 20, 20, 20, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4,
58
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 0
61
static bool IsSpace(int c) {
62
return c < 128 && (character_classification[c] & 1);
65
static bool IsOperator(int c) {
66
return c < 128 && (character_classification[c] & 2);
69
static bool IsIdentifier(int c) {
70
return c < 128 && (character_classification[c] & 4);
73
static bool IsDigit(int c) {
74
return c < 128 && (character_classification[c] & 8);
77
static bool IsHexDigit(int c) {
78
return c < 128 && (character_classification[c] & 16);
81
static bool IsBinDigit(int c) {
82
return c < 128 && (character_classification[c] & 32);
85
static int LowerCase(int c)
87
if (c >= 'A' && c <= 'Z')
92
static void ColouriseBasicDoc(unsigned int startPos, int length, int initStyle,
93
WordList *keywordlists[], Accessor &styler, char comment_char) {
94
bool wasfirst = true, isfirst = true; // true if first token in a line
95
styler.StartAt(startPos);
97
StyleContext sc(startPos, length, initStyle, styler);
99
// Can't use sc.More() here else we miss the last character
100
for (; ; sc.Forward()) {
101
if (sc.state == SCE_B_IDENTIFIER) {
102
if (!IsIdentifier(sc.ch)) {
104
if (wasfirst && sc.Match(':')) {
105
sc.ChangeState(SCE_B_LABEL);
106
sc.ForwardSetState(SCE_B_DEFAULT);
115
sc.GetCurrentLowered(s, sizeof(s));
116
for (int i = 0; i < 4; i++) {
117
if (keywordlists[i]->InList(s)) {
118
sc.ChangeState(kstates[i]);
121
// Types, must set them as operator else they will be
122
// matched as number/constant
123
if (sc.Match('.') || sc.Match('$') || sc.Match('%') ||
125
sc.SetState(SCE_B_OPERATOR);
127
sc.SetState(SCE_B_DEFAULT);
131
} else if (sc.state == SCE_B_OPERATOR) {
132
if (!IsOperator(sc.ch) || sc.Match('#'))
133
sc.SetState(SCE_B_DEFAULT);
134
} else if (sc.state == SCE_B_LABEL) {
135
if (!IsIdentifier(sc.ch))
136
sc.SetState(SCE_B_DEFAULT);
137
} else if (sc.state == SCE_B_CONSTANT) {
138
if (!IsIdentifier(sc.ch))
139
sc.SetState(SCE_B_DEFAULT);
140
} else if (sc.state == SCE_B_NUMBER) {
142
sc.SetState(SCE_B_DEFAULT);
143
} else if (sc.state == SCE_B_HEXNUMBER) {
144
if (!IsHexDigit(sc.ch))
145
sc.SetState(SCE_B_DEFAULT);
146
} else if (sc.state == SCE_B_BINNUMBER) {
147
if (!IsBinDigit(sc.ch))
148
sc.SetState(SCE_B_DEFAULT);
149
} else if (sc.state == SCE_B_STRING) {
151
sc.ForwardSetState(SCE_B_DEFAULT);
154
sc.ChangeState(SCE_B_ERROR);
155
sc.SetState(SCE_B_DEFAULT);
157
} else if (sc.state == SCE_B_COMMENT || sc.state == SCE_B_PREPROCESSOR) {
159
sc.SetState(SCE_B_DEFAULT);
166
if (sc.state == SCE_B_DEFAULT || sc.state == SCE_B_ERROR) {
167
if (isfirst && sc.Match('.')) {
168
sc.SetState(SCE_B_LABEL);
169
} else if (isfirst && sc.Match('#')) {
171
sc.SetState(SCE_B_IDENTIFIER);
172
} else if (sc.Match(comment_char)) {
173
// Hack to make deprecated QBASIC '$Include show
174
// up in freebasic with SCE_B_PREPROCESSOR.
175
if (comment_char == '\'' && sc.Match(comment_char, '$'))
176
sc.SetState(SCE_B_PREPROCESSOR);
178
sc.SetState(SCE_B_COMMENT);
179
} else if (sc.Match('"')) {
180
sc.SetState(SCE_B_STRING);
181
} else if (IsDigit(sc.ch)) {
182
sc.SetState(SCE_B_NUMBER);
183
} else if (sc.Match('$')) {
184
sc.SetState(SCE_B_HEXNUMBER);
185
} else if (sc.Match('%')) {
186
sc.SetState(SCE_B_BINNUMBER);
187
} else if (sc.Match('#')) {
188
sc.SetState(SCE_B_CONSTANT);
189
} else if (IsOperator(sc.ch)) {
190
sc.SetState(SCE_B_OPERATOR);
191
} else if (IsIdentifier(sc.ch)) {
193
sc.SetState(SCE_B_IDENTIFIER);
194
} else if (!IsSpace(sc.ch)) {
195
sc.SetState(SCE_B_ERROR);
208
static int CheckBlitzFoldPoint(char const *token, int &level) {
209
if (!strcmp(token, "function") ||
210
!strcmp(token, "type")) {
211
level |= SC_FOLDLEVELHEADERFLAG;
214
if (!strcmp(token, "end function") ||
215
!strcmp(token, "end type")) {
221
static int CheckPureFoldPoint(char const *token, int &level) {
222
if (!strcmp(token, "procedure") ||
223
!strcmp(token, "enumeration") ||
224
!strcmp(token, "interface") ||
225
!strcmp(token, "structure")) {
226
level |= SC_FOLDLEVELHEADERFLAG;
229
if (!strcmp(token, "endprocedure") ||
230
!strcmp(token, "endenumeration") ||
231
!strcmp(token, "endinterface") ||
232
!strcmp(token, "endstructure")) {
238
static int CheckFreeFoldPoint(char const *token, int &level) {
239
if (!strcmp(token, "function") ||
240
!strcmp(token, "sub") ||
241
!strcmp(token, "type")) {
242
level |= SC_FOLDLEVELHEADERFLAG;
245
if (!strcmp(token, "end function") ||
246
!strcmp(token, "end sub") ||
247
!strcmp(token, "end type")) {
253
static void FoldBasicDoc(unsigned int startPos, int length,
254
Accessor &styler, int (*CheckFoldPoint)(char const *, int &)) {
255
int line = styler.GetLine(startPos);
256
int level = styler.LevelAt(line);
257
int go = 0, done = 0;
258
int endPos = startPos + length;
262
bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
263
// Scan for tokens at the start of the line (they may include
264
// whitespace, for tokens like "End Function"
265
for (i = startPos; i < endPos; i++) {
266
int c = styler.SafeGetCharAt(i);
268
if (wordlen) { // are we scanning a token already?
269
word[wordlen] = static_cast<char>(LowerCase(c));
270
if (!IsIdentifier(c)) { // done with token
271
word[wordlen] = '\0';
272
go = CheckFoldPoint(word, level);
274
// Treat any whitespace as single blank, for
275
// things like "End Function".
276
if (IsSpace(c) && IsIdentifier(word[wordlen - 1])) {
281
else // done with this line
284
} else if (wordlen < 255) {
287
} else { // start scanning at first non-whitespace character
289
if (IsIdentifier(c)) {
290
word[0] = static_cast<char>(LowerCase(c));
292
} else // done with this line
297
if (c == '\n') { // line end
298
if (!done && wordlen == 0 && foldCompact) // line was only space
299
level |= SC_FOLDLEVELWHITEFLAG;
300
if (level != styler.LevelAt(line))
301
styler.SetLevel(line, level);
306
level &= ~SC_FOLDLEVELHEADERFLAG;
307
level &= ~SC_FOLDLEVELWHITEFLAG;
314
static void ColouriseBlitzBasicDoc(unsigned int startPos, int length, int initStyle,
315
WordList *keywordlists[], Accessor &styler) {
316
ColouriseBasicDoc(startPos, length, initStyle, keywordlists, styler, ';');
319
static void ColourisePureBasicDoc(unsigned int startPos, int length, int initStyle,
320
WordList *keywordlists[], Accessor &styler) {
321
ColouriseBasicDoc(startPos, length, initStyle, keywordlists, styler, ';');
324
static void ColouriseFreeBasicDoc(unsigned int startPos, int length, int initStyle,
325
WordList *keywordlists[], Accessor &styler) {
326
ColouriseBasicDoc(startPos, length, initStyle, keywordlists, styler, '\'');
329
static void FoldBlitzBasicDoc(unsigned int startPos, int length, int,
330
WordList *[], Accessor &styler) {
331
FoldBasicDoc(startPos, length, styler, CheckBlitzFoldPoint);
334
static void FoldPureBasicDoc(unsigned int startPos, int length, int,
335
WordList *[], Accessor &styler) {
336
FoldBasicDoc(startPos, length, styler, CheckPureFoldPoint);
339
static void FoldFreeBasicDoc(unsigned int startPos, int length, int,
340
WordList *[], Accessor &styler) {
341
FoldBasicDoc(startPos, length, styler, CheckFreeFoldPoint);
344
static const char * const blitzbasicWordListDesc[] = {
345
"BlitzBasic Keywords",
352
static const char * const purebasicWordListDesc[] = {
353
"PureBasic Keywords",
354
"PureBasic PreProcessor Keywords",
360
static const char * const freebasicWordListDesc[] = {
361
"FreeBasic Keywords",
362
"FreeBasic PreProcessor Keywords",
368
LexerModule lmBlitzBasic(SCLEX_BLITZBASIC, ColouriseBlitzBasicDoc, "blitzbasic",
369
FoldBlitzBasicDoc, blitzbasicWordListDesc);
371
LexerModule lmPureBasic(SCLEX_PUREBASIC, ColourisePureBasicDoc, "purebasic",
372
FoldPureBasicDoc, purebasicWordListDesc);
374
LexerModule lmFreeBasic(SCLEX_FREEBASIC, ColouriseFreeBasicDoc, "freebasic",
375
FoldFreeBasicDoc, freebasicWordListDesc);