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