Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e316d164 authored by Henri Chataing's avatar Henri Chataing
Browse files

pdl: Implement group inlining in the new analyzer

Test: cargo test
Change-Id: I5f06a09bb483fffeac88e77b085f32cb2a2a588e
Merged-In: I5f06a09bb483fffeac88e77b085f32cb2a2a588e
(cherry picked from commit 1efc3acb)
parent 15ebbbbb
Loading
Loading
Loading
Loading
+154 −3
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ use std::collections::HashMap;

use crate::ast::*;
use crate::parser::ast as parser_ast;
use crate::utils;

pub mod ast {
    use serde::Serialize;
@@ -25,7 +26,7 @@ pub mod ast {
        Unknown,
    }

    #[derive(Debug, Serialize, Default)]
    #[derive(Debug, Serialize, Default, Clone, PartialEq)]
    pub struct Annotation;

    #[derive(Default, Debug, Clone)]
@@ -1205,8 +1206,67 @@ fn compute_field_sizes(file: &parser_ast::File) -> ast::File {
}

/// Inline group fields and remove group declarations.
fn inline_groups(_file: &mut ast::File) -> Result<(), Diagnostics> {
    // TODO
fn inline_groups(file: &mut ast::File) -> Result<(), Diagnostics> {
    fn inline_fields<'a>(
        fields: impl Iterator<Item = &'a ast::Field>,
        groups: &HashMap<String, ast::Decl>,
        constraints: &HashMap<String, Constraint>,
    ) -> Vec<ast::Field> {
        fields
            .flat_map(|field| match &field.desc {
                FieldDesc::Group { group_id, constraints: group_constraints } => {
                    let mut constraints = constraints.clone();
                    constraints.extend(
                        group_constraints
                            .iter()
                            .map(|constraint| (constraint.id.clone(), constraint.clone())),
                    );
                    inline_fields(groups.get(group_id).unwrap().fields(), groups, &constraints)
                }
                FieldDesc::Scalar { id, width } if constraints.contains_key(id) => {
                    vec![ast::Field {
                        desc: FieldDesc::FixedScalar {
                            width: *width,
                            value: constraints.get(id).unwrap().value.unwrap(),
                        },
                        loc: field.loc,
                        annot: field.annot.clone(),
                    }]
                }
                FieldDesc::Typedef { id, type_id, .. } if constraints.contains_key(id) => {
                    vec![ast::Field {
                        desc: FieldDesc::FixedEnum {
                            enum_id: type_id.clone(),
                            tag_id: constraints
                                .get(id)
                                .and_then(|constraint| constraint.tag_id.clone())
                                .unwrap(),
                        },
                        loc: field.loc,
                        annot: field.annot.clone(),
                    }]
                }
                _ => vec![field.clone()],
            })
            .collect()
    }

    let groups = utils::drain_filter(&mut file.declarations, |decl| {
        matches!(&decl.desc, DeclDesc::Group { .. })
    })
    .into_iter()
    .map(|decl| (decl.id().unwrap().to_owned(), decl))
    .collect::<HashMap<String, _>>();

    for decl in file.declarations.iter_mut() {
        match &mut decl.desc {
            DeclDesc::Packet { fields, .. } | DeclDesc::Struct { fields, .. } => {
                *fields = inline_fields(fields.iter(), &groups, &HashMap::new())
            }
            _ => (),
        }
    }

    Ok(())
}

@@ -2052,4 +2112,95 @@ mod test {
        "#
        );
    }

    fn desugar(text: &str) -> analyzer::ast::File {
        let mut db = SourceDatabase::new();
        let file =
            parse_inline(&mut db, "stdin".to_owned(), text.to_owned()).expect("parsing failure");
        analyzer::analyze(&file).expect("analyzer failure")
    }

    #[test]
    fn test_inline_groups() {
        assert_eq!(
            desugar(
                r#"
        little_endian_packets
        enum E : 8 { X=0, Y=1 }
        group G {
            a: 8,
            b: E,
        }
        packet A {
            G { }
        }
        "#
            ),
            desugar(
                r#"
        little_endian_packets
        enum E : 8 { X=0, Y=1 }
        packet A {
            a: 8,
            b: E,
        }
        "#
            )
        );

        assert_eq!(
            desugar(
                r#"
        little_endian_packets
        enum E : 8 { X=0, Y=1 }
        group G {
            a: 8,
            b: E,
        }
        packet A {
            G { a=1, b=X }
        }
        "#
            ),
            desugar(
                r#"
        little_endian_packets
        enum E : 8 { X=0, Y=1 }
        packet A {
            _fixed_ = 1: 8,
            _fixed_ = X: E,
        }
        "#
            )
        );

        assert_eq!(
            desugar(
                r#"
        little_endian_packets
        enum E : 8 { X=0, Y=1 }
        group G1 {
            a: 8,
        }
        group G2 {
            G1 { a=1 },
            b: E,
        }
        packet A {
            G2 { b=X }
        }
        "#
            ),
            desugar(
                r#"
        little_endian_packets
        enum E : 8 { X=0, Y=1 }
        packet A {
            _fixed_ = 1: 8,
            _fixed_ = X: E,
        }
        "#
            )
        );
    }
}
+60 −3
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ pub struct Tag {
    pub value: usize,
}

