このページの内容

エラー処理

エラーには、いくつかの種類があります。

構文エラー #

最初に遭遇するのは、たいてい構文エラーでしょう。これは、コードが言語の文法に従っていない、次のような単純な不具合を含みます。


1 + * 2

Wrenは、コードを読み取ろうとした時点で、これらのエラーを検出します。エラーがあると、次のような分かりやすいメッセージを表示します。

[main line 1] Error on '*': Unexpected token for expression.

もう少し「意味的」なエラーも、この種類に含まれます。未定義の変数を使うことや、同じスコープで同じ名前の変数を二つ宣言することなどです。例えば、次のように書くと、


var a = "once"
var a = "twice"

Wrenは、次のように知らせます。

[main line 2] Error on 'a': Top-level variable is already defined.

これは、コードを一つでも実行する前に行う点に注意してください。ほかの一部のスクリプト言語とは異なり、Wrenは、可能な限り早くエラーを見つけられるようにします。

コードの実行を開始したなら、構文や変数のスコープに関するエラーがないことは確かです。

実行時エラー #

残念ながら、「コンパイル時」のエラーをすべて直しても、コードが意図どおりに動くとは限りません。プログラムには、静的には検出できないエラーが残っているかもしれません。コードを実行するまで見つけられないので、「実行時」エラーと呼びます。

実行時エラーの大半は、VM自身から発生します。VMができない操作を、コードが行おうとするためです。最も一般的なのは、「メソッドが見つからない」というエラーです。オブジェクトのメソッドを呼び出しても、そのクラスにも、すべてのスーパークラスにもそのメソッドが定義されていなければ、Wrenには何もできません。


class Foo {
  construct new() {}
}

var foo = Foo.new()
foo.someRandomMethod

これを実行すると、Wrenは次のように表示します。

Foo does not implement method 'someRandomMethod'.

その後、コードの実行を停止します。ほかの一部の言語とは異なり、Wrenは実行時エラーが起きた後に実行を続けません。実行時エラーは、コードに不具合があることを意味するため、それに気付いてほしいのです。助けとなるよう、エラーが起きたコードの位置と、そこへ至るすべてのメソッド呼び出しを示すスタックトレースを表示します。

ほかによくある実行時エラーは、メソッドに型の違う引数を渡すことです。例えば、リストのインデックスには数値を使います。ほかの型を渡そうとすると、エラーになります。


var list = ["a", "b", "c"]
list["1"]

これは、次のように表示して終了します。

Subscript must be a number or a range.
[main line 2] in (script)

この二つが、最も一般的な実行時エラーですが、ほかにもあります。リストの範囲外へのアクセス、引数の個数を間違えた関数呼び出しなどです。

実行時エラーを処理する #

ほとんどの場合、実行時エラーはコードの不具合を示し、最善の解決策は、不具合を直すことです。しかし、実行時にエラーを処理できると便利な場合もあります。

言語を単純にするため、Wrenには例外処理がありません。代わりに、エラーの処理にファイバーを使います。実行時エラーが起きると、現在のファイバーは中止します。通常は、そのファイバーを呼び出したファイバーも、メインファイバーまで順に中止し、VMを終了します。

ただし、tryメソッドを使ってファイバーを実行することもできます。呼び出したファイバーで実行時エラーが起きると、エラーを捕捉し、tryメソッドはエラーメッセージを文字列として返します。

例えば、次のプログラムを実行すると、


var fiber = Fiber.new {
  123.badMethod
}

var error = fiber.try()
System.print("Caught error: " + error)

次のように表示します。

Caught error: Num does not implement method 'badMethod'.

呼び出したファイバーは、もう使えませんが、ほかのファイバーは通常どおり続行できます。実行時エラーによってファイバーが中止された場合、そのファイバーオブジェクトからエラーを取得することもできます。上の例を続けると、


System.print(fiber.error)

これも、次のように表示します。

Num does not implement method 'badMethod'.

ファイバー呼び出しの連鎖の中で実行時エラーが起きると、その連鎖をたどってtry呼び出しを探します。したがって、tryを呼び出したファイバーが、さらに呼び出したファイバーで発生した実行時エラーも捕捉できます。

実行時エラーを発生させる #

実行時エラーの大半はWren VMの内部から発生しますが、自分で実行時エラーを発生させたい場合もあります。Fiberの静的abort()メソッドを呼び出すと、それができます。


Fiber.abort("Something bad happened")

エラーメッセージを渡す必要があり、そのメッセージは文字列でなければなりません。

渡したメッセージがnullなら、実行時エラーは発生しません。

処理の失敗 #

最後の種類のエラーは、最も高水準のものです。これまでのエラーは、すべて不具合、つまりコード自体が正しくない箇所を示します。しかし、予測できない理由により、単にコードが処理を達成できなかったことを示すエラーもあります。これを「失敗」と呼びます。

ユーザーから入力文字列を読み取り、数値へ解析するプログラムを考えてください。多くの文字列は有効な数値ではないので、この解析は失敗することがあります。プログラムが失敗を防ぐには、解析の前に文字列を検証するしかありませんが、文字列が数値かどうかの検証は、解析とほぼ同じ処理です。

このように失敗が起こり得て、プログラムがそれを処理したい場合、ファイバーとtry()は、粒度が粗すぎます。代わりに、こうした操作は、何らかのエラーの印を返すことで、失敗を示します。

例えば、数値を解析するメソッドは、成功したら数値を、解析が失敗したことを示すためにはnullを返せます。Wrenは動的型付けなので、メソッドから型の異なる値を返すことは、簡単で自然です。



モジュール化 →← 並行処理