Compare commits

...

2 Commits

4 changed files with 357 additions and 147 deletions

View File

@ -1,19 +1,23 @@
use crate::{lexer::LexerError, span::Span}; use crate::{lexer::LexerError, span::Span};
/// Errors that can occur during parsing. /// Kinds of errors that can occur during parsing.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum ParserErrorKind { pub enum ParserErrorKind {
/// An error which ocurred during lexical analysis. /// An error which ocurred during lexical analysis.
Lexer(LexerError), Lexer(LexerError),
/// An unexpecting closing parenthesis/bracket was encountered. /// Expected a value for key in map, but no value was found.
MissingValueInMap,
/// An unexpecting closing parenthesis/brace/bracket was encountered.
UnexpectedCloseBracket, UnexpectedCloseBracket,
/// Opening parenthesis/bracket does not have a matching closing /// Unexpectedly reached the end of the input.
/// parenthesis/bracket. UnexpectedEof,
UnclosedBracket,
/// An unmatched parenthesis/bracket was encountered.
UnmatchedBracket,
/// Unexpected parser state reached. /// Unexpected parser state reached.
Unreachable, UnexpectedState,
/// Opening parenthesis/brace/bracket does not have a matching closing
/// parenthesis/brace/bracket.
UnclosedBracket,
/// An unmatched parenthesis/brace/bracket was encountered.
UnmatchedBracket,
} }
/// Parser error, with a start and end location. /// Parser error, with a start and end location.
@ -35,16 +39,19 @@ impl ParserError {
impl std::error::Error for ParserError {} impl std::error::Error for ParserError {}
#[cfg(not(tarpaulin_include))]
impl std::fmt::Display for ParserError { impl std::fmt::Display for ParserError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use ParserErrorKind::*; use ParserErrorKind::*;
match &self.kind { match &self.kind {
Lexer(err) => write!(f, "{err}"), Lexer(err) => write!(f, "{err}"),
MissingValueInMap => write!(f, "Key in map is missing its value"),
UnexpectedCloseBracket => write!(f, "Unexpected closing bracket"), UnexpectedCloseBracket => write!(f, "Unexpected closing bracket"),
UnclosedBracket => write!(f, "Unclosed parenthesis"), UnexpectedEof => write!(f, "Unexpected reached end of input"),
UnexpectedState => write!(f, "Unexpected parsing state reached"),
UnclosedBracket => write!(f, "Unclosed bracket"),
UnmatchedBracket => write!(f, "Unmatched bracket"), UnmatchedBracket => write!(f, "Unmatched bracket"),
Unreachable => write!(f, "Unexpected parsing state reached"),
} }
} }
} }

View File

