functionalscript 0.45.0 → 0.46.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 (296) hide show
  1. package/README.md +5 -3
  2. package/fjs/asn.1/module.f.mjs +8 -1
  3. package/fjs/asn.1/proof.f.d.mts +10 -0
  4. package/fjs/asn.1/proof.f.mjs +16 -0
  5. package/fjs/basen/base128/module.f.mjs +22 -5
  6. package/fjs/bnf/data/module.f.d.mts +32 -6
  7. package/fjs/bnf/data/module.f.mjs +112 -7
  8. package/fjs/bnf/data/proof.f.d.mts +2 -0
  9. package/fjs/bnf/data/proof.f.mjs +74 -3
  10. package/fjs/bnf/data/types.d.ts +20 -2
  11. package/fjs/bnf/descent/module.f.d.mts +28 -14
  12. package/fjs/bnf/descent/module.f.mjs +122 -77
  13. package/fjs/bnf/descent/proof.f.d.mts +2 -0
  14. package/fjs/bnf/descent/proof.f.mjs +117 -31
  15. package/fjs/bnf/descent/types.d.ts +12 -14
  16. package/fjs/bnf/ll1/module.f.d.mts +64 -22
  17. package/fjs/bnf/ll1/module.f.mjs +214 -154
  18. package/fjs/bnf/ll1/proof.f.d.mts +15 -2
  19. package/fjs/bnf/ll1/proof.f.mjs +323 -149
  20. package/fjs/bnf/ll1/types.d.ts +16 -24
  21. package/fjs/bnf/matcher/module.f.d.mts +66 -0
  22. package/fjs/bnf/matcher/module.f.mjs +81 -0
  23. package/fjs/bnf/matcher/proof.f.d.mts +10 -0
  24. package/fjs/bnf/matcher/proof.f.mjs +79 -0
  25. package/fjs/bnf/matcher/types.d.ts +54 -0
  26. package/fjs/bnf/testlib.f.d.mts +31 -0
  27. package/fjs/bnf/testlib.f.mjs +80 -0
  28. package/fjs/cas/cli/module.f.d.mts +1 -1
  29. package/fjs/cas/cli/module.f.mjs +14 -20
  30. package/fjs/cas/cli/proof.f.d.mts +1 -3
  31. package/fjs/cas/cli/proof.f.mjs +44 -33
  32. package/fjs/cas/evo/module.f.d.mts +64 -18
  33. package/fjs/cas/evo/module.f.mjs +148 -70
  34. package/fjs/cas/evo/proof.f.d.mts +10 -1
  35. package/fjs/cas/evo/proof.f.mjs +305 -223
  36. package/fjs/cas/evo/types.d.ts +45 -9
  37. package/fjs/cas/module.f.d.mts +18 -32
  38. package/fjs/cas/module.f.mjs +129 -128
  39. package/fjs/cas/proof.f.d.mts +11 -8
  40. package/fjs/cas/proof.f.mjs +259 -147
  41. package/fjs/cas/types.d.ts +24 -10
  42. package/fjs/ci/config/module.f.d.mts +2 -2
  43. package/fjs/ci/config/module.f.mjs +2 -2
  44. package/fjs/ci/module.f.d.mts +5 -5
  45. package/fjs/ci/module.f.mjs +8 -7
  46. package/fjs/ci/nix/module.f.d.mts +7 -5
  47. package/fjs/ci/nix/module.f.mjs +13 -12
  48. package/fjs/ci/nix/proof.f.mjs +2 -2
  49. package/fjs/ci/proof.f.mjs +9 -5
  50. package/fjs/cli/module.f.d.mts +4 -6
  51. package/fjs/cli/module.f.mjs +4 -8
  52. package/fjs/cli/proof.f.mjs +17 -16
  53. package/fjs/cli/types.d.ts +2 -3
  54. package/fjs/common/monoid/types.d.ts +1 -1
  55. package/fjs/crypto/hmac/module.f.mjs +2 -2
  56. package/fjs/crypto/sha2/module.f.mjs +3 -1
  57. package/fjs/crypto/sha2/proof.f.d.mts +1 -0
  58. package/fjs/crypto/sha2/proof.f.mjs +24 -0
  59. package/fjs/crypto/sha2/types.d.ts +11 -0
  60. package/fjs/crypto/sign/module.f.mjs +2 -2
  61. package/fjs/dev/module.f.d.mts +13 -4
  62. package/fjs/dev/module.f.mjs +56 -27
  63. package/fjs/dev/update/module.f.d.mts +9 -4
  64. package/fjs/dev/update/module.f.mjs +14 -10
  65. package/fjs/dev/update/proof.f.d.mts +1 -3
  66. package/fjs/dev/update/proof.f.mjs +10 -5
  67. package/fjs/djs/module.f.d.mts +13 -5
  68. package/fjs/djs/module.f.mjs +31 -16
  69. package/fjs/djs/parser/module.f.d.mts +13 -3
  70. package/fjs/djs/parser/module.f.mjs +117 -16
  71. package/fjs/djs/parser/proof.f.d.mts +5 -1
  72. package/fjs/djs/parser/proof.f.mjs +274 -12
  73. package/fjs/djs/parser/types.d.ts +7 -1
  74. package/fjs/djs/proof.f.d.mts +18 -2
  75. package/fjs/djs/proof.f.mjs +187 -12
  76. package/fjs/djs/serializer/module.f.d.mts +25 -8
  77. package/fjs/djs/serializer/module.f.mjs +61 -15
  78. package/fjs/djs/serializer/proof.f.d.mts +5 -0
  79. package/fjs/djs/serializer/proof.f.mjs +23 -1
  80. package/fjs/djs/tokenizer/module.f.d.mts +17 -5
  81. package/fjs/djs/tokenizer/module.f.mjs +99 -60
  82. package/fjs/djs/tokenizer/proof.f.d.mts +1 -1
  83. package/fjs/djs/tokenizer/proof.f.mjs +74 -67
  84. package/fjs/djs/transpiler/module.f.d.mts +12 -7
  85. package/fjs/djs/transpiler/module.f.mjs +82 -56
  86. package/fjs/djs/transpiler/types.d.ts +7 -3
  87. package/fjs/djs/types.d.ts +7 -1
  88. package/fjs/effects/list/module.f.d.mts +14 -11
  89. package/fjs/effects/list/module.f.mjs +13 -11
  90. package/fjs/effects/list/types.d.ts +27 -7
  91. package/fjs/effects/memory/module.f.d.mts +2 -1
  92. package/fjs/effects/memory/module.f.mjs +3 -4
  93. package/fjs/effects/memory/proof.f.mjs +13 -7
  94. package/fjs/effects/memory/types.d.ts +4 -3
  95. package/fjs/effects/mock/module.f.d.mts +20 -4
  96. package/fjs/effects/mock/module.f.mjs +37 -5
  97. package/fjs/effects/mock/types.d.ts +10 -1
  98. package/fjs/effects/module.d.mts +4 -2
  99. package/fjs/effects/module.f.d.mts +466 -280
  100. package/fjs/effects/module.f.mjs +537 -299
  101. package/fjs/effects/module.mjs +2 -1
  102. package/fjs/effects/node/memory/module.d.mts +4 -2
  103. package/fjs/effects/node/memory/module.mjs +6 -3
  104. package/fjs/effects/node/memory/proof.mjs +10 -4
  105. package/fjs/effects/node/module.d.mts +4 -3
  106. package/fjs/effects/node/module.f.d.mts +168 -33
  107. package/fjs/effects/node/module.f.mjs +256 -52
  108. package/fjs/effects/node/module.mjs +57 -34
  109. package/fjs/effects/node/proof.f.d.mts +28 -2
  110. package/fjs/effects/node/proof.f.mjs +161 -42
  111. package/fjs/effects/node/types.d.ts +106 -18
  112. package/fjs/effects/node/virtual/module.f.d.mts +18 -4
  113. package/fjs/effects/node/virtual/module.f.mjs +110 -68
  114. package/fjs/effects/node/virtual/proof.f.d.mts +28 -2
  115. package/fjs/effects/node/virtual/proof.f.mjs +190 -9
  116. package/fjs/effects/proof.f.d.mts +69 -37
  117. package/fjs/effects/proof.f.mjs +410 -130
  118. package/fjs/effects/types.d.ts +161 -33
  119. package/fjs/emergent_testing/module.f.d.mts +19 -12
  120. package/fjs/emergent_testing/module.f.mjs +93 -33
  121. package/fjs/emergent_testing/proof.f.d.mts +21 -7
  122. package/fjs/emergent_testing/proof.f.mjs +166 -32
  123. package/fjs/emergent_testing/types.d.ts +22 -4
  124. package/fjs/fsm/module.f.d.mts +14 -4
  125. package/fjs/fsm/module.f.mjs +54 -37
  126. package/fjs/fsm/proof.f.d.mts +2 -0
  127. package/fjs/fsm/proof.f.mjs +83 -114
  128. package/fjs/js/keywords/module.f.d.mts +52 -0
  129. package/fjs/js/keywords/module.f.mjs +72 -0
  130. package/fjs/js/keywords/proof.f.d.mts +3 -0
  131. package/fjs/js/keywords/proof.f.mjs +13 -0
  132. package/fjs/js/tokenizer/module.f.d.mts +26 -6
  133. package/fjs/js/tokenizer/module.f.mjs +145 -159
  134. package/fjs/js/tokenizer/proof.f.d.mts +1 -0
  135. package/fjs/js/tokenizer/proof.f.mjs +54 -24
  136. package/fjs/js/tokenizer/types.d.ts +33 -24
  137. package/fjs/mcp/cas/module.f.d.mts +1 -6
  138. package/fjs/mcp/cas/module.f.mjs +55 -51
  139. package/fjs/mcp/cas/proof.f.d.mts +15 -0
  140. package/fjs/mcp/cas/proof.f.mjs +174 -0
  141. package/fjs/mcp/evo/module.f.d.mts +19 -10
  142. package/fjs/mcp/evo/module.f.mjs +48 -27
  143. package/fjs/mcp/evo/proof.f.d.mts +6 -1
  144. package/fjs/mcp/evo/proof.f.mjs +115 -31
  145. package/fjs/mcp/module.f.d.mts +4 -4
  146. package/fjs/mcp/module.f.mjs +6 -6
  147. package/fjs/mcp/proof.f.d.mts +5 -3
  148. package/fjs/mcp/proof.f.mjs +112 -46
  149. package/fjs/media/html/module.f.mjs +1 -1
  150. package/fjs/media/json/extended/module.f.d.mts +82 -0
  151. package/fjs/media/json/extended/module.f.mjs +153 -0
  152. package/fjs/media/json/extended/proof.f.d.mts +42 -0
  153. package/fjs/media/json/extended/proof.f.mjs +127 -0
  154. package/fjs/media/json/extended/types.d.ts +23 -0
  155. package/fjs/media/json/module.f.d.mts +8 -2
  156. package/fjs/media/json/module.f.mjs +43 -41
  157. package/fjs/media/json/number/module.f.d.mts +59 -0
  158. package/fjs/media/json/number/module.f.mjs +136 -0
  159. package/fjs/media/json/number/proof.f.d.mts +24 -0
  160. package/fjs/media/json/number/proof.f.mjs +86 -0
  161. package/fjs/media/json/number/types.d.ts +28 -0
  162. package/fjs/media/json/parser/module.f.d.mts +25 -13
  163. package/fjs/media/json/parser/module.f.mjs +114 -70
  164. package/fjs/media/json/parser/proof.f.d.mts +5 -0
  165. package/fjs/media/json/parser/proof.f.mjs +31 -1
  166. package/fjs/media/json/parser/types.d.ts +33 -14
  167. package/fjs/media/json/rtti/module.f.d.mts +1 -1
  168. package/fjs/media/json/rtti/module.f.mjs +1 -1
  169. package/fjs/media/json/rtti/proof.f.mjs +9 -9
  170. package/fjs/media/json/schema/module.f.mjs +3 -13
  171. package/fjs/media/json/schema/proof.f.d.mts +0 -1
  172. package/fjs/media/json/schema/proof.f.mjs +1 -2
  173. package/fjs/media/json/serializer/module.f.d.mts +32 -1
  174. package/fjs/media/json/serializer/module.f.mjs +64 -2
  175. package/fjs/media/json/tokenizer/module.f.mjs +7 -3
  176. package/fjs/media/json/tokenizer/proof.f.d.mts +6 -0
  177. package/fjs/media/json/tokenizer/proof.f.mjs +62 -21
  178. package/fjs/media/json/types.d.ts +36 -10
  179. package/fjs/media/lock/module.f.d.mts +100 -0
  180. package/fjs/media/lock/module.f.mjs +125 -0
  181. package/fjs/media/lock/proof.f.d.mts +33 -0
  182. package/fjs/media/lock/proof.f.mjs +196 -0
  183. package/fjs/media/lock/types.d.ts +15 -0
  184. package/fjs/media/module.f.d.mts +6 -5
  185. package/fjs/media/module.f.mjs +8 -7
  186. package/fjs/media/note/module.f.d.mts +121 -0
  187. package/fjs/media/note/module.f.mjs +131 -0
  188. package/fjs/media/note/proof.f.d.mts +29 -0
  189. package/fjs/media/note/proof.f.mjs +150 -0
  190. package/fjs/media/note/types.d.ts +10 -0
  191. package/fjs/media/proof.f.d.mts +4 -1
  192. package/fjs/media/proof.f.mjs +40 -21
  193. package/fjs/media/revision/module.f.d.mts +78 -7
  194. package/fjs/media/revision/module.f.mjs +119 -12
  195. package/fjs/media/revision/proof.f.d.mts +10 -0
  196. package/fjs/media/revision/proof.f.mjs +88 -0
  197. package/fjs/media/revision/types.d.ts +34 -5
  198. package/fjs/media/type/module.f.d.mts +33 -15
  199. package/fjs/media/type/module.f.mjs +35 -29
  200. package/fjs/media/type/proof.f.d.mts +2 -1
  201. package/fjs/media/type/proof.f.mjs +30 -10
  202. package/fjs/module.f.mjs +29 -8
  203. package/fjs/nanvm/proof.f.mjs +3 -3
  204. package/fjs/nanvm/rust/module.f.mjs +1 -1
  205. package/fjs/nanvm/update/module.f.d.mts +4 -4
  206. package/fjs/nanvm/update/module.f.mjs +8 -9
  207. package/fjs/nanvm/update/proof.f.mjs +4 -3
  208. package/fjs/proof.f.d.mts +3 -3
  209. package/fjs/proof.f.mjs +36 -10
  210. package/fjs/protocol/json_rpc/module.f.d.mts +2 -2
  211. package/fjs/protocol/json_rpc/module.f.mjs +3 -3
  212. package/fjs/protocol/json_rpc/proof.f.mjs +4 -4
  213. package/fjs/protocol/mcp/module.f.d.mts +10 -13
  214. package/fjs/protocol/mcp/module.f.mjs +74 -58
  215. package/fjs/protocol/mcp/proof.f.d.mts +13 -4
  216. package/fjs/protocol/mcp/proof.f.mjs +193 -61
  217. package/fjs/protocol/mcp/stdio/module.f.d.mts +15 -7
  218. package/fjs/protocol/mcp/stdio/module.f.mjs +38 -25
  219. package/fjs/protocol/mcp/stdio/proof.f.d.mts +4 -2
  220. package/fjs/protocol/mcp/stdio/proof.f.mjs +45 -12
  221. package/fjs/protocol/mcp/stdio/types.d.ts +1 -1
  222. package/fjs/protocol/mcp/types.d.ts +17 -6
  223. package/fjs/sul/id/module.f.d.mts +0 -1
  224. package/fjs/sul/id/module.f.mjs +2 -3
  225. package/fjs/sul/level/hash/module.f.mjs +2 -1
  226. package/fjs/sul/level/hash/proof.f.mjs +2 -2
  227. package/fjs/sul/module.f.mjs +18 -13
  228. package/fjs/text/code_point/module.f.d.mts +8 -0
  229. package/fjs/text/code_point/module.f.mjs +8 -1
  230. package/fjs/text/code_point/proof.f.d.mts +1 -0
  231. package/fjs/text/code_point/proof.f.mjs +11 -0
  232. package/fjs/text/sgr/module.f.d.mts +4 -6
  233. package/fjs/text/sgr/module.f.mjs +6 -7
  234. package/fjs/text/utf16/module.f.mjs +7 -2
  235. package/fjs/text/utf16/proof.f.mjs +3 -3
  236. package/fjs/text/utf8/module.f.mjs +4 -2
  237. package/fjs/types/array/module.f.mjs +14 -2
  238. package/fjs/types/bit_vec/module.f.d.mts +0 -2
  239. package/fjs/types/bit_vec/module.f.mjs +46 -32
  240. package/fjs/types/bit_vec/proof.f.mjs +2 -2
  241. package/fjs/types/btree/remove/module.f.mjs +1 -1
  242. package/fjs/types/btree/set/module.f.mjs +9 -11
  243. package/fjs/types/btree/set/proof.f.mjs +12 -0
  244. package/fjs/types/byte_set/module.f.d.mts +11 -4
  245. package/fjs/types/byte_set/module.f.mjs +14 -6
  246. package/fjs/types/byte_set/proof.f.mjs +7 -7
  247. package/fjs/types/function/compare/module.f.mjs +10 -3
  248. package/fjs/types/list/module.f.d.mts +1 -1
  249. package/fjs/types/list/module.f.mjs +1 -1
  250. package/fjs/types/nullable/module.f.d.mts +18 -4
  251. package/fjs/types/nullable/module.f.mjs +21 -4
  252. package/fjs/types/nullable/proof.f.d.mts +4 -0
  253. package/fjs/types/nullable/proof.f.mjs +15 -0
  254. package/fjs/types/object/module.f.d.mts +12 -2
  255. package/fjs/types/object/module.f.mjs +11 -1
  256. package/fjs/types/patricia_trie/module.f.mjs +26 -11
  257. package/fjs/types/result/module.f.d.mts +3 -3
  258. package/fjs/types/result/module.f.mjs +3 -3
  259. package/fjs/types/rtti/common/module.f.d.mts +25 -28
  260. package/fjs/types/rtti/common/module.f.mjs +36 -32
  261. package/fjs/types/rtti/common/proof.f.mjs +5 -5
  262. package/fjs/types/rtti/data/module.f.d.mts +14 -0
  263. package/fjs/types/rtti/data/module.f.mjs +25 -2
  264. package/fjs/types/rtti/data/proof.f.d.mts +1 -0
  265. package/fjs/types/rtti/data/proof.f.mjs +25 -2
  266. package/fjs/types/rtti/parse/module.f.d.mts +24 -14
  267. package/fjs/types/rtti/parse/module.f.mjs +37 -28
  268. package/fjs/types/rtti/parse/proof.f.d.mts +3 -2
  269. package/fjs/types/rtti/parse/proof.f.mjs +33 -14
  270. package/fjs/types/rtti/proof.f.mjs +3 -1
  271. package/fjs/types/rtti/ts/module.f.mjs +13 -17
  272. package/fjs/types/rtti/ts/types.d.ts +14 -1
  273. package/fjs/types/sorted_set/module.f.d.mts +18 -0
  274. package/fjs/types/sorted_set/module.f.mjs +22 -0
  275. package/fjs/types/sorted_set/proof.f.d.mts +1 -0
  276. package/fjs/types/sorted_set/proof.f.mjs +16 -1
  277. package/fjs/types/uint8array/module.f.d.mts +1 -1
  278. package/fjs/types/uint8array/module.f.mjs +1 -1
  279. package/fjs/website/module.f.d.mts +3 -3
  280. package/fjs/website/module.f.mjs +4 -7
  281. package/fjs/website/proof.f.mjs +2 -1
  282. package/package.json +2 -2
  283. package/fjs/dev/package_json/module.f.d.mts +0 -39
  284. package/fjs/dev/package_json/module.f.mjs +0 -40
  285. package/fjs/dev/package_json/proof.f.d.mts +0 -6
  286. package/fjs/dev/package_json/proof.f.mjs +0 -32
  287. package/fjs/effects/eff/module.f.d.mts +0 -20
  288. package/fjs/effects/eff/module.f.mjs +0 -70
  289. package/fjs/effects/eff/proof.f.d.mts +0 -15
  290. package/fjs/effects/eff/proof.f.mjs +0 -69
  291. package/fjs/effects/eff/types.d.ts +0 -71
  292. package/fjs/types/rtti/validate/module.f.d.mts +0 -41
  293. package/fjs/types/rtti/validate/module.f.mjs +0 -171
  294. package/fjs/types/rtti/validate/proof.f.d.mts +0 -125
  295. package/fjs/types/rtti/validate/proof.f.mjs +0 -437
  296. package/fjs/types/rtti/validate/types.d.ts +0 -6
