revset: insert pre-order callback that can terminate transformation early

This will be a hook for resolve_symbols() to transform Present(_) subtree.
This commit is contained in:
Yuya Nishihara 2023-04-02 13:34:31 +09:00
parent feaad6b5fa
commit aeb93c7591

View file

@ -1105,27 +1105,37 @@ pub fn parse(
/// `Some` for rewritten expression, or `None` to reuse the original expression.
type TransformedExpression = Option<Rc<RevsetExpression>>;
/// Walks `expression` tree and applies `f` recursively from leaf nodes.
fn transform_expression_bottom_up(
expression: &Rc<RevsetExpression>,
mut f: impl FnMut(&Rc<RevsetExpression>) -> TransformedExpression,
) -> TransformedExpression {
try_transform_expression_bottom_up(expression, |expression| Ok(f(expression))).unwrap()
try_transform_expression(expression, |_| Ok(None), |expression| Ok(f(expression))).unwrap()
}
type TransformResult = Result<TransformedExpression, RevsetResolutionError>;
/// Walks `expression` tree and applies `f` recursively from leaf nodes.
/// Walks `expression` tree and applies transformation recursively.
///
/// If `f` returns `None`, the original expression node is reused. If no nodes
/// rewritten, returns `None`. `std::iter::successors()` could be used if
/// the transformation needs to be applied repeatedly until converged.
fn try_transform_expression_bottom_up(
/// `pre` is the callback to rewrite subtree including children. It is
/// invoked before visiting the child nodes. If returned `Some`, children
/// won't be visited.
///
/// `post` is the callback to rewrite from leaf nodes. If returned `None`,
/// the original expression node will be reused.
///
/// If no nodes rewritten, this function returns `None`.
/// `std::iter::successors()` could be used if the transformation needs to be
/// applied repeatedly until converged.
fn try_transform_expression(
expression: &Rc<RevsetExpression>,
mut f: impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
mut pre: impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
mut post: impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
) -> TransformResult {
fn transform_child_rec(
expression: &Rc<RevsetExpression>,
f: &mut impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
pre: &mut impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
post: &mut impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
) -> TransformResult {
Ok(match expression.as_ref() {
RevsetExpression::None => None,
@ -1133,50 +1143,50 @@ fn try_transform_expression_bottom_up(
RevsetExpression::Commits(_) => None,
RevsetExpression::Symbol(_) => None,
RevsetExpression::Children(roots) => {
transform_rec(roots, f)?.map(RevsetExpression::Children)
transform_rec(roots, pre, post)?.map(RevsetExpression::Children)
}
RevsetExpression::Ancestors { heads, generation } => {
transform_rec(heads, f)?.map(|heads| RevsetExpression::Ancestors {
RevsetExpression::Ancestors { heads, generation } => transform_rec(heads, pre, post)?
.map(|heads| RevsetExpression::Ancestors {
heads,
generation: generation.clone(),
})
}
}),
RevsetExpression::Range {
roots,
heads,
generation,
} => transform_rec_pair((roots, heads), f)?.map(|(roots, heads)| {
} => transform_rec_pair((roots, heads), pre, post)?.map(|(roots, heads)| {
RevsetExpression::Range {
roots,
heads,
generation: generation.clone(),
}
}),
RevsetExpression::DagRange { roots, heads } => transform_rec_pair((roots, heads), f)?
.map(|(roots, heads)| RevsetExpression::DagRange { roots, heads }),
RevsetExpression::DagRange { roots, heads } => {
transform_rec_pair((roots, heads), pre, post)?
.map(|(roots, heads)| RevsetExpression::DagRange { roots, heads })
}
RevsetExpression::VisibleHeads => None,
RevsetExpression::Heads(candidates) => {
transform_rec(candidates, f)?.map(RevsetExpression::Heads)
transform_rec(candidates, pre, post)?.map(RevsetExpression::Heads)
}
RevsetExpression::Roots(candidates) => {
transform_rec(candidates, f)?.map(RevsetExpression::Roots)
transform_rec(candidates, pre, post)?.map(RevsetExpression::Roots)
}
RevsetExpression::Branches(_) => None,
RevsetExpression::RemoteBranches { .. } => None,
RevsetExpression::Tags => None,
RevsetExpression::GitRefs => None,
RevsetExpression::GitHead => None,
RevsetExpression::Latest { candidates, count } => {
transform_rec(candidates, f)?.map(|candidates| RevsetExpression::Latest {
RevsetExpression::Latest { candidates, count } => transform_rec(candidates, pre, post)?
.map(|candidates| RevsetExpression::Latest {
candidates,
count: *count,
})
}
}),
RevsetExpression::Filter(_) => None,
RevsetExpression::AsFilter(candidates) => {
transform_rec(candidates, f)?.map(RevsetExpression::AsFilter)
transform_rec(candidates, pre, post)?.map(RevsetExpression::AsFilter)
}
RevsetExpression::Present(candidates) => match transform_rec(candidates, f) {
RevsetExpression::Present(candidates) => match transform_rec(candidates, pre, post) {
Ok(None) => None,
Ok(Some(expression)) => Some(RevsetExpression::Present(expression)),
Err(RevsetResolutionError::NoSuchRevision(_)) => Some(RevsetExpression::None),
@ -1188,22 +1198,22 @@ fn try_transform_expression_bottom_up(
}
},
RevsetExpression::NotIn(complement) => {
transform_rec(complement, f)?.map(RevsetExpression::NotIn)
transform_rec(complement, pre, post)?.map(RevsetExpression::NotIn)
}
RevsetExpression::Union(expression1, expression2) => {
transform_rec_pair((expression1, expression2), f)?.map(
transform_rec_pair((expression1, expression2), pre, post)?.map(
|(expression1, expression2)| RevsetExpression::Union(expression1, expression2),
)
}
RevsetExpression::Intersection(expression1, expression2) => {
transform_rec_pair((expression1, expression2), f)?.map(
transform_rec_pair((expression1, expression2), pre, post)?.map(
|(expression1, expression2)| {
RevsetExpression::Intersection(expression1, expression2)
},
)
}
RevsetExpression::Difference(expression1, expression2) => {
transform_rec_pair((expression1, expression2), f)?.map(
transform_rec_pair((expression1, expression2), pre, post)?.map(
|(expression1, expression2)| {
RevsetExpression::Difference(expression1, expression2)
},
@ -1216,11 +1226,12 @@ fn try_transform_expression_bottom_up(
#[allow(clippy::type_complexity)]
fn transform_rec_pair(
(expression1, expression2): (&Rc<RevsetExpression>, &Rc<RevsetExpression>),
f: &mut impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
pre: &mut impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
post: &mut impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
) -> Result<Option<(Rc<RevsetExpression>, Rc<RevsetExpression>)>, RevsetResolutionError> {
match (
transform_rec(expression1, f)?,
transform_rec(expression2, f)?,
transform_rec(expression1, pre, post)?,
transform_rec(expression2, pre, post)?,
) {
(Some(new_expression1), Some(new_expression2)) => {
Ok(Some((new_expression1, new_expression2)))
@ -1233,17 +1244,21 @@ fn try_transform_expression_bottom_up(
fn transform_rec(
expression: &Rc<RevsetExpression>,
f: &mut impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
pre: &mut impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
post: &mut impl FnMut(&Rc<RevsetExpression>) -> TransformResult,
) -> TransformResult {
if let Some(new_expression) = transform_child_rec(expression, f)? {
if let Some(new_expression) = pre(expression)? {
return Ok(Some(new_expression));
}
if let Some(new_expression) = transform_child_rec(expression, pre, post)? {
// must propagate new expression tree
Ok(Some(f(&new_expression)?.unwrap_or(new_expression)))
Ok(Some(post(&new_expression)?.unwrap_or(new_expression)))
} else {
f(expression)
post(expression)
}
}
transform_rec(expression, &mut f)
transform_rec(expression, &mut pre, &mut post)
}
/// Transforms filter expressions, by applying the following rules.
@ -1607,8 +1622,10 @@ pub fn resolve_symbols(
expression: Rc<RevsetExpression>,
workspace_ctx: Option<&RevsetWorkspaceContext>,
) -> Result<Rc<RevsetExpression>, RevsetResolutionError> {
Ok(
try_transform_expression_bottom_up(&expression, |expression| {
Ok(try_transform_expression(
&expression,
|_| Ok(None),
|expression| {
Ok(match expression.as_ref() {
RevsetExpression::Symbol(symbol) => {
let commit_ids =
@ -1667,9 +1684,9 @@ pub fn resolve_symbols(
}
_ => None,
})
})?
.unwrap_or(expression),
)
},
)?
.unwrap_or(expression))
}
pub trait Revset<'index>: fmt::Debug {