[インデックス 1294] ファイルの概要
このコミットは、Go言語のfor...range
ループにおける新しい構文の導入に関するものです。具体的には、range
キーワードを用いたイテレーションにおいて、変数の宣言(:=
)と代入(=
)の形式、およびキーと値のペアを扱うための複数の構文(a range m
, a,b range m
, a:b range m
)を許可するようにコンパイラが変更されました。これにより、for
ループ内でrange
を使用する際の柔軟性が向上しました。
コミット
commit b79272d9a24256bee2755a21d6ee666e6eb6b9cb
Author: Ken Thompson <ken@golang.org>
Date: Sat Dec 6 13:40:30 2008 -0800
allowed syntax for range
a range m (implies :=)
a,b range m (implies :=)
a:b range m (implies :=)
a := range m
a,b := range m
a:b := range m
a = range m
a,b = range m
a:b = range m
R=r
OCL=20676
CL=20676
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b79272d9a24256bee2755a21d6ee666e6eb6b9cb
元コミット内容
allowed syntax for range
a range m (implies :=)
a,b range m (implies :=)
a:b range m (implies :=)
a := range m
a,b := range m
a:b := range m
a = range m
a,b = range m
a:b = range m
変更の背景
Go言語は、その設計初期からシンプルさと効率性を重視しており、ループ構造もその例外ではありませんでした。for
ループはGoにおける唯一のループ構文であり、他の言語が提供する多様なループ(while
, do-while
, foreach
など)の機能をすべてカバーするように設計されています。for...range
ループは、配列、スライス、文字列、マップ、チャネルといった組み込み型をイテレートするための強力なメカニズムとして導入されました。
このコミットが行われた2008年12月は、Go言語がまだ初期開発段階にあった時期です。当時のGo言語は、現在の安定版とは異なる構文やセマンティクスを持つ部分がありました。for...range
ループの初期の実装では、イテレーション変数の宣言や代入に関する構文が限定的であった可能性があります。
この変更の背景には、開発者がより自然で柔軟な方法でrange
ループを使用できるようにするという意図があったと考えられます。特に、:=
(短い変数宣言)と=
(代入)の両方でrange
を使用できるようにすること、そしてキーと値の両方、またはキーのみをイテレートする際の構文を明確にすることが目的でした。これにより、Go言語の表現力を高め、開発者がより簡潔で読みやすいコードを書けるようにすることが目指されました。
前提知識の解説
Go言語のfor...range
ループ
Go言語のfor...range
ループは、コレクション(配列、スライス、文字列、マップ、チャネル)の要素を順番に処理するための構文です。基本的な形式は以下の通りです。
for index, value := range collection {
// index と value を使った処理
}
- 配列とスライス:
index
には要素のインデックスが、value
にはそのインデックスに対応する要素の値が返されます。 - 文字列:
index
にはUnicodeコードポイントの開始バイトオフセットが、value
にはそのコードポイントのルーン(rune)が返されます。 - マップ:
index
にはキーが、value
にはそのキーに対応する値が返されます。マップのイテレーション順序は保証されません。 - チャネル:
value
にはチャネルから受信した値が返されます。チャネルが閉じられるまでイテレーションが続きます。
index
またはvalue
のいずれか一方のみが必要な場合は、不要な方を_
(ブランク識別子)で無視することができます。
for _, value := range collection { // 値のみが必要な場合
// value を使った処理
}
for index := range collection { // インデックス/キーのみが必要な場合
// index を使った処理
}
Go言語の変数宣言と代入
Go言語には、変数を宣言し値を割り当てるためのいくつかの方法があります。
-
短い変数宣言 (
:=
): 関数内で新しい変数を宣言し、初期値を割り当てるための簡潔な方法です。Goコンパイラが自動的に変数の型を推論します。name := "Alice" // string型と推論される count := 10 // int型と推論される
この構文は、既に宣言されている変数に対しては使用できません。少なくとも1つの新しい変数が宣言される必要があります。
-
変数宣言 (
var
):var
キーワードを使用して変数を宣言します。初期値を指定しない場合、変数はその型のゼロ値で初期化されます。var name string = "Bob" var count int
-
代入 (
=
): 既に宣言されている変数に新しい値を割り当てます。name = "Charlie" count = 20
このコミットは、これらの変数宣言と代入のメカニズムがfor...range
ループの構文とどのように統合されるか、特にrange
キーワードの直後に変数を配置する新しい形式を導入しています。
技術的詳細
このコミットは、Goコンパイラの字句解析器(lexer)と構文解析器(parser)、そして抽象構文木(AST)のウォーク(walk)処理に大きな変更を加えています。主な変更点は以下の通りです。
-
新しい構文の導入: コミットメッセージに示されているように、以下の新しい
range
構文が許可されるようになりました。a range m
(impliesa := range m
)a,b range m
(impliesa,b := range m
)a:b range m
(impliesa:b := range m
)a := range m
a,b := range m
a:b := range m
a = range m
a,b = range m
a:b = range m
ここで、
a
とb
はイテレーション変数、m
はイテレートされるマップ、配列、スライスなどのコレクションを表します。a:b
という形式は、マップのキーと値のペアをイテレートする際に、キーと値をそれぞれa
とb
に割り当てることを意図しています。 -
ASTノードの追加:
src/cmd/gc/go.h
において、新しいASTノードタイプORANGE
がenum
に追加されました。これは、range
ステートメントを表すために使用されます。 -
構文解析器の変更 (
src/cmd/gc/go.y
): Goコンパイラの構文解析器はYacc(またはBison)で記述されたgo.y
ファイルで定義されています。このファイルが変更され、新しいrange
構文を認識し、対応するASTノードを生成するように更新されました。orange_stmt
という新しいプロダクションルールが追加され、range
構文の様々なバリエーションを処理します。for_header
プロダクションが変更され、orange_stmt
をfor
ループのヘッダーとして受け入れるようになりました。- 特に注目すべきは、
ocolas
というプロダクションが追加され、:=
の有無をオプションとして扱えるようになった点です。これにより、a range m
のように:=
を省略した場合でも、内部的には:=
として扱われるようになります。 nod(ORANGE, $1, $4)
のように、ORANGE
ノードが生成され、そのleft
にはイテレーション変数(または変数のリスト)、right
にはイテレートされるコレクションが格納されます。etype
フィールドは、:=
(短い変数宣言)が使用されたかどうかを示すフラグとして利用されます(0
が:=
、1
が=
または省略形)。
-
セマンティックウォークの変更 (
src/cmd/gc/walk.c
):walk.c
ファイルには、ASTを走査し、セマンティックチェックやコード生成のための変換を行う関数が含まれています。dorange
関数のシグネチャが変更されました。以前はdorange(Node *k, Node *v, Node *m, int local)
のように複数の引数を取っていましたが、変更後はdorange(Node *nn)
のように単一のORANGE
ノードを受け取るようになりました。これにより、range
ステートメントの処理がより構造化され、ASTノードを通じて必要な情報が渡されるようになりました。dorange
関数内で、ORANGE
ノードからキー変数(k
)、値変数(v
)、イテレート対象(m
)、そしてlocal
フラグ(:=
か=
か)を抽出するロジックが追加されました。old2new
関数がlocal
フラグに基づいて呼び出されるようになりました。これは、新しい変数を宣言する場合(:=
)にのみ新しい変数を生成し、既存の変数に代入する場合(=
)は既存の変数を使用することを意味します。
-
オペレーション名の追加 (
src/cmd/gc/subr.c
):src/cmd/gc/subr.c
には、ASTノードのオペレーション名を文字列にマッピングする配列opnames
があります。この配列にORANGE
が追加され、デバッグやエラーメッセージの際にORANGE
ノードが正しく識別されるようになりました。
これらの変更により、Goコンパイラはfor...range
ループの新しい構文を正しく解析し、内部的なAST表現に変換し、最終的に実行可能なコードにコンパイルできるようになりました。特に、:=
と=
のセマンティクスの違いをdorange
関数で適切に処理することで、Go言語の変数宣言ルールとの整合性が保たれています。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更は以下の4つのファイルに集中しています。
-
src/cmd/gc/go.h
:enum
Op
に新しいASTノードタイプORANGE
が追加されました。dorange
関数のプロトタイプが変更され、引数がNode*
型を1つだけ取るようになりました。
--- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -300,7 +300,7 @@ enum ONAME, ONONAME, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, ODCLFUNC, ODCLFIELD, ODCLARG, - OLIST, OCMP, OPTR, OARRAY, + OLIST, OCMP, OPTR, OARRAY, ORANGE, ORETURN, OFOR, OIF, OSWITCH, OAS, OASOP, OCASE, OXCASE, OFALL, OXFALL, OGOTO, OPROC, ONEW, OEMPTY, OSELECT, @@ -806,7 +806,7 @@ int isandss(Type*, Node*);\n Node*\tconvas(Node*);\n void\tarrayconv(Type*, Node*);\n Node*\tcolas(Node*, Node*);\n-Node*\tdorange(Node*, Node*, Node*, int);\n+Node*\tdorange(Node*);\n Node*\treorder1(Node*);\n Node*\treorder2(Node*);\n Node*\treorder3(Node*);\n ```
-
src/cmd/gc/go.y
:orange_stmt
という新しい構文ルールが追加され、range
ループの様々な構文バリエーションを定義しています。for_header
ルールが変更され、orange_stmt
をfor
ループのヘッダーとして受け入れるようになりました。ocolas
という新しいルールが追加され、:=
の有無をオプションとして扱えるようになりました。
--- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -52,7 +52,7 @@ %type <node> Astmt Bstmt %type <node> for_stmt for_body for_header %type <node> if_stmt if_body if_header select_stmt -%type <node> simple_stmt osimple_stmt semi_stmt +%type <node> simple_stmt osimple_stmt orange_stmt semi_stmt %type <node> expr uexpr pexpr expr_list oexpr oexpr_list expr_list_r %type <node> exprsym3_list_r exprsym3 %type <node> name onew_name new_name new_name_list_r new_field @@ -416,7 +416,6 @@ simple_stmt: { if(addtop != N) fatal("exprsym3_list_r LCOLAS expr_list"); -\n \t\t$$ = rev($1);\n \t\t$$ = colas($$, $3);\n \t\t$$ = nod(OAS, $$, $3);\n@@ -555,31 +554,62 @@ compound_stmt: popdcl(); } +ocolas: +|\tLCOLAS + +orange_stmt: +\tosimple_stmt +|\texprsym3_list_r '=' LRANGE expr +\t{ +\t\t$$ = nod(ORANGE, $1, $4);\n+\t\t$$->etype = 0;\t// := flag +\t} +|\texprsym3 ':' exprsym3 '=' LRANGE expr +\t{ +\t\t$$ = nod(OLIST, $1, $3);\n+\t\t$$ = nod(ORANGE, $$, $6);\n+\t\t$$->etype = 0;\n+\t} +|\texprsym3_list_r ocolas LRANGE expr +\t{ +\t\t$$ = nod(ORANGE, $1, $4);\n+\t\t$$->etype = 1;\n+\t} +|\texprsym3 ':' exprsym3 ocolas LRANGE expr +\t{ +\t\t$$ = nod(OLIST, $1, $3);\n+\t\t$$ = nod(ORANGE, $$, $6);\n+\t\t$$->etype = 1;\n+\t} + for_header: -\tosimple_stmt ';' osimple_stmt ';' osimple_stmt +\tosimple_stmt ';' orange_stmt ';' osimple_stmt { +\t\tif($3 != N && $3->op == ORANGE) {\n+\t\t\t$$ = dorange($3);\n+\t\t\t$$->ninit = list($$->ninit, $1);\n+\t\t\t$$->nincr = list($$->nincr, $5);\n+\t\t\tbreak;\n+\t\t}\n // init ; test ; incr $$ = nod(OFOR, N, N);\n $$->ninit = $1;\n $$->ntest = $3;\n $$->nincr = $5;\n }\n -|\tosimple_stmt +|\torange_stmt { -\t\t// test +\t\t// range +\t\tif($1 != N && $1->op == ORANGE) {\n+\t\t\t$$ = dorange($1);\n+\t\t\tbreak;\n+\t\t}\n+\t\t// normal test $$ = nod(OFOR, N, N);\n $$->ninit = N;\n $$->ntest = $1;\n $$->nincr = N;\n }\n -|\tnew_name ':' new_name LRANGE expr - {\n - \t$$ = dorange($1, $3, $5, 1);\n - }\n -|\tnew_name LRANGE expr - {\n - \t$$ = dorange($1, N, $3, 1);\n - }\n for_body: for_header compound_stmt
-
src/cmd/gc/subr.c
:opnames
配列にORANGE
の文字列表現が追加されました。
--- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -687,6 +687,7 @@ opnames[] = [OREGISTER] = "REGISTER", [OINDREG] = "INDREG", [OSEND] = "SEND", + [ORANGE] = "RANGE", [ORECV] = "RECV", [OPTR] = "PTR", [ORETURN] = "RETURN",
-
src/cmd/gc/walk.c
:dorange
関数の実装が変更され、単一のORANGE
ノードを受け取るように修正されました。ORANGE
ノードからイテレーション変数とコレクションを抽出し、local
フラグ(:=
か=
か)に基づいてold2new
関数を呼び出すロジックが追加されました。
--- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -3033,14 +3033,32 @@ badt: return nl; } +/* + * rewrite a range statement + * k and v are names/new_names + * m is an array or map + * local is =/0 or :=/1 + */ Node*\n-dorange(Node *k, Node *v, Node *m, int local)\n+dorange(Node *nn)\n { +\tNode *k, *v, *m;\n Node *n, *hk, *on, *r, *a;\n Type *t, *th;\n +\tint local;\n -\tif(!local)\n -\t\tfatal("only local varables now"); +\tif(nn->op != ORANGE)\n +\t\tfatal("dorange not ORANGE"); +\n+\tk = nn->left;\n+\tm = nn->right;\n+\tlocal = nn->etype;\n+\n+\tv = N;\n+\tif(k->op == OLIST) {\n+\t\tv = k->right;\n+\t\tk = k->left;\n+\t}\n n = nod(OFOR, N, N);\n @@ -3073,11 +3091,13 @@ ary: n->nincr = nod(OASOP, hk, literal(1)); n->nincr->etype = OADD;\n -\tk = old2new(k, hk->type);\n+\tif(local)\n+\t\tk = old2new(k, hk->type);\n n->nbody = nod(OAS, k, hk);\n if(v != N) {\n -\t\tv = old2new(v, t->type);\n+\t\tif(local)\n+\t\t\tv = old2new(v, t->type);\n n->nbody = list(n->nbody,\n \tnod(OAS, v, nod(OINDEX, m, hk)) );\n }\n@@ -3112,7 +3132,8 @@ map: r = nod(OCALL, on, r);\n n->nincr = r;\n -\tk = old2new(k, t->down);\n+\tif(local)\n+\t\tk = old2new(k, t->down);\n if(v == N) {\n \ton = syslook("mapiter1", 1);\n \targtype(on, th);\n@@ -3122,7 +3143,8 @@ map: n->nbody = nod(OAS, k, r);\n goto out;\n }\n -\tv = old2new(v, t->type);\n+\tif(local)\n+\t\tv = old2new(v, t->type);\n on = syslook("mapiter2", 1);\n argtype(on, th);\n argtype(on, t->down);\
コアとなるコードの解説
src/cmd/gc/go.h
ORANGE
の追加:ORANGE
は、Goコンパイラがfor...range
ステートメントを内部的に表現するために使用する新しい抽象構文木(AST)ノードタイプです。コンパイラはソースコードを解析する際に、このORANGE
ノードを生成し、range
ループの構造と意味情報を保持します。dorange
関数のシグネチャ変更:dorange
関数は、range
ステートメントをより低レベルのfor
ループ構造に変換する役割を担っています。以前はイテレーション変数(キーと値)とコレクションを個別の引数として受け取っていましたが、変更後はORANGE
ノード全体を引数として受け取るようになりました。これにより、range
ステートメントに関するすべての情報が単一の構造体(ORANGE
ノード)にカプセル化され、関数のインターフェースが簡素化され、よりクリーンな設計になりました。
src/cmd/gc/go.y
このファイルはGo言語の文法を定義しており、Yacc/Bisonによって構文解析器が生成されます。
-
orange_stmt
ルールの追加: この新しいルールは、range
ステートメントの様々な構文バリエーションを認識するための中心的な役割を果たします。exprsym3_list_r '=' LRANGE expr
:a = range m
やa,b = range m
のような代入形式を扱います。nod(ORANGE, $1, $4)
でORANGE
ノードを生成し、$$->etype = 0;
で:=
フラグをセットしています。これは、=
による代入であっても、内部的には:=
と同様に新しい変数を扱うことを示唆している可能性があります(ただし、walk.c
でlocal
フラグとして使用され、old2new
の挙動を制御します)。exprsym3 ':' exprsym3 '=' LRANGE expr
:a:b = range m
のように、キーと値のペアを代入する形式を扱います。nod(OLIST, $1, $3)
でキーと値の変数をリストとしてまとめ、それをORANGE
ノードのleft
に渡します。exprsym3_list_r ocolas LRANGE expr
:a range m
やa,b range m
のように、:=
を省略した形式を扱います。ocolas
ルールがLCOLAS
(:=
)をオプションとして定義しているため、このルールで:=
の有無を吸収します。$$->etype = 1;
で:=
フラグをセットしています。exprsym3 ':' exprsym3 ocolas LRANGE expr
:a:b range m
のように、キーと値のペアで:=
を省略した形式を扱います。
-
for_header
ルールの変更:for
ループのヘッダー部分にorange_stmt
が直接現れることを許可するように変更されました。これにより、for range ...
という簡潔な構文が可能になります。if($3 != N && $3->op == ORANGE)
のブロックは、for
ループのテスト部分(2番目のセミコロンと3番目のセミコロンの間)がORANGE
ノードである場合に、dorange
関数を呼び出してrange
ステートメントを処理するようにしています。 また、for_header
が単一のorange_stmt
である場合も同様にdorange
を呼び出すように変更されています。
src/cmd/gc/subr.c
opnames
へのORANGE
追加:opnames
配列は、ASTノードのオペレーションコード(Op
enumの値)に対応する文字列名を提供します。ORANGE
がこの配列に追加されたことで、コンパイラのデバッグ出力やエラーメッセージにおいて、ORANGE
ノードが「RANGE」として表示されるようになり、可読性が向上します。
src/cmd/gc/walk.c
このファイルは、ASTを走査し、セマンティックチェックや最適化、中間コード生成のための変換を行う「ウォーカー」の役割を担っています。
dorange
関数の実装変更:dorange
関数は、ORANGE
ノードを受け取り、それをGoの内部的なfor
ループ構造(OFOR
ノード)に変換する中心的なロジックを含んでいます。- 関数冒頭で、引数
nn
がORANGE
ノードであることを確認しています。 k = nn->left; m = nn->right; local = nn->etype;
の行で、ORANGE
ノードからイテレーション変数(k
)、イテレート対象のコレクション(m
)、そしてlocal
フラグ(:=
か=
か)を抽出しています。local
フラグは、go.y
でetype
に設定された値(0
または1
)に対応します。if(k->op == OLIST)
ブロックは、a,b range m
やa:b range m
のように複数のイテレーション変数が指定された場合に、それらをk
とv
に分割します。old2new
関数の呼び出しにlocal
フラグが使用されています。old2new
は、新しい変数を宣言する必要がある場合(:=
の場合)に新しいシンボルを生成し、既存の変数に代入する場合(=
の場合)は既存のシンボルを使用するように制御します。これにより、:=
と=
のセマンティクスの違いが適切に処理されます。range
ループの内部的な変換ロジック(配列/スライス、マップのイテレーション)は、hk
(ハッシュキー)、on
(ヘルパー関数)、r
(結果)などのノードを生成し、最終的にOFOR
ノードのninit
(初期化)、ntest
(テスト)、nincr
(インクリメント)、nbody
(ループ本体)に変換されます。
- 関数冒頭で、引数
これらの変更により、Goコンパイラはfor...range
ループの新しい構文を正しく解釈し、Go言語のセマンティクスに沿った内部表現に変換できるようになりました。特に、go.y
での構文解析とwalk.c
でのセマンティック変換の連携が重要であり、ORANGE
ノードがその間の情報伝達の役割を担っています。
関連リンク
参考にした情報源リンク
- Go言語の
for...range
ループの歴史と進化に関する情報源(Web検索結果より)- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH65Uxo4K67rYLeuVjtp2XucV5uatvDHUdA682r9W-eupkGT6ednhG9mU4Obz344C_ukEpN3mpT5hEKA4YEK4lXEPGJBwMRIeuQieR-fyVYpjRENW6Y2ToJ90_3uouB7Tj1Gb4nXIvu8zIDXg_nDGLh2YRTO1mNLSM=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEXxmwhKN2gmJxZSmFfvVz7sXDH96qBO0Lgz_Axevr--DSdQXmxsggvVrkRBBVJyn-rwem-frwolf131dplLKxbEKmajzUQeZ96soA9wM1LWf75SvsebrsEOhGqWDTuQNTxp1eAIyT9uJTV
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE42QWMwSjYlbdNKAl10DMHFLo8jJqDYY-BGd4H8lE8q6QQP6yV3X-85AkYn6kdcSqZpU6nFTnTk_joQfn8TXEp06kfN3_RycWrlUc00uLPY23p0FV4F35en59Mu1LcPuC-GdZcdI6FNqkX-12rDQiiTkyyN_NjfUgMaL-amuQPPohBwoMGm0w=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHdpIdjzWKC-fdayT7VprhmNyAjvPQLyK6vHDMqNBjRaQ8psB49BDhHgQYhn7NKVUya6S7yWYOCHBLdE_KEDMAa3-GPZWuZrRlfiNpHdJ2MZd2SZpGP-iHt3d3d_
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEvjBXXTp3_lWHYJL_XxCtcIh1O3YQpmjFewuLQ2HbdZSZ3eq9D-uhceUMXVjNvmv_-HTYvmBOY6Dg68ISt3By5bzWO5cm3TEC-OY6FDjlLwwklkpggwYtsjonjTaxHWcgAhH4943Whebh06lfsjguMgPycnww=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFrxe9v23YnkUqmOYH4IrG9aAj1nMUXbkqTTfMrAHyhyoT2-G-cQXmy4R_g7hnSkMoxxKF1rNzr417LHO4W1JAzm5hUUr_s7bNisWoCi3N9_vJ0BYBxC-wnj5lyT8kScp4wx3V0WTo_Ji8F8IFS92aPI9hmcnz9hCTyBybMSJaSHAsdXw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFuNqc84MHkWaIsmk-rBENaE3zJnSwjdrCBkdHthWp4OGNThWiZpGBpz_gAMixykN8k1qhFaiTL_e-5boPaR7rWA_mDAZ4p9g9PSi07ii2jNB0UcS1PXOHMwv7AVg6nbWOEUoFsv017WSHKA28Y_s7XAJukNdHqfoYwShsx29RuBdIMy0fWz2huPulH4FBvYz2m5hv6GryQDWMe0V-ZG5hjSxW1fk9lAZY1kn4sdlzIUKIBmLWsluaUVIkerF0YRNzh0i9am6P
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHKgKxOVkMpm8t4O5mB6CVjhdnd7d3ROu1oTsTPZtktrULO6W7AnDgj8VkZ3zVg6TMK4vh4Pc_8dDcpVZEWQnMwgqmLjAORGx43XCyq7BYCLmPUWLo7RobQtSjEPLW-0I8N6yx3CWwO_L3Z_R3uszohsCO8duKekuxuElG-NlOwZmTtUJt7hIntcua-zIKj5S7f8s7gXLELx_J-
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFpn7i44RgJAVQmU0NvYNeQMYW8qqv7TGA5_aq22r9z0WNQGsZbVztOg0T_dsZGuNg1c9bfG_Ltou3TVx8YfaWW3QdUcexw9YaqapUDAO06KUdvomZ_7hpZEbTkJ-hQzns8v_0zlw9HKOv0HQdAWZWY67hQK9WnAlfzOarWd8VK22QlN3CMHmszME8KvL1cIciHiMiMu9D1_e9Kgc-OA==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGV86gaI2bml9XKgYHpP3NvPdc6MA9YqRDPUSqrtnBxEpTh_NXxcHwzQ4Mha28Tv1ZK2DQQf-DFWaLYf9JO4dpWrIo1hak9XkQUyiYA-u2uoJJ9fVr3ci9jX2834Ed63c7UT1sWDDEhEGRUtCpsQt_AgLKaNaGagYyOWftXGfAjj7B
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQERPq7pJCWZI2bvAPqvltvDBb_qCiw38yt4B5Xgj0tUofmChJpqpkuMqnrhdm1-GJXZJVaUg-_k6KLEirqEkFjR9e3wDR9Jh6iU2Kz6X55vdZNEZFQHgCBij9nsbiaGSz8cf5Zlu9c62scFQKqDWOo-wwryYaS9GVIrvUw=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEsFQuFW_7x3ypQ-0TEh-3Uvs5krQzc92ya3NDBLQy4jx4p2HAhmcf2qgb2FzksPU5hWz6f5mDDhwGBL1bp9EFaFb44liBrC8KK_U3m0U4S53FLHP35qu7bE_kvRRl7JTNTHw==