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

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

このコミットは、Go言語の初期のprettyパッケージにおけるソースコード位置の管理方法を根本的に変更するものです。具体的には、抽象構文木(AST)ノードやパーサー、プリンターがソースコード内の位置を示すために使用していた単純な整数型のオフセット(Pos int)を、より詳細な情報(行番号、列番号など)を含むscanner.Location型に置き換えています。これにより、コンパイラのフロントエンドにおけるエラー報告やコード生成の精度が向上し、より堅牢なツールチェインの基盤が築かれました。

コミット

commit 40e204b9eb14eea992dfb94bc61b1833f83927b4
Author: Robert Griesemer <gri@golang.org>
Date:   Wed Mar 11 12:52:11 2009 -0700

    - update pretty sources to match new scanner interface
    
    R=r
    OCL=26129
    CL=26131

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

https://github.com/golang/go/commit/40e204b9eb14eea992dfb94bc61b1833f83927b4

元コミット内容

このコミットの元のメッセージは非常に簡潔です。

- update pretty sources to match new scanner interface

これは、「新しいスキャナーインターフェースに合わせてprettyパッケージのソースを更新する」という意図を示しています。この簡潔なメッセージからは、スキャナーインターフェースの変更が、ソースコードの位置情報表現に影響を与え、それに対応するためにprettyパッケージの各コンポーネント(AST、パーサー、プリンターなど)が修正されたことが読み取れます。

変更の背景

Go言語の初期開発段階では、コンパイラやツールチェインの各コンポーネントが急速に進化していました。このコミットが行われた2009年3月は、Go言語が一般に公開される前の重要な時期であり、言語仕様や標準ライブラリの設計が活発に行われていました。

この変更の背景には、Goコンパイラのフロントエンドにおけるソースコード位置の管理をより洗練されたものにする必要があったと考えられます。従来のint型によるバイトオフセットのみの管理では、エラーメッセージの表示やデバッグ情報の生成において、行番号や列番号といったより人間が理解しやすい情報を提供することが困難でした。

新しいscanner.Locationインターフェースの導入は、以下の目的を達成するためと考えられます。

  1. 正確なエラー報告: コンパイルエラーや構文エラーが発生した際に、ファイル名、行番号、列番号を正確に報告できるようにするため。これにより、開発者は問題の箇所を特定しやすくなります。
  2. ツールチェインの連携強化: スキャナー、パーサー、AST、プリンターといった異なるコンポーネント間で、一貫したソースコード位置情報表現を共有することで、ツールチェイン全体の連携と保守性を向上させるため。
  3. リファクタリングの容易化: ソースコード位置の抽象化により、将来的なスキャナーやパーサーの実装変更が、ASTや他のコンポーネントに与える影響を最小限に抑えるため。
  4. デバッグ情報の充実: デバッガやプロファイラなどのツールが、より詳細なソースコード位置情報に基づいて動作できるようにするため。

prettyパッケージは、Go言語のソースコードを解析し、整形(pretty-printing)するための初期のツールであり、コンパイラのフロントエンドの重要な一部でした。このパッケージが新しいスキャナーインターフェースに追従することは、Go言語のツールチェイン全体の整合性を保つ上で不可欠でした。

前提知識の解説

