ranger-compiler 3.5.0 → 3.5.2

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 (338) hide show
  1. package/CHANGELOG.md +1160 -19
  2. package/LICENSE +3 -1
  3. package/README.md +124 -29
  4. package/dist/Lang.rgr +1188 -290
  5. package/dist/api.d.ts +2343 -879
  6. package/dist/api.js +79924 -56922
  7. package/dist/lib/JSON.rgr +102 -91
  8. package/dist/lib/Shell.rgr +4 -4
  9. package/dist/lib/apple/AppleToolchain.rgr +4 -4
  10. package/dist/lib/apple/README.md +5 -4
  11. package/dist/lib/apple/apple_test.rgr +1 -1
  12. package/dist/lib/core/README.md +1 -1
  13. package/dist/lib/evg/EVG.rgr +12 -0
  14. package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
  15. package/dist/lib/evg/EVGA11yTree.rgr +894 -0
  16. package/dist/lib/evg/EVGBox.rgr +267 -0
  17. package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
  18. package/dist/lib/evg/EVGCodepoint.rgr +316 -0
  19. package/dist/lib/evg/EVGColor.rgr +700 -0
  20. package/dist/lib/evg/EVGCommands.rgr +177 -0
  21. package/dist/lib/evg/EVGComponent.rgr +331 -0
  22. package/dist/lib/evg/EVGComponentTest.rgr +387 -0
  23. package/dist/lib/evg/EVGConnector.rgr +541 -0
  24. package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
  25. package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
  26. package/dist/lib/evg/EVGEasing.rgr +370 -0
  27. package/dist/lib/evg/EVGEffectTest.rgr +253 -0
  28. package/dist/lib/evg/EVGElement.rgr +4914 -0
  29. package/dist/lib/evg/EVGFixedTest.rgr +289 -0
  30. package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
  31. package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
  32. package/dist/lib/evg/EVGFling.rgr +244 -0
  33. package/dist/lib/evg/EVGFocus.rgr +400 -0
  34. package/dist/lib/evg/EVGFocusTest.rgr +399 -0
  35. package/dist/lib/evg/EVGGradient.rgr +322 -0
  36. package/dist/lib/evg/EVGGrapheme.rgr +190 -0
  37. package/dist/lib/evg/EVGGrid.rgr +975 -0
  38. package/dist/lib/evg/EVGHitTest.rgr +208 -0
  39. package/dist/lib/evg/EVGHoles.rgr +294 -0
  40. package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
  41. package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
  42. package/dist/lib/evg/EVGHostTree.rgr +834 -0
  43. package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
  44. package/dist/lib/evg/EVGImageDecode.rgr +117 -0
  45. package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
  46. package/dist/lib/evg/EVGInspect.rgr +883 -0
  47. package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
  48. package/dist/lib/evg/EVGJsonTest.rgr +535 -0
  49. package/dist/lib/evg/EVGLayout.rgr +4220 -0
  50. package/dist/lib/evg/EVGMeasure.rgr +1267 -0
  51. package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
  52. package/dist/lib/evg/EVGPatch.rgr +1721 -0
  53. package/dist/lib/evg/EVGPatchTest.rgr +692 -0
  54. package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
  55. package/dist/lib/evg/EVGReconcile.rgr +226 -0
  56. package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
  57. package/dist/lib/evg/EVGReject.rgr +126 -0
  58. package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
  59. package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
  60. package/dist/lib/evg/EVGRuler.rgr +232 -0
  61. package/dist/lib/evg/EVGRulerTest.rgr +172 -0
  62. package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
  63. package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
  64. package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
  65. package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
  66. package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
  67. package/dist/lib/evg/EVGText.rgr +105 -0
  68. package/dist/lib/evg/EVGTextEngine.rgr +536 -0
  69. package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
  70. package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
  71. package/dist/lib/evg/EVGToolbar.rgr +1369 -0
  72. package/dist/lib/evg/EVGTransition.rgr +728 -0
  73. package/dist/lib/evg/EVGTreeJson.rgr +721 -0
  74. package/dist/lib/evg/EVGUnit.rgr +554 -0
  75. package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
  76. package/dist/lib/evg/EvgApp.rgr +189 -0
  77. package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
  78. package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
  79. package/dist/lib/evg/EvgHost.rgr +451 -0
  80. package/dist/lib/evg/EvgTest.rgr +91 -0
  81. package/dist/lib/evg/EvgTraceColor.rgr +236 -0
  82. package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
  83. package/dist/lib/evg/EvgTraceFit.rgr +767 -0
  84. package/dist/lib/evg/EvgTracePath.rgr +435 -0
  85. package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
  86. package/dist/lib/evg/EvgViewport.rgr +313 -0
  87. package/dist/lib/evg/FxDemoDoc.rgr +185 -0
  88. package/dist/lib/evg/HOSTS.md +227 -0
  89. package/dist/lib/evg/ISSUES.md +835 -0
  90. package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
  91. package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
  92. package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
  93. package/dist/lib/evg/PLAN_EVG.md +518 -0
  94. package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
  95. package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
  96. package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
  97. package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
  98. package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
  99. package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
  100. package/dist/lib/evg/PathBuilder.rgr +333 -0
  101. package/dist/lib/evg/README.md +1236 -0
  102. package/dist/lib/evg/SPEC.md +1252 -0
  103. package/dist/lib/evg/SVGPathParser.rgr +1199 -0
  104. package/dist/lib/evg/SvgParser.rgr +1736 -0
  105. package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
  106. package/dist/lib/evg/VectorShapes.rgr +331 -0
  107. package/dist/lib/evg/VectorStroke.rgr +107 -0
  108. package/dist/lib/evg/VectorViewBox.rgr +380 -0
  109. package/dist/lib/evg/agent/README.md +290 -0
  110. package/dist/lib/evg/agent/evg_agent.rgr +899 -0
  111. package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
  112. package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
  113. package/dist/lib/evg/agent/fixtures/connector.css +63 -0
  114. package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
  115. package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
  116. package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
  117. package/dist/lib/evg/agent/fixtures/popover.css +99 -0
  118. package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
  119. package/dist/lib/evg/agent/roundtrip.sh +74 -0
  120. package/dist/lib/evg/agent/smoke.sh +230 -0
  121. package/dist/lib/evg/android/README.md +97 -0
  122. package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
  123. package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
  124. package/dist/lib/evg/android/androidstubs/App.kt +30 -0
  125. package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
  126. package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
  127. package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
  128. package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
  129. package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
  130. package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
  131. package/dist/lib/evg/android/androidstubs/View.kt +113 -0
  132. package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
  133. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
  134. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
  135. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
  136. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
  137. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
  138. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
  139. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
  140. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
  141. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
  142. package/dist/lib/evg/apple/README.md +128 -0
  143. package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
  144. package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
  145. package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
  146. package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
  147. package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
  148. package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
  149. package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
  150. package/dist/lib/evg/bench/README.md +64 -0
  151. package/dist/lib/evg/bench/layout-bench.mjs +316 -0
  152. package/dist/lib/evg/bench/layout-cases.mjs +510 -0
  153. package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
  154. package/dist/lib/evg/bin/.gitignore +17 -0
  155. package/dist/lib/evg/evg_test.rgr +313 -0
  156. package/dist/lib/evg/gl/README.md +198 -0
  157. package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
  158. package/dist/lib/evg/gl/blur-check.mjs +399 -0
  159. package/dist/lib/evg/gl/boxmodel.json +1 -0
  160. package/dist/lib/evg/gl/demo.html +46 -0
  161. package/dist/lib/evg/gl/effect-presets.css +285 -0
  162. package/dist/lib/evg/gl/effect-presets.js +63 -0
  163. package/dist/lib/evg/gl/effect-shots.mjs +135 -0
  164. package/dist/lib/evg/gl/evg-a11y.js +566 -0
  165. package/dist/lib/evg/gl/evg-binary.js +160 -0
  166. package/dist/lib/evg/gl/evg-engine.js +296 -0
  167. package/dist/lib/evg/gl/evg-fx.js +237 -0
  168. package/dist/lib/evg/gl/evg-gestures.js +209 -0
  169. package/dist/lib/evg/gl/evg-list.js +167 -0
  170. package/dist/lib/evg/gl/evg-measure.js +186 -0
  171. package/dist/lib/evg/gl/evg-textinput.js +303 -0
  172. package/dist/lib/evg/gl/evg-view.js +144 -0
  173. package/dist/lib/evg/gl/evg-webgl.js +3942 -0
  174. package/dist/lib/evg/gl/fx-check.mjs +998 -0
  175. package/dist/lib/evg/gl/fx-demo.html +166 -0
  176. package/dist/lib/evg/gl/fx-demo.js +2 -0
  177. package/dist/lib/evg/gl/fx-serve.mjs +47 -0
  178. package/dist/lib/evg/gl/gestures-check.mjs +189 -0
  179. package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
  180. package/dist/lib/evg/gl/measure-check.mjs +135 -0
  181. package/dist/lib/evg/gl/rotation-check.mjs +207 -0
  182. package/dist/lib/evg/gl/shift-check.mjs +113 -0
  183. package/dist/lib/evg/gl/stroke-check.mjs +168 -0
  184. package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
  185. package/dist/lib/evg/gl/view-check.mjs +343 -0
  186. package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
  187. package/dist/lib/evg/html/evg-dom.js +318 -0
  188. package/dist/lib/evg/html/evg-html.js +600 -0
  189. package/dist/lib/evg/inspect/README.md +415 -0
  190. package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
  191. package/dist/lib/evg/inspect/evg-inspect.js +947 -0
  192. package/dist/lib/evg/inspect/shots/css.png +0 -0
  193. package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
  194. package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
  195. package/dist/lib/evg/inspect/shots/state.png +0 -0
  196. package/dist/lib/evg/inspect/shots.mjs +226 -0
  197. package/dist/lib/evg/oracle/css-blur.json +576 -0
  198. package/dist/lib/evg/oracle/css-box.json +157 -0
  199. package/dist/lib/evg/oracle/css-timing.json +709 -0
  200. package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
  201. package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
  202. package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
  203. package/dist/lib/evg/original/EVGColor.clj +310 -0
  204. package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
  205. package/dist/lib/evg/original/SVGPath.rgr +627 -0
  206. package/dist/lib/evg/original/Vec2.crgr +103 -0
  207. package/dist/lib/evg/ranger.json +9 -0
  208. package/dist/lib/evg/showcase/README.md +246 -0
  209. package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
  210. package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
  211. package/dist/lib/evg/showcase/build.mjs +651 -0
  212. package/dist/lib/evg/showcase/pages/album.tsx +30 -0
  213. package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
  214. package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
  215. package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
  216. package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
  217. package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
  218. package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
  219. package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
  220. package/dist/lib/evg/showcase/pages/more.tsx +332 -0
  221. package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
  222. package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
  223. package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
  224. package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
  225. package/dist/lib/evg/showcase/pages/units.tsx +35 -0
  226. package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
  227. package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
  228. package/dist/lib/evg/showcase/pages/views.tsx +223 -0
  229. package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
  230. package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
  231. package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
  232. package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
  233. package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
  234. package/dist/lib/evg/showcase/themes/more-default.css +128 -0
  235. package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
  236. package/dist/lib/evg/showcase/themes/showcase.css +508 -0
  237. package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
  238. package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
  239. package/dist/lib/evg/showcase/themes/views-default.css +15 -0
  240. package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
  241. package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
  242. package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
  243. package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
  244. package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
  245. package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
  246. package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
  247. package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
  248. package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
  249. package/dist/lib/evg/web/responsive/README.md +97 -0
  250. package/dist/lib/evg/web/responsive/build.mjs +90 -0
  251. package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
  252. package/dist/lib/evg/web/responsive/index.html +177 -0
  253. package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
  254. package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
  255. package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
  256. package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
  257. package/dist/lib/evg/web/tools/minify.mjs +60 -0
  258. package/dist/lib/evg/web/tracer/build.mjs +87 -0
  259. package/dist/lib/evg/web/tracer/index.html +2805 -0
  260. package/dist/lib/evg/web/tracer/sample.png +0 -0
  261. package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
  262. package/dist/lib/evgr/Cargo.lock +21 -0
  263. package/dist/lib/evgr/Cargo.toml +19 -0
  264. package/dist/lib/evgr/README.md +140 -0
  265. package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
  266. package/dist/lib/evgr/bench/compare.mjs +252 -0
  267. package/dist/lib/evgr/bench/speed.mjs +277 -0
  268. package/dist/lib/evgr/bin/.gitignore +3 -0
  269. package/dist/lib/evgr/src/bin/bench.rs +106 -0
  270. package/dist/lib/evgr/src/bin/smoke.rs +13 -0
  271. package/dist/lib/evgr/src/grid.rs +730 -0
  272. package/dist/lib/evgr/src/lib.rs +1003 -0
  273. package/dist/lib/evgr/src/style.rs +468 -0
  274. package/dist/lib/evgr/src/text.rs +83 -0
  275. package/dist/lib/image/BitReader.rgr +171 -0
  276. package/dist/lib/image/Buffer.rgr +173 -0
  277. package/dist/lib/image/DCT.rgr +283 -0
  278. package/dist/lib/image/Deflate.rgr +339 -0
  279. package/dist/lib/image/HuffmanDecoder.rgr +181 -0
  280. package/dist/lib/image/ImageBuffer.rgr +706 -0
  281. package/dist/lib/image/JPEGDecoder.rgr +808 -0
  282. package/dist/lib/image/PNGDecoder.rgr +544 -0
  283. package/dist/lib/image/PNGEncoder.rgr +388 -0
  284. package/dist/lib/image/PPMImage.rgr +224 -0
  285. package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
  286. package/dist/lib/image/README.md +33 -0
  287. package/dist/lib/image/RasterBuffer.rgr +294 -0
  288. package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
  289. package/dist/lib/image/WebPDecoder.rgr +433 -0
  290. package/dist/lib/image/WebPLossless.rgr +1035 -0
  291. package/dist/lib/image/WebPLossy.rgr +1929 -0
  292. package/dist/lib/image/ranger.json +9 -0
  293. package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
  294. package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
  295. package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
  296. package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
  297. package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
  298. package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
  299. package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
  300. package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
  301. package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
  302. package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
  303. package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
  304. package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
  305. package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
  306. package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
  307. package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
  308. package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
  309. package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
  310. package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
  311. package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
  312. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
  313. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
  314. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
  315. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
  316. package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
  317. package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
  318. package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
  319. package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
  320. package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
  321. package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
  322. package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
  323. package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
  324. package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
  325. package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
  326. package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
  327. package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
  328. package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
  329. package/dist/lib/image/tests/run_webp_tests.sh +39 -0
  330. package/dist/lib/rust/RsJson.rgr +468 -0
  331. package/dist/lib/rust/RsPrelude.rgr +1663 -0
  332. package/dist/lib/shell_test.rgr +7 -7
  333. package/dist/lib/stdlib.rgr +21 -5
  334. package/dist/lib/zip/ranger.json +6 -0
  335. package/dist/rgrc.js +87318 -67610
  336. package/package.json +1010 -1040
  337. package/dist/README.md +0 -117
  338. package/dist/package.json +0 -47
