Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
41b9122
wip
alanpq Aug 21, 2026
048e0b2
wip: ast bench
alanpq Aug 21, 2026
c345c45
wip: port tests
alanpq Aug 21, 2026
6932ce1
wip: refactor
alanpq Aug 21, 2026
e1911f1
wip: make ast default on
alanpq Aug 21, 2026
a39d3df
wip: fix
alanpq Aug 21, 2026
a3bbb12
feat: impl DIsplay for AstValue
alanpq Aug 21, 2026
9090106
feat: better ast query api
alanpq Aug 21, 2026
99a05e2
fix: derive some debug/clone + add NodeKind
alanpq Aug 21, 2026
97b2d74
feat: ast visitor
alanpq Aug 22, 2026
7cb12cf
feat: even better ast query api
alanpq Aug 23, 2026
73ec8c1
fix: to_bin_value api
alanpq Aug 23, 2026
e8a6721
wip
alanpq Aug 23, 2026
73adc3d
wip: begin typecheck kil
alanpq Aug 23, 2026
8e0bc08
wip: gap fill
alanpq Aug 23, 2026
a40b2eb
typcheck is kil
alanpq Aug 23, 2026
ee5ed7e
chore: lint
alanpq Aug 23, 2026
811a52e
fix: new ast visitor semantics
alanpq Aug 26, 2026
e126869
feat: useful traits for Spanned/Span
alanpq Aug 26, 2026
5441853
feat: richer hash literal struct, change astvalue enum
alanpq Aug 26, 2026
e34f08f
fix: don't unwrap when getting option subtype
alanpq Aug 27, 2026
ee3b83e
chore: clippy
alanpq Aug 27, 2026
59238dd
refactor: move things around
alanpq Aug 27, 2026
82813cd
refactor: rename ast node struct names
alanpq Aug 27, 2026
c4419fa
refactor!: new ast visitor api
alanpq Aug 27, 2026
abe8746
refactor: small renames
alanpq Aug 27, 2026
12165ad
chore: cargo fix
alanpq Aug 27, 2026
546c228
feat: move ChildrenExt & impl it for ChildRange
alanpq Aug 27, 2026
f263ce2
refactor: move coerce module
alanpq Aug 27, 2026
8a23766
refactor: more moving
alanpq Aug 27, 2026
bef269e
refactor: more moves & rename RootObject -> RootEntry
alanpq Aug 27, 2026
188b694
refactor: move NodeRef::span
alanpq Aug 27, 2026
6e3fb0a
feat: useful Span methods/functions
alanpq Aug 28, 2026
196b79a
wip: make ast more partial
alanpq Aug 28, 2026
5174770
feat: add Unresolved value
alanpq Aug 28, 2026
51bea3c
test: tweak
alanpq Aug 28, 2026
6ace37a
fix: resolve null inside literal eval
alanpq Aug 28, 2026
35c04a7
feat: add Value::Unknown & RitoTypeOrVirtual::Unknown
alanpq Aug 28, 2026
b1cccb0
test: put cst printing in build_errs behind flag
alanpq Aug 28, 2026
ab44f16
feat: resolve_literal that returns Unknown/Unresolved
alanpq Aug 28, 2026
efcd036
fix: don't remove ritobin subtypes that are impossible
alanpq Aug 28, 2026
8199edf
fix: check entire RitoType when validating entries
alanpq Aug 28, 2026
e69cf30
test: add bad option coerce test
alanpq Aug 28, 2026
0be23e6
test: don't assert only 1 err for nested_container_fails
alanpq Aug 28, 2026
8448cdc
chore: fix/fmt
alanpq Aug 28, 2026
b13adf4
feat: allow unresolved type expressions in entry resolution
alanpq Aug 29, 2026
a63d66e
feat: add Span::contains_inclusive
alanpq Aug 29, 2026
0d898b9
fix: use contains_inclusive in path queries
alanpq Aug 29, 2026
e5094d7
fix: impl Copy for SubNodeRef
alanpq Aug 29, 2026
34be5bb
feat: SpannedExt helper trait
alanpq Aug 29, 2026
d85e210
feat: better type expr handling for ast properties
alanpq Aug 29, 2026
7b40446
fix: better entry type expr span in fail cases
alanpq Aug 29, 2026
d1dd37e
refactor: rename leftovers from the Object/Struct/RootEntry renaming
alanpq Aug 29, 2026
287ceba
feat: Value::is_containerlike
alanpq Aug 29, 2026
106bddf
fix(ritobin)!: reject invalid map key types at the declaration
Crauzer Aug 30, 2026
f3760dd
fix(ritobin): adapt to the 0.8.0 fallible map and optional constructors
Crauzer Aug 30, 2026
50a6e8a
refactor(ritobin): ask is_valid_map_key for map key validity
Crauzer Aug 30, 2026
0a1448a
refactor: reorder stuff in node::refs
alanpq Aug 30, 2026
1284635
feat: ritobin helper macro + other construction methods
alanpq Sep 3, 2026
e560e9b
feat(meta): promote methods on PropertyKind to const
alanpq Sep 3, 2026
43c116a
wip: roots
alanpq Sep 3, 2026
71d17fc
refactor: split into coerce_to & try_coerce_to
alanpq Sep 3, 2026
eaf758d
feat: Value::into_string
alanpq Sep 3, 2026
8adc33b
refactor: better ast root system
alanpq Sep 3, 2026
8b86db8
chore: fix
alanpq Sep 3, 2026
f13ac51
fix: make rito macro more hygenic
alanpq Sep 3, 2026
d3ee672
refactor
alanpq Sep 3, 2026
9a20394
fix: replace old RootKind, remove unused struct
alanpq Sep 3, 2026
e930f14
fix: make query work again
alanpq Sep 3, 2026
7a24d23
feat: ok actually good roots
alanpq Sep 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"rust-analyzer.cargo.allTargets": true
"rust-analyzer.cargo.allTargets": true,
"nixEnvSelector.suggestion": false
}
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ insta = { version = "1.39.0", features = ["ron"] }
camino = "1.2.1"

