   /// caller, but we might want to be parameterized at that point.
   ///
   /// TODO: Some way to integrate this with conversion...
-  var _patternBase: String {
+  var _patternBase: (String, canBeWrapped: Bool) {
     if let anchor = self.assertionKind {
-      return anchor._patternBase
+      return (anchor._patternBase, false)
     }
 
     if isUnprintableAtom {
-      return _regexBase
+      return (_regexBase, false)
     }
     
     return _dslBase
   }
   
-  var _dslBase: String {
+  var _dslBase: (String, canBeWrapped: Bool) {
     func scalarLiteral(_ s: UnicodeScalar) -> String {
       let hex = String(s.value, radix: 16, uppercase: true)
       return "\\u{\(hex)}"
     }
     switch kind {
     case let .char(c):
-      return String(c)
+      return (String(c), false)
 
     case let .scalar(s):
-      return scalarLiteral(s.value)
+      return (scalarLiteral(s.value), false)
 
     case let .scalarSequence(seq):
-      return seq.scalarValues.map(scalarLiteral).joined()
+      return (seq.scalarValues.map(scalarLiteral).joined(), false)
 
     case let .property(p):
-      return p._dslBase
+      return (p._dslBase, true)
       
     case let .escaped(e):
       switch e {
       // Anchors
       case .wordBoundary:
-        return "Anchor.wordBoundary"
+        return ("Anchor.wordBoundary", false)
       case .notWordBoundary:
-        return "Anchor.wordBoundary.inverted"
+        return ("Anchor.wordBoundary.inverted", false)
       case .startOfSubject:
-        return "Anchor.startOfSubject"
+        return ("Anchor.startOfSubject", false)
       case .endOfSubject:
-        return "Anchor.endOfSubject"
+        return ("Anchor.endOfSubject", false)
       case .endOfSubjectBeforeNewline:
-        return "Anchor.endOfSubjectBeforeNewline"
+        return ("Anchor.endOfSubjectBeforeNewline", false)
       case .firstMatchingPositionInSubject:
-        return "Anchor.firstMatchingPositionInSubject"
+        return ("Anchor.firstMatchingPositionInSubject", false)
       case .textSegment:
-        return "Anchor.textSegmentBoundary"
+        return ("Anchor.textSegmentBoundary", false)
       case .notTextSegment:
-        return "Anchor.textSegmentBoundary.inverted"
+        return ("Anchor.textSegmentBoundary.inverted", false)
         
       // Character Classes
       case .decimalDigit:
-        return ".digit"
+        return (".digit", true)
       case .notDecimalDigit:
-        return ".digit.inverted"
+        return (".digit.inverted", true)
       case .horizontalWhitespace:
-        return ".horizontalWhitespace"
+        return (".horizontalWhitespace", true)
       case .notHorizontalWhitespace:
-        return ".horizontalWhitespace.inverted"
+        return (".horizontalWhitespace.inverted", true)
       case .whitespace:
-        return ".whitespace"
+        return (".whitespace", true)
       case .notWhitespace:
-        return ".whitespace.inverted"
+        return (".whitespace.inverted", true)
       case .wordCharacter:
-        return ".word"
+        return (".word", true)
       case .notWordCharacter:
-        return ".word.inverted"
+        return (".word.inverted", true)
       case .graphemeCluster:
-        return ".anyGraphemeCluster"
+        return (".anyGraphemeCluster", true)
       case .newlineSequence:
-        return ".newlineSequence"
+        return (".newlineSequence", true)
       case .notNewline:
-        return ".newlineSequence.inverted"
+        return (".newlineSequence.inverted", true)
       case .verticalTab:
-        return ".verticalWhitespace"
+        return (".verticalWhitespace", true)
       case .notVerticalTab:
-        return ".verticalWhitespace.inverted"
+        return (".verticalWhitespace.inverted", true)
         
       // Literal single characters all get converted into DSLTree.Atom.scalar
         
       default:
-        return "TODO: escaped \(e)"
+        return ("TODO: escaped \(e)", false)
       }
       
     case .namedCharacter:
-      return " /* TODO: named character */"
+      return (" /* TODO: named character */", false)
 
     case .any:
-      return ".any"
+      return (".any", true)
 
     case .startOfLine, .endOfLine:
       fatalError("unreachable")
 
     case .backreference:
-      return " /* TODO: back reference */"
+      return (" /* TODO: back reference */", false)
 
     case .subpattern:
-      return " /* TODO: subpattern */"
+      return (" /* TODO: subpattern */", false)
 
     case .callout:
-      return " /* TODO: callout */"
+      return (" /* TODO: callout */", false)
 
     case .backtrackingDirective:
-      return " /* TODO: backtracking directive */"
+      return (" /* TODO: backtracking directive */", false)
 
     case .changeMatchingOptions:
-      return "/* TODO: change matching options */"
+      return ("/* TODO: change matching options */", false)
       
     // Every other case we've already decided cannot be represented inside the
     // DSL.
     default:
-      return ""
+      return ("", false)
     }
   }
   

      }
      
      for (i, member) in nonCharMembers.enumerated() {
        printer.printAsPattern(member)
        
        if i != nonCharMembers.count - 1 {
          printer.output(",")

      }
    }
    
    indent()
    output(")")
    
    if terminateLine {
      self.terminateLine()
    }
  }

  // TODO: Some way to integrate this with conversion...
  mutating func printAsPattern(
    _ member: DSLTree.CustomCharacterClass.Member,
    wrap: Bool = true
  ) {
    switch member {
    case let .custom(ccc):
