Skip to content

Commit 6d65b9a

Browse files
committed
Add LTokenEqualityComparer class.
Get LTokenParseTest passing. Add docs.
1 parent 587b5a1 commit 6d65b9a

File tree

6 files changed

+164
-12
lines changed

6 files changed

+164
-12
lines changed

Luaon.NET/Linq/LField.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,27 @@ public override void WriteTo(LuaTableTextWriter writer)
104104
Value.WriteTo(writer);
105105
}
106106

107+
/// <inheritdoc />
108+
internal override int GetDeepHashCode()
109+
{
110+
int hash = Value.GetDeepHashCode();
111+
if (Name != null) hash ^= Name.GetHashCode();
112+
return hash;
113+
}
114+
115+
/// <inheritdoc />
116+
internal override bool DeepEquals(LToken other)
117+
{
118+
var y = other as LField;
119+
if (y == null) return false;
120+
return DeepEquals(Name, y.Name) && DeepEquals(_Value, y._Value);
121+
}
122+
107123
/// <inheritdoc />
108124
public override LToken DeepClone()
109125
{
110126
return new LField(Name, Value);
111127
}
128+
112129
}
113130
}

Luaon.NET/Linq/LTable.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,33 @@ public override void WriteTo(LuaTableTextWriter writer)
277277
writer.WriteEndTable();
278278
}
279279

280+
/// <inheritdoc />
281+
internal override int GetDeepHashCode()
282+
{
283+
int hash = 0;
284+
foreach (var field in store)
285+
{
286+
hash = unchecked(hash * 13 + field.GetDeepHashCode());
287+
}
288+
return hash;
289+
}
290+
291+
/// <inheritdoc />
292+
internal override bool DeepEquals(LToken other)
293+
{
294+
var y = other as LTable;
295+
if (y == null) return false;
296+
if (ReferenceEquals(this, y)) return true;
297+
if (store.Count != y.store.Count) return false;
298+
var count = store.Count;
299+
for (int i = 0; i < count; i++)
300+
{
301+
if (!DeepEquals(store[i], y.store[i]))
302+
return false;
303+
}
304+
return true;
305+
}
306+
280307
/// <inheritdoc />
281308
public override LToken DeepClone()
282309
{

Luaon.NET/Linq/LToken.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,19 @@
55

66
namespace Luaon.Linq
77
{
8+
/// <summary>
9+
/// Represents a token in the Lua table expression.
10+
/// </summary>
811
public abstract class LToken
912
{
1013

14+
/// <summary>
15+
/// Loads next <see cref="LToken"/> from the specified <see cref="LuaTableTextReader"/>.
16+
/// </summary>
17+
/// <param name="reader">The reader from which to load the next <see cref="LToken"/>.</param>
18+
/// <exception cref="ArgumentNullException"><paramref name="reader"/> is <c>null</c>.</exception>
19+
/// <exception cref="LuaTableReaderException">There are unexpected input in the <paramref name="reader"/>.</exception>
20+
/// <returns>The loaded Lua token.</returns>
1121
public static LToken Load(LuaTableTextReader reader)
1222
{
1323
if (reader == null) throw new ArgumentNullException(nameof(reader));
@@ -28,8 +38,16 @@ public static LToken Load(LuaTableTextReader reader)
2838
}
2939
}
3040

41+
/// <summary>
42+
/// Parses the <see cref="LToken"/> from the specified Lua expression.
43+
/// </summary>
44+
/// <param name="expression">The Lua expression to be parsed.</param>
45+
/// <exception cref="ArgumentNullException"><paramref name="expression"/> is <c>null</c>.</exception>
46+
/// <exception cref="LuaTableReaderException"><paramref name="expression"/> is empty or invalid; or it contains extra content after the first parsed token.</exception>
47+
/// <returns>The loaded Lua token.</returns>
3148
public static LToken Parse(string expression)
3249
{
50+
if (expression == null) throw new ArgumentNullException(nameof(expression));
3351
using (var reader = new StringReader(expression))
3452
using (var lreader = new LuaTableTextReader(reader))
3553
{
@@ -195,6 +213,18 @@ public static implicit operator LToken(string value)
195213

196214
#endregion
197215

216+
internal abstract int GetDeepHashCode();
217+
218+
internal abstract bool DeepEquals(LToken other);
219+
220+
public static bool DeepEquals(LToken x, LToken y)
221+
{
222+
if (x == null || y == null) return x == null && y == null;
223+
return x.DeepEquals(y);
224+
}
225+
226+
#region Accessors
227+
198228
/// <summary>
199229
/// Gets/sets the child token associated with the specified key.
200230
/// </summary>
@@ -228,6 +258,8 @@ public virtual LToken this[LValue name]
228258
set => throw new NotSupportedException($"Cannot access child token of {GetType()}.");
229259
}
230260

261+
#endregion
262+
231263
}
232264

233265
public enum LTokenType
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace Luaon.Linq
6+
{
7+
/// <summary>
8+
/// Contains functionality for comparing the deep equality of <see cref="LToken"/> instances.
9+
/// </summary>
10+
public class LTokenEqualityComparer : IEqualityComparer<LToken>
11+
{
12+
13+
/// <summary>
14+
/// Gets an instance of <see cref="LTokenEqualityComparer"/>.
15+
/// </summary>
16+
public static LTokenEqualityComparer Instance { get; } = new LTokenEqualityComparer();
17+
18+
/// <inheritdoc />
19+
public bool Equals(LToken x, LToken y)
20+
{
21+
return LToken.DeepEquals(x, y);
22+
}
23+
24+
/// <inheritdoc />
25+
public int GetHashCode(LToken obj)
26+
{
27+
if (ReferenceEquals(obj, null)) return 0;
28+
return obj.GetDeepHashCode();
29+
}
30+
31+
}
32+
}

Luaon.NET/Linq/LValue.cs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,15 @@ public class LValue : LToken, IEquatable<LValue>
3636
reader.Read();
3737
SkipComments(reader);
3838
AssertReaderToken(reader, LuaTableReaderToken.Value);
39-
var v = new LValue(reader.CurrentValue);
39+
var rawValue = reader.CurrentValue;
4040
reader.Read();
41-
return v;
41+
if (rawValue == null)
42+
return Nil;
43+
if (rawValue is bool b)
44+
return b ? True : False;
45+
if (rawValue is int i)
46+
return i;
47+
return new LValue(rawValue);
4248
}
4349

4450
public LValue(LValue other)
@@ -271,6 +277,20 @@ public override void WriteTo(LuaTableTextWriter writer)
271277
writer.WriteLiteral(Value);
272278
}
273279