@@ -1,55 +1,230 @@
1
1
  /**
2
- * @import { Vec } from '../types/bit_vec/types.ts'
2
+ * @import { Unknown } from './types.ts'
3
3
  */
4
4
 
5
+ import { exitCode } from '../effects/node/module.f.mjs'
5
6
  import { compile } from './module.f.mjs'
7
+ import { transpile } from './transpiler/module.f.mjs'
8
+ import { stringify } from './serializer/module.f.mjs'
6
9
  import { virtual, emptyState } from '../effects/node/virtual/module.f.mjs'
7
10
  import { utf8, utf8ToString } from '../text/module.f.mjs'
8
- import { assert, assertEq } from '../asserts/module.f.mjs'
11
+ import { fromEntries, isObject, sort } from '../types/object/module.f.mjs'
12
+ import { assert, assertEq, assertStructurallySame } from '../asserts/module.f.mjs'
9
13
 
10
14
  /** @type {(root: typeof emptyState.root, path: string) => string} */
11
15
  const readOutput = (root, path) => {
12
16
  const file = root[path]
13
17
  if (!Array.isArray(file) || file.length === 0) { throw `${path} is not a file` }
14
- return utf8ToString(/** @type {readonly Vec[]} */ (file)[0])
18
+ return utf8ToString(file[0])
15
19
  }
