Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 1897] ファイルの概要

このコミットは、Go言語の標準ライブラリの一部であるscannerパッケージ(src/lib/go/scanner.go)と、そのテストファイル(src/lib/go/scanner_test.go)に対する変更を含んでいます。scannerパッケージは、ソースコードを字句解析(トークン化)するための機能を提供します。具体的には、入力されたバイト列をGo言語のキーワード、識別子、演算子、リテラルなどの意味のある最小単位(トークン)に分解する役割を担います。

このコミットの主な目的は、scannerの柔軟性を高め、エラーハンドリングの挙動を改善することにあります。具体的には、エラーハンドラが設定されていない場合でもスキャナーが動作し続けることを許可し、発生したエラーの総数を追跡するためのカウンタを導入しています。これにより、より堅牢で、かつエラー発生時にも処理を継続できるような字句解析器の実現を目指しています。

コミット

minor tweaks:
- permit scanner to run w/o error handler
- provide an error counter

R=iant
DELTA=43  (25 added, 0 deleted, 18 changed)
OCL=26804
CL=26812

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/3f9da829041dfaa1ee0a15b4f24ae94d7ad15a0e

元コミット内容

commit 3f9da829041dfaa1ee0a15b4f24ae94d7ad15a0e
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Mar 26 22:13:49 2009 -0700

    minor tweaks:
    - permit scanner to run w/o error handler
    - provide an error counter
    
    R=iant
    DELTA=43  (25 added, 0 deleted, 18 changed)
    OCL=26804
    CL=26812
---
 src/lib/go/scanner.go      | 42 ++++++++++++++++++++++++++++++------------
 src/lib/go/scanner_test.go | 13 ++++++++++---\n 2 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/src/lib/go/scanner.go b/src/lib/go/scanner.go
index 8b51a3e498..e35cc5c722 100644
--- a/src/lib/go/scanner.go
+++ b/src/lib/go/scanner.go
@@ -16,10 +16,10 @@ import (
 )
 
 
