1#[cfg(feature = "py")]
2mod py;
3#[expect(unused_imports)]
4#[cfg(not(feature = "tracing"))]
5use log::{debug, error, info, trace, warn};
6
7#[expect(unused_imports)]
8#[cfg(feature = "tracing")]
9use tracing::{debug, error, info, trace, warn};
10
11fn spawn<F>(future: F) -> tokio::task::JoinHandle<F::Output>
12where
13 F: Future + Send + 'static,
14 F::Output: Send + 'static,
15{
16 #[cfg(feature = "tracing")]
17 {
18 use tracing::instrument::WithSubscriber;
19 tokio::spawn(future.with_current_subscriber())
20 }
21 #[cfg(not(feature = "tracing"))]
22 {
23 tokio::spawn(future)
24 }
25}
26
27extern crate alloc;
28pub mod ast;
29pub mod instance;
30pub mod parser;
31pub mod utlis;
32
33pub mod _impl_display;
34mod builder;
35mod err;
36mod span;
37
38use alloc::borrow::Cow;
39use ast::ASTBuilder;
40use indexmap::IndexMap;
41pub use span::{FileId, ParsedId};
42use std::collections::HashMap;
43
44#[derive(Debug)]
45pub struct Parsed {
46 pub top_ids: Vec<span::ParsedId>,
47 pub id2idx: HashMap<span::FileId, span::ParsedId>,
48 pub inner: Vec<(span::FileId, ast::ASTBuilder)>,
49}
50#[derive(Debug)]
51pub struct Files {
52 pub inner: Vec<String>,
53}
54
55#[derive(Debug, Clone)]
62pub struct Subckt<'s> {
63 pub name: Cow<'s, str>,
64 pub ports: Vec<Cow<'s, str>>,
66 pub params: Vec<ast::KeyValue<'s>>,
67 pub ast: AST<'s>,
68}
69
70#[derive(Debug, Clone, Default)]
71pub struct AST<'s> {
72 pub subckt: IndexMap<String, Subckt<'s>>,
73 pub instance: Vec<instance::Instance<'s>>,
74 pub model: Vec<ast::Model<'s>>,
75 pub param: Vec<ast::KeyValue<'s>>,
76 pub option: Vec<(Cow<'s, str>, Option<ast::Value<'s>>)>,
77 pub init_condition: Vec<(Cow<'s, str>, ast::Value<'s>, Option<Cow<'s, str>>)>,
82 pub nodeset: Vec<(Cow<'s, str>, ast::Value<'s>, Option<Cow<'s, str>>)>,
87 pub general: Vec<ast::General<'s>>,
88 pub data: Vec<ast::Data<'s>>,
89 pub unknwon: Vec<ast::Unknwon<'s>>,
90}
91
92impl ast::ASTBuilder {
133 #[expect(clippy::too_many_arguments)]
134 fn build<'s>(
135 &self,
136 ast: &mut AST<'s>,
137 has_err: &mut bool,
138 file_id: &span::FileId,
139 parsed_id: span::ParsedId,
140 files: &'s Files,
141 parsed_id2idx: &HashMap<FileId, ParsedId>,
142 parsed_inner: &Vec<(FileId, ASTBuilder)>,
143 ) {
144 use builder::Builder as _;
145 fn build_local<'s>(
146 local_ast: &ast::LocalAST,
147 ast: &mut AST<'s>,
148 has_err: &mut bool,
149 file: &'s str,
150 file_id: &span::FileId,
151 parsed_id: span::ParsedId,
152 files: &'s Files,
153 parsed_id2idx: &HashMap<FileId, ParsedId>,
154 parsed_inner: &Vec<(FileId, ASTBuilder)>,
155 ) {
156 fn build_subckt<'s>(
157 s: &ast::SubcktBuilder,
158 has_err: &mut bool,
159 file: &'s str,
160 file_id: &span::FileId,
161 parsed_id: span::ParsedId,
162 files: &'s Files,
163 parsed_id2idx: &HashMap<FileId, ParsedId>,
164 parsed_inner: &Vec<(FileId, ASTBuilder)>,
165 ) -> Subckt<'s> {
166 let mut ast = AST::default();
167 s.ast.build(
168 &mut ast,
169 has_err,
170 file_id,
171 parsed_id,
172 files,
173 parsed_id2idx,
174 parsed_inner,
175 );
176 Subckt {
177 name: s.name.build(file),
178 ports: s.ports.build(file),
179 params: s.params.build(file),
180 ast,
181 }
182 }
183 ast.subckt.extend(local_ast.subckt.iter().map(|s| {
184 let subckt = build_subckt(
185 s,
186 has_err,
187 file,
188 file_id,
189 parsed_id,
190 files,
191 parsed_id2idx,
192 parsed_inner,
193 );
194 (subckt.name.to_lowercase(), subckt)
195 }));
196 ast.instance
197 .extend(local_ast.instance.iter().map(|b| b.build(file)));
198 ast.model
199 .extend(local_ast.model.iter().map(|b| b.build(file)));
200 ast.param
201 .extend(local_ast.param.iter().map(|b| b.build(file)));
202 ast.option
203 .extend(local_ast.option.iter().map(|b| b.build(file)));
204 ast.general
205 .extend(local_ast.general.iter().map(|b| b.build(file)));
206 ast.data
207 .extend(local_ast.data.iter().map(|b| b.build(file)));
208 ast.init_condition
209 .extend(local_ast.init_condition.iter().map(|b| b.build(file)));
210 ast.nodeset
211 .extend(local_ast.nodeset.iter().map(|b| b.build(file)));
212 ast.unknwon
213 .extend(local_ast.unknwon.iter().map(|b| b.build(file)));
214 for e in &local_ast.errors {
215 e.report(has_err, file_id, file);
216 }
217 }
218 let file = &files.inner[parsed_id.0];
219 for seg in &self.segments {
220 match seg {
221 ast::Segment::Local(local_ast) => {
222 build_local(
223 local_ast,
224 ast,
225 has_err,
226 file,
227 file_id,
228 parsed_id,
229 files,
230 parsed_id2idx,
231 parsed_inner,
232 );
233 }
234 ast::Segment::Include(ast_res) => {
235 let ast_res = ast_res.get().unwrap();
236 match ast_res {
237 Ok(parsed_id) => {
238 let (file_id, _ast) = &parsed_inner[parsed_id.0];
239 _ast.build(
240 ast,
241 has_err,
242 file_id,
243 *parsed_id,
244 files,
245 parsed_id2idx,
246 parsed_inner,
247 );
248 }
249 Err(e) => {
250 e.report(has_err, file_id, file);
251 }
252 }
253 }
254 }
255 }
256 }
257}
258
259impl Files {
260 #[inline]
261 pub fn build(&self, parsed: Parsed) -> (AST<'_>, bool) {
262 let mut ast = AST::default();
263 let mut has_err = false;
264 for top_id in parsed.top_ids {
265 let (file_id, _ast) = &parsed.inner[top_id.0];
266 _ast.build(
267 &mut ast,
268 &mut has_err,
269 file_id,
270 top_id,
271 self,
272 &parsed.id2idx,
273 &parsed.inner,
274 );
275 }
276 (ast, has_err)
277 }
278}