<commit_before>//! Various source mapping utilities

use std::borrow::Cow;
use std::{fmt, io};
use std::path::{Path, PathBuf};

use index::{ByteIndex, ByteOffset, ColumnIndex, LineIndex, LineOffset, RawIndex, RawOffset};
use span::ByteSpan;

#[derive(Clone, Debug)]
pub enum FileName {
    /// A real file on disk
    Real(PathBuf),
    /// A synthetic file, eg. from the REPL
    Virtual(Cow<'static, str>),
}

impl From<PathBuf> for FileName {
    fn from(name: PathBuf) -> FileName {
        FileName::real(name)
    }
}

impl<'a> From<&'a Path> for FileName {
    fn from(name: &Path) -> FileName {
        FileName::real(name)
    }
}

impl From<String> for FileName {
    fn from(name: String) -> FileName {
        FileName::virtual_(name)
    }
}

impl From<&'static str> for FileName {
    fn from(name: &'static str) -> FileName {
        FileName::virtual_(name)
    }
}

impl FileName {
    pub fn real<T: Into<PathBuf>>(name: T) -> FileName {
        FileName::Real(name.into())
    }

    pub fn virtual_<T: Into<Cow<'static, str>>>(name: T) -> FileName {
        FileName::Virtual(name.into())
    }
}

impl fmt::Display for FileName {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FileName::Real(ref path) => write!(fmt, "{}", path.display()),
            FileName::Virtual(ref name) => write!(fmt, "<{}>", name),
        }
    }
}

#[derive(Debug, Fail, PartialEq)]
pub enum LineIndexError {
    #[fail(display = "Line out of bounds - given: {:?}, max: {:?}", given, max)]
    OutOfBounds { given: LineIndex, max: LineIndex },
}

#[derive(Debug, Fail, PartialEq)]
pub enum ByteIndexError {
    #[fail(display = "Byte index out of bounds - given: {}, span: {}", given, span)]
    OutOfBounds { given: ByteIndex, span: ByteSpan },
    #[fail(display = "Byte index points within a character boundary - given: {}", given)]
    InvalidCharBoundary { given: ByteIndex },
}

#[derive(Debug, Fail, PartialEq)]
pub enum SpanError {
    #[fail(display = "Span out of bounds - given: {}, span: {}", given, span)]
    OutOfBounds { given: ByteSpan, span: ByteSpan },
}

#[derive(Debug)]
/// Some source code
pub struct FileMap {
    /// The name of the file that the source came from
    name: FileName,
    /// The complete source code
    src: String,
    /// The span of the source in the `CodeMap`
    span: ByteSpan,
    /// Offsets to the line beginnings in the source
    lines: Vec<ByteOffset>,
}

impl FileMap {
    pub fn anonymous(src: String) -> FileMap {
        Self::new(FileName::virtual_(""), src, ByteIndex::from(0))
    }

    /// Construct a new filemap, creating an index of line start locations
    pub(crate) fn new(name: FileName, src: String, start: ByteIndex) -> FileMap {
        use std::iter;

        let span = ByteSpan::from_offset(start, ByteOffset::from_str(&src));
        let lines = {
            let newline_off = ByteOffset::from_char_utf8('\n');
            let offsets = src.match_indices('\n')
                .map(|(i, _)| ByteOffset(i as RawOffset) + newline_off);

            iter::once(ByteOffset(0)).chain(offsets).collect()
        };

        FileMap {
            name,
            src,
            span,
            lines,
        }
    }

    /// Read some source code from a file, loading it into a filemap
    pub(crate) fn from_disk<P: Into<PathBuf>>(name: P, start: ByteIndex) -> io::Result<FileMap> {
        use std::fs::File;
        use std::io::Read;

        let name = name.into();
        let mut file = File::open(&name)?;
        let mut src = String::new();
        file.read_to_string(&mut src)?;

        Ok(FileMap::new(FileName::Real(name), src, start))
    }

    /// The name of the file that the source came from
    pub fn name(&self) -> &FileName {
        &self.name
    }

    /// The underlying source code
    pub fn src(&self) -> &str {
        &self.src
    }

    /// The span of the source in the `CodeMap`
    pub fn span(&self) -> ByteSpan {
        self.span
    }

    /// Returns the byte offset to the start of `line`
    pub fn line_offset(&self, index: LineIndex) -> Result<ByteOffset, LineIndexError> {
        self.lines
            .get(index.to_usize())
            .cloned()
            .ok_or_else(|| LineIndexError::OutOfBounds {
                given: index,
                max: LineIndex(self.lines.len() as RawIndex - 1),
            })
    }

    /// Returns the byte index of the start of `line`
    pub fn line_byte_index(&self, index: LineIndex) -> Result<ByteIndex, LineIndexError> {
        self.line_offset(index)
            .map(|offset| self.span.start() + offset)
    }

    /// Returns the byte offset to the start of `line`
    pub fn line_span(&self, line: LineIndex) -> Result<ByteSpan, LineIndexError> {
        let start = self.span.start() + self.line_offset(line)?;
        let end = match self.line_offset(line + LineOffset(1)) {
            Ok(offset_hi) => self.span.start() + offset_hi,
            Err(_) => self.span.end(),
        };

        Ok(ByteSpan::new(end, start))
    }

    /// Returns the line and column location of `byte`
    pub fn location(&self, index: ByteIndex) -> Result<(LineIndex, ColumnIndex), ByteIndexError> {
        let line_index = self.find_line(index)?;
        let line_span = self.line_span(line_index).unwrap(); // line_index should be valid!
        let line_slice = self.src_slice(line_span).unwrap(); // line_span should be valid!
        let byte_col = index - line_span.start();
        let column_index =
            ColumnIndex(line_slice[..byte_col.to_usize()].chars().count() as RawIndex);

        Ok((line_index, column_index))
    }

    /// Returns the line index that the byte index points to
    pub fn find_line(&self, index: ByteIndex) -> Result<LineIndex, ByteIndexError> {
        if index < self.span.start() || index > self.span.end() {
            Err(ByteIndexError::OutOfBounds {
                given: index,
                span: self.span,
            })
        } else {
            let offset = index - self.span.start();

            if self.src.is_char_boundary(offset.to_usize()) {
                match self.lines.binary_search(&offset) {
                    Ok(i) => Ok(LineIndex(i as RawIndex)),
                    Err(i) => Ok(LineIndex(i as RawIndex - 1)),
                }
            } else {
                Err(ByteIndexError::InvalidCharBoundary {
                    given: self.span.start(),
                })
            }
        }
    }

    /// Get the corresponding source string for a span
    ///
    /// Returns `Err` if the span is outside the bounds of the file
    pub fn src_slice(&self, span: ByteSpan) -> Result<&str, SpanError> {
        if self.span.contains(span) {
            let start = (span.start() - self.span.start()).to_usize();
            let end = (span.end() - self.span.start()).to_usize();

            // TODO: check char boundaries
            Ok(&self.src[start..end])
        } else {
            Err(SpanError::OutOfBounds {
                given: span,
                span: self.span,
            })
        }
    }
}

#[cfg(test)]
mod tests {
    use std::sync::Arc;
    use {CodeMap, FileMap, FileName};

    use super::*;

