@intentius/tsad-conformance 1.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 (257) hide show
  1. package/README.md +59 -0
  2. package/dist/adapter.d.ts +65 -0
  3. package/dist/adapter.d.ts.map +1 -0
  4. package/dist/adapter.js +2 -0
  5. package/dist/adapter.js.map +1 -0
  6. package/dist/bundled.d.ts +5 -0
  7. package/dist/bundled.d.ts.map +1 -0
  8. package/dist/bundled.js +28 -0
  9. package/dist/bundled.js.map +1 -0
  10. package/dist/fixture.d.ts +49 -0
  11. package/dist/fixture.d.ts.map +1 -0
  12. package/dist/fixture.js +95 -0
  13. package/dist/fixture.js.map +1 -0
  14. package/dist/host.d.ts +33 -0
  15. package/dist/host.d.ts.map +1 -0
  16. package/dist/host.js +79 -0
  17. package/dist/host.js.map +1 -0
  18. package/dist/index.d.ts +7 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +7 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/rules.d.ts +46 -0
  23. package/dist/rules.d.ts.map +1 -0
  24. package/dist/rules.js +135 -0
  25. package/dist/rules.js.map +1 -0
  26. package/dist/runner.d.ts +26 -0
  27. package/dist/runner.d.ts.map +1 -0
  28. package/dist/runner.js +152 -0
  29. package/dist/runner.js.map +1 -0
  30. package/fixtures/F-CallLeak/a-call-returns-a-live-object/expect.json +8 -0
  31. package/fixtures/F-CallLeak/a-call-returns-a-live-object/project/caller.ts +5 -0
  32. package/fixtures/F-CallLeak/a-call-returns-a-live-object/project/leaky.ts +7 -0
  33. package/fixtures/F-CallLeak/a-call-returns-a-live-object/project/pure.ts +5 -0
  34. package/fixtures/F-Cycle/mutual-imports/expect.json +6 -0
  35. package/fixtures/F-Cycle/mutual-imports/project/a.ts +3 -0
  36. package/fixtures/F-Cycle/mutual-imports/project/b.ts +3 -0
  37. package/fixtures/F-Depth/exhaustion-is-a-fallback/expect.json +19 -0
  38. package/fixtures/F-Depth/exhaustion-is-a-fallback/project/deep.ts +3 -0
  39. package/fixtures/F-Depth/exhaustion-is-a-fallback/project/rec.ts +3 -0
  40. package/fixtures/F-Div-Depth/new-inside-function-body/expect.json +21 -0
  41. package/fixtures/F-Div-Depth/new-inside-function-body/project/app.ts +3 -0
  42. package/fixtures/F-Div-Depth/new-inside-function-body/project/fn.ts +3 -0
  43. package/fixtures/F-Div-Eager/referenced-not-called/expect.json +8 -0
  44. package/fixtures/F-Div-Eager/referenced-not-called/project/app.ts +3 -0
  45. package/fixtures/F-Div-NsNew/namespace-callee/expect.json +18 -0
  46. package/fixtures/F-Div-NsNew/namespace-callee/input.ts +3 -0
  47. package/fixtures/F-Div-Provenance/helper-name-from-project-import/expect.json +20 -0
  48. package/fixtures/F-Div-Provenance/helper-name-from-project-import/project/app.ts +3 -0
  49. package/fixtures/F-Div-Provenance/helper-name-from-project-import/project/local.ts +3 -0
  50. package/fixtures/F-Div-SameFileNew/no-module-graph/expect.json +8 -0
  51. package/fixtures/F-Div-SameFileNew/no-module-graph/input.ts +5 -0
  52. package/fixtures/F-Div-Tag/unregistered-tag/expect.json +15 -0
  53. package/fixtures/F-Div-Tag/unregistered-tag/input.ts +1 -0
  54. package/fixtures/F-Eval-Array/spread-string-rejected/expect.json +14 -0
  55. package/fixtures/F-Eval-Array/spread-string-rejected/input.ts +1 -0
  56. package/fixtures/F-Eval-Binary/division-and-float/expect.json +19 -0
  57. package/fixtures/F-Eval-Binary/division-and-float/input.ts +1 -0
  58. package/fixtures/F-Eval-Binary/logical-return-operand/expect.json +20 -0
  59. package/fixtures/F-Eval-Binary/logical-return-operand/input.ts +1 -0
  60. package/fixtures/F-Eval-Binary/plus-dispatch/expect.json +19 -0
  61. package/fixtures/F-Eval-Binary/plus-dispatch/input.ts +1 -0
  62. package/fixtures/F-Eval-Binary/relational-on-strings/expect.json +18 -0
  63. package/fixtures/F-Eval-Binary/relational-on-strings/input.ts +1 -0
  64. package/fixtures/F-Eval-CallEager/evaluated-at-fold-time/expect.json +8 -0
  65. package/fixtures/F-Eval-CallEager/evaluated-at-fold-time/project/app.ts +3 -0
  66. package/fixtures/F-Eval-CallEager/not-a-function/expect.json +22 -0
  67. package/fixtures/F-Eval-CallEager/not-a-function/project/app.ts +3 -0
  68. package/fixtures/F-Eval-CallEager/not-a-function/project/local.ts +1 -0
  69. package/fixtures/F-Eval-CallHelper/helper-folds/expect.json +8 -0
  70. package/fixtures/F-Eval-CallHelper/helper-folds/project/app.ts +3 -0
  71. package/fixtures/F-Eval-CallHelper/inside-function-body/expect.json +22 -0
  72. package/fixtures/F-Eval-CallHelper/inside-function-body/project/app.ts +3 -0
  73. package/fixtures/F-Eval-CallHelper/inside-function-body/project/fn.ts +5 -0
  74. package/fixtures/F-Eval-CallIntrinsic/call-form-folds/expect.json +8 -0
  75. package/fixtures/F-Eval-CallIntrinsic/call-form-folds/project/app.ts +3 -0
  76. package/fixtures/F-Eval-CallIntrinsic/inside-function-body/expect.json +22 -0
  77. package/fixtures/F-Eval-CallIntrinsic/inside-function-body/project/app.ts +3 -0
  78. package/fixtures/F-Eval-CallIntrinsic/inside-function-body/project/fn.ts +5 -0
  79. package/fixtures/F-Eval-CallLocal/local-function-folds/expect.json +7 -0
  80. package/fixtures/F-Eval-CallLocal/local-function-folds/project/app.ts +3 -0
  81. package/fixtures/F-Eval-CallLocal/local-function-folds/project/pure.ts +3 -0
  82. package/fixtures/F-Eval-CallLocal/recursion-folds/expect.json +17 -0
  83. package/fixtures/F-Eval-CallLocal/recursion-folds/project/rec.ts +3 -0
  84. package/fixtures/F-Eval-CallLocal/recursion-folds/project/shallow.ts +3 -0
  85. package/fixtures/F-Eval-CallLocal/spread-argument-rejected/expect.json +20 -0
  86. package/fixtures/F-Eval-CallLocal/spread-argument-rejected/project/app.ts +5 -0
  87. package/fixtures/F-Eval-CallLocal/spread-argument-rejected/project/pure.ts +3 -0
  88. package/fixtures/F-Eval-CallMethod/method-missing/expect.json +17 -0
  89. package/fixtures/F-Eval-CallMethod/method-missing/input.ts +1 -0
  90. package/fixtures/F-Eval-CallMethod/null-receiver-dot/expect.json +13 -0
  91. package/fixtures/F-Eval-CallMethod/null-receiver-dot/input.ts +2 -0
  92. package/fixtures/F-Eval-CallMethod/null-receiver-optional/expect.json +2 -0
  93. package/fixtures/F-Eval-CallMethod/null-receiver-optional/input.ts +2 -0
  94. package/fixtures/F-Eval-CallMethod/real-receiver/expect.json +2 -0
  95. package/fixtures/F-Eval-CallMethod/real-receiver/input.ts +1 -0
  96. package/fixtures/F-Eval-Template/number-formatting/expect.json +11 -0
  97. package/fixtures/F-Eval-Template/number-formatting/input.ts +1 -0
  98. package/fixtures/F-Eval-Template/tostring-of-non-numbers/expect.json +10 -0
  99. package/fixtures/F-Eval-Template/tostring-of-non-numbers/input.ts +1 -0
  100. package/fixtures/F-Eval-Unary/coercion/expect.json +19 -0
  101. package/fixtures/F-Eval-Unary/coercion/input.ts +1 -0
  102. package/fixtures/F-Eval-Undefined/bare-undefined/expect.json +2 -0
  103. package/fixtures/F-Eval-Undefined/bare-undefined/input.ts +1 -0
  104. package/fixtures/F-Exc-Registry/call-form-without-registry/expect.json +15 -0
  105. package/fixtures/F-Exc-Registry/call-form-without-registry/input.ts +1 -0
  106. package/fixtures/F-Host-Closed-vs-Open/package-call-is-closed/expect.json +9 -0
  107. package/fixtures/F-Host-Closed-vs-Open/package-call-is-closed/project/closed.ts +3 -0
  108. package/fixtures/F-Host-Closed-vs-Open/package-call-is-closed/project/open.ts +3 -0
  109. package/fixtures/F-Host-Closed-vs-Open/package-call-is-closed/project/pure.ts +3 -0
  110. package/fixtures/F-Host-DataExports/plain-data-export/expect.json +8 -0
  111. package/fixtures/F-Host-DataExports/plain-data-export/project/app.ts +3 -0
  112. package/fixtures/F-Host-Registry/tag-called-as-function/expect.json +8 -0
  113. package/fixtures/F-Host-Registry/tag-called-as-function/project/app.ts +3 -0
  114. package/fixtures/F-Host-Trust/unowned-package/expect.json +6 -0
  115. package/fixtures/F-Host-Trust/unowned-package/project/app.ts +3 -0
  116. package/fixtures/F-Namespace/synthetic-namespace-object/expect.json +7 -0
  117. package/fixtures/F-Namespace/synthetic-namespace-object/project/use.ts +3 -0
  118. package/fixtures/F-Namespace/synthetic-namespace-object/project/values.ts +2 -0
  119. package/fixtures/F-NoExports/nothing-to-produce/expect.json +7 -0
  120. package/fixtures/F-NoExports/nothing-to-produce/project/defaulted.ts +1 -0
  121. package/fixtures/F-NoExports/nothing-to-produce/project/empty.ts +3 -0
  122. package/fixtures/F-Prebuild/same-file-construction-shared/expect.json +19 -0
  123. package/fixtures/F-Prebuild/same-file-construction-shared/project/shared.ts +6 -0
  124. package/fixtures/F-Profile-DataHost/plain-data-across-files/expect.json +39 -0
  125. package/fixtures/F-Profile-DataHost/plain-data-across-files/project/defaults.ts +5 -0
  126. package/fixtures/F-Profile-DataHost/plain-data-across-files/project/service.ts +10 -0
  127. package/fixtures/F-Seed/seeded-by-its-own-body/expect.json +8 -0
  128. package/fixtures/F-Seed/seeded-by-its-own-body/project/app.ts +8 -0
  129. package/fixtures/F-Seed/seeded-by-its-own-body/project/lone.ts +1 -0
  130. package/fixtures/F-Succ/backward-along-captures/expect.json +26 -0
  131. package/fixtures/F-Succ/backward-along-captures/project/breaker.ts +10 -0
  132. package/fixtures/F-Succ/backward-along-captures/project/shared.ts +1 -0
  133. package/fixtures/F-Succ/backward-along-captures/project/sibling.ts +3 -0
  134. package/fixtures/F-Succ/forward-into-imports/expect.json +8 -0
  135. package/fixtures/F-Succ/forward-into-imports/project/app.ts +10 -0
  136. package/fixtures/F-Succ/forward-into-imports/project/config.ts +1 -0
  137. package/fixtures/F-Taint/independent-file-still-folds/expect.json +9 -0
  138. package/fixtures/F-Taint/independent-file-still-folds/project/alone.ts +1 -0
  139. package/fixtures/F-Taint/independent-file-still-folds/project/app.ts +8 -0
  140. package/fixtures/F-Total/one-bad-declarator-fails-the-file/expect.json +7 -0
  141. package/fixtures/F-Total/one-bad-declarator-fails-the-file/project/mixed.ts +3 -0
  142. package/fixtures/F-Val-Arity/props-attributes-and-spread/expect.json +14 -0
  143. package/fixtures/F-Val-Arity/props-attributes-and-spread/project/arities.ts +5 -0
  144. package/fixtures/F-Val-Callable/host-function-as-value/expect.json +16 -0
  145. package/fixtures/F-Val-Callable/host-function-as-value/project/app.ts +3 -0
  146. package/fixtures/F-Val-Fate/revive-every-envelope/expect.json +15 -0
  147. package/fixtures/F-Val-Fate/revive-every-envelope/project/entities.ts +6 -0
  148. package/fixtures/F-Val-Live/plain-data-is-not-live/expect.json +9 -0
  149. package/fixtures/F-Val-Live/plain-data-is-not-live/project/breaker.ts +10 -0
  150. package/fixtures/F-Val-Live/plain-data-is-not-live/project/quiet.ts +3 -0
  151. package/fixtures/F-Val-Live/plain-data-is-not-live/project/source.ts +5 -0
  152. package/fixtures/F-Val-Position/attrref-in-host-arguments/expect.json +8 -0
  153. package/fixtures/F-Val-Position/attrref-in-host-arguments/project/rejected.ts +4 -0
  154. package/fixtures/F-Val-Undefined/array-element-undefined/expect.json +15 -0
  155. package/fixtures/F-Val-Undefined/array-element-undefined/input.ts +1 -0
  156. package/fixtures/F-Val-Undefined/conditional-yields-undefined/expect.json +13 -0
  157. package/fixtures/F-Val-Undefined/conditional-yields-undefined/input.ts +2 -0
  158. package/fixtures/F-Val-Undefined/optional-chain-yields-undefined-property/expect.json +13 -0
  159. package/fixtures/F-Val-Undefined/optional-chain-yields-undefined-property/input.ts +2 -0
  160. package/fixtures/F-Val-Undefined/property-literal-undefined/expect.json +16 -0
  161. package/fixtures/F-Val-Undefined/property-literal-undefined/input.ts +1 -0
  162. package/fixtures/F-Val-Undefined/spread-carries-undefined/expect.json +14 -0
  163. package/fixtures/F-Val-Undefined/spread-carries-undefined/input.ts +2 -0
  164. package/fixtures/S-Array/spread/expect.json +1 -0
  165. package/fixtures/S-Array/spread/input.ts +1 -0
  166. package/fixtures/S-Binary/arithmetic-and-concat/expect.json +1 -0
  167. package/fixtures/S-Binary/arithmetic-and-concat/input.ts +1 -0
  168. package/fixtures/S-Binary/lazy-untaken-unfoldable/expect.json +1 -0
  169. package/fixtures/S-Binary/lazy-untaken-unfoldable/input.ts +1 -0
  170. package/fixtures/S-Binary/nullish-coalesce/expect.json +1 -0
  171. package/fixtures/S-Binary/nullish-coalesce/input.ts +1 -0
  172. package/fixtures/S-CallLocal/imported-callee-is-shape-valid/expect.json +10 -0
  173. package/fixtures/S-CallLocal/imported-callee-is-shape-valid/input.ts +3 -0
  174. package/fixtures/S-Conditional/taken-branch-only/expect.json +1 -0
  175. package/fixtures/S-Conditional/taken-branch-only/input.ts +1 -0
  176. package/fixtures/S-ExportDefault/imported-by-sibling/expect.json +30 -0
  177. package/fixtures/S-ExportDefault/imported-by-sibling/project/app.ts +3 -0
  178. package/fixtures/S-ExportDefault/imported-by-sibling/project/policy.ts +1 -0
  179. package/fixtures/S-ExportDefault/plain-object/expect.json +21 -0
  180. package/fixtures/S-ExportDefault/plain-object/input.ts +1 -0
  181. package/fixtures/S-Ident/const-chain/expect.json +1 -0
  182. package/fixtures/S-Ident/const-chain/input.ts +1 -0
  183. package/fixtures/S-Ident/process/expect.json +1 -0
  184. package/fixtures/S-Ident/process/input.ts +1 -0
  185. package/fixtures/S-Ident/unresolved/expect.json +1 -0
  186. package/fixtures/S-Ident/unresolved/input.ts +1 -0
  187. package/fixtures/S-Index/dynamic-key/expect.json +1 -0
  188. package/fixtures/S-Index/dynamic-key/input.ts +1 -0
  189. package/fixtures/S-Index/literal-key/expect.json +1 -0
  190. package/fixtures/S-Index/literal-key/input.ts +1 -0
  191. package/fixtures/S-Literal/null-and-booleans/expect.json +1 -0
  192. package/fixtures/S-Literal/null-and-booleans/input.ts +1 -0
  193. package/fixtures/S-Literal/numeric/expect.json +1 -0
  194. package/fixtures/S-Literal/numeric/input.ts +1 -0
  195. package/fixtures/S-LocalFunction/arrow-const-call/expect.json +21 -0
  196. package/fixtures/S-LocalFunction/arrow-const-call/project/label.ts +3 -0
  197. package/fixtures/S-LocalFunction/function-as-value-still-rejects/expect.json +11 -0
  198. package/fixtures/S-LocalFunction/function-as-value-still-rejects/input.ts +3 -0
  199. package/fixtures/S-LocalFunction/same-file-declaration-call/expect.json +19 -0
  200. package/fixtures/S-LocalFunction/same-file-declaration-call/project/twice.ts +5 -0
  201. package/fixtures/S-Member/nested/expect.json +1 -0
  202. package/fixtures/S-Member/nested/input.ts +1 -0
  203. package/fixtures/S-Member/nullish-plain-refuses/expect.json +1 -0
  204. package/fixtures/S-Member/nullish-plain-refuses/input.ts +1 -0
  205. package/fixtures/S-Member/optional-chain/expect.json +1 -0
  206. package/fixtures/S-Member/optional-chain/input.ts +1 -0
  207. package/fixtures/S-Object/spread-later-wins/expect.json +1 -0
  208. package/fixtures/S-Object/spread-later-wins/input.ts +1 -0
  209. package/fixtures/S-Object/spread-non-object/expect.json +1 -0
  210. package/fixtures/S-Object/spread-non-object/input.ts +1 -0
  211. package/fixtures/S-Reject/arrow-as-value/expect.json +1 -0
  212. package/fixtures/S-Reject/arrow-as-value/input.ts +1 -0
  213. package/fixtures/S-Reject/call-as-value/expect.json +1 -0
  214. package/fixtures/S-Reject/call-as-value/input.ts +1 -0
  215. package/fixtures/S-Template/coercion/expect.json +2 -0
  216. package/fixtures/S-Template/coercion/input.ts +2 -0
  217. package/fixtures/S-Template/composite-step-span/expect.json +2 -0
  218. package/fixtures/S-Template/composite-step-span/input.ts +1 -0
  219. package/fixtures/S-Template/const-span/expect.json +2 -0
  220. package/fixtures/S-Template/const-span/input.ts +2 -0
  221. package/fixtures/S-Template/envelope-span/expect.json +2 -0
  222. package/fixtures/S-Template/envelope-span/input.ts +2 -0
  223. package/fixtures/S-Template/nested-construction-span/expect.json +2 -0
  224. package/fixtures/S-Template/nested-construction-span/input.ts +1 -0
  225. package/fixtures/S-Template/no-substitution/expect.json +2 -0
  226. package/fixtures/S-Template/no-substitution/input.ts +1 -0
  227. package/fixtures/S-Template/unfoldable-span/expect.json +16 -0
  228. package/fixtures/S-Template/unfoldable-span/input.ts +1 -0
  229. package/fixtures/S-Unary/not-and-negate/expect.json +1 -0
  230. package/fixtures/S-Unary/not-and-negate/input.ts +1 -0
  231. package/fixtures/S-Unclaimed/composite-step/expect.json +3 -0
  232. package/fixtures/S-Unclaimed/composite-step/input.ts +1 -0
  233. package/fixtures/S-Unwrap/as-and-parens/expect.json +1 -0
  234. package/fixtures/S-Unwrap/as-and-parens/input.ts +1 -0
  235. package/fixtures/UNCOVERED.md +37 -0
  236. package/package.json +35 -0
  237. package/spec/VERSION +1 -0
  238. package/spec/divergence.md +129 -0
  239. package/spec/grammar.md +277 -0
  240. package/spec/hosts.md +175 -0
  241. package/spec/judgments.md +1049 -0
  242. package/spec/rules.md +102 -0
  243. package/spec/values.md +247 -0
  244. package/src/adapter.ts +52 -0
  245. package/src/adapters/chant.ts +125 -0
  246. package/src/bundled.ts +27 -0
  247. package/src/chant-agreement.test.ts +67 -0
  248. package/src/corpus.test.ts +66 -0
  249. package/src/corpus.ts +540 -0
  250. package/src/fixture.ts +117 -0
  251. package/src/host.ts +105 -0
  252. package/src/index.ts +6 -0
  253. package/src/rules.test.ts +50 -0
  254. package/src/rules.ts +140 -0
  255. package/src/runner.test.ts +46 -0
  256. package/src/runner.ts +119 -0
  257. package/src/version.test.ts +31 -0
