@ifc-lite/codegen 1.0.0 → 1.1.1

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 (44) hide show
  1. package/dist/cli.js +23 -5
  2. package/dist/cli.js.map +1 -1
  3. package/dist/crc32.d.ts +16 -0
  4. package/dist/crc32.d.ts.map +1 -0
  5. package/dist/crc32.js +69 -0
  6. package/dist/crc32.js.map +1 -0
  7. package/dist/generator.d.ts +20 -4
  8. package/dist/generator.d.ts.map +1 -1
  9. package/dist/generator.js +175 -19
  10. package/dist/generator.js.map +1 -1
  11. package/dist/index.d.ts +13 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +19 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/rust-generator.d.ts +20 -0
  16. package/dist/rust-generator.d.ts.map +1 -0
  17. package/dist/rust-generator.js +640 -0
  18. package/dist/rust-generator.js.map +1 -0
  19. package/dist/serialization-generator.d.ts +11 -0
  20. package/dist/serialization-generator.d.ts.map +1 -0
  21. package/dist/serialization-generator.js +333 -0
  22. package/dist/serialization-generator.js.map +1 -0
  23. package/dist/type-ids-generator.d.ts +11 -0
  24. package/dist/type-ids-generator.d.ts.map +1 -0
  25. package/dist/type-ids-generator.js +153 -0
  26. package/dist/type-ids-generator.js.map +1 -0
  27. package/generated/ifc4x3/entities.ts +9 -13
  28. package/generated/ifc4x3/enums.ts +0 -4
  29. package/generated/ifc4x3/index.ts +4 -2
  30. package/generated/ifc4x3/schema-registry.ts +326 -2718
  31. package/generated/ifc4x3/selects.ts +0 -4
  32. package/generated/ifc4x3/serializers.ts +322 -0
  33. package/generated/ifc4x3/test-compile.ts +49 -0
  34. package/generated/ifc4x3/type-ids.ts +1882 -0
  35. package/generated/ifc4x3/types.ts +0 -4
  36. package/package.json +1 -1
  37. package/src/cli.ts +49 -18
  38. package/src/crc32.ts +77 -0
  39. package/src/generator.ts +213 -21
  40. package/src/index.ts +28 -2
  41. package/src/rust-generator.ts +715 -0
  42. package/src/serialization-generator.ts +343 -0
  43. package/src/type-ids-generator.ts +166 -0
  44. package/tsconfig.json +1 -0