@ -7,7 +7,8 @@ use crate::lexer::{Lexer, TokenKind};
mod error; mod error;
mod node; mod node;
/// A parser for the AST. /// Parser for converting Onihime source code into an Abstract Syntax Tree
/// (AST).
#[derive(Debug)] #[derive(Debug)]
pub struct Parser<'parser> { pub struct Parser<'parser> {
lexer: Lexer<'parser>, lexer: Lexer<'parser>,
@ -35,6 +36,7 @@ impl<'parser> Parser<'parser> {
while let Some(token) = self.lexer.read()? { while let Some(token) = self.lexer.read()? {
match token.kind { match token.kind {
TokenKind::BlockComment(_) | TokenKind::LineComment(_) => {} TokenKind::BlockComment(_) | TokenKind::LineComment(_) => {}
TokenKind::OpenParen => { TokenKind::OpenParen => {
let child = Node::new(NodeKind::List(Vec::new()), token.span); let child = Node::new(NodeKind::List(Vec::new()), token.span);
parents.push(cur_node); parents.push(cur_node);
@ -60,6 +62,47 @@ impl<'parser> Parser<'parser> {
parent.push_node(cur_node)?; parent.push_node(cur_node)?;
cur_node = parent; cur_node = parent;
} }
TokenKind::OpenBrace => {
let child = Node::new(NodeKind::Set(Vec::new()), token.span);
parents.push(cur_node);
cur_node = child;
}
TokenKind::OpenHashBrace => {
let child = Node::new(NodeKind::Map(Vec::new()), token.span);
parents.push(cur_node);
cur_node = child;
}
TokenKind::CloseBrace => {
let mut parent = parents.pop().ok_or_else(|| {
ParserError::new(
ParserErrorKind::UnexpectedCloseBracket,
token.span.clone(),
)
})?;
cur_node.span.extend(&token.span);
if !matches!(cur_node.kind, NodeKind::Map(_) | NodeKind::Set(_)) {
return Err(ParserError::new(
ParserErrorKind::UnmatchedBracket,
token.span,
));
}
if let NodeKind::Map(ref vec) = cur_node.kind {
if vec.len() % 2 != 0 {
return Err(ParserError::new(
ParserErrorKind::MissingValueInMap,
token.span,
));
}
}
parent.push_node(cur_node)?;
cur_node = parent;
}
TokenKind::OpenBracket => { TokenKind::OpenBracket => {
let child = Node::new(NodeKind::Vector(Vec::new()), token.span); let child = Node::new(NodeKind::Vector(Vec::new()), token.span);
parents.push(cur_node); parents.push(cur_node);
@ -86,7 +129,10 @@ impl<'parser> Parser<'parser> {
cur_node = parent; cur_node = parent;
} }
_ => cur_node.push_node(Node::try_from(token)?)?, _ => {
let node = Node::try_from(token)?;
cur_node.push_node(node)?;
}
} }
} }
@ -100,10 +146,7 @@ impl<'parser> Parser<'parser> {
if let NodeKind::List(body) = cur_node.kind { if let NodeKind::List(body) = cur_node.kind {
Ok(body) Ok(body)
} else { } else {
Err(ParserError::new( unreachable!() // In theory, at least...
ParserErrorKind::Unreachable,
cur_node.span,
))
} }
} }
} }
@ -113,6 +156,7 @@ mod tests {
use super::*; use super::*;
use crate::{ use crate::{
lexer::{LexerError, LexerErrorKind, Symbol}, lexer::{LexerError, LexerErrorKind, Symbol},
parser::node::Atom,
span::Span, span::Span,
}; };
@ -127,75 +171,7 @@ mod tests {
}; };
} }
test!(parse_list: "(+ 1 2)", src => Ok(vec![ test!(empty: "", _src => Ok(vec![]));
Node::new(
NodeKind::List(vec![
Node::new(NodeKind::Symbol(Symbol::from("+")), Span::new(1..2, src.clone())),
Node::new(NodeKind::Number(1.0), Span::new(3..4, src.clone())),
Node::new(NodeKind::Number(2.0), Span::new(5..6, src.clone())),
]),
Span::new(0..7, src)
)
]));
test!(parse_nested_list: "(+ 2.5 64 (* 2 3))", src => Ok(vec![Node::new(
NodeKind::List(vec![
Node::new(NodeKind::Symbol(Symbol::from("+")), Span::new(1..2, src.clone())),
Node::new(NodeKind::Number(2.5), Span::new(3..6, src.clone())),
Node::new(NodeKind::Number(64.0), Span::new(7..9, src.clone())),
Node::new(
NodeKind::List(vec![
Node::new(NodeKind::Symbol(Symbol::from("*")), Span::new(11..12, src.clone())),
Node::new(NodeKind::Number(2.0), Span::new(13..14, src.clone())),
Node::new(NodeKind::Number(3.0), Span::new(15..16, src.clone())),
]),
Span::new(10..17, src.clone())
),
]),
Span::new(0..18, src)
)]));
test!(parse_multiple_expressions: "(/ 6 3 (+ 1 2)) (* 2 5)\n(- 10 5)", src => Ok(vec![
Node::new(
NodeKind::List(vec![
Node::new(NodeKind::Symbol(Symbol::from("/")), Span::new(1..2, src.clone())),
Node::new(NodeKind::Number(6.0), Span::new(3..4, src.clone())),
Node::new(NodeKind::Number(3.0), Span::new(5..6, src.clone())),
Node::new(
NodeKind::List(vec![
Node::new(NodeKind::Symbol(Symbol::from("+")), Span::new(8..9, src.clone())),
Node::new(NodeKind::Number(1.0), Span::new(10..11, src.clone())),
Node::new(NodeKind::Number(2.0), Span::new(12..13, src.clone())),
]),
Span::new(7..14, src.clone())
),
]),
Span::new(0..15, src.clone())
),
Node::new(
NodeKind::List(vec![
Node::new(NodeKind::Symbol(Symbol::from("*")), Span::new(17..18, src.clone())),
Node::new(NodeKind::Number(2.0), Span::new(19..20, src.clone())),
Node::new(NodeKind::Number(5.0), Span::new(21..22, src.clone())),
]),
Span::new(16..23, src.clone())
),
Node::new(
NodeKind::List(vec![
Node::new(NodeKind::Symbol(Symbol::from("-")), Span::new(25..26, src.clone())),
Node::new(NodeKind::Number(10.0), Span::new(27..29, src.clone())),
Node::new(NodeKind::Number(5.0), Span::new(30..31, src.clone())),
]),
Span::new(24..32, src)
),
]));
test!(parse_float: "(2.500000)", src => Ok(vec![Node::new(
NodeKind::List(vec![Node::new(NodeKind::Number(2.5), Span::new(1..9, src.clone()))]),
Span::new(0..10, src)
)]));
test!(parse_empty: "", _src => Ok(vec![]));
test!(error_invalid_number: "(+ 1.2.3)", src => Err(ParserError::new( test!(error_invalid_number: "(+ 1.2.3)", src => Err(ParserError::new(
ParserErrorKind::Lexer(LexerError::new( ParserErrorKind::Lexer(LexerError::new(
@ -205,8 +181,151 @@ mod tests {
Span::new(3..8, src) Span::new(3..8, src)
))); )));
test!(error_unexpected_close_paren: ")", src => Err(ParserError::new( test!(list: "(+ 1 2) ; sneaky comment :)", src => Ok(vec![
Node::new(
NodeKind::List(vec![
Node::new(Atom::Symbol(Symbol::from("+")).into(), Span::new(1..2, src.clone())),
Node::new(Atom::Integer(1).into(), Span::new(3..4, src.clone())),
Node::new(Atom::Integer(2).into(), Span::new(5..6, src.clone())),
]),
Span::new(0..7, src)
)
]));
test!(error_list_unmatched_bracket: "(]", src => Err(ParserError::new(
ParserErrorKind::UnmatchedBracket,
Span::new(1..2, src),
)));
test!(error_list_missing_close_paren: "(true", src => Err(ParserError::new(
ParserErrorKind::UnclosedBracket,
Span::new(0..1, src),
)));
test!(error_list_unexpected_close_paren: ")", src => Err(ParserError::new(
ParserErrorKind::UnexpectedCloseBracket, ParserErrorKind::UnexpectedCloseBracket,
Span::new(0..1, src) Span::new(0..1, src)
))); )));
test!(map: "#{:a 0.0 :b 1.0}", src => Ok(vec![
Node::new(
NodeKind::Map(vec![
Node::new(Atom::Keyword(Symbol::from("a")).into(), Span::new(2..4, src.clone())),
Node::new(Atom::Float(0.0).into(), Span::new(5..8, src.clone())),
Node::new(Atom::Keyword(Symbol::from("b")).into(), Span::new(9..11, src.clone())),
Node::new(Atom::Float(1.0).into(), Span::new(12..15, src.clone())),
]),
Span::new(0..16, src)
)
]));
test!(error_map_missing_value: "#{:x}", src => Err(ParserError::new(
ParserErrorKind::MissingValueInMap,
Span::new(4..5, src.clone())
)));
test!(error_map_unmatched_bracket: "#{)", src => Err(ParserError::new(
ParserErrorKind::UnmatchedBracket,
Span::new(2..3, src),
)));
test!(error_map_missing_close_brace: "#{", src => Err(ParserError::new(
ParserErrorKind::UnclosedBracket,
Span::new(0..2, src),
)));
test!(set: "{{} nil}", src => Ok(vec![
Node::new(
NodeKind::Set(vec![
Node::new(NodeKind::Set(vec![]), Span::new(1..3, src.clone())),
Node::new(NodeKind::Atom(Atom::Nil), Span::new(4..7, src.clone())),
]),
Span::new(0..8, src),
)
]));
test!(error_set_unmatched_bracket: "{]", src => Err(ParserError::new(
ParserErrorKind::UnmatchedBracket,
Span::new(1..2, src),
)));
test!(error_set_missing_close_brace: "{", src => Err(ParserError::new(
ParserErrorKind::UnclosedBracket,
Span::new(0..1, src),
)));
test!(error_map_set_unexpected_close_brace: "}", src => Err(ParserError::new(
ParserErrorKind::UnexpectedCloseBracket,
Span::new(0..1, src)
)));
test!(vector: "['a' 'b' 'c']", src => Ok(vec![
Node::new(
NodeKind::Vector(vec![
Node::new(NodeKind::Atom(Atom::Char('a')), Span::new(1..4, src.clone())),
Node::new(NodeKind::Atom(Atom::Char('b')), Span::new(5..8, src.clone())),
Node::new(NodeKind::Atom(Atom::Char('c')), Span::new(9..12, src.clone())),
]),
Span::new(0..13, src),
)
]));
test!(error_vector_unmatched_bracket: "[}", src => Err(ParserError::new(
ParserErrorKind::UnmatchedBracket,
Span::new(1..2, src),
)));
test!(error_vector_missing_close_bracket: "{", src => Err(ParserError::new(
ParserErrorKind::UnclosedBracket,
Span::new(0..1, src),
)));
test!(error_vector_unexpected_close_bracket: "]", src => Err(ParserError::new(
ParserErrorKind::UnexpectedCloseBracket,
Span::new(0..1, src)
)));
test!(multiple_expressions: "(/ 6 3 (+ 1 2)) (* 2 5)\n(- 10 5)", src => Ok(vec![
Node::new(
NodeKind::List(vec![
Node::new(Atom::Symbol(Symbol::from("/")).into(), Span::new(1..2, src.clone())),
Node::new(Atom::Integer(6).into(), Span::new(3..4, src.clone())),
Node::new(Atom::Integer(3).into(), Span::new(5..6, src.clone())),
Node::new(
NodeKind::List(vec![
Node::new(Atom::Symbol(Symbol::from("+")).into(), Span::new(8..9, src.clone())),
Node::new(Atom::Integer(1).into(), Span::new(10..11, src.clone())),
Node::new(Atom::Integer(2).into(), Span::new(12..13, src.clone())),
]),
Span::new(7..14, src.clone())
),
]),
Span::new(0..15, src.clone())
),
Node::new(
NodeKind::List(vec![
Node::new(Atom::Symbol(Symbol::from("*")).into(), Span::new(17..18, src.clone())),
Node::new(Atom::Integer(2).into(), Span::new(19..20, src.clone())),
Node::new(Atom::Integer(5).into(), Span::new(21..22, src.clone())),
]),
Span::new(16..23, src.clone())
),
Node::new(
NodeKind::List(vec![
Node::new(Atom::Symbol(Symbol::from("-")).into(), Span::new(25..26, src.clone())),
Node::new(Atom::Integer(10).into(), Span::new(27..29, src.clone())),
Node::new(Atom::Integer(5).into(), Span::new(30..31, src.clone())),
]),
Span::new(24..32, src)
),
]));
test!(function_application: "(join \"foo\" \"bar\")", src => Ok(vec![Node::new(
NodeKind::List(vec![
Node::new(Atom::Symbol(Symbol::from("join")).into(), Span::new(1..5, src.clone())),
Node::new(Atom::String("foo".into()).into(), Span::new(6..11, src.clone())),
Node::new(Atom::String("bar".into()).into(), Span::new(12..17, src.clone())),
]),
Span::new(0..18, src)
)]));
} }

View File

@ -4,50 +4,116 @@ use crate::{
span::Span, span::Span,
}; };
/// The type of a node in the AST. /// An atomic value.
#[derive(Debug, Clone, PartialEq)]
pub enum Atom {
/// Boolean, e.g. `true`, `false`
Bool(bool),
/// Character, e.g. `'c'`, `'\n'`
Char(char),
/// Floating-point number, e.g. `-1.0`, `2.0`, `+0.003`
Float(f64),
/// Integer, e.g. `0`, `-1`, `+200`
Integer(i64),
/// Keyword, e.g. `:baz`
Keyword(Symbol),
/// String, e.g. `"foo bar"`
String(String),
/// Symbol, e.g. `qux`, `+`
Symbol(Symbol),
/// Nil, e.g. `nil`
Nil,
}
#[cfg(not(tarpaulin_include))]
impl std::fmt::Display for Atom {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Atom::*;
match self {
Bool(_) => write!(f, "BOOL"),
Char(_) => write!(f, "CHAR"),
Float(_) => write!(f, "FLOAT"),
Integer(_) => write!(f, "INTEGER"),
Keyword(_) => write!(f, "KEYWORD"),
String(_) => write!(f, "STRING"),
Symbol(_) => write!(f, "SYMBOL"),
Nil => write!(f, "NIL"),
}
}
}
/// An expression.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum NodeKind { pub enum NodeKind {
/// Bool. /// An atomic value.
Bool(bool), Atom(Atom),
/// Character. /// A list of nodes.
Char(char),
/// Keyword.
Keyword(Symbol),
/// Number.
Number(f64),
/// String.
String(String),
/// Symbol.
Symbol(Symbol),
/// Nil.
Nil,
/// List.
List(Vec<Node>), List(Vec<Node>),
/// Vector. /// A map of nodes.
Map(Vec<Node>),
/// A set of nodes.
Set(Vec<Node>),
/// A vector of nodes.
Vector(Vec<Node>), Vector(Vec<Node>),
} }
impl From<Atom> for NodeKind {
fn from(atom: Atom) -> Self {
Self::Atom(atom)
}
}
impl TryFrom<Token> for NodeKind {
type Error = ParserError;
fn try_from(token: Token) -> Result<Self, Self::Error> {
let kind = match token.kind {
TokenKind::Bool(b) => Atom::Bool(b),
TokenKind::Char(c) => Atom::Char(c),
TokenKind::Float(n) => Atom::Float(n),
TokenKind::Integer(n) => Atom::Integer(n),
TokenKind::Keyword(k) => Atom::Keyword(k),
TokenKind::String(s) => Atom::String(s),
TokenKind::Symbol(s) => Atom::Symbol(s),
TokenKind::Nil => Atom::Nil,
_ => {
return Err(ParserError::new(
ParserErrorKind::UnexpectedState,
token.span,
))
}
};
Ok(kind.into())
}
}
#[cfg(not(tarpaulin_include))]
impl std::fmt::Display for NodeKind { impl std::fmt::Display for NodeKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use NodeKind::*;
match self { match self {
NodeKind::Bool(_) => write!(f, "BOOL"), Atom(atom) => write!(f, "{atom}"),
NodeKind::Char(_) => write!(f, "CHAR"), List(_) => write!(f, "LIST"),
NodeKind::Keyword(_) => write!(f, "KEYWORD"), Map(_) => write!(f, "MAP"),
NodeKind::Number(_) => write!(f, "NUMBER"), Set(_) => write!(f, "SET"),
NodeKind::String(_) => write!(f, "STRING"), Vector(_) => write!(f, "VECTOR"),
NodeKind::Symbol(_) => write!(f, "SYMBOL"),
NodeKind::Nil => write!(f, "NIL"),
NodeKind::List(_) => write!(f, "LIST"),
NodeKind::Vector(_) => write!(f, "VECTOR"),
} }
} }
} }
/// A node in the AST with a start and end location. /// A node in the Abstract Syntax Tree (AST).
///
/// `Nodes`s contain the kind of node which was found, as well as a [Span]
/// specifying the [Source] and [Location] of the node.
///
/// [Source]: crate::span::Source
/// [Location]: crate::span::Location
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Node { pub struct Node {
/// The type of node. /// The kind of node.
pub kind: NodeKind, pub kind: NodeKind,
/// The span in which the node occurs. /// The span in which the node occurs.
pub span: Span, pub span: Span,
@ -63,15 +129,24 @@ impl Node {
/// Push a child node onto a list node. /// Push a child node onto a list node.
pub fn push_node(&mut self, child: Self) -> Result<(), ParserError> { pub fn push_node(&mut self, child: Self) -> Result<(), ParserError> {
match &mut self.kind { match &mut self.kind {
NodeKind::List(c) | NodeKind::Vector(c) => { NodeKind::List(vec)
c.push(child); | NodeKind::Map(vec)
| NodeKind::Set(vec)
| NodeKind::Vector(vec) => {
vec.push(child);
}
_ => {
return Err(ParserError::new(
ParserErrorKind::UnexpectedState,
child.span,
))
} }
_ => return Err(ParserError::new(ParserErrorKind::Unreachable, child.span)),
} }
Ok(()) Ok(())
} }
#[cfg(not(tarpaulin_include))]
fn display(&self, indent: usize) -> String { fn display(&self, indent: usize) -> String {
let mut text = format!( let mut text = format!(
"{}{}@{}..{}\n", "{}{}@{}..{}\n",
@ -82,48 +157,35 @@ impl Node {
); );
match &self.kind { match &self.kind {
NodeKind::List(vec) | NodeKind::Vector(vec) => { NodeKind::Atom(_) => {}
NodeKind::List(vec)
| NodeKind::Map(vec)
| NodeKind::Set(vec)
| NodeKind::Vector(vec) => {
for node in vec { for node in vec {
text.push_str(&node.display(indent + 1)); text.push_str(&format!("{}\n", node.display(indent + 1)));
} }
} }
_ => {}
} }
text.trim_end().to_string() text.trim_end().to_string()
} }
} }
impl std::fmt::Display for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display(0))
}
}
impl TryFrom<Token> for Node { impl TryFrom<Token> for Node {
type Error = ParserError; type Error = ParserError;
fn try_from(token: Token) -> Result<Self, Self::Error> { fn try_from(token: Token) -> Result<Self, Self::Error> {
let kind = match token.kind { let span = token.span.clone();
TokenKind::Bool(b) => NodeKind::Bool(b), let kind = NodeKind::try_from(token)?;
TokenKind::Char(c) => NodeKind::Char(c),
TokenKind::Number(n) => NodeKind::Number(n),
TokenKind::String(s) => NodeKind::String(s),
TokenKind::Keyword(k) => NodeKind::Keyword(k),
TokenKind::Symbol(s) => NodeKind::Symbol(s),
TokenKind::Nil => NodeKind::Nil,
TokenKind::OpenParen
| TokenKind::CloseParen
| TokenKind::OpenBrace
| TokenKind::CloseBrace
| TokenKind::OpenBracket
| TokenKind::CloseBracket
| TokenKind::LineComment(_)
| TokenKind::BlockComment(_) => {
return Err(ParserError::new(ParserErrorKind::Unreachable, token.span))
}
};
Ok(Self::new(kind, token.span)) Ok(Self::new(kind, span))
}
}
#[cfg(not(tarpaulin_include))]
impl std::fmt::Display for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display(0))
} }
} }

