semantifold 0.3.0 → 0.4.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 +58 -15
- package/build/index.d.ts +48 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +57 -8
- package/build/src/artifact-path.d.ts +23 -1
- package/build/src/artifact-path.d.ts.map +1 -1
- package/build/src/artifact-path.js +45 -3
- package/build/src/artifacts.d.ts.map +1 -1
- package/build/src/artifacts.js +66 -1
- package/build/src/backends/c-validation.d.ts.map +1 -1
- package/build/src/backends/c-validation.js +7 -3
- package/build/src/backends/cpp-validation.d.ts.map +1 -1
- package/build/src/backends/cpp-validation.js +7 -3
- package/build/src/backends/csharp.js +7 -1
- package/build/src/backends/dart-runtime.d.ts +2 -0
- package/build/src/backends/dart-runtime.d.ts.map +1 -0
- package/build/src/backends/dart-runtime.js +41 -0
- package/build/src/backends/dart.d.ts +20 -0
- package/build/src/backends/dart.d.ts.map +1 -0
- package/build/src/backends/dart.js +321 -0
- package/build/src/backends/effects.d.ts +60 -0
- package/build/src/backends/effects.d.ts.map +1 -0
- package/build/src/backends/effects.js +746 -0
- package/build/src/backends/go.js +18 -4
- package/build/src/backends/identifiers.d.ts +36 -4
- package/build/src/backends/identifiers.d.ts.map +1 -1
- package/build/src/backends/identifiers.js +154 -10
- package/build/src/backends/index.d.ts.map +1 -1
- package/build/src/backends/index.js +27 -4
- package/build/src/backends/ios.d.ts +68 -0
- package/build/src/backends/ios.d.ts.map +1 -0
- package/build/src/backends/ios.js +1503 -0
- package/build/src/backends/java.d.ts.map +1 -1
- package/build/src/backends/java.js +429 -36
- package/build/src/backends/javascript.d.ts +6 -0
- package/build/src/backends/javascript.d.ts.map +1 -1
- package/build/src/backends/javascript.js +380 -24
- package/build/src/backends/kotlin-runtime.d.ts +3 -0
- package/build/src/backends/kotlin-runtime.d.ts.map +1 -0
- package/build/src/backends/kotlin-runtime.js +29 -0
- package/build/src/backends/kotlin.d.ts +23 -0
- package/build/src/backends/kotlin.d.ts.map +1 -0
- package/build/src/backends/kotlin.js +136 -0
- package/build/src/backends/native-validation.d.ts +2 -2
- package/build/src/backends/native-validation.d.ts.map +1 -1
- package/build/src/backends/native-validation.js +2 -2
- package/build/src/backends/ordered-expressions.d.ts.map +1 -1
- package/build/src/backends/ordered-expressions.js +39 -7
- package/build/src/backends/php-stdlib-support.d.ts +19 -0
- package/build/src/backends/php-stdlib-support.d.ts.map +1 -0
- package/build/src/backends/php-stdlib-support.js +227 -0
- package/build/src/backends/php.d.ts.map +1 -1
- package/build/src/backends/php.js +448 -33
- package/build/src/backends/program.d.ts +81 -0
- package/build/src/backends/program.d.ts.map +1 -0
- package/build/src/backends/program.js +1308 -0
- package/build/src/backends/python.js +7 -1
- package/build/src/backends/ruby.d.ts.map +1 -1
- package/build/src/backends/ruby.js +310 -24
- package/build/src/backends/rust-runtime.d.ts +7 -0
- package/build/src/backends/rust-runtime.d.ts.map +1 -0
- package/build/src/backends/rust-runtime.js +32 -0
- package/build/src/backends/rust-validation.d.ts +9 -0
- package/build/src/backends/rust-validation.d.ts.map +1 -0
- package/build/src/backends/rust-validation.js +78 -0
- package/build/src/backends/rust.d.ts +24 -0
- package/build/src/backends/rust.d.ts.map +1 -0
- package/build/src/backends/rust.js +344 -0
- package/build/src/backends/scalars.d.ts +14 -6
- package/build/src/backends/scalars.d.ts.map +1 -1
- package/build/src/backends/scalars.js +167 -17
- package/build/src/backends/shared.d.ts +23 -4
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +2158 -62
- package/build/src/backends/stdlib-support.d.ts +14 -0
- package/build/src/backends/stdlib-support.d.ts.map +1 -0
- package/build/src/backends/stdlib-support.js +237 -0
- package/build/src/backends/swift-runtime.d.ts +3 -0
- package/build/src/backends/swift-runtime.d.ts.map +1 -0
- package/build/src/backends/swift-runtime.js +14 -0
- package/build/src/backends/swift.d.ts +8 -0
- package/build/src/backends/swift.d.ts.map +1 -0
- package/build/src/backends/swift.js +127 -0
- package/build/src/backends/typescript.d.ts.map +1 -1
- package/build/src/backends/typescript.js +344 -24
- package/build/src/backends/wasm.d.ts +43 -0
- package/build/src/backends/wasm.d.ts.map +1 -0
- package/build/src/backends/wasm.js +2164 -0
- package/build/src/backends/writer.d.ts +235 -4
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +468 -3
- package/build/src/diagnostic.d.ts +4 -3
- package/build/src/diagnostic.d.ts.map +1 -1
- package/build/src/diagnostic.js +5 -3
- package/build/src/frontends/c.d.ts.map +1 -1
- package/build/src/frontends/c.js +5 -3
- package/build/src/frontends/cpp.d.ts.map +1 -1
- package/build/src/frontends/cpp.js +5 -3
- package/build/src/frontends/csharp.d.ts.map +1 -1
- package/build/src/frontends/csharp.js +7 -3
- package/build/src/frontends/dart.d.ts +10 -0
- package/build/src/frontends/dart.d.ts.map +1 -0
- package/build/src/frontends/dart.js +913 -0
- package/build/src/frontends/index.d.ts +3 -1
- package/build/src/frontends/index.d.ts.map +1 -1
- package/build/src/frontends/index.js +15 -5
- package/build/src/frontends/java.d.ts +110 -1
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +1725 -70
- package/build/src/frontends/javascript-typescript.d.ts +69 -1
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +1825 -93
- package/build/src/frontends/kotlin.d.ts +10 -0
- package/build/src/frontends/kotlin.d.ts.map +1 -0
- package/build/src/frontends/kotlin.js +685 -0
- package/build/src/frontends/php.d.ts +118 -1
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +1583 -76
- package/build/src/frontends/program.d.ts +74 -0
- package/build/src/frontends/program.d.ts.map +1 -0
- package/build/src/frontends/program.js +989 -0
- package/build/src/frontends/ruby.d.ts +68 -1
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +1446 -101
- package/build/src/frontends/rust-parser.d.ts +11 -0
- package/build/src/frontends/rust-parser.d.ts.map +1 -0
- package/build/src/frontends/rust-parser.js +15 -0
- package/build/src/frontends/rust.d.ts +14 -0
- package/build/src/frontends/rust.d.ts.map +1 -0
- package/build/src/frontends/rust.js +715 -0
- package/build/src/frontends/scalars.d.ts +10 -0
- package/build/src/frontends/scalars.d.ts.map +1 -1
- package/build/src/frontends/scalars.js +28 -2
- package/build/src/frontends/swift.d.ts +10 -0
- package/build/src/frontends/swift.d.ts.map +1 -0
- package/build/src/frontends/swift.js +860 -0
- package/build/src/frontends/types.d.ts +186 -0
- package/build/src/frontends/types.d.ts.map +1 -0
- package/build/src/frontends/types.js +634 -0
- package/build/src/language-registry.d.ts +46 -4
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +167 -13
- package/build/src/mapping.d.ts +2 -2
- package/build/src/mapping.d.ts.map +1 -1
- package/build/src/mapping.js +31 -10
- package/build/src/semantic/capabilities.d.ts +66 -0
- package/build/src/semantic/capabilities.d.ts.map +1 -0
- package/build/src/semantic/capabilities.js +649 -0
- package/build/src/semantic/effects.d.ts +26 -0
- package/build/src/semantic/effects.d.ts.map +1 -0
- package/build/src/semantic/effects.js +764 -0
- package/build/src/semantic/generics.d.ts +23 -0
- package/build/src/semantic/generics.d.ts.map +1 -0
- package/build/src/semantic/generics.js +61 -0
- package/build/src/semantic/lifetime.d.ts +37 -0
- package/build/src/semantic/lifetime.d.ts.map +1 -0
- package/build/src/semantic/lifetime.js +527 -0
- package/build/src/semantic/provenance.d.ts +9 -2
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +462 -20
- package/build/src/semantic/scalars.d.ts +11 -0
- package/build/src/semantic/scalars.d.ts.map +1 -1
- package/build/src/semantic/scalars.js +16 -1
- package/build/src/semantic/stdlib.d.ts +254 -0
- package/build/src/semantic/stdlib.d.ts.map +1 -0
- package/build/src/semantic/stdlib.js +880 -0
- package/build/src/semantic/types.d.ts +2952 -202
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +688 -15
- package/build/src/semantic/validate.d.ts +85 -5
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +2576 -98
- package/build/src/stdlib-facades.d.ts +304 -0
- package/build/src/stdlib-facades.d.ts.map +1 -0
- package/build/src/stdlib-facades.js +1167 -0
- package/build/src/stdlib-negotiation.d.ts +69 -0
- package/build/src/stdlib-negotiation.d.ts.map +1 -0
- package/build/src/stdlib-negotiation.js +403 -0
- package/build/src/stdlib-providers.d.ts +72 -0
- package/build/src/stdlib-providers.d.ts.map +1 -0
- package/build/src/stdlib-providers.js +339 -0
- package/build/src/toolchains.d.ts +55 -1
- package/build/src/toolchains.d.ts.map +1 -1
- package/build/src/toolchains.js +15 -4
- package/docs/architecture.md +45 -13
- package/docs/dart.md +60 -0
- package/docs/goals.md +3 -3
- package/docs/ios.md +121 -0
- package/docs/kotlin.md +42 -0
- package/docs/language-support.md +231 -26
- package/docs/multi-file-modules.md +52 -0
- package/docs/parser-qualification.md +121 -8
- package/docs/plans/2026-09-05-standard-library-portability.md +10 -10
- package/docs/rust.md +88 -0
- package/docs/source-maps.md +15 -7
- package/docs/standard-library-portability.md +39 -8
- package/docs/swift.md +44 -0
- package/docs/testing.md +366 -4
- package/docs/webassembly.md +52 -0
- package/index.js +63 -8
- package/node_modules/semantifold-tree-sitter-legacy-internal/README.md +2 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/LICENSE +21 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/README.md +45 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/binding.gyp +30 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding.cc +20 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding_test.js +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.d.ts +28 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.js +11 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/grammar.js +1630 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/package.json +64 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/highlights.scm +159 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/injections.scm +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/tags.scm +60 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/grammar.json +9405 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/node-types.json +5399 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/parser.c +192569 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/scanner.c +393 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/alloc.h +54 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/array.h +290 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/parser.h +266 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter-rust.wasm +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter.json +53 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/package.json +3 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/src/c.js +5 -4
- package/package.json +5 -2
- package/src/artifact-path.js +47 -2
- package/src/artifacts.js +67 -0
- package/src/backends/c-validation.js +6 -1
- package/src/backends/cpp-validation.js +6 -1
- package/src/backends/csharp.js +3 -0
- package/src/backends/dart-runtime.js +41 -0
- package/src/backends/dart.js +326 -0
- package/src/backends/effects.js +705 -0
- package/src/backends/go.js +14 -2
- package/src/backends/identifiers.js +160 -9
- package/src/backends/index.js +27 -2
- package/src/backends/ios.js +1612 -0
- package/src/backends/java.js +454 -36
- package/src/backends/javascript.js +389 -23
- package/src/backends/kotlin-runtime.js +29 -0
- package/src/backends/kotlin.js +141 -0
- package/src/backends/native-validation.js +1 -1
- package/src/backends/ordered-expressions.js +40 -6
- package/src/backends/php-stdlib-support.js +239 -0
- package/src/backends/php.js +469 -32
- package/src/backends/program.js +1407 -0
- package/src/backends/python.js +3 -0
- package/src/backends/ruby.js +326 -23
- package/src/backends/rust-runtime.js +34 -0
- package/src/backends/rust-validation.js +75 -0
- package/src/backends/rust.js +335 -0
- package/src/backends/scalars.js +141 -16
- package/src/backends/shared.js +2304 -124
- package/src/backends/stdlib-support.js +234 -0
- package/src/backends/swift-runtime.js +14 -0
- package/src/backends/swift.js +132 -0
- package/src/backends/typescript.js +349 -23
- package/src/backends/wasm.js +2208 -0
- package/src/backends/writer.js +520 -2
- package/src/diagnostic.js +4 -2
- package/src/frontends/c.js +4 -2
- package/src/frontends/cpp.js +4 -2
- package/src/frontends/csharp.js +4 -2
- package/src/frontends/dart.js +963 -0
- package/src/frontends/index.js +14 -4
- package/src/frontends/java.js +1977 -69
- package/src/frontends/javascript-typescript.js +2046 -102
- package/src/frontends/kotlin.js +713 -0
- package/src/frontends/php.js +1725 -79
- package/src/frontends/program.js +1062 -0
- package/src/frontends/ruby.js +1624 -98
- package/src/frontends/rust-parser.js +18 -0
- package/src/frontends/rust.js +714 -0
- package/src/frontends/scalars.js +31 -1
- package/src/frontends/swift.js +893 -0
- package/src/frontends/types.js +750 -0
- package/src/language-registry.js +172 -12
- package/src/mapping.js +33 -9
- package/src/semantic/capabilities.js +671 -0
- package/src/semantic/effects.js +737 -0
- package/src/semantic/generics.js +58 -0
- package/src/semantic/lifetime.js +517 -0
- package/src/semantic/provenance.js +412 -19
- package/src/semantic/scalars.js +17 -0
- package/src/semantic/stdlib.js +921 -0
- package/src/semantic/types.js +765 -14
- package/src/semantic/validate.js +2934 -209
- package/src/stdlib-facades.js +1211 -0
- package/src/stdlib-negotiation.js +446 -0
- package/src/stdlib-providers.js +377 -0
- package/src/toolchains.js +16 -3
package/src/backends/shared.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import {unsupportedCapability} from "../diagnostic.js"
|
|
4
|
+
import {isDenseArray} from "../array.js"
|
|
4
5
|
import {validateBackendTypes} from "../semantic/validate.js"
|
|
5
|
-
import {validateTargetBindingIdentifier, validateTargetIdentifier} from "./identifiers.js"
|
|
6
|
-
import {emitStringLiteral} from "./scalars.js"
|
|
6
|
+
import {validateTargetBindingIdentifier, validateTargetIdentifier, validateTargetTypeIdentifier} from "./identifiers.js"
|
|
7
|
+
import {emitScalarType, emitStringLiteral} from "./scalars.js"
|
|
7
8
|
|
|
8
9
|
/** @type {Readonly<Record<import("../semantic/types.js").SemanticUnaryOperation, string>>} */
|
|
9
10
|
const unaryOperationSyntax = Object.freeze({BooleanNot: "!", IntegerNegate: "-"})
|
|
@@ -26,22 +27,91 @@ const binaryOperationSyntax = Object.freeze({
|
|
|
26
27
|
StringEqual: Object.freeze({default: "==", strict: "==="}),
|
|
27
28
|
StringNotEqual: Object.freeze({default: "!=", strict: "!=="})
|
|
28
29
|
})
|
|
30
|
+
const task005Languages = new Set(["php", "ruby", "javascript", "typescript", "java", "dart"])
|
|
31
|
+
const task006Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
32
|
+
const task007Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
33
|
+
const task008Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
34
|
+
const task009Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
35
|
+
const task011Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
36
|
+
const task012Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
37
|
+
const task014Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
38
|
+
const task032Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
39
|
+
const task033Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
40
|
+
const javaReferenceMethodHooks = new Set(["clone", "equals", "finalize", "getClass", "hashCode", "notify", "notifyAll", "toString", "wait"])
|
|
41
|
+
const javascriptReferenceMethodHooks = new Set([
|
|
42
|
+
"__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__", "constructor",
|
|
43
|
+
"hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "valueOf"
|
|
44
|
+
])
|
|
45
|
+
const rubyReferenceMethodHooks = new Set([
|
|
46
|
+
"__send__", "extend", "fetch", "instance_eval", "instance_exec", "method", "method_missing", "public_send",
|
|
47
|
+
"respond_to_missing?", "send", "singleton_class", "size"
|
|
48
|
+
])
|
|
49
|
+
const javaObjectInstanceMethodSignatures = new Set([
|
|
50
|
+
"clone()", "equals(Object)", "finalize()", "getClass()", "hashCode()", "notify()", "notifyAll()", "toString()",
|
|
51
|
+
"wait()", "wait(long)", "wait(long,int)"
|
|
52
|
+
])
|
|
29
53
|
|
|
30
54
|
/**
|
|
31
55
|
* Checks the intentionally narrow backend contract.
|
|
32
56
|
* @param {import("../semantic/types.js").SemanticModule} module - Semantic module.
|
|
33
|
-
* @param {import("../semantic/types.js").
|
|
57
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Backend language or binary target.
|
|
58
|
+
* @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.
|
|
34
59
|
* @returns {void}
|
|
35
60
|
*/
|
|
36
|
-
export function validateBackendModule(module, language) {
|
|
61
|
+
export function validateBackendModule(module, language, options = {}) {
|
|
37
62
|
if (!module || typeof module != "object" || Array.isArray(module)) {
|
|
38
63
|
return unsupportedCapability(language, "missing or invalid module", undefined)
|
|
39
64
|
}
|
|
40
65
|
if (module.kind != "Module") unsupportedCapability(language, module.kind, module.location)
|
|
41
66
|
if (!Array.isArray(module.functions) || !module.entryPoint || typeof module.entryPoint != "object" ||
|
|
42
67
|
module.entryPoint.kind != "EntryPoint") unsupportedCapability(language, "missing or invalid module members", module.location)
|
|
43
|
-
|
|
44
|
-
|
|
68
|
+
const declaredRecords = module.records
|
|
69
|
+
const records = declaredRecords === undefined ? [] : declaredRecords
|
|
70
|
+
const declaredClasses = module.classes
|
|
71
|
+
const classes = declaredClasses === undefined ? [] : declaredClasses
|
|
72
|
+
const declaredErrors = module.errors
|
|
73
|
+
const errors = declaredErrors === undefined ? [] : declaredErrors
|
|
74
|
+
|
|
75
|
+
if (!Array.isArray(records)) unsupportedCapability(language, "missing or invalid record declarations", module.location)
|
|
76
|
+
if (!Array.isArray(classes)) unsupportedCapability(language, "missing or invalid reference class declarations", module.location)
|
|
77
|
+
if (!Array.isArray(errors)) unsupportedCapability(language, "missing or invalid error declarations", module.location)
|
|
78
|
+
if (module.functions.length == 0 && (!options.program ||
|
|
79
|
+
records.length == 0 && classes.length == 0 && errors.length == 0 && module.entryPoint.body?.statements?.length == 0)) {
|
|
80
|
+
unsupportedCapability(language, "module without declarations or executable entry", module.location)
|
|
81
|
+
}
|
|
82
|
+
if (!task012Languages.has(language) && moduleHasTypeParameters(module)) {
|
|
83
|
+
unsupportedCapability(language, "Task 012 type parameters and generic declarations", module.location)
|
|
84
|
+
}
|
|
85
|
+
if (records.length > 0 && !task009Languages.has(language)) {
|
|
86
|
+
unsupportedCapability(language, "Task 009 closed records", records[0]?.location ?? module.location)
|
|
87
|
+
}
|
|
88
|
+
if (classes.length > 0 && !task033Languages.has(language)) {
|
|
89
|
+
unsupportedCapability(language, "Task 033 reference classes, methods, and constructors", classes[0]?.location ?? module.location)
|
|
90
|
+
}
|
|
91
|
+
if (!task011Languages.has(language)) rejectTypedErrors(module, language)
|
|
92
|
+
if (!task032Languages.has(language)) rejectConditionControlledLoops(module, language)
|
|
93
|
+
if (!task014Languages.has(language)) rejectOrderedMapCapability(module, language)
|
|
94
|
+
validateRecordTargets(records, module.functions, language, module.location)
|
|
95
|
+
validateReferenceClassTargets(classes, records, errors, module.functions, language, module.location)
|
|
96
|
+
if (!task008Languages.has(language)) rejectIterationStatements(module, language)
|
|
97
|
+
if (!task007Languages.has(language)) rejectOptionalTypes(module, language)
|
|
98
|
+
if (!task006Languages.has(language)) rejectCollectionTypes(module, language)
|
|
99
|
+
const loopOwners = collectLoopOwners(module, language)
|
|
100
|
+
|
|
101
|
+
for (const declaration of classes) {
|
|
102
|
+
validateBlock(declaration.constructor.body, language, declaration.constructor.location,
|
|
103
|
+
{active: [], owner: declaration.constructor, owners: loopOwners}, new Set(), options.externalDeclarationIds)
|
|
104
|
+
for (const method of declaration.methods) {
|
|
105
|
+
validateBlock(method.body, language, method.location,
|
|
106
|
+
{active: [], owner: method, owners: loopOwners}, new Set(), options.externalDeclarationIds)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
validateBlock(module.entryPoint.body, language, module.entryPoint.location,
|
|
111
|
+
{active: [], owner: module.entryPoint, owners: loopOwners}, new Set(), options.externalDeclarationIds)
|
|
112
|
+
const declarationIds = new Set()
|
|
113
|
+
const declarationNames = new Set()
|
|
114
|
+
const targetDeclarationNames = new Set()
|
|
45
115
|
|
|
46
116
|
for (const functionDeclaration of module.functions) {
|
|
47
117
|
if (!functionDeclaration || typeof functionDeclaration != "object" || Array.isArray(functionDeclaration) ||
|
|
@@ -49,63 +119,1085 @@ export function validateBackendModule(module, language) {
|
|
|
49
119
|
unsupportedCapability(language, "missing or invalid function declaration", module.location)
|
|
50
120
|
}
|
|
51
121
|
validateTargetBindingIdentifier(language, functionDeclaration.name, "function", functionDeclaration.location)
|
|
122
|
+
if (declarationNames.has(functionDeclaration.name)) {
|
|
123
|
+
unsupportedCapability(language, `duplicate function '${functionDeclaration.name}'`, functionDeclaration.location)
|
|
124
|
+
}
|
|
125
|
+
declarationNames.add(functionDeclaration.name)
|
|
126
|
+
const targetDeclarationName = language == "php"
|
|
127
|
+
? functionDeclaration.name.replace(/[A-Z]/gu, (character) => character.toLowerCase())
|
|
128
|
+
: functionDeclaration.name
|
|
129
|
+
|
|
130
|
+
if (targetDeclarationNames.has(targetDeclarationName)) {
|
|
131
|
+
unsupportedCapability(language, `target function-name collision '${functionDeclaration.name}'`, functionDeclaration.location)
|
|
132
|
+
}
|
|
133
|
+
targetDeclarationNames.add(targetDeclarationName)
|
|
134
|
+
if (typeof functionDeclaration.id != "string" || !/^(?:[a-z][a-z0-9._-]*#)?function:[0-9]+$/u.test(functionDeclaration.id) ||
|
|
135
|
+
declarationIds.has(functionDeclaration.id)) {
|
|
136
|
+
unsupportedCapability(language, "duplicate or invalid function declaration identity", functionDeclaration.location)
|
|
137
|
+
}
|
|
138
|
+
declarationIds.add(functionDeclaration.id)
|
|
139
|
+
validateTypeParameterTargets(functionDeclaration, language)
|
|
140
|
+
|
|
141
|
+
if (!task005Languages.has(language) && functionDeclaration.parameters.length != 2) {
|
|
142
|
+
unsupportedCapability(language, "function parameter count other than two", functionDeclaration.location)
|
|
143
|
+
}
|
|
144
|
+
if (!task005Languages.has(language) && functionDeclaration.returnType?.kind == "TypeReference" &&
|
|
145
|
+
functionDeclaration.returnType.name == "void") {
|
|
146
|
+
unsupportedCapability(language, "Task 005 void function return", functionDeclaration.location)
|
|
147
|
+
}
|
|
148
|
+
validateBlock(functionDeclaration.body, language, functionDeclaration.location,
|
|
149
|
+
{active: [], owner: functionDeclaration, owners: loopOwners}, new Set(), options.externalDeclarationIds)
|
|
150
|
+
|
|
151
|
+
for (const parameter of functionDeclaration.parameters) {
|
|
152
|
+
if (!parameter || typeof parameter != "object" || Array.isArray(parameter) || parameter.kind != "Parameter") {
|
|
153
|
+
unsupportedCapability(language, "missing or invalid parameter", functionDeclaration.location)
|
|
154
|
+
}
|
|
155
|
+
validateTargetBindingIdentifier(language, parameter.name, "parameter", parameter.location)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
validateErrorTargets(errors, records, module.functions, language, module.location)
|
|
159
|
+
validateScaffoldingNames(module, language)
|
|
160
|
+
validateBackendTypes(module, language, {
|
|
161
|
+
callEffects: options.visibleCallEffects,
|
|
162
|
+
classes: options.visibleClasses,
|
|
163
|
+
errors: options.visibleErrors,
|
|
164
|
+
functions: options.visibleFunctions,
|
|
165
|
+
records: options.visibleRecords
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Rejects Task 014 IR transactionally before a non-cohort target allocates output.
|
|
171
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Candidate module.
|
|
172
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target identity.
|
|
173
|
+
* @returns {void}
|
|
174
|
+
*/
|
|
175
|
+
function rejectOrderedMapCapability(module, language) {
|
|
176
|
+
const node = findOrderedMapNode(module)
|
|
177
|
+
|
|
178
|
+
if (node) unsupportedCapability(language, "Task 014 ordered-map iteration", diagnosticLocation(node.location, module.location))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Finds the first distinct ordered-map type, literal, or loop in parser-neutral IR.
|
|
183
|
+
* @param {unknown} value - Candidate subtree.
|
|
184
|
+
* @param {Set<object>} [seen] - Cycle protection.
|
|
185
|
+
* @returns {Record<string, unknown> | undefined} First Task 014 node.
|
|
186
|
+
*/
|
|
187
|
+
function findOrderedMapNode(value, seen = new Set()) {
|
|
188
|
+
if (!value || typeof value != "object" || seen.has(value)) return undefined
|
|
189
|
+
seen.add(value)
|
|
190
|
+
if (!Array.isArray(value)) {
|
|
191
|
+
const candidate = /** @type {Record<string, unknown>} */ (value)
|
|
192
|
+
|
|
193
|
+
if (["OrderedMapType", "OrderedMapLiteral", "ForEachMapStatement"].includes(String(candidate.kind))) return candidate
|
|
194
|
+
}
|
|
195
|
+
for (const child of Array.isArray(value) ? value : Object.values(value)) {
|
|
196
|
+
const found = findOrderedMapNode(child, seen)
|
|
197
|
+
|
|
198
|
+
if (found) return found
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return undefined
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Rejects typed errors for a target outside the Task 011 cohort.
|
|
206
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Candidate module.
|
|
207
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
208
|
+
* @returns {void}
|
|
209
|
+
*/
|
|
210
|
+
function rejectTypedErrors(/** @type {import("../semantic/types.js").SemanticModule} */ module,
|
|
211
|
+
/** @type {import("../semantic/types.js").BackendLanguage} */ language) {
|
|
212
|
+
if ((module.errors ?? []).length > 0) {
|
|
213
|
+
unsupportedCapability(language, "Task 011 typed errors and handling", module.errors?.[0]?.location ?? module.location)
|
|
214
|
+
}
|
|
215
|
+
for (const declaration of module.functions) rejectTypedErrorBlock(declaration?.body, language)
|
|
216
|
+
rejectTypedErrorBlock(module.entryPoint.body, language)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Finds typed-error statements without descending through malformed or cyclic blocks.
|
|
221
|
+
* @param {unknown} block - Candidate block.
|
|
222
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
223
|
+
* @param {Set<object>} [seen] - Visited blocks.
|
|
224
|
+
* @returns {void}
|
|
225
|
+
*/
|
|
226
|
+
function rejectTypedErrorBlock(/** @type {unknown} */ block,
|
|
227
|
+
/** @type {import("../semantic/types.js").BackendLanguage} */ language,
|
|
228
|
+
/** @type {Set<object>} */ seen = new Set()) {
|
|
229
|
+
if (!block || typeof block != "object" || Array.isArray(block) || seen.has(block)) return
|
|
230
|
+
seen.add(block)
|
|
231
|
+
const statements = Reflect.get(block, "statements")
|
|
232
|
+
|
|
233
|
+
if (!Array.isArray(statements)) return
|
|
234
|
+
for (const statement of statements) {
|
|
235
|
+
if (!statement || typeof statement != "object" || Array.isArray(statement)) continue
|
|
236
|
+
if (Reflect.get(statement, "kind") == "RaiseStatement" || Reflect.get(statement, "kind") == "TryStatement") {
|
|
237
|
+
unsupportedCapability(language, "Task 011 typed errors and handling",
|
|
238
|
+
diagnosticLocation(Reflect.get(statement, "location"), Reflect.get(block, "location")))
|
|
239
|
+
}
|
|
240
|
+
if (Reflect.get(statement, "kind") == "IfStatement") {
|
|
241
|
+
rejectTypedErrorBlock(Reflect.get(statement, "consequent"), language, seen)
|
|
242
|
+
rejectTypedErrorBlock(Reflect.get(statement, "alternate"), language, seen)
|
|
243
|
+
} else if (["ForEachStatement", "ForEachMapStatement", "WhileStatement"].includes(String(Reflect.get(statement, "kind")))) {
|
|
244
|
+
rejectTypedErrorBlock(Reflect.get(statement, "body"), language, seen)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Validates target-visible nominal error declarations before emission.
|
|
251
|
+
* @param {import("../semantic/types.js").ErrorDeclaration[]} errors - Error declarations.
|
|
252
|
+
* @param {import("../semantic/types.js").RecordDeclaration[]} records - Record declarations.
|
|
253
|
+
* @param {import("../semantic/types.js").FunctionDeclaration[]} functions - Function declarations.
|
|
254
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
255
|
+
* @param {import("../semantic/types.js").SourceLocation} moduleLocation - Module location.
|
|
256
|
+
* @returns {void}
|
|
257
|
+
*/
|
|
258
|
+
function validateErrorTargets(
|
|
259
|
+
/** @type {import("../semantic/types.js").ErrorDeclaration[]} */ errors,
|
|
260
|
+
/** @type {import("../semantic/types.js").RecordDeclaration[]} */ records,
|
|
261
|
+
/** @type {import("../semantic/types.js").FunctionDeclaration[]} */ functions,
|
|
262
|
+
/** @type {import("../semantic/types.js").BackendLanguage} */ language,
|
|
263
|
+
/** @type {import("../semantic/types.js").SourceLocation} */ moduleLocation
|
|
264
|
+
) {
|
|
265
|
+
const ids = new Set()
|
|
266
|
+
const names = new Set([...records, ...functions].map(({name}) => language == "php" ? name.toLowerCase() : name))
|
|
267
|
+
const forbidden = language == "javascript" || language == "typescript" ? new Set(["Error", "AggregateError"]) :
|
|
268
|
+
language == "php" ? new Set(["Throwable", "Exception", "RuntimeException", "Error"]) :
|
|
269
|
+
language == "ruby" ? new Set(["Exception", "StandardError", "ScriptError", "SystemExit"]) :
|
|
270
|
+
language == "java" ? new Set(["Throwable", "Exception", "RuntimeException", "Error"]) : new Set()
|
|
271
|
+
|
|
272
|
+
for (let index = 0; index < errors.length; index += 1) {
|
|
273
|
+
const declaration = errors[index]
|
|
274
|
+
const location = diagnosticLocation(declaration?.location, moduleLocation)
|
|
275
|
+
const fields = declaration && typeof declaration == "object" && !Array.isArray(declaration)
|
|
276
|
+
? Object.keys(declaration).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
277
|
+
|
|
278
|
+
if (!declaration || typeof declaration != "object" || Array.isArray(declaration) ||
|
|
279
|
+
declaration.kind != "ErrorDeclaration" || fields != "id,kind,location,name") {
|
|
280
|
+
unsupportedCapability(language, "missing or invalid error declaration", location)
|
|
281
|
+
}
|
|
282
|
+
requireSemanticLocation(declaration.location, language, "error declaration location", moduleLocation)
|
|
283
|
+
validateTargetTypeIdentifier(language, declaration.name, declaration.location)
|
|
284
|
+
if ((declaration.id != `error:${index}` &&
|
|
285
|
+
!new RegExp(`^[a-z][a-z0-9._-]*#error:${index}$`, "u").test(declaration.id ?? "")) || ids.has(declaration.id)) {
|
|
286
|
+
unsupportedCapability(language, "duplicate or invalid error declaration identity", declaration.location)
|
|
287
|
+
}
|
|
288
|
+
const normalized = language == "php" ? declaration.name.toLowerCase() : declaration.name
|
|
289
|
+
|
|
290
|
+
if (names.has(normalized) || forbidden.has(declaration.name)) {
|
|
291
|
+
unsupportedCapability(language, `error declaration collision '${declaration.name}'`, declaration.location)
|
|
292
|
+
}
|
|
293
|
+
ids.add(declaration.id)
|
|
294
|
+
names.add(normalized)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Validates every nominal record identifier, identity, field, and target collision before output allocation.
|
|
300
|
+
* @param {import("../semantic/types.js").RecordDeclaration[]} records - Candidate records.
|
|
301
|
+
* @param {import("../semantic/types.js").FunctionDeclaration[]} functions - Candidate functions.
|
|
302
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
303
|
+
* @param {import("../semantic/types.js").SourceLocation | undefined} moduleLocation - Module fallback location.
|
|
304
|
+
* @returns {void}
|
|
305
|
+
*/
|
|
306
|
+
function validateRecordTargets(records, functions, language, moduleLocation) {
|
|
307
|
+
const declarationIds = new Set()
|
|
308
|
+
const targetNames = new Set()
|
|
309
|
+
|
|
310
|
+
for (const declaration of functions) {
|
|
311
|
+
if (!declaration || typeof declaration != "object" || Array.isArray(declaration)) continue
|
|
312
|
+
const name = Reflect.get(declaration, "name")
|
|
313
|
+
|
|
314
|
+
if (typeof name != "string") continue
|
|
315
|
+
targetNames.add(language == "php" ? name.toLowerCase() : name)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
for (let recordIndex = 0; recordIndex < records.length; recordIndex += 1) {
|
|
319
|
+
const record = records[recordIndex]
|
|
320
|
+
const location = diagnosticLocation(record?.location, moduleLocation)
|
|
321
|
+
const recordKeys = record && typeof record == "object" && !Array.isArray(record)
|
|
322
|
+
? Object.keys(record).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
323
|
+
: ""
|
|
324
|
+
|
|
325
|
+
if (!record || typeof record != "object" || Array.isArray(record) || record.kind != "RecordDeclaration" ||
|
|
326
|
+
!["fields,id,kind,location,name", "fields,id,kind,location,name,typeParameters"].includes(recordKeys) || !isDenseArray(record.fields)) {
|
|
327
|
+
unsupportedCapability(language, "missing or invalid record declaration", location)
|
|
328
|
+
}
|
|
329
|
+
requireSemanticLocation(record.location, language, "record declaration location", moduleLocation)
|
|
330
|
+
validateTargetTypeIdentifier(language, record.name, record.location)
|
|
331
|
+
if (typeof record.id != "string" || !new RegExp(`^(?:[a-z][a-z0-9._-]*#)?record:${recordIndex}$`, "u").test(record.id) ||
|
|
332
|
+
declarationIds.has(record.id)) {
|
|
333
|
+
unsupportedCapability(language, "duplicate or invalid record declaration identity", record.location)
|
|
334
|
+
}
|
|
335
|
+
declarationIds.add(record.id)
|
|
336
|
+
validateTypeParameterTargets(record, language)
|
|
337
|
+
const targetName = language == "php" ? record.name.toLowerCase() : record.name
|
|
338
|
+
|
|
339
|
+
if (targetNames.has(targetName)) unsupportedCapability(language, `record declaration collision '${record.name}'`, record.location)
|
|
340
|
+
targetNames.add(targetName)
|
|
341
|
+
const fieldNames = new Set()
|
|
342
|
+
|
|
343
|
+
for (let fieldIndex = 0; fieldIndex < record.fields.length; fieldIndex += 1) {
|
|
344
|
+
const field = record.fields[fieldIndex]
|
|
345
|
+
const fieldLocation = diagnosticLocation(field?.location, record.location)
|
|
346
|
+
const fieldKeys = field && typeof field == "object" && !Array.isArray(field)
|
|
347
|
+
? Object.keys(field).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
348
|
+
: ""
|
|
349
|
+
|
|
350
|
+
if (!field || typeof field != "object" || Array.isArray(field) || field.kind != "RecordField" ||
|
|
351
|
+
fieldKeys != "id,kind,location,name,type") {
|
|
352
|
+
unsupportedCapability(language, "missing or invalid record field", fieldLocation)
|
|
353
|
+
}
|
|
354
|
+
requireSemanticLocation(field.location, language, "record field location", record.location)
|
|
355
|
+
if (field.id != `${record.id}:field:${fieldIndex}`) {
|
|
356
|
+
unsupportedCapability(language, "duplicate or invalid record field identity", field.location)
|
|
357
|
+
}
|
|
358
|
+
validateTargetIdentifier(language, field.name, "record field", field.location)
|
|
359
|
+
const collision = language == "javascript" && ["constructor", "prototype", "__proto__"].includes(field.name) ||
|
|
360
|
+
language == "typescript" && ["constructor", "prototype", "__proto__"].includes(field.name) ||
|
|
361
|
+
language == "php" && field.name.toLowerCase().startsWith("__") ||
|
|
362
|
+
language == "ruby" && ["initialize", "freeze", "send", "public_send", "method", "singleton_class"].includes(field.name) ||
|
|
363
|
+
language == "java" && javaObjectInstanceMethodSignatures.has(`${field.name}()`)
|
|
364
|
+
|
|
365
|
+
if (collision) unsupportedCapability(language, `record field collision '${field.name}'`, field.location)
|
|
366
|
+
const normalized = language == "php" ? field.name.toLowerCase() : field.name
|
|
367
|
+
|
|
368
|
+
if (fieldNames.has(normalized)) unsupportedCapability(language, `duplicate target record field '${field.name}'`, field.location)
|
|
369
|
+
fieldNames.add(normalized)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Validates the exact target-visible shape of every reference class before output allocation.
|
|
376
|
+
* @param {import("../semantic/types.js").ClassDeclaration[]} classes - Candidate classes.
|
|
377
|
+
* @param {import("../semantic/types.js").RecordDeclaration[]} records - Existing records.
|
|
378
|
+
* @param {import("../semantic/types.js").ErrorDeclaration[]} errors - Existing typed errors.
|
|
379
|
+
* @param {import("../semantic/types.js").FunctionDeclaration[]} functions - Existing functions.
|
|
380
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
381
|
+
* @param {import("../semantic/types.js").SourceLocation | undefined} moduleLocation - Fallback location.
|
|
382
|
+
* @returns {void}
|
|
383
|
+
*/
|
|
384
|
+
function validateReferenceClassTargets(classes, records, errors, functions, language, moduleLocation) {
|
|
385
|
+
const targetNames = new Set()
|
|
386
|
+
|
|
387
|
+
for (const declaration of [...records, ...errors, ...functions]) {
|
|
388
|
+
if (!declaration || typeof declaration != "object" || Array.isArray(declaration)) continue
|
|
389
|
+
const name = Reflect.get(declaration, "name")
|
|
390
|
+
|
|
391
|
+
if (typeof name != "string") continue
|
|
392
|
+
targetNames.add(language == "php" ? name.toLowerCase() : name)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
for (let classIndex = 0; classIndex < classes.length; classIndex += 1) {
|
|
396
|
+
const declaration = classes[classIndex]
|
|
397
|
+
const location = diagnosticLocation(declaration?.location, moduleLocation)
|
|
398
|
+
const keys = declaration && typeof declaration == "object" && !Array.isArray(declaration)
|
|
399
|
+
? Object.keys(declaration).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
400
|
+
|
|
401
|
+
if (!declaration || declaration.kind != "ClassDeclaration" ||
|
|
402
|
+
!["constructor,fields,id,kind,location,methods,name", "constructor,fields,id,kind,location,methods,name,ownership"].includes(keys) ||
|
|
403
|
+
!isDenseArray(declaration.fields) || declaration.fields.length == 0 || !isDenseArray(declaration.methods) ||
|
|
404
|
+
!new RegExp(`^(?:[a-z][a-z0-9._-]*#)?class:${classIndex}$`, "u").test(String(declaration.id))) {
|
|
405
|
+
unsupportedCapability(language, "missing or invalid reference class declaration", location)
|
|
406
|
+
}
|
|
407
|
+
if (declaration.ownership !== undefined && declaration.ownership?.kind != "ordinary" &&
|
|
408
|
+
(declaration.ownership?.kind != "ownedResource" || typeof declaration.ownership.fieldId != "string" ||
|
|
409
|
+
typeof declaration.ownership.resourceId != "string")) {
|
|
410
|
+
unsupportedCapability(language, "malformed reference class resource ownership", location)
|
|
411
|
+
}
|
|
412
|
+
const declarationLocation = requireSemanticLocation(declaration.location, language, "reference class declaration location", moduleLocation)
|
|
413
|
+
|
|
414
|
+
validateTargetTypeIdentifier(language, declaration.name, declarationLocation)
|
|
415
|
+
const targetName = language == "php" ? declaration.name.toLowerCase() : declaration.name
|
|
416
|
+
|
|
417
|
+
if (targetNames.has(targetName)) unsupportedCapability(language, `reference class collision '${declaration.name}'`, declarationLocation)
|
|
418
|
+
targetNames.add(targetName)
|
|
419
|
+
const fieldNames = new Set()
|
|
420
|
+
|
|
421
|
+
for (let fieldIndex = 0; fieldIndex < declaration.fields.length; fieldIndex += 1) {
|
|
422
|
+
const field = declaration.fields[fieldIndex]
|
|
423
|
+
const fieldLocation = diagnosticLocation(field?.location, location)
|
|
424
|
+
const fieldKeys = field && typeof field == "object" && !Array.isArray(field)
|
|
425
|
+
? Object.keys(field).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
426
|
+
|
|
427
|
+
if (!field || field.kind != "PrivateField" || fieldKeys != "id,kind,location,name,type" ||
|
|
428
|
+
field.id != `${declaration.id}:field:${fieldIndex}`) {
|
|
429
|
+
unsupportedCapability(language, "missing or invalid private field", fieldLocation)
|
|
430
|
+
}
|
|
431
|
+
const exactFieldLocation = requireSemanticLocation(field.location, language, "private field location", declarationLocation)
|
|
432
|
+
|
|
433
|
+
validateTargetIdentifier(language, field.name, "private field", exactFieldLocation)
|
|
434
|
+
const normalized = language == "php" ? field.name.toLowerCase() : field.name
|
|
435
|
+
|
|
436
|
+
if (fieldNames.has(normalized)) unsupportedCapability(language, `duplicate target private field '${field.name}'`, exactFieldLocation)
|
|
437
|
+
fieldNames.add(normalized)
|
|
438
|
+
}
|
|
439
|
+
const constructor = declaration.constructor
|
|
440
|
+
const constructorLocation = diagnosticLocation(constructor?.location, location)
|
|
441
|
+
const constructorKeys = constructor && typeof constructor == "object" && !Array.isArray(constructor)
|
|
442
|
+
? Object.keys(constructor).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
443
|
+
|
|
444
|
+
if (!constructor || constructor.kind != "ConstructorDeclaration" ||
|
|
445
|
+
!["body,id,kind,location,parameters", "body,effects,failureIds,id,kind,location,parameters"].includes(constructorKeys) ||
|
|
446
|
+
constructor.id != `${declaration.id}:constructor` || !isDenseArray(constructor.parameters)) {
|
|
447
|
+
unsupportedCapability(language, "missing or invalid constructor declaration", constructorLocation)
|
|
448
|
+
}
|
|
449
|
+
const exactConstructorLocation = requireSemanticLocation(constructor.location, language, "constructor declaration location", declarationLocation)
|
|
450
|
+
|
|
451
|
+
validateReferenceClassParameters(constructor.parameters, "constructor", language, exactConstructorLocation)
|
|
452
|
+
const methodNames = new Set()
|
|
453
|
+
|
|
454
|
+
for (let methodIndex = 0; methodIndex < declaration.methods.length; methodIndex += 1) {
|
|
455
|
+
const method = declaration.methods[methodIndex]
|
|
456
|
+
const methodLocation = diagnosticLocation(method?.location, location)
|
|
457
|
+
const methodKeys = method && typeof method == "object" && !Array.isArray(method)
|
|
458
|
+
? Object.keys(method).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
459
|
+
|
|
460
|
+
if (!method || method.kind != "MethodDeclaration" ||
|
|
461
|
+
!["body,id,kind,location,name,parameters,returnType",
|
|
462
|
+
"body,effects,failureIds,id,kind,location,name,parameters,returnType"].includes(methodKeys) ||
|
|
463
|
+
method.id != `${declaration.id}:method:${methodIndex}` || !isDenseArray(method.parameters)) {
|
|
464
|
+
unsupportedCapability(language, "missing or invalid method declaration", methodLocation)
|
|
465
|
+
}
|
|
466
|
+
const exactMethodLocation = requireSemanticLocation(method.location, language, "method declaration location", declarationLocation)
|
|
467
|
+
const lifecycleName = language == "php" ? "__construct" : language == "ruby" ? "initialize" :
|
|
468
|
+
language == "javascript" || language == "typescript" ? "constructor" : undefined
|
|
469
|
+
|
|
470
|
+
if (method.name == lifecycleName) {
|
|
471
|
+
unsupportedCapability(language, `reserved reference lifecycle method '${method.name}'`, exactMethodLocation)
|
|
472
|
+
}
|
|
473
|
+
const reservedHook = language == "php" ? /^__/u.test(method.name) :
|
|
474
|
+
language == "ruby" ? rubyReferenceMethodHooks.has(method.name) ||
|
|
475
|
+
typeof method.name == "string" && method.name.endsWith("=") :
|
|
476
|
+
language == "java" ? javaReferenceMethodHooks.has(method.name) :
|
|
477
|
+
language == "javascript" || language == "typescript" ? javascriptReferenceMethodHooks.has(method.name) : false
|
|
478
|
+
|
|
479
|
+
if (reservedHook) unsupportedCapability(language, `reserved reference method '${method.name}'`, exactMethodLocation)
|
|
480
|
+
if (language == "ruby") {
|
|
481
|
+
if (typeof method.name != "string" || !/^[A-Za-z_][A-Za-z0-9_]*[!?=]?$/u.test(method.name)) {
|
|
482
|
+
unsupportedCapability(language, `method identifier '${String(method.name)}'`, exactMethodLocation)
|
|
483
|
+
}
|
|
484
|
+
} else validateTargetIdentifier(language, method.name, "method", exactMethodLocation)
|
|
485
|
+
const normalized = language == "php" ? method.name.toLowerCase() : method.name
|
|
486
|
+
|
|
487
|
+
if (methodNames.has(normalized)) unsupportedCapability(language, `duplicate target method '${method.name}'`, exactMethodLocation)
|
|
488
|
+
if (language == "typescript" && fieldNames.has(normalized)) {
|
|
489
|
+
unsupportedCapability(language, `target class member collision '${method.name}'`, exactMethodLocation)
|
|
490
|
+
}
|
|
491
|
+
methodNames.add(normalized)
|
|
492
|
+
validateReferenceClassParameters(method.parameters, "method", language, exactMethodLocation)
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Validates one reference callable's exact parameter shape and target names.
|
|
499
|
+
* @param {import("../semantic/types.js").Parameter[]} parameters - Candidate parameters.
|
|
500
|
+
* @param {"constructor" | "method"} owner - Callable kind.
|
|
501
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
502
|
+
* @param {import("../semantic/types.js").SourceLocation | undefined} ownerLocation - Fallback location.
|
|
503
|
+
* @returns {void}
|
|
504
|
+
*/
|
|
505
|
+
function validateReferenceClassParameters(parameters, owner, language, ownerLocation) {
|
|
506
|
+
for (const parameter of parameters) {
|
|
507
|
+
const location = diagnosticLocation(parameter?.location, ownerLocation)
|
|
508
|
+
const keys = parameter && typeof parameter == "object" && !Array.isArray(parameter)
|
|
509
|
+
? Object.keys(parameter).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
510
|
+
|
|
511
|
+
if (!parameter || parameter.kind != "Parameter" || keys != "kind,location,name,type") {
|
|
512
|
+
unsupportedCapability(language, `missing or invalid ${owner} parameter`, location)
|
|
513
|
+
}
|
|
514
|
+
const exactLocation = requireSemanticLocation(parameter.location, language, `${owner} parameter location`, ownerLocation)
|
|
515
|
+
|
|
516
|
+
validateTargetBindingIdentifier(language, parameter.name, "parameter", exactLocation)
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Validates one declaration's target-visible type-parameter names and identities.
|
|
522
|
+
* @param {import("../semantic/types.js").FunctionDeclaration | import("../semantic/types.js").RecordDeclaration} declaration - Owner.
|
|
523
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
524
|
+
* @returns {void}
|
|
525
|
+
*/
|
|
526
|
+
function validateTypeParameterTargets(declaration, language) {
|
|
527
|
+
if (declaration.typeParameters === undefined) return
|
|
528
|
+
if (!isDenseArray(declaration.typeParameters)) {
|
|
529
|
+
unsupportedCapability(language, "missing or invalid type parameter declarations", declaration.location)
|
|
530
|
+
}
|
|
531
|
+
const names = new Set()
|
|
532
|
+
/**
|
|
533
|
+
* Normalizes a type/value spelling for target collision checks.
|
|
534
|
+
* @param {string} name - Candidate target name.
|
|
535
|
+
* @returns {string} Target comparison key.
|
|
536
|
+
*/
|
|
537
|
+
const targetKey = (name) => language == "php" ? name.toLowerCase() : name
|
|
538
|
+
const valueNames = new Set([
|
|
539
|
+
declaration.name,
|
|
540
|
+
...(declaration.kind == "FunctionDeclaration" ? declaration.parameters : declaration.fields).map(({name}) => name)
|
|
541
|
+
].map(targetKey))
|
|
542
|
+
|
|
543
|
+
for (let index = 0; index < declaration.typeParameters.length; index += 1) {
|
|
544
|
+
const parameter = declaration.typeParameters[index]
|
|
545
|
+
const keys = parameter && typeof parameter == "object" && !Array.isArray(parameter)
|
|
546
|
+
? Object.keys(parameter).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
547
|
+
|
|
548
|
+
if (!parameter || parameter.kind != "TypeParameter" || keys != "id,kind,location,name" ||
|
|
549
|
+
parameter.id != `${declaration.id}:type:${index}`) {
|
|
550
|
+
unsupportedCapability(language, "missing or invalid type parameter declaration", parameter?.location ?? declaration.location)
|
|
551
|
+
}
|
|
552
|
+
requireSemanticLocation(parameter.location, language, "type parameter location", declaration.location)
|
|
553
|
+
validateTargetTypeIdentifier(language, parameter.name, parameter.location)
|
|
554
|
+
const normalized = targetKey(parameter.name)
|
|
555
|
+
|
|
556
|
+
if (names.has(normalized) || valueNames.has(normalized)) {
|
|
557
|
+
unsupportedCapability(language, `target type parameter collision '${parameter.name}'`, parameter.location)
|
|
558
|
+
}
|
|
559
|
+
names.add(normalized)
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Detects Task 012 IR before a non-cohort backend reaches older feature checks.
|
|
565
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Candidate module.
|
|
566
|
+
* @returns {boolean} Whether generic declarations are present.
|
|
567
|
+
*/
|
|
568
|
+
function moduleHasTypeParameters(module) {
|
|
569
|
+
return [...module.records ?? [], ...module.functions].some((declaration) =>
|
|
570
|
+
Array.isArray(declaration?.typeParameters) && declaration.typeParameters.length > 0)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Rejects Task 008 statements before a non-cohort backend reaches collection-type checks.
|
|
575
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Candidate module.
|
|
576
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target identity.
|
|
577
|
+
* @returns {void}
|
|
578
|
+
*/
|
|
579
|
+
function rejectIterationStatements(module, language) {
|
|
580
|
+
for (const declaration of module.functions) {
|
|
581
|
+
rejectBlockIteration(declaration && typeof declaration == "object" && !Array.isArray(declaration)
|
|
582
|
+
? Reflect.get(declaration, "body")
|
|
583
|
+
: undefined, language)
|
|
584
|
+
}
|
|
585
|
+
rejectBlockIteration(module.entryPoint.body, language)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Rejects Task 032 IR before a non-cohort backend allocates output.
|
|
590
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Candidate module.
|
|
591
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target identity.
|
|
592
|
+
* @returns {void}
|
|
593
|
+
*/
|
|
594
|
+
function rejectConditionControlledLoops(module, language) {
|
|
595
|
+
for (const declaration of module.functions) {
|
|
596
|
+
rejectBlockConditionLoop(declaration && typeof declaration == "object" && !Array.isArray(declaration)
|
|
597
|
+
? Reflect.get(declaration, "body")
|
|
598
|
+
: undefined, language)
|
|
599
|
+
}
|
|
600
|
+
rejectBlockConditionLoop(module.entryPoint.body, language)
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Finds a condition-controlled loop without descending through malformed or cyclic containers.
|
|
605
|
+
* @param {unknown} block - Candidate block.
|
|
606
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target identity.
|
|
607
|
+
* @param {Set<object>} [seen] - Visited containers.
|
|
608
|
+
* @returns {void}
|
|
609
|
+
*/
|
|
610
|
+
function rejectBlockConditionLoop(block, language, seen = new Set()) {
|
|
611
|
+
if (!block || typeof block != "object" || Array.isArray(block) || seen.has(block)) return
|
|
612
|
+
seen.add(block)
|
|
613
|
+
const statements = Reflect.get(block, "statements")
|
|
614
|
+
|
|
615
|
+
if (!Array.isArray(statements)) return
|
|
616
|
+
for (const statement of statements) {
|
|
617
|
+
if (!statement || typeof statement != "object" || Array.isArray(statement)) continue
|
|
618
|
+
const kind = Reflect.get(statement, "kind")
|
|
619
|
+
const location = diagnosticLocation(Reflect.get(statement, "location"), Reflect.get(block, "location"))
|
|
620
|
+
|
|
621
|
+
if (kind == "WhileStatement") unsupportedCapability(language, "Task 032 condition-controlled loop", location)
|
|
622
|
+
if (kind == "IfStatement") {
|
|
623
|
+
rejectBlockConditionLoop(Reflect.get(statement, "consequent"), language, seen)
|
|
624
|
+
rejectBlockConditionLoop(Reflect.get(statement, "alternate"), language, seen)
|
|
625
|
+
} else if (["ForEachStatement", "ForEachMapStatement", "WhileStatement"].includes(String(kind))) {
|
|
626
|
+
rejectBlockConditionLoop(Reflect.get(statement, "body"), language, seen)
|
|
627
|
+
} else if (kind == "TryStatement") {
|
|
628
|
+
rejectBlockConditionLoop(Reflect.get(statement, "body"), language, seen)
|
|
629
|
+
rejectBlockConditionLoop(Reflect.get(statement, "catchBody"), language, seen)
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Finds a Task 008 node without descending through malformed or cyclic containers.
|
|
636
|
+
* @param {unknown} block - Candidate block.
|
|
637
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target identity.
|
|
638
|
+
* @param {Set<object>} [seen] - Visited containers.
|
|
639
|
+
* @returns {void}
|
|
640
|
+
*/
|
|
641
|
+
function rejectBlockIteration(block, language, seen = new Set()) {
|
|
642
|
+
if (!block || typeof block != "object" || Array.isArray(block) || seen.has(block)) return
|
|
643
|
+
seen.add(block)
|
|
644
|
+
const statements = Reflect.get(block, "statements")
|
|
645
|
+
|
|
646
|
+
if (!Array.isArray(statements)) return
|
|
647
|
+
for (const statement of statements) {
|
|
648
|
+
if (!statement || typeof statement != "object" || Array.isArray(statement)) continue
|
|
649
|
+
const kind = Reflect.get(statement, "kind")
|
|
650
|
+
const location = diagnosticLocation(Reflect.get(statement, "location"), Reflect.get(block, "location"))
|
|
651
|
+
|
|
652
|
+
if (["ForEachStatement", "ForEachMapStatement", "BreakStatement", "ContinueStatement"].includes(kind)) {
|
|
653
|
+
unsupportedCapability(language, "Task 008 ordered list iteration", location)
|
|
654
|
+
}
|
|
655
|
+
if (kind == "IfStatement") {
|
|
656
|
+
rejectBlockIteration(Reflect.get(statement, "consequent"), language, seen)
|
|
657
|
+
rejectBlockIteration(Reflect.get(statement, "alternate"), language, seen)
|
|
658
|
+
} else if (kind == "TryStatement") {
|
|
659
|
+
rejectBlockIteration(Reflect.get(statement, "body"), language, seen)
|
|
660
|
+
rejectBlockIteration(Reflect.get(statement, "catchBody"), language, seen)
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Rejects Task 007 optional types before a non-cohort emitter allocates output.
|
|
667
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Candidate semantic module.
|
|
668
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target identity.
|
|
669
|
+
* @returns {void}
|
|
670
|
+
*/
|
|
671
|
+
function rejectOptionalTypes(module, language) {
|
|
672
|
+
for (const declaration of module.functions) {
|
|
673
|
+
if (!declaration || typeof declaration != "object" || Array.isArray(declaration)) continue
|
|
674
|
+
const location = Reflect.get(declaration, "location") ?? module.location
|
|
675
|
+
|
|
676
|
+
if (containsOptionalType(Reflect.get(declaration, "returnType"))) {
|
|
677
|
+
unsupportedCapability(language, "Task 007 optional return type", location)
|
|
678
|
+
}
|
|
679
|
+
for (const parameter of Array.isArray(Reflect.get(declaration, "parameters")) ? Reflect.get(declaration, "parameters") : []) {
|
|
680
|
+
if (containsOptionalType(parameter?.type)) {
|
|
681
|
+
unsupportedCapability(language, "Task 007 optional parameter type", parameter.location ?? location)
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
rejectBlockOptionalTypes(Reflect.get(declaration, "body"), language)
|
|
685
|
+
}
|
|
686
|
+
rejectBlockOptionalTypes(module.entryPoint.body, language)
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Rejects optional local types recursively in one candidate block.
|
|
691
|
+
* @param {unknown} block - Candidate block.
|
|
692
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target identity.
|
|
693
|
+
* @returns {void}
|
|
694
|
+
*/
|
|
695
|
+
function rejectBlockOptionalTypes(block, language) {
|
|
696
|
+
if (!block || typeof block != "object" || Array.isArray(block) || !Array.isArray(Reflect.get(block, "statements"))) return
|
|
697
|
+
for (const statement of Reflect.get(block, "statements")) {
|
|
698
|
+
if (!statement || typeof statement != "object" || Array.isArray(statement)) continue
|
|
699
|
+
if (statement.kind == "LocalDeclaration" && containsOptionalType(statement.type)) {
|
|
700
|
+
unsupportedCapability(language, "Task 007 optional local type", statement.location)
|
|
701
|
+
}
|
|
702
|
+
if (statement.kind == "IfStatement") {
|
|
703
|
+
rejectBlockOptionalTypes(statement.consequent, language)
|
|
704
|
+
if (statement.alternate) rejectBlockOptionalTypes(statement.alternate, language)
|
|
705
|
+
}
|
|
706
|
+
if (statement.kind == "ForEachStatement" || statement.kind == "ForEachMapStatement") {
|
|
707
|
+
if (containsOptionalType(statement.valueBinding?.type)) {
|
|
708
|
+
unsupportedCapability(language, "Task 007 optional iteration binding type", statement.valueBinding.location ?? statement.location)
|
|
709
|
+
}
|
|
710
|
+
rejectBlockOptionalTypes(statement.body, language)
|
|
711
|
+
}
|
|
712
|
+
if (statement.kind == "WhileStatement") rejectBlockOptionalTypes(statement.body, language)
|
|
713
|
+
if (statement.kind == "TryStatement") {
|
|
714
|
+
rejectBlockOptionalTypes(statement.body, language)
|
|
715
|
+
rejectBlockOptionalTypes(statement.catchBody, language)
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Checks one bounded recursive semantic type for an optional node.
|
|
722
|
+
* @param {unknown} type - Candidate type.
|
|
723
|
+
* @param {Set<object>} [seen] - Cycle protection.
|
|
724
|
+
* @returns {boolean} Whether an optional type occurs.
|
|
725
|
+
*/
|
|
726
|
+
function containsOptionalType(type, seen = new Set()) {
|
|
727
|
+
if (!type || typeof type != "object" || Array.isArray(type) || seen.has(type)) return false
|
|
728
|
+
seen.add(type)
|
|
729
|
+
const kind = Reflect.get(type, "kind")
|
|
730
|
+
|
|
731
|
+
if (kind == "OptionalType") return true
|
|
732
|
+
if (kind == "ListType") return containsOptionalType(Reflect.get(type, "elementType"), seen)
|
|
733
|
+
if (kind == "MapType" || kind == "OrderedMapType") return containsOptionalType(Reflect.get(type, "keyType"), seen) ||
|
|
734
|
+
containsOptionalType(Reflect.get(type, "valueType"), seen)
|
|
735
|
+
|
|
736
|
+
return false
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Rejects Task 006 types before a registered non-cohort emitter can allocate an artifact.
|
|
741
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Semantic module.
|
|
742
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target identity.
|
|
743
|
+
* @returns {void}
|
|
744
|
+
*/
|
|
745
|
+
function rejectCollectionTypes(module, language) {
|
|
746
|
+
for (const declaration of module.functions) {
|
|
747
|
+
if (!declaration || typeof declaration != "object" || Array.isArray(declaration)) continue
|
|
748
|
+
const location = Reflect.get(declaration, "location") ?? module.location
|
|
749
|
+
const returnType = Reflect.get(declaration, "returnType")
|
|
750
|
+
|
|
751
|
+
if (isCollectionType(returnType)) {
|
|
752
|
+
unsupportedCapability(language, "Task 006 immutable collection return type", location)
|
|
753
|
+
}
|
|
754
|
+
const parameters = Reflect.get(declaration, "parameters")
|
|
755
|
+
|
|
756
|
+
for (const parameter of Array.isArray(parameters) ? parameters : []) {
|
|
757
|
+
if (isCollectionType(parameter?.type)) unsupportedCapability(language, "Task 006 immutable collection parameter type", parameter.location ?? location)
|
|
758
|
+
}
|
|
759
|
+
rejectBlockCollectionTypes(Reflect.get(declaration, "body"), language)
|
|
760
|
+
}
|
|
761
|
+
rejectBlockCollectionTypes(module.entryPoint.body, language)
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Rejects collection local types recursively in one block.
|
|
766
|
+
* @param {unknown} block - Candidate semantic block.
|
|
767
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target identity.
|
|
768
|
+
* @returns {void}
|
|
769
|
+
*/
|
|
770
|
+
function rejectBlockCollectionTypes(block, language) {
|
|
771
|
+
if (!block || typeof block != "object" || Array.isArray(block) || !Array.isArray(Reflect.get(block, "statements"))) return
|
|
772
|
+
for (const statement of Reflect.get(block, "statements")) {
|
|
773
|
+
if (!statement || typeof statement != "object" || Array.isArray(statement)) continue
|
|
774
|
+
if (statement.kind == "LocalDeclaration" && isCollectionType(statement.type)) {
|
|
775
|
+
unsupportedCapability(language, "Task 006 immutable collection local type", statement.location)
|
|
776
|
+
}
|
|
777
|
+
if (statement.kind == "IfStatement") {
|
|
778
|
+
rejectBlockCollectionTypes(statement.consequent, language)
|
|
779
|
+
if (statement.alternate) rejectBlockCollectionTypes(statement.alternate, language)
|
|
780
|
+
}
|
|
781
|
+
if (statement.kind == "ForEachStatement" || statement.kind == "ForEachMapStatement") {
|
|
782
|
+
if (isCollectionType(statement.valueBinding?.type)) {
|
|
783
|
+
unsupportedCapability(language, "Task 006 immutable collection iteration binding type", statement.valueBinding.location ?? statement.location)
|
|
784
|
+
}
|
|
785
|
+
rejectBlockCollectionTypes(statement.body, language)
|
|
786
|
+
}
|
|
787
|
+
if (statement.kind == "WhileStatement") rejectBlockCollectionTypes(statement.body, language)
|
|
788
|
+
if (statement.kind == "TryStatement") {
|
|
789
|
+
rejectBlockCollectionTypes(statement.body, language)
|
|
790
|
+
rejectBlockCollectionTypes(statement.catchBody, language)
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* Checks a possible recursive collection type without traversing malformed children.
|
|
797
|
+
* @param {unknown} type - Candidate type.
|
|
798
|
+
* @returns {boolean} Whether the outer type is a collection.
|
|
799
|
+
*/
|
|
800
|
+
function isCollectionType(type) {
|
|
801
|
+
return Boolean(type && typeof type == "object" && !Array.isArray(type) &&
|
|
802
|
+
(["ListType", "MapType", "OrderedMapType"].includes(String(Reflect.get(type, "kind")))))
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Rejects semantic names that would capture syntax owned by one backend emitter.
|
|
807
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Semantic module.
|
|
808
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Backend language or binary target.
|
|
809
|
+
* @returns {void}
|
|
810
|
+
*/
|
|
811
|
+
function validateScaffoldingNames(module, language) {
|
|
812
|
+
validateRecordConstructorNames(module, language)
|
|
813
|
+
const needsNativeMap = ["javascript", "typescript"].includes(language) &&
|
|
814
|
+
(moduleContainsExpressionKind(module, "MapLiteral") || moduleContainsExpressionKind(module, "OrderedMapLiteral"))
|
|
815
|
+
const needsPhpCount = language == "php" && moduleContainsExpressionKind(module, "CollectionSizeExpression")
|
|
816
|
+
const javascriptOwnedNames = new Set(["console", ...(needsNativeMap ? ["Map"] : [])])
|
|
817
|
+
const ownedEntryNames = language == "go" ? new Set(["fmt", "int64", "bool", "string", "true", "false"]) :
|
|
818
|
+
language == "csharp" ? new Set(["System"]) : language == "java" ? new Set(["args", "System"]) :
|
|
819
|
+
["javascript", "typescript"].includes(language) ? javascriptOwnedNames : new Set()
|
|
820
|
+
const ownedPrintReceiverNames = language == "go" ? new Set(["fmt", "int64", "bool", "string", "true", "false"]) :
|
|
821
|
+
language == "csharp" || language == "java" ? new Set(["System"]) :
|
|
822
|
+
["javascript", "typescript"].includes(language) ? javascriptOwnedNames : new Set()
|
|
823
|
+
const ownedCallableNames = language == "go" ? new Set(["main", "init", "fmt", "int64", "bool", "string", "true", "false"]) :
|
|
824
|
+
language == "csharp" ? new Set([
|
|
825
|
+
"Equals", "Finalize", "GetHashCode", "GetType", "Main", "MemberwiseClone", "Program", "ReferenceEquals", "System", "ToString"
|
|
826
|
+
]) :
|
|
827
|
+
language == "java" ? new Set(["main"]) :
|
|
828
|
+
["javascript", "typescript"].includes(language) ? javascriptOwnedNames :
|
|
829
|
+
needsPhpCount ? new Set(["count"]) :
|
|
830
|
+
language == "ruby" ? new Set(["puts", "send", "public_send", "__send__"]) : new Set()
|
|
831
|
+
/**
|
|
832
|
+
* Protects print/factory spellings inside one callable scope.
|
|
833
|
+
* @param {import("../semantic/types.js").Parameter[]} parameters - Callable parameters.
|
|
834
|
+
* @param {import("../semantic/types.js").Block} body - Callable body.
|
|
835
|
+
* @param {"function" | "constructor" | "method"} owner - Diagnostic owner.
|
|
836
|
+
*/
|
|
837
|
+
const validateCallableBindings = (parameters, body, owner) => {
|
|
838
|
+
for (const parameter of parameters) {
|
|
839
|
+
if (ownedPrintReceiverNames.has(parameter.name)) {
|
|
840
|
+
unsupportedCapability(language, `${owner} parameter '${parameter.name}' captures backend scaffolding`, parameter.location)
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
for (const statement of allStatements(body)) {
|
|
844
|
+
if (statement.kind == "LocalDeclaration" && ownedPrintReceiverNames.has(statement.name)) {
|
|
845
|
+
unsupportedCapability(language, `${owner} local '${statement.name}' captures backend scaffolding`, statement.location)
|
|
846
|
+
}
|
|
847
|
+
if (statement.kind == "ForEachStatement" && ownedPrintReceiverNames.has(statement.valueBinding.name)) {
|
|
848
|
+
unsupportedCapability(language, `${owner} iteration binding '${statement.valueBinding.name}' captures backend scaffolding`,
|
|
849
|
+
statement.valueBinding.location)
|
|
850
|
+
}
|
|
851
|
+
if (statement.kind == "ForEachMapStatement") {
|
|
852
|
+
for (const binding of [statement.keyBinding, statement.valueBinding]) {
|
|
853
|
+
if (ownedPrintReceiverNames.has(binding.name)) {
|
|
854
|
+
unsupportedCapability(language, `${owner} iteration binding '${binding.name}' captures backend scaffolding`, binding.location)
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
if (statement.kind == "TryStatement" && ownedPrintReceiverNames.has(statement.catchBinding.name)) {
|
|
859
|
+
unsupportedCapability(language, `${owner} catch binding '${statement.catchBinding.name}' captures backend scaffolding`,
|
|
860
|
+
statement.catchBinding.location)
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
for (const statement of allStatements(module.entryPoint.body)) {
|
|
866
|
+
if (statement.kind == "LocalDeclaration" && ownedEntryNames.has(statement.name)) {
|
|
867
|
+
unsupportedCapability(language, `entry local '${statement.name}' captures backend scaffolding`, statement.location)
|
|
868
|
+
}
|
|
869
|
+
if (statement.kind == "ForEachStatement" && ownedEntryNames.has(statement.valueBinding.name)) {
|
|
870
|
+
unsupportedCapability(language, `entry iteration binding '${statement.valueBinding.name}' captures backend scaffolding`,
|
|
871
|
+
statement.valueBinding.location)
|
|
872
|
+
}
|
|
873
|
+
if (statement.kind == "ForEachMapStatement") {
|
|
874
|
+
for (const binding of [statement.keyBinding, statement.valueBinding]) {
|
|
875
|
+
if (ownedEntryNames.has(binding.name)) {
|
|
876
|
+
unsupportedCapability(language, `entry iteration binding '${binding.name}' captures backend scaffolding`, binding.location)
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
if (statement.kind == "TryStatement" && ownedEntryNames.has(statement.catchBinding.name)) {
|
|
881
|
+
unsupportedCapability(language, `entry catch binding '${statement.catchBinding.name}' captures backend scaffolding`,
|
|
882
|
+
statement.catchBinding.location)
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
for (const declaration of module.functions) {
|
|
886
|
+
const targetName = language == "php" ? declaration.name.toLowerCase() : declaration.name
|
|
887
|
+
|
|
888
|
+
if (ownedCallableNames.has(targetName)) {
|
|
889
|
+
unsupportedCapability(language, `function '${declaration.name}' captures backend scaffolding`, declarationNameLocation(declaration))
|
|
890
|
+
}
|
|
891
|
+
if (language == "java") {
|
|
892
|
+
const signature = emittedJavaFunctionSignature(declaration)
|
|
893
|
+
|
|
894
|
+
if (signature && javaObjectInstanceMethodSignatures.has(signature)) {
|
|
895
|
+
unsupportedCapability(
|
|
896
|
+
language,
|
|
897
|
+
`function '${signature}' conflicts with an inherited java.lang.Object instance method`,
|
|
898
|
+
declarationNameLocation(declaration)
|
|
899
|
+
)
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
validateCallableBindings(declaration.parameters, declaration.body, "function")
|
|
903
|
+
}
|
|
904
|
+
for (const declaration of module.classes ?? []) {
|
|
905
|
+
validateCallableBindings(declaration.constructor.parameters, declaration.constructor.body, "constructor")
|
|
906
|
+
for (const method of declaration.methods) validateCallableBindings(method.parameters, method.body, "method")
|
|
907
|
+
}
|
|
908
|
+
if (language == "java") validateJavaUtilFactoryNames(module)
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Rejects JavaScript-family lexical bindings that can capture emitted bare record constructor names.
|
|
913
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Validated-shape semantic module.
|
|
914
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
915
|
+
* @returns {void}
|
|
916
|
+
*/
|
|
917
|
+
function validateRecordConstructorNames(module, language) {
|
|
918
|
+
if (language != "javascript" && language != "typescript") return
|
|
919
|
+
const recordNames = new Set([
|
|
920
|
+
...(module.records ?? []).map((record) => record.name),
|
|
921
|
+
...(module.classes ?? []).map((declaration) => declaration.name),
|
|
922
|
+
...(module.errors ?? []).map((error) => error.name)
|
|
923
|
+
])
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* Checks declarations nested beneath one owning block.
|
|
927
|
+
* @param {import("../semantic/types.js").Block} block - Block to inspect.
|
|
928
|
+
* @param {"entry" | "function" | "constructor" | "method"} owner - Owning scope kind.
|
|
929
|
+
* @returns {void}
|
|
930
|
+
*/
|
|
931
|
+
const validateBlockNames = (block, owner) => {
|
|
932
|
+
for (const statement of allStatements(block)) {
|
|
933
|
+
if (statement.kind == "LocalDeclaration" && recordNames.has(statement.name)) {
|
|
934
|
+
unsupportedCapability(language, `${owner} local '${statement.name}' captures record constructor`, bindingNameLocation(statement))
|
|
935
|
+
}
|
|
936
|
+
if (statement.kind == "ForEachStatement" && recordNames.has(statement.valueBinding.name)) {
|
|
937
|
+
unsupportedCapability(
|
|
938
|
+
language,
|
|
939
|
+
`${owner} iteration binding '${statement.valueBinding.name}' captures record constructor`,
|
|
940
|
+
bindingNameLocation(statement.valueBinding)
|
|
941
|
+
)
|
|
942
|
+
}
|
|
943
|
+
if (statement.kind == "ForEachMapStatement") {
|
|
944
|
+
for (const binding of [statement.keyBinding, statement.valueBinding]) {
|
|
945
|
+
if (recordNames.has(binding.name)) {
|
|
946
|
+
unsupportedCapability(language, `${owner} iteration binding '${binding.name}' captures record constructor`,
|
|
947
|
+
bindingNameLocation(binding))
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
if (statement.kind == "TryStatement" && recordNames.has(statement.catchBinding.name)) {
|
|
952
|
+
unsupportedCapability(
|
|
953
|
+
language,
|
|
954
|
+
`${owner} catch binding '${statement.catchBinding.name}' captures nominal constructor`,
|
|
955
|
+
bindingNameLocation(statement.catchBinding)
|
|
956
|
+
)
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
validateBlockNames(module.entryPoint.body, "entry")
|
|
962
|
+
for (const declaration of module.functions) {
|
|
963
|
+
for (const parameter of declaration.parameters) {
|
|
964
|
+
if (recordNames.has(parameter.name)) {
|
|
965
|
+
unsupportedCapability(language, `function parameter '${parameter.name}' captures record constructor`, bindingNameLocation(parameter))
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
validateBlockNames(declaration.body, "function")
|
|
969
|
+
}
|
|
970
|
+
for (const declaration of module.classes ?? []) {
|
|
971
|
+
for (const parameter of declaration.constructor.parameters) {
|
|
972
|
+
if (recordNames.has(parameter.name)) {
|
|
973
|
+
unsupportedCapability(language, `constructor parameter '${parameter.name}' captures nominal constructor`,
|
|
974
|
+
bindingNameLocation(parameter))
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
validateBlockNames(declaration.constructor.body, "constructor")
|
|
978
|
+
for (const method of declaration.methods) {
|
|
979
|
+
for (const parameter of method.parameters) {
|
|
980
|
+
if (recordNames.has(parameter.name)) {
|
|
981
|
+
unsupportedCapability(language, `method parameter '${parameter.name}' captures nominal constructor`,
|
|
982
|
+
bindingNameLocation(parameter))
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
validateBlockNames(method.body, "method")
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* Rejects lexical bindings that would capture Java's package qualifier in emitted collection factories.
|
|
992
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Validated semantic module.
|
|
993
|
+
* @returns {void}
|
|
994
|
+
*/
|
|
995
|
+
function validateJavaUtilFactoryNames(module) {
|
|
996
|
+
/**
|
|
997
|
+
* Walks one lexical block while retaining only a currently visible capture.
|
|
998
|
+
* @param {import("../semantic/types.js").Block} block - Block to inspect in semantic order.
|
|
999
|
+
* @param {{detail: string, location: import("../semantic/types.js").SourceLocation} | undefined} inherited - Visible capture.
|
|
1000
|
+
* @param {"entry" | "function" | "constructor" | "method"} owner - Owning scope kind.
|
|
1001
|
+
* @returns {void}
|
|
1002
|
+
*/
|
|
1003
|
+
const validateBlockNames = (block, inherited, owner) => {
|
|
1004
|
+
let capture = inherited
|
|
1005
|
+
|
|
1006
|
+
for (const statement of block.statements) {
|
|
1007
|
+
if (statement.kind == "LocalDeclaration" && statement.name == "java") {
|
|
1008
|
+
capture = {
|
|
1009
|
+
detail: `${owner} local 'java' captures java.util factory syntax`,
|
|
1010
|
+
location: statement.location
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
const expression = statement.kind == "IfStatement" ? statement.condition :
|
|
1014
|
+
statement.kind == "WhileStatement" ? statement.condition :
|
|
1015
|
+
statement.kind == "ForEachStatement" ? statement.list :
|
|
1016
|
+
statement.kind == "ForEachMapStatement" ? statement.map :
|
|
1017
|
+
statement.kind == "LocalDeclaration" ? statement.initializer :
|
|
1018
|
+
statement.kind == "AssignmentStatement" ? statement.expression :
|
|
1019
|
+
statement.kind == "PrivateFieldWriteStatement" ? statement.expression :
|
|
1020
|
+
statement.kind == "ReturnStatement" ? statement.expression :
|
|
1021
|
+
statement.kind == "ExpressionStatement" || statement.kind == "PrintStatement" ? statement.expression :
|
|
1022
|
+
statement.kind == "RaiseStatement" ? statement.error.message : undefined
|
|
1023
|
+
|
|
1024
|
+
if (capture && expression && (expressionContainsKind(expression, "ListLiteral") ||
|
|
1025
|
+
expressionContainsKind(expression, "MapLiteral") || expressionContainsKind(expression, "OrderedMapLiteral") ||
|
|
1026
|
+
expressionContainsKind(expression, "OptionalNone") ||
|
|
1027
|
+
expressionContainsKind(expression, "OptionalSome"))) {
|
|
1028
|
+
unsupportedCapability("java", capture.detail, capture.location)
|
|
1029
|
+
}
|
|
1030
|
+
if (statement.kind == "IfStatement") {
|
|
1031
|
+
validateBlockNames(statement.consequent, capture, owner)
|
|
1032
|
+
if (statement.alternate) validateBlockNames(statement.alternate, capture, owner)
|
|
1033
|
+
}
|
|
1034
|
+
if (statement.kind == "WhileStatement") validateBlockNames(statement.body, capture, owner)
|
|
1035
|
+
if (statement.kind == "ForEachStatement") {
|
|
1036
|
+
const loopCapture = statement.valueBinding.name == "java" ? {
|
|
1037
|
+
detail: `${owner} iteration binding 'java' captures java.util factory syntax`,
|
|
1038
|
+
location: statement.valueBinding.location
|
|
1039
|
+
} : capture
|
|
1040
|
+
|
|
1041
|
+
validateBlockNames(statement.body, loopCapture, owner)
|
|
1042
|
+
}
|
|
1043
|
+
if (statement.kind == "ForEachMapStatement") {
|
|
1044
|
+
const capturedBinding = [statement.keyBinding, statement.valueBinding].find(({name}) => name == "java")
|
|
1045
|
+
const loopCapture = capturedBinding ? {
|
|
1046
|
+
detail: `${owner} iteration binding 'java' captures java.util factory syntax`,
|
|
1047
|
+
location: capturedBinding.location
|
|
1048
|
+
} : capture
|
|
1049
|
+
|
|
1050
|
+
validateBlockNames(statement.body, loopCapture, owner)
|
|
1051
|
+
}
|
|
1052
|
+
if (statement.kind == "TryStatement") {
|
|
1053
|
+
validateBlockNames(statement.body, capture, owner)
|
|
1054
|
+
const catchCapture = statement.catchBinding.name == "java" ? {
|
|
1055
|
+
detail: `${owner} catch binding 'java' captures java.util factory syntax`,
|
|
1056
|
+
location: statement.catchBinding.location
|
|
1057
|
+
} : capture
|
|
1058
|
+
|
|
1059
|
+
validateBlockNames(statement.catchBody, catchCapture, owner)
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
validateBlockNames(module.entryPoint.body, undefined, "entry")
|
|
1065
|
+
for (const declaration of module.functions) {
|
|
1066
|
+
const parameter = declaration.parameters.find(({name}) => name == "java")
|
|
1067
|
+
const capture = parameter ? {
|
|
1068
|
+
detail: "function parameter 'java' captures java.util factory syntax",
|
|
1069
|
+
location: parameter.location
|
|
1070
|
+
} : undefined
|
|
1071
|
+
|
|
1072
|
+
validateBlockNames(declaration.body, capture, "function")
|
|
1073
|
+
}
|
|
1074
|
+
for (const declaration of module.classes ?? []) {
|
|
1075
|
+
const callables = [
|
|
1076
|
+
{body: declaration.constructor.body, owner: /** @type {const} */ ("constructor"), parameters: declaration.constructor.parameters},
|
|
1077
|
+
...declaration.methods.map((method) => ({body: method.body, owner: /** @type {const} */ ("method"), parameters: method.parameters}))
|
|
1078
|
+
]
|
|
52
1079
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
1080
|
+
for (const callable of callables) {
|
|
1081
|
+
const parameter = callable.parameters.find(({name}) => name == "java")
|
|
1082
|
+
const capture = parameter ? {
|
|
1083
|
+
detail: `${callable.owner} parameter 'java' captures java.util factory syntax`,
|
|
1084
|
+
location: parameter.location
|
|
1085
|
+
} : undefined
|
|
57
1086
|
|
|
58
|
-
|
|
59
|
-
if (!parameter || typeof parameter != "object" || Array.isArray(parameter) || parameter.kind != "Parameter") {
|
|
60
|
-
unsupportedCapability(language, "missing or invalid parameter", functionDeclaration.location)
|
|
61
|
-
}
|
|
62
|
-
validateTargetBindingIdentifier(language, parameter.name, "parameter", parameter.location)
|
|
1087
|
+
validateBlockNames(callable.body, capture, callable.owner)
|
|
63
1088
|
}
|
|
64
1089
|
}
|
|
65
|
-
validateScaffoldingNames(module, language)
|
|
66
|
-
validateBackendTypes(module, language)
|
|
67
1090
|
}
|
|
68
1091
|
|
|
69
1092
|
/**
|
|
70
|
-
*
|
|
71
|
-
* @param {import("../semantic/types.js").SemanticModule} module -
|
|
72
|
-
* @param {import("../semantic/types.js").
|
|
73
|
-
* @returns {
|
|
1093
|
+
* Reports whether a module contains an expression kind that requires target runtime scaffolding.
|
|
1094
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Validated-shape semantic module.
|
|
1095
|
+
* @param {import("../semantic/types.js").Expression["kind"]} kind - Sought expression kind.
|
|
1096
|
+
* @returns {boolean} Whether the kind occurs.
|
|
74
1097
|
*/
|
|
75
|
-
function
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const ownedPrintReceiverNames = language == "go" ? new Set(["fmt", "int64", "bool", "string", "true", "false"]) :
|
|
80
|
-
language == "csharp" || language == "java" ? new Set(["System"]) :
|
|
81
|
-
["javascript", "typescript"].includes(language) ? new Set(["console"]) : new Set()
|
|
82
|
-
const ownedCallableNames = language == "go" ? new Set(["main", "init", "fmt", "int64", "bool", "string", "true", "false"]) :
|
|
83
|
-
language == "csharp" ? new Set([
|
|
84
|
-
"Equals", "Finalize", "GetHashCode", "GetType", "Main", "MemberwiseClone", "Program", "ReferenceEquals", "System", "ToString"
|
|
85
|
-
]) :
|
|
86
|
-
["javascript", "typescript"].includes(language) ? new Set(["console"]) :
|
|
87
|
-
language == "ruby" ? new Set(["puts"]) : new Set()
|
|
1098
|
+
function moduleContainsExpressionKind(module, kind) {
|
|
1099
|
+
const classBlocks = (module.classes ?? []).flatMap((declaration) => [
|
|
1100
|
+
declaration.constructor.body, ...declaration.methods.map((method) => method.body)
|
|
1101
|
+
])
|
|
88
1102
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
1103
|
+
return [module.entryPoint.body, ...module.functions.map((declaration) => declaration.body), ...classBlocks].some((block) =>
|
|
1104
|
+
allStatements(block).some((statement) => {
|
|
1105
|
+
const expression = statement.kind == "IfStatement" ? statement.condition :
|
|
1106
|
+
statement.kind == "WhileStatement" ? statement.condition :
|
|
1107
|
+
statement.kind == "ForEachStatement" ? statement.list :
|
|
1108
|
+
statement.kind == "ForEachMapStatement" ? statement.map :
|
|
1109
|
+
statement.kind == "LocalDeclaration" ? statement.initializer :
|
|
1110
|
+
statement.kind == "AssignmentStatement" ? statement.expression :
|
|
1111
|
+
statement.kind == "PrivateFieldWriteStatement" ? statement.expression :
|
|
1112
|
+
statement.kind == "ReturnStatement" ? statement.expression :
|
|
1113
|
+
statement.kind == "ExpressionStatement" || statement.kind == "PrintStatement" ? statement.expression :
|
|
1114
|
+
statement.kind == "RaiseStatement" ? statement.error.message : undefined
|
|
1115
|
+
|
|
1116
|
+
return expression ? expressionContainsKind(expression, kind) : false
|
|
1117
|
+
}))
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* Searches one validated expression tree for a semantic kind.
|
|
1122
|
+
* @param {import("../semantic/types.js").Expression} expression - Expression root.
|
|
1123
|
+
* @param {import("../semantic/types.js").Expression["kind"]} kind - Sought kind.
|
|
1124
|
+
* @returns {boolean} Whether the kind occurs.
|
|
1125
|
+
*/
|
|
1126
|
+
function expressionContainsKind(expression, kind) {
|
|
1127
|
+
if (expression.kind == kind) return true
|
|
1128
|
+
if (expression.kind == "UnaryExpression") return expressionContainsKind(expression.operand, kind)
|
|
1129
|
+
if (expression.kind == "BinaryExpression") {
|
|
1130
|
+
return expressionContainsKind(expression.left, kind) || expressionContainsKind(expression.right, kind)
|
|
93
1131
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
1132
|
+
if (expression.kind == "CallExpression") return expression.arguments.some((argument) => expressionContainsKind(argument, kind))
|
|
1133
|
+
if (expression.kind == "MethodCallExpression") {
|
|
1134
|
+
return expressionContainsKind(expression.receiver, kind) ||
|
|
1135
|
+
expression.arguments.some((argument) => expressionContainsKind(argument, kind))
|
|
1136
|
+
}
|
|
1137
|
+
if (expression.kind == "RecordConstruction" || expression.kind == "ReferenceConstruction") {
|
|
1138
|
+
return expression.arguments.some((argument) => expressionContainsKind(argument, kind))
|
|
1139
|
+
}
|
|
1140
|
+
if (expression.kind == "MemberRead" || expression.kind == "PrivateFieldRead") {
|
|
1141
|
+
return expressionContainsKind(expression.receiver, kind)
|
|
1142
|
+
}
|
|
1143
|
+
if (expression.kind == "ListLiteral") return expression.elements.some((element) => expressionContainsKind(element, kind))
|
|
1144
|
+
if (expression.kind == "MapLiteral" || expression.kind == "OrderedMapLiteral") {
|
|
1145
|
+
return expression.entries.some((entry) => expressionContainsKind(entry.key, kind) || expressionContainsKind(entry.value, kind))
|
|
1146
|
+
}
|
|
1147
|
+
if (expression.kind == "ListIndexExpression") {
|
|
1148
|
+
return expressionContainsKind(expression.collection, kind) || expressionContainsKind(expression.index, kind)
|
|
1149
|
+
}
|
|
1150
|
+
if (expression.kind == "MapLookupExpression") {
|
|
1151
|
+
return expressionContainsKind(expression.collection, kind) || expressionContainsKind(expression.key, kind)
|
|
1152
|
+
}
|
|
1153
|
+
if (expression.kind == "CollectionSizeExpression") return expressionContainsKind(expression.collection, kind)
|
|
1154
|
+
if (expression.kind == "OptionalSome") return expressionContainsKind(expression.value, kind)
|
|
1155
|
+
if (expression.kind == "OptionalIsPresent" || expression.kind == "OptionalUnwrap") {
|
|
1156
|
+
return expressionContainsKind(expression.operand, kind)
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
return false
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/**
|
|
1163
|
+
* Returns a parser-backed function-name location when one survived semantic adaptation.
|
|
1164
|
+
* @param {import("../semantic/types.js").FunctionDeclaration} declaration - Semantic function declaration.
|
|
1165
|
+
* @returns {import("../semantic/types.js").SourceLocation} Exact name or declaration location.
|
|
1166
|
+
*/
|
|
1167
|
+
function declarationNameLocation(declaration) {
|
|
1168
|
+
return declaration.sourceProvenance?.ranges.name ?? declaration.location
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* Returns a parser-backed binding-name location when one survived semantic adaptation.
|
|
1173
|
+
* @param {import("../semantic/types.js").Parameter | import("../semantic/types.js").LocalDeclaration | import("../semantic/types.js").ValueBinding | import("../semantic/types.js").CatchBinding} binding - Semantic binding.
|
|
1174
|
+
* @returns {import("../semantic/types.js").SourceLocation} Exact name or binding location.
|
|
1175
|
+
*/
|
|
1176
|
+
function bindingNameLocation(binding) {
|
|
1177
|
+
return binding.sourceProvenance?.ranges.name ?? binding.location
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/**
|
|
1181
|
+
* Builds the Java method signature used for inherited-instance collision checks.
|
|
1182
|
+
* @param {import("../semantic/types.js").FunctionDeclaration} declaration - Candidate Java target function.
|
|
1183
|
+
* @returns {string | undefined} Emitted name and parameter types when all types are supported.
|
|
1184
|
+
*/
|
|
1185
|
+
function emittedJavaFunctionSignature(declaration) {
|
|
1186
|
+
const parameterTypes = []
|
|
1187
|
+
|
|
1188
|
+
for (const parameter of declaration.parameters) {
|
|
1189
|
+
const type = parameter.type
|
|
1190
|
+
|
|
1191
|
+
if (!type || typeof type != "object" || Array.isArray(type)) return undefined
|
|
1192
|
+
const emitted = type.kind == "ListType" ? "java.util.List" : type.kind == "MapType" ? "java.util.Map" :
|
|
1193
|
+
type.kind == "OrderedMapType" ? "java.util.SequencedMap" :
|
|
1194
|
+
type.kind == "TypeReference" ? emitScalarType("java", type) : undefined
|
|
1195
|
+
|
|
1196
|
+
if (!emitted) return undefined
|
|
1197
|
+
parameterTypes.push(emitted)
|
|
108
1198
|
}
|
|
1199
|
+
|
|
1200
|
+
return `${declaration.name}(${parameterTypes.join(",")})`
|
|
109
1201
|
}
|
|
110
1202
|
|
|
111
1203
|
/**
|
|
@@ -114,48 +1206,121 @@ function validateScaffoldingNames(module, language) {
|
|
|
114
1206
|
* @returns {import("../semantic/types.js").Statement[]} Statements including nested branches.
|
|
115
1207
|
*/
|
|
116
1208
|
function allStatements(block) {
|
|
117
|
-
return block.statements.flatMap((statement) =>
|
|
118
|
-
|
|
119
|
-
|
|
1209
|
+
return block.statements.flatMap((statement) => {
|
|
1210
|
+
if (statement.kind == "IfStatement") {
|
|
1211
|
+
return [statement, ...allStatements(statement.consequent), ...(statement.alternate ? allStatements(statement.alternate) : [])]
|
|
1212
|
+
}
|
|
1213
|
+
if (statement.kind == "ForEachStatement" || statement.kind == "ForEachMapStatement") {
|
|
1214
|
+
return [statement, ...allStatements(statement.body)]
|
|
1215
|
+
}
|
|
1216
|
+
if (statement.kind == "WhileStatement") return [statement, ...allStatements(statement.body)]
|
|
1217
|
+
if (statement.kind == "TryStatement") return [statement, ...allStatements(statement.body), ...allStatements(statement.catchBody)]
|
|
1218
|
+
|
|
1219
|
+
return [statement]
|
|
1220
|
+
})
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* Indexes exact loop identities before recursive target validation so cross-function targets fail deterministically.
|
|
1225
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Candidate semantic module.
|
|
1226
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
1227
|
+
* @returns {Map<string, object>} Loop owner by module-local identity.
|
|
1228
|
+
*/
|
|
1229
|
+
function collectLoopOwners(module, language) {
|
|
1230
|
+
const owners = new Map()
|
|
1231
|
+
const classOwners = (module.classes ?? []).flatMap((declaration) => [declaration.constructor, ...declaration.methods])
|
|
1232
|
+
|
|
1233
|
+
for (const owner of [...classOwners, ...module.functions, module.entryPoint]) {
|
|
1234
|
+
if (owner && typeof owner == "object" && !Array.isArray(owner)) visit(Reflect.get(owner, "body"), owner)
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
return owners
|
|
1238
|
+
|
|
1239
|
+
/**
|
|
1240
|
+
* Visits loop containers while bounding malformed cycles.
|
|
1241
|
+
* @param {unknown} block - Candidate block.
|
|
1242
|
+
* @param {object} owner - Enclosing function or entry point.
|
|
1243
|
+
* @param {Set<object>} [active] - Blocks on the active recursive path.
|
|
1244
|
+
* @returns {void}
|
|
1245
|
+
*/
|
|
1246
|
+
function visit(block, owner, active = new Set()) {
|
|
1247
|
+
if (!block || typeof block != "object" || Array.isArray(block) || active.has(block)) return
|
|
1248
|
+
const statements = Reflect.get(block, "statements")
|
|
1249
|
+
|
|
1250
|
+
if (!Array.isArray(statements)) return
|
|
1251
|
+
const path = new Set(active)
|
|
1252
|
+
|
|
1253
|
+
path.add(block)
|
|
1254
|
+
for (const statement of statements) {
|
|
1255
|
+
if (!statement || typeof statement != "object" || Array.isArray(statement)) continue
|
|
1256
|
+
const kind = String(Reflect.get(statement, "kind"))
|
|
1257
|
+
|
|
1258
|
+
if (["ForEachStatement", "ForEachMapStatement", "WhileStatement"].includes(kind)) {
|
|
1259
|
+
const id = Reflect.get(statement, "id")
|
|
1260
|
+
const location = diagnosticLocation(Reflect.get(statement, "location"), Reflect.get(block, "location"))
|
|
1261
|
+
|
|
1262
|
+
if (typeof id != "string" || !/^loop:[0-9]+$/u.test(id)) continue
|
|
1263
|
+
if (owners.has(id)) unsupportedCapability(language, "duplicate or invalid loop identity", location)
|
|
1264
|
+
owners.set(id, owner)
|
|
1265
|
+
visit(Reflect.get(statement, "body"), owner, path)
|
|
1266
|
+
} else if (kind == "IfStatement") {
|
|
1267
|
+
visit(Reflect.get(statement, "consequent"), owner, path)
|
|
1268
|
+
visit(Reflect.get(statement, "alternate"), owner, path)
|
|
1269
|
+
} else if (kind == "TryStatement") {
|
|
1270
|
+
visit(Reflect.get(statement, "body"), owner, path)
|
|
1271
|
+
visit(Reflect.get(statement, "catchBody"), owner, path)
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
120
1275
|
}
|
|
121
1276
|
|
|
122
1277
|
/**
|
|
123
1278
|
* Validates one complete semantic block before emission.
|
|
124
1279
|
* @param {unknown} block - Candidate block.
|
|
125
|
-
* @param {import("../semantic/types.js").
|
|
1280
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Backend language or binary target.
|
|
126
1281
|
* @param {import("../semantic/types.js").SourceLocation | undefined} ownerLocation - Enclosing location.
|
|
1282
|
+
* @param {{active: (import("../semantic/types.js").ForEachStatement | import("../semantic/types.js").ForEachMapStatement | import("../semantic/types.js").WhileStatement)[], owner: object, owners: Map<string, object>}} loopContext - Active loops and their lexical owner.
|
|
1283
|
+
* @param {Set<object>} [activePath] - Blocks on the active recursive path.
|
|
1284
|
+
* @param {Set<string>} [externalDeclarationIds] - Resolved declarations whose source spelling is target-independent.
|
|
127
1285
|
* @returns {void}
|
|
128
1286
|
*/
|
|
129
|
-
function validateBlock(block, language, ownerLocation) {
|
|
1287
|
+
function validateBlock(block, language, ownerLocation, loopContext, activePath = new Set(), externalDeclarationIds = new Set()) {
|
|
130
1288
|
if (!block || typeof block != "object" || Array.isArray(block)) {
|
|
131
1289
|
return unsupportedCapability(language, "missing or invalid block", ownerLocation)
|
|
132
1290
|
}
|
|
133
1291
|
|
|
134
1292
|
const candidate = /** @type {import("../semantic/types.js").Block} */ (block)
|
|
135
|
-
const location = candidate.location
|
|
1293
|
+
const location = diagnosticLocation(candidate.location, ownerLocation)
|
|
136
1294
|
|
|
1295
|
+
if (activePath.has(candidate)) unsupportedCapability(language, "cyclic block", location)
|
|
137
1296
|
if (candidate.kind != "Block") unsupportedCapability(language, `block ${String(Reflect.get(block, "kind"))}`, location)
|
|
138
1297
|
if (!Array.isArray(candidate.statements)) unsupportedCapability(language, "missing or invalid block statements", location)
|
|
1298
|
+
const blockPath = new Set(activePath)
|
|
139
1299
|
|
|
140
|
-
|
|
1300
|
+
blockPath.add(candidate)
|
|
1301
|
+
for (const statement of candidate.statements) {
|
|
1302
|
+
validateStatement(statement, language, location, loopContext, blockPath, externalDeclarationIds)
|
|
1303
|
+
}
|
|
141
1304
|
}
|
|
142
1305
|
|
|
143
1306
|
/**
|
|
144
1307
|
* Validates one supported statement recursively.
|
|
145
1308
|
* @param {unknown} statement - Candidate statement.
|
|
146
|
-
* @param {import("../semantic/types.js").
|
|
1309
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Backend language or binary target.
|
|
147
1310
|
* @param {import("../semantic/types.js").SourceLocation | undefined} ownerLocation - Enclosing body location.
|
|
1311
|
+
* @param {{active: (import("../semantic/types.js").ForEachStatement | import("../semantic/types.js").ForEachMapStatement | import("../semantic/types.js").WhileStatement)[], owner: object, owners: Map<string, object>}} loopContext - Active loops and their lexical owner.
|
|
1312
|
+
* @param {Set<object>} [activePath] - Active block containers.
|
|
1313
|
+
* @param {Set<string>} [externalDeclarationIds] - Resolved declarations whose source spelling is target-independent.
|
|
148
1314
|
* @returns {void}
|
|
149
1315
|
*/
|
|
150
|
-
function validateStatement(statement, language, ownerLocation) {
|
|
1316
|
+
function validateStatement(statement, language, ownerLocation, loopContext, activePath = new Set(), externalDeclarationIds = new Set()) {
|
|
151
1317
|
if (!statement || typeof statement != "object" || Array.isArray(statement)) {
|
|
152
1318
|
return unsupportedCapability(language, "missing or invalid statement", ownerLocation)
|
|
153
1319
|
}
|
|
154
1320
|
|
|
155
1321
|
const kind = Reflect.get(statement, "kind")
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
)
|
|
1322
|
+
const ownLocation = Reflect.get(statement, "location")
|
|
1323
|
+
const location = diagnosticLocation(ownLocation, ownerLocation)
|
|
159
1324
|
|
|
160
1325
|
if (typeof kind != "string") return unsupportedCapability(language, "missing or invalid statement", location)
|
|
161
1326
|
if (kind == "LocalDeclaration") {
|
|
@@ -165,36 +1330,319 @@ function validateStatement(statement, language, ownerLocation) {
|
|
|
165
1330
|
unsupportedCapability(language, "local declaration with invalid mutability", location)
|
|
166
1331
|
}
|
|
167
1332
|
validateTargetBindingIdentifier(language, declaration.name, "local", location)
|
|
168
|
-
|
|
1333
|
+
if (declaration.type?.kind == "OrderedMapType" && declaration.initializer?.kind != "OrderedMapLiteral") {
|
|
1334
|
+
unsupportedCapability(language, "ordered-map local requires a direct literal initializer",
|
|
1335
|
+
diagnosticLocation(declaration.initializer?.location, location))
|
|
1336
|
+
}
|
|
1337
|
+
if (declaration.initializer?.kind == "OrderedMapLiteral" && declaration.mutable !== false) {
|
|
1338
|
+
unsupportedCapability(language, "ordered-map local must be immutable", location)
|
|
1339
|
+
}
|
|
1340
|
+
validateExpression(declaration.initializer, language, location, false, new Set(), externalDeclarationIds,
|
|
1341
|
+
language == "java" && declaration.initializer?.kind == "OrderedMapLiteral")
|
|
169
1342
|
return
|
|
170
1343
|
}
|
|
171
1344
|
if (kind == "AssignmentStatement") {
|
|
172
1345
|
const assignment = /** @type {import("../semantic/types.js").AssignmentStatement} */ (statement)
|
|
173
1346
|
|
|
174
1347
|
validateAssignmentTarget(assignment.target, language, location)
|
|
175
|
-
validateExpression(assignment.expression, language, location)
|
|
1348
|
+
validateExpression(assignment.expression, language, location, false, new Set(), externalDeclarationIds)
|
|
1349
|
+
return
|
|
1350
|
+
}
|
|
1351
|
+
if (kind == "PrivateFieldWriteStatement") {
|
|
1352
|
+
const assignment = /** @type {import("../semantic/types.js").PrivateFieldWriteStatement} */ (statement)
|
|
1353
|
+
const fields = Object.keys(assignment).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1354
|
+
|
|
1355
|
+
if (fields != "expression,field,kind,location,receiver" || typeof assignment.field != "string" ||
|
|
1356
|
+
assignment.receiver?.kind != "ReceiverExpression") {
|
|
1357
|
+
unsupportedCapability(language, "malformed private field receiver or private field identity", location)
|
|
1358
|
+
}
|
|
1359
|
+
validateExpression(assignment.receiver, language, location, false, new Set(), externalDeclarationIds)
|
|
1360
|
+
validateExpression(assignment.expression, language, location, false, new Set(), externalDeclarationIds)
|
|
1361
|
+
return
|
|
1362
|
+
}
|
|
1363
|
+
if (kind == "ReturnStatement") {
|
|
1364
|
+
const expression = Reflect.get(statement, "expression")
|
|
1365
|
+
|
|
1366
|
+
if (expression !== undefined) validateExpression(expression, language, location, false, new Set(), externalDeclarationIds)
|
|
1367
|
+
if (language == "ruby" && loopContext.active.some(({kind}) => kind == "ForEachStatement" || kind == "ForEachMapStatement")) {
|
|
1368
|
+
unsupportedCapability(language, "return from an iteration body", location)
|
|
1369
|
+
}
|
|
176
1370
|
return
|
|
177
1371
|
}
|
|
178
|
-
if (kind == "
|
|
179
|
-
|
|
1372
|
+
if (kind == "ExpressionStatement") {
|
|
1373
|
+
if (!task005Languages.has(language)) {
|
|
1374
|
+
unsupportedCapability(language, "Task 005 void call expression statement", location)
|
|
1375
|
+
}
|
|
1376
|
+
const expression = Reflect.get(statement, "expression")
|
|
1377
|
+
|
|
1378
|
+
if (!expression || typeof expression != "object" ||
|
|
1379
|
+
!["CallExpression", "EffectCallExpression", "MethodCallExpression"].includes(String(Reflect.get(expression, "kind")))) {
|
|
1380
|
+
unsupportedCapability(language, "expression statement other than a direct or receiver call", location)
|
|
1381
|
+
}
|
|
1382
|
+
validateExpression(expression, language, location, false, new Set(), externalDeclarationIds)
|
|
1383
|
+
return
|
|
1384
|
+
}
|
|
1385
|
+
if (kind == "PrintStatement") {
|
|
1386
|
+
validateExpression(Reflect.get(statement, "expression"), language, location, false, new Set(), externalDeclarationIds)
|
|
180
1387
|
return
|
|
181
1388
|
}
|
|
182
1389
|
if (kind == "IfStatement") {
|
|
183
1390
|
const branch = /** @type {import("../semantic/types.js").IfStatement} */ (statement)
|
|
184
1391
|
|
|
185
|
-
validateExpression(branch.condition, language, location)
|
|
186
|
-
validateBlock(branch.consequent, language, location)
|
|
187
|
-
if (Object.hasOwn(branch, "alternate"))
|
|
1392
|
+
validateExpression(branch.condition, language, location, false, new Set(), externalDeclarationIds)
|
|
1393
|
+
validateBlock(branch.consequent, language, location, loopContext, activePath, externalDeclarationIds)
|
|
1394
|
+
if (Object.hasOwn(branch, "alternate")) {
|
|
1395
|
+
validateBlock(branch.alternate, language, location, loopContext, activePath, externalDeclarationIds)
|
|
1396
|
+
}
|
|
1397
|
+
return
|
|
1398
|
+
}
|
|
1399
|
+
if (kind == "BreakStatement" || kind == "ContinueStatement") {
|
|
1400
|
+
if (!task008Languages.has(language)) unsupportedCapability(language, "Task 008 ordered list iteration", location)
|
|
1401
|
+
requireSemanticLocation(ownLocation, language, `${kind} location`, ownerLocation)
|
|
1402
|
+
const fields = Object.keys(/** @type {object} */ (statement)).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1403
|
+
|
|
1404
|
+
if (fields != "kind,location,targetLoopId") unsupportedCapability(language, `malformed ${kind}`, location)
|
|
1405
|
+
const targetLoopId = Reflect.get(statement, "targetLoopId")
|
|
1406
|
+
const nearest = loopContext.active.at(-1)
|
|
1407
|
+
|
|
1408
|
+
if (!nearest) unsupportedCapability(language, `${kind == "BreakStatement" ? "break" : "continue"} outside a loop`, location)
|
|
1409
|
+
if (typeof targetLoopId != "string" || !loopContext.owners.has(targetLoopId)) {
|
|
1410
|
+
unsupportedCapability(language, "invalid loop control target identity", location)
|
|
1411
|
+
}
|
|
1412
|
+
if (loopContext.owners.get(targetLoopId) !== loopContext.owner) {
|
|
1413
|
+
unsupportedCapability(language, "loop control target crosses a function boundary", location)
|
|
1414
|
+
}
|
|
1415
|
+
if (targetLoopId != nearest.id) unsupportedCapability(language, "loop control must target the nearest enclosing loop", location)
|
|
1416
|
+
return
|
|
1417
|
+
}
|
|
1418
|
+
if (kind == "WhileStatement") {
|
|
1419
|
+
if (!task032Languages.has(language)) unsupportedCapability(language, "Task 032 condition-controlled loop", location)
|
|
1420
|
+
const loopLocation = requireSemanticLocation(ownLocation, language, "WhileStatement location", ownerLocation)
|
|
1421
|
+
const loop = /** @type {import("../semantic/types.js").WhileStatement} */ (statement)
|
|
1422
|
+
const fields = Object.keys(loop).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1423
|
+
|
|
1424
|
+
if (fields != "body,condition,id,kind,location") unsupportedCapability(language, "malformed WhileStatement", loopLocation)
|
|
1425
|
+
if (typeof loop.id != "string" || !/^loop:[0-9]+$/u.test(loop.id) || loopContext.owners.get(loop.id) !== loopContext.owner) {
|
|
1426
|
+
unsupportedCapability(language, "duplicate or invalid loop identity", loopLocation)
|
|
1427
|
+
}
|
|
1428
|
+
if (!loop.condition || typeof loop.condition != "object" || Array.isArray(loop.condition)) {
|
|
1429
|
+
unsupportedCapability(language, "missing or invalid condition-controlled loop condition", loopLocation)
|
|
1430
|
+
}
|
|
1431
|
+
requireSemanticLocation(Reflect.get(loop.condition, "location"), language, "condition-controlled loop condition location", loopLocation)
|
|
1432
|
+
validateExpression(loop.condition, language, loopLocation, false, new Set(), externalDeclarationIds)
|
|
1433
|
+
if (!loop.body || typeof loop.body != "object" || Array.isArray(loop.body)) {
|
|
1434
|
+
unsupportedCapability(language, "missing or invalid condition-controlled loop body", loopLocation)
|
|
1435
|
+
}
|
|
1436
|
+
requireSemanticLocation(Reflect.get(loop.body, "location"), language, "condition-controlled loop body location", loopLocation)
|
|
1437
|
+
validateBlock(loop.body, language, loopLocation,
|
|
1438
|
+
{active: [...loopContext.active, loop], owner: loopContext.owner, owners: loopContext.owners}, activePath,
|
|
1439
|
+
externalDeclarationIds)
|
|
1440
|
+
return
|
|
1441
|
+
}
|
|
1442
|
+
if (kind == "ForEachStatement") {
|
|
1443
|
+
if (!task008Languages.has(language)) unsupportedCapability(language, "Task 008 ordered list iteration", location)
|
|
1444
|
+
const loopLocation = requireSemanticLocation(ownLocation, language, "ForEachStatement location", ownerLocation)
|
|
1445
|
+
const loop = /** @type {import("../semantic/types.js").ForEachStatement} */ (statement)
|
|
1446
|
+
const fields = Object.keys(loop).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1447
|
+
|
|
1448
|
+
if (fields != "body,id,kind,list,location,valueBinding") unsupportedCapability(language, "malformed ForEachStatement", loopLocation)
|
|
1449
|
+
if (typeof loop.id != "string" || !/^loop:[0-9]+$/u.test(loop.id) || loopContext.owners.get(loop.id) !== loopContext.owner) {
|
|
1450
|
+
unsupportedCapability(language, "duplicate or invalid loop identity", loopLocation)
|
|
1451
|
+
}
|
|
1452
|
+
if (!loop.list || typeof loop.list != "object" || Array.isArray(loop.list)) {
|
|
1453
|
+
unsupportedCapability(language, "missing or invalid iteration collection", loopLocation)
|
|
1454
|
+
}
|
|
1455
|
+
requireSemanticLocation(Reflect.get(loop.list, "location"), language, "iteration collection location", loopLocation)
|
|
1456
|
+
validateExpression(loop.list, language, loopLocation, false, new Set(), externalDeclarationIds)
|
|
1457
|
+
if (!loop.valueBinding || typeof loop.valueBinding != "object" || Array.isArray(loop.valueBinding)) {
|
|
1458
|
+
unsupportedCapability(language, "missing or invalid iteration binding", loopLocation)
|
|
1459
|
+
}
|
|
1460
|
+
const binding = loop.valueBinding
|
|
1461
|
+
const bindingLocation = requireSemanticLocation(Reflect.get(binding, "location"), language, "iteration binding location", loopLocation)
|
|
1462
|
+
const bindingFields = Object.keys(binding).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1463
|
+
|
|
1464
|
+
if (binding.kind != "ValueBinding" || bindingFields != "kind,location,mutable,name,type") {
|
|
1465
|
+
unsupportedCapability(language, "malformed iteration binding", bindingLocation)
|
|
1466
|
+
}
|
|
1467
|
+
if (binding.mutable !== false) unsupportedCapability(language, "mutable iteration binding", bindingLocation)
|
|
1468
|
+
validateTargetBindingIdentifier(language, binding.name, "iteration binding", bindingLocation)
|
|
1469
|
+
if (!loop.body || typeof loop.body != "object" || Array.isArray(loop.body)) {
|
|
1470
|
+
unsupportedCapability(language, "missing or invalid iteration body", loopLocation)
|
|
1471
|
+
}
|
|
1472
|
+
requireSemanticLocation(Reflect.get(loop.body, "location"), language, "iteration body location", loopLocation)
|
|
1473
|
+
validateBlock(loop.body, language, loopLocation,
|
|
1474
|
+
{active: [...loopContext.active, loop], owner: loopContext.owner, owners: loopContext.owners}, activePath,
|
|
1475
|
+
externalDeclarationIds)
|
|
1476
|
+
return
|
|
1477
|
+
}
|
|
1478
|
+
if (kind == "ForEachMapStatement") {
|
|
1479
|
+
if (!task014Languages.has(language)) unsupportedCapability(language, "Task 014 ordered-map iteration", location)
|
|
1480
|
+
const loopLocation = requireSemanticLocation(ownLocation, language, "ForEachMapStatement location", ownerLocation)
|
|
1481
|
+
const loop = /** @type {import("../semantic/types.js").ForEachMapStatement} */ (statement)
|
|
1482
|
+
const fields = Object.keys(loop).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1483
|
+
|
|
1484
|
+
if (fields != "body,id,keyBinding,kind,location,map,valueBinding") {
|
|
1485
|
+
unsupportedCapability(language, "malformed ForEachMapStatement", loopLocation)
|
|
1486
|
+
}
|
|
1487
|
+
if (typeof loop.id != "string" || !/^loop:[0-9]+$/u.test(loop.id) || loopContext.owners.get(loop.id) !== loopContext.owner) {
|
|
1488
|
+
unsupportedCapability(language, "duplicate or invalid loop identity", loopLocation)
|
|
1489
|
+
}
|
|
1490
|
+
if (!loop.map || typeof loop.map != "object" || Array.isArray(loop.map)) {
|
|
1491
|
+
unsupportedCapability(language, "missing or invalid ordered-map iteration collection", loopLocation)
|
|
1492
|
+
}
|
|
1493
|
+
requireSemanticLocation(Reflect.get(loop.map, "location"), language, "ordered-map iteration collection location", loopLocation)
|
|
1494
|
+
validateExpression(loop.map, language, loopLocation, false, new Set(), externalDeclarationIds)
|
|
1495
|
+
for (const [role, binding] of [["key", loop.keyBinding], ["value", loop.valueBinding]]) {
|
|
1496
|
+
if (!binding || typeof binding != "object" || Array.isArray(binding)) {
|
|
1497
|
+
unsupportedCapability(language, `missing or invalid map iteration ${role} binding`, loopLocation)
|
|
1498
|
+
}
|
|
1499
|
+
const bindingLocation = requireSemanticLocation(binding.location, language, `map iteration ${role} binding location`, loopLocation)
|
|
1500
|
+
const bindingFields = Object.keys(binding).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1501
|
+
|
|
1502
|
+
if (binding.kind != "ValueBinding" || bindingFields != "kind,location,mutable,name,type") {
|
|
1503
|
+
unsupportedCapability(language, `malformed map iteration ${role} binding`, bindingLocation)
|
|
1504
|
+
}
|
|
1505
|
+
if (binding.mutable !== false) unsupportedCapability(language, "Map iteration bindings must be immutable", bindingLocation)
|
|
1506
|
+
validateTargetBindingIdentifier(language, binding.name, `map iteration ${role} binding`, bindingLocation)
|
|
1507
|
+
}
|
|
1508
|
+
if (!loop.body || typeof loop.body != "object" || Array.isArray(loop.body)) {
|
|
1509
|
+
unsupportedCapability(language, "missing or invalid map iteration body", loopLocation)
|
|
1510
|
+
}
|
|
1511
|
+
requireSemanticLocation(Reflect.get(loop.body, "location"), language, "map iteration body location", loopLocation)
|
|
1512
|
+
validateBlock(loop.body, language, loopLocation,
|
|
1513
|
+
{active: [...loopContext.active, loop], owner: loopContext.owner, owners: loopContext.owners}, activePath,
|
|
1514
|
+
externalDeclarationIds)
|
|
1515
|
+
return
|
|
1516
|
+
}
|
|
1517
|
+
if (kind == "RaiseStatement") {
|
|
1518
|
+
const fields = Object.keys(/** @type {object} */ (statement)).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1519
|
+
const raised = Reflect.get(statement, "error")
|
|
1520
|
+
const raiseLocation = requireSemanticLocation(ownLocation, language, "RaiseStatement location", ownerLocation)
|
|
1521
|
+
const constructionFields = raised && typeof raised == "object" && !Array.isArray(raised)
|
|
1522
|
+
? Object.keys(raised).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
1523
|
+
|
|
1524
|
+
if (fields != "error,kind,location" || !raised || typeof raised != "object" || Array.isArray(raised) ||
|
|
1525
|
+
raised.kind != "ErrorConstruction" || constructionFields != "error,kind,location,message") {
|
|
1526
|
+
unsupportedCapability(language, "malformed RaiseStatement", raiseLocation)
|
|
1527
|
+
}
|
|
1528
|
+
requireSemanticLocation(raised.location, language, "error construction location", raiseLocation)
|
|
1529
|
+
validateErrorTypeShape(raised.error, language, raised.location)
|
|
1530
|
+
validateExpression(raised.message, language, raised.location, false, new Set(), externalDeclarationIds)
|
|
1531
|
+
return
|
|
1532
|
+
}
|
|
1533
|
+
if (kind == "TryStatement") {
|
|
1534
|
+
const handler = /** @type {import("../semantic/types.js").TryStatement} */ (statement)
|
|
1535
|
+
const tryLocation = requireSemanticLocation(ownLocation, language, "TryStatement location", ownerLocation)
|
|
1536
|
+
const fields = Object.keys(handler).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1537
|
+
|
|
1538
|
+
if (fields != "body,catchBinding,catchBody,catchType,kind,location") {
|
|
1539
|
+
unsupportedCapability(language, "malformed TryStatement", tryLocation)
|
|
1540
|
+
}
|
|
1541
|
+
validateErrorTypeShape(handler.catchType, language, tryLocation)
|
|
1542
|
+
const binding = handler.catchBinding
|
|
1543
|
+
const bindingFields = binding && typeof binding == "object" && !Array.isArray(binding)
|
|
1544
|
+
? Object.keys(binding).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
1545
|
+
const bindingLocation = requireSemanticLocation(binding?.location, language, "catch binding location", tryLocation)
|
|
1546
|
+
|
|
1547
|
+
if (!binding || binding.kind != "CatchBinding" || bindingFields != "kind,location,mutable,name,type" || binding.mutable !== false) {
|
|
1548
|
+
unsupportedCapability(language, "malformed catch binding", bindingLocation)
|
|
1549
|
+
}
|
|
1550
|
+
validateTargetBindingIdentifier(language, binding.name, "catch binding", bindingLocation)
|
|
1551
|
+
validateErrorTypeShape(binding.type, language, bindingLocation)
|
|
1552
|
+
if (binding.type.declarationId != handler.catchType.declarationId) {
|
|
1553
|
+
unsupportedCapability(language, "catch binding type mismatch", bindingLocation)
|
|
1554
|
+
}
|
|
1555
|
+
validateBlock(handler.body, language, tryLocation, loopContext, activePath, externalDeclarationIds)
|
|
1556
|
+
validateBlock(handler.catchBody, language, tryLocation, loopContext, activePath, externalDeclarationIds)
|
|
188
1557
|
return
|
|
189
1558
|
}
|
|
190
1559
|
|
|
191
1560
|
unsupportedCapability(language, `statement ${kind}`, location)
|
|
192
1561
|
}
|
|
193
1562
|
|
|
1563
|
+
/**
|
|
1564
|
+
* Validates the closed parser-neutral error type shape.
|
|
1565
|
+
* @param {unknown} type - Candidate error type.
|
|
1566
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
1567
|
+
* @param {import("../semantic/types.js").SourceLocation} ownerLocation - Nearest owning location.
|
|
1568
|
+
* @returns {void}
|
|
1569
|
+
*/
|
|
1570
|
+
function validateErrorTypeShape(/** @type {unknown} */ type,
|
|
1571
|
+
/** @type {import("../semantic/types.js").BackendLanguage} */ language,
|
|
1572
|
+
/** @type {import("../semantic/types.js").SourceLocation} */ ownerLocation) {
|
|
1573
|
+
const fields = type && typeof type == "object" && !Array.isArray(type)
|
|
1574
|
+
? Object.keys(type).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
1575
|
+
const candidate = type && typeof type == "object" && !Array.isArray(type)
|
|
1576
|
+
? /** @type {Record<string, unknown>} */ (type) : {}
|
|
1577
|
+
|
|
1578
|
+
if (candidate.kind != "ErrorType" || fields != "declarationId,kind" || typeof candidate.declarationId != "string" ||
|
|
1579
|
+
!/^(?:(?:[a-z][a-z0-9._-]*#)?error:[0-9]+|(?:module:[0-9]+\/)?capability:[0-9]+\/failure:[0-9]+)$/u
|
|
1580
|
+
.test(candidate.declarationId)) {
|
|
1581
|
+
unsupportedCapability(language, "missing or invalid error type", ownerLocation)
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
/**
|
|
1586
|
+
* Returns a complete semantic location suitable for diagnostic construction.
|
|
1587
|
+
* @param {unknown} candidate - Preferred location.
|
|
1588
|
+
* @param {unknown} fallback - Nearest enclosing location.
|
|
1589
|
+
* @returns {import("../semantic/types.js").SourceLocation | undefined} Safe diagnostic location.
|
|
1590
|
+
*/
|
|
1591
|
+
function diagnosticLocation(candidate, fallback) {
|
|
1592
|
+
if (isSemanticLocation(candidate)) return candidate
|
|
1593
|
+
if (isSemanticLocation(fallback)) return fallback
|
|
1594
|
+
|
|
1595
|
+
return undefined
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
/**
|
|
1599
|
+
* Requires one complete parser-neutral location before any backend or diagnostic consumes it.
|
|
1600
|
+
* @param {unknown} candidate - Required location.
|
|
1601
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Backend identity.
|
|
1602
|
+
* @param {string} subject - Location owner for a deterministic diagnostic.
|
|
1603
|
+
* @param {unknown} fallback - Nearest enclosing location.
|
|
1604
|
+
* @returns {import("../semantic/types.js").SourceLocation} Validated location.
|
|
1605
|
+
*/
|
|
1606
|
+
function requireSemanticLocation(candidate, language, subject, fallback) {
|
|
1607
|
+
if (!isSemanticLocation(candidate)) unsupportedCapability(language, `missing or invalid ${subject}`, diagnosticLocation(fallback, undefined))
|
|
1608
|
+
|
|
1609
|
+
return candidate
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
/**
|
|
1613
|
+
* Checks a complete ordered UTF-16 source range.
|
|
1614
|
+
* @param {unknown} location - Candidate source range.
|
|
1615
|
+
* @returns {location is import("../semantic/types.js").SourceLocation} Whether the range is safe to consume.
|
|
1616
|
+
*/
|
|
1617
|
+
function isSemanticLocation(location) {
|
|
1618
|
+
if (!location || typeof location != "object" || Array.isArray(location)) return false
|
|
1619
|
+
const candidate = /** @type {{filename?: unknown, start?: unknown, end?: unknown}} */ (location)
|
|
1620
|
+
|
|
1621
|
+
if (typeof candidate.filename != "string" || candidate.filename.length == 0 ||
|
|
1622
|
+
!isSemanticPoint(candidate.start) || !isSemanticPoint(candidate.end)) return false
|
|
1623
|
+
|
|
1624
|
+
return candidate.end.offset >= candidate.start.offset && candidate.end.line >= candidate.start.line &&
|
|
1625
|
+
(candidate.end.line != candidate.start.line || candidate.end.column >= candidate.start.column)
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
/**
|
|
1629
|
+
* Checks one complete one-based UTF-16 source point.
|
|
1630
|
+
* @param {unknown} point - Candidate source point.
|
|
1631
|
+
* @returns {point is import("../semantic/types.js").SourcePoint} Whether all coordinates are valid.
|
|
1632
|
+
*/
|
|
1633
|
+
function isSemanticPoint(point) {
|
|
1634
|
+
if (!point || typeof point != "object" || Array.isArray(point)) return false
|
|
1635
|
+
const candidate = /** @type {{offset?: unknown, line?: unknown, column?: unknown}} */ (point)
|
|
1636
|
+
|
|
1637
|
+
return Number.isSafeInteger(candidate.offset) && Number(candidate.offset) >= 0 &&
|
|
1638
|
+
Number.isSafeInteger(candidate.line) && Number(candidate.line) >= 1 &&
|
|
1639
|
+
Number.isSafeInteger(candidate.column) && Number(candidate.column) >= 1
|
|
1640
|
+
}
|
|
1641
|
+
|
|
194
1642
|
/**
|
|
195
1643
|
* Validates the simple identifier target introduced by task 002.
|
|
196
1644
|
* @param {unknown} target - Candidate assignment target.
|
|
197
|
-
* @param {import("../semantic/types.js").
|
|
1645
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Backend language or binary target.
|
|
198
1646
|
* @param {import("../semantic/types.js").SourceLocation | undefined} ownerLocation - Assignment location.
|
|
199
1647
|
* @returns {void}
|
|
200
1648
|
*/
|
|
@@ -206,68 +1654,275 @@ function validateAssignmentTarget(target, language, ownerLocation) {
|
|
|
206
1654
|
const candidate = /** @type {import("../semantic/types.js").IdentifierExpression} */ (target)
|
|
207
1655
|
const location = candidate.location ?? ownerLocation
|
|
208
1656
|
|
|
209
|
-
if (candidate.kind != "IdentifierExpression") {
|
|
210
|
-
return unsupportedCapability(language, `assignment target ${String(Reflect.get(target, "kind"))}`, location)
|
|
1657
|
+
if (candidate.kind != "IdentifierExpression") {
|
|
1658
|
+
return unsupportedCapability(language, `assignment target ${String(Reflect.get(target, "kind"))}`, location)
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
validateTargetBindingIdentifier(language, candidate.name, "assignment target", location)
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
/**
|
|
1665
|
+
* Checks expression backend capabilities recursively.
|
|
1666
|
+
* @param {unknown} expression - Candidate semantic expression.
|
|
1667
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Backend language or binary target.
|
|
1668
|
+
* @param {import("../semantic/types.js").SourceLocation | undefined} ownerLocation - Nearest owning node location.
|
|
1669
|
+
* @param {boolean} [allowJavaNegatedMinimumOperand] - Whether Java may use 2147483648 only beneath integer negation.
|
|
1670
|
+
* @param {Set<object>} [activePath] - Ancestors on the current recursive validation path.
|
|
1671
|
+
* @param {Set<string>} [externalDeclarationIds] - Resolved declarations whose source spelling is target-independent.
|
|
1672
|
+
* @param {boolean} [allowJavaOrderedMapRoot] - Whether this root is a Java local initializer eligible for statement lowering.
|
|
1673
|
+
* @returns {void}
|
|
1674
|
+
*/
|
|
1675
|
+
function validateExpression(expression, language, ownerLocation, allowJavaNegatedMinimumOperand = false, activePath = new Set(), externalDeclarationIds = new Set(), allowJavaOrderedMapRoot = false) {
|
|
1676
|
+
if (!expression || typeof expression != "object" || Array.isArray(expression)) {
|
|
1677
|
+
return unsupportedCapability(language, "missing or invalid expression", ownerLocation)
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
const candidate = /** @type {import("../semantic/types.js").Expression} */ (expression)
|
|
1681
|
+
const location = candidate.location ?? ownerLocation
|
|
1682
|
+
|
|
1683
|
+
if (activePath.has(candidate)) unsupportedCapability(language, "cyclic expression", location)
|
|
1684
|
+
const expressionPath = new Set(activePath)
|
|
1685
|
+
|
|
1686
|
+
expressionPath.add(candidate)
|
|
1687
|
+
|
|
1688
|
+
if (typeof candidate.kind != "string") {
|
|
1689
|
+
return unsupportedCapability(language, "missing or invalid expression", location)
|
|
1690
|
+
}
|
|
1691
|
+
if (candidate.kind == "IdentifierExpression") {
|
|
1692
|
+
validateTargetIdentifier(language, candidate.name, "reference", location)
|
|
1693
|
+
return
|
|
1694
|
+
}
|
|
1695
|
+
if (candidate.kind == "ReceiverExpression") {
|
|
1696
|
+
const fields = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1697
|
+
|
|
1698
|
+
if (fields != "classId,kind,location" || typeof candidate.classId != "string" ||
|
|
1699
|
+
!/^(?:[a-z][a-z0-9._-]*#)?class:[0-9]+$/u.test(candidate.classId)) {
|
|
1700
|
+
unsupportedCapability(language, "malformed private field receiver", location)
|
|
1701
|
+
}
|
|
1702
|
+
return
|
|
1703
|
+
}
|
|
1704
|
+
if (candidate.kind == "ErrorMessageRead") {
|
|
1705
|
+
const fields = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1706
|
+
|
|
1707
|
+
if (fields != "kind,location,receiver" || candidate.receiver?.kind != "IdentifierExpression") {
|
|
1708
|
+
unsupportedCapability(language, "malformed ErrorMessageRead", location)
|
|
1709
|
+
}
|
|
1710
|
+
validateExpression(candidate.receiver, language, location, false, expressionPath, externalDeclarationIds)
|
|
1711
|
+
return
|
|
1712
|
+
}
|
|
1713
|
+
if (candidate.kind == "OwnedMoveExpression" || candidate.kind == "OwnedBorrowExpression") {
|
|
1714
|
+
const fields = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1715
|
+
const expected = candidate.kind == "OwnedMoveExpression" ? "expression,kind,location,type" : "expression,kind,location,mode,type"
|
|
1716
|
+
|
|
1717
|
+
if (fields != expected || candidate.expression.kind != "IdentifierExpression" && candidate.expression.kind != "ReceiverExpression" &&
|
|
1718
|
+
candidate.expression.kind != "PrivateFieldRead") unsupportedCapability(language, `malformed ${candidate.kind}`, location)
|
|
1719
|
+
validateExpression(candidate.expression, language, location, false, expressionPath, externalDeclarationIds)
|
|
1720
|
+
return
|
|
1721
|
+
}
|
|
1722
|
+
if (candidate.kind == "IntegerLiteral") {
|
|
1723
|
+
if (!Number.isSafeInteger(candidate.value)) {
|
|
1724
|
+
unsupportedCapability(language, "non-safe integer literal", location)
|
|
1725
|
+
}
|
|
1726
|
+
const validNegatedMinimumOperand = allowJavaNegatedMinimumOperand && candidate.value == 2147483648
|
|
1727
|
+
|
|
1728
|
+
if (language == "java" && !validNegatedMinimumOperand && (candidate.value < -2147483648 || candidate.value > 2147483647)) {
|
|
1729
|
+
unsupportedCapability(language, "integer literal outside signed 32-bit int range", location)
|
|
1730
|
+
}
|
|
1731
|
+
if ((language == "csharp" || language == "go" || language == "c" || language == "cpp" || language == "dart" || language == "kotlin" || language == "rust" || language == "swift") && candidate.value < 0 ||
|
|
1732
|
+
(language == "c" || language == "cpp" || language == "dart" || language == "kotlin" || language == "rust" || language == "swift") && Object.is(candidate.value, -0)) {
|
|
1733
|
+
unsupportedCapability(language, "negative integer literal without semantic negation", location)
|
|
1734
|
+
}
|
|
1735
|
+
return
|
|
1736
|
+
}
|
|
1737
|
+
if (candidate.kind == "BooleanLiteral" || candidate.kind == "StringLiteral") return
|
|
1738
|
+
if (["OptionalNone", "OptionalSome", "OptionalIsPresent", "OptionalUnwrap"].includes(candidate.kind)) {
|
|
1739
|
+
if (!task007Languages.has(language)) unsupportedCapability(language, "Task 007 optional values", location)
|
|
1740
|
+
const fields = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1741
|
+
const expectedFields = candidate.kind == "OptionalNone" ? "kind,location" :
|
|
1742
|
+
candidate.kind == "OptionalSome" ? "kind,location,value" : "kind,location,operand"
|
|
1743
|
+
|
|
1744
|
+
if (fields != expectedFields) unsupportedCapability(language, `malformed ${candidate.kind}`, location)
|
|
1745
|
+
if (candidate.kind == "OptionalNone") return
|
|
1746
|
+
if (candidate.kind == "OptionalSome") {
|
|
1747
|
+
validateExpression(candidate.value, language, location, false, expressionPath, externalDeclarationIds)
|
|
1748
|
+
return
|
|
1749
|
+
}
|
|
1750
|
+
const operation = /** @type {import("../semantic/types.js").OptionalIsPresent | import("../semantic/types.js").OptionalUnwrap} */ (candidate)
|
|
1751
|
+
|
|
1752
|
+
if (!operation.operand || operation.operand.kind != "IdentifierExpression") {
|
|
1753
|
+
unsupportedCapability(language, `${candidate.kind} without a simple identifier operand`, location)
|
|
1754
|
+
}
|
|
1755
|
+
validateExpression(operation.operand, language, location, false, expressionPath, externalDeclarationIds)
|
|
1756
|
+
return
|
|
1757
|
+
}
|
|
1758
|
+
if (["ListLiteral", "MapLiteral", "OrderedMapLiteral", "ListIndexExpression", "MapLookupExpression", "CollectionSizeExpression"].includes(candidate.kind)) {
|
|
1759
|
+
if (!task006Languages.has(language)) unsupportedCapability(language, "Task 006 immutable collections", location)
|
|
1760
|
+
if (candidate.kind == "OrderedMapLiteral" && !task014Languages.has(language)) {
|
|
1761
|
+
unsupportedCapability(language, "Task 014 ordered-map iteration", location)
|
|
1762
|
+
}
|
|
1763
|
+
if (candidate.kind == "OrderedMapLiteral" && language == "java" && !allowJavaOrderedMapRoot) {
|
|
1764
|
+
unsupportedCapability(language, "unsafe Java ordered-map expression lowering", location)
|
|
1765
|
+
}
|
|
1766
|
+
if (candidate.kind == "ListLiteral") {
|
|
1767
|
+
if (!isDenseArray(candidate.elements)) unsupportedCapability(language, "missing or sparse list elements", location)
|
|
1768
|
+
for (const element of candidate.elements) {
|
|
1769
|
+
validateExpression(element, language, location, false, expressionPath, externalDeclarationIds)
|
|
1770
|
+
}
|
|
1771
|
+
return
|
|
1772
|
+
}
|
|
1773
|
+
if (candidate.kind == "MapLiteral" || candidate.kind == "OrderedMapLiteral") {
|
|
1774
|
+
if (!isDenseArray(candidate.entries)) unsupportedCapability(language, "missing or sparse map entries", location)
|
|
1775
|
+
if (candidate.kind == "MapLiteral" && language == "java" && candidate.entries.length > 10) {
|
|
1776
|
+
unsupportedCapability(language, "java.util.Map.of supports at most ten entries", location)
|
|
1777
|
+
}
|
|
1778
|
+
for (const entry of candidate.entries) {
|
|
1779
|
+
if (!entry || entry.kind != "MapEntry" || !entry.key || entry.key.kind != "StringLiteral") {
|
|
1780
|
+
unsupportedCapability(language, "missing or invalid map entry", entry?.location ?? location)
|
|
1781
|
+
}
|
|
1782
|
+
validateExpression(entry.key, language, entry.location, false, expressionPath, externalDeclarationIds)
|
|
1783
|
+
validateExpression(entry.value, language, entry.location, false, expressionPath, externalDeclarationIds)
|
|
1784
|
+
}
|
|
1785
|
+
return
|
|
1786
|
+
}
|
|
1787
|
+
if (candidate.kind == "ListIndexExpression") {
|
|
1788
|
+
if (candidate.totality == "fail-on-absence" && language != "java") {
|
|
1789
|
+
unsupportedCapability(language, "list access depends on Java-specific bounds failure", location)
|
|
1790
|
+
}
|
|
1791
|
+
validateExpression(candidate.collection, language, location, false, expressionPath, externalDeclarationIds)
|
|
1792
|
+
validateExpression(candidate.index, language, location, false, expressionPath, externalDeclarationIds)
|
|
1793
|
+
return
|
|
1794
|
+
}
|
|
1795
|
+
if (candidate.kind == "MapLookupExpression") {
|
|
1796
|
+
if (candidate.totality == "fail-on-absence" && language != "ruby") {
|
|
1797
|
+
unsupportedCapability(language, "map lookup depends on Ruby fetch absence failure", location)
|
|
1798
|
+
}
|
|
1799
|
+
validateExpression(candidate.collection, language, location, false, expressionPath, externalDeclarationIds)
|
|
1800
|
+
validateExpression(candidate.key, language, location, false, expressionPath, externalDeclarationIds)
|
|
1801
|
+
return
|
|
1802
|
+
}
|
|
1803
|
+
if (candidate.kind == "CollectionSizeExpression") {
|
|
1804
|
+
if (candidate.collectionKind != "list" && candidate.collectionKind != "map") {
|
|
1805
|
+
unsupportedCapability(language, "collection size without a validated receiver kind", location)
|
|
1806
|
+
}
|
|
1807
|
+
validateExpression(candidate.collection, language, location, false, expressionPath, externalDeclarationIds)
|
|
1808
|
+
return
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
if (candidate.kind == "CallExpression") {
|
|
1812
|
+
const declarationId = candidate.resolution && typeof candidate.resolution == "object" && !Array.isArray(candidate.resolution)
|
|
1813
|
+
? Reflect.get(candidate.resolution, "declarationId")
|
|
1814
|
+
: undefined
|
|
1815
|
+
|
|
1816
|
+
if (typeof declarationId != "string" || !externalDeclarationIds.has(declarationId)) {
|
|
1817
|
+
validateTargetIdentifier(language, candidate.callee, "callee", location)
|
|
1818
|
+
}
|
|
1819
|
+
if (!candidate.resolution || typeof candidate.resolution != "object" || Array.isArray(candidate.resolution)) {
|
|
1820
|
+
unsupportedCapability(language, "missing or invalid resolved call signature", location)
|
|
1821
|
+
}
|
|
1822
|
+
if (!Array.isArray(candidate.arguments)) {
|
|
1823
|
+
unsupportedCapability(language, "missing or invalid call arguments", location)
|
|
1824
|
+
}
|
|
1825
|
+
if (!task005Languages.has(language) && candidate.arguments.length != 2) {
|
|
1826
|
+
unsupportedCapability(language, "call argument count other than two", location)
|
|
1827
|
+
}
|
|
1828
|
+
for (const argument of candidate.arguments) {
|
|
1829
|
+
validateExpression(argument, language, location, false, expressionPath, externalDeclarationIds)
|
|
1830
|
+
}
|
|
1831
|
+
return
|
|
211
1832
|
}
|
|
1833
|
+
if (candidate.kind == "EffectCallExpression") {
|
|
1834
|
+
const fields = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1835
|
+
const resolution = candidate.resolution
|
|
212
1836
|
|
|
213
|
-
|
|
214
|
-
|
|
1837
|
+
if (fields != "arguments,effectSiteId,kind,location,operation,resolution" || !isDenseArray(candidate.arguments) ||
|
|
1838
|
+
typeof candidate.effectSiteId != "string" || typeof candidate.operation != "string" || !resolution ||
|
|
1839
|
+
resolution.kind != "ResolvedEffectOperationSignature" || resolution.operationId != candidate.operation ||
|
|
1840
|
+
!isDenseArray(resolution.parameterTypes) || !isDenseArray(resolution.failureIds) || resolution.effects?.join(",") != "host") {
|
|
1841
|
+
unsupportedCapability(language, "malformed authorized effect call", location)
|
|
1842
|
+
}
|
|
1843
|
+
for (const argument of candidate.arguments) validateExpression(argument, language, location, false, expressionPath, externalDeclarationIds)
|
|
1844
|
+
return
|
|
1845
|
+
}
|
|
1846
|
+
if (candidate.kind == "ReferenceConstruction") {
|
|
1847
|
+
const fields = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1848
|
+
const resolution = candidate.resolution
|
|
1849
|
+
const effectfulFields = "arguments,effectSiteId,effects,failureIds,kind,location,reference,resolution"
|
|
1850
|
+
const pureFields = "arguments,kind,location,reference,resolution"
|
|
215
1851
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
1852
|
+
if (![pureFields, effectfulFields].includes(fields) ||
|
|
1853
|
+
candidate.reference?.kind != "ReferenceType" && candidate.reference?.kind != "OwnedReferenceType" ||
|
|
1854
|
+
typeof candidate.reference.declarationId != "string" || !isDenseArray(candidate.arguments) ||
|
|
1855
|
+
!resolution || resolution.kind != "ResolvedConstructorSignature" ||
|
|
1856
|
+
resolution.declarationId != `${candidate.reference.declarationId}:constructor` || !isDenseArray(resolution.parameterTypes) ||
|
|
1857
|
+
fields == effectfulFields &&
|
|
1858
|
+
(typeof candidate.effectSiteId != "string" || candidate.effects?.join(",") != "host" || !isDenseArray(candidate.failureIds))) {
|
|
1859
|
+
unsupportedCapability(language, "malformed reference construction or resolved constructor signature", location)
|
|
1860
|
+
}
|
|
1861
|
+
for (const argument of candidate.arguments) {
|
|
1862
|
+
validateExpression(argument, language, location, false, expressionPath, externalDeclarationIds)
|
|
1863
|
+
}
|
|
1864
|
+
return
|
|
227
1865
|
}
|
|
1866
|
+
if (candidate.kind == "MethodCallExpression") {
|
|
1867
|
+
const fields = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1868
|
+
const resolution = candidate.resolution
|
|
228
1869
|
|
|
229
|
-
|
|
230
|
-
|
|
1870
|
+
const effectfulFields = "arguments,effectSiteId,effects,failureIds,kind,location,method,receiver,resolution"
|
|
1871
|
+
const pureFields = "arguments,kind,location,method,receiver,resolution"
|
|
231
1872
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
1873
|
+
if (![pureFields, effectfulFields].includes(fields) || typeof candidate.method != "string" ||
|
|
1874
|
+
!isDenseArray(candidate.arguments) || !resolution || resolution.kind != "ResolvedMethodSignature" ||
|
|
1875
|
+
resolution.declarationId != candidate.method || !isDenseArray(resolution.parameterTypes) ||
|
|
1876
|
+
!Object.hasOwn(resolution, "returnType") || Object.hasOwn(resolution, "resourceFlow") &&
|
|
1877
|
+
(!resolution.resourceFlow || !["preserve", "terminal"].includes(String(resolution.resourceFlow.kind)) ||
|
|
1878
|
+
Object.hasOwn(resolution.resourceFlow, "terminalFailureId") && typeof resolution.resourceFlow.terminalFailureId != "string") ||
|
|
1879
|
+
fields == effectfulFields &&
|
|
1880
|
+
(typeof candidate.effectSiteId != "string" || candidate.effects?.join(",") != "host" || !isDenseArray(candidate.failureIds))) {
|
|
1881
|
+
unsupportedCapability(language, "malformed receiver call or resolved method signature", location)
|
|
1882
|
+
}
|
|
1883
|
+
validateExpression(candidate.receiver, language, location, false, expressionPath, externalDeclarationIds)
|
|
1884
|
+
for (const argument of candidate.arguments) {
|
|
1885
|
+
validateExpression(argument, language, location, false, expressionPath, externalDeclarationIds)
|
|
1886
|
+
}
|
|
237
1887
|
return
|
|
238
1888
|
}
|
|
239
|
-
if (candidate.kind == "
|
|
240
|
-
|
|
241
|
-
unsupportedCapability(language, "non-safe integer literal", location)
|
|
242
|
-
}
|
|
243
|
-
const validNegatedMinimumOperand = allowJavaNegatedMinimumOperand && candidate.value == 2147483648
|
|
1889
|
+
if (candidate.kind == "RecordConstruction") {
|
|
1890
|
+
const fields = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
244
1891
|
|
|
245
|
-
if (
|
|
246
|
-
|
|
1892
|
+
if (fields != "arguments,kind,location,record" || !candidate.record || candidate.record.kind != "RecordType" ||
|
|
1893
|
+
typeof candidate.record.declarationId != "string" || !isDenseArray(candidate.arguments)) {
|
|
1894
|
+
unsupportedCapability(language, "malformed RecordConstruction", location)
|
|
247
1895
|
}
|
|
248
|
-
|
|
249
|
-
(
|
|
250
|
-
unsupportedCapability(language, "negative integer literal without semantic negation", location)
|
|
1896
|
+
for (const argument of candidate.arguments) {
|
|
1897
|
+
validateExpression(argument, language, location, false, expressionPath, externalDeclarationIds)
|
|
251
1898
|
}
|
|
252
1899
|
return
|
|
253
1900
|
}
|
|
254
|
-
if (candidate.kind == "
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (
|
|
258
|
-
unsupportedCapability(language, "
|
|
1901
|
+
if (candidate.kind == "MemberRead") {
|
|
1902
|
+
const fields = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1903
|
+
|
|
1904
|
+
if (fields != "field,kind,location,receiver" || typeof candidate.field != "string") {
|
|
1905
|
+
unsupportedCapability(language, "malformed MemberRead", location)
|
|
259
1906
|
}
|
|
260
|
-
|
|
261
|
-
|
|
1907
|
+
validateExpression(candidate.receiver, language, location, false, expressionPath, externalDeclarationIds)
|
|
1908
|
+
return
|
|
1909
|
+
}
|
|
1910
|
+
if (candidate.kind == "PrivateFieldRead") {
|
|
1911
|
+
const fields = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1912
|
+
|
|
1913
|
+
if (fields != "field,kind,location,receiver" || typeof candidate.field != "string" ||
|
|
1914
|
+
candidate.receiver?.kind != "ReceiverExpression") {
|
|
1915
|
+
unsupportedCapability(language, "malformed private field receiver or private field identity", location)
|
|
262
1916
|
}
|
|
263
|
-
|
|
1917
|
+
validateExpression(candidate.receiver, language, location, false, expressionPath, externalDeclarationIds)
|
|
264
1918
|
return
|
|
265
1919
|
}
|
|
266
1920
|
if (candidate.kind == "UnaryExpression") {
|
|
267
1921
|
if (!Object.hasOwn(unaryOperationSyntax, String(candidate.operation))) {
|
|
268
1922
|
unsupportedCapability(language, `unary operation ${String(candidate.operation)}`, location)
|
|
269
1923
|
}
|
|
270
|
-
validateExpression(candidate.operand, language, location, candidate.operation == "IntegerNegate"
|
|
1924
|
+
validateExpression(candidate.operand, language, location, candidate.operation == "IntegerNegate", expressionPath,
|
|
1925
|
+
externalDeclarationIds)
|
|
271
1926
|
validateKnownTargetInteger(candidate, language, location)
|
|
272
1927
|
return
|
|
273
1928
|
}
|
|
@@ -275,8 +1930,8 @@ function validateExpression(expression, language, ownerLocation, allowJavaNegate
|
|
|
275
1930
|
if (!Object.hasOwn(binaryOperationSyntax, String(candidate.operation))) {
|
|
276
1931
|
unsupportedCapability(language, `binary operation ${String(candidate.operation)}`, location)
|
|
277
1932
|
}
|
|
278
|
-
validateExpression(candidate.left, language, location)
|
|
279
|
-
validateExpression(candidate.right, language, location)
|
|
1933
|
+
validateExpression(candidate.left, language, location, false, expressionPath, externalDeclarationIds)
|
|
1934
|
+
validateExpression(candidate.right, language, location, false, expressionPath, externalDeclarationIds)
|
|
280
1935
|
validateKnownTargetInteger(candidate, language, location)
|
|
281
1936
|
return
|
|
282
1937
|
}
|
|
@@ -287,7 +1942,7 @@ function validateExpression(expression, language, ownerLocation, allowJavaNegate
|
|
|
287
1942
|
/**
|
|
288
1943
|
* Rejects compile-time-known Java integer operation results outside primitive int.
|
|
289
1944
|
* @param {import("../semantic/types.js").UnaryExpression | import("../semantic/types.js").BinaryExpression} expression - Validated operation shape.
|
|
290
|
-
* @param {import("../semantic/types.js").
|
|
1945
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Backend language or binary target.
|
|
291
1946
|
* @param {import("../semantic/types.js").SourceLocation | undefined} location - Operation location.
|
|
292
1947
|
* @returns {void}
|
|
293
1948
|
*/
|
|
@@ -297,9 +1952,15 @@ function validateKnownTargetInteger(expression, language, location) {
|
|
|
297
1952
|
if (language == "java" && value !== undefined && (value < -2147483648n || value > 2147483647n)) {
|
|
298
1953
|
unsupportedCapability(language, "compile-time-known integer operation outside signed 32-bit int range", location)
|
|
299
1954
|
}
|
|
300
|
-
if ((language == "csharp" || language == "go" || language == "c" || language == "cpp") && value !== undefined &&
|
|
1955
|
+
if ((language == "csharp" || language == "go" || language == "c" || language == "cpp" || language == "rust" || language == "swift" || language == "wasm") && value !== undefined &&
|
|
301
1956
|
(value < -9223372036854775808n || value > 9223372036854775807n)) {
|
|
302
|
-
|
|
1957
|
+
const scalar = language == "wasm" ? "i64" : language == "swift" ? "Int64" : "long"
|
|
1958
|
+
|
|
1959
|
+
unsupportedCapability(language, `compile-time-known integer operation outside signed 64-bit ${scalar} range`, location)
|
|
1960
|
+
}
|
|
1961
|
+
if ((language == "dart" || language == "kotlin") && value !== undefined &&
|
|
1962
|
+
(value < -BigInt(Number.MAX_SAFE_INTEGER) || value > BigInt(Number.MAX_SAFE_INTEGER))) {
|
|
1963
|
+
unsupportedCapability(language, "compile-time-known integer operation outside the Semantifold safe-integer range", location)
|
|
303
1964
|
}
|
|
304
1965
|
}
|
|
305
1966
|
|
|
@@ -328,22 +1989,137 @@ function knownIntegerValue(expression) {
|
|
|
328
1989
|
return undefined
|
|
329
1990
|
}
|
|
330
1991
|
|
|
1992
|
+
/**
|
|
1993
|
+
* Emits one recursive semantic type while mapping every constituent identity.
|
|
1994
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
1995
|
+
* @param {import("../semantic/types.js").SemanticFunctionReturnType} type - Semantic type.
|
|
1996
|
+
* @param {string} path - Exact type occurrence path.
|
|
1997
|
+
* @param {import("../semantic/types.js").TextBackendLanguage} language - Target language.
|
|
1998
|
+
* @param {boolean} [javaBoxed] - Whether a Java scalar is a generic argument.
|
|
1999
|
+
* @returns {void}
|
|
2000
|
+
*/
|
|
2001
|
+
export function emitType(writer, type, path, language, javaBoxed = false) {
|
|
2002
|
+
if (type.kind == "TypeVariableReference") {
|
|
2003
|
+
const parameter = writer.typeParameterForId(type.parameterId)
|
|
2004
|
+
|
|
2005
|
+
writer.mapped(parameter.name, {mappingKind: "exact", name: parameter.name, node: type, path, role: "type"})
|
|
2006
|
+
return
|
|
2007
|
+
}
|
|
2008
|
+
if (type.kind == "RecordType") {
|
|
2009
|
+
const record = writer.recordForId(type.declarationId)
|
|
2010
|
+
|
|
2011
|
+
writer.mapped(writer.recordNameForId(type.declarationId), {
|
|
2012
|
+
mappingKind: "exact", name: record.name, node: type, path, role: "type"
|
|
2013
|
+
})
|
|
2014
|
+
if (type.arguments) {
|
|
2015
|
+
writer.mapped(language == "ruby" ? "[" : "<", {mappingKind: "anchor", node: type, path})
|
|
2016
|
+
type.arguments.forEach((argument, index) => {
|
|
2017
|
+
if (index > 0) writer.synthetic(language == "php" || language == "ruby" || language == "java" ? "," : ", ",
|
|
2018
|
+
"generic type argument separator", [type], [path])
|
|
2019
|
+
emitType(writer, argument, `${path}/arguments/${index}`, language, language == "java")
|
|
2020
|
+
})
|
|
2021
|
+
writer.mapped(language == "ruby" ? "]" : ">", {mappingKind: "anchor", node: type, path})
|
|
2022
|
+
}
|
|
2023
|
+
return
|
|
2024
|
+
}
|
|
2025
|
+
if (type.kind == "ReferenceType") {
|
|
2026
|
+
const declaration = writer.classForId(type.declarationId)
|
|
2027
|
+
|
|
2028
|
+
writer.mapped(writer.classNameForId(type.declarationId), {
|
|
2029
|
+
mappingKind: "exact", name: declaration.name, node: type, path, role: "type"
|
|
2030
|
+
})
|
|
2031
|
+
return
|
|
2032
|
+
}
|
|
2033
|
+
if (type.kind == "OwnedResourceType") {
|
|
2034
|
+
const resource = writer.resourceForId(type.resourceId)
|
|
2035
|
+
writer.mapped(writer.capabilityNominalName(resource.name), {mappingKind: "exact", name: resource.name, node: type, path, role: "type"})
|
|
2036
|
+
return
|
|
2037
|
+
}
|
|
2038
|
+
if (type.kind == "OwnedReferenceType") {
|
|
2039
|
+
const declaration = writer.classForId(type.declarationId)
|
|
2040
|
+
writer.mapped(writer.classNameForId(type.declarationId), {mappingKind: "exact", name: declaration.name, node: type, path, role: "type"})
|
|
2041
|
+
return
|
|
2042
|
+
}
|
|
2043
|
+
if (type.kind == "TypeReference") {
|
|
2044
|
+
const spelling = language == "java" && javaBoxed
|
|
2045
|
+
? type.name == "integer" ? "Integer" : type.name == "boolean" ? "Boolean" : type.name == "string" ? "String" : "void"
|
|
2046
|
+
: emitScalarType(language, type)
|
|
2047
|
+
|
|
2048
|
+
writer.mapped(spelling, {mappingKind: "exact", node: type, path, role: "type"})
|
|
2049
|
+
return
|
|
2050
|
+
}
|
|
2051
|
+
if (type.kind == "OptionalType") {
|
|
2052
|
+
const prefix = language == "java" ? "java.util.Optional<" : language == "php" ? "?" : ""
|
|
2053
|
+
|
|
2054
|
+
writer.mapped(prefix, {mappingKind: "exact", node: type, path, role: "type"})
|
|
2055
|
+
emitType(writer, type.valueType, `${path}/valueType`, language, language == "java")
|
|
2056
|
+
writer.mapped(language == "java" ? ">" : language == "ruby" ? "?" : language == "javascript" ? "|null" :
|
|
2057
|
+
language == "typescript" ? " | null" : "", {mappingKind: "anchor", node: type, path})
|
|
2058
|
+
return
|
|
2059
|
+
}
|
|
2060
|
+
const prefix = type.kind == "ListType"
|
|
2061
|
+
? language == "ruby" ? "Array[" : language == "php" ? "list<" :
|
|
2062
|
+
language == "java" ? "java.util.List<" : "ReadonlyArray<"
|
|
2063
|
+
: language == "ruby" ? "Hash[" : language == "php" ? "array<" :
|
|
2064
|
+
language == "java" ? type.kind == "OrderedMapType" ? "java.util.SequencedMap<" : "java.util.Map<" : "ReadonlyMap<"
|
|
2065
|
+
|
|
2066
|
+
writer.mapped(prefix, {mappingKind: "exact", node: type, path, role: "type"})
|
|
2067
|
+
if (type.kind == "ListType") {
|
|
2068
|
+
emitType(writer, type.elementType, `${path}/elementType`, language, language == "java")
|
|
2069
|
+
} else {
|
|
2070
|
+
emitType(writer, type.keyType, `${path}/keyType`, language, language == "java")
|
|
2071
|
+
writer.synthetic(language == "javascript" || language == "typescript" ? ", " : ",", "map type separator", [type], [path])
|
|
2072
|
+
emitType(writer, type.valueType, `${path}/valueType`, language, language == "java")
|
|
2073
|
+
}
|
|
2074
|
+
writer.mapped(language == "ruby" ? "]" : ">", {mappingKind: "anchor", node: type, path})
|
|
2075
|
+
}
|
|
2076
|
+
|
|
331
2077
|
/**
|
|
332
2078
|
* Emits a supported expression.
|
|
333
2079
|
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
334
2080
|
* @param {import("../semantic/types.js").Expression} expression - Semantic expression.
|
|
335
2081
|
* @param {string} path - Exact JSON Pointer for this expression occurrence.
|
|
336
|
-
* @param {import("../semantic/types.js").
|
|
2082
|
+
* @param {import("../semantic/types.js").TextBackendLanguage} language - Target language.
|
|
337
2083
|
* @param {(name: string) => string} emitIdentifier - Identifier formatter.
|
|
338
2084
|
* @returns {void}
|
|
339
2085
|
*/
|
|
340
2086
|
export function emitExpression(writer, expression, path, language, emitIdentifier) {
|
|
2087
|
+
const effectTemporary = writer.effectReplacementName(expression)
|
|
2088
|
+
|
|
2089
|
+
if (effectTemporary) {
|
|
2090
|
+
writer.synthetic(emitIdentifier(effectTemporary), "exactly-once effect temporary read", [expression], [path])
|
|
2091
|
+
return
|
|
2092
|
+
}
|
|
341
2093
|
if (expression.kind == "IdentifierExpression") {
|
|
342
2094
|
writer.mapped(emitIdentifier(expression.name), {mappingKind: "exact", node: expression, path, role: "name"})
|
|
343
2095
|
return
|
|
344
2096
|
}
|
|
2097
|
+
if (expression.kind == "ReceiverExpression") {
|
|
2098
|
+
writer.mapped(language == "php" ? "$this" : language == "ruby" ? "self" : "this",
|
|
2099
|
+
{mappingKind: "exact", node: expression, path, role: "receiver"})
|
|
2100
|
+
return
|
|
2101
|
+
}
|
|
2102
|
+
if (expression.kind == "OwnedMoveExpression" || expression.kind == "OwnedBorrowExpression") {
|
|
2103
|
+
if (expression.expression.kind == "IdentifierExpression") {
|
|
2104
|
+
writer.mapped(emitIdentifier(expression.expression.name), {mappingKind: "exact", name: expression.expression.name,
|
|
2105
|
+
node: expression, path, role: "name"})
|
|
2106
|
+
} else if (expression.expression.kind == "ReceiverExpression") {
|
|
2107
|
+
writer.mapped(language == "php" ? "$this" : language == "ruby" ? "self" : "this",
|
|
2108
|
+
{mappingKind: "exact", node: expression, path, role: "receiver"})
|
|
2109
|
+
} else {
|
|
2110
|
+
const field = writer.privateFieldForId(expression.expression.field)
|
|
2111
|
+
|
|
2112
|
+
if (language != "ruby") {
|
|
2113
|
+
emitExpression(writer, expression.expression.receiver, `${path}/expression/receiver`, language, emitIdentifier)
|
|
2114
|
+
}
|
|
2115
|
+
writer.mapped(language == "php" ? `->${field.name}` : language == "javascript" ? `.#${field.name}` :
|
|
2116
|
+
language == "ruby" ? `@${field.name}` : `.${field.name}`,
|
|
2117
|
+
{mappingKind: "exact", name: field.name, node: expression, path, role: "member"})
|
|
2118
|
+
}
|
|
2119
|
+
return
|
|
2120
|
+
}
|
|
345
2121
|
if (expression.kind == "IntegerLiteral") {
|
|
346
|
-
const spelling = language == "csharp" ? `${expression.value}L` : String(expression.value)
|
|
2122
|
+
const spelling = language == "csharp" || language == "kotlin" ? `${expression.value}L` : String(expression.value)
|
|
347
2123
|
|
|
348
2124
|
writer.mapped(spelling, {mappingKind: "exact", node: expression, path, role: "literal"})
|
|
349
2125
|
return
|
|
@@ -351,25 +2127,250 @@ export function emitExpression(writer, expression, path, language, emitIdentifie
|
|
|
351
2127
|
if (expression.kind == "BooleanLiteral") {
|
|
352
2128
|
const spelling = language == "python" ? expression.value ? "True" : "False" : expression.value ? "true" : "false"
|
|
353
2129
|
|
|
2130
|
+
if (language == "dart") {
|
|
2131
|
+
writer.synthetic("_semantifoldBoolean(", "Dart analyzed Boolean literal", [expression], [path])
|
|
2132
|
+
}
|
|
354
2133
|
writer.mapped(spelling, {mappingKind: "exact", node: expression, path, role: "literal"})
|
|
2134
|
+
if (language == "dart") writer.synthetic(")", "Dart analyzed Boolean literal", [expression], [path])
|
|
355
2135
|
return
|
|
356
2136
|
}
|
|
357
2137
|
if (expression.kind == "StringLiteral") {
|
|
358
2138
|
writer.mapped(emitStringLiteral(language, expression.value), {mappingKind: "exact", node: expression, path, role: "literal"})
|
|
359
2139
|
return
|
|
360
2140
|
}
|
|
2141
|
+
if (expression.kind == "OptionalNone") {
|
|
2142
|
+
writer.mapped(language == "java" ? "java.util.Optional.empty()" : language == "ruby" ? "nil" : "null", {
|
|
2143
|
+
mappingKind: "exact", node: expression, path, role: "absence"
|
|
2144
|
+
})
|
|
2145
|
+
return
|
|
2146
|
+
}
|
|
2147
|
+
if (expression.kind == "OptionalSome") {
|
|
2148
|
+
if (language == "java") {
|
|
2149
|
+
writer.mapped("java.util.Optional.of(", {mappingKind: "exact", node: expression, path, role: "some"})
|
|
2150
|
+
} else {
|
|
2151
|
+
writer.mapped("(", {mappingKind: "exact", node: expression, path, role: "some"})
|
|
2152
|
+
}
|
|
2153
|
+
emitExpression(writer, expression.value, `${path}/value`, language, emitIdentifier)
|
|
2154
|
+
writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
2155
|
+
return
|
|
2156
|
+
}
|
|
2157
|
+
if (expression.kind == "OptionalIsPresent") {
|
|
2158
|
+
if (language == "ruby") writer.mapped("!", {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
2159
|
+
else if (language != "java") writer.mapped("(", {mappingKind: "anchor", node: expression, path})
|
|
2160
|
+
emitExpression(writer, expression.operand, `${path}/operand`, language, emitIdentifier)
|
|
2161
|
+
writer.mapped(language == "java" ? ".isPresent()" : language == "ruby" ? ".nil?" : " !== null", {
|
|
2162
|
+
mappingKind: "exact", node: expression, path, role: "operator"
|
|
2163
|
+
})
|
|
2164
|
+
if (language != "ruby" && language != "java") writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
2165
|
+
return
|
|
2166
|
+
}
|
|
2167
|
+
if (expression.kind == "OptionalUnwrap") {
|
|
2168
|
+
if (language != "java") {
|
|
2169
|
+
writer.mapped("(", {mappingKind: "exact", node: expression, path, role: "unwrap"})
|
|
2170
|
+
}
|
|
2171
|
+
emitExpression(writer, expression.operand, `${path}/operand`, language, emitIdentifier)
|
|
2172
|
+
if (language == "java") {
|
|
2173
|
+
writer.mapped(".get()", {mappingKind: "exact", node: expression, path, role: "unwrap"})
|
|
2174
|
+
} else writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
2175
|
+
return
|
|
2176
|
+
}
|
|
2177
|
+
if (expression.kind == "ListLiteral") {
|
|
2178
|
+
writer.mapped(language == "java" ? "java.util.List.of(" : "[", {mappingKind: "anchor", node: expression, path})
|
|
2179
|
+
expression.elements.forEach((element, index) => {
|
|
2180
|
+
if (index) writer.synthetic(", ", "list element separator", [expression], [path])
|
|
2181
|
+
emitExpression(writer, element, `${path}/elements/${index}`, language, emitIdentifier)
|
|
2182
|
+
})
|
|
2183
|
+
writer.mapped(language == "java" ? ")" : "]", {mappingKind: "anchor", node: expression, path})
|
|
2184
|
+
return
|
|
2185
|
+
}
|
|
2186
|
+
if (expression.kind == "MapLiteral" || expression.kind == "OrderedMapLiteral") {
|
|
2187
|
+
const open = language == "java" ? "java.util.Map.of(" : language == "javascript" || language == "typescript" ? "new Map([" :
|
|
2188
|
+
language == "php" ? "[" : "{"
|
|
2189
|
+
const close = language == "java" ? ")" : language == "javascript" || language == "typescript" ? "])" :
|
|
2190
|
+
language == "php" ? "]" : "}"
|
|
2191
|
+
|
|
2192
|
+
writer.mapped(open, {mappingKind: "anchor", node: expression, path})
|
|
2193
|
+
expression.entries.forEach((entry, index) => {
|
|
2194
|
+
const entryPath = `${path}/entries/${index}`
|
|
2195
|
+
|
|
2196
|
+
if (index) writer.synthetic(", ", "map entry separator", [expression], [path])
|
|
2197
|
+
if (language == "javascript" || language == "typescript") writer.mapped("[", {mappingKind: "anchor", node: entry, path: entryPath})
|
|
2198
|
+
emitExpression(writer, entry.key, `${entryPath}/key`, language, emitIdentifier)
|
|
2199
|
+
writer.mapped(language == "ruby" ? " => " : language == "php" ? " => " : ", ", {
|
|
2200
|
+
mappingKind: "exact", node: entry, path: entryPath, role: "operator"
|
|
2201
|
+
})
|
|
2202
|
+
emitExpression(writer, entry.value, `${entryPath}/value`, language, emitIdentifier)
|
|
2203
|
+
if (language == "javascript" || language == "typescript") writer.mapped("]", {mappingKind: "anchor", node: entry, path: entryPath})
|
|
2204
|
+
})
|
|
2205
|
+
writer.mapped(close, {mappingKind: "anchor", node: expression, path})
|
|
2206
|
+
return
|
|
2207
|
+
}
|
|
2208
|
+
if (expression.kind == "ListIndexExpression") {
|
|
2209
|
+
emitExpression(writer, expression.collection, `${path}/collection`, language, emitIdentifier)
|
|
2210
|
+
writer.mapped(language == "java" ? ".get(" : "[", {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
2211
|
+
emitExpression(writer, expression.index, `${path}/index`, language, emitIdentifier)
|
|
2212
|
+
writer.mapped(language == "java" ? ")" : "]", {mappingKind: "anchor", node: expression, path})
|
|
2213
|
+
return
|
|
2214
|
+
}
|
|
2215
|
+
if (expression.kind == "MapLookupExpression") {
|
|
2216
|
+
emitExpression(writer, expression.collection, `${path}/collection`, language, emitIdentifier)
|
|
2217
|
+
const operator = language == "ruby" ? ".fetch(" : language == "php" ? "[" : ".get("
|
|
2218
|
+
|
|
2219
|
+
writer.mapped(operator, {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
2220
|
+
emitExpression(writer, expression.key, `${path}/key`, language, emitIdentifier)
|
|
2221
|
+
writer.mapped(language == "php" ? "]" : ")", {mappingKind: "anchor", node: expression, path})
|
|
2222
|
+
if (language == "typescript") {
|
|
2223
|
+
writer.synthetic("!", "statically proven map lookup", [expression], [path])
|
|
2224
|
+
}
|
|
2225
|
+
return
|
|
2226
|
+
}
|
|
2227
|
+
if (expression.kind == "CollectionSizeExpression") {
|
|
2228
|
+
if (language == "php") {
|
|
2229
|
+
writer.mapped("count(", {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
2230
|
+
emitExpression(writer, expression.collection, `${path}/collection`, language, emitIdentifier)
|
|
2231
|
+
writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
2232
|
+
return
|
|
2233
|
+
}
|
|
2234
|
+
emitExpression(writer, expression.collection, `${path}/collection`, language, emitIdentifier)
|
|
2235
|
+
const operator = language == "ruby" || language == "java" ? ".size" :
|
|
2236
|
+
expression.collectionKind == "list" ? ".length" : ".size"
|
|
2237
|
+
|
|
2238
|
+
writer.mapped(`${operator}${language == "java" ? "()" : ""}`, {
|
|
2239
|
+
mappingKind: "exact", node: expression, path, role: "operator"
|
|
2240
|
+
})
|
|
2241
|
+
return
|
|
2242
|
+
}
|
|
361
2243
|
if (expression.kind == "CallExpression") {
|
|
362
|
-
|
|
2244
|
+
const dartMultiline = language == "dart" && expression.arguments.length > 0 &&
|
|
2245
|
+
dartExpressionExceedsPageWidth(writer, expression)
|
|
2246
|
+
const dartIndent = dartMultiline ? currentLineIndent(writer) : ""
|
|
2247
|
+
|
|
2248
|
+
writer.mapped(writer.callNameFor(expression), {
|
|
2249
|
+
mappingKind: "exact", name: expression.callee, node: expression, path, role: "callee"
|
|
2250
|
+
})
|
|
2251
|
+
writer.mapped("(", {mappingKind: "anchor", node: expression, path})
|
|
2252
|
+
expression.arguments.forEach((argument, index) => {
|
|
2253
|
+
if (dartMultiline) writer.synthetic(`${index ? "" : "\n"}${dartIndent} `, "Dart argument indentation", [expression], [path])
|
|
2254
|
+
else if (index > 0) writer.synthetic(", ", "argument separator", [expression], [path])
|
|
2255
|
+
emitExpression(writer, argument, `${path}/arguments/${index}`, language, emitIdentifier)
|
|
2256
|
+
if (dartMultiline) writer.synthetic(",\n", "Dart formatter argument separator", [expression], [path])
|
|
2257
|
+
})
|
|
2258
|
+
if (dartMultiline) writer.synthetic(dartIndent, "Dart call closing indentation", [expression], [path])
|
|
2259
|
+
writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
2260
|
+
return
|
|
2261
|
+
}
|
|
2262
|
+
if (expression.kind == "EffectCallExpression") {
|
|
2263
|
+
const operation = writer.effectOperationForId(expression.operation)
|
|
2264
|
+
if (language == "java" && writer.referenceClassEmissionDepth > 0) {
|
|
2265
|
+
writer.synthetic("Main.", "protected Task 034 host support owner", [expression], [path])
|
|
2266
|
+
}
|
|
2267
|
+
writer.mapped(writer.stdlibProviderEntryFor(operation) ?? operation.name, {
|
|
2268
|
+
mappingKind: "exact", name: operation.name, node: expression, path, role: "callee"
|
|
2269
|
+
})
|
|
2270
|
+
writer.mapped("(", {mappingKind: "anchor", node: expression, path})
|
|
2271
|
+
expression.arguments.forEach((argument, index) => {
|
|
2272
|
+
if (index > 0) writer.synthetic(", ", "effect argument separator", [expression], [path])
|
|
2273
|
+
emitExpression(writer, argument, `${path}/arguments/${index}`, language, emitIdentifier)
|
|
2274
|
+
})
|
|
2275
|
+
writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
2276
|
+
return
|
|
2277
|
+
}
|
|
2278
|
+
if (expression.kind == "ReferenceConstruction") {
|
|
2279
|
+
const declaration = writer.classForId(expression.reference.declarationId)
|
|
2280
|
+
const name = writer.classNameForId(expression.reference.declarationId)
|
|
2281
|
+
const prefix = language == "ruby" ? `${name}.new` : `new ${name}`
|
|
2282
|
+
|
|
2283
|
+
writer.mapped(prefix, {mappingKind: "exact", name: declaration.name, node: expression, path, role: "class"})
|
|
2284
|
+
writer.mapped("(", {mappingKind: "anchor", node: expression, path})
|
|
2285
|
+
expression.arguments.forEach((argument, index) => {
|
|
2286
|
+
if (index > 0) writer.synthetic(", ", "constructor argument separator", [expression], [path])
|
|
2287
|
+
emitExpression(writer, argument, `${path}/arguments/${index}`, language, emitIdentifier)
|
|
2288
|
+
})
|
|
2289
|
+
writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
2290
|
+
return
|
|
2291
|
+
}
|
|
2292
|
+
if (expression.kind == "MethodCallExpression") {
|
|
2293
|
+
emitExpression(writer, expression.receiver, `${path}/receiver`, language, emitIdentifier)
|
|
2294
|
+
const method = writer.methodForId(expression.method)
|
|
2295
|
+
|
|
2296
|
+
writer.mapped(`${language == "php" ? "->" : "."}${method.name}`, {
|
|
2297
|
+
mappingKind: "exact", name: method.name, node: expression, path, role: "member"
|
|
2298
|
+
})
|
|
2299
|
+
writer.mapped("(", {mappingKind: "anchor", node: expression, path})
|
|
2300
|
+
expression.arguments.forEach((argument, index) => {
|
|
2301
|
+
if (index > 0) writer.synthetic(", ", "method argument separator", [expression], [path])
|
|
2302
|
+
emitExpression(writer, argument, `${path}/arguments/${index}`, language, emitIdentifier)
|
|
2303
|
+
})
|
|
2304
|
+
writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
2305
|
+
return
|
|
2306
|
+
}
|
|
2307
|
+
if (expression.kind == "RecordConstruction") {
|
|
2308
|
+
const record = writer.recordForId(expression.record.declarationId)
|
|
2309
|
+
const targetName = writer.recordNameForId(expression.record.declarationId, true)
|
|
2310
|
+
const prefix = language == "ruby" ? `${targetName}.new` : `new ${targetName}`
|
|
2311
|
+
|
|
2312
|
+
writer.mapped(prefix, {mappingKind: "exact", name: record.name, node: expression, path, role: "record"})
|
|
2313
|
+
if ((language == "typescript" || language == "java") && expression.record.arguments) {
|
|
2314
|
+
writer.mapped("<", {mappingKind: "anchor", node: expression.record, path: `${path}/record`})
|
|
2315
|
+
expression.record.arguments.forEach((argument, index) => {
|
|
2316
|
+
if (index > 0) writer.synthetic(language == "java" ? "," : ", ", "generic construction argument separator",
|
|
2317
|
+
[expression.record], [`${path}/record`])
|
|
2318
|
+
emitType(writer, argument, `${path}/record/arguments/${index}`, language, language == "java")
|
|
2319
|
+
})
|
|
2320
|
+
writer.mapped(">", {mappingKind: "anchor", node: expression.record, path: `${path}/record`})
|
|
2321
|
+
}
|
|
363
2322
|
writer.mapped("(", {mappingKind: "anchor", node: expression, path})
|
|
364
2323
|
expression.arguments.forEach((argument, index) => {
|
|
365
|
-
if (index > 0) writer.synthetic(", ", "argument separator", [expression], [path])
|
|
2324
|
+
if (index > 0) writer.synthetic(", ", "record argument separator", [expression], [path])
|
|
366
2325
|
emitExpression(writer, argument, `${path}/arguments/${index}`, language, emitIdentifier)
|
|
367
2326
|
})
|
|
368
2327
|
writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
369
2328
|
return
|
|
370
2329
|
}
|
|
2330
|
+
if (expression.kind == "MemberRead") {
|
|
2331
|
+
emitExpression(writer, expression.receiver, `${path}/receiver`, language, emitIdentifier)
|
|
2332
|
+
const field = writer.fieldForId(expression.field)
|
|
2333
|
+
const phpGenericRecord = language == "php" &&
|
|
2334
|
+
(writer.recordForFieldId(expression.field).typeParameters?.length ?? 0) > 0
|
|
2335
|
+
const spelling = language == "php" ? `->${field.name}${phpGenericRecord ? "()" : ""}` :
|
|
2336
|
+
language == "java" ? `.${field.name}()` : `.${field.name}`
|
|
2337
|
+
|
|
2338
|
+
writer.mapped(spelling, {mappingKind: "exact", node: expression, path, role: "member"})
|
|
2339
|
+
return
|
|
2340
|
+
}
|
|
2341
|
+
if (expression.kind == "PrivateFieldRead") {
|
|
2342
|
+
const field = writer.privateFieldForId(expression.field)
|
|
2343
|
+
|
|
2344
|
+
if (language == "ruby") {
|
|
2345
|
+
writer.mapped(`@${field.name}`, {mappingKind: "exact", name: field.name, node: expression, path, role: "member"})
|
|
2346
|
+
return
|
|
2347
|
+
}
|
|
2348
|
+
emitExpression(writer, expression.receiver, `${path}/receiver`, language, emitIdentifier)
|
|
2349
|
+
writer.mapped(language == "php" ? `->${field.name}` : language == "javascript" ? `.#${field.name}` : `.${field.name}`,
|
|
2350
|
+
{mappingKind: "exact", name: field.name, node: expression, path, role: "member"})
|
|
2351
|
+
return
|
|
2352
|
+
}
|
|
2353
|
+
if (expression.kind == "ErrorMessageRead") {
|
|
2354
|
+
emitExpression(writer, expression.receiver, `${path}/receiver`, language, emitIdentifier)
|
|
2355
|
+
writer.mapped(language == "php" ? "->getMessage()" : language == "java" ? ".getMessage()" : ".message", {
|
|
2356
|
+
mappingKind: "exact", node: expression, path, role: "member"
|
|
2357
|
+
})
|
|
2358
|
+
return
|
|
2359
|
+
}
|
|
371
2360
|
|
|
372
2361
|
if (expression.kind == "UnaryExpression") {
|
|
2362
|
+
if (language == "dart" && expression.operation == "IntegerNegate") {
|
|
2363
|
+
emitDartIntegerHelper(writer, expression, path, "_semantifoldIntegerNegate", [
|
|
2364
|
+
[expression.operand, `${path}/operand`]
|
|
2365
|
+
])
|
|
2366
|
+
return
|
|
2367
|
+
}
|
|
2368
|
+
if (language == "kotlin" && expression.operation == "IntegerNegate") {
|
|
2369
|
+
emitKotlinIntegerHelper(writer, expression, path, "semantifold_integer_negate", [
|
|
2370
|
+
[expression.operand, `${path}/operand`]
|
|
2371
|
+
])
|
|
2372
|
+
return
|
|
2373
|
+
}
|
|
373
2374
|
if (language == "csharp" && expression.operation == "IntegerNegate") {
|
|
374
2375
|
writer.mapped("checked(", {mappingKind: "anchor", node: expression, path})
|
|
375
2376
|
writer.mapped("-", {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
@@ -386,6 +2387,26 @@ export function emitExpression(writer, expression, path, language, emitIdentifie
|
|
|
386
2387
|
return
|
|
387
2388
|
}
|
|
388
2389
|
|
|
2390
|
+
if (language == "dart" && ["IntegerAdd", "IntegerSubtract", "IntegerMultiply"].includes(expression.operation)) {
|
|
2391
|
+
const helper = expression.operation == "IntegerAdd" ? "_semantifoldIntegerAdd" :
|
|
2392
|
+
expression.operation == "IntegerSubtract" ? "_semantifoldIntegerSubtract" : "_semantifoldIntegerMultiply"
|
|
2393
|
+
|
|
2394
|
+
emitDartIntegerHelper(writer, expression, path, helper, [
|
|
2395
|
+
[expression.left, `${path}/left`], [expression.right, `${path}/right`]
|
|
2396
|
+
])
|
|
2397
|
+
return
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
if (language == "kotlin" && ["IntegerAdd", "IntegerSubtract", "IntegerMultiply"].includes(expression.operation)) {
|
|
2401
|
+
const helper = expression.operation == "IntegerAdd" ? "semantifold_integer_add" :
|
|
2402
|
+
expression.operation == "IntegerSubtract" ? "semantifold_integer_subtract" : "semantifold_integer_multiply"
|
|
2403
|
+
|
|
2404
|
+
emitKotlinIntegerHelper(writer, expression, path, helper, [
|
|
2405
|
+
[expression.left, `${path}/left`], [expression.right, `${path}/right`]
|
|
2406
|
+
])
|
|
2407
|
+
return
|
|
2408
|
+
}
|
|
2409
|
+
|
|
389
2410
|
if (language == "csharp" && ["IntegerAdd", "IntegerSubtract", "IntegerMultiply"].includes(expression.operation)) {
|
|
390
2411
|
writer.mapped("checked(", {mappingKind: "anchor", node: expression, path})
|
|
391
2412
|
emitExpression(writer, expression.left, `${path}/left`, language, emitIdentifier)
|
|
@@ -419,6 +2440,21 @@ export function emitExpression(writer, expression, path, language, emitIdentifie
|
|
|
419
2440
|
return
|
|
420
2441
|
}
|
|
421
2442
|
|
|
2443
|
+
if (language == "swift" && (expression.operation == "StringEqual" || expression.operation == "StringNotEqual")) {
|
|
2444
|
+
writer.mapped(expression.operation == "StringEqual" ? "semantifold_string_equal" : "semantifold_string_not_equal", {
|
|
2445
|
+
mappingKind: "exact", node: expression, path, role: "operator"
|
|
2446
|
+
})
|
|
2447
|
+
writer.synthetic("(", "Swift scalar-equality helper call", [expression], [path])
|
|
2448
|
+
emitExpression(writer, expression.left, `${path}/left`, language, emitIdentifier)
|
|
2449
|
+
writer.synthetic(", ", "Swift scalar-equality argument separator", [expression], [path])
|
|
2450
|
+
emitExpression(writer, expression.right, `${path}/right`, language, emitIdentifier)
|
|
2451
|
+
writer.synthetic(")", "Swift scalar-equality helper call", [expression], [path])
|
|
2452
|
+
return
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
const dartMultiline = language == "dart" && dartExpressionExceedsPageWidth(writer, expression)
|
|
2456
|
+
const dartIndent = dartMultiline ? currentLineIndent(writer) : ""
|
|
2457
|
+
|
|
422
2458
|
writer.mapped("(", {mappingKind: "anchor", node: expression, path})
|
|
423
2459
|
emitExpression(writer, expression.left, `${path}/left`, language, emitIdentifier)
|
|
424
2460
|
writer.synthetic(" ", "operator spacing", [expression], [path])
|
|
@@ -428,11 +2464,119 @@ export function emitExpression(writer, expression, path, language, emitIdentifie
|
|
|
428
2464
|
writer.mapped(spelling.slice(0, 1), {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
429
2465
|
writer.mapped(spelling.slice(1), {mappingKind: "exact", node: expression, path, role: "equalityOperator"})
|
|
430
2466
|
} else writer.mapped(spelling, {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
431
|
-
writer.synthetic(" ", "operator spacing", [expression], [path])
|
|
2467
|
+
writer.synthetic(dartMultiline ? `\n${dartIndent} ` : " ", "operator spacing", [expression], [path])
|
|
432
2468
|
emitExpression(writer, expression.right, `${path}/right`, language, emitIdentifier)
|
|
433
2469
|
writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
434
2470
|
}
|
|
435
2471
|
|
|
2472
|
+
/**
|
|
2473
|
+
* Emits a Kotlin checked-integer helper while preserving the semantic operator range.
|
|
2474
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
2475
|
+
* @param {import("../semantic/types.js").UnaryExpression | import("../semantic/types.js").BinaryExpression} expression - Operation.
|
|
2476
|
+
* @param {string} path - Operation path.
|
|
2477
|
+
* @param {string} helper - Exact target helper.
|
|
2478
|
+
* @param {[import("../semantic/types.js").Expression, string][]} operands - Ordered operands and paths.
|
|
2479
|
+
*/
|
|
2480
|
+
function emitKotlinIntegerHelper(writer, expression, path, helper, operands) {
|
|
2481
|
+
writer.mapped(helper, {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
2482
|
+
writer.synthetic("(", "Kotlin checked-integer helper call", [expression], [path])
|
|
2483
|
+
operands.forEach(([operand, operandPath], index) => {
|
|
2484
|
+
if (index) writer.synthetic(", ", "Kotlin checked-integer argument separator", [expression], [path])
|
|
2485
|
+
emitExpression(writer, operand, operandPath, "kotlin", identityIdentifier)
|
|
2486
|
+
})
|
|
2487
|
+
writer.synthetic(")", "Kotlin checked-integer helper call", [expression], [path])
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
/**
|
|
2491
|
+
* Emits a compiler-owned Dart checked-integer helper call.
|
|
2492
|
+
* @param {import("./writer.js").SourceWriter} writer Source-aware writer.
|
|
2493
|
+
* @param {import("../semantic/types.js").UnaryExpression | import("../semantic/types.js").BinaryExpression} expression Operation.
|
|
2494
|
+
* @param {string} path Operation path.
|
|
2495
|
+
* @param {string} helper Exact target helper.
|
|
2496
|
+
* @param {[import("../semantic/types.js").Expression, string][]} operands Ordered operands and paths.
|
|
2497
|
+
* @returns {void}
|
|
2498
|
+
*/
|
|
2499
|
+
function emitDartIntegerHelper(writer, expression, path, helper, operands) {
|
|
2500
|
+
const flatLength = helper.length + 2 + operands.reduce((total, [operand], index) =>
|
|
2501
|
+
total + flatDartExpression(writer, operand).length + (index ? 2 : 0), 0)
|
|
2502
|
+
const multiline = writer.column - 1 + flatLength > 80
|
|
2503
|
+
const indent = multiline ? currentLineIndent(writer) : ""
|
|
2504
|
+
|
|
2505
|
+
writer.mapped(helper, {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
2506
|
+
writer.synthetic("(", "Dart checked-integer helper call", [expression], [path])
|
|
2507
|
+
operands.forEach(([operand, operandPath], index) => {
|
|
2508
|
+
if (multiline) writer.synthetic(`${index ? "" : "\n"}${indent} `, "Dart checked-integer argument indentation", [expression], [path])
|
|
2509
|
+
else if (index) writer.synthetic(", ", "Dart checked-integer argument separator", [expression], [path])
|
|
2510
|
+
emitExpression(writer, operand, operandPath, "dart", identityIdentifier)
|
|
2511
|
+
if (multiline) writer.synthetic(",\n", "Dart formatter checked-integer argument separator", [expression], [path])
|
|
2512
|
+
})
|
|
2513
|
+
if (multiline) writer.synthetic(indent, "Dart checked-integer closing indentation", [expression], [path])
|
|
2514
|
+
writer.synthetic(")", "Dart checked-integer helper call", [expression], [path])
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
/**
|
|
2518
|
+
* Reports whether Dart's default 80-column formatter must split an expression at the current site.
|
|
2519
|
+
* @param {import("./writer.js").SourceWriter} writer Source-aware writer.
|
|
2520
|
+
* @param {import("../semantic/types.js").Expression} expression Semantic expression.
|
|
2521
|
+
* @returns {boolean} Whether the flat expression crosses the formatter boundary.
|
|
2522
|
+
*/
|
|
2523
|
+
function dartExpressionExceedsPageWidth(writer, expression) {
|
|
2524
|
+
return writer.column - 1 + flatDartExpression(writer, expression).length > 80
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
/**
|
|
2528
|
+
* Returns the current generated line's structural indentation.
|
|
2529
|
+
* @param {import("./writer.js").SourceWriter} writer Source-aware writer.
|
|
2530
|
+
* @returns {string} Leading spaces.
|
|
2531
|
+
*/
|
|
2532
|
+
function currentLineIndent(writer) {
|
|
2533
|
+
return /^ */u.exec(writer.code.slice(writer.code.lastIndexOf("\n") + 1))?.[0] ?? ""
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
/**
|
|
2537
|
+
* Renders the bounded Task-005 Dart expression profile without mappings for page-width planning.
|
|
2538
|
+
* @param {import("./writer.js").SourceWriter} writer Source-aware writer.
|
|
2539
|
+
* @param {import("../semantic/types.js").Expression} expression Semantic expression.
|
|
2540
|
+
* @returns {string} Flat canonical spelling.
|
|
2541
|
+
*/
|
|
2542
|
+
function flatDartExpression(writer, expression) {
|
|
2543
|
+
if (expression.kind == "IdentifierExpression") return expression.name
|
|
2544
|
+
if (expression.kind == "IntegerLiteral") return String(expression.value)
|
|
2545
|
+
if (expression.kind == "BooleanLiteral") return `_semantifoldBoolean(${expression.value ? "true" : "false"})`
|
|
2546
|
+
if (expression.kind == "StringLiteral") return emitStringLiteral("dart", expression.value)
|
|
2547
|
+
if (expression.kind == "CallExpression") {
|
|
2548
|
+
return `${writer.callNameFor(expression)}(${expression.arguments.map((argument) =>
|
|
2549
|
+
flatDartExpression(writer, argument)).join(", ")})`
|
|
2550
|
+
}
|
|
2551
|
+
if (expression.kind == "UnaryExpression") {
|
|
2552
|
+
const operand = flatDartExpression(writer, expression.operand)
|
|
2553
|
+
|
|
2554
|
+
return expression.operation == "IntegerNegate" ? `_semantifoldIntegerNegate(${operand})` : `(!${operand})`
|
|
2555
|
+
}
|
|
2556
|
+
if (expression.kind == "BinaryExpression") {
|
|
2557
|
+
const left = flatDartExpression(writer, expression.left)
|
|
2558
|
+
const right = flatDartExpression(writer, expression.right)
|
|
2559
|
+
|
|
2560
|
+
if (["IntegerAdd", "IntegerSubtract", "IntegerMultiply"].includes(expression.operation)) {
|
|
2561
|
+
const helper = expression.operation == "IntegerAdd" ? "_semantifoldIntegerAdd" :
|
|
2562
|
+
expression.operation == "IntegerSubtract" ? "_semantifoldIntegerSubtract" : "_semantifoldIntegerMultiply"
|
|
2563
|
+
|
|
2564
|
+
return `${helper}(${left}, ${right})`
|
|
2565
|
+
}
|
|
2566
|
+
return `(${left} ${binaryOperationSpelling(expression.operation, "dart")} ${right})`
|
|
2567
|
+
}
|
|
2568
|
+
throw new TypeError("Unsupported Dart expression reached formatter planning after preflight.")
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
/**
|
|
2572
|
+
* Preserves a Kotlin identifier spelling.
|
|
2573
|
+
* @param {string} name - Identifier spelling.
|
|
2574
|
+
* @returns {string} Unchanged spelling.
|
|
2575
|
+
*/
|
|
2576
|
+
function identityIdentifier(name) {
|
|
2577
|
+
return name
|
|
2578
|
+
}
|
|
2579
|
+
|
|
436
2580
|
/**
|
|
437
2581
|
* Reports whether JavaScript-family execution can create IEEE-754 signed zero.
|
|
438
2582
|
* Semantifold integers have one mathematical zero, so those targets canonicalize
|
|
@@ -457,10 +2601,26 @@ function blockContainsSignProducingOperation(block) {
|
|
|
457
2601
|
blockContainsSignProducingOperation(statement.consequent) ||
|
|
458
2602
|
Boolean(statement.alternate && blockContainsSignProducingOperation(statement.alternate))
|
|
459
2603
|
}
|
|
2604
|
+
if (statement.kind == "ForEachStatement") {
|
|
2605
|
+
return expressionContainsSignProducingOperation(statement.list) || blockContainsSignProducingOperation(statement.body)
|
|
2606
|
+
}
|
|
2607
|
+
if (statement.kind == "ForEachMapStatement") {
|
|
2608
|
+
return expressionContainsSignProducingOperation(statement.map) || blockContainsSignProducingOperation(statement.body)
|
|
2609
|
+
}
|
|
2610
|
+
if (statement.kind == "WhileStatement") {
|
|
2611
|
+
return expressionContainsSignProducingOperation(statement.condition) || blockContainsSignProducingOperation(statement.body)
|
|
2612
|
+
}
|
|
2613
|
+
if (statement.kind == "TryStatement") {
|
|
2614
|
+
return blockContainsSignProducingOperation(statement.body) || blockContainsSignProducingOperation(statement.catchBody)
|
|
2615
|
+
}
|
|
2616
|
+
if (statement.kind == "RaiseStatement") return expressionContainsSignProducingOperation(statement.error.message)
|
|
460
2617
|
if (statement.kind == "LocalDeclaration") return expressionContainsSignProducingOperation(statement.initializer)
|
|
461
|
-
if (statement.kind == "AssignmentStatement" || statement.kind == "
|
|
2618
|
+
if (statement.kind == "AssignmentStatement" || statement.kind == "ExpressionStatement" || statement.kind == "PrintStatement") {
|
|
462
2619
|
return expressionContainsSignProducingOperation(statement.expression)
|
|
463
2620
|
}
|
|
2621
|
+
if (statement.kind == "ReturnStatement") {
|
|
2622
|
+
return Boolean(statement.expression && expressionContainsSignProducingOperation(statement.expression))
|
|
2623
|
+
}
|
|
464
2624
|
|
|
465
2625
|
return false
|
|
466
2626
|
})
|
|
@@ -482,6 +2642,26 @@ function expressionContainsSignProducingOperation(expression) {
|
|
|
482
2642
|
if (expression.kind == "CallExpression") {
|
|
483
2643
|
return expression.arguments.some((argument) => expressionContainsSignProducingOperation(argument))
|
|
484
2644
|
}
|
|
2645
|
+
if (expression.kind == "RecordConstruction") {
|
|
2646
|
+
return expression.arguments.some((argument) => expressionContainsSignProducingOperation(argument))
|
|
2647
|
+
}
|
|
2648
|
+
if (expression.kind == "MemberRead") return expressionContainsSignProducingOperation(expression.receiver)
|
|
2649
|
+
if (expression.kind == "ListLiteral") return expression.elements.some((element) => expressionContainsSignProducingOperation(element))
|
|
2650
|
+
if (expression.kind == "MapLiteral" || expression.kind == "OrderedMapLiteral") {
|
|
2651
|
+
return expression.entries.some((entry) => expressionContainsSignProducingOperation(entry.key) ||
|
|
2652
|
+
expressionContainsSignProducingOperation(entry.value))
|
|
2653
|
+
}
|
|
2654
|
+
if (expression.kind == "ListIndexExpression") {
|
|
2655
|
+
return expressionContainsSignProducingOperation(expression.collection) || expressionContainsSignProducingOperation(expression.index)
|
|
2656
|
+
}
|
|
2657
|
+
if (expression.kind == "MapLookupExpression") {
|
|
2658
|
+
return expressionContainsSignProducingOperation(expression.collection) || expressionContainsSignProducingOperation(expression.key)
|
|
2659
|
+
}
|
|
2660
|
+
if (expression.kind == "CollectionSizeExpression") return expressionContainsSignProducingOperation(expression.collection)
|
|
2661
|
+
if (expression.kind == "OptionalSome") return expressionContainsSignProducingOperation(expression.value)
|
|
2662
|
+
if (expression.kind == "OptionalIsPresent" || expression.kind == "OptionalUnwrap") {
|
|
2663
|
+
return expressionContainsSignProducingOperation(expression.operand)
|
|
2664
|
+
}
|
|
485
2665
|
|
|
486
2666
|
return false
|
|
487
2667
|
}
|