All checks were successful
Run the JSON parser tests / test (push) Has been skipped
186 lines
5.6 KiB
C#
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(),
|
|
};
|
|
}
|
|
} |