Skip to content

Commit

Permalink
perf: optimize expression state to use charcodes
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanPiercey committed Mar 16, 2022
1 parent b81384d commit 8e7c643
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 44 deletions.
53 changes: 38 additions & 15 deletions src/states/ATTRIBUTE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,36 @@ export interface AttrMeta extends Range {
bound: boolean;
}

const HTML_VALUE_TERMINATORS = [
CODE.CLOSE_ANGLE_BRACKET,
CODE.COMMA,
[CODE.FORWARD_SLASH, CODE.CLOSE_ANGLE_BRACKET],
];

const CONCISE_VALUE_TERMINATORS = [
CODE.CLOSE_SQUARE_BRACKET,
CODE.SEMICOLON,
CODE.COMMA,
];

const HTML_NAME_TERMINATORS = [
CODE.CLOSE_ANGLE_BRACKET,
CODE.COMMA,
CODE.OPEN_PAREN,
CODE.EQUAL,
[CODE.COLON, CODE.EQUAL],
[CODE.FORWARD_SLASH, CODE.CLOSE_ANGLE_BRACKET],
];

const CONCISE_NAME_TERMINATORS = [
CODE.CLOSE_SQUARE_BRACKET,
CODE.SEMICOLON,
CODE.EQUAL,
CODE.COMMA,
CODE.OPEN_PAREN,
[CODE.COLON, CODE.EQUAL],
];

