-
Notifications
You must be signed in to change notification settings - Fork 0
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 #4 from lixitrixi/rule-engine
Here I implement a decentralized rule registry which can be added to at compile time with the register_rule macro. I also implement a basic expression rewriting algorithm to iterate over rules and apply available ones until no more are found.
- Loading branch information
Showing
10 changed files
with
201 additions
and
3 deletions.
There are no files selected for viewing
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 |
---|---|---|
|
@@ -3,4 +3,5 @@ members = [ | |
"antwort", | ||
"antwort_core", | ||
"antwort_solver", | ||
"antwort_macros", | ||
] |
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 |
---|---|---|
@@ -0,0 +1,55 @@ | ||
use crate::rule::Rule; | ||
use crate::Expr; | ||
use linkme::distributed_slice; | ||
|
||
#[distributed_slice] | ||
pub static _RULES_DISTRIBUTED_SLICE: [Rule]; | ||
|
||
pub fn get_rules() -> Vec<Rule> { | ||
_RULES_DISTRIBUTED_SLICE.to_vec() | ||
} | ||
|
||
/// Applies the first applicable rule to the expression and returns the rewritten result. | ||
fn apply_first(expr: &Expr, rules: &Vec<Rule>) -> Option<Expr> { | ||
for rule in rules { | ||
match rule.apply(&expr) { | ||
Ok(new) => { | ||
return Some(new); | ||
} | ||
Err(_) => continue, | ||
} | ||
} | ||
return None; | ||
} | ||
|
||
/// # Returns | ||
/// - Some(<new_expression>) after applying the first applicable rule to `expr` or a sub-expression. | ||
/// - None if no rule is applicable to the expression or any sub-expression. | ||
fn rewrite_iteration(expr: &Expr, rules: &Vec<Rule>) -> Option<Expr> { | ||
if let Some(new) = apply_first(expr, rules) { | ||
return Some(new); | ||
} else { | ||
let mut sub = expr.sub_expressions(); | ||
for i in 0..sub.len() { | ||
if let Some(new) = rewrite_iteration(sub[i], rules) { | ||
sub[i] = &new; | ||
return Some(expr.with_sub_expressions(sub)); | ||
} | ||
} | ||
} | ||
None // No rules applicable to this branch of the expression | ||
} | ||
|
||
/// Rewrites the expression using the rules in the registry. | ||
/// Continues until no more rules are applicable to the expression or any sub-expression. | ||
pub fn rewrite(expr: &Expr) -> Expr { | ||
println!("REWRITE: {:?}", expr); | ||
let rules = get_rules(); | ||
let mut new = expr.clone(); | ||
while let Some(step) = rewrite_iteration(&new, &rules) { | ||
new = step; | ||
println!(" STEP: {:?}", new); | ||
} | ||
println!("DONE"); | ||
new | ||
} |
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 +1,2 @@ | ||
pub mod ast; | ||
pub mod 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,18 @@ | ||
use crate::ast::Expr; | ||
|
||
#[derive(Debug)] | ||
pub enum RuleApplicationError { | ||
RuleNotApplicable, | ||
} | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct Rule { | ||
pub name: &'static str, | ||
pub application: fn(&Expr) -> Result<Expr, RuleApplicationError>, | ||
} | ||
|
||
impl Rule { | ||
pub fn apply(&self, expr: &Expr) -> Result<Expr, RuleApplicationError> { | ||
(self.application)(expr) | ||
} | ||
} |
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 = "antwort_macros" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[lib] | ||
proc_macro = true | ||
|
||
[dependencies] | ||
linkme = "0.3.20" | ||
quote = "1.0.34" | ||
syn = { version = "2.0.43", features = ["full"] } |
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,28 @@ | ||
use proc_macro::TokenStream; | ||
use quote::quote; | ||
use syn::{parse_macro_input, Ident, ItemFn}; | ||
|
||
#[proc_macro_attribute] | ||
/// This procedural macro registers a decorated function with Antwort's rule engine. | ||
/// Functions must have the signature `fn(&Expr) -> Result<Expr, RuleApplicationError>`. | ||
/// | ||
/// Intermediary static variables are created to allow for the decentralized registry, with the prefix `_ANTWORT_GEN_`. | ||
/// Please ensure that other variable names do not conflict with these. | ||
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!("_ANTWORT_GEN_RULE_{}", rule_ident).to_uppercase(); | ||
let static_ident = Ident::new(&static_name, rule_ident.span()); | ||
|
||
let expanded = quote! { | ||
#func | ||
|
||
#[::linkme::distributed_slice(::antwort::_RULES_DISTRIBUTED_SLICE)] | ||
static #static_ident: ::antwort::rule::Rule = ::antwort::rule::Rule { | ||
name: stringify!(#rule_ident), | ||
application: #rule_ident, | ||
}; | ||
}; | ||
|
||
TokenStream::from(expanded) | ||
} |