semantifold 0.13.0 → 0.15.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/README.md +2 -1
- package/build/src/backends/effects.js +3 -1
- package/build/src/backends/php.d.ts.map +1 -1
- package/build/src/backends/php.js +56 -3
- package/build/src/backends/python.js +11 -7
- package/build/src/backends/scalars.js +2 -2
- package/build/src/backends/shared.d.ts +2 -1
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +210 -8
- package/build/src/backends/writer.d.ts +18 -4
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +40 -4
- package/build/src/frontends/java.d.ts +1 -1
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +14 -11
- package/build/src/frontends/javascript-typescript.d.ts +1 -1
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +17 -17
- package/build/src/frontends/php.d.ts +5 -1
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +159 -31
- package/build/src/frontends/python.d.ts.map +1 -1
- package/build/src/frontends/python.js +37 -7
- package/build/src/frontends/ruby.d.ts +1 -1
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +12 -9
- package/build/src/frontends/types.d.ts +9 -2
- package/build/src/frontends/types.d.ts.map +1 -1
- package/build/src/frontends/types.js +26 -9
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +30 -6
- package/build/src/mapping.d.ts.map +1 -1
- package/build/src/mapping.js +4 -2
- package/build/src/semantic/effects.d.ts.map +1 -1
- package/build/src/semantic/effects.js +3 -1
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +37 -4
- package/build/src/semantic/types.d.ts +140 -9
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +37 -6
- package/build/src/semantic/validate.d.ts +31 -2
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +347 -28
- package/docs/adr-bounded-subtyping.md +180 -0
- package/docs/goals.md +1 -1
- package/docs/language-support.md +5 -4
- package/docs/testing.md +2 -2
- package/package.json +1 -1
- package/src/backends/effects.js +1 -0
- package/src/backends/php.js +56 -2
- package/src/backends/python.js +10 -4
- package/src/backends/scalars.js +1 -1
- package/src/backends/shared.js +211 -7
- package/src/backends/writer.js +41 -3
- package/src/frontends/java.js +13 -10
- package/src/frontends/javascript-typescript.js +16 -16
- package/src/frontends/php.js +175 -31
- package/src/frontends/python.js +39 -5
- package/src/frontends/ruby.js +11 -8
- package/src/frontends/types.js +26 -8
- package/src/language-registry.js +29 -5
- package/src/mapping.js +3 -1
- package/src/semantic/effects.js +1 -0
- package/src/semantic/provenance.js +34 -3
- package/src/semantic/types.js +39 -5
- package/src/semantic/validate.js +378 -27
package/src/backends/shared.js
CHANGED
|
@@ -27,7 +27,7 @@ const binaryOperationSyntax = Object.freeze({
|
|
|
27
27
|
StringEqual: Object.freeze({default: "==", strict: "==="}),
|
|
28
28
|
StringNotEqual: Object.freeze({default: "!=", strict: "!=="})
|
|
29
29
|
})
|
|
30
|
-
const task005Languages = new Set(["php", "ruby", "javascript", "typescript", "java", "c", "dart", "zig"])
|
|
30
|
+
const task005Languages = new Set(["php", "ruby", "javascript", "typescript", "java", "c", "dart", "zig", "python"])
|
|
31
31
|
const task006Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
32
32
|
const task007Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
33
33
|
const task008Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
@@ -38,6 +38,7 @@ const task014Languages = new Set(["php", "ruby", "javascript", "typescript", "ja
|
|
|
38
38
|
const task032Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
39
39
|
const task033Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
40
40
|
const task048ConditionalLanguages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
41
|
+
const boundedSubtypingLanguages = new Set(["php"])
|
|
41
42
|
const javaReferenceMethodHooks = new Set(["clone", "equals", "finalize", "getClass", "hashCode", "notify", "notifyAll", "toString", "wait"])
|
|
42
43
|
const javascriptReferenceMethodHooks = new Set([
|
|
43
44
|
"__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__", "constructor",
|
|
@@ -56,7 +57,7 @@ const javaObjectInstanceMethodSignatures = new Set([
|
|
|
56
57
|
* Checks the intentionally narrow backend contract.
|
|
57
58
|
* @param {import("../semantic/types.js").SemanticModule} module - Semantic module.
|
|
58
59
|
* @param {import("../semantic/types.js").BackendLanguage} language - Backend language or binary target.
|
|
59
|
-
* @param {{externalDeclarationIds?: Set<string>, program?: boolean, visibleFunctions?: Map<string, import("../semantic/types.js").FunctionDeclaration>, visibleRecords?: Map<string, import("../semantic/types.js").RecordDeclaration>, visibleClasses?: Map<string, import("../semantic/types.js").ClassDeclaration>, visibleErrors?: Map<string, import("../semantic/types.js").ErrorDeclaration>, visibleCallEffects?: Map<string, Set<string>>}} [options] - Resolved program validation context.
|
|
60
|
+
* @param {{externalDeclarationIds?: Set<string>, program?: boolean, visibleFunctions?: Map<string, import("../semantic/types.js").FunctionDeclaration>, visibleRecords?: Map<string, import("../semantic/types.js").RecordDeclaration>, visibleClasses?: Map<string, import("../semantic/types.js").ClassDeclaration>, visibleInterfaces?: Map<string, import("../semantic/types.js").InterfaceDeclaration>, visibleErrors?: Map<string, import("../semantic/types.js").ErrorDeclaration>, visibleCallEffects?: Map<string, Set<string>>}} [options] - Resolved program validation context.
|
|
60
61
|
* @returns {void}
|
|
61
62
|
*/
|
|
62
63
|
export function validateBackendModule(module, language, options = {}) {
|
|
@@ -89,11 +90,19 @@ export function validateBackendModule(module, language, options = {}) {
|
|
|
89
90
|
if (classes.length > 0 && !task033Languages.has(language)) {
|
|
90
91
|
unsupportedCapability(language, "Task 033 reference classes, methods, and constructors", classes[0]?.location ?? module.location)
|
|
91
92
|
}
|
|
93
|
+
const declaredInterfaces = module.interfaces
|
|
94
|
+
const interfaces = declaredInterfaces === undefined ? [] : declaredInterfaces
|
|
95
|
+
|
|
96
|
+
if (!Array.isArray(interfaces)) unsupportedCapability(language, "missing or invalid interface declarations", module.location)
|
|
97
|
+
if (interfaces.length > 0 && !boundedSubtypingLanguages.has(language)) {
|
|
98
|
+
unsupportedCapability(language, "Task 048 bounded subtyping interfaces", interfaces[0]?.location ?? module.location)
|
|
99
|
+
}
|
|
92
100
|
if (!task011Languages.has(language)) rejectTypedErrors(module, language)
|
|
93
101
|
if (!task032Languages.has(language)) rejectConditionControlledLoops(module, language)
|
|
94
102
|
if (!task014Languages.has(language)) rejectOrderedMapCapability(module, language)
|
|
95
103
|
validateRecordTargets(records, module.functions, language, module.location)
|
|
96
|
-
|
|
104
|
+
validateInterfaceTargets(interfaces, records, errors, module.functions, language, module.location)
|
|
105
|
+
validateReferenceClassTargets(classes, records, errors, module.functions, interfaces, language, module.location)
|
|
97
106
|
if (!task008Languages.has(language)) rejectIterationStatements(module, language)
|
|
98
107
|
if (!task007Languages.has(language)) rejectOptionalTypes(module, language)
|
|
99
108
|
if (!task006Languages.has(language)) rejectCollectionTypes(module, language)
|
|
@@ -163,6 +172,7 @@ export function validateBackendModule(module, language, options = {}) {
|
|
|
163
172
|
classes: options.visibleClasses,
|
|
164
173
|
errors: options.visibleErrors,
|
|
165
174
|
functions: options.visibleFunctions,
|
|
175
|
+
interfaces: options.visibleInterfaces,
|
|
166
176
|
records: options.visibleRecords
|
|
167
177
|
})
|
|
168
178
|
}
|
|
@@ -382,7 +392,99 @@ function validateRecordTargets(records, functions, language, moduleLocation) {
|
|
|
382
392
|
* @param {import("../semantic/types.js").SourceLocation | undefined} moduleLocation - Fallback location.
|
|
383
393
|
* @returns {void}
|
|
384
394
|
*/
|
|
385
|
-
|
|
395
|
+
/**
|
|
396
|
+
* Validates the exact target-visible shape of every interface and its override contracts before output allocation.
|
|
397
|
+
* @param {import("../semantic/types.js").InterfaceDeclaration[]} interfaces - Candidate interfaces.
|
|
398
|
+
* @param {import("../semantic/types.js").RecordDeclaration[]} records - Existing records.
|
|
399
|
+
* @param {import("../semantic/types.js").ErrorDeclaration[]} errors - Existing typed errors.
|
|
400
|
+
* @param {import("../semantic/types.js").FunctionDeclaration[]} functions - Existing functions.
|
|
401
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
402
|
+
* @param {import("../semantic/types.js").SourceLocation | undefined} moduleLocation - Fallback location.
|
|
403
|
+
* @returns {void}
|
|
404
|
+
*/
|
|
405
|
+
function validateInterfaceTargets(interfaces, records, errors, functions, language, moduleLocation) {
|
|
406
|
+
if (interfaces.length == 0) return
|
|
407
|
+
const byId = new Map(interfaces.map((declaration) => [declaration.id, declaration]))
|
|
408
|
+
const targetNames = new Set()
|
|
409
|
+
|
|
410
|
+
for (const declaration of [...records, ...errors, ...functions]) {
|
|
411
|
+
if (!declaration || typeof declaration != "object" || Array.isArray(declaration)) continue
|
|
412
|
+
const name = Reflect.get(declaration, "name")
|
|
413
|
+
|
|
414
|
+
if (typeof name == "string") targetNames.add(language == "php" ? name.toLowerCase() : name)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
for (let interfaceIndex = 0; interfaceIndex < interfaces.length; interfaceIndex += 1) {
|
|
418
|
+
const declaration = interfaces[interfaceIndex]
|
|
419
|
+
const location = diagnosticLocation(declaration?.location, moduleLocation)
|
|
420
|
+
const keys = declaration && typeof declaration == "object" && !Array.isArray(declaration)
|
|
421
|
+
? Object.keys(declaration).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
422
|
+
|
|
423
|
+
if (!declaration || declaration.kind != "InterfaceDeclaration" ||
|
|
424
|
+
!["extends,id,kind,location,methods,name", "id,kind,location,methods,name"].includes(keys) ||
|
|
425
|
+
!isDenseArray(declaration.methods) ||
|
|
426
|
+
!new RegExp(`^(?:[a-z][a-z0-9._-]*#)?interface:${interfaceIndex}$`, "u").test(String(declaration.id))) {
|
|
427
|
+
unsupportedCapability(language, "missing or invalid interface declaration", location)
|
|
428
|
+
}
|
|
429
|
+
const declarationLocation = requireSemanticLocation(declaration.location, language, "interface declaration location", location)
|
|
430
|
+
|
|
431
|
+
validateTargetTypeIdentifier(language, declaration.name, declarationLocation)
|
|
432
|
+
const targetName = language == "php" ? declaration.name.toLowerCase() : declaration.name
|
|
433
|
+
|
|
434
|
+
if (targetNames.has(targetName)) unsupportedCapability(language, `interface collision '${declaration.name}'`, declarationLocation)
|
|
435
|
+
targetNames.add(targetName)
|
|
436
|
+
const methodNames = new Set()
|
|
437
|
+
|
|
438
|
+
for (let methodIndex = 0; methodIndex < declaration.methods.length; methodIndex += 1) {
|
|
439
|
+
const method = declaration.methods[methodIndex]
|
|
440
|
+
const methodLocation = diagnosticLocation(method?.location, declarationLocation)
|
|
441
|
+
const methodKeys = method && typeof method == "object" && !Array.isArray(method)
|
|
442
|
+
? Object.keys(method).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
443
|
+
|
|
444
|
+
if (!method || method.kind != "InterfaceMethodDeclaration" || methodKeys != "id,kind,location,name,parameters,returnType" ||
|
|
445
|
+
method.id != `${declaration.id}:method:${methodIndex}` || !isDenseArray(method.parameters)) {
|
|
446
|
+
unsupportedCapability(language, "missing or invalid interface method declaration", methodLocation)
|
|
447
|
+
}
|
|
448
|
+
const exactMethodLocation = requireSemanticLocation(method.location, language, "interface method declaration location", declarationLocation)
|
|
449
|
+
|
|
450
|
+
validateTargetIdentifier(language, method.name, "interface method", exactMethodLocation)
|
|
451
|
+
const normalized = language == "php" ? method.name.toLowerCase() : method.name
|
|
452
|
+
|
|
453
|
+
if (methodNames.has(normalized)) unsupportedCapability(language, `duplicate interface method '${method.name}'`, exactMethodLocation)
|
|
454
|
+
methodNames.add(normalized)
|
|
455
|
+
validateReferenceClassParameters(method.parameters, "interface method", language, exactMethodLocation)
|
|
456
|
+
}
|
|
457
|
+
const parents = declaration.extends ?? []
|
|
458
|
+
|
|
459
|
+
for (const parent of parents) {
|
|
460
|
+
const parentKeys = parent && typeof parent == "object" && !Array.isArray(parent)
|
|
461
|
+
? Object.keys(parent).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
462
|
+
|
|
463
|
+
if (!parent || parent.kind != "InterfaceType" || parentKeys != "declarationId,kind" ||
|
|
464
|
+
!byId.has(parent.declarationId)) {
|
|
465
|
+
unsupportedCapability(language, "missing or invalid interface parent reference", declarationLocation)
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
for (const declaration of interfaces) {
|
|
470
|
+
for (const parent of declaration.extends ?? []) {
|
|
471
|
+
if (parent.declarationId == declaration.id) unsupportedCapability(language, "interface extends itself", declaration.location)
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Validates the exact target-visible shape of every reference class before output allocation.
|
|
478
|
+
* @param {import("../semantic/types.js").ClassDeclaration[]} classes - Candidate classes.
|
|
479
|
+
* @param {import("../semantic/types.js").RecordDeclaration[]} records - Existing records.
|
|
480
|
+
* @param {import("../semantic/types.js").ErrorDeclaration[]} errors - Existing typed errors.
|
|
481
|
+
* @param {import("../semantic/types.js").FunctionDeclaration[]} functions - Existing functions.
|
|
482
|
+
* @param {import("../semantic/types.js").InterfaceDeclaration[]} interfaces - Existing interfaces.
|
|
483
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
484
|
+
* @param {import("../semantic/types.js").SourceLocation | undefined} moduleLocation - Fallback location.
|
|
485
|
+
* @returns {void}
|
|
486
|
+
*/
|
|
487
|
+
function validateReferenceClassTargets(classes, records, errors, functions, interfaces, language, moduleLocation) {
|
|
386
488
|
const targetNames = new Set()
|
|
387
489
|
|
|
388
490
|
for (const declaration of [...records, ...errors, ...functions]) {
|
|
@@ -400,7 +502,8 @@ function validateReferenceClassTargets(classes, records, errors, functions, lang
|
|
|
400
502
|
? Object.keys(declaration).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
401
503
|
|
|
402
504
|
if (!declaration || declaration.kind != "ClassDeclaration" ||
|
|
403
|
-
!["constructor,fields,id,kind,location,methods,name", "constructor,fields,id,kind,location,methods,name,ownership"
|
|
505
|
+
!["constructor,fields,id,implements,kind,location,methods,name", "constructor,fields,id,implements,kind,location,methods,name,ownership",
|
|
506
|
+
"constructor,fields,id,kind,location,methods,name", "constructor,fields,id,kind,location,methods,name,ownership"].includes(keys) ||
|
|
404
507
|
!isDenseArray(declaration.fields) || declaration.fields.length == 0 || !isDenseArray(declaration.methods) ||
|
|
405
508
|
!new RegExp(`^(?:[a-z][a-z0-9._-]*#)?class:${classIndex}$`, "u").test(String(declaration.id))) {
|
|
406
509
|
unsupportedCapability(language, "missing or invalid reference class declaration", location)
|
|
@@ -459,7 +562,9 @@ function validateReferenceClassTargets(classes, records, errors, functions, lang
|
|
|
459
562
|
? Object.keys(method).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
460
563
|
|
|
461
564
|
if (!method || method.kind != "MethodDeclaration" ||
|
|
462
|
-
!["body,id,kind,location,name,parameters,returnType",
|
|
565
|
+
!["body,id,implements,kind,location,name,parameters,returnType",
|
|
566
|
+
"body,effects,failureIds,id,implements,kind,location,name,parameters,returnType",
|
|
567
|
+
"body,id,kind,location,name,parameters,returnType",
|
|
463
568
|
"body,effects,failureIds,id,kind,location,name,parameters,returnType"].includes(methodKeys) ||
|
|
464
569
|
method.id != `${declaration.id}:method:${methodIndex}` || !isDenseArray(method.parameters)) {
|
|
465
570
|
unsupportedCapability(language, "missing or invalid method declaration", methodLocation)
|
|
@@ -492,13 +597,104 @@ function validateReferenceClassTargets(classes, records, errors, functions, lang
|
|
|
492
597
|
methodNames.add(normalized)
|
|
493
598
|
validateReferenceClassParameters(method.parameters, "method", language, exactMethodLocation)
|
|
494
599
|
}
|
|
600
|
+
const implemented = declaration.implements ?? []
|
|
601
|
+
|
|
602
|
+
for (const reference of implemented) {
|
|
603
|
+
const referenceKeys = reference && typeof reference == "object" && !Array.isArray(reference)
|
|
604
|
+
? Object.keys(reference).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
605
|
+
|
|
606
|
+
if (!reference || reference.kind != "InterfaceType" || referenceKeys != "declarationId,kind" ||
|
|
607
|
+
!interfaces.some((candidate) => candidate.id == reference.declarationId)) {
|
|
608
|
+
unsupportedCapability(language, "missing or invalid implemented interface reference", location)
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
const satisfied = new Set()
|
|
612
|
+
|
|
613
|
+
for (const reference of implemented) {
|
|
614
|
+
for (const member of flattenedTargetInterfaceMembers(reference.declarationId, interfaces, language, location)) {
|
|
615
|
+
const method = declaration.methods.find((candidate) => candidate.name == member.name)
|
|
616
|
+
|
|
617
|
+
if (!method) unsupportedCapability(language, `interface method '${member.name}' is not implemented`, location)
|
|
618
|
+
if (satisfied.has(member.name)) unsupportedCapability(language, `duplicate interface implementation '${member.name}'`, location)
|
|
619
|
+
if (method.parameters.length != member.parameters.length ||
|
|
620
|
+
method.parameters.some((parameter, index) => !sameTargetType(parameter.type, member.parameters[index].type))) {
|
|
621
|
+
unsupportedCapability(language, `interface method signature mismatch '${member.name}'`, location)
|
|
622
|
+
}
|
|
623
|
+
if (!sameTargetType(method.returnType, member.returnType)) {
|
|
624
|
+
unsupportedCapability(language, `interface method return mismatch '${member.name}'`, location)
|
|
625
|
+
}
|
|
626
|
+
satisfied.add(member.name)
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Flattens one target-validated interface signature set in declaration order.
|
|
634
|
+
* @param {string} interfaceId - Interface identity.
|
|
635
|
+
* @param {import("../semantic/types.js").InterfaceDeclaration[]} interfaces - Interfaces in declaration order.
|
|
636
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
637
|
+
* @param {import("../semantic/types.js").SourceLocation | undefined} location - Diagnostic location.
|
|
638
|
+
* @param {Set<string>} [active] - Active extends path.
|
|
639
|
+
* @returns {import("../semantic/types.js").InterfaceMethodDeclaration[]} Flattened members.
|
|
640
|
+
*/
|
|
641
|
+
function flattenedTargetInterfaceMembers(interfaceId, interfaces, language, location, active = new Set()) {
|
|
642
|
+
const declaration = interfaces.find((candidate) => candidate.id == interfaceId)
|
|
643
|
+
|
|
644
|
+
if (!declaration) unsupportedCapability(language, "missing or invalid implemented interface reference", location)
|
|
645
|
+
if (active.has(interfaceId)) unsupportedCapability(language, "interface extends itself", declaration.location)
|
|
646
|
+
active.add(interfaceId)
|
|
647
|
+
/** @type {import("../semantic/types.js").InterfaceMethodDeclaration[]} */
|
|
648
|
+
const members = []
|
|
649
|
+
|
|
650
|
+
for (const parent of declaration.extends ?? []) {
|
|
651
|
+
for (const member of flattenedTargetInterfaceMembers(parent.declarationId, interfaces, language, location, active)) {
|
|
652
|
+
if (!members.some((candidate) => candidate.name == member.name)) members.push(member)
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
for (const method of declaration.methods) {
|
|
656
|
+
if (!members.some((candidate) => candidate.name == method.name)) members.push(method)
|
|
495
657
|
}
|
|
658
|
+
active.delete(interfaceId)
|
|
659
|
+
|
|
660
|
+
return members
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Compares two backend-validated semantic types structurally.
|
|
665
|
+
* @param {import("../semantic/types.js").SemanticFunctionReturnType} left - First type.
|
|
666
|
+
* @param {import("../semantic/types.js").SemanticFunctionReturnType} right - Second type.
|
|
667
|
+
* @returns {boolean} Whether the types are identical.
|
|
668
|
+
*/
|
|
669
|
+
function sameTargetType(left, right) {
|
|
670
|
+
return JSON.stringify(targetTypeIdentity(left)) == JSON.stringify(targetTypeIdentity(right))
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Reduces one semantic type to its comparable target identity.
|
|
675
|
+
* @param {import("../semantic/types.js").SemanticFunctionReturnType} type - Semantic type.
|
|
676
|
+
* @returns {unknown} Comparable identity.
|
|
677
|
+
*/
|
|
678
|
+
function targetTypeIdentity(type) {
|
|
679
|
+
if (typeof type == "string") return type
|
|
680
|
+
if (type.kind == "TypeReference") return type.name
|
|
681
|
+
if (type.kind == "TypeVariableReference") return {kind: "TypeVariableReference", parameterId: type.parameterId}
|
|
682
|
+
if (type.kind == "RecordType") return {declarationId: type.declarationId, kind: "RecordType"}
|
|
683
|
+
if (type.kind == "ReferenceType") return {declarationId: type.declarationId, kind: "ReferenceType"}
|
|
684
|
+
if (type.kind == "InterfaceType") return {declarationId: type.declarationId, kind: "InterfaceType"}
|
|
685
|
+
if (type.kind == "OwnedResourceType") return {kind: "OwnedResourceType", resourceId: type.resourceId}
|
|
686
|
+
if (type.kind == "OwnedReferenceType") return {declarationId: type.declarationId, kind: "OwnedReferenceType"}
|
|
687
|
+
if (type.kind == "ListType") return {elementType: targetTypeIdentity(type.elementType), kind: "ListType"}
|
|
688
|
+
if (type.kind == "OptionalType") return {kind: "OptionalType", valueType: targetTypeIdentity(type.valueType)}
|
|
689
|
+
if (type.kind == "OrderedMapType") return {keyType: targetTypeIdentity(type.keyType), kind: "OrderedMapType", valueType: targetTypeIdentity(type.valueType)}
|
|
690
|
+
|
|
691
|
+
return {keyType: targetTypeIdentity(type.keyType), kind: "MapType", valueType: targetTypeIdentity(type.valueType)}
|
|
496
692
|
}
|
|
497
693
|
|
|
498
694
|
/**
|
|
499
695
|
* Validates one reference callable's exact parameter shape and target names.
|
|
500
696
|
* @param {import("../semantic/types.js").Parameter[]} parameters - Candidate parameters.
|
|
501
|
-
* @param {"constructor" | "method"} owner - Callable kind.
|
|
697
|
+
* @param {"constructor" | "interface method" | "method"} owner - Callable kind.
|
|
502
698
|
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
503
699
|
* @param {import("../semantic/types.js").SourceLocation | undefined} ownerLocation - Fallback location.
|
|
504
700
|
* @returns {void}
|
|
@@ -2095,6 +2291,14 @@ export function emitType(writer, type, path, language, javaBoxed = false) {
|
|
|
2095
2291
|
})
|
|
2096
2292
|
return
|
|
2097
2293
|
}
|
|
2294
|
+
if (type.kind == "InterfaceType") {
|
|
2295
|
+
const declaration = writer.interfaceForId(type.declarationId)
|
|
2296
|
+
|
|
2297
|
+
writer.mapped(writer.interfaceNameForId(type.declarationId), {
|
|
2298
|
+
mappingKind: "exact", name: declaration.name, node: type, path, role: "type"
|
|
2299
|
+
})
|
|
2300
|
+
return
|
|
2301
|
+
}
|
|
2098
2302
|
if (type.kind == "OwnedResourceType") {
|
|
2099
2303
|
const resource = writer.resourceForId(type.resourceId)
|
|
2100
2304
|
writer.mapped(writer.capabilityNominalName(resource.name), {mappingKind: "exact", name: resource.name, node: type, path, role: "type"})
|
package/src/backends/writer.js
CHANGED
|
@@ -106,6 +106,7 @@ export class SourceWriter {
|
|
|
106
106
|
this.stdlibProviderShims = stdlibProviderShims
|
|
107
107
|
const programRecords = program?.modules.flatMap((programModule) => programModule.records ?? []) ?? module.records ?? []
|
|
108
108
|
const programClasses = program?.modules.flatMap((programModule) => programModule.classes ?? []) ?? module.classes ?? []
|
|
109
|
+
const programInterfaces = program?.modules.flatMap((programModule) => programModule.interfaces ?? []) ?? module.interfaces ?? []
|
|
109
110
|
const capabilityFailures = module.capabilities?.flatMap(({failures}) => failures) ?? []
|
|
110
111
|
const programErrors = program?.modules.flatMap((programModule) => [
|
|
111
112
|
...(programModule.errors ?? []),
|
|
@@ -115,6 +116,7 @@ export class SourceWriter {
|
|
|
115
116
|
|
|
116
117
|
this.records = new Map(programRecords.map((record) => [record.id, record]))
|
|
117
118
|
this.classes = new Map(programClasses.map((declaration) => [declaration.id, declaration]))
|
|
119
|
+
this.interfaces = new Map(programInterfaces.map((declaration) => [declaration.id, declaration]))
|
|
118
120
|
this.errors = new Map(programErrors.map((error) => [error.id, error]))
|
|
119
121
|
this.resources = new Map((module.capabilities?.flatMap(({resources}) => resources) ?? []).map((resource) => [resource.id, resource]))
|
|
120
122
|
this.effectOperations = new Map((module.capabilities?.flatMap(({operations}) => operations) ?? []).map((operation) => [operation.id, operation]))
|
|
@@ -123,7 +125,14 @@ export class SourceWriter {
|
|
|
123
125
|
this.fields = new Map(programRecords.flatMap((record) => record.fields.map((field) => [field.id, field])))
|
|
124
126
|
this.fieldRecords = new Map(programRecords.flatMap((record) => record.fields.map((field) => [field.id, record])))
|
|
125
127
|
this.privateFields = new Map(programClasses.flatMap((declaration) => declaration.fields.map((field) => [field.id, field])))
|
|
126
|
-
|
|
128
|
+
/** @type {Map<string, import("../semantic/types.js").InterfaceMethodDeclaration | import("../semantic/types.js").MethodDeclaration>} */
|
|
129
|
+
this.methods = new Map()
|
|
130
|
+
for (const declaration of programClasses) {
|
|
131
|
+
for (const method of declaration.methods) this.methods.set(/** @type {string} */ (method.id), method)
|
|
132
|
+
}
|
|
133
|
+
for (const declaration of programInterfaces) {
|
|
134
|
+
for (const method of declaration.methods) this.methods.set(/** @type {string} */ (method.id), method)
|
|
135
|
+
}
|
|
127
136
|
this.typeParameters = new Map([...programRecords, ...programFunctions].flatMap((declaration) =>
|
|
128
137
|
(declaration.typeParameters ?? []).map((parameter) => [parameter.id, parameter])))
|
|
129
138
|
this.referenceClassEmissionDepth = 0
|
|
@@ -131,6 +140,7 @@ export class SourceWriter {
|
|
|
131
140
|
this.declarationModules = new Map(program?.modules.flatMap((programModule) => [
|
|
132
141
|
...(programModule.records ?? []).map((declaration) => /** @type {const} */ ([/** @type {string} */ (declaration.id), programModule])),
|
|
133
142
|
...(programModule.classes ?? []).map((declaration) => /** @type {const} */ ([/** @type {string} */ (declaration.id), programModule])),
|
|
143
|
+
...(programModule.interfaces ?? []).map((declaration) => /** @type {const} */ ([/** @type {string} */ (declaration.id), programModule])),
|
|
134
144
|
...(programModule.errors ?? []).map((declaration) => /** @type {const} */ ([/** @type {string} */ (declaration.id), programModule])),
|
|
135
145
|
...programModule.functions.map((declaration) => /** @type {const} */ ([/** @type {string} */ (declaration.id), programModule]))
|
|
136
146
|
]) ?? [])
|
|
@@ -282,6 +292,34 @@ export class SourceWriter {
|
|
|
282
292
|
return declaration.name
|
|
283
293
|
}
|
|
284
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Resolves one validated interface declaration.
|
|
297
|
+
* @param {string} declarationId - Stable interface identity.
|
|
298
|
+
* @returns {import("../semantic/types.js").InterfaceDeclaration} Interface declaration.
|
|
299
|
+
*/
|
|
300
|
+
interfaceForId(declarationId) {
|
|
301
|
+
const declaration = this.interfaces.get(declarationId)
|
|
302
|
+
|
|
303
|
+
if (!declaration) throw new RangeError(`Unknown validated interface identity '${declarationId}'.`)
|
|
304
|
+
|
|
305
|
+
return declaration
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Returns the target spelling of one interface.
|
|
310
|
+
* @param {string} declarationId - Stable interface declaration identity.
|
|
311
|
+
* @returns {string} Target spelling.
|
|
312
|
+
*/
|
|
313
|
+
interfaceNameForId(declarationId) {
|
|
314
|
+
const declaration = this.interfaceForId(declarationId)
|
|
315
|
+
const imported = this.#programModule()?.imports.find((item) => item.declarationId == declarationId)
|
|
316
|
+
|
|
317
|
+
if (imported && (this.language == "javascript" || this.language == "typescript" || this.language == "php")) {
|
|
318
|
+
return this.importNameFor(imported)
|
|
319
|
+
}
|
|
320
|
+
return declaration.name
|
|
321
|
+
}
|
|
322
|
+
|
|
285
323
|
/**
|
|
286
324
|
* Resolves one validated owned-resource declaration.
|
|
287
325
|
* @param {string} resourceId - Stable resource identity.
|
|
@@ -318,9 +356,9 @@ export class SourceWriter {
|
|
|
318
356
|
}
|
|
319
357
|
|
|
320
358
|
/**
|
|
321
|
-
* Resolves one validated receiver method.
|
|
359
|
+
* Resolves one validated receiver method, class-owned or interface-declared.
|
|
322
360
|
* @param {string} methodId - Stable method identity.
|
|
323
|
-
* @returns {import("../semantic/types.js").MethodDeclaration} Method declaration.
|
|
361
|
+
* @returns {import("../semantic/types.js").MethodDeclaration | import("../semantic/types.js").InterfaceMethodDeclaration} Method declaration.
|
|
324
362
|
*/
|
|
325
363
|
methodForId(methodId) {
|
|
326
364
|
const method = this.methods.get(methodId)
|
package/src/frontends/java.js
CHANGED
|
@@ -49,7 +49,7 @@ const referenceMethodHooks = new Set(["clone", "equals", "finalize", "getClass",
|
|
|
49
49
|
* @property {Map<string, import("../semantic/types.js").ErrorDeclaration>} errors - Errors by identity.
|
|
50
50
|
* @property {Set<string>} erasedBindingNames - Source bindings consumed by exact semantic folds in this scope.
|
|
51
51
|
* @property {Map<string, JavaFunctionSignature>} functions - Explicit module function signatures.
|
|
52
|
-
* @property {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
52
|
+
* @property {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
|
|
53
53
|
* @property {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Records by identity.
|
|
54
54
|
* @property {import("../semantic/types.js").SemanticFunctionReturnType | undefined} returnType - Enclosing return type.
|
|
55
55
|
* @property {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
@@ -260,6 +260,9 @@ function convertExpression(node, filename, source, context, expectedType) {
|
|
|
260
260
|
kind: /** @type {const} */ ("ReferenceConstruction"), location,
|
|
261
261
|
reference: referenceType(/** @type {string} */ (declaration.id), classLocation)}, {class: classLocation})
|
|
262
262
|
}
|
|
263
|
+
if (declaration.kind == "InterfaceDeclaration") {
|
|
264
|
+
return unsupportedSyntax("java", "construction of an interface", nodeLocation(typeNode, filename, source))
|
|
265
|
+
}
|
|
263
266
|
if (declaration.kind == "EffectResourceDeclaration") {
|
|
264
267
|
return unsupportedSyntax("java", "owned resource construction", nodeLocation(typeNode, filename, source))
|
|
265
268
|
}
|
|
@@ -1583,7 +1586,7 @@ function convertJavaOrderedMapSequence(statements, start, filename, source, cont
|
|
|
1583
1586
|
* @param {import("../semantic/types.js").SourceLocation} location - Source location.
|
|
1584
1587
|
* @param {string} filename - Source filename.
|
|
1585
1588
|
* @param {string} source - Complete source.
|
|
1586
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1589
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1587
1590
|
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
1588
1591
|
* @returns {import("../semantic/types.js").SemanticValueType} Semantic type.
|
|
1589
1592
|
*/
|
|
@@ -1685,7 +1688,7 @@ function convertType(sourceType, subject, location, filename, source, recordName
|
|
|
1685
1688
|
* @param {import("../semantic/types.js").SourceLocation} location - Owning location.
|
|
1686
1689
|
* @param {string} filename - Source filename.
|
|
1687
1690
|
* @param {string} source - Complete source.
|
|
1688
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1691
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1689
1692
|
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
1690
1693
|
* @returns {import("../semantic/types.js").SemanticValueType} Semantic type.
|
|
1691
1694
|
*/
|
|
@@ -1717,7 +1720,7 @@ function convertJavaTypeArgument(node, subject, location, filename, source, reco
|
|
|
1717
1720
|
* @param {import("../semantic/types.js").SourceLocation} location - Function location.
|
|
1718
1721
|
* @param {string} filename - Source filename.
|
|
1719
1722
|
* @param {string} source - Complete source.
|
|
1720
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1723
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1721
1724
|
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
1722
1725
|
* @returns {import("../semantic/types.js").SemanticFunctionReturnType} Semantic return type.
|
|
1723
1726
|
*/
|
|
@@ -1773,7 +1776,7 @@ function convertJavaTypeParameters(node, ownerId, filename, source) {
|
|
|
1773
1776
|
* @param {import("@lezer/common").SyntaxNode} node - Method declaration.
|
|
1774
1777
|
* @param {string} filename - Source filename.
|
|
1775
1778
|
* @param {string} source - Complete source.
|
|
1776
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1779
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1777
1780
|
* @param {boolean} [programFunction] - Whether the canonical project profile admits explicit public or private visibility.
|
|
1778
1781
|
* @param {string} [ownerId] - Stable function identity.
|
|
1779
1782
|
* @param {boolean} [referenceModule] - Whether package-visible static functions are required by sibling reference classes.
|
|
@@ -1843,7 +1846,7 @@ function convertFunctionSignature(node, filename, source, recordNames, programFu
|
|
|
1843
1846
|
* @param {import("@lezer/common").SyntaxNode} node - Method declaration.
|
|
1844
1847
|
* @param {JavaFunctionSignature} signature - Preconverted function signature.
|
|
1845
1848
|
* @param {Map<string, JavaFunctionSignature>} functions - Module function signatures.
|
|
1846
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1849
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1847
1850
|
* @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Record declarations by identity.
|
|
1848
1851
|
* @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Reference classes by identity.
|
|
1849
1852
|
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errorNames - Error declarations by source name.
|
|
@@ -1932,7 +1935,7 @@ function convertJavaError(node, declaration, filename, source, programError = fa
|
|
|
1932
1935
|
* Reads one package-private Java reference constructor or instance-method signature.
|
|
1933
1936
|
* @param {import("@lezer/common").SyntaxNode} node - Constructor or method declaration.
|
|
1934
1937
|
* @param {import("../semantic/types.js").ClassDeclaration} declaration - Declaring class.
|
|
1935
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1938
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1936
1939
|
* @param {boolean} constructor - Whether this is the sole constructor.
|
|
1937
1940
|
* @param {string} filename - Source filename.
|
|
1938
1941
|
* @param {string} source - Complete source.
|
|
@@ -1978,7 +1981,7 @@ function convertJavaClassCallableSignature(node, declaration, nominalNames, cons
|
|
|
1978
1981
|
* Converts the bounded final/private-field Java reference-class profile.
|
|
1979
1982
|
* @param {import("@lezer/common").SyntaxNode} node - Class declaration.
|
|
1980
1983
|
* @param {import("../semantic/types.js").ClassDeclaration} declaration - Predeclared class identity.
|
|
1981
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1984
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1982
1985
|
* @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Records by identity.
|
|
1983
1986
|
* @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Classes by identity.
|
|
1984
1987
|
* @param {Map<string, JavaFunctionSignature>} functions - Top-level signatures.
|
|
@@ -2276,7 +2279,7 @@ function convertWhile(node, filename, source, context) {
|
|
|
2276
2279
|
* @param {string} filename - Source filename.
|
|
2277
2280
|
* @param {string} source - Complete source.
|
|
2278
2281
|
* @param {Map<string, JavaFunctionSignature>} functions - Module function signatures.
|
|
2279
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
2282
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
|
|
2280
2283
|
* @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Record declarations by identity.
|
|
2281
2284
|
* @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Reference classes by identity.
|
|
2282
2285
|
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errorNames - Error declarations by source name.
|
|
@@ -2442,7 +2445,7 @@ export function parseJava({capabilities = [], filename, source, program}) {
|
|
|
2442
2445
|
kind: /** @type {const} */ ("ClassDeclaration"), location: nodeLocation(classNode, filename, source), methods: [],
|
|
2443
2446
|
name: nodeText(requiredChild(classNode, "Definition", filename, source), source)
|
|
2444
2447
|
}))
|
|
2445
|
-
const recordNames = /** @type {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} */ (
|
|
2448
|
+
const recordNames = /** @type {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} */ (
|
|
2446
2449
|
new Map(recordDeclarations.map((declaration) => /** @type {const} */ ([declaration.name, declaration]))))
|
|
2447
2450
|
for (const resource of capabilities.flatMap(({resources}) => resources)) recordNames.set(resource.name, resource)
|
|
2448
2451
|
for (const declaration of referenceDeclarations) recordNames.set(declaration.name, declaration)
|