Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 44 additions & 9 deletions examples/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use std::sync::atomic::{self, AtomicBool};
use std::sync::Arc;
use std::time::Instant;

use logru::ast::{Sym, VarScope};
use logru::analysis::find_unique_variables;
use logru::ast::{Rule, Sym, VarScope};
use logru::resolve::{ArithmeticResolver, ResolverExt};
use logru::search::{query_dfs, Resolved, Resolver};
use logru::term_arena::{AppTerm, ArgRange};
Expand Down Expand Up @@ -193,6 +194,22 @@ fn query(state: &mut AppState, args: &str) {
}
}

fn analyze_rules(rules: &[Rule]) {
for rule in rules.iter().filter(|r| r.scope.is_some()) {
let orphans = find_unique_variables(&rule)
.iter()
.filter_map(|var| rule.scope.as_ref().unwrap().get_name(*var))
.collect::<Vec<_>>();
if !orphans.is_empty() {
print!("Some variables appear only once in the rule:");
for name in orphans {
print!(" {},", name);
}
println!("\nare those typos?");
}
}
}

static COMMANDS: &[Command] = &[
Command {
name: ":define",
Expand All @@ -203,7 +220,15 @@ static COMMANDS: &[Command] = &[
println!("Usage:\n\t:define <source>");
return;
}
match state.universe.load_str(args) {
let res = state
.universe
.parse_rules(args)
.map(|rules| {
analyze_rules(&rules);
rules
})
.map(|rules| state.universe.insert_rules(rules));
match res {
Ok(()) => {
println!("Defined!");
}
Expand Down Expand Up @@ -248,14 +273,24 @@ static COMMANDS: &[Command] = &[
return;
}
match std::fs::read_to_string(args) {
Ok(contents) => match state.universe.load_str(&contents) {
Ok(()) => {
println!("Loaded!");
}
Err(err) => {
println!("Failed to parse: {:?}", err);
Ok(contents) => {
let res = state
.universe
.parse_rules(&contents)
.map(|rules| {
analyze_rules(&rules);
rules
})
.map(|rules| state.universe.insert_rules(rules));
match res {
Ok(()) => {
println!("Loaded!");
}
Err(err) => {
println!("Failed to parse: {:?}", err);
}
}
},
}
Err(err) => {
println!("Failed to load: {}", err);
}
Expand Down
32 changes: 32 additions & 0 deletions src/analysis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//! Program analysis proceduress

use crate::ast::{Rule, Term, Var};
use std::collections::{HashMap, HashSet};

/// Finds variables occuring only once in the rule (together in head and tail).
pub fn find_unique_variables(rule: &Rule) -> HashSet<Var> {
let vars = count_variables(rule);
vars.into_iter()
.filter(|(_var, count)| *count == 1)
.map(|(var, _count)| var)
.collect()
}

/// Counts the number of occurrences of all variables in the rule (both in head and tail).
pub fn count_variables(rule: &Rule) -> HashMap<Var, usize> {
let mut vars = HashMap::new();
count_in_args(&mut vars, &rule.head.args);
count_in_args(&mut vars, &rule.tail);
vars
}

fn count_in_args(mut vars: &mut HashMap<Var, usize>, args: &[Term]) {
for term in args {
match term {
Term::Var(var) => *vars.entry(*var).or_insert(0) += 1,
Term::Int(_) => {}
Term::Cut => {}
Term::App(appterm) => count_in_args(&mut vars, &appterm.args),
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
#[cfg(doctest)]
pub struct ReadmeDoctests;

pub mod analysis;
pub mod ast;
pub mod resolve;
pub mod search;
Expand Down
18 changes: 14 additions & 4 deletions src/textual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub use parser::{ParseError, ParseErrorKind};
use pretty::ScopedPrettifier;

use crate::{
ast::Query,
ast::{Query, Rule},
resolve::RuleResolver,
search::{self, SolutionIter},
universe::{RuleSet, SymbolOverlay, SymbolStore},
Expand Down Expand Up @@ -91,12 +91,22 @@ impl TextualUniverse {
}
}

/// Load a set of rules from a string.
pub fn load_str(&mut self, rules: &str) -> Result<(), ParseError> {
let rules = Parser::new(&mut self.symbols).parse_rules_str(rules)?;
/// Parse a set of rules using the symbols defined in this universe.
pub fn parse_rules(&mut self, rules: &str) -> Result<Vec<Rule>, ParseError> {
Parser::new(&mut self.symbols).parse_rules_str(rules)
}

/// Insert rules previously parsed using [`Self::parse_rules`].
pub fn insert_rules(&mut self, rules: Vec<Rule>) {
for rule in rules {
self.rules.insert(rule);
}
}

/// Load a set of rules from a string.
pub fn load_str(&mut self, rules: &str) -> Result<(), ParseError> {
let rules = self.parse_rules(rules)?;
self.insert_rules(rules);
Ok(())
}

Expand Down