1873 lines · 66.4 KB · Rust
RawDownload
// 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 itertools::Itertools as _;use once_cell::sync::Lazy;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::Parser as _;use pest_derive::Parser;use thiserror::Error; use crate::dsl_util;use crate::dsl_util::collect_similar;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::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,            Rule::compat_parents_op                | Rule::compat_dag_range_op                | Rule::compat_dag_range_pre_op                | Rule::compat_dag_range_post_op                | Rule::compat_add_op                | Rule::compat_sub_op        )    }     fn to_symbol(self) -> Option<&'static str> {        match self {            Rule::EOI => None,            Rule::whitespace => None,            Rule::identifier_part => None,            Rule::identifier => None,            Rule::strict_identifier_part => None,            Rule::strict_identifier => None,            Rule::symbol => None,            Rule::string_escape => None,            Rule::string_content_char => None,            Rule::string_content => None,            Rule::string_literal => None,            Rule::raw_string_content => None,            Rule::raw_string_literal => None,            Rule::at_op => Some("@"),            Rule::pattern_kind_op => Some(":"),            Rule::parents_op => Some("-"),            Rule::children_op => Some("+"),            Rule::compat_parents_op => Some("^"),            Rule::dag_range_op            | Rule::dag_range_pre_op            | Rule::dag_range_post_op            | Rule::dag_range_all_op => Some("::"),            Rule::compat_dag_range_op            | Rule::compat_dag_range_pre_op            | Rule::compat_dag_range_post_op => Some(":"),            Rule::range_op => Some(".."),            Rule::range_pre_op | Rule::range_post_op | Rule::range_all_op => Some(".."),            Rule::range_ops => None,            Rule::range_pre_ops => None,            Rule::range_post_ops => None,            Rule::range_all_ops => None,            Rule::negate_op => Some("~"),            Rule::union_op => Some("|"),            Rule::intersection_op => Some("&"),            Rule::difference_op => Some("~"),            Rule::compat_add_op => Some("+"),            Rule::compat_sub_op => Some("-"),            Rule::infix_op => None,            Rule::function => None,            Rule::function_name => None,            Rule::keyword_argument => None,            Rule::argument => None,            Rule::function_arguments => None,            Rule::formal_parameters => None,            Rule::string_pattern => None,            Rule::primary => None,            Rule::neighbors_expression => None,            Rule::range_expression => None,            Rule::expression => None,            Rule::program_modifier => None,            Rule::program => None,            Rule::symbol_name => None,            Rule::function_alias_declaration => None,            Rule::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,        ));        RevsetParseError {            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 {        RevsetParseError {            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 {            ExpressionKind::Identifier(name) => folder.fold_identifier(name, span),            ExpressionKind::String(_)            | ExpressionKind::StringPattern { .. }            | ExpressionKind::RemoteSymbol(_)            | ExpressionKind::AtWorkspace(_)            | ExpressionKind::AtCurrentWorkspace            | ExpressionKind::DagRangeAll            | ExpressionKind::RangeAll => 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::UnionAll(nodes) => {                let nodes = dsl_util::fold_expression_nodes(folder, nodes)?;                Ok(ExpressionKind::UnionAll(nodes))            }            ExpressionKind::FunctionCall(function) => folder.fold_function_call(function, span),            ExpressionKind::Modifier(modifier) => {                let modifier = Box::new(ModifierNode {                    name: modifier.name,                    name_span: modifier.name_span,                    body: folder.fold_expression(modifier.body)?,                });                Ok(ExpressionKind::Modifier(modifier))            }            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)    }     fn function_call(function: Box<FunctionCallNode<'i>>) -> Self {        ExpressionKind::FunctionCall(function)    }     fn alias_expanded(id: AliasId<'i>, subst: Box<ExpressionNode<'i>>) -> Self {        ExpressionKind::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_tuple().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 1873.