use std::{collections::HashSet, fmt};
use crate::{
ast::TypeRef,
diagnostic::{AeriError, Span},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Type {
Bool,
Int,
ByteArray,
Data,
Tx,
String,
Unit,
List(Box<Type>),
Custom(String),
Never,
}
impl Type {
pub fn from_ref(
file: &str,
ty: &TypeRef,
custom_types: &HashSet<String>,
) -> Result<Self, AeriError> {
match ty.name.as_str() {
"Bool" if ty.args.is_empty() => Ok(Self::Bool),
"Int" if ty.args.is_empty() => Ok(Self::Int),
"ByteArray" if ty.args.is_empty() => Ok(Self::ByteArray),
"Data" if ty.args.is_empty() => Ok(Self::Data),
"Tx" if ty.args.is_empty() => Ok(Self::Tx),
"String" if ty.args.is_empty() => Ok(Self::String),
"Unit" if ty.args.is_empty() => Ok(Self::Unit),
"List" => {
if ty.args.len() != 1 {
return Err(AeriError::at_span(
file,
ty.span,
"List expects exactly one type argument",
));
}
Ok(Self::List(Box::new(Self::from_ref(
file,
&ty.args[0],
custom_types,
)?)))
}
name if custom_types.contains(name) && ty.args.is_empty() => {
Ok(Self::Custom(name.to_string()))
}
name if !ty.args.is_empty() => Err(AeriError::at_span(
file,
ty.span,
format!("type '{name}' does not take arguments"),
)),
name => Err(AeriError::at_span(
file,
ty.span,
format!("unknown type '{name}'"),
)),
}
}
pub fn schema_name(&self) -> &'static str {
match self {
Type::Bool => "boolean",
Type::Int => "integer",
Type::ByteArray => "bytes",
Type::Data => "data",
Type::Tx => "transaction",
Type::String => "string",
Type::Unit => "unit",
Type::List(_) => "list",
Type::Custom(_) => "constructor",
Type::Never => "never",
}
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
Type::Bool => "Bool",
Type::Int => "Int",
Type::ByteArray => "ByteArray",
Type::Data => "Data",
Type::Tx => "Tx",
Type::String => "String",
Type::Unit => "Unit",
Type::List(item) => return write!(f, "List<{item}>"),
Type::Custom(name) => name,
Type::Never => "Never",
};
write!(f, "{name}")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Signature {
pub params: Vec<Type>,
pub result: Type,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BuiltinSignature {
pub params: Vec<Type>,
pub result: Type,
}
pub fn is_transaction_builtin(name: &str) -> bool {
matches!(
name,
"tx_signed_by"
| "tx_paid_to"
| "tx_mints"
| "tx_after"
| "tx_before"
| "tx_spends"
| "tx_has_datum"
)
}
pub fn is_test_fixture_builtin(name: &str) -> bool {
matches!(name, "test_data" | "test_tx")
}
pub fn is_builtin_type_name(name: &str) -> bool {
matches!(
name,
"Bool" | "Int" | "ByteArray" | "Data" | "Tx" | "String" | "Unit" | "List" | "Never"
)
}
pub fn is_builtin_name(name: &str) -> bool {
builtin_signature(name).is_some() || matches!(name, "list_len" | "list_has")
}
pub fn builtin_signature(name: &str) -> Option<BuiltinSignature> {
use Type::{Bool, ByteArray, Data, Int, Tx};
let signature = match name {
"tx_signed_by" => BuiltinSignature {
params: vec![Tx, ByteArray],
result: Bool,
},
"tx_paid_to" => BuiltinSignature {
params: vec![Tx, ByteArray, Int],
result: Bool,
},
"tx_mints" => BuiltinSignature {
params: vec![Tx, ByteArray, Int],
result: Bool,
},
"tx_after" | "tx_before" => BuiltinSignature {
params: vec![Tx, Int],
result: Bool,
},
"tx_spends" => BuiltinSignature {
params: vec![Tx, ByteArray],
result: Bool,
},
"tx_has_datum" => BuiltinSignature {
params: vec![Tx, Data],
result: Bool,
},
"test_data" => BuiltinSignature {
params: vec![ByteArray],
result: Data,
},
"test_tx" => BuiltinSignature {
params: vec![
Type::List(Box::new(ByteArray)),
Type::List(Box::new(ByteArray)),
Type::List(Box::new(Int)),
Type::List(Box::new(ByteArray)),
Type::List(Box::new(Int)),
Int,
Int,
Type::List(Box::new(ByteArray)),
Type::List(Box::new(Data)),
],
result: Tx,
},
"datum_equals" => BuiltinSignature {
params: vec![Data, Data],
result: Bool,
},
"sha2_256" | "blake2b_256" => BuiltinSignature {
params: vec![ByteArray],
result: ByteArray,
},
"append_bytes" => BuiltinSignature {
params: vec![ByteArray, ByteArray],
result: ByteArray,
},
"list_has_bytes" => BuiltinSignature {
params: vec![Type::List(Box::new(ByteArray)), ByteArray],
result: Bool,
},
"list_has_int" => BuiltinSignature {
params: vec![Type::List(Box::new(Int)), Int],
result: Bool,
},
"list_has_bool" => BuiltinSignature {
params: vec![Type::List(Box::new(Bool)), Bool],
result: Bool,
},
"list_has_string" => BuiltinSignature {
params: vec![Type::List(Box::new(Type::String)), Type::String],
result: Bool,
},
"list_has_data" => BuiltinSignature {
params: vec![Type::List(Box::new(Data)), Data],
result: Bool,
},
"list_has_unit" => BuiltinSignature {
params: vec![Type::List(Box::new(Type::Unit)), Type::Unit],
result: Bool,
},
"list_len_bytes" => BuiltinSignature {
params: vec![Type::List(Box::new(ByteArray))],
result: Int,
},
"list_len_int" => BuiltinSignature {
params: vec![Type::List(Box::new(Int))],
result: Int,
},
"list_len_bool" => BuiltinSignature {
params: vec![Type::List(Box::new(Bool))],
result: Int,
},
"list_len_string" => BuiltinSignature {
params: vec![Type::List(Box::new(Type::String))],
result: Int,
},
"list_len_data" => BuiltinSignature {
params: vec![Type::List(Box::new(Data))],
result: Int,
},
"list_len_unit" => BuiltinSignature {
params: vec![Type::List(Box::new(Type::Unit))],
result: Int,
},
_ => return None,
};
Some(signature)
}