@ifc-lite/codegen 1.15.3 → 1.15.5

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.
Files changed (41) hide show
  1. package/README.md +17 -14
  2. package/dist/serialization-generator.d.ts +7 -2
  3. package/dist/serialization-generator.d.ts.map +1 -1
  4. package/dist/serialization-generator.js +30 -301
  5. package/dist/serialization-generator.js.map +1 -1
  6. package/package.json +18 -7
  7. package/.turbo/turbo-build.log +0 -4
  8. package/CHANGELOG.md +0 -117
  9. package/INTEGRATION.md +0 -354
  10. package/generated/ifc4/entities.ts +0 -7400
  11. package/generated/ifc4/enums.ts +0 -2467
  12. package/generated/ifc4/index.ts +0 -17
  13. package/generated/ifc4/schema-registry.ts +0 -63186
  14. package/generated/ifc4/selects.ts +0 -315
  15. package/generated/ifc4/serializers.ts +0 -322
  16. package/generated/ifc4/test-compile.ts +0 -49
  17. package/generated/ifc4/type-ids.ts +0 -1682
  18. package/generated/ifc4/types.ts +0 -401
  19. package/generated/ifc4x3/entities.ts +0 -8300
  20. package/generated/ifc4x3/enums.ts +0 -3096
  21. package/generated/ifc4x3/index.ts +0 -17
  22. package/generated/ifc4x3/schema-registry.ts +0 -71489
  23. package/generated/ifc4x3/selects.ts +0 -312
  24. package/generated/ifc4x3/serializers.ts +0 -322
  25. package/generated/ifc4x3/test-compile.ts +0 -49
  26. package/generated/ifc4x3/type-ids.ts +0 -1882
  27. package/generated/ifc4x3/types.ts +0 -407
  28. package/src/cli.ts +0 -72
  29. package/src/crc32.ts +0 -77
  30. package/src/express-parser.ts +0 -457
  31. package/src/generator.ts +0 -268
  32. package/src/index.ts +0 -57
  33. package/src/rust-generator.ts +0 -714
  34. package/src/serialization-generator.ts +0 -343
  35. package/src/type-ids-generator.ts +0 -166
  36. package/src/typescript-generator.ts +0 -631
  37. package/test/express-parser.test.ts +0 -363
  38. package/test/integration.test.ts +0 -246
  39. package/test/typescript-generator.test.ts +0 -348
  40. package/tsconfig.json +0 -21
  41. package/vitest.config.ts +0 -18
