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

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

このコミットは、Go言語の標準ライブラリにおける複数の重要な改善と機能追加を含んでいます。主にfmtパッケージのフォーマット機能の拡張、utf8パッケージのUnicode文字処理の強化、reflectパッケージのインターフェース操作の改善、そしてstrconvパッケージへの文字列引用符付け機能の追加が行われています。

変更された主なファイルは以下の通りです。

  • src/lib/bufio.go: バッファリングされたI/OにUnicodeルーンの読み取り機能を追加。
  • src/lib/fmt/fmt_test.go: fmtパッケージの新しいフォーマット機能のためのテストスイート。
  • src/lib/fmt/format.go: fmtパッケージの内部フォーマットロジック、特に数値、文字列のパディング、および新しいフォーマットフラグの処理を更新。
  • src/lib/fmt/print.go: fmtパッケージのprintf系関数の実装を更新し、新しいフォーマット動詞とリフレクションによる引数処理をサポート。
  • src/lib/reflect/all_test.go: reflectパッケージのインターフェース関連の新しいテスト。
  • src/lib/reflect/cast_amd64.s: AMD64アーキテクチャ向けのリフレクション関連のアセンブリコードに、インターフェースポインタ変換関数を追加。
  • src/lib/reflect/gencast.sh: リフレクションの型生成スクリプトにInterface型を追加。
  • src/lib/reflect/value.go: reflectパッケージのValueインターフェースとインターフェース値の取得方法を更新。
  • src/lib/strconv/Makefile: strconvパッケージのビルド設定に新しいquote.goファイルを追加。
  • src/lib/strconv/ftoa_test.go: 浮動小数点数から文字列への変換テストに新しいケースを追加。
  • src/lib/strconv/quote.go: 文字列の引用符付けとバッククォート可能かどうかの判定を行う新しいユーティリティ関数群。
  • src/lib/strconv/quote_test.go: strconv/quote.goのテストスイート。
  • src/lib/utf8.go: UTF-8エンコーディングの定数と、文字列からのルーンデコード機能を追加。
  • src/lib/utf8_test.go: utf8パッケージのテストを更新し、新しい文字列デコード機能に対応。
  • test/fmt_test.go: 古いfmtテストファイルが削除され、新しいsrc/lib/fmt/fmt_test.goに置き換えられた。

コミット

commit 387df5e1763a5d400b1d0bf153b9d753eaea3471
Author: Russ Cox <rsc@golang.org>
Date:   Mon Nov 24 14:51:33 2008 -0800

    replay CL 19916 and CL 19913 now that the build can handle them
    
    TBR=r
    OCL=19924
    CL=19934

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

https://github.com/golang/go/commit/387df5e1763a5d400b1d0bf153b9d753eaea3471

元コミット内容

このコミットメッセージにある「replay CL 19916 and CL 19913 now that the build can handle them」という記述は、以前に提出された変更リスト(Change List, CL)である19916と19913が、当時のビルドシステムや依存関係の問題により適用できなかったか、あるいは一時的に revert されていたことを示唆しています。そして、それらの問題が解決されたため、今回改めてこれらの変更を適用(replay)したことを意味します。これは、Go言語の初期開発段階において、コードベースの進化とビルドシステムの調整が並行して行われていた状況を反映しています。

変更の背景

このコミットが行われた2008年11月は、Go言語がまだ一般に公開される前の初期開発段階にありました。この時期のGo言語は、その設計思想(シンプルさ、並行性、効率性)を具体化するための基本的なライブラリとランタイムの構築に注力していました。

このコミットにおける変更の背景には、以下の主要な動機が考えられます。

  1. より堅牢なフォーマット機能の必要性: fmtパッケージは、Go言語における標準的なフォーマット済みI/Oを提供します。C言語のprintfに似た機能を持つこのパッケージは、デバッグ出力、ログ記録、ユーザーへの表示など、あらゆる場面で利用されます。初期のfmtパッケージは基本的な機能しか持っていなかったため、より柔軟で表現力豊かなフォーマット(例:数値のゼロパディング、文字列の引用符付け、16進数表示)が求められていました。特に、Go言語がシステムプログラミング言語としての側面を持つことを考えると、低レベルなデータ(バイト列など)の表現能力は重要でした。
  2. UnicodeとUTF-8の適切なサポート: Go言語は設計当初からUnicodeとUTF-8を第一級の市民として扱っています。しかし、初期の実装では、文字列とバイト列の間の変換や、UTF-8エンコードされた文字列からのルーン(Unicodeコードポイント)の効率的なデコードに関して、改善の余地がありました。特に、バッファリングされたI/O (bufio) において、バイトストリームから直接ルーンを読み取る機能は、多言語対応アプリケーションにとって不可欠です。
  3. リフレクションの進化: reflectパッケージは、実行時に型情報にアクセスし、値を操作するための強力な機能を提供します。fmtパッケージのような汎用的なフォーマットライブラリは、引数の型を動的に検査し、それに応じて適切なフォーマットを適用するためにリフレクションを多用します。初期のreflectパッケージはまだ発展途上であり、特にインターフェース型を介した値の取得や操作に関して、より洗練されたメカニズムが必要とされていました。
  4. 文字列ユーティリティの拡充: 文字列のパースや変換は、多くのアプリケーションで頻繁に行われる操作です。strconvパッケージはこれらの機能を提供しますが、文字列を安全に引用符付けしたり、バッククォートで表現できるかを判定する機能は、特にコード生成やデータシリアライズの文脈で有用です。

