1mod _impl_display;
2mod builder;
3mod err;
4pub mod instance;
5pub mod parser;
6
7use alloc::borrow::Cow;
8use builder::{
9 Builder as _,
10 span::{FileId, ParsedId},
11};
12use err::{ParseError, ParseErrorInner};
13use std::collections::HashMap;
14
15#[derive(Debug)]
16pub struct Parsed {
17 pub top_id: ParsedId,
18 pub id2idx: HashMap<FileId, ParsedId>,
19 pub inner: Vec<(FileId, builder::AST)>,
20}
21#[derive(Debug)]
22pub struct Files {
23 pub inner: Vec<String>,
24}
25
26#[derive(Debug, Clone)]
27pub enum Value<'s> {
28 Num(f64),
29 Expr(Cow<'s, str>),
30}
31
32#[derive(Debug, Clone)]
33pub struct KeyValue<'s> {
34 pub k: Cow<'s, str>,
35 pub v: Value<'s>,
36}
37
38#[derive(Debug, Clone)]
39pub enum Token<'s> {
40 KV(KeyValue<'s>),
41 Value(Value<'s>),
42 V(Cow<'s, str>),
43 I(Cow<'s, str>),
44}
45
46#[derive(Debug, Clone)]
53pub struct Subckt<'s> {
54 pub name: Cow<'s, str>,
55 pub ports: Vec<Cow<'s, str>>,
57 pub params: Vec<KeyValue<'s>>,
58 pub ast: AST<'s>,
59}
60
61#[derive(Debug, Clone)]
62pub struct General<'s> {
63 pub cmd: builder::GeneralCmd,
64 pub tokens: Vec<Token<'s>>,
65}
66
67#[derive(Debug, Clone)]
68pub struct Unknwon<'s> {
69 pub cmd: Cow<'s, str>,
70 pub tokens: Vec<Token<'s>>,
71}
72
73#[derive(Debug, Clone)]
74pub struct Model<'s> {
75 pub name: Cow<'s, str>,
76 pub model_type: ModelType<'s>,
77 pub params: Vec<KeyValue<'s>>,
78}
79
80#[derive(Debug, Clone)]
81pub struct Data<'s> {
82 pub name: Cow<'s, str>,
83 pub values: DataValues<'s>,
84}
85#[derive(Debug, Clone)]
86pub enum DataValues<'s> {
87 InlineExpr {
88 params: Vec<Cow<'s, str>>,
89 values: Vec<Value<'s>>,
90 },
91 InlineNum {
92 params: Vec<Cow<'s, str>>,
93 values: Vec<f64>,
94 },
95 MER(),
98 LAM(),
100}
101pub struct DataValuesCsv<'s, 'a>(pub(crate) &'a DataValues<'s>);
102impl<'s> DataValues<'s> {
103 pub fn csv(&self) -> DataValuesCsv<'s, '_> {
104 DataValuesCsv(self)
105 }
106}
107#[expect(clippy::upper_case_acronyms)]
108#[derive(Debug, Clone)]
109pub enum ModelType<'s> {
110 AMP,
112 C,
114 CORE,
116 D,
118 L,
120 NJF,
122 NMOS,
124 NPN,
126 OPT,
128 PJF,
130 PMOS,
132 PNP,
134 R,
136 U,
138 W,
140 S,
142 Unknown(Cow<'s, str>),
143}
144#[expect(clippy::upper_case_acronyms)]
145#[derive(Debug, Clone, Default)]
146pub struct AST<'s> {
147 pub subckt: Vec<Subckt<'s>>,
148 pub instance: Vec<instance::Instance<'s>>,
149 pub model: Vec<Model<'s>>,
150 pub param: Vec<KeyValue<'s>>,
151 pub option: Vec<(Cow<'s, str>, Option<Value<'s>>)>,
152 pub init_condition: Vec<(Cow<'s, str>, Value<'s>, Option<Cow<'s, str>>)>,
157 pub general: Vec<General<'s>>,
158 pub data: Vec<Data<'s>>,
159 pub unknwon: Vec<Unknwon<'s>>,
160}
161
162impl builder::AST {
163 #[expect(clippy::too_many_arguments)]
164 fn build<'s>(
165 &self,
166 ast: &mut AST<'s>,
167 has_err: &mut bool,
168 file_id: &FileId,
169 parsed_id: ParsedId,
170 files: &'s Files,
171 parsed: &Parsed,
172 ) {
173 fn build_local<'s>(
174 local_ast: &builder::LocalAST,
175 ast: &mut AST<'s>,
176 has_err: &mut bool,
177 file: &'s str,
178 file_id: &FileId,
179 parsed_id: ParsedId,
180 files: &'s Files,
181 parsed: &Parsed,
182 ) {
183 fn build_subckt<'s>(
184 s: &builder::Subckt,
185 has_err: &mut bool,
186 file: &'s str,
187 file_id: &FileId,
188 parsed_id: ParsedId,
189 files: &'s Files,
190 parsed: &Parsed,
191 ) -> Subckt<'s> {
192 let mut ast = AST::default();
193 s.ast
194 .build(&mut ast, has_err, file_id, parsed_id, files, parsed);
195 Subckt {
196 name: s.name.build(file),
197 ports: s.ports.build(file),
198 params: s.params.build(file),
199 ast,
200 }
201 }
202 ast.subckt.extend(
203 local_ast
204 .subckt
205 .iter()
206 .map(|s| build_subckt(s, has_err, file, file_id, parsed_id, files, parsed)),
207 );
208 ast.instance
209 .extend(local_ast.instance.iter().map(|b| b.build(file)));
210 ast.model
211 .extend(local_ast.model.iter().map(|b| b.build(file)));
212 ast.param
213 .extend(local_ast.param.iter().map(|b| b.build(file)));
214 ast.option
215 .extend(local_ast.option.iter().map(|b| b.build(file)));
216 ast.general
217 .extend(local_ast.general.iter().map(|b| b.build(file)));
218 ast.data
219 .extend(local_ast.data.iter().map(|b| b.build(file)));
220 ast.init_condition
221 .extend(local_ast.init_condition.iter().map(|b| b.build(file)));
222 ast.unknwon
223 .extend(local_ast.unknwon.iter().map(|b| b.build(file)));
224 for e in &local_ast.errors {
225 e.report(has_err, file_id, file);
226 }
227 }
228 let file = &files.inner[parsed_id.0];
229 for seg in &self.segments {
230 match seg {
231 builder::Segment::Local(local_ast) => {
232 build_local(
233 local_ast, ast, has_err, file, file_id, parsed_id, files, parsed,
234 );
235 }
236 builder::Segment::Include(ast_res) => {
237 let ast_res = ast_res.get().unwrap();
238 match ast_res {
239 Ok(parsed_id) => {
240 let (file_id, _ast) = &parsed.inner[parsed_id.0];
241 _ast.build(ast, has_err, file_id, *parsed_id, files, parsed);
242 }
243 Err(e) => {
244 e.report(has_err, file_id, file);
245 }
246 }
247 }
248 }
249 }
250 }
251}
252
253impl Files {
254 #[inline]
255 pub fn build(&self, parsed: Parsed) -> (AST<'_>, bool) {
256 let mut ast = AST::default();
257 let mut has_err = false;
258 let (file_id, _ast) = &parsed.inner[parsed.top_id.0];
259 _ast.build(
260 &mut ast,
261 &mut has_err,
262 file_id,
263 parsed.top_id,
264 self,
265 &parsed,
266 );
267 (ast, has_err)
268 }
269}