diff --git a/Hypercube.Utilities.sln b/Hypercube.Utilities.sln index 0a62cca..0b7609f 100644 --- a/Hypercube.Utilities.sln +++ b/Hypercube.Utilities.sln @@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hypercube.Utilities.Analyze EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hypercube.Utilities.Analyzers.CodeFix", "src\Hypercube.Utilities.Analyzers.CodeFix\Hypercube.Utilities.Analyzers.CodeFix.csproj", "{33E0A347-BAE3-43A1-9D6B-E5AF033CCE16}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Main", "Main\Main.csproj", "{6BB9C8F4-4BA5-45EA-8924-1DE5771DF4BF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,5 +32,9 @@ Global {33E0A347-BAE3-43A1-9D6B-E5AF033CCE16}.Debug|Any CPU.Build.0 = Debug|Any CPU {33E0A347-BAE3-43A1-9D6B-E5AF033CCE16}.Release|Any CPU.ActiveCfg = Release|Any CPU {33E0A347-BAE3-43A1-9D6B-E5AF033CCE16}.Release|Any CPU.Build.0 = Release|Any CPU + {6BB9C8F4-4BA5-45EA-8924-1DE5771DF4BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BB9C8F4-4BA5-45EA-8924-1DE5771DF4BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BB9C8F4-4BA5-45EA-8924-1DE5771DF4BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BB9C8F4-4BA5-45EA-8924-1DE5771DF4BF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Main/Main.csproj b/Main/Main.csproj new file mode 100644 index 0000000..fcb2d26 --- /dev/null +++ b/Main/Main.csproj @@ -0,0 +1,14 @@ + + + + Exe + net9.0 + enable + enable + + + + + + + diff --git a/Main/Program.cs b/Main/Program.cs new file mode 100644 index 0000000..66a0f3c --- /dev/null +++ b/Main/Program.cs @@ -0,0 +1,13 @@ +using Hypercube.Utilities.Serialization.Hml; + +namespace Main; + +public static class Program +{ + public static void Main() + { + var data = new { Name = "ТесмиДев", Age = 20, Da = new {}, Roles = new[] {"Programmer", "Driver"} }; + //var data = new[] { new[] { "Programmer" }, new[] { "Programmer" }}; + Console.WriteLine(HmlSerializer.Serialize(data, new HmlSerializerOptions() { WriteIndented = true })); + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities.Analyzers.CodeFix/DependencyCodeFixProvider.cs b/src/Hypercube.Utilities.Analyzers.CodeFix/DependencyCodeFixProvider.cs index c5122dd..d488ff7 100644 --- a/src/Hypercube.Utilities.Analyzers.CodeFix/DependencyCodeFixProvider.cs +++ b/src/Hypercube.Utilities.Analyzers.CodeFix/DependencyCodeFixProvider.cs @@ -5,7 +5,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; -namespace Hypercube.Analyzers.CodeFix; +namespace Hypercube.Utilities.Analyzers.CodeFix; [Shared, ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DependencyCodeFixProvider))] public sealed class DependencyCodeFixProvider : CodeFixProvider diff --git a/src/Hypercube.Utilities.Analyzers/DependencyAnalyzer.cs b/src/Hypercube.Utilities.Analyzers/DependencyAnalyzer.cs index 65d0ee6..fb0a5dd 100644 --- a/src/Hypercube.Utilities.Analyzers/DependencyAnalyzer.cs +++ b/src/Hypercube.Utilities.Analyzers/DependencyAnalyzer.cs @@ -4,7 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace Hypercube.Analyzers; +namespace Hypercube.Utilities.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class DependencyAnalyzer : DiagnosticAnalyzer diff --git a/src/Hypercube.Utilities.Analyzers/DependencySuppressor.cs b/src/Hypercube.Utilities.Analyzers/DependencySuppressor.cs index f84c20b..31e6b79 100644 --- a/src/Hypercube.Utilities.Analyzers/DependencySuppressor.cs +++ b/src/Hypercube.Utilities.Analyzers/DependencySuppressor.cs @@ -3,7 +3,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace Hypercube.Analyzers; +namespace Hypercube.Utilities.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class DependencySuppressor : DiagnosticSuppressor diff --git a/src/Hypercube.Utilities/Helpers/ReflectionHelper.cs b/src/Hypercube.Utilities/Helpers/ReflectionHelper.cs index fdd5b3a..6ea88ba 100644 --- a/src/Hypercube.Utilities/Helpers/ReflectionHelper.cs +++ b/src/Hypercube.Utilities/Helpers/ReflectionHelper.cs @@ -1,7 +1,5 @@ using System.Reflection; -using System.Xml.XPath; using Hypercube.Utilities.Extensions; -using Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value; using JetBrains.Annotations; namespace Hypercube.Utilities.Helpers; @@ -262,7 +260,7 @@ public static IReadOnlyList GetValueInfos(Type type, BindingFlags? fl foreach (var info in type.GetFields((BindingFlags) flags)) { - if (info.Name.Contains("k__BackingField")) + if (info.Name.Contains("k__BackingField") || info.Name.Contains("i__Field")) continue; if (Attribute.IsDefined(info, typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute))) diff --git a/src/Hypercube.Utilities/Hypercube.Utilities.csproj b/src/Hypercube.Utilities/Hypercube.Utilities.csproj index ef57b3b..73de427 100644 --- a/src/Hypercube.Utilities/Hypercube.Utilities.csproj +++ b/src/Hypercube.Utilities/Hypercube.Utilities.csproj @@ -15,5 +15,9 @@ + + + + diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/CompilerTypes/BuildASTStackFrame.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/CompilerTypes/BuildASTStackFrame.cs new file mode 100644 index 0000000..cd55a54 --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/CompilerTypes/BuildASTStackFrame.cs @@ -0,0 +1,9 @@ +using Hypercube.Utilities.Serialization.Hml.Core.Nodes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +public record BuildAstStackFrame() +{ + public required Node Node; + public required Node Parent; +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/CompilerTypes/RenderASTStackFrame.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/CompilerTypes/RenderASTStackFrame.cs new file mode 100644 index 0000000..f9e288f --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/CompilerTypes/RenderASTStackFrame.cs @@ -0,0 +1,15 @@ +using Hypercube.Utilities.Serialization.Hml.Core.Nodes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +public record RenderAstStackFrame +{ + public INode Node; + public int State; + public int Index; + + public RenderAstStackFrame(INode node) + { + Node = node; + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/CompilerTypes/RenderASTState.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/CompilerTypes/RenderASTState.cs new file mode 100644 index 0000000..7fd4972 --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/CompilerTypes/RenderASTState.cs @@ -0,0 +1,17 @@ +namespace Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +public class RenderAstState +{ + public string Indent { get; private set; } = string.Empty; + public int IndentSize { get; init; } + + public void PushIndent() + { + Indent += new string(' ', IndentSize); + } + + public void PopIndent() + { + Indent = Indent.Remove(Indent.Length - IndentSize); + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/HmlCompiler.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/HmlCompiler.cs index d61c190..b1aac68 100644 --- a/src/Hypercube.Utilities/Serialization/Hml/Core/HmlCompiler.cs +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/HmlCompiler.cs @@ -1,123 +1,119 @@ using System.Collections; using System.Text; +using System.Text.Json; using Hypercube.Utilities.Helpers; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; +using Hypercube.Utilities.Serialization.Hml.Core.Nodes; +using Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value; +using Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value.Primitives; namespace Hypercube.Utilities.Serialization.Hml.Core; public static class HmlCompiler { - private record StackItem(object? Obj, int Indent, string? Name, bool IsClosing = false, bool Enumerated = false); - public static StringBuilder Serialize(object? obj, HmlSerializerOptions options) { - var builder = new StringBuilder(); - var stack = new Stack(); - - stack.Push(new StackItem(obj, 0, null)); + var stack = new Stack(); + var nodeQueue = new Queue(); + stack.Push(obj); while (stack.Count > 0) { var current = stack.Pop(); - var indent = new string(' ', current.Indent * options.IndentSize); - if (current.IsClosing) + if (current is Node currentNode) { - builder.Append(indent); - - if (current.Obj is not null) - builder.Append(current.Obj); - - if (current.Enumerated) - builder.Append(','); - - builder.AppendLine(); + nodeQueue.Enqueue(currentNode); continue; } - - if (current.Obj is null) + + if (current is null) { - builder.Append(indent); - - if (current.Name is not null) - { - builder.Append(current.Name); - builder.Append(": "); - } - - builder.AppendLine(current.Enumerated ? "null," : "null"); + nodeQueue.Enqueue(new NullValueNode()); continue; } - - var type = current.Obj.GetType(); - if (IsSimpleType(current.Obj) || IsStringType(current.Obj)) + + if (IsSimpleType(current) || IsStringType(current)) { - builder.Append(indent); - - if (current.Name is not null) + PrimitiveValueNode node = current switch { - builder.Append(current.Name); - builder.Append(": "); - } - - builder.Append(FormatValue(current.Obj, in options)); - - if (current.Enumerated) - builder.Append(','); + bool value => new BoolValue() { Value = value }, + decimal value => new NumberValueNode() { Value = value}, + float value => new NumberValueNode() { Value = (decimal)value}, + double value => new NumberValueNode() { Value = (decimal)value}, + string value => new StringValueNode() { Value = value}, + char value => new StringValueNode() { Value = $"{value}"}, + _ => new UnknownValueNode() { Value = current! } + }; - builder.AppendLine(); + nodeQueue.Enqueue(node); continue; } - - if (current.Obj is IEnumerable enumerable) - { - builder.Append(indent); - - if (current.Name is not null) - { - builder.Append(current.Name); - builder.Append(": "); - } - builder.Append('['); - builder.AppendLine(); + if (current is IList list) + { + nodeQueue.Enqueue(new ListNode()); - var items = new List(); - foreach (var e in enumerable) - items.Add(e!); + stack.Push(new EndNode()); - stack.Push(new StackItem("]", current.Indent, null, true, Enumerated: current.Enumerated)); - for (var i = items.Count - 1; i >= 0; i--) - stack.Push(new StackItem(items[i], current.Indent + 1, null, Enumerated: i != items.Count - 1 || options.TrailingComma)); + for (var i = list.Count - 1; i >= 0; i--) + stack.Push(list[i]); continue; } - - builder.Append(indent); - - if (current.Name is not null) - { - builder.Append(current.Name); - builder.Append(": "); - } - - builder.Append('{'); - builder.AppendLine(); + var type = current.GetType(); var values = ReflectionHelper.GetValueInfos(type); - - stack.Push(new StackItem("}", current.Indent, null, true, Enumerated: current.Enumerated)); + nodeQueue.Enqueue(new ObjectNode()); + stack.Push(new EndNode()); for (var i = values.Count - 1; i >= 0; i--) { var valueInfo = values[i]; - var value = valueInfo.GetValue(current.Obj); - stack.Push(new StackItem(value!, current.Indent + 1, valueInfo.Name)); + var value = valueInfo.GetValue(current); + + stack.Push(value); + stack.Push(new IdentifierNode { Name = valueInfo.Name }); + stack.Push(new KeyValuePairNode()); } } + + return RenderAst(BuildAst(nodeQueue), options); + } + + private static RootNode BuildAst(Queue nodes) + { + var stack = new Stack(); + var ast = new RootNode(); + stack.Push(new BuildAstStackFrame { Node = ast, Parent = null! }); + + while (stack.Count > 0) + { + var frame = stack.Pop(); + + frame.Node.Parent = frame.Parent; + frame.Node.OnBuild(stack, nodes, frame); + } - return builder; + return ast; } + private static StringBuilder RenderAst(RootNode ast, HmlSerializerOptions options) + { + var state = new RenderAstState() { IndentSize = options.IndentSize}; + var result = new StringBuilder(); + var stack = new Stack(); + stack.Push(new RenderAstStackFrame(ast)); + + while (stack.Count > 0) + { + var frame = stack.Pop(); + result.Append(frame.Node.Render(stack, result, frame, state, options)); + } + + return result; + } + private static bool IsSimpleType(object obj) { var type = obj.GetType(); @@ -129,18 +125,4 @@ private static bool IsStringType(object obj) var type = obj.GetType(); return type == typeof(string) || type == typeof(char); } - - private static string FormatValue(object obj, in HmlSerializerOptions options) - { - return obj switch - { - bool value => value ? "true" : "false", - decimal value => value.ToString(options.CultureInfo), - float value => value.ToString(options.CultureInfo), - double value => value.ToString(options.CultureInfo), - string value => $"'{value}'", - char value => $"'{value}'", - _ => obj.ToString()! - }; - } } diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/HmlParser.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/HmlParser.cs index b32f2ef..fbb019b 100644 --- a/src/Hypercube.Utilities/Serialization/Hml/Core/HmlParser.cs +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/HmlParser.cs @@ -6,7 +6,7 @@ namespace Hypercube.Utilities.Serialization.Hml.Core; public static class HmlParser { - public static IReadOnlyList Parse(IReadOnlyList tokens) + /*public static IReadOnlyList Parse(IReadOnlyList tokens) { var result = new List(); var pos = 0; @@ -15,7 +15,7 @@ public static IReadOnlyList Parse(IReadOnlyList tokens) result.Add(ParseDefinition(tokens, ref pos)); return result; - } + }*/ private static Token Consume(IReadOnlyList tokens, ref int pos, TokenType expected) { @@ -26,7 +26,7 @@ private static Token Consume(IReadOnlyList tokens, ref int pos, TokenType return token; } - private static DefinitionNode ParseDefinition(IReadOnlyList tokens, ref int pos) + /*private static DefinitionNode ParseDefinition(IReadOnlyList tokens, ref int pos) { var name = Consume(tokens, ref pos, TokenType.Identifier).Value; Consume(tokens, ref pos, TokenType.LAngle); @@ -42,13 +42,14 @@ private static DefinitionNode ParseDefinition(IReadOnlyList tokens, ref i TypeName = typeName, Value = value }; - } + }*/ private static ValueNode ParseValue(IReadOnlyList tokens, ref int pos) { if (Match(tokens, ref pos, TokenType.LBrace)) return ParseObject(tokens, ref pos); if (Match(tokens, ref pos, TokenType.LBracket)) return ParseArray(tokens, ref pos); - return ParseLiteral(tokens, ref pos); + //return ParseLiteral(tokens, ref pos); + return default!; } private static ObjectNode ParseObject(IReadOnlyList tokens, ref int pos) @@ -61,31 +62,31 @@ private static ObjectNode ParseObject(IReadOnlyList tokens, ref int pos) var key = Consume(tokens, ref pos, TokenType.Identifier).Value; Consume(tokens, ref pos, TokenType.Colon); var val = ParseValue(tokens, ref pos); - obj.Properties[key] = val; + obj.Properties1[key] = val; } Consume(tokens, ref pos, TokenType.RBrace); return obj; } - private static ArrayNode ParseArray(IReadOnlyList tokens, ref int pos) + private static ListNode ParseArray(IReadOnlyList tokens, ref int pos) { Consume(tokens, ref pos, TokenType.LBracket); - var arr = new ArrayNode(); + var arr = new ListNode(); while (!Match(tokens, ref pos, TokenType.RBracket)) { var key = Consume(tokens, ref pos, TokenType.Identifier).Value; Consume(tokens, ref pos, TokenType.Colon); var val = ParseValue(tokens, ref pos); - arr.Elements.Add(new KeyValuePair(key, val)); + //arr.Elements.Add(new KeyValuePair(key, val)); } Consume(tokens, ref pos, TokenType.RBracket); return arr; } - private static LiteralNode ParseLiteral(IReadOnlyList tokens, ref int pos) + /* private static LiteralNode ParseLiteral(IReadOnlyList tokens, ref int pos) { var tok = Peek(tokens, ref pos); if (tok.Type is TokenType.String or TokenType.Number or TokenType.Boolean) @@ -95,7 +96,7 @@ private static LiteralNode ParseLiteral(IReadOnlyList tokens, ref int pos } throw new Exception($"Expected literal, got {tok.Type}"); - } + }*/ [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Token Peek(IReadOnlyList tokens, ref int pos) diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/DefinitionNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/DefinitionNode.cs deleted file mode 100644 index f19bd57..0000000 --- a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/DefinitionNode.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value; - -namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes; - -public class DefinitionNode : Node -{ - public string Name { get; set; } = ""; - public string TypeName { get; set; } = ""; - public ValueNode Value { get; set; } = default!; -} diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/IIdentifier.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/IIdentifier.cs new file mode 100644 index 0000000..04473d1 --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/IIdentifier.cs @@ -0,0 +1,6 @@ +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes; + +public interface IIdentifierNode : INode +{ + string Name { get; } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/INode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/INode.cs new file mode 100644 index 0000000..02ce9cb --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/INode.cs @@ -0,0 +1,12 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes; + +public interface INode +{ + INode Parent { get; } + + void OnBuild(Stack stack, Queue nodes, BuildAstStackFrame frame); + string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options); +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/IdentifierNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/IdentifierNode.cs new file mode 100644 index 0000000..d95d85f --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/IdentifierNode.cs @@ -0,0 +1,19 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes; + +public class IdentifierNode : Node, IIdentifierNode +{ + public required string Name { get; init; } + + public override void OnBuild(Stack stack, Queue nodes, BuildAstStackFrame frame) + { + // Do noting + } + + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + return Name; + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Node.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Node.cs index 4f8569a..f16f114 100644 --- a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Node.cs +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Node.cs @@ -1,3 +1,12 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes; -public abstract class Node; +public abstract class Node : INode +{ + public INode Parent { get; set; } = null!; + + public abstract void OnBuild(Stack stack, Queue nodes, BuildAstStackFrame frame); + public abstract string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options); +} diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/RootNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/RootNode.cs new file mode 100644 index 0000000..8766168 --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/RootNode.cs @@ -0,0 +1,21 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes; + +public class RootNode : Node +{ + public Node Child = null!; + + public override void OnBuild(Stack stack, Queue nodes, BuildAstStackFrame frame) + { + Child = nodes.Dequeue(); + stack.Push(new BuildAstStackFrame { Node = Child, Parent = this }); + } + + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + stack.Push(new RenderAstStackFrame(Child)); + return string.Empty; + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/ArrayNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/ArrayNode.cs deleted file mode 100644 index 776651d..0000000 --- a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/ArrayNode.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value; - -public class ArrayNode : ValueNode -{ - public List> Elements { get; set; } = []; -} diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/EndNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/EndNode.cs new file mode 100644 index 0000000..ac4669e --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/EndNode.cs @@ -0,0 +1,17 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value; + +public class EndNode : Node +{ + public override void OnBuild(Stack stack, Queue nodes, BuildAstStackFrame frame) + { + // Do nothing + } + + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + return string.Empty; + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Expression/BinaryExpressionNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Expression/BinaryExpressionNode.cs index 2f91840..07523b9 100644 --- a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Expression/BinaryExpressionNode.cs +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Expression/BinaryExpressionNode.cs @@ -1,8 +1,20 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value.Expression; public class BinaryExpressionNode : ExpressionNode { - public ValueNode Left { get; set; } = default!; + public ValueNode Left { get; set; } = null!; public string Operator { get; set; } = ""; - public ValueNode Right { get; set; } = default!; + public ValueNode Right { get; set; } = null!; + public override void OnBuild(Stack stack, Queue nodes, BuildAstStackFrame frame) + { + + } + + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + return string.Empty; + } } diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/KeyValuePair.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/KeyValuePair.cs new file mode 100644 index 0000000..3886865 --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/KeyValuePair.cs @@ -0,0 +1,56 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value; + +public class KeyValuePairNode : Node +{ + public IIdentifierNode Key = null!; + public ValueNode Value = null!; + + public override void OnBuild(Stack stack, Queue nodes, BuildAstStackFrame frame) + { + var node = nodes.Dequeue(); + + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (Key is null) + { + if (node is not IIdentifierNode keyNode) + throw new Exception(""); + + stack.Push(frame); + stack.Push(new BuildAstStackFrame { Node = node, Parent = this}); + Key = keyNode; + return; + } + + if (node is not ValueNode valueNode) + throw new Exception(""); + + Value = valueNode; + stack.Push(new BuildAstStackFrame { Node = node, Parent = this}); + } + + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + if (frame.State == 0) + { + stack.Push(frame with { State = 1 }); + stack.Push(new RenderAstStackFrame(Key)); + return string.Empty; + } + + if (frame.State == 1) + { + buffer.Append(":"); + + if (options.WriteIndented) + buffer.Append(" "); + + stack.Push(new RenderAstStackFrame(Value)); + return string.Empty; + } + + return string.Empty; + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/ListNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/ListNode.cs new file mode 100644 index 0000000..527875c --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/ListNode.cs @@ -0,0 +1,87 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value; + +public class ListNode : ValueNode +{ + public List Elements { get; set; } = []; + + public override void OnBuild(Stack stack, Queue nodes, BuildAstStackFrame frame) + { + var nextNode = nodes.Dequeue(); + + if (nextNode is EndNode) + return; + + if (nextNode is not ValueNode valueNode) + throw new Exception(""); + + Elements.Add(valueNode); + stack.Push(frame); + stack.Push(new BuildAstStackFrame() { Node = valueNode, Parent = this}); + } + + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + if (frame.State == 0) + { + buffer.Append('['); + state.PushIndent(); + + if (Elements.Count == 0) + { + buffer.Append(']'); + state.PopIndent(); + return string.Empty; + } + + if (options.WriteIndented) + { + buffer.AppendLine(); + buffer.Append(state.Indent); + } + + stack.Push(frame with { State = 2 }); + if (Elements.Count > 1) + stack.Push(frame with { State = 1, Index = 1}); + + stack.Push(new RenderAstStackFrame(Elements[0])); + return string.Empty; + } + + if (frame.State == 1) + { + if (frame.Index < Elements.Count) + { + var index = frame.Index; + buffer.Append(','); + + if (options.WriteIndented) + { + buffer.AppendLine(); + buffer.Append(state.Indent); + } + + stack.Push(frame with { State = 1, Index = index + 1 }); + stack.Push(new RenderAstStackFrame(Elements[index])); + } + + return string.Empty; + } + + if (frame.State == 2) + { + if (options.WriteIndented) + { + buffer.AppendLine(); + state.PopIndent(); + buffer.Append(state.Indent); + } + buffer.Append(']'); + return string.Empty; + } + + return string.Empty; + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/LiteralNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/LiteralNode.cs deleted file mode 100644 index d7f52aa..0000000 --- a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/LiteralNode.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value; - -public class LiteralNode : ValueNode -{ - public string Value { get; set; } = ""; -} diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/ObjectNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/ObjectNode.cs index 4bbc670..8c1af2a 100644 --- a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/ObjectNode.cs +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/ObjectNode.cs @@ -1,6 +1,88 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value; public class ObjectNode : ValueNode { - public Dictionary Properties { get; set; } = new(); + public Dictionary Properties1 { get; set; } = new(); + public List Properties { get; set; } = new(); + + public override void OnBuild(Stack stack, Queue nodes, BuildAstStackFrame frame) + { + var nextNode = nodes.Dequeue(); + + if (nextNode is EndNode) + return; + + if (nextNode is not KeyValuePairNode keyValuePair) + throw new Exception(""); + + Properties.Add(keyValuePair); + stack.Push(frame); + stack.Push(new BuildAstStackFrame() { Node = keyValuePair, Parent = this }); + } + + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + if (frame.State == 0) + { + buffer.Append('{'); + state.PushIndent(); + + if (options.WriteIndented && Properties.Count != 0) + { + buffer.AppendLine(); + buffer.Append(state.Indent); + } + + if (Properties.Count == 0) + { + buffer.Append('}'); + state.PopIndent(); + return string.Empty; + } + + stack.Push(frame with { State = 2 }); + if (Properties.Count > 1) + stack.Push(frame with { State = 1, Index = 1 }); + + stack.Push(new RenderAstStackFrame(Properties[0])); + return string.Empty; + } + + if (frame.State == 1) + { + if (frame.Index < Properties.Count) + { + var index = frame.Index; + buffer.Append(','); + + if (options.WriteIndented) + { + buffer.AppendLine(); + buffer.Append(state.Indent); + } + + stack.Push(frame with { State = 1, Index = index + 1 }); + stack.Push(new RenderAstStackFrame(Properties[index])); + } + + return string.Empty; + } + + if (frame.State == 2) + { + if (options.WriteIndented) + { + buffer.AppendLine(); + state.PopIndent(); + buffer.Append(state.Indent); + } + buffer.Append('}'); + return string.Empty; + } + + return string.Empty; + } } diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/PrimitiveValueNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/PrimitiveValueNode.cs new file mode 100644 index 0000000..0323cbd --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/PrimitiveValueNode.cs @@ -0,0 +1,11 @@ +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value; + +public abstract class PrimitiveValueNode : ValueNode +{ + public override void OnBuild(Stack stack, Queue nodes, BuildAstStackFrame frame) + { + // Do nothing + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/BoolValue.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/BoolValue.cs new file mode 100644 index 0000000..6603e79 --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/BoolValue.cs @@ -0,0 +1,14 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value.Primitives; + +public class BoolValue : PrimitiveValueNode +{ + public required bool Value; + + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + return Value.ToString(); + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/NullValueNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/NullValueNode.cs new file mode 100644 index 0000000..81585bf --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/NullValueNode.cs @@ -0,0 +1,12 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value.Primitives; + +public class NullValueNode : PrimitiveValueNode +{ + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + return "null"; + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/NumberValueNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/NumberValueNode.cs new file mode 100644 index 0000000..1466094 --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/NumberValueNode.cs @@ -0,0 +1,16 @@ +using System.Globalization; +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value.Primitives; + +public class NumberValueNode : PrimitiveValueNode +{ + public required decimal Value; + + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + return Value.ToString(options.CultureInfo) ?? string.Empty; + } + +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/StringValueNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/StringValueNode.cs new file mode 100644 index 0000000..28a1bf6 --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/StringValueNode.cs @@ -0,0 +1,16 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value.Primitives; + +public class StringValueNode : PrimitiveValueNode, IIdentifierNode +{ + public required string Value; + public string Name => Value; + + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + return $"'{Value}'"; + } + +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/UnknownValueNode.cs b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/UnknownValueNode.cs new file mode 100644 index 0000000..df7018a --- /dev/null +++ b/src/Hypercube.Utilities/Serialization/Hml/Core/Nodes/Value/Primitives/UnknownValueNode.cs @@ -0,0 +1,14 @@ +using System.Text; +using Hypercube.Utilities.Serialization.Hml.Core.CompilerTypes; + +namespace Hypercube.Utilities.Serialization.Hml.Core.Nodes.Value.Primitives; + +public class UnknownValueNode : PrimitiveValueNode +{ + public required object Value; + + public override string Render(Stack stack, StringBuilder buffer, RenderAstStackFrame frame, RenderAstState state, HmlSerializerOptions options) + { + return Value.ToString() ?? string.Empty; + } +} \ No newline at end of file diff --git a/src/Hypercube.Utilities/Serialization/Hml/HmlSerializer.cs b/src/Hypercube.Utilities/Serialization/Hml/HmlSerializer.cs index dbb4070..ca00f9f 100644 --- a/src/Hypercube.Utilities/Serialization/Hml/HmlSerializer.cs +++ b/src/Hypercube.Utilities/Serialization/Hml/HmlSerializer.cs @@ -17,10 +17,10 @@ public static object Deserialize(string content, HmlSerializerOptions? options = public static T Deserialize(string content, HmlSerializerOptions? options = null) { - options ??= new HmlSerializerOptions(); + /*options ??= new HmlSerializerOptions(); var tokens = HmlLexer.Tokenize(content); - var ast = HmlParser.Parse(tokens); + var ast = HmlParser.Parse(tokens);*/ return default!; //compiler.Compile(ast, options); } diff --git a/src/Hypercube.Utilities/Serialization/Hml/HmlSerializerOptions.cs b/src/Hypercube.Utilities/Serialization/Hml/HmlSerializerOptions.cs index 4632f59..8085dc9 100644 --- a/src/Hypercube.Utilities/Serialization/Hml/HmlSerializerOptions.cs +++ b/src/Hypercube.Utilities/Serialization/Hml/HmlSerializerOptions.cs @@ -2,10 +2,12 @@ namespace Hypercube.Utilities.Serialization.Hml; -public readonly struct HmlSerializerOptions +public record HmlSerializerOptions { - public CultureInfo CultureInfo { get; init; } = CultureInfo.InvariantCulture; + public CultureInfo CultureInfo => CultureInfo.InvariantCulture; public bool TrailingComma { get; init; } = true; + + public bool WriteIndented { get; init; } public int IndentSize { get; init; } = 2; public HmlSerializerOptions()