How it works...

In this recipe, we added basic lexer and parser support for the switch operator (?:). Lexer is primarily responsible for scanning the text and generating tokens. LanguageParser is responsible for parsing the lexed tokens and generate syntax tree with nodes and tokens.

Let's walk through our code changes in this recipe. We added the following highlighted code to the lexer:

case '?': 
if (TextWindow.PeekChar() == '?')
{ ...
}
else if (TextWindow.PeekChar() == ':')
{
TextWindow.AdvanceChar();
info.Kind = SyntaxKind.QuestionColonToken;
}
else
{ ...
}

In the original code, when we were scanning the text and identify a '?' character, we peeked at the next character to identify it is another '?' character (the ?? null coalescing operator) or a whitespace (the ? token for the conditional operator). Our new code adds an additional check for whether the next character is ':' (the ?: token for the switch operator). If so, it advances the current character in the text window and sets the syntax kind for the current token to SyntaxKind.QuestionColonToken.

We added the following highlighted code to the parser:

if (tk == SyntaxKind.QuestionToken && precedence <= Precedence.Ternary)
{ ...
}
else if (tk == SyntaxKind.QuestionColonToken && precedence <= Precedence.Ternary)
{
var questionColonToken = this.EatToken();
var labels = this.ParseBracketedArgumentList();
var colon = this.EatToken(SyntaxKind.ColonToken);
var values = this.ParseBracketedArgumentList();
leftOperand = _syntaxFactory.SwitchExpression(leftOperand, questionColonToken, labels, colon, values);
}

We extended the original code that parsed the QuestionToken in the parser to also check for the QuestionColonToken and ternary precedence. If so, we eat the next token as the questionColonToken. Then, we attempt to parse the labels as a bracketed argument list by invoking ParseBracketedArgumentList (this code already exists in the language parser for parsing the bracketed argument list for a dictionary initializer). This is followed by parsing the colon token by invoking EatToken with the expected syntax kind for the colon token. This method handles both the valid and invalid token cases:

protected SyntaxToken EatToken(SyntaxKind kind)
{
Debug.Assert(SyntaxFacts.IsAnyToken(kind));

var ct = this.CurrentToken;
if (ct.Kind == kind)
{
MoveToNextToken();
return ct;
}

//slow part of EatToken(SyntaxKind kind)
return CreateMissingToken(kind, this.CurrentToken.Kind, reportError: true);
}

For a valid token of the expected kind, it moves to the next token and returns the current colon token. If the next token is not of the expected kind, it generates a missing token and also reports a syntax diagnostic for the missing token:

Error CS1003 Syntax error, ':' expected ClassLibrary <%PROJECT_DIR%>ClassLibraryClass1.cs 5

Finally, we parse the values as another bracketed argument list. We invoke the newly auto generated syntax factory helper SwitchExpression to generate a SwitchExpressionSyntax node with the parsed tokens.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset