use core::{convert::TryFrom, fmt};
#[cfg(not(feature = "std"))]
use alloc::borrow::Cow;
#[cfg(feature = "std")]
use std::borrow::Cow;
#[cfg(not(feature = "std"))]
use alloc::string::String;
#[cfg(target_arch = "wasm32")]
use serde::{Deserialize, Serialize};
#[derive(PartialEq, Debug, Clone, Copy)]
#[cfg_attr(target_arch = "wasm32", derive(Serialize, Deserialize))]
pub enum TagConstraint<'a> {
Literal(u64),
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
Type(&'a str),
}
impl<'a> TagConstraint<'a> {
pub fn as_literal(&self) -> Option<u64> {
match self {
TagConstraint::Literal(value) => Some(*value),
TagConstraint::Type(_) => None,
}
}
pub fn is_literal(&self, value: u64) -> bool {
matches!(self, TagConstraint::Literal(v) if *v == value)
}
}
impl fmt::Display for TagConstraint<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TagConstraint::Literal(n) => write!(f, "{}", n),
TagConstraint::Type(t) => write!(f, "<{}>", t),
}
}
}
#[derive(PartialEq, Debug, Clone)]
pub enum Token<'a> {
ILLEGAL(&'a str),
EOF,
IDENT(
&'a str,
Option<SocketPlug>,
),
VALUE(Value<'a>),
TAG(
Option<u8>,
Option<TagConstraint<'a>>,
),
ASSIGN,
OPTIONAL,
ASTERISK,
ONEORMORE,
UNWRAP,
COMMA,
COLON,
COMMENT(&'a str),
TCHOICE,
GCHOICE,
TCHOICEALT,
GCHOICEALT,
ARROWMAP,
CUT,
RANGEOP(bool),
RANGE(
RangeValue<'a>,
RangeValue<'a>,
bool,
),
LPAREN,
RPAREN,
LBRACE,
RBRACE,
LBRACKET,
RBRACKET,
LANGLEBRACKET,
RANGLEBRACKET,
ControlOperator(ControlOperator),
GTOCHOICE,
FALSE,
TRUE,
BOOL,
NIL,
NULL,
UINT,
NINT,
INT,
FLOAT16,
FLOAT32,
FLOAT64,
FLOAT1632,
FLOAT3264,
FLOAT,
BSTR,
TSTR,
ANY,
BYTES,
TEXT,
TDATE,
TIME,
NUMBER,
BIGUINT,
BIGNINT,
BIGINT,
INTEGER,
UNSIGNED,
DECFRAC,
BIGFLOAT,
EB64URL,
EB64LEGACY,
EB16,
ENCODEDCBOR,
URI,
B64URL,
B64LEGACY,
REGEXP,
MIMEMESSAGE,
CBORANY,
UNDEFINED,
NEWLINE,
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum ControlOperator {
SIZE,
BITS,
REGEXP,
CBOR,
CBORSEQ,
WITHIN,
AND,
LT,
LE,
GT,
GE,
EQ,
NE,
DEFAULT,
PCRE,
#[cfg(feature = "freezer")]
IREGEXP,
#[cfg(feature = "freezer")]
BITFIELD,
#[cfg(feature = "additional-controls")]
CAT,
#[cfg(feature = "additional-controls")]
DET,
#[cfg(feature = "additional-controls")]
PLUS,
#[cfg(feature = "additional-controls")]
ABNF,
#[cfg(feature = "additional-controls")]
ABNFB,
#[cfg(feature = "additional-controls")]
FEATURE,
#[cfg(feature = "additional-controls")]
B64U,
#[cfg(feature = "additional-controls")]
B64C,
#[cfg(feature = "additional-controls")]
B64USLOPPY,
#[cfg(feature = "additional-controls")]
B64CSLOPPY,
#[cfg(feature = "additional-controls")]
HEX,
#[cfg(feature = "additional-controls")]
HEXLC,
#[cfg(feature = "additional-controls")]
HEXUC,
#[cfg(feature = "additional-controls")]
B32,
#[cfg(feature = "additional-controls")]
H32,
#[cfg(feature = "additional-controls")]
B45,
#[cfg(feature = "additional-controls")]
BASE10,
#[cfg(feature = "additional-controls")]
PRINTF,
#[cfg(feature = "additional-controls")]
JSON,
#[cfg(feature = "additional-controls")]
JOIN,
}
impl Token<'_> {
pub fn in_standard_prelude(&self) -> Option<&'static str> {
match self {
Token::ANY => Some("any"),
Token::UINT => Some("uint"),
Token::NINT => Some("nint"),
Token::INT => Some("int"),
Token::BSTR => Some("bstr"),
Token::BYTES => Some("bytes"),
Token::TSTR => Some("tstr"),
Token::TEXT => Some("text"),
Token::TDATE => Some("tdate"),
Token::TIME => Some("time"),
Token::NUMBER => Some("number"),
Token::BIGUINT => Some("biguint"),
Token::BIGNINT => Some("bignint"),
Token::BIGINT => Some("bigint"),
Token::INTEGER => Some("integer"),
Token::UNSIGNED => Some("unsigned"),
Token::DECFRAC => Some("decfrac"),
Token::BIGFLOAT => Some("bigfloat"),
Token::EB64URL => Some("eb64url"),
Token::EB64LEGACY => Some("eb64legacy"),
Token::EB16 => Some("eb16"),
Token::ENCODEDCBOR => Some("encoded-cbor"),
Token::URI => Some("uri"),
Token::B64URL => Some("b64url"),
Token::B64LEGACY => Some("b64legacy"),
Token::REGEXP => Some("regexp"),
Token::MIMEMESSAGE => Some("mime-message"),
Token::CBORANY => Some("cbor-any"),
Token::FLOAT16 => Some("float16"),
Token::FLOAT32 => Some("float32"),
Token::FLOAT64 => Some("float64"),
Token::FLOAT1632 => Some("float16-32"),
Token::FLOAT3264 => Some("float32-64"),
Token::FLOAT => Some("float"),
Token::FALSE => Some("false"),
Token::TRUE => Some("true"),
Token::BOOL => Some("bool"),
Token::NIL => Some("nil"),
Token::NULL => Some("null"),
Token::UNDEFINED => Some("undefined"),
_ => None,
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum RangeValue<'a> {
IDENT(
&'a str,
Option<SocketPlug>,
),
INT(isize),
UINT(usize),
FLOAT(f64),
}
impl<'a> TryFrom<Token<'a>> for RangeValue<'a> {
type Error = &'static str;
fn try_from(t: Token<'a>) -> Result<Self, Self::Error> {
match t {
Token::IDENT(ident, socket) => Ok(RangeValue::IDENT(ident, socket)),
Token::VALUE(value) => match value {
Value::INT(i) => Ok(RangeValue::INT(i)),
Value::UINT(ui) => Ok(RangeValue::UINT(ui)),
Value::FLOAT(f) => Ok(RangeValue::FLOAT(f)),
_ => Err("Invalid range token"),
},
_ => Err("Invalid range token"),
}
}
}
impl RangeValue<'_> {
pub fn as_value(&self) -> Option<Value<'_>> {
match &self {
RangeValue::UINT(ui) => Some(Value::UINT(*ui)),
RangeValue::FLOAT(f) => Some(Value::FLOAT(*f)),
_ => None,
}
}
}
impl fmt::Display for RangeValue<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RangeValue::IDENT(ident, _) => write!(f, "{}", ident),
RangeValue::INT(i) => write!(f, "{}", i),
RangeValue::UINT(i) => write!(f, "{}", i),
RangeValue::FLOAT(fl) => write!(f, "{}", fl),
}
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Clone)]
pub enum Value<'a> {
INT(isize),
UINT(usize),
FLOAT(f64),
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
TEXT(Cow<'a, str>),
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
BYTE(ByteValue<'a>),
}
#[derive(Debug, PartialEq)]
pub enum Numeric {
INT(isize),
UINT(usize),
FLOAT(f64),
}
impl fmt::Display for Value<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Value::TEXT(text) => write!(f, "\"{}\"", text),
Value::INT(i) => write!(f, "{}", i),
Value::UINT(ui) => write!(f, "{}", ui),
Value::FLOAT(float) => write!(f, "{}", float),
Value::BYTE(bv) => write!(f, "{}", bv),
}
}
}
impl<'a> From<&'a str> for Value<'a> {
fn from(value: &'a str) -> Self {
Value::TEXT(value.into())
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ByteValue<'a> {
UTF8(Cow<'a, [u8]>),
B16(Cow<'a, [u8]>),
B64(Cow<'a, [u8]>),
}
impl fmt::Display for ByteValue<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ByteValue::UTF8(b) => write!(f, "'{}'", core::str::from_utf8(b).map_err(|_| fmt::Error)?),
ByteValue::B16(b) => write!(
f,
"h'{}'",
String::from_utf8(b.to_vec())
.map_err(|_| fmt::Error)?
.replace(' ', "")
),
ByteValue::B64(b) => write!(
f,
"b64'{}'",
String::from_utf8(b.to_vec())
.map_err(|_| fmt::Error)?
.replace(' ', "")
),
}
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SocketPlug {
TYPE,
GROUP,
}
impl core::str::FromStr for SocketPlug {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(c) = s.chars().next() {
if c == '$' {
if let Some(c) = s.chars().nth(1) {
if c == '$' {
return Ok(SocketPlug::GROUP);
}
}
return Ok(SocketPlug::TYPE);
}
}
Err("Malformed socket plug string")
}
}
impl fmt::Display for SocketPlug {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SocketPlug::TYPE => write!(f, "$"),
SocketPlug::GROUP => write!(f, "$$"),
}
}
}
impl fmt::Display for ControlOperator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ControlOperator::SIZE => write!(f, ".size"),
ControlOperator::BITS => write!(f, ".bits"),
ControlOperator::REGEXP => write!(f, ".regexp"),
ControlOperator::PCRE => write!(f, ".pcre"),
#[cfg(feature = "freezer")]
ControlOperator::IREGEXP => write!(f, ".iregexp"),
#[cfg(feature = "freezer")]
ControlOperator::BITFIELD => write!(f, ".bitfield"),
ControlOperator::CBOR => write!(f, ".cbor"),
ControlOperator::CBORSEQ => write!(f, ".cborseq"),
ControlOperator::WITHIN => write!(f, ".within"),
#[cfg(feature = "additional-controls")]
ControlOperator::CAT => write!(f, ".cat"),
#[cfg(feature = "additional-controls")]
ControlOperator::DET => write!(f, ".det"),
#[cfg(feature = "additional-controls")]
ControlOperator::PLUS => write!(f, ".plus"),
#[cfg(feature = "additional-controls")]
ControlOperator::ABNF => write!(f, ".abnf"),
#[cfg(feature = "additional-controls")]
ControlOperator::ABNFB => write!(f, ".abnfb"),
#[cfg(feature = "additional-controls")]
ControlOperator::FEATURE => write!(f, ".feature"),
#[cfg(feature = "additional-controls")]
ControlOperator::B64U => write!(f, ".b64u"),
#[cfg(feature = "additional-controls")]
ControlOperator::B64C => write!(f, ".b64c"),
#[cfg(feature = "additional-controls")]
ControlOperator::B64USLOPPY => write!(f, ".b64u-sloppy"),
#[cfg(feature = "additional-controls")]
ControlOperator::B64CSLOPPY => write!(f, ".b64c-sloppy"),
#[cfg(feature = "additional-controls")]
ControlOperator::HEX => write!(f, ".hex"),
#[cfg(feature = "additional-controls")]
ControlOperator::HEXLC => write!(f, ".hexlc"),
#[cfg(feature = "additional-controls")]
ControlOperator::HEXUC => write!(f, ".hexuc"),
#[cfg(feature = "additional-controls")]
ControlOperator::B32 => write!(f, ".b32"),
#[cfg(feature = "additional-controls")]
ControlOperator::H32 => write!(f, ".h32"),
#[cfg(feature = "additional-controls")]
ControlOperator::B45 => write!(f, ".b45"),
#[cfg(feature = "additional-controls")]
ControlOperator::BASE10 => write!(f, ".base10"),
#[cfg(feature = "additional-controls")]
ControlOperator::PRINTF => write!(f, ".printf"),
#[cfg(feature = "additional-controls")]
ControlOperator::JSON => write!(f, ".json"),
#[cfg(feature = "additional-controls")]
ControlOperator::JOIN => write!(f, ".join"),
ControlOperator::AND => write!(f, ".and"),
ControlOperator::LT => write!(f, ".lt"),
ControlOperator::LE => write!(f, ".le"),
ControlOperator::GT => write!(f, ".gt"),
ControlOperator::GE => write!(f, ".ge"),
ControlOperator::EQ => write!(f, ".eq"),
ControlOperator::NE => write!(f, ".ne"),
ControlOperator::DEFAULT => write!(f, ".default"),
}
}
}
impl fmt::Display for Token<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Token::IDENT(ident, socket_plug) => {
if let Some(sp) = socket_plug {
return write!(f, "{}{}", sp, ident);
}
write!(f, "{}", ident)
}
Token::ILLEGAL(s) => write!(f, "ILLEGAL({})", s),
Token::ASSIGN => write!(f, "="),
Token::ONEORMORE => write!(f, "+"),
Token::OPTIONAL => write!(f, "?"),
Token::ASTERISK => write!(f, "*"),
Token::LPAREN => write!(f, "("),
Token::RPAREN => write!(f, ")"),
Token::LBRACE => write!(f, "{{"),
Token::RBRACE => write!(f, "}}"),
Token::LBRACKET => write!(f, "["),
Token::RBRACKET => write!(f, "]"),
Token::TCHOICE => write!(f, "/"),
Token::TCHOICEALT => write!(f, "/="),
Token::GCHOICEALT => write!(f, "//="),
Token::COMMA => write!(f, ","),
Token::COMMENT(c) => write!(f, ";{}", c),
Token::COLON => write!(f, ":"),
Token::CUT => write!(f, "^"),
Token::EOF => write!(f, ""),
Token::TSTR => write!(f, "tstr"),
Token::LANGLEBRACKET => write!(f, "<"),
Token::RANGLEBRACKET => write!(f, ">"),
Token::INT => write!(f, "int"),
Token::UINT => write!(f, "uint"),
Token::ARROWMAP => write!(f, "=>"),
Token::ControlOperator(co) => write!(f, "{}", co),
Token::NUMBER => write!(f, "number"),
Token::BSTR => write!(f, "bstr"),
Token::BYTES => write!(f, "bytes"),
Token::GCHOICE => write!(f, "//"),
Token::TRUE => write!(f, "true"),
Token::GTOCHOICE => write!(f, "&"),
Token::VALUE(value) => write!(f, "{}", value),
Token::REGEXP => write!(f, "regexp"),
Token::RANGEOP(i) => {
if *i {
write!(f, "..")
} else {
write!(f, "...")
}
}
Token::RANGE(l, u, i) => match l {
RangeValue::IDENT(..) if *i => write!(f, "{} .. {}", l, u),
RangeValue::IDENT(..) => write!(f, "{} ... {}", l, u),
_ => {
if *i {
write!(f, "{}..{}", l, u)
} else {
write!(f, "{}...{}", l, u)
}
}
},
Token::TAG(mt, tag) => {
if let Some(m) = mt {
if let Some(t) = tag {
return write!(f, "#{}.{}", m, t);
}
return write!(f, "#{}", m);
}
write!(f, "#")
}
_ => write!(f, ""),
}
}
}
pub fn lookup_control_from_str(ident: &str) -> Option<ControlOperator> {
match ident {
".size" => Some(ControlOperator::SIZE),
".bits" => Some(ControlOperator::BITS),
".regexp" => Some(ControlOperator::REGEXP),
".cbor" => Some(ControlOperator::CBOR),
".cborseq" => Some(ControlOperator::CBORSEQ),
".within" => Some(ControlOperator::WITHIN),
".and" => Some(ControlOperator::AND),
".lt" => Some(ControlOperator::LT),
".le" => Some(ControlOperator::LE),
".gt" => Some(ControlOperator::GT),
".ge" => Some(ControlOperator::GE),
".eq" => Some(ControlOperator::EQ),
".ne" => Some(ControlOperator::NE),
".default" => Some(ControlOperator::DEFAULT),
".pcre" => Some(ControlOperator::PCRE),
#[cfg(feature = "freezer")]
".iregexp" => Some(ControlOperator::IREGEXP),
#[cfg(feature = "freezer")]
".bitfield" => Some(ControlOperator::BITFIELD),
#[cfg(feature = "additional-controls")]
".cat" => Some(ControlOperator::CAT),
#[cfg(feature = "additional-controls")]
".det" => Some(ControlOperator::DET),
#[cfg(feature = "additional-controls")]
".plus" => Some(ControlOperator::PLUS),
#[cfg(feature = "additional-controls")]
".abnf" => Some(ControlOperator::ABNF),
#[cfg(feature = "additional-controls")]
".abnfb" => Some(ControlOperator::ABNFB),
#[cfg(feature = "additional-controls")]
".feature" => Some(ControlOperator::FEATURE),
#[cfg(feature = "additional-controls")]
".b64u" => Some(ControlOperator::B64U),
#[cfg(feature = "additional-controls")]
".b64c" => Some(ControlOperator::B64C),
#[cfg(feature = "additional-controls")]
".b64u-sloppy" => Some(ControlOperator::B64USLOPPY),
#[cfg(feature = "additional-controls")]
".b64c-sloppy" => Some(ControlOperator::B64CSLOPPY),
#[cfg(feature = "additional-controls")]
".hex" => Some(ControlOperator::HEX),
#[cfg(feature = "additional-controls")]
".hexlc" => Some(ControlOperator::HEXLC),
#[cfg(feature = "additional-controls")]
".hexuc" => Some(ControlOperator::HEXUC),
#[cfg(feature = "additional-controls")]
".b32" => Some(ControlOperator::B32),
#[cfg(feature = "additional-controls")]
".h32" => Some(ControlOperator::H32),
#[cfg(feature = "additional-controls")]
".b45" => Some(ControlOperator::B45),
#[cfg(feature = "additional-controls")]
".base10" => Some(ControlOperator::BASE10),
#[cfg(feature = "additional-controls")]
".printf" => Some(ControlOperator::PRINTF),
#[cfg(feature = "additional-controls")]
".json" => Some(ControlOperator::JSON),
#[cfg(feature = "additional-controls")]
".join" => Some(ControlOperator::JOIN),
_ => None,
}
}
pub fn lookup_ident(ident: &str) -> Token<'_> {
match ident {
"false" => Token::FALSE,
"true" => Token::TRUE,
"bool" => Token::BOOL,
"nil" => Token::NIL,
"null" => Token::NULL,
"uint" => Token::UINT,
"nint" => Token::NINT,
"int" => Token::INT,
"float16" => Token::FLOAT16,
"float32" => Token::FLOAT32,
"float64" => Token::FLOAT64,
"float16-32" => Token::FLOAT1632,
"float32-64" => Token::FLOAT3264,
"float" => Token::FLOAT,
"bstr" => Token::BSTR,
"tstr" => Token::TSTR,
"any" => Token::ANY,
"bytes" => Token::BYTES,
"text" => Token::TEXT,
"tdate" => Token::TDATE,
"time" => Token::TIME,
"number" => Token::NUMBER,
"biguint" => Token::BIGUINT,
"bignint" => Token::BIGNINT,
"bigint" => Token::BIGINT,
"integer" => Token::INTEGER,
"unsigned" => Token::UNSIGNED,
"decfrac" => Token::DECFRAC,
"bigfloat" => Token::BIGFLOAT,
"eb64url" => Token::EB64URL,
"eb64legacy" => Token::EB64LEGACY,
"eb16" => Token::EB16,
"encoded-cbor" => Token::ENCODEDCBOR,
"uri" => Token::URI,
"b64url" => Token::B64URL,
"b64legacy" => Token::B64LEGACY,
"regexp" => Token::REGEXP,
"mime-message" => Token::MIMEMESSAGE,
"cbor-any" => Token::CBORANY,
"undefined" => Token::UNDEFINED,
_ => {
if let Some(c) = ident.chars().next() {
if c == '$' {
if let Some(c) = ident.chars().nth(1) {
if c == '$' {
return Token::IDENT(&ident[2..], Some(SocketPlug::GROUP));
}
}
return Token::IDENT(&ident[1..], Some(SocketPlug::TYPE));
}
}
Token::IDENT(ident, None)
}
}
}
pub fn closing_delimiter<'a>(token: &Token) -> Option<Token<'a>> {
match token {
Token::LBRACE => Some(Token::RBRACE),
Token::LBRACKET => Some(Token::RBRACKET),
Token::LPAREN => Some(Token::RPAREN),
Token::LANGLEBRACKET => Some(Token::RANGLEBRACKET),
_ => None,
}
}