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 itertools::Itertools as _;
|
||||||
use jj_lib::dsl_util::{
|
use jj_lib::dsl_util::{
|
||||||
self, collect_similar, AliasDeclaration, AliasDeclarationParser, AliasExpandError,
|
self, collect_similar, AliasDeclaration, AliasDeclarationParser, AliasExpandError,
|
||||||
AliasExpandableExpression, AliasId, AliasesMap, InvalidArguments, StringLiteralParser,
|
AliasExpandableExpression, AliasId, AliasesMap, ExpressionFolder, FoldableExpression,
|
||||||
|
InvalidArguments, StringLiteralParser,
|
||||||
};
|
};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use pest::iterators::{Pair, Pairs};
|
use pest::iterators::{Pair, Pairs};
|
||||||
|
@ -272,6 +273,53 @@ pub enum ExpressionKind<'i> {
|
||||||
AliasExpanded(AliasId<'i>, Box<ExpressionNode<'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> {
|
impl<'i> AliasExpandableExpression<'i> for ExpressionKind<'i> {
|
||||||
fn identifier(name: &'i str) -> Self {
|
fn identifier(name: &'i str) -> Self {
|
||||||
ExpressionKind::Identifier(name)
|
ExpressionKind::Identifier(name)
|
||||||
|
|
|
@ -130,6 +130,73 @@ pub struct InvalidArguments<'i> {
|
||||||
pub span: pest::Span<'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.
|
/// Helper to parse string literal.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StringLiteralParser<R> {
|
pub struct StringLiteralParser<R> {
|
||||||
|
@ -267,7 +334,7 @@ pub trait AliasDeclarationParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expression item that supports alias substitution.
|
/// Expression item that supports alias substitution.
|
||||||
pub trait AliasExpandableExpression<'i>: Sized {
|
pub trait AliasExpandableExpression<'i>: FoldableExpression<'i> {
|
||||||
/// Wraps identifier.
|
/// Wraps identifier.
|
||||||
fn identifier(name: &'i str) -> Self;
|
fn identifier(name: &'i str) -> Self;
|
||||||
/// Wraps function call.
|
/// Wraps function call.
|
||||||
|
|
Loading…
Reference in a new issue