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