LoginSignup
0
0

More than 5 years have passed since last update.

Scalaのソースコードを読んでみる その3

Last updated at Posted at 2013-07-08

src/compiler/scala/tools/nsc/Global.scala
をじっくり読もうかと思ったのですが、24、25行目の
import文がちょっと気になりました。

Global.scala
import ast._
import ast.parser._

ast といえば、abstract syntax treeの略でしょう。
しかも、parserとくれば、この辺が構文解析すくなくとも抽象構文木に関わっているのは間違いありません。

ということで、次の標的は
src/compiler/scala/tools/nsc/ast/parserディレクトリです。

Prasers.scala やら Scanners.scala, Tokens.scala, TreeBuilder.scalaなど
それっぽいファイルがありました。
この辺を読んでいきましょう。

まずは、Tokens.scalaから。

Tokens.scala
abstract class Tokens {
  /** special tokens */
  final val EMPTY = -3
  final val UNDEF = -2
  final val ERROR = -1
  final val EOF = 0

 (以下略

定数の宣言だけなのかな。これはあれですね。字句解析結果のトークンを表す定数を定義しているだけのようです。
というか、lexみたいな字句解析器は使ってなさそうですね…。

次は、Scanners.scalaです。
ファイル名から考えて、ここでトークンを切り出してると思われます。
ですので、さきほどのTokens.scalaで定義されている定数をファイル中から
検索してみましょう。

例えば、"EQUALS"などで検索すると、575行目あたりの

Scanners.scala
    /** Can token start a statement? */
    def inFirstOfStat(token: Int) = token match {
      case EOF | CATCH | ELSE | EXTENDS | FINALLY | FORSOME | MATCH | WITH | YIELD |
           COMMA | SEMI | NEWLINE | NEWLINES | DOT | COLON | EQUALS | ARROW | LARROW |
           SUBTYPE | VIEWBOUND | SUPERTYPE | HASH | RPAREN | RBRACKET | RBRACE | LBRACKET =>
        false
      case _ =>
        true
    }

が見つかります。
inFirstOfStatというメソッドは、すぐ下にある
inLastOfStatというメソッドとペアだと思われます。

とりあえず、この2つのメソッドを頭にいれといて、さらに
"EQUALS"を探すと、1169行目あたりの

Scanner.scala
  // ------------- keyword configuration -----------------------------------

  private val allKeywords = List[(Name, Int)](
    nme.ABSTRACTkw  -> ABSTRACT,
    nme.CASEkw      -> CASE,
    ・・・(中略)・・・
    nme.EQUALSkw    -> EQUALS,

がみつかります。

このallKeywordsというリストは、名前通り「すべてのキーワードのリスト」と考えていいでしょう。

もし、Scalaの文法を変更するにあたり、キーワードを追加したいときには、ここに足してやればよさそうです。
nme.ほにゃらら、が何を表しているのかは調べておく必要がありそうですが…。

nme.ほにゃらら ですが、これは
src/reflect/scala/reflect/internal/StdNames.scala
の中のclass Keywordsで定義されているものみたいです。

字句解析を変更するには

ここまでをまとめておきます。

もし、字句解析器に変更を加えたいときには、

  1. src/compiler/scala/tools/nsc/ast/parser/Tokens.scalaにキーワード用の定数を追加する。

  2. src/compiler/scala/tools/nsc/ast/parser/Scanner.scalaのallKeywordsというリストに1で定義した定数を追加する。

  3. メソッドinFirstOfStat, inLastOfStat に必要に応じて、1で定義した定数を追加してやる。

  4. src/reflect/scala/reflect/internal/StdNames.scala のclass Keywordsに追加したいキーワードを定義しておく。具体的なコードは以下。

StdNames.scala
    final val HOGEHOGEkw: TermName       = kw("hogehoge")

3に関しては、似たような働きをするトークンと同じようにしてやればいいと思われます。まあ、inFirstOfStatは「文の最初にくることが可能なトークンかどうか」で判断して、inLastOfStatは「文の最後にくることが可能なトークンかどうか」で判断すればよさそうです。
ここで注意する点は、inFirstOfStatだと

Scanner.scala
    def inFirstOfStat(token: Int) = token match {
      case EOF | CATCH | ELSE | EXTENDS | FINALLY | FORSOME | MATCH | WITH | YIELD |
           COMMA | SEMI | NEWLINE | NEWLINES | DOT | COLON | EQUALS | ARROW | LARROW |
           SUBTYPE | VIEWBOUND | SUPERTYPE | HASH | RPAREN | RBRACKET | RBRACE | LBRACKET =>
        false
      case _ =>
        true
    }

のように、case EOF | … のほうは、falseを返すというところでしょうか。つまり、この場合だと、「EOFは文の最初にくることが出来ない」という意味になっています。

補足:nme.ほにゃらら

nme.ほにゃらら関係。

・nmeという変数が宣言されているファイルは
 src/reflect/scala/reflect/api/StandardNames.scala

・nmeの型:TermNamesApiに関係すると思われるファイルは
 src/reflect/scala/reflect/api/Names.scala

(つづく)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0