internal static InstructAST Build(InstructAST left, InstructAST right) { // a && a == a if (left.Equals(right)) return left; // <empty> && a == a && <empty> == a if zero-width, <fail> otherwise if (left == EmptyAST.Instance) return right.IsZeroWidth ? right : AnyCharAST.Negate; if (right == EmptyAST.Instance) return left.IsZeroWidth ? left : AnyCharAST.Negate; // When a contains b, a && b == b if (left.Contains(right)) return right; // When b contains a, a && b == a if (right.Contains(left)) return left; // When a and b are disjoint, a && b matches nowhere if (left.DisjointWith(right)) return AnyCharAST.Negate; // !a && !b == !(a || b), !a && b == b -- a, a && !b == a -- b NotTestAST negLeft = left as NotTestAST; NotTestAST negRight = right as NotTestAST; if (negLeft != null) { if (negRight != null) return NotTestAST.Make(OrTestAST.Make(negLeft.Argument, negRight.Argument)); else return DiffTestAST.Make(right, negLeft.Argument); } else if (negRight != null) return DiffTestAST.Make(left, negRight.Argument); // (a && b) && c == a && (b && c) AndTestAST andRight = right as AndTestAST; if (andRight != null) return AndTestAST.Make(AndTestAST.Make(left, andRight.Left), andRight.Right); // (a || b) && c == (a && c) || (b && c) OrTestAST orLeft = left as OrTestAST; if (orLeft != null) return OrTestAST.Make(AndTestAST.Make(orLeft.Left, right), AndTestAST.Make(orLeft.Right, right)); // a && (b || c) == (a && b) || (a && c) if (!left.IsZeroWidth) { OrTestAST orRight = right as OrTestAST; if (orRight != null) return OrTestAST.Make(AndTestAST.Make(left, orRight.Left), AndTestAST.Make(left, orRight.Right)); } // Intersection of overlapping ranges is the overlap CharRangeAST leftRange = left as CharRangeAST; CharRangeAST rightRange = right as CharRangeAST; if (leftRange != null && rightRange != null && (leftRange.FoldsCase == rightRange.FoldsCase)) { int low = Math.Max(leftRange.Min, rightRange.Min); int high = Math.Min(leftRange.Max, rightRange.Max); return CharRangeAST.Make(low, high, leftRange.FoldsCase); } return new AndTestAST(left, right); }
internal static InstructAST Build(InstructAST left, InstructAST right) { // a -- a == <fail> if (left.Equals(right)) return AnyCharAST.Negate; // When b contains a, a -- b matches no codepoints if (right.Contains(left)) return AnyCharAST.Negate; // When a and b are disjoint, a -- b == a if (left.DisjointWith(right)) return left; // \p{Any} -- a == !a if (AnyCharAST.Instance.Equals(left)) return NotTestAST.Make(right); // !a -- !b == b -- a; !a -- b == !(a || b); a -- !b == a && b NotTestAST negLeft = left as NotTestAST; NotTestAST negRight = right as NotTestAST; if (negLeft != null) { if (negRight != null) return DiffTestAST.Make(negRight.Argument, negLeft.Argument); else return NotTestAST.Make(OrTestAST.Make(negLeft.Argument, right)); } else if (negRight != null) return AndTestAST.Make(left, negRight.Argument); // (a -- b) -- c == a -- (b || c) DiffTestAST diffLeft = left as DiffTestAST; if (diffLeft != null) return DiffTestAST.Make(diffLeft.Left, OrTestAST.Make(diffLeft.Right, right)); // (a || b) -- c == (a -- c) || (b -- c) OrTestAST orLeft = left as OrTestAST; if (orLeft != null) return OrTestAST.Make(DiffTestAST.Make(orLeft.Left, right), DiffTestAST.Make(orLeft.Right, right)); // When a and b are overlapping ranges, a -- b == a with a gap CharRangeAST leftRange = left as CharRangeAST; if (leftRange != null) { int excludeMin = -1; int excludeMax = -1; OneCharAST rightChar = right as OneCharAST; if (rightChar != null && (leftRange.FoldsCase == rightChar.FoldsCase)) { excludeMin = excludeMax = rightChar.Character; } CharRangeAST rightRange = right as CharRangeAST; if (rightRange != null && (leftRange.FoldsCase == rightRange.FoldsCase)) { excludeMin = rightRange.Min; excludeMax = rightRange.Max; } if (leftRange.Min <= excludeMax && excludeMin <= leftRange.Max) return OrTestAST.Make(CharRangeAST.Make(leftRange.Min, excludeMin - 1, leftRange.FoldsCase), CharRangeAST.Make(excludeMax + 1, leftRange.Max, leftRange.FoldsCase)); } return new DiffTestAST(left, right); }