using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using LexicalAnalysis;
using AST;
using System.Collections;
using System.Linq;
using System.Text;
namespace Parsing {
///
/// A minimal environment solely for parsing, intended to resolve ambiguity.
///
public sealed class ParserEnvironment {
private ParserEnvironment(ImmutableStack scopes) {
this.Scopes = scopes;
}
public ParserEnvironment()
: this(ImmutableStack.Create(new Scope())) { }
public ParserEnvironment InScope() =>
new ParserEnvironment(this.Scopes.Push(new Scope()));
public ParserEnvironment OutScope() =>
new ParserEnvironment(this.Scopes.Pop());
public ParserEnvironment AddSymbol(String name, StorageClsSpec storageClsSpec) =>
new ParserEnvironment(
this.Scopes.Pop().Push(
this.Scopes.Peek().AddSymbol(name, storageClsSpec)
)
);
public Boolean IsTypedefName(String name) {
foreach (var scope in this.Scopes) {
if (scope.Symbols.ContainsKey(name)) {
return scope.Symbols[name] == StorageClsSpec.TYPEDEF;
}
}
return false;
}
private class Scope {
public Scope()
: this(ImmutableDictionary.Empty) { }
private Scope(ImmutableDictionary symbols) {
this.Symbols = symbols;
}
public Scope AddSymbol(String name, StorageClsSpec storageClsSpec) =>
new Scope(this.Symbols.Add(name, storageClsSpec));
public ImmutableDictionary Symbols { get; }
}
private ImmutableStack Scopes { get; }
}
/// Bypasses a number of elements in a sequence and then returns the remaining elements.
/// The type of the elements of the sequence.
/// Allows for stacking many Skips at once without overflowing the stack, unlike the version in LINQ
public sealed class SkipEnumerable : IEnumerable
{
private readonly IEnumerable source;
private uint count = 0;
internal SkipEnumerable(IEnumerable source)
{
this.source = source;
// Merge any downlevel SkipEnumerables into this one.
var skip = source as SkipEnumerable;
while (skip != null)
{
this.source = skip.source;
count += skip.count;
skip = this.source as SkipEnumerable;
}
}
public IEnumerator GetEnumerator()
{
return new Enumerator(source.GetEnumerator(), count);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public SkipEnumerable Skip(uint count)
{
if (count == 0) return this;
var ret = new SkipEnumerable(source);
ret.count = this.count + count;
return ret;
}
public SkipEnumerable Skip(int count)
{
if (count <= 0) return this;
return Skip((uint)count);
}
private sealed class Enumerator : IEnumerator
{
private readonly IEnumerator source;
private readonly uint count;
internal Enumerator(IEnumerator source, uint count)
{
this.source = source;
this.count = count;
Reset();
}
public T Current => source.Current;
object IEnumerator.Current => Current;
public void Dispose() => source.Dispose();
public bool MoveNext() => source.MoveNext();
public void Reset()
{
source.Reset();
for (int i = 0; i < count; i++) source.MoveNext();
}
}
}
///
/// The input Type for every parsing function.
///
public sealed class ParserInput {
public ParserInput(ParserEnvironment environment, SkipEnumerable source) {
this.Environment = environment;
this.Source = source;
}
public ParserInput(ParserEnvironment environment, IEnumerable source)
: this(environment, new SkipEnumerable(source))
{
}
public ParserEnvironment Environment { get; }
public SkipEnumerable Source { get; }
public IParserResult Parse(IParser parser) =>
parser.Parse(this);
}
///
/// A parser result with/without content.
///
public interface IParserResult : ILineInfo {
ParserInput ToInput();
Boolean IsSuccessful { get; }
ParserEnvironment Environment { get; }
IEnumerable Source { get; }
string Name { get; set; }
}
public interface IParserFailed : IParserResult
{
IReadOnlyList InnerFailures { get; }
IEnumerable WalkInnerFailures(bool named = false);
IEnumerable ToStrings();
}
///
/// A failed result.
///
public sealed class ParserFailed : IParserFailed {
public ParserInput ToInput() {
throw new InvalidOperationException("Parser failed, can't construct input.");
}
public Boolean IsSuccessful => false;
public ParserEnvironment Environment {
get {
throw new NotSupportedException("Parser failed, can't get environment.");
}
}
public IEnumerable Source {
get {
throw new NotSupportedException("Parser failed, can't get source.");
}
}
public int Line { get; }
public int Column { get; }
private string m_Name = string.Empty;
public string Name
{
get => m_Name;
set
{
if (m_Name == string.Empty) m_Name = value;
}
}
public IReadOnlyList InnerFailures { get; } = null;
public IEnumerable WalkInnerFailures(bool named = false)
{
var inner = InnerFailures;
if (inner == null) yield break;
foreach (var item in inner)
{
if (named && !string.IsNullOrEmpty(item.Name)) yield return item;
foreach (var innerItem in item.WalkInnerFailures(named)) yield return innerItem;
}
yield break;
}
public IEnumerable ToStrings()
{
var sb = new StringBuilder();
if (!string.IsNullOrEmpty(Name)) sb.AppendFormat("{0}: ", Name);
sb.AppendFormat("line {0}, column {1}", Line, Column);
yield return sb;
if (InnerFailures == null) yield break;
foreach (var inner in InnerFailures)
{
foreach (var innerSb in inner.ToStrings())
{
innerSb.Insert(0, " ");
yield return innerSb;
}
}
yield break;
}
public override string ToString()
{
var sb = new StringBuilder();
foreach (var innerSb in ToStrings())
{
sb.Append(innerSb);
sb.AppendLine();
}
return sb.ToString();
}
public ParserFailed(int line, int column)
{
Line = line;
Column = column;
}
public ParserFailed(IParserResult result) : this(result.Line, result.Column) {
var failure = result as IParserFailed;
if (failure != null)
{
InnerFailures = new List() { failure }.AsReadOnly();
}
}
public ParserFailed(ParserInput input, List inner) : this(input)
{
InnerFailures = inner.AsReadOnly();
}
public ParserFailed(ParserInput input) : this(input.Source) { }
public ParserFailed(IEnumerable source) : this(source.First()) { }
public ParserFailed(Token token) : this(token.Line, token.Column) { }
}
///
/// A succeeded result.
///
public sealed class ParserSucceeded : IParserResult {
public ParserSucceeded(ParserEnvironment environment, IEnumerable source) {
this.Environment = environment;
this.Source = source;
}
public Boolean IsSuccessful => true;
public ParserInput ToInput() => new ParserInput(this.Environment, this.Source);
public ParserEnvironment Environment { get; }
public IEnumerable Source { get; }
public int Line => Source.First().Line;
public int Column => Source.First().Column;
private string m_Name = string.Empty;
public string Name
{
get => m_Name;
set
{
if (m_Name == string.Empty) m_Name = value;
}
}
public static ParserSucceeded Create(ParserEnvironment environment, IEnumerable source) =>
new ParserSucceeded(environment, source);
public static ParserSucceeded Create(R result, ParserEnvironment environment, IEnumerable source) =>
new ParserSucceeded(result, environment, source);
}
///
/// A parser result with content.
///
public interface IParserResult : IParserResult {
R Result { get; }
}
public sealed class ParserFailed : IParserResult, IParserFailed {
public ParserInput ToInput() {
throw new InvalidOperationException("Parser failed, can't construct input.");
}
public Boolean IsSuccessful => false;
public R Result {
get {
throw new NotSupportedException("Parser failed, can't get result.");
}
}
public ParserEnvironment Environment {
get {
throw new NotSupportedException("Parser failed, can't get environment.");
}
}
public IEnumerable Source {
get {
throw new NotSupportedException("Parser failed, can't get source.");
}
}
public int Line { get; }
public int Column { get; }
private string m_Name = string.Empty;
public string Name
{
get => m_Name;
set
{
if (m_Name == string.Empty) m_Name = value;
}
}
public IReadOnlyList InnerFailures { get; } = null;
public IEnumerable WalkInnerFailures(bool named = false)
{
var inner = InnerFailures;
if (inner == null) yield break;
foreach (var item in inner)
{
if (!named || !string.IsNullOrEmpty(item.Name)) yield return item;
foreach (var innerItem in item.WalkInnerFailures(named)) yield return innerItem;
}
yield break;
}
public IEnumerable ToStrings()
{
var sb = new StringBuilder();
if (!string.IsNullOrEmpty(Name)) sb.AppendFormat("{0}: ", Name);
sb.AppendFormat("line {0}, column {1}", Line, Column);
yield return sb;
if (InnerFailures == null) yield break;
foreach (var inner in InnerFailures)
{
foreach (var innerSb in inner.ToStrings())
{
innerSb.Insert(0, " ");
yield return innerSb;
}
}
yield break;
}
public override string ToString()
{
var sb = new StringBuilder();
foreach (var innerSb in ToStrings())
{
sb.Append(innerSb);
sb.AppendLine();
}
return sb.ToString();
}
public ParserFailed(int line, int column)
{
Line = line;
Column = column;
}
public ParserFailed(IParserResult result) : this(result.Line, result.Column)
{
var failure = result as IParserFailed;
if (failure != null)
{
InnerFailures = new List() { failure }.AsReadOnly();
}
}
public ParserFailed(ParserInput input, List inner) : this(input)
{
InnerFailures = inner.AsReadOnly();
}
public ParserFailed(ParserInput input) : this(input.Source) { }
public ParserFailed(IEnumerable source) : this(source.First()) { }
public ParserFailed(Token token) : this(token.Line, token.Column) { }
}
public sealed class ParserSucceeded : IParserResult {
public ParserSucceeded(R result, ParserEnvironment environment, IEnumerable source) {
this.Result = result;
this.Environment = environment;
this.Source = source;
}
public ParserInput ToInput() => new ParserInput(this.Environment, this.Source);
public Boolean IsSuccessful => true;
public R Result { get; }
public ParserEnvironment Environment { get; }
public IEnumerable Source { get; }
private string m_Name = string.Empty;
public string Name
{
get => m_Name;
set
{
if (m_Name == string.Empty) m_Name = value;
}
}
public int Line => Source.First().Line;
public int Column => Source.First().Column;
}
}