このコミットを理解するためには、以下の概念に関する前提知識が必要です。

  1. コンパイラのフロントエンド:

    • スキャナー(字句解析器): ソースコードを読み込み、意味のある最小単位である「トークン」(キーワード、識別子、演算子など)のストリームに変換するコンポーネントです。このコミットでは、スキャナーが生成するトークンに付随する位置情報の形式が変更されています。
    • パーサー(構文解析器): スキャナーが生成したトークンのストリームを読み込み、言語の文法規則に従って「抽象構文木(AST)」を構築するコンポーネントです。
    • 抽象構文木(AST: Abstract Syntax Tree): ソースコードの構文構造を木構造で表現したものです。各ノードは、プログラムの要素(式、文、宣言など)を表し、その属性としてソースコード内の位置情報を持つことが一般的です。
  2. Go言語のtokenパッケージ:

    • Go言語の標準ライブラリには、字句解析器が生成するトークンを定義するtokenパッケージが存在します。このパッケージは、トークンの種類(例: token.IDENTtoken.LPAREN)や、トークンの優先順位などを定義します。
  3. Go言語のscannerパッケージ:

    • Go言語の標準ライブラリには、字句解析を行うためのscannerパッケージが存在します。このパッケージは、ソースコードをスキャンし、トークンとそれに対応する位置情報を生成する機能を提供します。このコミットで言及されている「新しいスキャナーインターフェース」は、このscannerパッケージの進化を指している可能性が高いです。特に、scanner.Location型は、ファイル内のバイトオフセットだけでなく、行番号や列番号といった情報をカプセル化するために導入されたと考えられます。
  4. ソースコード位置情報:

    • コンパイラや開発ツールにおいて、ソースコード内の特定の位置を指し示すための情報です。通常、ファイル名、行番号、列番号、そしてバイトオフセットが含まれます。正確な位置情報は、エラーメッセージの表示、デバッグ、コードエディタの機能(例: 定義へのジャンプ)に不可欠です。
  5. prettyパッケージ(初期のGoツール):

    • このコミットで変更されているusr/gri/prettyは、Go言語の初期のツールチェインの一部であり、Goソースコードの整形(pretty-printing)や解析に関連する機能を提供していたと考えられます。これは、現在のgo/ast, go/parser, go/printerパッケージの前身、あるいはそれらと関連する実験的な実装であった可能性があります。

技術的詳細

このコミットの核心は、ソースコード位置の表現方法をintからscanner.Locationへ変更した点にあります。

scanner.Locationの導入

  • 目的: 従来のint型による位置情報は、単なるバイトオフセットであり、行番号や列番号といった人間が読みやすい情報を提供できませんでした。scanner.Locationは、この不足を補い、よりリッチな位置情報(ファイル内のバイトオフセット、行番号、列番号)をカプセル化することを目的としています。
  • 構造(推測): コミットの変更内容から、scanner.Locationは少なくともPos(バイトオフセット)、Line(行番号)、Col(列番号)といったフィールドを持つ構造体であると推測されます。これにより、エラーハンドリングやデバッグ情報の生成が格段に容易になります。

ASTノードの変更 (usr/gri/pretty/ast.go)

  • Node構造体の削除: 以前はNode構造体がPos intTok intを持っていましたが、これが削除されました。これは、各ASTノードが直接scanner.Locationを持つように設計が変更されたことを示唆しています。
  • Pos_ intからLoc_ scanner.Locationへの変更: BadExpr, Ident, BinaryExpr, UnaryExpr, BasicLit, FunctionLit, Group, Selector, TypeGuard, Index, Call, Ellipsis, TypeType, ArrayType, StructType, PointerType, FunctionType, InterfaceType, SliceType, MapType, ChannelTypeなど、ほぼ全てのASTノードのフィールドがPos_ intからLoc_ scanner.Locationに変更されています。これにより、各ノードが自身の正確なソースコード位置を保持できるようになります。
  • Pos()メソッドからLoc()メソッドへの変更: 各ASTノードが持つ位置情報を返すメソッドも、Pos() intからLoc() scanner.Locationに変更されました。これにより、ASTを走査する際に、より詳細な位置情報を取得できるようになります。
  • StructTypeInterfaceTypeEndフィールド: これらの構造体では、End intEnd scanner.Locationに変更されています。これは、構造体やインターフェースの終端を示す位置も、より詳細な情報を持つようになったことを意味します。

エラーハンドリングの変更 (usr/gri/pretty/compilation.go)

  • errorHandlerの変更: errorHandler構造体からerrpos intが削除され、errline intが追加されています。これは、エラーの重複報告を避けるためのロジックが、バイトオフセットではなく行番号に基づいて行われるようになったことを示唆しています。
  • ErrorMsgErrorメソッドの引数変更: ErrorMsgErrorメソッドの引数がpos intからloc scanner.Locationに変更されています。これにより、エラーメッセージの出力時にscanner.Locationから直接行番号と列番号を取得して表示できるようになりました。
  • LineCol関数のコメントアウト: 以前はバイトオフセットから行番号と列番号を計算するLineCol関数が存在しましたが、これがコメントアウトされています。これは、scanner.Locationが既にこの情報を持っているため、この関数が不要になったことを明確に示しています。

