Skip to content

Commit

Permalink
Build call graph.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth committed Feb 5, 2024
1 parent 0525cd2 commit bf0967e
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 2 deletions.
65 changes: 65 additions & 0 deletions pil-analyzer/src/call_graph.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use std::collections::{HashMap, HashSet};

use powdr_ast::{
analyzed::{Expression, Reference},
parsed::visitor::ExpressionVisitable,
};
use powdr_number::FieldElement;

/// Returns a sorted list of symbols such that called symbols appear before the symbols that reference them.
/// Circular dependencies appear in an arbitray order.
pub fn sort_called_first<
'a,
T: FieldElement,
I: Iterator<Item = (&'a str, Option<&'a Expression<T>>)>,
>(
symbols: I,
) -> Vec<&'a str> {
let graph = call_graph(symbols);
let mut visited: HashSet<&str> = HashSet::new();
let mut result: Vec<&str> = Vec::new();
for (name, _) in graph.iter() {
topo_sort_visit(name, &graph, &mut visited, &mut result);
}
assert_eq!(graph.len(), result.len());
result
}

fn topo_sort_visit<'a, 'b>(
name: &'a str,
graph: &'b HashMap<&'a str, HashSet<String>>,
visited: &'b mut HashSet<&'a str>,
result: &'b mut Vec<&'a str>,
) {
if !visited.insert(name) {
return;
}
if let Some(called) = graph.get(name) {
for c in called {
println!(" - {c}");
let n = graph.get_key_value(c.as_str()).unwrap().0;
topo_sort_visit(n, graph, visited, result);
}
}
result.push(name);
}

fn call_graph<'a, T: FieldElement, I: Iterator<Item = (&'a str, Option<&'a Expression<T>>)>>(
symbols: I,
) -> HashMap<&'a str, HashSet<String>> {
symbols
.map(|(name, expr)| {
let mut called: HashSet<String> = HashSet::new();
expr.map(|e| {
e.pre_visit_expressions(&mut |e: &Expression<T>| {
if let Expression::Reference(Reference::Poly(r)) = e {
// Tried with &'a str here, but it does not really work
// with the lambda.
called.insert(r.name.clone());
}
})
});
(name, called)
})
.collect()
}
1 change: 1 addition & 0 deletions pil-analyzer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![deny(clippy::print_stdout)]

mod call_graph;
mod condenser;
pub mod evaluator;
pub mod expression_processor;
Expand Down
15 changes: 13 additions & 2 deletions pil-analyzer/src/type_inference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use powdr_ast::{
use powdr_number::{FieldElement, GoldilocksField};
use powdr_parser::{parse_type_name, parse_type_var_bounds};

use crate::call_graph::sort_called_first;

// TODO in the end, this needs to modify the expressions,
// because it has to ineject implict conversions
// and it has to change generic trait function calls to concrete ones
Expand Down Expand Up @@ -175,12 +177,21 @@ impl TypeChecker {

// Now check the declarations for consistency or derive a concrete type
// (or check against consistency of the type scheme in the declaration).
let names = sort_called_first(
definitions
.iter()
.map(|(n, (_, e))| (n.as_str(), e.clone())),
);

println!("Visitation order: {}", names.iter().format("\n"));

for (name, (_, value)) in definitions {
for name in names {
if builtin_schemes().contains_key(name) {
continue;
}
let Some(value) = value else { continue };
let (_, Some(value)) = definitions[&name.to_string()] else {
continue;
};

let ty = self.instantiate_scheme(self.types[name].clone());
println!(
Expand Down

0 comments on commit bf0967e

Please sign in to comment.