pretty_assertions = "1.4.1"
proptest = "1.5.0"

criterion = {version = "0.8", features = ["html_reports"] }

Expand Down
8 changes: 4 additions & 4 deletions crates/ltk_meta/src/property/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl Kind {
/// Whether this property kind is a primitive type. (i8, u8, .. u32, u64, f32, Vector2, Vector3, Vector4, Matrix44, Color, String, Hash, WadChunkLink),
#[inline(always)]
#[must_use]
pub fn is_primitive(&self) -> bool {
pub const fn is_primitive(&self) -> bool {
use Kind::*;
matches!(
self,
Expand Down Expand Up @@ -123,7 +123,7 @@ impl Kind {
/// shipped bin in the client keys a map on any of them.
#[inline(always)]
#[must_use]
pub fn is_valid_map_key(&self) -> bool {
pub const fn is_valid_map_key(&self) -> bool {
use Kind::*;
matches!(
self,
Expand Down Expand Up @@ -151,13 +151,13 @@ impl Kind {
/// Whether this property kind is a container type (container, unordered container, optional, map).
#[inline(always)]
#[must_use]
pub fn is_container(&self) -> bool {
pub const fn is_container(&self) -> bool {
self.subtype_count() > 0
}

#[inline(always)]
#[must_use]
pub fn subtype_count(&self) -> u8 {
pub const fn subtype_count(&self) -> u8 {
use Kind::*;
match self {
Container | UnorderedContainer | Optional => 1,
Expand Down
8 changes: 7 additions & 1 deletion crates/ltk_ritobin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@ harness = false
name = "e2e"
harness = false

[[bench]]
name = "ast"
harness = false

[lints]
workspace = true

[features]
default = []
debug = []
serde = ["dep:serde", "ltk_meta/serde"]
salsa = ["dep:salsa"]

[dependencies]
nom = "7.1"
Expand All @@ -46,11 +51,12 @@ ltk_hash = { version = "0.4.0", path = "../ltk_hash" }
ltk_primitives = { version = "0.3.5", path = "../ltk_primitives" }

serde = { workspace = true, optional = true }
salsa = "0.22.0"
salsa = { version = "0.22.0", optional = true }

[dev-dependencies]
insta.workspace = true
pretty_assertions.workspace = true
proptest.workspace = true
serde.workspace = true
criterion.workspace = true
ltk_ritobin = { path = ".", features = ["serde"] }
Expand Down
86 changes: 86 additions & 0 deletions crates/ltk_ritobin/benches/ast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//! Compares the two typecheckers' phases against the same parsed input: `typecheck::walk`
//! (`Cst::build_bin`, CST -> `Bin` in one pass) vs. the new `ast` engine's two halves
//! (`Cst::build_ast`, CST -> `Ast`; and `Ast::to_bin`, `Ast` -> `Bin`) - see the crate's design
//! notes for why `ast` is a second, independent implementation rather than a shared pipeline.
//!
//! Parsing itself is excluded (see `benches/parse.rs`): the `Cst` (and, for the `ast_to_bin`
//! group, the `Ast`) is built once outside every timed closure, so each group measures only the
//! one phase it names.

use std::fs::read_to_string;

use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use ltk_ritobin::Cst;

fn criterion_benchmark(c: &mut Criterion) {
let dir = env!("CARGO_MANIFEST_DIR");
let samples = [
read_to_string(format!("{dir}/samples/aatrox.rito")).unwrap(),
read_to_string(format!("{dir}/samples/azirultsoldier.rito")).unwrap(),
read_to_string(format!("{dir}/samples/big.rito")).unwrap(),
read_to_string(format!("{dir}/samples/skin38.rito")).unwrap(),
read_to_string(format!("{dir}/samples/test.rito")).unwrap(),
read_to_string(format!("{dir}/samples/zaahen.rito")).unwrap(),
];

{
let mut group = c.benchmark_group("cst_to_bin");
for sample in &samples {
let size = sample.len();
let cst = Cst::parse(sample);

group.throughput(Throughput::Bytes(size.try_into().unwrap()));
group.bench_with_input(
BenchmarkId::from_parameter(size),
&(cst, sample),
|b, (cst, sample)| {
b.iter(|| {
let _partial = std::hint::black_box(cst.build_bin(sample));
})
},
);
}
}

{
let mut group = c.benchmark_group("cst_to_ast");
for sample in &samples {
let size = sample.len();
let cst = Cst::parse(sample);

group.throughput(Throughput::Bytes(size.try_into().unwrap()));
group.bench_with_input(
BenchmarkId::from_parameter(size),
&(cst, sample),
|b, (cst, sample)| {
b.iter(|| {
let _ast = std::hint::black_box(cst.build_ast(sample));
})
},
);
}
}

{
let mut group = c.benchmark_group("ast_to_bin");
for sample in &samples {
let size = sample.len();
let cst = Cst::parse(sample);
let ast = cst.build_ast(sample);

group.throughput(Throughput::Bytes(size.try_into().unwrap()));
group.bench_with_input(
BenchmarkId::from_parameter(size),
&(ast, sample),
|b, (ast, sample)| {
b.iter(|| {
let _bin = std::hint::black_box(ast.to_bin(sample));
})
},
);
}
}
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
4 changes: 2 additions & 2 deletions crates/ltk_ritobin/benches/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ fn criterion_benchmark(c: &mut Criterion) {

fn e2e(txt: &str) -> Cst {
let cst = Cst::parse(txt);
let (bin, _errs) = cst.build_bin(txt);
let partial = cst.build_bin(txt);
let mut str = String::new();
bin.print_to_writer(&mut str).unwrap();
partial.bin.print_to_writer(&mut str).unwrap();
cst
}

Expand Down
2 changes: 1 addition & 1 deletion crates/ltk_ritobin/benches/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fn criterion_benchmark(c: &mut Criterion) {
&(cst, sample),
|b, (cst, sample)| {
b.iter(|| {
let (_bin, _errs) = black_box(cst.build_bin(sample));
let _partial = black_box(cst.build_bin(sample));
})
},
);
Expand Down
2 changes: 1 addition & 1 deletion crates/ltk_ritobin/benches/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn criterion_benchmark(c: &mut Criterion) {
for sample in &samples {
let size = sample.len();
let cst = Cst::parse(sample);
let (bin, _errs) = cst.build_bin(sample);
let bin = cst.build_bin(sample).bin;

group.throughput(Throughput::Bytes(size.try_into().unwrap()));
group.bench_with_input(BenchmarkId::from_parameter(size), &bin, |b, bin| {
Expand Down
16 changes: 9 additions & 7 deletions crates/ltk_ritobin/examples/rito_to_bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ fn main() {
return;
}

let (bin, errors) = cst.build_bin(&text);
if !errors.is_empty() {
eprintln!("Errors while converting to bin:");
for err in errors {
eprintln!("- {err:#?}");
let bin = match cst.build_bin(&text).into_result() {
Ok(bin) => bin,
Err(partial) => {
eprintln!("Errors while converting to bin:");
for diag in &partial.diagnostics {
eprintln!("- {diag:#?}");
}
return;
}
return;
}
};

let mut file = File::create(output_path).unwrap();
bin.to_writer(&mut file).unwrap();
Expand Down
44 changes: 44 additions & 0 deletions crates/ltk_ritobin/src/ast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
pub mod builder;
pub mod diagnostics;
pub mod hash;
pub mod node;
pub mod query;
pub mod resolve;
pub mod visitor;

mod to_bin;

#[cfg(test)]
mod tests;

pub use crate::Spanned;
pub use node::{Object, Property, RootEntry, Value};
pub use to_bin::PartialBin;

use crate::{
ast::{diagnostics::DiagnosticWithSpan, node::roots::Roots},
Cst,
};

#[cfg(not(feature = "salsa"))]
pub(crate) type Ptr<T> = Box<T>;
#[cfg(feature = "salsa")]
pub(crate) type Ptr<T> = std::sync::Arc<T>;

#[derive(Debug, Clone)]
pub struct Ast {
pub roots: Roots,
pub diagnostics: Vec<DiagnosticWithSpan>,
}

impl Ast {
pub fn root_entries(&self) -> impl Iterator<Item = &RootEntry> {
self.roots.entries().unwrap_or_default().iter()
}
}

impl Cst {
pub fn build_ast(&self, text: &str) -> crate::ast::Ast {
crate::ast::Ast::from_cst(self, text)
}
}
35 changes: 35 additions & 0 deletions crates/ltk_ritobin/src/ast/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::{
ast::{diagnostics::DiagnosticWithSpan, Ast},
cst::Cst,
};

mod root_entry;
pub use root_entry::*;

impl Ast {
pub fn from_cst(cst: &Cst, text: &str) -> Self {
let ctx = Builder {
cst,
text,
diagnostics: Vec::new(),
};
ctx.build()
}
}

#[derive(Debug, Clone)]
pub(super) struct Builder<'a> {
pub cst: &'a Cst,
pub text: &'a str,
pub diagnostics: Vec<DiagnosticWithSpan>,
}

impl<'a> Builder<'a> {
pub(super) fn cst(&self) -> &'a Cst {
self.cst
}

pub(super) fn push(&mut self, d: DiagnosticWithSpan) {
self.diagnostics.push(d);
}
}
Loading
Loading