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; } }