View File

@ -87,7 +87,7 @@ impl Source {
} }
/// A contiguous sequence of bytes within some source. /// A contiguous sequence of bytes within some source.
#[derive(Debug, Default, Clone, Hash)] #[derive(Debug, Default, Clone, Eq, Hash)]
pub struct Span { pub struct Span {
bytes: Range<usize>, bytes: Range<usize>,
source: Arc<Source>, source: Arc<Source>,
@ -111,7 +111,6 @@ impl Span {
/// Extend one span to include another. /// Extend one span to include another.
pub fn extend(&mut self, other: &Self) { pub fn extend(&mut self, other: &Self) {
debug_assert!(self.same_source(other)); debug_assert!(self.same_source(other));
self.bytes.end = other.bytes.end; self.bytes.end = other.bytes.end;
} }
@ -133,6 +132,7 @@ impl Span {
Arc::ptr_eq(&self.source, &other.source) Arc::ptr_eq(&self.source, &other.source)
} }
#[must_use]
pub(crate) fn bytes(&self) -> &Range<usize> { pub(crate) fn bytes(&self) -> &Range<usize> {
&self.bytes &self.bytes
} }
@ -143,3 +143,25 @@ impl PartialEq for Span {
self.same_source(other) && self.bytes == other.bytes self.same_source(other) && self.bytes == other.bytes
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn location_partial_ord() {
assert!(Location::new(1, 1) < Location::new(1, 2));
assert!(Location::new(1, 10) < Location::new(2, 1));
assert!(Location::new(5, 5) == Location::new(5, 5));
assert!(Location::new(10, 1) > Location::new(9, 99));
}
#[test]
fn source_get_set_name() {
let mut src = Source::new(None, "".into());
assert!(src.name().is_none());
src.set_name("foo".into());
assert!(src.name() == Some("foo"));
}
}