@voxgig/sdkgen-langpack 0.0.1
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/.sdk/model/target/dart.aon +63 -0
- package/.sdk/model/target/haskell.aon +72 -0
- package/.sdk/model/target/lean.aon +91 -0
- package/.sdk/src/cmp/dart/Config_dart.ts +248 -0
- package/.sdk/src/cmp/dart/EntityBase_dart.ts +34 -0
- package/.sdk/src/cmp/dart/EntityOperation_dart.ts +43 -0
- package/.sdk/src/cmp/dart/EntityTypes_dart.ts +207 -0
- package/.sdk/src/cmp/dart/Entity_dart.ts +87 -0
- package/.sdk/src/cmp/dart/Gitignore_dart.ts +34 -0
- package/.sdk/src/cmp/dart/MainEntity_dart.ts +33 -0
- package/.sdk/src/cmp/dart/Main_dart.ts +178 -0
- package/.sdk/src/cmp/dart/Package_dart.ts +82 -0
- package/.sdk/src/cmp/dart/ReadmeEntity_dart.ts +177 -0
- package/.sdk/src/cmp/dart/ReadmeExamplesTest_dart.ts +238 -0
- package/.sdk/src/cmp/dart/ReadmeExplanation_dart.ts +53 -0
- package/.sdk/src/cmp/dart/ReadmeHowto_dart.ts +152 -0
- package/.sdk/src/cmp/dart/ReadmeInstall_dart.ts +59 -0
- package/.sdk/src/cmp/dart/ReadmeIntro_dart.ts +65 -0
- package/.sdk/src/cmp/dart/ReadmeModel_dart.ts +151 -0
- package/.sdk/src/cmp/dart/ReadmeOptions_dart.ts +63 -0
- package/.sdk/src/cmp/dart/ReadmeQuick_dart.ts +225 -0
- package/.sdk/src/cmp/dart/ReadmeRef_dart.ts +397 -0
- package/.sdk/src/cmp/dart/ReadmeTopHowto_dart.ts +24 -0
- package/.sdk/src/cmp/dart/ReadmeTopQuick_dart.ts +106 -0
- package/.sdk/src/cmp/dart/ReadmeTopTest_dart.ts +78 -0
- package/.sdk/src/cmp/dart/SdkError_dart.ts +42 -0
- package/.sdk/src/cmp/dart/TestDirect_dart.ts +516 -0
- package/.sdk/src/cmp/dart/TestEntity_dart.ts +628 -0
- package/.sdk/src/cmp/dart/Test_dart.ts +138 -0
- package/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +98 -0
- package/.sdk/src/cmp/dart/fragment/Config.fragment.dart +70 -0
- package/.sdk/src/cmp/dart/fragment/Direct.test.fragment.dart +25 -0
- package/.sdk/src/cmp/dart/fragment/Entity.fragment.dart +29 -0
- package/.sdk/src/cmp/dart/fragment/Entity.test.fragment.dart +28 -0
- package/.sdk/src/cmp/dart/fragment/EntityBase.fragment.dart +302 -0
- package/.sdk/src/cmp/dart/fragment/EntityCreateOp.fragment.dart +98 -0
- package/.sdk/src/cmp/dart/fragment/EntityListOp.fragment.dart +90 -0
- package/.sdk/src/cmp/dart/fragment/EntityLoadOp.fragment.dart +102 -0
- package/.sdk/src/cmp/dart/fragment/EntityRemoveOp.fragment.dart +108 -0
- package/.sdk/src/cmp/dart/fragment/EntityUpdateOp.fragment.dart +102 -0
- package/.sdk/src/cmp/dart/fragment/Main.fragment.dart +339 -0
- package/.sdk/src/cmp/dart/fragment/SdkError.fragment.dart +26 -0
- package/.sdk/src/cmp/dart/tsconfig.json +15 -0
- package/.sdk/src/cmp/dart/utility_dart.ts +155 -0
- package/.sdk/src/cmp/haskell/Config_haskell.ts +98 -0
- package/.sdk/src/cmp/haskell/Entity_haskell.ts +13 -0
- package/.sdk/src/cmp/haskell/Gitignore_haskell.ts +27 -0
- package/.sdk/src/cmp/haskell/Main_haskell.ts +123 -0
- package/.sdk/src/cmp/haskell/Package_haskell.ts +60 -0
- package/.sdk/src/cmp/haskell/ReadmeEntity_haskell.ts +186 -0
- package/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +135 -0
- package/.sdk/src/cmp/haskell/ReadmeExplanation_haskell.ts +48 -0
- package/.sdk/src/cmp/haskell/ReadmeHowto_haskell.ts +168 -0
- package/.sdk/src/cmp/haskell/ReadmeInstall_haskell.ts +51 -0
- package/.sdk/src/cmp/haskell/ReadmeIntro_haskell.ts +67 -0
- package/.sdk/src/cmp/haskell/ReadmeModel_haskell.ts +162 -0
- package/.sdk/src/cmp/haskell/ReadmeOptions_haskell.ts +74 -0
- package/.sdk/src/cmp/haskell/ReadmeQuick_haskell.ts +241 -0
- package/.sdk/src/cmp/haskell/ReadmeRef_haskell.ts +408 -0
- package/.sdk/src/cmp/haskell/ReadmeTopHowto_haskell.ts +31 -0
- package/.sdk/src/cmp/haskell/ReadmeTopQuick_haskell.ts +104 -0
- package/.sdk/src/cmp/haskell/ReadmeTopTest_haskell.ts +80 -0
- package/.sdk/src/cmp/haskell/Test_haskell.ts +297 -0
- package/.sdk/src/cmp/haskell/tsconfig.json +15 -0
- package/.sdk/src/cmp/haskell/utility_haskell.ts +166 -0
- package/.sdk/src/cmp/lean/Config_lean.ts +93 -0
- package/.sdk/src/cmp/lean/Entity_lean.ts +9 -0
- package/.sdk/src/cmp/lean/Gitignore_lean.ts +18 -0
- package/.sdk/src/cmp/lean/Main_lean.ts +120 -0
- package/.sdk/src/cmp/lean/Package_lean.ts +93 -0
- package/.sdk/src/cmp/lean/ReadmeEntity_lean.ts +29 -0
- package/.sdk/src/cmp/lean/ReadmeExplanation_lean.ts +19 -0
- package/.sdk/src/cmp/lean/ReadmeHowto_lean.ts +25 -0
- package/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +39 -0
- package/.sdk/src/cmp/lean/ReadmeIntro_lean.ts +25 -0
- package/.sdk/src/cmp/lean/ReadmeModel_lean.ts +24 -0
- package/.sdk/src/cmp/lean/ReadmeOptions_lean.ts +22 -0
- package/.sdk/src/cmp/lean/ReadmeQuick_lean.ts +50 -0
- package/.sdk/src/cmp/lean/ReadmeRef_lean.ts +40 -0
- package/.sdk/src/cmp/lean/ReadmeTopHowto_lean.ts +16 -0
- package/.sdk/src/cmp/lean/ReadmeTopQuick_lean.ts +23 -0
- package/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +19 -0
- package/.sdk/src/cmp/lean/Test_lean.ts +261 -0
- package/.sdk/src/cmp/lean/utility_lean.ts +129 -0
- package/.sdk/tm/dart/LICENSE +22 -0
- package/.sdk/tm/dart/Makefile +50 -0
- package/.sdk/tm/dart/lib/Context.dart +150 -0
- package/.sdk/tm/dart/lib/Control.dart +15 -0
- package/.sdk/tm/dart/lib/Operation.dart +31 -0
- package/.sdk/tm/dart/lib/Point.dart +51 -0
- package/.sdk/tm/dart/lib/Response.dart +32 -0
- package/.sdk/tm/dart/lib/Result.dart +39 -0
- package/.sdk/tm/dart/lib/Spec.dart +49 -0
- package/.sdk/tm/dart/lib/feature/audit/AuditFeature.dart +103 -0
- package/.sdk/tm/dart/lib/feature/base/BaseFeature.dart +76 -0
- package/.sdk/tm/dart/lib/feature/cache/CacheFeature.dart +152 -0
- package/.sdk/tm/dart/lib/feature/clienttrack/ClienttrackFeature.dart +104 -0
- package/.sdk/tm/dart/lib/feature/cost/CostFeature.dart +446 -0
- package/.sdk/tm/dart/lib/feature/debug/DebugFeature.dart +153 -0
- package/.sdk/tm/dart/lib/feature/idempotency/IdempotencyFeature.dart +92 -0
- package/.sdk/tm/dart/lib/feature/log/LogFeature.dart +85 -0
- package/.sdk/tm/dart/lib/feature/metrics/MetricsFeature.dart +103 -0
- package/.sdk/tm/dart/lib/feature/netsim/NetsimFeature.dart +186 -0
- package/.sdk/tm/dart/lib/feature/paging/PagingFeature.dart +240 -0
- package/.sdk/tm/dart/lib/feature/proxy/ProxyFeature.dart +118 -0
- package/.sdk/tm/dart/lib/feature/ratelimit/RatelimitFeature.dart +103 -0
- package/.sdk/tm/dart/lib/feature/rbac/RbacFeature.dart +121 -0
- package/.sdk/tm/dart/lib/feature/retry/RetryFeature.dart +155 -0
- package/.sdk/tm/dart/lib/feature/secrets/SecretsFeature.dart +649 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/capability.dart +105 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/catalog.dart +46 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/config.dart +297 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/depend.dart +198 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/env.dart +144 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/export.dart +58 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/graph.dart +160 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/host.dart +1067 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/order.dart +172 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/plugin.dart +23 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/point.dart +117 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/ref.dart +105 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/resolve.dart +58 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/types.dart +201 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/version.dart +116 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/aws.dart +285 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/azuresecrets.dart +178 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/boru.dart +145 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/crypto.dart +194 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/doppler.dart +101 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/gcpsecrets.dart +143 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/hashicorp.dart +190 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/httpjson.dart +363 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/infisical.dart +149 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/onepassword.dart +146 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/secretspec.dart +116 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/sigv4.dart +237 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/addr.dart +138 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/json.dart +276 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/provider.dart +31 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/providers.dart +243 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/sekreto.dart +730 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/spec.dart +200 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/support.dart +217 -0
- package/.sdk/tm/dart/lib/feature/streaming/StreamingFeature.dart +97 -0
- package/.sdk/tm/dart/lib/feature/telemetry/TelemetryFeature.dart +136 -0
- package/.sdk/tm/dart/lib/feature/test/TestFeature.dart +328 -0
- package/.sdk/tm/dart/lib/feature/timeout/TimeoutFeature.dart +104 -0
- package/.sdk/tm/dart/lib/utility/CleanUtility.dart +12 -0
- package/.sdk/tm/dart/lib/utility/DoneUtility.dart +18 -0
- package/.sdk/tm/dart/lib/utility/ErrUtility.dart +46 -0
- package/.sdk/tm/dart/lib/utility/FeatureAddUtility.dart +35 -0
- package/.sdk/tm/dart/lib/utility/FeatureHookUtility.dart +20 -0
- package/.sdk/tm/dart/lib/utility/FeatureInitUtility.dart +9 -0
- package/.sdk/tm/dart/lib/utility/FetcherUtility.dart +104 -0
- package/.sdk/tm/dart/lib/utility/GraphqlUtility.dart +162 -0
- package/.sdk/tm/dart/lib/utility/MakeContextUtility.dart +6 -0
- package/.sdk/tm/dart/lib/utility/MakeErrorUtility.dart +71 -0
- package/.sdk/tm/dart/lib/utility/MakeFetchDefUtility.dart +42 -0
- package/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +233 -0
- package/.sdk/tm/dart/lib/utility/MakePointUtility.dart +130 -0
- package/.sdk/tm/dart/lib/utility/MakeRequestUtility.dart +66 -0
- package/.sdk/tm/dart/lib/utility/MakeResponseUtility.dart +59 -0
- package/.sdk/tm/dart/lib/utility/MakeResultUtility.dart +49 -0
- package/.sdk/tm/dart/lib/utility/MakeSpecUtility.dart +77 -0
- package/.sdk/tm/dart/lib/utility/MakeUrlUtility.dart +51 -0
- package/.sdk/tm/dart/lib/utility/ParamUtility.dart +56 -0
- package/.sdk/tm/dart/lib/utility/PrepareAuthUtility.dart +46 -0
- package/.sdk/tm/dart/lib/utility/PrepareBodyUtility.dart +19 -0
- package/.sdk/tm/dart/lib/utility/PrepareHeadersUtility.dart +11 -0
- package/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +30 -0
- package/.sdk/tm/dart/lib/utility/PrepareParamsUtility.dart +23 -0
- package/.sdk/tm/dart/lib/utility/PreparePathUtility.dart +9 -0
- package/.sdk/tm/dart/lib/utility/PrepareQueryUtility.dart +18 -0
- package/.sdk/tm/dart/lib/utility/ResultBasicUtility.dart +29 -0
- package/.sdk/tm/dart/lib/utility/ResultBodyUtility.dart +15 -0
- package/.sdk/tm/dart/lib/utility/ResultHeadersUtility.dart +18 -0
- package/.sdk/tm/dart/lib/utility/StructUtility.dart +127 -0
- package/.sdk/tm/dart/lib/utility/TransformRequestUtility.dart +28 -0
- package/.sdk/tm/dart/lib/utility/TransformResponseUtility.dart +44 -0
- package/.sdk/tm/dart/lib/utility/Utility.dart +239 -0
- package/.sdk/tm/dart/lib/utility/voxgig_struct.dart +2462 -0
- package/.sdk/tm/dart/src/feature/README.md +3 -0
- package/.sdk/tm/dart/src/feature/audit/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/base/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/cache/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/clienttrack/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/cost/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/debug/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/idempotency/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/log/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/metrics/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/netsim/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/paging/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/proxy/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/ratelimit/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/rbac/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/retry/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/secrets/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/streaming/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/telemetry/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/test/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/timeout/.gitkeep +0 -0
- package/.sdk/tm/dart/test/custom_test.dart +65 -0
- package/.sdk/tm/dart/test/exists_test.dart +12 -0
- package/.sdk/tm/dart/test/feature/harness.dart +347 -0
- package/.sdk/tm/dart/test/feature/secrets/secrets_test.dart +1107 -0
- package/.sdk/tm/dart/test/feature_test.dart +1193 -0
- package/.sdk/tm/dart/test/harness.dart +153 -0
- package/.sdk/tm/dart/test/netsim_test.dart +48 -0
- package/.sdk/tm/dart/test/omni.dart +520 -0
- package/.sdk/tm/dart/test/omni_smoke_test.dart +143 -0
- package/.sdk/tm/dart/test/pipeline_test.dart +608 -0
- package/.sdk/tm/dart/test/primary_test.dart +476 -0
- package/.sdk/tm/dart/test/sdk-test-control.json +19 -0
- package/.sdk/tm/dart/test/struct_test.dart +468 -0
- package/.sdk/tm/dart/test/utility.dart +198 -0
- package/.sdk/tm/dart/test/vendor/omni/omni.dart +7 -0
- package/.sdk/tm/dart/test/vendor/omni/runner.dart +653 -0
- package/.sdk/tm/dart/test/vendor/omni/util.dart +232 -0
- package/.sdk/tm/haskell/LICENSE +21 -0
- package/.sdk/tm/haskell/Makefile +22 -0
- package/.sdk/tm/haskell/VERSION +1 -0
- package/.sdk/tm/haskell/src/SdkFeatures.hs +1440 -0
- package/.sdk/tm/haskell/src/SdkHelpers.hs +337 -0
- package/.sdk/tm/haskell/src/SdkJson.hs +111 -0
- package/.sdk/tm/haskell/src/SdkRuntime.hs +1279 -0
- package/.sdk/tm/haskell/src/SdkTypes.hs +195 -0
- package/.sdk/tm/haskell/src/VoxgigStruct.hs +2299 -0
- package/.sdk/tm/haskell/src/Vregex.hs +237 -0
- package/.sdk/tm/haskell/src/feature/README.md +5 -0
- package/.sdk/tm/haskell/src/feature/audit/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/base/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/cache/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/clienttrack/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/debug/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/idempotency/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/log/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/metrics/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/netsim/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/paging/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/proxy/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/ratelimit/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/rbac/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/retry/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/streaming/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/telemetry/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/test/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/timeout/.gitkeep +0 -0
- package/.sdk/tm/haskell/test/Harness.hs +281 -0
- package/.sdk/tm/haskell/test/OmniResolver.hs +252 -0
- package/.sdk/tm/haskell/test/Runner.hs +49 -0
- package/.sdk/tm/haskell/test/StructCorpus.hs +449 -0
- package/.sdk/tm/haskell/test/TCustomUtility.hs +38 -0
- package/.sdk/tm/haskell/test/TFeature.hs +794 -0
- package/.sdk/tm/haskell/test/TNetsim.hs +50 -0
- package/.sdk/tm/haskell/test/TPipeline.hs +334 -0
- package/.sdk/tm/haskell/test/TPrimaryCorpus.hs +263 -0
- package/.sdk/tm/haskell/test/TPrimaryUtility.hs +386 -0
- package/.sdk/tm/haskell/test/Testutil.hs +59 -0
- package/.sdk/tm/haskell/test/vendor/omni/Omni.hs +1147 -0
- package/.sdk/tm/lean/LICENSE +21 -0
- package/.sdk/tm/lean/Makefile +32 -0
- package/.sdk/tm/lean/VERSION +1 -0
- package/.sdk/tm/lean/src/SdkFeature.lean +171 -0
- package/.sdk/tm/lean/src/SdkFeatures.lean +787 -0
- package/.sdk/tm/lean/src/SdkJson.lean +134 -0
- package/.sdk/tm/lean/src/SdkRuntime.lean +374 -0
- package/.sdk/tm/lean/src/SdkUtility.lean +809 -0
- package/.sdk/tm/lean/src/VoxgigStruct.lean +2880 -0
- package/.sdk/tm/lean/src/Vregex.lean +412 -0
- package/.sdk/tm/lean/src/feature/README.md +5 -0
- package/.sdk/tm/lean/src/feature/audit/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/base/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/cache/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/clienttrack/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/cost/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/debug/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/idempotency/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/log/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/metrics/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/netsim/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/paging/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/proxy/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/ratelimit/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/rbac/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/retry/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/streaming/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/telemetry/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/test/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/timeout/.gitkeep +0 -0
- package/.sdk/tm/lean/test/OmniResolver.lean +358 -0
- package/.sdk/tm/lean/test/OmniSmoke.lean +174 -0
- package/.sdk/tm/lean/test/StructCorpus.lean +368 -0
- package/.sdk/tm/lean/test/TFeature.lean +281 -0
- package/.sdk/tm/lean/test/TPrimaryUtility.lean +227 -0
- package/.sdk/tm/lean/test/vendor/omni/Omni.lean +997 -0
- package/LICENSE +21 -0
- package/README.md +104 -0
- package/package.json +78 -0
- package/sdkgen-package.json +22 -0
|
@@ -0,0 +1,2880 @@
|
|
|
1
|
+
/- Copyright (c) 2025-2026 Voxgig Ltd. MIT LICENSE.
|
|
2
|
+
|
|
3
|
+
Voxgig Struct — Lean 4 port.
|
|
4
|
+
|
|
5
|
+
A faithful port of the canonical TypeScript implementation
|
|
6
|
+
(typescript/src/StructUtility.ts), following the OCaml port's structure.
|
|
7
|
+
Like TypeScript (and the OCaml, Rust and Haskell ports), Lean keeps
|
|
8
|
+
`undefined` (noval) and JSON `null` (null) distinct, so this port mirrors
|
|
9
|
+
the canonical TS logic directly.
|
|
10
|
+
|
|
11
|
+
Nodes are mutable and reference-stable: because Lean's strict positivity
|
|
12
|
+
rules forbid `IO.Ref` fields inside the `Value` inductive, lists and maps
|
|
13
|
+
are handles (indices) into a global mutable heap (the same design as the
|
|
14
|
+
Elixir port's ETS heap). Function values are likewise handles into a
|
|
15
|
+
registry, and the mutable `Injection` state lives in its own arena. The
|
|
16
|
+
whole API therefore runs in `IO` (as in the Haskell port). Zero
|
|
17
|
+
third-party runtime dependencies; the regex helper is the in-tree Vregex
|
|
18
|
+
engine (RE2 subset). -/
|
|
19
|
+
|
|
20
|
+
import Vregex
|
|
21
|
+
|
|
22
|
+
namespace VoxgigStruct
|
|
23
|
+
|
|
24
|
+
-- ---------------------------------------------------------------------------
|
|
25
|
+
-- Value model
|
|
26
|
+
-- ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
/-- A JSON-shaped dynamic value. `noval` is TS `undefined` (property absent);
|
|
29
|
+
`null` is JSON null. `list`/`map` are handles into the global node heap;
|
|
30
|
+
`func` is a handle into the injector registry. -/
|
|
31
|
+
inductive Value where
|
|
32
|
+
| noval
|
|
33
|
+
| null
|
|
34
|
+
| bool (b : Bool)
|
|
35
|
+
| num (n : Float)
|
|
36
|
+
| str (s : String)
|
|
37
|
+
| list (id : Nat)
|
|
38
|
+
| map (id : Nat)
|
|
39
|
+
| func (fid : Nat)
|
|
40
|
+
| sentinel (tag : String) -- SKIP / DELETE, by tag
|
|
41
|
+
deriving Repr
|
|
42
|
+
|
|
43
|
+
instance : Inhabited Value := ⟨.noval⟩
|
|
44
|
+
|
|
45
|
+
instance : BEq Value where
|
|
46
|
+
beq a b :=
|
|
47
|
+
match a, b with
|
|
48
|
+
| .noval, .noval => true
|
|
49
|
+
| .null, .null => true
|
|
50
|
+
| .bool x, .bool y => x == y
|
|
51
|
+
| .num x, .num y => x == y
|
|
52
|
+
| .str x, .str y => x == y
|
|
53
|
+
| .list x, .list y => x == y
|
|
54
|
+
| .map x, .map y => x == y
|
|
55
|
+
| .func x, .func y => x == y
|
|
56
|
+
| .sentinel x, .sentinel y => x == y
|
|
57
|
+
| _, _ => false
|
|
58
|
+
|
|
59
|
+
/-- A heap slot: the payload of one list or map node. -/
|
|
60
|
+
inductive Slot where
|
|
61
|
+
| slist (items : Array Value)
|
|
62
|
+
| smap (entries : Array (String × Value))
|
|
63
|
+
deriving Inhabited
|
|
64
|
+
|
|
65
|
+
/-- Injection state handle (index into the injection arena). -/
|
|
66
|
+
abbrev InjId := Nat
|
|
67
|
+
|
|
68
|
+
/-- The mutable `Injection` record (canonical TS `Injection`). Mutation goes
|
|
69
|
+
through the arena (`modInj`), so child injections can share and update
|
|
70
|
+
ancestor state exactly as the canonical algorithm requires. -/
|
|
71
|
+
structure InjData where
|
|
72
|
+
mode : Nat := 4
|
|
73
|
+
full : Bool := false
|
|
74
|
+
keyi : Int := 0
|
|
75
|
+
keys : Value := .noval -- List of Str
|
|
76
|
+
key : Value := .noval -- Str
|
|
77
|
+
ival : Value := .noval
|
|
78
|
+
parent : Value := .noval
|
|
79
|
+
path : Value := .noval -- List of Str
|
|
80
|
+
nodes : Value := .noval -- List
|
|
81
|
+
handler : Nat := 0 -- injector registry id
|
|
82
|
+
errs : Value := .noval -- List
|
|
83
|
+
imeta : Value := .noval -- Map ("meta" is a Lean keyword)
|
|
84
|
+
dparent : Value := .noval
|
|
85
|
+
dpath : Value := .noval -- List
|
|
86
|
+
base : Value := .noval -- Str or noval
|
|
87
|
+
modify : Option Nat := none -- modify registry id
|
|
88
|
+
prior : Option InjId := none
|
|
89
|
+
extra : Value := .noval
|
|
90
|
+
deriving Inhabited
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
/-- injdef: the loose `Partial<Injection>` the public API accepts. -/
|
|
94
|
+
structure InjDef where
|
|
95
|
+
dMeta : Value := .noval
|
|
96
|
+
dExtra : Value := .noval
|
|
97
|
+
dErrs : Value := .noval
|
|
98
|
+
dModify : Option Nat := none -- modify registry id
|
|
99
|
+
dHandler : Option Nat := none -- injector registry id
|
|
100
|
+
dBase : Value := .noval
|
|
101
|
+
dDparent : Value := .noval
|
|
102
|
+
dDpath : Value := .noval
|
|
103
|
+
dKey : Value := .noval
|
|
104
|
+
deriving Inhabited
|
|
105
|
+
|
|
106
|
+
inductive InjArg where
|
|
107
|
+
| iinj (i : InjId)
|
|
108
|
+
| idef (d : InjDef)
|
|
109
|
+
| inone
|
|
110
|
+
|
|
111
|
+
/-- All mutable state of one library instance: the node heap, the function
|
|
112
|
+
registries and the injection arena.
|
|
113
|
+
|
|
114
|
+
This is threaded through every operation via `SIO` (a `ReaderT`) rather
|
|
115
|
+
than stored in module-`initialize` globals: values stored in a persistent
|
|
116
|
+
global `IO.Ref` are marked as shared by the Lean runtime, which defeats
|
|
117
|
+
functional-but-in-place array updates and would make every heap write
|
|
118
|
+
O(heap). Runtime-created refs stay unshared and update in place.
|
|
119
|
+
|
|
120
|
+
Registered injector/modify functions are stored as plain `IO` closures
|
|
121
|
+
with the ctx captured at registration time, so the structure is not
|
|
122
|
+
recursive. -/
|
|
123
|
+
structure Ctx where
|
|
124
|
+
valueHeap : IO.Ref (Array Slot)
|
|
125
|
+
funcReg : IO.Ref (Array (InjId → Value → String → Value → IO Value))
|
|
126
|
+
modifyReg : IO.Ref (Array (Value → Value → Value → InjId → IO Unit))
|
|
127
|
+
injHeap : IO.Ref (Array InjData)
|
|
128
|
+
dummyInj : IO.Ref (Option InjId)
|
|
129
|
+
injectHandlerFid : IO.Ref Nat
|
|
130
|
+
|
|
131
|
+
end VoxgigStruct
|
|
132
|
+
|
|
133
|
+
/-- The library monad: `IO` plus the `Ctx` state handle. -/
|
|
134
|
+
abbrev VoxgigStruct.SIO := ReaderT VoxgigStruct.Ctx IO
|
|
135
|
+
|
|
136
|
+
namespace VoxgigStruct
|
|
137
|
+
|
|
138
|
+
/-- The signature of injector functions (transform commands, validators,
|
|
139
|
+
custom handlers): `inj → val → ref → store → result`. -/
|
|
140
|
+
abbrev InjectorFn := InjId → Value → String → Value → SIO Value
|
|
141
|
+
|
|
142
|
+
/-- The signature of `modify` callbacks: `val → key → parent → inj`. -/
|
|
143
|
+
abbrev ModifyFn := Value → Value → Value → InjId → SIO Unit
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
-- ---------------------------------------------------------------------------
|
|
147
|
+
-- Heap / arena / registry primitives
|
|
148
|
+
-- ---------------------------------------------------------------------------
|
|
149
|
+
|
|
150
|
+
def newList (xs : Array Value) : SIO Value := do
|
|
151
|
+
(← read).valueHeap.modifyGet fun h => (Value.list h.size, h.push (.slist xs))
|
|
152
|
+
|
|
153
|
+
def newMap (es : Array (String × Value)) : SIO Value := do
|
|
154
|
+
(← read).valueHeap.modifyGet fun h => (Value.map h.size, h.push (.smap es))
|
|
155
|
+
|
|
156
|
+
def emptyList : SIO Value := newList #[]
|
|
157
|
+
def emptyMap : SIO Value := newMap #[]
|
|
158
|
+
|
|
159
|
+
def listItems (id : Nat) : SIO (Array Value) := do
|
|
160
|
+
match (← (← read).valueHeap.get)[id]? with
|
|
161
|
+
| some (.slist xs) => pure xs
|
|
162
|
+
| _ => pure #[]
|
|
163
|
+
|
|
164
|
+
def setListItems (id : Nat) (xs : Array Value) : SIO Unit := do
|
|
165
|
+
(← read).valueHeap.modify (·.set! id (.slist xs))
|
|
166
|
+
|
|
167
|
+
def mapEntries (id : Nat) : SIO (Array (String × Value)) := do
|
|
168
|
+
match (← (← read).valueHeap.get)[id]? with
|
|
169
|
+
| some (.smap es) => pure es
|
|
170
|
+
| _ => pure #[]
|
|
171
|
+
|
|
172
|
+
def setMapEntries (id : Nat) (es : Array (String × Value)) : SIO Unit := do
|
|
173
|
+
(← read).valueHeap.modify (·.set! id (.smap es))
|
|
174
|
+
|
|
175
|
+
def listItemsOf (v : Value) : SIO (Array Value) := do
|
|
176
|
+
match v with
|
|
177
|
+
| .list id => listItems id
|
|
178
|
+
| _ => pure #[]
|
|
179
|
+
|
|
180
|
+
def mapEntriesOf (v : Value) : SIO (Array (String × Value)) := do
|
|
181
|
+
match v with
|
|
182
|
+
| .map id => mapEntries id
|
|
183
|
+
| _ => pure #[]
|
|
184
|
+
|
|
185
|
+
-- ----- ordered map ops (insertion order preserved) -----
|
|
186
|
+
|
|
187
|
+
def omapGet (es : Array (String × Value)) (k : String) : Option Value :=
|
|
188
|
+
(es.find? (·.1 == k)).map (·.2)
|
|
189
|
+
|
|
190
|
+
def omapHas (es : Array (String × Value)) (k : String) : Bool :=
|
|
191
|
+
es.any (·.1 == k)
|
|
192
|
+
|
|
193
|
+
def omapSet (es : Array (String × Value)) (k : String) (v : Value) :
|
|
194
|
+
Array (String × Value) :=
|
|
195
|
+
if omapHas es k then es.map (fun (k', v') => if k' == k then (k, v) else (k', v'))
|
|
196
|
+
else es.push (k, v)
|
|
197
|
+
|
|
198
|
+
def omapDel (es : Array (String × Value)) (k : String) : Array (String × Value) :=
|
|
199
|
+
es.filter (·.1 != k)
|
|
200
|
+
|
|
201
|
+
def mapSetKV (id : Nat) (k : String) (v : Value) : SIO Unit := do
|
|
202
|
+
setMapEntries id (omapSet (← mapEntries id) k v)
|
|
203
|
+
|
|
204
|
+
def mapDelK (id : Nat) (k : String) : SIO Unit := do
|
|
205
|
+
setMapEntries id (omapDel (← mapEntries id) k)
|
|
206
|
+
|
|
207
|
+
-- ----- injection arena -----
|
|
208
|
+
|
|
209
|
+
def newInjData (d : InjData) : SIO InjId := do
|
|
210
|
+
(← read).injHeap.modifyGet fun a => (a.size, a.push d)
|
|
211
|
+
|
|
212
|
+
def getInj (i : InjId) : SIO InjData := do
|
|
213
|
+
pure (((← (← read).injHeap.get)[i]?).getD default)
|
|
214
|
+
|
|
215
|
+
def modInj (i : InjId) (f : InjData → InjData) : SIO Unit := do
|
|
216
|
+
(← read).injHeap.modify fun a => a.modify i f
|
|
217
|
+
|
|
218
|
+
def getDummyInj : SIO InjId := do
|
|
219
|
+
pure ((← (← read).dummyInj.get).getD 0)
|
|
220
|
+
|
|
221
|
+
-- ----- function / modify registries -----
|
|
222
|
+
|
|
223
|
+
def registerFunc (f : InjectorFn) : SIO Nat := do
|
|
224
|
+
let ctx ← read
|
|
225
|
+
ctx.funcReg.modifyGet fun a =>
|
|
226
|
+
(a.size, a.push (fun i v r st => (f i v r st).run ctx))
|
|
227
|
+
|
|
228
|
+
def vFunc (f : InjectorFn) : SIO Value := do
|
|
229
|
+
pure (.func (← registerFunc f))
|
|
230
|
+
|
|
231
|
+
def callFunc (fid : Nat) (inj : InjId) (v : Value) (ref : String) (store : Value) :
|
|
232
|
+
SIO Value := do
|
|
233
|
+
match (← (← read).funcReg.get)[fid]? with
|
|
234
|
+
| some f => f inj v ref store
|
|
235
|
+
| none => pure .noval
|
|
236
|
+
|
|
237
|
+
def registerModify (f : ModifyFn) : SIO Nat := do
|
|
238
|
+
let ctx ← read
|
|
239
|
+
ctx.modifyReg.modifyGet fun a =>
|
|
240
|
+
(a.size, a.push (fun v k p i => (f v k p i).run ctx))
|
|
241
|
+
|
|
242
|
+
def callModify (mid : Nat) (v key parent : Value) (inj : InjId) : SIO Unit := do
|
|
243
|
+
match (← (← read).modifyReg.get)[mid]? with
|
|
244
|
+
| some f => f v key parent inj
|
|
245
|
+
| none => pure ()
|
|
246
|
+
|
|
247
|
+
-- ---------------------------------------------------------------------------
|
|
248
|
+
-- Constants
|
|
249
|
+
-- ---------------------------------------------------------------------------
|
|
250
|
+
|
|
251
|
+
def M_KEYPRE : Nat := 1
|
|
252
|
+
def M_KEYPOST : Nat := 2
|
|
253
|
+
def M_VAL : Nat := 4
|
|
254
|
+
/-- Canonical MODENAME is keyed by the mode flag values, not positions. -/
|
|
255
|
+
def MODENAME (m : Nat) : String :=
|
|
256
|
+
if m == M_VAL then "val"
|
|
257
|
+
else if m == M_KEYPRE then "key:pre"
|
|
258
|
+
else if m == M_KEYPOST then "key:post"
|
|
259
|
+
else ""
|
|
260
|
+
|
|
261
|
+
def S_DKEY : String := "$KEY"
|
|
262
|
+
def S_BANNO : String := "`$ANNO`"
|
|
263
|
+
def S_DTOP : String := "$TOP"
|
|
264
|
+
def S_DERRS : String := "$ERRS"
|
|
265
|
+
def S_DSPEC : String := "$SPEC"
|
|
266
|
+
def S_BEXACT : String := "`$EXACT`"
|
|
267
|
+
def S_BVAL : String := "`$VAL`"
|
|
268
|
+
def S_BKEY : String := "`$KEY`"
|
|
269
|
+
def S_BOPEN : String := "`$OPEN`"
|
|
270
|
+
|
|
271
|
+
def S_MT : String := ""
|
|
272
|
+
def S_BT : String := "`"
|
|
273
|
+
def S_DS : String := "$"
|
|
274
|
+
def S_DT : String := "."
|
|
275
|
+
def S_CN : String := ":"
|
|
276
|
+
def S_FS : String := "/"
|
|
277
|
+
def S_KEY : String := "KEY"
|
|
278
|
+
def S_VIZ : String := ": "
|
|
279
|
+
|
|
280
|
+
def S_STRING : String := "string"
|
|
281
|
+
def S_OBJECT : String := "object"
|
|
282
|
+
def S_LIST : String := "list"
|
|
283
|
+
def S_MAP : String := "map"
|
|
284
|
+
def S_NIL : String := "nil"
|
|
285
|
+
def S_NULL : String := "null"
|
|
286
|
+
|
|
287
|
+
def T_ANY : Nat := 2 ^ 31 - 1
|
|
288
|
+
def T_NOVAL : Nat := 2 ^ 30
|
|
289
|
+
def T_BOOLEAN : Nat := 2 ^ 29
|
|
290
|
+
def T_DECIMAL : Nat := 2 ^ 28
|
|
291
|
+
def T_INTEGER : Nat := 2 ^ 27
|
|
292
|
+
def T_NUMBER : Nat := 2 ^ 26
|
|
293
|
+
def T_STRING : Nat := 2 ^ 25
|
|
294
|
+
def T_FUNCTION : Nat := 2 ^ 24
|
|
295
|
+
def T_SYMBOL : Nat := 2 ^ 23
|
|
296
|
+
def T_NULL : Nat := 2 ^ 22
|
|
297
|
+
def T_LIST : Nat := 2 ^ 14
|
|
298
|
+
def T_MAP : Nat := 2 ^ 13
|
|
299
|
+
def T_INSTANCE : Nat := 2 ^ 12
|
|
300
|
+
def T_SCALAR : Nat := 2 ^ 7
|
|
301
|
+
def T_NODE : Nat := 2 ^ 6
|
|
302
|
+
|
|
303
|
+
def typenameTbl : Array String := #[
|
|
304
|
+
"any", "nil", "boolean", "decimal", "integer", "number", "string", "function",
|
|
305
|
+
"symbol", "null", "", "", "", "", "", "", "", "list", "map", "instance",
|
|
306
|
+
"", "", "", "", "scalar", "node"]
|
|
307
|
+
|
|
308
|
+
def SKIP : Value := .sentinel "skip"
|
|
309
|
+
def DELETE : Value := .sentinel "delete"
|
|
310
|
+
|
|
311
|
+
def MAXDEPTH : Int := 32
|
|
312
|
+
|
|
313
|
+
-- ---------------------------------------------------------------------------
|
|
314
|
+
-- Small helpers
|
|
315
|
+
-- ---------------------------------------------------------------------------
|
|
316
|
+
|
|
317
|
+
def isNoval : Value → Bool
|
|
318
|
+
| .noval => true
|
|
319
|
+
| _ => false
|
|
320
|
+
|
|
321
|
+
def isNullish : Value → Bool
|
|
322
|
+
| .noval | .null => true
|
|
323
|
+
| _ => false
|
|
324
|
+
|
|
325
|
+
def isSkip (v : Value) : Bool :=
|
|
326
|
+
match v with
|
|
327
|
+
| .sentinel "skip" => true
|
|
328
|
+
| _ => false
|
|
329
|
+
|
|
330
|
+
def isDelete (v : Value) : Bool :=
|
|
331
|
+
match v with
|
|
332
|
+
| .sentinel "delete" => true
|
|
333
|
+
| _ => false
|
|
334
|
+
|
|
335
|
+
def isIntegerF (n : Float) : Bool :=
|
|
336
|
+
n.isFinite && n.floor == n
|
|
337
|
+
|
|
338
|
+
/-- Truncate-toward-zero conversion (OCaml `int_of_float`). -/
|
|
339
|
+
def fToInt (f : Float) : Int :=
|
|
340
|
+
f.toInt64.toInt
|
|
341
|
+
|
|
342
|
+
def intToFloat (i : Int) : Float := Float.ofInt i
|
|
343
|
+
|
|
344
|
+
def vInt (i : Int) : Value := .num (intToFloat i)
|
|
345
|
+
|
|
346
|
+
def strLtB (a b : String) : Bool := decide (a < b)
|
|
347
|
+
|
|
348
|
+
def strContains (s : String) (c : Char) : Bool := s.contains c
|
|
349
|
+
|
|
350
|
+
/-- Character-indexed substring `[a, b)`. -/
|
|
351
|
+
def strSub (s : String) (a b : Nat) : String :=
|
|
352
|
+
String.ofList ((s.toList.toArray.extract a b).toList)
|
|
353
|
+
|
|
354
|
+
def zerosStr (n : Nat) : String := String.ofList (List.replicate n '0')
|
|
355
|
+
|
|
356
|
+
def strTake (s : String) (n : Nat) : String := String.ofList (s.toList.take n)
|
|
357
|
+
def strDrop (s : String) (n : Nat) : String := String.ofList (s.toList.drop n)
|
|
358
|
+
|
|
359
|
+
def hexDigits : Array Char :=
|
|
360
|
+
#['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
|
|
361
|
+
|
|
362
|
+
def toHex (n : Nat) (width : Nat) (upper : Bool := false) : String := Id.run do
|
|
363
|
+
let mut digits : List Char := []
|
|
364
|
+
let mut x := n
|
|
365
|
+
while x > 0 do
|
|
366
|
+
let d := hexDigits[x % 16]!
|
|
367
|
+
digits := (if upper then d.toUpper else d) :: digits
|
|
368
|
+
x := x / 16
|
|
369
|
+
while digits.length < width do
|
|
370
|
+
digits := '0' :: digits
|
|
371
|
+
return String.ofList digits
|
|
372
|
+
|
|
373
|
+
/-- Strict integer string parse: optional leading `-`, then decimal digits. -/
|
|
374
|
+
def strToInt? (s : String) : Option Int := Id.run do
|
|
375
|
+
let cs := s.toList.toArray
|
|
376
|
+
if cs.size == 0 then return none
|
|
377
|
+
let neg := cs[0]! == '-'
|
|
378
|
+
let start := if neg then 1 else 0
|
|
379
|
+
if cs.size ≤ start then return none
|
|
380
|
+
let mut n : Nat := 0
|
|
381
|
+
for i in [start:cs.size] do
|
|
382
|
+
let c := cs[i]!
|
|
383
|
+
if c >= '0' && c <= '9' then
|
|
384
|
+
n := n * 10 + (c.toNat - '0'.toNat)
|
|
385
|
+
else
|
|
386
|
+
return none
|
|
387
|
+
return some (if neg then -(n : Int) else (n : Int))
|
|
388
|
+
|
|
389
|
+
/-- OCaml `is_int_key`: nonempty and every char is a digit or `-`. -/
|
|
390
|
+
def isIntKey (s : String) : Bool :=
|
|
391
|
+
s.length > 0 && s.toList.all (fun c => (c >= '0' && c <= '9') || c == '-')
|
|
392
|
+
|
|
393
|
+
-- ----- JS number formatting (shortest round-trip, ECMA Number::toString) -----
|
|
394
|
+
|
|
395
|
+
/-- Shortest round-trip decimal digits of a positive finite float. Returns
|
|
396
|
+
`(digits, n10)` where the value is `0.digits × 10^n10`. Exact `Nat`
|
|
397
|
+
arithmetic on the float's binary mantissa/exponent; candidates are
|
|
398
|
+
validated by re-parsing with `Float.ofScientific` (the same primitive the
|
|
399
|
+
JSON reader uses, so printing and parsing agree). -/
|
|
400
|
+
def shortestDigits (a : Float) : String × Int := Id.run do
|
|
401
|
+
let (fr, ex) := a.frExp
|
|
402
|
+
let m : Nat := ((fr * (2.0 ^ (53 : Nat).toFloat)).toUInt64).toNat
|
|
403
|
+
let e2 : Int := ex - 53
|
|
404
|
+
let (nN, dD) : Nat × Nat :=
|
|
405
|
+
if e2 >= 0 then (m <<< e2.toNat, 1) else (m, 1 <<< (-e2).toNat)
|
|
406
|
+
-- t: smallest exponent with value < 10^t (so 10^(t-1) ≤ value < 10^t)
|
|
407
|
+
let cond : Int → Bool := fun t =>
|
|
408
|
+
if t >= 0 then nN < dD * 10 ^ t.toNat
|
|
409
|
+
else nN * 10 ^ (-t).toNat < dD
|
|
410
|
+
let mut t : Int := (ex * 30103) / 100000
|
|
411
|
+
while !(cond t) do t := t + 1
|
|
412
|
+
while cond (t - 1) do t := t - 1
|
|
413
|
+
let mut result : String × Int := ("", 0)
|
|
414
|
+
let mut found := false
|
|
415
|
+
for p in [1:18] do
|
|
416
|
+
if !found then
|
|
417
|
+
let scale : Int := (p : Int) - t
|
|
418
|
+
let num := nN * 10 ^ (max 0 scale).toNat
|
|
419
|
+
let den := dD * 10 ^ (max 0 (-scale)).toNat
|
|
420
|
+
let q0 := (2 * num + den) / (2 * den)
|
|
421
|
+
let (q, tt) := if q0 == 10 ^ p then (10 ^ (p - 1), t + 1) else (q0, t)
|
|
422
|
+
let e10 : Int := tt - (p : Int)
|
|
423
|
+
let f := if e10 < 0 then Float.ofScientific q true (-e10).toNat
|
|
424
|
+
else Float.ofScientific q false e10.toNat
|
|
425
|
+
if f == a || p == 17 then
|
|
426
|
+
let mut qq := q
|
|
427
|
+
while qq > 0 && qq % 10 == 0 do qq := qq / 10
|
|
428
|
+
result := (toString qq, tt)
|
|
429
|
+
found := true
|
|
430
|
+
return result
|
|
431
|
+
|
|
432
|
+
/-- ECMA-262 Number::toString(10) layout from shortest digits. -/
|
|
433
|
+
def formatDecimal (neg : Bool) (s : String) (n10 : Int) : String :=
|
|
434
|
+
let k : Int := s.length
|
|
435
|
+
let core :=
|
|
436
|
+
if k <= n10 && n10 <= 21 then s ++ zerosStr (n10 - k).toNat
|
|
437
|
+
else if 0 < n10 && n10 <= 21 then strTake s n10.toNat ++ "." ++ strDrop s n10.toNat
|
|
438
|
+
else if -6 < n10 && n10 <= 0 then "0." ++ zerosStr (-n10).toNat ++ s
|
|
439
|
+
else
|
|
440
|
+
let mant := if k == 1 then s else strTake s 1 ++ "." ++ strDrop s 1
|
|
441
|
+
let e := n10 - 1
|
|
442
|
+
mant ++ "e" ++ (if e >= 0 then "+" ++ toString e else toString e)
|
|
443
|
+
if neg then "-" ++ core else core
|
|
444
|
+
|
|
445
|
+
/-- JS `String(n)` for numbers. -/
|
|
446
|
+
def numToString (n : Float) : String :=
|
|
447
|
+
if n.isNaN then "NaN"
|
|
448
|
+
else if n == 0.0 then "0"
|
|
449
|
+
else if n.isInf then (if n < 0.0 then "-Infinity" else "Infinity")
|
|
450
|
+
else
|
|
451
|
+
let neg := n < 0.0
|
|
452
|
+
let a := if neg then -n else n
|
|
453
|
+
if isIntegerF n && a < 1e16 then
|
|
454
|
+
(if neg then "-" else "") ++ toString a.toUInt64
|
|
455
|
+
else
|
|
456
|
+
let (s, e10) := shortestDigits a
|
|
457
|
+
formatDecimal neg s e10
|
|
458
|
+
|
|
459
|
+
/-- Simple decimal float parse (OCaml `float_of_string` for the corpus needs);
|
|
460
|
+
`none` when the string is not a plain decimal number. -/
|
|
461
|
+
def parseFloatJS (s : String) : Option Float := Id.run do
|
|
462
|
+
let cs := s.toList.toArray
|
|
463
|
+
let n := cs.size
|
|
464
|
+
if n == 0 then return none
|
|
465
|
+
let mut i := 0
|
|
466
|
+
let mut neg := false
|
|
467
|
+
if cs[0]! == '-' then neg := true; i := 1
|
|
468
|
+
else if cs[0]! == '+' then i := 1
|
|
469
|
+
let mut mant : Nat := 0
|
|
470
|
+
let mut digits := 0
|
|
471
|
+
while i < n && cs[i]! >= '0' && cs[i]! <= '9' do
|
|
472
|
+
mant := mant * 10 + (cs[i]!.toNat - '0'.toNat)
|
|
473
|
+
digits := digits + 1
|
|
474
|
+
i := i + 1
|
|
475
|
+
let mut fracLen : Nat := 0
|
|
476
|
+
if i < n && cs[i]! == '.' then
|
|
477
|
+
i := i + 1
|
|
478
|
+
while i < n && cs[i]! >= '0' && cs[i]! <= '9' do
|
|
479
|
+
mant := mant * 10 + (cs[i]!.toNat - '0'.toNat)
|
|
480
|
+
fracLen := fracLen + 1
|
|
481
|
+
digits := digits + 1
|
|
482
|
+
i := i + 1
|
|
483
|
+
if digits == 0 then return none
|
|
484
|
+
let mut exp10 : Int := -(fracLen : Int)
|
|
485
|
+
if i < n && (cs[i]! == 'e' || cs[i]! == 'E') then
|
|
486
|
+
i := i + 1
|
|
487
|
+
let mut eneg := false
|
|
488
|
+
if i < n && (cs[i]! == '-' || cs[i]! == '+') then
|
|
489
|
+
eneg := cs[i]! == '-'
|
|
490
|
+
i := i + 1
|
|
491
|
+
let mut e : Nat := 0
|
|
492
|
+
let mut edigits := 0
|
|
493
|
+
while i < n && cs[i]! >= '0' && cs[i]! <= '9' do
|
|
494
|
+
e := e * 10 + (cs[i]!.toNat - '0'.toNat)
|
|
495
|
+
edigits := edigits + 1
|
|
496
|
+
i := i + 1
|
|
497
|
+
if edigits == 0 then return none
|
|
498
|
+
exp10 := exp10 + (if eneg then -(e : Int) else (e : Int))
|
|
499
|
+
if i != n then return none
|
|
500
|
+
let a := if exp10 < 0 then Float.ofScientific mant true (-exp10).toNat
|
|
501
|
+
else Float.ofScientific mant false exp10.toNat
|
|
502
|
+
return some (if neg then -a else a)
|
|
503
|
+
|
|
504
|
+
/-- JS `Number(v)` coercion for scalars (canonical formatter behaviour):
|
|
505
|
+
booleans map to 1/0, null to 0, trimmed decimal strings parse ("" is 0),
|
|
506
|
+
everything else is NaN. -/
|
|
507
|
+
def jsNumberOfString (str : String) : Float :=
|
|
508
|
+
let t := str.trimAscii.toString
|
|
509
|
+
if t == "" then 0.0
|
|
510
|
+
else match parseFloatJS t with
|
|
511
|
+
| some f => f
|
|
512
|
+
| none => (0.0 : Float) / 0.0
|
|
513
|
+
|
|
514
|
+
def jsNumber (v : Value) : Float :=
|
|
515
|
+
match v with
|
|
516
|
+
| .num n => n
|
|
517
|
+
| .bool b => if b then 1.0 else 0.0
|
|
518
|
+
| .null => 0.0
|
|
519
|
+
| .str str => jsNumberOfString str
|
|
520
|
+
| _ => (0.0 : Float) / 0.0
|
|
521
|
+
|
|
522
|
+
/-- JS `n | 0`: truncate toward zero, wrap into signed 32-bit. -/
|
|
523
|
+
def toInt32 (f : Float) : Float :=
|
|
524
|
+
if !f.isFinite || f.abs >= 9.2e18 then 0.0
|
|
525
|
+
else
|
|
526
|
+
let t : Int := fToInt f
|
|
527
|
+
let m : Int := ((t % 4294967296) + 4294967296) % 4294967296
|
|
528
|
+
intToFloat (if m >= 2147483648 then m - 4294967296 else m)
|
|
529
|
+
|
|
530
|
+
def clz32 (t : Int) : Nat :=
|
|
531
|
+
let n : Nat := ((t % (2 ^ 32 : Int) + 2 ^ 32) % 2 ^ 32).toNat
|
|
532
|
+
if n == 0 then 32 else 31 - Nat.log2 n
|
|
533
|
+
|
|
534
|
+
-- ---------------------------------------------------------------------------
|
|
535
|
+
-- Minor utilities
|
|
536
|
+
-- ---------------------------------------------------------------------------
|
|
537
|
+
|
|
538
|
+
def isnode (v : Value) : Bool :=
|
|
539
|
+
match v with
|
|
540
|
+
| .map _ | .list _ => true
|
|
541
|
+
| _ => false
|
|
542
|
+
|
|
543
|
+
def ismap (v : Value) : Bool :=
|
|
544
|
+
match v with
|
|
545
|
+
| .map _ => true
|
|
546
|
+
| _ => false
|
|
547
|
+
|
|
548
|
+
def islist (v : Value) : Bool :=
|
|
549
|
+
match v with
|
|
550
|
+
| .list _ => true
|
|
551
|
+
| _ => false
|
|
552
|
+
|
|
553
|
+
def isfunc (v : Value) : Bool :=
|
|
554
|
+
match v with
|
|
555
|
+
| .func _ => true
|
|
556
|
+
| _ => false
|
|
557
|
+
|
|
558
|
+
def iskey (k : Value) : Bool :=
|
|
559
|
+
match k with
|
|
560
|
+
| .str s => s != ""
|
|
561
|
+
| .num _ => true
|
|
562
|
+
| _ => false
|
|
563
|
+
|
|
564
|
+
def isempty (v : Value) : SIO Bool := do
|
|
565
|
+
if isNullish v then return true
|
|
566
|
+
if v == .str "" then return true
|
|
567
|
+
match v with
|
|
568
|
+
| .list id => return (← listItems id).isEmpty
|
|
569
|
+
| .map id => return (← mapEntries id).isEmpty
|
|
570
|
+
| _ => return false
|
|
571
|
+
|
|
572
|
+
def getdef (v alt : Value) : Value :=
|
|
573
|
+
if isNoval v then alt else v
|
|
574
|
+
|
|
575
|
+
def typify (v : Value) : Nat :=
|
|
576
|
+
match v with
|
|
577
|
+
| .noval => T_NOVAL
|
|
578
|
+
| .null => T_SCALAR ||| T_NULL
|
|
579
|
+
| .bool _ => T_SCALAR ||| T_BOOLEAN
|
|
580
|
+
| .num n =>
|
|
581
|
+
if n.isNaN then T_NOVAL
|
|
582
|
+
else if isIntegerF n then T_SCALAR ||| T_NUMBER ||| T_INTEGER
|
|
583
|
+
else T_SCALAR ||| T_NUMBER ||| T_DECIMAL
|
|
584
|
+
| .str _ => T_SCALAR ||| T_STRING
|
|
585
|
+
| .func _ => T_SCALAR ||| T_FUNCTION
|
|
586
|
+
| .list _ => T_NODE ||| T_LIST
|
|
587
|
+
| .map _ => T_NODE ||| T_MAP
|
|
588
|
+
| .sentinel _ => T_NODE ||| T_MAP
|
|
589
|
+
|
|
590
|
+
def typename (t : Int) : String :=
|
|
591
|
+
let i := clz32 t
|
|
592
|
+
if i < typenameTbl.size then typenameTbl[i]! else typenameTbl[0]!
|
|
593
|
+
|
|
594
|
+
def size (v : Value) : SIO Int := do
|
|
595
|
+
match v with
|
|
596
|
+
| .list id => return ((← listItems id).size : Int)
|
|
597
|
+
| .map id => return ((← mapEntries id).size : Int)
|
|
598
|
+
| .str s => return (s.length : Int)
|
|
599
|
+
| .bool b => return (if b then 1 else 0)
|
|
600
|
+
| .num n => return fToInt n.floor
|
|
601
|
+
| _ => return 0
|
|
602
|
+
|
|
603
|
+
def strkey (key : Value := .noval) : String :=
|
|
604
|
+
match key with
|
|
605
|
+
| .noval => S_MT
|
|
606
|
+
| .str s => s
|
|
607
|
+
| .bool _ => S_MT
|
|
608
|
+
| .num n => if isIntegerF n then numToString n else numToString n.floor
|
|
609
|
+
| _ => S_MT
|
|
610
|
+
|
|
611
|
+
def keysof (v : Value) : SIO (Array String) := do
|
|
612
|
+
match v with
|
|
613
|
+
| .map id =>
|
|
614
|
+
return ((← mapEntries id).map (·.1)).qsort strLtB
|
|
615
|
+
| .list id =>
|
|
616
|
+
let xs ← listItems id
|
|
617
|
+
return (Array.range xs.size).map (fun i => toString i)
|
|
618
|
+
| _ => return #[]
|
|
619
|
+
|
|
620
|
+
/-- JS `'' + v` / `String(v)` for keys and concatenation. -/
|
|
621
|
+
partial def jsString (v : Value) : SIO String := do
|
|
622
|
+
match v with
|
|
623
|
+
| .noval => pure "undefined"
|
|
624
|
+
| .null => pure "null"
|
|
625
|
+
| .bool b => pure (if b then "true" else "false")
|
|
626
|
+
| .num n => pure (numToString n)
|
|
627
|
+
| .str s => pure s
|
|
628
|
+
| .list id => do
|
|
629
|
+
let xs ← listItems id
|
|
630
|
+
let mut parts : List String := []
|
|
631
|
+
for x in xs do
|
|
632
|
+
let p ← match x with
|
|
633
|
+
| .noval | .null => pure ""
|
|
634
|
+
| _ => jsString x
|
|
635
|
+
parts := p :: parts
|
|
636
|
+
pure (String.intercalate "," parts.reverse)
|
|
637
|
+
| .map _ => pure "[object Object]"
|
|
638
|
+
| .func _ => pure "function"
|
|
639
|
+
| .sentinel s => pure s
|
|
640
|
+
|
|
641
|
+
/-- internal: list element by numeric key, no negative wrap, noval if oob. -/
|
|
642
|
+
def listIndex (id : Nat) (key : Value) : SIO Value := do
|
|
643
|
+
let ks := match key with
|
|
644
|
+
| .str s => s
|
|
645
|
+
| .num n => numToString n
|
|
646
|
+
| _ => ""
|
|
647
|
+
match strToInt? ks with
|
|
648
|
+
| some i =>
|
|
649
|
+
if i >= 0 then
|
|
650
|
+
let xs ← listItems id
|
|
651
|
+
if i < (xs.size : Int) then return xs[i.toNat]! else return .noval
|
|
652
|
+
else return .noval
|
|
653
|
+
| none => return .noval
|
|
654
|
+
|
|
655
|
+
partial def getprop (v key : Value) (alt : Value := .noval) : SIO Value := do
|
|
656
|
+
if isNoval v || isNoval key then return alt
|
|
657
|
+
let out ←
|
|
658
|
+
match v with
|
|
659
|
+
| .map id => do
|
|
660
|
+
pure ((omapGet (← mapEntries id) (← jsString key)).getD .noval)
|
|
661
|
+
| .list id => listIndex id key
|
|
662
|
+
| _ => pure .noval
|
|
663
|
+
if isNullish out then return alt else return out
|
|
664
|
+
|
|
665
|
+
/-- Raw lookup preserving stored `null` (canonical TS bracket access). -/
|
|
666
|
+
partial def lookupRaw (v key : Value) : SIO Value := do
|
|
667
|
+
if isNoval v || isNoval key then return .noval
|
|
668
|
+
match v with
|
|
669
|
+
| .map id => pure ((omapGet (← mapEntries id) (← jsString key)).getD .noval)
|
|
670
|
+
| .list id => listIndex id key
|
|
671
|
+
| _ => pure .noval
|
|
672
|
+
|
|
673
|
+
def haskey (v key : Value) : SIO Bool := do
|
|
674
|
+
return !(isNullish (← getprop v key))
|
|
675
|
+
|
|
676
|
+
partial def getelem (v key : Value) (alt : Value := .noval) : SIO Value := do
|
|
677
|
+
if isNoval v || isNoval key then return alt
|
|
678
|
+
let out ←
|
|
679
|
+
match v with
|
|
680
|
+
| .list id => do
|
|
681
|
+
let ks := match key with
|
|
682
|
+
| .str s => s
|
|
683
|
+
| .num n => numToString n
|
|
684
|
+
| _ => ""
|
|
685
|
+
if isIntKey ks then
|
|
686
|
+
match strToInt? ks with
|
|
687
|
+
| some nk0 => do
|
|
688
|
+
let xs ← listItems id
|
|
689
|
+
let len : Int := xs.size
|
|
690
|
+
let nk := if nk0 < 0 then len + nk0 else nk0
|
|
691
|
+
if nk >= 0 && nk < len then pure xs[nk.toNat]! else pure .noval
|
|
692
|
+
| none => pure .noval
|
|
693
|
+
else pure .noval
|
|
694
|
+
| _ => pure .noval
|
|
695
|
+
if isNullish out then
|
|
696
|
+
match alt with
|
|
697
|
+
| .func fid => callFunc fid (← getDummyInj) .noval "" .noval
|
|
698
|
+
| _ => pure alt
|
|
699
|
+
else pure out
|
|
700
|
+
|
|
701
|
+
/-- literal stored value at string key k, preserving null. -/
|
|
702
|
+
def getpropRaw (v : Value) (k : String) : SIO Value := do
|
|
703
|
+
match v with
|
|
704
|
+
| .map id => pure ((omapGet (← mapEntries id) k).getD .noval)
|
|
705
|
+
| .list id =>
|
|
706
|
+
match strToInt? k with
|
|
707
|
+
| some i =>
|
|
708
|
+
if i >= 0 then do
|
|
709
|
+
let xs ← listItems id
|
|
710
|
+
if i < (xs.size : Int) then pure xs[i.toNat]! else pure .noval
|
|
711
|
+
else pure .noval
|
|
712
|
+
| none => pure .noval
|
|
713
|
+
| _ => pure .noval
|
|
714
|
+
|
|
715
|
+
def itemsPairs (v : Value) : SIO (Array (String × Value)) := do
|
|
716
|
+
if !(isnode v) then return #[]
|
|
717
|
+
let ks ← keysof v
|
|
718
|
+
let mut out : Array (String × Value) := #[]
|
|
719
|
+
for k in ks do
|
|
720
|
+
out := out.push (k, ← getpropRaw v k)
|
|
721
|
+
return out
|
|
722
|
+
|
|
723
|
+
def itemsV (v : Value) (f : (String × Value) → SIO Value) : SIO Value := do
|
|
724
|
+
let ps ← itemsPairs v
|
|
725
|
+
let mut out : Array Value := #[]
|
|
726
|
+
for p in ps do
|
|
727
|
+
out := out.push (← f p)
|
|
728
|
+
newList out
|
|
729
|
+
|
|
730
|
+
def items (v : Value) : SIO Value := do
|
|
731
|
+
let ps ← itemsPairs v
|
|
732
|
+
let mut out : Array Value := #[]
|
|
733
|
+
for (k, x) in ps do
|
|
734
|
+
out := out.push (← newList #[.str k, x])
|
|
735
|
+
newList out
|
|
736
|
+
|
|
737
|
+
partial def flatten (l : Value) (depth : Int := 1) : SIO Value := do
|
|
738
|
+
if !(islist l) then return l
|
|
739
|
+
let xs ← listItemsOf l
|
|
740
|
+
let mut out : Array Value := #[]
|
|
741
|
+
for item in xs do
|
|
742
|
+
if islist item && depth > 0 then
|
|
743
|
+
let f ← flatten item (depth - 1)
|
|
744
|
+
for x in (← listItemsOf f) do
|
|
745
|
+
out := out.push x
|
|
746
|
+
else
|
|
747
|
+
out := out.push item
|
|
748
|
+
newList out
|
|
749
|
+
|
|
750
|
+
def filter (v : Value) (check : (String × Value) → Bool) : SIO Value := do
|
|
751
|
+
let ps ← itemsPairs v
|
|
752
|
+
let mut out : Array Value := #[]
|
|
753
|
+
for (k, x) in ps do
|
|
754
|
+
if check (k, x) then out := out.push x
|
|
755
|
+
newList out
|
|
756
|
+
|
|
757
|
+
partial def setprop (parent key v : Value) : SIO Value := do
|
|
758
|
+
if !(iskey key) then return parent
|
|
759
|
+
match parent with
|
|
760
|
+
| .map id => mapSetKV id (← jsString key) v
|
|
761
|
+
| .list id => do
|
|
762
|
+
let keyF : Float := jsNumber key
|
|
763
|
+
if keyF.isNaN then pure ()
|
|
764
|
+
else do
|
|
765
|
+
let ki0 : Int := fToInt keyF.floor
|
|
766
|
+
let xs ← listItems id
|
|
767
|
+
let len : Int := xs.size
|
|
768
|
+
if ki0 >= 0 then
|
|
769
|
+
let ki := if ki0 > len then len else ki0
|
|
770
|
+
if ki >= len then setListItems id (xs.push v)
|
|
771
|
+
else setListItems id (xs.set! ki.toNat v)
|
|
772
|
+
else
|
|
773
|
+
setListItems id (#[v] ++ xs)
|
|
774
|
+
| _ => pure ()
|
|
775
|
+
return parent
|
|
776
|
+
|
|
777
|
+
partial def delprop (parent key : Value) : SIO Value := do
|
|
778
|
+
if !(iskey key) then return parent
|
|
779
|
+
match parent with
|
|
780
|
+
| .map id => mapDelK id (← jsString key)
|
|
781
|
+
| .list id => do
|
|
782
|
+
let keyF : Float := jsNumber key
|
|
783
|
+
if keyF.isNaN then pure ()
|
|
784
|
+
else do
|
|
785
|
+
let ki : Int := fToInt keyF.floor
|
|
786
|
+
if ki >= 0 then do
|
|
787
|
+
let xs ← listItems id
|
|
788
|
+
if ki < (xs.size : Int) then
|
|
789
|
+
setListItems id ((xs.toList.eraseIdx ki.toNat).toArray)
|
|
790
|
+
| _ => pure ()
|
|
791
|
+
return parent
|
|
792
|
+
|
|
793
|
+
partial def clone (v : Value) : SIO Value := do
|
|
794
|
+
match v with
|
|
795
|
+
| .list id => do
|
|
796
|
+
let xs ← listItems id
|
|
797
|
+
let mut out : Array Value := #[]
|
|
798
|
+
for x in xs do
|
|
799
|
+
out := out.push (← clone x)
|
|
800
|
+
newList out
|
|
801
|
+
| .map id => do
|
|
802
|
+
let es ← mapEntries id
|
|
803
|
+
let mut out : Array (String × Value) := #[]
|
|
804
|
+
for (k, x) in es do
|
|
805
|
+
out := out.push (k, ← clone x)
|
|
806
|
+
newMap out
|
|
807
|
+
| _ => pure v
|
|
808
|
+
|
|
809
|
+
partial def slice (v : Value) (start : Value := .noval) (stop : Value := .noval)
|
|
810
|
+
(mutate : Bool := false) : SIO Value := do
|
|
811
|
+
match v with
|
|
812
|
+
| .num n =>
|
|
813
|
+
let lo : Float := match start with | .num s => s | _ => -(1.0 / 0.0)
|
|
814
|
+
let hi : Float := match stop with | .num e => e - 1.0 | _ => 1.0 / 0.0
|
|
815
|
+
let x := if n < hi then n else hi
|
|
816
|
+
return .num (if lo > x then lo else x)
|
|
817
|
+
| .list _ | .str _ => do
|
|
818
|
+
let vlen ← size v
|
|
819
|
+
let start := match start with
|
|
820
|
+
| .noval => if !(isNoval stop) then Value.num 0.0 else start
|
|
821
|
+
| _ => start
|
|
822
|
+
match start with
|
|
823
|
+
| .num sf =>
|
|
824
|
+
let s0 : Int := fToInt sf
|
|
825
|
+
let (s1, e1) : Int × Int :=
|
|
826
|
+
if s0 < 0 then (0, max 0 (vlen + s0))
|
|
827
|
+
else match stop with
|
|
828
|
+
| .num ef =>
|
|
829
|
+
let e := fToInt ef
|
|
830
|
+
if e < 0 then (s0, max 0 (vlen + e))
|
|
831
|
+
else if vlen < e then (s0, vlen)
|
|
832
|
+
else (s0, e)
|
|
833
|
+
| _ => (s0, vlen)
|
|
834
|
+
let s2 := if vlen < s1 then vlen else s1
|
|
835
|
+
if s2 > -1 && s2 <= e1 && e1 <= vlen then
|
|
836
|
+
match v with
|
|
837
|
+
| .list id => do
|
|
838
|
+
let xs ← listItems id
|
|
839
|
+
let sub := xs.extract s2.toNat e1.toNat
|
|
840
|
+
if mutate then do
|
|
841
|
+
setListItems id sub
|
|
842
|
+
return v
|
|
843
|
+
else newList sub
|
|
844
|
+
| .str str => return .str (strSub str s2.toNat e1.toNat)
|
|
845
|
+
| _ => return v
|
|
846
|
+
else
|
|
847
|
+
match v with
|
|
848
|
+
| .list id =>
|
|
849
|
+
if mutate then do
|
|
850
|
+
setListItems id #[]
|
|
851
|
+
return v
|
|
852
|
+
else emptyList
|
|
853
|
+
| .str _ => return .str S_MT
|
|
854
|
+
| _ => return v
|
|
855
|
+
| _ => return v
|
|
856
|
+
| _ => return v
|
|
857
|
+
|
|
858
|
+
-- ---------------------------------------------------------------------------
|
|
859
|
+
-- escre / escurl / regex helpers
|
|
860
|
+
-- ---------------------------------------------------------------------------
|
|
861
|
+
|
|
862
|
+
partial def escre (s : Value) : SIO Value := do
|
|
863
|
+
let str ← match s with
|
|
864
|
+
| .str x => pure x
|
|
865
|
+
| .noval => pure S_MT
|
|
866
|
+
| _ => jsString s
|
|
867
|
+
let mut out := ""
|
|
868
|
+
for c in str.toList do
|
|
869
|
+
match c with
|
|
870
|
+
| '.' | '*' | '+' | '?' | '^' | '$' | '{' | '}' | '(' | ')' | '|'
|
|
871
|
+
| '[' | ']' | '\\' => out := out.push '\\' |>.push c
|
|
872
|
+
| _ => out := out.push c
|
|
873
|
+
return .str out
|
|
874
|
+
|
|
875
|
+
partial def escurl (s : Value) : SIO Value := do
|
|
876
|
+
let str ← match s with
|
|
877
|
+
| .str x => pure x
|
|
878
|
+
| .noval => pure S_MT
|
|
879
|
+
| _ => jsString s
|
|
880
|
+
let bytes := str.toUTF8
|
|
881
|
+
let mut out := ""
|
|
882
|
+
for i in [0:bytes.size] do
|
|
883
|
+
let b := bytes[i]!
|
|
884
|
+
let c := Char.ofNat b.toNat
|
|
885
|
+
let unreserved :=
|
|
886
|
+
(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
|
|
887
|
+
|| c == '-' || c == '_' || c == '.' || c == '!' || c == '~' || c == '*'
|
|
888
|
+
|| c == '\'' || c == '(' || c == ')'
|
|
889
|
+
if unreserved then out := out.push c
|
|
890
|
+
else out := out ++ "%" ++ toHex b.toNat 2 (upper := true)
|
|
891
|
+
return .str out
|
|
892
|
+
|
|
893
|
+
partial def reCompile (p : Value) (_flags : Value := .noval) : SIO Value := do
|
|
894
|
+
match p with
|
|
895
|
+
| .str _ => pure p
|
|
896
|
+
| _ => pure (.str (← jsString p))
|
|
897
|
+
|
|
898
|
+
def reStr (p : Value) : SIO String := do
|
|
899
|
+
match p with
|
|
900
|
+
| .str s => pure s
|
|
901
|
+
| _ => jsString p
|
|
902
|
+
|
|
903
|
+
partial def reFind (p input : Value) : SIO Value := do
|
|
904
|
+
let re := Vregex.compile (← reStr p)
|
|
905
|
+
match Vregex.find re (← reStr input) with
|
|
906
|
+
| some groups => newList (groups.map (fun g => Value.str g)).toArray
|
|
907
|
+
| none => pure .null
|
|
908
|
+
|
|
909
|
+
partial def reFindAll (p input : Value) : SIO Value := do
|
|
910
|
+
let re := Vregex.compile (← reStr p)
|
|
911
|
+
let mut out : Array Value := #[]
|
|
912
|
+
for m in Vregex.findAll re (← reStr input) do
|
|
913
|
+
out := out.push (← newList (m.map (fun g => Value.str g)).toArray)
|
|
914
|
+
newList out
|
|
915
|
+
|
|
916
|
+
partial def reReplace (p input r : Value) : SIO Value := do
|
|
917
|
+
let re := Vregex.compile (← reStr p)
|
|
918
|
+
pure (.str (Vregex.replaceAll re (← reStr input) (← reStr r)))
|
|
919
|
+
|
|
920
|
+
partial def reTest (p input : Value) : SIO Value := do
|
|
921
|
+
return .bool (Vregex.testStr (← reStr p) (← reStr input))
|
|
922
|
+
|
|
923
|
+
partial def reEscape (s : Value) : SIO Value := escre s
|
|
924
|
+
|
|
925
|
+
-- ---------------------------------------------------------------------------
|
|
926
|
+
-- stringify / jsonify / pathify / join
|
|
927
|
+
-- ---------------------------------------------------------------------------
|
|
928
|
+
|
|
929
|
+
def jsonEscapeStr (s : String) : String := Id.run do
|
|
930
|
+
let mut b := "\""
|
|
931
|
+
for c in s.toList do
|
|
932
|
+
match c with
|
|
933
|
+
| '"' => b := b ++ "\\\""
|
|
934
|
+
| '\\' => b := b ++ "\\\\"
|
|
935
|
+
| '\n' => b := b ++ "\\n"
|
|
936
|
+
| '\r' => b := b ++ "\\r"
|
|
937
|
+
| '\t' => b := b ++ "\\t"
|
|
938
|
+
| c =>
|
|
939
|
+
if c.toNat < 32 then b := b ++ "\\u" ++ toHex c.toNat 4
|
|
940
|
+
else b := b.push c
|
|
941
|
+
return b.push '"'
|
|
942
|
+
|
|
943
|
+
partial def jsonEncode (v : Value) (sort : Bool := false) (indent : Option Nat := none) :
|
|
944
|
+
SIO String := do
|
|
945
|
+
enc v 0
|
|
946
|
+
where
|
|
947
|
+
enc (v : Value) (level : Nat) : SIO String := do
|
|
948
|
+
match v with
|
|
949
|
+
| .noval | .null => pure "null"
|
|
950
|
+
| .bool b => pure (if b then "true" else "false")
|
|
951
|
+
| .num n => pure (if n.isFinite then numToString n else "null")
|
|
952
|
+
| .str s => pure (jsonEscapeStr s)
|
|
953
|
+
| .func _ | .sentinel _ => pure "null"
|
|
954
|
+
| .list id => do
|
|
955
|
+
let xs ← listItems id
|
|
956
|
+
if xs.isEmpty then pure "[]"
|
|
957
|
+
else match indent with
|
|
958
|
+
| some ind => do
|
|
959
|
+
let pad := String.ofList (List.replicate (ind * (level + 1)) ' ')
|
|
960
|
+
let cpad := String.ofList (List.replicate (ind * level) ' ')
|
|
961
|
+
let mut b := "[\n"
|
|
962
|
+
for i in [0:xs.size] do
|
|
963
|
+
if i > 0 then b := b ++ ",\n"
|
|
964
|
+
b := b ++ pad ++ (← enc xs[i]! (level + 1))
|
|
965
|
+
pure (b ++ "\n" ++ cpad ++ "]")
|
|
966
|
+
| none => do
|
|
967
|
+
let mut b := "["
|
|
968
|
+
for i in [0:xs.size] do
|
|
969
|
+
if i > 0 then b := b ++ ","
|
|
970
|
+
b := b ++ (← enc xs[i]! (level + 1))
|
|
971
|
+
pure (b ++ "]")
|
|
972
|
+
| .map id => do
|
|
973
|
+
let es ← mapEntries id
|
|
974
|
+
-- JSON.stringify omits properties whose value is undefined, a function
|
|
975
|
+
-- or a symbol; only real values (null included) are serialized.
|
|
976
|
+
let keep := fun (k : String) =>
|
|
977
|
+
match omapGet es k with
|
|
978
|
+
| some .noval | some (.func _) | some (.sentinel _) | none => false
|
|
979
|
+
| some _ => true
|
|
980
|
+
let ks := (es.map (·.1)).filter keep
|
|
981
|
+
let ks := if sort then ks.qsort strLtB else ks
|
|
982
|
+
if ks.isEmpty then pure "{}"
|
|
983
|
+
else match indent with
|
|
984
|
+
| some ind => do
|
|
985
|
+
let pad := String.ofList (List.replicate (ind * (level + 1)) ' ')
|
|
986
|
+
let cpad := String.ofList (List.replicate (ind * level) ' ')
|
|
987
|
+
let mut b := "{\n"
|
|
988
|
+
for i in [0:ks.size] do
|
|
989
|
+
if i > 0 then b := b ++ ",\n"
|
|
990
|
+
let k := ks[i]!
|
|
991
|
+
b := b ++ pad ++ jsonEscapeStr k ++ ": "
|
|
992
|
+
++ (← enc ((omapGet es k).getD .noval) (level + 1))
|
|
993
|
+
pure (b ++ "\n" ++ cpad ++ "}")
|
|
994
|
+
| none => do
|
|
995
|
+
let mut b := "{"
|
|
996
|
+
for i in [0:ks.size] do
|
|
997
|
+
if i > 0 then b := b ++ ","
|
|
998
|
+
let k := ks[i]!
|
|
999
|
+
b := b ++ jsonEscapeStr k ++ ":"
|
|
1000
|
+
++ (← enc ((omapGet es k).getD .noval) (level + 1))
|
|
1001
|
+
pure (b ++ "}")
|
|
1002
|
+
|
|
1003
|
+
/-- Cycle check tracking only the current recursion path (node ids). -/
|
|
1004
|
+
partial def hasCycle (v : Value) : SIO Bool :=
|
|
1005
|
+
go v []
|
|
1006
|
+
where
|
|
1007
|
+
go (v : Value) (seen : List Nat) : SIO Bool := do
|
|
1008
|
+
match v with
|
|
1009
|
+
| .list id =>
|
|
1010
|
+
if seen.contains id then return true
|
|
1011
|
+
let xs ← listItems id
|
|
1012
|
+
for x in xs do
|
|
1013
|
+
if (← go x (id :: seen)) then return true
|
|
1014
|
+
return false
|
|
1015
|
+
| .map id =>
|
|
1016
|
+
if seen.contains id then return true
|
|
1017
|
+
let es ← mapEntries id
|
|
1018
|
+
for (_, x) in es do
|
|
1019
|
+
if (← go x (id :: seen)) then return true
|
|
1020
|
+
return false
|
|
1021
|
+
| _ => return false
|
|
1022
|
+
|
|
1023
|
+
partial def stringify (v : Value) (maxlen : Value := .noval) (pretty : Bool := false) :
|
|
1024
|
+
SIO String := do
|
|
1025
|
+
match v with
|
|
1026
|
+
| .noval => return (if pretty then "<>" else S_MT)
|
|
1027
|
+
| _ =>
|
|
1028
|
+
let valstr ←
|
|
1029
|
+
match v with
|
|
1030
|
+
| .str s => pure s
|
|
1031
|
+
| _ => do
|
|
1032
|
+
if ← hasCycle v then pure "__STRINGIFY_FAILED__"
|
|
1033
|
+
else
|
|
1034
|
+
try
|
|
1035
|
+
pure ((← jsonEncode v (sort := true)).replace "\"" "")
|
|
1036
|
+
catch _ => pure "__STRINGIFY_FAILED__"
|
|
1037
|
+
let valstr :=
|
|
1038
|
+
match maxlen with
|
|
1039
|
+
| .num m =>
|
|
1040
|
+
if m > -1.0 then
|
|
1041
|
+
let mi := fToInt m
|
|
1042
|
+
let l : Int := valstr.length
|
|
1043
|
+
if mi < l then strTake valstr (max 0 (mi - 3)).toNat ++ "..."
|
|
1044
|
+
else valstr
|
|
1045
|
+
else valstr
|
|
1046
|
+
| _ => valstr
|
|
1047
|
+
if pretty then
|
|
1048
|
+
let colors : Array Nat := #[81, 118, 213, 39, 208, 201, 45, 190, 129, 51, 160, 121, 226, 33, 207, 69]
|
|
1049
|
+
let c := colors.map (fun n => "\x1b[38;5;" ++ toString n ++ "m")
|
|
1050
|
+
let r := "\x1b[0m"
|
|
1051
|
+
let nc : Int := c.size
|
|
1052
|
+
let mut d : Int := 0
|
|
1053
|
+
let mut o := c[0]!
|
|
1054
|
+
let mut t := c[0]!
|
|
1055
|
+
for ch in valstr.toList do
|
|
1056
|
+
if ch == '{' || ch == '[' then
|
|
1057
|
+
d := d + 1
|
|
1058
|
+
o := c[(((d % nc) + nc) % nc).toNat]!
|
|
1059
|
+
t := t ++ o ++ String.singleton ch
|
|
1060
|
+
else if ch == '}' || ch == ']' then
|
|
1061
|
+
t := t ++ o ++ String.singleton ch
|
|
1062
|
+
d := d - 1
|
|
1063
|
+
o := c[(((d % nc) + nc) % nc).toNat]!
|
|
1064
|
+
else
|
|
1065
|
+
t := t ++ o ++ String.singleton ch
|
|
1066
|
+
return t ++ r
|
|
1067
|
+
else return valstr
|
|
1068
|
+
|
|
1069
|
+
partial def jsonify (v : Value) (flags : Value := .noval) : SIO String := do
|
|
1070
|
+
match v with
|
|
1071
|
+
| .noval => return S_NULL
|
|
1072
|
+
| _ =>
|
|
1073
|
+
-- Canonical JSON.stringify throws on circular structures and jsonify
|
|
1074
|
+
-- reports that as __JSONIFY_FAILED__. A recursion-based encoder cannot
|
|
1075
|
+
-- recover from the resulting stack overflow, so check up front.
|
|
1076
|
+
if ← hasCycle v then return "__JSONIFY_FAILED__"
|
|
1077
|
+
let indent ← do
|
|
1078
|
+
match (← getprop flags (.str "indent") (.num 2.0)) with
|
|
1079
|
+
| .num n => pure (fToInt n)
|
|
1080
|
+
| _ => pure (2 : Int)
|
|
1081
|
+
try
|
|
1082
|
+
let str ← if indent > 0 then jsonEncode v (indent := some indent.toNat)
|
|
1083
|
+
else jsonEncode v
|
|
1084
|
+
let offset ← do
|
|
1085
|
+
match (← getprop flags (.str "offset") (.num 0.0)) with
|
|
1086
|
+
| .num n => pure (fToInt n)
|
|
1087
|
+
| _ => pure (0 : Int)
|
|
1088
|
+
if offset > 0 then
|
|
1089
|
+
match str.splitOn "\n" with
|
|
1090
|
+
| _ :: rest =>
|
|
1091
|
+
let pad := String.ofList (List.replicate offset.toNat ' ')
|
|
1092
|
+
return "{\n" ++ String.intercalate "\n" (rest.map (fun l => pad ++ l))
|
|
1093
|
+
| [] => return str
|
|
1094
|
+
else return str
|
|
1095
|
+
catch _ => return "__JSONIFY_FAILED__"
|
|
1096
|
+
|
|
1097
|
+
partial def pad (s : Value) (padding : Value := .noval) (padchar : Value := .noval) :
|
|
1098
|
+
SIO String := do
|
|
1099
|
+
let str ← match s with
|
|
1100
|
+
| .str x => pure x
|
|
1101
|
+
| .null => pure "null"
|
|
1102
|
+
| _ => stringify s
|
|
1103
|
+
let padn : Int := match padding with
|
|
1104
|
+
| .num n => fToInt n
|
|
1105
|
+
| _ => 44
|
|
1106
|
+
let padc : String := match padchar with
|
|
1107
|
+
| .str x => strTake (x ++ " ") 1
|
|
1108
|
+
| _ => " "
|
|
1109
|
+
if padn > -1 then
|
|
1110
|
+
let n : Int := padn - str.length
|
|
1111
|
+
if n > 0 then
|
|
1112
|
+
return str ++ String.join (List.replicate n.toNat padc)
|
|
1113
|
+
else return str
|
|
1114
|
+
else
|
|
1115
|
+
let n : Int := (-padn) - str.length
|
|
1116
|
+
if n > 0 then
|
|
1117
|
+
return String.join (List.replicate n.toNat padc) ++ str
|
|
1118
|
+
else return str
|
|
1119
|
+
|
|
1120
|
+
partial def join (arr : Value) (sep : Value := .noval) (url : Bool := false) :
|
|
1121
|
+
SIO String := do
|
|
1122
|
+
if !(islist arr) then return S_MT
|
|
1123
|
+
let sepdef ← match sep with
|
|
1124
|
+
| .noval | .null => pure ","
|
|
1125
|
+
| .str s => pure s
|
|
1126
|
+
| _ => jsString sep
|
|
1127
|
+
let single := sepdef.length == 1
|
|
1128
|
+
let sc : Char := if single then sepdef.toList.headD ' ' else ' '
|
|
1129
|
+
let itemsArr ← listItemsOf arr
|
|
1130
|
+
let sarr := itemsArr.size -- canonical sarr: the ORIGINAL size
|
|
1131
|
+
let survivors := itemsArr.filterMap fun v =>
|
|
1132
|
+
match v with
|
|
1133
|
+
| .str s => if s != S_MT then some s else none
|
|
1134
|
+
| _ => none
|
|
1135
|
+
let stripTrailing (s : String) : String := Id.run do
|
|
1136
|
+
let a := s.toList.toArray
|
|
1137
|
+
let mut i := a.size
|
|
1138
|
+
while i > 0 && a[i - 1]! == sc do
|
|
1139
|
+
i := i - 1
|
|
1140
|
+
return String.ofList (a.extract 0 i).toList
|
|
1141
|
+
let stripLeading (s : String) : String := Id.run do
|
|
1142
|
+
let a := s.toList.toArray
|
|
1143
|
+
let mut i := 0
|
|
1144
|
+
while i < a.size && a[i]! == sc do
|
|
1145
|
+
i := i + 1
|
|
1146
|
+
return String.ofList (a.extract i a.size).toList
|
|
1147
|
+
-- Collapse runs of the sep char to one, but only when the run is bounded
|
|
1148
|
+
-- by a non-sep char on both sides. Boundary runs are left untouched.
|
|
1149
|
+
let collapse (s : String) : String := Id.run do
|
|
1150
|
+
let a := s.toList.toArray
|
|
1151
|
+
let n := a.size
|
|
1152
|
+
let mut b := ""
|
|
1153
|
+
let mut i := 0
|
|
1154
|
+
while i < n do
|
|
1155
|
+
if a[i]! != sc then
|
|
1156
|
+
b := b.push a[i]!
|
|
1157
|
+
i := i + 1
|
|
1158
|
+
else
|
|
1159
|
+
let mut j := i
|
|
1160
|
+
while j < n && a[j]! == sc do
|
|
1161
|
+
j := j + 1
|
|
1162
|
+
let beforeNonsep := i > 0 && a[i - 1]! != sc
|
|
1163
|
+
let afterNonsep := j < n
|
|
1164
|
+
if beforeNonsep && afterNonsep then b := b.push sc
|
|
1165
|
+
else b := b ++ String.ofList (a.extract i j).toList
|
|
1166
|
+
i := j
|
|
1167
|
+
return b
|
|
1168
|
+
let mut out : List String := []
|
|
1169
|
+
for idx in [0:survivors.size] do
|
|
1170
|
+
let s := survivors[idx]!
|
|
1171
|
+
let s :=
|
|
1172
|
+
if single then
|
|
1173
|
+
if url && idx == 0 then stripTrailing s
|
|
1174
|
+
else
|
|
1175
|
+
let s := if idx > 0 then stripLeading s else s
|
|
1176
|
+
let s := if (idx : Int) < (sarr : Int) - 1 || !url then stripTrailing s else s
|
|
1177
|
+
collapse s
|
|
1178
|
+
else s
|
|
1179
|
+
if s != S_MT then out := s :: out
|
|
1180
|
+
return String.intercalate sepdef out.reverse
|
|
1181
|
+
|
|
1182
|
+
partial def joinurl (arr : Value) : SIO String :=
|
|
1183
|
+
join arr (sep := .str "/") (url := true)
|
|
1184
|
+
|
|
1185
|
+
partial def replace (s from_ to_ : Value) : SIO String := do
|
|
1186
|
+
let ts := typify s
|
|
1187
|
+
let rs ←
|
|
1188
|
+
if (T_STRING &&& ts) == 0 then stringify s
|
|
1189
|
+
else if ((T_NOVAL ||| T_NULL) &&& ts) > 0 then pure S_MT
|
|
1190
|
+
else stringify s
|
|
1191
|
+
let toS ← match to_ with
|
|
1192
|
+
| .str x => pure x
|
|
1193
|
+
| _ => jsString to_
|
|
1194
|
+
match from_ with
|
|
1195
|
+
| .str f =>
|
|
1196
|
+
if f == "" then return rs
|
|
1197
|
+
else return rs.replace f toS
|
|
1198
|
+
| _ => return rs
|
|
1199
|
+
|
|
1200
|
+
partial def pathify (v : Value) (startin : Value := .noval) (endin : Value := .noval)
|
|
1201
|
+
(absent : Bool := false) : SIO String := do
|
|
1202
|
+
let path ←
|
|
1203
|
+
if islist v then pure (some (← listItemsOf v))
|
|
1204
|
+
else if iskey v then pure (some #[v])
|
|
1205
|
+
else pure none
|
|
1206
|
+
let start : Nat := match startin with
|
|
1207
|
+
| .num n => if n > -1.0 then (fToInt n).toNat else 0
|
|
1208
|
+
| _ => 0
|
|
1209
|
+
let endn : Nat := match endin with
|
|
1210
|
+
| .num n => if n > -1.0 then (fToInt n).toNat else 0
|
|
1211
|
+
| _ => 0
|
|
1212
|
+
let pathstr ←
|
|
1213
|
+
match path with
|
|
1214
|
+
| some p => do
|
|
1215
|
+
let len := p.size
|
|
1216
|
+
let e := len - endn -- Nat subtraction: floors at 0 (max 0 in OCaml)
|
|
1217
|
+
let s := min start len
|
|
1218
|
+
let sub := if s <= e then p.extract s e else #[]
|
|
1219
|
+
if sub.isEmpty then pure (some "<root>")
|
|
1220
|
+
else do
|
|
1221
|
+
let fp := sub.filter iskey
|
|
1222
|
+
let mut mapped : List String := []
|
|
1223
|
+
for pp in fp do
|
|
1224
|
+
match pp with
|
|
1225
|
+
| .num n => mapped := numToString n.floor :: mapped
|
|
1226
|
+
| _ => mapped := ((← jsString pp).replace "." "") :: mapped
|
|
1227
|
+
pure (some (String.intercalate "." mapped.reverse))
|
|
1228
|
+
| none => pure none
|
|
1229
|
+
match pathstr with
|
|
1230
|
+
| some s => return s
|
|
1231
|
+
| none => do
|
|
1232
|
+
let suffix ← if absent then pure S_MT
|
|
1233
|
+
else do pure (S_CN ++ (← stringify v (maxlen := .num 47.0)))
|
|
1234
|
+
return "<unknown-path" ++ suffix ++ ">"
|
|
1235
|
+
|
|
1236
|
+
-- ---------------------------------------------------------------------------
|
|
1237
|
+
-- walk
|
|
1238
|
+
-- ---------------------------------------------------------------------------
|
|
1239
|
+
|
|
1240
|
+
/-- Walk callbacks receive `key value parent path`. -/
|
|
1241
|
+
abbrev WalkFn := Value → Value → Value → Value → SIO Value
|
|
1242
|
+
|
|
1243
|
+
partial def walk (v : Value) (before : Option WalkFn := none)
|
|
1244
|
+
(after : Option WalkFn := none) (maxdepth : Value := .noval)
|
|
1245
|
+
(key : Value := .noval) (parent : Value := .noval)
|
|
1246
|
+
(path : Option Value := none) : SIO Value := do
|
|
1247
|
+
let path ← match path with
|
|
1248
|
+
| some p => pure p
|
|
1249
|
+
| none => emptyList
|
|
1250
|
+
let depth ← size path
|
|
1251
|
+
let out0 ← match before with
|
|
1252
|
+
| none => pure v
|
|
1253
|
+
| some f => f key v parent path
|
|
1254
|
+
let mdv : Int := match maxdepth with
|
|
1255
|
+
| .num n => if n >= 0.0 then fToInt n else MAXDEPTH
|
|
1256
|
+
| .noval | .null => MAXDEPTH
|
|
1257
|
+
| _ => MAXDEPTH
|
|
1258
|
+
if mdv == 0 || (mdv > 0 && mdv <= depth) then return out0
|
|
1259
|
+
if isnode out0 then
|
|
1260
|
+
let pfx ← listItemsOf path
|
|
1261
|
+
let pairs ← itemsPairs out0
|
|
1262
|
+
for (ckey, child) in pairs do
|
|
1263
|
+
let childpath ← newList (pfx.push (.str ckey))
|
|
1264
|
+
let result ← walk child before after (.num (intToFloat mdv)) (.str ckey)
|
|
1265
|
+
out0 (some childpath)
|
|
1266
|
+
match out0 with
|
|
1267
|
+
| .map id => mapSetKV id ckey result
|
|
1268
|
+
| .list id => do
|
|
1269
|
+
match strToInt? ckey with
|
|
1270
|
+
| some i =>
|
|
1271
|
+
if i >= 0 then do
|
|
1272
|
+
let xs ← listItems id
|
|
1273
|
+
if i < (xs.size : Int) then setListItems id (xs.set! i.toNat result)
|
|
1274
|
+
| none => pure ()
|
|
1275
|
+
| _ => pure ()
|
|
1276
|
+
match after with
|
|
1277
|
+
| none => return out0
|
|
1278
|
+
| some f => f key out0 parent path
|
|
1279
|
+
|
|
1280
|
+
-- ---------------------------------------------------------------------------
|
|
1281
|
+
-- merge
|
|
1282
|
+
-- ---------------------------------------------------------------------------
|
|
1283
|
+
|
|
1284
|
+
partial def merge (objs : Value) (maxdepth : Value := .noval) : SIO Value := do
|
|
1285
|
+
let md : Int := match maxdepth with
|
|
1286
|
+
| .num n => if n < 0.0 then 0 else fToInt n
|
|
1287
|
+
| .noval | .null => 32
|
|
1288
|
+
| _ => 32
|
|
1289
|
+
if !(islist objs) then return objs
|
|
1290
|
+
let l ← listItemsOf objs
|
|
1291
|
+
let lenlist := l.size
|
|
1292
|
+
if lenlist == 0 then return .noval
|
|
1293
|
+
if lenlist == 1 then return l[0]!
|
|
1294
|
+
let outRef ← IO.mkRef (← getprop objs (.num 0.0) (← emptyMap))
|
|
1295
|
+
for oi in [1:lenlist] do
|
|
1296
|
+
let obj := l[oi]!
|
|
1297
|
+
if !(isnode obj) then
|
|
1298
|
+
outRef.set obj
|
|
1299
|
+
else
|
|
1300
|
+
let cur ← IO.mkRef #[← outRef.get]
|
|
1301
|
+
let dst ← IO.mkRef #[← outRef.get]
|
|
1302
|
+
let grow (r : IO.Ref (Array Value)) (n : Nat) : SIO Unit := do
|
|
1303
|
+
let a ← r.get
|
|
1304
|
+
if a.size <= n then
|
|
1305
|
+
r.set (a ++ Array.replicate (n + 1 - a.size) Value.noval)
|
|
1306
|
+
let before : WalkFn := fun key v _parent path => do
|
|
1307
|
+
let pi ← size path
|
|
1308
|
+
let piN := pi.toNat
|
|
1309
|
+
if md <= pi then do
|
|
1310
|
+
grow cur piN
|
|
1311
|
+
cur.modify (·.set! piN v)
|
|
1312
|
+
if pi > 0 then do
|
|
1313
|
+
let target := (← cur.get)[piN - 1]!
|
|
1314
|
+
let _ ← setprop target key v
|
|
1315
|
+
pure .noval
|
|
1316
|
+
else if !(isnode v) then do
|
|
1317
|
+
grow cur piN
|
|
1318
|
+
cur.modify (·.set! piN v)
|
|
1319
|
+
pure v
|
|
1320
|
+
else do
|
|
1321
|
+
grow dst piN
|
|
1322
|
+
grow cur piN
|
|
1323
|
+
let dprev ← if piN > 0 then do
|
|
1324
|
+
pure ((← dst.get)[piN - 1]!)
|
|
1325
|
+
else do
|
|
1326
|
+
pure ((← dst.get)[piN]!)
|
|
1327
|
+
let tval ← if piN > 0 then getprop dprev key else pure dprev
|
|
1328
|
+
dst.modify (·.set! piN tval)
|
|
1329
|
+
if isNullish tval then do
|
|
1330
|
+
let fresh ← if islist v then emptyList else emptyMap
|
|
1331
|
+
cur.modify (·.set! piN fresh)
|
|
1332
|
+
pure v
|
|
1333
|
+
else if (islist v && islist tval) || (ismap v && ismap tval) then do
|
|
1334
|
+
cur.modify (·.set! piN tval)
|
|
1335
|
+
pure v
|
|
1336
|
+
else do
|
|
1337
|
+
cur.modify (·.set! piN v)
|
|
1338
|
+
pure .noval
|
|
1339
|
+
let after : WalkFn := fun key v _parent path => do
|
|
1340
|
+
let ci ← size path
|
|
1341
|
+
let ciN := ci.toNat
|
|
1342
|
+
if ci < 1 then do
|
|
1343
|
+
let a ← cur.get
|
|
1344
|
+
if a.size > 0 then pure a[0]! else pure v
|
|
1345
|
+
else do
|
|
1346
|
+
let a ← cur.get
|
|
1347
|
+
let target := if ciN - 1 < a.size then a[ciN - 1]! else .noval
|
|
1348
|
+
let value := if ciN < a.size then a[ciN]! else .noval
|
|
1349
|
+
let _ ← setprop target key value
|
|
1350
|
+
pure value
|
|
1351
|
+
outRef.set (← walk obj (before := some before) (after := some after))
|
|
1352
|
+
if md == 0 then do
|
|
1353
|
+
let o ← getprop objs (vInt ((lenlist : Int) - 1))
|
|
1354
|
+
let res ← if islist o then emptyList else if ismap o then emptyMap else pure o
|
|
1355
|
+
outRef.set res
|
|
1356
|
+
outRef.get
|
|
1357
|
+
|
|
1358
|
+
-- ---------------------------------------------------------------------------
|
|
1359
|
+
-- string-pattern helpers (hand-rolled, RE2-subset-free)
|
|
1360
|
+
-- ---------------------------------------------------------------------------
|
|
1361
|
+
|
|
1362
|
+
/-- R_META_PATH = `^([^$]+)\$([=~])(.+)$` -/
|
|
1363
|
+
def metaPathMatch (s : String) : Option (String × String × String) := Id.run do
|
|
1364
|
+
let cs := s.toList.toArray
|
|
1365
|
+
let n := cs.size
|
|
1366
|
+
let mut di : Option Nat := none
|
|
1367
|
+
for i in [0:n] do
|
|
1368
|
+
if di.isNone && cs[i]! == '$' then di := some i
|
|
1369
|
+
match di with
|
|
1370
|
+
| some i =>
|
|
1371
|
+
if i > 0 && i + 1 < n && (cs[i + 1]! == '=' || cs[i + 1]! == '~') && i + 2 <= n - 1 then
|
|
1372
|
+
return some (String.ofList (cs.extract 0 i).toList,
|
|
1373
|
+
String.singleton cs[i + 1]!,
|
|
1374
|
+
String.ofList (cs.extract (i + 2) n).toList)
|
|
1375
|
+
else return none
|
|
1376
|
+
| none => return none
|
|
1377
|
+
|
|
1378
|
+
/-- R_INJECTION_FULL: whole string is a single backtick injection; returns the
|
|
1379
|
+
captured reference ($NAME with trailing digits stripped, or the literal). -/
|
|
1380
|
+
def injectionFull (s : String) : Option String := Id.run do
|
|
1381
|
+
let cs := s.toList.toArray
|
|
1382
|
+
let n := cs.size
|
|
1383
|
+
if n >= 2 && cs[0]! == '`' && cs[n - 1]! == '`' then
|
|
1384
|
+
let inner := cs.extract 1 (n - 1)
|
|
1385
|
+
if inner.contains '`' then return none
|
|
1386
|
+
-- $[A-Z]+[0-9]*$ -> group is $ + uppercase run
|
|
1387
|
+
let ilen := inner.size
|
|
1388
|
+
if ilen > 1 && inner[0]! == '$' then
|
|
1389
|
+
let mut j := 1
|
|
1390
|
+
while j < ilen && inner[j]! >= 'A' && inner[j]! <= 'Z' do
|
|
1391
|
+
j := j + 1
|
|
1392
|
+
let lettersEnd := j
|
|
1393
|
+
let mut k := lettersEnd
|
|
1394
|
+
while k < ilen && inner[k]! >= '0' && inner[k]! <= '9' do
|
|
1395
|
+
k := k + 1
|
|
1396
|
+
if lettersEnd > 1 && k == ilen then
|
|
1397
|
+
return some (String.ofList (inner.extract 0 lettersEnd).toList)
|
|
1398
|
+
return some (String.ofList inner.toList)
|
|
1399
|
+
else return none
|
|
1400
|
+
|
|
1401
|
+
/-- Replace each backtick pair (R_INJECTION_PARTIAL) using f on the inner text. -/
|
|
1402
|
+
partial def injectionPartialReplace (s : String) (f : String → SIO String) : SIO String := do
|
|
1403
|
+
let cs := s.toList.toArray
|
|
1404
|
+
let n := cs.size
|
|
1405
|
+
let mut b := ""
|
|
1406
|
+
let mut i := 0
|
|
1407
|
+
while i < n do
|
|
1408
|
+
if cs[i]! == '`' then
|
|
1409
|
+
let mut j := i + 1
|
|
1410
|
+
while j < n && cs[j]! != '`' do
|
|
1411
|
+
j := j + 1
|
|
1412
|
+
if j < n then
|
|
1413
|
+
let inner := String.ofList (cs.extract (i + 1) j).toList
|
|
1414
|
+
b := b ++ (← f inner)
|
|
1415
|
+
i := j + 1
|
|
1416
|
+
else
|
|
1417
|
+
b := b.push cs[i]!
|
|
1418
|
+
i := i + 1
|
|
1419
|
+
else
|
|
1420
|
+
b := b.push cs[i]!
|
|
1421
|
+
i := i + 1
|
|
1422
|
+
return b
|
|
1423
|
+
|
|
1424
|
+
/-- Replace `` `$NAME` `` with `name` (lowercase); used in validate errors. -/
|
|
1425
|
+
def replaceTransformNames (s : String) : String := Id.run do
|
|
1426
|
+
let cs := s.toList.toArray
|
|
1427
|
+
let n := cs.size
|
|
1428
|
+
let mut b := ""
|
|
1429
|
+
let mut i := 0
|
|
1430
|
+
while i < n do
|
|
1431
|
+
if cs[i]! == '`' && i + 1 < n && cs[i + 1]! == '$' then
|
|
1432
|
+
let mut j := i + 2
|
|
1433
|
+
while j < n && cs[j]! >= 'A' && cs[j]! <= 'Z' do
|
|
1434
|
+
j := j + 1
|
|
1435
|
+
if j < n && cs[j]! == '`' && j > i + 2 then
|
|
1436
|
+
b := b ++ (String.ofList (cs.extract (i + 2) j).toList).toLower
|
|
1437
|
+
i := j + 1
|
|
1438
|
+
else
|
|
1439
|
+
b := b.push cs[i]!
|
|
1440
|
+
i := i + 1
|
|
1441
|
+
else
|
|
1442
|
+
b := b.push cs[i]!
|
|
1443
|
+
i := i + 1
|
|
1444
|
+
return b
|
|
1445
|
+
|
|
1446
|
+
-- ---------------------------------------------------------------------------
|
|
1447
|
+
-- getpath / setpath
|
|
1448
|
+
-- ---------------------------------------------------------------------------
|
|
1449
|
+
|
|
1450
|
+
def iaIsSome : InjArg → Bool
|
|
1451
|
+
| .inone => false
|
|
1452
|
+
| _ => true
|
|
1453
|
+
|
|
1454
|
+
def iaBase (a : InjArg) : SIO Value := do
|
|
1455
|
+
match a with
|
|
1456
|
+
| .iinj i => pure (← getInj i).base
|
|
1457
|
+
| .idef d => pure d.dBase
|
|
1458
|
+
| .inone => pure .noval
|
|
1459
|
+
|
|
1460
|
+
def iaDparent (a : InjArg) : SIO Value := do
|
|
1461
|
+
match a with
|
|
1462
|
+
| .iinj i => pure (← getInj i).dparent
|
|
1463
|
+
| .idef d => pure d.dDparent
|
|
1464
|
+
| .inone => pure .noval
|
|
1465
|
+
|
|
1466
|
+
def iaMeta (a : InjArg) : SIO Value := do
|
|
1467
|
+
match a with
|
|
1468
|
+
| .iinj i => pure (← getInj i).imeta
|
|
1469
|
+
| .idef d => pure d.dMeta
|
|
1470
|
+
| .inone => pure .noval
|
|
1471
|
+
|
|
1472
|
+
def iaKey (a : InjArg) : SIO Value := do
|
|
1473
|
+
match a with
|
|
1474
|
+
| .iinj i => pure (← getInj i).key
|
|
1475
|
+
| .idef d => pure d.dKey
|
|
1476
|
+
| .inone => pure .noval
|
|
1477
|
+
|
|
1478
|
+
def iaDpath (a : InjArg) : SIO Value := do
|
|
1479
|
+
match a with
|
|
1480
|
+
| .iinj i => pure (← getInj i).dpath
|
|
1481
|
+
| .idef d => pure d.dDpath
|
|
1482
|
+
| .inone => pure .noval
|
|
1483
|
+
|
|
1484
|
+
def iaHandler (a : InjArg) : SIO (Option Nat) := do
|
|
1485
|
+
match a with
|
|
1486
|
+
| .iinj i => pure (some (← getInj i).handler)
|
|
1487
|
+
| .idef d => pure d.dHandler
|
|
1488
|
+
| .inone => pure none
|
|
1489
|
+
|
|
1490
|
+
partial def getpath (store path : Value) (inj : InjArg := .inone) : SIO Value := do
|
|
1491
|
+
let pa0 : Option (Array Value) ←
|
|
1492
|
+
match path with
|
|
1493
|
+
| .list _ => pure (some (← listItemsOf path))
|
|
1494
|
+
| .str s => pure (some ((s.splitOn ".").map (fun x => Value.str x)).toArray)
|
|
1495
|
+
| .num n => pure (some #[.str (strkey (.num n))])
|
|
1496
|
+
| _ => pure none
|
|
1497
|
+
match pa0 with
|
|
1498
|
+
| none => return .noval
|
|
1499
|
+
| some pa0 => do
|
|
1500
|
+
let paRef ← IO.mkRef pa0
|
|
1501
|
+
let base ← iaBase inj
|
|
1502
|
+
let dparent ← iaDparent inj
|
|
1503
|
+
let injMeta ← iaMeta inj
|
|
1504
|
+
let injKey ← iaKey inj
|
|
1505
|
+
let dpath ← iaDpath inj
|
|
1506
|
+
let src ← if iskey base then getprop store base store else pure store
|
|
1507
|
+
let numparts := pa0.size
|
|
1508
|
+
let vRef ← IO.mkRef store
|
|
1509
|
+
let arrGet (i : Int) : SIO Value := do
|
|
1510
|
+
let pa ← paRef.get
|
|
1511
|
+
if i >= 0 && i < (pa.size : Int) then pure pa[i.toNat]! else pure .noval
|
|
1512
|
+
if isNoval path || isNoval store
|
|
1513
|
+
|| (numparts == 1 && pa0[0]! == .str S_MT) || numparts == 0 then
|
|
1514
|
+
vRef.set src
|
|
1515
|
+
else do
|
|
1516
|
+
if numparts == 1 then
|
|
1517
|
+
vRef.set (← getprop store pa0[0]!)
|
|
1518
|
+
if !(isfunc (← vRef.get)) then do
|
|
1519
|
+
vRef.set src
|
|
1520
|
+
(match pa0[0]! with
|
|
1521
|
+
| .str s0 =>
|
|
1522
|
+
match metaPathMatch s0 with
|
|
1523
|
+
| some (g1, _, g3) => do
|
|
1524
|
+
if !(isNoval injMeta) && iaIsSome inj then do
|
|
1525
|
+
vRef.set (← getprop injMeta (.str g1))
|
|
1526
|
+
paRef.modify (·.set! 0 (.str g3))
|
|
1527
|
+
| none => pure ()
|
|
1528
|
+
| _ => pure ())
|
|
1529
|
+
let piRef ← IO.mkRef (0 : Nat)
|
|
1530
|
+
let contRef ← IO.mkRef true
|
|
1531
|
+
repeat do
|
|
1532
|
+
let cont ← contRef.get
|
|
1533
|
+
let pi ← piRef.get
|
|
1534
|
+
if !cont || isNoval (← vRef.get) || pi >= numparts then break
|
|
1535
|
+
let raw := (← paRef.get)[pi]!
|
|
1536
|
+
let part ←
|
|
1537
|
+
match raw with
|
|
1538
|
+
| .str s =>
|
|
1539
|
+
if iaIsSome inj && s == S_DKEY then
|
|
1540
|
+
pure (if !(isNoval injKey) then injKey else raw)
|
|
1541
|
+
else if iaIsSome inj && s.startsWith "$GET:" then do
|
|
1542
|
+
let sub ← slice (.str s) (start := .num 5.0) (stop := .num (-1.0))
|
|
1543
|
+
pure (Value.str (← stringify (← getpath src sub)))
|
|
1544
|
+
else if iaIsSome inj && s.startsWith "$REF:" then do
|
|
1545
|
+
let sub ← slice (.str s) (start := .num 5.0) (stop := .num (-1.0))
|
|
1546
|
+
pure (Value.str (← stringify (← getpath (← getprop store (.str S_DSPEC)) sub)))
|
|
1547
|
+
else if iaIsSome inj && s.startsWith "$META:" then do
|
|
1548
|
+
let sub ← slice (.str s) (start := .num 6.0) (stop := .num (-1.0))
|
|
1549
|
+
pure (Value.str (← stringify (← getpath injMeta sub)))
|
|
1550
|
+
else pure raw
|
|
1551
|
+
| _ => pure raw
|
|
1552
|
+
-- $$ escapes $
|
|
1553
|
+
let part : Value :=
|
|
1554
|
+
match part with
|
|
1555
|
+
| .str s => .str (if strContains s '$' then s.replace "$$" "$" else s)
|
|
1556
|
+
| _ => .str (strkey part)
|
|
1557
|
+
if part == .str S_MT then do
|
|
1558
|
+
let ascendsRef ← IO.mkRef (0 : Nat)
|
|
1559
|
+
repeat do
|
|
1560
|
+
let pi' ← piRef.get
|
|
1561
|
+
if (← arrGet ((pi' : Int) + 1)) == .str S_MT then
|
|
1562
|
+
ascendsRef.modify (· + 1)
|
|
1563
|
+
piRef.modify (· + 1)
|
|
1564
|
+
else break
|
|
1565
|
+
let ascends0 ← ascendsRef.get
|
|
1566
|
+
if iaIsSome inj && ascends0 > 0 then do
|
|
1567
|
+
let pi' ← piRef.get
|
|
1568
|
+
if pi' == numparts - 1 then ascendsRef.modify (· - 1)
|
|
1569
|
+
let ascends ← ascendsRef.get
|
|
1570
|
+
if ascends == 0 then vRef.set dparent
|
|
1571
|
+
else do
|
|
1572
|
+
let pa ← paRef.get
|
|
1573
|
+
let pi'' ← piRef.get
|
|
1574
|
+
let tailparts := pa.extract (pi'' + 1) numparts
|
|
1575
|
+
let dsliced ← slice dpath (start := .num (intToFloat (-(ascends : Int))))
|
|
1576
|
+
let tailV ← newList tailparts
|
|
1577
|
+
let fullpath ← flatten (← newList #[dsliced, tailV])
|
|
1578
|
+
if (ascends : Int) <= (← size dpath) then
|
|
1579
|
+
vRef.set (← getpath store fullpath)
|
|
1580
|
+
else
|
|
1581
|
+
vRef.set .noval
|
|
1582
|
+
contRef.set false
|
|
1583
|
+
else vRef.set dparent
|
|
1584
|
+
else
|
|
1585
|
+
vRef.set (← getprop (← vRef.get) part)
|
|
1586
|
+
if ← contRef.get then piRef.modify (· + 1)
|
|
1587
|
+
let handlerOpt ← iaHandler inj
|
|
1588
|
+
match handlerOpt with
|
|
1589
|
+
| some h =>
|
|
1590
|
+
if iaIsSome inj then do
|
|
1591
|
+
let refp ← pathify path
|
|
1592
|
+
let i ← match inj with
|
|
1593
|
+
| .iinj i => pure i
|
|
1594
|
+
| _ => getDummyInj
|
|
1595
|
+
vRef.set (← callFunc h i (← vRef.get) refp store)
|
|
1596
|
+
| none => pure ()
|
|
1597
|
+
vRef.get
|
|
1598
|
+
|
|
1599
|
+
partial def setpath (store path v : Value) (inj : InjArg := .inone) : SIO Value := do
|
|
1600
|
+
let ptype := typify path
|
|
1601
|
+
let parts ←
|
|
1602
|
+
if (T_LIST &&& ptype) > 0 then do
|
|
1603
|
+
pure (some (← newList (← listItemsOf path)))
|
|
1604
|
+
else if (T_STRING &&& ptype) > 0 then do
|
|
1605
|
+
match path with
|
|
1606
|
+
| .str s => pure (some (← newList ((s.splitOn ".").map (fun x => Value.str x)).toArray))
|
|
1607
|
+
| _ => pure (some (← emptyList))
|
|
1608
|
+
else if (T_NUMBER &&& ptype) > 0 then do
|
|
1609
|
+
pure (some (← newList #[path]))
|
|
1610
|
+
else pure none
|
|
1611
|
+
match parts with
|
|
1612
|
+
| none => return .noval
|
|
1613
|
+
| some parts => do
|
|
1614
|
+
let base ← match inj with
|
|
1615
|
+
| .inone => pure Value.noval
|
|
1616
|
+
| _ => iaBase inj
|
|
1617
|
+
let numparts ← size parts
|
|
1618
|
+
let n := numparts.toNat
|
|
1619
|
+
let parentRef ← IO.mkRef (← if iskey base then getprop store base store else pure store)
|
|
1620
|
+
for pi in [0:n-1] do
|
|
1621
|
+
let pkey ← getelem parts (vInt pi)
|
|
1622
|
+
let np ← getprop (← parentRef.get) pkey
|
|
1623
|
+
let np ← if !(isnode np) then do
|
|
1624
|
+
let nextpart ← getelem parts (vInt ((pi : Int) + 1))
|
|
1625
|
+
let nn ← if (T_NUMBER &&& typify nextpart) > 0 then emptyList else emptyMap
|
|
1626
|
+
let _ ← setprop (← parentRef.get) pkey nn
|
|
1627
|
+
pure nn
|
|
1628
|
+
else pure np
|
|
1629
|
+
parentRef.set np
|
|
1630
|
+
let lastkey ← getelem parts (.num (-1.0))
|
|
1631
|
+
if isDelete v then
|
|
1632
|
+
let _ ← delprop (← parentRef.get) lastkey
|
|
1633
|
+
else
|
|
1634
|
+
let _ ← setprop (← parentRef.get) lastkey v
|
|
1635
|
+
parentRef.get
|
|
1636
|
+
|
|
1637
|
+
-- ---------------------------------------------------------------------------
|
|
1638
|
+
-- Injection state
|
|
1639
|
+
-- ---------------------------------------------------------------------------
|
|
1640
|
+
|
|
1641
|
+
def newInj (v parent : Value) : SIO InjId := do
|
|
1642
|
+
let keys ← newList #[.str S_DTOP]
|
|
1643
|
+
let path ← newList #[.str S_DTOP]
|
|
1644
|
+
let nodes ← newList #[parent]
|
|
1645
|
+
let errs ← emptyList
|
|
1646
|
+
let meta0 ← emptyMap
|
|
1647
|
+
let dpath ← newList #[.str S_DTOP]
|
|
1648
|
+
let h ← (← read).injectHandlerFid.get
|
|
1649
|
+
newInjData {
|
|
1650
|
+
mode := M_VAL, full := false, keyi := 0, keys, key := .str S_DTOP, ival := v,
|
|
1651
|
+
parent, path, nodes, handler := h, errs, imeta := meta0, dparent := .noval,
|
|
1652
|
+
dpath, base := .str S_DTOP, modify := none, prior := none, extra := .noval }
|
|
1653
|
+
|
|
1654
|
+
partial def injDescend (i : InjId) : SIO Value := do
|
|
1655
|
+
let d ← getInj i
|
|
1656
|
+
(match d.imeta with
|
|
1657
|
+
| .map mid => do
|
|
1658
|
+
let es ← mapEntries mid
|
|
1659
|
+
let dv := match omapGet es "__d" with
|
|
1660
|
+
| some (.num n) => n
|
|
1661
|
+
| _ => 0.0
|
|
1662
|
+
mapSetKV mid "__d" (.num (dv + 1.0))
|
|
1663
|
+
| _ => pure ())
|
|
1664
|
+
let parentkey ← getelem d.path (.num (-2.0))
|
|
1665
|
+
if isNoval d.dparent then do
|
|
1666
|
+
if (← size d.dpath) > 1 then
|
|
1667
|
+
match d.dpath with
|
|
1668
|
+
| .list r => do
|
|
1669
|
+
let xs ← listItems r
|
|
1670
|
+
let nl ← newList (xs.push parentkey)
|
|
1671
|
+
modInj i (fun dd => { dd with dpath := nl })
|
|
1672
|
+
| _ => pure ()
|
|
1673
|
+
else if !(isNoval parentkey) then do
|
|
1674
|
+
let ndp ← getprop d.dparent parentkey
|
|
1675
|
+
modInj i (fun dd => { dd with dparent := ndp })
|
|
1676
|
+
let d2 ← getInj i
|
|
1677
|
+
let lastpart ← getelem d2.dpath (.num (-1.0))
|
|
1678
|
+
let pkstr ← jsString parentkey
|
|
1679
|
+
if lastpart == .str ("$:" ++ pkstr) then do
|
|
1680
|
+
let ndpath ← slice d2.dpath (start := .num (-1.0))
|
|
1681
|
+
modInj i (fun dd => { dd with dpath := ndpath })
|
|
1682
|
+
else
|
|
1683
|
+
match d2.dpath with
|
|
1684
|
+
| .list r => do
|
|
1685
|
+
let xs ← listItems r
|
|
1686
|
+
let nl ← newList (xs.push parentkey)
|
|
1687
|
+
modInj i (fun dd => { dd with dpath := nl })
|
|
1688
|
+
| _ => pure ()
|
|
1689
|
+
pure (← getInj i).dparent
|
|
1690
|
+
|
|
1691
|
+
/-- Create a child Injection (internal; the canonical public `injectChild`
|
|
1692
|
+
helper is defined further below). -/
|
|
1693
|
+
partial def makeChildInj (i : InjId) (keyi : Int) (keys : Value) : SIO InjId := do
|
|
1694
|
+
let d ← getInj i
|
|
1695
|
+
let kElem ← getelem keys (vInt keyi)
|
|
1696
|
+
let key := strkey kElem
|
|
1697
|
+
let v := d.ival
|
|
1698
|
+
let ival ← getprop v (.str key)
|
|
1699
|
+
let path ← match d.path with
|
|
1700
|
+
| .list r => do newList ((← listItems r).push (.str key))
|
|
1701
|
+
| _ => newList #[.str key]
|
|
1702
|
+
let nodes ← match d.nodes with
|
|
1703
|
+
| .list r => do newList ((← listItems r).push v)
|
|
1704
|
+
| _ => newList #[v]
|
|
1705
|
+
let dpath ← match d.dpath with
|
|
1706
|
+
| .list r => do newList (← listItems r)
|
|
1707
|
+
| _ => pure d.dpath
|
|
1708
|
+
newInjData { d with
|
|
1709
|
+
keyi, keys, key := .str key, ival, parent := v, path, nodes,
|
|
1710
|
+
prior := some i, dpath }
|
|
1711
|
+
|
|
1712
|
+
partial def injSetval (i : InjId) (v : Value) (ancestor : Int := 1) : SIO Unit := do
|
|
1713
|
+
let d ← getInj i
|
|
1714
|
+
let (target, key) ←
|
|
1715
|
+
if ancestor < 2 then pure (d.parent, d.key)
|
|
1716
|
+
else do
|
|
1717
|
+
let t ← getelem d.nodes (vInt (-ancestor))
|
|
1718
|
+
let k ← getelem d.path (vInt (-ancestor))
|
|
1719
|
+
pure (t, k)
|
|
1720
|
+
if isNoval v then
|
|
1721
|
+
let _ ← delprop target key
|
|
1722
|
+
else
|
|
1723
|
+
let _ ← setprop target key v
|
|
1724
|
+
|
|
1725
|
+
-- ---------------------------------------------------------------------------
|
|
1726
|
+
-- inject
|
|
1727
|
+
-- ---------------------------------------------------------------------------
|
|
1728
|
+
|
|
1729
|
+
def pushErr (i : InjId) (msg : String) : SIO Unit := do
|
|
1730
|
+
let d ← getInj i
|
|
1731
|
+
let n ← size d.errs
|
|
1732
|
+
let _ ← setprop d.errs (vInt n) (.str msg)
|
|
1733
|
+
|
|
1734
|
+
mutual
|
|
1735
|
+
|
|
1736
|
+
partial def inject (v : Value) (store : Value) (inj : InjArg := .inone) : SIO Value := do
|
|
1737
|
+
let state ←
|
|
1738
|
+
match inj with
|
|
1739
|
+
| .iinj i => pure i
|
|
1740
|
+
| _ => do
|
|
1741
|
+
let parent ← newMap #[(S_DTOP, v)]
|
|
1742
|
+
let i ← newInj v parent
|
|
1743
|
+
let errsV ← getprop store (.str S_DERRS) (← emptyList)
|
|
1744
|
+
modInj i (fun d => { d with dparent := store, errs := errsV })
|
|
1745
|
+
(match (← getInj i).imeta with
|
|
1746
|
+
| .map mid => mapSetKV mid "__d" (.num 0.0)
|
|
1747
|
+
| _ => pure ())
|
|
1748
|
+
(match inj with
|
|
1749
|
+
| .idef dd => do
|
|
1750
|
+
if dd.dModify.isSome then modInj i (fun d => { d with modify := dd.dModify })
|
|
1751
|
+
if !(isNoval dd.dExtra) then modInj i (fun d => { d with extra := dd.dExtra })
|
|
1752
|
+
if !(isNoval dd.dMeta) then modInj i (fun d => { d with imeta := dd.dMeta })
|
|
1753
|
+
(match dd.dHandler with
|
|
1754
|
+
| some h => modInj i (fun d => { d with handler := h })
|
|
1755
|
+
| none => pure ())
|
|
1756
|
+
| _ => pure ())
|
|
1757
|
+
pure i
|
|
1758
|
+
let _ ← injDescend state
|
|
1759
|
+
let v ←
|
|
1760
|
+
if isnode v then do
|
|
1761
|
+
let nodekeys0 : Array String ←
|
|
1762
|
+
match v with
|
|
1763
|
+
| .map mid => do
|
|
1764
|
+
let es ← mapEntries mid
|
|
1765
|
+
let ks := es.map (·.1)
|
|
1766
|
+
let normal := (ks.filter (fun k => !(strContains k '$'))).qsort strLtB
|
|
1767
|
+
let trans := (ks.filter (fun k => strContains k '$')).qsort strLtB
|
|
1768
|
+
pure (normal ++ trans)
|
|
1769
|
+
| .list lid => do
|
|
1770
|
+
let xs ← listItems lid
|
|
1771
|
+
pure ((Array.range xs.size).map (fun i => toString i))
|
|
1772
|
+
| _ => pure #[]
|
|
1773
|
+
let nodekeysRef ← IO.mkRef nodekeys0
|
|
1774
|
+
let rereadKeys (ci : InjId) : SIO Unit := do
|
|
1775
|
+
match (← getInj ci).keys with
|
|
1776
|
+
| .list r => do
|
|
1777
|
+
let xs ← listItems r
|
|
1778
|
+
let mut out : Array String := #[]
|
|
1779
|
+
for x in xs do
|
|
1780
|
+
out := out.push (← jsString x)
|
|
1781
|
+
nodekeysRef.set out
|
|
1782
|
+
| _ => nodekeysRef.set #[]
|
|
1783
|
+
let mut nki : Int := 0
|
|
1784
|
+
repeat do
|
|
1785
|
+
let nodekeys ← nodekeysRef.get
|
|
1786
|
+
if nki >= (nodekeys.size : Int) then break
|
|
1787
|
+
let keysV ← newList (nodekeys.map (fun s => Value.str s))
|
|
1788
|
+
let ci ← makeChildInj state nki keysV
|
|
1789
|
+
let nodekey := (← getInj ci).key
|
|
1790
|
+
modInj ci (fun d => { d with mode := M_KEYPRE })
|
|
1791
|
+
let prekey ← injectstr (← jsString nodekey) store (some ci)
|
|
1792
|
+
rereadKeys ci
|
|
1793
|
+
if !(isNoval prekey) then do
|
|
1794
|
+
let iv ← getprop v prekey
|
|
1795
|
+
modInj ci (fun d => { d with ival := iv, mode := M_VAL })
|
|
1796
|
+
let _ ← inject iv store (.iinj ci)
|
|
1797
|
+
rereadKeys ci
|
|
1798
|
+
modInj ci (fun d => { d with mode := M_KEYPOST })
|
|
1799
|
+
let _ ← injectstr (← jsString nodekey) store (some ci)
|
|
1800
|
+
rereadKeys ci
|
|
1801
|
+
nki := (← getInj ci).keyi + 1
|
|
1802
|
+
pure v
|
|
1803
|
+
else if (match v with | .str _ => true | _ => false) then do
|
|
1804
|
+
modInj state (fun d => { d with mode := M_VAL })
|
|
1805
|
+
let nv ← injectstr (← jsString v) store (some state)
|
|
1806
|
+
if !(isSkip nv) then injSetval state nv
|
|
1807
|
+
pure nv
|
|
1808
|
+
else pure v
|
|
1809
|
+
let d ← getInj state
|
|
1810
|
+
(match d.modify with
|
|
1811
|
+
| some mid =>
|
|
1812
|
+
if !(isSkip v) then do
|
|
1813
|
+
let mkey := d.key
|
|
1814
|
+
let mparent := d.parent
|
|
1815
|
+
let mval ← getprop mparent mkey
|
|
1816
|
+
callModify mid mval mkey mparent state
|
|
1817
|
+
else pure ()
|
|
1818
|
+
| none => pure ())
|
|
1819
|
+
modInj state (fun dd => { dd with ival := v })
|
|
1820
|
+
lookupRaw d.parent (.str S_DTOP)
|
|
1821
|
+
|
|
1822
|
+
partial def injectHandler : InjectorFn := fun inj v refstr store => do
|
|
1823
|
+
let iscmd := isfunc v && (refstr == "" || refstr.startsWith S_DS)
|
|
1824
|
+
if iscmd then
|
|
1825
|
+
match v with
|
|
1826
|
+
| .func fid => callFunc fid inj v refstr store
|
|
1827
|
+
| _ => pure v
|
|
1828
|
+
else do
|
|
1829
|
+
let d ← getInj inj
|
|
1830
|
+
if d.mode == M_VAL && d.full then do
|
|
1831
|
+
injSetval inj v
|
|
1832
|
+
pure v
|
|
1833
|
+
else pure v
|
|
1834
|
+
|
|
1835
|
+
partial def injectstr (v : String) (store : Value) (injOpt : Option InjId) : SIO Value := do
|
|
1836
|
+
if v == S_MT then return .str S_MT
|
|
1837
|
+
match injectionFull v with
|
|
1838
|
+
| some pathref0 => do
|
|
1839
|
+
(match injOpt with
|
|
1840
|
+
| some i => modInj i (fun d => { d with full := true })
|
|
1841
|
+
| none => pure ())
|
|
1842
|
+
let pathref :=
|
|
1843
|
+
if pathref0.length > 3 then (pathref0.replace "$BT" S_BT).replace "$DS" S_DS
|
|
1844
|
+
else pathref0
|
|
1845
|
+
let ia := match injOpt with
|
|
1846
|
+
| some i => InjArg.iinj i
|
|
1847
|
+
| none => .inone
|
|
1848
|
+
getpath store (.str pathref) ia
|
|
1849
|
+
| none => do
|
|
1850
|
+
let out ← injectionPartialReplace v (fun ref0 => do
|
|
1851
|
+
let refp :=
|
|
1852
|
+
if ref0.length > 3 then (ref0.replace "$BT" S_BT).replace "$DS" S_DS
|
|
1853
|
+
else ref0
|
|
1854
|
+
(match injOpt with
|
|
1855
|
+
| some i => modInj i (fun d => { d with full := false })
|
|
1856
|
+
| none => pure ())
|
|
1857
|
+
let ia := match injOpt with
|
|
1858
|
+
| some i => InjArg.iinj i
|
|
1859
|
+
| none => .inone
|
|
1860
|
+
let found ← getpath store (.str refp) ia
|
|
1861
|
+
match found with
|
|
1862
|
+
| .noval => pure S_MT
|
|
1863
|
+
| .str s => pure (if s == "__NULL__" then "null" else s)
|
|
1864
|
+
| .func _ => pure S_MT
|
|
1865
|
+
| _ => try jsonEncode found catch _ => stringify found)
|
|
1866
|
+
match injOpt with
|
|
1867
|
+
| some i => do
|
|
1868
|
+
modInj i (fun d => { d with full := true })
|
|
1869
|
+
callFunc (← getInj i).handler i (.str out) v store
|
|
1870
|
+
| none => pure (.str out)
|
|
1871
|
+
|
|
1872
|
+
end
|
|
1873
|
+
|
|
1874
|
+
-- ---------------------------------------------------------------------------
|
|
1875
|
+
-- transform commands
|
|
1876
|
+
-- ---------------------------------------------------------------------------
|
|
1877
|
+
|
|
1878
|
+
partial def transformDelete : InjectorFn := fun inj _v _ref _store => do
|
|
1879
|
+
let d ← getInj inj
|
|
1880
|
+
let _ ← delprop d.parent d.key
|
|
1881
|
+
pure .noval
|
|
1882
|
+
|
|
1883
|
+
partial def transformCopy : InjectorFn := fun inj _v _ref _store => do
|
|
1884
|
+
let d ← getInj inj
|
|
1885
|
+
if d.mode == M_KEYPRE || d.mode == M_KEYPOST then pure d.key
|
|
1886
|
+
else do
|
|
1887
|
+
let out ← lookupRaw d.dparent d.key
|
|
1888
|
+
injSetval inj out
|
|
1889
|
+
pure out
|
|
1890
|
+
|
|
1891
|
+
partial def transformKey : InjectorFn := fun inj _v _ref _store => do
|
|
1892
|
+
let d ← getInj inj
|
|
1893
|
+
if d.mode != M_VAL then pure .noval
|
|
1894
|
+
else do
|
|
1895
|
+
let keyspec ← lookupRaw d.parent (.str S_BKEY)
|
|
1896
|
+
if !(isNoval keyspec) then do
|
|
1897
|
+
let _ ← delprop d.parent (.str S_BKEY)
|
|
1898
|
+
getprop d.dparent keyspec
|
|
1899
|
+
else do
|
|
1900
|
+
let anno ← lookupRaw d.parent (.str S_BANNO)
|
|
1901
|
+
let fromanno ← lookupRaw anno (.str S_KEY)
|
|
1902
|
+
if !(isNoval fromanno) then pure fromanno
|
|
1903
|
+
else getelem d.path (.num (-2.0))
|
|
1904
|
+
|
|
1905
|
+
partial def transformAnno : InjectorFn := fun inj _v _ref _store => do
|
|
1906
|
+
let d ← getInj inj
|
|
1907
|
+
let _ ← delprop d.parent (.str S_BANNO)
|
|
1908
|
+
pure .noval
|
|
1909
|
+
|
|
1910
|
+
partial def transformMerge : InjectorFn := fun inj _v _ref _store => do
|
|
1911
|
+
let d ← getInj inj
|
|
1912
|
+
if d.mode == M_KEYPRE then pure d.key
|
|
1913
|
+
else if d.mode == M_KEYPOST then do
|
|
1914
|
+
let args0 ← getprop d.parent d.key
|
|
1915
|
+
let args ← if islist args0 then pure args0 else newList #[args0]
|
|
1916
|
+
injSetval inj .noval
|
|
1917
|
+
let pl ← newList #[d.parent]
|
|
1918
|
+
let cl ← newList #[← clone d.parent]
|
|
1919
|
+
let mergelist ← flatten (← newList #[pl, args, cl])
|
|
1920
|
+
let _ ← merge mergelist
|
|
1921
|
+
pure d.key
|
|
1922
|
+
else pure .noval
|
|
1923
|
+
|
|
1924
|
+
partial def transformEach : InjectorFn := fun inj _v _ref store => do
|
|
1925
|
+
let d0 ← getInj inj
|
|
1926
|
+
if islist d0.keys then
|
|
1927
|
+
let _ ← slice d0.keys (start := .num 0.0) (stop := .num 1.0) (mutate := true)
|
|
1928
|
+
if d0.mode != M_VAL then pure .noval
|
|
1929
|
+
else do
|
|
1930
|
+
let d ← getInj inj
|
|
1931
|
+
let parent := d.parent
|
|
1932
|
+
let psize ← size parent
|
|
1933
|
+
let srcpath ← if psize > 1 then getelem parent (.num 1.0) else pure .noval
|
|
1934
|
+
let childTm ← if psize > 2 then clone (← getelem parent (.num 2.0)) else pure .noval
|
|
1935
|
+
let srcstore ← getprop store d.base store
|
|
1936
|
+
let src ← getpath srcstore srcpath (.iinj inj)
|
|
1937
|
+
let tkey ← getelem d.path (.num (-2.0))
|
|
1938
|
+
let nodes := d.nodes
|
|
1939
|
+
let target ← do
|
|
1940
|
+
let t ← getelem nodes (.num (-2.0))
|
|
1941
|
+
if isNullish t then getelem nodes (.num (-1.0)) else pure t
|
|
1942
|
+
let rvalRef ← IO.mkRef (← emptyList)
|
|
1943
|
+
if isnode src then do
|
|
1944
|
+
let mut tval : Array Value := #[]
|
|
1945
|
+
match src with
|
|
1946
|
+
| .list r =>
|
|
1947
|
+
for _ in (← listItems r) do
|
|
1948
|
+
tval := tval.push (← clone childTm)
|
|
1949
|
+
| .map m =>
|
|
1950
|
+
for (k, _) in (← mapEntries m) do
|
|
1951
|
+
let cc ← clone childTm
|
|
1952
|
+
if ismap cc then
|
|
1953
|
+
let _ ← setprop cc (.str S_BANNO) (← newMap #[(S_KEY, .str k)])
|
|
1954
|
+
tval := tval.push cc
|
|
1955
|
+
| _ => pure ()
|
|
1956
|
+
let tvalv ← newList tval
|
|
1957
|
+
let tcurrent ← match src with
|
|
1958
|
+
| .map m => do newList ((← mapEntries m).map (·.2))
|
|
1959
|
+
| .list r => do newList (← listItems r)
|
|
1960
|
+
| _ => pure src
|
|
1961
|
+
if tval.size > 0 then do
|
|
1962
|
+
let path := d.path
|
|
1963
|
+
let ckey ← getelem path (.num (-2.0))
|
|
1964
|
+
let plist ← listItemsOf path
|
|
1965
|
+
let tpath ← newList (if plist.isEmpty then #[] else plist.extract 0 (plist.size - 1))
|
|
1966
|
+
let mut dpathArr : Array Value := #[.str S_DTOP]
|
|
1967
|
+
match srcpath with
|
|
1968
|
+
| .str sp =>
|
|
1969
|
+
if sp != S_MT then
|
|
1970
|
+
for p in sp.splitOn "." do
|
|
1971
|
+
if p != S_MT then dpathArr := dpathArr.push (.str p)
|
|
1972
|
+
| _ => pure ()
|
|
1973
|
+
let ckstr ← jsString ckey
|
|
1974
|
+
if !(isNoval ckey) then dpathArr := dpathArr.push (.str ("$:" ++ ckstr))
|
|
1975
|
+
let tcurRef ← IO.mkRef (← newMap #[(ckstr, tcurrent)])
|
|
1976
|
+
if (← size tpath) > 1 then do
|
|
1977
|
+
let pkey ← getelem path (.num (-3.0)) (.str S_DTOP)
|
|
1978
|
+
let pkstr ← jsString pkey
|
|
1979
|
+
dpathArr := dpathArr.push (.str ("$:" ++ pkstr))
|
|
1980
|
+
tcurRef.set (← newMap #[(pkstr, ← tcurRef.get)])
|
|
1981
|
+
let keys0 ← if !(isNoval ckey) then newList #[ckey] else emptyList
|
|
1982
|
+
let tinj ← makeChildInj inj 0 keys0
|
|
1983
|
+
let nlist ← listItemsOf nodes
|
|
1984
|
+
let tnodes ← newList (if nlist.isEmpty then #[] else nlist.extract 0 (nlist.size - 1))
|
|
1985
|
+
modInj tinj (fun dd => { dd with path := tpath, nodes := tnodes })
|
|
1986
|
+
let tparent ← do
|
|
1987
|
+
if (← size tnodes) > 0 then getelem tnodes (.num (-1.0)) else pure .noval
|
|
1988
|
+
modInj tinj (fun dd => { dd with parent := tparent })
|
|
1989
|
+
if !(isNoval ckey) && !(isNoval tparent) then
|
|
1990
|
+
let _ ← setprop tparent ckey tvalv
|
|
1991
|
+
let dpathV ← newList dpathArr
|
|
1992
|
+
let tcur ← tcurRef.get
|
|
1993
|
+
modInj tinj (fun dd => { dd with
|
|
1994
|
+
ival := tvalv, dpath := dpathV, dparent := tcur })
|
|
1995
|
+
let _ ← inject tvalv store (.iinj tinj)
|
|
1996
|
+
rvalRef.set (← getInj tinj).ival
|
|
1997
|
+
else pure ()
|
|
1998
|
+
let rval ← rvalRef.get
|
|
1999
|
+
let _ ← setprop target tkey rval
|
|
2000
|
+
if islist rval && (← size rval) > 0 then getelem rval (.num 0.0) else pure .noval
|
|
2001
|
+
|
|
2002
|
+
partial def transformPack : InjectorFn := fun inj _v _ref store => do
|
|
2003
|
+
let d ← getInj inj
|
|
2004
|
+
if d.mode != M_KEYPRE || !(match d.key with | .str _ => true | _ => false) then
|
|
2005
|
+
pure .noval
|
|
2006
|
+
else do
|
|
2007
|
+
let parent := d.parent
|
|
2008
|
+
let path := d.path
|
|
2009
|
+
let nodes := d.nodes
|
|
2010
|
+
let argsVal ← getprop parent d.key
|
|
2011
|
+
if !(islist argsVal) || (← size argsVal) < 2 then pure .noval
|
|
2012
|
+
else do
|
|
2013
|
+
let srcpath ← getelem argsVal (.num 0.0)
|
|
2014
|
+
let origchildspec ← getelem argsVal (.num 1.0)
|
|
2015
|
+
let tkey ← getelem path (.num (-2.0))
|
|
2016
|
+
let pathsize ← size path
|
|
2017
|
+
let target ← do
|
|
2018
|
+
let t ← getelem nodes (vInt (pathsize - 2))
|
|
2019
|
+
if isNullish t then getelem nodes (vInt (pathsize - 1)) else pure t
|
|
2020
|
+
let srcstore ← getprop store d.base store
|
|
2021
|
+
let src0 ← getpath srcstore srcpath (.iinj inj)
|
|
2022
|
+
let src ←
|
|
2023
|
+
if !(islist src0) then
|
|
2024
|
+
if ismap src0 then do
|
|
2025
|
+
let ps ← itemsPairs src0
|
|
2026
|
+
let mut arr : Array Value := #[]
|
|
2027
|
+
for (k, node) in ps do
|
|
2028
|
+
let _ ← setprop node (.str S_BANNO) (← newMap #[(S_KEY, .str k)])
|
|
2029
|
+
arr := arr.push node
|
|
2030
|
+
newList arr
|
|
2031
|
+
else pure Value.noval
|
|
2032
|
+
else pure src0
|
|
2033
|
+
if isNoval src then pure .noval
|
|
2034
|
+
else do
|
|
2035
|
+
let keypath ← getprop origchildspec (.str S_BKEY)
|
|
2036
|
+
let childspec ← delprop origchildspec (.str S_BKEY)
|
|
2037
|
+
let child ← getprop childspec (.str S_BVAL) childspec
|
|
2038
|
+
let tval ← emptyMap
|
|
2039
|
+
let packKey (node : Value) (fallback : Value) : SIO Value := do
|
|
2040
|
+
if isNoval keypath then pure fallback
|
|
2041
|
+
else match keypath with
|
|
2042
|
+
| .str kp =>
|
|
2043
|
+
if kp.startsWith S_BT then do
|
|
2044
|
+
let em ← emptyMap
|
|
2045
|
+
let wrap ← newMap #[(S_DTOP, node)]
|
|
2046
|
+
let m ← merge (← newList #[em, store, wrap]) (maxdepth := .num 1.0)
|
|
2047
|
+
inject (.str kp) m
|
|
2048
|
+
else getpath node keypath (.iinj inj)
|
|
2049
|
+
| _ => getpath node keypath (.iinj inj)
|
|
2050
|
+
for (srckey, srcnode) in (← itemsPairs src) do
|
|
2051
|
+
let k ← packKey srcnode (.str srckey)
|
|
2052
|
+
let tchild ← clone child
|
|
2053
|
+
let _ ← setprop tval k tchild
|
|
2054
|
+
let anno ← getprop srcnode (.str S_BANNO)
|
|
2055
|
+
if isNoval anno then
|
|
2056
|
+
let _ ← delprop tchild (.str S_BANNO)
|
|
2057
|
+
else
|
|
2058
|
+
let _ ← setprop tchild (.str S_BANNO) anno
|
|
2059
|
+
let rvalRef ← IO.mkRef (← emptyMap)
|
|
2060
|
+
if !(← isempty tval) then do
|
|
2061
|
+
let tsrc ← emptyMap
|
|
2062
|
+
let srcItems ← listItemsOf src
|
|
2063
|
+
for i in [0:srcItems.size] do
|
|
2064
|
+
let node := srcItems[i]!
|
|
2065
|
+
let kn ← packKey node (vInt i)
|
|
2066
|
+
let _ ← setprop tsrc kn node
|
|
2067
|
+
let tpath ← slice d.path (start := .num (-1.0))
|
|
2068
|
+
let ckey ← getelem d.path (.num (-2.0))
|
|
2069
|
+
let mut dpathArr : Array Value := #[.str S_DTOP]
|
|
2070
|
+
match srcpath with
|
|
2071
|
+
| .str sp =>
|
|
2072
|
+
for p in sp.splitOn "." do
|
|
2073
|
+
if p != S_MT then dpathArr := dpathArr.push (.str p)
|
|
2074
|
+
| _ => pure ()
|
|
2075
|
+
let ckstr ← jsString ckey
|
|
2076
|
+
dpathArr := dpathArr.push (.str ("$:" ++ ckstr))
|
|
2077
|
+
let tcurRef ← IO.mkRef (← newMap #[(ckstr, tsrc)])
|
|
2078
|
+
if (← size tpath) > 1 then do
|
|
2079
|
+
let pkey ← getelem d.path (.num (-3.0)) (.str S_DTOP)
|
|
2080
|
+
let pkstr ← jsString pkey
|
|
2081
|
+
dpathArr := dpathArr.push (.str ("$:" ++ pkstr))
|
|
2082
|
+
tcurRef.set (← newMap #[(pkstr, ← tcurRef.get)])
|
|
2083
|
+
let tinj ← makeChildInj inj 0 (← newList #[ckey])
|
|
2084
|
+
let tnodes ← slice d.nodes (start := .num (-1.0))
|
|
2085
|
+
modInj tinj (fun dd => { dd with path := tpath, nodes := tnodes })
|
|
2086
|
+
let tparent ← getelem tnodes (.num (-1.0))
|
|
2087
|
+
let dpathV ← newList dpathArr
|
|
2088
|
+
let tcur ← tcurRef.get
|
|
2089
|
+
modInj tinj (fun dd => { dd with
|
|
2090
|
+
parent := tparent, ival := tval, dpath := dpathV, dparent := tcur })
|
|
2091
|
+
let _ ← inject tval store (.iinj tinj)
|
|
2092
|
+
rvalRef.set (← getInj tinj).ival
|
|
2093
|
+
let _ ← setprop target tkey (← rvalRef.get)
|
|
2094
|
+
pure .noval
|
|
2095
|
+
|
|
2096
|
+
partial def transformRef : InjectorFn := fun inj v _ref store => do
|
|
2097
|
+
let d ← getInj inj
|
|
2098
|
+
if d.mode != M_VAL then pure .noval
|
|
2099
|
+
else do
|
|
2100
|
+
let nodes := d.nodes
|
|
2101
|
+
let refpath ← lookupRaw d.parent (.num 1.0)
|
|
2102
|
+
let nkeys ← size d.keys
|
|
2103
|
+
modInj inj (fun dd => { dd with keyi := nkeys })
|
|
2104
|
+
let specFunc ← getprop store (.str S_DSPEC)
|
|
2105
|
+
match specFunc with
|
|
2106
|
+
| .func fid => do
|
|
2107
|
+
let spec ← callFunc fid inj .noval "" .noval
|
|
2108
|
+
let refv ← getpath spec refpath
|
|
2109
|
+
let hasSub ← IO.mkRef false
|
|
2110
|
+
if isnode refv then
|
|
2111
|
+
let _ ← walk refv (before := some (fun _k v2 _p _path => do
|
|
2112
|
+
if v2 == .str "`$REF`" then hasSub.set true
|
|
2113
|
+
pure v2))
|
|
2114
|
+
let tref ← clone refv
|
|
2115
|
+
let d2 ← getInj inj
|
|
2116
|
+
let psize ← size d2.path
|
|
2117
|
+
let cpath ← slice d2.path (start := .num 0.0) (stop := vInt (psize - 3))
|
|
2118
|
+
let tpath ← slice d2.path (start := .num 0.0) (stop := vInt (psize - 1))
|
|
2119
|
+
let tcur ← getpath store cpath
|
|
2120
|
+
let tval ← getpath store tpath
|
|
2121
|
+
let rvalRef ← IO.mkRef Value.noval
|
|
2122
|
+
let hs ← hasSub.get
|
|
2123
|
+
if !(isNoval refv) && (!hs || !(isNoval tval)) then do
|
|
2124
|
+
let lastp ← getelem tpath (.num (-1.0))
|
|
2125
|
+
let cs ← makeChildInj inj 0 (← newList #[lastp])
|
|
2126
|
+
let nsize ← size d2.nodes
|
|
2127
|
+
let cnodes ← slice d2.nodes (start := .num 0.0) (stop := vInt (nsize - 1))
|
|
2128
|
+
let cparent ← getelem nodes (.num (-2.0))
|
|
2129
|
+
modInj cs (fun dd => { dd with
|
|
2130
|
+
path := tpath, nodes := cnodes, parent := cparent,
|
|
2131
|
+
ival := tref, dparent := tcur })
|
|
2132
|
+
let _ ← inject tref store (.iinj cs)
|
|
2133
|
+
rvalRef.set (← getInj cs).ival
|
|
2134
|
+
injSetval inj (← rvalRef.get) (ancestor := 2)
|
|
2135
|
+
let d3 ← getInj inj
|
|
2136
|
+
(match d3.prior with
|
|
2137
|
+
| some p =>
|
|
2138
|
+
if islist d3.parent then
|
|
2139
|
+
modInj p (fun dd => { dd with keyi := dd.keyi - 1 })
|
|
2140
|
+
else pure ()
|
|
2141
|
+
| none => pure ())
|
|
2142
|
+
pure v
|
|
2143
|
+
| _ => pure .noval
|
|
2144
|
+
|
|
2145
|
+
-- ---------------------------------------------------------------------------
|
|
2146
|
+
-- $FORMAT formatters / placement checks / argument checks
|
|
2147
|
+
-- ---------------------------------------------------------------------------
|
|
2148
|
+
|
|
2149
|
+
partial def formatterTbl : List (String × (Value → Value → SIO Value)) := [
|
|
2150
|
+
("identity", fun _k v => pure v),
|
|
2151
|
+
("upper", fun _k v => do
|
|
2152
|
+
if isnode v then pure v else pure (.str (← jsString v).toUpper)),
|
|
2153
|
+
("lower", fun _k v => do
|
|
2154
|
+
if isnode v then pure v else pure (.str (← jsString v).toLower)),
|
|
2155
|
+
("string", fun _k v => do
|
|
2156
|
+
if isnode v then pure v else pure (.str (← jsString v))),
|
|
2157
|
+
("number", fun _k v => do
|
|
2158
|
+
if isnode v then pure v
|
|
2159
|
+
else do
|
|
2160
|
+
let n := jsNumber v
|
|
2161
|
+
let n := if n.isNaN then 0.0 else n
|
|
2162
|
+
pure (.num n)),
|
|
2163
|
+
("integer", fun _k v => do
|
|
2164
|
+
if isnode v then pure v
|
|
2165
|
+
else do
|
|
2166
|
+
let n := jsNumber v
|
|
2167
|
+
let n := if n.isNaN then 0.0 else n
|
|
2168
|
+
pure (.num (toInt32 n))),
|
|
2169
|
+
("concat", fun k v => do
|
|
2170
|
+
if isNoval k && islist v then do
|
|
2171
|
+
let parts ← itemsV v (fun (_, x) => do
|
|
2172
|
+
if isnode x then pure (Value.str S_MT) else pure (Value.str (← jsString x)))
|
|
2173
|
+
pure (.str (← join parts (sep := .str S_MT)))
|
|
2174
|
+
else pure v)]
|
|
2175
|
+
|
|
2176
|
+
partial def checkPlacement (modes : Nat) (ijname : String) (parenttypes : Nat)
|
|
2177
|
+
(inj : InjId) : SIO Bool := do
|
|
2178
|
+
let d ← getInj inj
|
|
2179
|
+
let modenum := d.mode
|
|
2180
|
+
if (modes &&& modenum) == 0 then do
|
|
2181
|
+
let allowed := [M_KEYPRE, M_KEYPOST, M_VAL].filter (fun m => (modes &&& m) != 0)
|
|
2182
|
+
let placements := String.intercalate ","
|
|
2183
|
+
(allowed.map (fun m => if m == M_VAL then "value" else "key"))
|
|
2184
|
+
let cur := if modenum == M_VAL then "value" else "key"
|
|
2185
|
+
pushErr inj ("$" ++ ijname ++ ": invalid placement as " ++ cur
|
|
2186
|
+
++ ", expected: " ++ placements ++ ".")
|
|
2187
|
+
pure false
|
|
2188
|
+
else if !(← isempty (vInt parenttypes)) then do
|
|
2189
|
+
let ptype := typify d.parent
|
|
2190
|
+
if (parenttypes &&& ptype) == 0 then do
|
|
2191
|
+
pushErr inj ("$" ++ ijname ++ ": invalid placement in parent "
|
|
2192
|
+
++ typename (ptype : Int) ++ ", expected: " ++ typename (parenttypes : Int) ++ ".")
|
|
2193
|
+
pure false
|
|
2194
|
+
else pure true
|
|
2195
|
+
else pure true
|
|
2196
|
+
|
|
2197
|
+
partial def injectorArgs (argtypes : List Nat) (args : Value) : SIO (List Value) := do
|
|
2198
|
+
let numargs := argtypes.length
|
|
2199
|
+
let foundRef ← IO.mkRef (Array.replicate (1 + numargs) Value.noval)
|
|
2200
|
+
let mut argi : Nat := 0
|
|
2201
|
+
let mut stop := false
|
|
2202
|
+
for at_ in argtypes do
|
|
2203
|
+
if !stop then
|
|
2204
|
+
let arg ← getelem args (vInt (argi : Int))
|
|
2205
|
+
let argtype := typify arg
|
|
2206
|
+
if (at_ &&& argtype) == 0 then do
|
|
2207
|
+
let msg := "invalid argument: " ++ (← stringify arg (maxlen := .num 22.0))
|
|
2208
|
+
++ " (" ++ typename (argtype : Int) ++ " at position " ++ toString (1 + argi)
|
|
2209
|
+
++ ") is not of type: " ++ typename (at_ : Int) ++ "."
|
|
2210
|
+
foundRef.modify (fun a => a.set! 0 (Value.str msg))
|
|
2211
|
+
stop := true
|
|
2212
|
+
else
|
|
2213
|
+
foundRef.modify (fun a => a.set! (1 + argi) arg)
|
|
2214
|
+
argi := argi + 1
|
|
2215
|
+
pure (← foundRef.get).toList
|
|
2216
|
+
|
|
2217
|
+
partial def injectChild (child : Value) (store : Value) (inj : InjId) : SIO InjId := do
|
|
2218
|
+
let d ← getInj inj
|
|
2219
|
+
let cinjRef ← IO.mkRef inj
|
|
2220
|
+
(match d.prior with
|
|
2221
|
+
| some prior => do
|
|
2222
|
+
let pd ← getInj prior
|
|
2223
|
+
(match pd.prior with
|
|
2224
|
+
| some pprior => do
|
|
2225
|
+
let c ← makeChildInj pprior pd.keyi pd.keys
|
|
2226
|
+
modInj c (fun dd => { dd with ival := child })
|
|
2227
|
+
let _ ← setprop (← getInj c).parent pd.key child
|
|
2228
|
+
cinjRef.set c
|
|
2229
|
+
| none => do
|
|
2230
|
+
let c ← makeChildInj prior d.keyi d.keys
|
|
2231
|
+
modInj c (fun dd => { dd with ival := child })
|
|
2232
|
+
let _ ← setprop (← getInj c).parent d.key child
|
|
2233
|
+
cinjRef.set c)
|
|
2234
|
+
| none => pure ())
|
|
2235
|
+
let cinj ← cinjRef.get
|
|
2236
|
+
let _ ← inject child store (.iinj cinj)
|
|
2237
|
+
pure cinj
|
|
2238
|
+
|
|
2239
|
+
partial def transformFormat : InjectorFn := fun inj _v _ref store => do
|
|
2240
|
+
let d0 ← getInj inj
|
|
2241
|
+
let _ ← slice d0.keys (start := .num 0.0) (stop := .num 1.0) (mutate := true)
|
|
2242
|
+
if d0.mode != M_VAL then pure .noval
|
|
2243
|
+
else do
|
|
2244
|
+
let d ← getInj inj
|
|
2245
|
+
let name ← lookupRaw d.parent (.num 1.0)
|
|
2246
|
+
let child ← lookupRaw d.parent (.num 2.0)
|
|
2247
|
+
let tkey ← getelem d.path (.num (-2.0))
|
|
2248
|
+
let target ← do
|
|
2249
|
+
let t ← getelem d.nodes (.num (-2.0))
|
|
2250
|
+
if isNullish t then getelem d.nodes (.num (-1.0)) else pure t
|
|
2251
|
+
let cinj ← injectChild child store inj
|
|
2252
|
+
let resolved := (← getInj cinj).ival
|
|
2253
|
+
let formatter : Option (Value → Value → SIO Value) ←
|
|
2254
|
+
if (T_FUNCTION &&& typify name) > 0 then
|
|
2255
|
+
match name with
|
|
2256
|
+
| .func fid => pure (some (fun k v => do
|
|
2257
|
+
callFunc fid (← getDummyInj) v (← jsString k) .noval))
|
|
2258
|
+
| _ => pure none
|
|
2259
|
+
else do
|
|
2260
|
+
let nm ← jsString name
|
|
2261
|
+
pure ((formatterTbl.find? (·.1 == nm)).map (·.2))
|
|
2262
|
+
match formatter with
|
|
2263
|
+
| none => do
|
|
2264
|
+
pushErr inj ("$FORMAT: unknown format: " ++ (← jsString name) ++ ".")
|
|
2265
|
+
pure .noval
|
|
2266
|
+
| some f => do
|
|
2267
|
+
let out ← walk resolved (before := some (fun k v _p _path => f k v))
|
|
2268
|
+
let _ ← setprop target tkey out
|
|
2269
|
+
pure out
|
|
2270
|
+
|
|
2271
|
+
partial def transformApply : InjectorFn := fun inj _v _ref store => do
|
|
2272
|
+
if !(← checkPlacement M_VAL "APPLY" T_LIST inj) then pure .noval
|
|
2273
|
+
else do
|
|
2274
|
+
let d ← getInj inj
|
|
2275
|
+
let args ← slice d.parent (start := .num 1.0)
|
|
2276
|
+
let res ← injectorArgs [T_FUNCTION, T_ANY] args
|
|
2277
|
+
let err := res.getD 0 .noval
|
|
2278
|
+
let applyFn := res.getD 1 .noval
|
|
2279
|
+
let child := res.getD 2 .noval
|
|
2280
|
+
if !(isNoval err) then do
|
|
2281
|
+
pushErr inj ("$APPLY: " ++ (← jsString err))
|
|
2282
|
+
pure .noval
|
|
2283
|
+
else do
|
|
2284
|
+
let tkey ← getelem d.path (.num (-2.0))
|
|
2285
|
+
let target ← do
|
|
2286
|
+
let t ← getelem d.nodes (.num (-2.0))
|
|
2287
|
+
if isNullish t then getelem d.nodes (.num (-1.0)) else pure t
|
|
2288
|
+
let cinj ← injectChild child store inj
|
|
2289
|
+
let resolved := (← getInj cinj).ival
|
|
2290
|
+
let out ← match applyFn with
|
|
2291
|
+
| .func fid => callFunc fid cinj resolved "" store
|
|
2292
|
+
| _ => pure Value.noval
|
|
2293
|
+
let _ ← setprop target tkey out
|
|
2294
|
+
pure out
|
|
2295
|
+
|
|
2296
|
+
-- ---------------------------------------------------------------------------
|
|
2297
|
+
-- transform
|
|
2298
|
+
-- ---------------------------------------------------------------------------
|
|
2299
|
+
|
|
2300
|
+
partial def transform (data spec : Value) (inj : InjArg := .inone) : SIO Value := do
|
|
2301
|
+
let origspec := spec
|
|
2302
|
+
let spec ← clone spec
|
|
2303
|
+
let extra := match inj with
|
|
2304
|
+
| .idef d => d.dExtra
|
|
2305
|
+
| _ => .noval
|
|
2306
|
+
let collect := match inj with
|
|
2307
|
+
| .idef d => !(isNoval d.dErrs)
|
|
2308
|
+
| _ => false
|
|
2309
|
+
let errs ← match inj with
|
|
2310
|
+
| .idef d => if collect then pure d.dErrs else emptyList
|
|
2311
|
+
| _ => emptyList
|
|
2312
|
+
let extraTransforms ← emptyMap
|
|
2313
|
+
let extraData ← emptyMap
|
|
2314
|
+
if !(isNoval extra) then
|
|
2315
|
+
for (k, v) in (← itemsPairs extra) do
|
|
2316
|
+
if k.startsWith S_DS then
|
|
2317
|
+
let _ ← setprop extraTransforms (.str k) v
|
|
2318
|
+
else
|
|
2319
|
+
let _ ← setprop extraData (.str k) v
|
|
2320
|
+
let edPart ← if (← isempty extraData) then pure Value.noval else clone extraData
|
|
2321
|
+
let dataClone ← merge (← newList #[edPart, ← clone data])
|
|
2322
|
+
let store ← emptyMap
|
|
2323
|
+
let put (k : String) (v : Value) : SIO Unit := do
|
|
2324
|
+
let _ ← setprop store (.str k) v
|
|
2325
|
+
put S_DTOP dataClone
|
|
2326
|
+
put S_DSPEC (← vFunc (fun _ _ _ _ => pure origspec))
|
|
2327
|
+
put "$BT" (← vFunc (fun _ _ _ _ => pure (.str S_BT)))
|
|
2328
|
+
put "$DS" (← vFunc (fun _ _ _ _ => pure (.str S_DS)))
|
|
2329
|
+
put "$WHEN" (← vFunc (fun _ _ _ _ => pure (.str "1970-01-01T00:00:00.000Z")))
|
|
2330
|
+
put "$DELETE" (← vFunc transformDelete)
|
|
2331
|
+
put "$COPY" (← vFunc transformCopy)
|
|
2332
|
+
put "$KEY" (← vFunc transformKey)
|
|
2333
|
+
put "$ANNO" (← vFunc transformAnno)
|
|
2334
|
+
put "$MERGE" (← vFunc transformMerge)
|
|
2335
|
+
put "$EACH" (← vFunc transformEach)
|
|
2336
|
+
put "$PACK" (← vFunc transformPack)
|
|
2337
|
+
put "$REF" (← vFunc transformRef)
|
|
2338
|
+
put "$FORMAT" (← vFunc transformFormat)
|
|
2339
|
+
put "$APPLY" (← vFunc transformApply)
|
|
2340
|
+
for (k, v) in (← itemsPairs extraTransforms) do
|
|
2341
|
+
put k v
|
|
2342
|
+
put S_DERRS errs
|
|
2343
|
+
let idef0 : InjDef := { dErrs := errs }
|
|
2344
|
+
let idef := match inj with
|
|
2345
|
+
| .idef d => { idef0 with
|
|
2346
|
+
dMeta := d.dMeta, dModify := d.dModify,
|
|
2347
|
+
dHandler := d.dHandler, dBase := d.dBase }
|
|
2348
|
+
| _ => idef0
|
|
2349
|
+
let out ← inject spec store (.idef idef)
|
|
2350
|
+
if (← size errs) > 0 && !collect then
|
|
2351
|
+
throw (IO.userError (← join errs (sep := .str " | ")))
|
|
2352
|
+
pure out
|
|
2353
|
+
|
|
2354
|
+
-- ---------------------------------------------------------------------------
|
|
2355
|
+
-- validate
|
|
2356
|
+
-- ---------------------------------------------------------------------------
|
|
2357
|
+
|
|
2358
|
+
partial def invalidTypeMsg (path : Value) (needtype : String) (vt : Int) (v : Value) :
|
|
2359
|
+
SIO String := do
|
|
2360
|
+
let vs ← if isNullish v then pure "no value" else stringify v
|
|
2361
|
+
let fieldPart ← if (← size path) > 1 then do
|
|
2362
|
+
pure ("field " ++ (← pathify path (startin := .num 1.0)) ++ " to be ")
|
|
2363
|
+
else pure ""
|
|
2364
|
+
let typePart := if !(isNullish v) then typename vt ++ S_VIZ else ""
|
|
2365
|
+
return "Expected " ++ fieldPart ++ needtype ++ ", but found " ++ typePart ++ vs ++ "."
|
|
2366
|
+
|
|
2367
|
+
partial def validateString : InjectorFn := fun inj _v _ref _store => do
|
|
2368
|
+
let d ← getInj inj
|
|
2369
|
+
let out ← lookupRaw d.dparent d.key
|
|
2370
|
+
let t := typify out
|
|
2371
|
+
if (T_STRING &&& t) == 0 then do
|
|
2372
|
+
pushErr inj (← invalidTypeMsg d.path S_STRING (t : Int) out)
|
|
2373
|
+
pure .noval
|
|
2374
|
+
else if out == .str S_MT then do
|
|
2375
|
+
pushErr inj ("Empty string at " ++ (← pathify d.path (startin := .num 1.0)))
|
|
2376
|
+
pure .noval
|
|
2377
|
+
else pure out
|
|
2378
|
+
|
|
2379
|
+
partial def validateType : InjectorFn := fun inj _v refstr _store => do
|
|
2380
|
+
let d ← getInj inj
|
|
2381
|
+
let tname := if refstr.length > 1 then (strDrop refstr 1).toLower else "any"
|
|
2382
|
+
let idx : Int := Id.run do
|
|
2383
|
+
let mut r : Int := -1
|
|
2384
|
+
for i in [0:typenameTbl.size] do
|
|
2385
|
+
if typenameTbl[i]! == tname && r < 0 then r := i
|
|
2386
|
+
return r
|
|
2387
|
+
let typev0 : Nat := if idx >= 0 then 1 <<< (31 - idx.toNat) else 0
|
|
2388
|
+
let typev := if tname == S_NIL then typev0 ||| T_NULL else typev0
|
|
2389
|
+
let out ← lookupRaw d.dparent d.key
|
|
2390
|
+
let t := typify out
|
|
2391
|
+
if (t &&& typev) == 0 then do
|
|
2392
|
+
pushErr inj (← invalidTypeMsg d.path tname (t : Int) out)
|
|
2393
|
+
pure .noval
|
|
2394
|
+
else pure out
|
|
2395
|
+
|
|
2396
|
+
partial def validateAny : InjectorFn := fun inj _v _ref _store => do
|
|
2397
|
+
let d ← getInj inj
|
|
2398
|
+
lookupRaw d.dparent d.key
|
|
2399
|
+
|
|
2400
|
+
partial def validateChild : InjectorFn := fun inj _v _ref _store => do
|
|
2401
|
+
let d ← getInj inj
|
|
2402
|
+
let parent := d.parent
|
|
2403
|
+
let key := d.key
|
|
2404
|
+
let path := d.path
|
|
2405
|
+
let keys := d.keys
|
|
2406
|
+
if d.mode == M_KEYPRE then do
|
|
2407
|
+
let childtm ← getprop parent key
|
|
2408
|
+
let pkey ← getelem path (.num (-2.0))
|
|
2409
|
+
let tval ← getprop d.dparent pkey
|
|
2410
|
+
if isNoval tval then do
|
|
2411
|
+
let _ ← delprop parent key
|
|
2412
|
+
pure .noval
|
|
2413
|
+
else if !(ismap tval) then do
|
|
2414
|
+
let psize ← size path
|
|
2415
|
+
let ppath ← slice path (start := .num 0.0) (stop := vInt (psize - 1))
|
|
2416
|
+
pushErr inj (← invalidTypeMsg ppath S_OBJECT ((typify tval : Nat) : Int) tval)
|
|
2417
|
+
pure .noval
|
|
2418
|
+
else do
|
|
2419
|
+
for ckey in (← keysof tval) do
|
|
2420
|
+
let _ ← setprop parent (.str ckey) (← clone childtm)
|
|
2421
|
+
let n ← size keys
|
|
2422
|
+
let _ ← setprop keys (vInt n) (.str ckey)
|
|
2423
|
+
let _ ← delprop parent key
|
|
2424
|
+
pure .noval
|
|
2425
|
+
else if d.mode == M_VAL then do
|
|
2426
|
+
let childtm ← getprop parent (.num 1.0)
|
|
2427
|
+
if !(islist parent) then do
|
|
2428
|
+
pushErr inj "Invalid $CHILD as value"
|
|
2429
|
+
pure .noval
|
|
2430
|
+
else if isNoval d.dparent then do
|
|
2431
|
+
(match parent with
|
|
2432
|
+
| .list r => setListItems r #[]
|
|
2433
|
+
| _ => pure ())
|
|
2434
|
+
pure .noval
|
|
2435
|
+
else if !(islist d.dparent) then do
|
|
2436
|
+
let psize ← size path
|
|
2437
|
+
let ppath ← slice path (start := .num 0.0) (stop := vInt (psize - 1))
|
|
2438
|
+
pushErr inj (← invalidTypeMsg ppath S_LIST ((typify d.dparent : Nat) : Int) d.dparent)
|
|
2439
|
+
let np ← size parent
|
|
2440
|
+
modInj inj (fun dd => { dd with keyi := np })
|
|
2441
|
+
pure d.dparent
|
|
2442
|
+
else do
|
|
2443
|
+
for (k, _) in (← itemsPairs d.dparent) do
|
|
2444
|
+
let _ ← setprop parent (.str k) (← clone childtm)
|
|
2445
|
+
(match parent with
|
|
2446
|
+
| .list r => do
|
|
2447
|
+
let n ← size d.dparent
|
|
2448
|
+
let xs ← listItems r
|
|
2449
|
+
setListItems r (xs.extract 0 (min n.toNat xs.size))
|
|
2450
|
+
| _ => pure ())
|
|
2451
|
+
modInj inj (fun dd => { dd with keyi := 0 })
|
|
2452
|
+
getprop d.dparent (.num 0.0)
|
|
2453
|
+
else pure .noval
|
|
2454
|
+
|
|
2455
|
+
/-- Deep structural equality (validate $EXACT). -/
|
|
2456
|
+
partial def veq (a b : Value) : SIO Bool := do
|
|
2457
|
+
match a, b with
|
|
2458
|
+
| .noval, .noval => pure true
|
|
2459
|
+
| .null, .null => pure true
|
|
2460
|
+
| .bool x, .bool y => pure (x == y)
|
|
2461
|
+
| .num x, .num y => pure (x == y)
|
|
2462
|
+
| .str x, .str y => pure (x == y)
|
|
2463
|
+
| .sentinel x, .sentinel y => pure (x == y)
|
|
2464
|
+
| .list x, .list y => do
|
|
2465
|
+
let xs ← listItems x
|
|
2466
|
+
let ys ← listItems y
|
|
2467
|
+
if xs.size != ys.size then pure false
|
|
2468
|
+
else do
|
|
2469
|
+
for i in [0:xs.size] do
|
|
2470
|
+
if !(← veq xs[i]! ys[i]!) then return false
|
|
2471
|
+
pure true
|
|
2472
|
+
| .map x, .map y => do
|
|
2473
|
+
let xes ← mapEntries x
|
|
2474
|
+
let yes ← mapEntries y
|
|
2475
|
+
if xes.size != yes.size then pure false
|
|
2476
|
+
else do
|
|
2477
|
+
for (k, v) in xes do
|
|
2478
|
+
match omapGet yes k with
|
|
2479
|
+
| some w =>
|
|
2480
|
+
if !(← veq v w) then return false
|
|
2481
|
+
| none => return false
|
|
2482
|
+
pure true
|
|
2483
|
+
| _, _ => pure false
|
|
2484
|
+
|
|
2485
|
+
partial def validateExact : InjectorFn := fun inj _v _ref _store => do
|
|
2486
|
+
let d ← getInj inj
|
|
2487
|
+
if d.mode == M_VAL then do
|
|
2488
|
+
let parent := d.parent
|
|
2489
|
+
if !(islist parent) || d.keyi != 0 then do
|
|
2490
|
+
pushErr inj ("The $EXACT validator at field "
|
|
2491
|
+
++ (← pathify d.path (startin := .num 1.0) (endin := .num 1.0))
|
|
2492
|
+
++ " must be the first element of an array.")
|
|
2493
|
+
pure .noval
|
|
2494
|
+
else do
|
|
2495
|
+
let nk ← size d.keys
|
|
2496
|
+
modInj inj (fun dd => { dd with keyi := nk })
|
|
2497
|
+
injSetval inj d.dparent (ancestor := 2)
|
|
2498
|
+
let psize ← size d.path
|
|
2499
|
+
let npath ← slice d.path (start := .num 0.0) (stop := vInt (psize - 1))
|
|
2500
|
+
let nkey ← getelem npath (.num (-1.0))
|
|
2501
|
+
modInj inj (fun dd => { dd with path := npath, key := nkey })
|
|
2502
|
+
let tvals ← slice parent (start := .num 1.0)
|
|
2503
|
+
if (← size tvals) == 0 then do
|
|
2504
|
+
let d2 ← getInj inj
|
|
2505
|
+
pushErr inj ("The $EXACT validator at field "
|
|
2506
|
+
++ (← pathify d2.path (startin := .num 1.0) (endin := .num 1.0))
|
|
2507
|
+
++ " must have at least one argument.")
|
|
2508
|
+
pure .noval
|
|
2509
|
+
else do
|
|
2510
|
+
let mut matched := false
|
|
2511
|
+
for tval in (← listItemsOf tvals) do
|
|
2512
|
+
if !matched && (← veq tval d.dparent) then matched := true
|
|
2513
|
+
if !matched then do
|
|
2514
|
+
let d2 ← getInj inj
|
|
2515
|
+
let mut descs : List String := []
|
|
2516
|
+
for (_, x) in (← itemsPairs tvals) do
|
|
2517
|
+
descs := (← stringify x) :: descs
|
|
2518
|
+
let valdesc := replaceTransformNames (String.intercalate ", " descs.reverse)
|
|
2519
|
+
let ntv ← size tvals
|
|
2520
|
+
pushErr inj (← invalidTypeMsg d2.path
|
|
2521
|
+
((if (← size d2.path) > 1 then "" else "value ")
|
|
2522
|
+
++ "exactly equal to " ++ (if ntv == 1 then "" else "one of ") ++ valdesc)
|
|
2523
|
+
((typify d.dparent : Nat) : Int) d.dparent)
|
|
2524
|
+
pure .noval
|
|
2525
|
+
else do
|
|
2526
|
+
let _ ← delprop d.parent d.key
|
|
2527
|
+
pure .noval
|
|
2528
|
+
|
|
2529
|
+
/-- The validate modify callback: structural checks at each node. -/
|
|
2530
|
+
partial def validation : ModifyFn := fun pval key parent inj => do
|
|
2531
|
+
if isSkip pval then pure ()
|
|
2532
|
+
else do
|
|
2533
|
+
let d ← getInj inj
|
|
2534
|
+
let exact ← getprop d.imeta (.str S_BEXACT) (.bool false)
|
|
2535
|
+
let cval ← getprop d.dparent key
|
|
2536
|
+
let exactB := exact == .bool true
|
|
2537
|
+
if !exactB && isNoval cval then pure ()
|
|
2538
|
+
else do
|
|
2539
|
+
let ptype := typify pval
|
|
2540
|
+
if (T_STRING &&& ptype) > 0 && strContains (← jsString pval) '$' then pure ()
|
|
2541
|
+
else do
|
|
2542
|
+
let ctype := typify cval
|
|
2543
|
+
if ptype != ctype && !(isNoval pval) then
|
|
2544
|
+
pushErr inj (← invalidTypeMsg d.path (typename (ptype : Int)) (ctype : Int) cval)
|
|
2545
|
+
else if ismap cval then do
|
|
2546
|
+
if !(ismap pval) then
|
|
2547
|
+
pushErr inj (← invalidTypeMsg d.path (typename (ptype : Int)) (ctype : Int) cval)
|
|
2548
|
+
else do
|
|
2549
|
+
let ckeys ← keysof cval
|
|
2550
|
+
let pkeys ← keysof pval
|
|
2551
|
+
let bopen ← getprop pval (.str S_BOPEN)
|
|
2552
|
+
if pkeys.size > 0 && !(bopen == .bool true) then do
|
|
2553
|
+
let mut badkeys : List String := []
|
|
2554
|
+
for ck in ckeys do
|
|
2555
|
+
if isNoval (← lookupRaw pval (.str ck)) then badkeys := ck :: badkeys
|
|
2556
|
+
if badkeys.length > 0 then
|
|
2557
|
+
pushErr inj ("Unexpected keys at field "
|
|
2558
|
+
++ (← pathify d.path (startin := .num 1.0)) ++ S_VIZ
|
|
2559
|
+
++ String.intercalate ", " badkeys.reverse)
|
|
2560
|
+
else do
|
|
2561
|
+
let _ ← merge (← newList #[pval, cval])
|
|
2562
|
+
if isnode pval then
|
|
2563
|
+
let _ ← delprop pval (.str S_BOPEN)
|
|
2564
|
+
else if islist cval then do
|
|
2565
|
+
if !(islist pval) then
|
|
2566
|
+
pushErr inj (← invalidTypeMsg d.path (typename (ptype : Int)) (ctype : Int) cval)
|
|
2567
|
+
else if exactB then do
|
|
2568
|
+
if !(← veq cval pval) then do
|
|
2569
|
+
let pathmsg ← if (← size d.path) > 1 then do
|
|
2570
|
+
pure ("at field " ++ (← pathify d.path (startin := .num 1.0)) ++ ": ")
|
|
2571
|
+
else pure ""
|
|
2572
|
+
pushErr inj ("Value " ++ pathmsg ++ (← jsString cval)
|
|
2573
|
+
++ " should equal " ++ (← jsString pval) ++ ".")
|
|
2574
|
+
else
|
|
2575
|
+
let _ ← setprop parent key cval
|
|
2576
|
+
|
|
2577
|
+
partial def validateHandler : InjectorFn := fun inj v refstr store => do
|
|
2578
|
+
match metaPathMatch refstr with
|
|
2579
|
+
| some (_, g2, _) => do
|
|
2580
|
+
if g2 == "=" then
|
|
2581
|
+
injSetval inj (← newList #[.str S_BEXACT, v])
|
|
2582
|
+
else
|
|
2583
|
+
injSetval inj v
|
|
2584
|
+
modInj inj (fun dd => { dd with keyi := -1 })
|
|
2585
|
+
pure SKIP
|
|
2586
|
+
| none => injectHandler inj v refstr store
|
|
2587
|
+
|
|
2588
|
+
mutual
|
|
2589
|
+
|
|
2590
|
+
partial def validateOne : InjectorFn := fun inj _v _ref store => do
|
|
2591
|
+
let d ← getInj inj
|
|
2592
|
+
if d.mode == M_VAL then do
|
|
2593
|
+
let parent := d.parent
|
|
2594
|
+
if !(islist parent) || d.keyi != 0 then do
|
|
2595
|
+
pushErr inj ("The $ONE validator at field "
|
|
2596
|
+
++ (← pathify d.path (startin := .num 1.0) (endin := .num 1.0))
|
|
2597
|
+
++ " must be the first element of an array.")
|
|
2598
|
+
pure .noval
|
|
2599
|
+
else do
|
|
2600
|
+
let nk ← size d.keys
|
|
2601
|
+
modInj inj (fun dd => { dd with keyi := nk })
|
|
2602
|
+
injSetval inj d.dparent (ancestor := 2)
|
|
2603
|
+
let psize ← size d.path
|
|
2604
|
+
let npath ← slice d.path (start := .num 0.0) (stop := vInt (psize - 1))
|
|
2605
|
+
let nkey ← getelem npath (.num (-1.0))
|
|
2606
|
+
modInj inj (fun dd => { dd with path := npath, key := nkey })
|
|
2607
|
+
let tvals ← slice parent (start := .num 1.0)
|
|
2608
|
+
if (← size tvals) == 0 then do
|
|
2609
|
+
let d2 ← getInj inj
|
|
2610
|
+
pushErr inj ("The $ONE validator at field "
|
|
2611
|
+
++ (← pathify d2.path (startin := .num 1.0) (endin := .num 1.0))
|
|
2612
|
+
++ " must have at least one argument.")
|
|
2613
|
+
pure .noval
|
|
2614
|
+
else do
|
|
2615
|
+
let mut matched := false
|
|
2616
|
+
for tval in (← listItemsOf tvals) do
|
|
2617
|
+
if !matched then do
|
|
2618
|
+
let terrs ← emptyList
|
|
2619
|
+
let em ← emptyMap
|
|
2620
|
+
let vstore ← merge (← newList #[em, store]) (maxdepth := .num 1.0)
|
|
2621
|
+
let _ ← setprop vstore (.str S_DTOP) d.dparent
|
|
2622
|
+
let idef : InjDef := { dExtra := vstore, dErrs := terrs, dMeta := d.imeta }
|
|
2623
|
+
let vcurrent ← validate d.dparent tval (.idef idef)
|
|
2624
|
+
injSetval inj vcurrent (ancestor := -2)
|
|
2625
|
+
if (← size terrs) == 0 then matched := true
|
|
2626
|
+
if !matched then do
|
|
2627
|
+
let d2 ← getInj inj
|
|
2628
|
+
let mut descs : List String := []
|
|
2629
|
+
for (_, x) in (← itemsPairs tvals) do
|
|
2630
|
+
descs := (← stringify x) :: descs
|
|
2631
|
+
let valdesc := replaceTransformNames (String.intercalate ", " descs.reverse)
|
|
2632
|
+
let ntv ← size tvals
|
|
2633
|
+
pushErr inj (← invalidTypeMsg d2.path
|
|
2634
|
+
((if ntv > 1 then "one of " else "") ++ valdesc)
|
|
2635
|
+
((typify d.dparent : Nat) : Int) d.dparent)
|
|
2636
|
+
pure .noval
|
|
2637
|
+
else pure .noval
|
|
2638
|
+
|
|
2639
|
+
partial def validate (data spec : Value) (inj : InjArg := .inone) : SIO Value := do
|
|
2640
|
+
let extra := match inj with
|
|
2641
|
+
| .idef d => d.dExtra
|
|
2642
|
+
| _ => .noval
|
|
2643
|
+
let collect := match inj with
|
|
2644
|
+
| .idef d => !(isNoval d.dErrs)
|
|
2645
|
+
| _ => false
|
|
2646
|
+
let errs ← match inj with
|
|
2647
|
+
| .idef d => if collect then pure d.dErrs else emptyList
|
|
2648
|
+
| _ => emptyList
|
|
2649
|
+
let base ← emptyMap
|
|
2650
|
+
let put (k : String) (v : Value) : SIO Unit := do
|
|
2651
|
+
let _ ← setprop base (.str k) v
|
|
2652
|
+
for k in ["$DELETE", "$COPY", "$KEY", "$META", "$MERGE", "$EACH", "$PACK"] do
|
|
2653
|
+
put k .null
|
|
2654
|
+
put "$STRING" (← vFunc validateString)
|
|
2655
|
+
for k in ["$NUMBER", "$INTEGER", "$DECIMAL", "$BOOLEAN", "$NULL", "$NIL",
|
|
2656
|
+
"$MAP", "$LIST", "$FUNCTION", "$INSTANCE"] do
|
|
2657
|
+
put k (← vFunc validateType)
|
|
2658
|
+
put "$ANY" (← vFunc validateAny)
|
|
2659
|
+
put "$CHILD" (← vFunc validateChild)
|
|
2660
|
+
put "$ONE" (← vFunc validateOne)
|
|
2661
|
+
put "$EXACT" (← vFunc validateExact)
|
|
2662
|
+
let extraV ← if isNoval extra then emptyMap else pure extra
|
|
2663
|
+
let errsWrap ← newMap #[(S_DERRS, errs)]
|
|
2664
|
+
let store ← merge (← newList #[base, extraV, errsWrap]) (maxdepth := .num 1.0)
|
|
2665
|
+
let meta0 ← match inj with
|
|
2666
|
+
| .idef d => if !(isNoval d.dMeta) then pure d.dMeta else emptyMap
|
|
2667
|
+
| _ => emptyMap
|
|
2668
|
+
let bexact ← getprop meta0 (.str S_BEXACT) (.bool false)
|
|
2669
|
+
let _ ← setprop meta0 (.str S_BEXACT) bexact
|
|
2670
|
+
let modId ← registerModify validation
|
|
2671
|
+
let handlerId ← registerFunc validateHandler
|
|
2672
|
+
let idef : InjDef := { dMeta := meta0, dExtra := store, dModify := some modId,
|
|
2673
|
+
dHandler := some handlerId, dErrs := errs }
|
|
2674
|
+
let out ← transform data spec (.idef idef)
|
|
2675
|
+
if (← size errs) > 0 && !collect then
|
|
2676
|
+
throw (IO.userError (← join errs (sep := .str " | ")))
|
|
2677
|
+
pure out
|
|
2678
|
+
|
|
2679
|
+
end
|
|
2680
|
+
|
|
2681
|
+
-- ---------------------------------------------------------------------------
|
|
2682
|
+
-- select
|
|
2683
|
+
-- ---------------------------------------------------------------------------
|
|
2684
|
+
|
|
2685
|
+
partial def selectAnd : InjectorFn := fun inj _v _ref store => do
|
|
2686
|
+
let d ← getInj inj
|
|
2687
|
+
if d.mode == M_KEYPRE then do
|
|
2688
|
+
let terms ← getprop d.parent d.key
|
|
2689
|
+
let ppath ← slice d.path (start := .num (-1.0))
|
|
2690
|
+
let point ← getpath store ppath
|
|
2691
|
+
let em ← emptyMap
|
|
2692
|
+
let vstore ← merge (← newList #[em, store]) (maxdepth := .num 1.0)
|
|
2693
|
+
let _ ← setprop vstore (.str S_DTOP) point
|
|
2694
|
+
for (_, term) in (← itemsPairs terms) do
|
|
2695
|
+
let terrs ← emptyList
|
|
2696
|
+
let idef : InjDef := { dExtra := vstore, dErrs := terrs, dMeta := d.imeta }
|
|
2697
|
+
let _ ← validate point term (.idef idef)
|
|
2698
|
+
if (← size terrs) != 0 then
|
|
2699
|
+
pushErr inj ("AND:" ++ (← pathify ppath) ++ "⨯" ++ (← stringify point)
|
|
2700
|
+
++ " fail:" ++ (← stringify terms))
|
|
2701
|
+
let gkey ← getelem d.path (.num (-2.0))
|
|
2702
|
+
let gp ← getelem d.nodes (.num (-2.0))
|
|
2703
|
+
let _ ← setprop gp gkey point
|
|
2704
|
+
pure .noval
|
|
2705
|
+
else pure .noval
|
|
2706
|
+
|
|
2707
|
+
partial def selectOr : InjectorFn := fun inj _v _ref store => do
|
|
2708
|
+
let d ← getInj inj
|
|
2709
|
+
if d.mode == M_KEYPRE then do
|
|
2710
|
+
let terms ← getprop d.parent d.key
|
|
2711
|
+
let ppath ← slice d.path (start := .num (-1.0))
|
|
2712
|
+
let point ← getpath store ppath
|
|
2713
|
+
let em ← emptyMap
|
|
2714
|
+
let vstore ← merge (← newList #[em, store]) (maxdepth := .num 1.0)
|
|
2715
|
+
let _ ← setprop vstore (.str S_DTOP) point
|
|
2716
|
+
let mut done := false
|
|
2717
|
+
for (_, term) in (← itemsPairs terms) do
|
|
2718
|
+
if !done then do
|
|
2719
|
+
let terrs ← emptyList
|
|
2720
|
+
let idef : InjDef := { dExtra := vstore, dErrs := terrs, dMeta := d.imeta }
|
|
2721
|
+
let _ ← validate point term (.idef idef)
|
|
2722
|
+
if (← size terrs) == 0 then do
|
|
2723
|
+
let gkey ← getelem d.path (.num (-2.0))
|
|
2724
|
+
let gp ← getelem d.nodes (.num (-2.0))
|
|
2725
|
+
let _ ← setprop gp gkey point
|
|
2726
|
+
done := true
|
|
2727
|
+
if !done then
|
|
2728
|
+
pushErr inj ("OR:" ++ (← pathify ppath) ++ "⨯" ++ (← stringify point)
|
|
2729
|
+
++ " fail:" ++ (← stringify terms))
|
|
2730
|
+
pure .noval
|
|
2731
|
+
else pure .noval
|
|
2732
|
+
|
|
2733
|
+
partial def selectNot : InjectorFn := fun inj _v _ref store => do
|
|
2734
|
+
let d ← getInj inj
|
|
2735
|
+
if d.mode == M_KEYPRE then do
|
|
2736
|
+
let term ← getprop d.parent d.key
|
|
2737
|
+
let ppath ← slice d.path (start := .num (-1.0))
|
|
2738
|
+
let point ← getpath store ppath
|
|
2739
|
+
let em ← emptyMap
|
|
2740
|
+
let vstore ← merge (← newList #[em, store]) (maxdepth := .num 1.0)
|
|
2741
|
+
let _ ← setprop vstore (.str S_DTOP) point
|
|
2742
|
+
let terrs ← emptyList
|
|
2743
|
+
let idef : InjDef := { dExtra := vstore, dErrs := terrs, dMeta := d.imeta }
|
|
2744
|
+
let _ ← validate point term (.idef idef)
|
|
2745
|
+
if (← size terrs) == 0 then
|
|
2746
|
+
pushErr inj ("NOT:" ++ (← pathify ppath) ++ "⨯" ++ (← stringify point)
|
|
2747
|
+
++ " fail:" ++ (← stringify term))
|
|
2748
|
+
let gkey ← getelem d.path (.num (-2.0))
|
|
2749
|
+
let gp ← getelem d.nodes (.num (-2.0))
|
|
2750
|
+
let _ ← setprop gp gkey point
|
|
2751
|
+
pure .noval
|
|
2752
|
+
else pure .noval
|
|
2753
|
+
|
|
2754
|
+
def numCmp (a b : Value) (op : String) : Bool :=
|
|
2755
|
+
match a, b with
|
|
2756
|
+
| .num x, .num y =>
|
|
2757
|
+
match op with
|
|
2758
|
+
| "$GT" => x > y
|
|
2759
|
+
| "$LT" => x < y
|
|
2760
|
+
| "$GTE" => x >= y
|
|
2761
|
+
| "$LTE" => x <= y
|
|
2762
|
+
| _ => false
|
|
2763
|
+
| _, _ => false
|
|
2764
|
+
|
|
2765
|
+
partial def selectCmp : InjectorFn := fun inj _v refstr store => do
|
|
2766
|
+
let d ← getInj inj
|
|
2767
|
+
if d.mode == M_KEYPRE then do
|
|
2768
|
+
let term ← getprop d.parent d.key
|
|
2769
|
+
let gkey ← getelem d.path (.num (-2.0))
|
|
2770
|
+
let ppath ← slice d.path (start := .num (-1.0))
|
|
2771
|
+
let point ← getpath store ppath
|
|
2772
|
+
let pass ←
|
|
2773
|
+
if refstr == "$GT" || refstr == "$LT" || refstr == "$GTE" || refstr == "$LTE" then
|
|
2774
|
+
pure (numCmp point term refstr)
|
|
2775
|
+
else if refstr == "$LIKE" then
|
|
2776
|
+
match term with
|
|
2777
|
+
| .str t => pure (Vregex.testStr t (← stringify point))
|
|
2778
|
+
| _ => pure false
|
|
2779
|
+
else pure false
|
|
2780
|
+
if pass then do
|
|
2781
|
+
let gp ← getelem d.nodes (.num (-2.0))
|
|
2782
|
+
let _ ← setprop gp gkey point
|
|
2783
|
+
else
|
|
2784
|
+
pushErr inj ("CMP: " ++ (← pathify ppath) ++ "⨯" ++ (← stringify point)
|
|
2785
|
+
++ " fail:" ++ refstr ++ " " ++ (← stringify term))
|
|
2786
|
+
pure .noval
|
|
2787
|
+
else pure .noval
|
|
2788
|
+
|
|
2789
|
+
partial def select (children query : Value) : SIO Value := do
|
|
2790
|
+
if !(isnode children) then emptyList
|
|
2791
|
+
else do
|
|
2792
|
+
let children ←
|
|
2793
|
+
if ismap children then do
|
|
2794
|
+
let mut arr : Array Value := #[]
|
|
2795
|
+
for (k, n) in (← itemsPairs children) do
|
|
2796
|
+
let _ ← setprop n (.str S_DKEY) (.str k)
|
|
2797
|
+
arr := arr.push n
|
|
2798
|
+
newList arr
|
|
2799
|
+
else do
|
|
2800
|
+
let xs ← listItemsOf children
|
|
2801
|
+
let mut arr : Array Value := #[]
|
|
2802
|
+
for i in [0:xs.size] do
|
|
2803
|
+
let n := xs[i]!
|
|
2804
|
+
if ismap n then
|
|
2805
|
+
let _ ← setprop n (.str S_DKEY) (vInt i)
|
|
2806
|
+
arr := arr.push n
|
|
2807
|
+
newList arr
|
|
2808
|
+
let results ← emptyList
|
|
2809
|
+
let extra ← emptyMap
|
|
2810
|
+
let _ ← setprop extra (.str "$AND") (← vFunc selectAnd)
|
|
2811
|
+
let _ ← setprop extra (.str "$OR") (← vFunc selectOr)
|
|
2812
|
+
let _ ← setprop extra (.str "$NOT") (← vFunc selectNot)
|
|
2813
|
+
for k in ["$GT", "$LT", "$GTE", "$LTE", "$LIKE"] do
|
|
2814
|
+
let _ ← setprop extra (.str k) (← vFunc selectCmp)
|
|
2815
|
+
let q ← clone query
|
|
2816
|
+
let _ ← walk q (before := some (fun _k v _p _path => do
|
|
2817
|
+
if ismap v then do
|
|
2818
|
+
let cur ← getprop v (.str S_BOPEN) (.bool true)
|
|
2819
|
+
let _ ← setprop v (.str S_BOPEN) cur
|
|
2820
|
+
pure v
|
|
2821
|
+
else pure v))
|
|
2822
|
+
for child in (← listItemsOf children) do
|
|
2823
|
+
let errs ← emptyList
|
|
2824
|
+
let m ← emptyMap
|
|
2825
|
+
let _ ← setprop m (.str S_BEXACT) (.bool true)
|
|
2826
|
+
let idef : InjDef := { dErrs := errs, dMeta := m, dExtra := extra }
|
|
2827
|
+
let _ ← validate child (← clone q) (.idef idef)
|
|
2828
|
+
if (← size errs) == 0 then do
|
|
2829
|
+
let n ← size results
|
|
2830
|
+
let _ ← setprop results (vInt n) child
|
|
2831
|
+
pure results
|
|
2832
|
+
|
|
2833
|
+
-- ---------------------------------------------------------------------------
|
|
2834
|
+
-- builders
|
|
2835
|
+
-- ---------------------------------------------------------------------------
|
|
2836
|
+
|
|
2837
|
+
partial def jm (kv : List Value) : SIO Value := do
|
|
2838
|
+
let m ← emptyMap
|
|
2839
|
+
let arr := kv.toArray
|
|
2840
|
+
let n := arr.size
|
|
2841
|
+
let mut i := 0
|
|
2842
|
+
while i < n do
|
|
2843
|
+
let k0 := arr[i]!
|
|
2844
|
+
let k ← match k0 with
|
|
2845
|
+
| .null => pure "null"
|
|
2846
|
+
| .str s => pure s
|
|
2847
|
+
| _ => stringify k0
|
|
2848
|
+
let v := if i + 1 < n then arr[i + 1]! else Value.null
|
|
2849
|
+
(match m with
|
|
2850
|
+
| .map mid => mapSetKV mid k v
|
|
2851
|
+
| _ => pure ())
|
|
2852
|
+
i := i + 2
|
|
2853
|
+
pure m
|
|
2854
|
+
|
|
2855
|
+
def jt (v : List Value) : SIO Value := newList v.toArray
|
|
2856
|
+
|
|
2857
|
+
def tn (t : Int) : String := typename t
|
|
2858
|
+
|
|
2859
|
+
-- ---------------------------------------------------------------------------
|
|
2860
|
+
-- Finish: register the default handler and the dummy inj used by the
|
|
2861
|
+
-- (corpus-unreached) getelem function-alt path.
|
|
2862
|
+
-- ---------------------------------------------------------------------------
|
|
2863
|
+
|
|
2864
|
+
/-- Create a fresh library context: empty heaps plus the default handler and
|
|
2865
|
+
the dummy inj used by the (corpus-unreached) getelem function-alt path. -/
|
|
2866
|
+
def mkCtx : IO Ctx := do
|
|
2867
|
+
let ctx : Ctx := {
|
|
2868
|
+
valueHeap := ← IO.mkRef #[], funcReg := ← IO.mkRef #[],
|
|
2869
|
+
modifyReg := ← IO.mkRef #[], injHeap := ← IO.mkRef #[],
|
|
2870
|
+
dummyInj := ← IO.mkRef none, injectHandlerFid := ← IO.mkRef 0 }
|
|
2871
|
+
let setup : SIO Unit := do
|
|
2872
|
+
let fid ← registerFunc injectHandler
|
|
2873
|
+
(← read).injectHandlerFid.set fid
|
|
2874
|
+
let parent ← newMap #[(S_DTOP, Value.noval)]
|
|
2875
|
+
let di ← newInj .noval parent
|
|
2876
|
+
(← read).dummyInj.set (some di)
|
|
2877
|
+
setup.run ctx
|
|
2878
|
+
pure ctx
|
|
2879
|
+
|
|
2880
|
+
end VoxgigStruct
|