Skip to content

Commit bb6aaf6

Browse files
committed
yeast: Distinguish between input and output IDs
As part of the input/output AST split, we need to distinguish between nodes that live in the input AST arena, and ones that live in the output AST arena. To keep these separate, we use different types (both wrapping `usize`) to represent their IDs. Because most of the associated machinery is the same for both node types, we make these generic in the type of ID. This does mean a fair amount of churn in function definitions etc., but I've endeavoured to keep this to a minimum by using `Id` for the input AST. The output AST uses -- you guessed it -- `OutputId`.
1 parent 5be6d86 commit bb6aaf6

4 files changed

Lines changed: 140 additions & 67 deletions

File tree

‎shared/tree-sitter-extractor/src/extractor/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ impl<'a> AstNode for Node<'a> {
6666
}
6767
}
6868

69-
impl AstNode for yeast::Node {
69+
impl<I: yeast::TreeId> AstNode for yeast::Node<I> {
7070
fn kind(&self) -> &str {
7171
yeast::Node::kind_name(self)
7272
}

‎shared/yeast/src/dump.rs‎

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fmt::Write;
22

3-
use crate::{schema::Schema, Ast, Id, Node, NodeContent, CHILD_FIELD};
3+
use crate::{schema::Schema, Ast, Node, NodeContent, TreeId, CHILD_FIELD};
44

55
#[derive(Clone, Copy)]
66
struct TypeCheckContext<'a> {
@@ -45,11 +45,16 @@ impl Default for DumpOptions {
4545
/// method:
4646
/// identifier "foo"
4747
/// ```
48-
pub fn dump_ast(ast: &Ast, root: Id, source: &str) -> String {
48+
pub fn dump_ast<Id: TreeId>(ast: &Ast<Id>, root: Id, source: &str) -> String {
4949
dump_ast_with_options(ast, root, source, &DumpOptions::default())
5050
}
5151

52-
pub fn dump_ast_with_options(ast: &Ast, root: Id, source: &str, options: &DumpOptions) -> String {
52+
pub fn dump_ast_with_options<Id: TreeId>(
53+
ast: &Ast<Id>,
54+
root: Id,
55+
source: &str,
56+
options: &DumpOptions,
57+
) -> String {
5358
let mut out = String::new();
5459
dump_node(ast, root, source, options, 0, None, false, &mut out);
5560
out
@@ -59,16 +64,21 @@ pub fn dump_ast_with_options(ast: &Ast, root: Id, source: &str, options: &DumpOp
5964
///
6065
/// Any node that does not match the expected type set for its parent field is
6166
/// rendered with a trailing `" <-- ERROR: ..."` annotation on the same line.
62-
pub fn dump_ast_with_type_errors(ast: &Ast, root: Id, source: &str, schema: &Schema) -> String {
67+
pub fn dump_ast_with_type_errors<Id: TreeId>(
68+
ast: &Ast<Id>,
69+
root: Id,
70+
source: &str,
71+
schema: &Schema,
72+
) -> String {
6373
dump_ast_with_type_errors_and_options(ast, root, source, schema, &DumpOptions::default())
6474
}
6575

6676
/// Dump an AST and annotate type mismatches against a schema inline.
6777
///
6878
/// Any node that does not match the expected type set for its parent field is
6979
/// rendered with a trailing `" <-- ERROR: ..."` annotation on the same line.
70-
pub fn dump_ast_with_type_errors_and_options(
71-
ast: &Ast,
80+
pub fn dump_ast_with_type_errors_and_options<Id: TreeId>(
81+
ast: &Ast<Id>,
7282
root: Id,
7383
source: &str,
7484
schema: &Schema,
@@ -119,9 +129,9 @@ const EMPTY_NODE_TYPES: &[crate::schema::NodeType] = &[];
119129
/// # Returns
120130
/// `Some(error_message)` if the node violates the schema (e.g., wrong kind, missing field declaration).
121131
/// `None` if the node matches the expected types or if type-checking is disabled.
122-
fn type_error_for_node(
132+
fn type_error_for_node<Id: TreeId>(
123133
schema: &Schema,
124-
node: &Node,
134+
node: &Node<Id>,
125135
expected: Option<&[crate::schema::NodeType]>,
126136
parent_field: Option<(&str, &str)>,
127137
) -> Option<String> {
@@ -189,8 +199,8 @@ fn expected_for_field<'a>(
189199
.map(|v| v.as_slice())
190200
}
191201

192-
fn dump_node(
193-
ast: &Ast,
202+
fn dump_node<Id: TreeId>(
203+
ast: &Ast<Id>,
194204
id: Id,
195205
source: &str,
196206
options: &DumpOptions,
@@ -402,8 +412,8 @@ fn dump_node(
402412
}
403413

404414
/// Dump a leaf node inline (no newline prefix, caller provides context).
405-
fn dump_node_inline(
406-
ast: &Ast,
415+
fn dump_node_inline<Id: TreeId>(
416+
ast: &Ast<Id>,
407417
id: Id,
408418
source: &str,
409419
options: &DumpOptions,
@@ -457,7 +467,7 @@ fn dump_node_inline(
457467
writeln!(out).unwrap();
458468
}
459469

460-
fn is_leaf(node: &Node) -> bool {
470+
fn is_leaf<Id: TreeId>(node: &Node<Id>) -> bool {
461471
node.fields.is_empty()
462472
}
463473

@@ -467,29 +477,34 @@ enum SourceSkeleton {
467477
Invalid(String),
468478
}
469479

470-
fn write_source_skeleton(ast: &Ast, node: &Node, source: &str, out: &mut String) {
480+
fn write_source_skeleton<Id: TreeId>(
481+
ast: &Ast<Id>,
482+
node: &Node<Id>,
483+
source: &str,
484+
out: &mut String,
485+
) {
471486
match source_skeleton(ast, node, source) {
472487
SourceSkeleton::Missing => write!(out, " source=<no location>").unwrap(),
473488
SourceSkeleton::Text(text) => write!(out, " source={text:?}").unwrap(),
474489
SourceSkeleton::Invalid(error) => write!(out, " source=<invalid: {error}>").unwrap(),
475490
}
476491
}
477492

478-
fn node_source_range(node: &Node) -> Option<crate::Range> {
493+
fn node_source_range<Id: TreeId>(node: &Node<Id>) -> Option<crate::Range> {
479494
match node.content {
480495
NodeContent::Range(range) => Some(range),
481496
_ => node.source_range,
482497
}
483498
}
484499

485-
fn is_external_child(parent: &Node, child: &Node) -> bool {
500+
fn is_external_child<Id: TreeId>(parent: &Node<Id>, child: &Node<Id>) -> bool {
486501
let (Some(parent), Some(child)) = (node_source_range(parent), node_source_range(child)) else {
487502
return false;
488503
};
489504
child.start_byte < parent.start_byte || child.end_byte > parent.end_byte
490505
}
491506

492-
fn source_skeleton(ast: &Ast, node: &Node, source: &str) -> SourceSkeleton {
507+
fn source_skeleton<Id: TreeId>(ast: &Ast<Id>, node: &Node<Id>, source: &str) -> SourceSkeleton {
493508
let Some(parent) = node_source_range(node) else {
494509
return SourceSkeleton::Missing;
495510
};
@@ -551,7 +566,7 @@ fn source_skeleton(ast: &Ast, node: &Node, source: &str) -> SourceSkeleton {
551566
SourceSkeleton::Text(result)
552567
}
553568

554-
fn node_content(node: &Node, source: &str) -> String {
569+
fn node_content<Id: TreeId>(node: &Node<Id>, source: &str) -> String {
555570
match &node.content {
556571
NodeContent::DynamicString(s) if !s.is_empty() => s.clone(),
557572
_ => {
@@ -568,7 +583,7 @@ fn node_content(node: &Node, source: &str) -> String {
568583
#[cfg(test)]
569584
mod tests {
570585
use super::*;
571-
use crate::{NodeContent, Point, Range};
586+
use crate::{NodeContent, OutputAst, Point, Range};
572587
use std::collections::BTreeMap;
573588

574589
fn range(start: usize, end: usize) -> Range {
@@ -581,7 +596,7 @@ mod tests {
581596
}
582597

583598
fn dump_with_children(source: &str, parent_range: Range, children: &[(&str, Range)]) -> String {
584-
let mut ast = Ast::with_schema(crate::schema::Schema::new());
599+
let mut ast = OutputAst::with_schema(crate::schema::Schema::new());
585600
let parent_kind = ast.register_kind("parent");
586601
let child_kind = ast.register_kind("child");
587602
let mut fields = BTreeMap::new();

0 commit comments

Comments
 (0)