Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 103 additions & 3 deletions pdl-compiler/src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ impl<'d> Scope<'d> {
| FieldDesc::Scalar { .. }
| FieldDesc::Array { type_id: None, .. } => None,
FieldDesc::FixedEnum { enum_id: type_id, .. }
| FieldDesc::Enum { enum_id: type_id, .. }
| FieldDesc::Array { type_id: Some(type_id), .. }
| FieldDesc::Typedef { type_id, .. } => self.typedef.get(type_id).cloned(),
}
Expand All @@ -318,12 +319,18 @@ impl<'d> Scope<'d> {
| FieldDesc::FixedEnum { .. }
| FieldDesc::Reserved { .. }
| FieldDesc::Flag { .. }
| FieldDesc::Scalar { .. } => true,
| FieldDesc::Scalar { .. }
| FieldDesc::Enum { .. } => true,
FieldDesc::Typedef { type_id, .. } => {
let field = self.typedef.get(type_id.as_str());
matches!(field, Some(Decl { desc: DeclDesc::Enum { .. }, .. }))
}
_ => false,
FieldDesc::Checksum { .. }
| FieldDesc::Padding { .. }
| FieldDesc::Array { .. }
| FieldDesc::Group { .. }
| FieldDesc::Body { .. }
| FieldDesc::Payload { .. } => false,
}
}
}
Expand Down Expand Up @@ -402,7 +409,8 @@ impl Schema {
| FieldDesc::ElementSize { width, .. }
| FieldDesc::FixedScalar { width, .. }
| FieldDesc::Reserved { width }
| FieldDesc::Scalar { width, .. } => Size::Static(*width),
| FieldDesc::Scalar { width, .. }
| FieldDesc::Enum { width, .. } => Size::Static(*width),
FieldDesc::Flag { .. } => Size::Static(1),
FieldDesc::Body | FieldDesc::Payload { .. } => {
let has_payload_size = decl.fields().any(|field| match &field.desc {
Expand Down Expand Up @@ -1022,6 +1030,57 @@ fn check_constraint(
_ => (),
}
}
Some(field @ Field { desc: FieldDesc::Enum { enum_id, .. }, .. }) => {
match scope.typedef.get(enum_id) {
Some(Decl { desc: DeclDesc::Enum { tags, .. }, .. }) => match &constraint.tag_id {
None => diagnostics.push(
Diagnostic::error()
.with_code(ErrorCode::E19)
.with_message(format!(
"invalid constraint value `{}`",
constraint.value.unwrap()
))
.with_labels(vec![
constraint.loc.primary(),
field.loc.secondary().with_message(format!(
"`{}` is declared here as typedef field",
constraint.id
)),
])
.with_notes(vec!["hint: expected enum value".to_owned()]),
),
Some(tag_id) => match tags.iter().find(|tag| tag.id() == tag_id) {
None => diagnostics.push(
Diagnostic::error()
.with_code(ErrorCode::E20)
.with_message(format!("undeclared enum tag `{}`", tag_id))
.with_labels(vec![
constraint.loc.primary(),
field.loc.secondary().with_message(format!(
"`{}` is declared here",
constraint.id
)),
]),
),
Some(Tag::Range { .. }) => diagnostics.push(
Diagnostic::error()
.with_code(ErrorCode::E42)
.with_message(format!("enum tag `{}` defines a range", tag_id))
.with_labels(vec![
constraint.loc.primary(),
field.loc.secondary().with_message(format!(
"`{}` is declared here",
constraint.id
)),
])
.with_notes(vec!["hint: expected enum tag with value".to_owned()]),
),
Some(_) => (),
},
},
_ => unreachable!(),
}
}
Some(field @ Field { desc: FieldDesc::Typedef { type_id, .. }, .. }) => {
match scope.typedef.get(type_id) {
None => (),
Expand Down Expand Up @@ -1650,6 +1709,7 @@ fn check_field_offsets(file: &File, scope: &Scope, schema: &Schema) -> Result<()
| FieldDesc::FixedEnum { .. }
| FieldDesc::FixedScalar { .. }
| FieldDesc::Group { .. }
| FieldDesc::Enum { .. }
| FieldDesc::Flag { .. }
| FieldDesc::Reserved { .. }
| FieldDesc::Scalar { .. } => (),
Expand Down Expand Up @@ -1833,6 +1893,45 @@ fn desugar_flags(file: &mut File) {
}
}

/// Replace Typedef fields with enum types by the more specific Enum construct.
fn desugar_enums(file: &mut File) {
// Gather information about enum declarations.
let mut enum_declarations = HashMap::new();
for decl in &file.declarations {
if let DeclDesc::Enum { id, width, .. } = &decl.desc {
enum_declarations.insert(id.to_string(), *width);
}
}

fn to_enum_field(
desc: &FieldDesc,
enum_declarations: &HashMap<String, usize>,
) -> Option<FieldDesc> {
match desc {
FieldDesc::Typedef { id, type_id, .. } => enum_declarations.get(type_id).map(|width| {
FieldDesc::Enum { id: id.to_owned(), enum_id: type_id.to_owned(), width: *width }
}),
_ => None,
}
}

for decl in &mut file.declarations {
match &mut decl.desc {
DeclDesc::Packet { fields, .. }
| DeclDesc::Struct { fields, .. }
| DeclDesc::Group { fields, .. } => {
// Replace enum Typedef fields in other declarations.
for field in fields.iter_mut() {
if let Some(desc) = to_enum_field(&field.desc, &enum_declarations) {
field.desc = desc
}
}
}
_ => (),
}
}
}

/// Analyzer entry point, produces a new AST with annotations resulting
/// from the analysis.
pub fn analyze(file: &File) -> Result<File, Diagnostics> {
Expand All @@ -1850,6 +1949,7 @@ pub fn analyze(file: &File) -> Result<File, Diagnostics> {
check_group_constraints(file, &scope)?;
let mut file = inline_groups(file)?;
desugar_flags(&mut file);
desugar_enums(&mut file);
let scope = Scope::new(&file)?;
check_decl_constraints(&file, &scope)?;
let schema = Schema::new(&file);
Expand Down
7 changes: 6 additions & 1 deletion pdl-compiler/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,13 @@ pub enum FieldDesc {
},
#[serde(rename = "scalar_field")]
Scalar { id: String, width: usize },
/// Special case of Typedef for enum types.
#[serde(rename = "enum_field")]
Enum { id: String, width: usize, enum_id: String },
/// Special case of Scalar for fields used as condition for
/// optional fields. The width is always 1.
#[serde(rename = "flag_field")]
Flag { id: String, optional_field_ids: Vec<(String, usize)> },
Flag { id: String, optional_field_ids: Vec<String>, set_value: usize },
#[serde(rename = "typedef_field")]
Typedef { id: String, type_id: String },
#[serde(rename = "group_field")]
Expand Down Expand Up @@ -501,6 +504,7 @@ impl Field {
| FieldDesc::Group { .. } => None,
FieldDesc::Array { id, .. }
| FieldDesc::Scalar { id, .. }
| FieldDesc::Enum { id, .. }
| FieldDesc::Flag { id, .. }
| FieldDesc::Typedef { id, .. } => Some(id),
}
Expand All @@ -520,6 +524,7 @@ impl Field {
FieldDesc::Group { .. } => "group",
FieldDesc::Array { .. } => "array",
FieldDesc::Scalar { .. } => "scalar",
FieldDesc::Enum { .. } => "enum",
FieldDesc::Flag { .. } => "scalar",
FieldDesc::Typedef { .. } => "typedef",
}
Expand Down
48 changes: 24 additions & 24 deletions pdl-compiler/src/backends/rust/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,28 +99,28 @@ impl<'a> FieldParser<'a> {
let #id = (#cond_id == #cond_value).then(|| #value);
}
}
ast::FieldDesc::Typedef { id, type_id } => match &self.scope.typedef[type_id].desc {
ast::DeclDesc::Enum { width, .. } => {
let name = id;
let type_name = type_id;
let id = id.to_ident();
let type_id = type_id.to_ident();
let decl_id = &self.packet_name;
let value = types::get_uint(self.endianness, *width, self.span);
quote! {
let #id = (#cond_id == #cond_value)
.then(||
#type_id::try_from(#value).map_err(|unknown_val| {
DecodeError::InvalidEnumValueError {
obj: #decl_id,
field: #name,
value: unknown_val as u64,
type_: #type_name,
}
}))
.transpose()?;
}
ast::FieldDesc::Enum { id, enum_id, width } => {
let name = id;
let type_name = enum_id;
let id = id.to_ident();
let type_id = enum_id.to_ident();
let decl_id = &self.packet_name;
let value = types::get_uint(self.endianness, *width, self.span);
quote! {
let #id = (#cond_id == #cond_value)
.then(||
#type_id::try_from(#value).map_err(|unknown_val| {
DecodeError::InvalidEnumValueError {
obj: #decl_id,
field: #name,
value: unknown_val as u64,
type_: #type_name,
}
}))
.transpose()?;
}
}
ast::FieldDesc::Typedef { id, type_id } => match &self.scope.typedef[type_id].desc {
ast::DeclDesc::Struct { .. } => {
let id = id.to_ident();
let type_id = type_id.to_ident();
Expand Down Expand Up @@ -226,12 +226,12 @@ impl<'a> FieldParser<'a> {
}
}
}
ast::FieldDesc::Typedef { id, type_id } => {
ast::FieldDesc::Enum { id, enum_id, .. } => {
let field_name = id;
let type_name = type_id;
let type_name = enum_id;
let packet_name = &self.packet_name;
let id = id.to_ident();
let type_id = type_id.to_ident();
let type_id = enum_id.to_ident();
quote! {
let #id = #type_id::try_from(#v).map_err(|unknown_val| DecodeError::InvalidEnumValueError {
obj: #packet_name,
Expand Down
45 changes: 21 additions & 24 deletions pdl-compiler/src/backends/rust/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,35 +167,32 @@ impl Encoder {
}
}
}
ast::FieldDesc::Typedef { id, type_id } => match &scope.typedef[type_id].desc {
ast::DeclDesc::Enum { width, .. } => {
let id = id.to_ident();
let backing_type = types::Integer::new(*width);
let put_uint = types::put_uint(
self.endianness,
&quote!(#backing_type::from(#id)),
*width,
&self.buf,
);
ast::FieldDesc::Enum { id, width, .. } => {
let id = id.to_ident();
let backing_type = types::Integer::new(*width);
let put_uint = types::put_uint(
self.endianness,
&quote!(#backing_type::from(#id)),
*width,
&self.buf,
);

quote! {
if let Some(#id) = &self.#id {
#put_uint;
}
quote! {
if let Some(#id) = &self.#id {
#put_uint;
}
}
ast::DeclDesc::Struct { .. } => {
let id = id.to_ident();
let buf = &self.buf;
}
ast::FieldDesc::Typedef { id, .. } => {
let id = id.to_ident();
let buf = &self.buf;

quote! {
if let Some(#id) = &self.#id {
#id.encode(#buf)?;
}
quote! {
if let Some(#id) = &self.#id {
#id.encode(#buf)?;
}
}
_ => unreachable!(),
},
}
_ => unreachable!(),
});

Expand Down Expand Up @@ -320,7 +317,7 @@ impl Encoder {
let value = proc_macro2::Literal::usize_unsuffixed(*value);
self.bit_fields.push(BitField { value: quote!(#value), field_type, shift });
}
ast::FieldDesc::Typedef { id, .. } => {
ast::FieldDesc::Enum { id, .. } => {
let id = id.to_ident();
let field_type = types::Integer::new(width);
self.bit_fields.push(BitField {
Expand Down
7 changes: 4 additions & 3 deletions pdl-compiler/src/backends/rust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ fn constraint_value(
let type_id = fields
.iter()
.filter_map(|f| match &f.desc {
ast::FieldDesc::Typedef { id, type_id } if id == &constraint.id => {
Some(type_id.to_ident())
ast::FieldDesc::Enum { id, enum_id, .. } if id == &constraint.id => {
Some(enum_id.to_ident())
}
_ => None,
})
Expand Down Expand Up @@ -213,8 +213,9 @@ fn constraint_value_str(fields: &[&'_ ast::Field], constraint: &ast::Constraint)
fn implements_copy(scope: &analyzer::Scope<'_>, field: &ast::Field) -> bool {
match &field.desc {
ast::FieldDesc::Scalar { .. } => true,
ast::FieldDesc::Enum { .. } => true,
ast::FieldDesc::Typedef { type_id, .. } => match &scope.typedef[type_id].desc {
ast::DeclDesc::Enum { .. } | ast::DeclDesc::CustomField { .. } => true,
ast::DeclDesc::CustomField { .. } => true,
ast::DeclDesc::Struct { .. } => false,
desc => unreachable!("unexpected declaration: {desc:?}"),
},
Expand Down
8 changes: 8 additions & 0 deletions pdl-compiler/src/backends/rust/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ pub fn rust_type(field: &ast::Field) -> proc_macro2::TokenStream {
let field_type = Integer::new(*width);
quote!(#field_type)
}
ast::FieldDesc::Enum { enum_id, .. } if field.cond.is_some() => {
let field_type = enum_id.to_ident();
quote!(Option<#field_type>)
}
ast::FieldDesc::Enum { enum_id, .. } => {
let field_type = enum_id.to_ident();
quote!(#field_type)
}
ast::FieldDesc::Typedef { type_id, .. } if field.cond.is_some() => {
let field_type = type_id.to_ident();
quote!(Option<#field_type>)
Expand Down
Loading