mod error;
mod input;
mod parser;
pub use self::error::ParseError;
use self::{
input::Input,
parser::{IResult, comments, hash_directives, module},
};
use crate::ast::{Comment, Expression, HashDirective};
use allocator_api2::{alloc::Allocator, vec::Vec};
pub fn parse<A: Allocator + Clone>(
source: &str,
allocator: A,
) -> Result<Vec<Expression<'_, A>, A>, ParseError> {
convert_result(module(Input::new_extra(source, allocator)), source)
}
pub fn parse_comments<A: Allocator + Clone>(
source: &str,
allocator: A,
) -> Result<Vec<Comment<'_>, A>, ParseError> {
convert_result(comments(Input::new_extra(source, allocator)), source)
}
pub fn parse_hash_directives<A: Allocator + Clone>(
source: &str,
allocator: A,
) -> Result<Vec<HashDirective<'_>, A>, ParseError> {
convert_result(hash_directives(Input::new_extra(source, allocator)), source)
}
fn convert_result<T, A: Allocator + Clone>(
result: IResult<T, A>,
source: &str,
) -> Result<T, ParseError> {
result
.map(|(_, value)| value)
.map_err(|error| ParseError::new(source, error))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::position::Position;
use allocator_api2::{alloc::Global, vec};
use pretty_assertions::assert_eq;
#[test]
fn parse_nothing() {
assert_eq!(parse("", Global), Ok(vec![]));
}
#[test]
fn parse_symbol() {
assert_eq!(
parse("foo", Global),
Ok(vec![Expression::Symbol("foo", Position::new(0, 3))])
);
}
#[test]
fn parse_shebang() {
assert_eq!(
parse("#!/bin/sh\n#t", Global),
Ok(vec![Expression::Quote(
"#",
Expression::Symbol("t", Position::new(11, 12)).into(),
Position::new(10, 12)
)])
);
}
#[test]
fn parse_lang_directive() {
assert_eq!(
parse("#lang racket\n#t", Global),
Ok(vec![Expression::Quote(
"#",
Expression::Symbol("t", Position::new(14, 15)).into(),
Position::new(13, 15)
)])
);
}
#[test]
fn parse_empty_list() {
assert_eq!(
parse("()", Global),
Ok(vec![Expression::List(
"(",
")",
vec![],
Position::new(0, 2)
)])
);
}
#[test]
fn parse_list_with_element() {
assert_eq!(
parse("(foo)", Global),
Ok(vec![Expression::List(
"(",
")",
vec![Expression::Symbol("foo", Position::new(1, 4))],
Position::new(0, 5)
)])
);
}
#[test]
fn parse_list_with_elements() {
assert_eq!(
parse("(foo bar)", Global),
Ok(vec![Expression::List(
"(",
")",
vec![
Expression::Symbol("foo", Position::new(1, 4)),
Expression::Symbol("bar", Position::new(5, 8))
],
Position::new(0, 9)
)])
);
}
#[test]
fn parse_vector() {
assert_eq!(
parse("#()", Global),
Ok(vec![Expression::Quote(
"#",
Expression::List("(", ")", vec![], Position::new(1, 3)).into(),
Position::new(0, 3)
)])
);
}
#[test]
fn parse_symbol_starting_with_escaped_hash() {
assert_eq!(
parse("\\#foo", Global),
Ok(vec![Expression::Symbol("\\#foo", Position::new(0, 5)),])
);
}
#[test]
fn parse_symbol_quoted_by_hash_and_single_quote() {
assert_eq!(
parse("#'foo", Global),
Ok(vec![Expression::Quote(
"#",
Expression::Quote(
"'",
Expression::Symbol("foo", Position::new(2, 5)).into(),
Position::new(1, 5)
)
.into(),
Position::new(0, 5)
)])
);
}
#[test]
fn parse_last_boolean_in_list() {
assert_eq!(
parse("(#f)", Global),
Ok(vec![Expression::List(
"(",
")",
vec![Expression::Quote(
"#",
Expression::Symbol("f", Position::new(2, 3)).into(),
Position::new(1, 3)
)],
Position::new(0, 4)
)])
);
}
}