use std::{convert::TryFrom, fmt};
#[cfg(feature = "std")]
use std::borrow::Cow;
#[cfg(target_arch = "wasm32")]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "std"))]
use alloc::{borrow::Cow, string::String};
#[derive(PartialEq, Debug, Clone)]
pub enum Token<'a> {
ILLEGAL(&'a str),
EOF,
IDENT((&'a str, Option<SocketPlug>)),
VALUE(Value<'a>),
TAG((Option<u8>, Option<usize>)),
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,
SIZE,
BITS,
CREGEXP,
CBOR,
CBORSEQ,
WITHIN,
AND,
LT,
LE,
GT,
GE,
EQ,
NE,
DEFAULT,
PCRE,
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,
}
impl<'a> Token<'a> {
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) => Ok(RangeValue::IDENT(ident)),
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<'a> RangeValue<'a> {
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<'a> fmt::Display for RangeValue<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RangeValue::IDENT(ident) => write!(f, "{}", ident.0),
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(&'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<'a> fmt::Display for Value<'a> {
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<&'static str> for Value<'a> {
fn from(value: &'static str) -> Self {
Value::TEXT(value)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Clone)]
pub enum ByteValue<'a> {
UTF8(Cow<'a, [u8]>),
B16(Cow<'a, [u8]>),
B64(Cow<'a, [u8]>),
}
impl<'a> fmt::Display for ByteValue<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ByteValue::UTF8(b) => write!(f, "'{}'", std::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, Clone, Copy)]
pub enum SocketPlug {
TYPE,
GROUP,
}
impl std::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<'a> fmt::Display for SocketPlug {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SocketPlug::TYPE => write!(f, "$"),
SocketPlug::GROUP => write!(f, "$$"),
}
}
}
impl<'a> fmt::Display for Token<'a> {
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::SIZE => write!(f, ".size"),
Token::BITS => write!(f, ".bits"),
Token::REGEXP => write!(f, ".regexp"),
Token::PCRE => write!(f, ".pcre"),
Token::CBOR => write!(f, ".cbor"),
Token::CBORSEQ => write!(f, ".cborseq"),
Token::WITHIN => write!(f, ".within"),
Token::AND => write!(f, ".and"),
Token::LT => write!(f, ".lt"),
Token::LE => write!(f, ".le"),
Token::GT => write!(f, ".gt"),
Token::GE => write!(f, ".ge"),
Token::EQ => write!(f, ".eq"),
Token::NE => write!(f, ".ne"),
Token::DEFAULT => write!(f, ".default"),
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::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<'a>(ident: &str) -> Option<Token<'a>> {
match ident {
".size" => Some(Token::SIZE),
".bits" => Some(Token::BITS),
".regexp" => Some(Token::REGEXP),
".cbor" => Some(Token::CBOR),
".cborseq" => Some(Token::CBORSEQ),
".within" => Some(Token::WITHIN),
".and" => Some(Token::AND),
".lt" => Some(Token::LT),
".le" => Some(Token::LE),
".gt" => Some(Token::GT),
".ge" => Some(Token::GE),
".eq" => Some(Token::EQ),
".ne" => Some(Token::NE),
".default" => Some(Token::DEFAULT),
".pcre" => Some(Token::PCRE),
_ => None,
}
}
pub fn control_str_from_token(t: &Token) -> Option<&'static str> {
match t {
Token::SIZE => Some(".size"),
Token::BITS => Some(".bits"),
Token::REGEXP => Some(".regexp"),
Token::CBOR => Some(".cbor"),
Token::CBORSEQ => Some(".cborseq"),
Token::WITHIN => Some(".within"),
Token::AND => Some(".and"),
Token::LT => Some(".lt"),
Token::LE => Some(".le"),
Token::GT => Some(".gt"),
Token::GE => Some(".ge"),
Token::EQ => Some(".eq"),
Token::NE => Some(".ne"),
Token::DEFAULT => Some(".default"),
Token::PCRE => Some(".pcre"),
_ => 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,
"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,
}
}