You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// 字符链// 比如`name`这样一个Token,有4个字符,这4个字符就连成了一个LiteralScope// 当扫描完这4个字符后,会调用`LiteralScope::Complete`,这样就完成了一个Token的分析
LiteralScope literal(this);
// 扫描unicode转义字符if (c0_ == '\\') {
uc32 c = ScanIdentifierUnicodeEscape();
// Only allow legal identifier start characters.if (c < 0 ||
c == '\\' || // No recursive escapes.
!unicode_cache_->IsIdentifierStart(c)) {
return Token::ILLEGAL;
}
// \u0020AddLiteralChar(c);
returnScanIdentifierSuffix(&literal);
}
uc32 first_char = c0_;
Advance();
AddLiteralChar(first_char);
// 向前扫描字符,直至遇到一个不是有效的identifier字符为止,比如空格、(、[等while (unicode_cache_->IsIdentifierPart(c0_)) {
if (c0_ != '\\') {
uc32 next_char = c0_;
Advance();
AddLiteralChar(next_char);
continue;
}
// Fallthrough if no longer able to complete keyword.returnScanIdentifierSuffix(&literal);
}
// 结束一个字符链,即已经扫描出了一个identifier或者keyword
literal.Complete();
if (next_.literal_chars->is_one_byte()) {
Vector<constuint8_t> chars = next_.literal_chars->one_byte_literal();
// 判断是标志符还是关键词// v8内部定义了一系列关键词matcher,能match到的就是关键词,否则就是标志符returnKeywordOrIdentifierToken(chars.start(),
chars.length(),
harmony_scoping_,
harmony_modules_);
}
return Token::IDENTIFIER;
我们以前文的buffer_字符数组为例,经过Scanner扫描后,会产生类似这样的结果:
最后,对整个文件内容扫描完的结果大概就类似这样:
No of tokens: 12
=> FUNCTION at (0, 8)
=> IDENTIFIER at (9, 11)
=> LPAREN at (11, 12)
=> RPAREN at (12, 13)
=> LBRACE at (14, 15)
=> VAR at (20, 23)
=> IDENTIFIER at (24, 26)
=> ASSIGN at (27, 28)
=> STRING at (29, 41)
=> SEMICOLON at (41, 42)
=> RBRACE at (43, 44)
=> EOS at (45, 45)
本文将探讨v8是如何对utf8编码的js文件进行词法分析的!
简介
v8提供了一个
Scanner
类作为词法分析器,在该类中有这么一个指针:源代码中对这个类的解释:
大概就是说,
Utf16CharacterStream
就是一个UTF-16代码单元的缓冲流。什么是代码单元?
这就涉及到了unicode规范,在unicode字符集中,每个unicode字符都有一个唯一的代码点。
在utf8、utf16等编码形式中,代码点被映射到一个或者多个代码单元。
代码单元是各个编码形式中的最小单元,其大小:
每个代码点需要多少个代码单元呢?不同编码形式不一样:
综上,我们可以知道
Utf16CharacterStream
中每一个UTF-16代码单元的含义:简而言之,
Utf16CharacterStream
中的每个代码单元,要么是一个独立的unicode字符,要么是一个unicode字符的一半(和下一个代码单元组合起来才能形成一个unicode字符)。v8实际上就是基于utf16对输入字符序列进行词法分析的!这也是Ecmascript规范所要求的:
v8词法分析首先要做的是把输入字符序列转换成utf16 stream。
那如何把utf8编码的js文件转换成utf16 stream呢?
字节流
首先,我们需要先获得utf8字节流:
假设文件内容如下:
utf8字节流如下:
转换
现在,我们需要把utf8的字节流转换成utf16字符流,前面我们说了,词法分析器扫描的是
Utf16CharacterStream
类,不过这个类是一个抽象类,不能直接使用,v8用了一个
BufferedUtf16CharacterStream
类来提供具体的功能,它继承至Utf16CharacterStream
:这个
BufferedUtf16CharacterStream
提供了一个缓冲区buffer_
,我们转换的时候,就是要向这个缓冲区填充字符。具体的转换功能由一个叫
Utf8ToUtf16CharacterStream
的类提供,它继承至BufferedUtf16CharacterStream
:基本用法:
在生成一个
BufferedUtf16CharacterStream
实例之时,在其构造函数中,会进行第一次字符填充。在
Utf8ToUtf16CharacterStream::FillBuffer
方法中,有这么一段代码:最后的结果就是,
BufferedUtf16CharacterStream::buffer_
中的每一个元素都是一个16位代码单元,它要么是一个字符,要么是一个“Unicode代码对”的一半。我们以上文得到的字节流为例,最后
BufferedUtf16CharacterStream::buffer_
中会类似这样:得到所有字符之后,接下来的工作就是扫描每个字符,分析出
Token
。扫描
v8扫描
BufferedUtf16CharacterStream::buffer_
中的每个代码单元,然后按照Ecmascript的文法产生式分析出所有Token。我们先来看看
Scanner::Next
这个方法:我们注意到上面代码中的
one_char_tokens
,这货是啥?它其实就是个大小为128的数组,其中元素大部分是Token::ILLEGAL,只有几个有效的Token:如果没匹配到
one_char_tokens
,则调用Scanner::Scan
继续扫描:注意到上面的
ScanIdentifierOrKeyword
方法,这个方法是Token分析的核心,顾名思义,它就是用来分析identifier
或者keyword
的:我们以前文的
buffer_
字符数组为例,经过Scanner
扫描后,会产生类似这样的结果:最后,对整个文件内容扫描完的结果大概就类似这样:
在utf8编码的情况下,v8整个词法分析过程,大概就是这样,当然实际过程中,还有很多细节问题需要处理,比如跳过js注释、html注释、扫描十六进制字符、8进制字符、unicode转义字符等等。
这就是本人粗略看了v8词法分析源码之后的一点理解,欢迎各路大神点评!
The text was updated successfully, but these errors were encountered: