openalgo-script 0.2.0 → 0.5.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 (357) hide show
  1. package/CHANGELOG.md +1198 -0
  2. package/README.md +127 -40
  3. package/dist/adapters/charts/driving.d.ts +50 -0
  4. package/dist/adapters/charts/driving.d.ts.map +1 -0
  5. package/dist/adapters/charts/driving.js +57 -0
  6. package/dist/adapters/charts/driving.js.map +1 -0
  7. package/dist/adapters/charts/run.d.ts +20 -0
  8. package/dist/adapters/charts/run.d.ts.map +1 -1
  9. package/dist/adapters/charts/run.js +83 -16
  10. package/dist/adapters/charts/run.js.map +1 -1
  11. package/dist/adapters/charts/surfaces.d.ts +17 -0
  12. package/dist/adapters/charts/surfaces.d.ts.map +1 -1
  13. package/dist/adapters/charts/tables.js +82 -0
  14. package/dist/adapters/charts/tables.js.map +1 -1
  15. package/dist/adapters/charts/venue.d.ts +73 -0
  16. package/dist/adapters/charts/venue.d.ts.map +1 -0
  17. package/dist/adapters/charts/venue.js +104 -0
  18. package/dist/adapters/charts/venue.js.map +1 -0
  19. package/dist/adapters/codemirror/commands.d.ts +14 -0
  20. package/dist/adapters/codemirror/commands.d.ts.map +1 -0
  21. package/dist/adapters/codemirror/commands.js +52 -0
  22. package/dist/adapters/codemirror/commands.js.map +1 -0
  23. package/dist/adapters/codemirror/completion.d.ts +15 -0
  24. package/dist/adapters/codemirror/completion.d.ts.map +1 -0
  25. package/dist/adapters/codemirror/completion.js +64 -0
  26. package/dist/adapters/codemirror/completion.js.map +1 -0
  27. package/dist/adapters/codemirror/contract.d.ts +156 -0
  28. package/dist/adapters/codemirror/contract.d.ts.map +1 -0
  29. package/dist/adapters/codemirror/contract.js +42 -0
  30. package/dist/adapters/codemirror/contract.js.map +1 -0
  31. package/dist/adapters/codemirror/index.d.ts +43 -0
  32. package/dist/adapters/codemirror/index.d.ts.map +1 -0
  33. package/dist/adapters/codemirror/index.js +8 -0
  34. package/dist/adapters/codemirror/index.js.map +1 -0
  35. package/dist/adapters/codemirror/lint.d.ts +17 -0
  36. package/dist/adapters/codemirror/lint.d.ts.map +1 -0
  37. package/dist/adapters/codemirror/lint.js +62 -0
  38. package/dist/adapters/codemirror/lint.js.map +1 -0
  39. package/dist/adapters/codemirror/positions.d.ts +16 -0
  40. package/dist/adapters/codemirror/positions.d.ts.map +1 -0
  41. package/dist/adapters/codemirror/positions.js +65 -0
  42. package/dist/adapters/codemirror/positions.js.map +1 -0
  43. package/dist/adapters/codemirror/stream.d.ts +23 -0
  44. package/dist/adapters/codemirror/stream.d.ts.map +1 -0
  45. package/dist/adapters/codemirror/stream.js +70 -0
  46. package/dist/adapters/codemirror/stream.js.map +1 -0
  47. package/dist/adapters/codemirror/tokens.d.ts +44 -0
  48. package/dist/adapters/codemirror/tokens.d.ts.map +1 -0
  49. package/dist/adapters/codemirror/tokens.js +34 -0
  50. package/dist/adapters/codemirror/tokens.js.map +1 -0
  51. package/dist/adapters/codemirror/tooltips.d.ts +35 -0
  52. package/dist/adapters/codemirror/tooltips.d.ts.map +1 -0
  53. package/dist/adapters/codemirror/tooltips.js +108 -0
  54. package/dist/adapters/codemirror/tooltips.js.map +1 -0
  55. package/dist/core/accounting/analysis.d.ts +111 -0
  56. package/dist/core/accounting/analysis.d.ts.map +1 -0
  57. package/dist/core/accounting/analysis.js +123 -0
  58. package/dist/core/accounting/analysis.js.map +1 -0
  59. package/dist/core/accounting/charges.d.ts +136 -0
  60. package/dist/core/accounting/charges.d.ts.map +1 -0
  61. package/dist/core/accounting/charges.js +362 -0
  62. package/dist/core/accounting/charges.js.map +1 -0
  63. package/dist/core/accounting/equity.d.ts +191 -0
  64. package/dist/core/accounting/equity.d.ts.map +1 -0
  65. package/dist/core/accounting/equity.js +167 -0
  66. package/dist/core/accounting/equity.js.map +1 -0
  67. package/dist/core/accounting/index.d.ts +46 -0
  68. package/dist/core/accounting/index.d.ts.map +1 -0
  69. package/dist/core/accounting/index.js +37 -0
  70. package/dist/core/accounting/index.js.map +1 -0
  71. package/dist/core/accounting/markers.d.ts +43 -0
  72. package/dist/core/accounting/markers.d.ts.map +1 -0
  73. package/dist/core/accounting/markers.js +64 -0
  74. package/dist/core/accounting/markers.js.map +1 -0
  75. package/dist/core/accounting/monthly.d.ts +52 -0
  76. package/dist/core/accounting/monthly.d.ts.map +1 -0
  77. package/dist/core/accounting/monthly.js +99 -0
  78. package/dist/core/accounting/monthly.js.map +1 -0
  79. package/dist/core/accounting/report.d.ts +41 -0
  80. package/dist/core/accounting/report.d.ts.map +1 -0
  81. package/dist/core/accounting/report.js +65 -0
  82. package/dist/core/accounting/report.js.map +1 -0
  83. package/dist/core/accounting/shapes.d.ts +70 -0
  84. package/dist/core/accounting/shapes.d.ts.map +1 -0
  85. package/dist/core/accounting/shapes.js +21 -0
  86. package/dist/core/accounting/shapes.js.map +1 -0
  87. package/dist/core/accounting/statistics.d.ts +87 -0
  88. package/dist/core/accounting/statistics.d.ts.map +1 -0
  89. package/dist/core/accounting/statistics.js +268 -0
  90. package/dist/core/accounting/statistics.js.map +1 -0
  91. package/dist/core/accounting/trades.d.ts +118 -0
  92. package/dist/core/accounting/trades.d.ts.map +1 -0
  93. package/dist/core/accounting/trades.js +186 -0
  94. package/dist/core/accounting/trades.js.map +1 -0
  95. package/dist/core/backtest/case.d.ts +60 -0
  96. package/dist/core/backtest/case.d.ts.map +1 -0
  97. package/dist/core/backtest/case.js +319 -0
  98. package/dist/core/backtest/case.js.map +1 -0
  99. package/dist/core/backtest/compare.d.ts +38 -0
  100. package/dist/core/backtest/compare.d.ts.map +1 -0
  101. package/dist/core/backtest/compare.js +191 -0
  102. package/dist/core/backtest/compare.js.map +1 -0
  103. package/dist/core/backtest/declaration.d.ts +52 -0
  104. package/dist/core/backtest/declaration.d.ts.map +1 -0
  105. package/dist/core/backtest/declaration.js +48 -0
  106. package/dist/core/backtest/declaration.js.map +1 -0
  107. package/dist/core/backtest/deliver.d.ts +93 -0
  108. package/dist/core/backtest/deliver.d.ts.map +1 -0
  109. package/dist/core/backtest/deliver.js +94 -0
  110. package/dist/core/backtest/deliver.js.map +1 -0
  111. package/dist/core/backtest/drive.d.ts +87 -0
  112. package/dist/core/backtest/drive.d.ts.map +1 -0
  113. package/dist/core/backtest/drive.js +312 -0
  114. package/dist/core/backtest/drive.js.map +1 -0
  115. package/dist/core/backtest/index.d.ts +58 -0
  116. package/dist/core/backtest/index.d.ts.map +1 -0
  117. package/dist/core/backtest/index.js +47 -0
  118. package/dist/core/backtest/index.js.map +1 -0
  119. package/dist/core/backtest/range.d.ts +84 -0
  120. package/dist/core/backtest/range.d.ts.map +1 -0
  121. package/dist/core/backtest/range.js +90 -0
  122. package/dist/core/backtest/range.js.map +1 -0
  123. package/dist/core/backtest/record.d.ts +299 -0
  124. package/dist/core/backtest/record.d.ts.map +1 -0
  125. package/dist/core/backtest/record.js +243 -0
  126. package/dist/core/backtest/record.js.map +1 -0
  127. package/dist/core/backtest/replay.d.ts +48 -0
  128. package/dist/core/backtest/replay.d.ts.map +1 -0
  129. package/dist/core/backtest/replay.js +137 -0
  130. package/dist/core/backtest/replay.js.map +1 -0
  131. package/dist/core/backtest/resting.d.ts +62 -0
  132. package/dist/core/backtest/resting.d.ts.map +1 -0
  133. package/dist/core/backtest/resting.js +59 -0
  134. package/dist/core/backtest/resting.js.map +1 -0
  135. package/dist/core/backtest/settings.d.ts +117 -0
  136. package/dist/core/backtest/settings.d.ts.map +1 -0
  137. package/dist/core/backtest/settings.js +207 -0
  138. package/dist/core/backtest/settings.js.map +1 -0
  139. package/dist/core/backtest/simulate.d.ts +258 -0
  140. package/dist/core/backtest/simulate.d.ts.map +1 -0
  141. package/dist/core/backtest/simulate.js +335 -0
  142. package/dist/core/backtest/simulate.js.map +1 -0
  143. package/dist/core/catalogue/catalogue.generated.d.ts +66 -0
  144. package/dist/core/catalogue/catalogue.generated.d.ts.map +1 -1
  145. package/dist/core/catalogue/catalogue.generated.js +6 -0
  146. package/dist/core/catalogue/catalogue.generated.js.map +1 -1
  147. package/dist/core/catalogue/values.generated.d.ts +24 -0
  148. package/dist/core/catalogue/values.generated.d.ts.map +1 -1
  149. package/dist/core/check/index.d.ts +2 -1
  150. package/dist/core/check/index.d.ts.map +1 -1
  151. package/dist/core/check/index.js +1 -1
  152. package/dist/core/check/index.js.map +1 -1
  153. package/dist/core/check/library-orders.js +2 -2
  154. package/dist/core/check/library-orders.js.map +1 -1
  155. package/dist/core/check/library-prose.generated.d.ts +16 -0
  156. package/dist/core/check/library-prose.generated.d.ts.map +1 -0
  157. package/dist/core/check/library-prose.generated.js +353 -0
  158. package/dist/core/check/library-prose.generated.js.map +1 -0
  159. package/dist/core/check/surface.d.ts +24 -0
  160. package/dist/core/check/surface.d.ts.map +1 -1
  161. package/dist/core/check/surface.js +29 -0
  162. package/dist/core/check/surface.js.map +1 -1
  163. package/dist/core/emit/canonical.d.ts +22 -8
  164. package/dist/core/emit/canonical.d.ts.map +1 -1
  165. package/dist/core/emit/canonical.js +67 -6
  166. package/dist/core/emit/canonical.js.map +1 -1
  167. package/dist/core/emit/defaults.d.ts +35 -16
  168. package/dist/core/emit/defaults.d.ts.map +1 -1
  169. package/dist/core/emit/defaults.js +66 -0
  170. package/dist/core/emit/defaults.js.map +1 -1
  171. package/dist/core/emit/index.d.ts +2 -0
  172. package/dist/core/emit/index.d.ts.map +1 -1
  173. package/dist/core/emit/index.js +2 -0
  174. package/dist/core/emit/index.js.map +1 -1
  175. package/dist/core/engine/arithmetic.d.ts +6 -25
  176. package/dist/core/engine/arithmetic.d.ts.map +1 -1
  177. package/dist/core/engine/arithmetic.js +48 -3
  178. package/dist/core/engine/arithmetic.js.map +1 -1
  179. package/dist/core/engine/index.d.ts +2 -2
  180. package/dist/core/engine/index.d.ts.map +1 -1
  181. package/dist/core/engine/index.js +2 -2
  182. package/dist/core/engine/index.js.map +1 -1
  183. package/dist/core/engine/library/arrays.d.ts.map +1 -1
  184. package/dist/core/engine/library/arrays.js +8 -2
  185. package/dist/core/engine/library/arrays.js.map +1 -1
  186. package/dist/core/engine/library/code-points.d.ts +40 -0
  187. package/dist/core/engine/library/code-points.d.ts.map +1 -0
  188. package/dist/core/engine/library/code-points.js +74 -0
  189. package/dist/core/engine/library/code-points.js.map +1 -0
  190. package/dist/core/engine/library/index.d.ts +5 -0
  191. package/dist/core/engine/library/index.d.ts.map +1 -1
  192. package/dist/core/engine/library/index.js +5 -0
  193. package/dist/core/engine/library/index.js.map +1 -1
  194. package/dist/core/engine/library/text.d.ts.map +1 -1
  195. package/dist/core/engine/library/text.js +51 -34
  196. package/dist/core/engine/library/text.js.map +1 -1
  197. package/dist/core/engine/load.d.ts +35 -1
  198. package/dist/core/engine/load.d.ts.map +1 -1
  199. package/dist/core/engine/load.js +63 -0
  200. package/dist/core/engine/load.js.map +1 -1
  201. package/dist/core/engine/verify-tables.d.ts.map +1 -1
  202. package/dist/core/engine/verify-tables.js +41 -0
  203. package/dist/core/engine/verify-tables.js.map +1 -1
  204. package/dist/core/index.d.ts +43 -4
  205. package/dist/core/index.d.ts.map +1 -1
  206. package/dist/core/index.js +23 -2
  207. package/dist/core/index.js.map +1 -1
  208. package/dist/core/stdlib/index.d.ts +1 -1
  209. package/dist/core/stdlib/index.d.ts.map +1 -1
  210. package/dist/core/stdlib/index.js +1 -1
  211. package/dist/core/stdlib/index.js.map +1 -1
  212. package/dist/core/stdlib/maths/index.d.ts +1 -1
  213. package/dist/core/stdlib/maths/index.d.ts.map +1 -1
  214. package/dist/core/stdlib/maths/index.js +1 -1
  215. package/dist/core/stdlib/maths/index.js.map +1 -1
  216. package/dist/core/stdlib/maths/rounding.d.ts +5 -0
  217. package/dist/core/stdlib/maths/rounding.d.ts.map +1 -1
  218. package/dist/core/stdlib/maths/rounding.js +19 -1
  219. package/dist/core/stdlib/maths/rounding.js.map +1 -1
  220. package/dist/core/version/version.generated.d.ts +1 -1
  221. package/dist/core/version/version.generated.js +1 -1
  222. package/dist/editor/complete.d.ts +40 -0
  223. package/dist/editor/complete.d.ts.map +1 -0
  224. package/dist/editor/complete.js +206 -0
  225. package/dist/editor/complete.js.map +1 -0
  226. package/dist/editor/diagnose.d.ts +18 -0
  227. package/dist/editor/diagnose.d.ts.map +1 -0
  228. package/dist/editor/diagnose.js +70 -0
  229. package/dist/editor/diagnose.js.map +1 -0
  230. package/dist/editor/format.d.ts +11 -0
  231. package/dist/editor/format.d.ts.map +1 -0
  232. package/dist/editor/format.js +50 -0
  233. package/dist/editor/format.js.map +1 -0
  234. package/dist/editor/highlight.d.ts +28 -0
  235. package/dist/editor/highlight.d.ts.map +1 -0
  236. package/dist/editor/highlight.js +65 -0
  237. package/dist/editor/highlight.js.map +1 -0
  238. package/dist/editor/hover.d.ts +40 -0
  239. package/dist/editor/hover.d.ts.map +1 -0
  240. package/dist/editor/hover.js +147 -0
  241. package/dist/editor/hover.js.map +1 -0
  242. package/dist/editor/index.d.ts +60 -0
  243. package/dist/editor/index.d.ts.map +1 -0
  244. package/dist/editor/index.js +7 -0
  245. package/dist/editor/index.js.map +1 -0
  246. package/dist/editor/kinds.d.ts +27 -0
  247. package/dist/editor/kinds.d.ts.map +1 -0
  248. package/dist/editor/kinds.js +118 -0
  249. package/dist/editor/kinds.js.map +1 -0
  250. package/dist/editor/layout.d.ts +47 -0
  251. package/dist/editor/layout.d.ts.map +1 -0
  252. package/dist/editor/layout.js +135 -0
  253. package/dist/editor/layout.js.map +1 -0
  254. package/dist/editor/manifest.d.ts +47 -0
  255. package/dist/editor/manifest.d.ts.map +1 -0
  256. package/dist/editor/manifest.js +93 -0
  257. package/dist/editor/manifest.js.map +1 -0
  258. package/dist/editor/reading.d.ts +41 -0
  259. package/dist/editor/reading.d.ts.map +1 -0
  260. package/dist/editor/reading.js +51 -0
  261. package/dist/editor/reading.js.map +1 -0
  262. package/dist/editor/scan.d.ts +26 -0
  263. package/dist/editor/scan.d.ts.map +1 -0
  264. package/dist/editor/scan.js +139 -0
  265. package/dist/editor/scan.js.map +1 -0
  266. package/dist/editor/scope.d.ts +19 -0
  267. package/dist/editor/scope.d.ts.map +1 -0
  268. package/dist/editor/scope.js +99 -0
  269. package/dist/editor/scope.js.map +1 -0
  270. package/dist/editor/signature.d.ts +38 -0
  271. package/dist/editor/signature.d.ts.map +1 -0
  272. package/dist/editor/signature.js +112 -0
  273. package/dist/editor/signature.js.map +1 -0
  274. package/dist/editor/site.d.ts +46 -0
  275. package/dist/editor/site.d.ts.map +1 -0
  276. package/dist/editor/site.js +184 -0
  277. package/dist/editor/site.js.map +1 -0
  278. package/dist/editor/spacing.d.ts +29 -0
  279. package/dist/editor/spacing.d.ts.map +1 -0
  280. package/dist/editor/spacing.js +116 -0
  281. package/dist/editor/spacing.js.map +1 -0
  282. package/package.json +42 -3
  283. package/spec/README.md +2 -1
  284. package/spec/errors.json +141 -1
  285. package/src/adapters/charts/driving.ts +109 -0
  286. package/src/adapters/charts/run.ts +120 -28
  287. package/src/adapters/charts/surfaces.ts +17 -0
  288. package/src/adapters/charts/tables.ts +88 -0
  289. package/src/adapters/charts/venue.ts +132 -0
  290. package/src/adapters/codemirror/commands.ts +53 -0
  291. package/src/adapters/codemirror/completion.ts +74 -0
  292. package/src/adapters/codemirror/contract.ts +156 -0
  293. package/src/adapters/codemirror/index.ts +66 -0
  294. package/src/adapters/codemirror/lint.ts +66 -0
  295. package/src/adapters/codemirror/positions.ts +79 -0
  296. package/src/adapters/codemirror/stream.ts +87 -0
  297. package/src/adapters/codemirror/tokens.ts +64 -0
  298. package/src/adapters/codemirror/tooltips.ts +113 -0
  299. package/src/core/accounting/analysis.ts +188 -0
  300. package/src/core/accounting/charges.ts +452 -0
  301. package/src/core/accounting/equity.ts +314 -0
  302. package/src/core/accounting/index.ts +51 -0
  303. package/src/core/accounting/markers.ts +95 -0
  304. package/src/core/accounting/monthly.ts +137 -0
  305. package/src/core/accounting/report.ts +94 -0
  306. package/src/core/accounting/shapes.ts +73 -0
  307. package/src/core/accounting/statistics.ts +387 -0
  308. package/src/core/accounting/trades.ts +313 -0
  309. package/src/core/backtest/case.ts +395 -0
  310. package/src/core/backtest/compare.ts +246 -0
  311. package/src/core/backtest/declaration.ts +97 -0
  312. package/src/core/backtest/deliver.ts +161 -0
  313. package/src/core/backtest/drive.ts +468 -0
  314. package/src/core/backtest/index.ts +64 -0
  315. package/src/core/backtest/range.ts +137 -0
  316. package/src/core/backtest/record.ts +470 -0
  317. package/src/core/backtest/replay.ts +168 -0
  318. package/src/core/backtest/resting.ts +125 -0
  319. package/src/core/backtest/settings.ts +280 -0
  320. package/src/core/backtest/simulate.ts +499 -0
  321. package/src/core/catalogue/catalogue.generated.ts +6 -0
  322. package/src/core/catalogue/values.generated.ts +6 -0
  323. package/src/core/check/index.ts +3 -0
  324. package/src/core/check/library-orders.ts +2 -2
  325. package/src/core/check/library-prose.generated.ts +367 -0
  326. package/src/core/check/surface.ts +34 -0
  327. package/src/core/emit/canonical.ts +67 -9
  328. package/src/core/emit/defaults.ts +70 -0
  329. package/src/core/emit/index.ts +2 -0
  330. package/src/core/engine/arithmetic.ts +23 -3
  331. package/src/core/engine/index.ts +2 -2
  332. package/src/core/engine/library/arrays.ts +8 -2
  333. package/src/core/engine/library/code-points.ts +73 -0
  334. package/src/core/engine/library/index.ts +6 -0
  335. package/src/core/engine/library/text.ts +53 -35
  336. package/src/core/engine/load.ts +84 -2
  337. package/src/core/engine/verify-tables.ts +43 -0
  338. package/src/core/index.ts +100 -2
  339. package/src/core/stdlib/index.ts +1 -0
  340. package/src/core/stdlib/maths/index.ts +1 -0
  341. package/src/core/stdlib/maths/rounding.ts +23 -1
  342. package/src/core/version/version.generated.ts +1 -1
  343. package/src/editor/complete.ts +266 -0
  344. package/src/editor/diagnose.ts +71 -0
  345. package/src/editor/format.ts +85 -0
  346. package/src/editor/highlight.ts +110 -0
  347. package/src/editor/hover.ts +199 -0
  348. package/src/editor/index.ts +65 -0
  349. package/src/editor/kinds.ts +157 -0
  350. package/src/editor/layout.ts +189 -0
  351. package/src/editor/manifest.ts +119 -0
  352. package/src/editor/reading.ts +69 -0
  353. package/src/editor/scan.ts +162 -0
  354. package/src/editor/scope.ts +122 -0
  355. package/src/editor/signature.ts +150 -0
  356. package/src/editor/site.ts +233 -0
  357. package/src/editor/spacing.ts +132 -0
