use chumsky::input::ValueInput;
use chumsky::pratt::*;
use chumsky::prelude::*;
use crate::Span;
use crate::pr::*;
use super::helpers::*;
use super::interpolation;
use super::{PExtra, TokenKind};
pub(crate) fn expr<'src, I>(
ty: impl Parser<'src, I, Ty, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
recursive(|expr| {
let literal = literal().map(ExprKind::Literal).boxed();
let identified = identified(expr.clone()).boxed();
let variant = variant(expr.clone()).map(ExprKind::Variant).boxed();
let func = func(expr.clone(), ty.clone()).boxed();
let tuple = tuple(expr.clone()).boxed();
let array = array(expr.clone()).boxed();
let nested = nested(expr.clone()).boxed();
let interpolation = interpolation().boxed();
let match_ = match_(expr.clone()).boxed();
let if_else = if_else(expr.clone()).boxed();
let term = choice((
identified,
literal,
tuple,
nested,
func,
match_,
if_else,
array,
interpolation,
variant,
))
.map_with(|kind, e| Expr::new_with_span(kind, e.span()))
.boxed();
let atom = field_lookup(term).boxed();
let atom = type_annotation(atom, ty).boxed();
let expr_ops = unary_and_binary_expr(atom).boxed();
let expr_with_range = range(expr_ops).boxed();
binary_op_parser(expr_with_range, ctrl('|').to(BinOp::Pipe).boxed())
})
}
fn unary_and_binary_expr<'src, I>(
term: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
term.pratt((
prefix(9, operator_unary(), |op, rhs: Expr, e| {
let kind = ExprKind::Unary(UnaryExpr {
op,
expr: Box::new(rhs),
});
Expr::new_with_span(kind, e.span())
}),
infix(right(8), just(TokenKind::Pow).to(BinOp::Pow), new_bin_op),
infix(left(7), operator_mul(), new_bin_op),
infix(left(6), operator_add(), new_bin_op),
infix(left(5), operator_compare(), new_bin_op),
infix(
left(4),
just(TokenKind::Coalesce).to(BinOp::Coalesce),
new_bin_op,
),
infix(left(3), just(TokenKind::And).to(BinOp::And), new_bin_op),
infix(left(2), just(TokenKind::Or).to(BinOp::Or), new_bin_op),
))
}
fn literal<'src, I>() -> impl Parser<'src, I, Literal, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
select! { TokenKind::Literal(lit) => lit }
}
fn tuple<'src, I>(
nested_expr: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, ExprKind, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
delimited_by_braces(
choice((
just(TokenKind::Range)
.ignore_then(nested_expr.clone())
.map(|expr| TupleField {
name: None,
unpack: true,
expr,
}),
ident_part()
.then_ignore(ctrl('='))
.or_not()
.then(nested_expr)
.map(|(name, expr)| TupleField {
name,
unpack: false,
expr,
}),
))
.separated_by(ctrl(','))
.allow_trailing()
.collect::<Vec<_>>(),
|_| vec![],
)
.map(ExprKind::Tuple)
.labelled("tuple")
}
fn array<'src, I>(
expr: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, ExprKind, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
delimited_by_brackets(sequence(expr), |_| vec![])
.map(ExprKind::Array)
.labelled("array")
}
fn interpolation<'src, I>() -> impl Parser<'src, I, ExprKind, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
select! {
TokenKind::Interpolation('f', string) => (ExprKind::FString as fn(_) -> _, string),
}
.validate(|(finish, string), e, emitter| {
let mut span: Span = e.span();
span.start += 2;
match interpolation::parse(string, span) {
Ok(items) => finish(items),
Err(errors) => {
for err in errors {
emitter.emit(Rich::custom(*err.span(), "invalid interpolated string"));
}
finish(vec![])
}
}
})
.labelled("interpolated string")
}
fn match_<'src, I>(
expr: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, ExprKind, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
let pattern = recursive(|pattern| {
let variant = ctrl('.')
.ignore_then(ident_part())
.then(
delimited_by_parenthesis(pattern, |s| {
Pattern::new_with_span(PatternKind::AnyOf(vec![]), s)
})
.map(Box::new)
.or_not(),
)
.map(|(path, inner)| PatternKind::Enum(path, inner));
let term = choice((
variant,
ident_part().map(PatternKind::Bind),
literal().map(PatternKind::Literal),
))
.map_with(|kind, e| Pattern::new_with_span(kind, e.span()));
term.separated_by(ctrl('|'))
.at_least(1)
.collect::<Vec<_>>()
.map_with(|terms, e| {
if terms.len() > 1 {
Pattern::new_with_span(PatternKind::AnyOf(terms), e.span())
} else {
terms.into_iter().next().unwrap()
}
})
})
.labelled("pattern");
let branch = pattern
.then_ignore(just(TokenKind::ArrowFat))
.then(expr.clone().map(Box::new))
.map(|(pattern, value)| MatchBranch { pattern, value });
keyword("match")
.ignore_then(expr.map(Box::new))
.then(
branch
.separated_by(ctrl(','))
.allow_trailing()
.at_least(1)
.collect::<Vec<_>>()
.delimited_by(ctrl('{'), ctrl('}')),
)
.map(|(subject, branches)| ExprKind::Match(Match { subject, branches }))
}
fn field_lookup<'src, I>(
expr: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
let lookup_step = ctrl('.')
.ignore_then(choice((
ident_part().map(Lookup::Name),
select! {
TokenKind::Literal(Literal::Number(i)) if i.parse::<u32>().is_ok()
=> Lookup::Position(i.parse::<i64>().unwrap())
},
)))
.map_with(|f, e| (f, e.span()));
expr.foldl(lookup_step.repeated(), |base, (lookup, span)| {
let base = Box::new(base);
let kind = ExprKind::Lookup { base, lookup };
Expr::new_with_span(kind, span)
})
}
fn range<'src, I>(
expr: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
let end_only = just(TokenKind::Range)
.ignore_then(expr.clone())
.map(|end| Range {
start: None,
end: Some(Box::new(end)),
})
.map(ExprKind::Range)
.map_with(|kind, e| Expr::new_with_span(kind, e.span()));
end_only.or(expr
.clone()
.then(just(TokenKind::Range).ignore_then(expr.or_not()).or_not())
.map_with(|(start, range), e| {
if let Some(end) = range {
Expr::new_with_span(
ExprKind::Range(Range {
start: Some(Box::new(start)),
end: end.map(Box::new),
}),
e.span(),
)
} else {
start
}
}))
}
fn nested<'src, I>(
expr: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, ExprKind, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
let var_bindings = keyword("let")
.ignore_then(ident_part().map_with(|name, e| (name, e.span())))
.then_ignore(ctrl('='))
.then(expr.clone().map(Box::new))
.then_ignore(ctrl(';'))
.map_with(|stmt, e| (stmt, e.span()));
let main = expr.map(Box::new);
let nested =
var_bindings
.repeated()
.foldr(main, |(((name, name_span), bound), binding_span), main| {
let mut span = binding_span;
span.set_end_of(main.span.as_ref().unwrap());
let kind = ExprKind::VarBinding(VarBinding {
name,
name_span,
bound,
main,
});
Box::new(Expr::new_with_span(kind, span))
});
delimited_by_parenthesis(nested.map(ExprKind::Nested), |_| ExprKind::Tuple(vec![]))
}
fn binary_op_parser<'src, I>(
term: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
op: impl Parser<'src, I, BinOp, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
let term = term.map_with(|e, ex| (e, ex.span()));
let rest = op.then(term.clone()).repeated();
term.foldl(rest, |left, (op, right)| {
let mut span = left.1;
span.set_end_of(&right.1);
let kind = ExprKind::Binary(BinaryExpr {
left: Box::new(left.0),
op,
right: Box::new(right.0),
});
(Expr::new_with_span(kind, span), span)
})
.map(|(e, _)| e)
}
fn identified<'src, I>(
expr: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, ExprKind, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
let call_arg = ident_part()
.then_ignore(ctrl('='))
.or_not()
.then(expr.clone())
.map_with(|(label, expr), e| CallArg {
label,
expr,
span: Some(e.span()),
});
let call_args = delimited_by_parenthesis(
call_arg.separated_by(ctrl(',')).allow_trailing().collect(),
|_| vec![],
);
enum Suffix {
Multi {
rest: Vec<String>,
rest_span: Span,
args: Option<Vec<CallArg>>,
},
Call(Vec<CallArg>),
Arrow(Box<Expr>),
Bare,
}
ident_part()
.map_with(|name, e| (name, e.span()))
.then(
choice((
just(TokenKind::PathSep)
.ignore_then(
ident_part()
.separated_by(just(TokenKind::PathSep))
.at_least(1)
.collect::<Vec<_>>(),
)
.map_with(|rest, e| (rest, e.span()))
.then(call_args.clone().or_not())
.map(|((rest, rest_span), args)| Suffix::Multi {
rest,
rest_span,
args,
})
.boxed(),
call_args.map(Suffix::Call).boxed(),
just(TokenKind::ArrowThin)
.ignore_then(expr.map(Box::new))
.map(Suffix::Arrow)
.boxed(),
empty().map(|_| Suffix::Bare).boxed(),
)),
)
.map_with(|((first_name, first_span), suffix), _| match suffix {
Suffix::Multi {
rest,
rest_span,
args,
} => {
let mut path = Path::from_name(first_name);
for seg in rest {
path.push(seg);
}
let mut path_span = first_span;
path_span.set_end(rest_span.end());
if let Some(args) = args {
let subject = Box::new(Expr::new_with_span(ExprKind::Ident(path), path_span));
ExprKind::Call(Call { subject, args })
} else {
ExprKind::Ident(path)
}
}
Suffix::Call(args) => {
let subject = Box::new(Expr::new_with_span(
ExprKind::Ident(Path::from_name(first_name)),
first_span,
));
ExprKind::Call(Call { subject, args })
}
Suffix::Arrow(body) => {
let param = FuncParam {
constant: false,
label: None,
name: first_name,
ty: None,
span: first_span,
};
ExprKind::FuncShort(Box::new(FuncShort { param, body }))
}
Suffix::Bare => ExprKind::Ident(Path::from_name(first_name)),
})
}
fn func<'src, I>(
expr: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
ty: impl Parser<'src, I, Ty, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, ExprKind, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
keyword("func")
.ignore_then(delimited_by_parenthesis(
func_param(ty.clone())
.separated_by(ctrl(','))
.allow_trailing()
.collect(),
|_| vec![],
))
.then(ctrl(':').ignore_then(ty.clone()).or_not())
.then_ignore(just(TokenKind::ArrowThin))
.then(expr.map(Box::new).map(Some))
.map(|((params, return_ty), body)| {
Box::new(Func {
params,
return_ty,
body,
ty_params: Vec::new(),
})
})
.map(ExprKind::Func)
.labelled("function")
}
pub fn func_param<'src, I>(
ty: impl Parser<'src, I, Ty, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, FuncParam, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
(keyword("const").or_not().map(|x| x.is_some()))
.then(
ident_part()
.then(ident_part().or_not())
.map(|(a, b)| match b {
Some(b) => (Some(a), b),
None => (None, a),
}),
)
.then(ctrl(':').ignore_then(ty.clone()).or_not())
.map_with(|((constant, (label, name)), ty), e| FuncParam {
constant,
label,
name,
ty,
span: e.span(),
})
}
pub(crate) fn path<'src, I>() -> impl Parser<'src, I, Path, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
ident_part()
.separated_by(just(TokenKind::PathSep))
.at_least(1)
.collect::<Vec<_>>()
.map(Path::new::<String, Vec<String>>)
}
pub fn variant<'src, I>(
expr: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, Variant, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
ctrl('.')
.ignore_then(ident_part())
.then(
delimited_by_parenthesis(expr, |s| Expr::new_with_span(ExprKind::Tuple(vec![]), s))
.map(Box::new)
.or_not(),
)
.map(|(name, inner)| Variant { name, inner })
}
fn operator_unary<'src, I>() -> impl Parser<'src, I, UnOp, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
(ctrl('+').to(UnOp::Pos))
.or(ctrl('-').to(UnOp::Neg))
.or(ctrl('!').to(UnOp::Not))
}
fn operator_mul<'src, I>() -> impl Parser<'src, I, BinOp, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
(just(TokenKind::DivInt).to(BinOp::DivInt))
.or(ctrl('*').to(BinOp::Mul))
.or(ctrl('/').to(BinOp::DivFloat))
.or(ctrl('%').to(BinOp::Mod))
}
fn operator_add<'src, I>() -> impl Parser<'src, I, BinOp, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
(ctrl('+').to(BinOp::Add)).or(ctrl('-').to(BinOp::Sub))
}
fn operator_compare<'src, I>() -> impl Parser<'src, I, BinOp, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
choice((
just(TokenKind::Eq).to(BinOp::Eq),
just(TokenKind::Ne).to(BinOp::Ne),
just(TokenKind::Lte).to(BinOp::Lte),
just(TokenKind::Gte).to(BinOp::Gte),
just(TokenKind::RegexSearch).to(BinOp::RegexSearch),
ctrl('<').to(BinOp::Lt),
ctrl('>').to(BinOp::Gt),
))
}
fn type_annotation<'src, I>(
expr: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
ty: impl Parser<'src, I, Ty, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
expr.then(ctrl(':').ignore_then(ty).or_not())
.map_with(|(expr, ty), e| {
if let Some(ty) = ty {
let expr = Box::new(expr);
let ty = Box::new(ty);
let kind = ExprKind::TypeAnnotation(TypeAnnotation { expr, ty });
Expr::new_with_span(kind, e.span())
} else {
expr
}
})
}
fn if_else<'src, I>(
expr: impl Parser<'src, I, Expr, PExtra<'src>> + Clone + 'src,
) -> impl Parser<'src, I, ExprKind, PExtra<'src>> + Clone + 'src
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
let expr = expr.map(Box::new);
keyword("if")
.ignore_then(expr.clone())
.then_ignore(keyword("then"))
.then(expr.clone())
.then_ignore(keyword("else"))
.then(expr)
.map(|((condition, then), els)| {
ExprKind::If(If {
condition,
then,
els,
})
})
}
fn new_bin_op<'src, 't, I>(
l: Expr,
op: BinOp,
r: Expr,
e: &mut chumsky::input::MapExtra<'src, 't, I, PExtra<'src>>,
) -> Expr
where
I: ValueInput<'src, Token = TokenKind, Span = Span>,
{
Expr::new_with_span(
ExprKind::Binary(BinaryExpr {
left: Box::new(l),
op,
right: Box::new(r),
}),
e.span(),
)
}