@@ -0,0 +1,343 @@
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
+ * Serialization Generator
7
+ *
8
+ * Generates TypeScript code for serializing IFC entities to STEP format.
9
+ */
10
+
11
+ import type { ExpressSchema, EntityDefinition, AttributeDefinition } from './express-parser.js';
12
+ import { getAllAttributes } from './express-parser.js';
13
+
14
+ /**
15
+ * Generate serialization support code
16
+ */
17
+ export function generateSerializers(schema: ExpressSchema): string {
18
+ let code = `/* This Source Code Form is subject to the terms of the Mozilla Public
19
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
20
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
21
+
22
+ /**
23
+ * IFC Serialization Support
24
+ * Generated from EXPRESS schema: ${schema.name}
25
+ *
26
+ * Utilities for serializing IFC entities to STEP format.
27
+ *
28
+ * DO NOT EDIT - This file is auto-generated
29
+ */
30
+
31
+ import { SCHEMA_REGISTRY } from './schema-registry.js';
32
+
33
+ /**
34
+ * STEP value types
35
+ */
36
+ export type StepValue =
37
+ | string
38
+ | number
39
+ | boolean
40
+ | null
41
+ | undefined
42
+ | StepValue[]
43
+ | EntityRef
44
+ | EnumValue;
45
+
46
+ /**
47
+ * Entity reference (#123)
48
+ */
49
+ export interface EntityRef {
50
+ ref: number;
51
+ }
52
+
53
+ /**
54
+ * Enum value (.VALUE.)
55
+ */
56
+ export interface EnumValue {
57
+ enum: string;
58
+ }
59
+
60
+ /**
61
+ * Check if value is an entity reference
62
+ */
63
+ export function isEntityRef(value: unknown): value is EntityRef {
64
+ return typeof value === 'object' && value !== null && 'ref' in value;
65
+ }
66
+
67
+ /**
68
+ * Check if value is an enum value
69
+ */
70
+ export function isEnumValue(value: unknown): value is EnumValue {
71
+ return typeof value === 'object' && value !== null && 'enum' in value;
72
+ }
73
+
74
+ /**
75
+ * Create an entity reference
76
+ */
77
+ export function ref(id: number): EntityRef {
78
+ return { ref: id };
79
+ }
80
+
81
+ /**
82
+ * Create an enum value
83
+ */
84
+ export function enumVal(value: string): EnumValue {
85
+ return { enum: value };
86
+ }
87
+
88
+ /**
89
+ * Base interface for serializable entities
90
+ */
91
+ export interface StepEntity {
92
+ /** Express ID (#123) */
93
+ expressId: number;
94
+ /** IFC type name */
95
+ type: string;
96
+ /** Attribute values */
97
+ [key: string]: unknown;
98
+ }
99
+
100
+ /**
101
+ * Serialize a single value to STEP format
102
+ */
103
+ export function serializeValue(value: StepValue): string {
104
+ // Null/undefined -> $
105
+ if (value === null || value === undefined) {
106
+ return '$';
107
+ }
108
+
109
+ // Derived value -> *
110
+ if (value === '*') {
111
+ return '*';
112
+ }
113
+
114
+ // Boolean
115
+ if (typeof value === 'boolean') {
116
+ return value ? '.T.' : '.F.';
117
+ }
118
+
119
+ // Number
120
+ if (typeof value === 'number') {
121
+ if (!Number.isFinite(value)) {
122
+ return '$';
123
+ }
124
+ // Use exponential notation for large/small numbers
125
+ if (Math.abs(value) > 1e10 || (Math.abs(value) < 1e-10 && value !== 0)) {
126
+ return value.toExponential(10).toUpperCase().replace('E+', 'E');
127
+ }
128
+ // Otherwise use fixed notation
129
+ const str = value.toString();
130
+ // Ensure there's a decimal point for REAL values
131
+ return str.includes('.') ? str : str + '.';
132
+ }
133
+
134
+ // String
135
+ if (typeof value === 'string') {
136
+ return "'" + escapeStepString(value) + "'";
137
+ }
138
+
139
+ // Entity reference
140
+ if (isEntityRef(value)) {
141
+ return '#' + value.ref;
142
+ }
143
+
144
+ // Enum value
145
+ if (isEnumValue(value)) {
146
+ return '.' + value.enum + '.';
147
+ }
148
+
149
+ // Array/List
150
+ if (Array.isArray(value)) {
151
+ if (value.length === 0) {
152
+ return '()';
153
+ }
154
+ return '(' + value.map(v => serializeValue(v as StepValue)).join(',') + ')';
155
+ }
156
+
157
+ // Object (shouldn't happen for valid STEP values)
158
+ return '$';
159
+ }
160
+
161
+ /**
162
+ * Escape a string for STEP format
163
+ */
164
+ function escapeStepString(str: string): string {
165
+ return str
166
+ .replace(/\\\\/g, '\\\\\\\\') // Backslash
167
+ .replace(/'/g, "''"); // Single quote
168
+ }
169
+
170
+ /**
171
+ * Serialize an entity to a STEP line
172
+ */
173
+ export function toStepLine(entity: StepEntity): string {
174
+ const schema = SCHEMA_REGISTRY.entities[entity.type];
175
+ if (!schema) {
176
+ throw new Error(\`Unknown entity type: \${entity.type}\`);
177
+ }
178
+
179
+ // Get all attributes in order
180
+ const values: string[] = [];
181
+ for (const attr of schema.allAttributes) {
182
+ const value = entity[attr.name];
183
+ values.push(serializeValue(value as StepValue));
184
+ }
185
+
186
+ return \`#\${entity.expressId}=\${entity.type.toUpperCase()}(\${values.join(',')});\`;
187
+ }
188
+
189
+ /**
190
+ * Generate STEP file header
191
+ */
192
+ export function generateHeader(options: {
193
+ description?: string;
194
+ author?: string;
195
+ organization?: string;
196
+ application?: string;
197
+ schema: 'IFC2X3' | 'IFC4' | 'IFC4X3';
198
+ filename?: string;
199
+ }): string {
200
+ const now = new Date().toISOString().replace(/[-:]/g, '').split('.')[0];
201
+ const desc = options.description || 'ViewDefinition [CoordinationView]';
202
+ const author = options.author || '';
203
+ const org = options.organization || '';
204
+ const app = options.application || 'ifc-lite';
205
+ const filename = options.filename || 'output.ifc';
206
+
207
+ return \`ISO-10303-21;
208
+ HEADER;
209
+ FILE_DESCRIPTION('\${desc}','2;1');
210
+ FILE_NAME('\${filename}','\${now}',('\${author}'),('\${org}'),'\${app}','\${app}','');
211
+ FILE_SCHEMA(('\${options.schema}'));
212
+ ENDSEC;
213
+ \`;
214
+ }
215
+
216
+ /**
217
+ * Generate complete STEP file content
218
+ */
219
+ export function generateStepFile(
220
+ entities: StepEntity[],
221
+ options: Parameters<typeof generateHeader>[0]
222
+ ): string {
223
+ const header = generateHeader(options);
224
+
225
+ // Sort entities by ID for deterministic output
226
+ const sorted = [...entities].sort((a, b) => a.expressId - b.expressId);
227
+
228
+ const data = sorted.map(e => toStepLine(e)).join('\\n');
229
+
230
+ return \`\${header}DATA;
231
+ \${data}
232
+ ENDSEC;
233
+ END-ISO-10303-21;
234
+ \`;
235
+ }
236
+
237
+ /**
238
+ * Parse a STEP value from string
239
+ */
240
+ export function parseStepValue(str: string): StepValue {
241
+ str = str.trim();
242
+
243
+ // Null
244
+ if (str === '$') {
245
+ return null;
246
+ }
247
+
248
+ // Derived
249
+ if (str === '*') {
250
+ return '*' as unknown as StepValue;
251
+ }
252
+
253
+ // Boolean
254
+ if (str === '.T.') {
255
+ return true;
256
+ }
257
+ if (str === '.F.') {
258
+ return false;
259
+ }
260
+ if (str === '.U.') {
261
+ return null; // Unknown/indeterminate
262
+ }
263
+
264
+ // Entity reference
265
+ if (str.startsWith('#')) {
266
+ return { ref: parseInt(str.substring(1), 10) };
267
+ }
268
+
269
+ // Enum
270
+ if (str.startsWith('.') && str.endsWith('.')) {
271
+ return { enum: str.substring(1, str.length - 1) };
272
+ }
273
+
274
+ // String
275
+ if (str.startsWith("'") && str.endsWith("'")) {
276
+ return unescapeStepString(str.substring(1, str.length - 1));
277
+ }
278
+
279
+ // List
280
+ if (str.startsWith('(') && str.endsWith(')')) {
281
+ return parseStepList(str);
282
+ }
283
+
284
+ // Number
285
+ const num = parseFloat(str);
286
+ if (!isNaN(num)) {
287
+ return num;
288
+ }
289
+
290
+ // Unknown
291
+ return str;
292
+ }
293
+
294
+ /**
295
+ * Parse a STEP list
296
+ */
297
+ function parseStepList(str: string): StepValue[] {
298
+ // Remove outer parentheses
299
+ const inner = str.substring(1, str.length - 1).trim();
300
+ if (inner === '') {
301
+ return [];
302
+ }
303
+
304
+ const values: StepValue[] = [];
305
+ let depth = 0;
306
+ let current = '';
307
+
308
+ for (let i = 0; i < inner.length; i++) {
309
+ const char = inner[i];
310
+
311
+ if (char === '(' || char === '[') {
312
+ depth++;
313
+ current += char;
314
+ } else if (char === ')' || char === ']') {
315
+ depth--;
316
+ current += char;
317
+ } else if (char === ',' && depth === 0) {
318
+ values.push(parseStepValue(current));
319
+ current = '';
320
+ } else {
321
+ current += char;
322
+ }
323
+ }
324
+
325
+ if (current.trim()) {
326
+ values.push(parseStepValue(current));
327
+ }
328
+
329
+ return values;
330
+ }
331
+
332
+ /**
333
+ * Unescape a STEP string
334
+ */
335
+ function unescapeStepString(str: string): string {
336
+ return str
337
+ .replace(/''/g, "'")
338
+ .replace(/\\\\\\\\/g, '\\\\');
339
+ }
340
+ `;
341
+
342
+ return code;
343
+ }
@@ -0,0 +1,166 @@
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
+ * Type IDs Generator
7
+ *
8
+ * Generates TypeScript type ID constants and lookup maps.
9
+ */
10
+
11
+ import type { ExpressSchema } from './express-parser.js';
12
+ import { crc32 } from './crc32.js';
13
+
14
+ /**
15
+ * Generate TypeScript type IDs file
16
+ */
17
+ export function generateTypeIds(schema: ExpressSchema): string {
18
+ let code = `/* This Source Code Form is subject to the terms of the Mozilla Public
19
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
20
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
21
+
22
+ /**
23
+ * IFC Type IDs
24
+ * Generated from EXPRESS schema: ${schema.name}
25
+ *
26
+ * CRC32 hashes for fast O(1) type lookup.
27
+ *
28
+ * DO NOT EDIT - This file is auto-generated
29
+ */
30
+
31
+ /**
32
+ * CRC32 Type IDs for all IFC entities
33
+ */
34
+ export const TYPE_IDS = {
35
+ `;
36
+
37
+ // Generate type IDs for all entities
38
+ for (const entity of schema.entities) {
39
+ const id = crc32(entity.name);
40
+ code += ` ${entity.name}: ${id},\n`;
41
+ }
42
+
43
+ code += `} as const;
44
+
45
+ /**
46
+ * Type for any valid type ID
47
+ */
48
+ export type TypeId = (typeof TYPE_IDS)[keyof typeof TYPE_IDS];
49
+
50
+ /**
51
+ * Reverse lookup: ID -> Name
52
+ */
53
+ export const TYPE_NAMES: Record<number, string> = {
54
+ `;
55
+
56
+ for (const entity of schema.entities) {
57
+ const id = crc32(entity.name);
58
+ code += ` ${id}: '${entity.name}',\n`;
59
+ }
60
+
61
+ code += `};
62
+
63
+ /**
64
+ * Get type ID from name (case-insensitive)
65
+ */
66
+ export function getTypeId(name: string): number | undefined {
67
+ // Normalize to IfcXxx format
68
+ const normalized = normalizeTypeName(name);
69
+ return (TYPE_IDS as Record<string, number>)[normalized];
70
+ }
71
+
72
+ /**
73
+ * Get type name from ID
74
+ */
75
+ export function getTypeName(id: number): string | undefined {
76
+ return TYPE_NAMES[id];
77
+ }
78
+
79
+ /**
80
+ * Check if a type ID exists
81
+ */
82
+ export function isValidTypeId(id: number): boolean {
83
+ return id in TYPE_NAMES;
84
+ }
85
+
86
+ /**
87
+ * Normalize type name to IfcXxx format
88
+ */
89
+ function normalizeTypeName(name: string): string {
90
+ // Handle IFCWALL -> IfcWall
91
+ if (name.toUpperCase().startsWith('IFC')) {
92
+ const rest = name.substring(3);
93
+ // Find the entity that matches case-insensitively
94
+ const upperRest = rest.toUpperCase();
95
+ for (const key of Object.keys(TYPE_IDS)) {
96
+ if (key.substring(3).toUpperCase() === upperRest) {
97
+ return key;
98
+ }
99
+ }
100
+ }
101
+ return name;
102
+ }
103
+
104
+ /**
105
+ * Calculate CRC32 hash for any string
106
+ * (Useful for unknown types)
107
+ */
108
+ export function crc32Hash(str: string): number {
109
+ const upper = str.toUpperCase();
110
+ let crc = 0xffffffff;
111
+ for (let i = 0; i < upper.length; i++) {
112
+ crc = CRC32_TABLE[(crc ^ upper.charCodeAt(i)) & 0xff] ^ (crc >>> 8);
113
+ }
114
+ return (crc ^ 0xffffffff) >>> 0;
115
+ }
116
+
117
+ // CRC32 lookup table
118
+ const CRC32_TABLE = new Uint32Array([
119
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
120
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
121
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
122
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
123
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
124
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
125
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
126
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
127
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
128
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
129
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
130
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
131
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
132
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
133
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
134
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
135
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
136
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
137
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cd9, 0x5005713c, 0x270241aa,
138
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
139
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
140
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
141
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
142
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
143
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
144
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
145
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
146
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
147
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
148
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
149
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
150
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
151
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
152
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
153
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
154
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
155
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
156
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
157
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
158
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
159
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd706b3,
160
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
161
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
162
+ ]);
163
+ `;
164
+
165
+ return code;
166
+ }
package/tsconfig.json CHANGED
@@ -3,6 +3,7 @@
3
3
  "target": "ES2022",
4
4
  "module": "ES2022",
5
5
  "lib": ["ES2022"],
6
+ "types": ["node"],
6
7
  "moduleResolution": "node",
7
8
  "outDir": "./dist",
8
9
  "rootDir": "./src",