Enforce and make use of span ordering

This commit is contained in:
Laurenz 2022-05-31 13:19:09 +02:00
parent 9bbebd69dd
commit 0a9172cb15
5 changed files with 80 additions and 28 deletions

View File

@ -1,6 +1,7 @@
//! Diagnostics. //! Diagnostics.
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::ops::Range;
use crate::syntax::{Span, Spanned}; use crate::syntax::{Span, Spanned};
@ -69,6 +70,17 @@ pub enum ErrorPos {
End, End,
} }
impl ErrorPos {
/// Apply this to a node's byte range.
pub fn apply(self, range: Range<usize>) -> Range<usize> {
match self {
ErrorPos::Start => range.start .. range.start,
ErrorPos::Full => range,
ErrorPos::End => range.end .. range.end,
}
}
}
/// A part of an error's [trace](Error::trace). /// A part of an error's [trace](Error::trace).
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum Tracepoint { pub enum Tracepoint {

View File

@ -169,7 +169,7 @@ impl SourceFile {
lines.extend(Line::iter(0, 0, &src)); lines.extend(Line::iter(0, 0, &src));
let mut root = parse(&src); let mut root = parse(&src);
root.number(id, 1); root.number(id, Span::MIN_NUMBER);
Self { Self {
id, id,
@ -243,7 +243,7 @@ impl SourceFile {
self.lines = vec![Line { byte_idx: 0, utf16_idx: 0 }]; self.lines = vec![Line { byte_idx: 0, utf16_idx: 0 }];
self.lines.extend(Line::iter(0, 0, &self.src)); self.lines.extend(Line::iter(0, 0, &self.src));
self.root = parse(&self.src); self.root = parse(&self.src);
self.root.number(self.id(), 1); self.root.number(self.id(), Span::MIN_NUMBER);
self.rev = self.rev.wrapping_add(1); self.rev = self.rev.wrapping_add(1);
} }
@ -278,7 +278,7 @@ impl SourceFile {
// Incrementally reparse the replaced range. // Incrementally reparse the replaced range.
let range = reparse(&mut self.root, &self.src, replace, with.len()); let range = reparse(&mut self.root, &self.src, replace, with.len());
self.root.number(self.id(), 1); self.root.number(self.id(), Span::MIN_NUMBER);
range range
} }

View File

@ -62,7 +62,9 @@ impl SyntaxNode {
pub fn range(&self, span: Span, offset: usize) -> Option<Range<usize>> { pub fn range(&self, span: Span, offset: usize) -> Option<Range<usize>> {
match self { match self {
SyntaxNode::Inner(inner) => inner.range(span, offset), SyntaxNode::Inner(inner) => inner.range(span, offset),
SyntaxNode::Leaf(leaf) => leaf.range(span, offset), SyntaxNode::Leaf(leaf) => {
(span == leaf.span).then(|| offset .. offset + self.len())
}
} }
} }
@ -242,13 +244,30 @@ impl InnerNode {
/// If the span points into this node, convert it to a byte range. /// If the span points into this node, convert it to a byte range.
pub fn range(&self, span: Span, mut offset: usize) -> Option<Range<usize>> { pub fn range(&self, span: Span, mut offset: usize) -> Option<Range<usize>> {
if let Some(range) = self.data.range(span, offset) { // Check whether we found it.
return Some(range); if self.data.span == span {
return Some(offset .. offset + self.len());
} }
for child in &self.children { // The parent of a subtree has a smaller span number than all of its
if let Some(range) = child.range(span, offset) { // descendants. Therefore, we can bail out early if the target span's
return Some(range); // number is smaller than our number.
if span.number() < self.span().number() {
return None;
}
let mut children = self.children.iter().peekable();
while let Some(child) = children.next() {
// Every node in this child's subtree has a smaller span number than
// the next sibling. Therefore we only need to recurse if the next
// sibling's span number is larger than the target span's number.
if children
.peek()
.map_or(true, |next| next.span().number() > span.number())
{
if let Some(range) = child.range(span, offset) {
return Some(range);
}
} }
offset += child.len(); offset += child.len();
@ -388,11 +407,6 @@ impl NodeData {
self.span self.span
} }
/// If the span points into this node, convert it to a byte range.
pub fn range(&self, span: Span, offset: usize) -> Option<Range<usize>> {
(self.span == span).then(|| offset .. offset + self.len())
}
/// Assign spans to each node. /// Assign spans to each node.
pub fn number(&mut self, id: SourceId, from: u64) { pub fn number(&mut self, id: SourceId, from: u64) {
self.span = Span::new(id, from); self.span = Span::new(id, from);

View File

@ -58,26 +58,37 @@ impl<T: Debug> Debug for Spanned<T> {
pub struct Span(NonZeroU64); pub struct Span(NonZeroU64);
impl Span { impl Span {
// Number of bits for and minimum and maximum numbers assigned to nodes.
const BITS: usize = 48;
const DETACHED: u64 = 1;
pub(crate) const MIN_NUMBER: u64 = 2;
pub(crate) const MAX_NUMBER: u64 = (1 << Self::BITS) - 1;
/// Create a new span from a source id and a unique number. /// Create a new span from a source id and a unique number.
pub const fn new(id: SourceId, number: u64) -> Self { pub const fn new(id: SourceId, number: u64) -> Self {
assert!(number > 0 && number < (1 << 48)); assert!(number >= Self::MIN_NUMBER && number <= Self::MAX_NUMBER);
let bits = ((id.into_raw() as u64) << 48) | number; let bits = ((id.into_raw() as u64) << Self::BITS) | number;
Self(nonzero(bits)) Self(convert(bits))
} }
/// A node that does not belong to any source file. /// A node that does not belong to any source file.
pub const fn detached() -> Self { pub const fn detached() -> Self {
Self(nonzero(1)) Self(convert(Self::DETACHED))
} }
/// The id of the source file the span points into. /// The id of the source file the span points into.
pub const fn source(self) -> SourceId { pub const fn source(self) -> SourceId {
SourceId::from_raw((self.0.get() >> 48) as u16) SourceId::from_raw((self.0.get() >> Self::BITS) as u16)
}
/// The unique number of the span within the source file.
pub const fn number(self) -> u64 {
self.0.get() & Self::MAX_NUMBER
} }
} }
/// Convert to a non zero u64. /// Convert to a non zero u64.
const fn nonzero(v: u64) -> NonZeroU64 { const fn convert(v: u64) -> NonZeroU64 {
match NonZeroU64::new(v) { match NonZeroU64::new(v) {
Some(v) => v, Some(v) => v,
None => unreachable!(), None => unreachable!(),

View File

@ -9,7 +9,6 @@ use tiny_skia as sk;
use unscanny::Scanner; use unscanny::Scanner;
use walkdir::WalkDir; use walkdir::WalkDir;
use typst::diag::ErrorPos;
use typst::eval::{Smart, Value}; use typst::eval::{Smart, Value};
use typst::frame::{Element, Frame}; use typst::frame::{Element, Frame};
use typst::geom::{Length, RgbaColor, Sides}; use typst::geom::{Length, RgbaColor, Sides};
@ -18,6 +17,7 @@ use typst::library::text::{TextNode, TextSize};
use typst::loading::FsLoader; use typst::loading::FsLoader;
use typst::model::StyleMap; use typst::model::StyleMap;
use typst::source::SourceFile; use typst::source::SourceFile;
use typst::syntax::SyntaxNode;
use typst::{bail, Config, Context}; use typst::{bail, Config, Context};
const TYP_DIR: &str = "./typ"; const TYP_DIR: &str = "./typ";
@ -295,6 +295,8 @@ fn test_part(
println!("Syntax Tree: {:#?}", source.root()) println!("Syntax Tree: {:#?}", source.root())
} }
test_spans(source.root());
let (local_compare_ref, mut ref_errors) = parse_metadata(&source); let (local_compare_ref, mut ref_errors) = parse_metadata(&source);
let compare_ref = local_compare_ref.unwrap_or(compare_ref); let compare_ref = local_compare_ref.unwrap_or(compare_ref);
@ -316,12 +318,7 @@ fn test_part(
.into_iter() .into_iter()
.filter(|error| error.span.source() == id) .filter(|error| error.span.source() == id)
.map(|error| { .map(|error| {
let mut range = ctx.sources.range(error.span); let range = error.pos.apply(ctx.sources.range(error.span));
match error.pos {
ErrorPos::Start => range.end = range.start,
ErrorPos::Full => {}
ErrorPos::End => range.start = range.end,
}
(range, error.message) (range, error.message)
}) })
.collect(); .collect();
@ -461,8 +458,8 @@ fn test_reparse(src: &str, i: usize, rng: &mut LinearShift) -> bool {
incr_source.edit(replace.clone(), with); incr_source.edit(replace.clone(), with);
let edited_src = incr_source.src(); let edited_src = incr_source.src();
let ref_source = SourceFile::detached(edited_src);
let incr_root = incr_source.root(); let incr_root = incr_source.root();
let ref_source = SourceFile::detached(edited_src);
let ref_root = ref_source.root(); let ref_root = ref_source.root();
let same = incr_root == ref_root; let same = incr_root == ref_root;
if !same { if !same {
@ -475,6 +472,9 @@ fn test_reparse(src: &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());
} }
test_spans(ref_root);
test_spans(incr_root);
same same
}; };
@ -505,6 +505,21 @@ fn test_reparse(src: &str, i: usize, rng: &mut LinearShift) -> bool {
ok ok
} }
/// Ensure that all spans are properly ordered (and therefore unique).
fn test_spans(root: &SyntaxNode) {
test_spans_impl(root, 0 .. u64::MAX);
}
fn test_spans_impl(root: &SyntaxNode, within: Range<u64>) {
assert!(within.contains(&root.span().number()), "wrong span order");
let start = root.span().number() + 1;
let mut children = root.children().peekable();
while let Some(child) = children.next() {
let end = children.peek().map_or(within.end, |next| next.span().number());
test_spans_impl(child, start .. end);
}
}
/// Draw all frames into one image with padding in between. /// Draw all frames into one image with padding in between.
fn render(ctx: &mut Context, frames: &[Arc<Frame>]) -> sk::Pixmap { fn render(ctx: &mut Context, frames: &[Arc<Frame>]) -> sk::Pixmap {
let pixel_per_pt = 2.0; let pixel_per_pt = 2.0;