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

Trimmed Binary & Output Properties #8

Merged
merged 2 commits into from
Nov 28, 2023
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
20 changes: 14 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ jobs:
chmod +x dotnetastgen-linux-arm
chmod +x dotnetastgen-macos
chmod +x dotnetastgen-macos-arm
- name: Compress all executables
run: |
zip dotnetastgen-linux.zip dotnetastgen-linux
zip dotnetastgen-linux-arm.zip dotnetastgen-linux-arm
zip dotnetastgen-macos.zip dotnetastgen-macos
zip dotnetastgen-macos-arm.zip dotnetastgen-macos-arm
zip dotnetastgen-win.zip dotnetastgen-win.exe
zip dotnetastgen-win-arm.zip dotnetastgen-win-arm.exe
- name: Set next release version
id: taggerFinal
uses: anothrNick/[email protected]
Expand All @@ -69,9 +77,9 @@ jobs:
with:
tag_name: ${{ steps.taggerDryRun.outputs.new_tag }}
files: |
dotnetastgen-linux
dotnetastgen-linux-arm
dotnetastgen-macos
dotnetastgen-macos-arm
dotnetastgen-win.exe
dotnetastgen-win-arm.exe
dotnetastgen-linux.zip
dotnetastgen-linux-arm.zip
dotnetastgen-macos.zip
dotnetastgen-macos-arm.zip
dotnetastgen-win.exe.zip
dotnetastgen-win-arm.exe.zip
4 changes: 4 additions & 0 deletions DotNetAstGen/DotNetAstGen.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

<ItemGroup>
<Folder Include="release\" />
</ItemGroup>

</Project>
69 changes: 45 additions & 24 deletions DotNetAstGen/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Diagnostics;
using System.Text;
using CommandLine;
using DotNetAstGen.Utils;
Expand Down Expand Up @@ -34,62 +34,83 @@ public static void Main(string[] args)
_logger = LoggerFactory.CreateLogger<Program>();
_logger.LogDebug("Show verbose output.");

_RunAstGet(options.InputFilePath);
_RunAstGet(options.InputFilePath, new DirectoryInfo(options.OutputDirectory));
});
}

private static void _RunAstGet(string inputPath)
private static void _RunAstGet(string inputPath, DirectoryInfo rootOutputPath)
{
if (!rootOutputPath.Exists)
{
rootOutputPath.Create();
}

if (Directory.Exists(inputPath))
{
_logger?.LogInformation("Parsing directory {dirName}", inputPath);
foreach (FileInfo fileInfo in new DirectoryInfo(inputPath).EnumerateFiles("*.cs"))
var rootDirectory = new DirectoryInfo(inputPath);
foreach (var inputFile in new DirectoryInfo(inputPath).EnumerateFiles("*.cs"))
{
_AstForFile(fileInfo);
_AstForFile(rootDirectory, rootOutputPath, inputFile);
}
}
else if (File.Exists(inputPath))
{
_logger?.LogInformation("Parsing file {fileName}", inputPath);
_AstForFile(new FileInfo(inputPath));
var file = new FileInfo(inputPath);
Debug.Assert(file.Directory != null, "Given file has a null parent directory!");
_AstForFile(file.Directory, rootOutputPath, file);
}
else
{
_logger?.LogError("The path {inputPath} does not exist!", inputPath);
Environment.Exit(1);
}
_logger?.LogInformation("Parsing successful!");

_logger?.LogInformation("AST generation complete");
}

private static void _AstForFile(FileInfo filePath)
private static void _AstForFile(FileSystemInfo rootInputPath, FileSystemInfo rootOutputPath, FileInfo filePath)
{
var fullPath = filePath.FullName;
_logger?.LogDebug("Parsing file: {filePath}", fullPath);
using var streamReader = new StreamReader(fullPath, Encoding.UTF8);
var programText = streamReader.ReadToEnd();
var tree = CSharpSyntaxTree.ParseText(programText);
_logger?.LogDebug("Successfully parsed: {filePath}", fullPath);
var root = tree.GetCompilationUnitRoot();
var rootType = root.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(rootType.GetProperties());
var jsonString = JsonConvert.SerializeObject(root, Formatting.Indented, new JsonSerializerSettings
try
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new IgnorePropertiesResolver() // Comment this to see the unfiltered parser output
});
var outputName = Path.Combine(filePath.DirectoryName ?? "./", $"{Path.GetFileNameWithoutExtension(fullPath)}.json");
_logger?.LogDebug("Writing AST to {astJsonPath}", outputName);
File.WriteAllText(outputName, jsonString);
using var streamReader = new StreamReader(fullPath, Encoding.UTF8);
var programText = streamReader.ReadToEnd();
var tree = CSharpSyntaxTree.ParseText(programText);
_logger?.LogDebug("Successfully parsed: {filePath}", fullPath);
var root = tree.GetCompilationUnitRoot();
var jsonString = JsonConvert.SerializeObject(root, Formatting.Indented, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver =
new IgnorePropertiesResolver() // Comment this to see the unfiltered parser output
});
var outputName = Path.Combine(filePath.DirectoryName ?? "./",
$"{Path.GetFileNameWithoutExtension(fullPath)}.json")
.Replace(rootInputPath.FullName, rootOutputPath.FullName);

