LexPB.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. // Scintilla source code edit control
  2. // @file LexPB.cxx
  3. // Lexer for PowerBasic by Roland Walter, roland@rowalt.de (for PowerBasic see www.powerbasic.com)
  4. //
  5. // Changes:
  6. // 17.10.2003: Toggling of subs/functions now until next sub/function - this gives better results
  7. // 29.10.2003: 1. Bug: Toggling didn't work for subs/functions added in editor
  8. // 2. Own colors for PB constants and Inline Assembler SCE_B_CONSTANT and SCE_B_ASM
  9. // 3. Several smaller syntax coloring improvements and speed optimizations
  10. // 12.07.2004: 1. Toggling for macros added
  11. // 2. Further folding speed optimitations (for people dealing with very large listings)
  12. //
  13. // Necessary changes for the PB lexer in Scintilla project:
  14. // - In SciLexer.h and Scintilla.iface:
  15. //
  16. // #define SCLEX_POWERBASIC 51 //ID for PowerBasic lexer
  17. // (...)
  18. // #define SCE_B_DEFAULT 0 //in both VB and PB lexer
  19. // #define SCE_B_COMMENT 1 //in both VB and PB lexer
  20. // #define SCE_B_NUMBER 2 //in both VB and PB lexer
  21. // #define SCE_B_KEYWORD 3 //in both VB and PB lexer
  22. // #define SCE_B_STRING 4 //in both VB and PB lexer
  23. // #define SCE_B_PREPROCESSOR 5 //VB lexer only, not in PB lexer
  24. // #define SCE_B_OPERATOR 6 //in both VB and PB lexer
  25. // #define SCE_B_IDENTIFIER 7 //in both VB and PB lexer
  26. // #define SCE_B_DATE 8 //VB lexer only, not in PB lexer
  27. // #define SCE_B_CONSTANT 13 //PB lexer only, not in VB lexer
  28. // #define SCE_B_ASM 14 //PB lexer only, not in VB lexer
  29. // - Statement added to KeyWords.cxx: 'LINK_LEXER(lmPB);'
  30. // - Statement added to scintilla_vc6.mak: '$(DIR_O)\LexPB.obj: ...\src\LexPB.cxx $(LEX_HEADERS)'
  31. //
  32. // Copyright for Scintilla: 1998-2001 by Neil Hodgson <neilh@scintilla.org>
  33. // The License.txt file describes the conditions under which this software may be distributed.
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <stdio.h>
  37. #include <stdarg.h>
  38. #include <assert.h>
  39. #include <ctype.h>
  40. #include "ILexer.h"
  41. #include "Scintilla.h"
  42. #include "SciLexer.h"
  43. #include "WordList.h"
  44. #include "LexAccessor.h"
  45. #include "Accessor.h"
  46. #include "StyleContext.h"
  47. #include "CharacterSet.h"
  48. #include "LexerModule.h"
  49. #ifdef SCI_NAMESPACE
  50. using namespace Scintilla;
  51. #endif
  52. static inline bool IsTypeCharacter(const int ch)
  53. {
  54. return ch == '%' || ch == '&' || ch == '@' || ch == '!' || ch == '#' || ch == '$' || ch == '?';
  55. }
  56. static inline bool IsAWordChar(const int ch)
  57. {
  58. return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
  59. }
  60. static inline bool IsAWordStart(const int ch)
  61. {
  62. return (ch < 0x80) && (isalnum(ch) || ch == '_');
  63. }
  64. static bool MatchUpperCase(Accessor &styler, Sci_Position pos, const char *s) //Same as styler.Match() but uppercase comparison (a-z,A-Z and space only)
  65. {
  66. char ch;
  67. for (Sci_Position i=0; *s; i++)
  68. {
  69. ch=styler.SafeGetCharAt(pos+i);
  70. if (ch > 0x60) ch -= '\x20';
  71. if (*s != ch) return false;
  72. s++;
  73. }
  74. return true;
  75. }
  76. static void ColourisePBDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,WordList *keywordlists[],Accessor &styler) {
  77. WordList &keywords = *keywordlists[0];
  78. styler.StartAt(startPos);
  79. StyleContext sc(startPos, length, initStyle, styler);
  80. for (; sc.More(); sc.Forward()) {
  81. switch (sc.state)
  82. {
  83. case SCE_B_OPERATOR:
  84. {
  85. sc.SetState(SCE_B_DEFAULT);
  86. break;
  87. }
  88. case SCE_B_KEYWORD:
  89. {
  90. if (!IsAWordChar(sc.ch))
  91. {
  92. if (!IsTypeCharacter(sc.ch))
  93. {
  94. char s[100];
  95. sc.GetCurrentLowered(s, sizeof(s));
  96. if (keywords.InList(s))
  97. {
  98. if (strcmp(s, "rem") == 0)
  99. {
  100. sc.ChangeState(SCE_B_COMMENT);
  101. if (sc.atLineEnd) {sc.SetState(SCE_B_DEFAULT);}
  102. }
  103. else if (strcmp(s, "asm") == 0)
  104. {
  105. sc.ChangeState(SCE_B_ASM);
  106. if (sc.atLineEnd) {sc.SetState(SCE_B_DEFAULT);}
  107. }
  108. else
  109. {
  110. sc.SetState(SCE_B_DEFAULT);
  111. }
  112. }
  113. else
  114. {
  115. sc.ChangeState(SCE_B_IDENTIFIER);
  116. sc.SetState(SCE_B_DEFAULT);
  117. }
  118. }
  119. }
  120. break;
  121. }
  122. case SCE_B_NUMBER:
  123. {
  124. if (!IsAWordChar(sc.ch)) {sc.SetState(SCE_B_DEFAULT);}
  125. break;
  126. }
  127. case SCE_B_STRING:
  128. {
  129. if (sc.ch == '\"'){sc.ForwardSetState(SCE_B_DEFAULT);}
  130. break;
  131. }
  132. case SCE_B_CONSTANT:
  133. {
  134. if (!IsAWordChar(sc.ch)) {sc.SetState(SCE_B_DEFAULT);}
  135. break;
  136. }
  137. case SCE_B_COMMENT:
  138. {
  139. if (sc.atLineEnd) {sc.SetState(SCE_B_DEFAULT);}
  140. break;
  141. }
  142. case SCE_B_ASM:
  143. {
  144. if (sc.atLineEnd) {sc.SetState(SCE_B_DEFAULT);}
  145. break;
  146. }
  147. } //switch (sc.state)
  148. // Determine if a new state should be entered:
  149. if (sc.state == SCE_B_DEFAULT)
  150. {
  151. if (sc.ch == '\'') {sc.SetState(SCE_B_COMMENT);}
  152. else if (sc.ch == '\"') {sc.SetState(SCE_B_STRING);}
  153. else if (sc.ch == '&' && tolower(sc.chNext) == 'h') {sc.SetState(SCE_B_NUMBER);}
  154. else if (sc.ch == '&' && tolower(sc.chNext) == 'b') {sc.SetState(SCE_B_NUMBER);}
  155. else if (sc.ch == '&' && tolower(sc.chNext) == 'o') {sc.SetState(SCE_B_NUMBER);}
  156. else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {sc.SetState(SCE_B_NUMBER);}
  157. else if (IsAWordStart(sc.ch)) {sc.SetState(SCE_B_KEYWORD);}
  158. else if (sc.ch == '%') {sc.SetState(SCE_B_CONSTANT);}
  159. else if (sc.ch == '$') {sc.SetState(SCE_B_CONSTANT);}
  160. else if (sc.ch == '#') {sc.SetState(SCE_B_KEYWORD);}
  161. else if (sc.ch == '!') {sc.SetState(SCE_B_ASM);}
  162. else if (isoperator(static_cast<char>(sc.ch)) || (sc.ch == '\\')) {sc.SetState(SCE_B_OPERATOR);}
  163. }
  164. } //for (; sc.More(); sc.Forward())
  165. sc.Complete();
  166. }
  167. //The folding routine for PowerBasic toggles SUBs and FUNCTIONs only. This was exactly what I wanted,
  168. //nothing more. I had worked with this kind of toggling for several years when I used the great good old
  169. //GFA Basic which is dead now. After testing the feature of toggling FOR-NEXT loops, WHILE-WEND loops
  170. //and so on too I found this is more disturbing then helping (for me). So if You think in another way
  171. //you can (or must) write Your own toggling routine ;-)
  172. static void FoldPBDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler)
  173. {
  174. // No folding enabled, no reason to continue...
  175. if( styler.GetPropertyInt("fold") == 0 )
  176. return;
  177. Sci_PositionU endPos = startPos + length;
  178. Sci_Position lineCurrent = styler.GetLine(startPos);
  179. int levelCurrent = SC_FOLDLEVELBASE;
  180. if (lineCurrent > 0)
  181. levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
  182. int levelNext = levelCurrent;
  183. char chNext = styler[startPos];
  184. bool fNewLine=true;
  185. bool fMightBeMultiLineMacro=false;
  186. bool fBeginOfCommentFound=false;
  187. for (Sci_PositionU i = startPos; i < endPos; i++)
  188. {
  189. char ch = chNext;
  190. chNext = styler.SafeGetCharAt(i + 1);
  191. if (fNewLine) //Begin of a new line (The Sub/Function/Macro keywords may occur at begin of line only)
  192. {
  193. fNewLine=false;
  194. fBeginOfCommentFound=false;
  195. switch (ch)
  196. {
  197. case ' ': //Most lines start with space - so check this first, the code is the same as for 'default:'
  198. case '\t': //Handle tab too
  199. {
  200. int levelUse = levelCurrent;
  201. int lev = levelUse | levelNext << 16;
  202. styler.SetLevel(lineCurrent, lev);
  203. break;
  204. }
  205. case 'F':
  206. case 'f':
  207. {
  208. switch (chNext)
  209. {
  210. case 'U':
  211. case 'u':
  212. {
  213. if( MatchUpperCase(styler,i,"FUNCTION") )
  214. {
  215. styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
  216. levelNext=SC_FOLDLEVELBASE+1;
  217. }
  218. break;
  219. }
  220. }
  221. break;
  222. }
  223. case 'S':
  224. case 's':
  225. {
  226. switch (chNext)
  227. {
  228. case 'U':
  229. case 'u':
  230. {
  231. if( MatchUpperCase(styler,i,"SUB") )
  232. {
  233. styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
  234. levelNext=SC_FOLDLEVELBASE+1;
  235. }
  236. break;
  237. }
  238. case 'T':
  239. case 't':
  240. {
  241. if( MatchUpperCase(styler,i,"STATIC FUNCTION") )
  242. {
  243. styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
  244. levelNext=SC_FOLDLEVELBASE+1;
  245. }
  246. else if( MatchUpperCase(styler,i,"STATIC SUB") )
  247. {
  248. styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
  249. levelNext=SC_FOLDLEVELBASE+1;
  250. }
  251. break;
  252. }
  253. }
  254. break;
  255. }
  256. case 'C':
  257. case 'c':
  258. {
  259. switch (chNext)
  260. {
  261. case 'A':
  262. case 'a':
  263. {
  264. if( MatchUpperCase(styler,i,"CALLBACK FUNCTION") )
  265. {
  266. styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
  267. levelNext=SC_FOLDLEVELBASE+1;
  268. }
  269. break;
  270. }
  271. }
  272. break;
  273. }
  274. case 'M':
  275. case 'm':
  276. {
  277. switch (chNext)
  278. {
  279. case 'A':
  280. case 'a':
  281. {
  282. if( MatchUpperCase(styler,i,"MACRO") )
  283. {
  284. fMightBeMultiLineMacro=true; //Set folder level at end of line, we have to check for single line macro
  285. }
  286. break;
  287. }
  288. }
  289. break;
  290. }
  291. default:
  292. {
  293. int levelUse = levelCurrent;
  294. int lev = levelUse | levelNext << 16;
  295. styler.SetLevel(lineCurrent, lev);
  296. break;
  297. }
  298. } //switch (ch)
  299. } //if( fNewLine )
  300. switch (ch)
  301. {
  302. case '=': //To test single line macros
  303. {
  304. if (fBeginOfCommentFound==false)
  305. fMightBeMultiLineMacro=false; //The found macro is a single line macro only;
  306. break;
  307. }
  308. case '\'': //A comment starts
  309. {
  310. fBeginOfCommentFound=true;
  311. break;
  312. }
  313. case '\n':
  314. {
  315. if (fMightBeMultiLineMacro) //The current line is the begin of a multi line macro
  316. {
  317. fMightBeMultiLineMacro=false;
  318. styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
  319. levelNext=SC_FOLDLEVELBASE+1;
  320. }
  321. lineCurrent++;
  322. levelCurrent = levelNext;
  323. fNewLine=true;
  324. break;
  325. }
  326. case '\r':
  327. {
  328. if (chNext != '\n')
  329. {
  330. lineCurrent++;
  331. levelCurrent = levelNext;
  332. fNewLine=true;
  333. }
  334. break;
  335. }
  336. } //switch (ch)
  337. } //for (Sci_PositionU i = startPos; i < endPos; i++)
  338. }
  339. static const char * const pbWordListDesc[] = {
  340. "Keywords",
  341. 0
  342. };
  343. LexerModule lmPB(SCLEX_POWERBASIC, ColourisePBDoc, "powerbasic", FoldPBDoc, pbWordListDesc);