ranger-compiler 3.1.1 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/README.md +21 -1
- package/dist/Lang.rgr +209 -19
- package/dist/api.d.ts +881 -47
- package/dist/api.js +13986 -961
- package/dist/lib/ranger-dir.rgr +25 -5
- package/dist/package.json +1 -1
- package/dist/rgrc.js +3746 -754
- package/package.json +139 -5
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.2.0] - 2026-08-01
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`@serialize(true)` array / hash / property types without the annotation** — the generator assumed every referenced class had `toDictionary` / `fromDictionary` and emitted the call regardless, so the compile failed inside `extension <Class>` with `Could not match argument types for push` / `for case` and never named the real cause. Hash-valued fields were worse: the field was silently dropped from the serialized output. A `@serialize(true)` class that references a class which is neither `@serialize(true)` nor implements the pair by hand now fails at the declaration:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
[FAIL] Parent.kids: [Child] can not be serialized - class Child is not @serialize(true).
|
|
18
|
+
Add @serialize(true) to Child, or implement toDictionary / fromDictionary in it.
|
|
19
|
+
9 │ def kids:[Child]
|
|
20
|
+
^── here
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- **`@serialize(true)` with mutually referencing classes** — serializer extensions were generated one class at a time and `is_serialized` was set as each was reached, so whichever class came first in the dependency sort saw its peer as non-serializable and emitted primitive element code. All `@serialize(true)` classes are now marked before any extension is generated, which mutual references could not satisfy by sorting.
|
|
24
|
+
|
|
25
|
+
- **`@serialize(true)` element types with hand-written `toDictionary` / `fromDictionary`** — accepted as serializable; the generator now routes them through the object path instead of treating them as primitives.
|
|
26
|
+
|
|
27
|
+
- **Duplicate serializer extension when one file is reached by two import spellings** — a file imported as both `Child.rgr` and `domain/Child.rgr` was expanded twice, and with `@serialize(true)` the second expansion produced `method with the same name and parameter signature declared earlier` pointing at generated code. Fixed since 3.1.1 was published, released here; covered by a regression test.
|
|
28
|
+
|
|
29
|
+
- **`SourceCode` constructed with three arguments in `ng_CodeNode.rgr`** — `SourceCode` takes a single `code_str`, but `vref1`, `vref2`, `newStr`, `newBool` and nine sibling factories passed `(name 0 (strlen name))` or `("" 0 0)`, copying the shape of the adjacent `new CodeNode(code 0 ...)`. JavaScript discards the extra arguments, so this was invisible until the TypeScript API bundle was type checked, where it produced all 13 of the `TS2554: Expected 1 arguments, but got 3` errors that `build:dist:module` reported. Dropping the dead arguments makes `tsc` clean, so the step is `&&`-chained again and a real type error blocks the build instead of scrolling past
|
|
30
|
+
|
|
31
|
+
- **`file_mtime` C++ backend** — added missing `cpp` template in `Lang.rgr` (`stat()` + ms, matching es6 `mtimeMs`); fixes `game_runtime.rgr` hot-reload compile for `game_sdl` native binary
|
|
32
|
+
|
|
33
|
+
- **`TSLexer` UTF-8 / native C++ tokenization** — code-unit length vs byte `charAt` mismatch after multi-byte characters (e.g. em dash in comments) no longer desyncs the lexer; fixes native SDL parse failures on [`invaders.game.tsx`](./gallery/game_engine/scripting/invaders.game.tsx) and similar scripts
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- **LPC spritesheet compositor (standalone MVP)** — Ranger-native `gallery/game_engine/lpc/` (`png_decoder.rgr`, layer blit, male walk demo); embedded `pack/demo-male-walk` (~28 KB); `npm run engine:lpc:build` / `engine:lpc:run`; design doc [`LPC_HEADLESS_SPRITESHEET.md`](./gallery/game_engine/LPC_HEADLESS_SPRITESHEET.md)
|
|
38
|
+
|
|
39
|
+
- **JPEG background loading for scripted games** — `GameImageLoader` + `GameRunner` asset queue (`resources()`, `backgroundImage()`, `onLoading()`); `splash_demo.game.tsx` smoke test; `game.d.ts` `ResourceDef` / `LoadingProps` types
|
|
40
|
+
|
|
41
|
+
- **In-process TS hot reload (Path A)** — `TSAstPatcher` (`ts_ast_patch.rgr`), `ComponentEngine.patchScript()`, `GameRunner` runtime options (`setHotReload`, `trackScriptFile`, `maybeHotReload`); `game_sdl --hot-reload` / `--no-hot-reload`; default on for interactive SDL, off for `maxFrames` smoke runs
|
|
42
|
+
|
|
43
|
+
- **SDL + native compiled games (Path B)** — `game_sdl_native_host.rgr`, `invaders_native_sdl_runner.rgr`, `build-game-sdl-native.sh`; `npm run engine:game-sdl-native:run:invaders` runs emitter output in an SDL window without `ComponentEngine`
|
|
44
|
+
|
|
45
|
+
- **`@serialize(true)` regression suite** — `tests/compiler-serialize.test.ts` and `tests/fixtures/serialize/`: round trip over primitives, nested objects, object arrays and object hashes; mutual references; hand-written `toDictionary` / `fromDictionary`; duplicate import spellings; and the three diagnostics above
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **`prepublishOnly` no longer runs the whole repository suite** — publishing the compiler ran all 56 test files, including the gallery, game-engine and native-toolchain suites. Those need SDL2, `g++`, Cannon and game fixtures that ship with neither the repo nor the package, so `npm publish` failed on the publisher's machine for reasons unrelated to the compiler (missing `SDL2/SDL.h`, `gallery/game_engine/games/ylos/index.tsx` and `physics_race/index.tsx` are absent from the repository entirely). `prepublishOnly` and `.github/workflows/publish.yml` now run `npm run test:publish` — 44 files, 355 tests, ~90s — covering parsing, type checking and code generation for every target backend. `npm test` still runs everything, and `ci.yml` is unchanged.
|
|
50
|
+
|
|
51
|
+
- **`build:dist` now includes `build:dist:module`** — `dist/api.js` was not rebuilt by the release build, which is how it drifted behind `bin/output.js`
|
|
52
|
+
|
|
53
|
+
- **`dist/api.js` rebuilt from current sources** — the committed programmatic-API bundle predated several compiler fixes, so `require("ranger-compiler")` shipped older behaviour than the `rgrc` CLI. `scripts/patch-chain-desugar.js` now patches `dist/api.js` as well as `bin/output.js`, and `build:dist:module` runs it after `tsc`
|
|
54
|
+
|
|
10
55
|
## [3.1.1] - 2026-06-23
|
|
11
56
|
|
|
12
57
|
### Changed
|
package/README.md
CHANGED
|
@@ -69,6 +69,7 @@ host.notifyPath = (path) => { /* sync view model + re-render */ };
|
|
|
69
69
|
- `gallery/js_parser` - substantial parser example with benchmarks and README
|
|
70
70
|
- `gallery/pdf_writer` - EVG / TSX document tooling and preview server
|
|
71
71
|
- `gallery/invaders` - cross-target demo game
|
|
72
|
+
- `gallery/game_engine` - retained-mode game runner, SDL launcher, and TSX games (Pong, Breakout, Invaders, Pac-Man); see `gallery/game_engine/scripting/GAME_SCRIPTING.md`
|
|
72
73
|
- `gallery/invaders/llvm/invaders.ll` - checked-in LLVM IR sample from the experimental `-l=llvm` backend
|
|
73
74
|
|
|
74
75
|
## Compatibility Snapshot
|
|
@@ -84,6 +85,23 @@ The project can target `JavaScript`, `Java`, `Go`, `Swift`, `PHP`, `C++`, `C#`,
|
|
|
84
85
|
| **LLVM / WASM** (`-l=llvm`) | **Experimental.** Lowers to LLVM IR; optional WAT export for freestanding WASM. Native libc builds work for demos like Space Invaders; browser WASM is still rough. See `npm run test:llvm`, `npm run game:build:llvm`. |
|
|
85
86
|
| Gallery examples | Good for understanding direction and capability, but some require manual setup or platform-specific tooling |
|
|
86
87
|
|
|
88
|
+
### Conformance suite (Track 1)
|
|
89
|
+
|
|
90
|
+
Cross-target semantic fixtures live in `tests/conformance/`. Run `npx vitest run tests/compiler-conformance.test.ts`.
|
|
91
|
+
Regenerate the fixture list with `node scripts/generate-conformance-table.mjs`.
|
|
92
|
+
|
|
93
|
+
<!-- BEGIN CONFORMANCE_TABLE -->
|
|
94
|
+
| Fixture | Topic | Targets |
|
|
95
|
+
| --- | --- | --- |
|
|
96
|
+
| `array_param_mutate` | array parameters use reference semantics (Issue #58; Go known gap) | ES6, Go, Kotlin (when toolchain present) |
|
|
97
|
+
| `clear_then_push` | clear resets slice without nil, push refills (Issue #59) | ES6, Go, Kotlin (when toolchain present) |
|
|
98
|
+
| `int_division_to_double` | Conformance: integer division promoted to double (Issue #4) | ES6, Go, Kotlin (when toolchain present) |
|
|
99
|
+
| `lf_line_endings` | LF-only source (Issue #12 class must not break operator spacing) | ES6, Go, Kotlin (when toolchain present) |
|
|
100
|
+
| `math_ops` | Conformance: arithmetic and comparisons | ES6, Go, Kotlin (when toolchain present) |
|
|
101
|
+
| `string_codepoint_index` | Conformance: Unicode code-point string indexing (Issue #57) | ES6, Go, Kotlin (when toolchain present) |
|
|
102
|
+
| `while_loop` | Conformance: while loop control flow | ES6, Go, Kotlin (when toolchain present) |
|
|
103
|
+
<!-- END CONFORMANCE_TABLE -->
|
|
104
|
+
|
|
87
105
|
## What's New in Version 3.0
|
|
88
106
|
|
|
89
107
|
- **New File Extension** - Transitioning from `.clj` to `.rgr` for Ranger identity
|
|
@@ -434,7 +452,7 @@ npm run demo:wasm # smaller freestanding WASM demo (tests/fixtures/ll
|
|
|
434
452
|
|
|
435
453
|
**Checked-in sample:** [`gallery/invaders/llvm/invaders.ll`](gallery/invaders/llvm/invaders.ll) is LLVM IR kept in the repo so you can inspect codegen without building. Regenerate with `cp tmp/invaders-native/invaders.ll gallery/invaders/llvm/invaders.ll` after `game:build:llvm` (see [`gallery/invaders/llvm/README.md`](gallery/invaders/llvm/README.md)).
|
|
436
454
|
|
|
437
|
-
Status: experimental — libc-linked native builds are the most reliable path
|
|
455
|
+
Status: experimental — libc-linked native builds are the most reliable path. The freestanding WAT/WASM path (`-wasmrc`) now has a reference-counting runtime (free-list heap with `memory.grow`, typedesc-driven recursive object destruction), classes with reference-counted fields, singletons, strings, `[T]`/`[K:V]` collections, and lambdas/closures (function table + `call_indirect`, with value/object capture and mutation); the `gallery/game_engine/games/ranger_autopeli` guest is authored in Ranger and compiles to WASM this way. Terminal-import lowering for the full Space Invaders game is still incomplete. Smaller freestanding demos run under `npm run demo:wasm`.
|
|
438
456
|
|
|
439
457
|
#### Cross-Compiling the Game
|
|
440
458
|
|
|
@@ -1329,6 +1347,8 @@ this.foo({
|
|
|
1329
1347
|
})
|
|
1330
1348
|
```
|
|
1331
1349
|
|
|
1350
|
+
Lambdas compile on every backend, including the freestanding WASM/WAT path (`-wasmrc`): each body is hoisted to a function-table entry and calls go through `call_indirect`, with the value a reference-counted closure record. Captured variables are copied (values/strings) or retained (objects); a captured object can be mutated through the closure, and a captured value can be shared by boxing it in a heap cell.
|
|
1351
|
+
|
|
1332
1352
|
# Automatically infixed math support
|
|
1333
1353
|
|
|
1334
1354
|
It is easy to define new mathematical operations in the Lang.clj file or in modules. However, some mathematical operations are automatically infixed
|
package/dist/Lang.rgr
CHANGED
|
@@ -60,7 +60,7 @@ language {
|
|
|
60
60
|
llvm LLVM IR ll
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
toplevel_keywords (class systemclass Constructor fn sfn Import Extend Enum def let operators)
|
|
63
|
+
toplevel_keywords (class record systemclass Constructor fn sfn Import Extend Enum def let operators)
|
|
64
64
|
annotation_keywors (weak strong lives temp)
|
|
65
65
|
|
|
66
66
|
; transformations for the reserved words for function names or other keywords
|
|
@@ -80,12 +80,34 @@ language {
|
|
|
80
80
|
class _class
|
|
81
81
|
new _new
|
|
82
82
|
delete _delete
|
|
83
|
+
and _and
|
|
84
|
+
or _or
|
|
85
|
+
not _not
|
|
86
|
+
xor _xor
|
|
87
|
+
compl _compl
|
|
88
|
+
bitand _bitand
|
|
89
|
+
bitor _bitor
|
|
90
|
+
and_eq _and_eq
|
|
91
|
+
or_eq _or_eq
|
|
92
|
+
xor_eq _xor_eq
|
|
93
|
+
not_eq _not_eq
|
|
83
94
|
template _template
|
|
84
95
|
namespace _namespace
|
|
85
96
|
virtual _virtual
|
|
86
97
|
public _public
|
|
87
98
|
private _private
|
|
88
99
|
protected _protected
|
|
100
|
+
signed _signed
|
|
101
|
+
unsigned _unsigned
|
|
102
|
+
register _register
|
|
103
|
+
volatile _volatile
|
|
104
|
+
mutable _mutable
|
|
105
|
+
friend _friend
|
|
106
|
+
typename _typename
|
|
107
|
+
typedef _typedef
|
|
108
|
+
extern _extern
|
|
109
|
+
explicit _explicit
|
|
110
|
+
goto _goto
|
|
89
111
|
}
|
|
90
112
|
go {
|
|
91
113
|
type _type
|
|
@@ -222,6 +244,19 @@ language {
|
|
|
222
244
|
templates {
|
|
223
245
|
es6 ('Math.random()')
|
|
224
246
|
kotlin ('Math.random()' (imp "java.lang.Math"))
|
|
247
|
+
cpp ( "rg_random_double()" (imp "<random>")
|
|
248
|
+
(create_polyfill "
|
|
249
|
+
double rg_random_double() {
|
|
250
|
+
static thread_local std::mt19937 _rg{std::random_device{}()};
|
|
251
|
+
static thread_local std::uniform_real_distribution<double> _rd(0.0, 1.0);
|
|
252
|
+
return _rd(_rg);
|
|
253
|
+
}
|
|
254
|
+
")
|
|
255
|
+
)
|
|
256
|
+
go ( 'rand.Float64()' (imp "math/rand") )
|
|
257
|
+
; std-only (rustc, no crates): each RandomState::new() draws a fresh
|
|
258
|
+
; OS-seeded key, so hashing a constant yields a varying 53-bit value.
|
|
259
|
+
rust ( '{ use std::hash::{BuildHasher, Hasher}; let mut h = std::collections::hash_map::RandomState::new().build_hasher(); h.write_u64(0); ((h.finish() >> 11) as f64) / ((1u64 << 53) as f64) }' )
|
|
225
260
|
}
|
|
226
261
|
}
|
|
227
262
|
|
|
@@ -413,11 +448,27 @@ func _r_md5(value string) string {
|
|
|
413
448
|
tan tan:double ( v:double ) {
|
|
414
449
|
templates {
|
|
415
450
|
es6 ("Math.tan(" (e 1) ")")
|
|
416
|
-
go ( "math.Tan(" (e 1) ")" (imp "math"))
|
|
417
|
-
swift3 ( "tan(" (e 1) ")" (imp "Foundation"))
|
|
418
|
-
java7 ( "Math.tan(" (e 1) ")" (imp "java.lang.Math"))
|
|
419
|
-
php ( "tan(" (e 1) ")")
|
|
420
|
-
cpp ("tan(" (e 1) ")" (imp "<math.h>"))
|
|
451
|
+
go ( "math.Tan(" (e 1) ")" (imp "math"))
|
|
452
|
+
swift3 ( "tan(" (e 1) ")" (imp "Foundation"))
|
|
453
|
+
java7 ( "Math.tan(" (e 1) ")" (imp "java.lang.Math"))
|
|
454
|
+
php ( "tan(" (e 1) ")")
|
|
455
|
+
cpp ("tan(" (e 1) ")" (imp "<math.h>"))
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
atan2 atan2:double ( y:double x:double ) {
|
|
460
|
+
templates {
|
|
461
|
+
ranger ( "(atan2 " (e 1) " " (e 2) ")" )
|
|
462
|
+
es6 ( "Math.atan2(" (e 1) ", " (e 2) ")" )
|
|
463
|
+
go ( "math.Atan2(" (e 1) ", " (e 2) ")" (imp "math"))
|
|
464
|
+
swift3 ( "atan2(" (e 1) ", " (e 2) ")" (imp "Foundation"))
|
|
465
|
+
java7 ( "Math.atan2(" (e 1) ", " (e 2) ")" (imp "java.lang.Math"))
|
|
466
|
+
php ( "atan2(" (e 1) ", " (e 2) ")" )
|
|
467
|
+
csharp ( "Math.Atan2(" (e 1) ", " (e 2) ")" (imp "System"))
|
|
468
|
+
rust ( "" (e 1) ".atan2(" (e 2) ")" )
|
|
469
|
+
scala ( "math.atan2(" (e 1) ", " (e 2) ")" (imp "scala.math"))
|
|
470
|
+
cpp ( "atan2(" (e 1) ", " (e 2) ")" (imp "<math.h>"))
|
|
471
|
+
* ( "Math.atan2(" (e 1) ", " (e 2) ")" )
|
|
421
472
|
}
|
|
422
473
|
}
|
|
423
474
|
|
|
@@ -713,7 +764,7 @@ func r_dir_exists(pathName string) bool {
|
|
|
713
764
|
}
|
|
714
765
|
}
|
|
715
766
|
|
|
716
|
-
; Returns the file modification time
|
|
767
|
+
; Returns the file modification time (ms on es6/cpp; seconds on go)
|
|
717
768
|
file_mtime cmdFileMTime:int (path:string filename:string) {
|
|
718
769
|
templates {
|
|
719
770
|
go ( "r_file_mtime(" (e 1) ", " (e 2) ")"
|
|
@@ -730,6 +781,18 @@ func r_file_mtime(pathName string, fileName string) int64 {
|
|
|
730
781
|
)
|
|
731
782
|
)
|
|
732
783
|
es6 ( "require('fs').statSync(" (e 1) " + '/' + " (e 2) ").mtimeMs" )
|
|
784
|
+
cpp ( "r_file_mtime(" (e 1) ", " (e 2) ")" (imp "<sys/stat.h>") (imp "<string>")
|
|
785
|
+
(create_polyfill "
|
|
786
|
+
int64_t r_file_mtime(const std::string& path, const std::string& filename) {
|
|
787
|
+
struct stat st;
|
|
788
|
+
std::string full = path + \"/\" + filename;
|
|
789
|
+
if (stat(full.c_str(), &st) != 0) {
|
|
790
|
+
return 0;
|
|
791
|
+
}
|
|
792
|
+
return (int64_t)st.st_mtime * 1000;
|
|
793
|
+
}
|
|
794
|
+
")
|
|
795
|
+
)
|
|
733
796
|
ranger ( "(file_mtime " (e 1) " " (e 2) ")" )
|
|
734
797
|
}
|
|
735
798
|
}
|
|
@@ -1310,6 +1373,24 @@ fn r_read_file(path: &str, filename: &str) -> Option<String> {
|
|
|
1310
1373
|
}
|
|
1311
1374
|
}
|
|
1312
1375
|
|
|
1376
|
+
; Integer division (truncates toward zero). Use instead of / when both
|
|
1377
|
+
; operands are int — plain / returns double for int/int in Ranger.
|
|
1378
|
+
idiv _:int (left:int right:int) {
|
|
1379
|
+
templates {
|
|
1380
|
+
ranger @macro(true) ( "(/ " (e 1) " " (e 2) ")" )
|
|
1381
|
+
cpp ( "((" (e 1) ") / (" (e 2) "))" )
|
|
1382
|
+
llvm ( "(idiv " (e 1) " " (e 2) ")" )
|
|
1383
|
+
es6 ( "((" (e 1) " / " (e 2) ") | 0)" )
|
|
1384
|
+
go ( (e 1) " / " (e 2) )
|
|
1385
|
+
rust ( "((" (e 1) ") / (" (e 2) "))" )
|
|
1386
|
+
kotlin ( (e 1) " / " (e 2) )
|
|
1387
|
+
python ( "((" (e 1) ") // (" (e 2) "))" )
|
|
1388
|
+
java7 ( "((" (e 1) ") / (" (e 2) "))" )
|
|
1389
|
+
php ( "intdiv(" (e 1) ", " (e 2) ")" )
|
|
1390
|
+
* ( (e 1) " / " (e 2) )
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1313
1394
|
; ============================================================================
|
|
1314
1395
|
; Bitwise operators for integer manipulation
|
|
1315
1396
|
; ============================================================================
|
|
@@ -1449,12 +1530,16 @@ fn r_read_file(path: &str, filename: &str) -> Option<String> {
|
|
|
1449
1530
|
* cmdMulOp:double ( left:double right:double ) { templates { * ( (e 1) " * " (e 2) ) } }
|
|
1450
1531
|
* cmdMulOp:int ( left:int right:int ) { templates { * ( (e 1) " * " (e 2) ) } }
|
|
1451
1532
|
|
|
1452
|
-
/ cmdDivOp:double ( left:double right:double ) { templates {
|
|
1453
|
-
|
|
1533
|
+
/ cmdDivOp:double ( left:double right:double ) { templates {
|
|
1534
|
+
cpp ( "((double)(" (e 1) ") / (double)(" (e 2) "))" )
|
|
1535
|
+
* ( (e 1) " / " (e 2) )
|
|
1536
|
+
} }
|
|
1537
|
+
/ cmdDivOp:double ( left:int right:int ) { templates {
|
|
1454
1538
|
go ( "float64(" (e 1) ") / float64(" (e 2) ")" )
|
|
1455
1539
|
rust ( "(" (e 1) " as f64) / (" (e 2) " as f64)" )
|
|
1456
1540
|
kotlin ( (e 1) ".toDouble() / " (e 2) ".toDouble()" )
|
|
1457
|
-
|
|
1541
|
+
cpp ( "((double)(" (e 1) ") / (double)(" (e 2) "))" )
|
|
1542
|
+
* ( (e 1) " / " (e 2) )
|
|
1458
1543
|
} }
|
|
1459
1544
|
|
|
1460
1545
|
; optional string operator...
|
|
@@ -1687,7 +1772,22 @@ fn r_read_file(path: &str, filename: &str) -> Option<String> {
|
|
|
1687
1772
|
templates {
|
|
1688
1773
|
cpp ( "if ( " (e 1) ".has_value ) {" nl I (block 2) i nl "}" nl )
|
|
1689
1774
|
}
|
|
1690
|
-
}
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
; typed optional int/double with an else branch: r_optional_primitive is
|
|
1778
|
+
; a value type (has_value field), not a pointer, so the generic :T
|
|
1779
|
+
; `!= NULL` form below does not compile under clang. Emit `.has_value`.
|
|
1780
|
+
if cmdIf:void ( condition@(optional):int then_block:block else_block ) {
|
|
1781
|
+
templates {
|
|
1782
|
+
cpp ( "if ( " (e 1) ".has_value ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl )
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
if cmdIf:void ( condition@(optional):double then_block:block else_block ) {
|
|
1787
|
+
templates {
|
|
1788
|
+
cpp ( "if ( " (e 1) ".has_value ) {" I nl (block 2) i nl "} else {" nl I (block 3) i "}" nl )
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1691
1791
|
|
|
1692
1792
|
if cmdIf:void ( condition@(optional):T then_block:block ) {
|
|
1693
1793
|
templates {
|
|
@@ -1930,9 +2030,18 @@ std::vector<T> r_make_vector_from_array( const T (&data)[N] )
|
|
|
1930
2030
|
}
|
|
1931
2031
|
null? cmdIsNotNull:boolean ( arg@(optional):string ) {
|
|
1932
2032
|
templates {
|
|
1933
|
-
cpp ((e 1) ".empty()")
|
|
2033
|
+
cpp ((e 1) ".empty()")
|
|
1934
2034
|
}
|
|
1935
|
-
}
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
; buffer is a value type (std::vector<uint8_t>) in C++, so the generic
|
|
2038
|
+
; `!= NULL` path below does not compile. Treat empty/absent as null,
|
|
2039
|
+
; mirroring the string overload above.
|
|
2040
|
+
null? cmdIsNotNull:boolean ( arg@(optional):buffer ) {
|
|
2041
|
+
templates {
|
|
2042
|
+
cpp ((e 1) ".empty()")
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
1936
2045
|
|
|
1937
2046
|
null? cmdIsNull:boolean ( arg@(optional):T ) {
|
|
1938
2047
|
templates {
|
|
@@ -1964,9 +2073,18 @@ std::vector<T> r_make_vector_from_array( const T (&data)[N] )
|
|
|
1964
2073
|
}
|
|
1965
2074
|
!null? cmdIsNotNull:boolean ( arg@(optional):string ) {
|
|
1966
2075
|
templates {
|
|
1967
|
-
cpp ((e 1) ".empty() == false ")
|
|
2076
|
+
cpp ((e 1) ".empty() == false ")
|
|
1968
2077
|
}
|
|
1969
|
-
}
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
; buffer is a value type (std::vector<uint8_t>) in C++; the generic
|
|
2081
|
+
; `!= NULL` path below does not compile. Present == non-empty, matching
|
|
2082
|
+
; the string overload above.
|
|
2083
|
+
!null? cmdIsNotNull:boolean ( arg@(optional):buffer ) {
|
|
2084
|
+
templates {
|
|
2085
|
+
cpp ((e 1) ".empty() == false ")
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
1970
2088
|
|
|
1971
2089
|
!null? cmdIsNotNull:boolean ( arg@(optional):T ) {
|
|
1972
2090
|
templates {
|
|
@@ -2123,7 +2241,7 @@ case class customException(smth:String) extends Exception
|
|
|
2123
2241
|
php ( "trim(" (e 1 ) ")")
|
|
2124
2242
|
cpp ( "r_cpp_trim( " (e 1) ")" (imp "<cctype>") (imp "<string>") (imp "<algorithm>")
|
|
2125
2243
|
(create_polyfill "
|
|
2126
|
-
inline std::string r_cpp_trim(std::string &s)
|
|
2244
|
+
inline std::string r_cpp_trim(const std::string &s)
|
|
2127
2245
|
{
|
|
2128
2246
|
auto wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);});
|
|
2129
2247
|
auto wsback=std::find_if_not(s.rbegin(),s.rend(),[](int c){return std::isspace(c);}).base();
|
|
@@ -3016,10 +3134,10 @@ std::vector<std::string> r_get_keys_of_map( std::map<std::string, T> orig_map )
|
|
|
3016
3134
|
(create_polyfill '
|
|
3017
3135
|
std::string r_utf8_substr(const std::string& str, int start_i, int leng_i)
|
|
3018
3136
|
{
|
|
3019
|
-
|
|
3020
|
-
|
|
3137
|
+
size_t start = (size_t)start_i;
|
|
3138
|
+
size_t leng = (size_t)leng_i;
|
|
3021
3139
|
if (leng==0) { return ""; }
|
|
3022
|
-
|
|
3140
|
+
size_t c, i, ix, q, min= std::string::npos, max= std::string::npos;
|
|
3023
3141
|
for (q=0, i=0, ix=str.length(); i < ix; i++, q++)
|
|
3024
3142
|
{
|
|
3025
3143
|
if (q==start){ min=i; }
|
|
@@ -3538,6 +3656,49 @@ r_optional_primitive<int> cpp_get_map_int_value( std::map<" (r_ktype 1) ", int>
|
|
|
3538
3656
|
)
|
|
3539
3657
|
}
|
|
3540
3658
|
}
|
|
3659
|
+
|
|
3660
|
+
get cmdGet@(optional weak):double ( map:[K:double] key:K ) {
|
|
3661
|
+
templates {
|
|
3662
|
+
cpp( "cpp_get_map_string_double_value(" (e 1) ", " (e 2) ")"
|
|
3663
|
+
(create_polyfill
|
|
3664
|
+
"
|
|
3665
|
+
template <class T>
|
|
3666
|
+
class r_optional_primitive {
|
|
3667
|
+
public:
|
|
3668
|
+
bool has_value;
|
|
3669
|
+
T value;
|
|
3670
|
+
r_optional_primitive<T> & operator=(const r_optional_primitive<T> & rhs) {
|
|
3671
|
+
has_value = rhs.has_value;
|
|
3672
|
+
value = rhs.value;
|
|
3673
|
+
return *this;
|
|
3674
|
+
}
|
|
3675
|
+
r_optional_primitive<T> & operator=(const T a_value) {
|
|
3676
|
+
has_value = true;
|
|
3677
|
+
value = a_value;
|
|
3678
|
+
return *this;
|
|
3679
|
+
}
|
|
3680
|
+
};
|
|
3681
|
+
"
|
|
3682
|
+
)
|
|
3683
|
+
(macro
|
|
3684
|
+
("
|
|
3685
|
+
r_optional_primitive<double> cpp_get_map_string_double_value( std::map<std::string, double> m , std::string key) {
|
|
3686
|
+
r_optional_primitive<double> result;
|
|
3687
|
+
auto it = m.find(key);
|
|
3688
|
+
if (it != m.end()) {
|
|
3689
|
+
result.value = it->second;
|
|
3690
|
+
result.has_value = true;
|
|
3691
|
+
}
|
|
3692
|
+
return result;
|
|
3693
|
+
}")
|
|
3694
|
+
)
|
|
3695
|
+
)
|
|
3696
|
+
ranger ( "(get " (e 1) " " (e 2) ")")
|
|
3697
|
+
llvm ( "(get " (e 1) " " (e 2) ")")
|
|
3698
|
+
es6 ( "( " (e 1) ".hasOwnProperty(" (e 2) ") ? " (e 1) "[" (e 2) "] : undefined )" )
|
|
3699
|
+
* ( (e 1) "[" (e 2) "]" )
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3541
3702
|
|
|
3542
3703
|
get cmdGet@(optional weak):T ( map:[K:T] key:K ) {
|
|
3543
3704
|
templates {
|
|
@@ -3545,6 +3706,21 @@ r_optional_primitive<int> cpp_get_map_int_value( std::map<" (r_ktype 1) ", int>
|
|
|
3545
3706
|
llvm ( "(get " (e 1) " " (e 2) ")")
|
|
3546
3707
|
java7 ( (e 1) ".get(" (e 2) ")" )
|
|
3547
3708
|
rust ( (e 1) ".get(" (e 2) ")" )
|
|
3709
|
+
cpp ( "r_map_get_val(" (e 1) ", " (e 2) ")"
|
|
3710
|
+
(create_polyfill
|
|
3711
|
+
"
|
|
3712
|
+
// Map read that never inserts on miss (unlike std::map::operator[]) and does a
|
|
3713
|
+
// single lookup. Returns a default-constructed value (e.g. null shared_ptr)
|
|
3714
|
+
// when the key is absent.
|
|
3715
|
+
template<typename M, typename K>
|
|
3716
|
+
typename M::mapped_type r_map_get_val( const M & m , const K & key) {
|
|
3717
|
+
auto it = m.find(key);
|
|
3718
|
+
if ( it != m.end() ) { return it->second; }
|
|
3719
|
+
return typename M::mapped_type();
|
|
3720
|
+
}
|
|
3721
|
+
"
|
|
3722
|
+
)
|
|
3723
|
+
)
|
|
3548
3724
|
|
|
3549
3725
|
scala ( (e 1) ".get(" (e 2) ")" )
|
|
3550
3726
|
|
|
@@ -5007,6 +5183,20 @@ fun r_read_key(): String {
|
|
|
5007
5183
|
swift6 ((e 1) ".lowercased()")
|
|
5008
5184
|
scala ((e 1) ".toLowerCase()")
|
|
5009
5185
|
python ((e 1) ".lower()")
|
|
5186
|
+
cpp (
|
|
5187
|
+
"r_cpp_str_to_lowercase(" (e 1) ")"
|
|
5188
|
+
(imp "<algorithm>")
|
|
5189
|
+
(imp "<string>")
|
|
5190
|
+
(create_polyfill "
|
|
5191
|
+
std::string r_cpp_str_to_lowercase(std::string original)
|
|
5192
|
+
{
|
|
5193
|
+
std::string str = original;
|
|
5194
|
+
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
|
5195
|
+
return str;
|
|
5196
|
+
}
|
|
5197
|
+
")
|
|
5198
|
+
|
|
5199
|
+
)
|
|
5010
5200
|
}
|
|
5011
5201
|
}
|
|
5012
5202
|
to_uppercase _:string (s:string) {
|