1 /******************************************************************
\r
4 * A haskell lexer for the scintilla code control.
\r
5 * Some stuff "lended" from LexPython.cxx and LexCPP.cxx.
\r
6 * External lexer stuff inspired from the caml external lexer.
\r
8 * Written by Tobias Engvall - tumm at dtek dot chalmers dot se
\r
12 * * Implement a folder :)
\r
13 * * Nice Character-lexing (stuff inside '\''), LexPython has
\r
17 *****************************************************************/
\r
25 #include "Platform.h"
\r
27 #include "PropSet.h"
\r
28 #include "Accessor.h"
\r
29 #include "StyleContext.h"
\r
30 #include "KeyWords.h"
\r
31 #include "Scintilla.h"
\r
32 #include "SciLexer.h"
\r
34 #ifdef SCI_NAMESPACE
\r
35 using namespace Scintilla;
\r
38 #ifdef BUILD_AS_EXTERNAL_LEXER
\r
40 #include "ExternalLexer.h"
\r
41 #include "WindowAccessor.h"
\r
43 #define BUILD_EXTERNAL_LEXER 0
\r
47 // Max level of nested comments
\r
48 #define SCE_HA_COMMENTMAX SCE_HA_COMMENTBLOCK3
\r
51 enum kwType { kwOther, kwClass, kwData, kwInstance, kwImport, kwModule, kwType};
\r
53 static inline bool IsNewline(const int ch) {
\r
54 return (ch == '\n' || ch == '\r');
\r
57 static inline bool IsWhitespace(const int ch) {
\r
63 static inline bool IsAWordStart(const int ch) {
\r
64 return (ch < 0x80) && (isalnum(ch) || ch == '_');
\r
67 static inline bool IsAWordChar(const int ch) {
\r
68 return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch == '\'');
\r
71 static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,
\r
72 WordList *keywordlists[], Accessor &styler) {
\r
74 WordList &keywords = *keywordlists[0];
\r
76 int kwLast = kwOther;
\r
78 StyleContext sc(startPos, length, initStyle, styler);
\r
80 for (; sc.More(); sc.Forward()) {
\r
82 // Check for state end
\r
84 if (sc.state == SCE_HA_OPERATOR) {
\r
86 sc.SetState(SCE_HA_DEFAULT);
\r
89 else if (sc.state == SCE_HA_STRING) {
\r
90 if (sc.ch == '\"') {
\r
91 sc.ForwardSetState(SCE_HA_DEFAULT);
\r
92 } else if (sc.ch == '\\') {
\r
97 else if (sc.state == SCE_HA_CHARACTER) {
\r
98 if (sc.ch == '\'') {
\r
99 sc.ForwardSetState(SCE_HA_DEFAULT);
\r
100 } else if (sc.ch == '\\') {
\r
105 else if (sc.state == SCE_HA_NUMBER) {
\r
106 if (!IsADigit(sc.ch)) {
\r
107 sc.SetState(SCE_HA_DEFAULT);
\r
110 // Types, constructors, etc.
\r
111 else if (sc.state == SCE_HA_CAPITAL) {
\r
112 if (!IsAWordChar(sc.ch) || sc.ch == '.') {
\r
113 sc.SetState(SCE_HA_DEFAULT);
\r
117 else if (sc.state == SCE_HA_IDENTIFIER) {
\r
118 if (!IsAWordChar(sc.ch)) {
\r
120 sc.GetCurrent(s, sizeof(s));
\r
121 int style = SCE_HA_IDENTIFIER;
\r
122 if ((kwLast == kwImport) || (strcmp(s,"qualified") == 0) || (strcmp(s,"as") == 0)) {
\r
123 style = SCE_HA_IMPORT;
\r
124 } else if (keywords.InList(s)) {
\r
125 style = SCE_HA_KEYWORD;
\r
126 } else if (kwLast == kwData) {
\r
127 style = SCE_HA_DATA;
\r
128 } else if (kwLast == kwClass) {
\r
129 style = SCE_HA_CLASS;
\r
130 } else if (kwLast == kwModule) {
\r
131 style = SCE_HA_MODULE;
\r
132 } else if (isupper(s[0])) {
\r
133 style = SCE_HA_CAPITAL;
\r
135 sc.ChangeState(style);
\r
136 sc.SetState(SCE_HA_DEFAULT);
\r
137 if (style == SCE_HA_KEYWORD) {
\r
138 if (0 == strcmp(s, "class"))
\r
140 else if (0 == strcmp(s, "data"))
\r
142 else if (0 == strcmp(s, "instance"))
\r
143 kwLast = kwInstance;
\r
144 else if (0 == strcmp(s, "import"))
\r
146 else if (0 == strcmp(s, "module"))
\r
150 } else if (style == SCE_HA_CLASS || style == SCE_HA_IMPORT ||
\r
151 style == SCE_HA_MODULE || style == SCE_HA_CAPITAL ||
\r
152 style == SCE_HA_DATA || style == SCE_HA_INSTANCE) {
\r
159 else if (sc.state == SCE_HA_COMMENTLINE) {
\r
160 if (IsNewline(sc.ch))
\r
161 sc.SetState(SCE_HA_DEFAULT);
\r
164 else if (sc.state >= SCE_HA_COMMENTBLOCK) {
\r
165 if (sc.Match("{-")) {
\r
166 if (sc.state < SCE_HA_COMMENTMAX)
\r
167 sc.SetState(sc.state + 1);
\r
169 else if (sc.Match("-}")) {
\r
171 if (sc.state == SCE_HA_COMMENTBLOCK)
\r
172 sc.ForwardSetState(SCE_HA_DEFAULT);
\r
174 sc.ForwardSetState(sc.state - 1);
\r
178 if (sc.state == SCE_HA_DEFAULT) {
\r
180 if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
\r
181 sc.SetState(SCE_HA_NUMBER);
\r
182 if (sc.ch == '0' && (sc.chNext == 'X' || sc.chNext == 'x')) { // Match anything starting with "0x" or "0X", too
\r
187 else if (sc.Match("--")) {
\r
188 sc.SetState(SCE_HA_COMMENTLINE);
\r
191 else if (sc.Match("{-")) {
\r
192 sc.SetState(SCE_HA_COMMENTBLOCK);
\r
195 else if (sc.Match('\"')) {
\r
196 sc.SetState(SCE_HA_STRING);
\r
199 else if (sc.Match('\'')) {
\r
200 sc.SetState(SCE_HA_CHARACTER);
\r
203 else if (sc.Match('\"')) {
\r
204 sc.SetState(SCE_HA_STRING);
\r
207 else if (isascii(sc.ch) && isoperator(static_cast<char>(sc.ch))) {
\r
208 sc.SetState(SCE_HA_OPERATOR);
\r
211 else if (IsAWordStart(sc.ch)) {
\r
212 sc.SetState(SCE_HA_IDENTIFIER);
\r
220 // External stuff - used for dynamic-loading, not implemented in wxStyledTextCtrl yet.
\r
221 // Inspired by the caml external lexer - Credits to Robert Roessler - http://www.rftp.com
\r
222 #ifdef BUILD_EXTERNAL_LEXER
\r
223 static const char* LexerName = "haskell";
\r
225 void EXT_LEXER_DECL Lex(unsigned int lexer, unsigned int startPos, int length, int initStyle,
\r
226 char *words[], WindowID window, char *props)
\r
229 ps.SetMultiple(props);
\r
230 WindowAccessor wa(window, ps);
\r
233 for (; words[nWL]; nWL++) ;
\r
234 WordList** wl = new WordList* [nWL + 1];
\r
238 wl[i] = new WordList();
\r
239 wl[i]->Set(words[i]);
\r
243 ColorizeHaskellDoc(startPos, length, initStyle, wl, wa);
\r
245 for (i=nWL-1;i>=0;i--)
\r
250 void EXT_LEXER_DECL Fold (unsigned int lexer, unsigned int startPos, int length, int initStyle,
\r
251 char *words[], WindowID window, char *props)
\r
256 int EXT_LEXER_DECL GetLexerCount()
\r
261 void EXT_LEXER_DECL GetLexerName(unsigned int Index, char *name, int buflength)
\r
263 if (buflength > 0) {
\r
265 int n = strlen(LexerName);
\r
268 memcpy(name, LexerName, n), name[n] = '\0';
\r
273 LexerModule lmHaskell(SCLEX_HASKELL, ColorizeHaskellDoc, "haskell");
\r