Skip to content

Commit

Permalink
Implement let_chains. (#152)
Browse files Browse the repository at this point in the history
* Implement let_chains.

Instead of a separate `if_let_expression` construct, we make
`if_expression` hold multiple `condition`s, each of which can be a
`let_condition`. This also applies to `while let` and `match` pattern
guards.

* Create a separate let_chain node for &&-chains involving let_conditions

* Un-name the children of `let_chain`

* Avoid unnecessary conflict between let_chain and condition

* Avoid conflict for binary expressions within if expressions

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
  • Loading branch information
goffrie and maxbrunsfeld committed Nov 3, 2022
1 parent a6e1a62 commit 6ebdb1d
Show file tree
Hide file tree
Showing 6 changed files with 63,177 additions and 65,943 deletions.
138 changes: 127 additions & 11 deletions corpus/expressions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,10 @@ fn main() {

let y = if x == 5 { 10 } else { 15 };

if foo && bar {}

if foo && bar || baz {}

--------------------------------------------------------------------------------

(source_file
Expand Down Expand Up @@ -520,7 +524,52 @@ let y = if x == 5 { 10 } else { 15 };
(integer_literal))
alternative: (else_clause
(block
(integer_literal))))))
(integer_literal)))))
(expression_statement
(if_expression
condition: (binary_expression
left: (identifier)
right: (identifier))
consequence: (block)))
(expression_statement
(if_expression
condition: (binary_expression
left: (binary_expression
left: (identifier)
right: (identifier))
right: (identifier))
consequence: (block))))

================================================================================
Basic if let expressions
================================================================================

if let Some(a) = b
&& c
&& d
&& let Some(e) = f
{
}

--------------------------------------------------------------------------------

(source_file
(expression_statement
(if_expression
condition: (let_chain
(let_condition
pattern: (tuple_struct_pattern
type: (identifier)
(identifier))
value: (identifier))
(identifier)
(identifier)
(let_condition
pattern: (tuple_struct_pattern
type: (identifier)
(identifier))
value: (identifier)))
consequence: (block))))

================================================================================
If let expressions
Expand All @@ -529,16 +578,65 @@ If let expressions
if let ("Bacon", b) = dish {
}

if let Some("chained") = a && b && let (C, D) = e {
}

if foo && let bar = || baz && quux {}

if a && let b = || c || d && e {}

--------------------------------------------------------------------------------

(source_file
(expression_statement
(if_let_expression
(tuple_pattern
(string_literal)
(identifier))
(identifier)
(block))))
(if_expression
condition: (let_condition
pattern: (tuple_pattern
(string_literal)
(identifier))
value: (identifier))
consequence: (block)))
(expression_statement
(if_expression
condition: (let_chain
(let_condition
pattern: (tuple_struct_pattern
type: (identifier)
(string_literal))
value: (identifier))
(identifier)
(let_condition
pattern: (tuple_pattern
(identifier)
(identifier))
value: (identifier)))
consequence: (block)))
(expression_statement
(if_expression
condition: (let_chain
(identifier)
(let_condition
pattern: (identifier)
value: (closure_expression
parameters: (closure_parameters)
body: (binary_expression
left: (identifier)
right: (identifier)))))
consequence: (block)))
(expression_statement
(if_expression
condition: (let_chain
(identifier)
(let_condition
pattern: (identifier)
value: (closure_expression
parameters: (closure_parameters)
body: (binary_expression
left: (identifier)
right: (binary_expression
left: (identifier)
right: (identifier))))))
consequence: (block))))

================================================================================
While let expressions
Expand All @@ -551,11 +649,12 @@ while let ("Bacon", b) = dish {

(source_file
(expression_statement
(while_let_expression
(tuple_pattern
(string_literal)
(while_expression
(let_condition
(tuple_pattern
(string_literal)
(identifier))
(identifier))
(identifier)
(block))))

================================================================================
Expand All @@ -581,6 +680,7 @@ let msg = match x {
if a {
"200 (but this is not very stylish)"
}
y if let Some(z) = foo && z && let Some(w) = bar => "very chained",
_ => "something else",
};

Expand Down Expand Up @@ -657,6 +757,22 @@ let msg = match x {
condition: (identifier)
consequence: (block
(string_literal))))
(match_arm
pattern: (match_pattern
(identifier)
condition: (let_chain
(let_condition
pattern: (tuple_struct_pattern
type: (identifier)
(identifier))
value: (identifier))
(identifier)
(let_condition
pattern: (tuple_struct_pattern
type: (identifier)
(identifier))
value: (identifier))))
value: (string_literal))
(match_arm
pattern: (match_pattern)
value: (string_literal))))))
Expand Down
57 changes: 30 additions & 27 deletions corpus/patterns.txt
Original file line number Diff line number Diff line change
Expand Up @@ -255,31 +255,33 @@ if let x!() | y!() = () {}

