问题 以编程方式检查字符串是否是Scala中的保留字


有没有一种简单的方法来实现以下功能?

即,在生成代码时判断给定的文本是否需要用反引号引用。

def isReservedWord(text: String): Boolean

isReservedWord("type") // true
isReservedWord("foo")  // false

当然,我可以根据语言规范末尾的Scala语法摘要维护一个关键字列表,并检查一下,但是有更好的方法吗?


6081
2018-02-26 09:43


起源



答案:


编译器维护一个可以轻松访问的关键字列表:

scala> import scala.tools.nsc._
import scala.tools.nsc._

scala> val compiler = new Global(new Settings)
compiler: scala.tools.nsc.Global = scala.tools.nsc.Global@29935953

scala> compiler.nme.keywords
res0: Set[compiler.TermName] = Set(abstract, >:, true, val, do, throw, <-, package, _, macro, @, object, false, this, if, then, var, trait, ., catch, with, def, else, class, type, #, lazy, null, =, <:, override, protected, =>, private, sealed, finally, new, implicit, extends, final, for, return, case, import, forSome, :, super, while, yield, try, match, <%)

scala> compiler.javanme.keywords
res1: Set[compiler.TermName] = Set(abstract, strictfp, short, int, do, goto, interface, throw, float, package, implements, enum, this, long, if, switch, native, throws, boolean, catch, else, const, class, assert, public, void, instanceof, protected, static, default, private, finally, synchronized, new, char, extends, final, volatile, for, return, continue, case, import, double, super, byte, while, break, try, transient)

幸运的是,Scala已经提供了反射API,它只不过是公共API访问的编译器。将公共类型转换为内部类型时,可以访问包含所有定义的符号表:

scala> val st = scala.reflect.runtime.universe.asInstanceOf[scala.reflect.internal.SymbolTable]
st: scala.reflect.internal.SymbolTable = scala.reflect.runtime.JavaUniverse@472250c4

scala> st.nme.keywords
res10: Set[st.TermName] = Set(abstract, >:, true, val, do, throw, <-, package, _, macro, @, object, false, this, if, then, var, trait, ., catch, with, def, else, class, type, #, lazy, null, =, <:, override, protected, =>, private, sealed, finally, new, implicit, extends, final, for, return, case, import, forSome, :, super, while, yield, try, match, <%)

在REPL里面你也可以使用 :power 模式直接访问编译器:

scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'.          **
** scala.tools.nsc._ has been imported      **
** global._, definitions._ also imported    **
** Try  :help, :vals, power.<tab>           **

scala> nme.keywords
res3: Set[$r.intp.global.TermName] = Set(abstract, >:, true, val, do, throw, <-, package, _, macro, @, object, false, this, if, then, var, trait, ., catch, with, def, else, class, type, #, lazy, null, =, <:, override, protected, =>, private, sealed, finally, new, implicit, extends, final, for, return, case, import, forSome, :, super, while, yield, try, match, <%)

scala> javanme.keywords
res4: Set[$r.intp.global.TermName] = Set(abstract, strictfp, short, int, do, goto, interface, throw, float, package, implements, enum, this, long, if, switch, native, throws, boolean, catch, else, const, class, assert, public, void, instanceof, protected, static, default, private, finally, synchronized, new, char, extends, final, volatile, for, return, continue, case, import, double, super, byte, while, break, try, transient)

10
2018-02-26 10:24



谢谢!那很完美。 - Chris B


答案:


编译器维护一个可以轻松访问的关键字列表:

scala> import scala.tools.nsc._
import scala.tools.nsc._

scala> val compiler = new Global(new Settings)
compiler: scala.tools.nsc.Global = scala.tools.nsc.Global@29935953

scala> compiler.nme.keywords
res0: Set[compiler.TermName] = Set(abstract, >:, true, val, do, throw, <-, package, _, macro, @, object, false, this, if, then, var, trait, ., catch, with, def, else, class, type, #, lazy, null, =, <:, override, protected, =>, private, sealed, finally, new, implicit, extends, final, for, return, case, import, forSome, :, super, while, yield, try, match, <%)

scala> compiler.javanme.keywords
res1: Set[compiler.TermName] = Set(abstract, strictfp, short, int, do, goto, interface, throw, float, package, implements, enum, this, long, if, switch, native, throws, boolean, catch, else, const, class, assert, public, void, instanceof, protected, static, default, private, finally, synchronized, new, char, extends, final, volatile, for, return, continue, case, import, double, super, byte, while, break, try, transient)

幸运的是,Scala已经提供了反射API,它只不过是公共API访问的编译器。将公共类型转换为内部类型时,可以访问包含所有定义的符号表:

scala> val st = scala.reflect.runtime.universe.asInstanceOf[scala.reflect.internal.SymbolTable]
st: scala.reflect.internal.SymbolTable = scala.reflect.runtime.JavaUniverse@472250c4

scala> st.nme.keywords
res10: Set[st.TermName] = Set(abstract, >:, true, val, do, throw, <-, package, _, macro, @, object, false, this, if, then, var, trait, ., catch, with, def, else, class, type, #, lazy, null, =, <:, override, protected, =>, private, sealed, finally, new, implicit, extends, final, for, return, case, import, forSome, :, super, while, yield, try, match, <%)

在REPL里面你也可以使用 :power 模式直接访问编译器:

scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'.          **
** scala.tools.nsc._ has been imported      **
** global._, definitions._ also imported    **
** Try  :help, :vals, power.<tab>           **

scala> nme.keywords
res3: Set[$r.intp.global.TermName] = Set(abstract, >:, true, val, do, throw, <-, package, _, macro, @, object, false, this, if, then, var, trait, ., catch, with, def, else, class, type, #, lazy, null, =, <:, override, protected, =>, private, sealed, finally, new, implicit, extends, final, for, return, case, import, forSome, :, super, while, yield, try, match, <%)

scala> javanme.keywords
res4: Set[$r.intp.global.TermName] = Set(abstract, strictfp, short, int, do, goto, interface, throw, float, package, implements, enum, this, long, if, switch, native, throws, boolean, catch, else, const, class, assert, public, void, instanceof, protected, static, default, private, finally, synchronized, new, char, extends, final, volatile, for, return, continue, case, import, double, super, byte, while, break, try, transient)

10
2018-02-26 10:24



谢谢!那很完美。 - Chris B


在编译器源代码中,我发现了以下内容:

scala.tools.nsc.doc.html.SyntaxHigh.reserved

这是包私有的 html,所以你可能要编写一个包装器。可能只是将该数组复制到您自己的源代码更容易。

内部API中有更多内容,例如 scala.reflect.internal.StdNames。和 scala.reflect.internal.Printers 有一个方法 quotedName,但你需要整个蛋糕来访问这些。也许你可以通过官方反映API获得任何这些?


1
2018-02-26 10:05