Add the ability for users to await expressions.

This enabled things like @await Foo().  We special case the await keyword and allow another snippet of code to follow it.
This commit is contained in:
N. Taylor Mullen 2014-03-17 15:35:17 -07:00
parent aef8a79081
commit 50314ca7e5
4 changed files with 64 additions and 5 deletions

View File

@ -0,0 +1,41 @@
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
namespace Microsoft.AspNet.Razor.Parser
{
public partial class CSharpCodeParser
{
private void SetUpExpressions()
{
MapKeywords(AwaitExpression, CSharpKeyword.Await);
}
private void AwaitExpression(bool topLevel)
{
// Ensure that we're on the await statement (only runs in debug)
Assert(CSharpKeyword.Await);
// Accept the "await" and move on
AcceptAndMoveNext();
// Accept 1 or more spaces between the await and the following code.
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
// Accept a single code piece to await. This will accept up until a method "call" signature.
// Ex: "@await |Foo|()" Inbetween the pipes is what is accepted. The Statement/ImplicitExpression
// handling capture method calls and the parameters passed in.
AcceptWhile(CSharpSymbolType.Identifier);
// Top level basically indicates if we're within an expression or statement.
// Ex: topLevel true = @await Foo() | topLevel false = @{ await Foo(); }
// Note that in this case @{ <b>@await Foo()</b> } top level is true for await.
// Therefore, if we're top level then we want to act like an implicit expression,
// otherwise just act as whatever we're contained in.
if (topLevel)
{
// Setup the Span to be an async implicit expression (an implicit expresison that allows spaces).
// Spaces are allowed because of "@await Foo()".
AsyncImplicitExpression();
}
}
}
}

View File

@ -18,6 +18,7 @@ namespace Microsoft.AspNet.Razor.Parser
internal static ISet<string> DefaultKeywords = new HashSet<string>() internal static ISet<string> DefaultKeywords = new HashSet<string>()
{ {
"await",
"if", "if",
"do", "do",
"try", "try",
@ -45,6 +46,7 @@ namespace Microsoft.AspNet.Razor.Parser
Keywords = new HashSet<string>(); Keywords = new HashSet<string>();
SetUpKeywords(); SetUpKeywords();
SetupDirectives(); SetupDirectives();
SetUpExpressions();
} }
protected internal ISet<string> Keywords { get; private set; } protected internal ISet<string> Keywords { get; private set; }
@ -307,6 +309,18 @@ namespace Microsoft.AspNet.Razor.Parser
} }
private void ImplicitExpression() private void ImplicitExpression()
{
ImplicitExpression(AcceptedCharacters.NonWhiteSpace);
}
// Async implicit expressions include the "await" keyword and therefore need to allow spaces to
// separate the "await" and the following code.
private void AsyncImplicitExpression()
{
ImplicitExpression(AcceptedCharacters.AnyExceptNewline);
}
private void ImplicitExpression(AcceptedCharacters acceptedCharacters)
{ {
Context.CurrentBlock.Type = BlockType.Expression; Context.CurrentBlock.Type = BlockType.Expression;
Context.CurrentBlock.CodeGenerator = new ExpressionCodeGenerator(); Context.CurrentBlock.CodeGenerator = new ExpressionCodeGenerator();
@ -314,7 +328,7 @@ namespace Microsoft.AspNet.Razor.Parser
using (PushSpanConfig(span => using (PushSpanConfig(span =>
{ {
span.EditHandler = new ImplicitExpressionEditHandler(Language.TokenizeString, Keywords, acceptTrailingDot: IsNested); span.EditHandler = new ImplicitExpressionEditHandler(Language.TokenizeString, Keywords, acceptTrailingDot: IsNested);
span.EditHandler.AcceptedCharacters = AcceptedCharacters.NonWhiteSpace; span.EditHandler.AcceptedCharacters = acceptedCharacters;
span.CodeGenerator = new ExpressionCodeGenerator(); span.CodeGenerator = new ExpressionCodeGenerator();
})) }))
{ {
@ -325,14 +339,14 @@ namespace Microsoft.AspNet.Razor.Parser
AcceptAndMoveNext(); AcceptAndMoveNext();
} }
} }
while (MethodCallOrArrayIndex()); while (MethodCallOrArrayIndex(acceptedCharacters));
PutCurrentBack(); PutCurrentBack();
Output(SpanKind.Code); Output(SpanKind.Code);
} }
} }
private bool MethodCallOrArrayIndex() private bool MethodCallOrArrayIndex(AcceptedCharacters acceptedCharacters)
{ {
if (!EndOfFile) if (!EndOfFile)
{ {
@ -361,9 +375,11 @@ namespace Microsoft.AspNet.Razor.Parser
if (At(right)) if (At(right))
{ {
AcceptAndMoveNext(); AcceptAndMoveNext();
Span.EditHandler.AcceptedCharacters = AcceptedCharacters.NonWhiteSpace;
// At the ending brace, restore the initial accepted characters.
Span.EditHandler.AcceptedCharacters = acceptedCharacters;
} }
return MethodCallOrArrayIndex(); return MethodCallOrArrayIndex(acceptedCharacters);
} }
if (CurrentSymbol.Type == CSharpSymbolType.Dot) if (CurrentSymbol.Type == CSharpSymbolType.Dot)
{ {

View File

@ -10,6 +10,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer
{ {
private static readonly Dictionary<string, CSharpKeyword> _keywords = new Dictionary<string, CSharpKeyword>(StringComparer.Ordinal) private static readonly Dictionary<string, CSharpKeyword> _keywords = new Dictionary<string, CSharpKeyword>(StringComparer.Ordinal)
{ {
{ "await", CSharpKeyword.Await },
{ "abstract", CSharpKeyword.Abstract }, { "abstract", CSharpKeyword.Abstract },
{ "byte", CSharpKeyword.Byte }, { "byte", CSharpKeyword.Byte },
{ "class", CSharpKeyword.Class }, { "class", CSharpKeyword.Class },

View File

@ -4,6 +4,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols
{ {
public enum CSharpKeyword public enum CSharpKeyword
{ {
Await,
Abstract, Abstract,
Byte, Byte,
Class, Class,