@@ -1,457 +0,0 @@
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
- /**
6
- * EXPRESS Schema Parser
7
- *
8
- * Parses IFC EXPRESS schemas (.exp files) into an AST for code generation.
9
- *
10
- * EXPRESS is a data modeling language (ISO 10303-11) used to define IFC schemas.
11
- * We parse the subset needed for TypeScript code generation:
12
- * - ENTITY definitions (with attributes, inheritance, WHERE clauses)
13
- * - TYPE definitions (simple types, enumerations, selects)
14
- * - Comments
15
- */
16
-
17
- export interface ExpressSchema {
18
- name: string;
19
- entities: EntityDefinition[];
20
- types: TypeDefinition[];
21
- enums: EnumDefinition[];
22
- selects: SelectDefinition[];
23
- }
24
-
25
- export interface EntityDefinition {
26
- name: string;
27
- isAbstract: boolean;
28
- supertype?: string; // SUBTYPE OF
29
- supertypeOf?: string[]; // SUPERTYPE OF (subtypes)
30
- attributes: AttributeDefinition[];
31
- derived?: DerivedAttribute[];
32
- inverse?: InverseAttribute[];
33
- whereRules?: string[];
34
- uniqueRules?: string[];
35
- }
36
-
37
- export interface AttributeDefinition {
38
- name: string;
39
- type: string;
40
- optional: boolean;
41
- isArray: boolean;
42
- isList: boolean;
43
- isSet: boolean;
44
- arrayBounds?: [number, number]; // [min, max] or [exact, exact]
45
- }
46
-
47
- export interface DerivedAttribute {
48
- name: string;
49
- type: string;
50
- expression: string;
51
- }
52
-
53
- export interface InverseAttribute {
54
- name: string;
55
- type: string;
56
- reference: string;
57
- for: string;
58
- }
59
-
60
- export interface TypeDefinition {
61
- name: string;
62
- underlyingType: string; // e.g., "REAL", "STRING", "INTEGER"
63
- whereRules?: string[];
64
- }
65
-
66
- export interface EnumDefinition {
67
- name: string;
68
- values: string[];
69
- }
70
-
71
- export interface SelectDefinition {
72
- name: string;
73
- types: string[]; // Union of types
74
- }
75
-
76
- /**
77
- * Parse an EXPRESS schema file
78
- */
79
- export function parseExpressSchema(content: string): ExpressSchema {
80
- // Remove comments
81
- content = removeComments(content);
82
-
83
- // Extract schema name
84
- const schemaMatch = content.match(/SCHEMA\s+(\w+)\s*;/);
85
- const schemaName = schemaMatch ? schemaMatch[1] : 'UNKNOWN';
86
-
87
- // Parse different constructs
88
- const entities = parseEntities(content);
89
- const types = parseTypes(content);
90
- const enums = parseEnums(content);
91
- const selects = parseSelects(content);
92
-
93
- return {
94
- name: schemaName,
95
- entities,
96
- types,
97
- enums,
98
- selects,
99
- };
100
- }
101
-
102
- /**
103
- * Remove (* ... *) comments from EXPRESS
104
- */
105
- function removeComments(content: string): string {
106
- // Remove multi-line comments (* ... *)
107
- return content.replace(/\(\*[\s\S]*?\*\)/g, '');
108
- }
109
-
110
- /**
111
- * Parse ENTITY definitions
112
- */
113
- function parseEntities(content: string): EntityDefinition[] {
114
- const entities: EntityDefinition[] = [];
115
-
116
- // Match ENTITY ... END_ENTITY blocks
117
- const entityRegex = /ENTITY\s+(\w+)([\s\S]*?)END_ENTITY\s*;/g;
118
-
119
- let match;
120
- while ((match = entityRegex.exec(content)) !== null) {
121
- const entityName = match[1];
122
- const entityBody = match[2];
123
-
124
- entities.push(parseEntity(entityName, entityBody));
125
- }
126
-
127
- return entities;
128
- }
129
-
130
- /**
131
- * Parse a single ENTITY definition
132
- */
133
- function parseEntity(name: string, body: string): EntityDefinition {
134
- const entity: EntityDefinition = {
135
- name,
136
- isAbstract: false,
137
- attributes: [],
138
- };
139
-
140
- // Check if abstract
141
- if (body.includes('ABSTRACT')) {
142
- entity.isAbstract = true;
143
- }
144
-
145
- // Parse SUBTYPE OF
146
- const subtypeMatch = body.match(/SUBTYPE\s+OF\s+\((\w+)\)/);
147
- if (subtypeMatch) {
148
- entity.supertype = subtypeMatch[1];
149
- }
150
-
151
- // Parse SUPERTYPE OF (list of subtypes)
152
- const supertypeMatch = body.match(/SUPERTYPE\s+OF\s+\(ONEOF\s*\(([\s\S]*?)\)\)/);
153
- if (supertypeMatch) {
154
- entity.supertypeOf = supertypeMatch[1]
155
- .split(',')
156
- .map(s => s.trim())
157
- .filter(s => s.length > 0);
158
- }
159
-
160
- // Extract just the attributes section (before WHERE, DERIVE, INVERSE, UNIQUE)
161
- let attributesSection = body;
162
-
163
- // Find where attributes section ends
164
- // Match only section-level keywords at line start (not inside attribute types, e.g. "OF UNIQUE")
165
- const sectionMatches = [
166
- body.match(/^\s*WHERE\b/m),
167
- body.match(/^\s*DERIVE\b/m),
168
- body.match(/^\s*INVERSE\b/m),
169
- body.match(/^\s*UNIQUE\b/m),
170
- ].filter(m => m !== null) as RegExpMatchArray[];
171
-
172
- if (sectionMatches.length > 0) {
173
- // Find the earliest match
174
- const earliestIndex = Math.min(...sectionMatches.map(m => m.index!));
175
- attributesSection = body.substring(0, earliestIndex);
176
- }
177
-
178
- // Parse attributes (lines with : between name and type)
179
- // Format: AttributeName : [OPTIONAL] Type;
180
- const attributeRegex = /^\s*(\w+)\s*:\s*(OPTIONAL\s+)?([\s\S]*?);/gm;
181
-
182
- let attrMatch;
183
- while ((attrMatch = attributeRegex.exec(attributesSection)) !== null) {
184
- const attrName = attrMatch[1];
185
- const optional = !!attrMatch[2];
186
- const typeStr = attrMatch[3].trim();
187
-
188
- entity.attributes.push(parseAttribute(attrName, typeStr, optional));
189
- }
190
-
191
- // Parse WHERE rules
192
- const whereMatch = body.match(/WHERE([\s\S]*?)(?:UNIQUE|DERIVE|INVERSE|END_ENTITY|$)/);
193
- if (whereMatch) {
194
- entity.whereRules = whereMatch[1]
195
- .split(';')
196
- .map(s => s.trim())
197
- .filter(s => s.length > 0);
198
- }
199
-
200
- // Parse UNIQUE rules
201
- const uniqueMatch = body.match(/UNIQUE([\s\S]*?)(?:WHERE|DERIVE|INVERSE|END_ENTITY|$)/);
202
- if (uniqueMatch) {
203
- entity.uniqueRules = uniqueMatch[1]
204
- .split(';')
205
- .map(s => s.trim())
206
- .filter(s => s.length > 0);
207
- }
208
-
209
- return entity;
210
- }
211
-
212
- /**
213
- * Parse an attribute definition
214
- */
215
- function parseAttribute(name: string, typeStr: string, optional: boolean): AttributeDefinition {
216
- const attr: AttributeDefinition = {
217
- name,
218
- type: '',
219
- optional,
220
- isArray: false,
221
- isList: false,
222
- isSet: false,
223
- };
224
-
225
- // Check for aggregation types: LIST, ARRAY, SET
226
- // Match patterns like: LIST [1:?] OF Type, ARRAY [1:3] OF REAL, SET [0:?] OF Label
227
- // Also handle nested collections: LIST [2:?] OF LIST [2:?] OF IfcCartesianPoint
228
- if (typeStr.includes('LIST')) {
229
- attr.isList = true;
230
- const listMatch = typeStr.match(/LIST\s+\[(\d+|\?):(\d+|\?)\]\s+OF\s+(.*)/);
231
- if (listMatch) {
232
- attr.arrayBounds = [
233
- listMatch[1] === '?' ? Infinity : parseInt(listMatch[1]),
234
- listMatch[2] === '?' ? Infinity : parseInt(listMatch[2])
235
- ];
236
- const innerType = listMatch[3].trim();
237
- // Recursively parse nested collection
238
- attr.type = parseNestedCollection(innerType);
239
- } else {
240
- // Fallback if regex doesn't match
241
- attr.type = typeStr.replace(/LIST\s+\[.*?\]\s+OF\s+/, '').trim();
242
- }
243
- } else if (typeStr.includes('ARRAY')) {
244
- attr.isArray = true;
245
- const arrayMatch = typeStr.match(/ARRAY\s+\[(\d+|\?):(\d+|\?)\]\s+OF\s+(.*)/);
246
- if (arrayMatch) {
247
- attr.arrayBounds = [
248
- arrayMatch[1] === '?' ? Infinity : parseInt(arrayMatch[1]),
249
- arrayMatch[2] === '?' ? Infinity : parseInt(arrayMatch[2])
250
- ];
251
- const innerType = arrayMatch[3].trim();
252
- // Recursively parse nested collection
253
- attr.type = parseNestedCollection(innerType);
254
- } else {
255
- // Fallback if regex doesn't match
256
- attr.type = typeStr.replace(/ARRAY\s+\[.*?\]\s+OF\s+/, '').trim();
257
- }
258
- } else if (typeStr.includes('SET')) {
259
- attr.isSet = true;
260
- const setMatch = typeStr.match(/SET\s+\[(\d+|\?):(\d+|\?)\]\s+OF\s+(.*)/);
261
- if (setMatch) {
262
- attr.arrayBounds = [
263
- setMatch[1] === '?' ? Infinity : parseInt(setMatch[1]),
264
- setMatch[2] === '?' ? Infinity : parseInt(setMatch[2])
265
- ];
266
- const innerType = setMatch[3].trim();
267
- // Recursively parse nested collection
268
- attr.type = parseNestedCollection(innerType);
269
- } else {
270
- // Fallback if regex doesn't match
271
- attr.type = typeStr.replace(/SET\s+\[.*?\]\s+OF\s+/, '').trim();
272
- }
273
- } else {
274
- attr.type = typeStr;
275
- }
276
-
277
- return attr;
278
- }
279
-
280
- /**
281
- * Parse nested collection types
282
- * e.g., "LIST [2:?] OF IfcCartesianPoint" -> "IfcCartesianPoint[]"
283
- */
284
- function parseNestedCollection(typeStr: string): string {
285
- // Check if this is another collection
286
- if (typeStr.match(/^(LIST|ARRAY|SET)\s+\[/)) {
287
- // Match: LIST|ARRAY|SET [min:max] OF <innerType>
288
- const match = typeStr.match(/^(?:LIST|ARRAY|SET)\s+\[(?:\d+|\?):(?:\d+|\?)\]\s+OF\s+(.*)/);
289
- if (match) {
290
- const innerType = match[1].trim();
291
- // Recursively parse and wrap in array
292
- return `${parseNestedCollection(innerType)}[]`;
293
- }
294
- }
295
-
296
- // Base case: return the type as-is
297
- return typeStr;
298
- }
299
-
300
- /**
301
- * Parse TYPE definitions (non-enum, non-select)
302
- */
303
- function parseTypes(content: string): TypeDefinition[] {
304
- const types: TypeDefinition[] = [];
305
-
306
- // Match TYPE ... END_TYPE blocks (exclude ENUMERATION and SELECT)
307
- const typeRegex = /TYPE\s+(\w+)\s*=\s*(?!ENUMERATION|SELECT)([\s\S]*?)END_TYPE\s*;/g;
308
-
309
- let match;
310
- while ((match = typeRegex.exec(content)) !== null) {
311
- const typeName = match[1];
312
- const typeBody = match[2].trim();
313
-
314
- // Extract underlying type (first word before semicolon or WHERE)
315
- const underlyingMatch = typeBody.match(/^([^;]*?)(?:;|WHERE|$)/);
316
- const underlyingType = underlyingMatch ? underlyingMatch[1].trim() : 'STRING';
317
-
318
- // Parse WHERE rules if present
319
- const whereMatch = typeBody.match(/WHERE([\s\S]*?)$/);
320
- const whereRules = whereMatch
321
- ? whereMatch[1]
322
- .split(';')
323
- .map(s => s.trim())
324
- .filter(s => s.length > 0)
325
- : undefined;
326
-
327
- types.push({
328
- name: typeName,
329
- underlyingType,
330
- whereRules,
331
- });
332
- }
333
-
334
- return types;
335
- }
336
-
337
- /**
338
- * Parse ENUMERATION types
339
- */
340
- function parseEnums(content: string): EnumDefinition[] {
341
- const enums: EnumDefinition[] = [];
342
-
343
- // Match TYPE ... = ENUMERATION OF (...) blocks
344
- const enumRegex = /TYPE\s+(\w+)\s*=\s*ENUMERATION\s+OF\s*\(([\s\S]*?)\)\s*;[\s\S]*?END_TYPE\s*;/g;
345
-
346
- let match;
347
- while ((match = enumRegex.exec(content)) !== null) {
348
- const enumName = match[1];
349
- const valuesStr = match[2];
350
-
351
- // Parse enum values (comma-separated, may have line breaks)
352
- const values = valuesStr
353
- .split(',')
354
- .map(s => s.trim())
355
- .filter(s => s.length > 0);
356
-
357
- enums.push({
358
- name: enumName,
359
- values,
360
- });
361
- }
362
-
363
- return enums;
364
- }
365
-
366
- /**
367
- * Parse SELECT types (union types)
368
- */
369
- function parseSelects(content: string): SelectDefinition[] {
370
- const selects: SelectDefinition[] = [];
371
-
372
- // Match TYPE ... = SELECT (...) blocks
373
- const selectRegex = /TYPE\s+(\w+)\s*=\s*SELECT\s*\(([\s\S]*?)\)\s*;[\s\S]*?END_TYPE\s*;/g;
374
-
375
- let match;
376
- while ((match = selectRegex.exec(content)) !== null) {
377
- const selectName = match[1];
378
- const typesStr = match[2];
379
-
380
- // Parse select types (comma-separated, may have line breaks)
381
- const types = typesStr
382
- .split(',')
383
- .map(s => s.trim())
384
- .filter(s => s.length > 0);
385
-
386
- selects.push({
387
- name: selectName,
388
- types,
389
- });
390
- }
391
-
392
- return selects;
393
- }
394
-
395
- /**
396
- * Get all attributes for an entity including inherited attributes
397
- */
398
- export function getAllAttributes(
399
- entity: EntityDefinition,
400
- schema: ExpressSchema
401
- ): AttributeDefinition[] {
402
- // Collect attributes by walking up the inheritance chain,
403
- // then reverse to get STEP order (parent-first / root → leaf)
404
- const levels: AttributeDefinition[][] = [];
405
-
406
- // Guard against cyclic SUBTYPE OF chains in malformed schemas
407
- const seen = new Set<string>();
408
- let current: EntityDefinition | undefined = entity;
409
- while (current && !seen.has(current.name)) {
410
- seen.add(current.name);
411
-
412
- if (current.attributes.length > 0) {
413
- levels.push(current.attributes);
414
- }
415
-
416
- // Move to parent
417
- if (current.supertype) {
418
- current = schema.entities.find(e => e.name === current!.supertype);
419
- } else {
420
- current = undefined;
421
- }
422
- }
423
-
424
- // Reverse: root attributes first (STEP positional order)
425
- const attributes: AttributeDefinition[] = [];
426
- for (let i = levels.length - 1; i >= 0; i--) {
427
- attributes.push(...levels[i]);
428
- }
429
-
430
- return attributes;
431
- }
432
-
433
- /**
434
- * Get inheritance chain for an entity (from root to entity)
435
- */
436
- export function getInheritanceChain(
437
- entity: EntityDefinition,
438
- schema: ExpressSchema
439
- ): string[] {
440
- const chain: string[] = [];
441
-
442
- // Guard against cyclic SUBTYPE OF chains in malformed schemas
443
- const seen = new Set<string>();
444
- let current: EntityDefinition | undefined = entity;
445
- while (current && !seen.has(current.name)) {
446
- seen.add(current.name);
447
- chain.unshift(current.name); // Add to front
448
-
449
- if (current.supertype) {
450
- current = schema.entities.find(e => e.name === current!.supertype);
451
- } else {
452
- current = undefined;
453
- }
454
- }
455
-
456
- return chain;
457
- }