@@ -33,6 +33,7 @@ use jj_lib::dsl_util::FunctionCallParser;
33
33
use jj_lib:: dsl_util:: InvalidArguments ;
34
34
use jj_lib:: dsl_util:: StringLiteralParser ;
35
35
use jj_lib:: dsl_util:: collect_similar;
36
+ use jj_lib:: str_util:: StringPattern ;
36
37
use pest:: Parser as _;
37
38
use pest:: iterators:: Pair ;
38
39
use pest:: iterators:: Pairs ;
@@ -69,8 +70,11 @@ impl Rule {
69
70
Self :: string_literal => None ,
70
71
Self :: raw_string_content => None ,
71
72
Self :: raw_string_literal => None ,
73
+ Self :: any_string_literal => None ,
74
+ Self :: string_pattern => None ,
72
75
Self :: integer_literal => None ,
73
76
Self :: identifier => None ,
77
+ Self :: string_pattern_identifier => None ,
74
78
Self :: concat_op => Some ( "++" ) ,
75
79
Self :: logical_or_op => Some ( "||" ) ,
76
80
Self :: logical_and_op => Some ( "&&" ) ,
@@ -87,6 +91,7 @@ impl Rule {
87
91
Self :: rem_op => Some ( "%" ) ,
88
92
Self :: logical_not_op => Some ( "!" ) ,
89
93
Self :: negate_op => Some ( "-" ) ,
94
+ Self :: pattern_kind_op => Some ( ":" ) ,
90
95
Self :: prefix_ops => None ,
91
96
Self :: infix_ops => None ,
92
97
Self :: function => None ,
@@ -285,6 +290,11 @@ pub enum ExpressionKind<'i> {
285
290
Boolean ( bool ) ,
286
291
Integer ( i64 ) ,
287
292
String ( String ) ,
293
+ /// `<kind>:"<value>"`
294
+ StringPattern {
295
+ kind : & ' i str ,
296
+ value : String ,
297
+ } ,
288
298
Unary ( UnaryOp , Box < ExpressionNode < ' i > > ) ,
289
299
Binary ( BinaryOp , Box < ExpressionNode < ' i > > , Box < ExpressionNode < ' i > > ) ,
290
300
Concat ( Vec < ExpressionNode < ' i > > ) ,
@@ -302,7 +312,10 @@ impl<'i> FoldableExpression<'i> for ExpressionKind<'i> {
302
312
{
303
313
match self {
304
314
Self :: Identifier ( name) => folder. fold_identifier ( name, span) ,
305
- Self :: Boolean ( _) | Self :: Integer ( _) | Self :: String ( _) => Ok ( self ) ,
315
+ ExpressionKind :: Boolean ( _)
316
+ | ExpressionKind :: Integer ( _)
317
+ | ExpressionKind :: String ( _)
318
+ | ExpressionKind :: StringPattern { .. } => Ok ( self ) ,
306
319
Self :: Unary ( op, arg) => {
307
320
let arg = Box :: new ( folder. fold_expression ( * arg) ?) ;
308
321
Ok ( Self :: Unary ( op, arg) )
@@ -458,6 +471,12 @@ fn parse_lambda_node(pair: Pair<Rule>) -> TemplateParseResult<LambdaNode> {
458
471
} )
459
472
}
460
473
474
+ fn parse_raw_string_literal ( pair : Pair < Rule > ) -> String {
475
+ let [ content] = pair. into_inner ( ) . collect_array ( ) . unwrap ( ) ;
476
+ assert_eq ! ( content. as_rule( ) , Rule :: raw_string_content) ;
477
+ content. as_str ( ) . to_owned ( )
478
+ }
479
+
461
480
fn parse_term_node ( pair : Pair < Rule > ) -> TemplateParseResult < ExpressionNode > {
462
481
assert_eq ! ( pair. as_rule( ) , Rule :: term) ;
463
482
let mut inner = pair. into_inner ( ) ;
@@ -469,9 +488,7 @@ fn parse_term_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode> {
469
488
ExpressionNode :: new ( ExpressionKind :: String ( text) , span)
470
489
}
471
490
Rule :: raw_string_literal => {
472
- let [ content] = expr. into_inner ( ) . collect_array ( ) . unwrap ( ) ;
473
- assert_eq ! ( content. as_rule( ) , Rule :: raw_string_content) ;
474
- let text = content. as_str ( ) . to_owned ( ) ;
491
+ let text = parse_raw_string_literal ( expr) ;
475
492
ExpressionNode :: new ( ExpressionKind :: String ( text) , span)
476
493
}
477
494
Rule :: integer_literal => {
@@ -480,6 +497,21 @@ fn parse_term_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode> {
480
497
} ) ?;
481
498
ExpressionNode :: new ( ExpressionKind :: Integer ( value) , span)
482
499
}
500
+ Rule :: string_pattern => {
501
+ let [ kind, op, literal] = expr. into_inner ( ) . collect_array ( ) . unwrap ( ) ;
502
+ assert_eq ! ( kind. as_rule( ) , Rule :: string_pattern_identifier) ;
503
+ assert_eq ! ( op. as_rule( ) , Rule :: pattern_kind_op) ;
504
+ let kind = kind. as_str ( ) ;
505
+ let text = match literal. as_rule ( ) {
506
+ Rule :: string_literal => STRING_LITERAL_PARSER . parse ( literal. into_inner ( ) ) ,
507
+ Rule :: raw_string_literal => parse_raw_string_literal ( literal) ,
508
+ other => {
509
+ panic ! ( "Unexpected literal rule in string pattern: {other:?}" )
510
+ }
511
+ } ;
512
+ // The actual parsing and construction of the pattern is deferred to later.
513
+ ExpressionNode :: new ( ExpressionKind :: StringPattern { kind, value : text } , span)
514
+ }
483
515
Rule :: identifier => ExpressionNode :: new ( parse_identifier_or_literal ( expr) , span) ,
484
516
Rule :: function => {
485
517
let function = Box :: new ( FUNCTION_CALL_PARSER . parse (
@@ -663,6 +695,23 @@ pub fn expect_string_literal<'a>(node: &'a ExpressionNode<'_>) -> TemplateParseR
663
695
} )
664
696
}
665
697
698
+ /// Unwraps inner value if the given `node` is a string pattern
699
+ ///
700
+ /// This forces it to be static so that it need not be part of the type system.
701
+ pub fn expect_string_pattern ( node : & ' _ ExpressionNode < ' _ > ) -> TemplateParseResult < StringPattern > {
702
+ catch_aliases_no_diagnostics ( node, |node| match & node. kind {
703
+ ExpressionKind :: StringPattern { kind, value } => StringPattern :: from_str_kind ( value, kind)
704
+ . map_err ( |err| {
705
+ TemplateParseError :: expression ( "Bad string pattern" , node. span ) . with_source ( err)
706
+ } ) ,
707
+ ExpressionKind :: String ( string) => Ok ( StringPattern :: Substring ( string. clone ( ) ) ) ,
708
+ _ => Err ( TemplateParseError :: expression (
709
+ "Expected string pattern" ,
710
+ node. span ,
711
+ ) ) ,
712
+ } )
713
+ }
714
+
666
715
/// Unwraps inner node if the given `node` is a lambda.
667
716
pub fn expect_lambda < ' a , ' i > (
668
717
node : & ' a ExpressionNode < ' i > ,
@@ -835,6 +884,7 @@ mod tests {
835
884
| ExpressionKind :: Boolean ( _)
836
885
| ExpressionKind :: Integer ( _)
837
886
| ExpressionKind :: String ( _) => node. kind ,
887
+ ExpressionKind :: StringPattern { .. } => node. kind ,
838
888
ExpressionKind :: Unary ( op, arg) => {
839
889
let arg = Box :: new ( normalize_tree ( * arg) ) ;
840
890
ExpressionKind :: Unary ( op, arg)
@@ -1139,6 +1189,51 @@ mod tests {
1139
1189
) ;
1140
1190
}
1141
1191
1192
+ #[ test]
1193
+ fn test_string_pattern ( ) {
1194
+ assert_eq ! (
1195
+ parse_into_kind( r#"regex:"meow""# ) ,
1196
+ Ok ( ExpressionKind :: StringPattern {
1197
+ kind: "regex" ,
1198
+ value: "meow" . to_owned( )
1199
+ } ) ,
1200
+ ) ;
1201
+ assert_eq ! (
1202
+ parse_into_kind( r#"regex:'\r\n'"# ) ,
1203
+ Ok ( ExpressionKind :: StringPattern {
1204
+ kind: "regex" ,
1205
+ value: r#"\r\n"# . to_owned( )
1206
+ } )
1207
+ ) ;
1208
+ assert_eq ! (
1209
+ parse_into_kind( r#"regex-i:'\r\n'"# ) ,
1210
+ Ok ( ExpressionKind :: StringPattern {
1211
+ kind: "regex-i" ,
1212
+ value: r#"\r\n"# . to_owned( )
1213
+ } )
1214
+ ) ;
1215
+ assert_eq ! (
1216
+ parse_into_kind( "regex:meow" ) ,
1217
+ Err ( TemplateParseErrorKind :: SyntaxError ) ,
1218
+ "no bare words in string patterns in templates"
1219
+ ) ;
1220
+ assert_eq ! (
1221
+ parse_into_kind( "regex: 'with spaces'" ) ,
1222
+ Err ( TemplateParseErrorKind :: SyntaxError ) ,
1223
+ "no spaces after"
1224
+ ) ;
1225
+ assert_eq ! (
1226
+ parse_into_kind( "regex :'with spaces'" ) ,
1227
+ Err ( TemplateParseErrorKind :: SyntaxError ) ,
1228
+ "no spaces before either"
1229
+ ) ;
1230
+ assert_eq ! (
1231
+ parse_into_kind( "regex : 'with spaces'" ) ,
1232
+ Err ( TemplateParseErrorKind :: SyntaxError ) ,
1233
+ "certainly not both"
1234
+ ) ;
1235
+ }
1236
+
1142
1237
#[ test]
1143
1238
fn test_integer_literal ( ) {
1144
1239
assert_eq ! ( parse_into_kind( "0" ) , Ok ( ExpressionKind :: Integer ( 0 ) ) ) ;
0 commit comments