private Snapshot() { if (PerformanceProfiler.IsActive) { PerformanceProfiler.Begin(); } }
public static void StartProfiler() { #if PROFILE if (PerformanceProfiler.IsActive) { if (!profilerrunning) { profilerrunning = true; WriteLine(DebugMessageType.SPECIAL, "Starting the Profiler..."); PerformanceProfiler.Begin(); PerformanceProfiler.Start(); } else { WriteLine(DebugMessageType.SPECIAL, "Profiler is already running!"); } } else { WriteLine(DebugMessageType.SPECIAL, "Unable to start the Profiler..."); } #else WriteLine(DebugMessageType.SPECIAL, "Unable to start the Profiler: incorrect build configuration selected!"); #endif }
public PerformanceProfile(string sdkPath, string resultsDirectory) : base(resultsDirectory) { var saveSnapshotProfilingConfig = new SaveSnapshotProfilingConfig { ProfilingControlKind = ProfilingControlKind.Api, TempDir = Path.GetTempPath(), SaveDir = resultsDirectory, RedistDir = sdkPath, ProfilingType = ProfilingType.Performance, ListFile = this.ListFile, SnapshotFormat = SnapshotFormat.Uncompressed }; while (SelfAttach.State != SelfApiState.None) { Thread.Sleep(250); } SelfAttach.Attach(saveSnapshotProfilingConfig); this.WaitForProfilerToAttachToProcess(); if (PerformanceProfiler.IsActive) { PerformanceProfiler.Begin(); PerformanceProfiler.Start(); } }
public MorestachioDocumentInfo TokenizeAndParse(ParserOptions parsingOptions) { var errors = new List <IMorestachioError>(); var profiler = new PerformanceProfiler(parsingOptions.ProfileExecution); Queue <TokenPair> tokens; using (profiler.Begin("Tokenize")) { tokens = new Queue <TokenPair>(Tokenizer.Tokenize(parsingOptions, errors, profiler)); } //if there are any errors do not parse the template MorestachioDocumentInfo documentInfo; if (errors.Any()) { documentInfo = new MorestachioDocumentInfo(parsingOptions, null, errors); } else { documentInfo = Parse(parsingOptions, tokens); } documentInfo.Profiler = profiler; return(documentInfo); }
private static void InitJbProfilers() { if (PerformanceProfiler.IsActive) { PerformanceProfiler.Stop(); } else { PerformanceProfiler.Begin(); } }
// ReSharper disable once UnusedParameter.Local private void ExecuteHandler(ClientSession session) { while (SelfAttach.State != SelfApiState.Active) { Thread.Sleep(250); // wait until API starts } if (PerformanceProfiler.IsActive) { PerformanceProfiler.Begin(); PerformanceProfiler.Start(); } }
internal static IEnumerable <TokenPair> Tokenize(ParserOptions parserOptions, PerformanceProfiler profiler, out TokenzierContext tokenzierContext) { var templateString = parserOptions.Template; MatchCollection matches; using (profiler.Begin("Find Tokens")) { matches = TokenFinder.Matches(templateString); } var scopestack = new Stack <Tuple <string, int> >(); var partialsNames = new List <string>(parserOptions.PartialsStore?.GetNames() ?? new string[0]); var context = new TokenzierContext(NewlineFinder.Matches(templateString).OfType <Match>().Select(k => k.Index).ToArray()); tokenzierContext = context; context.SetLocation(0); var tokens = new List <TokenPair>(); void BeginElse(Match match) { var firstNonContentToken = tokens .AsReadOnly() .Reverse() .FirstOrDefault(e => !e.Type.Equals(TokenType.Content)); if (firstNonContentToken == null || !firstNonContentToken.Type.Equals(TokenType.IfClose)) { context.Errors .Add(new MorestachioSyntaxError( context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "find if block for else", firstNonContentToken?.Value, "{{/if}}", "Could not find an /if block for this else")); } else { scopestack.Push(Tuple.Create($"#else_{firstNonContentToken.Value}", match.Index)); tokens.Add(new TokenPair(TokenType.Else, firstNonContentToken.Value, context.CurrentLocation)); } } void EndIf(Match match, string expected) { if (!string.Equals(match.Value, "{{" + expected + "}}", StringComparison.InvariantCultureIgnoreCase)) { context.Errors .Add(new MorestachioSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "close", expected, "{{" + expected + "}}")); } else { if (!scopestack.Any()) { context.Errors.Add(new MorestachioUnopendScopeError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "if", "{{#if name}}")); } else { var item1 = scopestack.Peek().Item1; if (item1.StartsWith("#if") || item1.StartsWith("^if")) { var token = scopestack.Pop().Item1; tokens.Add(new TokenPair(TokenType.IfClose, token, context.CurrentLocation)); } else { context.Errors.Add(new MorestachioUnopendScopeError( context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "if", "{{#if name}}")); } } } } foreach (Match match in matches) { //yield front content. if (match.Index > context.Character) { tokens.Add(new TokenPair(TokenType.Content, templateString.Substring(context.Character, match.Index - context.Character), context.CurrentLocation)); } context.SetLocation(match.Index + 2); var tokenValue = match.Value; var trimmedToken = tokenValue.TrimStart('{').TrimEnd('}'); if (tokenValue.StartsWith("{{#declare ", true, CultureInfo.InvariantCulture)) { scopestack.Push(Tuple.Create(tokenValue, match.Index)); var token = trimmedToken.TrimStart('#').Trim() .Substring("declare ".Length).Trim(); if (string.IsNullOrWhiteSpace(token)) { context.Errors.Add(new MorestachioSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "open", "declare", "{{#declare name}}", " Missing the Name.")); } else { partialsNames.Add(token); tokens.Add(new TokenPair(TokenType.PartialDeclarationOpen, token, context.CurrentLocation)); } } else if (tokenValue.StartsWith("{{/declare", true, CultureInfo.InvariantCulture)) { if (!string.Equals(tokenValue, "{{/declare}}", StringComparison.InvariantCultureIgnoreCase)) { context.Errors.Add(new MorestachioSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "close", "declare", "{{/declare}}")); } else if (scopestack.Any() && scopestack.Peek().Item1.StartsWith("{{#declare", StringComparison.InvariantCultureIgnoreCase)) { var token = scopestack.Pop().Item1.TrimStart('{').TrimEnd('}').TrimStart('#').Trim() .Substring("declare".Length); tokens.Add(new TokenPair(TokenType.PartialDeclarationClose, token, context.CurrentLocation)); } else { context.Errors.Add(new MorestachioUnopendScopeError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "declare", "{{#declare name}}")); } } else if (tokenValue.StartsWith("{{#include ", true, CultureInfo.InvariantCulture)) { var token = trimmedToken.TrimStart('#').Trim() .Substring("include ".Length).Trim(); if (string.IsNullOrWhiteSpace(token) || !partialsNames.Contains(token)) { context.Errors.Add(new MorestachioSyntaxError( context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "use", "include", "{{#include name}}", $" There is no Partial declared '{token}'. Partial names are case sensitive and must be declared before an include.")); } else { tokens.Add(new TokenPair(TokenType.RenderPartial, token, context.CurrentLocation)); } } else if (tokenValue.StartsWith("{{#each", true, CultureInfo.InvariantCulture)) { var token = trimmedToken.TrimStart('#').Trim().Substring("each".Length); var eval = EvaluateNameFromToken(token); token = eval.Item1; var alias = eval.Item2; scopestack.Push(Tuple.Create($"#each{alias ?? token}", match.Index)); if (token.StartsWith(" ") && token.Trim() != "") { token = token.Trim(); tokens.Add(new TokenPair(TokenType.CollectionOpen, token, context.CurrentLocation) { MorestachioExpression = ExpressionTokenizer.ParseExpressionOrString(token, context) }); } else { context.Errors.Add(new InvalidPathSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "")); } if (!string.IsNullOrWhiteSpace(alias)) { context.AdvanceLocation("each ".Length + alias.Length); tokens.Add(new TokenPair(TokenType.Alias, alias, context.CurrentLocation)); } } else if (tokenValue.StartsWith("{{/each", true, CultureInfo.InvariantCulture)) { if (!string.Equals(tokenValue, "{{/each}}", StringComparison.InvariantCultureIgnoreCase)) { context.Errors.Add(new MorestachioSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "close", "each", "{{/each}}")); } else if (scopestack.Any() && scopestack.Peek().Item1.StartsWith("#each")) { var token = scopestack.Pop().Item1; tokens.Add(new TokenPair(TokenType.CollectionClose, token, context.CurrentLocation)); } else { context.Errors.Add(new MorestachioUnopendScopeError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "each", "{{#each name}}")); } } else if (tokenValue.StartsWith("{{#while", true, CultureInfo.InvariantCulture)) { var token = trimmedToken.TrimStart('#') .Trim() .Substring("while".Length); scopestack.Push(Tuple.Create($"#while{token}", match.Index)); if (token.StartsWith(" ") && token.Trim() != "") { token = token.Trim(); tokens.Add(new TokenPair(TokenType.WhileLoopOpen, token, context.CurrentLocation) { MorestachioExpression = ExpressionTokenizer.ParseExpressionOrString(token, context) }); } else { context.Errors.Add(new InvalidPathSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "")); } } else if (tokenValue.StartsWith("{{/while", true, CultureInfo.InvariantCulture)) { if (!string.Equals(tokenValue, "{{/while}}", StringComparison.InvariantCultureIgnoreCase)) { context.Errors.Add(new MorestachioSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "close", "while", "{{/while}}")); } else if (scopestack.Any() && scopestack.Peek().Item1.StartsWith("#while")) { var token = scopestack.Pop().Item1; tokens.Add(new TokenPair(TokenType.WhileLoopClose, token, context.CurrentLocation)); } else { context.Errors.Add(new MorestachioUnopendScopeError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "while", "{{#while Expression}}")); } } else if (tokenValue.StartsWith("{{#do", true, CultureInfo.InvariantCulture)) { var token = trimmedToken.TrimStart('#') .Trim() .Substring("do".Length); scopestack.Push(Tuple.Create($"#do{token}", match.Index)); if (token.StartsWith(" ") && token.Trim() != "") { token = token.Trim(); tokens.Add(new TokenPair(TokenType.DoLoopOpen, token, context.CurrentLocation) { MorestachioExpression = ExpressionTokenizer.ParseExpressionOrString(token, context) }); } else { context.Errors.Add(new InvalidPathSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "")); } } else if (tokenValue.StartsWith("{{/do", true, CultureInfo.InvariantCulture)) { if (!string.Equals(tokenValue, "{{/do}}", StringComparison.InvariantCultureIgnoreCase)) { context.Errors.Add(new MorestachioSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "close", "do", "{{/do}}")); } else if (scopestack.Any() && scopestack.Peek().Item1.StartsWith("#do")) { var token = scopestack.Pop().Item1; tokens.Add(new TokenPair(TokenType.DoLoopClose, token, context.CurrentLocation)); } else { context.Errors.Add(new MorestachioUnopendScopeError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "do", "{{#do Expression}}")); } } else if (tokenValue.StartsWith("{{#if ", true, CultureInfo.InvariantCulture)) { var token = trimmedToken.TrimStart('#').Trim().Substring("if".Length); var eval = EvaluateNameFromToken(token); token = eval.Item1; if (eval.Item2 != null) { context.Errors.Add(new MorestachioSyntaxError( context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "^if", "AS", "No Alias")); } scopestack.Push(Tuple.Create($"#if{token}", match.Index)); if (token.StartsWith(" ") && token.Trim() != "") { token = token.Trim(); tokens.Add(new TokenPair(TokenType.If, token, context.CurrentLocation) { MorestachioExpression = ExpressionTokenizer.ParseExpressionOrString(token, context) }); } else { context.Errors.Add(new InvalidPathSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "")); } } else if (tokenValue.StartsWith("{{^if ", true, CultureInfo.InvariantCulture)) { var token = trimmedToken.TrimStart('^').Trim().Substring("if".Length); var eval = EvaluateNameFromToken(token); token = eval.Item1; if (eval.Item2 != null) { context.Errors.Add(new MorestachioSyntaxError( context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "^if", "AS", "No Alias")); } scopestack.Push(Tuple.Create($"^if{token}", match.Index)); if (token.StartsWith(" ") && token.Trim() != "") { token = token.Trim(); tokens.Add(new TokenPair(TokenType.IfNot, token, context.CurrentLocation) { MorestachioExpression = ExpressionTokenizer.ParseExpressionOrString(token, context) }); } else { context.Errors.Add(new InvalidPathSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "")); } } else if (tokenValue.StartsWith("{{/if", true, CultureInfo.InvariantCulture)) { EndIf(match, "/If"); } else if (tokenValue.StartsWith("{{#ifelse", true, CultureInfo.InvariantCulture)) { EndIf(match, "#ifelse"); BeginElse(match); } else if (tokenValue.Equals("{{#else}}", StringComparison.InvariantCultureIgnoreCase)) { BeginElse(match); } else if (tokenValue.Equals("{{/else}}", StringComparison.InvariantCultureIgnoreCase)) { if (!string.Equals(tokenValue, "{{/else}}", StringComparison.InvariantCultureIgnoreCase)) { context.Errors.Add(new MorestachioSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "close", "else", "{{/else}}")); } else { if (scopestack.Any() && scopestack.Peek().Item1.StartsWith("#else_")) { var token = scopestack.Pop().Item1; tokens.Add(new TokenPair(TokenType.ElseClose, token, context.CurrentLocation)); } else { context.Errors.Add(new MorestachioUnopendScopeError( context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "else", "{{#else name}}")); } } } else if (tokenValue.StartsWith("{{#var ", true, CultureInfo.InvariantCulture)) { tokens.AddRange(ExpressionTokenizer.TokenizeVariableAssignment(tokenValue.Trim('{', '}'), context)); } else if (tokenValue.StartsWith("{{#")) { //open group var token = trimmedToken.TrimStart('#').Trim(); var eval = EvaluateNameFromToken(token); token = eval.Item1; var alias = eval.Item2; scopestack.Push(Tuple.Create(alias ?? token, match.Index)); //if (scopestack.Any() && scopestack.Peek().Item1 == token) //{ // tokens.Add(new TokenPair(TokenType.ElementClose, // Validated(token, match.Index, lines, context.Errors), context.CurrentLocation)); //} //else //{ // scopestack.Push(Tuple.Create(alias ?? token, match.Index)); //} tokens.Add(new TokenPair(TokenType.ElementOpen, token, context.CurrentLocation) { MorestachioExpression = ExpressionTokenizer.ParseExpressionOrString(token, context) }); if (!string.IsNullOrWhiteSpace(alias)) { context.AdvanceLocation(3 + alias.Length); tokens.Add(new TokenPair(TokenType.Alias, alias, context.CurrentLocation)); } } else if (tokenValue.StartsWith("{{^")) { //open inverted group var token = trimmedToken.TrimStart('^').Trim(); var eval = EvaluateNameFromToken(token); token = eval.Item1; var alias = eval.Item2; scopestack.Push(Tuple.Create(alias ?? token, match.Index)); tokens.Add(new TokenPair(TokenType.InvertedElementOpen, token, context.CurrentLocation) { MorestachioExpression = ExpressionTokenizer.ParseExpressionOrString(token, context) }); if (!string.IsNullOrWhiteSpace(alias)) { context.AdvanceLocation(1 + alias.Length); tokens.Add(new TokenPair(TokenType.Alias, alias, context.CurrentLocation)); } } else if (tokenValue.StartsWith("{{/")) { var token = trimmedToken.TrimStart('/').Trim(); //close group if (scopestack.Any() && scopestack.Peek().Item1 == token) { scopestack.Pop(); tokens.Add(new TokenPair(TokenType.ElementClose, token, context.CurrentLocation)); } else { context.Errors.Add(new MorestachioUnopendScopeError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), "/", "{{#path}}", " There are more closing elements then open.")); } } else if (tokenValue.StartsWith("{{{") || tokenValue.StartsWith("{{&")) { //escaped single element var token = trimmedToken.TrimStart('&').Trim(); tokens.Add(new TokenPair(TokenType.UnescapedSingleValue, token, context.CurrentLocation) { MorestachioExpression = ExpressionTokenizer.ParseExpressionOrString(token, context) }); } else if (tokenValue.StartsWith("{{!")) { //it's a comment drop this on the floor, no need to even yield it. } else if (tokenValue.StartsWith("#") || tokenValue.StartsWith("/")) { //catch expression handler context.Errors.Add(new MorestachioSyntaxError(context.CurrentLocation .AddWindow(new CharacterSnippedLocation(1, 1, match.Value)), $"Unexpected token. Expected an valid Expression but got '{tokenValue}'", tokenValue, "")); } else { //check for custom DocumentItem provider var customDocumentProvider = parserOptions.CustomDocumentItemProviders.FirstOrDefault(e => e.ShouldTokenize(tokenValue)); if (customDocumentProvider != null) { var tokenInfo = new CustomDocumentItemProvider.TokenInfo(tokenValue, context, scopestack); var tokenPairs = customDocumentProvider.Tokenize(tokenInfo, parserOptions); tokens.AddRange(tokenPairs); } else { //unsingle value. var token = trimmedToken.Trim(); tokens.Add(new TokenPair(TokenType.EscapedSingleValue, token, context.CurrentLocation) { MorestachioExpression = ExpressionTokenizer.ParseExpressionOrString(token, context) }); } } //move forward in the string. if (context.Character > match.Index + match.Length) { throw new InvalidOperationException("Internal index location error"); } context.SetLocation(match.Index + match.Length); } if (context.Character < templateString.Length) { tokens.Add(new TokenPair(TokenType.Content, templateString.Substring(context.Character), context.CurrentLocation)); } if (scopestack.Any() || parserOptions.CustomDocumentItemProviders.Any(f => f.ScopeStack.Any())) { foreach (var unclosedScope in scopestack .Concat(parserOptions.CustomDocumentItemProviders.SelectMany(f => f.ScopeStack)) .Select(k => { var value = k.Item1.Trim('{', '#', '}'); if (value.StartsWith("each ")) { value = value.Substring(5); } return(new { scope = value, location = HumanizeCharacterLocation(k.Item2, context.Lines) }); }).Reverse()) { context.Errors.Add(new MorestachioUnopendScopeError(unclosedScope.location .AddWindow(new CharacterSnippedLocation(1, -1, "")), unclosedScope.scope, "")); } } return(tokens); }
internal static IEnumerable <TokenPair> TokenizeString(string partial, ICollection <IMorestachioError> parseErrors, PerformanceProfiler profiler) { var templateString = partial; MatchCollection matches; using (profiler.Begin("Find Tokens")) { matches = TokenFinder.Matches(templateString); } var scopestack = new Stack <Tuple <string, int> >(); var idx = 0; var lines = new List <int>(); lines.AddRange(NewlineFinder.Matches(templateString).OfType <Match>().Select(k => k.Index)); var partialsNames = new List <string>(); var tokens = new List <TokenPair>(); foreach (Match m in matches) { int tokenIndex = m.Index; //yield front content. if (m.Index > idx) { tokens.Add(new TokenPair(TokenType.Content, templateString.Substring(idx, m.Index - idx), HumanizeCharacterLocation(tokenIndex, lines))); } if (m.Value.StartsWith("{{#declare", true, CultureInfo.InvariantCulture)) { scopestack.Push(Tuple.Create(m.Value, m.Index)); var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('#').Trim().Substring("declare".Length); if (string.IsNullOrWhiteSpace(token)) { parseErrors.Add(new MorestachioSyntaxError(HumanizeCharacterLocation(m.Index, lines), "open", "declare", "{{#declare name}}", " Missing the Name.")); } else { partialsNames.Add(token); tokens.Add(new TokenPair(TokenType.PartialDeclarationOpen, token, HumanizeCharacterLocation(tokenIndex, lines))); } } else if (m.Value.StartsWith("{{/declare", true, CultureInfo.InvariantCulture)) { if (!string.Equals(m.Value, "{{/declare}}", StringComparison.InvariantCultureIgnoreCase)) { parseErrors.Add(new MorestachioSyntaxError(HumanizeCharacterLocation(m.Index, lines), "close", "declare", "{{/declare}}")); } else if (scopestack.Any() && scopestack.Peek().Item1.StartsWith("{{#declare")) { var token = scopestack.Pop().Item1.TrimStart('{').TrimEnd('}').TrimStart('#').Trim() .Substring("declare".Length); tokens.Add(new TokenPair(TokenType.PartialDeclarationClose, token, HumanizeCharacterLocation(tokenIndex, lines))); } else { parseErrors.Add(new MorestachioUnopendScopeError(HumanizeCharacterLocation(m.Index, lines), "declare", "{{#declare name}}")); } } else if (m.Value.StartsWith("{{#include", true, CultureInfo.InvariantCulture)) { var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('#').Trim().Substring("include".Length); if (string.IsNullOrWhiteSpace(token) || !partialsNames.Contains(token)) { parseErrors.Add(new MorestachioSyntaxError( HumanizeCharacterLocation(m.Index, lines), "use", "include", "{{#include name}}", $" There is no Partial declared '{token}'. Partial names are case sensitive and must be declared before an include.")); } else { tokens.Add(new TokenPair(TokenType.RenderPartial, token, HumanizeCharacterLocation(tokenIndex, lines))); } } else if (m.Value.StartsWith("{{#each", true, CultureInfo.InvariantCulture)) { var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('#').Trim().Substring("each".Length); var eval = EvaluateNameFromToken(token); token = eval.Item1; var alias = eval.Item2; scopestack.Push(Tuple.Create($"#each{alias}", m.Index)); if (token.StartsWith(" ") && token.Trim() != "") { token = token.Trim(); if (FormatInExpressionFinder.IsMatch(token)) { tokens.AddRange(TokenizeFormattables(token, templateString, lines, tokenIndex, parseErrors)); tokens.Add(new TokenPair(TokenType.CollectionOpen, ".", HumanizeCharacterLocation(tokenIndex, lines))); } else { tokens.Add(new TokenPair(TokenType.CollectionOpen, Validated(token, templateString, m.Index, lines, parseErrors).Trim(), HumanizeCharacterLocation(tokenIndex, lines))); } } else { parseErrors.Add(new InvalidPathSyntaxError(HumanizeCharacterLocation(m.Index, lines), "")); } if (!string.IsNullOrWhiteSpace(alias)) { tokens.Add(new TokenPair(TokenType.Alias, alias, HumanizeCharacterLocation(m.Index + $"#each{alias}".Length, lines))); } } else if (m.Value.StartsWith("{{#if", true, CultureInfo.InvariantCulture)) { var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('#').Trim().Substring("if".Length); var eval = EvaluateNameFromToken(token); token = eval.Item1; var alias = eval.Item2; scopestack.Push(Tuple.Create($"#if{alias}", m.Index)); if (token.StartsWith(" ") && token.Trim() != "") { token = token.Trim(); if (FormatInExpressionFinder.IsMatch(token)) { tokens.AddRange(TokenizeFormattables(token, templateString, lines, tokenIndex, parseErrors)); tokens.Add(new TokenPair(TokenType.If, ".", HumanizeCharacterLocation(tokenIndex, lines))); } else { tokens.Add(new TokenPair(TokenType.If, Validated(token, templateString, m.Index, lines, parseErrors).Trim(), HumanizeCharacterLocation(tokenIndex, lines))); } } else { parseErrors.Add(new InvalidPathSyntaxError(HumanizeCharacterLocation(m.Index, lines), "")); } if (!string.IsNullOrWhiteSpace(alias)) { tokens.Add(new TokenPair(TokenType.Alias, alias, HumanizeCharacterLocation(m.Index + $"#if{alias}".Length, lines))); } } else if (m.Value.StartsWith("{{/if", true, CultureInfo.InvariantCulture)) { if (!string.Equals(m.Value, "{{/if}}", StringComparison.InvariantCultureIgnoreCase)) { parseErrors.Add(new MorestachioSyntaxError(HumanizeCharacterLocation(m.Index, lines), "close", "if", "{{/if}}")); } else if (scopestack.Any() && scopestack.Peek().Item1.StartsWith("#if")) { var token = scopestack.Pop().Item1; tokens.Add(new TokenPair(TokenType.IfClose, token, HumanizeCharacterLocation(tokenIndex, lines))); } else { parseErrors.Add(new MorestachioUnopendScopeError(HumanizeCharacterLocation(m.Index, lines), "if", "{{#if name}}")); } } else if (m.Value.StartsWith("{{/each", true, CultureInfo.InvariantCulture)) { if (!string.Equals(m.Value, "{{/each}}", StringComparison.InvariantCultureIgnoreCase)) { parseErrors.Add(new MorestachioSyntaxError(HumanizeCharacterLocation(m.Index, lines), "close", "each", "{{/each}}")); } else if (scopestack.Any() && scopestack.Peek().Item1.StartsWith("#each")) { var token = scopestack.Pop().Item1; tokens.Add(new TokenPair(TokenType.CollectionClose, token, HumanizeCharacterLocation(tokenIndex, lines))); } else { parseErrors.Add(new MorestachioUnopendScopeError(HumanizeCharacterLocation(m.Index, lines), "each", "{{#each name}}")); } } else if (m.Value.StartsWith("{{#")) { //open group var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('#').Trim(); var eval = EvaluateNameFromToken(token); token = eval.Item1; var alias = eval.Item2; if (scopestack.Any() && scopestack.Peek().Item1 == token) { tokens.Add(new TokenPair(TokenType.ElementClose, Validated(token, templateString, m.Index, lines, parseErrors), HumanizeCharacterLocation(tokenIndex, lines))); } else { scopestack.Push(Tuple.Create(alias, m.Index)); } if (FormatInExpressionFinder.IsMatch(token)) { tokens.AddRange(TokenizeFormattables(token, templateString, lines, tokenIndex, parseErrors)); tokens.Add(new TokenPair(TokenType.ElementOpen, ".", HumanizeCharacterLocation(tokenIndex, lines))); } else { tokens.Add(new TokenPair(TokenType.ElementOpen, Validated(token, templateString, m.Index, lines, parseErrors), HumanizeCharacterLocation(tokenIndex, lines))); } if (!string.IsNullOrWhiteSpace(alias)) { tokens.Add(new TokenPair(TokenType.Alias, alias, HumanizeCharacterLocation(m.Index + token.Length, lines))); } } else if (m.Value.StartsWith("{{^")) { //open inverted group var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('^').Trim(); var eval = EvaluateNameFromToken(token); token = eval.Item1; var alias = eval.Item2; if (scopestack.Any() && scopestack.Peek().Item1 == token) { tokens.Add(new TokenPair(TokenType.ElementClose, Validated(token, templateString, m.Index, lines, parseErrors), HumanizeCharacterLocation(tokenIndex, lines))); } else { scopestack.Push(Tuple.Create(alias, m.Index)); } if (FormatInExpressionFinder.IsMatch(token)) { tokens.AddRange(TokenizeFormattables(token, templateString, lines, tokenIndex, parseErrors)); tokens.Add(new TokenPair(TokenType.InvertedElementOpen, ".", HumanizeCharacterLocation(tokenIndex, lines))); } else { tokens.Add(new TokenPair(TokenType.InvertedElementOpen, Validated(token, templateString, m.Index, lines, parseErrors), HumanizeCharacterLocation(tokenIndex, lines))); } if (!string.IsNullOrWhiteSpace(alias)) { tokens.Add(new TokenPair(TokenType.Alias, alias, HumanizeCharacterLocation(m.Index + $"#each{alias}".Length, lines))); } } else if (m.Value.StartsWith("{{/")) { var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('/').Trim(); //close group if (scopestack.Any() && scopestack.Peek().Item1 == token) { scopestack.Pop(); tokens.Add(new TokenPair(TokenType.ElementClose, token, HumanizeCharacterLocation(tokenIndex, lines))); } else { parseErrors.Add(new MorestachioUnopendScopeError(HumanizeCharacterLocation(m.Index, lines), "/", "{{#path}}", " There are more closing elements then open.")); } } else if (m.Value.StartsWith("{{{") | m.Value.StartsWith("{{&")) { //escaped single element var token = m.Value.TrimStart('{').TrimEnd('}').TrimStart('&').Trim(); tokens.Add(new TokenPair(TokenType.UnescapedSingleValue, Validated(token, templateString, m.Index, lines, parseErrors), HumanizeCharacterLocation(tokenIndex, lines))); } else if (m.Value.StartsWith("{{!")) { //it's a comment drop this on the floor, no need to even yield it. } else { tokens.AddRange(TokenizePath(parseErrors, m.Value, templateString, lines, tokenIndex)); } //move forward in the string. idx = m.Index + m.Length; } if (idx < templateString.Length) { tokens.Add(new TokenPair(TokenType.Content, templateString.Substring(idx), HumanizeCharacterLocation(idx, lines))); } #region Assert that any scopes opened must be closed. if (scopestack.Any()) { foreach (var unclosedScope in scopestack.Select(k => { var value = k.Item1.Trim('{', '#', '}'); if (value.StartsWith("each ")) { value = value.Substring(5); } return(new { scope = value, location = HumanizeCharacterLocation(k.Item2, lines) }); }).Reverse()) { parseErrors.Add(new MorestachioUnopendScopeError(unclosedScope.location, unclosedScope.scope, "")); } } return(tokens); #endregion ////We want to throw an aggregate template exception, but in due time. //if (!parseErrors.Any()) //{ // yield break; //} //var innerExceptions = parseErrors.OrderBy(k => k.LineNumber).ThenBy(k => k.CharacterOnLine).ToArray(); //throw new AggregateException(innerExceptions); }