Skip to content

Commit

Permalink
Made dictionaries iterable and more generic.
Browse files Browse the repository at this point in the history
  • Loading branch information
Uralstech committed Nov 4, 2024
1 parent ac48361 commit dd9951f
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 55 deletions.
81 changes: 79 additions & 2 deletions src/Runtime/Collections/RuntimeEzrObjectDictionary.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using EzrSquared.Runtime.Types;
using EzrSquared.Runtime.Types.Collections;
using EzrSquared.Runtime.Types.Core.Errors;
using EzrSquared.Runtime.WrapperAttributes;
using System.Collections.Generic;

Expand Down Expand Up @@ -113,10 +115,47 @@ public bool RemoveHash(int key)
/// Merges a <see cref="RuntimeEzrObjectDictionary"/> to the current <see cref="RuntimeEzrObjectDictionary"/>.
/// </summary>
/// <param name="other">The other <see cref="RuntimeEzrObjectDictionary"/> to be merged.</param>
public void Merge(RuntimeEzrObjectDictionary other)
/// <param name="result">The <see cref="RuntimeResult"/> object for returning errors.</param>
public void Merge(RuntimeEzrObjectDictionary other, RuntimeResult result)
{
foreach (KeyValuePair<int, KeyValuePair<IEzrObject, Reference>> pair in other._items)
_items[pair.Key] = pair.Value;
{
IEzrObject keyObject = pair.Value.Key;
if (keyObject is IEzrMutableObject mutableElement)
{
IEzrObject? keyCopy = (IEzrObject?)mutableElement.DeepCopy(result);
if (result.ShouldReturn)
return;

keyObject = keyCopy!;
}

_items[pair.Key] = new KeyValuePair<IEzrObject, Reference>(keyObject, pair.Value.Value);
}
}

/// <summary>
/// Merges an <see cref="IEzrDictionary"/> to the current <see cref="RuntimeEzrObjectDictionary"/>.
/// </summary>
/// <param name="other">The other <see cref="IEzrDictionary"/> to be merged.</param>
/// <param name="executionContext">The context under which this operation is being executed.</param>
/// <param name="result">The <see cref="RuntimeResult"/> object for returning errors.</param>
public void Merge(IEzrDictionary other, Context executionContext, RuntimeResult result)
{
foreach (IEzrIndexedCollection pair in other)
{
if (pair.Count is > 2 or < 2)
{
result.Failure(new EzrUnexpectedTypeError($"Object of type \"{other.TypeName}\" is in an unexpected format and cannot be merged into this dictionary!", executionContext, other.StartPosition, other.EndPosition));
return;
}

(IEzrObject keyObject, IEzrObject valueObject) = (pair.At(0), pair.At(1));

Update(keyObject, valueObject, result);
if (result.ShouldReturn)
return;
}
}

/// <summary>
Expand Down Expand Up @@ -172,6 +211,44 @@ public bool IsEqual(RuntimeEzrObjectDictionary other, RuntimeResult result)
return true;
}

/// <summary>
/// Checks if the current dictionary is equal to the given keyed collection.
/// </summary>
/// <param name="other">The other keyed collection.</param>
/// <param name="executionContext">The context under which this operation is being executed.</param>
/// <param name="result">The <see cref="RuntimeResult"/> object for returning errors.</param>
/// <returns>The comparison result.</returns>
public bool IsEqual(IEzrDictionary other, Context executionContext, RuntimeResult result)
{
if (other.Count != _items.Count)
return false;

foreach (IEzrIndexedCollection pair in other)
{
if (pair.Count is > 2 or < 2)
{
result.Failure(new EzrUnexpectedTypeError($"Object of type \"{other.TypeName}\" is in an unexpected format and cannot be compared with this dictionary!", executionContext, other.StartPosition, other.EndPosition));
return false;
}

(IEzrObject keyObject, IEzrObject valueObject) = (pair.At(0), pair.At(1));

int keyHashCode = keyObject.ComputeHashCode(result);
if (result.ShouldReturn || !_items.TryGetValue(keyHashCode, out KeyValuePair<IEzrObject, Reference> thisPair))
return false;

bool keyEquals = keyObject.StrictEquals(thisPair.Key, result);
if (result.ShouldReturn || !keyEquals)
return false;

bool valueEquals = valueObject.StrictEquals(thisPair.Value.Object, result);
if (result.ShouldReturn || !valueEquals)
return false;
}

return true;
}

