sexp: add span info to Sexps
This commit is contained in:
@@ -4,6 +4,13 @@ use std::{cmp, fmt};
|
|||||||
pub struct Error {
|
pub struct Error {
|
||||||
/// The error message.
|
/// The error message.
|
||||||
pub message: &'static str,
|
pub message: &'static str,
|
||||||
|
/// Position in the source string where the error was detected
|
||||||
|
pub pos: SourcePosition,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Position in the input string
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct SourcePosition {
|
||||||
/// The line number on which the error occurred.
|
/// The line number on which the error occurred.
|
||||||
pub line: usize,
|
pub line: usize,
|
||||||
/// The column number on which the error occurred.
|
/// The column number on which the error occurred.
|
||||||
@@ -25,7 +32,7 @@ pub(crate) type ERes<T> = Result<T, Err>;
|
|||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(f, "{}:{}: {}", self.line, self.column, self.message)
|
write!(f, "{}:{}: {}", self.pos.line, self.pos.column, self.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +44,7 @@ impl fmt::Debug for Error {
|
|||||||
|
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
pub(crate) fn get_line_and_column(s: &str, pos: usize) -> (usize, usize) {
|
pub(crate) fn get_line_and_column(s: &str, pos: usize) -> SourcePosition {
|
||||||
let mut line: usize = 1;
|
let mut line: usize = 1;
|
||||||
let mut col: isize = -1;
|
let mut col: isize = -1;
|
||||||
for c in s.chars().take(pos + 1) {
|
for c in s.chars().take(pos + 1) {
|
||||||
@@ -48,17 +55,18 @@ pub(crate) fn get_line_and_column(s: &str, pos: usize) -> (usize, usize) {
|
|||||||
col += 1;
|
col += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(line, cmp::max(col, 0) as usize)
|
SourcePosition {
|
||||||
|
line,
|
||||||
|
column: cmp::max(col, 0) as usize,
|
||||||
|
index: pos,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err {
|
fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err {
|
||||||
let (line, column) = get_line_and_column(s, *pos);
|
|
||||||
Box::new(Error {
|
Box::new(Error {
|
||||||
message: message,
|
message,
|
||||||
line: line,
|
pos: get_line_and_column(s, *pos)
|
||||||
column: column,
|
|
||||||
index: *pos,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,3 +74,12 @@ fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err {
|
|||||||
pub(crate) fn err<T>(message: &'static str, s: &str, pos: &usize) -> ERes<T> {
|
pub(crate) fn err<T>(message: &'static str, s: &str, pos: &usize) -> ERes<T> {
|
||||||
Err(err_impl(message, s, pos))
|
Err(err_impl(message, s, pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build a span
|
||||||
|
pub(crate) fn spos(s: &str, pos: &usize) -> Option<Box<SourcePosition>> {
|
||||||
|
if *pos >= s.len() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Box::new(get_line_and_column(s, *pos)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+32
-13
@@ -13,7 +13,8 @@ use std::fmt;
|
|||||||
use std::str::{self, FromStr};
|
use std::str::{self, FromStr};
|
||||||
|
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
use error::{ERes, err};
|
pub use error::SourcePosition;
|
||||||
|
use error::{ERes, err, spos};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
@@ -34,11 +35,26 @@ pub enum Atom {
|
|||||||
|
|
||||||
/// An s-expression is either an atom or a list of s-expressions. This is
|
/// An s-expression is either an atom or a list of s-expressions. This is
|
||||||
/// similar to the data format used by lisp.
|
/// similar to the data format used by lisp.
|
||||||
#[derive(PartialEq, Clone, PartialOrd)]
|
#[derive(Clone)]
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum Sexp {
|
pub enum Sexp {
|
||||||
Atom(Atom),
|
/// Atom
|
||||||
List(Vec<Sexp>),
|
Atom(Atom, Option<Box<SourcePosition>>),
|
||||||
|
/// List of expressions
|
||||||
|
List(Vec<Sexp>, Option<Box<SourcePosition>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Sexp {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Sexp::Atom(a, _), Sexp::Atom(b, _)) => {
|
||||||
|
a == b
|
||||||
|
}
|
||||||
|
(Sexp::List(a, _), Sexp::List(b, _)) => {
|
||||||
|
a == b
|
||||||
|
}
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -180,30 +196,33 @@ fn parse_sexp(s: &str, pos: &mut usize) -> ERes<Sexp> {
|
|||||||
trace!("parse_sexp {}", pos);
|
trace!("parse_sexp {}", pos);
|
||||||
zspace(s, pos)?;
|
zspace(s, pos)?;
|
||||||
let (c, _) = peek(s, pos)?;
|
let (c, _) = peek(s, pos)?;
|
||||||
let r =
|
let r = if c == '(' {
|
||||||
if c == '(' { Ok(Sexp::List(parse_list(s, pos)?)) } else { Ok(Sexp::Atom(parse_atom(s, pos)?)) };
|
Ok(Sexp::List(parse_list(s, pos)?, spos(s, pos)))
|
||||||
|
} else {
|
||||||
|
Ok(Sexp::Atom(parse_atom(s, pos)?, spos(s, pos)))
|
||||||
|
};
|
||||||
zspace(s, pos)?;
|
zspace(s, pos)?;
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs an atomic s-expression from a string.
|
/// Constructs an atomic s-expression from a string.
|
||||||
pub fn atom_s(s: &str) -> Sexp {
|
pub fn atom_s(s: &str) -> Sexp {
|
||||||
Sexp::Atom(Atom::S(s.to_owned()))
|
Sexp::Atom(Atom::S(s.to_owned()), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs an atomic s-expression from an int.
|
/// Constructs an atomic s-expression from an int.
|
||||||
pub fn atom_i(i: i64) -> Sexp {
|
pub fn atom_i(i: i64) -> Sexp {
|
||||||
Sexp::Atom(Atom::I(i))
|
Sexp::Atom(Atom::I(i), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs an atomic s-expression from a float.
|
/// Constructs an atomic s-expression from a float.
|
||||||
pub fn atom_f(f: f64) -> Sexp {
|
pub fn atom_f(f: f64) -> Sexp {
|
||||||
Sexp::Atom(Atom::F(f))
|
Sexp::Atom(Atom::F(f), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a list s-expression given a slice of s-expressions.
|
/// Constructs a list s-expression given a slice of s-expressions.
|
||||||
pub fn list(xs: &[Sexp]) -> Sexp {
|
pub fn list(xs: &[Sexp]) -> Sexp {
|
||||||
Sexp::List(xs.to_owned())
|
Sexp::List(xs.to_owned(), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads an s-expression out of a `&str`.
|
/// Reads an s-expression out of a `&str`.
|
||||||
@@ -256,8 +275,8 @@ impl fmt::Display for Atom {
|
|||||||
impl fmt::Display for Sexp {
|
impl fmt::Display for Sexp {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Sexp::Atom(ref a) => write!(f, "{}", a),
|
Sexp::Atom(ref a, _) => write!(f, "{}", a),
|
||||||
Sexp::List(ref xs) => {
|
Sexp::List(ref xs, _) => {
|
||||||
write!(f, "(")?;
|
write!(f, "(")?;
|
||||||
for (i, x) in xs.iter().enumerate() {
|
for (i, x) in xs.iter().enumerate() {
|
||||||
let s = if i == 0 { "" } else { " " };
|
let s = if i == 0 { "" } else { " " };
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ fn test_pp() {
|
|||||||
fn test_tight_parens() {
|
fn test_tight_parens() {
|
||||||
let s = "(hello(world))";
|
let s = "(hello(world))";
|
||||||
let sexp = parse(s).unwrap();
|
let sexp = parse(s).unwrap();
|
||||||
assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into())),
|
assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into()), None),
|
||||||
Sexp::List(vec![Sexp::Atom(Atom::S("world".into()))])]));
|
Sexp::List(vec![Sexp::Atom(Atom::S("world".into()), None)], None)], None));
|
||||||
let s = "(this (has)tight(parens))";
|
let s = "(this (has)tight(parens))";
|
||||||
let s2 = "( this ( has ) tight ( parens ) )";
|
let s2 = "( this ( has ) tight ( parens ) )";
|
||||||
assert_eq!(parse(s).unwrap(), parse(s2).unwrap());
|
assert_eq!(parse(s).unwrap(), parse(s2).unwrap());
|
||||||
@@ -50,16 +50,16 @@ fn show_an_error() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn line_and_col_test() {
|
fn line_and_col_test() {
|
||||||
let s = "0123456789\n0123456789\n\n6";
|
let s = "0123456789\n0123456789\n\n6";
|
||||||
assert_eq!(get_line_and_column(s, 4), (1, 4));
|
assert_eq!(get_line_and_column(s, 4), SourcePosition { line: 1, column: 4, index: 4 });
|
||||||
|
|
||||||
assert_eq!(get_line_and_column(s, 10), (2, 0));
|
assert_eq!(get_line_and_column(s, 10), SourcePosition { line: 2, column: 0, index: 10 });
|
||||||
assert_eq!(get_line_and_column(s, 11), (2, 0));
|
assert_eq!(get_line_and_column(s, 11), SourcePosition { line: 2, column: 0, index: 11 });
|
||||||
assert_eq!(get_line_and_column(s, 15), (2, 4));
|
assert_eq!(get_line_and_column(s, 15), SourcePosition { line: 2, column: 4, index: 15 });
|
||||||
|
|
||||||
assert_eq!(get_line_and_column(s, 21), (3, 0));
|
assert_eq!(get_line_and_column(s, 21), SourcePosition { line: 3, column: 0, index: 21 });
|
||||||
assert_eq!(get_line_and_column(s, 22), (4, 0));
|
assert_eq!(get_line_and_column(s, 22), SourcePosition { line: 4, column: 0, index: 22 });
|
||||||
assert_eq!(get_line_and_column(s, 23), (4, 0));
|
assert_eq!(get_line_and_column(s, 23), SourcePosition { line: 4, column: 0, index: 23 });
|
||||||
assert_eq!(get_line_and_column(s, 500), (4, 0));
|
assert_eq!(get_line_and_column(s, 500), SourcePosition { line: 4, column: 0, index: 500 });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user