@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.
Files changed (300) hide show
  1. package/.sdk/model/target/dart.aon +63 -0
  2. package/.sdk/model/target/haskell.aon +72 -0
  3. package/.sdk/model/target/lean.aon +91 -0
  4. package/.sdk/src/cmp/dart/Config_dart.ts +248 -0
  5. package/.sdk/src/cmp/dart/EntityBase_dart.ts +34 -0
  6. package/.sdk/src/cmp/dart/EntityOperation_dart.ts +43 -0
  7. package/.sdk/src/cmp/dart/EntityTypes_dart.ts +207 -0
  8. package/.sdk/src/cmp/dart/Entity_dart.ts +87 -0
  9. package/.sdk/src/cmp/dart/Gitignore_dart.ts +34 -0
  10. package/.sdk/src/cmp/dart/MainEntity_dart.ts +33 -0
  11. package/.sdk/src/cmp/dart/Main_dart.ts +178 -0
  12. package/.sdk/src/cmp/dart/Package_dart.ts +82 -0
  13. package/.sdk/src/cmp/dart/ReadmeEntity_dart.ts +177 -0
  14. package/.sdk/src/cmp/dart/ReadmeExamplesTest_dart.ts +238 -0
  15. package/.sdk/src/cmp/dart/ReadmeExplanation_dart.ts +53 -0
  16. package/.sdk/src/cmp/dart/ReadmeHowto_dart.ts +152 -0
  17. package/.sdk/src/cmp/dart/ReadmeInstall_dart.ts +59 -0
  18. package/.sdk/src/cmp/dart/ReadmeIntro_dart.ts +65 -0
  19. package/.sdk/src/cmp/dart/ReadmeModel_dart.ts +151 -0
  20. package/.sdk/src/cmp/dart/ReadmeOptions_dart.ts +63 -0
  21. package/.sdk/src/cmp/dart/ReadmeQuick_dart.ts +225 -0
  22. package/.sdk/src/cmp/dart/ReadmeRef_dart.ts +397 -0
  23. package/.sdk/src/cmp/dart/ReadmeTopHowto_dart.ts +24 -0
  24. package/.sdk/src/cmp/dart/ReadmeTopQuick_dart.ts +106 -0
  25. package/.sdk/src/cmp/dart/ReadmeTopTest_dart.ts +78 -0
  26. package/.sdk/src/cmp/dart/SdkError_dart.ts +42 -0
  27. package/.sdk/src/cmp/dart/TestDirect_dart.ts +516 -0
  28. package/.sdk/src/cmp/dart/TestEntity_dart.ts +628 -0
  29. package/.sdk/src/cmp/dart/Test_dart.ts +138 -0
  30. package/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +98 -0
  31. package/.sdk/src/cmp/dart/fragment/Config.fragment.dart +70 -0
  32. package/.sdk/src/cmp/dart/fragment/Direct.test.fragment.dart +25 -0
  33. package/.sdk/src/cmp/dart/fragment/Entity.fragment.dart +29 -0
  34. package/.sdk/src/cmp/dart/fragment/Entity.test.fragment.dart +28 -0
  35. package/.sdk/src/cmp/dart/fragment/EntityBase.fragment.dart +302 -0
  36. package/.sdk/src/cmp/dart/fragment/EntityCreateOp.fragment.dart +98 -0
  37. package/.sdk/src/cmp/dart/fragment/EntityListOp.fragment.dart +90 -0
  38. package/.sdk/src/cmp/dart/fragment/EntityLoadOp.fragment.dart +102 -0
  39. package/.sdk/src/cmp/dart/fragment/EntityRemoveOp.fragment.dart +108 -0
  40. package/.sdk/src/cmp/dart/fragment/EntityUpdateOp.fragment.dart +102 -0
  41. package/.sdk/src/cmp/dart/fragment/Main.fragment.dart +339 -0
  42. package/.sdk/src/cmp/dart/fragment/SdkError.fragment.dart +26 -0
  43. package/.sdk/src/cmp/dart/tsconfig.json +15 -0
  44. package/.sdk/src/cmp/dart/utility_dart.ts +155 -0
  45. package/.sdk/src/cmp/haskell/Config_haskell.ts +98 -0
  46. package/.sdk/src/cmp/haskell/Entity_haskell.ts +13 -0
  47. package/.sdk/src/cmp/haskell/Gitignore_haskell.ts +27 -0
  48. package/.sdk/src/cmp/haskell/Main_haskell.ts +123 -0
  49. package/.sdk/src/cmp/haskell/Package_haskell.ts +60 -0
  50. package/.sdk/src/cmp/haskell/ReadmeEntity_haskell.ts +186 -0
  51. package/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +135 -0
  52. package/.sdk/src/cmp/haskell/ReadmeExplanation_haskell.ts +48 -0
  53. package/.sdk/src/cmp/haskell/ReadmeHowto_haskell.ts +168 -0
  54. package/.sdk/src/cmp/haskell/ReadmeInstall_haskell.ts +51 -0
  55. package/.sdk/src/cmp/haskell/ReadmeIntro_haskell.ts +67 -0
  56. package/.sdk/src/cmp/haskell/ReadmeModel_haskell.ts +162 -0
  57. package/.sdk/src/cmp/haskell/ReadmeOptions_haskell.ts +74 -0
  58. package/.sdk/src/cmp/haskell/ReadmeQuick_haskell.ts +241 -0
  59. package/.sdk/src/cmp/haskell/ReadmeRef_haskell.ts +408 -0
  60. package/.sdk/src/cmp/haskell/ReadmeTopHowto_haskell.ts +31 -0
  61. package/.sdk/src/cmp/haskell/ReadmeTopQuick_haskell.ts +104 -0
  62. package/.sdk/src/cmp/haskell/ReadmeTopTest_haskell.ts +80 -0
  63. package/.sdk/src/cmp/haskell/Test_haskell.ts +297 -0
  64. package/.sdk/src/cmp/haskell/tsconfig.json +15 -0
  65. package/.sdk/src/cmp/haskell/utility_haskell.ts +166 -0
  66. package/.sdk/src/cmp/lean/Config_lean.ts +93 -0
  67. package/.sdk/src/cmp/lean/Entity_lean.ts +9 -0
  68. package/.sdk/src/cmp/lean/Gitignore_lean.ts +18 -0
  69. package/.sdk/src/cmp/lean/Main_lean.ts +120 -0
  70. package/.sdk/src/cmp/lean/Package_lean.ts +93 -0
  71. package/.sdk/src/cmp/lean/ReadmeEntity_lean.ts +29 -0
  72. package/.sdk/src/cmp/lean/ReadmeExplanation_lean.ts +19 -0
  73. package/.sdk/src/cmp/lean/ReadmeHowto_lean.ts +25 -0
  74. package/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +39 -0
  75. package/.sdk/src/cmp/lean/ReadmeIntro_lean.ts +25 -0
  76. package/.sdk/src/cmp/lean/ReadmeModel_lean.ts +24 -0
  77. package/.sdk/src/cmp/lean/ReadmeOptions_lean.ts +22 -0
  78. package/.sdk/src/cmp/lean/ReadmeQuick_lean.ts +50 -0
  79. package/.sdk/src/cmp/lean/ReadmeRef_lean.ts +40 -0
  80. package/.sdk/src/cmp/lean/ReadmeTopHowto_lean.ts +16 -0
  81. package/.sdk/src/cmp/lean/ReadmeTopQuick_lean.ts +23 -0
  82. package/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +19 -0
  83. package/.sdk/src/cmp/lean/Test_lean.ts +261 -0
  84. package/.sdk/src/cmp/lean/utility_lean.ts +129 -0
  85. package/.sdk/tm/dart/LICENSE +22 -0
  86. package/.sdk/tm/dart/Makefile +50 -0
  87. package/.sdk/tm/dart/lib/Context.dart +150 -0
  88. package/.sdk/tm/dart/lib/Control.dart +15 -0
  89. package/.sdk/tm/dart/lib/Operation.dart +31 -0
  90. package/.sdk/tm/dart/lib/Point.dart +51 -0
  91. package/.sdk/tm/dart/lib/Response.dart +32 -0
  92. package/.sdk/tm/dart/lib/Result.dart +39 -0
  93. package/.sdk/tm/dart/lib/Spec.dart +49 -0
  94. package/.sdk/tm/dart/lib/feature/audit/AuditFeature.dart +103 -0
  95. package/.sdk/tm/dart/lib/feature/base/BaseFeature.dart +76 -0
  96. package/.sdk/tm/dart/lib/feature/cache/CacheFeature.dart +152 -0
  97. package/.sdk/tm/dart/lib/feature/clienttrack/ClienttrackFeature.dart +104 -0
  98. package/.sdk/tm/dart/lib/feature/cost/CostFeature.dart +446 -0
  99. package/.sdk/tm/dart/lib/feature/debug/DebugFeature.dart +153 -0
  100. package/.sdk/tm/dart/lib/feature/idempotency/IdempotencyFeature.dart +92 -0
  101. package/.sdk/tm/dart/lib/feature/log/LogFeature.dart +85 -0
  102. package/.sdk/tm/dart/lib/feature/metrics/MetricsFeature.dart +103 -0
  103. package/.sdk/tm/dart/lib/feature/netsim/NetsimFeature.dart +186 -0
  104. package/.sdk/tm/dart/lib/feature/paging/PagingFeature.dart +240 -0
  105. package/.sdk/tm/dart/lib/feature/proxy/ProxyFeature.dart +118 -0
  106. package/.sdk/tm/dart/lib/feature/ratelimit/RatelimitFeature.dart +103 -0
  107. package/.sdk/tm/dart/lib/feature/rbac/RbacFeature.dart +121 -0
  108. package/.sdk/tm/dart/lib/feature/retry/RetryFeature.dart +155 -0
  109. package/.sdk/tm/dart/lib/feature/secrets/SecretsFeature.dart +649 -0
  110. package/.sdk/tm/dart/lib/feature/secrets/plugin/capability.dart +105 -0
  111. package/.sdk/tm/dart/lib/feature/secrets/plugin/catalog.dart +46 -0
  112. package/.sdk/tm/dart/lib/feature/secrets/plugin/config.dart +297 -0
  113. package/.sdk/tm/dart/lib/feature/secrets/plugin/depend.dart +198 -0
  114. package/.sdk/tm/dart/lib/feature/secrets/plugin/env.dart +144 -0
  115. package/.sdk/tm/dart/lib/feature/secrets/plugin/export.dart +58 -0
  116. package/.sdk/tm/dart/lib/feature/secrets/plugin/graph.dart +160 -0
  117. package/.sdk/tm/dart/lib/feature/secrets/plugin/host.dart +1067 -0
  118. package/.sdk/tm/dart/lib/feature/secrets/plugin/order.dart +172 -0
  119. package/.sdk/tm/dart/lib/feature/secrets/plugin/plugin.dart +23 -0
  120. package/.sdk/tm/dart/lib/feature/secrets/plugin/point.dart +117 -0
  121. package/.sdk/tm/dart/lib/feature/secrets/plugin/ref.dart +105 -0
  122. package/.sdk/tm/dart/lib/feature/secrets/plugin/resolve.dart +58 -0
  123. package/.sdk/tm/dart/lib/feature/secrets/plugin/types.dart +201 -0
  124. package/.sdk/tm/dart/lib/feature/secrets/plugin/version.dart +116 -0
  125. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/aws.dart +285 -0
  126. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/azuresecrets.dart +178 -0
  127. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/boru.dart +145 -0
  128. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/crypto.dart +194 -0
  129. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/doppler.dart +101 -0
  130. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/gcpsecrets.dart +143 -0
  131. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/hashicorp.dart +190 -0
  132. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/httpjson.dart +363 -0
  133. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/infisical.dart +149 -0
  134. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/onepassword.dart +146 -0
  135. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/secretspec.dart +116 -0
  136. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/sigv4.dart +237 -0
  137. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/addr.dart +138 -0
  138. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/json.dart +276 -0
  139. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/provider.dart +31 -0
  140. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/providers.dart +243 -0
  141. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/sekreto.dart +730 -0
  142. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/spec.dart +200 -0
  143. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/support.dart +217 -0
  144. package/.sdk/tm/dart/lib/feature/streaming/StreamingFeature.dart +97 -0
  145. package/.sdk/tm/dart/lib/feature/telemetry/TelemetryFeature.dart +136 -0
  146. package/.sdk/tm/dart/lib/feature/test/TestFeature.dart +328 -0
  147. package/.sdk/tm/dart/lib/feature/timeout/TimeoutFeature.dart +104 -0
  148. package/.sdk/tm/dart/lib/utility/CleanUtility.dart +12 -0
  149. package/.sdk/tm/dart/lib/utility/DoneUtility.dart +18 -0
  150. package/.sdk/tm/dart/lib/utility/ErrUtility.dart +46 -0
  151. package/.sdk/tm/dart/lib/utility/FeatureAddUtility.dart +35 -0
  152. package/.sdk/tm/dart/lib/utility/FeatureHookUtility.dart +20 -0
  153. package/.sdk/tm/dart/lib/utility/FeatureInitUtility.dart +9 -0
  154. package/.sdk/tm/dart/lib/utility/FetcherUtility.dart +104 -0
  155. package/.sdk/tm/dart/lib/utility/GraphqlUtility.dart +162 -0
  156. package/.sdk/tm/dart/lib/utility/MakeContextUtility.dart +6 -0
  157. package/.sdk/tm/dart/lib/utility/MakeErrorUtility.dart +71 -0
  158. package/.sdk/tm/dart/lib/utility/MakeFetchDefUtility.dart +42 -0
  159. package/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +233 -0
  160. package/.sdk/tm/dart/lib/utility/MakePointUtility.dart +130 -0
  161. package/.sdk/tm/dart/lib/utility/MakeRequestUtility.dart +66 -0
  162. package/.sdk/tm/dart/lib/utility/MakeResponseUtility.dart +59 -0
  163. package/.sdk/tm/dart/lib/utility/MakeResultUtility.dart +49 -0
  164. package/.sdk/tm/dart/lib/utility/MakeSpecUtility.dart +77 -0
  165. package/.sdk/tm/dart/lib/utility/MakeUrlUtility.dart +51 -0
  166. package/.sdk/tm/dart/lib/utility/ParamUtility.dart +56 -0
  167. package/.sdk/tm/dart/lib/utility/PrepareAuthUtility.dart +46 -0
  168. package/.sdk/tm/dart/lib/utility/PrepareBodyUtility.dart +19 -0
  169. package/.sdk/tm/dart/lib/utility/PrepareHeadersUtility.dart +11 -0
  170. package/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +30 -0
  171. package/.sdk/tm/dart/lib/utility/PrepareParamsUtility.dart +23 -0
  172. package/.sdk/tm/dart/lib/utility/PreparePathUtility.dart +9 -0
  173. package/.sdk/tm/dart/lib/utility/PrepareQueryUtility.dart +18 -0
  174. package/.sdk/tm/dart/lib/utility/ResultBasicUtility.dart +29 -0
  175. package/.sdk/tm/dart/lib/utility/ResultBodyUtility.dart +15 -0
  176. package/.sdk/tm/dart/lib/utility/ResultHeadersUtility.dart +18 -0
  177. package/.sdk/tm/dart/lib/utility/StructUtility.dart +127 -0
  178. package/.sdk/tm/dart/lib/utility/TransformRequestUtility.dart +28 -0
  179. package/.sdk/tm/dart/lib/utility/TransformResponseUtility.dart +44 -0
  180. package/.sdk/tm/dart/lib/utility/Utility.dart +239 -0
  181. package/.sdk/tm/dart/lib/utility/voxgig_struct.dart +2462 -0
  182. package/.sdk/tm/dart/src/feature/README.md +3 -0
  183. package/.sdk/tm/dart/src/feature/audit/.gitkeep +0 -0
  184. package/.sdk/tm/dart/src/feature/base/.gitkeep +0 -0
  185. package/.sdk/tm/dart/src/feature/cache/.gitkeep +0 -0
  186. package/.sdk/tm/dart/src/feature/clienttrack/.gitkeep +0 -0
  187. package/.sdk/tm/dart/src/feature/cost/.gitkeep +0 -0
  188. package/.sdk/tm/dart/src/feature/debug/.gitkeep +0 -0
  189. package/.sdk/tm/dart/src/feature/idempotency/.gitkeep +0 -0
  190. package/.sdk/tm/dart/src/feature/log/.gitkeep +0 -0
  191. package/.sdk/tm/dart/src/feature/metrics/.gitkeep +0 -0
  192. package/.sdk/tm/dart/src/feature/netsim/.gitkeep +0 -0
  193. package/.sdk/tm/dart/src/feature/paging/.gitkeep +0 -0
  194. package/.sdk/tm/dart/src/feature/proxy/.gitkeep +0 -0
  195. package/.sdk/tm/dart/src/feature/ratelimit/.gitkeep +0 -0
  196. package/.sdk/tm/dart/src/feature/rbac/.gitkeep +0 -0
  197. package/.sdk/tm/dart/src/feature/retry/.gitkeep +0 -0
  198. package/.sdk/tm/dart/src/feature/secrets/.gitkeep +0 -0
  199. package/.sdk/tm/dart/src/feature/streaming/.gitkeep +0 -0
  200. package/.sdk/tm/dart/src/feature/telemetry/.gitkeep +0 -0
  201. package/.sdk/tm/dart/src/feature/test/.gitkeep +0 -0
  202. package/.sdk/tm/dart/src/feature/timeout/.gitkeep +0 -0
  203. package/.sdk/tm/dart/test/custom_test.dart +65 -0
  204. package/.sdk/tm/dart/test/exists_test.dart +12 -0
  205. package/.sdk/tm/dart/test/feature/harness.dart +347 -0
  206. package/.sdk/tm/dart/test/feature/secrets/secrets_test.dart +1107 -0
  207. package/.sdk/tm/dart/test/feature_test.dart +1193 -0
  208. package/.sdk/tm/dart/test/harness.dart +153 -0
  209. package/.sdk/tm/dart/test/netsim_test.dart +48 -0
  210. package/.sdk/tm/dart/test/omni.dart +520 -0
  211. package/.sdk/tm/dart/test/omni_smoke_test.dart +143 -0
  212. package/.sdk/tm/dart/test/pipeline_test.dart +608 -0
  213. package/.sdk/tm/dart/test/primary_test.dart +476 -0
  214. package/.sdk/tm/dart/test/sdk-test-control.json +19 -0
  215. package/.sdk/tm/dart/test/struct_test.dart +468 -0
  216. package/.sdk/tm/dart/test/utility.dart +198 -0
  217. package/.sdk/tm/dart/test/vendor/omni/omni.dart +7 -0
  218. package/.sdk/tm/dart/test/vendor/omni/runner.dart +653 -0
  219. package/.sdk/tm/dart/test/vendor/omni/util.dart +232 -0
  220. package/.sdk/tm/haskell/LICENSE +21 -0
  221. package/.sdk/tm/haskell/Makefile +22 -0
  222. package/.sdk/tm/haskell/VERSION +1 -0
  223. package/.sdk/tm/haskell/src/SdkFeatures.hs +1440 -0
  224. package/.sdk/tm/haskell/src/SdkHelpers.hs +337 -0
  225. package/.sdk/tm/haskell/src/SdkJson.hs +111 -0
  226. package/.sdk/tm/haskell/src/SdkRuntime.hs +1279 -0
  227. package/.sdk/tm/haskell/src/SdkTypes.hs +195 -0
  228. package/.sdk/tm/haskell/src/VoxgigStruct.hs +2299 -0
  229. package/.sdk/tm/haskell/src/Vregex.hs +237 -0
  230. package/.sdk/tm/haskell/src/feature/README.md +5 -0
  231. package/.sdk/tm/haskell/src/feature/audit/.gitkeep +0 -0
  232. package/.sdk/tm/haskell/src/feature/base/.gitkeep +0 -0
  233. package/.sdk/tm/haskell/src/feature/cache/.gitkeep +0 -0
  234. package/.sdk/tm/haskell/src/feature/clienttrack/.gitkeep +0 -0
  235. package/.sdk/tm/haskell/src/feature/debug/.gitkeep +0 -0
  236. package/.sdk/tm/haskell/src/feature/idempotency/.gitkeep +0 -0
  237. package/.sdk/tm/haskell/src/feature/log/.gitkeep +0 -0
  238. package/.sdk/tm/haskell/src/feature/metrics/.gitkeep +0 -0
  239. package/.sdk/tm/haskell/src/feature/netsim/.gitkeep +0 -0
  240. package/.sdk/tm/haskell/src/feature/paging/.gitkeep +0 -0
  241. package/.sdk/tm/haskell/src/feature/proxy/.gitkeep +0 -0
  242. package/.sdk/tm/haskell/src/feature/ratelimit/.gitkeep +0 -0
  243. package/.sdk/tm/haskell/src/feature/rbac/.gitkeep +0 -0
  244. package/.sdk/tm/haskell/src/feature/retry/.gitkeep +0 -0
  245. package/.sdk/tm/haskell/src/feature/streaming/.gitkeep +0 -0
  246. package/.sdk/tm/haskell/src/feature/telemetry/.gitkeep +0 -0
  247. package/.sdk/tm/haskell/src/feature/test/.gitkeep +0 -0
  248. package/.sdk/tm/haskell/src/feature/timeout/.gitkeep +0 -0
  249. package/.sdk/tm/haskell/test/Harness.hs +281 -0
  250. package/.sdk/tm/haskell/test/OmniResolver.hs +252 -0
  251. package/.sdk/tm/haskell/test/Runner.hs +49 -0
  252. package/.sdk/tm/haskell/test/StructCorpus.hs +449 -0
  253. package/.sdk/tm/haskell/test/TCustomUtility.hs +38 -0
  254. package/.sdk/tm/haskell/test/TFeature.hs +794 -0
  255. package/.sdk/tm/haskell/test/TNetsim.hs +50 -0
  256. package/.sdk/tm/haskell/test/TPipeline.hs +334 -0
  257. package/.sdk/tm/haskell/test/TPrimaryCorpus.hs +263 -0
  258. package/.sdk/tm/haskell/test/TPrimaryUtility.hs +386 -0
  259. package/.sdk/tm/haskell/test/Testutil.hs +59 -0
  260. package/.sdk/tm/haskell/test/vendor/omni/Omni.hs +1147 -0
  261. package/.sdk/tm/lean/LICENSE +21 -0
  262. package/.sdk/tm/lean/Makefile +32 -0
  263. package/.sdk/tm/lean/VERSION +1 -0
  264. package/.sdk/tm/lean/src/SdkFeature.lean +171 -0
  265. package/.sdk/tm/lean/src/SdkFeatures.lean +787 -0
  266. package/.sdk/tm/lean/src/SdkJson.lean +134 -0
  267. package/.sdk/tm/lean/src/SdkRuntime.lean +374 -0
  268. package/.sdk/tm/lean/src/SdkUtility.lean +809 -0
  269. package/.sdk/tm/lean/src/VoxgigStruct.lean +2880 -0
  270. package/.sdk/tm/lean/src/Vregex.lean +412 -0
  271. package/.sdk/tm/lean/src/feature/README.md +5 -0
  272. package/.sdk/tm/lean/src/feature/audit/.gitkeep +0 -0
  273. package/.sdk/tm/lean/src/feature/base/.gitkeep +0 -0
  274. package/.sdk/tm/lean/src/feature/cache/.gitkeep +0 -0
  275. package/.sdk/tm/lean/src/feature/clienttrack/.gitkeep +0 -0
  276. package/.sdk/tm/lean/src/feature/cost/.gitkeep +0 -0
  277. package/.sdk/tm/lean/src/feature/debug/.gitkeep +0 -0
  278. package/.sdk/tm/lean/src/feature/idempotency/.gitkeep +0 -0
  279. package/.sdk/tm/lean/src/feature/log/.gitkeep +0 -0
  280. package/.sdk/tm/lean/src/feature/metrics/.gitkeep +0 -0
  281. package/.sdk/tm/lean/src/feature/netsim/.gitkeep +0 -0
  282. package/.sdk/tm/lean/src/feature/paging/.gitkeep +0 -0
  283. package/.sdk/tm/lean/src/feature/proxy/.gitkeep +0 -0
  284. package/.sdk/tm/lean/src/feature/ratelimit/.gitkeep +0 -0
  285. package/.sdk/tm/lean/src/feature/rbac/.gitkeep +0 -0
  286. package/.sdk/tm/lean/src/feature/retry/.gitkeep +0 -0
  287. package/.sdk/tm/lean/src/feature/streaming/.gitkeep +0 -0
  288. package/.sdk/tm/lean/src/feature/telemetry/.gitkeep +0 -0
  289. package/.sdk/tm/lean/src/feature/test/.gitkeep +0 -0
  290. package/.sdk/tm/lean/src/feature/timeout/.gitkeep +0 -0
  291. package/.sdk/tm/lean/test/OmniResolver.lean +358 -0
  292. package/.sdk/tm/lean/test/OmniSmoke.lean +174 -0
  293. package/.sdk/tm/lean/test/StructCorpus.lean +368 -0
  294. package/.sdk/tm/lean/test/TFeature.lean +281 -0
  295. package/.sdk/tm/lean/test/TPrimaryUtility.lean +227 -0
  296. package/.sdk/tm/lean/test/vendor/omni/Omni.lean +997 -0
  297. package/LICENSE +21 -0
  298. package/README.md +104 -0
  299. package/package.json +78 -0
  300. 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