Impl Eq for syntax tree types

This commit is contained in:
Laurenz 2023-02-17 10:01:40 +01:00
parent 80e73979f3
commit 56b6a2a908
5 changed files with 47 additions and 57 deletions

View File

@ -29,7 +29,7 @@ pub trait AstNode: Sized {
macro_rules! node { macro_rules! node {
($(#[$attr:meta])* $name:ident) => { ($(#[$attr:meta])* $name:ident) => {
#[derive(Debug, Default, Clone, PartialEq, Hash)] #[derive(Debug, Default, Clone, Hash)]
#[repr(transparent)] #[repr(transparent)]
$(#[$attr])* $(#[$attr])*
pub struct $name(SyntaxNode); pub struct $name(SyntaxNode);
@ -73,7 +73,7 @@ impl Markup {
} }
/// An expression in markup, math or code. /// An expression in markup, math or code.
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, Hash)]
pub enum Expr { pub enum Expr {
/// Plain text without markup. /// Plain text without markup.
Text(Text), Text(Text),
@ -1055,7 +1055,7 @@ impl Array {
} }
/// An item in an array. /// An item in an array.
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, Hash)]
pub enum ArrayItem { pub enum ArrayItem {
/// A bare expression: `12`. /// A bare expression: `12`.
Pos(Expr), Pos(Expr),
@ -1092,7 +1092,7 @@ impl Dict {
} }
/// An item in an dictionary expresssion. /// An item in an dictionary expresssion.
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, Hash)]
pub enum DictItem { pub enum DictItem {
/// A named pair: `thickness: 3pt`. /// A named pair: `thickness: 3pt`.
Named(Named), Named(Named),
@ -1452,7 +1452,7 @@ impl Args {
} }
/// An argument to a function call. /// An argument to a function call.
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, Hash)]
pub enum Arg { pub enum Arg {
/// A positional argument: `12`. /// A positional argument: `12`.
Pos(Expr), Pos(Expr),
@ -1509,7 +1509,7 @@ impl Closure {
} }
/// A parameter to a closure. /// A parameter to a closure.
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, Hash)]
pub enum Param { pub enum Param {
/// A positional parameter: `x`. /// A positional parameter: `x`.
Pos(Ident), Pos(Ident),
@ -1724,7 +1724,7 @@ impl ModuleImport {
} }
/// The items that ought to be imported from a file. /// The items that ought to be imported from a file.
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, Hash)]
pub enum Imports { pub enum Imports {
/// All items in the scope of the file should be imported. /// All items in the scope of the file should be imported.
Wildcard, Wildcard,

View File

@ -9,11 +9,11 @@ use crate::diag::SourceError;
use crate::util::EcoString; use crate::util::EcoString;
/// A node in the untyped syntax tree. /// A node in the untyped syntax tree.
#[derive(Clone, PartialEq, Hash)] #[derive(Clone, Eq, PartialEq, Hash)]
pub struct SyntaxNode(Repr); pub struct SyntaxNode(Repr);
/// The three internal representations. /// The three internal representations.
#[derive(Clone, PartialEq, Hash)] #[derive(Clone, Eq, PartialEq, Hash)]
enum Repr { enum Repr {
/// A leaf node. /// A leaf node.
Leaf(LeafNode), Leaf(LeafNode),
@ -147,6 +147,15 @@ impl SyntaxNode {
.collect() .collect()
} }
} }
/// Set a synthetic span for the node and all its descendants.
pub fn synthesize(&mut self, span: Span) {
match &mut self.0 {
Repr::Leaf(leaf) => leaf.span = span,
Repr::Inner(inner) => Arc::make_mut(inner).synthesize(span),
Repr::Error(error) => Arc::make_mut(error).span = span,
}
}
} }
impl SyntaxNode { impl SyntaxNode {
@ -174,15 +183,6 @@ impl SyntaxNode {
*self = SyntaxNode::error(message, text, ErrorPos::Full); *self = SyntaxNode::error(message, text, ErrorPos::Full);
} }
/// Set a synthetic span for the node and all its descendants.
pub(crate) fn synthesize(&mut self, span: Span) {
match &mut self.0 {
Repr::Leaf(leaf) => leaf.span = span,
Repr::Inner(inner) => Arc::make_mut(inner).synthesize(span),
Repr::Error(error) => Arc::make_mut(error).span = span,
}
}
/// Assign spans to each node. /// Assign spans to each node.
pub(super) fn numberize( pub(super) fn numberize(
&mut self, &mut self,
@ -212,7 +212,7 @@ impl SyntaxNode {
} }
/// Whether this is a leaf node. /// Whether this is a leaf node.
pub(crate) fn is_leaf(&self) -> bool { pub(super) fn is_leaf(&self) -> bool {
matches!(self.0, Repr::Leaf(_)) matches!(self.0, Repr::Leaf(_))
} }
@ -291,7 +291,7 @@ impl Default for SyntaxNode {
} }
/// A leaf node in the untyped syntax tree. /// A leaf node in the untyped syntax tree.
#[derive(Clone, Hash)] #[derive(Clone, Eq, PartialEq, Hash)]
struct LeafNode { struct LeafNode {
/// What kind of node this is (each kind would have its own struct in a /// What kind of node this is (each kind would have its own struct in a
/// strongly typed AST). /// strongly typed AST).
@ -322,14 +322,8 @@ impl Debug for LeafNode {
} }
} }
impl PartialEq for LeafNode {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind && self.text == other.text
}
}
/// An inner node in the untyped syntax tree. /// An inner node in the untyped syntax tree.
#[derive(Clone, Hash)] #[derive(Clone, Eq, PartialEq, Hash)]
struct InnerNode { struct InnerNode {
/// What kind of node this is (each kind would have its own struct in a /// What kind of node this is (each kind would have its own struct in a
/// strongly typed AST). /// strongly typed AST).
@ -378,6 +372,7 @@ impl InnerNode {
/// Set a synthetic span for the node and all its descendants. /// Set a synthetic span for the node and all its descendants.
fn synthesize(&mut self, span: Span) { fn synthesize(&mut self, span: Span) {
self.span = span; self.span = span;
self.upper = span.number();
for child in &mut self.children { for child in &mut self.children {
child.synthesize(span); child.synthesize(span);
} }
@ -573,18 +568,8 @@ impl Debug for InnerNode {
} }
} }
impl PartialEq for InnerNode {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind
&& self.len == other.len
&& self.descendants == other.descendants
&& self.erroneous == other.erroneous
&& self.children == other.children
}
}
/// An error node in the untyped syntax tree. /// An error node in the untyped syntax tree.
#[derive(Clone, Hash)] #[derive(Clone, Eq, PartialEq, Hash)]
struct ErrorNode { struct ErrorNode {
/// The error message. /// The error message.
message: EcoString, message: EcoString,
@ -623,12 +608,6 @@ impl Debug for ErrorNode {
} }
} }
impl PartialEq for ErrorNode {
fn eq(&self, other: &Self) -> bool {
self.message == other.message && self.text == other.text && self.pos == other.pos
}
}
/// Where in a node an error should be annotated, /// Where in a node an error should be annotated,
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ErrorPos { pub enum ErrorPos {

View File

@ -213,16 +213,18 @@ fn next_nesting(node: &SyntaxNode, nesting: &mut usize) {
mod tests { mod tests {
use std::ops::Range; use std::ops::Range;
use super::super::{parse, Source}; use super::super::{parse, Source, Span};
#[track_caller] #[track_caller]
fn test(prev: &str, range: Range<usize>, with: &str, incremental: bool) { fn test(prev: &str, range: Range<usize>, with: &str, incremental: bool) {
let mut source = Source::detached(prev); let mut source = Source::detached(prev);
let prev = source.root().clone(); let prev = source.root().clone();
let range = source.edit(range, with); let range = source.edit(range, with);
let found = source.root(); let mut found = source.root().clone();
let expected = parse(source.text()); let mut expected = parse(source.text());
if found != &expected { found.synthesize(Span::detached());
expected.synthesize(Span::detached());
if found != expected {
eprintln!("source: {:?}", source.text()); eprintln!("source: {:?}", source.text());
eprintln!("previous: {prev:#?}"); eprintln!("previous: {prev:#?}");
eprintln!("expected: {expected:#?}"); eprintln!("expected: {expected:#?}");

View File

@ -406,6 +406,8 @@ mod tests {
#[test] #[test]
fn test_source_file_edit() { fn test_source_file_edit() {
// This tests only the non-parser parts. The reparsing itself is
// tested separately.
#[track_caller] #[track_caller]
fn test(prev: &str, range: Range<usize>, with: &str, after: &str) { fn test(prev: &str, range: Range<usize>, with: &str, after: &str) {
let mut source = Source::detached(prev); let mut source = Source::detached(prev);
@ -413,7 +415,6 @@ mod tests {
source.edit(range, with); source.edit(range, with);
assert_eq!(source.text, result.text); assert_eq!(source.text, result.text);
assert_eq!(source.lines, result.lines); assert_eq!(source.lines, result.lines);
assert_eq!(*source.root, *result.root);
} }
// Test inserting at the begining. // Test inserting at the begining.

View File

@ -16,7 +16,7 @@ use typst::doc::{Document, Element, Frame, Meta};
use typst::font::{Font, FontBook}; use typst::font::{Font, FontBook};
use typst::geom::{Abs, RgbaColor, Sides, Smart}; use typst::geom::{Abs, RgbaColor, Sides, Smart};
use typst::model::{func, Library, Value}; use typst::model::{func, Library, Value};
use typst::syntax::{Source, SourceId, SyntaxNode}; use typst::syntax::{Source, SourceId, Span, SyntaxNode};
use typst::util::{Buffer, PathExt}; use typst::util::{Buffer, PathExt};
use typst::World; use typst::World;
use typst_library::layout::PageNode; use typst_library::layout::PageNode;
@ -591,11 +591,21 @@ fn test_reparse(text: &str, i: usize, rng: &mut LinearShift) -> bool {
incr_source.edit(replace.clone(), with); incr_source.edit(replace.clone(), with);
let edited_src = incr_source.text(); let edited_src = incr_source.text();
let incr_root = incr_source.root();
let ref_source = Source::detached(edited_src); let ref_source = Source::detached(edited_src);
let ref_root = ref_source.root(); let mut ref_root = ref_source.root().clone();
let mut ok = incr_root == ref_root; let mut incr_root = incr_source.root().clone();
if !ok {
// Ensures that the span numbering invariants hold.
let spans_ok = test_spans(&ref_root) && test_spans(&incr_root);
// Remove all spans so that the comparison works out.
let tree_ok = {
ref_root.synthesize(Span::detached());
incr_root.synthesize(Span::detached());
ref_root == incr_root
};
if !tree_ok {
println!( println!(
" Subtest {i} reparse differs from clean parse when inserting '{with}' at {}-{} ❌\n", " Subtest {i} reparse differs from clean parse when inserting '{with}' at {}-{} ❌\n",
replace.start, replace.end, replace.start, replace.end,
@ -605,9 +615,7 @@ fn test_reparse(text: &str, i: usize, rng: &mut LinearShift) -> bool {
println!(" Full source ({}):\n\"{edited_src:?}\"", edited_src.len()); println!(" Full source ({}):\n\"{edited_src:?}\"", edited_src.len());
} }
ok &= test_spans(ref_root); spans_ok && tree_ok
ok &= test_spans(incr_root);
ok
}; };
let mut pick = |range: Range<usize>| { let mut pick = |range: Range<usize>| {