blob: f11dfb72ae0f32fbc46c0f6689c6db081d3aedd8 (
plain) (
tree)
|
|
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2002-2012, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */
package scala
import language.experimental.macros
/** A class to support string interpolation.
* This class supports string interpolation as outlined in Scala SIP-11.
* It needs to be fully documented once the SIP is accepted.
*
* @param parts The parts that make up the interpolated string,
* without the expressions that get inserted by interpolation.
*/
case class StringContext(parts: String*) {
import StringContext._
/** Checks that the given arguments `args` number one less than the number
* of `parts` supplied to the enclosing `StringContext`.
* @param `args` The arguments to be checked.
* @throws An `IllegalArgumentException` if this is not the case.
*/
def checkLengths(args: Any*): Unit =
if (parts.length != args.length + 1)
throw new IllegalArgumentException("wrong number of arguments for interpolated string")
/** The simple string interpolator.
*
* It inserts its arguments between corresponding parts of the string context.
* It also treats standard escape sequences as defined in the Scala specification.
* @param `args` The arguments to be inserted into the resulting string.
* @throws An `IllegalArgumentException`
* if the number of `parts` in the enclosing `StringContext` does not exceed
* the number of arguments `arg` by exactly 1.
* @throws A `StringContext.InvalidEscapeException` if if a `parts` string contains a backslash (`\`) character
* that does not start a valid escape sequence.
*/
def s(args: Any*): String = {
checkLengths(args: _*)
val pi = parts.iterator
val ai = args.iterator
val bldr = new java.lang.StringBuilder(treatEscapes(pi.next()))
while (ai.hasNext) {
bldr append ai.next
bldr append treatEscapes(pi.next())
}
bldr.toString
}
/** The formatted string interpolator.
*
* It inserts its arguments between corresponding parts of the string context.
* It also treats standard escape sequences as defined in the Scala specification.
* Finally, if an interpolated expression is followed by a `parts` string
* that starts with a formatting specifier, the expression is formatted according to that
* specifier. All specifiers allowed in Java format strings are handled, and in the same
* way they are treated in Java.
*
* @param `args` The arguments to be inserted into the resulting string.
* @throws An `IllegalArgumentException`
* if the number of `parts` in the enclosing `StringContext` does not exceed
* the number of arguments `arg` by exactly 1.
* @throws A `StringContext.InvalidEscapeException` if a `parts` string contains a backslash (`\`) character
* that does not start a valid escape sequence.
*
* Note: The `f` method works by assembling a format string from all the `parts` strings and using
* `java.lang.String.format` to format all arguments with that format string. The format string is
* obtained by concatenating all `parts` strings, and performing two transformations:
*
* 1. Let a _formatting position_ be a start of any `parts` string except the first one.
* If a formatting position does not refer to a `%` character (which is assumed to
* start a format specifier), then the string format specifier `%s` is inserted.
*
* 2. Any `%` characters not in formatting positions are left in the resulting
* string literally. This is achieved by replacing each such occurrence by the
* format specifier `%%`.
*/
// The implementation is magically hardwired into `scala.tools.reflect.MacroImplementations.macro_StringInterpolation_f`
def f(args: Any*): String = macro ???
}
object StringContext {
/** An exception that is thrown if a string contains a backslash (`\`) character that
* that does not start a valid escape sequence.
* @param str The offending string
* @param idx The index of the offending backslash character in `str`.
*/
class InvalidEscapeException(str: String, idx: Int)
extends IllegalArgumentException("invalid escape character at index "+idx+" in \""+str+"\"")
/** Expands standard Scala escape sequences in a string.
* Escape sequences are:
* control: `\b`, `\t`, `\n`, `\f`, `\r`
* escape: `\\`, `\"`, `\'`
* octal: `\d` `\dd` `\ddd` where `d` is an octal digit between `0` and `7`.
*
* @param str A string that may contain escape sequences
* @return The string with all escape sequences expanded.
*/
def treatEscapes(str: String): String = {
lazy val bldr = new java.lang.StringBuilder
val len = str.length
var start = 0
var cur = 0
var idx = 0
def output(ch: Char) = {
bldr append str.substring (start, cur)
bldr append ch
start = idx
}
while (idx < len) {
cur = idx
if (str(idx) == '\\') {
idx += 1
if ('0' <= str(idx) && str(idx) <= '7') {
val leadch = str(idx)
var oct = leadch - '0'
idx += 1
if ('0' <= str(idx) && str(idx) <= '7') {
oct = oct * 8 + str(idx) - '0'
idx += 1
if (leadch <= '3' && '0' <= str(idx) && str(idx) <= '7') {
oct = oct * 8 + str(idx) - '0'
idx += 1
}
}
output(oct.toChar)
} else {
val ch = str(idx)
idx += 1
output {
ch match {
case 'b' => '\b'
case 't' => '\t'
case 'n' => '\n'
case 'f' => '\f'
case 'r' => '\r'
case '\"' => '\"'
case '\'' => '\''
case '\\' => '\\'
case _ => throw new InvalidEscapeException(str, cur)
}
}
}
} else {
idx += 1
}
}
if (start == 0) str
else (bldr append str.substring(start, idx)).toString
}
}
|