use std::{cell::RefCell, fs::read_to_string, path::Path};
use crate::{
mem::MutatorRef,
parser::{Code, Input, ParseError, ParseResult, Parser},
value::{PositionMap, Value},
Position,
};
#[derive(Clone, Debug)]
pub struct Sexpr<'a, const ECO: Code> {
m: MutatorRef<'a>,
pmap: Option<RefCell<PositionMap>>,
}
impl<'a, const ECO: Code> Sexpr<'a, ECO> {
#[must_use]
pub const fn new(m: MutatorRef<'a>) -> Self {
Self { m, pmap: None }
}
#[must_use]
pub fn with_pmap(mut self, pos: PositionMap) -> Self {
self.pmap = Some(RefCell::new(pos));
self
}
#[must_use]
pub fn tracked(mut self) -> Self {
self.pmap = Some(RefCell::new(PositionMap::new()));
self
}
pub fn take_positions(&mut self) -> Option<PositionMap> {
self.pmap.take().map(RefCell::into_inner)
}
pub fn parse_script<'b>(&mut self, input: Input<'b>) -> ParseResult<'b, Box<[Value<'a>]>> {
use crate::parser::prelude::*;
let (script, cursor) = delimited(
Self::intertoken,
fold_many0(
terminated(self.datum_or_shadowed(), Self::intertoken),
Vec::new,
|mut acc, _, value| {
if let Datum::Exposed(value) = value {
acc.push(value);
}
acc
},
),
eof,
)(input)?;
Ok((script.into_boxed_slice(), cursor))
}
pub fn parse_file<P: AsRef<Path>>(&mut self, path: P) -> ParseResult<Box<[Value<'a>]>> {
use error::code::ERR_FILE;
let input = &read_to_string(path.as_ref()).map_err(|err| {
ParseError::new(
Position::new(0, 1, 1),
ERR_FILE + ECO,
SexprError::FileError(path.as_ref()),
)
.and_source(err)
})?;
let (exprs, cursor) = self.parse_script(input.into())?;
Ok((exprs, Input::eof_from(cursor)))
}
}
impl<'a, 'b, const ECO: Code> Parser<'b, 'a, Value<'a>> for Sexpr<'a, ECO> {
fn parse(&mut self, input: Input<'b>) -> ParseResult<'b, Value<'a>> {
use crate::parser::prelude::*;
terminated(
delimited(Self::intertoken, self.datum(), Self::intertoken),
eof,
)(input)
}
}
mod abbreviation;
mod boolean;
mod bytevec;
mod character;
mod datum;
mod error;
mod list;
mod number;
mod string;
mod symbol;
mod vector;
mod whitespace;
pub use datum::Datum;
#[allow(clippy::module_name_repetitions)]
pub use error::{code, SexprError};
#[cfg(test)]
mod tests {
use kamo_macros::{sexpr, sexpr_file};
use crate::{mem::Mutator, Position};
use super::*;
#[test]
fn parse_success() {
let m = Mutator::new_ref();
let mut p = Sexpr::<0>::new(m.clone());
let result = p.parse("; Adding two numbers\n(+ 1 2) ".into());
assert_eq!(result, Ok((sexpr!(m, "(+ 1 2)"), Input::new(""))));
}
#[test]
fn parse_files_success() {
let m = Mutator::new_ref();
let mut p = Sexpr::<0>::new(m.clone());
parse_file_chapter_01(m.clone(), &mut p);
parse_file_chapter_02(m.clone(), &mut p);
parse_file_chapter_03(m.clone(), &mut p);
parse_file_chapter_04(m.clone(), &mut p);
parse_file_chapter_05(m.clone(), &mut p);
parse_file_chapter_06(m.clone(), &mut p);
parse_file_chapter_07(m.clone(), &mut p);
parse_file_chapter_08(m.clone(), &mut p);
parse_file_chapter_09(m.clone(), &mut p);
parse_file_chapter_10(m.clone(), &mut p);
parse_file_the_little_schemer(&mut p);
}
#[allow(clippy::needless_pass_by_value)]
fn parse_file_chapter_01(m: MutatorRef<'_>, p: &mut Sexpr<0>) {
let expected = &sexpr_file!(m, "tests/sexpr/chapter-01.scm");
let result = p.parse_file("tests/sexpr/chapter-01.scm");
assert!(result.is_ok());
let (exprs, rest) = result.unwrap();
assert_eq!(rest.position(), Position::new(4864, 184, 66));
assert_eq!(exprs.len(), expected.len());
for (i, (actual, expected)) in exprs.iter().zip(expected.iter()).enumerate() {
assert_eq!(actual, expected, "Expression {} does not match", i + 1);
}
}
#[allow(clippy::needless_pass_by_value)]
fn parse_file_chapter_02(m: MutatorRef<'_>, p: &mut Sexpr<0>) {
let expected = &sexpr_file!(m, "tests/sexpr/chapter-02.scm");
let result = p.parse_file("tests/sexpr/chapter-02.scm");
assert!(result.is_ok());
let (exprs, rest) = result.unwrap();
assert_eq!(rest.position(), Position::new(1373, 48, 49));
assert_eq!(exprs.len(), expected.len());
for (i, (actual, expected)) in exprs.iter().zip(expected.iter()).enumerate() {
assert_eq!(actual, expected, "Expression {} does not match", i + 1);
}
}
#[allow(clippy::needless_pass_by_value)]
fn parse_file_chapter_03(m: MutatorRef<'_>, p: &mut Sexpr<0>) {
let expected = &sexpr_file!(m, "tests/sexpr/chapter-03.scm");
let result = p.parse_file("tests/sexpr/chapter-03.scm");
assert!(result.is_ok());
let (exprs, rest) = result.unwrap();
assert_eq!(rest.position(), Position::new(3638, 102, 56));
assert_eq!(exprs.len(), expected.len());
for (i, (actual, expected)) in exprs.iter().zip(expected.iter()).enumerate() {
assert_eq!(actual, expected, "Expression {} does not match", i + 1);
}
}
#[allow(clippy::needless_pass_by_value)]
fn parse_file_chapter_04(m: MutatorRef<'_>, p: &mut Sexpr<0>) {
let expected = &sexpr_file!(m, "tests/sexpr/chapter-04.scm");
let result = p.parse_file("tests/sexpr/chapter-04.scm");
assert!(result.is_ok());
let (exprs, rest) = result.unwrap();
assert_eq!(rest.position(), Position::new(3861, 161, 23));
assert_eq!(exprs.len(), expected.len());
for (i, (actual, expected)) in exprs.iter().zip(expected.iter()).enumerate() {
assert_eq!(actual, expected, "Expression {} does not match", i + 1);
}
}
#[allow(clippy::needless_pass_by_value)]
fn parse_file_chapter_05(m: MutatorRef<'_>, p: &mut Sexpr<0>) {
let expected = &sexpr_file!(m, "tests/sexpr/chapter-05.scm");
let result = p.parse_file("tests/sexpr/chapter-05.scm");
assert!(result.is_ok());
let (exprs, rest) = result.unwrap();
assert_eq!(rest.position(), Position::new(5786, 152, 61));
assert_eq!(exprs.len(), expected.len());
for (i, (actual, expected)) in exprs.iter().zip(expected.iter()).enumerate() {
assert_eq!(actual, expected, "Expression {} does not match", i + 1);
}
}
#[allow(clippy::needless_pass_by_value)]
fn parse_file_chapter_06(m: MutatorRef<'_>, p: &mut Sexpr<0>) {
let expected = &sexpr_file!(m, "tests/sexpr/chapter-06.scm");
let result = p.parse_file("tests/sexpr/chapter-06.scm");
assert!(result.is_ok());
let (exprs, rest) = result.unwrap();
assert_eq!(rest.position(), Position::new(1640, 65, 92));
assert_eq!(exprs.len(), expected.len());
for (i, (actual, expected)) in exprs.iter().zip(expected.iter()).enumerate() {
assert_eq!(actual, expected, "Expression {} does not match", i + 1);
}
}
#[allow(clippy::needless_pass_by_value)]
fn parse_file_chapter_07(m: MutatorRef<'_>, p: &mut Sexpr<0>) {
let expected = &sexpr_file!(m, "tests/sexpr/chapter-07.scm");
let result = p.parse_file("tests/sexpr/chapter-07.scm");
assert!(result.is_ok());
let (exprs, rest) = result.unwrap();
assert_eq!(rest.position(), Position::new(2589, 86, 63));
assert_eq!(exprs.len(), expected.len());
for (i, (actual, expected)) in exprs.iter().zip(expected.iter()).enumerate() {
assert_eq!(actual, expected, "Expression {} does not match", i + 1);
}
}
#[allow(clippy::needless_pass_by_value)]
fn parse_file_chapter_08(m: MutatorRef<'_>, p: &mut Sexpr<0>) {
let expected = &sexpr_file!(m, "tests/sexpr/chapter-08.scm");
let result = p.parse_file("tests/sexpr/chapter-08.scm");
assert!(result.is_ok());
let (exprs, rest) = result.unwrap();
assert_eq!(rest.position(), Position::new(2342, 76, 44));
assert_eq!(exprs.len(), expected.len());
for (i, (actual, expected)) in exprs.iter().zip(expected.iter()).enumerate() {
assert_eq!(actual, expected, "Expression {} does not match", i + 1);
}
}
#[allow(clippy::needless_pass_by_value)]
fn parse_file_chapter_09(m: MutatorRef<'_>, p: &mut Sexpr<0>) {
let expected = &sexpr_file!(m, "tests/sexpr/chapter-09.scm");
let result = p.parse_file("tests/sexpr/chapter-09.scm");
assert!(result.is_ok());
let (exprs, rest) = result.unwrap();
assert_eq!(rest.position(), Position::new(1011, 44, 25));
assert_eq!(exprs.len(), expected.len());
for (i, (actual, expected)) in exprs.iter().zip(expected.iter()).enumerate() {
assert_eq!(actual, expected, "Expression {} does not match", i + 1);
}
}
#[allow(clippy::needless_pass_by_value)]
fn parse_file_chapter_10(m: MutatorRef<'_>, p: &mut Sexpr<0>) {
let expected = &sexpr_file!(m, "tests/sexpr/chapter-10.scm");
let result = p.parse_file("tests/sexpr/chapter-10.scm");
assert!(result.is_ok());
let (exprs, rest) = result.unwrap();
assert_eq!(rest.position(), Position::new(2796, 88, 67));
assert_eq!(exprs.len(), expected.len());
for (i, (actual, expected)) in exprs.iter().zip(expected.iter()).enumerate() {
assert_eq!(actual, expected, "Expression {} does not match", i + 1);
}
}
fn parse_file_the_little_schemer(p: &mut Sexpr<0>) {
let result = p.parse_file("tests/sexpr/the-little-schemer.scm");
assert!(result.is_ok());
let (exprs, rest) = result.unwrap();
assert_eq!(rest.position(), Position::new(36810, 1245, 4));
assert_eq!(exprs.len(), 147);
}
}