Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: v2 to v3 converter #293

Merged
merged 1 commit into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Compiler/CliStrings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Compiler;

public static class CliStrings {
public static class CliStrings
{

public const string ConfigFlag = "--config";
public const string NoEmitFlag = "--no-emit";
Expand All @@ -19,8 +20,15 @@ public static class CliStrings {
public const string TraceFlag = "--trace";
public const string StandardInputFlag = "--stdin";
public const string StandardOutputFlag = "--stdout";

public const string DryRunFlag = "--dry-run";

public const string FromFlag = "--from";
public const string ToFlag = "--to";
public const string WatchCommand = "watch";
public const string BuildCommand = "build";

public const string ConvertCommand = "convert";
public const string LangServerCommand = "langserver";

}
132 changes: 132 additions & 0 deletions Compiler/Commands/ConvertCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.IO;
using System.Text.RegularExpressions;
using Core.Logging;
using Core.Meta;
using Spectre.Console;

namespace Compiler.Commands;

public partial class ConvertCommand : CliCommand
{

public ConvertCommand() : base(CliStrings.ConvertCommand, "Convert schema files from one format to another.")
{
SetAction(HandleCommand);
}

private int HandleCommand(ParseResult result)
{
var config = result.GetValue<BebopConfig>(CliStrings.ConfigFlag)!;
config.Validate();
var schemas = config.ResolveIncludes();
if (result.GetValue<string>(CliStrings.FromFlag) is "v2" && result.GetValue<string>(CliStrings.ToFlag) is "v3")
{
var isDryRun = result.GetValue<bool>(CliStrings.DryRunFlag);
if (isDryRun)
{
DiagnosticLogger.Instance.Out.MarkupLine("[yellow]Dry run mode enabled. No files will be written.[/]");
}
foreach (var (schemaPath, originalSchema, transformedSchema) in TwoToThreeConverter.Convert(schemas))
{
ShowDiff(schemaPath, originalSchema, transformedSchema);
if (!isDryRun)
{
File.WriteAllText(schemaPath, transformedSchema);
}
}
return 0;
}
DiagnosticLogger.Instance.Error.MarkupLine("[red]Only conversion from v2 to v3 is currently supported.[/]");
return 1;
}

private static readonly string[] separators = ["\r\n", "\r", "\n"];

internal static void ShowDiff(string schemaPath, string originalContent, string transformedSchema)
{
string[] originalLines = originalContent.Split(separators, StringSplitOptions.None);
string[] transformedLines = transformedSchema.Split(separators, StringSplitOptions.None);

var panelContent = new List<string>();
int maxLines = Math.Max(originalLines.Length, transformedLines.Length);

for (int i = 0; i < maxLines; i++)
{
string originalLine = i < originalLines.Length ? originalLines[i] : string.Empty;
string modifiedLine = i < transformedLines.Length ? transformedLines[i] : string.Empty;

if (originalLine != modifiedLine)
{
if (!string.IsNullOrEmpty(originalLine))
{
panelContent.Add($"[yellow]- {originalLine.EscapeMarkup()}[/]");
}
if (!string.IsNullOrEmpty(modifiedLine))
{
panelContent.Add($"[blue]+ {modifiedLine.EscapeMarkup()}[/]");
}
}
else
{
panelContent.Add($"{originalLine.EscapeMarkup()}");
}
}




// Render in a panel
var panel = new Panel(string.Join(Environment.NewLine, panelContent))
.Header($"[underline white][bold]{schemaPath}[/][/]")
.Expand()

.Border(BoxBorder.Heavy);



DiagnosticLogger.Instance.Out.Write(panel);
}


internal static partial class TwoToThreeConverter
{
public static IEnumerable<(string SchemaPath, string OriginalSchema, string TransformedSchema)> Convert(IEnumerable<string> schemas)
{

foreach (var schemaPath in schemas)
{

var schema = File.ReadAllText(schemaPath);
var wasModified = false;
var transformedContent = AttributePattern().Replace(schema, match =>
{
wasModified = true;
string identifier = match.Groups[1].Value;
string? value = match.Groups[2].Success ? match.Groups[2].Value : null;
// Handling for cases with and without value
return value != null ? $"@{identifier}({value})" : $"@{identifier}";
});
transformedContent = ReadOnlyPattern().Replace(transformedContent, match =>
{
wasModified = true;
// Remove 'readonly' if present, otherwise append 'mut'
return match.Groups[1].Success ? $"struct {match.Groups[2].Value}" : $"mut struct {match.Groups[2].Value}";
});
if (wasModified)
{
yield return (schemaPath, schema, transformedContent);
}
}
}

[GeneratedRegex(@"\[([\w]+)(?:\(([^,\]]*?)\))?\](?![\],])")]
private static partial Regex AttributePattern();
[GeneratedRegex(@"(readonly\s+)?struct\s+(\w+)")]
private static partial Regex ReadOnlyPattern();
}


}
34 changes: 32 additions & 2 deletions Compiler/Options/SimpleOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public WatchExcludeDirectoriesOption() : base(
/// Represents a command line option for excluding certain files from being watched.
/// </summary>
public class WatchExcludeFilesOption : CliOption<string[]>
{
{
public WatchExcludeFilesOption() : base(
name: CliStrings.ExcludeFilesFlag)
{
Expand Down Expand Up @@ -101,7 +101,7 @@ public InitOption() : base(
AllowMultipleArgumentsPerToken = false;
}
}

public class ListSchemaOption : CliOption<bool>
{
public ListSchemaOption() : base(
Expand Down Expand Up @@ -161,4 +161,34 @@ public StandardOutputOption() : base(
AllowMultipleArgumentsPerToken = false;
}
}

public class FromOption : CliOption<string>
{
public FromOption() : base(
name: CliStrings.FromFlag)
{
Description = "The format of the input schema.";
AllowMultipleArgumentsPerToken = false;
}
}

public class ToOption : CliOption<string>
{
public ToOption() : base(
name: CliStrings.ToFlag)
{
Description = "The format of the output schema.";
AllowMultipleArgumentsPerToken = false;
}
}

public class DryRunOption : CliOption<bool>
{
public DryRunOption() : base(
name: CliStrings.DryRunFlag)
{
Description = "Do not write any files to disk.";
AllowMultipleArgumentsPerToken = false;
}
}
}
8 changes: 7 additions & 1 deletion Compiler/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@
new NoWarnOption(),
new PreserveWatchOutputOption(),
},
new ConvertCommand()
{
new FromOption(),
new ToOption(),
new DryRunOption(),
},
#endif
};

Expand All @@ -60,7 +66,7 @@

var results = rootCommand.Parse(args);



results.Configuration.EnableDefaultExceptionHandler = false;
results.Configuration.ProcessTerminationTimeout = null;
Expand Down
24 changes: 12 additions & 12 deletions Laboratory/Schemas/bebop.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"inputDirectory": "./Valid",
"exclude": ["./ShouldFail"],
"generators": {
"cs": {
"outFile": "csoutput/models.cs"
},
"ts": {
"outFile": "tsoutput/models.ts"
},
"py": {
"outFile": "pyoutput/models.py"
}
"include": ["./Valid/*.bop"],
"exclude": ["./ShouldFail"],
"generators": {
"cs": {
"outFile": "csoutput/models.cs"
},
"ts": {
"outFile": "tsoutput/models.ts"
},
"py": {
"outFile": "pyoutput/models.py"
}
}
}
Loading