パーサーの変更 (usr/gri/pretty/parser.go)

  • ScannerおよびErrorHandlerインターフェースの削除: パーサーが依存していたScannerErrorHandlerのローカルなインターフェース定義が削除されました。これは、Go言語の標準scannerパッケージが提供するインターフェースを直接使用するようになったことを意味します。
  • Parser構造体の変更: Parser構造体のscanner Scannerフィールドがscanner *scanner.Scannerに、err ErrorHandlerフィールドがerr scanner.ErrorHandlerに変更されています。これにより、パーサーは標準のscannerパッケージと直接連携するようになります。
  • pos intからloc scanner.Locationへの変更: パーサーが現在処理しているトークンの位置を示すP.posフィールドがP.loc scanner.Locationに変更されました。
  • next0()メソッドの変更: P.pos, P.tok, P.val = P.scanner.Scan()P.loc, P.tok, P.val = P.scanner.Scan()に変更され、スキャナーから直接scanner.Locationを受け取るようになりました。
  • エラー報告の変更: P.error(pos, msg)P.error(loc, msg)に変更され、エラー報告もscanner.Locationを使用するようになりました。
  • ASTノード生成時の位置情報: ast.Ident, ast.BinaryExpr, ast.Selector, ast.ArrayType, ast.Ellipsis, ast.ChannelType, ast.FunctionLit, ast.BasicLit, ast.Group, ast.Call, ast.LabelDecl, ast.ExpressionStat, ast.ControlFlowStat, ast.IfStat, ast.ForStat, ast.CaseClause, ast.SwitchStat, ast.SelectStat, ast.EmptyStat, ast.BadStat, ast.ImportDecl, ast.ConstDecl, ast.TypeDecl, ast.VarDecl, ast.DeclList, ast.FuncDecl, ast.BadDecl, ast.Programなど、パーサーがASTノードを生成する際に、P.posの代わりにP.locを渡すように変更されています。

プリンターの変更 (usr/gri/pretty/printer.go)

  • TaggedStringメソッドの変更: TaggedString(pos int, ...)TaggedString(loc scanner.Location, ...)に変更され、位置情報としてscanner.Locationを受け取るようになりました。内部ではloc.Posを使用してバイトオフセットを取得しています。
  • StringおよびTokenメソッドの変更: String(pos int, ...)String(loc scanner.Location, ...)に、Token(pos int, ...)Token(loc scanner.Location, ...)に変更されています。
  • Errorメソッドの変更: Error(pos int, ...)Error(loc scanner.Location, ...)に変更され、エラーメッセージの出力時にloc.Posを使用するようになりました。
  • ASTノードからの位置情報取得: HtmlIdentifier, DoBadExpr, DoBinaryExpr, DoUnaryExpr, DoBasicLit, DoFunctionLit, DoGroup, DoSelector, DoTypeGuard, DoIndex, DoCall, DoEllipsis, DoArrayType, DoTypeType, DoStructType, DoPointerType, DoFunctionType, DoInterfaceType, DoMapType, DoChannelTypeなど、プリンターがASTノードから位置情報を取得する際に、x.Pos_の代わりにx.Loc_を使用するように変更されています。
  • noloc変数の導入: scanner.Location型のゼロ値としてnoloc変数が導入されています。これは、位置情報が特に重要でない場合や、一時的なプレースホルダーとして使用されることを示唆しています。

usr/gri/pretty/typechecker.go

  • このファイルでは、Pos()メソッドの呼び出しがLoc()メソッドの呼び出しに置き換えられているのみで、大きなロジックの変更はありません。これは、型チェッカーがASTノードから位置情報を取得する際に、新しいインターフェースに準拠するように修正されたことを示しています。

これらの変更は、Go言語のコンパイラフロントエンドにおける位置情報管理の統一と強化を目指したものであり、より堅牢で使いやすい開発ツールを提供するための重要なステップでした。

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

