sack is fully implemented
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 1 Aug 2023 17:07:06 +0000 (10:07 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 1 Aug 2023 17:07:06 +0000 (10:07 -0700)
rust/src/sack.rs
rust/tests/sack.rs

index 4dfcf335ef43cd35c1b729ddf8084760c28b80d8..ab35f8661e8c4963b1e07380082fae995d4031b0 100644 (file)
@@ -101,14 +101,18 @@ fn parse_data_item(
     let initial_len = output.len();
     match lexer.take()? {
         Token::Integer(integer) => {
-            let Ok(integer): Result<i32, _> = integer.try_into() else {
+            if let Ok(integer) = TryInto::<i32>::try_into(integer) {
+                output.extend_from_slice(&lexer.endian.to_bytes(integer));
+            } else if let Ok(integer) = TryInto::<u32>::try_into(integer) {
+                output.extend_from_slice(&lexer.endian.to_bytes(integer));
+            } else {
                 Err(lexer.error(format!(
                     "{integer} is not in the valid range [{},{}]",
-                    u32::min_value(),
+                    i32::min_value(),
                     u32::max_value()
-                )))?
+                )))?;
             };
-            output.extend_from_slice(&lexer.endian.to_bytes(integer))},
+        }
         Token::Float(float) => output.extend_from_slice(&lexer.endian.to_bytes(float.0)),
         Token::PcSysmis => {
             output.extend_from_slice(&[0xf5, 0x1e, 0x26, 0x02, 0x8a, 0x8c, 0xed, 0xff])
@@ -143,18 +147,28 @@ fn parse_data_item(
             let Some((Token::String(ref string), _)) = lexer.token else {
                 Err(lexer.error(String::from("string expected after 'hex'")))?
             };
-            let mut i = string.chars();
+            let mut string = &string[..];
             loop {
+                string = string.trim_start();
+                if string.is_empty() {
+                    break;
+                };
+
+                let mut i = string.chars();
                 let Some(c0) = i.next() else { return Ok(true) };
                 let Some(c1) = i.next() else {
                     Err(lexer.error(String::from("hex string has odd number of characters")))?
                 };
+
                 let (Some(digit0), Some(digit1)) = (c0.to_digit(16), c1.to_digit(16)) else {
                     Err(lexer.error(String::from("invalid digit in hex string")))?
                 };
                 let byte = digit0 * 16 + digit1;
                 output.push(byte as u8);
+
+                string = i.as_str();
             }
+            lexer.get()?;
         }
         Token::Label(name) => {
             println!("define {name}");
@@ -177,11 +191,7 @@ fn parse_data_item(
             return Ok(true);
         }
         Token::At(name) => {
-            let mut value = symbol_table
-                .entry(name.clone())
-                .or_insert(None)
-                .unwrap_or(0);
-            println!("{name} has value {value}");
+            let mut value = *symbol_table.entry(name.clone()).or_insert(None);
             loop {
                 let plus = match lexer.token {
                     Some((Token::Plus, _)) => true,
@@ -191,27 +201,33 @@ fn parse_data_item(
                 lexer.get()?;
 
                 let operand = match lexer.token {
-                    Some((Token::At(ref name), _)) => if let Some(value) = symbol_table.get(name) {
-                        *value
-                    } else {
-                        symbol_table.insert(name.clone(), None);
-                        None
+                    Some((Token::At(ref name), _)) => {
+                        *symbol_table.entry(name.clone()).or_insert(None)
                     }
-                    .unwrap_or(0),
-                    Some((Token::Integer(integer), _)) => integer
-                        .try_into()
-                        .map_err(|msg| lexer.error(format!("bad offset literal ({msg})")))?,
+                    Some((Token::Integer(integer), _)) => Some(
+                        integer
+                            .try_into()
+                            .map_err(|msg| lexer.error(format!("bad offset literal ({msg})")))?,
+                    ),
                     _ => Err(lexer.error(String::from("expecting @label or integer literal")))?,
                 };
                 lexer.get()?;
 
-                value = if plus {
-                    value.checked_add(operand)
-                } else {
-                    value.checked_sub(operand)
-                }
-                .ok_or_else(|| lexer.error(String::from("overflow in offset arithmetic")))?;
+                value = match (value, operand) {
+                    (Some(a), Some(b)) => Some(
+                        if plus {
+                            a.checked_add(b)
+                        } else {
+                            a.checked_sub(b)
+                        }
+                        .ok_or_else(|| {
+                            lexer.error(String::from("overflow in offset arithmetic"))
+                        })?,
+                    ),
+                    _ => None,
+                };
             }
+            let value = value.unwrap_or(0);
             output.extend_from_slice(&lexer.endian.to_bytes(value));
         }
         _ => (),
index abb51abe2a2589e3038db0a7733fc3666a1fe12e..5db8d43bb305322a614d846d77804740fccfece2 100644 (file)
@@ -67,15 +67,27 @@ struct Args {
 }
 
 fn main() -> Result<()> {
-    let Args { be, le, input_file_name, output_file_name } = Args::parse();
+    let Args {
+        be,
+        le,
+        input_file_name,
+        output_file_name,
+    } = Args::parse();
     let endian = match (be, le) {
         (false, false) | (true, false) => Endian::Big,
         (false, true) => Endian::Little,
         (true, true) => return Err(anyhow!("can't use both `--be` and `--le`")),
     };
-    let input = read_to_string(&input_file_name)?;
-    let input_file_name = input_file_name.to_string_lossy();
-    let output = sack(&input, Some(&input_file_name), endian)?;
-    std::fs::write(&output_file_name, &output)?;
+
+    let input_file_str = input_file_name.to_string_lossy();
+    let input = read_to_string(&input_file_name)
+        .map_err(|err| anyhow!("{input_file_str}: read failed ({err})"))?;
+
+    let output = sack(&input, Some(&input_file_str), endian)?;
+
+    let output_file_str = output_file_name.to_string_lossy();
+    std::fs::write(&output_file_name, &output)
+        .map_err(|err| anyhow!("{output_file_str}: write failed ({err})"))?;
+
     Ok(())
 }