Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

quasiquotes (and reify) mishandle string literals #8547

Open
scabug opened this issue Apr 29, 2014 · 11 comments
Open

quasiquotes (and reify) mishandle string literals #8547

scabug opened this issue Apr 29, 2014 · 11 comments
Milestone

Comments

@scabug
Copy link

scabug commented Apr 29, 2014

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> q""" "bob's" """
res0: reflect.runtime.universe.Literal = "bob\'s"

scala> "bob's"
res1: String = bob's

I also just noticed this doesn't deconst the type argument to Expr.

reify { "bob's" }
res2: reflect.runtime.universe.Expr[String] = Expr[String("bob\'s")]("bob\'s")
@scabug
Copy link
Author

scabug commented Apr 29, 2014

Imported From: https://issues.scala-lang.org/browse/SI-8547?orig=1
Reporter: @paulp
Affected Versions: 2.11.0

@scabug
Copy link
Author

scabug commented Apr 29, 2014

@paulp said:
Or... maybe it's just printing it that way. But why is it printing it that way?

@scabug
Copy link
Author

scabug commented Apr 29, 2014

@paulp said:
Concretely, this is also a string literal:

"""bob\'s"""
res5: String = bob\'s

So when it says I have a Literal "bob's" I ought to have a literal "bob's" and that's the literal I ought to have.

@scabug
Copy link
Author

scabug commented Apr 29, 2014

@xeno-by said:

scala> val Literal(Constant(x1)) = q""" "bob's" """
x1: Any = bob's

scala> x1.asInstanceOf[String].length
res2: Int = 5

scala> val Literal(Constant(x2)) = q""" "bob\'s" """
x2: Any = bob's

scala> x2.asInstanceOf[String].length
res4: Int = 5

Looks like the former is just a repl printing bug, whereas the latter seems to be a genuine problem. Maybe that's caused by one of the string interpolation issues?

@scabug
Copy link
Author

scabug commented Apr 30, 2014

@som-snytt said:
TreePrinter adds the escapement in the OP res0:

        case Literal(x) => print(x.escapedStringValue)

Eugene's x2 is just normal parsing of the thing inside triple quotes, where normal escaping happens for normal strings. Isn't that normal?

scala> val Literal(Constant(s: String)) = q""" "bob\\'s" """   // normal escaping required for literal
s: String = bob\'s

scala> val Literal(Constant(s: String)) = q""" "\"bob's\"" """
s: String = "bob's"

scala> val Literal(Constant(s: String)) = q""""bob\\'s""""   // it's possible to ruin your eyesight
s: String = bob\'s

I was just improving messages for interactions like

s"\d".r  // expecting wrongly that \d is preserved for regex
s"abc\"def"  // expecting wrongly that quote is escaped under interpolation
                   // so s/interpolation/interpretation/

scala> s"\d".r    // real people spend hours debugging this
scala.StringContext$InvalidEscapeException: invalid escape '\d' not one of [\b, \t, \n, \f, \r, \\, \", \'] at index 0 in "\d". Use \\ for literal \.

My two thoughts were that s should be a macro to get ahead of the errors, as f can, and that it's impossible to warn in general for arbitrary interpolators, so it would be nice if non-macro interpolators could have a compile-time verify feature, because you don't know in advance what the escape policy is for the macro.

What if by convention, s"""\'""" did escapes and S"""\'""" was raw, with the mnemonic of Constants or Stable things.

@scabug
Copy link
Author

scabug commented Apr 30, 2014

@paulp said:
I'm not sure what you expect to see in the example above, but "bob's" and "bob's" should create the same string, and that's what your transcript shows. In a double-quoted string a backslash behind a single quote is a no-op.

Part of the overall difficulty is that all the quoting forms are overloaded for quasiquoting and for the literals you might be quasiquoting. How do you express a triple-quoted string literal in quasiquotes? Some of these are errors right now but shouldn't be:

"""-quasiquote containing """-literal // this may not be expressible
"""-quasiquote containing "-literal   // this does the wrong thing (injecting backslashes)
"-quasiquote containing """-literal   // this is not presently expressible but should be
"-quasiquote containing "-literal     // this does the wrong thing (parse error)

The poor design of string interpolation most likely excludes any good solution. But in a single quoted quasiquote, backslash-quote should be escaped from the quasiquote and thus act like a normal double-quote within the quasiquote context. This should be a string literal "b" not a weird error.

scala> q"\"b\""
<console>:1: error: ';' expected but string literal found.
       q"\"b\""
             ^

If I switch to reify (is there a block-syntax quasiquoting mode?) then I get the right behavior for all the quoting forms.

scala> val Literal(Constant(x1)) = (reify { """bob's""" }).tree
x1: Any = bob's

scala> val Literal(Constant(x1)) = (reify { """bob\'s""" }).tree
x1: Any = bob\'s

scala> val Literal(Constant(x1)) = (reify { "bob's" }).tree
x1: Any = bob's

scala> val Literal(Constant(x1)) = (reify { "bob\'s" }).tree
x1: Any = bob's

scala> val Literal(Constant(x1)) = (reify { "bob\\'s" }).tree
x1: Any = bob\'s

@scabug
Copy link
Author

scabug commented Apr 30, 2014

@som-snytt said:
I think your "b" example is waiting for $" in #6476. I was about to ask if I could add it under -Xfuture (next PR).

@scabug
Copy link
Author

scabug commented Apr 30, 2014

@paulp said:
Agreeing that this seems to be some kind of printing and/or repl artifact and not an issue with the representation. The appended output all looks right.

import scala.reflect.runtime.universe._

object Test {
  def main(args: Array[String]): Unit = {
    println(q""" "bob's" """.value)
    println(q""" "bob\'s" """.value)
    println(q""" "bob\\'s" """.value)
    println(q""" "bob\\\'s" """.value)

    println(q""" "\n" """.value)
    println(q""" "\\n" """.value)
    println(q""" "\\\n" """.value)
    println(q""" "\\\\n" """.value)
  }
}


Constant(bob's)
Constant(bob's)
Constant(bob\'s)
Constant(bob\'s)
Constant(
)
Constant(\n)
Constant(\
)
Constant(\\n)

@scabug
Copy link
Author

scabug commented Apr 30, 2014

@paulp said:
The really important/obvious lesson, too late, is that when nesting may occur, open and close delimiters must be distinguishable. Everyone figures this out eventually, like bash going from backticks to $( and )$ to enclose subshells.

The requirement that many different tasks be accomplished via string interpolation yet with no way to nest strings indicates foresight comparable to that shown by the US during the invasion of iraq. "Freedom is untidy," we heard after that one.

@scabug
Copy link
Author

scabug commented Apr 30, 2014

@paulp said:
It's especially ironic that scala would blow this given that it's one of the only languages where you can nest block comments.

@scabug
Copy link
Author

scabug commented Apr 30, 2014

@som-snytt said:
Weren't guillemets coming to SIP-24?

It's not a question of if guillemets are coming, but what feature they will serve when they arrive.

scala>"bob's"»
res0: String = "bob's"

It will depend on Martin's keyboard.

@scabug scabug added this to the Backlog milestone Apr 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants