Skip to content

Commit d63a4d4

Browse files
committed
wip caching.
1 parent 959382b commit d63a4d4

4 files changed

Lines changed: 99 additions & 89 deletions

File tree

NTDLS.ExpressionParser/ExpressionState.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
using Microsoft.Extensions.Caching.Memory;
2-
using Microsoft.Extensions.Options;
32
using System.Diagnostics.CodeAnalysis;
43
using System.Runtime.CompilerServices;
54
using System.Text;
6-
using static System.Net.Mime.MediaTypeNames;
75

86
namespace NTDLS.ExpressionParser
97
{
@@ -15,12 +13,12 @@ internal class ExpressionState
1513
private int _nextComputedStepSlot = 0;
1614
private int _consumedComputedStepSlots = 0;
1715
private ComputedStepItem[] _computedStep = [];
18-
private bool _isTemplateCacheHydrated = false;
1916

2017
private int _nextScanStepSlot = 0;
2118
private int _consumedScanStepSlots = 0;
2219
private ScanStepItem[] _scanStep = [];
23-
private bool _isScanStepHydrated = false;
20+
21+
private bool _isTemplateCacheHydrated = false;
2422

2523
private PlaceholderCacheItem[] _placeholderCache = [];
2624
private int _nextPlaceholderCacheSlot = 0;
@@ -39,7 +37,7 @@ public ExpressionState(Sanitized sanitized, ExpressionOptions options)
3937
_computedStep = new ComputedStepItem[_operationCount];
4038
_nextComputedStepSlot = 0;
4139

42-
_scanStep = new ScanStepItem[_operationCount];
40+
_scanStep = new ScanStepItem[_operationCount * 3];
4341
_nextScanStepSlot = 0;
4442

4543
for (int i = 0; i < sanitized.ConsumedPlaceholderCacheSlots; i++)
@@ -217,6 +215,7 @@ public void Reset(Sanitized sanitized)
217215
WorkingText = sanitized.Text;
218216
_nextPlaceholderCacheSlot = sanitized.ConsumedPlaceholderCacheSlots;
219217
_nextComputedStepSlot = 0;
218+
_nextScanStepSlot = 0;
220219
}
221220

222221
public ExpressionState Clone(Sanitized sanitized)

NTDLS.ExpressionParser/SubExpression.cs

