mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-08-04 19:08:32 +00:00
Improved ‘compiler’ example showing parsing of an actual language and use of actual interning
This commit is contained in:
parent
e1f6572a00
commit
09d9592f14
6 changed files with 146 additions and 69 deletions
|
@ -1,38 +0,0 @@
|
|||
use crate::compiler;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[salsa::query_group(ClassTable)]
|
||||
pub trait ClassTableDatabase: compiler::CompilerDatabase {
|
||||
/// Get the fields.
|
||||
fn fields(&self, class: DefId) -> Arc<Vec<DefId>>;
|
||||
|
||||
/// Get the list of all classes
|
||||
fn all_classes(&self) -> Arc<Vec<DefId>>;
|
||||
|
||||
/// Get the list of all fields
|
||||
fn all_fields(&self) -> Arc<Vec<DefId>>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct DefId(usize);
|
||||
|
||||
fn all_classes(_: &impl ClassTableDatabase) -> Arc<Vec<DefId>> {
|
||||
Arc::new(vec![DefId(0), DefId(10)]) // dummy impl
|
||||
}
|
||||
|
||||
fn fields(_: &impl ClassTableDatabase, class: DefId) -> Arc<Vec<DefId>> {
|
||||
Arc::new(vec![DefId(class.0 + 1), DefId(class.0 + 2)]) // dummy impl
|
||||
}
|
||||
|
||||
fn all_fields(db: &impl ClassTableDatabase) -> Arc<Vec<DefId>> {
|
||||
Arc::new(
|
||||
db.all_classes()
|
||||
.iter()
|
||||
.cloned()
|
||||
.flat_map(|def_id| {
|
||||
let fields = db.fields(def_id);
|
||||
(0..fields.len()).map(move |i| fields[i])
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
|
@ -1,6 +1,74 @@
|
|||
pub trait CompilerDatabase: salsa::Database {
|
||||
fn interner(&self) -> &Interner;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{interner::Interner, values::*};
|
||||
|
||||
#[salsa::query_group(CompilerDatabase)]
|
||||
#[salsa::requires(Interner)]
|
||||
pub trait Compiler: Interner {
|
||||
#[salsa::input]
|
||||
fn input_string(&self) -> Arc<String>;
|
||||
|
||||
/// Get the fields.
|
||||
fn fields(&self, class: Class) -> Arc<Vec<Field>>;
|
||||
|
||||
/// Get the list of all classes
|
||||
fn all_classes(&self) -> Arc<Vec<Class>>;
|
||||
|
||||
/// Get the list of all fields
|
||||
fn all_fields(&self) -> Arc<Vec<Field>>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Interner;
|
||||
/// This function parses a dummy language with the following structure:
|
||||
///
|
||||
/// Classes are defined one per line, consisting of a comma-separated list of fields.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// lorem,ipsum
|
||||
/// dolor,sit,amet,
|
||||
/// consectetur,adipiscing,elit
|
||||
/// ```
|
||||
fn all_classes(db: &impl Compiler) -> Arc<Vec<Class>> {
|
||||
let string = db.input_string();
|
||||
|
||||
let rows = string.split('\n');
|
||||
let classes: Vec<_> = rows
|
||||
.filter(|string| !string.is_empty())
|
||||
.map(|string| {
|
||||
let fields = string
|
||||
.trim()
|
||||
.split(',')
|
||||
.filter(|string| !string.is_empty())
|
||||
.map(|name_str| {
|
||||
let name = name_str.to_owned();
|
||||
let field_data = FieldData { name };
|
||||
db.intern_field(field_data)
|
||||
})
|
||||
.collect();
|
||||
let class_data = ClassData { fields };
|
||||
db.intern_class(class_data)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Arc::new(classes)
|
||||
}
|
||||
|
||||
fn fields(db: &impl Compiler, class: Class) -> Arc<Vec<Field>> {
|
||||
let class = db.lookup_intern_class(class);
|
||||
let fields = class.fields.clone();
|
||||
Arc::new(fields)
|
||||
}
|
||||
|
||||
fn all_fields(db: &impl Compiler) -> Arc<Vec<Field>> {
|
||||
Arc::new(
|
||||
db.all_classes()
|
||||
.iter()
|
||||
.cloned()
|
||||
.flat_map(|class| {
|
||||
let fields = db.fields(class);
|
||||
(0..fields.len()).map(move |i| fields[i])
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::class_table;
|
||||
use crate::compiler::{CompilerDatabase, Interner};
|
||||
use crate::compiler::CompilerDatabase;
|
||||
use crate::interner::InternerDatabase;
|
||||
|
||||
/// Our "database" will be threaded through our application (though
|
||||
/// 99% of the application only interacts with it through traits and
|
||||
|
@ -13,16 +13,10 @@ use crate::compiler::{CompilerDatabase, Interner};
|
|||
/// to your context (e.g., a shared counter or some such thing). If
|
||||
/// mutations to that shared state affect the results of your queries,
|
||||
/// that's going to mess up the incremental results.
|
||||
#[salsa::database(class_table::ClassTable)]
|
||||
#[salsa::database(InternerDatabase, CompilerDatabase)]
|
||||
#[derive(Default)]
|
||||
pub struct DatabaseImpl {
|
||||
runtime: salsa::Runtime<DatabaseImpl>,
|
||||
|
||||
/// An interner is an example of shared mutable state that would
|
||||
/// be ok: although the interner allocates internally when you
|
||||
/// intern something new, this never affects any previously
|
||||
/// interned values, so it's not going to affect query results.
|
||||
interner: Interner,
|
||||
}
|
||||
|
||||
/// This impl tells salsa where to find the salsa runtime.
|
||||
|
@ -35,12 +29,3 @@ impl salsa::Database for DatabaseImpl {
|
|||
&mut self.runtime
|
||||
}
|
||||
}
|
||||
|
||||
/// In addition to the "query provider" traits, you may have other
|
||||
/// trait requirements that your application needs -- you can
|
||||
/// implement those yourself (in this case, an `interner`).
|
||||
impl CompilerDatabase for DatabaseImpl {
|
||||
fn interner(&self) -> &Interner {
|
||||
&self.interner
|
||||
}
|
||||
}
|
||||
|
|
10
examples/compiler/interner.rs
Normal file
10
examples/compiler/interner.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use crate::values::*;
|
||||
|
||||
#[salsa::query_group(InternerDatabase)]
|
||||
pub trait Interner: salsa::Database {
|
||||
#[salsa::interned]
|
||||
fn intern_field(&self, field: FieldData) -> Field;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_class(&self, class: ClassData) -> Class;
|
||||
}
|
|
@ -1,23 +1,40 @@
|
|||
mod class_table;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod compiler;
|
||||
mod implementation;
|
||||
mod interner;
|
||||
mod values;
|
||||
|
||||
use self::class_table::ClassTableDatabase;
|
||||
use self::compiler::Compiler;
|
||||
use self::implementation::DatabaseImpl;
|
||||
use self::interner::Interner;
|
||||
|
||||
static INPUT_STR: &'static str = r#"
|
||||
lorem,ipsum
|
||||
dolor,sit,amet,
|
||||
consectetur,adipiscing,elit
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let query = DatabaseImpl::default();
|
||||
let all_def_ids = query.all_fields();
|
||||
let mut db = DatabaseImpl::default();
|
||||
|
||||
db.set_input_string(Arc::new(INPUT_STR.to_owned()));
|
||||
|
||||
let all_fields = db.all_fields();
|
||||
assert_eq!(
|
||||
format!("{:?}", all_def_ids),
|
||||
"[DefId(1), DefId(2), DefId(11), DefId(12)]"
|
||||
format!("{:?}", all_fields),
|
||||
"[Field(0), Field(1), Field(2), Field(3), Field(4), Field(5), Field(6), Field(7)]"
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let query = DatabaseImpl::default();
|
||||
for f in query.all_fields().iter() {
|
||||
println!("{:?}", f);
|
||||
let mut db = DatabaseImpl::default();
|
||||
|
||||
db.set_input_string(Arc::new(INPUT_STR.to_owned()));
|
||||
|
||||
for field in db.all_fields().iter() {
|
||||
let field_data = db.lookup_intern_field(*field);
|
||||
println!("{:?} => {:?}", field, field_data);
|
||||
}
|
||||
}
|
||||
|
|
35
examples/compiler/values.rs
Normal file
35
examples/compiler/values.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct ClassData {
|
||||
pub fields: Vec<Field>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Class(salsa::InternId);
|
||||
|
||||
impl salsa::InternKey for Class {
|
||||
fn from_intern_id(id: salsa::InternId) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
|
||||
fn as_intern_id(&self) -> salsa::InternId {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct FieldData {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Field(salsa::InternId);
|
||||
|
||||
impl salsa::InternKey for Field {
|
||||
fn from_intern_id(id: salsa::InternId) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
|
||||
fn as_intern_id(&self) -> salsa::InternId {
|
||||
self.0
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue