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,7 +1,7 @@
1
1
  // @ts-check
2
2
 
3
- import {emitExpression} from "./shared.js"
4
- import {emitScalarType} from "./scalars.js"
3
+ import {emitExpression, emitType} from "./shared.js"
4
+ import {emitEffectPrefixes, emitEffectSupport} from "./effects.js"
5
5
 
6
6
  /**
7
7
  * Emits an independently executable PHP program through the source-aware writer.
@@ -10,11 +10,108 @@ import {emitScalarType} from "./scalars.js"
10
10
  * @returns {void}
11
11
  */
12
12
  export function generatePhp(module, writer) {
13
- writer.synthetic("<?php\ndeclare(strict_types=1);\n\n", "PHP program scaffolding", [module])
13
+ if (writer.program) emitProgramHeader(module, writer)
14
+ else writer.synthetic("<?php\ndeclare(strict_types=1);\n\n", "PHP program scaffolding", [module])
15
+ emitEffectSupport(writer, module, "php")
16
+ const records = module.records ?? []
17
+ const classes = module.classes ?? []
18
+ const errors = module.errors ?? []
19
+
20
+ errors.forEach((error, index) => {
21
+ const path = `/errors/${index}`
22
+
23
+ if (index > 0) writer.synthetic("\n\n", "error declaration separator", [error], [path])
24
+ writer.mapped("final class", {mappingKind: "anchor", node: error, path})
25
+ writer.synthetic(" ", "error declaration spacing", [error], [path])
26
+ writer.mapped(error.name, {mappingKind: "exact", node: error, path, role: "name"})
27
+ writer.synthetic(writer.program ? " extends \\RuntimeException {}" : " extends RuntimeException {}",
28
+ "PHP unchecked error scaffolding", [error], [path])
29
+ })
30
+ if (errors.length > 0) writer.synthetic("\n\n", "error/declaration separator", [module])
31
+
32
+ records.forEach((record, recordIndex) => {
33
+ const recordPath = `/records/${recordIndex}`
34
+
35
+ if (recordIndex > 0) writer.synthetic("\n\n", "record declaration separator", [record], [recordPath])
36
+ emitTemplateDocumentation(writer, record.typeParameters ?? [], `${recordPath}/typeParameters`)
37
+ if ((record.typeParameters?.length ?? 0) > 0) {
38
+ emitDocumentedGenericRecord(writer, record, recordPath)
39
+ return
40
+ }
41
+ writer.mapped("final readonly class", {mappingKind: "anchor", node: record, path: recordPath})
42
+ writer.synthetic(" ", "record declaration spacing", [record], [recordPath])
43
+ writer.mapped(record.name, {mappingKind: "exact", node: record, path: recordPath, role: "name"})
44
+ writer.synthetic(" {\n", "record class scaffolding", [record], [recordPath])
45
+ const documented = record.fields.filter((field) => phpTypeNeedsDocumentation(field.type))
46
+
47
+ if (documented.length > 0) {
48
+ writer.synthetic(" /**\n", "PHP record type scaffolding", [record], [recordPath])
49
+ for (const field of documented) {
50
+ const fieldIndex = record.fields.indexOf(field)
51
+ const fieldPath = `${recordPath}/fields/${fieldIndex}`
52
+
53
+ writer.synthetic(" * @param ", "PHP record type scaffolding", [field], [fieldPath])
54
+ emitType(writer, field.type, `${fieldPath}/type`, "php")
55
+ writer.synthetic(" ", "PHP record type scaffolding", [field], [fieldPath])
56
+ writer.mapped(`$${field.name}`, {mappingKind: "exact", node: field, path: fieldPath, role: "name"})
57
+ writer.synthetic("\n", "PHP record type scaffolding", [field], [fieldPath])
58
+ }
59
+ writer.synthetic(" */\n", "PHP record type scaffolding", [record], [recordPath])
60
+ }
61
+ writer.synthetic(" public function __construct(\n", "PHP record constructor scaffolding", [record], [recordPath])
62
+ record.fields.forEach((field, fieldIndex) => {
63
+ const fieldPath = `${recordPath}/fields/${fieldIndex}`
64
+
65
+ writer.synthetic(" public ", "PHP promoted field scaffolding", [field], [fieldPath])
66
+ emitNativePhpType(writer, field.type, `${fieldPath}/type`)
67
+ if (hasNativePhpType(field.type)) writer.synthetic(" ", "PHP promoted field spacing", [field], [fieldPath])
68
+ writer.mapped(`$${field.name}`, {mappingKind: "exact", node: field, path: fieldPath, role: "name"})
69
+ writer.synthetic(fieldIndex + 1 == record.fields.length ? "\n" : ",\n", "PHP promoted field separator", [field], [fieldPath])
70
+ })
71
+ writer.synthetic(" ) {}\n}", "PHP record constructor scaffolding", [record], [recordPath])
72
+ })
73
+ if (records.length > 0) writer.synthetic("\n\n", "record/function separator", [module])
74
+
75
+ classes.forEach((declaration, classIndex) => {
76
+ if (classIndex > 0) writer.synthetic("\n\n", "reference class separator", [declaration], [`/classes/${classIndex}`])
77
+ emitReferenceClass(writer, declaration, classIndex)
78
+ })
79
+ if (classes.length > 0) writer.synthetic("\n\n", "reference class/function separator", [module])
14
80
 
15
81
  module.functions.forEach((declaration, functionIndex) => {
16
82
  if (functionIndex > 0) writer.synthetic("\n\n", "declaration separator", [declaration])
17
83
 
84
+ const documentedParameters = declaration.parameters.map((parameter, index) => [parameter, index])
85
+ .filter(([parameter]) => phpTypeNeedsDocumentation(/** @type {import("../semantic/types.js").Parameter} */ (parameter).type))
86
+ const documentedReturn = phpTypeNeedsDocumentation(declaration.returnType)
87
+
88
+ if ((declaration.typeParameters?.length ?? 0) > 0 || documentedParameters.length > 0 || documentedReturn) {
89
+ writer.synthetic("/**\n", "PHP collection type scaffolding", [declaration])
90
+ for (const [parameterIndex, parameter] of (declaration.typeParameters ?? []).entries()) {
91
+ writer.synthetic(" * @template ", "PHP type parameter scaffolding", [parameter], [`/functions/${functionIndex}/typeParameters/${parameterIndex}`])
92
+ writer.mapped(parameter.name, {
93
+ mappingKind: "exact", node: parameter, path: `/functions/${functionIndex}/typeParameters/${parameterIndex}`, role: "name"
94
+ })
95
+ writer.synthetic("\n", "line break", [parameter])
96
+ }
97
+ for (const [candidate, index] of documentedParameters) {
98
+ const parameter = /** @type {import("../semantic/types.js").Parameter} */ (candidate)
99
+ const parameterIndex = /** @type {number} */ (index)
100
+ const parameterPath = `/functions/${functionIndex}/parameters/${parameterIndex}`
101
+
102
+ writer.synthetic(" * @param ", "PHP collection type scaffolding", [parameter], [parameterPath])
103
+ emitType(writer, parameter.type, `${parameterPath}/type`, "php")
104
+ writer.synthetic(" ", "PHP collection type scaffolding", [parameter], [parameterPath])
105
+ writer.mapped(`$${parameter.name}`, {mappingKind: "exact", node: parameter, path: parameterPath, role: "name"})
106
+ writer.synthetic("\n", "PHP collection type scaffolding", [parameter], [parameterPath])
107
+ }
108
+ if (documentedReturn) {
109
+ writer.synthetic(" * @return ", "PHP collection type scaffolding", [declaration])
110
+ emitType(writer, declaration.returnType, `/functions/${functionIndex}/returnType`, "php")
111
+ writer.synthetic("\n", "PHP collection type scaffolding", [declaration])
112
+ }
113
+ writer.synthetic(" */\n", "PHP collection type scaffolding", [declaration])
114
+ }
18
115
  writer.mapped("function", {mappingKind: "anchor", node: declaration})
19
116
  writer.synthetic(" ", "function spacing", [declaration])
20
117
  writer.mapped(declaration.name, {mappingKind: "exact", node: declaration, role: "name"})
@@ -23,23 +120,15 @@ export function generatePhp(module, writer) {
23
120
  const parameterPath = `/functions/${functionIndex}/parameters/${index}`
24
121
 
25
122
  if (index > 0) writer.synthetic(", ", "parameter separator", [declaration])
26
- writer.mapped(emitScalarType("php", parameter.type), {
27
- mappingKind: "exact",
28
- node: parameter.type,
29
- path: `${parameterPath}/type`,
30
- role: "type"
31
- })
32
- writer.synthetic(" ", "parameter spacing", [parameter], [parameterPath])
123
+ emitNativePhpType(writer, parameter.type, `${parameterPath}/type`)
124
+ if (hasNativePhpType(parameter.type)) writer.synthetic(" ", "parameter spacing", [parameter], [parameterPath])
33
125
  writer.mapped(`$${parameter.name}`, {mappingKind: "exact", node: parameter, path: parameterPath, role: "name"})
34
126
  })
35
127
  writer.mapped(")", {mappingKind: "anchor", node: declaration})
36
- writer.synthetic(": ", "return type separator", [declaration])
37
- writer.mapped(emitScalarType("php", declaration.returnType), {
38
- mappingKind: "exact",
39
- node: declaration.returnType,
40
- path: `/functions/${functionIndex}/returnType`,
41
- role: "type"
42
- })
128
+ if (hasNativePhpType(declaration.returnType)) {
129
+ writer.synthetic(": ", "return type separator", [declaration])
130
+ emitNativePhpType(writer, declaration.returnType, `/functions/${functionIndex}/returnType`)
131
+ }
43
132
  writer.synthetic("\n", "line break", [declaration])
44
133
  writer.mapped("{", {mappingKind: "anchor", node: declaration})
45
134
  writer.synthetic("\n", "line break", [declaration])
@@ -48,8 +137,251 @@ export function generatePhp(module, writer) {
48
137
  writer.mapped("}", {mappingKind: "anchor", node: declaration})
49
138
  })
50
139
 
51
- writer.synthetic("\n\n", "entry-point separator", [module.entryPoint])
52
- emitBlock(writer, module.entryPoint.body, "", "/entryPoint/body")
140
+ if (!writer.program || writer.isProgramEntry()) {
141
+ writer.synthetic("\n\n", "entry-point separator", [module.entryPoint])
142
+ emitBlock(writer, module.entryPoint.body, "", "/entryPoint/body")
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Emits one native PHP reference class.
148
+ * @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
149
+ * @param {import("../semantic/types.js").ClassDeclaration} declaration - Reference class.
150
+ * @param {number} classIndex - Module class index.
151
+ * @returns {void}
152
+ */
153
+ function emitReferenceClass(writer, declaration, classIndex) {
154
+ const path = `/classes/${classIndex}`
155
+
156
+ writer.mapped("final class", {mappingKind: "anchor", node: declaration, path})
157
+ writer.synthetic(" ", "reference class spacing", [declaration], [path])
158
+ writer.mapped(declaration.name, {mappingKind: "exact", node: declaration, path, role: "name"})
159
+ writer.synthetic(" {\n", "reference class open", [declaration], [path])
160
+ declaration.fields.forEach((field, index) => {
161
+ const fieldPath = `${path}/fields/${index}`
162
+
163
+ writer.synthetic(" private ", "private field scaffolding", [field], [fieldPath])
164
+ emitNativePhpType(writer, field.type, `${fieldPath}/type`)
165
+ writer.synthetic(" $", "private field spacing", [field], [fieldPath])
166
+ writer.mapped(field.name, {mappingKind: "exact", node: field, path: fieldPath, role: "name"})
167
+ writer.synthetic(";\n", "line break", [field], [fieldPath])
168
+ })
169
+ const constructor = declaration.constructor
170
+ const constructorPath = `${path}/constructor`
171
+
172
+ writer.synthetic("\n public function ", "constructor scaffolding", [constructor], [constructorPath])
173
+ writer.mapped("__construct", {mappingKind: "exact", node: constructor, path: constructorPath, role: "constructor"})
174
+ emitPhpParameters(writer, constructor.parameters, `${constructorPath}/parameters`)
175
+ writer.synthetic(" {\n", "constructor open", [constructor], [constructorPath])
176
+ emitBlock(writer, constructor.body, " ", `${constructorPath}/body`)
177
+ writer.synthetic(" }", "constructor close", [constructor], [constructorPath])
178
+ declaration.methods.forEach((method, index) => {
179
+ const methodPath = `${path}/methods/${index}`
180
+
181
+ writer.synthetic("\n\n public function ", "method scaffolding", [method], [methodPath])
182
+ writer.mapped(method.name, {mappingKind: "exact", node: method, path: methodPath, role: "name"})
183
+ emitPhpParameters(writer, method.parameters, `${methodPath}/parameters`)
184
+ writer.synthetic(": ", "method return separator", [method], [methodPath])
185
+ emitNativePhpType(writer, method.returnType, `${methodPath}/returnType`)
186
+ writer.synthetic(" {\n", "method open", [method], [methodPath])
187
+ emitBlock(writer, method.body, " ", `${methodPath}/body`)
188
+ writer.synthetic(" }", "method close", [method], [methodPath])
189
+ })
190
+ writer.synthetic("\n", "line break", [declaration], [path])
191
+ writer.mapped("}", {mappingKind: "anchor", node: declaration, path})
192
+ }
193
+
194
+ /**
195
+ * Emits one PHP parameter list.
196
+ * @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
197
+ * @param {import("../semantic/types.js").Parameter[]} parameters - Ordered parameters.
198
+ * @param {string} path - Parameter collection path.
199
+ * @returns {void}
200
+ */
201
+ function emitPhpParameters(writer, parameters, path) {
202
+ writer.synthetic("(", "parameter list open", parameters)
203
+ parameters.forEach((parameter, index) => {
204
+ const parameterPath = `${path}/${index}`
205
+
206
+ if (index) writer.synthetic(", ", "parameter separator", parameters)
207
+ emitNativePhpType(writer, parameter.type, `${parameterPath}/type`)
208
+ writer.synthetic(" $", "parameter spacing", [parameter], [parameterPath])
209
+ writer.mapped(parameter.name, {mappingKind: "exact", node: parameter, path: parameterPath, role: "name"})
210
+ })
211
+ writer.synthetic(")", "parameter list close", parameters)
212
+ }
213
+
214
+ /**
215
+ * Emits PHP's exact documented generic-record profile. Private promoted storage
216
+ * plus public getters preserves record immutability when a type variable has no
217
+ * faithful native property type.
218
+ * @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
219
+ * @param {import("../semantic/types.js").RecordDeclaration} record - Generic semantic record.
220
+ * @param {string} recordPath - Exact record path.
221
+ */
222
+ function emitDocumentedGenericRecord(writer, record, recordPath) {
223
+ writer.mapped("final class", {mappingKind: "anchor", node: record, path: recordPath})
224
+ writer.synthetic(" ", "record declaration spacing", [record], [recordPath])
225
+ writer.mapped(record.name, {mappingKind: "exact", node: record, path: recordPath, role: "name"})
226
+ writer.synthetic(" {\n /**\n", "PHP documented generic record scaffolding", [record], [recordPath])
227
+ record.fields.forEach((field, fieldIndex) => {
228
+ const fieldPath = `${recordPath}/fields/${fieldIndex}`
229
+
230
+ writer.synthetic(" * @param ", "PHP documented generic record scaffolding", [field], [fieldPath])
231
+ emitType(writer, field.type, `${fieldPath}/type`, "php")
232
+ writer.synthetic(" ", "PHP documented generic record scaffolding", [field], [fieldPath])
233
+ writer.mapped(`$${field.name}`, {mappingKind: "exact", node: field, path: fieldPath, role: "name"})
234
+ writer.synthetic("\n", "PHP documented generic record scaffolding", [field], [fieldPath])
235
+ })
236
+ writer.synthetic(" */\n public function __construct(\n", "PHP documented generic record scaffolding", [record], [recordPath])
237
+ record.fields.forEach((field, fieldIndex) => {
238
+ const fieldPath = `${recordPath}/fields/${fieldIndex}`
239
+
240
+ writer.synthetic(" private ", "PHP private promoted field scaffolding", [field], [fieldPath])
241
+ emitNativePhpType(writer, field.type, `${fieldPath}/type`)
242
+ if (hasNativePhpType(field.type)) writer.synthetic(" ", "PHP promoted field spacing", [field], [fieldPath])
243
+ writer.mapped(`$${field.name}`, {mappingKind: "exact", node: field, path: fieldPath, role: "name"})
244
+ writer.synthetic(fieldIndex + 1 == record.fields.length ? "\n" : ",\n", "PHP promoted field separator", [field], [fieldPath])
245
+ })
246
+ writer.synthetic(" ) {}", "PHP documented generic record scaffolding", [record], [recordPath])
247
+ record.fields.forEach((field, fieldIndex) => {
248
+ const fieldPath = `${recordPath}/fields/${fieldIndex}`
249
+
250
+ writer.synthetic("\n\n /**\n * @return ", "PHP documented generic getter scaffolding", [field], [fieldPath])
251
+ emitType(writer, field.type, `${fieldPath}/type`, "php")
252
+ writer.synthetic("\n */\n public function ", "PHP documented generic getter scaffolding", [field], [fieldPath])
253
+ writer.mapped(field.name, {mappingKind: "exact", node: field, path: fieldPath, role: "name"})
254
+ writer.synthetic("()", "PHP documented generic getter scaffolding", [field], [fieldPath])
255
+ if (hasNativePhpType(field.type)) {
256
+ writer.synthetic(": ", "PHP getter return type spacing", [field], [fieldPath])
257
+ emitNativePhpType(writer, field.type, `${fieldPath}/type`)
258
+ }
259
+ writer.synthetic(" {\n return $this->", "PHP documented generic getter scaffolding", [field], [fieldPath])
260
+ writer.mapped(field.name, {mappingKind: "exact", node: field, path: fieldPath, role: "name"})
261
+ writer.synthetic(";\n }", "PHP documented generic getter scaffolding", [field], [fieldPath])
262
+ })
263
+ writer.synthetic("\n}", "PHP documented generic record scaffolding", [record], [recordPath])
264
+ }
265
+
266
+ /**
267
+ * Emits a standalone PHPDoc template block.
268
+ * @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
269
+ * @param {import("../semantic/types.js").TypeParameter[]} parameters - Ordered type parameters.
270
+ * @param {string} path - Type-parameter collection path.
271
+ */
272
+ function emitTemplateDocumentation(writer, parameters, path) {
273
+ if (parameters.length == 0) return
274
+ writer.synthetic("/**\n", "PHP type parameter scaffolding", parameters)
275
+ parameters.forEach((parameter, index) => {
276
+ writer.synthetic(" * @template ", "PHP type parameter scaffolding", [parameter], [`${path}/${index}`])
277
+ writer.mapped(parameter.name, {mappingKind: "exact", node: parameter, path: `${path}/${index}`, role: "name"})
278
+ writer.synthetic("\n", "line break", [parameter])
279
+ })
280
+ writer.synthetic(" */\n", "PHP type parameter scaffolding", parameters)
281
+ }
282
+
283
+ /**
284
+ * Emits one canonical namespace, explicit imports, and matching literal load edges.
285
+ * @param {import("../semantic/types.js").SemanticModule} module - Current program module.
286
+ * @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
287
+ */
288
+ function emitProgramHeader(module, writer) {
289
+ const programModule = /** @type {import("../semantic/types.js").SemanticProgramModule} */ (/** @type {unknown} */ (module))
290
+
291
+ writer.synthetic(`<?php\ndeclare(strict_types=1);\nnamespace Semantifold\\Generated\\${writer.programModuleName(programModule.id)};\n`,
292
+ "PHP semantic namespace", [module])
293
+ for (const imported of programModule.imports) {
294
+ const dependency = writer.programModuleName(imported.moduleId)
295
+ const prefix = imported.symbolKind == "function" ? "use function" : "use"
296
+ const importPath = `/imports/${programModule.imports.indexOf(imported)}`
297
+ const localName = writer.importNameFor(imported)
298
+
299
+ writer.synthetic(`${prefix} Semantifold\\Generated\\${dependency}\\`, "PHP semantic import", [imported], [importPath])
300
+ writer.mapped(imported.importedName, {mappingKind: "exact", node: imported, path: importPath, role: "importedName"})
301
+ if (localName != imported.importedName) {
302
+ writer.synthetic(" as ", "PHP semantic import alias", [imported], [importPath])
303
+ writer.mapped(localName, {mappingKind: "exact", node: imported, path: importPath, role: "localName"})
304
+ }
305
+ writer.synthetic(";\n", "PHP semantic import terminator", [imported], [importPath])
306
+ }
307
+ for (const moduleId of [...new Set(programModule.imports.map((item) => item.moduleId))]) {
308
+ const relative = writer.relativeModuleSpecifier(moduleId).replace(/^\.\//u, "")
309
+ const imported = /** @type {import("../semantic/types.js").SemanticImport} */ (
310
+ programModule.imports.find((item) => item.moduleId == moduleId))
311
+ const importPath = `/imports/${programModule.imports.indexOf(imported)}`
312
+
313
+ writer.synthetic("require_once __DIR__ . ", "PHP require-once edge", [imported], [importPath])
314
+ writer.mapped(JSON.stringify(`/${relative}`), {mappingKind: "anchor", node: imported, path: importPath, role: "path"})
315
+ writer.synthetic(";\n", "PHP require-once terminator", [imported], [importPath])
316
+ }
317
+ if (writer.stdlibProviderEntries) {
318
+ for (const providerPath of writer.stdlibProviderPaths ?? []) {
319
+ const relative = writer.stdlibProviderSpecifier(undefined, providerPath).replace(/^\.\//u, "")
320
+
321
+ writer.synthetic("require_once __DIR__ . ", "PHP stdlib provider require-once edge", [module])
322
+ writer.synthetic(JSON.stringify(`/${relative}`), "PHP stdlib provider require-once path", [module])
323
+ writer.synthetic(";\n", "PHP stdlib provider require-once terminator", [module])
324
+ }
325
+ }
326
+ writer.synthetic("\n", "PHP namespace/declaration separator", [module])
327
+ }
328
+
329
+ /**
330
+ * Returns whether a PHP native type needs an accompanying recursive PHPDoc type.
331
+ * @param {import("../semantic/types.js").SemanticFunctionReturnType} type - Semantic type.
332
+ * @returns {boolean} Whether the type contains a collection.
333
+ */
334
+ function phpTypeNeedsDocumentation(type) {
335
+ return type.kind == "TypeVariableReference" || type.kind == "ListType" || type.kind == "MapType" ||
336
+ type.kind == "OrderedMapType" ||
337
+ type.kind == "RecordType" && (type.arguments?.length ?? 0) > 0 ||
338
+ type.kind == "OptionalType" && phpTypeNeedsDocumentation(type.valueType)
339
+ }
340
+
341
+ /**
342
+ * Emits the exact native PHP carrier while retaining recursive type mappings.
343
+ * @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
344
+ * @param {import("../semantic/types.js").SemanticFunctionReturnType} type - Semantic type.
345
+ * @param {string} path - Exact type occurrence path.
346
+ * @returns {void}
347
+ */
348
+ function emitNativePhpType(writer, type, path) {
349
+ if (type.kind == "TypeVariableReference" || type.kind == "OptionalType" && type.valueType.kind == "TypeVariableReference") return
350
+ if (type.kind == "RecordType") {
351
+ const record = writer.recordForId(type.declarationId)
352
+
353
+ writer.mapped(writer.recordNameForId(type.declarationId), {
354
+ mappingKind: "exact", name: record.name, node: type, path, role: "type"
355
+ })
356
+ return
357
+ }
358
+ if (type.kind == "ReferenceType" || type.kind == "OwnedResourceType" || type.kind == "OwnedReferenceType") {
359
+ emitType(writer, type, path, "php")
360
+ return
361
+ }
362
+ if (type.kind == "TypeReference") {
363
+ emitType(writer, type, path, "php")
364
+ return
365
+ }
366
+ if (type.kind == "OptionalType") {
367
+ writer.mapped("?", {mappingKind: "exact", node: type, path, role: "type"})
368
+ if (type.valueType.kind == "RecordType") emitNativePhpType(writer, type.valueType, `${path}/valueType`)
369
+ else if (type.valueType.kind == "TypeReference") emitType(writer, type.valueType, `${path}/valueType`, "php")
370
+ else writer.mapped("array", {mappingKind: "anchor", node: type, path})
371
+ return
372
+ }
373
+
374
+ writer.mapped("array", {mappingKind: "exact", node: type, path, role: "type"})
375
+ }
376
+
377
+ /**
378
+ * Returns whether PHP has a faithful native carrier for one exact semantic type.
379
+ * @param {import("../semantic/types.js").SemanticFunctionReturnType} type - Semantic type.
380
+ * @returns {boolean} Whether a native annotation can be emitted.
381
+ */
382
+ function hasNativePhpType(type) {
383
+ return type.kind != "TypeVariableReference" &&
384
+ !(type.kind == "OptionalType" && type.valueType.kind == "TypeVariableReference")
53
385
  }
54
386
 
55
387
  /**
@@ -73,11 +405,126 @@ function emitBlock(writer, block, indent, path) {
73
405
  * @returns {void}
74
406
  */
75
407
  function emitStatement(writer, statement, indent, path) {
408
+ emitEffectPrefixes(writer, statement, indent, path, "php",
409
+ (expression, expressionPath) => emitExpression(writer, expression, expressionPath, "php", phpIdentifier))
76
410
  if (statement.kind == "LocalDeclaration" || statement.kind == "AssignmentStatement") return emitLocal(writer, statement, indent, path)
77
411
  writer.synthetic(indent, "indentation", [statement], [path])
412
+ if (statement.kind == "PrivateFieldWriteStatement") {
413
+ emitExpression(writer, statement.receiver, `${path}/receiver`, "php", phpIdentifier)
414
+ const field = writer.privateFieldForId(statement.field)
415
+
416
+ writer.mapped(`->${field.name}`, {mappingKind: "exact", name: field.name, node: statement, path, role: "member"})
417
+ writer.synthetic(" = ", "private field assignment", [statement], [path])
418
+ emitExpression(writer, statement.expression, `${path}/expression`, "php", phpIdentifier)
419
+ writer.mapped(";", {mappingKind: "anchor", node: statement, path})
420
+ writer.synthetic("\n", "line break", [statement], [path])
421
+ return
422
+ }
423
+ if (statement.kind == "BreakStatement" || statement.kind == "ContinueStatement") {
424
+ writer.mapped(statement.kind == "BreakStatement" ? "break" : "continue", {mappingKind: "exact", node: statement, path})
425
+ writer.mapped(";", {mappingKind: "anchor", node: statement, path})
426
+ writer.synthetic("\n", "line break", [statement], [path])
427
+ return
428
+ }
429
+ if (statement.kind == "WhileStatement") {
430
+ writer.mapped("while", {mappingKind: "exact", node: statement, path})
431
+ writer.synthetic(" (", "condition-controlled loop spacing", [statement], [path])
432
+ emitExpression(writer, statement.condition, `${path}/condition`, "php", phpIdentifier)
433
+ writer.synthetic(") ", "condition-controlled loop spacing", [statement], [path])
434
+ writer.mapped("{", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
435
+ writer.synthetic("\n", "line break", [statement], [path])
436
+ emitBlock(writer, statement.body, `${indent} `, `${path}/body`)
437
+ writer.synthetic(indent, "indentation", [statement], [path])
438
+ writer.mapped("}", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
439
+ writer.synthetic("\n", "line break", [statement], [path])
440
+ return
441
+ }
442
+ if (statement.kind == "ForEachStatement") {
443
+ writer.mapped("foreach", {mappingKind: "anchor", node: statement, path})
444
+ writer.synthetic(" (", "loop scaffolding", [statement], [path])
445
+ emitExpression(writer, statement.list, `${path}/list`, "php", phpIdentifier)
446
+ writer.synthetic(" as ", "loop scaffolding", [statement], [path])
447
+ writer.mapped(`$${statement.valueBinding.name}`, {
448
+ mappingKind: "exact", node: statement.valueBinding, path: `${path}/valueBinding`, role: "name"
449
+ })
450
+ writer.synthetic(") ", "loop scaffolding", [statement], [path])
451
+ writer.mapped("{", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
452
+ writer.synthetic("\n", "line break", [statement], [path])
453
+ emitBlock(writer, statement.body, `${indent} `, `${path}/body`)
454
+ writer.synthetic(indent, "indentation", [statement], [path])
455
+ writer.mapped("}", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
456
+ writer.synthetic("\n", "line break", [statement], [path])
457
+ return
458
+ }
459
+ if (statement.kind == "ForEachMapStatement") {
460
+ writer.mapped("foreach", {mappingKind: "anchor", node: statement, path})
461
+ writer.synthetic(" (", "map loop scaffolding", [statement], [path])
462
+ emitExpression(writer, statement.map, `${path}/map`, "php", phpIdentifier)
463
+ writer.synthetic(" as ", "map loop scaffolding", [statement], [path])
464
+ writer.mapped(`$${statement.keyBinding.name}`, {
465
+ mappingKind: "exact", node: statement.keyBinding, path: `${path}/keyBinding`, role: "name"
466
+ })
467
+ writer.synthetic(" => ", "map iteration binding separator", [statement], [path])
468
+ writer.mapped(`$${statement.valueBinding.name}`, {
469
+ mappingKind: "exact", node: statement.valueBinding, path: `${path}/valueBinding`, role: "name"
470
+ })
471
+ writer.synthetic(") ", "map loop scaffolding", [statement], [path])
472
+ writer.mapped("{", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
473
+ writer.synthetic("\n", "line break", [statement], [path])
474
+ emitBlock(writer, statement.body, `${indent} `, `${path}/body`)
475
+ writer.synthetic(indent, "indentation", [statement], [path])
476
+ writer.mapped("}", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
477
+ writer.synthetic("\n", "line break", [statement], [path])
478
+ return
479
+ }
480
+ if (statement.kind == "RaiseStatement") {
481
+ const type = statement.error.error
482
+ const error = writer.errorForId(type.declarationId)
483
+
484
+ writer.mapped("throw", {mappingKind: "anchor", node: statement, path})
485
+ writer.synthetic(" new ", "error construction scaffolding", [statement.error], [`${path}/error`])
486
+ writer.mapped(writer.errorNameForId(type.declarationId), {mappingKind: "exact", name: error.name, node: statement.error, path: `${path}/error`, role: "type"})
487
+ writer.synthetic("(", "error construction open", [statement.error], [`${path}/error`])
488
+ emitExpression(writer, statement.error.message, `${path}/error/message`, "php", phpIdentifier)
489
+ writer.synthetic(");\n", "error construction close", [statement.error], [`${path}/error`])
490
+ return
491
+ }
492
+ if (statement.kind == "TryStatement") {
493
+ const caught = writer.errorForId(statement.catchType.declarationId)
494
+
495
+ writer.mapped("try", {mappingKind: "anchor", node: statement, path})
496
+ writer.synthetic(" ", "try spacing", [statement], [path])
497
+ writer.mapped("{", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
498
+ writer.synthetic("\n", "line break", [statement], [path])
499
+ emitBlock(writer, statement.body, `${indent} `, `${path}/body`)
500
+ writer.synthetic(indent, "indentation", [statement], [path])
501
+ writer.mapped("}", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
502
+ writer.synthetic(" ", "catch spacing", [statement], [path])
503
+ writer.mapped("catch", {mappingKind: "anchor", node: statement, path, role: "catch"})
504
+ writer.synthetic(" (", "catch binding scaffolding", [statement.catchBinding], [`${path}/catchBinding`])
505
+ writer.mapped(writer.errorNameForId(statement.catchType.declarationId), {mappingKind: "exact", name: caught.name, node: statement.catchType, path: `${path}/catchType`, role: "type"})
506
+ writer.synthetic(" ", "catch binding spacing", [statement.catchBinding], [`${path}/catchBinding`])
507
+ writer.mapped(`$${statement.catchBinding.name}`, {mappingKind: "exact", node: statement.catchBinding, path: `${path}/catchBinding`, role: "name"})
508
+ writer.synthetic(") ", "catch binding scaffolding", [statement.catchBinding], [`${path}/catchBinding`])
509
+ writer.mapped("{", {mappingKind: "anchor", node: statement.catchBody, path: `${path}/catchBody`})
510
+ writer.synthetic("\n", "line break", [statement], [path])
511
+ emitBlock(writer, statement.catchBody, `${indent} `, `${path}/catchBody`)
512
+ writer.synthetic(indent, "indentation", [statement], [path])
513
+ writer.mapped("}", {mappingKind: "anchor", node: statement.catchBody, path: `${path}/catchBody`})
514
+ writer.synthetic("\n", "line break", [statement], [path])
515
+ return
516
+ }
78
517
  if (statement.kind == "ReturnStatement") {
79
518
  writer.mapped("return", {mappingKind: "anchor", node: statement, path})
80
- writer.synthetic(" ", "return spacing", [statement], [path])
519
+ if (statement.expression) {
520
+ writer.synthetic(" ", "return spacing", [statement], [path])
521
+ emitExpression(writer, statement.expression, `${path}/expression`, "php", phpIdentifier)
522
+ }
523
+ writer.mapped(";", {mappingKind: "anchor", node: statement, path})
524
+ writer.synthetic("\n", "line break", [statement], [path])
525
+ return
526
+ }
527
+ if (statement.kind == "ExpressionStatement") {
81
528
  emitExpression(writer, statement.expression, `${path}/expression`, "php", phpIdentifier)
82
529
  writer.mapped(";", {mappingKind: "anchor", node: statement, path})
83
530
  writer.synthetic("\n", "line break", [statement], [path])
@@ -141,24 +588,14 @@ function emitLocal(writer, statement, indent, statementPath) {
141
588
 
142
589
  if (statement.mutable) {
143
590
  writer.synthetic("/** @var ", "PHP local type scaffolding", [statement], [statementPath])
144
- writer.mapped(emitScalarType("php", statement.type), {
145
- mappingKind: "exact",
146
- node: statement.type,
147
- path: `${statementPath}/type`,
148
- role: "type"
149
- })
591
+ emitType(writer, statement.type, `${statementPath}/type`, "php")
150
592
  writer.synthetic(" ", "PHP local type scaffolding", [statement], [statementPath])
151
593
  writer.mapped(`$${statement.name}`, {mappingKind: "exact", node: statement, path: statementPath, role: "name"})
152
594
  writer.synthetic(" */\n", "PHP local type scaffolding", [statement], [statementPath])
153
595
  } else {
154
596
  writer.synthetic("/**\n", "PHP local type scaffolding", [statement], [statementPath])
155
597
  writer.synthetic(`${indent} * @var `, "PHP local type scaffolding", [statement], [statementPath])
156
- writer.mapped(emitScalarType("php", statement.type), {
157
- mappingKind: "exact",
158
- node: statement.type,
159
- path: `${statementPath}/type`,
160
- role: "type"
161
- })
598
+ emitType(writer, statement.type, `${statementPath}/type`, "php")
162
599
  writer.synthetic(" ", "PHP local type scaffolding", [statement], [statementPath])
163
600
  writer.mapped(`$${statement.name}`, {mappingKind: "exact", node: statement, path: statementPath, role: "name"})
164
601
  writer.synthetic(`\n${indent} * @semantifold-immutable\n${indent} */\n`, "PHP local type scaffolding", [statement], [statementPath])