(source_file
(expression_statement
(if_let_expression
pattern: (or_pattern
(tuple_struct_pattern
type: (identifier)
(identifier))
(tuple_struct_pattern
type: (identifier)
(identifier)))
value: (identifier)
(if_expression
condition: (let_condition
pattern: (or_pattern
(tuple_struct_pattern
type: (identifier)
(identifier))
(tuple_struct_pattern
type: (identifier)
(identifier)))
value: (identifier))
consequence: (block
(expression_statement
(call_expression
function: (identifier)
arguments: (arguments
(identifier)))))))
(expression_statement
(while_let_expression
pattern: (or_pattern
(tuple_struct_pattern
type: (identifier)
(identifier))
(tuple_struct_pattern
type: (identifier)
(identifier)))
value: (identifier)
(while_expression
condition: (let_condition
pattern: (or_pattern
(tuple_struct_pattern
type: (identifier)
(identifier))
(tuple_struct_pattern
type: (identifier)
(identifier)))
value: (identifier))
body: (block
(expression_statement
(call_expression
Expand Down Expand Up @@ -369,15 +371,16 @@ if let x!() | y!() = () {}
type: (primitive_type)))
body: (block))
(expression_statement
(if_let_expression
pattern: (or_pattern
(macro_invocation
macro: (identifier)
(token_tree))
(macro_invocation
macro: (identifier)
(token_tree)))
value: (unit_expression)
(if_expression
condition: (let_condition
pattern: (or_pattern
(macro_invocation
macro: (identifier)
(token_tree))
(macro_invocation
macro: (identifier)
(token_tree)))
value: (unit_expression))
consequence: (block)))
(line_comment)
(line_comment)
Expand Down
46 changes: 24 additions & 22 deletions grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -874,10 +874,8 @@ module.exports = grammar({
$.async_block,
$.block,
$.if_expression,
$.if_let_expression,
$.match_expression,
$.while_expression,
$.while_let_expression,
$.loop_expression,
$.for_expression,
$.const_block
Expand Down Expand Up @@ -1096,27 +1094,37 @@ module.exports = grammar({

if_expression: $ => prec.right(seq(
'if',
field('condition', $._expression),
field('condition', $._condition),
field('consequence', $.block),
optional(field("alternative", $.else_clause))
)),

if_let_expression: $ => prec.right(seq(
'if',
let_condition: $ => seq(
'let',
field('pattern', $._pattern),
'=',
field('value', $._expression),
field('consequence', $.block),
optional(field('alternative', $.else_clause))
field('value', prec.left(PREC.and, $._expression))
),

_let_chain: $ => prec.left(PREC.and, choice(
seq($._let_chain, '&&', $.let_condition),
seq($._let_chain, '&&', $._expression),
seq($.let_condition, '&&', $._expression),
seq($.let_condition, '&&', $.let_condition),
seq($._expression, '&&', $.let_condition),
)),

_condition: $ => choice(
$._expression,
$.let_condition,
alias($._let_chain, $.let_chain),
),

else_clause: $ => seq(
'else',
choice(
$.block,
$.if_expression,
$.if_let_expression
$.if_expression
)
),

Expand Down Expand Up @@ -1155,23 +1163,13 @@ module.exports = grammar({

match_pattern: $ => seq(
$._pattern,
optional(seq('if', field('condition', $._expression)))
optional(seq('if', field('condition', $._condition)))
),

while_expression: $ => seq(
optional(seq($.loop_label, ':')),
'while',
field('condition', $._expression),
field('body', $.block)
),

while_let_expression: $ => seq(
optional(seq($.loop_label, ':')),
'while',
'let',
field('pattern', $._pattern),
'=',
field('value', $._expression),
field('condition', $._condition),
field('body', $.block)
),

Expand Down Expand Up @@ -1479,6 +1477,10 @@ module.exports = grammar({
}
})

function sepBy2(sep, rule) {
return seq(rule, repeat1(seq(sep, rule)))
}

function sepBy1(sep, rule) {
return seq(rule, repeat(seq(sep, rule)))
}
Expand Down
Loading

0 comments on commit 6ebdb1d

Please sign in to comment.