Skip to content

Commit a94c996

Browse files
committed
remake else if as repeat($.else_clause)
Before, it looked like this: ``` if ... { } else if let ... = ... { } else if ... { } else { } (if_expression condition: ... consequence: (block) alternative: (else_clause (if_expression condition: (let_condition pattern: ... value: ...) consequence: (block) alternative: (else_clause (if_expression condition: ... consequence: (block) alternative: (else_clause (block))))))) ``` After, it looks like this: ``` if ... { } else if let ... = ... { } else if ... { } else { } (if_expression condition: ... consequence: (block) (else_clause condition: (let_condition pattern: ... value: ...) consequence: (block)) (else_clause condition: ... consequence: (block)) (else_clause (block))) ``` Previously, the "else" and "if" were not adjacent tokens, and therefore could not be grouped together in an `("else" "if") @match` query. The main motivation here is highlighting each if/else if/else branch with vim-matchup. It was also difficult to query only a complete if/else expression, and exclude if_expressions that were simply the "if" in an "else if". It is maybe wrong to say that the latter is actually an expression, but moreover if you wanted to query only the former, you would have to either list all the contexts where an if_expression can occur (except else_clause) or use an #if-not? to exclude the `(else_clause (if_expression) @not_this)`. Again, the motivation is attempting to navigate between if/else branches in the editor using vim matchup, which requires matching one single `(if_expression) @scope.if` to link all the branches to, and not creating a bunch of smaller scopes on all the if_expressions contained within. The resulting tree is flatter. There is no need for the alternative: field name as (else_clause) only appears in the tail of an (if_expression) and never at the top level in the condition/consequence, hence writing (else_clause) in a query unambiguously selects nodes at the tail. And the previous two problems are solved: - `(else_clause "else" "if")` can match now, and it will not match a final `else {}` clause. Previously it was an impossible pattern. - `(if_expression)` will only match once in a chain of `if {} else if {} ...`s, with the match over the entire expression. No need for hacky tricks to avoid matches on inner `if_expression`s.
1 parent 3275f51 commit a94c996

File tree

5 files changed

+65969
-64747
lines changed

5 files changed

+65969
-64747
lines changed

corpus/expressions.txt

+60-38
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,10 @@ If expressions
481481
================================================================================
482482

483483
fn main() {
484+
if n == 2 {
485+
}
484486
if n == 1 {
487+
} else if let Some(k) = z {
485488
} else if n == 2 {
486489
} else {
487490
}
@@ -502,41 +505,36 @@ if foo && bar || baz {}
502505
body: (block
503506
(expression_statement
504507
(if_expression
505-
condition: (binary_expression
506-
left: (identifier)
507-
right: (integer_literal))
508+
condition: (binary_expression left: (identifier) right: (integer_literal))
509+
consequence: (block)))
510+
(expression_statement
511+
(if_expression
512+
condition: (binary_expression left: (identifier) right: (integer_literal))
508513
consequence: (block)
509-
alternative: (else_clause
510-
(if_expression
511-
condition: (binary_expression
512-
left: (identifier)
513-
right: (integer_literal))
514-
consequence: (block)
515-
alternative: (else_clause
516-
(block))))))))
514+
(else_clause
515+
condition: (let_condition
516+
pattern: (tuple_struct_pattern type: (identifier) (identifier))
517+
value: (identifier))
518+
consequence: (block))
519+
(else_clause
520+
condition: (binary_expression left: (identifier) right: (integer_literal))
521+
consequence: (block))
522+
(else_clause
523+
(block))))))
517524
(let_declaration
518525
pattern: (identifier)
519526
value: (if_expression
520-
condition: (binary_expression
521-
left: (identifier)
522-
right: (integer_literal))
523-
consequence: (block
524-
(integer_literal))
525-
alternative: (else_clause
526-
(block
527-
(integer_literal)))))
527+
condition: (binary_expression left: (identifier) right: (integer_literal))
528+
consequence: (block (integer_literal))
529+
(else_clause (block (integer_literal)))))
528530
(expression_statement
529531
(if_expression
530-
condition: (binary_expression
531-
left: (identifier)
532-
right: (identifier))
532+
condition: (binary_expression left: (identifier) right: (identifier))
533533
consequence: (block)))
534534
(expression_statement
535535
(if_expression
536536
condition: (binary_expression
537-
left: (binary_expression
538-
left: (identifier)
539-
right: (identifier))
537+
left: (binary_expression left: (identifier) right: (identifier))
540538
right: (identifier))
541539
consequence: (block))))
542540

