use crate::error::{Error, ErrorCode};
use crate::format::{Arbitrary, Character, Expression};
use core::slice::Iter;
use core::str;
use core::convert::TryFrom;
use crate::expression::{channel_list, numeric_list};
use crate::{util, NumericValues};
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Token<'a> {
HeaderMnemonicSeparator,
HeaderCommonPrefix,
HeaderQuerySuffix,
ProgramMessageUnitSeparator,
ProgramHeaderSeparator,
ProgramDataSeparator,
ProgramMnemonic(&'a [u8]),
CharacterProgramData(&'a [u8]),
DecimalNumericProgramData(&'a [u8]),
DecimalNumericSuffixProgramData(&'a [u8], &'a [u8]),
NonDecimalNumericProgramData(u64),
StringProgramData(&'a [u8]),
ArbitraryBlockData(&'a [u8]),
ExpressionProgramData(&'a [u8]),
}
impl<'a> Token<'a> {
pub fn is_data(&self) -> bool {
matches!(
self,
Self::CharacterProgramData(_)
| Self::DecimalNumericProgramData(_)
| Self::DecimalNumericSuffixProgramData(_, _)
| Self::NonDecimalNumericProgramData(_)
| Self::StringProgramData(_)
| Self::ArbitraryBlockData(_)
| Self::ExpressionProgramData(_)
)
}
pub fn match_program_header(&self, mnemonic: &'a [u8]) -> bool {
match self {
Token::ProgramMnemonic(s) | Token::CharacterProgramData(s) => {
util::mnemonic_compare(mnemonic, s)
|| match (
util::mnemonic_split_index(mnemonic),
util::mnemonic_split_index(s),
) {
(None, None) => false,
(Some((m, index)), None) => util::mnemonic_compare(m, s) && index == b"1",
(None, Some((x, index))) => {
util::mnemonic_compare(mnemonic, x) && index == b"1"
}
(Some((m, index1)), Some((x, index2))) => {
util::mnemonic_compare(m, x) && (index1 == index2)
}
}
}
_ => false,
}
}
pub fn numeric<F, R: TryFrom<Token<'a>, Error = Error>>(self, special: F) -> Result<R, Error>
where
F: FnOnce(NumericValues) -> Result<R, Error>,
{
match self {
Token::CharacterProgramData(s) => match s {
x if util::mnemonic_compare(b"MAXimum", x) => special(NumericValues::Maximum),
x if util::mnemonic_compare(b"MINimum", x) => special(NumericValues::Minimum),
x if util::mnemonic_compare(b"DEFault", x) => special(NumericValues::Default),
x if util::mnemonic_compare(b"UP", x) => special(NumericValues::Up),
x if util::mnemonic_compare(b"DOWN", x) => special(NumericValues::Down),
x if util::mnemonic_compare(b"AUTO", x) => special(NumericValues::Auto),
_ => <R>::try_from(self),
},
_ => <R>::try_from(self),
}
}
pub fn numeric_range<F, R: TryFrom<Token<'a>, Error = Error>>(
&self,
min: R,
max: R,
special: F,
) -> Result<R, Error>
where
F: FnOnce(NumericValues) -> Result<R, Error>,
R: PartialOrd + Copy,
{
let value = self.numeric(|choice| match choice {
NumericValues::Maximum => Ok(max),
NumericValues::Minimum => Ok(min),
x => special(x),
})?;
if value > max || value < min {
Err(ErrorCode::DataOutOfRange.into())
} else {
Ok(value)
}
}
}
impl<'a> TryFrom<Token<'a>> for &'a [u8] {
type Error = Error;
fn try_from(value: Token<'a>) -> Result<&'a [u8], Self::Error> {
match value {
Token::StringProgramData(s) => Ok(s),
t => {
if t.is_data() {
Err(ErrorCode::DataTypeError.into())
} else {
Err(Error::extended(
ErrorCode::DeviceSpecificError,
b"Parser error",
))
}
}
}
}
}
impl<'a> TryFrom<Token<'a>> for bool {
type Error = Error;
fn try_from(value: Token<'a>) -> Result<bool, Self::Error> {
match value {
Token::DecimalNumericProgramData(_) => {
Ok(<isize>::try_from(value)? != 0)
}
Token::CharacterProgramData(s) => {
if s.eq_ignore_ascii_case(b"ON") {
Ok(true)
} else if s.eq_ignore_ascii_case(b"OFF") {
Ok(false)
} else {
Err(ErrorCode::IllegalParameterValue.into())
}
}
t => {
if t.is_data() {
Err(ErrorCode::DataTypeError.into())
} else {
Err(Error::extended(
ErrorCode::DeviceSpecificError,
b"Parser error",
))
}
}
}
}
}
impl<'a> TryFrom<Token<'a>> for &'a str {
type Error = Error;
fn try_from(value: Token<'a>) -> Result<&'a str, Self::Error> {
match value {
Token::StringProgramData(s) | Token::ArbitraryBlockData(s) => {
str::from_utf8(s).map_err(|_| ErrorCode::StringDataError.into())
}
t => {
if t.is_data() {
Err(ErrorCode::DataTypeError.into())
} else {
Err(Error::extended(
ErrorCode::DeviceSpecificError,
b"Parser error",
))
}
}
}
}
}
impl<'a> TryFrom<Token<'a>> for Arbitrary<'a> {
type Error = Error;
fn try_from(value: Token<'a>) -> Result<Arbitrary<'a>, Self::Error> {
match value {
Token::ArbitraryBlockData(s) => Ok(Arbitrary(s)),
t => {
if t.is_data() {
Err(ErrorCode::DataTypeError.into())
} else {
Err(Error::extended(
ErrorCode::DeviceSpecificError,
b"Parser error",
))
}
}
}
}
}
impl<'a> TryFrom<Token<'a>> for Character<'a> {
type Error = Error;
fn try_from(value: Token<'a>) -> Result<Character<'a>, Self::Error> {
match value {
Token::CharacterProgramData(s) => Ok(Character(s)),
t => {
if t.is_data() {
Err(ErrorCode::DataTypeError.into())
} else {
Err(Error::extended(
ErrorCode::DeviceSpecificError,
b"Parser error",
))
}
}
}
}
}
impl<'a> TryFrom<Token<'a>> for numeric_list::NumericList<'a> {
type Error = Error;
fn try_from(value: Token<'a>) -> Result<numeric_list::NumericList<'a>, Self::Error> {
match value {
Token::ExpressionProgramData(s) => Ok(numeric_list::NumericList::new(s)),
t => {
if t.is_data() {
Err(ErrorCode::DataTypeError.into())
} else {
Err(Error::extended(
ErrorCode::DeviceSpecificError,
b"Parser error",
))
}
}
}
}
}
impl<'a> TryFrom<Token<'a>> for channel_list::ChannelList<'a> {
type Error = Error;
fn try_from(value: Token<'a>) -> Result<channel_list::ChannelList<'a>, Self::Error> {
match value {
Token::ExpressionProgramData(s) => channel_list::ChannelList::new(s).ok_or_else(|| {
Error::extended(ErrorCode::InvalidExpression, b"Invalid channel list")
}),
t => {
if t.is_data() {
Err(ErrorCode::DataTypeError.into())
} else {
Err(Error::extended(
ErrorCode::DeviceSpecificError,
b"Parser error",
))
}
}
}
}
}
impl<'a> TryFrom<Token<'a>> for Expression<'a> {
type Error = Error;
fn try_from(value: Token<'a>) -> Result<Expression<'a>, Self::Error> {
match value {
Token::ArbitraryBlockData(s) => Ok(Expression(s)),
t => {
if t.is_data() {
Err(ErrorCode::DataTypeError.into())
} else {
Err(Error::extended(
ErrorCode::DeviceSpecificError,
b"Parser error",
))
}
}
}
}
}
macro_rules! impl_tryfrom_float {
($from:ty) => {
impl<'a> TryFrom<Token<'a>> for $from {
type Error = Error;
fn try_from(value: Token) -> Result<Self, Self::Error> {
match value {
Token::DecimalNumericProgramData(value) => lexical_core::parse::<$from>(value)
.map_err(|e| match e {
lexical_core::Error::InvalidDigit(_) => {
ErrorCode::InvalidCharacterInNumber.into()
}
lexical_core::Error::Overflow(_)
| lexical_core::Error::Underflow(_) => ErrorCode::DataOutOfRange.into(),
_ => ErrorCode::NumericDataError.into(),
}),
Token::CharacterProgramData(s) => match s {
ref x if util::mnemonic_compare(b"INFinity", x) => Ok(<$from>::INFINITY),
ref x if util::mnemonic_compare(b"NINFinity", x) => {
Ok(<$from>::NEG_INFINITY)
}
ref x if util::mnemonic_compare(b"NAN", x) => Ok(<$from>::NAN),
ref x if util::mnemonic_compare(b"MAXimum", x) => Ok(<$from>::MAX),
ref x if util::mnemonic_compare(b"MINimum", x) => Ok(<$from>::MIN),
_ => Err(ErrorCode::DataTypeError.into()),
},
Token::DecimalNumericSuffixProgramData(_, _) => {
Err(ErrorCode::SuffixNotAllowed.into())
}
t => {
if t.is_data() {
Err(ErrorCode::DataTypeError.into())
} else {
Err(Error::extended(
ErrorCode::DeviceSpecificError,
b"Parser error",
))
}
}
}
}
}
};
}
impl_tryfrom_float!(f32);
impl_tryfrom_float!(f64);
macro_rules! impl_tryfrom_integer {
($from:ty, $intermediate:ty) => {
impl<'a> TryFrom<Token<'a>> for $from {
type Error = Error;
fn try_from(value: Token) -> Result<Self, Self::Error> {
match value {
Token::DecimalNumericProgramData(value) => lexical_core::parse::<$from>(value)
.or_else(|e| {
if matches!(e, lexical_core::Error::InvalidDigit(_)) {
let value = lexical_core::parse::<$intermediate>(value)?;
if !value.is_normal() {
Err(lexical_core::Error::Overflow(0).into())
} else if value > (<$from>::MAX as $intermediate) {
Err(lexical_core::Error::Overflow(0).into())
} else if value < (<$from>::MIN as $intermediate) {
Err(lexical_core::Error::Underflow(0).into())
} else {
if value.is_sign_positive() {
Ok(unsafe { (value + 0.5).to_int_unchecked() })
} else {
Ok(unsafe { (value - 0.5).to_int_unchecked() })
}
}
} else {
Err(e)
}
})
.map_err(|e| match e {
lexical_core::Error::InvalidDigit(_) => {
ErrorCode::InvalidCharacterInNumber.into()
}
lexical_core::Error::Overflow(_)
| lexical_core::Error::Underflow(_) => ErrorCode::DataOutOfRange.into(),
_ => ErrorCode::NumericDataError.into(),
}),
Token::NonDecimalNumericProgramData(value) => {
<$from>::try_from(value).map_err(|_| ErrorCode::DataOutOfRange.into())
}
Token::CharacterProgramData(s) => match s {
ref x if util::mnemonic_compare(b"MAXimum", x) => Ok(<$from>::MAX),
ref x if util::mnemonic_compare(b"MINimum", x) => Ok(<$from>::MIN),
_ => Err(ErrorCode::DataTypeError.into()),
},
Token::DecimalNumericSuffixProgramData(_, _) => {
Err(ErrorCode::SuffixNotAllowed.into())
}
t => {
if t.is_data() {
Err(ErrorCode::DataTypeError.into())
} else {
Err(Error::extended(
ErrorCode::DeviceSpecificError,
b"Parser error",
))
}
}
}
}
}
};
}
impl_tryfrom_integer!(usize, f64);
impl_tryfrom_integer!(isize, f64);
impl_tryfrom_integer!(i64, f64);
impl_tryfrom_integer!(u64, f64);
impl_tryfrom_integer!(i32, f64);
impl_tryfrom_integer!(u32, f64);
impl_tryfrom_integer!(i16, f32);
impl_tryfrom_integer!(u16, f32);
impl_tryfrom_integer!(i8, f32);
impl_tryfrom_integer!(u8, f32);
#[derive(Clone)]
pub struct Tokenizer<'a> {
pub(crate) chars: Iter<'a, u8>,
in_header: bool,
in_common: bool,
}
impl<'a> Tokenizer<'a> {
pub fn next_data(&mut self, optional: bool) -> Result<Option<Token<'a>>, Error> {
if let Some(item) = self.clone().next() {
let token = item?;
match token {
t if t.is_data() => {
self.next();
Ok(Some(token))
}
Token::ProgramDataSeparator => {
self.next();
self.next_data(false)
}
_ => {
if optional {
Ok(None)
} else {
Err(ErrorCode::MissingParameter.into())
}
}
}
} else {
if optional {
Ok(None)
} else {
Err(ErrorCode::MissingParameter.into())
}
}
}
}
impl<'a> Tokenizer<'a> {
pub fn new(buf: &'a [u8]) -> Self {
Tokenizer::from_byte_iter(buf.iter())
}
pub(crate) fn empty() -> Self {
Tokenizer::from_byte_iter(b"".iter())
}
pub(crate) fn from_byte_iter(iter: Iter<'a, u8>) -> Self {
Tokenizer {
chars: iter,
in_header: true,
in_common: false,
}
}
fn read_mnemonic(&mut self) -> Result<Token<'a>, ErrorCode> {
let s = self.chars.as_slice();
let mut len = 0u8;
while self
.chars
.clone()
.next()
.map_or(false, |ch| ch.is_ascii_alphanumeric() || *ch == b'_')
{
self.chars.next();
len += 1;
if len > 12 {
return Err(ErrorCode::ProgramMnemonicTooLong);
}
}
Ok(Token::ProgramMnemonic(
&s[0..s.len() - self.chars.as_slice().len()],
))
}
pub(crate) fn read_character_data(&mut self) -> Result<Token<'a>, ErrorCode> {
let s = self.chars.as_slice();
let mut len = 0u8;
while self
.chars
.clone()
.next()
.map_or(false, |ch| ch.is_ascii_alphanumeric() || *ch == b'_')
{
self.chars.next();
len += 1;
if len > 12 {
return Err(ErrorCode::CharacterDataTooLong);
}
}
let ret = Ok(Token::CharacterProgramData(
&s[0..s.len() - self.chars.as_slice().len()],
));
self.skip_ws_to_separator(ErrorCode::InvalidCharacterData)?;
ret
}
pub(crate) fn read_nrf(&mut self) -> Result<Token<'a>, ErrorCode> {
let s = self.chars.as_slice();
util::skip_sign(&mut self.chars);
let leading_digits = util::skip_digits(&mut self.chars);
if let Some(b'.') = self.chars.clone().next() {
self.chars.next().unwrap();
if !util::skip_digits(&mut self.chars) && !leading_digits {
return Err(ErrorCode::NumericDataError);
}
} else if !leading_digits {
return Err(ErrorCode::NumericDataError);
}
if let Some(exponent) = self.chars.clone().next() {
if *exponent == b'E' || *exponent == b'e' {
self.chars.next().unwrap();
util::skip_sign(&mut self.chars);
if !util::skip_digits(&mut self.chars) {
return Err(ErrorCode::NumericDataError);
}
}
}
Ok(Token::DecimalNumericProgramData(
&s[0..s.len() - self.chars.as_slice().len()],
))
}
pub(crate) fn read_numeric_data(&mut self) -> Result<Token<'a>, ErrorCode> {
let tok = self.read_nrf()?;
if let Token::DecimalNumericProgramData(s) = tok {
util::skip_ws(&mut self.chars);
if let Some(x) = self.chars.clone().next() {
if x.is_ascii_alphabetic() || *x == b'/' {
return self.read_suffix_data(s);
} else {
self.skip_ws_to_separator(ErrorCode::InvalidSuffix)?;
}
}
}
Ok(tok)
}
fn read_suffix_data(&mut self, val: &'a [u8]) -> Result<Token<'a>, ErrorCode> {
let s = self.chars.as_slice();
let mut len = 0u8;
while self.chars
.clone()
.next()
.map_or(false, |ch| ch.is_ascii_alphanumeric() || *ch == b'-' || *ch == b'/' || *ch == b'.') {
self.chars.next();
len += 1;
if len > 12 {
return Err(ErrorCode::SuffixTooLong);
}
}
let ret = Ok(Token::DecimalNumericSuffixProgramData(
val,
&s[0..s.len() - self.chars.as_slice().len()],
));
self.skip_ws_to_separator(ErrorCode::InvalidSuffix)?;
ret
}
fn read_nondecimal_data(&mut self, radix: u8) -> Result<Token<'a>, ErrorCode> {
let options = lexical_core::ParseIntegerOptions::new();
let (n, len) = match radix {
b'H' | b'h' => {
const FORMAT: u128 = lexical_core::NumberFormatBuilder::from_radix(16);
lexical_core::parse_partial_with_options::<u64, FORMAT>(
self.chars.as_slice(),
&options,
)
}
b'Q' | b'q' => {
const FORMAT: u128 = lexical_core::NumberFormatBuilder::from_radix(8);
lexical_core::parse_partial_with_options::<u64, FORMAT>(
self.chars.as_slice(),
&options,
)
}
b'B' | b'b' => {
const FORMAT: u128 = lexical_core::NumberFormatBuilder::from_radix(2);
lexical_core::parse_partial_with_options::<u64, FORMAT>(
self.chars.as_slice(),
&options,
)
}
_ => return Err(ErrorCode::NumericDataError),
}
.map_err(|e| match e {
lexical_core::Error::InvalidDigit(_) => ErrorCode::InvalidCharacterInNumber,
lexical_core::Error::Overflow(_) | lexical_core::Error::Underflow(_) => {
ErrorCode::DataOutOfRange
}
_ => ErrorCode::NumericDataError,
})?;
if len > 0 {
self.chars.nth(len - 1).unwrap();
let ret = Token::NonDecimalNumericProgramData(n);
self.skip_ws_to_separator(ErrorCode::SuffixNotAllowed)?;
Ok(ret)
} else {
Err(ErrorCode::NumericDataError)
}
}
pub(crate) fn read_string_data(&mut self, x: u8, ascii: bool) -> Result<Token<'a>, ErrorCode> {
self.chars.next(); let s = self.chars.as_slice();
loop {
if let Some(c) = self.chars.next() {
if *c == x {
if let Some(c2) = self.chars.clone().next() {
if *c2 == x {
self.chars.next().unwrap();
continue;
} else {
break;
}
}
break;
}
if ascii && !c.is_ascii() {
return Err(ErrorCode::InvalidCharacter);
}
} else {
return Err(ErrorCode::InvalidStringData);
}
}
let ret = Ok(Token::StringProgramData(
&s[0..s.len() - self.chars.as_slice().len() - 1],
));
self.skip_ws_to_separator(ErrorCode::SuffixNotAllowed)?;
ret
}
fn read_arbitrary_data(&mut self, format: u8) -> Result<Token<'a>, ErrorCode> {
if let Some(len) = util::ascii_to_digit(format, 10) {
if len == 0 {
let rest = self.chars.as_slice();
if rest.is_empty() {
return Err(ErrorCode::InvalidBlockData);
}
let u8str = rest
.get(0..rest.len() - 1)
.ok_or(ErrorCode::InvalidBlockData)?;
for _ in u8str {
self.chars.next();
}
if *self.chars.next().unwrap() != b'\n' {
return Err(ErrorCode::InvalidBlockData);
}
return Ok(Token::ArbitraryBlockData(u8str));
}
let payload_len = lexical_core::parse::<usize>(
self.chars
.as_slice()
.get(..len as usize)
.ok_or(ErrorCode::InvalidBlockData)?,
)
.map_err(|_| ErrorCode::InvalidBlockData)?;
self.chars.nth(len as usize - 1).unwrap();
let u8str = self
.chars
.as_slice()
.get(0..payload_len)
.ok_or(ErrorCode::InvalidBlockData)?;
for _ in 0..payload_len {
self.chars.next();
}
let ret = Ok(Token::ArbitraryBlockData(u8str));
self.skip_ws_to_separator(ErrorCode::SuffixNotAllowed)?;
ret
} else {
Err(ErrorCode::InvalidBlockData)
}
}
pub(crate) fn read_expression_data(&mut self) -> Result<Token<'a>, ErrorCode> {
self.chars.next();
let s = self.chars.as_slice();
static ILLEGAL_CHARS: &[u8] = &[b'"', b'\'', b';', b'(', b')'];
while self.chars.clone().next().map_or(false, |ch| *ch != b')') {
let c = self.chars.next().unwrap();
if ILLEGAL_CHARS.contains(c) || !c.is_ascii() {
return Err(ErrorCode::InvalidExpression);
}
}
let ret = Ok(Token::ExpressionProgramData(
&s[0..s.len() - self.chars.as_slice().len()],
));
if self.chars.next().is_none() {
return Err(ErrorCode::InvalidExpression);
}
self.skip_ws_to_separator(ErrorCode::SuffixNotAllowed)?;
ret
}
fn skip_ws_to_separator(&mut self, error: ErrorCode) -> Result<(), ErrorCode> {
util::skip_ws(&mut self.chars);
if let Some(c) = self.chars.clone().next() {
if *c != b',' && *c != b';' && *c != b'\n' {
return Err(error);
}
}
Ok(())
}
}
#[cfg(test)]
mod test_parse {
use crate::error::ErrorCode;
use crate::tokenizer::{Token, Tokenizer};
use crate::util;
extern crate std;
#[test]
fn test_split_mnemonic() {
assert_eq!(
util::mnemonic_split_index(b"TRIGger54"),
Some((b"TRIGger".as_ref(), b"54".as_ref()))
);
assert_eq!(
util::mnemonic_split_index(b"T123r54"),
Some((b"T123r".as_ref(), b"54".as_ref()))
);
assert_eq!(util::mnemonic_split_index(b"TRIGger"), None);
assert_eq!(util::mnemonic_split_index(b""), None);
}
#[test]
fn test_compare_mnemonic() {
assert!(util::mnemonic_compare(b"TRIGger", b"trigger"));
assert!(util::mnemonic_compare(b"TRIGger", b"trig"));
assert!(util::mnemonic_compare(b"TRIGger", b"TRIGGER"));
assert!(util::mnemonic_compare(b"TRIGger", b"TRIG"));
assert!(!util::mnemonic_compare(b"TRIGger", b"TRIGge"));
assert!(!util::mnemonic_compare(b"TRIGger", b"triggeristoodamnlong"));
assert!(!util::mnemonic_compare(b"TRIGger", b"tri"));
}
#[test]
fn test_eq_mnemonic() {
assert!(Token::ProgramMnemonic(b"trigger").match_program_header(b"TRIGger1"));
assert!(Token::ProgramMnemonic(b"trig").match_program_header(b"TRIGger1"));
assert!(Token::ProgramMnemonic(b"trigger1").match_program_header(b"TRIGger1"));
assert!(Token::ProgramMnemonic(b"trigger1").match_program_header(b"TRIGger"));
assert!(Token::ProgramMnemonic(b"trig1").match_program_header(b"TRIGger1"));
assert!(!Token::ProgramMnemonic(b"trigger2").match_program_header(b"TRIGger1"));
assert!(!Token::ProgramMnemonic(b"trig2").match_program_header(b"TRIGger1"));
assert!(!Token::ProgramMnemonic(b"trig2").match_program_header(b"TRIGger1"));
assert!(Token::ProgramMnemonic(b"trigger2").match_program_header(b"TRIGger2"));
assert!(Token::ProgramMnemonic(b"trig2").match_program_header(b"TRIGger2"));
assert!(!Token::ProgramMnemonic(b"trigger").match_program_header(b"TRIGger2"));
assert!(!Token::ProgramMnemonic(b"trig").match_program_header(b"TRIGger2"));
assert!(!Token::ProgramMnemonic(b"trigger1").match_program_header(b"TRIGger2"));
assert!(!Token::ProgramMnemonic(b"trig1").match_program_header(b"TRIGger2"));
}
#[test]
fn test_read_character_data() {
assert_eq!(
Tokenizer::new(b"CHARacter4 , pperg").read_character_data(),
Ok(Token::CharacterProgramData(b"CHARacter4"))
);
assert_eq!(
Tokenizer::new(b"CHARacterIsTooLong").read_character_data(),
Err(ErrorCode::CharacterDataTooLong)
);
assert_eq!(
Tokenizer::new(b"Character Invalid").read_character_data(),
Err(ErrorCode::InvalidCharacterData)
);
}
#[test]
fn test_read_numeric_data() {
assert_eq!(
Tokenizer::new(b"25").read_numeric_data().unwrap(),
Token::DecimalNumericProgramData(b"25")
);
assert_eq!(
Tokenizer::new(b".2").read_numeric_data().unwrap(),
Token::DecimalNumericProgramData(b".2")
);
assert_eq!(
Tokenizer::new(b"1.0E5").read_numeric_data().unwrap(),
Token::DecimalNumericProgramData(b"1.0E5")
);
assert_eq!(
Tokenizer::new(b"-25e5").read_numeric_data().unwrap(),
Token::DecimalNumericProgramData(b"-25e5")
);
assert_eq!(
Tokenizer::new(b"25E-2").read_numeric_data().unwrap(),
Token::DecimalNumericProgramData(b"25E-2")
);
assert_eq!(
Tokenizer::new(b".1E2").read_numeric_data().unwrap(),
Token::DecimalNumericProgramData(b".1E2")
);
assert_eq!(
Tokenizer::new(b".1E2 SUFFIX").read_numeric_data().unwrap(),
Token::DecimalNumericSuffixProgramData(b".1E2", b"SUFFIX")
);
assert_eq!(
Tokenizer::new(b".1E2 /S").read_numeric_data().unwrap(),
Token::DecimalNumericSuffixProgramData(b".1E2", b"/S")
);
assert_eq!(
Tokenizer::new(b".1E2 'SUFFIX'")
.read_numeric_data()
.unwrap_err(),
ErrorCode::InvalidSuffix
);
}
#[test]
fn test_read_suffix_data() {}
#[test]
fn test_read_numeric_suffix_data() {
let mut tokenizer = Tokenizer::new(b"header 25 KHZ , 12.7E6 KOHM.M/S-2");
assert_eq!(
tokenizer.next(),
Some(Ok(Token::ProgramMnemonic(b"header")))
);
assert_eq!(tokenizer.next(), Some(Ok(Token::ProgramHeaderSeparator)));
assert_eq!(
tokenizer.next(),
Some(Ok(Token::DecimalNumericSuffixProgramData(b"25", b"KHZ")))
);
assert_eq!(tokenizer.next(), Some(Ok(Token::ProgramDataSeparator)));
assert_eq!(
tokenizer.next(),
Some(Ok(Token::DecimalNumericSuffixProgramData(
b"12.7E6",
b"KOHM.M/S-2"
)))
);
assert_eq!(tokenizer.next(), None);
}
#[test]
fn test_read_string_data() {
assert_eq!(
Tokenizer::new(b"\"MOHM\", gui").read_string_data(b'"', true),
Ok(Token::StringProgramData(b"MOHM"))
);
assert_eq!(
Tokenizer::new(b"'MOHM', gui").read_string_data(b'\'', true),
Ok(Token::StringProgramData(b"MOHM"))
);
assert_eq!(
Tokenizer::new(b"'MO''HM', gui").read_string_data(b'\'', true),
Ok(Token::StringProgramData(b"MO''HM"))
);
assert_eq!(
Tokenizer::new(b"\"MOHM").read_string_data(b'"', true),
Err(ErrorCode::InvalidStringData)
);
assert_eq!(
Tokenizer::new(b"'MOHM").read_string_data(b'"', true),
Err(ErrorCode::InvalidStringData)
);
assert_eq!(
Tokenizer::new(b"'MO\xffHM").read_string_data(b'"', true),
Err(ErrorCode::InvalidCharacter)
);
}
#[test]
fn test_read_arb_data() {
assert_eq!(
Tokenizer::new(b"02\x01\x02,").read_arbitrary_data(b'2'),
Ok(Token::ArbitraryBlockData(&[1, 2]))
);
assert_eq!(
Tokenizer::new(b"2\x01\x02,").read_arbitrary_data(b'1'),
Ok(Token::ArbitraryBlockData(&[1, 2]))
);
assert_eq!(
Tokenizer::new(b"02\x01").read_arbitrary_data(b'2'),
Err(ErrorCode::InvalidBlockData)
);
assert_eq!(
Tokenizer::new(b"a2\x01\x02,").read_arbitrary_data(b'2'),
Err(ErrorCode::InvalidBlockData)
);
assert_eq!(
Tokenizer::new(b"2\x01\x02,").read_arbitrary_data(b'2'),
Err(ErrorCode::InvalidBlockData)
);
assert_eq!(
Tokenizer::new(b"\x01\x02\n").read_arbitrary_data(b'0'),
Ok(Token::ArbitraryBlockData(&[1, 2]))
);
assert_eq!(
Tokenizer::new(b"\x01\x02").read_arbitrary_data(b'0'),
Err(ErrorCode::InvalidBlockData)
);
}
#[test]
fn test_read_expr_data() {
assert_eq!(
Tokenizer::new(b"(@1!2,2,3,4,5,#,POTATO)").read_expression_data(),
Ok(Token::ExpressionProgramData(b"@1!2,2,3,4,5,#,POTATO"))
);
}
}
impl<'a> Iterator for Tokenizer<'a> {
type Item = Result<Token<'a>, ErrorCode>;
fn next(&mut self) -> Option<Self::Item> {
let x = self.chars.clone().next()?;
match x {
b'*' => {
self.in_common = true;
self.chars.next();
if let Some(x) = self.chars.clone().next() {
if !x.is_ascii_alphabetic() {
return Some(Err(ErrorCode::CommandHeaderError));
}
}
Some(Ok(Token::HeaderCommonPrefix))
}
b':' => {
self.chars.next();
if let Some(x) = self.chars.clone().next() {
if !x.is_ascii_alphabetic() {
return Some(Err(ErrorCode::InvalidSeparator));
}
}
if !self.in_header || self.in_common {
Some(Err(ErrorCode::InvalidSeparator))
} else {
Some(Ok(Token::HeaderMnemonicSeparator))
}
}
b'?' => {
self.chars.next();
if let Some(x) = self.chars.clone().next() {
if !x.is_ascii_whitespace() && *x != b';' {
return Some(Err(ErrorCode::InvalidSeparator));
}
}
if !self.in_header {
Some(Err(ErrorCode::InvalidSeparator))
} else {
self.in_header = false;
Some(Ok(Token::HeaderQuerySuffix))
}
}
b';' => {
self.chars.next();
util::skip_ws(&mut self.chars);
self.in_header = true;
self.in_common = false;
Some(Ok(Token::ProgramMessageUnitSeparator))
}
b'\n' => {
self.chars.next();
if self.chars.next().is_none() {
None
} else {
Some(Err(ErrorCode::SyntaxError))
}
}
b',' => {
self.chars.next();
if self.in_header {
Some(Err(ErrorCode::HeaderSeparatorError))
} else {
util::skip_ws(&mut self.chars);
if let Some(c) = self.chars.clone().next() {
if *c == b',' || *c == b';' || *c == b'\n' {
return Some(Err(ErrorCode::SyntaxError));
}
}
Some(Ok(Token::ProgramDataSeparator))
}
}
x if x.is_ascii_whitespace() => {
util::skip_ws(&mut self.chars);
self.in_header = false;
Some(Ok(Token::ProgramHeaderSeparator))
}
x if x.is_ascii_alphabetic() => {
if self.in_header {
Some(self.read_mnemonic())
} else {
Some(self.read_character_data())
}
}
x if x.is_ascii_digit() || *x == b'-' || *x == b'+' || *x == b'.' => {
if self.in_header {
Some(Err(ErrorCode::CommandHeaderError))
} else {
Some(self.read_numeric_data())
}
}
b'#' => {
self.chars.next();
if self.in_header {
Some(Err(ErrorCode::CommandHeaderError))
} else if let Some(x) = self.chars.next() {
Some(match x {
x if x.is_ascii_digit() => self.read_arbitrary_data(*x),
_ => self.read_nondecimal_data(*x),
})
} else {
Some(Err(ErrorCode::BlockDataError))
}
}
x if *x == b'\'' || *x == b'"' => {
if self.in_header {
Some(Err(ErrorCode::CommandHeaderError))
} else {
Some(self.read_string_data(*x, true))
}
}
b'(' => Some(self.read_expression_data()),
_ => {
let x = self.chars.next().unwrap();
if x.is_ascii() {
Some(Err(ErrorCode::SyntaxError))
} else {
Some(Err(ErrorCode::InvalidCharacter))
}
}
}
}
}