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