private ProgramSet LearnTree(PremSpec <TInput, SyntaxNode> spec) { // Case 1: leaf nodes, using `Leaf`. if (spec.Forall((i, o) => o is Token)) { #if DEBUG Log.Tree("Leaf |- {0}", spec); #endif Label label; if (spec.Identical((i, o) => o.label, out label)) { #if DEBUG Log.IncIndent(); Log.Tree("label = {0}", label); #endif var tokenSpace = Intersect(spec.Select(p => LearnToken(p.Key, p.Value.code))); #if DEBUG Log.DecIndent(); #endif if (!tokenSpace.IsEmpty) { return(ProgramSet.Join(Op(nameof(Semantics.Leaf)), ProgramSet.List(Symbol("label"), Label(label)), tokenSpace)); } } // else: Inconsistent specification. } // Case 2: nodes/lists, copy a reference from old tree. if (spec.Forall((i, o) => o.matches.Any())) { #if DEBUG Log.Tree("Copy |- {0}", spec); Log.IncIndent(); #endif var refSpecs = spec.FlatMap((i, o) => o.matches); var refSpaces = new List <ProgramSet>(); #if DEBUG var total = refSpecs.Count(); var count = 1; #endif foreach (var refSpec in refSpecs) { #if DEBUG Log.Tree("{0}/{1} ref |- {2}", count, total, refSpec); Log.IncIndent(); #endif var space = LearnRef(refSpec); if (!space.IsEmpty) { refSpaces.Add(space); break; } #if DEBUG Log.DecIndent(); count++; #endif } #if DEBUG Log.DecIndent(); #endif var refSpace = Union(refSpaces); if (!refSpace.IsEmpty) { return(ProgramSet.Join(Op(nameof(Semantics.Copy)), refSpace)); } } // Case 3: constructor nodes, using `Node`. if (spec.Forall((i, o) => o is Node)) { var childrenSpace = Optional <ProgramSet> .Nothing; #if DEBUG Log.Tree("Node |- {0}", spec); #endif Label label; if (spec.Identical((i, o) => o.label, out label)) { var labelSpace = ProgramSet.List(Symbol("Label"), Label(label)); var childrenSpec = spec.MapOutputs((i, o) => o.GetChildren()); #if DEBUG Log.IncIndent(); Log.Tree("label = {0}", label); #endif int arity; // Same number of children, learn one-by-one. if (childrenSpec.Identical((i, cs) => cs.Count(), out arity)) { #if DEBUG Log.Tree("children |- {0}", childrenSpec); Log.IncIndent(); #endif childrenSpace = LearnChildren(childrenSpec); #if DEBUG Log.DecIndent(); #endif if (childrenSpace.HasValue && !childrenSpace.Value.IsEmpty) { return(ProgramSet.Join(Op(nameof(Semantics.Node)), ProgramSet.List(Symbol("label"), Label(label)), childrenSpace.Value)); } } else // Different number of children, try `Append`. { #if DEBUG Log.Tree("append |- {0}", childrenSpec); Log.IncIndent(); #endif for (int k = 1; k <= 2; k++) { childrenSpace = LearnAppend(childrenSpec, k); if (childrenSpace.HasValue) { break; } } #if DEBUG Log.DecIndent(); #endif } #if DEBUG Log.DecIndent(); #endif if (childrenSpace.HasValue && !childrenSpace.Value.IsEmpty) { return(ProgramSet.Join(Op(nameof(Semantics.Node)), labelSpace, childrenSpace.Value)); } } } // else: Inconsistent specification. return(ProgramSet.Empty(Symbol("tree"))); }