use anyhow::{anyhow, bail, Result};
use move_core_types::account_address::AccountAddress;
use num_bigint::BigUint;
use std::{collections::BTreeMap, fmt::Display, iter::Peekable, num::ParseIntError};
use crate::{
address::{NumericalAddress, ParsedAddress},
types::{ParsedStructType, ParsedType, TypeToken},
values::{ParsableValue, ParsedValue, ValueToken},
};
pub trait Token: Display + Copy + Eq {
fn is_whitespace(&self) -> bool;
fn next_token(s: &str) -> Result<Option<(Self, usize)>>;
fn tokenize(mut s: &str) -> Result<Vec<(Self, &str)>> {
let mut v = vec![];
while let Some((tok, n)) = Self::next_token(s)? {
v.push((tok, &s[..n]));
s = &s[n..];
}
Ok(v)
}
}
pub struct Parser<'a, Tok: Token, I: Iterator<Item = (Tok, &'a str)>> {
it: Peekable<I>,
}
impl ParsedType {
pub fn parse(s: &str) -> Result<ParsedType> {
parse(s, |parser| parser.parse_type())
}
}
impl ParsedStructType {
pub fn parse(s: &str) -> Result<ParsedStructType> {
let ty = parse(s, |parser| parser.parse_type())
.map_err(|e| anyhow!("Invalid struct type: {}. Got error: {}", s, e))?;
match ty {
ParsedType::Struct(s) => Ok(s),
_ => bail!("Invalid struct type: {}", s),
}
}
}
impl ParsedAddress {
pub fn parse(s: &str) -> Result<ParsedAddress> {
parse(s, |parser| parser.parse_address())
}
}
impl<Extra: ParsableValue> ParsedValue<Extra> {
pub fn parse(s: &str) -> Result<ParsedValue<Extra>> {
parse(s, |parser| parser.parse_value())
}
}
fn parse<'a, Tok: Token, R>(
s: &'a str,
f: impl FnOnce(&mut Parser<'a, Tok, std::vec::IntoIter<(Tok, &'a str)>>) -> Result<R>,
) -> Result<R> {
let tokens: Vec<_> = Tok::tokenize(s)?
.into_iter()
.filter(|(tok, _)| !tok.is_whitespace())
.collect();
let mut parser = Parser::new(tokens);
let res = f(&mut parser)?;
if let Ok((_, contents)) = parser.advance_any() {
bail!("Expected end of token stream. Got: {}", contents)
}
Ok(res)
}
impl<'a, Tok: Token, I: Iterator<Item = (Tok, &'a str)>> Parser<'a, Tok, I> {
pub fn new<T: IntoIterator<Item = (Tok, &'a str), IntoIter = I>>(v: T) -> Self {
Self {
it: v.into_iter().peekable(),
}
}
pub fn advance_any(&mut self) -> Result<(Tok, &'a str)> {
match self.it.next() {
Some(tok) => Ok(tok),
None => bail!("unexpected end of tokens"),
}
}
pub fn advance(&mut self, expected_token: Tok) -> Result<&'a str> {
let (t, contents) = self.advance_any()?;
if t != expected_token {
bail!("expected token {}, got {}", expected_token, t)
}
Ok(contents)
}
pub fn peek(&mut self) -> Option<(Tok, &'a str)> {
self.it.peek().copied()
}
pub fn peek_tok(&mut self) -> Option<Tok> {
self.it.peek().map(|(tok, _)| *tok)
}
pub fn parse_list<R>(
&mut self,
parse_list_item: impl Fn(&mut Self) -> Result<R>,
delim: Tok,
end_token: Tok,
allow_trailing_delim: bool,
) -> Result<Vec<R>> {
let is_end =
|tok_opt: Option<Tok>| -> bool { tok_opt.map(|tok| tok == end_token).unwrap_or(true) };
let mut v = vec![];
while !is_end(self.peek_tok()) {
v.push(parse_list_item(self)?);
if is_end(self.peek_tok()) {
break;
}
self.advance(delim)?;
if is_end(self.peek_tok()) && allow_trailing_delim {
break;
}
}
Ok(v)
}
}
impl<'a, I: Iterator<Item = (TypeToken, &'a str)>> Parser<'a, TypeToken, I> {
pub fn parse_type(&mut self) -> Result<ParsedType> {
let (tok, contents) = self.advance_any()?;
Ok(match (tok, contents) {
(TypeToken::Ident, "u8") => ParsedType::U8,
(TypeToken::Ident, "u64") => ParsedType::U64,
(TypeToken::Ident, "u128") => ParsedType::U128,
(TypeToken::Ident, "bool") => ParsedType::Bool,
(TypeToken::Ident, "address") => ParsedType::Address,
(TypeToken::Ident, "signer") => ParsedType::Signer,
(TypeToken::Ident, "vector") => {
self.advance(TypeToken::Lt)?;
let ty = self.parse_type()?;
self.advance(TypeToken::Gt)?;
ParsedType::Vector(Box::new(ty))
}
(TypeToken::Ident, _) | (TypeToken::AddressIdent, _) => {
let addr_tok = match tok {
TypeToken::Ident => ValueToken::Ident,
TypeToken::AddressIdent => ValueToken::Number,
_ => unreachable!(),
};
let address = parse_address_impl(addr_tok, contents)?;
self.advance(TypeToken::ColonColon)?;
let module_contents = self.advance(TypeToken::Ident)?;
self.advance(TypeToken::ColonColon)?;
let struct_contents = self.advance(TypeToken::Ident)?;
let type_args = match self.peek_tok() {
Some(TypeToken::Lt) => {
self.advance(TypeToken::Lt)?;
let type_args = self.parse_list(
|parser| parser.parse_type(),
TypeToken::Comma,
TypeToken::Gt,
true,
)?;
self.advance(TypeToken::Gt)?;
type_args
}
_ => vec![],
};
ParsedType::Struct(ParsedStructType {
address,
module: module_contents.to_owned(),
name: struct_contents.to_owned(),
type_args,
})
}
_ => bail!("unexpected token {}, expected type", tok),
})
}
}
impl<'a, I: Iterator<Item = (ValueToken, &'a str)>> Parser<'a, ValueToken, I> {
pub fn parse_value<Extra: ParsableValue>(&mut self) -> Result<ParsedValue<Extra>> {
if let Some(extra) = Extra::parse_value(self) {
return Ok(ParsedValue::Custom(extra?));
}
let (tok, contents) = self.advance_any()?;
Ok(match tok {
ValueToken::Number if !matches!(self.peek_tok(), Some(ValueToken::ColonColon)) => {
let (u, _) = parse_u128(contents)?;
ParsedValue::InferredNum(u)
}
ValueToken::NumberTyped => {
if let Some(s) = contents.strip_suffix("u8") {
let (u, _) = parse_u8(s)?;
ParsedValue::U8(u)
} else if let Some(s) = contents.strip_suffix("u64") {
let (u, _) = parse_u64(s)?;
ParsedValue::U64(u)
} else {
let (u, _) = parse_u128(contents.strip_suffix("u128").unwrap())?;
ParsedValue::U128(u)
}
}
ValueToken::True => ParsedValue::Bool(true),
ValueToken::False => ParsedValue::Bool(false),
ValueToken::ByteString => {
let contents = contents
.strip_prefix("b\"")
.unwrap()
.strip_suffix("\"")
.unwrap();
ParsedValue::Vector(
contents
.as_bytes()
.iter()
.copied()
.map(ParsedValue::U8)
.collect(),
)
}
ValueToken::HexString => {
let contents = contents
.strip_prefix("x\"")
.unwrap()
.strip_suffix("\"")
.unwrap()
.to_ascii_lowercase();
ParsedValue::Vector(
hex::decode(contents)
.unwrap()
.into_iter()
.map(ParsedValue::U8)
.collect(),
)
}
ValueToken::AtSign => ParsedValue::Address(self.parse_address()?),
ValueToken::Ident if contents == "vector" => {
self.advance(ValueToken::LBracket)?;
let values = self.parse_list(
|parser| parser.parse_value(),
ValueToken::Comma,
ValueToken::RBracket,
true,
)?;
self.advance(ValueToken::RBracket)?;
ParsedValue::Vector(values)
}
ValueToken::Number | ValueToken::Ident => {
let addr_ident = parse_address_impl(tok, contents)?;
self.advance(ValueToken::ColonColon)?;
let module_name = self.advance(ValueToken::Ident)?.to_owned();
self.advance(ValueToken::ColonColon)?;
let struct_name = self.advance(ValueToken::Ident)?.to_owned();
self.advance(ValueToken::LBrace)?;
let values_vec = self.parse_list(
|parser| {
let field = parser.advance(ValueToken::Ident)?.to_owned();
parser.advance(ValueToken::Colon)?;
let value = parser.parse_value()?;
Ok((field, value))
},
ValueToken::Comma,
ValueToken::RBracket,
true,
)?;
self.advance(ValueToken::RBrace)?;
let mut values = BTreeMap::new();
for (field, value) in values_vec {
if let Some(_prev) = values.insert(field.clone(), value) {
bail!("Duplicate field binding for field: {}", field)
}
}
ParsedValue::Struct(addr_ident, module_name, struct_name, values)
}
_ => bail!("unexpected token {}, expected type", tok),
})
}
pub fn parse_address(&mut self) -> Result<ParsedAddress> {
let (tok, contents) = self.advance_any()?;
parse_address_impl(tok, contents)
}
}
pub fn parse_address_impl(tok: ValueToken, contents: &str) -> Result<ParsedAddress> {
Ok(match tok {
ValueToken::Number => {
ParsedAddress::Numerical(NumericalAddress::parse_str(contents).map_err(|s| {
anyhow!(
"Failed to parse numerical address '{}'. Got error: {}",
contents,
s
)
})?)
}
ValueToken::Ident => ParsedAddress::Named(contents.to_owned()),
_ => bail!("unexpected token {}, expected identifier or number", tok),
})
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
#[repr(u32)]
pub enum NumberFormat {
Decimal = 10,
Hex = 16,
}
pub(crate) fn determine_num_text_and_base(s: &str) -> (&str, NumberFormat) {
match s.strip_prefix("0x") {
Some(s_hex) => (s_hex, NumberFormat::Hex),
None => (s, NumberFormat::Decimal),
}
}
pub fn parse_u8(s: &str) -> Result<(u8, NumberFormat), ParseIntError> {
let (txt, base) = determine_num_text_and_base(s);
Ok((u8::from_str_radix(txt, base as u32)?, base))
}
pub fn parse_u64(s: &str) -> Result<(u64, NumberFormat), ParseIntError> {
let (txt, base) = determine_num_text_and_base(s);
Ok((u64::from_str_radix(txt, base as u32)?, base))
}
pub fn parse_u128(s: &str) -> Result<(u128, NumberFormat), ParseIntError> {
let (txt, base) = determine_num_text_and_base(s);
Ok((u128::from_str_radix(txt, base as u32)?, base))
}
pub fn parse_address_number(s: &str) -> Option<([u8; AccountAddress::LENGTH], NumberFormat)> {
let (txt, base) = determine_num_text_and_base(s);
let parsed = BigUint::parse_bytes(
txt.as_bytes(),
match base {
NumberFormat::Hex => 16,
NumberFormat::Decimal => 10,
},
)?;
let bytes = parsed.to_bytes_be();
if bytes.len() > AccountAddress::LENGTH {
return None;
}
let mut result = [0u8; AccountAddress::LENGTH];
result[(AccountAddress::LENGTH - bytes.len())..].clone_from_slice(&bytes);
Some((result, base))
}
#[cfg(test)]
mod tests {
use crate::{
address::{NumericalAddress, ParsedAddress},
types::{ParsedStructType, ParsedType},
values::ParsedValue,
};
use move_core_types::account_address::AccountAddress;
#[allow(clippy::unreadable_literal)]
#[test]
fn tests_parse_value_positive() {
use ParsedValue as V;
let cases: &[(&str, V)] = &[
(" 0u8", V::U8(0)),
("0u8", V::U8(0)),
("255u8", V::U8(255)),
("0", V::InferredNum(0)),
("0123", V::InferredNum(123)),
("0xFF", V::InferredNum(0xFF)),
("0u64", V::U64(0)),
("0x0u64", V::U64(0)),
("18446744073709551615", V::InferredNum(18446744073709551615)),
("18446744073709551615u64", V::U64(18446744073709551615)),
("0u128", V::U128(0)),
(
"340282366920938463463374607431768211455u128",
V::U128(340282366920938463463374607431768211455),
),
("true", V::Bool(true)),
("false", V::Bool(false)),
(
"@0x0",
V::Address(ParsedAddress::Numerical(NumericalAddress::new(
AccountAddress::from_hex_literal("0x0")
.unwrap()
.into_bytes(),
crate::parser::NumberFormat::Hex,
))),
),
(
"@0",
V::Address(ParsedAddress::Numerical(NumericalAddress::new(
AccountAddress::from_hex_literal("0x0")
.unwrap()
.into_bytes(),
crate::parser::NumberFormat::Hex,
))),
),
(
"@0x54afa3526",
V::Address(ParsedAddress::Numerical(NumericalAddress::new(
AccountAddress::from_hex_literal("0x54afa3526")
.unwrap()
.into_bytes(),
crate::parser::NumberFormat::Hex,
))),
),
(
"b\"hello\"",
V::Vector("hello".as_bytes().iter().copied().map(V::U8).collect()),
),
("x\"7fff\"", V::Vector(vec![V::U8(0x7f), V::U8(0xff)])),
("x\"\"", V::Vector(vec![])),
("x\"00\"", V::Vector(vec![V::U8(0x00)])),
(
"x\"deadbeef\"",
V::Vector(vec![V::U8(0xde), V::U8(0xad), V::U8(0xbe), V::U8(0xef)]),
),
];
for (s, expected) in cases {
assert_eq!(&ParsedValue::parse(s).unwrap(), expected)
}
}
#[test]
fn tests_parse_value_negative() {
for s in &[
"-3",
"0u42",
"0u645",
"0u64x",
"0u6 4",
"0u",
"256u8",
"18446744073709551616u64",
"340282366920938463463374607431768211456u128",
"0xg",
"0x00g0",
"0x",
"0x_",
"0XFF",
"0X0",
"",
"@@",
"()",
"x\"ffff",
"x\"a \"",
"x\" \"",
"x\"0g\"",
"x\"0\"",
"garbage",
"true3",
"3false",
"3 false",
"",
] {
assert!(
ParsedValue::<()>::parse(s).is_err(),
"Unexpectedly succeeded in parsing: {}",
s
)
}
}
#[test]
fn test_type_type() {
for s in &[
"u64",
"bool",
"vector<u8>",
"vector<vector<u64>>",
"address",
"signer",
"0x1::M::S",
"0x2::M::S_",
"0x3::M_::S",
"0x4::M_::S_",
"0x00000000004::M::S",
"0x1::M::S<u64>",
"0x1::M::S<0x2::P::Q>",
"vector<0x1::M::S>",
"vector<0x1::M_::S_>",
"vector<vector<0x1::M_::S_>>",
"0x1::M::S<vector<u8>>",
] {
assert!(ParsedType::parse(s).is_ok(), "Failed to parse type {}", s);
}
}
#[test]
fn test_parse_valid_struct_type() {
let valid = vec![
"0x1::Foo::Foo",
"0x1::Foo_Type::Foo",
"0x1::Foo_::Foo",
"0x1::X_123::X32_",
"0x1::Foo::Foo_Type",
"0x1::Foo::Foo<0x1::ABC::ABC>",
"0x1::Foo::Foo<0x1::ABC::ABC_Type>",
"0x1::Foo::Foo<u8>",
"0x1::Foo::Foo<u64>",
"0x1::Foo::Foo<u128>",
"0x1::Foo::Foo<bool>",
"0x1::Foo::Foo<address>",
"0x1::Foo::Foo<signer>",
"0x1::Foo::Foo<vector<0x1::ABC::ABC>>",
"0x1::Foo::Foo<u8,bool>",
"0x1::Foo::Foo<u8, bool>",
"0x1::Foo::Foo<u8 ,bool>",
"0x1::Foo::Foo<u8 , bool , vector<u8>,address,signer>",
"0x1::Foo::Foo<vector<0x1::Foo::Struct<0x1::XYZ::XYZ>>>",
"0x1::Foo::Foo<0x1::Foo::Struct<vector<0x1::XYZ::XYZ>, 0x1::Foo::Foo<vector<0x1::Foo::Struct<0x1::XYZ::XYZ>>>>>",
];
for s in valid {
assert!(
ParsedStructType::parse(s).is_ok(),
"Failed to parse struct {}",
s
);
}
}
}