@shapeshift-labs/frontier-lang-compiler 0.2.2 → 0.2.4

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/index.d.ts CHANGED
@@ -75,6 +75,7 @@ export interface ImportNativeSourceOptions {
75
75
  readonly parserVersion?: string;
76
76
  readonly sourcePath?: string;
77
77
  readonly sourceHash?: string;
78
+ readonly sourceText?: string;
78
79
  readonly symbol?: string;
79
80
  readonly name?: string;
80
81
  readonly nativeAst?: NativeAstRecord;
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  createImportResult,
4
4
  createNativeAstRecord,
5
5
  createPatch,
6
+ createSemanticIndexRecord,
6
7
  createUniversalAstEnvelope,
7
8
  hashDocumentBase,
8
9
  hashSemanticValue,
@@ -138,17 +139,26 @@ export function importNativeSource(input) {
138
139
  const language = input.language ?? input.nativeAst?.language;
139
140
  if (!language) throw new Error('importNativeSource requires a language or nativeAst.language');
140
141
  const sourcePath = input.sourcePath ?? input.nativeAst?.sourcePath;
141
- const sourceHash = input.sourceHash ?? input.nativeAst?.sourceHash ?? hashSemanticValue(input.nativeAst?.nodes ?? input.nativeAst ?? {});
142
+ const sourceHash = input.sourceHash ?? input.nativeAst?.sourceHash ?? (input.sourceText ? hashSemanticValue(input.sourceText) : hashSemanticValue(input.nativeAst?.nodes ?? input.nativeAst ?? {}));
142
143
  const importIdPart = idFragment(input.id ?? input.nativeSourceId ?? sourcePath ?? language);
144
+ const lightweight = !input.nativeAst && !input.nodes && input.sourceText
145
+ ? createLightweightNativeImport({
146
+ language,
147
+ sourceText: input.sourceText,
148
+ sourcePath,
149
+ sourceHash,
150
+ parser: input.parser
151
+ })
152
+ : undefined;
143
153
  const nativeAst = input.nativeAst ?? createNativeAstRecord({
144
154
  id: input.nativeAstId ?? `native_ast_${importIdPart}`,
145
155
  language,
146
- parser: input.parser,
156
+ parser: input.parser ?? lightweight?.parser,
147
157
  parserVersion: input.parserVersion,
148
158
  sourcePath,
149
159
  sourceHash,
150
- rootId: input.rootId ?? 'native_root',
151
- nodes: input.nodes ?? {
160
+ rootId: input.rootId ?? lightweight?.rootId ?? 'native_root',
161
+ nodes: input.nodes ?? lightweight?.nodes ?? {
152
162
  native_root: {
153
163
  id: 'native_root',
154
164
  kind: 'Unknown',
@@ -157,11 +167,15 @@ export function importNativeSource(input) {
157
167
  metadata: { reason: 'no-native-ast-nodes-provided' }
158
168
  }
159
169
  },
160
- losses: input.losses,
161
- metadata: input.nativeAstMetadata
170
+ losses: input.losses ?? lightweight?.losses,
171
+ metadata: {
172
+ ...(input.sourceText ? { sourceBytes: input.sourceText.length } : {}),
173
+ ...lightweight?.metadata,
174
+ ...input.nativeAstMetadata
175
+ }
162
176
  });
163
177
  const frontierNodeIds = input.frontierNodeIds ?? input.semanticNodes?.map((node) => node.id) ?? [];
164
- const losses = input.losses ?? nativeAst.losses ?? [];
178
+ const losses = input.losses ?? nativeAst.losses ?? lightweight?.losses ?? [];
165
179
  const nativeSource = nativeSourceNode({
166
180
  id: input.nativeSourceId ?? `native_source_${importIdPart}`,
167
181
  name: input.name ?? sourcePath?.split(/[\\/]/).filter(Boolean).at(-1) ?? `${language}NativeSource`,
@@ -205,7 +219,7 @@ export function importNativeSource(input) {
205
219
  semanticStatus: input.semanticStatus ?? (semanticNodes.length ? 'mapped' : 'native-only')
206
220
  }
207
221
  }];
208
- const semanticIndex = input.semanticIndex;
222
+ const semanticIndex = input.semanticIndex ?? lightweight?.semanticIndex;
209
223
  const universalAst = createUniversalAstEnvelope({
210
224
  id: input.universalAstId ?? `universal_ast_${importIdPart}`,
211
225
  document,
@@ -257,6 +271,303 @@ export function importNativeSource(input) {
257
271
  };
258
272
  }
259
273
 
274
+ function createLightweightNativeImport(input) {
275
+ const parser = input.parser ?? `${input.language}.lightweight-declaration-scan`;
276
+ const rootId = 'native_root';
277
+ const nodes = {
278
+ [rootId]: {
279
+ id: rootId,
280
+ kind: 'Program',
281
+ languageKind: `${input.language}.program`,
282
+ children: [],
283
+ metadata: { parser, sourceHash: input.sourceHash }
284
+ }
285
+ };
286
+ const declarations = scanNativeDeclarations(input);
287
+ const losses = [];
288
+ const documentId = `doc_${idFragment(input.sourcePath ?? input.language)}`;
289
+ const symbols = [];
290
+ const occurrences = [];
291
+ const relations = [];
292
+ const facts = [];
293
+
294
+ for (const declaration of declarations) {
295
+ nodes[rootId].children.push(declaration.nodeId);
296
+ nodes[declaration.nodeId] = {
297
+ id: declaration.nodeId,
298
+ kind: declaration.kind,
299
+ languageKind: declaration.languageKind,
300
+ span: declaration.span,
301
+ value: declaration.name ?? declaration.importPath ?? null,
302
+ fields: declaration.fields,
303
+ metadata: declaration.metadata
304
+ };
305
+ if (declaration.symbolId) {
306
+ symbols.push({
307
+ id: declaration.symbolId,
308
+ scheme: 'frontier',
309
+ name: declaration.name,
310
+ kind: declaration.symbolKind,
311
+ language: input.language,
312
+ nativeAstNodeId: declaration.nodeId,
313
+ signatureHash: hashSemanticValue([input.language, declaration.kind, declaration.name, declaration.fields ?? {}]),
314
+ definitionSpan: declaration.span
315
+ });
316
+ occurrences.push({
317
+ id: `occ_${idFragment(declaration.nodeId)}_def`,
318
+ documentId,
319
+ symbolId: declaration.symbolId,
320
+ role: declaration.role ?? 'definition',
321
+ span: declaration.span,
322
+ nativeAstNodeId: declaration.nodeId
323
+ });
324
+ relations.push({
325
+ id: `rel_${idFragment(documentId)}_${idFragment(declaration.nodeId)}`,
326
+ sourceId: documentId,
327
+ predicate: declaration.role === 'import' ? 'imports' : 'defines',
328
+ targetId: declaration.symbolId
329
+ });
330
+ facts.push({
331
+ id: `fact_${idFragment(declaration.nodeId)}_kind`,
332
+ predicate: 'nativeKind',
333
+ subjectId: declaration.symbolId,
334
+ value: declaration.languageKind
335
+ });
336
+ }
337
+ if (declaration.loss) losses.push(declaration.loss);
338
+ }
339
+
340
+ const semanticIndex = createSemanticIndexRecord({
341
+ id: `index_${idFragment(input.sourcePath ?? input.language)}`,
342
+ documents: [{
343
+ id: documentId,
344
+ path: input.sourcePath ?? `${input.language}:memory`,
345
+ language: input.language,
346
+ sourceHash: input.sourceHash
347
+ }],
348
+ symbols,
349
+ occurrences,
350
+ relations,
351
+ facts,
352
+ evidence: [{
353
+ id: `evidence_${idFragment(input.sourcePath ?? input.language)}_lightweight_scan`,
354
+ kind: 'import',
355
+ status: 'passed',
356
+ path: input.sourcePath,
357
+ summary: `Lightweight declaration scan found ${symbols.length} symbol(s).`,
358
+ metadata: { parser }
359
+ }],
360
+ metadata: {
361
+ parser,
362
+ coverage: 'declarations-only',
363
+ unsupported: ['full expression AST', 'type checking', 'control flow', 'comments and formatting preservation']
364
+ }
365
+ });
366
+
367
+ return {
368
+ parser,
369
+ rootId,
370
+ nodes,
371
+ losses,
372
+ semanticIndex,
373
+ metadata: { parser, scanKind: 'lightweight-declaration-scan', declarationCount: declarations.length }
374
+ };
375
+ }
376
+
377
+ function scanNativeDeclarations(input) {
378
+ const language = String(input.language).toLowerCase();
379
+ if (language === 'javascript' || language === 'typescript') return scanJavaScriptLike(input);
380
+ if (language === 'python') return scanPython(input);
381
+ if (language === 'rust') return scanRust(input);
382
+ if (language === 'c' || language === 'cpp' || language === 'c++') return scanCLike(input);
383
+ return scanGenericDeclarations(input);
384
+ }
385
+
386
+ function scanJavaScriptLike(input) {
387
+ const declarations = [];
388
+ for (const { line, number } of sourceLines(input.sourceText)) {
389
+ const trimmed = line.trim();
390
+ let match;
391
+ if ((match = trimmed.match(/^(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)/))) {
392
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
393
+ } else if ((match = trimmed.match(/^(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/))) {
394
+ declarations.push(nativeDeclaration(input, number, 'ClassDeclaration', 'class', match[1], {}, trimmed.includes('{')));
395
+ } else if ((match = trimmed.match(/^(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)/))) {
396
+ declarations.push(nativeDeclaration(input, number, 'InterfaceDeclaration', 'interface', match[1], {}, trimmed.includes('{')));
397
+ } else if ((match = trimmed.match(/^(?:export\s+)?type\s+([A-Za-z_$][\w$]*)\s*=/))) {
398
+ declarations.push(nativeDeclaration(input, number, 'TypeAliasDeclaration', 'type', match[1], {}, false));
399
+ } else if ((match = trimmed.match(/^(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(?([^=;]*)\)?\s*=>/))) {
400
+ declarations.push(nativeDeclaration(input, number, 'VariableFunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
401
+ } else if ((match = trimmed.match(/^import\s+(?:.+?\s+from\s+)?['"]([^'"]+)['"]/))) {
402
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDeclaration', 'module'));
403
+ } else if ((match = trimmed.match(/^export\s+\{[^}]*\}\s+from\s+['"]([^'"]+)['"]/))) {
404
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ExportFromDeclaration', 'module'));
405
+ }
406
+ }
407
+ return declarations;
408
+ }
409
+
410
+ function scanPython(input) {
411
+ const declarations = [];
412
+ for (const { line, number } of sourceLines(input.sourceText)) {
413
+ const trimmed = line.trim();
414
+ let match;
415
+ if ((match = trimmed.match(/^(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*:/))) {
416
+ declarations.push(nativeDeclaration(input, number, 'FunctionDef', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
417
+ } else if ((match = trimmed.match(/^class\s+([A-Za-z_]\w*)/))) {
418
+ declarations.push(nativeDeclaration(input, number, 'ClassDef', 'class', match[1], {}, true));
419
+ } else if ((match = trimmed.match(/^(?:from\s+([A-Za-z_][\w.]*)\s+import\s+.+|import\s+([A-Za-z_][\w.]*))/))) {
420
+ declarations.push(nativeImportDeclaration(input, number, match[1] ?? match[2], 'Import', 'module'));
421
+ }
422
+ }
423
+ return declarations;
424
+ }
425
+
426
+ function scanRust(input) {
427
+ const declarations = [];
428
+ for (const { line, number } of sourceLines(input.sourceText)) {
429
+ const trimmed = line.trim();
430
+ let match;
431
+ if ((match = trimmed.match(/^(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?fn\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/))) {
432
+ declarations.push(nativeDeclaration(input, number, 'ItemFn', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
433
+ } else if ((match = trimmed.match(/^(?:pub(?:\([^)]*\))?\s+)?struct\s+([A-Za-z_]\w*)/))) {
434
+ declarations.push(nativeDeclaration(input, number, 'ItemStruct', 'type', match[1], {}, trimmed.includes('{')));
435
+ } else if ((match = trimmed.match(/^(?:pub(?:\([^)]*\))?\s+)?enum\s+([A-Za-z_]\w*)/))) {
436
+ declarations.push(nativeDeclaration(input, number, 'ItemEnum', 'type', match[1], {}, trimmed.includes('{')));
437
+ } else if ((match = trimmed.match(/^(?:pub(?:\([^)]*\))?\s+)?trait\s+([A-Za-z_]\w*)/))) {
438
+ declarations.push(nativeDeclaration(input, number, 'ItemTrait', 'trait', match[1], {}, trimmed.includes('{')));
439
+ } else if ((match = trimmed.match(/^impl(?:\s*<[^>]+>)?\s+(.+?)\s*\{/))) {
440
+ declarations.push(nativeDeclaration(input, number, 'ItemImpl', 'implementation', idFragment(match[1]), { target: match[1].trim() }, true));
441
+ } else if ((match = trimmed.match(/^(?:pub(?:\([^)]*\))?\s+)?mod\s+([A-Za-z_]\w*)/))) {
442
+ declarations.push(nativeDeclaration(input, number, 'ItemMod', 'module', match[1], {}, trimmed.includes('{')));
443
+ } else if ((match = trimmed.match(/^use\s+(.+?);/))) {
444
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ItemUse', 'module'));
445
+ } else if (/^[A-Za-z_]\w*!\s*[({[]/.test(trimmed)) {
446
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'macroExpansion'));
447
+ }
448
+ }
449
+ return declarations;
450
+ }
451
+
452
+ function scanCLike(input) {
453
+ const declarations = [];
454
+ for (const { line, number } of sourceLines(input.sourceText)) {
455
+ const trimmed = line.trim();
456
+ let match;
457
+ if ((match = trimmed.match(/^#\s*include\s+[<"]([^>"]+)[>"]/))) {
458
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'IncludeDirective', 'header'));
459
+ } else if ((match = trimmed.match(/^#\s*define\s+([A-Za-z_]\w*)/))) {
460
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'preprocessor', match[1]));
461
+ } else if ((match = trimmed.match(/^typedef\s+struct(?:\s+([A-Za-z_]\w*))?/))) {
462
+ declarations.push(nativeDeclaration(input, number, 'TypedefStructDeclaration', 'type', match[1] ?? `anonymous_struct_${number}`, {}, trimmed.includes('{')));
463
+ } else if ((match = trimmed.match(/^(?:struct|enum)\s+([A-Za-z_]\w*)/))) {
464
+ declarations.push(nativeDeclaration(input, number, 'TagDeclaration', 'type', match[1], {}, trimmed.includes('{')));
465
+ } else if ((match = trimmed.match(/^(?:[A-Za-z_][\w\s*:&<>]+)\s+([A-Za-z_]\w*)\s*\(([^;{}]*)\)\s*(?:;|\{)?$/))) {
466
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.endsWith('{')));
467
+ }
468
+ }
469
+ return declarations;
470
+ }
471
+
472
+ function scanGenericDeclarations(input) {
473
+ return sourceLines(input.sourceText)
474
+ .filter(({ line }) => /\b(function|class|struct|enum|trait|interface|def)\b/.test(line))
475
+ .map(({ line, number }) => nativeDeclaration(input, number, 'NativeDeclaration', 'variable', idFragment(line.trim()).slice(0, 40), { source: line.trim() }, true));
476
+ }
477
+
478
+ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fields = {}, hasBody = false) {
479
+ const nodeId = `native_${idFragment(languageKind)}_${lineNumber}_${idFragment(name)}`;
480
+ return {
481
+ nodeId,
482
+ kind: languageKind,
483
+ languageKind: `${input.language}.${languageKind}`,
484
+ name,
485
+ symbolKind,
486
+ symbolId: `symbol:${input.language}:${idFragment(name)}`,
487
+ span: spanForLine(input, lineNumber),
488
+ fields,
489
+ metadata: { scan: 'lightweight-declaration', hasBody },
490
+ ...(hasBody ? { loss: opaqueBodyLoss(input, lineNumber, nodeId, name) } : {})
491
+ };
492
+ }
493
+
494
+ function nativeImportDeclaration(input, lineNumber, importPath, languageKind, symbolKind) {
495
+ const name = String(importPath);
496
+ const nodeId = `native_${idFragment(languageKind)}_${lineNumber}_${idFragment(name)}`;
497
+ return {
498
+ nodeId,
499
+ kind: languageKind,
500
+ languageKind: `${input.language}.${languageKind}`,
501
+ name,
502
+ symbolKind,
503
+ symbolId: `symbol:${input.language}:import:${idFragment(name)}`,
504
+ role: 'import',
505
+ importPath: name,
506
+ span: spanForLine(input, lineNumber),
507
+ fields: { importPath: name },
508
+ metadata: { scan: 'lightweight-import' }
509
+ };
510
+ }
511
+
512
+ function nativeMacroLoss(input, lineNumber, source, kind, name = idFragment(source).slice(0, 40)) {
513
+ const nodeId = `native_${kind}_${lineNumber}_${idFragment(name)}`;
514
+ return {
515
+ nodeId,
516
+ kind: kind === 'preprocessor' ? 'PreprocessorDirective' : 'MacroInvocation',
517
+ languageKind: `${input.language}.${kind}`,
518
+ name,
519
+ symbolKind: 'constant',
520
+ symbolId: `symbol:${input.language}:${kind}:${idFragment(name)}`,
521
+ span: spanForLine(input, lineNumber),
522
+ fields: { source },
523
+ metadata: { scan: 'lightweight-macro' },
524
+ loss: {
525
+ id: `loss_${idFragment(nodeId)}`,
526
+ severity: 'warning',
527
+ phase: 'read',
528
+ sourceFormat: input.language,
529
+ kind,
530
+ message: `${input.language} ${kind} retained as native source; expansion is not evaluated by the lightweight importer.`,
531
+ span: spanForLine(input, lineNumber),
532
+ nodeId
533
+ }
534
+ };
535
+ }
536
+
537
+ function opaqueBodyLoss(input, lineNumber, nodeId, name) {
538
+ return {
539
+ id: `loss_${idFragment(nodeId)}_body`,
540
+ severity: 'info',
541
+ phase: 'read',
542
+ sourceFormat: input.language,
543
+ kind: 'opaqueNative',
544
+ message: `Body for ${name} is retained as native source by the lightweight declaration importer.`,
545
+ span: spanForLine(input, lineNumber),
546
+ nodeId
547
+ };
548
+ }
549
+
550
+ function sourceLines(sourceText) {
551
+ return String(sourceText ?? '').split(/\r?\n/).map((line, index) => ({ line, number: index + 1 }));
552
+ }
553
+
554
+ function spanForLine(input, lineNumber) {
555
+ return {
556
+ sourceId: input.sourceHash,
557
+ path: input.sourcePath,
558
+ startLine: lineNumber,
559
+ endLine: lineNumber,
560
+ startColumn: 1
561
+ };
562
+ }
563
+
564
+ function splitParameters(raw) {
565
+ return String(raw ?? '')
566
+ .split(',')
567
+ .map((part) => part.trim())
568
+ .filter(Boolean);
569
+ }
570
+
260
571
  export function createUniversalAstFromDocument(document, input = {}) {
261
572
  return createUniversalAstEnvelope({
262
573
  id: input.id ?? `universal_ast_${idFragment(document.id)}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -56,14 +56,14 @@
56
56
  "access": "public"
57
57
  },
58
58
  "dependencies": {
59
- "@shapeshift-labs/frontier-lang-c": "0.2.0",
60
- "@shapeshift-labs/frontier-lang-checker": "0.3.0",
61
- "@shapeshift-labs/frontier-lang-javascript": "0.2.0",
59
+ "@shapeshift-labs/frontier-lang-c": "0.2.1",
60
+ "@shapeshift-labs/frontier-lang-checker": "0.3.1",
61
+ "@shapeshift-labs/frontier-lang-javascript": "0.2.1",
62
62
  "@shapeshift-labs/frontier-lang-kernel": "0.3.1",
63
- "@shapeshift-labs/frontier-lang-parser": "0.3.0",
64
- "@shapeshift-labs/frontier-lang-python": "0.2.0",
65
- "@shapeshift-labs/frontier-lang-rust": "0.2.0",
66
- "@shapeshift-labs/frontier-lang-typescript": "0.3.0"
63
+ "@shapeshift-labs/frontier-lang-parser": "0.3.1",
64
+ "@shapeshift-labs/frontier-lang-python": "0.2.1",
65
+ "@shapeshift-labs/frontier-lang-rust": "0.2.1",
66
+ "@shapeshift-labs/frontier-lang-typescript": "0.3.1"
67
67
  },
68
68
  "devDependencies": {
69
69
  "typescript": "^5.9.3"