Skip to content

Commit f9f1758

Browse files
committed
WIP template parsing
1 parent 0737469 commit f9f1758

17 files changed

+362
-3
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace Serilog.Expressions.Parsing
55
{
66
static class ExpressionParser
77
{
8+
static ExpressionTokenizer Tokenizer { get; } = new ExpressionTokenizer(false);
9+
810
public static Expression Parse(string filterExpression)
911
{
1012
if (!TryParse(filterExpression, out var root, out var error))
@@ -17,7 +19,7 @@ public static bool TryParse(string filterExpression, out Expression root, out st
1719
{
1820
if (filterExpression == null) throw new ArgumentNullException(nameof(filterExpression));
1921

20-
var tokenList = ExpressionTokenizer.Instance.TryTokenize(filterExpression);
22+
var tokenList = Tokenizer.TryTokenize(filterExpression);
2123
if (!tokenList.HasValue)
2224
{
2325
error = tokenList.ToString();

src/Serilog.Expressions/Expressions/Parsing/ExpressionToken.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ namespace Serilog.Expressions.Parsing
55
enum ExpressionToken
66
{
77
None,
8+
9+
[Token(Description = "literal text")]
10+
TemplateLiteral,
811

912
Identifier,
1013

src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenParsers.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ public static TokenListParserResult<ExpressionToken, Expression> TryParse(
1717
return Expr.AtEnd().TryParse(input);
1818
}
1919

20+
public static TokenListParserResult<ExpressionToken, Expression> TryPartialParse(
21+
TokenList<ExpressionToken> input)
22+
{
23+
return Expr.TryParse(input);
24+
}
25+
2026
static readonly TokenListParser<ExpressionToken, string> Add = Token.EqualTo(ExpressionToken.Plus).Value(Operators.OpAdd);
2127
static readonly TokenListParser<ExpressionToken, string> Subtract = Token.EqualTo(ExpressionToken.Minus).Value(Operators.OpSubtract);
2228
static readonly TokenListParser<ExpressionToken, string> Multiply = Token.EqualTo(ExpressionToken.Asterisk).Value(Operators.OpMultiply);

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

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ namespace Serilog.Expressions.Parsing
66
{
77
class ExpressionTokenizer : Tokenizer<ExpressionToken>
88
{
9+
readonly bool _templateSyntax;
10+
911
static readonly ExpressionToken[] SimpleOps = new ExpressionToken[128];
1012

1113
static readonly ExpressionKeyword[] Keywords =
@@ -42,7 +44,78 @@ static ExpressionTokenizer()
4244
SimpleOps['?'] = ExpressionToken.QuestionMark;
4345
}
4446

47+
public ExpressionTokenizer(bool templateSyntax)
48+
{
49+
_templateSyntax = templateSyntax;
50+
}
51+
4552
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)
46119
{
47120
var next = SkipWhiteSpace(stringSpan);
48121
if (!next.HasValue)
@@ -166,7 +239,5 @@ static bool TryGetKeyword(TextSpan span, out ExpressionToken keyword)
166239
keyword = ExpressionToken.None;
167240
return false;
168241
}
169-
170-
public static ExpressionTokenizer Instance { get; } = new ExpressionTokenizer();
171242
}
172243
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using Serilog.Expressions.Ast;
3+
4+
namespace Serilog.Templates.Ast
5+
{
6+
class FormattedExpression : Template
7+
{
8+
public Expression Expression { get; }
9+
public string Format { get; }
10+
11+
public FormattedExpression(Expression expression, string format)
12+
{
13+
Expression = expression ?? throw new ArgumentNullException(nameof(expression));
14+
Format = format;
15+
}
16+
}
17+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
3+
namespace Serilog.Templates.Ast
4+
{
5+
class LiteralText : Template
6+
{
7+
public string Text { get; }
8+
9+
public LiteralText(string text)
10+
{
11+
Text = text ?? throw new ArgumentNullException(nameof(text));
12+
}
13+
}
14+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Serilog.Templates.Ast
2+
{
3+
abstract class Template
4+
{
5+
}
6+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
3+
namespace Serilog.Templates.Ast
4+
{
5+
class TemplateBlock : Template
6+
{
7+
public Template[] Elements { get; }
8+
9+
public TemplateBlock(Template[] elements)
10+
{
11+
Elements = elements ?? throw new ArgumentNullException(nameof(elements));
12+
}
13+
}
14+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.IO;
3+
using Serilog.Events;
4+
using Serilog.Expressions;
5+
6+
namespace Serilog.Templates.Compilation
7+
{
8+
class CompiledFormattedExpression : CompiledTemplate
9+
{
10+
readonly CompiledExpression _expression;
11+
readonly string _format;
12+
13+
public CompiledFormattedExpression(CompiledExpression expression, string format)
14+
{
15+
_expression = expression ?? throw new ArgumentNullException(nameof(expression));
16+
_format = format;
17+
}
18+
19+
public override void Evaluate(LogEvent logEvent, TextWriter output, IFormatProvider formatProvider)
20+
{
21+
var value = _expression(logEvent);
22+
value?.Render(output, _format, formatProvider);
23+
}
24+
}
25+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.IO;
3+
using Serilog.Events;
4+
5+
namespace Serilog.Templates.Compilation
6+
{
7+
class CompiledLiteralText : CompiledTemplate
8+
{
9+
readonly string _text;
10+
11+
public CompiledLiteralText(string text)
12+
{
13+
_text = text ?? throw new ArgumentNullException(nameof(text));
14+
}
15+
16+
public override void Evaluate(LogEvent logEvent, TextWriter output, IFormatProvider formatProvider)
17+
{
18+
output.Write(_text);
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)