mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-01 09:08:51 +00:00
revset: extract helper that parses function call
I'm going to add keyword arguments support to templater.
This commit is contained in:
parent
ca5d119c10
commit
2413fffcf9
2 changed files with 85 additions and 51 deletions
|
@ -20,6 +20,7 @@ use std::fmt;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
use itertools::Itertools as _;
|
use itertools::Itertools as _;
|
||||||
|
use pest::iterators::Pair;
|
||||||
use pest::iterators::Pairs;
|
use pest::iterators::Pairs;
|
||||||
use pest::RuleType;
|
use pest::RuleType;
|
||||||
|
|
||||||
|
@ -428,6 +429,76 @@ impl<R: RuleType> StringLiteralParser<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper to parse function call.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FunctionCallParser<R> {
|
||||||
|
/// Function name.
|
||||||
|
pub function_name_rule: R,
|
||||||
|
/// List of positional and keyword arguments.
|
||||||
|
pub function_arguments_rule: R,
|
||||||
|
/// Pair of parameter name and value.
|
||||||
|
pub keyword_argument_rule: R,
|
||||||
|
/// Parameter name.
|
||||||
|
pub argument_name_rule: R,
|
||||||
|
/// Value expression.
|
||||||
|
pub argument_value_rule: R,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: RuleType> FunctionCallParser<R> {
|
||||||
|
/// Parses the given `pair` as function call.
|
||||||
|
pub fn parse<'i, T, E: From<InvalidArguments<'i>>>(
|
||||||
|
&self,
|
||||||
|
pair: Pair<'i, R>,
|
||||||
|
// parse_name can be defined for any Pair<'_, R>, but parse_value should
|
||||||
|
// be allowed to construct T by capturing Pair<'i, R>.
|
||||||
|
parse_name: impl Fn(Pair<'i, R>) -> Result<&'i str, E>,
|
||||||
|
parse_value: impl Fn(Pair<'i, R>) -> Result<ExpressionNode<'i, T>, E>,
|
||||||
|
) -> Result<FunctionCallNode<'i, T>, E> {
|
||||||
|
let (name_pair, args_pair) = pair.into_inner().collect_tuple().unwrap();
|
||||||
|
assert_eq!(name_pair.as_rule(), self.function_name_rule);
|
||||||
|
assert_eq!(args_pair.as_rule(), self.function_arguments_rule);
|
||||||
|
let name_span = name_pair.as_span();
|
||||||
|
let args_span = args_pair.as_span();
|
||||||
|
let function_name = parse_name(name_pair)?;
|
||||||
|
let mut args = Vec::new();
|
||||||
|
let mut keyword_args = Vec::new();
|
||||||
|
for pair in args_pair.into_inner() {
|
||||||
|
let span = pair.as_span();
|
||||||
|
if pair.as_rule() == self.argument_value_rule {
|
||||||
|
if !keyword_args.is_empty() {
|
||||||
|
return Err(InvalidArguments {
|
||||||
|
name: function_name,
|
||||||
|
message: "Positional argument follows keyword argument".to_owned(),
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
args.push(parse_value(pair)?);
|
||||||
|
} else if pair.as_rule() == self.keyword_argument_rule {
|
||||||
|
let (name_pair, value_pair) = pair.into_inner().collect_tuple().unwrap();
|
||||||
|
assert_eq!(name_pair.as_rule(), self.argument_name_rule);
|
||||||
|
assert_eq!(value_pair.as_rule(), self.argument_value_rule);
|
||||||
|
let name_span = name_pair.as_span();
|
||||||
|
let arg = KeywordArgument {
|
||||||
|
name: parse_name(name_pair)?,
|
||||||
|
name_span,
|
||||||
|
value: parse_value(value_pair)?,
|
||||||
|
};
|
||||||
|
keyword_args.push(arg);
|
||||||
|
} else {
|
||||||
|
panic!("unexpected argument rule {pair:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(FunctionCallNode {
|
||||||
|
name: function_name,
|
||||||
|
name_span,
|
||||||
|
args,
|
||||||
|
keyword_args,
|
||||||
|
args_span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Map of symbol and function aliases.
|
/// Map of symbol and function aliases.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct AliasesMap<P, V> {
|
pub struct AliasesMap<P, V> {
|
||||||
|
|
|
@ -42,8 +42,8 @@ use crate::dsl_util::AliasesMap;
|
||||||
use crate::dsl_util::Diagnostics;
|
use crate::dsl_util::Diagnostics;
|
||||||
use crate::dsl_util::ExpressionFolder;
|
use crate::dsl_util::ExpressionFolder;
|
||||||
use crate::dsl_util::FoldableExpression;
|
use crate::dsl_util::FoldableExpression;
|
||||||
|
use crate::dsl_util::FunctionCallParser;
|
||||||
use crate::dsl_util::InvalidArguments;
|
use crate::dsl_util::InvalidArguments;
|
||||||
use crate::dsl_util::KeywordArgument;
|
|
||||||
use crate::dsl_util::StringLiteralParser;
|
use crate::dsl_util::StringLiteralParser;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
@ -54,6 +54,13 @@ const STRING_LITERAL_PARSER: StringLiteralParser<Rule> = StringLiteralParser {
|
||||||
content_rule: Rule::string_content,
|
content_rule: Rule::string_content,
|
||||||
escape_rule: Rule::string_escape,
|
escape_rule: Rule::string_escape,
|
||||||
};
|
};
|
||||||
|
const FUNCTION_CALL_PARSER: FunctionCallParser<Rule> = FunctionCallParser {
|
||||||
|
function_name_rule: Rule::function_name,
|
||||||
|
function_arguments_rule: Rule::function_arguments,
|
||||||
|
keyword_argument_rule: Rule::keyword_argument,
|
||||||
|
argument_name_rule: Rule::identifier,
|
||||||
|
argument_value_rule: Rule::expression,
|
||||||
|
};
|
||||||
|
|
||||||
impl Rule {
|
impl Rule {
|
||||||
/// Whether this is a placeholder rule for compatibility with the other
|
/// Whether this is a placeholder rule for compatibility with the other
|
||||||
|
@ -613,7 +620,11 @@ fn parse_primary_node(pair: Pair<Rule>) -> Result<ExpressionNode, RevsetParseErr
|
||||||
let expr = match first.as_rule() {
|
let expr = match first.as_rule() {
|
||||||
Rule::expression => return parse_expression_node(first.into_inner()),
|
Rule::expression => return parse_expression_node(first.into_inner()),
|
||||||
Rule::function => {
|
Rule::function => {
|
||||||
let function = Box::new(parse_function_call_node(first)?);
|
let function = Box::new(FUNCTION_CALL_PARSER.parse(
|
||||||
|
first,
|
||||||
|
|pair| Ok(pair.as_str()),
|
||||||
|
|pair| parse_expression_node(pair.into_inner()),
|
||||||
|
)?);
|
||||||
ExpressionKind::FunctionCall(function)
|
ExpressionKind::FunctionCall(function)
|
||||||
}
|
}
|
||||||
Rule::string_pattern => {
|
Rule::string_pattern => {
|
||||||
|
@ -668,55 +679,6 @@ fn parse_as_string_literal(pair: Pair<Rule>) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_function_call_node(pair: Pair<Rule>) -> Result<FunctionCallNode, RevsetParseError> {
|
|
||||||
assert_eq!(pair.as_rule(), Rule::function);
|
|
||||||
let (name_pair, args_pair) = pair.into_inner().collect_tuple().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 function_name = name_pair.as_str();
|
|
||||||
let mut args = Vec::new();
|
|
||||||
let mut keyword_args = Vec::new();
|
|
||||||
for pair in args_pair.into_inner() {
|
|
||||||
let span = pair.as_span();
|
|
||||||
match pair.as_rule() {
|
|
||||||
Rule::expression => {
|
|
||||||
if !keyword_args.is_empty() {
|
|
||||||
return Err(InvalidArguments {
|
|
||||||
name: function_name,
|
|
||||||
message: "Positional argument follows keyword argument".to_owned(),
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
args.push(parse_expression_node(pair.into_inner())?);
|
|
||||||
}
|
|
||||||
Rule::keyword_argument => {
|
|
||||||
let mut pairs = pair.into_inner();
|
|
||||||
let name = pairs.next().unwrap();
|
|
||||||
let expr = pairs.next().unwrap();
|
|
||||||
assert_eq!(name.as_rule(), Rule::identifier);
|
|
||||||
assert_eq!(expr.as_rule(), Rule::expression);
|
|
||||||
let arg = KeywordArgument {
|
|
||||||
name: name.as_str(),
|
|
||||||
name_span: name.as_span(),
|
|
||||||
value: parse_expression_node(expr.into_inner())?,
|
|
||||||
};
|
|
||||||
keyword_args.push(arg);
|
|
||||||
}
|
|
||||||
r => panic!("unexpected argument rule {r:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(FunctionCallNode {
|
|
||||||
name: function_name,
|
|
||||||
name_span,
|
|
||||||
args,
|
|
||||||
keyword_args,
|
|
||||||
args_span,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type RevsetAliasesMap = AliasesMap<RevsetAliasParser, String>;
|
pub type RevsetAliasesMap = AliasesMap<RevsetAliasParser, String>;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
|
@ -855,6 +817,7 @@ mod tests {
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::dsl_util::KeywordArgument;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct WithRevsetAliasesMap(RevsetAliasesMap);
|
struct WithRevsetAliasesMap(RevsetAliasesMap);
|
||||||
|
|
Loading…
Reference in a new issue