All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ensime.sexp.SexpPrinter.scala Maven / Gradle / Ivy

// Copyright: 2010 - 2018 https://github.com/ensime/ensime-server/graphs
// License: http://www.gnu.org/licenses/lgpl-3.0.en.html
package org.ensime.sexp

/**
 * Emacs flavoured lisp printer.
 */
trait SexpPrinter extends (Sexp => String) {

  /**
   * Convert the input to a `String` (constructing the entire `String`
   * in memory).
   */
  def apply(x: Sexp): String = {
    val sb = new StringBuilder
    print(x, sb)
    sb.toString()
  }

  /**
   * Convert the input to a `String` with a swank message title.
   */
  def apply(x: Sexp, swank: String, rpc: Long): String = {
    val sb = new StringBuilder
    print(x, swank, rpc, sb)
    sb.toString()
  }

  /**
   * Perform the side effect of rendering `x` to `sb`, wrapping in a
   * swank message title and RPC id.
   */
  def print(x: Sexp, swank: String, rpc: Long, sb: StringBuilder): Unit = {
    sb.append('(').append(swank).append(' ')
    print(x, sb)
    sb.append(' ').append(rpc).append(')')
  }

  /**
   * Perform the side effect of rendering `x` to `sb`, which may
   * reduce memory requirements when rendering very large objects.
   */
  def print(x: Sexp, sb: StringBuilder): Unit

  protected def printAtom(sexp: SexpAtom, sb: StringBuilder): Unit =
    sexp match {
      case SexpChar(c)   => sb.append('?').append(c)
      case SexpSymbol(s) => printSymbol(s, sb)
      case SexpString(s) => printString(s, sb)
      // will not work for Scheme
      // e.g. http://rosettacode.org/wiki/Infinity#Scheme
      case SexpNil       => sb.append("nil")
      case SexpNegInf    => sb.append("-1.0e+INF")
      case SexpPosInf    => sb.append("1.0e+INF")
      case SexpNaN       => sb.append("0.0e+NaN")
      case SexpNumber(n) =>
        // .toString and .apply does not round-trip for really big exponents
        // but PlainString can be *really* slow and eat up loads of memory
        //sb.append(n.underlying.toPlainString)
        //sb.append(n.underlying.toEngineeringString())
        sb.append(n.toString())
    }

  // we prefer not to escape some characters when in strings
  private val exclude  = Set("\n", "\t", " ")
  private val specials = SexpParser.specialChars.toList.map(_.swap)
  private val stringSpecials =
    SexpParser.specialChars.toList.map(_.swap).filterNot {
      case (from, to) => exclude(from)
    }

  protected def printSymbol(s: String, sb: StringBuilder): Unit = {
    val escaped = specials.foldLeft(s) {
      case (r, (from, to)) => r.replace(from, "\\" + to)
    }
    sb.append(escaped)
  }

  protected def printString(s: String, sb: StringBuilder): Unit = {
    val escaped = stringSpecials.foldLeft(s) {
      case (r, (from, to)) => r.replace(from, "\\" + to)
    }
    sb.append('"').append(escaped).append('"')
  }

  protected def printSeq[A](iterable: Iterable[A],
                            printSeparator: => Unit)(f: A => Unit): Unit = {
    var first = true
    iterable.foreach { a =>
      if (first) first = false else printSeparator
      f(a)
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy