diff --git a/Sources/SynKit/Libraries/Parser/ParseError.cs b/Sources/SynKit/Libraries/Parser/ParseError.cs
index 78f6e0c0..5dcd99e0 100644
--- a/Sources/SynKit/Libraries/Parser/ParseError.cs
+++ b/Sources/SynKit/Libraries/Parser/ParseError.cs
@@ -4,8 +4,6 @@
using System;
using System.Collections.Generic;
-using System.Collections.Generic.Polyfill;
-using System.Linq;
namespace Yoakke.SynKit.Parser;
@@ -14,10 +12,12 @@ namespace Yoakke.SynKit.Parser;
///
public class ParseError
{
+ private ParseErrorElementDictionary elements;
+
///
/// The error cases in different parse contexts.
///
- public IReadOnlyDictionary Elements { get; }
+ public IReadOnlyDictionary Elements => this.elements;
///
/// The item that was found, if any.
@@ -37,13 +37,13 @@ public class ParseError
/// The position where the error occurred.
/// The context in which the error occurred.
public ParseError(object expected, object? got, IComparable position, string context)
- : this(new Dictionary { { context, new ParseErrorElement(expected, context) } }, got, position)
+ : this(new ParseErrorElementDictionary(context, new ParseErrorElement(expected, context)), got, position)
{
}
- private ParseError(IReadOnlyDictionary elements, object? got, IComparable position)
+ private ParseError(ParseErrorElementDictionary elements, object? got, IComparable position)
{
- this.Elements = elements;
+ this.elements = elements;
this.Got = got;
this.Position = position;
}
@@ -65,19 +65,7 @@ private ParseError(IReadOnlyDictionary elements, obje
if (cmp < 0) return second;
if (cmp > 0) return first;
// Both of them got stuck at the same place, merge entries
- var elements = first.Elements.Values.ToDictionary(e => e.Context, e => new ParseErrorElement(e.Expected.ToHashSet(), e.Context));
- foreach (var element in second.Elements.Values)
- {
- if (elements.TryGetValue(element.Context, out var part))
- {
- foreach (var e in element.Expected) part.Expected.Add(e);
- }
- else
- {
- part = new (element.Expected.ToHashSet(), element.Context);
- elements.Add(element.Context, part);
- }
- }
+ var elements = first.elements.Merge(second.elements);
// TODO: Think this through
// NOTE: Could it ever happen that first.Got and second.Got are different but neither are null?
// Would we want to unify these and move them to ParseErrorElement or something?
diff --git a/Sources/SynKit/Libraries/Parser/ParseErrorElementDictionary.cs b/Sources/SynKit/Libraries/Parser/ParseErrorElementDictionary.cs
new file mode 100644
index 00000000..c908badb
--- /dev/null
+++ b/Sources/SynKit/Libraries/Parser/ParseErrorElementDictionary.cs
@@ -0,0 +1,94 @@
+// Copyright (c) 2021-2022 Yoakke.
+// Licensed under the Apache License, Version 2.0.
+// Source repository: https://github.com/LanguageDev/Yoakke
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Generic.Polyfill;
+using System.Linq;
+
+namespace Yoakke.SynKit.Parser;
+
+internal class ParseErrorElementDictionary : IReadOnlyDictionary
+{
+ private string? firstKey;
+ private ParseErrorElement? firstItem;
+ private Dictionary? elements;
+
+ private ParseErrorElementDictionary()
+ {
+ }
+
+ private ParseErrorElementDictionary(Dictionary elements)
+ {
+ this.elements = elements;
+ }
+
+ public ParseErrorElementDictionary(string key, ParseErrorElement value)
+ {
+ this.firstKey = key;
+ this.firstItem = value;
+ }
+
+ public ParseErrorElement this[string key] => this.firstKey is null
+ ? this.elements is null
+ ? throw new KeyNotFoundException($"The key {key} was not found in the dictionary")
+ : this.elements[key]
+ : this.firstItem!;
+
+ public IEnumerable Keys => this.firstKey is null ? this.elements!.Keys : new[] { this.firstKey };
+
+ public IEnumerable Values => this.firstKey is null ? this.elements!.Values : new[] { this.firstItem! };
+
+ public int Count => this.firstKey is null ? this.elements is null ? 0 : this.elements.Count : 1;
+
+ public bool ContainsKey(string key) => this.firstKey is null ? this.elements is null ? false : this.elements.ContainsKey(key) : this.firstKey == key;
+ public IEnumerator> GetEnumerator() => throw new NotImplementedException();
+ public bool TryGetValue(string key, out ParseErrorElement value) => throw new NotImplementedException();
+ IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
+
+ public ParseErrorElementDictionary Merge(ParseErrorElementDictionary other)
+ {
+ if (this.elements is null)
+ {
+ if (other.elements is null)
+ {
+ if (this.firstKey == other.firstKey)
+ {
+ var newExpected = other.firstItem!.Expected.ToHashSet();
+ newExpected.UnionWith(this.firstItem!.Expected);
+ return new(other.firstKey!, new (newExpected, this.firstItem.Context));
+ }
+ else
+ {
+ return new(new Dictionary
+ {
+ { this.firstKey!, this.firstItem! },
+ { other.firstKey!, other.firstItem! },
+ });
+ }
+ }
+ else
+ {
+ return new(new (other.elements));
+ }
+ }
+
+ var elements = this.elements.Values.ToDictionary(e => e.Context, e => new ParseErrorElement(e.Expected.ToHashSet(), e.Context));
+ foreach (var element in other.Values)
+ {
+ if (elements.TryGetValue(element.Context, out var part))
+ {
+ foreach (var e in element.Expected) part.Expected.Add(e);
+ }
+ else
+ {
+ part = new(element.Expected.ToHashSet(), element.Context);
+ elements.Add(element.Context, part);
+ }
+ }
+
+ return new (elements);
+ }
+}