    struct TestData {
        filemap: Arc<FileMap>,
        lines: &'static [&'static str],
    }

    impl TestData {
        fn new() -> TestData {
            let mut codemap = CodeMap::new();
            let lines = &["hello!\n", "howdy\n", "\n", "hi萤\n", "bloop\n"];
            let filemap = codemap.add_filemap(FileName::Virtual("test".into()), lines.concat());

            TestData { filemap, lines }
        }

        fn byte_offsets(&self) -> Vec<ByteOffset> {
            let mut offset = ByteOffset(0);
            let mut byte_offsets = Vec::new();

            for line in self.lines {
                byte_offsets.push(offset);
                offset += ByteOffset::from_str(line);
                byte_offsets.push(offset + -ByteOffset::from_char_utf8('\n'));
            }

            // bump us past the end
            byte_offsets.push(offset);

            byte_offsets
        }

        fn byte_indices(&self) -> Vec<ByteIndex> {
            let mut offsets = vec![ByteIndex::none()];
            offsets.extend(self.byte_offsets().iter().map(|&off| ByteIndex(1) + off));
            let out_of_bounds = *offsets.last().unwrap() + ByteOffset(1);
            offsets.push(out_of_bounds);
            offsets
        }

        fn line_indices(&self) -> Vec<LineIndex> {
            (0..self.lines.len() + 2)
                .map(|i| LineIndex(i as RawIndex))
                .collect()
        }
    }

    #[test]
    fn line_offset() {
        let test_data = TestData::new();
        let offsets: Vec<_> = test_data
            .line_indices()
            .iter()
            .map(|&i| test_data.filemap.line_offset(i))
            .collect();

        assert_eq!(
            offsets,
            vec![
                Ok(ByteOffset(0)),
                Ok(ByteOffset(7)),
                Ok(ByteOffset(13)),
                Ok(ByteOffset(14)),
                Ok(ByteOffset(20)),
                Ok(ByteOffset(26)),
                Err(LineIndexError::OutOfBounds {
                    given: LineIndex(6),
                    max: LineIndex(5),
                }),
            ],
        );
    }

    #[test]
    fn line_byte_index() {
        let test_data = TestData::new();
        let offsets: Vec<_> = test_data
            .line_indices()
            .iter()
            .map(|&i| test_data.filemap.line_byte_index(i))
            .collect();

        assert_eq!(
            offsets,
            vec![
                Ok(test_data.filemap.span().start() + ByteOffset(0)),
                Ok(test_data.filemap.span().start() + ByteOffset(7)),
                Ok(test_data.filemap.span().start() + ByteOffset(13)),
                Ok(test_data.filemap.span().start() + ByteOffset(14)),
                Ok(test_data.filemap.span().start() + ByteOffset(20)),
                Ok(test_data.filemap.span().start() + ByteOffset(26)),
                Err(LineIndexError::OutOfBounds {
                    given: LineIndex(6),
                    max: LineIndex(5),
                }),
            ],
        );
    }

    // #[test]
    // fn line_span() {
    //     let filemap = filemap();
    //     let start = filemap.span().start();

    //     assert_eq!(filemap.line_byte_index(Li(0)), Some(start + BOff(0)));
    //     assert_eq!(filemap.line_byte_index(Li(1)), Some(start + BOff(7)));
    //     assert_eq!(filemap.line_byte_index(Li(2)), Some(start + BOff(13)));
    //     assert_eq!(filemap.line_byte_index(Li(3)), Some(start + BOff(14)));
    //     assert_eq!(filemap.line_byte_index(Li(4)), Some(start + BOff(20)));
    //     assert_eq!(filemap.line_byte_index(Li(5)), Some(start + BOff(26)));
    //     assert_eq!(filemap.line_byte_index(Li(6)), None);
    // }

    #[test]
    fn location() {
        let test_data = TestData::new();
        let lines: Vec<_> = test_data
            .byte_indices()
            .iter()
            .map(|&index| test_data.filemap.location(index))
            .collect();

        assert_eq!(
            lines,
            vec![
                Err(ByteIndexError::OutOfBounds {
                    given: ByteIndex(0),
                    span: test_data.filemap.span(),
                }),
                Ok((LineIndex(0), ColumnIndex(0))),
                Ok((LineIndex(0), ColumnIndex(6))),
                Ok((LineIndex(1), ColumnIndex(0))),
                Ok((LineIndex(1), ColumnIndex(5))),
                Ok((LineIndex(2), ColumnIndex(0))),
                Ok((LineIndex(2), ColumnIndex(0))),
                Ok((LineIndex(3), ColumnIndex(0))),
                Ok((LineIndex(3), ColumnIndex(3))),
                Ok((LineIndex(4), ColumnIndex(0))),
                Ok((LineIndex(4), ColumnIndex(5))),
                Ok((LineIndex(5), ColumnIndex(0))),
                Err(ByteIndexError::OutOfBounds {
                    given: ByteIndex(28),
                    span: test_data.filemap.span(),
                }),
            ],
        );
    }

    #[test]
    fn find_line() {
        let test_data = TestData::new();
        let lines: Vec<_> = test_data
            .byte_indices()
            .iter()
            .map(|&index| test_data.filemap.find_line(index))
            .collect();

        assert_eq!(
            lines,
            vec![
                Err(ByteIndexError::OutOfBounds {
                    given: ByteIndex(0),
                    span: test_data.filemap.span(),
                }),
                Ok(LineIndex(0)),
                Ok(LineIndex(0)),
                Ok(LineIndex(1)),
                Ok(LineIndex(1)),
                Ok(LineIndex(2)),
                Ok(LineIndex(2)),
                Ok(LineIndex(3)),
                Ok(LineIndex(3)),
                Ok(LineIndex(4)),
                Ok(LineIndex(4)),
                Ok(LineIndex(5)),
                Err(ByteIndexError::OutOfBounds {
                    given: ByteIndex(28),
                    span: test_data.filemap.span(),
                }),
            ],
        );
    }
}
<commit_msg>Add row, column -> byte index translation<commit_after>//! Various source mapping utilities

use std::borrow::Cow;
use std::{fmt, io};
use std::path::{Path, PathBuf};

use index::{ByteIndex, ByteOffset, ColumnIndex, LineIndex, LineOffset, RawIndex, RawOffset};
use span::ByteSpan;

#[derive(Clone, Debug)]
pub enum FileName {
    /// A real file on disk
    Real(PathBuf),
    /// A synthetic file, eg. from the REPL
    Virtual(Cow<'static, str>),
}

impl From<PathBuf> for FileName {
    fn from(name: PathBuf) -> FileName {
        FileName::real(name)
    }
}

impl<'a> From<&'a Path> for FileName {
    fn from(name: &Path) -> FileName {
        FileName::real(name)
    }
}

impl From<String> for FileName {
    fn from(name: String) -> FileName {
        FileName::virtual_(name)
    }
}

impl From<&'static str> for FileName {
    fn from(name: &'static str) -> FileName {
        FileName::virtual_(name)
    }
}

impl FileName {
    pub fn real<T: Into<PathBuf>>(name: T) -> FileName {
        FileName::Real(name.into())
    }

    pub fn virtual_<T: Into<Cow<'static, str>>>(name: T) -> FileName {
        FileName::Virtual(name.into())
    }
}

impl fmt::Display for FileName {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FileName::Real(ref path) => write!(fmt, "{}", path.display()),
            FileName::Virtual(ref name) => write!(fmt, "<{}>", name),
        }
    }
}

#[derive(Debug, Fail, PartialEq)]
pub enum LineIndexError {
    #[fail(display = "Line out of bounds - given: {:?}, max: {:?}", given, max)]
    OutOfBounds { given: LineIndex, max: LineIndex },
}

#[derive(Debug, Fail, PartialEq)]
pub enum ByteIndexError {
    #[fail(display = "Byte index out of bounds - given: {}, span: {}", given, span)]
    OutOfBounds { given: ByteIndex, span: ByteSpan },
    #[fail(display = "Byte index points within a character boundary - given: {}", given)]
    InvalidCharBoundary { given: ByteIndex },
}

#[derive(Debug, Fail, PartialEq)]
pub enum SpanError {
    #[fail(display = "Span out of bounds - given: {}, span: {}", given, span)]
    OutOfBounds { given: ByteSpan, span: ByteSpan },
}

#[derive(Debug)]
/// Some source code
pub struct FileMap {
    /// The name of the file that the source came from
    name: FileName,
    /// The complete source code
    src: String,
    /// The span of the source in the `CodeMap`
    span: ByteSpan,
    /// Offsets to the line beginnings in the source
    lines: Vec<ByteOffset>,
}

