1364 lines · 48.5 KB · Rust
RawDownload
// Copyright 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. //! Parser for the fileset language. use std::error;use std::sync::LazyLock; use itertools::Itertools as _;use pest::Parser as _;use pest::iterators::Pair;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::InvalidArguments;use crate::dsl_util::StringLiteralParser; #[derive(Parser)]#[grammar = "fileset.pest"]struct FilesetParser; const STRING_LITERAL_PARSER: StringLiteralParser<Rule> = StringLiteralParser {    content_rule: Rule::string_content,    escape_rule: Rule::string_escape,}; impl Rule {    fn to_symbol(self) -> Option<&'static str> {        match self {            Self::EOI => None,            Self::whitespace => None,            Self::identifier => None,            Self::strict_identifier_part => None,            Self::strict_identifier => None,            Self::bare_string => 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::pattern_kind_op => Some(":"),            Self::negate_op => Some("~"),            Self::union_op => Some("|"),            Self::intersection_op => Some("&"),            Self::difference_op => Some("~"),            Self::prefix_ops => None,            Self::infix_ops => None,            Self::function => None,            Self::function_name => None,            Self::function_arguments => None,            Self::formal_parameters => None,            Self::pattern => None,            Self::bare_string_pattern => None,            Self::primary => None,            Self::expression => None,            Self::program => None,            Self::program_or_bare_string => None,            Self::function_alias_declaration => None,            Self::pattern_alias_declaration => None,            Self::alias_declaration => None,        }    }} /// Manages diagnostic messages emitted during fileset parsing and name/// resolution.pub type FilesetDiagnostics = Diagnostics<FilesetParseError>; /// Result of fileset parsing and name resolution.pub type FilesetParseResult<T> = Result<T, FilesetParseError>; /// Error occurred during fileset parsing and name resolution.#[derive(Debug, Error)]#[error("{pest_error}")]pub struct FilesetParseError {    kind: FilesetParseErrorKind,    pest_error: Box<pest::error::Error<Rule>>,    source: Option<Box<dyn error::Error + Send + Sync>>,} /// Categories of fileset parsing and name resolution error.#[expect(missing_docs)]#[derive(Clone, Debug, Eq, Error, PartialEq)]pub enum FilesetParseErrorKind {    #[error("Syntax error")]    SyntaxError,    #[error("Function `{name}` doesn't exist")]    NoSuchFunction {        name: String,        candidates: Vec<String>,    },    #[error("Function `{name}`: {message}")]    InvalidArguments { name: String, message: String },    #[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 FilesetParseError {    pub(super) fn new(kind: FilesetParseErrorKind, 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,            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(super) fn expression(message: impl Into<String>, span: pest::Span<'_>) -> Self {        Self::new(FilesetParseErrorKind::Expression(message.into()), span)    }     /// Category of the underlying error.    pub fn kind(&self) -> &FilesetParseErrorKind {        &self.kind    }} impl AliasExpandError for FilesetParseError {    fn invalid_arguments(err: InvalidArguments<'_>) -> Self {        err.into()    }     fn recursive_expansion(id: AliasId<'_>, span: pest::Span<'_>) -> Self {        Self::new(FilesetParseErrorKind::RecursiveAlias(id.to_string()), span)    }     fn within_alias_expansion(self, id: AliasId<'_>, span: pest::Span<'_>) -> Self {        let kind = match id {            AliasId::Symbol(_) | AliasId::Pattern(..) | AliasId::Function(..) => {                FilesetParseErrorKind::InAliasExpansion(id.to_string())            }            AliasId::Parameter(_) => FilesetParseErrorKind::InParameterExpansion(id.to_string()),        };        Self::new(kind, span).with_source(self)    }} impl From<pest::error::Error<Rule>> for FilesetParseError {    fn from(err: pest::error::Error<Rule>) -> Self {        Self {            kind: FilesetParseErrorKind::SyntaxError,            pest_error: Box::new(rename_rules_in_pest_error(err)),            source: None,        }    }} impl From<InvalidArguments<'_>> for FilesetParseError {    fn from(err: InvalidArguments<'_>) -> Self {        let kind = FilesetParseErrorKind::InvalidArguments {            name: err.name.to_owned(),            message: err.message,        };        Self::new(kind, err.span)    }} fn rename_rules_in_pest_error(err: pest::error::Error<Rule>) -> pest::error::Error<Rule> {    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> {    Identifier(&'i str),    String(String),    /// `<name>:<value>` where `<value>` is usually `Identifier` or `String`.    Pattern(Box<PatternNode<'i>>),    Unary(UnaryOp, Box<ExpressionNode<'i>>),    Binary(BinaryOp, Box<ExpressionNode<'i>>, Box<ExpressionNode<'i>>),    /// `x | y | ..`    UnionAll(Vec<ExpressionNode<'i>>),    FunctionCall(Box<FunctionCallNode<'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(_) => Ok(self),            Self::Pattern(pattern) => folder.fold_pattern(pattern, span),            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::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 pattern(pattern: Box<PatternNode<'i>>) -> Self {        Self::Pattern(pattern)    }     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 {    /// `~`    Negate,} #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]pub enum BinaryOp {    /// `&`    Intersection,    /// `~`    Difference,} pub type ExpressionNode<'i> = dsl_util::ExpressionNode<'i, ExpressionKind<'i>>;pub type FunctionCallNode<'i> = dsl_util::FunctionCallNode<'i, ExpressionKind<'i>>;pub type PatternNode<'i> = dsl_util::PatternNode<'i, ExpressionKind<'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)} fn parse_function_call_node(pair: Pair<Rule>) -> FilesetParseResult<FunctionCallNode> {    assert_eq!(pair.as_rule(), Rule::function);    let [name_pair, args_pair] = pair.into_inner().collect_array().unwrap();    assert_eq!(name_pair.as_rule(), Rule::function_name);    assert_eq!(args_pair.as_rule(), Rule::function_arguments);    let name_span = name_pair.as_span();    let args_span = args_pair.as_span();    let name = name_pair.as_str();    let args = args_pair        .into_inner()        .map(parse_expression_node)        .try_collect()?;    Ok(FunctionCallNode {        name,        name_span,        args,        keyword_args: vec![], // unsupported        args_span,    })} fn parse_as_string_literal(pair: Pair<Rule>) -> String {    match pair.as_rule() {        Rule::identifier => pair.as_str().to_owned(),        Rule::string_literal => STRING_LITERAL_PARSER.parse(pair.into_inner()),        Rule::raw_string_literal => {            let [content] = pair.into_inner().collect_array().unwrap();            assert_eq!(content.as_rule(), Rule::raw_string_content);            content.as_str().to_owned()        }        r => panic!("unexpected string literal rule: {r:?}"),    }} fn parse_primary_node(pair: Pair<Rule>) -> FilesetParseResult<ExpressionNode> {    assert_eq!(pair.as_rule(), Rule::primary);    let span = pair.as_span();    let first = pair.into_inner().next().unwrap();    let expr = match first.as_rule() {        // Ignore inner span to preserve parenthesized expression as such.        Rule::expression => parse_expression_node(first)?.kind,        Rule::function => {            let function = Box::new(parse_function_call_node(first)?);            ExpressionKind::FunctionCall(function)        }        Rule::pattern => {            let [lhs, op, rhs] = first.into_inner().collect_array().unwrap();            assert_eq!(lhs.as_rule(), Rule::strict_identifier);            assert_eq!(op.as_rule(), Rule::pattern_kind_op);            let pattern = Box::new(PatternNode {                name: lhs.as_str(),                name_span: lhs.as_span(),                value: parse_primary_node(rhs)?,            });            ExpressionKind::Pattern(pattern)        }        Rule::identifier => ExpressionKind::Identifier(first.as_str()),        Rule::string_literal | Rule::raw_string_literal => {            ExpressionKind::String(parse_as_string_literal(first))        }        r => panic!("unexpected primary rule: {r:?}"),    };    Ok(ExpressionNode::new(expr, span))} fn parse_expression_node(pair: Pair<Rule>) -> FilesetParseResult<ExpressionNode> {    assert_eq!(pair.as_rule(), Rule::expression);    static PRATT: LazyLock<PrattParser<Rule>> = LazyLock::new(|| {        PrattParser::new()            .op(Op::infix(Rule::union_op, Assoc::Left))            .op(Op::infix(Rule::intersection_op, Assoc::Left)                | Op::infix(Rule::difference_op, Assoc::Left))            .op(Op::prefix(Rule::negate_op))    });    PRATT        .map_primary(parse_primary_node)        .map_prefix(|op, rhs| {            let op_kind = match op.as_rule() {                Rule::negate_op => UnaryOp::Negate,                r => panic!("unexpected prefix operator rule {r:?}"),            };            let rhs = Box::new(rhs?);            let span = op.as_span().start_pos().span(&rhs.span.end_pos());            let expr = ExpressionKind::Unary(op_kind, rhs);            Ok(ExpressionNode::new(expr, span))        })        .map_infix(|lhs, op, rhs| {            let op_kind = match op.as_rule() {                Rule::union_op => return Ok(union_nodes(lhs?, rhs?)),                Rule::intersection_op => BinaryOp::Intersection,                Rule::difference_op => BinaryOp::Difference,                r => panic!("unexpected infix operator rule {r:?}"),            };            let lhs = Box::new(lhs?);            let rhs = Box::new(rhs?);            let span = lhs.span.start_pos().span(&rhs.span.end_pos());            let expr = ExpressionKind::Binary(op_kind, lhs, rhs);            Ok(ExpressionNode::new(expr, span))        })        .parse(pair.into_inner())} /// Parses text into expression tree. No name resolution is made at this stage.pub fn parse_program(text: &str) -> FilesetParseResult<ExpressionNode<'_>> {    let mut pairs = FilesetParser::parse(Rule::program, text)?;    let first = pairs.next().unwrap();    parse_expression_node(first)} /// Parses text into expression tree with bare string fallback. No name/// resolution is made at this stage.////// If the text can't be parsed as a fileset expression, and if it doesn't/// contain any operator-like characters, it will be parsed as a file path.pub fn parse_program_or_bare_string(text: &str) -> FilesetParseResult<ExpressionNode<'_>> {    let mut pairs = FilesetParser::parse(Rule::program_or_bare_string, text)?;    let first = pairs.next().unwrap();    let span = first.as_span();    let expr = match first.as_rule() {        Rule::expression => return parse_expression_node(first),        Rule::bare_string_pattern => {            let [lhs, op, rhs] = first.into_inner().collect_array().unwrap();            assert_eq!(lhs.as_rule(), Rule::strict_identifier);            assert_eq!(op.as_rule(), Rule::pattern_kind_op);            assert_eq!(rhs.as_rule(), Rule::bare_string);            let name_span = lhs.as_span();            let value_span = rhs.as_span();            let name = lhs.as_str();            let value_expr = ExpressionKind::String(rhs.as_str().to_owned());            let pattern = Box::new(PatternNode {                name,                name_span,                value: ExpressionNode::new(value_expr, value_span),            });            ExpressionKind::Pattern(pattern)        }        Rule::bare_string => ExpressionKind::String(first.as_str().to_owned()),        r => panic!("unexpected program or bare string rule: {r:?}"),    };    Ok(ExpressionNode::new(expr, span))} /// Map of fileset aliases.pub type FilesetAliasesMap = AliasesMap<FilesetAliasParser, String>; #[derive(Clone, Debug, Default)]pub struct FilesetAliasParser; impl AliasDeclarationParser for FilesetAliasParser {    type Error = FilesetParseError;     fn parse_declaration(&self, source: &str) -> Result<AliasDeclaration, Self::Error> {        let mut pairs = FilesetParser::parse(Rule::alias_declaration, source)?;        let first = pairs.next().unwrap();        match first.as_rule() {            Rule::strict_identifier => Ok(AliasDeclaration::Symbol(first.as_str().to_owned())),            Rule::pattern_alias_declaration => {                let [name_pair, op, param_pair] = first.into_inner().collect_array().unwrap();                assert_eq!(name_pair.as_rule(), Rule::strict_identifier);                assert_eq!(op.as_rule(), Rule::pattern_kind_op);                assert_eq!(param_pair.as_rule(), Rule::strict_identifier);                let name = name_pair.as_str().to_owned();                let param = param_pair.as_str().to_owned();                Ok(AliasDeclaration::Pattern(name, param))            }            Rule::function_alias_declaration => {                let [name_pair, params_pair] = first.into_inner().collect_array().unwrap();                assert_eq!(name_pair.as_rule(), Rule::function_name);                assert_eq!(params_pair.as_rule(), Rule::formal_parameters);                let name = name_pair.as_str().to_owned();                let params_span = params_pair.as_span();                let params = params_pair                    .into_inner()                    .map(|pair| match pair.as_rule() {                        Rule::strict_identifier => pair.as_str().to_owned(),                        r => panic!("unexpected formal parameter rule {r:?}"),                    })                    .collect_vec();                if params.iter().all_unique() {                    Ok(AliasDeclaration::Function(name, params))                } else {                    Err(FilesetParseError::new(                        FilesetParseErrorKind::RedefinedFunctionParameter,                        params_span,                    ))                }            }            r => panic!("unexpected alias declaration rule {r:?}"),        }    }} 

Lines 1–500 of 1364.