// We enter STATE.ATTRIBUTE when we see a non-whitespace
// character after reading the tag name
export const ATTRIBUTE: StateDefinition<AttrMeta> = {
Expand Down Expand Up @@ -73,40 +103,33 @@ export const ATTRIBUTE: StateDefinition<AttrMeta> = {
attr.state = ATTR_STATE.VALUE;
const expr = this.enterState(STATE.EXPRESSION);
expr.terminatedByWhitespace = true;
expr.terminator = [
this.isConcise ? "]" : "/>",
this.isConcise ? ";" : ">",
",",
];
expr.terminator = this.isConcise
? CONCISE_VALUE_TERMINATORS
: HTML_VALUE_TERMINATORS;

this.rewind(1);
} else if (code === CODE.OPEN_PAREN) {
ensureAttrName(this, attr);
attr.state = ATTR_STATE.ARGUMENT;
this.skip(1); // skip (
this.enterState(STATE.EXPRESSION).terminator = ")";
this.enterState(STATE.EXPRESSION).terminator = CODE.CLOSE_PAREN;
this.rewind(1);
} else if (code === CODE.OPEN_CURLY_BRACE && attr.args) {
ensureAttrName(this, attr);
attr.state = ATTR_STATE.BLOCK;
this.skip(1); // skip {
const expr = this.enterState(STATE.EXPRESSION);
expr.terminatedByWhitespace = false;
expr.terminator = "}";
expr.terminator = CODE.CLOSE_CURLY_BRACE;
this.rewind(1);
} else if (attr.state === undefined) {
attr.state = ATTR_STATE.NAME;
const expr = this.enterState(STATE.EXPRESSION);
expr.terminatedByWhitespace = true;
expr.skipOperators = true;
expr.terminator = [
this.isConcise ? "]" : "/>",
this.isConcise ? ";" : ">",
":=",
"=",
",",
"(",
];
expr.terminator = this.isConcise
? CONCISE_NAME_TERMINATORS
: HTML_NAME_TERMINATORS;
this.rewind(1);
} else {
this.exitState();
Expand Down
38 changes: 21 additions & 17 deletions src/states/EXPRESSION.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {

export interface ExpressionMeta extends Range {
groupStack: number[];
terminator?: string | string[];
terminator?: number | (number | number[])[];
skipOperators: boolean;
terminatedByEOL: boolean;
terminatedByWhitespace: boolean;
Expand Down Expand Up @@ -66,9 +66,13 @@ export const EXPRESSION: StateDefinition<ExpressionMeta> = {
return;
}

const { terminator } = expression;

if (
expression.terminator &&
checkForTerminator(this, expression.terminator)
terminator &&
(typeof terminator === "number"
? terminator === code
: checkForTerminators(this, code, terminator))
) {
this.exitState();
return;
Expand Down Expand Up @@ -279,21 +283,21 @@ function buildOperatorPattern(isConcise: boolean) {
return new RegExp(`${lookAheadPattern}|${lookBehindPattern}`, "y");
}

function checkForTerminator(parser: Parser, terminator: string | string[]) {
if (typeof terminator === "string") {
if (parser.data[parser.pos] === terminator) {
return true;
} else if (terminator.length > 1) {
for (let i = 0; i < terminator.length; i++) {
if (parser.data[parser.pos + i] !== terminator[i]) {
return false;
function checkForTerminators(
parser: Parser,
code: number,
terminators: (number | number[])[]
) {
outer: for (const terminator of terminators) {
if (typeof terminator === "number") {
if (code === terminator) return true;
} else {
if (terminator[0] === code) {
for (let i = terminator.length; i-- > 1; ) {
if (parser.data.charCodeAt(parser.pos + i) !== terminator[i])
continue outer;
}
}
return true;
}
} else {
for (let i = 0; i < terminator.length; i++) {
if (checkForTerminator(parser, terminator[i])) {

return true;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/states/INLINE_SCRIPT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const INLINE_SCRIPT: StateDefinition<ScriptletMeta> = {
inlineScript.block = true;
this.skip(1); // skip {
const expr = this.enterState(STATE.EXPRESSION);
expr.terminator = "}";
expr.terminator = CODE.CLOSE_CURLY_BRACE;
expr.skipOperators = true;
this.rewind(1);
} else {
Expand Down
24 changes: 20 additions & 4 deletions src/states/OPEN_TAG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ export interface OpenTagMeta extends Range {
parentTag: OpenTagMeta | undefined;
}
const PARSED_TEXT_TAGS = ["script", "style", "textarea", "html-comment"];
const CONCISE_TAG_VAR_TERMINATORS = [
CODE.SEMICOLON,
CODE.OPEN_PAREN,
CODE.PIPE,
CODE.EQUAL,
[CODE.COLON, CODE.EQUAL],
];

const HTML_TAG_VAR_TERMINATORS = [
CODE.CLOSE_ANGLE_BRACKET,
CODE.OPEN_PAREN,
CODE.PIPE,
CODE.EQUAL,
[CODE.COLON, CODE.EQUAL],
[CODE.FORWARD_SLASH, CODE.CLOSE_ANGLE_BRACKET],
];

export const OPEN_TAG: StateDefinition<OpenTagMeta> = {
name: "OPEN_TAG",
Expand Down Expand Up @@ -275,8 +291,8 @@ export const OPEN_TAG: StateDefinition<OpenTagMeta> = {
expr.skipOperators = true;
expr.terminatedByWhitespace = true;
expr.terminator = this.isConcise
? [";", "(", "|", "=", ":="]
: [">", "/>", "(", "|", "=", ":="];
? CONCISE_TAG_VAR_TERMINATORS
: HTML_TAG_VAR_TERMINATORS;
this.rewind(1);
} else if (code === CODE.OPEN_PAREN && !tag.hasAttrs) {
if (tag.hasArgs) {
Expand All @@ -291,14 +307,14 @@ export const OPEN_TAG: StateDefinition<OpenTagMeta> = {
this.skip(1); // skip (
const expr = this.enterState(STATE.EXPRESSION);
expr.skipOperators = true;
expr.terminator = ")";
expr.terminator = CODE.CLOSE_PAREN;
this.rewind(1);
} else if (code === CODE.PIPE && !tag.hasAttrs) {
tag.state = TAG_STATE.PARAMS;
this.skip(1); // skip |
const expr = this.enterState(STATE.EXPRESSION);
expr.skipOperators = true;
expr.terminator = "|";
expr.terminator = CODE.PIPE;
this.rewind(1);
} else {
if (tag.tagName) {
Expand Down
2 changes: 1 addition & 1 deletion src/states/PLACEHOLDER.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function checkForPlaceholder(parser: Parser, code: number) {
parser.endText();
parser.enterState(PLACEHOLDER).escape = escape;
parser.skip(escape ? 2 : 3); // skip ${ or $!{
parser.enterState(STATE.EXPRESSION).terminator = "}";
parser.enterState(STATE.EXPRESSION).terminator = CODE.CLOSE_CURLY_BRACE;
parser.rewind(1);
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/states/TAG_NAME.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export const TAG_NAME: StateDefinition<TagNameMeta> = {
this.lookAtCharCodeAhead(1) === CODE.OPEN_CURLY_BRACE
) {
this.skip(2); // skip ${
this.enterState(STATE.EXPRESSION).terminator = "}";
this.enterState(STATE.EXPRESSION).terminator = CODE.CLOSE_CURLY_BRACE;
this.rewind(1);
} else if (
isWhitespaceCode(code) ||
Expand Down
2 changes: 1 addition & 1 deletion src/states/TEMPLATE_STRING.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const TEMPLATE_STRING: StateDefinition = {
this.skip(1); // skip {
const expr = this.enterState(STATE.EXPRESSION);
expr.skipOperators = true;
expr.terminator = "}";
expr.terminator = CODE.CLOSE_CURLY_BRACE;
} else {
if (code === CODE.BACK_SLASH) {
this.skip(1); // skip \
Expand Down
8 changes: 4 additions & 4 deletions src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ export function getLoc(lines: number[], range: Range): Location {
export function getPos(
lines: number[],
startLine: number,
pos: number
index: number
): Position {
let max = lines.length - 1;
let line = startLine;

while (line < max) {
const mid = (1 + line + max) >>> 1;

if (lines[mid] <= pos) {
if (lines[mid] <= index) {
line = mid;
} else {
max = mid - 1;
Expand All @@ -38,12 +38,12 @@ export function getPos(

return {
line: line + 1,
column: line === 0 ? pos + 1 : pos - lines[line],
column: index - lines[line],
};
}

export function getLines(src: string) {
const lines = [0];
const lines = [-1];
for (let i = 0; i < src.length; i++) {
if (src.charCodeAt(i) === CODE.NEWLINE) {
lines.push(i);
Expand Down

0 comments on commit 8e7c643

Please sign in to comment.