use codespan::{ByteIndex, Span};
use lalrpop_util::ParseError;
use lazy_static::lazy_static;
use solana_libra_types::{
account_address::AccountAddress,
byte_array::ByteArray,
identifier::{IdentStr, Identifier},
language_storage::ModuleId,
};
use std::{
collections::{HashSet, VecDeque},
fmt,
ops::Deref,
};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct Spanned<T> {
pub span: Loc,
pub value: T,
}
pub type Loc = Span<ByteIndex>;
#[derive(Debug, Clone)]
pub struct Program {
pub modules: Vec<ModuleDefinition>,
pub script: Script,
}
#[derive(Debug, Clone)]
pub enum ScriptOrModule {
Script(Script),
Module(ModuleDefinition),
}
#[derive(Debug, Clone)]
pub struct Script {
pub imports: Vec<ImportDefinition>,
pub main: Function,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ModuleName(Identifier);
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct QualifiedModuleIdent {
pub name: ModuleName,
pub address: AccountAddress,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ModuleDefinition {
pub name: ModuleName,
pub imports: Vec<ImportDefinition>,
pub structs: Vec<StructDefinition>,
pub functions: Vec<(FunctionName, Function)>,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum ModuleIdent {
Transaction(ModuleName),
Qualified(QualifiedModuleIdent),
}
#[derive(Clone, Debug, PartialEq)]
pub struct ImportDefinition {
pub ident: ModuleIdent,
pub alias: ModuleName,
}
#[derive(Debug, PartialEq, Hash, Eq, Clone, Ord, PartialOrd)]
pub struct Var(Identifier);
pub type Var_ = Spanned<Var>;
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct TypeVar(Identifier);
pub type TypeVar_ = Spanned<TypeVar>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Kind {
All,
Resource,
Unrestricted,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Type {
Address,
U64,
Bool,
ByteArray,
String,
Struct(QualifiedStructIdent, Vec<Type>),
Reference(bool, Box<Type>),
TypeParameter(TypeVar),
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct QualifiedStructIdent {
pub module: ModuleName,
pub name: StructName,
}
pub type Field = solana_libra_types::access_path::Field;
pub type Field_ = Spanned<Field>;
pub type Fields<T> = Vec<(Field_, T)>;
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct StructName(Identifier);
#[derive(Clone, Debug, PartialEq)]
pub struct StructDefinition {
pub is_nominal_resource: bool,
pub name: StructName,
pub type_formals: Vec<(TypeVar_, Kind)>,
pub fields: StructDefinitionFields,
}
#[derive(Clone, Debug, PartialEq)]
pub enum StructDefinitionFields {
Move { fields: Fields<Type> },
Native,
}
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Clone)]
pub struct FunctionName(Identifier);
#[derive(PartialEq, Debug, Clone)]
pub struct FunctionSignature {
pub formals: Vec<(Var_, Type)>,
pub return_type: Vec<Type>,
pub type_formals: Vec<(TypeVar_, Kind)>,
}
#[derive(PartialEq, Debug, Clone)]
pub enum FunctionVisibility {
Public,
Internal,
}
#[derive(PartialEq, Debug, Clone)]
pub enum FunctionBody {
Move {
locals: Vec<(Var_, Type)>,
code: Block,
},
Native,
}
#[derive(PartialEq, Debug, Clone)]
pub struct Function {
pub visibility: FunctionVisibility,
pub signature: FunctionSignature,
pub acquires: Vec<StructName>,
pub body: FunctionBody,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Builtin {
Exists(StructName, Vec<Type>),
BorrowGlobal(bool, StructName, Vec<Type>),
GetTxnGasUnitPrice,
GetTxnMaxGasUnits,
GetTxnPublicKey,
GetTxnSender,
GetTxnSequenceNumber,
GetGasRemaining,
CreateAccount,
MoveFrom(StructName, Vec<Type>),
MoveToSender(StructName, Vec<Type>),
Freeze,
}
#[derive(Debug, PartialEq, Clone)]
pub enum FunctionCall {
Builtin(Builtin),
ModuleFunctionCall {
module: ModuleName,
name: FunctionName,
type_actuals: Vec<Type>,
},
}
pub type FunctionCall_ = Spanned<FunctionCall>;
#[derive(Debug, Clone, PartialEq)]
pub enum LValue {
Var(Var_),
Mutate(Exp_),
Pop,
}
pub type LValue_ = Spanned<LValue>;
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq)]
pub enum Cmd {
Assign(Vec<LValue_>, Exp_),
Unpack(StructName, Vec<Type>, Fields<Var_>, Box<Exp_>),
Abort(Option<Box<Exp_>>),
Return(Box<Exp_>),
Break,
Continue,
Exp(Box<Exp_>),
}
pub type Cmd_ = Spanned<Cmd>;
#[derive(Debug, PartialEq, Clone)]
pub struct IfElse {
pub cond: Exp_,
pub if_block: Block,
pub else_block: Option<Block>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct While {
pub cond: Exp_,
pub block: Block,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Loop {
pub block: Block,
}
#[derive(Debug, PartialEq, Clone)]
#[allow(clippy::large_enum_variant)]
pub enum Statement {
CommandStatement(Cmd_),
IfElseStatement(IfElse),
WhileStatement(While),
LoopStatement(Loop),
EmptyStatement,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Block {
pub stmts: VecDeque<Statement>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum CopyableVal {
Address(AccountAddress),
U64(u64),
Bool(bool),
ByteArray(ByteArray),
String(String),
}
pub type CopyableVal_ = Spanned<CopyableVal>;
pub type ExpFields = Fields<Exp_>;
#[derive(Debug, Clone, PartialEq)]
pub enum UnaryOp {
Not,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BinOp {
Add,
Sub,
Mul,
Mod,
Div,
BitOr,
BitAnd,
Xor,
And,
Or,
Eq,
Neq,
Lt,
Gt,
Le,
Ge,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Exp {
Dereference(Box<Exp_>),
UnaryExp(UnaryOp, Box<Exp_>),
BinopExp(Box<Exp_>, BinOp, Box<Exp_>),
Value(CopyableVal_),
Pack(StructName, Vec<Type>, ExpFields),
Borrow {
is_mutable: bool,
exp: Box<Exp_>,
field: Field,
},
Move(Var_),
Copy(Var_),
BorrowLocal(bool, Var_),
FunctionCall(FunctionCall, Box<Exp_>),
ExprList(Vec<Exp_>),
}
pub type Exp_ = Spanned<Exp>;
fn get_external_deps(imports: &[ImportDefinition]) -> Vec<ModuleId> {
let mut deps = HashSet::new();
for dep in imports.iter() {
if let ModuleIdent::Qualified(id) = &dep.ident {
deps.insert(ModuleId::new(id.address, id.name.clone().into_inner()));
}
}
deps.into_iter().collect()
}
impl Program {
pub fn new(modules: Vec<ModuleDefinition>, script: Script) -> Self {
Program { modules, script }
}
}
impl Script {
pub fn new(imports: Vec<ImportDefinition>, main: Function) -> Self {
Script { imports, main }
}
pub fn body(&self) -> &Block {
match self.main.body {
FunctionBody::Move { ref code, .. } => &code,
FunctionBody::Native => panic!("main() can't be native"),
}
}
pub fn get_external_deps(&self) -> Vec<ModuleId> {
get_external_deps(self.imports.as_slice())
}
}
lazy_static! {
static ref SELF_MODULE_NAME: Identifier = Identifier::new("Self").unwrap();
}
impl ModuleName {
pub fn new(name: Identifier) -> Self {
assert!(!name.is_empty());
ModuleName(name)
}
pub fn parse<L, T>(s: impl Into<Box<str>>) -> Result<Self, ParseError<L, T, failure::Error>> {
Ok(ModuleName::new(parse_identifier(s.into())?))
}
pub fn self_name() -> &'static IdentStr {
&*SELF_MODULE_NAME
}
pub fn module_self() -> Self {
ModuleName::new(ModuleName::self_name().into())
}
pub fn into_inner(self) -> Identifier {
self.0
}
pub fn as_inner(&self) -> &IdentStr {
&self.0
}
}
impl QualifiedModuleIdent {
pub fn new(name: ModuleName, address: AccountAddress) -> Self {
QualifiedModuleIdent { address, name }
}
pub fn name(&self) -> &ModuleName {
&self.name
}
pub fn address(&self) -> &AccountAddress {
&self.address
}
}
impl ModuleIdent {
pub fn name(&self) -> &ModuleName {
match self {
ModuleIdent::Transaction(name) => &name,
ModuleIdent::Qualified(id) => &id.name,
}
}
}
impl ModuleDefinition {
pub fn new<L, T>(
name: impl Into<Box<str>>,
imports: Vec<ImportDefinition>,
structs: Vec<StructDefinition>,
functions: Vec<(FunctionName, Function)>,
) -> Result<Self, ParseError<L, T, failure::Error>> {
Ok(ModuleDefinition {
name: ModuleName::parse(name.into())?,
imports,
structs,
functions,
})
}
pub fn get_external_deps(&self) -> Vec<ModuleId> {
get_external_deps(self.imports.as_slice())
}
}
impl Type {
pub fn r#struct(ident: QualifiedStructIdent, type_actuals: Vec<Type>) -> Type {
Type::Struct(ident, type_actuals)
}
pub fn reference(is_mutable: bool, t: Type) -> Type {
Type::Reference(is_mutable, Box::new(t))
}
pub fn address() -> Type {
Type::Address
}
pub fn u64() -> Type {
Type::U64
}
pub fn bool() -> Type {
Type::Bool
}
pub fn bytearray() -> Type {
Type::ByteArray
}
}
impl QualifiedStructIdent {
pub fn new(module: ModuleName, name: StructName) -> Self {
QualifiedStructIdent { module, name }
}
pub fn module(&self) -> &ModuleName {
&self.module
}
pub fn name(&self) -> &StructName {
&self.name
}
}
impl ImportDefinition {
pub fn new(ident: ModuleIdent, alias_opt: Option<ModuleName>) -> Self {
let alias = match alias_opt {
Some(alias) => alias,
None => ident.name().clone(),
};
ImportDefinition { ident, alias }
}
}
impl StructName {
pub fn new(name: Identifier) -> Self {
StructName(name)
}
pub fn parse<L, T>(s: impl Into<Box<str>>) -> Result<Self, ParseError<L, T, failure::Error>> {
Ok(StructName::new(parse_identifier(s.into())?))
}
pub fn into_inner(self) -> Identifier {
self.0
}
pub fn as_inner(&self) -> &IdentStr {
&self.0
}
}
impl StructDefinition {
pub fn move_declared<L, T>(
is_nominal_resource: bool,
name: impl Into<Box<str>>,
type_formals: Vec<(TypeVar_, Kind)>,
fields: Fields<Type>,
) -> Result<Self, ParseError<L, T, failure::Error>> {
Ok(StructDefinition {
is_nominal_resource,
name: StructName::parse(name)?,
type_formals,
fields: StructDefinitionFields::Move { fields },
})
}
pub fn native<L, T>(
is_nominal_resource: bool,
name: impl Into<Box<str>>,
type_formals: Vec<(TypeVar_, Kind)>,
) -> Result<Self, ParseError<L, T, failure::Error>> {
Ok(StructDefinition {
is_nominal_resource,
name: StructName::parse(name)?,
type_formals,
fields: StructDefinitionFields::Native,
})
}
}
impl FunctionName {
pub fn new(name: Identifier) -> Self {
FunctionName(name)
}
pub fn parse<L, T>(s: impl Into<Box<str>>) -> Result<Self, ParseError<L, T, failure::Error>> {
Ok(FunctionName::new(parse_identifier(s.into())?))
}
pub fn into_inner(self) -> Identifier {
self.0
}
pub fn as_inner(&self) -> &IdentStr {
&self.0
}
}
impl FunctionSignature {
pub fn new(
formals: Vec<(Var_, Type)>,
return_type: Vec<Type>,
type_formals: Vec<(TypeVar_, Kind)>,
) -> Self {
FunctionSignature {
formals,
return_type,
type_formals,
}
}
}
impl Function {
pub fn new(
visibility: FunctionVisibility,
formals: Vec<(Var_, Type)>,
return_type: Vec<Type>,
type_formals: Vec<(TypeVar_, Kind)>,
acquires: Vec<StructName>,
body: FunctionBody,
) -> Self {
let signature = FunctionSignature::new(formals, return_type, type_formals);
Function {
visibility,
signature,
acquires,
body,
}
}
}
impl Var {
pub fn new(s: Identifier) -> Self {
Var(s)
}
pub fn new_(s: Identifier) -> Var_ {
Spanned::no_loc(Var::new(s))
}
pub fn parse<L, T>(s: impl Into<Box<str>>) -> Result<Self, ParseError<L, T, failure::Error>> {
Ok(Var::new(parse_identifier(s.into())?))
}
pub fn name(&self) -> &IdentStr {
&self.0
}
}
impl TypeVar {
pub fn new(s: Identifier) -> Self {
TypeVar(s)
}
pub fn parse<L, T>(s: impl Into<Box<str>>) -> Result<Self, ParseError<L, T, failure::Error>> {
Ok(TypeVar::new(parse_identifier(s.into())?))
}
pub fn name(&self) -> &IdentStr {
&self.0
}
}
impl FunctionCall {
pub fn module_call(module: ModuleName, name: FunctionName, type_actuals: Vec<Type>) -> Self {
FunctionCall::ModuleFunctionCall {
module,
name,
type_actuals,
}
}
pub fn builtin(bif: Builtin) -> FunctionCall_ {
Spanned::no_loc(FunctionCall::Builtin(bif))
}
}
impl Cmd {
pub fn return_empty() -> Self {
Cmd::Return(Box::new(Spanned::no_loc(Exp::ExprList(vec![]))))
}
pub fn return_(op: Exp_) -> Self {
Cmd::Return(Box::new(op))
}
}
impl IfElse {
pub fn if_block(cond: Exp_, if_block: Block) -> Self {
IfElse {
cond,
if_block,
else_block: None,
}
}
pub fn if_else(cond: Exp_, if_block: Block, else_block: Block) -> Self {
IfElse {
cond,
if_block,
else_block: Some(else_block),
}
}
}
impl Statement {
pub fn cmd(c: Cmd_) -> Self {
Statement::CommandStatement(c)
}
pub fn if_block(cond: Exp_, if_block: Block) -> Self {
Statement::IfElseStatement(IfElse::if_block(cond, if_block))
}
pub fn if_else(cond: Exp_, if_block: Block, else_block: Block) -> Self {
Statement::IfElseStatement(IfElse::if_else(cond, if_block, else_block))
}
}
impl Block {
pub fn new(stmts: Vec<Statement>) -> Self {
Block {
stmts: VecDeque::from(stmts),
}
}
pub fn empty() -> Self {
Block {
stmts: VecDeque::new(),
}
}
}
impl Exp {
pub fn address(addr: AccountAddress) -> Exp_ {
Spanned::no_loc(Exp::Value(Spanned::no_loc(CopyableVal::Address(addr))))
}
pub fn value(b: CopyableVal) -> Exp_ {
Spanned::no_loc(Exp::Value(Spanned::no_loc(b)))
}
pub fn u64(i: u64) -> Exp_ {
Exp::value(CopyableVal::U64(i))
}
pub fn bool(b: bool) -> Exp_ {
Exp::value(CopyableVal::Bool(b))
}
pub fn byte_array(buf: ByteArray) -> Exp_ {
Exp::value(CopyableVal::ByteArray(buf))
}
pub fn instantiate(n: StructName, tys: Vec<Type>, s: ExpFields) -> Exp_ {
Spanned::no_loc(Exp::Pack(n, tys, s))
}
pub fn binop(lhs: Exp_, op: BinOp, rhs: Exp_) -> Exp_ {
Spanned::no_loc(Exp::BinopExp(Box::new(lhs), op, Box::new(rhs)))
}
pub fn add(lhs: Exp_, rhs: Exp_) -> Exp_ {
Exp::binop(lhs, BinOp::Add, rhs)
}
pub fn sub(lhs: Exp_, rhs: Exp_) -> Exp_ {
Exp::binop(lhs, BinOp::Sub, rhs)
}
pub fn dereference(e: Exp_) -> Exp_ {
Spanned::no_loc(Exp::Dereference(Box::new(e)))
}
pub fn borrow(is_mutable: bool, exp: Box<Exp_>, field: Field) -> Exp_ {
Spanned::no_loc(Exp::Borrow {
is_mutable,
exp,
field,
})
}
pub fn copy(v: Var_) -> Exp_ {
Spanned::no_loc(Exp::Copy(v))
}
pub fn move_(v: Var_) -> Exp_ {
Spanned::no_loc(Exp::Move(v))
}
pub fn function_call(f: FunctionCall, e: Exp_) -> Exp_ {
Spanned::no_loc(Exp::FunctionCall(f, Box::new(e)))
}
pub fn expr_list(exps: Vec<Exp_>) -> Exp_ {
Spanned::no_loc(Exp::ExprList(exps))
}
}
pub fn parse_field<L, T>(
s: impl Into<Box<str>>,
) -> Result<Field, ParseError<L, T, failure::Error>> {
Ok(Field::new(parse_identifier(s.into())?))
}
fn parse_identifier<L, T>(s: Box<str>) -> Result<Identifier, ParseError<L, T, failure::Error>> {
Identifier::new(s).map_err(|error| ParseError::User { error })
}
impl Iterator for Script {
type Item = Statement;
fn next(&mut self) -> Option<Statement> {
match self.main.body {
FunctionBody::Move { ref mut code, .. } => code.stmts.pop_front(),
FunctionBody::Native => panic!("main() cannot be native code"),
}
}
}
impl PartialEq for Script {
fn eq(&self, other: &Script) -> bool {
self.imports == other.imports && self.main.body == other.main.body
}
}
impl<T> Deref for Spanned<T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl<T> AsRef<T> for Spanned<T> {
fn as_ref(&self) -> &T {
&self.value
}
}
impl<T> Spanned<T> {
pub fn no_loc(value: T) -> Spanned<T> {
Spanned {
value,
span: Span::default(),
}
}
}
impl Iterator for Block {
type Item = Statement;
fn next(&mut self) -> Option<Statement> {
self.stmts.pop_front()
}
}
impl<T> fmt::Display for Spanned<T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl fmt::Display for TypeVar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Kind::All => "all",
Kind::Resource => "resource",
Kind::Unrestricted => "unrestricted",
}
)
}
}
impl fmt::Display for ScriptOrModule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ScriptOrModule::*;
match self {
Module(module_def) => write!(f, "{}", module_def),
Script(script) => write!(f, "{}", script),
}
}
}
impl fmt::Display for Script {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Script(")?;
write!(f, "Imports(")?;
write!(f, "{}", intersperse(&self.imports, ", "))?;
writeln!(f, ")")?;
write!(f, "Main(")?;
write!(f, "{}", self.main)?;
write!(f, ")")?;
write!(f, ")")
}
}
impl fmt::Display for ImportDefinition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ModuleIdent::*;
write!(f, "ImportDefinition(")?;
match &self.ident {
Transaction(module_name) => write!(f, "{}", module_name)?,
Qualified(qual_module_ident) => write!(f, "{}", qual_module_ident)?,
};
write!(f, " => {})", self.alias)
}
}
impl fmt::Display for ModuleName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for QualifiedModuleIdent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}.{}", self.address, self.name)
}
}
impl fmt::Display for ModuleDefinition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Module({}, ", self.name)?;
write!(f, "Structs(")?;
for struct_def in &self.structs {
write!(f, "{}, ", struct_def)?;
}
write!(f, "Functions(")?;
for (fun_name, fun) in &self.functions {
write!(f, "({}, {}), ", fun_name, fun)?;
}
write!(f, ")")
}
}
impl fmt::Display for StructDefinition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"Struct({}{}, ",
self.name,
format_type_formals(&self.type_formals)
)?;
match &self.fields {
StructDefinitionFields::Move { fields } => writeln!(f, "{}", format_fields(fields))?,
StructDefinitionFields::Native => writeln!(f, "{{native}}")?,
}
write!(f, ")")
}
}
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.signature, self.body)
}
}
impl fmt::Display for StructName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for FunctionName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for FunctionBody {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FunctionBody::Move {
ref locals,
ref code,
} => {
for (local, ty) in locals {
write!(f, "let {}: {};", local, ty)?;
}
writeln!(f, "{}", code)
}
FunctionBody::Native => write!(f, "native"),
}
}
}
fn intersperse<T: fmt::Display>(items: &[T], join: &str) -> String {
items.iter().fold(String::new(), |acc, v| {
format!("{acc}{join}{v}", acc = acc, join = join, v = v)
})
}
fn format_fields<T: fmt::Display>(fields: &[(Field_, T)]) -> String {
fields.iter().fold(String::new(), |acc, (field, val)| {
format!("{} {}: {},", acc, field.value, val)
})
}
impl fmt::Display for FunctionSignature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", format_type_formals(&self.type_formals))?;
write!(f, "(")?;
for (v, ty) in self.formals.iter() {
write!(f, "{}: {}, ", v, ty)?;
}
write!(f, ")")?;
Ok(())
}
}
impl fmt::Display for QualifiedStructIdent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}", self.module, self.name)
}
}
fn format_type_actuals(tys: &[Type]) -> String {
if tys.is_empty() {
"".to_string()
} else {
format!("<{}>", intersperse(tys, ", "))
}
}
fn format_type_formals(formals: &[(TypeVar_, Kind)]) -> String {
if formals.is_empty() {
"".to_string()
} else {
let formatted = formals
.iter()
.map(|(tv, k)| format!("{}: {}", tv.value, k))
.collect::<Vec<_>>();
format!("<{}>", intersperse(&formatted, ", "))
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Type::U64 => write!(f, "u64"),
Type::Bool => write!(f, "bool"),
Type::Address => write!(f, "address"),
Type::ByteArray => write!(f, "bytearray"),
Type::String => write!(f, "string"),
Type::Struct(ident, tys) => write!(f, "{}{}", ident, format_type_actuals(tys)),
Type::Reference(is_mutable, t) => {
write!(f, "&{}{}", if *is_mutable { "mut " } else { "" }, t)
}
Type::TypeParameter(s) => write!(f, "{}", s),
}
}
}
impl fmt::Display for Var {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for Builtin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Builtin::CreateAccount => write!(f, "create_account"),
Builtin::Exists(t, tys) => write!(f, "exists<{}{}>", t, format_type_actuals(tys)),
Builtin::BorrowGlobal(mut_, t, tys) => {
let mut_flag = if *mut_ { "_mut" } else { "" };
write!(
f,
"borrow_global{}<{}{}>",
mut_flag,
t,
format_type_actuals(tys)
)
}
Builtin::GetTxnMaxGasUnits => write!(f, "get_txn_max_gas_units"),
Builtin::GetTxnGasUnitPrice => write!(f, "get_txn_gas_unit_price"),
Builtin::GetTxnPublicKey => write!(f, "get_txn_public_key"),
Builtin::GetTxnSender => write!(f, "get_txn_sender"),
Builtin::GetTxnSequenceNumber => write!(f, "get_txn_sequence_number"),
Builtin::GetGasRemaining => write!(f, "get_gas_remaining"),
Builtin::MoveFrom(t, tys) => write!(f, "move_from<{}{}>", t, format_type_actuals(tys)),
Builtin::MoveToSender(t, tys) => {
write!(f, "move_to_sender<{}{}>", t, format_type_actuals(tys))
}
Builtin::Freeze => write!(f, "freeze"),
}
}
}
impl fmt::Display for FunctionCall {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FunctionCall::Builtin(fun) => write!(f, "{}", fun),
FunctionCall::ModuleFunctionCall {
module,
name,
type_actuals,
} => write!(
f,
"{}.{}{}",
module,
name,
format_type_actuals(type_actuals)
),
}
}
}
impl fmt::Display for LValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LValue::Var(x) => write!(f, "{}", x),
LValue::Mutate(e) => write!(f, "*{}", e),
LValue::Pop => write!(f, "_"),
}
}
}
impl fmt::Display for Cmd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Cmd::Assign(var_list, e) => {
if var_list.is_empty() {
write!(f, "{};", e)
} else {
write!(f, "{} = ({});", intersperse(var_list, ", "), e)
}
}
Cmd::Unpack(n, tys, bindings, e) => write!(
f,
"{}{} {{ {} }} = {}",
n,
format_type_actuals(tys),
bindings
.iter()
.fold(String::new(), |acc, (field, var)| format!(
"{} {} : {},",
acc, field, var
)),
e
),
Cmd::Abort(None) => write!(f, "abort;"),
Cmd::Abort(Some(err)) => write!(f, "abort {};", err),
Cmd::Return(exps) => write!(f, "return {};", exps),
Cmd::Break => write!(f, "break;"),
Cmd::Continue => write!(f, "continue;"),
Cmd::Exp(e) => write!(f, "({});", e),
}
}
}
impl fmt::Display for IfElse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"if ({}) {{\n{:indent$}\n}}",
self.cond,
self.if_block,
indent = 4
)?;
match self.else_block {
None => Ok(()),
Some(ref block) => write!(f, " else {{\n{:indent$}\n}}", block, indent = 4),
}
}
}
impl fmt::Display for While {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"while ({}) {{\n{:indent$}\n}}",
self.cond,
self.block,
indent = 4
)?;
Ok(())
}
}
impl fmt::Display for Loop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "loop {{\n{:indent$}\n}}", self.block, indent = 4)?;
Ok(())
}
}
impl fmt::Display for Statement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Statement::CommandStatement(cmd) => write!(f, "{}", cmd),
Statement::IfElseStatement(if_else) => write!(f, "{}", if_else),
Statement::WhileStatement(while_) => write!(f, "{}", while_),
Statement::LoopStatement(loop_) => write!(f, "{}", loop_),
Statement::EmptyStatement => write!(f, "<empty statement>"),
}
}
}
impl fmt::Display for Block {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for stmt in self.stmts.iter() {
writeln!(f, "{}", stmt)?;
}
Ok(())
}
}
impl fmt::Display for CopyableVal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CopyableVal::U64(v) => write!(f, "{}", v),
CopyableVal::Bool(v) => write!(f, "{}", v),
CopyableVal::ByteArray(v) => write!(f, "{}", v),
CopyableVal::Address(v) => write!(f, "0x{}", hex::encode(&v)),
CopyableVal::String(v) => write!(f, "{}", v),
}
}
}
impl fmt::Display for UnaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
UnaryOp::Not => "!",
}
)
}
}
impl fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
BinOp::Add => "+",
BinOp::Sub => "-",
BinOp::Mul => "*",
BinOp::Mod => "%",
BinOp::Div => "/",
BinOp::BitOr => "|",
BinOp::BitAnd => "&",
BinOp::Xor => "^",
BinOp::Or => "||",
BinOp::And => "&&",
BinOp::Eq => "==",
BinOp::Neq => "!=",
BinOp::Lt => "<",
BinOp::Gt => ">",
BinOp::Le => "<=",
BinOp::Ge => ">=",
}
)
}
}
impl fmt::Display for Exp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Exp::Dereference(e) => write!(f, "*({})", e),
Exp::UnaryExp(o, e) => write!(f, "({}{})", o, e),
Exp::BinopExp(e1, o, e2) => write!(f, "({} {} {})", o, e1, e2),
Exp::Value(v) => write!(f, "{}", v),
Exp::Pack(n, tys, s) => write!(
f,
"{}{}{{{}}}",
n,
format_type_actuals(tys),
s.iter().fold(String::new(), |acc, (field, op)| format!(
"{} {} : {},",
acc, field, op,
))
),
Exp::Borrow {
is_mutable,
exp,
field,
} => write!(
f,
"&{}{}.{}",
if *is_mutable { "mut " } else { "" },
exp,
field
),
Exp::Move(v) => write!(f, "move({})", v),
Exp::Copy(v) => write!(f, "copy({})", v),
Exp::BorrowLocal(is_mutable, v) => {
write!(f, "&{}{}", if *is_mutable { "mut " } else { "" }, v)
}
Exp::FunctionCall(func, e) => write!(f, "{}({})", func, e),
Exp::ExprList(exps) => {
if exps.is_empty() {
write!(f, "()")
} else {
write!(f, "({})", intersperse(exps, ", "))
}
}
}
}
}