impl EfficiencyAnalyzer {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new() -> Self {
Self {
_max_loop_depth: 0,
_recursive_calls: 0,
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn analyze(&self, ast: &syn::File) -> String {
let mut visitor = EfficiencyVisitor {
current_loop_depth: 0,
max_loop_depth: 0,
has_recursion: false,
};
visitor.visit_file(ast);
visitor.compute_complexity()
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn analyze_string(&self, code: &str) -> Result<EfficiencyResult, syn::Error> {
let ast = syn::parse_file(code)?;
Ok(EfficiencyResult {
time_complexity: self.analyze(&ast),
space_complexity: self.analyze_space(&ast),
})
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
fn analyze_space(&self, ast: &syn::File) -> String {
let mut visitor = SpaceComplexityVisitor {
allocations: 0,
recursive_depth: 0,
};
visitor.visit_file(ast);
visitor.compute_space_complexity()
}
}
impl EfficiencyVisitor {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
fn compute_complexity(&self) -> String {
match self.max_loop_depth {
0 => "O(1)".to_string(),
1 => {
if self.has_recursion {
"O(n log n)".to_string() } else {
"O(n)".to_string()
}
}
2 => "O(n^2)".to_string(),
3 => "O(n^3)".to_string(),
_ => format!("O(n^{})", self.max_loop_depth),
}
}
}
impl<'ast> Visit<'ast> for EfficiencyVisitor {
fn visit_expr_for_loop(&mut self, node: &'ast syn::ExprForLoop) {
self.current_loop_depth += 1;
if self.current_loop_depth > self.max_loop_depth {
self.max_loop_depth = self.current_loop_depth;
}
syn::visit::visit_expr_for_loop(self, node);
self.current_loop_depth -= 1;
}
fn visit_expr_while(&mut self, node: &'ast syn::ExprWhile) {
self.current_loop_depth += 1;
if self.current_loop_depth > self.max_loop_depth {
self.max_loop_depth = self.current_loop_depth;
}
syn::visit::visit_expr_while(self, node);
self.current_loop_depth -= 1;
}
fn visit_expr_loop(&mut self, node: &'ast syn::ExprLoop) {
self.current_loop_depth += 1;
if self.current_loop_depth > self.max_loop_depth {
self.max_loop_depth = self.current_loop_depth;
}
syn::visit::visit_expr_loop(self, node);
self.current_loop_depth -= 1;
}
fn visit_expr_call(&mut self, node: &'ast syn::ExprCall) {
if let syn::Expr::Path(_path) = &*node.func {
self.has_recursion = self.current_loop_depth == 0;
}
syn::visit::visit_expr_call(self, node);
}
}
impl SpaceComplexityVisitor {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
fn compute_space_complexity(&self) -> String {
if self.recursive_depth > 0 {
"O(n)".to_string() } else if self.allocations > 0 {
"O(n)".to_string() } else {
"O(1)".to_string() }
}
}
impl<'ast> Visit<'ast> for SpaceComplexityVisitor {
fn visit_expr_call(&mut self, node: &'ast syn::ExprCall) {
if let syn::Expr::Path(path) = &*node.func {
if let Some(ident) = path.path.get_ident() {
let name = ident.to_string();
if name.contains("vec") || name.contains("Vec") || name.contains("alloc") {
self.allocations += 1;
}
}
}
syn::visit::visit_expr_call(self, node);
}
fn visit_local(&mut self, node: &'ast syn::Local) {
if let syn::Pat::Type(_pat_type) = &node.pat {
self.allocations += 1;
}
syn::visit::visit_local(self, node);
}
}