impl FileMap {
    pub fn anonymous(src: String) -> FileMap {
        Self::new(FileName::virtual_(""), src, ByteIndex::from(0))
    }

    /// Construct a new filemap, creating an index of line start locations
    pub(crate) fn new(name: FileName, src: String, start: ByteIndex) -> FileMap {
        use std::iter;

        let span = ByteSpan::from_offset(start, ByteOffset::from_str(&src));
        let lines = {
            let newline_off = ByteOffset::from_char_utf8('\n');
            let offsets = src.match_indices('\n')
                .map(|(i, _)| ByteOffset(i as RawOffset) + newline_off);

            iter::once(ByteOffset(0)).chain(offsets).collect()
        };

        FileMap {
            name,
            src,
            span,
            lines,
        }
    }

    /// Read some source code from a file, loading it into a filemap
    pub(crate) fn from_disk<P: Into<PathBuf>>(name: P, start: ByteIndex) -> io::Result<FileMap> {
        use std::fs::File;
        use std::io::Read;

        let name = name.into();
        let mut file = File::open(&name)?;
        let mut src = String::new();
        file.read_to_string(&mut src)?;

        Ok(FileMap::new(FileName::Real(name), src, start))
    }

    /// The name of the file that the source came from
    pub fn name(&self) -> &FileName {
        &self.name
    }

    /// The underlying source code
    pub fn src(&self) -> &str {
        &self.src
    }

    /// The span of the source in the `CodeMap`
    pub fn span(&self) -> ByteSpan {
        self.span
    }

    pub fn offset(&self, line: LineIndex, column: ColumnIndex) -> Option<ByteOffset> {
        self.line_offset(line).ok().and_then(|mut offset| {
            offset += ByteOffset::from(column.0 as i64);
            if offset.to_usize() >= self.src.len() {
                None
            } else {
                Some(offset)
            }
        })
    }

    pub fn byte_index(&self, line: LineIndex, column: ColumnIndex) -> Option<ByteIndex> {
        self.offset(line, column)
            .map(|offset| self.span.start() + offset)
    }

    /// Returns the byte offset to the start of `line`
    pub fn line_offset(&self, index: LineIndex) -> Result<ByteOffset, LineIndexError> {
        self.lines
            .get(index.to_usize())
            .cloned()
            .ok_or_else(|| LineIndexError::OutOfBounds {
                given: index,
                max: LineIndex(self.lines.len() as RawIndex - 1),
            })
    }

    /// Returns the byte index of the start of `line`
    pub fn line_byte_index(&self, index: LineIndex) -> Result<ByteIndex, LineIndexError> {
        self.line_offset(index)
            .map(|offset| self.span.start() + offset)
    }

    /// Returns the byte offset to the start of `line`
    pub fn line_span(&self, line: LineIndex) -> Result<ByteSpan, LineIndexError> {
        let start = self.span.start() + self.line_offset(line)?;
        let end = match self.line_offset(line + LineOffset(1)) {
            Ok(offset_hi) => self.span.start() + offset_hi,
            Err(_) => self.span.end(),
        };

        Ok(ByteSpan::new(end, start))
    }

    /// Returns the line and column location of `byte`
    pub fn location(&self, index: ByteIndex) -> Result<(LineIndex, ColumnIndex), ByteIndexError> {
        let line_index = self.find_line(index)?;
        let line_span = self.line_span(line_index).unwrap(); // line_index should be valid!
        let line_slice = self.src_slice(line_span).unwrap(); // line_span should be valid!
        let byte_col = index - line_span.start();
        let column_index =
            ColumnIndex(line_slice[..byte_col.to_usize()].chars().count() as RawIndex);

        Ok((line_index, column_index))
    }

    /// Returns the line index that the byte index points to
    pub fn find_line(&self, index: ByteIndex) -> Result<LineIndex, ByteIndexError> {
        if index < self.span.start() || index > self.span.end() {
            Err(ByteIndexError::OutOfBounds {
                given: index,
                span: self.span,
            })
        } else {
            let offset = index - self.span.start();

            if self.src.is_char_boundary(offset.to_usize()) {
                match self.lines.binary_search(&offset) {
                    Ok(i) => Ok(LineIndex(i as RawIndex)),
                    Err(i) => Ok(LineIndex(i as RawIndex - 1)),
                }
            } else {
                Err(ByteIndexError::InvalidCharBoundary {
                    given: self.span.start(),
                })
            }
        }
    }

    /// Get the corresponding source string for a span
    ///
    /// Returns `Err` if the span is outside the bounds of the file
    pub fn src_slice(&self, span: ByteSpan) -> Result<&str, SpanError> {
        if self.span.contains(span) {
            let start = (span.start() - self.span.start()).to_usize();
            let end = (span.end() - self.span.start()).to_usize();

            // TODO: check char boundaries
            Ok(&self.src[start..end])
        } else {
            Err(SpanError::OutOfBounds {
                given: span,
                span: self.span,
            })
        }
    }
}

#[cfg(test)]
mod tests {
    use std::sync::Arc;
    use {CodeMap, FileMap, FileName};

    use super::*;

