rudus/src/parser.rs
2024-12-08 23:33:02 -05:00

985 lines
31 KiB
Rust

use crate::lexer::*;
use crate::spans::*;
use chumsky::{input::ValueInput, prelude::*, recursive::Recursive};
use std::fmt;
use struct_scalpel::Dissectible;
#[derive(Clone, Debug, PartialEq)]
pub struct WhenClause {
pub cond: Spanned<Ast>,
pub body: Spanned<Ast>,
}
impl fmt::Display for WhenClause {
fn fmt(self: &WhenClause, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "cond: {}, body: {}", self.cond.0, self.body.0)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct MatchClause {
pub patt: Spanned<Pattern>,
pub guard: Option<Spanned<Ast>>,
pub body: Spanned<Ast>,
}
impl fmt::Display for MatchClause {
fn fmt(self: &MatchClause, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"pattern: {}, guard: {:?} body: {}",
self.patt.0, self.guard, self.body.0
)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StringPart {
Data(String),
Word(String),
Inline(String),
}
impl fmt::Display for StringPart {
fn fmt(self: &StringPart, f: &mut fmt::Formatter) -> fmt::Result {
let rep = match self {
StringPart::Word(s) => format!("{{{s}}}"),
StringPart::Data(s) => s.to_string(),
StringPart::Inline(s) => s.to_string(),
};
write!(f, "{}", rep)
}
}
#[derive(Clone, Debug, PartialEq, Dissectible)]
pub enum Ast {
Error,
Placeholder,
Nil,
Boolean(bool),
Number(f64),
Keyword(&'static str),
Word(&'static str),
String(&'static str),
Interpolated(Vec<Spanned<StringPart>>),
Block(Vec<Spanned<Self>>),
If(Box<Spanned<Self>>, Box<Spanned<Self>>, Box<Spanned<Self>>),
Tuple(Vec<Spanned<Self>>),
Arguments(Vec<Spanned<Self>>),
List(Vec<Spanned<Self>>),
Dict(Vec<Spanned<Self>>),
Let(Box<Spanned<Pattern>>, Box<Spanned<Self>>),
Box(&'static str, Box<Spanned<Self>>),
Synthetic(Box<Spanned<Self>>, Box<Spanned<Self>>, Vec<Spanned<Self>>),
When(Vec<Spanned<WhenClause>>),
Match(Box<Spanned<Self>>, Vec<MatchClause>),
Fn(&'static str, Vec<MatchClause>, Option<&'static str>),
FnDeclaration(&'static str),
Panic(Box<Spanned<Self>>),
Do(Vec<Spanned<Self>>),
Repeat(Box<Spanned<Self>>, Box<Spanned<Self>>),
Splat(&'static str),
Pair(&'static str, Box<Spanned<Self>>),
Loop(Box<Spanned<Self>>, Vec<MatchClause>),
Recur(Vec<Spanned<Self>>),
}
impl fmt::Display for Ast {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Ast::Error => write!(f, "Error"),
Ast::Nil => write!(f, "nil"),
Ast::String(s) => write!(f, "String: \"{}\"", s),
Ast::Interpolated(strs) => {
write!(
f,
"Interpolated: \"{}\"",
strs.iter()
.map(|(s, _)| s.to_string())
.collect::<Vec<_>>()
.join("")
)
}
Ast::Boolean(b) => write!(f, "Boolean: {}", b),
Ast::Number(n) => write!(f, "Number: {}", n),
Ast::Keyword(k) => write!(f, "Keyword: :{}", k),
Ast::Word(w) => write!(f, "Word: {}", w),
Ast::Block(b) => write!(
f,
"Block: <{}>",
b.iter()
.map(|(line, _)| line.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Ast::If(cond, then_branch, else_branch) => write!(
f,
"If: {} Then: {} Else: {}",
cond.0, then_branch.0, else_branch.0
),
Ast::Let(pattern, expression) => {
write!(f, "Let: {} = {}", pattern.0, expression.0)
}
Ast::Dict(entries) => write!(
f,
"#{{{}}}",
entries
.iter()
.map(|pair| pair.0.to_string())
.collect::<Vec<_>>()
.join(", ")
),
Ast::List(l) => write!(
f,
"List: [{}]",
l.iter()
.map(|(line, _)| line.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Ast::Tuple(t) | Ast::Arguments(t) => write!(
f,
"Tuple: ({})",
t.iter()
.map(|(line, _)| line.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Ast::Synthetic(root, first, rest) => write!(
f,
"Synth: [{}, {}, {}]",
root.0,
first.0,
rest.iter()
.map(|(term, _)| term.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Ast::When(clauses) => write!(
f,
"When: [{}]",
clauses
.iter()
.map(|clause| clause.0.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Ast::Placeholder => todo!(),
Ast::Box(_name, _rhs) => todo!(),
Ast::Match(value, clauses) => {
write!(
f,
"match: {} with {}",
&value.0.to_string(),
clauses
.iter()
.map(|clause| clause.to_string())
.collect::<Vec<_>>()
.join("\n")
)
}
Ast::Fn(name, clauses, _) => {
write!(
f,
"fn: {}\n{}",
name,
clauses
.iter()
.map(|clause| clause.to_string())
.collect::<Vec<_>>()
.join("\n")
)
}
Ast::FnDeclaration(_name) => todo!(),
Ast::Panic(_expr) => todo!(),
Ast::Do(terms) => {
write!(
f,
"do: {}",
terms
.iter()
.map(|(term, _)| term.to_string())
.collect::<Vec<_>>()
.join(" > ")
)
}
Ast::Repeat(_times, _body) => todo!(),
Ast::Splat(word) => {
write!(f, "splat: {}", word)
}
Ast::Pair(k, v) => {
write!(f, "pair: {} {}", k, v.0)
}
Ast::Loop(init, body) => {
write!(
f,
"loop: {} with {}",
init.0,
body.iter()
.map(|clause| clause.to_string())
.collect::<Vec<_>>()
.join("\n")
)
}
Ast::Recur(args) => {
write!(
f,
"recur: {}",
args.iter()
.map(|(arg, _)| arg.to_string())
.collect::<Vec<_>>()
.join(", ")
)
}
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PairPattern {
pub key: &'static str,
pub patt: Spanned<Pattern>,
}
impl fmt::Display for PairPattern {
fn fmt(self: &PairPattern, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "pair pattern: {}: {}", self.key, self.patt.0)
}
}
pub struct StringMatcher(pub Box<dyn Fn(String) -> Option<Vec<(String, String)>>>);
impl PartialEq for StringMatcher {
fn eq(&self, _other: &StringMatcher) -> bool {
true
}
}
impl Clone for StringMatcher {
fn clone(&self) -> StringMatcher {
unreachable!()
}
}
impl fmt::Display for StringMatcher {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "string matcher")
}
}
impl fmt::Debug for StringMatcher {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "string matcher")
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Pattern {
Nil,
Boolean(bool),
Number(f64),
String(&'static str),
Interpolated(Vec<Spanned<StringPart>>, StringMatcher),
Keyword(&'static str),
Word(&'static str),
As(&'static str, &'static str),
Splattern(Box<Spanned<Self>>),
Placeholder,
Tuple(Vec<Spanned<Self>>),
List(Vec<Spanned<Self>>),
Pair(&'static str, Box<Spanned<Self>>),
Dict(Vec<Spanned<Self>>),
}
impl fmt::Display for Pattern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Pattern::Nil => write!(f, "nil"),
Pattern::Boolean(b) => write!(f, "{}", b),
Pattern::Number(n) => write!(f, "{}", n),
Pattern::String(s) => write!(f, "{}", s),
Pattern::Keyword(k) => write!(f, ":{}", k),
Pattern::Word(w) => write!(f, "{}", w),
Pattern::As(w, t) => write!(f, "{} as {}", w, t),
Pattern::Splattern(p) => write!(f, "...{}", p.0),
Pattern::Placeholder => write!(f, "_"),
Pattern::Tuple(t) => write!(
f,
"({})",
t.iter()
.map(|x| x.0.to_string())
.collect::<Vec<_>>()
.join(", ")
),
Pattern::List(l) => write!(
f,
"({})",
l.iter()
.map(|x| x.0.to_string())
.collect::<Vec<_>>()
.join(", ")
),
Pattern::Dict(entries) => write!(
f,
"#{{{}}}",
entries
.iter()
.map(|(pair, _)| pair.to_string())
.collect::<Vec<_>>()
.join(", ")
),
Pattern::Pair(key, value) => write!(f, ":{} {}", key, value.0),
Pattern::Interpolated(strprts, _) => write!(
f,
"interpolated: \"{}\"",
strprts
.iter()
.map(|part| part.0.to_string())
.collect::<Vec<_>>()
.join("")
),
}
}
}
fn is_word_char(c: char) -> bool {
if c.is_ascii_alphanumeric() {
return true;
};
matches!(c, '_' | '/' | '?' | '!')
}
fn parse_string(s: &'static str, span: SimpleSpan) -> Result<Vec<Spanned<StringPart>>, String> {
let mut parts = vec![];
let mut current_part = String::new();
let mut start = span.start;
let mut is_word = false;
let mut chars = s.char_indices();
while let Some((i, char)) = chars.next() {
match char {
'{' => {
if is_word {
return Err("interpolations must only contain words".to_string());
};
match chars.next() {
None => return Err("unclosed brace".to_string()),
Some((_, '{')) => current_part.push('{'),
Some((i, c)) => {
if !current_part.is_empty() {
parts.push((
StringPart::Data(current_part),
SimpleSpan::new(start, start + i),
));
};
current_part = String::new();
start = i;
is_word = true;
if c.is_ascii_lowercase() {
current_part.push(c);
} else {
return Err("interpolations must only contain words".to_string());
}
}
}
}
'}' => {
if is_word {
parts.push((
StringPart::Word(current_part.clone()),
SimpleSpan::new(start, start + i),
));
current_part = String::new();
start = i;
is_word = false;
} else {
match chars.next() {
None => return Err("unclosed brace".to_string()),
Some((_, c)) => current_part.push(c),
}
}
}
_ => {
if is_word {
if is_word_char(char) {
current_part.push(char)
} else {
return Err("interpolations must only contain words".to_string());
}
} else {
current_part.push(char)
}
}
}
}
if current_part == s {
parts.push((
StringPart::Inline(current_part),
SimpleSpan::new(start, span.end),
))
}
Ok(parts)
}
pub fn compile_string_pattern(parts: Vec<Spanned<StringPart>>) -> StringMatcher {
StringMatcher(Box::new(move |scrutinee| {
let mut last_match = 0;
let mut parts_iter = parts.iter();
let mut matches = vec![];
while let Some((part, _)) = parts_iter.next() {
match part {
StringPart::Data(string) => match scrutinee.find(string.as_str()) {
Some(i) => {
// if i = 0, we're at the beginning
if i == 0 && last_match == 0 {
last_match = i + string.len();
continue;
}
// in theory, we only hit this branch if the first part is Data
unreachable!("internal Ludus error: bad string pattern")
}
None => return None,
},
StringPart::Word(word) => {
let to_test = scrutinee.get(last_match..scrutinee.len()).unwrap();
match parts_iter.next() {
None => matches.push((word.clone(), to_test.to_string())),
Some(part) => {
let (StringPart::Data(part), _) = part else {
unreachable!("internal Ludus error: bad string pattern")
};
match to_test.find(part) {
None => return None,
Some(i) => {
matches.push((
word.clone(),
to_test.get(last_match..i).unwrap().to_string(),
));
last_match = i + part.len();
continue;
}
}
}
}
}
_ => unreachable!("internal Ludus error"),
}
}
Some(matches)
}))
}
pub fn parser<I>(
) -> impl Parser<'static, I, Spanned<Ast>, extra::Err<Rich<'static, Token, Span>>> + Clone
where
I: ValueInput<'static, Token = Token, Span = Span>,
{
let mut expr = Recursive::declare();
let mut pattern = Recursive::declare();
let mut simple = Recursive::declare();
let mut nonbinding = Recursive::declare();
let separators = recursive(|separators| {
just(Token::Punctuation(","))
.or(just(Token::Punctuation("\n")))
.then(separators.clone().repeated())
});
let terminators = recursive(|terminators| {
just(Token::Punctuation(";"))
.or(just(Token::Punctuation("\n")))
.then(terminators.clone().repeated())
});
let placeholder_pattern =
select! {Token::Punctuation("_") => Pattern::Placeholder}.map_with(|p, e| (p, e.span()));
let word_pattern =
select! { Token::Word(w) => Pattern::Word(w) }.map_with(|w, e| (w, e.span()));
let atom_pattern = select! {
Token::Nil => Pattern::Nil,
Token::Boolean(b) => Pattern::Boolean(b),
Token::Number(n) => Pattern::Number(n),
Token::Keyword(k) => Pattern::Keyword(k),
}
.map_with(|a, e| (a, e.span()));
let string_pattern = select! {Token::String(s) => s}.try_map_with(|s, e| {
let parsed = parse_string(s, e.span());
match parsed {
Ok(parts) => match parts[0] {
(StringPart::Inline(_), _) => Ok((Pattern::String(s), e.span())),
_ => Ok((
Pattern::Interpolated(parts.clone(), compile_string_pattern(parts)),
e.span(),
)),
},
Err(msg) => Err(Rich::custom(e.span(), msg)),
}
});
let bare_splat = just(Token::Punctuation("...")).map_with(|_, e| {
(
Pattern::Splattern(Box::new((Pattern::Placeholder, e.span()))),
e.span(),
)
});
let splattable = word_pattern.or(placeholder_pattern);
let patt_splat = just(Token::Punctuation("..."))
.ignore_then(splattable)
.map_with(|x, e| (Pattern::Splattern(Box::new(x)), e.span()));
let splattern = patt_splat.or(bare_splat);
let tuple_pattern = pattern
.clone()
.or(splattern.clone())
.separated_by(separators.clone())
.allow_leading()
.allow_trailing()
.collect()
.delimited_by(just(Token::Punctuation("(")), just(Token::Punctuation(")")))
.map_with(|tuple, e| (Pattern::Tuple(tuple), e.span()))
.labelled("tuple pattern");
let list_pattern = pattern
.clone()
.or(splattern.clone())
.separated_by(separators.clone())
.allow_leading()
.allow_trailing()
.collect()
.delimited_by(just(Token::Punctuation("[")), just(Token::Punctuation("]")))
.map_with(|list, e| (Pattern::List(list), e.span()));
let pair_pattern = select! {Token::Keyword(k) => k}
.then(pattern.clone())
.map_with(|(key, patt), e| (Pattern::Pair(key, Box::new(patt)), e.span()));
let shorthand_pattern = select! {Token::Word(w) => w}.map_with(|w, e| {
(
Pattern::Pair(w, Box::new((Pattern::Word(w), e.span()))),
e.span(),
)
});
let dict_pattern = pair_pattern
.or(shorthand_pattern)
.or(splattern.clone())
.separated_by(separators.clone())
.allow_leading()
.allow_trailing()
.collect()
.delimited_by(
just(Token::Punctuation("#{")),
just(Token::Punctuation("}")),
)
.map_with(|dict, e| (Pattern::Dict(dict), e.span()));
let keyword = select! {Token::Keyword(k) => Ast::Keyword(k),}.map_with(|k, e| (k, e.span()));
let as_pattern = select! {Token::Word(w) => w}
.then_ignore(just(Token::Reserved("as")))
.then(select! {Token::Keyword(k) => k})
.map_with(|(w, t), e| (Pattern::As(w, t), e.span()));
pattern.define(
atom_pattern
.or(string_pattern)
.or(as_pattern)
.or(word_pattern)
.or(placeholder_pattern)
.or(tuple_pattern.clone())
.or(list_pattern)
.or(dict_pattern)
.labelled("pattern"),
);
let placeholder =
select! {Token::Punctuation("_") => Ast::Placeholder}.map_with(|p, e| (p, e.span()));
let word = select! { Token::Word(w) => Ast::Word(w) }
.map_with(|w, e| (w, e.span()))
.labelled("word");
let value = select! {
Token::Nil => Ast::Nil,
Token::Boolean(b) => Ast::Boolean(b),
Token::Number(n) => Ast::Number(n),
}
.map_with(|v, e| (v, e.span()));
let string = select! {Token::String(s) => s}.try_map_with(|s, e| {
let parsed = parse_string(s, e.span());
match parsed {
Ok(parts) => match parts[0] {
(StringPart::Inline(_), _) => Ok((Ast::String(s), e.span())),
_ => Ok((Ast::Interpolated(parts), e.span())),
},
Err(msg) => Err(Rich::custom(e.span(), msg)),
}
});
let tuple = simple
.clone()
.separated_by(separators.clone())
.allow_leading()
.allow_trailing()
.collect()
.delimited_by(just(Token::Punctuation("(")), just(Token::Punctuation(")")))
.map_with(|tuple, e| (Ast::Tuple(tuple), e.span()));
let args = simple
.clone()
.or(placeholder)
.separated_by(separators.clone())
.allow_leading()
.allow_trailing()
.collect()
.delimited_by(just(Token::Punctuation("(")), just(Token::Punctuation(")")))
.map_with(|args, e| (Ast::Arguments(args), e.span()));
let synth_root = word.or(keyword);
let synth_term = keyword.or(args);
let synthetic = synth_root
.then(synth_term.clone())
.then(synth_term.clone().repeated().collect())
.map_with(|((root, first), rest), e| {
(
Ast::Synthetic(Box::new(root), Box::new(first), rest),
e.span(),
)
});
let splat = just(Token::Punctuation("..."))
.ignore_then(word)
.map_with(|(w, _), e| {
(
Ast::Splat(if let Ast::Word(w) = w {
w
} else {
unreachable!()
}),
e.span(),
)
});
let list = simple
.clone()
.or(splat.clone())
.separated_by(separators.clone())
.allow_leading()
.allow_trailing()
.collect()
.delimited_by(just(Token::Punctuation("[")), just(Token::Punctuation("]")))
.map_with(|list, e| (Ast::List(list), e.span()));
let pair = select! {Token::Keyword(k) => k}
.then(simple.clone())
.map_with(|(key, value), e| (Ast::Pair(key, Box::new(value)), e.span()));
let shorthand = select! {Token::Word(w) => w}
.map_with(|w, e| (Ast::Pair(w, Box::new((Ast::Word(w), e.span()))), e.span()));
let dict = pair
.or(shorthand)
.or(splat.clone())
.separated_by(separators.clone())
.allow_leading()
.allow_trailing()
.collect()
.delimited_by(
just(Token::Punctuation("#{")),
just(Token::Punctuation("}")),
)
.map_with(|dict, e| (Ast::Dict(dict), e.span()));
let recur = just(Token::Reserved("recur"))
.ignore_then(tuple.clone())
.map_with(|args, e| {
let (Ast::Tuple(args), _) = args else {
unreachable!()
};
(Ast::Recur(args), e.span())
});
let block = expr
.clone()
.separated_by(terminators.clone())
.allow_leading()
.allow_trailing()
.collect()
.delimited_by(just(Token::Punctuation("{")), just(Token::Punctuation("}")))
.map_with(|block, e| (Ast::Block(block), e.span()))
.recover_with(via_parser(nested_delimiters(
Token::Punctuation("{"),
Token::Punctuation("}"),
[
(Token::Punctuation("("), Token::Punctuation(")")),
(Token::Punctuation("["), Token::Punctuation("]")),
],
|span| (Ast::Error, span),
)));
let if_ = just(Token::Reserved("if"))
.ignore_then(simple.clone())
.then_ignore(terminators.clone().or_not())
.then_ignore(just(Token::Reserved("then")))
.then(expr.clone())
.then_ignore(terminators.clone().or_not())
.then_ignore(just(Token::Reserved("else")))
.then(expr.clone())
.map_with(|((condition, then_branch), else_branch), e| {
(
Ast::If(
Box::new(condition),
Box::new(then_branch),
Box::new(else_branch),
),
e.span(),
)
});
let when_clause = simple
.clone()
.then_ignore(just(Token::Punctuation("->")))
.then(expr.clone())
.map_with(|(cond, body), e| (WhenClause { cond, body }, e.span()));
let when = just(Token::Reserved("when"))
.ignore_then(
when_clause
.separated_by(terminators.clone())
.allow_trailing()
.allow_leading()
.collect()
.delimited_by(just(Token::Punctuation("{")), just(Token::Punctuation("}"))),
)
.map_with(|clauses, e| (Ast::When(clauses), e.span()));
let guarded_clause = pattern
.clone()
.then_ignore(just(Token::Reserved("if")))
.then(simple.clone())
.then_ignore(just(Token::Punctuation("->")))
.then(expr.clone())
.map_with(|((patt, guard), body), _| MatchClause {
patt,
guard: Some(guard),
body,
});
let match_clause = pattern
.clone()
.then_ignore(just(Token::Punctuation("->")))
.then(expr.clone())
.map_with(|(patt, body), _| MatchClause {
patt,
guard: None,
body,
});
let match_ = just(Token::Reserved("match"))
.ignore_then(simple.clone())
.then_ignore(just(Token::Reserved("with")))
.then(
match_clause
.clone()
.or(guarded_clause)
.separated_by(terminators.clone())
.allow_leading()
.allow_trailing()
.collect()
.delimited_by(just(Token::Punctuation("{")), just(Token::Punctuation("}"))),
)
.map_with(|(expr, clauses), e| (Ast::Match(Box::new(expr), clauses), e.span()));
let conditional = when.or(if_).or(match_);
let panic = just(Token::Reserved("panic!"))
.ignore_then(nonbinding.clone())
.map_with(|expr, e| (Ast::Panic(Box::new(expr)), e.span()));
let do_ = just(Token::Reserved("do"))
.ignore_then(
nonbinding
.clone()
.separated_by(
just(Token::Punctuation(">")).then(just(Token::Punctuation("\n")).repeated()),
)
.collect(),
)
.map_with(|exprs, e| (Ast::Do(exprs), e.span()));
let repeat = just(Token::Reserved("repeat"))
.ignore_then(simple.clone())
.then(block.clone())
.map_with(|(count, body), e| (Ast::Repeat(Box::new(count), Box::new(body)), e.span()));
let fn_guarded = tuple_pattern
.clone()
.then_ignore(just(Token::Reserved("if")))
.then(simple.clone())
.then_ignore(just(Token::Punctuation("->")))
.then(nonbinding.clone())
.map_with(|((patt, guard), body), _| MatchClause {
patt,
body,
guard: Some(guard),
})
.labelled("function clause");
let fn_unguarded = tuple_pattern
.clone()
.then_ignore(just(Token::Punctuation("->")))
.then(nonbinding.clone())
.map_with(|(patt, body), _| MatchClause {
patt,
body,
guard: None,
})
.labelled("function clause");
let fn_clause = fn_guarded.clone().or(fn_unguarded.clone());
let lambda = just(Token::Reserved("fn"))
.ignore_then(fn_unguarded.clone())
.map_with(|clause, e| (Ast::Fn("anonymous", vec![clause], None), e.span()));
let fn_clauses = fn_clause
.clone()
.separated_by(terminators.clone())
.allow_leading()
.allow_trailing()
.collect();
let loop_multiclause = fn_clauses
.clone()
.delimited_by(just(Token::Punctuation("{")), just(Token::Punctuation("}")));
let fn_single_clause = fn_clause.clone().map_with(|c, _| vec![c]);
let r#loop = just(Token::Reserved("loop"))
.ignore_then(tuple.clone())
.then_ignore(just(Token::Reserved("with")))
.then(loop_multiclause.clone().or(fn_single_clause.clone()))
.map_with(|(init, body), e| (Ast::Loop(Box::new(init), body), e.span()));
simple.define(
synthetic
.or(recur)
.or(word)
.or(keyword)
.or(value)
.or(tuple.clone())
.or(list)
.or(dict)
.or(string)
.or(lambda.clone())
.labelled("simple expression"),
);
nonbinding.define(
simple
.clone()
.or(conditional)
.or(block)
.or(panic)
.or(do_)
.or(repeat)
.or(r#loop)
.labelled("nonbinding expression"),
);
let let_ = just(Token::Reserved("let"))
.ignore_then(pattern.clone())
.then_ignore(just(Token::Punctuation("=")))
.then(nonbinding.clone())
.map_with(|(pattern, expression), e| {
(Ast::Let(Box::new(pattern), Box::new(expression)), e.span())
});
let box_ = just(Token::Reserved("box"))
.ignore_then(word)
.then_ignore(just(Token::Punctuation("=")))
.then(nonbinding.clone())
.map_with(|(word, expr), e| {
let name = if let Ast::Word(w) = word.0 {
w
} else {
unreachable!()
};
(Ast::Box(name, Box::new(expr)), e.span())
});
let fn_decl = just(Token::Reserved("fn"))
.ignore_then(word)
.map_with(|(word, _), e| {
let name = if let Ast::Word(w) = word {
w
} else {
unreachable!()
};
(Ast::FnDeclaration(name), e.span())
});
let fn_named = just(Token::Reserved("fn"))
.ignore_then(word)
.then(fn_unguarded.clone())
.map_with(|(word, clause), e| {
let name = if let Ast::Word(word) = word.0 {
word
} else {
unreachable!()
};
(Ast::Fn(name, vec![clause], None), e.span())
});
let docstr = select! {Token::String(s) => s};
let fn_multiclause = separators
.clone()
.or_not()
.ignore_then(docstr.or_not())
.then(fn_clauses.clone())
.delimited_by(just(Token::Punctuation("{")), just(Token::Punctuation("}")))
.map_with(|(docstr, clauses), e| (docstr, clauses, e.span()));
let fn_compound = just(Token::Reserved("fn"))
.ignore_then(word)
.then(fn_multiclause)
.map_with(|(word, (docstr, clauses, _)), e| {
let name = if let Ast::Word(word) = word.0 {
word
} else {
unreachable!()
};
(Ast::Fn(name, clauses, docstr), e.span())
});
let fn_ = fn_named.or(fn_compound).or(fn_decl);
let binding = let_.or(box_).or(fn_);
expr.define(binding.or(nonbinding));
let script = expr
.separated_by(terminators.clone())
.allow_trailing()
.allow_leading()
.collect()
.map_with(|exprs, e| (Ast::Block(exprs), e.span()));
script
}