use crate::{ builtins::{self, bool, g1_element, g2_element}, expr::{TypedExpr, UntypedExpr}, line_numbers::LineNumbers, parser::token::{Base, Token}, tipo::{PatternConstructor, Type, TypeInfo}, }; use indexmap::IndexMap; use miette::Diagnostic; use owo_colors::{OwoColorize, Stream::Stdout}; use std::{ fmt::{self, Display}, ops::Range, rc::Rc, }; use uplc::machine::runtime::Compressable; use vec1::Vec1; pub const BACKPASS_VARIABLE: &str = "_backpass"; pub const CAPTURE_VARIABLE: &str = "_capture"; pub const PIPE_VARIABLE: &str = "_pipe"; pub type TypedModule = Module; pub type UntypedModule = Module<(), UntypedDefinition>; #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub enum ModuleKind { Lib, Validator, } impl ModuleKind { pub fn is_validator(&self) -> bool { matches!(self, ModuleKind::Validator) } pub fn is_lib(&self) -> bool { matches!(self, ModuleKind::Lib) } } #[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) -> Vec<(String, Span)> { self.definitions() .flat_map(|def| { if let Definition::Use(Use { location, module, .. }) = def { Some((module.join("/"), *location)) } else { None } }) .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, 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(); if let Some((k, v)) = v.into_function_definition(module_name, |f, _| Some(f)) { functions.insert(k, v); } if let Some((k, v)) = v.into_function_definition(module_name, |_, f| f) { functions.insert(k, v); } } Definition::TypeAlias(_) | Definition::ModuleConstant(_) | 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>; pub type TypedTest = Function, TypedExpr, TypedArgVia>; pub type UntypedTest = Function<(), UntypedExpr, UntypedArgVia>; #[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 can_error: bool, } 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, can_error: f.can_error, 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, can_error: f.can_error, 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 TypedDataType = DataType>; impl TypedDataType { pub fn data() -> Self { DataType { constructors: vec![], doc: None, location: Span::empty(), name: "Data".to_string(), opaque: false, parameters: vec![], public: true, typed_parameters: vec![], } } pub fn bool() -> Self { DataType { constructors: vec![ RecordConstructor { location: Span::empty(), name: "False".to_string(), arguments: vec![], doc: None, sugar: false, }, RecordConstructor { location: Span::empty(), name: "True".to_string(), arguments: vec![], doc: None, sugar: false, }, ], doc: None, location: Span::empty(), name: "Bool".to_string(), opaque: false, parameters: vec![], public: true, typed_parameters: vec![], } } pub fn prng() -> Self { DataType { constructors: vec![ RecordConstructor { location: Span::empty(), name: "Seeded".to_string(), arguments: vec![], doc: None, sugar: false, }, RecordConstructor { location: Span::empty(), name: "Replayed".to_string(), arguments: vec![], doc: None, sugar: false, }, ], doc: None, location: Span::empty(), name: "PRNG".to_string(), opaque: false, parameters: vec![], public: true, typed_parameters: vec![], } } pub fn ordering() -> Self { DataType { constructors: vec![ RecordConstructor { location: Span::empty(), name: "Less".to_string(), arguments: vec![], doc: None, sugar: false, }, RecordConstructor { location: Span::empty(), name: "Equal".to_string(), arguments: vec![], doc: None, sugar: false, }, RecordConstructor { location: Span::empty(), name: "Greater".to_string(), arguments: vec![], doc: None, sugar: false, }, ], doc: None, location: Span::empty(), name: "Ordering".to_string(), opaque: false, parameters: vec![], public: true, typed_parameters: vec![], } } pub fn option(tipo: Rc) -> Self { DataType { constructors: vec![ RecordConstructor { location: Span::empty(), name: "Some".to_string(), arguments: vec![RecordConstructorArg { label: None, annotation: Annotation::Var { location: Span::empty(), name: "a".to_string(), }, location: Span::empty(), tipo: tipo.clone(), doc: None, }], doc: None, sugar: false, }, RecordConstructor { location: Span::empty(), name: "None".to_string(), arguments: vec![], doc: None, sugar: false, }, ], doc: None, location: Span::empty(), name: "Option".to_string(), opaque: false, parameters: vec!["a".to_string()], public: true, typed_parameters: vec![tipo], } } } pub type UntypedDataType = DataType<()>; #[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: Box, pub tipo: T, } pub type TypedValidator = Validator, TypedExpr>; pub type UntypedValidator = Validator<(), UntypedExpr>; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct Validator { pub doc: Option, pub end_position: usize, pub fun: Function>, pub other_fun: Option>>, pub location: Span, pub params: Vec>, } impl TypedValidator { pub fn into_function_definition<'a, F>( &'a self, module_name: &str, select: F, ) -> Option<(FunctionAccessKey, TypedFunction)> where F: Fn(&'a TypedFunction, Option<&'a TypedFunction>) -> Option<&'a TypedFunction> + 'a, { match select(&self.fun, self.other_fun.as_ref()) { None => None, Some(fun) => { let mut fun = fun.clone(); fun.arguments = self .params .clone() .into_iter() .chain(fun.arguments) .collect(); Some(( FunctionAccessKey { module_name: module_name.to_string(), function_name: fun.name.clone(), }, fun, )) } } } } pub type TypedDefinition = Definition, TypedExpr, String>; pub type UntypedDefinition = Definition<(), 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. match self { Definition::Validator(Validator { fun: Function { body, .. }, other_fun: Some(Function { body: other_body, .. }), .. }) => { if let Some(located) = body.find_node(byte_index) { return Some(located); } if let Some(located) = other_body.find_node(byte_index) { return Some(located); } } Definition::Fn(Function { body, .. }) | Definition::Test(Function { body, .. }) | Definition::Validator(Validator { fun: Function { body, .. }, .. }) => { if let Some(located) = body.find_node(byte_index) { return Some(located); } } _ => (), } if self.location().contains(byte_index) { Some(Located::Definition(self)) } else { None } } } #[derive(Debug, Clone, PartialEq)] pub enum Located<'a> { Expression(&'a TypedExpr), Pattern(&'a TypedPattern, Rc), Definition(&'a TypedDefinition), } impl<'a> Located<'a> { pub fn definition_location(&self) -> Option> { match self { Self::Expression(expression) => expression.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(_, _) => None, Self::Definition(definition) => Some(DefinitionLocation { module: None, span: definition.location(), }), } } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct DefinitionLocation<'module> { pub module: Option<&'module str>, pub span: Span, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub enum Constant { Int { location: Span, value: String, base: Base, }, String { location: Span, value: String, }, ByteArray { location: Span, bytes: Vec, preferred_format: ByteArrayFormatPreference, }, CurvePoint { location: Span, point: Box, preferred_format: ByteArrayFormatPreference, }, } impl Constant { pub fn tipo(&self) -> Rc { match self { Constant::Int { .. } => builtins::int(), Constant::String { .. } => builtins::string(), Constant::ByteArray { .. } => builtins::byte_array(), Constant::CurvePoint { point, .. } => match point.as_ref() { Curve::Bls12_381(Bls12_381Point::G1(_)) => builtins::g1_element(), Curve::Bls12_381(Bls12_381Point::G2(_)) => builtins::g2_element(), }, } } pub fn location(&self) -> Span { match self { Constant::Int { location, .. } | Constant::String { location, .. } | Constant::ByteArray { location, .. } | Constant::CurvePoint { location, .. } => *location, } } } 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) } } #[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 { pub fn put_doc(&mut self, new_doc: String) { self.doc = Some(new_doc); } } #[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); } } pub type TypedArg = Arg>; pub type UntypedArg = Arg<()>; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct Arg { pub arg_name: ArgName, pub location: Span, pub annotation: Option, pub doc: Option, pub tipo: T, } impl Arg { pub fn set_type(self, tipo: B) -> Arg { Arg { tipo, arg_name: self.arg_name, location: self.location, annotation: self.annotation, doc: self.doc, } } pub fn get_variable_name(&self) -> Option<&str> { self.arg_name.get_variable_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 put_doc(&mut self, new_doc: String) { self.doc = Some(new_doc); } } pub type TypedArgVia = ArgVia, TypedExpr>; pub type UntypedArgVia = ArgVia<(), UntypedExpr>; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct ArgVia { pub arg_name: ArgName, pub location: Span, pub via: Expr, pub tipo: T, pub annotation: Option, } impl From> for Arg { fn from(arg: ArgVia) -> Arg { Arg { arg_name: arg.arg_name, location: arg.location, tipo: arg.tipo, annotation: None, doc: None, } } } #[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, is_validator_param: bool, }, } impl ArgName { pub fn get_variable_name(&self) -> Option<&str> { match self { ArgName::Discarded { .. } => None, ArgName::Named { name, .. } => Some(name), } } pub fn get_label(&self) -> String { match self { ArgName::Discarded { label, .. } => label.to_string(), 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, }, } impl Annotation { pub fn location(&self) -> Span { match self { Annotation::Fn { location, .. } | Annotation::Tuple { location, .. } | Annotation::Var { location, .. } | Annotation::Hole { location, .. } | Annotation::Constructor { 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 data(location: Span) -> Self { Annotation::Constructor { name: "Data".to_string(), module: None, arguments: vec![], 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, }, } } } #[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>; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub enum Pattern { Int { location: Span, value: String, base: Base, }, /// 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, with_spread: bool, tipo: Type, }, 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::Constructor { location, .. } => *location, } } /// 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 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::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::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, }, } } pub fn tipo(&self, value: &TypedExpr) -> Option> { match self { Pattern::Int { .. } => Some(builtins::int()), Pattern::Constructor { tipo, .. } => Some(tipo.clone()), Pattern::Var { .. } | Pattern::Assign { .. } | Pattern::Discard { .. } => { Some(value.tipo()) } Pattern::List { .. } | Pattern::Tuple { .. } => 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(_) => g1_element(), Bls12_381Point::G2(_) => 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, } impl AssignmentPattern { pub fn new(pattern: UntypedPattern, annotation: Option) -> AssignmentPattern { Self { pattern, annotation, } } } 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 { Let { backpassing: T }, Expect { backpassing: T }, } impl From for TypedAssignmentKind { fn from(kind: UntypedAssignmentKind) -> TypedAssignmentKind { match kind { 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 location_offset(&self) -> usize { match self { AssignmentKind::Let { .. } => 3, AssignmentKind::Expect { .. } => 6, } } } impl AssignmentKind { pub fn is_backpassing(&self) -> bool { match self { Self::Let { backpassing } | Self::Expect { backpassing } => *backpassing, } } } impl AssignmentKind { pub fn let_() -> Self { AssignmentKind::Let { backpassing: Default::default(), } } 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 guard: Option>, pub then: UntypedExpr, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct TypedClause { pub location: Span, pub pattern: Pattern>, pub guard: Option>>, 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 type UntypedClauseGuard = ClauseGuard<()>; pub type TypedClauseGuard = ClauseGuard>; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub enum ClauseGuard { Not { location: Span, value: Box, }, Equals { location: Span, left: Box, right: Box, }, NotEquals { location: Span, left: Box, right: Box, }, GtInt { location: Span, left: Box, right: Box, }, GtEqInt { location: Span, left: Box, right: Box, }, LtInt { location: Span, left: Box, right: Box, }, LtEqInt { location: Span, left: Box, right: Box, }, Or { location: Span, left: Box, right: Box, }, And { location: Span, left: Box, right: Box, }, Var { location: Span, tipo: Type, name: String, }, Constant(Constant), } impl ClauseGuard { pub fn location(&self) -> Span { match self { ClauseGuard::Constant(constant) => constant.location(), ClauseGuard::Not { location, .. } | ClauseGuard::Or { location, .. } | ClauseGuard::And { location, .. } | ClauseGuard::Var { location, .. } | ClauseGuard::Equals { location, .. } | ClauseGuard::NotEquals { location, .. } | ClauseGuard::GtInt { location, .. } | ClauseGuard::GtEqInt { location, .. } | ClauseGuard::LtInt { location, .. } | ClauseGuard::LtEqInt { location, .. } => *location, } } pub fn precedence(&self) -> u8 { // Ensure that this matches the other precedence function for guards match self { ClauseGuard::Not { .. } => 1, ClauseGuard::Or { .. } => 2, ClauseGuard::And { .. } => 3, ClauseGuard::Equals { .. } | ClauseGuard::NotEquals { .. } => 4, ClauseGuard::GtInt { .. } | ClauseGuard::GtEqInt { .. } | ClauseGuard::LtInt { .. } | ClauseGuard::LtEqInt { .. } => 5, ClauseGuard::Constant(_) | ClauseGuard::Var { .. } => 6, } } } impl TypedClauseGuard { pub fn tipo(&self) -> Rc { match self { ClauseGuard::Var { tipo, .. } => tipo.clone(), ClauseGuard::Constant(constant) => constant.tipo(), ClauseGuard::Not { .. } | ClauseGuard::Or { .. } | ClauseGuard::And { .. } | ClauseGuard::Equals { .. } | ClauseGuard::NotEquals { .. } | ClauseGuard::GtInt { .. } | ClauseGuard::GtEqInt { .. } | ClauseGuard::LtInt { .. } | ClauseGuard::LtEqInt { .. } => bool(), } } } 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 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 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).into()) } } 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 }, }