use super::types;
use crate::sexp;
use crate::Interpreter;
use pest::Parser;
use pest_derive::Parser;
use std::borrow::Cow;
type Expr = <super::MiniLang as Interpreter>::Expr;
type Value = <super::MiniLang as Interpreter>::Value;
type Pair<'a> = pest::iterators::Pair<'a, Rule>;
#[derive(Parser)]
#[grammar_inline = r##"
WHITESPACE = _{" " | "\t" | NEWLINE}
COMMENT = _{ ";" ~ (!NEWLINE ~ ANY)* }
list = { ("(" ~ listbody ~ ")") | ("[" ~ listbody ~ "]") }
listbody = _{ (quote | symbol | list | string | int | bool)* }
quote = {"'" ~ (list | symbol)}
int = @{ "-"? ~ ('0'..'9')+ }
string = @{ "\"" ~ (!("\\" | "\"") ~ ANY | "\\" ~ ANY)* ~ "\"" }
symbol = @{('a'..'z' | 'A'..'Z' | "λ" | "<" | ">" | "!" | "+" | "-" | "*" | "/" | "=" | "?")+}
bool = @{"#t" | "#f"}
file = {SOI ~ listbody ~ EOI}
"##]
struct MiniLangParser;
/// Parse code into a `MiniLang` expression.
pub fn parse(code: &str) -> Result<Expr, String> {
let pairs = MiniLangParser::parse(Rule::file, code).map_err(|e| e.to_string())?;
fn handle_pair(pair: Pair) -> Result<Expr, String> {
Ok(match pair.as_rule() {
Rule::file => {
// Wrap lists in a "begin" call
let mut vec = vec![sexp!(begin)];
for pair in pair.into_inner() {
let rule = pair.as_rule();
if rule != Rule::EOI {
vec.push(handle_pair(pair)?);
}
}
Expr::Compound(vec)
}
Rule::list => {
let mut vec = vec![];
for pair in pair.into_inner() {
vec.push(handle_pair(pair)?);
}
Expr::Compound(vec)
}
Rule::quote => {
// Wrap in a "Quote" type
let inner = pair.into_inner().nth(0).unwrap();
Expr::Inlined(Value::new(types::Quote(handle_pair(inner)?)))
}
Rule::string => sexp!(types::Str::from_escaped(pair.as_str())),
Rule::symbol => Expr::Symbol(Cow::Owned(pair.as_str().to_string())),
Rule::bool => Expr::Inlined(Value::new(types::Bool(pair.as_str() == "#t"))),
Rule::int => {
let value: i64 = pair.as_str().parse::<i64>().map_err(|e| e.to_string())?;
Expr::Inlined(Value::new(types::Int(value)))
}
Rule::EOI | Rule::listbody | Rule::WHITESPACE | Rule::COMMENT => unreachable!(),
})
};
for pair in pairs {
return handle_pair(pair);
}
Err("empty input".into())
}