Type-check utils (#491)

* Type-check utils

* Improve test coverage
This commit is contained in:
Andy 2017-04-27 07:23:13 -07:00 committed by Henry Zhu
parent 47cade874c
commit e1a06544bc
4 changed files with 30 additions and 8 deletions

View File

@ -809,6 +809,7 @@ export default class Tokenizer extends LocationParser {
++this.state.pos; ++this.state.pos;
const esc = this.readCodePoint(true); const esc = this.readCodePoint(true);
// $FlowFixMe (thinks esc may be null, but throwOnInvalid is true)
if (!(first ? isIdentifierStart : isIdentifierChar)(esc, true)) { if (!(first ? isIdentifierStart : isIdentifierChar)(esc, true)) {
this.raise(escStart, "Invalid Unicode escape"); this.raise(escStart, "Invalid Unicode escape");
} }

View File

@ -1,5 +1,7 @@
/* eslint max-len: 0 */ /* eslint max-len: 0 */
// @flow
// This is a trick taken from Esprima. It turns out that, on // This is a trick taken from Esprima. It turns out that, on
// non-Chrome browsers, to check whether a string is in a set, a // non-Chrome browsers, to check whether a string is in a set, a
// predicate containing a big ugly `switch` statement is faster than // predicate containing a big ugly `switch` statement is faster than
@ -9,17 +11,17 @@
// //
// It starts by sorting the words by length. // It starts by sorting the words by length.
function makePredicate(words) { function makePredicate(words: string): (str: string) => boolean {
words = words.split(" "); const wordsArr = words.split(" ");
return function (str) { return function (str) {
return words.indexOf(str) >= 0; return wordsArr.indexOf(str) >= 0;
}; };
} }
// Reserved word lists for various dialects of the language // Reserved word lists for various dialects of the language
export const reservedWords = { export const reservedWords = {
6: makePredicate("enum await"), "6": makePredicate("enum await"),
strict: makePredicate("implements interface let package private protected public static yield"), strict: makePredicate("implements interface let package private protected public static yield"),
strictBind: makePredicate("eval arguments") strictBind: makePredicate("eval arguments")
}; };
@ -57,7 +59,7 @@ const astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,1
// This has a complexity linear to the value of the code. The // This has a complexity linear to the value of the code. The
// assumption is that looking up astral identifier characters is // assumption is that looking up astral identifier characters is
// rare. // rare.
function isInAstralSet(code, set) { function isInAstralSet(code: number, set: $ReadOnlyArray<number>): boolean {
let pos = 0x10000; let pos = 0x10000;
for (let i = 0; i < set.length; i += 2) { for (let i = 0; i < set.length; i += 2) {
pos += set[i]; pos += set[i];
@ -66,11 +68,12 @@ function isInAstralSet(code, set) {
pos += set[i + 1]; pos += set[i + 1];
if (pos >= code) return true; if (pos >= code) return true;
} }
return false;
} }
// Test whether a given character code starts an identifier. // Test whether a given character code starts an identifier.
export function isIdentifierStart(code) { export function isIdentifierStart(code: number): boolean {
if (code < 65) return code === 36; if (code < 65) return code === 36;
if (code < 91) return true; if (code < 91) return true;
if (code < 97) return code === 95; if (code < 97) return code === 95;
@ -81,7 +84,7 @@ export function isIdentifierStart(code) {
// Test whether a given character is part of an identifier. // Test whether a given character is part of an identifier.
export function isIdentifierChar(code) { export function isIdentifierChar(code: number): boolean {
if (code < 48) return code === 36; if (code < 48) return code === 36;
if (code < 58) return true; if (code < 58) return true;
if (code < 65) return false; if (code < 65) return false;

View File

@ -1,9 +1,18 @@
// @flow
import { lineBreakG } from "./whitespace"; import { lineBreakG } from "./whitespace";
export type Pos = {
start: number;
}
// These are used when `options.locations` is on, for the // These are used when `options.locations` is on, for the
// `startLoc` and `endLoc` properties. // `startLoc` and `endLoc` properties.
export class Position { export class Position {
line: number;
column: number;
constructor(line: number, col: number) { constructor(line: number, col: number) {
this.line = line; this.line = line;
this.column = col; this.column = col;
@ -11,8 +20,13 @@ export class Position {
} }
export class SourceLocation { export class SourceLocation {
start: Position;
end: Position;
filename: string;
constructor(start: Position, end?: Position) { constructor(start: Position, end?: Position) {
this.start = start; this.start = start;
// $FlowIgnore (may start as null, but initialized later)
this.end = end; this.end = end;
} }
} }
@ -23,7 +37,7 @@ export class SourceLocation {
// offset. `input` should be the code string that the offset refers // offset. `input` should be the code string that the offset refers
// into. // into.
export function getLineInfo(input, offset) { export function getLineInfo(input: string, offset: number): Position {
for (let line = 1, cur = 0; ;) { for (let line = 1, cur = 0; ;) {
lineBreakG.lastIndex = cur; lineBreakG.lastIndex = cur;
const match = lineBreakG.exec(input); const match = lineBreakG.exec(input);
@ -34,4 +48,6 @@ export function getLineInfo(input, offset) {
return new Position(line, offset - cur); return new Position(line, offset - cur);
} }
} }
// istanbul ignore next
throw new Error("Unreachable");
} }

View File

@ -1,3 +1,5 @@
// @flow
// Matches a whole line break (where CRLF is considered a single // Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines. // line break). Used to count lines.