File.WriteAllText(outputName, jsonString);
_logger?.LogInformation("Successfully wrote AST to '{astJsonPath}'", outputName);
}
catch (Exception e)
{
_logger?.LogError("Error encountered while parsing '{filePath}': {errorMsg}", fullPath, e.Message);
}
}
}


internal class Options
{
[Option('v', "verbose", Required = false, HelpText = "Enable verbose output.")]
public bool Verbose { get; set; }
public bool Verbose { get; set; } = false;

[Option('i', "input", Required = true, HelpText = "Input file or directory.")]
public string InputFilePath { get; set; }
public string InputFilePath { get; set; } = "";

[Option('o', "input", Required = false, HelpText = "Output directory. (default `./.ast`)")]
public string OutputDirectory { get; set; } = ".ast";
}
}
50 changes: 35 additions & 15 deletions DotNetAstGen/Utils/JsonResolver.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,54 @@
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace DotNetAstGen.Utils
{
class IgnorePropertiesResolver : DefaultContractResolver
internal class IgnorePropertiesResolver : DefaultContractResolver
{
private readonly string[] _propsToIgnore =
private static readonly ILogger? Logger = Program.LoggerFactory?.CreateLogger("IgnorePropertiesResolver");


private readonly HashSet<string> _propsToAllow = new(new[]
{
"Parent", "ParentTrivia", "ContainsSkippedText", "SyntaxTree", "SpanStart", "IsMissing",
"IsStructuredTrivia", "HasStructuredTrivia", "ContainsDiagnostics", "ContainsDirectives",
"HasLeadingTrivia", "HasTrailingTrivia", "ContainsAnnotations", "Span", "FullSpan", "LeadingTrivia",
"TrailingTrivia"
};
"Value", "Externs", "Usings", "Name", "Identifier", "Left", "Right", "Members", "ConstraintClauses",
"Alias", "NamespaceOrType", "Arguments", "Expression", "Declaration", "ElementType", "Initializer", "Else",
"Condition", "Statement", "Statements", "Variables", "WhenNotNull", "AllowsAnyExpression", "Expressions",
"Modifiers", "ReturnType", "IsUnboundGenericName", "Default", "IsConst", "Parameters", "Types",
"ExplicitInterfaceSpecifier", "Text", "Length", "Location"
});

private readonly HashSet<string> _ignoreProps;
private readonly List<string> _regexToAllow = new(new[]
{
".*Token$", ".*Keyword$", ".*Lists?$", ".*Body$", "(Span)?Start"
});

private readonly List<string> _regexToIgnore = new(new[]
{
".*(Semicolon|Brace|Bracket|EndOfFile|Paren|Dot)Token$"
});

public IgnorePropertiesResolver()
private bool MatchesAllow(string input)
{
_ignoreProps = new HashSet<string>(this._propsToIgnore);
return _regexToAllow.Any(regex => Regex.IsMatch(input, regex));
}

private bool MatchesIgnore(string input)
{
return _regexToIgnore.Any(regex => Regex.IsMatch(input, regex));
}

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (_ignoreProps.Contains(property.PropertyName ?? ""))
{
property.ShouldSerialize = _ => false;
}

var propertyName = property.PropertyName ?? "";
var shouldSerialize = propertyName != "" &&
(_propsToAllow.Contains(propertyName) || MatchesAllow(propertyName)) &&
!MatchesIgnore(propertyName);
Logger?.LogDebug(shouldSerialize ? $"Allowing {propertyName}" : $"Ignoring {propertyName}");
property.ShouldSerialize = _ => shouldSerialize;
return property;
}
}
Expand Down
9 changes: 3 additions & 6 deletions DotNetAstGen/Utils/MapSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Newtonsoft.Json.Linq;
using System.Data;
using System.Runtime.InteropServices;
using DotNetAstGen.Utils;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;

namespace DotNetAstGen.Utils
{
public class MapSerializer

{
private static ILogger? _logger = Program.LoggerFactory?.CreateLogger("MapSerializer");
private static readonly ILogger? Logger = Program.LoggerFactory?.CreateLogger("MapSerializer");

public static JArray ProcessSyntaxTokenList(SyntaxTokenList list)
{
JArray syntaxTokenList = new();
Expand Down Expand Up @@ -50,7 +47,7 @@

if (key?.GetValue(node)?.GetType() == typeof(SyntaxToken))
{
Console.WriteLine(ProcessSyntaxToken((SyntaxToken)key.GetValue(node)));

Check warning on line 50 in DotNetAstGen/Utils/MapSerializer.cs

View workflow job for this annotation

GitHub Actions / build

Unboxing a possibly null value.

Check warning on line 50 in DotNetAstGen/Utils/MapSerializer.cs

View workflow job for this annotation

GitHub Actions / build

Unboxing a possibly null value.
// Console.WriteLine(key?.GetValue(node)?.GetType());
}
else if (key?.GetValue(node)?.GetType() == typeof(SyntaxNode))
Expand All @@ -61,7 +58,7 @@

if (genericType?.IsSubclassOf(typeof(SyntaxNode)) ?? false)
{
ProcessSyntaxNodeList(value);

Check warning on line 61 in DotNetAstGen/Utils/MapSerializer.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'list' in 'void MapSerializer.ProcessSyntaxNodeList(dynamic list)'.

Check warning on line 61 in DotNetAstGen/Utils/MapSerializer.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'list' in 'void MapSerializer.ProcessSyntaxNodeList(dynamic list)'.
}
}
}
Expand Down
Loading