/// <summary>
/// Retrieves all keys from the <see cref="RuntimeEzrObjectDictionary"/>.
/// </summary>
Expand Down
6 changes: 3 additions & 3 deletions src/Runtime/Interpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ private void AssignValuesToVariables(VariableAssignmentNode node, Reference[] va

switch (value)
{
case IEzrIndexedCollection otherCollection when otherCollection.Length != variables.Length:
case IEzrIndexedCollection otherCollection when otherCollection.Count != variables.Length:
RuntimeResult.Failure(new EzrIllegalOperationError("Mismatched number of variables and values in assignment!", executionContext, node.StartPosition, node.EndPosition));
return;

Expand Down Expand Up @@ -1037,13 +1037,13 @@ private void VisitForEachNode(ForEachNode node, Context executionContext, Contex
return;

Reference iterableObjectReference = RuntimeResult.Reference;
if (iterableObjectReference.Object is not IEzrIndexedCollection iterableObject)
if (iterableObjectReference.Object is not IReadOnlyCollection<IEzrObject> iterableObject)
{
RuntimeResult.Failure(new EzrUnexpectedTypeError($"Expected an iterable object to iterate, but got object of type \"{iterableObjectReference.Object.TypeName}\"!", executionContext, node.Expression.Right.StartPosition, node.Expression.Right.EndPosition));
return;
}

List<IEzrObject> returns = new(iterableObject.Length);
List<IEzrObject> returns = new(iterableObject.Count);
foreach (IEzrObject ezrObject in iterableObject)
{
ezrObject.Update(executionContext, node.Expression.StartPosition, node.Expression.EndPosition);
Expand Down
20 changes: 10 additions & 10 deletions src/Runtime/Types/Collections/EzrArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class EzrArray : EzrObject, IEzrIndexedCollection
public readonly IEzrObject[] Value;

/// <inheritdoc/>
public int Length { get; }
public int Count { get; }

/// <summary>
/// Creates a new <see cref="EzrArray"/>.
Expand All @@ -38,9 +38,9 @@ public class EzrArray : EzrObject, IEzrIndexedCollection
public EzrArray(IEzrObject[] elements, Context parentContext, Position startPosition, Position endPosition) : base(parentContext, startPosition, endPosition)
{
Value = elements;
Length = elements.Length;
Count = elements.Length;

Context.Set(null, "length", ReferencePool.Get(new EzrSharpCompatibilityProperty(GetMemberInfo<PropertyInfo, EzrArray>(nameof(Length))!, this, Context, StartPosition, EndPosition), AccessMod.Constant));
Context.Set(null, "length", ReferencePool.Get(new EzrSharpCompatibilityProperty(GetMemberInfo<PropertyInfo, EzrArray>(nameof(Count))!, this, Context, StartPosition, EndPosition), AccessMod.Constant));
}

/// <summary>
Expand All @@ -51,7 +51,7 @@ public EzrArray(IEzrObject[] elements, Context parentContext, Position startPosi
/// <returns>The result of the comparison.</returns>
private bool Compare(IEzrIndexedCollection other, RuntimeResult result)
{
if (Value.Length != other.Length)
if (Value.Length != other.Count)
return false;

for (int i = 0; i < Value.Length; i++)
Expand Down Expand Up @@ -154,10 +154,10 @@ public override void ComparisonLessThan(IEzrObject other, RuntimeResult result)
case EzrInteger:
result.Failure(new EzrValueOutOfRangeError("The value is too large for this operation!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection { Length: < 2 }:
case IEzrIndexedCollection { Count: < 2 }:
result.Failure(new EzrIllegalOperationError($"The indices {other.TypeName} must contain two values, the starting index and the ending index!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection { Length: > 2 }:
case IEzrIndexedCollection { Count: > 2 }:
result.Failure(new EzrIllegalOperationError($"The indices {other.TypeName} must only contain two values, the starting index and the ending index!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection otherCollection:
Expand Down Expand Up @@ -231,10 +231,10 @@ public override void Addition(IEzrObject other, RuntimeResult result)
switch (other)
{
case EzrArray otherArray:
newArray = new IEzrObject[Value.Length + otherArray.Length];
newArray = new IEzrObject[Value.Length + otherArray.Count];

Array.Copy(Value, newArray, Value.Length);
Array.Copy(otherArray.Value, 0, newArray, Value.Length, otherArray.Length);
Array.Copy(otherArray.Value, 0, newArray, Value.Length, otherArray.Count);

break;

Expand Down Expand Up @@ -279,10 +279,10 @@ public override void Subtraction(IEzrObject other, RuntimeResult result)
case EzrInteger:
result.Failure(new EzrValueOutOfRangeError("The value is too large for this operation!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection { Length: < 2 }:
case IEzrIndexedCollection { Count: < 2 }:
result.Failure(new EzrIllegalOperationError($"The indices {other.TypeName} must contain two values, the starting index and the ending index!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection { Length: > 2 }:
case IEzrIndexedCollection { Count: > 2 }:
result.Failure(new EzrIllegalOperationError($"The indices {other.TypeName} must only contain two values, the starting index and the ending index!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection otherCollection:
Expand Down
79 changes: 68 additions & 11 deletions src/Runtime/Types/Collections/EzrDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using EzrSquared.Runtime.Types.CSharpWrappers.SourceWrappers;
using EzrSquared.Runtime.WrapperAttributes;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

Expand All @@ -15,7 +16,7 @@ namespace EzrSquared.Runtime.Types.Collections;
/// <summary>
/// The mutable dictionary type object.
/// </summary>
public class EzrDictionary : EzrObject, IEzrMutableObject
public class EzrDictionary : EzrObject, IEzrMutableObject, IEzrDictionary
{
/// <inheritdoc/>
public override string TypeName { get; protected internal set; } = "dictionary";
Expand All @@ -28,6 +29,9 @@ public class EzrDictionary : EzrObject, IEzrMutableObject
/// </summary>
public readonly RuntimeEzrObjectDictionary Value;

/// <inheritdoc/>
public int Count => Value.Length;

/// <summary>
/// Creates a new <see cref="EzrDictionary"/>.
/// </summary>
Expand All @@ -44,6 +48,36 @@ public EzrDictionary(RuntimeEzrObjectDictionary value, Context parentContext, Po
Context.Set(null, "has_key", ReferencePool.Get(new EzrSharpSourceFunctionWrapper(DictionaryExists, Context, StartPosition, EndPosition), AccessMod.Constant));
}

/// <inheritdoc/>
/// <exception cref="KeyNotFoundException">Thrown if the key could not be hashed or was not found.</exception>
public IEzrObject At(IEzrObject key, RuntimeResult result)
{
Reference value = Value.Get(key, result);
return !value.IsEmpty
? value.Object
: throw new KeyNotFoundException("The key could not be hashed or was not found in the dictionary!");
}

/// <inheritdoc/>
public IEzrObject? TryAt(IEzrObject key, RuntimeResult result)
{
Reference value = Value.Get(key, result);
return !value.IsEmpty ? value.Object : null;
}

/// <inheritdoc/>
public IEnumerator<IEzrIndexedCollection> GetEnumerator()
{
EzrArray[] pairs = Array.ConvertAll(Value.GetPairs(), pair => new EzrArray([pair.Key, pair.Value], _executionContext, StartPosition, EndPosition));
return ((IEnumerable<EzrArray>)pairs).GetEnumerator();
}

/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

/// <summary>
/// Basic key checking function. Implements <see cref="RuntimeEzrObjectDictionary.HasKey(IEzrObject, RuntimeResult)"/>.
/// </summary>
Expand Down Expand Up @@ -78,11 +112,18 @@ public override void ComparisonEqual(IEzrObject other, RuntimeResult result)
switch (other)
{
case EzrDictionary otherDictionary:
bool equal = Value.IsEqual(otherDictionary.Value, result);
bool equalDictionaries = Value.IsEqual(otherDictionary.Value, result);
if (result.ShouldReturn)
return;

result.Success(NewBooleanConstant(equal)); break;
result.Success(NewBooleanConstant(equalDictionaries)); break;

case IEzrDictionary otherIDictionary:
bool equalIDictionaries = Value.IsEqual(otherIDictionary, _executionContext, result);
if (result.ShouldReturn)
break;

result.Success(NewBooleanConstant(equalIDictionaries)); break;

default:
result.Success(NewBooleanConstant(false)); break;
Expand All @@ -95,11 +136,19 @@ public override void ComparisonNotEqual(IEzrObject other, RuntimeResult result)
switch (other)
{
case EzrDictionary otherDictionary:
bool equal = Value.IsEqual(otherDictionary.Value, result);
bool equalDictionaries = Value.IsEqual(otherDictionary.Value, result);
if (result.ShouldReturn)
return;

result.Success(NewBooleanConstant(!equal)); break;
result.Success(NewBooleanConstant(!equalDictionaries)); break;


case IEzrDictionary otherIDictionary:
bool equalIDictionaries = Value.IsEqual(otherIDictionary, _executionContext, result);
if (result.ShouldReturn)
break;

result.Success(NewBooleanConstant(!equalIDictionaries)); break;

default:
result.Success(NewBooleanConstant(true)); break;
Expand Down Expand Up @@ -136,24 +185,32 @@ public override void Addition(IEzrObject other, RuntimeResult result)
{
switch (other)
{
case IEzrIndexedCollection { Length: < 2 }:
case IEzrIndexedCollection { Count: < 2 }:
result.Failure(new EzrIllegalOperationError($"The {other.TypeName} must contain two values, the key and value!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection { Length: > 2 }:
case IEzrIndexedCollection { Count: > 2 }:
result.Failure(new EzrIllegalOperationError($"The {other.TypeName} must only contain two values, the key and value!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection otherCollection:
Value.Update(otherCollection.At(0), otherCollection.At(1), result);
if (result.ShouldReturn)
return;
break;

result.Success(NewNothingConstant()); break;

case EzrDictionary otherDictionary:
Value.Merge(otherDictionary.Value);
result.Success(NewNothingConstant());
Value.Merge(otherDictionary.Value, result);
if (result.ShouldReturn)
break;

break;
result.Success(NewNothingConstant()); break;

case IEzrDictionary otherIDictionary:
Value.Merge(otherIDictionary, _executionContext, result);
if (result.ShouldReturn)
break;

result.Success(NewNothingConstant()); break;

default:
result.Failure(IllegalOperation(other)); break;
Expand Down
14 changes: 7 additions & 7 deletions src/Runtime/Types/Collections/EzrList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class EzrList : EzrObject, IEzrMutableObject, IEzrIndexedCollection

/// <inheritdoc/>
[SharpAutoWrapper(isReadOnly: true)]
public int Length => Value.Count;
public int Count => Value.Count;

/// <param name="elements">The base value.</param>
/// <param name="parentContext">The parent context.</param>
Expand All @@ -39,7 +39,7 @@ public EzrList(RuntimeEzrObjectList elements, Context parentContext, Position st
{
Value = elements;

Context.Set(null, "length", ReferencePool.Get(new EzrSharpCompatibilityProperty(GetMemberInfo<PropertyInfo, EzrList>(nameof(Length))!, this, Context, StartPosition, EndPosition), AccessMod.Constant));
Context.Set(null, "length", ReferencePool.Get(new EzrSharpCompatibilityProperty(GetMemberInfo<PropertyInfo, EzrList>(nameof(Count))!, this, Context, StartPosition, EndPosition), AccessMod.Constant));
}

/// <summary>
Expand All @@ -50,7 +50,7 @@ public EzrList(RuntimeEzrObjectList elements, Context parentContext, Position st
/// <returns>The result of the comparison.</returns>
private bool Compare(IEzrIndexedCollection other, RuntimeResult result)
{
if (Value.Count != other.Length)
if (Value.Count != other.Count)
return false;

for (int i = 0; i < Value.Count; i++)
Expand Down Expand Up @@ -153,10 +153,10 @@ public override void ComparisonLessThan(IEzrObject other, RuntimeResult result)
case EzrInteger:
result.Failure(new EzrValueOutOfRangeError("The value is too large for this operation!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection { Length: < 2 }:
case IEzrIndexedCollection { Count: < 2 }:
result.Failure(new EzrIllegalOperationError($"The indices {other.TypeName} must contain two values, the starting index and the ending index!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection { Length: > 2 }:
case IEzrIndexedCollection { Count: > 2 }:
result.Failure(new EzrIllegalOperationError($"The indices {other.TypeName} must only contain two values, the starting index and the ending index!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection otherCollection:
Expand Down Expand Up @@ -266,10 +266,10 @@ public override void Subtraction(IEzrObject other, RuntimeResult result)
case EzrInteger:
result.Failure(new EzrValueOutOfRangeError("The value is too large for this operation!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection { Length: < 2 }:
case IEzrIndexedCollection { Count: < 2 }:
result.Failure(new EzrIllegalOperationError($"The indices {other.TypeName} must contain two values, the starting index and the ending index!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection { Length: > 2 }:
case IEzrIndexedCollection { Count: > 2 }:
result.Failure(new EzrIllegalOperationError($"The indices {other.TypeName} must only contain two values, the starting index and the ending index!", _executionContext, other.StartPosition, other.EndPosition)); break;

case IEzrIndexedCollection otherCollection:
Expand Down
Loading

0 comments on commit dd9951f

Please sign in to comment.