概要
この記事は、ANTLRを応用してC++のパーサーを検討した際にわかったことをまとめたものです。誰かの役に立つことを期待して、記事を公開します。
結果は最後の方にあるので、お急ぎの方は最後の方だけ見てください。
ANTLRはパーサージェネレータ
ソースコードなどの構造化されたテキストをデータとして処理するのに便利な、ANTLR(ANother Tool for Language Recognition)をご存知でしょうか。
構造化されたテキストデータを、構造を持ったものとしてコンピュータに理解させる処理は、パース(構文解析)と呼ばれます。
通常、テキストの構造はプログラミング言語毎に異なるので、構文解析はその言語の文法に特化する形で作る必要があります。
その構文解析の作成のハードルを少し下げてくれるのが、ANTLRです。
(だいぶ端折りますが)ANTLRは、EBNF記法に似た独自のプログラミング言語(ANTLR4向けの拡張子はg4)で構造を定義し、指定の実装(実行?)言語向けに構文解析を行うソースコードを生成することもあって、パーサージェネレータと呼ばれます。
なお、ANTLRについての特徴は、検討の過程で見つけた優れた内容の記事で確認できます。
目標はC++ソースコードの構文解析
ANTLRで自由度高くパーサーを生成できることもあって、独自のプログラミング言語向けのパーサーを定義することも比較的容易になりますが、ここではC++のソースコードを構文解析するをパーサーの実装を目標に取り組むことにします。
理由は、組み込み機器向けのソフトウェア開発において、私が使用しているプログラミング言語は主にC/C++で、これらが解析できるようになると、コンパイラではカバー出来ない独自のチェック処理などが作れると見込んでいるからです。
とは言え、1からC++の構造を定義するのは骨が折れるに決まっています。そこで、grammars-v4に有志によってメンテナンスされている
というC++向けの文法定義を使うことにします。
開発環境にはEclipseを利用
本記事ではANTLRの開発環境としてEclipseを用います。
私がEclipseの使用に慣れていることと、ネット上に日本語での情報がある程度は見つかること、などからです。
環境構築には
などを参考にしました。
バージョン
- Windows10 Pro
- JDK 1.8
- Eclipse 2019-12 (4.14.0)
構築手順
EclipseへのANTLRの導入手順は次のとおりです。
- Help -> Eclipse Market placeで、Antlrを検索
- ANTLR 4 IDE 0.3.6をインストール。途中で出てくる選択肢はInstall anywayで進める
- Eclipse再起動後、Window -> Show view -> others -> ANTLR 4で、
Parse Tree
とSyntax Diagram
を選択
最後までこなすと、Eclipseの表示は赤枠で示したように
な感じになっているはずです。
コマンドラインから構文解析
大抵のコンパイラがコマンドラインから実行できるようになっているのにならって、
java -jar xxx.jar filepath
といったように、C++パーサーもコマンドラインから実行できるようにしておいたほうが使い勝手がいいでしょう。
また、構文解析後に結果がわかるよう、エラーの有無を表示させるようにすれば、同様に使い勝手がいいはずです。
上述のとおり、パーサー開発で用いている言語がJavaですので、コマンドライン実行を可能とするにはjarファイル化することが求められます。
そこで、ここからはcppにある2つのg4ファイルから生成されたJavaソースコードを組み込んだjarファイルの作成手順を説明します。
Javaプロジェクト作成とANTLR runtime設定
g4ファイルを格納するだけであれば、Javaプロジェクトを作ることは必須ではありません。
ただ、jarファイルを作る上ではJavaプロジェクトがあったほうが便利なので、g4ファイルの受け皿も兼ねてJavaプロジェクトを作成し、そこにg4ファイルを格納し、あわせてANTLR runtimeも設定します。
手順は次の通りです。
- File -> New -> Project -> Java -> Java ProjectでProject nameに任意の名称を入れてFinish
- Window -> Preferences -> ANTRL4 -> Tool -> ANTLR Tool -> Versionに記載のバージョン番号を控える
- 一旦Eclipseから離れ、ここから、上記で控えたバージョンに合致するruntime.jarをローカルの任意のパスにダウンロード
なお、ANTLR 4 IDEのバージョン0.3.6に対して、runtimeはバージョン4.4でした - Eclipseに戻り、上記で作成したプロジェクトで、右クリック -> Build Path -> Configure Build Path... -> Libraries -> Add External JARs... から上記でダウンロードしたruntime.jarを指定
g4ファイルをコピー
先の手順で作成したプロジェクトであればコピー先は任意です。
srcフォルダと同階層にg4向けとしてantlrフォルダを作ってそこにコピーしたので、結果はこうなります。
g4ファイルからソースコード生成を確認
コピー後に自動でソースコード生成が実行されているかもしれないものの、生成されたソースコードをこれから作成するクラスから参照しやすくするために、CPP14Lexer.g4とCPP14Parser.g4のそれぞれに@headerを使ってパッケージ名を追加する修正を加えます。
なお、packageに続く名前空間の指定は任意です。
(略)
parser grammar CPP14Parser;
@header {
package antlr.cpp14.parser;
}
(略)
(略)
lexer grammar CPP14Lexer;
@header {
package antlr.cpp14.parser;
}
(略)
すべて入力後、File -> Seve Allを選択します。
Save All後、自動でソースコード生成が行われたことを確認します。
赤枠で示したように複数のファイルが存在する状況になっているはずです。
デフォルト設定のままでは、target\generated-sources\antlr4**\CPP14Lexer.java といったように階層深くファイルが生成されます。preferenceから生成パスの指定で浅くすることが可能です
これらをsrcフォルダの下にパッケージを作り、ファイルを複製してこの作業は終わりです。
構文解析のエラーをリスナーで取得
冒頭で述べたように、構文解析後にエラーの有無を表示させるようにします。ANTLRが生成したCPP14Parserクラスは文法エラーをキャッチするリスナーを登録可能になっています。リスナーはANTLRErrorListenerインターフェイスを実装していることだけですので、ここで用意します。
ANTLRErrorListenerインターフェイスを実装し、1つでもエラーがあったらプライベートフィールドにエラー履歴を残し、パース後に他のクラスは履歴を取得できる、という使い方が可能となるように実装を進めます。
Cpp14ErrorListener
public class Cpp14ErrorListener implements ANTLRErrorListener {
private boolean error = false;
@Override
public void reportAmbiguity(Parser arg0, DFA arg1, int arg2, int arg3, boolean arg4, BitSet arg5,
ATNConfigSet arg6) {
error = true;
}
@Override
public void reportAttemptingFullContext(Parser arg0, DFA arg1, int arg2, int arg3, BitSet arg4, ATNConfigSet arg5) {
}
@Override
public void reportContextSensitivity(Parser arg0, DFA arg1, int arg2, int arg3, int arg4, ATNConfigSet arg5) {
}
@Override
public void syntaxError(Recognizer<?, ?> arg0, Object arg1, int arg2, int arg3, String arg4,
RecognitionException arg5) {
}
public boolean hasError() {
return error;
}
}
mainメソッドを持つクラスを作る
コマンドラインからjarを呼び出すには、mainメソッドとそれを有するクラスが必要です。
パッケージで右クリック -> New -> Class -> 現れたダイアログで
のようにNameボックスにクラス名を適当に入れ、public static void mainのチェックボックスにチェックし、Finishです。
mainメソッドからparserを呼び出す
さて、
java -jar xxx.jar filepath
とできるようにするには、mainメソッドで受けた引数がファイルパスである前提で、ファイルをパーサーに渡す処理を追記する必要があります。
を参考にしつつ、実装したmainメソッドは以下になります。
Cpp14ErrorListener
public static void main(String[] args) {
if (args.length < 1) {
return;
}
File file = new File(args[0]);
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
ANTLRInputStream stream = new ANTLRInputStream(fis);
CPP14Lexer lexer = new CPP14Lexer(stream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CPP14Parser parser = new CPP14Parser(tokens);
Cpp14ErrorListener listener = new Cpp14ErrorListener();
parser.addErrorListener(listener);
parser.translationUnit();
fis.close();
int errorCode = 0;
if (listener.hasError()) {
errorCode = 1;
}
System.out.println("Finished. Result: " + errorCode + " file:///" + args[0]);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
なお、パーサーに構文解析を行わせるには、
parser.translationUnit();
のように、定義したルールのルートに当たるルール名をメソッド名に呼ぶことです。
これはANTLRの暗黙の了解とも言えるので、オリジナルの言語を作った場合は注意が必要です。
jarを作る
以上で必要な処理は揃ったので、次はjarファイルを作ります。runtime.jarを含めた形でのjar生成が必要です。
手順は、
- プロジェクトで右クリック -> Run as -> Java Application -> Consoleを選択し、OK
この手順の理由は、後の工程で指定するLaunch Configuration のエントリを作るためです - プロジェクト名上で右クリック -> Export... -> Java -> Runnable JAR file -> エントリを選択
- Launch ConfigurationのプルダウンにあるConsole - antlr.cpp14を選択する
- Export destinationは任意のパスを選択
- Library handlingはExtract required ~が選択されていることを確認してFinish
jarを単独で動かすためにはExtract~ではなく、Package~でもよいです。
詳しくはこちらで確認できます。
exeファイルで配布したい方はこちらを参考にしてください
パーサーを検証
対象のソースコードはESP32
以上で構文解析を行う準備が整ったので、パーサーの出来具合について検証を行います。
検証に用いるコードは、定期的にメンテナンスされているC++ソースコードを含みかつ、組み込み機器向けのソースコードを条件に探したところ、これ
が良さそうだったので用いることにします。
これはESP32というラズパイに似たマイコンボード向けに開発、メンテナンスされているC/C++のソースコードです。
ここからヘッダファイルを除いたC/C++のファイルを取り出した結果は次のようになります。
C/C++ファイル一覧
arduino-esp32-master\arduino-esp32-master\cores\esp32\base64.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\cbuf.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\Esp.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\FirmwareMSC.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\FunctionalInterrupt.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\HardwareSerial.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\HWCDC.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\IPAddress.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\IPv6Address.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\main.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\MD5Builder.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\Print.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\Stream.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\StreamString.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\Tone.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\USB.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\USBCDC.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\USBMSC.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\WMath.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\WString.cpp
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-adc.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-bt.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-cpu.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-dac.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-gpio.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-i2c-slave.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-i2c.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-ledc.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-matrix.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-misc.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-psram.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-rmt.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-sigmadelta.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-spi.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-time.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-timer.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-tinyusb.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-touch.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\esp32-hal-uart.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\firmware_msc_fat.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\stdlib_noniso.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\wiring_pulse.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\wiring_shift.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\libb64\cdecode.c
arduino-esp32-master\arduino-esp32-master\cores\esp32\libb64\cencode.c
arduino-esp32-master\arduino-esp32-master\libraries\ArduinoOTA\src\ArduinoOTA.cpp
arduino-esp32-master\arduino-esp32-master\libraries\AsyncUDP\src\AsyncUDP.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLE2902.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLE2904.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEAddress.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEAdvertisedDevice.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEAdvertising.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEBeacon.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLECharacteristic.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLECharacteristicMap.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEClient.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEDescriptor.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEDescriptorMap.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEDevice.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEEddystoneTLM.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEEddystoneURL.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEExceptions.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEHIDDevice.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLERemoteCharacteristic.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLERemoteDescriptor.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLERemoteService.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEScan.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLESecurity.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEServer.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEService.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEServiceMap.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEUtils.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEUUID.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\BLEValue.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\FreeRTOS.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BLE\src\GeneralUtils.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BluetoothSerial\src\BluetoothSerial.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BluetoothSerial\src\BTAddress.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BluetoothSerial\src\BTAdvertisedDeviceSet.cpp
arduino-esp32-master\arduino-esp32-master\libraries\BluetoothSerial\src\BTScanResultsSet.cpp
arduino-esp32-master\arduino-esp32-master\libraries\DNSServer\src\DNSServer.cpp
arduino-esp32-master\arduino-esp32-master\libraries\EEPROM\src\EEPROM.cpp
arduino-esp32-master\arduino-esp32-master\libraries\ESP32\examples\Camera\CameraWebServer\app_httpd.cpp
arduino-esp32-master\arduino-esp32-master\libraries\ESPmDNS\src\ESPmDNS.cpp
arduino-esp32-master\arduino-esp32-master\libraries\Ethernet\src\ETH.cpp
arduino-esp32-master\arduino-esp32-master\libraries\FFat\src\FFat.cpp
arduino-esp32-master\arduino-esp32-master\libraries\FS\src\FS.cpp
arduino-esp32-master\arduino-esp32-master\libraries\FS\src\vfs_api.cpp
arduino-esp32-master\arduino-esp32-master\libraries\HTTPClient\src\HTTPClient.cpp
arduino-esp32-master\arduino-esp32-master\libraries\HTTPUpdate\src\HTTPUpdate.cpp
arduino-esp32-master\arduino-esp32-master\libraries\LittleFS\examples\LITTLEFS_PlatformIO\src\main.cpp
arduino-esp32-master\arduino-esp32-master\libraries\LittleFS\src\LittleFS.cpp
arduino-esp32-master\arduino-esp32-master\libraries\NetBIOS\src\NetBIOS.cpp
arduino-esp32-master\arduino-esp32-master\libraries\Preferences\src\Preferences.cpp
arduino-esp32-master\arduino-esp32-master\libraries\RainMaker\src\RMaker.cpp
arduino-esp32-master\arduino-esp32-master\libraries\RainMaker\src\RMakerDevice.cpp
arduino-esp32-master\arduino-esp32-master\libraries\RainMaker\src\RMakerNode.cpp
arduino-esp32-master\arduino-esp32-master\libraries\RainMaker\src\RMakerParam.cpp
arduino-esp32-master\arduino-esp32-master\libraries\RainMaker\src\RMakerType.cpp
arduino-esp32-master\arduino-esp32-master\libraries\SD\src\SD.cpp
arduino-esp32-master\arduino-esp32-master\libraries\SD\src\sd_diskio.cpp
arduino-esp32-master\arduino-esp32-master\libraries\SD\src\sd_diskio_crc.c
arduino-esp32-master\arduino-esp32-master\libraries\SD_MMC\src\SD_MMC.cpp
arduino-esp32-master\arduino-esp32-master\libraries\SimpleBLE\src\SimpleBLE.cpp
arduino-esp32-master\arduino-esp32-master\libraries\SPI\src\SPI.cpp
arduino-esp32-master\arduino-esp32-master\libraries\SPIFFS\src\SPIFFS.cpp
arduino-esp32-master\arduino-esp32-master\libraries\Ticker\src\Ticker.cpp
arduino-esp32-master\arduino-esp32-master\libraries\Update\src\HttpsOTAUpdate.cpp
arduino-esp32-master\arduino-esp32-master\libraries\Update\src\Updater.cpp
arduino-esp32-master\arduino-esp32-master\libraries\USB\src\USBHID.cpp
arduino-esp32-master\arduino-esp32-master\libraries\USB\src\USBHIDConsumerControl.cpp
arduino-esp32-master\arduino-esp32-master\libraries\USB\src\USBHIDGamepad.cpp
arduino-esp32-master\arduino-esp32-master\libraries\USB\src\USBHIDKeyboard.cpp
arduino-esp32-master\arduino-esp32-master\libraries\USB\src\USBHIDMouse.cpp
arduino-esp32-master\arduino-esp32-master\libraries\USB\src\USBHIDSystemControl.cpp
arduino-esp32-master\arduino-esp32-master\libraries\USB\src\USBHIDVendor.cpp
arduino-esp32-master\arduino-esp32-master\libraries\USB\src\USBVendor.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WebServer\src\Parsing.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WebServer\src\WebServer.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WebServer\src\detail\mimetable.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFi\src\WiFi.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFi\src\WiFiAP.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFi\src\WiFiClient.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFi\src\WiFiGeneric.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFi\src\WiFiMulti.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFi\src\WiFiScan.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFi\src\WiFiServer.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFi\src\WiFiSTA.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFi\src\WiFiUdp.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFiClientSecure\src\ssl_client.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFiClientSecure\src\WiFiClientSecure.cpp
arduino-esp32-master\arduino-esp32-master\libraries\WiFiClientSecure\src\esp_crt_bundle.c
arduino-esp32-master\arduino-esp32-master\libraries\WiFiProv\src\WiFiProv.cpp
arduino-esp32-master\arduino-esp32-master\libraries\Wire\src\Wire.cpp
arduino-esp32-master\arduino-esp32-master\variants\adafruit_feather_esp32s2\variant.cpp
arduino-esp32-master\arduino-esp32-master\variants\adafruit_feather_esp32s2_reversetft\variant.cpp
arduino-esp32-master\arduino-esp32-master\variants\adafruit_feather_esp32s2_tft\variant.cpp
arduino-esp32-master\arduino-esp32-master\variants\adafruit_feather_esp32_v2\variant.cpp
arduino-esp32-master\arduino-esp32-master\variants\adafruit_funhouse_esp32s2\variant.cpp
arduino-esp32-master\arduino-esp32-master\variants\adafruit_magtag29_esp32s2\variant.cpp
arduino-esp32-master\arduino-esp32-master\variants\adafruit_metro_esp32s2\variant.cpp
arduino-esp32-master\arduino-esp32-master\variants\adafruit_qtpy_esp32\variant.cpp
arduino-esp32-master\arduino-esp32-master\variants\adafruit_qtpy_esp32s2\variant.cpp
パスしたのは約70%
指標はパース成功の割合、つまり、(パースでエラーとならなかったファイルの数/パースにかけたC/C++ソースコードのファイル総数) * 100、とします。
結果だけ示すと、パースの成功率は71%(エラーなし102ファイル/全体143ファイル)でした。
ESP32のソースコードはコンパイラによるコンパイルは100%成功しているでしょうから、そこから比較するとかなり低い数値です。
C++パーサーにCソースコードを解析させているので数値が低いのは致し方ない面もありそうですが、このままでは実用性に乏しいと言えるでしょう。
そこで、次はエラーの原因を取り除く、さまざまな改善に取り組んでみようと思います。