netlist_db/lexer/
mod.rs

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/// ``` spice
47/// .subckt pulvt11ll_ckt d g s b w=1e-6 l=1e-6 sa='sar'
48/// ...
49/// .ends pulvt11ll_ckt
50/// ```
51/// Do NOT support `.include` / `.lib` in `.subckt`
52#[derive(Debug, Clone)]
53pub struct Subckt<'s> {
54    pub name: Cow<'s, str>,
55    /// subckt/model name is the last arg
56    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    /// https://eda-cpu1.eias.junzhuo.site/~junzhuo/hspice/index.htm#page/hspice_14/data.htm
96    /// Concatenated (series merging) data files to use.
97    MER(),
98    /// Column-laminated (parallel merging) data files to use.
99    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    /// operational amplifier model
111    AMP,
112    /// capacitor model
113    C,
114    /// magnetic core model
115    CORE,
116    /// diode model
117    D,
118    /// inductor model or magnetic core mutual inductor model
119    L,
120    /// n-channel JFET model
121    NJF,
122    /// n-channel MOSFET model
123    NMOS,
124    /// npn BJT model
125    NPN,
126    /// optimization model
127    OPT,
128    /// p-channel JFET model
129    PJF,
130    /// p-channel MOSFET model
131    PMOS,
132    /// pnp BJT model
133    PNP,
134    /// resistor model
135    R,
136    /// lossy transmission line model (lumped)
137    U,
138    /// lossy transmission line model
139    W,
140    /// S-parameter
141    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    /// transient initial conditions
153    /// https://eda-cpu1.eias.junzhuo.site/~junzhuo/hspice/index.htm#page/hspice_14/ic.htm
154    ///
155    /// `node, val, [subckt]`
156    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}