sexp: add span info to Sexps
This commit is contained in:
@@ -4,6 +4,13 @@ use std::{cmp, fmt};
|
||||
pub struct Error {
|
||||
/// The error message.
|
||||
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.
|
||||
pub line: usize,
|
||||
/// 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 {
|
||||
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 {}
|
||||
|
||||
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 col: isize = -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;
|
||||
}
|
||||
}
|
||||
(line, cmp::max(col, 0) as usize)
|
||||
SourcePosition {
|
||||
line,
|
||||
column: cmp::max(col, 0) as usize,
|
||||
index: pos,
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err {
|
||||
let (line, column) = get_line_and_column(s, *pos);
|
||||
Box::new(Error {
|
||||
message: message,
|
||||
line: line,
|
||||
column: column,
|
||||
index: *pos,
|
||||
message,
|
||||
pos: get_line_and_column(s, *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> {
|
||||
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};
|
||||
|
||||
pub use error::Error;
|
||||
use error::{ERes, err};
|
||||
pub use error::SourcePosition;
|
||||
use error::{ERes, err, spos};
|
||||
|
||||
#[cfg(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
|
||||
/// similar to the data format used by lisp.
|
||||
#[derive(PartialEq, Clone, PartialOrd)]
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone)]
|
||||
pub enum Sexp {
|
||||
Atom(Atom),
|
||||
List(Vec<Sexp>),
|
||||
/// Atom
|
||||
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);
|
||||
zspace(s, pos)?;
|
||||
let (c, _) = peek(s, pos)?;
|
||||
let r =
|
||||
if c == '(' { Ok(Sexp::List(parse_list(s, pos)?)) } else { Ok(Sexp::Atom(parse_atom(s, pos)?)) };
|
||||
let r = if c == '(' {
|
||||
Ok(Sexp::List(parse_list(s, pos)?, spos(s, pos)))
|
||||
} else {
|
||||
Ok(Sexp::Atom(parse_atom(s, pos)?, spos(s, pos)))
|
||||
};
|
||||
zspace(s, pos)?;
|
||||
r
|
||||
}
|
||||
|
||||
/// Constructs an atomic s-expression from a string.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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`.
|
||||
@@ -256,8 +275,8 @@ impl fmt::Display for Atom {
|
||||
impl fmt::Display for Sexp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Sexp::Atom(ref a) => write!(f, "{}", a),
|
||||
Sexp::List(ref xs) => {
|
||||
Sexp::Atom(ref a, _) => write!(f, "{}", a),
|
||||
Sexp::List(ref xs, _) => {
|
||||
write!(f, "(")?;
|
||||
for (i, x) in xs.iter().enumerate() {
|
||||
let s = if i == 0 { "" } else { " " };
|
||||
|
||||
@@ -27,8 +27,8 @@ fn test_pp() {
|
||||
fn test_tight_parens() {
|
||||
let s = "(hello(world))";
|
||||
let sexp = parse(s).unwrap();
|
||||
assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into())),
|
||||
Sexp::List(vec![Sexp::Atom(Atom::S("world".into()))])]));
|
||||
assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into()), None),
|
||||
Sexp::List(vec![Sexp::Atom(Atom::S("world".into()), None)], None)], None));
|
||||
let s = "(this (has)tight(parens))";
|
||||
let s2 = "( this ( has ) tight ( parens ) )";
|
||||
assert_eq!(parse(s).unwrap(), parse(s2).unwrap());
|
||||
@@ -50,16 +50,16 @@ fn show_an_error() {
|
||||
#[test]
|
||||
fn line_and_col_test() {
|
||||
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, 11), (2, 0));
|
||||
assert_eq!(get_line_and_column(s, 15), (2, 4));
|
||||
assert_eq!(get_line_and_column(s, 10), SourcePosition { line: 2, column: 0, index: 10 });
|
||||
assert_eq!(get_line_and_column(s, 11), SourcePosition { line: 2, column: 0, index: 11 });
|
||||
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, 22), (4, 0));
|
||||
assert_eq!(get_line_and_column(s, 23), (4, 0));
|
||||
assert_eq!(get_line_and_column(s, 500), (4, 0));
|
||||
assert_eq!(get_line_and_column(s, 21), SourcePosition { line: 3, column: 0, index: 21 });
|
||||
assert_eq!(get_line_and_column(s, 22), SourcePosition { line: 4, column: 0, index: 22 });
|
||||
assert_eq!(get_line_and_column(s, 23), SourcePosition { line: 4, column: 0, index: 23 });
|
||||
assert_eq!(get_line_and_column(s, 500), SourcePosition { line: 4, column: 0, index: 500 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user