阅读go语言源代码2

上回说到6g程序应该如何看,这回书接上文,从yylex这个函数看起。vim -t yylex仍然选gc那个。

1107 int32 1108 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 to 1116 // 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 a 1119 // good message. the syntax error we'd get 1120 // 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 };

 

你可能感兴趣的:(struct,buffer,语言,token,Go,newline)