    struct TestData {
        filemap: Arc<FileMap>,
        lines: &'static [&'static str],
    }

    impl TestData {
        fn new() -> TestData {
            let mut codemap = CodeMap::new();
            let lines = &["hello!\n", "howdy\n", "\n", "hi萤\n", "bloop\n"];
            let filemap = codemap.add_filemap(FileName::Virtual("test".into()), lines.concat());

            TestData { filemap, lines }
        }

        fn byte_offsets(&self) -> Vec<ByteOffset> {
            let mut offset = ByteOffset(0);
            let mut byte_offsets = Vec::new();

            for line in self.lines {
                byte_offsets.push(offset);
                offset += ByteOffset::from_str(line);
                byte_offsets.push(offset + -ByteOffset::from_char_utf8('\n'));
            }

            // bump us past the end
            byte_offsets.push(offset);

            byte_offsets
        }

        fn byte_indices(&self) -> Vec<ByteIndex> {
            let mut offsets = vec![ByteIndex::none()];
            offsets.extend(self.byte_offsets().iter().map(|&off| ByteIndex(1) + off));
            let out_of_bounds = *offsets.last().unwrap() + ByteOffset(1);
            offsets.push(out_of_bounds);
            offsets
        }

        fn line_indices(&self) -> Vec<LineIndex> {
            (0..self.lines.len() + 2)
                .map(|i| LineIndex(i as RawIndex))
                .collect()
        }
    }

    #[test]
    fn line_offset() {
        let test_data = TestData::new();
        let offsets: Vec<_> = test_data
            .line_indices()
            .iter()
            .map(|&i| test_data.filemap.line_offset(i))
            .collect();

        assert_eq!(
            offsets,
            vec![
                Ok(ByteOffset(0)),
                Ok(ByteOffset(7)),
                Ok(ByteOffset(13)),
                Ok(ByteOffset(14)),
                Ok(ByteOffset(20)),
                Ok(ByteOffset(26)),
                Err(LineIndexError::OutOfBounds {
                    given: LineIndex(6),
                    max: LineIndex(5),
                }),
            ],
        );
    }

    #[test]
    fn line_byte_index() {
        let test_data = TestData::new();
        let offsets: Vec<_> = test_data
            .line_indices()
            .iter()
            .map(|&i| test_data.filemap.line_byte_index(i))
            .collect();

        assert_eq!(
            offsets,
            vec![
                Ok(test_data.filemap.span().start() + ByteOffset(0)),
                Ok(test_data.filemap.span().start() + ByteOffset(7)),
                Ok(test_data.filemap.span().start() + ByteOffset(13)),
                Ok(test_data.filemap.span().start() + ByteOffset(14)),
                Ok(test_data.filemap.span().start() + ByteOffset(20)),
                Ok(test_data.filemap.span().start() + ByteOffset(26)),
                Err(LineIndexError::OutOfBounds {
                    given: LineIndex(6),
                    max: LineIndex(5),
                }),
            ],
        );
    }

    // #[test]
    // fn line_span() {
    //     let filemap = filemap();
    //     let start = filemap.span().start();

    //     assert_eq!(filemap.line_byte_index(Li(0)), Some(start + BOff(0)));
    //     assert_eq!(filemap.line_byte_index(Li(1)), Some(start + BOff(7)));
    //     assert_eq!(filemap.line_byte_index(Li(2)), Some(start + BOff(13)));
    //     assert_eq!(filemap.line_byte_index(Li(3)), Some(start + BOff(14)));
    //     assert_eq!(filemap.line_byte_index(Li(4)), Some(start + BOff(20)));
    //     assert_eq!(filemap.line_byte_index(Li(5)), Some(start + BOff(26)));
    //     assert_eq!(filemap.line_byte_index(Li(6)), None);
    // }

    #[test]
    fn location() {
        let test_data = TestData::new();
        let lines: Vec<_> = test_data
            .byte_indices()
            .iter()
            .map(|&index| test_data.filemap.location(index))
            .collect();

        assert_eq!(
            lines,
            vec![
                Err(ByteIndexError::OutOfBounds {
                    given: ByteIndex(0),
                    span: test_data.filemap.span(),
                }),
                Ok((LineIndex(0), ColumnIndex(0))),
                Ok((LineIndex(0), ColumnIndex(6))),
                Ok((LineIndex(1), ColumnIndex(0))),
                Ok((LineIndex(1), ColumnIndex(5))),
                Ok((LineIndex(2), ColumnIndex(0))),
                Ok((LineIndex(2), ColumnIndex(0))),
                Ok((LineIndex(3), ColumnIndex(0))),
                Ok((LineIndex(3), ColumnIndex(3))),
                Ok((LineIndex(4), ColumnIndex(0))),
                Ok((LineIndex(4), ColumnIndex(5))),
                Ok((LineIndex(5), ColumnIndex(0))),
                Err(ByteIndexError::OutOfBounds {
                    given: ByteIndex(28),
                    span: test_data.filemap.span(),
                }),
            ],
        );
    }

    #[test]
    fn find_line() {
        let test_data = TestData::new();
        let lines: Vec<_> = test_data
            .byte_indices()
            .iter()
            .map(|&index| test_data.filemap.find_line(index))
            .collect();

        assert_eq!(
            lines,
            vec![
                Err(ByteIndexError::OutOfBounds {
                    given: ByteIndex(0),
                    span: test_data.filemap.span(),
                }),
                Ok(LineIndex(0)),
                Ok(LineIndex(0)),
                Ok(LineIndex(1)),
                Ok(LineIndex(1)),
                Ok(LineIndex(2)),
                Ok(LineIndex(2)),
                Ok(LineIndex(3)),
                Ok(LineIndex(3)),
                Ok(LineIndex(4)),
                Ok(LineIndex(4)),
                Ok(LineIndex(5)),
                Err(ByteIndexError::OutOfBounds {
                    given: ByteIndex(28),
                    span: test_data.filemap.span(),
                }),
            ],
        );
    }
}
<|endoftext|>


<commit_before>extern crate "parser-combinators" as parser_combinators;

use self::parser_combinators::{try, between, spaces, string, parser, many, many1, digit, any_char, optional, hex_digit, not_followed_by, satisfy, Parser, ParserExt, ParseResult};
use self::parser_combinators::primitives::{State, Stream};

use super::ast::*;
use super::ast::ExprNode::*;

use std::str::FromStr;
use std::num::FromStrRadix;
use std::char;

/// Parser for signed integer constants.
///
/// This parses signed integer constants in decimal and hexadecimal.
///
/// TODO: add support for octal
/// TODO: add support for binary
/// TODO: add support for R6RS exponents
fn sint_const<I>(input: State<I>) -> ParseResult<NumNode, I>
    where I: Stream<Item=char> {

        fn hex_string<I>(input: State<I>) -> ParseResult<isize, I>
            where I: Stream<Item=char> {
                (satisfy(|c| c == '#')
                    .and(satisfy(|c| c == 'x' || c == 'X')))
                    .with(many1::<Vec<_>, _>(hex_digit()))
                    .map(|x| {
                        isize::from_str_radix(
                            x.iter()
                             .fold(
                                String::new(),
                                |mut s: String, i| { s.push(*i); s })
                             .as_slice(),
                        16).unwrap()
                    }).parse_state(input)
        }

        fn dec_string<I>(input: State<I>) -> ParseResult<isize, I>
            where I: Stream<Item=char> {
                optional(satisfy(|c| c == '#')
                    .and(satisfy(|c| c == 'd' || c == 'D')))
                .with(many1::<Vec<_>, _>(digit())
                    .map(|x| isize::from_str(x.iter().fold(
                        String::new(), |mut s: String, i| { s.push(*i); s })
                        .as_slice()
                    ).unwrap()
                    )).parse_state(input)
            }

        optional(satisfy(|c| c == '-'))
            .and(
                try(parser(hex_string))
                .or(parser(dec_string))
                )
            .map(|x| {
                if let Some(sign) = x.0 {
                    let mut s = String::new();
                    s.push(sign);
                    s.push('1');
                    x.1 * isize::from_str(s.as_slice()).unwrap()
                } else {
                    x.1
                }
                })
            .skip(not_followed_by(satisfy(|c|
                c == 'u' || c == 'U' || c == '.' || c == 'f' || c == 'F')
            ))
            .map(|x: isize| NumNode::IntConst(IntNode{value: x}))
            .parse_state(input)
}
/// Parser for unsigned integer constants.
///
/// This parses unssigned integer constants in decimal and hexadecimal.
///
/// TODO: add support for octal
/// TODO: add support for binary
/// TODO: add support for R6RS exponents
fn uint_const<I>(input: State<I>) -> ParseResult<NumNode, I>
    where I: Stream<Item=char> {

        fn hex_string<I>(input: State<I>) -> ParseResult<usize, I>
            where I: Stream<Item=char> {
            (satisfy(|c| c == '#')
                .and(satisfy(|c| c == 'x' || c == 'X')))
                .with(many1::<Vec<_>, _>(hex_digit()))
                .map(|x| usize::from_str_radix(
                        x.iter()
                         .fold(
                            String::new(),
                            |mut s: String, i| { s.push(*i); s })
                         .as_slice(),
                    16).unwrap()
                ).parse_state(input)
        }

        try(parser(hex_string))
        .or( many1::<Vec<_>, _>(digit())
            .map(|x|usize::from_str(x.iter().fold(
                String::new(), |mut s: String, i| { s.push(*i); s })
                .as_slice()
            ).unwrap())
        )
        .skip(satisfy(|c| c == 'u' || c == 'U'))
        .map(|x: usize| NumNode::UIntConst(UIntNode{value: x}))
        .parse_state(input)
}

/// Parser for floating-point constants.
///
/// This parses floating-point constants. Currently, this parser
/// recognizes numbers with decimal points as floating point, followed
/// by an optional `f` or `F`. Numbers with `f`s but no decimal points,
/// i.e. `1F`, are currently not recognized. While this form of number
/// is not specified by R6RS, I'd like to support it anyway as it's
/// a common form for floating-point numbers. Priority: low.
fn float_const<I>(input: State<I>) -> ParseResult<NumNode, I>
    where I: Stream<Item=char> {
        many1::<Vec<_>, _>(digit())
            .and(satisfy(|c| c == '.'))
            .and(many1::<Vec<_>, _>(digit()))
            .map(|x| {
                let mut s = String::new();
                for i in (x.0).0.iter() { s.push(*i); } ;
                s.push((x.0).1);

                for i in x.1.iter() { s.push(*i); };
                NumNode::FloatConst(FloatNode{
                    value: f64::from_str(s.as_slice()).unwrap()
                })
            })
            .skip(optional(satisfy(|c| c == 'f' || c == 'F')))
            .parse_state(input)
}

