From 403a526c330fed39ebb61f0679b2e93c50c21908 Mon Sep 17 00:00:00 2001 From: Leandro Fernandes Vieira Date: Sat, 23 Mar 2024 11:49:59 -0300 Subject: [PATCH] WIP(?) --- .../FixedLengthReaderBuilderTest.cs | 2 +- .../Reader/FixedLengthReaderBuilder.cs | 19 +- RecordParser/Engines/ExpressionHelper.cs | 7 +- .../Reader/PrimitiveTypeReaderEngine.cs | 2 +- RecordParser/Engines/Reader/ReaderEngine.cs | 55 ++++- RecordParser/Parsers/FixedLengthReader.cs | 9 +- RecordParser/RecordParser.csproj | 11 +- RecordParser/Visitors/MemoryVisitor.cs | 195 ++++++++++++------ 8 files changed, 217 insertions(+), 83 deletions(-) diff --git a/RecordParser.Test/FixedLengthReaderBuilderTest.cs b/RecordParser.Test/FixedLengthReaderBuilderTest.cs index 3354192..6cb39c9 100644 --- a/RecordParser.Test/FixedLengthReaderBuilderTest.cs +++ b/RecordParser.Test/FixedLengthReaderBuilderTest.cs @@ -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); diff --git a/RecordParser/Builders/Reader/FixedLengthReaderBuilder.cs b/RecordParser/Builders/Reader/FixedLengthReaderBuilder.cs index 67bcf27..e2411ec 100644 --- a/RecordParser/Builders/Reader/FixedLengthReaderBuilder.cs +++ b/RecordParser/Builders/Reader/FixedLengthReaderBuilder.cs @@ -1,4 +1,5 @@ -using RecordParser.Engines.Reader; +using AgileObjects.ReadableExpressions; +using RecordParser.Engines.Reader; using RecordParser.Parsers; using RecordParser.Visitors; using System; @@ -95,12 +96,22 @@ public IFixedLengthReader Build(CultureInfo cultureInfo = null, Func 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(func.Compile(true), memory.Compile(true)); + try + { + var f1 = func.Compile(true); + var f2 = func2.Compile(true); + return new FixedLengthReader(f1, f2); + } + catch (Exception ex) + { + throw; + } } } } diff --git a/RecordParser/Engines/ExpressionHelper.cs b/RecordParser/Engines/ExpressionHelper.cs index af3171f..9c0b051 100644 --- a/RecordParser/Engines/ExpressionHelper.cs +++ b/RecordParser/Engines/ExpressionHelper.cs @@ -1,4 +1,5 @@ -using System; +using RecordParser.Visitors; +using System; using System.Linq.Expressions; namespace RecordParser.Engines @@ -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) + ? 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); diff --git a/RecordParser/Engines/Reader/PrimitiveTypeReaderEngine.cs b/RecordParser/Engines/Reader/PrimitiveTypeReaderEngine.cs index 1f1c0b1..5d0eeb7 100644 --- a/RecordParser/Engines/Reader/PrimitiveTypeReaderEngine.cs +++ b/RecordParser/Engines/Reader/PrimitiveTypeReaderEngine.cs @@ -47,7 +47,7 @@ static PrimitiveTypeReaderEngine() dic = mapping; } - private static char ToChar(ReadOnlySpan span) => span[0]; + public static char ToChar(ReadOnlySpan span) => span[0]; private static void AddMapForReadOnlySpan( this IDictionary<(Type, Type), Func> dic, diff --git a/RecordParser/Engines/Reader/ReaderEngine.cs b/RecordParser/Engines/Reader/ReaderEngine.cs index bb69069..d462aa5 100644 --- a/RecordParser/Engines/Reader/ReaderEngine.cs +++ b/RecordParser/Engines/Reader/ReaderEngine.cs @@ -34,6 +34,25 @@ public static Expression> RecordParserSpanCSV(IEnumerable> RecordParserSpanFlatAOT(IEnumerable mappedColumns, Func 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>(body, line); + + return result; + } + public static Expression> RecordParserSpanFlat(IEnumerable mappedColumns, Func factory) { // parameters @@ -54,9 +73,9 @@ public static Expression> RecordParserSpanFlat(IEnumerable( - ParameterExpression instanceVariable, - BlockExpression blockThatSetProperties, - IEnumerable mappedColumns, + ParameterExpression instanceVariable, + BlockExpression blockThatSetProperties, + IEnumerable mappedColumns, Func factory) { var getNewInstance = factory != null @@ -75,12 +94,15 @@ public static Expression> RecordParserSpanFlat(IEnumerable mappedColumns, - Func getTextValue) + Func getTextValue, + bool AOT = false) { var replacer = new ParameterReplacerVisitor(objectParameter); var assignsExpressions = new List(); var i = -1; + Func resolve = AOT ? GetValueToBeSetExpressionAOT : GetValueToBeSetExpression; + foreach (var x in mappedColumns) { i++; @@ -95,7 +117,7 @@ public static Expression> RecordParserSpanFlat(IEnumerable internal class FixedLengthReader : IFixedLengthReader { private readonly FuncSpanT parser; - private readonly Func, T> parser2; + private readonly Func parser2; - internal FixedLengthReader(FuncSpanT parser, Func, T> parser2) + internal FixedLengthReader(FuncSpanT parser, Func parser2) { this.parser = parser; this.parser2 = parser2; @@ -22,7 +23,7 @@ internal FixedLengthReader(FuncSpanT parser, Func, T> pa public T Parse(ReadOnlyMemory line) { - return parser2(line); + return parser2(new Foo(line)); } public T Parse(ReadOnlySpan line) diff --git a/RecordParser/RecordParser.csproj b/RecordParser/RecordParser.csproj index 9e00fba..4d619fd 100644 --- a/RecordParser/RecordParser.csproj +++ b/RecordParser/RecordParser.csproj @@ -27,7 +27,7 @@ - + @@ -36,10 +36,11 @@ - + + + + + true diff --git a/RecordParser/Visitors/MemoryVisitor.cs b/RecordParser/Visitors/MemoryVisitor.cs index 2989fb0..55ab440 100644 --- a/RecordParser/Visitors/MemoryVisitor.cs +++ b/RecordParser/Visitors/MemoryVisitor.cs @@ -1,78 +1,151 @@ -using System; -using System.Linq; -using System.Linq.Expressions; +using RecordParser.Engines.Reader; +using System; +using System.Globalization; namespace RecordParser.Visitors { - internal class SpanReplacerVisitor : ExpressionVisitor + internal struct Foo { - private readonly static ParameterExpression memory = Expression.Parameter(typeof(ReadOnlyMemory), "memory"); - private readonly static MemberExpression span = Expression.Property(memory, "Span"); + ReadOnlyMemory memory; - - - public Expression, T>> Modify(Expression> ex) + public Foo(ReadOnlyMemory memory) { - if (ex is null) return null; - - var body = Visit(ex.Body); - - body = new StaticMethodVisitor().Visit(body); - - var lamb = Expression.Lambda, T>>(body, memory); - - return lamb; + this.memory = memory; } - + public Foo Slice(int index, int count) + { + memory = memory.Slice(index, count); + return this; + } - protected override Expression VisitParameter(ParameterExpression node) + public Foo Slice(int index) { - if (node.Type == typeof(ReadOnlySpan)) - return span; + memory = memory.Slice(index); + return this; + } - return base.VisitParameter(node); + public Foo Trim() + { +#if NET6_0_OR_GREATER + memory = memory.Trim(); +#else + // TODO +#endif + return this; } - class StaticMethodVisitor : ExpressionVisitor + public T AsCustom(FuncSpanT func) => func(memory.Span); + public string AsString() => new string(memory.Span); + public char AsChar() => PrimitiveTypeReaderEngine.ToChar(memory.Span); + public byte AsByte() => byte.Parse(memory.Span, NumberStyles.Integer, null); + public sbyte AsSByte() => sbyte.Parse(memory.Span, NumberStyles.Integer, null); + public double AsDouble() => double.Parse(memory.Span, NumberStyles.AllowThousands | NumberStyles.Float, null); + public float AsSingle() => float.Parse(memory.Span, NumberStyles.AllowThousands | NumberStyles.Float, null); + public int AsInt32() => int.Parse(memory.Span, NumberStyles.Integer, null); + public uint AsUInt32() => uint.Parse(memory.Span, NumberStyles.Integer, null); + public long AsInt64() => long.Parse(memory.Span, NumberStyles.Integer, null); + public ulong AsUInt64() => ulong.Parse(memory.Span, NumberStyles.Integer, null); + public short AsInt16() => short.Parse(memory.Span, NumberStyles.Integer, null); + public ushort AsUInt16() => ushort.Parse(memory.Span, NumberStyles.Integer, null); + public Guid AsGuid() => Guid.Parse(memory.Span); + public DateTime AsDateTime() => DateTime.Parse(memory.Span, null, DateTimeStyles.AllowWhiteSpaces); + public TimeSpan AsTimeSpan() => TimeSpan.Parse(memory.Span, null); + public bool AsBoolean() => bool.Parse(memory.Span); + public decimal AsDecimal() => decimal.Parse(memory.Span, NumberStyles.Number, null); + public TEnum AsEnum() where TEnum : struct { - public static string ToString(ReadOnlySpan span) => span.ToString(); - public static ReadOnlySpan Trim(ReadOnlySpan span) => span.Trim(); - public static ReadOnlySpan Slice1(ReadOnlySpan span, int start) => span.Slice(start); - public static ReadOnlySpan Slice2(ReadOnlySpan span, int start, int count) => span.Slice(start, count); - - protected override Expression VisitMethodCall(MethodCallExpression node) - { - if (node.Object?.Type == typeof(ReadOnlySpan)) - { - var args = node.Arguments.Prepend(node.Object).ToArray(); - - if (node.Method.Name == "Slice") - { - Delegate f = node.Arguments.Count == 1 - ? StaticMethodVisitor.Slice1 - : StaticMethodVisitor.Slice2; - - return Expression.Call(f.Method, args); - } - - if (node.Method.Name == "ToString") - { - var f = StaticMethodVisitor.ToString; - - return Expression.Call(f.Method, args); - } - - if (node.Method.Name == "Trim") - { - var f = StaticMethodVisitor.Trim; - - return Expression.Call(f.Method, args); - } - } - - return base.VisitMethodCall(node); - } +#if NET6_0_OR_GREATER + return Enum.Parse(memory.Span, ignoreCase: true); +#else + return Enum.Parse(memory.Span.ToString(), ignoreCase: true); +#endif } } + + //internal class SpanReplacerVisitor : ExpressionVisitor + //{ + // private readonly static ParameterExpression memory = Expression.Parameter(typeof(Foo), "memory"); + + // public Expression> Modify(Expression> ex) + // { + // if (ex is null) return null; + + // var body = Visit(ex.Body); + + // body = new StaticMethodVisitor().Visit(body); + + // var lamb = Expression.Lambda>(body, memory); + + // return lamb; + // } + + // protected override Expression VisitBinary(BinaryExpression node) + // { + // if (node.NodeType != ExpressionType.Assign) + // return base.VisitBinary(node); + + // if (node.Right is MethodCallExpression method) + // { + // var resolve + + // var origin = method.Method.DeclaringType; + // if (origin.Assembly == typeof(object).Assembly) + // { + // Expression.Call(memory, ) + // } + // } + + // return base.VisitBinary(node); + // } + + //protected override Expression VisitParameter(ParameterExpression node) + //{ + // if (node.Type == typeof(ReadOnlySpan)) + // return span; + + // return base.VisitParameter(node); + //} + + //class StaticMethodVisitor : ExpressionVisitor + //{ + // public static string ToString(ReadOnlySpan span) => span.ToString(); + // public static ReadOnlySpan Trim(ReadOnlySpan span) => span.Trim(); + // public static ReadOnlySpan Slice1(ReadOnlySpan span, int start) => span.Slice(start); + // public static ReadOnlySpan Slice2(ReadOnlySpan span, int start, int count) => span.Slice(start, count); + + // protected override Expression VisitMethodCall(MethodCallExpression node) + // { + // if (node.Object?.Type == typeof(ReadOnlySpan)) + // { + // var args = node.Arguments.Prepend(node.Object).ToArray(); + + // if (node.Method.Name == "Slice") + // { + // Delegate f = node.Arguments.Count == 1 + // ? StaticMethodVisitor.Slice1 + // : StaticMethodVisitor.Slice2; + + // return Expression.Call(f.Method, args); + // } + + // if (node.Method.Name == "ToString") + // { + // var f = StaticMethodVisitor.ToString; + + // return Expression.Call(f.Method, args); + // } + + // if (node.Method.Name == "Trim") + // { + // var f = StaticMethodVisitor.Trim; + + // return Expression.Call(f.Method, args); + // } + // } + + // return base.VisitMethodCall(node); + // } + //} + //} }