@@ -0,0 +1 @@
1
+ { "rules": ["S-SpreadProp", "F-Eval-Object", "F-Div-SpreadType"], "export": "x", "shape": "accept", "fold": "run", "note": "Shape-valid (n is an identifier); the folder rejects once n folds to a number." }
@@ -0,0 +1 @@
1
+ const n = 5; export const x = { ...n };
@@ -0,0 +1 @@
1
+ { "rules": ["S-Reject", "F-Eval-Function", "F-Val-Callable"], "export": "x", "shape": "reject", "fold": "run", "note": "A function is never a value." }
@@ -0,0 +1 @@
1
+ export const x = () => 1;
@@ -0,0 +1 @@
1
+ { "rules": ["S-Reject", "F-Eval-Reject"], "export": "x", "shape": "reject", "fold": "run", "note": "A call has no evaluation case: structurally unrepresentable." }
@@ -0,0 +1 @@
1
+ export const x = getId();
@@ -0,0 +1,2 @@
1
+ { "rules": ["S-Template", "F-Eval-Template"], "export": "x", "shape": "accept", "fold": "fold", "value": "count=3, ok=true, n=null",
2
+ "note": "Spans coerce by ECMAScript ToString (R10.4 / F-Eval-Template)." }
@@ -0,0 +1,2 @@
1
+ const count = 3;
2
+ export const x = `count=${count}, ok=${true}, n=${null}`;
@@ -0,0 +1,2 @@
1
+ { "rules": ["S-Template", "F-Eval-Template", "F-Div-TemplateEnvelope"], "export": "x", "shape": "accept", "fold": "run",
2
+ "note": "The .step idiom in a plain template span. Refused since chant-v0.69.0 (#2397), the fifth and last coercible envelope kind; __symbol cannot reach a plain span." }
@@ -0,0 +1 @@
1
+ export const x = `step=${Checkout({}).step}`;
@@ -0,0 +1,2 @@
1
+ { "rules": ["S-Template", "F-Eval-Template", "F-Eval-Ident"], "export": "x", "shape": "accept", "fold": "fold", "value": "Hello World!",
2
+ "note": "The claim chant#2306 found stated backwards in the docs: a template interpolating a const folds." }
@@ -0,0 +1,2 @@
1
+ const name = "World";
2
+ export const x = `Hello ${name}!`;
@@ -0,0 +1,2 @@
1
+ { "rules": ["S-Template", "F-Eval-Template", "F-Div-TemplateEnvelope"], "export": "x", "shape": "accept", "fold": "run",
2
+ "note": "A symbolic value (here an attribute reference) inside a plain template span is refused. Shape accepts (bucket.arn is a member access); the folder rejects once the span folds to an envelope. chant-v0.68.0 (#2349) made this a refusal on both paths; before it, both paths produced \"[object Object]\"." }
@@ -0,0 +1,2 @@
1
+ const bucket = new Bucket({ name: "b" });
2
+ export const x = `${bucket.arn}-suffix`;
@@ -0,0 +1,2 @@
1
+ { "rules": ["S-Template", "F-Eval-Template", "F-Div-TemplateEnvelope"], "export": "x", "shape": "accept", "fold": "run",
2
+ "note": "A nested construction in a plain template span. Refused since chant-v0.69.0 (#2397); before that it coerced to \"[object Object]\" on both paths." }
@@ -0,0 +1 @@
1
+ export const x = `img=${new Image({ name: "node:22" })}`;
@@ -0,0 +1,2 @@
1
+ { "rules": ["S-Literal", "F-Eval-Literal"], "export": "x", "shape": "accept", "fold": "fold", "value": "plain",
2
+ "note": "A template with no substitutions is a literal (S-Literal), not S-Template." }
@@ -0,0 +1 @@
1
+ export const x = `plain`;
@@ -0,0 +1,16 @@
1
+ {
2
+ "rules": [
3
+ "S-Template",
4
+ "S-Reject",
5
+ "F-Eval-Reject",
6
+ "F-Reason"
7
+ ],
8
+ "export": "x",
9
+ "shape": "reject",
10
+ "fold": "run",
11
+ "rejectAt": {
12
+ "line": 1,
13
+ "column": 24
14
+ },
15
+ "note": "A span outside the subset rejects the template; the rejection points at the span, not the template (F-Reason: a located rejection is a conformance requirement; its wording is not)."
16
+ }
@@ -0,0 +1 @@
1
+ export const x = `id=${getId()}`;
@@ -0,0 +1 @@
1
+ { "rules": ["S-Unary", "F-Eval-Unary"], "export": "x", "shape": "accept", "fold": "fold", "value": [true, -2] }
@@ -0,0 +1 @@
1
+ export const x = [!0, -(2)];
@@ -0,0 +1,3 @@
1
+ { "rules": ["S-Unclaimed", "S-CompositeStep", "F-Eval-Member"], "export": "x", "shape": "accept", "fold": "fold",
2
+ "value": { "__compositeStep": "Checkout", "args": [{ "v": 4 }] },
3
+ "note": "An unclaimed bare-identifier callee narrowed to .step folds to the composite-step envelope. Claimed callees (a registered helper, an admitted intrinsic) are not unclaimed and take their own S-Call form; the reference ships no host, so nothing claims Checkout here." }
@@ -0,0 +1 @@
1
+ export const x = Checkout({ v: 4 }).step;
@@ -0,0 +1 @@
1
+ { "rules": ["S-Unwrap", "F-Eval-Unwrap"], "export": "x", "shape": "accept", "fold": "fold", "value": "a" }
@@ -0,0 +1 @@
1
+ export const x = (("a" as string) satisfies string)!;
@@ -0,0 +1,37 @@
1
+ # Rules with no conformance fixture yet
2
+
3
+ Deliberate, and this list may only shrink (#8). Each entry names the rule and why it is still uncovered. Remove an entry when its fixture lands; the gate fails if an entry is stale.
4
+
5
+ 107 of 138 rules have fixtures. Most of the rules below are uncovered because the corpus is still growing (#24); where a rule needs something the reference implementation does not have, the entry says so.
6
+
7
+ - `F-Rule-Input` — the harness has no rule hook and no findings-as-data fixture kind yet (#101)
8
+ - `F-Rule-Phase` — the harness has no rule hook and no findings-as-data fixture kind yet (#101)
9
+ - `F-Rule-Pure` — the harness has no rule hook and no findings-as-data fixture kind yet (#101)
10
+ - `F-Rule-Finding` — the harness has no rule hook and no findings-as-data fixture kind yet (#101)
11
+ - `F-Rule-Equivalence` — the harness has no rule hook and no findings-as-data fixture kind yet (#101)
12
+ - `F-Rule-Supply` — the harness has no rule hook and no findings-as-data fixture kind yet (#101)
13
+ - `F-Div-Step` — needs `{__compositeStep}`, which has no fate in the reference implementation (no composite factory form)
14
+ - `F-Eval-Interior` — an unresolved chain inside an intrinsic call folds to a symbol only with a registry in scope, and an expression fixture names no host
15
+ - `F-Host-Composite` — needs the composite registration form `Composite(fn, "N")`, which the reference implementation does not have
16
+ - `F-Host-Generality` — says what a host may vary and what it may not; no verdict exercises it
17
+ - `F-IsolatedRefusal` — needs isolation mode, which the reference implementation does not have
18
+ - `F-NoOwnExecution` — measured by chant's `test/leftness` profile; an adapter reports verdicts and cannot observe execution
19
+ - `F-NotProject` — a file inside the host's own module tree; the reference's project is a map with no outside
20
+ - `F-Obs-Counters` — the conformance adapter exposes no counters
21
+ - `F-Obs-Provenance` — an optional capability, reported rather than asserted
22
+ - `F-Val-Domain` — no fixture yet; corpus is #24's
23
+ - `F-Val-Envelope` — no fixture yet; corpus is #24's
24
+ - `F-Val-Symbol-Scope` — no fixture yet; corpus is #24's
25
+ - `S-Disqualify` — no fixture yet; corpus is #24's
26
+ - `S-ExportDestructure` — no fixture yet; corpus is #24's
27
+ - `S-ExportNamed` — no fixture yet; corpus is #24's
28
+ - `S-ExportResource` — no fixture yet; corpus is #24's
29
+ - `S-ExportSingle` — no fixture yet; corpus is #24's
30
+ - `S-ExportTypeOnly` — no fixture yet; corpus is #24's
31
+ - `S-FactoryBody` — needs the composite factory form, which the reference implementation does not have
32
+ - `S-FactoryParams` — needs the composite factory form, which the reference implementation does not have
33
+ - `S-Module` — no fixture yet; corpus is #24's
34
+ - `S-Prop` — no fixture yet; corpus is #24's
35
+ - `S-ReExport` — no fixture yet; corpus is #24's
36
+ - `S-Shorthand` — no fixture yet; corpus is #24's
37
+ - `S-TopConst` — no fixture yet; corpus is #24's
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@intentius/tsad-conformance",
3
+ "version": "1.4.0",
4
+ "description": "The conformance suite of the typescript-as-data specification: fixture loader, runner, and the named hosts. An implementation supplies an adapter and gets a report.",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/INTENTIUS/typescript-as-data.git",
9
+ "directory": "packages/conformance"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "type": "module",
15
+ "exports": {
16
+ ".": {
17
+ "development": "./src/index.ts",
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ }
21
+ },
22
+ "types": "./dist/index.d.ts",
23
+ "main": "./dist/index.js",
24
+ "files": [
25
+ "src/",
26
+ "dist/",
27
+ "fixtures/",
28
+ "spec/"
29
+ ],
30
+ "scripts": {
31
+ "build": "rm -rf dist fixtures spec && tsc -p tsconfig.build.json && cp -R ../../spec/fixtures fixtures && mkdir spec && cp ../../spec/VERSION ../../spec/grammar.md ../../spec/judgments.md ../../spec/values.md ../../spec/divergence.md ../../spec/hosts.md ../../spec/rules.md spec/",
32
+ "prepack": "npm run build"
33
+ },
34
+ "dependencies": {}
35
+ }
package/spec/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.4
@@ -0,0 +1,129 @@
1
+ # Divergence between the shape classifier and the folder
2
+
3
+ Normative draft (#17). One subset, two consumers with unequal information
4
+ (R3). This file states the direction in which they may disagree as a claim
5
+ with a proof obligation, enumerates every known divergence, and states the
6
+ two exceptions with the reason each is tolerated. Derived from
7
+ `subset.ts`'s module doc and `findSubsetViolation`, and `fold()`, at
8
+ `e4074c17` with the chant-v0.63.0 addition.
9
+
10
+ ---
11
+
12
+ ## F-Direction (the claim)
13
+
14
+ Let `shape(e, ρ)` be the classifier's verdict on expression `e` given an
15
+ optional registry `ρ`, and `fold(e, Γ, ρ)` the folder's, given also an
16
+ environment `Γ`.
17
+
18
+ > **Proposition.** For every `e`, `Γ`, `ρ`: if `fold(e, Γ, ρ)` succeeds, then
19
+ > `shape(e, ρ)` accepts, *except* in the two cases F-Exc-Lazy and
20
+ > F-Exc-Registry below.
21
+
22
+ Equivalently: the classifier may accept what the folder rejects (a *false
23
+ negative* relative to fold, visible only as a later fallback), and must never
24
+ reject what the folder accepts (a *false positive*, visible as a lint error on
25
+ folding code), outside the two named exceptions.
26
+
27
+ **Why this direction.** A false negative costs a fallback the author learns
28
+ about from `[fold:run]`. A false positive costs a lint error on correct
29
+ source, and a lint that cries wolf gets disabled. The classifier is also the
30
+ predicate a downstream tool asks "will this fold?", a tool with no registry
31
+ must get an answer that is safe to act on, which means erring toward "it may
32
+ run" (`subset.ts` module doc, point 2c).
33
+
34
+ **Discharge.** By construction: both consumers import one classifier
35
+ (`subset.ts`), so they cannot disagree on node kinds, operators or key shapes;
36
+ every remaining disagreement is a *resolution* the classifier does not
37
+ perform, enumerated below. And by fixture: one accepting and one rejecting
38
+ fixture per row of F-Div, asserting the classifier's and the folder's verdicts
39
+ side by side (#8, #24). chant's `subset-doc-parity.test.ts` is the precedent.
40
+
41
+ ## F-Div (the divergences in the permitted direction)
42
+
43
+ Shape accepts; the folder may reject, because it resolves.
44
+
45
+ | Rule | Shape sees | Folder additionally requires | Row |
46
+ |---|---|---|---|
47
+ | F-Div-Ident | any bare identifier is valid | resolves in `consts` then `externals`; `process` is a pointed rejection | L2.3, L3.6, L3.7 |
48
+ | F-Div-Tag | any tagged-template tag; interior opaque | tag is a registered, tag-foldable intrinsic; interior folds | L2.4 |
49
+ | F-Div-Provenance | a registered helper *name* | the name is bound by an import from chant, not shadowed | L2.11 |
50
+ | F-Div-SpreadType | a spread operand of valid shape | operand folds to an object (object spread) or array (array spread) | L3.4, L3.5 |
51
+ | F-Div-SameFileNew | a bare identifier | if bound to a same-file `new`, only `externals` may answer, and F-Prebuild is what puts the instance there; a folder that cannot construct rejects rather than build a duplicate | L3.8 |
52
+ | F-Div-Nullish | `a.b`, `a[k]` | the object does not fold to `null`/`undefined`; `?.` short-circuits instead (chant-v0.63.0) | L3.10, L3.21 |
53
+ | F-Div-NsNew | `new ⟨Expr⟩(…)` with any callee | the callee is a plain identifier | L3.15 |
54
+ | F-Div-Method | `x.m(…)` | the receiver folds to a real value that is not an envelope and whose `m` is a function | L3.18, L3.19 |
55
+ | F-Div-Step | `⟨call⟩.step` | the callee is an *unclaimed* bare identifier | L3.20 |
56
+ | F-Div-Depth | a `new`, tagged template, helper call, intrinsic call, or `.step` | is not inside a folded function body | L3.16 |
57
+ | F-Div-Eager | a registered eager intrinsic name | resolves to a function; is called, not referenced | L3.17 |
58
+ | F-Div-TemplateEnvelope | a member access or call inside a template span | the span does not fold to an envelope; chant refuses all five reachable kinds since v0.69.0 | L3.23 |
59
+
60
+ Every row is a fallback, never wrong output. That property, a divergence in
61
+ this direction can only lose coverage, is what makes the direction the safe
62
+ one.
63
+
64
+ ## F-Exc (the two exceptions)
65
+
66
+ Shape rejects; the folder accepts. Each is stated with why it is tolerated
67
+ rather than fixed.
68
+
69
+ **F-Exc-Lazy.** Short-circuit laziness. The folder evaluates `&&`, `||`,
70
+ `??` and `?:` lazily and never folds the untaken operand; the classifier is
71
+ flow-insensitive and requires every operand to be shape-valid (L2.9, L3.13).
72
+ `false && sideEffect()` folds to `false` and is a lint error. *Tolerated
73
+ because:* a flow-sensitive classifier is an evaluator, and the cost of the
74
+ false positive is a visible lint error on code whose untaken branch is
75
+ suspect anyway. The implementation's own module doc calls it a wart, and a
76
+ specification should not launder the word.
77
+
78
+ **F-Exc-Registry.** Call-form registration without a registry. The
79
+ classifier takes `ρ` as an optional parameter. Given one, its answer for a
80
+ plain call is the folder's own; without one, every plain call is a violation,
81
+ including `Ref(bucket)` that the folder accepts (L2.12, L2.13). *Tolerated
82
+ because:* the registry-less answer is the conservative one, it is a
83
+ parameter rather than a guess, and the lint engine now threads the active
84
+ lexicons' registry through (chant#1106) so the exception is closed in
85
+ practice for `chant lint` while remaining true of the classifier in
86
+ isolation.
87
+
88
+ There is no third exception. An implementation that discovers one has found
89
+ either a bug or a new rule, and must add it here before shipping it.
90
+
91
+ ## What this file does not claim
92
+
93
+ That the classifier and the folder agree. They do not, and the enumeration
94
+ above is the point. The claim is narrower and more useful: **every
95
+ disagreement outside F-Exc costs coverage, never correctness**, and the two
96
+ that go the other way are named, bounded, and justified.
97
+
98
+ ---
99
+
100
+ ## Rationale
101
+
102
+ Non-normative. The reasoning that motivated each rule, carried over from the retired `requirements.md` (#46). Keyed by the rule(s) each note supports.
103
+
104
+ **F-Direction** *(was R3. Membership is decided once, by a classifier permitted to err in one direction only)*
105
+
106
+ The subset has exactly one definition: `findSubsetViolation` (`subset.ts:289`),
107
+ shared by the folder and by the lint rules EVL001/EVL003 so the linted subset
108
+ and the folded subset cannot drift (L2.*).
109
+
110
+ **F-Div-*** *(was R3.1. The direction is the requirement, not the agreement)*
111
+
112
+ The two consumers have different information. A lint pass has no binding
113
+ resolver and no lexicon registry; the folder has both. The shape-only
114
+ classifier may accept what the resolving evaluator rejects, and must never
115
+ reject what it accepts. Enumerated divergences in that direction: identifier
116
+ resolution (L2.3), tag registration (L2.4), helper provenance (L2.11),
117
+ spread-source runtime type (L3.4, L3.5), a bare identifier bound to a
118
+ same-file construction (L3.8), and, since chant-v0.63.0, a member read whose
119
+ object resolves to `null`/`undefined` (L3.10; shape-valid, folder refuses).
120
+
121
+ **F-Exc-Lazy, F-Exc-Registry** *(was R3.2. The two exceptions must be stated, not tidied away)*
122
+
123
+ 1. **Short-circuit laziness** (L2.9, L3.13). The folder evaluates `&&`, `||`,
124
+ `??` and the conditional lazily; the classifier requires every branch to be
125
+ valid. The implementation's own module doc calls this a wart.
126
+ 2. **Intrinsic call-form registration** (L2.12). The classifier takes the
127
+ registry as an optional parameter, exact with one, conservatively rejecting
128
+ without. The parameter exists so a downstream tool can ask "will this fold?"
129
+ without running a fold.
@@ -0,0 +1,277 @@
1
+ # Grammar of the fold subset
2
+
3
+ Normative draft (#12). Identifiers are `S-*` per #6: every production here is
4
+ decidable from syntax alone. Where the folder is stricter than the shape rule
5
+ - because it also resolves names or consults a registry, the production says
6
+ so and names the `F-*` rule (judgments.md) that adds the condition. That is
7
+ the one direction R3.1 permits; a production must never be stricter than the
8
+ folder.
9
+
10
+ Derived from `findSubsetViolation` (`subset.ts:289`), `fold()`
11
+ (`fold.ts:896`), `scanExports` (`fold-import.ts:591`),
12
+ `findFunctionSubsetViolation` and `findFactorySubsetViolation`, at
13
+ `e4074c17`, with the `?.` forms from chant-v0.63.0 (`11572c7a`, #2328). Node kinds are TypeScript's.
14
+
15
+ Notation: `⟨X⟩` is a nonterminal; `|` alternation; `*` zero or more; `+` one
16
+ or more. Terminals are TypeScript tokens or node kinds. Each production cites
17
+ its inventory row.
18
+
19
+ ---
20
+
21
+ ## 1. Module and statements, the gate
22
+
23
+ **S-Module.** A module is a sequence of top-level statements. The gate
24
+ examines **only statements carrying `export`**. Every other statement, a
25
+ non-exported `let`, a bare call, an `if`, a non-exported class, is neither
26
+ admitted nor a disqualifier: it is invisible to the gate, and its effects are
27
+ never performed (R2). (`fold-import.ts` `scanExports`, the `continue` for
28
+ non-exported statements; L1.1.)
29
+
30
+ **S-TopConst.** `const ⟨Identifier⟩ = ⟨Expr⟩`, exported or not, is a binding
31
+ (R6.6, L5.1). A destructured or uninitialized top-level `const` is not a
32
+ binding: the name is invisible.
33
+
34
+ **S-LocalFunction.** A top-level `function ⟨Identifier⟩ ( ⟨Params⟩ ) ⟨Block⟩`,
35
+ exported or not, and a top-level `const ⟨Identifier⟩ = ⟨Arrow⟩` or
36
+ `= function …`, bind the name to a project-local function (F-Bind). The body
37
+ is judged by S-FnBody at the call, never here; the name used as a value is
38
+ F-Eval-Ident's rejection. Added in `1.2` (#95), after chant was found to
39
+ fold both forms while the text said so for neither.
40
+
41
+ A module is **admitted** when every exported statement matches one of
42
+ S-ExportResource … S-ExportTypeOnly. One S-Disqualify match rejects the whole
43
+ module (R4.1, L1.2–L1.5).
44
+
45
+ ```
46
+ S-ExportResource ::= export const ⟨Identifier⟩ = new ⟨Expr⟩ ( ⟨Args⟩ )
47
+ S-ExportSingle ::= export const ⟨Identifier⟩ = ⟨Expr⟩
48
+ S-ExportDestructure ::= export const { ⟨PlainElement⟩+ } = ⟨Expr⟩
49
+ S-ExportNamed ::= export { ( ⟨Identifier⟩ ( as ⟨Identifier⟩ )? )+ }
50
+ S-ReExport ::= export { ( ⟨Identifier⟩ ( as ⟨Identifier⟩ )? )+ } from ⟨StringLiteral⟩
51
+ S-ExportFunction ::= export function ⟨Identifier⟩ ( ⟨Params⟩ ) ⟨Block⟩
52
+ S-ExportDefault ::= export default ⟨Expr⟩ -- data-host; see below
53
+ S-ExportTypeOnly ::= export type { … } | a type-only element of S-ExportNamed / S-ReExport
54
+
55
+ ⟨PlainElement⟩ ::= ⟨Identifier⟩ | ⟨PropertyName⟩ : ⟨Identifier⟩ -- no rest, no default, no nesting
56
+ ```
57
+
58
+ Notes. S-ExportFunction: an overload signature (no body) is skipped, not a
59
+ disqualifier; the bodied declaration that follows is the export. S-ReExport:
60
+ named elements only. S-ExportNamed: a local name must be an identifier, the
61
+ TS 4.5 string module-export-name form disqualifies.
62
+
63
+ S-ExportDefault is the declarator named `default`, admitted in the
64
+ `data-host` profile since `1.2` (#94); in `full` it stays a disqualifier
65
+ until chant admits it, which the profile table records as permitted and not
66
+ required. `export default function` and `export =` disqualify in both.
67
+
68
+ ```
69
+ S-Disqualify ::= export default … -- full only; data-host admits S-ExportDefault
70
+ | export default function …
71
+ | export * from …
72
+ | export class …
73
+ | export let … | export var …
74
+ | export const ⟨Identifier⟩ -- no initializer
75
+ | export const [ … ] = … -- array pattern
76
+ | export const { … ⟨rest | default | nested⟩ … } = …
77
+ | export { "…" as ⟨Identifier⟩ }
78
+ ```
79
+
80
+ ---
81
+
82
+ ## 2. Expressions, the classifier
83
+
84
+ `⟨Expr⟩` is any production below. The shape rule is what `findSubsetViolation`
85
+ decides; an **F** note is a condition only the folder can check (R3.1).
86
+
87
+ ```
88
+ S-Unwrap ::= ( ⟨Expr⟩ ) | ⟨Expr⟩ as ⟨Type⟩ | ⟨Expr⟩ satisfies ⟨Type⟩ | ⟨Expr⟩ !
89
+ S-Literal ::= ⟨StringLiteral⟩ | ⟨NoSubstitutionTemplateLiteral⟩ | ⟨NumericLiteral⟩ | true | false | null
90
+ S-Undefined ::= undefined
91
+ S-Ident ::= ⟨Identifier⟩
92
+ S-Template ::= ` ⟨text⟩ ( ${ ⟨Expr⟩ } ⟨text⟩ )+ `
93
+ S-Tagged ::= ⟨Identifier⟩ ` … `
94
+ S-Object ::= { ⟨Member⟩* }
95
+ ⟨Member⟩ ::= S-Prop | S-Shorthand | S-SpreadProp
96
+ S-Prop ::= ⟨LiteralKey⟩ : ⟨Expr⟩
97
+ S-Shorthand ::= ⟨Identifier⟩
98
+ S-SpreadProp::= ... ⟨Expr⟩
99
+ ⟨LiteralKey⟩::= ⟨Identifier⟩ | ⟨StringLiteral⟩ | ⟨NumericLiteral⟩
100
+ S-Array ::= [ ( ⟨Expr⟩ | ... ⟨Expr⟩ )* ]
101
+ S-Member ::= ⟨Expr⟩ ( . | ?. ) ⟨Identifier⟩
102
+ S-Index ::= ⟨Expr⟩ ( [ | ?.[ ) ⟨StringLiteral⟩ | ⟨NumericLiteral⟩ ]
103
+ S-Unary ::= ! ⟨Expr⟩ | - ⟨Expr⟩
104
+ S-Binary ::= ⟨Expr⟩ ⟨BinOp⟩ ⟨Expr⟩
105
+ ⟨BinOp⟩ ::= && | || | ?? | + | - | * | / | === | !== | > | < | >= | <=
106
+ S-Conditional ::= ⟨Expr⟩ ? ⟨Expr⟩ : ⟨Expr⟩
107
+ S-New ::= new ⟨Expr⟩ ( ⟨Expr⟩* )
108
+ S-Call ::= S-CallHelper | S-CallIntrinsic | S-CallEager | S-CallMethod | S-CompositeStep
109
+ S-CallHelper ::= ⟨HelperName⟩ ( ⟨Expr⟩* )
110
+ S-CallIntrinsic ::= ⟨Identifier⟩ ( ⟨Expr⟩* ) -- registry-gated
111
+ S-CallEager ::= ⟨Identifier⟩ ( ⟨Expr⟩* ) -- registry-gated
112
+ S-CallMethod ::= ⟨Expr⟩ ( . | ?. ) ⟨Identifier⟩ ( ⟨Expr⟩* )
113
+ S-CompositeStep ::= ⟨UnclaimedCall⟩ . step
114
+ ⟨UnclaimedCall⟩ ::= ⟨Identifier⟩ ( ⟨Expr⟩* ) -- see S-Unclaimed
115
+ S-Reject ::= anything else
116
+ ```
117
+
118
+ Per-production conditions and divergences:
119
+
120
+ | Production | Row | Shape rule | F: what the folder adds |
121
+ |---|---|---|---|
122
+ | S-Unwrap | L2.1 | recurse into the operand |; |
123
+ | S-Literal | L2.2 | admitted | numeric → `Number(text)` |
124
+ | S-Undefined |; | an identifier, shape-valid as S-Ident | folds to `undefined` |
125
+ | S-Ident | L2.3 | always shape-valid | must resolve in `consts` then `externals` (R6.6); a bare `process` is a pointed rejection (R8); a name bound to a same-file `new` resolves only via `externals`, where F-Prebuild placed the single instance (R4.6) |
126
+ | S-Template | L2.5 | every span ∈ ⟨Expr⟩ | spans coerce by `ToString` (R10.4) |
127
+ | S-Tagged | L2.4 | interior **opaque**; not recursed | tag must be a registered, tag-foldable intrinsic; interior folds with unresolved dotted chains kept symbolic |
128
+ | S-Prop | L2.6 | key must be ⟨LiteralKey⟩ (EVL001); value ∈ ⟨Expr⟩ |; |
129
+ | S-Shorthand | L2.6 | always shape-valid | the name resolves as S-Ident |
130
+ | S-SpreadProp | L2.6 | operand ∈ ⟨Expr⟩ | operand must fold to a non-null object (R10.6) |
131
+ | S-Array |; | each element or spread operand ∈ ⟨Expr⟩ | spread operand must be an array (R10.6) |
132
+ | S-Member | L2.15 | object ∈ ⟨Expr⟩, `.` or `?.`; **S-CompositeStep takes precedence** when the member is `step` and the object is a call | on a resource-bound identifier → `{__attrRef}` (R10.3); `.` on `null`/`undefined` → **refused** (R10.2, L3.10); `?.` on `null`/`undefined` → short-circuits the rest of the chain to `undefined` (L3.21) |
133
+ | S-Index | L2.7 | key must be a string or numeric **literal** (EVL003); object ∈ ⟨Expr⟩, `[` or `?.[` | same as S-Member, including the `.`/`?.` distinction |
134
+ | S-Unary | L2.8 | operator ∈ {`!`, `-`} | ECMAScript coercion (R10.1) |
135
+ | S-Binary | L2.8, L2.9 | operator ∈ ⟨BinOp⟩; **both** sides ∈ ⟨Expr⟩ (flow-insensitive) | `&&`/`\|\|`/`??` evaluate lazily; the untaken side is never folded (R3.2) |
136
+ | S-Conditional | L2.9 | all three ∈ ⟨Expr⟩ | only the taken branch is folded (R3.2) |
137
+ | S-New | L2.10 | every argument ∈ ⟨Expr⟩, positionally; **callee shape unconstrained** | callee must be a plain identifier (L3.15, R6.3); refused inside a folded function body (R6.3) |
138
+ | S-CallHelper | L2.11 | name ∈ `FOLDABLE_AUTHORING_HELPERS`; args ∈ ⟨Expr⟩ | name must be bound by an import from chant (R3.3); not shadowed by a local `const`; refused inside a folded function body |
139
+ | S-CallIntrinsic | L2.12 | with a registry: name registered with `foldsAsCall`; **without a registry: S-Reject** | name must resolve through the file's imports; refused inside a folded function body |
140
+ | S-CallEager | L2.13 | with a registry: name registered with `foldsEagerly`; without: S-Reject | callee must resolve to a function; evaluated at fold time (R7.3) |
141
+ | S-CallLocal | L2.17, L5.4 | callee is an identifier this file binds by S-LocalFunction or by an import from a project specifier (`./`, `../`); args ∈ ⟨Expr⟩ | the body must satisfy S-FnBody at the call (F-Eval-CallLocal); a cross-file callee needs the module graph, so an expression-level folder refuses it |
142
+ | S-CallMethod | L2.14 | receiver ∈ ⟨Expr⟩, `.` or `?.`; args ∈ ⟨Expr⟩; method name unconstrained | receiver must fold to a real value, not a symbolic envelope; the named property must be a function (R3.3). Receiver `null`/`undefined`: `.` refuses, `?.` short-circuits (L3.22) |
143
+ | S-CompositeStep | L2.15 | any call, any arguments, member exactly `step` | callee must be an *unclaimed* bare identifier (L3.20); refused inside a folded function body |
144
+ | S-Reject | L2.16 | EVL001 |; |
145
+
146
+ **S-Unclaimed.** A call is *unclaimed* when its callee is a bare identifier
147
+ that no other S-Call form claims. The callee must not be:
148
+
149
+ - a registered authoring helper
150
+ - an intrinsic the registry admits in call or eager form
151
+ - a project-local function the scope binds
152
+ - shadowed by a local `const`
153
+
154
+ Only the bare-identifier part is decidable from syntax. The four conditions
155
+ are resolution, so S-CompositeStep admits any call at shape level and
156
+ F-Eval-Member step 2 applies the full test. S-CompositeStep, F-Div-Step and
157
+ F-Eval-Member all used this term without a definition until #51.
158
+
159
+ **Explicitly outside the subset** (S-Reject at shape level, and rejected by
160
+ the folder): an arrow or function expression as a value (L3.1); class
161
+ expressions; `await`, `yield`; assignment and compound assignment; the comma
162
+ operator; `typeof`, `void`, `delete`, `++`, `--`; `==`, `!=`, `%`, `**`, the
163
+ bitwise operators, `in`, `instanceof`; a computed property name; a
164
+ non-literal element-access key; a call not matching any S-Call form. Optional chaining is
165
+ specified, not merely admitted: since chant-v0.63.0 a `?.` on a nullish object
166
+ produces a chain-short-circuit value that propagates through the remaining
167
+ `.`/`[]`/`!`/`?.()` of the same chain and becomes `undefined` at its end -
168
+ ECMAScript's semantics, implemented rather than coincidental.
169
+
170
+ ---
171
+
172
+ ## 3. Body sub-grammars
173
+
174
+ Two further statement-level grammars govern what may appear *inside* a
175
+ function the folder evaluates. Both are stricter than S-Module.
176
+
177
+ **S-FnBody**, a project-local function (R6.5, L5.4):
178
+
179
+ ```
180
+ S-FnParams ::= ( ⟨Identifier⟩ ( = ⟨Expr⟩ )? | { ⟨PlainElement⟩+ } )* -- no rest, no array pattern
181
+ S-FnBody ::= ⟨Expr⟩
182
+ | { ( const ⟨Identifier⟩ = ⟨Expr⟩ | const { ⟨PlainElement⟩+ } = ⟨Expr⟩ )* ( return ⟨Expr⟩? )? }
183
+ ```
184
+
185
+ No generator, no `async`, no early `return`, no `let`/`var`, no uninitialized
186
+ `const`, no other statement kind. A block with no `return` evaluates to
187
+ `undefined`. Inside S-FnBody the expression grammar **loses** S-New, S-Tagged,
188
+ S-CallHelper, S-CallIntrinsic and S-CompositeStep (R6.3, L3.16).
189
+
190
+ **S-FactoryBody**, an interpretable composite factory (R7.2 rules 3–5, L7.3–L7.5):
191
+
192
+ ```
193
+ S-FactoryParams ::= ( ⟨Identifier⟩ | { ⟨PlainElement⟩+ } )? -- at most one; no default, no rest
194
+ S-FactoryBody ::= ⟨Expr⟩
195
+ | { ( const … = ⟨Expr⟩ )* return ⟨Expr⟩ } -- must end in return; empty body rejected
196
+ ```
197
+
198
+ Inside S-FactoryBody the expression grammar **gains** `new` in any value
199
+ position and a call through a bare identifier (a nested composite, a
200
+ registered helper, an opted-in intrinsic), and a method call stays out (per
201
+ the contract above `resolveInterpretableFactory`; `checkFactoryExpression`'s
202
+ exact set is not transcribed here, #44 precondition).
203
+
204
+ The asymmetry between the two. S-FnBody tolerates a missing `return`,
205
+ S-FactoryBody requires one, is R6.5's and is a decision the spec should make
206
+ rather than inherit.
207
+
208
+ ---
209
+
210
+ ## What this grammar does not decide
211
+
212
+ Resolution (R6.6), registration (R3.2), trust (R2.1), the fold/run verdict and
213
+ its taint (R4, judgments.md), and every semantic rule in R10. A string that
214
+ parses under this grammar is *shape-admissible*; whether it folds is the
215
+ judgments' question.
216
+
217
+ ---
218
+
219
+ ## Rationale
220
+
221
+ Non-normative. The reasoning that motivated each rule, carried over from the retired `requirements.md` (#46). Keyed by the rule(s) each note supports.
222
+
223
+ **S-Call** *(was R3.3. A call is structurally unrepresentable, with an enumerated set of exceptions)*
224
+
225
+ A function call as a value has no evaluation case, it is absent from the
226
+ mechanism, not forbidden by a rule (L2.16). The spec must enumerate the
227
+ exceptions exhaustively, and say which *kind* each is:
228
+
229
+ | Exception | Kind | Rows |
230
+ |---|---|---|
231
+ | registered authoring helper | closed allowlist, name **and** import provenance | L2.11 |
232
+ | lexicon intrinsic, call form opted in | closed allowlist, per intrinsic | L2.12 |
233
+ | project-local function with a foldable body | open, local; the callee's binding is visible in the file, which is S-CallLocal since `1.2` | L2.17, L5.4 |
234
+ | eagerly-evaluated lexicon function | closed allowlist, evaluates at fold time | L2.13, R7.3 |
235
+ | method call on a real receiver | receiver-type condition, method never checked by name | L2.14, L3.18, L3.19 |
236
+ | `<Identifier>(...).step` | one idiom, member fixed, callee must be unclaimed | L2.15, L3.20 |
237
+
238
+ "The callee is admitted" and "the receiver is admitted" are different
239
+ admissibility rules and an implementer will conflate them.
240
+
241
+ ---
242
+
243
+ **S-Module** *(was R6. Admissibility is decided at two layers, and is scope-dependent)*
244
+
245
+ The first revision specified only the expression layer.
246
+
247
+ **S-Module, S-Disqualify** *(was R6.1. The statement gate runs first and disqualifies whole files)*
248
+
249
+ `scanExports` (L1.1–L1.6) recognizes exactly: `export const X = new Type(...)`,
250
+ `export const X = <expr>`, `export const {a, b} = <expr>`, `export {a, b}`,
251
+ `export {a, b} from "./m"`, and `export function f() {}`. Anything else
252
+ disqualifies the file: `export default`, `export * from`, an exported class,
253
+ `let`/`var`, a destructured export with a rest, nested or defaulted element.
254
+ `export type {...}` and type-only re-export elements are erased, not
255
+ disqualifiers (L1.6).
256
+
257
+ **§2** *(was R6.2. The expression layer is R3's subject and #12's grammar)*
258
+
259
+ Every expression reachable from an admitted statement is classified by R3's
260
+ single definition. The admissible forms, literals, templates with spans,
261
+ object members with literal keys, element access with literal keys, the
262
+ operator sets, positional `new` arguments, are enumerated by the grammar
263
+ (#12), not here. Inventory rows L2.1, L2.2, L2.5–L2.8, L2.10 remain GAP until
264
+ #12 lands.
265
+
266
+ **S-FnBody, S-FactoryBody** *(was R6.5. Two further statement-level subsets, and an asymmetry between them)*
267
+
268
+ A **project-local function** is admissible (L5.4) when its parameters bind
269
+ plainly, its body is a single expression or `const` declarations followed by
270
+ one `return`, and it is not a generator, async, rest-parameter, early-return,
271
+ or `let`/`var` function. Parameter defaults fold in the callee's scope (L5.6).
272
+ A block body with no `return` evaluates to `undefined` (L5.7).
273
+
274
+ A **composite factory** is admissible (R7.2) under rules 3–5 of the same
275
+ shape, except that its body **must** end in `return` and an empty body is
276
+ rejected (L7.4). The two subsets differ on exactly this point and the spec
277
+ should say why, or fix one.