このコミットにおけるコアとなるコードの変更は、主に以下のファイルに集中しています。

  1. usr/gri/pretty/ast.go:

    • ASTノードの構造体定義において、Pos_ intフィールドがLoc_ scanner.Locationに置き換えられました。
    • Pos() intメソッドがLoc() scanner.Locationメソッドに置き換えられました。
    • Node構造体が削除されました。
  2. usr/gri/pretty/parser.go:

    • Parser構造体のscannererrフィールドの型が、標準のscannerパッケージの型に更新されました。
    • パーサーが現在処理しているトークンの位置を示すP.pos intP.loc scanner.Locationに置き換えられました。
    • ASTノードを生成する際に、P.posの代わりにP.locを渡すように変更されました。
    • エラー報告のメソッド(error, expect)がscanner.Locationを使用するように変更されました。
  3. usr/gri/pretty/printer.go:

    • プリンターのメソッド(TaggedString, String, Token, Error)が、位置情報としてscanner.Locationを受け取るように変更されました。
    • ASTノードから位置情報を取得する際に、x.Pos_の代わりにx.Loc_を使用するように変更されました。
  4. usr/gri/pretty/compilation.go:

    • エラーハンドラーがscanner.Locationを使用してエラーを報告するように変更されました。
    • バイトオフセットから行/列を計算するLineCol関数が不要になり、コメントアウトされました。

これらの変更は、Go言語のコンパイラフロントエンド全体で、ソースコード位置の表現と利用方法を統一し、より詳細な位置情報を扱えるようにするためのものです。

コアとなるコードの解説

usr/gri/pretty/ast.go の変更例

--- a/usr/gri/pretty/ast.go
+++ b/usr/gri/pretty/ast.go
@@ -27,15 +28,6 @@ func assert(pred bool) {
 }
 
 
-// ----------------------------------------------------------------------------
-// All nodes have a source position and a token.
-
-type Node struct {
-	Pos int;  // source position (< 0 => unknown position)
-	Tok int;  // identifying token
-}
-
-
 // ----------------------------------------------------------------------------
 // Expressions
 
