1 // Scintilla source code edit control
2 /** @file LexSpice.cxx
3  ** Lexer for Spice
4  **/
5 // Copyright 2006 by Fabien Proriol
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 
17 #include "ILexer.h"
18 #include "Scintilla.h"
19 #include "SciLexer.h"
20 
21 #include "WordList.h"
22 #include "LexAccessor.h"
23 #include "Accessor.h"
24 #include "StyleContext.h"
25 #include "CharacterSet.h"
26 #include "LexerModule.h"
27 
28 #ifdef SCI_NAMESPACE
29 using namespace Scintilla;
30 #endif
31 
32 /*
33  * Interface
34  */
35 
36 static void ColouriseDocument(
37     unsigned int startPos,
38     int length,
39     int initStyle,
40     WordList *keywordlists[],
41     Accessor &styler);
42 
43 static const char * const spiceWordListDesc[] = {
44     "Keywords",        // SPICE command
45     "Keywords2",    // SPICE functions
46     "Keywords3",    // SPICE params
47     0
48 };
49 
50 LexerModule lmSpice(SCLEX_SPICE, ColouriseDocument, "spice", NULL, spiceWordListDesc);
51 
52 /*
53  * Implementation
54  */
55 
56 static void ColouriseComment(StyleContext& sc, bool& apostropheStartsAttribute);
57 static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute);
58 static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute);
59 static void ColouriseWhiteSpace(StyleContext& sc, bool& apostropheStartsAttribute);
60 static void ColouriseWord(StyleContext& sc, WordList& keywords, WordList& keywords2, WordList& keywords3, bool& apostropheStartsAttribute);
61 
62 static inline bool IsDelimiterCharacter(int ch);
63 static inline bool IsNumberStartCharacter(int ch);
64 static inline bool IsNumberCharacter(int ch);
65 static inline bool IsSeparatorOrDelimiterCharacter(int ch);
66 static inline bool IsWordStartCharacter(int ch);
67 static inline bool IsWordCharacter(int ch);
68 
ColouriseComment(StyleContext & sc,bool &)69 static void ColouriseComment(StyleContext& sc, bool&) {
70     sc.SetState(SCE_SPICE_COMMENTLINE);
71     while (!sc.atLineEnd) {
72         sc.Forward();
73     }
74 }
75 
ColouriseDelimiter(StyleContext & sc,bool & apostropheStartsAttribute)76 static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute) {
77     apostropheStartsAttribute = sc.Match (')');
78     sc.SetState(SCE_SPICE_DELIMITER);
79     sc.ForwardSetState(SCE_SPICE_DEFAULT);
80 }
81 
ColouriseNumber(StyleContext & sc,bool & apostropheStartsAttribute)82 static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute) {
83     apostropheStartsAttribute = true;
84     std::string number;
85     sc.SetState(SCE_SPICE_NUMBER);
86     // Get all characters up to a delimiter or a separator, including points, but excluding
87     // double points (ranges).
88     while (!IsSeparatorOrDelimiterCharacter(sc.ch) || (sc.ch == '.' && sc.chNext != '.')) {
89         number += static_cast<char>(sc.ch);
90         sc.Forward();
91     }
92     // Special case: exponent with sign
93     if ((sc.chPrev == 'e' || sc.chPrev == 'E') &&
94             (sc.ch == '+' || sc.ch == '-')) {
95         number += static_cast<char>(sc.ch);
96         sc.Forward ();
97         while (!IsSeparatorOrDelimiterCharacter(sc.ch)) {
98             number += static_cast<char>(sc.ch);
99             sc.Forward();
100         }
101     }
102     sc.SetState(SCE_SPICE_DEFAULT);
103 }
104 
ColouriseWhiteSpace(StyleContext & sc,bool &)105 static void ColouriseWhiteSpace(StyleContext& sc, bool& ) {
106     sc.SetState(SCE_SPICE_DEFAULT);
107     sc.ForwardSetState(SCE_SPICE_DEFAULT);
108 }
109 
ColouriseWord(StyleContext & sc,WordList & keywords,WordList & keywords2,WordList & keywords3,bool & apostropheStartsAttribute)110 static void ColouriseWord(StyleContext& sc, WordList& keywords, WordList& keywords2, WordList& keywords3, bool& apostropheStartsAttribute) {
111     apostropheStartsAttribute = true;
112     sc.SetState(SCE_SPICE_IDENTIFIER);
113     std::string word;
114     while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
115         word += static_cast<char>(tolower(sc.ch));
116         sc.Forward();
117     }
118     if (keywords.InList(word.c_str())) {
119         sc.ChangeState(SCE_SPICE_KEYWORD);
120         if (word != "all") {
121             apostropheStartsAttribute = false;
122         }
123     }
124     else if (keywords2.InList(word.c_str())) {
125         sc.ChangeState(SCE_SPICE_KEYWORD2);
126         if (word != "all") {
127             apostropheStartsAttribute = false;
128         }
129     }
130     else if (keywords3.InList(word.c_str())) {
131         sc.ChangeState(SCE_SPICE_KEYWORD3);
132         if (word != "all") {
133             apostropheStartsAttribute = false;
134         }
135     }
136     sc.SetState(SCE_SPICE_DEFAULT);
137 }
138 
139 //
140 // ColouriseDocument
141 //
ColouriseDocument(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler)142 static void ColouriseDocument(
143     unsigned int startPos,
144     int length,
145     int initStyle,
146     WordList *keywordlists[],
147     Accessor &styler) {
148     WordList &keywords = *keywordlists[0];
149     WordList &keywords2 = *keywordlists[1];
150     WordList &keywords3 = *keywordlists[2];
151     StyleContext sc(startPos, length, initStyle, styler);
152     int lineCurrent = styler.GetLine(startPos);
153     bool apostropheStartsAttribute = (styler.GetLineState(lineCurrent) & 1) != 0;
154     while (sc.More()) {
155         if (sc.atLineEnd) {
156             // Go to the next line
157             sc.Forward();
158             lineCurrent++;
159             // Remember the line state for future incremental lexing
160             styler.SetLineState(lineCurrent, apostropheStartsAttribute);
161             // Don't continue any styles on the next line
162             sc.SetState(SCE_SPICE_DEFAULT);
163         }
164         // Comments
165         if ((sc.Match('*') && sc.atLineStart) || sc.Match('*','~')) {
166             ColouriseComment(sc, apostropheStartsAttribute);
167         // Whitespace
168         } else if (IsASpace(sc.ch)) {
169             ColouriseWhiteSpace(sc, apostropheStartsAttribute);
170         // Delimiters
171         } else if (IsDelimiterCharacter(sc.ch)) {
172             ColouriseDelimiter(sc, apostropheStartsAttribute);
173         // Numbers
174         } else if (IsADigit(sc.ch) || sc.ch == '#') {
175             ColouriseNumber(sc, apostropheStartsAttribute);
176         // Keywords or identifiers
177         } else {
178             ColouriseWord(sc, keywords, keywords2, keywords3, apostropheStartsAttribute);
179         }
180     }
181     sc.Complete();
182 }
183 
IsDelimiterCharacter(int ch)184 static inline bool IsDelimiterCharacter(int ch) {
185     switch (ch) {
186     case '&':
187     case '\'':
188     case '(':
189     case ')':
190     case '*':
191     case '+':
192     case ',':
193     case '-':
194     case '.':
195     case '/':
196     case ':':
197     case ';':
198     case '<':
199     case '=':
200     case '>':
201     case '|':
202         return true;
203     default:
204         return false;
205     }
206 }
207 
IsNumberCharacter(int ch)208 static inline bool IsNumberCharacter(int ch) {
209     return IsNumberStartCharacter(ch) ||
210            ch == '_' ||
211            ch == '.' ||
212            ch == '#' ||
213            (ch >= 'a' && ch <= 'f') ||
214            (ch >= 'A' && ch <= 'F');
215 }
216 
IsNumberStartCharacter(int ch)217 static inline bool IsNumberStartCharacter(int ch) {
218     return IsADigit(ch);
219 }
220 
IsSeparatorOrDelimiterCharacter(int ch)221 static inline bool IsSeparatorOrDelimiterCharacter(int ch) {
222     return IsASpace(ch) || IsDelimiterCharacter(ch);
223 }
224 
IsWordCharacter(int ch)225 static inline bool IsWordCharacter(int ch) {
226     return IsWordStartCharacter(ch) || IsADigit(ch);
227 }
228 
IsWordStartCharacter(int ch)229 static inline bool IsWordStartCharacter(int ch) {
230     return (isascii(ch) && isalpha(ch)) || ch == '_';
231 }
232