[インデックス 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
インターフェースの導入は、以下の目的を達成するためと考えられます。
- 正確なエラー報告: コンパイルエラーや構文エラーが発生した際に、ファイル名、行番号、列番号を正確に報告できるようにするため。これにより、開発者は問題の箇所を特定しやすくなります。
- ツールチェインの連携強化: スキャナー、パーサー、AST、プリンターといった異なるコンポーネント間で、一貫したソースコード位置情報表現を共有することで、ツールチェイン全体の連携と保守性を向上させるため。
- リファクタリングの容易化: ソースコード位置の抽象化により、将来的なスキャナーやパーサーの実装変更が、ASTや他のコンポーネントに与える影響を最小限に抑えるため。
- デバッグ情報の充実: デバッガやプロファイラなどのツールが、より詳細なソースコード位置情報に基づいて動作できるようにするため。
pretty
パッケージは、Go言語のソースコードを解析し、整形(pretty-printing)するための初期のツールであり、コンパイラのフロントエンドの重要な一部でした。このパッケージが新しいスキャナーインターフェースに追従することは、Go言語のツールチェイン全体の整合性を保つ上で不可欠でした。
前提知識の解説
このコミットを理解するためには、以下の概念に関する前提知識が必要です。
-
コンパイラのフロントエンド:
- スキャナー(字句解析器): ソースコードを読み込み、意味のある最小単位である「トークン」(キーワード、識別子、演算子など)のストリームに変換するコンポーネントです。このコミットでは、スキャナーが生成するトークンに付随する位置情報の形式が変更されています。
- パーサー(構文解析器): スキャナーが生成したトークンのストリームを読み込み、言語の文法規則に従って「抽象構文木(AST)」を構築するコンポーネントです。
- 抽象構文木(AST: Abstract Syntax Tree): ソースコードの構文構造を木構造で表現したものです。各ノードは、プログラムの要素(式、文、宣言など)を表し、その属性としてソースコード内の位置情報を持つことが一般的です。
-
Go言語の
token
パッケージ:- Go言語の標準ライブラリには、字句解析器が生成するトークンを定義する
token
パッケージが存在します。このパッケージは、トークンの種類(例:token.IDENT
、token.LPAREN
)や、トークンの優先順位などを定義します。
- Go言語の標準ライブラリには、字句解析器が生成するトークンを定義する
-
Go言語の
scanner
パッケージ:- Go言語の標準ライブラリには、字句解析を行うための
scanner
パッケージが存在します。このパッケージは、ソースコードをスキャンし、トークンとそれに対応する位置情報を生成する機能を提供します。このコミットで言及されている「新しいスキャナーインターフェース」は、このscanner
パッケージの進化を指している可能性が高いです。特に、scanner.Location
型は、ファイル内のバイトオフセットだけでなく、行番号や列番号といった情報をカプセル化するために導入されたと考えられます。
- Go言語の標準ライブラリには、字句解析を行うための
-
ソースコード位置情報:
- コンパイラや開発ツールにおいて、ソースコード内の特定の位置を指し示すための情報です。通常、ファイル名、行番号、列番号、そしてバイトオフセットが含まれます。正確な位置情報は、エラーメッセージの表示、デバッグ、コードエディタの機能(例: 定義へのジャンプ)に不可欠です。
-
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 int
とTok 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を走査する際に、より詳細な位置情報を取得できるようになります。StructType
とInterfaceType
のEnd
フィールド: これらの構造体では、End int
がEnd scanner.Location
に変更されています。これは、構造体やインターフェースの終端を示す位置も、より詳細な情報を持つようになったことを意味します。
エラーハンドリングの変更 (usr/gri/pretty/compilation.go
)
errorHandler
の変更:errorHandler
構造体からerrpos int
が削除され、errline int
が追加されています。これは、エラーの重複報告を避けるためのロジックが、バイトオフセットではなく行番号に基づいて行われるようになったことを示唆しています。ErrorMsg
とError
メソッドの引数変更:ErrorMsg
とError
メソッドの引数がpos int
からloc scanner.Location
に変更されています。これにより、エラーメッセージの出力時にscanner.Location
から直接行番号と列番号を取得して表示できるようになりました。LineCol
関数のコメントアウト: 以前はバイトオフセットから行番号と列番号を計算するLineCol
関数が存在しましたが、これがコメントアウトされています。これは、scanner.Location
が既にこの情報を持っているため、この関数が不要になったことを明確に示しています。
パーサーの変更 (usr/gri/pretty/parser.go
)
Scanner
およびErrorHandler
インターフェースの削除: パーサーが依存していたScanner
とErrorHandler
のローカルなインターフェース定義が削除されました。これは、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言語のコンパイラフロントエンドにおける位置情報管理の統一と強化を目指したものであり、より堅牢で使いやすい開発ツールを提供するための重要なステップでした。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下のファイルに集中しています。
-
usr/gri/pretty/ast.go
:- ASTノードの構造体定義において、
Pos_ int
フィールドがLoc_ scanner.Location
に置き換えられました。 Pos() int
メソッドがLoc() scanner.Location
メソッドに置き換えられました。Node
構造体が削除されました。
- ASTノードの構造体定義において、
-
usr/gri/pretty/parser.go
:Parser
構造体のscanner
とerr
フィールドの型が、標準のscanner
パッケージの型に更新されました。- パーサーが現在処理しているトークンの位置を示す
P.pos int
がP.loc scanner.Location
に置き換えられました。 - ASTノードを生成する際に、
P.pos
の代わりにP.loc
を渡すように変更されました。 - エラー報告のメソッド(
error
,expect
)がscanner.Location
を使用するように変更されました。
-
usr/gri/pretty/printer.go
:- プリンターのメソッド(
TaggedString
,String
,Token
,Error
)が、位置情報としてscanner.Location
を受け取るように変更されました。 - ASTノードから位置情報を取得する際に、
x.Pos_
の代わりにx.Loc_
を使用するように変更されました。
- プリンターのメソッド(
-
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\
}\
パーサーの変更は、スキャナーとの連携方法とエラー報告のメカニズムに影響を与えています。
Scanner
とErrorHandler
のローカルインターフェースが削除され、Go標準のscanner
パッケージの型を直接使用するようになりました。これは、Go言語のツールチェインが標準ライブラリのコンポーネントに統合されていく過程を示しています。- パーサーが次に処理するトークンの位置を保持する
P.pos
がP.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
(エラー行番号)が追加されました。これは、エラーの重複報告を避けるロジックが、バイトオフセットではなく行番号に基づいて行われるようになったことを示しています。ErrorMsg
とError
メソッドの引数がpos int
からloc scanner.Location
に変更され、エラーメッセージの出力時にscanner.Location
から直接行番号と列番号を取得して表示できるようになりました。- 以前はバイトオフセットから行番号と列番号を計算していた
LineCol
関数がコメントアウトされました。これは、scanner.Location
が既にこの情報を持っているため、この関数が不要になったことを明確に示しています。
これらの変更は、Go言語のコンパイラがより正確でユーザーフレンドリーなエラーメッセージを提供するための基盤を築いたと言えます。
関連リンク
- Go言語の
go/ast
パッケージ: https://pkg.go.dev/go/ast - Go言語の
go/parser
パッケージ: https://pkg.go.dev/go/parser - Go言語の
go/scanner
パッケージ: https://pkg.go.dev/go/scanner - Go言語の
go/token
パッケージ: https://pkg.go.dev/go/token
参考にした情報源リンク
- Go言語の公式ドキュメント(
go/ast
,go/parser
,go/scanner
,go/token
パッケージの解説) - Go言語のGitHubリポジトリのコミット履歴
- Go言語の初期開発に関するブログ記事やメーリングリストの議論(一般的な情報収集のため)