file and variable attributes
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 23 Dec 2024 23:01:09 +0000 (15:01 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Mon, 23 Dec 2024 23:01:09 +0000 (15:01 -0800)
rust/pspp/src/cooked.rs
rust/pspp/src/dictionary.rs
rust/pspp/src/identifier.rs
rust/pspp/src/raw.rs

index 84b473fdf7bd452642ab47fd44b19105cdf60d87..5cf07f67fa3c80b06f05714b4213bbc2fcee3ed3 100644 (file)
@@ -647,6 +647,31 @@ pub fn decode(
         }
     }
 
+    for (k, v) in headers
+        .file_attributes
+        .iter()
+        .flat_map(|map| map.0 .0.iter())
+    {
+        dictionary.attributes.insert(k.clone(), v.clone());
+    }
+
+    for attr_set in headers
+        .variable_attributes
+        .iter()
+        .flat_map(|record| record.0.iter())
+    {
+        if let Some((_, variable)) = dictionary
+            .variables
+            .get_full_mut2(&attr_set.long_var_name.0)
+        {
+            for (k, v) in attr_set.attributes.0.iter() {
+                variable.attributes.insert(k.clone(), v.clone());
+            }
+        } else {
+            warn(Error::TBD);
+        }
+    }
+
     let metadata = Metadata::decode(&headers, warn);
     Ok((dictionary, metadata))
 }
index e8988ca2c1d6cb00ededbbb515fad1d78201557f..ffe63571fac5bd726e0667a6b14c23318d64bc97 100644 (file)
@@ -417,31 +417,6 @@ pub enum Role {
     Split,
 }
 
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum DictClass {
-    Ordinary,
-    System,
-    Scratch,
-}
-
-impl DictClass {
-    pub fn must_leave(self) -> bool {
-        self == DictClass::Scratch
-    }
-}
-
-impl From<&Identifier> for DictClass {
-    fn from(id: &Identifier) -> Self {
-        if id.0.starts_with('$') {
-            Self::System
-        } else if id.0.starts_with('#') {
-            Self::Scratch
-        } else {
-            Self::Ordinary
-        }
-    }
-}
-
 #[derive(Clone, Debug)]
 pub struct Variable {
     pub name: Identifier,
@@ -457,13 +432,13 @@ pub struct Variable {
     pub alignment: Alignment,
     pub leave: bool,
     pub short_names: Vec<Identifier>,
-    pub attributes: HashSet<ByIdentifier<Attribute>>,
+    pub attributes: HashMap<Identifier, Vec<String>>,
 }
 
 impl Variable {
     pub fn new(name: Identifier, width: VarWidth) -> Self {
         let var_type = VarType::from_width(width);
-        let leave = DictClass::from(&name).must_leave();
+        let leave = name.class().must_leave();
         Self {
             name,
             width,
@@ -478,7 +453,7 @@ impl Variable {
             alignment: Alignment::default_for_type(var_type),
             leave,
             short_names: Vec::new(),
-            attributes: HashSet::new(),
+            attributes: HashMap::new(),
         }
     }
 
@@ -519,18 +494,6 @@ impl HasIdentifier for Vector {
     }
 }
 
-#[derive(Clone, Debug)]
-pub struct Attribute {
-    pub name: Identifier,
-    pub values: Vec<String>,
-}
-
-impl HasIdentifier for Attribute {
-    fn identifier(&self) -> &UniCase<String> {
-        &self.name.0
-    }
-}
-
 #[derive(Clone, Debug)]
 pub struct MultipleResponseSet {
     pub name: Identifier,
index 1f6694b6332ba9cf2a7ecfbdf4b209066c48f21a..c4e21544ff8e565bbd58ba31a814cbeefb21396e 100644 (file)
@@ -11,7 +11,40 @@ use finl_unicode::categories::{CharacterCategories, MajorCategory};
 use thiserror::Error as ThisError;
 use unicase::UniCase;
 
-use crate::dictionary::DictClass;
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum Class {
+    /// No distinguishing prefix.
+    Ordinary,
+
+    /// Starting with `$`.
+    System,
+
+    /// Starting with `#`.
+    Scratch,
+
+    /// Starting with `!`.
+    Macro,
+}
+
+impl Class {
+    pub fn must_leave(self) -> bool {
+        self == Self::Scratch
+    }
+}
+
+impl From<&Identifier> for Class {
+    fn from(id: &Identifier) -> Self {
+        if id.0.starts_with('$') {
+            Self::System
+        } else if id.0.starts_with('#') {
+            Self::Scratch
+        } else if id.0.starts_with('!') {
+            Self::Macro
+        } else {
+            Self::Ordinary
+        }
+    }
+}
 
 pub trait IdentifierChar {
     /// Returns true if `self` is an ASCII character that may be the first
@@ -158,7 +191,11 @@ impl Identifier {
     pub fn new(s: impl Into<UniCase<String>>) -> Result<Self, Error> {
         Self::from_encoding(s, UTF_8)
     }
-    pub fn from_encoding(s: impl Into<UniCase<String>>, encoding: &'static Encoding) -> Result<Identifier, Error> {
+
+    pub fn from_encoding(
+        s: impl Into<UniCase<String>>,
+        encoding: &'static Encoding,
+    ) -> Result<Identifier, Error> {
         let s: UniCase<String> = s.into();
         Self::is_plausible(&s)?;
         let identifier = Identifier(s);
@@ -246,8 +283,8 @@ impl Identifier {
     }
 
     pub fn must_be_ordinary(self) -> Result<Self, Error> {
-        match DictClass::from(&self)  {
-            DictClass::Ordinary => Ok(self),
+        match Class::from(&self) {
+            Class::Ordinary => Ok(self),
             _ => {
                 let s = self.0.into_inner();
                 let first = s.chars().next().unwrap();
@@ -255,6 +292,10 @@ impl Identifier {
             }
         }
     }
+
+    pub fn class(&self) -> Class {
+        self.into()
+    }
 }
 
 impl PartialEq<str> for Identifier {
index 63b7c04c3e1c66878f5b5688ceb9394892dee67e..a34d05bbd8168e06601d671844f3030d81b165d0 100644 (file)
@@ -2434,7 +2434,7 @@ impl VarAttributeSet {
 }
 
 #[derive(Clone, Debug)]
-pub struct VariableAttributeRecord(Vec<VarAttributeSet>);
+pub struct VariableAttributeRecord(pub Vec<VarAttributeSet>);
 
 impl VariableAttributeRecord {
     fn decode(source: &TextRecord, decoder: &Decoder) -> Self {