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}
101
102#[expect(clippy::upper_case_acronyms)]
103#[derive(Debug, Clone)]
104pub enum ModelType<'s> {
105    /// operational amplifier model
106    AMP,
107    /// capacitor model
108    C,
109    /// magnetic core model
110    CORE,
111    /// diode model
112    D,
113    /// inductor model or magnetic core mutual inductor model
114    L,
115    /// n-channel JFET model
116    NJF,
117    /// n-channel MOSFET model
118    NMOS,
119    /// npn BJT model
120    NPN,
121    /// optimization model
122    OPT,
123    /// p-channel JFET model
124    PJF,
125    /// p-channel MOSFET model
126    PMOS,
127    /// pnp BJT model
128    PNP,
129    /// resistor model
130    R,
131    /// lossy transmission line model (lumped)
132    U,
133    /// lossy transmission line model
134    W,
135    /// S-parameter
136    S,
137    Unknown(Cow<'s, str>),
138}
139#[expect(clippy::upper_case_acronyms)]
140#[derive(Debug, Clone, Default)]
141pub struct AST<'s> {
142    pub subckt: Vec<Subckt<'s>>,
143    pub instance: Vec<instance::Instance<'s>>,
144    pub model: Vec<Model<'s>>,
145    pub param: Vec<KeyValue<'s>>,
146    pub option: Vec<(Cow<'s, str>, Option<Value<'s>>)>,
147    /// transient initial conditions
148    /// https://eda-cpu1.eias.junzhuo.site/~junzhuo/hspice/index.htm#page/hspice_14/ic.htm
149    ///
150    /// `node, val, [subckt]`
151    pub init_condition: Vec<(Cow<'s, str>, Value<'s>, Option<Cow<'s, str>>)>,
152    pub general: Vec<General<'s>>,
153    pub data: Vec<Data<'s>>,
154    pub unknwon: Vec<Unknwon<'s>>,
155}
156
157impl builder::AST {
158    #[expect(clippy::too_many_arguments)]
159    fn build<'s>(
160        &self,
161        ast: &mut AST<'s>,
162        has_err: &mut bool,
163        file_id: &FileId,
164        parsed_id: ParsedId,
165        files: &'s Files,
166        parsed: &Parsed,
167    ) {
168        fn build_local<'s>(
169            local_ast: &builder::LocalAST,
170            ast: &mut AST<'s>,
171            has_err: &mut bool,
172            file: &'s str,
173            file_id: &FileId,
174            parsed_id: ParsedId,
175            files: &'s Files,
176            parsed: &Parsed,
177        ) {
178            fn build_subckt<'s>(
179                s: &builder::Subckt,
180                has_err: &mut bool,
181                file: &'s str,
182                file_id: &FileId,
183                parsed_id: ParsedId,
184                files: &'s Files,
185                parsed: &Parsed,
186            ) -> Subckt<'s> {
187                let mut ast = AST::default();
188                s.ast
189                    .build(&mut ast, has_err, file_id, parsed_id, files, parsed);
190                Subckt {
191                    name: s.name.build(file),
192                    ports: s.ports.build(file),
193                    params: s.params.build(file),
194                    ast,
195                }
196            }
197            ast.subckt.extend(
198                local_ast
199                    .subckt
200                    .iter()
201                    .map(|s| build_subckt(s, has_err, file, file_id, parsed_id, files, parsed)),
202            );
203            ast.instance
204                .extend(local_ast.instance.iter().map(|b| b.build(file)));
205            ast.model
206                .extend(local_ast.model.iter().map(|b| b.build(file)));
207            ast.param
208                .extend(local_ast.param.iter().map(|b| b.build(file)));
209            ast.option
210                .extend(local_ast.option.iter().map(|b| b.build(file)));
211            ast.general
212                .extend(local_ast.general.iter().map(|b| b.build(file)));
213            ast.data
214                .extend(local_ast.data.iter().map(|b| b.build(file)));
215            ast.init_condition
216                .extend(local_ast.init_condition.iter().map(|b| b.build(file)));
217            ast.unknwon
218                .extend(local_ast.unknwon.iter().map(|b| b.build(file)));
219            for e in &local_ast.errors {
220                e.report(has_err, file_id, file);
221            }
222        }
223        let file = &files.inner[parsed_id.0];
224        for seg in &self.segments {
225            match seg {
226                builder::Segment::Local(local_ast) => {
227                    build_local(
228                        local_ast, ast, has_err, file, file_id, parsed_id, files, parsed,
229                    );
230                }
231                builder::Segment::Include(ast_res) => {
232                    let ast_res = ast_res.get().unwrap();
233                    match ast_res {
234                        Ok(parsed_id) => {
235                            let (file_id, _ast) = &parsed.inner[parsed_id.0];
236                            _ast.build(ast, has_err, file_id, *parsed_id, files, parsed);
237                        }
238                        Err(e) => {
239                            e.report(has_err, file_id, file);
240                        }
241                    }
242                }
243            }
244        }
245    }
246}
247
248impl Files {
249    #[inline]
250    pub fn build(&self, parsed: Parsed) -> (AST<'_>, bool) {
251        let mut ast = AST::default();
252        let mut has_err = false;
253        let (file_id, _ast) = &parsed.inner[parsed.top_id.0];
254        _ast.build(
255            &mut ast,
256            &mut has_err,
257            file_id,
258            parsed.top_id,
259            self,
260            &parsed,
261        );
262        (ast, has_err)
263    }
264}