1 // Scintilla source code edit control
2 
3 // File: LexMetapost.cxx - general context conformant metapost coloring scheme
4 // Author: Hans Hagen - PRAGMA ADE - Hasselt NL - www.pragma-ade.com
5 // Version: September 28, 2003
6 
7 // Copyright: 1998-2003 by Neil Hodgson <neilh@scintilla.org>
8 // The License.txt file describes the conditions under which this software may be distributed.
9 
10 // This lexer is derived from the one written for the texwork environment (1999++) which in
11 // turn is inspired on texedit (1991++) which finds its roots in wdt (1986).
12 
13 #include <stdlib.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <stdio.h>
17 #include <stdarg.h>
18 
19 #include "Platform.h"
20 
21 #include "PropSet.h"
22 #include "Accessor.h"
23 #include "KeyWords.h"
24 #include "Scintilla.h"
25 #include "SciLexer.h"
26 #include "StyleContext.h"
27 
28 // val SCE_METAPOST_DEFAULT = 0
29 // val SCE_METAPOST_SPECIAL = 1
30 // val SCE_METAPOST_GROUP = 2
31 // val SCE_METAPOST_SYMBOL = 3
32 // val SCE_METAPOST_COMMAND = 4
33 // val SCE_METAPOST_TEXT = 5
34 
35 // Definitions in SciTEGlobal.properties:
36 //
37 // Metapost Highlighting
38 //
39 // # Default
40 // style.metapost.0=fore:#7F7F00
41 // # Special
42 // style.metapost.1=fore:#007F7F
43 // # Group
44 // style.metapost.2=fore:#880000
45 // # Symbol
46 // style.metapost.3=fore:#7F7F00
47 // # Command
48 // style.metapost.4=fore:#008800
49 // # Text
50 // style.metapost.5=fore:#000000
51 
52 // lexer.tex.comment.process=0
53 
54 // Auxiliary functions:
55 
endOfLine(Accessor & styler,unsigned int i)56 static inline bool endOfLine(Accessor &styler, unsigned int i) {
57 	return
58       (styler[i] == '\n') || ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n')) ;
59 }
60 
isMETAPOSTcomment(int ch)61 static inline bool isMETAPOSTcomment(int ch) {
62 	return
63       (ch == '%') ;
64 }
65 
isMETAPOSTone(int ch)66 static inline bool isMETAPOSTone(int ch) {
67 	return
68       (ch == '[') || (ch == ']') || (ch == '(') || (ch == ')') ||
69       (ch == ':') || (ch == '=') || (ch == '<') || (ch == '>') ||
70       (ch == '{') || (ch == '}') || (ch == '\'') || (ch == '\"') ;
71 }
72 
isMETAPOSTtwo(int ch)73 static inline bool isMETAPOSTtwo(int ch) {
74 	return
75       (ch == ';') || (ch == '$') || (ch == '@') || (ch == '#');
76 }
77 
isMETAPOSTthree(int ch)78 static inline bool isMETAPOSTthree(int ch) {
79 	return
80       (ch == '.') || (ch == '-') || (ch == '+') || (ch == '/') ||
81       (ch == '*') || (ch == ',') || (ch == '|') || (ch == '`') ||
82       (ch == '!') || (ch == '?') || (ch == '^') || (ch == '&') ||
83       (ch == '%') ;
84 }
85 
isMETAPOSTidentifier(int ch)86 static inline bool isMETAPOSTidentifier(int ch) {
87 	return
88       ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) ||
89       (ch == '_') ;
90 }
91 
isMETAPOSTnumber(int ch)92 static inline bool isMETAPOSTnumber(int ch) {
93 	return
94       (ch >= '0') && (ch <= '9') ;
95 }
96 
isMETAPOSTstring(int ch)97 static inline bool isMETAPOSTstring(int ch) {
98 	return
99       (ch == '\"') ;
100 }
101 
isMETAPOSTcolon(int ch)102 static inline bool isMETAPOSTcolon(int ch) {
103 	return
104 		(ch == ':') ;
105 }
106 
isMETAPOSTequal(int ch)107 static inline bool isMETAPOSTequal(int ch) {
108 	return
109 		(ch == '=') ;
110 }
111 
CheckMETAPOSTInterface(unsigned int startPos,int length,Accessor & styler,int defaultInterface)112 static int CheckMETAPOSTInterface(
113     unsigned int startPos,
114     int length,
115     Accessor &styler,
116 	int defaultInterface) {
117 
118     char lineBuffer[1024] ;
119 	unsigned int linePos = 0 ;
120 
121 	// some day we can make something lexer.metapost.mapping=(none,0)(metapost,1)(mp,1)(metafun,2)...
122 
123     if (styler.SafeGetCharAt(0) == '%') {
124         for (unsigned int i = 0; i < startPos + length; i++) {
125             lineBuffer[linePos++] = styler.SafeGetCharAt(i) ;
126             if (endOfLine(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
127                 lineBuffer[linePos] = '\0';
128 				if (strstr(lineBuffer, "interface=none")) {
129                     return 0 ;
130 				} else if (strstr(lineBuffer, "interface=metapost") || strstr(lineBuffer, "interface=mp")) {
131                     return 1 ;
132 				} else if (strstr(lineBuffer, "interface=metafun")) {
133                     return 2 ;
134 				} else if (styler.SafeGetCharAt(1) == 'D' && strstr(lineBuffer, "%D \\module")) {
135 					// better would be to limit the search to just one line
136 					return 2 ;
137                 } else {
138                     return defaultInterface ;
139                 }
140             }
141 		}
142     }
143 
144     return defaultInterface ;
145 }
146 
ColouriseMETAPOSTDoc(unsigned int startPos,int length,int,WordList * keywordlists[],Accessor & styler)147 static void ColouriseMETAPOSTDoc(
148     unsigned int startPos,
149     int length,
150     int,
151     WordList *keywordlists[],
152     Accessor &styler) {
153 
154 	styler.StartAt(startPos) ;
155 	styler.StartSegment(startPos) ;
156 
157 	bool processComment   = styler.GetPropertyInt("lexer.metapost.comment.process",   0) == 1 ;
158     int  defaultInterface = styler.GetPropertyInt("lexer.metapost.interface.default", 1) ;
159 
160 	int currentInterface = CheckMETAPOSTInterface(startPos,length,styler,defaultInterface) ;
161 
162 	// 0  no keyword highlighting
163 	// 1  metapost keyword hightlighting
164 	// 2+ metafun keyword hightlighting
165 
166 	int extraInterface = 0 ;
167 
168 	if (currentInterface != 0) {
169 		extraInterface = currentInterface ;
170 	}
171 
172 	WordList &keywords  = *keywordlists[0] ;
173 	WordList &keywords2 = *keywordlists[extraInterface-1] ;
174 
175 	StyleContext sc(startPos, length, SCE_METAPOST_TEXT, styler) ;
176 
177 	char key[100] ;
178 
179     bool inTeX     = false ;
180 	bool inComment = false ;
181 	bool inString  = false ;
182 	bool inClause  = false ;
183 
184 	bool going = sc.More() ; // needed because of a fuzzy end of file state
185 
186 	for (; going; sc.Forward()) {
187 
188 		if (! sc.More()) { going = false ; } // we need to go one behind the end of text
189 
190 		if (inClause) {
191 			sc.SetState(SCE_METAPOST_TEXT) ;
192 			inClause = false ;
193 		}
194 
195 		if (inComment) {
196 			if (sc.atLineEnd) {
197 				sc.SetState(SCE_METAPOST_TEXT) ;
198 				inTeX = false ;
199 				inComment = false ;
200 				inClause = false ;
201 				inString = false ; // not correct but we want to stimulate one-lines
202 			}
203 		} else if (inString) {
204 			if (isMETAPOSTstring(sc.ch)) {
205 				sc.SetState(SCE_METAPOST_SPECIAL) ;
206 				sc.ForwardSetState(SCE_METAPOST_TEXT) ;
207 				inString = false ;
208 			} else if (sc.atLineEnd) {
209 				sc.SetState(SCE_METAPOST_TEXT) ;
210 				inTeX = false ;
211 				inComment = false ;
212 				inClause = false ;
213 				inString = false ; // not correct but we want to stimulate one-lines
214 			}
215 		} else {
216 			if ((! isMETAPOSTidentifier(sc.ch)) && (sc.LengthCurrent() > 0)) {
217 				if (sc.state == SCE_METAPOST_COMMAND) {
218 					sc.GetCurrent(key, sizeof(key)) ;
219 					if ((strcmp(key,"btex") == 0) || (strcmp(key,"verbatimtex") == 0)) {
220     					sc.ChangeState(SCE_METAPOST_GROUP) ;
221 						inTeX = true ;
222 					} else if (inTeX) {
223 						if (strcmp(key,"etex") == 0) {
224 	    					sc.ChangeState(SCE_METAPOST_GROUP) ;
225 							inTeX = false ;
226 						} else {
227 	    					sc.ChangeState(SCE_METAPOST_TEXT) ;
228 						}
229 					} else {
230 						if (keywords && keywords.InList(key)) {
231     						sc.ChangeState(SCE_METAPOST_COMMAND) ;
232 						} else if (keywords2 && keywords2.InList(key)) {
233 							sc.ChangeState(SCE_METAPOST_EXTRA) ;
234 						} else {
235 							sc.ChangeState(SCE_METAPOST_TEXT) ;
236 						}
237 					}
238 				}
239 			}
240 			if (isMETAPOSTcomment(sc.ch)) {
241 				if (! inTeX) {
242 					sc.SetState(SCE_METAPOST_SYMBOL) ;
243 					sc.ForwardSetState(SCE_METAPOST_DEFAULT) ;
244 					inComment = ! processComment ;
245 				} else {
246 					sc.SetState(SCE_METAPOST_TEXT) ;
247 				}
248 			} else if (isMETAPOSTstring(sc.ch)) {
249 				if (! inTeX) {
250 					sc.SetState(SCE_METAPOST_SPECIAL) ;
251 					if (! isMETAPOSTstring(sc.chNext)) {
252 						sc.ForwardSetState(SCE_METAPOST_TEXT) ;
253 					}
254 					inString = true ;
255 				} else {
256 					sc.SetState(SCE_METAPOST_TEXT) ;
257 				}
258 			} else if (isMETAPOSTcolon(sc.ch)) {
259 				if (! inTeX) {
260 					if (! isMETAPOSTequal(sc.chNext)) {
261 						sc.SetState(SCE_METAPOST_COMMAND) ;
262 						inClause = true ;
263 					} else {
264 						sc.SetState(SCE_METAPOST_SPECIAL) ;
265 					}
266 				} else {
267 					sc.SetState(SCE_METAPOST_TEXT) ;
268 				}
269 			} else if (isMETAPOSTone(sc.ch)) {
270 				if (! inTeX) {
271 					sc.SetState(SCE_METAPOST_SPECIAL) ;
272 				} else {
273 					sc.SetState(SCE_METAPOST_TEXT) ;
274 				}
275 			} else if (isMETAPOSTtwo(sc.ch)) {
276 				if (! inTeX) {
277 					sc.SetState(SCE_METAPOST_GROUP) ;
278 				} else {
279 					sc.SetState(SCE_METAPOST_TEXT) ;
280 				}
281 			} else if (isMETAPOSTthree(sc.ch)) {
282 				if (! inTeX) {
283 					sc.SetState(SCE_METAPOST_SYMBOL) ;
284 				} else {
285 					sc.SetState(SCE_METAPOST_TEXT) ;
286 				}
287 			} else if (isMETAPOSTidentifier(sc.ch)) {
288 				if (sc.state != SCE_METAPOST_COMMAND) {
289 					sc.SetState(SCE_METAPOST_TEXT) ;
290 					sc.ChangeState(SCE_METAPOST_COMMAND) ;
291 				}
292 			} else if (isMETAPOSTnumber(sc.ch)) {
293 				// rather redundant since for the moment we don't handle numbers
294 				sc.SetState(SCE_METAPOST_TEXT) ;
295 			} else if (sc.atLineEnd) {
296 				sc.SetState(SCE_METAPOST_TEXT) ;
297 				inTeX = false ;
298 				inComment = false ;
299 				inClause = false ;
300 				inString = false ;
301 			} else {
302 				sc.SetState(SCE_METAPOST_TEXT) ;
303 			}
304 		}
305 
306 	}
307 
308 	sc.Complete();
309 
310 }
311 
312 // Hooks info the system:
313 
314 static const char * const metapostWordListDesc[] = {
315 	"MetaPost",
316 	"MetaFun",
317 	0
318 } ;
319 
320 LexerModule lmMETAPOST(SCLEX_METAPOST, ColouriseMETAPOSTDoc, "metapost", 0, metapostWordListDesc);
321