srcML / srcML

srcML Toolkit
srcml.org
GNU General Public License v3.0
104 stars 25 forks source link

Add Support for C# Switch Expression #1988

Open brywhitak opened 3 months ago

brywhitak commented 3 months ago

Below is an example of a C# switch expression, which is not currently marked up properly in srcML:

var n = argument switch
{
    Direction.Left => Orientation.West
};

Here is suggested markup for the given code:

var n = <expr><switch><expr><name>argument</name></expr> switch
<block>{<block_content>
    <case><expr><name>Direction.Left</name></expr> =&gt; <expr><name>Orientation.West</name></expr></case>,
</block_content>}</block>;</switch></expr></function>

Below are examples of multiple different cases, also not properly marked up in srcML:

The first case uses a code block to hold a pattern to match to; this is done if the object passed to the switch expression has multiple different member variables that can be checked.

 { X: 0, Y: 0 } => new Point(0, 0)

Marked up as:

<case><expr><block>{<block_content> <expr><name>X</name>: <literal type="number">0</literal></expr>, <expr><name>Y</name>: <literal type="number">0</literal></expr> </block_content>}</block></expr> =&gt; <expr><operator>new</operator> <call><name>Point</name><argument_list>(<argument><expr><literal type="number">0</literal></expr></argument>, <argument><expr><literal type="number">0</literal></expr></argument>)</argument_list></call></expr></case>

The second case uses a code block and a guard clause to limit when the case can be matched; this case only executes when x is greater than y, and then returns a point with a transform.

{ X: var x, Y: var y } when x > y => new Point(x + y, y)

Marked up as:

<case><expr><block>{<block_content> <expr><name>X</name>: <literal type="number">0</literal></expr>, <expr><name>Y</name>: <literal type="number">0</literal></expr> </block_content>}</block></expr> <guard>when <expr><name>x</name> <operator>&gt;</operator> <name>y</name></expr></guard> =&gt; <expr><operator>new</operator> <call><name>Point</name><argument_list>(<argument><expr><name>x</name> <operator>-</operator> <name>y</name></expr></argument>, <argument><expr><name>y</name></expr></argument>)</argument_list></call></expr></case>,

In the third case, _ is used as a catch-all to make sure there is something for all execution paths. Therefore, it is the default case.

_ => something()

Marked up as:

<default><name>_</name> =&gt; <call><expr><name>something</name><argument_list>()</argument_list></expr></call></default>
brywhitak commented 3 months ago

Some further clarification: => is usually reserved for lambda functions, however, there is another use where it can be used to make named functions. An example:

public override string ToString() => $"{fname} {lname}".Trim();

These are called expression body definitions in C#. This will require further investigation into whether or not it should be marked up as a function or as a lambda. This does confirm that it is an expression, though, so that would change some of the markup:

<switch_expr> <condition/> switch <block/> </switch_expr>

Since a <guard> tag is already being used as a statement in other proposals, another change in suggested markup would be to change the <guard> tag to a <condition> tag, in order to keep srcML consistent across languages:

<case> <expr/> when <condition/> => <expr/> , </case>

Cases without conditions remain the same:

<case> <expr/> => <expr/> , </case>

Additionally, there should be no <default> case specifically marked up. Instead, since _ is just a <name>, it should just be another case. As a side note, C# formally calls the entire block of x => y as an "arm," but I believe it is best to leave it as case as to keep it consistent with switch statements.