use crate::callable::{core::*, keywords::*, operators::*};
use crate::cli::Experiment;
use crate::error::Error;
use crate::internal_err;
use crate::lang::Signal;
use crate::object::{Expr, ExprList};
use crate::parser::*;
use crate::session::SessionParserConfig;
use pest::iterators::{Pair, Pairs};
use pest::pratt_parser::PrattParser;
use pest::{Parser, RuleType};
pub type ParseResult = Result<Expr, Signal>;
pub type ParseListResult = Result<ExprList, Signal>;
pub fn parse_expr<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pairs: Pairs<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
pratt
.map_primary(|pair| parse_primary(config, parser, pratt, pair))
.map_infix(|lhs, op, rhs| {
let args = vec![(None, lhs?), (None, rhs?)].into();
let op: Box<dyn Builtin> = match op.as_rule().into() {
en::Rule::add => Box::new(InfixAdd),
en::Rule::subtract => Box::new(InfixSub),
en::Rule::multiply => Box::new(InfixMul),
en::Rule::divide => Box::new(InfixDiv),
en::Rule::dollar => Box::new(InfixDollar),
en::Rule::power => Box::new(InfixPow),
en::Rule::colon => Box::new(InfixColon),
en::Rule::modulo => Box::new(InfixMod),
en::Rule::assign => Box::new(InfixAssign),
en::Rule::or => Box::new(InfixOr),
en::Rule::and => Box::new(InfixAnd),
en::Rule::vor => Box::new(InfixVectorOr),
en::Rule::vand => Box::new(InfixVectorAnd),
en::Rule::gt => Box::new(InfixGreater),
en::Rule::lt => Box::new(InfixLess),
en::Rule::gte => Box::new(InfixGreaterEqual),
en::Rule::lte => Box::new(InfixLessEqual),
en::Rule::eq => Box::new(InfixEqual),
en::Rule::neq => Box::new(InfixNotEqual),
en::Rule::pipe => Box::new(InfixPipe),
rule => {
let span = (op.as_span().start(), op.as_span().end());
return Err(Error::ParseUnexpected(rule, span).into());
}
};
Ok(Expr::Call(Box::new(Expr::Primitive(op)), args))
})
.parse(pairs)
}
fn parse_primary<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
match pair.as_rule().into() {
en::Rule::postfixed => parse_postfixed(config, parser, pratt, pair),
en::Rule::prefixed => parse_prefixed(config, parser, pratt, pair),
en::Rule::expr => parse_expr(config, parser, pratt, pair.into_inner()),
en::Rule::paren_expr => parse_paren(config, parser, pratt, pair),
en::Rule::block_exprs => parse_block(config, parser, pratt, pair),
en::Rule::kw_function => parse_function(config, parser, pratt, pair),
en::Rule::kw_while => parse_while(config, parser, pratt, pair),
en::Rule::kw_for => parse_for(config, parser, pratt, pair),
en::Rule::kw_if_else => parse_if_else(config, parser, pratt, pair),
en::Rule::kw_repeat => parse_repeat(config, parser, pratt, pair),
en::Rule::kw_break => Ok(Expr::Break),
en::Rule::kw_continue => Ok(Expr::Continue),
en::Rule::kw_return => parse_return(config, parser, pratt, pair),
en::Rule::val_true => Ok(Expr::Bool(true)),
en::Rule::val_false => Ok(Expr::Bool(false)),
en::Rule::val_null => Ok(Expr::Null),
en::Rule::val_na => Ok(Expr::NA),
en::Rule::val_inf => Ok(Expr::Inf),
en::Rule::more => Ok(Expr::More),
en::Rule::number => Ok(Expr::Number(
pair.as_str()
.replace('_', "")
.parse::<f64>()
.map_or(internal_err!(), Ok)?,
)),
en::Rule::integer => Ok(Expr::Integer(
pair.as_str()
.replace('_', "")
.parse::<i32>()
.map_or(internal_err!(), Ok)?,
)),
en::Rule::single_quoted_string => Ok(Expr::String(String::from(pair.as_str()))),
en::Rule::double_quoted_string => Ok(Expr::String(String::from(pair.as_str()))),
en::Rule::vec => parse_vec(config, parser, pratt, pair),
en::Rule::list => parse_list(config, parser, pratt, pair),
en::Rule::call => parse_call(config, parser, pratt, pair),
en::Rule::symbol_ident => parse_symbol(config, parser, pratt, pair),
en::Rule::symbol_backticked => Ok(Expr::Symbol(String::from(pair.as_str()))),
rule => {
let span = (pair.as_span().start(), pair.as_span().end());
Err(Error::ParseUnexpected(rule, span).into())
}
}
}
fn parse_paren<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let exprs: ExprList = pair
.into_inner()
.map(|i| parse_expr(config, parser, pratt, i.into_inner()))
.collect::<Result<_, _>>()?;
Ok(Expr::new_primitive_call(KeywordParen, exprs))
}
fn parse_block<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let exprs: ExprList = pair
.into_inner()
.map(|i| parse_expr(config, parser, pratt, i.into_inner()))
.collect::<Result<_, _>>()?;
Ok(Expr::new_primitive_call(KeywordBlock, exprs))
}
fn parse_named<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> Result<(Option<String>, Expr), Signal>
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let mut inner = pair.into_inner();
let name = String::from(inner.next().unwrap().as_str());
Ok((Some(name), parse_expr(config, parser, pratt, inner)?))
}
fn parse_list_elements<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseListResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let exprs: ExprList = pair
.into_inner()
.map(|i| match i.as_rule().into() {
en::Rule::named => parse_named(config, parser, pratt, i),
_ => Ok((None, parse_primary(config, parser, pratt, i)?)),
})
.collect::<Result<_, _>>()?;
Ok(exprs)
}
fn parse_call<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let mut inner = pair.into_inner();
let name = inner.next().map_or(internal_err!(), |i| Ok(i.as_str()))?;
let pairs = parse_list_elements(
config,
parser,
pratt,
inner.next().map_or(internal_err!(), Ok)?,
)?;
match name {
"list" => Ok(Expr::List(pairs)),
name => Ok(Expr::Call(Box::new(Expr::String(name.to_string())), pairs)),
}
}
fn parse_function<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let mut inner = pair.into_inner();
let params = parse_list_elements(
config,
parser,
pratt,
inner.next().map_or(internal_err!(), Ok)?,
)?
.as_formals();
let body = parse_expr(config, parser, pratt, inner)?;
Ok(Expr::Function(params, Box::new(body)))
}
fn parse_if_else<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let mut inner = pair.into_inner();
let inner_cond = inner.next().map_or(internal_err!(), Ok)?.into_inner();
let cond = parse_expr(config, parser, pratt, inner_cond)?;
let inner_true = inner.next().map_or(internal_err!(), Ok)?.into_inner();
let true_expr = parse_expr(config, parser, pratt, inner_true)?;
let false_expr = if let Some(false_block) = inner.next() {
parse_expr(config, parser, pratt, false_block.into_inner())?
} else {
Expr::Null
};
let args = ExprList::from(vec![cond, true_expr, false_expr]);
Ok(Expr::new_primitive_call(KeywordIf, args))
}
fn parse_return<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let mut inner = pair.into_inner();
let inner_expr = inner.next().map_or(internal_err!(), Ok)?.into_inner();
let expr = parse_expr(config, parser, pratt, inner_expr)?;
let args = ExprList::from(vec![expr]);
Ok(Expr::new_primitive_call(KeywordReturn, args))
}
fn parse_symbol<P, R>(
_config: &SessionParserConfig,
_parser: &P,
_pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
Ok(Expr::Symbol(String::from(pair.as_str())))
}
fn parse_for<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let mut inner = pair.into_inner();
let inner_sym = inner.next().map_or(internal_err!(), Ok)?;
let Expr::Symbol(var) = parse_symbol(config, parser, pratt, inner_sym)? else {
return internal_err!();
};
let inner_iter = inner.next().map_or(internal_err!(), Ok)?.into_inner();
let iter = parse_expr(config, parser, pratt, inner_iter)?;
let inner_body = inner.next().map_or(internal_err!(), Ok)?.into_inner();
let body = parse_expr(config, parser, pratt, inner_body)?;
let args = ExprList::from(vec![(Some(var), iter), (None, body)]);
Ok(Expr::new_primitive_call(KeywordFor, args))
}
fn parse_while<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let mut inner = pair.into_inner();
let inner_cond = inner.next().map_or(internal_err!(), Ok)?.into_inner();
let cond = parse_expr(config, parser, pratt, inner_cond)?;
let inner_body = inner.next().map_or(internal_err!(), Ok)?.into_inner();
let body = parse_expr(config, parser, pratt, inner_body)?;
let args = ExprList::from(vec![cond, body]);
Ok(Expr::new_primitive_call(KeywordWhile, args))
}
fn parse_repeat<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let mut inner = pair.into_inner();
let inner_body = inner.next().map_or(internal_err!(), Ok)?.into_inner();
let body = parse_expr(config, parser, pratt, inner_body)?;
let args = ExprList::from(vec![body]);
Ok(Expr::new_primitive_call(KeywordRepeat, args))
}
fn parse_postfix<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> Result<(Expr, ExprList), Signal>
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
match pair.as_rule().into() {
en::Rule::call => Ok((
Expr::Null,
parse_list_elements(config, parser, pratt, pair)?,
)),
en::Rule::index => {
let args = parse_list_elements(config, parser, pratt, pair)?;
Ok((Expr::as_primitive(PostfixIndex), args))
}
en::Rule::vector_index => Ok((
Expr::as_primitive(PostfixVecIndex),
parse_list_elements(config, parser, pratt, pair)?,
)),
en::Rule::more => {
let val = pair.as_str();
let is_ellipsis = val == ".";
if config.experiments.contains(&Experiment::RestArgs) {
Ok((Expr::Ellipsis(Some(val.to_string())), ExprList::new()))
} else if is_ellipsis {
Ok((Expr::Ellipsis(None), ExprList::new()))
} else {
Err(Error::FeatureDisabledRestArgs.into())
}
}
rule => {
let span = (pair.as_span().start(), pair.as_span().end());
Err(Error::ParseUnexpected(rule, span).into())
}
}
}
fn parse_postfixed<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let mut inner = pair.into_inner();
let inner_next = inner.next().map_or(internal_err!(), Ok)?;
let mut result = parse_primary(config, parser, pratt, inner_next)?;
for next in inner {
let (what, mut args) = parse_postfix(config, parser, pratt, next)?;
result = match what {
Expr::Null => Expr::Call(Box::new(result), args),
_ => {
args.insert(0, result);
Expr::Call(Box::new(what), args)
}
};
}
Ok(result)
}
fn parse_prefixed<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let mut inner = pair.into_inner().rev();
let inner_next = inner.next().map_or(internal_err!(), Ok)?;
let mut result = parse_postfixed(config, parser, pratt, inner_next)?;
for prev in inner {
result = match prev.as_rule().into() {
en::Rule::subtract => {
let args = ExprList::from(vec![result]);
Expr::new_primitive_call(PrefixSub, args)
}
en::Rule::not => {
let args = ExprList::from(vec![result]);
Expr::new_primitive_call(PrefixNot, args)
}
en::Rule::more => {
let is_ellipsis = result.to_string() == ".";
if config.experiments.contains(&Experiment::RestArgs) {
Expr::Ellipsis(Some(result.to_string()))
} else if is_ellipsis {
Expr::Ellipsis(None)
} else {
return Err(Error::FeatureDisabledRestArgs.into());
}
}
rule => {
let span = (prev.as_span().start(), prev.as_span().end());
return Err(Error::ParseUnexpected(rule, span).into());
}
}
}
Ok(result)
}
fn parse_vec<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let args = parse_list_elements(config, parser, pratt, pair)?;
Ok(Expr::new_primitive_call(KeywordVec, args))
}
fn parse_list<P, R>(
config: &SessionParserConfig,
parser: &P,
pratt: &PrattParser<R>,
pair: Pair<R>,
) -> ParseResult
where
P: Parser<R> + LocalizedParser,
R: RuleType + Into<en::Rule>,
{
let args = parse_list_elements(config, parser, pratt, pair)?;
Ok(Expr::new_primitive_call(KeywordList, args))
}
#[cfg(test)]
mod test {
use crate::r;
#[test]
fn prefix_with_space() {
assert_eq! {
r! {{"- 1"}},
r! { -1 }
}
}
#[test]
fn postfix_with_space() {
assert_eq! {
r! {{"c (1)"}},
r! {{"c(1)"}}
}
}
#[test]
fn separation_integer() {
assert_eq! {
r! {{"1_200_000L"}},
r! {{"1200000L"}}
}
}
#[test]
fn separation_double_trailing() {
assert_eq! {
r! {{".000_123"}},
r! {{".000123"}}
}
}
#[test]
fn separation_double_leading() {
assert_eq! {
r! {{"1_2.0_1"}},
r! {{"1_2.01"}}
}
}
#[test]
fn separation_double_leading_zero() {
assert_eq! {
r! {{"0.000_123"}},
r! {{"0.000123"}}
}
}
}