refactor(compiler): refactor code matching helpers for compliance spec (#22835)
PR Close #22835
This commit is contained in:
parent
49396ca2ae
commit
129bb1800b
|
@ -114,6 +114,7 @@ export function getParseErrors(error: Error): ParseError[] {
|
||||||
return (error as any)[ERROR_PARSE_ERRORS] || [];
|
return (error as any)[ERROR_PARSE_ERRORS] || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Escape characters that have a special meaning in Regular Expressions
|
||||||
export function escapeRegExp(s: string): string {
|
export function escapeRegExp(s: string): string {
|
||||||
return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
|
return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,70 +20,64 @@ import {compilePipe} from '../../src/render3/r3_pipe_compiler';
|
||||||
import {OutputMode} from '../../src/render3/r3_types';
|
import {OutputMode} from '../../src/render3/r3_types';
|
||||||
import {compileComponent, compileDirective} from '../../src/render3/r3_view_compiler';
|
import {compileComponent, compileDirective} from '../../src/render3/r3_view_compiler';
|
||||||
import {BindingParser} from '../../src/template_parser/binding_parser';
|
import {BindingParser} from '../../src/template_parser/binding_parser';
|
||||||
import {OutputContext} from '../../src/util';
|
import {OutputContext, escapeRegExp} from '../../src/util';
|
||||||
import {MockAotCompilerHost, MockCompilerHost, MockData, MockDirectory, arrayToMockDir, expectNoDiagnostics, settings, toMockFileArray} from '../aot/test_util';
|
import {MockAotCompilerHost, MockCompilerHost, MockData, MockDirectory, arrayToMockDir, expectNoDiagnostics, settings, toMockFileArray} from '../aot/test_util';
|
||||||
|
|
||||||
const IDENTIFIER = /[A-Za-z_$ɵ][A-Za-z0-9_$]*/;
|
const IDENTIFIER = /[A-Za-z_$ɵ][A-Za-z0-9_$]*/;
|
||||||
const OPERATOR =
|
const OPERATOR =
|
||||||
/!|%|\*|\/|\^|\&|\&\&\|\||\|\||\(|\)|\{|\}|\[|\]|:|;|\.|<|<=|>|>=|=|==|===|!=|!==|=>|\+|\+\+|-|--|@|,|\.|\.\.\./;
|
/!|%|\*|\/|\^|&{1,2}|\|{1,2}|\(|\)|\{|\}|\[|\]|:|;|<=?|>=?|={1,3}|!={1,2}|=>|\+{1,2}|-{1,2}|@|,|\.|\.\.\./;
|
||||||
const STRING = /\'[^'\n]*\'|"[^'\n]*"|`[^`]*`/;
|
const STRING = /'[^']*'|"[^"]*"|`[\s\S]*?`/;
|
||||||
const NUMBER = /[0-9]+/;
|
const NUMBER = /\d+/;
|
||||||
|
|
||||||
const ELLIPSIS = '…';
|
const ELLIPSIS = '…';
|
||||||
const TOKEN = new RegExp(
|
const TOKEN = new RegExp(
|
||||||
`^((${IDENTIFIER.source})|(${OPERATOR.source})|(${STRING.source})|${NUMBER.source}|${ELLIPSIS})`);
|
`\\s*((${IDENTIFIER.source})|(${OPERATOR.source})|(${STRING.source})|${NUMBER.source}|${ELLIPSIS})`,
|
||||||
const WHITESPACE = /^\s+/;
|
'y');
|
||||||
|
|
||||||
type Piece = string | RegExp;
|
type Piece = string | RegExp;
|
||||||
|
|
||||||
const IDENT = /[A-Za-z$_][A-Za-z0-9$_]*/;
|
|
||||||
const SKIP = /(?:.|\n|\r)*/;
|
const SKIP = /(?:.|\n|\r)*/;
|
||||||
const MATCHING_IDENT = /^\$.*\$$/;
|
const MATCHING_IDENT = /^\$.*\$$/;
|
||||||
|
|
||||||
|
// Transform the expected output to set of tokens
|
||||||
function tokenize(text: string): Piece[] {
|
function tokenize(text: string): Piece[] {
|
||||||
function matches(exp: RegExp): string|false {
|
TOKEN.lastIndex = 0;
|
||||||
const m = text.match(exp);
|
|
||||||
if (!m) return false;
|
|
||||||
text = text.substr(m[0].length);
|
|
||||||
return m[0];
|
|
||||||
}
|
|
||||||
function next(): string {
|
|
||||||
const result = matches(TOKEN);
|
|
||||||
if (!result) {
|
|
||||||
throw Error(`Invalid test, no token found for '${text.substr(0, 30)}...'`);
|
|
||||||
}
|
|
||||||
matches(WHITESPACE);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let match: RegExpMatchArray|null;
|
||||||
const pieces: Piece[] = [];
|
const pieces: Piece[] = [];
|
||||||
matches(WHITESPACE);
|
|
||||||
while (text) {
|
while ((match = TOKEN.exec(text)) !== null) {
|
||||||
const token = next();
|
const token = match[1];
|
||||||
if (token === 'IDENT') {
|
if (token === 'IDENT') {
|
||||||
pieces.push(IDENT);
|
pieces.push(IDENTIFIER);
|
||||||
} else if (token === ELLIPSIS) {
|
} else if (token === ELLIPSIS) {
|
||||||
pieces.push(SKIP);
|
pieces.push(SKIP);
|
||||||
} else {
|
} else {
|
||||||
pieces.push(token);
|
pieces.push(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOKEN.lastIndex !== 0) {
|
||||||
|
const from = TOKEN.lastIndex;
|
||||||
|
const to = from + 30;
|
||||||
|
throw Error(`Invalid test, no token found for '${text.substr(from, to)}...'`)
|
||||||
|
}
|
||||||
|
|
||||||
return pieces;
|
return pieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contextWidth = 100;
|
export function expectEmit(source: string, expected: string, description: string) {
|
||||||
export function expectEmit(source: string, emitted: string, description: string) {
|
const pieces = tokenize(expected);
|
||||||
const pieces = tokenize(emitted);
|
const expr = r(pieces);
|
||||||
const expr = r(...pieces);
|
|
||||||
if (!expr.test(source)) {
|
if (!expr.test(source)) {
|
||||||
let last: number = 0;
|
let last: number = 0;
|
||||||
for (let i = 1; i <= pieces.length; i++) {
|
for (let i = 1; i < pieces.length; i++) {
|
||||||
const t = r(...pieces.slice(0, i));
|
const t = r(pieces.slice(0, i));
|
||||||
const m = source.match(t);
|
const m = source.match(t);
|
||||||
const expected = pieces[i - 1] == IDENT ? '<IDENT>' : pieces[i - 1];
|
const expectedPiece = pieces[i - 1] == IDENTIFIER ? '<IDENT>' : pieces[i - 1];
|
||||||
if (!m) {
|
if (!m) {
|
||||||
const contextPieceWidth = contextWidth / 2;
|
|
||||||
fail(
|
fail(
|
||||||
`${description}: Expected to find ${expected} '${source.substr(0,last)}[<---HERE expected "${expected}"]${source.substr(last)}'`);
|
`${description}: Expected to find ${expectedPiece} '${source.substr(0,last)}[<---HERE expected "${expectedPiece}"]${source.substr(last)}'`);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
last = (m.index || 0) + m[0].length;
|
last = (m.index || 0) + m[0].length;
|
||||||
|
@ -95,8 +89,7 @@ export function expectEmit(source: string, emitted: string, description: string)
|
||||||
}
|
}
|
||||||
|
|
||||||
const IDENT_LIKE = /^[a-z][A-Z]/;
|
const IDENT_LIKE = /^[a-z][A-Z]/;
|
||||||
const SPECIAL_RE_CHAR = /\/|\(|\)|\||\*|\+|\[|\]|\{|\}|\$/g;
|
function r(pieces: (string | RegExp)[]): RegExp {
|
||||||
function r(...pieces: (string | RegExp)[]): RegExp {
|
|
||||||
const results: string[] = [];
|
const results: string[] = [];
|
||||||
let first = true;
|
let first = true;
|
||||||
let group = 0;
|
let group = 0;
|
||||||
|
@ -110,14 +103,14 @@ function r(...pieces: (string | RegExp)[]): RegExp {
|
||||||
if (MATCHING_IDENT.test(piece)) {
|
if (MATCHING_IDENT.test(piece)) {
|
||||||
const matchGroup = groups.get(piece);
|
const matchGroup = groups.get(piece);
|
||||||
if (!matchGroup) {
|
if (!matchGroup) {
|
||||||
results.push('(' + IDENT.source + ')');
|
results.push('(' + IDENTIFIER.source + ')');
|
||||||
const newGroup = ++group;
|
const newGroup = ++group;
|
||||||
groups.set(piece, newGroup);
|
groups.set(piece, newGroup);
|
||||||
} else {
|
} else {
|
||||||
results.push(`\\${matchGroup}`);
|
results.push(`\\${matchGroup}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
results.push(piece.replace(SPECIAL_RE_CHAR, s => '\\' + s));
|
results.push(escapeRegExp(piece));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
results.push('(?:' + piece.source + ')');
|
results.push('(?:' + piece.source + ')');
|
||||||
|
|
Loading…
Reference in New Issue