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.
- package/CHANGELOG.md +1198 -0
- package/README.md +127 -40
- package/dist/adapters/charts/driving.d.ts +50 -0
- package/dist/adapters/charts/driving.d.ts.map +1 -0
- package/dist/adapters/charts/driving.js +57 -0
- package/dist/adapters/charts/driving.js.map +1 -0
- package/dist/adapters/charts/run.d.ts +20 -0
- package/dist/adapters/charts/run.d.ts.map +1 -1
- package/dist/adapters/charts/run.js +83 -16
- package/dist/adapters/charts/run.js.map +1 -1
- package/dist/adapters/charts/surfaces.d.ts +17 -0
- package/dist/adapters/charts/surfaces.d.ts.map +1 -1
- package/dist/adapters/charts/tables.js +82 -0
- package/dist/adapters/charts/tables.js.map +1 -1
- package/dist/adapters/charts/venue.d.ts +73 -0
- package/dist/adapters/charts/venue.d.ts.map +1 -0
- package/dist/adapters/charts/venue.js +104 -0
- package/dist/adapters/charts/venue.js.map +1 -0
- package/dist/adapters/codemirror/commands.d.ts +14 -0
- package/dist/adapters/codemirror/commands.d.ts.map +1 -0
- package/dist/adapters/codemirror/commands.js +52 -0
- package/dist/adapters/codemirror/commands.js.map +1 -0
- package/dist/adapters/codemirror/completion.d.ts +15 -0
- package/dist/adapters/codemirror/completion.d.ts.map +1 -0
- package/dist/adapters/codemirror/completion.js +64 -0
- package/dist/adapters/codemirror/completion.js.map +1 -0
- package/dist/adapters/codemirror/contract.d.ts +156 -0
- package/dist/adapters/codemirror/contract.d.ts.map +1 -0
- package/dist/adapters/codemirror/contract.js +42 -0
- package/dist/adapters/codemirror/contract.js.map +1 -0
- package/dist/adapters/codemirror/index.d.ts +43 -0
- package/dist/adapters/codemirror/index.d.ts.map +1 -0
- package/dist/adapters/codemirror/index.js +8 -0
- package/dist/adapters/codemirror/index.js.map +1 -0
- package/dist/adapters/codemirror/lint.d.ts +17 -0
- package/dist/adapters/codemirror/lint.d.ts.map +1 -0
- package/dist/adapters/codemirror/lint.js +62 -0
- package/dist/adapters/codemirror/lint.js.map +1 -0
- package/dist/adapters/codemirror/positions.d.ts +16 -0
- package/dist/adapters/codemirror/positions.d.ts.map +1 -0
- package/dist/adapters/codemirror/positions.js +65 -0
- package/dist/adapters/codemirror/positions.js.map +1 -0
- package/dist/adapters/codemirror/stream.d.ts +23 -0
- package/dist/adapters/codemirror/stream.d.ts.map +1 -0
- package/dist/adapters/codemirror/stream.js +70 -0
- package/dist/adapters/codemirror/stream.js.map +1 -0
- package/dist/adapters/codemirror/tokens.d.ts +44 -0
- package/dist/adapters/codemirror/tokens.d.ts.map +1 -0
- package/dist/adapters/codemirror/tokens.js +34 -0
- package/dist/adapters/codemirror/tokens.js.map +1 -0
- package/dist/adapters/codemirror/tooltips.d.ts +35 -0
- package/dist/adapters/codemirror/tooltips.d.ts.map +1 -0
- package/dist/adapters/codemirror/tooltips.js +108 -0
- package/dist/adapters/codemirror/tooltips.js.map +1 -0
- package/dist/core/accounting/analysis.d.ts +111 -0
- package/dist/core/accounting/analysis.d.ts.map +1 -0
- package/dist/core/accounting/analysis.js +123 -0
- package/dist/core/accounting/analysis.js.map +1 -0
- package/dist/core/accounting/charges.d.ts +136 -0
- package/dist/core/accounting/charges.d.ts.map +1 -0
- package/dist/core/accounting/charges.js +362 -0
- package/dist/core/accounting/charges.js.map +1 -0
- package/dist/core/accounting/equity.d.ts +191 -0
- package/dist/core/accounting/equity.d.ts.map +1 -0
- package/dist/core/accounting/equity.js +167 -0
- package/dist/core/accounting/equity.js.map +1 -0
- package/dist/core/accounting/index.d.ts +46 -0
- package/dist/core/accounting/index.d.ts.map +1 -0
- package/dist/core/accounting/index.js +37 -0
- package/dist/core/accounting/index.js.map +1 -0
- package/dist/core/accounting/markers.d.ts +43 -0
- package/dist/core/accounting/markers.d.ts.map +1 -0
- package/dist/core/accounting/markers.js +64 -0
- package/dist/core/accounting/markers.js.map +1 -0
- package/dist/core/accounting/monthly.d.ts +52 -0
- package/dist/core/accounting/monthly.d.ts.map +1 -0
- package/dist/core/accounting/monthly.js +99 -0
- package/dist/core/accounting/monthly.js.map +1 -0
- package/dist/core/accounting/report.d.ts +41 -0
- package/dist/core/accounting/report.d.ts.map +1 -0
- package/dist/core/accounting/report.js +65 -0
- package/dist/core/accounting/report.js.map +1 -0
- package/dist/core/accounting/shapes.d.ts +70 -0
- package/dist/core/accounting/shapes.d.ts.map +1 -0
- package/dist/core/accounting/shapes.js +21 -0
- package/dist/core/accounting/shapes.js.map +1 -0
- package/dist/core/accounting/statistics.d.ts +87 -0
- package/dist/core/accounting/statistics.d.ts.map +1 -0
- package/dist/core/accounting/statistics.js +268 -0
- package/dist/core/accounting/statistics.js.map +1 -0
- package/dist/core/accounting/trades.d.ts +118 -0
- package/dist/core/accounting/trades.d.ts.map +1 -0
- package/dist/core/accounting/trades.js +186 -0
- package/dist/core/accounting/trades.js.map +1 -0
- package/dist/core/backtest/case.d.ts +60 -0
- package/dist/core/backtest/case.d.ts.map +1 -0
- package/dist/core/backtest/case.js +319 -0
- package/dist/core/backtest/case.js.map +1 -0
- package/dist/core/backtest/compare.d.ts +38 -0
- package/dist/core/backtest/compare.d.ts.map +1 -0
- package/dist/core/backtest/compare.js +191 -0
- package/dist/core/backtest/compare.js.map +1 -0
- package/dist/core/backtest/declaration.d.ts +52 -0
- package/dist/core/backtest/declaration.d.ts.map +1 -0
- package/dist/core/backtest/declaration.js +48 -0
- package/dist/core/backtest/declaration.js.map +1 -0
- package/dist/core/backtest/deliver.d.ts +93 -0
- package/dist/core/backtest/deliver.d.ts.map +1 -0
- package/dist/core/backtest/deliver.js +94 -0
- package/dist/core/backtest/deliver.js.map +1 -0
- package/dist/core/backtest/drive.d.ts +87 -0
- package/dist/core/backtest/drive.d.ts.map +1 -0
- package/dist/core/backtest/drive.js +312 -0
- package/dist/core/backtest/drive.js.map +1 -0
- package/dist/core/backtest/index.d.ts +58 -0
- package/dist/core/backtest/index.d.ts.map +1 -0
- package/dist/core/backtest/index.js +47 -0
- package/dist/core/backtest/index.js.map +1 -0
- package/dist/core/backtest/range.d.ts +84 -0
- package/dist/core/backtest/range.d.ts.map +1 -0
- package/dist/core/backtest/range.js +90 -0
- package/dist/core/backtest/range.js.map +1 -0
- package/dist/core/backtest/record.d.ts +299 -0
- package/dist/core/backtest/record.d.ts.map +1 -0
- package/dist/core/backtest/record.js +243 -0
- package/dist/core/backtest/record.js.map +1 -0
- package/dist/core/backtest/replay.d.ts +48 -0
- package/dist/core/backtest/replay.d.ts.map +1 -0
- package/dist/core/backtest/replay.js +137 -0
- package/dist/core/backtest/replay.js.map +1 -0
- package/dist/core/backtest/resting.d.ts +62 -0
- package/dist/core/backtest/resting.d.ts.map +1 -0
- package/dist/core/backtest/resting.js +59 -0
- package/dist/core/backtest/resting.js.map +1 -0
- package/dist/core/backtest/settings.d.ts +117 -0
- package/dist/core/backtest/settings.d.ts.map +1 -0
- package/dist/core/backtest/settings.js +207 -0
- package/dist/core/backtest/settings.js.map +1 -0
- package/dist/core/backtest/simulate.d.ts +258 -0
- package/dist/core/backtest/simulate.d.ts.map +1 -0
- package/dist/core/backtest/simulate.js +335 -0
- package/dist/core/backtest/simulate.js.map +1 -0
- package/dist/core/catalogue/catalogue.generated.d.ts +66 -0
- package/dist/core/catalogue/catalogue.generated.d.ts.map +1 -1
- package/dist/core/catalogue/catalogue.generated.js +6 -0
- package/dist/core/catalogue/catalogue.generated.js.map +1 -1
- package/dist/core/catalogue/values.generated.d.ts +24 -0
- package/dist/core/catalogue/values.generated.d.ts.map +1 -1
- package/dist/core/check/index.d.ts +2 -1
- package/dist/core/check/index.d.ts.map +1 -1
- package/dist/core/check/index.js +1 -1
- package/dist/core/check/index.js.map +1 -1
- package/dist/core/check/library-orders.js +2 -2
- package/dist/core/check/library-orders.js.map +1 -1
- package/dist/core/check/library-prose.generated.d.ts +16 -0
- package/dist/core/check/library-prose.generated.d.ts.map +1 -0
- package/dist/core/check/library-prose.generated.js +353 -0
- package/dist/core/check/library-prose.generated.js.map +1 -0
- package/dist/core/check/surface.d.ts +24 -0
- package/dist/core/check/surface.d.ts.map +1 -1
- package/dist/core/check/surface.js +29 -0
- package/dist/core/check/surface.js.map +1 -1
- package/dist/core/emit/canonical.d.ts +22 -8
- package/dist/core/emit/canonical.d.ts.map +1 -1
- package/dist/core/emit/canonical.js +67 -6
- package/dist/core/emit/canonical.js.map +1 -1
- package/dist/core/emit/defaults.d.ts +35 -16
- package/dist/core/emit/defaults.d.ts.map +1 -1
- package/dist/core/emit/defaults.js +66 -0
- package/dist/core/emit/defaults.js.map +1 -1
- package/dist/core/emit/index.d.ts +2 -0
- package/dist/core/emit/index.d.ts.map +1 -1
- package/dist/core/emit/index.js +2 -0
- package/dist/core/emit/index.js.map +1 -1
- package/dist/core/engine/arithmetic.d.ts +6 -25
- package/dist/core/engine/arithmetic.d.ts.map +1 -1
- package/dist/core/engine/arithmetic.js +48 -3
- package/dist/core/engine/arithmetic.js.map +1 -1
- package/dist/core/engine/index.d.ts +2 -2
- package/dist/core/engine/index.d.ts.map +1 -1
- package/dist/core/engine/index.js +2 -2
- package/dist/core/engine/index.js.map +1 -1
- package/dist/core/engine/library/arrays.d.ts.map +1 -1
- package/dist/core/engine/library/arrays.js +8 -2
- package/dist/core/engine/library/arrays.js.map +1 -1
- package/dist/core/engine/library/code-points.d.ts +40 -0
- package/dist/core/engine/library/code-points.d.ts.map +1 -0
- package/dist/core/engine/library/code-points.js +74 -0
- package/dist/core/engine/library/code-points.js.map +1 -0
- package/dist/core/engine/library/index.d.ts +5 -0
- package/dist/core/engine/library/index.d.ts.map +1 -1
- package/dist/core/engine/library/index.js +5 -0
- package/dist/core/engine/library/index.js.map +1 -1
- package/dist/core/engine/library/text.d.ts.map +1 -1
- package/dist/core/engine/library/text.js +51 -34
- package/dist/core/engine/library/text.js.map +1 -1
- package/dist/core/engine/load.d.ts +35 -1
- package/dist/core/engine/load.d.ts.map +1 -1
- package/dist/core/engine/load.js +63 -0
- package/dist/core/engine/load.js.map +1 -1
- package/dist/core/engine/verify-tables.d.ts.map +1 -1
- package/dist/core/engine/verify-tables.js +41 -0
- package/dist/core/engine/verify-tables.js.map +1 -1
- package/dist/core/index.d.ts +43 -4
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +23 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/stdlib/index.d.ts +1 -1
- package/dist/core/stdlib/index.d.ts.map +1 -1
- package/dist/core/stdlib/index.js +1 -1
- package/dist/core/stdlib/index.js.map +1 -1
- package/dist/core/stdlib/maths/index.d.ts +1 -1
- package/dist/core/stdlib/maths/index.d.ts.map +1 -1
- package/dist/core/stdlib/maths/index.js +1 -1
- package/dist/core/stdlib/maths/index.js.map +1 -1
- package/dist/core/stdlib/maths/rounding.d.ts +5 -0
- package/dist/core/stdlib/maths/rounding.d.ts.map +1 -1
- package/dist/core/stdlib/maths/rounding.js +19 -1
- package/dist/core/stdlib/maths/rounding.js.map +1 -1
- package/dist/core/version/version.generated.d.ts +1 -1
- package/dist/core/version/version.generated.js +1 -1
- package/dist/editor/complete.d.ts +40 -0
- package/dist/editor/complete.d.ts.map +1 -0
- package/dist/editor/complete.js +206 -0
- package/dist/editor/complete.js.map +1 -0
- package/dist/editor/diagnose.d.ts +18 -0
- package/dist/editor/diagnose.d.ts.map +1 -0
- package/dist/editor/diagnose.js +70 -0
- package/dist/editor/diagnose.js.map +1 -0
- package/dist/editor/format.d.ts +11 -0
- package/dist/editor/format.d.ts.map +1 -0
- package/dist/editor/format.js +50 -0
- package/dist/editor/format.js.map +1 -0
- package/dist/editor/highlight.d.ts +28 -0
- package/dist/editor/highlight.d.ts.map +1 -0
- package/dist/editor/highlight.js +65 -0
- package/dist/editor/highlight.js.map +1 -0
- package/dist/editor/hover.d.ts +40 -0
- package/dist/editor/hover.d.ts.map +1 -0
- package/dist/editor/hover.js +147 -0
- package/dist/editor/hover.js.map +1 -0
- package/dist/editor/index.d.ts +60 -0
- package/dist/editor/index.d.ts.map +1 -0
- package/dist/editor/index.js +7 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/editor/kinds.d.ts +27 -0
- package/dist/editor/kinds.d.ts.map +1 -0
- package/dist/editor/kinds.js +118 -0
- package/dist/editor/kinds.js.map +1 -0
- package/dist/editor/layout.d.ts +47 -0
- package/dist/editor/layout.d.ts.map +1 -0
- package/dist/editor/layout.js +135 -0
- package/dist/editor/layout.js.map +1 -0
- package/dist/editor/manifest.d.ts +47 -0
- package/dist/editor/manifest.d.ts.map +1 -0
- package/dist/editor/manifest.js +93 -0
- package/dist/editor/manifest.js.map +1 -0
- package/dist/editor/reading.d.ts +41 -0
- package/dist/editor/reading.d.ts.map +1 -0
- package/dist/editor/reading.js +51 -0
- package/dist/editor/reading.js.map +1 -0
- package/dist/editor/scan.d.ts +26 -0
- package/dist/editor/scan.d.ts.map +1 -0
- package/dist/editor/scan.js +139 -0
- package/dist/editor/scan.js.map +1 -0
- package/dist/editor/scope.d.ts +19 -0
- package/dist/editor/scope.d.ts.map +1 -0
- package/dist/editor/scope.js +99 -0
- package/dist/editor/scope.js.map +1 -0
- package/dist/editor/signature.d.ts +38 -0
- package/dist/editor/signature.d.ts.map +1 -0
- package/dist/editor/signature.js +112 -0
- package/dist/editor/signature.js.map +1 -0
- package/dist/editor/site.d.ts +46 -0
- package/dist/editor/site.d.ts.map +1 -0
- package/dist/editor/site.js +184 -0
- package/dist/editor/site.js.map +1 -0
- package/dist/editor/spacing.d.ts +29 -0
- package/dist/editor/spacing.d.ts.map +1 -0
- package/dist/editor/spacing.js +116 -0
- package/dist/editor/spacing.js.map +1 -0
- package/package.json +42 -3
- package/spec/README.md +2 -1
- package/spec/errors.json +141 -1
- package/src/adapters/charts/driving.ts +109 -0
- package/src/adapters/charts/run.ts +120 -28
- package/src/adapters/charts/surfaces.ts +17 -0
- package/src/adapters/charts/tables.ts +88 -0
- package/src/adapters/charts/venue.ts +132 -0
- package/src/adapters/codemirror/commands.ts +53 -0
- package/src/adapters/codemirror/completion.ts +74 -0
- package/src/adapters/codemirror/contract.ts +156 -0
- package/src/adapters/codemirror/index.ts +66 -0
- package/src/adapters/codemirror/lint.ts +66 -0
- package/src/adapters/codemirror/positions.ts +79 -0
- package/src/adapters/codemirror/stream.ts +87 -0
- package/src/adapters/codemirror/tokens.ts +64 -0
- package/src/adapters/codemirror/tooltips.ts +113 -0
- package/src/core/accounting/analysis.ts +188 -0
- package/src/core/accounting/charges.ts +452 -0
- package/src/core/accounting/equity.ts +314 -0
- package/src/core/accounting/index.ts +51 -0
- package/src/core/accounting/markers.ts +95 -0
- package/src/core/accounting/monthly.ts +137 -0
- package/src/core/accounting/report.ts +94 -0
- package/src/core/accounting/shapes.ts +73 -0
- package/src/core/accounting/statistics.ts +387 -0
- package/src/core/accounting/trades.ts +313 -0
- package/src/core/backtest/case.ts +395 -0
- package/src/core/backtest/compare.ts +246 -0
- package/src/core/backtest/declaration.ts +97 -0
- package/src/core/backtest/deliver.ts +161 -0
- package/src/core/backtest/drive.ts +468 -0
- package/src/core/backtest/index.ts +64 -0
- package/src/core/backtest/range.ts +137 -0
- package/src/core/backtest/record.ts +470 -0
- package/src/core/backtest/replay.ts +168 -0
- package/src/core/backtest/resting.ts +125 -0
- package/src/core/backtest/settings.ts +280 -0
- package/src/core/backtest/simulate.ts +499 -0
- package/src/core/catalogue/catalogue.generated.ts +6 -0
- package/src/core/catalogue/values.generated.ts +6 -0
- package/src/core/check/index.ts +3 -0
- package/src/core/check/library-orders.ts +2 -2
- package/src/core/check/library-prose.generated.ts +367 -0
- package/src/core/check/surface.ts +34 -0
- package/src/core/emit/canonical.ts +67 -9
- package/src/core/emit/defaults.ts +70 -0
- package/src/core/emit/index.ts +2 -0
- package/src/core/engine/arithmetic.ts +23 -3
- package/src/core/engine/index.ts +2 -2
- package/src/core/engine/library/arrays.ts +8 -2
- package/src/core/engine/library/code-points.ts +73 -0
- package/src/core/engine/library/index.ts +6 -0
- package/src/core/engine/library/text.ts +53 -35
- package/src/core/engine/load.ts +84 -2
- package/src/core/engine/verify-tables.ts +43 -0
- package/src/core/index.ts +100 -2
- package/src/core/stdlib/index.ts +1 -0
- package/src/core/stdlib/maths/index.ts +1 -0
- package/src/core/stdlib/maths/rounding.ts +23 -1
- package/src/core/version/version.generated.ts +1 -1
- package/src/editor/complete.ts +266 -0
- package/src/editor/diagnose.ts +71 -0
- package/src/editor/format.ts +85 -0
- package/src/editor/highlight.ts +110 -0
- package/src/editor/hover.ts +199 -0
- package/src/editor/index.ts +65 -0
- package/src/editor/kinds.ts +157 -0
- package/src/editor/layout.ts +189 -0
- package/src/editor/manifest.ts +119 -0
- package/src/editor/reading.ts +69 -0
- package/src/editor/scan.ts +162 -0
- package/src/editor/scope.ts +122 -0
- package/src/editor/signature.ts +150 -0
- package/src/editor/site.ts +233 -0
- 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
|
-
|
|
106
|
-
|
|
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);
|
package/src/core/engine/index.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
|
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
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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 {
|
|
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
|
|
30
|
-
if (typeof value === 'boolean') return
|
|
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
|
|
34
|
-
return `#${
|
|
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
|
-
* **
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
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 *
|
|
143
|
+
const scaled = roundHalfAway(x * scaleOf(decimals));
|
|
122
144
|
const usable = Number.isFinite(scaled);
|
|
123
145
|
const sign = (usable ? scaled < 0 : x < 0) ? '-' : '';
|
|
124
|
-
//
|
|
125
|
-
//
|
|
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)
|
|
128
|
-
: spread(Math.abs(x)
|
|
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)
|
|
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
|
|
163
|
-
if (!NUMERIC.test(
|
|
164
|
-
const parsed = Number(
|
|
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,
|
|
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))),
|
package/src/core/engine/load.ts
CHANGED
|
@@ -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';
|
package/src/core/stdlib/index.ts
CHANGED