// Copyright 2021-2024 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. #![allow(missing_docs)] use std::collections::HashSet;use std::error;use std::mem;use std::str::FromStr;use std::sync::LazyLock; use itertools::Itertools as _;use pest::Parser as _;use pest::iterators::Pair;use pest::iterators::Pairs;use pest::pratt_parser::Assoc;use pest::pratt_parser::Op;use pest::pratt_parser::PrattParser;use pest_derive::Parser;use thiserror::Error; use crate::dsl_util;use crate::dsl_util::AliasDeclaration;use crate::dsl_util::AliasDeclarationParser;use crate::dsl_util::AliasDefinitionParser;use crate::dsl_util::AliasExpandError;use crate::dsl_util::AliasExpandableExpression;use crate::dsl_util::AliasId;use crate::dsl_util::AliasesMap;use crate::dsl_util::Diagnostics;use crate::dsl_util::ExpressionFolder;use crate::dsl_util::FoldableExpression;use crate::dsl_util::FunctionCallParser;use crate::dsl_util::InvalidArguments;use crate::dsl_util::StringLiteralParser;use crate::dsl_util::collect_similar;use crate::ref_name::RefNameBuf;use crate::ref_name::RemoteNameBuf;use crate::ref_name::RemoteRefSymbolBuf; #[derive(Parser)]#[grammar = "revset.pest"]struct RevsetParser; const STRING_LITERAL_PARSER: StringLiteralParser<Rule> = StringLiteralParser { content_rule: Rule::string_content, escape_rule: Rule::string_escape,};const FUNCTION_CALL_PARSER: FunctionCallParser<Rule> = FunctionCallParser { function_name_rule: Rule::function_name, function_arguments_rule: Rule::function_arguments, keyword_argument_rule: Rule::keyword_argument, argument_name_rule: Rule::strict_identifier, argument_value_rule: Rule::expression,}; impl Rule { /// Whether this is a placeholder rule for compatibility with the other /// systems. fn is_compat(&self) -> bool { matches!( self, Self::compat_parents_op | Self::compat_dag_range_op | Self::compat_dag_range_pre_op | Self::compat_dag_range_post_op | Self::compat_add_op | Self::compat_sub_op ) } fn to_symbol(self) -> Option<&'static str> { match self { Self::EOI => None, Self::whitespace => None, Self::identifier_part => None, Self::identifier => None, Self::strict_identifier_part => None, Self::strict_identifier => None, Self::symbol => None, Self::string_escape => None, Self::string_content_char => None, Self::string_content => None, Self::string_literal => None, Self::raw_string_content => None, Self::raw_string_literal => None, Self::at_op => Some("@"), Self::pattern_kind_op => Some(":"), Self::parents_op => Some("-"), Self::children_op => Some("+"), Self::compat_parents_op => Some("^"), Self::dag_range_op | Self::dag_range_pre_op | Self::dag_range_post_op | Self::dag_range_all_op => Some("::"), Self::compat_dag_range_op | Self::compat_dag_range_pre_op | Self::compat_dag_range_post_op => Some(":"), Self::range_op => Some(".."), Self::range_pre_op | Self::range_post_op | Self::range_all_op => Some(".."), Self::range_ops => None, Self::range_pre_ops => None, Self::range_post_ops => None, Self::range_all_ops => None, Self::negate_op => Some("~"), Self::union_op => Some("|"), Self::intersection_op => Some("&"), Self::difference_op => Some("~"), Self::compat_add_op => Some("+"), Self::compat_sub_op => Some("-"), Self::infix_op => None, Self::function => None, Self::function_name => None, Self::keyword_argument => None, Self::argument => None, Self::function_arguments => None, Self::formal_parameters => None, Self::string_pattern => None, Self::primary => None, Self::neighbors_expression => None, Self::range_expression => None, Self::expression => None, Self::program_modifier => None, Self::program => None, Self::symbol_name => None, Self::function_alias_declaration => None, Self::alias_declaration => None, } }} /// Manages diagnostic messages emitted during revset parsing and function-call/// resolution.pub type RevsetDiagnostics = Diagnostics<RevsetParseError>; #[derive(Debug, Error)]#[error("{pest_error}")]pub struct RevsetParseError { kind: Box<RevsetParseErrorKind>, pest_error: Box<pest::error::Error<Rule>>, source: Option<Box<dyn error::Error + Send + Sync>>,} #[derive(Debug, Error, PartialEq, Eq)]pub enum RevsetParseErrorKind { #[error("Syntax error")] SyntaxError, #[error("`{op}` is not a prefix operator")] NotPrefixOperator { op: String, similar_op: String, description: String, }, #[error("`{op}` is not a postfix operator")] NotPostfixOperator { op: String, similar_op: String, description: String, }, #[error("`{op}` is not an infix operator")] NotInfixOperator { op: String, similar_op: String, description: String, }, #[error("Modifier `{0}` doesn't exist")] NoSuchModifier(String), #[error("Function `{name}` doesn't exist")] NoSuchFunction { name: String, candidates: Vec<String>, }, #[error("Function `{name}`: {message}")] InvalidFunctionArguments { name: String, message: String }, #[error("Cannot resolve file pattern without workspace")] FsPathWithoutWorkspace, #[error("Cannot resolve `@` without workspace")] WorkingCopyWithoutWorkspace, #[error("Redefinition of function parameter")] RedefinedFunctionParameter, #[error("{0}")] Expression(String), #[error("In alias `{0}`")] InAliasExpansion(String), #[error("In function parameter `{0}`")] InParameterExpansion(String), #[error("Alias `{0}` expanded recursively")] RecursiveAlias(String),} impl RevsetParseError { pub(super) fn with_span(kind: RevsetParseErrorKind, span: pest::Span<'_>) -> Self { let message = kind.to_string(); let pest_error = Box::new(pest::error::Error::new_from_span( pest::error::ErrorVariant::CustomError { message }, span, )); Self { kind: Box::new(kind), pest_error, source: None, } } pub(super) fn with_source( mut self, source: impl Into<Box<dyn error::Error + Send + Sync>>, ) -> Self { self.source = Some(source.into()); self } /// Some other expression error. pub fn expression(message: impl Into<String>, span: pest::Span<'_>) -> Self { Self::with_span(RevsetParseErrorKind::Expression(message.into()), span) } /// If this is a `NoSuchFunction` error, expands the candidates list with /// the given `other_functions`. pub(super) fn extend_function_candidates<I>(mut self, other_functions: I) -> Self where I: IntoIterator, I::Item: AsRef<str>, { if let RevsetParseErrorKind::NoSuchFunction { name, candidates } = self.kind.as_mut() { let other_candidates = collect_similar(name, other_functions); *candidates = itertools::merge(mem::take(candidates), other_candidates) .dedup() .collect(); } self } pub fn kind(&self) -> &RevsetParseErrorKind { &self.kind } /// Original parsing error which typically occurred in an alias expression. pub fn origin(&self) -> Option<&Self> { self.source.as_ref().and_then(|e| e.downcast_ref()) }} impl AliasExpandError for RevsetParseError { fn invalid_arguments(err: InvalidArguments<'_>) -> Self { err.into() } fn recursive_expansion(id: AliasId<'_>, span: pest::Span<'_>) -> Self { Self::with_span(RevsetParseErrorKind::RecursiveAlias(id.to_string()), span) } fn within_alias_expansion(self, id: AliasId<'_>, span: pest::Span<'_>) -> Self { let kind = match id { AliasId::Symbol(_) | AliasId::Function(..) => { RevsetParseErrorKind::InAliasExpansion(id.to_string()) } AliasId::Parameter(_) => RevsetParseErrorKind::InParameterExpansion(id.to_string()), }; Self::with_span(kind, span).with_source(self) }} impl From<pest::error::Error<Rule>> for RevsetParseError { fn from(err: pest::error::Error<Rule>) -> Self { Self { kind: Box::new(RevsetParseErrorKind::SyntaxError), pest_error: Box::new(rename_rules_in_pest_error(err)), source: None, } }} impl From<InvalidArguments<'_>> for RevsetParseError { fn from(err: InvalidArguments<'_>) -> Self { let kind = RevsetParseErrorKind::InvalidFunctionArguments { name: err.name.to_owned(), message: err.message, }; Self::with_span(kind, err.span) }} fn rename_rules_in_pest_error(mut err: pest::error::Error<Rule>) -> pest::error::Error<Rule> { let pest::error::ErrorVariant::ParsingError { positives, negatives, } = &mut err.variant else { return err; }; // Remove duplicated symbols. Compat symbols are also removed from the // (positive) suggestion. let mut known_syms = HashSet::new(); positives.retain(|rule| { !rule.is_compat() && rule.to_symbol().is_none_or(|sym| known_syms.insert(sym)) }); let mut known_syms = HashSet::new(); negatives.retain(|rule| rule.to_symbol().is_none_or(|sym| known_syms.insert(sym))); err.renamed_rules(|rule| { rule.to_symbol() .map(|sym| format!("`{sym}`")) .unwrap_or_else(|| format!("<{rule:?}>")) })} #[derive(Clone, Debug, Eq, PartialEq)]pub enum ExpressionKind<'i> { /// Unquoted symbol. Identifier(&'i str), /// Quoted symbol or string. String(String), /// `<kind>:<value>` StringPattern { kind: &'i str, value: String, }, /// `<name>@<remote>` RemoteSymbol(RemoteRefSymbolBuf), /// `<name>@` AtWorkspace(String), /// `@` AtCurrentWorkspace, /// `::` DagRangeAll, /// `..` RangeAll, Unary(UnaryOp, Box<ExpressionNode<'i>>), Binary(BinaryOp, Box<ExpressionNode<'i>>, Box<ExpressionNode<'i>>), /// `x | y | ..` UnionAll(Vec<ExpressionNode<'i>>), FunctionCall(Box<FunctionCallNode<'i>>), /// `name: body` Modifier(Box<ModifierNode<'i>>), /// Identity node to preserve the span in the source text. 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 { Self::Identifier(name) => folder.fold_identifier(name, span), Self::String(_) | Self::StringPattern { .. } | Self::RemoteSymbol(_) | ExpressionKind::AtWorkspace(_) | Self::AtCurrentWorkspace | Self::DagRangeAll | Self::RangeAll => Ok(self), Self::Unary(op, arg) => { let arg = Box::new(folder.fold_expression(*arg)?); Ok(Self::Unary(op, arg)) } Self::Binary(op, lhs, rhs) => { let lhs = Box::new(folder.fold_expression(*lhs)?); let rhs = Box::new(folder.fold_expression(*rhs)?); Ok(Self::Binary(op, lhs, rhs)) } Self::UnionAll(nodes) => { let nodes = dsl_util::fold_expression_nodes(folder, nodes)?; Ok(Self::UnionAll(nodes)) } Self::FunctionCall(function) => folder.fold_function_call(function, span), Self::Modifier(modifier) => { let modifier = Box::new(ModifierNode { name: modifier.name, name_span: modifier.name_span, body: folder.fold_expression(modifier.body)?, }); Ok(Self::Modifier(modifier)) } Self::AliasExpanded(id, subst) => { let subst = Box::new(folder.fold_expression(*subst)?); Ok(Self::AliasExpanded(id, subst)) } } }} impl<'i> AliasExpandableExpression<'i> for ExpressionKind<'i> { fn identifier(name: &'i str) -> Self { Self::Identifier(name) } fn function_call(function: Box<FunctionCallNode<'i>>) -> Self { Self::FunctionCall(function) } fn alias_expanded(id: AliasId<'i>, subst: Box<ExpressionNode<'i>>) -> Self { Self::AliasExpanded(id, subst) }} #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]pub enum UnaryOp { /// `~x` Negate, /// `::x` DagRangePre, /// `x::` DagRangePost, /// `..x` RangePre, /// `x..` RangePost, /// `x-` Parents, /// `x+` Children,} #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]pub enum BinaryOp { /// `&` Intersection, /// `~` Difference, /// `::` DagRange, /// `..` Range,} pub type ExpressionNode<'i> = dsl_util::ExpressionNode<'i, ExpressionKind<'i>>;pub type FunctionCallNode<'i> = dsl_util::FunctionCallNode<'i, ExpressionKind<'i>>; /// Expression with modifier `name: body`.#[derive(Clone, Debug, Eq, PartialEq)]pub struct ModifierNode<'i> { /// Modifier name. pub name: &'i str, /// Span of the modifier name. pub name_span: pest::Span<'i>, /// Expression body. pub body: ExpressionNode<'i>,} fn union_nodes<'i>(lhs: ExpressionNode<'i>, rhs: ExpressionNode<'i>) -> ExpressionNode<'i> { let span = lhs.span.start_pos().span(&rhs.span.end_pos()); let expr = match lhs.kind { // Flatten "x | y | z" to save recursion stack. Machine-generated query // might have long chain of unions. ExpressionKind::UnionAll(mut nodes) => { nodes.push(rhs); ExpressionKind::UnionAll(nodes) } _ => ExpressionKind::UnionAll(vec![lhs, rhs]), }; ExpressionNode::new(expr, span)} /// Parses text into expression tree. No name resolution is made at this stage.pub fn parse_program(revset_str: &str) -> Result<ExpressionNode<'_>, RevsetParseError> { let mut pairs = RevsetParser::parse(Rule::program, revset_str)?; let first = pairs.next().unwrap(); match first.as_rule() { Rule::expression => parse_expression_node(first.into_inner()), Rule::program_modifier => { let [lhs, op] = first.into_inner().collect_array().unwrap(); let rhs = pairs.next().unwrap(); assert_eq!(lhs.as_rule(), Rule::strict_identifier); assert_eq!(op.as_rule(), Rule::pattern_kind_op); assert_eq!(rhs.as_rule(), Rule::expression); let span = lhs.as_span().start_pos().span(&rhs.as_span().end_pos()); let modifier = Box::new(ModifierNode { name: lhs.as_str(), name_span: lhs.as_span(), body: parse_expression_node(rhs.into_inner())?, }); let expr = ExpressionKind::Modifier(modifier); Ok(ExpressionNode::new(expr, span)) } r => panic!("unexpected revset parse rule: {r:?}"), }} fn parse_expression_node(pairs: Pairs<Rule>) -> Result<ExpressionNode, RevsetParseError> { fn not_prefix_op( op: &Pair<Rule>, similar_op: impl Into<String>, description: impl Into<String>, ) -> RevsetParseError { RevsetParseError::with_span( RevsetParseErrorKind::NotPrefixOperator { op: op.as_str().to_owned(),Lines 1–500 of 1876. Load more
// Copyright 2021-2024 The Jujutsu Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #![allow(missing_docs)] use std::collections::HashSet; use std::error; use std::mem; use std::str::FromStr; use std::sync::LazyLock; use itertools::Itertools as _; use pest::Parser as _; use pest::iterators::Pair; use pest::iterators::Pairs; use pest::pratt_parser::Assoc; use pest::pratt_parser::Op; use pest::pratt_parser::PrattParser; use pest_derive::Parser; use thiserror::Error; use crate::dsl_util; use crate::dsl_util::AliasDeclaration; use crate::dsl_util::AliasDeclarationParser; use crate::dsl_util::AliasDefinitionParser; use crate::dsl_util::AliasExpandError; use crate::dsl_util::AliasExpandableExpression; use crate::dsl_util::AliasId; use crate::dsl_util::AliasesMap; use crate::dsl_util::Diagnostics; use crate::dsl_util::ExpressionFolder; use crate::dsl_util::FoldableExpression; use crate::dsl_util::FunctionCallParser; use crate::dsl_util::InvalidArguments; use crate::dsl_util::StringLiteralParser; use crate::dsl_util::collect_similar; use crate::ref_name::RefNameBuf; use crate::ref_name::RemoteNameBuf; use crate::ref_name::RemoteRefSymbolBuf; #[derive(Parser)] #[grammar = "revset.pest"] struct RevsetParser; const STRING_LITERAL_PARSER: StringLiteralParser<Rule> = StringLiteralParser { content_rule: Rule::string_content, escape_rule: Rule::string_escape, }; const FUNCTION_CALL_PARSER: FunctionCallParser<Rule> = FunctionCallParser { function_name_rule: Rule::function_name, function_arguments_rule: Rule::function_arguments, keyword_argument_rule: Rule::keyword_argument, argument_name_rule: Rule::strict_identifier, argument_value_rule: Rule::expression, }; impl Rule { /// Whether this is a placeholder rule for compatibility with the other /// systems. fn is_compat(&self) -> bool { matches!( self, Self::compat_parents_op | Self::compat_dag_range_op | Self::compat_dag_range_pre_op | Self::compat_dag_range_post_op | Self::compat_add_op | Self::compat_sub_op ) } fn to_symbol(self) -> Option<&'static str> { match self { Self::EOI => None, Self::whitespace => None, Self::identifier_part => None, Self::identifier => None, Self::strict_identifier_part => None, Self::strict_identifier => None, Self::symbol => None, Self::string_escape => None, Self::string_content_char => None, Self::string_content => None, Self::string_literal => None, Self::raw_string_content => None, Self::raw_string_literal => None, Self::at_op => Some("@"), Self::pattern_kind_op => Some(":"), Self::parents_op => Some("-"), Self::children_op => Some("+"), Self::compat_parents_op => Some("^"), Self::dag_range_op | Self::dag_range_pre_op | Self::dag_range_post_op | Self::dag_range_all_op => Some("::"), Self::compat_dag_range_op | Self::compat_dag_range_pre_op | Self::compat_dag_range_post_op => Some(":"), Self::range_op => Some(".."), Self::range_pre_op | Self::range_post_op | Self::range_all_op => Some(".."), Self::range_ops => None, Self::range_pre_ops => None, Self::range_post_ops => None, Self::range_all_ops => None, Self::negate_op => Some("~"), Self::union_op => Some("|"), Self::intersection_op => Some("&"), Self::difference_op => Some("~"), Self::compat_add_op => Some("+"), Self::compat_sub_op => Some("-"), Self::infix_op => None, Self::function => None, Self::function_name => None, Self::keyword_argument => None, Self::argument => None, Self::function_arguments => None, Self::formal_parameters => None, Self::string_pattern => None, Self::primary => None, Self::neighbors_expression => None, Self::range_expression => None, Self::expression => None, Self::program_modifier => None, Self::program => None, Self::symbol_name => None, Self::function_alias_declaration => None, Self::alias_declaration => None, } } } /// Manages diagnostic messages emitted during revset parsing and function-call /// resolution. pub type RevsetDiagnostics = Diagnostics<RevsetParseError>; #[derive(Debug, Error)] #[error("{pest_error}")] pub struct RevsetParseError { kind: Box<RevsetParseErrorKind>, pest_error: Box<pest::error::Error<Rule>>, source: Option<Box<dyn error::Error + Send + Sync>>, } #[derive(Debug, Error, PartialEq, Eq)] pub enum RevsetParseErrorKind { #[error("Syntax error")] SyntaxError, #[error("`{op}` is not a prefix operator")] NotPrefixOperator { op: String, similar_op: String, description: String, }, #[error("`{op}` is not a postfix operator")] NotPostfixOperator { op: String, similar_op: String, description: String, }, #[error("`{op}` is not an infix operator")] NotInfixOperator { op: String, similar_op: String, description: String, }, #[error("Modifier `{0}` doesn't exist")] NoSuchModifier(String), #[error("Function `{name}` doesn't exist")] NoSuchFunction { name: String, candidates: Vec<String>, }, #[error("Function `{name}`: {message}")] InvalidFunctionArguments { name: String, message: String }, #[error("Cannot resolve file pattern without workspace")] FsPathWithoutWorkspace, #[error("Cannot resolve `@` without workspace")] WorkingCopyWithoutWorkspace, #[error("Redefinition of function parameter")] RedefinedFunctionParameter, #[error("{0}")] Expression(String), #[error("In alias `{0}`")] InAliasExpansion(String), #[error("In function parameter `{0}`")] InParameterExpansion(String), #[error("Alias `{0}` expanded recursively")] RecursiveAlias(String), } impl RevsetParseError { pub(super) fn with_span(kind: RevsetParseErrorKind, span: pest::Span<'_>) -> Self { let message = kind.to_string(); let pest_error = Box::new(pest::error::Error::new_from_span( pest::error::ErrorVariant::CustomError { message }, span, )); Self { kind: Box::new(kind), pest_error, source: None, } } pub(super) fn with_source( mut self, source: impl Into<Box<dyn error::Error + Send + Sync>>, ) -> Self { self.source = Some(source.into()); self } /// Some other expression error. pub fn expression(message: impl Into<String>, span: pest::Span<'_>) -> Self { Self::with_span(RevsetParseErrorKind::Expression(message.into()), span) } /// If this is a `NoSuchFunction` error, expands the candidates list with /// the given `other_functions`. pub(super) fn extend_function_candidates<I>(mut self, other_functions: I) -> Self where I: IntoIterator, I::Item: AsRef<str>, { if let RevsetParseErrorKind::NoSuchFunction { name, candidates } = self.kind.as_mut() { let other_candidates = collect_similar(name, other_functions); *candidates = itertools::merge(mem::take(candidates), other_candidates) .dedup() .collect(); } self } pub fn kind(&self) -> &RevsetParseErrorKind { &self.kind } /// Original parsing error which typically occurred in an alias expression. pub fn origin(&self) -> Option<&Self> { self.source.as_ref().and_then(|e| e.downcast_ref()) } } impl AliasExpandError for RevsetParseError { fn invalid_arguments(err: InvalidArguments<'_>) -> Self { err.into() } fn recursive_expansion(id: AliasId<'_>, span: pest::Span<'_>) -> Self { Self::with_span(RevsetParseErrorKind::RecursiveAlias(id.to_string()), span) } fn within_alias_expansion(self, id: AliasId<'_>, span: pest::Span<'_>) -> Self { let kind = match id { AliasId::Symbol(_) | AliasId::Function(..) => { RevsetParseErrorKind::InAliasExpansion(id.to_string()) } AliasId::Parameter(_) => RevsetParseErrorKind::InParameterExpansion(id.to_string()), }; Self::with_span(kind, span).with_source(self) } } impl From<pest::error::Error<Rule>> for RevsetParseError { fn from(err: pest::error::Error<Rule>) -> Self { Self { kind: Box::new(RevsetParseErrorKind::SyntaxError), pest_error: Box::new(rename_rules_in_pest_error(err)), source: None, } } } impl From<InvalidArguments<'_>> for RevsetParseError { fn from(err: InvalidArguments<'_>) -> Self { let kind = RevsetParseErrorKind::InvalidFunctionArguments { name: err.name.to_owned(), message: err.message, }; Self::with_span(kind, err.span) } } fn rename_rules_in_pest_error(mut err: pest::error::Error<Rule>) -> pest::error::Error<Rule> { let pest::error::ErrorVariant::ParsingError { positives, negatives, } = &mut err.variant else { return err; }; // Remove duplicated symbols. Compat symbols are also removed from the // (positive) suggestion. let mut known_syms = HashSet::new(); positives.retain(|rule| { !rule.is_compat() && rule.to_symbol().is_none_or(|sym| known_syms.insert(sym)) }); let mut known_syms = HashSet::new(); negatives.retain(|rule| rule.to_symbol().is_none_or(|sym| known_syms.insert(sym))); err.renamed_rules(|rule| { rule.to_symbol() .map(|sym| format!("`{sym}`")) .unwrap_or_else(|| format!("<{rule:?}>")) }) } #[derive(Clone, Debug, Eq, PartialEq)] pub enum ExpressionKind<'i> { /// Unquoted symbol. Identifier(&'i str), /// Quoted symbol or string. String(String), /// `<kind>:<value>` StringPattern { kind: &'i str, value: String, }, /// `<name>@<remote>` RemoteSymbol(RemoteRefSymbolBuf), /// `<name>@` AtWorkspace(String), /// `@` AtCurrentWorkspace, /// `::` DagRangeAll, /// `..` RangeAll, Unary(UnaryOp, Box<ExpressionNode<'i>>), Binary(BinaryOp, Box<ExpressionNode<'i>>, Box<ExpressionNode<'i>>), /// `x | y | ..` UnionAll(Vec<ExpressionNode<'i>>), FunctionCall(Box<FunctionCallNode<'i>>), /// `name: body` Modifier(Box<ModifierNode<'i>>), /// Identity node to preserve the span in the source text. 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 { Self::Identifier(name) => folder.fold_identifier(name, span), Self::String(_) | Self::StringPattern { .. } | Self::RemoteSymbol(_) | ExpressionKind::AtWorkspace(_) | Self::AtCurrentWorkspace | Self::DagRangeAll | Self::RangeAll => Ok(self), Self::Unary(op, arg) => { let arg = Box::new(folder.fold_expression(*arg)?); Ok(Self::Unary(op, arg)) } Self::Binary(op, lhs, rhs) => { let lhs = Box::new(folder.fold_expression(*lhs)?); let rhs = Box::new(folder.fold_expression(*rhs)?); Ok(Self::Binary(op, lhs, rhs)) } Self::UnionAll(nodes) => { let nodes = dsl_util::fold_expression_nodes(folder, nodes)?; Ok(Self::UnionAll(nodes)) } Self::FunctionCall(function) => folder.fold_function_call(function, span), Self::Modifier(modifier) => { let modifier = Box::new(ModifierNode { name: modifier.name, name_span: modifier.name_span, body: folder.fold_expression(modifier.body)?, }); Ok(Self::Modifier(modifier)) } Self::AliasExpanded(id, subst) => { let subst = Box::new(folder.fold_expression(*subst)?); Ok(Self::AliasExpanded(id, subst)) } } } } impl<'i> AliasExpandableExpression<'i> for ExpressionKind<'i> { fn identifier(name: &'i str) -> Self { Self::Identifier(name) } fn function_call(function: Box<FunctionCallNode<'i>>) -> Self { Self::FunctionCall(function) } fn alias_expanded(id: AliasId<'i>, subst: Box<ExpressionNode<'i>>) -> Self { Self::AliasExpanded(id, subst) } } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum UnaryOp { /// `~x` Negate, /// `::x` DagRangePre, /// `x::` DagRangePost, /// `..x` RangePre, /// `x..` RangePost, /// `x-` Parents, /// `x+` Children, } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum BinaryOp { /// `&` Intersection, /// `~` Difference, /// `::` DagRange, /// `..` Range, } pub type ExpressionNode<'i> = dsl_util::ExpressionNode<'i, ExpressionKind<'i>>; pub type FunctionCallNode<'i> = dsl_util::FunctionCallNode<'i, ExpressionKind<'i>>; /// Expression with modifier `name: body`. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ModifierNode<'i> { /// Modifier name. pub name: &'i str, /// Span of the modifier name. pub name_span: pest::Span<'i>, /// Expression body. pub body: ExpressionNode<'i>, } fn union_nodes<'i>(lhs: ExpressionNode<'i>, rhs: ExpressionNode<'i>) -> ExpressionNode<'i> { let span = lhs.span.start_pos().span(&rhs.span.end_pos()); let expr = match lhs.kind { // Flatten "x | y | z" to save recursion stack. Machine-generated query // might have long chain of unions. ExpressionKind::UnionAll(mut nodes) => { nodes.push(rhs); ExpressionKind::UnionAll(nodes) } _ => ExpressionKind::UnionAll(vec![lhs, rhs]), }; ExpressionNode::new(expr, span) } /// Parses text into expression tree. No name resolution is made at this stage. pub fn parse_program(revset_str: &str) -> Result<ExpressionNode<'_>, RevsetParseError> { let mut pairs = RevsetParser::parse(Rule::program, revset_str)?; let first = pairs.next().unwrap(); match first.as_rule() { Rule::expression => parse_expression_node(first.into_inner()), Rule::program_modifier => { let [lhs, op] = first.into_inner().collect_array().unwrap(); let rhs = pairs.next().unwrap(); assert_eq!(lhs.as_rule(), Rule::strict_identifier); assert_eq!(op.as_rule(), Rule::pattern_kind_op); assert_eq!(rhs.as_rule(), Rule::expression); let span = lhs.as_span().start_pos().span(&rhs.as_span().end_pos()); let modifier = Box::new(ModifierNode { name: lhs.as_str(), name_span: lhs.as_span(), body: parse_expression_node(rhs.into_inner())?, }); let expr = ExpressionKind::Modifier(modifier); Ok(ExpressionNode::new(expr, span)) } r => panic!("unexpected revset parse rule: {r:?}"), } } fn parse_expression_node(pairs: Pairs<Rule>) -> Result<ExpressionNode, RevsetParseError> { fn not_prefix_op( op: &Pair<Rule>, similar_op: impl Into<String>, description: impl Into<String>, ) -> RevsetParseError { RevsetParseError::with_span( RevsetParseErrorKind::NotPrefixOperator { op: op.as_str().to_owned(),