Skip to content

Commit a63c347

Browse files
committed
Hacky template parser
1 parent f9f1758 commit a63c347

File tree

3 files changed

+62
-84
lines changed

3 files changed

+62
-84
lines changed

src/Serilog.Expressions/Expressions/Parsing/ExpressionParser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace Serilog.Expressions.Parsing
55
{
66
static class ExpressionParser
77
{
8-
static ExpressionTokenizer Tokenizer { get; } = new ExpressionTokenizer(false);
8+
static ExpressionTokenizer Tokenizer { get; } = new ExpressionTokenizer();
99

1010
public static Expression Parse(string filterExpression)
1111
{

src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenizer.cs

Lines changed: 8 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
using System.Collections.Generic;
2+
using System.Linq;
23
using Superpower;
34
using Superpower.Model;
45

56
namespace Serilog.Expressions.Parsing
67
{
78
class ExpressionTokenizer : Tokenizer<ExpressionToken>
89
{
9-
readonly bool _templateSyntax;
10-
1110
static readonly ExpressionToken[] SimpleOps = new ExpressionToken[128];
1211

1312
static readonly ExpressionKeyword[] Keywords =
@@ -44,78 +43,17 @@ static ExpressionTokenizer()
4443
SimpleOps['?'] = ExpressionToken.QuestionMark;
4544
}
4645

47-
public ExpressionTokenizer(bool templateSyntax)
46+
public TokenList<ExpressionToken> GreedyTokenize(TextSpan textSpan)
4847
{
49-
_templateSyntax = templateSyntax;
48+
// Dropping error info off for now
49+
return new TokenList<ExpressionToken>(
50+
Tokenize(textSpan)
51+
.TakeWhile(r => r.HasValue)
52+
.Select(r => new Token<ExpressionToken>(r.Value, r.Location.Until(r.Remainder)))
53+
.ToArray());
5054
}
5155

5256
protected override IEnumerable<Result<ExpressionToken>> Tokenize(TextSpan stringSpan)
53-
{
54-
return _templateSyntax ? TokenizeTemplate(stringSpan) : TokenizeExpression(stringSpan);
55-
}
56-
57-
IEnumerable<Result<ExpressionToken>> TokenizeTemplate(TextSpan stringSpan)
58-
{
59-
var last = stringSpan;
60-
var next = SkipLiteral(stringSpan);
61-
if (!next.HasValue)
62-
yield break;
63-
64-
do
65-
{
66-
yield return Result.Value(ExpressionToken.TemplateLiteral, last, next.Location);
67-
68-
if (next.Value == '{')
69-
{
70-
next = next.Remainder.ConsumeChar();
71-
if (!next.HasValue)
72-
{
73-
yield return Result.Empty<ExpressionToken>(next.Location, "Unexpected end-of-input.");
74-
yield break;
75-
}
76-
77-
if (next.Value == '{')
78-
{
79-
yield return Result.Value(ExpressionToken.TemplateLiteral, next.Location, next.Remainder);
80-
}
81-
else
82-
{
83-
var until = next.Location;
84-
85-
// Here we'll need to contextually apply the parser...
86-
foreach (var tok in TokenizeExpression(next.Location))
87-
{
88-
yield return tok;
89-
until = tok.Remainder;
90-
}
91-
92-
next = until.ConsumeChar();
93-
if (!next.HasValue)
94-
{
95-
yield return Result.Empty<ExpressionToken>(next.Location, "Unexpected end-of-input.");
96-
yield break;
97-
}
98-
}
99-
}
100-
else if (next.Value == '}')
101-
{
102-
103-
}
104-
105-
last = next.Remainder;
106-
next = SkipLiteral(next.Remainder);
107-
} while (next.HasValue);
108-
}
109-
110-
static Result<char> SkipLiteral(TextSpan stringSpan)
111-
{
112-
Result<char> result = stringSpan.ConsumeChar();
113-
while (result.HasValue && result.Value != '{' && result.Value != '}')
114-
result = result.Remainder.ConsumeChar();
115-
return result;
116-
}
117-
118-
IEnumerable<Result<ExpressionToken>> TokenizeExpression(TextSpan stringSpan)
11957
{
12058
var next = SkipWhiteSpace(stringSpan);
12159
if (!next.HasValue)

src/Serilog.Expressions/Templates/Parsing/TemplateParser.cs

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,77 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Linq;
4+
using System.Text;
35
using Serilog.Expressions.Parsing;
46
using Serilog.Templates.Ast;
7+
using Superpower.Model;
58

69
namespace Serilog.Templates.Parsing
710
{
811
static class TemplateParser
912
{
10-
static ExpressionTokenizer Tokenizer { get; } = new ExpressionTokenizer(true);
13+
static ExpressionTokenizer Tokenizer { get; } = new ExpressionTokenizer();
1114

1215
public static Template Parse(string template)
1316
{
14-
var tokens = Tokenizer.Tokenize(template);
15-
17+
if (template == null) throw new ArgumentNullException(nameof(template));
18+
1619
var elements = new List<Template>();
1720

18-
var rest = tokens;
19-
while (!rest.IsAtEnd)
21+
var i = 0;
22+
while (i < template.Length)
2023
{
21-
var peek = rest.ConsumeToken();
22-
if (peek.Value.Kind == ExpressionToken.TemplateLiteral)
24+
var ch = template[i];
25+
26+
if (ch == '{')
2327
{
24-
elements.Add(new LiteralText(peek.Value.ToStringValue()));
25-
rest = peek.Remainder;
28+
i++;
29+
if (i == template.Length)
30+
throw new ArgumentException("Character `{` must be escaped by doubling in literal text.");
31+
32+
if (template[i] == '{')
33+
{
34+
elements.Add(new LiteralText("}"));
35+
i++;
36+
}
37+
else
38+
{
39+
// Not reporting line/column
40+
var tokens = Tokenizer.GreedyTokenize(new TextSpan(template, new Position(i, 0, 0), template.Length - i));
41+
// Dropping error info; may return a zero-length parse
42+
var expr = ExpressionTokenParsers.TryPartialParse(tokens);
43+
// Throw on error; no format parsing
44+
elements.Add(new FormattedExpression(expr.Value, null));
45+
if (expr.Remainder.Position == tokens.Count())
46+
i = tokens.Last().Position.Absolute + tokens.Last().Span.Length;
47+
else
48+
i = tokens.ElementAt(i).Position.Absolute;
49+
50+
if (i == template.Length || template[i] != '}')
51+
throw new ArgumentException("Un-closed hole, `}` expected.");
52+
i++;
53+
}
54+
}
55+
else if (ch == '}')
56+
{
57+
i++;
58+
if (i == template.Length || template[i] != '}')
59+
throw new ArgumentException("Character `}` must be escaped by doubling in literal text.");
60+
elements.Add(new LiteralText("}"));
61+
i++;
2662
}
2763
else
2864
{
29-
var partial = ExpressionTokenParsers.TryPartialParse(rest);
30-
elements.Add(new FormattedExpression(partial.Value, null));
31-
rest = partial.Remainder;
65+
var literal = new StringBuilder();
66+
do
67+
{
68+
literal.Append(template[i]);
69+
i++;
70+
} while (i < template.Length && template[i] != '{' && template[i] != '}');
71+
elements.Add(new LiteralText(literal.ToString()));
3272
}
3373
}
34-
74+
3575
if (elements.Count == 1)
3676
return elements.Single();
3777

0 commit comments

Comments
 (0)