diff --git a/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs b/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs index fd679e1ce..018e151ad 100644 --- a/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs +++ b/src/Draco.Compiler/Internal/BoundTree/BoundNode.cs @@ -207,5 +207,6 @@ internal partial class BoundPattern internal partial class BoundDiscardPattern { + public static BoundDiscardPattern Default { get; } = new(null); public override TypeSymbol Type => IntrinsicSymbols.Never; } diff --git a/src/Draco.Compiler/Internal/FlowAnalysis/DecisionTree.cs b/src/Draco.Compiler/Internal/FlowAnalysis/DecisionTree.cs index ed10f407b..38413688c 100644 --- a/src/Draco.Compiler/Internal/FlowAnalysis/DecisionTree.cs +++ b/src/Draco.Compiler/Internal/FlowAnalysis/DecisionTree.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -80,6 +81,14 @@ public abstract class Node public abstract ImmutableArray> Children { get; } } + /// + /// A method that builds the accessor expression for a given expression and its member. + /// + /// The expression to build the accessor for. + /// The accessed member. + /// An expression that accesses the given member of the given value. + public delegate BoundExpression BuildAccessorDelegate(BoundExpression value, object? member); + private sealed class MutableNode : Node { // Observers @@ -176,7 +185,11 @@ public static DecisionTree Build( .Select(a => a.Action) .ToList()); // Wrap in the tree - var tree = new DecisionTree(intrinsicSymbols, root); + var tree = new DecisionTree( + intrinsicSymbols, + // TODO + (expr, mem) => throw new NotImplementedException(), + root); // Build it tree.Build(root); return tree; @@ -217,11 +230,13 @@ public static DecisionTree Build( public BoundPattern? UncoveredExample => throw new NotImplementedException(); private readonly IntrinsicSymbols intrinsicSymbols; + private readonly BuildAccessorDelegate buildAccessor; - private DecisionTree(IntrinsicSymbols intrinsicSymbols, MutableNode root) + private DecisionTree(IntrinsicSymbols intrinsicSymbols, BuildAccessorDelegate buildAccessor, MutableNode root) { this.intrinsicSymbols = intrinsicSymbols; this.mutableRoot = root; + this.buildAccessor = buildAccessor; } private void Build(MutableNode node) @@ -258,20 +273,53 @@ private void Build(MutableNode node) foreach (var pat in coveredPatterns) { // Specialize to the pattern - this.Specialize(node, pat); + var child = this.Specialize(node, pat); // We covered the value, subtract uncoveredDomain.SubtractPattern(pat); + // Add as child + node.Children.Add(new(pat, child)); } // If not complete, do defaulting - if (!uncoveredDomain.IsEmpty) this.Default(node); + if (!uncoveredDomain.IsEmpty) + { + var @default = this.Default(node); + // Add as child + node.Children.Add(new(BoundDiscardPattern.Default, @default)); + } // Recurse to children foreach (var (_, child) in node.MutableChildren) this.Build(child); } - private MutableNode Specialize(MutableNode node, BoundPattern specializer) => - throw new NotImplementedException(); + private MutableNode Specialize(MutableNode node, BoundPattern specializer) + { + var remainingRows = new List>(); + var remainingActions = new List(); + foreach (var (row, action) in node.PatternMatrix.Zip(node.Actions)) + { + var exploded = TryExplode(specializer, row[0]); + if (exploded is null) continue; + + var newRow = exploded.Concat(row.Skip(1)).ToList(); + if (newRow.Count == 0) newRow.Add(BoundDiscardPattern.Default); + + remainingRows.Add(newRow); + remainingActions.Add(action); + } + + var newArguments = Enumerable + .Range(0, remainingRows[0].Count - node.PatternMatrix[0].Count + 1) + .Select(i => this.buildAccessor(node.Arguments[0], i)) + .Concat(node.Arguments.Skip(1)) + .ToList(); + + return new MutableNode( + parent: node, + arguments: newArguments, + patternMatrix: remainingRows, + actions: remainingActions); + } private MutableNode Default(MutableNode node) => throw new NotImplementedException();