jtr/Jtr.Tools/Json2CSharpTranslator.cs
mdnapo 96c203f842
All checks were successful
Run the JSON parser tests / test (push) Has been skipped
Moved UI to separate project, added Dockerfile & renamed project
2024-10-19 12:04:40 +02:00

186 lines
5.6 KiB
C#

using Jtr.Parsing.Json;
using Jtr.Parsing.Json.Syntax;
using Jtr.Tools.Extensions;
using Jtr.Parsing;
using Jtr.Parsing.Extensions;
namespace Jtr.Tools;
public static partial class Json2CSharpTranslator
{
private static readonly VisitorContainer<IJsonSyntax, Context, ITranslation> Visitors = new();
static Json2CSharpTranslator()
{
Visitors.Register<JsonObjectSyntax>(Object);
Visitors.Register<JsonArraySyntax>(Array);
Visitors.Register<JsonStringSyntax>(String);
Visitors.Register<JsonNumberSyntax>(Number);
Visitors.Register<JsonBoolSyntax>(Bool);
Visitors.Register<JsonNullSyntax>(Null);
}
public static string Translate(string input, Context? context = null)
{
if (JsonParser.Parse("<input>", input) is not JsonObjectSyntax root)
throw new SyntaxException("Expected a JSON object.");
context ??= new();
context.Name.Push(context.RootClassName);
var visitor = Visitors[typeof(JsonObjectSyntax)];
visitor(root, context);
context.Builder.Append("//using System;\n");
context.Builder.Append("//using System.Collections.Generic;\n");
context.Builder.Append('\n');
context.Builder.Append($"namespace {context.Namespace};\n\n");
context.Classes.Reverse();
context.Classes.ForEach(@class => @class.Translate(context));
context.Name.Pop();
return context.Builder.ToString();
}
private static ITranslation Translate(IJsonSyntax visitee, Context context)
{
return Visitors[visitee.GetType()](visitee, context);
}
private static ITranslation Object(IJsonSyntax visitee, Context context)
{
var @object = visitee.As<JsonObjectSyntax>();
var @class = new ClassTranslation { Name = context.CurrentClassName, Properties = new() };
context.Class.Push(@class);
context.Scope.Push(Context.TranslationScope.Object);
foreach (var prop in @object.Properties)
{
context.Name.Push(prop.Key.Lexeme);
var translation = Translate(prop.Value, context);
switch (translation)
{
case ClassTranslation:
// TODO: Handle class exists
context.Class.Peek().Properties.Add(new PropertyTranslation
{
Type = translation.Name,
Name = prop.Key.Lexeme,
});
break;
case PropertyTranslation property:
// TODO: Handle property exists
context.Class.Peek().Properties.Add(property);
break;
}
context.Name.Pop();
}
if (!context.Scope.IsChildOf(Context.TranslationScope.Array))
{
context.Classes.Add(@class);
}
context.Scope.Pop();
return context.Class.Pop();
}
private static ITranslation Array(IJsonSyntax visitee, Context context)
{
var array = visitee.As<JsonArraySyntax>();
var type = "object";
context.Scope.Push(Context.TranslationScope.Array);
if (array.Elements.Length == 0)
{
type = "object";
}
else if (array.Elements.All(e => e is JsonObjectSyntax))
{
type = context.CurrentClassName;
var composite = array.Elements
.Select(el => Translate(el, context))
.Cast<ClassTranslation>();
var @class = new ClassTranslation
{
Name = type,
Properties = composite
.SelectMany(cls => cls.Properties)
.ToList(),
};
context.Classes.Add(@class);
}
else if (array.Elements.All(e => e is JsonStringSyntax es && DateTime.TryParse(es.Value, out _)))
{
type = "DateTime";
}
else if (array.Elements.All(e => e is JsonStringSyntax))
{
type = "string";
}
else if (array.Elements.All(e => e is JsonNumberSyntax))
{
type = array.Elements.Any(e => ((JsonNumberSyntax)e).Token.Lexeme.Contains('.')) ? "double" : "int";
}
context.Scope.Pop();
return new PropertyTranslation
{
Type = $"List<{type}>",
Name = context.Name.Peek(),
};
}
private static ITranslation String(IJsonSyntax visitee, Context context)
{
var @string = visitee.As<JsonStringSyntax>();
var type = DateTime.TryParse(@string.Value, out _) ? "DateTime" : "string";
return new PropertyTranslation
{
Type = type,
Name = context.Name.Peek(),
};
}
private static ITranslation Number(IJsonSyntax visitee, Context context)
{
return new PropertyTranslation
{
Type = visitee.As<JsonNumberSyntax>().Token.Lexeme.Contains('.') ? "double" : "int",
Name = context.Name.Peek(),
};
}
private static ITranslation Bool(IJsonSyntax visitee, Context context)
{
return new PropertyTranslation
{
Type = "bool",
Name = context.Name.Peek(),
};
}
private static ITranslation Null(IJsonSyntax visitee, Context context)
{
return new PropertyTranslation
{
Type = "object",
Name = context.Name.Peek(),
};
}
}