pub trait ToBytes<T, const N: usize> {
fn to_bytes(self, value: T) -> [u8; N];
}
+impl ToBytes<i64, 8> for Endian {
+ fn to_bytes(self, value: i64) -> [u8; 8] {
+ match self {
+ Endian::Big => i64::to_be_bytes(value),
+ Endian::Little => i64::to_le_bytes(value),
+ }
+ }
+}
+impl ToBytes<u32, 4> for Endian {
+ fn to_bytes(self, value: u32) -> [u8; 4] {
+ match self {
+ Endian::Big => u32::to_be_bytes(value),
+ Endian::Little => u32::to_le_bytes(value),
+ }
+ }
+}
+impl ToBytes<u16, 2> for Endian {
+ fn to_bytes(self, value: u16) -> [u8; 2] {
+ match self {
+ Endian::Big => u16::to_be_bytes(value),
+ Endian::Little => u16::to_le_bytes(value),
+ }
+ }
+}
+impl ToBytes<u8, 1> for Endian {
+ fn to_bytes(self, value: u8) -> [u8; 1] {
+ [value]
+ }
+}
impl ToBytes<f64, 8> for Endian {
fn to_bytes(self, value: f64) -> [u8; 8] {
match self {
use anyhow::{anyhow, Result};
use float_next_after::NextAfter;
-use std::{iter::Peekable, str::Chars};
+use num::Bounded;
+use ordered_float::OrderedFloat;
+use std::{fmt::Display, iter::Peekable, str::Chars};
-use crate::endian::Endian;
+use crate::endian::{Endian, ToBytes};
pub fn sack(input: &str, endian: Endian) -> Result<Vec<u8>> {
- let lexer = Lexer::new(input, endian)?;
- //let mut output = Vec::new();
+ let mut lexer = Lexer::new(input, endian)?;
+ let mut output = Vec::new();
+ while parse_data_item(&mut lexer, &mut output)? {}
Ok(Vec::new())
}
+fn parse_data_item(lexer: &mut Lexer, output: &mut Vec<u8>) -> Result<bool> {
+ if lexer.token.is_none() {
+ return Ok(false);
+ };
+ match lexer.take()? {
+ Token::Integer(integer) => 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])
+ }
+ Token::I8 => collect_integers::<u8, 1>(lexer, "i8", output)?,
+ Token::I16 => collect_integers::<u16, 2>(lexer, "i16", output)?,
+ Token::I64 => collect_integers::<i64, 8>(lexer, "i64", output)?,
+ _ => return Err(anyhow!("syntax error")),
+ }
+ Ok(true)
+}
+
+fn collect_integers<T, const N: usize>(
+ lexer: &mut Lexer,
+ name: &str,
+ output: &mut Vec<u8>,
+) -> Result<()>
+where
+ T: Bounded + Display + TryFrom<i64> + Copy,
+ Endian: ToBytes<T, N>,
+{
+ let mut n = 0;
+ while let Some(integer) = lexer.take_if(|t| match t {
+ Token::Integer(integer) => Some(*integer),
+ _ => None,
+ })? {
+ let Ok(integer) = integer.try_into() else {
+ return Err(anyhow!(
+ "{integer} is not in the valid range [{},{}]",
+ T::min_value(),
+ T::max_value()
+ ));
+ };
+ output.extend_from_slice(&lexer.endian.to_bytes(integer));
+ n += 1;
+ }
+ if n == 0 {
+ return Err(anyhow!("integer expected after '{name}'"));
+ }
+ Ok(())
+}
+
+#[derive(PartialEq, Eq, Clone)]
enum Token {
Integer(i64),
- Float(f64),
+ Float(OrderedFloat<f64>),
PcSysmis,
String(String),
Semicolon,
lexer.next()?;
Ok(lexer)
}
+ fn take(&mut self) -> Result<Token> {
+ let Some(token) = self.token.take() else {
+ return Err(anyhow!("unexpected end of input"));
+ };
+ self.token = self.next()?;
+ Ok(token)
+ }
+ fn take_if<F, T>(&mut self, condition: F) -> Result<Option<T>>
+ where
+ F: FnOnce(&Token) -> Option<T>,
+ {
+ let Some(ref token) = self.token else {
+ return Ok(None);
+ };
+ match condition(&token) {
+ Some(value) => {
+ self.token = self.next()?;
+ Ok(Some(value))
+ }
+ None => Ok(None),
+ }
+ }
fn get(&'a mut self) -> Result<Option<&'a Token>> {
if self.token.is_none() {
Err(anyhow!("unexpected end of input"))
"i8" => Token::I8,
"i16" => Token::I16,
"i64" => Token::I64,
- "SYSMIS" => Token::Float(-f64::MAX),
+ "SYSMIS" => Token::Float(OrderedFloat(-f64::MAX)),
"PCSYSMIS" => Token::PcSysmis,
- "LOWEST" => Token::Float((-f64::MAX).next_after(0.0)),
- "HIGHEST" => Token::Float(f64::MAX),
+ "LOWEST" => Token::Float((-f64::MAX).next_after(0.0).into()),
+ "HIGHEST" => Token::Float(f64::MAX.into()),
"ENDIAN" => Token::Integer(if self.endian == Endian::Big { 1 } else { 2 }),
"COUNT" => Token::Count,
"COUNT8" => Token::Count8,