private bool IsConcatenationDecorator(string name, PreTokenDecorator decorator, out string joiningString) { joiningString = null; if (string.Compare("concat", decorator.Name, StringComparison.InvariantCultureIgnoreCase) != 0) { return(false); } if (decorator.Args.Count == 1) { joiningString = decorator.Args[0]; } if (decorator.Args.Count > 1) { throw new TokenizerException($"Token '{name}' Concat() must have a single argument."); } return(true); }
private void AppendDecorator(PreTokenEnumerator enumerator, PreToken token, PreTokenDecorator decorator) { if (decorator == null) { return; } if (string.IsNullOrEmpty(decorator.Name)) { return; } switch (decorator.Name.ToLowerInvariant()) { case "eol": case "$": if (decorator.Args.Any()) { throw new ParsingException($"'{decorator.Name}' decorator does not take any arguments", enumerator); } token.TerminateOnNewline = true; break; case "optional": case "?": if (decorator.Args.Any()) { throw new ParsingException($"'{decorator.Name}' decorator does not take any arguments", enumerator); } token.Optional = true; break; case "repeating": case "*": if (decorator.Args.Any()) { throw new ParsingException($"'{decorator.Name}' decorator does not take any arguments", enumerator); } token.Repeating = true; break; case "required": case "!": if (decorator.Args.Any()) { throw new ParsingException($"'{decorator.Name}' decorator does not take any arguments", enumerator); } token.Required = true; break; case "once": if (decorator.Args.Any()) { throw new ParsingException($"'{decorator.Name}' decorator does not take any arguments", enumerator); } token.ConsiderOnce = true; break; default: token.Decorators.Add(decorator); break; } }
private void ParseDecoratorArgumentInDoubleQuotes(PreTokenEnumerator enumerator, ref FlatTokenParserState state, ref PreTokenDecorator decorator, ref string argument, ref StringBuilder tokenContent) { var next = enumerator.Next(); switch (next) { case @"""": decorator.Args.Add(argument); argument = string.Empty; state = FlatTokenParserState.InDecoratorArgumentRunOff; break; default: argument += next; break; } tokenContent.Append(next); }
private void ParseDecoratorArgument(PreTokenEnumerator enumerator, ref FlatTokenParserState state, ref PreTokenDecorator decorator, ref string argument, ref StringBuilder tokenContent) { var next = enumerator.Next(); tokenContent.Append(next); if (string.IsNullOrWhiteSpace(argument) && string.IsNullOrWhiteSpace(next)) { return; } switch (next) { case ")": decorator.Args.Add(argument.Trim()); argument = string.Empty; state = FlatTokenParserState.InDecorator; break; case "'": if (string.IsNullOrWhiteSpace(argument)) { argument = string.Empty; state = FlatTokenParserState.InDecoratorArgumentSingleQuotes; } else { argument += next; } break; case @"""": if (string.IsNullOrWhiteSpace(argument)) { argument = string.Empty; state = FlatTokenParserState.InDecoratorArgumentDoubleQuotes; } else { argument += next; } break; case ",": decorator.Args.Add(argument.Trim()); argument = string.Empty; state = FlatTokenParserState.InDecoratorArgument; break; default: argument += next; break; } }
private void ParseDecorator(PreTemplate template, ref PreToken token, PreTokenEnumerator enumerator, ref FlatTokenParserState state, ref PreTokenDecorator decorator, ref bool inFrontMatterToken, ref StringBuilder tokenContent, TokenizerOptions options) { var next = enumerator.Next(); tokenContent.Append(next); if (string.IsNullOrWhiteSpace(next)) { if (inFrontMatterToken == false) { return; } if (next != "\n") { return; } } switch (next) { case "}" when inFrontMatterToken == false: case "\n" when inFrontMatterToken: token.IsFrontMatterToken = inFrontMatterToken; AppendDecorator(enumerator, token, decorator); AppendToken(template, token, ref tokenContent, options); token = new PreToken(); decorator = new PreTokenDecorator(); if (inFrontMatterToken) { inFrontMatterToken = false; state = FlatTokenParserState.InFrontMatter; } else { state = FlatTokenParserState.InPreamble; } break; case ",": AppendDecorator(enumerator, token, decorator); decorator = new PreTokenDecorator(); break; case "(": state = FlatTokenParserState.InDecoratorArgument; break; case "}" when inFrontMatterToken: case "\n" when inFrontMatterToken == false: throw new ParsingException($"'{decorator.Name}' unexpected character: {next}", enumerator); case "!": if (string.IsNullOrWhiteSpace(decorator.Name)) { decorator.IsNotDecorator = true; } else { throw new ParsingException($"'{decorator.Name}' unexpected character: {next}", enumerator); } break; default: decorator.AppendName(next); break; } }
/// <summary> /// Parses the template string and constructs a <see cref="PreTemplate"/>. /// </summary> public PreTemplate Parse(string template, TokenizerOptions options) { var preTemplate = new PreTemplate { Options = options.Clone() }; var enumerator = new PreTokenEnumerator(template); if (enumerator.IsEmpty) { return(preTemplate); } var state = FlatTokenParserState.AtStart; var token = new PreToken(); var decorator = new PreTokenDecorator(); var argument = string.Empty; var tokenContent = new StringBuilder(); var frontMatterName = new StringBuilder(); var frontMatterValue = new StringBuilder(); var inFrontMatterToken = false; // Basic State Machine to parse the template input while (enumerator.IsEmpty == false) { switch (state) { case FlatTokenParserState.AtStart: ParseStart(enumerator, ref state); break; case FlatTokenParserState.InFrontMatter: ParseFrontMatter(enumerator, ref frontMatterName, ref state); break; case FlatTokenParserState.InFrontMatterComment: ParseFrontMatterComment(enumerator, ref state); break; case FlatTokenParserState.InFrontMatterOption: ParseFrontMatterOption(enumerator, ref frontMatterName, ref state, ref inFrontMatterToken, ref token); break; case FlatTokenParserState.InFrontMatterOptionValue: ParseFrontMatterOptionValue(preTemplate, enumerator, ref frontMatterName, ref frontMatterValue, ref state); break; case FlatTokenParserState.InPreamble: ParsePreamble(ref token, enumerator, ref state, ref tokenContent); break; case FlatTokenParserState.InTokenName: ParseTokenName(preTemplate, ref token, enumerator, ref state, ref inFrontMatterToken, ref tokenContent, preTemplate.Options); break; case FlatTokenParserState.InTokenValue: ParseTokenValue(preTemplate, ref token, enumerator, ref state, ref inFrontMatterToken, ref tokenContent, preTemplate.Options); break; case FlatTokenParserState.InTokenValueSingleQuotes: ParseTokenValueInSingleQuotes(enumerator, ref token, ref state, ref tokenContent); break; case FlatTokenParserState.InTokenValueDoubleQuotes: ParseTokenValueInDoubleQuotes(enumerator, ref token, ref state, ref tokenContent); break; case FlatTokenParserState.InTokenValueRunOff: ParseTokenValueRunOff(enumerator, ref preTemplate, ref token, ref state, ref inFrontMatterToken, ref tokenContent, preTemplate.Options); break; case FlatTokenParserState.InDecorator: ParseDecorator(preTemplate, ref token, enumerator, ref state, ref decorator, ref inFrontMatterToken, ref tokenContent, preTemplate.Options); break; case FlatTokenParserState.InDecoratorArgument: ParseDecoratorArgument(enumerator, ref state, ref decorator, ref argument, ref tokenContent); break; case FlatTokenParserState.InDecoratorArgumentSingleQuotes: ParseDecoratorArgumentInSingleQuotes(enumerator, ref state, ref decorator, ref argument, ref tokenContent); break; case FlatTokenParserState.InDecoratorArgumentDoubleQuotes: ParseDecoratorArgumentInDoubleQuotes(enumerator, ref state, ref decorator, ref argument, ref tokenContent); break; case FlatTokenParserState.InDecoratorArgumentRunOff: ParseDecoratorArgumentRunOff(enumerator, ref state, ref tokenContent); break; default: throw new TokenizerException($"Unknown FlatTokenParserState: {state}"); } } // Append current token if it has contents // Note: allow empty token values, as these will serve to truncate the last // token in the template if (string.IsNullOrWhiteSpace(token.Preamble) == false) { AppendToken(preTemplate, token, ref tokenContent, preTemplate.Options); } return(preTemplate); }