SyntaxList
がなんだかよく分からず終わった。
TypeScript/types.ts at 3cd9f3d2d4afc1c817ea53b3e40d9598197e9aaa · Microsoft/TypeScript · GitHub
Scanner
TypeScript/scanner.ts at 3cd9f3d2d4afc1c817ea53b3e40d9598197e9aaa · Microsoft/TypeScript · GitHub
export interface Scanner { getStartPos(): number; getToken(): SyntaxKind; getTextPos(): number; getTokenPos(): number; getTokenText(): string; getTokenValue(): string; hasExtendedUnicodeEscape(): boolean; hasPrecedingLineBreak(): boolean; isIdentifier(): boolean; isReservedWord(): boolean; isUnterminated(): boolean; /* @internal */ getNumericLiteralFlags(): NumericLiteralFlags; reScanGreaterToken(): SyntaxKind; reScanSlashToken(): SyntaxKind; reScanTemplateToken(): SyntaxKind; scanJsxIdentifier(): SyntaxKind; scanJsxAttributeValue(): SyntaxKind; reScanJsxToken(): SyntaxKind; scanJsxToken(): SyntaxKind; scanJSDocToken(): SyntaxKind; scan(): SyntaxKind; getText(): string; // Sets the text for the scanner to scan. An optional subrange starting point and length // can be provided to have the scanner only scan a portion of the text. setText(text: string, start?: number, length?: number): void; setOnError(onError: ErrorCallback): void; setScriptTarget(scriptTarget: ScriptTarget): void; setLanguageVariant(variant: LanguageVariant): void; setTextPos(textPos: number): void; // Invokes the provided callback then unconditionally restores the scanner to the state it // was in immediately prior to invoking the callback. The result of invoking the callback // is returned from this function. lookAhead<T>(callback: () => T): T; // Invokes the callback with the scanner set to scan the specified range. When the callback // returns, the scanner is restored to the state it was in before scanRange was called. scanRange<T>(start: number, length: number, callback: () => T): T; // Invokes the provided callback. If the callback returns something falsy, then it restores // the scanner to the state it was in immediately prior to invoking the callback. If the // callback returns something truthy, then the scanner state is not rolled back. The result // of invoking the callback is returned from this function. tryScan<T>(callback: () => T): T; }
Interfaceとしてはこんな感じ。与えられた文字列に対して scan()
で1文字ずつ舐めていって token( SyntaxKind
) を取得する。
TypeScript/types.ts at 3cd9f3d2d4afc1c817ea53b3e40d9598197e9aaa · Microsoft/TypeScript · GitHub
tokenが識別子(keyword)かどうかは token > SyntaxKind.Identifer
で判定できるようになっている。最近入ったJSDocの解釈に対応するつため JSDoc*Type
みたいな SyntaxKind
もある。以前だったら単なるコメントなので *CommentTrivia
扱いされていたはず。はず。
scan()
自体は普通に pos
を進めていきながら token
を判定していく感じ。
TypeScript/scanner.ts at 3cd9f3d2d4afc1c817ea53b3e40d9598197e9aaa · Microsoft/TypeScript · GitHub
TypeScriptはあんまり独自のキーワードいれない(実JSに影響及ぼすものに関しては)方針だと思われるけどその中でも enum
だけは存在しているのは多分
switch (ch) { case CharacterCodes.lineFeed:
みたいなのを書きやすくするためなんじゃないかなぁと思ってる。セルフホスティングだからね。
jsxだったりテンプレートリテラルだったりする場合は特別対応してるんだけど、正直なにやってるかよくわからない。
import * as ts from 'typescript'; let text = `let a = 'm';`; let scanner = ts.createScanner(ts.ScriptTarget.Latest, false,ts.LanguageVariant.Standard, text); console.log(scanner.scan()); console.log(scanner.getTokenValue());
みたいなの書いていけば理解できそうだけどparser読んだほうが良さそうなので今は置いとく。 reScan
系とか使ってる側見たいとイメージ沸かない。
ちなみに ConflictMarkerTrivia
みたいのもあって親切。
突然だけどTypescriptのコードを読んでいくことにする。特に目的はない、のでどこから読んでいいかわからないけど、scannerからいく。そして、scanner自体もそこそこあるし、疲れるので、大体関数1個ずつくらいのペースで行くのではないか。飽きたらやめる。
lookupInUnicodeMap
TypeScript/scanner.ts at 3cd9f3d2d4afc1c817ea53b3e40d9598197e9aaa · Microsoft/TypeScript · GitHub
ES3以前とES5移行で識別子に使えるコードポイントが違うので使える文字であるのかチェックするのがlookupInUnicodeMap
である。
識別子に使える文字も最初に使えるコードポイントとそれ移行に使えるコードポイントは違うため
unicodeES5IdentifierStart
unicodeES5IdentifierPart
のように2つの定数が定義されている。(もちろんES3版もある)
この配列の見方は、たとえば [2, 2, 4, 6]
のようになっていれば2〜2と4〜6、つまり2、4、5、6が含まれるということである。
Goで正規表現のバイトコードを見たときもこういう感じの表現の仕方をしていたのでこれがセオリーっぽい。
function lookupInUnicodeMap(code: number, map: number[]): boolean { ... }
第二引数に unicodeES5IdentifierStart
や unicodeES5IdentifierPart
を与え、第一引数に与えたコードポイントが含まれているかをチェックする。
含まれているかのチェック自体は素朴な二分探索法という感じ。2要素で1ペアとしその中に code
が含まれるか調べていく。
isUnicodeIdentifierStart
と isUnicodeIdentifierPart
は lookupInUnicodeMap
のwrapperである。さらにそれらのwrapperである isIdentifierStart
や isIdentifierPart
が存在する。 isIdentifier*
ではascii文字であるならば直ちに ture
を返すことになっている(ES5かES3か区別する必要ないからね)。
isIdentifier*
は今後いたるところで使われる。
scannerはscannerだし読まなくて良い気がしてきた。
今日はここまで。