Skip to content

Commit

Permalink
Refactor ast api (#47)
Browse files Browse the repository at this point in the history
* Add builder for `AstMessage`/`AstTerm`

* Make more fields/properties `readonly`

* Bump version 0.8.0

* Added serialization for everything.

* Fix equality of everything
  • Loading branch information
Ygg01 authored Jan 22, 2024
1 parent a52b14f commit dfa1d1b
Show file tree
Hide file tree
Showing 40 changed files with 1,887 additions and 222 deletions.
1 change: 1 addition & 0 deletions Linguini.Bench/Linguini.Bench.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<PackageVersion>0.8.0</PackageVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Linguini.Bundle.Test/Linguini.Bundle.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<PackageVersion>0.7.0</PackageVersion>
<PackageVersion>0.8.0</PackageVersion>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
</PropertyGroup>

Expand Down
13 changes: 6 additions & 7 deletions Linguini.Bundle/Errors/FluentError.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using Linguini.Shared.Util;
using Linguini.Syntax.Ast;
using Linguini.Syntax.Parser.Error;

Expand Down Expand Up @@ -51,23 +50,23 @@ public override string ToString()

public record ResolverFluentError : FluentError
{
private string Description;
private ErrorType Kind;
private readonly string _description;
private readonly ErrorType _kind;

private ResolverFluentError(string desc, ErrorType kind)
{
Description = desc;
Kind = kind;
_description = desc;
_kind = kind;
}

public override ErrorType ErrorKind()
{
return Kind;
return _kind;
}

public override string ToString()
{
return Description;
return _description;
}

public static ResolverFluentError NoValue(ReadOnlyMemory<char> idName)
Expand Down
2 changes: 1 addition & 1 deletion Linguini.Bundle/Linguini.Bundle.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ It provides easy to use and extend system for describing translations.</Descript
<Win32Resource />
<PackageProjectUrl>https://github.com/Ygg01/Linguini</PackageProjectUrl>
<RepositoryType>git</RepositoryType>
<PackageVersion>0.7.0</PackageVersion>
<PackageVersion>0.8.0</PackageVersion>
<TargetFrameworks>net8.0;netstandard2.1;net6.0</TargetFrameworks>
<PackageIcon>linguini.jpg</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
Expand Down
63 changes: 20 additions & 43 deletions Linguini.Bundle/Resolver/WriterHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#nullable enable
using System;
using System;
using System.Collections.Generic;
using System.IO;
using Linguini.Bundle.Errors;
Expand Down Expand Up @@ -100,11 +99,9 @@ public static bool TryWrite(this IExpression expression, TextWriter writer, Scop
for (var i = 0; i < selectExpression.Variants.Count; i++)
{
var variant = selectExpression.Variants[i];
if (variant.IsDefault)
{
variant.Value.Write(writer, scope);
return errors.Count == 0;
}
if (!variant.IsDefault) continue;
variant.Value.Write(writer, scope);
return errors.Count == 0;
}

errors.Add(ResolverFluentError.MissingDefault());
Expand Down Expand Up @@ -312,46 +309,26 @@ public static void WriteError(this IExpression self, TextWriter writer)

public static void WriteError(this IInlineExpression self, TextWriter writer)
{
if (self is MessageReference msgRef)
switch (self)
{
if (msgRef.Attribute == null)
{
writer.Write($"{msgRef.Id}");
case MessageReference msgRef:
writer.Write(msgRef.Attribute == null ? $"{msgRef.Id}" : $"{msgRef.Id}.{msgRef.Attribute}");
return;
}

writer.Write($"{msgRef.Id}.{msgRef.Attribute}");
return;
}

if (self is TermReference termRef)
{
if (termRef.Attribute == null)
{
writer.Write($"-{termRef.Id}");
case TermReference termReference:
writer.Write(termReference.Attribute == null ? $"-{termReference.Id}" : $"-{termReference.Id}.{termReference.Attribute}");
return;
}

writer.Write($"-{termRef.Id}.{termRef.Attribute}");
}
else if (self is FunctionReference funcRef)
{
writer.Write($"{funcRef.Id}()");
return;
}
else if (self is VariableReference varRef)
{
writer.Write($"${varRef.Id}");
return;
}
else if (self is DynamicReference dynamicReference)
{
writer.Write($"$${dynamicReference.Id}");
return;
case FunctionReference funcRef:
writer.Write($"{funcRef.Id}()");
return;
case VariableReference varRef:
writer.Write($"${varRef.Id}");
return;
case DynamicReference dynamicReference:
writer.Write($"$${dynamicReference.Id}");
return;
default:
throw new ArgumentException($"Unexpected inline expression `{self.GetType()}`!");
}


throw new ArgumentException($"Unexpected inline expression `{self.GetType()}`!");
}
}
}
20 changes: 20 additions & 0 deletions Linguini.Serialization.Test/Linguini.Serialization.Test.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Linguini.Serialization\Linguini.Serialization.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="NUnit" Version="4.0.1" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
</ItemGroup>

</Project>
105 changes: 105 additions & 0 deletions Linguini.Serialization.Test/SerializeAndDeserializeTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System.Diagnostics;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using Linguini.Serialization.Converters;
using Linguini.Syntax.Ast;
using NUnit.Framework;
using Attribute = Linguini.Syntax.Ast.Attribute;


namespace Linguini.Serialization.Test;

[TestFixture]
public class SerializeAndDeserializeTest
{
[Test]
[TestCaseSource(nameof(AstExamples))]
[Parallelizable]
public void RoundTripTest(object x)
{
// Serialize the object to JSON string.
var jsonString = JsonSerializer.Serialize(x, Options);

// Deserialize the JSON string back into an object.
Debug.Assert(x != null, nameof(x) + " != null");
var deserializedObject = JsonSerializer.Deserialize(jsonString, x.GetType(), Options);

// Now you have a 'deserializedObject' which should be equivalent to the original 'expected' object.
Assert.That(deserializedObject, Is.Not.Null);
Assert.That(deserializedObject, Is.EqualTo(x));
}

public static IEnumerable<object> AstExamples()
{
yield return new Attribute("desc", new PatternBuilder("description"));
yield return new CallArgumentsBuilder()
.AddPositionalArg(InlineExpressionBuilder.CreateMessageReference("x"))
.AddNamedArg("y", 3)
.Build();
yield return new AstComment(CommentLevel.Comment, new() { "test".AsMemory() });
yield return new DynamicReference("dyn", "attr", new CallArgumentsBuilder()
.AddPositionalArg(InlineExpressionBuilder.CreateMessageReference("x"))
.AddNamedArg("y", 3));
yield return new FunctionReference("foo", new CallArgumentsBuilder()
.AddPositionalArg(3)
.AddNamedArg("test", InlineExpressionBuilder.CreateTermReference("x", "y"))
.Build()
);
yield return new Identifier("test");
yield return new Junk("Test".AsMemory());
yield return new MessageReference("message", "attribute");
yield return new AstMessage(
new Identifier("x"),
new PatternBuilder(3).Build(),
new List<Attribute>()
{
new("attr1", new PatternBuilder("value1")),
new("attr2", new PatternBuilder("value2"))
},
new(CommentLevel.ResourceComment, new()
{
"test".AsMemory()
}));
yield return new PatternBuilder("text ").AddMessage("x").AddText(" more text").Build();
yield return new SelectExpressionBuilder(new VariableReference("x"))
.AddVariant("one", new PatternBuilder("select 1"))
.AddVariant("other", new PatternBuilder("select other"))
.SetDefault(1)
.Build();
yield return new TermReference("x", "y");
yield return new VariableReference("x");
yield return new Variant(2.0f, new PatternBuilder(3));
}

private static readonly JsonSerializerOptions Options = new()
{
IgnoreReadOnlyFields = false,
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters =
{
new AttributeSerializer(),
new CallArgumentsSerializer(),
new CommentSerializer(),
new FunctionReferenceSerializer(),
new IdentifierSerializer(),
new JunkSerializer(),
new MessageReferenceSerializer(),
new MessageSerializer(),
new DynamicReferenceSerializer(),
new NamedArgumentSerializer(),
new ParseErrorSerializer(),
new PatternSerializer(),
new PlaceableSerializer(),
new ResourceSerializer(),
new PlaceableSerializer(),
new SelectExpressionSerializer(),
new TermReferenceSerializer(),
new TermSerializer(),
new VariantSerializer(),
new VariableReferenceSerializer(),
}
};
}
16 changes: 16 additions & 0 deletions Linguini.Serialization.Test/TestUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using Linguini.Serialization.Converters;
using NUnit.Framework;

namespace Linguini.Serialization.Test
{
public static class TestUtil
{




}
}
48 changes: 47 additions & 1 deletion Linguini.Serialization/Converters/AttributeSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Linguini.Syntax.Ast;
using Attribute = Linguini.Syntax.Ast.Attribute;

namespace Linguini.Serialization.Converters
Expand All @@ -9,7 +10,52 @@ public class AttributeSerializer : JsonConverter<Attribute>
{
public override Attribute Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}

var id = new Identifier("");
var value = new Pattern();

while (reader.Read())
{

if (reader.TokenType == JsonTokenType.EndObject)
{
break;
}

if (reader.TokenType == JsonTokenType.PropertyName)
{
string? propertyName = reader.GetString();

reader.Read();

switch (propertyName)
{
case "id":
id = JsonSerializer.Deserialize<Identifier>(ref reader, options);
break;

case "value":
value = JsonSerializer.Deserialize<Pattern>(ref reader, options);
break;
case "type":
var typeField = reader.GetString();
if (typeField != "Attribute")
{
throw new JsonException(
$"Invalid type: Expected 'Attribute' found {typeField} instead");
}
break;
default:
throw new JsonException($"Unexpected property: {propertyName}");
}
}
}

return new Attribute(id!, value!);
}

public override void Write(Utf8JsonWriter writer, Attribute attribute, JsonSerializerOptions options)
Expand Down
Loading

0 comments on commit dfa1d1b

Please sign in to comment.