Skip to content
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
26 changes: 7 additions & 19 deletions Sources/SynKit/Libraries/Parser/ParseError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

using System;
using System.Collections.Generic;
using System.Collections.Generic.Polyfill;
using System.Linq;

namespace Yoakke.SynKit.Parser;

Expand All @@ -14,10 +12,12 @@ namespace Yoakke.SynKit.Parser;
/// </summary>
public class ParseError
{
private ParseErrorElementDictionary elements;

/// <summary>
/// The error cases in different parse contexts.
/// </summary>
public IReadOnlyDictionary<string, ParseErrorElement> Elements { get; }
public IReadOnlyDictionary<string, ParseErrorElement> Elements => this.elements;

/// <summary>
/// The item that was found, if any.
Expand All @@ -37,13 +37,13 @@ public class ParseError
/// <param name="position">The position where the error occurred.</param>
/// <param name="context">The context in which the error occurred.</param>
public ParseError(object expected, object? got, IComparable position, string context)
: this(new Dictionary<string, ParseErrorElement> { { context, new ParseErrorElement(expected, context) } }, got, position)
: this(new ParseErrorElementDictionary(context, new ParseErrorElement(expected, context)), got, position)
{
}

private ParseError(IReadOnlyDictionary<string, ParseErrorElement> 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;
}
Expand All @@ -65,19 +65,7 @@ private ParseError(IReadOnlyDictionary<string, ParseErrorElement> 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?
Expand Down
94 changes: 94 additions & 0 deletions Sources/SynKit/Libraries/Parser/ParseErrorElementDictionary.cs
Original file line number Diff line number Diff line change
@@ -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<string, ParseErrorElement>
{
private string? firstKey;
private ParseErrorElement? firstItem;
private Dictionary<string, ParseErrorElement>? elements;

private ParseErrorElementDictionary()
{
}

private ParseErrorElementDictionary(Dictionary<string, ParseErrorElement> 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<string> Keys => this.firstKey is null ? this.elements!.Keys : new[] { this.firstKey };

public IEnumerable<ParseErrorElement> 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<KeyValuePair<string, ParseErrorElement>> 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<string, ParseErrorElement>
{
{ 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);
}
}
Loading