formality_core/
lib.rs

1//! `formality-core` contains core definitions that can be used for
2//! languages that are not Rust. It is intended to play a role similar
3//! to
4//!
5
6#![allow(type_alias_bounds)]
7
8// Re-export things from dependencies to avoid everybody repeating same set
9// in their Cargo.toml.
10pub use anyhow::bail;
11pub use contracts::requires;
12pub use tracing::debug;
13pub use tracing::instrument;
14pub use tracing::trace;
15
16// Re-export things from formality-macros.
17pub use formality_macros::{fixed_point, term, test, Visit};
18
19pub type Fallible<T> = anyhow::Result<T>;
20
21// Modules are *pub* if the items they export aren't meant to be directly
22// used, or at least not most of the time. The idea is that you should use
23// the names from `declare_language`.
24pub mod binder;
25mod cast;
26mod collections;
27pub mod fixed_point;
28pub mod fold;
29pub mod judgment;
30pub mod language;
31pub mod parse;
32pub mod substitution;
33pub mod term;
34pub mod variable;
35pub mod visit;
36
37pub use cast::{Downcast, DowncastFrom, DowncastTo, Downcasted, To, Upcast, UpcastFrom, Upcasted};
38pub use collections::Deduplicate;
39pub use collections::Map;
40pub use collections::Set;
41pub use collections::SetExt;
42
43/// Run an action with a tracing log subscriber. The logging level is loaded
44/// from `RUST_LOG`. The `formality_macro::test` expansion uses this to enable logs.
45pub fn with_tracing_logs<T>(action: impl FnOnce() -> T) -> T {
46    use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
47    use tracing_tree::HierarchicalLayer;
48    let filter = EnvFilter::from_env("RUST_LOG");
49    let subscriber = Registry::default()
50        .with(filter)
51        .with(HierarchicalLayer::new(2).with_writer(std::io::stdout));
52    tracing::subscriber::with_default(subscriber, action)
53}
54
55#[macro_export]
56macro_rules! trait_alias {
57    (
58        $(#[$m:meta])*
59        $v:vis trait $t:ident = $($u:tt)*
60    ) => {
61        $($m)*
62        $v trait $t: $($u)* { }
63
64        impl<T> $t for T
65        where
66            T: $($u)*
67        { }
68    }
69}
70
71/// Declares a new formality language.
72/// This will generate a module with a name you choose that contains various items;
73/// among them will be a struct named `FormalityLang` that implements the
74/// [`Language`](`crate::language::Language`) trait.
75/// When you use the auto-derives or the [`term`](`crate::term`) macro, they will generate
76/// code that references `crate::FormalityLang`, so you need to bring that in scope at the root
77/// of your crate (e.g., if you called the module `mylang`, you might
78/// add `use crate::mylang::FormalityLang` at the root of your crate,
79/// so that the auto-derives can find it.)
80///
81/// See the mdbook for more coverage of how it works.
82#[macro_export]
83macro_rules! declare_language {
84    (
85        $(#[$the_lang_m:meta])*
86        $the_lang_v:vis mod $the_lang:ident {
87            const NAME = $name:expr;
88            type Kind = $kind:ty;
89            type Parameter = $param:ty;
90            const BINDING_OPEN = $binding_open:expr;
91            const BINDING_CLOSE = $binding_close:expr;
92        }
93    ) => {
94        $(#[$the_lang_m:meta])*
95        $the_lang_v mod $the_lang {
96            use super::*;
97
98            #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
99            pub struct FormalityLang;
100
101            impl $crate::language::Language for FormalityLang {
102                const NAME: &'static str = $name;
103                type Kind = $kind;
104                type Parameter = $param;
105                const BINDING_OPEN: char = $binding_open;
106                const BINDING_CLOSE: char = $binding_close;
107            }
108
109            $crate::trait_alias! {
110                pub trait Fold = $crate::fold::CoreFold<FormalityLang>
111            }
112
113            $crate::trait_alias! {
114                pub trait Visit = $crate::visit::CoreVisit<FormalityLang>
115            }
116
117            $crate::trait_alias! {
118                pub trait Parse = $crate::parse::CoreParse<FormalityLang>
119            }
120
121            $crate::trait_alias! {
122                pub trait Term = $crate::term::CoreTerm<FormalityLang>
123            }
124
125            pub type Variable = $crate::variable::CoreVariable<FormalityLang>;
126            pub type ExistentialVar = $crate::variable::CoreExistentialVar<FormalityLang>;
127            pub type UniversalVar = $crate::variable::CoreUniversalVar<FormalityLang>;
128            pub type BoundVar = $crate::variable::CoreBoundVar<FormalityLang>;
129            pub type DebruijnIndex = $crate::variable::DebruijnIndex;
130            pub type VarIndex = $crate::variable::VarIndex;
131            pub type Binder<T> = $crate::binder::CoreBinder<FormalityLang, T>;
132            pub type Substitution = $crate::substitution::CoreSubstitution<FormalityLang>;
133            pub type VarSubstitution = $crate::substitution::CoreVarSubstitution<FormalityLang>;
134
135            /// Parses `text` as a term with no bindings in scope.
136            #[track_caller]
137            pub fn term<T>(text: &str) -> T
138            where
139                T: Parse,
140            {
141                try_term(text).unwrap()
142            }
143
144            /// Parses `text` as a term with no bindings in scope.
145            #[track_caller]
146            pub fn try_term<T>(text: &str) -> anyhow::Result<T>
147            where
148                T: Parse,
149            {
150                term_with(None::<(String, $param)>, text)
151            }
152
153            /// Parses `text` as a term with the given bindings in scope.
154            ///
155            /// References to the given string will be replaced with the given parameter
156            /// when parsing types, lifetimes, etc.
157            #[track_caller]
158            pub fn term_with<T, B>(bindings: impl IntoIterator<Item = B>, text: &str) -> anyhow::Result<T>
159            where
160                T: Parse,
161                B: $crate::Upcast<(String, $param)>,
162            {
163                let scope = $crate::parse::Scope::new(bindings.into_iter().map(|b| b.upcast()));
164                let (t, remainder) = match T::parse(&scope, text) {
165                    Ok(v) => v,
166                    Err(errors) => {
167                        let mut err = anyhow::anyhow!("failed to parse {text}");
168                        for error in errors {
169                            err = err.context(error.text.to_owned()).context(error.message);
170                        }
171                        return Err(err);
172                    }
173                };
174                if !$crate::parse::skip_whitespace(remainder).is_empty() {
175                    anyhow::bail!("extra tokens after parsing {text:?} to {t:?}: {remainder:?}");
176                }
177                Ok(t)
178            }
179        }
180    }
181}
182
183/// Declares a newtyped struct that is an arbitrary (string) identifier,
184/// like the name of a module or something.
185#[macro_export]
186macro_rules! id {
187    ($n:ident) => {
188        #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
189        pub struct $n {
190            data: std::sync::Arc<String>,
191        }
192
193        const _: () = {
194            use $crate::fold::{self, CoreFold};
195            use $crate::parse::{self, CoreParse};
196            use $crate::variable::CoreVariable;
197            use $crate::visit::CoreVisit;
198
199            $crate::cast_impl!($n);
200
201            impl $n {
202                pub fn new(s: &str) -> $n {
203                    $n {
204                        data: std::sync::Arc::new(s.to_string()),
205                    }
206                }
207            }
208
209            impl std::ops::Deref for $n {
210                type Target = String;
211
212                fn deref(&self) -> &String {
213                    &self.data
214                }
215            }
216
217            impl CoreVisit<crate::FormalityLang> for $n {
218                fn free_variables(&self) -> Vec<CoreVariable<crate::FormalityLang>> {
219                    vec![]
220                }
221
222                fn size(&self) -> usize {
223                    1
224                }
225
226                fn assert_valid(&self) {}
227            }
228
229            impl CoreFold<crate::FormalityLang> for $n {
230                fn substitute(
231                    &self,
232                    _substitution_fn: fold::SubstitutionFn<'_, crate::FormalityLang>,
233                ) -> Self {
234                    self.clone()
235                }
236            }
237
238            impl CoreParse<crate::FormalityLang> for $n {
239                fn parse<'t>(
240                    _scope: &parse::Scope<crate::FormalityLang>,
241                    text: &'t str,
242                ) -> parse::ParseResult<'t, Self> {
243                    let (string, text) = parse::identifier(text)?;
244                    let n = $n::new(&string);
245                    Ok((n, text))
246                }
247            }
248
249            impl std::fmt::Debug for $n {
250                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251                    write!(f, "{}", &self.data)
252                }
253            }
254        };
255    };
256}