// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Diagnostics; using System.Globalization; using System.Linq; using System.Web.Razor.Generator; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Resources; using System.Web.Razor.Text; using System.Web.Razor.Tokenizer; using System.Web.Razor.Tokenizer.Symbols; namespace System.Web.Razor.Parser { public partial class VBCodeParser : TokenizerBackedParser { private void SetUpKeywords() { MapKeyword(VBKeyword.Using, EndTerminatedStatement(VBKeyword.Using, supportsExit: false, supportsContinue: false)); // http://msdn.microsoft.com/en-us/library/htd05whh.aspx MapKeyword(VBKeyword.While, EndTerminatedStatement(VBKeyword.While, supportsExit: true, supportsContinue: true)); // http://msdn.microsoft.com/en-us/library/zh1f56zs.aspx MapKeyword(VBKeyword.If, EndTerminatedStatement(VBKeyword.If, supportsExit: false, supportsContinue: false)); // http://msdn.microsoft.com/en-us/library/752y8abs.aspx MapKeyword(VBKeyword.Select, EndTerminatedStatement(VBKeyword.Select, supportsExit: true, supportsContinue: false, blockName: SyntaxConstants.VB.SelectCaseKeyword)); // http://msdn.microsoft.com/en-us/library/cy37t14y.aspx MapKeyword(VBKeyword.Try, EndTerminatedStatement(VBKeyword.Try, supportsExit: true, supportsContinue: false)); // http://msdn.microsoft.com/en-us/library/fk6t46tz.aspx MapKeyword(VBKeyword.With, EndTerminatedStatement(VBKeyword.With, supportsExit: false, supportsContinue: false)); // http://msdn.microsoft.com/en-us/library/wc500chb.aspx MapKeyword(VBKeyword.SyncLock, EndTerminatedStatement(VBKeyword.SyncLock, supportsExit: false, supportsContinue: false)); // http://msdn.microsoft.com/en-us/library/3a86s51t.aspx // http://msdn.microsoft.com/en-us/library/5z06z1kb.aspx // http://msdn.microsoft.com/en-us/library/5ebk1751.aspx MapKeyword(VBKeyword.For, KeywordTerminatedStatement(VBKeyword.For, VBKeyword.Next, supportsExit: true, supportsContinue: true)); MapKeyword(VBKeyword.Do, KeywordTerminatedStatement(VBKeyword.Do, VBKeyword.Loop, supportsExit: true, supportsContinue: true)); // http://msdn.microsoft.com/en-us/library/eked04a7.aspx MapKeyword(VBKeyword.Imports, ImportsStatement); MapKeyword(VBKeyword.Option, OptionStatement); MapKeyword(VBKeyword.Inherits, InheritsStatement); MapKeyword(VBKeyword.Class, ReservedWord); MapKeyword(VBKeyword.Namespace, ReservedWord); } protected virtual bool InheritsStatement() { Assert(VBKeyword.Inherits); Span.CodeGenerator = SpanCodeGenerator.Null; Context.CurrentBlock.Type = BlockType.Directive; AcceptAndMoveNext(); SourceLocation endInherits = CurrentLocation; if (At(VBSymbolType.WhiteSpace)) { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; } AcceptWhile(VBSymbolType.WhiteSpace); Output(SpanKind.MetaCode); if (EndOfFile || At(VBSymbolType.WhiteSpace) || At(VBSymbolType.NewLine)) { Context.OnError(endInherits, RazorResources.ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName); } // Just accept to a newline AcceptUntil(VBSymbolType.NewLine); if (!Context.DesignTimeMode) { // We want the newline to be treated as code, but it causes issues at design-time. Optional(VBSymbolType.NewLine); } string baseType = Span.GetContent(); Span.CodeGenerator = new SetBaseTypeCodeGenerator(baseType.Trim()); Output(SpanKind.Code); return false; } protected virtual bool OptionStatement() { try { Context.CurrentBlock.Type = BlockType.Directive; Assert(VBKeyword.Option); AcceptAndMoveNext(); AcceptWhile(VBSymbolType.WhiteSpace); if (!At(VBSymbolType.Identifier)) { if (CurrentSymbol != null) { Context.OnError(CurrentLocation, String.Format(CultureInfo.CurrentCulture, RazorResources.ParseError_Unexpected, CurrentSymbol.Content)); } return false; } SourceLocation optionLoc = CurrentLocation; string option = CurrentSymbol.Content; AcceptAndMoveNext(); AcceptWhile(VBSymbolType.WhiteSpace); bool boolVal; if (At(VBKeyword.On)) { AcceptAndMoveNext(); boolVal = true; } else if (At(VBSymbolType.Identifier)) { if (String.Equals(CurrentSymbol.Content, SyntaxConstants.VB.OffKeyword, StringComparison.OrdinalIgnoreCase)) { AcceptAndMoveNext(); boolVal = false; } else { Context.OnError(CurrentLocation, String.Format(CultureInfo.CurrentCulture, RazorResources.ParseError_InvalidOptionValue, option, CurrentSymbol.Content)); AcceptAndMoveNext(); return false; } } else { if (!EndOfFile) { Context.OnError(CurrentLocation, String.Format(CultureInfo.CurrentCulture, RazorResources.ParseError_Unexpected, CurrentSymbol.Content)); AcceptAndMoveNext(); } return false; } if (String.Equals(option, SyntaxConstants.VB.StrictKeyword, StringComparison.OrdinalIgnoreCase)) { Span.CodeGenerator = SetVBOptionCodeGenerator.Strict(boolVal); } else if (String.Equals(option, SyntaxConstants.VB.ExplicitKeyword, StringComparison.OrdinalIgnoreCase)) { Span.CodeGenerator = SetVBOptionCodeGenerator.Explicit(boolVal); } else { Span.CodeGenerator = new SetVBOptionCodeGenerator(option, boolVal); Context.OnError(optionLoc, RazorResources.ParseError_UnknownOption, option); } } finally { if (Span.Symbols.Count > 0) { Output(SpanKind.MetaCode); } } return true; } protected virtual bool ImportsStatement() { Context.CurrentBlock.Type = BlockType.Directive; Assert(VBKeyword.Imports); AcceptAndMoveNext(); AcceptVBSpaces(); if (At(VBSymbolType.WhiteSpace) || At(VBSymbolType.NewLine)) { Context.OnError(CurrentLocation, RazorResources.ParseError_NamespaceOrTypeAliasExpected); } // Just accept to a newline AcceptUntil(VBSymbolType.NewLine); Optional(VBSymbolType.NewLine); string ns = String.Concat(Span.Symbols.Skip(1).Select(s => s.Content)); Span.CodeGenerator = new AddImportCodeGenerator(ns, SyntaxConstants.VB.ImportsKeywordLength); Output(SpanKind.MetaCode); return false; } protected virtual Func EndTerminatedStatement(VBKeyword keyword, bool supportsExit, bool supportsContinue) { return EndTerminatedStatement(keyword, supportsExit, supportsContinue, blockName: keyword.ToString()); } protected virtual Func EndTerminatedStatement(VBKeyword keyword, bool supportsExit, bool supportsContinue, string blockName) { return () => { using (PushSpanConfig(StatementBlockSpanConfiguration(new StatementCodeGenerator()))) { SourceLocation blockStart = CurrentLocation; Assert(keyword); AcceptAndMoveNext(); while (!EndOfFile) { VBSymbol lastWhitespace = AcceptWhiteSpaceInLines(); if (IsAtEmbeddedTransition(allowTemplatesAndComments: true, allowTransitions: true)) { HandleEmbeddedTransition(lastWhitespace); } else { Accept(lastWhitespace); if ((supportsExit && At(VBKeyword.Exit)) || (supportsContinue && At(VBKeyword.Continue))) { HandleExitOrContinue(keyword); } else if (At(VBKeyword.End)) { AcceptAndMoveNext(); AcceptVBSpaces(); if (At(keyword)) { AcceptAndMoveNext(); if (!Context.DesignTimeMode) { Optional(VBSymbolType.NewLine); } Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; return false; } } else if (At(keyword)) { // Parse nested statement EndTerminatedStatement(keyword, supportsExit, supportsContinue)(); } else if (!EndOfFile) { AcceptAndMoveNext(); } } } Context.OnError(blockStart, RazorResources.ParseError_BlockNotTerminated, blockName, // This is a language keyword, so it does not need to be localized String.Concat(VBKeyword.End, " ", keyword)); return false; } }; } protected virtual Func KeywordTerminatedStatement(VBKeyword start, VBKeyword terminator, bool supportsExit, bool supportsContinue) { return () => { using (PushSpanConfig(StatementBlockSpanConfiguration(new StatementCodeGenerator()))) { SourceLocation blockStart = CurrentLocation; Assert(start); AcceptAndMoveNext(); while (!EndOfFile) { VBSymbol lastWhitespace = AcceptWhiteSpaceInLines(); if (IsAtEmbeddedTransition(allowTemplatesAndComments: true, allowTransitions: true)) { HandleEmbeddedTransition(lastWhitespace); } else { Accept(lastWhitespace); if ((supportsExit && At(VBKeyword.Exit)) || (supportsContinue && At(VBKeyword.Continue))) { HandleExitOrContinue(start); } else if (At(start)) { // Parse nested statement KeywordTerminatedStatement(start, terminator, supportsExit, supportsContinue)(); } else if (At(terminator)) { AcceptUntil(VBSymbolType.NewLine); Optional(VBSymbolType.NewLine); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.AnyExceptNewline; return false; } else if (!EndOfFile) { AcceptAndMoveNext(); } } } Context.OnError(blockStart, RazorResources.ParseError_BlockNotTerminated, start, terminator); return false; } }; } protected void HandleExitOrContinue(VBKeyword keyword) { Assert(VBSymbolType.Keyword); Debug.Assert(CurrentSymbol.Keyword == VBKeyword.Continue || CurrentSymbol.Keyword == VBKeyword.Exit); // Accept, read whitespace and look for the next keyword AcceptAndMoveNext(); AcceptWhile(VBSymbolType.WhiteSpace); // If this is the start keyword, skip it and continue (to avoid starting a nested statement block) Optional(keyword); } } }