Skip to content

Commit

Permalink
WIP(?)
Browse files Browse the repository at this point in the history
  • Loading branch information
leandromoh committed Mar 23, 2024
1 parent 10664b7 commit 403a526
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 83 deletions.
2 changes: 1 addition & 1 deletion RecordParser.Test/FixedLengthReaderBuilderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void Given_factory_method_should_invoke_it_on_parse()
.Map(x => x.Money, 23, 7)
.Build(factory: () => { called++; return (default, date, default); });

var result = reader.Parse("foo bar baz yyyy.MM.dd 0123.45");
var result = reader.Parse("foo bar baz yyyy.MM.dd 0123.45".AsMemory());

called.Should().Be(1);

Expand Down
19 changes: 15 additions & 4 deletions RecordParser/Builders/Reader/FixedLengthReaderBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using RecordParser.Engines.Reader;
using AgileObjects.ReadableExpressions;
using RecordParser.Engines.Reader;
using RecordParser.Parsers;
using RecordParser.Visitors;
using System;
Expand Down Expand Up @@ -95,12 +96,22 @@ public IFixedLengthReader<T> Build(CultureInfo cultureInfo = null, Func<T> facto
{
var map = MappingReadConfiguration.Merge(list, dic);
var func = ReaderEngine.RecordParserSpanFlat(map, factory);
var func2 = ReaderEngine.RecordParserSpanFlatAOT(map, factory);

func = CultureInfoVisitor.ReplaceCulture(func, cultureInfo);
func2 = CultureInfoVisitor.ReplaceCulture(func2, cultureInfo);
var saf = func2.ToReadableString();

var memory = new SpanReplacerVisitor().Modify(func);

return new FixedLengthReader<T>(func.Compile(true), memory.Compile(true));
try
{
var f1 = func.Compile(true);
var f2 = func2.Compile(true);
return new FixedLengthReader<T>(f1, f2);
}
catch (Exception ex)

Check warning on line 111 in RecordParser/Builders/Reader/FixedLengthReaderBuilder.cs

View workflow job for this annotation

GitHub Actions / dotnet test

The variable 'ex' is declared but never used
{
throw;
}
}
}
}
7 changes: 5 additions & 2 deletions RecordParser/Engines/ExpressionHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using RecordParser.Visitors;
using System;
using System.Linq.Expressions;

namespace RecordParser.Engines
Expand All @@ -15,7 +16,9 @@ internal static class ExpressionHelper
Expression.Call(span, "ToString", Type.EmptyTypes);

public static Expression Trim(Expression str) =>
Expression.Call(typeof(MemoryExtensions), "Trim", Type.EmptyTypes, str);
str.Type == typeof(ReadOnlySpan<char>)
? Expression.Call(typeof(MemoryExtensions), "Trim", Type.EmptyTypes, str)
: Expression.Call(str, nameof(Foo.Trim), Type.EmptyTypes);

public static Expression Slice(Expression span, Expression start) =>
Expression.Call(span, "Slice", Type.EmptyTypes, start);
Expand Down
2 changes: 1 addition & 1 deletion RecordParser/Engines/Reader/PrimitiveTypeReaderEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ static PrimitiveTypeReaderEngine()
dic = mapping;
}

private static char ToChar(ReadOnlySpan<char> span) => span[0];
public static char ToChar(ReadOnlySpan<char> span) => span[0];

