ANTLRとPythonを利用してJavaのソースコードをAST（Abstract Syntax Tree：抽象構文木）で構文解析する方法

## 1. はじめに

今回はANTLRとPythonを利用してJavaのソースコードをAST（Abstract Syntax Tree：抽象構文木）で構文解析する方法について説明したいと思います。
なぜPythonなのかといえば、Pythonの方が手軽で最近はPythonばかりイジっているのと、Javaでは以前にやっているので今回はPythonでという感じです。なお、Javaの要望があればいずれQiitaの記事にしたいと思います。
今回の記事のゴールは以下の３点です。

* AST（抽象構文木）による静的解析の概要を理解する
* ANTLRで文法ファイルから解析器を自動生成する方法を理解する
* ANTLRとPythonを利用してJavaの独自の構文解析器を実装する

![overviewAstForJavaByAntlr.jpg](https://qiita-image-store.s3.amazonaws.com/0/195748/fd5874ad-fa16-2f07-2215-413348afe84f.jpeg)

### 1.1. AST（Abstract Syntax Tree：抽象構文木）とは

[wikipediaにおける抽象構文木](https://ja.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E6%A7%8B%E6%96%87%E6%9C%A8)の説明（一部）は以下の通りです。

> 抽象構文木（英: abstract syntax tree、AST）とは、通常の構文木（具象構文木あるいは解析木とも言う）から、言語の意味に関係ない情報を取り除き、意味に関係ある情報のみを取り出した（抽象した）木構造のデータ構造である。 

[Googleイメージで抽象構文木](https://www.google.com/search?client=firefox-b&biw=1314&bih=959&tbm=isch&sa=1&ei=ATyOW9_CN8it8QXrhIGwCQ&q=%E6%8A%BD%E8%B1%A1%E6%A7%8B%E6%96%87%E6%9C%A8&oq=%E6%8A%BD%E8%B1%A1%E6%A7%8B%E6%96%87%E6%9C%A8&gs_l=img.3..0j0i24k1l4.4434.5025.0.5921.5.5.0.0.0.0.78.375.5.5.0....0...1c.1.64.img..0.2.152....0.6gcyAKZr7NM)を検索すると何となくイメージが掴めるかと思います。が、やはり難しいですね。

簡単に言うと以下の３点かと思います。

* 解析対象のプログラミング言語の文法に応じたソースコード解析ができる
* 通常のテキスト解析（Grep、正規表現等）ではなく、構造に即したソースコード解析ができる
* 解析結果をプログラミングで馴染みのある木構造で管理し、該当のデータにアクセスするための便利なAPIが用意されている

### 1.2. ANTLRとは

ANTLR（ http://www.antlr.org/ ）とは、文法ファイル（g4ファイル）を入力として、抽象構文木の解析で必要となる機能、いわゆる解析器を自動生成するツールです。
文法ファイルは解析器のプログラミング言語に非依存のため、複数のプログラミング言語に応じた解析器を生成することが可能です。
今回はPython用の解析器のコードを生成することにします。

解析器の主要なコンポーネントを以下に示します。

#### Lexer　：　字句解析

[wikipediaにおける字句解析](https://ja.wikipedia.org/wiki/%E5%AD%97%E5%8F%A5%E8%A7%A3%E6%9E%90)の説明（一部）は以下の通りです。

> 字句解析 (じくかいせき、英: Lexical Analysis) とは、広義の構文解析の前半の処理で、自然言語の文やプログラミング言語のソースコードなどの文字列を解析して、後半の狭義の構文解析で最小単位（終端記号）となっている「トークン」（字句）の並びを得る手続きである。字句解析を行うプログラムは字句解析器である。自然言語の字句解析については形態素解析を参照。 

難しいですね。
簡単に言えば、プログラミング言語の仕様として利用可能な文字列で書かれているかをチェックする処理です。
例えばPythonでは`from`、`def`、`elif`等々という単語が構文として利用可能ですが、Javaではこれらの単語は構文として使えません。この処理を行うのがLexerです。
Lexerは文法ファイルの仕様に従うため、基本的にANTLRが生成したLexerをそのまま利用します。

#### Parser　：　構文解析

[wikipediaにおける構文解析](https://ja.wikipedia.org/wiki/%E6%A7%8B%E6%96%87%E8%A7%A3%E6%9E%90)の説明（一部）は以下の通りです。

> 構文解析（こうぶんかいせき、syntactic analysis あるいは parse）とは、文章、具体的にはマークアップなどの注記の入っていないベタの文字列を、自然言語であれば形態素に切分け、さらにその間の関連（修飾-被修飾など）といったような、統語論的（構文論的）な関係を図式化するなどして明確にする（解析する）手続きである。自然言語については自然言語処理における要点のひとつであり、プログラミング言語など形式言語の場合は、形式文法に従い構文木を得る。構文解析を行う機構を構文解析器（parser）と呼ぶ。 

難しいですね。
『構文解析』の文言が示す通り、解析対象のテキストを読み込んで構文木に分解する処理です。なんとなくみなさんがイメージするその通りの処理です。
Parserは文法ファイルの仕様に従うため、基本的にANTLRが生成したParserをそのまま利用します。

#### Listener　：　イベントリスナ

Lexer、Parserは自然言語処理で登場するメイン処理ですが、Listenerはユーザが独自の解析器を作るためのAPIです。
どのようなAPIかというと、構文木のノードが切り替わるタイミングで発生するイベントのフックポイントです。
今回の解析対象であるJavaを例にするとclass定義の文脈に入りました＆文脈が終わりました、メソッド定義の文脈に入りました＆文脈が終わりました等々です。
ANTLRが自動生成したListenerはデフォルト実装されているだけなので、LexerやParserと異なり、生成されたListenerを拡張（継承）して利用します。

## 2. ANTLRで解析器を自動生成する

### 2.1. ANTLRの取得

ANTLRの解析器を生成する機能はJavaのプログラムとして提供されています。
http://www.antlr.org/download/antlr-runtime-4.7.1.jar を取得して任意のディレクトリに格納してください。
JDKがインストールされていない方はインストールをしてください。

### 2.2. 文法ファイル（g4）の取得

プログラミング言語に応じた文法ファイル（g4）がANTLRのGitHubページで公開されています。
Javaはバージョン毎に文法ファイルが異なります。今回はJava8用の文法ファイルを https://github.com/antlr/grammars-v4/tree/master/java から取得します。
取得したファイル（`JavaLexer.g4`、`JavaParser.g4`）を任意のディレクトリ（例：grammar）に格納してください。


```console:生成前
.
│  antlr-4.7.1-complete.jar
│
└─grammar
        JavaLexer.g4
        JavaParser.g4
```

### 2.3. Lexer、Parser、デフォルトListenerの生成

ANTLRでPython3用のLexer、Parser、Listenerを生成するコマンドを以下に示します。
`-Dlanguage`オプションで解析器のプログラミング言語を指定します。
Pythonの場合、Python3とPython2で異なるので注意してください。

```console:Python3用のLexer、Parser、Listenerを生成するコマンド
java -jar antlr-4.7.1-complete.jar -Dlanguage=Python3 grammar\*.g4
```

```console:生成後
.
│  antlr-4.7.1-complete.jar
│
└─grammar
        JavaLexer.g4
        JavaParser.g4
        JavaLexer.interp
        JavaLexer.py
        JavaLexer.tokens
        JavaParser.interp
        JavaParser.py
        JavaParser.tokens
        JavaParserListener.py
```

これでJavaのソースコード解析で必要となる解析器のソースコード（`JavaLexer.py`、`JavaParser.py`、`JavaParserListener.py`）が手に入りました。
以降はこのファイルを利用して独自の構文解析のプログラムを実装していきます。

### 2.4. （参考）Java用のLexer、Parser、デフォルトListenerの生成

参考までにJava用のLexer、Parser、Linstenerを生成するコマンドを以下に示します。
`-package`オプションでJavaのパッケージを指定することができます。

```console:Java用のLexer、Parser、Linstenerを生成するコマンド
// com.example.service.antlr パッケージとして出力
java -jar antlr-4.7.1-complete.jar -Dlanguage=Java -encoding utf-8 -package com.example.service.antlr grammar\*.g4
```

## 3. 構文解析のプログラムを作成

PythonでANTLRを使うために`antlr4-python3-runtime`をインストールします。
Python2用とPython3用でそれぞれ別のライブラリになるので注意してください。

```console:pipでインストールしたantlrのruntime
antlr4-python3-runtime             4.7.1
```

前述した通りListenerは解析したい目的毎に作成します。
今回はJavaクラスの基本情報とメソッドコールを取得するListenerを作成したいと思います。

### 3.1. Listenerで取得する情報

* パッケージ名
* クラス名
* 継承しているクラス
* 実装しているインターフェース
* インポートしているクラス
* フィールド
 * フィールドのデータ型
 * フィールドの定義
* メソッド
 * 戻り値のデータ型
 * メソッド名
 * パラメータ
  * パラメータのデータ型
  * パラメータ名
 * コールしているメソッド

### 3.2. ディレクトリ構成

```console:ディレクトリ構成
C:/temp/sample
├─logs
│      utiltools.log
│
├─resources
│  └─logging
│          utiltools_log.conf
│
└─src
    │  ast_analyze_executor.py
    │
    └─ast
            ast_processor.py
            basic_info_listener.py
            JavaLexer.py
            JavaParser.py
            JavaParserListener.py
            __init__.py
```

`JavaLexer.py`、`JavaParser.py`、`JavaParserListener.py`はANTLRで自動生成したファイルをそのままコピーします。

### 3.3. メイン処理

main関数のある処理です。説明上ファイルパスを直書きしていますが、適宜修正してください。

```python:ast_analyze_executor.py
import logging.config
from ast.ast_processor import AstProcessor
from ast.basic_info_listener import BasicInfoListener


if __name__ == '__main__':
    logging_setting_path = '../resources/logging/utiltools_log.conf'
    logging.config.fileConfig(logging_setting_path)
    logger = logging.getLogger(__file__)

    target_file_path = 'C:/temp/解析対象のファイル.java'

    # ★ポイント１
    ast_info = AstProcessor(logging, BasicInfoListener()).execute(target_file_path)
```

#### ★ポイント１
独自に実装したListenerのインスタンスを生成した後、後述する`AstProcessor`のインスタンスを生成します。
解析対象のファイルのファイルパスを引数として`AstProcessor`の`execute`メソッドを実行し、ソースコードの解析を実行します。
`execute`メソッドの戻り値は解析結果です。

### 3.4. Listenerを実行する処理

独自の解析処理を行うのがListenerと説明しました。そのため通常は目的に応じて複数のListenerを作成します。しかしListenerを実行する処理は変わらないため、汎用的にListenerを実行する処理を実装することにします。

```python:ast_processor.py
from antlr4 import FileStream, CommonTokenStream, ParseTreeWalker
from ast.JavaLexer import JavaLexer
from ast.JavaParser import JavaParser
from pprint import pformat


class AstProcessor:

    def __init__(self, logging, listener):
        self.logging = logging
        self.logger = logging.getLogger(self.__class__.__name__)
        self.listener = listener

    # ★ポイント２
    def execute(self, input_source):
        parser = JavaParser(CommonTokenStream(JavaLexer(FileStream(input_source, encoding="utf-8"))))
        walker = ParseTreeWalker()
        walker.walk(self.listener, parser.compilationUnit())
        self.logger.debug('Display all data extracted by AST. \n' + pformat(self.listener.ast_info, width=160))
        return self.listener.ast_info
```

#### ★ポイント２
解析対象のファイルから`JavaLexer`のインスタンスを生成し、それを利用して`JavaParser`のインスタンスを生成します。
解析は`ParseTreeWalker`のインスタンスの`walk`メソッドを呼び出すことで実行されます。
Listenerの処理が異なってもこの流れは同じです。

### 3.5. 独自Listenerの処理

ソースコード解析のポイントとなる独自Listenerの実装方法について説明します。
といってもポイントは３つでだけす。

* ANTLRが自動生成した`JavaParserListener`を拡張（継承）して独自のListenerを定義する。
* `JavaParserListener`の関数をオーバーライドし、解析に応じた処理を実装する。
* Listenerは解析処理の状態を意識して処理を実装する必要がある。
  * （状態を保持するためステートレスではない　⇒　毎回インスタンスを生成する必要がある）

```python:basic_info_listener.py
from ast.JavaParserListener import JavaParserListener
from ast.JavaParser import JavaParser


# ★ポイント３
class BasicInfoListener(JavaParserListener):

    # ★ポイント４
    def __init__(self):
        self.call_methods = []
        self.ast_info = {
            'packageName': '',
            'className': '',
            'implements': [],
            'extends': '',
            'imports': [],
            'fields': [],
            'methods': []
        }

    # ★ポイント５
    # Enter a parse tree produced by JavaParser#packageDeclaration.
    def enterPackageDeclaration(self, ctx:JavaParser.PackageDeclarationContext):
        self.ast_info['packageName'] = ctx.qualifiedName().getText()

    # Enter a parse tree produced by JavaParser#importDeclaration.
    def enterImportDeclaration(self, ctx:JavaParser.ImportDeclarationContext):
        import_class = ctx.qualifiedName().getText()
        self.ast_info['imports'].append(import_class)

    # Enter a parse tree produced by JavaParser#methodDeclaration.
    def enterMethodDeclaration(self, ctx:JavaParser.MethodDeclarationContext):

        print("{0} {1} {2}".format(ctx.start.line, ctx.start.column, ctx.getText()))
        self.call_methods = []

    # Exit a parse tree produced by JavaParser#methodDeclaration.
    def exitMethodDeclaration(self, ctx:JavaParser.MethodDeclarationContext):

        # ★ポイント６
        c1 = ctx.getChild(0).getText()  # ---> return type
        c2 = ctx.getChild(1).getText()  # ---> method name
        # c3 = ctx.getChild(2).getText()  # ---> params
        params = self.parse_method_params_block(ctx.getChild(2))

        method_info = {
            'returnType': c1,
            'methodName': c2,
            'params': params,
            'callMethods': self.call_methods
        }
        self.ast_info['methods'].append(method_info)

    # Enter a parse tree produced by JavaParser#methodCall.
    def enterMethodCall(self, ctx:JavaParser.MethodCallContext):
        # ★ポイント７
        line_number = str(ctx.start.line)
        column_number = str(ctx.start.column)
        self.call_methods.append(line_number + ' ' + column_number + ' ' + ctx.parentCtx.getText())

    # Enter a parse tree produced by JavaParser#classDeclaration.
    def enterClassDeclaration(self, ctx:JavaParser.ClassDeclarationContext):
        child_count = int(ctx.getChildCount())
        if child_count == 7:
            # class Foo extends Bar implements Hoge
            # c1 = ctx.getChild(0)  # ---> class
            c2 = ctx.getChild(1).getText()  # ---> class name
            # c3 = ctx.getChild(2)  # ---> extends
            c4 = ctx.getChild(3).getChild(0).getText()  # ---> extends class name
            # c5 = ctx.getChild(4)  # ---> implements
            # c7 = ctx.getChild(6)  # ---> method body
            self.ast_info['className'] = c2
            self.ast_info['implements'] = self.parse_implements_block(ctx.getChild(5))
            self.ast_info['extends'] = c4
        elif child_count == 5:
            # class Foo extends Bar
            # or
            # class Foo implements Hoge
            # c1 = ctx.getChild(0)  # ---> class
            c2 = ctx.getChild(1).getText()  # ---> class name
            c3 = ctx.getChild(2).getText()  # ---> extends or implements

            # c5 = ctx.getChild(4)  # ---> method body
            self.ast_info['className'] = c2
            if c3 == 'implements':
                self.ast_info['implements'] = self.parse_implements_block(ctx.getChild(3))
            elif c3 == 'extends':
                c4 = ctx.getChild(3).getChild(0).getText()  # ---> extends class name or implements class name
                self.ast_info['extends'] = c4
        elif child_count == 3:
            # class Foo
            # c1 = ctx.getChild(0)  # ---> class
            c2 = ctx.getChild(1).getText()  # ---> class name
            # c3 = ctx.getChild(2)  # ---> method body
            self.ast_info['className'] = c2

    # Enter a parse tree produced by JavaParser#fieldDeclaration.
    def enterFieldDeclaration(self, ctx:JavaParser.FieldDeclarationContext):
        field = {
            'fieldType': ctx.getChild(0).getText(),
            'fieldDefinition': ctx.getChild(1).getText()
        }
        self.ast_info['fields'].append(field)

    def parse_implements_block(self, ctx):
        implements_child_count = int(ctx.getChildCount())
        result = []
        if implements_child_count == 1:
            impl_class = ctx.getChild(0).getText()
            result.append(impl_class)
        elif implements_child_count > 1:
            for i in range(implements_child_count):
                if i % 2 == 0:
                    impl_class = ctx.getChild(i).getText()
                    result.append(impl_class)
        return result

    def parse_method_params_block(self, ctx):
        params_exist_check = int(ctx.getChildCount())
        result = []
        # () ---> 2
        # (Foo foo) ---> 3
        # (Foo foo, Bar bar) ---> 3
        # (Foo foo, Bar bar, int count) ---> 3
        if params_exist_check == 3:
            params_child_count = int(ctx.getChild(1).getChildCount())
            if params_child_count == 1:
                param_type = ctx.getChild(1).getChild(0).getChild(0).getText()
                param_name = ctx.getChild(1).getChild(0).getChild(1).getText()
                param_info = {
                    'paramType': param_type,
                    'paramName': param_name
                }
                result.append(param_info)
            elif params_child_count > 1:
                for i in range(params_child_count):
                    if i % 2 == 0:
                        param_type = ctx.getChild(1).getChild(i).getChild(0).getText()
                        param_name = ctx.getChild(1).getChild(i).getChild(1).getText()
                        param_info = {
                            'paramType': param_type,
                            'paramName': param_name
                        }
                        result.append(param_info)
        return result
```

#### ★ポイント３
`JavaParserListener`を拡張（継承）して独自のListenerである`BasicInfoListener`を定義します。

#### ★ポイント４
解析結果を保持する`ast_info`を定義します。プロパティの名称および内容は解析したい目的に応じて各自で適宜修正してください。

#### ★ポイント５
`JavaParserListener`で定義されているフックポイントの関数をオーバライドして独自の解析処理を実装します。
例えば`enterPackageDeclaration`は名前が示す通り、Javaのソースコードのパッケージ定義が開始された箇所で呼び出されます。
引数の`ctx`は型が異なりますが、親クラスが存在するため、構文解析で必要となる基本情報にはどのコンテキストクラスでもアクセスすることができます。

#### ★ポイント６
AST（抽象構文木）の名前が示す通り`ctx`は木構造になっています。`getChild`関数でそのコンテキストの子ノードにアクセスすることができます。子ノードの内容はコンテキストによって異なります。

#### ★ポイント７
通常、AST（抽象構文木）では具象情報である行番号や文字位置を保持しませんが、ANTLRではこれらの情報をコンテキストで保持しています。やはりソースコードを解析する際にこれらの情報は役立つので、必要に応じて利用してください。

* `ctx.start.line`　：　当該コンテキストのソースコード上の行番号
* `ctx.start.column`　：　当該コンテキストのソースコード上の文字位置

## 4. テスト

[TEARASOLUNA5.xのサンプルアプリケーションの１クラス](
https://github.com/terasolunaorg/terasoluna-tourreservation-mybatis3/blob/master/terasoluna-tourreservation-domain/src/main/java/org/terasoluna/tourreservation/domain/service/tourinfo/TourInfoServiceImpl.java)を対象に解析を行ってみたいと思います。

```java:TourInfoServiceImpl.java
/*
 * Copyright (C) 2013-2018 NTT DATA Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.terasoluna.tourreservation.domain.service.tourinfo;

import java.util.Collections;
import java.util.List;

import javax.inject.Inject;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.terasoluna.tourreservation.domain.model.TourInfo;
import org.terasoluna.tourreservation.domain.repository.tourinfo.TourInfoRepository;
import org.terasoluna.tourreservation.domain.repository.tourinfo.TourInfoSearchCriteria;

@Service
@Transactional
public class TourInfoServiceImpl implements TourInfoService {

    @Inject
    TourInfoRepository tourInfoRepository;

    @Override
    public Page<TourInfo> searchTour(TourInfoSearchCriteria criteria,
            Pageable pageable) {

        long total = tourInfoRepository.countBySearchCriteria(criteria);
        List<TourInfo> content;
        if (0 < total) {
            content = tourInfoRepository.findPageBySearchCriteria(criteria,
                    pageable);
        } else {
            content = Collections.emptyList();
        }

        Page<TourInfo> page = new PageImpl<TourInfo>(content, pageable, total);
        return page;
    }
}
```



```console:解析結果
{'className': 'TourInfoServiceImpl',
 'extends': '',
 'fields': [{'fieldDefinition': 'tourInfoRepository', 'fieldType': 'TourInfoRepository'}],
 'implements': ['TourInfoService'],
 'imports': ['java.util.Collections',
             'java.util.List',
             'javax.inject.Inject',
             'org.springframework.data.domain.Page',
             'org.springframework.data.domain.PageImpl',
             'org.springframework.data.domain.Pageable',
             'org.springframework.stereotype.Service',
             'org.springframework.transaction.annotation.Transactional',
             'org.terasoluna.tourreservation.domain.model.TourInfo',
             'org.terasoluna.tourreservation.domain.repository.tourinfo.TourInfoRepository',
             'org.terasoluna.tourreservation.domain.repository.tourinfo.TourInfoSearchCriteria'],
 'methods': [{'callMethods': ['43 40 tourInfoRepository.countBySearchCriteria(criteria)',
                              '46 41 tourInfoRepository.findPageBySearchCriteria(criteria,pageable)',
                              '49 34 Collections.emptyList()'],
              'methodName': 'searchTour',
              'params': [{'paramName': 'criteria', 'paramType': 'TourInfoSearchCriteria'}, {'paramName': 'pageable', 'paramType': 'Pageable'}],
              'returnType': 'Page<TourInfo>'}],
 'packageName': 'org.terasoluna.tourreservation.domain.service.tourinfo'}

```

解析結果を見るとクラスの構造および処理内容を把握することができるのが分かるかと思います。

## 5. さいごに

今回はANTLRとPythonを利用してJavaのソースコードをAST（Abstract Syntax Tree：抽象構文木）で構文解析する方法について説明しました。
ANTLRを利用することでGrep等のテキスト解析するよりも簡単かつ構造に従った解析ができることを理解して頂けたかと思います。
サンプルでは取得していませんが、他にも以下のような情報も取得することができます。

* アノテーションを取得する
* ステートメントを取得する（条件分岐、ループ、変数操作等）

ANTLRを利用した構文解析を取り入れることで、ソースコードをインプットとしたツールやアプリケーションを作れるようになります。


![exampleUsecaseOfUsingAntlr.jpg](https://qiita-image-store.s3.amazonaws.com/0/195748/67b0386a-6d1f-6f77-e656-ac36a610cda7.jpeg)

## 6. フックポイントの関数の一覧（参考）

参考としてListenerのフックポイントの関数一覧を以下に示します。

```python:フックポイントの関数の一覧（全部で208個）
def enterCompilationUnit(self, ctx:JavaParser.CompilationUnitContext):
def exitCompilationUnit(self, ctx:JavaParser.CompilationUnitContext):
def enterPackageDeclaration(self, ctx:JavaParser.PackageDeclarationContext):
def exitPackageDeclaration(self, ctx:JavaParser.PackageDeclarationContext):
def enterImportDeclaration(self, ctx:JavaParser.ImportDeclarationContext):
def exitImportDeclaration(self, ctx:JavaParser.ImportDeclarationContext):
def enterTypeDeclaration(self, ctx:JavaParser.TypeDeclarationContext):
def exitTypeDeclaration(self, ctx:JavaParser.TypeDeclarationContext):
def enterModifier(self, ctx:JavaParser.ModifierContext):
def exitModifier(self, ctx:JavaParser.ModifierContext):
def enterClassOrInterfaceModifier(self, ctx:JavaParser.ClassOrInterfaceModifierContext):
def exitClassOrInterfaceModifier(self, ctx:JavaParser.ClassOrInterfaceModifierContext):
def enterVariableModifier(self, ctx:JavaParser.VariableModifierContext):
def exitVariableModifier(self, ctx:JavaParser.VariableModifierContext):
def enterClassDeclaration(self, ctx:JavaParser.ClassDeclarationContext):
def exitClassDeclaration(self, ctx:JavaParser.ClassDeclarationContext):
def enterTypeParameters(self, ctx:JavaParser.TypeParametersContext):
def exitTypeParameters(self, ctx:JavaParser.TypeParametersContext):
def enterTypeParameter(self, ctx:JavaParser.TypeParameterContext):
def exitTypeParameter(self, ctx:JavaParser.TypeParameterContext):
def enterTypeBound(self, ctx:JavaParser.TypeBoundContext):
def exitTypeBound(self, ctx:JavaParser.TypeBoundContext):
def enterEnumDeclaration(self, ctx:JavaParser.EnumDeclarationContext):
def exitEnumDeclaration(self, ctx:JavaParser.EnumDeclarationContext):
def enterEnumConstants(self, ctx:JavaParser.EnumConstantsContext):
def exitEnumConstants(self, ctx:JavaParser.EnumConstantsContext):
def enterEnumConstant(self, ctx:JavaParser.EnumConstantContext):
def exitEnumConstant(self, ctx:JavaParser.EnumConstantContext):
def enterEnumBodyDeclarations(self, ctx:JavaParser.EnumBodyDeclarationsContext):
def exitEnumBodyDeclarations(self, ctx:JavaParser.EnumBodyDeclarationsContext):
def enterInterfaceDeclaration(self, ctx:JavaParser.InterfaceDeclarationContext):
def exitInterfaceDeclaration(self, ctx:JavaParser.InterfaceDeclarationContext):
def enterClassBody(self, ctx:JavaParser.ClassBodyContext):
def exitClassBody(self, ctx:JavaParser.ClassBodyContext):
def enterInterfaceBody(self, ctx:JavaParser.InterfaceBodyContext):
def exitInterfaceBody(self, ctx:JavaParser.InterfaceBodyContext):
def enterClassBodyDeclaration(self, ctx:JavaParser.ClassBodyDeclarationContext):
def exitClassBodyDeclaration(self, ctx:JavaParser.ClassBodyDeclarationContext):
def enterMemberDeclaration(self, ctx:JavaParser.MemberDeclarationContext):
def exitMemberDeclaration(self, ctx:JavaParser.MemberDeclarationContext):
def enterMethodDeclaration(self, ctx:JavaParser.MethodDeclarationContext):
def exitMethodDeclaration(self, ctx:JavaParser.MethodDeclarationContext):
def enterMethodBody(self, ctx:JavaParser.MethodBodyContext):
def exitMethodBody(self, ctx:JavaParser.MethodBodyContext):
def enterTypeTypeOrVoid(self, ctx:JavaParser.TypeTypeOrVoidContext):
def exitTypeTypeOrVoid(self, ctx:JavaParser.TypeTypeOrVoidContext):
def enterGenericMethodDeclaration(self, ctx:JavaParser.GenericMethodDeclarationContext):
def exitGenericMethodDeclaration(self, ctx:JavaParser.GenericMethodDeclarationContext):
def enterGenericConstructorDeclaration(self, ctx:JavaParser.GenericConstructorDeclarationContext):
def exitGenericConstructorDeclaration(self, ctx:JavaParser.GenericConstructorDeclarationContext):
def enterConstructorDeclaration(self, ctx:JavaParser.ConstructorDeclarationContext):
def exitConstructorDeclaration(self, ctx:JavaParser.ConstructorDeclarationContext):
def enterFieldDeclaration(self, ctx:JavaParser.FieldDeclarationContext):
def exitFieldDeclaration(self, ctx:JavaParser.FieldDeclarationContext):
def enterInterfaceBodyDeclaration(self, ctx:JavaParser.InterfaceBodyDeclarationContext):
def exitInterfaceBodyDeclaration(self, ctx:JavaParser.InterfaceBodyDeclarationContext):
def enterInterfaceMemberDeclaration(self, ctx:JavaParser.InterfaceMemberDeclarationContext):
def exitInterfaceMemberDeclaration(self, ctx:JavaParser.InterfaceMemberDeclarationContext):
def enterConstDeclaration(self, ctx:JavaParser.ConstDeclarationContext):
def exitConstDeclaration(self, ctx:JavaParser.ConstDeclarationContext):
def enterConstantDeclarator(self, ctx:JavaParser.ConstantDeclaratorContext):
def exitConstantDeclarator(self, ctx:JavaParser.ConstantDeclaratorContext):
def enterInterfaceMethodDeclaration(self, ctx:JavaParser.InterfaceMethodDeclarationContext):
def exitInterfaceMethodDeclaration(self, ctx:JavaParser.InterfaceMethodDeclarationContext):
def enterInterfaceMethodModifier(self, ctx:JavaParser.InterfaceMethodModifierContext):
def exitInterfaceMethodModifier(self, ctx:JavaParser.InterfaceMethodModifierContext):
def enterGenericInterfaceMethodDeclaration(self, ctx:JavaParser.GenericInterfaceMethodDeclarationContext):
def exitGenericInterfaceMethodDeclaration(self, ctx:JavaParser.GenericInterfaceMethodDeclarationContext):
def enterVariableDeclarators(self, ctx:JavaParser.VariableDeclaratorsContext):
def exitVariableDeclarators(self, ctx:JavaParser.VariableDeclaratorsContext):
def enterVariableDeclarator(self, ctx:JavaParser.VariableDeclaratorContext):
def exitVariableDeclarator(self, ctx:JavaParser.VariableDeclaratorContext):
def enterVariableDeclaratorId(self, ctx:JavaParser.VariableDeclaratorIdContext):
def exitVariableDeclaratorId(self, ctx:JavaParser.VariableDeclaratorIdContext):
def enterVariableInitializer(self, ctx:JavaParser.VariableInitializerContext):
def exitVariableInitializer(self, ctx:JavaParser.VariableInitializerContext):
def enterArrayInitializer(self, ctx:JavaParser.ArrayInitializerContext):
def exitArrayInitializer(self, ctx:JavaParser.ArrayInitializerContext):
def enterClassOrInterfaceType(self, ctx:JavaParser.ClassOrInterfaceTypeContext):
def exitClassOrInterfaceType(self, ctx:JavaParser.ClassOrInterfaceTypeContext):
def enterTypeArgument(self, ctx:JavaParser.TypeArgumentContext):
def exitTypeArgument(self, ctx:JavaParser.TypeArgumentContext):
def enterQualifiedNameList(self, ctx:JavaParser.QualifiedNameListContext):
def exitQualifiedNameList(self, ctx:JavaParser.QualifiedNameListContext):
def enterFormalParameters(self, ctx:JavaParser.FormalParametersContext):
def exitFormalParameters(self, ctx:JavaParser.FormalParametersContext):
def enterFormalParameterList(self, ctx:JavaParser.FormalParameterListContext):
def exitFormalParameterList(self, ctx:JavaParser.FormalParameterListContext):
def enterFormalParameter(self, ctx:JavaParser.FormalParameterContext):
def exitFormalParameter(self, ctx:JavaParser.FormalParameterContext):
def enterLastFormalParameter(self, ctx:JavaParser.LastFormalParameterContext):
def exitLastFormalParameter(self, ctx:JavaParser.LastFormalParameterContext):
def enterQualifiedName(self, ctx:JavaParser.QualifiedNameContext):
def exitQualifiedName(self, ctx:JavaParser.QualifiedNameContext):
def enterLiteral(self, ctx:JavaParser.LiteralContext):
def exitLiteral(self, ctx:JavaParser.LiteralContext):
def enterIntegerLiteral(self, ctx:JavaParser.IntegerLiteralContext):
def exitIntegerLiteral(self, ctx:JavaParser.IntegerLiteralContext):
def enterFloatLiteral(self, ctx:JavaParser.FloatLiteralContext):
def exitFloatLiteral(self, ctx:JavaParser.FloatLiteralContext):
def enterAnnotation(self, ctx:JavaParser.AnnotationContext):
def exitAnnotation(self, ctx:JavaParser.AnnotationContext):
def enterElementValuePairs(self, ctx:JavaParser.ElementValuePairsContext):
def exitElementValuePairs(self, ctx:JavaParser.ElementValuePairsContext):
def enterElementValuePair(self, ctx:JavaParser.ElementValuePairContext):
def exitElementValuePair(self, ctx:JavaParser.ElementValuePairContext):
def enterElementValue(self, ctx:JavaParser.ElementValueContext):
def exitElementValue(self, ctx:JavaParser.ElementValueContext):
def enterElementValueArrayInitializer(self, ctx:JavaParser.ElementValueArrayInitializerContext):
def exitElementValueArrayInitializer(self, ctx:JavaParser.ElementValueArrayInitializerContext):
def enterAnnotationTypeDeclaration(self, ctx:JavaParser.AnnotationTypeDeclarationContext):
def exitAnnotationTypeDeclaration(self, ctx:JavaParser.AnnotationTypeDeclarationContext):
def enterAnnotationTypeBody(self, ctx:JavaParser.AnnotationTypeBodyContext):
def exitAnnotationTypeBody(self, ctx:JavaParser.AnnotationTypeBodyContext):
def enterAnnotationTypeElementDeclaration(self, ctx:JavaParser.AnnotationTypeElementDeclarationContext):
def exitAnnotationTypeElementDeclaration(self, ctx:JavaParser.AnnotationTypeElementDeclarationContext):
def enterAnnotationTypeElementRest(self, ctx:JavaParser.AnnotationTypeElementRestContext):
def exitAnnotationTypeElementRest(self, ctx:JavaParser.AnnotationTypeElementRestContext):
def enterAnnotationMethodOrConstantRest(self, ctx:JavaParser.AnnotationMethodOrConstantRestContext):
def exitAnnotationMethodOrConstantRest(self, ctx:JavaParser.AnnotationMethodOrConstantRestContext):
def enterAnnotationMethodRest(self, ctx:JavaParser.AnnotationMethodRestContext):
def exitAnnotationMethodRest(self, ctx:JavaParser.AnnotationMethodRestContext):
def enterAnnotationConstantRest(self, ctx:JavaParser.AnnotationConstantRestContext):
def exitAnnotationConstantRest(self, ctx:JavaParser.AnnotationConstantRestContext):
def enterDefaultValue(self, ctx:JavaParser.DefaultValueContext):
def exitDefaultValue(self, ctx:JavaParser.DefaultValueContext):
def enterBlock(self, ctx:JavaParser.BlockContext):
def exitBlock(self, ctx:JavaParser.BlockContext):
def enterBlockStatement(self, ctx:JavaParser.BlockStatementContext):
def exitBlockStatement(self, ctx:JavaParser.BlockStatementContext):
def enterLocalVariableDeclaration(self, ctx:JavaParser.LocalVariableDeclarationContext):
def exitLocalVariableDeclaration(self, ctx:JavaParser.LocalVariableDeclarationContext):
def enterLocalTypeDeclaration(self, ctx:JavaParser.LocalTypeDeclarationContext):
def exitLocalTypeDeclaration(self, ctx:JavaParser.LocalTypeDeclarationContext):
def enterStatement(self, ctx:JavaParser.StatementContext):
def exitStatement(self, ctx:JavaParser.StatementContext):
def enterCatchClause(self, ctx:JavaParser.CatchClauseContext):
def exitCatchClause(self, ctx:JavaParser.CatchClauseContext):
def enterCatchType(self, ctx:JavaParser.CatchTypeContext):
def exitCatchType(self, ctx:JavaParser.CatchTypeContext):
def enterFinallyBlock(self, ctx:JavaParser.FinallyBlockContext):
def exitFinallyBlock(self, ctx:JavaParser.FinallyBlockContext):
def enterResourceSpecification(self, ctx:JavaParser.ResourceSpecificationContext):
def exitResourceSpecification(self, ctx:JavaParser.ResourceSpecificationContext):
def enterResources(self, ctx:JavaParser.ResourcesContext):
def exitResources(self, ctx:JavaParser.ResourcesContext):
def enterResource(self, ctx:JavaParser.ResourceContext):
def exitResource(self, ctx:JavaParser.ResourceContext):
def enterSwitchBlockStatementGroup(self, ctx:JavaParser.SwitchBlockStatementGroupContext):
def exitSwitchBlockStatementGroup(self, ctx:JavaParser.SwitchBlockStatementGroupContext):
def enterSwitchLabel(self, ctx:JavaParser.SwitchLabelContext):
def exitSwitchLabel(self, ctx:JavaParser.SwitchLabelContext):
def enterForControl(self, ctx:JavaParser.ForControlContext):
def exitForControl(self, ctx:JavaParser.ForControlContext):
def enterForInit(self, ctx:JavaParser.ForInitContext):
def exitForInit(self, ctx:JavaParser.ForInitContext):
def enterEnhancedForControl(self, ctx:JavaParser.EnhancedForControlContext):
def exitEnhancedForControl(self, ctx:JavaParser.EnhancedForControlContext):
def enterParExpression(self, ctx:JavaParser.ParExpressionContext):
def exitParExpression(self, ctx:JavaParser.ParExpressionContext):
def enterExpressionList(self, ctx:JavaParser.ExpressionListContext):
def exitExpressionList(self, ctx:JavaParser.ExpressionListContext):
def enterMethodCall(self, ctx:JavaParser.MethodCallContext):
def exitMethodCall(self, ctx:JavaParser.MethodCallContext):
def enterExpression(self, ctx:JavaParser.ExpressionContext):
def exitExpression(self, ctx:JavaParser.ExpressionContext):
def enterLambdaExpression(self, ctx:JavaParser.LambdaExpressionContext):
def exitLambdaExpression(self, ctx:JavaParser.LambdaExpressionContext):
def enterLambdaParameters(self, ctx:JavaParser.LambdaParametersContext):
def exitLambdaParameters(self, ctx:JavaParser.LambdaParametersContext):
def enterLambdaBody(self, ctx:JavaParser.LambdaBodyContext):
def exitLambdaBody(self, ctx:JavaParser.LambdaBodyContext):
def enterPrimary(self, ctx:JavaParser.PrimaryContext):
def exitPrimary(self, ctx:JavaParser.PrimaryContext):
def enterClassType(self, ctx:JavaParser.ClassTypeContext):
def exitClassType(self, ctx:JavaParser.ClassTypeContext):
def enterCreator(self, ctx:JavaParser.CreatorContext):
def exitCreator(self, ctx:JavaParser.CreatorContext):
def enterCreatedName(self, ctx:JavaParser.CreatedNameContext):
def exitCreatedName(self, ctx:JavaParser.CreatedNameContext):
def enterInnerCreator(self, ctx:JavaParser.InnerCreatorContext):
def exitInnerCreator(self, ctx:JavaParser.InnerCreatorContext):
def enterArrayCreatorRest(self, ctx:JavaParser.ArrayCreatorRestContext):
def exitArrayCreatorRest(self, ctx:JavaParser.ArrayCreatorRestContext):
def enterClassCreatorRest(self, ctx:JavaParser.ClassCreatorRestContext):
def exitClassCreatorRest(self, ctx:JavaParser.ClassCreatorRestContext):
def enterExplicitGenericInvocation(self, ctx:JavaParser.ExplicitGenericInvocationContext):
def exitExplicitGenericInvocation(self, ctx:JavaParser.ExplicitGenericInvocationContext):
def enterTypeArgumentsOrDiamond(self, ctx:JavaParser.TypeArgumentsOrDiamondContext):
def exitTypeArgumentsOrDiamond(self, ctx:JavaParser.TypeArgumentsOrDiamondContext):
def enterNonWildcardTypeArgumentsOrDiamond(self, ctx:JavaParser.NonWildcardTypeArgumentsOrDiamondContext):
def exitNonWildcardTypeArgumentsOrDiamond(self, ctx:JavaParser.NonWildcardTypeArgumentsOrDiamondContext):
def enterNonWildcardTypeArguments(self, ctx:JavaParser.NonWildcardTypeArgumentsContext):
def exitNonWildcardTypeArguments(self, ctx:JavaParser.NonWildcardTypeArgumentsContext):
def enterTypeList(self, ctx:JavaParser.TypeListContext):
def exitTypeList(self, ctx:JavaParser.TypeListContext):
def enterTypeType(self, ctx:JavaParser.TypeTypeContext):
def exitTypeType(self, ctx:JavaParser.TypeTypeContext):
def enterPrimitiveType(self, ctx:JavaParser.PrimitiveTypeContext):
def exitPrimitiveType(self, ctx:JavaParser.PrimitiveTypeContext):
def enterTypeArguments(self, ctx:JavaParser.TypeArgumentsContext):
def exitTypeArguments(self, ctx:JavaParser.TypeArgumentsContext):
def enterSuperSuffix(self, ctx:JavaParser.SuperSuffixContext):
def exitSuperSuffix(self, ctx:JavaParser.SuperSuffixContext):
def enterExplicitGenericInvocationSuffix(self, ctx:JavaParser.ExplicitGenericInvocationSuffixContext):
def exitExplicitGenericInvocationSuffix(self, ctx:JavaParser.ExplicitGenericInvocationSuffixContext):
def enterArguments(self, ctx:JavaParser.ArgumentsContext):
def exitArguments(self, ctx:JavaParser.ArgumentsContext):
```