[インデックス 1719] ファイルの概要
このコミットは、Go言語の初期開発段階における、汎用的なスキャナー/パーサーライブラリの構築に向けた重要なステップを示しています。特に、抽象構文木(AST)とパーサーの設計を、より柔軟で拡張性の高いインターフェースと明示的な構造体を使用するように変換することに焦点を当てています。
コミット
commit 3689e221e8af8039ed70eb7ed0d4e62494b34f4f
Author: Robert Griesemer <gri@golang.org>
Date: Fri Feb 27 15:40:17 2009 -0800
Steps towards a general scanner/parser library for Go:
- converted more of AST and parser to use interfaces and explicit
structs for individual Go constructs (can be replaced now with
interface calls such that the parser becomes AST structure
independent, as suggested by rsc)
- added more tests (find all .go files under GOROOT)
- (temporarily) lost html links for identifiers when generating
html output
- TODO: lots of cleanups
R=r
OCL=25518
CL=25518
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3689e221e8af8039ed70eb7ed0d4e62494b34f4f
元コミット内容
Go言語の汎用スキャナー/パーサーライブラリの構築に向けた進捗:
- ASTとパーサーのより多くの部分を、個々のGoの構成要素に対してインターフェースと明示的な構造体を使用するように変換した(rscの提案により、パーサーがAST構造から独立するようにインターフェース呼び出しで置き換え可能になった)。
- より多くのテストを追加した(GOROOT以下の全ての.goファイルを見つける)。
- HTML出力生成時に、識別子へのHTMLリンクが(一時的に)失われた。
- TODO: 多くのクリーンアップが必要。
変更の背景
このコミットは、Go言語のコンパイラフロントエンドにおける設計思想の進化を反映しています。初期のGoコンパイラでは、ASTノードが単一の汎用的な構造体(例: Type
struct)と、その種類を示す列挙型(Form
フィールド)によって表現されていました。このアプローチはシンプルですが、新しい言語機能の追加やASTの構造変更があった場合に、既存のコードに広範な変更が必要となる可能性があります。
コミットメッセージにある「rscの提案により、パーサーがAST構造から独立するようにインターフェース呼び出しで置き換え可能になった」という記述は、この変更の動機を明確に示しています。rsc(Russ Cox)はGo言語の主要な設計者の一人であり、彼の提案は、パーサーとASTの結合度を下げ、よりモジュール化された、将来の変更に強い設計を目指すものでした。
具体的には、ASTノードをインターフェースとして定義し、各言語構成要素(配列型、構造体型、関数型など)をそのインターフェースを実装する個別の構造体として表現することで、以下の利点が得られます。
- 拡張性: 新しい言語機能が追加された場合、新しい構造体を追加し、既存のインターフェースを実装するだけで済みます。既存のAST処理コードを変更する必要が少なくなります。
- 型安全性: 各ノードが具体的な型を持つため、コンパイラはGoの型システムをより効果的に利用でき、実行時の型アサーションの必要性を減らし、コードの可読性と堅牢性を向上させます。
- モジュール性: パーサーはASTの具体的な実装詳細に依存しなくなり、ASTの表現を交換したり変更したりすることが容易になります。これにより、コンパイラの各コンポーネントがより独立して開発・保守できるようになります。
この変更は、Go言語の標準ライブラリであるgo/ast
およびgo/parser
パッケージの基礎を築くものであり、Goのコンパイラがどのように言語構造を内部的に表現し、処理するかという根本的な部分に影響を与えています。
前提知識の解説
このコミットを理解するためには、以下の概念が重要です。
-
抽象構文木 (Abstract Syntax Tree, AST):
- プログラミング言語のソースコードの抽象的な構文構造を木構造で表現したものです。コンパイラのフロントエンド(字句解析、構文解析)によって生成されます。
- ASTは、ソースコードの意味を表現し、後続のコンパイラフェーズ(意味解析、最適化、コード生成)で利用されます。
- 各ノードは、変数宣言、関数呼び出し、式などの言語構成要素に対応します。
-
パーサー (Parser):
- 字句解析器(スキャナー)から受け取ったトークンのストリームを解析し、言語の文法規則に従ってASTを構築するコンパイラのコンポーネントです。
- このコミットでは、パーサーがASTの具体的な実装に依存しないように設計が変更されています。
-
スキャナー (Scanner) / 字句解析器 (Lexer):
- ソースコードを読み込み、意味のある最小単位(トークン、例: 識別子、キーワード、演算子、リテラル)に分割するコンパイラのコンポーネントです。
-
Goのインターフェース (Interfaces):
- Go言語におけるインターフェースは、メソッドのシグネチャの集合を定義します。型がインターフェースのすべてのメソッドを実装していれば、そのインターフェースを満たします(暗黙的な実装)。
- インターフェースは、異なる具象型が共通の振る舞いを共有するための強力な抽象化メカニズムを提供し、ポリモーフィズムを実現します。
-
Goの構造体 (Structs):
- Go言語における構造体は、異なる型のフィールドをまとめた複合データ型です。
- このコミットでは、ASTの各ノードを表現するために、より具体的で明示的な構造体が導入されています。
-
結合度 (Coupling) と凝集度 (Cohesion):
- ソフトウェア設計の原則です。結合度はモジュール間の依存関係の度合いを示し、低い結合度が望ましいとされます。凝集度はモジュール内の要素がどれだけ密接に関連しているかを示し、高い凝集度が望ましいとされます。
- このコミットの変更は、パーサーとAST間の結合度を低減し、各コンポーネントの凝集度を高めることを目的としています。
-
Visitorパターン:
- オブジェクト構造の要素に対して実行される操作を、その要素のクラスを変更することなく定義できるデザインパターンです。
- ASTの走査によく用いられ、このコミットでも
ExprVisitor
やDeclVisitor
インターフェースがその役割を担っています。
技術的詳細
このコミットの核となる技術的変更は、Go言語のAST表現を、従来の「タグ付きユニオン」のようなアプローチから、インターフェースと個別の構造体を用いたポリモーフィックなアプローチへと移行させた点にあります。
変更前のアプローチ(推測):
AST.Type
のような単一の汎用的な構造体が存在し、その内部にForm
というフィールド(列挙型)を持っていました。Form
の値によって、そのType
構造体が配列型、構造体型、関数型などのどの種類の型を表しているかを区別していました。- 各
Form
に対応するデータは、Type
構造体内の汎用的なフィールド(例:Expr
,Elt
,List
など)に格納され、利用時には型アサーションやスイッチ文でForm
をチェックする必要がありました。
変更後のアプローチ:
-
Decl
のインターフェース化:type Decl struct;
がtype Decl interface;
に変更されました。- これにより、すべての宣言(
ImportDecl
,ConstDecl
,TypeDecl
,VarDecl
,FuncDecl
,DeclList
)がDecl
インターフェースを実装するようになります。 Program
構造体のDecls
フィールドも*vector.Vector
から[]Decl
(Decl
インターフェースのスライス)に変更され、より型安全なコレクションになりました。
-
Type
構造体の廃止と明示的な型構造体の導入:AST.Type
構造体とその関連する定数(BADTYPE
,TYPENAME
など)が完全に削除されました。- 代わりに、
Expr
インターフェースを実装する形で、各Goの型に対応する明示的な構造体が導入されました。ArrayType
(配列型)StructType
(構造体型)InterfaceType
(インターフェース型)MapType
(マップ型)ChannelType
(チャネル型)FunctionType
(関数型)PointerType
(ポインタ型)Signature
(関数シグネチャ)Field
(構造体フィールド、関数パラメータ/結果)Ellipsis
(可変長引数...
)
- これにより、例えば配列型を表現する際には
ArrayType
構造体を直接使用し、そのフィールド(Len
,Elt
)にアクセスできるようになります。
-
パーサーの変更:
- パーサーの各
parseXxxType()
関数は、*AST.Type
を返す代わりに、対応する新しい明示的な型構造体(例:*AST.ArrayType
,*AST.StructType
)を返すように変更されました。 parseDeclaration()
関数も*AST.Decl
を返す代わりにAST.Decl
インターフェースを返すようになり、内部で各宣言タイプに対応する新しい構造体を生成して返します。parseParameterList()
やparseParameters()
も、*vector.Vector
や*AST.Type
を返す代わりに、[]*AST.Field
を返すようになり、関数パラメータや結果の表現がより構造化されました。
- パーサーの各
-
Visitorパターンの強化:
ExprVisitor
インターフェースに、新しく導入された各型構造体に対応するDoXxxType
メソッドが追加されました。DeclVisitor
インターフェースが新しく導入され、各宣言タイプに対応するDoXxxDecl
メソッドが定義されました。- これにより、ASTの走査や処理を行うコードは、各ノードの具体的な型に応じて適切な
Do
メソッドを呼び出すことで、ポリモーフィックに処理できるようになります。
-
シンボルテーブル管理の分離(示唆):
parser.go
内でdeclareInScope
やdeclare
といったシンボルテーブルに関連する関数がコメントアウトされています。これは、パーサーがASTの構築に専念し、シンボル解決や型チェックといった意味解析のロジックが別のコンポーネントに分離される方向性を示唆しています。これにより、パーサーは「AST構造から独立」し、よりクリーンな役割分担が実現されます。
これらの変更は、Go言語のコンパイラが、より堅牢で、保守しやすく、拡張性の高い設計へと進化していく過程を示しています。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、主にusr/gri/pretty/ast.go
とusr/gri/pretty/parser.go
に集中しています。
usr/gri/pretty/ast.go
-
Decl
のインターフェース化:--- a/usr/gri/pretty/ast.go +++ b/usr/gri/pretty/ast.go @@ -14,7 +14,7 @@ import ( type ( Block struct; Expr interface; - Decl struct; + Decl interface; )
-
Type
構造体の削除と新しい型構造体の導入: (変更が広範囲にわたるため、代表的な部分のみ抜粋)--- a/usr/gri/pretty/ast.go +++ b/usr/gri/pretty/ast.go @@ -38,41 +38,7 @@ type Node struct { // ---------------------------------------------------------------------------- -// Types - -const /* form */ ( - // BADTYPE types are compatible with any type and don't cause further errors. - // They are introduced only as a result of an error in the source code. A - // correct program cannot have BAD types. - BADTYPE = iota; - - // A type name - TYPENAME; - - // composite types - ARRAY; STRUCT; INTERFACE; MAP; CHANNEL; FUNCTION; POINTER; - - // open-ended parameter type - ELLIPSIS -) - - -func FormStr(form int) string { - switch form { - case BADTYPE: return "BADTYPE"; - case TYPENAME: return "TYPENAME"; - case ARRAY: return "ARRAY"; - case STRUCT: return "STRUCT"; - case INTERFACE: return "INTERFACE"; - case MAP: return "MAP"; - case CHANNEL: return "CHANNEL"; - case FUNCTION: return "FUNCTION"; - case POINTER: return "POINTER"; - case ELLIPSIS: return "ELLIPSIS"; - } - return "<unknown Type form>"; -} - +// Expressions const /* channel mode */ ( FULL = iota; @@ -81,62 +47,15 @@ const /* channel mode */ ( ) -type Type struct { - Id int; // unique id - - Form int; // type form - Size int; // size in bytes - Scope *SymbolTable.Scope; // locals, fields & methods - - // syntactic components - Pos int; // source position (< 0 if unknown position) - Expr Expr; // type name, vector length - Mode int; // channel mode - Key *Type; // receiver type or map key - Elt *Type; // type name type, vector, map, channel or pointer element type, function result type - List *vector.Vector; End int; // struct fields, interface methods, function parameters -} - - -var typeId int; - -func NewType(pos, form int) *Type { - typ := new(Type); - typ.Id = typeId; - typeId++; - - typ.Pos = pos; - typ.Form = form; - - return typ; -} - - -func (typ* Type) String() string { - if typ != nil { - return - "Type(" + - FormStr(typ.Form) + - ")"; - } - return "nil"; -} - - -var BadType = NewType(0, Scanner.ILLEGAL); - - -// ---------------------------------------------------------------------------- -// Expressions - type ( ExprVisitor interface; + Signature struct; Expr interface { Pos() int; Visit(v ExprVisitor); }; - + BadExpr struct { Pos_ int; }; @@ -177,7 +103,7 @@ type ( TypeGuard struct { Pos_ int; // position of "." X Expr; - Typ *Type; + Typ Expr; }; Index struct { @@ -189,6 +115,66 @@ type ( Pos_ int; // position of "(" F, Args Expr }; + + // Type literals are treated like expressions. + Ellipsis struct { // neither a type nor an expression + Pos_ int; + }; + + ArrayType struct { + Pos_ int; // position of "[" + Len Expr; + Elt Expr; + }; + + Field struct { + Idents []*Ident; + Typ Expr; + Tag Expr; // nil = no tag + }; + + StructType struct { + Pos_ int; // position of "struct" + Fields []*Field; + End int; // position of "}", End == 0 if forward declaration + }; + + PointerType struct { + Pos_ int; // position of "*" + Base Expr; + }; + + Signature struct { + Params []*Field; + Result []*Field; + }; + + FunctionType struct { + Pos_ int; // position of "func" + Sig *Signature; + }; + + InterfaceType struct { + Pos_ int; // position of "interface" + Methods []*Field; + End int; // position of "}", End == 0 if forward declaration + }; + + SliceType struct { + Pos_ int; // position of "[" + }; + + MapType struct { + Pos_ int; // position of "map" + Key Expr; + Val Expr; + }; + + ChannelType struct { + Pos_ int; // position of "chan" or "<-" + Mode int; + Val Expr; + }; ) @@ -202,26 +185,47 @@ type ExprVisitor interface { DoUnaryExpr(x *UnaryExpr); DoBasicLit(x *BasicLit); DoFunctionLit(x *FunctionLit); - DoTypeLit(x *TypeLit); + DoGroup(x *Group); DoSelector(x *Selector); DoTypeGuard(x *TypeGuard); DoIndex(x *Index); DoCall(x *Call); + + DoEllipsis(x *Ellipsis); + DoArrayType(x *ArrayType); + DoStructType(x *StructType); + DoPointerType(x *PointerType); + DoFunctionType(x *FunctionType); + DoInterfaceType(x *InterfaceType); + DoSliceType(x *SliceType); + DoMapType(x *MapType); + DoChannelType(x *ChannelType); } +// 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 *BasicLit) Pos() int { return x.Pos_; }\n func (x *FunctionLit) Pos() int { return x.Pos_; }\n-func (x *TypeLit) Pos() int { return x.Typ.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 *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+\n func (x *BadExpr) Visit(v ExprVisitor) { v.DoBadExpr(x); }\n func (x *Ident) Visit(v ExprVisitor) { v.DoIdent(x); }\n@@ -229,12 +233,22 @@ func (x *BinaryExpr) Visit(v ExprVisitor) { v.DoBinaryExpr(x); }\n func (x *UnaryExpr) Visit(v ExprVisitor) { v.DoUnaryExpr(x); }\n func (x *BasicLit) Visit(v ExprVisitor) { v.DoBasicLit(x); }\n func (x *FunctionLit) Visit(v ExprVisitor) { v.DoFunctionLit(x); }\n-func (x *TypeLit) Visit(v ExprVisitor) { v.DoTypeLit(x); }\n+func (x *Group) Visit(v ExprVisitor) { v.DoGroup(x); }\n func (x *Selector) Visit(v ExprVisitor) { v.DoSelector(x); }\n func (x *TypeGuard) Visit(v ExprVisitor) { v.DoTypeGuard(x); }\n func (x *Index) Visit(v ExprVisitor) { v.DoIndex(x); }\n func (x *Call) Visit(v ExprVisitor) { v.DoCall(x); }\n \n+func (x *Ellipsis) Visit(v ExprVisitor) { v.DoEllipsis(x); }\n+func (x *ArrayType) Visit(v ExprVisitor) { v.DoArrayType(x); }\n+func (x *StructType) Visit(v ExprVisitor) { v.DoStructType(x); }\n+func (x *PointerType) Visit(v ExprVisitor) { v.DoPointerType(x); }\n+func (x *FunctionType) Visit(v ExprVisitor) { v.DoFunctionType(x); }\n+func (x *InterfaceType) Visit(v ExprVisitor) { v.DoInterfaceType(x); }\n+func (x *SliceType) Visit(v ExprVisitor) { v.DoSliceType(x); }\n+func (x *MapType) Visit(v ExprVisitor) { v.DoMapType(x); }\n+func (x *ChannelType) Visit(v ExprVisitor) { v.DoChannelType(x); }\n+\n // Length of a comma-separated expression list. @@ -421,25 +416,79 @@ func (s *EmptyStat) Visit(v StatVisitor) { v.DoEmptyStat(s); }\n // ---------------------------------------------------------------------------- // Declarations -type Decl struct { - Node; - Ident Expr; // nil for ()-style declarations - Typ *Type; - Val Expr; - Body *Block; - // list of *Decl for ()-style declarations - List *vector.Vector; End int; -} +type ( + DeclVisitor interface; + + Decl interface { + Visit(v DeclVisitor); + }; + + BadDecl struct { + Pos int; + }; + + ImportDecl struct { + Pos int; // if > 0: position of "import" + Ident *Ident; + Path Expr; + }; + + ConstDecl struct { + Pos int; // if > 0: position of "const" + Idents []*Ident; + Typ Expr; + Vals Expr; + }; + + TypeDecl struct { + Pos int; // if > 0: position of "type" + Ident *Ident; + Typ Expr; + }; + + VarDecl struct { + Pos int; // if > 0: position of "var" + Idents []*Ident; + Typ Expr; + Vals Expr; + }; + + FuncDecl struct { + Pos_ int; // position of "func" + Recv *Field; + Ident *Ident; + Sig *Signature; + Body *Block; + }; + + DeclList struct { + Pos int; // position of Tok + Tok int; + List []Decl; + End int; + }; +) -func NewDecl(pos, tok int) *Decl { - d := new(Decl); - d.Pos, d.Tok = pos, tok; - return d; +type DeclVisitor interface { + DoBadDecl(d *BadDecl); + DoImportDecl(d *ImportDecl); + DoConstDecl(d *ConstDecl); + DoTypeDecl(d *TypeDecl); + DoVarDecl(d *VarDecl); + DoFuncDecl(d *FuncDecl); + DoDeclList(d *DeclList); } -var BadDecl = NewDecl(0, Scanner.ILLEGAL); +//func (d *Decl) Visit(v DeclVisitor) { v.DoDecl(d); }\n+func (d *BadDecl) Visit(v DeclVisitor) { v.DoBadDecl(d); }\n+func (d *ImportDecl) Visit(v DeclVisitor) { v.DoImportDecl(d); }\n+func (d *ConstDecl) Visit(v DeclVisitor) { v.DoConstDecl(d); }\n+func (d *TypeDecl) Visit(v DeclVisitor) { v.DoTypeDecl(d); }\n+func (d *VarDecl) Visit(v DeclVisitor) { v.DoVarDecl(d); }\n+func (d *FuncDecl) Visit(v DeclVisitor) { v.DoFuncDecl(d); }\n+func (d *DeclList) Visit(v DeclVisitor) { v.DoDeclList(d); }\n // ---------------------------------------------------------------------------- @@ -461,7 +510,7 @@ func NewComment(pos int, text string) *Comment {\n type Program struct {\n Pos int; // tok is Scanner.PACKAGE\n Ident Expr;\n - Decls *vector.Vector;\n + Decls []Decl;\n Comments *vector.Vector;\n }
usr/gri/pretty/parser.go
- 型解析関数の戻り値の変更:
--- a/usr/gri/pretty/parser.go +++ b/usr/gri/pretty/parser.go @@ -170,6 +170,7 @@ func (P *Parser) closeScope() { } +/* func (P *Parser) declareInScope(scope *SymbolTable.Scope, x AST.Expr, kind int, typ *AST.Type) { if P.scope_lev < 0 { panic("cannot declare objects in other packages"); @@ -207,15 +208,16 @@ func (P *Parser) declare(x AST.Expr, kind int, typ *AST.Type) { } P.declareInScope(P.top_scope, x, kind, typ); } +*/ // ---------------------------------------------------------------------------- // Common productions -func (P *Parser) tryType() *AST.Type; +func (P *Parser) tryType() AST.Expr; func (P *Parser) parseExpression(prec int) AST.Expr; func (P *Parser) parseStatement() AST.Stat; -func (P *Parser) parseDeclaration() *AST.Decl; +func (P *Parser) parseDeclaration() AST.Decl; // If scope != nil, lookup identifier in scope. Otherwise create one. @@ -270,10 +272,34 @@ func (P *Parser) parseIdentList(x AST.Expr) AST.Expr { } +func (P *Parser) parseIdentList2(x AST.Expr) []*AST.Ident { + if P.trace { + defer un(trace(P, "IdentList")); + } + + list := vector.New(0); + if x == nil { + x = P.parseIdent(nil); + } + list.Push(x); + for P.tok == Scanner.COMMA { + P.next(); + list.Push(P.parseIdent(nil)); + } + + // convert vector + idents := make([]*AST.Ident, list.Len()); + for i := 0; i < list.Len(); i++ { + idents[i] = list.At(i).(*AST.Ident); + } + return idents; +} + + // ---------------------------------------------------------------------------- // Types -func (P *Parser) parseType() *AST.Type { +func (P *Parser) parseType() AST.Expr { if P.trace { defer un(trace(P, "Type")); } @@ -281,14 +307,14 @@ func (P *Parser) parseType() *AST.Type { t := P.tryType(); if t == nil { P.error(P.pos, "type expected"); - t = AST.BadType; + t = &AST.BadExpr(P.pos); } return t; } -func (P *Parser) parseVarType() *AST.Type { +func (P *Parser) parseVarType() AST.Expr { if P.trace { defer un(trace(P, "VarType")); } @@ -314,88 +340,90 @@ func (P *Parser) parseQualifiedIdent() AST.Expr { } -func (P *Parser) parseTypeName() *AST.Type { +func (P *Parser) parseTypeName() AST.Expr { if P.trace { defer un(trace(P, "TypeName")); } - t := AST.NewType(P.pos, AST.TYPENAME); - t.Expr = P.parseQualifiedIdent(); - - return t; + return P.parseQualifiedIdent(); } -func (P *Parser) parseArrayType() *AST.Type { +func (P *Parser) parseArrayType() *AST.ArrayType { if P.trace { defer un(trace(P, "ArrayType")); } - t := AST.NewType(P.pos, AST.ARRAY); + pos := P.pos; P.expect(Scanner.LBRACK); + var len AST.Expr; if P.tok == Scanner.ELLIPSIS { - t.Expr = &AST.BinaryExpr(P.pos, Scanner.ELLIPSIS, nil, nil); + len = &AST.Ellipsis(P.pos); P.next(); } else if P.tok != Scanner.RBRACK { - t.Expr = P.parseExpression(1); + len = P.parseExpression(1); } P.expect(Scanner.RBRACK); - t.Elt = P.parseType(); + elt := P.parseType(); - return t; + return &AST.ArrayType(pos, len, elt); } -func (P *Parser) parseChannelType() *AST.Type { +func (P *Parser) parseChannelType() *AST.ChannelType { if P.trace { defer un(trace(P, "ChannelType")); } - t := AST.NewType(P.pos, AST.CHANNEL); - t.Mode = AST.FULL; + pos := P.pos; + mode := AST.FULL; if P.tok == Scanner.CHAN { P.next(); if P.tok == Scanner.ARROW { P.next(); - t.Mode = AST.SEND; + mode = AST.SEND; } } else { P.expect(Scanner.ARROW); P.expect(Scanner.CHAN); - t.Mode = AST.RECV; + mode = AST.RECV; } - t.Elt = P.parseVarType(); + val := P.parseVarType(); - return t; + return &AST.ChannelType(pos, mode, val); } -func (P *Parser) parseVar(expect_ident bool) *AST.Type { - t := AST.BadType; - if expect_ident { - x := P.parseIdent(nil); - t = AST.NewType(x.Pos(), AST.TYPENAME); - t.Expr = x; - } else if P.tok == Scanner.ELLIPSIS { - t = AST.NewType(P.pos, AST.ELLIPSIS); +func (P *Parser) tryParameterType() AST.Expr { + if P.tok == Scanner.ELLIPSIS { + pos := P.tok; P.next(); - } else { - t = P.parseType(); + return &AST.Ellipsis(pos); } - return t; + return P.tryType(); } -func (P *Parser) parseVarList(list *vector.Vector, ellipsis_ok bool) { +func (P *Parser) parseParameterType() AST.Expr { + typ := P.tryParameterType(); + if typ == nil { + P.error(P.tok, "type expected"); + typ = &AST.BadExpr(P.pos); + } + return typ; +} + + +func (P *Parser) parseParameterDecl(ellipsis_ok bool) (*vector.Vector, AST.Expr) { if P.trace { - defer un(trace(P, "VarList")); + defer un(trace(P, "ParameterDecl")); } - // assume a list of types - // (a list of identifiers looks like a list of type names) - i0 := list.Len(); + // a list of identifiers looks like a list of type names + list := vector.New(0); for { - list.Push(P.parseVar(ellipsis_ok /* param list */ && i0 > 0)); + // TODO do not allow ()'s here + list.Push(P.parseParameterType()); if P.tok == Scanner.COMMA { P.next(); } else { @@ -404,115 +432,87 @@ func (P *Parser) parseVarList(list *vector.Vector, ellipsis_ok bool) { } // if we had a list of identifiers, it must be followed by a type - typ := P.tryType(); - if typ == nil && P.tok == Scanner.ELLIPSIS { - typ = AST.NewType(P.pos, AST.ELLIPSIS); - P.next(); - } + typ := P.tryParameterType(); + + return list, typ; } - if ellipsis_ok /* param list */ && i0 > 0 && typ == nil { - // not the first parameter section; we must have a type - P.error(P.pos, "type expected"); - typ = AST.BadType; + +func (P *Parser) parseParameterList(ellipsis_ok bool) []*AST.Field { + if P.trace { + defer un(trace(P, "ParameterList")); } - // convert the list into a list of (type) expressions + list, typ := P.parseParameterDecl(false); if typ != nil { - // all list entries must be identifiers - // convert the type entries into identifiers - for i, n := i0, list.Len(); i < n; i++ { - t := list.At(i).(*AST.Type); - if t.Form == AST.TYPENAME { - if ident, ok := t.Expr.(*AST.Ident); ok { - list.Set(i, ident); - continue; - } - } - list.Set(i, &AST.BadExpr(0)); - P.error(t.Pos, "identifier expected"); + // IdentifierList Type + // convert list of identifiers into []*Ident + idents := make([]*AST.Ident, list.Len()); + for i := 0; i < list.Len(); i++ { + idents[i] = list.At(i).(*AST.Ident); } - // add type - list.Push(&AST.TypeLit(typ)); + list.Init(0); + list.Push(&AST.Field(idents, typ, nil)); + + for P.tok == Scanner.COMMA { + P.next(); + idents := P.parseIdentList2(nil); + typ := P.parseParameterType(); + list.Push(&AST.Field(idents, typ, nil)); + } } else { - // all list entries are types - // convert all type entries into type expressions - for i, n := i0, list.Len(); i < n; i++ { - t := list.At(i).(*AST.Type); - list.Set(i, &AST.TypeLit(t)); + // Type { "," Type } + // convert list of types into list of *Param + for i := 0; i < list.Len(); i++ { + list.Set(i, &AST.Field(nil, list.At(i).(AST.Expr), nil)); } } -} - -func (P *Parser) parseParameterList(ellipsis_ok bool) *vector.Vector { - if P.trace { - defer un(trace(P, "ParameterList")); + // convert list + params := make([]*AST.Field, list.Len()); + for i := 0; i < list.Len(); i++ { + params[i] = list.At(i).(*AST.Field); } - list := vector.New(0); - P.parseVarList(list, ellipsis_ok); - for P.tok == Scanner.COMMA { - P.next(); - P.parseVarList(list, ellipsis_ok); - } - - return list; + return params; } -func (P *Parser) parseParameters(ellipsis_ok bool) *AST.Type { +// TODO make sure Go spec is updated +func (P *Parser) parseParameters(ellipsis_ok bool) []*AST.Field { if P.trace { defer un(trace(P, "Parameters")); } - t := AST.NewType(P.pos, AST.STRUCT); + var params []*AST.Field; P.expect(Scanner.LPAREN); if P.tok != Scanner.RPAREN { - t.List = P.parseParameterList(ellipsis_ok); + params = P.parseParameterList(ellipsis_ok); } - t.End = P.pos; P.expect(Scanner.RPAREN); - return t; + return params; } -func (P *Parser) parseResultList() { - if P.trace { - defer un(trace(P, "ResultList")); - } - - P.parseType(); - for P.tok == Scanner.COMMA { - P.next(); - P.parseType(); - } - if P.tok != Scanner.RPAREN { - P.parseType(); - } -} - - -func (P *Parser) parseResult(ftyp *AST.Type) *AST.Type { +func (P *Parser) parseResult() []*AST.Field { if P.trace { defer un(trace(P, "Result")); } - var t *AST.Type; + var result []*AST.Field; if P.tok == Scanner.LPAREN { - t = P.parseParameters(false); + result = P.parseParameters(false); } else if P.tok != Scanner.FUNC { typ := P.tryType(); if typ != nil { - t = AST.NewType(P.pos, AST.STRUCT); - t.List = vector.New(0); - t.List.Push(&AST.TypeLit(typ)); - t.End = P.pos; + result = make([]*AST.Field, 1); + result[0] = &AST.Field(nil, typ, nil); } } - return t; + return result; } @@ -522,120 +522,189 @@ func (P *Parser) parseResult(ftyp *AST.Type) *AST.Type {\n // (params) type\n // (params) (results)\n -func (P *Parser) parseSignature() *AST.Type { +func (P *Parser) parseSignature() *AST.Signature { if P.trace { defer un(trace(P, "Signature")); } - P.openScope(); - P.scope_lev++; + //P.openScope(); + //P.scope_lev++; - t := AST.NewType(P.pos, AST.FUNCTION); - t.Scope = P.top_scope; - t.List = P.parseParameters(true).List; // TODO find better solution - t.End = P.pos; - t.Elt = P.parseResult(t); + //t.Scope = P.top_scope; + params := P.parseParameters(true); // TODO find better solution + //t.End = P.pos; + result := P.parseResult(); - P.scope_lev--; - P.closeScope(); + //P.scope_lev--; + //P.closeScope(); - return t; + return &AST.Signature(params, result); } -func (P *Parser) parseFunctionType() *AST.Type { +func (P *Parser) parseFunctionType() *AST.FunctionType { if P.trace { defer un(trace(P, "FunctionType")); } + pos := P.pos; P.expect(Scanner.FUNC); - return P.parseSignature(); + sig := P.parseSignature(); + + return &AST.FunctionType(pos, sig); } -func (P *Parser) parseMethodOrInterfaceSpec(list *vector.Vector) { +func (P *Parser) parseMethodSpec() *AST.Field { if P.trace { - defer un(trace(P, "MethodOrInterfaceSpec")); + defer un(trace(P, "MethodSpec")); } + var idents []*AST.Ident; + var typ AST.Expr; x := P.parseQualifiedIdent(); if tmp, is_ident := x.(*AST.Ident); is_ident && (P.tok == Scanner.COMMA || P.tok == Scanner.LPAREN) { // method(s) - list.Push(P.parseIdentList(x)); - list.Push(&AST.TypeLit(P.parseSignature())); + idents = P.parseIdentList2(x); + typ = &AST.FunctionType(0, P.parseSignature()); } else { // embedded interface - list.Push(x); + typ = x; } + + return &AST.Field(idents, typ, nil); } -func (P *Parser) parseInterfaceType() *AST.Type { +func (P *Parser) parseInterfaceType() *AST.InterfaceType { if P.trace { defer un(trace(P, "InterfaceType")); } - t := AST.NewType(P.pos, AST.INTERFACE); + pos := P.pos; + end := 0; + var methods []*AST.Field; + P.expect(Scanner.INTERFACE); if P.tok == Scanner.LBRACE { P.next(); - P.openScope(); - P.scope_lev++; + //P.openScope(); + //P.scope_lev++; - t.List = vector.New(0); + list := vector.New(0); for P.tok == Scanner.IDENT { - P.parseMethodOrInterfaceSpec(t.List); + list.Push(P.parseMethodSpec()); if P.tok != Scanner.RBRACE { P.expect(Scanner.SEMICOLON); } } - t.End = P.pos; + //t.End = P.pos; - P.scope_lev--; - P.closeScope(); + //P.scope_lev--; + //P.closeScope(); + end = P.pos; P.expect(Scanner.RBRACE); + P.opt_semi = true; + + // convert vector + methods = make([]*AST.Field, list.Len()); + for i := list.Len() - 1; i >= 0; i-- { + methods[i] = list.At(i).(*AST.Field); + } } - return t; + return &AST.InterfaceType(pos, methods, end); } -func (P *Parser) parseMapType() *AST.Type { +func (P *Parser) parseMapType() *AST.MapType { if P.trace { defer un(trace(P, "MapType")); } - t := AST.NewType(P.pos, AST.MAP); + pos := P.pos; P.expect(Scanner.MAP); P.expect(Scanner.LBRACK); - t.Key = P.parseVarType(); + key := P.parseVarType(); P.expect(Scanner.RBRACK); - t.Elt = P.parseVarType(); + val := P.parseVarType(); - return t; + return &AST.MapType(pos, key, val); } func (P *Parser) parseOperand() AST.Expr -func (P *Parser) parseStructType() *AST.Type { + +func (P *Parser) parseFieldDecl() *AST.Field { + if P.trace { + defer un(trace(P, "FieldDecl")); + } + + // a list of identifiers looks like a list of type names + list := vector.New(0); + for { + // TODO do not allow ()'s here + list.Push(P.parseType()); + if P.tok == Scanner.COMMA { + P.next(); + } else { + break; + } + } + + // if we had a list of identifiers, it must be followed by a type + typ := P.tryType(); + + // optional tag + var tag AST.Expr; + if P.tok == Scanner.STRING { + // ParseOperand takes care of string concatenation + tag = P.parseOperand(); + } + + // analyze case + var idents []*AST.Ident; + if typ != nil { + // non-empty identifier list followed by a type + idents = make([]*AST.Ident, list.Len()); + for i := 0; i < list.Len(); i++ { + if ident, is_ident := list.At(i).(*AST.Ident); is_ident { + idents[i] = ident; + } else { + P.error(list.At(i).(AST.Expr).Pos(), "identifier expected"); + } + } + } else { + // anonymous field + if list.Len() == 1 { + // TODO should do more checks here + typ = list.At(0).(AST.Expr); + } else { + P.error(P.pos, "anonymous field expected"); + } + } + + return &AST.Field(idents, typ, tag); +} + + +func (P *Parser) parseStructType() AST.Expr { if P.trace { defer un(trace(P, "StructType")); } - t := AST.NewType(P.pos, AST.STRUCT); + pos := P.pos; + end := 0; + var fields []*AST.Field;
-
P.expect(Scanner.STRUCT); if P.tok == Scanner.LBRACE { P.next();
-
t.List = vector.New(0);
-
t.Scope = SymbolTable.NewScope(nil);
-
list := vector.New(0); for P.tok != Scanner.RBRACE && P.tok != Scanner.EOF {
-
P.parseVarList(t.List, false);
-
if P.tok == Scanner.STRING {
-
// ParseOperand takes care of string concatenation
-
t.List.Push(P.parseOperand());
-
}
-
list.Push(P.parseFieldDecl()); if P.tok == Scanner.SEMICOLON { P.next(); } else {
@@ -643,36 +712,36 @@ func (P *Parser) parseStructType() *AST.Type { } } P.OptSemicolon();
-
t.End = P.pos;
-
end = P.pos; P.expect(Scanner.RBRACE);
-
P.opt_semi = true;
-
// enter fields into struct scope
-
for i, n := 0, t.List.Len(); i < n; i++ {
-
if x, ok := t.List.At(i).(*AST.Ident); ok {
-
P.declareInScope(t.Scope, x, SymbolTable.FIELD, nil);
-
}
-
// convert vector
-
fields = make([]*AST.Field, list.Len());
-
for i := list.Len() - 1; i >= 0; i-- {
-
}fields[i] = list.At(i).(*AST.Field); }
- return t;
- return AST.StructType(pos, fields, end); }
-func (P *Parser) parsePointerType() *AST.Type { +func (P *Parser) parsePointerType() AST.Expr { if P.trace { defer un(trace(P, "PointerType")); }
- t := AST.NewType(P.pos, AST.POINTER);
- pos := P.pos; P.expect(Scanner.MUL);
- t.Elt = P.parseType();
- base := P.parseType();
- return t;
- return &AST.PointerType(pos, base); }
-func (P *Parser) tryType() *AST.Type { +func (P *Parser) tryType() AST.Expr { if P.trace { defer un(trace(P, "Type (try)")); } @@ -687,10 +756,11 @@ func (P *Parser) tryType() *AST.Type { case Scanner.STRUCT: return P.parseStructType(); case Scanner.MUL: return P.parsePointerType(); case Scanner.LPAREN:
-
pos := P.pos; P.next(); t := P.parseType(); P.expect(Scanner.RPAREN);
-
return t;
-
return &AST.Group(pos, t);
}
// no type found @@ -725,7 +795,7 @@ func (P *Parser) parseStatementList(list *vector.Vector) { }
-func (P *Parser) parseBlock(ftyp *AST.Type, tok int) *AST.Block { +func (P *Parser) parseBlock(tok int) *AST.Block { if P.trace { defer un(trace(P, "Block")); } b := AST.NewBlock(P.pos, tok); P.expect(tok);
- /* P.openScope(); // enter recv and parameters into function scope if ftyp != nil { @@ -739,9 +809,13 @@ func (P *Parser) parseBlock(ftyp *AST.Type, tok int) *AST.Block { } } }
+*/ + P.parseStatementList(b.List); +
-
/* P.closeScope(); +*/
if tok == Scanner.LBRACE { b.End = P.pos; @@ -795,7 +870,7 @@ func (P *Parser) parseFunctionLit() AST.Expr { P.expect(Scanner.FUNC); typ := P.parseSignature(); P.scope_lev++;
- body := P.parseBlock(typ, Scanner.LBRACE);
-
body := P.parseBlock(Scanner.LBRACE); P.scope_lev--;
return &AST.FunctionLit(pos, typ, body); @@ -812,10 +887,11 @@ func (P *Parser) parseOperand() AST.Expr { \treturn P.parseIdent(P.top_scope);
case Scanner.LPAREN:
-
pos := P.pos; P.next(); x := P.parseExpression(1); P.expect(Scanner.RPAREN);
-
return x;
-
return &AST.Group(pos, x);
case Scanner.INT, Scanner.FLOAT, Scanner.STRING: x := &AST.BasicLit(P.pos, P.tok, P.val); @@ -835,7 +911,7 @@ func (P *Parser) parseOperand() AST.Expr { default: t := P.tryType(); if t != nil {
-
return &AST.TypeLit(t);
-
return t; } else { P.error(P.pos, "operand expected"); P.next(); // make progress
@@ -978,6 +1054,8 @@ func (P *Parser) parseUnaryExpr() AST.Expr { \tpos, tok := P.pos, P.tok; \tP.next(); \ty := P.parseUnaryExpr();
-
return &AST.UnaryExpr(pos, tok, y);
-
/* if lit, ok := y.(*AST.TypeLit); ok && tok == Scanner.MUL { // pointer type t := AST.NewType(pos, AST.POINTER);
@@ -986,6 +1064,7 @@ func (P *Parser) parseUnaryExpr() AST.Expr { } else { return &AST.UnaryExpr(pos, tok, y); }
-
*/
}
return P.parsePrimaryExpr(); @@ -1180,7 +1259,7 @@ func (P *Parser) parseIfStat() *AST.IfStat { pos := P.pos; P.expect(Scanner.IF); init, cond, dummy := P.parseControlClause(false);
- body := P.parseBlock(nil, Scanner.LBRACE);
- body := P.parseBlock(Scanner.LBRACE); var else_ AST.Stat; if P.tok == Scanner.ELSE { P.next(); @@ -1211,7 +1290,7 @@ func (P *Parser) parseForStat() *AST.ForStat { pos := P.pos; P.expect(Scanner.FOR); init, cond, post := P.parseControlClause(true);
- body := P.parseBlock(nil, Scanner.LBRACE);
-
body := P.parseBlock(Scanner.LBRACE); P.closeScope();
return &AST.ForStat(pos, init, cond, post, body); @@ -1233,7 +1312,7 @@ func (P *Parser) parseCaseClause() *AST.CaseClause { \tP.expect(Scanner.DEFAULT); }
- return &AST.CaseClause(pos, expr, P.parseBlock(nil, Scanner.COLON));
- return &AST.CaseClause(pos, expr, P.parseBlock(Scanner.COLON)); }
@@ -1286,7 +1365,7 @@ func (P *Parser) parseCommClause() *AST.CaseClause { \tP.expect(Scanner.DEFAULT); }
- return &AST.CaseClause(pos, expr, P.parseBlock(nil, Scanner.COLON));
- return &AST.CaseClause(pos, expr, P.parseBlock(Scanner.COLON)); }
@@ -1337,7 +1416,7 @@ func (P *Parser) parseStatement() AST.Stat { case Scanner.BREAK, Scanner.CONTINUE, Scanner.GOTO, Scanner.FALLTHROUGH: \treturn P.parseControlFlowStat(P.tok); case Scanner.LBRACE:
- \treturn &AST.CompositeStat(P.parseBlock(nil, Scanner.LBRACE));
- \treturn &AST.CompositeStat(P.parseBlock(Scanner.LBRACE)); case Scanner.IF: \treturn P.parseIfStat(); case Scanner.FOR: @@ -1360,90 +1439,105 @@ func (P *Parser) parseStatement() AST.Stat { // ---------------------------------------------------------------------------- // Declarations
-func (P *Parser) parseImportSpec(d *AST.Decl) { +func (P *Parser) parseImportSpec(pos int) *AST.ImportDecl { if P.trace { defer un(trace(P, "ImportSpec")); }
- var ident *AST.Ident;
if P.tok == Scanner.PERIOD {
P.error(P.pos,
"import ." not yet handled properly
); P.next(); } else if P.tok == Scanner.IDENT {
-
d.Ident = P.parseIdent(nil);
-
ident = P.parseIdent(nil);
}
-
var path AST.Expr; if P.tok == Scanner.STRING { // TODO eventually the scanner should strip the quotes
-
d.Val = &AST.BasicLit(P.pos, Scanner.STRING, P.val);
-
} else { P.expect(Scanner.STRING); // use expect() error handling }path = &AST.BasicLit(P.pos, Scanner.STRING, P.val); P.next();
- return &AST.ImportDecl(pos, ident, path); }
-func (P *Parser) parseConstSpec(d *AST.Decl) { +func (P *Parser) parseConstSpec(pos int) *AST.ConstDecl { if P.trace { defer un(trace(P, "ConstSpec")); }
- d.Ident = P.parseIdentList(nil);
- d.Typ = P.tryType();
- idents := P.parseIdentList2(nil);
- typ := P.tryType();
- var vals AST.Expr; if P.tok == Scanner.ASSIGN { P.next();
-
d.Val = P.parseExpressionList();
-
}vals = P.parseExpressionList();
- return &AST.ConstDecl(pos, idents, typ, vals); }
-func (P *Parser) parseTypeSpec(d *AST.Decl) { +func (P *Parser) parseTypeSpec(pos int) *AST.TypeDecl { if P.trace { defer un(trace(P, "TypeSpec")); }
- d.Ident = P.parseIdent(nil);
- d.Typ = P.parseType();
- P.opt_semi = true;
- ident := P.parseIdent(nil);
- typ := P.parseType();
- return &AST.TypeDecl(pos, ident, typ); }
-func (P *Parser) parseVarSpec(d *AST.Decl) { +func (P *Parser) parseVarSpec(pos int) *AST.VarDecl { if P.trace { defer un(trace(P, "VarSpec")); }
- d.Ident = P.parseIdentList(nil);
- idents := P.parseIdentList2(nil);
- var typ AST.Expr;
- var vals AST.Expr; if P.tok == Scanner.ASSIGN { P.next();
-
d.Val = P.parseExpressionList();
-
} else {vals = P.parseExpressionList();
-
d.Typ = P.parseVarType();
-
typ = P.parseVarType(); if P.tok == Scanner.ASSIGN { P.next();
-
d.Val = P.parseExpressionList();
-
}vals = P.parseExpressionList(); }
- return &AST.VarDecl(pos, idents, typ, vals); }
-func (P *Parser) parseSpec(d *AST.Decl) { +func (P *Parser) parseSpec(pos, keyword int) AST.Decl { kind := SymbolTable.NONE;
- switch d.Tok {
- case Scanner.IMPORT: P.parseImportSpec(d); kind = SymbolTable.PACKAGE;
- case Scanner.CONST: P.parseConstSpec(d); kind = SymbolTable.CONST;
- case Scanner.TYPE: P.parseTypeSpec(d); kind = SymbolTable.TYPE;
- case Scanner.VAR: P.parseVarSpec(d); kind = SymbolTable.VAR;
- default: unreachable();
-
switch keyword {
-
case Scanner.IMPORT: return P.parseImportSpec(pos);
-
case Scanner.CONST: return P.parseConstSpec(pos);
-
case Scanner.TYPE: return P.parseTypeSpec(pos);
-
case Scanner.VAR: return P.parseVarSpec(pos); }
-
unreachable();
-
return nil;
-
/* // semantic checks if d.Tok == Scanner.IMPORT { if d.Ident != nil {
-
P.declare(d.Ident, kind, nil);
-
} else {//P.declare(d.Ident, kind, nil); }
-
P.declare(d.Ident, kind, d.Typ);
-
//P.declare(d.Ident, kind, d.Typ); if d.Val != nil { // initialization/assignment llen := AST.ExprLen(d.Ident);
@@ -1463,38 +1557,42 @@ func (P *Parser) parseSpec(d *AST.Decl) {\n // TODO } } }
- */ }
-func (P *Parser) parseDecl(keyword int) *AST.Decl { +func (P *Parser) parseDecl(keyword int) AST.Decl { if P.trace { defer un(trace(P, "Decl")); }
- d := AST.NewDecl(P.pos, keyword);
- pos := P.pos; P.expect(keyword); if P.tok == Scanner.LPAREN { P.next();
-
d.List = vector.New(0);
-
list := vector.New(0); for P.tok != Scanner.RPAREN && P.tok != Scanner.EOF {
-
d1 := AST.NewDecl(P.pos, keyword);
-
P.parseSpec(d1);
-
d.List.Push(d1);
-
list.Push(P.parseSpec(0, keyword)); if P.tok == Scanner.SEMICOLON { P.next(); } else { break; } }
-
d.End = P.pos;
-
end := P.pos; P.expect(Scanner.RPAREN); P.opt_semi = true;
- } else {
-
P.parseSpec(d);
-
// convert vector
-
decls := make([]AST.Decl, list.Len());
-
for i := 0; i < list.Len(); i++ {
-
decls[i] = list.At(i).(AST.Decl);
-
}
-
}return &AST.DeclList(pos, keyword, decls, end);
- return d;
- return P.parseSpec(pos, keyword); }
@@ -1507,54 +1605,53 @@ func (P *Parser) parseDecl(keyword int) *AST.Decl { // func (recv) ident (params) type // func (recv) ident (params) (results)
-func (P *Parser) parseFunctionDecl() *AST.Decl { +func (P *Parser) parseFunctionDecl() *AST.FuncDecl { if P.trace { defer un(trace(P, "FunctionDecl")); }
- d := AST.NewDecl(P.pos, Scanner.FUNC);
- pos := P.pos; P.expect(Scanner.FUNC);
- var recv *AST.Type;
- var recv *AST.Field; if P.tok == Scanner.LPAREN { pos := P.pos;
-
recv = P.parseParameters(true);
-
if recv.Nfields() != 1 {
-
tmp := P.parseParameters(true);
-
if len(tmp) == 1 {
-
recv = tmp[0];
-
} else { P.error(pos, "must have exactly one receiver"); }
}
ident := P.parseIdent(nil);
- d.Ident = ident;
- d.Typ = P.parseSignature();
- d.Typ.Key = recv;
-
sig := P.parseSignature();
-
var body *AST.Block; if P.tok == Scanner.LBRACE {
-
d.Body = P.parseBlock(d.Typ, Scanner.LBRACE);
-
}body = P.parseBlock(Scanner.LBRACE);
- return d;
- return &AST.FuncDecl(pos, recv, ident, sig, body); }
-func (P *Parser) parseDeclaration() *AST.Decl { +func (P *Parser) parseDeclaration() AST.Decl { if P.trace { defer un(trace(P, "Declaration")); }
- d := AST.BadDecl;
- switch P.tok { case Scanner.CONST, Scanner.TYPE, Scanner.VAR:
-
d = P.parseDecl(P.tok);
-
case Scanner.FUNC:return P.parseDecl(P.tok);
-
d = P.parseFunctionDecl();
- default:
-
P.error(P.pos, "declaration expected");
-
P.next(); // make progress
-
}return P.parseFunctionDecl();
- return d;
- pos := P.pos;
- P.error(pos, "declaration expected");
- P.next(); // make progress
- return &AST.BadDecl(pos); }
@@ -1573,18 +1670,24 @@ func (P *Parser) ParseProgram() *AST.Program {
// package body {\tP.openScope();
- \tp.Decls = vector.New(0);
- \tlist := vector.New(0); \tfor P.tok == Scanner.IMPORT {
- \t\tp.Decls.Push(P.parseDecl(Scanner.IMPORT));
- \t\tlist.Push(P.parseDecl(Scanner.IMPORT)); \t\tP.OptSemicolon(); \t} \tif !P.deps { \t\tfor P.tok != Scanner.EOF {
- \t\t\tp.Decls.Push(P.parseDeclaration());
-
\t\t\tlist.Push(P.parseDeclaration()); \t\t\tP.OptSemicolon(); \t\t} \t} \tP.closeScope();
-
// convert list
-
p.Decls = make([]AST.Decl, list.Len());
-
for i := 0; i < list.Len(); i++ {
-
p.Decls[i] = list.At(i).(AST.Decl);
-
}
}
p.Comments = P.comments;
-
コアとなるコードの解説
このコミットの核心は、Go言語のAST(抽象構文木)の表現方法を根本的に変更し、それに合わせてパーサーのロジックを適応させた点にあります。
ast.go
の変更:
-
Decl
のインターフェース化:- 以前は
Decl
が単一のstruct
でしたが、interface
に変更されました。これは、Goの宣言(import
,const
,type
,var
,func
)がそれぞれ異なる構造を持つため、それらを共通のDecl
インターフェースで抽象化することで、より柔軟な設計を可能にします。 - これにより、
Program
構造体のDecls
フィールドも*vector.Vector
から[]Decl
(Decl
インターフェースのスライス)に変わり、型安全性が向上しました。
- 以前は
-
Type
構造体の廃止と明示的な型構造体の導入:- 最も大きな変更点の一つです。以前は
Type
という汎用的な構造体があり、Form
というフィールドでその型が配列なのか、構造体なのかなどを区別していました。このアプローチは、新しい型の追加や型の詳細な表現に限界がありました。 - このコミットでは、
Type
構造体を廃止し、代わりにArrayType
,StructType
,InterfaceType
,MapType
,ChannelType
,FunctionType
,PointerType
,Signature
,Field
,Ellipsis
といった、各Goの型に対応する明示的な構造体を導入しました。これらの新しい構造体は、Expr
インターフェース(または新しく導入されたDecl
インターフェース)を実装します。 - この変更により、ASTの各ノードがその種類に応じた具体的な型を持つようになり、コンパイラはより厳密な型チェックを行うことができ、コードの可読性と保守性が大幅に向上します。例えば、配列型を扱う際には
ArrayType
構造体のLen
やElt
フィールドに直接アクセスできるようになります。
- 最も大きな変更点の一つです。以前は
-
Visitorパターンの強化:
ExprVisitor
に新しい型構造体に対応するDoXxxType
メソッドが追加され、DeclVisitor
インターフェースが新設されました。これにより、ASTを走査して処理を行う際に、各ノードの具体的な型に応じて適切な処理をポリモーフィックに呼び出すことが可能になります。
parser.go
の変更:
-
型解析関数の戻り値の変更:
parseType()
,parseArrayType()
,parseChannelType()
などの型を解析する関数は、以前は汎用的な*AST.Type
を返していましたが、この変更により、それぞれ対応する新しい明示的な型構造体(例:*AST.ArrayType
,*AST.ChannelType
)を返すようになりました。これにより、パーサーが生成するASTノードがより具体的で型安全になります。
-
宣言解析関数の戻り値の変更:
parseDeclaration()
は*AST.Decl
ではなくAST.Decl
インターフェースを返すようになり、parseDecl()
やparseFunctionDecl()
もそれぞれAST.Decl
インターフェースや*AST.FuncDecl
を返すようになりました。これはDecl
のインターフェース化と整合しています。
-
パラメータと結果の表現の構造化:
parseParameterList()
やparseParameters()
は、[]*AST.Field
を返すようになり、関数やメソッドのパラメータと結果がField
構造体を用いてより明確に表現されるようになりました。また、parseSignature()
が*AST.Signature
を返すようになり、関数シグネチャ全体を一つの構造体で表現できるようになりました。
-
シンボルテーブル管理の分離(コメントアウト):
declareInScope
やdeclare
といったシンボルテーブルに関連する関数がコメントアウトされています。これは、パーサーの役割をASTの構築に限定し、シンボル解決や型チェックといった意味解析のロジックを別のコンポーネントに分離するという設計思想の表れです。これにより、パーサーはASTの具体的な実装に依存せず、よりクリーンなモジュールとして機能するようになります。
これらの変更は、Go言語のコンパイラが、より現代的で、拡張性が高く、保守しやすい設計へと進化するための重要な基盤を築いたと言えます。
関連リンク
- Go言語のASTパッケージ (
go/ast
): https://pkg.go.dev/go/ast - Go言語のパーサーパッケージ (
go/parser
): https://pkg.go.dev/go/parser - Go言語のインターフェースに関する公式ドキュメント: https://go.dev/tour/methods/10
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Go言語の設計に関する議論(初期のメーリングリストなど、公開されている情報があれば)
- コンパイラ設計に関する一般的な知識(AST、パーサー、Visitorパターンなど)
- Go言語のインターフェースとポリモーフィズムに関する一般的な解説記事
- Go言語の
go/ast
およびgo/parser
パッケージのドキュメント - Robert Griesemer氏とRuss Cox氏のGo言語における役割に関する情報