use std::{borrow::Cow, fmt::Display, num::NonZeroU32};
use chumsky::{
IterParser, Parser,
error::Rich,
extra,
input::{Input, MappedInput},
prelude::{choice, just, recursive},
select,
};
use device_driver_common::{
span::{Span, SpanExt, Spanned},
specifiers::{Access, AddressMode, BaseType, ByteOrder, Integer},
};
use device_driver_diagnostics::{Diagnostics, errors::ParsingError};
use device_driver_lexer::Token;
use crate::parse_num::{ParseIntRadix, ParseIntRadixError, ParseIntRadixErrorKind, parse_num};
#[cfg(feature = "gen-docs")]
pub mod gen_docs;
mod parse_num;
pub fn parse<'src>(tokens: &[Spanned<Token<'src>>], diagnostics: &mut Diagnostics) -> Ast<'src> {
let (ast, parse_errs) = node()
.map_with(|ast, e| (ast, e.span()))
.parse(
tokens.map(
tokens
.last()
.map(|t| Span::from(t.span.end..t.span.end))
.unwrap_or_default(),
|token| (&token.value, &token.span),
),
)
.into_output_errors();
for error in parse_errs {
diagnostics.add(ParsingError {
reason: error.to_string(),
span: *error.span(),
});
}
ast.map(|(root_node, span)| Ast {
root_node: Some(root_node),
span,
})
.unwrap_or_default()
}
#[derive(Debug, Default)]
pub struct Ast<'src> {
pub root_node: Option<Node<'src>>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct Node<'src> {
pub doc_comments: Vec<Spanned<&'src str>>,
pub node_type: Ident<'src>,
pub name: Ident<'src>,
pub repeat: Option<Spanned<Repeat<'src>>>,
pub type_specifier: Option<Spanned<TypeSpecifier<'src>>>,
pub short_properties: Vec<Spanned<Expression<'src>>>,
pub properties: Vec<Spanned<Property<'src>>>,
pub sub_nodes: Vec<Node<'src>>,
pub span: Span,
}
impl<'src> Display for Node<'src> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let indentation_level = f.width().unwrap_or_default();
let indentation = format!("{:width$}", "", width = indentation_level * 4);
for doc_comment in &self.doc_comments {
writeln!(
f,
"{indentation}///{}{doc_comment}",
if doc_comment.starts_with(" ") {
""
} else {
" "
}
)?;
}
write!(f, "{indentation}{} {}", self.node_type.val, self.name.val)?;
if let Some(repeat) = self.repeat {
write!(f, "[{} stride {}]", repeat.source, repeat.stride)?;
}
for expression in self.short_properties.iter() {
write!(f, " {}", expression.get_human_string())?;
}
if let Some(type_specifier) = self.type_specifier.as_ref() {
write!(f, " -> {}", type_specifier.base_type)?;
if let Some(conversion) = type_specifier.conversion.as_ref() {
write!(f, " as")?;
if type_specifier.use_try {
write!(f, " try")?;
}
match conversion {
TypeConversion::Reference(ident) => write!(f, " {}", ident.val)?,
TypeConversion::Subnode(node) => {
if node.doc_comments.is_empty() {
for (i, line) in node.to_string().lines().enumerate() {
if i == 0 {
write!(f, " {line}")?;
} else {
write!(f, "\n{indentation}{line}")?;
}
}
} else {
write!(f, "\n{node:width$}", width = indentation_level + 1)?;
}
}
}
}
}
if !self.sub_nodes.is_empty() || !self.properties.is_empty() {
writeln!(f, " {{")?;
for property in self.properties.iter() {
for doc_comment in property.doc_comments.iter() {
writeln!(
f,
"{indentation} ///{}{}",
if doc_comment.starts_with(" ") {
""
} else {
" "
},
doc_comment
)?;
}
write!(f, "{indentation} {}:", property.name.val)?;
let expression = property.expression.get_human_string();
if expression.starts_with("///") {
for line in expression.lines() {
write!(f, "\n{indentation} {line}")?;
}
} else {
for (i, line) in expression.lines().enumerate() {
if i == 0 {
write!(f, " {line}")?;
} else {
write!(f, "\n{indentation} {line}")?;
}
}
}
writeln!(f, ",")?;
}
if !self.properties.is_empty() && !self.sub_nodes.is_empty() {
writeln!(f, "{indentation}",)?;
}
for node in self.sub_nodes.iter() {
writeln!(f, "{node:width$},", width = indentation_level + 1)?;
}
write!(f, "{indentation}}}")?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct TypeSpecifier<'src> {
pub base_type: Spanned<BaseType>,
pub use_try: bool,
pub conversion: Option<TypeConversion<'src>>,
}
#[derive(Debug, Clone)]
pub enum TypeConversion<'src> {
Reference(Ident<'src>),
Subnode(Box<Node<'src>>),
}
#[derive(Debug, Clone)]
pub struct Property<'src> {
pub doc_comments: Vec<Spanned<&'src str>>,
pub name: Ident<'src>,
pub expression: Spanned<Expression<'src>>,
}
#[derive(Debug, Clone)]
pub enum Expression<'src> {
AddressRange { end: i128, start: i128 },
ByteArray(Vec<u8>),
BaseType(BaseType),
Integer(Integer),
Allow,
Number(i128),
DefaultNumber(Option<i128>),
CatchAllNumber(Option<i128>),
String(&'src str),
Access(Access),
ByteOrder(ByteOrder),
TypeReference(Ident<'src>),
SubNode(Box<Node<'src>>),
Auto,
AddressMode(AddressMode),
Error,
}
impl<'src> Expression<'src> {
pub fn as_range(&self) -> Option<(i128, i128)> {
if let Self::AddressRange { end, start } = self {
Some((*end, *start))
} else {
None
}
}
pub fn as_byte_order(&self) -> Option<ByteOrder> {
if let Self::ByteOrder(v) = self {
Some(*v)
} else {
None
}
}
pub fn as_access(&self) -> Option<Access> {
if let Self::Access(v) = self {
Some(*v)
} else {
None
}
}
pub fn as_integer(&self) -> Option<Integer> {
if let Self::Integer(v) = self {
Some(*v)
} else {
None
}
}
pub fn as_unsigned_integer(&self) -> Option<Integer> {
if let Self::Integer(v) = self {
Some(*v)
} else {
None
}
}
pub fn as_number(&self) -> Option<i128> {
if let Self::Number(v) = self {
Some(*v)
} else {
None
}
}
pub fn as_string(&self) -> Option<&'src str> {
if let Self::String(v) = self {
Some(*v)
} else {
None
}
}
pub fn as_address_mode(&self) -> Option<AddressMode> {
if let Self::AddressMode(v) = self {
Some(*v)
} else {
None
}
}
pub fn get_human_string(&self) -> Cow<'static, str> {
match self {
Expression::AddressRange { end, start } => format!("{end}:{start}").into(),
Expression::ByteArray(items) => format!("{items:?}").into(),
Expression::BaseType(base_type) => base_type.to_string().into(),
Expression::Integer(integer) => integer.to_string().into(),
Expression::Allow => "allow".into(),
Expression::Number(num) => num.to_string().into(),
Expression::DefaultNumber(Some(num)) => format!("default {num}").into(),
Expression::DefaultNumber(None) => "default _".into(),
Expression::CatchAllNumber(Some(num)) => format!("catch-all {num}").into(),
Expression::CatchAllNumber(None) => "catch-all _".into(),
Expression::String(val) => format!("\"{val}\"").into(),
Expression::Access(val) => val.to_string().into(),
Expression::ByteOrder(val) => val.to_string().into(),
Expression::TypeReference(ident) => ident.val.to_string().into(),
Expression::SubNode(val) => val.to_string().into(),
Expression::Auto => "_".into(),
Expression::AddressMode(val) => val.to_string().into(),
Expression::Error => "ERROR".into(),
}
}
}
impl<'src> Display for Expression<'src> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expression::AddressRange { .. } => write!(f, "range"),
Expression::ByteArray(_) => write!(f, "[bytes]"),
Expression::BaseType(_) => write!(f, "base type"),
Expression::Integer(_) => write!(f, "integer type"),
Expression::Allow => write!(f, "allow"),
Expression::Number(_) => write!(f, "number"),
Expression::DefaultNumber(None) => write!(f, "default auto"),
Expression::CatchAllNumber(None) => write!(f, "catch-all auto"),
Expression::DefaultNumber(Some(_)) => write!(f, "default number"),
Expression::CatchAllNumber(Some(_)) => write!(f, "catch-all number"),
Expression::String(_) => write!(f, "string"),
Expression::Access(_) => write!(f, "access specifier"),
Expression::ByteOrder(_) => write!(f, "byte order"),
Expression::TypeReference(_) => write!(f, "type reference"),
Expression::SubNode(_) => write!(f, "sub node"),
Expression::Auto => write!(f, "auto"),
Expression::AddressMode(_) => write!(f, "address mode"),
Expression::Error => write!(f, "error"),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Repeat<'src> {
pub source: Spanned<RepeatSource<'src>>,
pub stride: Spanned<i32>,
}
#[derive(Debug, Clone, Copy)]
pub enum RepeatSource<'src> {
Count(NonZeroU32),
Enum(Ident<'src>),
}
impl<'src> Default for RepeatSource<'src> {
fn default() -> Self {
Self::Count(1.try_into().unwrap())
}
}
impl Display for RepeatSource<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RepeatSource::Count(non_zero) => write!(f, "{non_zero}"),
RepeatSource::Enum(ident) => write!(f, "{}", ident.val),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Ident<'src> {
pub val: &'src str,
pub span: Span,
is_auto: bool,
}
impl<'src> Ident<'src> {
pub const fn new(val: &'src str, span: Span) -> Self {
Self {
val,
span,
is_auto: false,
}
}
pub const fn new_no_span(val: &'src str) -> Self {
Self {
val,
span: Span::empty(),
is_auto: false,
}
}
pub const fn new_auto(span: Span) -> Self {
Self {
val: "_",
span,
is_auto: true,
}
}
pub fn is_auto(&self) -> bool {
self.is_auto
}
}
fn try_num<'tokens, 'src: 'tokens, I: ParseIntRadix>(
num_str: &'src str,
span: Span,
) -> Result<I, RichErr<'tokens, 'src>> {
match parse_num::<I>(num_str) {
Ok(num) => Ok(num),
Err(ParseIntRadixError {
source,
kind,
target_bits,
target_signed,
}) => match kind {
ParseIntRadixErrorKind::Overflow => Err(Rich::custom(
span,
format!(
"number `{source}` is parsed as a {}{target_bits}, but overflows.",
if target_signed { 'i' } else { 'u' }
),
)),
ParseIntRadixErrorKind::Underflow => Err(Rich::custom(
span,
format!(
"number `{source}` is parsed as a {}{target_bits}, but underflows.",
if target_signed { 'i' } else { 'u' }
),
)),
ParseIntRadixErrorKind::Empty => Err(Rich::custom(
span,
format!("could not parse `{source}` as a number because it contains no numbers"),
)),
ParseIntRadixErrorKind::Zero => {
Err(Rich::custom(span, "number can't be 0 in this position"))
}
},
}
}
pub type InputType<'tokens, 'src> =
MappedInput<'tokens, Token<'src>, Span, &'tokens [Spanned<Token<'src>>]>;
pub type RichErr<'tokens, 'src> = Rich<'tokens, Token<'src>, Span>;
pub type RichExtra<'tokens, 'src> = extra::Err<RichErr<'tokens, 'src>>;
pub fn ident<'tokens, 'src: 'tokens>(
allow_auto: bool,
) -> impl Parser<'tokens, InputType<'tokens, 'src>, Ident<'src>, RichExtra<'tokens, 'src>> + Clone {
select! {
Token::Ident(val) = e => Ident::new(val, e.span()),
Token::Underscore = e if allow_auto => Ident::new_auto(e.span()),
}
.labelled(format!(
"Ident{}",
if allow_auto { "|Underscore" } else { "" }
))
.as_terminal()
}
pub fn doc_comment<'tokens, 'src: 'tokens>()
-> impl Parser<'tokens, InputType<'tokens, 'src>, Spanned<&'src str>, RichExtra<'tokens, 'src>> + Copy
{
select! {
Token::DocCommentLine(val) => val
}
.map_with(|line, extra| line.spanned(extra.span()))
.labelled("DocCommentLine")
.as_terminal()
}
pub fn num<'tokens, 'src: 'tokens, I: ParseIntRadix>()
-> impl Parser<'tokens, InputType<'tokens, 'src>, I, RichExtra<'tokens, 'src>> + Clone {
select! {
Token::Num(num) => num
}
.try_map(try_num::<I>)
.labelled(format!(
"Num<{}>",
std::any::type_name::<I>().split("::").last().unwrap()
))
.as_terminal()
}
pub fn range<'tokens, 'src: 'tokens>()
-> impl Parser<'tokens, InputType<'tokens, 'src>, Expression<'src>, RichExtra<'tokens, 'src>> + Clone
{
num::<i128>()
.then_ignore(just(Token::Colon))
.then(num::<i128>())
.map(|(end, start)| Expression::AddressRange { end, start })
.labelled("range")
}
pub fn base_type<'tokens, 'src: 'tokens>()
-> impl Parser<'tokens, InputType<'tokens, 'src>, BaseType, RichExtra<'tokens, 'src>> + Copy {
select! { Token::BaseType(bt) => bt }
.labelled("BaseType")
.as_terminal()
}
pub fn integer<'tokens, 'src: 'tokens>()
-> impl Parser<'tokens, InputType<'tokens, 'src>, Integer, RichExtra<'tokens, 'src>> + Copy {
select! { Token::Integer(i) => i }
.labelled("Integer")
.as_terminal()
}
pub fn byte_array<'tokens, 'src: 'tokens>()
-> impl Parser<'tokens, InputType<'tokens, 'src>, Expression<'src>, RichExtra<'tokens, 'src>> + Clone
{
num::<u8>()
.separated_by(just(Token::Comma))
.collect::<Vec<_>>()
.map(Expression::ByteArray)
.then_ignore(just(Token::Comma).or_not())
.delimited_by(just(Token::BracketOpen), just(Token::BracketClose))
.labelled("byte-array")
}
pub fn simple_expression<'tokens, 'src: 'tokens>()
-> impl Parser<'tokens, InputType<'tokens, 'src>, Spanned<Expression<'src>>, RichExtra<'tokens, 'src>>
+ Clone {
choice((
range().labelled("range").as_non_terminal(),
base_type().map(Expression::BaseType),
integer().map(Expression::Integer),
num::<i128>().map(Expression::Number),
just(Token::Default)
.ignore_then(
num::<i128>()
.map(Some)
.or(just(Token::Underscore).map(|_| None)),
)
.map(Expression::DefaultNumber)
.labelled("default-number"),
just(Token::CatchAll)
.ignore_then(
num::<i128>()
.map(Some)
.or(just(Token::Underscore).map(|_| None)),
)
.map(Expression::CatchAllNumber)
.labelled("catch-all-number"),
byte_array().labelled("byte-array").as_non_terminal(),
just(Token::Allow).map(|_| Expression::Allow),
select! { Token::Access(val) => val }
.map(Expression::Access)
.labelled("Access")
.as_terminal(),
select! { Token::ByteOrder(val) => val }
.map(Expression::ByteOrder)
.labelled("ByteOrder")
.as_terminal(),
just(Token::Underscore).map(|_| Expression::Auto),
select! { Token::String(val) => val }
.map(Expression::String)
.labelled("String")
.as_terminal(),
select! { Token::AddressMode(val) => val }
.map(Expression::AddressMode)
.labelled("AddressMode")
.as_terminal(),
))
.map_with(|expression, extra| expression.spanned(extra.span()))
.labelled("simple-expression")
}
pub fn repeat<'tokens, 'src: 'tokens>()
-> impl Parser<'tokens, InputType<'tokens, 'src>, Spanned<Repeat<'src>>, RichExtra<'tokens, 'src>>
+ Clone {
choice((
num::<NonZeroU32>().map(RepeatSource::Count),
ident(false).map(RepeatSource::Enum),
))
.map_with(|repeat_source, extra| repeat_source.with_span(extra.span()))
.then(
just(Token::Stride)
.ignore_then(num::<i32>().map_with(|num, extra| num.with_span(extra.span()))),
)
.delimited_by(just(Token::BracketOpen), just(Token::BracketClose))
.map_with(|(source, stride), extra| Repeat { source, stride }.spanned(extra.span()))
.labelled("repeat")
}
pub fn property<'tokens, 'src: 'tokens, 'node>(
node: impl Parser<'tokens, InputType<'tokens, 'src>, Node<'src>, RichExtra<'tokens, 'src>> + Clone,
) -> impl Parser<'tokens, InputType<'tokens, 'src>, Spanned<Property<'src>>, RichExtra<'tokens, 'src>>
+ Clone {
doc_comment()
.repeated()
.collect()
.then(
ident(false)
.then(
just(Token::Colon).ignore_then(choice((
simple_expression()
.labelled("simple-expression")
.as_non_terminal(),
node.clone()
.map_with(|node, extra| {
Expression::SubNode(Box::new(node)).spanned(extra.span())
})
.labelled("node")
.as_non_terminal(),
ident(false)
.map(Expression::TypeReference)
.map_with(|expression, extra| expression.spanned(extra.span())),
))),
)
.map_with(|(name, expression), extra| {
Property {
doc_comments: Vec::new(),
name,
expression,
}
.spanned(extra.span())
}),
)
.map(|(docs, mut prop)| {
prop.doc_comments = docs;
prop
})
.labelled("property")
}
pub fn type_specifier<'tokens, 'src: 'tokens>(
node: impl Parser<'tokens, InputType<'tokens, 'src>, Node<'src>, RichExtra<'tokens, 'src>> + Clone,
) -> impl Parser<
'tokens,
InputType<'tokens, 'src>,
Spanned<TypeSpecifier<'src>>,
RichExtra<'tokens, 'src>,
> + Clone {
let type_conversion = just(Token::As).ignore_then(just(Token::Try).or_not()).then(
node.labelled("node")
.as_non_terminal()
.map(|node| TypeConversion::Subnode(Box::new(node)))
.or(ident(false).map(TypeConversion::Reference)),
);
just(Token::Arrow)
.ignore_then(
choice((
base_type(),
integer().map(BaseType::FixedSize),
just(Token::Underscore).map(|_| BaseType::Unspecified),
))
.map_with(|b, e| b.spanned(e.span())),
)
.then(type_conversion.or_not())
.map(|(base_type, conversion)| TypeSpecifier {
base_type,
use_try: conversion
.as_ref()
.map(|(try_token, _)| try_token.is_some())
.unwrap_or_default(),
conversion: conversion.map(|(_, conversion)| conversion),
})
.map_with(|ts, e| ts.spanned(e.span()))
.labelled("type-specifier")
}
pub fn node_body<'tokens, 'src: 'tokens>(
node: impl Parser<'tokens, InputType<'tokens, 'src>, Node<'src>, RichExtra<'tokens, 'src>> + Clone,
) -> impl Parser<
'tokens,
InputType<'tokens, 'src>,
(Vec<Spanned<Property<'src>>>, Vec<Node<'src>>),
RichExtra<'tokens, 'src>,
> + Clone {
let properties = property(node.clone())
.labelled("property")
.as_non_terminal()
.separated_by(just(Token::Comma))
.at_least(1)
.collect::<Vec<_>>();
let nodes = node
.labelled("node")
.as_non_terminal()
.separated_by(just(Token::Comma))
.at_least(1)
.collect::<Vec<_>>();
choice((
properties
.clone()
.then_ignore(just(Token::Comma))
.then(nodes.clone()),
properties
.clone()
.map(|properties| (properties, Vec::new())),
nodes.map(|nodes| (Vec::new(), nodes)),
))
.then_ignore(just(Token::Comma).or_not())
.or_not()
.map(|body| body.unwrap_or_default())
.delimited_by(just(Token::CurlyOpen), just(Token::CurlyClose))
.labelled("node-body")
}
pub fn node<'tokens, 'src: 'tokens>()
-> impl Parser<'tokens, InputType<'tokens, 'src>, Node<'src>, RichExtra<'tokens, 'src>> + Clone {
recursive(|node| {
let node = node.labelled("node").as_non_terminal();
doc_comment()
.repeated()
.collect()
.then(ident(false).labelled("node-type"))
.then(ident(true).labelled("node-name"))
.then(repeat().labelled("repeat").as_non_terminal().or_not())
.then(
simple_expression()
.labelled("simple-expression")
.as_non_terminal()
.repeated()
.collect::<Vec<_>>(),
)
.then(
type_specifier(node.clone())
.labelled("type-specifier")
.as_non_terminal()
.or_not(),
)
.then(
node_body(node.clone())
.labelled("node-body")
.as_non_terminal()
.or_not(),
)
.map_with(
|(
(((((doc_comments, node_type), name), repeat), expressions), type_specifier),
body,
),
extra| {
let (properties, sub_nodes) = body.unwrap_or_default();
let mut span: Span = extra.span();
span = span.start_from(node_type.span);
Node {
doc_comments,
node_type,
name,
repeat,
type_specifier,
properties,
short_properties: expressions,
sub_nodes,
span,
}
},
)
.labelled("node")
})
}