Lines changed: 82 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -136,22 +136,22 @@ internal string Compute(bool isCacheable)
136136
{
137137
TruncateParenthesizes();
138138

139+
//Process all function calls from right-to-left.
139140
while (true)
140141
{
141-
int operatorIndex;
142-
143-
//Process all function calls from right-to-left.
144-
while (true)
142+
if (ProcessFunctionCall())
145143
{
146-
if (ProcessFunctionCall())
147-
{
148-
isCacheable = false;
149-
}
150-
else
151-
{
152-
break;
153-
}
144+
isCacheable = false;
154145
}
146+
else
147+
{
148+
break;
149+
}
150+
}
151+
152+
while (true)
153+
{
154+
int operatorIndex;
155155

156156
//Pre-first-order:
157157
while ((operatorIndex = GetFreestandingNotOperation(out _)) != -1)
@@ -187,18 +187,14 @@ internal string Compute(bool isCacheable)
187187
operatorIndex = GetIndexOfOperation(Utility.FirstOrderOperations, out string operation);
188188
if (operatorIndex > 0)
189189
{
190+
var calculatedResult = GetLeftAndRightValues(operation, operatorIndex, out double leftValue, out double rightValue, out int beginPosition, out int endPosition, out bool isOperationCacheable);
191+
190192
if (_parentExpression.State.TryGetComputedStep(out ComputedStepItem cachedObj))
191193
{
192194
StorePreComputed(cachedObj.BeginPosition, cachedObj.EndPosition, cachedObj.ParsedValue);
193195
}
194196
else
195197
{
196-
double? calculatedResult = null;
197-
if (GetLeftAndRightValues(operation, operatorIndex, out double leftValue, out double rightValue, out int beginPosition, out int endPosition, out bool isOperationCacheable))
198-
{
199-
calculatedResult = Utility.ComputePrivative(leftValue, operation, rightValue);
200-
}
201-
202198
StorePreComputed(beginPosition, endPosition, calculatedResult);
203199
if (isCacheable && isOperationCacheable)
204200
{
@@ -223,18 +219,14 @@ internal string Compute(bool isCacheable)
223219
operatorIndex = GetIndexOfOperation(Utility.SecondOrderOperations, out operation);
224220
if (operatorIndex > 0)
225221
{
222+
var calculatedResult = GetLeftAndRightValues(operation, operatorIndex, out double leftValue, out double rightValue, out int beginPosition, out int endPosition, out bool isOperationCacheable);
223+
226224
if (_parentExpression.State.TryGetComputedStep(out ComputedStepItem cachedObj))
227225
{
228226
StorePreComputed(cachedObj.BeginPosition, cachedObj.EndPosition, cachedObj.ParsedValue);
229227
}
230228
else
231229
{
232-
double? calculatedResult = null;
233-
if (GetLeftAndRightValues(operation, operatorIndex, out double leftValue, out double rightValue, out int beginPosition, out int endPosition, out bool isOperationCacheable))
234-
{
235-
calculatedResult = Utility.ComputePrivative(leftValue, operation, rightValue);
236-
}
237-
238230
StorePreComputed(beginPosition, endPosition, calculatedResult);
239231
if (isCacheable && isOperationCacheable)
240232
{
@@ -258,19 +250,14 @@ internal string Compute(bool isCacheable)
258250
operatorIndex = GetIndexOfOperation(Utility.ThirdOrderOperations, out operation);
259251
if (operatorIndex > 0)
260252
{
253+
var calculatedResult = GetLeftAndRightValues(operation, operatorIndex, out double leftValue, out double rightValue, out int beginPosition, out int endPosition, out bool isOperationCacheable);
254+
261255
if (_parentExpression.State.TryGetComputedStep(out ComputedStepItem cachedObj))
262256
{
263257
StorePreComputed(cachedObj.BeginPosition, cachedObj.EndPosition, cachedObj.ParsedValue);
264258
}
265259
else
266260
{
267-
double? calculatedResult = null;
268-
269-
if (GetLeftAndRightValues(operation, operatorIndex, out double leftValue, out double rightValue, out int beginPosition, out int endPosition, out bool isOperationCacheable))
270-
{
271-
calculatedResult = Utility.ComputePrivative(leftValue, operation, rightValue);
272-
}
273-
274261
StorePreComputed(beginPosition, endPosition, calculatedResult);
275262
if (isCacheable && isOperationCacheable)
276263
{
@@ -323,7 +310,7 @@ internal void TruncateParenthesizes()
323310
/// Gets the numbers to the left and right of an operator.
324311
/// Returns FALSE when NULL is found for either value.
325312
/// </summary>
326-
private bool GetLeftAndRightValues(string operation,
313+
private double? GetLeftAndRightValues(string operation,
327314
int operationBeginIndex, out double leftValue, out double rightValue, out int beginPosition, out int endPosition, out bool isCacheable)
328315
{
329316
var left = GetLeftValue(operationBeginIndex, out int leftParsedLength, out bool isLeftCacheable);
@@ -337,66 +324,87 @@ private bool GetLeftAndRightValues(string operation,
337324

338325
isCacheable = isLeftCacheable && isRightCacheable;
339326

340-
return left != null && right != null;
327+
if (left != null && right != null)
328+
{
329+
return Utility.ComputePrivative(leftValue, operation, rightValue);
330+
}
331+
332+
return null;
341333
}
342334

343335
private double? GetLeftValue(int operationIndex, out int outParsedLength, out bool isCacheable)
344336
{
345-
var span = Text.AsSpan(0, operationIndex);
346-
347-
int i = operationIndex - 1;
348-
349-
if (span[i] == '$')
337+
if (_parentExpression.State.TryGetScanStep(out var cachedObj))
350338
{
351-
i--; //Skip the cache indicator.
352-
while (span[i] != '$')
353-
{
354-
i--;
355-
}
356-
i--;
357-
outParsedLength = (operationIndex - i) - 1;
358-
var cacheKey = span.Slice(operationIndex - outParsedLength + 1, outParsedLength - 2);
359-
var cachedItem = _parentExpression.State.GetPlaceholderCacheItem(cacheKey);
360-
isCacheable = false;
361-
return cachedItem.ComputedValue;
339+
outParsedLength = cachedObj.Length;
340+
isCacheable = true;
341+
return cachedObj.Value;
362342
}
363343
else
364344
{
365-
while (i > -1 && ((span[i] - '0' >= 0 && span[i] - '0' <= 9) || span[i] == '.'))
345+
var span = Text.AsSpan(0, operationIndex);
346+
347+
int i = operationIndex - 1;
348+
349+
if (span[i] == '$')
366350
{
351+
i--; //Skip the cache indicator.
352+
while (span[i] != '$')
353+
{
354+
i--;
355+
}
367356
i--;
357+
outParsedLength = (operationIndex - i) - 1;
358+
var cacheKey = span.Slice(operationIndex - outParsedLength + 1, outParsedLength - 2);
359+
var cachedItem = _parentExpression.State.GetPlaceholderCacheItem(cacheKey);
360+
isCacheable = false;
361+
_parentExpression.State.IncrementScanStep();
362+
return cachedItem.ComputedValue;
368363
}
369-
370-
//Check for explicit positive or negative sign if the number is not at the start of the expression.
371-
if (i == 0 && (span[i] == '-' || span[i] == '+'))
364+
else
372365
{
373-
i--; //Skip the explicit positive or negative sign or cache indicator.
374-
}
366+
while (i > -1 && ((span[i] - '0' >= 0 && span[i] - '0' <= 9) || span[i] == '.'))
367+
{
368+
i--;
369+
}
375370

376-
//Check for explicit positive or negative sign when the preceding character is a math character.
377-
if (i > 0 && Utility.IsMathChar(span[i - 1]) && (span[i] == '-' || span[i] == '+'))
378-
{
379-
i--; //Skip the explicit positive or negative sign or cache indicator.
380-
}
371+
//Check for explicit positive or negative sign if the number is not at the start of the expression.
372+
if (i == 0 && (span[i] == '-' || span[i] == '+'))
373+
{
374+
i--; //Skip the explicit positive or negative sign or cache indicator.
375+
}
381376

382-
outParsedLength = (operationIndex - i) - 1;
383-
isCacheable = true;
384-
return _parentExpression.StringToDouble(span.Slice(operationIndex - outParsedLength, outParsedLength));
377+
//Check for explicit positive or negative sign when the preceding character is a math character.
378+
if (i > 0 && Utility.IsMathChar(span[i - 1]) && (span[i] == '-' || span[i] == '+'))
379+
{
380+
i--; //Skip the explicit positive or negative sign or cache indicator.
381+
}
382+
383+
outParsedLength = (operationIndex - i) - 1;
384+
isCacheable = true;
385+
var result = _parentExpression.StringToDouble(span.Slice(operationIndex - outParsedLength, outParsedLength));
386+
387+
_parentExpression.State.StoreScanStep(new ScanStepItem
388+
{
389+
Value = result,
390+
Length = outParsedLength
391+
});
392+
393+
return result;
394+
}
385395
}
386396
}
387397

388398
private double? GetRightValue(int endOfOperationIndex, out int outParsedLength, out bool isCacheable)
389399
{
390-
/*
391-
if (_parentExpression.State.TryGetComputedStep(out ComputedStepItem cachedObj))
400+
if (_parentExpression.State.TryGetScanStep(out var cachedObj))
392401
{
393402
outParsedLength = cachedObj.Length;
394-
isCacheable = false;
395-
return cachedObj.ParsedValue;
403+
isCacheable = true;
404+
return cachedObj.Value;
396405
}
397406
else
398407
{
399-
*/
400408
var span = Text.AsSpan(endOfOperationIndex);
401409

402410
int i = 0;
@@ -412,7 +420,7 @@ private bool GetLeftAndRightValues(string operation,
412420
outParsedLength = i;
413421
var cachedItem = _parentExpression.State.GetPlaceholderCacheItem(span.Slice(1, i - 2));
414422
isCacheable = false;
415-
//_parentExpression.State.IncrementComputedStep();
423+
_parentExpression.State.IncrementScanStep();
416424
return cachedItem.ComputedValue;
417425
}
418426
else
@@ -430,18 +438,16 @@ private bool GetLeftAndRightValues(string operation,
430438
outParsedLength = i;
431439
isCacheable = true;
432440
var result = _parentExpression.StringToDouble(span.Slice(0, i));
433-
/*
434-
_parentExpression.State.StoreComputedStep(new ComputedStepItem
441+
442+
_parentExpression.State.StoreScanStep(new ScanStepItem
435443
{
436-
ParsedValue = result,
437-
//BeginPosition = endOfOperationIndex,
438-
//EndPosition = endOfOperationIndex + outParsedLength,
444+
Value = result,
439445
Length = outParsedLength
440446
});
441-
*/
447+
442448
return result;
443449
}
444-
//}
450+
}
445451
}
446452

447453
[MethodImpl(MethodImplOptions.AggressiveInlining)]

NTDLS.ExpressionParser/Types.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ internal struct ComputedStepItem
1616
internal struct ScanStepItem
1717
{
1818
public bool IsValid;
19-
public double? ParsedValue;
19+
public double? Value;
2020
public int Length;
2121
}
2222

TestHarness/Program.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@ static void Baseline()
2929

3030
static void Main()
3131
{
32-
var expression = new Expression("10 * ((5 + extra + ( 10 )) * 60.5) * 10");
33-
34-
expression.SetParameter("extra", 10);
35-
Console.WriteLine(expression.Evaluate()); //151250
36-
37-
expression.SetParameter("extra", 20);
38-
Console.WriteLine(expression.Evaluate()); //211750
32+
var expression = new Expression("1+2+3");
33+
Console.WriteLine("---FIRST---");
34+
Console.WriteLine(expression.Evaluate()); //20
35+
Console.WriteLine("---SECOND---");
36+
Console.WriteLine(expression.Evaluate()); //20
37+
//Console.WriteLine(expression.Evaluate()); //20
38+
39+
//expression.SetParameter("extra", 10);
40+
//Console.WriteLine(expression.Evaluate()); //151250
41+
42+
//expression.SetParameter("extra", 20);
43+
//Console.WriteLine(expression.Evaluate()); //211750
3944

4045
#if !DEBUG
4146
var timings = new List<double>();

0 commit comments

Comments
 (0)