use std::{
path::Path,
sync::{Arc, LazyLock},
};
use indexmap::IndexMap;
use nom::{
IResult, Input, Parser,
branch::alt,
bytes::complete::{tag, take, take_till, take_while, take_while1},
character::{char, complete::digit1},
combinator::{map, map_res, opt, recognize, verify},
error::ErrorKind,
multi::{many0, many1},
sequence::{delimited, preceded},
};
use regex::Regex;
use super::{ast, manager::ParseManager};
use crate::{
ast::{ASTBuilder, LocalAST, SubcktBuilder},
err::ParseErrorInner,
parser::{
cmds::{init_condition, nodeset},
instance::instance,
},
};
use crate::{
ast::{
DataBuilder, DataFileBuilder, DataFilesBuilder, DataValuesBuilder, KeyValueBuilder,
ModelBuilder, PnameColNumBuilder, TokenBuilder, UnknwonBuilder, ValueBuilder,
},
span::{EndReason, FileId, LocatedSpan, Pos, Span},
};
#[inline]
pub(super) fn space(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
take_while(|c: char| matches!(c, '\t' | '\r' | ' ')).parse_complete(i)
}
#[inline]
pub(super) fn space1(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
take_while1(|c: char| matches!(c, '\t' | '\r' | ' ')).parse_complete(i)
}
#[inline]
pub(super) fn space_newline(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
take_while(|c: char| matches!(c, '\t' | '\r' | ' ' | '\n')).parse_complete(i)
}
#[inline]
pub(super) fn multiline_sep<'a, T>(
f: fn(LocatedSpan<'a>) -> IResult<LocatedSpan<'a>, T>,
) -> impl MyParser<'a, T> {
alt((
preceded(space1, f),
map(
(comment_space_newline, char('+'), space, f),
|(_, _, _, t)| t,
),
))
}
#[inline]
pub(super) fn loss_sep(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
alt((
map((comment_space_newline, char('+'), space), |(_, _, s)| s),
space,
))
.parse_complete(i)
}
#[inline]
pub(super) fn key(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
map(take_while1(is_key), |s: LocatedSpan| s.into()).parse_complete(i)
}
enum Unit {
Factor(f64),
DB,
}
#[inline]
fn unit(i: LocatedSpan) -> IResult<LocatedSpan, Unit> {
map_res(take_while1(char::is_alphanumeric), |s: LocatedSpan| match s
.fragment()
.to_uppercase()
.as_str()
{
"T" => Ok(Unit::Factor(1e+12)),
"G" => Ok(Unit::Factor(1e+9)),
"ME" | "MEG" | "X" | "Z" => Ok(Unit::Factor(1e+6)),
"K" => Ok(Unit::Factor(1e+3)),
"MI" | "MIL" => Ok(Unit::Factor(25.4e-6)),
"U" => Ok(Unit::Factor(1e-6)),
"N" => Ok(Unit::Factor(1e-9)),
"P" => Ok(Unit::Factor(1e-12)),
"F" => Ok(Unit::Factor(1e-15)),
"A" => Ok(Unit::Factor(1e-18)),
"DB" => Ok(Unit::DB),
"MIN" => Ok(Unit::Factor(60.0)),
"HR" => Ok(Unit::Factor(3600.0)),
"DAY" => Ok(Unit::Factor(86400.0)),
"YR" => Ok(Unit::Factor(31536000.0)),
_ => Err(ErrorKind::Float),
})
.parse_complete(i)
}
#[inline]
pub(super) fn float(i: LocatedSpan) -> IResult<LocatedSpan, f64> {
match fast_float2::parse_partial(i) {
Ok((f, pos)) => Ok((i.take_from(pos), f)),
Err(_) => Err(nom::Err::Error(nom::error::Error::new(i, ErrorKind::Float))),
}
}
#[inline]
pub(super) fn float_unit(i: LocatedSpan) -> IResult<LocatedSpan, f64> {
map((float, opt(unit)), |(f, u)| match u {
Some(Unit::Factor(u)) => f * u,
Some(Unit::DB) => 10.0_f64.powf(f / 20.0),
None => f,
})
.parse_complete(i)
}
#[inline]
pub(super) fn key_str(i: LocatedSpan) -> IResult<LocatedSpan, (&str, Span)> {
map(take_while1(is_key), |s: LocatedSpan| {
let _s: &str = s.fragment();
(_s, s.into())
})
.parse_complete(i)
}
#[inline]
fn is_path(c: char) -> bool {
c.is_alphanumeric() || !c.is_whitespace()
}
#[inline]
fn is_key(c: char) -> bool {
c.is_alphanumeric() || c == '_'
}
fn is_name(c: char) -> bool {
c.is_alphanumeric() || "/_.+-*^:@%#[]".contains(c)
}
fn is_formula(c: char) -> bool {
c.is_alphanumeric() || "/_.+-*^:".contains(c)
}
#[inline]
pub(super) fn path(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
alt((
unquote,
map(take_while1(is_path), |s: LocatedSpan| s.into()),
))
.parse_complete(i)
}
#[inline]
pub(super) fn integer(i: LocatedSpan) -> IResult<LocatedSpan, usize> {
map_res(digit1, |s: LocatedSpan| s.parse()).parse_complete(i)
}
#[inline]
pub(super) fn path_str(i: LocatedSpan) -> IResult<LocatedSpan, &str> {
alt((
unquote_str,
map(take_while1(is_path), |s: LocatedSpan| {
let _s: &str = s.fragment();
_s
}),
))
.parse_complete(i)
}
#[inline]
pub(super) fn name_char(i: LocatedSpan) -> IResult<LocatedSpan, (u8, Span)> {
map(take_while1(is_name), |s: LocatedSpan| {
(s.fragment().as_bytes()[0], s.into())
})
.parse_complete(i)
}
#[inline]
pub(super) fn name(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
map(take_while1(is_name), |s: LocatedSpan| s.into()).parse_complete(i)
}
#[inline]
pub(super) fn name_str(i: LocatedSpan) -> IResult<LocatedSpan, (&str, Span)> {
map(take_while1(is_name), |s: LocatedSpan| {
let _s: &str = s.fragment();
(_s, s.into())
})
.parse_complete(i)
}
#[inline]
pub(super) fn formula(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
map(take_while1(is_formula), |s: LocatedSpan| s.into()).parse_complete(i)
}
#[inline]
pub(super) fn unquote(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
map(
delimited(char('\''), take_till(|c| c == '\''), take(1_usize)),
|s: LocatedSpan| s.into(),
)
.parse_complete(i)
}
#[inline]
pub(super) fn unquote_str(i: LocatedSpan) -> IResult<LocatedSpan, &str> {
map(
delimited(char('\''), take_till(|c| c == '\''), take(1_usize)),
|s: LocatedSpan| {
let _s: &str = s.fragment();
_s
},
)
.parse_complete(i)
}
#[inline]
pub(super) fn comment(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
recognize((alt((tag("*"), tag("$"))), take_till(|c| c == '\n'))).parse_complete(i)
}
#[inline]
pub(super) fn comment_space_newline(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
recognize(many0(verify(
recognize((
space,
opt(comment),
opt((
char('\n'),
opt((space, char('+'), space, opt(comment), char('\n'))),
)),
)),
|s| !s.is_empty(),
)))
.parse_complete(i)
}
#[inline]
pub(super) fn value(i: LocatedSpan) -> IResult<LocatedSpan, ValueBuilder> {
if let Ok((i, s)) = unquote.parse_complete(i) {
return Ok((i, ValueBuilder::Expr(s)));
}
match (float_unit.parse_complete(i), formula.parse_complete(i)) {
(Ok((i_num, num)), Ok((i_formula, formula))) => {
if i_formula.len() < i_num.len() {
Ok((i_formula, ValueBuilder::Expr(formula)))
} else {
Ok((i_num, ValueBuilder::Num(num)))
}
}
(Ok((i_num, num)), Err(_)) => Ok((i_num, ValueBuilder::Num(num))),
(Err(_), Ok((i_formula, formula))) => Ok((i_formula, ValueBuilder::Expr(formula))),
(Err(e), Err(_)) => Err(e),
}
}
pub(super) trait MyParser<'a, T>:
Parser<LocatedSpan<'a>, Output = T, Error = nom::error::Error<LocatedSpan<'a>>>
{
}
impl<'a, T, P> MyParser<'a, T> for P where
P: Parser<LocatedSpan<'a>, Output = T, Error = nom::error::Error<LocatedSpan<'a>>>
{
}
#[inline]
pub(super) fn equal<'a, T, F: MyParser<'a, T>>(f: F) -> impl MyParser<'a, T> {
map((space, char('='), space, f), |(_, _, _, v)| v)
}
#[inline]
pub(super) fn key_value(i: LocatedSpan) -> IResult<LocatedSpan, KeyValueBuilder> {
map((name, equal(value)), |(k, v)| KeyValueBuilder { k, v }).parse_complete(i)
}
#[inline]
pub(super) fn token(input: LocatedSpan) -> IResult<LocatedSpan, TokenBuilder> {
alt((
map(key_value, TokenBuilder::KV),
map(v, TokenBuilder::V),
map(i, TokenBuilder::I),
map(value, TokenBuilder::Value),
))
.parse_complete(input)
}
#[inline]
pub(super) fn option(i: LocatedSpan) -> IResult<LocatedSpan, (Span, Option<ValueBuilder>)> {
alt((
map(key_value, |kv| (kv.k, Some(kv.v))),
map(name, |k| (k, None)),
))
.parse_complete(i)
}
#[inline]
pub(super) fn v(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
map(
(
alt((char('V'), char('v'))),
loss_sep,
char('('),
loss_sep,
name,
loss_sep,
char(')'),
),
|(_, _, _, _, name, _, _)| name,
)
.parse_complete(i)
}
#[inline]
pub(super) fn i(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
map(
(
alt((char('I'), char('i'))),
loss_sep,
char('('),
loss_sep,
name,
loss_sep,
char(')'),
),
|(_, _, _, _, name, _, _)| name,
)
.parse_complete(i)
}
pub(super) fn many0_dummyfirst<'a, T, F>(
mut f: F,
) -> impl FnMut(LocatedSpan<'a>) -> IResult<LocatedSpan<'a>, Vec<T>>
where
F: MyParser<'a, T>,
T: Default,
{
move |mut i: LocatedSpan| {
let mut acc = Vec::with_capacity(4);
acc.push(T::default());
loop {
let len = i.input_len();
match f.parse_complete(i) {
Err(nom::Err::Error(_)) => return Ok((i, acc)),
Err(e) => return Err(e),
Ok((i1, o)) => {
if i1.input_len() == len {
return Err(nom::Err::Error(nom::error::Error::new(i, ErrorKind::Many0)));
}
i = i1;
acc.push(o);
}
}
}
}
}
#[inline]
pub(super) fn ports_params(
i: LocatedSpan,
) -> IResult<LocatedSpan, (Vec<Span>, Vec<KeyValueBuilder>)> {
map(
(
many1(multiline_sep(name)),
opt((equal(value), many0_dummyfirst(multiline_sep(key_value)))),
),
|(mut ports, _params)| match _params {
Some((first_value, mut params)) => {
let first_key = ports.pop().unwrap();
params[0] = KeyValueBuilder {
k: first_key,
v: first_value,
};
(ports, params)
}
None => (ports, Vec::new()),
},
)
.parse_complete(i)
}
#[inline]
pub(super) fn data(mut i: LocatedSpan) -> IResult<LocatedSpan, DataBuilder> {
#[inline]
fn enddata(i: LocatedSpan) -> IResult<LocatedSpan, ()> {
map_res((char('.'), key_str), |(_, (key, _))| {
if key.to_uppercase().as_str() == "ENDDATA" {
Ok(())
} else {
Err(())
}
})
.parse_complete(i)
}
#[inline]
fn data_files(i: LocatedSpan) -> IResult<LocatedSpan, DataFilesBuilder> {
#[inline]
fn file(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
map_res(
(multiline_sep(name_str), equal(path)),
|((key, _), path)| {
if key.to_uppercase().as_str() == "FILE" {
Ok(path)
} else {
Err(())
}
},
)
.parse_complete(i)
}
#[inline]
fn out(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
map_res(
(multiline_sep(name_str), equal(path)),
|((key, _), path)| {
if key.to_uppercase().as_str() == "OUT" {
Ok(path)
} else {
Err(())
}
},
)
.parse_complete(i)
}
#[inline]
fn pname_col_num(i: LocatedSpan) -> IResult<LocatedSpan, PnameColNumBuilder> {
map_res(
(multiline_sep(name_str), equal(integer)),
|((pname_str, pname), col_num)| {
let binding = pname_str.to_uppercase();
let s = binding.as_str();
if s != "FILE" && s != "OUT" {
Ok(PnameColNumBuilder { pname, col_num })
} else {
Err(())
}
},
)
.parse_complete(i)
}
map(
(
many1(map(
(file, many1(pname_col_num)),
|(file, pname_col_num)| DataFileBuilder {
file,
pname_col_num,
},
)),
opt(out),
space_newline,
enddata,
),
|(files, out, _, _)| DataFilesBuilder { files, out },
)
.parse_complete(i)
}
let name;
(i, name) = multiline_sep(key).parse_complete(i)?;
let first;
let first_str;
(i, (first_str, first)) = multiline_sep(name_str).parse_complete(i)?;
match first_str.to_uppercase().as_str() {
"MER" => {
return data_files.parse_complete(i).map(|(i, data_files)| {
(
i,
DataBuilder {
name,
values: DataValuesBuilder::MER(data_files),
},
)
});
}
"LAM" => {
return data_files.parse_complete(i).map(|(i, data_files)| {
(
i,
DataBuilder {
name,
values: DataValuesBuilder::LAM(data_files),
},
)
});
}
_ => {}
}
let mut params = vec![first];
loop {
match multiline_sep(float_unit).parse_complete(i) {
Ok((_i, first_n)) => {
return map(
(
many0_dummyfirst(multiline_sep(float_unit)),
space_newline,
opt(enddata),
),
|(mut values, _, _)| {
values[0] = first_n;
values
},
)
.parse_complete(_i)
.map(|(i, values)| {
(
i,
DataBuilder {
name,
values: DataValuesBuilder::InlineNum { params, values },
},
)
});
}
Err(_) => {
let param;
let param_str;
(i, (param_str, param)) = multiline_sep(name_str).parse_complete(i)?;
if param_str.to_uppercase().as_str() == "DATAFORM" {
return map(
(many1(multiline_sep(value)), space_newline, opt(enddata)),
|(values, _, _)| values,
)
.parse_complete(i)
.map(|(i, values)| {
(
i,
DataBuilder {
name,
values: DataValuesBuilder::InlineExpr { params, values },
},
)
});
} else {
params.push(param);
}
}
}
}
}
#[inline]
pub(super) fn model(i: LocatedSpan) -> IResult<LocatedSpan, ModelBuilder> {
map(
(
multiline_sep(key),
multiline_sep(key_str),
alt((
many1(multiline_sep(key_value)),
map(
(
loss_sep,
char('('),
loss_sep,
opt((key_value, many0_dummyfirst(multiline_sep(key_value)))),
loss_sep,
char(')'),
),
|(_, _, _, v, _, _)| {
if let Some((first, mut vec)) = v {
vec[0] = first;
vec
} else {
Vec::new()
}
},
),
)),
),
|(name, model_type_ctx, params)| ModelBuilder {
name,
model_type: model_type_ctx.into(),
params,
},
)
.parse_complete(i)
}
#[inline]
fn endlib(i: LocatedSpan) -> IResult<LocatedSpan, ()> {
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new("(?i)\\.endl").unwrap());
match RE.find(i.fragment()) {
Some(m) => Ok((i.take_from(m.end()), ())),
None => Err(nom::Err::Error(nom::error::Error::new(
i,
ErrorKind::RegexpCapture,
))),
}
}
pub(super) fn lib(i: LocatedSpan) -> IResult<LocatedSpan, Option<(&str, String)>> {
alt((
map(
(path_str, space1, key_str),
|(path_str, _, (section_str, _))| Some((path_str, section_str.to_lowercase())),
),
map((endlib, opt((space1, key))), |_| None),
))
.parse_complete(i)
}
#[inline]
pub(super) fn subckt<'a>(
i: LocatedSpan<'a>,
loaded: &IndexMap<FileId, Option<Pos>>,
manager: &Arc<ParseManager>,
work_dir: &Path,
) -> IResult<LocatedSpan<'a>, SubcktBuilder> {
let ast_subckt = |i: LocatedSpan<'a>| -> IResult<LocatedSpan<'a>, ASTBuilder> {
ast(manager.clone(), loaded.clone(), work_dir.to_path_buf(), i)
};
map(
(space1, name, ports_params, space_newline, ast_subckt),
|(_, name, (ports, params), _, ast)| SubcktBuilder {
name,
ports,
params,
ast,
},
)
.parse_complete(i)
}
#[inline]
pub(super) fn local_ast<'a>(
mut i: LocatedSpan<'a>,
loaded: &IndexMap<FileId, Option<Pos>>,
manager: &Arc<ParseManager>,
work_dir: &Path,
) -> IResult<LocatedSpan<'a>, (LocalAST, EndReason<'a>)> {
let mut ast = LocalAST::default();
loop {
(i, _) = comment_space_newline(i)?;
if i.is_empty() {
return Ok((i, (ast, EndReason::End)));
}
match char('.').parse_complete(i) {
Err(nom::Err::Error(_)) => {
let inst;
(i, inst) = instance(i)?;
ast.instance.push(inst);
}
Err(e) => return Err(e),
Ok((_i, _)) => {
i = _i;
let cmd;
let cmd_str;
(i, (cmd_str, cmd)) = key_str(i)?;
match cmd_str.to_lowercase().as_str() {
"lib" => {
let lib_info;
(i, (_, lib_info)) = (space1, lib).parse_complete(i)?;
if let Some((file_name_str, section_str)) = lib_info {
return Ok((
i,
(
ast,
EndReason::Include {
file_name: Path::new(file_name_str),
section: Some(section_str),
},
),
));
}
}
"inc" | "include" => {
let file_name;
(i, (_, file_name)) = (space1, path_str).parse_complete(i)?;
return Ok((
i,
(
ast,
EndReason::Include {
file_name: Path::new(file_name),
section: None,
},
),
));
}
"model" => {
let _model;
(i, _model) = model(i)?;
ast.model.push(_model);
}
"subckt" => {
let _subckt;
(i, _subckt) = subckt(i, loaded, manager, work_dir)?;
ast.subckt.push(_subckt);
}
"data" => {
let _data;
(i, _data) = data(i)?;
ast.data.push(_data);
}
"option" => {
let options;
(i, options) = many0(multiline_sep(option)).parse_complete(i)?;
ast.option.extend(options);
}
"param" | "parameter" => {
let param;
(i, param) = many0(multiline_sep(key_value)).parse_complete(i)?;
ast.param.extend(param);
}
"ic" => {
let _init_condition;
(i, _init_condition) = init_condition(i)?;
ast.init_condition.extend(_init_condition);
}
"nodeset" => {
let _nodeset;
(i, _nodeset) = nodeset(i)?;
ast.nodeset.extend(_nodeset);
}
"ends" => {
(i, _) = opt((space1, key)).parse_complete(i)?;
return Ok((i, (ast, EndReason::End)));
}
"end" => {
(i, _) = many0((loss_sep, name)).parse_complete(i)?;
}
_ => {
ast.errors.push(ParseErrorInner::Unknown(cmd).record(i));
let tokens;
(i, tokens) = many0(multiline_sep(token)).parse_complete(i)?;
ast.unknwon.push(UnknwonBuilder { cmd, tokens })
}
}
}
}
}
}