// Copyright 2020 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. use std::collections::HashMap;use std::error;use std::mem;use std::sync::LazyLock; use itertools::Itertools as _;use jj_lib::dsl_util;use jj_lib::dsl_util::AliasDeclaration;use jj_lib::dsl_util::AliasDeclarationParser;use jj_lib::dsl_util::AliasDefinitionParser;use jj_lib::dsl_util::AliasExpandError;use jj_lib::dsl_util::AliasExpandableExpression;use jj_lib::dsl_util::AliasId;use jj_lib::dsl_util::AliasesMap;use jj_lib::dsl_util::Diagnostics;use jj_lib::dsl_util::ExpressionFolder;use jj_lib::dsl_util::FoldableExpression;use jj_lib::dsl_util::FunctionCallParser;use jj_lib::dsl_util::InvalidArguments;use jj_lib::dsl_util::StringLiteralParser;use jj_lib::dsl_util::collect_similar;use jj_lib::str_util::StringPattern;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; #[derive(Parser)]#[grammar = "template.pest"]struct TemplateParser; 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::identifier, function_arguments_rule: Rule::function_arguments, keyword_argument_rule: Rule::keyword_argument, argument_name_rule: Rule::identifier, argument_value_rule: Rule::template,}; impl Rule { fn to_symbol(self) -> Option<&'static str> { match self { Self::EOI => None, Self::WHITESPACE => 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::any_string_literal => None, Self::integer_literal => None, Self::identifier => None, Self::concat_op => Some("++"), Self::logical_or_op => Some("||"), Self::logical_and_op => Some("&&"), Self::eq_op => Some("=="), Self::ne_op => Some("!="), Self::ge_op => Some(">="), Self::gt_op => Some(">"), Self::le_op => Some("<="), Self::lt_op => Some("<"), Self::add_op => Some("+"), Self::sub_op => Some("-"), Self::mul_op => Some("*"), Self::div_op => Some("/"), Self::rem_op => Some("%"), Self::logical_not_op => Some("!"), Self::negate_op => Some("-"), Self::pattern_kind_op => Some(":"), Self::prefix_ops => None, Self::infix_ops => None, Self::function => None, Self::keyword_argument => None, Self::argument => None, Self::function_arguments => None, Self::lambda => None, Self::formal_parameters => None, Self::string_pattern_identifier => None, Self::string_pattern => None, Self::primary => None, Self::term => None, Self::expression => None, Self::template => None, Self::program => None, Self::function_alias_declaration => None, Self::alias_declaration => None, } }} /// Manages diagnostic messages emitted during template parsing and building.pub type TemplateDiagnostics = Diagnostics<TemplateParseError>; pub type TemplateParseResult<T> = Result<T, TemplateParseError>; #[derive(Debug, Error)]#[error("{pest_error}")]pub struct TemplateParseError { kind: TemplateParseErrorKind, pest_error: Box<pest::error::Error<Rule>>, source: Option<Box<dyn error::Error + Send + Sync>>,} #[derive(Clone, Debug, Eq, Error, PartialEq)]pub enum TemplateParseErrorKind { #[error("Syntax error")] SyntaxError, #[error("Keyword `{name}` doesn't exist")] NoSuchKeyword { name: String, candidates: Vec<String>, }, #[error("Function `{name}` doesn't exist")] NoSuchFunction { name: String, candidates: Vec<String>, }, #[error("Method `{name}` doesn't exist for type `{type_name}`")] NoSuchMethod { type_name: String, 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 TemplateParseError { pub fn with_span(kind: TemplateParseErrorKind, 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 fn with_source(mut self, source: impl Into<Box<dyn error::Error + Send + Sync>>) -> Self { self.source = Some(source.into()); self } pub fn expected_type(expected: &str, actual: &str, span: pest::Span<'_>) -> Self { let message = format!("Expected expression of type `{expected}`, but actual type is `{actual}`"); Self::expression(message, span) } /// Some other expression error. pub fn expression(message: impl Into<String>, span: pest::Span<'_>) -> Self { Self::with_span(TemplateParseErrorKind::Expression(message.into()), span) } /// If this is a `NoSuchKeyword` error, expands the candidates list with the /// given `other_keywords`. pub fn extend_keyword_candidates<I>(mut self, other_keywords: I) -> Self where I: IntoIterator, I::Item: AsRef<str>, { if let TemplateParseErrorKind::NoSuchKeyword { name, candidates } = &mut self.kind { let other_candidates = collect_similar(name, other_keywords); *candidates = itertools::merge(mem::take(candidates), other_candidates) .dedup() .collect(); } self } /// If this is a `NoSuchFunction` error, expands the candidates list with /// the given `other_functions`. pub fn extend_function_candidates<I>(mut self, other_functions: I) -> Self where I: IntoIterator, I::Item: AsRef<str>, { if let TemplateParseErrorKind::NoSuchFunction { name, candidates } = &mut self.kind { let other_candidates = collect_similar(name, other_functions); *candidates = itertools::merge(mem::take(candidates), other_candidates) .dedup() .collect(); } self } /// Expands keyword/function candidates with the given aliases. pub fn extend_alias_candidates(self, aliases_map: &TemplateAliasesMap) -> Self { self.extend_keyword_candidates(aliases_map.symbol_names()) .extend_function_candidates(aliases_map.function_names()) } pub fn kind(&self) -> &TemplateParseErrorKind { &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 TemplateParseError { fn invalid_arguments(err: InvalidArguments<'_>) -> Self { err.into() } fn recursive_expansion(id: AliasId<'_>, span: pest::Span<'_>) -> Self { Self::with_span(TemplateParseErrorKind::RecursiveAlias(id.to_string()), span) } fn within_alias_expansion(self, id: AliasId<'_>, span: pest::Span<'_>) -> Self { let kind = match id { AliasId::Symbol(_) | AliasId::Function(..) => { TemplateParseErrorKind::InAliasExpansion(id.to_string()) } AliasId::Parameter(_) => TemplateParseErrorKind::InParameterExpansion(id.to_string()), }; Self::with_span(kind, span).with_source(self) }} impl From<pest::error::Error<Rule>> for TemplateParseError { fn from(err: pest::error::Error<Rule>) -> Self { Self { kind: TemplateParseErrorKind::SyntaxError, pest_error: Box::new(rename_rules_in_pest_error(err)), source: None, } }} impl From<InvalidArguments<'_>> for TemplateParseError { fn from(err: InvalidArguments<'_>) -> Self { let kind = TemplateParseErrorKind::InvalidArguments { name: err.name.to_owned(), message: err.message, }; Self::with_span(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, PartialEq)]pub enum ExpressionKind<'i> { Identifier(&'i str), Boolean(bool), Integer(i64), String(String), /// `<kind>:"<value>"` StringPattern { kind: &'i str, value: String, }, Unary(UnaryOp, Box<ExpressionNode<'i>>), Binary(BinaryOp, Box<ExpressionNode<'i>>, Box<ExpressionNode<'i>>), Concat(Vec<ExpressionNode<'i>>), FunctionCall(Box<FunctionCallNode<'i>>), MethodCall(Box<MethodCallNode<'i>>), Lambda(Box<LambdaNode<'i>>), /// Identity node to preserve the span in the source template 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), ExpressionKind::Boolean(_) | ExpressionKind::Integer(_) | ExpressionKind::String(_) | ExpressionKind::StringPattern { .. } => 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::Concat(nodes) => Ok(Self::Concat(dsl_util::fold_expression_nodes( folder, nodes, )?)), Self::FunctionCall(function) => folder.fold_function_call(function, span), Self::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(Self::MethodCall(method)) } Self::Lambda(lambda) => { let lambda = Box::new(LambdaNode { params: lambda.params, params_span: lambda.params_span, body: folder.fold_expression(lambda.body)?, }); Ok(Self::Lambda(lambda)) } 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 { 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 { /// `!` LogicalNot, /// `-` Negate,} #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]pub enum BinaryOp { /// `||` LogicalOr, /// `&&` LogicalAnd, /// `==` Eq, /// `!=` Ne, /// `>=` Ge, /// `>` Gt, /// `<=` Le, /// `<` Lt, /// `+` Add, /// `-` Sub, /// `*` Mul, /// `/` Div, /// `%` Rem,} pub type ExpressionNode<'i> = dsl_util::ExpressionNode<'i, ExpressionKind<'i>>;pub type FunctionCallNode<'i> = dsl_util::FunctionCallNode<'i, ExpressionKind<'i>>; #[derive(Clone, Debug, PartialEq)]pub struct MethodCallNode<'i> { pub object: ExpressionNode<'i>, pub function: FunctionCallNode<'i>,} #[derive(Clone, Debug, PartialEq)]pub struct LambdaNode<'i> { pub params: Vec<&'i str>, pub params_span: pest::Span<'i>, pub body: ExpressionNode<'i>,} fn parse_identifier_or_literal(pair: Pair<Rule>) -> ExpressionKind { assert_eq!(pair.as_rule(), Rule::identifier); match pair.as_str() { "false" => ExpressionKind::Boolean(false), "true" => ExpressionKind::Boolean(true), name => ExpressionKind::Identifier(name), }} fn parse_identifier_name(pair: Pair<'_, Rule>) -> TemplateParseResult<&str> { let span = pair.as_span(); if let ExpressionKind::Identifier(name) = parse_identifier_or_literal(pair) { Ok(name) } else { Err(TemplateParseError::expression("Expected identifier", span)) }} fn parse_formal_parameters(params_pair: Pair<'_, Rule>) -> TemplateParseResult<Vec<&str>> { assert_eq!(params_pair.as_rule(), Rule::formal_parameters); let params_span = params_pair.as_span(); let params: Vec<_> = params_pair .into_inner() .map(parse_identifier_name) .try_collect()?; if params.iter().all_unique() { Ok(params) } else { Err(TemplateParseError::with_span( TemplateParseErrorKind::RedefinedFunctionParameter, params_span, )) }} fn parse_lambda_node(pair: Pair<Rule>) -> TemplateParseResult<LambdaNode> { assert_eq!(pair.as_rule(), Rule::lambda); let mut inner = pair.into_inner(); let params_pair = inner.next().unwrap(); let params_span = params_pair.as_span(); let body_pair = inner.next().unwrap(); let params = parse_formal_parameters(params_pair)?; let body = parse_template_node(body_pair)?; Ok(LambdaNode { params, params_span, body, })} fn parse_raw_string_literal(pair: Pair<Rule>) -> String { let [content] = pair.into_inner().collect_array().unwrap(); assert_eq!(content.as_rule(), Rule::raw_string_content); content.as_str().to_owned()} fn parse_term_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode> { assert_eq!(pair.as_rule(), Rule::term); let mut inner = pair.into_inner(); let primary = inner.next().unwrap(); assert_eq!(primary.as_rule(), Rule::primary); let primary_span = primary.as_span(); let expr = primary.into_inner().next().unwrap(); let primary_kind = match expr.as_rule() { Rule::string_literal => { let text = STRING_LITERAL_PARSER.parse(expr.into_inner()); ExpressionKind::String(text) } Rule::raw_string_literal => { let text = parse_raw_string_literal(expr); ExpressionKind::String(text) } Rule::integer_literal => { let value = expr.as_str().parse().map_err(|err| { TemplateParseError::expression("Invalid integer literal", expr.as_span()) .with_source(err) })?;Lines 1–500 of 1521. Load more
// Copyright 2020 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. use std::collections::HashMap; use std::error; use std::mem; use std::sync::LazyLock; use itertools::Itertools as _; use jj_lib::dsl_util; use jj_lib::dsl_util::AliasDeclaration; use jj_lib::dsl_util::AliasDeclarationParser; use jj_lib::dsl_util::AliasDefinitionParser; use jj_lib::dsl_util::AliasExpandError; use jj_lib::dsl_util::AliasExpandableExpression; use jj_lib::dsl_util::AliasId; use jj_lib::dsl_util::AliasesMap; use jj_lib::dsl_util::Diagnostics; use jj_lib::dsl_util::ExpressionFolder; use jj_lib::dsl_util::FoldableExpression; use jj_lib::dsl_util::FunctionCallParser; use jj_lib::dsl_util::InvalidArguments; use jj_lib::dsl_util::StringLiteralParser; use jj_lib::dsl_util::collect_similar; use jj_lib::str_util::StringPattern; 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; #[derive(Parser)] #[grammar = "template.pest"] struct TemplateParser; 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::identifier, function_arguments_rule: Rule::function_arguments, keyword_argument_rule: Rule::keyword_argument, argument_name_rule: Rule::identifier, argument_value_rule: Rule::template, }; impl Rule { fn to_symbol(self) -> Option<&'static str> { match self { Self::EOI => None, Self::WHITESPACE => 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::any_string_literal => None, Self::integer_literal => None, Self::identifier => None, Self::concat_op => Some("++"), Self::logical_or_op => Some("||"), Self::logical_and_op => Some("&&"), Self::eq_op => Some("=="), Self::ne_op => Some("!="), Self::ge_op => Some(">="), Self::gt_op => Some(">"), Self::le_op => Some("<="), Self::lt_op => Some("<"), Self::add_op => Some("+"), Self::sub_op => Some("-"), Self::mul_op => Some("*"), Self::div_op => Some("/"), Self::rem_op => Some("%"), Self::logical_not_op => Some("!"), Self::negate_op => Some("-"), Self::pattern_kind_op => Some(":"), Self::prefix_ops => None, Self::infix_ops => None, Self::function => None, Self::keyword_argument => None, Self::argument => None, Self::function_arguments => None, Self::lambda => None, Self::formal_parameters => None, Self::string_pattern_identifier => None, Self::string_pattern => None, Self::primary => None, Self::term => None, Self::expression => None, Self::template => None, Self::program => None, Self::function_alias_declaration => None, Self::alias_declaration => None, } } } /// Manages diagnostic messages emitted during template parsing and building. pub type TemplateDiagnostics = Diagnostics<TemplateParseError>; pub type TemplateParseResult<T> = Result<T, TemplateParseError>; #[derive(Debug, Error)] #[error("{pest_error}")] pub struct TemplateParseError { kind: TemplateParseErrorKind, pest_error: Box<pest::error::Error<Rule>>, source: Option<Box<dyn error::Error + Send + Sync>>, } #[derive(Clone, Debug, Eq, Error, PartialEq)] pub enum TemplateParseErrorKind { #[error("Syntax error")] SyntaxError, #[error("Keyword `{name}` doesn't exist")] NoSuchKeyword { name: String, candidates: Vec<String>, }, #[error("Function `{name}` doesn't exist")] NoSuchFunction { name: String, candidates: Vec<String>, }, #[error("Method `{name}` doesn't exist for type `{type_name}`")] NoSuchMethod { type_name: String, 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 TemplateParseError { pub fn with_span(kind: TemplateParseErrorKind, 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 fn with_source(mut self, source: impl Into<Box<dyn error::Error + Send + Sync>>) -> Self { self.source = Some(source.into()); self } pub fn expected_type(expected: &str, actual: &str, span: pest::Span<'_>) -> Self { let message = format!("Expected expression of type `{expected}`, but actual type is `{actual}`"); Self::expression(message, span) } /// Some other expression error. pub fn expression(message: impl Into<String>, span: pest::Span<'_>) -> Self { Self::with_span(TemplateParseErrorKind::Expression(message.into()), span) } /// If this is a `NoSuchKeyword` error, expands the candidates list with the /// given `other_keywords`. pub fn extend_keyword_candidates<I>(mut self, other_keywords: I) -> Self where I: IntoIterator, I::Item: AsRef<str>, { if let TemplateParseErrorKind::NoSuchKeyword { name, candidates } = &mut self.kind { let other_candidates = collect_similar(name, other_keywords); *candidates = itertools::merge(mem::take(candidates), other_candidates) .dedup() .collect(); } self } /// If this is a `NoSuchFunction` error, expands the candidates list with /// the given `other_functions`. pub fn extend_function_candidates<I>(mut self, other_functions: I) -> Self where I: IntoIterator, I::Item: AsRef<str>, { if let TemplateParseErrorKind::NoSuchFunction { name, candidates } = &mut self.kind { let other_candidates = collect_similar(name, other_functions); *candidates = itertools::merge(mem::take(candidates), other_candidates) .dedup() .collect(); } self } /// Expands keyword/function candidates with the given aliases. pub fn extend_alias_candidates(self, aliases_map: &TemplateAliasesMap) -> Self { self.extend_keyword_candidates(aliases_map.symbol_names()) .extend_function_candidates(aliases_map.function_names()) } pub fn kind(&self) -> &TemplateParseErrorKind { &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 TemplateParseError { fn invalid_arguments(err: InvalidArguments<'_>) -> Self { err.into() } fn recursive_expansion(id: AliasId<'_>, span: pest::Span<'_>) -> Self { Self::with_span(TemplateParseErrorKind::RecursiveAlias(id.to_string()), span) } fn within_alias_expansion(self, id: AliasId<'_>, span: pest::Span<'_>) -> Self { let kind = match id { AliasId::Symbol(_) | AliasId::Function(..) => { TemplateParseErrorKind::InAliasExpansion(id.to_string()) } AliasId::Parameter(_) => TemplateParseErrorKind::InParameterExpansion(id.to_string()), }; Self::with_span(kind, span).with_source(self) } } impl From<pest::error::Error<Rule>> for TemplateParseError { fn from(err: pest::error::Error<Rule>) -> Self { Self { kind: TemplateParseErrorKind::SyntaxError, pest_error: Box::new(rename_rules_in_pest_error(err)), source: None, } } } impl From<InvalidArguments<'_>> for TemplateParseError { fn from(err: InvalidArguments<'_>) -> Self { let kind = TemplateParseErrorKind::InvalidArguments { name: err.name.to_owned(), message: err.message, }; Self::with_span(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, PartialEq)] pub enum ExpressionKind<'i> { Identifier(&'i str), Boolean(bool), Integer(i64), String(String), /// `<kind>:"<value>"` StringPattern { kind: &'i str, value: String, }, Unary(UnaryOp, Box<ExpressionNode<'i>>), Binary(BinaryOp, Box<ExpressionNode<'i>>, Box<ExpressionNode<'i>>), Concat(Vec<ExpressionNode<'i>>), FunctionCall(Box<FunctionCallNode<'i>>), MethodCall(Box<MethodCallNode<'i>>), Lambda(Box<LambdaNode<'i>>), /// Identity node to preserve the span in the source template 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), ExpressionKind::Boolean(_) | ExpressionKind::Integer(_) | ExpressionKind::String(_) | ExpressionKind::StringPattern { .. } => 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::Concat(nodes) => Ok(Self::Concat(dsl_util::fold_expression_nodes( folder, nodes, )?)), Self::FunctionCall(function) => folder.fold_function_call(function, span), Self::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(Self::MethodCall(method)) } Self::Lambda(lambda) => { let lambda = Box::new(LambdaNode { params: lambda.params, params_span: lambda.params_span, body: folder.fold_expression(lambda.body)?, }); Ok(Self::Lambda(lambda)) } 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 { 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 { /// `!` LogicalNot, /// `-` Negate, } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum BinaryOp { /// `||` LogicalOr, /// `&&` LogicalAnd, /// `==` Eq, /// `!=` Ne, /// `>=` Ge, /// `>` Gt, /// `<=` Le, /// `<` Lt, /// `+` Add, /// `-` Sub, /// `*` Mul, /// `/` Div, /// `%` Rem, } pub type ExpressionNode<'i> = dsl_util::ExpressionNode<'i, ExpressionKind<'i>>; pub type FunctionCallNode<'i> = dsl_util::FunctionCallNode<'i, ExpressionKind<'i>>; #[derive(Clone, Debug, PartialEq)] pub struct MethodCallNode<'i> { pub object: ExpressionNode<'i>, pub function: FunctionCallNode<'i>, } #[derive(Clone, Debug, PartialEq)] pub struct LambdaNode<'i> { pub params: Vec<&'i str>, pub params_span: pest::Span<'i>, pub body: ExpressionNode<'i>, } fn parse_identifier_or_literal(pair: Pair<Rule>) -> ExpressionKind { assert_eq!(pair.as_rule(), Rule::identifier); match pair.as_str() { "false" => ExpressionKind::Boolean(false), "true" => ExpressionKind::Boolean(true), name => ExpressionKind::Identifier(name), } } fn parse_identifier_name(pair: Pair<'_, Rule>) -> TemplateParseResult<&str> { let span = pair.as_span(); if let ExpressionKind::Identifier(name) = parse_identifier_or_literal(pair) { Ok(name) } else { Err(TemplateParseError::expression("Expected identifier", span)) } } fn parse_formal_parameters(params_pair: Pair<'_, Rule>) -> TemplateParseResult<Vec<&str>> { assert_eq!(params_pair.as_rule(), Rule::formal_parameters); let params_span = params_pair.as_span(); let params: Vec<_> = params_pair .into_inner() .map(parse_identifier_name) .try_collect()?; if params.iter().all_unique() { Ok(params) } else { Err(TemplateParseError::with_span( TemplateParseErrorKind::RedefinedFunctionParameter, params_span, )) } } fn parse_lambda_node(pair: Pair<Rule>) -> TemplateParseResult<LambdaNode> { assert_eq!(pair.as_rule(), Rule::lambda); let mut inner = pair.into_inner(); let params_pair = inner.next().unwrap(); let params_span = params_pair.as_span(); let body_pair = inner.next().unwrap(); let params = parse_formal_parameters(params_pair)?; let body = parse_template_node(body_pair)?; Ok(LambdaNode { params, params_span, body, }) } fn parse_raw_string_literal(pair: Pair<Rule>) -> String { let [content] = pair.into_inner().collect_array().unwrap(); assert_eq!(content.as_rule(), Rule::raw_string_content); content.as_str().to_owned() } fn parse_term_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode> { assert_eq!(pair.as_rule(), Rule::term); let mut inner = pair.into_inner(); let primary = inner.next().unwrap(); assert_eq!(primary.as_rule(), Rule::primary); let primary_span = primary.as_span(); let expr = primary.into_inner().next().unwrap(); let primary_kind = match expr.as_rule() { Rule::string_literal => { let text = STRING_LITERAL_PARSER.parse(expr.into_inner()); ExpressionKind::String(text) } Rule::raw_string_literal => { let text = parse_raw_string_literal(expr); ExpressionKind::String(text) } Rule::integer_literal => { let value = expr.as_str().parse().map_err(|err| { TemplateParseError::expression("Invalid integer literal", expr.as_span()) .with_source(err) })?;