/// Parses boolean constants.
///
/// Note that this parser recognizes the strings `"true"` and `"false"`
/// as true and false. While this is not specified in R6RS, the use of
/// these tokens is common enough in other programming languages that
/// I've decided that Seax Scheme should support it as well. This may
/// be removed in a future version if it causes unforseen compatibility
/// issues.
///
/// `#t`, `#T`, or `true`  -> `true`
/// `#f`, `#F`, or `false` -> `false`
pub fn bool_const<I>(input: State<I>) -> ParseResult<BoolNode, I>
    where I: Stream<Item=char> {
        let t_const = try(string("#t"))
            .or(try(string("#T")))
            .or(try(string("true")))
            .map(|_| BoolNode{ value: true });
        let f_const = try(string("#f"))
            .or(try(string("#F")))
            .or(try(string("false")))
            .map(|_| BoolNode{ value: false });
        t_const
            .or(f_const)
            .parse_state(input)
    }

/// Parses a floating-point, signed integer, or unsigned integer constant.
pub fn number<I>(input: State<I>) -> ParseResult<NumNode, I>
    where I: Stream<Item=char> {
        try(parser(sint_const))
            .or(try(parser(uint_const)))
            .or(try(parser(float_const)))
            .parse_state(input)
}

/// Parser for valid R6RS identifiers.
///
/// An identifier may begin with an alphabetic character or
/// one of the following special characters `!`, `$`, `&`, `:`, `^`,
/// `<`, `>`, `_`,`~`,`\`, or `?`. Subsequent characters may also include
/// numbers or the special characters `+`, `-`, `.`, and `@`.
///
/// Essentially, this parser recognizes the regular expression
/// `[a-zA-Z!\$%:\^<>_~\\\?][a-zA-Z0-9!\$%:\^<>_~\\\?\+\-\.@]*`.
///
/// For more information, consult the
/// [R6RS](http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-7.html).
pub fn name<I>(input: State<I>) -> ParseResult<NameNode, I>
    where I: Stream<Item=char> {

        fn initial<I>(input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                satisfy(|c|
                    c.is_alphabetic()
                    // R6RS 'special initial' characters
                    || c == '!' || c == '$' || c == '%' || c == ':' || c == '^'
                    || c == '<' || c == '>' || c == '_' || c == '~' || c == '\\'
                    || c == '?'
                ).parse_state(input)
            }

        fn subsequent<I>(input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {

            satisfy(|c|
                c.is_alphanumeric()
                    // R6RS 'special initial' characters
                    || c == '!' || c == '$' || c == '%' || c == ':' || c == '^'
                    || c == '<' || c == '>' || c == '_' || c == '~' || c == '\\'
                    || c == '?'
                    // R6RS 'special subsequent' characters
                    || c == '+' || c == '-' || c == '.' || c == '@'
                ).parse_state(input)
            }

        fn rest<I>(input: State<I>) -> ParseResult<String, I>
            where I: Stream<Item=char> {
                many::<Vec<_>, _>(parser(subsequent))
                    .map(|it|
                        it.iter().fold(
                            String::new(),
                            |mut s: String, i| {
                                s.push(*i);
                                s
                            })
                        )
                    .parse_state(input)
            }

        parser(initial)
            .and(parser(rest))
            .parse_state(input)
            .map(|x| {
                let mut s = String::new();
                s.push((x.0).0);
                s.push_str(&(x.0).1);
                (NameNode{ name: s}, x.1)
            })
}

pub fn character<I> (input: State<I>) -> ParseResult<CharNode, I>
    where I: Stream<Item=char> {

        fn newline<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("newline"))
                    .or(try(string("linefeed")))
                    .map(|_| '\n')
                    .parse_state(input)
            }

        fn tab<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("tab")).map(|_| '\t').parse_state(input)
            }

        fn nul<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("nul"))
                    .map(|_| char::from_u32(0x0000).unwrap())
                    .parse_state(input)
            }

        fn backspace<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("backspace"))
                    .map(|_| char::from_u32(0x0008).unwrap())
                    .parse_state(input)
            }

        fn vtab<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("vtab"))
                    .map(|_| char::from_u32(0x000B).unwrap())
                    .parse_state(input)
            }

        fn page<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("page"))
                    .map(|_| char::from_u32(0x000C).unwrap())
                    .parse_state(input)
            }

        fn retn<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("return"))
                    .map(|_| char::from_u32(0x000D).unwrap())
                    .parse_state(input)
            }

        fn esc<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("esc"))
                    .map(|_| char::from_u32(0x001B).unwrap())
                    .parse_state(input)
            }

        fn delete<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("delete"))
                    .map(|_| char::from_u32(0x007F).unwrap())
                    .parse_state(input)
            }

        fn alarm<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("alarm"))
                    .map(|_| char::from_u32(0x0007).unwrap())
                    .parse_state(input)
            }

        fn space<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("space"))
                    .map(|_| char::from_u32(0x0020).unwrap())
                    .parse_state(input)
            }

        fn char_name<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                parser(newline)
                    .or(parser(tab))
                    .or(parser(vtab))
                    .or(parser(backspace))
                    .or(parser(nul))
                    .or(parser(page))
                    .or(parser(retn))
                    .or(parser(esc))
                    .or(parser(delete))
                    .or(parser(alarm))
                    .or(parser(space))
                    .parse_state(input)
        }

        string("#\\")
            .with(
                parser(char_name)
                .or(parser(any_char))
            ).map(|c| CharNode { value: c})
            .parse_state(input)

}

/// Parses Scheme expressions.
#[allow(unconditional_recursion)]
pub fn expr<I>(input: State<I>) -> ParseResult<ExprNode, I>
    where I: Stream<Item=char> {
        let spaces = spaces();

        fn sexpr<I>(input: State<I>) -> ParseResult<ExprNode, I>
            where I: Stream<Item=char> {
                between(
                    satisfy(|c| c == '('),
                    satisfy(|c| c == ')'),
                    parser(name)
                        .and(many(parser(expr)))
                        .map(|x| {
                            SExpr(SExprNode {
                                operator: x.0,
                                operands: x.1
                            })
                        })
                ).parse_state(input)
            }

        fn list<I>(input: State<I>) -> ParseResult<ExprNode, I>
            where I: Stream<Item=char> {
                between(
                    satisfy(|c| c == '('),
                    satisfy(|c| c == ')'),
                    many(parser(expr))
                        .map(|x| {
                            ListConst(ListNode {
                                elements: x
                            })
                        })
                ).parse_state(input)
            }

        spaces.clone().with(
            try(parser(sexpr))
                .or(try(parser(list)))
                .or(try(parser(name).map(Name)))
                .or(try(parser(number).map(NumConst)))
            ).parse_state(input)
}

#[cfg(test)]
mod tests;<commit_msg>Implement flexing characters for hex scalar values<commit_after>extern crate "parser-combinators" as parser_combinators;

use self::parser_combinators::{try, between, spaces, string, parser, many, many1, digit, any_char, optional, hex_digit, not_followed_by, satisfy, Parser, ParserExt, ParseResult};
use self::parser_combinators::primitives::{State, Stream};

use super::ast::*;
use super::ast::ExprNode::*;

use std::str::FromStr;
use std::num::FromStrRadix;
use std::char;

