semantifold 0.12.0 → 0.14.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 -0
- package/build/src/backends/effects.js +5 -2
- package/build/src/backends/java.js +33 -14
- package/build/src/backends/javascript.js +83 -18
- package/build/src/backends/php.d.ts.map +1 -1
- package/build/src/backends/php.js +88 -16
- package/build/src/backends/ruby.js +37 -12
- 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 +299 -42
- package/build/src/backends/typescript.js +83 -18
- 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 +9 -1
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +90 -43
- package/build/src/frontends/javascript-typescript.d.ts +3 -1
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +133 -52
- package/build/src/frontends/php.d.ts +13 -1
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +226 -59
- package/build/src/frontends/ruby.d.ts +4 -1
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +113 -37
- 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 +14 -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 +20 -6
- package/build/src/semantic/lifetime.js +28 -4
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +61 -9
- package/build/src/semantic/types.d.ts +214 -23
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +58 -11
- 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 +450 -60
- package/docs/adr/20261026-typed-error-handling-extension.md +55 -0
- package/docs/adr-bounded-subtyping.md +180 -0
- package/docs/adr-numeric-tower.md +156 -0
- package/docs/architecture.md +1 -1
- package/docs/language-support.md +8 -8
- package/docs/testing.md +5 -3
- package/package.json +1 -1
- package/src/backends/effects.js +3 -1
- package/src/backends/java.js +33 -14
- package/src/backends/javascript.js +83 -18
- package/src/backends/php.js +89 -16
- package/src/backends/ruby.js +36 -12
- package/src/backends/scalars.js +1 -1
- package/src/backends/shared.js +298 -47
- package/src/backends/typescript.js +83 -18
- package/src/backends/writer.js +41 -3
- package/src/frontends/java.js +95 -43
- package/src/frontends/javascript-typescript.js +145 -53
- package/src/frontends/php.js +243 -58
- package/src/frontends/ruby.js +117 -35
- package/src/frontends/types.js +26 -8
- package/src/language-registry.js +13 -5
- package/src/mapping.js +3 -1
- package/src/semantic/effects.js +18 -5
- package/src/semantic/lifetime.js +29 -3
- package/src/semantic/provenance.js +60 -14
- package/src/semantic/types.js +62 -10
- package/src/semantic/validate.js +481 -61
package/src/backends/effects.js
CHANGED
|
@@ -266,6 +266,7 @@ function semanticTypeForIdentity(identity) {
|
|
|
266
266
|
if (identity.kind == "RecordType") return {arguments: identity.arguments?.map(semanticTypeForValueIdentity),
|
|
267
267
|
declarationId: identity.declarationId, kind: "RecordType"}
|
|
268
268
|
if (identity.kind == "ReferenceType") return {declarationId: identity.declarationId, kind: "ReferenceType"}
|
|
269
|
+
if (identity.kind == "InterfaceType") return {declarationId: identity.declarationId, kind: "InterfaceType"}
|
|
269
270
|
if (identity.kind == "OwnedResourceType") return {kind: "OwnedResourceType", resourceId: identity.resourceId}
|
|
270
271
|
if (identity.kind == "OwnedReferenceType") return {declarationId: identity.declarationId, kind: "OwnedReferenceType"}
|
|
271
272
|
return {kind: "TypeVariableReference", location: /** @type {never} */ (undefined), parameterId: identity.parameterId}
|
|
@@ -478,7 +479,8 @@ function blockExcluded(block) {
|
|
|
478
479
|
}
|
|
479
480
|
const nested = statement.kind == "IfStatement" ? [statement.consequent, ...(statement.alternate ? [statement.alternate] : [])] :
|
|
480
481
|
statement.kind == "ForEachStatement" || statement.kind == "ForEachMapStatement" || statement.kind == "WhileStatement" ? [statement.body] :
|
|
481
|
-
statement.kind == "TryStatement" ? [statement.body, statement.
|
|
482
|
+
statement.kind == "TryStatement" ? [statement.body, ...statement.handlers.map((handler) => handler.catchBody),
|
|
483
|
+
...(statement.finallyBody ? [statement.finallyBody] : [])] : []
|
|
482
484
|
for (const child of nested) {
|
|
483
485
|
const found = blockExcluded(child)
|
|
484
486
|
if (found) return found
|
package/src/backends/java.js
CHANGED
|
@@ -391,9 +391,15 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
391
391
|
writer.synthetic(");\n", "error construction close", [statement.error], [`${path}/error`])
|
|
392
392
|
return
|
|
393
393
|
}
|
|
394
|
+
if (statement.kind == "RethrowStatement") {
|
|
395
|
+
writer.mapped("throw", {mappingKind: "anchor", node: statement, path})
|
|
396
|
+
writer.synthetic(" ", "throw spacing", [statement], [path])
|
|
397
|
+
writer.mapped(statement.binding, {mappingKind: "exact", node: statement.bindingNode, path: `${path}/bindingNode`, role: "name"})
|
|
398
|
+
writer.mapped(";", {mappingKind: "anchor", node: statement, path})
|
|
399
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
400
|
+
return
|
|
401
|
+
}
|
|
394
402
|
if (statement.kind == "TryStatement") {
|
|
395
|
-
const caught = writer.errorForId(statement.catchType.declarationId)
|
|
396
|
-
|
|
397
403
|
writer.mapped("try", {mappingKind: "anchor", node: statement, path})
|
|
398
404
|
writer.synthetic(" ", "try spacing", [statement], [path])
|
|
399
405
|
writer.mapped("{", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
@@ -401,18 +407,31 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
401
407
|
emitBlock(writer, statement.body, `${indent} `, `${path}/body`)
|
|
402
408
|
writer.synthetic(indent, "indentation", [statement], [path])
|
|
403
409
|
writer.mapped("}", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
410
|
+
for (const [index, handler] of statement.handlers.entries()) {
|
|
411
|
+
const handlerPath = `${path}/handlers/${index}`
|
|
412
|
+
const caught = writer.errorForId(handler.catchType.declarationId)
|
|
413
|
+
|
|
414
|
+
writer.synthetic(" ", "catch spacing", [handler], [handlerPath])
|
|
415
|
+
writer.mapped("catch", {mappingKind: "anchor", node: handler, path: handlerPath, role: "catch"})
|
|
416
|
+
writer.synthetic(" (", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
417
|
+
writer.mapped(writer.errorNameForId(handler.catchType.declarationId), {mappingKind: "exact", name: caught.name, node: handler.catchType, path: `${handlerPath}/catchType`, role: "type"})
|
|
418
|
+
writer.synthetic(" ", "catch binding spacing", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
419
|
+
writer.mapped(handler.catchBinding.name, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
420
|
+
writer.synthetic(") ", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
421
|
+
writer.mapped("{", {mappingKind: "anchor", node: handler.catchBody, path: `${handlerPath}/catchBody`})
|
|
422
|
+
writer.synthetic("\n", "line break", [handler], [handlerPath])
|
|
423
|
+
emitBlock(writer, handler.catchBody, `${indent} `, `${handlerPath}/catchBody`)
|
|
424
|
+
writer.synthetic(indent, "indentation", [handler], [handlerPath])
|
|
425
|
+
writer.mapped("}", {mappingKind: "anchor", node: handler.catchBody, path: `${handlerPath}/catchBody`})
|
|
426
|
+
}
|
|
427
|
+
if (statement.finallyBody) {
|
|
428
|
+
writer.synthetic(" ", "finally spacing", [statement], [path])
|
|
429
|
+
writer.mapped("finally", {mappingKind: "anchor", node: statement, path, role: "finally"})
|
|
430
|
+
writer.synthetic(" {\n", "finally scaffolding", [statement], [path])
|
|
431
|
+
emitBlock(writer, statement.finallyBody, `${indent} `, `${path}/finallyBody`)
|
|
432
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
433
|
+
writer.mapped("}", {mappingKind: "anchor", node: statement.finallyBody, path: `${path}/finallyBody`})
|
|
434
|
+
}
|
|
416
435
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
417
436
|
return
|
|
418
437
|
}
|
|
@@ -427,10 +427,14 @@ function emitStatement(writer, statement, indent, path, canonicalizeZero) {
|
|
|
427
427
|
writer.synthetic(")\n", "error construction close", [statement.error], [`${path}/error`])
|
|
428
428
|
return
|
|
429
429
|
}
|
|
430
|
+
if (statement.kind == "RethrowStatement") {
|
|
431
|
+
writer.mapped("throw", {mappingKind: "anchor", node: statement, path})
|
|
432
|
+
writer.synthetic(" ", "throw spacing", [statement], [path])
|
|
433
|
+
writer.mapped(statement.binding, {mappingKind: "exact", node: statement.bindingNode, path: `${path}/bindingNode`, role: "name"})
|
|
434
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
435
|
+
return
|
|
436
|
+
}
|
|
430
437
|
if (statement.kind == "TryStatement") {
|
|
431
|
-
const caught = writer.errorForId(statement.catchType.declarationId)
|
|
432
|
-
const targetName = writer.errorNameForId(statement.catchType.declarationId)
|
|
433
|
-
|
|
434
438
|
writer.mapped("try", {mappingKind: "anchor", node: statement, path})
|
|
435
439
|
writer.synthetic(" ", "try spacing", [statement], [path])
|
|
436
440
|
writer.mapped("{", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
@@ -438,21 +442,15 @@ function emitStatement(writer, statement, indent, path, canonicalizeZero) {
|
|
|
438
442
|
emitBlock(writer, statement.body, `${indent} `, `${path}/body`, canonicalizeZero)
|
|
439
443
|
writer.synthetic(indent, "indentation", [statement], [path])
|
|
440
444
|
writer.mapped("}", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
writer.synthetic(`)) {\n${indent} throw `, "unmatched error propagation", [statement], [path])
|
|
451
|
-
writer.mapped(statement.catchBinding.name, {mappingKind: "exact", node: statement.catchBinding, path: `${path}/catchBinding`, role: "name"})
|
|
452
|
-
writer.synthetic(`\n${indent} }\n`, "unmatched error propagation", [statement], [path])
|
|
453
|
-
emitBlock(writer, statement.catchBody, `${indent} `, `${path}/catchBody`, canonicalizeZero)
|
|
454
|
-
writer.synthetic(indent, "indentation", [statement], [path])
|
|
455
|
-
writer.mapped("}", {mappingKind: "anchor", node: statement.catchBody, path: `${path}/catchBody`})
|
|
445
|
+
if (statement.handlers.length > 0) emitJavaScriptHandler(writer, statement, 0, indent, path, canonicalizeZero)
|
|
446
|
+
if (statement.finallyBody) {
|
|
447
|
+
writer.synthetic(" ", "finally spacing", [statement], [path])
|
|
448
|
+
writer.mapped("finally", {mappingKind: "anchor", node: statement, path, role: "finally"})
|
|
449
|
+
writer.synthetic(" {\n", "finally scaffolding", [statement], [path])
|
|
450
|
+
emitBlock(writer, statement.finallyBody, `${indent} `, `${path}/finallyBody`, canonicalizeZero)
|
|
451
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
452
|
+
writer.mapped("}", {mappingKind: "anchor", node: statement.finallyBody, path: `${path}/finallyBody`})
|
|
453
|
+
}
|
|
456
454
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
457
455
|
return
|
|
458
456
|
}
|
|
@@ -505,6 +503,73 @@ function emitStatement(writer, statement, indent, path, canonicalizeZero) {
|
|
|
505
503
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
506
504
|
}
|
|
507
505
|
|
|
506
|
+
/**
|
|
507
|
+
* Emits one ordered JavaScript catch handler with its exact nominal guard.
|
|
508
|
+
*
|
|
509
|
+
* JavaScript has no sibling catch clauses, so handler k > 0 hangs off handler k - 1's
|
|
510
|
+
* unmatched rethrow inside a nested try. Each handler body sits outside its own guard,
|
|
511
|
+
* so a raise from one handler body escapes outward instead of being caught by a later
|
|
512
|
+
* sibling handler, exactly like native ordered multi-catch.
|
|
513
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
514
|
+
* @param {import("../semantic/types.js").TryStatement} statement - Semantic try statement.
|
|
515
|
+
* @param {number} index - Zero-based ordered handler index.
|
|
516
|
+
* @param {string} indent - Leading indentation for this catch clause.
|
|
517
|
+
* @param {string} path - Exact JSON Pointer for the owning try statement.
|
|
518
|
+
* @param {boolean} canonicalizeZero - Whether scalar output canonicalizes zero.
|
|
519
|
+
* @returns {void}
|
|
520
|
+
*/
|
|
521
|
+
function emitJavaScriptHandler(writer, statement, index, indent, path, canonicalizeZero) {
|
|
522
|
+
const handler = statement.handlers[index]
|
|
523
|
+
const handlerPath = `${path}/handlers/${index}`
|
|
524
|
+
const bodyIndent = `${indent} `
|
|
525
|
+
const caught = writer.errorForId(handler.catchType.declarationId)
|
|
526
|
+
const targetName = writer.errorNameForId(handler.catchType.declarationId)
|
|
527
|
+
|
|
528
|
+
writer.synthetic(" ", "catch spacing", [statement], [path])
|
|
529
|
+
writer.mapped("catch", {mappingKind: "anchor", node: handler, path: handlerPath, role: "catch"})
|
|
530
|
+
writer.synthetic(" (", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
531
|
+
writer.mapped(handler.catchBinding.name, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
532
|
+
writer.synthetic(") {\n", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
533
|
+
writer.synthetic(`${bodyIndent}if (!(`, "exact catch guard", [handler], [handlerPath])
|
|
534
|
+
writer.mapped(handler.catchBinding.name, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
535
|
+
writer.synthetic(" instanceof ", "exact catch guard", [handler], [handlerPath])
|
|
536
|
+
writer.mapped(targetName, {mappingKind: "exact", name: caught.name, node: handler.catchType, path: `${handlerPath}/catchType`, role: "type"})
|
|
537
|
+
writer.synthetic(")) {\n", "exact catch guard", [handler], [handlerPath])
|
|
538
|
+
if (index + 1 < statement.handlers.length) {
|
|
539
|
+
emitJavaScriptDispatch(writer, statement, index, indent, path, canonicalizeZero)
|
|
540
|
+
writer.synthetic(`${bodyIndent}} else {\n`, "matched error handling", [handler], [handlerPath])
|
|
541
|
+
emitBlock(writer, handler.catchBody, `${bodyIndent} `, `${handlerPath}/catchBody`, canonicalizeZero)
|
|
542
|
+
writer.synthetic(`${bodyIndent}}\n`, "matched error handling close", [handler], [handlerPath])
|
|
543
|
+
} else {
|
|
544
|
+
writer.synthetic(`${bodyIndent} throw `, "unmatched error propagation", [handler], [handlerPath])
|
|
545
|
+
writer.mapped(handler.catchBinding.name, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
546
|
+
writer.synthetic(`\n${bodyIndent}}\n`, "unmatched error propagation", [handler], [handlerPath])
|
|
547
|
+
emitBlock(writer, handler.catchBody, bodyIndent, `${handlerPath}/catchBody`, canonicalizeZero)
|
|
548
|
+
}
|
|
549
|
+
writer.synthetic(indent, "indentation", [handler], [handlerPath])
|
|
550
|
+
writer.mapped("}", {mappingKind: "anchor", node: handler.catchBody, path: `${handlerPath}/catchBody`})
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Emits the nested re-dispatch of one unmatched caught value to the remaining ordered handlers.
|
|
555
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
556
|
+
* @param {import("../semantic/types.js").TryStatement} statement - Semantic try statement.
|
|
557
|
+
* @param {number} index - Index of the handler whose value is re-dispatched.
|
|
558
|
+
* @param {string} indent - Leading indentation for this dispatch try.
|
|
559
|
+
* @param {string} path - Exact JSON Pointer for the owning try statement.
|
|
560
|
+
* @param {boolean} canonicalizeZero - Whether scalar output canonicalizes zero.
|
|
561
|
+
* @returns {void}
|
|
562
|
+
*/
|
|
563
|
+
function emitJavaScriptDispatch(writer, statement, index, indent, path, canonicalizeZero) {
|
|
564
|
+
writer.synthetic(`${indent} try {\n`, "ordered handler dispatch", [statement], [path])
|
|
565
|
+
writer.synthetic(`${indent} throw `, "ordered handler dispatch", [statement], [path])
|
|
566
|
+
writer.mapped(statement.handlers[index].catchBinding.name, {
|
|
567
|
+
mappingKind: "exact", node: statement.handlers[index].catchBinding, path: `${path}/handlers/${index}/catchBinding`, role: "name"
|
|
568
|
+
})
|
|
569
|
+
writer.synthetic(`\n${indent} }`, "ordered handler dispatch", [statement], [path])
|
|
570
|
+
emitJavaScriptHandler(writer, statement, index + 1, `${indent} `, path, canonicalizeZero)
|
|
571
|
+
}
|
|
572
|
+
|
|
508
573
|
/**
|
|
509
574
|
* Emits one JavaScript local statement and its JSDoc type carrier.
|
|
510
575
|
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
package/src/backends/php.js
CHANGED
|
@@ -15,6 +15,7 @@ export function generatePhp(module, writer) {
|
|
|
15
15
|
emitEffectSupport(writer, module, "php")
|
|
16
16
|
const records = module.records ?? []
|
|
17
17
|
const classes = module.classes ?? []
|
|
18
|
+
const interfaces = module.interfaces ?? []
|
|
18
19
|
const errors = module.errors ?? []
|
|
19
20
|
|
|
20
21
|
errors.forEach((error, index) => {
|
|
@@ -72,6 +73,12 @@ export function generatePhp(module, writer) {
|
|
|
72
73
|
})
|
|
73
74
|
if (records.length > 0) writer.synthetic("\n\n", "record/function separator", [module])
|
|
74
75
|
|
|
76
|
+
interfaces.forEach((declaration, interfaceIndex) => {
|
|
77
|
+
if (interfaceIndex > 0) writer.synthetic("\n\n", "interface separator", [declaration], [`/interfaces/${interfaceIndex}`])
|
|
78
|
+
emitInterface(writer, declaration, interfaceIndex)
|
|
79
|
+
})
|
|
80
|
+
if (interfaces.length > 0) writer.synthetic("\n\n", "interface/class separator", [module])
|
|
81
|
+
|
|
75
82
|
classes.forEach((declaration, classIndex) => {
|
|
76
83
|
if (classIndex > 0) writer.synthetic("\n\n", "reference class separator", [declaration], [`/classes/${classIndex}`])
|
|
77
84
|
emitReferenceClass(writer, declaration, classIndex)
|
|
@@ -156,6 +163,15 @@ function emitReferenceClass(writer, declaration, classIndex) {
|
|
|
156
163
|
writer.mapped("final class", {mappingKind: "anchor", node: declaration, path})
|
|
157
164
|
writer.synthetic(" ", "reference class spacing", [declaration], [path])
|
|
158
165
|
writer.mapped(declaration.name, {mappingKind: "exact", node: declaration, path, role: "name"})
|
|
166
|
+
const implemented = declaration.implements ?? []
|
|
167
|
+
|
|
168
|
+
if (implemented.length > 0) {
|
|
169
|
+
writer.mapped(" implements ", {mappingKind: "anchor", node: declaration, path})
|
|
170
|
+
implemented.forEach((reference, referenceIndex) => {
|
|
171
|
+
if (referenceIndex > 0) writer.mapped(", ", {mappingKind: "anchor", node: declaration, path})
|
|
172
|
+
emitType(writer, reference, `${path}/implements/${referenceIndex}`, "php")
|
|
173
|
+
})
|
|
174
|
+
}
|
|
159
175
|
writer.synthetic(" {\n", "reference class open", [declaration], [path])
|
|
160
176
|
declaration.fields.forEach((field, index) => {
|
|
161
177
|
const fieldPath = `${path}/fields/${index}`
|
|
@@ -191,6 +207,44 @@ function emitReferenceClass(writer, declaration, classIndex) {
|
|
|
191
207
|
writer.mapped("}", {mappingKind: "anchor", node: declaration, path})
|
|
192
208
|
}
|
|
193
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Emits one PHP native interface declaration with signature-only members.
|
|
212
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
213
|
+
* @param {import("../semantic/types.js").InterfaceDeclaration} declaration - Semantic interface.
|
|
214
|
+
* @param {number} interfaceIndex - Declaration index.
|
|
215
|
+
* @returns {void}
|
|
216
|
+
*/
|
|
217
|
+
function emitInterface(writer, declaration, interfaceIndex) {
|
|
218
|
+
const path = `/interfaces/${interfaceIndex}`
|
|
219
|
+
|
|
220
|
+
writer.mapped("interface", {mappingKind: "anchor", node: declaration, path})
|
|
221
|
+
writer.synthetic(" ", "interface spacing", [declaration], [path])
|
|
222
|
+
writer.mapped(declaration.name, {mappingKind: "exact", node: declaration, path, role: "name"})
|
|
223
|
+
const parents = declaration.extends ?? []
|
|
224
|
+
|
|
225
|
+
if (parents.length > 0) {
|
|
226
|
+
writer.mapped(" extends ", {mappingKind: "anchor", node: declaration, path})
|
|
227
|
+
parents.forEach((parent, parentIndex) => {
|
|
228
|
+
if (parentIndex > 0) writer.mapped(", ", {mappingKind: "anchor", node: declaration, path})
|
|
229
|
+
emitType(writer, parent, `${path}/extends/${parentIndex}`, "php")
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
writer.synthetic(" {\n", "interface open", [declaration], [path])
|
|
233
|
+
declaration.methods.forEach((method, methodIndex) => {
|
|
234
|
+
const methodPath = `${path}/methods/${methodIndex}`
|
|
235
|
+
|
|
236
|
+
if (methodIndex > 0) writer.synthetic("\n", "line break", [method], [methodPath])
|
|
237
|
+
writer.synthetic(" public function ", "interface method scaffolding", [method], [methodPath])
|
|
238
|
+
writer.mapped(method.name, {mappingKind: "exact", node: method, path: methodPath, role: "name"})
|
|
239
|
+
emitPhpParameters(writer, method.parameters, `${methodPath}/parameters`)
|
|
240
|
+
writer.synthetic(": ", "interface method return separator", [method], [methodPath])
|
|
241
|
+
emitNativePhpType(writer, method.returnType, `${methodPath}/returnType`)
|
|
242
|
+
writer.mapped(";", {mappingKind: "anchor", node: method, path: methodPath})
|
|
243
|
+
})
|
|
244
|
+
writer.synthetic("\n", "line break", [declaration], [path])
|
|
245
|
+
writer.mapped("}", {mappingKind: "anchor", node: declaration, path})
|
|
246
|
+
}
|
|
247
|
+
|
|
194
248
|
/**
|
|
195
249
|
* Emits one PHP parameter list.
|
|
196
250
|
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
@@ -355,7 +409,7 @@ function emitNativePhpType(writer, type, path) {
|
|
|
355
409
|
})
|
|
356
410
|
return
|
|
357
411
|
}
|
|
358
|
-
if (type.kind == "ReferenceType" || type.kind == "OwnedResourceType" || type.kind == "OwnedReferenceType") {
|
|
412
|
+
if (type.kind == "ReferenceType" || type.kind == "InterfaceType" || type.kind == "OwnedResourceType" || type.kind == "OwnedReferenceType") {
|
|
359
413
|
emitType(writer, type, path, "php")
|
|
360
414
|
return
|
|
361
415
|
}
|
|
@@ -365,7 +419,7 @@ function emitNativePhpType(writer, type, path) {
|
|
|
365
419
|
}
|
|
366
420
|
if (type.kind == "OptionalType") {
|
|
367
421
|
writer.mapped("?", {mappingKind: "exact", node: type, path, role: "type"})
|
|
368
|
-
if (type.valueType.kind == "RecordType") emitNativePhpType(writer, type.valueType, `${path}/valueType`)
|
|
422
|
+
if (type.valueType.kind == "RecordType" || type.valueType.kind == "InterfaceType") emitNativePhpType(writer, type.valueType, `${path}/valueType`)
|
|
369
423
|
else if (type.valueType.kind == "TypeReference") emitType(writer, type.valueType, `${path}/valueType`, "php")
|
|
370
424
|
else writer.mapped("array", {mappingKind: "anchor", node: type, path})
|
|
371
425
|
return
|
|
@@ -489,9 +543,15 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
489
543
|
writer.synthetic(");\n", "error construction close", [statement.error], [`${path}/error`])
|
|
490
544
|
return
|
|
491
545
|
}
|
|
546
|
+
if (statement.kind == "RethrowStatement") {
|
|
547
|
+
writer.mapped("throw", {mappingKind: "anchor", node: statement, path})
|
|
548
|
+
writer.synthetic(" ", "throw spacing", [statement], [path])
|
|
549
|
+
writer.mapped(`$${statement.binding}`, {mappingKind: "exact", node: statement.bindingNode, path: `${path}/bindingNode`, role: "name"})
|
|
550
|
+
writer.mapped(";", {mappingKind: "anchor", node: statement, path})
|
|
551
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
552
|
+
return
|
|
553
|
+
}
|
|
492
554
|
if (statement.kind == "TryStatement") {
|
|
493
|
-
const caught = writer.errorForId(statement.catchType.declarationId)
|
|
494
|
-
|
|
495
555
|
writer.mapped("try", {mappingKind: "anchor", node: statement, path})
|
|
496
556
|
writer.synthetic(" ", "try spacing", [statement], [path])
|
|
497
557
|
writer.mapped("{", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
@@ -499,18 +559,31 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
499
559
|
emitBlock(writer, statement.body, `${indent} `, `${path}/body`)
|
|
500
560
|
writer.synthetic(indent, "indentation", [statement], [path])
|
|
501
561
|
writer.mapped("}", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
562
|
+
for (const [index, handler] of statement.handlers.entries()) {
|
|
563
|
+
const handlerPath = `${path}/handlers/${index}`
|
|
564
|
+
const caught = writer.errorForId(handler.catchType.declarationId)
|
|
565
|
+
|
|
566
|
+
writer.synthetic(" ", "catch spacing", [handler], [handlerPath])
|
|
567
|
+
writer.mapped("catch", {mappingKind: "anchor", node: handler, path: handlerPath, role: "catch"})
|
|
568
|
+
writer.synthetic(" (", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
569
|
+
writer.mapped(writer.errorNameForId(handler.catchType.declarationId), {mappingKind: "exact", name: caught.name, node: handler.catchType, path: `${handlerPath}/catchType`, role: "type"})
|
|
570
|
+
writer.synthetic(" ", "catch binding spacing", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
571
|
+
writer.mapped(`$${handler.catchBinding.name}`, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
572
|
+
writer.synthetic(") ", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
573
|
+
writer.mapped("{", {mappingKind: "anchor", node: handler.catchBody, path: `${handlerPath}/catchBody`})
|
|
574
|
+
writer.synthetic("\n", "line break", [handler], [handlerPath])
|
|
575
|
+
emitBlock(writer, handler.catchBody, `${indent} `, `${handlerPath}/catchBody`)
|
|
576
|
+
writer.synthetic(indent, "indentation", [handler], [handlerPath])
|
|
577
|
+
writer.mapped("}", {mappingKind: "anchor", node: handler.catchBody, path: `${handlerPath}/catchBody`})
|
|
578
|
+
}
|
|
579
|
+
if (statement.finallyBody) {
|
|
580
|
+
writer.synthetic(" ", "finally spacing", [statement], [path])
|
|
581
|
+
writer.mapped("finally", {mappingKind: "anchor", node: statement, path, role: "finally"})
|
|
582
|
+
writer.synthetic(" {\n", "finally scaffolding", [statement], [path])
|
|
583
|
+
emitBlock(writer, statement.finallyBody, `${indent} `, `${path}/finallyBody`)
|
|
584
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
585
|
+
writer.mapped("}", {mappingKind: "anchor", node: statement.finallyBody, path: `${path}/finallyBody`})
|
|
586
|
+
}
|
|
514
587
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
515
588
|
return
|
|
516
589
|
}
|
package/src/backends/ruby.js
CHANGED
|
@@ -364,22 +364,46 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
364
364
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
365
365
|
return
|
|
366
366
|
}
|
|
367
|
+
if (statement.kind == "RethrowStatement") {
|
|
368
|
+
writer.mapped("raise", {mappingKind: "anchor", node: statement, path})
|
|
369
|
+
writer.synthetic(" ", "raise spacing", [statement], [path])
|
|
370
|
+
writer.mapped(statement.binding, {mappingKind: "exact", node: statement.bindingNode, path: `${path}/bindingNode`, role: "name"})
|
|
371
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
372
|
+
return
|
|
373
|
+
}
|
|
367
374
|
if (statement.kind == "TryStatement") {
|
|
368
|
-
const caught = writer.errorForId(statement.catchType.declarationId)
|
|
369
|
-
|
|
370
375
|
writer.mapped("begin", {mappingKind: "anchor", node: statement, path})
|
|
371
376
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
372
377
|
emitBlock(writer, statement.body, `${indent} `, `${path}/body`)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
378
|
+
for (const [index, handler] of statement.handlers.entries()) {
|
|
379
|
+
const handlerPath = `${path}/handlers/${index}`
|
|
380
|
+
const caught = writer.errorForId(handler.catchType.declarationId)
|
|
381
|
+
|
|
382
|
+
writer.synthetic(indent, "indentation", [handler], [handlerPath])
|
|
383
|
+
writer.mapped("rescue", {mappingKind: "anchor", node: handler, path: handlerPath, role: "catch"})
|
|
384
|
+
writer.synthetic(" ", "rescue spacing", [handler], [handlerPath])
|
|
385
|
+
writer.mapped(writer.errorNameForId(handler.catchType.declarationId), {mappingKind: "exact", name: caught.name, node: handler.catchType, path: `${handlerPath}/catchType`, role: "type"})
|
|
386
|
+
writer.synthetic(" => ", "catch binding separator", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
387
|
+
writer.mapped(handler.catchBinding.name, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
388
|
+
writer.synthetic("\n", "line break", [handler], [handlerPath])
|
|
389
|
+
emitBlock(writer, handler.catchBody, `${indent} `, `${handlerPath}/catchBody`)
|
|
390
|
+
}
|
|
391
|
+
if (statement.finallyBody) {
|
|
392
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
393
|
+
writer.mapped("ensure", {mappingKind: "anchor", node: statement, path, role: "ensure"})
|
|
394
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
395
|
+
emitBlock(writer, statement.finallyBody, `${indent} `, `${path}/finallyBody`)
|
|
396
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
397
|
+
writer.mapped("end", {mappingKind: "anchor", node: statement.finallyBody, path: `${path}/finallyBody`})
|
|
398
|
+
} else if (statement.handlers.length > 0) {
|
|
399
|
+
const lastIndex = statement.handlers.length - 1
|
|
400
|
+
|
|
401
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
402
|
+
writer.mapped("end", {mappingKind: "anchor", node: statement.handlers[lastIndex].catchBody, path: `${path}/handlers/${lastIndex}/catchBody`})
|
|
403
|
+
} else {
|
|
404
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
405
|
+
writer.mapped("end", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
406
|
+
}
|
|
383
407
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
384
408
|
return
|
|
385
409
|
}
|
package/src/backends/scalars.js
CHANGED
|
@@ -37,7 +37,7 @@ export function emitScalarType(language, type) {
|
|
|
37
37
|
* @returns {string} Exact target spelling.
|
|
38
38
|
*/
|
|
39
39
|
export function emitSemanticType(language, type, javaBoxed = false) {
|
|
40
|
-
if (type.kind == "RecordType" || type.kind == "ReferenceType") {
|
|
40
|
+
if (type.kind == "RecordType" || type.kind == "ReferenceType" || type.kind == "InterfaceType") {
|
|
41
41
|
throw new TypeError("Nominal type emission requires declaration context.")
|
|
42
42
|
}
|
|
43
43
|
if (type.kind == "OwnedResourceType" || type.kind == "OwnedReferenceType") {
|