mirror of https://github.com/boa-dev/boa.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
306 lines
8.5 KiB
306 lines
8.5 KiB
use std::{cmp::Ordering, fmt, num::NonZeroU32}; |
|
|
|
/// A position in the ECMAScript source code. |
|
/// |
|
/// Stores both the column number and the line number. |
|
/// |
|
/// ## Similar Implementations |
|
/// [V8: Location](https://cs.chromium.org/chromium/src/v8/src/parsing/scanner.h?type=cs&q=isValid+Location&g=0&l=216) |
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
|
pub struct Position { |
|
/// Line number. |
|
line_number: NonZeroU32, |
|
/// Column number. |
|
column_number: NonZeroU32, |
|
} |
|
|
|
impl Position { |
|
/// Creates a new `Position` from Non-Zero values. |
|
/// |
|
/// # Panics |
|
/// |
|
/// Will panic if the line number or column number is zero. |
|
#[inline] |
|
#[track_caller] |
|
#[must_use] |
|
pub fn new(line_number: u32, column_number: u32) -> Self { |
|
Self { |
|
line_number: NonZeroU32::new(line_number).expect("line number cannot be 0"), |
|
column_number: NonZeroU32::new(column_number).expect("column number cannot be 0"), |
|
} |
|
} |
|
|
|
/// Gets the line number of the position. |
|
#[inline] |
|
#[must_use] |
|
pub const fn line_number(self) -> u32 { |
|
self.line_number.get() |
|
} |
|
|
|
/// Gets the column number of the position. |
|
#[inline] |
|
#[must_use] |
|
pub const fn column_number(self) -> u32 { |
|
self.column_number.get() |
|
} |
|
} |
|
|
|
impl fmt::Display for Position { |
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
|
write!(f, "{}:{}", self.line_number, self.column_number) |
|
} |
|
} |
|
|
|
/// A span in the ECMAScript source code. |
|
/// |
|
/// Stores a start position and an end position. |
|
/// |
|
/// Note that spans are of the form [start, end) i.e. that the start position is inclusive |
|
/// and the end position is exclusive. |
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
|
pub struct Span { |
|
start: Position, |
|
end: Position, |
|
} |
|
|
|
impl Span { |
|
/// Creates a new `Span`. |
|
/// |
|
/// # Panics |
|
/// |
|
/// Panics if the start position is bigger than the end position. |
|
#[inline] |
|
#[track_caller] |
|
#[must_use] |
|
pub fn new(start: Position, end: Position) -> Self { |
|
assert!(start <= end, "a span cannot start after its end"); |
|
|
|
Self { start, end } |
|
} |
|
|
|
/// Gets the starting position of the span. |
|
#[inline] |
|
#[must_use] |
|
pub const fn start(self) -> Position { |
|
self.start |
|
} |
|
|
|
/// Gets the final position of the span. |
|
#[inline] |
|
#[must_use] |
|
pub const fn end(self) -> Position { |
|
self.end |
|
} |
|
|
|
/// Checks if this span inclusively contains another span or position. |
|
pub fn contains<S>(self, other: S) -> bool |
|
where |
|
S: Into<Self>, |
|
{ |
|
let other = other.into(); |
|
self.start <= other.start && self.end >= other.end |
|
} |
|
} |
|
|
|
impl From<Position> for Span { |
|
fn from(pos: Position) -> Self { |
|
Self { |
|
start: pos, |
|
end: pos, |
|
} |
|
} |
|
} |
|
|
|
impl PartialOrd for Span { |
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
|
if self == other { |
|
Some(Ordering::Equal) |
|
} else if self.end < other.start { |
|
Some(Ordering::Less) |
|
} else if self.start > other.end { |
|
Some(Ordering::Greater) |
|
} else { |
|
None |
|
} |
|
} |
|
} |
|
|
|
impl fmt::Display for Span { |
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
|
write!(f, "[{}..{}]", self.start, self.end) |
|
} |
|
} |
|
|
|
#[cfg(test)] |
|
mod tests { |
|
#![allow(clippy::similar_names)] |
|
#![allow(unused_must_use)] |
|
use super::{Position, Span}; |
|
|
|
/// Checks that we cannot create a position with 0 as the column. |
|
#[test] |
|
#[should_panic(expected = "column number cannot be 0")] |
|
fn invalid_position_column() { |
|
Position::new(10, 0); |
|
} |
|
|
|
/// Checks that we cannot create a position with 0 as the line. |
|
#[test] |
|
#[should_panic(expected = "line number cannot be 0")] |
|
fn invalid_position_line() { |
|
Position::new(0, 10); |
|
} |
|
|
|
/// Checks that the `PartialEq` implementation of `Position` is consistent. |
|
#[test] |
|
fn position_equality() { |
|
assert_eq!(Position::new(10, 50), Position::new(10, 50)); |
|
assert_ne!(Position::new(10, 50), Position::new(10, 51)); |
|
assert_ne!(Position::new(10, 50), Position::new(11, 50)); |
|
assert_ne!(Position::new(10, 50), Position::new(11, 51)); |
|
} |
|
|
|
/// Checks that the `PartialOrd` implementation of `Position` is consistent. |
|
#[test] |
|
fn position_order() { |
|
assert!(Position::new(10, 50) < Position::new(10, 51)); |
|
assert!(Position::new(9, 50) < Position::new(10, 50)); |
|
assert!(Position::new(10, 50) < Position::new(11, 51)); |
|
assert!(Position::new(10, 50) < Position::new(11, 49)); |
|
|
|
assert!(Position::new(10, 51) > Position::new(10, 50)); |
|
assert!(Position::new(10, 50) > Position::new(9, 50)); |
|
assert!(Position::new(11, 51) > Position::new(10, 50)); |
|
assert!(Position::new(11, 49) > Position::new(10, 50)); |
|
} |
|
|
|
/// Checks that the position getters actually retrieve correct values. |
|
#[test] |
|
fn position_getters() { |
|
let pos = Position::new(10, 50); |
|
assert_eq!(pos.line_number(), 10); |
|
assert_eq!(pos.column_number(), 50); |
|
} |
|
|
|
/// Checks that the string representation of a position is correct. |
|
#[test] |
|
fn position_to_string() { |
|
let pos = Position::new(10, 50); |
|
|
|
assert_eq!("10:50", pos.to_string()); |
|
assert_eq!("10:50", pos.to_string()); |
|
} |
|
|
|
/// Checks that we cannot create an invalid span. |
|
#[test] |
|
#[should_panic(expected = "a span cannot start after its end")] |
|
fn invalid_span() { |
|
let a = Position::new(10, 30); |
|
let b = Position::new(10, 50); |
|
Span::new(b, a); |
|
} |
|
|
|
/// Checks that we can create valid spans. |
|
#[test] |
|
fn span_creation() { |
|
let a = Position::new(10, 30); |
|
let b = Position::new(10, 50); |
|
|
|
Span::new(a, b); |
|
Span::new(a, a); |
|
Span::from(a); |
|
} |
|
|
|
/// Checks that the `PartialEq` implementation of `Span` is consistent. |
|
#[test] |
|
fn span_equality() { |
|
let a = Position::new(10, 50); |
|
let b = Position::new(10, 52); |
|
let c = Position::new(11, 20); |
|
|
|
let span_ab = Span::new(a, b); |
|
let span_ab_2 = Span::new(a, b); |
|
let span_ac = Span::new(a, c); |
|
let span_bc = Span::new(b, c); |
|
|
|
assert_eq!(span_ab, span_ab_2); |
|
assert_ne!(span_ab, span_ac); |
|
assert_ne!(span_ab, span_bc); |
|
assert_ne!(span_bc, span_ac); |
|
|
|
let span_a = Span::from(a); |
|
let span_aa = Span::new(a, a); |
|
|
|
assert_eq!(span_a, span_aa); |
|
} |
|
|
|
/// Checks that the getters retrieve the correct value. |
|
#[test] |
|
fn span_getters() { |
|
let a = Position::new(10, 50); |
|
let b = Position::new(10, 52); |
|
|
|
let span = Span::new(a, b); |
|
|
|
assert_eq!(span.start(), a); |
|
assert_eq!(span.end(), b); |
|
} |
|
|
|
/// Checks that the `Span::contains()` method works properly. |
|
#[test] |
|
fn span_contains() { |
|
let a = Position::new(10, 50); |
|
let b = Position::new(10, 52); |
|
let c = Position::new(11, 20); |
|
let d = Position::new(12, 5); |
|
|
|
let span_ac = Span::new(a, c); |
|
assert!(span_ac.contains(b)); |
|
|
|
let span_ab = Span::new(a, b); |
|
let span_cd = Span::new(c, d); |
|
|
|
assert!(!span_ab.contains(span_cd)); |
|
assert!(span_ab.contains(b)); |
|
|
|
let span_ad = Span::new(a, d); |
|
let span_bc = Span::new(b, c); |
|
|
|
assert!(span_ad.contains(span_bc)); |
|
assert!(!span_bc.contains(span_ad)); |
|
|
|
let span_ac = Span::new(a, c); |
|
let span_bd = Span::new(b, d); |
|
|
|
assert!(!span_ac.contains(span_bd)); |
|
assert!(!span_bd.contains(span_ac)); |
|
} |
|
|
|
/// Checks that the string representation of a span is correct. |
|
#[test] |
|
fn span_to_string() { |
|
let a = Position::new(10, 50); |
|
let b = Position::new(11, 20); |
|
let span = Span::new(a, b); |
|
|
|
assert_eq!("[10:50..11:20]", span.to_string()); |
|
assert_eq!("[10:50..11:20]", span.to_string()); |
|
} |
|
|
|
/// Checks that the ordering of spans is correct. |
|
#[test] |
|
fn span_ordering() { |
|
let a = Position::new(10, 50); |
|
let b = Position::new(10, 52); |
|
let c = Position::new(11, 20); |
|
let d = Position::new(12, 5); |
|
|
|
let span_ab = Span::new(a, b); |
|
let span_cd = Span::new(c, d); |
|
|
|
assert!(span_ab < span_cd); |
|
assert!(span_cd > span_ab); |
|
} |
|
}
|
|
|