openalgo-script 0.5.0 → 0.7.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 (343) hide show
  1. package/CHANGELOG.md +226 -0
  2. package/README.md +53 -22
  3. package/dist/adapters/charts/run.d.ts.map +1 -1
  4. package/dist/adapters/charts/run.js +6 -0
  5. package/dist/adapters/charts/run.js.map +1 -1
  6. package/dist/adapters/charts/tables.d.ts +6 -19
  7. package/dist/adapters/charts/tables.d.ts.map +1 -1
  8. package/dist/adapters/charts/tables.js.map +1 -1
  9. package/dist/adapters/charts/undrawable.d.ts +4 -0
  10. package/dist/adapters/charts/undrawable.d.ts.map +1 -0
  11. package/dist/adapters/charts/undrawable.js +47 -0
  12. package/dist/adapters/charts/undrawable.js.map +1 -0
  13. package/dist/core/backtest/drive.d.ts +36 -1
  14. package/dist/core/backtest/drive.d.ts.map +1 -1
  15. package/dist/core/backtest/drive.js +13 -107
  16. package/dist/core/backtest/drive.js.map +1 -1
  17. package/dist/core/backtest/index.d.ts +2 -0
  18. package/dist/core/backtest/index.d.ts.map +1 -1
  19. package/dist/core/backtest/index.js.map +1 -1
  20. package/dist/core/backtest/walk.d.ts +39 -0
  21. package/dist/core/backtest/walk.d.ts.map +1 -0
  22. package/dist/core/backtest/walk.js +125 -0
  23. package/dist/core/backtest/walk.js.map +1 -0
  24. package/dist/core/catalogue/catalogue.generated.d.ts +178 -1
  25. package/dist/core/catalogue/catalogue.generated.d.ts.map +1 -1
  26. package/dist/core/catalogue/catalogue.generated.js +18 -1
  27. package/dist/core/catalogue/catalogue.generated.js.map +1 -1
  28. package/dist/core/catalogue/types.d.ts +8 -2
  29. package/dist/core/catalogue/types.d.ts.map +1 -1
  30. package/dist/core/catalogue/values.generated.d.ts +51 -0
  31. package/dist/core/catalogue/values.generated.d.ts.map +1 -1
  32. package/dist/core/check/assigned.d.ts +19 -0
  33. package/dist/core/check/assigned.d.ts.map +1 -0
  34. package/dist/core/check/assigned.js +14 -0
  35. package/dist/core/check/assigned.js.map +1 -0
  36. package/dist/core/check/call-sites.d.ts.map +1 -1
  37. package/dist/core/check/call-sites.js +9 -4
  38. package/dist/core/check/call-sites.js.map +1 -1
  39. package/dist/core/check/calls.d.ts.map +1 -1
  40. package/dist/core/check/calls.js +12 -4
  41. package/dist/core/check/calls.js.map +1 -1
  42. package/dist/core/check/check.d.ts.map +1 -1
  43. package/dist/core/check/check.js +15 -2
  44. package/dist/core/check/check.js.map +1 -1
  45. package/dist/core/check/checker.d.ts +26 -0
  46. package/dist/core/check/checker.d.ts.map +1 -1
  47. package/dist/core/check/checker.js +38 -2
  48. package/dist/core/check/checker.js.map +1 -1
  49. package/dist/core/check/constant.d.ts +16 -0
  50. package/dist/core/check/constant.d.ts.map +1 -1
  51. package/dist/core/check/constant.js +52 -0
  52. package/dist/core/check/constant.js.map +1 -1
  53. package/dist/core/check/deleted.d.ts +3 -0
  54. package/dist/core/check/deleted.d.ts.map +1 -0
  55. package/dist/core/check/deleted.js +109 -0
  56. package/dist/core/check/deleted.js.map +1 -0
  57. package/dist/core/check/expressions.js +1 -1
  58. package/dist/core/check/expressions.js.map +1 -1
  59. package/dist/core/check/library-output.d.ts.map +1 -1
  60. package/dist/core/check/library-output.js +1 -0
  61. package/dist/core/check/library-output.js.map +1 -1
  62. package/dist/core/check/library-prose.generated.js +10 -10
  63. package/dist/core/check/library-prose.generated.js.map +1 -1
  64. package/dist/core/check/library.d.ts +7 -0
  65. package/dist/core/check/library.d.ts.map +1 -1
  66. package/dist/core/check/library.js +1 -0
  67. package/dist/core/check/library.js.map +1 -1
  68. package/dist/core/check/surface.d.ts +9 -0
  69. package/dist/core/check/surface.d.ts.map +1 -1
  70. package/dist/core/check/surface.js +13 -0
  71. package/dist/core/check/surface.js.map +1 -1
  72. package/dist/core/engine/bar-source.d.ts +65 -0
  73. package/dist/core/engine/bar-source.d.ts.map +1 -0
  74. package/dist/core/engine/bar-source.js +58 -0
  75. package/dist/core/engine/bar-source.js.map +1 -0
  76. package/dist/core/engine/channels.js +2 -2
  77. package/dist/core/engine/channels.js.map +1 -1
  78. package/dist/core/engine/engine.d.ts +9 -11
  79. package/dist/core/engine/engine.d.ts.map +1 -1
  80. package/dist/core/engine/engine.js +26 -27
  81. package/dist/core/engine/engine.js.map +1 -1
  82. package/dist/core/engine/guard.d.ts.map +1 -1
  83. package/dist/core/engine/guard.js +10 -0
  84. package/dist/core/engine/guard.js.map +1 -1
  85. package/dist/core/engine/host.d.ts +4 -2
  86. package/dist/core/engine/host.d.ts.map +1 -1
  87. package/dist/core/engine/host.js.map +1 -1
  88. package/dist/core/engine/index.d.ts +1 -0
  89. package/dist/core/engine/index.d.ts.map +1 -1
  90. package/dist/core/engine/index.js.map +1 -1
  91. package/dist/core/engine/library/binding.d.ts +2 -0
  92. package/dist/core/engine/library/binding.d.ts.map +1 -1
  93. package/dist/core/engine/library/binding.js.map +1 -1
  94. package/dist/core/engine/library/index.d.ts +8 -0
  95. package/dist/core/engine/library/index.d.ts.map +1 -1
  96. package/dist/core/engine/library/index.js +10 -0
  97. package/dist/core/engine/library/index.js.map +1 -1
  98. package/dist/core/engine/library/maths.js +2 -2
  99. package/dist/core/engine/library/maths.js.map +1 -1
  100. package/dist/core/engine/library/objects.d.ts.map +1 -1
  101. package/dist/core/engine/library/objects.js +1 -1
  102. package/dist/core/engine/library/objects.js.map +1 -1
  103. package/dist/core/engine/machine.js +5 -5
  104. package/dist/core/engine/machine.js.map +1 -1
  105. package/dist/core/engine/memory.js +2 -2
  106. package/dist/core/engine/memory.js.map +1 -1
  107. package/dist/core/engine/registers.js +2 -2
  108. package/dist/core/engine/registers.js.map +1 -1
  109. package/dist/core/engine/request-body.d.ts +3 -1
  110. package/dist/core/engine/request-body.d.ts.map +1 -1
  111. package/dist/core/engine/request-body.js +3 -1
  112. package/dist/core/engine/request-body.js.map +1 -1
  113. package/dist/core/engine/request-plan.d.ts +3 -3
  114. package/dist/core/engine/request-plan.js +3 -3
  115. package/dist/core/engine/requests.d.ts +2 -2
  116. package/dist/core/engine/requests.d.ts.map +1 -1
  117. package/dist/core/engine/requests.js +5 -4
  118. package/dist/core/engine/requests.js.map +1 -1
  119. package/dist/core/engine/series.d.ts +18 -6
  120. package/dist/core/engine/series.d.ts.map +1 -1
  121. package/dist/core/engine/series.js +24 -5
  122. package/dist/core/engine/series.js.map +1 -1
  123. package/dist/core/engine/session/record.d.ts +4 -4
  124. package/dist/core/engine/session/record.js +4 -4
  125. package/dist/core/engine/values/index.d.ts +1 -1
  126. package/dist/core/engine/values/index.d.ts.map +1 -1
  127. package/dist/core/engine/values/index.js +1 -1
  128. package/dist/core/engine/values/index.js.map +1 -1
  129. package/dist/core/engine/values/value.d.ts +2 -0
  130. package/dist/core/engine/values/value.d.ts.map +1 -1
  131. package/dist/core/engine/values/value.js +4 -0
  132. package/dist/core/engine/values/value.js.map +1 -1
  133. package/dist/core/importer/calls.d.ts +29 -0
  134. package/dist/core/importer/calls.d.ts.map +1 -0
  135. package/dist/core/importer/calls.js +267 -0
  136. package/dist/core/importer/calls.js.map +1 -0
  137. package/dist/core/importer/context.d.ts +118 -0
  138. package/dist/core/importer/context.d.ts.map +1 -0
  139. package/dist/core/importer/context.js +120 -0
  140. package/dist/core/importer/context.js.map +1 -0
  141. package/dist/core/importer/expressions.d.ts +44 -0
  142. package/dist/core/importer/expressions.d.ts.map +1 -0
  143. package/dist/core/importer/expressions.js +223 -0
  144. package/dist/core/importer/expressions.js.map +1 -0
  145. package/dist/core/importer/importer.d.ts +13 -0
  146. package/dist/core/importer/importer.d.ts.map +1 -0
  147. package/dist/core/importer/importer.js +220 -0
  148. package/dist/core/importer/importer.js.map +1 -0
  149. package/dist/core/importer/index.d.ts +20 -0
  150. package/dist/core/importer/index.d.ts.map +1 -0
  151. package/dist/core/importer/index.js +18 -0
  152. package/dist/core/importer/index.js.map +1 -0
  153. package/dist/core/importer/lexer.d.ts +68 -0
  154. package/dist/core/importer/lexer.d.ts.map +1 -0
  155. package/dist/core/importer/lexer.js +207 -0
  156. package/dist/core/importer/lexer.js.map +1 -0
  157. package/dist/core/importer/names.d.ts +41 -0
  158. package/dist/core/importer/names.d.ts.map +1 -0
  159. package/dist/core/importer/names.js +78 -0
  160. package/dist/core/importer/names.js.map +1 -0
  161. package/dist/core/importer/orders.d.ts +8 -0
  162. package/dist/core/importer/orders.d.ts.map +1 -0
  163. package/dist/core/importer/orders.js +332 -0
  164. package/dist/core/importer/orders.js.map +1 -0
  165. package/dist/core/importer/outputs.d.ts +19 -0
  166. package/dist/core/importer/outputs.d.ts.map +1 -0
  167. package/dist/core/importer/outputs.js +266 -0
  168. package/dist/core/importer/outputs.js.map +1 -0
  169. package/dist/core/importer/parser.d.ts +5 -0
  170. package/dist/core/importer/parser.d.ts.map +1 -0
  171. package/dist/core/importer/parser.js +439 -0
  172. package/dist/core/importer/parser.js.map +1 -0
  173. package/dist/core/importer/presence.d.ts +32 -0
  174. package/dist/core/importer/presence.d.ts.map +1 -0
  175. package/dist/core/importer/presence.js +146 -0
  176. package/dist/core/importer/presence.js.map +1 -0
  177. package/dist/core/importer/settle.d.ts +23 -0
  178. package/dist/core/importer/settle.d.ts.map +1 -0
  179. package/dist/core/importer/settle.js +135 -0
  180. package/dist/core/importer/settle.js.map +1 -0
  181. package/dist/core/importer/statements.d.ts +4 -0
  182. package/dist/core/importer/statements.d.ts.map +1 -0
  183. package/dist/core/importer/statements.js +339 -0
  184. package/dist/core/importer/statements.js.map +1 -0
  185. package/dist/core/importer/syntax.d.ts +182 -0
  186. package/dist/core/importer/syntax.d.ts.map +1 -0
  187. package/dist/core/importer/syntax.js +14 -0
  188. package/dist/core/importer/syntax.js.map +1 -0
  189. package/dist/core/importer/table.d.ts +52 -0
  190. package/dist/core/importer/table.d.ts.map +1 -0
  191. package/dist/core/importer/table.js +156 -0
  192. package/dist/core/importer/table.js.map +1 -0
  193. package/dist/core/importer/values.d.ts +27 -0
  194. package/dist/core/importer/values.d.ts.map +1 -0
  195. package/dist/core/importer/values.js +236 -0
  196. package/dist/core/importer/values.js.map +1 -0
  197. package/dist/core/index.d.ts +12 -0
  198. package/dist/core/index.d.ts.map +1 -1
  199. package/dist/core/index.js +11 -0
  200. package/dist/core/index.js.map +1 -1
  201. package/dist/core/stdlib/averages/exponential.d.ts.map +1 -1
  202. package/dist/core/stdlib/averages/exponential.js +3 -6
  203. package/dist/core/stdlib/averages/exponential.js.map +1 -1
  204. package/dist/core/stdlib/averages/gaussian.d.ts +15 -0
  205. package/dist/core/stdlib/averages/gaussian.d.ts.map +1 -0
  206. package/dist/core/stdlib/averages/gaussian.js +55 -0
  207. package/dist/core/stdlib/averages/gaussian.js.map +1 -0
  208. package/dist/core/stdlib/averages/shaped.d.ts +2 -6
  209. package/dist/core/stdlib/averages/shaped.d.ts.map +1 -1
  210. package/dist/core/stdlib/averages/shaped.js +8 -22
  211. package/dist/core/stdlib/averages/shaped.js.map +1 -1
  212. package/dist/core/stdlib/calendar/civil.d.ts.map +1 -1
  213. package/dist/core/stdlib/calendar/civil.js +5 -2
  214. package/dist/core/stdlib/calendar/civil.js.map +1 -1
  215. package/dist/core/stdlib/maths/arithmetic.d.ts.map +1 -1
  216. package/dist/core/stdlib/maths/arithmetic.js +2 -1
  217. package/dist/core/stdlib/maths/arithmetic.js.map +1 -1
  218. package/dist/core/stdlib/maths/elementary.d.ts +3 -20
  219. package/dist/core/stdlib/maths/elementary.d.ts.map +1 -1
  220. package/dist/core/stdlib/maths/elementary.js +17 -13
  221. package/dist/core/stdlib/maths/elementary.js.map +1 -1
  222. package/dist/core/stdlib/maths/hypot.d.ts +8 -0
  223. package/dist/core/stdlib/maths/hypot.d.ts.map +1 -0
  224. package/dist/core/stdlib/maths/hypot.js +68 -0
  225. package/dist/core/stdlib/maths/hypot.js.map +1 -0
  226. package/dist/core/stdlib/maths/power.d.ts +3 -0
  227. package/dist/core/stdlib/maths/power.d.ts.map +1 -0
  228. package/dist/core/stdlib/maths/power.js +83 -0
  229. package/dist/core/stdlib/maths/power.js.map +1 -0
  230. package/dist/core/stdlib/maths/transcendental.d.ts +15 -0
  231. package/dist/core/stdlib/maths/transcendental.d.ts.map +1 -0
  232. package/dist/core/stdlib/maths/transcendental.js +185 -0
  233. package/dist/core/stdlib/maths/transcendental.js.map +1 -0
  234. package/dist/core/stdlib/maths/trigonometric.d.ts +4 -0
  235. package/dist/core/stdlib/maths/trigonometric.d.ts.map +1 -0
  236. package/dist/core/stdlib/maths/trigonometric.js +196 -0
  237. package/dist/core/stdlib/maths/trigonometric.js.map +1 -0
  238. package/dist/core/stdlib/series/conditions.d.ts +2 -4
  239. package/dist/core/stdlib/series/conditions.d.ts.map +1 -1
  240. package/dist/core/stdlib/series/conditions.js +9 -13
  241. package/dist/core/stdlib/series/conditions.js.map +1 -1
  242. package/dist/core/stdlib/series/statistics.d.ts.map +1 -1
  243. package/dist/core/stdlib/series/statistics.js +2 -0
  244. package/dist/core/stdlib/series/statistics.js.map +1 -1
  245. package/dist/core/stdlib/values/history.d.ts +26 -0
  246. package/dist/core/stdlib/values/history.d.ts.map +1 -0
  247. package/dist/core/stdlib/values/history.js +52 -0
  248. package/dist/core/stdlib/values/history.js.map +1 -0
  249. package/dist/core/stdlib/values/index.d.ts +1 -0
  250. package/dist/core/stdlib/values/index.d.ts.map +1 -1
  251. package/dist/core/stdlib/values/index.js +1 -0
  252. package/dist/core/stdlib/values/index.js.map +1 -1
  253. package/dist/core/stdlib/values/lookback.d.ts +4 -1
  254. package/dist/core/stdlib/values/lookback.d.ts.map +1 -1
  255. package/dist/core/stdlib/values/lookback.js +48 -45
  256. package/dist/core/stdlib/values/lookback.js.map +1 -1
  257. package/dist/core/stdlib/values/region.d.ts +6 -5
  258. package/dist/core/stdlib/values/region.d.ts.map +1 -1
  259. package/dist/core/stdlib/values/region.js +3 -3
  260. package/dist/core/stdlib/values/region.js.map +1 -1
  261. package/dist/core/stdlib/volatility/measures.d.ts.map +1 -1
  262. package/dist/core/stdlib/volatility/measures.js +6 -4
  263. package/dist/core/stdlib/volatility/measures.js.map +1 -1
  264. package/dist/core/version/version.generated.d.ts +1 -1
  265. package/dist/core/version/version.generated.js +1 -1
  266. package/package.json +13 -2
  267. package/spec/errors.json +380 -20
  268. package/src/adapters/charts/run.ts +6 -0
  269. package/src/adapters/charts/tables.ts +6 -19
  270. package/src/adapters/charts/undrawable.ts +51 -0
  271. package/src/core/backtest/drive.ts +66 -154
  272. package/src/core/backtest/index.ts +2 -0
  273. package/src/core/backtest/walk.ts +182 -0
  274. package/src/core/catalogue/catalogue.generated.ts +18 -1
  275. package/src/core/catalogue/types.ts +8 -2
  276. package/src/core/catalogue/values.generated.ts +16 -0
  277. package/src/core/check/assigned.ts +31 -0
  278. package/src/core/check/call-sites.ts +11 -4
  279. package/src/core/check/calls.ts +13 -4
  280. package/src/core/check/check.ts +14 -2
  281. package/src/core/check/checker.ts +39 -2
  282. package/src/core/check/constant.ts +57 -0
  283. package/src/core/check/deleted.ts +135 -0
  284. package/src/core/check/expressions.ts +1 -1
  285. package/src/core/check/library-output.ts +1 -0
  286. package/src/core/check/library-prose.generated.ts +10 -10
  287. package/src/core/check/library.ts +8 -0
  288. package/src/core/check/surface.ts +16 -0
  289. package/src/core/engine/bar-source.ts +113 -0
  290. package/src/core/engine/channels.ts +2 -2
  291. package/src/core/engine/engine.ts +29 -28
  292. package/src/core/engine/guard.ts +12 -1
  293. package/src/core/engine/host.ts +4 -2
  294. package/src/core/engine/index.ts +1 -0
  295. package/src/core/engine/library/binding.ts +2 -0
  296. package/src/core/engine/library/index.ts +11 -0
  297. package/src/core/engine/library/maths.ts +2 -2
  298. package/src/core/engine/library/objects.ts +1 -6
  299. package/src/core/engine/machine.ts +5 -5
  300. package/src/core/engine/memory.ts +2 -2
  301. package/src/core/engine/registers.ts +2 -2
  302. package/src/core/engine/request-body.ts +5 -7
  303. package/src/core/engine/request-plan.ts +3 -3
  304. package/src/core/engine/requests.ts +11 -8
  305. package/src/core/engine/series.ts +30 -6
  306. package/src/core/engine/session/record.ts +4 -4
  307. package/src/core/engine/values/index.ts +1 -0
  308. package/src/core/engine/values/value.ts +5 -0
  309. package/src/core/importer/calls.ts +274 -0
  310. package/src/core/importer/context.ts +197 -0
  311. package/src/core/importer/expressions.ts +247 -0
  312. package/src/core/importer/importer.ts +226 -0
  313. package/src/core/importer/index.ts +19 -0
  314. package/src/core/importer/lexer.ts +285 -0
  315. package/src/core/importer/names.ts +104 -0
  316. package/src/core/importer/orders.ts +307 -0
  317. package/src/core/importer/outputs.ts +245 -0
  318. package/src/core/importer/parser.ts +436 -0
  319. package/src/core/importer/presence.ts +164 -0
  320. package/src/core/importer/settle.ts +141 -0
  321. package/src/core/importer/statements.ts +331 -0
  322. package/src/core/importer/syntax.ts +144 -0
  323. package/src/core/importer/table.ts +191 -0
  324. package/src/core/importer/values.ts +241 -0
  325. package/src/core/index.ts +13 -0
  326. package/src/core/stdlib/averages/exponential.ts +3 -4
  327. package/src/core/stdlib/averages/gaussian.ts +52 -0
  328. package/src/core/stdlib/averages/shaped.ts +8 -21
  329. package/src/core/stdlib/calendar/civil.ts +5 -2
  330. package/src/core/stdlib/maths/arithmetic.ts +2 -1
  331. package/src/core/stdlib/maths/elementary.ts +20 -33
  332. package/src/core/stdlib/maths/hypot.ts +63 -0
  333. package/src/core/stdlib/maths/power.ts +57 -0
  334. package/src/core/stdlib/maths/transcendental.ts +160 -0
  335. package/src/core/stdlib/maths/trigonometric.ts +160 -0
  336. package/src/core/stdlib/series/conditions.ts +9 -12
  337. package/src/core/stdlib/series/statistics.ts +1 -0
  338. package/src/core/stdlib/values/history.ts +70 -0
  339. package/src/core/stdlib/values/index.ts +1 -0
  340. package/src/core/stdlib/values/lookback.ts +51 -47
  341. package/src/core/stdlib/values/region.ts +7 -5
  342. package/src/core/stdlib/volatility/measures.ts +6 -4
  343. package/src/core/version/version.generated.ts +1 -1
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Expressions in the source dialect, read by recursive descent.
3
+ *
4
+ * The precedence ladder is the source dialect's, and it is the same shape as
5
+ * OpenScript's (`language.md` 9.1): the ternary lowest, then `or`, `and`,
6
+ * equality, comparison, the additive and multiplicative operators, and the
7
+ * unary operators with `not` among them. So a tree read here prints back into
8
+ * OpenScript with parentheses only where the tree needs them, and never with a
9
+ * different grouping.
10
+ *
11
+ * What the reader does not accept it reports by throwing `Unreadable` with the
12
+ * place and a few words naming the form, and the statement reader turns that
13
+ * into a statement kept whole. It is the one exception in the importer, and it
14
+ * never leaves this module and the statement reader beside it.
15
+ */
16
+ import type { Token } from './lexer.js';
17
+ import { joined } from './syntax.js';
18
+ import type { Arg, Expr, Place } from './syntax.js';
19
+
20
+ /** A form the reader does not accept, and where it starts. */
21
+ export class Unreadable extends Error {
22
+ readonly at: Place;
23
+ readonly construct: string;
24
+
25
+ constructor(at: Place, construct: string) {
26
+ super(construct);
27
+ this.at = at;
28
+ this.construct = construct;
29
+ }
30
+ }
31
+
32
+ /** Words that open a statement form and are never a value. */
33
+ const STATEMENT_WORDS: Readonly<Record<string, string>> = {
34
+ if: 'an if used as a value',
35
+ switch: 'a switch used as a value',
36
+ for: 'a for loop used as a value',
37
+ while: 'a while loop used as a value',
38
+ };
39
+
40
+ /** The words the reader takes for a form it does not read, per bad token. */
41
+ export function badConstruct(token: Token): string {
42
+ if (token.text.startsWith('"') || token.text.startsWith("'")) return 'a string that is never closed';
43
+ if (token.text.startsWith('#')) return `the colour literal ${token.text}`;
44
+ return `the character ${token.text}`;
45
+ }
46
+
47
+ export const UNREAD = 'a statement in a form its reader does not accept';
48
+
49
+ export class Cursor {
50
+ index = 0;
51
+ readonly tokens: readonly Token[];
52
+
53
+ constructor(tokens: readonly Token[]) {
54
+ this.tokens = tokens;
55
+ }
56
+
57
+ peek(ahead = 0): Token | undefined {
58
+ return this.tokens[this.index + ahead];
59
+ }
60
+
61
+ /** Whether the next token is this operator or word. */
62
+ sees(text: string, ahead = 0): boolean {
63
+ const token = this.peek(ahead);
64
+ return token !== undefined && (token.kind === 'op' || token.kind === 'name') && token.text === text;
65
+ }
66
+
67
+ done(): boolean {
68
+ return this.index >= this.tokens.length;
69
+ }
70
+
71
+ take(): Token {
72
+ const token = this.tokens[this.index];
73
+ if (token === undefined) throw new Unreadable(this.endPlace(), UNREAD);
74
+ this.index += 1;
75
+ return token;
76
+ }
77
+
78
+ expect(text: string): Token {
79
+ if (!this.sees(text)) this.fail();
80
+ return this.take();
81
+ }
82
+
83
+ /** Refuse at the next token, or at the end of the line when there is none. */
84
+ fail(): never {
85
+ const token = this.peek();
86
+ if (token === undefined) throw new Unreadable(this.endPlace(), UNREAD);
87
+ if (token.kind === 'bad') throw new Unreadable(placeOf(token), badConstruct(token));
88
+ throw new Unreadable(placeOf(token), UNREAD);
89
+ }
90
+
91
+ /** Zero width, just after the last token. */
92
+ endPlace(): Place {
93
+ const last = this.tokens[this.tokens.length - 1];
94
+ return last === undefined ? { offset: 0, length: 0 } : { offset: last.offset + last.length, length: 0 };
95
+ }
96
+ }
97
+
98
+ export function placeOf(token: Token): Place {
99
+ return { offset: token.offset, length: token.length };
100
+ }
101
+
102
+ const COMPARISON = new Set(['<', '<=', '>', '>=']);
103
+ const EQUALITY = new Set(['==', '!=']);
104
+ const ADDITIVE = new Set(['+', '-']);
105
+ const MULTIPLICATIVE = new Set(['*', '/', '%']);
106
+
107
+ type Level = (c: Cursor) => Expr;
108
+
109
+ /** One left associative level of binary operators. */
110
+ function binaryLevel(operators: ReadonlySet<string>, next: Level, words = false): Level {
111
+ return (c) => {
112
+ let left = next(c);
113
+ for (;;) {
114
+ const token = c.peek();
115
+ if (token === undefined || !operators.has(token.text)) return left;
116
+ if (words ? token.kind !== 'name' : token.kind !== 'op') return left;
117
+ c.take();
118
+ const right = next(c);
119
+ left = { kind: 'binary', op: token.text, left, right, at: joined(left.at, right.at), opAt: placeOf(token) };
120
+ }
121
+ };
122
+ }
123
+
124
+ export function expression(c: Cursor): Expr {
125
+ return ternary(c);
126
+ }
127
+
128
+ function ternary(c: Cursor): Expr {
129
+ const condition = orLevel(c);
130
+ if (!c.sees('?')) return condition;
131
+ c.take();
132
+ const then = ternary(c);
133
+ c.expect(':');
134
+ const otherwise = ternary(c);
135
+ return { kind: 'ternary', condition, then, otherwise, at: joined(condition.at, otherwise.at) };
136
+ }
137
+
138
+ function unary(c: Cursor): Expr {
139
+ const token = c.peek();
140
+ if (token !== undefined) {
141
+ const sign = token.kind === 'op' && (token.text === '-' || token.text === '+');
142
+ const not = token.kind === 'name' && token.text === 'not';
143
+ if (sign || not) {
144
+ c.take();
145
+ const operand = unary(c);
146
+ return { kind: 'unary', op: token.text, operand, at: joined(placeOf(token), operand.at) };
147
+ }
148
+ }
149
+ return postfix(c);
150
+ }
151
+
152
+ const multiplicative = binaryLevel(MULTIPLICATIVE, unary);
153
+ const additive = binaryLevel(ADDITIVE, multiplicative);
154
+ const comparison = binaryLevel(COMPARISON, additive);
155
+ const equality = binaryLevel(EQUALITY, comparison);
156
+ const andLevel = binaryLevel(new Set(['and']), equality, true);
157
+ const orLevel = binaryLevel(new Set(['or']), andLevel, true);
158
+
159
+ function argumentsOf(c: Cursor): Arg[] {
160
+ const args: Arg[] = [];
161
+ if (c.sees(')')) return args;
162
+ for (;;) {
163
+ const first = c.peek();
164
+ let label: string | undefined;
165
+ if (first !== undefined && first.kind === 'name' && c.sees('=', 1)) {
166
+ label = first.text;
167
+ c.take();
168
+ c.take();
169
+ }
170
+ const value = expression(c);
171
+ args.push({ label, value, at: first === undefined ? value.at : joined(placeOf(first), value.at) });
172
+ if (!c.sees(',')) return args;
173
+ c.take();
174
+ }
175
+ }
176
+
177
+ function postfix(c: Cursor): Expr {
178
+ let expr = primary(c);
179
+ for (;;) {
180
+ if (c.sees('(')) {
181
+ c.take();
182
+ const args = argumentsOf(c);
183
+ const close = c.expect(')');
184
+ expr = { kind: 'call', callee: expr, args, at: joined(expr.at, placeOf(close)) };
185
+ } else if (c.sees('[')) {
186
+ c.take();
187
+ const index = expression(c);
188
+ const close = c.expect(']');
189
+ expr = { kind: 'index', target: expr, index, at: joined(expr.at, placeOf(close)) };
190
+ } else if (c.sees('.')) {
191
+ c.take();
192
+ const property = c.take();
193
+ if (property.kind !== 'name') throw new Unreadable(placeOf(property), UNREAD);
194
+ expr = { kind: 'member', object: expr, property: property.text, at: joined(expr.at, placeOf(property)) };
195
+ } else {
196
+ return expr;
197
+ }
198
+ }
199
+ }
200
+
201
+ function primary(c: Cursor): Expr {
202
+ const token = c.peek();
203
+ if (token === undefined) c.fail();
204
+ const at = placeOf(token);
205
+ switch (token.kind) {
206
+ case 'number':
207
+ c.take();
208
+ return { kind: 'number', text: token.text, at };
209
+ case 'string':
210
+ c.take();
211
+ return { kind: 'string', value: token.value, at };
212
+ case 'color':
213
+ c.take();
214
+ return { kind: 'color', text: token.text, at };
215
+ case 'bad':
216
+ throw new Unreadable(at, badConstruct(token));
217
+ case 'name': {
218
+ const word = STATEMENT_WORDS[token.text];
219
+ if (word !== undefined) throw new Unreadable(at, word);
220
+ c.take();
221
+ if (token.text === 'true' || token.text === 'false') {
222
+ return { kind: 'bool', value: token.text === 'true', at };
223
+ }
224
+ return { kind: 'name', name: token.text, at };
225
+ }
226
+ case 'op':
227
+ break;
228
+ }
229
+ if (token.text === '(') {
230
+ c.take();
231
+ const inner = expression(c);
232
+ const close = c.expect(')');
233
+ return { kind: 'group', inner, at: joined(at, placeOf(close)) };
234
+ }
235
+ if (token.text === '[') {
236
+ c.take();
237
+ const items: Expr[] = [];
238
+ while (!c.sees(']')) {
239
+ items.push(expression(c));
240
+ if (!c.sees(',')) break;
241
+ c.take();
242
+ }
243
+ const close = c.expect(']');
244
+ return { kind: 'list', items, at: joined(at, placeOf(close)) };
245
+ }
246
+ return c.fail();
247
+ }
@@ -0,0 +1,226 @@
1
+ /**
2
+ * The importer: a script in the source dialect in, OpenScript and findings out.
3
+ *
4
+ * The source dialect is the widely used chart scripting language whose scripts
5
+ * open with a version annotation comment, declare themselves with an
6
+ * `indicator` or `strategy` call and reach their built-ins through namespaces
7
+ * such as `ta` and `math`. Versions 5 and 6 are read.
8
+ *
9
+ * It is text to text and nothing else: no file is read, no global is touched
10
+ * and nothing is evaluated. What it cannot translate faithfully it does not
11
+ * approximate. A statement is translated whole, with the source dialect's
12
+ * meaning, or kept as a comment line per source line and reported; a
13
+ * translation whose meaning differs in a way the importer can state is written
14
+ * and reported with a warning. Every finding points into the imported text.
15
+ */
16
+ import { diagnosticFor } from '../diagnostics/index.js';
17
+ import type { Diagnostic } from '../diagnostics/index.js';
18
+ import { sourceFile } from '../source/index.js';
19
+ import type { SourceFile } from '../source/index.js';
20
+ import { Context } from './context.js';
21
+ import type { Node, Reassignment, Version } from './context.js';
22
+ import { readItems } from './lexer.js';
23
+ import type { Item, Remark } from './lexer.js';
24
+ import { binding } from './names.js';
25
+ import { declarationLine } from './orders.js';
26
+ import { readScript } from './parser.js';
27
+ import { findingsOf, settle } from './settle.js';
28
+ import type { Piece } from './settle.js';
29
+ import { translateStatement } from './statements.js';
30
+ import { pathOf } from './syntax.js';
31
+ import type { Body, CallExpr, Named, Stmt } from './syntax.js';
32
+
33
+ export interface ImportResult {
34
+ /** The OpenScript text, or the empty string where nothing could be translated. */
35
+ readonly source: string;
36
+ /** Every finding, positioned in the imported text, in the order a reader walks it. */
37
+ readonly findings: readonly Diagnostic[];
38
+ }
39
+
40
+ export interface ImportOptions {
41
+ /** What the imported text is called, for the first line of a rendered finding. */
42
+ readonly name?: string;
43
+ }
44
+
45
+ const ANNOTATION = /^\s*@version\s*=\s*(\d+)\s*$/;
46
+
47
+ /** The version annotation, if one comes before the first line of code. */
48
+ function annotationOf(items: readonly Item[]): { remark: Remark; version: string } | undefined {
49
+ for (const item of items) {
50
+ if (item.kind === 'code') return undefined;
51
+ if (item.kind !== 'remark') continue;
52
+ const found = ANNOTATION.exec(item.remark.text);
53
+ if (found !== null) return { remark: item.remark, version: found[1] ?? '' };
54
+ }
55
+ return undefined;
56
+ }
57
+
58
+ function declaresOf(stmt: Stmt): readonly Named[] {
59
+ switch (stmt.kind) {
60
+ case 'declare':
61
+ case 'function':
62
+ return [stmt.name];
63
+ case 'tuple':
64
+ return stmt.names;
65
+ case 'refused':
66
+ return stmt.declares;
67
+ default:
68
+ return [];
69
+ }
70
+ }
71
+
72
+ function nodeOf(stmt: Stmt, declaration: boolean): Node {
73
+ return {
74
+ stmt,
75
+ lines: stmt.lines,
76
+ declares: declaresOf(stmt),
77
+ reads: [],
78
+ output: [],
79
+ refusal: undefined,
80
+ failed: undefined,
81
+ lost: undefined,
82
+ notes: [],
83
+ declaration,
84
+ };
85
+ }
86
+
87
+ function visit(body: Body, each: (stmt: Stmt) => void): void {
88
+ for (const entry of body) {
89
+ if (entry.kind !== 'stmt') continue;
90
+ const stmt = entry.stmt;
91
+ each(stmt);
92
+ if (stmt.kind === 'if') {
93
+ for (const branch of stmt.branches) visit(branch.body, each);
94
+ if (stmt.otherwise !== undefined) visit(stmt.otherwise, each);
95
+ } else if (stmt.kind === 'for' || stmt.kind === 'while') visit(stmt.body, each);
96
+ else if (stmt.kind === 'switch') for (const arm of stmt.arms) visit(arm.body, each);
97
+ else if (stmt.kind === 'function' && Array.isArray(stmt.body)) visit(stmt.body as Body, each);
98
+ }
99
+ }
100
+
101
+ /** The side each literal entry id opens, read from the script's entry calls. */
102
+ function entrySides(body: Body): Map<string, 'long' | 'short'> {
103
+ const sides = new Map<string, 'long' | 'short'>();
104
+ visit(body, (stmt) => {
105
+ if (stmt.kind !== 'evaluate' || stmt.expr.kind !== 'call') return;
106
+ if (pathOf(stmt.expr.callee) !== 'strategy.entry') return;
107
+ const args = stmt.expr.args;
108
+ const id = args.find((arg) => arg.label === 'id') ?? args.find((arg, i) => arg.label === undefined && i === 0);
109
+ const side = args.find((arg) => arg.label === 'direction') ?? args.filter((arg) => arg.label === undefined)[1];
110
+ const direction = side === undefined ? undefined : pathOf(side.value);
111
+ if (id?.value.kind !== 'string') return;
112
+ if (direction === 'strategy.long') sides.set(id.value.value, 'long');
113
+ if (direction === 'strategy.short') sides.set(id.value.value, 'short');
114
+ });
115
+ return sides;
116
+ }
117
+
118
+ function reassignments(body: Body): Map<string, Reassignment[]> {
119
+ const found = new Map<string, Reassignment[]>();
120
+ visit(body, (stmt) => {
121
+ if (stmt.kind !== 'assign') return;
122
+ const held = found.get(stmt.name.name) ?? [];
123
+ held.push({ op: stmt.op, value: stmt.value });
124
+ found.set(stmt.name.name, held);
125
+ });
126
+ return found;
127
+ }
128
+
129
+ function refusal(diagnostic: Diagnostic): ImportResult {
130
+ return { source: '', findings: [diagnostic] };
131
+ }
132
+
133
+ /** Zero width, where the first statement starts: where a declaration was expected. */
134
+ function firstStatement(file: SourceFile, body: Body): Diagnostic['span'] {
135
+ const first = body.find((entry) => entry.kind === 'stmt');
136
+ return first?.kind === 'stmt' ? file.spanAt(first.stmt.at.offset, 0) : file.spanAt(file.text.length, 0);
137
+ }
138
+
139
+ function declarationOf(body: Body): { stmt: Stmt; call: CallExpr; path: string } | undefined {
140
+ for (const entry of body) {
141
+ if (entry.kind !== 'stmt' || entry.stmt.kind !== 'evaluate' || entry.stmt.expr.kind !== 'call') continue;
142
+ const path = pathOf(entry.stmt.expr.callee);
143
+ if (path === 'indicator' || path === 'strategy' || path === 'library') {
144
+ return { stmt: entry.stmt, call: entry.stmt.expr, path };
145
+ }
146
+ }
147
+ return undefined;
148
+ }
149
+
150
+ export function importScript(text: string, options: ImportOptions = {}): ImportResult {
151
+ const file = sourceFile(options.name ?? 'imported', text);
152
+ const items = readItems(file);
153
+ const annotation = annotationOf(items);
154
+ if (annotation === undefined || (annotation.version !== '5' && annotation.version !== '6')) {
155
+ const at = annotation === undefined ? file.spanAt(0, 0) : file.spanAt(annotation.remark.offset, annotation.remark.text.length + 2);
156
+ const found = annotation === undefined ? 'declares no version' : `declares version ${annotation.version}`;
157
+ return refusal(diagnosticFor('OS9001', at, { found }));
158
+ }
159
+
160
+ const body = readScript(items);
161
+ const declaration = declarationOf(body);
162
+ if (declaration === undefined) return refusal(diagnosticFor('OS9004', firstStatement(file, body), { found: 'has none' }));
163
+ if (declaration.path === 'library') {
164
+ return refusal(diagnosticFor('OS9004', file.spanAt(declaration.call.callee.at.offset, declaration.call.callee.at.length), { found: 'declares a library' }));
165
+ }
166
+
167
+ const used = new Set<string>();
168
+ for (const item of items) {
169
+ if (item.kind !== 'code') continue;
170
+ for (const token of item.logical.tokens) if (token.kind === 'name') used.add(token.text);
171
+ }
172
+ const version: Version = annotation.version === '5' ? 5 : 6;
173
+ const globals = new Set<string>();
174
+ for (const entry of body) if (entry.kind === 'stmt') for (const one of declaresOf(entry.stmt)) globals.add(one.name);
175
+ const ctx = new Context({ file, version, used, globals, reassigned: reassignments(body), entries: entrySides(body) });
176
+
177
+ const declared = nodeOf(declaration.stmt, true);
178
+ ctx.node = declared;
179
+ const trailing = declaration.stmt.remarks.map((one) => ` //${one.text}`).join('');
180
+ declared.output = [{ depth: 0, text: `${declarationLine(ctx, declaration.call, declaration.path)}${trailing}` }];
181
+ ctx.node = undefined;
182
+
183
+ const nodes: Node[] = [declared];
184
+ const pieces: Piece[] = [];
185
+ const later: Piece[] = [];
186
+ let stage: 'before' | 'header' | 'body' = 'before';
187
+ for (const entry of body) {
188
+ if (entry.kind === 'remark' && entry.remark === annotation.remark) {
189
+ pieces.push({ kind: 'text', text: 'version 1' }, { kind: 'blank' });
190
+ stage = 'header';
191
+ continue;
192
+ }
193
+ if (entry.kind === 'stmt' && entry.stmt === declaration.stmt) {
194
+ pieces.push({ kind: 'node', node: declared }, ...later);
195
+ later.length = 0;
196
+ stage = 'body';
197
+ continue;
198
+ }
199
+ const piece: Piece | undefined =
200
+ entry.kind === 'remark'
201
+ ? { kind: 'remark', remark: entry.remark, depth: 0 }
202
+ : entry.kind === 'blank'
203
+ ? { kind: 'blank' }
204
+ : undefined;
205
+ if (piece !== undefined) {
206
+ pieces.push(piece);
207
+ continue;
208
+ }
209
+ if (entry.kind !== 'stmt') continue;
210
+ const node = nodeOf(entry.stmt, false);
211
+ ctx.node = node;
212
+ node.output = translateStatement(ctx, entry.stmt, 0);
213
+ for (const one of node.declares) {
214
+ if (!ctx.fileScope.names.has(one.name)) {
215
+ ctx.fileScope.names.set(one.name, binding(ctx.namer.declare(one.name, false), 'variable', true));
216
+ }
217
+ }
218
+ ctx.node = undefined;
219
+ nodes.push(node);
220
+ if (stage === 'body') pieces.push({ kind: 'node', node });
221
+ else later.push({ kind: 'node', node });
222
+ }
223
+
224
+ const source = settle(ctx, nodes, pieces);
225
+ return { source, findings: findingsOf(nodes) };
226
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The importer: a script written in another chart language, as OpenScript.
3
+ *
4
+ * `importScript` reads a script in the version-annotated chart dialect, the one
5
+ * whose scripts open with a `//@version=` comment and reach their built-ins
6
+ * through namespaces, and returns the OpenScript text and a finding for every
7
+ * statement it kept as a comment and every translation whose meaning differs in
8
+ * a stated way. It is pure: text in, text and findings out, and nothing read,
9
+ * written or evaluated on the way.
10
+ *
11
+ * `MAPPINGS` and `COLOURS` are the table of what translates to what. They are
12
+ * behind this door for the documentation page's comparison test and for a tool
13
+ * that wants to show a reader the same table; the package surface is
14
+ * `importScript` and its two types.
15
+ */
16
+ export { importScript } from './importer.js';
17
+ export type { ImportOptions, ImportResult } from './importer.js';
18
+ export { COLOURS, MAPPINGS } from './table.js';
19
+ export type { Difference, Mapping } from './table.js';