Lenses are working, implemented addition of an operator to the parser.

This commit is contained in:
Suzanne Soy 2020-08-20 20:40:45 +00:00
parent b517d4f032
commit fce4e7f53c
12 changed files with 225 additions and 103 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
/tests_results
/main.exe
/main.exe.mdb
/*Generated.cs
/*Generator.exe

View File

@ -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<GraphemeCluster, bool>", "test"),
Field("S", "throughState"),
Field("S", "newState"))));
Field("Lexer.S", "throughState"),
Field("Lexer.S", "newState"))));
}
}

View File

@ -8,32 +8,44 @@ using S = Lexer.S;
using Lexeme = Lexer.Lexeme;
using static Global;
using PrecedenceDAG = System.Collections.Immutable.ImmutableDictionary<string, Parser.DAGNode>;
using PrecedenceDAG = ImmutableDefaultDictionary<string, Parser.DAGNode>;
public static partial class Parser {
public static PrecedenceDAG DefaultPrecedenceDAG = new PrecedenceDAG();
public static DAGNode EmptyDAGNode = new DAGNode(
infixLeftAssociative: ImmutableList<Operator>.Empty,
prefix: ImmutableList<Operator>.Empty,
closed: ImmutableList<Operator>.Empty,
terminal: ImmutableList<Operator>.Empty,
infixRightAssociative: ImmutableList<Operator>.Empty,
infixNonAssociative: ImmutableList<Operator>.Empty,
postfix: ImmutableList<Operator>.Empty,
successorNodes: ImmutableList<string>.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<Whole>(this ILens<DAGNode, Whole> 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) {

View File

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

View File

@ -6,6 +6,15 @@ using System.Linq;
using Record = System.Collections.Immutable.ImmutableDictionary<string, string>;
public static class RecordGenerator {
private static void NewExampleComment(this Action<string> 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<string> 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<string> w, string qualifier, string name, Record record) {
w($" public sealed class Lens<Whole> : 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<string> 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}<Whole>(");
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<string> 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<string> w, string qualifier, string name, Record record) {
if (qualifier != "") {
w($"using {name} = {qualifier}{name};");
}
}
public static void RecordUsing(this Action<string> w, string header, string footer, string qualifier, string name, Record record) {
w.QualifierAliases(qualifier, name, record);
}
}

View File

@ -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<string> w, string qualifier, string name, Variant variant) {
if (qualifier != "") {
w($"using {name} = {qualifier}{name};");
}
}
public static void VariantUsing(this Action<string> w, string header, string footer, string qualifier, string name, Variant variant) {
w.QualifierAliases(qualifier, name, variant);
}
}

View File

@ -7,6 +7,11 @@ public class ImmutableDefaultDictionary<TKey, TValue> : IEnumerable<KeyValuePair
public readonly TValue defaultValue;
public readonly ImmutableDictionary<TKey, TValue> dictionary;
public ImmutableDefaultDictionary(TValue defaultValue) {
this.defaultValue = defaultValue;
this.dictionary = ImmutableDictionary<TKey, TValue>.Empty;
}
public ImmutableDefaultDictionary(TValue defaultValue, ImmutableDictionary<TKey, TValue> dictionary) {
this.defaultValue = defaultValue;
this.dictionary = dictionary;
@ -16,9 +21,15 @@ public class ImmutableDefaultDictionary<TKey, TValue> : IEnumerable<KeyValuePair
get => dictionary.GetOrDefault(key, defaultValue);
}
public ImmutableDefaultDictionary<TKey, TValue> With(TKey key, TValue value)
public ImmutableDefaultDictionary<TKey, TValue> Add(TKey key, TValue value)
=> new ImmutableDefaultDictionary<TKey, TValue>(defaultValue, dictionary.Add(key, value));
public ImmutableDefaultDictionary<TKey, TValue> SetItem(TKey key, TValue value)
=> new ImmutableDefaultDictionary<TKey, TValue>(defaultValue, dictionary.SetItem(key, value));
public ImmutableDefaultDictionary<TKey, TValue> Remove(TKey key)
=> new ImmutableDefaultDictionary<TKey, TValue>(defaultValue, dictionary.Remove(key));
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => dictionary.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => dictionary.GetEnumerator();

View File

@ -1,9 +1,8 @@
// Code quality of this file: low.
using System;
using System.Collections.Immutable;
public interface ILens<Hole, Whole> {
Hole value { get; }
Whole Update(Func<Hole, Hole> update);
}
@ -14,12 +13,6 @@ public static class LensExtensionMethods {
public static Whole Cons<T, Whole>(this ILens<ImmutableList<T>, Whole> lens, T value)
=> lens.Update(oldHole => oldHole.Cons(value));
public static ImmutableListLens<T, Whole>
ChainLens<T, Whole>(
this ImmutableList<T> hole,
System.Func<ImmutableList<T>, Whole> wrap)
=> new ImmutableListLens<T, Whole>(wrap: wrap, oldHole: hole);
public static ILens<string, Whole> ChainLens<Whole>(this string hole, System.Func<string, Whole> wrap) => new LeafLens<string, Whole>(wrap: wrap, oldHole: hole);
public static ILens<Func<GraphemeCluster,bool>, Whole> ChainLens<Whole>(this Func<GraphemeCluster,bool> hole, System.Func<Func<GraphemeCluster,bool>, Whole> wrap) => new LeafLens<Func<GraphemeCluster,bool>, Whole>(wrap: wrap, oldHole: hole);

View File

@ -0,0 +1,86 @@
// Code quality of this file: medium.
using System;
using System.Collections.Immutable;
public sealed class ImmutableDefaultDictionaryValueLens<TKey, TValue, Whole> : ILens<TValue, Whole> {
public readonly Func<ImmutableDefaultDictionary<TKey, TValue>, Whole> wrap;
private readonly ImmutableDefaultDictionary<TKey, TValue> oldDictionary;
private readonly TKey oldKey;
public TValue value { get => oldDictionary[oldKey]; }
public ImmutableDefaultDictionaryValueLens(Func<ImmutableDefaultDictionary<TKey, TValue>, Whole> wrap, ImmutableDefaultDictionary<TKey, TValue> 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<ImmutableDefaultDictionary<T>,Whole> sub-part => oldHole.sub-part.ChainLens(value => oldHole.with-sub-part(value));
public Whole Update(Func<TValue, TValue> update) {
var oldValue = oldDictionary[oldKey];
return wrap(oldDictionary.SetItem(oldKey, update(oldValue)));
}
public ImmutableDefaultDictionaryValueLens<TKey, TValue, Whole> UpdateKey(Func<TKey, TKey> update) {
var newKey = update(oldKey);
return new ImmutableDefaultDictionaryValueLens<TKey, TValue, Whole>(
wrap,
oldDictionary.Remove(oldKey).Add(newKey, oldDictionary[oldKey]),
newKey);
}
}
public sealed class ImmutableDefaultDictionaryLens<TKey, TValue, Whole> : ILens<ImmutableDefaultDictionary<TKey, TValue>, Whole> {
public readonly Func<ImmutableDefaultDictionary<TKey, TValue>, Whole> wrap;
private readonly ImmutableDefaultDictionary<TKey, TValue> oldHole;
public ImmutableDefaultDictionary<TKey, TValue> value { get => oldHole; }
public ImmutableDefaultDictionaryLens(Func<ImmutableDefaultDictionary<TKey, TValue>, Whole> wrap, ImmutableDefaultDictionary<TKey, TValue> 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<TKey, TValue, Whole> this[TKey key] {
get => new ImmutableDefaultDictionaryValueLens<TKey, TValue, Whole>(wrap, oldHole, key);
}
public Whole Update(Func<ImmutableDefaultDictionary<TKey, TValue>, ImmutableDefaultDictionary<TKey, TValue>> update) {
return wrap(update(oldHole));
}
}
public static class ImmutableDefaultDictionaryLensExtensionMethods {
public static ImmutableDefaultDictionaryLens<TKey, TValue, Whole>
ChainLens<TKey, TValue, Whole>(
this ImmutableDefaultDictionary<TKey, TValue> hole,
System.Func<ImmutableDefaultDictionary<TKey, TValue>, Whole> wrap)
=> new ImmutableDefaultDictionaryLens<TKey, TValue, Whole>(wrap: wrap, oldHole: hole);
// TODO: this should be an extension property (once C# supports them)
public static ImmutableDefaultDictionaryLens<TKey, TValue, ImmutableDefaultDictionary<TKey, TValue>>
lens<TKey, TValue>(
this ImmutableDefaultDictionary<TKey, TValue> d)
=> d.ChainLens(x => x);
// this is a shorthand since we don't have extension properties
public static ImmutableDefaultDictionaryValueLens<TKey, TValue, ImmutableDefaultDictionary<TKey, TValue>>
lens<TKey, TValue>(
this ImmutableDefaultDictionary<TKey, TValue> d,
TKey key)
=> d.lens()[key];
public static ImmutableDefaultDictionaryValueLens<TKey, TValue, Whole>
UpdateKey<TKey, TValue, Whole>(
this ImmutableDefaultDictionaryValueLens<TKey, TValue, Whole> lens,
TKey newKey)
=> lens.UpdateKey(oldKey => newKey);
}

View File

@ -1,63 +0,0 @@
using System;
using System.Collections.Immutable;
public sealed class ImmutableDictionaryValueLens<TKey, TValue, Whole> : ILens<TValue, Whole> {
public readonly Func<ImmutableDictionary<TKey, TValue>, Whole> wrap;
public readonly ImmutableDictionary<TKey, TValue> oldDictionary;
public readonly TKey oldKey;
public ImmutableDictionaryValueLens(Func<ImmutableDictionary<TKey, TValue>, Whole> wrap, ImmutableDictionary<TKey, TValue> 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<ImmutableList<T>,Whole> sub-part => oldHole.sub-part.ChainLens(value => oldHole.with-sub-part(value));
public Whole Update(Func<TValue, TValue> update) {
var oldValue = oldDictionary[oldKey];
return wrap(oldDictionary.SetItem(oldKey, update(oldValue)));
}
public ImmutableDictionaryValueLens<TKey, TValue, Whole> UpdateKey(Func<TKey, TKey> update) {
var newKey = update(oldKey);
return new ImmutableDictionaryValueLens<TKey, TValue, Whole>(
wrap,
oldDictionary.Remove(oldKey).Add(newKey, oldDictionary[oldKey]),
newKey);
}
}
public sealed class ImmutableDictionaryLens<TKey, TValue, Whole> : ILens<ImmutableDictionary<TKey, TValue>, Whole> {
public readonly Func<ImmutableDictionary<TKey, TValue>, Whole> wrap;
public readonly ImmutableDictionary<TKey, TValue> oldHole;
public ImmutableDictionaryLens(Func<ImmutableDictionary<TKey, TValue>, Whole> wrap, ImmutableDictionary<TKey, TValue> 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<TKey, TValue, Whole> this[TKey key] {
get => new ImmutableDictionaryValueLens<TKey, TValue, Whole>(wrap, oldHole, key);
}
public Whole Update(Func<ImmutableDictionary<TKey, TValue>, ImmutableDictionary<TKey, TValue>> update) {
return wrap(update(oldHole));
}
}
public static class ImmutableDictionaryLensExtensionMethods {
public static ImmutableDictionaryLens<TKey, TValue, ImmutableDictionary<TKey, TValue>> lens<TKey, TValue>(this ImmutableDictionary<TKey, TValue> d)
=> new ImmutableDictionaryLens<TKey, TValue, ImmutableDictionary<TKey, TValue>>(x => x, d);
public static ImmutableDictionaryValueLens<TKey, TValue, Whole> UpdateKey<TKey, TValue, Whole, Whole>(this ImmutableDictionaryValueLens<TKey, TValue, Whole> lens, TKey newKey)
=> lens.UpdateKey(oldKey => newKey);
// This would need an IFocusable<TValue> constraint which is hard to get
//public static ILens<ImmutableDictionary<TKey, ?>, Whole> ChainLens<TKey, Whole>(this ImmutableDictionary<TKey, ?> hole, System.Func<ImmutableDictionary<TKey, ?>, Whole> wrap)
// => new ImmutableDictionaryLens<TKey, ?, Whole>(wrap: wrap, oldHole: hole);
}

View File

@ -3,7 +3,9 @@ using System.Collections.Immutable;
public sealed class ImmutableListLens<T, Whole> : ILens<ImmutableList<T>, Whole> {
public readonly Func<ImmutableList<T>, Whole> wrap;
public readonly ImmutableList<T> oldHole;
private readonly ImmutableList<T> oldHole;
public ImmutableList<T> value { get => oldHole; }
public ImmutableListLens(Func<ImmutableList<T>, Whole> wrap, ImmutableList<T> oldHole) {
this.wrap = wrap;
@ -11,12 +13,21 @@ public sealed class ImmutableListLens<T, Whole> : ILens<ImmutableList<T>, Whole>
}
// Put methods with the following signature here to focus on sub-parts of the list as needed.
// public ILens<ImmutableList<T>,Whole> sub-part => oldHole.sub-part.ChainLens(value => oldHole.with-sub-part(value));
// public ILens<ImmutableList<T>,Whole> sub-part
// => oldHole.sub-part.ChainLens(value => oldHole.with-sub-part(value));
public Whole Update(Func<ImmutableList<T>, ImmutableList<T>> update) => wrap(update(oldHole));
}
public static class ImmutableListLensExtensionMethods {
public static ILens<ImmutableList<string>, Whole> ChainLens<Whole>(this ImmutableList<string> hole, System.Func<ImmutableList<string>, Whole> wrap)
=> new ImmutableListLens<string, Whole>(wrap: wrap, oldHole: hole);
}
public static ImmutableListLens<T, Whole>
ChainLens<T, Whole>(
this ImmutableList<T> hole,
System.Func<ImmutableList<T>, Whole> wrap)
=> new ImmutableListLens<T, Whole>(wrap: wrap, oldHole: hole);
public static ImmutableListLens<T, ImmutableList<T>>
lens<T>(
this ImmutableList<T> d)
=> d.ChainLens(x => x);
}

View File

@ -5,7 +5,9 @@ using System.Collections.Immutable;
// interesting to further focus.
public sealed class LeafLens<T, Whole> : ILens<T, Whole> {
public readonly System.Func<T, Whole> wrap;
public readonly T oldHole;
private readonly T oldHole;
public T value { get => oldHole; }
public LeafLens(System.Func<T, Whole> wrap, T oldHole) {
this.wrap = wrap;