// Copyright 2020-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. //! Domain-specific language helpers. use std::ascii;use std::collections::HashMap;use std::fmt;use std::slice; use itertools::Itertools as _;use pest::RuleType;use pest::iterators::Pair;use pest::iterators::Pairs; /// Manages diagnostic messages emitted during parsing.////// `T` is usually a parse error type of the language, which contains a message/// and source span of 'static lifetime.#[derive(Debug)]pub struct Diagnostics<T> { // This might be extended to [{ kind: Warning|Error, message: T }, ..]. diagnostics: Vec<T>,} impl<T> Diagnostics<T> { /// Creates new empty diagnostics collector. pub fn new() -> Self { Self { diagnostics: Vec::new(), } } /// Returns `true` if there are no diagnostic messages. pub fn is_empty(&self) -> bool { self.diagnostics.is_empty() } /// Returns the number of diagnostic messages. pub fn len(&self) -> usize { self.diagnostics.len() } /// Returns iterator over diagnostic messages. pub fn iter(&self) -> slice::Iter<'_, T> { self.diagnostics.iter() } /// Adds a diagnostic message of warning level. pub fn add_warning(&mut self, diag: T) { self.diagnostics.push(diag); } /// Moves diagnostic messages of different type (such as fileset warnings /// emitted within `file()` revset.) pub fn extend_with<U>(&mut self, diagnostics: Diagnostics<U>, mut f: impl FnMut(U) -> T) { self.diagnostics .extend(diagnostics.diagnostics.into_iter().map(&mut f)); }} impl<T> Default for Diagnostics<T> { fn default() -> Self { Self::new() }} impl<'a, T> IntoIterator for &'a Diagnostics<T> { type Item = &'a T; type IntoIter = slice::Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { self.iter() }} /// AST node without type or name checking.#[derive(Clone, Debug, Eq, PartialEq)]pub struct ExpressionNode<'i, T> { /// Expression item such as identifier, literal, function call, etc. pub kind: T, /// Span of the node. pub span: pest::Span<'i>,} impl<'i, T> ExpressionNode<'i, T> { /// Wraps the given expression and span. pub fn new(kind: T, span: pest::Span<'i>) -> Self { Self { kind, span } }} /// `<name>:<value>` expression in AST.#[derive(Clone, Debug, Eq, PartialEq)]pub struct PatternNode<'i, T> { /// Pattern name or type (such as `glob`.) pub name: &'i str, /// Span of the pattern name. pub name_span: pest::Span<'i>, /// Value expression. pub value: ExpressionNode<'i, T>,} /// Function call in AST.#[derive(Clone, Debug, Eq, PartialEq)]pub struct FunctionCallNode<'i, T> { /// Function name. pub name: &'i str, /// Span of the function name. pub name_span: pest::Span<'i>, /// List of positional arguments. pub args: Vec<ExpressionNode<'i, T>>, /// List of keyword arguments. pub keyword_args: Vec<KeywordArgument<'i, T>>, /// Span of the arguments list. pub args_span: pest::Span<'i>,} /// Keyword argument pair in AST.#[derive(Clone, Debug, Eq, PartialEq)]pub struct KeywordArgument<'i, T> { /// Parameter name. pub name: &'i str, /// Span of the parameter name. pub name_span: pest::Span<'i>, /// Value expression. pub value: ExpressionNode<'i, T>,} impl<'i, T> FunctionCallNode<'i, T> { /// Number of arguments assuming named arguments are all unique. pub fn arity(&self) -> usize { self.args.len() + self.keyword_args.len() } /// Ensures that no arguments passed. pub fn expect_no_arguments(&self) -> Result<(), InvalidArguments<'i>> { let ([], []) = self.expect_arguments()?; Ok(()) } /// Extracts exactly N required arguments. pub fn expect_exact_arguments<const N: usize>( &self, ) -> Result<&[ExpressionNode<'i, T>; N], InvalidArguments<'i>> { let (args, []) = self.expect_arguments()?; Ok(args) } /// Extracts N required arguments and remainders. /// /// This can be used to get all the positional arguments without requiring /// any (N = 0): /// ```ignore /// let ([], content_nodes) = function.expect_some_arguments()?; /// ``` /// Avoid accessing `function.args` directly, as that may allow keyword /// arguments to be silently ignored. #[expect(clippy::type_complexity)] pub fn expect_some_arguments<const N: usize>( &self, ) -> Result<(&[ExpressionNode<'i, T>; N], &[ExpressionNode<'i, T>]), InvalidArguments<'i>> { self.ensure_no_keyword_arguments()?; if self.args.len() >= N { let (required, rest) = self.args.split_at(N); Ok((required.try_into().unwrap(), rest)) } else { Err(self.invalid_arguments_count(N, None)) } } /// Extracts N required arguments and M optional arguments. #[expect(clippy::type_complexity)] pub fn expect_arguments<const N: usize, const M: usize>( &self, ) -> Result< ( &[ExpressionNode<'i, T>; N], [Option<&ExpressionNode<'i, T>>; M], ), InvalidArguments<'i>, > { self.ensure_no_keyword_arguments()?; let count_range = N..=(N + M); if count_range.contains(&self.args.len()) { let (required, rest) = self.args.split_at(N); let mut optional = rest.iter().map(Some).collect_vec(); optional.resize(M, None); Ok(( required.try_into().unwrap(), optional.try_into().ok().unwrap(), )) } else { let (min, max) = count_range.into_inner(); Err(self.invalid_arguments_count(min, Some(max))) } } /// Extracts N required arguments and M optional arguments. Some of them can /// be specified as keyword arguments. /// /// `names` is a list of parameter names. Unnamed positional arguments /// should be padded with `""`. #[expect(clippy::type_complexity)] pub fn expect_named_arguments<const N: usize, const M: usize>( &self, names: &[&str], ) -> Result< ( [&ExpressionNode<'i, T>; N], [Option<&ExpressionNode<'i, T>>; M], ), InvalidArguments<'i>, > { if self.keyword_args.is_empty() { let (required, optional) = self.expect_arguments::<N, M>()?; Ok((required.each_ref(), optional)) } else { let (required, optional) = self.expect_named_arguments_vec(names, N, N + M)?; Ok(( required.try_into().ok().unwrap(), optional.try_into().ok().unwrap(), )) } } #[expect(clippy::type_complexity)] fn expect_named_arguments_vec( &self, names: &[&str], min: usize, max: usize, ) -> Result< ( Vec<&ExpressionNode<'i, T>>, Vec<Option<&ExpressionNode<'i, T>>>, ), InvalidArguments<'i>, > { assert!(names.len() <= max); if self.args.len() > max { return Err(self.invalid_arguments_count(min, Some(max))); } let mut extracted = Vec::with_capacity(max); extracted.extend(self.args.iter().map(Some)); extracted.resize(max, None); for arg in &self.keyword_args { let name = arg.name; let span = arg.name_span.start_pos().span(&arg.value.span.end_pos()); let pos = names.iter().position(|&n| n == name).ok_or_else(|| { self.invalid_arguments(format!(r#"Unexpected keyword argument "{name}""#), span) })?; if extracted[pos].is_some() { return Err(self.invalid_arguments( format!(r#"Got multiple values for keyword "{name}""#), span, )); } extracted[pos] = Some(&arg.value); } let optional = extracted.split_off(min); let required = extracted.into_iter().flatten().collect_vec(); if required.len() != min { return Err(self.invalid_arguments_count(min, Some(max))); } Ok((required, optional)) } fn ensure_no_keyword_arguments(&self) -> Result<(), InvalidArguments<'i>> { if let (Some(first), Some(last)) = (self.keyword_args.first(), self.keyword_args.last()) { let span = first.name_span.start_pos().span(&last.value.span.end_pos()); Err(self.invalid_arguments("Unexpected keyword arguments".to_owned(), span)) } else { Ok(()) } } fn invalid_arguments(&self, message: String, span: pest::Span<'i>) -> InvalidArguments<'i> { InvalidArguments { name: self.name, message, span, } } fn invalid_arguments_count(&self, min: usize, max: Option<usize>) -> InvalidArguments<'i> { let message = match (min, max) { (min, Some(max)) if min == max => format!("Expected {min} arguments"), (min, Some(max)) => format!("Expected {min} to {max} arguments"), (min, None) => format!("Expected at least {min} arguments"), }; self.invalid_arguments(message, self.args_span) } fn invalid_arguments_count_with_arities( &self, arities: impl IntoIterator<Item = usize>, ) -> InvalidArguments<'i> { let message = format!("Expected {} arguments", arities.into_iter().join(", ")); self.invalid_arguments(message, self.args_span) }} /// Unexpected number of arguments, or invalid combination of arguments.////// This error is supposed to be converted to language-specific parse error/// type, where lifetime `'i` will be eliminated.#[derive(Clone, Debug)]pub struct InvalidArguments<'i> { /// Function name. pub name: &'i str, /// Error message. pub message: String, /// Span of the bad arguments. pub span: pest::Span<'i>,} /// Expression item that can be transformed recursively by using `folder: F`.pub trait FoldableExpression<'i>: Sized { /// Transforms `self` by applying the `folder` to inner items. fn fold<F>(self, folder: &mut F, span: pest::Span<'i>) -> Result<Self, F::Error> where F: ExpressionFolder<'i, Self> + ?Sized;} /// Visitor-like interface to transform AST nodes recursively.pub trait ExpressionFolder<'i, T: FoldableExpression<'i>> { /// Transform error. type Error; /// Transforms the expression `node`. By default, inner items are /// transformed recursively. fn fold_expression( &mut self, node: ExpressionNode<'i, T>, ) -> Result<ExpressionNode<'i, T>, Self::Error> { let ExpressionNode { kind, span } = node; let kind = kind.fold(self, span)?; Ok(ExpressionNode { kind, span }) } /// Transforms identifier. fn fold_identifier(&mut self, name: &'i str, span: pest::Span<'i>) -> Result<T, Self::Error>; /// Transforms pattern. fn fold_pattern( &mut self, pattern: Box<PatternNode<'i, T>>, span: pest::Span<'i>, ) -> Result<T, Self::Error>; /// Transforms function call. fn fold_function_call( &mut self, function: Box<FunctionCallNode<'i, T>>, span: pest::Span<'i>, ) -> Result<T, Self::Error>;} /// Transforms list of `nodes` by using `folder`.pub fn fold_expression_nodes<'i, F, T>( folder: &mut F, nodes: Vec<ExpressionNode<'i, T>>,) -> Result<Vec<ExpressionNode<'i, T>>, F::Error>where F: ExpressionFolder<'i, T> + ?Sized, T: FoldableExpression<'i>,{ nodes .into_iter() .map(|node| folder.fold_expression(node)) .try_collect()} /// Transforms pattern value by using `folder`.pub fn fold_pattern_value<'i, F, T>( folder: &mut F, pattern: PatternNode<'i, T>,) -> Result<PatternNode<'i, T>, F::Error>where F: ExpressionFolder<'i, T> + ?Sized, T: FoldableExpression<'i>,{ Ok(PatternNode { name: pattern.name, name_span: pattern.name_span, value: folder.fold_expression(pattern.value)?, })} /// Transforms function call arguments by using `folder`.pub fn fold_function_call_args<'i, F, T>( folder: &mut F, function: FunctionCallNode<'i, T>,) -> Result<FunctionCallNode<'i, T>, F::Error>where F: ExpressionFolder<'i, T> + ?Sized, T: FoldableExpression<'i>,{ Ok(FunctionCallNode { name: function.name, name_span: function.name_span, args: fold_expression_nodes(folder, function.args)?, keyword_args: function .keyword_args .into_iter() .map(|arg| { Ok(KeywordArgument { name: arg.name, name_span: arg.name_span, value: folder.fold_expression(arg.value)?, }) }) .try_collect()?, args_span: function.args_span, })} /// Helper to parse string literal.#[derive(Debug)]pub struct StringLiteralParser<R> { /// String content part. pub content_rule: R, /// Escape sequence part including backslash character. pub escape_rule: R,} impl<R: RuleType> StringLiteralParser<R> { /// Parses the given string literal `pairs` into string. pub fn parse(&self, pairs: Pairs<R>) -> String { let mut result = String::new(); for part in pairs { if part.as_rule() == self.content_rule { result.push_str(part.as_str()); } else if part.as_rule() == self.escape_rule { match &part.as_str()[1..] { "\"" => result.push('"'), "\\" => result.push('\\'), "t" => result.push('\t'), "r" => result.push('\r'), "n" => result.push('\n'), "0" => result.push('\0'), "e" => result.push('\x1b'), hex if hex.starts_with('x') => { result.push(char::from( u8::from_str_radix(&hex[1..], 16).expect("hex characters"), )); } char => panic!("invalid escape: \\{char:?}"), } } else { panic!("unexpected part of string: {part:?}"); } } result }} /// Escape special characters in the inputpub fn escape_string(unescaped: &str) -> String { let mut escaped = String::with_capacity(unescaped.len()); for c in unescaped.chars() { match c { '"' => escaped.push_str(r#"\""#), '\\' => escaped.push_str(r#"\\"#), '\t' => escaped.push_str(r#"\t"#), '\r' => escaped.push_str(r#"\r"#), '\n' => escaped.push_str(r#"\n"#), '\0' => escaped.push_str(r#"\0"#), c if c.is_ascii_control() => { for b in ascii::escape_default(c as u8) { escaped.push(b as char); } } c => escaped.push(c), } } escaped} /// Helper to parse function call.#[derive(Debug)]pub struct FunctionCallParser<R> { /// Function name. pub function_name_rule: R, /// List of positional and keyword arguments.Lines 1–500 of 1059. Load more
// Copyright 2020-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. //! Domain-specific language helpers. use std::ascii; use std::collections::HashMap; use std::fmt; use std::slice; use itertools::Itertools as _; use pest::RuleType; use pest::iterators::Pair; use pest::iterators::Pairs; /// Manages diagnostic messages emitted during parsing. /// /// `T` is usually a parse error type of the language, which contains a message /// and source span of 'static lifetime. #[derive(Debug)] pub struct Diagnostics<T> { // This might be extended to [{ kind: Warning|Error, message: T }, ..]. diagnostics: Vec<T>, } impl<T> Diagnostics<T> { /// Creates new empty diagnostics collector. pub fn new() -> Self { Self { diagnostics: Vec::new(), } } /// Returns `true` if there are no diagnostic messages. pub fn is_empty(&self) -> bool { self.diagnostics.is_empty() } /// Returns the number of diagnostic messages. pub fn len(&self) -> usize { self.diagnostics.len() } /// Returns iterator over diagnostic messages. pub fn iter(&self) -> slice::Iter<'_, T> { self.diagnostics.iter() } /// Adds a diagnostic message of warning level. pub fn add_warning(&mut self, diag: T) { self.diagnostics.push(diag); } /// Moves diagnostic messages of different type (such as fileset warnings /// emitted within `file()` revset.) pub fn extend_with<U>(&mut self, diagnostics: Diagnostics<U>, mut f: impl FnMut(U) -> T) { self.diagnostics .extend(diagnostics.diagnostics.into_iter().map(&mut f)); } } impl<T> Default for Diagnostics<T> { fn default() -> Self { Self::new() } } impl<'a, T> IntoIterator for &'a Diagnostics<T> { type Item = &'a T; type IntoIter = slice::Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { self.iter() } } /// AST node without type or name checking. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExpressionNode<'i, T> { /// Expression item such as identifier, literal, function call, etc. pub kind: T, /// Span of the node. pub span: pest::Span<'i>, } impl<'i, T> ExpressionNode<'i, T> { /// Wraps the given expression and span. pub fn new(kind: T, span: pest::Span<'i>) -> Self { Self { kind, span } } } /// `<name>:<value>` expression in AST. #[derive(Clone, Debug, Eq, PartialEq)] pub struct PatternNode<'i, T> { /// Pattern name or type (such as `glob`.) pub name: &'i str, /// Span of the pattern name. pub name_span: pest::Span<'i>, /// Value expression. pub value: ExpressionNode<'i, T>, } /// Function call in AST. #[derive(Clone, Debug, Eq, PartialEq)] pub struct FunctionCallNode<'i, T> { /// Function name. pub name: &'i str, /// Span of the function name. pub name_span: pest::Span<'i>, /// List of positional arguments. pub args: Vec<ExpressionNode<'i, T>>, /// List of keyword arguments. pub keyword_args: Vec<KeywordArgument<'i, T>>, /// Span of the arguments list. pub args_span: pest::Span<'i>, } /// Keyword argument pair in AST. #[derive(Clone, Debug, Eq, PartialEq)] pub struct KeywordArgument<'i, T> { /// Parameter name. pub name: &'i str, /// Span of the parameter name. pub name_span: pest::Span<'i>, /// Value expression. pub value: ExpressionNode<'i, T>, } impl<'i, T> FunctionCallNode<'i, T> { /// Number of arguments assuming named arguments are all unique. pub fn arity(&self) -> usize { self.args.len() + self.keyword_args.len() } /// Ensures that no arguments passed. pub fn expect_no_arguments(&self) -> Result<(), InvalidArguments<'i>> { let ([], []) = self.expect_arguments()?; Ok(()) } /// Extracts exactly N required arguments. pub fn expect_exact_arguments<const N: usize>( &self, ) -> Result<&[ExpressionNode<'i, T>; N], InvalidArguments<'i>> { let (args, []) = self.expect_arguments()?; Ok(args) } /// Extracts N required arguments and remainders. /// /// This can be used to get all the positional arguments without requiring /// any (N = 0): /// ```ignore /// let ([], content_nodes) = function.expect_some_arguments()?; /// ``` /// Avoid accessing `function.args` directly, as that may allow keyword /// arguments to be silently ignored. #[expect(clippy::type_complexity)] pub fn expect_some_arguments<const N: usize>( &self, ) -> Result<(&[ExpressionNode<'i, T>; N], &[ExpressionNode<'i, T>]), InvalidArguments<'i>> { self.ensure_no_keyword_arguments()?; if self.args.len() >= N { let (required, rest) = self.args.split_at(N); Ok((required.try_into().unwrap(), rest)) } else { Err(self.invalid_arguments_count(N, None)) } } /// Extracts N required arguments and M optional arguments. #[expect(clippy::type_complexity)] pub fn expect_arguments<const N: usize, const M: usize>( &self, ) -> Result< ( &[ExpressionNode<'i, T>; N], [Option<&ExpressionNode<'i, T>>; M], ), InvalidArguments<'i>, > { self.ensure_no_keyword_arguments()?; let count_range = N..=(N + M); if count_range.contains(&self.args.len()) { let (required, rest) = self.args.split_at(N); let mut optional = rest.iter().map(Some).collect_vec(); optional.resize(M, None); Ok(( required.try_into().unwrap(), optional.try_into().ok().unwrap(), )) } else { let (min, max) = count_range.into_inner(); Err(self.invalid_arguments_count(min, Some(max))) } } /// Extracts N required arguments and M optional arguments. Some of them can /// be specified as keyword arguments. /// /// `names` is a list of parameter names. Unnamed positional arguments /// should be padded with `""`. #[expect(clippy::type_complexity)] pub fn expect_named_arguments<const N: usize, const M: usize>( &self, names: &[&str], ) -> Result< ( [&ExpressionNode<'i, T>; N], [Option<&ExpressionNode<'i, T>>; M], ), InvalidArguments<'i>, > { if self.keyword_args.is_empty() { let (required, optional) = self.expect_arguments::<N, M>()?; Ok((required.each_ref(), optional)) } else { let (required, optional) = self.expect_named_arguments_vec(names, N, N + M)?; Ok(( required.try_into().ok().unwrap(), optional.try_into().ok().unwrap(), )) } } #[expect(clippy::type_complexity)] fn expect_named_arguments_vec( &self, names: &[&str], min: usize, max: usize, ) -> Result< ( Vec<&ExpressionNode<'i, T>>, Vec<Option<&ExpressionNode<'i, T>>>, ), InvalidArguments<'i>, > { assert!(names.len() <= max); if self.args.len() > max { return Err(self.invalid_arguments_count(min, Some(max))); } let mut extracted = Vec::with_capacity(max); extracted.extend(self.args.iter().map(Some)); extracted.resize(max, None); for arg in &self.keyword_args { let name = arg.name; let span = arg.name_span.start_pos().span(&arg.value.span.end_pos()); let pos = names.iter().position(|&n| n == name).ok_or_else(|| { self.invalid_arguments(format!(r#"Unexpected keyword argument "{name}""#), span) })?; if extracted[pos].is_some() { return Err(self.invalid_arguments( format!(r#"Got multiple values for keyword "{name}""#), span, )); } extracted[pos] = Some(&arg.value); } let optional = extracted.split_off(min); let required = extracted.into_iter().flatten().collect_vec(); if required.len() != min { return Err(self.invalid_arguments_count(min, Some(max))); } Ok((required, optional)) } fn ensure_no_keyword_arguments(&self) -> Result<(), InvalidArguments<'i>> { if let (Some(first), Some(last)) = (self.keyword_args.first(), self.keyword_args.last()) { let span = first.name_span.start_pos().span(&last.value.span.end_pos()); Err(self.invalid_arguments("Unexpected keyword arguments".to_owned(), span)) } else { Ok(()) } } fn invalid_arguments(&self, message: String, span: pest::Span<'i>) -> InvalidArguments<'i> { InvalidArguments { name: self.name, message, span, } } fn invalid_arguments_count(&self, min: usize, max: Option<usize>) -> InvalidArguments<'i> { let message = match (min, max) { (min, Some(max)) if min == max => format!("Expected {min} arguments"), (min, Some(max)) => format!("Expected {min} to {max} arguments"), (min, None) => format!("Expected at least {min} arguments"), }; self.invalid_arguments(message, self.args_span) } fn invalid_arguments_count_with_arities( &self, arities: impl IntoIterator<Item = usize>, ) -> InvalidArguments<'i> { let message = format!("Expected {} arguments", arities.into_iter().join(", ")); self.invalid_arguments(message, self.args_span) } } /// Unexpected number of arguments, or invalid combination of arguments. /// /// This error is supposed to be converted to language-specific parse error /// type, where lifetime `'i` will be eliminated. #[derive(Clone, Debug)] pub struct InvalidArguments<'i> { /// Function name. pub name: &'i str, /// Error message. pub message: String, /// Span of the bad arguments. pub span: pest::Span<'i>, } /// Expression item that can be transformed recursively by using `folder: F`. pub trait FoldableExpression<'i>: Sized { /// Transforms `self` by applying the `folder` to inner items. fn fold<F>(self, folder: &mut F, span: pest::Span<'i>) -> Result<Self, F::Error> where F: ExpressionFolder<'i, Self> + ?Sized; } /// Visitor-like interface to transform AST nodes recursively. pub trait ExpressionFolder<'i, T: FoldableExpression<'i>> { /// Transform error. type Error; /// Transforms the expression `node`. By default, inner items are /// transformed recursively. fn fold_expression( &mut self, node: ExpressionNode<'i, T>, ) -> Result<ExpressionNode<'i, T>, Self::Error> { let ExpressionNode { kind, span } = node; let kind = kind.fold(self, span)?; Ok(ExpressionNode { kind, span }) } /// Transforms identifier. fn fold_identifier(&mut self, name: &'i str, span: pest::Span<'i>) -> Result<T, Self::Error>; /// Transforms pattern. fn fold_pattern( &mut self, pattern: Box<PatternNode<'i, T>>, span: pest::Span<'i>, ) -> Result<T, Self::Error>; /// Transforms function call. fn fold_function_call( &mut self, function: Box<FunctionCallNode<'i, T>>, span: pest::Span<'i>, ) -> Result<T, Self::Error>; } /// Transforms list of `nodes` by using `folder`. pub fn fold_expression_nodes<'i, F, T>( folder: &mut F, nodes: Vec<ExpressionNode<'i, T>>, ) -> Result<Vec<ExpressionNode<'i, T>>, F::Error> where F: ExpressionFolder<'i, T> + ?Sized, T: FoldableExpression<'i>, { nodes .into_iter() .map(|node| folder.fold_expression(node)) .try_collect() } /// Transforms pattern value by using `folder`. pub fn fold_pattern_value<'i, F, T>( folder: &mut F, pattern: PatternNode<'i, T>, ) -> Result<PatternNode<'i, T>, F::Error> where F: ExpressionFolder<'i, T> + ?Sized, T: FoldableExpression<'i>, { Ok(PatternNode { name: pattern.name, name_span: pattern.name_span, value: folder.fold_expression(pattern.value)?, }) } /// Transforms function call arguments by using `folder`. pub fn fold_function_call_args<'i, F, T>( folder: &mut F, function: FunctionCallNode<'i, T>, ) -> Result<FunctionCallNode<'i, T>, F::Error> where F: ExpressionFolder<'i, T> + ?Sized, T: FoldableExpression<'i>, { Ok(FunctionCallNode { name: function.name, name_span: function.name_span, args: fold_expression_nodes(folder, function.args)?, keyword_args: function .keyword_args .into_iter() .map(|arg| { Ok(KeywordArgument { name: arg.name, name_span: arg.name_span, value: folder.fold_expression(arg.value)?, }) }) .try_collect()?, args_span: function.args_span, }) } /// Helper to parse string literal. #[derive(Debug)] pub struct StringLiteralParser<R> { /// String content part. pub content_rule: R, /// Escape sequence part including backslash character. pub escape_rule: R, } impl<R: RuleType> StringLiteralParser<R> { /// Parses the given string literal `pairs` into string. pub fn parse(&self, pairs: Pairs<R>) -> String { let mut result = String::new(); for part in pairs { if part.as_rule() == self.content_rule { result.push_str(part.as_str()); } else if part.as_rule() == self.escape_rule { match &part.as_str()[1..] { "\"" => result.push('"'), "\\" => result.push('\\'), "t" => result.push('\t'), "r" => result.push('\r'), "n" => result.push('\n'), "0" => result.push('\0'), "e" => result.push('\x1b'), hex if hex.starts_with('x') => { result.push(char::from( u8::from_str_radix(&hex[1..], 16).expect("hex characters"), )); } char => panic!("invalid escape: \\{char:?}"), } } else { panic!("unexpected part of string: {part:?}"); } } result } } /// Escape special characters in the input pub fn escape_string(unescaped: &str) -> String { let mut escaped = String::with_capacity(unescaped.len()); for c in unescaped.chars() { match c { '"' => escaped.push_str(r#"\""#), '\\' => escaped.push_str(r#"\\"#), '\t' => escaped.push_str(r#"\t"#), '\r' => escaped.push_str(r#"\r"#), '\n' => escaped.push_str(r#"\n"#), '\0' => escaped.push_str(r#"\0"#), c if c.is_ascii_control() => { for b in ascii::escape_default(c as u8) { escaped.push(b as char); } } c => escaped.push(c), } } escaped } /// Helper to parse function call. #[derive(Debug)] pub struct FunctionCallParser<R> { /// Function name. pub function_name_rule: R, /// List of positional and keyword arguments.