1 | // Scintilla source code edit control |
2 | /** @file LexForth.cxx |
3 | ** Lexer for FORTH |
4 | **/ |
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. |
7 | |
8 | #include <stdlib.h> |
9 | #include <string.h> |
10 | #include <stdio.h> |
11 | #include <stdarg.h> |
12 | #include <assert.h> |
13 | #include <ctype.h> |
14 | |
15 | #include <string> |
16 | #include <string_view> |
17 | |
18 | #include "ILexer.h" |
19 | #include "Scintilla.h" |
20 | #include "SciLexer.h" |
21 | |
22 | #include "WordList.h" |
23 | #include "LexAccessor.h" |
24 | #include "Accessor.h" |
25 | #include "StyleContext.h" |
26 | #include "CharacterSet.h" |
27 | #include "LexerModule.h" |
28 | |
29 | using namespace Lexilla; |
30 | |
31 | static inline bool IsAWordStart(int ch) { |
32 | return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '.'); |
33 | } |
34 | |
35 | static inline bool IsANumChar(int ch) { |
36 | return (ch < 0x80) && (isxdigit(ch) || ch == '.' || ch == 'e' || ch == 'E' ); |
37 | } |
38 | |
39 | static inline bool IsASpaceChar(int ch) { |
40 | return (ch < 0x80) && isspace(ch); |
41 | } |
42 | |
43 | static void ColouriseForthDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordLists[], |
44 | Accessor &styler) { |
45 | |
46 | WordList &control = *keywordLists[0]; |
47 | WordList &keyword = *keywordLists[1]; |
48 | WordList &defword = *keywordLists[2]; |
49 | WordList &preword1 = *keywordLists[3]; |
50 | WordList &preword2 = *keywordLists[4]; |
51 | WordList &strings = *keywordLists[5]; |
52 | |
53 | StyleContext sc(startPos, length, initStyle, styler); |
54 | |
55 | for (; sc.More(); sc.Forward()) |
56 | { |
57 | // Determine if the current state should terminate. |
58 | if (sc.state == SCE_FORTH_COMMENT) { |
59 | if (sc.atLineEnd) { |
60 | sc.SetState(SCE_FORTH_DEFAULT); |
61 | } |
62 | }else if (sc.state == SCE_FORTH_COMMENT_ML) { |
63 | if (sc.ch == ')') { |
64 | sc.ForwardSetState(SCE_FORTH_DEFAULT); |
65 | } |
66 | }else if (sc.state == SCE_FORTH_IDENTIFIER || sc.state == SCE_FORTH_NUMBER) { |
67 | // handle numbers here too, because what we thought was a number might |
68 | // turn out to be a keyword e.g. 2DUP |
69 | if (IsASpaceChar(sc.ch) ) { |
70 | char s[100]; |
71 | sc.GetCurrentLowered(s, sizeof(s)); |
72 | int newState = sc.state == SCE_FORTH_NUMBER ? SCE_FORTH_NUMBER : SCE_FORTH_DEFAULT; |
73 | if (control.InList(s)) { |
74 | sc.ChangeState(SCE_FORTH_CONTROL); |
75 | } else if (keyword.InList(s)) { |
76 | sc.ChangeState(SCE_FORTH_KEYWORD); |
77 | } else if (defword.InList(s)) { |
78 | sc.ChangeState(SCE_FORTH_DEFWORD); |
79 | } else if (preword1.InList(s)) { |
80 | sc.ChangeState(SCE_FORTH_PREWORD1); |
81 | } else if (preword2.InList(s)) { |
82 | sc.ChangeState(SCE_FORTH_PREWORD2); |
83 | } else if (strings.InList(s)) { |
84 | sc.ChangeState(SCE_FORTH_STRING); |
85 | newState = SCE_FORTH_STRING; |
86 | } |
87 | sc.SetState(newState); |
88 | } |
89 | if (sc.state == SCE_FORTH_NUMBER) { |
90 | if (IsASpaceChar(sc.ch)) { |
91 | sc.SetState(SCE_FORTH_DEFAULT); |
92 | } else if (!IsANumChar(sc.ch)) { |
93 | sc.ChangeState(SCE_FORTH_IDENTIFIER); |
94 | } |
95 | } |
96 | }else if (sc.state == SCE_FORTH_STRING) { |
97 | if (sc.ch == '\"') { |
98 | sc.ForwardSetState(SCE_FORTH_DEFAULT); |
99 | } |
100 | }else if (sc.state == SCE_FORTH_LOCALE) { |
101 | if (sc.ch == '}') { |
102 | sc.ForwardSetState(SCE_FORTH_DEFAULT); |
103 | } |
104 | }else if (sc.state == SCE_FORTH_DEFWORD) { |
105 | if (IsASpaceChar(sc.ch)) { |
106 | sc.SetState(SCE_FORTH_DEFAULT); |
107 | } |
108 | } |
109 | |
110 | // Determine if a new state should be entered. |
111 | if (sc.state == SCE_FORTH_DEFAULT) { |
112 | if (sc.ch == '\\'){ |
113 | sc.SetState(SCE_FORTH_COMMENT); |
114 | } else if (sc.ch == '(' && |
115 | (sc.atLineStart || IsASpaceChar(sc.chPrev)) && |
116 | (sc.atLineEnd || IsASpaceChar(sc.chNext))) { |
117 | sc.SetState(SCE_FORTH_COMMENT_ML); |
118 | } else if ( (sc.ch == '$' && (IsASCII(sc.chNext) && isxdigit(sc.chNext))) ) { |
119 | // number starting with $ is a hex number |
120 | sc.SetState(SCE_FORTH_NUMBER); |
121 | while(sc.More() && IsASCII(sc.chNext) && isxdigit(sc.chNext)) |
122 | sc.Forward(); |
123 | } else if ( (sc.ch == '%' && (IsASCII(sc.chNext) && (sc.chNext == '0' || sc.chNext == '1'))) ) { |
124 | // number starting with % is binary |
125 | sc.SetState(SCE_FORTH_NUMBER); |
126 | while(sc.More() && IsASCII(sc.chNext) && (sc.chNext == '0' || sc.chNext == '1')) |
127 | sc.Forward(); |
128 | } else if ( IsASCII(sc.ch) && |
129 | (isxdigit(sc.ch) || ((sc.ch == '.' || sc.ch == '-') && IsASCII(sc.chNext) && isxdigit(sc.chNext)) ) |
130 | ){ |
131 | sc.SetState(SCE_FORTH_NUMBER); |
132 | } else if (IsAWordStart(sc.ch)) { |
133 | sc.SetState(SCE_FORTH_IDENTIFIER); |
134 | } else if (sc.ch == '{') { |
135 | sc.SetState(SCE_FORTH_LOCALE); |
136 | } else if (sc.ch == ':' && IsASCII(sc.chNext) && isspace(sc.chNext)) { |
137 | // highlight word definitions e.g. : GCD ( n n -- n ) ..... ; |
138 | // ^ ^^^ |
139 | sc.SetState(SCE_FORTH_DEFWORD); |
140 | while(sc.More() && IsASCII(sc.chNext) && isspace(sc.chNext)) |
141 | sc.Forward(); |
142 | } else if (sc.ch == ';' && |
143 | (sc.atLineStart || IsASpaceChar(sc.chPrev)) && |
144 | (sc.atLineEnd || IsASpaceChar(sc.chNext)) ) { |
145 | // mark the ';' that ends a word |
146 | sc.SetState(SCE_FORTH_DEFWORD); |
147 | sc.ForwardSetState(SCE_FORTH_DEFAULT); |
148 | } |
149 | } |
150 | |
151 | } |
152 | sc.Complete(); |
153 | } |
154 | |
155 | static void FoldForthDoc(Sci_PositionU, Sci_Position, int, WordList *[], |
156 | Accessor &) { |
157 | } |
158 | |
159 | static const char * const forthWordLists[] = { |
160 | "control keywords" , |
161 | "keywords" , |
162 | "definition words" , |
163 | "prewords with one argument" , |
164 | "prewords with two arguments" , |
165 | "string definition keywords" , |
166 | 0, |
167 | }; |
168 | |
169 | LexerModule lmForth(SCLEX_FORTH, ColouriseForthDoc, "forth" , FoldForthDoc, forthWordLists); |
170 | |
171 | |
172 | |