diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..f440859d20 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +################################################################################ +# This .gitignore file was automatically created by Microsoft(R) Visual Studio. +################################################################################ + +.vs/ +/**/obj/** +/**/bin/** +/**/TestResults/** +/**/*.tmp \ No newline at end of file diff --git a/docs/architecture.md b/docs/0_architecture.md similarity index 99% rename from docs/architecture.md rename to docs/0_architecture.md index 17afb7c4f8..ee5a819326 100644 --- a/docs/architecture.md +++ b/docs/0_architecture.md @@ -486,61 +486,3 @@ Clearly, there's still a lot of infrastructure to flesh out, and a lot of implem 4. handling serialization of customer-defined types 5. CSDL defined authz 6. custom headers, query options, endpoints, etc. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/1_interfaces+datatypes.md b/docs/1_interfaces+datatypes.md new file mode 100644 index 0000000000..a9bb20bbd4 --- /dev/null +++ b/docs/1_interfaces+datatypes.md @@ -0,0 +1,70 @@ +## pattern overview + +We should stick to the naming conventions laid out in the [architecture](./architecture.md). So, we should have "parser"s to create CSTs from strings, "converter"s to move between CSTs and ASTs, "translator"s to move between different types of ASTs, "transcriber"s to create strings from CSTs, "serializer"s to go from user defined types to ASTs, and "deserializer"s to go from ASTs to user defined types. + +The CSTs will be a discriminated union that corresponds directly to the [OData ABNF](https://docs.oasis-open.org/odata/odata/v4.01/cs01/abnf/odata-abnf-construction-rules.txt). + +The ASTs will be a discriminated union that corresponds to the same ABNF but with the string literals removed, as well as "overhead" from the CST like aliases. + +The transcribers will be implemented as visitors on the CST nodes to convert them to strings using an intermediate `StringBuilder`. + +The converters will be implemented as visitors on the AST or CST nodes to produce instances of the other. + +TODO parsers will be... + +The translators will be implemented as visitors on the AST nodes to produce a corresponding AST for the desired data store. + +Please see the [appendix](#appendix) for other modeling options that were explored. + +## odata resource path example + +Let's use the odata resource path as an example of the above patterns. + +### syntactic CST? + +TODO do you want to have a syntactic CST that is a context-free grammar (should the context free grammar be defined by the literals used in the ABNF? write this out and keep it somewhere if so)? then your parsers could be combinators; if you do this you will need a converter from syntactic to semantic CST +TODO regardless of the above, you need to have a syntactic CST **anyway** to have parity with ODL + +### (semantic?) CST + +A sample implementation of the CST is [here](../odata/Root/OdataResourcePath/ConcreteSyntaxTreeNodes/OdataRelativeUri.cs). + +### AST + +A sample implementation of the AST is [here](../odata/Root/OdataResourcePath/AbstractSyntaxTreeNodes/OdataRelativeUri.cs). + +### CST transcriber + +A sample implementation of the transcriber is [here](../odata/Root/OdataResourcePath/Transcribers/OdataRelativeUriTranscriber.cs). + +### CST to AST converter + +A sample implementation of the CST to AST translator is [here](../odata/Root/OdataResourcePath/CstToAstTranslators/OdataRelativeUriTranslator.cs). + +### AST to CST converter + +A sample implementation of the CST to AST translator is [here](../odata/Root/OdataResourcePath/AstToCstTranslators/OdataRelativeUriTranslator.cs). + +### uri parser + +TODO + +### translator + +TODO + +## let's now attempt take some time to show mechanically how to implement a node in the AST and all of its associated utilities + +Let's try with `keyPredicate` using the [ABNF](https://docs.oasis-open.org/odata/odata/v4.01/cs01/abnf/odata-abnf-construction-rules.txt). + +## some takeaways + +Using this discriminated union pattern, it is clear from the code where the "feature gaps" are: any nodes that only have a private constructor have not yet been implemented. We should implement a node in its completeness so that we can maintain this status. Doing this will ensure that the "feature gaps" are never tribal knowledge, but something that any developer can discover just by looking at the code. + +These unions *also* allow us to easily scale across developers. Any number of developers can implement as many nodes as there are developers provided that no two developers are working on the same node. Also, by separating each phase of the handling process in this way, we are able to implement the nodes "piecewise", meaning: a developer can define the AST node, create a PR, and merge it; the developer can then define the CST node, create a PR, and merge it; they can then implement a converter, and so in. These can all be done as individual, discrete, atomic steps done (mostly) independently of each other. + +## appendix + +### parsing + +Parser combinators were tried using the Sprache nuget package. Though combinators are very flexible, the code is trivially readable, and development is exceedingly fast, combinators result in potentially incorrect parsing on grammars that are not context-free; the OData ABNF is not a context-free grammar, so we cannot rely on combinators. You can see an attempt at this implementation [here](https://github.com/OData/odata.net/blob/corranrogue9/framework/interfacesanddatatypes/odata/Root/OdataResourcePath/CombinatorParsers/OdataRelativeUriParser.cs). diff --git a/odata.sln b/odata.sln new file mode 100644 index 0000000000..87252d662f --- /dev/null +++ b/odata.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35514.174 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "odata", "odata\odata.csproj", "{6E3F11CA-8949-47C1-894C-CE61D911A2F2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "odata.tests", "odata.tests\odata.tests.csproj", "{07CA58E0-3FA7-4BC2-8CB1-6974049CB5CF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6E3F11CA-8949-47C1-894C-CE61D911A2F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E3F11CA-8949-47C1-894C-CE61D911A2F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E3F11CA-8949-47C1-894C-CE61D911A2F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E3F11CA-8949-47C1-894C-CE61D911A2F2}.Release|Any CPU.Build.0 = Release|Any CPU + {07CA58E0-3FA7-4BC2-8CB1-6974049CB5CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07CA58E0-3FA7-4BC2-8CB1-6974049CB5CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07CA58E0-3FA7-4BC2-8CB1-6974049CB5CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07CA58E0-3FA7-4BC2-8CB1-6974049CB5CF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/odata.tests/MSTestSettings.cs b/odata.tests/MSTestSettings.cs new file mode 100644 index 0000000000..aaf278c844 --- /dev/null +++ b/odata.tests/MSTestSettings.cs @@ -0,0 +1 @@ +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/odata.tests/Test1.cs b/odata.tests/Test1.cs new file mode 100644 index 0000000000..3a80f1022b --- /dev/null +++ b/odata.tests/Test1.cs @@ -0,0 +1,228 @@ +namespace odata.tests +{ + using AbnfParser.CstNodes.Core; + using Root; + using Root.OdataResourcePath.CombinatorParsers; + using Root.OdataResourcePath.Transcribers; + using Sprache; + using System; + using System.IO; + using System.Text; + + [TestClass] + public sealed class Test1 + { + [TestMethod] + public void TestMethod1() + { + var url = "$metadata"; + var cst = OdataRelativeUriParser.Instance.Parse(url); + + var ast = Root.OdataResourcePath.CstToAstConverters.OdataRelativeUriConverter + .Default + .Visit(cst, default); + + var newCst = Root.OdataResourcePath.AstToCstConverters.OdataRelativeUriConverter + .Default + .Visit(ast, default); + + var stringBuilder = new StringBuilder(); + OdataRelativeUriTranscriber.Default.Visit(newCst, stringBuilder); + + Assert.AreEqual(url, stringBuilder.ToString()); + } + + [TestMethod] + public void GenerateAlphaTranscribers() + { + var range = (0x01, 0x7E); + + var builder = new StringBuilder(); + for (int i = range.Item1; i <= range.Item2; ++i) + { + var className = $"x{i:X2}"; + builder.AppendLine($"public sealed class {className}Transcriber"); + builder.AppendLine("{"); + builder.AppendLine($"private {className}Transcriber()"); + builder.AppendLine("{"); + builder.AppendLine("}"); + builder.AppendLine(); + builder.AppendLine($"public static {className}Transcriber Instance {{ get; }} = new {className}Transcriber();"); + builder.AppendLine(); + builder.AppendLine($"public Void Transcribe({className} node, StringBuilder context)"); + builder.AppendLine("{"); + builder.AppendLine($"context.Append((char)0{className});"); + builder.AppendLine("return default;"); + builder.AppendLine("}"); + builder.AppendLine("}"); + } + + File.WriteAllText(@"C:\Users\gdebruin\code.txt", builder.ToString()); + } + + [TestMethod] + public void GenerateTranscriber() + { + var ranges = new[] + { + (0x20, 0x3D), + (0x3F, 0x7E), + }; + var elementName = "ProseVal"; + + var builder = new StringBuilder(); + builder.AppendLine("using System.Text;"); + builder.AppendLine(); + builder.AppendLine("using AbnfParser.CstNodes.Core;"); + builder.AppendLine("using Root;"); + builder.AppendLine(); + builder.AppendLine($"public sealed class {elementName}Transcriber : {elementName}.Visitor"); + builder.AppendLine("{"); + builder.AppendLine($"private {elementName}Transcriber()"); + builder.AppendLine("{"); + builder.AppendLine("}"); + builder.AppendLine(); + builder.AppendLine($"public static {elementName}Transcriber Instance {{ get; }} = new {elementName}Transcriber();"); + builder.AppendLine(); + foreach (var range in ranges) + { + GenerateAccepts(builder, elementName, range.Item1, range.Item2); + } + + builder.AppendLine("}"); + + File.WriteAllText(@"C:\Users\gdebruin\code.txt", builder.ToString()); + } + + private void GenerateAccepts(StringBuilder builder, string elementName, int start, int end) + { + for (int i = start; i <= end; ++i) + { + var className = $"x{i:X2}"; + builder.AppendLine($"protected internal override Void Accept({elementName}.{className} node, StringBuilder context)"); + builder.AppendLine("{"); + builder.AppendLine($"return {className}Transcriber.Instance.Transcribe(node.Value, context);"); + builder.AppendLine("}"); + builder.AppendLine(); + } + } + + [TestMethod] + public void Generate() + { + var ranges = new[] + { + (0x41, 0x5A), + (0x61, 0x7A), + }; + var elementName = "ProseVal"; + + var builder = new StringBuilder(); + builder.AppendLine("public abstract TResult Dispatch(Visitor visitor, TContext context);"); + builder.AppendLine(); + builder.AppendLine("public abstract class Visitor"); + builder.AppendLine("{"); + builder.AppendLine($"public TResult Visit({elementName} node, TContext context)"); + builder.AppendLine("{"); + builder.AppendLine("return node.Dispatch(this, context);"); + builder.AppendLine("}"); + builder.AppendLine(); + foreach (var range in ranges) + { + GenerateAccepts(builder, range.Item1, range.Item2); + } + + builder.AppendLine("}"); + builder.AppendLine(); + foreach (var range in ranges) + { + GenerateClasses(builder, elementName, range.Item1, range.Item2); + } + + + File.WriteAllText(@"C:\Users\gdebruin\code.txt", builder.ToString()); + } + + private void GenerateAccepts(StringBuilder builder, int start, int end) + { + for (int i = start; i <= end; ++i) + { + var className = $"x{i:X2}"; + builder.AppendLine($"protected internal abstract TResult Accept({className} node, TContext context);"); + } + } + + private void GenerateClasses(StringBuilder builder, string elementName, int start, int end) + { + for (int i = start; i <= end; ++i) + { + var className = $"x{i:X2}"; + builder.AppendLine($"public sealed class {className} : {elementName}"); + builder.AppendLine("{"); + builder.AppendLine($"public {className}(Core.x3C lessThan, Core.{className} value, Core.x3E greaterThan)"); + builder.AppendLine("{"); + builder.AppendLine("LessThan = lessThan;"); + builder.AppendLine("Value = value;"); + builder.AppendLine("GreaterThan = greaterThan;"); + builder.AppendLine("}"); + builder.AppendLine(); + builder.AppendLine($"public Core.x3C LessThan {{ get; }}"); + builder.AppendLine($"public Core.{className} Value {{ get; }}"); + builder.AppendLine($"public Core.x3E GreaterThan {{ get; }}"); + builder.AppendLine(); + builder.AppendLine($"public sealed override TResult Dispatch(Visitor visitor, TContext context)"); + builder.AppendLine("{"); + builder.AppendLine("return visitor.Accept(this, context);"); + builder.AppendLine("}"); + builder.AppendLine("}"); + builder.AppendLine(); + } + } + + [TestMethod] + public void CoreRules() + { + var coreRulesPath = @"C:\github\odata.net\odata\AbnfParser\core.abnf"; + var coreRulesText = File.ReadAllText(coreRulesPath); + var cst = AbnfParser.CombinatorParsers.RuleListParser.Instance.Parse(coreRulesText); + + //// TODO if the ABNF is missing a trailing newline, the last rule will be dropped + + var stringBuilder = new StringBuilder(); + AbnfParser.Transcribers.RuleListTranscriber.Instance.Transcribe(cst, stringBuilder); + var transcribedText = stringBuilder.ToString(); + Assert.AreEqual(coreRulesText, transcribedText); + } + + [TestMethod] + public void AbnfRules() + { + var coreRulesPath = @"C:\github\odata.net\odata\AbnfParser\core.abnf"; + var coreRulesText = File.ReadAllText(coreRulesPath); + var abnfRulesPath = @"C:\github\odata.net\odata\AbnfParser\abnf.abnf"; + var abnfRulesText = File.ReadAllText(abnfRulesPath); + var fullRulesText = string.Join(Environment.NewLine, abnfRulesText, coreRulesText); + var cst = AbnfParser.CombinatorParsers.RuleListParser.Instance.Parse(fullRulesText); + + var stringBuilder = new StringBuilder(); + AbnfParser.Transcribers.RuleListTranscriber.Instance.Transcribe(cst, stringBuilder); + var transcribedText = stringBuilder.ToString(); + Assert.AreEqual(fullRulesText, transcribedText); + } + + [TestMethod] + public void OdataRules() + { + var odataRulesPath = @"C:\github\odata.net\odata\odata.abnf"; + var odataRulesText = File.ReadAllText(odataRulesPath); + var cst = AbnfParser.CombinatorParsers.RuleListParser.Instance.Parse(odataRulesText); + + var stringBuilder = new StringBuilder(); + AbnfParser.Transcribers.RuleListTranscriber.Instance.Transcribe(cst, stringBuilder); + var transcribedText = stringBuilder.ToString(); + + File.WriteAllText(odataRulesPath, transcribedText); + Assert.AreEqual(odataRulesText, transcribedText); + } + } +} diff --git a/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTests.cs b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTests.cs new file mode 100644 index 0000000000..f6d2b070a4 --- /dev/null +++ b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTests.cs @@ -0,0 +1,190 @@ +namespace V2.Fx.Collections +{ + using System; + using System.Collections.Immutable; + using System.IO; + using System.Linq; + using System.Runtime.CompilerServices; + + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp.Scripting; + using Microsoft.CodeAnalysis.Scripting; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public sealed class LinkedListMemoryIntegrityTests + { + [TestMethod] + public void SingleValueLinkedListLeavingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(1, compilationOutput.Length); + Assert.AreEqual("CS8352", compilationOutput[0].Id); + } + + [TestMethod] + public void AppendedListLeavingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(1, compilationOutput.Length); + Assert.AreEqual("CS8352", compilationOutput[0].Id); + } + + [TestMethod] + public void LoopedAppendedListLeavingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(1, compilationOutput.Length); + Assert.AreEqual("CS8352", compilationOutput[0].Id); + } + + [TestMethod] + public void ListParameterAppended() + { + //// TODO i think this one maybe *should* be able to compile, as long as one where the input parameter is by reference *doesn't* compile + //// TODO if you do get this compiling, what *shouldn't* compile is appending to the input parameter and then returning the input parameter + var compilationOutput = Compile(); + Assert.AreEqual(2, compilationOutput.Length); + CollectionAssert.AreEquivalent( + new[] { "CS8352", "CS8350" }, + compilationOutput.Select(element => element.Id).ToArray()); + } + + [TestMethod] + public void ListByReferenceParameterAppended() + { + var compilationOutput = Compile(); + Assert.AreEqual(2, compilationOutput.Length); + CollectionAssert.AreEquivalent( + new[] { "CS8352", "CS8350" }, + compilationOutput.Select(element => element.Id).ToArray()); + } + + [TestMethod] + public void ListParameterReturned() + { + var compilationOutput = Compile(); + Assert.AreEqual(0, compilationOutput.Length); + } + + [TestMethod] + public void AppendToDefaultList() + { + var compilationOutput = Compile(); + Assert.AreEqual(2, compilationOutput.Length); + CollectionAssert.AreEquivalent( + new[] { "CS8352", "CS8350" }, + compilationOutput.Select(element => element.Id).ToArray()); + } + + [TestMethod] + public void AppendToReturnedDefaultList() + { + var compilationOutput = Compile(); + Assert.AreEqual(2, compilationOutput.Length); + CollectionAssert.AreEquivalent( + new[] { "CS8352", "CS8350" }, + compilationOutput.Select(element => element.Id).ToArray()); + } + + [TestMethod] + public void EmptyListLeavingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(1, compilationOutput.Length); + Assert.AreEqual("CS8352", compilationOutput[0].Id); + } + + private ImmutableArray Compile([CallerMemberName] string? testMethod = null) + { + var scriptContents = GetResource(testMethod); + var script = CSharpScript + .Create( + scriptContents, + ScriptOptions + .Default + .WithReferences( + typeof(SpanEx<>).Assembly)); + + return script.Compile(); + } + + private string GetResource([CallerMemberName] string? testMethod = null) + { + ArgumentNullException.ThrowIfNull(testMethod); + + var type = this.GetType(); + var path = $"{type.Namespace}.{type.Name}Resources.{testMethod}.cs"; + var assembly = type.Assembly; + var names = assembly.GetManifestResourceNames(); + using (var resourceStream = assembly.GetManifestResourceStream(path)) //// TODO you've previously done something in onboarding for embedded resources, check out that code + { + if (resourceStream == null) + { + throw new Exception("TODO"); + } + + using (var textReader = new StreamReader(resourceStream)) + { + return textReader.ReadToEnd(); + } + } + } + + + /*public static class V1 + { + private static LinkedListNode Test3() + { + //// TODO this is probably applicable to an empty list + var list = new LinkedListNode(BetterSpan.FromInstance(42)); + return list; + } + + + private static LinkedListNode Test6() + { + //// TODO this might apply to the stack implementation + var list = new LinkedListNode(BetterSpan.FromInstance(42)); + Span memory = stackalloc byte[Unsafe.SizeOf>()]; + Unsafe2.Copy(memory, in list); + + var nextValue = BetterSpan.FromInstance(67); + var previousNode = BetterSpan.FromMemory>(memory, 1); + list = new LinkedListNode(nextValue, previousNode); + + //// THIS IS A GOOD THING + return list; + } + + private static LinkedListNode Test8() + { + //// TODO this might apply to the stack implementation + var list = new LinkedListNode(BetterSpan.FromInstance(42)); + for (int i = 0; i < 10; ++i) + { + Span memory = stackalloc byte[Unsafe.SizeOf>()]; + Unsafe2.Copy(memory, in list); + + var nextValue = BetterSpan.FromInstance(i); + var previousNode = BetterSpan.FromMemory>(memory, 1); + list = new LinkedListNode(nextValue, previousNode); + } + + //// THIS IS A GOOD THING + return list; + } + + private static LinkedListNode Test15() + { + //// TODO this is probably applicable if you allow adding spans of values + var betterSpan = BetterSpan.FromSpan(new Span(new[] { 1, 2, 3, 4 })); + + var list = new LinkedListNode(betterSpan); + + //// TODO this should be allowed + return list; + } + }*/ + } +} diff --git a/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/AppendToDefaultList.cs b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/AppendToDefaultList.cs new file mode 100644 index 0000000000..9668c4fba8 --- /dev/null +++ b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/AppendToDefaultList.cs @@ -0,0 +1,13 @@ +using V2.Fx; +using V2.Fx.Collections; + +public static class AppendToDefaultList +{ + public static void Method() + { + var list = new LinkedList(); + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(42, memory); + } +} diff --git a/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/AppendToReturnedDefaultList.cs b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/AppendToReturnedDefaultList.cs new file mode 100644 index 0000000000..bd1b98034b --- /dev/null +++ b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/AppendToReturnedDefaultList.cs @@ -0,0 +1,18 @@ +using V2.Fx; +using V2.Fx.Collections; + +public static class AppendToReturnedDefaultList +{ + public static void Method() + { + var list = GetList(); + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(42, memory); + } + + private static LinkedList GetList() + { + return new LinkedList(); + } +} diff --git a/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/AppendedListLeavingFrame.cs b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/AppendedListLeavingFrame.cs new file mode 100644 index 0000000000..5ca8d9a81e --- /dev/null +++ b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/AppendedListLeavingFrame.cs @@ -0,0 +1,18 @@ +using V2.Fx; +using V2.Fx.Collections; + +public static class AppendedListLeavingFrame +{ + public static LinkedList Method() + { + var list = new LinkedList(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(42, memory); + + memory = stackalloc byte[list.MemorySize]; + list.Append(67, memory); + + return list; + } +} diff --git a/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/EmptyListLeavingFrame.cs b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/EmptyListLeavingFrame.cs new file mode 100644 index 0000000000..83f13de579 --- /dev/null +++ b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/EmptyListLeavingFrame.cs @@ -0,0 +1,12 @@ +using V2.Fx; +using V2.Fx.Collections; + +public static class EmptyListLeavingFrame +{ + public static LinkedList Method() + { + var list = new LinkedList(stackalloc byte[0]); + + return list; + } +} diff --git a/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/ListByReferenceParameterAppended.cs b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/ListByReferenceParameterAppended.cs new file mode 100644 index 0000000000..a4f7f3d9df --- /dev/null +++ b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/ListByReferenceParameterAppended.cs @@ -0,0 +1,12 @@ +using V2.Fx; +using V2.Fx.Collections; + +public static class ListByReferenceParameterAppended +{ + private static void Method(in LinkedList list) + { + ByteSpan memory = stackalloc byte[list.MemorySize]; + + list.Append(42, memory); + } +} diff --git a/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/ListParameterAppended.cs b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/ListParameterAppended.cs new file mode 100644 index 0000000000..0eb78f73d3 --- /dev/null +++ b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/ListParameterAppended.cs @@ -0,0 +1,12 @@ +using V2.Fx; +using V2.Fx.Collections; + +public static class ListParameterAppended +{ + private static void Method(LinkedList list) + { + ByteSpan memory = stackalloc byte[list.MemorySize]; + + list.Append(42, memory); + } +} diff --git a/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/ListParameterReturned.cs b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/ListParameterReturned.cs new file mode 100644 index 0000000000..919c074568 --- /dev/null +++ b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/ListParameterReturned.cs @@ -0,0 +1,9 @@ +using V2.Fx.Collections; + +public static class ListParameterReturned +{ + public static LinkedList Method(LinkedList list) + { + return list; + } +} diff --git a/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/LoopedAppendedListLeavingFrame.cs b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/LoopedAppendedListLeavingFrame.cs new file mode 100644 index 0000000000..2ae26cf5ee --- /dev/null +++ b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/LoopedAppendedListLeavingFrame.cs @@ -0,0 +1,21 @@ +using V2.Fx; +using V2.Fx.Collections; + +public static class LoopedAppendedListLeavingFrame +{ + public static LinkedList Method() + { + var list = new LinkedList(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(42, memory); + + for (int i = 0; i < 10; ++i) + { + ByteSpan memory2 = stackalloc byte[list.MemorySize]; + list.Append(i, memory2); + } + + return list; + } +} diff --git a/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/SingleValueLinkedListLeavingFrame.cs b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/SingleValueLinkedListLeavingFrame.cs new file mode 100644 index 0000000000..d42933fe3c --- /dev/null +++ b/odata.tests/V2/Fx/Collections/LinkedListMemoryIntegrityTestsResources/SingleValueLinkedListLeavingFrame.cs @@ -0,0 +1,14 @@ +using V2.Fx; +using V2.Fx.Collections; + +public static class SingleValueLinkedListLeavingFrame +{ + public static LinkedList Method() + { + var list = new LinkedList(stackalloc byte[0]); + + ByteSpan span = stackalloc byte[list.MemorySize]; + list.Append(42, span); + return list; + } +} diff --git a/odata.tests/V2/Fx/Collections/LinkedListUnitTests.cs b/odata.tests/V2/Fx/Collections/LinkedListUnitTests.cs new file mode 100644 index 0000000000..bb9ca1a4cc --- /dev/null +++ b/odata.tests/V2/Fx/Collections/LinkedListUnitTests.cs @@ -0,0 +1,357 @@ +namespace V2.Fx.Collections +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.WebSockets; + using System.Runtime.InteropServices; + using System.Threading; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using static V2.Fx.Collections.LinkedListUnitTests; + +#pragma warning disable CA2014 // Do not use stackalloc in loops + [TestClass] + public sealed class LinkedListUnitTests + { + private readonly ref struct Wrapper where T : allows ref struct + { + public Wrapper(T value) + { + Value = value; + } + + public T Value { get; } + } + + private static int ReferenceElementsFinalized = 0; + + public class ReferencedElementWithFinalizer + { + ~ReferencedElementWithFinalizer() + { + Interlocked.Increment(ref ReferenceElementsFinalized); + } + } + + [TestMethod] + public void ReferenceElements() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + + ReferenceElementsFinalized = 0; + + Exception? exception = null; + + exception = Assert.ThrowsException(() => new LinkedList>(stackalloc byte[0])); + + if (exception == null) + { + LinkedList> list = new LinkedList>(stackalloc byte[0]); + + for (int i = 0; i < 10; ++i) + { + list.Append( + new Wrapper(new ReferencedElementWithFinalizer()), + stackalloc byte[list.MemorySize]); + } + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + Assert.AreEqual(0, ReferenceElementsFinalized); + } + } + + [TestMethod] + public void AppendRefStruct() + { + var list = new LinkedList>(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(new Wrapper(-1), memory); + + for (int i = 0; i < 10; ++i) + { + memory = stackalloc byte[list.MemorySize]; + list.Append(new Wrapper(i), memory); + } + + AssertEnumerable(Enumerable.Range(-1, 11), list); + + for (int i = 10; i < 20; ++i) + { + memory = stackalloc byte[list.MemorySize]; + list.Append(new Wrapper(i), memory); + } + + AssertEnumerable(Enumerable.Range(-1, 21), list); + } + + [TestMethod] + public void Append() + { + var list = new LinkedList(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(-1, memory); + + for (int i = 0; i < 10; ++i) + { + memory = stackalloc byte[list.MemorySize]; + list.Append(i, memory); + } + + AssertEnumerable(Enumerable.Range(-1, 11), list); + + for (int i = 10; i < 20; ++i) + { + memory = stackalloc byte[list.MemorySize]; + list.Append(i, memory); + } + + AssertEnumerable(Enumerable.Range(-1, 21), list); + } + + /*[TestMethod] + public void Append2() + { + var list = new LinkedList(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(-1, memory); + + for (int i = 0; i < 2; ++i) + { + memory = stackalloc byte[list.MemorySize]; + list.Append(i, memory); + } + + AssertEnumerable(Enumerable.Range(-1, 11), list); + + DelegateCall(list); + } + + private static void DelegateCall(LinkedList list2) + { + //// TODO for some reason this isn't compiling, fix it + for (int i = 10; i < 2; ++i) + { + ByteSpan memory = stackalloc byte[list2.MemorySize]; + list2.Append(i, memory); + } + + AssertEnumerable(Enumerable.Range(-1, 21), list2); + }*/ + + [TestMethod] + public void EnumerateReturnedList() + { + var list = new LinkedList>(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(new Wrapper(-1), memory); + + for (int i = 0; i < 10; ++i) + { + memory = stackalloc byte[list.MemorySize]; + list.Append(new Wrapper(i), memory); + } + + AssertEnumerable(Enumerable.Range(-1, 11), list); + + var newList = PassListThrough(list); + AssertEnumerable(Enumerable.Range(-1, 11), newList); + } + + private static LinkedList> PassListThrough(LinkedList> list) + { + return list; + } + + [TestMethod] + public void AddElementToListInCallee() + { + var list = new LinkedList>(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(new Wrapper(-1), memory); + + AssertEnumerable(new[] { -1 }, list); + + memory = stackalloc byte[list.MemorySize]; + var newList = Foo(list, memory); + + AssertEnumerable(new[] { -1, 42 }, list); //// TODO if `list` is passed by value, shouldn't this still only have 1 element + AssertEnumerable(new[] { -1, 42 }, newList); + + Span zeroed = stackalloc byte[100]; + zeroed.Clear(); + + AssertEnumerable(new[] { -1, 42 }, list); //// TODO if `list` is passed by value, shouldn't this still only have 1 element + AssertEnumerable(new[] { -1, 42 }, newList); + + //// TODO the by-value vs by-ref thing aside, what happens if you append more elements + } + + private static LinkedList> Foo(LinkedList> list, ByteSpan memory) + { + list.Append(new Wrapper(42), memory); + return list; + } + + [TestMethod] + public void UpdateValue() + { + //// TODO this test is to demonstrate the behavior `LinkedList.Enumerator.Current` returning by ref; that isn't necessarily the correct behavior + + var list = new LinkedList>(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(new Wrapper(-1), memory); + + for (int i = 0; i < 10; ++i) + { + memory = stackalloc byte[list.MemorySize]; + list.Append(new Wrapper(i), memory); + } + + var enumerator = list.GetEnumerator(); + enumerator.MoveNext(); + enumerator.MoveNext(); + enumerator.Current = new Wrapper(42); + + AssertEnumerable(new[] { -1, 42, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, list); + } + + [TestMethod] + public void AppendToEmptyList() + { + var list = new LinkedList>(stackalloc byte[0]); + + for (int i = 0; i < 10; ++i) + { + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(new Wrapper(i), memory); + } + + AssertEnumerable(Enumerable.Range(0, 10), list); + } + + [TestMethod] + public void EmptyList() + { + var list = new LinkedList>(stackalloc byte[0]); + + AssertEnumerable(Enumerable.Empty(), list); + } + + [TestMethod] + public void DefaultList() + { + var list = new LinkedList>(); + + AssertEnumerable(Enumerable.Empty(), list); + } + + [TestMethod] + public void AppendSpan() + { + var list = new LinkedList(stackalloc byte[0]); + + Span ints = stackalloc int[] { 1, 2, 3, 4 }; + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(SpanEx.FromSpan(ints), memory); + + AssertEnumerable(new[] { 1, 2, 3, 4 }, list); + } + + [TestMethod] + public void AppendSingle() + { + var list = new LinkedList(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(42, memory); + + AssertEnumerable(new[] { 42 }, list); + } + + [TestMethod] + public void StitchStackAndHeap() + { + var list = new LinkedList(stackalloc byte[0]); + + Span stack = stackalloc int[] { 1, 2, 3, 4 }; + Span heap = new[] { 5, 6, 7, 8, 9 }; + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(SpanEx.FromSpan(stack), memory); + + memory = stackalloc byte[list.MemorySize]; + list.Append(SpanEx.FromSpan(heap), memory); + + AssertEnumerable(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, list); + } + + [TestMethod] + public void AppendStackHeapSingle() + { + var list = new LinkedList(stackalloc byte[0]); + + Span stack = stackalloc int[] { 1, 2, 3, 4 }; + Span heap = new[] { 5, 6, 7, 8, 9 }; + + ByteSpan memory = stackalloc byte[list.MemorySize]; + list.Append(SpanEx.FromSpan(stack), memory); + + memory = stackalloc byte[list.MemorySize]; + list.Append(SpanEx.FromSpan(heap), memory); + + memory = stackalloc byte[list.MemorySize]; + list.Append(42, memory); + + AssertEnumerable(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 42 }, list); + } + + private static void AssertEnumerable(IEnumerable expected, LinkedList> actual) //// TODO add allows ref struct constraint + { + AssertEnumerable(expected, actual, wrapper => wrapper.Value); + } + + private static void AssertEnumerable(IEnumerable expected, LinkedList actual) where T : struct + { + AssertEnumerable(expected, actual, _ => _); + } + + private static void AssertEnumerable(IEnumerable expected, LinkedList actual, Func selector) where TWrapper : struct, allows ref struct //// TODO add allows ref struct constraint to TValue + { + using (var expectedEnumerator = expected.GetEnumerator()) + { + if (!expectedEnumerator.MoveNext()) + { + foreach (var actualElement in actual) + { + Assert.Fail(); + } + } + else + { + var expectedHasAnotherElement = true; + foreach (var actualElement in actual) + { + Assert.IsTrue(expectedHasAnotherElement); + + var value = selector(actualElement); + Assert.AreEqual(expectedEnumerator.Current, value); + expectedHasAnotherElement = expectedEnumerator.MoveNext(); + } + + Assert.IsFalse(expectedHasAnotherElement); + } + } + } + } +#pragma warning restore CA2014 // Do not use stackalloc in loops +} diff --git a/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/CopiedMemoryLeavingFrame.cs b/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/CopiedMemoryLeavingFrame.cs new file mode 100644 index 0000000000..5b122b1c0f --- /dev/null +++ b/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/CopiedMemoryLeavingFrame.cs @@ -0,0 +1,31 @@ +using System.Runtime.CompilerServices; + +using V2.Fx; +using V2.Fx.Runtime.InteropServices; + +public static class SpanExFactoryMemoryIntegrityTestsResourcesCopiedMemoryLeavingFrame +{ + public static SpanEx> Method() + { + var element = 42; + var list = new Wrapper(SpanEx.FromInstance(ref element)); + ByteSpan memory = stackalloc byte[Unsafe.SizeOf>()]; + MemoryMarshal.Write(memory, in list); + + var nextElement = 67; + var nextValue = SpanEx.FromInstance(ref nextElement); + var previousNode = SpanEx.FromMemory>(memory, 1); + + return previousNode; + } + + public readonly ref struct Wrapper + { + private readonly SpanEx span; + + public Wrapper(SpanEx span) + { + this.span = span; + } + } +} \ No newline at end of file diff --git a/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/MemoryCopiedToCallingFrame.cs b/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/MemoryCopiedToCallingFrame.cs new file mode 100644 index 0000000000..2a14606d2d --- /dev/null +++ b/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/MemoryCopiedToCallingFrame.cs @@ -0,0 +1,25 @@ +using System; + +using V2.Fx; +using V2.Fx.Runtime.InteropServices; + +public static class SpanExFactoryMemoryIntegrityTestsResourcesMemoryCopiedToCallingFrame +{ + public static Wrapper Method(Span memory) + { + var value = 42; + var list = new Wrapper(SpanEx.FromInstance(ref value)); + MemoryMarshal.Write(memory, in list); + return list; + } + + public readonly ref struct Wrapper + { + private readonly SpanEx span; + + public Wrapper(SpanEx span) + { + this.span = span; + } + } +} diff --git a/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/StackAllocLeavingFrame.cs b/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/StackAllocLeavingFrame.cs new file mode 100644 index 0000000000..fc9aeb0270 --- /dev/null +++ b/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/StackAllocLeavingFrame.cs @@ -0,0 +1,14 @@ +using System.Runtime.CompilerServices; + +using V2.Fx; + +public static class SpanExFactoryMemoryIntegrityTestsResourcesStackAllocLeavingFrame +{ + private static SpanEx Method() + { + ByteSpan span = stackalloc byte[Unsafe.SizeOf()]; + var betterspan = SpanEx.FromMemory(span, 1); + + return betterspan; + } +} diff --git a/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/StackAllocWithinFrame.cs b/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/StackAllocWithinFrame.cs new file mode 100644 index 0000000000..34875d5a12 --- /dev/null +++ b/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/StackAllocWithinFrame.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; + +using V2.Fx; + +public static class SpanExFactoryMemoryIntegrityTestsResourcesStackAllocWithinFrame +{ + public static void Method() + { + ByteSpan span = stackalloc byte[Unsafe.SizeOf()]; + var betterSpan = SpanEx.FromMemory(span, 1); + } +} diff --git a/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/WrappedValueLeavingFrame.cs b/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/WrappedValueLeavingFrame.cs new file mode 100644 index 0000000000..5586ad0fae --- /dev/null +++ b/odata.tests/V2/Fx/SpanExFactoryMemoryIntegrityTestsResources/WrappedValueLeavingFrame.cs @@ -0,0 +1,21 @@ +using V2.Fx; + +public static class SpanExFactoryMemoryIntegrityTestsResourcesWrappedValueLeavingFrame +{ + public static Wrapper Method() + { + var value = 42; + var wrapper = new Wrapper(SpanEx.FromInstance(ref value)); + return wrapper; + } + + public readonly ref struct Wrapper + { + private readonly SpanEx span; + + public Wrapper(SpanEx span) + { + this.span = span; + } + } +} diff --git a/odata.tests/V2/Fx/SpanExMemoryIntegrityTests.cs b/odata.tests/V2/Fx/SpanExMemoryIntegrityTests.cs new file mode 100644 index 0000000000..d8ffbacbe8 --- /dev/null +++ b/odata.tests/V2/Fx/SpanExMemoryIntegrityTests.cs @@ -0,0 +1,89 @@ +namespace V2.Fx +{ + using System; + using System.Collections.Immutable; + using System.IO; + using System.Runtime.CompilerServices; + + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp.Scripting; + using Microsoft.CodeAnalysis.Scripting; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public sealed class SpanExFactoryMemoryIntegrityTests + { + [TestMethod] + public void StackAllocWithinFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(0, compilationOutput.Length); + } + + [TestMethod] + public void StackAllocLeavingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(1, compilationOutput.Length); + Assert.AreEqual("CS8352", compilationOutput[0].Id); + } + + [TestMethod] + public void CopiedMemoryLeavingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(1, compilationOutput.Length); + Assert.AreEqual("CS8352", compilationOutput[0].Id); + } + + [TestMethod] + public void MemoryCopiedToCallingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(0, compilationOutput.Length); + } + + [TestMethod] + public void WrappedValueLeavingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(0, compilationOutput.Length); + } + + private ImmutableArray Compile([CallerMemberName] string? testMethod = null) + { + var scriptContents = GetResource(testMethod); + var script = CSharpScript + .Create( + scriptContents, + ScriptOptions + .Default + .WithReferences( + typeof(SpanEx<>).Assembly)); + + return script.Compile(); + } + + private string GetResource([CallerMemberName] string? testMethod = null) + { + ArgumentNullException.ThrowIfNull(testMethod); + + var type = this.GetType(); + var path = $"{type.Namespace}.{type.Name}Resources.{testMethod}.cs"; + var assembly = type.Assembly; + var names = assembly.GetManifestResourceNames(); + using (var resourceStream = assembly.GetManifestResourceStream(path)) //// TODO you've previously done something in onboarding for embedded resources, check out that code + { + if (resourceStream == null) + { + throw new Exception("TODO"); + } + + using (var textReader = new StreamReader(resourceStream)) + { + return textReader.ReadToEnd(); + } + } + } + } +} diff --git a/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/CopiedMemoryLeavingFrame.cs b/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/CopiedMemoryLeavingFrame.cs new file mode 100644 index 0000000000..62aba85c19 --- /dev/null +++ b/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/CopiedMemoryLeavingFrame.cs @@ -0,0 +1,31 @@ +using System.Runtime.CompilerServices; + +using V2.Fx; +using V2.Fx.Runtime.InteropServices; + +public static class SpanExMemoryIntegrityTestsResourcesCopiedMemoryLeavingFrame +{ + public static SpanEx> Method() + { + var element = 42; + var list = new Wrapper(SpanEx.FromInstance(ref element)); + ByteSpan memory = stackalloc byte[Unsafe.SizeOf>()]; + MemoryMarshal.Write(memory, in list); + + var nextElement = 67; + var nextValue = SpanEx.FromInstance(ref nextElement); + var previousNode = SpanEx>.Create(memory, 1); + + return previousNode; + } + + public readonly ref struct Wrapper + { + private readonly SpanEx span; + + public Wrapper(SpanEx span) + { + this.span = span; + } + } +} \ No newline at end of file diff --git a/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/MemoryCopiedToCallingFrame.cs b/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/MemoryCopiedToCallingFrame.cs new file mode 100644 index 0000000000..759c480c68 --- /dev/null +++ b/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/MemoryCopiedToCallingFrame.cs @@ -0,0 +1,25 @@ +using System; + +using V2.Fx; +using V2.Fx.Runtime.InteropServices; + +public static class SpanExMemoryIntegrityTestsResourcesMemoryCopiedToCallingFrame +{ + public static Wrapper Method(Span memory) + { + var value = 42; + var list = new Wrapper(SpanEx.FromInstance(ref value)); + MemoryMarshal.Write(memory, in list); + return list; + } + + public readonly ref struct Wrapper + { + private readonly SpanEx span; + + public Wrapper(SpanEx span) + { + this.span = span; + } + } +} diff --git a/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/StackAllocLeavingFrame.cs b/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/StackAllocLeavingFrame.cs new file mode 100644 index 0000000000..0914d38e06 --- /dev/null +++ b/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/StackAllocLeavingFrame.cs @@ -0,0 +1,14 @@ +using System.Runtime.CompilerServices; + +using V2.Fx; + +public static class SpanExMemoryIntegrityTestsResourcesStackAllocLeavingFrame +{ + private static SpanEx Method() + { + ByteSpan span = stackalloc byte[Unsafe.SizeOf()]; + var betterspan = SpanEx.Create(span, 1); + + return betterspan; + } +} diff --git a/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/StackAllocWithinFrame.cs b/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/StackAllocWithinFrame.cs new file mode 100644 index 0000000000..092ebd8728 --- /dev/null +++ b/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/StackAllocWithinFrame.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; + +using V2.Fx; + +public static class SpanExMemoryIntegrityTestsResourcesStackAllocWithinFrame +{ + public static void Method() + { + ByteSpan span = stackalloc byte[Unsafe.SizeOf()]; + var betterSpan = SpanEx.Create(span, 1); + } +} diff --git a/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/WrappedValueLeavingFrame.cs b/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/WrappedValueLeavingFrame.cs new file mode 100644 index 0000000000..008f946c3c --- /dev/null +++ b/odata.tests/V2/Fx/SpanExMemoryIntegrityTestsResources/WrappedValueLeavingFrame.cs @@ -0,0 +1,21 @@ +using V2.Fx; + +public static class SpanExMemoryIntegrityTestsResourcesWrappedValueLeavingFrame +{ + public static Wrapper Method() + { + var value = 42; + var wrapper = new Wrapper(SpanEx.FromInstance(ref value)); + return wrapper; + } + + public readonly ref struct Wrapper + { + private readonly SpanEx span; + + public Wrapper(SpanEx span) + { + this.span = span; + } + } +} diff --git a/odata.tests/V2/Fx/SpanExUnitTests.cs b/odata.tests/V2/Fx/SpanExUnitTests.cs new file mode 100644 index 0000000000..2f22319542 --- /dev/null +++ b/odata.tests/V2/Fx/SpanExUnitTests.cs @@ -0,0 +1,20 @@ +namespace V2.Fx +{ + using System; + + [TestClass] + public sealed class SpanExUnitTests + { + [TestMethod] + public void FromInstance() + { + var value = 42; + var span = SpanEx.FromInstance(ref value); + + Span zeroed = stackalloc byte[100]; + zeroed.Clear(); + + Assert.AreEqual(42, span[0]); + } + } +} diff --git a/odata.tests/V2/Fx/SpanEx{T}MemoryIntegrityTests.cs b/odata.tests/V2/Fx/SpanEx{T}MemoryIntegrityTests.cs new file mode 100644 index 0000000000..afe88c14ed --- /dev/null +++ b/odata.tests/V2/Fx/SpanEx{T}MemoryIntegrityTests.cs @@ -0,0 +1,89 @@ +namespace V2.Fx +{ + using System; + using System.Collections.Immutable; + using System.IO; + using System.Runtime.CompilerServices; + + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp.Scripting; + using Microsoft.CodeAnalysis.Scripting; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public sealed class SpanExMemoryIntegrityTests + { + [TestMethod] + public void StackAllocWithinFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(0, compilationOutput.Length); + } + + [TestMethod] + public void StackAllocLeavingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(1, compilationOutput.Length); + Assert.AreEqual("CS8352", compilationOutput[0].Id); + } + + [TestMethod] + public void CopiedMemoryLeavingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(1, compilationOutput.Length); + Assert.AreEqual("CS8352", compilationOutput[0].Id); + } + + [TestMethod] + public void MemoryCopiedToCallingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(0, compilationOutput.Length); + } + + [TestMethod] + public void WrappedValueLeavingFrame() + { + var compilationOutput = Compile(); + Assert.AreEqual(0, compilationOutput.Length); + } + + private ImmutableArray Compile([CallerMemberName] string? testMethod = null) + { + var scriptContents = GetResource(testMethod); + var script = CSharpScript + .Create( + scriptContents, + ScriptOptions + .Default + .WithReferences( + typeof(SpanEx<>).Assembly)); + + return script.Compile(); + } + + private string GetResource([CallerMemberName] string? testMethod = null) + { + ArgumentNullException.ThrowIfNull(testMethod); + + var type = this.GetType(); + var path = $"{type.Namespace}.{type.Name}Resources.{testMethod}.cs"; + var assembly = type.Assembly; + var names = assembly.GetManifestResourceNames(); + using (var resourceStream = assembly.GetManifestResourceStream(path)) //// TODO you've previously done something in onboarding for embedded resources, check out that code + { + if (resourceStream == null) + { + throw new Exception("TODO"); + } + + using (var textReader = new StreamReader(resourceStream)) + { + return textReader.ReadToEnd(); + } + } + } + } +} diff --git a/odata.tests/V3/AllocationPlayground.cs b/odata.tests/V3/AllocationPlayground.cs new file mode 100644 index 0000000000..19181edb95 --- /dev/null +++ b/odata.tests/V3/AllocationPlayground.cs @@ -0,0 +1,322 @@ +using System; +using V2.Fx; +using V2.Fx.Collections; + +namespace V3 +{ +#pragma warning disable CA2014 // Do not use stackalloc in loops + public static class AllocationPlayground + { + public static void AllocateNodes() + { + var list = new LinkedList(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize * 3]; // allocate memory for 3 nodes, we intend to use that much + list.Append(42, memory); + list.TryAppend(67); + list.TryAppend(96); + if (!list.TryAppend(11)) + { + memory = stackalloc byte[list.MemorySize * 5]; // we need more memory... + list.Append(11, memory); + } + + for (int i = 0; i < 10; ++i) + { + if (!list.TryAppend(i)) + { + memory = stackalloc byte[list.MemorySize * 5]; + list.Append(i, memory); + } + } + + memory = stackalloc byte[list.MemorySize * 10]; + list.Append(-1, memory); + memory = stackalloc byte[list.MemorySize * 10]; + list.Append(-2, memory); + //// TODO what happens to the memory here? do we just waste 9 nodes of the first allocation? + } + + public static bool TryAppend(this LinkedList list, T value) where T : struct, allows ref struct + { + return true; + } + + public static void AllocateNodes2() + { + var list = new LinkedList(stackalloc byte[0]); + + list.TryAppend2(42); //// TODO should return false + + ByteSpan memory = stackalloc byte[list.MemorySize * 5]; + list.TryAllocate2(memory); //// TODO should return true, but what if it returns false? + + list.TryAppend2(42); //// TODO should return true, but what if it returns false? + } + + public static bool TryAppend2(this LinkedList list, T value) where T : struct, allows ref struct + { + return true; + } + + public static bool TryAllocate2(this LinkedList list, ByteSpan memory) where T : struct, allows ref struct + { + return true; + } + + public static void AllocateNodes3() + { + var list = new LinkedList(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize * 5]; + var appender = list.Allocate3(memory); + + appender.TryAppend(42); + + memory = stackalloc byte[list.MemorySize * 2]; + appender = list.Allocate3(memory); //// TODO what happens to the unused memory from the existing appender? + } + + public static LinkedListAppender Allocate3(this LinkedList list, ByteSpan memory) where T : struct, allows ref struct + { + return new LinkedListAppender(); + } + + public ref struct LinkedListAppender where T : allows ref struct + { + public bool TryAppend(T value) + { + return true; + } + } + + public static void AllocateNodes4() + { + var list = new LinkedList(stackalloc byte[0]); + + ByteSpan memory = stackalloc byte[list.MemorySize * 2]; + + list.TryAppend4(42); // should return false + list.TryAppend4(42, memory); // should return true + memory = stackalloc byte[list.MemorySize * 2]; + list.TryAppend4(67, memory); // should return false + list.TryAppend4(96, memory); // should return true; + + var list2 = new LinkedList(stackalloc byte[0]); + + ByteSpan memory2 = stackalloc byte[list2.MemorySize * 3]; + for (int i = 0; i < 10; ++i) + { + if (list2.TryAppend4(i, memory2)) + { + memory2 = stackalloc byte[list2.MemorySize * 3]; + // if the loop were to end right now, we would have 5 nodes worth of memory unused + } + } + + var list3 = new LinkedList(stackalloc byte[0]); + + for (int i = 0; i < 10; ++i) + { + if (!list3.TryAppend4(i)) + { + ByteSpan memory3 = stackalloc byte[list3.MemorySize * 3]; + list3.TryAppend4(i, memory3); + // if the loop were to end right now, we would have 2 nodes worth of memory unused; this is the cost of doing business with this kind of pre-allocation though + // but what if this call returns `false`? + } + } + + var list4 = new LinkedList(stackalloc byte[0]); + ByteSpan memory4 = stackalloc byte[list4.MemorySize * 3]; + for (int i = 0; i < 10; ++i) + { + if (!list4.TryAppend4(i)) + { + if (list4.TryAppend4(i, memory4)) + { + // only if `memory4` actually got used do we allocate some more + memory4 = stackalloc byte[list4.MemorySize * 3]; + // if the loop were to end right now, we would have 5 nodes worth of memory unused + } + } + } + + var list5 = new LinkedList(stackalloc byte[0]); + ByteSpan memory5 = stackalloc byte[0]; + var reallocate = true; + for (int i = 0; i < 10; ++i) + { + if (!list5.TryAppend4(i)) + { + if (reallocate) + { + memory5 = stackalloc byte[list.MemorySize * 3]; + } + + reallocate = list5.TryAppend4(i, memory5); + // if the loop were to end right now, we would have 2 nodes worth of memory unused; this is the cost of doing business with this kind of pre-allocation though + } + } + } + + public static bool TryAppend4(this LinkedList list, T value, ByteSpan memory) where T : struct, allows ref struct + { + //// TODO you *might* want to consider if someone invents a stack-based arraypool and that pool has arrays that are not aligned with your node sizes; in that case, you would (currently) be disallowed the use of those buffer, so maybe it makes sense to be able to stitch multiple buffers together if they have some "dangling" memory + + // true if `memory` was used, false if we had enough existing memory + return true; + } + + public static bool TryAppend4(this LinkedList list, T value) where T : struct, allows ref struct + { + // `true` if we had memory available for the operation, false otherwise (and therefore the append didn't happen) + return true; + } + } + + [TestClass] + public sealed class SpanExPoolTests + { + [TestMethod] + public void Test() + { + var numberOfSpans = 2; + var spanLength = 100; + ByteSpan memory = stackalloc byte[numberOfSpans * SpanExPool.SizePerSpan(spanLength)]; + var pool = new SpanExPool(memory, spanLength); + + Assert.IsTrue(pool.TryRent(out var first)); + Assert.IsTrue(pool.TryRent(out var second)); + Assert.IsFalse(pool.TryRent(out var third)); + + first.Span[0] = 5; + first.Span[99] = 42; + + for (int i = 0; i < first.Span.Length; ++i) + { + Console.WriteLine(first.Span[i]); + } + + first.Dispose(); + + Assert.IsTrue(pool.TryRent(out var fourth)); + + Console.WriteLine("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + + for (int i = 0; i < fourth.Span.Length; ++i) + { + Console.WriteLine(first.Span[i]); + } + } + } + + public ref struct SpanExPool where T : allows ref struct + { + private readonly SpanEx array; + + private int next; + + public SpanExPool(ByteSpan totalMemory, int spanLength) + { + // you have an array (spanex>) of pointers (spanex) to elements (T) + + var pointerSize = System.Runtime.CompilerServices.Unsafe.SizeOf(); + var individualElementSize = System.Runtime.CompilerServices.Unsafe.SizeOf(); + var elementsSize = individualElementSize * spanLength; + + var overheadSize = pointerSize + elementsSize; + if (totalMemory.Length % overheadSize != 0) + { + throw new System.Exception("TODO"); + } + + var spanCount = totalMemory.Length / overheadSize; + + var pointersLength = pointerSize * spanCount; + var pointersMemory = totalMemory.Slice(0, pointersLength); + var spansMemory = totalMemory.Slice(pointersLength, totalMemory.Length - pointersLength); //// TODO off by 1? + + this.array = SpanEx.FromMemory(pointersMemory, spanCount); + for (int i = 0; i < this.array.Length; ++i) + { + this.array[i] = new Pointer() + { + IsInUse = false, + Span = SpanEx.FromMemory(spansMemory.Slice(i * elementsSize, elementsSize), spanLength), + }; + } + + this.next = 0; + } + + public static int SizePerSpan(int spanLength) + { + var pointerSize = System.Runtime.CompilerServices.Unsafe.SizeOf(); + var individualElementSize = System.Runtime.CompilerServices.Unsafe.SizeOf(); + var elementsSize = individualElementSize * spanLength; + + return pointerSize + elementsSize; + } + + public bool TryRent(out Disposable rented) + { + //// TODO start with `this.next` + for (int i = 0; i < this.array.Length; ++i) + { + ref Pointer pointer = ref this.array[i]; + if (!pointer.IsInUse) + { + pointer.IsInUse = true; + rented = new Disposable(SpanEx.FromInstance(ref pointer)); + return true; + } + } + + rented = default; + return false; + } + + public ref struct Disposable + { + private SpanEx pointer; + + private bool disposed; + + internal Disposable(SpanEx pointer) //// TODO can you make this private? or public in a way that's always useful? + { + this.pointer = pointer; + } + + public SpanEx Span + { + get + { + //// TODO it'd be nice to return this by ref + //// TODO it'd be better to not even return a spanex but instead for `disposable` to *look* like a `spanex` + return this.pointer[0].Span; + } + } + + public void Dispose() + { + //// TODO this should go in spanex... + for (int i = 0; i < this.pointer[0].Span.Length; ++i) + { + this.pointer[0].Span[i] = default; + } + + this.pointer[0].IsInUse = false; + } + } + + internal ref struct Pointer //// TODO can you make this private? + { + public bool IsInUse; + + public SpanEx Span; + } + } + +#pragma warning restore CA2014 // Do not use stackalloc in loops +} diff --git a/odata.tests/V3/EnumerationTests.cs b/odata.tests/V3/EnumerationTests.cs new file mode 100644 index 0000000000..fa89fbaca1 --- /dev/null +++ b/odata.tests/V3/EnumerationTests.cs @@ -0,0 +1,424 @@ +namespace V3 +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.CompilerServices; + + using V2.Fx; + using V2.Fx.Collections; + + [TestClass] + public sealed class EnumerationTests + { + //// TODO iequalitycomparer for ref structs? maybe use reflection for this? + + private readonly ref struct Wrapper where T : allows ref struct + { + public Wrapper(T value) + { + Value = value; + } + + public T Value { get; } + } + + private sealed class Comparer : IEqualityComparer> + { + private Comparer() + { + } + + public static Comparer Instance { get; } = new Comparer(); + + public bool Equals(Wrapper x, Wrapper y) + { + return x.Value == y.Value; + } + + public int GetHashCode([DisallowNull] Wrapper obj) + { + return obj.Value.GetHashCode(); + } + } + + public static unsafe void Copy(ByteSpan destination, in T source, int offset) where T : allows ref struct + { + //// TODO if you keep this method, you should have the overload without `offset` delegate to it + + var index = offset * System.Runtime.CompilerServices.Unsafe.SizeOf(); + if (offset + System.Runtime.CompilerServices.Unsafe.SizeOf() >= destination.Length) + { + throw new Exception("TODO"); + } + + fixed (byte* pointer = destination) + { + var indexedPointer = pointer + index; + + System.Runtime.CompilerServices.Unsafe.Copy(indexedPointer, in source); + } + } + + private static ReadOnlyArray ToArray2(TEnumerable enumerable, ByteSpan memory, Params typeParameters) where TEnumerable : IBetterReadOnlyCollection, allows ref struct where TValue : allows ref struct where TEnumerator : IEnumerator, allows ref struct + { + return ToArray(enumerable, memory); + } + + private static ReadOnlyArray ToArray3(Params enumerable, ByteSpan memory) where TEnumerable : IBetterReadOnlyCollection, allows ref struct where TValue : allows ref struct where TEnumerator : IEnumerator, allows ref struct + { + return ToArray(enumerable.Self, memory); + } + + private static ReadOnlyArray ToArray(TEnumerable enumerable, ByteSpan memory) where TEnumerable : IBetterReadOnlyCollection, allows ref struct where TValue : allows ref struct where TEnumerator : IEnumerator, allows ref struct + { + if (memory.Length != enumerable.Count * Unsafe.SizeOf()) + { + throw new Exception("TODO"); + } + + var index = 0; + foreach (var element in enumerable) + { + Copy(memory, element, index); + + ++index; + } + + return new ReadOnlyArray(memory, enumerable.Count); + } + + private static void AssertEnumerable2(TEnumerable expected, TEnumerable actual, IEqualityComparer comparer, Params typeParameters) where TEnumerable : IBetterReadOnlyCollection, allows ref struct where TValue : allows ref struct where TEnumerator : IEnumerator, allows ref struct + { + AssertEnumerable(expected, actual, comparer); + } + + private static void AssertEnumerable3(Params expected, Params actual, IEqualityComparer comparer) where TEnumerable : IBetterReadOnlyCollection, allows ref struct where TValue : allows ref struct where TEnumerator : IEnumerator, allows ref struct + { + AssertEnumerable(expected.Self, actual.Self, comparer); + } + + private static void AssertEnumerable(TEnumerable expected, TEnumerable actual, IEqualityComparer comparer) where TEnumerable : IBetterReadOnlyCollection, allows ref struct where TValue : allows ref struct where TEnumerator : IEnumerator, allows ref struct + { + //// TODO why does this work? + ByteSpan memory = stackalloc byte[expected.Count * Unsafe.SizeOf()]; + var expectedArray = ToArray2(expected, memory, expected.TypeParameters); + + var index = 0; + foreach (var element in actual) + { + Assert.IsTrue(comparer.Equals(element, expectedArray[index])); + + ++index; + } + } + + [TestMethod] + public void Enumeration() + { + Span memory = stackalloc byte[LinkedList>.MemorySize]; + var list = new LinkedList>(new Wrapper(-1), memory); + + for (int i = 0; i < 10; ++i) + { + memory = stackalloc byte[LinkedList>.MemorySize]; + list.Append(new Wrapper(i), memory); + } + + + Span memory2 = stackalloc byte[LinkedList>.MemorySize]; + var list2 = new LinkedList>(new Wrapper(-1), memory2); + + for (int i = 0; i < 10; ++i) + { + memory2 = stackalloc byte[LinkedList>.MemorySize]; + list2.Append(new Wrapper(i), memory2); + } + + //// TODO fix the type inference + AssertEnumerable3(list.TypeParameters, list2.TypeParameters, Comparer.Instance); + } + + /*[TestMethod] + public void Cast() + { + Span memory = stackalloc byte[LinkedList.MemorySize]; + var list = new LinkedList(new Dog(), memory); + + DoCast(list); + + var array = new Dog[0]; + DoCast2(array); + + var genericList = new System.Collections.Generic.List(); + DoCast3(genericList); + + var thing = new Thing(); + DoCast4(thing); + } + + public static void DoCast(LinkedList animals) + { + + } + + public static void DoCast2(Animal[] animals) + { + } + + public static void DoCast3(System.Collections.Generic.List animals) + { + } + + public static void DoCast4(Thing thing) + { + } + + public interface IThing + { + } + + public sealed class Thing : IThing + { + } + + public class Animal + { + } + + public class Dog : Animal + { + }*/ + + public ref struct LinkedList : IBetterReadOnlyCollection, T, LinkedList.Enumerator> where T : allows ref struct + { + private SpanEx first; + + private SpanEx current; + + private int count; + + private bool hasValues; + + public LinkedList() + { + this.count = 0; + this.hasValues = false; + } + + public LinkedList(T value, ByteSpan memory) //// TODO can you use betterspan instead of span? how about readonlyspan? + { + //// TODO do you still want this constructor now that empty lists are a thing? + this.SetFirstValue(value, memory); + } + + public int ArraySize + { + get + { + return this.Count * System.Runtime.CompilerServices.Unsafe.SizeOf(); + } + } + + private void SetFirstValue(T value, ByteSpan memory) //// TODO can you use betterspan instead of span? how about readonlyspan? + { + var firstNode = new LinkedListNode(value); + V2.Fx.Runtime.InteropServices.MemoryMarshal.Write(memory, firstNode); + + this.first = SpanEx.FromMemory(memory, 1); + this.current = this.first; + + this.count = 1; + this.hasValues = true; + } + + public void Append(T value, ByteSpan memory) + { + if (!this.hasValues) + { + this.SetFirstValue(value, memory); + } + else + { + var nextNode = new LinkedListNode(value); + V2.Fx.Runtime.InteropServices.MemoryMarshal.Write(memory, nextNode); + + var next = SpanEx.FromMemory(memory, 1); + + this.current[0].Next = next; + + this.current = next; + + ++this.count; + } + } + + public static int MemorySize { get; } = System.Runtime.CompilerServices.Unsafe.SizeOf(); + + public int Count + { + get + { + return this.count; + } + } + + public Params, T, Enumerator> TypeParameters + { + get + { + return new Params, T, Enumerator>(this); + } + } + + internal ref struct LinkedListNode //// TODO can you make this private + { + public readonly T Value; + + public SpanEx Next; + + public LinkedListNode(T value) + { + this.Value = value; + } + } + + public Enumerator GetEnumerator() + { + if (this.hasValues) + { + return new Enumerator(this.first); + } + else + { + return new Enumerator(); + } + } + + public ref struct Enumerator : IEnumerator + { + private SpanEx node; + + private bool hasMoved; + + private readonly bool hasValues; + + internal Enumerator(SpanEx node) + { + this.node = node; + + this.hasMoved = false; + this.hasValues = true; + } + + public T Current //// TODO cna you make this return `ref t`? + { + get + { + if (!this.hasValues || !this.hasMoved) + { + throw new Exception("TODO"); + } + + return this.node[0].Value; + } + } + + object IEnumerator.Current => throw new NotImplementedException(); + + public bool MoveNext() + { + if (!this.hasValues) + { + return false; + } + + if (!this.hasMoved) + { + this.hasMoved = true; + return true; + } + + var nextNode = this.node[0].Next; + if (nextNode.Length != 0) + { + this.node = nextNode; + return true; + } + + return false; + } + + public void Reset() + { + ////throw new NotImplementedException(); + } + + public void Dispose() + { + ////throw new NotImplementedException(); + } + } + } + + public interface IBetterReadOnlyCollection where TSelf : allows ref struct where TValue : allows ref struct where TEnumerator : IEnumerator, allows ref struct + { + int Count { get; } + + TEnumerator GetEnumerator(); + + Params TypeParameters { get; } //// TODO this requires removing covariance...but if this is really only used for ref structs (why else would it have the enumerator?) then you can't leverage the covariance anyway + } + + public readonly ref struct Params where TSelf : allows ref struct where T1 : allows ref struct where T2 : allows ref struct + { + public Params(TSelf self) + { + this.Self = self; + } + + public TSelf Self { get; } + } + + public interface IReadOnlyArray where T : allows ref struct + { + T this[int index] { get; } + + int Count { get; } + } + + public ref struct ReadOnlyArray : IReadOnlyArray where T : allows ref struct + { + private readonly ByteSpan memory; + private readonly int length; + + public ReadOnlyArray(ByteSpan memory, int length) + { + this.memory = memory; + this.length = length; + } + + public unsafe T this[int index] + { + get + { + fixed (byte* pointer = memory) + { + byte* indexed = pointer + index * System.Runtime.CompilerServices.Unsafe.SizeOf(); + + return System.Runtime.CompilerServices.Unsafe.AsRef(indexed); + } + } + } + + public int Count + { + get + { + return this.Count; + } + } + } + } +} diff --git a/odata.tests/odata.tests.csproj b/odata.tests/odata.tests.csproj new file mode 100644 index 0000000000..4608a73769 --- /dev/null +++ b/odata.tests/odata.tests.csproj @@ -0,0 +1,40 @@ + + + + net9.0 + latest + enable + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/odata/AbnfParser/CombinatorParsers/AlternationParser.cs b/odata/AbnfParser/CombinatorParsers/AlternationParser.cs new file mode 100644 index 0000000000..0964dd3bb2 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/AlternationParser.cs @@ -0,0 +1,24 @@ +namespace AbnfParser.CombinatorParsers +{ + using AbnfParser.CombinatorParsers.Core; + using AbnfParser.CstNodes; + using Sprache; + + public static class AlternationParser + { + public static Parser Instance { get; } = + from concatenation in ConcatenationParser.Instance + from inners in InnerParser.Instance.Many() + select new Alternation(concatenation, inners); + + public static class InnerParser + { + public static Parser Instance { get; } = + from prefixCwsps in CwspParser.Instance.Many() + from slash in x2FParser.Instance + from suffixCwsps in CwspParser.Instance.Many() + from concatenation in ConcatenationParser.Instance + select new Alternation.Inner(prefixCwsps, slash, suffixCwsps, concatenation); + } + } +} diff --git a/odata/AbnfParser/CombinatorParsers/BinValParser.cs b/odata/AbnfParser/CombinatorParsers/BinValParser.cs new file mode 100644 index 0000000000..3575cce6d2 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/BinValParser.cs @@ -0,0 +1,57 @@ +namespace AbnfParser.CombinatorParsers +{ + using AbnfParser.CombinatorParsers.Core; + using AbnfParser.CstNodes; + using Sprache; + + public static class BinValParser + { + public static Parser BitsOnly { get; } = + from b in x62Parser.Instance + from bits in BitParser.Instance.AtLeastOnce() + select new BinVal.BitsOnly(b, bits); + + public static Parser ConcatenatedBits { get; } = ConcatenatedBitsParser.Instance; + + public static class ConcatenatedBitsParser + { + public static Parser Instance { get; } = + from b in x62Parser.Instance + from bits in BitParser.Instance.AtLeastOnce() + from inners in InnerParser.Instance.AtLeastOnce() + select new BinVal.ConcatenatedBits(b, bits, inners); + + public static class InnerParser + { + public static Parser Instance { get; } = + from dot in x2EParser.Instance + from bits in BitParser.Instance.AtLeastOnce() + select new BinVal.ConcatenatedBits.Inner(dot, bits); + } + } + + public static Parser Range { get; } = RangeParser.Instance; + + public static class RangeParser + { + public static Parser Instance { get; } = + from b in x62Parser.Instance + from bits in BitParser.Instance.AtLeastOnce() + from inners in InnerParser.Instance.AtLeastOnce() + select new BinVal.Range(b, bits, inners); + + public static class InnerParser + { + public static Parser Instance { get; } = + from dash in x2DParser.Instance + from bits in BitParser.Instance.AtLeastOnce() + select new BinVal.Range.Inner(dash, bits); + } + } + + public static Parser Instance { get; } = + BitsOnly + .Or(ConcatenatedBits) + .Or(Range); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/CharValParser.cs b/odata/AbnfParser/CombinatorParsers/CharValParser.cs new file mode 100644 index 0000000000..6f25f93442 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/CharValParser.cs @@ -0,0 +1,490 @@ +namespace AbnfParser.CombinatorParsers +{ + using AbnfParser.CombinatorParsers.Core; + using AbnfParser.CstNodes; + using Sprache; + + public static class CharValParser + { + public static Parser Instance { get; } = + from openDquote in DquoteParser.Instance + from inners in InnerParser.Instance.Many() + from closeDquote in DquoteParser.Instance + select new CharVal(openDquote, inners, closeDquote); + + public static class InnerParser + { + public static Parser x20 { get; } = + from value in x20Parser.Instance + select new CharVal.Inner.x20(value); + + public static Parser x21 { get; } = + from value in x21Parser.Instance + select new CharVal.Inner.x21(value); + + public static Parser x23 { get; } = + from value in x23Parser.Instance + select new CharVal.Inner.x23(value); + + public static Parser x24 { get; } = + from value in x24Parser.Instance + select new CharVal.Inner.x24(value); + + public static Parser x25 { get; } = + from value in x25Parser.Instance + select new CharVal.Inner.x25(value); + + public static Parser x26 { get; } = + from value in x26Parser.Instance + select new CharVal.Inner.x26(value); + + public static Parser x27 { get; } = + from value in x27Parser.Instance + select new CharVal.Inner.x27(value); + + public static Parser x28 { get; } = + from value in x28Parser.Instance + select new CharVal.Inner.x28(value); + + public static Parser x29 { get; } = + from value in x29Parser.Instance + select new CharVal.Inner.x29(value); + + public static Parser x2A { get; } = + from value in x2AParser.Instance + select new CharVal.Inner.x2A(value); + + public static Parser x2B { get; } = + from value in x2BParser.Instance + select new CharVal.Inner.x2B(value); + + public static Parser x2C { get; } = + from value in x2CParser.Instance + select new CharVal.Inner.x2C(value); + + public static Parser x2D { get; } = + from value in x2DParser.Instance + select new CharVal.Inner.x2D(value); + + public static Parser x2E { get; } = + from value in x2EParser.Instance + select new CharVal.Inner.x2E(value); + + public static Parser x2F { get; } = + from value in x2FParser.Instance + select new CharVal.Inner.x2F(value); + + public static Parser x30 { get; } = + from value in x30Parser.Instance + select new CharVal.Inner.x30(value); + + public static Parser x31 { get; } = + from value in x31Parser.Instance + select new CharVal.Inner.x31(value); + + public static Parser x32 { get; } = + from value in x32Parser.Instance + select new CharVal.Inner.x32(value); + + public static Parser x33 { get; } = + from value in x33Parser.Instance + select new CharVal.Inner.x33(value); + + public static Parser x34 { get; } = + from value in x34Parser.Instance + select new CharVal.Inner.x34(value); + + public static Parser x35 { get; } = + from value in x35Parser.Instance + select new CharVal.Inner.x35(value); + + public static Parser x36 { get; } = + from value in x36Parser.Instance + select new CharVal.Inner.x36(value); + + public static Parser x37 { get; } = + from value in x37Parser.Instance + select new CharVal.Inner.x37(value); + + public static Parser x38 { get; } = + from value in x38Parser.Instance + select new CharVal.Inner.x38(value); + + public static Parser x39 { get; } = + from value in x39Parser.Instance + select new CharVal.Inner.x39(value); + + public static Parser x3A { get; } = + from value in x3AParser.Instance + select new CharVal.Inner.x3A(value); + + public static Parser x3B { get; } = + from value in x3BParser.Instance + select new CharVal.Inner.x3B(value); + + public static Parser x3C { get; } = + from value in x3CParser.Instance + select new CharVal.Inner.x3C(value); + + public static Parser x3D { get; } = + from value in x3DParser.Instance + select new CharVal.Inner.x3D(value); + + public static Parser x3E { get; } = + from value in x3EParser.Instance + select new CharVal.Inner.x3E(value); + + public static Parser x3F { get; } = + from value in x3FParser.Instance + select new CharVal.Inner.x3F(value); + + public static Parser x40 { get; } = + from value in x40Parser.Instance + select new CharVal.Inner.x40(value); + + public static Parser x41 { get; } = + from value in x41Parser.Instance + select new CharVal.Inner.x41(value); + + public static Parser x42 { get; } = + from value in x42Parser.Instance + select new CharVal.Inner.x42(value); + + public static Parser x43 { get; } = + from value in x43Parser.Instance + select new CharVal.Inner.x43(value); + + public static Parser x44 { get; } = + from value in x44Parser.Instance + select new CharVal.Inner.x44(value); + + public static Parser x45 { get; } = + from value in x45Parser.Instance + select new CharVal.Inner.x45(value); + + public static Parser x46 { get; } = + from value in x46Parser.Instance + select new CharVal.Inner.x46(value); + + public static Parser x47 { get; } = + from value in x47Parser.Instance + select new CharVal.Inner.x47(value); + + public static Parser x48 { get; } = + from value in x48Parser.Instance + select new CharVal.Inner.x48(value); + + public static Parser x49 { get; } = + from value in x49Parser.Instance + select new CharVal.Inner.x49(value); + + public static Parser x4A { get; } = + from value in x4AParser.Instance + select new CharVal.Inner.x4A(value); + + public static Parser x4B { get; } = + from value in x4BParser.Instance + select new CharVal.Inner.x4B(value); + + public static Parser x4C { get; } = + from value in x4CParser.Instance + select new CharVal.Inner.x4C(value); + + public static Parser x4D { get; } = + from value in x4DParser.Instance + select new CharVal.Inner.x4D(value); + + public static Parser x4E { get; } = + from value in x4EParser.Instance + select new CharVal.Inner.x4E(value); + + public static Parser x4F { get; } = + from value in x4FParser.Instance + select new CharVal.Inner.x4F(value); + + public static Parser x50 { get; } = + from value in x50Parser.Instance + select new CharVal.Inner.x50(value); + + public static Parser x51 { get; } = + from value in x51Parser.Instance + select new CharVal.Inner.x51(value); + + public static Parser x52 { get; } = + from value in x52Parser.Instance + select new CharVal.Inner.x52(value); + + public static Parser x53 { get; } = + from value in x53Parser.Instance + select new CharVal.Inner.x53(value); + + public static Parser x54 { get; } = + from value in x54Parser.Instance + select new CharVal.Inner.x54(value); + + public static Parser x55 { get; } = + from value in x55Parser.Instance + select new CharVal.Inner.x55(value); + + public static Parser x56 { get; } = + from value in x56Parser.Instance + select new CharVal.Inner.x56(value); + + public static Parser x57 { get; } = + from value in x57Parser.Instance + select new CharVal.Inner.x57(value); + + public static Parser x58 { get; } = + from value in x58Parser.Instance + select new CharVal.Inner.x58(value); + + public static Parser x59 { get; } = + from value in x59Parser.Instance + select new CharVal.Inner.x59(value); + + public static Parser x5A { get; } = + from value in x5AParser.Instance + select new CharVal.Inner.x5A(value); + + public static Parser x5B { get; } = + from value in x5BParser.Instance + select new CharVal.Inner.x5B(value); + + public static Parser x5C { get; } = + from value in x5CParser.Instance + select new CharVal.Inner.x5C(value); + + public static Parser x5D { get; } = + from value in x5DParser.Instance + select new CharVal.Inner.x5D(value); + + public static Parser x5E { get; } = + from value in x5EParser.Instance + select new CharVal.Inner.x5E(value); + + public static Parser x5F { get; } = + from value in x5FParser.Instance + select new CharVal.Inner.x5F(value); + + public static Parser x60 { get; } = + from value in x60Parser.Instance + select new CharVal.Inner.x60(value); + + public static Parser x61 { get; } = + from value in x61Parser.Instance + select new CharVal.Inner.x61(value); + + public static Parser x62 { get; } = + from value in x62Parser.Instance + select new CharVal.Inner.x62(value); + + public static Parser x63 { get; } = + from value in x63Parser.Instance + select new CharVal.Inner.x63(value); + + public static Parser x64 { get; } = + from value in x64Parser.Instance + select new CharVal.Inner.x64(value); + + public static Parser x65 { get; } = + from value in x65Parser.Instance + select new CharVal.Inner.x65(value); + + public static Parser x66 { get; } = + from value in x66Parser.Instance + select new CharVal.Inner.x66(value); + + public static Parser x67 { get; } = + from value in x67Parser.Instance + select new CharVal.Inner.x67(value); + + public static Parser x68 { get; } = + from value in x68Parser.Instance + select new CharVal.Inner.x68(value); + + public static Parser x69 { get; } = + from value in x69Parser.Instance + select new CharVal.Inner.x69(value); + + public static Parser x6A { get; } = + from value in x6AParser.Instance + select new CharVal.Inner.x6A(value); + + public static Parser x6B { get; } = + from value in x6BParser.Instance + select new CharVal.Inner.x6B(value); + + public static Parser x6C { get; } = + from value in x6CParser.Instance + select new CharVal.Inner.x6C(value); + + public static Parser x6D { get; } = + from value in x6DParser.Instance + select new CharVal.Inner.x6D(value); + + public static Parser x6E { get; } = + from value in x6EParser.Instance + select new CharVal.Inner.x6E(value); + + public static Parser x6F { get; } = + from value in x6FParser.Instance + select new CharVal.Inner.x6F(value); + + public static Parser x70 { get; } = + from value in x70Parser.Instance + select new CharVal.Inner.x70(value); + + public static Parser x71 { get; } = + from value in x71Parser.Instance + select new CharVal.Inner.x71(value); + + public static Parser x72 { get; } = + from value in x72Parser.Instance + select new CharVal.Inner.x72(value); + + public static Parser x73 { get; } = + from value in x73Parser.Instance + select new CharVal.Inner.x73(value); + + public static Parser x74 { get; } = + from value in x74Parser.Instance + select new CharVal.Inner.x74(value); + + public static Parser x75 { get; } = + from value in x75Parser.Instance + select new CharVal.Inner.x75(value); + + public static Parser x76 { get; } = + from value in x76Parser.Instance + select new CharVal.Inner.x76(value); + + public static Parser x77 { get; } = + from value in x77Parser.Instance + select new CharVal.Inner.x77(value); + + public static Parser x78 { get; } = + from value in x78Parser.Instance + select new CharVal.Inner.x78(value); + + public static Parser x79 { get; } = + from value in x79Parser.Instance + select new CharVal.Inner.x79(value); + + public static Parser x7A { get; } = + from value in x7AParser.Instance + select new CharVal.Inner.x7A(value); + + public static Parser x7B { get; } = + from value in x7BParser.Instance + select new CharVal.Inner.x7B(value); + + public static Parser x7C { get; } = + from value in x7CParser.Instance + select new CharVal.Inner.x7C(value); + + public static Parser x7D { get; } = + from value in x7DParser.Instance + select new CharVal.Inner.x7D(value); + + public static Parser x7E { get; } = + from value in x7EParser.Instance + select new CharVal.Inner.x7E(value); + + public static Parser Instance { get; } = + x20 + .Or(x21) + .Or(x23) + .Or(x24) + .Or(x25) + .Or(x26) + .Or(x27) + .Or(x28) + .Or(x29) + .Or(x2A) + .Or(x2B) + .Or(x2C) + .Or(x2D) + .Or(x2E) + .Or(x2F) + .Or(x30) + .Or(x31) + .Or(x32) + .Or(x33) + .Or(x34) + .Or(x35) + .Or(x36) + .Or(x37) + .Or(x38) + .Or(x39) + .Or(x3A) + .Or(x3B) + .Or(x3C) + .Or(x3D) + .Or(x3E) + .Or(x3F) + .Or(x40) + .Or(x41) + .Or(x42) + .Or(x43) + .Or(x44) + .Or(x45) + .Or(x46) + .Or(x47) + .Or(x48) + .Or(x49) + .Or(x4A) + .Or(x4B) + .Or(x4C) + .Or(x4D) + .Or(x4E) + .Or(x4F) + .Or(x50) + .Or(x51) + .Or(x52) + .Or(x53) + .Or(x54) + .Or(x55) + .Or(x56) + .Or(x57) + .Or(x58) + .Or(x59) + .Or(x5A) + .Or(x5B) + .Or(x5C) + .Or(x5D) + .Or(x5E) + .Or(x5F) + .Or(x60) + .Or(x61) + .Or(x62) + .Or(x63) + .Or(x64) + .Or(x65) + .Or(x66) + .Or(x67) + .Or(x68) + .Or(x69) + .Or(x6A) + .Or(x6B) + .Or(x6C) + .Or(x6D) + .Or(x6E) + .Or(x6F) + .Or(x70) + .Or(x71) + .Or(x72) + .Or(x73) + .Or(x74) + .Or(x75) + .Or(x76) + .Or(x77) + .Or(x78) + .Or(x79) + .Or(x7A) + .Or(x7B) + .Or(x7C) + .Or(x7D) + .Or(x7E); + } + } +} diff --git a/odata/AbnfParser/CombinatorParsers/CnlParser.cs b/odata/AbnfParser/CombinatorParsers/CnlParser.cs new file mode 100644 index 0000000000..6961c6a12d --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/CnlParser.cs @@ -0,0 +1,21 @@ +namespace AbnfParser.CombinatorParsers +{ + using AbnfParser.CombinatorParsers.Core; + using AbnfParser.CstNodes; + using Sprache; + + public static class CnlParser + { + public static Parser Comment { get; } = + from comment in CommentParser.Instance + select new Cnl.Comment(comment); + + public static Parser NewLine { get; } = + from crlf in CrlfParser.Instance + select new Cnl.Newline(crlf); + + public static Parser Instance { get; } = + Comment + .Or(NewLine); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/CommentParser.cs b/odata/AbnfParser/CombinatorParsers/CommentParser.cs new file mode 100644 index 0000000000..6b668b4e5c --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/CommentParser.cs @@ -0,0 +1,31 @@ +namespace AbnfParser.CombinatorParsers +{ + using AbnfParser.CombinatorParsers.Core; + using AbnfParser.CstNodes; + using AbnfParser.CstNodes.Core; + using Sprache; + + public static class CommentParser + { + public static Parser Instance { get; } = + from semicolon in x3BParser.Instance + from inners in InnerParser.Instance.Many() + from crlf in CrlfParser.Instance + select new Comment(semicolon, inners, crlf); + + public static class InnerParser + { + public static Parser InnerWsp { get; } = + from wsp in WspParser.Instance + select new Comment.Inner.InnerWsp(wsp); + + public static Parser InnerVchar { get; } = + from vchar in VcharParser.Instance + select new Comment.Inner.InnerVchar(vchar); + + public static Parser Instance { get; } = + InnerWsp + .Or(InnerVchar); + } + } +} diff --git a/odata/AbnfParser/CombinatorParsers/ConcatenationParser.cs b/odata/AbnfParser/CombinatorParsers/ConcatenationParser.cs new file mode 100644 index 0000000000..798f6e2ea8 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/ConcatenationParser.cs @@ -0,0 +1,21 @@ +namespace AbnfParser.CombinatorParsers +{ + using AbnfParser.CstNodes; + using Sprache; + + public static class ConcatenationParser + { + public static Parser Instance { get; } = + from repetition in RepetitionParser.Instance + from inners in InnerParser.Instance.Many() + select new Concatenation(repetition, inners); + + public static class InnerParser + { + public static Parser Instance { get; } = + from cwsps in CwspParser.Instance.AtLeastOnce() + from repetition in RepetitionParser.Instance + select new Concatenation.Inner(cwsps, repetition); + } + } +} diff --git a/odata/AbnfParser/CombinatorParsers/Core/AlphaParser.cs b/odata/AbnfParser/CombinatorParsers/Core/AlphaParser.cs new file mode 100644 index 0000000000..76b4bf4776 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/Core/AlphaParser.cs @@ -0,0 +1,270 @@ +namespace AbnfParser.CombinatorParsers.Core +{ + using AbnfParser.CstNodes.Core; + using Sprache; + + public static class AlphaParser + { + public static Parser x41 { get; } = + from value in x41Parser.Instance + select new Alpha.x41(value); + + public static Parser x42 { get; } = + from value in x42Parser.Instance + select new Alpha.x42(value); + + public static Parser x43 { get; } = + from value in x43Parser.Instance + select new Alpha.x43(value); + + public static Parser x44 { get; } = + from value in x44Parser.Instance + select new Alpha.x44(value); + + public static Parser x45 { get; } = + from value in x45Parser.Instance + select new Alpha.x45(value); + + public static Parser x46 { get; } = + from value in x46Parser.Instance + select new Alpha.x46(value); + + public static Parser x47 { get; } = + from value in x47Parser.Instance + select new Alpha.x47(value); + + public static Parser x48 { get; } = + from value in x48Parser.Instance + select new Alpha.x48(value); + + public static Parser x49 { get; } = + from value in x49Parser.Instance + select new Alpha.x49(value); + + public static Parser x4A { get; } = + from value in x4AParser.Instance + select new Alpha.x4A(value); + + public static Parser x4B { get; } = + from value in x4BParser.Instance + select new Alpha.x4B(value); + + public static Parser x4C { get; } = + from value in x4CParser.Instance + select new Alpha.x4C(value); + + public static Parser x4D { get; } = + from value in x4DParser.Instance + select new Alpha.x4D(value); + + public static Parser x4E { get; } = + from value in x4EParser.Instance + select new Alpha.x4E(value); + + public static Parser x4F { get; } = + from value in x4FParser.Instance + select new Alpha.x4F(value); + + public static Parser x50 { get; } = + from value in x50Parser.Instance + select new Alpha.x50(value); + + public static Parser x51 { get; } = + from value in x51Parser.Instance + select new Alpha.x51(value); + + public static Parser x52 { get; } = + from value in x52Parser.Instance + select new Alpha.x52(value); + + public static Parser x53 { get; } = + from value in x53Parser.Instance + select new Alpha.x53(value); + + public static Parser x54 { get; } = + from value in x54Parser.Instance + select new Alpha.x54(value); + + public static Parser x55 { get; } = + from value in x55Parser.Instance + select new Alpha.x55(value); + + public static Parser x56 { get; } = + from value in x56Parser.Instance + select new Alpha.x56(value); + + public static Parser x57 { get; } = + from value in x57Parser.Instance + select new Alpha.x57(value); + + public static Parser x58 { get; } = + from value in x58Parser.Instance + select new Alpha.x58(value); + + public static Parser x59 { get; } = + from value in x59Parser.Instance + select new Alpha.x59(value); + + public static Parser x5A { get; } = + from value in x5AParser.Instance + select new Alpha.x5A(value); + + public static Parser x61 { get; } = + from value in x61Parser.Instance + select new Alpha.x61(value); + + public static Parser x62 { get; } = + from value in x62Parser.Instance + select new Alpha.x62(value); + + public static Parser x63 { get; } = + from value in x63Parser.Instance + select new Alpha.x63(value); + + public static Parser x64 { get; } = + from value in x64Parser.Instance + select new Alpha.x64(value); + + public static Parser x65 { get; } = + from value in x65Parser.Instance + select new Alpha.x65(value); + + public static Parser x66 { get; } = + from value in x66Parser.Instance + select new Alpha.x66(value); + + public static Parser x67 { get; } = + from value in x67Parser.Instance + select new Alpha.x67(value); + + public static Parser x68 { get; } = + from value in x68Parser.Instance + select new Alpha.x68(value); + + public static Parser x69 { get; } = + from value in x69Parser.Instance + select new Alpha.x69(value); + + public static Parser x6A { get; } = + from value in x6AParser.Instance + select new Alpha.x6A(value); + + public static Parser x6B { get; } = + from value in x6BParser.Instance + select new Alpha.x6B(value); + + public static Parser x6C { get; } = + from value in x6CParser.Instance + select new Alpha.x6C(value); + + public static Parser x6D { get; } = + from value in x6DParser.Instance + select new Alpha.x6D(value); + + public static Parser x6E { get; } = + from value in x6EParser.Instance + select new Alpha.x6E(value); + + public static Parser x6F { get; } = + from value in x6FParser.Instance + select new Alpha.x6F(value); + + public static Parser x70 { get; } = + from value in x70Parser.Instance + select new Alpha.x70(value); + + public static Parser x71 { get; } = + from value in x71Parser.Instance + select new Alpha.x71(value); + + public static Parser x72 { get; } = + from value in x72Parser.Instance + select new Alpha.x72(value); + + public static Parser x73 { get; } = + from value in x73Parser.Instance + select new Alpha.x73(value); + + public static Parser x74 { get; } = + from value in x74Parser.Instance + select new Alpha.x74(value); + + public static Parser x75 { get; } = + from value in x75Parser.Instance + select new Alpha.x75(value); + + public static Parser x76 { get; } = + from value in x76Parser.Instance + select new Alpha.x76(value); + + public static Parser x77 { get; } = + from value in x77Parser.Instance + select new Alpha.x77(value); + + public static Parser x78 { get; } = + from value in x78Parser.Instance + select new Alpha.x78(value); + + public static Parser x79 { get; } = + from value in x79Parser.Instance + select new Alpha.x79(value); + + public static Parser x7A { get; } = + from value in x7AParser.Instance + select new Alpha.x7A(value); + + public static Parser Instance { get; } = + x41 + .Or(x42) + .Or(x43) + .Or(x44) + .Or(x45) + .Or(x46) + .Or(x47) + .Or(x48) + .Or(x49) + .Or(x4A) + .Or(x4B) + .Or(x4C) + .Or(x4D) + .Or(x4E) + .Or(x4F) + .Or(x50) + .Or(x51) + .Or(x52) + .Or(x53) + .Or(x54) + .Or(x55) + .Or(x56) + .Or(x57) + .Or(x58) + .Or(x59) + .Or(x5A) + .Or(x61) + .Or(x62) + .Or(x63) + .Or(x64) + .Or(x65) + .Or(x66) + .Or(x67) + .Or(x68) + .Or(x69) + .Or(x6A) + .Or(x6B) + .Or(x6C) + .Or(x6D) + .Or(x6E) + .Or(x6F) + .Or(x70) + .Or(x71) + .Or(x72) + .Or(x73) + .Or(x74) + .Or(x75) + .Or(x76) + .Or(x77) + .Or(x78) + .Or(x79) + .Or(x7A); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/Core/BitParser.cs b/odata/AbnfParser/CombinatorParsers/Core/BitParser.cs new file mode 100644 index 0000000000..8997cc35d8 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/Core/BitParser.cs @@ -0,0 +1,20 @@ +namespace AbnfParser.CombinatorParsers.Core +{ + using AbnfParser.CstNodes.Core; + using Sprache; + + public static class BitParser + { + public static Parser Zero { get; } = + from value in x30Parser.Instance + select new Bit.Zero(value); + + public static Parser One { get; } = + from value in x31Parser.Instance + select new Bit.One(value); + + public static Parser Instance { get; } = + Zero + .Or(One); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/Core/CharParser.cs b/odata/AbnfParser/CombinatorParsers/Core/CharParser.cs new file mode 100644 index 0000000000..b81c96f500 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/Core/CharParser.cs @@ -0,0 +1,899 @@ +namespace AbnfParser.CombinatorParsers.Core +{ + using AbnfParser.CstNodes.Core; + using Sprache; + + public static class CharParser + { + //// TODO implement this + } + + public static class x01Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x01) + .Return(x01.Instance); + } + + public static class x02Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x02) + .Return(x02.Instance); + } + + public static class x03Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x03) + .Return(x03.Instance); + } + + public static class x04Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x04) + .Return(x04.Instance); + } + + public static class x05Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x05) + .Return(x05.Instance); + } + + public static class x06Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x06) + .Return(x06.Instance); + } + + public static class x07Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x07) + .Return(x07.Instance); + } + + public static class x08Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x08) + .Return(x08.Instance); + } + + public static class x09Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x09) + .Return(x09.Instance); + } + + public static class x0AParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x0A) + .Return(x0A.Instance); + } + + public static class x0BParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x0B) + .Return(x0B.Instance); + } + + public static class x0CParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x0C) + .Return(x0C.Instance); + } + + public static class x0DParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x0D) + .Return(x0D.Instance); + } + + public static class x0EParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x0E) + .Return(x0E.Instance); + } + + public static class x0FParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x0F) + .Return(x0F.Instance); + } + + public static class x10Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x10) + .Return(x10.Instance); + } + + public static class x11Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x11) + .Return(x11.Instance); + } + + public static class x12Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x12) + .Return(x12.Instance); + } + + public static class x13Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x13) + .Return(x13.Instance); + } + + public static class x14Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x14) + .Return(x14.Instance); + } + + public static class x15Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x15) + .Return(x15.Instance); + } + + public static class x16Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x16) + .Return(x16.Instance); + } + + public static class x17Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x17) + .Return(x17.Instance); + } + + public static class x18Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x18) + .Return(x18.Instance); + } + + public static class x19Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x19) + .Return(x19.Instance); + } + + public static class x1AParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x1A) + .Return(x1A.Instance); + } + + public static class x1BParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x1B) + .Return(x1B.Instance); + } + + public static class x1CParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x1C) + .Return(x1C.Instance); + } + + public static class x1DParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x1D) + .Return(x1D.Instance); + } + + public static class x1EParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x1E) + .Return(x1E.Instance); + } + + public static class x1FParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x1F) + .Return(x1F.Instance); + } + + public static class x20Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x20) + .Return(x20.Instance); + } + + public static class x21Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x21) + .Return(x21.Instance); + } + + public static class x22Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x22) + .Return(x22.Instance); + } + + public static class x23Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x23) + .Return(x23.Instance); + } + + public static class x24Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x24) + .Return(x24.Instance); + } + + public static class x25Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x25) + .Return(x25.Instance); + } + + public static class x26Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x26) + .Return(x26.Instance); + } + + public static class x27Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x27) + .Return(x27.Instance); + } + + public static class x28Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x28) + .Return(x28.Instance); + } + + public static class x29Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x29) + .Return(x29.Instance); + } + + public static class x2AParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x2A) + .Return(x2A.Instance); + } + + public static class x2BParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x2B) + .Return(x2B.Instance); + } + + public static class x2CParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x2C) + .Return(x2C.Instance); + } + + public static class x2DParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x2D) + .Return(x2D.Instance); + } + + public static class x2EParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x2E) + .Return(x2E.Instance); + } + + public static class x2FParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x2F) + .Return(x2F.Instance); + } + + public static class x30Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x30) + .Return(x30.Instance); + } + + public static class x31Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x31) + .Return(x31.Instance); + } + + public static class x32Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x32) + .Return(x32.Instance); + } + + public static class x33Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x33) + .Return(x33.Instance); + } + + public static class x34Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x34) + .Return(x34.Instance); + } + + public static class x35Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x35) + .Return(x35.Instance); + } + + public static class x36Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x36) + .Return(x36.Instance); + } + + public static class x37Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x37) + .Return(x37.Instance); + } + + public static class x38Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x38) + .Return(x38.Instance); + } + + public static class x39Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x39) + .Return(x39.Instance); + } + + public static class x3AParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x3A) + .Return(x3A.Instance); + } + + public static class x3BParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x3B) + .Return(x3B.Instance); + } + + public static class x3CParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x3C) + .Return(x3C.Instance); + } + + public static class x3DParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x3D) + .Return(x3D.Instance); + } + + public static class x3EParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x3E) + .Return(x3E.Instance); + } + + public static class x3FParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x3F) + .Return(x3F.Instance); + } + + public static class x40Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x40) + .Return(x40.Instance); + } + + public static class x41Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x41) + .Return(x41.Instance); + } + + public static class x42Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x42) + .Return(x42.Instance); + } + + public static class x43Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x43) + .Return(x43.Instance); + } + + public static class x44Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x44) + .Return(x44.Instance); + } + + public static class x45Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x45) + .Return(x45.Instance); + } + + public static class x46Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x46) + .Return(x46.Instance); + } + + public static class x47Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x47) + .Return(x47.Instance); + } + + public static class x48Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x48) + .Return(x48.Instance); + } + + public static class x49Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x49) + .Return(x49.Instance); + } + + public static class x4AParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x4A) + .Return(x4A.Instance); + } + + public static class x4BParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x4B) + .Return(x4B.Instance); + } + + public static class x4CParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x4C) + .Return(x4C.Instance); + } + + public static class x4DParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x4D) + .Return(x4D.Instance); + } + + public static class x4EParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x4E) + .Return(x4E.Instance); + } + + public static class x4FParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x4F) + .Return(x4F.Instance); + } + + public static class x50Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x50) + .Return(x50.Instance); + } + + public static class x51Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x51) + .Return(x51.Instance); + } + + public static class x52Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x52) + .Return(x52.Instance); + } + + public static class x53Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x53) + .Return(x53.Instance); + } + + public static class x54Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x54) + .Return(x54.Instance); + } + + public static class x55Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x55) + .Return(x55.Instance); + } + + public static class x56Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x56) + .Return(x56.Instance); + } + + public static class x57Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x57) + .Return(x57.Instance); + } + + public static class x58Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x58) + .Return(x58.Instance); + } + + public static class x59Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x59) + .Return(x59.Instance); + } + + public static class x5AParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x5A) + .Return(x5A.Instance); + } + + public static class x5BParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x5B) + .Return(x5B.Instance); + } + + public static class x5CParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x5C) + .Return(x5C.Instance); + } + + public static class x5DParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x5D) + .Return(x5D.Instance); + } + + public static class x5EParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x5E) + .Return(x5E.Instance); + } + + public static class x5FParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x5F) + .Return(x5F.Instance); + } + + public static class x60Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x60) + .Return(x60.Instance); + } + + public static class x61Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x61) + .Return(x61.Instance); + } + + public static class x62Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x62) + .Return(x62.Instance); + } + + public static class x63Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x63) + .Return(x63.Instance); + } + + public static class x64Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x64) + .Return(x64.Instance); + } + + public static class x65Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x65) + .Return(x65.Instance); + } + + public static class x66Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x66) + .Return(x66.Instance); + } + + public static class x67Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x67) + .Return(x67.Instance); + } + + public static class x68Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x68) + .Return(x68.Instance); + } + + public static class x69Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x69) + .Return(x69.Instance); + } + + public static class x6AParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x6A) + .Return(x6A.Instance); + } + + public static class x6BParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x6B) + .Return(x6B.Instance); + } + + public static class x6CParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x6C) + .Return(x6C.Instance); + } + + public static class x6DParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x6D) + .Return(x6D.Instance); + } + + public static class x6EParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x6E) + .Return(x6E.Instance); + } + + public static class x6FParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x6F) + .Return(x6F.Instance); + } + + public static class x70Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x70) + .Return(x70.Instance); + } + + public static class x71Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x71) + .Return(x71.Instance); + } + + public static class x72Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x72) + .Return(x72.Instance); + } + + public static class x73Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x73) + .Return(x73.Instance); + } + + public static class x74Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x74) + .Return(x74.Instance); + } + + public static class x75Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x75) + .Return(x75.Instance); + } + + public static class x76Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x76) + .Return(x76.Instance); + } + + public static class x77Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x77) + .Return(x77.Instance); + } + + public static class x78Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x78) + .Return(x78.Instance); + } + + public static class x79Parser + { + public static Parser Instance { get; } = Parse + .Char((char)0x79) + .Return(x79.Instance); + } + + public static class x7AParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x7A) + .Return(x7A.Instance); + } + + public static class x7BParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x7B) + .Return(x7B.Instance); + } + + public static class x7CParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x7C) + .Return(x7C.Instance); + } + + public static class x7DParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x7D) + .Return(x7D.Instance); + } + + public static class x7EParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x7E) + .Return(x7E.Instance); + } + + public static class x7FParser + { + public static Parser Instance { get; } = Parse + .Char((char)0x7F) + .Return(x7F.Instance); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/Core/CrParser.cs b/odata/AbnfParser/CombinatorParsers/Core/CrParser.cs new file mode 100644 index 0000000000..72695c0bca --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/Core/CrParser.cs @@ -0,0 +1,12 @@ +namespace AbnfParser.CombinatorParsers.Core +{ + using AbnfParser.CstNodes.Core; + using Sprache; + + public static class CrParser + { + public static Parser Instance { get; } = + from value in x0DParser.Instance + select new Cr(value); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/Core/CrlfParser.cs b/odata/AbnfParser/CombinatorParsers/Core/CrlfParser.cs new file mode 100644 index 0000000000..2fa623c34f --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/Core/CrlfParser.cs @@ -0,0 +1,13 @@ +namespace AbnfParser.CombinatorParsers.Core +{ + using AbnfParser.CstNodes.Core; + using Sprache; + + public static class CrlfParser + { + public static Parser Instance { get; } = + from cr in CrParser.Instance + from lf in LfParser.Instance + select new Crlf(cr, lf); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/Core/DigitParser.cs b/odata/AbnfParser/CombinatorParsers/Core/DigitParser.cs new file mode 100644 index 0000000000..eb62cfb768 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/Core/DigitParser.cs @@ -0,0 +1,60 @@ +namespace AbnfParser.CombinatorParsers.Core +{ + using AbnfParser.CstNodes.Core; + using Sprache; + + public static class DigitParser + { + public static Parser x30 { get; } = + from value in x30Parser.Instance + select new Digit.x30(value); + + public static Parser x31 { get; } = + from value in x31Parser.Instance + select new Digit.x31(value); + + public static Parser x32 { get; } = + from value in x32Parser.Instance + select new Digit.x32(value); + + public static Parser x33 { get; } = + from value in x33Parser.Instance + select new Digit.x33(value); + + public static Parser x34 { get; } = + from value in x34Parser.Instance + select new Digit.x34(value); + + public static Parser x35 { get; } = + from value in x35Parser.Instance + select new Digit.x35(value); + + public static Parser x36 { get; } = + from value in x36Parser.Instance + select new Digit.x36(value); + + public static Parser x37 { get; } = + from value in x37Parser.Instance + select new Digit.x37(value); + + public static Parser x38 { get; } = + from value in x38Parser.Instance + select new Digit.x38(value); + + public static Parser x39 { get; } = + from value in x39Parser.Instance + select new Digit.x39(value); + + public static Parser Instance { get; } = + x30 + .Or(x31) + .Or(x32) + .Or(x33) + .Or(x34) + .Or(x35) + .Or(x36) + .Or(x37) + .Or(x38) + .Or(x39); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/Core/DquoteParser.cs b/odata/AbnfParser/CombinatorParsers/Core/DquoteParser.cs new file mode 100644 index 0000000000..3785655771 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/Core/DquoteParser.cs @@ -0,0 +1,12 @@ +namespace AbnfParser.CombinatorParsers.Core +{ + using AbnfParser.CstNodes.Core; + using Sprache; + + public static class DquoteParser + { + public static Parser Instance { get; } = + from value in x22Parser.Instance + select new Dquote(value); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/Core/HexDigParser.cs b/odata/AbnfParser/CombinatorParsers/Core/HexDigParser.cs new file mode 100644 index 0000000000..c215d46284 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/Core/HexDigParser.cs @@ -0,0 +1,45 @@ +namespace AbnfParser.CombinatorParsers.Core +{ + using AbnfParser.CstNodes.Core; + using Sprache; + + public static class HexDigParser + { + public static Parser Digit { get; } = + from value in DigitParser.Instance + select new HexDig.Digit(value); + + public static Parser A { get; } = + from value in x41Parser.Instance + select new HexDig.A(value); + + public static Parser B { get; } = + from value in x42Parser.Instance + select new HexDig.B(value); + + public static Parser C { get; } = + from value in x43Parser.Instance + select new HexDig.C(value); + + public static Parser D { get; } = + from value in x44Parser.Instance + select new HexDig.D(value); + + public static Parser E { get; } = + from value in x45Parser.Instance + select new HexDig.E(value); + + public static Parser F { get; } = + from value in x46Parser.Instance + select new HexDig.F(value); + + public static Parser Instance { get; } = + Digit + .Or(A) + .Or(B) + .Or(C) + .Or(D) + .Or(E) + .Or(F); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/Core/HtabParser.cs b/odata/AbnfParser/CombinatorParsers/Core/HtabParser.cs new file mode 100644 index 0000000000..22e3952b9d --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/Core/HtabParser.cs @@ -0,0 +1,12 @@ +namespace AbnfParser.CombinatorParsers.Core +{ + using AbnfParser.CstNodes.Core; + using Sprache; + + public static class HtabParser + { + public static Parser Instance { get; } = + from value in x09Parser.Instance + select new Htab(value); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/Core/LfParser.cs b/odata/AbnfParser/CombinatorParsers/Core/LfParser.cs new file mode 100644 index 0000000000..fca36b0458 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/Core/LfParser.cs @@ -0,0 +1,12 @@ +namespace AbnfParser.CombinatorParsers.Core +{ + using AbnfParser.CstNodes.Core; + using Sprache; + + public static class LfParser + { + public static Parser Instance { get; } = + from value in x0AParser.Instance + select new Lf(value); + } +} diff --git a/odata/AbnfParser/CombinatorParsers/Core/OptionParser.cs b/odata/AbnfParser/CombinatorParsers/Core/OptionParser.cs new file mode 100644 index 0000000000..8621ca7e01 --- /dev/null +++ b/odata/AbnfParser/CombinatorParsers/Core/OptionParser.cs @@ -0,0 +1,16 @@ +namespace AbnfParser.CombinatorParsers.Core +{ + using AbnfParser.CstNodes; + using Sprache; + + public static class OptionParser + { + public static Parser