semantifold 0.11.0 → 0.13.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/c.d.ts +2 -1
- package/build/src/backends/c.d.ts.map +1 -1
- package/build/src/backends/c.js +26 -5
- package/build/src/backends/cpp.js +3 -1
- package/build/src/backends/effects.js +3 -2
- package/build/src/backends/java.js +33 -14
- package/build/src/backends/javascript.js +83 -18
- package/build/src/backends/ordered-expressions.d.ts +19 -7
- package/build/src/backends/ordered-expressions.d.ts.map +1 -1
- package/build/src/backends/ordered-expressions.js +46 -11
- package/build/src/backends/php.js +33 -14
- package/build/src/backends/ruby.js +37 -12
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +92 -37
- package/build/src/backends/typescript.js +83 -18
- package/build/src/frontends/c.d.ts +34 -1
- package/build/src/frontends/c.d.ts.map +1 -1
- package/build/src/frontends/c.js +128 -21
- package/build/src/frontends/java.d.ts +8 -0
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +77 -33
- package/build/src/frontends/javascript-typescript.d.ts +2 -0
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +118 -37
- package/build/src/frontends/php.d.ts +8 -0
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +68 -29
- package/build/src/frontends/ruby.d.ts +3 -0
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +103 -30
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +16 -1
- package/build/src/semantic/effects.d.ts.map +1 -1
- package/build/src/semantic/effects.js +18 -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 +26 -7
- package/build/src/semantic/types.d.ts +76 -16
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +23 -7
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +106 -35
- package/docs/adr/20261026-typed-error-handling-extension.md +55 -0
- package/docs/adr-numeric-tower.md +156 -0
- package/docs/architecture.md +1 -1
- package/docs/c.md +1 -1
- package/docs/language-support.md +10 -9
- package/docs/testing.md +8 -5
- package/package.json +1 -1
- package/src/backends/c.js +23 -4
- package/src/backends/cpp.js +1 -0
- package/src/backends/effects.js +2 -1
- package/src/backends/java.js +33 -14
- package/src/backends/javascript.js +83 -18
- package/src/backends/ordered-expressions.js +46 -10
- package/src/backends/php.js +33 -14
- package/src/backends/ruby.js +36 -12
- package/src/backends/shared.js +89 -42
- package/src/backends/typescript.js +83 -18
- package/src/frontends/c.js +135 -19
- package/src/frontends/java.js +82 -33
- package/src/frontends/javascript-typescript.js +130 -38
- package/src/frontends/php.js +68 -27
- package/src/frontends/ruby.js +107 -28
- package/src/language-registry.js +15 -0
- package/src/semantic/effects.js +17 -5
- package/src/semantic/lifetime.js +29 -3
- package/src/semantic/provenance.js +27 -12
- package/src/semantic/types.js +24 -6
- package/src/semantic/validate.js +105 -36
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.
|
|
@@ -5,7 +5,8 @@ import {unsupportedCapability} from "../diagnostic.js"
|
|
|
5
5
|
|
|
6
6
|
/** @typedef {import("../semantic/types.js").Expression} Expression */
|
|
7
7
|
/** @typedef {import("../semantic/types.js").SemanticTypeName} Scalar */
|
|
8
|
-
/** @typedef {
|
|
8
|
+
/** @typedef {Scalar | "void"} PlannedType */
|
|
9
|
+
/** @typedef {{expression: Expression, path: string, type: PlannedType, name?: string}} PlannedValue */
|
|
9
10
|
/** @typedef {{result: PlannedValue, operands: PlannedValue[], rightSteps?: PlannedStep[]}} PlannedStep */
|
|
10
11
|
/** @typedef {Array<string | number | boolean | Signature>} Signature */
|
|
11
12
|
|
|
@@ -20,7 +21,7 @@ export class OrderedExpressionPlanner {
|
|
|
20
21
|
this.language = language
|
|
21
22
|
this.functions = new Map(module.functions.map((declaration) => [
|
|
22
23
|
declaration.name,
|
|
23
|
-
|
|
24
|
+
returnName(declaration.returnType, language, declaration.location)
|
|
24
25
|
]))
|
|
25
26
|
this.nextTemporary = 1
|
|
26
27
|
}
|
|
@@ -29,7 +30,7 @@ export class OrderedExpressionPlanner {
|
|
|
29
30
|
* Resolves an expression type from the validated lexical environment.
|
|
30
31
|
* @param {Expression} expression - Semantic expression.
|
|
31
32
|
* @param {Map<string, Scalar>} bindings - Lexical scalar types.
|
|
32
|
-
* @returns {
|
|
33
|
+
* @returns {PlannedType} Exact semantic result type.
|
|
33
34
|
*/
|
|
34
35
|
type(expression, bindings) {
|
|
35
36
|
if (expression.kind == "IntegerLiteral") return "integer"
|
|
@@ -76,12 +77,31 @@ export class OrderedExpressionPlanner {
|
|
|
76
77
|
}
|
|
77
78
|
operands.push(this.plan(expression.right, `${path}/right`, bindings, steps))
|
|
78
79
|
}
|
|
80
|
+
if (result.type == "void") throw new Error("Void call reached native value planning.")
|
|
79
81
|
const planned = {...result, name: this.allocate(expression)}
|
|
80
82
|
|
|
81
83
|
steps.push({operands, result: planned})
|
|
82
84
|
return planned
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Plans the arguments of one terminal void call without inventing a void temporary.
|
|
89
|
+
* @param {Expression} expression - Terminal expression occurrence.
|
|
90
|
+
* @param {string} path - Occurrence path.
|
|
91
|
+
* @param {Map<string, Scalar>} bindings - Visible scalar bindings.
|
|
92
|
+
* @param {PlannedStep[]} steps - Ordered enclosing prelude.
|
|
93
|
+
* @returns {PlannedStep} Terminal direct-call step.
|
|
94
|
+
*/
|
|
95
|
+
terminal(expression, path, bindings, steps) {
|
|
96
|
+
if (expression.kind != "CallExpression" || this.type(expression, bindings) != "void") {
|
|
97
|
+
throw new Error("Non-void expression reached native terminal-call planning.")
|
|
98
|
+
}
|
|
99
|
+
const result = {expression, path, type: /** @type {const} */ ("void")}
|
|
100
|
+
const operands = expression.arguments.map((argument, index) => this.plan(argument, `${path}/arguments/${index}`, bindings, steps))
|
|
101
|
+
|
|
102
|
+
return {operands, result}
|
|
103
|
+
}
|
|
104
|
+
|
|
85
105
|
/**
|
|
86
106
|
* Allocates one bounded deterministic occurrence name.
|
|
87
107
|
* @param {Expression} expression - Related expression.
|
|
@@ -108,9 +128,9 @@ export function statementSignature(statement) {
|
|
|
108
128
|
statement.kind == "AssignmentStatement" || statement.kind == "ExpressionStatement" || statement.kind == "PrintStatement" ? statement.expression :
|
|
109
129
|
statement.kind == "ReturnStatement" ? statement.expression : undefined
|
|
110
130
|
|
|
111
|
-
if (!expression) throw new TypeError("Bare return reached native statement signature planning.")
|
|
131
|
+
if (!expression && statement.kind != "ReturnStatement") throw new TypeError("Bare return reached native statement signature planning.")
|
|
112
132
|
|
|
113
|
-
return createHash("sha256").update(JSON.stringify([consumer, expressionSignature(expression)])).digest("hex")
|
|
133
|
+
return createHash("sha256").update(JSON.stringify([consumer, expression ? expressionSignature(expression) : null])).digest("hex")
|
|
114
134
|
}
|
|
115
135
|
|
|
116
136
|
/**
|
|
@@ -128,7 +148,7 @@ function expressionSignature(expression) {
|
|
|
128
148
|
return [expression.kind, expression.operation, expression.type, expressionSignature(expression.left), expressionSignature(expression.right)]
|
|
129
149
|
}
|
|
130
150
|
|
|
131
|
-
/** @typedef {{id: string, steps: PlannedStep[], value
|
|
151
|
+
/** @typedef {{id: string, steps: PlannedStep[], value?: PlannedValue, terminal?: PlannedStep}} StatementPlan */
|
|
132
152
|
/**
|
|
133
153
|
* Plans every occurrence before constructing a writer or exposing either artifact.
|
|
134
154
|
* @param {import("../semantic/types.js").SemanticModule} module - Validated module.
|
|
@@ -159,12 +179,13 @@ export function planNativeModule(module, language = "c") {
|
|
|
159
179
|
statement.kind == "ReturnStatement" ? statement.expression : undefined
|
|
160
180
|
/** @type {PlannedStep[]} */
|
|
161
181
|
const steps = []
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const value = planner.plan(expression, `${statementPath}/${field}`, bindings, steps)
|
|
182
|
+
/** @type {StatementPlan} */
|
|
183
|
+
const plan = {id: String(plans.size + 1).padStart(6, "0"), steps}
|
|
165
184
|
|
|
166
185
|
if (plans.size >= 999999) unsupportedCapability(language, "ordered statement limit", statement.location)
|
|
167
|
-
|
|
186
|
+
if (statement.kind == "ExpressionStatement") plan.terminal = planner.terminal(statement.expression, `${statementPath}/${field}`, bindings, steps)
|
|
187
|
+
else if (expression) plan.value = planner.plan(expression, `${statementPath}/${field}`, bindings, steps)
|
|
188
|
+
plans.set(statementPath, plan)
|
|
168
189
|
if (statement.kind == "LocalDeclaration") bindings.set(statement.name, scalarName(statement.type, language, statement.location))
|
|
169
190
|
if (statement.kind == "IfStatement") {
|
|
170
191
|
visit(statement.consequent, `${statementPath}/consequent`, bindings)
|
|
@@ -193,3 +214,18 @@ function scalarName(type, language, location) {
|
|
|
193
214
|
|
|
194
215
|
return type.name
|
|
195
216
|
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Narrows a validated function return, admitting the non-value `void` result.
|
|
220
|
+
* @param {import("../semantic/types.js").SemanticFunctionReturnType} type - Semantic type.
|
|
221
|
+
* @param {"c" | "cpp"} language - Diagnostic language.
|
|
222
|
+
* @param {import("../semantic/types.js").SourceLocation} location - Owning location.
|
|
223
|
+
* @returns {PlannedType} Scalar or void name.
|
|
224
|
+
*/
|
|
225
|
+
function returnName(type, language, location) {
|
|
226
|
+
if (type.kind != "TypeReference") {
|
|
227
|
+
return unsupportedCapability(language, "collection type reached native scalar planning", location)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return type.name
|
|
231
|
+
}
|
package/src/backends/php.js
CHANGED
|
@@ -489,9 +489,15 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
489
489
|
writer.synthetic(");\n", "error construction close", [statement.error], [`${path}/error`])
|
|
490
490
|
return
|
|
491
491
|
}
|
|
492
|
+
if (statement.kind == "RethrowStatement") {
|
|
493
|
+
writer.mapped("throw", {mappingKind: "anchor", node: statement, path})
|
|
494
|
+
writer.synthetic(" ", "throw spacing", [statement], [path])
|
|
495
|
+
writer.mapped(`$${statement.binding}`, {mappingKind: "exact", node: statement.bindingNode, path: `${path}/bindingNode`, role: "name"})
|
|
496
|
+
writer.mapped(";", {mappingKind: "anchor", node: statement, path})
|
|
497
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
498
|
+
return
|
|
499
|
+
}
|
|
492
500
|
if (statement.kind == "TryStatement") {
|
|
493
|
-
const caught = writer.errorForId(statement.catchType.declarationId)
|
|
494
|
-
|
|
495
501
|
writer.mapped("try", {mappingKind: "anchor", node: statement, path})
|
|
496
502
|
writer.synthetic(" ", "try spacing", [statement], [path])
|
|
497
503
|
writer.mapped("{", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
@@ -499,18 +505,31 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
499
505
|
emitBlock(writer, statement.body, `${indent} `, `${path}/body`)
|
|
500
506
|
writer.synthetic(indent, "indentation", [statement], [path])
|
|
501
507
|
writer.mapped("}", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
508
|
+
for (const [index, handler] of statement.handlers.entries()) {
|
|
509
|
+
const handlerPath = `${path}/handlers/${index}`
|
|
510
|
+
const caught = writer.errorForId(handler.catchType.declarationId)
|
|
511
|
+
|
|
512
|
+
writer.synthetic(" ", "catch spacing", [handler], [handlerPath])
|
|
513
|
+
writer.mapped("catch", {mappingKind: "anchor", node: handler, path: handlerPath, role: "catch"})
|
|
514
|
+
writer.synthetic(" (", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
515
|
+
writer.mapped(writer.errorNameForId(handler.catchType.declarationId), {mappingKind: "exact", name: caught.name, node: handler.catchType, path: `${handlerPath}/catchType`, role: "type"})
|
|
516
|
+
writer.synthetic(" ", "catch binding spacing", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
517
|
+
writer.mapped(`$${handler.catchBinding.name}`, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
518
|
+
writer.synthetic(") ", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
519
|
+
writer.mapped("{", {mappingKind: "anchor", node: handler.catchBody, path: `${handlerPath}/catchBody`})
|
|
520
|
+
writer.synthetic("\n", "line break", [handler], [handlerPath])
|
|
521
|
+
emitBlock(writer, handler.catchBody, `${indent} `, `${handlerPath}/catchBody`)
|
|
522
|
+
writer.synthetic(indent, "indentation", [handler], [handlerPath])
|
|
523
|
+
writer.mapped("}", {mappingKind: "anchor", node: handler.catchBody, path: `${handlerPath}/catchBody`})
|
|
524
|
+
}
|
|
525
|
+
if (statement.finallyBody) {
|
|
526
|
+
writer.synthetic(" ", "finally spacing", [statement], [path])
|
|
527
|
+
writer.mapped("finally", {mappingKind: "anchor", node: statement, path, role: "finally"})
|
|
528
|
+
writer.synthetic(" {\n", "finally scaffolding", [statement], [path])
|
|
529
|
+
emitBlock(writer, statement.finallyBody, `${indent} `, `${path}/finallyBody`)
|
|
530
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
531
|
+
writer.mapped("}", {mappingKind: "anchor", node: statement.finallyBody, path: `${path}/finallyBody`})
|
|
532
|
+
}
|
|
514
533
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
515
534
|
return
|
|
516
535
|
}
|
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
|
}
|