@@ -23,6 +23,7 @@
23
23
  * what a script wants when it wraps an index. Collapsing them would silently
24
24
  * change one of the two.
25
25
  */
26
+ import { compareStrings } from './library/index.js';
26
27
  import type { Value } from './values/index.js';
27
28
  import { ABSENT, numberValue, valuesEqual } from './values/index.js';
28
29
 
@@ -99,12 +100,17 @@ function numeric(
99
100
  * absence is treated two ways, and it is deliberate: an operator that can
100
101
  * itself be absent gives a script no way to ask whether a value is absent at
101
102
  * all, so equality is the exception and every other comparison is not.
103
+ *
104
+ * **Two strings are ordered by code point** (`language.md` 9.3, `stdlib.md`
105
+ * section 10), through the one order `sort` uses, and not by the host's own
106
+ * operator, which orders by sixteen bit unit and puts a symbol outside the
107
+ * basic plane below the last thousands of the plane. Numbers take the host's
108
+ * operator, which is the binary64 order every engine shares.
102
109
  */
103
110
  export function compare(opcode: 'LT' | 'LE' | 'GT' | 'GE', a: Value, b: Value): Value {
104
111
  if (a === null || b === null) return ABSENT;
105
- const bothNumbers = typeof a === 'number' && typeof b === 'number';
106
- const bothStrings = typeof a === 'string' && typeof b === 'string';
107
- if (!bothNumbers && !bothStrings) throw new OperandMismatch(opcode);
112
+ if (typeof a === 'string' && typeof b === 'string') return ordered(opcode, compareStrings(a, b));
113
+ if (typeof a !== 'number' || typeof b !== 'number') throw new OperandMismatch(opcode);
108
114
  switch (opcode) {
109
115
  case 'LT':
110
116
  return a < b;
@@ -117,6 +123,20 @@ export function compare(opcode: 'LT' | 'LE' | 'GT' | 'GE', a: Value, b: Value):
117
123
  }
118
124
  }
119
125
 
126
+ /** An ordering comparison read off a three way order: negative, zero or positive. */
127
+ function ordered(opcode: 'LT' | 'LE' | 'GT' | 'GE', order: number): boolean {
128
+ switch (opcode) {
129
+ case 'LT':
130
+ return order < 0;
131
+ case 'LE':
132
+ return order <= 0;
133
+ case 'GT':
134
+ return order > 0;
135
+ default:
136
+ return order >= 0;
137
+ }
138
+ }
139
+
120
140
  /** `EQ` and `NE` are total: they always produce a boolean. */
121
141
  export function equals(a: Value, b: Value): boolean {
122
142
  return valuesEqual(a, b);
@@ -24,7 +24,7 @@
24
24
  export { Engine } from './engine.js';
25
25
  export type { BarResult, RunResult } from './results.js';
26
26
 
27
- export { load } from './load.js';
27
+ export { load, loadText } from './load.js';
28
28
  export type { LoadOptions, LoadResult } from './load.js';
29
29
 
30
30
  export type { AlertFiring } from './alerts.js';
@@ -72,7 +72,7 @@ export type { Timeframe, TimeframeUnit } from './timeframe.js';
72
72
  export { bucketKeyOf, isIntraday, minutesOf, nominalMinutes, parseTimeframe } from './timeframe.js';
73
73
 
74
74
  export type { ResolvedInput, SettingCheck, TimeResolver } from './inputs.js';
75
- export { checkSetting, constantValue, utcTime } from './inputs.js';
75
+ export { checkSetting, constantValue, fieldValue, utcTime } from './inputs.js';
76
76
 
77
77
  export { LANGUAGE_VERSIONS, capabilitiesFor, verify } from './verify.js';
78
78
  export type { VerifyOptions, VerifyResult } from './verify.js';
@@ -22,6 +22,7 @@ import type { Value } from '../values/index.js';
22
22
  import { isNumber, reference, valuesEqual } from '../values/index.js';
23
23
  import { entry, refAt, stringAt, valueAt, wholeAt } from './binding.js';
24
24
  import type { CallContext, ManifestEntry } from './binding.js';
25
+ import { compareStrings } from './code-points.js';
25
26
  import { result } from '../../stdlib/index.js';
26
27
 
27
28
  /** The array a handle names, or nothing when the handle is absent or stale. */
@@ -68,12 +69,17 @@ function within(
68
69
  return index;
69
70
  }
70
71
 
71
- /** The order `sort` uses, written down so two engines cannot differ. */
72
+ /**
73
+ * The order `sort` uses, written down so two engines cannot differ.
74
+ *
75
+ * Two strings order by code point (`language.md` 9.3), which is not the order
76
+ * the host's `<` gives once a string holds a symbol outside the basic plane.
77
+ */
72
78
  function rank(a: Value, b: Value): number {
73
79
  if (a === null) return b === null ? 0 : 1;
74
80
  if (b === null) return -1;
75
81
  if (typeof a === 'number' && typeof b === 'number') return a < b ? -1 : a > b ? 1 : 0;
76
- if (typeof a === 'string' && typeof b === 'string') return a < b ? -1 : a > b ? 1 : 0;
82
+ if (typeof a === 'string' && typeof b === 'string') return compareStrings(a, b);
77
83
  if (typeof a === 'boolean' && typeof b === 'boolean') return a === b ? 0 : a ? 1 : -1;
78
84
  return 0;
79
85
  }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The two string rules two engines have to share, written from the page.
3
+ *
4
+ * **A string is a sequence of code points** (`compiled-program.md` 3.1), and the
5
+ * host this engine is written in stores one as sixteen bit units. Every rule
6
+ * here counts code points and not units, because the two disagree the moment a
7
+ * string holds a symbol outside the basic plane, and a rule written on the
8
+ * host's units would be a rule a second engine could not follow.
9
+ *
10
+ * **Ordering.** Two strings are ordered by comparing code points from the
11
+ * front, the first difference deciding, and a string that runs out first
12
+ * ordering first (`language.md` 9.3, `stdlib.md` section 10). The host's own
13
+ * `<` orders by unit, which puts every symbol outside the basic plane, stored
14
+ * as a surrogate pair from U+D800, below the code points from U+E000 to U+FFFF
15
+ * that come after it, so it is not used.
16
+ *
17
+ * **Whitespace.** `str.trim` removes, and `toNumber` ignores, exactly the code
18
+ * points `stdlib.md` section 10 lists, which are the ones with the Unicode
19
+ * White_Space property. The host's own `trim` removes a set that is nearly this
20
+ * one: it takes the byte order mark U+FEFF as well, and a second engine's host
21
+ * takes the four separators U+001C to U+001F and leaves the byte order mark, so
22
+ * neither host's default is the set, and the set is implemented from the list.
23
+ * `tests/engine/strings.test.ts` walks every code point of the basic plane
24
+ * against the list read out of the page.
25
+ */
26
+
27
+ /** The code points `str.trim` removes, as `stdlib.md` section 10 lists them. */
28
+ const WHITESPACE: ReadonlySet<number> = new Set([
29
+ 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x0020, 0x0085, 0x00a0, 0x1680,
30
+ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200a,
31
+ 0x2028, 0x2029, 0x202f, 0x205f, 0x3000,
32
+ ]);
33
+
34
+ /** Code points, which is what every index over a string counts. */
35
+ export function points(text: string): string[] {
36
+ return [...text];
37
+ }
38
+
39
+ /** Whether one code point is in the trimmed set. */
40
+ export function isWhitespace(point: number): boolean {
41
+ return WHITESPACE.has(point);
42
+ }
43
+
44
+ /** `text` with the whitespace of the written set taken off both ends. */
45
+ export function trimmed(text: string): string {
46
+ const all = points(text);
47
+ let from = 0;
48
+ let to = all.length;
49
+ while (from < to && isWhitespace(all[from]?.codePointAt(0) ?? -1)) from += 1;
50
+ while (to > from && isWhitespace(all[to - 1]?.codePointAt(0) ?? -1)) to -= 1;
51
+ return from === 0 && to === all.length ? text : all.slice(from, to).join('');
52
+ }
53
+
54
+ /**
55
+ * The order of two strings, negative when `a` comes first, by code point.
56
+ *
57
+ * Walked with the string iterator so a surrogate pair is one code point, and
58
+ * compared as numbers so U+10000 comes after U+FFFF, which is the order the
59
+ * page states and the one an engine with four byte strings gets for free.
60
+ */
61
+ export function compareStrings(a: string, b: string): number {
62
+ const left = a[Symbol.iterator]();
63
+ const right = b[Symbol.iterator]();
64
+ for (;;) {
65
+ const one = left.next();
66
+ const other = right.next();
67
+ if (one.done) return other.done ? 0 : -1;
68
+ if (other.done) return 1;
69
+ const x = one.value.codePointAt(0) ?? 0;
70
+ const y = other.value.codePointAt(0) ?? 0;
71
+ if (x !== y) return x < y ? -1 : 1;
72
+ }
73
+ }
@@ -37,6 +37,12 @@ import { TREND_ENTRIES } from './trend.js';
37
37
  import { VOLUME_ENTRIES } from './volume.js';
38
38
  import type { ManifestEntry } from './binding.js';
39
39
 
40
+ /**
41
+ * The two string rules, for the operators outside this module and the tests:
42
+ * `code-points.ts` says why neither is the host's own.
43
+ */
44
+ export { compareStrings, isWhitespace, trimmed } from './code-points.js';
45
+
40
46
  const ENTRIES: readonly ManifestEntry[] = [
41
47
  ...COLOUR_ENTRIES,
42
48
  ...MATHS_ENTRIES,
@@ -8,34 +8,50 @@
8
8
  * one element, or two engines will disagree about the length of a string
9
9
  * holding a symbol outside the basic plane and about every substring taken
10
10
  * after one. That is what the spread into an array is doing everywhere below:
11
- * it costs a pass over the string and buys agreement.
11
+ * it costs a pass over the string and buys agreement. The trimmed set and the
12
+ * order of two strings are in `code-points.ts`, written from the page.
12
13
  *
13
14
  * **Number to string is specified, not the host's default.** `text(x)` is the
14
- * shortest decimal string that reads back as the same binary64 value, which is
15
- * what this language's own conversion already produces. `text(x, d)` rounds to
16
- * `d` decimals with halves away from zero and always emits exactly `d` digits
17
- * after the point, because this is a display conversion and half up is what a
18
- * reader of a price expects.
15
+ * rule of `language.md` 5.5 and goes through the one writer that implements
16
+ * it, `canonicalNumber`, as does every digit this file writes: a host's own
17
+ * conversion is never asked. `text(x, d)` rounds to `d` decimals with halves
18
+ * away from zero and always emits exactly `d` digits after the point, because
19
+ * this is a display conversion and half up is what a reader of a price expects.
19
20
  */
20
- import { roundHalfAway } from '../../stdlib/index.js';
21
+ import { canonicalNumber } from '../../emit/index.js';
22
+ import { roundHalfAway, scaleOf } from '../../stdlib/index.js';
21
23
  import type { Value } from '../values/index.js';
22
24
  import { isColour, reference } from '../values/index.js';
23
25
  import { entry, numberAt, refAt, stringAt, valueAt, wholeAt } from './binding.js';
24
26
  import type { CallContext, ManifestEntry } from './binding.js';
27
+ import { points, trimmed } from './code-points.js';
25
28
 
26
29
  /** A value as `text(x)` spells it. */
27
30
  export function spell(ctx: CallContext, value: Value): string {
28
31
  if (value === null) return 'none';
29
- if (typeof value === 'number') return String(value);
30
- if (typeof value === 'boolean') return String(value);
32
+ if (typeof value === 'number') return canonicalNumber(value);
33
+ if (typeof value === 'boolean') return value ? 'true' : 'false';
31
34
  if (typeof value === 'string') return value;
32
35
  if (isColour(value)) {
33
- const byte = (x: number): string => x.toString(16).padStart(2, '0');
34
- return `#${byte(value.r)}${byte(value.g)}${byte(value.b)}${byte(roundHalfAway(value.a * 255))}`;
36
+ const alpha = roundHalfAway(value.a * 255);
37
+ return `#${hexByte(value.r)}${hexByte(value.g)}${hexByte(value.b)}${hexByte(alpha)}`;
35
38
  }
36
39
  return ctx.heap.get(value.id)?.kind ?? 'none';
37
40
  }
38
41
 
42
+ /**
43
+ * A whole number from 0 to 255 as two hex digits, from a table.
44
+ *
45
+ * A colour's channels are whole numbers (`compiled-program.md` 3.1), so this
46
+ * is not the decimal rendering rule and asks no host conversion: each nibble
47
+ * indexes a string of sixteen characters.
48
+ */
49
+ const HEX_DIGITS = '0123456789abcdef';
50
+
51
+ function hexByte(channel: number): string {
52
+ return `${HEX_DIGITS[channel >> 4] ?? '0'}${HEX_DIGITS[channel & 15] ?? '0'}`;
53
+ }
54
+
39
55
  /** A magnitude as decimal digits, and how many of them fall before the point. */
40
56
  interface Spread {
41
57
  readonly digits: string;
@@ -108,24 +124,31 @@ function written(of: Spread): string {
108
124
  * least one digit, and exactly `d` digits after the point. `spread` is what
109
125
  * makes that true of every magnitude rather than of the ones below a threshold.
110
126
  *
111
- * **Past the scaling range the digits are the shortest form's, zero filled.**
112
- * Scaling by `10 ** d` leaves binary64 altogether for a large enough magnitude
113
- * or a large enough `d`, and there is nothing to round out there: a binary64 at
114
- * or above 2 ** 53 is a whole number already, and a decimal place that far from
115
- * the leading digit is past every digit the value carries. Two engines write
116
- * the same digits, because the shortest decimal form is what each one's own
117
- * conversion produces. Inside the range the scaling is what it was, so no value
118
- * that had an answer has a different one.
127
+ * **The scale is `round(x, d)`'s**, `scaleOf` from the numeric library, so
128
+ * the display conversion and the rounding call multiply by one value
129
+ * (`stdlib.md` 20.7) and cannot part by an ulp at the one count where the
130
+ * host's power does.
131
+ *
132
+ * **The digits are the shortest form's, zero filled, at every magnitude.**
133
+ * Below 2 ** 53 a rounded whole number has no digits but its own, so nothing
134
+ * ordinary changes; at or above it the shortest form is what both engines
135
+ * write, and it is what this uses inside the scaling range as well as past it,
136
+ * where scaling by ten to the `d` leaves binary64 altogether and there is
137
+ * nothing to round: a binary64 that large is a whole number already, and a
138
+ * decimal place that far from the leading digit is past every digit the value
139
+ * carries. One rule for the digits at every magnitude, rather than the exact
140
+ * binary expansion below a threshold and the shortest form above it.
119
141
  */
120
142
  function fixed(x: number, decimals: number): string {
121
- const scaled = roundHalfAway(x * Math.pow(10, decimals));
143
+ const scaled = roundHalfAway(x * scaleOf(decimals));
122
144
  const usable = Number.isFinite(scaled);
123
145
  const sign = (usable ? scaled < 0 : x < 0) ? '-' : '';
124
- // Within the scaling range the whole number's own digits are asked for and
125
- // not its shortest form, which drops the low digits of a large one.
146
+ // The digits are the shortest form's at every magnitude, through the one
147
+ // writer: a rounded whole below 2 ** 53 has no other digits, and above it
148
+ // the shortest form is the one rule both engines write (language.md 5.5).
126
149
  const magnitude = usable
127
- ? spread(Math.abs(scaled).toFixed(0), 0)
128
- : spread(Math.abs(x).toString(), decimals);
150
+ ? spread(canonicalNumber(Math.abs(scaled)), 0)
151
+ : spread(canonicalNumber(Math.abs(x)), decimals);
129
152
  const digits = written(magnitude).padStart(decimals + 1, '0');
130
153
  if (decimals === 0) return sign + digits;
131
154
  const whole = digits.slice(0, digits.length - decimals);
@@ -143,7 +166,7 @@ function fixed(x: number, decimals: number): string {
143
166
  * have. `str.repeat` measures first for the same reason.
144
167
  */
145
168
  function lengthOf(x: number, decimals: number): number {
146
- const before = Math.max(1, spread(Math.abs(x).toString(), 0).point);
169
+ const before = Math.max(1, spread(canonicalNumber(Math.abs(x)), 0).point);
147
170
  return before + decimals + (decimals > 0 ? 1 : 0);
148
171
  }
149
172
 
@@ -154,22 +177,17 @@ function lengthOf(x: number, decimals: number): number {
154
177
  * which accepts hexadecimal, infinities and a bare leading point in some
155
178
  * languages and not others. Absence rather than zero for text that is not a
156
179
  * number, so a script can tell text that is not a number apart from the number
157
- * zero.
180
+ * zero. The whitespace ignored at either end is `str.trim`'s set and no other.
158
181
  */
159
182
  const NUMERIC = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
160
183
 
161
184
  function parseNumber(text: string): Value {
162
- const trimmed = text.trim();
163
- if (!NUMERIC.test(trimmed)) return null;
164
- const parsed = Number(trimmed);
185
+ const bare = trimmed(text);
186
+ if (!NUMERIC.test(bare)) return null;
187
+ const parsed = Number(bare);
165
188
  return Number.isFinite(parsed) ? (parsed === 0 ? 0 : parsed) : null;
166
189
  }
167
190
 
168
- /** Code points, which is what every index in this file counts. */
169
- function points(text: string): string[] {
170
- return [...text];
171
- }
172
-
173
191
  function makeArray(ctx: CallContext, items: Value[]): Value {
174
192
  ctx.guard.array(ctx.span, 'the array', items.length);
175
193
  return reference(ctx.heap.allocate({ kind: 'array', items }));
@@ -198,7 +216,7 @@ export const TEXT_ENTRIES: readonly ManifestEntry[] = [
198
216
 
199
217
  entry('str.upper', 's', (ctx, args) => map(ctx, args, (s) => s.toUpperCase())),
200
218
  entry('str.lower', 's', (ctx, args) => map(ctx, args, (s) => s.toLowerCase())),
201
- entry('str.trim', 's', (ctx, args) => map(ctx, args, (s) => s.trim())),
219
+ entry('str.trim', 's', (ctx, args) => map(ctx, args, trimmed)),
202
220
 
203
221
  entry('str.contains', 's part', (_ctx, args) => pair(args, (s, part) => s.includes(part))),
204
222
  entry('str.startsWith', 's part', (_ctx, args) => pair(args, (s, part) => s.startsWith(part))),
@@ -10,6 +10,7 @@
10
10
  * bar four thousand with half a study on the screen.
11
11
  */
12
12
  import type { Diagnostic } from '../diagnostics/index.js';
13
+ import { canonicalise } from '../emit/index.js';
13
14
  import type { SourceFile } from '../source/index.js';
14
15
  import { BAR_FIELDS } from './bars.js';
15
16
  import { limitsWith } from './budget.js';
@@ -18,7 +19,7 @@ import { Engine } from './engine.js';
18
19
  import { NO_POSITION, failure, malformed } from './errors.js';
19
20
  import type { EngineHost } from './host.js';
20
21
  import { resolveInputs, utcTime } from './inputs.js';
21
- import type { TimeResolver } from './inputs.js';
22
+ import type { ResolvedInput, TimeResolver } from './inputs.js';
22
23
  import { planRequests } from './request-plan.js';
23
24
  import { recordProblem } from './session/index.js';
24
25
  import { capabilitiesFor, verify } from './verify.js';
@@ -36,8 +37,21 @@ export interface LoadOptions {
36
37
  readonly time?: TimeResolver;
37
38
  }
38
39
 
40
+ /**
41
+ * A loaded program, and the settings it was loaded under.
42
+ *
43
+ * The resolved inputs come back beside the engine because a declaration field
44
+ * may be written by an input (`compiled-program.md` 2.3), and a caller outside
45
+ * the engine reads some of those fields: what a run was carried out with is the
46
+ * declaration's capital, its commission and the bar a fill is priced at, and
47
+ * the backtest driver is what reports them. Resolving an input a second time
48
+ * out there would be the same rule written in two files, and the first host
49
+ * setting either of them read differently would be a report nobody could
50
+ * explain. So the resolution happens once, here, and what it produced is handed
51
+ * over rather than kept.
52
+ */
39
53
  export type LoadResult =
40
- | { readonly ok: true; readonly engine: Engine }
54
+ | { readonly ok: true; readonly engine: Engine; readonly inputs: readonly ResolvedInput[] }
41
55
  | { readonly ok: false; readonly diagnostic: Diagnostic };
42
56
 
43
57
  /**
@@ -100,5 +114,73 @@ export function load(program: unknown, options: LoadOptions = {}): LoadResult {
100
114
  return {
101
115
  ok: true,
102
116
  engine: new Engine(checked.program, resolved.inputs, options, limits, planned.plans),
117
+ inputs: resolved.inputs,
103
118
  };
104
119
  }
120
+
121
+ /**
122
+ * Loads a compiled program from text, which is where canonicity is required.
123
+ *
124
+ * `load` takes a parsed object because a host that compiled in this process
125
+ * never serialised, and there is nothing for such an object to be canonical
126
+ * about. Text is the other boundary, `compiled-program.md` 9.4 step 1 and
127
+ * section 13 (decision 57): a program an engine reads from outside its process
128
+ * arrives as the canonical encoding of 2.14, and text that parses to a program
129
+ * but is not that encoding is refused, because the hash a host recorded was
130
+ * taken over canonical bytes and text in any other spelling is text that hash
131
+ * does not name. So the text is parsed, written out again by the one canonical
132
+ * writer, and the two are compared character for character. Either failure is
133
+ * OS6018 naming where the text stops being readable or stops being canonical,
134
+ * counted in characters of the text, which is where an editor's cursor lands.
135
+ *
136
+ * What passes here is the object `load` verifies from step 2 on, so every
137
+ * refusal that applies to an object applies to text as well and in the same
138
+ * order.
139
+ */
140
+ export function loadText(text: string, options: LoadOptions = {}): LoadResult {
141
+ let parsed: unknown;
142
+ try {
143
+ parsed = JSON.parse(text);
144
+ } catch (thrown) {
145
+ const message = thrown instanceof Error ? thrown.message : String(thrown);
146
+ const at = /position (\d+)/.exec(message);
147
+ return {
148
+ ok: false,
149
+ diagnostic: malformed(
150
+ at === null ? 'the text' : `character ${at[1]}`,
151
+ 'the text stops being the object notation of section 2.14 there, so it is not a program',
152
+ ),
153
+ };
154
+ }
155
+
156
+ let canonical: string;
157
+ try {
158
+ canonical = canonicalise(parsed);
159
+ } catch (thrown) {
160
+ const message = thrown instanceof Error ? thrown.message : String(thrown);
161
+ return {
162
+ ok: false,
163
+ diagnostic: malformed('the text', `it parses but cannot be written in the canonical form: ${message}`),
164
+ };
165
+ }
166
+ if (canonical !== text) {
167
+ return {
168
+ ok: false,
169
+ diagnostic: malformed(
170
+ `character ${firstDifference(text, canonical)}`,
171
+ 'the text is not the canonical encoding of the program it parses to, and a program ' +
172
+ 'read from outside the process has to be, because that is what its hash was taken over',
173
+ ),
174
+ };
175
+ }
176
+ return load(parsed, options);
177
+ }
178
+
179
+ /** Where two texts part, as an index into either, or the shorter one's length. */
180
+ function firstDifference(a: string, b: string): number {
181
+ const shorter = Math.min(a.length, b.length);
182
+ for (let i = 0; i < shorter; i += 1) {
183
+ if (a[i] !== b[i]) return i;
184
+ }
185
+ return shorter;
186
+ }
@@ -21,8 +21,51 @@ const META_KINDS = ['study', 'strategy'] as const;
21
21
  const CHANNEL_TYPES = ['number', 'string', 'color', 'bool'] as const;
22
22
  const EFFECTS = ['none', 'signal', 'order', 'draw', 'log'] as const;
23
23
 
24
+ /**
25
+ * The tables a later minor of this format major added, with the minor that
26
+ * added each.
27
+ *
28
+ * 9.2's rule read from the other side, which 9.4 step 3 states and decision 56
29
+ * settled: a table a later minor added and an earlier program lacks reads as
30
+ * empty, never as a refusal, because 9.5's first line is a promise about that
31
+ * program. A program stamped at this minor or a later one has no such excuse.
32
+ * Section 2 says an empty table is written as an empty array and never
33
+ * omitted, so its absence there is the defect check 1 exists for, and the
34
+ * version is what tells the two apart.
35
+ *
36
+ * `spec/format-history.json` records the same additions, one entry per format
37
+ * version, and `tests/engine/format-minors.test.ts` holds this list to that
38
+ * file: a table the history says a later minor added has to be one a program
39
+ * at the earlier minor may lack.
40
+ */
41
+ const ADDED_AT_MINOR: readonly { readonly table: string; readonly minor: number }[] = [
42
+ { table: 'requests', minor: 1 },
43
+ ];
44
+
45
+ /** The minor of a `major.minor` the version step has already proved is one. */
46
+ function minorOf(raw: Readonly<Record<string, unknown>>): number {
47
+ const version = raw['openscript'] as Readonly<Record<string, unknown>>;
48
+ return Number(String(version['format']).split('.')[1] ?? '0');
49
+ }
50
+
51
+ /**
52
+ * Writes the empty table an earlier minor is owed into the program itself.
53
+ *
54
+ * Into the object rather than into a local, because every later step reads
55
+ * the program as its own type and would find the table missing again: the
56
+ * verifier hands the same object on, and the engine holds it.
57
+ */
58
+ function supplyAddedTables(raw: Readonly<Record<string, unknown>>): void {
59
+ const minor = minorOf(raw);
60
+ for (const added of ADDED_AT_MINOR) {
61
+ if (raw[added.table] !== undefined || minor >= added.minor) continue;
62
+ (raw as Record<string, unknown>)[added.table] = [];
63
+ }
64
+ }
65
+
24
66
  /** Check 1 over every table the machine indexes, and every declaration field. */
25
67
  export function checkTables(shape: ShapeCheck, raw: Readonly<Record<string, unknown>>): boolean {
68
+ supplyAddedTables(raw);
26
69
  for (const name of ['requires', 'inputs', 'channels', 'consts', 'series', 'cells', 'states',
27
70
  'functions', 'callSites', 'loops', 'code', 'requests']) {
28
71
  if (!shape.array(raw[name], name)) return false;
package/src/core/index.ts CHANGED
@@ -170,6 +170,7 @@ export type {
170
170
  InputKind,
171
171
  LibraryEntry,
172
172
  LibraryParameter,
173
+ LibraryProse,
173
174
  ObjectKind,
174
175
  RequestMode,
175
176
  Storage,
@@ -192,6 +193,7 @@ export {
192
193
  atLeastBar,
193
194
  check,
194
195
  delayed,
196
+ describedNames,
195
197
  earlier,
196
198
  elementOf,
197
199
  isLibraryName,
@@ -205,6 +207,7 @@ export {
205
207
  membersOf,
206
208
  sameType,
207
209
  seriesOf,
210
+ proseFor,
208
211
  typeText,
209
212
  } from './check/index.js';
210
213
  export { REQUEST_NAMES } from './check/index.js';
@@ -223,9 +226,21 @@ export { COMPILED_FORMAT_VERSION, VERSION } from './version/index.js';
223
226
  */
224
227
  export { emit } from './emit/index.js';
225
228
  export type { EmitOptions, EmitResult } from './emit/index.js';
226
- export type { CompiledProgram } from './emit/index.js';
229
+ export type { Colour, CompiledProgram } from './emit/index.js';
227
230
  export { canonicalise, programHash, sourceHash } from './emit/index.js';
228
231
 
232
+ /**
233
+ * Two answers the emitter holds that a tool built on the language needs.
234
+ *
235
+ * A declaration call's defaults and the channels a colour name denotes are both
236
+ * facts the compiler applies and neither is in the library manifest, so a tier
237
+ * above this one that read the manifest alone would show a writer nothing where
238
+ * the compiler has a value. They are here rather than reached for behind the
239
+ * emitter's door, and `scripts/check-defaults.mjs` holds the first of them to
240
+ * what `stdlib.md` prints.
241
+ */
242
+ export { DECLARATION_CALLS, declarationDefaultText, namedColour } from './emit/index.js';
243
+
229
244
  /**
230
245
  * The engine: a program in, one bar at a time, values out.
231
246
  *
@@ -235,10 +250,15 @@ export { canonicalise, programHash, sourceHash } from './emit/index.js';
235
250
  * same check on its own, for a host that keeps compiled programs and wants to
236
251
  * refuse a bad one when it arrives rather than when it is first drawn.
237
252
  *
253
+ * `loadText` is `load` for a program that arrives as text, which is the one
254
+ * boundary where the canonical encoding is required (`compiled-program.md`
255
+ * section 13): text that parses to a program but is not that encoding is
256
+ * refused, because the hash a host recorded was taken over canonical bytes.
257
+ *
238
258
  * `capabilitiesFor` and `LANGUAGE_VERSIONS` are this engine's half of the
239
259
  * compatibility statement in 9.1, beside `COMPILED_FORMAT_VERSION` above.
240
260
  */
241
- export { load } from './engine/index.js';
261
+ export { load, loadText } from './engine/index.js';
242
262
  export type { BarResult, Engine, LoadOptions, LoadResult, RunResult } from './engine/index.js';
243
263
  export type {
244
264
  BarState,
@@ -267,3 +287,81 @@ export { isAbsent } from './engine/index.js';
267
287
  export type { Value } from './engine/index.js';
268
288
  export { LANGUAGE_VERSIONS, capabilitiesFor, verify } from './engine/index.js';
269
289
  export type { VerifyOptions, VerifyResult } from './engine/index.js';
290
+
291
+ /**
292
+ * The money, and the run it is folded from (`stdlib.md` 17.1, 17.7).
293
+ *
294
+ * Two modules rather than one, and the line between them is the design. The
295
+ * accounting shapes are arithmetic over portable data and know nothing about an
296
+ * engine, so a stored record can be reported again with no engine present and
297
+ * the engine can one day call the same arithmetic with no second implementation
298
+ * to disagree with. The backtest shapes are what one run was carried out under
299
+ * and what it produced, and a record of one is a conformance case a second
300
+ * engine can be handed whole.
301
+ *
302
+ * They are here rather than behind an entry point of their own because they are
303
+ * the same layer: a host that compiles and runs a program is the host that
304
+ * reports what it did.
305
+ */
306
+ export type {
307
+ BarMark,
308
+ ChargeBase,
309
+ ChargeBreakdown,
310
+ ChargeLine,
311
+ ChargeSchedule,
312
+ ChargeSide,
313
+ Contract,
314
+ EquityPoint,
315
+ Money,
316
+ MonthlyReturn,
317
+ RecordedFill,
318
+ Report,
319
+ SideAnalysis,
320
+ Summary,
321
+ Trade,
322
+ TradeAnalysis,
323
+ TradeMarker,
324
+ } from './accounting/index.js';
325
+ export { chargeFor, scheduleFromDeclaration, scheduleProblem } from './accounting/index.js';
326
+ export { markersOf, monthlyOver, reportOf } from './accounting/index.js';
327
+ export { tradesOf } from './accounting/index.js';
328
+ export { analysisOf, equityOver, summaryOf } from './accounting/index.js';
329
+ export {
330
+ DEFAULT_FILL,
331
+ EXACT,
332
+ WHOLE_RANGE,
333
+ RECORD_VERSION,
334
+ backtest,
335
+ backtestSupplied,
336
+ barsHash,
337
+ caseFilesFrom,
338
+ checkSettings,
339
+ compareRuns,
340
+ recordFromJson,
341
+ recordToJson,
342
+ recordOf,
343
+ replay,
344
+ rerun,
345
+ runBytes,
346
+ settingsFor,
347
+ windowFor,
348
+ } from './backtest/index.js';
349
+ export type { BacktestResult, ReplayResult, ReportWindow } from './backtest/index.js';
350
+ export type {
351
+ BacktestSettings,
352
+ BarsInRecord,
353
+ CaseFiles,
354
+ CaseIdentity,
355
+ CaseResult,
356
+ DateRange,
357
+ DriveOptions,
358
+ FillPolicy,
359
+ InstrumentFacts,
360
+ RecordedBar,
361
+ RecordedDiagnostic,
362
+ RecordedFrame,
363
+ RecordedOrder,
364
+ RunComparison,
365
+ RunRecord,
366
+ Tolerance,
367
+ } from './backtest/index.js';
@@ -105,6 +105,7 @@ export {
105
105
  roundTo,
106
106
  roundToStep,
107
107
  roundToTick,
108
+ scaleOf,
108
109
  sign,
109
110
  sin,
110
111
  sqrt,
@@ -18,6 +18,7 @@ export {
18
18
  roundTo,
19
19
  roundToStep,
20
20
  roundToTick,
21
+ scaleOf,
21
22
  trunc,
22
23
  } from './rounding.js';
23
24