Skip to content

Commit

Permalink
Implement initial query engine
Browse files Browse the repository at this point in the history
  • Loading branch information
purefunctor committed Jan 29, 2025
1 parent 138f1b4 commit e95ad89
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 2 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions crates/building/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ edition = "2021"
[dependencies]
dashmap = "6.1.0"
id = { version = "0.1.0", path = "../id" }
indexing = { version = "0.1.0", path = "../indexing" }
indexmap = "2.7.1"
lexing = { version = "0.1.0", path = "../lexing" }
parking_lot = "0.12.3"
parsing = { version = "0.1.0", path = "../parsing" }
petgraph = "0.7.1"
rayon = "1.10.0"
rowan = "0.16.1"
rustc-hash = "2.1.0"
syntax = { version = "0.1.0", path = "../syntax" }
206 changes: 204 additions & 2 deletions crates/building/src/base.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,207 @@
use std::{mem, sync::Arc};

use indexing::IndexingResult;
use rowan::ast::AstNode;
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::cst;

use crate::files::FileId;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum QueryKey {
Index(usize),
Resolve(usize),
Content(FileId),
Parse(FileId),
Index(FileId),
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Trace {
built: usize,
changed: usize,
dependencies: Arc<[QueryKey]>,
}

impl Trace {
pub fn create_input(revision: usize) -> Trace {
Trace { built: revision, changed: revision, dependencies: [].into() }
}

pub fn create_fresh(revision: usize, dependencies: Arc<[QueryKey]>) -> Trace {
Trace { built: revision, changed: revision, dependencies }
}
}

#[derive(Default)]
pub struct Runtime {
revision: usize,
traces: FxHashMap<QueryKey, Trace>,

content: FxHashMap<FileId, Arc<str>>,
parse: FxHashMap<FileId, cst::Module>,
index: FxHashMap<FileId, Arc<IndexingResult>>,

parent: Option<QueryKey>,
dependencies: FxHashMap<QueryKey, FxHashSet<QueryKey>>,
}

impl Runtime {
pub fn compute<T>(&mut self, k: QueryKey, compute: impl Fn(&mut Runtime) -> T) -> T {
let parent = mem::replace(&mut self.parent, Some(k));
let result = compute(self);

self.revision += 1;
let revision = self.revision;

let dependencies = self
.dependencies
.get(&k)
.map(|dependencies| dependencies.iter().copied())
.unwrap_or_default()
.collect();

let v = Trace::create_fresh(revision, dependencies);
self.traces.insert(k, v);

self.parent = parent;
result
}

pub fn query<T: Clone>(
&mut self,
k: QueryKey,
compute: impl Fn(&mut Runtime) -> T,
get_storage: impl Fn(&mut Runtime) -> Option<(T, &mut Trace)>,
set_storage: impl Fn(&mut Runtime, T),
) -> T {
if let Some(parent) = self.parent {
self.dependencies.entry(parent).or_default().insert(k);
}

let revision = self.revision;
if let Some((value, trace)) = get_storage(self) {
if trace.built == revision {
value
} else {
let built = trace.built;
let dependencies = Arc::clone(&trace.dependencies);

let mut latest = 0;
for dependency in dependencies.iter() {
match dependency {
QueryKey::Content(_) => (),
QueryKey::Parse(id) => {
self.parse(*id);
}
QueryKey::Index(_) => (),
}
if let Some(dependency) = self.traces.get(dependency) {
latest = latest.max(dependency.changed);
}
}

if built >= latest {
if let Some(trace) = self.traces.get_mut(&k) {
trace.built = revision;
}
value
} else {
let fresh = self.compute(k, compute);
set_storage(self, T::clone(&fresh));
fresh
}
}
} else {
let fresh = self.compute(k, compute);
set_storage(self, T::clone(&fresh));
fresh
}
}

pub fn set_content(&mut self, id: FileId, content: Arc<str>) {
self.revision += 1;
let revision = self.revision;

self.content.insert(id, content);

let k = QueryKey::Content(id);
let v = Trace::create_input(revision);
self.traces.insert(k, v);
}

pub fn content(&mut self, id: FileId) -> Arc<str> {
let k = QueryKey::Content(id);
if let Some(parent) = self.parent {
self.dependencies.entry(parent).or_default().insert(k);
}
let v = self.content.get(&id).expect("invalid violated: invalid query key");
Arc::clone(v)
}

pub fn parse(&mut self, id: FileId) -> cst::Module {
let k = QueryKey::Parse(id);
self.query(
k,
|this| {
let content = this.content(id);

let lexed = lexing::lex(&content);
let tokens = lexing::layout(&lexed);

let (node, _) = parsing::parse(&lexed, &tokens);
cst::Module::cast(node).expect("invariant violated: cannot cast parse result")
},
|this| {
let value = this.parse.get(&id).cloned()?;
let trace = this.traces.get_mut(&k)?;
Some((value, trace))
},
|this, value| {
this.parse.insert(id, value);
},
)
}

pub fn index(&mut self, id: FileId) -> Arc<IndexingResult> {
let k = QueryKey::Index(id);
self.query(
k,
|this| {
let module = this.parse(id);
let (result, _) = indexing::index(&module);
Arc::new(result)
},
|this| {
let value = this.index.get(&id).cloned()?;
let trace = this.traces.get_mut(&k)?;
Some((value, trace))
},
|this, value| {
this.index.insert(id, value);
},
)
}
}

#[cfg(test)]
mod tests {
use crate::files::Files;

use super::Runtime;

#[test]
fn test_basic() {
let mut runtime = Runtime::default();
let mut files = Files::default();

let id = files.insert("./src/Main.purs", "module Main where\n\nlife = 42");
let content = files.content(id);

runtime.set_content(id, content);
dbg!(runtime.index(id));
dbg!(runtime.index(id));

runtime.set_content(id, "module Main where\n\n\n\nlife = 42".into());
dbg!(runtime.index(id));
dbg!(runtime.index(id));
}
}

0 comments on commit e95ad89

Please sign in to comment.