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.
Files changed (297) hide show
  1. package/README.md +58 -15
  2. package/build/index.d.ts +48 -2
  3. package/build/index.d.ts.map +1 -1
  4. package/build/index.js +57 -8
  5. package/build/src/artifact-path.d.ts +23 -1
  6. package/build/src/artifact-path.d.ts.map +1 -1
  7. package/build/src/artifact-path.js +45 -3
  8. package/build/src/artifacts.d.ts.map +1 -1
  9. package/build/src/artifacts.js +66 -1
  10. package/build/src/backends/c-validation.d.ts.map +1 -1
  11. package/build/src/backends/c-validation.js +7 -3
  12. package/build/src/backends/cpp-validation.d.ts.map +1 -1
  13. package/build/src/backends/cpp-validation.js +7 -3
  14. package/build/src/backends/csharp.js +7 -1
  15. package/build/src/backends/dart-runtime.d.ts +2 -0
  16. package/build/src/backends/dart-runtime.d.ts.map +1 -0
  17. package/build/src/backends/dart-runtime.js +41 -0
  18. package/build/src/backends/dart.d.ts +20 -0
  19. package/build/src/backends/dart.d.ts.map +1 -0
  20. package/build/src/backends/dart.js +321 -0
  21. package/build/src/backends/effects.d.ts +60 -0
  22. package/build/src/backends/effects.d.ts.map +1 -0
  23. package/build/src/backends/effects.js +746 -0
  24. package/build/src/backends/go.js +18 -4
  25. package/build/src/backends/identifiers.d.ts +36 -4
  26. package/build/src/backends/identifiers.d.ts.map +1 -1
  27. package/build/src/backends/identifiers.js +154 -10
  28. package/build/src/backends/index.d.ts.map +1 -1
  29. package/build/src/backends/index.js +27 -4
  30. package/build/src/backends/ios.d.ts +68 -0
  31. package/build/src/backends/ios.d.ts.map +1 -0
  32. package/build/src/backends/ios.js +1503 -0
  33. package/build/src/backends/java.d.ts.map +1 -1
  34. package/build/src/backends/java.js +429 -36
  35. package/build/src/backends/javascript.d.ts +6 -0
  36. package/build/src/backends/javascript.d.ts.map +1 -1
  37. package/build/src/backends/javascript.js +380 -24
  38. package/build/src/backends/kotlin-runtime.d.ts +3 -0
  39. package/build/src/backends/kotlin-runtime.d.ts.map +1 -0
  40. package/build/src/backends/kotlin-runtime.js +29 -0
  41. package/build/src/backends/kotlin.d.ts +23 -0
  42. package/build/src/backends/kotlin.d.ts.map +1 -0
  43. package/build/src/backends/kotlin.js +136 -0
  44. package/build/src/backends/native-validation.d.ts +2 -2
  45. package/build/src/backends/native-validation.d.ts.map +1 -1
  46. package/build/src/backends/native-validation.js +2 -2
  47. package/build/src/backends/ordered-expressions.d.ts.map +1 -1
  48. package/build/src/backends/ordered-expressions.js +39 -7
  49. package/build/src/backends/php-stdlib-support.d.ts +19 -0
  50. package/build/src/backends/php-stdlib-support.d.ts.map +1 -0
  51. package/build/src/backends/php-stdlib-support.js +227 -0
  52. package/build/src/backends/php.d.ts.map +1 -1
  53. package/build/src/backends/php.js +448 -33
  54. package/build/src/backends/program.d.ts +81 -0
  55. package/build/src/backends/program.d.ts.map +1 -0
  56. package/build/src/backends/program.js +1308 -0
  57. package/build/src/backends/python.js +7 -1
  58. package/build/src/backends/ruby.d.ts.map +1 -1
  59. package/build/src/backends/ruby.js +310 -24
  60. package/build/src/backends/rust-runtime.d.ts +7 -0
  61. package/build/src/backends/rust-runtime.d.ts.map +1 -0
  62. package/build/src/backends/rust-runtime.js +32 -0
  63. package/build/src/backends/rust-validation.d.ts +9 -0
  64. package/build/src/backends/rust-validation.d.ts.map +1 -0
  65. package/build/src/backends/rust-validation.js +78 -0
  66. package/build/src/backends/rust.d.ts +24 -0
  67. package/build/src/backends/rust.d.ts.map +1 -0
  68. package/build/src/backends/rust.js +344 -0
  69. package/build/src/backends/scalars.d.ts +14 -6
  70. package/build/src/backends/scalars.d.ts.map +1 -1
  71. package/build/src/backends/scalars.js +167 -17
  72. package/build/src/backends/shared.d.ts +23 -4
  73. package/build/src/backends/shared.d.ts.map +1 -1
  74. package/build/src/backends/shared.js +2158 -62
  75. package/build/src/backends/stdlib-support.d.ts +14 -0
  76. package/build/src/backends/stdlib-support.d.ts.map +1 -0
  77. package/build/src/backends/stdlib-support.js +237 -0
  78. package/build/src/backends/swift-runtime.d.ts +3 -0
  79. package/build/src/backends/swift-runtime.d.ts.map +1 -0
  80. package/build/src/backends/swift-runtime.js +14 -0
  81. package/build/src/backends/swift.d.ts +8 -0
  82. package/build/src/backends/swift.d.ts.map +1 -0
  83. package/build/src/backends/swift.js +127 -0
  84. package/build/src/backends/typescript.d.ts.map +1 -1
  85. package/build/src/backends/typescript.js +344 -24
  86. package/build/src/backends/wasm.d.ts +43 -0
  87. package/build/src/backends/wasm.d.ts.map +1 -0
  88. package/build/src/backends/wasm.js +2164 -0
  89. package/build/src/backends/writer.d.ts +235 -4
  90. package/build/src/backends/writer.d.ts.map +1 -1
  91. package/build/src/backends/writer.js +468 -3
  92. package/build/src/diagnostic.d.ts +4 -3
  93. package/build/src/diagnostic.d.ts.map +1 -1
  94. package/build/src/diagnostic.js +5 -3
  95. package/build/src/frontends/c.d.ts.map +1 -1
  96. package/build/src/frontends/c.js +5 -3
  97. package/build/src/frontends/cpp.d.ts.map +1 -1
  98. package/build/src/frontends/cpp.js +5 -3
  99. package/build/src/frontends/csharp.d.ts.map +1 -1
  100. package/build/src/frontends/csharp.js +7 -3
  101. package/build/src/frontends/dart.d.ts +10 -0
  102. package/build/src/frontends/dart.d.ts.map +1 -0
  103. package/build/src/frontends/dart.js +913 -0
  104. package/build/src/frontends/index.d.ts +3 -1
  105. package/build/src/frontends/index.d.ts.map +1 -1
  106. package/build/src/frontends/index.js +15 -5
  107. package/build/src/frontends/java.d.ts +110 -1
  108. package/build/src/frontends/java.d.ts.map +1 -1
  109. package/build/src/frontends/java.js +1725 -70
  110. package/build/src/frontends/javascript-typescript.d.ts +69 -1
  111. package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
  112. package/build/src/frontends/javascript-typescript.js +1825 -93
  113. package/build/src/frontends/kotlin.d.ts +10 -0
  114. package/build/src/frontends/kotlin.d.ts.map +1 -0
  115. package/build/src/frontends/kotlin.js +685 -0
  116. package/build/src/frontends/php.d.ts +118 -1
  117. package/build/src/frontends/php.d.ts.map +1 -1
  118. package/build/src/frontends/php.js +1583 -76
  119. package/build/src/frontends/program.d.ts +74 -0
  120. package/build/src/frontends/program.d.ts.map +1 -0
  121. package/build/src/frontends/program.js +989 -0
  122. package/build/src/frontends/ruby.d.ts +68 -1
  123. package/build/src/frontends/ruby.d.ts.map +1 -1
  124. package/build/src/frontends/ruby.js +1446 -101
  125. package/build/src/frontends/rust-parser.d.ts +11 -0
  126. package/build/src/frontends/rust-parser.d.ts.map +1 -0
  127. package/build/src/frontends/rust-parser.js +15 -0
  128. package/build/src/frontends/rust.d.ts +14 -0
  129. package/build/src/frontends/rust.d.ts.map +1 -0
  130. package/build/src/frontends/rust.js +715 -0
  131. package/build/src/frontends/scalars.d.ts +10 -0
  132. package/build/src/frontends/scalars.d.ts.map +1 -1
  133. package/build/src/frontends/scalars.js +28 -2
  134. package/build/src/frontends/swift.d.ts +10 -0
  135. package/build/src/frontends/swift.d.ts.map +1 -0
  136. package/build/src/frontends/swift.js +860 -0
  137. package/build/src/frontends/types.d.ts +186 -0
  138. package/build/src/frontends/types.d.ts.map +1 -0
  139. package/build/src/frontends/types.js +634 -0
  140. package/build/src/language-registry.d.ts +46 -4
  141. package/build/src/language-registry.d.ts.map +1 -1
  142. package/build/src/language-registry.js +167 -13
  143. package/build/src/mapping.d.ts +2 -2
  144. package/build/src/mapping.d.ts.map +1 -1
  145. package/build/src/mapping.js +31 -10
  146. package/build/src/semantic/capabilities.d.ts +66 -0
  147. package/build/src/semantic/capabilities.d.ts.map +1 -0
  148. package/build/src/semantic/capabilities.js +649 -0
  149. package/build/src/semantic/effects.d.ts +26 -0
  150. package/build/src/semantic/effects.d.ts.map +1 -0
  151. package/build/src/semantic/effects.js +764 -0
  152. package/build/src/semantic/generics.d.ts +23 -0
  153. package/build/src/semantic/generics.d.ts.map +1 -0
  154. package/build/src/semantic/generics.js +61 -0
  155. package/build/src/semantic/lifetime.d.ts +37 -0
  156. package/build/src/semantic/lifetime.d.ts.map +1 -0
  157. package/build/src/semantic/lifetime.js +527 -0
  158. package/build/src/semantic/provenance.d.ts +9 -2
  159. package/build/src/semantic/provenance.d.ts.map +1 -1
  160. package/build/src/semantic/provenance.js +462 -20
  161. package/build/src/semantic/scalars.d.ts +11 -0
  162. package/build/src/semantic/scalars.d.ts.map +1 -1
  163. package/build/src/semantic/scalars.js +16 -1
  164. package/build/src/semantic/stdlib.d.ts +254 -0
  165. package/build/src/semantic/stdlib.d.ts.map +1 -0
  166. package/build/src/semantic/stdlib.js +880 -0
  167. package/build/src/semantic/types.d.ts +2952 -202
  168. package/build/src/semantic/types.d.ts.map +1 -1
  169. package/build/src/semantic/types.js +688 -15
  170. package/build/src/semantic/validate.d.ts +85 -5
  171. package/build/src/semantic/validate.d.ts.map +1 -1
  172. package/build/src/semantic/validate.js +2576 -98
  173. package/build/src/stdlib-facades.d.ts +304 -0
  174. package/build/src/stdlib-facades.d.ts.map +1 -0
  175. package/build/src/stdlib-facades.js +1167 -0
  176. package/build/src/stdlib-negotiation.d.ts +69 -0
  177. package/build/src/stdlib-negotiation.d.ts.map +1 -0
  178. package/build/src/stdlib-negotiation.js +403 -0
  179. package/build/src/stdlib-providers.d.ts +72 -0
  180. package/build/src/stdlib-providers.d.ts.map +1 -0
  181. package/build/src/stdlib-providers.js +339 -0
  182. package/build/src/toolchains.d.ts +55 -1
  183. package/build/src/toolchains.d.ts.map +1 -1
  184. package/build/src/toolchains.js +15 -4
  185. package/docs/architecture.md +45 -13
  186. package/docs/dart.md +60 -0
  187. package/docs/goals.md +3 -3
  188. package/docs/ios.md +121 -0
  189. package/docs/kotlin.md +42 -0
  190. package/docs/language-support.md +231 -26
  191. package/docs/multi-file-modules.md +52 -0
  192. package/docs/parser-qualification.md +121 -8
  193. package/docs/plans/2026-09-05-standard-library-portability.md +10 -10
  194. package/docs/rust.md +88 -0
  195. package/docs/source-maps.md +15 -7
  196. package/docs/standard-library-portability.md +39 -8
  197. package/docs/swift.md +44 -0
  198. package/docs/testing.md +366 -4
  199. package/docs/webassembly.md +52 -0
  200. package/index.js +63 -8
  201. package/node_modules/semantifold-tree-sitter-legacy-internal/README.md +2 -2
  202. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/LICENSE +21 -0
  203. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/README.md +45 -0
  204. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/binding.gyp +30 -0
  205. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding.cc +20 -0
  206. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding_test.js +9 -0
  207. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.d.ts +28 -0
  208. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.js +11 -0
  209. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/grammar.js +1630 -0
  210. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/package.json +64 -0
  211. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-arm64/tree-sitter-rust.node +0 -0
  212. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-x64/tree-sitter-rust.node +0 -0
  213. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-arm64/tree-sitter-rust.node +0 -0
  214. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-x64/tree-sitter-rust.node +0 -0
  215. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-arm64/tree-sitter-rust.node +0 -0
  216. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-x64/tree-sitter-rust.node +0 -0
  217. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/highlights.scm +159 -0
  218. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/injections.scm +9 -0
  219. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/tags.scm +60 -0
  220. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/grammar.json +9405 -0
  221. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/node-types.json +5399 -0
  222. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/parser.c +192569 -0
  223. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/scanner.c +393 -0
  224. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/alloc.h +54 -0
  225. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/array.h +290 -0
  226. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/parser.h +266 -0
  227. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter-rust.wasm +0 -0
  228. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter.json +53 -0
  229. package/node_modules/semantifold-tree-sitter-legacy-internal/package.json +3 -2
  230. package/node_modules/semantifold-tree-sitter-legacy-internal/src/c.js +5 -4
  231. package/package.json +5 -2
  232. package/src/artifact-path.js +47 -2
  233. package/src/artifacts.js +67 -0
  234. package/src/backends/c-validation.js +6 -1
  235. package/src/backends/cpp-validation.js +6 -1
  236. package/src/backends/csharp.js +3 -0
  237. package/src/backends/dart-runtime.js +41 -0
  238. package/src/backends/dart.js +326 -0
  239. package/src/backends/effects.js +705 -0
  240. package/src/backends/go.js +14 -2
  241. package/src/backends/identifiers.js +160 -9
  242. package/src/backends/index.js +27 -2
  243. package/src/backends/ios.js +1612 -0
  244. package/src/backends/java.js +454 -36
  245. package/src/backends/javascript.js +389 -23
  246. package/src/backends/kotlin-runtime.js +29 -0
  247. package/src/backends/kotlin.js +141 -0
  248. package/src/backends/native-validation.js +1 -1
  249. package/src/backends/ordered-expressions.js +40 -6
  250. package/src/backends/php-stdlib-support.js +239 -0
  251. package/src/backends/php.js +469 -32
  252. package/src/backends/program.js +1407 -0
  253. package/src/backends/python.js +3 -0
  254. package/src/backends/ruby.js +326 -23
  255. package/src/backends/rust-runtime.js +34 -0
  256. package/src/backends/rust-validation.js +75 -0
  257. package/src/backends/rust.js +335 -0
  258. package/src/backends/scalars.js +141 -16
  259. package/src/backends/shared.js +2304 -124
  260. package/src/backends/stdlib-support.js +234 -0
  261. package/src/backends/swift-runtime.js +14 -0
  262. package/src/backends/swift.js +132 -0
  263. package/src/backends/typescript.js +349 -23
  264. package/src/backends/wasm.js +2208 -0
  265. package/src/backends/writer.js +520 -2
  266. package/src/diagnostic.js +4 -2
  267. package/src/frontends/c.js +4 -2
  268. package/src/frontends/cpp.js +4 -2
  269. package/src/frontends/csharp.js +4 -2
  270. package/src/frontends/dart.js +963 -0
  271. package/src/frontends/index.js +14 -4
  272. package/src/frontends/java.js +1977 -69
  273. package/src/frontends/javascript-typescript.js +2046 -102
  274. package/src/frontends/kotlin.js +713 -0
  275. package/src/frontends/php.js +1725 -79
  276. package/src/frontends/program.js +1062 -0
  277. package/src/frontends/ruby.js +1624 -98
  278. package/src/frontends/rust-parser.js +18 -0
  279. package/src/frontends/rust.js +714 -0
  280. package/src/frontends/scalars.js +31 -1
  281. package/src/frontends/swift.js +893 -0
  282. package/src/frontends/types.js +750 -0
  283. package/src/language-registry.js +172 -12
  284. package/src/mapping.js +33 -9
  285. package/src/semantic/capabilities.js +671 -0
  286. package/src/semantic/effects.js +737 -0
  287. package/src/semantic/generics.js +58 -0
  288. package/src/semantic/lifetime.js +517 -0
  289. package/src/semantic/provenance.js +412 -19
  290. package/src/semantic/scalars.js +17 -0
  291. package/src/semantic/stdlib.js +921 -0
  292. package/src/semantic/types.js +765 -14
  293. package/src/semantic/validate.js +2934 -209
  294. package/src/stdlib-facades.js +1211 -0
  295. package/src/stdlib-negotiation.js +446 -0
  296. package/src/stdlib-providers.js +377 -0
  297. package/src/toolchains.js +16 -3