-// An implementation of an ErrorHandler must be provided to the Scanner.\n-// If a syntax error is encountered, Error is called with a position and\n-// an error message. The position points to the beginning of the offending\n-// token.\n+// An implementation of an ErrorHandler may be provided to the Scanner.\n+// If a syntax error is encountered and a handler was installed, Error\n+// is called with a position and an error message. The position points\n+// to the beginning of the offending token.\n //
 type ErrorHandler interface {
  	Error(pos token.Position, msg string);
 @@ -34,13 +34,16 @@ type ErrorHandler interface {
 type Scanner struct {
  	// immutable state
  	src []byte;  // source
-\terr ErrorHandler;  // error reporting\n+\terr ErrorHandler;  // error reporting; or nil\n  	scan_comments bool;  // if set, comments are reported as tokens
 
  	// scanning state
  	pos token.Position;  // previous reading position (position before ch)
  	offset int;  // current reading offset (position after ch)
  	ch int;  // one char look-ahead
+\n+\t// public state - ok to modify\n+\tErrorCount int;  // number of errors encountered\n }\n 
 
 
 @@ -70,10 +73,12 @@ func (S *Scanner) next() {
 
 
 // Init prepares the scanner S to tokenize the text src. Calls to Scan
-// will use the error handler err if they encounter a syntax error. The boolean\n-// scan_comments specifies whether comments should be recognized and returned\n-// by Scan as token.COMMENT. If scan_comments is false, they are treated as\n-// white space and ignored.\n+// will use the error handler err if they encounter a syntax error and\n+// err is not nil. Also, for each error encountered, the Scanner field\n+// ErrorCount is incremented by one. The boolean scan_comments specifies\n+// whether comments should be recognized and returned by Scan as COMMENT\n+// tokens. If scan_comments is false, they are treated as white space and\n+// ignored.\n //
 func (S *Scanner) Init(src []byte, err ErrorHandler, scan_comments bool) {
  	// Explicitly initialize all fields since a scanner may be reused.
  	//
 @@ -82,6 +87,7 @@ func (S *Scanner) Init(src []byte, err ErrorHandler, scan_comments bool) {
  	S.scan_comments = scan_comments;
  	S.pos = token.Position{0, 1, 0};
  	S.offset = 0;
+\tS.ErrorCount = 0;\n  	S.next();
 }\n 
 @@ -105,7 +111,10 @@ func charString(ch int) string {
 
 
 func (S *Scanner) error(pos token.Position, msg string) {
-\tS.err.Error(pos, msg);\n+\tif S.err != nil {\n+\t\tS.err.Error(pos, msg);\n+\t}\n+\tS.ErrorCount++;\n }\n 
 
 
 @@ -374,6 +383,13 @@ func (S *Scanner) switch4(tok0, tok1 token.Token, ch2 int, tok2, tok3 token.Toke
 // the token tok, and the literal text lit corresponding to the
 // token. The source end is indicated by token.EOF.
 //
+// For more tolerant parsing, Scan will return a valid token if
+// possible even if a syntax error was encountered. Thus, even
+// if the resulting token sequence contains no illegal tokens,
+// a client may not assume that no error occurred. Instead it
+// must check the scanner\'s ErrorCount or the number of calls
+// of the error handler, if there was one installed.\n+//
 func (S *Scanner) Scan() (pos token.Position, tok token.Token, lit []byte) {
  scan_again:
  	// skip white space
@@ -462,12 +478,14 @@ scan_again:
 // Tokenize calls a function f with the token position, token value, and token
 // text for each token in the source src. The other parameters have the same
 // meaning as for the Init function. Tokenize keeps scanning until f returns
-// false (usually when the token value is token.EOF).\n+// false (usually when the token value is token.EOF). The result is the number\n+// of errors encountered.\n //
-func Tokenize(src []byte, err ErrorHandler, scan_comments bool, f func (pos token.Position, tok token.Token, lit []byte) bool) {\n+func Tokenize(src []byte, err ErrorHandler, scan_comments bool, f func (pos token.Position, tok token.Token, lit []byte) bool) int {\n  	var s Scanner;\n  	s.Init(src, err, scan_comments);\n  	for f(s.Scan()) {\n  		// action happens in f\n  	}\n+\treturn s.ErrorCount;\n }\ndiff --git a/src/lib/go/scanner_test.go b/src/lib/go/scanner_test.go
index bbe0b7602d..f6edea10a9 100644
--- a/src/lib/go/scanner_test.go
+++ b/src/lib/go/scanner_test.go
@@ -188,7 +188,7 @@ func TestScan(t *testing.T) {
  	// verify scan
  	index := 0;
  	eloc := token.Position{0, 1, 1};
-\tscanner.Tokenize(io.StringBytes(src), &TestErrorHandler{t}, true,\n+\tnerrors := scanner.TokenBytes(src), &TestErrorHandler{t}, true,\n  		func (pos token.Position, tok token.Token, litb []byte) bool {
  		\t\te := elt{token.EOF, \"\", special};
  		\t\tif index < len(tokens) {
 @@ -223,6 +223,9 @@ func TestScan(t *testing.T) {
  		\t\treturn tok != token.EOF;
  		\t}
  	);\n+\tif nerrors != 0 {\n+\t\tt.Errorf(\"found %d errors\", nerrors);\n+\t}\n }\n 
 
 
 @@ -231,7 +234,7 @@ func TestInit(t *testing.T) {
  	var s scanner.Scanner;
 
  	// 1st init
-\ts.Init(io.StringBytes(\"if true { }\"), &TestErrorHandler{t}, false);\n+\ts.Init(io.StringBytes(\"if true { }\"), nil, false);\n  	s.Scan();  // if
  	s.Scan();  // true
  	pos, tok, lit := s.Scan();  // {
 @@ -240,9 +243,13 @@ func TestInit(t *testing.T) {
  	}\n 
  	// 2nd init
-\ts.Init(io.StringBytes(\"go true { ]\"), &TestErrorHandler{t}, false);\n+\ts.Init(io.StringBytes(\"go true { ]\"), nil, false);\n  	pos, tok, lit = s.Scan();  // go
  	if tok != token.GO {\n  	\t\tt.Errorf(\"bad token: got %s, expected %s\", tok.String(), token.GO);\n  	}\n+\n+\tif s.ErrorCount != 0 {\n+\t\tt.Errorf(\"found %d errors\", s.ErrorCount);\n+\t}\n }\n

変更の背景

このコミットが行われた2009年3月は、Go言語がまだ一般に公開される前の初期開発段階にありました。この時期のコミットは、言語の基本的な設計や標準ライブラリの基盤が形成されていく過程を反映しています。

字句解析器(スキャナー)は、コンパイラやインタプリタのフロントエンドにおいて非常に重要なコンポーネントです。初期の設計では、scannerパッケージのInitメソッドはErrorHandlerインターフェースの実装を必須としていました。これは、字句解析中にエラーが発生した場合に、そのエラーを報告するためのコールバックメカニズムを提供するためです。

しかし、すべての利用シナリオにおいてエラーハンドラが必須であるとは限りません。例えば、単にトークン列を生成したいだけで、エラーが発生しても処理を中断せずに続行したい場合や、エラーの報告は後でまとめて行いたい場合などです。また、エラーが発生した回数をプログラム的に取得したいというニーズも考えられます。

このコミットは、このような背景から、scannerの柔軟性と実用性を向上させるために導入されました。具体的には、以下の2つの主要な改善が図られています。

  1. エラーハンドラの任意化: ErrorHandlerの実装を必須ではなく任意とすることで、ユーザーがエラーハンドリングの挙動をより細かく制御できるようになります。エラーハンドラがnilの場合でもスキャナーがクラッシュすることなく動作を継続できるようになります。
  2. エラーカウンタの導入: 字句解析中に発生したエラーの総数を追跡するためのErrorCountフィールドをScanner構造体に追加することで、エラーの発生状況をプログラム的に把握できるようになります。これにより、エラーハンドラを介した報告とは別に、エラーの統計情報を収集することが可能になります。

これらの変更は、Go言語の設計哲学である「シンプルさ」と「実用性」を反映しており、開発者がscannerをより多様な状況で利用できるようにするための重要なステップでした。

前提知識の解説

このコミットの変更内容を理解するためには、以下の概念について基本的な知識が必要です。

1. 字句解析 (Lexical Analysis / Tokenization)

字句解析は、コンパイラやインタプリタの最初のフェーズです。ソースコードの文字列を読み込み、それを意味のある最小単位である「トークン」の列に変換します。例えば、if x > 0 { ... }というコードは、if (キーワード), x (識別子), > (演算子), 0 (整数リテラル), { (区切り文字) といったトークンに分解されます。

Go言語では、text/scannerパッケージがこの字句解析の機能を提供します。

2. tokenパッケージ

Go言語のtokenパッケージは、字句解析器が生成するトークンの種類を定義する定数(例: token.IDENTtoken.INTtoken.ADDなど)や、ソースコード内の位置情報(行番号、列番号、オフセットなど)を表すtoken.Position構造体を提供します。

3. scannerパッケージ

scannerパッケージは、Go言語のソースコード(または類似のテキスト)をトークンに分解するためのScanner構造体を提供します。主な機能は以下の通りです。

  • Scanner構造体: 字句解析の状態(現在の読み取り位置、次の文字など)を保持します。
  • Initメソッド: スキャナーを初期化し、解析対象のソースコード、エラーハンドラ、コメントをトークンとして扱うかどうかの設定を行います。
  • Scanメソッド: 次のトークンを読み込み、その位置、種類、リテラル値を返します。
  • ErrorHandlerインターフェース: 字句解析中にエラーが発生した場合に呼び出されるErrorメソッドを定義します。

4. インターフェース (Interfaces)

Go言語のインターフェースは、メソッドのシグネチャの集合を定義します。特定のインターフェースを実装する型は、そのインターフェースが定義するすべてのメソッドを提供する必要があります。ErrorHandlerは、Error(pos token.Position, msg string)というメソッドを持つインターフェースです。

5. エラーハンドリングのパターン

プログラミングにおけるエラーハンドリングには様々なパターンがあります。

  • 即時報告: エラーが発生した時点で、登録されたハンドラにエラーを報告し、必要に応じて処理を中断します。
  • エラーコード/カウンタ: エラーが発生しても処理を継続し、エラーの有無や数を戻り値や内部カウンタで示します。呼び出し元が後でこれらの情報をチェックします。
  • パニック/リカバリ: Go言語特有のメカニズムで、予期せぬエラーが発生した場合にパニックを発生させ、deferrecoverを使って処理を回復させることができます。

このコミットは、scannerにおいて「即時報告」と「エラーコード/カウンタ」の両方のアプローチを柔軟に選択できるようにするものです。

技術的詳細

このコミットは、scannerパッケージのScanner構造体と関連するメソッドに、以下の重要な技術的変更を加えています。

1. ErrorHandlerの任意化

変更前は、ErrorHandlerインターフェースの実装がScannerInitメソッドに必須でした。これは、scanner.goのコメントで「An implementation of an ErrorHandler must be provided to the Scanner.」と明記されていました。

変更後は、このコメントが「An implementation of an ErrorHandler may be provided to the Scanner.」と修正され、エラーハンドラが任意であることが示されています。

これを実現するために、Scanner構造体のerrフィールドのコメントが「// error reporting; or nil」に変更され、nilが許容されるようになりました。

そして、errorメソッド(スキャナー内部でエラーを報告する際に呼び出される)において、以下のチェックが追加されました。

func (S *Scanner) error(pos token.Position, msg string) {
	if S.err != nil { // エラーハンドラがnilでない場合のみ呼び出す
		S.err.Error(pos, msg);
	}
	S.ErrorCount++; // エラーカウンタは常にインクリメント
}

この変更により、S.errnilの場合でもS.err.Errorが呼び出されなくなり、スキャナーがパニックを起こすことなく動作を継続できるようになりました。

2. エラーカウンタ ErrorCount の導入

Scanner構造体にErrorCount intという新しい公開フィールドが追加されました。このフィールドは、字句解析中に発生したエラーの総数を保持します。

  • 初期化: Scanner.Initメソッド内でS.ErrorCount = 0;と初期化されます。これにより、スキャナーが再利用される際にもエラーカウントがリセットされることが保証されます。
  • インクリメント: errorメソッドが呼び出されるたびに、S.ErrorCount++によってインクリメントされます。これは、エラーハンドラが設定されているかどうかにかかわらず、常に実行されます。

このErrorCountフィールドは「// public state - ok to modify」とコメントされており、外部から参照・変更可能であることが示唆されていますが、通常は参照のみに利用されます。

3. Scanメソッドの挙動とドキュメントの変更

Scanメソッドのドキュメントに、以下の重要な説明が追加されました。

// For more tolerant parsing, Scan will return a valid token if
// possible even if a syntax error was encountered. Thus, even
// if the resulting token sequence contains no illegal tokens,
// a client may not assume that no error occurred. Instead it
// must check the scanner's ErrorCount or the number of calls
// of the error handler, if there was one installed.

これは、スキャナーがエラーに遭遇した場合でも、可能な限り有効なトークンを返そうとする「より寛容なパース」の考え方を示しています。したがって、Scanメソッドがtoken.ILLEGALのようなエラーを示すトークンを返さなかったとしても、エラーが発生していないとは限りません。クライアントは、エラーの有無を確認するために、Scanner.ErrorCountをチェックするか、設定されている場合はエラーハンドラの呼び出し回数を数える必要があることを明示しています。

4. Tokenize関数の戻り値の変更

Tokenize関数は、ソースコードをトークン化し、各トークンに対してコールバック関数fを呼び出すユーティリティ関数です。変更前はvoid(何も返さない)でしたが、変更後はintを返すようになりました。

// 変更前:
// func Tokenize(src []byte, err ErrorHandler, scan_comments bool, f func (pos token.Position, tok token.Token, lit []byte) bool) {
// 変更後:
func Tokenize(src []byte, err ErrorHandler, scan_comments bool, f func (pos token.Position, tok token.Token, lit []byte) bool) int {
	var s Scanner;
	s.Init(src, err, scan_comments);
	for f(s.Scan()) {
		// action happens in f
	}
	return s.ErrorCount; // スキャナーのエラーカウントを返す
}

この変更により、Tokenize関数を呼び出した側は、字句解析が完了した後に発生したエラーの総数を直接取得できるようになりました。これは、エラーハンドラを登録しない場合でもエラーの発生状況を把握できるという、ErrorCount導入のメリットを最大限に活用するための変更です。

5. テストコードの変更 (scanner_test.go)

テストコードもこれらの変更に合わせて更新されています。

  • TestScan関数では、scanner.Tokenizeの戻り値(エラーカウント)を受け取るようになり、その値が0でない場合にテストを失敗させるアサーションが追加されました。
  • TestInit関数では、s.Initの呼び出しでエラーハンドラとしてnilを渡すケースが追加され、その後にs.ErrorCountが0であることを確認するアサーションが追加されました。これにより、エラーハンドラがnilの場合でもスキャナーが正しく初期化され、エラーカウントが期待通りに動作することが検証されています。

これらの変更は、Go言語の字句解析器がより柔軟で、エラー耐性があり、かつエラー情報をプログラム的に利用しやすいように進化していることを示しています。

コアとなるコードの変更箇所

このコミットにおけるコアとなるコードの変更箇所は、主にsrc/lib/go/scanner.goファイルに集中しています。

  1. ErrorHandlerインターフェースのコメント変更:

    --- a/src/lib/go/scanner.go
    +++ b/src/lib/go/scanner.go
    @@ -16,10 +16,10 @@ import (
     )
     
     
    -// An implementation of an ErrorHandler must be provided to the Scanner.
    -// If a syntax error is encountered, Error is called with a position and
    -// an error message. The position points to the beginning of the offending
    -// token.
    +// An implementation of an ErrorHandler may be provided to the Scanner.
    +// If a syntax error is encountered and a handler was installed, Error
    +// is called with a position and an error message. The position points
    +// to the beginning of the offending token.
     //
     type ErrorHandler interface {
      	Error(pos token.Position, msg string);
    
  2. Scanner構造体へのErrorCountフィールド追加とerrフィールドのコメント変更:

    --- a/src/lib/go/scanner.go
    +++ b/src/lib/go/scanner.go
    @@ -34,13 +34,16 @@ type ErrorHandler interface {
     type Scanner struct {
      	// immutable state
      	src []byte;  // source
    -\terr ErrorHandler;  // error reporting
    +\terr ErrorHandler;  // error reporting; or nil
      	scan_comments bool;  // if set, comments are reported as tokens
      
      	// scanning state
      	pos token.Position;  // previous reading position (position before ch)
      	offset int;  // current reading offset (position after ch)
      	ch int;  // one char look-ahead
    +\n+\t// public state - ok to modify
    +\tErrorCount int;  // number of errors encountered
     }\n 
     
    
  3. Scanner.InitメソッドでのErrorCount初期化とコメント変更:

    --- a/src/lib/go/scanner.go
    +++ b/src/lib/go/scanner.go
    @@ -70,10 +73,12 @@ func (S *Scanner) next() {
     
     
     // Init prepares the scanner S to tokenize the text src. Calls to Scan
    -// will use the error handler err if they encounter a syntax error. The boolean
    -// scan_comments specifies whether comments should be recognized and returned
    -// by Scan as token.COMMENT. If scan_comments is false, they are treated as
    -// white space and ignored.
    +// will use the error handler err if they encounter a syntax error and
    +// err is not nil. Also, for each error encountered, the Scanner field
    +// ErrorCount is incremented by one. The boolean scan_comments specifies
    +// whether comments should be recognized and returned by Scan as COMMENT
    +// tokens. If scan_comments is false, they are treated as white space and
    +// ignored.
     //
     func (S *Scanner) Init(src []byte, err ErrorHandler, scan_comments bool) {
      	// Explicitly initialize all fields since a scanner may be reused.
      	//
    @@ -82,6 +87,7 @@ func (S *Scanner) Init(src []byte, err ErrorHandler, scan_comments bool) {
      	S.scan_comments = scan_comments;
      	S.pos = token.Position{0, 1, 0};
      	S.offset = 0;
    +\tS.ErrorCount = 0;\n      	S.next();
     }\n 
    
  4. Scanner.errorメソッドでのエラーハンドラ呼び出し条件追加とErrorCountインクリメント:

    --- a/src/lib/go/scanner.go
    +++ b/src/lib/go/scanner.go
    @@ -105,7 +111,10 @@ func charString(ch int) string {
     
     
     func (S *Scanner) error(pos token.Position, msg string) {
    -\tS.err.Error(pos, msg);\n+\tif S.err != nil {\n+\t\tS.err.Error(pos, msg);\n+\t}\n+\tS.ErrorCount++;\n     }\n 
    
  5. Scanner.Scanメソッドのコメント変更:

    --- a/src/lib/go/scanner.go
    +++ b/src/lib/go/scanner.go
    @@ -374,6 +383,13 @@ func (S *Scanner) switch4(tok0, tok1 token.Token, ch2 int, tok2, tok3 token.Toke
     // the token tok, and the literal text lit corresponding to the
     // token. The source end is indicated by token.EOF.
     //
    +// For more tolerant parsing, Scan will return a valid token if
    +// possible even if a syntax error was encountered. Thus, even
    +// if the resulting token sequence contains no illegal tokens,
    +// a client may not assume that no error occurred. Instead it
    +// must check the scanner\'s ErrorCount or the number of calls
    +// of the error handler, if there was one installed.\n+//
     func (S *Scanner) Scan() (pos token.Position, tok token.Token, lit []byte) {
      scan_again:
      	// skip white space
    
  6. Tokenize関数の戻り値の型変更とErrorCountの返却:

    --- a/src/lib/go/scanner.go
    +++ b/src/lib/go/scanner.go
    @@ -462,12 +478,14 @@ scan_again:
     // Tokenize calls a function f with the token position, token value, and token
     // text for each token in the source src. The other parameters have the same
     // meaning as for the Init function. Tokenize keeps scanning until f returns
    -// false (usually when the token value is token.EOF).\n+// false (usually when the token value is token.EOF). The result is the number\n+// of errors encountered.\n //
    -func Tokenize(src []byte, err ErrorHandler, scan_comments bool, f func (pos token.Position, tok token.Token, lit []byte) bool) {\n+func Tokenize(src []byte, err ErrorHandler, scan_comments bool, f func (pos token.Position, tok token.Token, lit []byte) bool) int {\n      	var s Scanner;\n      	s.Init(src, err, scan_comments);\n      	for f(s.Scan()) {\n      		// action happens in f
      	}\n+\treturn s.ErrorCount;\n     }\n    ```
    
    

コアとなるコードの解説

上記の変更箇所は、scannerパッケージの動作と利用方法に直接影響を与える重要な部分です。

  1. ErrorHandlerインターフェースのコメント変更:

    • 変更前は、ErrorHandlerの実装がScannerに「必須」であると明記されていました。これは、エラーハンドラを提供しないとスキャナーが正しく動作しない、あるいはエラー時にパニックを起こす可能性を示唆していました。
    • 変更後は、「任意」であると修正されました。これは、エラーハンドラがnilであってもスキャナーが動作し続けることを意図していることを明確に示しています。
  2. Scanner構造体へのErrorCountフィールド追加とerrフィールドのコメント変更:

    • Scanner構造体にErrorCount intという新しいフィールドが追加されました。これは、字句解析中に発生したエラーの総数を追跡するためのカウンタです。このカウンタは、エラーハンドラが設定されているかどうかにかかわらず、常にインクリメントされます。これにより、エラーハンドラを介した詳細な報告とは別に、エラーの発生回数を簡単に取得できるようになります。
    • err ErrorHandlerフィールドのコメントが「// error reporting; or nil」に変更されました。これは、errフィールドにnilを代入することが許容され、その場合でもスキャナーが正常に動作することを明示しています。
  3. Scanner.InitメソッドでのErrorCount初期化とコメント変更:

    • S.ErrorCount = 0;という行が追加され、Scannerが初期化される際にErrorCountが必ず0にリセットされるようになりました。これにより、Scannerインスタンスが再利用される場合でも、以前の解析結果によるエラーカウントが残らないことが保証されます。
    • コメントも更新され、errnilでない場合にエラーハンドラが使用されることと、ErrorCountがインクリメントされることが明記されました。
  4. Scanner.errorメソッドでのエラーハンドラ呼び出し条件追加とErrorCountインクリメント:

    • if S.err != nil { S.err.Error(pos, msg); }という条件分岐が追加されました。これにより、S.err(エラーハンドラ)がnilの場合にはErrorメソッドが呼び出されなくなり、nilポインタデリファレンスによるパニックを防ぎます。
    • S.ErrorCount++;という行が追加され、errorメソッドが呼び出されるたびに(つまり、字句解析エラーが検出されるたびに)ErrorCountがインクリメントされるようになりました。これは、エラーハンドラの有無にかかわらず、エラーの総数を正確に追跡するための重要な変更です。
  5. Scanner.Scanメソッドのコメント変更:

    • Scanメソッドのドキュメントに、スキャナーが「より寛容なパース」を行うこと、つまりエラーが発生しても可能な限り有効なトークンを返すように努めることが追記されました。
    • これにより、Scanがエラーを示すトークンを返さなかったとしても、エラーが発生していないとは限らないため、クライアントはScanner.ErrorCountまたはエラーハンドラの呼び出し回数をチェックしてエラーの有無を確認する必要があることが強調されています。これは、エラーハンドラを任意にしたことと、エラーカウンタを導入したことの利用方法を明確にするものです。
  6. Tokenize関数の戻り値の型変更とErrorCountの返却:

    • Tokenize関数の戻り値の型がvoidからintに変更され、s.ErrorCount(字句解析中に発生したエラーの総数)を返すようになりました。
    • これにより、Tokenize関数を呼び出す側は、エラーハンドラを登録しなくても、字句解析の完了後にエラーの総数を直接取得できるようになり、エラー処理の柔軟性が向上しました。

これらの変更は、Go言語の字句解析器がより堅牢で、柔軟なエラーハンドリングを提供し、開発者がエラー情報をより簡単に利用できるようにするための基盤を築いたと言えます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (text/scanner, go/token): 上記の関連リンクに記載。
  • Go言語のGitHubリポジトリ: コミット履歴とソースコードの差分を確認するために使用。
  • 字句解析に関する一般的な情報 (コンパイラ理論): 字句解析の概念、エラーハンドリングのパターンを理解するために参照。
  • Go言語の初期開発に関する議論やメーリングリストのアーカイブ (もしあれば): このコミットの背景にある具体的な議論を理解するために役立つ可能性があるが、今回は直接参照せず、コミットメッセージとコードの変更から推測した。