@@ -558,23 +556,52 @@ if let Some(a) = b
558556
(if_expression
559557
condition: (let_chain
560558
(let_condition
561-
pattern: (tuple_struct_pattern
562-
type: (identifier)
563-
(identifier))
559+
pattern: (tuple_struct_pattern type: (identifier) (identifier))
564560
value: (identifier))
565561
(identifier)
566562
(identifier)
567563
(let_condition
568-
pattern: (tuple_struct_pattern
569-
type: (identifier)
570-
(identifier))
564+
pattern: (tuple_struct_pattern type: (identifier) (identifier))
571565
value: (identifier)))
572566
consequence: (block))))
573567

574568
================================================================================
575569
If let expressions
576570
================================================================================
577571

572+
let x = if let Some(a) = dish {
573+
a
574+
} else if let None = dish {
575+
99
576+
} else if n == 8 {
577+
9
578+
} else {
579+
7
580+
};
581+
582+
--------------------------------------------------------------------------------
583+
584+
(source_file
585+
(let_declaration
586+
pattern: (identifier)
587+
value: (if_expression
588+
condition: (let_condition
589+
pattern: (tuple_struct_pattern type: (identifier) (identifier))
590+
value: (identifier))
591+
consequence: (block (identifier))
592+
(else_clause
593+
condition: (let_condition pattern: (identifier) value: (identifier))
594+
consequence: (block (integer_literal)))
595+
(else_clause
596+
condition: (binary_expression left: (identifier) right: (integer_literal))
597+
consequence: (block (integer_literal)))
598+
(else_clause
599+
(block (integer_literal))))))
600+
601+
================================================================================
602+
If let chains
603+
================================================================================
604+
578605
if let ("Bacon", b) = dish {
579606
}
580607