private static void AddMapForReadOnlySpan<T>(
this IDictionary<(Type, Type), Func<Type, Expression, Expression>> dic,
Expand Down
55 changes: 50 additions & 5 deletions RecordParser/Engines/Reader/ReaderEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@ public static Expression<FuncSpanArrayT<T>> RecordParserSpanCSV<T>(IEnumerable<M
return result;
}

public static Expression<Func<Foo, T>> RecordParserSpanFlatAOT<T>(IEnumerable<MappingReadConfiguration> mappedColumns, Func<T> factory)
{
// parameters
var line = Expression.Parameter(typeof(Foo), "span");

// variables
var instanceVariable = Expression.Variable(typeof(T), "inst");

var blockThatSetProperties = MountSetProperties(instanceVariable, mappedColumns, (i, mapConfig) =>
{
return Slice(line, Expression.Constant(mapConfig.start), Expression.Constant(mapConfig.length.Value));
}, true);

var body = MountBody(instanceVariable, blockThatSetProperties, mappedColumns, factory);
var result = Expression.Lambda<Func<Foo, T>>(body, line);

return result;
}

public static Expression<FuncSpanT<T>> RecordParserSpanFlat<T>(IEnumerable<MappingReadConfiguration> mappedColumns, Func<T> factory)
{
// parameters
Expand All @@ -54,9 +73,9 @@ public static Expression<FuncSpanT<T>> RecordParserSpanFlat<T>(IEnumerable<Mappi
}

private static BlockExpression MountBody<T>(
ParameterExpression instanceVariable,
BlockExpression blockThatSetProperties,
IEnumerable<MappingReadConfiguration> mappedColumns,
ParameterExpression instanceVariable,
BlockExpression blockThatSetProperties,
IEnumerable<MappingReadConfiguration> mappedColumns,
Func<T> factory)
{
var getNewInstance = factory != null
Expand All @@ -75,12 +94,15 @@ public static Expression<FuncSpanT<T>> RecordParserSpanFlat<T>(IEnumerable<Mappi
private static BlockExpression MountSetProperties(
ParameterExpression objectParameter,
IEnumerable<MappingReadConfiguration> mappedColumns,
Func<int, MappingReadConfiguration, Expression> getTextValue)
Func<int, MappingReadConfiguration, Expression> getTextValue,
bool AOT = false)
{
var replacer = new ParameterReplacerVisitor(objectParameter);
var assignsExpressions = new List<Expression>();
var i = -1;

Func<Type, Expression, Delegate, Expression> resolve = AOT ? GetValueToBeSetExpressionAOT : GetValueToBeSetExpression;

foreach (var x in mappedColumns)
{
i++;
Expand All @@ -95,7 +117,7 @@ public static Expression<FuncSpanT<T>> RecordParserSpanFlat<T>(IEnumerable<Mappi
var isPropertyNullable = nullableUnderlyingType != null;
var propertyUnderlyingType = nullableUnderlyingType ?? propertyType;

Expression valueToBeSetExpression = GetValueToBeSetExpression(
Expression valueToBeSetExpression = resolve(
propertyUnderlyingType,
textValue,
x.fmask);
Expand Down Expand Up @@ -137,5 +159,28 @@ private static Expression GetValueToBeSetExpression(Type propertyType, Expressio

throw new InvalidOperationException($"Type '{propertyType.FullName}' does not have a default parse");
}

private static Expression GetValueToBeSetExpressionAOT(Type propertyType, Expression valueText, Delegate func)
{
if (func != null)
{
var fun = Expression.Constant(func);

return Expression.Call(valueText, nameof(Foo.AsCustom), new[] { func.Method.ReturnType }, fun);
}

if (propertyType.IsEnum)
{
return Expression.Call(valueText, nameof(Foo.AsEnum), new[] { propertyType });
}

var methodName = "As" + propertyType.Name;
var method = typeof(Foo).GetMethod(methodName);

if (method == null)
throw new InvalidOperationException($"Type '{propertyType.FullName}' does not have a default parse");

return Expression.Call(valueText, method);
}
}
}
9 changes: 5 additions & 4 deletions RecordParser/Parsers/FixedLengthReader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using RecordParser.Visitors;
using System;

namespace RecordParser.Parsers
{
Expand All @@ -12,17 +13,17 @@ public interface IFixedLengthReader<T>
internal class FixedLengthReader<T> : IFixedLengthReader<T>
{
private readonly FuncSpanT<T> parser;
private readonly Func<ReadOnlyMemory<char>, T> parser2;
private readonly Func<Foo, T> parser2;

internal FixedLengthReader(FuncSpanT<T> parser, Func<ReadOnlyMemory<char>, T> parser2)
internal FixedLengthReader(FuncSpanT<T> parser, Func<Foo, T> parser2)
{
this.parser = parser;
this.parser2 = parser2;
}

public T Parse(ReadOnlyMemory<char> line)
{
return parser2(line);
return parser2(new Foo(line));
}

public T Parse(ReadOnlySpan<char> line)
Expand Down
11 changes: 6 additions & 5 deletions RecordParser/RecordParser.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</PropertyGroup>

<ItemGroup>
<None Include="..\LICENSE.md" Pack="true" PackagePath="LICENSE.md"/>
<None Include="..\LICENSE.md" Pack="true" PackagePath="LICENSE.md" />
</ItemGroup>

<ItemGroup>
Expand All @@ -36,10 +36,11 @@
</AssemblyAttribute>
</ItemGroup>

<PropertyGroup Condition="
( '$(Configuration)' == 'Debug' OR '$(Configuration)' == 'Release') AND
( '$(TargetFramework)' == 'net6.0' OR '$(TargetFramework)' == 'net7.0' OR '$(TargetFramework)' == 'net8.0') AND
( '$(Platform)' == 'AnyCPU') ">
<ItemGroup>
<PackageReference Include="AgileObjects.ReadableExpressions" Version="4.1.1" />
</ItemGroup>

<PropertyGroup Condition="&#xD;&#xA; ( '$(Configuration)' == 'Debug' OR '$(Configuration)' == 'Release') AND&#xD;&#xA; ( '$(TargetFramework)' == 'net6.0' OR '$(TargetFramework)' == 'net7.0' OR '$(TargetFramework)' == 'net8.0') AND&#xD;&#xA; ( '$(Platform)' == 'AnyCPU') ">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

Expand Down

0 comments on commit 403a526

Please sign in to comment.