/// Parser for signed integer constants.
///
/// This parses signed integer constants in decimal and hexadecimal.
///
/// TODO: add support for octal
/// TODO: add support for binary
/// TODO: add support for R6RS exponents
fn sint_const<I>(input: State<I>) -> ParseResult<NumNode, I>
    where I: Stream<Item=char> {

        fn hex_string<I>(input: State<I>) -> ParseResult<isize, I>
            where I: Stream<Item=char> {
                (satisfy(|c| c == '#')
                    .and(satisfy(|c| c == 'x' || c == 'X')))
                    .with(many1::<Vec<_>, _>(hex_digit()))
                    .map(|x| {
                        isize::from_str_radix(
                            x.iter()
                             .fold(
                                String::new(),
                                |mut s: String, i| { s.push(*i); s })
                             .as_slice(),
                        16).unwrap()
                    }).parse_state(input)
        }

        fn dec_string<I>(input: State<I>) -> ParseResult<isize, I>
            where I: Stream<Item=char> {
                optional(satisfy(|c| c == '#')
                    .and(satisfy(|c| c == 'd' || c == 'D')))
                .with(many1::<Vec<_>, _>(digit())
                    .map(|x| isize::from_str(x.iter().fold(
                        String::new(), |mut s: String, i| { s.push(*i); s })
                        .as_slice()
                    ).unwrap()
                    )).parse_state(input)
            }

        optional(satisfy(|c| c == '-'))
            .and(
                try(parser(hex_string))
                .or(parser(dec_string))
                )
            .map(|x| {
                if let Some(sign) = x.0 {
                    let mut s = String::new();
                    s.push(sign);
                    s.push('1');
                    x.1 * isize::from_str(s.as_slice()).unwrap()
                } else {
                    x.1
                }
                })
            .skip(not_followed_by(satisfy(|c|
                c == 'u' || c == 'U' || c == '.' || c == 'f' || c == 'F')
            ))
            .map(|x: isize| NumNode::IntConst(IntNode{value: x}))
            .parse_state(input)
}
/// Parser for unsigned integer constants.
///
/// This parses unssigned integer constants in decimal and hexadecimal.
///
/// TODO: add support for octal
/// TODO: add support for binary
/// TODO: add support for R6RS exponents
fn uint_const<I>(input: State<I>) -> ParseResult<NumNode, I>
    where I: Stream<Item=char> {

        fn hex_string<I>(input: State<I>) -> ParseResult<usize, I>
            where I: Stream<Item=char> {
            (satisfy(|c| c == '#')
                .and(satisfy(|c| c == 'x' || c == 'X')))
                .with(many1::<Vec<_>, _>(hex_digit()))
                .map(|x| usize::from_str_radix(
                        x.iter()
                         .fold(
                            String::new(),
                            |mut s: String, i| { s.push(*i); s })
                         .as_slice(),
                    16).unwrap()
                ).parse_state(input)
        }

        try(parser(hex_string))
        .or( many1::<Vec<_>, _>(digit())
            .map(|x|usize::from_str(x.iter().fold(
                String::new(), |mut s: String, i| { s.push(*i); s })
                .as_slice()
            ).unwrap())
        )
        .skip(satisfy(|c| c == 'u' || c == 'U'))
        .map(|x: usize| NumNode::UIntConst(UIntNode{value: x}))
        .parse_state(input)
}

/// Parser for floating-point constants.
///
/// This parses floating-point constants. Currently, this parser
/// recognizes numbers with decimal points as floating point, followed
/// by an optional `f` or `F`. Numbers with `f`s but no decimal points,
/// i.e. `1F`, are currently not recognized. While this form of number
/// is not specified by R6RS, I'd like to support it anyway as it's
/// a common form for floating-point numbers. Priority: low.
fn float_const<I>(input: State<I>) -> ParseResult<NumNode, I>
    where I: Stream<Item=char> {
        many1::<Vec<_>, _>(digit())
            .and(satisfy(|c| c == '.'))
            .and(many1::<Vec<_>, _>(digit()))
            .map(|x| {
                let mut s = String::new();
                for i in (x.0).0.iter() { s.push(*i); } ;
                s.push((x.0).1);

                for i in x.1.iter() { s.push(*i); };
                NumNode::FloatConst(FloatNode{
                    value: f64::from_str(s.as_slice()).unwrap()
                })
            })
            .skip(optional(satisfy(|c| c == 'f' || c == 'F')))
            .parse_state(input)
}

/// Parses boolean constants.
///
/// Note that this parser recognizes the strings `"true"` and `"false"`
/// as true and false. While this is not specified in R6RS, the use of
/// these tokens is common enough in other programming languages that
/// I've decided that Seax Scheme should support it as well. This may
/// be removed in a future version if it causes unforseen compatibility
/// issues.
///
/// `#t`, `#T`, or `true`  -> `true`
/// `#f`, `#F`, or `false` -> `false`
pub fn bool_const<I>(input: State<I>) -> ParseResult<BoolNode, I>
    where I: Stream<Item=char> {
        let t_const = try(string("#t"))
            .or(try(string("#T")))
            .or(try(string("true")))
            .map(|_| BoolNode{ value: true });
        let f_const = try(string("#f"))
            .or(try(string("#F")))
            .or(try(string("false")))
            .map(|_| BoolNode{ value: false });
        t_const
            .or(f_const)
            .parse_state(input)
    }

/// Parses a floating-point, signed integer, or unsigned integer constant.
pub fn number<I>(input: State<I>) -> ParseResult<NumNode, I>
    where I: Stream<Item=char> {
        try(parser(sint_const))
            .or(try(parser(uint_const)))
            .or(try(parser(float_const)))
            .parse_state(input)
}

/// Parser for valid R6RS identifiers.
///
/// An identifier may begin with an alphabetic character or
/// one of the following special characters `!`, `$`, `&`, `:`, `^`,
/// `<`, `>`, `_`,`~`,`\`, or `?`. Subsequent characters may also include
/// numbers or the special characters `+`, `-`, `.`, and `@`.
///
/// Essentially, this parser recognizes the regular expression
/// `[a-zA-Z!\$%:\^<>_~\\\?][a-zA-Z0-9!\$%:\^<>_~\\\?\+\-\.@]*`.
///
/// For more information, consult the
/// [R6RS](http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-7.html).
pub fn name<I>(input: State<I>) -> ParseResult<NameNode, I>
    where I: Stream<Item=char> {

        fn initial<I>(input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                satisfy(|c|
                    c.is_alphabetic()
                    // R6RS 'special initial' characters
                    || c == '!' || c == '$' || c == '%' || c == ':' || c == '^'
                    || c == '<' || c == '>' || c == '_' || c == '~' || c == '\\'
                    || c == '?'
                ).parse_state(input)
            }

        fn subsequent<I>(input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {

            satisfy(|c|
                c.is_alphanumeric()
                    // R6RS 'special initial' characters
                    || c == '!' || c == '$' || c == '%' || c == ':' || c == '^'
                    || c == '<' || c == '>' || c == '_' || c == '~' || c == '\\'
                    || c == '?'
                    // R6RS 'special subsequent' characters
                    || c == '+' || c == '-' || c == '.' || c == '@'
                ).parse_state(input)
            }

        fn rest<I>(input: State<I>) -> ParseResult<String, I>
            where I: Stream<Item=char> {
                many::<Vec<_>, _>(parser(subsequent))
                    .map(|it|
                        it.iter().fold(
                            String::new(),
                            |mut s: String, i| {
                                s.push(*i);
                                s
                            })
                        )
                    .parse_state(input)
            }

        parser(initial)
            .and(parser(rest))
            .parse_state(input)
            .map(|x| {
                let mut s = String::new();
                s.push((x.0).0);
                s.push_str(&(x.0).1);
                (NameNode{ name: s}, x.1)
            })
}

pub fn character<I> (input: State<I>) -> ParseResult<CharNode, I>
    where I: Stream<Item=char> {

        fn newline<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("newline"))
                    .or(try(string("linefeed")))
                    .map(|_| '\n')
                    .parse_state(input)
            }

        fn tab<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("tab")).map(|_| '\t').parse_state(input)
            }

        fn nul<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("nul"))
                    .map(|_| char::from_u32(0x0000).unwrap())
                    .parse_state(input)
            }

        fn backspace<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("backspace"))
                    .map(|_| char::from_u32(0x0008).unwrap())
                    .parse_state(input)
            }

        fn vtab<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("vtab"))
                    .map(|_| char::from_u32(0x000B).unwrap())
                    .parse_state(input)
            }

        fn page<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("page"))
                    .map(|_| char::from_u32(0x000C).unwrap())
                    .parse_state(input)
            }

        fn retn<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("return"))
                    .map(|_| char::from_u32(0x000D).unwrap())
                    .parse_state(input)
            }

        fn esc<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("esc"))
                    .map(|_| char::from_u32(0x001B).unwrap())
                    .parse_state(input)
            }

        fn delete<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("delete"))
                    .map(|_| char::from_u32(0x007F).unwrap())
                    .parse_state(input)
            }

        fn alarm<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("alarm"))
                    .map(|_| char::from_u32(0x0007).unwrap())
                    .parse_state(input)
            }

        fn space<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                try(string("space"))
                    .map(|_| char::from_u32(0x0020).unwrap())
                    .parse_state(input)
            }

        fn char_name<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                parser(newline)
                    .or(parser(tab))
                    .or(parser(vtab))
                    .or(parser(backspace))
                    .or(parser(nul))
                    .or(parser(page))
                    .or(parser(retn))
                    .or(parser(esc))
                    .or(parser(delete))
                    .or(parser(alarm))
                    .or(parser(space))
                    .parse_state(input)
            }

        fn hex_char<I> (input: State<I>) -> ParseResult<char, I>
            where I: Stream<Item=char> {
                satisfy(|c| c == 'x')
                    .with(many1::<Vec<_>, _>(hex_digit()))
                    .map(|x| {
                        char::from_u32(
                            u32::from_str_radix(
                                x.iter()
                                 .fold(
                                    String::new(),
                                    |mut s: String, i| { s.push(*i); s })
                                 .as_slice(),
                            16).unwrap()
                        ).unwrap()
                    })
                    .parse_state(input)
            }

        string("#\\")
            .with(
                parser(char_name)
                .or(parser(hex_char))
                .or(parser(any_char))
            ).map(|c| CharNode { value: c})
            .parse_state(input)

}