@@ -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").SemanticLanguage} language - Backend language.
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
- if (module.functions.length == 0) unsupportedCapability(language, "module without functions", module.location)
44
- validateBlock(module.entryPoint.body, language, module.entryPoint.location)
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
- if (functionDeclaration.parameters.length != 2) {
54
- unsupportedCapability(language, "function parameter count other than two", functionDeclaration.location)
55
- }
56
- validateBlock(functionDeclaration.body, language, functionDeclaration.location)
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
- for (const parameter of functionDeclaration.parameters) {
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
- * Rejects semantic names that would capture syntax owned by one backend emitter.
71
- * @param {import("../semantic/types.js").SemanticModule} module - Semantic module.
72
- * @param {import("../semantic/types.js").SemanticLanguage} language - Backend language.
73
- * @returns {void}
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 validateScaffoldingNames(module, language) {
76
- const ownedEntryNames = language == "go" ? new Set(["fmt", "int64", "bool", "string", "true", "false"]) :
77
- language == "csharp" ? new Set(["System"]) : language == "java" ? new Set(["args", "System"]) :
78
- ["javascript", "typescript"].includes(language) ? new Set(["console"]) : new Set()
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
- for (const statement of allStatements(module.entryPoint.body)) {
90
- if (statement.kind == "LocalDeclaration" && ownedEntryNames.has(statement.name)) {
91
- unsupportedCapability(language, `entry local '${statement.name}' captures backend scaffolding`, statement.location)
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
- for (const declaration of module.functions) {
95
- if (ownedCallableNames.has(declaration.name)) {
96
- unsupportedCapability(language, `function '${declaration.name}' captures backend scaffolding`, declaration.location)
97
- }
98
- for (const parameter of declaration.parameters) {
99
- if (ownedPrintReceiverNames.has(parameter.name)) {
100
- unsupportedCapability(language, `function parameter '${parameter.name}' captures backend scaffolding`, parameter.location)
101
- }
102
- }
103
- for (const statement of allStatements(declaration.body)) {
104
- if (statement.kind == "LocalDeclaration" && ownedPrintReceiverNames.has(statement.name)) {
105
- unsupportedCapability(language, `function local '${statement.name}' captures backend scaffolding`, statement.location)
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) => statement.kind == "IfStatement"
118
- ? [statement, ...allStatements(statement.consequent), ...(statement.alternate ? allStatements(statement.alternate) : [])]
119
- : [statement])
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").SemanticLanguage} language - Backend language.
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 ?? ownerLocation
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
- for (const statement of candidate.statements) validateStatement(statement, language, location)
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").SemanticLanguage} language - Backend language.
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 location = /** @type {import("../semantic/types.js").SourceLocation | undefined} */ (
157
- Reflect.get(statement, "location") ?? ownerLocation
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
- validateExpression(declaration.initializer, language, location)
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 == "ReturnStatement" || kind == "PrintStatement") {
179
- validateExpression(Reflect.get(statement, "expression"), language, location)
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")) validateBlock(branch.alternate, language, location)
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").SemanticLanguage} language - Backend language.
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
- validateTargetBindingIdentifier(language, candidate.name, "assignment target", location)
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
- * Checks expression backend capabilities recursively.
218
- * @param {unknown} expression - Candidate semantic expression.
219
- * @param {import("../semantic/types.js").SemanticLanguage} language - Backend language.
220
- * @param {import("../semantic/types.js").SourceLocation | undefined} ownerLocation - Nearest owning node location.
221
- * @param {boolean} [allowJavaNegatedMinimumOperand] - Whether Java may use 2147483648 only beneath integer negation.
222
- * @returns {void}
223
- */
224
- function validateExpression(expression, language, ownerLocation, allowJavaNegatedMinimumOperand = false) {
225
- if (!expression || typeof expression != "object" || Array.isArray(expression)) {
226
- return unsupportedCapability(language, "missing or invalid expression", ownerLocation)
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
- const candidate = /** @type {import("../semantic/types.js").Expression} */ (expression)
230
- const location = candidate.location ?? ownerLocation
1870
+ const effectfulFields = "arguments,effectSiteId,effects,failureIds,kind,location,method,receiver,resolution"
1871
+ const pureFields = "arguments,kind,location,method,receiver,resolution"
231
1872
 
232
- if (typeof candidate.kind != "string") {
233
- return unsupportedCapability(language, "missing or invalid expression", location)
234
- }
235
- if (candidate.kind == "IdentifierExpression") {
236
- validateTargetIdentifier(language, candidate.name, "reference", location)
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 == "IntegerLiteral") {
240
- if (!Number.isSafeInteger(candidate.value)) {
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 (language == "java" && !validNegatedMinimumOperand && (candidate.value < -2147483648 || candidate.value > 2147483647)) {
246
- unsupportedCapability(language, "integer literal outside signed 32-bit int range", location)
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
- if ((language == "csharp" || language == "go" || language == "c" || language == "cpp") && candidate.value < 0 ||
249
- (language == "c" || language == "cpp") && Object.is(candidate.value, -0)) {
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 == "BooleanLiteral" || candidate.kind == "StringLiteral") return
255
- if (candidate.kind == "CallExpression") {
256
- validateTargetIdentifier(language, candidate.callee, "callee", location)
257
- if (!Array.isArray(candidate.arguments)) {
258
- unsupportedCapability(language, "missing or invalid call arguments", location)
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
- if (candidate.arguments.length != 2) {
261
- unsupportedCapability(language, "call argument count other than two", location)
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
- for (const argument of candidate.arguments) validateExpression(argument, language, location)
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").SemanticLanguage} language - Backend language.
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
- unsupportedCapability(language, "compile-time-known integer operation outside signed 64-bit long range", location)
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").SemanticLanguage} language - Target language.
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
- writer.mapped(expression.callee, {mappingKind: "exact", node: expression, path, role: "callee"})
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 == "ReturnStatement" || statement.kind == "PrintStatement") {
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
  }