//! Datastructures representing the units of macro execution //! //! These structures are produced by the pipeline, processed by the macro //! executor, and then converted to other usable formats. use std::fmt; use std::hash::Hash; use std::rc::Rc; use hashbrown::HashSet; use intern_all::Tok; use itertools::Itertools; use ordered_float::NotNan; use crate::foreign::atom::AtomGenerator; #[allow(unused)] // for doc use crate::interpreter::nort; use crate::location::SourceRange; use crate::name::{Sym, VName, VPath}; use crate::parse::numeric::print_nat16; /// A [Clause] with associated metadata #[derive(Clone, Debug)] pub struct Expr { /// The actual value pub value: Clause, /// Information about the code that produced this value pub range: SourceRange, } impl Expr { /// Process all names with the given mapper. /// Return a new object if anything was processed #[must_use] pub fn map_names(&self, pred: &mut impl FnMut(Sym) -> Option) -> Option { (self.value.map_names(pred)).map(|value| Self { value, range: self.range.clone() }) } /// Visit all expressions in the tree. The search can be exited early by /// returning [Some] /// /// See also [crate::interpreter::nort::Expr::search_all] pub fn search_all(&self, f: &mut impl FnMut(&Self) -> Option) -> Option { f(self).or_else(|| self.value.search_all(f)) } } /// Visit all expression sequences including this sequence itself. pub fn search_all_slcs(this: &[Expr], f: &mut impl FnMut(&[Expr]) -> Option) -> Option { f(this).or_else(|| this.iter().find_map(|expr| expr.value.search_all_slcs(f))) } impl Expr { /// Add the specified prefix to every Name #[must_use] pub fn prefix(&self, prefix: &[Tok], except: &impl Fn(Tok) -> bool) -> Self { Self { value: self.value.prefix(prefix, except), range: self.range.clone() } } } impl fmt::Display for Expr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.value.fmt(f) } } /// Various types of placeholders #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum PHClass { /// Matches multiple tokens, lambdas or parenthesized groups Vec { /// If true, must match at least one clause nonzero: bool, /// Greediness in the allocation of tokens prio: usize, }, /// Matches exactly one token, lambda or parenthesized group Scalar, /// Matches exactly one name Name, } /// Properties of a placeholder that matches unknown tokens in macros #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Placeholder { /// Identifier to pair placeholders in the pattern and template pub name: Tok, /// The nature of the token set matched by this placeholder pub class: PHClass, } impl fmt::Display for Placeholder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let name = &self.name; match self.class { PHClass::Scalar => write!(f, "${name}"), PHClass::Name => write!(f, "$_{name}"), PHClass::Vec { nonzero, prio } => { if nonzero { write!(f, "...") } else { write!(f, "..") }?; write!(f, "${name}:{prio}") }, } } } /// Different types of brackets supported by Orchid #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub enum PType { /// () Par, /// [] Sqr, /// {} Curl, } impl PType { /// Left paren character for this paren type pub fn l(self) -> char { match self { PType::Curl => '{', PType::Par => '(', PType::Sqr => '[', } } /// Right paren character for this paren type pub fn r(self) -> char { match self { PType::Curl => '}', PType::Par => ')', PType::Sqr => ']', } } } /// An S-expression as read from a source file #[derive(Debug, Clone)] pub enum Clause { /// An opaque non-callable value, eg. a file handle Atom(AtomGenerator), /// A c-style name or an operator, eg. `+`, `i`, `foo::bar` Name(Sym), /// A parenthesized expression /// eg. `(print out "hello")`, `[1, 2, 3]`, `{Some(t) => t}` S(PType, Rc>), /// A function expression, eg. `\x. x + 1` Lambda(Rc>, Rc>), /// A placeholder for macros, eg. `$name`, `...$body`, `...$lhs:1` Placeh(Placeholder), } impl Clause { /// Extract the expressions from an auto, lambda or S #[must_use] pub fn body(&self) -> Option>> { match self { Self::Lambda(_, body) | Self::S(_, body) => Some(body.clone()), _ => None, } } /// Convert with identical meaning #[must_use] pub fn into_expr(self, range: SourceRange) -> Expr { if let Self::S(PType::Par, body) = &self { if let [wrapped] = &body[..] { return wrapped.clone(); } } Expr { value: self, range } } /// Convert with identical meaning #[must_use] pub fn from_exprs(exprs: &[Expr]) -> Option { match exprs { [] => None, [only] => Some(only.value.clone()), _ => Some(Self::S(PType::Par, Rc::new(exprs.to_vec()))), } } /// Convert with identical meaning #[must_use] pub fn from_exprv(exprv: &Rc>) -> Option { if exprv.len() < 2 { Self::from_exprs(exprv) } else { Some(Self::S(PType::Par, exprv.clone())) } } /// Collect all names that appear in this expression. /// NOTICE: this isn't the total set of unbound names, it's mostly useful to /// make weak statements for optimization. #[must_use] pub fn collect_names(&self) -> HashSet { if let Self::Name(n) = self { return HashSet::from([n.clone()]); } let mut glossary = HashSet::new(); let result = self.search_all(&mut |e| { if let Clause::Name(n) = &e.value { glossary.insert(n.clone()); } None::<()> }); assert!(result.is_none(), "Callback never returns Some"); glossary } /// Process all names with the given mapper. /// Return a new object if anything was processed #[must_use] pub fn map_names(&self, pred: &mut impl FnMut(Sym) -> Option) -> Option { match self { Clause::Atom(_) | Clause::Placeh(_) => None, Clause::Name(name) => pred(name.clone()).map(Clause::Name), Clause::S(c, body) => { let mut any_some = false; let new_body = body .iter() .map(|e| { let val = e.map_names(pred); any_some |= val.is_some(); val.unwrap_or_else(|| e.clone()) }) .collect(); if any_some { Some(Clause::S(*c, Rc::new(new_body))) } else { None } }, Clause::Lambda(arg, body) => { let mut any_some = false; let new_arg = (arg.iter()) .map(|e| { let val = e.map_names(pred); any_some |= val.is_some(); val.unwrap_or_else(|| e.clone()) }) .collect(); let new_body = (body.iter()) .map(|e| { let val = e.map_names(pred); any_some |= val.is_some(); val.unwrap_or_else(|| e.clone()) }) .collect(); if any_some { Some(Clause::Lambda(Rc::new(new_arg), Rc::new(new_body))) } else { None } }, } } /// Pair of [Expr::search_all] pub fn search_all(&self, f: &mut impl FnMut(&Expr) -> Option) -> Option { match self { Clause::Lambda(arg, body) => arg.iter().chain(body.iter()).find_map(|expr| expr.search_all(f)), Clause::Name(_) | Clause::Atom(_) | Clause::Placeh(_) => None, Clause::S(_, body) => body.iter().find_map(|expr| expr.search_all(f)), } } /// Visit all expression sequences. Most useful when looking for some pattern pub fn search_all_slcs(&self, f: &mut impl FnMut(&[Expr]) -> Option) -> Option { match self { Clause::Lambda(arg, body) => search_all_slcs(arg, f).or_else(|| search_all_slcs(body, f)), Clause::Name(_) | Clause::Atom(_) | Clause::Placeh(_) => None, Clause::S(_, body) => search_all_slcs(body, f), } } /// Generate a parenthesized expression sequence pub fn s(delimiter: char, body: impl IntoIterator, range: SourceRange) -> Self { let ptype = match delimiter { '(' => PType::Par, '[' => PType::Sqr, '{' => PType::Curl, _ => panic!("not an opening paren"), }; let body = body.into_iter().map(|it| it.into_expr(range.clone())).collect(); Self::S(ptype, Rc::new(body)) } } impl Clause { /// Add the specified prefix to every Name #[must_use] pub fn prefix(&self, prefix: &[Tok], except: &impl Fn(Tok) -> bool) -> Self { self .map_names(&mut |name| match except(name[0].clone()) { true => None, false => { let prefixed = prefix.iter().cloned().chain(name.iter()).collect::>(); Some(Sym::from_tok(name.tok().interner().i(&prefixed)).unwrap()) }, }) .unwrap_or_else(|| self.clone()) } } impl fmt::Display for Clause { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Atom(a) => write!(f, "{a:?}"), Self::Name(name) => write!(f, "{}", name), Self::S(t, items) => { let body = items.iter().join(" "); write!(f, "{}{body}{}", t.l(), t.r()) }, Self::Lambda(arg, body) => { let args = arg.iter().join(" "); let bodys = body.iter().join(" "); write!(f, "\\{args}.{bodys}") }, Self::Placeh(ph) => ph.fmt(f), } } } /// A substitution rule as loaded from source #[derive(Debug, Clone)] pub struct Rule { /// Expressions on the left side of the arrow pub pattern: Vec, /// Priority number written inside the arrow pub prio: NotNan, /// Expressions on the right side of the arrow pub template: Vec, } impl fmt::Display for Rule { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "rule {} ={}=> {}", self.pattern.iter().join(" "), print_nat16(self.prio), self.template.iter().join(" ") ) } } /// A named constant #[derive(Debug, Clone)] pub struct Constant { /// Used to reference the constant pub name: Tok, /// The constant value inserted where the name is found pub value: Expr, } impl fmt::Display for Constant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "const {} := {}", *self.name, self.value) } } /// An import pointing at another module, either specifying the symbol to be /// imported or importing all available symbols with a globstar (*) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Import { /// Import path, a sequence of module names. Can either start with /// /// - `self` to reference the current module /// - any number of `super` to reference the parent module of the implied /// `self` /// - a root name pub path: VPath, /// If name is None, this is a wildcard import pub name: Option>, /// Location of the final name segment, which uniquely identifies this name pub range: SourceRange, } impl Import { /// Constructor pub fn new( path: impl IntoIterator>, name: Option>, range: SourceRange, ) -> Self { let path = VPath(path.into_iter().collect()); assert!(name.is_some() || !path.0.is_empty(), "import * not allowed"); Self { range, name, path } } /// Get the preload target space for this import - the prefix below /// which all files should be included in the compilation /// /// Returns the path if this is a glob import, or the path plus the /// name if this is a specific import #[must_use] pub fn nonglob_path(&self) -> VName { VName::new(self.path.0.iter().chain(&self.name).cloned()) .expect("Everything import (`import *`) not allowed") } } impl fmt::Display for Import { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.name { None => write!(f, "{}::*", self.path), Some(n) => write!(f, "{}::{}", self.path, n), } } } /// A namespace block #[derive(Debug, Clone)] pub struct ModuleBlock { /// Name prefixed to all names in the block pub name: Tok, /// Prefixed entries pub body: Vec, } impl fmt::Display for ModuleBlock { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let bodys = self.body.iter().map(|e| e.to_string()).join("\n"); write!(f, "module {} {{\n{}\n}}", self.name, bodys) } } /// see [Member] #[derive(Debug, Clone)] pub enum MemberKind { /// A substitution rule. Rules apply even when they're not in scope, if the /// absolute names are present eg. because they're produced by other rules Rule(Rule), /// A constant (or function) associated with a name Constant(Constant), /// A prefixed set of other entries Module(ModuleBlock), } impl MemberKind { /// Convert to [SourceLine] pub fn into_line(self, exported: bool, range: SourceRange) -> SourceLine { SourceLineKind::Member(Member { exported, kind: self }).wrap(range) } } impl fmt::Display for MemberKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Constant(c) => c.fmt(f), Self::Module(m) => m.fmt(f), Self::Rule(r) => r.fmt(f), } } } /// Things that may be prefixed with an export /// see [MemberKind] #[derive(Debug, Clone)] pub struct Member { /// Various members pub kind: MemberKind, /// Whether this member is exported or not pub exported: bool, } impl fmt::Display for Member { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self { exported: true, kind } => write!(f, "export {kind}"), Self { exported: false, kind } => write!(f, "{kind}"), } } } /// See [SourceLine] #[derive(Debug, Clone)] pub enum SourceLineKind { /// Imports one or all names in a module Import(Vec), /// Comments are kept here in case dev tooling wants to parse documentation Comment(String), /// An element with visibility information Member(Member), /// A list of tokens exported explicitly. This can also create new exported /// tokens that the local module doesn't actually define a role for Export(Vec<(Tok, SourceRange)>), } impl SourceLineKind { /// Wrap with no location pub fn wrap(self, range: SourceRange) -> SourceLine { SourceLine { kind: self, range } } } impl fmt::Display for SourceLineKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Comment(s) => write!(f, "--[{s}]--"), Self::Export(s) => { write!(f, "export ::({})", s.iter().map(|t| &**t.0).join(", ")) }, Self::Member(member) => write!(f, "{member}"), Self::Import(i) => { write!(f, "import ({})", i.iter().map(|i| i.to_string()).join(", ")) }, } } } /// Anything the parser might encounter in a file. See [SourceLineKind] #[derive(Debug, Clone)] pub struct SourceLine { /// What we encountered pub kind: SourceLineKind, /// Where we encountered it. pub range: SourceRange, } impl fmt::Display for SourceLine { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.kind.fmt(f) } }