/// Parses Scheme expressions.
#[allow(unconditional_recursion)]
pub fn expr<I>(input: State<I>) -> ParseResult<ExprNode, I>
    where I: Stream<Item=char> {
        let spaces = spaces();

        fn sexpr<I>(input: State<I>) -> ParseResult<ExprNode, I>
            where I: Stream<Item=char> {
                between(
                    satisfy(|c| c == '('),
                    satisfy(|c| c == ')'),
                    parser(name)
                        .and(many(parser(expr)))
                        .map(|x| {
                            SExpr(SExprNode {
                                operator: x.0,
                                operands: x.1
                            })
                        })
                ).parse_state(input)
            }

        fn list<I>(input: State<I>) -> ParseResult<ExprNode, I>
            where I: Stream<Item=char> {
                between(
                    satisfy(|c| c == '('),
                    satisfy(|c| c == ')'),
                    many(parser(expr))
                        .map(|x| {
                            ListConst(ListNode {
                                elements: x
                            })
                        })
                ).parse_state(input)
            }

        spaces.clone().with(
            try(parser(sexpr))
                .or(try(parser(list)))
                .or(try(parser(name).map(Name)))
                .or(try(parser(number).map(NumConst)))
            ).parse_state(input)
}

#[cfg(test)]
mod tests;<|endoftext|>


<commit_before>use std::collections::HashMap;
use graph_match::graph::Graph;
use graph_parser;

pub fn frame_index() -> HashMap<String, (usize, Graph)> {
    let mut index: HashMap<String, (usize, Graph)> = HashMap::new();
    let frames = vec![
        np_v(),
        np_v_np(),
        np_v_adv(),
        np_v_adv_lex(),
        np_v_adv_prep_np(),
        lex_v_np_prep_np(),
        lex_v_prep_np_np(),
        np_v_prep_np(),
        np_v_prep_np_prep_np(),
        np_v_np_prep_np(),
        prep_np_v_np(),

        copula_np_v_adj(),
    ];

    for frame in frames {
        index.insert(frame.0, (frame.1, frame.2));
    }
    return index;
}

