diff --git a/.gitignore b/.gitignore index b489d32..f9d5d37 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /tests_results /main.exe +/main.exe.mdb /*Generated.cs /*Generator.exe \ No newline at end of file diff --git a/LexerGenerator.cs b/LexerGenerator.cs index bb51d5f..3aeba58 100644 --- a/LexerGenerator.cs +++ b/LexerGenerator.cs @@ -18,10 +18,10 @@ public static class LexerGenerator { Case("StringOpen"), Case("StringClose")), Record("Rule", - Field("S", "oldState"), + Field("Lexer.S", "oldState"), Field("string", "description"), Field("Func", "test"), - Field("S", "throughState"), - Field("S", "newState")))); + Field("Lexer.S", "throughState"), + Field("Lexer.S", "newState")))); } } \ No newline at end of file diff --git a/Parser.cs b/Parser.cs index 1e118eb..65b6d6c 100644 --- a/Parser.cs +++ b/Parser.cs @@ -8,32 +8,44 @@ using S = Lexer.S; using Lexeme = Lexer.Lexeme; using static Global; -using PrecedenceDAG = System.Collections.Immutable.ImmutableDictionary; +using PrecedenceDAG = ImmutableDefaultDictionary; public static partial class Parser { - public static PrecedenceDAG DefaultPrecedenceDAG = new PrecedenceDAG(); + public static DAGNode EmptyDAGNode = new DAGNode( + infixLeftAssociative: ImmutableList.Empty, + prefix: ImmutableList.Empty, + closed: ImmutableList.Empty, + terminal: ImmutableList.Empty, + infixRightAssociative: ImmutableList.Empty, + infixNonAssociative: ImmutableList.Empty, + postfix: ImmutableList.Empty, + successorNodes: ImmutableList.Empty + ); - public static DAGNode With(DAGNode node, Operator @operator) { - var newNode = @operator.fixity.Match( - Closed: () => node.lens.closed.Cons(@operator), - InfixLeftAssociative: () => node.lens.infixLeftAssociative.Cons(@operator), - InfixRightAssociative: () => node.lens.infixRightAssociative.Cons(@operator), - InfixNonAssociative: () => node.lens.infixNonAssociative.Cons(@operator), - Prefix: () => node.lens.prefix.Cons(@operator), - Postfix: () => node.lens.postfix.Cons(@operator), - Terminal: () => node.lens.terminal.Cons(@operator) + public static PrecedenceDAG DefaultPrecedenceDAG + = new PrecedenceDAG(EmptyDAGNode); + + public static Whole With(this ILens node, Operator @operator) { + return @operator.fixity.Match( + Closed: + () => node.Closed().Cons(@operator), + InfixLeftAssociative: + () => node.InfixLeftAssociative().Cons(@operator), + InfixRightAssociative: + () => node.InfixRightAssociative().Cons(@operator), + InfixNonAssociative: + () => node.InfixNonAssociative().Cons(@operator), + Prefix: + () => node.Prefix().Cons(@operator), + Postfix: + () => node.Postfix().Cons(@operator), + Terminal: + () => node.Terminal().Cons(@operator) ); - // op.fixity, parts, holes - throw new NotImplementedException(); } - public static PrecedenceDAG With(PrecedenceDAG precedenceDAG, Operator @operator) { - precedenceDAG.lens(@operator.precedenceGroup); - /*precedenceDAG.update( - dagNode => dagNode.Add(@operator) - );*/ - throw new NotImplementedException(); - } + public static PrecedenceDAG With(PrecedenceDAG precedenceDAG, Operator @operator) + => precedenceDAG.lens()[@operator.precedenceGroup].With(@operator); public static void DagToGrammar(DAGNode precedenceDAG) { diff --git a/T4/Generator.cs b/T4/Generator.cs index 9e98141..a7fe0dd 100644 --- a/T4/Generator.cs +++ b/T4/Generator.cs @@ -19,6 +19,19 @@ public static class Generator { w("using System;"); w($"{singleHeader}"); + foreach (var type in types) { + var name = type.Key; + var kind = type.Value.Item1; + var components = type.Value.Item2; + switch (kind) { + case Kind.Record: + w.RecordUsing(header, footer, qualifier, name, @components); + break; + case Kind.Variant: + w.VariantUsing(header, footer, qualifier, name, @components); + break; + } + } w(""); foreach (var type in types) { var name = type.Key; diff --git a/T4/RecordGenerator.cs b/T4/RecordGenerator.cs index 9aaec48..c0ecdcd 100644 --- a/T4/RecordGenerator.cs +++ b/T4/RecordGenerator.cs @@ -6,6 +6,15 @@ using System.Linq; using Record = System.Collections.Immutable.ImmutableDictionary; public static class RecordGenerator { + private static void NewExampleComment(this Action w, string qualifier, string name, Record record) { + w($" /* To create an instance of {name}, write:"); + w($" new {name}("); + w(String.Join(",\n", record.Select(@field => + $" {@field.Key}: new {@field.Value}(…)"))); + w($" )"); + w($" */"); + } + private static void Fields(this Action w, string qualifier, string name, Record record) { foreach (var @field in record) { var F = @field.Key; @@ -69,7 +78,9 @@ public static class RecordGenerator { private static void Lenses(this Action w, string qualifier, string name, Record record) { w($" public sealed class Lens : ILens<{name}, Whole> {{"); w($" public readonly System.Func<{name}, Whole> wrap;"); - w($" public readonly {name} oldHole;"); + w($" private readonly {name} oldHole;"); + w($""); + w($" public {name} value {{ get => oldHole; }}"); w($""); w($" public Lens(System.Func<{name}, Whole> wrap, {name} oldHole) {{"); w($" this.wrap = wrap;"); @@ -84,7 +95,8 @@ public static class RecordGenerator { w($" => oldHole.{F}.ChainLens("); w($" value => wrap(oldHole.With{caseF}(value)));"); } - w($" public Whole Update(Func<{name}, {name}> update) => wrap(update(oldHole));"); + w($" public Whole Update(Func<{name}, {name}> update)"); + w($" => wrap(update(oldHole));"); w($" }}"); } @@ -106,10 +118,42 @@ public static class RecordGenerator { w($" }}"); } + private static void LensExtensionMethods(this Action w, string qualifier, string name, Record record) { + w($" public static class {name}LensExtensionMethods {{"); + foreach (var @field in record) { + var F = @field.Key; + var noAtF = F.StartsWith("@") ? F.Substring(1) : F; + var caseF = Char.ToUpper(noAtF[0]) + noAtF.Substring(1); + var Ty = @field.Value; + // same as {name}.Lens but as extension mehtods (should + // be extension properties once C# supports those) to + // be applied to instances of ILens<{name}, Whole> + w($" public static ILens<{Ty}, Whole>"); + w($" {caseF}("); + w($" this ILens<{qualifier}{name}, Whole> self)"); + w($" => self.value.{F}.ChainLens("); + w($" value => self.Update(oldHole => oldHole.With{caseF}(value)));"); + } + w($" }}"); + } + public static void Record(this Action w, string header, string footer, string qualifier, string name, Record record) { w($"{header}"); w(""); + w.NewExampleComment(qualifier, name, record); + w(""); w.RecordClass(qualifier, name, record); w($"{footer}"); + w.LensExtensionMethods(qualifier, name, record); + } + + private static void QualifierAliases(this Action w, string qualifier, string name, Record record) { + if (qualifier != "") { + w($"using {name} = {qualifier}{name};"); + } + } + + public static void RecordUsing(this Action w, string header, string footer, string qualifier, string name, Record record) { + w.QualifierAliases(qualifier, name, record); } } diff --git a/T4/VariantGenerator.cs b/T4/VariantGenerator.cs index 919290d..8111ebb 100644 --- a/T4/VariantGenerator.cs +++ b/T4/VariantGenerator.cs @@ -185,8 +185,20 @@ public static class VariantGenerator { w($"{header}"); w($""); w.MatchExampleComment(qualifier, name, variant); + w($""); w.VariantClass(qualifier, name, variant); + w($""); w.ExtensionMethods(qualifier, name, variant); w($"{footer}"); } + + private static void QualifierAliases(this Action w, string qualifier, string name, Variant variant) { + if (qualifier != "") { + w($"using {name} = {qualifier}{name};"); + } + } + + public static void VariantUsing(this Action w, string header, string footer, string qualifier, string name, Variant variant) { + w.QualifierAliases(qualifier, name, variant); + } } \ No newline at end of file diff --git a/Utils/Immutable/DefaultDictionary.cs b/Utils/Immutable/DefaultDictionary.cs index 2d76399..737f119 100644 --- a/Utils/Immutable/DefaultDictionary.cs +++ b/Utils/Immutable/DefaultDictionary.cs @@ -7,6 +7,11 @@ public class ImmutableDefaultDictionary : IEnumerable dictionary; + public ImmutableDefaultDictionary(TValue defaultValue) { + this.defaultValue = defaultValue; + this.dictionary = ImmutableDictionary.Empty; + } + public ImmutableDefaultDictionary(TValue defaultValue, ImmutableDictionary dictionary) { this.defaultValue = defaultValue; this.dictionary = dictionary; @@ -16,9 +21,15 @@ public class ImmutableDefaultDictionary : IEnumerable dictionary.GetOrDefault(key, defaultValue); } - public ImmutableDefaultDictionary With(TKey key, TValue value) + public ImmutableDefaultDictionary Add(TKey key, TValue value) => new ImmutableDefaultDictionary(defaultValue, dictionary.Add(key, value)); + public ImmutableDefaultDictionary SetItem(TKey key, TValue value) + => new ImmutableDefaultDictionary(defaultValue, dictionary.SetItem(key, value)); + + public ImmutableDefaultDictionary Remove(TKey key) + => new ImmutableDefaultDictionary(defaultValue, dictionary.Remove(key)); + public IEnumerator> GetEnumerator() => dictionary.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => dictionary.GetEnumerator(); diff --git a/Utils/Lens.cs b/Utils/Lens.cs index 1780f1d..fb759f7 100644 --- a/Utils/Lens.cs +++ b/Utils/Lens.cs @@ -1,9 +1,8 @@ -// Code quality of this file: low. - using System; using System.Collections.Immutable; public interface ILens { + Hole value { get; } Whole Update(Func update); } @@ -14,12 +13,6 @@ public static class LensExtensionMethods { public static Whole Cons(this ILens, Whole> lens, T value) => lens.Update(oldHole => oldHole.Cons(value)); - public static ImmutableListLens - ChainLens( - this ImmutableList hole, - System.Func, Whole> wrap) - => new ImmutableListLens(wrap: wrap, oldHole: hole); - public static ILens ChainLens(this string hole, System.Func wrap) => new LeafLens(wrap: wrap, oldHole: hole); public static ILens, Whole> ChainLens(this Func hole, System.Func, Whole> wrap) => new LeafLens, Whole>(wrap: wrap, oldHole: hole); diff --git a/Utils/Lens/ImmutableDefaultDictionaryLens.cs b/Utils/Lens/ImmutableDefaultDictionaryLens.cs new file mode 100644 index 0000000..93cf184 --- /dev/null +++ b/Utils/Lens/ImmutableDefaultDictionaryLens.cs @@ -0,0 +1,86 @@ +// Code quality of this file: medium. + +using System; +using System.Collections.Immutable; + +public sealed class ImmutableDefaultDictionaryValueLens : ILens { + public readonly Func, Whole> wrap; + + private readonly ImmutableDefaultDictionary oldDictionary; + + private readonly TKey oldKey; + + public TValue value { get => oldDictionary[oldKey]; } + + public ImmutableDefaultDictionaryValueLens(Func, Whole> wrap, ImmutableDefaultDictionary oldDictionary, TKey oldKey) { + // TODO: check that key exists. + this.wrap = wrap; + this.oldDictionary = oldDictionary; + this.oldKey = oldKey; + } + + // Put methods with the following signature here to focus on sub-parts of the list as needed. + // public ILens,Whole> sub-part => oldHole.sub-part.ChainLens(value => oldHole.with-sub-part(value)); + + public Whole Update(Func update) { + var oldValue = oldDictionary[oldKey]; + return wrap(oldDictionary.SetItem(oldKey, update(oldValue))); + } + + public ImmutableDefaultDictionaryValueLens UpdateKey(Func update) { + var newKey = update(oldKey); + return new ImmutableDefaultDictionaryValueLens( + wrap, + oldDictionary.Remove(oldKey).Add(newKey, oldDictionary[oldKey]), + newKey); + } +} + +public sealed class ImmutableDefaultDictionaryLens : ILens, Whole> { + public readonly Func, Whole> wrap; + private readonly ImmutableDefaultDictionary oldHole; + + public ImmutableDefaultDictionary value { get => oldHole; } + + public ImmutableDefaultDictionaryLens(Func, Whole> wrap, ImmutableDefaultDictionary oldHole) { + // TODO: check that key exists. + this.wrap = wrap; + this.oldHole = oldHole; + } + + // Put methods with the following signature here to focus on sub-parts of the list as needed. + public ImmutableDefaultDictionaryValueLens this[TKey key] { + get => new ImmutableDefaultDictionaryValueLens(wrap, oldHole, key); + } + + public Whole Update(Func, ImmutableDefaultDictionary> update) { + return wrap(update(oldHole)); + } +} + +public static class ImmutableDefaultDictionaryLensExtensionMethods { + public static ImmutableDefaultDictionaryLens + ChainLens( + this ImmutableDefaultDictionary hole, + System.Func, Whole> wrap) + => new ImmutableDefaultDictionaryLens(wrap: wrap, oldHole: hole); + + // TODO: this should be an extension property (once C# supports them) + public static ImmutableDefaultDictionaryLens> + lens( + this ImmutableDefaultDictionary d) + => d.ChainLens(x => x); + + // this is a shorthand since we don't have extension properties + public static ImmutableDefaultDictionaryValueLens> + lens( + this ImmutableDefaultDictionary d, + TKey key) + => d.lens()[key]; + + public static ImmutableDefaultDictionaryValueLens + UpdateKey( + this ImmutableDefaultDictionaryValueLens lens, + TKey newKey) + => lens.UpdateKey(oldKey => newKey); +} \ No newline at end of file diff --git a/Utils/Lens/ImmutableDictionaryLens.cs b/Utils/Lens/ImmutableDictionaryLens.cs deleted file mode 100644 index 4a48b90..0000000 --- a/Utils/Lens/ImmutableDictionaryLens.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Immutable; - -public sealed class ImmutableDictionaryValueLens : ILens { - public readonly Func, Whole> wrap; - public readonly ImmutableDictionary oldDictionary; - public readonly TKey oldKey; - - public ImmutableDictionaryValueLens(Func, Whole> wrap, ImmutableDictionary oldDictionary, TKey oldKey) { - // TODO: check that key exists. - this.wrap = wrap; - this.oldDictionary = oldDictionary; - this.oldKey = oldKey; - } - - // Put methods with the following signature here to focus on sub-parts of the list as needed. - // public ILens,Whole> sub-part => oldHole.sub-part.ChainLens(value => oldHole.with-sub-part(value)); - - public Whole Update(Func update) { - var oldValue = oldDictionary[oldKey]; - return wrap(oldDictionary.SetItem(oldKey, update(oldValue))); - } - - public ImmutableDictionaryValueLens UpdateKey(Func update) { - var newKey = update(oldKey); - return new ImmutableDictionaryValueLens( - wrap, - oldDictionary.Remove(oldKey).Add(newKey, oldDictionary[oldKey]), - newKey); - } -} - -public sealed class ImmutableDictionaryLens : ILens, Whole> { - public readonly Func, Whole> wrap; - public readonly ImmutableDictionary oldHole; - - public ImmutableDictionaryLens(Func, Whole> wrap, ImmutableDictionary oldHole) { - // TODO: check that key exists. - this.wrap = wrap; - this.oldHole = oldHole; - } - - // Put methods with the following signature here to focus on sub-parts of the list as needed. - public ImmutableDictionaryValueLens this[TKey key] { - get => new ImmutableDictionaryValueLens(wrap, oldHole, key); - } - - public Whole Update(Func, ImmutableDictionary> update) { - return wrap(update(oldHole)); - } -} - -public static class ImmutableDictionaryLensExtensionMethods { - public static ImmutableDictionaryLens> lens(this ImmutableDictionary d) - => new ImmutableDictionaryLens>(x => x, d); - - public static ImmutableDictionaryValueLens UpdateKey(this ImmutableDictionaryValueLens lens, TKey newKey) - => lens.UpdateKey(oldKey => newKey); - - // This would need an IFocusable constraint which is hard to get - //public static ILens, Whole> ChainLens(this ImmutableDictionary hole, System.Func, Whole> wrap) - // => new ImmutableDictionaryLens(wrap: wrap, oldHole: hole); -} \ No newline at end of file diff --git a/Utils/Lens/ImmutableListLens.cs b/Utils/Lens/ImmutableListLens.cs index 118e2cb..30cd4d1 100644 --- a/Utils/Lens/ImmutableListLens.cs +++ b/Utils/Lens/ImmutableListLens.cs @@ -3,7 +3,9 @@ using System.Collections.Immutable; public sealed class ImmutableListLens : ILens, Whole> { public readonly Func, Whole> wrap; - public readonly ImmutableList oldHole; + private readonly ImmutableList oldHole; + + public ImmutableList value { get => oldHole; } public ImmutableListLens(Func, Whole> wrap, ImmutableList oldHole) { this.wrap = wrap; @@ -11,12 +13,21 @@ public sealed class ImmutableListLens : ILens, Whole> } // Put methods with the following signature here to focus on sub-parts of the list as needed. - // public ILens,Whole> sub-part => oldHole.sub-part.ChainLens(value => oldHole.with-sub-part(value)); + // public ILens,Whole> sub-part + // => oldHole.sub-part.ChainLens(value => oldHole.with-sub-part(value)); public Whole Update(Func, ImmutableList> update) => wrap(update(oldHole)); } public static class ImmutableListLensExtensionMethods { - public static ILens, Whole> ChainLens(this ImmutableList hole, System.Func, Whole> wrap) - => new ImmutableListLens(wrap: wrap, oldHole: hole); -} \ No newline at end of file + public static ImmutableListLens + ChainLens( + this ImmutableList hole, + System.Func, Whole> wrap) + => new ImmutableListLens(wrap: wrap, oldHole: hole); + + public static ImmutableListLens> + lens( + this ImmutableList d) + => d.ChainLens(x => x); +} diff --git a/Utils/Lens/LeafLens.cs b/Utils/Lens/LeafLens.cs index d079acc..10ab6cf 100644 --- a/Utils/Lens/LeafLens.cs +++ b/Utils/Lens/LeafLens.cs @@ -5,7 +5,9 @@ using System.Collections.Immutable; // interesting to further focus. public sealed class LeafLens : ILens { public readonly System.Func wrap; - public readonly T oldHole; + private readonly T oldHole; + + public T value { get => oldHole; } public LeafLens(System.Func wrap, T oldHole) { this.wrap = wrap;