16
20
 
21
+ /** @type {(source: string) => (outputFileName: string) => string} */
22
+ const compileSource = source => outputFileName => {
23
+ const root = { 'input.f.js': [utf8(source)] }
24
+ const [state, code] = virtual({ ...emptyState, root })(compile(['input.f.js', outputFileName]))
25
+ assertEq(exitCode(code), 0, state.stderr)
26
+ return readOutput(state.root, outputFileName)
27
+ }
28
+
29
+ const { getPrototypeOf, prototype: objectPrototype } = Object
30
+
31
+ /** The value every `protoKey` test below denotes. */
32
+ const protoValue = fromEntries([['__proto__', { a: 42 }]])
33
+
34
+ const sharedArray = [1, 2]
35
+
36
+ /**
37
+ * Values the module emitter must be able to write as source that evaluates
38
+ * back to them. It covers every leaf type, both containers, the shared values
39
+ * that become a `const`, and the `__proto__` key in both positions — the key
40
+ * whose obvious spelling evaluates to something else entirely.
41
+ *
42
+ * @type {readonly Unknown[]}
43
+ */
44
+ const roundTripCorpus = [
45
+ null,
46
+ true,
47
+ false,
48
+ undefined,
49
+ 0,
50
+ -1.5,
51
+ 42n,
52
+ 'a"b\n\\',
53
+ [],
54
+ {},
55
+ [1, [2, [3, []]]],
56
+ { a: 1, 'b c': [true, undefined, 3n], d: {} },
57
+ [sharedArray, sharedArray],
58
+ { a: 'dup', b: 'dup' },
59
+ protoValue,
60
+ fromEntries([['__proto__', 3]]),
61
+ [protoValue, protoValue],
62
+ { a: protoValue },
63
+ ]
64
+
17
65
  export const proof = {
18
66
  tooFewArgs: {
19
67
  noArgs: () => {
20
68
  const [state, code] = virtual(emptyState)(compile([]))
21
- assertEq(code, 1)
69
+ assertEq(exitCode(code), 1)
22
70
  assert(state.stderr.includes('Requires 2 or more arguments'), state.stderr)
23
71
  },
24
72
  oneArg: () => {
25
73
  const [state, code] = virtual(emptyState)(compile(['input.f.js']))
26
- assertEq(code, 1)
74
+ assertEq(exitCode(code), 1)
27
75
  assert(state.stderr.includes('Requires 2 or more arguments'), state.stderr)
28
76
  },
29
77
  },
30
78
  success: () => {
31
79
  const root = { 'input.f.js': [utf8('export default 42')] }
32
80
  const [state, code] = virtual({ ...emptyState, root })(compile(['input.f.js', 'output.f.js']))
33
- assertEq(code, 0)
81
+ assertEq(exitCode(code), 0)
34
82
  const content = readOutput(state.root, 'output.f.js')
35
83
  assertEq(content, 'export default 42')
36
84
  },
37
85
  jsonOutput: () => {
38
86
  const root = { 'input.f.js': [utf8('export default 42')] }
39
87
  const [state, code] = virtual({ ...emptyState, root })(compile(['input.f.js', 'output.json']))
40
- assertEq(code, 0)
88
+ assertEq(exitCode(code), 0)
41
89
  const content = readOutput(state.root, 'output.json')
42
90
  assertEq(content, '42')
43
91
  },
44
- fileNotFound: () => {
45
- const [state, code] = virtual(emptyState)(compile(['missing.f.js', 'output.f.js']))
46
- assertEq(code, 0)
47
- assert(state.stderr.includes('file not found'), state.stderr)
92
+ // An error with no token to point at names the file being compiled, not
93
+ // `undefined:undefined:undefined`. Each language reports its own missing
94
+ // file: the module reader and the JSON reader read their inputs
95
+ // separately.
96
+ fileNotFound: {
97
+ module: () => {
98
+ const [state, code] = virtual(emptyState)(compile(['missing.f.js', 'output.f.js']))
99
+ assertEq(exitCode(code), 1)
100
+ assertEq(state.stderr.trim(), 'missing.f.js - error: file not found')
101
+ assertEq(state.root['output.f.js'], undefined)
102
+ },
103
+ json: () => {
104
+ const [state, code] = virtual(emptyState)(compile(['missing.json', 'output.f.js']))
105
+ assertEq(exitCode(code), 1)
106
+ assertEq(state.stderr.trim(), 'missing.json - error: file not found')
107
+ assertEq(state.root['output.f.js'], undefined)
108
+ },
48
109
  },
49
110
  parseError: () => {
50
111
  const root = { 'bad.f.js': [utf8('export default @')] }
51
112
  const [state, code] = virtual({ ...emptyState, root })(compile(['bad.f.js', 'output.f.js']))
52
- assertEq(code, 0)
113
+ assertEq(exitCode(code), 1)
53
114
  assert(state.stderr !== '', 'expected error output')
115
+ assertEq(state.root['output.f.js'], undefined)
116
+ },
117
+ // serialize → evaluate → structurally the same, one test per corpus value.
118
+ // The emitter is only correct if its output is an input denoting the value
119
+ // it was given, which no assertion on the text alone can state.
120
+ roundTrip: roundTripCorpus.map(value => () => {
121
+ const source = stringify(sort)(value)
122
+ const root = { 'input.f.js': [utf8(source)] }
123
+ const [, result] = virtual({ ...emptyState, root })(transpile('input.f.js'))
124
+ assert(result[0] === 'ok', result[1])
125
+ assertStructurallySame(result[1], value, source)
126
+ }),
127
+ // The `__proto__` key end to end: one value, two output languages, and one
128
+ // spelling of the key in each (#2480).
129
+ protoKey: {
130
+ // The module output uses the computed form, which is also the only
131
+ // input spelling — so the emitter's output is an input that means the
132
+ // same value, and compiling it again is the identity.
133
+ moduleRoundTrip: () => {
134
+ const source = 'export default {["__proto__"]:{"a":42}}'
135
+ const output = compileSource(source)('output.f.js')
136
+ assertEq(output, source)
137
+ assertEq(compileSource(output)('output.f.js'), source)
138
+ },
139
+ // The JSON output keeps the plain key: the computed form is a
140
+ // JavaScript spelling that no JSON parser accepts.
141
+ jsonOutput: () => {
142
+ assertEq(
143
+ compileSource('export default {["__proto__"]:{"a":42}}')('output.json'),
144
+ '{"__proto__":{"a":42}}')
145
+ },
146
+ // `fjs compile proto.json a.js` — the two languages meeting. The input
147
+ // is a JSON document, where `"__proto__"` is an ordinary data key, and
148
+ // the output is a JavaScript module, where only the computed form
149
+ // denotes one. Each hop uses its own language's spelling of the key.
150
+ jsonInput: () => {
151
+ const root = { 'proto.json': [utf8('{"__proto__":5}')] }
152
+ const [state, code] = virtual({ ...emptyState, root })(compile(['proto.json', 'a.js']))
153
+ assertEq(exitCode(code), 0, state.stderr)
154
+ assertEq(readOutput(state.root, 'a.js'), 'export default {["__proto__"]:5}')
155
+ },
156
+ // …and back, byte for byte: a JSON document survives the loop
157
+ // `proto.json → a.js → out.json` with no `["__proto__"]:` artifact,
158
+ // which no JSON parser would accept.
159
+ jsonInputRoundTrip: () => {
160
+ const document = '{"__proto__":{"a":42}}'
161
+ const root = { 'proto.json': [utf8(document)] }
162
+ const [state, code] = virtual({ ...emptyState, root })(compile(['proto.json', 'a.js']))
163
+ assertEq(exitCode(code), 0, state.stderr)
164
+ const module = readOutput(state.root, 'a.js')
165
+ assertEq(module, 'export default {["__proto__"]:{"a":42}}')
166
+ assertEq(compileSource(module)('out.json'), document)
167
+ },
168
+ // The extension speaks for the file named on the command line and for
169
+ // no other: an import is resolved as a FunctionalScript module, and a
170
+ // JSON document is not one — a statement never begins with a value —
171
+ // so importing JSON fails until an import can say
172
+ // `with { type: "json" }` (spec/todo/2140).
173
+ jsonImportRejected: () => {
174
+ const root = {
175
+ 'main.f.js': [utf8('import a from "./a.json"\nexport default [a]')],
176
+ 'a.json': [utf8('{"a":42}')],
177
+ }
178
+ const [state, code] = virtual({ ...emptyState, root })(compile(['main.f.js', 'out.json']))
179
+ assertEq(exitCode(code), 1)
180
+ assert(state.stderr.includes('a.json:1:1 - error: unexpected token'), state.stderr)
181
+ assertEq(state.root['out.json'], undefined)
182
+ },
183
+ // A `.json` input is read as JSON, and an identifier key is no JSON
184
+ // document's key — so this one fails in the JSON reader, which has no
185
+ // position to report and is named by its file instead.
186
+ jsonInputIdKeyRejected: () => {
187
+ const root = { 'proto.json': [utf8('{__proto__:5}')] }
188
+ const [state, code] = virtual({ ...emptyState, root })(compile(['proto.json', 'a.js']))
189
+ assertEq(exitCode(code), 1)
190
+ assertEq(state.stderr.trim(), 'proto.json - error: unexpected token')
191
+ assertEq(state.root['a.js'], undefined)
192
+ },
193
+ // The `.json` reader is JSON, not DJS with a JSON flag: a bigint is
194
+ // not JSON, whatever DJS makes of it.
195
+ jsonInputRejectsDjsExtensions: () => {
196
+ const root = { 'a.json': [utf8('{"a":1n}')] }
197
+ const [state, code] = virtual({ ...emptyState, root })(compile(['a.json', 'a.js']))
198
+ assertEq(exitCode(code), 1)
199
+ assertEq(state.root['a.js'], undefined)
200
+ },
201
+ // The statement behind the textual assertions: the property is an
202
+ // ordinary own property and the prototype is untouched. A textual test
203
+ // alone would also pass for a spelling that merely looks right.
204
+ value: () => {
205
+ const root = { 'input.f.js': [utf8('export default {["__proto__"]:{"a":42}}')] }
206
+ const [, result] = virtual({ ...emptyState, root })(transpile('input.f.js'))
207
+ assert(result[0] === 'ok', result[1])
208
+ const value = result[1]
209
+ assert(isObject(value), value)
210
+ assertStructurallySame(value, protoValue)
211
+ assertEq(getPrototypeOf(value), objectPrototype)
212
+ },
213
+ // The two spellings JavaScript reads as a prototype assignment are
214
+ // compilation errors, not silently accepted properties.
215
+ idKeyRejected: () => {
216
+ const root = { 'input.f.js': [utf8('export default {__proto__:{"a":42}}')] }
217
+ const [state, code] = virtual({ ...emptyState, root })(compile(['input.f.js', 'output.f.js']))
218
+ assertEq(exitCode(code), 1)
219
+ assert(state.stderr.includes('__proto__ requires the computed key form'), state.stderr)
220
+ assertEq(state.root['output.f.js'], undefined)
221
+ },
222
+ stringKeyRejected: () => {
223
+ const root = { 'input.f.js': [utf8('export default {"__proto__":{"a":42}}')] }
224
+ const [state, code] = virtual({ ...emptyState, root })(compile(['input.f.js', 'output.f.js']))
225
+ assertEq(exitCode(code), 1)
226
+ assert(state.stderr.includes('__proto__ requires the computed key form'), state.stderr)
227
+ assertEq(state.root['output.f.js'], undefined)
228
+ },
54
229
  },
55
230
  }
@@ -1,30 +1,47 @@
1
1
  /**
2
2
  * DJS serializer for formatting AST values back to source text.
3
3
  *
4
+ * Two output formats share this one walk over the value: `stringify` emits a
5
+ * JavaScript module and `stringifyAsTree` a JSON tree. They differ in `const`
6
+ * hoisting and in how a property key is spelled — see `_KeySerialize`.
7
+ *
4
8
  * @module
5
9
  *
6
- * @import { Unknown, Object } from '../types.ts'
10
+ * @import { Unknown, Object, _MapEntries } from '../types.ts'
7
11
  * @import { Fold } from '../../types/function/operator/types.ts'
8
- * @import { Entry as ObjectEntry } from '../../types/object/types.ts'
9
12
  * @import { List } from '../../types/list/types.ts'
10
13
  */
11
- import type { Unknown } from '../types.ts';
12
- import type { Entry as ObjectEntry } from '../../types/object/types.ts';
14
+ import type { Unknown, _MapEntries } from '../types.ts';
13
15
  import type { List } from '../../types/list/types.ts';
14
16
  export declare const undefinedSerialize: string[];
15
17
  export type _RefCounter = readonly [number, number];
16
- export type _MapEntries = (entries: List<ObjectEntry<Unknown>>) => List<ObjectEntry<Unknown>>;
17
18
  export type _Refs = ReadonlyMap<Unknown, _RefCounter>;
18
19
  export type _GetConstsState = {
19
20
  readonly added: ReadonlySet<Unknown>;
20
21
  readonly consts: List<Unknown>;
21
22
  };
22
23
  export type _RefLookup = (value: Unknown) => List<string> | null;
23
- /** @type {(mapEntries: _MapEntries) => (value: Unknown) => List<string>} */
24
+ export type _KeySerialize = (key: string) => List<string>;
25
+ /**
26
+ * Serializes a value as a JSON tree — no `const` hoisting, and JSON's key
27
+ * spelling.
28
+ *
29
+ * @type {(mapEntries: _MapEntries) => (value: Unknown) => List<string>}
30
+ */
24
31
  export declare const serializeWithoutConst: (mapEntries: _MapEntries) => (value: Unknown) => List<string>;
25
- /** @type {(sort: _MapEntries) => (djs: Unknown) => string} */
32
+ /**
33
+ * Serializes a value as a JavaScript module: a shared value becomes a `const`,
34
+ * and a `__proto__` key is written in the computed form the language requires.
35
+ *
36
+ * @type {(sort: _MapEntries) => (djs: Unknown) => string}
37
+ */
26
38
  export declare const stringify: (sort: _MapEntries) => (djs: Unknown) => string;
27
- /** @type {(mapEntries: _MapEntries) => (value: Unknown) => string} */
39
+ /**
40
+ * Serializes a value as a JSON tree: shared values are expanded, and keys keep
41
+ * JSON's plain spelling.
42
+ *
43
+ * @type {(mapEntries: _MapEntries) => (value: Unknown) => string}
44
+ */
28
45
  export declare const stringifyAsTree: (mapEntries: _MapEntries) => (value: Unknown) => string;
29
46
  /** @type {(djs: Unknown) => _Refs} */
30
47
  export declare const countRefs: (djs: Unknown) => _Refs;
@@ -1,11 +1,14 @@
1
1
  /**
2
2
  * DJS serializer for formatting AST values back to source text.
3
3
  *
4
+ * Two output formats share this one walk over the value: `stringify` emits a
5
+ * JavaScript module and `stringifyAsTree` a JSON tree. They differ in `const`
6
+ * hoisting and in how a property key is spelled — see `_KeySerialize`.
7
+ *
4
8
  * @module
5
9
  *
6
- * @import { Unknown, Object } from '../types.ts'
10
+ * @import { Unknown, Object, _MapEntries } from '../types.ts'
7
11
  * @import { Fold } from '../../types/function/operator/types.ts'
8
- * @import { Entry as ObjectEntry } from '../../types/object/types.ts'
9
12
  * @import { List } from '../../types/list/types.ts'
10
13
  */
11
14
 
@@ -15,17 +18,13 @@ import { flat, flatMap, map, concat as listConcat } from '../../types/list/modul
15
18
  const { entries } = Object
16
19
  import { compose, fn } from '../../types/function/module.f.mjs'
17
20
  import { serialize as bigintSerialize } from '../../types/bigint/module.f.mjs'
18
- import { objectWrap, arrayWrap, stringSerialize, numberSerialize, nullSerialize, boolSerialize } from '../../media/json/serializer/module.f.mjs'
21
+ import { objectWrap, arrayWrap, colon, stringSerialize, numberSerialize, nullSerialize, boolSerialize } from '../../media/json/serializer/module.f.mjs'
19
22
  import { assertNotNullish } from '../../asserts/module.f.mjs'
20
23
 
21
- const colon = [':']
22
-
23
24
  export const undefinedSerialize = ['undefined']
24
25
 
25
26
  /** @typedef {readonly [number, number]} _RefCounter */
26
27
 
27
- /** @typedef {(entries: List<ObjectEntry<Unknown>>) => List<ObjectEntry<Unknown>>} _MapEntries */
28
-
29
28
  /** @typedef {ReadonlyMap<Unknown, _RefCounter>} _Refs */
30
29
 
31
30
  /**
@@ -93,11 +92,43 @@ const entryValue = kv => kv[1]
93
92
  /** @type {_RefLookup} */
94
93
  const noRef = () => null
95
94
 
96
- /** @type {(refLookup: _RefLookup) => (sort: _MapEntries) => (value: Unknown) => List<string>} */
97
- const buildSerialize = refLookup => sort => {
95
+ /**
96
+ * How one output format spells a property key. The two formats disagree about
97
+ * exactly one key, `__proto__`, so the spelling is a parameter of
98
+ * `buildSerialize` rather than a property of the shared JSON helper.
99
+ *
100
+ * @typedef {(key: string) => List<string>} _KeySerialize
101
+ */
102
+
103
+ const protoKey = '__proto__'
104
+
105
+ /**
106
+ * JSON spells every key as a quoted string. `"__proto__"` included: `JSON.parse`
107
+ * has no prototype special case, so the plain spelling already round-trips, and
108
+ * the computed form below is not JSON at all.
109
+ *
110
+ * @type {_KeySerialize}
111
+ */
112
+ const jsonKeySerialize = stringSerialize
113
+
114
+ /**
115
+ * JavaScript reads `{"__proto__": v}` as a prototype assignment, so a module
116
+ * emitting that spelling would not read back the value it was given. The
117
+ * computed form is the only spelling whose evaluation reproduces the property,
118
+ * which makes it a requirement of round-tripping rather than a style choice.
119
+ * See [spec: the `__proto__` key](../../../spec/README.md#the-__proto__-key).
120
+ *
121
+ * @type {_KeySerialize}
122
+ */
123
+ const jsKeySerialize = key => key === protoKey
124
+ ? flat([['['], stringSerialize(key), [']']])
125
+ : stringSerialize(key)
126
+
127
+ /** @type {(keySerialize: _KeySerialize) => (refLookup: _RefLookup) => (sort: _MapEntries) => (value: Unknown) => List<string>} */
128
+ const buildSerialize = keySerialize => refLookup => sort => {
98
129
  /** @type {(kv: readonly [string, Unknown]) => List<string>} */
99
130
  const propertySerialize = ([k, v]) => flat([
100
- stringSerialize(k),
131
+ keySerialize(k),
101
132
  colon,
102
133
  f(v)
103
134
  ])
@@ -129,13 +160,18 @@ const buildSerialize = refLookup => sort => {
129
160
  return f
130
161
  }
131
162
 
132
- /** @type {(mapEntries: _MapEntries) => (value: Unknown) => List<string>} */
133
- export const serializeWithoutConst = buildSerialize(noRef)
163
+ /**
164
+ * Serializes a value as a JSON tree — no `const` hoisting, and JSON's key
165
+ * spelling.
166
+ *
167
+ * @type {(mapEntries: _MapEntries) => (value: Unknown) => List<string>}
168
+ */
169
+ export const serializeWithoutConst = buildSerialize(jsonKeySerialize)(noRef)
134
170
 
135
171
  /** @type {(sort: _MapEntries) => (refs: _Refs) => (root: Unknown) => (djs: Unknown) => List<string>} */
136
172
  const serializeWithConst = sort => refs => {
137
173
  const shared = sharedRef(refs)
138
- return root => buildSerialize(value => {
174
+ return root => buildSerialize(jsKeySerialize)(value => {
139
175
  if (value === root) { return null }
140
176
  const rc = shared(value)
141
177
  if (rc !== undefined) { return [`c${rc[0]}`] }
@@ -180,7 +216,12 @@ const addRef = djs => refs => {
180
216
  return new Map([...refs, [djs, newCounter]])
181
217
  }
182
218
 
183
- /** @type {(sort: _MapEntries) => (djs: Unknown) => string} */
219
+ /**
220
+ * Serializes a value as a JavaScript module: a shared value becomes a `const`,
221
+ * and a `__proto__` key is written in the computed form the language requires.
222
+ *
223
+ * @type {(sort: _MapEntries) => (djs: Unknown) => string}
224
+ */
184
225
  export const stringify = sort => djs => {
185
226
  const refs = countRefs(djs)
186
227
  const consts = getConstants(refs)(djs)
@@ -197,7 +238,12 @@ export const stringify = sort => djs => {
197
238
  return concat(listConcat(constStrings)(rootStrings))
198
239
  }
199
240
 
200
- /** @type {(mapEntries: _MapEntries) => (value: Unknown) => string} */
241
+ /**
242
+ * Serializes a value as a JSON tree: shared values are expanded, and keys keep
243
+ * JSON's plain spelling.
244
+ *
245
+ * @type {(mapEntries: _MapEntries) => (value: Unknown) => string}
246
+ */
201
247
  export const stringifyAsTree = sort => compose(serializeWithoutConst(sort))(concat)
202
248
 
203
249
 
@@ -12,6 +12,11 @@ export declare const proof: {
12
12
  testSort: () => void;
13
13
  testIdentity: () => void;
14
14
  })[];
15
+ protoKey: {
16
+ module: () => void;
17
+ moduleShared: () => void;
18
+ json: () => void;
19
+ };
15
20
  stringifyAsTree: ({
16
21
  sort: () => void;
17
22
  identity: () => void;
@@ -1,5 +1,5 @@
1
1
  import { countRefs, stringify, stringifyAsTree } from './module.f.mjs'
2
- import { sort } from '../../types/object/module.f.mjs'
2
+ import { fromEntries, sort } from '../../types/object/module.f.mjs'
3
3
  import { identity } from '../../types/function/module.f.mjs'
4
4
  import { setProperty } from '../../media/json/module.f.mjs'
5
5
  import { assertEq } from '../../asserts/module.f.mjs'
@@ -55,6 +55,28 @@ export const proof = {
55
55
  },
56
56
  }
57
57
  ],
58
+ // The one key the two output formats spell differently. A `__proto__`
59
+ // property is built with `fromEntries` because an object literal cannot
60
+ // hold one: `{__proto__: 3}` assigns a prototype instead (#2480).
61
+ protoKey: {
62
+ // JavaScript output: only the computed form evaluates back to the
63
+ // property, so the module emitter must use it.
64
+ module: () => {
65
+ const res = stringify(sort)(fromEntries([['__proto__', 3]]))
66
+ assertEq(res, 'export default {["__proto__"]:3}')
67
+ },
68
+ moduleShared: () => {
69
+ const shared = fromEntries([['__proto__', 3]])
70
+ const res = stringify(sort)([shared, shared])
71
+ assertEq(res, 'const c0 = {["__proto__"]:3}\nexport default [c0,c0]')
72
+ },
73
+ // JSON output: `JSON.parse` has no prototype special case, so the plain
74
+ // spelling already round-trips — and the computed form is not JSON.
75
+ json: () => {
76
+ const res = stringifyAsTree(sort)(fromEntries([['__proto__', 3]]))
77
+ assertEq(res, '{"__proto__":3}')
78
+ },
79
+ },
58
80
  stringifyAsTree: [
59
81
  {
60
82
  sort: () => {
@@ -3,13 +3,11 @@
3
3
  *
4
4
  * @module
5
5
  *
6
+ * @import { Ast, AstSequence, AstTag } from '../../bnf/matcher/types.ts'
6
7
  * @import {
7
- * AstRuleMeta,
8
- * AstSequenceMeta,
9
- * AstTag,
10
8
  * CodePointMeta,
11
9
  * DescentMatch,
12
- * DescentMatchResult
10
+ * DescentMatchResult,
13
11
  * } from '../../bnf/descent/types.ts'
14
12
  * @import { DataRule, Rule } from '../../bnf/types.ts'
15
13
  * @import {
@@ -29,8 +27,9 @@
29
27
  * @import { CodePoint } from '../../text/utf16/types.ts'
30
28
  * @import { StateScan } from '../../types/function/operator/types.ts'
31
29
  * @import { List } from '../../types/list/types.ts'
32
- * @import { Unknown } from '../types.ts'
33
30
  * @import { DjsToken, DjsTokenWithMetadata } from './types.ts'
31
+ * @import { TriviaKind } from '../../js/tokenizer/types.ts'
32
+ * @import { Nullable } from '../../types/nullable/types.ts'
34
33
  */
35
34
  import type { CodePointMeta, DescentMatch, DescentMatchResult } from '../../bnf/descent/types.ts';
36
35
  import type { Rule } from '../../bnf/types.ts';
@@ -40,6 +39,16 @@ import type { List } from '../../types/list/types.ts';
40
39
  import type { DjsTokenWithMetadata } from './types.ts';
41
40
  /** @type {() => Rule} */
42
41
  export declare const jsGrammar: () => Rule;
42
+ /**
43
+ * The whole-file matcher together with the name of the rule to start it at.
44
+ *
45
+ * `toData` generates rule names, so the entry name belongs to the conversion
46
+ * rather than to the grammar's spelling and is read back from it here. Building
47
+ * both from one conversion also keeps the grammar built once per matcher.
48
+ *
49
+ * @type {<T>() => readonly [DescentMatch<T>, string]}
50
+ */
51
+ export declare const jsMatcher: <T>() => readonly [DescentMatch<T>, string];
43
52
  /** @type {(m: DescentMatch<unknown>, name: string, cp: readonly CodePoint[]) => DescentMatchResult<unknown>} */
44
53
  export declare const descentParserCpOnly: (m: DescentMatch<unknown>, name: string, cp: readonly CodePoint[]) => DescentMatchResult<unknown>;
45
54
  export type _Token = [string, TokenMetadata, readonly number[]];
@@ -63,3 +72,6 @@ export type _DjsScanState = {
63
72
  };
64
73
  /** @type {(input: List<number>) => (path: string) => List<DjsTokenWithMetadata>} */
65
74
  export declare const tokenize: (input: List<number>) => (path: string) => List<DjsTokenWithMetadata>;
75
+ export declare const proof: {
76
+ tagToTokenTrueTag: () => void;
77
+ };