#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
#[derive(Debug, Serialize, Clone)]
#[serde(tag = "kind", rename = "constraint")]
pub struct Constraint {
    pub id: String,
@@ -112,7 +112,7 @@ pub enum FieldDesc {
    Group { group_id: String, constraints: Vec<Constraint> },
}

#[derive(Debug, Serialize, PartialEq, Eq)]
#[derive(Debug, Serialize, Clone)]
pub struct Field<A: Annotation> {
    pub loc: SourceRange,
    #[serde(skip_serializing)]
@@ -128,7 +128,7 @@ pub struct TestCase {
    pub input: String,
}

#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, PartialEq, Eq)]
#[serde(tag = "kind")]
pub enum DeclDesc<A: Annotation> {
    #[serde(rename = "checksum_declaration")]
@@ -235,6 +235,47 @@ impl ops::Add<SourceRange> for SourceRange {
    }
}

impl Eq for Endianness {}
impl PartialEq for Endianness {
    fn eq(&self, other: &Self) -> bool {
        // Implement structual equality, leave out loc.
        self.value == other.value
    }
}

impl Eq for Tag {}
impl PartialEq for Tag {
    fn eq(&self, other: &Self) -> bool {
        // Implement structual equality, leave out loc.
        self.id == other.id && self.value == other.value
    }
}

impl Eq for Constraint {}
impl PartialEq for Constraint {
    fn eq(&self, other: &Self) -> bool {
        // Implement structual equality, leave out loc.
        self.id == other.id && self.value == other.value && self.tag_id == other.tag_id
    }
}

impl Eq for TestCase {}
impl PartialEq for TestCase {
    fn eq(&self, other: &Self) -> bool {
        // Implement structual equality, leave out loc.
        self.input == other.input
    }
}

impl<A: Annotation + std::cmp::PartialEq> Eq for File<A> {}
impl<A: Annotation + std::cmp::PartialEq> PartialEq for File<A> {
    fn eq(&self, other: &Self) -> bool {
        // Implement structual equality, leave out comments and PDL
        // version information.
        self.endianness == other.endianness && self.declarations == other.declarations
    }
}

impl<A: Annotation> File<A> {
    pub fn new(file: FileId) -> File<A> {
        File {
@@ -259,6 +300,14 @@ impl<A: Annotation> File<A> {
    }
}

impl<A: Annotation + std::cmp::PartialEq> Eq for Decl<A> {}
impl<A: Annotation + std::cmp::PartialEq> PartialEq for Decl<A> {
    fn eq(&self, other: &Self) -> bool {
        // Implement structual equality, leave out loc and annot.
        self.desc == other.desc
    }
}

impl<A: Annotation> Decl<A> {
    pub fn new(loc: SourceRange, desc: DeclDesc<A>) -> Decl<A> {
        Decl { loc, annot: Default::default(), desc }
@@ -382,6 +431,14 @@ impl<A: Annotation> Decl<A> {
    }
}

impl<A: Annotation> Eq for Field<A> {}
impl<A: Annotation> PartialEq for Field<A> {
    fn eq(&self, other: &Self) -> bool {
        // Implement structual equality, leave out loc and annot.
        self.desc == other.desc
    }
}

impl<A: Annotation> Field<A> {
    pub fn annotate<B: Annotation>(&self, annot: B::FieldAnnotation) -> Field<B> {
        Field { loc: self.loc, annot, desc: self.desc.clone() }
+0 −7
Original line number Diff line number Diff line
@@ -45,13 +45,6 @@ pub struct PacketScope<'d> {
    pub all_constraints: HashMap<String, &'d Constraint>,
}

impl std::cmp::Eq for &parser::ast::Decl {}
impl<'d> std::cmp::PartialEq for &'d parser::ast::Decl {
    fn eq(&self, other: &Self) -> bool {
        std::ptr::eq(*self, *other)
    }
}

impl<'d> std::hash::Hash for &'d parser::ast::Decl {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        std::ptr::hash(*self, state);
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ mod lint;
mod parser;
#[cfg(test)]
mod test_utils;
mod utils;

#[derive(Debug)]
enum OutputFormat {
+1 −1
Original line number Diff line number Diff line
@@ -77,7 +77,7 @@ array_field = { identifier ~ ":" ~ (integer|identifier) ~
}
scalar_field = { identifier ~ ":" ~ integer }
typedef_field = { identifier ~ ":" ~ identifier }
group_field = { identifier ~ ("{" ~ constraint_list ~ "}")? }
group_field = { identifier ~ ("{" ~ constraint_list? ~ "}")? }

field = _{
    checksum_field |
Loading