Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve expression trees #199

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Parlot/Compilation/CompilationResult.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

Expand Down
11 changes: 6 additions & 5 deletions src/Parlot/Fluent/Deferred.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,15 @@ public CompilationResult Compile(CompilationContext context)
type: typeof(ValueTuple<bool, T>),
variables: parserCompileResult.Variables.Append(resultExpression),
Expression.Block(parserCompileResult.Body),
Expression.Assign(resultExpression, Expression.New(
typeof(ValueTuple<bool, T>).GetConstructor([typeof(bool), typeof(T)])!,
parserCompileResult.Success,
context.DiscardResult ? Expression.Default(parserCompileResult.Value.Type) : parserCompileResult.Value)),
Expression.Assign(Expression.Field(resultExpression, nameof(ValueTuple<bool, T>.Item1)), parserCompileResult.Success),
context.DiscardResult
? Expression.Empty()
: Expression.Assign(Expression.Field(resultExpression, nameof(ValueTuple<bool, T>.Item2)), parserCompileResult.Value),
returnExpression,
returnLabel),
name: $"Deferred_{context.NextNumber}",
true,
context.ParseContext
[context.ParseContext]
);

// Store the source lambda for debugging
Expand Down
1 change: 0 additions & 1 deletion src/Parlot/Fluent/If.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ public CompilationResult Compile(CompilationContext context)
//

var start = context.DeclarePositionVariable(result);
// parserCompileResult.Success

