@ifc-lite/parser 4.3.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attribute-helpers.d.ts +38 -4
- package/dist/attribute-helpers.d.ts.map +1 -1
- package/dist/attribute-helpers.js +71 -4
- package/dist/attribute-helpers.js.map +1 -1
- package/dist/columnar-parser-attributes.d.ts.map +1 -1
- package/dist/columnar-parser-attributes.js +35 -5
- package/dist/columnar-parser-attributes.js.map +1 -1
- package/dist/columnar-parser-indexes.d.ts.map +1 -1
- package/dist/columnar-parser-indexes.js +17 -2
- package/dist/columnar-parser-indexes.js.map +1 -1
- package/dist/columnar-parser-relationships.d.ts.map +1 -1
- package/dist/columnar-parser-relationships.js +6 -1
- package/dist/columnar-parser-relationships.js.map +1 -1
- package/dist/columnar-parser.d.ts +3 -1
- package/dist/columnar-parser.d.ts.map +1 -1
- package/dist/columnar-parser.js +27 -76
- package/dist/columnar-parser.js.map +1 -1
- package/dist/compact-entity-index-transport.d.ts +23 -0
- package/dist/compact-entity-index-transport.d.ts.map +1 -0
- package/dist/compact-entity-index-transport.js +28 -0
- package/dist/compact-entity-index-transport.js.map +1 -0
- package/dist/compact-entity-index.d.ts +0 -10
- package/dist/compact-entity-index.d.ts.map +1 -1
- package/dist/compact-entity-index.js +31 -24
- package/dist/compact-entity-index.js.map +1 -1
- package/dist/data-store-transport.d.ts +2 -12
- package/dist/data-store-transport.d.ts.map +1 -1
- package/dist/data-store-transport.js +7 -20
- package/dist/data-store-transport.js.map +1 -1
- package/dist/entity-extractor.d.ts.map +1 -1
- package/dist/entity-extractor.js +101 -6
- package/dist/entity-extractor.js.map +1 -1
- package/dist/entity-refs-from-index.d.ts.map +1 -1
- package/dist/entity-refs-from-index.js +12 -2
- package/dist/entity-refs-from-index.js.map +1 -1
- package/dist/entity-scanner.d.ts +63 -0
- package/dist/entity-scanner.d.ts.map +1 -1
- package/dist/entity-scanner.js +79 -2
- package/dist/entity-scanner.js.map +1 -1
- package/dist/express-id.d.ts +47 -0
- package/dist/express-id.d.ts.map +1 -0
- package/dist/express-id.js +57 -0
- package/dist/express-id.js.map +1 -0
- package/dist/generated/entities.d.ts +1 -1
- package/dist/generated/entities.d.ts.map +1 -1
- package/dist/generated/schema-registry.js +146 -146
- package/dist/generated/schema-registry.js.map +1 -1
- package/dist/georef-epset.d.ts +25 -0
- package/dist/georef-epset.d.ts.map +1 -0
- package/dist/georef-epset.js +214 -0
- package/dist/georef-epset.js.map +1 -0
- package/dist/georef-extractor.d.ts +9 -12
- package/dist/georef-extractor.d.ts.map +1 -1
- package/dist/georef-extractor.js +66 -304
- package/dist/georef-extractor.js.map +1 -1
- package/dist/georef-map-conversion.d.ts +29 -0
- package/dist/georef-map-conversion.d.ts.map +1 -0
- package/dist/georef-map-conversion.js +75 -0
- package/dist/georef-map-conversion.js.map +1 -0
- package/dist/georef-transform.d.ts +25 -0
- package/dist/georef-transform.d.ts.map +1 -0
- package/dist/georef-transform.js +97 -0
- package/dist/georef-transform.js.map +1 -0
- package/dist/ifc-schema.d.ts +4 -2
- package/dist/ifc-schema.d.ts.map +1 -1
- package/dist/ifc-schema.js +15 -5
- package/dist/ifc-schema.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/map-unit-label.d.ts +43 -0
- package/dist/map-unit-label.d.ts.map +1 -0
- package/dist/map-unit-label.js +140 -0
- package/dist/map-unit-label.js.map +1 -0
- package/dist/material-extractor.d.ts +2 -17
- package/dist/material-extractor.d.ts.map +1 -1
- package/dist/material-extractor.js +12 -37
- package/dist/material-extractor.js.map +1 -1
- package/dist/material-layer-reader.d.ts +45 -0
- package/dist/material-layer-reader.d.ts.map +1 -0
- package/dist/material-layer-reader.js +63 -0
- package/dist/material-layer-reader.js.map +1 -0
- package/dist/material-resolver.d.ts.map +1 -1
- package/dist/material-resolver.js +14 -14
- package/dist/material-resolver.js.map +1 -1
- package/dist/measure-unit-scale.d.ts +41 -0
- package/dist/measure-unit-scale.d.ts.map +1 -0
- package/dist/measure-unit-scale.js +53 -0
- package/dist/measure-unit-scale.js.map +1 -0
- package/dist/on-demand-cache.d.ts +3 -0
- package/dist/on-demand-cache.d.ts.map +1 -0
- package/dist/on-demand-cache.js +25 -0
- package/dist/on-demand-cache.js.map +1 -0
- package/dist/on-demand-extractors.d.ts +5 -21
- package/dist/on-demand-extractors.d.ts.map +1 -1
- package/dist/on-demand-extractors.js +64 -437
- package/dist/on-demand-extractors.js.map +1 -1
- package/dist/on-demand-georeferencing.d.ts +19 -0
- package/dist/on-demand-georeferencing.d.ts.map +1 -0
- package/dist/on-demand-georeferencing.js +139 -0
- package/dist/on-demand-georeferencing.js.map +1 -0
- package/dist/owning-project.d.ts +54 -0
- package/dist/owning-project.d.ts.map +1 -0
- package/dist/owning-project.js +76 -0
- package/dist/owning-project.js.map +1 -0
- package/dist/parser.worker.d.ts +14 -0
- package/dist/parser.worker.d.ts.map +1 -1
- package/dist/parser.worker.js +2 -0
- package/dist/parser.worker.js.map +1 -1
- package/dist/project-units-symbols.d.ts +33 -0
- package/dist/project-units-symbols.d.ts.map +1 -0
- package/dist/project-units-symbols.js +265 -0
- package/dist/project-units-symbols.js.map +1 -0
- package/dist/project-units.d.ts +11 -19
- package/dist/project-units.d.ts.map +1 -1
- package/dist/project-units.js +9 -267
- package/dist/project-units.js.map +1 -1
- package/dist/property-set-merge.d.ts +65 -8
- package/dist/property-set-merge.d.ts.map +1 -1
- package/dist/property-set-merge.js +130 -29
- package/dist/property-set-merge.js.map +1 -1
- package/dist/property-value-parser.d.ts +55 -0
- package/dist/property-value-parser.d.ts.map +1 -0
- package/dist/property-value-parser.js +277 -0
- package/dist/property-value-parser.js.map +1 -0
- package/dist/quantity-collect.d.ts +135 -0
- package/dist/quantity-collect.d.ts.map +1 -0
- package/dist/quantity-collect.js +253 -0
- package/dist/quantity-collect.js.map +1 -0
- package/dist/query-backend-maps.d.ts +5 -8
- package/dist/query-backend-maps.d.ts.map +1 -1
- package/dist/query-backend-maps.js +159 -15
- package/dist/query-backend-maps.js.map +1 -1
- package/dist/relationship-extractor.d.ts.map +1 -1
- package/dist/relationship-extractor.js +18 -0
- package/dist/relationship-extractor.js.map +1 -1
- package/dist/scan-entities-balanced.d.ts +38 -0
- package/dist/scan-entities-balanced.d.ts.map +1 -0
- package/dist/scan-entities-balanced.js +258 -0
- package/dist/scan-entities-balanced.js.map +1 -0
- package/dist/scan-worker-inline.d.ts +17 -11
- package/dist/scan-worker-inline.d.ts.map +1 -1
- package/dist/scan-worker-inline.js +9 -235
- package/dist/scan-worker-inline.js.map +1 -1
- package/dist/scan-worker-source.d.ts +9 -0
- package/dist/scan-worker-source.d.ts.map +1 -0
- package/dist/scan-worker-source.js +383 -0
- package/dist/scan-worker-source.js.map +1 -0
- package/dist/source-header.d.ts +23 -1
- package/dist/source-header.d.ts.map +1 -1
- package/dist/source-header.js +133 -43
- package/dist/source-header.js.map +1 -1
- package/dist/spatial-hierarchy-builder.js +3 -3
- package/dist/spatial-hierarchy-builder.js.map +1 -1
- package/dist/step-lexing.d.ts +94 -0
- package/dist/step-lexing.d.ts.map +1 -1
- package/dist/step-lexing.js +292 -25
- package/dist/step-lexing.js.map +1 -1
- package/dist/step-trivia.d.ts +60 -0
- package/dist/step-trivia.d.ts.map +1 -0
- package/dist/step-trivia.js +63 -0
- package/dist/step-trivia.js.map +1 -0
- package/dist/tokenizer.d.ts +20 -21
- package/dist/tokenizer.d.ts.map +1 -1
- package/dist/tokenizer.js +164 -203
- package/dist/tokenizer.js.map +1 -1
- package/dist/unit-extractor.d.ts +56 -1
- package/dist/unit-extractor.d.ts.map +1 -1
- package/dist/unit-extractor.js +80 -6
- package/dist/unit-extractor.js.map +1 -1
- package/dist/worker-parser.d.ts +11 -1
- package/dist/worker-parser.d.ts.map +1 -1
- package/dist/worker-parser.js +16 -2
- package/dist/worker-parser.js.map +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regex source for ISO 10303-21 "trivia": a run of ASCII whitespace and
|
|
3
|
+
* non-nesting `/* ... */` comments, in any order, ANYWHERE whitespace is
|
|
4
|
+
* legal in a record — including between a type name and its `(`.
|
|
5
|
+
*
|
|
6
|
+
* String-side counterpart to `skipTrivia` in `step-lexing.ts`, for callers
|
|
7
|
+
* that already hold a decoded record as a JS string and build a regex around
|
|
8
|
+
* it (entity/typed-value extraction and rewriting), rather than scanning raw
|
|
9
|
+
* bytes. Both mirror `skip_step_trivia` in `rust/core/src/parser/lexical.rs`.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately ASCII-only (`[ \t\n\r\x0b\x0c]`, matching `is_step_space` /
|
|
12
|
+
* `isSpaceByte` in `step-lexing.ts`) rather than `\s`, which also matches
|
|
13
|
+
* U+00A0 and other Unicode space separators Rust's byte scanner does not
|
|
14
|
+
* treat as whitespace — the exact TS/Rust divergence class issue #3733
|
|
15
|
+
* fixed for the byte scanners; a new trivia matcher should not reintroduce
|
|
16
|
+
* it under a different name.
|
|
17
|
+
*
|
|
18
|
+
* ## Why each alternative is shaped the way it is
|
|
19
|
+
*
|
|
20
|
+
* The outer `(?:A|B)*` must have exactly ONE way to partition any input into
|
|
21
|
+
* iterations. Where it has more than one, a failing suffix makes the engine
|
|
22
|
+
* enumerate them, which is exponential in the length of the trivia run. Both
|
|
23
|
+
* alternatives below are written to keep that count at one, and BOTH shapes
|
|
24
|
+
* have been measured — the two axes fail independently, so neither is
|
|
25
|
+
* theoretical:
|
|
26
|
+
*
|
|
27
|
+
* - The comment body is `(?:[^*]|\*(?!/))*`, NOT a lazy `[\s\S]*?`. A lazy
|
|
28
|
+
* body looks unambiguous locally, but when the overall pattern fails past
|
|
29
|
+
* a comment the engine retries it against every later `*/`, so one comment
|
|
30
|
+
* can absorb the ones after it and the two alternatives start overlapping
|
|
31
|
+
* on the same span. Requiring every `*` in the body to not be followed by
|
|
32
|
+
* `/` gives the body exactly one maximal extent, so a comment can never
|
|
33
|
+
* swallow the next one.
|
|
34
|
+
* - The whitespace alternative is a SINGLE-CHARACTER class, NOT `[...]+`.
|
|
35
|
+
* This is the counter-intuitive half: `+` looks like it collapses a run
|
|
36
|
+
* into one iteration, but the outer `*` can still split an n-character run
|
|
37
|
+
* into any composition of `+` matches (2^(n-1) of them) and walks all of
|
|
38
|
+
* them on failure — the textbook `(?:A+|B)*` blowup. A single-character
|
|
39
|
+
* class has exactly one partition: n iterations of one character each.
|
|
40
|
+
*
|
|
41
|
+
* Both hazards are pinned by `packages/parser/test/step-trivia-redos.test.ts`,
|
|
42
|
+
* one case per axis, and the test comment there carries the measurements.
|
|
43
|
+
* Change either alternative only with that test in front of you.
|
|
44
|
+
*
|
|
45
|
+
* ## Relation to the byte scanners
|
|
46
|
+
*
|
|
47
|
+
* The comment body also brings this pattern into line with
|
|
48
|
+
* `skip_step_trivia`, which stops a comment at its FIRST `*/`. A lazy body
|
|
49
|
+
* did not: on backtracking it would accept `/* a */ */` as one comment,
|
|
50
|
+
* where Rust reads a comment followed by junk and refuses the record. The
|
|
51
|
+
* shared vector "reject: comments do not nest, so the trailing `*/` is junk"
|
|
52
|
+
* in `rust/core/tests/fixtures/type_paren_trivia_vectors.json` pins that
|
|
53
|
+
* agreement, so this is a deliberate narrowing, not an accident.
|
|
54
|
+
*
|
|
55
|
+
* An unpaired, unterminated `/*` still fails to match rather than hanging:
|
|
56
|
+
* the body runs out of input, backtracking is O(1) per position, and the
|
|
57
|
+
* required `\*/` after it never appears.
|
|
58
|
+
*/
|
|
59
|
+
export declare const STEP_TRIVIA = "(?:[ \\t\\n\\r\\x0b\\x0c]|/\\*(?:[^*]|\\*(?!/))*\\*/)*";
|
|
60
|
+
//# sourceMappingURL=step-trivia.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-trivia.d.ts","sourceRoot":"","sources":["../src/step-trivia.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,eAAO,MAAM,WAAW,2DAA2D,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Regex source for ISO 10303-21 "trivia": a run of ASCII whitespace and
|
|
6
|
+
* non-nesting `/* ... */` comments, in any order, ANYWHERE whitespace is
|
|
7
|
+
* legal in a record — including between a type name and its `(`.
|
|
8
|
+
*
|
|
9
|
+
* String-side counterpart to `skipTrivia` in `step-lexing.ts`, for callers
|
|
10
|
+
* that already hold a decoded record as a JS string and build a regex around
|
|
11
|
+
* it (entity/typed-value extraction and rewriting), rather than scanning raw
|
|
12
|
+
* bytes. Both mirror `skip_step_trivia` in `rust/core/src/parser/lexical.rs`.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately ASCII-only (`[ \t\n\r\x0b\x0c]`, matching `is_step_space` /
|
|
15
|
+
* `isSpaceByte` in `step-lexing.ts`) rather than `\s`, which also matches
|
|
16
|
+
* U+00A0 and other Unicode space separators Rust's byte scanner does not
|
|
17
|
+
* treat as whitespace — the exact TS/Rust divergence class issue #3733
|
|
18
|
+
* fixed for the byte scanners; a new trivia matcher should not reintroduce
|
|
19
|
+
* it under a different name.
|
|
20
|
+
*
|
|
21
|
+
* ## Why each alternative is shaped the way it is
|
|
22
|
+
*
|
|
23
|
+
* The outer `(?:A|B)*` must have exactly ONE way to partition any input into
|
|
24
|
+
* iterations. Where it has more than one, a failing suffix makes the engine
|
|
25
|
+
* enumerate them, which is exponential in the length of the trivia run. Both
|
|
26
|
+
* alternatives below are written to keep that count at one, and BOTH shapes
|
|
27
|
+
* have been measured — the two axes fail independently, so neither is
|
|
28
|
+
* theoretical:
|
|
29
|
+
*
|
|
30
|
+
* - The comment body is `(?:[^*]|\*(?!/))*`, NOT a lazy `[\s\S]*?`. A lazy
|
|
31
|
+
* body looks unambiguous locally, but when the overall pattern fails past
|
|
32
|
+
* a comment the engine retries it against every later `*/`, so one comment
|
|
33
|
+
* can absorb the ones after it and the two alternatives start overlapping
|
|
34
|
+
* on the same span. Requiring every `*` in the body to not be followed by
|
|
35
|
+
* `/` gives the body exactly one maximal extent, so a comment can never
|
|
36
|
+
* swallow the next one.
|
|
37
|
+
* - The whitespace alternative is a SINGLE-CHARACTER class, NOT `[...]+`.
|
|
38
|
+
* This is the counter-intuitive half: `+` looks like it collapses a run
|
|
39
|
+
* into one iteration, but the outer `*` can still split an n-character run
|
|
40
|
+
* into any composition of `+` matches (2^(n-1) of them) and walks all of
|
|
41
|
+
* them on failure — the textbook `(?:A+|B)*` blowup. A single-character
|
|
42
|
+
* class has exactly one partition: n iterations of one character each.
|
|
43
|
+
*
|
|
44
|
+
* Both hazards are pinned by `packages/parser/test/step-trivia-redos.test.ts`,
|
|
45
|
+
* one case per axis, and the test comment there carries the measurements.
|
|
46
|
+
* Change either alternative only with that test in front of you.
|
|
47
|
+
*
|
|
48
|
+
* ## Relation to the byte scanners
|
|
49
|
+
*
|
|
50
|
+
* The comment body also brings this pattern into line with
|
|
51
|
+
* `skip_step_trivia`, which stops a comment at its FIRST `*/`. A lazy body
|
|
52
|
+
* did not: on backtracking it would accept `/* a */ */` as one comment,
|
|
53
|
+
* where Rust reads a comment followed by junk and refuses the record. The
|
|
54
|
+
* shared vector "reject: comments do not nest, so the trailing `*/` is junk"
|
|
55
|
+
* in `rust/core/tests/fixtures/type_paren_trivia_vectors.json` pins that
|
|
56
|
+
* agreement, so this is a deliberate narrowing, not an accident.
|
|
57
|
+
*
|
|
58
|
+
* An unpaired, unterminated `/*` still fails to match rather than hanging:
|
|
59
|
+
* the body runs out of input, backtracking is O(1) per position, and the
|
|
60
|
+
* required `\*/` after it never appears.
|
|
61
|
+
*/
|
|
62
|
+
export const STEP_TRIVIA = '(?:[ \\t\\n\\r\\x0b\\x0c]|/\\*(?:[^*]|\\*(?!/))*\\*/)*';
|
|
63
|
+
//# sourceMappingURL=step-trivia.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-trivia.js","sourceRoot":"","sources":["../src/step-trivia.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,wDAAwD,CAAC"}
|
package/dist/tokenizer.d.ts
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
|
+
import { type ScannedEntityRef } from './scan-entities-balanced.js';
|
|
1
2
|
export declare class StepTokenizer {
|
|
2
3
|
private buffer;
|
|
3
|
-
private
|
|
4
|
-
private
|
|
4
|
+
private oversizedIds;
|
|
5
|
+
private malformedRecords;
|
|
5
6
|
constructor(buffer: Uint8Array);
|
|
7
|
+
/** Records the last scan refused for an out-of-contract express id
|
|
8
|
+
* (express-id.ts, #3395). Reset per scan; the caller reports it. */
|
|
9
|
+
get oversizedIdCount(): number;
|
|
10
|
+
/** 0 or 1: whether the last `scanEntitiesFast`/`scanEntities` run stopped
|
|
11
|
+
* early on an unclosed `'` string, an unclosed block comment, or a
|
|
12
|
+
* declaration cut off before its own '(' -- never a count of how many,
|
|
13
|
+
* since the scan has no reliable way to resume past the first one it
|
|
14
|
+
* hits. Reset at the start of every scan; the caller reports it. */
|
|
15
|
+
get malformedRecordCount(): number;
|
|
6
16
|
/**
|
|
7
17
|
* Scan for all entity declarations (#EXPRESS_ID = TYPE(...))
|
|
8
|
-
* Returns entity references without parsing full content
|
|
18
|
+
* Returns entity references without parsing full content.
|
|
19
|
+
*
|
|
20
|
+
* Closes each record on the ')' balancing its argument list. The scan itself
|
|
21
|
+
* lives in `scan-entities-balanced.ts`; only the refusal count comes back
|
|
22
|
+
* here, and it comes back in a `finally` so an abandoned generator still
|
|
23
|
+
* reports what it refused.
|
|
9
24
|
*/
|
|
10
|
-
scanEntities(): Generator<
|
|
11
|
-
expressId: number;
|
|
12
|
-
type: string;
|
|
13
|
-
offset: number;
|
|
14
|
-
length: number;
|
|
15
|
-
line: number;
|
|
16
|
-
}>;
|
|
25
|
+
scanEntities(): Generator<ScannedEntityRef>;
|
|
17
26
|
/**
|
|
18
27
|
* FAST scan - skips to semicolon instead of matching parentheses
|
|
19
28
|
* ~5-10x faster for large files, yields length=0 (calculate on-demand)
|
|
20
29
|
*/
|
|
21
|
-
scanEntitiesFast(): Generator<
|
|
22
|
-
expressId: number;
|
|
23
|
-
type: string;
|
|
24
|
-
offset: number;
|
|
25
|
-
length: number;
|
|
26
|
-
line: number;
|
|
27
|
-
}>;
|
|
28
|
-
private readExpressId;
|
|
29
|
-
private readTypeName;
|
|
30
|
-
private skipWhitespace;
|
|
31
|
-
private findEntityLength;
|
|
30
|
+
scanEntitiesFast(): Generator<ScannedEntityRef>;
|
|
32
31
|
}
|
|
33
32
|
//# sourceMappingURL=tokenizer.d.ts.map
|
package/dist/tokenizer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../src/tokenizer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../src/tokenizer.ts"],"names":[],"mappings":"AAUA,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAUxF,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,gBAAgB,CAAa;gBAEzB,MAAM,EAAE,UAAU;IAI9B;yEACqE;IACrE,IAAI,gBAAgB,IAAI,MAAM,CAA8B;IAE5D;;;;yEAIqE;IACrE,IAAI,oBAAoB,IAAI,MAAM,CAAkC;IAEpE;;;;;;;;OAQG;IACF,YAAY,IAAI,SAAS,CAAC,gBAAgB,CAAC;IAY5C;;;OAGG;IACF,gBAAgB,IAAI,SAAS,CAAC,gBAAgB,CAAC;CAqQjD"}
|
package/dist/tokenizer.js
CHANGED
|
@@ -5,97 +5,44 @@
|
|
|
5
5
|
* STEP tokenizer - fast byte-level scanning for entity markers
|
|
6
6
|
* Leverages Spike 1 approach: ~1,259 MB/s throughput
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { isIndexableExpressId } from './express-id.js';
|
|
9
|
+
import { BalancedEntityScan } from './scan-entities-balanced.js';
|
|
10
|
+
import { countNewlines, opensComment, opensLiteralOrComment, skipComment, skipLexical, skipTrivia, } from './step-lexing.js';
|
|
10
11
|
export class StepTokenizer {
|
|
11
12
|
buffer;
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
oversizedIds = 0;
|
|
14
|
+
malformedRecords = 0;
|
|
14
15
|
constructor(buffer) {
|
|
15
16
|
this.buffer = buffer;
|
|
16
17
|
}
|
|
18
|
+
/** Records the last scan refused for an out-of-contract express id
|
|
19
|
+
* (express-id.ts, #3395). Reset per scan; the caller reports it. */
|
|
20
|
+
get oversizedIdCount() { return this.oversizedIds; }
|
|
21
|
+
/** 0 or 1: whether the last `scanEntitiesFast`/`scanEntities` run stopped
|
|
22
|
+
* early on an unclosed `'` string, an unclosed block comment, or a
|
|
23
|
+
* declaration cut off before its own '(' -- never a count of how many,
|
|
24
|
+
* since the scan has no reliable way to resume past the first one it
|
|
25
|
+
* hits. Reset at the start of every scan; the caller reports it. */
|
|
26
|
+
get malformedRecordCount() { return this.malformedRecords; }
|
|
17
27
|
/**
|
|
18
28
|
* Scan for all entity declarations (#EXPRESS_ID = TYPE(...))
|
|
19
|
-
* Returns entity references without parsing full content
|
|
29
|
+
* Returns entity references without parsing full content.
|
|
30
|
+
*
|
|
31
|
+
* Closes each record on the ')' balancing its argument list. The scan itself
|
|
32
|
+
* lives in `scan-entities-balanced.ts`; only the refusal count comes back
|
|
33
|
+
* here, and it comes back in a `finally` so an abandoned generator still
|
|
34
|
+
* reports what it refused.
|
|
20
35
|
*/
|
|
21
36
|
*scanEntities() {
|
|
22
|
-
|
|
23
|
-
this.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (expressId === null) {
|
|
32
|
-
this.position++;
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
// Skip whitespace
|
|
36
|
-
this.skipWhitespace();
|
|
37
|
-
// Check for '=' (assignment)
|
|
38
|
-
if (this.position >= this.buffer.length || this.buffer[this.position] !== 0x3D) {
|
|
39
|
-
this.position++;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
this.position++; // Skip '='
|
|
43
|
-
// Skip whitespace
|
|
44
|
-
this.skipWhitespace();
|
|
45
|
-
// Read type name
|
|
46
|
-
const type = this.readTypeName();
|
|
47
|
-
if (!type) {
|
|
48
|
-
this.position++;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
// Skip whitespace
|
|
52
|
-
this.skipWhitespace();
|
|
53
|
-
// Check for '(' (start of parameters)
|
|
54
|
-
if (this.position >= this.buffer.length || this.buffer[this.position] !== 0x28) {
|
|
55
|
-
this.position++;
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
// Find matching closing parenthesis to get full entity length
|
|
59
|
-
const entityLength = this.findEntityLength(startOffset);
|
|
60
|
-
if (entityLength > 0) {
|
|
61
|
-
// Step past the whole record, as Rust's next_entity does. Leaving
|
|
62
|
-
// `position` at the '(' made this loop re-walk the body, which was
|
|
63
|
-
// harmless only while it ignored quotes and comments.
|
|
64
|
-
//
|
|
65
|
-
// Count from `position`, not from `startOffset`: `position` is on the
|
|
66
|
-
// '(' here, and every newline before it was already counted by the
|
|
67
|
-
// three skipWhitespace calls above. Counting the whole record instead
|
|
68
|
-
// double-counts a newline written between `#1=` and its type name,
|
|
69
|
-
// which is ordinary whitespace and legal.
|
|
70
|
-
this.lineNumber += countNewlines(this.buffer, this.position, startOffset + entityLength);
|
|
71
|
-
this.position = startOffset + entityLength;
|
|
72
|
-
yield {
|
|
73
|
-
expressId,
|
|
74
|
-
type,
|
|
75
|
-
offset: startOffset,
|
|
76
|
-
length: entityLength,
|
|
77
|
-
line: startLine,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
else if (this.buffer[this.position] === 0x0A) {
|
|
82
|
-
// Newline
|
|
83
|
-
this.lineNumber++;
|
|
84
|
-
this.position++;
|
|
85
|
-
}
|
|
86
|
-
else if (opensLiteralOrComment(this.buffer, this.position, this.buffer.length)) {
|
|
87
|
-
// A commented-out record satisfies every check above, so a comment has
|
|
88
|
-
// to be skipped as a region; a literal has to be skipped so its
|
|
89
|
-
// contents cannot look like one. See step-lexing.
|
|
90
|
-
const skip = skipLexical(this.buffer, this.position, this.buffer.length);
|
|
91
|
-
this.lineNumber += skip.lines;
|
|
92
|
-
this.position = skip.next;
|
|
93
|
-
if (skip.stop)
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
this.position++;
|
|
98
|
-
}
|
|
37
|
+
const scan = new BalancedEntityScan(this.buffer);
|
|
38
|
+
this.oversizedIds = 0;
|
|
39
|
+
this.malformedRecords = 0;
|
|
40
|
+
try {
|
|
41
|
+
yield* scan.run();
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
this.oversizedIds = scan.oversizedIdCount;
|
|
45
|
+
this.malformedRecords = scan.malformedRecordCount;
|
|
99
46
|
}
|
|
100
47
|
}
|
|
101
48
|
/**
|
|
@@ -103,8 +50,8 @@ export class StepTokenizer {
|
|
|
103
50
|
* ~5-10x faster for large files, yields length=0 (calculate on-demand)
|
|
104
51
|
*/
|
|
105
52
|
*scanEntitiesFast() {
|
|
106
|
-
this.
|
|
107
|
-
this.
|
|
53
|
+
this.oversizedIds = 0;
|
|
54
|
+
this.malformedRecords = 0;
|
|
108
55
|
// Pre-compute common byte codes
|
|
109
56
|
const HASH = 0x23; // '#'
|
|
110
57
|
const EQUALS = 0x3D; // '='
|
|
@@ -112,6 +59,7 @@ export class StepTokenizer {
|
|
|
112
59
|
const SEMICOLON = 0x3B; // ';'
|
|
113
60
|
const QUOTE = 0x27; // '\''
|
|
114
61
|
const NEWLINE = 0x0A; // '\n'
|
|
62
|
+
const SLASH = 0x2F; // '/'
|
|
115
63
|
const buf = this.buffer;
|
|
116
64
|
const len = buf.length;
|
|
117
65
|
let pos = 0;
|
|
@@ -119,6 +67,22 @@ export class StepTokenizer {
|
|
|
119
67
|
// Cache type name strings: IFC files have ~776 unique types repeated
|
|
120
68
|
// across 8M+ entities. Caching avoids millions of String.fromCharCode allocations.
|
|
121
69
|
const typeCache = new Map();
|
|
70
|
+
// Set on the way to the single post-loop check at the bottom of this
|
|
71
|
+
// function, not counted at each site: `stopped` for an unclosed string or
|
|
72
|
+
// comment that ran the scan to end of buffer with nothing left to find,
|
|
73
|
+
// `declOpen` while a `#id=TYPE(` header is incomplete. `declOpen` stays
|
|
74
|
+
// armed ONLY when the reason for abandoning is running out of buffer
|
|
75
|
+
// (`pos >= len`); a mismatch with buffer still left (bad byte, oversized
|
|
76
|
+
// id) clears it, because the scan resumes byte-by-byte from wherever it
|
|
77
|
+
// gave up, and a `#ref` token inside the abandoned record's own argument
|
|
78
|
+
// list reads as a fresh, equally incomplete attempt -- one that must not
|
|
79
|
+
// report "cut off" just because nothing later happens to clear it.
|
|
80
|
+
// Per-site increments used to miss whole shapes -- a leading unterminated
|
|
81
|
+
// comment before '=', or a declaration cut off before its own '(' --
|
|
82
|
+
// because each site only knew about its own exit, never the scan's final
|
|
83
|
+
// state.
|
|
84
|
+
let stopped = false;
|
|
85
|
+
let declOpen = false;
|
|
122
86
|
while (pos < len) {
|
|
123
87
|
const char = buf[pos];
|
|
124
88
|
if (char === HASH) {
|
|
@@ -141,10 +105,15 @@ export class StepTokenizer {
|
|
|
141
105
|
}
|
|
142
106
|
if (!hasDigits)
|
|
143
107
|
continue;
|
|
144
|
-
|
|
108
|
+
declOpen = true;
|
|
109
|
+
// Skip whitespace (inline). Kept byte-for-byte in sync with
|
|
110
|
+
// `isSpaceByte` in step-lexing.ts (space, tab, CR, LF, form feed,
|
|
111
|
+
// vertical tab) -- this loop, its two twins below in this method, and
|
|
112
|
+
// the worker's copy in scan-worker-source.ts are the same rule
|
|
113
|
+
// hand-duplicated for speed, not four independent decisions.
|
|
145
114
|
while (pos < len) {
|
|
146
115
|
const c = buf[pos];
|
|
147
|
-
if (c === 0x20 || c === 0x09 || c === 0x0D) {
|
|
116
|
+
if (c === 0x20 || c === 0x09 || c === 0x0D || c === 0x0C || c === 0x0B) {
|
|
148
117
|
pos++;
|
|
149
118
|
}
|
|
150
119
|
else if (c === NEWLINE) {
|
|
@@ -154,14 +123,50 @@ export class StepTokenizer {
|
|
|
154
123
|
else
|
|
155
124
|
break;
|
|
156
125
|
}
|
|
157
|
-
//
|
|
158
|
-
|
|
126
|
+
// 10303-21 allows a comment wherever whitespace is allowed, so
|
|
127
|
+
// `#1 /* was #7 */ =` is a declaration. The inline loop above stays
|
|
128
|
+
// for the common case; this runs only once a comment actually opens,
|
|
129
|
+
// and skipTrivia (step-lexing) then takes the whole run of both.
|
|
130
|
+
if (opensComment(buf, pos, len)) {
|
|
131
|
+
const t = skipTrivia(buf, pos, len);
|
|
132
|
+
line += t.lines;
|
|
133
|
+
pos = t.next;
|
|
134
|
+
if (t.stop) {
|
|
135
|
+
stopped = true;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Check for '='. A byte that is not '=' with buffer left to scan is
|
|
140
|
+
// not a truncation -- clear declOpen so a reference token inside a
|
|
141
|
+
// LATER abandoned record's argument list (see the oversized-id note
|
|
142
|
+
// below) cannot leave it stuck armed with nothing left to clear it.
|
|
143
|
+
if (pos >= len)
|
|
144
|
+
continue;
|
|
145
|
+
if (buf[pos] !== EQUALS) {
|
|
146
|
+
declOpen = false;
|
|
159
147
|
continue;
|
|
148
|
+
}
|
|
160
149
|
pos++;
|
|
150
|
+
// Storage contract, not just overflow: see express-id.ts (#3395).
|
|
151
|
+
// Tested only now that `#<digits>[ws]*=` has matched, which is the
|
|
152
|
+
// DECLARATION shape Rust's `EntityScanner` validates before it
|
|
153
|
+
// refuses. Refusing above the '=' check counted references too: the
|
|
154
|
+
// `continue` resumes inside the refused record's argument list
|
|
155
|
+
// (unlike the accepted path, which skips to the terminating ';'), so
|
|
156
|
+
// `#4294967297=IFCWALL(#4294967298,#4294967299,...)` reported three
|
|
157
|
+
// skipped records for the one record actually dropped. A count that
|
|
158
|
+
// overstates is the same class of defect as one that undercounts.
|
|
159
|
+
// Refused for being out of range, not for running out of buffer, so
|
|
160
|
+
// it does not belong to `declOpen`'s "cut off by EOF" story either.
|
|
161
|
+
if (!isIndexableExpressId(expressId)) {
|
|
162
|
+
this.oversizedIds++;
|
|
163
|
+
declOpen = false;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
161
166
|
// Skip whitespace
|
|
162
167
|
while (pos < len) {
|
|
163
168
|
const c = buf[pos];
|
|
164
|
-
if (c === 0x20 || c === 0x09 || c === 0x0D) {
|
|
169
|
+
if (c === 0x20 || c === 0x09 || c === 0x0D || c === 0x0C || c === 0x0B) {
|
|
165
170
|
pos++;
|
|
166
171
|
}
|
|
167
172
|
else if (c === NEWLINE) {
|
|
@@ -171,10 +176,24 @@ export class StepTokenizer {
|
|
|
171
176
|
else
|
|
172
177
|
break;
|
|
173
178
|
}
|
|
174
|
-
|
|
179
|
+
if (opensComment(buf, pos, len)) {
|
|
180
|
+
const t = skipTrivia(buf, pos, len);
|
|
181
|
+
line += t.lines;
|
|
182
|
+
pos = t.next;
|
|
183
|
+
if (t.stop) {
|
|
184
|
+
stopped = true;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Read type name (inline). Must start A-Z; a bad start byte with
|
|
189
|
+
// buffer left clears declOpen for the same reason as the '=' check.
|
|
175
190
|
const typeStart = pos;
|
|
176
|
-
if (pos >= len
|
|
177
|
-
continue;
|
|
191
|
+
if (pos >= len)
|
|
192
|
+
continue;
|
|
193
|
+
if (buf[pos] < 0x41 || buf[pos] > 0x5A) {
|
|
194
|
+
declOpen = false;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
178
197
|
while (pos < len) {
|
|
179
198
|
const c = buf[pos];
|
|
180
199
|
if ((c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A) ||
|
|
@@ -218,7 +237,7 @@ export class StepTokenizer {
|
|
|
218
237
|
// Skip whitespace
|
|
219
238
|
while (pos < len) {
|
|
220
239
|
const c = buf[pos];
|
|
221
|
-
if (c === 0x20 || c === 0x09 || c === 0x0D) {
|
|
240
|
+
if (c === 0x20 || c === 0x09 || c === 0x0D || c === 0x0C || c === 0x0B) {
|
|
222
241
|
pos++;
|
|
223
242
|
}
|
|
224
243
|
else if (c === NEWLINE) {
|
|
@@ -228,11 +247,26 @@ export class StepTokenizer {
|
|
|
228
247
|
else
|
|
229
248
|
break;
|
|
230
249
|
}
|
|
231
|
-
|
|
232
|
-
|
|
250
|
+
if (opensComment(buf, pos, len)) {
|
|
251
|
+
const t = skipTrivia(buf, pos, len);
|
|
252
|
+
line += t.lines;
|
|
253
|
+
pos = t.next;
|
|
254
|
+
if (t.stop) {
|
|
255
|
+
stopped = true;
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Check for '('. Same EOF-vs-mismatch split as '=' and the type name.
|
|
260
|
+
if (pos >= len)
|
|
233
261
|
continue;
|
|
262
|
+
if (buf[pos] !== LPAREN) {
|
|
263
|
+
declOpen = false;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
declOpen = false; // Header complete: '(' found.
|
|
234
267
|
// FAST: Skip to semicolon (handling strings)
|
|
235
268
|
let inString = false;
|
|
269
|
+
let foundTerminator = false;
|
|
236
270
|
while (pos < len) {
|
|
237
271
|
const c = buf[pos];
|
|
238
272
|
if (c === QUOTE) {
|
|
@@ -242,11 +276,29 @@ export class StepTokenizer {
|
|
|
242
276
|
}
|
|
243
277
|
inString = !inString;
|
|
244
278
|
}
|
|
279
|
+
else if (c === SLASH && !inString && opensComment(buf, pos, len)) {
|
|
280
|
+
// The ';' that ends a record can be preceded by a comment holding
|
|
281
|
+
// its own ';'. Take the comment whole -- which also makes the
|
|
282
|
+
// quotes and parens inside it text, the other half of the rule the
|
|
283
|
+
// literal skip above provides in the opposite direction.
|
|
284
|
+
const end = skipComment(buf, pos, len);
|
|
285
|
+
if (end < 0) {
|
|
286
|
+
// Unterminated: this record has no terminator, and neither has
|
|
287
|
+
// anything after it. Drop it and stop, which is the None Rust's
|
|
288
|
+
// find_entity_end returns on the same input.
|
|
289
|
+
pos = len;
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
line += countNewlines(buf, pos, end);
|
|
293
|
+
pos = end;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
245
296
|
else if (c === SEMICOLON && !inString) {
|
|
246
297
|
// Found end of entity
|
|
247
298
|
const entityLength = pos - startOffset + 1; // Include semicolon
|
|
248
299
|
yield { expressId, type, offset: startOffset, length: entityLength, line: startLine };
|
|
249
300
|
pos++;
|
|
301
|
+
foundTerminator = true;
|
|
250
302
|
break;
|
|
251
303
|
}
|
|
252
304
|
else if (c === NEWLINE) {
|
|
@@ -254,6 +306,14 @@ export class StepTokenizer {
|
|
|
254
306
|
}
|
|
255
307
|
pos++;
|
|
256
308
|
}
|
|
309
|
+
// Ran off the end without an unquoted ';' — usually an unescaped `'`
|
|
310
|
+
// left open, or the unterminated-comment break above; `pos` is
|
|
311
|
+
// already `len`, ending the scan here. Not resynced: with no known
|
|
312
|
+
// terminator, guessing a resume point risks fabricating entities from
|
|
313
|
+
// misaligned bytes. Recorded in `stopped`, not incremented here --
|
|
314
|
+
// see the post-loop check below.
|
|
315
|
+
if (!foundTerminator)
|
|
316
|
+
stopped = true;
|
|
257
317
|
}
|
|
258
318
|
else if (char === NEWLINE) {
|
|
259
319
|
line++;
|
|
@@ -268,120 +328,21 @@ export class StepTokenizer {
|
|
|
268
328
|
line += skip.lines;
|
|
269
329
|
pos = skip.next;
|
|
270
330
|
if (skip.stop) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
else {
|
|
277
|
-
pos++;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
this.position = pos;
|
|
281
|
-
this.lineNumber = line;
|
|
282
|
-
}
|
|
283
|
-
readExpressId() {
|
|
284
|
-
let id = 0;
|
|
285
|
-
let digits = 0;
|
|
286
|
-
let pos = this.position + 1; // Skip '#'
|
|
287
|
-
while (pos < this.buffer.length) {
|
|
288
|
-
const char = this.buffer[pos];
|
|
289
|
-
if (char >= 0x30 && char <= 0x39) { // '0'-'9'
|
|
290
|
-
id = id * 10 + (char - 0x30);
|
|
291
|
-
digits++;
|
|
292
|
-
pos++;
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
break;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
if (digits === 0)
|
|
299
|
-
return null;
|
|
300
|
-
this.position = pos;
|
|
301
|
-
return id;
|
|
302
|
-
}
|
|
303
|
-
readTypeName() {
|
|
304
|
-
let start = this.position;
|
|
305
|
-
let end = start;
|
|
306
|
-
// Type names start with uppercase letter
|
|
307
|
-
if (this.position >= this.buffer.length || this.buffer[this.position] < 0x41 || this.buffer[this.position] > 0x5A) {
|
|
308
|
-
return null;
|
|
309
|
-
}
|
|
310
|
-
while (end < this.buffer.length) {
|
|
311
|
-
const char = this.buffer[end];
|
|
312
|
-
// Allow letters, numbers, and underscore
|
|
313
|
-
if ((char >= 0x41 && char <= 0x5A) || // A-Z
|
|
314
|
-
(char >= 0x61 && char <= 0x7A) || // a-z
|
|
315
|
-
(char >= 0x30 && char <= 0x39) || // 0-9
|
|
316
|
-
char === 0x5F // _
|
|
317
|
-
) {
|
|
318
|
-
end++;
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
break;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
if (end === start)
|
|
325
|
-
return null;
|
|
326
|
-
const typeName = safeUtf8Decode(this.buffer, start, end);
|
|
327
|
-
this.position = end;
|
|
328
|
-
return typeName;
|
|
329
|
-
}
|
|
330
|
-
skipWhitespace() {
|
|
331
|
-
while (this.position < this.buffer.length) {
|
|
332
|
-
const char = this.buffer[this.position];
|
|
333
|
-
if (char === 0x20 || char === 0x09 || char === 0x0D || char === 0x0A) { // space, tab, CR, LF
|
|
334
|
-
if (char === 0x0A)
|
|
335
|
-
this.lineNumber++;
|
|
336
|
-
this.position++;
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
break;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
findEntityLength(startOffset) {
|
|
344
|
-
let pos = this.position;
|
|
345
|
-
let depth = 0;
|
|
346
|
-
let inString = false;
|
|
347
|
-
while (pos < this.buffer.length) {
|
|
348
|
-
const char = this.buffer[pos];
|
|
349
|
-
if (char === 0x27) { // Single quote (string delimiter)
|
|
350
|
-
if (inString) {
|
|
351
|
-
// Check for escaped quote ('') - STEP uses doubled quotes
|
|
352
|
-
if (pos + 1 < this.buffer.length && this.buffer[pos + 1] === 0x27) {
|
|
353
|
-
pos += 2; // Skip escaped quote
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
|
-
inString = false;
|
|
357
|
-
}
|
|
358
|
-
else {
|
|
359
|
-
inString = true;
|
|
360
|
-
}
|
|
361
|
-
pos++;
|
|
362
|
-
continue;
|
|
363
|
-
}
|
|
364
|
-
if (inString) {
|
|
365
|
-
pos++;
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
368
|
-
if (char === 0x28) { // '('
|
|
369
|
-
depth++;
|
|
370
|
-
pos++;
|
|
371
|
-
}
|
|
372
|
-
else if (char === 0x29) { // ')'
|
|
373
|
-
depth--;
|
|
374
|
-
pos++;
|
|
375
|
-
if (depth === 0) {
|
|
376
|
-
// Found matching closing parenthesis
|
|
377
|
-
return pos - startOffset;
|
|
331
|
+
stopped = true;
|
|
332
|
+
break;
|
|
378
333
|
}
|
|
379
334
|
}
|
|
380
335
|
else {
|
|
381
336
|
pos++;
|
|
382
337
|
}
|
|
383
338
|
}
|
|
384
|
-
|
|
339
|
+
// ONE post-loop check, not an increment at every exit site above: the
|
|
340
|
+
// scan stopped early if it hit an explicit "no terminator" boundary
|
|
341
|
+
// (`stopped`), or the last `#id=TYPE(` header was cut short before its
|
|
342
|
+
// '(' was found (`declOpen`). Always 0 or 1 -- the scan stops at the
|
|
343
|
+
// first one, so there is nothing further to accumulate.
|
|
344
|
+
if (stopped || declOpen)
|
|
345
|
+
this.malformedRecords = 1;
|
|
385
346
|
}
|
|
386
347
|
}
|
|
387
348
|
//# sourceMappingURL=tokenizer.js.map
|