ranger-compiler 3.0.4 → 3.1.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 +32 -0
- package/README.md +35 -1
- package/dist/Lang.rgr +142 -9
- package/dist/lib/JSON.rgr +31 -4
- package/dist/lib/RangerProcess.rgr +304 -0
- package/dist/lib/stdlib.rgr +33 -3
- package/dist/package.json +1 -1
- package/dist/rgrc.js +22720 -21131
- package/package.json +2 -2
- package/dist/lib/lib/ACEEditor.rgr +0 -5
- package/dist/lib/lib/Ajax.rgr +0 -196
- package/dist/lib/lib/CmdParams.rgr +0 -72
- package/dist/lib/lib/Crypto.rgr +0 -90
- package/dist/lib/lib/DOMLib.rgr +0 -307
- package/dist/lib/lib/Engine3D.rgr +0 -870
- package/dist/lib/lib/ImmutableVector.rgr +0 -364
- package/dist/lib/lib/IndexedDB.rgr +0 -32
- package/dist/lib/lib/JSON.rgr +0 -702
- package/dist/lib/lib/JinxProcess.rgr +0 -838
- package/dist/lib/lib/SQL.rgr +0 -44
- package/dist/lib/lib/ServiceLib.rgr +0 -9
- package/dist/lib/lib/Storage.rgr +0 -89
- package/dist/lib/lib/Time.rgr +0 -75
- package/dist/lib/lib/Timers.rgr +0 -176
- package/dist/lib/lib/TypedArrays.rgr +0 -14
- package/dist/lib/lib/ViewLib.rgr +0 -851
- package/dist/lib/lib/WebLib.rgr +0 -47
- package/dist/lib/lib/WebServerLib.rgr +0 -137
- package/dist/lib/lib/ranger-dir.rgr +0 -175
- package/dist/lib/lib/stdlib.rgr +0 -665
- package/dist/lib/lib/stdops.rgr +0 -168
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.1.0] - 2026-06-02
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`ProcessUiHost` notify suppress** — `beginSuppressUiNotify` / `endSuppressUiNotify` / `isUiNotifySuppressed` for batching parent↔child sync without re-entrant UI notify loops ([PROCESS_UI_NOTIFY.md](PROCESS_UI_NOTIFY.md))
|
|
15
|
+
- **Process view DTO regression fixture** — [tests/fixtures/process_view_dto_assign.rgr](tests/fixtures/process_view_dto_assign.rgr) (cross-class field assignment with method call on RHS)
|
|
16
|
+
- **Docs** — [PROCESS_UI_NOTIFY.md](PROCESS_UI_NOTIFY.md), [PROCESS_UI_VIEW_MODELS.md](PROCESS_UI_VIEW_MODELS.md); README `@process` quick start
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **Parser: assignment RHS method calls** — `row.field = this.helper(index)` no longer splits the call into invalid `=` operands ([PROCESS_UI_VIEW_MODELS.md](PROCESS_UI_VIEW_MODELS.md)); fix in [compiler/ng_RangerFlowParser.rgr](compiler/ng_RangerFlowParser.rgr) (`repairAssignMethodCallRhs`)
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Version bumped from `3.0.5` to `3.1.0`
|
|
25
|
+
|
|
26
|
+
## [3.0.5] - 2026-05-29
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **`@process` messaging** — `proc_send` operator; `receiveMessage` on `RangerProcessBase`; `find_process` / `ProcessNameRegistry.findProcess`
|
|
31
|
+
- **TypeScript named paths** — `-typescript` emits `ProcessPath`, `findProcess` on registry, singleton `new` returns shared instance
|
|
32
|
+
- **`RangerProcess.rgr` in npm lib** — included under `dist/lib/` when running `npm run build:dist`
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- **`build:dist:copy`** — `rm -rf dist/lib` before copy so new lib files (e.g. `RangerProcess.rgr`) land in `dist/lib/` instead of nested `dist/lib/lib/`
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- Version bumped from `3.0.4` to `3.0.5`
|
|
41
|
+
|
|
10
42
|
## [3.0.4] - 2026-05-12
|
|
11
43
|
|
|
12
44
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Ranger cross language compiler
|
|
2
2
|
|
|
3
|
-
**Version 3.0
|
|
3
|
+
**Version 3.1.0** | Status: `experimental`
|
|
4
4
|
|
|
5
5
|
Ranger is a self-hosting cross-language compiler for writing portable algorithms, parsers, generators, and small tools once and compiling them to multiple target languages.
|
|
6
6
|
|
|
@@ -25,8 +25,42 @@ Ranger is best approached today as a compiler and language lab with practical mu
|
|
|
25
25
|
|
|
26
26
|
If you want one sentence of positioning: Ranger is currently more convincing as a portable algorithm compiler / DSL toolchain than as a drop-in replacement for mainstream application languages.
|
|
27
27
|
|
|
28
|
+
## `@process` runtime (experimental)
|
|
29
|
+
|
|
30
|
+
Ranger can mark classes with `@process` to get a **small object runtime** in generated code (especially JavaScript/TypeScript): parent/child tree, instance registry, `proc_start` / `proc_stop`, and UI refresh via `markStateDirty()`.
|
|
31
|
+
|
|
32
|
+
| Piece | What it does |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `@process` / `@process(true)` | Process instance with lifecycle hooks (`start`, `stop`, …) |
|
|
35
|
+
| `proc_start` / `proc_stop` | Activate or tear down a process subtree (children first) |
|
|
36
|
+
| `proc_send target handler arg…` | Typed message dispatch to `fn on…` handlers on a live instance |
|
|
37
|
+
| `ProcessNameRegistry.findProcess(path)` | Lookup by `@name("app.foo")` (TypeScript path literals when using `-typescript`) |
|
|
38
|
+
| `markStateDirty()` | Bump generation + notify host (`ProcessUiHost`) for UI binding |
|
|
39
|
+
| `beginSuppressUiNotify` / `endSuppressUiNotify` | Batch parent↔child sync without notify storms |
|
|
40
|
+
|
|
41
|
+
**Typical app shape:** domain logic and UI flags live in `@process` classes; **view DTOs** are plain Ranger classes filled by a builder; React/CLI/native hosts subscribe to `ProcessUiHost` and call `findProcess` — I/O and async work stay in the host (Node, Swift, …), not inside the process bytecode.
|
|
42
|
+
|
|
43
|
+
```ranger
|
|
44
|
+
class CounterPage @process @name("app.counter") extends RangerProcessBase {
|
|
45
|
+
def count:int 0
|
|
46
|
+
fn onUiIncrement:void () {
|
|
47
|
+
count = count + 1
|
|
48
|
+
this.markStateDirty()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// Host (TypeScript): wire notify once, then bind UI to process fields
|
|
55
|
+
const host = ProcessUiHost.__singleton();
|
|
56
|
+
host.notifyPath = (path) => { /* sync view model + re-render */ };
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Docs:** [PROCESS_MVP.md](PROCESS_MVP.md) (scope), [PROCESS_STATUS.md](PROCESS_STATUS.md) (compiler checklist), [PROCESS_UI_NOTIFY.md](PROCESS_UI_NOTIFY.md) (notify batching), [PROCESS_UI_VIEW_MODELS.md](PROCESS_UI_VIEW_MODELS.md) (view DTO assignment). **Gallery:** [process_counter_board](gallery/process_counter_board/README.md). **Pilot:** [realtrainer `app-ranger` Active Workout demo](https://github.com/terotests/realtrainer/tree/copilot/create-watch-ui-components/app-ranger/demo/active-workout-process).
|
|
60
|
+
|
|
28
61
|
## Where To Start
|
|
29
62
|
|
|
63
|
+
- [Online playground](https://terotests.github.io/Ranger/) — try Ranger in the browser (`playground/`, Vite + current compiler)
|
|
30
64
|
- `README.md` - language overview, installation, and syntax notes
|
|
31
65
|
- `ai/QUICKREF.md` - fast reference for syntax and core concepts
|
|
32
66
|
- `ai/INSTRUCTIONS.md` - fuller language guide for operators, templates, and compiler concepts
|
package/dist/Lang.rgr
CHANGED
|
@@ -193,9 +193,11 @@ language {
|
|
|
193
193
|
templates {
|
|
194
194
|
ranger ("(cast " (e 1) " " (e 2) ":" (typeof 2) " )")
|
|
195
195
|
ts ( (e 1) " as " (typeof 2) )
|
|
196
|
+
kotlin ( (e 1) " as " (typeof 2) )
|
|
196
197
|
es6 ( (e 1) )
|
|
197
198
|
java7 ( "((" (typeof 2) ")" (e 1) ")" )
|
|
198
199
|
swift3 ( (e 1) " as " (typeof 2) "" )
|
|
200
|
+
swift6 ( (e 1) " as " (typeof 2) "" )
|
|
199
201
|
cpp ( "mpark::get<" (typeof 2) ">(" (e 1) ")"
|
|
200
202
|
|
|
201
203
|
(plugin 'makefile' ((dep 'variant.hpp' 'https://github.com/mpark/variant/releases/download/v1.2.2/variant.hpp')))
|
|
@@ -493,6 +495,7 @@ func r_io_get_env( name string) *GoNullable {
|
|
|
493
495
|
swift6 ("CommandLine.arguments[" (e 1) " + 1]")
|
|
494
496
|
es6 ( "process.argv[ 2 + " (e 1) "]")
|
|
495
497
|
rust ( "std::env::args().nth((" (e 1) " + 1) as usize).unwrap_or_default()")
|
|
498
|
+
kotlin ( "args[" (e 1) "]")
|
|
496
499
|
ranger ("( shell_arg " (e 1) " )")
|
|
497
500
|
}
|
|
498
501
|
}
|
|
@@ -508,6 +511,7 @@ func r_io_get_env( name string) *GoNullable {
|
|
|
508
511
|
go ( "int64( len( os.Args) - 1 )" (imp "os"))
|
|
509
512
|
es6 ( "(process.argv.length - 2)" )
|
|
510
513
|
rust ( "(std::env::args().len() as i64 - 1)")
|
|
514
|
+
kotlin ( "(args.size - 1)")
|
|
511
515
|
ranger ("( shell_arg_cnt )")
|
|
512
516
|
}
|
|
513
517
|
}
|
|
@@ -930,6 +934,7 @@ fn r_read_file(path: &str, filename: &str) -> Option<String> {
|
|
|
930
934
|
}
|
|
931
935
|
"))
|
|
932
936
|
es6 ( "(require('fs').readFileSync( " (e 1) " + '/' + " (e 2) " , 'utf8'))" )
|
|
937
|
+
kotlin ( "try { java.io.File(if (" (e 1) ".isEmpty()) " (e 2) " else " (e 1) " + \"/\" + " (e 2) ").readText() } catch (_: Exception) { null }" (imp "java.io.File") )
|
|
933
938
|
}
|
|
934
939
|
}
|
|
935
940
|
|
|
@@ -2716,6 +2721,7 @@ std::vector<uint8_t> r_buffer_read_file(const std::string& path, const std::stri
|
|
|
2716
2721
|
ranger ( "(to_int " (e 1) ")")
|
|
2717
2722
|
csharp ( "(int)" (e 1 ) "" )
|
|
2718
2723
|
swift3 ( "Int(" (e 1 ) ")" )
|
|
2724
|
+
swift6 ( "Int(" (e 1 ) ")" )
|
|
2719
2725
|
kotlin ( (e 1 ) ".toInt()" )
|
|
2720
2726
|
scala ( (e 1 ) ".toInt" )
|
|
2721
2727
|
rust ( (e 1 ) " as i64 " )
|
|
@@ -3165,9 +3171,50 @@ func r_str_2_i64(s string) *GoNullable {
|
|
|
3165
3171
|
)
|
|
3166
3172
|
php ( "is_numeric(" (e 1) ") ? intval(" (e 1) ") : NULL ")
|
|
3167
3173
|
scala ( "Try(" (e 1) ".toInt).toOption" (imp "scala.util.Try"))
|
|
3168
|
-
kotlin (
|
|
3169
|
-
|
|
3170
|
-
|
|
3174
|
+
kotlin ( "rangerStr2IntPrefix(" (e 1) ")"
|
|
3175
|
+
(create_polyfill "
|
|
3176
|
+
fun rangerStr2IntPrefix(s: String): Int? {
|
|
3177
|
+
val m = Regex(\"^-?\\\\d+\").find(s) ?: return null
|
|
3178
|
+
return m.value.toIntOrNull()
|
|
3179
|
+
}
|
|
3180
|
+
")
|
|
3181
|
+
)
|
|
3182
|
+
swift3 ("rangerStr2IntPrefix(" (e 1) ")"
|
|
3183
|
+
(create_polyfill "
|
|
3184
|
+
func rangerStr2IntPrefix(_ s: String) -> Int? {
|
|
3185
|
+
var i = s.startIndex
|
|
3186
|
+
if i < s.endIndex && s[i] == \"-\" {
|
|
3187
|
+
i = s.index(after: i)
|
|
3188
|
+
}
|
|
3189
|
+
var end = i
|
|
3190
|
+
while end < s.endIndex && s[end].isNumber {
|
|
3191
|
+
end = s.index(after: end)
|
|
3192
|
+
}
|
|
3193
|
+
if end == i {
|
|
3194
|
+
return nil
|
|
3195
|
+
}
|
|
3196
|
+
return Int(s[i..<end])
|
|
3197
|
+
}
|
|
3198
|
+
")
|
|
3199
|
+
)
|
|
3200
|
+
swift6 ("rangerStr2IntPrefix(" (e 1) ")"
|
|
3201
|
+
(create_polyfill "
|
|
3202
|
+
func rangerStr2IntPrefix(_ s: String) -> Int? {
|
|
3203
|
+
var i = s.startIndex
|
|
3204
|
+
if i < s.endIndex && s[i] == \"-\" {
|
|
3205
|
+
i = s.index(after: i)
|
|
3206
|
+
}
|
|
3207
|
+
var end = i
|
|
3208
|
+
while end < s.endIndex && s[end].isNumber {
|
|
3209
|
+
end = s.index(after: end)
|
|
3210
|
+
}
|
|
3211
|
+
if end == i {
|
|
3212
|
+
return nil
|
|
3213
|
+
}
|
|
3214
|
+
return Int(s[i..<end])
|
|
3215
|
+
}
|
|
3216
|
+
")
|
|
3217
|
+
)
|
|
3171
3218
|
csharp( "Int32.Parse(" (e 1) ")" )
|
|
3172
3219
|
rust ( (e 1) ".parse::<i64>().ok()" )
|
|
3173
3220
|
* ( "isNaN( parseInt(" (e 1) ") ) ? undefined : parseInt(" (e 1) ")")
|
|
@@ -3247,7 +3294,7 @@ Double _getDoubleOrNull ( String str ) {
|
|
|
3247
3294
|
scala ( "Try(" (e 1) ".toDouble).toOption" (imp "scala.util.Try"))
|
|
3248
3295
|
kotlin ( (e 1) ".toDoubleOrNull()")
|
|
3249
3296
|
swift3 ("Double(" (e 1) ")")
|
|
3250
|
-
|
|
3297
|
+
swift6 ("Double(" (e 1) ")")
|
|
3251
3298
|
* ( "isNaN( parseFloat(" (e 1) ") ) ? undefined : parseFloat(" (e 1) ")")
|
|
3252
3299
|
}
|
|
3253
3300
|
}
|
|
@@ -4090,9 +4137,12 @@ public <T> T _arr_extract( ArrayList<T> list, Integer i ) {
|
|
|
4090
4137
|
"const readline = require('readline');" nl
|
|
4091
4138
|
"readline.emitKeypressEvents(process.stdin);" nl
|
|
4092
4139
|
"if (process.stdin.isTTY) { process.stdin.setRawMode(true); }" nl
|
|
4093
|
-
"process.stdin.
|
|
4094
|
-
"
|
|
4095
|
-
"if (
|
|
4140
|
+
"process.stdin.resume();" nl
|
|
4141
|
+
"process.stdin.on('keypress', (str, key) => {" nl I
|
|
4142
|
+
"if (key && key.ctrl && key.name === 'c') { process.stdout.write('\\x1b[?25h'); process.exit(); }" nl
|
|
4143
|
+
"var __rgr_k = \"\";" nl
|
|
4144
|
+
"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
|
|
4145
|
+
"if (__rgr_k !== \"\") { " (e 1) " = __rgr_k; global.r_key_queue.push(__rgr_k); }" nl
|
|
4096
4146
|
(block 2)
|
|
4097
4147
|
i nl "});" nl
|
|
4098
4148
|
)
|
|
@@ -4519,7 +4569,11 @@ var r_key_queue: [String] = []
|
|
|
4519
4569
|
var r_key_queue_lock = NSLock()
|
|
4520
4570
|
|
|
4521
4571
|
func r_setup_raw_mode() {
|
|
4522
|
-
let
|
|
4572
|
+
let p = Process()
|
|
4573
|
+
p.executableURL = URL(fileURLWithPath: \"/bin/stty\")
|
|
4574
|
+
p.arguments = [\"-F\", \"/dev/tty\", \"cbreak\", \"min\", \"1\", \"-echo\"]
|
|
4575
|
+
try? p.run()
|
|
4576
|
+
p.waitUntilExit()
|
|
4523
4577
|
}
|
|
4524
4578
|
|
|
4525
4579
|
func r_read_key() -> String? {
|
|
@@ -4609,7 +4663,11 @@ func r_read_key() -> String? {
|
|
|
4609
4663
|
}
|
|
4610
4664
|
#else
|
|
4611
4665
|
func r_setup_raw_mode() {
|
|
4612
|
-
let
|
|
4666
|
+
let p = Process()
|
|
4667
|
+
p.executableURL = URL(fileURLWithPath: \"/bin/stty\")
|
|
4668
|
+
p.arguments = [\"-F\", \"/dev/tty\", \"cbreak\", \"min\", \"1\", \"-echo\"]
|
|
4669
|
+
try? p.run()
|
|
4670
|
+
p.waitUntilExit()
|
|
4613
4671
|
}
|
|
4614
4672
|
|
|
4615
4673
|
func r_read_key() -> String? {
|
|
@@ -4863,8 +4921,10 @@ std::string r_cpp_str_to_uppercase(std::string original)
|
|
|
4863
4921
|
go ("float64( " (e 1) " )")
|
|
4864
4922
|
es6 ( (e 1) )
|
|
4865
4923
|
swift3 ("Double(" (e 1) ")")
|
|
4924
|
+
swift6 ("Double(" (e 1) ")")
|
|
4866
4925
|
scala ( (e 1) '.toDouble')
|
|
4867
4926
|
java7 ("Double.valueOf(" (e 1) ")")
|
|
4927
|
+
kotlin ( (e 1) ".toDouble()" )
|
|
4868
4928
|
cpp ("(double)(" (e 1) ")")
|
|
4869
4929
|
php ( (e 1) )
|
|
4870
4930
|
rust ("(" (e 1) " as f64)")
|
|
@@ -5280,6 +5340,79 @@ std::string r_cpp_str_to_uppercase(std::string original)
|
|
|
5280
5340
|
}
|
|
5281
5341
|
}
|
|
5282
5342
|
|
|
5343
|
+
; Process lifecycle operators (distinct from HttpServer start/stop)
|
|
5344
|
+
find_process@(moves@( 1 )) cmdFindProcess:RangerProcessBase@(optional) (path:string) {
|
|
5345
|
+
templates {
|
|
5346
|
+
es6 ( "ProcessNameRegistry.__singleton().findByPath(" (e 1) ")" )
|
|
5347
|
+
kotlin ( "ProcessNameRegistry.__singleton().findByPath(" (e 1) ")" )
|
|
5348
|
+
swift6 ( "ProcessNameRegistry.__singleton().findByPath(" (e 1) ")" )
|
|
5349
|
+
swift3 ( "ProcessNameRegistry.__singleton().findByPath(" (e 1) ")" )
|
|
5350
|
+
* ( "/* find_process not implemented */" )
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
|
|
5354
|
+
; proc_send is lowered in ng_RangerProcessProcSend.rgr (typed handler methods, overload match).
|
|
5355
|
+
|
|
5356
|
+
proc_start@(moves@( 1 )) cmdProcStart:void (proc:RangerProcessBase) {
|
|
5357
|
+
templates {
|
|
5358
|
+
es6 ( "ProcessRuntime.startInstance(" (e 1) ")" )
|
|
5359
|
+
kotlin ( "ProcessRuntime.startInstance(" (e 1) ")" )
|
|
5360
|
+
swift6 ( "ProcessRuntime.startInstance(proc: " (e 1) ")" )
|
|
5361
|
+
swift3 ( "ProcessRuntime.startInstance(proc: " (e 1) ")" )
|
|
5362
|
+
* ( "/* proc_start not implemented */" )
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
5365
|
+
|
|
5366
|
+
proc_stop@(moves@( 1 )) cmdProcStopInst:void (proc:RangerProcessBase) {
|
|
5367
|
+
templates {
|
|
5368
|
+
es6 ( "ProcessRuntime.stopInstance(" (e 1) ")" )
|
|
5369
|
+
kotlin ( "ProcessRuntime.stopInstance(" (e 1) ")" )
|
|
5370
|
+
swift6 ( "ProcessRuntime.stopInstance(proc: " (e 1) ")" )
|
|
5371
|
+
swift3 ( "ProcessRuntime.stopInstance(proc: " (e 1) ")" )
|
|
5372
|
+
* ( "/* proc_stop instance not implemented */" )
|
|
5373
|
+
}
|
|
5374
|
+
}
|
|
5375
|
+
|
|
5376
|
+
proc_stop@(moves@( 1 )) cmdProcStopProcessId:void (processId:int) {
|
|
5377
|
+
templates {
|
|
5378
|
+
es6 ( "ProcessRuntime.stopByProcessId(" (e 1) ")" )
|
|
5379
|
+
kotlin ( "ProcessRuntime.stopByProcessId(" (e 1) ")" )
|
|
5380
|
+
swift6 ( "ProcessRuntime.stopByProcessId(" (e 1) ")" )
|
|
5381
|
+
swift3 ( "ProcessRuntime.stopByProcessId(" (e 1) ")" )
|
|
5382
|
+
* ( "/* proc_stop processId not implemented */" )
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
5385
|
+
|
|
5386
|
+
proc_stop@(moves@( 1 )) cmdProcStopClass:void (className:string) {
|
|
5387
|
+
templates {
|
|
5388
|
+
es6 ( "ProcessRuntime.stopByClassName(" (e 1) ")" )
|
|
5389
|
+
kotlin ( "ProcessRuntime.stopByClassName(" (e 1) ")" )
|
|
5390
|
+
swift6 ( "ProcessRuntime.stopByClassName(" (e 1) ")" )
|
|
5391
|
+
swift3 ( "ProcessRuntime.stopByClassName(" (e 1) ")" )
|
|
5392
|
+
* ( "/* proc_stop class not implemented */" )
|
|
5393
|
+
}
|
|
5394
|
+
}
|
|
5395
|
+
|
|
5396
|
+
proc_hibernate@(moves@( 1 )) cmdProcHibernate:string (proc:RangerProcessBase) {
|
|
5397
|
+
templates {
|
|
5398
|
+
es6 ( "ProcessRuntime.hibernateInstance(" (e 1) ")" )
|
|
5399
|
+
kotlin ( "ProcessRuntime.hibernateInstance(" (e 1) ")" )
|
|
5400
|
+
swift6 ( "ProcessRuntime.hibernateInstance(proc: " (e 1) ")" )
|
|
5401
|
+
swift3 ( "ProcessRuntime.hibernateInstance(proc: " (e 1) ")" )
|
|
5402
|
+
* ( "\"\"" )
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5405
|
+
|
|
5406
|
+
proc_wakeup@(moves@( 2 )) cmdProcWakeup:void (proc:RangerProcessBase state:string) {
|
|
5407
|
+
templates {
|
|
5408
|
+
es6 ( "ProcessRuntime.wakeupInstance(" (e 1) ", " (e 2) ")" )
|
|
5409
|
+
kotlin ( "ProcessRuntime.wakeupInstance(" (e 1) ", " (e 2) ")" )
|
|
5410
|
+
swift6 ( "ProcessRuntime.wakeupInstance(proc: " (e 1) ", state: " (e 2) ")" )
|
|
5411
|
+
swift3 ( "ProcessRuntime.wakeupInstance(proc: " (e 1) ", state: " (e 2) ")" )
|
|
5412
|
+
* ( "/* proc_wakeup not implemented */" )
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
|
|
5283
5416
|
}
|
|
5284
5417
|
|
|
5285
5418
|
|
package/dist/lib/JSON.rgr
CHANGED
|
@@ -90,10 +90,37 @@ operators {
|
|
|
90
90
|
java7 ( (e 1) ".isNull( " (e 2) " ) ? null : " (e 1) ".optString(" (e 2) ") " )
|
|
91
91
|
kotlin ( "(if (" (e 1) ".isNull(" (e 2) ")) null else " (e 1) ".optString(" (e 2) "))"
|
|
92
92
|
(create_polyfill "
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
open class JSONObject {
|
|
94
|
+
protected open val values: MutableMap<String, Any?> = mutableMapOf()
|
|
95
|
+
open fun isNull(key: String): Boolean = !values.containsKey(key) || values[key] == null
|
|
96
|
+
open fun optString(key: String): String? = values[key]?.toString()
|
|
97
|
+
open fun optInt(key: String): Int? = when (val v = values[key]) {
|
|
98
|
+
is Int -> v
|
|
99
|
+
is Double -> v.toInt()
|
|
100
|
+
is String -> v.toIntOrNull()
|
|
101
|
+
else -> null
|
|
102
|
+
}
|
|
103
|
+
open fun optDouble(key: String): Double? = when (val v = values[key]) {
|
|
104
|
+
is Double -> v
|
|
105
|
+
is Int -> v.toDouble()
|
|
106
|
+
is String -> v.toDoubleOrNull()
|
|
107
|
+
else -> null
|
|
108
|
+
}
|
|
109
|
+
open fun optBoolean(key: String): Boolean? = values[key] as? Boolean
|
|
110
|
+
open fun getJSONObject(key: String): JSONObject? = values[key] as? JSONObject
|
|
111
|
+
open fun getJSONArray(key: String): JSONArray? = values[key] as? JSONArray
|
|
112
|
+
open fun put(key: String, value: Any?): JSONObject { values[key] = value; return this }
|
|
113
|
+
override fun toString(): String = values.toString()
|
|
114
|
+
constructor(source: String) : this() {}
|
|
115
|
+
constructor()
|
|
116
|
+
}
|
|
117
|
+
open class JSONArray : ArrayList<Any?>() {
|
|
118
|
+
open fun length(): Int = size
|
|
119
|
+
override fun get(index: Int): Any? = if (index in indices) super.get(index) else null
|
|
120
|
+
override fun toString(): String = joinToString(prefix = \"[\", postfix = \"]\")
|
|
121
|
+
}
|
|
122
|
+
typealias JSONDataObject = JSONObject
|
|
123
|
+
typealias JSONArrayObject = JSONArray
|
|
97
124
|
")
|
|
98
125
|
)
|
|
99
126
|
go ( "r_get_opt_json_string(" (e 1) ", " (e 2) ")"
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
|
|
2
|
+
Import "stdops.rgr"
|
|
3
|
+
|
|
4
|
+
class RangerProcessBase {
|
|
5
|
+
def __rangerId:int 0
|
|
6
|
+
def __rangerParentId:int 0
|
|
7
|
+
def __rangerTypeId:int 0
|
|
8
|
+
def __rangerClassName:string ""
|
|
9
|
+
def __rangerPath:string ""
|
|
10
|
+
def __rangerStateGeneration:int 0
|
|
11
|
+
def __rangerParent:RangerProcessBase@(optional)
|
|
12
|
+
def __rangerChildren:[RangerProcessBase]
|
|
13
|
+
|
|
14
|
+
; Bump revision only (e.g. parent sync merging child pending*). Prefer over markStateDirty in sync blocks.
|
|
15
|
+
fn bumpStateGeneration:void () {
|
|
16
|
+
__rangerStateGeneration = __rangerStateGeneration + 1
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
fn markStateDirty:void () {
|
|
20
|
+
__rangerStateGeneration = __rangerStateGeneration + 1
|
|
21
|
+
this.flushUiNotify()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
; Notify host without bumping __rangerStateGeneration (after suppressUiNotify batch).
|
|
25
|
+
fn flushUiNotify:void () {
|
|
26
|
+
def uiHost:ProcessUiHost (ProcessUiHost.__singleton())
|
|
27
|
+
if (uiHost.isUiNotifySuppressed()) {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
if ((strlen __rangerPath) > 0) {
|
|
31
|
+
uiHost.notifyPath(__rangerPath)
|
|
32
|
+
}
|
|
33
|
+
if (__rangerId != 0) {
|
|
34
|
+
uiHost.notifyId(__rangerId)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fn __rangerOnDescendantUiChanged:void (child:RangerProcessBase hint:string) {
|
|
39
|
+
this.markStateDirty()
|
|
40
|
+
def parent@(optional):RangerProcessBase (__rangerParent)
|
|
41
|
+
if ((null? parent) == false) {
|
|
42
|
+
def p:RangerProcessBase (unwrap parent)
|
|
43
|
+
if (p.__rangerId != 0) {
|
|
44
|
+
p.__rangerOnDescendantUiChanged(child hint)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fn __rangerTrackChild:void (child:RangerProcessBase) {
|
|
50
|
+
push __rangerChildren child
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fn __rangerClearChildren:void () {
|
|
54
|
+
def empty:[RangerProcessBase]
|
|
55
|
+
__rangerChildren = empty
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fn __rangerInvokeStart:void () {
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fn __rangerInvokeStop:void () {
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fn __rangerInvokeHibernate:string () {
|
|
65
|
+
return ""
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fn __rangerInvokeWakeup:void (state:string) {
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fn __rangerStopSubtree:void () {
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
; Legacy hook; prefer typed proc_send → handler methods on @process classes.
|
|
75
|
+
fn receiveMessage:void (name:string value:string) {
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
; Single app-wide ID space for all @process instances (roots and children).
|
|
80
|
+
class ProcessIdRegistry @singleton(true) {
|
|
81
|
+
def next:int 1
|
|
82
|
+
|
|
83
|
+
fn allocate:int () {
|
|
84
|
+
def id (this.next)
|
|
85
|
+
this.next = id + 1
|
|
86
|
+
return id
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
class ProcessNameRegistry @singleton(true) {
|
|
91
|
+
def byPath:[string:RangerProcessBase]
|
|
92
|
+
|
|
93
|
+
fn register:void (path:string proc:RangerProcessBase) {
|
|
94
|
+
if ((strlen path) == 0) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
if (proc.__rangerId == 0) {
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
def existing@(optional):RangerProcessBase (get byPath path)
|
|
101
|
+
if ((null? existing) == false) {
|
|
102
|
+
def old:RangerProcessBase (unwrap existing)
|
|
103
|
+
if (old.__rangerId != 0 && old.__rangerId != proc.__rangerId) {
|
|
104
|
+
print ("ERROR: duplicate live @name process path: " + path)
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
set byPath path proc
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
fn unregister:void (path:string) {
|
|
112
|
+
; Map entry may remain until overwritten; hasLive/findByPath check __rangerId.
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fn findByPath@(optional):RangerProcessBase (path:string) {
|
|
116
|
+
return (get byPath path)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
fn hasLive:boolean (path:string) {
|
|
120
|
+
def found@(optional):RangerProcessBase (get byPath path)
|
|
121
|
+
if (null? found) {
|
|
122
|
+
return false
|
|
123
|
+
}
|
|
124
|
+
def inst:RangerProcessBase (unwrap found)
|
|
125
|
+
if (inst.__rangerId == 0) {
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
return true
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fn unbindIfConfigured:void (proc:RangerProcessBase) {
|
|
132
|
+
if ((strlen proc.__rangerPath) > 0) {
|
|
133
|
+
this.unregister(proc.__rangerPath)
|
|
134
|
+
proc.__rangerPath = ""
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
; Host-side UI subscriptions (React useProcess wires listeners in TS).
|
|
140
|
+
; Hosts may replace notifyPath/notifyId on the singleton instance; markStateDirty respects suppressUiNotify.
|
|
141
|
+
class ProcessUiHost @singleton(true) {
|
|
142
|
+
def __uiNotifySuppressDepth:int 0
|
|
143
|
+
|
|
144
|
+
fn isUiNotifySuppressed:boolean () {
|
|
145
|
+
return __uiNotifySuppressDepth > 0
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fn beginSuppressUiNotify:void () {
|
|
149
|
+
__uiNotifySuppressDepth = __uiNotifySuppressDepth + 1
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fn endSuppressUiNotify:void () {
|
|
153
|
+
if (__uiNotifySuppressDepth > 0) {
|
|
154
|
+
__uiNotifySuppressDepth = __uiNotifySuppressDepth - 1
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
fn notifyPath:void (path:string) {
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fn notifyId:void (processId:int) {
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
class ProcessRuntime @singleton(true) {
|
|
166
|
+
|
|
167
|
+
sfn stopInstance:void (proc:RangerProcessBase) {
|
|
168
|
+
ProcessNameRegistry.__singleton().unbindIfConfigured(proc)
|
|
169
|
+
proc.__rangerStopSubtree()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
sfn startInstance:void (proc:RangerProcessBase) {
|
|
173
|
+
ProcessNameRegistry.__singleton().bindIfConfigured(proc)
|
|
174
|
+
proc.__rangerInvokeStart()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
sfn hibernateInstance:string (proc:RangerProcessBase) {
|
|
178
|
+
def res:string (proc.__rangerInvokeHibernate())
|
|
179
|
+
return res
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
sfn wakeupInstance:void (proc:RangerProcessBase state:string) {
|
|
183
|
+
proc.__rangerInvokeWakeup(state)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
class RangerFieldDescriptor {
|
|
188
|
+
def name:string ""
|
|
189
|
+
def typeName:string ""
|
|
190
|
+
def isOptional:boolean false
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class RangerClassDescriptor {
|
|
194
|
+
def className:string ""
|
|
195
|
+
def fields:[RangerFieldDescriptor]
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
; CLI tree renderer for @process hierarchies (__rangerChildren). Root list:
|
|
199
|
+
; ProcessRuntime.collectAllLiveRoots() (generated when the app has @process classes).
|
|
200
|
+
class ProcessTreeView {
|
|
201
|
+
def showParentId:boolean true
|
|
202
|
+
|
|
203
|
+
fn isLive:boolean (proc:RangerProcessBase) {
|
|
204
|
+
if (proc.__rangerId == 0) {
|
|
205
|
+
return false
|
|
206
|
+
}
|
|
207
|
+
return true
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
fn nodeLabel:string (proc:RangerProcessBase) {
|
|
211
|
+
def label:string ""
|
|
212
|
+
label = proc.__rangerClassName
|
|
213
|
+
label = label + " #"
|
|
214
|
+
label = label + (to_string proc.__rangerId)
|
|
215
|
+
if (showParentId) {
|
|
216
|
+
label = label + " parent="
|
|
217
|
+
label = label + (to_string proc.__rangerParentId)
|
|
218
|
+
}
|
|
219
|
+
return label
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
fn countLiveChildren:int (proc:RangerProcessBase) {
|
|
223
|
+
def n:int 0
|
|
224
|
+
for proc.__rangerChildren ch:RangerProcessBase i {
|
|
225
|
+
if (this.isLive(ch)) {
|
|
226
|
+
n = n + 1
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return n
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
fn renderSubtree:void (proc:RangerProcessBase indent:string) {
|
|
233
|
+
def liveIdx:int 0
|
|
234
|
+
def total:int (this.countLiveChildren(proc))
|
|
235
|
+
for proc.__rangerChildren ch:RangerProcessBase i {
|
|
236
|
+
if ((this.isLive(ch)) == false) {
|
|
237
|
+
continue
|
|
238
|
+
}
|
|
239
|
+
def branch:string "|-> "
|
|
240
|
+
def childIndent:string " "
|
|
241
|
+
if (liveIdx == (total - 1)) {
|
|
242
|
+
branch = "L-> "
|
|
243
|
+
}
|
|
244
|
+
def line:string ""
|
|
245
|
+
line = indent + branch
|
|
246
|
+
def nodeText:string (this.nodeLabel(ch))
|
|
247
|
+
line = line + nodeText
|
|
248
|
+
print line
|
|
249
|
+
def nextIndent:string ""
|
|
250
|
+
nextIndent = indent + childIndent
|
|
251
|
+
this.renderSubtree(ch nextIndent)
|
|
252
|
+
liveIdx = liveIdx + 1
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
fn countLiveRoots:int (roots:[RangerProcessBase]) {
|
|
257
|
+
def c:int 0
|
|
258
|
+
for roots r:RangerProcessBase i {
|
|
259
|
+
if (this.isLive(r)) {
|
|
260
|
+
c = c + 1
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return c
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
fn renderRoots:void (roots:[RangerProcessBase] title:string) {
|
|
267
|
+
print ""
|
|
268
|
+
if (title != "") {
|
|
269
|
+
print title
|
|
270
|
+
} {
|
|
271
|
+
print "Process tree"
|
|
272
|
+
}
|
|
273
|
+
def n:int (size roots)
|
|
274
|
+
if (n == 0) {
|
|
275
|
+
print " (no live root processes)"
|
|
276
|
+
return
|
|
277
|
+
}
|
|
278
|
+
def liveTotal:int (this.countLiveRoots(roots))
|
|
279
|
+
if (liveTotal == 0) {
|
|
280
|
+
print " (no live root processes)"
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
def shown:int 0
|
|
284
|
+
def i:int 0
|
|
285
|
+
while (i < n) {
|
|
286
|
+
def root:RangerProcessBase (at roots i)
|
|
287
|
+
if (this.isLive(root)) {
|
|
288
|
+
def branch:string "|-> "
|
|
289
|
+
def childIndent:string " "
|
|
290
|
+
if (shown == (liveTotal - 1)) {
|
|
291
|
+
branch = "L-> "
|
|
292
|
+
}
|
|
293
|
+
def line:string ""
|
|
294
|
+
line = " " + branch
|
|
295
|
+
def rootText:string (this.nodeLabel(root))
|
|
296
|
+
line = line + rootText
|
|
297
|
+
print line
|
|
298
|
+
this.renderSubtree(root childIndent)
|
|
299
|
+
shown = shown + 1
|
|
300
|
+
}
|
|
301
|
+
i = i + 1
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|