// 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 itertools::Itertools as _;use jj_lib::dsl_util;use jj_lib::dsl_util::collect_similar;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 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; #[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 { Rule::EOI => None, Rule::whitespace => 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::integer_literal => None, Rule::identifier => None, Rule::concat_op => Some("++"), Rule::logical_or_op => Some("||"), Rule::logical_and_op => Some("&&"), Rule::eq_op => Some("=="), Rule::ne_op => Some("!="), Rule::ge_op => Some(">="), Rule::gt_op => Some(">"), Rule::le_op => Some("<="), Rule::lt_op => Some("<"), Rule::logical_not_op => Some("!"), Rule::negate_op => Some("-"), Rule::prefix_ops => None, Rule::infix_ops => None, Rule::function => None, Rule::keyword_argument => None, Rule::argument => None, Rule::function_arguments => None, Rule::lambda => None, Rule::formal_parameters => None, Rule::primary => None, Rule::term => None, Rule::expression => None, Rule::template => None, Rule::program => None, Rule::function_alias_declaration => None, Rule::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, )); TemplateParseError { 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 } // TODO: migrate all callers to table-based lookup_method() pub(crate) fn no_such_method( type_name: impl Into<String>, function: &FunctionCallNode, ) -> Self { TemplateParseError::with_span( TemplateParseErrorKind::NoSuchMethod { type_name: type_name.into(), name: function.name.to_owned(), candidates: vec![], }, function.name_span, ) } 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}`"); TemplateParseError::expression(message, span) } /// Some other expression error. pub fn expression(message: impl Into<String>, span: pest::Span<'_>) -> Self { TemplateParseError::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 { TemplateParseError { 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), 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 { ExpressionKind::Identifier(name) => folder.fold_identifier(name, span), ExpressionKind::Boolean(_) | ExpressionKind::Integer(_) | ExpressionKind::String(_) => { 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::Concat(nodes) => Ok(ExpressionKind::Concat( dsl_util::fold_expression_nodes(folder, nodes)?, )), ExpressionKind::FunctionCall(function) => folder.fold_function_call(function, span), ExpressionKind::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(ExpressionKind::MethodCall(method)) } ExpressionKind::Lambda(lambda) => { let lambda = Box::new(LambdaNode { params: lambda.params, params_span: lambda.params_span, body: folder.fold_expression(lambda.body)?, }); Ok(ExpressionKind::Lambda(lambda)) } 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 { /// `!` LogicalNot, /// `-` Negate,} #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]pub enum BinaryOp { /// `||` LogicalOr, /// `&&` LogicalAnd, /// `==` Eq, /// `!=` Ne, /// `>=` Ge, /// `>` Gt, /// `<=` Le, /// `<` Lt,} 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_term_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode> { assert_eq!(pair.as_rule(), Rule::term); let mut inner = pair.into_inner(); let expr = inner.next().unwrap(); let span = expr.as_span(); let primary = match expr.as_rule() { Rule::string_literal => { let text = STRING_LITERAL_PARSER.parse(expr.into_inner()); ExpressionNode::new(ExpressionKind::String(text), span) } Rule::raw_string_literal => { let [content] = expr.into_inner().collect_array().unwrap(); assert_eq!(content.as_rule(), Rule::raw_string_content); let text = content.as_str().to_owned(); ExpressionNode::new(ExpressionKind::String(text), span) } Rule::integer_literal => { let value = expr.as_str().parse().map_err(|err| { TemplateParseError::expression("Invalid integer literal", span).with_source(err) })?; ExpressionNode::new(ExpressionKind::Integer(value), span) } Rule::identifier => ExpressionNode::new(parse_identifier_or_literal(expr), span), Rule::function => { let function = Box::new(FUNCTION_CALL_PARSER.parse( expr, parse_identifier_name, parse_template_node, )?); ExpressionNode::new(ExpressionKind::FunctionCall(function), span) } Rule::lambda => { let lambda = Box::new(parse_lambda_node(expr)?); ExpressionNode::new(ExpressionKind::Lambda(lambda), span) } Rule::template => parse_template_node(expr)?, other => panic!("unexpected term: {other:?}"), };Lines 1–500 of 1350. 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 itertools::Itertools as _; use jj_lib::dsl_util; use jj_lib::dsl_util::collect_similar; 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 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; #[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 { Rule::EOI => None, Rule::whitespace => 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::integer_literal => None, Rule::identifier => None, Rule::concat_op => Some("++"), Rule::logical_or_op => Some("||"), Rule::logical_and_op => Some("&&"), Rule::eq_op => Some("=="), Rule::ne_op => Some("!="), Rule::ge_op => Some(">="), Rule::gt_op => Some(">"), Rule::le_op => Some("<="), Rule::lt_op => Some("<"), Rule::logical_not_op => Some("!"), Rule::negate_op => Some("-"), Rule::prefix_ops => None, Rule::infix_ops => None, Rule::function => None, Rule::keyword_argument => None, Rule::argument => None, Rule::function_arguments => None, Rule::lambda => None, Rule::formal_parameters => None, Rule::primary => None, Rule::term => None, Rule::expression => None, Rule::template => None, Rule::program => None, Rule::function_alias_declaration => None, Rule::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, )); TemplateParseError { 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 } // TODO: migrate all callers to table-based lookup_method() pub(crate) fn no_such_method( type_name: impl Into<String>, function: &FunctionCallNode, ) -> Self { TemplateParseError::with_span( TemplateParseErrorKind::NoSuchMethod { type_name: type_name.into(), name: function.name.to_owned(), candidates: vec![], }, function.name_span, ) } 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}`"); TemplateParseError::expression(message, span) } /// Some other expression error. pub fn expression(message: impl Into<String>, span: pest::Span<'_>) -> Self { TemplateParseError::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 { TemplateParseError { 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), 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 { ExpressionKind::Identifier(name) => folder.fold_identifier(name, span), ExpressionKind::Boolean(_) | ExpressionKind::Integer(_) | ExpressionKind::String(_) => { 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::Concat(nodes) => Ok(ExpressionKind::Concat( dsl_util::fold_expression_nodes(folder, nodes)?, )), ExpressionKind::FunctionCall(function) => folder.fold_function_call(function, span), ExpressionKind::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(ExpressionKind::MethodCall(method)) } ExpressionKind::Lambda(lambda) => { let lambda = Box::new(LambdaNode { params: lambda.params, params_span: lambda.params_span, body: folder.fold_expression(lambda.body)?, }); Ok(ExpressionKind::Lambda(lambda)) } 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 { /// `!` LogicalNot, /// `-` Negate, } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum BinaryOp { /// `||` LogicalOr, /// `&&` LogicalAnd, /// `==` Eq, /// `!=` Ne, /// `>=` Ge, /// `>` Gt, /// `<=` Le, /// `<` Lt, } 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_term_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode> { assert_eq!(pair.as_rule(), Rule::term); let mut inner = pair.into_inner(); let expr = inner.next().unwrap(); let span = expr.as_span(); let primary = match expr.as_rule() { Rule::string_literal => { let text = STRING_LITERAL_PARSER.parse(expr.into_inner()); ExpressionNode::new(ExpressionKind::String(text), span) } Rule::raw_string_literal => { let [content] = expr.into_inner().collect_array().unwrap(); assert_eq!(content.as_rule(), Rule::raw_string_content); let text = content.as_str().to_owned(); ExpressionNode::new(ExpressionKind::String(text), span) } Rule::integer_literal => { let value = expr.as_str().parse().map_err(|err| { TemplateParseError::expression("Invalid integer literal", span).with_source(err) })?; ExpressionNode::new(ExpressionKind::Integer(value), span) } Rule::identifier => ExpressionNode::new(parse_identifier_or_literal(expr), span), Rule::function => { let function = Box::new(FUNCTION_CALL_PARSER.parse( expr, parse_identifier_name, parse_template_node, )?); ExpressionNode::new(ExpressionKind::FunctionCall(function), span) } Rule::lambda => { let lambda = Box::new(parse_lambda_node(expr)?); ExpressionNode::new(ExpressionKind::Lambda(lambda), span) } Rule::template => parse_template_node(expr)?, other => panic!("unexpected term: {other:?}"), };