package/dist/Lang.rgr CHANGED
@@ -1290,7 +1290,7 @@ func rg_sha256_hex(_ input: String) -> String {
1290
1290
  llvm ( nl "nullify " (e 1) nl)
1291
1291
  es6 ( (e 1) " = undefined;" nl)
1292
1292
  java7 ( (e 1) " = null;" nl)
1293
- cpp ( (e 1) "= NULL;" nl)
1293
+ cpp ( (e 1) " = std::nullopt;" nl (imp "<optional>"))
1294
1294
  ; Optionals ride a *GoNullable wrapper the constructor
1295
1295
  ; allocates once; clearing means emptying the wrapper, never
1296
1296
  ; nilling the wrapper pointer itself -- readers dereference it.
@@ -1513,7 +1513,19 @@ func r_io_get_env( name string) *GoNullable {
1513
1513
  ; optional this operator returns
1514
1514
  python ( "os.environ.get(" (e 1) ")" (imp "os") )
1515
1515
  dart ( "Platform.environment[" (e 1) "]" (imp "dart:io") )
1516
- cpp ( "std::getenv(" (e 1) ".c_str()) ? std::string(std::getenv(" (e 1) ".c_str())) : std::string(\"\")" (imp "<cstdlib>") )
1516
+ ; same story as read_file: the operator is optional and Python
1517
+ ; already answers None for a name that is not set. C++ handed
1518
+ ; back the empty string for unset and for set-to-empty alike.
1519
+ cpp ( "r_cpp_env_var(" (e 1) ")" (imp "<cstdlib>") (imp "<optional>")
1520
+ (create_polyfill "
1521
+ inline std::optional<std::string> r_cpp_env_var(const std::string& name) {
1522
+ std::optional<std::string> res;
1523
+ const char* v = std::getenv(name.c_str());
1524
+ if (v) { res = std::string(v); }
1525
+ return res;
1526
+ }
1527
+ ")
1528
+ )
1517
1529
  ; * ("/* environment variable reading not implemented */")
1518
1530
  }
1519
1531
  }
@@ -1524,7 +1536,11 @@ func r_io_get_env( name string) *GoNullable {
1524
1536
  llvm ( "(shell_arg " (e 1) ")")
1525
1537
  cpp ( "std::string(__g_argv[" (e 1) " + 1])")
1526
1538
  php ( "$argv[" (e 1) " + 1]" )
1527
- java7 ( "args[" (e 1) "]")
1539
+ ; `args` is in scope only inside main on Java, and a class
1540
+ ; method that reads the command line is not. main copies it
1541
+ ; into a static holder the writer emits (RgArgs.java), the
1542
+ ; same shape as __g_args on Kotlin and Dart.
1543
+ java7 ( "RgArgs.args[" (e 1) "]")
1528
1544
  csharp ('Environment.GetCommandLineArgs()[' (e 1) ' + 1]' (imp "System"))
1529
1545
  go ( "os.Args[" (e 1) " + 1]" (imp "os"))
1530
1546
  swift3 ("CommandLine.arguments[" (e 1) " + 1]")
@@ -1552,7 +1568,7 @@ func r_io_get_env( name string) *GoNullable {
1552
1568
  swift6 ("CommandLine.arguments.count - 1")
1553
1569
  csharp ('Environment.GetCommandLineArgs().Length - 1' (imp "System"))
1554
1570
  php ( "(count($argv) - 1)" )
1555
- java7 ( "args.length")
1571
+ java7 ( "RgArgs.args.length")
1556
1572
  go ( "int64( len( os.Args) - 1 )" (imp "os"))
1557
1573
  es6 ( "(process.argv.length - 2)" )
1558
1574
  rust ( "(std::env::args().len() as i64 - 1)")
@@ -3231,7 +3247,7 @@ func r_file_exists ( fileName:String ) -> Bool {
3231
3247
  }
3232
3248
  ")
3233
3249
  )
3234
- es6 ("require(\"fs\").existsSync(" (e 1) " + \"/\" + " (e 2) " )")
3250
+ es6 ("require(\"fs\").existsSync( require(\"path\").join(" (e 1) ", " (e 2) ") )")
3235
3251
  python ( "os.path.isfile(" (e 1) " + \"/\" + " (e 2) ")" (imp "os") )
3236
3252
  ranger ("( file_exists " (e 1) " + \"/\" + " (e 2) " )")
3237
3253
  java7 ( "new File(" (e 1) " + '/' + " (e 2) ").exists()" (imp "java.io.File") )
@@ -3379,7 +3395,7 @@ func r_file_mtime(pathName string, fileName string) int64 {
3379
3395
  "
3380
3396
  )
3381
3397
  )
3382
- es6 ( "require('fs').statSync(" (e 1) " + '/' + " (e 2) ").mtimeMs" )
3398
+ es6 ( "require('fs').statSync( require('path').join(" (e 1) ", " (e 2) ") ).mtimeMs" )
3383
3399
  cpp ( "r_file_mtime(" (e 1) ", " (e 2) ")" (imp "<sys/stat.h>") (imp "<string>")
3384
3400
  (create_polyfill "
3385
3401
  int64_t r_file_mtime(const std::string& path, const std::string& filename) {
@@ -3448,7 +3464,7 @@ func r_file_mtime ( path:String, name:String ) -> Int {
3448
3464
  python ( "int(os.path.getmtime(" (e 1) " + \"/\" + " (e 2) ") * 1000)" (imp "os") )
3449
3465
  java7 ( "((int)(new File(" (e 1) " + \"/\" + " (e 2) ").lastModified()))" (imp "java.io.File") )
3450
3466
  csharp ( "((int)(long)(System.IO.File.GetLastWriteTimeUtc(" (e 1) " + \"/\" + " (e 2) ") - new System.DateTime(1970, 1, 1)).TotalMilliseconds)" )
3451
- scala ( "new java.io.File(" (e 1) " + \"/\" + " (e 2) ").lastModified().toInt()" )
3467
+ scala ( "new java.io.File(" (e 1) " + \"/\" + " (e 2) ").lastModified().toInt" )
3452
3468
  php ( "(filemtime(" (e 1) ".'/'." (e 2) ") * 1000)" )
3453
3469
  ranger ( "(file_mtime " (e 1) " " (e 2) ")" )
3454
3470
  dart ( (imp "dart:io") "(() { try { return File((" (e 1) ") + \"/\" + (" (e 2) ")).lastModifiedSync().millisecondsSinceEpoch; } catch (_) { return 0; } })()" )
@@ -3490,6 +3506,14 @@ fn r_wall_clock_ms() -> f64 {
3490
3506
  csharp ( "(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds" )
3491
3507
  kotlin ( "System.currentTimeMillis().toDouble()" )
3492
3508
  java7 ( "((double)System.currentTimeMillis())" )
3509
+ php ( "(microtime(true) * 1000.0)" )
3510
+ dart ( "(DateTime.now().microsecondsSinceEpoch / 1000.0)" )
3511
+ scala ( "(System.currentTimeMillis().toDouble)" )
3512
+ swift3 ( "(Date().timeIntervalSince1970 * 1000.0)" (imp "Foundation") )
3513
+ swift6 ( "(Date().timeIntervalSince1970 * 1000.0)" (imp "Foundation") )
3514
+ ; `*` is 0.0: a target with no clock reports every duration as
3515
+ ; zero rather than refusing to compile, which is what a program
3516
+ ; that only prints a timing wants.
3493
3517
  * ( "0.0" )
3494
3518
  }
3495
3519
  }
@@ -3768,7 +3792,7 @@ fn r_write_file(path: &str, filename: &str, data: &str) {
3768
3792
 
3769
3793
  async_read_file _@(optional async):string (path:string filename:string) {
3770
3794
  templates {
3771
- es6 @flags('typescript') ( "await (new Promise<string>(resolve => { require('fs').readFile( " (e 1) " + '/' + " (e 2) " , 'utf8', (err,data)=>{ resolve(data) }) } ))" )
3795
+ es6 @flags('typescript') ( "await (new Promise<string>(resolve => { require('fs').readFile( require('path').join(" (e 1) ", " (e 2) ") , 'utf8', (err,data)=>{ resolve(data) }) } ))" )
3772
3796
  es6 ( "await (new Promise(resolve => { require('fs').readFile( " (e 1) " + '/' + " (e 2) " , 'utf8', (err,data)=>{ resolve(data) }) } ))" )
3773
3797
  }
3774
3798
  }
@@ -3777,8 +3801,8 @@ fn r_write_file(path: &str, filename: &str, data: &str) {
3777
3801
 
3778
3802
  templates {
3779
3803
  llvm ( "(read_file " (e 1) " " (e 2) ")")
3780
- ts ( "await (new Promise<string>(resolve => { require('fs').readFile( " (e 1) " + '/' + " (e 2) " , 'utf8', (err,data)=>{ resolve(data) }) } ))" )
3781
- es6 ( "await (new Promise(resolve => { require('fs').readFile( " (e 1) " + '/' + " (e 2) " , 'utf8', (err,data)=>{ resolve(data) }) } ))" )
3804
+ ts ( "await (new Promise<string>(resolve => { require('fs').readFile( require('path').join(" (e 1) ", " (e 2) ") , 'utf8', (err,data)=>{ resolve(data) }) } ))" )
3805
+ es6 ( "await (new Promise(resolve => { require('fs').readFile( require('path').join(" (e 1) ", " (e 2) ") , 'utf8', (err,data)=>{ resolve(data) }) } ))" )
3782
3806
  ranger ( "(read_file " (e 1) " " (e 2) ")" )
3783
3807
  ; returns the optional: None is how Python spells absent, which
3784
3808
  ; is what a missing or unreadable file has to answer
@@ -3792,17 +3816,186 @@ def rg_read_file(path, filename):
3792
3816
  return None
3793
3817
  ")
3794
3818
  )
3819
+ ; read_file is declared optional, and Python already answers
3820
+ ; None for a file it cannot read. C++ used to hand back the
3821
+ ; empty string for both a missing file and an empty one.
3795
3822
  cpp ( "r_cpp_readFile( " (e 1) " , " (e 2) ")" (imp "<fstream>")
3823
+
3796
3824
  (create_polyfill "
3797
- std::string r_cpp_readFile(const std::string& path, const std::string& filename)
3825
+ std::optional<std::string> r_cpp_readFile(const std::string& path, const std::string& filename)
3798
3826
  {
3827
+ std::optional<std::string> res;
3799
3828
  std::ifstream ifs(path + \"/\" + filename);
3829
+ if (!ifs) { return res; }
3800
3830
  std::string content( (std::istreambuf_iterator<char>(ifs) ),
3801
3831
  (std::istreambuf_iterator<char>() ) );
3802
- return content;
3832
+ res = content;
3833
+ return res;
3834
+ }
3835
+ ")
3836
+
3837
+ )
3838
+ php ("file_get_contents(" (e 1) " . \"/\" . " (e 2) ") " )
3839
+ swift3 ("r_read_file(dirName: " (e 1) " + \"/\" + " (e 2) ") "
3840
+ (create_polyfill "
3841
+ func r_read_file ( dirName:String ) -> String? {
3842
+ let res: String?
3843
+ do {
3844
+ res = try String(contentsOfFile:dirName)
3845
+ } catch {
3846
+ res = nil
3847
+ }
3848
+ return res
3849
+ }
3850
+ ")
3851
+ )
3852
+ swift6 ("r_read_file(dirName: " (e 1) " + \"/\" + " (e 2) ") "
3853
+ (imp "Foundation")
3854
+ (create_polyfill "
3855
+ func r_read_file ( dirName:String ) -> String? {
3856
+ let res: String?
3857
+ do {
3858
+ res = try String(contentsOfFile:dirName, encoding: .utf8)
3859
+ } catch let error {
3860
+ print(\"Error reading file: \\(dirName)\")
3861
+ print(\"Error: \\(error.localizedDescription)\")
3862
+ res = nil
3863
+ }
3864
+ return res
3865
+ }
3866
+ ")
3867
+ )
3868
+ java7 ( "readFile(" (e 1) " + \"/\" + " (e 2) " , StandardCharsets.UTF_8 )"
3869
+ (imp "java.nio.file.Paths")
3870
+ (imp "java.io.File")
3871
+ (imp "java.nio.file.Files")
3872
+ (imp 'java.io.IOException')
3873
+ (imp "java.nio.charset.Charset")
3874
+ (imp "java.nio.charset.StandardCharsets")
3875
+ (create_polyfill "
3876
+ static String readFile(String path, Charset encoding)
3877
+ {
3878
+ try {
3879
+ byte[] encoded = Files.readAllBytes(Paths.get(path));
3880
+ return new String(encoded, encoding);
3881
+ } catch(IOException e) {
3882
+ return \"\";
3883
+ }
3803
3884
  }
3804
3885
  ")
3805
-
3886
+
3887
+ )
3888
+ scala ( "Try(Source.fromFile(" (e 1) " + \"/\" + " (e 2) ").getLines.mkString).toOption" (imp "scala.io.Source") (imp "scala.util.Try"))
3889
+ go ( "r_io_read_file(" (e 1) ", " (e 2) ")" (imp "io/ioutil")
3890
+ (create_polyfill "
3891
+
3892
+ // polyfill for reading files
3893
+ func r_io_read_file( path string , fileName string ) *GoNullable {
3894
+ res := new(GoNullable);
3895
+ if v, err := ioutil.ReadFile(path + \"/\" + fileName); err == nil {
3896
+ res.has_value = true
3897
+ res.value = string(v)
3898
+ } else {
3899
+ res.has_value = false
3900
+ }
3901
+ return res
3902
+ }
3903
+ "))
3904
+ rust ( "r_read_file(&" (e 1) ", &" (e 2) ")"
3905
+ (create_polyfill "
3906
+ fn r_read_file(path: &str, filename: &str) -> Option<String> {
3907
+ let full_path = format!(\"{}/{}\", path, filename);
3908
+ std::fs::read_to_string(full_path).ok()
3909
+ }
3910
+ "))
3911
+ es6 ( "(require('fs').readFileSync( require('path').join(" (e 1) ", " (e 2) ") , 'utf8'))" )
3912
+ kotlin ( "try { java.io.File(if (" (e 1) ".isEmpty()) " (e 2) " else " (e 1) " + \"/\" + " (e 2) ").readText() } catch (_: Exception) { null }" (imp "java.io.File") )
3913
+ dart ( (imp "dart:io") "(() { try { final __p = (" (e 1) ").isEmpty ? (" (e 2) ") : ((" (e 1) ") + \"/\" + (" (e 2) ")); return File(__p).readAsStringSync(); } catch (_) { return null; } })()" )
3914
+ csharp ( "RgFiles.ReadFile(" (e 1) ", " (e 2) ")"
3915
+ (polyfill "after_imports" "
3916
+ // Files on the C# target. System.IO says all of this, but each operator needs
3917
+ // one expression and several need a try/catch, so they are wrapped here.
3918
+ static class RgFiles {
3919
+ static string Join(string path, string name) {
3920
+ if (path.Length == 0) { return name; }
3921
+ return path + \"/\" + name;
3922
+ }
3923
+ // The optional this operator returns: null is how C# spells absent, which is
3924
+ // what a missing or unreadable file has to answer.
3925
+ public static string ReadFile(string path, string name) {
3926
+ try {
3927
+ return System.IO.File.ReadAllText(Join(path, name), new System.Text.UTF8Encoding(false));
3928
+ } catch (System.Exception) {
3929
+ return null;
3930
+ }
3931
+ }
3932
+ public static void WriteFile(string path, string name, string data) {
3933
+ string full = Join(path, name);
3934
+ string dir = System.IO.Path.GetDirectoryName(full);
3935
+ if (dir != null && dir.Length > 0) { System.IO.Directory.CreateDirectory(dir); }
3936
+ // No BOM: every other target writes plain UTF-8 and the compiler compares
3937
+ // its own output byte for byte.
3938
+ System.IO.File.WriteAllText(full, data, new System.Text.UTF8Encoding(false));
3939
+ }
3940
+ public static void CreateDir(string path) {
3941
+ try { System.IO.Directory.CreateDirectory(path); } catch (System.Exception) { }
3942
+ }
3943
+ public static bool DirExists(string path) {
3944
+ return System.IO.Directory.Exists(path);
3945
+ }
3946
+ }
3947
+ ")
3948
+ )
3949
+ }
3950
+ }
3951
+
3952
+ ; read_file, but not async on JavaScript. read_file awaits fs.readFile,
3953
+ ; so on JavaScript every function that reads a file, and every function
3954
+ ; that can reach one, is emitted async: in the compiler that was the
3955
+ ; whole import, analysis and writer chain, and each call then paid for
3956
+ ; a promise and a heap frame -- three seconds of a twelve second
3957
+ ; compile, all of it garbage. This one reads with readFileSync and
3958
+ ; answers absent on failure, as read_file does. The other targets read
3959
+ ; synchronously either way and share read_file's polyfills, which the
3960
+ ; writer emits once per text. Use it where blocking the event loop is
3961
+ ; fine: a command line tool, not a server.
3962
+ read_file_sync cmdReadFileSync@(optional):string (path:string filename:string) {
3963
+
3964
+ templates {
3965
+ llvm ( "(read_file " (e 1) " " (e 2) ")")
3966
+ ts ( "(() => { try { return require('fs').readFileSync( require('path').join(" (e 1) ", " (e 2) ") , 'utf8'); } catch (e) { return undefined; } })()" )
3967
+ es6 ( "(() => { try { return require('fs').readFileSync( require('path').join(" (e 1) ", " (e 2) ") , 'utf8'); } catch (e) { return undefined; } })()" )
3968
+ ranger ( "(read_file " (e 1) " " (e 2) ")" )
3969
+ ; returns the optional: None is how Python spells absent, which
3970
+ ; is what a missing or unreadable file has to answer
3971
+ python ( "rg_read_file(" (e 1) ", " (e 2) ")" (imp "os")
3972
+ (create_polyfill "
3973
+ def rg_read_file(path, filename):
3974
+ try:
3975
+ with open(os.path.join(path, filename), \"r\", encoding=\"utf-8\") as f:
3976
+ return f.read()
3977
+ except Exception:
3978
+ return None
3979
+ ")
3980
+ )
3981
+ ; read_file is declared optional, and Python already answers
3982
+ ; None for a file it cannot read. C++ used to hand back the
3983
+ ; empty string for both a missing file and an empty one.
3984
+ cpp ( "r_cpp_readFile( " (e 1) " , " (e 2) ")" (imp "<fstream>")
3985
+
3986
+ (create_polyfill "
3987
+ std::optional<std::string> r_cpp_readFile(const std::string& path, const std::string& filename)
3988
+ {
3989
+ std::optional<std::string> res;
3990
+ std::ifstream ifs(path + \"/\" + filename);
3991
+ if (!ifs) { return res; }
3992
+ std::string content( (std::istreambuf_iterator<char>(ifs) ),
3993
+ (std::istreambuf_iterator<char>() ) );
3994
+ res = content;
3995
+ return res;
3996
+ }
3997
+ ")
3998
+
3806
3999
  )
3807
4000
  php ("file_get_contents(" (e 1) " . \"/\" . " (e 2) ") " )
3808
4001
  swift3 ("r_read_file(dirName: " (e 1) " + \"/\" + " (e 2) ") "
@@ -3877,7 +4070,6 @@ fn r_read_file(path: &str, filename: &str) -> Option<String> {
3877
4070
  std::fs::read_to_string(full_path).ok()
3878
4071
  }
3879
4072
  "))
3880
- es6 ( "(require('fs').readFileSync( " (e 1) " + '/' + " (e 2) " , 'utf8'))" )
3881
4073
  kotlin ( "try { java.io.File(if (" (e 1) ".isEmpty()) " (e 2) " else " (e 1) " + \"/\" + " (e 2) ").readText() } catch (_: Exception) { null }" (imp "java.io.File") )
3882
4074
  dart ( (imp "dart:io") "(() { try { final __p = (" (e 1) ").isEmpty ? (" (e 2) ") : ((" (e 1) ") + \"/\" + (" (e 2) ")); return File(__p).readAsStringSync(); } catch (_) { return null; } })()" )
3883
4075
  csharp ( "RgFiles.ReadFile(" (e 1) ", " (e 2) ")"
@@ -4032,7 +4224,7 @@ static class RgFiles {
4032
4224
 
4033
4225
  unwrap cmdUnwrap:int ( arg@(optional):int ) {
4034
4226
  templates {
4035
- cpp ( "/*unwrap int*/" (e 1) ".value" )
4227
+ cpp ( (e 1) ".value()" )
4036
4228
  ; an optional int is `int?` on C#, and Nullable<T> does not
4037
4229
  ; convert back to T on its own
4038
4230
  csharp ( "(" (e 1) ").Value" )
@@ -4040,16 +4232,37 @@ static class RgFiles {
4040
4232
  }
4041
4233
  unwrap cmdUnwrap:double ( arg@(optional):double ) {
4042
4234
  templates {
4043
- cpp ( "/*unwrap dbl*/" (e 1) ".value" )
4235
+ cpp ( (e 1) ".value()" )
4044
4236
  csharp ( "(" (e 1) ").Value" )
4045
4237
  }
4046
4238
  }
4239
+ ; see the null? overload: an optional string carries its own presence
4240
+ ; bit on C++, so reading the value out of one is not a no-op.
4241
+ unwrap cmdUnwrap:string ( arg@(optional):string ) {
4242
+ templates {
4243
+ cpp ( (e 1) ".value()" )
4244
+ }
4245
+ }
4047
4246
  ; `bool?` is a Nullable<bool> like the two above, and `if (unwrap ok)`
4048
4247
  ; without the .Value does not compile. Only C# has an entry, so every
4049
4248
  ; other target keeps reaching the generic overload below.
4050
4249
  unwrap cmdUnwrap:boolean ( arg@(optional):boolean ) {
4051
4250
  templates {
4052
4251
  csharp ( "(" (e 1) ").Value" )
4252
+ ; an optional boolean VARIABLE is a bare `bool` on C++ (see
4253
+ ; null?), but a map read (`get`) hands back std::optional<bool>
4254
+ cpp ( "rg_opt_bool(" (e 1) ")" (imp "<optional>") (create_polyfill "
4255
+ inline bool rg_opt_bool(bool b) { return b; }
4256
+ inline bool rg_opt_bool(const std::optional<bool> & b) { return b.value(); }
4257
+ ") )
4258
+ }
4259
+ }
4260
+
4261
+ ; an optional charbuffer is a bare `const char*` on C++ (writeTypeDef
4262
+ ; does not wrap it), so there is no optional to read the value out of
4263
+ unwrap cmdUnwrap:charbuffer ( arg@(optional):charbuffer ) {
4264
+ templates {
4265
+ cpp ( (e 1) )
4053
4266
  }
4054
4267
  }
4055
4268
 
@@ -4066,7 +4279,7 @@ static class RgFiles {
4066
4279
  swift3 ( (e 1) "!" )
4067
4280
  swift6 ( (e 1) "!" )
4068
4281
  go ( (e 1) ".value.(" (typeof 1) ")" )
4069
- cpp ( (e 1) )
4282
+ cpp ( (e 1) ".value()" )
4070
4283
 
4071
4284
  * ( (e 1) )
4072
4285
  }
@@ -4234,12 +4447,15 @@ static class RgFiles {
4234
4447
  ; An enum value is an int on every target, and Python does
4235
4448
  ; not concatenate an int onto a str.
4236
4449
  python ( (e 1) " + str(" (e 2) ")" )
4237
- kotlin ( (e 1) " + " (e 2) ".toString()" )
4450
+ ; the operand is parenthesised because `.toString()` binds
4451
+ ; to the last TOKEN, not to the expression: `"" + i * 4`
4452
+ ; came out as `"" + i * 4.toString()`, which is Int * String.
4453
+ kotlin ( (e 1) " + (" (e 2) ").toString()" )
4238
4454
  ; an enum is an int on Dart, and Dart has no String + int
4239
- dart ( (e 1) " + " (e 2) ".toString()" )
4455
+ dart ( (e 1) " + (" (e 2) ").toString()" )
4240
4456
  java7 ( (e 1) " + String.valueOf(" (e 2) ")" )
4241
- csharp ( (e 1) " + " (e 2) ".ToString()" )
4242
- scala ( (e 1) " + " (e 2) ".toString" )
4457
+ csharp ( (e 1) " + (" (e 2) ").ToString()" )
4458
+ scala ( (e 1) " + (" (e 2) ").toString" )
4243
4459
  * ( (e 1) " + " (e 2) )
4244
4460
  }
4245
4461
  }
@@ -4265,8 +4481,8 @@ static class RgFiles {
4265
4481
  rust ( (custom _) )
4266
4482
  swift3 ( (e 1) " + String(" (e 2)")" )
4267
4483
  swift6 ( (e 1) " + String(" (e 2)")" )
4268
- kotlin ( (e 1) " + " (e 2)".toString()" )
4269
- dart ( (e 1) " + " (e 2)".toString()" )
4484
+ kotlin ( (e 1) " + (" (e 2) ").toString()" )
4485
+ dart ( (e 1) " + (" (e 2) ").toString()" )
4270
4486
  cpp ( (e 1 ) " + " "r_double_to_string(" (e 2) ")" (imp "<sstream>") (imp "<iomanip>") (imp "<string>") (imp "<cstdlib>")
4271
4487
  (create_polyfill "
4272
4488
  inline std::string r_double_to_string(double v) {
@@ -4298,8 +4514,8 @@ inline std::string r_double_to_string(double v) {
4298
4514
  go ( "strings.Join([]string{ " (e 1) ",strconv.FormatInt(" (e 2) ", 10) }, \"\")" (imp "strings") (imp "strconv"))
4299
4515
  swift3 ( (e 1) " + String(" (e 2)")" )
4300
4516
  swift6 ( (e 1) " + String(" (e 2)")" )
4301
- kotlin ( (e 1) " + " (e 2)".toString()" )
4302
- dart ( (e 1) " + " (e 2)".toString()" )
4517
+ kotlin ( (e 1) " + (" (e 2) ").toString()" )
4518
+ dart ( (e 1) " + (" (e 2) ").toString()" )
4303
4519
  rust ( (custom _) )
4304
4520
  cpp ( (e 1 ) " + std::to_string(" (e 2) ")")
4305
4521
  php ( (e 1) " . " (e 2) )
@@ -4323,7 +4539,10 @@ inline std::string r_double_to_string(double v) {
4323
4539
  llvm ( "(idiv " (e 1) " " (e 2) ")" )
4324
4540
  es6 ( "((" (e 1) " / " (e 2) ") | 0)" )
4325
4541
  go ( (e 1) " / " (e 2) )
4326
- rust ( "((" (e 1) ") / (" (e 2) "))" )
4542
+ ; (custom _): the template's own pair plus the one each
4543
+ ; operand writes for itself gave `(((sum * pct)) / (100))`.
4544
+ ; RustOperators writes it as `sum * pct / 100`.
4545
+ rust ( (custom _) )
4327
4546
  kotlin ( (e 1) " / " (e 2) )
4328
4547
  dart ( "((" (e 1) ") ~/ (" (e 2) "))" )
4329
4548
  python ( "((" (e 1) ") // (" (e 2) "))" )
@@ -4462,8 +4681,8 @@ inline std::string r_double_to_string(double v) {
4462
4681
  templates {
4463
4682
  swift3 ( "String(" (e 1) ") + " (e 2) )
4464
4683
  swift6 ( "String(" (e 1) ") + " (e 2) )
4465
- kotlin ( (e 1) ".toString() + " (e 2) )
4466
- dart ( (e 1) ".toString() + " (e 2) )
4684
+ kotlin ( "(" (e 1) ").toString() + " (e 2) )
4685
+ dart ( "(" (e 1) ").toString() + " (e 2) )
4467
4686
  go ( "strings.Join([]string{ strconv.FormatFloat(" (e 1) ",'f', -1, 64)," (e 2) " }, \"\")" (imp "strings") (imp "strconv"))
4468
4687
  cpp ( "r_double_to_string(" (e 1) ")" (imp "<sstream>") (imp "<iomanip>") (imp "<string>") (imp "<cstdlib>")
4469
4688
  (create_polyfill "
@@ -4496,8 +4715,8 @@ inline std::string r_double_to_string(double v) {
4496
4715
  go ( "strings.Join([]string{ strconv.FormatInt(" (e 1) ", 10)," (e 2) " }, \"\")" (imp "strings") (imp "strconv"))
4497
4716
  swift3 ( "String(" (e 1) ") + " (e 2) )
4498
4717
  swift6 ( "String(" (e 1) ") + " (e 2) )
4499
- kotlin ( (e 1) ".toString() + " (e 2) )
4500
- dart ( (e 1) ".toString() + " (e 2) )
4718
+ kotlin ( "(" (e 1) ").toString() + " (e 2) )
4719
+ dart ( "(" (e 1) ").toString() + " (e 2) )
4501
4720
  rust ( "format!(\"{}{}\", " (e 1) ", " (e 2) ")" )
4502
4721
  cpp ( "std::to_string(" (e 1) ") + " (e 2) )
4503
4722
  php ( (e 1) " . " (e 2) )
@@ -4605,8 +4824,25 @@ def r_div_f64(a, b):
4605
4824
  }
4606
4825
 
4607
4826
 
4827
+ ; Every target reached this through the `*` macro, which expands to a
4828
+ ; ternary over an explicit presence test. On JavaScript that is
4829
+ ; (((typeof(hit) !== "undefined" && hit != null ) ) ? hit : "unknown")
4830
+ ; where `hit ?? "unknown"` says the same thing -- and says it once,
4831
+ ; rather than naming the operand twice. Every entry below triggers on
4832
+ ; exactly what that target's `null?` tests, so the meaning is
4833
+ ; unchanged; the macro stays for the targets with no such operator
4834
+ ; (Java, Go, Python, C++, Rust), which keep the ternary.
4608
4835
  ?? elvis:T ( left@(optional):T right:T ) {
4609
4836
  templates {
4837
+ es6 ( "(" (e 1) " ?? " (e 2) ")" )
4838
+ dart ( "(" (e 1) " ?? " (e 2) ")" )
4839
+ csharp ( "(" (e 1) " ?? " (e 2) ")" )
4840
+ swift3 ( "(" (e 1) " ?? " (e 2) ")" )
4841
+ swift6 ( "(" (e 1) " ?? " (e 2) ")" )
4842
+ php ( "(" (e 1) " ?? " (e 2) ")" )
4843
+ kotlin ( "(" (e 1) " ?: " (e 2) ")" )
4844
+ ; an optional is an Option[T] here, not a nullable reference
4845
+ scala ( "(" (e 1) ").getOrElse(" (e 2) ")" )
4610
4846
  * @macro(true) ( '(? (!null? ' (e 1) ') (unwrap ' (e 1) ') ' (e 2) ')' )
4611
4847
  }
4612
4848
  }
@@ -4662,7 +4898,7 @@ def r_div_f64(a, b):
4662
4898
  kotlin ( "(Math.ceil(" (e 1) ")).toInt()" (imp "java.lang.Math"))
4663
4899
  dart ( "(" (e 1) ").ceil()" )
4664
4900
  cpp ( "(int)ceil(" (e 1) ")" (imp "<cmath>"))
4665
- csharp ( "(int)Math.Ceil(" (e 1) ")" (imp "System"))
4901
+ csharp ( "(int)Math.Ceiling(" (e 1) ")" (imp "System"))
4666
4902
  go ( "int64(math.Ceil(" (e 1) "))" (imp "math"))
4667
4903
  php ( "ceil(" (e 1) ")" )
4668
4904
  rust ( "" (e 1) ".ceil() as i64" )
@@ -4811,6 +5047,7 @@ def r_div_f64(a, b):
4811
5047
 
4812
5048
  if! cmdIf:void ( condition:boolean then_block:block ) {
4813
5049
  templates {
5050
+ cpp ( (custom _) )
4814
5051
  * @macro(true) ('if (false == (' (e 1) ' ) ) { ' (block 2) ' } ' )
4815
5052
  }
4816
5053
  }
@@ -4827,24 +5064,31 @@ def r_div_f64(a, b):
4827
5064
  }
4828
5065
 
4829
5066
  if cmdIf:void ( condition:boolean then_block:block ) {
5067
+ ; NOTE: this is triggered in normal C++ execution
4830
5068
  templates {
4831
5069
  ranger ( "if (" (e 1) ") {" I nl (block 2) nl i "}" nl )
4832
5070
  rust ( "if " (e 1) " {" I nl (block 2) nl i "}" nl )
4833
5071
  go ( "if " (e 1) " {" I nl (block 2) nl i "}" nl )
4834
5072
  python ( "if " (e 1) ":" I nl (block 2) nl i nl )
5073
+ cpp ((custom _))
4835
5074
  * ( "if ( " (e 1) " ) {" I nl (block 2) nl i "}" nl )
4836
5075
  }
4837
5076
  }
4838
5077
 
4839
5078
  if cmdIf:void ( condition@(optional):int then_block:block ) {
4840
5079
  templates {
4841
- cpp ( "if ( " (e 1) ".has_value ) {" nl I (block 2) i nl "}" nl )
5080
+ cpp ( "if ( " (e 1) ".has_value() ) {" nl I (block 2) i nl "}" nl )
4842
5081
  ranger ( "if ( " (e 1) " ) {" I nl (block 2) i nl "}" nl )
4843
5082
  scala ( "if ( " (e 1) ".isDefined ) {" nl I (block 2) i nl "}" nl )
4844
5083
  swift3 ( "if ( " (e 1) " != nil ) {" nl I (block 2) i nl "}" nl )
4845
5084
  swift6 ( "if ( " (e 1) " != nil ) {" nl I (block 2) i nl "}" nl )
4846
5085
  kotlin ( "if ( " (e 1) " != null ) {" nl I (block 2) i nl "}" nl )
4847
- java7 ( "if ( " (e 1) ".isPresent()) {" nl I (block 2) i nl "}" nl )
5086
+ ; NOT Optional.isPresent(): java.util.Optional is Java 8, and
5087
+ ; an optional on this target is a nullable reference or a boxed
5088
+ ; primitive -- which is what `null?` already compares against.
5089
+ ; The six `isPresent()` entries were the only thing that
5090
+ ; disagreed, and each one was a "cannot find symbol".
5091
+ java7 ( "if ( " (e 1) " != null) {" nl I (block 2) i nl "}" nl )
4848
5092
  csharp ( "if ( " (e 1) " != null) {" nl I (block 2) i nl "}" nl )
4849
5093
  php ( "if ( isset( " (e 1) " ) ) {" nl I (block 2) i nl "}" nl )
4850
5094
  go ( "if ( " (e 1) ".has_value) {" nl I (block 2) i nl "}" nl )
@@ -4857,13 +5101,13 @@ def r_div_f64(a, b):
4857
5101
 
4858
5102
  if cmdIf:void ( condition@(optional):double then_block:block ) {
4859
5103
  templates {
4860
- cpp ( "if ( " (e 1) ".has_value ) {" nl I (block 2) i nl "}" nl )
5104
+ cpp ( "if ( " (e 1) ".has_value() ) {" nl I (block 2) i nl "}" nl )
4861
5105
  ranger ( "if ( " (e 1) " ) {" I nl (block 2) i nl "}" nl )
4862
5106
  scala ( "if ( " (e 1) ".isDefined ) {" nl I (block 2) i nl "}" nl )
4863
5107
  swift3 ( "if ( " (e 1) " != nil ) {" nl I (block 2) i nl "}" nl )
4864
5108
  swift6 ( "if ( " (e 1) " != nil ) {" nl I (block 2) i nl "}" nl )
4865
5109
  kotlin ( "if ( " (e 1) " != null ) {" nl I (block 2) i nl "}" nl )
4866
- java7 ( "if ( " (e 1) ".isPresent()) {" nl I (block 2) i nl "}" nl )
5110
+ java7 ( "if ( " (e 1) " != null) {" nl I (block 2) i nl "}" nl )
4867
5111
  csharp ( "if ( " (e 1) " != null) {" nl I (block 2) i nl "}" nl )
4868
5112
  php ( "if ( isset( " (e 1) " ) ) {" nl I (block 2) i nl "}" nl )
4869
5113
  go ( "if ( " (e 1) ".has_value) {" nl I (block 2) i nl "}" nl )
@@ -4874,16 +5118,15 @@ def r_div_f64(a, b):
4874
5118
  }
4875
5119
  }
4876
5120
 
4877
- ; typed optional int/double with an else branch: r_optional_primitive is
4878
- ; a value type (has_value field), not a pointer, so the generic :T
4879
- ; `!= NULL` form below does not compile under clang. Emit `.has_value`.
5121
+ ; typed optional int/double with an else branch: a std::optional is a
5122
+ ; value, not a pointer, so the check is `.has_value()`.
4880
5123
  if cmdIf:void ( condition@(optional):int then_block:block else_block ) {
4881
5124
  templates {
4882
- cpp ( "if ( " (e 1) ".has_value ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl )
5125
+ cpp ( "if ( " (e 1) ".has_value() ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl )
4883
5126
  ranger ( "if ( " (e 1) " ) {" I nl (block 2) i nl "} {" nl I (block 3) i "}" nl)
4884
5127
  php ( "if ( isset(" (e 1) ") ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4885
5128
  scala ( "if ( " (e 1) ".isDefined ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4886
- java7 ( "if ( " (e 1) ".isPresent() ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
5129
+ java7 ( "if ( " (e 1) " != null ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4887
5130
  csharp ( "if ( " (e 1) " != null ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4888
5131
  kotlin ( "if ( " (e 1) " != null ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4889
5132
  rust ( "if " (e 1) ".is_some() {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
@@ -4898,11 +5141,11 @@ def r_div_f64(a, b):
4898
5141
 
4899
5142
  if cmdIf:void ( condition@(optional):double then_block:block else_block ) {
4900
5143
  templates {
4901
- cpp ( "if ( " (e 1) ".has_value ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl )
5144
+ cpp ( "if ( " (e 1) ".has_value() ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl )
4902
5145
  ranger ( "if ( " (e 1) " ) {" I nl (block 2) i nl "} {" nl I (block 3) i "}" nl)
4903
5146
  php ( "if ( isset(" (e 1) ") ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4904
5147
  scala ( "if ( " (e 1) ".isDefined ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4905
- java7 ( "if ( " (e 1) ".isPresent() ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
5148
+ java7 ( "if ( " (e 1) " != null ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4906
5149
  csharp ( "if ( " (e 1) " != null ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4907
5150
  kotlin ( "if ( " (e 1) " != null ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4908
5151
  rust ( "if " (e 1) ".is_some() {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
@@ -4923,13 +5166,13 @@ def r_div_f64(a, b):
4923
5166
  swift6 ( "if ( " (e 1) " != nil ) {" nl I (block 2) i nl "}" nl )
4924
5167
  kotlin ( "if ( " (e 1) " != null ) {" nl I (block 2) i nl "}" nl )
4925
5168
  dart ( "if ( " (e 1) " != null ) {" nl I (block 2) i nl "}" nl )
4926
- java7 ( "if ( " (e 1) ".isPresent()) {" nl I (block 2) i nl "}" nl )
5169
+ java7 ( "if ( " (e 1) " != null) {" nl I (block 2) i nl "}" nl )
4927
5170
  csharp ( "if ( " (e 1) " != null) {" nl I (block 2) i nl "}" nl )
4928
5171
  ; go ( "" (e 1 ) " == nil " )
4929
5172
  ; is_some
4930
5173
  php ( "if ( isset( " (e 1) " ) ) {" nl I (block 2) i nl "}" nl )
4931
5174
  go ( "if ( " (e 1) ".has_value) {" nl I (block 2) i nl "}" nl )
4932
- cpp ( "if ( " (e 1) " != NULL ) {" nl I (block 2) i nl "}" nl )
5175
+ cpp ( "if ( " (e 1) ".has_value() ) {" nl I (block 2) i nl "}" nl )
4933
5176
  rust ( "if " (e 1) ".is_some() {" nl I (block 2) i nl "}" nl )
4934
5177
  python ( "if " (e 1) " is not None:" nl I (block 2) i nl )
4935
5178
  * ( "if ( typeof(" ( e 1 ) ") != \"undefined\" ) {" nl I (block 2) i nl "}" nl )
@@ -4942,7 +5185,7 @@ def r_div_f64(a, b):
4942
5185
  ranger ( "if ( " (e 1) " ) {" I nl (block 2) i nl "} {" nl I (block 3) i "}" nl)
4943
5186
  php ( "if ( isset(" (e 1) ") ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4944
5187
  scala ( "if ( " (e 1) ".isDefined ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4945
- java7 ( "if ( " (e 1) ".isPresent() ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
5188
+ java7 ( "if ( " (e 1) " != null ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4946
5189
  csharp ( "if ( " (e 1) " != null ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4947
5190
  kotlin ( "if ( " (e 1) " != null ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4948
5191
  dart ( "if ( " (e 1) " != null ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl )
@@ -4950,7 +5193,7 @@ def r_div_f64(a, b):
4950
5193
  swift3 ( "if ( " (e 1) " != nil ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4951
5194
  swift6 ( "if ( " (e 1) " != nil ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4952
5195
  go ( "if ( " (e 1) ".has_value ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4953
- cpp ( "if ( " (e 1) " != NULL ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
5196
+ cpp ( "if ( " (e 1) ".has_value() ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4954
5197
  python ( "if " (e 1) " is not None:" I nl (block 2) i nl "else:" I nl (block 3) i nl)
4955
5198
  * ( "if ( typeof(" ( e 1 ) ") != \"undefined\" ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl)
4956
5199
  }
@@ -4984,7 +5227,7 @@ def r_div_f64(a, b):
4984
5227
  switch cmdSwitch:void ( condition:int case_list:block ) {
4985
5228
  templates {
4986
5229
  scala ( (e 1) " match { " I (block 2) i "}" )
4987
- kotlin ( "when (" (e 1) ") { " I (block 2) i "}" )
5230
+ kotlin ( (custom _) )
4988
5231
  python ( nl "match (" (e 1) "):" I (block 2) i nl )
4989
5232
  ; Rust demands an exhaustive match, and a Ranger `switch` need not
4990
5233
  ; have a `default`. The catch-all goes in unconditionally: where
@@ -5037,7 +5280,7 @@ def r_div_f64(a, b):
5037
5280
  templates {
5038
5281
  cpp ( (custom _) )
5039
5282
  scala ( (e 1) " match { " I (block 2) i "}" )
5040
- kotlin ( "when (" (e 1) ") { " I (block 2) i "}" )
5283
+ kotlin ( (custom _) )
5041
5284
  python ( nl "match (" (e 1) "):" I (block 2) i nl )
5042
5285
  ; Rust matches a &str, so a String subject needs as_str().
5043
5286
  ; `&*x` is a &str whether x is a String or already a &str;
@@ -5054,7 +5297,7 @@ def r_div_f64(a, b):
5054
5297
  templates {
5055
5298
 
5056
5299
  scala ( (e 1) " match { " I (block 2) i "}" )
5057
- kotlin ( "when (" (e 1) ") { " I (block 2) i "}" )
5300
+ kotlin ( (custom _) )
5058
5301
  ; An ENUM condition lands here, not on the int overload above,
5059
5302
  ; and Python and Rust have no C style switch -- without these two
5060
5303
  ; the `*` fallback wrote a C `switch` into a Python file. The
@@ -5076,8 +5319,10 @@ def r_div_f64(a, b):
5076
5319
 
5077
5320
  ; A case over a string needs its own entry for Rust only: a match arm is
5078
5321
  ; a pattern, and the Rust writer gives a string literal in expression
5079
- ; position a `.to_string()`, which a pattern does not take. `(str 1)`
5080
- ; writes the text of the literal and nothing else. Every other target
5322
+ ; position a `.to_string()`, which a pattern does not take. `(estr 1)`
5323
+ ; writes the text of the literal, escaped and unquoted -- escaped
5324
+ ; because a pattern holding a raw quote, backslash or newline ends
5325
+ ; early, which is how `case "\""` became `"""`. Every other target
5081
5326
  ; keeps the spelling of the case above.
5082
5327
  case cmdCase:void ( condition:string case_block:block ) {
5083
5328
  templates {
@@ -5091,7 +5336,7 @@ def r_div_f64(a, b):
5091
5336
  es6 ( nl "case " (e 1)" : " nl I (java_case 2) nl i )
5092
5337
  cpp ( nl "case " (e 1)" : " nl I "{" nl I (block 2) nl "break;" i nl "}" i )
5093
5338
  python ( nl "case " (e 1) ":" nl I "pass" nl (block 2) nl i )
5094
- rust ( nl "\"" (str 1) "\" => {" nl I (block 2) nl i "}" nl )
5339
+ rust ( nl "\"" (estr 1) "\" => {" nl I (block 2) nl i "}" nl )
5095
5340
  * ( nl "case " (e 1)" : " nl I (block 2) nl "break;" i )
5096
5341
  }
5097
5342
  }
@@ -5232,17 +5477,23 @@ std::vector<T> r_make_vector_from_array( const T (&data)[N] )
5232
5477
  }
5233
5478
  null? cmdIsNotNull:boolean ( arg@(optional):int ) {
5234
5479
  templates {
5235
- cpp ((e 1) ".has_value == false")
5480
+ cpp ("(" (e 1) ".has_value() == false)" )
5236
5481
  }
5237
5482
  }
5238
5483
  null? cmdIsNotNull:boolean ( arg@(optional):double ) {
5239
5484
  templates {
5240
- cpp ((e 1) ".has_value == false")
5485
+ cpp ("(" (e 1) ".has_value() == false)" )
5241
5486
  }
5242
5487
  }
5488
+ ; an optional string used to be a bare std::string on C++, with ""
5489
+ ; standing in for absent. That is the one place the C++ output
5490
+ ; disagreed with every other target: `s = ""` then `null? s` answered
5491
+ ; true here and false on JS, Python, Go and Rust. It is an
5492
+ ; std::optional<std::string> now, so "" is a value like any
5493
+ ; other and the presence bit is its own.
5243
5494
  null? cmdIsNotNull:boolean ( arg@(optional):string ) {
5244
5495
  templates {
5245
- cpp ((e 1) ".empty()")
5496
+ cpp ((e 1) ".has_value() == false")
5246
5497
  }
5247
5498
  }
5248
5499
 
@@ -5270,7 +5521,7 @@ std::vector<T> r_make_vector_from_array( const T (&data)[N] )
5270
5521
  templates {
5271
5522
  ranger ("(null? " (e 1) ")")
5272
5523
  php ( "(!isset(" (e 1) "))")
5273
- cpp ((e 1) " == NULL")
5524
+ cpp ("!" (e 1) ".has_value()")
5274
5525
  swift3 ((e 1) " == nil")
5275
5526
  swift6 ((e 1) " == nil")
5276
5527
  java7 ( (e 1) " == null")
@@ -5287,17 +5538,17 @@ std::vector<T> r_make_vector_from_array( const T (&data)[N] )
5287
5538
  }
5288
5539
  !null? cmdIsNotNull:boolean ( arg@(optional):int ) {
5289
5540
  templates {
5290
- cpp ((e 1) ".has_value")
5541
+ cpp ((e 1) ".has_value()" )
5291
5542
  }
5292
5543
  }
5293
5544
  !null? cmdIsNotNull:boolean ( arg@(optional):double ) {
5294
5545
  templates {
5295
- cpp ((e 1) ".has_value")
5546
+ cpp ((e 1) ".has_value()" )
5296
5547
  }
5297
5548
  }
5298
5549
  !null? cmdIsNotNull:boolean ( arg@(optional):string ) {
5299
5550
  templates {
5300
- cpp ((e 1) ".empty() == false ")
5551
+ cpp ((e 1) ".has_value()")
5301
5552
  }
5302
5553
  }
5303
5554
 
@@ -5324,7 +5575,7 @@ std::vector<T> r_make_vector_from_array( const T (&data)[N] )
5324
5575
  scala ((e 1) ".isDefined")
5325
5576
  swift3 ((e 1) " != nil ")
5326
5577
  swift6 ((e 1) " != nil ")
5327
- cpp ((e 1) " != NULL ")
5578
+ cpp ((e 1) ".has_value()")
5328
5579
  java7 ((e 1) " != null ")
5329
5580
  csharp ("" (e 1) " != null")
5330
5581
  rust ((e 1) ".is_some()")
@@ -5347,7 +5598,8 @@ std::vector<T> r_make_vector_from_array( const T (&data)[N] )
5347
5598
  es6 ( "( e.toString())" )
5348
5599
  php ( "( $e->getMessage())" )
5349
5600
  java7 ( "( e.getMessage())" )
5350
- cpp ( " \"unspecified error\" " )
5601
+ ; the local the try template binds in its handler
5602
+ cpp ( "__rg_err_msg" )
5351
5603
  ; Dart has no `try` entry of its own and takes the `*` one, which
5352
5604
  ; binds `e`.
5353
5605
  dart ( "e.toString()" )
@@ -5372,6 +5624,17 @@ std::vector<T> r_make_vector_from_array( const T (&data)[N] )
5372
5624
  php ( "throw new Exception(" (e 1) ");")
5373
5625
  csharp ( "throw new ConfigurationErrorsException(" (e 1) ");" (imp "System.Configuration"))
5374
5626
  java7 ( "throw new IllegalArgumentException(" (e 1) ");")
5627
+ ; `throw "negative"` in C++ throws a `const char*`, which the
5628
+ ; catch-all below caught and could say nothing about — every
5629
+ ; error came out as "unspecified error". A std::exception
5630
+ ; carries the text, and the try template asks it for one.
5631
+ cpp ( "throw std::runtime_error(" (e 1) ");" (imp "<stdexcept>"))
5632
+ ; Kotlin has no checked exceptions and needs no `throws` on the
5633
+ ; signature, but what is thrown must be a Throwable — a String
5634
+ ; is not, and `throw "negative"` is a kotlinc error, not a
5635
+ ; warning. Exception(msg) puts the text where `e.message` reads
5636
+ ; it, which is what the `error_msg` template already expects.
5637
+ kotlin ( "throw Exception(" (e 1) ")")
5375
5638
  swift3 (
5376
5639
  nl "throw " (e 1) ";" nl
5377
5640
  (create_polyfill
@@ -5379,6 +5642,26 @@ std::vector<T> r_make_vector_from_array( const T (&data)[N] )
5379
5642
  extension String: Error {}
5380
5643
  "
5381
5644
  )
5645
+ )
5646
+ ; swift6 had no entry of its own and fell through to `*`, which
5647
+ ; writes `throw "negative"` — a String is not an Error, and
5648
+ ; swiftc refuses it. A small error type rather than
5649
+ ; `extension String: Error {}` (what swift3 does): extending a
5650
+ ; stdlib type with a stdlib protocol is a retroactive
5651
+ ; conformance, which Swift 6 language mode warns about.
5652
+ ; CustomStringConvertible is what keeps `error_msg` correct
5653
+ ; without a template of its own — `String(describing: error)`
5654
+ ; is the message, not `RgError(message: "…")`.
5655
+ swift6 (
5656
+ nl "throw RgError(message: " (e 1) ")" nl
5657
+ (create_polyfill
5658
+ "
5659
+ struct RgError: Error, CustomStringConvertible {
5660
+ let message: String
5661
+ var description: String { message }
5662
+ }
5663
+ "
5664
+ )
5382
5665
  )
5383
5666
  scala ( "throw new customException(" (e 1) ")"
5384
5667
 
@@ -5424,7 +5707,18 @@ case class customException(smth:String) extends Exception
5424
5707
  ; with swift there is no do without try...
5425
5708
  swift3 ( "do {" nl I (block 1) i nl "} catch { " nl I (block 2) i nl "}" )
5426
5709
  swift6 ( "do {" nl I (block 1) i nl "} catch { " nl I (block 2) i nl "}" )
5427
- cpp ( nl "try {" nl I (block 1) i nl "} catch( ... ) {" nl I (block 2) i nl "}" nl )
5710
+ ; The catch stays a catch-ALL — a Ranger `try` block has to
5711
+ ; survive whatever crosses it, including a throw from a
5712
+ ; polyfill or a library that is not a std::exception. Asking
5713
+ ; what it was is the rethrow idiom: re-throw inside the
5714
+ ; handler and catch it again by type. That gives `error_msg`
5715
+ ; the real text without narrowing what is caught, and without
5716
+ ; writing the user's catch block out twice.
5717
+ cpp ( nl "try {" nl I (block 1) i nl "} catch( ... ) {" nl I
5718
+ "std::string __rg_err_msg = \"unspecified error\";" nl
5719
+ "try { throw; } catch( const std::exception& __rg_e ) { __rg_err_msg = __rg_e.what(); } catch( ... ) {}" nl
5720
+ "(void)__rg_err_msg;" nl
5721
+ (block 2) i nl "}" nl (imp "<stdexcept>") (imp "<string>") )
5428
5722
  ; Python needs a statement in each suite, and a generated catch
5429
5723
  ; block is often empty (the @serialize writer emits one), so both
5430
5724
  ; suites open with `pass`.
@@ -5448,9 +5742,13 @@ case class customException(smth:String) extends Exception
5448
5742
  ; body wants one. swift_rc writes `_` for either name when its
5449
5743
  ; ref_cnt is 0 -- the swift3 template already did this for the
5450
5744
  ; index, and the item needs it just as much.
5451
- swift6 ( (forkctx _ ) (def 2) (def 3) nl "for (" (swift_rc 3) ", " (swift_rc 2) ") in " (e 1) ".enumerated() {" nl I (block 4) nl i "}" )
5452
- kotlin ( (forkctx _ ) (def 2) (def 3) "for ( " (e 3) " in " (e 1) ".indices ) {" nl I "val " (e 2) " = " (e 1) "[" (e 3) "]" nl (block 4) nl i "}" )
5453
- dart ( (forkctx _ ) (def 2) (def 3) "for ( int " (e 3) " = 0; " (e 3) " < " (e 1) ".length; " (e 3) "++) {" nl I "var " (e 2) " = " (e 1) "[" (e 3) "];" nl (block 4) nl i "}" )
5745
+ ; PLAN_CPP_IDIOMS C4: `for v in xs` when the body reads
5746
+ ; neither the index nor the collection.
5747
+ swift6 ( (forkctx _ ) (def 2) (def 3) nl (custom _) )
5748
+ ; PLAN_CPP_IDIOMS C4: `for (v in xs)` when the body reads
5749
+ ; neither the index nor the collection.
5750
+ kotlin ( (forkctx _ ) (def 2) (def 3) (custom _) )
5751
+ dart ( (forkctx _ ) (def 2) (def 3) (custom _) )
5454
5752
 
5455
5753
  ; Use index-based loop for Rust with cloned item - no reassignment needed as item is cloned.
5456
5754
  ; The index counts in i64, because Ranger declares it `int` and the
@@ -5462,7 +5760,12 @@ case class customException(smth:String) extends Exception
5462
5760
  ; borrow alive for the whole loop, and a body that borrows the
5463
5761
  ; same thing mutably cannot run. The temporary is named after
5464
5762
  ; the index, so nested loops do not collide.
5465
- rust ( (forkctx _ ) (def 2) (def 3) "let __n_" (e 3) " = (" (e 1) ".len() as i64);" nl "for " (e 3) " in 0..__n_" (e 3) " {" nl I "let mut " (e 2) " = " (e 1) "[" (e 3) " as usize]" (cloneif 2) ";" nl (block 4) nl i "}" )
5763
+ ; PLAN_RUST_SEMANTIC_IDIOMS K: the writer picks between the
5764
+ ; index loop this used to be and `for v in xs.iter().copied()`
5765
+ ; (or `.cloned()`), because only it can ask whether the body
5766
+ ; reads the index or touches the collection.
5767
+ ; See RustOperators.rustWriteForLoop.
5768
+ rust ( (forkctx _ ) (def 2) (def 3) nl (custom _) )
5466
5769
 
5467
5770
  ; idea of go for macro implementation, not working yet...
5468
5771
  go_idea @macro(true) (nl
@@ -5476,17 +5779,35 @@ case class customException(smth:String) extends Exception
5476
5779
  i
5477
5780
  "}" nl
5478
5781
  )
5479
- go ( (forkctx _ ) (def 3) "var " (e 3) " int64 = 0; " nl "for ; " (e 3) " < int64(len(" (e 1) ")) ; " (e 3) "++ {" nl I nl (go_for_bind 2) nl (block 4) nl i "}" )
5480
-
5481
- php ( (forkctx _ ) (def 2) (def 3) "for ( " (e 3) " = 0; " (e 3) " < count(" (e 1) "); " (e 3) "++) {" nl I (e 2) " = " (e 1) "[" (e 3) "];" nl (block 4) nl i "}" )
5482
- java7 ( (forkctx _ ) (def 2) (def 3) "for ( int " (e 3) " = 0; " (e 3) " < " (e 1) ".size(); " (e 3) "++) {" nl I (typeof 2) " " (e 2) " = " (e 1) ".get(" (e 3) ");" nl (block 4) nl i "}" )
5483
- csharp ( (forkctx _ ) (def 2) (def 3) "for ( int " (e 3) " = 0; " (e 3) " < " (e 1) ".Count; " (e 3) "++) {" nl I (typeof 2) " " (e 2) " = " (e 1) "[" (e 3) "];" nl (block 4) nl i "}" )
5782
+ ; PLAN_CPP_IDIOMS C4, the Go half: the writer picks between the
5783
+ ; index loop this used to be and `for _, v := range xs`, because
5784
+ ; only it can ask whether the body reads the index or touches
5785
+ ; the collection. See RangerGolangClassWriter.goWriteForLoop.
5786
+ go ( (forkctx _ ) (def 2) (def 3) (custom _) )
5787
+
5788
+ ; PLAN_CPP_IDIOMS C4, the PHP half: `foreach ($xs as $v)` when the
5789
+ ; body reads neither the index nor the collection.
5790
+ php ( (forkctx _ ) (def 2) (def 3) (custom _) )
5791
+ ; PLAN_CPP_IDIOMS C4: `for (T v : xs)` when the body reads
5792
+ ; neither the index nor the collection.
5793
+ java7 ( (forkctx _ ) (def 2) (def 3) (custom _) )
5794
+ csharp ( (forkctx _ ) (def 2) (def 3) (custom _) )
5484
5795
  scala ( (custom _) )
5485
- cpp ( (forkctx _ ) (def 2) (def 3) "for ( int " (e 3) " = 0; " (e 3) " != (int)(" (e 1) ".size()); " (e 3) "++) {" nl
5486
- I (typeof 2) " " (e 2) " = " (e 1) ".at(" (e 3) ");" nl (block 4) nl i "}" )
5796
+ ; PLAN_CPP_IDIOMS C4: the writer picks between the index loop
5797
+ ; this used to be and `for ( const T& v : xs )`, because only it
5798
+ ; can ask whether the body reads the index or touches the
5799
+ ; collection. See RangerCppClassWriter.cppWriteForLoop.
5800
+ cpp ( (forkctx _ ) (def 2) (def 3) (custom _) )
5487
5801
  cpp.old ( (forkctx _ ) (def 2) (def 3) "for ( std::vector< " (typeof 2) ">::size_type " (e 3) " = 0; " (e 3) " != " (e 1) ".size(); " (e 3) "++) {" nl
5488
5802
  I (typeof 2) " " (e 2) " = " (e 1) ".at(" (e 3) ");" nl (block 4) nl i "}" )
5489
- python ( (forkctx _ ) (def 2) (def 3) "for " (e 3) ", " (e 2) " in enumerate(" (e 1) "):" nl I (block 4) nl i )
5803
+ ; PLAN_CPP_IDIOMS C4: `for v in xs:` when the body never
5804
+ ; reads the index enumerate() was handing it.
5805
+ python ( (forkctx _ ) (def 2) (def 3) (custom _) )
5806
+ ; PLAN_CPP_IDIOMS C4: `for (const v of xs)` when the body reads
5807
+ ; neither the index nor the collection. es5 is left on the `*`
5808
+ ; template below -- `for...of` is ES6.
5809
+ es6 ( (forkctx _ ) (def 2) (def 3) (custom _) )
5810
+ ts ( (forkctx _ ) (def 2) (def 3) (custom _) )
5490
5811
  * ( (forkctx _ ) (def 2) (def 3) "for ( let " (e 3) " = 0; " (e 3) " < " (e 1) ".length; " (e 3) "++) {" nl I "var " (e 2) " = " (e 1) "[" (e 3) "];" nl (block 4) nl i "}" )
5491
5812
  }
5492
5813
  }
@@ -5502,7 +5823,7 @@ case class customException(smth:String) extends Exception
5502
5823
  llvm ( nl "for " (e 1) " " (e 2) ":" (typeof 2) " " (e 3) " {" nl I (block 4) nl i "}" )
5503
5824
  es6 ("for( var " (e 3) " in " (e 1) ") {" nl I "if(" (e 1) ".hasOwnProperty(" (e 3) ")) {"
5504
5825
  nl I "var " (e 2) " = " (e 1) "[" (e 3) "] " nl (block 4)
5505
- nl i "} }"
5826
+ nl i "}" nl i "}"
5506
5827
  )
5507
5828
  dart ( (forkctx _ ) (def 2) (def 3) "for ( var " (e 3) " in " (e 1) ".keys ) {" nl I "var " (e 2) " = " (e 1) "[" (e 3) "]!; " nl (block 4) nl i "}" )
5508
5829
  ; Go rejects a declared and unused loop variable, so both names
@@ -5771,9 +6092,10 @@ func r_last_index_of(_ s: String, _ needle: String) -> Int {
5771
6092
  )
5772
6093
  csharp ( (e 1) ".Length")
5773
6094
  php ( "strlen(" (e 1) ")")
5774
- go ( "int64(len([]rune(" (e 1) ")))")
6095
+ ; bytes, not runes -- see the note on `charAt`
6096
+ go ( "int64(len(" (e 1) "))")
5775
6097
  python ( "len(" (e 1) ")")
5776
- rust ( (e 1) ".chars().count() as i64")
6098
+ rust ( "(" (e 1) ".len() as i64)")
5777
6099
  * ( (e 1) ".length")
5778
6100
  }
5779
6101
  }
@@ -5904,24 +6226,113 @@ static bool r_regex_test_i(string pattern, string s) {
5904
6226
  ; swift:
5905
6227
  ; String(data: Data(bytes: s[sp ..< i]) , encoding: .utf8)!
5906
6228
 
6229
+ ; ===================================================================
6230
+ ; `charbuffer` IS A BUFFER OF BYTES.
6231
+ ; ===================================================================
6232
+ ;
6233
+ ; One element is one OCTET, 0..255. It is not a character, it is not
6234
+ ; a code point, and it is not "a string that happens to hold bytes".
6235
+ ; `Vec<u8>` on Rust, `[]byte` on Go, `Uint8Array` on JavaScript,
6236
+ ; `bytes` on Python, `byte[]` on the JVM and .NET, `[UInt8]` on
6237
+ ; Swift, `List<int>` on Dart. That is the whole of the type, and it
6238
+ ; is the same type whether the bytes came from a file, a socket, a
6239
+ ; JPEG or a piece of text.
6240
+ ;
6241
+ ; TEXT IS A SEPARATE THING, AND THE CONVERSION NAMES ITS ENCODING.
6242
+ ; `string` is text; `charbuffer` is bytes; nothing turns one into the
6243
+ ; other by itself. Three operators cross that line, and because a
6244
+ ; text<->bytes conversion cannot be done without choosing an
6245
+ ; encoding, all three are defined to use UTF-8:
6246
+ ;
6247
+ ; (to_charbuffer s) text -> its UTF-8 bytes
6248
+ ; (to_string buf) bytes -> the text they are the UTF-8 of
6249
+ ; (substring buf a b) bytes -> the text THAT RANGE is the UTF-8 of
6250
+ ;
6251
+ ; UTF-8 is a property of those three conversions. It is NOT a
6252
+ ; property of the buffer: a `charbuffer` holding a PNG is not
6253
+ ; "UTF-8 bytes", it is bytes, and `to_string` on it is meaningless.
6254
+ ;
6255
+ ; THE MISTAKE THIS COMMENT EXISTS TO STOP. `(charAt buf i)` answers
6256
+ ; ONE BYTE. A character outside ASCII is two, three or four of them,
6257
+ ; so a loop that copies a buffer back into text one element at a time
6258
+ ;
6259
+ ; while (i < (length buf)) { ; WRONG
6260
+ ; out = out + (substring buf i (i + 1))
6261
+ ; i = i + 1
6262
+ ; }
6263
+ ;
6264
+ ; decodes each byte of a multi-byte character on its own and gets a
6265
+ ; replacement character for each. That is a real defect this
6266
+ ; compiler shipped: `RangerLispParser` decoded escapes that way, and
6267
+ ; every literal holding both a backslash escape and a non-ASCII
6268
+ ; character came out broken. Walk the STRING when the subject is
6269
+ ; text (`strlen`, `charAt` and `substring` on a `string` agree with
6270
+ ; each other on any one target), and reach for a `charbuffer` when
6271
+ ; the subject really is bytes.
6272
+ ;
6273
+ ; WHY BYTES AND NOT THE HOST'S OWN UNIT. A `charbuffer` used to be
6274
+ ; whatever each host's string was made of -- UTF-16 units on
6275
+ ; JavaScript, Kotlin and Dart, code points on Python, bytes
6276
+ ; elsewhere, and on Scala a `toCharArray.map(_.toByte)` that
6277
+ ; silently truncated anything above U+00FF. A byte is the one unit
6278
+ ; every host can hold exactly: a UTF-16 target cannot hold a lone
6279
+ ; code point above the BMP in one unit, and a code-point target
6280
+ ; cannot hold a surrogate half. Every one of them can hold a byte.
6281
+ ; docs/plans/PLAN_STRING_INDEXING.md 2.3.
6282
+ ;
6283
+ ; THE VIEWS OF TEXT, for when a byte is not what is wanted:
6284
+ ;
6285
+ ; (to_chars s) [int], Unicode CODE POINTS, one per character,
6286
+ ; the same sequence on every target
6287
+ ; (char_length s) how many characters, without building that
6288
+ ; array
6289
+ ; (strlen s) / (charAt s i) / (substring s a b)
6290
+ ; the TARGET'S OWN unit -- a byte, a UTF-16 code
6291
+ ; unit or a code point, depending on where it
6292
+ ; runs. Right for a scan over ASCII structure,
6293
+ ; wrong for a column or a width. `-strict-strings`
6294
+ ; reports the sites where the difference shows.
6295
+ ; ===================================================================
6296
+
6297
+ ; The TEXT that this range of bytes is the UTF-8 of. One of the
6298
+ ; three places the line between bytes and text is crossed, so it
6299
+ ; decodes: a range that cuts a character in half gives a replacement
6300
+ ; character, which is what makes a one-byte-at-a-time copy of a
6301
+ ; buffer back into a string wrong. See the block above.
5907
6302
  substring cmdSubstring:string ( text:charbuffer start:int end:int ) {
5908
6303
  templates {
5909
6304
  ranger ( "(substring " (e 1) " " (e 2) " " (e 3) ")")
5910
6305
  csharp ( "System.Text.Encoding.UTF8.GetString(" (e 1) ", " (e 2) ", " (e 3) " - " (e 2) ")" )
5911
- scala ( "new String(" (e 1) ".slice(" (e 2) ", " (e 3) ").map(_.toChar) )")
5912
- java7 ( "new String(" (e 1) "," (e 2) ", " (e 3) " - " (e 2) " )")
6306
+ ; the bytes are UTF-8, so they are decoded as UTF-8 -- a
6307
+ ; byte-to-char cast reads them as Latin-1 and mangles
6308
+ ; everything above U+007F
6309
+ scala ( "new String(" (e 1) ".slice(" (e 2) ", " (e 3) "), \"UTF-8\")")
6310
+ java7 ( "new String(" (e 1) "," (e 2) ", " (e 3) " - " (e 2) ", java.nio.charset.StandardCharsets.UTF_8)")
5913
6311
  swift3 ( "String(data: Data(bytes:" (e 1) "[" (e 2) " ..< " (e 3) "]), encoding: .utf8)!" (imp "Foundation"))
5914
6312
  swift6 ( "String(data: Data(" (e 1) "[" (e 2) " ..< " (e 3) "]), encoding: .utf8)!" (imp "Foundation"))
5915
- kotlin ( "String(" (e 1) "," (e 2) ", " (e 3) " - " (e 2) " )")
6313
+ kotlin ( "String(" (e 1) "," (e 2) ", " (e 3) " - " (e 2) ", Charsets.UTF_8)")
5916
6314
  go ( "fmt.Sprintf(\"%s\", " (e 1) "[" (e 2) ":" (e 3) "])"
5917
6315
  (imp "fmt")
5918
6316
  )
5919
6317
  cpp ( "std::string( " (e 1) " + " (e 2) ", " (e 3) " - " (e 2) " )")
5920
- ; a charbuffer is List<int> on Dart, so the `*` fallback (the
5921
- ; JavaScript String.substring) does not apply to it
5922
- dart ( "String.fromCharCodes(" (e 1) ".sublist(" (e 2) ", " (e 3) "))" )
5923
- ; ...and a str on Python, where the slice is the substring
5924
- python ( (e 1) "[" (e 2) ":" (e 3) "]" )
6318
+ ; a charbuffer is a List<int> of BYTES on Dart, so the `*`
6319
+ ; fallback (the JavaScript String.substring) does not apply to
6320
+ ; it, and the bytes have to be decoded rather than read as
6321
+ ; code units
6322
+ dart ( (imp "dart:convert") "utf8.decode(" (e 1) ".sublist(" (e 2) ", " (e 3) "))" )
6323
+ ; ...and `bytes` on Python, where slicing gives bytes back
6324
+ python ( (e 1) "[" (e 2) ":" (e 3) "].decode(\"utf-8\")" )
6325
+ ; ...and a Uint8Array on JavaScript, where it is neither a
6326
+ ; string nor sliceable with String.substring
6327
+ es6 ( "r_cb_dec.decode(" (e 1) ".subarray(" (e 2) ", " (e 3) "))"
6328
+ (create_polyfill "
6329
+ // A charbuffer is a buffer of BYTES, so it is a Uint8Array here
6330
+ // rather than the string it used to be. One encoder and one decoder for the
6331
+ // process: constructing them per call is most of the cost of a short slice.
6332
+ const r_cb_enc = new TextEncoder();
6333
+ const r_cb_dec = new TextDecoder();
6334
+ ")
6335
+ )
5925
6336
  php ( "substr(" (e 1) ", " (e 2) ", " (e 3) " - " (e 2) ")")
5926
6337
  ; a charbuffer is Vec<u8> on Rust: slice the bytes and read
5927
6338
  ; them back as UTF-8. The `*` fallback is JavaScript's String
@@ -5931,40 +6342,345 @@ static bool r_regex_test_i(string pattern, string s) {
5931
6342
  }
5932
6343
  }
5933
6344
 
6345
+ ; The TEXT these bytes are the UTF-8 of -- the other direction of
6346
+ ; `to_charbuffer`. Bytes that are not valid UTF-8 come back as
6347
+ ; replacement characters; a buffer holding a PNG has no text in it
6348
+ ; and this operator has nothing to say about one.
5934
6349
  to_string bufferToString:string ( text:charbuffer ) {
5935
6350
  templates {
5936
6351
  ranger ( "(to_string " (e 1) ")")
5937
6352
  go ( "string(" (e 1) ")" )
5938
- dart ( "String.fromCharCodes(" (e 1) ")" )
6353
+ dart ( (imp "dart:convert") "utf8.decode(" (e 1) ")" )
5939
6354
  csharp ( "System.Text.Encoding.UTF8.GetString(" (e 1) ")" )
6355
+ python ( (e 1) ".decode(\"utf-8\")" )
6356
+ es6 ( "r_cb_dec.decode(" (e 1) ")"
6357
+ (create_polyfill "
6358
+ // A charbuffer is a buffer of BYTES, so it is a Uint8Array here
6359
+ // rather than the string it used to be. One encoder and one decoder for the
6360
+ // process: constructing them per call is most of the cost of a short slice.
6361
+ const r_cb_enc = new TextEncoder();
6362
+ const r_cb_dec = new TextDecoder();
6363
+ ")
6364
+ )
6365
+ rust ( "String::from_utf8_lossy(&" (e 1) ").to_string()" )
6366
+ java7 ( "new String(" (e 1) ", java.nio.charset.StandardCharsets.UTF_8)" )
6367
+ kotlin ( "String(" (e 1) ", Charsets.UTF_8)" )
6368
+ scala ( "new String(" (e 1) ", \"UTF-8\")" )
6369
+ swift3 ( "String(decoding: " (e 1) ", as: UTF8.self)" )
6370
+ swift6 ( "String(decoding: " (e 1) ", as: UTF8.self)" )
6371
+ ; cpp (const char*) and php (a byte string) already ARE the
6372
+ ; text; everything else has to decode it
5940
6373
  * ( (e 1) )
5941
6374
  }
5942
6375
  }
5943
6376
 
6377
+ ; ...and the same thing under the method-call spelling.
5944
6378
  toString bufferToString:string ( text:charbuffer ) {
5945
6379
  templates {
5946
6380
  go ( "string(" (e 1) ")" )
5947
- dart ( "String.fromCharCodes(" (e 1) ")" )
6381
+ dart ( (imp "dart:convert") "utf8.decode(" (e 1) ")" )
5948
6382
  csharp ( "System.Text.Encoding.UTF8.GetString(" (e 1) ")" )
6383
+ python ( (e 1) ".decode(\"utf-8\")" )
6384
+ es6 ( "r_cb_dec.decode(" (e 1) ")"
6385
+ (create_polyfill "
6386
+ // A charbuffer is a buffer of BYTES, so it is a Uint8Array here
6387
+ // rather than the string it used to be. One encoder and one decoder for the
6388
+ // process: constructing them per call is most of the cost of a short slice.
6389
+ const r_cb_enc = new TextEncoder();
6390
+ const r_cb_dec = new TextDecoder();
6391
+ ")
6392
+ )
6393
+ rust ( "String::from_utf8_lossy(&" (e 1) ").to_string()" )
6394
+ java7 ( "new String(" (e 1) ", java.nio.charset.StandardCharsets.UTF_8)" )
6395
+ kotlin ( "String(" (e 1) ", Charsets.UTF_8)" )
6396
+ scala ( "new String(" (e 1) ", \"UTF-8\")" )
6397
+ swift3 ( "String(decoding: " (e 1) ", as: UTF8.self)" )
6398
+ swift6 ( "String(decoding: " (e 1) ", as: UTF8.self)" )
6399
+ ; cpp (const char*) and php (a byte string) already ARE the
6400
+ ; text; everything else has to decode it
5949
6401
  * ( (e 1) )
5950
6402
  }
5951
6403
  }
5952
6404
 
6405
+ ; A `charbuffer` is a buffer of BYTES -- see the block above the
6406
+ ; charbuffer overloads for what that does and does not mean. This
6407
+ ; operator is one of the two places the line between text and bytes
6408
+ ; is crossed, so it is the place that names the encoding: the bytes
6409
+ ; are the UTF-8 of the text, on every target.
6410
+ ; docs/plans/PLAN_STRING_INDEXING.md 2.3.
6411
+ ; The portable indexable view of text: Unicode CODE POINTS, the same
6412
+ ; sequence on every target, built once in O(n) and read in O(1).
6413
+ ;
6414
+ ; `charAt` on a `string` is the target's own unit -- a UTF-16 code
6415
+ ; unit, a code point or a UTF-8 byte, depending on where it runs --
6416
+ ; and that is what makes it O(1) there. It is right for a scanner over
6417
+ ; ASCII structure and wrong for anything a human wrote. `to_chars` is
6418
+ ; the conversion that makes an index mean one thing everywhere, and
6419
+ ; because the program names it, the allocation is asked for rather
6420
+ ; than hidden.
6421
+ ;
6422
+ ; def cs:[int] (to_chars s)
6423
+ ; def n:int (array_length cs)
6424
+ ; def c:int (itemAt cs 0)
6425
+ ;
6426
+ ; Above the Basic Multilingual Plane this is the only view that agrees
6427
+ ; with itself: an emoji is ONE element here and two UTF-16 units or
6428
+ ; four UTF-8 bytes in the other views.
6429
+ ; docs/plans/PLAN_STRING_INDEXING.md 2.2.
6430
+ to_chars cmdToChars:[int] ( text:string ) {
6431
+ templates {
6432
+ ranger ( "(to_chars " (e 1) ")" )
6433
+ ; Array.from walks a string by code point, not by code unit,
6434
+ ; which is exactly the difference this operator exists for
6435
+ es6 ( "Array.from(" (e 1) ", (rg_c) => rg_c.codePointAt(0))" )
6436
+ ; a Python str IS a sequence of code points
6437
+ python ( "[ord(rg_c) for rg_c in " (e 1) "]" )
6438
+ ; a Dart String indexes UTF-16, but `runes` is the code points
6439
+ dart ( (e 1) ".runes.toList()" )
6440
+ ; a Rust char IS a code point
6441
+ rust ( (e 1) ".chars().map(|c| c as i64).collect::<Vec<i64>>()" )
6442
+ swift3 ( "Array(" (e 1) ".unicodeScalars.map { Int($0.value) })" )
6443
+ swift6 ( "Array(" (e 1) ".unicodeScalars.map { Int($0.value) })" )
6444
+ php ( "array_map('mb_ord', preg_split('//u', " (e 1) ", -1, PREG_SPLIT_NO_EMPTY))" )
6445
+ ; a Go rune IS a code point; the loop is only the int64 the
6446
+ ; Ranger `[int]` is written as
6447
+ go ( "r_to_chars(" (e 1) ")"
6448
+ (create_polyfill "
6449
+ func r_to_chars(s string) []int64 {
6450
+ rs := []rune(s)
6451
+ out := make([]int64, len(rs))
6452
+ for i, r := range rs {
6453
+ out[i] = int64(r)
6454
+ }
6455
+ return out
6456
+ }
6457
+ ")
6458
+ )
6459
+ ; the JVM and .NET store UTF-16, so a surrogate pair has to be
6460
+ ; folded back into the one code point it encodes
6461
+ java7 ( "rg_to_chars(" (e 1) ")"
6462
+ (create_polyfill "
6463
+ static java.util.ArrayList<Integer> rg_to_chars(String s) {
6464
+ java.util.ArrayList<Integer> out = new java.util.ArrayList<Integer>();
6465
+ int i = 0;
6466
+ while (i < s.length()) {
6467
+ int cp = s.codePointAt(i);
6468
+ out.add(cp);
6469
+ i += Character.charCount(cp);
6470
+ }
6471
+ return out;
6472
+ }
6473
+ ")
6474
+ )
6475
+ kotlin ( "rg_to_chars(" (e 1) ")"
6476
+ (create_polyfill "
6477
+ fun rg_to_chars(s: String): MutableList<Int> {
6478
+ val out = mutableListOf<Int>()
6479
+ var i = 0
6480
+ while (i < s.length) {
6481
+ val cp = s.codePointAt(i)
6482
+ out.add(cp)
6483
+ i += Character.charCount(cp)
6484
+ }
6485
+ return out
6486
+ }
6487
+ ")
6488
+ )
6489
+ csharp ( "RgChars.ToChars(" (e 1) ")"
6490
+ (polyfill "after_imports" "
6491
+ static class RgChars {
6492
+ public static System.Collections.Generic.List<int> ToChars(string s) {
6493
+ System.Collections.Generic.List<int> res = new System.Collections.Generic.List<int>();
6494
+ for (int i = 0; i < s.Length; i++) {
6495
+ if (char.IsHighSurrogate(s[i]) && (i + 1) < s.Length && char.IsLowSurrogate(s[i + 1])) {
6496
+ res.Add(char.ConvertToUtf32(s[i], s[i + 1]));
6497
+ i++;
6498
+ } else {
6499
+ res.Add((int)s[i]);
6500
+ }
6501
+ }
6502
+ return res;
6503
+ }
6504
+ }
6505
+ ")
6506
+ )
6507
+ scala ( "rg_to_chars(" (e 1) ")"
6508
+ (create_polyfill "
6509
+ def rg_to_chars(s: String): collection.mutable.ArrayBuffer[Int] = {
6510
+ val out = collection.mutable.ArrayBuffer[Int]()
6511
+ var i = 0
6512
+ while (i < s.length) {
6513
+ val cp = s.codePointAt(i)
6514
+ out += cp
6515
+ i += Character.charCount(cp)
6516
+ }
6517
+ out
6518
+ }
6519
+ ")
6520
+ )
6521
+ ; a std::string is UTF-8 bytes, so this is the decode
6522
+ cpp ( "r_to_chars(" (e 1) ")" (imp "<vector>") (imp "<string>")
6523
+ (create_polyfill "
6524
+ static std::vector<int> r_to_chars(const std::string & s) {
6525
+ std::vector<int> out;
6526
+ size_t i = 0;
6527
+ while (i < s.size()) {
6528
+ unsigned char c = (unsigned char)s[i];
6529
+ int cp = 0;
6530
+ size_t n = 1;
6531
+ if ((c & 0x80) == 0) { cp = c; n = 1; }
6532
+ else if ((c & 0xE0) == 0xC0) { cp = c & 0x1F; n = 2; }
6533
+ else if ((c & 0xF0) == 0xE0) { cp = c & 0x0F; n = 3; }
6534
+ else if ((c & 0xF8) == 0xF0) { cp = c & 0x07; n = 4; }
6535
+ else { out.push_back(0xFFFD); i += 1; continue; }
6536
+ if ((i + n) > s.size()) { out.push_back(0xFFFD); break; }
6537
+ bool bad = false;
6538
+ for (size_t k = 1; k < n; k++) {
6539
+ unsigned char cc = (unsigned char)s[i + k];
6540
+ if ((cc & 0xC0) != 0x80) { bad = true; break; }
6541
+ cp = (cp << 6) | (cc & 0x3F);
6542
+ }
6543
+ out.push_back(bad ? 0xFFFD : cp);
6544
+ i += n;
6545
+ }
6546
+ return out;
6547
+ }
6548
+ ")
6549
+ )
6550
+ }
6551
+ }
6552
+
6553
+ ; How many CHARACTERS are in this string -- the same number on every
6554
+ ; target, whatever unit that target happens to index in.
6555
+ ;
6556
+ ; `strlen` counts the target's own unit: UTF-16 code units on
6557
+ ; JavaScript, Java, Kotlin, C#, Scala, Dart and Swift 6, UTF-8 bytes
6558
+ ; on Rust, Go, C++ and PHP, code points on Python.
6559
+ ; That is the right number for a scan -- `while (i < (strlen s))` with
6560
+ ; `charAt s i` reads the same string either way -- and the wrong one
6561
+ ; for anything a person sees: a column, a width, a padding count, a
6562
+ ; truncation point. Those differ per target the moment the text stops
6563
+ ; being ASCII, and they differ silently.
6564
+ ;
6565
+ ; `char_length` is the count that does not: Unicode code points, the
6566
+ ; length of `(to_chars s)` without building the array.
6567
+ ;
6568
+ ; def w:int (char_length line) ; a column
6569
+ ; def n:int (strlen line) ; a scan bound
6570
+ ;
6571
+ ; For ASCII the two are equal, so the cost of choosing the honest one
6572
+ ; is nothing on the input that dominates.
6573
+ ; docs/plans/PLAN_STRING_INDEXING.md 3.3.
6574
+ char_length cmdCharLength:int ( text:string ) {
6575
+ templates {
6576
+ ranger ( "(char_length " (e 1) ")" )
6577
+ ; a Python str IS a sequence of code points
6578
+ python ( "len(" (e 1) ")" )
6579
+ ; a Rust char IS a code point
6580
+ rust ( "(" (e 1) ".chars().count() as i64)" )
6581
+ ; a Go rune IS a code point
6582
+ go ( "int64(utf8.RuneCountInString(" (e 1) "))" (imp "unicode/utf8") )
6583
+ dart ( (e 1) ".runes.length" )
6584
+ swift3 ( (e 1) ".unicodeScalars.count" )
6585
+ swift6 ( (e 1) ".unicodeScalars.count" )
6586
+ php ( "mb_strlen(" (e 1) ", 'UTF-8')" )
6587
+ ; the JVM stores UTF-16: codePointCount folds each surrogate
6588
+ ; pair back into the one code point it encodes. The helper is
6589
+ ; there so the argument is evaluated once
6590
+ java7 ( "rg_char_length(" (e 1) ")"
6591
+ (create_polyfill "
6592
+ static int rg_char_length(String s) {
6593
+ return s.codePointCount(0, s.length());
6594
+ }
6595
+ ")
6596
+ )
6597
+ kotlin ( "rg_char_length(" (e 1) ")"
6598
+ (create_polyfill "
6599
+ fun rg_char_length(s: String): Int {
6600
+ return s.codePointCount(0, s.length)
6601
+ }
6602
+ ")
6603
+ )
6604
+ scala ( "rg_char_length(" (e 1) ")"
6605
+ (create_polyfill "
6606
+ def rg_char_length(s: String): Int = s.codePointCount(0, s.length)
6607
+ ")
6608
+ )
6609
+ ; .NET is UTF-16 as well, but EnumerateRunes is too new for
6610
+ ; every runtime this targets -- counting everything that is
6611
+ ; not the low half of a pair is the same answer
6612
+ csharp ( "RgCharLength.Of(" (e 1) ")"
6613
+ (polyfill "after_imports" "
6614
+ static class RgCharLength {
6615
+ public static int Of(string s) {
6616
+ int n = 0;
6617
+ for (int i = 0; i < s.Length; i++) {
6618
+ if (char.IsLowSurrogate(s[i])) { continue; }
6619
+ n++;
6620
+ }
6621
+ return n;
6622
+ }
6623
+ }
6624
+ ")
6625
+ )
6626
+ ; a std::string is UTF-8 bytes: every byte that is not a
6627
+ ; continuation byte starts a new code point
6628
+ cpp ( "r_char_length(" (e 1) ")" (imp "<string>")
6629
+ (create_polyfill "
6630
+ static int r_char_length(const std::string & s) {
6631
+ int n = 0;
6632
+ for (size_t i = 0; i < s.size(); i++) {
6633
+ if (((unsigned char)s[i] & 0xC0) != 0x80) { n++; }
6634
+ }
6635
+ return n;
6636
+ }
6637
+ ")
6638
+ )
6639
+ ; a JavaScript string is UTF-16: skip the low half of every
6640
+ ; surrogate pair. Array.from(s).length is the same number and
6641
+ ; allocates the array this exists to avoid
6642
+ es6 ( "r_char_length(" (e 1) ")"
6643
+ (create_polyfill "
6644
+ function r_char_length(s) {
6645
+ let n = 0;
6646
+ for (let i = 0; i < s.length; i++) {
6647
+ const c = s.charCodeAt(i);
6648
+ if (c >= 0xDC00 && c <= 0xDFFF) { continue; }
6649
+ n++;
6650
+ }
6651
+ return n;
6652
+ }
6653
+ ")
6654
+ )
6655
+ }
6656
+ }
6657
+
5953
6658
  to_charbuffer cmdToCharBuffer:charbuffer ( text:string ) {
5954
6659
  templates {
5955
6660
  ranger ( "(to_charbuffer " (e 1) ")")
5956
6661
  swift3 ( "Array(" (e 1) ".utf8)" )
5957
- scala ( (e 1) ".toCharArray.map(_.toByte)")
5958
- java7 ( (e 1) ".getBytes()" )
6662
+ swift6 ( "Array(" (e 1) ".utf8)" )
6663
+ scala ( (e 1) ".getBytes(\"UTF-8\")")
6664
+ java7 ( (e 1) ".getBytes(java.nio.charset.StandardCharsets.UTF_8)" )
5959
6665
  csharp ( "System.Text.Encoding.UTF8.GetBytes(" (e 1) ")" )
5960
- kotlin ( (e 1 ) ".toCharArray()" )
6666
+ kotlin ( (e 1 ) ".toByteArray(Charsets.UTF_8)" )
5961
6667
  ; as_bytes/to_vec, not into_bytes: the source may be a
5962
6668
  ; borrowed &str, and into_bytes consumes an owned String
5963
6669
  rust ( (e 1) ".as_bytes().to_vec()")
5964
6670
  cpp ( (e 1) ".c_str()")
5965
- dart ( (e 1) ".codeUnits" )
6671
+ dart ( (imp "dart:convert") "utf8.encode(" (e 1) ")" )
5966
6672
  php ( (e 1) )
5967
6673
  go("[]byte(" (e 1) ")")
6674
+ python ( (e 1) ".encode(\"utf-8\")" )
6675
+ es6 ( "r_cb_enc.encode(" (e 1) ")"
6676
+ (create_polyfill "
6677
+ // A charbuffer is a buffer of BYTES, so it is a Uint8Array here
6678
+ // rather than the string it used to be. One encoder and one decoder for the
6679
+ // process: constructing them per call is most of the cost of a short slice.
6680
+ const r_cb_enc = new TextEncoder();
6681
+ const r_cb_dec = new TextDecoder();
6682
+ ")
6683
+ )
5968
6684
  * ( (e 1) )
5969
6685
  }
5970
6686
  }
@@ -5982,7 +6698,7 @@ static bool r_regex_test_i(string pattern, string s) {
5982
6698
  templates {
5983
6699
  llvm ( "(buffer_alloc " (e 1) ")" )
5984
6700
  ranger ( "(buffer_alloc " (e 1) ")" )
5985
- es6 ( "(function(){ var b = new ArrayBuffer(" (e 1) "); b._view = new DataView(b); return b; })()" )
6701
+ es6 ( "(function(b){ return Object.assign(b, { _view: new DataView(b) }); })(new ArrayBuffer(" (e 1) "))" )
5986
6702
  go ( "make([]byte, " (e 1) ")" )
5987
6703
  rust ( "vec![0u8; " (idx 1) "]" )
5988
6704
  cpp ( "std::vector<uint8_t>(" (e 1) ", 0)" (imp "<vector>") (imp "<cstdint>") )
@@ -6091,7 +6807,7 @@ function r_buf_slice($buf, $start, $end) {
6091
6807
  buffer_from_string cmdBufferFromString:buffer ( str:string ) {
6092
6808
  templates {
6093
6809
  ranger ( "(buffer_from_string " (e 1) ")" )
6094
- es6 ( "(function(s){ var b = new ArrayBuffer(s.length); var v = new Uint8Array(b); for(var i=0;i<s.length;i++)v[i]=s.charCodeAt(i); b._view = new DataView(b); return b; })(" (e 1) ")" )
6810
+ es6 ( "(function(s){ var b = new ArrayBuffer(s.length); var v = new Uint8Array(b); for(var i=0;i<s.length;i++)v[i]=s.charCodeAt(i); return Object.assign(b, { _view: new DataView(b) }); })(" (e 1) ")" )
6095
6811
  go ( "[]byte(" (e 1) ")" )
6096
6812
  rust ( (e 1) ".as_bytes().to_vec()" )
6097
6813
  cpp ( "std::vector<uint8_t>(" (e 1) ".begin(), " (e 1) ".end())" )
@@ -6193,7 +6909,7 @@ std::string r_buffer_to_base64(const std::vector<uint8_t>& data) {
6193
6909
  templates {
6194
6910
  llvm ( "(buffer_write_file " (e 1) " " (e 2) " " (e 3) ")" )
6195
6911
  ranger ( "(buffer_write_file " (e 1) " " (e 2) " " (e 3) ")" )
6196
- es6 ( "require('fs').writeFileSync(" (e 1) " + '/' + " (e 2) ", Buffer.from(" (e 3) "))" )
6912
+ es6 ( "require('fs').writeFileSync(require('path').join(" (e 1) ", " (e 2) "), Buffer.from(" (e 3) "))" )
6197
6913
  go ( "os.WriteFile(" (e 1) " + \"/\" + " (e 2) ", " (e 3) ", 0644)" (imp "os") )
6198
6914
  rust ( "std::fs::write(format!(\"{}/{}\", " (e 1) ", " (e 2) "), &" (e 3) ").unwrap()" )
6199
6915
  cpp ( "r_buffer_write_file(" (e 1) ", " (e 2) ", " (e 3) ")" (imp "<fstream>") (imp "<vector>")
@@ -6216,7 +6932,7 @@ static void r_buffer_write_file(String path, String filename, byte[] data) {
6216
6932
  )
6217
6933
  python ( "open(" (e 1) " + '/' + " (e 2) ", 'wb').write(" (e 3) ")" )
6218
6934
  swift6 ( "try? Data(" (e 3) ").write(to: URL(fileURLWithPath: " (e 1) " + \"/\" + " (e 2) "))" (imp "Foundation") )
6219
- csharp ( "System.IO.File.WriteAllBytes(" (e 1) " + \"/\" + " (e 2) ", " (e 3) ")" )
6935
+ csharp ( "System.IO.File.WriteAllBytes(" (e 1) " + \"/\" + " (e 2) ", " (e 3) ");" )
6220
6936
  kotlin ( "java.io.File(" (e 1) " + \"/\" + " (e 2) ").writeBytes(" (e 3) ")" )
6221
6937
  dart ( (imp "dart:io") "File((" (e 1) ") + \"/\" + (" (e 2) ")).writeAsBytesSync(" (e 3) ")" )
6222
6938
  php ( "file_put_contents(" (e 1) " . '/' . " (e 2) ", " (e 3) "->d);" )
@@ -6228,7 +6944,7 @@ static void r_buffer_write_file(String path, String filename, byte[] data) {
6228
6944
  templates {
6229
6945
  llvm ( "(buffer_read_file " (e 1) " " (e 2) ")" )
6230
6946
  ranger ( "(buffer_read_file " (e 1) " " (e 2) ")" )
6231
- es6 ( "(function(){ var b = require('fs').readFileSync(" (e 1) " + '/' + " (e 2) "); var ab = new ArrayBuffer(b.length); var v = new Uint8Array(ab); for(var i=0;i<b.length;i++)v[i]=b[i]; ab._view = new DataView(ab); return ab; })()" )
6947
+ es6 ( "(function(){ var b = require('fs').readFileSync( require('path').join(" (e 1) ", " (e 2) ") ); var ab = new ArrayBuffer(b.length); var v = new Uint8Array(ab); for(var i=0;i<b.length;i++)v[i]=b[i]; return Object.assign(ab, { _view: new DataView(ab) }); })()" )
6232
6948
  go ( "func() []byte { d, _ := os.ReadFile(filepath.Join(" (e 1) ", " (e 2) ")); return d }()" (imp "os") (imp "path/filepath") )
6233
6949
  rust ( "std::fs::read(format!(\"{}/{}\", " (e 1) ", " (e 2) ")).unwrap_or_default()" )
6234
6950
  cpp ( "r_buffer_read_file(" (e 1) ", " (e 2) ")" (imp "<fstream>") (imp "<vector>") (imp "<iterator>")
@@ -6271,7 +6987,7 @@ static byte[] r_buffer_read_file(String path, String filename) {
6271
6987
  java7 ( "System.arraycopy(" (e 3) ", (int)" (e 4) ", " (e 1) ", (int)" (e 2) ", (int)" (e 5) ");" )
6272
6988
  python ( (e 1) "[" (e 2) ":" (e 2) "+" (e 5) "] = " (e 3) "[" (e 4) ":" (e 4) "+" (e 5) "]" )
6273
6989
  swift6 ( (e 1) ".replaceSubrange(Int(" (e 2) ")..<Int(" (e 2) "+" (e 5) "), with: " (e 3) "[Int(" (e 4) ")..<Int(" (e 4) "+" (e 5) ")])" )
6274
- csharp ( "Array.Copy(" (e 3) ", (int)" (e 4) ", " (e 1) ", (int)" (e 2) ", (int)" (e 5) ")" )
6990
+ csharp ( "Array.Copy(" (e 3) ", (int)" (e 4) ", " (e 1) ", (int)" (e 2) ", (int)" (e 5) ");" )
6275
6991
  kotlin ( "System.arraycopy(" (e 3) ", " (e 4) ", " (e 1) ", " (e 2) ", " (e 5) ")" )
6276
6992
  php ( "r_buf_copy(" (e 1) ", " (e 2) ", " (e 3) ", " (e 4) ", " (e 5) ");" )
6277
6993
  }
@@ -6299,7 +7015,7 @@ static byte[] r_buffer_read_file(String path, String filename) {
6299
7015
  buffer_slice cmdBufferSlice:buffer ( buf:buffer start:int end:int ) {
6300
7016
  templates {
6301
7017
  ranger ( "(buffer_slice " (e 1) " " (e 2) " " (e 3) ")" )
6302
- es6 ( "(function(b,s,e){ var slice = b.slice(s,e); slice._view = new DataView(slice); return slice; })(" (e 1) "," (e 2) "," (e 3) ")" )
7018
+ es6 ( "(function(b,s,e){ var slice = b.slice(s,e); return Object.assign(slice, { _view: new DataView(slice) }); })(" (e 1) "," (e 2) "," (e 3) ")" )
6303
7019
  dart ( (e 1) ".sublist(" (e 2) ", " (e 3) ")" )
6304
7020
  go ( "append([]byte{}, " (e 1) "[" (e 2) ":" (e 3) "]...)" )
6305
7021
  rust ( (e 1) "[" (idx 2) ".." (idx 3) "].to_vec()" )
@@ -6423,7 +7139,7 @@ static byte[] r_buffer_read_file(String path, String filename) {
6423
7139
  java7 ( "System.arraycopy(" (e 3) ", (int)" (e 4) ", " (e 1) ", (int)" (e 2) ", (int)" (e 5) ")" )
6424
7140
  python ( (e 1) "[" (e 2) ":" (e 2) "+" (e 5) "] = " (e 3) "[" (e 4) ":" (e 4) "+" (e 5) "]" )
6425
7141
  swift6 ( (e 1) ".replaceSubrange(Int(" (e 2) ")..<Int(" (e 2) "+" (e 5) "), with: " (e 3) "[Int(" (e 4) ")..<Int(" (e 4) "+" (e 5) ")])" )
6426
- csharp ( "Array.Copy(" (e 3) ", (int)" (e 4) ", " (e 1) ", (int)" (e 2) ", (int)" (e 5) ")" )
7142
+ csharp ( "Array.Copy(" (e 3) ", (int)" (e 4) ", " (e 1) ", (int)" (e 2) ", (int)" (e 5) ");" )
6427
7143
  kotlin ( "System.arraycopy(" (e 3) ", " (e 4) ", " (e 1) ", " (e 2) ", " (e 5) ")" )
6428
7144
  }
6429
7145
  }
@@ -6550,7 +7266,7 @@ static byte[] r_buffer_read_file(String path, String filename) {
6550
7266
  java7 ( "System.arraycopy(" (e 3) ", (int)" (e 4) ", " (e 1) ", (int)" (e 2) ", (int)" (e 5) ")" )
6551
7267
  python ( (e 1) "[" (e 2) ":" (e 2) "+" (e 5) "] = " (e 3) "[" (e 4) ":" (e 4) "+" (e 5) "]" )
6552
7268
  swift6 ( (e 1) ".replaceSubrange(Int(" (e 2) ")..<Int(" (e 2) "+" (e 5) "), with: " (e 3) "[Int(" (e 4) ")..<Int(" (e 4) "+" (e 5) ")])" )
6553
- csharp ( "Array.Copy(" (e 3) ", (int)" (e 4) ", " (e 1) ", (int)" (e 2) ", (int)" (e 5) ")" )
7269
+ csharp ( "Array.Copy(" (e 3) ", (int)" (e 4) ", " (e 1) ", (int)" (e 2) ", (int)" (e 5) ");" )
6554
7270
  kotlin ( "System.arraycopy(" (e 3) ", " (e 4) ", " (e 1) ", " (e 2) ", " (e 5) ")" )
6555
7271
  }
6556
7272
  }
@@ -6613,6 +7329,9 @@ static byte[] r_buffer_read_file(String path, String filename) {
6613
7329
  }
6614
7330
 
6615
7331
  ; Length
7332
+ ; HOW MANY BYTES. For text that is not ASCII this is more than the
7333
+ ; number of characters -- `char_length` is that one. See the block
7334
+ ; above the charbuffer overloads.
6616
7335
  length cmdLength:int ( buffer:charbuffer ) {
6617
7336
  templates {
6618
7337
  ranger ( "(length " (e 1) ")")
@@ -6624,9 +7343,10 @@ static byte[] r_buffer_read_file(String path, String filename) {
6624
7343
  java7 ( (e 1 ) ".length" )
6625
7344
  cpp( "strlen( " (e 1) " )" (imp "<cstring>"))
6626
7345
  go ( "int64(len(" (e 1 ) "))")
6627
- ; to_charbuffer passes the string through on Python, so a
6628
- ; charbuffer IS a str there -- the `*` fallback is the
6629
- ; JavaScript `.length` property
7346
+ ; how many BYTES. A Python charbuffer is `bytes`, so `len`
7347
+ ; counts octets, not characters -- the same number every
7348
+ ; other arm here answers. The `*` fallback is the JavaScript
7349
+ ; `.length` property of a Uint8Array
6630
7350
  python ( "len(" (e 1) ")" )
6631
7351
  php ( "strlen(" (e 1 ) ")")
6632
7352
  * ( (e 1) ".length" )
@@ -6828,7 +7548,22 @@ func r_last_index_of(_ s: String, _ needle: String) -> Int {
6828
7548
  kotlin ( (e 1) "[" (e 2) "].code")
6829
7549
  dart ( (e 1) ".codeUnitAt(" (e 2) ")" )
6830
7550
  scala ( (e 1) "(" (e 2) ")")
6831
- go ( "int64([]rune(" (e 1) ")[" (e 2) "])")
7551
+ ; A Ranger string index is the target's own unit, and on Go
7552
+ ; that unit is the UTF-8 byte -- what a Go string is made of,
7553
+ ; and the only one it indexes in constant time. It was the
7554
+ ; rune, and `[]rune(s)` allocates and copies the WHOLE string
7555
+ ; on every read, so a scan was quadratic with a large
7556
+ ; constant: 60 000 characters did not finish inside two
7557
+ ; minutes where C++ took 5 ms.
7558
+ ; It also disagreed with Go's own other string operators.
7559
+ ; `indexOf` is strings.Index and answers a BYTE offset, so a
7560
+ ; scanner that found a delimiter and sliced at it sliced in
7561
+ ; the wrong place as soon as anything non-ASCII stood before
7562
+ ; it: for "<a-umlaut>,b" the head came back as "<a-umlaut>,"
7563
+ ; and the tail as "" -- the rest of the text, dropped without
7564
+ ; a word. `charcode` read []byte(s)[0] all along.
7565
+ ; docs/plans/PLAN_STRING_INDEXING.md 2.4.
7566
+ go ( "int64(" (e 1) "[" (e 2) "])")
6832
7567
  swift3 ( "Int( ( NSString(string: " (e 1) " ) ).character( at: " (e 2) " ) )")
6833
7568
  swift3 ( "Int( String( " (e 1) ".characters[" (e 1) ".index(" (e 1) ".startIndex, offsetBy: " (e 2) ")]))!")
6834
7569
  ; Also a named helper, and for the same reason: the inline form
@@ -6927,7 +7662,55 @@ func r_last_index_of(_ s: String, _ needle: String) -> Int {
6927
7662
  ")
6928
7663
  )
6929
7664
  python ( "ord(" (e 1) "[" (e 2) "])")
6930
- rust ( (e 1) ".chars().nth(" (idx 2) ").unwrap_or('\\0') as i64")
7665
+ rust ( "rg_char_at(&" (e 1) ", " (e 2) ")"
7666
+ (create_polyfill "
7667
+ // A Ranger string index is the TARGET'S OWN unit, and on Rust that unit is
7668
+ // the UTF-8 byte -- what a String is actually made of, and the only one it
7669
+ // indexes in constant time. It used to be the char: strlen counted chars(),
7670
+ // charAt was chars().nth(i) and substring was chars().skip().take(), so the
7671
+ // ordinary `while (i < (strlen s)) { charAt s i }` scan walked the string
7672
+ // once per character and was quadratic -- 120 000 characters took 8.8 s
7673
+ // where C++ took 11 ms. It also disagreed with charcode, which has read
7674
+ // as_bytes()[0] all along.
7675
+ //
7676
+ // Both helpers are TOTAL, because the forms they replace were: an index past
7677
+ // the end answered a zero rather than panicking, and scanners here rely on
7678
+ // reading one past the last character.
7679
+ fn rg_char_at(s: &str, at: i64) -> i64 {
7680
+ let b = s.as_bytes();
7681
+ if at < 0 { return 0; }
7682
+ let i = at as usize;
7683
+ if i >= b.len() { return 0; }
7684
+ b[i] as i64
7685
+ }
7686
+
7687
+ // A byte slice of a String has to be valid UTF-8, and a slice that cuts a
7688
+ // multi byte character is not. Measured over the compiler compiling itself
7689
+ // and the markdown gallery -- 78 012 slices -- not one cut a character:
7690
+ // a scanner slices at a delimiter it found, every delimiter here is ASCII,
7691
+ // and an ASCII byte is always a character boundary in UTF-8. So
7692
+ // from_utf8_lossy is the backstop for what the measurement did not cover,
7693
+ // not the expected path. docs/plans/PLAN_STRING_INDEXING.md 4.1.
7694
+ fn rg_substring(s: &str, from: i64, to: i64) -> String {
7695
+ let b = s.as_bytes();
7696
+ let n = b.len() as i64;
7697
+ let mut a = if from < 0 { 0 } else { from };
7698
+ if a > n { a = n; }
7699
+ let mut e = if to > n { n } else { to };
7700
+ if e < a { e = a; }
7701
+ // A Rust String cannot hold half a character, so a slice that cuts one
7702
+ // has to be rounded to a boundary rather than returned as bytes -- which
7703
+ // is what C++, PHP and Go can do and Rust cannot. The rule is Swift's,
7704
+ // for the same reason (see r_substring): the unit that STARTS a character
7705
+ // carries the whole of it and the units inside it carry nothing, so a
7706
+ // walk that copies one unit at a time still reproduces the text.
7707
+ while a < n && (b[a as usize] & 0xC0) == 0x80 { a += 1; }
7708
+ if e < a { e = a; }
7709
+ while e < n && (b[e as usize] & 0xC0) == 0x80 { e += 1; }
7710
+ String::from_utf8_lossy(&b[a as usize..e as usize]).to_string()
7711
+ }
7712
+ ")
7713
+ )
6931
7714
  * ( (e 1) ".charCodeAt(" (e 2) " )")
6932
7715
  }
6933
7716
  }
@@ -6974,6 +7757,11 @@ std::vector<std::string> r_get_keys_of_map( const M & orig_map ) {
6974
7757
  }
6975
7758
  }
6976
7759
 
7760
+ ; ONE BYTE, 0..255, at that index. Not one character: a character
7761
+ ; outside ASCII is two, three or four of these, so this is the
7762
+ ; operator to reach for when the subject really is bytes and the
7763
+ ; wrong one for walking text. See the block above the charbuffer
7764
+ ; overloads.
6977
7765
  charAt cmdCharAt:char ( text:charbuffer position:int ) {
6978
7766
  templates {
6979
7767
  ranger ( "(charAt " (e 1) " " ( e 2 ) ")")
@@ -6983,14 +7771,18 @@ std::vector<std::string> r_get_keys_of_map( const M & orig_map ) {
6983
7771
  ; a charbuffer is List<int> on Dart: index it, do not call
6984
7772
  ; codeUnitAt (which is the String overload's entry)
6985
7773
  dart ( (e 1) "[" (e 2) "]" )
6986
- python ( "ord(" (e 1) "[" (e 2) "])" )
7774
+ ; `bytes` on Python: indexing one already gives the int
7775
+ python ( (e 1) "[" (e 2) "]" )
7776
+ ; a Uint8Array on JavaScript, so index it rather than falling
7777
+ ; through to the String.charCodeAt below
7778
+ es6 ( (e 1) "[" (e 2) "]" )
6987
7779
  csharp ( (e 1) "[" (e 2) "]")
6988
7780
  php ( "ord(" (e 1) "[" (e 2) "])")
6989
- java7 ( (e 1) "[" (e 2) "]")
6990
- ; .code: a charbuffer is a CharArray on Kotlin, and `char` is an
6991
- ; integer code unit there as it is on every other target
6992
- kotlin ( (e 1) "[" (e 2) "].code")
6993
- scala ( (e 1) "(" (e 2) ")")
7781
+ ; a byte is SIGNED on the JVM, so & 0xFF is what makes the
7782
+ ; answer the 0..255 the operator is declared to return
7783
+ java7 ( "((" (e 1) "[(int)" (e 2) "]) & 0xFF)")
7784
+ kotlin ( "(" (e 1) "[(" (e 2) ").toInt()].toInt() and 0xFF)")
7785
+ scala ( "(" (e 1) "(" (e 2) ".toInt) & 0xFF)")
6994
7786
  go ( (e 1) "[" (e 2) "]")
6995
7787
  swift3 ( (e 1) "[" (e 2) "]")
6996
7788
  ; Vec<u8> on Rust, so index the byte; `char` is an integer
@@ -7024,7 +7816,10 @@ std::vector<std::string> r_get_keys_of_map( const M & orig_map ) {
7024
7816
  cpp ( '' (e 1) '.substr(' (e 2) ', ' (e 3) ' - ' (e 2) ')' )
7025
7817
  csharp ( (e 1) ".Substring(" (e 2) ", " (e 3) " - " (e 2) " )")
7026
7818
  php ( "substr(" (e 1) ", " (e 2) ", " (e 3) " - " (e 2) ")")
7027
- go ( "string([]rune(" (e 1) ")[" (e 2) ":" (e 3) "])")
7819
+ ; bytes, like strlen and charAt above and like C++ below.
7820
+ ; Slicing bytes still round-trips UTF-8 -- the pieces of a
7821
+ ; multi byte character concatenate back into the character.
7822
+ go ( (e 1) "[" (e 2) ":" (e 3) "]")
7028
7823
  swift3 ( (e 1) "[" (e 1) ".index(" (e 1) ".startIndex, offsetBy:" (e 2) ")..<" (e 1) ".index(" (e 1) ".startIndex, offsetBy:" (e 3) ")]" )
7029
7824
  swift6 ( "r_substring(" (e 1) ", " (e 2) ", " (e 3) ")"
7030
7825
  (create_polyfill "
@@ -7118,7 +7913,58 @@ func r_last_index_of(_ s: String, _ needle: String) -> Int {
7118
7913
  ")
7119
7914
  )
7120
7915
  python ( (e 1) "[" (e 2) ":" (e 3) "]" )
7121
- rust ( (custom _) )
7916
+ ; rg_substring, the byte slice. The polyfill is the same
7917
+ ; text as the one on `charAt` above, so whichever operator a
7918
+ ; program uses first writes it and the other is deduped.
7919
+ rust ( "rg_substring(&" (e 1) ", " (e 2) ", " (e 3) ")"
7920
+ (create_polyfill "
7921
+ // A Ranger string index is the TARGET'S OWN unit, and on Rust that unit is
7922
+ // the UTF-8 byte -- what a String is actually made of, and the only one it
7923
+ // indexes in constant time. It used to be the char: strlen counted chars(),
7924
+ // charAt was chars().nth(i) and substring was chars().skip().take(), so the
7925
+ // ordinary `while (i < (strlen s)) { charAt s i }` scan walked the string
7926
+ // once per character and was quadratic -- 120 000 characters took 8.8 s
7927
+ // where C++ took 11 ms. It also disagreed with charcode, which has read
7928
+ // as_bytes()[0] all along.
7929
+ //
7930
+ // Both helpers are TOTAL, because the forms they replace were: an index past
7931
+ // the end answered a zero rather than panicking, and scanners here rely on
7932
+ // reading one past the last character.
7933
+ fn rg_char_at(s: &str, at: i64) -> i64 {
7934
+ let b = s.as_bytes();
7935
+ if at < 0 { return 0; }
7936
+ let i = at as usize;
7937
+ if i >= b.len() { return 0; }
7938
+ b[i] as i64
7939
+ }
7940
+
7941
+ // A byte slice of a String has to be valid UTF-8, and a slice that cuts a
7942
+ // multi byte character is not. Measured over the compiler compiling itself
7943
+ // and the markdown gallery -- 78 012 slices -- not one cut a character:
7944
+ // a scanner slices at a delimiter it found, every delimiter here is ASCII,
7945
+ // and an ASCII byte is always a character boundary in UTF-8. So
7946
+ // from_utf8_lossy is the backstop for what the measurement did not cover,
7947
+ // not the expected path. docs/plans/PLAN_STRING_INDEXING.md 4.1.
7948
+ fn rg_substring(s: &str, from: i64, to: i64) -> String {
7949
+ let b = s.as_bytes();
7950
+ let n = b.len() as i64;
7951
+ let mut a = if from < 0 { 0 } else { from };
7952
+ if a > n { a = n; }
7953
+ let mut e = if to > n { n } else { to };
7954
+ if e < a { e = a; }
7955
+ // A Rust String cannot hold half a character, so a slice that cuts one
7956
+ // has to be rounded to a boundary rather than returned as bytes -- which
7957
+ // is what C++, PHP and Go can do and Rust cannot. The rule is Swift's,
7958
+ // for the same reason (see r_substring): the unit that STARTS a character
7959
+ // carries the whole of it and the units inside it carry nothing, so a
7960
+ // walk that copies one unit at a time still reproduces the text.
7961
+ while a < n && (b[a as usize] & 0xC0) == 0x80 { a += 1; }
7962
+ if e < a { e = a; }
7963
+ while e < n && (b[e as usize] & 0xC0) == 0x80 { e += 1; }
7964
+ String::from_utf8_lossy(&b[a as usize..e as usize]).to_string()
7965
+ }
7966
+ ")
7967
+ )
7122
7968
  * ( (e 1) ".substring(" (e 2) ", " (e 3) " )")
7123
7969
  }
7124
7970
  }
@@ -7311,7 +8157,12 @@ func r_last_index_of(_ s: String, _ needle: String) -> Int {
7311
8157
  cpp( "((unsigned char)" (e 1) ".at(0))")
7312
8158
  dart ( (e 1) ".codeUnitAt(0)" )
7313
8159
  php ( "ord(" (e 1) ")")
7314
- java7 ( "((" (e 1) ".getBytes())[0])")
8160
+ ; charAt, not getBytes()[0]. A Ranger char is an int on
8161
+ ; Java and a string index is a UTF-16 code unit here, so
8162
+ ; `charcode` has to answer what `charAt s 0` answers. The byte
8163
+ ; form answered neither: a JVM byte is SIGNED, so the first
8164
+ ; byte of "<a-umlaut>" came back as -61 where charAt says 228.
8165
+ java7 ( "((int)" (e 1) ".charAt(0))")
7315
8166
  swift3 ( "UInt8( ( NSString(string: " (e 1) " ) ).character( at: 0 ) )" (imp "Foundation"))
7316
8167
  swift3 ( "UInt8( String( " (e 1) ".characters[" (e 1) ".startIndex]))! ")
7317
8168
  swift6 ( "r_char_at(" (e 1) ", 0)"
@@ -7424,7 +8275,10 @@ func r_last_index_of(_ s: String, _ needle: String) -> Int {
7424
8275
  templates {
7425
8276
  ranger ( "(strfromcode " (e 1) ")")
7426
8277
  csharp ( "((char)" (e 1) ").ToString()")
7427
- java7 ( "(new String( new char[] {" (e 1) " }))")
8278
+ ; the cast is not decoration: a Ranger `char` is a Java
8279
+ ; `byte`, and a byte in a char[] initialiser is a narrowing
8280
+ ; conversion javac refuses
8281
+ java7 ( "(new String( new char[] {(char)(" (e 1) ") }))")
7428
8282
  kotlin ( (e 1) ".toChar().toString()")
7429
8283
  swift3 ( "(String( Character( UnicodeScalar(" (e 1) " ) )))")
7430
8284
  ; A named helper, not an inline expression. UnicodeScalar has
@@ -7709,32 +8563,10 @@ inline std::string r_double_to_string(double v) {
7709
8563
  ranger ( "(str2int " (e 1) ")")
7710
8564
  cpp ("cpp_str_to_int(" (e 1) ")" (imp "<string>")
7711
8565
 
8566
+ (imp "<optional>")
7712
8567
  (create_polyfill
7713
- "
7714
- template <class T>
7715
- class r_optional_primitive {
7716
- public:
7717
- // has_value has to start false: cpp_str_to_int and its siblings leave the
7718
- // field untouched when the conversion throws, and an indeterminate bool
7719
- // made a failed str2int read back as a value on the C++ target.
7720
- bool has_value = false;
7721
- T value = T();
7722
- r_optional_primitive<T> & operator=(const r_optional_primitive<T> & rhs) {
7723
- has_value = rhs.has_value;
7724
- value = rhs.value;
7725
- return *this;
7726
- }
7727
- r_optional_primitive<T> & operator=(const T a_value) {
7728
- has_value = true;
7729
- value = a_value;
7730
- return *this;
7731
- }
7732
- };
7733
- "
7734
- )
7735
- (create_polyfill
7736
- "r_optional_primitive<int> cpp_str_to_int(std::string s) {
7737
- r_optional_primitive<int> result;
8568
+ "std::optional<int> cpp_str_to_int(std::string s) {
8569
+ std::optional<int> result;
7738
8570
  try {
7739
8571
  // Ranger `int` is 64-bit on Go, Rust, Java, Kotlin and Python and 32-bit
7740
8572
  // here, so a literal between INT_MAX and INT64_MAX has no C++ int to
@@ -7744,8 +8576,7 @@ class r_optional_primitive {
7744
8576
  long long wide = std::stoll(s);
7745
8577
  if (wide > 2147483647LL) { wide = 2147483647LL; }
7746
8578
  if (wide < -2147483648LL) { wide = -2147483648LL; }
7747
- result.value = (int)wide;
7748
- result.has_value = true;
8579
+ result = (int)wide;
7749
8580
  } catch (...) {
7750
8581
 
7751
8582
  }
@@ -7892,32 +8723,10 @@ int? rangerStr2IntPrefix(String s) {
7892
8723
  templates {
7893
8724
  ranger ( "(str2double " (e 1) ")")
7894
8725
  cpp ("cpp_str_to_double(" (e 1) ")" (imp "<string>")
8726
+ (imp "<optional>")
7895
8727
  (create_polyfill
7896
- "
7897
- template <class T>
7898
- class r_optional_primitive {
7899
- public:
7900
- // has_value has to start false: cpp_str_to_int and its siblings leave the
7901
- // field untouched when the conversion throws, and an indeterminate bool
7902
- // made a failed str2int read back as a value on the C++ target.
7903
- bool has_value = false;
7904
- T value = T();
7905
- r_optional_primitive<T> & operator=(const r_optional_primitive<T> & rhs) {
7906
- has_value = rhs.has_value;
7907
- value = rhs.value;
7908
- return *this;
7909
- }
7910
- r_optional_primitive<T> & operator=(const T a_value) {
7911
- has_value = true;
7912
- value = a_value;
7913
- return *this;
7914
- }
7915
- };
7916
- "
7917
- )
7918
- (create_polyfill
7919
- "r_optional_primitive<double> cpp_str_to_double(std::string s) {
7920
- r_optional_primitive<double> result;
8728
+ "std::optional<double> cpp_str_to_double(std::string s) {
8729
+ std::optional<double> result;
7921
8730
  /* strtod, not stod: glibc's stod throws out_of_range whenever strtod
7922
8731
  sets ERANGE, which includes SUBNORMAL results -- parsing 5e-324
7923
8732
  (Number.MIN_VALUE) answered nothing at all. strtod still returns the
@@ -7929,8 +8738,7 @@ class r_optional_primitive {
7929
8738
  if (end != cs) {
7930
8739
  while (*end == 32 || *end == 9 || *end == 10 || *end == 13) { end++; }
7931
8740
  if (*end == 0) {
7932
- result.value = v;
7933
- result.has_value = true;
8741
+ result = v;
7934
8742
  }
7935
8743
  }
7936
8744
  return result;
@@ -7942,7 +8750,7 @@ class r_optional_primitive {
7942
8750
  "_getDoubleOrNull(" (e 1) " )"
7943
8751
  (create_polyfill
7944
8752
  "
7945
- Double _getDoubleOrNull ( String str ) {
8753
+ static Double _getDoubleOrNull ( String str ) {
7946
8754
  try {
7947
8755
  return (Double.parseDouble(str));
7948
8756
  } catch ( NullPointerException e ) {
@@ -8030,6 +8838,9 @@ def r_str_to_double(s):
8030
8838
  ranger ( "(join " (e 1) " " (e 2) ")")
8031
8839
  java7 ( "joinStrings(" (e 1 ) ", " (e 2) ")"
8032
8840
  (imp "java.lang.StringBuilder")
8841
+ ; the helper takes an ArrayList, so the file that gets the
8842
+ ; helper needs the import even when nothing else in it does
8843
+ (imp "java.util.*")
8033
8844
  (create_polyfill "
8034
8845
  static String joinStrings(ArrayList<String> list, String delimiter)
8035
8846
  {
@@ -8062,7 +8873,7 @@ std::string join(const T& v, const std::string& delim) {
8062
8873
  ") )
8063
8874
 
8064
8875
  csharp ( "String.Join(" (e 2) ", " (e 1) ")" (imp "System"))
8065
- go ( "strings.Join(" (e 1) ", " (e 2) ")")
8876
+ go ( "strings.Join(" (e 1) ", " (e 2) ")" (imp "strings"))
8066
8877
  scala ( (e 1) ".mkString(" (e 2) ")" )
8067
8878
  php ( "implode(" (e 2) ", " (e 1) ")")
8068
8879
  swift3 ( (e 1) ".joined(separator:" (e 2) ")")
@@ -8118,39 +8929,15 @@ std::string join(const T& v, const std::string& delim) {
8118
8929
  get cmdGet@(optional weak):int ( map:[K:int] key:K ) {
8119
8930
  templates {
8120
8931
  cpp( "cpp_get_map_int_value(" (e 1) ", " (e 2) ")"
8121
- (create_polyfill
8122
- "
8123
- template <class T>
8124
- class r_optional_primitive {
8125
- public:
8126
- // has_value has to start false: cpp_str_to_int and its siblings leave the
8127
- // field untouched when the conversion throws, and an indeterminate bool
8128
- // made a failed str2int read back as a value on the C++ target.
8129
- bool has_value = false;
8130
- T value = T();
8131
- r_optional_primitive<T> & operator=(const r_optional_primitive<T> & rhs) {
8132
- has_value = rhs.has_value;
8133
- value = rhs.value;
8134
- return *this;
8135
- }
8136
- r_optional_primitive<T> & operator=(const T a_value) {
8137
- has_value = true;
8138
- value = a_value;
8139
- return *this;
8140
- }
8141
- };
8142
- "
8143
- )
8932
+ (imp "<optional>")
8144
8933
  (macro
8145
8934
  ("
8146
8935
  template<typename M, typename K>
8147
- r_optional_primitive<int> cpp_get_map_int_value( const M & m , const K & key) {
8148
- r_optional_primitive<int> result;
8149
- result.has_value = false;
8936
+ std::optional<int> cpp_get_map_int_value( const M & m , const K & key) {
8937
+ std::optional<int> result;
8150
8938
  auto it = m.find(key);
8151
8939
  if (it != m.end()) {
8152
- result.value = it->second;
8153
- result.has_value = true;
8940
+ result = it->second;
8154
8941
  }
8155
8942
  return result;
8156
8943
  }")
@@ -8169,39 +8956,15 @@ r_optional_primitive<int> cpp_get_map_int_value( const M & m , const K & key) {
8169
8956
  get cmdGet@(optional weak):double ( map:[K:double] key:K ) {
8170
8957
  templates {
8171
8958
  cpp( "cpp_get_map_string_double_value(" (e 1) ", " (e 2) ")"
8172
- (create_polyfill
8173
- "
8174
- template <class T>
8175
- class r_optional_primitive {
8176
- public:
8177
- // has_value has to start false: cpp_str_to_int and its siblings leave the
8178
- // field untouched when the conversion throws, and an indeterminate bool
8179
- // made a failed str2int read back as a value on the C++ target.
8180
- bool has_value = false;
8181
- T value = T();
8182
- r_optional_primitive<T> & operator=(const r_optional_primitive<T> & rhs) {
8183
- has_value = rhs.has_value;
8184
- value = rhs.value;
8185
- return *this;
8186
- }
8187
- r_optional_primitive<T> & operator=(const T a_value) {
8188
- has_value = true;
8189
- value = a_value;
8190
- return *this;
8191
- }
8192
- };
8193
- "
8194
- )
8959
+ (imp "<optional>")
8195
8960
  (macro
8196
8961
  ("
8197
8962
  template<typename M, typename K>
8198
- r_optional_primitive<double> cpp_get_map_string_double_value( const M & m , const K & key) {
8199
- r_optional_primitive<double> result;
8200
- result.has_value = false;
8963
+ std::optional<double> cpp_get_map_string_double_value( const M & m , const K & key) {
8964
+ std::optional<double> result;
8201
8965
  auto it = m.find(key);
8202
8966
  if (it != m.end()) {
8203
- result.value = it->second;
8204
- result.has_value = true;
8967
+ result = it->second;
8205
8968
  }
8206
8969
  return result;
8207
8970
  }")
@@ -8219,13 +8982,29 @@ r_optional_primitive<double> cpp_get_map_string_double_value( const M & m , cons
8219
8982
 
8220
8983
  ; `bool` and `null` have no common type, so the generic overload's
8221
8984
  ; conditional does not compile for a map of booleans -- the int and the
8222
- ; double overloads above carry a cast for exactly this reason. Only C#
8223
- ; has an entry here, so every other target keeps reaching the generic
8224
- ; overload below.
8985
+ ; double overloads above carry a cast for exactly this reason. Rust, C#
8986
+ ; and C++ have entries here; every other target keeps reaching the
8987
+ ; generic overload below.
8225
8988
  get cmdGet@(optional weak):boolean ( map:[K:boolean] key:K ) {
8226
8989
  templates {
8227
8990
  rust ( (e 1) ".get(" (kref 2) ").cloned()" )
8228
8991
  csharp ( "(" (e 1) ".ContainsKey(" (e 2) ") ? (bool?)" (e 1) "[" (e 2) "] : null)" )
8992
+ ; An optional boolean is a bare bool on C++, so a miss reads
8993
+ ; back as false, the value `null?` tests for.
8994
+ cpp ( "r_map_get_bool(" (e 1) ", " (ckey 2) ")"
8995
+ (create_polyfill
8996
+ "
8997
+ template<typename M, typename K>
8998
+ bool r_map_get_bool(const M& m, const K& key) {
8999
+ auto it = m.find(key);
9000
+ if (it != m.end()) {
9001
+ return it->second;
9002
+ }
9003
+ return false;
9004
+ }
9005
+ "
9006
+ )
9007
+ )
8229
9008
  }
8230
9009
  }
8231
9010
  get cmdGet@(optional weak):T ( map:[K:T] key:K ) {
@@ -8239,31 +9018,16 @@ r_optional_primitive<double> cpp_get_map_string_double_value( const M & m , cons
8239
9018
  cpp ( "r_map_get_val(" (e 1) ", " (ckey 2) ")"
8240
9019
  (create_polyfill
8241
9020
  "
8242
- // Map read that never inserts on miss (unlike std::map::operator[]) and does a
8243
- // single lookup. Returns a default-constructed value (e.g. null shared_ptr)
8244
- // when the key is absent.
8245
- //
8246
- // A closed family (std::variant) is the one mapped_type with no null state:
8247
- // every alternative is a live value, so a default-constructed variant reads
8248
- // back as its FIRST alternative and a miss is indistinguishable from a genuine
8249
- // value of that case. For those maps the result carries the presence bit in
8250
- // r_optional_union; it converts back to the bare union, so a non-optional
8251
- // destination behaves exactly as it did before. Every other mapped_type keeps
8252
- // the original return type and body.
8253
- #include <variant>
8254
- // Defined next to the union typedefs when the program has a closed family. It
8255
- // is only ever named for a variant-valued map, so a declaration is enough here
8256
- // and a program with no shape at all still compiles.
8257
- template <class T> class r_optional_union;
8258
- template<typename T> struct r_map_result { typedef T type; };
8259
- template<typename... A> struct r_map_result<std::variant<A...>> {
8260
- typedef r_optional_union<std::variant<A...>> type;
8261
- };
8262
9021
  template<typename M, typename K>
8263
- typename r_map_result<typename M::mapped_type>::type r_map_get_val( const M & m , const K & key) {
9022
+ std::optional<typename M::mapped_type>
9023
+ r_map_get_val(const M& m, const K& key) {
8264
9024
  auto it = m.find(key);
8265
- if ( it != m.end() ) { return it->second; }
8266
- return typename r_map_result<typename M::mapped_type>::type();
9025
+
9026
+ if (it != m.end()) {
9027
+ return it->second;
9028
+ }
9029
+
9030
+ return std::nullopt;
8267
9031
  }
8268
9032
  "
8269
9033
  )
@@ -8792,7 +9556,7 @@ bool r_string_ends_with(const std::string& str, const std::string& suffix) {
8792
9556
  ; change only the first one -- JavaScript, Go, C++ and Python -- and the
8793
9557
  ; other half changed them all, so the same program gave two answers.
8794
9558
  ; The compiler itself wants every occurrence: the trigraph escaping of
8795
- ; ng_RangerCppClassWriter.rgr calls `(replace s "??=" ...)` on a whole
9559
+ ; RangerCppClassWriter.rgr calls `(replace s "??=" ...)` on a whole
8796
9560
  ; generated file.
8797
9561
  replace _:string (str:string oldStr:string newStr:string) {
8798
9562
  templates {
@@ -8858,7 +9622,14 @@ int r_arr_index_of( std::vector<T> vec, T elem ) {
8858
9622
  ; element -- an Rc is compared by identity, since
8859
9623
  ; Rc<RefCell<T>> implements PartialEq only when T does
8860
9624
  rust ( (custom _) )
8861
- php ( "array_search(" (e 2) ", " (e 1) ", true)")
9625
+ ; array_search answers false on a miss, and `false >= 0` holds
9626
+ php ( "r_arr_index_of(" (e 1) ", " (e 2) ")"
9627
+ (create_polyfill "
9628
+ function r_arr_index_of($arr, $item) {
9629
+ $i = array_search($item, $arr, true);
9630
+ return $i === false ? -1 : $i;
9631
+ }
9632
+ "))
8862
9633
  csharp ( (e 1) ".IndexOf(" (e 2) ")" )
8863
9634
  go ( "r_indexof_arr_" (rawtype 1) "(" (e 1) ", " (e 2) ")"
8864
9635
  (macro ("func r_indexof_arr_" (rawtype 1) "( a []" (ptr 1) (rawtype 1) ", item " (ptr 1) (rawtype 1) " ) ( int64 ) { " nl I
@@ -8956,7 +9727,12 @@ func r_index_of ( arr:" (typeof 1) " , elem: " (typeof 2) ") -> Int { " nl I
8956
9727
  java7 ( (e 1) ".remove((int)(" (e 2) "));" )
8957
9728
  csharp ( (e 1) ".RemoveAt((int)(" (e 2) "));" )
8958
9729
  scala ( (e 1) ".remove((" (e 2) ").toInt)" )
8959
- rust ( (e 1) ".remove(" (e 2) " as usize);" )
9730
+ ; the index is parenthesised: `as` binds tighter than `-`
9731
+ ; in Rust, so `remove(cnt - 1 as usize)` is
9732
+ ; `cnt - (1 as usize)` -- i64 minus usize, which is the
9733
+ ; only thing standing between this compiler's own Rust
9734
+ ; rendering and rustc
9735
+ rust ( (e 1) ".remove((" (e 2) ") as usize);" )
8960
9736
  }
8961
9737
  }
8962
9738
 
@@ -9207,6 +9983,23 @@ func r_index_of ( arr:" (typeof 1) " , elem: " (typeof 2) ") -> Int { " nl I
9207
9983
  ; .toMutableList(): sortedWith answers a read only List, and every
9208
9984
  ; array in the writer is a MutableList
9209
9985
  kotlin ( (e 1) ".sortedWith(Comparator { __a, __b -> (" (e 2) ")(__a, __b).toInt() }).toMutableList()" )
9986
+ ; Java 7 has no lambdas and no statement expressions, so the
9987
+ ; callback arrives as a LambdaSignature object and the sort
9988
+ ; itself is a helper. Collections.sort is in place, and `sort`
9989
+ ; leaves its argument alone on every other target, so the
9990
+ ; helper sorts a COPY. Without this entry there was no java7
9991
+ ; template at all and the operator did not match: the compiler
9992
+ ; could not be rendered to Java at all, stopping at
9993
+ ; "Could not match argument types for sort" in FlowCollect.
9994
+ java7 ( "r_sort_copy(" (e 1) ", new java.util.Comparator<" (atype 1) ">() { public int compare(" (atype 1) " __a, " (atype 1) " __b) { return (" (e 2) ").run(__a, __b).intValue(); } })" (imp "java.util.*")
9995
+ (create_polyfill "
9996
+ static <T> ArrayList<T> r_sort_copy(ArrayList<T> list, java.util.Comparator<T> cmp) {
9997
+ ArrayList<T> copy = new ArrayList<T>(list);
9998
+ java.util.Collections.sort(copy, cmp);
9999
+ return copy;
10000
+ }
10001
+ ")
10002
+ )
9210
10003
  ; `..sort` is a cascade, so the expression is the sorted COPY --
9211
10004
  ; `sort` leaves its argument alone on every other target.
9212
10005
  dart ( "(" (typeof 1) ".from(" (e 1) ")..sort((__a, __b) => (" (e 2) ")(__a, __b)))" )
@@ -9214,7 +10007,7 @@ func r_index_of ( arr:" (typeof 1) " , elem: " (typeof 2) ") -> Int { " nl I
9214
10007
  swift3 ( (e 1) ".sorted(by: { (" (e 2) ")($0, $1) < 0 })" )
9215
10008
  swift6 ( (e 1) ".sorted(by: { (" (e 2) ")($0, $1) < 0 })" )
9216
10009
  go ( "(func(list " (typeof 1) ") " (typeof 1) " {" nl
9217
- I "sort.Slice(" (e 1) ", func(i, j int) bool { " nl
10010
+ I "sort.SliceStable(" (e 1) ", func(i, j int) bool { " nl
9218
10011
  I "sortfn := " (e 2) nl
9219
10012
  "sortval := sortfn(" (e 1)"[i], " (e 1)"[j])" nl
9220
10013
  "return (sortval < 0)" nl i
@@ -9239,6 +10032,64 @@ static class RgSort {
9239
10032
  }
9240
10033
  }
9241
10034
 
10035
+ ; `sort` of numbers without a comparison: a new array, ascending, the
10036
+ ; argument left alone like the `sort` above
10037
+ sort _:[double] ( array:[double] ) {
10038
+ templates {
10039
+ ranger ( "(sort " (e 1) ")" )
10040
+ es6 ( (e 1) ".slice().sort((__a, __b) => __a - __b)" )
10041
+ python ( "sorted(" (e 1) ")" )
10042
+ php ( "(function($__arr) { sort($__arr); return $__arr; })(" (e 1) ")" )
10043
+ rust ( "{ let mut __v = " (e 1) ".clone(); __v.sort_by(|__a, __b| __a.partial_cmp(__b).unwrap()); __v }" )
10044
+ cpp ( "[&]() { auto __v = " (e 1) "; std::stable_sort(__v.begin(), __v.end()); return __v; }()" (imp "<algorithm>") )
10045
+ kotlin ( (e 1) ".sorted().toMutableList()" )
10046
+ java7 ( "r_sort_natural(" (e 1) ")" (imp "java.util.*")
10047
+ (create_polyfill "
10048
+ static <T extends Comparable<T>> ArrayList<T> r_sort_natural(ArrayList<T> list) {
10049
+ ArrayList<T> copy = new ArrayList<T>(list);
10050
+ java.util.Collections.sort(copy);
10051
+ return copy;
10052
+ }
10053
+ ")
10054
+ )
10055
+ dart ( "(" (typeof 1) ".from(" (e 1) ")..sort())" )
10056
+ scala ( (e 1) ".sorted" )
10057
+ swift3 ( (e 1) ".sorted()" )
10058
+ swift6 ( (e 1) ".sorted()" )
10059
+ go ( "func(src " (typeof 1) ") " (typeof 1) " { out := append(" (typeof 1) "{}, src...); sort.SliceStable(out, func(i, j int) bool { return out[i] < out[j] }); return out }(" (e 1) ")" (imp "sort") )
10060
+ csharp ( "System.Linq.Enumerable.ToList(System.Linq.Enumerable.OrderBy(" (e 1) ", __x => __x))" (imp "System.Linq") )
10061
+ }
10062
+ }
10063
+
10064
+ ; `sort` of numbers without a comparison: a new array, ascending, the
10065
+ ; argument left alone like the `sort` above
10066
+ sort _:[int] ( array:[int] ) {
10067
+ templates {
10068
+ ranger ( "(sort " (e 1) ")" )
10069
+ es6 ( (e 1) ".slice().sort((__a, __b) => __a - __b)" )
10070
+ python ( "sorted(" (e 1) ")" )
10071
+ php ( "(function($__arr) { sort($__arr); return $__arr; })(" (e 1) ")" )
10072
+ rust ( "{ let mut __v = " (e 1) ".clone(); __v.sort_by(|__a, __b| __a.partial_cmp(__b).unwrap()); __v }" )
10073
+ cpp ( "[&]() { auto __v = " (e 1) "; std::stable_sort(__v.begin(), __v.end()); return __v; }()" (imp "<algorithm>") )
10074
+ kotlin ( (e 1) ".sorted().toMutableList()" )
10075
+ java7 ( "r_sort_natural(" (e 1) ")" (imp "java.util.*")
10076
+ (create_polyfill "
10077
+ static <T extends Comparable<T>> ArrayList<T> r_sort_natural(ArrayList<T> list) {
10078
+ ArrayList<T> copy = new ArrayList<T>(list);
10079
+ java.util.Collections.sort(copy);
10080
+ return copy;
10081
+ }
10082
+ ")
10083
+ )
10084
+ dart ( "(" (typeof 1) ".from(" (e 1) ")..sort())" )
10085
+ scala ( (e 1) ".sorted" )
10086
+ swift3 ( (e 1) ".sorted()" )
10087
+ swift6 ( (e 1) ".sorted()" )
10088
+ go ( "func(src " (typeof 1) ") " (typeof 1) " { out := append(" (typeof 1) "{}, src...); sort.SliceStable(out, func(i, j int) bool { return out[i] < out[j] }); return out }(" (e 1) ")" (imp "sort") )
10089
+ csharp ( "System.Linq.Enumerable.ToList(System.Linq.Enumerable.OrderBy(" (e 1) ", __x => __x))" (imp "System.Linq") )
10090
+ }
10091
+ }
10092
+
9242
10093
  ; `reverse` gives a new array and leaves the argument alone -- the es6
9243
10094
  ; template has always been `.slice().reverse()`. Only es6 had one, so
9244
10095
  ; the operator did not compile anywhere else.
@@ -9398,6 +10249,8 @@ static class RgList {
9398
10249
  php ( nl "echo " (e 1) ";" nl)
9399
10250
  python ( nl "print(" (e 1) ", end='')" nl)
9400
10251
  csharp ( nl "Console.Write(" (e 1) ");" nl (imp "System"))
10252
+ dart ( nl "stdout.write(" (e 1) ");" nl (imp "dart:io"))
10253
+ scala ( nl "print(" (e 1) ")" nl)
9401
10254
  swift3 ( nl "print(" (e 1) ", terminator: \"\")" nl)
9402
10255
  swift6 ( nl "print(" (e 1) ", terminator: \"\")" nl)
9403
10256
  * ( nl "process.stdout.write(" (e 1) ");" nl)
@@ -9478,7 +10331,7 @@ static class RgList {
9478
10331
 
9479
10332
  ; Listen for keyboard input - callback receives key:string
9480
10333
  ; Creates a global key queue/receiver that poll_keypress can use
9481
- on_keypress cmdOnKeypress:void (keyvar:string code:block) {
10334
+ on_keypress cmdOnKeypress:void (keyvar@(mutates):string code:block) {
9482
10335
  templates {
9483
10336
  es6 ( nl
9484
10337
  "global.r_key_queue = [];" nl
@@ -9486,10 +10339,16 @@ static class RgList {
9486
10339
  "readline.emitKeypressEvents(process.stdin);" nl
9487
10340
  "if (process.stdin.isTTY) { process.stdin.setRawMode(true); }" nl
9488
10341
  "process.stdin.resume();" nl
9489
- "process.stdin.on('keypress', (str, key) => {" nl I
9490
- "if (key && key.ctrl && key.name === 'c') { process.stdout.write('\\x1b[?25h'); process.exit(); }" nl
10342
+ ; The handler's parameters are PREFIXED. Named `str` and `key`, they
10343
+ ; shadowed any Ranger variable of the same name -- and `key` is the
10344
+ ; obvious name for a key variable, so `on_keypress key { ... }` gave the
10345
+ ; block node's key OBJECT instead of the string, and the assignment
10346
+ ; below landed on the parameter rather than on the Ranger local.
10347
+ ; ISSUES.md #91.
10348
+ "process.stdin.on('keypress', (__rgr_s, __rgr_key) => {" nl I
10349
+ "if (__rgr_key && __rgr_key.ctrl && __rgr_key.name === 'c') { process.stdout.write('\\x1b[?25h'); process.exit(); }" nl
9491
10350
  "var __rgr_k = \"\";" nl
9492
- "if (key && key.name) { __rgr_k = key.name; } else if (str && str.length > 0) { __rgr_k = str; } else if (key && key.sequence) { __rgr_k = key.sequence; }" nl
10351
+ "if (__rgr_key && __rgr_key.name) { __rgr_k = __rgr_key.name; } else if (__rgr_s && __rgr_s.length > 0) { __rgr_k = __rgr_s; } else if (__rgr_key && __rgr_key.sequence) { __rgr_k = __rgr_key.sequence; }" nl
9493
10352
  "if (__rgr_k !== \"\") { " (e 1) " = __rgr_k; global.r_key_queue.push(__rgr_k); }" nl
9494
10353
  (block 2)
9495
10354
  i nl "});" nl
@@ -10203,6 +11062,35 @@ fun r_read_key(): String {
10203
11062
  }
10204
11063
  }
10205
11064
 
11065
+ ; Set the status this program ends with, WITHOUT ending it here.
11066
+ ;
11067
+ ; `exit` stops the process at once, and on node a stdout that is a pipe
11068
+ ; -- `$(rgrc ... 2>&1)`, a log file, CI -- is written asynchronously, so
11069
+ ; whatever was printed last is dropped on the floor. That is exactly the
11070
+ ; error report the non-zero status is about. So set the code and let the
11071
+ ; program end on its own. The targets whose stdout is unbuffered or
11072
+ ; flushed at exit have nothing to defer and exit here.
11073
+ set_exit_code cmdSetExitCode:void ( code:int) {
11074
+ templates {
11075
+ llvm ( nl "exit " (e 1) nl)
11076
+ ranger ( nl "set_exit_code " (e 1) nl)
11077
+ cpp ( nl "exit(" (e 1) ");" nl (imp "<cstdlib>"))
11078
+ kotlin ( nl "System.exit(" (e 1) ")" nl )
11079
+ scala ( nl "System.exit(" (e 1) ")" nl )
11080
+ ; Narrowed for the same reason as in `exit` below.
11081
+ go ( nl "os.Exit(int(" (e 1) "))" nl (imp "os"))
11082
+ rust ( nl "std::process::exit((" (e 1) ") as i32);" nl )
11083
+ java7 ( nl "System.exit(" (e 1) ");" nl )
11084
+ php ( nl "exit(" (e 1) ");" nl )
11085
+ python ( nl "sys.exit(" (e 1) ")" nl (imp "sys"))
11086
+ csharp ( nl "Environment.ExitCode = " (e 1) ";" nl (imp "System"))
11087
+ dart ( nl "exitCode = " (e 1) ";" nl (imp "dart:io"))
11088
+ swift3 ( nl "exit(Int32(" (e 1) "))" nl (imp "Foundation"))
11089
+ swift6 ( nl "exit(Int32(" (e 1) "))" nl (imp "Foundation"))
11090
+ * ( nl "process.exitCode = " (e 1) ";" nl)
11091
+ }
11092
+ }
11093
+
10206
11094
  exit cmdExit:void ( code:int) {
10207
11095
  templates {
10208
11096
  llvm ( nl "exit " (e 1) nl)
@@ -10335,9 +11223,29 @@ std::string r_cpp_str_to_uppercase(const std::string& original)
10335
11223
  }
10336
11224
  }
10337
11225
 
11226
+ ; Java: an int, double or boolean is often an Integer / Double /
11227
+ ; Boolean object, and `==` between two of them compares references
11228
+ ; (Integer caches -128..127 only, so 128 == 128 was false, and two
11229
+ ; equal Double fields were never ==). rg_eq compares numbers by value
11230
+ ; and everything else by reference, as before.
10338
11231
  == cmdEqual:boolean ( left:T right:T ) {
10339
11232
  templates {
10340
11233
  rust ( (custom _) )
11234
+ java7 ( "rg_eq(" (e 1) ", " (e 2) ")"
11235
+ (create_polyfill "
11236
+ static boolean rg_eq(Object a, Object b) {
11237
+ if (a instanceof Number && b instanceof Number) {
11238
+ if (a instanceof Double || b instanceof Double || a instanceof Float || b instanceof Float) {
11239
+ return ((Number) a).doubleValue() == ((Number) b).doubleValue();
11240
+ }
11241
+ return ((Number) a).longValue() == ((Number) b).longValue();
11242
+ }
11243
+ if (a instanceof Boolean && b instanceof Boolean) {
11244
+ return ((Boolean) a).booleanValue() == ((Boolean) b).booleanValue();
11245
+ }
11246
+ return a == b;
11247
+ }
11248
+ "))
10341
11249
  * ( (e 1) " == " (e 2) )
10342
11250
  }
10343
11251
  }
@@ -10449,6 +11357,21 @@ std::string r_cpp_str_to_uppercase(const std::string& original)
10449
11357
  != cmdNeq:boolean ( left:T right:T ) {
10450
11358
  templates {
10451
11359
  rust ( (custom _) )
11360
+ java7 ( "!rg_eq(" (e 1) ", " (e 2) ")"
11361
+ (create_polyfill "
11362
+ static boolean rg_eq(Object a, Object b) {
11363
+ if (a instanceof Number && b instanceof Number) {
11364
+ if (a instanceof Double || b instanceof Double || a instanceof Float || b instanceof Float) {
11365
+ return ((Number) a).doubleValue() == ((Number) b).doubleValue();
11366
+ }
11367
+ return ((Number) a).longValue() == ((Number) b).longValue();
11368
+ }
11369
+ if (a instanceof Boolean && b instanceof Boolean) {
11370
+ return ((Boolean) a).booleanValue() == ((Boolean) b).booleanValue();
11371
+ }
11372
+ return a == b;
11373
+ }
11374
+ "))
10452
11375
  * ( (e 1) " != " (e 2) )
10453
11376
  }
10454
11377
  }
@@ -10499,7 +11422,7 @@ std::string r_cpp_str_to_uppercase(const std::string& original)
10499
11422
  csharp ( (e 1) " != null && " (e 2) " != null")
10500
11423
 
10501
11424
  php ( "isset("(e 1) ") && isset(" (e 2) ")")
10502
- java7 ( (e 1) ".isPresent() && " (e 2) ".isPresent()")
11425
+ java7 ( (e 1) " != null && " (e 2) " != null")
10503
11426
  rust ( (e 1) ".is_some() && " (e 2) ".is_some()")
10504
11427
  swift3 ( (e 1) " != nil && " (e 2) " != nil")
10505
11428
  swift6 ( (e 1) " != nil && " (e 2) " != nil")
@@ -10517,7 +11440,7 @@ std::string r_cpp_str_to_uppercase(const std::string& original)
10517
11440
  ranger ( "(" (e 1) " && " (e 2) ")" )
10518
11441
  php ( (e 1) " && isset(" (e 2) ")")
10519
11442
  scala ( (e 1) " && " (e 2) ".isDefined")
10520
- java7 ( (e 1) " && " (e 2) ".isPresent()")
11443
+ java7 ( (e 1) " && " (e 2) " != null")
10521
11444
  csharp ( (e 1) " && " (e 2) " != null")
10522
11445
 
10523
11446
  rust ( (e 1) " && " (e 2) ".is_some()")
@@ -10537,7 +11460,7 @@ std::string r_cpp_str_to_uppercase(const std::string& original)
10537
11460
  ranger ( "(" (e 1) " && " (e 2) ")" )
10538
11461
  php ( "isset("(e 1) ") && " (e 2) )
10539
11462
  scala ( ""(e 1) ".isDefined && " (e 2) )
10540
- java7 ( ""(e 1) ".isPresent() && " (e 2) )
11463
+ java7 ( ""(e 1) " != null && " (e 2) )
10541
11464
  csharp ( ""(e 1) " != null && " (e 2) )
10542
11465
 
10543
11466
  rust ( ""(e 1) ".is_some() && " (e 2) )
@@ -10725,32 +11648,7 @@ std::string r_cpp_str_to_uppercase(const std::string& original)
10725
11648
  * ( "false /* sse_is_connected not implemented */" )
10726
11649
  }
10727
11650
  }
10728
-
10729
- ; HTTP Server Lifecycle Operators
10730
- ; These are special operators - the Go writer will detect @(HttpServer) class
10731
- ; and generate route setup code when start is called on HttpServer
10732
11651
 
10733
- ; Start HTTP server - wires up all @(GET), @(POST), @(SSE) routes and starts listening
10734
- ; Usage: start server 8080 OR start server (uses default port from annotation)
10735
- ; The Go writer generates http.HandleFunc calls for each annotated method
10736
- start@(moves@( 1 )) cmdHttpServerStart:void (server:HttpServer port:int) {
10737
- templates {
10738
- go ( (custom _) )
10739
- es6 ( (e 1) ".start(" (e 2) ")" )
10740
- * ( "/* start HttpServer not implemented */" )
10741
- }
10742
- }
10743
-
10744
- ; Stop HTTP server gracefully
10745
- ; Usage: stop server
10746
- stop@(moves@( 1 )) cmdHttpServerStop:void (server:HttpServer) {
10747
- templates {
10748
- go ( (e 1) ".Shutdown(context.Background())" )
10749
- es6 ( (e 1) ".stop()" )
10750
- * ( "/* stop HttpServer not implemented */" )
10751
- }
10752
- }
10753
-
10754
11652
  ; Process lifecycle operators (distinct from HttpServer start/stop)
10755
11653
  find_process@(moves@( 1 )) cmdFindProcess:RangerProcessBase@(optional) (path:string) {
10756
11654
  templates {
@@ -10762,7 +11660,7 @@ std::string r_cpp_str_to_uppercase(const std::string& original)
10762
11660
  }
10763
11661
  }
10764
11662
 
10765
- ; proc_send is lowered in ng_RangerProcessProcSend.rgr (typed handler methods, overload match).
11663
+ ; proc_send is lowered in RangerProcessProcSend.rgr (typed handler methods, overload match).
10766
11664
 
10767
11665
  begin_dispatch_turn@(moves@( 1 )) cmdBeginDispatchTurn:void (root:RangerProcessBase) {
10768
11666
  templates {