fn np_v_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:obj\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:obj source:1 target:2 label:dobj";
    return (String::from("NP VERB NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn np_v() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:edge identifier:obj source:1 target:0 label:nsubj";
    return (String::from("NP VERB"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn np_v_adv() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:adv\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:advmod source:1 target:2 label:advmod";
    return (String::from("NP VERB ADV"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn np_v_prep_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:obj\n\
                        type:node identifier:prep\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:obj source:1 target:2 label:nmod\n\
                        type:edge identifier:case source:2 target:3 label:case";
    return (String::from("NP VERB PREP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn np_v_np_prep_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:obj\n\
                        type:node identifier:prep\n\
                        type:node identifier:np3\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:obj source:1 target:2 label:dobj\n\
                        type:edge identifier:nmod source:1 target:4 label:nmod\n\
                        type:edge identifier:case source:4 target:3 label:case";
    return (String::from("NP VERB NP PREP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

// this requires a more fuzzy match on clausal relations that are assumed not to exist elsewhere
fn np_v_prep_np_prep_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:nmod\n\
                        type:node identifier:prep\n\
                        type:node identifier:verb2 pos:VB\n\
                        type:node identifier:prep2\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:nmod source:1 target:2 label:nmod\n\
                        type:edge identifier:case source:2 target:3 label:case\n\
                        type:edge identifier:advcl source:4 target:1 label:advcl\n\
                        type:edge identifier:case source:4 target:5 label:case";
    return (String::from("NP VERB PREP NP PREP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn np_v_adv_prep_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:adv\n\
                        type:node identifier:nmod\n\
                        type:node identifier:prep\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:advmod source:1 target:2 label:advmod\n\
                        type:edge identifier:nmod source:1 target:3 label:nmod\n\
                        type:edge identifier:case source:3 target:4 label:case";
    return (String::from("NP VERB ADV PREP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

// lex should be before avd
fn np_v_adv_lex() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:dobj\n\
                        type:node identifier:adv\n\
                        type:node identifier:lex\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:obj source:1 target:2 label:dobj\n\
                        type:edge identifier:adv source:1 target:3 label:advmod\n\
                        type:edge identifier:lex source:1 target:4 label:advmod";
    return (String::from("NP VERB ADV LEX"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn prep_np_v_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:prep\n\
                        type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:obj\n\
                        type:edge identifier:nmod source:2 target:1 label:nmod\n\
                        type:edge identifier:obj source:2 target:3 label:dobj\n\
                        type:edge identifier:case source:1 target:0 label:case";
    return (String::from("PREP NP VERB NP"), 2, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn lex_v_np_prep_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:lex\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:obj\n\
                        type:node identifier:prep\n\
                        type:node identifier:nmod\n\
                        type:edge identifier:expl source:1 target:0 label:expl\n\
                        type:edge identifier:dobj source:1 target:2 label:dobj\n\
                        type:edge identifier:nmod source:1 target:3 label:nmod\n\
                        type:edge identifier:case source:3 target:4 label:case";
    return (String::from("LEX VERB NP PREP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

// uses generic dependency, this should not expand.
fn lex_v_prep_np_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:lex\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:nmod\n\
                        type:node identifier:prep\n\
                        type:node identifier:dep\n\
                        type:edge identifier:expl source:1 target:0 label:expl\n\
                        type:edge identifier:nmod source:1 target:2 label:nmod\n\
                        type:edge identifier:case source:2 target:3 label:case\n\
                        type:edge identifier:dep source:2 target:4 label:dep";
    return (String::from("LEX VERB PREP NP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

// copula frames
fn copula_np_v_adj() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:adj\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:xcomp source:1 target:2 label:xcomp";
    return (String::from("copula:NP VERB ADJ"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

<commit_msg>Add a final verb frame<commit_after>use std::collections::HashMap;
use graph_match::graph::Graph;
use graph_parser;

pub fn frame_index() -> HashMap<String, (usize, Graph)> {
    let mut index: HashMap<String, (usize, Graph)> = HashMap::new();
    let frames = vec![
        np_v(),
        np_v_np(),
        np_v_adv(),
        np_v_adv_lex(),
        np_v_adv_prep_np(),
        lex_v_np_prep_np(),
        lex_v_prep_np_np(),
        np_v_prep_np(),
        np_v_prep_np_prep_np(),
        np_v_np_prep_np(),
        np_v_np_np(),
        prep_np_v_np(),

        copula_np_v_adj(),
    ];

    for frame in frames {
        index.insert(frame.0, (frame.1, frame.2));
    }
    return index;
}

fn np_v_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:obj\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:obj source:1 target:2 label:dobj";
    return (String::from("NP VERB NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn np_v() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:edge identifier:obj source:1 target:0 label:nsubj";
    return (String::from("NP VERB"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

// not currently implementable, requires fuzzy list of edge matching
// fn np_v_lex() {
// }

fn np_v_adv() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:adv\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:advmod source:1 target:2 label:advmod";
    return (String::from("NP VERB ADV"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn np_v_np_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:dobj\n\
                        type:node identifier:xcomp\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:obj source:1 target:2 label:nmod\n\
                        type:edge identifier:xcomp source:1 target:3 label:xcomp";
    return (String::from("NP VERB NP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn np_v_prep_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:obj\n\
                        type:node identifier:prep\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:obj source:1 target:2 label:nmod\n\
                        type:edge identifier:case source:2 target:3 label:case";
    return (String::from("NP VERB PREP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn np_v_np_prep_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:obj\n\
                        type:node identifier:prep\n\
                        type:node identifier:np3\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:obj source:1 target:2 label:dobj\n\
                        type:edge identifier:nmod source:1 target:4 label:nmod\n\
                        type:edge identifier:case source:4 target:3 label:case";
    return (String::from("NP VERB NP PREP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

// this requires a more fuzzy match on clausal relations that are assumed not to exist elsewhere
fn np_v_prep_np_prep_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:nmod\n\
                        type:node identifier:prep\n\
                        type:node identifier:verb2 pos:VB\n\
                        type:node identifier:prep2\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:nmod source:1 target:2 label:nmod\n\
                        type:edge identifier:case source:2 target:3 label:case\n\
                        type:edge identifier:advcl source:4 target:1 label:advcl\n\
                        type:edge identifier:case source:4 target:5 label:case";
    return (String::from("NP VERB PREP NP PREP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn np_v_adv_prep_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:adv\n\
                        type:node identifier:nmod\n\
                        type:node identifier:prep\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:advmod source:1 target:2 label:advmod\n\
                        type:edge identifier:nmod source:1 target:3 label:nmod\n\
                        type:edge identifier:case source:3 target:4 label:case";
    return (String::from("NP VERB ADV PREP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

// lex should be before avd
fn np_v_adv_lex() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:dobj\n\
                        type:node identifier:adv\n\
                        type:node identifier:lex\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:obj source:1 target:2 label:dobj\n\
                        type:edge identifier:adv source:1 target:3 label:advmod\n\
                        type:edge identifier:lex source:1 target:4 label:advmod";
    return (String::from("NP VERB ADV LEX"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn prep_np_v_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:prep\n\
                        type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:obj\n\
                        type:edge identifier:nmod source:2 target:1 label:nmod\n\
                        type:edge identifier:obj source:2 target:3 label:dobj\n\
                        type:edge identifier:case source:1 target:0 label:case";
    return (String::from("PREP NP VERB NP"), 2, graph_parser::parse(&query_string.to_string()).unwrap());
}

fn lex_v_np_prep_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:lex\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:obj\n\
                        type:node identifier:prep\n\
                        type:node identifier:nmod\n\
                        type:edge identifier:expl source:1 target:0 label:expl\n\
                        type:edge identifier:dobj source:1 target:2 label:dobj\n\
                        type:edge identifier:nmod source:1 target:3 label:nmod\n\
                        type:edge identifier:case source:3 target:4 label:case";
    return (String::from("LEX VERB NP PREP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

// uses generic dependency, this should not expand.
fn lex_v_prep_np_np() -> (String, usize, Graph) {
    let query_string = "type:node identifier:lex\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:nmod\n\
                        type:node identifier:prep\n\
                        type:node identifier:dep\n\
                        type:edge identifier:expl source:1 target:0 label:expl\n\
                        type:edge identifier:nmod source:1 target:2 label:nmod\n\
                        type:edge identifier:case source:2 target:3 label:case\n\
                        type:edge identifier:dep source:2 target:4 label:dep";
    return (String::from("LEX VERB PREP NP NP"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

// copula frames
fn copula_np_v_adj() -> (String, usize, Graph) {
    let query_string = "type:node identifier:subj\n\
                        type:node identifier:verb pos:VB\n\
                        type:node identifier:adj\n\
                        type:edge identifier:subj source:1 target:0 label:nsubj\n\
                        type:edge identifier:xcomp source:1 target:2 label:xcomp";
    return (String::from("copula:NP VERB ADJ"), 1, graph_parser::parse(&query_string.to_string()).unwrap());
}

<|endoftext|>


<commit_before>extern crate imap;
extern crate openssl;

use openssl::ssl::{SslContext, SslMethod};
use imap::client::IMAPStream;
use imap::client::IMAPMailbox;

fn main() {
	let mut imap_socket = match IMAPStream::connect("imap.gmail.com", 993, Some(SslContext::new(SslMethod::Sslv23).unwrap())) {
		Ok(s) => s,
		Err(e) => panic!("{}", e)
	};

	if let Err(e) = imap_socket.login("username", "password") {
		println!("Error: {}", e)
	};

	match imap_socket.capability() {
		Ok(capabilities) => {
			for capability in capabilities.iter() {
				//println!("{}", capability);
			}
		},
		Err(_) => println!("Error retreiving capabilities")
	};

	match imap_socket.select("INBOX") {
		Ok(IMAPMailbox{flags, exists, recent, unseen, permanent_flags, uid_next, uid_validity}) => {
			println!("flags: {}, exists: {}, recent: {}, unseen: {:?}, parmanent_flags: {:?}, uid_next: {:?}, uid_validity: {:?}", flags, exists, recent, unseen, permanent_flags, uid_next, uid_validity);
		},
		Err(_) => println!("Error selecting INBOX")
	};

	match imap_socket.fetch("2", "body[text]") {
		Ok(lines) => {
			for line in lines.iter() {
				print!("{}", line);
			}
		},
		Err(_) => println!("Error Fetching email 2")
	};

	if let Err(e) = imap_socket.logout() {
		println!("Error: {}", e)
	};
}
<commit_msg>Uncommenting printing in main<commit_after>extern crate imap;
extern crate openssl;

use openssl::ssl::{SslContext, SslMethod};
use imap::client::IMAPStream;
use imap::client::IMAPMailbox;

fn main() {
	let mut imap_socket = match IMAPStream::connect("imap.gmail.com", 993, Some(SslContext::new(SslMethod::Sslv23).unwrap())) {
		Ok(s) => s,
		Err(e) => panic!("{}", e)
	};

	if let Err(e) = imap_socket.login("username", "password") {
		println!("Error: {}", e)
	};

	match imap_socket.capability() {
		Ok(capabilities) => {
			for capability in capabilities.iter() {
				println!("{}", capability);
			}
		},
		Err(_) => println!("Error retreiving capabilities")
	};

	match imap_socket.select("INBOX") {
		Ok(IMAPMailbox{flags, exists, recent, unseen, permanent_flags, uid_next, uid_validity}) => {
			println!("flags: {}, exists: {}, recent: {}, unseen: {:?}, parmanent_flags: {:?}, uid_next: {:?}, uid_validity: {:?}", flags, exists, recent, unseen, permanent_flags, uid_next, uid_validity);
		},
		Err(_) => println!("Error selecting INBOX")
	};

	match imap_socket.fetch("2", "body[text]") {
		Ok(lines) => {
			for line in lines.iter() {
				print!("{}", line);
			}
		},
		Err(_) => println!("Error Fetching email 2")
	};

	if let Err(e) = imap_socket.logout() {
		println!("Error: {}", e)
	};
}
<|endoftext|>