2013-09-10 3 views
5

Sto cercando di ottenere la stringa di input originale di un parametro su una macro, ma la posizione restituita sembra un po 'spenta. Considerate questa macro, ad esempio:Che succede con la posizione sui macro Scala?

object M { 
    import scala.reflect.macros.Context 
    import language.experimental.macros 
    def f[T](v: => T) = macro fImpl[T] 
    def fImpl[T : c.WeakTypeTag](c: Context)(v: c.Expr[T]): c.Expr[Unit] = { 
    import c.universe._ 
    val pos = v.tree.pos 
    println(pos.lineContent) 
    println(" " * pos.column + "^") 
    println(" " * pos.point + "^") 
    c.literalUnit 
    } 
} 

Quando provo con questo file:

object N extends App { 
    val x = 1 
    val y = 2 
    println(M.f(x + y)) 
} 

ottengo questo output:

println(M.f(x + y)) 
       ^
                   ^

che non ha senso per me. Mi aspetterei che indicasse x o che venisse disattivato di uno. Cosa succede con quello?

+0

Se si modifica a 'println (Mf (x. + (Y))) 'il punto si sposta su uno per puntare su ye con' println (Mf (if (true) {x + y})) 'punta alla f in if. Sospetto che stia cercando di indicare la cima dell'albero, l'invocazione di + o il se, anche se è supposta o no è un'altra domanda. Potrebbe esserci almeno un errore off-by. Questo mi sembra appropriato per un bug report. – wingedsubmariner

+0

Sarebbe più divertente se la domanda venisse letta, qual è la tua posizione sulle macro? –

risposta

3

È un errore di tipo "off-by-one" nel senso che Position.column e Position.line sono a base 1.

Si tratta di un errore di documentazione nel senso che si sono presi la briga di documentare l'API ma non si sono preoccupati di menzionarlo.

È possibile compilare con -Yrangepos e:

val n = pos.column - (pos.point - pos.startOrPoint) - 1 
println(" " * n + "^") 

o simili per indicare la prima posizione nella struttura.

println(M.f(x + y)) 
      ^

Aggiornamento:

Lasciando il ritorno macro l'espressione è dato, e la compilazione con -Xprint:typer -Yshow-trees, l'albero è l'interno Apply nodo, che è posizionata al +:

 Apply(// def println(x: Any): Unit in object Predef, tree.tpe=Unit 
     scala.this."Predef"."println" // def println(x: Any): Unit in object Predef, tree.tpe=(x: Any)Unit 
     Apply(// def +(x: Int): Int in class Int, tree.tpe=Int 
      "x"."$plus" // def +(x: Int): Int in class Int, tree.tpe=(x: Int)Int 
      "y" // val y: Int, tree.tpe=Int 
     ) 
    ) 

Con una posizione "range", la posizione della parte superiore dell'albero include tutto ciò che si trova al di sotto di esso. Quindi, mentre point è dove + è, la start di una posizione di intervallo è la prima posizione di tutto ciò che è racchiuso dalla posizione di intervallo, cioè tutto più in basso nell'albero. In questo caso, l'angolo sinistro è x.

Quindi la differenza point - start indica la distanza di backup.

(non ho considerato che cosa se gli offset nel file sorgente sono diverse da offset di colonna a causa delle differenze di codifica dei caratteri.)

+0

Ma _what_ è 'colonna' che punta a? E qual è il significato di 'pos.point - pos.startOrPoint' (entrambi per me sono stati privi di significato)? –

+0

@ DanielC.Sobral So solo abbastanza sulle posizioni di intervallo per aver introdotto un bug una volta che le persone IDE dovevano risolvere. Almeno stavo riparando un bug, quindi era un lavaggio. Inoltre, pensavo che Yrangepos sarebbe diventato il default? Aggiornato con le mie conoscenze limitate. –

+0

@ DanielC.Sobral Ci sono commenti migliorati (e codice) su https://github.com/scala/scala/pull/2936 –