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<sor<bracedExpr,
108 not_one<'}'>>>,
109 one<'}'>> {};
110
111 // Ngspice has some heuristic logic to allow + and - in tokens. We replicate that here.
112 struct tokenStart : seq<opt<one<'+', '-'>>,
113 opt<seq<star<sor<tao::pegtl::digit,
114 one<'.'>>>,
115 one<'e', 'E'>,
116 opt<one<'+', '-'>>>>> {};
117
118 struct token : seq<tokenStart,
119 star<not_at<eolf>,
120 not_at<backslashContinuation>,
121 not_one<' ', '\t', '=', '(', ')', ',', ';'>>> {};
122
123 // Param names cannot be `token` because LTspice models contain spurious values without
124 // parameter names, which we need to skip, and because tokens can include a very limited
125 // subset of un-braced expressions
126 struct param : identifier {};
127 struct paramValue : sor<bracedExpr,
128 vectorExpr,
129 token> {};
130
131 struct paramValuePair : seq<param,
132 sep,
133 paramValue> {};
134 struct paramValuePairs : list<paramValuePair, sep> {};
135 struct dotModelAko : seq<opt<sep>,
136 if_must<seq<TAO_PEGTL_ISTRING( ".model" ),
137 sep,
138 modelName,
139 sep,
140 TAO_PEGTL_ISTRING( "ako:" )>,
141 opt<sep>,
142 modelName,
143 opt<sep,
144 dotModelType>,
145 opt<sep,
146 paramValuePairs>,
147 opt<sep>,
148 newline>> {};
149
150 struct dotModel : seq<opt<sep>,
151 if_must<TAO_PEGTL_ISTRING( ".model" ),
152 sep,
153 modelName,
154 sep,
155 dotModelType,
156 opt<sep,
157 paramValuePairs>,
158 opt<sep>,
159 newline>> {};
160
161
162
163 struct dotSubcktParamValuePair : seq<param,
164 // TODO: Check if these `star<space>`s match Ngspice's
165 // behavior.
166 star<space>,
167 opt<plusContinuation>,
168 one<'='>,
169 opt<plusContinuation>,
170 star<space>,
171 paramValue> {};
172 struct dotSubcktParamValuePairs : list<dotSubcktParamValuePair, sep> {};
173 struct dotSubcktParams : seq<opt<TAO_PEGTL_ISTRING( "params:" ),
174 opt<sep>>,
175 dotSubcktParamValuePairs> {};
176 struct dotSubcktPinName : seq<not_at<dotSubcktParams>,
177 plus<not_at<space>, any>> {};
178 struct dotSubcktPinSequence : list<dotSubcktPinName, sep> {};
179 struct dotSubcktEnd : seq<TAO_PEGTL_ISTRING( ".ends" ),
180 until<newline>> {};
181 struct spiceUnit;
182 struct dotSubckt : seq<opt<sep>,
183 if_must<TAO_PEGTL_ISTRING( ".subckt" ),
184 sep,
185 modelName,
186 opt<sep,
187 dotSubcktPinSequence>,
188 opt<sep,
189 dotSubcktParams>,
190 opt<sep>,
191 newline,
192 until<dotSubcktEnd,
193 spiceUnit>>> {};
194
195
196 struct modelUnit : seq<star<commentLine>,
197 sor<dotModelAko,
198 dotModel,
199 dotSubckt>> {};
200
201
202 // Intentionally no if_must<>.
203 struct dotControl : seq<opt<sep>,
204 TAO_PEGTL_ISTRING( ".control" ),
205 until<TAO_PEGTL_ISTRING( ".endc" )>,
206 until<newline>> {};
207
208
209 struct dotTitleTitle : star<not_at<newline>, any> {};
210 // Intentionally no if_must<>.
211 struct dotTitle : seq<opt<sep>,
212 TAO_PEGTL_ISTRING( ".title" ),
213 sep,
214 dotTitleTitle,
215 newline> {};
216
217
218 struct dotIncludePathWithoutQuotes : star<not_one<'"'>> {};
219 struct dotIncludePathWithoutApostrophes : star<not_one<'\''>> {};
220 struct dotIncludePath : star<not_at<newline>, any> {};
221 // Intentionally no if_must<>.
222 struct dotInclude : seq<opt<sep>,
223 TAO_PEGTL_ISTRING( ".inc" ),
224 star<not_at<garbageOrEolf>, any>,
225 sep,
226 sor<seq<one<'\"'>,
227 dotIncludePathWithoutQuotes,
228 one<'\"'>>,
229 seq<one<'\''>,
230 dotIncludePathWithoutApostrophes,
231 one<'\''>>,
232 dotIncludePath>,
233 opt<sep>,
234 newline> {};
235
236
237 // Intentionally no if_must<>.
238 struct dotLine : seq<opt<sep>,
239 one<'.'>,
240 until<newline>> {};
241
242
243 // Intentionally no if_must<>.
244 struct kLine : seq<opt<sep>,
245 one<'K'>,
246 until<sep>,
247 one<'L'>,
248 until<sep>,
249 one<'L'>,
250 until<sep>,
251 until<newline>> {};
252
253 struct unknownLine : seq<plus<not_at<newline>, any>,
254 until<newline>> {};
255
256
257 struct spiceUnit : sor<modelUnit,
258 dotControl,
259 dotTitle,
260 dotInclude,
261 dotLine,
262 kLine,
263 eol, // Empty line. This is necessary to terminate on EOF.
264 unknownLine> {};
265 struct spiceUnitGrammar : must<spiceUnit> {};
266
267
268 struct spiceSource : star<spiceUnit> {};
269 struct spiceSourceGrammar : must<spiceSource> {};
270
271
272 template <typename> inline constexpr const char* errorMessage = nullptr;
273 template <> inline constexpr auto errorMessage<newline> =
274 "expected newline";
275 template <> inline constexpr auto errorMessage<sep> =
276 "expected token separator (one or more whitespace, parenthesis, '=', ',', or line continuation)";
277 template <> inline constexpr auto errorMessage<opt<sep>> =
278 "";
279 template <> inline constexpr auto errorMessage<modelName> =
280 "expected model name";
281 template <> inline constexpr auto errorMessage<dotModelType> =
282 "expected model type";
283 template <> inline constexpr auto errorMessage<opt<sep, dotModelType>> =
284 "";
285 template <> inline constexpr auto errorMessage<opt<sep, paramValuePairs>> =
286 "";
287 template <> inline constexpr auto errorMessage<opt<sep, dotSubcktPinSequence>> =
288 "";
289 template <> inline constexpr auto errorMessage<opt<sep, dotSubcktParams>> =
290 "";
291 template <> inline constexpr auto errorMessage<until<dotSubcktEnd, spiceUnit>> =
292 "expected (possibly empty) sequence of Spice lines followed by an .ends line";
293 template <> inline constexpr auto errorMessage<spiceUnit> =
294 "expected Spice directive, item, subcircuit definitions, or empty or commented-out line";
295 template <> inline constexpr auto errorMessage<spiceSource> =
296 "expected zero or more Spice directives, items, subcircuit definitions, or empty or commented-out lines";
297
298 // We create a custom PEGTL control to modify the parser error messages.
299 struct error
300 {
301 template <typename Rule> static constexpr bool raise_on_failure = false;
302 template <typename Rule> static constexpr auto message = errorMessage<Rule>;
303 };
304
305 template <typename Rule> using control = must_if<error>::control<Rule>;
306}
307
308#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