Skip to content

Commit

Permalink
A few checks on items and submodules.
Browse files Browse the repository at this point in the history
  • Loading branch information
mmaloney-sf committed May 8, 2024
1 parent 23ece24 commit bb96c34
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 8 deletions.
6 changes: 6 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ pub type StaticIndex = u64;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Ident(String);

impl std::borrow::Borrow<str> for Ident {
fn borrow(&self) -> &str {
&self.0
}
}

#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Path(String);

Expand Down
15 changes: 10 additions & 5 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ mod structureq;
mod typecheckq;
mod packageq;

pub use astq::{AstQ, AstQStorage};
pub use structureq::{StructureQ, StructureQStorage};
pub use typecheckq::{TypecheckQ, TypecheckQStorage};
pub use packageq::{PackageQ, PackageQStorage};
pub use astq::AstQ;
pub use structureq::StructureQ;
pub use typecheckq::TypecheckQ;
pub use packageq::PackageQ;

use std::sync::Arc;
use crate::hir;
use crate::common::*;

#[salsa::database(AstQStorage, StructureQStorage, TypecheckQStorage, PackageQStorage)]
#[salsa::database(
astq::AstQStorage,
structureq::StructureQStorage,
typecheckq::TypecheckQStorage,
packageq::PackageQStorage,
)]
#[derive(Default)]
pub struct Database {
storage: salsa::Storage<Self>,
Expand Down
7 changes: 4 additions & 3 deletions src/db/packageq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ pub trait PackageQ: TypecheckQ {
}

fn check(db: &dyn PackageQ) -> Result<(), VirdantError> {
db.check_item_names_unique()?;
db.check_submodule_moddefs_exist()?;
db.check_no_submodule_cycles()?;

let mut errors = ErrorReport::new();
for moddef in &db.package_moddef_names()? {
if let Err(err) = db.check_moddef(moddef.clone()) {
Expand All @@ -21,7 +25,6 @@ fn check(db: &dyn PackageQ) -> Result<(), VirdantError> {
errors.check()
}


fn check_moddef(db: &dyn PackageQ, moddef: Ident) -> VirdantResult<()> {
let mut errors = ErrorReport::new();
for component in db.moddef_component_names(moddef.clone())? {
Expand Down Expand Up @@ -59,5 +62,3 @@ fn package_hir(db: &dyn PackageQ) -> VirdantResult<hir::Package> {
moddefs,
})
}


96 changes: 96 additions & 0 deletions src/db/structureq.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::{HashSet, HashMap};
use crate::common::*;
use crate::hir;
use crate::ast;
Expand All @@ -14,6 +15,10 @@ pub trait StructureQ: AstQ {
fn moddef_component_hir(&self, moddef: Ident, component: Ident) -> VirdantResult<hir::Component>;
fn moddef_component(&self, moddef: Ident, component: Ident) -> Result<ast::Component, VirdantError>;
fn moddef_component_connects(&self, moddef: Ident, component: Ident) -> Result<Vec<ast::InlineConnect>, VirdantError>;

fn check_item_names_unique(&self) -> Result<(), VirdantError>;
fn check_submodule_moddefs_exist(&self) -> Result<(), VirdantError>;
fn check_no_submodule_cycles(&self) -> Result<(), VirdantError>;
}

fn moddef_component_hir(db: &dyn StructureQ, moddef: Ident, component: Ident) -> VirdantResult<hir::Component> {
Expand Down Expand Up @@ -139,3 +144,94 @@ fn moddef_component_connects(db: &dyn StructureQ, moddef: Ident, component: Iden
}
Ok(result)
}

fn check_item_names_unique(db: &dyn StructureQ) -> Result<(), VirdantError> {
let mut errors = ErrorReport::new();
let mut item_names = HashSet::new();

for name in db.package_item_names()? {
if !item_names.insert(name.clone()) {
errors.add(VirdantError::Other(format!("Duplicate item: {name}")));
}
}

errors.check()
}

fn check_submodule_moddefs_exist(db: &dyn StructureQ) -> Result<(), VirdantError> {
let mut errors = ErrorReport::new();

let moddef_names: Vec<Ident> = db.package_moddef_names()?;
for moddef_name in &moddef_names {
for submodule in &db.moddef_submodules(moddef_name.clone())? {
if !moddef_names.contains(&submodule.moddef) {
let submoddef_name = &submodule.moddef;
let msg = format!("Module contains an undefined submodule: {moddef_name} contains unknown {submoddef_name}");
errors.add(VirdantError::Other(msg));
}
}
}

errors.check()
}

fn check_no_submodule_cycles(db: &dyn StructureQ) -> Result<(), VirdantError> {
let mut errors = ErrorReport::new();
let moddef_names: Vec<Ident> = db.package_moddef_names()?;

let mut depends: HashMap<Ident, Vec<Ident>> = HashMap::new();

for moddef_name in &moddef_names {
let submodules = db.moddef_submodules(moddef_name.clone())?;
depends.insert(moddef_name.clone(), submodules.into_iter().map(|s| s.moddef).collect());
}

for cycle in find_cycles(&depends) {
let moddef = &cycle[0];
let cycle_str = cycle.iter().map(|moddef| moddef.as_str()).collect::<Vec<_>>().join(" contains ");
errors.add(VirdantError::Other(format!("Module contains itself: {cycle_str} contains {moddef}")));
}

errors.check()
}


fn find_cycles(graph: &HashMap<Ident, Vec<Ident>>) -> Vec<Vec<Ident>> {
let mut cycles = Vec::new();
let mut visited = HashSet::new();
let mut stack = Vec::new();

for node in graph.keys() {
if !visited.contains(node) {
dfs(node.clone(), graph, &mut visited, &mut stack, &mut cycles);
}
}

cycles
}

fn dfs(
node: Ident,
graph: &HashMap<Ident, Vec<Ident>>,
visited: &mut HashSet<Ident>,
stack: &mut Vec<Ident>,
cycles: &mut Vec<Vec<Ident>>,
) {
visited.insert(node.clone());
stack.push(node.clone());

if let Some(neighbors) = graph.get(&node) {
for neighbor in neighbors {
if !visited.contains(neighbor) {
dfs(neighbor.clone(), graph, visited, stack, cycles);
} else if stack.contains(&neighbor) {
// Found a cycle
let cycle_start = stack.iter().position(|x| x == neighbor).unwrap();
let cycle: Vec<Ident> = stack[cycle_start..].iter().cloned().collect();
cycles.push(cycle);
}
}
}

stack.pop();
}

0 comments on commit bb96c34

Please sign in to comment.