use std::{fmt, ops::Range, sync::Arc}; use internment::Intern; use crate::{ builtins::{self, bool}, expr::{TypedExpr, UntypedExpr}, tipo::{fields::FieldMap, PatternConstructor, Type, TypeInfo, ValueConstructor}, }; pub const ASSERT_VARIABLE: &str = "_try"; pub const CAPTURE_VARIABLE: &str = "_capture"; pub const PIPE_VARIABLE: &str = "_pipe"; pub const TRY_VARIABLE: &str = "_try"; pub type TypedModule = Module; pub type UntypedModule = Module<(), UntypedDefinition>; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ModuleKind { Lib, Script, } impl ModuleKind { pub fn is_script(&self) -> bool { matches!(self, ModuleKind::Script) } pub fn is_lib(&self) -> bool { matches!(self, ModuleKind::Lib) } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Module { pub name: String, pub docs: Vec, pub type_info: Info, pub definitions: Vec, pub kind: ModuleKind, } impl UntypedModule { pub fn dependencies(&self) -> Vec<(String, Span)> { self.definitions() .flat_map(|def| { if let Definition::Use { location, module, .. } = def { Some((module.join("/"), *location)) } else { None } }) .collect() } pub fn definitions(&self) -> impl Iterator { self.definitions.iter() } pub fn into_definitions(self) -> impl Iterator { self.definitions.into_iter() } } pub type TypedDefinition = Definition, TypedExpr, String, String>; pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>; #[derive(Debug, Clone, PartialEq)] pub enum Definition { Fn { arguments: Vec>, body: Expr, doc: Option, location: Span, name: String, public: bool, return_annotation: Option, return_type: T, }, TypeAlias { alias: String, annotation: Annotation, doc: Option, location: Span, parameters: Vec, public: bool, tipo: T, }, DataType { constructors: Vec>, doc: Option, location: Span, name: String, opaque: bool, parameters: Vec, public: bool, typed_parameters: Vec, }, Use { as_name: Option, location: Span, module: Vec, package: PackageName, unqualified: Vec, }, ModuleConstant { doc: Option, location: Span, public: bool, name: String, annotation: Option, value: Box>, tipo: T, }, } #[derive(Debug, PartialEq, Eq, Clone)] pub struct DefinitionLocation<'module> { pub module: Option<&'module str>, pub span: Span, } pub type TypedConstant = Constant, String>; pub type UntypedConstant = Constant<(), ()>; #[derive(Debug, Clone, PartialEq)] pub enum Constant { Int { location: Span, value: String, }, String { location: Span, value: String, }, List { location: Span, elements: Vec, tipo: T, }, Record { location: Span, module: Option, name: String, args: Vec>, tag: RecordTag, tipo: T, field_map: Option, }, ByteArray { location: Span, bytes: Vec, }, Var { location: Span, module: Option, name: String, constructor: Option>, tipo: T, }, } impl TypedConstant { pub fn tipo(&self) -> Arc { match self { Constant::Int { .. } => builtins::int(), Constant::String { .. } => builtins::string(), Constant::ByteArray { .. } => builtins::byte_array(), Constant::List { tipo, .. } | Constant::Record { tipo, .. } | Constant::Var { tipo, .. } => tipo.clone(), } } } impl Constant { pub fn location(&self) -> Span { match self { Constant::Int { location, .. } | Constant::List { location, .. } | Constant::String { location, .. } | Constant::Record { location, .. } | Constant::ByteArray { location, .. } | Constant::Var { location, .. } => *location, } } pub fn is_simple(&self) -> bool { matches!( self, Self::Int { .. } | Self::ByteArray { .. } | Self::String { .. } ) } } pub type TypedCallArg = CallArg; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CallArg { pub label: Option, pub location: Span, pub value: A, } #[derive(Debug, Clone, PartialEq)] pub struct RecordConstructor { pub location: Span, pub name: String, pub arguments: Vec>, pub documentation: Option, pub sugar: bool, } #[derive(Debug, Clone, PartialEq)] pub struct RecordConstructorArg { pub label: Option, // ast pub annotation: Annotation, pub location: Span, pub tipo: T, pub doc: Option, } pub type TypedArg = Arg>; pub type UntypedArg = Arg<()>; #[derive(Debug, Clone, PartialEq)] pub struct Arg { pub arg_name: ArgName, pub location: Span, pub annotation: 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, } } pub fn get_variable_name(&self) -> Option<&str> { self.arg_name.get_variable_name() } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum ArgName { Discard { name: String, location: Span, }, LabeledDiscard { label: String, name: String, location: Span, }, Named { name: String, location: Span, }, NamedLabeled { name: String, label: String, location: Span, }, } impl ArgName { pub fn get_variable_name(&self) -> Option<&str> { match self { ArgName::Discard { .. } | ArgName::LabeledDiscard { .. } => None, ArgName::NamedLabeled { name, .. } | ArgName::Named { name, .. } => Some(name), } } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct UnqualifiedImport { pub location: Span, pub name: String, pub as_name: Option, pub layer: Layer, } impl UnqualifiedImport { pub fn variable_name(&self) -> &str { self.as_name.as_deref().unwrap_or(&self.name) } pub fn is_value(&self) -> bool { self.layer.is_value() } } // TypeAst #[derive(Debug, Clone, PartialEq)] 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, }, } impl Annotation { pub fn location(&self) -> Span { match self { Annotation::Fn { location, .. } | Annotation::Var { location, .. } | Annotation::Hole { location, .. } | Annotation::Constructor { location, .. } => *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::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, PartialEq, Eq)] pub enum Layer { Value, Type, } impl Default for Layer { fn default() -> Self { Layer::Value } } impl Layer { /// Returns `true` if the layer is [`Value`]. pub fn is_value(&self) -> bool { matches!(self, Self::Value) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum BinOp { // Boolean logic And, Or, // Equality Eq, NotEq, // Order comparison LtInt, LtEqInt, GtEqInt, GtInt, // Maths AddInt, SubInt, MultInt, DivInt, ModInt, } pub type UntypedPattern = Pattern<(), ()>; pub type TypedPattern = Pattern>; #[derive(Debug, Clone, PartialEq)] pub enum Pattern { Int { location: Span, value: String, }, String { location: Span, value: String, }, /// The creation of a variable. /// e.g. `assert [this_is_a_var, .._] = x` Var { location: Span, name: String, }, /// A reference to a variable in a bit string. This is always a variable /// being used rather than a new variable being assigned. VarUsage { location: Span, name: String, tipo: Type, }, /// A name given to a sub-pattern using the `as` keyword. /// e.g. `assert #(1, [_, _] as the_list) = x` 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 { 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::VarUsage { location, .. } | Pattern::List { location, .. } | Pattern::Discard { location, .. } | Pattern::String { location, .. } // | Pattern::Tuple { location, .. } // | Pattern::Concatenate { 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 { .. }) } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum AssignmentKind { Let, Assert, } pub type MultiPattern = Vec>; pub type UntypedMultiPattern = MultiPattern<(), ()>; pub type TypedMultiPattern = MultiPattern>; pub type TypedClause = Clause, String>; pub type UntypedClause = Clause; #[derive(Debug, Clone, PartialEq)] pub struct Clause { pub location: Span, pub pattern: MultiPattern, pub alternative_patterns: Vec>, pub guard: Option>, pub then: Expr, } impl TypedClause { pub fn location(&self) -> Span { Span { src: SrcId::empty(), start: self .pattern .get(0) .map(|p| p.location().start) .unwrap_or_default(), end: self.then.location().end, } } } pub type UntypedClauseGuard = ClauseGuard<(), ()>; pub type TypedClauseGuard = ClauseGuard, String>; #[derive(Debug, Clone, PartialEq)] pub enum ClauseGuard { 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, }, // TupleIndex { // location: Span, // index: u64, // tipo: Type, // tuple: Box, // }, Constant(Constant), } impl ClauseGuard { pub fn location(&self) -> Span { match self { ClauseGuard::Constant(constant) => constant.location(), ClauseGuard::Or { location, .. } | ClauseGuard::And { location, .. } | ClauseGuard::Var { location, .. } // | ClauseGuard::TupleIndex { location, .. } | ClauseGuard::Equals { location, .. } | ClauseGuard::NotEquals { location, .. } | ClauseGuard::GtInt { location, .. } | ClauseGuard::GtEqInt { location, .. } | ClauseGuard::LtInt { location, .. } | ClauseGuard::LtEqInt { location, .. } => *location, } } } impl TypedClauseGuard { pub fn tipo(&self) -> Arc { match self { ClauseGuard::Var { tipo, .. } => tipo.clone(), // ClauseGuard::TupleIndex { type_, .. } => type_.clone(), ClauseGuard::Constant(constant) => constant.tipo(), 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)] pub struct IfBranch { pub condition: Expr, pub body: Expr, pub location: Span, } #[derive(Debug, Clone)] pub struct TypedRecordUpdateArg { pub label: String, pub location: Span, pub value: TypedExpr, pub index: usize, } #[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 TodoKind { Keyword, EmptyFunction, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct SrcId(Intern>); impl SrcId { pub fn empty() -> Self { SrcId(Intern::new(Vec::new())) } } #[derive(Copy, Clone, PartialEq, Eq)] pub struct Span { pub src: SrcId, 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(SrcId::empty(), 0..0) } pub fn src(&self) -> SrcId { self.src } pub fn range(&self) -> Range { use chumsky::Span; self.start()..self.end() } pub fn union(self, other: Self) -> Self { use chumsky::Span; assert_eq!( self.src, other.src, "attempted to union spans with different sources" ); Self { start: self.start().min(other.start()), end: self.end().max(other.end()), ..self } } } impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}:{:?}", self.src, self.range()) } } impl chumsky::Span for Span { type Context = SrcId; type Offset = usize; fn new(context: Self::Context, range: Range) -> Self { assert!(range.start <= range.end); Self { src: context, start: range.start, end: range.end, } } fn context(&self) -> Self::Context { self.src } fn start(&self) -> Self::Offset { self.start } fn end(&self) -> Self::Offset { self.end } }