use std::iter::Peekable;
use crate::ast::{Op, Pattern, Refrain};
use crate::error::{RefrainError, Result};
#[derive(Debug, Clone, PartialEq)]
enum Sexp {
Atom(String),
List(Vec<Sexp>),
}
pub fn parse(src: &str) -> Result<Refrain> {
let tokens = tokenize(src);
let mut iter = tokens.into_iter().peekable();
let s = parse_sexp(&mut iter)?;
if iter.next().is_some() {
return Err(RefrainError::Parse(
"trailing tokens after top-level form".into(),
));
}
sexp_to_refrain(&s)
}
fn tokenize(src: &str) -> Vec<String> {
let mut tokens = Vec::new();
let mut cur = String::new();
let mut in_comment = false;
for ch in src.chars() {
if in_comment {
if ch == '\n' {
in_comment = false;
}
continue;
}
if ch == ';' {
flush(&mut cur, &mut tokens);
in_comment = true;
continue;
}
if ch.is_whitespace() {
flush(&mut cur, &mut tokens);
continue;
}
if ch == '(' || ch == ')' {
flush(&mut cur, &mut tokens);
tokens.push(ch.to_string());
continue;
}
cur.push(ch);
}
flush(&mut cur, &mut tokens);
tokens
}
fn flush(cur: &mut String, out: &mut Vec<String>) {
if !cur.is_empty() {
out.push(std::mem::take(cur));
}
}
fn parse_sexp<I: Iterator<Item = String>>(iter: &mut Peekable<I>) -> Result<Sexp> {
let tok = iter
.next()
.ok_or_else(|| RefrainError::Parse("unexpected EOF".into()))?;
if tok == "(" {
let mut items = Vec::new();
loop {
let peek = iter
.peek()
.ok_or_else(|| RefrainError::Parse("unclosed list".into()))?;
if peek == ")" {
iter.next();
return Ok(Sexp::List(items));
}
items.push(parse_sexp(iter)?);
}
} else if tok == ")" {
Err(RefrainError::Parse("unexpected `)` at top".into()))
} else {
Ok(Sexp::Atom(tok))
}
}
fn sexp_to_refrain(s: &Sexp) -> Result<Refrain> {
let items = expect_list(s, "refrain form")?;
if items.len() < 2 {
return Err(RefrainError::Parse(
"refrain form requires `refrain` head and a name".into(),
));
}
let head = expect_atom(&items[0], "refrain head")?;
if head != "refrain" {
return Err(RefrainError::Parse(format!(
"expected `refrain`, got `{}`",
head
)));
}
let name = expect_atom(&items[1], "refrain name")?.to_string();
let mut r = Refrain::new(name);
for stage in &items[2..] {
let stage_items = expect_list(stage, "stage form")?;
if stage_items.is_empty() {
return Err(RefrainError::Parse("empty stage form".into()));
}
let kind = expect_atom(&stage_items[0], "stage kind")?;
let body = sexp_to_pattern_body(&stage_items[1..])?;
match kind {
"territorialize" => r.territorialize = Some(body),
"deterritorialize" => r.deterritorialize = Some(body),
"reterritorialize" => r.reterritorialize = Some(body),
other => {
return Err(RefrainError::Parse(format!("unknown stage `{}`", other)));
}
}
}
Ok(r)
}
fn sexp_to_pattern_body(items: &[Sexp]) -> Result<Pattern> {
match items.len() {
0 => Err(RefrainError::Parse("empty stage body".into())),
1 => sexp_to_pattern(&items[0]),
_ => {
let mut pats = Vec::with_capacity(items.len());
for i in items {
pats.push(sexp_to_pattern(i)?);
}
Ok(Pattern::Seq(pats))
}
}
}
fn sexp_to_pattern(s: &Sexp) -> Result<Pattern> {
match s {
Sexp::Atom(a) => Ok(Pattern::Op(Op::Sym(a.clone()))),
Sexp::List(xs) => {
if xs.is_empty() {
return Err(RefrainError::Parse("empty pattern list".into()));
}
let head = expect_atom(&xs[0], "op head")?;
match head {
"note" => {
if xs.len() != 3 {
return Err(RefrainError::Parse("note expects (note PITCH DUR)".into()));
}
let pitch = expect_atom(&xs[1], "note pitch")?.to_string();
let dur = expect_atom(&xs[2], "note dur")?.to_string();
Ok(Pattern::Op(Op::Note { pitch, dur }))
}
"loop" => {
if xs.len() != 3 {
return Err(RefrainError::Parse("loop expects (loop COUNT BODY)".into()));
}
let count_atom = expect_atom(&xs[1], "loop count")?;
let count: u32 = count_atom.parse().map_err(|_| {
RefrainError::Parse(format!("loop count `{}` not u32", count_atom))
})?;
let body = Box::new(sexp_to_pattern(&xs[2])?);
Ok(Pattern::Op(Op::Loop { count, body }))
}
"dy/dx" => {
if xs.len() != 3 {
return Err(RefrainError::Parse("dy/dx expects (dy/dx X T)".into()));
}
let x = expect_atom(&xs[1], "dy/dx x")?.to_string();
let t = expect_atom(&xs[2], "dy/dx t")?.to_string();
Ok(Pattern::Op(Op::Diff { x, t }))
}
"quotient" => {
let mut rels = Vec::new();
for s in &xs[1..] {
rels.push(expect_atom(s, "quotient rel")?.to_string());
}
Ok(Pattern::Op(Op::Quotient { rels }))
}
_ => {
let head_owned = head.to_string();
let mut args = Vec::with_capacity(xs.len() - 1);
for s in &xs[1..] {
args.push(sexp_to_pattern(s)?);
}
Ok(Pattern::Op(Op::Call {
head: head_owned,
args,
}))
}
}
}
}
}
fn expect_list<'a>(s: &'a Sexp, what: &str) -> Result<&'a [Sexp]> {
match s {
Sexp::List(xs) => Ok(xs),
Sexp::Atom(_) => Err(RefrainError::Parse(format!("expected list ({})", what))),
}
}
fn expect_atom<'a>(s: &'a Sexp, what: &str) -> Result<&'a str> {
match s {
Sexp::Atom(a) => Ok(a.as_str()),
Sexp::List(_) => Err(RefrainError::Parse(format!("expected atom ({})", what))),
}
}