Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions dist/datamodel/parser/MdlParser.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,17 @@ import type { ParsedSlx } from './SlxParser.js';
*/
export type ParsedMdl = ParsedSlx;
export declare function parseMdl(buffer: ArrayBuffer, filename: string): ParsedMdl;
/**
* A mask style with its options cut off, `popup(a|b)` to `popup`, exactly as
* `style.replace(/\(.*$/, '')` cut them, on every input.
*
* With no `m` flag `$` is the end of the text, and `.` passes no line terminator (`\n`,
* `\r`, U+2028, U+2029), so the regex cut at the first `(` that no line terminator
* follows. It found that `(` by trying each in turn, and each try walked to the next
* line terminator before it failed, so a style of many `(` ahead of one cost their count
* times that walk. The scanner keeps a line break inside a quoted value and unescapes
* `\n` and `\r`, so a file puts one wherever it likes. The `(` the regex cut at is the
* first after the last line terminator, which five scans of the text find.
*/
export declare function styleWithoutOptions(style: string): string;
//# sourceMappingURL=MdlParser.d.ts.map
2 changes: 1 addition & 1 deletion dist/datamodel/parser/MdlParser.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 28 additions & 2 deletions dist/datamodel/parser/MdlParser.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/datamodel/parser/MdlParser.js.map

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions dist/datamodel/parser/xmlEntrySplice.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@ export interface XmlSpan {
offset: number;
length: number;
}
/**
* The text of the first P-node that `open` begins, exactly as
* `fragment.match(/<P Name="Name"[^>]*>([^<]*)<\/P>/)?.[1] ?? null` read it for
* `open` = `<P Name="Name"`, and the same pattern with `UUID` for `<P Name="UUID"`,
* on every input.
*
* The regex retried at every `open` in the fragment, and each retry walked to the
* next `>` and the `<` after it, so a fragment holding many `open`s that no `>`
* follows soon cost their count times that walk: the square of a run that the
* file's bytes decide.
*
* At one start the regex can match only one way. `[^>]*` cannot pass a `>`, so its
* `>` is the first one after `open`; `[^<]*` cannot pass a `<`, so the text ends at
* the first `<` after that, where `</P>` either is or is not. `open` starts with its
* only `<` and holds no `>`, so a later start either lies before that `>` (and meets
* the same `>`, the same `<` and the same answer) or lies at or after that `<`. So
* the next start worth trying is the next `open` from there, and no character is
* read more than a bounded number of times.
*/
export declare function pNodeText(fragment: string, open: string): string | null;
/**
* Byte span of the <Object Class="DD.ENTRY">…</Object> the selector identifies
* (a bare string means "whichever entry has this name").
Expand Down
2 changes: 1 addition & 1 deletion dist/datamodel/parser/xmlEntrySplice.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 36 additions & 4 deletions dist/datamodel/parser/xmlEntrySplice.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/datamodel/parser/xmlEntrySplice.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 35 additions & 2 deletions src/datamodel/parser/MdlParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,10 @@ function classicMask(block: MdlNode): { names: string[]; params: { name: string;
const values = (prop(block, 'MaskValueString') || '').split('|');
const names: string[] = [];
const params: { name: string; value: string }[] = [];
// Whether a slot's style is an expression type, worked out once for each slot. Reading a
// style is linear in its length, and every declaration may name the same slot, so
// reading it once per declaration cost the product of the two.
const expression = new Map<number, boolean>();
for (const decl of vars.split(';')) {
const parsed = /^\s*([A-Za-z_]\w*)\s*=\s*([@&])(\d+)\s*$/.exec(decl);
if (!parsed) {
Expand All @@ -875,15 +879,44 @@ function classicMask(block: MdlNode): { names: string[]; params: { name: string;
if (evaluated !== '@' || slot < 0 || slot >= values.length) {
continue;
}
const type = (styles[slot] ?? 'edit').replace(/\(.*$/, '').trim().toLowerCase();
if (!isExpressionMaskType(type)) {
let isExpression = expression.get(slot);
if (isExpression === undefined) {
const type = styleWithoutOptions(styles[slot] ?? 'edit').trim().toLowerCase();
isExpression = isExpressionMaskType(type);
expression.set(slot, isExpression);
}
if (!isExpression) {
continue;
}
params.push({ name, value: values[slot] });
}
return { names, params };
}

/**
* A mask style with its options cut off, `popup(a|b)` to `popup`, exactly as
* `style.replace(/\(.*$/, '')` cut them, on every input.
*
* With no `m` flag `$` is the end of the text, and `.` passes no line terminator (`\n`,
* `\r`, U+2028, U+2029), so the regex cut at the first `(` that no line terminator
* follows. It found that `(` by trying each in turn, and each try walked to the next
* line terminator before it failed, so a style of many `(` ahead of one cost their count
* times that walk. The scanner keeps a line break inside a quoted value and unescapes
* `\n` and `\r`, so a file puts one wherever it likes. The `(` the regex cut at is the
* first after the last line terminator, which five scans of the text find.
*/
export function styleWithoutOptions(style: string): string {
const from =
Math.max(
style.lastIndexOf('\n'),
style.lastIndexOf('\r'),
style.lastIndexOf('\u2028'),
style.lastIndexOf('\u2029'),
) + 1;
const open = style.indexOf('(', from);
return open < 0 ? style : style.slice(0, open);
}

/**
* MaskStyleString split into one style per parameter — at the commas OUTSIDE the
* parentheses, because a popup carries its options inside them (`popup(a|b)`) and an
Expand Down
Loading
Loading