Skip to content

Commit

Permalink
Add support for if/switch expressions
Browse files Browse the repository at this point in the history
As pointed out in #351.

[SE-0380](https://github.com/apple/swift-evolution/blob/main/proposals/0380-if-switch-expressions.md)
made it possible to do this, but support was lacking in this repository.
The fix turned out to be easier than anticipated, simply requiring the
statement to be added in some new places. Theoretically, it should also
be renamed, but that would make this a breaking change.

The proposal does not make clear whether labels are permitted on
expression-position `if` and `switch` statements. This assumes they are
not, for simplicity.
  • Loading branch information
alex-pinkus committed Feb 13, 2024
1 parent 1c58633 commit f3063c6
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 2 deletions.
21 changes: 19 additions & 2 deletions grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ module.exports = grammar({

// `lazy` is also allowed as an identifier...
[$.property_behavior_modifier, $.simple_identifier],

// SE-0380: if/switch expressions
[$._expression, $.if_statement],
[$._expression, $.switch_statement],
],
extras: ($) => [
$.comment,
Expand Down Expand Up @@ -474,6 +478,8 @@ module.exports = grammar({
$._binary_expression,
$.ternary_expression,
$._primary_expression,
$.if_statement,
$.switch_statement,
$.assignment,
seq($._expression, alias($._immediate_quest, "?")),
alias("async", $.simple_identifier)
Expand Down Expand Up @@ -541,7 +547,13 @@ module.exports = grammar({
PRECS.prefix_operations,
seq(
field("operation", $._prefix_unary_operator),
field("target", $._expression)
field(
"target",
choice(
$._expression,
alias(choice("async", "if", "switch"), $._expression)
)
)
)
),
as_expression: ($) =>
Expand Down Expand Up @@ -711,7 +723,12 @@ module.exports = grammar({
),
value_argument_label: ($) =>
prec.left(
choice($.simple_identifier, alias("async", $.simple_identifier))
choice(
$.simple_identifier,
alias("async", $.simple_identifier),
alias("if", $.simple_identifier),
alias("switch", $.simple_identifier)
)
),
value_argument: ($) =>
prec.left(
Expand Down
223 changes: 223 additions & 0 deletions test/corpus/expressions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1272,3 +1272,226 @@ foo()
(call_suffix
(value_arguments)))
(fully_open_range))

================================================================================
If/else expression
================================================================================

let number = if Bool.random() {
1.0
} else {
0.0
}

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

(source_file
(property_declaration
(value_binding_pattern)
(pattern
(simple_identifier))
(if_statement
(call_expression
(navigation_expression
(simple_identifier)
(navigation_suffix
(simple_identifier)))
(call_suffix
(value_arguments)))
(statements
(real_literal))
(else)
(statements
(real_literal)))))

================================================================================
Complex if/else expression
================================================================================

let bullet =
if isRoot && (count == 0 || !willExpand) { "" }
else if count == 0 { "- " }
else if maxDepth <= 0 { "▹ " }
else { "▿ " }

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

(source_file
(property_declaration
(value_binding_pattern)
(pattern
(simple_identifier))
(if_statement
(conjunction_expression
(simple_identifier)
(tuple_expression
(equality_expression
(simple_identifier)
(disjunction_expression
(integer_literal)
(prefix_expression
(bang)
(simple_identifier))))))
(statements
(line_string_literal))
(else)
(if_statement
(equality_expression
(simple_identifier)
(integer_literal))
(statements
(line_string_literal
(line_str_text)))
(else)
(if_statement
(infix_expression
(simple_identifier)
(custom_operator)
(integer_literal))
(statements
(line_string_literal
(line_str_text)))
(else)
(statements
(line_string_literal
(line_str_text))))))))

================================================================================
Switch expression
================================================================================

let y: Float = switch x.value {
case 0..<0x80: 1
case 0x80..<0x0800: 2.0
case 0x0800..<0x1_0000: 3.0
default: 4.5
}

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

(source_file
(property_declaration
(value_binding_pattern)
(pattern
(simple_identifier))
(type_annotation
(user_type
(type_identifier)))
(switch_statement
(navigation_expression
(simple_identifier)
(navigation_suffix
(simple_identifier)))
(switch_entry
(switch_pattern
(pattern
(range_expression
(integer_literal)
(hex_literal))))
(statements
(integer_literal)))
(switch_entry
(switch_pattern
(pattern
(range_expression
(hex_literal)
(hex_literal))))
(statements
(real_literal)))
(switch_entry
(switch_pattern
(pattern
(range_expression
(hex_literal)
(hex_literal))))
(statements
(real_literal)))
(switch_entry
(default_keyword)
(statements
(real_literal))))))

================================================================================
If and switch can still be used as a value argument labels
================================================================================

atomicValue.set(to: "value + 1", if: "value <= 100")
atomicValue.replace(from: otherValue, switch: true)

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

(source_file
(call_expression
(navigation_expression
(simple_identifier)
(navigation_suffix
(simple_identifier)))
(call_suffix
(value_arguments
(value_argument
(value_argument_label
(simple_identifier))
(line_string_literal
(line_str_text)))
(value_argument
(value_argument_label
(simple_identifier))
(line_string_literal
(line_str_text))))))
(call_expression
(navigation_expression
(simple_identifier)
(navigation_suffix
(simple_identifier)))
(call_suffix
(value_arguments
(value_argument
(value_argument_label
(simple_identifier))
(simple_identifier))
(value_argument
(value_argument_label
(simple_identifier))
(boolean_literal))))))

================================================================================
If and switch can still be used as enum identifiers
================================================================================

static let statements: Set<StatementKind> = [
.if,
.switch,
.while
]

parseStatements(.switch)

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

(source_file
(property_declaration
(modifiers
(property_modifier))
(value_binding_pattern)
(pattern
(simple_identifier))
(type_annotation
(user_type
(type_identifier)
(type_arguments
(user_type
(type_identifier)))))
(array_literal
(prefix_expression
(_expression))
(prefix_expression
(_expression))
(prefix_expression
(simple_identifier))))
(call_expression
(simple_identifier)
(call_suffix
(value_arguments
(value_argument
(prefix_expression
(_expression)))))))
41 changes: 41 additions & 0 deletions test/corpus/statements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,47 @@ someLabel: if a.isEmpty, let b = getB() {
(call_suffix
(value_arguments)))))

================================================================================
If let on multiple lines
================================================================================

if
let foo = getFoo(),
let bar = getBar()
{
doSomethingWith(foo: foo, bar: bar)
}

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

(source_file
(if_statement
(value_binding_pattern)
(simple_identifier)
(call_expression
(simple_identifier)
(call_suffix
(value_arguments)))
(value_binding_pattern)
(simple_identifier)
(call_expression
(simple_identifier)
(call_suffix
(value_arguments)))
(statements
(call_expression
(simple_identifier)
(call_suffix
(value_arguments
(value_argument
(value_argument_label
(simple_identifier))
(simple_identifier))
(value_argument
(value_argument_label
(simple_identifier))
(simple_identifier))))))))

================================================================================
If let with function call
================================================================================
Expand Down

0 comments on commit f3063c6

Please sign in to comment.