@ifc-lite/parser 4.3.0 → 4.3.2
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/columnar-parser-indexes.d.ts.map +1 -1
- package/dist/columnar-parser-indexes.js +3 -0
- package/dist/columnar-parser-indexes.js.map +1 -1
- package/dist/columnar-parser.d.ts.map +1 -1
- package/dist/columnar-parser.js +13 -53
- package/dist/columnar-parser.js.map +1 -1
- package/dist/georef-extractor.d.ts +9 -12
- package/dist/georef-extractor.d.ts.map +1 -1
- package/dist/georef-extractor.js +29 -130
- package/dist/georef-extractor.js.map +1 -1
- 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.map +1 -1
- package/dist/ifc-schema.js +9 -5
- package/dist/ifc-schema.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/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 +2 -2
- package/dist/on-demand-extractors.d.ts.map +1 -1
- package/dist/on-demand-extractors.js +14 -177
- 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/quantity-collect.d.ts +96 -0
- package/dist/quantity-collect.d.ts.map +1 -0
- package/dist/quantity-collect.js +146 -0
- package/dist/quantity-collect.js.map +1 -0
- package/dist/query-backend-maps.d.ts.map +1 -1
- package/dist/query-backend-maps.js +4 -0
- package/dist/query-backend-maps.js.map +1 -1
- 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/step-lexing.d.ts +92 -0
- package/dist/step-lexing.d.ts.map +1 -1
- package/dist/step-lexing.js +176 -7
- package/dist/step-lexing.js.map +1 -1
- package/dist/unit-extractor.d.ts +13 -0
- package/dist/unit-extractor.d.ts.map +1 -1
- package/dist/unit-extractor.js +17 -2
- package/dist/unit-extractor.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,146 @@
|
|
|
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
|
+
* Shared reader for an `IfcElementQuantity.Quantities` list (#3254).
|
|
6
|
+
*
|
|
7
|
+
* The instance path (`ColumnarParser.extractQuantitiesOnDemand`) and the type
|
|
8
|
+
* path (`extractQsetsFromIds`) both walk that list, and each used to inline its
|
|
9
|
+
* own copy of the walk — two copies that would disagree the moment either was
|
|
10
|
+
* touched. The walk lives here and both call it.
|
|
11
|
+
*/
|
|
12
|
+
import { QuantityType } from '@ifc-lite/data';
|
|
13
|
+
import { QUANTITY_TYPE_MAP } from './columnar-parser-indexes.js';
|
|
14
|
+
/**
|
|
15
|
+
* `IfcPhysicalComplexQuantity` groups other quantities instead of carrying a
|
|
16
|
+
* value of its own (`packages/codegen/schemas/IFC4_ADD2_TC1.exp`, identically
|
|
17
|
+
* in `IFC4X3.exp`):
|
|
18
|
+
*
|
|
19
|
+
* ENTITY IfcPhysicalComplexQuantity
|
|
20
|
+
* SUBTYPE OF (IfcPhysicalQuantity);
|
|
21
|
+
* HasQuantities : SET [1:?] OF IfcPhysicalQuantity;
|
|
22
|
+
* Discrimination : IfcLabel;
|
|
23
|
+
* Quality : OPTIONAL IfcLabel;
|
|
24
|
+
* Usage : OPTIONAL IfcLabel;
|
|
25
|
+
*
|
|
26
|
+
* With `Name` and `Description` inherited from `IfcPhysicalQuantity`, the
|
|
27
|
+
* flattened slots are HasQuantities[2], Discrimination[3], Quality[4],
|
|
28
|
+
* Usage[5]. Slot 3 — where every `IfcPhysicalSimpleQuantity` subtype keeps its
|
|
29
|
+
* measure — therefore holds a label here.
|
|
30
|
+
*/
|
|
31
|
+
const COMPLEX_QUANTITY_TYPE = 'IFCPHYSICALCOMPLEXQUANTITY';
|
|
32
|
+
/**
|
|
33
|
+
* Value slot on every `IfcPhysicalSimpleQuantity` subtype: Name[0],
|
|
34
|
+
* Description[1], Unit[2], *Value[3].
|
|
35
|
+
*/
|
|
36
|
+
const SIMPLE_QUANTITY_VALUE_SLOT = 3;
|
|
37
|
+
/**
|
|
38
|
+
* Read an `IfcElementQuantity.Quantities` list into flat quantity records.
|
|
39
|
+
*
|
|
40
|
+
* An `IfcPhysicalComplexQuantity` is skipped: it has no measure to report, and
|
|
41
|
+
* a `{name, type, value}` triple has nowhere to put its `HasQuantities`
|
|
42
|
+
* children. Before #3254 it fell through the simple-quantity path and surfaced
|
|
43
|
+
* as a phantom `Count = 0` — a row that satisfied IDS existence requirements,
|
|
44
|
+
* counted as "has quantities" in `validate`, entered the compare fingerprints
|
|
45
|
+
* and rendered as a bogus quantity card. Skipping matches what the legacy
|
|
46
|
+
* `quantity-extractor.ts` already does for a type it does not recognise, so all
|
|
47
|
+
* three quantity readers now agree.
|
|
48
|
+
*
|
|
49
|
+
* **Its nested quantities are dropped with it, and that is a known gap with no
|
|
50
|
+
* tracking issue behind it.** Not "tracked separately" — an earlier version of
|
|
51
|
+
* this comment said so and nothing tracked it. The children are lost in every
|
|
52
|
+
* case — #3254's fixture nests two `IfcQuantityArea` totalling 26 m² that read
|
|
53
|
+
* back as nothing — and a set whose ONLY member is a complex quantity collects
|
|
54
|
+
* nothing, so since #3261 {@link readQuantitySet} drops the whole
|
|
55
|
+
* `IfcElementQuantity` rather than reporting an empty one.
|
|
56
|
+
*
|
|
57
|
+
* The gap is deliberate rather than overlooked. Flattening the children into
|
|
58
|
+
* this list would feed new names to a dozen name-keyed consumers, and — via the
|
|
59
|
+
* mutable property view that re-writes a touched `IfcElementQuantity` from these
|
|
60
|
+
* records, and `step-property-sets.ts` which emits them as flat siblings — would
|
|
61
|
+
* permanently destroy the complex structure on the next export. Surfacing them
|
|
62
|
+
* safely needs a representation these records do not have: one that read-side
|
|
63
|
+
* consumers can see and the write-back path provably skips. Until that exists,
|
|
64
|
+
* under-reporting is the lesser harm, and this paragraph is the whole of what
|
|
65
|
+
* anyone is doing about it.
|
|
66
|
+
*
|
|
67
|
+
* An entity of a type absent from {@link QUANTITY_TYPE_MAP} still reports as a
|
|
68
|
+
* `Count`, keeping its value under a wrong label rather than vanishing. No
|
|
69
|
+
* `IfcPhysicalSimpleQuantity` subtype relies on that fallback today —
|
|
70
|
+
* `IfcQuantityNumber` (IFC4X3) did until #3266 gave it `QuantityType.Number`,
|
|
71
|
+
* and `test/quantity-type-map-coverage.test.ts` now reds if a schema declares a
|
|
72
|
+
* subtype the map has not gained. That test guards the OTHER hand-written set
|
|
73
|
+
* too: `PROPERTY_ENTITY_TYPES` in `columnar-parser-indexes.ts` decides whether
|
|
74
|
+
* the entity is retained at all, so a subtype missing THERE never reaches this
|
|
75
|
+
* map and the quantity does not exist rather than being mislabelled.
|
|
76
|
+
*/
|
|
77
|
+
export function collectQuantitiesFromRefs(store, extractor, refs) {
|
|
78
|
+
const quantities = [];
|
|
79
|
+
if (!Array.isArray(refs))
|
|
80
|
+
return quantities;
|
|
81
|
+
for (const qtyRef of refs) {
|
|
82
|
+
if (typeof qtyRef !== 'number')
|
|
83
|
+
continue;
|
|
84
|
+
const qtyEntityRef = store.entityIndex.byId.get(qtyRef) ?? store.deferredEntityIndex?.get(qtyRef);
|
|
85
|
+
if (!qtyEntityRef)
|
|
86
|
+
continue;
|
|
87
|
+
const qtyEntity = extractor.extractEntity(qtyEntityRef);
|
|
88
|
+
if (!qtyEntity)
|
|
89
|
+
continue;
|
|
90
|
+
const qtyTypeUpper = qtyEntity.type.toUpperCase();
|
|
91
|
+
if (qtyTypeUpper === COMPLEX_QUANTITY_TYPE)
|
|
92
|
+
continue;
|
|
93
|
+
const qtyAttrs = qtyEntity.attributes || [];
|
|
94
|
+
const qtyName = typeof qtyAttrs[0] === 'string' ? qtyAttrs[0] : '';
|
|
95
|
+
if (!qtyName)
|
|
96
|
+
continue;
|
|
97
|
+
const qtyType = QUANTITY_TYPE_MAP[qtyTypeUpper] ?? QuantityType.Count;
|
|
98
|
+
const rawValue = qtyAttrs[SIMPLE_QUANTITY_VALUE_SLOT];
|
|
99
|
+
const value = typeof rawValue === 'number' ? rawValue : 0;
|
|
100
|
+
quantities.push({ name: qtyName, type: qtyType, value });
|
|
101
|
+
}
|
|
102
|
+
return quantities;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* `Quantities` slot on `IfcElementQuantity`: GlobalId[0], OwnerHistory[1],
|
|
106
|
+
* Name[2], Description[3] inherited from `IfcRoot`, then MethodOfMeasurement[4]
|
|
107
|
+
* and Quantities[5].
|
|
108
|
+
*/
|
|
109
|
+
const QUANTITIES_SLOT = 5;
|
|
110
|
+
/**
|
|
111
|
+
* Read one `IfcElementQuantity` into a reportable quantity set, or `null` when
|
|
112
|
+
* it carries nothing worth reporting.
|
|
113
|
+
*
|
|
114
|
+
* `IFC4_ADD2_TC1.exp` (identically `IFC4X3.exp`):
|
|
115
|
+
*
|
|
116
|
+
* ENTITY IfcElementQuantity
|
|
117
|
+
* SUBTYPE OF (IfcQuantitySet);
|
|
118
|
+
* MethodOfMeasurement : OPTIONAL IfcLabel;
|
|
119
|
+
* Quantities : SET [1:?] OF IfcPhysicalQuantity;
|
|
120
|
+
*
|
|
121
|
+
* `SET [1:?]` admits no empty set, so a set that walks to zero quantities —
|
|
122
|
+
* written empty, or filled only with members this reader cannot report, such as
|
|
123
|
+
* an unresolvable reference or an `IfcPhysicalComplexQuantity` (#3254) — is
|
|
124
|
+
* non-conformant data. Reporting it anyway would assert "this element has
|
|
125
|
+
* quantities" on the strength of a name alone, and the consumers act on exactly
|
|
126
|
+
* that: `validate` counts the element as quantified in its quantity-completeness
|
|
127
|
+
* figure, an IDS quantity-set existence check passes, and the viewer's fallback
|
|
128
|
+
* to the element's TYPE quantities is suppressed by the phantom occurrence set,
|
|
129
|
+
* hiding the real numbers the type carries. So it is dropped (#3259).
|
|
130
|
+
*
|
|
131
|
+
* The instance path and the type path both go through here, so the drop cannot
|
|
132
|
+
* come apart between them again: it used to be inlined at each site, and the
|
|
133
|
+
* type site dropped while the instance site kept.
|
|
134
|
+
*/
|
|
135
|
+
export function readQuantitySet(store, extractor, qsetRef, qsetId) {
|
|
136
|
+
const qsetEntity = extractor.extractEntity(qsetRef);
|
|
137
|
+
if (!qsetEntity)
|
|
138
|
+
return null;
|
|
139
|
+
const qsetAttrs = qsetEntity.attributes || [];
|
|
140
|
+
const qsetName = typeof qsetAttrs[2] === 'string' ? qsetAttrs[2] : `QuantitySet #${qsetId}`;
|
|
141
|
+
const quantities = collectQuantitiesFromRefs(store, extractor, qsetAttrs[QUANTITIES_SLOT]);
|
|
142
|
+
if (quantities.length === 0)
|
|
143
|
+
return null;
|
|
144
|
+
return { name: qsetName, quantities };
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=quantity-collect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quantity-collect.js","sourceRoot":"","sources":["../src/quantity-collect.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAmBjE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,qBAAqB,GAAG,4BAA4B,CAAC;AAE3D;;;GAGG;AACH,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,UAAU,yBAAyB,CACrC,KAA0B,EAC1B,SAA0B,EAC1B,IAAa;IAEb,MAAM,UAAU,GAAwB,EAAE,CAAC;IAC3C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,UAAU,CAAC;IAE5C,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,CAAC;QACxB,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,SAAS;QAEzC,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QAClG,IAAI,CAAC,YAAY;YAAE,SAAS;QAE5B,MAAM,SAAS,GAAG,SAAS,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;QACxD,IAAI,CAAC,SAAS;YAAE,SAAS;QAEzB,MAAM,YAAY,GAAG,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAClD,IAAI,YAAY,KAAK,qBAAqB;YAAE,SAAS;QAErD,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,IAAI,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,IAAI,CAAC,OAAO;YAAE,SAAS;QAEvB,MAAM,OAAO,GAAG,iBAAiB,CAAC,YAAY,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC;QACtE,MAAM,QAAQ,GAAG,QAAQ,CAAC,0BAA0B,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAE1D,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,eAAe,GAAG,CAAC,CAAC;AAQ1B;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,eAAe,CAC3B,KAA0B,EAC1B,SAA0B,EAC1B,OAAkB,EAClB,MAAc;IAEd,MAAM,UAAU,GAAG,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACpD,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7B,MAAM,SAAS,GAAG,UAAU,CAAC,UAAU,IAAI,EAAE,CAAC;IAC9C,MAAM,QAAQ,GAAG,OAAO,SAAS,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,MAAM,EAAE,CAAC;IAC5F,MAAM,UAAU,GAAG,yBAAyB,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC;IAE3F,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AAC1C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-backend-maps.d.ts","sourceRoot":"","sources":["../src/query-backend-maps.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"query-backend-maps.d.ts","sourceRoot":"","sources":["../src/query-backend-maps.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAcjD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAWrD;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAM/D,CAAC"}
|
|
@@ -34,6 +34,10 @@ export const IFC_SUBTYPES = {
|
|
|
34
34
|
IFCMEMBER: ['IFCMEMBERSTANDARDCASE'],
|
|
35
35
|
IFCPLATE: ['IFCPLATESTANDARDCASE'],
|
|
36
36
|
IFCOPENINGELEMENT: ['IFCOPENINGSTANDARDCASE'],
|
|
37
|
+
// Not a `*StandardCase` family, and absent until #3229: IFC4 exporters write
|
|
38
|
+
// furniture as IFCFURNITURE, so `byType('IfcFurnishingElement')` answered
|
|
39
|
+
// with nothing on a model that plainly contained furniture.
|
|
40
|
+
IFCFURNISHINGELEMENT: ['IFCFURNITURE', 'IFCSYSTEMFURNITUREELEMENT'],
|
|
37
41
|
};
|
|
38
42
|
/**
|
|
39
43
|
* Expand a caller's type list to include the known IFC subtypes, uppercasing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-backend-maps.js","sourceRoot":"","sources":["../src/query-backend-maps.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,YAAY,GAA6B;IACpD,OAAO,EAAE,CAAC,qBAAqB,EAAE,sBAAsB,CAAC;IACxD,OAAO,EAAE,CAAC,qBAAqB,CAAC;IAChC,SAAS,EAAE,CAAC,uBAAuB,CAAC;IACpC,OAAO,EAAE,CAAC,qBAAqB,CAAC;IAChC,SAAS,EAAE,CAAC,uBAAuB,CAAC;IACpC,OAAO,EAAE,CAAC,qBAAqB,EAAE,sBAAsB,CAAC;IACxD,SAAS,EAAE,CAAC,uBAAuB,CAAC;IACpC,QAAQ,EAAE,CAAC,sBAAsB,CAAC;IAClC,iBAAiB,EAAE,CAAC,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"query-backend-maps.js","sourceRoot":"","sources":["../src/query-backend-maps.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,YAAY,GAA6B;IACpD,OAAO,EAAE,CAAC,qBAAqB,EAAE,sBAAsB,CAAC;IACxD,OAAO,EAAE,CAAC,qBAAqB,CAAC;IAChC,SAAS,EAAE,CAAC,uBAAuB,CAAC;IACpC,OAAO,EAAE,CAAC,qBAAqB,CAAC;IAChC,SAAS,EAAE,CAAC,uBAAuB,CAAC;IACpC,OAAO,EAAE,CAAC,qBAAqB,EAAE,sBAAsB,CAAC;IACxD,SAAS,EAAE,CAAC,uBAAuB,CAAC;IACpC,QAAQ,EAAE,CAAC,sBAAsB,CAAC;IAClC,iBAAiB,EAAE,CAAC,wBAAwB,CAAC;IAC7C,6EAA6E;IAC7E,0EAA0E;IAC1E,4DAA4D;IAC5D,oBAAoB,EAAE,CAAC,cAAc,EAAE,2BAA2B,CAAC;CACpE,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAe;IACzC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnB,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,QAAQ,EAAE,CAAC;YACb,KAAK,MAAM,GAAG,IAAI,QAAQ;gBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAqC;IAClE,iCAAiC,EAAE,gBAAgB,CAAC,gBAAgB;IACpE,gBAAgB,EAAE,gBAAgB,CAAC,UAAU;IAC7C,mBAAmB,EAAE,gBAAgB,CAAC,aAAa;IACnD,kBAAkB,EAAE,gBAAgB,CAAC,YAAY;IACjD,kBAAkB,EAAE,gBAAgB,CAAC,YAAY;CAClD,CAAC"}
|
package/dist/source-header.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* strings (e.g. `'CoordinateReference [..., ProjectSite: Origin]'`), which a
|
|
9
9
|
* splitter that ignores quote state would mis-split.
|
|
10
10
|
*/
|
|
11
|
-
import type { IfcSourceHeader } from '@ifc-lite/data';
|
|
11
|
+
import type { IfcSourceHeader, IfcStoreBase } from '@ifc-lite/data';
|
|
12
12
|
import { type IfcSourceBytes } from './source-bytes.js';
|
|
13
13
|
/**
|
|
14
14
|
* Parse the HEADER section of a STEP/IFC buffer into {@link IfcSourceHeader}.
|
|
@@ -17,4 +17,26 @@ import { type IfcSourceBytes } from './source-bytes.js';
|
|
|
17
17
|
* truncated at the first `ENDSEC` so the DATA section is never scanned.
|
|
18
18
|
*/
|
|
19
19
|
export declare function parseSourceHeader(buffer: Uint8Array | IfcSourceBytes): IfcSourceHeader | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Determine which IFC schema a STEP buffer declares (issue #3278).
|
|
22
|
+
*
|
|
23
|
+
* The `FILE_SCHEMA` declaration is authoritative and is read from the
|
|
24
|
+
* already-parsed {@link IfcSourceHeader}; free text elsewhere in the header is
|
|
25
|
+
* not. That distinction is the whole point. `FILE_DESCRIPTION` and `FILE_NAME`
|
|
26
|
+
* carry author, organisation, preprocessor and originating-system strings, and
|
|
27
|
+
* exporters routinely stamp a schema token into their product name ("SomeApp
|
|
28
|
+
* IFC4 Exporter") — which a raw substring scan of the header bytes cannot tell
|
|
29
|
+
* apart from a declaration. Reading the record also reaches declarations that
|
|
30
|
+
* sit past the first 2 KB: ISO 10303-21 puts `FILE_SCHEMA` *after* `FILE_NAME`,
|
|
31
|
+
* and a long author or organisation list pushes it out of a small fixed window.
|
|
32
|
+
*
|
|
33
|
+
* Free on the hot path: {@link parseSourceHeader} already runs on every parse,
|
|
34
|
+
* so nothing extra is scanned. The raw decode below now happens only for a file
|
|
35
|
+
* that declares no schema at all.
|
|
36
|
+
*
|
|
37
|
+
* When no `FILE_SCHEMA` identifier resolves, fall back to the historical raw
|
|
38
|
+
* scan of the first 2000 bytes rather than refusing, so every file that
|
|
39
|
+
* resolves today keeps resolving the same way.
|
|
40
|
+
*/
|
|
41
|
+
export declare function detectSchemaVersion(buffer: Uint8Array | IfcSourceBytes, header: IfcSourceHeader | undefined): IfcStoreBase['schemaVersion'];
|
|
20
42
|
//# sourceMappingURL=source-header.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-header.d.ts","sourceRoot":"","sources":["../src/source-header.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"source-header.d.ts","sourceRoot":"","sources":["../src/source-header.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGpE,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AA4JvE;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,UAAU,GAAG,cAAc,GAClC,eAAe,GAAG,SAAS,CA2D7B;AA8BD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,GAAG,cAAc,EACnC,MAAM,EAAE,eAAe,GAAG,SAAS,GAClC,YAAY,CAAC,eAAe,CAAC,CAuB/B"}
|
package/dist/source-header.js
CHANGED
|
@@ -3,38 +3,33 @@
|
|
|
3
3
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
4
|
import { decodeStepStringLiteral } from '@ifc-lite/encoding';
|
|
5
5
|
import { asSourceBytes } from './source-bytes.js';
|
|
6
|
+
import { matchesKeywordAt, StepTextScan } from './step-lexing.js';
|
|
6
7
|
/** Headers are tiny; cap the decode so a huge file's body is never scanned. */
|
|
7
8
|
const MAX_HEADER_BYTES = 64 * 1024;
|
|
8
9
|
/**
|
|
9
10
|
* Split STEP record arguments at top-level commas, respecting paren/bracket
|
|
10
|
-
* nesting
|
|
11
|
-
* still-escaped argument substrings (trimmed).
|
|
11
|
+
* nesting, single-quoted strings (with `''` escapes) and comments. Returns the
|
|
12
|
+
* raw, still-escaped argument substrings (trimmed).
|
|
13
|
+
*
|
|
14
|
+
* A comment is dropped rather than copied through: it is not part of the
|
|
15
|
+
* argument's value, and its commas are not separators.
|
|
12
16
|
*/
|
|
13
17
|
function splitTopLevel(inner) {
|
|
14
18
|
const args = [];
|
|
15
19
|
let depth = 0;
|
|
16
|
-
let inString = false;
|
|
17
20
|
let current = '';
|
|
21
|
+
const scan = new StepTextScan(inner);
|
|
18
22
|
for (let i = 0; i < inner.length; i++) {
|
|
19
|
-
const
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
i++;
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
inString = false;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
23
|
+
const skip = scan.skipLexicalAt(i);
|
|
24
|
+
if (skip >= 0) {
|
|
25
|
+
// A literal is part of the argument's text; a comment is not.
|
|
26
|
+
if (inner[i] === "'")
|
|
27
|
+
current += inner.slice(i, skip);
|
|
28
|
+
i = skip - 1;
|
|
31
29
|
continue;
|
|
32
30
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
current += ch;
|
|
36
|
-
}
|
|
37
|
-
else if (ch === '(' || ch === '[') {
|
|
31
|
+
const ch = inner[i];
|
|
32
|
+
if (ch === '(' || ch === '[') {
|
|
38
33
|
depth++;
|
|
39
34
|
current += ch;
|
|
40
35
|
}
|
|
@@ -111,38 +106,56 @@ function decodeStringList(arg) {
|
|
|
111
106
|
.filter((v) => v !== undefined);
|
|
112
107
|
}
|
|
113
108
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
109
|
+
* Index of `keyword` occurring as a RECORD, outside any string or comment, or
|
|
110
|
+
* -1.
|
|
111
|
+
*
|
|
112
|
+
* A plain `indexOf` is not enough here and the reason is the same one #3278 is
|
|
113
|
+
* about, one level down: header FREE TEXT is not a declaration. STEP strings
|
|
114
|
+
* are single-quoted with `''` as the escape, and a `FILE_DESCRIPTION` item is
|
|
115
|
+
* free to contain the literal text `FILE_SCHEMA(('IFC2X3'))` -- an exporter
|
|
116
|
+
* stamping its own header into a description, a file round-tripped through a
|
|
117
|
+
* tool that quotes what it read. `indexOf` would take that quoted copy as the
|
|
118
|
+
* declaration and answer IFC2X3 for an IFC4X3 file. The same applies to
|
|
119
|
+
* `ENDSEC`: a quoted one would truncate the header before the real
|
|
120
|
+
* `FILE_SCHEMA` record, losing the declaration entirely.
|
|
117
121
|
*/
|
|
118
|
-
function
|
|
119
|
-
const
|
|
120
|
-
|
|
122
|
+
function indexOfRecord(text, keyword) {
|
|
123
|
+
const scan = new StepTextScan(text);
|
|
124
|
+
for (let i = 0; i < text.length; i++) {
|
|
125
|
+
const skip = scan.skipLexicalAt(i);
|
|
126
|
+
if (skip >= 0) {
|
|
127
|
+
i = skip - 1;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (matchesKeywordAt(text, i, keyword))
|
|
131
|
+
return i;
|
|
132
|
+
}
|
|
133
|
+
return -1;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Extract the argument substring inside the parentheses of `KEYWORD( ... )`.
|
|
137
|
+
* Quote-, comment- and nesting-aware so a quoted
|
|
138
|
+
* `)` never closes the record early, and so a quoted KEYWORD is never mistaken
|
|
139
|
+
* for the record itself. Returns `null` if not found.
|
|
140
|
+
*/
|
|
141
|
+
function extractRecordArgs(text, keyword) {
|
|
142
|
+
const at = indexOfRecord(text, keyword);
|
|
121
143
|
if (at < 0)
|
|
122
144
|
return null;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
i++;
|
|
145
|
+
const scan = new StepTextScan(text);
|
|
146
|
+
let i = scan.skipTrivia(at + keyword.length);
|
|
126
147
|
if (text[i] !== '(')
|
|
127
148
|
return null;
|
|
128
149
|
const start = i;
|
|
129
150
|
let depth = 0;
|
|
130
|
-
let inString = false;
|
|
131
151
|
for (; i < text.length; i++) {
|
|
132
|
-
const
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
if (text[i + 1] === "'")
|
|
136
|
-
i++;
|
|
137
|
-
else
|
|
138
|
-
inString = false;
|
|
139
|
-
}
|
|
152
|
+
const skip = scan.skipLexicalAt(i);
|
|
153
|
+
if (skip >= 0) {
|
|
154
|
+
i = skip - 1;
|
|
140
155
|
continue;
|
|
141
156
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
else if (ch === '(') {
|
|
157
|
+
const ch = text[i];
|
|
158
|
+
if (ch === '(') {
|
|
146
159
|
depth++;
|
|
147
160
|
}
|
|
148
161
|
else if (ch === ')') {
|
|
@@ -163,7 +176,7 @@ export function parseSourceHeader(buffer) {
|
|
|
163
176
|
const src = asSourceBytes(buffer);
|
|
164
177
|
const cap = Math.min(src.byteLength, MAX_HEADER_BYTES);
|
|
165
178
|
let text = src.decodeUtf8(0, cap);
|
|
166
|
-
const endSec = text
|
|
179
|
+
const endSec = indexOfRecord(text, 'ENDSEC');
|
|
167
180
|
if (endSec >= 0)
|
|
168
181
|
text = text.slice(0, endSec);
|
|
169
182
|
const descRecord = extractRecordArgs(text, 'FILE_DESCRIPTION');
|
|
@@ -216,4 +229,81 @@ export function parseSourceHeader(buffer) {
|
|
|
216
229
|
schemaIdentifiers,
|
|
217
230
|
};
|
|
218
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Resolve one `FILE_SCHEMA` identifier to the schema version a store carries.
|
|
234
|
+
*
|
|
235
|
+
* Matched by PREFIX, longest first: the spellings that reach us in the wild
|
|
236
|
+
* carry addendum/corrigendum suffixes (`IFC4X3_ADD2`, `IFC4X1`, `IFC2X3_TC1`),
|
|
237
|
+
* and `IFC4X3` itself begins with `IFC4`, so the `IFC4X3` branch has to be
|
|
238
|
+
* tried before the `IFC4` one. Returns `undefined` for an identifier naming no
|
|
239
|
+
* schema we model, so the caller can keep looking.
|
|
240
|
+
*/
|
|
241
|
+
function schemaFromIdentifier(identifier) {
|
|
242
|
+
const token = identifier.trim().toUpperCase();
|
|
243
|
+
if (token.startsWith('IFC5'))
|
|
244
|
+
return 'IFC5';
|
|
245
|
+
if (token.startsWith('IFC4X3'))
|
|
246
|
+
return 'IFC4X3';
|
|
247
|
+
if (token.startsWith('IFC4'))
|
|
248
|
+
return 'IFC4';
|
|
249
|
+
if (token.startsWith('IFC2X3'))
|
|
250
|
+
return 'IFC2X3';
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
/** Upper-case the ASCII letters and nothing else. See `detectSchemaVersion`. */
|
|
254
|
+
function asciiUpper(text) {
|
|
255
|
+
let out = '';
|
|
256
|
+
for (let i = 0; i < text.length; i++) {
|
|
257
|
+
const c = text.charCodeAt(i);
|
|
258
|
+
out += c >= 97 && c <= 122 ? String.fromCharCode(c - 32) : text[i];
|
|
259
|
+
}
|
|
260
|
+
return out;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Determine which IFC schema a STEP buffer declares (issue #3278).
|
|
264
|
+
*
|
|
265
|
+
* The `FILE_SCHEMA` declaration is authoritative and is read from the
|
|
266
|
+
* already-parsed {@link IfcSourceHeader}; free text elsewhere in the header is
|
|
267
|
+
* not. That distinction is the whole point. `FILE_DESCRIPTION` and `FILE_NAME`
|
|
268
|
+
* carry author, organisation, preprocessor and originating-system strings, and
|
|
269
|
+
* exporters routinely stamp a schema token into their product name ("SomeApp
|
|
270
|
+
* IFC4 Exporter") — which a raw substring scan of the header bytes cannot tell
|
|
271
|
+
* apart from a declaration. Reading the record also reaches declarations that
|
|
272
|
+
* sit past the first 2 KB: ISO 10303-21 puts `FILE_SCHEMA` *after* `FILE_NAME`,
|
|
273
|
+
* and a long author or organisation list pushes it out of a small fixed window.
|
|
274
|
+
*
|
|
275
|
+
* Free on the hot path: {@link parseSourceHeader} already runs on every parse,
|
|
276
|
+
* so nothing extra is scanned. The raw decode below now happens only for a file
|
|
277
|
+
* that declares no schema at all.
|
|
278
|
+
*
|
|
279
|
+
* When no `FILE_SCHEMA` identifier resolves, fall back to the historical raw
|
|
280
|
+
* scan of the first 2000 bytes rather than refusing, so every file that
|
|
281
|
+
* resolves today keeps resolving the same way.
|
|
282
|
+
*/
|
|
283
|
+
export function detectSchemaVersion(buffer, header) {
|
|
284
|
+
for (const identifier of header?.schemaIdentifiers ?? []) {
|
|
285
|
+
const version = schemaFromIdentifier(identifier);
|
|
286
|
+
if (version !== undefined)
|
|
287
|
+
return version;
|
|
288
|
+
}
|
|
289
|
+
const src = asSourceBytes(buffer);
|
|
290
|
+
const headerEnd = Math.min(src.byteLength, 2000);
|
|
291
|
+
// ASCII-only, for the same reason `matchesKeywordAt` is. `toUpperCase()`
|
|
292
|
+
// maps `ı` (dotless i) to `I`, so a FILE_DESCRIPTION mentioning `ıFC5` chose
|
|
293
|
+
// IFC5 for a file that never said so. This scan is already a loose
|
|
294
|
+
// last-resort substring match -- it only runs when no FILE_SCHEMA identifier
|
|
295
|
+
// resolved at all -- but loose is not a reason to accept a fold 10303-21
|
|
296
|
+
// does not use. Offsets are not taken from this copy, so a copy is fine here
|
|
297
|
+
// where it was not in the record scan.
|
|
298
|
+
const headerText = asciiUpper(src.decodeUtf8(0, headerEnd));
|
|
299
|
+
if (headerText.includes('IFC5'))
|
|
300
|
+
return 'IFC5';
|
|
301
|
+
if (headerText.includes('IFC4X3'))
|
|
302
|
+
return 'IFC4X3';
|
|
303
|
+
if (headerText.includes('IFC4'))
|
|
304
|
+
return 'IFC4';
|
|
305
|
+
if (headerText.includes('IFC2X3'))
|
|
306
|
+
return 'IFC2X3';
|
|
307
|
+
return 'IFC4'; // Default fallback
|
|
308
|
+
}
|
|
219
309
|
//# sourceMappingURL=source-header.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-header.js","sourceRoot":"","sources":["../src/source-header.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAc/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,OAAO,EAAE,aAAa,EAAuB,MAAM,mBAAmB,CAAC;AAEvE,+EAA+E;AAC/E,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,CAAC;AAEnC
|
|
1
|
+
{"version":3,"file":"source-header.js","sourceRoot":"","sources":["../src/source-header.ts"],"names":[],"mappings":"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"}
|
package/dist/step-lexing.d.ts
CHANGED
|
@@ -9,4 +9,96 @@ export interface Skip {
|
|
|
9
9
|
export declare function skipLexical(buf: Uint8Array, pos: number, len: number): Skip;
|
|
10
10
|
export declare function opensLiteralOrComment(buf: Uint8Array, pos: number, len: number): boolean;
|
|
11
11
|
export declare function countNewlines(buf: Uint8Array, from: number, to: number): number;
|
|
12
|
+
/**
|
|
13
|
+
* A scan of one decoded string.
|
|
14
|
+
*
|
|
15
|
+
* The buffer and the scan state are bound together on purpose. The state is a
|
|
16
|
+
* memo: once a `*\/` search from some `i` fails, no closer exists at or after
|
|
17
|
+
* any later position either, so no later `/*` can open a comment. Every scan
|
|
18
|
+
* here moves its index forward monotonically, so AT MOST ONE closer search can
|
|
19
|
+
* ever fail, and every other one succeeds and consumes a span disjoint from the
|
|
20
|
+
* rest. That is what keeps the whole thing linear.
|
|
21
|
+
*
|
|
22
|
+
* Two earlier shapes were both wrong, and the second was mine:
|
|
23
|
+
*
|
|
24
|
+
* - searching for the closer at every `/*` is QUADRATIC. A failing search
|
|
25
|
+
* runs to the end of the text, the caller advances one character, and the
|
|
26
|
+
* next `/*` repeats it. A 64 KiB header holding 21000 unterminated `/*`
|
|
27
|
+
* took 5.7 seconds.
|
|
28
|
+
* - hoisting `lastIndexOf('*\/')` to the top of each scan fixes that but
|
|
29
|
+
* makes every scan pay a full pass over the buffer even when the input is
|
|
30
|
+
* well formed and the answer is 100 bytes in. The Rust twin is handed whole
|
|
31
|
+
* uncapped files, where that measured 250ns -> 52ms on 200 MB.
|
|
32
|
+
*
|
|
33
|
+
* Deferring the search until a `/*` is actually seen, and remembering the one
|
|
34
|
+
* failure, costs nothing on well-formed input and stays linear on hostile
|
|
35
|
+
* input.
|
|
36
|
+
*
|
|
37
|
+
* Binding the buffer to the state also removes an invariant that was previously
|
|
38
|
+
* only checkable by hand: the old free functions took the closer position as an
|
|
39
|
+
* argument, so passing one derived from a DIFFERENT string compiled and
|
|
40
|
+
* silently mis-scanned.
|
|
41
|
+
*/
|
|
42
|
+
export declare class StepTextScan {
|
|
43
|
+
private readonly text;
|
|
44
|
+
private searchCount;
|
|
45
|
+
private noCloser;
|
|
46
|
+
/** `*\/` searches performed. At most one of them can fail; see the class doc. */
|
|
47
|
+
get searches(): number;
|
|
48
|
+
constructor(text: string);
|
|
49
|
+
/**
|
|
50
|
+
* If a `/* ... *\/` comment starts at `text[i]`, the index just past it.
|
|
51
|
+
* Otherwise -1.
|
|
52
|
+
*
|
|
53
|
+
* An UNTERMINATED `/*` is not a comment. Running it to end-of-text would
|
|
54
|
+
* swallow every record after it and lose the whole header, which is worse
|
|
55
|
+
* than the malformed input deserves and worse than doing nothing. Treating it
|
|
56
|
+
* as ordinary text costs only that one `/`, and the scan still advances.
|
|
57
|
+
*/
|
|
58
|
+
private skipCommentAt;
|
|
59
|
+
/**
|
|
60
|
+
* If a string literal or a comment starts at `text[i]`, the index just past
|
|
61
|
+
* it. Otherwise -1.
|
|
62
|
+
*
|
|
63
|
+
* Neither carries structure: a keyword, a comma or a bracket inside one is
|
|
64
|
+
* text. Every scan in `source-header.ts` has to agree on that, so it is one
|
|
65
|
+
* function rather than a copy per loop. #3284 was one file holding two rules
|
|
66
|
+
* for the same thing; three private copies of this loop is that shape again.
|
|
67
|
+
*
|
|
68
|
+
* The result always ADVANCES the caller's index. An earlier form returned the
|
|
69
|
+
* `*\/` offset and left -1 to mean unterminated, which a caller adding 1 to
|
|
70
|
+
* turns into 0: the scan restarts at the top, finds the same comment, and
|
|
71
|
+
* never terminates.
|
|
72
|
+
*
|
|
73
|
+
* The two unterminated cases are deliberately not symmetric. An unterminated
|
|
74
|
+
* literal runs to end-of-text, because a lone `'` cannot be read as ordinary
|
|
75
|
+
* text. An unterminated `/*` is simply not a comment, because it can. This is
|
|
76
|
+
* also where this half differs from `skipLexical` above; see the divider.
|
|
77
|
+
*/
|
|
78
|
+
skipLexicalAt(i: number): number;
|
|
79
|
+
/**
|
|
80
|
+
* Advance past whitespace and comments. ISO 10303-21 allows a comment
|
|
81
|
+
* wherever whitespace is allowed, including between a record keyword and its
|
|
82
|
+
* `(`.
|
|
83
|
+
*
|
|
84
|
+
* ASCII whitespace only, which is what 10303-21 means. `/\s/` also matches
|
|
85
|
+
* U+00A0 and the other Unicode space separators, while the Rust half tests
|
|
86
|
+
* bytes, so the two halves disagreed on `FILE_SCHEMA (('IFC2X3'))`.
|
|
87
|
+
*/
|
|
88
|
+
skipTrivia(i: number): number;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Case-insensitive ASCII compare of `keyword` against `text` at `i`. Callers
|
|
92
|
+
* pass upper-case keywords.
|
|
93
|
+
*
|
|
94
|
+
* Compares character by character instead of uppercasing a copy of `text`,
|
|
95
|
+
* because a copy cannot be indexed into. `'ß'.toUpperCase()` is two characters,
|
|
96
|
+
* so one of them shifts every later offset, and the caller then slices the
|
|
97
|
+
* ORIGINAL text with an offset measured in the copy.
|
|
98
|
+
*
|
|
99
|
+
* ASCII-only on purpose. That is what ISO 10303-21 case-insensitivity means,
|
|
100
|
+
* and it declines the matches a full Unicode fold accepts: `'ſ'` (long s)
|
|
101
|
+
* uppercases to `'S'`, so a full fold reads ` ENDſEC` as `ENDSEC`.
|
|
102
|
+
*/
|
|
103
|
+
export declare function matchesKeywordAt(text: string, i: number, keyword: string): boolean;
|
|
12
104
|
//# sourceMappingURL=step-lexing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step-lexing.d.ts","sourceRoot":"","sources":["../src/step-lexing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"step-lexing.d.ts","sourceRoot":"","sources":["../src/step-lexing.ts"],"names":[],"mappings":"AAyBA,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/E;AAyCD,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAO7E;AAWD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAanF;AAED,MAAM,WAAW,IAAI;IAEnB,IAAI,EAAE,MAAM,CAAC;IAEb,KAAK,EAAE,MAAM,CAAC;IAGd,IAAI,EAAE,OAAO,CAAC;CACf;AAKD,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAU3E;AAGD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAExF;AAGD,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAM/E;AAmBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,YAAY;IASX,OAAO,CAAC,QAAQ,CAAC,IAAI;IARjC,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAAS;IAEzB,iFAAiF;IACjF,IAAI,QAAQ,IAAI,MAAM,CAErB;gBAE4B,IAAI,EAAE,MAAM;IAEzC;;;;;;;;OAQG;IACH,OAAO,CAAC,aAAa;IAarB;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM;IAahC;;;;;;;;OAQG;IACH,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM;CAS9B;AAOD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAOlF"}
|