これらの変更は、Go言語がより実用的で、多様なアプリケーション開発に対応できるような、基本的なインフラストラクチャを強化する一環として行われました。

前提知識の解説

このコミットの変更内容を深く理解するためには、以下のGo言語の概念と関連技術についての知識が役立ちます。

  1. Go言語のパッケージシステム: Goのコードはパッケージに分割され、import文で他のパッケージの機能を利用します。fmt, reflect, strconv, utf8, bufioはGoの標準ライブラリの一部です。
  2. Go言語の型システムとインターフェース: Goは静的型付け言語ですが、インターフェースを通じてポリモーフィズムを実現します。インターフェースはメソッドの集合を定義し、任意の型がそのメソッドを実装していれば、そのインターフェース型として扱えます。interface{}は「空のインターフェース」と呼ばれ、任意の型の値を保持できます。
  3. Go言語の文字列とバイト列: Goの文字列はUTF-8でエンコードされたバイト列であり、不変です。string型はバイト列として扱われますが、rangeループを使うとUnicodeのルーン(rune型)としてイテレートできます。[]byteは可変なバイトスライスです。
  4. UnicodeとUTF-8:
    • Unicode: 世界中の文字を統一的に扱うための文字コード標準です。各文字には一意の「コードポイント」が割り当てられています。
    • UTF-8: Unicodeコードポイントをバイト列にエンコードするための可変長エンコーディング方式です。ASCII文字は1バイトで表現され、非ASCII文字は2〜4バイトで表現されます。これにより、ASCIIとの互換性を保ちつつ、効率的に多言語を扱えます。
    • ルーン (Rune): Go言語では、Unicodeのコードポイントを表現するためにrune型(int32のエイリアス)を使用します。
  5. fmtパッケージ: Go言語のフォーマット済みI/Oを提供するパッケージです。Printf, Sprintf, Fprintなどの関数があり、フォーマット文字列と引数を使って様々な型の値を整形して出力できます。フォーマット文字列は%で始まる「フォーマット動詞」を含みます(例: %dは整数、%sは文字列)。
  6. reflectパッケージ: 実行時にGoのプログラムの構造(型、値、メソッドなど)を検査・操作するための機能を提供します。reflect.ValueはGoの任意の値を抽象的に表現し、reflect.Typeはその値の型情報を提供します。
  7. bufioパッケージ: バッファリングされたI/O操作を提供し、効率的な読み書きを可能にします。ReaderWriterなどの型があります。
  8. strconvパッケージ: 文字列と基本的なデータ型(数値、真偽値など)の間で変換を行う機能を提供します。
  9. Goのアセンブリ言語: Go言語の一部は、パフォーマンスが重要な部分や、特定のハードウェア機能にアクセスするためにアセンブリ言語で書かれています。GoのアセンブリはPlan 9アセンブリの派生であり、一般的なx86/x64アセンブリとは異なる構文を持ちます。このコミットでは、reflectパッケージのインターフェース操作に関連するアセンブリコードが変更されています。
  10. テスト駆動開発 (TDD): Go言語の標準ライブラリ開発では、テストが非常に重視されます。_test.goファイルに書かれたテストコードは、変更が正しく機能することを確認するための重要な役割を果たします。

技術的詳細

このコミットは、Go言語の基本的な機能であるフォーマット、Unicode処理、リフレクション、文字列変換にわたる広範な改善を導入しています。

1. fmtパッケージの拡張

