Ruby 没有使用 LEX 来实现词法分析,而是选择本身手写词法分析器,结合 YACC(BISON)实现语法分析,相关的源代码在 parse.y(YACC语法描述)文件中缓存
parse.y 中的 parser_yylex 是词法分析器的入口,函数的末尾调用 parse_indent 解析标识符less
static int parser_yylex(struct parser_params *parser) { ... parse_ident(parser, c, cmd_state); }
先来看看 parse_ident 函数开始的局部变量声明:ide
static int parse_ident(struct parser_params *parser, int c, int cmd_state) { int result = 0; const enum lex_state_e last_state = lex_state; ID ident; }
result 用于保存该函数返回给 YACC 的语法单元标识,它能够是 tIDENTIFIER, tCONSTANT 或者 tLABEL函数
last_state 用于保存 LEX(词法分析器)内部的状态code
ident 用于保存标识符在 Ruby 解释器 的内部表示(索引)对象
函数的一开始使用 do-while 循环来收集组成标识符的字符索引
do { if (!ISASCII(c)) mb = ENC_CODERANGE_UNKNOWN; if (tokadd_mbchar(c) == -1) return 0; c = nextc(); } while (parser_is_identchar()); if ((c == '!' || c == '?') && !peek('=')) { tokadd(c); } else { pushback(c); } tokfix();
tokadd_mbchar,tokadd: 将字符 c 加入到标识符内部缓存中token
parser_is_identchar:判断字符 c 是不是合法的标识符字符串
pushback:回退字符cmd
tokfix:在标识符内部缓存末尾添加 0(C语言中的字符串结束符)
咱们先略过 parse_ident 函数中关于 关键字 和其它内容判断,看看函数末尾:
ident = tokenize_ident(parser, last_state); if (!IS_lex_state_for(last_state, EXPR_DOT|EXPR_FNAME) && (result == tIDENTIFIER) && /* not EXPR_FNAME, not attrasgn */ lvar_defined(ident)) { SET_LEX_STATE(EXPR_END|EXPR_LABEL); } return result;
tokenize_ident 用于将标识符添加到解释器内部的符号表
static ID tokenize_ident(struct parser_params *parser, const enum lex_state_e last_state) { ID ident = TOK_INTERN(); set_yylval_name(ident); return ident; }
TOK_INTERN 是一个宏定义:
# parse.y #ifdef RIPPER #define intern_cstr(n,l,en) rb_intern3(n,l,en) #else #define intern_cstr(n,l,en) rb_intern3(n,l,en) #endif #define TOK_INTERN() intern_cstr(tok(), toklen(), current_enc)
tok 和 toklen 的定义,能够在(从 parse.y 生成)parse.c 中找到
#define tokbuf (parser->tokenbuf) #define toklen (parser->tokidx) #define tok() tokenbuf #define toklen() tokidx
使用宏定义来访问结构体或函数的代码风格在 Ruby 源代码中随处可见~
咱们接着来看 rb_intern3 函数
# symbol.c ID rb_intern3(const char *name, long len, rb_encoding *enc) { VALUE sym; struct RString fake_str; VALUE str = rb_setup_fake_str(&fake_str, name, len, enc); OBJ_FREEZE(str); sym = lookup_str_sym(str); if (sym) return rb_sym2id(sym); str = rb_enc_str_new(name, len, enc); /* make true string */ return intern_str(str, 1); }
rb_setup_fake_str 建立一个 FAKE Ruby String 对象(结构体)RString
lookup_str_sym 使用 建立出来的 RString 在符号表里查找 符号(sym)
若是找到 sym,将 sym 转化为 ID 直接返回
不然调用 rb_enc_str_new 建立一个"真实"的 str 并调用 intern_str 函数插入到符号表中
咱们再回到 tokenize_ident 函数:
static ID tokenize_ident(struct parser_params *parser, const enum lex_state_e last_state) { ID ident = TOK_INTERN(); set_yylval_name(ident); return ident; }
在调用 TOK_INTERN 宏将标识符保存到符号表以后,set_yylval_name(ident) 设置 yylval:
#ifndef RIPPER ... # define set_yylval_name(x) (yylval.id = (x)) ... #else ...
关键字相关的操做主要在 lex.c 源代码文件中,lex.c 文件头部的注释显示该文件是使用 gperf 自动生成的
/* C code produced by gperf version 3.0.4 */ /* Command-line: gperf -C -P -p -j1 -i 1 -g -o -t -N rb_reserved_word -k'1,3,$' defs/keywords */
stringpool_t 结构体封装了关键字缓冲池
因为代码是使用 gperf 自动生成的,因此会有一些 hard code 的数字 str8 .etc
struct stringpool_t { char stringpool_str8[sizeof("break")]; char stringpool_str9[sizeof("else")]; char stringpool_str10[sizeof("nil")]; char stringpool_str11[sizeof("ensure")]; char stringpool_str12[sizeof("end")]; char stringpool_str13[sizeof("then")]; char stringpool_str14[sizeof("not")]; char stringpool_str15[sizeof("false")]; char stringpool_str16[sizeof("self")]; char stringpool_str17[sizeof("elsif")]; char stringpool_str18[sizeof("rescue")]; char stringpool_str19[sizeof("true")]; char stringpool_str20[sizeof("until")]; char stringpool_str21[sizeof("unless")]; char stringpool_str22[sizeof("return")]; char stringpool_str23[sizeof("def")]; char stringpool_str24[sizeof("and")]; char stringpool_str25[sizeof("do")]; char stringpool_str26[sizeof("yield")]; char stringpool_str27[sizeof("for")]; char stringpool_str28[sizeof("undef")]; char stringpool_str29[sizeof("or")]; char stringpool_str30[sizeof("in")]; char stringpool_str31[sizeof("when")]; char stringpool_str32[sizeof("retry")]; char stringpool_str33[sizeof("if")]; char stringpool_str34[sizeof("case")]; char stringpool_str35[sizeof("redo")]; char stringpool_str36[sizeof("next")]; char stringpool_str37[sizeof("super")]; char stringpool_str38[sizeof("module")]; char stringpool_str39[sizeof("begin")]; char stringpool_str40[sizeof("__LINE__")]; char stringpool_str41[sizeof("__FILE__")]; char stringpool_str42[sizeof("__ENCODING__")]; char stringpool_str43[sizeof("END")]; char stringpool_str44[sizeof("alias")]; char stringpool_str45[sizeof("BEGIN")]; char stringpool_str46[sizeof("defined?")]; char stringpool_str47[sizeof("class")]; char stringpool_str50[sizeof("while")]; };
stringpool_contents 变量是缓存池的一个实例:
static const struct stringpool_t stringpool_contents = { "break", "else", "nil", "ensure", "end", "then", "not", "false", "self", "elsif", "rescue", "true", "until", "unless", "return", "def", "and", "do", "yield", "for", "undef", "or", "in", "when", "retry", "if", "case", "redo", "next", "super", "module", "begin", "__LINE__", "__FILE__", "__ENCODING__", "END", "alias", "BEGIN", "defined?", "class", "while" };
rb_reserved_word 函数判断 长度为 len 的字符串 str 是不是关键字
若是字符串的长度不在 关键字 长度区间内,直接返回 0
根据 str, len 计算 str 在 上面提到的 stringpool_contents 里面的索引(key)
若是 key 不在 范围内内,直接返回 0
比较字符串
const struct kwtable *rb_reserved_word(str, len) register const char *str; register unsigned int len; { if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register int key = hash (str, len); if (key <= MAX_HASH_VALUE && key >= 0) { register int o = wordlist[key].name; if (o >= 0) { register const char *s = o + stringpool; if (*str == *s && !strcmp (str + 1, s + 1)) return &wordlist[key]; } } } return 0; }