1#[cfg(feature = "py")]
2mod py;
3#[expect(unused_imports)]
4#[cfg(not(feature = "tracing"))]
5use log::{debug, error, info, trace, warn};
6#[expect(unused_imports)]
7#[cfg(feature = "tracing")]
8use tracing::{debug, error, info, trace, warn};
9
10extern crate alloc;
11pub mod ast;
12pub mod instance;
13pub mod parser;
14
15pub mod _impl_display;
16mod _impl_hash;
17mod builder;
18mod err;
19mod span;
20
21use alloc::borrow::Cow;
22use ast::ASTBuilder;
23use indexmap::IndexSet;
24pub use span::{FileId, ParsedId};
25use std::collections::HashMap;
26
27#[derive(Debug)]
28pub struct Parsed {
29 pub top_ids: Vec<span::ParsedId>,
30 pub id2idx: HashMap<span::FileId, span::ParsedId>,
31 pub inner: Vec<(span::FileId, ast::ASTBuilder)>,
32}
33#[derive(Debug)]
34pub struct Files {
35 pub inner: Vec<String>,
36}
37
38#[derive(Debug, Clone)]
45pub struct Subckt<'s> {
46 pub name: Cow<'s, str>,
47 pub ports: Vec<Cow<'s, str>>,
49 pub params: Vec<ast::KeyValue<'s>>,
50 pub ast: AST<'s>,
51}
52
53#[derive(Debug, Clone, Default)]
54pub struct AST<'s> {
55 pub subckt: IndexSet<Subckt<'s>>,
56 pub instance: Vec<instance::Instance<'s>>,
57 pub model: Vec<ast::Model<'s>>,
58 pub param: Vec<ast::KeyValue<'s>>,
59 pub option: Vec<(Cow<'s, str>, Option<ast::Value<'s>>)>,
60 pub init_condition: Vec<(Cow<'s, str>, ast::Value<'s>, Option<Cow<'s, str>>)>,
65 pub nodeset: Vec<(Cow<'s, str>, ast::Value<'s>, Option<Cow<'s, str>>)>,
70 pub general: Vec<ast::General<'s>>,
71 pub data: Vec<ast::Data<'s>>,
72 pub unknwon: Vec<ast::Unknwon<'s>>,
73}
74
75impl ast::ASTBuilder {
116 #[expect(clippy::too_many_arguments)]
117 fn build<'s>(
118 &self,
119 ast: &mut AST<'s>,
120 has_err: &mut bool,
121 file_id: &span::FileId,
122 parsed_id: span::ParsedId,
123 files: &'s Files,
124 parsed_id2idx: &HashMap<FileId, ParsedId>,
125 parsed_inner: &Vec<(FileId, ASTBuilder)>,
126 ) {
127 use builder::Builder as _;
128 fn build_local<'s>(
129 local_ast: &ast::LocalAST,
130 ast: &mut AST<'s>,
131 has_err: &mut bool,
132 file: &'s str,
133 file_id: &span::FileId,
134 parsed_id: span::ParsedId,
135 files: &'s Files,
136 parsed_id2idx: &HashMap<FileId, ParsedId>,
137 parsed_inner: &Vec<(FileId, ASTBuilder)>,
138 ) {
139 fn build_subckt<'s>(
140 s: &ast::SubcktBuilder,
141 has_err: &mut bool,
142 file: &'s str,
143 file_id: &span::FileId,
144 parsed_id: span::ParsedId,
145 files: &'s Files,
146 parsed_id2idx: &HashMap<FileId, ParsedId>,
147 parsed_inner: &Vec<(FileId, ASTBuilder)>,
148 ) -> Subckt<'s> {
149 let mut ast = AST::default();
150 s.ast.build(
151 &mut ast,
152 has_err,
153 file_id,
154 parsed_id,
155 files,
156 parsed_id2idx,
157 parsed_inner,
158 );
159 Subckt {
160 name: s.name.build(file),
161 ports: s.ports.build(file),
162 params: s.params.build(file),
163 ast,
164 }
165 }
166 ast.subckt.extend(local_ast.subckt.iter().map(|s| {
167 build_subckt(
168 s,
169 has_err,
170 file,
171 file_id,
172 parsed_id,
173 files,
174 parsed_id2idx,
175 parsed_inner,
176 )
177 }));
178 ast.instance
179 .extend(local_ast.instance.iter().map(|b| b.build(file)));
180 ast.model
181 .extend(local_ast.model.iter().map(|b| b.build(file)));
182 ast.param
183 .extend(local_ast.param.iter().map(|b| b.build(file)));
184 ast.option
185 .extend(local_ast.option.iter().map(|b| b.build(file)));
186 ast.general
187 .extend(local_ast.general.iter().map(|b| b.build(file)));
188 ast.data
189 .extend(local_ast.data.iter().map(|b| b.build(file)));
190 ast.init_condition
191 .extend(local_ast.init_condition.iter().map(|b| b.build(file)));
192 ast.nodeset
193 .extend(local_ast.nodeset.iter().map(|b| b.build(file)));
194 ast.unknwon
195 .extend(local_ast.unknwon.iter().map(|b| b.build(file)));
196 for e in &local_ast.errors {
197 e.report(has_err, file_id, file);
198 }
199 }
200 let file = &files.inner[parsed_id.0];
201 for seg in &self.segments {
202 match seg {
203 ast::Segment::Local(local_ast) => {
204 build_local(
205 local_ast,
206 ast,
207 has_err,
208 file,
209 file_id,
210 parsed_id,
211 files,
212 parsed_id2idx,
213 parsed_inner,
214 );
215 }
216 ast::Segment::Include(ast_res) => {
217 let ast_res = ast_res.get().unwrap();
218 match ast_res {
219 Ok(parsed_id) => {
220 let (file_id, _ast) = &parsed_inner[parsed_id.0];
221 _ast.build(
222 ast,
223 has_err,
224 file_id,
225 *parsed_id,
226 files,
227 parsed_id2idx,
228 parsed_inner,
229 );
230 }
231 Err(e) => {
232 e.report(has_err, file_id, file);
233 }
234 }
235 }
236 }
237 }
238 }
239}
240
241impl Files {
242 #[inline]
243 pub fn build(&self, parsed: Parsed) -> (AST<'_>, bool) {
244 let mut ast = AST::default();
245 let mut has_err = false;
246 for top_id in parsed.top_ids {
247 let (file_id, _ast) = &parsed.inner[top_id.0];
248 _ast.build(
249 &mut ast,
250 &mut has_err,
251 file_id,
252 top_id,
253 self,
254 &parsed.id2idx,
255 &parsed.inner,
256 );
257 }
258 (ast, has_err)
259 }
260}