KiCad PCB EDA Suite
Loading...
Searching...
No Matches
spice_grammar.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2022 Mikolaj Wielgus
5 * Copyright (C) 2022-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * https://www.gnu.org/licenses/gpl-3.0.html
20 * or you may search the http://www.gnu.org website for the version 3 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#ifndef SPICE_GRAMMAR_H
26#define SPICE_GRAMMAR_H
27
28#include <sim/sim_value.h>
29
30
32{
33 using namespace SIM_VALUE_GRAMMAR;
34
35
36 struct garbage : plus<one<' ', '\t', '=', '(', ')', ','>> {};
37 struct leaders : plus<one<' ', '\t'>> {};
38 struct trailers : plus<one<' ', '\t', '\v', '\f'>> {};
39
40 struct garbageOrEolf : sor<garbage, eolf> {};
41
42 // NOTE: In Ngspice, a '$' opening a comment must be preceded by ' ', ',', or '\t'. We don't
43 // implement that here - this may cause problems in the future.
44 // Ngspice supports '//' for comments.
45 struct eolfCommentStart : sor<one<';', '$'>,
46 string<'/', '/'>> {};
47
48 struct eolfComment : seq<eolfCommentStart,
49 until<eolf>> {};
50
51
52 struct commentLine : seq<opt<garbage>,
53 one<'*'>,
54 until<eolf>> {};
55
56
57 struct newline : seq<sor<eolf,
58 eolfComment>,
59 not_at<one<'+'>>> {};
60
61 struct backslashContinuation : seq<string<'\\', '\\'>,
62 opt<trailers>,
63 eolf> {};
64
65 struct commentBackslashContinuation : seq<eolfCommentStart,
66 seq<star<not_at<eolf>,
67 not_at<string<'\\', '\\'>,
68 opt<trailers>,
69 eolf>,
70 any>,
71 string<'\\', '\\'>,
72 opt<trailers>,
73 eolf>> {};
74
75
76 struct plusContinuation : seq<sor<eolf,
77 eolfComment>,
78 star<commentLine>,
79 opt<leaders>,
80 one<'+'>> {};
81
82 struct continuation : seq<opt<garbage>,
83 sor<backslashContinuation,
84 commentBackslashContinuation,
85 plusContinuation>,
86 opt<garbage>> {};
87
88
89 // Token separator.
90 struct sep : sor<plus<continuation>,
91 garbage> {};
92
93 struct modelName : plus<not_at<garbageOrEolf>, any> {};
94
95 struct dotModelType : sor<// VDMOS models have a special syntax.
96 seq<TAO_PEGTL_ISTRING( "vdmos" ),
97 sep,
98 sor<TAO_PEGTL_ISTRING( "nchan" ),
99 TAO_PEGTL_ISTRING( "pchan" )>>,
100 plus<not_at<garbageOrEolf>, any>> {};
101
102 struct vectorExpr : seq<one<'['>,
103 star<not_one<']'>>,
104 one<']'>> {};
105
106 struct bracedExpr : seq<one<'{'>,
107 star<not_one<'}'>>,
108 one<'}'>> {};
109
110 // Ngspice has some heuristic logic to allow + and - in tokens. We replicate that here.
111 struct tokenStart : seq<opt<one<'+', '-'>>,
112 opt<seq<star<sor<tao::pegtl::digit,
113 one<'.'>>>,
114 one<'e', 'E'>,
115 opt<one<'+', '-'>>>>> {};
116
117 struct token : seq<tokenStart,
118 star<not_at<eolf>,
119 not_at<backslashContinuation>,
120 not_one<' ', '\t', '=', '(', ')', ',', ';'>>> {};
121
122 // Param names cannot be `token` because LTspice models contain spurious values without
123 // parameter names, which we need to skip, and because tokens can include a very limited
124 // subset of un-braced expressions
125 struct param : identifier {};
126 struct paramValue : sor<bracedExpr,
127 vectorExpr,
128 token> {};
129
130 struct paramValuePair : seq<param,
131 sep,
132 paramValue> {};
133 struct paramValuePairs : list<paramValuePair, sep> {};
134
135 struct cplSep : opt<one<' '>> {};
136 struct cplParamValue : sor<list<bracedExpr, cplSep>,
137 vectorExpr,
138 list<token, cplSep>> {};
139 struct cplParamValuePair : seq<param,
140 sep,
141 cplParamValue> {};
142 struct cplParamValuePairs : list<cplParamValuePair, sep> {};
143
144 struct dotModelAko : seq<opt<sep>,
145 if_must<seq<TAO_PEGTL_ISTRING( ".model" ),
146 sep,
147 modelName,
148 sep,
149 TAO_PEGTL_ISTRING( "ako:" )>,
150 opt<sep>,
151 modelName,
152 opt<sep,
153 dotModelType>,
154 opt<sep,
155 paramValuePairs>,
156 opt<sep>,
157 newline>> {};
158
159 struct dotModelCPL : seq<opt<sep>,
160 if_must<seq<TAO_PEGTL_ISTRING( ".model" ),
161 sep,
162 modelName,
163 sep,
164 TAO_PEGTL_ISTRING( "CPL" )>,
165 opt<sep,
166 cplParamValuePairs>,
167 opt<sep>,
168 newline>> {};
169
170 struct dotModel : seq<opt<sep>,
171 if_must<TAO_PEGTL_ISTRING( ".model" ),
172 sep,
173 modelName,
174 sep,
175 dotModelType,
176 opt<sep,
177 paramValuePairs>,
178 opt<sep>,
179 newline>> {};
180
181 struct dotSubcktParamValuePair : seq<param,
182 // TODO: Check if these `star<space>`s match Ngspice's
183 // behavior.
184 star<space>,
185 opt<plusContinuation>,
186 one<'='>,
187 opt<plusContinuation>,
188 star<space>,
189 paramValue> {};
190 struct dotSubcktParamValuePairs : list<dotSubcktParamValuePair, sep> {};
191 struct dotSubcktParams : seq<opt<TAO_PEGTL_ISTRING( "params:" ),
192 opt<sep>>,
193 dotSubcktParamValuePairs> {};
194 struct dotSubcktPinName : seq<not_at<dotSubcktParams>,
195 plus<not_at<space>, any>> {};
196 struct dotSubcktPinSequence : list<dotSubcktPinName, sep> {};
197 struct dotSubcktEnd : seq<opt<sep>,
198 TAO_PEGTL_ISTRING( ".ends" ),
199 until<newline>> {};
200 struct spiceUnit;
201 struct dotSubckt : seq<opt<sep>,
202 if_must<TAO_PEGTL_ISTRING( ".subckt" ),
203 sep,
204 modelName,
205 opt<sep,
206 dotSubcktPinSequence>,
207 opt<sep,
208 dotSubcktParams>,
209 opt<sep>,
210 newline,
211 until<dotSubcktEnd,
212 spiceUnit>>> {};
213
214
215 struct modelUnit : seq<star<commentLine>,
216 sor<dotModelAko,
217 dotModelCPL,
218 dotModel,
219 dotSubckt>> {};
220
221
222 // Intentionally no if_must<>.
223 struct dotControl : seq<opt<sep>,
224 TAO_PEGTL_ISTRING( ".control" ),
225 until<TAO_PEGTL_ISTRING( ".endc" )>,
226 until<newline>> {};
227
228
229 struct dotTitleTitle : star<not_at<newline>, any> {};
230 // Intentionally no if_must<>.
231 struct dotTitle : seq<opt<sep>,
232 TAO_PEGTL_ISTRING( ".title" ),
233 sep,
234 dotTitleTitle,
235 newline> {};
236
237
238 struct dotIncludePathWithoutQuotes : star<not_one<'"'>> {};
239 struct dotIncludePathWithoutApostrophes : star<not_one<'\''>> {};
240 struct dotIncludePath : star<not_at<newline>, any> {};
241 // Intentionally no if_must<>.
242 struct dotInclude : seq<opt<sep>,
243 TAO_PEGTL_ISTRING( ".inc" ),
244 star<not_at<garbageOrEolf>, any>,
245 sep,
246 sor<seq<one<'\"'>,
247 dotIncludePathWithoutQuotes,
248 one<'\"'>>,
249 seq<one<'\''>,
250 dotIncludePathWithoutApostrophes,
251 one<'\''>>,
252 dotIncludePath>,
253 opt<sep>,
254 newline> {};
255
256
257 // Intentionally no if_must<>.
258 struct dotLine : seq<opt<sep>,
259 one<'.'>,
260 until<newline>> {};
261
262
263 // Intentionally no if_must<>.
264 struct kLine : seq<opt<sep>,
265 one<'K'>,
266 until<sep>,
267 one<'L'>,
268 until<sep>,
269 one<'L'>,
270 until<sep>,
271 until<newline>> {};
272
273 struct unknownLine : seq<plus<not_at<newline>, any>,
274 until<newline>> {};
275
276
277 struct spiceUnit : sor<modelUnit,
278 dotControl,
279 dotTitle,
280 dotInclude,
281 dotLine,
282 kLine,
283 eol, // Empty line. This is necessary to terminate on EOF.
284 unknownLine> {};
285 struct spiceUnitGrammar : must<spiceUnit> {};
286
287
288 struct spiceSource : star<spiceUnit> {};
289 struct spiceSourceGrammar : must<spiceSource> {};
290
291
292 template <typename> inline constexpr const char* errorMessage = nullptr;
293 template <> inline constexpr auto errorMessage<newline> =
294 "expected newline";
295 template <> inline constexpr auto errorMessage<sep> =
296 "expected token separator (one or more whitespace, parenthesis, '=', ',', or line continuation)";
297 template <> inline constexpr auto errorMessage<opt<sep>> =
298 "";
299 template <> inline constexpr auto errorMessage<modelName> =
300 "expected model name";
301 template <> inline constexpr auto errorMessage<dotModelType> =
302 "expected model type";
303 template <> inline constexpr auto errorMessage<opt<sep, dotModelType>> =
304 "";
305 template <> inline constexpr auto errorMessage<opt<sep, paramValuePairs>> =
306 "";
307 template <> inline constexpr auto errorMessage<opt<sep, cplParamValuePairs>> =
308 "";
309 template <> inline constexpr auto errorMessage<opt<sep, dotSubcktPinSequence>> =
310 "";
311 template <> inline constexpr auto errorMessage<opt<sep, dotSubcktParams>> =
312 "";
313 template <> inline constexpr auto errorMessage<until<dotSubcktEnd, spiceUnit>> =
314 "expected (possibly empty) sequence of Spice lines followed by an .ends line";
315 template <> inline constexpr auto errorMessage<spiceUnit> =
316 "expected Spice directive, item, subcircuit definitions, or empty or commented-out line";
317 template <> inline constexpr auto errorMessage<spiceSource> =
318 "expected zero or more Spice directives, items, subcircuit definitions, or empty or commented-out lines";
319
320 // We create a custom PEGTL control to modify the parser error messages.
321 struct error
322 {
323 template <typename Rule> static constexpr bool raise_on_failure = false;
324 template <typename Rule> static constexpr auto message = errorMessage<Rule>;
325 };
326
327 template <typename Rule> using control = must_if<error>::control<Rule>;
328}
329
330#endif // SPICE_GRAMMAR_H
constexpr auto errorMessage< dotModelType >
constexpr auto errorMessage< spiceUnit >
constexpr auto errorMessage< sep >
constexpr auto errorMessage< spiceSource >
must_if< error >::control< Rule > control
constexpr auto errorMessage< modelName >
constexpr auto errorMessage< newline >
constexpr const char * errorMessage
static constexpr auto message
static constexpr bool raise_on_failure