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