280+
/// <inheritdoc />
281+
internal override int GetDeepHashCode()
282+
{
283+
var hash = (int)TokenType;
284+
if (Value != null) hash = Value.GetHashCode();
285+
return hash;
286+
}
287+
288+
/// <inheritdoc />
289+
internal override bool DeepEquals(LToken other)
290+
{
291+
return Equals(other);
292+
}
293+
274294
/// <inheritdoc />
275295
public override LToken DeepClone()
276296
{
@@ -284,8 +304,22 @@ public bool Equals(LValue other)
284304
if (ReferenceEquals(this, other)) return true;
285305
if (TokenType != other.TokenType) return false;
286306
if (Equals(Value, other.Value)) return true;
287-
if (TokenType == LTokenType.Integer && Value.GetType() != other.Value.GetType())
288-
return Convert.ToDecimal(Value) == Convert.ToDecimal(other.Value);
307+
if (TokenType == LTokenType.Integer)
308+
{
309+
var t1 = Value.GetType();
310+
var t2 = other.Value.GetType();
311+
if (t1 != t2)
312+
{
313+
if (t1 == typeof(ulong) || t2 == typeof(ulong) || t1 == typeof(decimal) || t2 == typeof(decimal))
314+
{
315+
return Convert.ToDecimal(Value) == Convert.ToDecimal(other.Value);
316+
}
317+
else
318+
{
319+
return Convert.ToInt64(Value) == Convert.ToInt64(other.Value);
320+
}
321+
}
322+
}
289323
return false;
290324
}
291325

XUnitTestProject1/Tests/LinqTests.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,25 @@ public void LTableToStringTest()
6666
}", table.ToString());
6767
}
6868

69+
private void AssertTokenEquals(LToken expected, LToken actual)
70+
{
71+
Assert.Equal(expected, actual, LTokenEqualityComparer.Instance);
72+
}
73+
74+
private void AssertTokenEquals(LToken expected, string actual)
75+
{
76+
Assert.Equal(expected, LToken.Parse(actual), LTokenEqualityComparer.Instance);
77+
}
78+
6979
[Fact]
7080
public void LTokenParseTest()
7181
{
72-
Assert.Equal(LValue.True, LToken.Parse("true"));
73-
Assert.Equal(LValue.False, LToken.Parse("--content\nfalse\n--content"));
74-
Assert.Equal(LValue.Nil, LToken.Parse("nil"));
75-
Assert.Equal(new LTable { 10, 20, 30 }, LToken.Parse("{10, 20, 30}"));
76-
Assert.Equal(new LTable { 10, 20, 30, new LTable { 40, 50 } }, LToken.Parse("{10, 20, 30, {40, 50}}"));
77-
Assert.Equal(new LTable
82+
AssertTokenEquals(LValue.True, "true");
83+
AssertTokenEquals(LValue.False, "--content\nfalse\n--content");
84+
AssertTokenEquals(LValue.Nil, "nil");
85+
AssertTokenEquals(new LTable { 10, 20, 30 }, "{10, 20, 30}");
86+
AssertTokenEquals(new LTable { 10, 20, 30, new LTable { 40, 50 } }, "{10, 20, 30, {40, 50}}");
87+
AssertTokenEquals(new LTable
7888
{
7989
{"Test1", 1},
8090
2,
@@ -83,7 +93,7 @@ public void LTokenParseTest()
8393
{20, 5},
8494
{true, "6"},
8595
{"Child", new LTable(1, 2, 3, 4, 5)}
86-
}, LToken.Parse(@"{
96+
}, @"{
8797
Test1 = 1,
8898
2,
8999
Test2 = 3,
@@ -97,7 +107,7 @@ public void LTokenParseTest()
97107
4,
98108
5
99109
}
100-
}"));
110+
}");
101111
}
102112

103113
}

0 commit comments

Comments
 (0)