@@ -51,27 +43,27 @@ type (
 	Signature struct;
 
 	Expr interface {
-		Pos() int;
+		Loc() scanner.Location;
 		Visit(v ExprVisitor);
 	};
 	
 	BadExpr struct {
-		Pos_ int;
+		Loc_ scanner.Location;
 	};
 
 	Ident struct {
-		Pos_ int;
+		Loc_ scanner.Location;
 		Str string;
 	};
 
 	BinaryExpr struct {
-		Pos_ int;
+		Loc_ scanner.Location;
 		Tok int;
 		X, Y Expr;
 	};
 
 	UnaryExpr struct {
-		Pos_ int;
+		Loc_ scanner.Location;
 		Tok int;
 		X Expr;
 	};
@@ -215,29 +207,29 @@ type ExprVisitor interface {
 
 
 // TODO replace these with an embedded field
-func (x *BadExpr) Pos() int { return x.Pos_; }\n-func (x *Ident) Pos() int { return x.Pos_; }\n-func (x *BinaryExpr) Pos() int { return x.Pos_; }\n-func (x *UnaryExpr) Pos() int { return x.Pos_; }\n-func (x *ConcatExpr) Pos() int { return x.X.Pos(); }\n-func (x *BasicLit) Pos() int { return x.Pos_; }\n-func (x *FunctionLit) Pos() int { return x.Pos_; }\n-func (x *Group) Pos() int { return x.Pos_; }\n-func (x *Selector) Pos() int { return x.Pos_; }\n-func (x *TypeGuard) Pos() int { return x.Pos_; }\n-func (x *Index) Pos() int { return x.Pos_; }\n-func (x *Call) Pos() int { return x.Pos_; }\n-\n-func (x *Ellipsis) Pos() int { return x.Pos_; }\n-func (x *TypeType) Pos() int { return x.Pos_; }\n-func (x *ArrayType) Pos() int { return x.Pos_; }\n-func (x *StructType) Pos() int { return x.Pos_; }\n-func (x *PointerType) Pos() int { return x.Pos_; }\n-func (x *FunctionType) Pos() int { return x.Pos_; }\n-func (x *InterfaceType) Pos() int { return x.Pos_; }\n-func (x *SliceType) Pos() int { return x.Pos_; }\n-func (x *MapType) Pos() int { return x.Pos_; }\n-func (x *ChannelType) Pos() int { return x.Pos_; }\n+func (x *BadExpr) Loc() scanner.Location { return x.Loc_; }\n+func (x *Ident) Loc() scanner.Location { return x.Loc_; }\n+func (x *BinaryExpr) Loc() scanner.Location { return x.Loc_; }\n+func (x *UnaryExpr) Loc() scanner.Location { return x.Loc_; }\n+func (x *ConcatExpr) Loc() scanner.Location { return x.X.Loc(); }\n+func (x *BasicLit) Loc() scanner.Location { return x.Loc_; }\n+func (x *FunctionLit) Loc() scanner.Location { return x.Loc_; }\n+func (x *Group) Loc() scanner.Location { return x.Loc_; }\n+func (x *Selector) Loc() scanner.Location { return x.Loc_; }\n+func (x *TypeGuard) Loc() scanner.Location { return x.Loc_; }\n+func (x *Index) Loc() scanner.Location { return x.Loc_; }\n+func (x *Call) Loc() scanner.Location { return x.Loc_; }\n+\n+func (x *Ellipsis) Loc() scanner.Location { return x.Loc_; }\n+func (x *TypeType) Loc() scanner.Location { return x.Loc_; }\n+func (x *ArrayType) Loc() scanner.Location { return x.Loc_; }\n+func (x *StructType) Loc() scanner.Location { return x.Loc_; }\n+func (x *PointerType) Loc() scanner.Location { return x.Loc_; }\n+func (x *FunctionType) Loc() scanner.Location { return x.Loc_; }\n+func (x *InterfaceType) Loc() scanner.Location { return x.Loc_; }\n+func (x *SliceType) Loc() scanner.Location { return x.Loc_; }\n+func (x *MapType) Loc() scanner.Location { return x.Loc_; }\n+func (x *ChannelType) Loc() scanner.Location { return x.Loc_; }\n```

この変更は、ASTノードがソースコード内の位置情報をどのように保持するかを根本的に変えています。以前は`int`型の`Pos_`フィールドでバイトオフセットを保持し、`Pos()`メソッドでそれを返していました。しかし、このコミットでは`Node`構造体が削除され、各ASTノードが直接`scanner.Location`型の`Loc_`フィールドを持つようになりました。これにより、各ノードはバイトオフセットだけでなく、行番号や列番号といったより詳細な位置情報を直接保持できるようになります。`Pos()`メソッドも`Loc()`メソッドに変わり、`scanner.Location`を返すようになりました。

### `usr/gri/pretty/parser.go` の変更例

```diff
--- a/usr/gri/pretty/parser.go
+++ b/usr/gri/pretty/parser.go
@@ -16,36 +16,18 @@ import (
 	"fmt";
 	"vector";
 	"token";
+\t"scanner";
 	"ast";
 )
 
 
-// An implementation of an ErrorHandler must be provided to the Parser.\n-// If a syntax error is encountered, Error is called with the exact\n-// token position (the byte position of the token in the source) and the\n-// error message.\n-//\n-type ErrorHandler interface {\n-\tError(pos int, msg string);\n-}\n-\n-\n-// An implementation of a Scanner must be provided to the Parser.\n-// The parser calls Scan repeatedly to get a sequential stream of\n-// tokens. The source end is indicated by token.EOF.\n-//\n-type Scanner interface {\n-\tScan() (pos, tok int, lit []byte);\n-}\n-\n-\n // A Parser holds the parser\'s internal state while processing\n // a given text. It can be allocated as part of another data\n // structure but must be initialized via Init before use.\n //\n type Parser struct {\n-\tscanner Scanner;\n-\terr ErrorHandler;\n+\tscanner *scanner.Scanner;\n+\terr scanner.ErrorHandler;\
 \n 	// Tracing/debugging\n 	trace bool;\
@@ -54,7 +36,7 @@ type Parser struct {
 	comments *vector.Vector;\
 
 	// The next token\n-\tpos int;  // token source position\n+\tloc scanner.Location;  // token location\
 \ttok int;  // one token look-ahead\n \tval []byte;  // token value\
 \n@@ -105,23 +92,22 @@ func un/*trace*/(P *Parser) {\
 
 
 func (P *Parser) next0() {\
-\tvar val []byte;\n-\tP.pos, P.tok, P.val = P.scanner.Scan();\n+\tP.loc, P.tok, P.val = P.scanner.Scan();\
 \tP.opt_semi = false;\
 \n \tif P.trace {\n \t\tP.printIndent();\n \t\tswitch P.tok {\n \t\tcase token.IDENT, token.INT, token.FLOAT, token.CHAR, token.STRING:\n-\t\t\tfmt.Printf(\"[%d] %s = %s\\n\", P.pos, token.TokenString(P.tok), P.val);\n+\t\t\tfmt.Printf(\"%d:%d: %s = %s\\n\", P.loc.Line, P.loc.Col, token.TokenString(P.tok), P.val);\
 \t\tcase token.LPAREN:\n \t\t\t// don\'t print \'(\' - screws up selection in terminal window\n-\t\t\tfmt.Printf(\"[%d] LPAREN\\n\", P.pos);\n+\t\t\tfmt.Printf(\"%d:%d: LPAREN\\n\", P.loc.Line, P.loc.Col);\
 \t\tcase token.RPAREN:\n \t\t\t// don\'t print \')\' - screws up selection in terminal window\n-\t\t\tfmt.Printf(\"[%d] RPAREN\\n\", P.pos);\n+\t\t\tfmt.Printf(\"%d:%d: RPAREN\\n\", P.loc.Line, P.loc.Col);\
 \t\tdefault:\n-\t\t\tfmt.Printf(\"[%d] %s\\n\", P.pos, token.TokenString(P.tok));\n+\t\t\tfmt.Printf(\"%d:%d: %s\\n\", P.loc.Line, P.loc.Col, token.TokenString(P.tok));\
 \t\t}\n \t}\n }\
@@ -129,12 +115,12 @@ func (P *Parser) next0() {\
 
 func (P *Parser) next() {\
 \tfor P.next0(); P.tok == token.COMMENT; P.next0() {\
-\t\tP.comments.Push(&ast.Comment{P.pos, P.val});\n+\t\tP.comments.Push(&ast.Comment{P.loc, P.val});\
 \t}\n }\
 
 
-func (P *Parser) Init(scanner Scanner, err ErrorHandler, trace bool) {\
+func (P *Parser) Init(scanner *scanner.Scanner, err scanner.ErrorHandler, trace bool) {\
 \tP.scanner = scanner;\
 \tP.err = err;\
 \n@@ -148,8 +134,8 @@ func (P *Parser) Init(scanner Scanner, err ErrorHandler, trace bool) {\
 }\
 
 
-func (P *Parser) error(pos int, msg string) {\
-\tP.err.Error(pos, msg);\
+func (P *Parser) error(loc scanner.Location, msg string) {\
+\tP.err.Error(loc, msg);\
 }\
 
 
@@ -159,7 +145,7 @@ func (P *Parser) expect(tok int) {\
 \t\tif token.IsLiteral(P.tok) {\
 \t\t\tmsg += \" \" + string(P.val);\
 \t\t}\n-\t\tP.error(P.pos, msg);\n+\t\tP.error(P.loc, msg);\
 \t}\n \tP.next();  // make progress in any case\
 }\

パーサーの変更は、スキャナーとの連携方法とエラー報告のメカニズムに影響を与えています。

  • ScannerErrorHandlerのローカルインターフェースが削除され、Go標準のscannerパッケージの型を直接使用するようになりました。これは、Go言語のツールチェインが標準ライブラリのコンポーネントに統合されていく過程を示しています。
  • パーサーが次に処理するトークンの位置を保持するP.posP.locに変更され、scanner.Scan()から直接scanner.Locationを受け取るようになりました。
  • エラー報告を行うerrorメソッドもscanner.Locationを引数に取るようになり、より詳細なエラーメッセージの生成が可能になりました。

usr/gri/pretty/compilation.go の変更例

--- a/usr/gri/pretty/compilation.go
+++ b/usr/gri/pretty/compilation.go
@@ -35,23 +35,20 @@ type Flags struct {
 type errorHandler struct {
 	filename string;
 	src []byte;
-\tnerrors int;
-\tnwarnings int;
-\terrpos int;
 \tcolumns bool;
+\terrline int;
+\tnerrors int;
 }
 
 
 func (h *errorHandler) Init(filename string, src []byte, columns bool) {
 	h.filename = filename;
 	h.src = src;
-\th.nerrors = 0;
-\th.nwarnings = 0;
-\th.errpos = 0;
 \th.columns = columns;
 }
 
 
+/*
 // Compute (line, column) information for a given source position.
 func (h *errorHandler) LineCol(pos int) (line, col int) {
 	line = 1;
@@ -71,40 +68,30 @@ func (h *errorHandler) LineCol(pos int) (line, col int) {
 
 	return line, utf8.RuneCount(src[lpos : pos]);
 }
+*/
 
 
-func (h *errorHandler) ErrorMsg(pos int, msg string) {\n-\tprint(h.filename, \":\");\n-\tif pos >= 0 {\n-\t\t// print position\n-\t\tline, col := h.LineCol(pos);\n-\t\tprint(line, \":\");\n-\t\tif h.columns {\n-\t\t\tprint(col, \":\");\n-\t\t}\n+\tfmt.Printf(\"%s:%d:\", h.filename, loc.Line);\n+\tif h.columns {\n+\t\tfmt.Printf(\"%d:\", loc.Col);\
 \t}\n-\tprint(\" \", msg, \"\\n\");\n+\tfmt.Printf(\" %s\\n\", msg);\
 \n-\th.nerrors++;\n-\th.errpos = pos;\n+\th.errline = loc.Line;\
 \n+\th.nerrors++;\
 \tif h.nerrors >= 10 {\n \t\tsys.Exit(1);\n \t}\n }\
 
 
-func (h *errorHandler) Error(pos int, msg string) {\n-\t// only report errors that are sufficiently far away from the previous error\n+func (h *errorHandler) Error(loc scanner.Location, msg string) {\n+\t// only report errors that are on a new line \n \t// in the hope to avoid most follow-up errors\n-\tconst errdist = 20;\n-\tdelta := pos - h.errpos;  // may be negative!\n-\tif delta < 0 {\n-\t\tdelta = -delta;\n-\t}\n-\n-\tif delta > errdist || h.nerrors == 0 /* always report first error */ {\n-\t\th.ErrorMsg(pos, msg);\n+\tif loc.Line != h.errline {\n+\t\th.ErrorMsg(loc, msg);\
 \t}\n }\

コンパイル処理におけるエラーハンドリングも大きく変更されています。

  • errorHandler構造体からerrpos(エラー位置のバイトオフセット)が削除され、errline(エラー行番号)が追加されました。これは、エラーの重複報告を避けるロジックが、バイトオフセットではなく行番号に基づいて行われるようになったことを示しています。
  • ErrorMsgErrorメソッドの引数がpos intからloc scanner.Locationに変更され、エラーメッセージの出力時にscanner.Locationから直接行番号と列番号を取得して表示できるようになりました。
  • 以前はバイトオフセットから行番号と列番号を計算していたLineCol関数がコメントアウトされました。これは、scanner.Locationが既にこの情報を持っているため、この関数が不要になったことを明確に示しています。

これらの変更は、Go言語のコンパイラがより正確でユーザーフレンドリーなエラーメッセージを提供するための基盤を築いたと言えます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント(go/ast, go/parser, go/scanner, go/tokenパッケージの解説)
  • Go言語のGitHubリポジトリのコミット履歴
  • Go言語の初期開発に関するブログ記事やメーリングリストの議論(一般的な情報収集のため)