這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
上回說到6g程式應該如何看,這回書接上文,從yylex這個函數看起。vim -t yylex仍然選gc那個。
1107 int321108 yylex(void)1109 {1110 int lx;1111 1112 lx = _yylex();//主要處理函數1113 1114 if(curio.nlsemi && lx == EOF) {1115 // if the nlsemi bit is set, we'd be willing to1116 // insert a ; if we saw a /n, but we didn't.1117 // that means the final /n is missing.1118 // complain here, because we can give a1119 // good message. the syntax error we'd get1120 // otherwise is inscrutable.1121 yyerror("missing newline at end of file");1122 lx = ';';1123 }1124 1125 switch(lx) {1126 case LNAME:1127 case LLITERAL:1128 case LBREAK:1129 case LCONTINUE:1130 case LFALL:1131 case LRETURN:1132 case LINC:1133 case LDEC:1134 case ')':1135 case '}':1136 case ']':1137 curio.nlsemi = 1;1138 break;1139 default:1140 curio.nlsemi = 0;1141 break;1142 }1143 return lx;1144 }
其實這個函數做的事情比較簡單,就是對於需要預設加;的代碼後面加了分號而已。主要的處理邏輯還是在_yylex裡完成了。
實際上這裡需要關注的是兩個全域變數curio和pushedio,這兩個都是Io結構體類型,定義如下:
566 struct Io 567 { 568 char* infile;//本結構體所代表的內部檔案 569 Biobuf* bin;//代表實際的檔案以及buffer 570 int32 ilineno;//內部行號 571 int nlsemi;//是否為檔案結尾,需要加分號 572 int peekc; //當前未讀出的字元 573 int peekc1; // second peekc for ... 574 char* cp; // used for content when bin==nil,主要用於內部package,比如builtin 575 int importsafe; 576 };
而pushedio是相當於一個臨時儲存當前處理檔案的對象,因為一些內部的參數定義也是以package形式存在的,因此在parse一個package之前,6g會自己先載入一些臨時的package,目前看到就是runtime.builtin,這個時候curio會被壓到pushedio裡暫存,然後再恢複。
再來看一下Biobuf的結構:
56 struct Biobuf 57 { 58 int icount; /* neg num of bytes at eob */ 59 int ocount; /* num of bytes at bob */ 60 int rdline; /* num of bytes after rdline */ 61 int runesize; /* num of bytes of last getrune */ 62 int state; /* r/w/inactive */ 63 int fid; /* open file */ 64 int flag; /* magic if malloc'ed */ 65 vlong offset; /* offset of buffer in file */ 66 int bsize; /* size of buffer */ 67 unsigned char* bbuf; /* pointer to beginning of buffer */ 68 unsigned char* ebuf; /* pointer to end of buffer */ 69 unsigned char* gbuf; /* pointer to good data in buf */ 70 unsigned char b[Bungetsize+Bsize]; 71 };
這個結構體主要是一個緩衝,因此我們知道yylex裡是一個字元一個字元進行處理的,如果每次都從檔案裡實際讀取效率顯然是極低的,因此這裡是先讀出一部分字元,然後緩衝到buffer裡,這樣回退前進都會比較容易。具體的邏輯可以參考_yylex。這裡需要額外說明的資料結構有Sym,這個結構體代表了一些關鍵字的文法,比如if, for等等。事實上從go.y裡的關於%union的定義我們可以看出來:
24 %union { 25 Node* node; 26 NodeList* list; 27 Type* type; 28 Sym* sym; 29 struct Val val; 30 int lint; 31 } 32 33 // |sed 's/.* //' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx /' 34 35 %token <val> LLITERAL 36 %token <lint> LASOP 37 %token <sym> LBREAK LCASE LCHAN LCOLAS LCONST LCONTINUE LDDD 38 %token <sym> LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO 39 %token <sym> LIF LIMPORT LINTERFACE LMAP LNAME 40 %token <sym> LPACKAGE LRANGE LRETURN LSELECT LSTRUCT LSWITCH 41 %token <sym> LTYPE LVAR 42 43 %token LANDAND LANDNOT LBODY LCOMM LDEC LEQ LGE LGT 44 %token LIGNORE LINC LLE LLSH LLT LNE LOROR LRSH
從這裡我們可以看到一些詞法的定義。接下來我們主要關注一下Sym和Val這個結構體
286 struct Sym 287 { 288 ushort lexical; 289 uchar flags; 290 uchar sym; // huffman encoding in object file 291 Sym* link; 292 293 // saved and restored by dcopy 294 Pkg* pkg; 295 char* name; // variable name 296 Node* def; // definition: ONAME OTYPE OPACK or OLITERAL 297 int32 block; // blocknumber to catch redeclaration 298 int32 lastlineno; // last declaration for diagnostic 299 };119 struct Val 120 { 121 short ctype; 122 union 123 { 124 short reg; // OREGISTER 125 short bval; // bool value CTBOOL 126 Mpint* xval; // int CTINT 127 Mpflt* fval; // float CTFLT 128 Mpcplx* cval; // float CTCPLX 129 Strlit* sval; // string CTSTR 130 } u; 131 };