fmtパッケージは、C言語のprintfにインスパイアされた強力なフォーマット機能を提供します。このコミットでは、特に以下の点が強化されています。

  • 新しいフォーマット動詞:
    • %x, %X (文字列/バイト列の16進数表現): 文字列やバイトスライスをそのバイト値の16進数表現として出力する機能が追加されました。%xは小文字、%Xは大文字を使用します。これは、バイナリデータのデバッグや表示に非常に有用です。
    • %q (引用符付き文字列): 文字列をGoの文字列リテラル形式で引用符付けして出力する機能が追加されました。特殊文字はエスケープされ、必要に応じてバッククォート文字列(raw string literal)も使用されます。これは、文字列の内容を正確に表現したい場合に役立ちます。
  • フォーマットフラグの強化:
    • Fmt構造体にminus, plus, sharp, space, zeroといった新しいフラグが追加されました。これらはフォーマット文字列中の%と動詞の間に指定される修飾子(例: %-10s, %+d, %#x, % d, %05d)に対応します。
    • minus (-): 左寄せ。
    • plus (+): 数値に常に符号(+または-)を付ける。
    • sharp (#): 別の形式(例: 16進数に0xプレフィックス、%qでバッククォート)。
    • space ( ): 正の数値の前にスペースを置く。
    • zero (0): 数値のゼロパディング。
    • これらのフラグは、数値や文字列の出力形式をより細かく制御するために使用されます。特に、integer関数やpad関数でこれらのフラグが考慮されるようになりました。
  • []byteの文字列としての扱い: print.gogetString関数が*[]byte型をstringとして扱えるように拡張されました。これにより、fmtパッケージはバイトスライスを直接文字列としてフォーマットできるようになり、string(myBytes)のような明示的な変換が不要になる場合があります。

2. utf8パッケージの強化

Go言語はUTF-8をネイティブにサポートしていますが、このコミットではそのサポートがさらに強化されました。

  • RuneMaxUTFMaxの更新:
    • RuneMax1<<21 - 1から0x10FFFFに変更されました。これはUnicodeの最大コードポイント(U+10FFFF)を正確に反映しています。
    • UTFMax4に設定されました。これはUTF-8エンコーディングにおける1つのルーンの最大バイト長が4バイトであることを示します。
  • 文字列からのルーンデコード:
    • DecodeRuneInStringInternal, FullRuneInString, DecodeRuneInStringといった関数が追加されました。これらは、バイトスライスではなく、Goのstring型から直接UTF-8ルーンをデコードするためのものです。これにより、文字列操作におけるUTF-8の扱いがより効率的かつ安全になります。
  • bufio.ReadRune()の追加: bufioパッケージにReadRune()メソッドが追加されました。これは、バッファリングされた入力ストリームから1つのUnicodeルーンを読み取るためのものです。内部的にはutf8パッケージの機能を利用し、部分的なUTF-8シーケンスがバッファの終端にある場合でも、追加のバイトを読み込んで完全なルーンをデコードしようとします。

3. reflectパッケージの改善

reflectパッケージは、Goのプログラムが自身の構造を検査・操作することを可能にします。

  • interface{}の汎用的な扱い:
    • 以前はEmpty interface{}という特定の空のインターフェース型が定義されていましたが、これが削除され、Goの組み込み型であるinterface{}がより汎用的に扱われるようになりました。
    • ValueインターフェースのInterface()メソッドの戻り値がEmptyからinterface{}に変更されました。
    • NewValue関数もEmptyではなくinterface{}を引数に取るようになりました。
  • InterfaceValue.Get()の追加: reflect.InterfaceValue型にGet()メソッドが追加されました。このメソッドは、インターフェースが保持している実際の値(インターフェースの内部にある具体的な値)をinterface{}型として返します。これにより、リフレクションを通じてインターフェースの内部値にアクセスする際の利便性が向上しました。
  • アセンブリレベルでのインターフェースサポート: src/lib/reflect/cast_amd64.sAddrToPtrInterfacePtrInterfaceToAddrというアセンブリ関数が追加されました。これらは、インターフェースの内部表現(型情報とデータポインタ)とGoのポインタ型との間で変換を行うための低レベルなサポートを提供します。これにより、リフレクションがインターフェースをより効率的に扱えるようになります。

4. strconvパッケージへの文字列引用符付け機能の追加

strconvパッケージは、文字列と基本的なデータ型間の変換を提供します。

  • Quote関数の追加: 文字列をGoの文字列リテラル形式(ダブルクォートで囲み、特殊文字をエスケープ)に変換するQuote関数が追加されました。これは、文字列をログに出力したり、設定ファイルに書き込んだりする際に、その内容が曖昧にならないようにするために重要です。
  • CanBackquote関数の追加: 文字列がバッククォート文字列リテラル(raw string literal、例: `abc`)として表現可能かどうかを判定するCanBackquote関数が追加されました。バッククォート文字列は、改行や特殊文字をエスケープせずにそのまま記述できるため、正規表現や複数行の文字列を扱う際に便利です。この関数は、文字列にバッククォート文字( )や制御文字が含まれていない場合にtrue`を返します。

これらの変更は、Go言語の初期段階における基盤ライブラリの成熟度を高め、開発者がより表現力豊かで堅牢なアプリケーションを構築できるようにするための重要なステップでした。

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

このコミットにおける主要な変更点を、それぞれのパッケージから抜粋して解説します。

src/lib/fmt/format.go - Fmt構造体とパディングロジックの変更

--- a/src/lib/fmt/format.go
+++ b/src/lib/fmt/format.go
@@ -39,11 +41,22 @@ export type Fmt struct {
  	wid_present bool;
  	prec int;
  	prec_present bool;
+	// flags
+	minus bool;
+	plus bool;
+	sharp bool;
+	space bool;
+	zero bool;
  }
 
  func (f *Fmt) clearflags() {
  	f.wid_present = false;
  	f.prec_present = false;
+	f.minus = false;
+	f.plus = false;
+	f.sharp = false;
+	f.space = false;
+	f.zero = false;
  }
 
  func (f *Fmt) clearbuf() {
@@ -101,24 +114,28 @@ func (f *Fmt) w(x int) *Fmt {
  	return f;
  }
 
-// append s to buf, padded on left (w > 0) or right (w < 0)
+// append s to buf, padded on left (w > 0) or right (w < 0 or f.minus)
 // padding is in bytes, not characters (agrees with ANSIC C, not Plan 9 C)
  func (f *Fmt) pad(s string) {
  	if f.wid_present && f.wid != 0 {
-\t\tleft := true;
+\t\tleft := !f.minus;
  	\tw := f.wid;
  	\tif w < 0 {\
  	\t\tleft = false;
  	\t\tw = -w;
  	\t}\
  	\tw -= len(s);
+\t\tpadchar := byte(' ');
+\t\tif left && f.zero {
+\t\t\tpadchar = '0';
+\t\t}
  	\tif w > 0 {\
  	\t\tif w > NByte {\
  	\t\t\tw = NByte;
  	\t\t}\
  	\t\tbuf := new([]byte, w);
  	\t\tfor i := 0; i < w; i++ {\
-\t\t\t\tbuf[i] = ' ';
+\t\t\t\tbuf[i] = padchar;
  	\t\t}\
  	\t\tif left {\
  	\t\t\ts = string(buf) + s;
@@ -163,16 +180,35 @@ func (f *Fmt) integer(a int64, base uint, is_signed bool, digits *string) string
  	if negative {
  	\ta = -a;
  	}
-\ti := putint(&buf, NByte-1, uint64(base), uint64(a), digits);\
+\n	// two ways to ask for extra leading zero digits: %.3d or %03d.
+\t// apparently the first cancels the second.
+\tprec := 0;
  	if f.prec_present {
-\t\tfor i > 0 && f.prec > (NByte-1-i) {\
-\t\t\tbuf[i] = '0';
-\t\t\ti--;
+\t\tprec = f.prec;
+\t\tf.zero = false;
+\t} else if f.zero && f.wid_present && !f.minus && f.wid > 0{
+\t\tprec = f.wid;
+\t\tif negative || f.plus || f.space {
+\t\t\tprec--;  // leave room for sign
  	\t}\
  	}\
+\n	i := putint(&buf, NByte-1, uint64(base), uint64(a), digits);
+\tfor i > 0 && prec > (NByte-1-i) {
+\t\tbuf[i] = '0';
+\t\ti--;
+\t}
+\n  	if negative {
  	\tbuf[i] = '-';
  	\ti--;
+\t} else if f.plus {
+\t\tbuf[i] = '+';
+\t\ti--;
+\t} else if f.space {
+\t\tbuf[i] = ' ';
+\t\ti--;
  	}\
  	return string(buf)[i+1:NByte];
  }
@@ -334,6 +370,44 @@ func (f *Fmt) s(s string) *Fmt {
  	return f;
  }
 
+// hexadecimal string
+func (f *Fmt) sx(s string) *Fmt {
+\tt := "";
+\tfor i := 0; i < len(s); i++ {
+\t\tv := s[i];
+\t\tt += string(ldigits[v>>4]);
+\t\tt += string(ldigits[v&0xF]);
+\t}
+\tf.pad(t);
+\tf.clearflags();
+\treturn f;
+}
+
+func (f *Fmt) sX(s string) *Fmt {
+\tt := "";
+\tfor i := 0; i < len(s); i++ {
+\t\tv := s[i];
+\t\tt += string(udigits[v>>4]);
+\t\tt += string(udigits[v&0xF]);
+\t}
+\tf.pad(t);
+\tf.clearflags();
+\treturn f;
+}
+
+// quoted string
+func (f *Fmt) q(s string) *Fmt {
+\tvar quoted string;
+\tif f.sharp && strconv.CanBackquote(s) {
+\t\tquoted = "`"+s+"`";
+\t} else {
+\t\tquoted = strconv.Quote(s);
+\t}
+\tf.pad(quoted);
+\tf.clearflags();
+\treturn f;
+}
+
  // floating-point
 
  func Prec(f *Fmt, def int) int {
@@ -370,7 +444,7 @@ func (f *Fmt) fb64(a float64) *Fmt {
  // cannot defer to float64 versions
  // because it will get rounding wrong in corner cases.
  func (f *Fmt) e32(a float32) *Fmt {
-\treturn FmtString(f, strconv.ftoa32(a, 'e', Prec(f, -1)));
+\treturn FmtString(f, strconv.ftoa32(a, 'e', Prec(f, 6)));
  }
 
  func (f *Fmt) f32(a float32) *Fmt {

解説:

  • Fmt構造体にminus, plus, sharp, space, zeroといったブール型のフラグが追加されました。これらは、printfスタイルのフォーマット文字列で指定される修飾子(例: %-s, %+d, %#x, % d, %0d)に対応します。clearflags()メソッドもこれらの新しいフラグをリセットするように更新されています。
  • pad関数は、文字列のパディングロジックを改善しました。特に、f.minusフラグ(左寄せ)とf.zeroフラグ(ゼロパディング)を考慮するようになりました。padchar変数が導入され、パディング文字がスペース(デフォルト)または0(ゼロパディングの場合)に動的に設定されます。
  • integer関数は、数値のフォーマットにおいて、精度(prec)とゼロパディング(f.zero)の相互作用をより正確に処理するようになりました。また、f.plus(常に符号を表示)とf.space(正の数値の前にスペース)フラグに基づいて、数値の前に+またはスペースを追加するロジックが追加されました。
  • sx, sX, qという新しいメソッドがFmt構造体に追加されました。
    • sx(s string): 文字列sを小文字の16進数表現に変換し、パディングを適用します。
    • sX(s string): 文字列sを大文字の16進数表現に変換し、パディングを適用します。
    • q(s string): 文字列sをGoの引用符付き文字列リテラル形式に変換し、パディングを適用します。f.sharpフラグが設定されており、文字列がバッククォート可能であれば、バッククォート文字列を使用します。

src/lib/fmt/print.go - doprintfと引数処理の変更

--- a/src/lib/fmt/print.go
+++ b/src/lib/fmt/print.go
@@ -186,6 +186,19 @@ export func sprintln(a ...) string {
  	return s;
  }
 
+
+// Get the i'th arg of the struct value.
+// If the arg itself is an interface, return a value for
+// the thing inside the interface, not the interface itself.
+func getField(v reflect.StructValue, i int) reflect.Value {
+\tval := v.Field(i);
+\tif val.Kind() == reflect.InterfaceKind {
+\t\tinter := val.(reflect.InterfaceValue).Get();
+\t\treturn reflect.NewValue(inter);
+\t}
+\treturn val;
+}
+
  // Getters for the fields of the argument structure.
 
  func getBool(v reflect.Value) (val bool, ok bool) {
@@ -227,6 +240,9 @@ func getString(v reflect.Value) (val string, ok bool) {
  	case reflect.StringKind:
  	\treturn v.(reflect.StringValue).Get(), true;
  	}\
+\tif valb, okb := v.Interface().(*[]byte); okb {
+\t\treturn string(valb), true;
+\t}
  	return "", false;
  }
 
@@ -280,12 +296,6 @@ func parsenum(s string, start, end int) (n int, got bool, newi int) {
  	if start >= end {
  	\treturn 0, false, end
  	}\
-\tif s[start] == '-' {
-\t\ta, b, c := parsenum(s, start+1, end);
-\t\tif b {
-\t\t\treturn -a, b, c;
-\t\t}
-\t}
  	isnum := false;
  	num := 0;
  	for '0' <= s[start] && s[start] <= '9' {
@@ -371,10 +381,28 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
  	\t\ti += w;
  	\t\tcontinue;
  	\t}\
-\t\t// saw % - do we have %20 (width)?
-\t\tp.wid, p.wid_ok, i = parsenum(format, i+1, end);\
+\t\ti++;
+\t\t// flags
+\t\tF: for ; i < end; i++ {
+\t\t\tswitch format[i] {
+\t\t\tcase '#':
+\t\t\t\tp.fmt.sharp = true;
+\t\t\tcase '0':
+\t\t\t\tp.fmt.zero = true;
+\t\t\tcase '+':
+\t\t\t\tp.fmt.plus = true;
+\t\t\tcase '-':
+\t\t\t\tp.fmt.minus = true;
+\t\t\tcase ' ':
+\t\t\t\tp.fmt.space = true;
+\t\t\tdefault:
+\t\t\t\tbreak F;
+\t\t\t}
+\t\t}
+\t\t// do we have 20 (width)?
+\t\tp.wid, p.wid_ok, i = parsenum(format, i, end);
  	\tp.prec_ok = false;
-\t\t// do we have %.20 (precision)?
+\t\t// do we have .20 (precision)?
  	\tif i < end && format[i] == '.' {
  	\t\tp.prec, p.prec_ok, i = parsenum(format, i+1, end);
  	\t}
@@ -391,7 +419,7 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
  	\t\tp.addstr("(missing)");
  	\t\tcontinue;
  	\t}\
-\t\tfield := v.Field(fieldnum);\
+\t\tfield := getField(v, fieldnum);
  	\tfieldnum++;
  	\tif c != 'T' {\t// don't want thing to describe itself if we're asking for its type
  	\t\tif formatter, ok := field.Interface().(Format); ok {
@@ -463,6 +491,20 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
  	\t\t\t\t\t} else {
  	\t\t\t\t\t\ts = p.fmt.ux64(uint64(v)).str()
  	\t\t\t\t\t}\
+\t\t\t\t} else if v, ok := getString(field); ok {
+\t\t\t\t\ts = p.fmt.sx(v).str();
+\t\t\t\t} else {
+\t\t\t\t\tgoto badtype
+\t\t\t\t}
+\t\t\tcase 'X':
+\t\t\t\tif v, signed, ok := getInt(field); ok {
+\t\t\t\t\tif signed {
+\t\t\t\t\t\ts = p.fmt.X64(v).str()
+\t\t\t\t\t} else {
+\t\t\t\t\t\ts = p.fmt.uX64(uint64(v)).str()
+\t\t\t\t\t}
+\t\t\t\t} else if v, ok := getString(field); ok {
+\t\t\t\t\ts = p.fmt.sX(v).str();
  	\t\t\t\t} else {
  	\t\t\t\t\tgoto badtype
  	\t\t\t\t}
@@ -500,6 +542,12 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
  	\t\t\t\t} else {
  	\t\t\t\t\tgoto badtype
  	\t\t\t\t}\
+\t\t\tcase 'q':
+\t\t\t\tif v, ok := getString(field); ok {
+\t\t\t\t\ts = p.fmt.q(v).str()
+\t\t\t\t} else {
+\t\t\t\t\tgoto badtype
+\t\t\t\t}
 
  	\t\t// pointer
  	\t\tcase 'p':
@@ -530,7 +578,7 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
  \tif fieldnum < v.Len() {
  \t\tp.addstr("?(extra ");
  \t\tfor ; fieldnum < v.Len(); fieldnum++ {
-\t\t\tp.addstr(v.Field(fieldnum).Type().String());
+\t\t\tp.addstr(getField(v, fieldnum).Type().String());
  \t\t\tif fieldnum + 1 < v.Len() {
  \t\t\t\tp.addstr(", ");
  \t\t\t}\
@@ -543,7 +591,7 @@ func (p *P) doprint(v reflect.StructValue, addspace, addnewline bool) {
  \tprev_string := false;
  \tfor fieldnum := 0; fieldnum < v.Len();  fieldnum++ {
  \t\t// always add spaces if we're doing println
-\t\tfield := v.Field(fieldnum);\
+\t\tfield := getField(v, fieldnum);
  \t\tif fieldnum > 0 {
  \t\t\tif addspace {
  \t\t\t\tp.add(' ')

解説:

  • getFieldというヘルパー関数が追加されました。この関数は、reflect.StructValueから指定されたインデックスのフィールドを取得します。もしそのフィールドがインターフェース型であれば、そのインターフェースが保持している実際の値(reflect.InterfaceValue.Get()で取得)をreflect.NewValueでラップして返します。これにより、fmtパッケージがインターフェースの内部の具体的な値を直接フォーマットできるようになり、より柔軟な出力が可能になります。
  • getString関数は、reflect.Value*[]byte型である場合にもstringに変換できるように拡張されました。これは、バイトスライスを文字列としてフォーマットする際の利便性を高めます。
  • doprintf関数は、フォーマット文字列のパースロジックが大幅に改善されました。
    • %の直後に続くフラグ(#, 0, +, -, )を正しくパースし、p.fmtFmt構造体)の対応するフラグを設定するループが追加されました。
    • 幅(width)と精度(precision)のパースロジックも調整され、フラグの後に続く数値として正しく解釈されるようになりました。
    • switch c文に新しいケース'X', 'x', 'q'が追加され、それぞれp.fmt.sX, p.fmt.sx, p.fmt.qメソッドを呼び出すことで、16進数文字列フォーマットと引用符付き文字列フォーマットをサポートします。
  • doprintfdoprint関数内で、引数のフィールドを取得する際に直接v.Field(fieldnum)を呼び出す代わりに、新しく追加されたgetFieldヘルパー関数を使用するように変更されました。これにより、インターフェースの内部値が適切に処理されるようになります。

src/lib/strconv/quote.go - 新しい文字列引用符付け機能

--- /dev/null
+++ b/src/lib/strconv/quote.go
@@ -0,0 +1,76 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package strconv
+
+import (
+\t"utf8";
+)
+
+const ldigits = "0123456789abcdef"
+const udigits = "0123456789ABCDEF"
+
+export func Quote(s string) string {
+\tt := "`";
+\tfor i := 0; i < len(s); i++ {
+\t\tswitch {
+\t\tcase s[i] == '"':
+\t\t\tt += `\"`;
+\t\tcase s[i] == '\\':
+\t\t\tt += `\\`;
+\t\tcase ' ' <= s[i] && s[i] <= '~':
+\t\t\tt += string(s[i]);
+\t\tcase s[i] == '\a':
+\t\t\tt += `\a`;
+\t\tcase s[i] == '\b':
+\t\t\tt += `\b`;
+\t\tcase s[i] == '\f':
+\t\t\tt += `\f`;
+\t\tcase s[i] == '\n':
+\t\t\tt += `\n`;
+\t\tcase s[i] == '\r':
+\t\t\tt += `\r`;
+\t\tcase s[i] == '\t':
+\t\t\tt += `\t`;
+\t\tcase s[i] == '\v':
+\t\t\tt += `\v`;
+
+\t\tcase utf8.FullRuneInString(s, i):
+\t\t\tr, size := utf8.DecodeRuneInString(s, i);
+\t\t\tif r == utf8.RuneError && size == 1 {
+\t\t\t\tgoto EscX;
+\t\t\t}
+\t\t\ti += size-1;  // i++ on next iteration
+\t\t\tif r < 0x10000 {
+\t\t\t\tt += `\u`;
+\t\t\t\tfor j:=uint(0); j<4; j++ {
+\t\t\t\t\tt += string(ldigits[(r>>(12-4*j))&0xF]);
+\t\t\t\t}
+\t\t\t} else {
+\t\t\t\tt += `\U`;
+\t\t\t\tfor j:=uint(0); j<8; j++ {
+\t\t\t\t\tt += string(ldigits[(r>>(28-4*j))&0xF]);
+\t\t\t\t}\
+\t\t\t}
+
+\t\tdefault:
+\t\tEscX:
+\t\t\tt += `\x`;
+\t\t\tt += string(ldigits[s[i]>>4]);
+\t\t\tt += string(ldigits[s[i]&0xF]);
+\t\t}
+\t}
+\tt += `"`;
+\treturn t;
+}
+
+export func CanBackquote(s string) bool {
+\tfor i := 0; i < len(s); i++ {
+\t\tif s[i] < ' ' || s[i] == '`' {
+\t\t\treturn false;
+\t\t}
+\t}
+\treturn true;
+}

解説:

  • Quote(s string) string関数は、入力文字列sをGoのダブルクォート文字列リテラル形式に変換して返します。
    • ダブルクォート(")とバックスラッシュ(\)はそれぞれ\"\\にエスケープされます。
    • 表示可能なASCII文字(スペースからチルダまで)はそのまま追加されます。
    • 一般的なエスケープシーケンス(\a, \b, \f, \n, \r, \t, \v)に対応します。
    • UTF-8でエンコードされたUnicode文字は、\uXXXX(U+FFFFまで)または\UXXXXXXXX(U+10FFFFまで)の形式でエスケープされます。
    • その他の制御文字や不正なUTF-8シーケンスは\xXXの形式で16進数エスケープされます。
  • CanBackquote(s string) bool関数は、入力文字列sがGoのバッククォート文字列リテラル(raw string literal)として表現可能かどうかを判定します。
    • バッククォート文字列は、改行や特殊文字をエスケープせずにそのまま記述できるため、文字列内にバッククォート文字( )や制御文字(ASCIIコード32未満の文字)が含まれていない場合にのみ使用できます。この関数は、これらの条件を満たす場合にtrue`を返します。

src/lib/utf8.go - 文字列からのルーンデコード機能

--- a/src/lib/utf8.go
+++ b/src/lib/utf8.go
@@ -9,7 +9,8 @@ package utf8
  export const (
  \tRuneError = 0xFFFD;
  \tRuneSelf = 0x80;
-\tRuneMax = 1<<21 - 1;
+\tRuneMax = 0x10FFFF;
+\tUTFMax = 4;
  )
 
  const (
@@ -105,17 +107,103 @@ func DecodeRuneInternal(p *[]byte) (rune, size int, short bool) {
  	return RuneError, 1, false
  }
 
+func DecodeRuneInStringInternal(s string, i int) (rune, size int, short bool) {
+\tn := len(s) - i;
+\tif n < 1 {
+\t\treturn RuneError, 0, true;
+\t}
+\tc0 := s[i];
+
+\t// 1-byte, 7-bit sequence?
+\tif c0 < Tx {
+\t\treturn int(c0), 1, false
+\t}
+
+\t// unexpected continuation byte?
+\tif c0 < T2 {
+\t\treturn RuneError, 1, false
+\t}
+
+\t// need first continuation byte
+\tif n < 2 {
+\t\treturn RuneError, 1, true
+\t}
+\tc1 := s[i+1];
+\tif c1 < Tx || T2 <= c1 {
+\t\treturn RuneError, 1, false
+\t}
+
+\t// 2-byte, 11-bit sequence?
+\tif c0 < T3 {
+\t\trune = int(c0&Mask2)<<6 | int(c1&Maskx);
+\t\tif rune <= Rune1Max {
+\t\t\treturn RuneError, 1, false
+\t\t}
+\t\treturn rune, 2, false
+\t}
+
+\t// need second continuation byte
+\tif n < 3 {
+\t\treturn RuneError, 1, true
+\t}
+\tc2 := s[i+2];
+\tif c2 < Tx || T2 <= c2 {
+\t\treturn RuneError, 1, false
+\t}
+
+\t// 3-byte, 16-bit sequence?
+\tif c0 < T4 {
+\t\trune = int(c0&Mask3)<<12 | int(c1&Maskx)<<6 | int(c2&Maskx);
+\t\tif rune <= Rune2Max {
+\t\t\treturn RuneError, 1, false
+\t\t}
+\t\treturn rune, 3, false
+\t}
+
+\t// need third continuation byte
+\tif n < 4 {
+\t\treturn RuneError, 1, true
+\t}
+\tc3 := s[i+3];
+\tif c3 < Tx || T2 <= c3 {
+\t\treturn RuneError, 1, false
+\t}
+
+\t// 4-byte, 21-bit sequence?
+\tif c0 < T5 {
+\t\trune = int(c0&Mask4)<<18 | int(c1&Maskx)<<12 | int(c2&Maskx)<<6 | int(c3&Maskx);
+\t\tif rune <= Rune3Max {
+\t\t\treturn RuneError, 1, false
+\t\t}
+\t\treturn rune, 4, false
+\t}
+
+\t// error
+\treturn RuneError, 1, false
+}
+
  export func FullRune(p *[]byte) bool {
  \trune, size, short := DecodeRuneInternal(p);
  \treturn !short
  }
 
+export func FullRuneInString(s string, i int) bool {
+\trune, size, short := DecodeRuneInStringInternal(s, i);
+\treturn !short
+}
+
  export func DecodeRune(p *[]byte) (rune, size int) {
  \tvar short bool;
  \trune, size, short = DecodeRuneInternal(p);
  \treturn;
  }
 
+export func DecodeRuneInString(s string, i int) (rune, size int) {
+\tvar short bool;
+\trune, size, short = DecodeRuneInStringInternal(s, i);
+\treturn;
+}
+
  export func RuneLen(rune int) int {
  \tswitch {
  \tcase rune <= Rune1Max:

解説:

  • RuneMaxUTFMaxの定数が更新され、Unicodeの最大コードポイントとUTF-8の最大バイト長を正確に反映するようになりました。
  • DecodeRuneInStringInternal(s string, i int)関数が追加されました。これは、[]byteではなくstring型のsi番目のインデックスからUTF-8ルーンをデコードするための内部ヘルパー関数です。この関数は、UTF-8のバイトシーケンスを解析し、対応するルーンとバイト長を返します。不正なシーケンスや不完全なシーケンスの場合にはRuneErrorを返します。
  • FullRuneInString(s string, i int) bool関数が追加されました。これは、si番目のインデックスから始まるバイトシーケンスが完全なUTF-8ルーンを形成しているかどうかを判定します。
  • DecodeRuneInString(s string, i int) (rune, size int)関数が追加されました。これは、si番目のインデックスからUTF-8ルーンをデコードし、ルーンとバイト長を返します。

src/lib/reflect/value.go - インターフェースの扱い

--- a/src/lib/reflect/value.go
+++ b/src/lib/reflect/value.go
@@ -36,14 +36,13 @@ func AddrToPtrString(Addr) *string
  func AddrToPtrBool(Addr) *bool
  func AddrToPtrRuntimeArray(Addr) *RuntimeArray
  func PtrRuntimeArrayToAddr(*RuntimeArray) Addr
-\n-export type Empty interface {}\t// TODO(r): Delete when no longer needed?
+func AddrToPtrInterface(Addr) *interface{}
 
  export type Value interface {
  \tKind()\tint;
  \tType()\tType;
  \tAddr()\tAddr;
-\tInterface()\tEmpty;
+\tInterface()\tinterface {};
  }
 
  // Common fields and functionality for all values
@@ -66,7 +65,7 @@ func (c *Common) Addr() Addr {
  \treturn c.addr
  }
 
-func (c *Common) Interface() Empty {
+func (c *Common) Interface() interface {} {
  \treturn sys.unreflect(*AddrToPtrAddr(c.addr), c.typ.String());
  }
 
@@ -714,12 +713,17 @@ func StructCreator(typ Type, addr Addr) Value {
  export type InterfaceValue interface {
  \tKind()\tint;
  \tType()\tType;
+\tGet()\tinterface {};
  }
 
  type InterfaceValueStruct struct {
  \tCommon
  }
 
+func (v *InterfaceValueStruct) Get() interface{} {
+\treturn *AddrToPtrInterface(v.addr);
+}
+
  func InterfaceCreator(typ Type, addr Addr) Value {
  \treturn &InterfaceValueStruct{ Common{InterfaceKind, typ, addr} }\
  }
@@ -824,7 +828,7 @@ export func NewOpenArrayValue(typ ArrayType, len, cap int) ArrayValue {
  \treturn NewValueAddr(typ, PtrRuntimeArrayToAddr(array));
  }
 
-export func NewValue(e Empty) Value {
+export func NewValue(e interface {}) Value {
  \tvalue, typestring  := sys.reflect(e);\
  \tp, ok := typecache[typestring];
  \tif !ok {

解説:

  • Empty interface{}型が削除され、Goの組み込みの空のインターフェース型interface{}が使用されるようになりました。これにより、リフレクションAPIがより標準的で汎用的なインターフェースの扱いをサポートします。
  • ValueインターフェースのInterface()メソッドの戻り値がEmptyからinterface{}に変更されました。
  • NewValue関数もEmptyではなくinterface{}を引数に取るようになりました。
  • InterfaceValueインターフェースにGet()メソッドが追加されました。このメソッドは、InterfaceValueStructの実装で、インターフェースが保持する実際の値を*AddrToPtrInterface(v.addr)を通じて取得し、interface{}型として返します。これは、リフレクションを通じてインターフェースの内部値にアクセスする際の重要な変更です。
  • AddrToPtrInterface(Addr) *interface{}という関数宣言が追加されました。これは、アセンブリコードで実装される、アドレスからインターフェースポインタへの変換関数です。

関連リンク

参考にした情報源リンク