pub mod well_known; use crate::{ ast::well_known::VALIDATOR_ELSE, expr::{TypedExpr, UntypedExpr}, line_numbers::LineNumbers, parser::token::{Base, Token}, tipo::{PatternConstructor, Type, TypeInfo}, }; use indexmap::IndexMap; use miette::Diagnostic; use ordinal::Ordinal; use owo_colors::{OwoColorize, Stream::Stdout}; use std::{ fmt::{self, Display}, ops::Range, rc::Rc, }; use uplc::machine::runtime::Compressable; use vec1::{vec1, Vec1}; pub const BACKPASS_VARIABLE: &str = "_backpass"; pub const CAPTURE_VARIABLE: &str = "_capture"; pub const PIPE_VARIABLE: &str = "_pipe"; pub const ENV_MODULE: &str = "env"; pub const CONFIG_MODULE: &str = "config"; pub const DEFAULT_ENV_MODULE: &str = "default"; pub const HANDLER_SPEND: &str = "spend"; pub const HANDLER_MINT: &str = "mint"; pub const HANDLER_WITHDRAW: &str = "withdraw"; pub const HANDLER_PUBLISH: &str = "publish"; pub const HANDLER_VOTE: &str = "vote"; pub const HANDLER_PROPOSE: &str = "propose"; pub type TypedModule = Module; pub type UntypedModule = Module<(), UntypedDefinition>; #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub enum ModuleKind { Lib, Validator, Env, Config, } impl ModuleKind { pub fn is_validator(&self) -> bool { matches!(self, ModuleKind::Validator) } pub fn is_lib(&self) -> bool { matches!(self, ModuleKind::Lib) } pub fn is_env(&self) -> bool { matches!(self, ModuleKind::Env) } pub fn is_config(&self) -> bool { matches!(self, ModuleKind::Config) } } #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Module { pub name: String, pub docs: Vec, pub type_info: Info, pub definitions: Vec, pub lines: LineNumbers, pub kind: ModuleKind, } impl Module { pub fn definitions(&self) -> impl Iterator { self.definitions.iter() } pub fn into_definitions(self) -> impl Iterator { self.definitions.into_iter() } } impl UntypedModule { pub fn dependencies(&self, env_modules: &[String]) -> Vec { self.definitions() .flat_map(|def| { if let Definition::Use(Use { module, .. }) = def { let name = module.join("/"); if name == ENV_MODULE { env_modules.to_vec() } else { vec![name] } } else { Vec::new() } }) .collect() } } impl TypedModule { pub fn find_node(&self, byte_index: usize) -> Option> { self.definitions .iter() .find_map(|definition| definition.find_node(byte_index)) } pub fn has_definition(&self, name: &str) -> bool { self.definitions.iter().any(|def| match def { Definition::Fn(f) => f.public && f.name == name, Definition::TypeAlias(alias) => alias.public && alias.alias == name, Definition::ModuleConstant(cst) => cst.public && cst.name == name, Definition::DataType(t) => t.public && t.name == name, Definition::Use(_) => false, Definition::Test(_) => false, Definition::Validator(_) => false, }) } pub fn has_constructor(&self, name: &str) -> bool { self.definitions.iter().any(|def| match def { Definition::DataType(t) if t.public && !t.opaque => t .constructors .iter() .any(|constructor| constructor.name == name), Definition::DataType(_) => false, Definition::Fn(_) => false, Definition::TypeAlias(_) => false, Definition::ModuleConstant(_) => false, Definition::Use(_) => false, Definition::Test(_) => false, Definition::Validator(_) => false, }) } pub fn validate_module_name(&self) -> Result<(), Error> { if self.name == "aiken" || self.name == "aiken/builtin" { return Err(Error::ReservedModuleName { name: self.name.to_string(), }); }; for segment in self.name.split('/') { if str_to_keyword(segment).is_some() { return Err(Error::KeywordInModuleName { name: self.name.to_string(), keyword: segment.to_string(), }); } } Ok(()) } // TODO: Avoid cloning definitions here. This would likely require having a lifetime on // 'Project', so that we can enforce that those references live from the ast to here. pub fn register_definitions( &self, functions: &mut IndexMap, constants: &mut IndexMap, data_types: &mut IndexMap, ) { for def in self.definitions() { match def { Definition::Fn(func) => { functions.insert( FunctionAccessKey { module_name: self.name.clone(), function_name: func.name.clone(), }, func.clone(), ); } Definition::Test(test) => { functions.insert( FunctionAccessKey { module_name: self.name.clone(), function_name: test.name.clone(), }, test.clone().into(), ); } Definition::DataType(dt) => { data_types.insert( DataTypeKey { module_name: self.name.clone(), defined_type: dt.name.clone(), }, dt.clone(), ); } Definition::Validator(v) => { let module_name = self.name.as_str(); for (k, v) in v.into_function_definitions(module_name) { functions.insert(k, v); } } Definition::ModuleConstant(ModuleConstant { name, value, .. }) => { constants.insert( FunctionAccessKey { module_name: self.name.clone(), function_name: name.clone(), }, value.clone(), ); } Definition::TypeAlias(_) | Definition::Use(_) => {} } } } } fn str_to_keyword(word: &str) -> Option { // Alphabetical keywords: match word { "expect" => Some(Token::Expect), "else" => Some(Token::Else), "is" => Some(Token::Is), "as" => Some(Token::As), "when" => Some(Token::When), "const" => Some(Token::Const), "fn" => Some(Token::Fn), "if" => Some(Token::If), "use" => Some(Token::Use), "let" => Some(Token::Let), "opaque" => Some(Token::Opaque), "pub" => Some(Token::Pub), "todo" => Some(Token::Todo), "type" => Some(Token::Type), "trace" => Some(Token::Trace), "test" => Some(Token::Test), // TODO: remove this in a future release "error" => Some(Token::Fail), "fail" => Some(Token::Fail), "and" => Some(Token::And), "or" => Some(Token::Or), "validator" => Some(Token::Validator), "via" => Some(Token::Via), _ => None, } } pub type TypedFunction = Function, TypedExpr, TypedArg>; pub type UntypedFunction = Function<(), UntypedExpr, UntypedArg>; impl UntypedFunction { pub fn is_default_fallback(&self) -> bool { matches!( &self.arguments[..], [UntypedArg { by: ArgBy::ByName(ArgName::Discarded { .. }), .. }] ) && matches!(&self.body, UntypedExpr::ErrorTerm { .. }) && self.name.as_str() == well_known::VALIDATOR_ELSE } } pub type TypedTest = Function, TypedExpr, TypedArgVia>; pub type UntypedTest = Function<(), UntypedExpr, UntypedArgVia>; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub enum OnTestFailure { FailImmediately, SucceedImmediately, SucceedEventually, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct Function { pub arguments: Vec, pub body: Expr, pub doc: Option, pub location: Span, pub name: String, pub public: bool, pub return_annotation: Option, pub return_type: T, pub end_position: usize, pub on_test_failure: OnTestFailure, } impl Function { pub fn is_spend(&self) -> bool { self.name == HANDLER_SPEND } pub fn is_mint(&self) -> bool { self.name == HANDLER_MINT } } impl TypedFunction { pub fn find_node(&self, byte_index: usize) -> Option> { self.arguments .iter() .find_map(|arg| arg.find_node(byte_index)) .or_else(|| self.body.find_node(byte_index)) .or_else(|| { self.return_annotation .as_ref() .and_then(|a| a.find_node(byte_index)) }) } pub fn has_valid_purpose_name(&self) -> bool { self.name == HANDLER_SPEND || self.name == HANDLER_PUBLISH || self.name == HANDLER_PROPOSE || self.name == HANDLER_MINT || self.name == HANDLER_WITHDRAW || self.name == HANDLER_VOTE } pub fn validator_arity(&self) -> usize { if self.name == HANDLER_SPEND { 4 } else if self.name == HANDLER_MINT || self.name == HANDLER_WITHDRAW || self.name == HANDLER_VOTE || self.name == HANDLER_PUBLISH || self.name == HANDLER_PROPOSE { 3 } else { panic!( "tried to get validator arity of a non-validator function {}", &self.name ); } } } impl TypedTest { pub fn find_node(&self, byte_index: usize) -> Option> { self.arguments .iter() .find_map(|arg| arg.find_node(byte_index)) .or_else(|| self.body.find_node(byte_index)) .or_else(|| { self.return_annotation .as_ref() .and_then(|a| a.find_node(byte_index)) }) } } pub type TypedTypeAlias = TypeAlias>; pub type UntypedTypeAlias = TypeAlias<()>; impl From for UntypedFunction { fn from(f: UntypedTest) -> Self { Function { doc: f.doc, location: f.location, name: f.name, public: f.public, arguments: f.arguments.into_iter().map(|arg| arg.into()).collect(), return_annotation: f.return_annotation, return_type: f.return_type, body: f.body, on_test_failure: f.on_test_failure, end_position: f.end_position, } } } impl From for TypedFunction { fn from(f: TypedTest) -> Self { Function { doc: f.doc, location: f.location, name: f.name, public: f.public, arguments: f.arguments.into_iter().map(|arg| arg.into()).collect(), return_annotation: f.return_annotation, return_type: f.return_type, body: f.body, on_test_failure: f.on_test_failure, end_position: f.end_position, } } } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct TypeAlias { pub alias: String, pub annotation: Annotation, pub doc: Option, pub location: Span, pub parameters: Vec, pub public: bool, pub tipo: T, } #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct DataTypeKey { pub module_name: String, pub defined_type: String, } #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct FunctionAccessKey { pub module_name: String, pub function_name: String, } pub type UntypedDataType = DataType<()>; pub type TypedDataType = DataType>; impl TypedDataType { pub fn known_enum(name: &str, constructors: &[&str]) -> Self { Self::known_data_type(name, &RecordConstructor::known_enum(constructors)) } pub fn known_data_type(name: &str, constructors: &[RecordConstructor>]) -> Self { Self { name: name.to_string(), constructors: constructors.to_vec(), location: Span::empty(), opaque: false, public: true, parameters: vec![], typed_parameters: vec![], doc: None, } } pub fn is_never(&self) -> bool { self.name == well_known::NEVER && self.constructors.len() == well_known::NEVER_CONSTRUCTORS.len() && self.location == Span::empty() && self .constructors .iter() .zip(well_known::NEVER_CONSTRUCTORS) .all(|(constructor, name)| { name == &constructor.name && constructor.arguments.is_empty() }) } } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct DataType { pub constructors: Vec>, pub doc: Option, pub location: Span, pub name: String, pub opaque: bool, pub parameters: Vec, pub public: bool, pub typed_parameters: Vec, } pub type TypedUse = Use; pub type UntypedUse = Use<()>; #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Use { pub as_name: Option, pub location: Span, pub module: Vec, pub package: PackageName, pub unqualified: Vec, } pub type TypedModuleConstant = ModuleConstant; pub type UntypedModuleConstant = ModuleConstant; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct ModuleConstant { pub doc: Option, pub location: Span, pub public: bool, pub name: String, pub annotation: Option, pub value: Expr, } pub type TypedValidator = Validator, TypedArg, TypedExpr>; pub type UntypedValidator = Validator<(), UntypedArg, UntypedExpr>; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub enum Purpose { Spend, Mint, Withdraw, Publish, Propose, Vote, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct Validator { pub doc: Option, pub end_position: usize, pub handlers: Vec>, pub location: Span, pub name: String, pub params: Vec, pub fallback: Function, } impl Validator { pub fn handler_name(validator: &str, handler: &str) -> String { format!("{}.{}", validator, handler) } } impl UntypedValidator { pub fn default_fallback(location: Span) -> UntypedFunction { Function { arguments: vec![UntypedArg { by: ArgBy::ByName(ArgName::Discarded { name: "_".to_string(), label: "_".to_string(), location, }), location, annotation: None, doc: None, is_validator_param: false, }], body: UntypedExpr::fail(None, location), doc: None, location, end_position: location.end - 1, name: well_known::VALIDATOR_ELSE.to_string(), public: true, return_annotation: Some(Annotation::boolean(location)), return_type: (), on_test_failure: OnTestFailure::FailImmediately, } } } impl TypedValidator { pub fn available_handler_names() -> Vec { vec![ HANDLER_SPEND.to_string(), HANDLER_MINT.to_string(), HANDLER_WITHDRAW.to_string(), HANDLER_PUBLISH.to_string(), HANDLER_VOTE.to_string(), HANDLER_PROPOSE.to_string(), VALIDATOR_ELSE.to_string(), ] } // Define a validator wrapper extracting and matching on script purpose for // users. pub fn into_script_context_handler(&self) -> TypedExpr { let var_context = "__context__"; let var_transaction = "__transaction__"; let var_redeemer = "__redeemer__"; let var_purpose = "__purpose__"; let var_purpose_arg = "__purpose_arg__"; let var_datum = "__datum__"; TypedExpr::sequence(&[ TypedExpr::let_( TypedExpr::local_var(var_context, Type::script_context(), self.location), TypedPattern::Constructor { is_record: false, location: Span::empty(), name: well_known::SCRIPT_CONTEXT_CONSTRUCTORS[0].to_string(), arguments: vec![ CallArg::var(var_transaction, Span::empty()), CallArg::var(var_redeemer, Span::empty()), CallArg::var(var_purpose, Span::empty()), ], module: None, constructor: PatternConstructor::Record { name: well_known::SCRIPT_CONTEXT_CONSTRUCTORS[0].to_string(), field_map: None, }, spread_location: None, tipo: Type::function( vec![Type::data(), Type::data(), Type::script_purpose()], Type::data(), ), }, Type::script_context(), Span::empty(), ), TypedExpr::When { location: Span::empty(), tipo: Type::bool(), subject: TypedExpr::local_var(var_purpose, Type::script_purpose(), Span::empty()) .into(), clauses: self .handlers .iter() .map(|handler| { let datum = if handler.name.as_str() == "spend" { handler.arguments.first() } else { None }; let redeemer = handler .arguments .get(if datum.is_some() { 1 } else { 0 }) .unwrap(); let purpose_arg = handler.arguments.iter().nth_back(1).unwrap(); let transaction = handler.arguments.last().unwrap(); let pattern = match handler.name.as_str() { "spend" => TypedPattern::spend_purpose( (var_purpose_arg, purpose_arg.location), ( var_datum, datum.map(|x| x.location).unwrap_or(Span::empty()), ), redeemer.location, ), "mint" => TypedPattern::mint_purpose( (var_purpose_arg, purpose_arg.location), redeemer.location, ), "withdraw" => TypedPattern::withdraw_purpose( (var_purpose_arg, purpose_arg.location), redeemer.location, ), "publish" => TypedPattern::publish_purpose( (var_purpose_arg, purpose_arg.location), redeemer.location, ), "propose" => TypedPattern::propose_purpose( (var_purpose_arg, purpose_arg.location), redeemer.location, ), "vote" => TypedPattern::vote_purpose( (var_purpose_arg, purpose_arg.location), redeemer.location, ), purpose => { unreachable!("unexpected/unknown purpose: {:?}", purpose) } }; let mut then = vec![]; // expect redeemer: tipo = __redeemer__ then.push(TypedExpr::flexible_expect( TypedExpr::local_var(var_redeemer, Type::data(), redeemer.location), TypedPattern::var(redeemer.get_variable_name().unwrap_or("_")), redeemer.tipo.clone(), redeemer.location, )); // Cast the datum, if any if let Some(datum) = datum { // expect datum: tipo = __datum__ then.push(TypedExpr::flexible_expect( TypedExpr::local_var( var_datum, Type::option(Type::data()), datum.location, ), TypedPattern::var(datum.get_variable_name().unwrap_or("_")), datum.tipo.clone(), datum.location, )) } // let purpose_arg = __purpose_arg__ if let Some(arg_name) = purpose_arg.get_variable_name() { then.push(TypedExpr::let_( TypedExpr::local_var( var_purpose_arg, Type::data(), purpose_arg.location, ), TypedPattern::var(arg_name), purpose_arg.tipo.clone(), purpose_arg.location, )); } // let last_arg_name = __transaction__ if let Some(arg_name) = transaction.get_variable_name() { then.push(TypedExpr::let_( TypedExpr::local_var( var_transaction, Type::data(), transaction.location, ), TypedPattern::var(arg_name), Type::data(), transaction.location, )); } then.push(handler.body.clone()); TypedClause { location: Span::empty(), pattern, then: TypedExpr::Sequence { location: Span::empty(), expressions: then, }, } }) .chain(std::iter::once(&self.fallback).map(|fallback| { let arg = fallback.arguments.first().unwrap(); let then = match arg.get_variable_name() { Some(arg_name) => TypedExpr::sequence(&[ TypedExpr::let_( TypedExpr::local_var( var_context, arg.tipo.clone(), arg.location, ), TypedPattern::var(arg_name), arg.tipo.clone(), arg.location, ), fallback.body.clone(), ]), None => fallback.body.clone(), }; TypedClause { location: Span::empty(), pattern: TypedPattern::Discard { name: "_".to_string(), location: arg.location, }, then, } })) .collect(), }, ]) } pub fn find_node(&self, byte_index: usize) -> Option> { self.params .iter() .find_map(|arg| arg.find_node(byte_index)) .or_else(|| { self.handlers .iter() .find_map(|func| func.find_node(byte_index)) }) .or_else(|| self.fallback.find_node(byte_index)) } pub fn into_function_definitions( &self, module_name: &str, ) -> Vec<(FunctionAccessKey, TypedFunction)> { self.handlers .iter() .chain(std::iter::once(&self.fallback)) .map(|handler| { let mut handler = handler.clone(); handler.arguments = self .params .clone() .into_iter() .chain(handler.arguments) .collect(); ( FunctionAccessKey { module_name: module_name.to_string(), function_name: TypedValidator::handler_name( self.name.as_str(), handler.name.as_str(), ), }, handler, ) }) .collect() } } pub type TypedDefinition = Definition, TypedArg, TypedExpr, String>; pub type UntypedDefinition = Definition<(), UntypedArg, UntypedExpr, ()>; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub enum Definition { Fn(Function), TypeAlias(TypeAlias), DataType(DataType), Use(Use), ModuleConstant(ModuleConstant), Test(Function>), Validator(Validator), } impl Definition { pub fn location(&self) -> Span { match self { Definition::Fn(Function { location, .. }) | Definition::Use(Use { location, .. }) | Definition::TypeAlias(TypeAlias { location, .. }) | Definition::DataType(DataType { location, .. }) | Definition::ModuleConstant(ModuleConstant { location, .. }) | Definition::Validator(Validator { location, .. }) | Definition::Test(Function { location, .. }) => *location, } } pub fn put_doc(&mut self, new_doc: String) { match self { Definition::Use { .. } => (), Definition::Fn(Function { doc, .. }) | Definition::TypeAlias(TypeAlias { doc, .. }) | Definition::DataType(DataType { doc, .. }) | Definition::ModuleConstant(ModuleConstant { doc, .. }) | Definition::Validator(Validator { doc, .. }) | Definition::Test(Function { doc, .. }) => { let _ = std::mem::replace(doc, Some(new_doc)); } } } pub fn doc(&self) -> Option { match self { Definition::Use { .. } => None, Definition::Fn(Function { doc, .. }) | Definition::TypeAlias(TypeAlias { doc, .. }) | Definition::DataType(DataType { doc, .. }) | Definition::ModuleConstant(ModuleConstant { doc, .. }) | Definition::Validator(Validator { doc, .. }) | Definition::Test(Function { doc, .. }) => doc.clone(), } } } impl TypedDefinition { pub fn find_node(&self, byte_index: usize) -> Option> { // Note that the fn span covers the function head, not // the entire statement. let located = match self { Definition::Validator(validator) => validator.find_node(byte_index), Definition::Fn(func) => func.find_node(byte_index), Definition::Test(func) => func.find_node(byte_index), _ => None, }; if located.is_none() && self.location().contains(byte_index) { return Some(Located::Definition(self)); } located } } #[derive(Debug, Clone, PartialEq)] pub enum Located<'a> { Expression(&'a TypedExpr), Pattern(&'a TypedPattern, Rc), Definition(&'a TypedDefinition), Argument(&'a ArgName, Rc), Annotation(&'a Annotation), } impl<'a> Located<'a> { pub fn definition_location(&self) -> Option> { match self { Self::Expression(expression) => expression.definition_location(), Self::Definition(definition) => Some(DefinitionLocation { module: None, span: definition.location(), }), // TODO: Revise definition location semantic for 'Pattern' // e.g. for constructors, we might want to show the type definition // for that constructor. Self::Pattern(_, _) | Located::Argument(_, _) | Located::Annotation(_) => None, } } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct DefinitionLocation<'module> { pub module: Option<&'module str>, pub span: Span, } pub type TypedCallArg = CallArg; pub type ParsedCallArg = CallArg>; #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct CallArg { pub label: Option, pub location: Span, pub value: A, } impl CallArg { pub fn is_capture_hole(&self) -> bool { match &self.value { UntypedExpr::Var { ref name, .. } => name.contains(CAPTURE_VARIABLE), _ => false, } } } impl TypedCallArg { pub fn find_node(&self, byte_index: usize) -> Option> { self.value.find_node(byte_index) } } impl CallArg { pub fn var(name: &str, location: Span) -> Self { CallArg { label: None, location: Span::empty(), value: TypedPattern::Var { location, name: name.to_string(), }, } } } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct RecordConstructor { pub location: Span, pub name: String, pub arguments: Vec>, pub doc: Option, pub sugar: bool, } impl RecordConstructor where A: Clone, { pub fn put_doc(&mut self, new_doc: String) { self.doc = Some(new_doc); } pub fn known_enum(names: &[&str]) -> Vec> { names .iter() .map(|name| RecordConstructor { location: Span::empty(), name: name.to_string(), arguments: vec![], doc: None, sugar: false, }) .collect() } pub fn known_record(name: &str, args: &[RecordConstructorArg]) -> Self { RecordConstructor { location: Span::empty(), name: name.to_string(), arguments: args.to_vec(), doc: None, sugar: false, } } } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct RecordConstructorArg { pub label: Option, // ast pub annotation: Annotation, pub location: Span, pub tipo: T, pub doc: Option, } impl RecordConstructorArg { pub fn put_doc(&mut self, new_doc: String) { self.doc = Some(new_doc); } } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub enum ArgBy { ByName(ArgName), ByPattern(UntypedPattern), } impl ArgBy { pub fn into_extra_assignment( self, name: &ArgName, annotation: Option<&Annotation>, location: Span, ) -> Option { match self { ArgBy::ByName(..) => None, ArgBy::ByPattern(pattern) => Some(UntypedExpr::Assignment { location, value: Box::new(UntypedExpr::Var { location, name: name.get_name(), }), patterns: vec1![AssignmentPattern { pattern, location, annotation: annotation.cloned(), }], kind: AssignmentKind::Let { backpassing: false }, }), } } } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct UntypedArg { pub by: ArgBy, pub location: Span, pub annotation: Option, pub doc: Option, pub is_validator_param: bool, } impl UntypedArg { pub fn arg_name(&self, ix: usize) -> ArgName { match self.by { ArgBy::ByName(ref name) => name.clone(), ArgBy::ByPattern(..) => { // NOTE: We use ordinal here not only because it's cute, but because // such a name cannot be parsed to begin with and thus, will not clash // with any user-defined name. let name = format!("{}{}_arg", ix + 1, Ordinal::(ix + 1).suffix()); ArgName::Named { label: name.clone(), name, location: self.location, } } } } pub fn set_type(self, tipo: Rc, ix: usize) -> TypedArg { TypedArg { tipo, arg_name: self.arg_name(ix), location: self.location, annotation: self.annotation, is_validator_param: self.is_validator_param, doc: self.doc, } } } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct TypedArg { pub arg_name: ArgName, pub location: Span, pub annotation: Option, pub doc: Option, pub is_validator_param: bool, pub tipo: Rc, } impl TypedArg { pub fn new(name: &str, tipo: Rc) -> Self { TypedArg { arg_name: ArgName::Named { name: name.to_string(), label: name.to_string(), location: Span::empty(), }, location: Span::empty(), annotation: None, doc: None, is_validator_param: false, tipo: tipo.clone(), } } pub fn put_doc(&mut self, new_doc: String) { self.doc = Some(new_doc); } pub fn get_variable_name(&self) -> Option<&str> { self.arg_name.get_variable_name() } pub fn get_name(&self) -> String { self.arg_name.get_name() } pub fn is_capture(&self) -> bool { if let ArgName::Named { ref name, location, .. } = self.arg_name { return name.starts_with(CAPTURE_VARIABLE) && location == Span::empty() && self.location == Span::empty(); } false } pub fn find_node(&self, byte_index: usize) -> Option> { if self.arg_name.location().contains(byte_index) { Some(Located::Argument(&self.arg_name, self.tipo.clone())) } else { self.annotation .as_ref() .and_then(|annotation| annotation.find_node(byte_index)) } } } pub type TypedArgVia = ArgVia; pub type UntypedArgVia = ArgVia; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct ArgVia { pub arg: Arg, pub via: Expr, } impl From> for TypedArg { fn from(this: ArgVia) -> TypedArg { this.arg } } impl From> for UntypedArg { fn from(this: ArgVia) -> UntypedArg { this.arg } } impl TypedArgVia { pub fn find_node(&self, byte_index: usize) -> Option> { if self.arg.arg_name.location().contains(byte_index) { Some(Located::Argument(&self.arg.arg_name, self.arg.tipo.clone())) } else { // `via` is done first here because when there is no manually written // annotation, it seems one is injected leading to a `found` returning too early // because the span of the filled in annotation matches the span of the via expr. self.via.find_node(byte_index).or_else(|| { self.arg .annotation .as_ref() .and_then(|annotation| annotation.find_node(byte_index)) }) } } } #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub enum ArgName { Discarded { name: String, label: String, location: Span, }, Named { name: String, label: String, location: Span, }, } impl ArgName { pub fn location(&self) -> Span { match self { ArgName::Discarded { location, .. } => *location, ArgName::Named { location, .. } => *location, } } /// Returns the name of the variable if it is named, otherwise None. /// Code gen uses the fact that this returns None to do certain things. pub fn get_variable_name(&self) -> Option<&str> { match self { ArgName::Discarded { .. } => None, ArgName::Named { name, .. } => Some(name), } } pub fn get_name(&self) -> String { match self { ArgName::Discarded { name, .. } | ArgName::Named { name, .. } => name.clone(), } } pub fn get_label(&self) -> String { match self { ArgName::Discarded { label, .. } | ArgName::Named { label, .. } => label.to_string(), } } } #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct UnqualifiedImport { pub location: Span, pub name: String, pub as_name: Option, } impl UnqualifiedImport { pub fn variable_name(&self) -> &str { self.as_name.as_deref().unwrap_or(&self.name) } } // TypeAst #[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub enum Annotation { Constructor { location: Span, module: Option, name: String, arguments: Vec, }, Fn { location: Span, arguments: Vec, ret: Box, }, Var { location: Span, name: String, }, Hole { location: Span, name: String, }, Tuple { location: Span, elems: Vec, }, Pair { location: Span, fst: Box, snd: Box, }, } impl Annotation { pub fn location(&self) -> Span { match self { Annotation::Fn { location, .. } | Annotation::Tuple { location, .. } | Annotation::Var { location, .. } | Annotation::Hole { location, .. } | Annotation::Constructor { location, .. } | Annotation::Pair { location, .. } => *location, } } pub fn boolean(location: Span) -> Self { Annotation::Constructor { name: "Bool".to_string(), module: None, arguments: vec![], location, } } pub fn int(location: Span) -> Self { Annotation::Constructor { name: "Int".to_string(), module: None, arguments: vec![], location, } } pub fn bytearray(location: Span) -> Self { Annotation::Constructor { name: "ByteArray".to_string(), module: None, arguments: vec![], location, } } pub fn data(location: Span) -> Self { Annotation::Constructor { name: "Data".to_string(), module: None, arguments: vec![], location, } } pub fn option(inner: Annotation) -> Self { Annotation::Constructor { name: "Option".to_string(), module: None, location: inner.location(), arguments: vec![inner], } } pub fn list(inner: Annotation, location: Span) -> Self { Annotation::Constructor { name: "List".to_string(), module: None, arguments: vec![inner], location, } } pub fn tuple(elems: Vec, location: Span) -> Self { Annotation::Tuple { elems, location } } pub fn is_logically_equal(&self, other: &Annotation) -> bool { match self { Annotation::Constructor { module, name, arguments, location: _, } => match other { Annotation::Constructor { module: o_module, name: o_name, arguments: o_arguments, location: _, } => { module == o_module && name == o_name && arguments.len() == o_arguments.len() && arguments .iter() .zip(o_arguments) .all(|a| a.0.is_logically_equal(a.1)) } _ => false, }, Annotation::Tuple { elems, location: _ } => match other { Annotation::Tuple { elems: o_elems, location: _, } => { elems.len() == o_elems.len() && elems .iter() .zip(o_elems) .all(|a| a.0.is_logically_equal(a.1)) } _ => false, }, Annotation::Fn { arguments, ret, location: _, } => match other { Annotation::Fn { arguments: o_arguments, ret: o_return, location: _, } => { arguments.len() == o_arguments.len() && arguments .iter() .zip(o_arguments) .all(|a| a.0.is_logically_equal(a.1)) && ret.is_logically_equal(o_return) } _ => false, }, Annotation::Var { name, location: _ } => match other { Annotation::Var { name: o_name, location: _, } => name == o_name, _ => false, }, Annotation::Hole { name, location: _ } => match other { Annotation::Hole { name: o_name, location: _, } => name == o_name, _ => false, }, Annotation::Pair { fst, snd, .. } => { if let Annotation::Pair { fst: o_fst, snd: o_snd, .. } = other { fst.is_logically_equal(o_fst) && snd.is_logically_equal(o_snd) } else { false } } } } pub fn find_node(&self, byte_index: usize) -> Option> { if !self.location().contains(byte_index) { return None; } let located = match self { Annotation::Constructor { arguments, .. } => { arguments.iter().find_map(|arg| arg.find_node(byte_index)) } Annotation::Fn { arguments, ret, .. } => arguments .iter() .find_map(|arg| arg.find_node(byte_index)) .or_else(|| ret.find_node(byte_index)), Annotation::Tuple { elems, .. } => { elems.iter().find_map(|arg| arg.find_node(byte_index)) } Annotation::Var { .. } | Annotation::Hole { .. } => None, Annotation::Pair { fst, snd, .. } => fst .find_node(byte_index) .or_else(|| snd.find_node(byte_index)), }; located.or(Some(Located::Annotation(self))) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub enum BinOp { // Boolean logic And, Or, // Equality Eq, NotEq, // Order comparison LtInt, LtEqInt, GtEqInt, GtInt, // Maths AddInt, SubInt, MultInt, DivInt, ModInt, } impl From for BinOp { fn from(value: LogicalOpChainKind) -> Self { match value { LogicalOpChainKind::And => BinOp::And, LogicalOpChainKind::Or => BinOp::Or, } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub enum UnOp { /// ! Not, /// - Negate, } impl BinOp { pub fn precedence(&self) -> u8 { // Ensure that this matches the other precedence function for guards match self { // Pipe is 0 // Unary operators are 1 Self::Or => 2, Self::And => 3, Self::Eq | Self::NotEq | Self::LtInt | Self::LtEqInt | Self::GtEqInt | Self::GtInt => 4, // Concatenation operators are typically 5, so we skip it. Self::AddInt | Self::SubInt => 6, Self::MultInt | Self::DivInt | Self::ModInt => 7, } } } pub type UntypedPattern = Pattern<(), ()>; pub type TypedPattern = Pattern>; impl TypedPattern { pub fn var(name: &str) -> Self { TypedPattern::Var { name: name.to_string(), location: Span::empty(), } } pub fn constructor( name: &str, arguments: &[CallArg], tipo: Rc, location: Span, ) -> Self { TypedPattern::Constructor { is_record: false, location, name: name.to_string(), arguments: arguments.to_vec(), module: None, constructor: PatternConstructor::Record { name: name.to_string(), field_map: None, }, spread_location: None, tipo: tipo.clone(), } } pub fn mint_purpose( (var_purpose_arg, purpose_span): (&str, Span), redeemer_span: Span, ) -> Self { TypedPattern::constructor( well_known::SCRIPT_PURPOSE_MINT, &[CallArg::var(var_purpose_arg, purpose_span)], Type::function(vec![Type::byte_array()], Type::script_purpose()), redeemer_span, ) } pub fn spend_purpose( (var_purpose_arg, purpose_span): (&str, Span), (var_datum, datum_span): (&str, Span), redeemer_span: Span, ) -> Self { TypedPattern::constructor( well_known::SCRIPT_PURPOSE_SPEND, &[ CallArg::var(var_purpose_arg, purpose_span), CallArg::var(var_datum, datum_span), ], Type::function( vec![Type::data(), Type::option(Type::data())], Type::script_purpose(), ), redeemer_span, ) } pub fn withdraw_purpose( (var_purpose_arg, purpose_span): (&str, Span), redeemer_span: Span, ) -> Self { TypedPattern::constructor( well_known::SCRIPT_PURPOSE_WITHDRAW, &[CallArg::var(var_purpose_arg, purpose_span)], Type::function(vec![Type::data()], Type::script_purpose()), redeemer_span, ) } pub fn publish_purpose( (var_purpose_arg, purpose_span): (&str, Span), redeemer_span: Span, ) -> Self { TypedPattern::constructor( well_known::SCRIPT_PURPOSE_PUBLISH, &[ CallArg::var("__discarded_purpose_ix__", purpose_span), CallArg::var(var_purpose_arg, purpose_span), ], Type::function(vec![Type::int(), Type::data()], Type::script_purpose()), redeemer_span, ) } pub fn vote_purpose( (var_purpose_arg, purpose_span): (&str, Span), redeemer_span: Span, ) -> Self { TypedPattern::constructor( well_known::SCRIPT_PURPOSE_VOTE, &[CallArg::var(var_purpose_arg, purpose_span)], Type::function(vec![Type::data()], Type::script_purpose()), redeemer_span, ) } pub fn propose_purpose( (var_purpose_arg, purpose_span): (&str, Span), redeemer_span: Span, ) -> Self { TypedPattern::constructor( well_known::SCRIPT_PURPOSE_PROPOSE, &[ CallArg::var("__discarded_purpose_ix__", purpose_span), CallArg::var(var_purpose_arg, purpose_span), ], Type::function(vec![Type::int(), Type::data()], Type::script_purpose()), redeemer_span, ) } } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub enum Pattern { Int { location: Span, value: String, base: Base, }, ByteArray { location: Span, value: Vec, preferred_format: ByteArrayFormatPreference, }, /// The creation of a variable. /// e.g. `expect [this_is_a_var, .._] = x` /// e.g. `let foo = 42` Var { location: Span, name: String, }, /// A name given to a sub-pattern using the `as` keyword. /// /// ```aiken /// when foo is { /// [_, _] as the_list -> ... /// } /// ``` Assign { name: String, location: Span, pattern: Box, }, /// A pattern that binds to any value but does not assign a variable. /// Always starts with an underscore. Discard { name: String, location: Span, }, List { location: Span, elements: Vec, tail: Option>, }, /// The constructor for a custom type. Starts with an uppercase letter. Constructor { is_record: bool, location: Span, name: String, arguments: Vec>, module: Option, constructor: Constructor, spread_location: Option, tipo: Type, }, Pair { location: Span, fst: Box, snd: Box, }, Tuple { location: Span, elems: Vec, }, } impl Pattern { pub fn location(&self) -> Span { match self { Pattern::Assign { pattern, .. } => pattern.location(), Pattern::Int { location, .. } | Pattern::Var { location, .. } | Pattern::List { location, .. } | Pattern::Discard { location, .. } | Pattern::Tuple { location, .. } | Pattern::Pair { location, .. } | Pattern::ByteArray { location, .. } | Pattern::Constructor { location, .. } => *location, } } /// Returns true when a Pattern can be displayed in a flex-break manner (i.e. tries to fit as /// much as possible on a single line). When false, long lines with several of those patterns /// will be broken down to one pattern per line. pub fn is_simple_pattern_to_format(&self) -> bool { match self { Self::ByteArray { .. } | Self::Int { .. } | Self::Var { .. } | Self::Discard { .. } => { true } Self::Pair { fst, snd, .. } => { fst.is_simple_pattern_to_format() && snd.is_simple_pattern_to_format() } Self::Tuple { elems, .. } => elems.iter().all(|e| e.is_simple_pattern_to_format()), Self::List { elements, .. } if elements.len() <= 3 => { elements.iter().all(|e| e.is_simple_pattern_to_format()) } Self::Constructor { arguments, .. } => arguments.is_empty(), _ => false, } } pub fn with_spread(&self) -> bool { match self { Pattern::Constructor { spread_location, .. } => spread_location.is_some(), _ => false, } } /// Returns `true` if the pattern is [`Discard`]. /// /// [`Discard`]: Pattern::Discard pub fn is_discard(&self) -> bool { matches!(self, Self::Discard { .. }) } /// Returns `true` if the pattern is [`Var`]. /// /// [`Var`]: Pattern::Discard pub fn is_var(&self) -> bool { matches!(self, Self::Var { .. }) } } impl UntypedPattern { pub fn true_(location: Span) -> UntypedPattern { UntypedPattern::Constructor { location, name: "True".to_string(), arguments: vec![], constructor: (), spread_location: None, tipo: (), module: None, is_record: false, } } pub fn collect_identifiers(&self, collect: &mut F) where F: FnMut((String, Span)), { match self { Pattern::Var { name, location } => { collect((name.to_string(), *location)); } Pattern::List { elements, .. } => { elements.iter().for_each(|e| e.collect_identifiers(collect)); } Pattern::Pair { fst, snd, .. } => { fst.collect_identifiers(collect); snd.collect_identifiers(collect); } Pattern::Tuple { elems, .. } => { elems.iter().for_each(|e| e.collect_identifiers(collect)); } Pattern::Constructor { arguments, .. } => { arguments .iter() .for_each(|arg| arg.value.collect_identifiers(collect)); } Pattern::Int { .. } | Pattern::ByteArray { .. } | Pattern::Discard { .. } | Pattern::Assign { .. } => {} } } } impl TypedPattern { pub fn find_node<'a>(&'a self, byte_index: usize, value: &Rc) -> Option> { if !self.location().contains(byte_index) { return None; } match self { Pattern::Int { .. } | Pattern::Var { .. } | Pattern::Assign { .. } | Pattern::ByteArray { .. } | Pattern::Discard { .. } => Some(Located::Pattern(self, value.clone())), Pattern::List { elements, .. } | Pattern::Tuple { elems: elements, .. } => match &**value { Type::Tuple { elems, .. } => elements .iter() .zip(elems.iter()) .find_map(|(e, t)| e.find_node(byte_index, t)) .or(Some(Located::Pattern(self, value.clone()))), Type::App { module, name, args, .. } if module.is_empty() && name == "List" => elements .iter() // this is the same as above but this uses // cycle to repeat the single type arg for a list // there's probably a cleaner way to re-use the code // from this branch and the above. .zip(args.iter().cycle()) .find_map(|(e, t)| e.find_node(byte_index, t)) .or(Some(Located::Pattern(self, value.clone()))), _ => None, }, Pattern::Pair { fst, snd, .. } => match &**value { Type::Pair { fst: fst_v, snd: snd_v, .. } => [fst, snd] .into_iter() .zip([fst_v, snd_v].iter()) .find_map(|(e, t)| e.find_node(byte_index, t)) .or(Some(Located::Pattern(self, value.clone()))), _ => None, }, Pattern::Constructor { arguments, tipo, .. } => match &**tipo { Type::Fn { args, .. } => arguments .iter() .zip(args.iter()) .find_map(|(e, t)| e.value.find_node(byte_index, t)) .or(Some(Located::Pattern(self, value.clone()))), _ => None, }, } } // TODO: This function definition is weird, see where this is used and how. pub fn tipo(&self, value: &TypedExpr) -> Option> { match self { Pattern::Int { .. } => Some(Type::int()), Pattern::ByteArray { .. } => Some(Type::byte_array()), Pattern::Constructor { tipo, .. } => Some(tipo.clone()), Pattern::Var { .. } | Pattern::Assign { .. } | Pattern::Discard { .. } => { Some(value.tipo()) } Pattern::List { .. } | Pattern::Tuple { .. } | Pattern::Pair { .. } => None, } } } #[derive(Debug, Clone, PartialEq, Eq, Copy, serde::Serialize, serde::Deserialize)] pub enum ByteArrayFormatPreference { HexadecimalString, ArrayOfBytes(Base), Utf8String, } #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub enum CurveType { Bls12_381(Bls12_381PointType), } impl Display for CurveType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { CurveType::Bls12_381(point) => write!(f, ""), } } } impl From<&Curve> for CurveType { fn from(value: &Curve) -> Self { match value { Curve::Bls12_381(point) => CurveType::Bls12_381(point.into()), } } } #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub enum Bls12_381PointType { G1, G2, } impl From<&Bls12_381Point> for Bls12_381PointType { fn from(value: &Bls12_381Point) -> Self { match value { Bls12_381Point::G1(_) => Bls12_381PointType::G1, Bls12_381Point::G2(_) => Bls12_381PointType::G2, } } } impl Display for Bls12_381PointType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Bls12_381PointType::G1 => write!(f, "G1"), Bls12_381PointType::G2 => write!(f, "G2"), } } } #[derive(Debug, Clone, PartialEq, Eq, Copy, serde::Serialize, serde::Deserialize)] pub enum Curve { Bls12_381(Bls12_381Point), } impl Curve { pub fn compress(&self) -> Vec { match self { Curve::Bls12_381(point) => match point { Bls12_381Point::G1(g1) => g1.compress(), Bls12_381Point::G2(g2) => g2.compress(), }, } } pub fn tipo(&self) -> Rc { match self { Curve::Bls12_381(point) => point.tipo(), } } } #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub enum Bls12_381Point { G1(blst::blst_p1), G2(blst::blst_p2), } impl serde::Serialize for Bls12_381Point { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { match *self { Bls12_381Point::G1(ref p1) => { // Assuming `to_bytes` for compression to Vec let bytes = p1.compress(); // Serialize as a tuple with a tag for differentiation serializer.serialize_newtype_variant("Bls12_381Point", 0, "G1", &bytes) } Bls12_381Point::G2(ref p2) => { let bytes = p2.compress(); serializer.serialize_newtype_variant("Bls12_381Point", 1, "G2", &bytes) } } } } impl<'de> serde::Deserialize<'de> for Bls12_381Point { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { enum Field { G1, G2, } impl<'de> serde::Deserialize<'de> for Field { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct FieldVisitor; impl<'de> serde::de::Visitor<'de> for FieldVisitor { type Value = Field; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("`G1` or `G2`") } fn visit_str(self, value: &str) -> Result where E: serde::de::Error, { match value { "G1" => Ok(Field::G1), "G2" => Ok(Field::G2), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } } deserializer.deserialize_identifier(FieldVisitor) } } struct Bls12_381PointVisitor; impl<'de> serde::de::Visitor<'de> for Bls12_381PointVisitor { type Value = Bls12_381Point; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("struct Bls12_381Point") } fn visit_seq(self, mut seq: V) -> Result where V: serde::de::SeqAccess<'de>, { let tag = seq .next_element::()? .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; let bytes = seq .next_element::>()? .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?; match tag { Field::G1 => { let p1 = blst::blst_p1::uncompress(&bytes).map_err(serde::de::Error::custom)?; Ok(Bls12_381Point::G1(p1)) } Field::G2 => { let p2 = blst::blst_p2::uncompress(&bytes).map_err(serde::de::Error::custom)?; Ok(Bls12_381Point::G2(p2)) } } } } const FIELDS: &[&str] = &["G1", "G2"]; deserializer.deserialize_enum("Bls12_381Point", FIELDS, Bls12_381PointVisitor) } } impl Bls12_381Point { pub fn tipo(&self) -> Rc { match self { Bls12_381Point::G1(_) => Type::g1_element(), Bls12_381Point::G2(_) => Type::g2_element(), } } } impl Default for Bls12_381Point { fn default() -> Self { Bls12_381Point::G1(Default::default()) } } #[derive(Debug, Clone, PartialEq)] pub struct AssignmentPattern { pub pattern: UntypedPattern, pub annotation: Option, pub location: Span, } impl AssignmentPattern { pub fn new( pattern: UntypedPattern, annotation: Option, location: Span, ) -> AssignmentPattern { Self { pattern, annotation, location, } } } impl From for Vec1 { fn from(value: AssignmentPattern) -> Self { Vec1::new(value) } } pub type UntypedAssignmentKind = AssignmentKind; pub type TypedAssignmentKind = AssignmentKind<()>; #[derive(Debug, Clone, PartialEq, Eq, Copy, serde::Serialize, serde::Deserialize)] pub enum AssignmentKind { Is, Let { backpassing: T }, Expect { backpassing: T }, } impl From for TypedAssignmentKind { fn from(kind: UntypedAssignmentKind) -> TypedAssignmentKind { match kind { AssignmentKind::Is => AssignmentKind::Is, AssignmentKind::Let { .. } => AssignmentKind::Let { backpassing: () }, AssignmentKind::Expect { .. } => AssignmentKind::Expect { backpassing: () }, } } } impl AssignmentKind { pub fn is_let(&self) -> bool { matches!(self, AssignmentKind::Let { .. }) } pub fn is_expect(&self) -> bool { matches!(self, AssignmentKind::Expect { .. }) } pub fn if_is(&self) -> bool { matches!(self, AssignmentKind::Is) } pub fn location_offset(&self) -> usize { match self { AssignmentKind::Is => 2, AssignmentKind::Let { .. } => 3, AssignmentKind::Expect { .. } => 6, } } } impl AssignmentKind { pub fn is_backpassing(&self) -> bool { match self { Self::Is => unreachable!(), Self::Let { backpassing } | Self::Expect { backpassing } => *backpassing, } } } impl AssignmentKind { pub fn let_() -> Self { AssignmentKind::Let { backpassing: Default::default(), } } pub fn is() -> Self { AssignmentKind::Is } pub fn expect() -> Self { AssignmentKind::Expect { backpassing: Default::default(), } } } pub type MultiPattern = Vec>; pub type UntypedMultiPattern = MultiPattern<(), ()>; pub type TypedMultiPattern = MultiPattern>; #[derive(Debug, Clone, PartialEq)] pub struct UntypedClause { pub location: Span, pub patterns: Vec1>, pub then: UntypedExpr, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct TypedClause { pub location: Span, pub pattern: Pattern>, pub then: TypedExpr, } impl TypedClause { pub fn location(&self) -> Span { Span { start: self.pattern.location().start, end: self.then.location().end, } } pub fn find_node(&self, byte_index: usize, subject_type: &Rc) -> Option> { self.pattern .find_node(byte_index, subject_type) .or_else(|| self.then.find_node(byte_index)) } } pub struct UntypedClauseGuard {} pub type TypedIfBranch = IfBranch)>; pub type UntypedIfBranch = IfBranch; #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct IfBranch { pub condition: Expr, pub body: Expr, pub is: Option, pub location: Span, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct TypedRecordUpdateArg { pub label: String, pub location: Span, pub value: TypedExpr, pub index: usize, } impl TypedRecordUpdateArg { pub fn find_node(&self, byte_index: usize) -> Option> { self.value.find_node(byte_index) } } #[derive(Debug, Clone, PartialEq)] pub struct UntypedRecordUpdateArg { pub label: String, pub location: Span, pub value: UntypedExpr, } #[derive(Debug, Clone, PartialEq)] pub struct RecordUpdateSpread { pub base: Box, pub location: Span, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum TraceKind { Trace, Todo, Error, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Tracing { UserDefined(TraceLevel), CompilerGenerated(TraceLevel), All(TraceLevel), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TraceLevel { Silent, // No traces Compact, // Line numbers only Verbose, // Full verbose traces as provided by the user or the compiler } impl Tracing { pub fn verbose() -> Self { Tracing::All(TraceLevel::Verbose) } pub fn silent() -> Self { Tracing::All(TraceLevel::Silent) } /// Get the tracing level based on the context we're in. pub fn trace_level(&self, is_code_gen: bool) -> TraceLevel { match self { Tracing::UserDefined(lvl) => { if is_code_gen { TraceLevel::Silent } else { *lvl } } Tracing::CompilerGenerated(lvl) => { if is_code_gen { *lvl } else { TraceLevel::Silent } } Tracing::All(lvl) => *lvl, } } } impl Display for TraceLevel { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { match self { TraceLevel::Silent => f.write_str("silent"), TraceLevel::Compact => f.write_str("compact"), TraceLevel::Verbose => f.write_str("verbose"), } } } #[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub struct Span { pub start: usize, pub end: usize, } impl From for miette::SourceSpan { fn from(span: Span) -> Self { Self::new(span.start.into(), span.end - span.start) } } impl Span { pub fn empty() -> Self { use chumsky::Span; Self::new((), 0..0) } pub fn create(i: usize, n: usize) -> Self { use chumsky::Span; Self::new((), i..i + n) } pub fn range(&self) -> Range { use chumsky::Span; self.start()..self.end() } pub fn union(self, other: Self) -> Self { use chumsky::Span; Self { start: self.start().min(other.start()), end: self.end().max(other.end()), } } /// Map the current start and end of the Span to new values. /// /// # Examples /// /// ``` /// use aiken_lang::ast::Span; /// /// let span = Span { start: 0, end: 1 }; /// /// let other = span.map(|start, end| (start + 2, end + 4)); /// /// assert_eq!(other.start, 2); /// assert_eq!(other.end, 5); /// ``` pub fn map (usize, usize)>(&self, f: F) -> Self { let (start, end) = f(self.start, self.end); Self { start, end } } /// Map the current end of the Span to a new value. /// /// # Examples /// /// ``` /// use aiken_lang::ast::Span; /// /// let span = Span { start: 0, end: 1 }; /// /// let other = span.map_end(|end| end + 1); /// /// assert_eq!(other.start, 0); /// assert_eq!(other.end, 2); /// ``` pub fn map_end usize>(&self, f: F) -> Self { Self { start: self.start, end: f(self.end), } } pub fn contains(&self, byte_index: usize) -> bool { byte_index >= self.start && byte_index < self.end } } impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.range()) } } impl chumsky::Span for Span { type Context = (); type Offset = usize; fn new(_context: Self::Context, range: Range) -> Self { assert!(range.start <= range.end); Self { start: range.start, end: range.end, } } fn context(&self) -> Self::Context {} fn start(&self) -> Self::Offset { self.start } fn end(&self) -> Self::Offset { self.end } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum LogicalOpChainKind { And, Or, } impl Display for LogicalOpChainKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { LogicalOpChainKind::And => write!(f, "and"), LogicalOpChainKind::Or => write!(f, "or"), } } } #[derive(Debug, thiserror::Error, Diagnostic)] pub enum Error { #[error( "I realized the module '{}' contains the keyword '{}', which is forbidden.\n", name.if_supports_color(Stdout, |s| s.purple()), keyword.if_supports_color(Stdout, |s| s.purple()) )] #[diagnostic(url("https://aiken-lang.org/language-tour/modules"))] #[diagnostic(code("illegal::module_name"))] #[diagnostic(help(r#"You cannot use keywords as part of a module path name. As a quick reminder, here's a list of all the keywords (and thus, of invalid module path names): as, expect, check, const, else, fn, if, is, let, opaque, pub, test, todo, trace, type, use, when"#))] KeywordInModuleName { name: String, keyword: String }, #[error("I realized you used '{}' as a module name, which is reserved (and not available).\n", name.if_supports_color(Stdout, |s| s.purple()) )] #[diagnostic(code("illegal::module_name"))] #[diagnostic(help(r#"Some module names are reserved for internal use. This the case of: - aiken: where the prelude is located; - aiken/builtin: where I store low-level Plutus builtins. Note that 'aiken' is also imported by default; but you can refer to it explicitly to disambiguate with a local value that would clash with one from that module."# ))] ReservedModuleName { name: String }, }