-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #141 from lixitrixi/rule-registry
Rule registry
- Loading branch information
Showing
10 changed files
with
214 additions
and
11 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,7 @@ | ||
use conjure_core::ast::Expression; | ||
use conjure_core::rule::{Rule, RuleApplicationError}; | ||
use conjure_core::{ast::Expression, rule::RuleApplicationError}; | ||
use conjure_rules::register_rule; | ||
|
||
#[register_rule] | ||
fn identity(expr: &Expression) -> Result<Expression, RuleApplicationError> { | ||
Ok(expr.clone()) | ||
} | ||
|
||
pub static IDENTITY_RULE: Rule = Rule { | ||
name: "identity", | ||
application: identity, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
name = "conjure_rules" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
conjure_rules_proc_macro = { path = "../conjure_rules_proc_macro" } | ||
conjure_core = { path = "../conjure_core" } | ||
linkme = "0.3.22" | ||
|
||
[lints] | ||
workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
//! ### A decentralised rule registry for Conjure Oxide | ||
//! | ||
//! This crate allows registering valid functions as expression-reduction rules. | ||
//! Functions can be decorated with the `register_rule` macro in any downstream crate and be used by Conjure Oxide's rule engine. | ||
//! To achieve compile-time linking, we make use of the [`linkme`](https://docs.rs/linkme/latest/linkme/) crate. | ||
//! | ||
|
||
// Why all the re-exports and wierdness? | ||
// ============================ | ||
// | ||
// Procedural macros are unhygenic - they directly subsitute into source code, and do not have | ||
// their own scope, imports, and so on. | ||
// | ||
// See [https://doc.rust-lang.org/reference/procedural-macros.html#procedural-macro-hygiene]. | ||
// | ||
// Therefore, we cannot assume the user has any dependencies apart from the one they imported the | ||
// macro from. (Also, note Rust does not bring transitive dependencies into scope, so we cannot | ||
// assume the presence of a dependency of the crate.) | ||
// | ||
// To solve this, the crate the macro is in must re-export everything the macro needs to run. | ||
// | ||
// However, proc-macro crates can only export proc-macros. Therefore, we must use a "front end | ||
// crate" (i.e. this one) to re-export both the macro and all the things it may need. | ||
|
||
use conjure_core::rule::Rule; | ||
use linkme::distributed_slice; | ||
|
||
#[doc(hidden)] | ||
pub mod _dependencies { | ||
pub use conjure_core::rule::Rule; | ||
pub use linkme::distributed_slice; | ||
} | ||
|
||
#[doc(hidden)] | ||
#[distributed_slice] | ||
pub static RULES_DISTRIBUTED_SLICE: [Rule<'static>]; | ||
|
||
/// Returns a copied `Vec` of all rules registered with the `register_rule` macro. | ||
/// | ||
/// Rules are not guaranteed to be in any particular order. | ||
/// | ||
/// # Example | ||
/// ```rust | ||
/// # use conjure_rules::register_rule; | ||
/// # use conjure_core::rule::{Rule, RuleApplicationError}; | ||
/// # use conjure_core::ast::Expression; | ||
/// # | ||
/// #[register_rule] | ||
/// fn identity(expr: &Expression) -> Result<Expression, RuleApplicationError> { | ||
/// Ok(expr.clone()) | ||
/// } | ||
/// | ||
/// fn main() { | ||
/// println!("Rules: {:?}", conjure_rules::get_rules()); | ||
/// } | ||
/// ``` | ||
/// | ||
/// This will print (if no other rules are registered): | ||
/// ```text | ||
/// Rules: [Rule { name: "identity", application: MEM }] | ||
/// ``` | ||
/// Where `MEM` is the memory address of the `identity` function. | ||
pub fn get_rules() -> Vec<Rule<'static>> { | ||
RULES_DISTRIBUTED_SLICE.to_vec() | ||
} | ||
|
||
/// This procedural macro registers a decorated function with `conjure_rules`' global registry. | ||
/// It may be used in any downstream crate. For more information on linker magic, see the [`linkme`](https://docs.rs/linkme/latest/linkme/) crate. | ||
/// | ||
/// **IMPORTANT**: Since the resulting rule may not be explicitly referenced, it may be removed by the compiler's dead code elimination. | ||
/// To prevent this, you must ensure that either: | ||
/// 1. codegen-units is set to 1, i.e. in Cargo.toml: | ||
/// ```toml | ||
/// [profile.release] | ||
/// codegen-units = 1 | ||
/// ``` | ||
/// 2. The function is included somewhere else in the code | ||
/// | ||
/// <hr> | ||
/// | ||
/// Functions must have the signature `fn(&Expr) -> Result<Expr, RuleApplicationError>`. | ||
/// The created rule will have the same name as the function. | ||
/// | ||
/// Intermediary static variables are created to allow for the decentralized registry, with the prefix `CONJURE_GEN_`. | ||
/// Please ensure that other variable names in the same scope do not conflict with these. | ||
/// | ||
/// <hr> | ||
/// | ||
/// For example: | ||
/// ```rust | ||
/// # use conjure_core::ast::Expression; | ||
/// # use conjure_core::rule::RuleApplicationError; | ||
/// # use conjure_rules::register_rule; | ||
/// # | ||
/// #[register_rule] | ||
/// fn identity(expr: &Expression) -> Result<Expression, RuleApplicationError> { | ||
/// Ok(expr.clone()) | ||
/// } | ||
/// ``` | ||
#[doc(inline)] | ||
pub use conjure_rules_proc_macro::register_rule; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "conjure_rules_proc_macro" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[lib] | ||
proc_macro = true | ||
|
||
[dependencies] | ||
quote = "1.0.34" | ||
conjure_core = {path= "../conjure_core"} | ||
syn = { version = "2.0.43", features = ["full"] } | ||
|
||
[lints] | ||
workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
//! This is the backend procedural macro crate for `conjure_rules`. USE THAT INSTEAD! | ||
|
||
use proc_macro::TokenStream; | ||
use quote::quote; | ||
use syn::{parse_macro_input, Ident, ItemFn}; | ||
|
||
#[proc_macro_attribute] | ||
pub fn register_rule(_: TokenStream, item: TokenStream) -> TokenStream { | ||
let func = parse_macro_input!(item as ItemFn); | ||
let rule_ident = &func.sig.ident; | ||
let static_name = format!("CONJURE_GEN_RULE_{}", rule_ident).to_uppercase(); | ||
let static_ident = Ident::new(&static_name, rule_ident.span()); | ||
|
||
let expanded = quote! { | ||
#func | ||
|
||
#[::conjure_rules::_dependencies::distributed_slice(::conjure_rules::RULES_DISTRIBUTED_SLICE)] | ||
pub static #static_ident: ::conjure_rules::_dependencies::Rule = ::conjure_rules::_dependencies::Rule { | ||
name: stringify!(#rule_ident), | ||
application: #rule_ident, | ||
}; | ||
}; | ||
|
||
TokenStream::from(expanded) | ||
} |