var block = Expression.Block(
Expression.IfThen(
Expand Down
20 changes: 17 additions & 3 deletions src/Parlot/Fluent/NumberLiteral.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ public NumberLiteral(NumberOptions numberOptions = NumberOptions.Number, char de
_groupSeparator = groupSeparator;
_numberStyles = numberOptions.ToNumberStyles();

if (decimalSeparator != NumberLiterals.DefaultDecimalSeparator ||
groupSeparator != NumberLiterals.DefaultGroupSeparator)
if (_decimalSeparator != NumberLiterals.DefaultDecimalSeparator ||
_groupSeparator != NumberLiterals.DefaultGroupSeparator)
{
_culture = (CultureInfo)CultureInfo.InvariantCulture.Clone();
_culture.NumberFormat.NumberDecimalSeparator = decimalSeparator.ToString();
Expand Down Expand Up @@ -107,11 +107,25 @@ public CompilationResult Compile(CompilationContext context)

var reset = context.DeclarePositionVariable(result);

// Automatically converted to e.g., numberStyles16 = NumberStyles.AllowLeadingSign|NumberStyles.AllowDecimalPoint|NumberStyles.AllowExponent;
var numberStyles = result.DeclareVariable<NumberStyles>($"numberStyles{context.NextNumber}", Expression.Constant(_numberStyles));
var culture = result.DeclareVariable<CultureInfo>($"culture{context.NextNumber}", Expression.Constant(_culture));
var culture = result.DeclareVariable<CultureInfo>($"culture{context.NextNumber}");
var numberSpan = result.DeclareVariable($"number{context.NextNumber}", typeof(ReadOnlySpan<char>));
var end = result.DeclareVariable<int>($"end{context.NextNumber}");

if (_decimalSeparator != NumberLiterals.DefaultDecimalSeparator || _groupSeparator != NumberLiterals.DefaultGroupSeparator)
{
// culture = CultureInfo.InvariantCulture.Clone();
result.Body.Add(Expression.Assign(culture, Expression.Convert(Expression.Call(Expression.Property(null, typeof(CultureInfo), nameof(CultureInfo.InvariantCulture)), methodName: nameof(CultureInfo.Clone), Array.Empty<Type>()), typeof(CultureInfo))));
result.Body.Add(Expression.Assign(Expression.Property(Expression.Property(culture, nameof(CultureInfo.NumberFormat)), nameof(NumberFormatInfo.NumberDecimalSeparator)), Expression.Constant(_decimalSeparator.ToString())));
result.Body.Add(Expression.Assign(Expression.Property(Expression.Property(culture, nameof(CultureInfo.NumberFormat)), nameof(NumberFormatInfo.NumberGroupSeparator)), Expression.Constant(_groupSeparator.ToString())));
}
else
{
// culture = CultureInfo.InvariantCulture;
result.Body.Add(Expression.Assign(culture, Expression.Property(null, typeof(CultureInfo), nameof(CultureInfo.InvariantCulture))));
}

// if (context.Scanner.ReadDecimal(_numberOptions, out var numberSpan, _decimalSeparator, _groupSeparator))
// {
// var end = context.Scanner.Cursor.Offset;
Expand Down
58 changes: 36 additions & 22 deletions src/Parlot/Fluent/NumberLiteralBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public NumberLiteralBase(NumberOptions numberOptions = NumberOptions.Number, cha
_tryParseMethodInfo = tryParseMethodInfo ?? _defaultTryParseMethodInfo;
_numberStyles = numberOptions.ToNumberStyles();

if (decimalSeparator != NumberLiterals.DefaultDecimalSeparator ||
groupSeparator != NumberLiterals.DefaultGroupSeparator)
if (_decimalSeparator != NumberLiterals.DefaultDecimalSeparator ||
_groupSeparator != NumberLiterals.DefaultGroupSeparator)
{
_culture = (CultureInfo)CultureInfo.InvariantCulture.Clone();
_culture.NumberFormat.NumberDecimalSeparator = decimalSeparator.ToString();
Expand Down Expand Up @@ -110,11 +110,25 @@ public CompilationResult Compile(CompilationContext context)

var reset = context.DeclarePositionVariable(result);

// Automatically converted to e.g., numberStyles16 = NumberStyles.AllowLeadingSign|NumberStyles.AllowDecimalPoint|NumberStyles.AllowExponent;
var numberStyles = result.DeclareVariable<NumberStyles>($"numberStyles{context.NextNumber}", Expression.Constant(_numberStyles));
var culture = result.DeclareVariable<CultureInfo>($"culture{context.NextNumber}", Expression.Constant(_culture));
var culture = result.DeclareVariable<CultureInfo>($"culture{context.NextNumber}");
var numberSpan = result.DeclareVariable($"number{context.NextNumber}", typeof(ReadOnlySpan<char>));
var end = result.DeclareVariable<int>($"end{context.NextNumber}");

if (_decimalSeparator != NumberLiterals.DefaultDecimalSeparator || _groupSeparator != NumberLiterals.DefaultGroupSeparator)
{
// culture = CultureInfo.InvariantCulture.Clone();
result.Body.Add(Expression.Assign(culture, Expression.Convert(Expression.Call(Expression.Property(null, typeof(CultureInfo), nameof(CultureInfo.InvariantCulture)), methodName: nameof(CultureInfo.Clone), Array.Empty<Type>()), typeof(CultureInfo))));
result.Body.Add(Expression.Assign(Expression.Property(Expression.Property(culture, nameof(CultureInfo.NumberFormat)), nameof(NumberFormatInfo.NumberDecimalSeparator)), Expression.Constant(_decimalSeparator.ToString())));
result.Body.Add(Expression.Assign(Expression.Property(Expression.Property(culture, nameof(CultureInfo.NumberFormat)), nameof(NumberFormatInfo.NumberGroupSeparator)), Expression.Constant(_groupSeparator.ToString())));
}
else
{
// culture = CultureInfo.InvariantCulture;
result.Body.Add(Expression.Assign(culture, Expression.Property(null, typeof(CultureInfo), nameof(CultureInfo.InvariantCulture))));
}

// if (context.Scanner.ReadDecimal(_numberOptions, out var numberSpan, _decimalSeparator, _groupSeparator))
// {
// var end = context.Scanner.Cursor.Offset;
Expand All @@ -129,25 +143,25 @@ public CompilationResult Compile(CompilationContext context)
//

var block =
Expression.IfThen(
context.ReadDecimal(Expression.Constant(_allowLeadingSign),
Expression.Constant(_allowDecimalSeparator),
Expression.Constant(_allowGroupSeparator),
Expression.Constant(_allowExponent),
numberSpan, Expression.Constant(_decimalSeparator), Expression.Constant(_groupSeparator)),
Expression.Block(
Expression.Assign(end, context.Offset()),
Expression.Assign(result.Success,
Expression.Call(
_tryParseMethodInfo,
// This class is only used before NET7.0, when there is no overload for TryParse that takes a ReadOnlySpan<char>
Expression.Call(numberSpan, ExpressionHelper.ReadOnlySpan_ToString),
numberStyles,
culture,
result.Value)
)
)
);
Expression.IfThen(
context.ReadDecimal(Expression.Constant(_allowLeadingSign),
Expression.Constant(_allowDecimalSeparator),
Expression.Constant(_allowGroupSeparator),
Expression.Constant(_allowExponent),
numberSpan, Expression.Constant(_decimalSeparator), Expression.Constant(_groupSeparator)),
Expression.Block(
Expression.Assign(end, context.Offset()),
Expression.Assign(result.Success,
Expression.Call(
_tryParseMethodInfo,
// This class is only used before NET7.0, when there is no overload for TryParse that takes a ReadOnlySpan<char>
Expression.Call(numberSpan, ExpressionHelper.ReadOnlySpan_ToString),
numberStyles,
culture,
result.Value)
)
)
);

result.Body.Add(block);

Expand Down
9 changes: 4 additions & 5 deletions src/Parlot/Fluent/Parser.Compile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace Parlot.Fluent;

public abstract partial class Parser<T>
{
private static readonly ConstructorInfo _valueTupleConstructor = typeof(ValueTuple<bool, T>).GetConstructor([typeof(bool), typeof(T)])!;

/// <summary>
/// Compiles the current parser.
/// </summary>
Expand Down Expand Up @@ -38,7 +35,8 @@ public Parser<T> Compile()
compilationResult.Variables.Add(resultExpression);
compilationResult.Body.Add(
Expression.Block(
Expression.Assign(resultExpression, Expression.New(_valueTupleConstructor, compilationResult.Success, compilationResult.Value)),
Expression.Assign(Expression.Field(resultExpression, nameof(ValueTuple<bool, T>.Item1)), compilationResult.Success),
Expression.Assign(Expression.Field(resultExpression, nameof(ValueTuple<bool, T>.Item2)), compilationResult.Value),
returnExpression,
returnLabel
)
Expand All @@ -58,6 +56,7 @@ public Parser<T> Compile()

var allExpressions = new List<Expression>();
allExpressions.AddRange(compilationContext.GlobalExpressions);
//allExpressions.AddRange(compilationContext.Lambdas);
allExpressions.AddRange(compilationResult.Body);

var body = Expression.Block(
Expand All @@ -66,7 +65,7 @@ public Parser<T> Compile()
allExpressions
);

var result = Expression.Lambda<Func<ParseContext, ValueTuple<bool, T>>>(body, compilationContext.ParseContext);
var result = Expression.Lambda<Func<ParseContext, ValueTuple<bool, T>>>(body, name: $"Parse_{compilationContext.NextNumber}", parameters: [compilationContext.ParseContext]);

// In Debug mode, inspect the generated code with
// result.ToCSharpString();
Expand Down
14 changes: 14 additions & 0 deletions test/Parlot.Tests/Calc/FluentParserCompiledTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Parlot.Fluent;

namespace Parlot.Tests.Calc;

public class FluentParserCompiledTests : CalcTests
{
static Parser<Expression> _compiled = FluentParser.Expression.Compile();

protected override decimal Evaluate(string text)
{
_compiled.TryParse(text, out var expression);
return expression.Evaluate();
}
}
3 changes: 3 additions & 0 deletions test/Parlot.Tests/CompileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,9 @@ public void CompiledWhenShouldResetPositionWhenFalse()
[Fact]
public void CompiledIfShouldNotInvokeParserWhenFalse()
{
// Add a test to ensure state can be a custom object (Expression.Constant might not work with that)
Assert.False(true);

bool invoked = false;

var evenState = If(predicate: (context, x) => x % 2 == 0, state: 0, parser: Literals.Integer().Then(x => invoked = true)).Compile();
Expand Down
Loading