@@ -1270,10 +1297,7 @@ let three_ranges = [const { (0..=5).into_inner() }; 3];
12701297
(empty_statement)
12711298
(expression_statement
12721299
(if_expression
1273-
condition: (binary_expression
1274-
left: (unary_expression
1275-
(identifier))
1276-
right: (integer_literal))
1300+
condition: (binary_expression left: (unary_expression (identifier)) right: (integer_literal))
12771301
consequence: (block
12781302
(expression_statement
12791303
(const_block
@@ -1285,9 +1309,7 @@ let three_ranges = [const { (0..=5).into_inner() }; 3];
12851309
field: (field_identifier))
12861310
arguments: (arguments
12871311
(integer_literal))))))))
1288-
alternative: (else_clause
1289-
(block
1290-
(identifier)))))
1312+
(else_clause (block (identifier)))))
12911313
(let_declaration
12921314
pattern: (identifier)
12931315
value: (array_expression

grammar.js

+21-15
Original file line numberDiff line numberDiff line change
@@ -1096,9 +1096,29 @@ module.exports = grammar({
10961096
'if',
10971097
field('condition', $._condition),
10981098
field('consequence', $.block),
1099-
optional(field("alternative", $.else_clause))
1099+
repeat($.else_clause),
11001100
)),
11011101

1102+
_else_if: $ => prec.right(seq(
1103+
'if',
1104+
field('condition', $._condition),
1105+
field('consequence', $.block),
1106+
)),
1107+
1108+
else_clause: $ => seq(
1109+
'else',
1110+
choice(
1111+
$.block,
1112+
$._else_if
1113+
)
1114+
),
1115+
1116+
_condition: $ => choice(
1117+
$._expression,
1118+
$.let_condition,
1119+
alias($._let_chain, $.let_chain),
1120+
),
1121+
11021122
let_condition: $ => seq(
11031123
'let',
11041124
field('pattern', $._pattern),
@@ -1114,20 +1134,6 @@ module.exports = grammar({
11141134
seq($._expression, '&&', $.let_condition),
11151135
)),
11161136

1117-
_condition: $ => choice(
1118-
$._expression,
1119-
$.let_condition,
1120-
alias($._let_chain, $.let_chain),
1121-
),
1122-
1123-
else_clause: $ => seq(
1124-
'else',
1125-
choice(
1126-
$.block,
1127-
$.if_expression
1128-
)
1129-
),
1130-
11311137
match_expression: $ => seq(
11321138
'match',
11331139
field('value', $._expression),

src/grammar.json

+78-58
Original file line numberDiff line numberDiff line change
@@ -6337,24 +6337,88 @@
63376337
}
63386338
},
63396339
{
6340-
"type": "CHOICE",
6341-
"members": [
6342-
{
6343-
"type": "FIELD",
6344-
"name": "alternative",
6345-
"content": {
6346-
"type": "SYMBOL",
6347-
"name": "else_clause"
6348-
}
6349-
},
6350-
{
6351-
"type": "BLANK"
6352-
}
6353-
]
6340+
"type": "REPEAT",
6341+
"content": {
6342+
"type": "SYMBOL",
6343+
"name": "else_clause"
6344+
}
63546345
}
63556346
]
63566347
}
63576348
},
6349+
"_else_if": {
6350+
"type": "PREC_RIGHT",
6351+
"value": 0,
6352+
"content": {
6353+
"type": "SEQ",
6354+
"members": [
6355+
{
6356+
"type": "STRING",
6357+
"value": "if"
6358+
},
6359+
{
6360+
"type": "FIELD",
6361+
"name": "condition",
6362+
"content": {
6363+
"type": "SYMBOL",
6364+
"name": "_condition"
6365+
}
6366+
},
6367+
{
6368+
"type": "FIELD",
6369+
"name": "consequence",
6370+
"content": {
6371+
"type": "SYMBOL",
6372+
"name": "block"
6373+
}
6374+
}
6375+
]
6376+
}
6377+
},
6378+
"else_clause": {
6379+
"type": "SEQ",
6380+
"members": [
6381+
{
6382+
"type": "STRING",
6383+
"value": "else"
6384+
},
6385+
{
6386+
"type": "CHOICE",
6387+
"members": [
6388+
{
6389+
"type": "SYMBOL",
6390+
"name": "block"
6391+
},
6392+
{
6393+
"type": "SYMBOL",
6394+
"name": "_else_if"
6395+
}
6396+
]
6397+
}
6398+
]
6399+
},
6400+
"_condition": {
6401+
"type": "CHOICE",
6402+
"members": [
6403+
{
6404+
"type": "SYMBOL",
6405+
"name": "_expression"
6406+
},
6407+
{
6408+
"type": "SYMBOL",
6409+
"name": "let_condition"
6410+
},
6411+
{
6412+
"type": "ALIAS",
6413+
"content": {
6414+
"type": "SYMBOL",
6415+
"name": "_let_chain"
6416+
},
6417+
"named": true,
6418+
"value": "let_chain"
6419+
}
6420+
]
6421+
},
63586422
"let_condition": {
63596423
"type": "SEQ",
63606424
"members": [
@@ -6482,50 +6546,6 @@
64826546
]
64836547
}
64846548
},
6485-
"_condition": {
6486-
"type": "CHOICE",
6487-
"members": [
6488-
{
6489-
"type": "SYMBOL",
6490-
"name": "_expression"
6491-
},
6492-
{
6493-
"type": "SYMBOL",
6494-
"name": "let_condition"
6495-
},
6496-
{
6497-
"type": "ALIAS",
6498-
"content": {
6499-
"type": "SYMBOL",
6500-
"name": "_let_chain"
6501-
},
6502-
"named": true,
6503-
"value": "let_chain"
6504-
}
6505-
]
6506-
},
6507-
"else_clause": {
6508-
"type": "SEQ",
6509-
"members": [
6510-
{
6511-
"type": "STRING",
6512-
"value": "else"
6513-
},
6514-
{
6515-
"type": "CHOICE",
6516-
"members": [
6517-
{
6518-
"type": "SYMBOL",
6519-
"name": "block"
6520-
},
6521-
{
6522-
"type": "SYMBOL",
6523-
"name": "if_expression"
6524-
}
6525-
]
6526-
}
6527-
]
6528-
},
65296549
"match_expression": {
65306550
"type": "SEQ",
65316551
"members": [

0 commit comments

Comments
 (0)