forked from mirrors/jj
dsl_util: add visitor-like API primarily designed for alias substitution
The templater implementation of FoldableExpression is a stripped-down version of expand_node(). It's visitor-like because I'm going to write generic alias substitution rules over abstract expression types (template, revset, fileset.) Naming comes from rustc. https://rust-unofficial.github.io/patterns/patterns/creational/fold.html
This commit is contained in:
parent
5061d4b831
commit
7be4a0a560
2 changed files with 117 additions and 2 deletions
|
@ -18,7 +18,8 @@ use std::{error, mem};
|
|||
use itertools::Itertools as _;
|
||||
use jj_lib::dsl_util::{
|
||||
self, collect_similar, AliasDeclaration, AliasDeclarationParser, AliasExpandError,
|
||||
AliasExpandableExpression, AliasId, AliasesMap, InvalidArguments, StringLiteralParser,
|
||||
AliasExpandableExpression, AliasId, AliasesMap, ExpressionFolder, FoldableExpression,
|
||||
InvalidArguments, StringLiteralParser,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use pest::iterators::{Pair, Pairs};
|
||||
|
@ -272,6 +273,53 @@ pub enum ExpressionKind<'i> {
|
|||
AliasExpanded(AliasId<'i>, Box<ExpressionNode<'i>>),
|
||||
}
|
||||
|
||||
impl<'i> FoldableExpression<'i> for ExpressionKind<'i> {
|
||||
fn fold<F>(self, folder: &mut F, span: pest::Span<'i>) -> Result<Self, F::Error>
|
||||
where
|
||||
F: ExpressionFolder<'i, Self> + ?Sized,
|
||||
{
|
||||
match self {
|
||||
ExpressionKind::Identifier(name) => folder.fold_identifier(name, span),
|
||||
ExpressionKind::Boolean(_) | ExpressionKind::Integer(_) | ExpressionKind::String(_) => {
|
||||
Ok(self)
|
||||
}
|
||||
ExpressionKind::Unary(op, arg) => {
|
||||
let arg = Box::new(folder.fold_expression(*arg)?);
|
||||
Ok(ExpressionKind::Unary(op, arg))
|
||||
}
|
||||
ExpressionKind::Binary(op, lhs, rhs) => {
|
||||
let lhs = Box::new(folder.fold_expression(*lhs)?);
|
||||
let rhs = Box::new(folder.fold_expression(*rhs)?);
|
||||
Ok(ExpressionKind::Binary(op, lhs, rhs))
|
||||
}
|
||||
ExpressionKind::Concat(nodes) => Ok(ExpressionKind::Concat(
|
||||
dsl_util::fold_expression_nodes(folder, nodes)?,
|
||||
)),
|
||||
ExpressionKind::FunctionCall(function) => folder.fold_function_call(function, span),
|
||||
ExpressionKind::MethodCall(method) => {
|
||||
// Method call is syntactically different from function call.
|
||||
let method = Box::new(MethodCallNode {
|
||||
object: folder.fold_expression(method.object)?,
|
||||
function: dsl_util::fold_function_call_args(folder, method.function)?,
|
||||
});
|
||||
Ok(ExpressionKind::MethodCall(method))
|
||||
}
|
||||
ExpressionKind::Lambda(lambda) => {
|
||||
let lambda = Box::new(LambdaNode {
|
||||
params: lambda.params,
|
||||
params_span: lambda.params_span,
|
||||
body: folder.fold_expression(lambda.body)?,
|
||||
});
|
||||
Ok(ExpressionKind::Lambda(lambda))
|
||||
}
|
||||
ExpressionKind::AliasExpanded(id, subst) => {
|
||||
let subst = Box::new(folder.fold_expression(*subst)?);
|
||||
Ok(ExpressionKind::AliasExpanded(id, subst))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> AliasExpandableExpression<'i> for ExpressionKind<'i> {
|
||||
fn identifier(name: &'i str) -> Self {
|
||||
ExpressionKind::Identifier(name)
|
||||
|
|
|
@ -130,6 +130,73 @@ pub struct InvalidArguments<'i> {
|
|||
pub span: pest::Span<'i>,
|
||||
}
|
||||
|
||||
/// Expression item that can be transformed recursively by using `folder: F`.
|
||||
pub trait FoldableExpression<'i>: Sized {
|
||||
/// Transforms `self` by applying the `folder` to inner items.
|
||||
fn fold<F>(self, folder: &mut F, span: pest::Span<'i>) -> Result<Self, F::Error>
|
||||
where
|
||||
F: ExpressionFolder<'i, Self> + ?Sized;
|
||||
}
|
||||
|
||||
/// Visitor-like interface to transform AST nodes recursively.
|
||||
pub trait ExpressionFolder<'i, T: FoldableExpression<'i>> {
|
||||
/// Transform error.
|
||||
type Error;
|
||||
|
||||
/// Transforms the expression `node`. By default, inner items are
|
||||
/// transformed recursively.
|
||||
fn fold_expression(
|
||||
&mut self,
|
||||
node: ExpressionNode<'i, T>,
|
||||
) -> Result<ExpressionNode<'i, T>, Self::Error> {
|
||||
let ExpressionNode { kind, span } = node;
|
||||
let kind = kind.fold(self, span)?;
|
||||
Ok(ExpressionNode { kind, span })
|
||||
}
|
||||
|
||||
/// Transforms identifier.
|
||||
fn fold_identifier(&mut self, name: &'i str, span: pest::Span<'i>) -> Result<T, Self::Error>;
|
||||
|
||||
/// Transforms function call.
|
||||
fn fold_function_call(
|
||||
&mut self,
|
||||
function: Box<FunctionCallNode<'i, T>>,
|
||||
span: pest::Span<'i>,
|
||||
) -> Result<T, Self::Error>;
|
||||
}
|
||||
|
||||
/// Transforms list of `nodes` by using `folder`.
|
||||
pub fn fold_expression_nodes<'i, F, T>(
|
||||
folder: &mut F,
|
||||
nodes: Vec<ExpressionNode<'i, T>>,
|
||||
) -> Result<Vec<ExpressionNode<'i, T>>, F::Error>
|
||||
where
|
||||
F: ExpressionFolder<'i, T> + ?Sized,
|
||||
T: FoldableExpression<'i>,
|
||||
{
|
||||
nodes
|
||||
.into_iter()
|
||||
.map(|node| folder.fold_expression(node))
|
||||
.try_collect()
|
||||
}
|
||||
|
||||
/// Transforms function call arguments by using `folder`.
|
||||
pub fn fold_function_call_args<'i, F, T>(
|
||||
folder: &mut F,
|
||||
function: FunctionCallNode<'i, T>,
|
||||
) -> Result<FunctionCallNode<'i, T>, F::Error>
|
||||
where
|
||||
F: ExpressionFolder<'i, T> + ?Sized,
|
||||
T: FoldableExpression<'i>,
|
||||
{
|
||||
Ok(FunctionCallNode {
|
||||
name: function.name,
|
||||
name_span: function.name_span,
|
||||
args: fold_expression_nodes(folder, function.args)?,
|
||||
args_span: function.args_span,
|
||||
})
|
||||
}
|
||||
|
||||
/// Helper to parse string literal.
|
||||
#[derive(Debug)]
|
||||
pub struct StringLiteralParser<R> {
|
||||
|
@ -267,7 +334,7 @@ pub trait AliasDeclarationParser {
|
|||
}
|
||||
|
||||
/// Expression item that supports alias substitution.
|
||||
pub trait AliasExpandableExpression<'i>: Sized {
|
||||
pub trait AliasExpandableExpression<'i>: FoldableExpression<'i> {
|
||||
/// Wraps identifier.
|
||||
fn identifier(name: &'i str) -> Self;
|
||||
/// Wraps function call.
|
||||
|
|
Loading…
Reference in a new issue