@@ -20,6 +20,8 @@ import cc.*
2020import CaptureSet .Mutability
2121import Capabilities .*
2222
23+ import java .lang .StringBuilder
24+
2325class PlainPrinter (_ctx : Context ) extends Printer {
2426
2527 /** The context of all public methods in Printer and subclasses.
@@ -694,22 +696,18 @@ class PlainPrinter(_ctx: Context) extends Printer {
694696
695697 def toText (denot : Denotation ): Text = toText(denot.symbol) ~ " /D"
696698
697- private def escapedChar (ch : Char ): String = (ch : @ switch) match {
698- case '\b ' => " \\ b"
699- case '\t ' => " \\ t"
700- case '\n ' => " \\ n"
701- case '\f ' => " \\ f"
702- case '\r ' => " \\ r"
703- case '"' => " \\\" "
704- case '\' ' => " \\\' "
705- case '\\ ' => " \\\\ "
706- case _ => if ch.isControl then f " ${" \\ " }u ${ch.toInt}%04x " else String .valueOf(ch)
707- }
699+ private def escapedChar (ch : Char ): String =
700+ if requiresFormat(ch) then
701+ val b = StringBuilder ().append('\' ' )
702+ escapedChar(b, ch)
703+ b.append('\' ' ).toString
704+ else
705+ " '" + ch + " '"
708706
709707 def toText (const : Constant ): Text = const.tag match {
710- case StringTag => stringText(" \" " + escapedString(const.value.toString) + " \" " )
708+ case StringTag => stringText(escapedString(const.value.toString, quoted = true ) )
711709 case ClazzTag => " classOf[" ~ toText(const.typeValue) ~ " ]"
712- case CharTag => literalText(s " ' ${ escapedChar(const.charValue)} ' " )
710+ case CharTag => literalText(escapedChar(const.charValue))
713711 case LongTag => literalText(const.longValue.toString + " L" )
714712 case DoubleTag => literalText(const.doubleValue.toString + " d" )
715713 case FloatTag => literalText(const.floatValue.toString + " f" )
@@ -731,7 +729,57 @@ class PlainPrinter(_ctx: Context) extends Printer {
731729 ~ (if param.isTypeParam then " " else " : " )
732730 ~ toText(param.paramInfo)
733731
734- protected def escapedString (str : String ): String = str flatMap escapedChar
732+ protected final def escapedString (str : String ): String = escapedString(str, quoted = false )
733+
734+ private def requiresFormat (c : Char ): Boolean = (c : @ switch) match
735+ case '\b ' | '\t ' | '\n ' | '\f ' | '\r ' | '"' | '\' ' | '\\ ' => true
736+ case c => c.isControl
737+
738+ private def escapedString (text : String , quoted : Boolean ): String =
739+ def mustBuild : Boolean =
740+ var i = 0
741+ while i < text.length do
742+ if requiresFormat(text.charAt(i)) then return true
743+ i += 1
744+ false
745+ if mustBuild then
746+ val b = StringBuilder (text.length + 16 )
747+ if quoted then
748+ b.append('"' )
749+ var i = 0
750+ while i < text.length do
751+ escapedChar(b, text.charAt(i))
752+ i += 1
753+ if quoted then
754+ b.append('"' )
755+ b.toString
756+ else if quoted then " \" " + text + " \" "
757+ else text
758+
759+ private def escapedChar (b : StringBuilder , c : Char ): Unit =
760+ def quadNibble (b : StringBuilder , x : Int , i : Int ): Unit =
761+ if i < 4 then
762+ quadNibble(b, x >> 4 , i + 1 )
763+ val n = x & 0xF
764+ val c = if (n < 10 ) '0' + n else 'a' + (n - 10 )
765+ b.append(c.toChar)
766+ val replace = (c : @ switch) match
767+ case '\b ' => " \\ b"
768+ case '\t ' => " \\ t"
769+ case '\n ' => " \\ n"
770+ case '\f ' => " \\ f"
771+ case '\r ' => " \\ r"
772+ case '"' => " \\\" "
773+ case '\' ' => " \\\' "
774+ case '\\ ' => " \\\\ "
775+ case c =>
776+ if c.isControl then
777+ b.append(" \\ u" )
778+ quadNibble(b, c.toInt, 0 )
779+ else
780+ b.append(c)
781+ return
782+ b.append(replace)
735783
736784 def dclsText (syms : List [Symbol ], sep : String ): Text = Text (syms map dclText, sep)
737785
0 commit comments