ranger-compiler 3.2.0 → 3.5.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +2463 -0
  2. package/LICENSE +28 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +1651 -1895
  5. package/dist/Lang.rgr +10946 -5852
  6. package/dist/README.md +3 -2
  7. package/dist/api.d.ts +1548 -27
  8. package/dist/api.js +70572 -38295
  9. package/dist/git-http.mjs +126 -0
  10. package/dist/lib/ACEEditor.rgr +2 -0
  11. package/dist/lib/Ajax.rgr +2 -0
  12. package/dist/lib/CmdParams.rgr +2 -0
  13. package/dist/lib/Crypto.rgr +2 -0
  14. package/dist/lib/DOMLib.rgr +2 -0
  15. package/dist/lib/Engine3D.rgr +2 -0
  16. package/dist/lib/ImmutableVector.rgr +2 -0
  17. package/dist/lib/IndexedDB.rgr +2 -0
  18. package/dist/lib/IsoDate/DateMath.rgr +2 -0
  19. package/dist/lib/IsoDate/IsoCalendar.rgr +2 -0
  20. package/dist/lib/IsoDate/IsoDateParse.rgr +2 -0
  21. package/dist/lib/IsoDateLib.rgr +2 -0
  22. package/dist/lib/JSON.rgr +4906 -48
  23. package/dist/lib/JinxProcess.rgr +2 -0
  24. package/dist/lib/RangerProcess.rgr +2 -0
  25. package/dist/lib/Regex/RegexMatch.rgr +2 -0
  26. package/dist/lib/RegexLib.rgr +2 -0
  27. package/dist/lib/SQL.rgr +2 -0
  28. package/dist/lib/ServiceLib.rgr +2 -0
  29. package/dist/lib/Shell.rgr +326 -0
  30. package/dist/lib/Storage.rgr +2 -0
  31. package/dist/lib/Time.rgr +2 -0
  32. package/dist/lib/Timers.rgr +2 -0
  33. package/dist/lib/TypedArrays.rgr +2 -0
  34. package/dist/lib/ViewLib.rgr +2 -0
  35. package/dist/lib/WebLib.rgr +2 -0
  36. package/dist/lib/WebServerLib.rgr +2 -0
  37. package/dist/lib/apple/AppleAppBuilder.rgr +572 -0
  38. package/dist/lib/apple/AppleAppSpec.rgr +201 -0
  39. package/dist/lib/apple/AppleDevice.rgr +295 -0
  40. package/dist/lib/apple/AppleDeviceDoctor.rgr +453 -0
  41. package/dist/lib/apple/AppleSigning.rgr +379 -0
  42. package/dist/lib/apple/AppleSimulator.rgr +248 -0
  43. package/dist/lib/apple/AppleTarget.rgr +185 -0
  44. package/dist/lib/apple/AppleToolchain.rgr +458 -0
  45. package/dist/lib/apple/README.md +311 -0
  46. package/dist/lib/apple/apple_test.rgr +669 -0
  47. package/dist/lib/core/README.md +251 -0
  48. package/dist/lib/core/RgBase.rgr +313 -0
  49. package/dist/lib/core/RgNum.rgr +653 -0
  50. package/dist/lib/core/RgText.rgr +680 -0
  51. package/dist/lib/core/RgU32.rgr +309 -0
  52. package/dist/lib/ranger-dir.rgr +2 -0
  53. package/dist/lib/shell_test.rgr +193 -0
  54. package/dist/lib/stdlib.rgr +1176 -665
  55. package/dist/lib/stdops.rgr +2 -0
  56. package/dist/lib/zip/Inflate.rgr +675 -0
  57. package/dist/lib/zip/ZipBuffer.rgr +358 -0
  58. package/dist/package.json +1 -1
  59. package/dist/rgrc.js +76333 -40647
  60. package/dist/stdops.rgr +2 -0
  61. package/package.json +1121 -339
package/README.md CHANGED
@@ -1,1895 +1,1651 @@
1
- # Ranger cross language compiler
2
-
3
- **Version 3.1.1** | Status: `experimental`
4
-
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
-
7
- It includes a compact typed language with classes, inheritance, traits, lambdas, type inference, extension methods, custom operators, and host integration through system classes.
8
-
9
- Ranger is best approached today as a compiler and language lab with practical multi-target output, not as a polished general-purpose language ecosystem.
10
-
11
- ## What Ranger Is Good At
12
-
13
- - Writing one algorithm or tool and emitting several target languages from the same source
14
- - Building parsers, analyzers, generators, and DSL-like tooling with a small runtime surface
15
- - Experimenting with language design, operator templates, and code generation strategies
16
- - Studying a self-hosting compiler that is actively used to compile itself
17
-
18
- ## Word of Warning
19
-
20
- - Ranger is still `experimental`, which means: be ready to fix bugs or add new capabilities when needed
21
- - Target quality varies by language and by feature area
22
- - The compiler is self-hosting, but the official and best-supported host is Node.js
23
- - Not every example in this repository works fully out of the box on every machine
24
- - Several examples in `gallery/` are research or showcase projects and may require extra toolchains, platform-specific commands, or manual setup
25
-
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
-
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_RUNTIME_INVARIANTS.md](PROCESS_RUNTIME_INVARIANTS.md) (dispatch turn / one notify), [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) (Vite + React host for `@process`).
60
-
61
- ## Where To Start
62
-
63
- - [Online playground](https://terotests.github.io/Ranger/) — try Ranger in the browser (`playground/`, Vite + current compiler)
64
- - `README.md` - language overview, installation, and syntax notes
65
- - `ai/QUICKREF.md` - fast reference for syntax and core concepts
66
- - `ai/INSTRUCTIONS.md` - fuller language guide for operators, templates, and compiler concepts
67
- - `ai/EXAMPLES.md` - short focused language examples
68
- - `gallery/` - larger examples and experiments such as parsers, EVG/TSX tooling, and games
69
- - `gallery/js_parser` - substantial parser example with benchmarks and README
70
- - `gallery/pdf_writer` - EVG / TSX document tooling and preview server
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`
73
- - `gallery/invaders/llvm/invaders.ll` - checked-in LLVM IR sample from the experimental `-l=llvm` backend
74
-
75
- ## Compatibility Snapshot
76
-
77
- The project can target `JavaScript`, `Java`, `Go`, `Swift`, `PHP`, `C++`, `C#`, `Scala`, `Python`, `Kotlin`, and `Rust`, but support is uneven.
78
-
79
- | Area | Current expectation |
80
- | --- | --- |
81
- | Host/runtime | Node.js is the primary supported host for the compiler |
82
- | Self-hosting | Actively used, but full compiler generation quality is strongest in JavaScript |
83
- | JavaScript / ES6 | Best baseline target and most reliable place to start |
84
- | Go / Swift / Rust / Kotlin / C++ | Useful and increasingly capable, but expect edge cases and target-specific gaps |
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`. |
86
- | Gallery examples | Good for understanding direction and capability, but some require manual setup or platform-specific tooling |
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
-
105
- ## What's New in Version 3.0
106
-
107
- - **New File Extension** - Transitioning from `.clj` to `.rgr` for Ranger identity
108
- - **Simplified CLI** - Use `rgrc` command for shorter invocations
109
- - **VSCode Extension** - Language server with syntax highlighting (in development)
110
- - **CI/CD Pipeline** - Automated testing and NPM publishing
111
- - **Unit Test Suite** - Comprehensive test coverage with Vitest
112
-
113
- ### Quick Start
114
-
115
- ```bash
116
- # Install globally
117
- npm install -g ranger-compiler
118
-
119
- # Compile to JavaScript
120
- rgrc -l=es6 myfile.rgr -o=output.js
121
-
122
- # Compile to TypeScript
123
- rgrc -l=es6 -typescript myfile.rgr -o=output.ts
124
-
125
- # Compile to Python
126
- rgrc -l=python myfile.rgr -o=output.py
127
- ```
128
-
129
- See [CHANGELOG.md](CHANGELOG.md) for full version history and [PLAN_3.md](PLAN_3.md) for the roadmap.
130
-
131
- ---
132
-
133
- ## Host platforms and target languages
134
-
135
- The compiler is _self hosting_ which means that it has been written using the compiler itself and thus it can be hosted
136
- on several platforms. At the moment the official platform is node.js, because external plugins are only available as npm packages.
137
-
138
- The target languages supported are `JavaScript`, `Java`, `Go`, `Swift`, `PHP`, `C++`, `C#`, `Scala`, `Python`, and `Rust`. An **experimental LLVM backend** (`-l=llvm`) emits LLVM IR and optional freestanding WAT for WASM toolchains; it is not a separate surface language but another codegen path from the same Ranger sources.
139
-
140
- The quality of the target translation still varies and at the moment of this writing the compiler can only be compiled fully to JavaScript target. However, most targets already can compile reasonably good code.
141
-
142
- ## Recent Updates (December 2025)
143
-
144
- ### TypeScript Parser (TSParser) Enhancements
145
-
146
- The TSParser (`gallery/ts_parser`) now supports additional JavaScript/TypeScript syntax features:
147
-
148
- **New Operators:**
149
- - **UpdateExpression**: `i++`, `++i`, `i--`, `--i` with proper prefix/postfix semantics
150
- - **Compound Assignment**: `+=`, `-=`, `*=`, `/=`, `%=` operators
151
- - **Computed Member Access**: Array indexing `arr[i]` now correctly sets the `computed` flag
152
-
153
- These enhancements enable the EVG ComponentEngine to evaluate for loops and dynamic array operations in TSX files.
154
-
155
- ### Swift 6 Target Support
156
-
157
- The Swift 6 target (`-l=swift6`) has been significantly enhanced with the following features:
158
-
159
- - Modern Swift 6 compatible code generation
160
- - Simple `main()` function entry point (avoids @main conflicts with operator overloads)
161
- - Proper integer-to-string conversion using `String()`
162
- - Array operations using `.append()` instead of `.push()`
163
- - File I/O with `Foundation` framework integration
164
- - String operations: `substring`, `indexOf`, `startsWith`, `endsWith`, `contains`, `split`, `trim`
165
- - Optional handling with `unwrap` and `!!` operators
166
- - Command-line argument access
167
- - CRLF grapheme cluster handling for cross-platform string compatibility
168
-
169
- **Successfully compiled projects:**
170
-
171
- - ✅ JavaScript ES6+ Parser (`gallery/js_parser`) - 4500+ lines, parses and pretty-prints ES6+ code
172
- - ✅ Space Invaders game (`gallery/invaders`)
173
-
174
- Example compilation:
175
-
176
- ```bash
177
- node bin/output.js myfile.rgr -l=swift6 -o=myfile.swift
178
- sed -i '' $'s/\r$//' myfile.swift # Fix line endings on macOS
179
- swiftc myfile.swift -o myfile
180
- ```
181
-
182
- ### Rust Target Support (Preliminary)
183
-
184
- The Rust target (`-l=rust`) now has preliminary support with the following features:
185
-
186
- - Classes compiled to structs with `impl` blocks
187
- - Constructors as `pub fn new()` returning owned structs
188
- - Static factory methods
189
- - Instance methods with `&mut self`
190
- - Proper String handling with `.to_string()` for literals
191
- - Array operations (`push`, `itemAt`, `set`) with `Vec<T>`
192
- - String concatenation using `format!` macro
193
- - Ternary expressions as `if/else` expressions
194
- - Automatic `#[derive(Clone)]` for structs
195
- - Smart mutability detection (`let` vs `let mut`)
196
-
197
- Example compilation:
198
-
199
- ```bash
200
- node bin/output.js myfile.rgr -l=rust -o=myfile.rs
201
- rustc myfile.rs -o myfile
202
- ```
203
-
204
- ### C++ Static Analysis Optimizer (New)
205
-
206
- The C++ target (`-l=cpp`) now includes a static analysis pass that automatically detects mutation patterns and generates proper C++ references. This solves a common issue where local variables assigned from member fields were incorrectly copied instead of referenced.
207
-
208
- **The Problem:**
209
-
210
- ```ranger
211
- fn writeByte:void (b:int) {
212
- def buf:buffer currentChunk.data ; Assigned from member field
213
- buffer_set buf 0 b ; Mutates the buffer
214
- }
215
- ```
216
-
217
- Without static analysis, this would generate:
218
-
219
- ```cpp
220
- void writeByte(int b) {
221
- std::vector<uint8_t> buf = currentChunk->data; // COPY!
222
- buf[0] = static_cast<uint8_t>(b); // Modifies copy, not original!
223
- }
224
- ```
225
-
226
- **The Solution:**
227
-
228
- The static analyzer detects when:
229
-
230
- 1. A local variable is assigned from a member field (e.g., `obj.field`)
231
- 2. That variable is later mutated with in-place operations (`buffer_set`, `push`, `set`, etc.)
232
-
233
- When both conditions are met, it generates a C++ reference:
234
-
235
- ```cpp
236
- void writeByte(int b) {
237
- std::vector<uint8_t>& buf = currentChunk->data; // REFERENCE!
238
- buf[0] = static_cast<uint8_t>(b); // Modifies original
239
- }
240
- ```
241
-
242
- **Mutating Operations Detected:**
243
-
244
- | Category | Operators |
245
- | ---------- | ------------------------------------------------------------- |
246
- | Buffer | `buffer_set`, `int_buffer_set`, `double_buffer_set`, `*_fill` |
247
- | Array | `push`, `set`, `clear`, `remove`, `removeIndex` |
248
- | Dictionary | `put` |
249
-
250
- This optimization is automatically applied when compiling to C++ - no source code changes required.
251
-
252
- ### HTTP Server Support (New - December 2025)
253
-
254
- Ranger now supports defining HTTP servers using **annotation-based type aliasing**. Classes marked with `@(HttpServer)` can use HTTP operators and route annotations.
255
-
256
- **Example HTTP Server:**
257
-
258
- ```ranger
259
- Import "stdlib.rgr"
260
-
261
- class MyServer@(HttpServer) {
262
- fn handleIndex@(GET "/"):void (req:HttpRequest res:HttpResponse) {
263
- http_set_header res "Content-Type" "text/html"
264
- http_set_status res 200
265
- http_send res "<h1>Hello from Ranger!</h1>"
266
- }
267
-
268
- fn handleEvents@(SSE "/events"):void (client:SSEClient) {
269
- sse_send client "message" "Welcome!"
270
- }
271
- }
272
-
273
- sfn main@(main):void () {
274
- def server:MyServer (new MyServer())
275
- start server 3000
276
- }
277
- ```
278
-
279
- **Key Features:**
280
-
281
- - **Systemclass types**: `HttpRequest`, `HttpResponse`, `SSEClient`, `HttpServer`
282
- - **HTTP operators**: `http_get_method`, `http_get_path`, `http_set_status`, `http_set_header`, `http_send`
283
- - **SSE operators**: `sse_send`, `sse_is_connected`
284
- - **Route annotations**: `@(GET "/path")`, `@(POST "/path")`, `@(SSE "/path")`
285
- - **Server lifecycle**: `start server port`, `stop server`
286
-
287
- **Compilation:**
288
-
289
- ```bash
290
- # Compile to Go
291
- RANGER_LIB=./compiler/Lang.rgr node bin/output.js -l=go ./myserver.rgr -d=./bin -o=myserver.go -nodecli
292
-
293
- # Run the server
294
- cd bin && go run myserver.go
295
- ```
296
-
297
- Currently supports **Go** target. See `tests/fixtures/http_server.rgr` for a complete example.
298
-
299
- ### EVG Document Preview Tools (New - December 2025)
300
-
301
- Ranger includes tools for creating and previewing documents using a React-like TSX syntax. The EVG (Extensible Vector Graphics) system supports multi-page documents with flexbox layout.
302
-
303
- **Live Preview Server:**
304
-
305
- ```bash
306
- # Build the preview server (one-time)
307
- npm run evgpreview:build
308
-
309
- # Start live preview with auto-reload
310
- cd gallery/pdf_writer
311
- ./bin/evg_preview_server examples/test_gallery.tsx 3006
312
-
313
- # Open http://localhost:3006 - auto-refreshes on file save!
314
- ```
315
-
316
- **HTML Generation:**
317
-
318
- ```bash
319
- # Build the HTML tool (one-time)
320
- npm run evg:tool:build:go
321
-
322
- # Convert TSX to HTML
323
- cd gallery/pdf_writer
324
- ./bin/evg_tool examples/test_gallery.tsx output.html
325
-
326
- # With component imports
327
- ./bin/evg_tool document.tsx --assets=../components;../assets
328
- ```
329
-
330
- **Features:**
331
-
332
- - **Live reload** - Browser auto-refreshes when you save
333
- - **Component imports** - Reusable TSX components
334
- - **Multi-page documents** - Print, Section, Page elements
335
- - **Flexbox layout** - CSS-like positioning
336
- - **Images & fonts** - Asset serving from configurable paths
337
-
338
- See `gallery/pdf_writer/README.md` for full documentation and TSX syntax reference.
339
-
340
- ### EVG ComponentEngine TypeScript Evaluation (New - December 2025)
341
-
342
- The EVG ComponentEngine now supports **full TypeScript control flow evaluation**, enabling dynamic document generation with loops and conditionals. Functions defined in TSX files can use for loops, array operations, and return arrays of elements.
343
-
344
- **Supported Features:**
345
-
346
- | Feature | Syntax | Description |
347
- |---------|--------|-------------|
348
- | For loops | `for (let i = 0; i < n; i++)` | Standard for loop with init/test/update |
349
- | Decrement loops | `for (let i = 5; i > 0; i--)` | Countdown loops |
350
- | Step loops | `for (let i = 0; i < n; i += 2)` | Custom step increments |
351
- | Array.push | `arr.push(<Element />)` | Build arrays of JSX elements |
352
- | Array indexing | `colors[i]` | Access array elements by index |
353
- | Compound assignment | `total += value` | `+=`, `-=`, `*=`, `/=`, `%=` operators |
354
- | Update expressions | `i++`, `++i`, `i--`, `--i` | Pre/post increment/decrement |
355
- | Function calls in JSX | `{buildItems()}` | Call functions that return element arrays |
356
-
357
- **Example - Dynamic List Generation:**
358
-
359
- ```tsx
360
- const colors = ["#ef4444", "#f97316", "#eab308", "#22c55e", "#3b82f6"];
361
-
362
- function buildColorBoxes() {
363
- const boxes: any[] = [];
364
-
365
- for (let i = 0; i < colors.length; i++) {
366
- const color = colors[i];
367
- boxes.push(
368
- <View backgroundColor={color} padding={8}>
369
- <Label color="#ffffff">Box {i + 1}: {color}</Label>
370
- </View>
371
- );
372
- }
373
-
374
- return boxes;
375
- }
376
-
377
- function render() {
378
- return (
379
- <Print>
380
- <Section>
381
- <Page>
382
- <View padding={16}>
383
- <Label fontSize={20} fontWeight="bold">Color Boxes</Label>
384
- {buildColorBoxes()}
385
- </View>
386
- </Page>
387
- </Section>
388
- </Print>
389
- );
390
- }
391
- ```
392
-
393
- **Example - Progressive Widths with Accumulator:**
394
-
395
- ```tsx
396
- function buildProgressBars() {
397
- const bars: any[] = [];
398
- let totalWidth = 0;
399
-
400
- for (let i = 1; i <= 5; i++) {
401
- totalWidth += i * 20; // 20, 60, 120, 200, 300
402
- bars.push(
403
- <View width={totalWidth} backgroundColor="#0ea5e9" padding={4}>
404
- <Label color="#ffffff">Width: {totalWidth}px</Label>
405
- </View>
406
- );
407
- }
408
-
409
- return bars;
410
- }
411
- ```
412
-
413
- See `gallery/pdf_writer/examples/test_for_loop.tsx` for a complete demonstration.
414
-
415
- ### Space Invaders Demo Game
416
-
417
- A complete terminal-based Space Invaders game demonstrating Ranger's cross-language capabilities. The same source code compiles to **several targets**:
418
-
419
- | Target | Output | Build Command |
420
- | -------------- | ------------------- | --------------------------- |
421
- | ES6/JavaScript | `invaders.js` | `npm run game:compile` |
422
- | **LLVM native**| `tmp/invaders-native/invaders` | `npm run game:build:llvm` |
423
- | Rust | `invaders_rust.exe` | `npm run game:build:rust` |
424
- | Go | `invaders_go.exe` | `npm run game:build:go` |
425
- | Kotlin | `invaders.jar` | `npm run game:build:kotlin` |
426
- | C++ | `invaders_cpp.exe` | Cross-compile via WSL |
427
- | Swift | `invaders_swift` | macOS/Linux only |
428
-
429
- > **Note:** Kotlin target renders correctly but keyboard input has issues on Windows (uses PowerShell subprocess for key reading which is slow).
430
-
431
- ```bash
432
- # Build all targets at once
433
- npm run game:build:all
434
-
435
- # Run the game
436
- npm run game:run # JavaScript
437
- npm run game:run:rust # Rust
438
- npm run game:run:go # Go
439
- ./tmp/invaders-native/invaders # LLVM native (after game:build:llvm)
440
- ```
441
-
442
- #### Experimental LLVM backend (Space Invaders)
443
-
444
- Ranger can lower the same `invaders.rgr` through a **Low IR → LLVM IR** pipeline (`-l=llvm`), then link with `clang` and a small C runtime (`runtime/ranger_term.c`) for terminal I/O.
445
-
446
- ```bash
447
- npm run compile # refresh bin/output.js after compiler changes
448
- npm run game:build:llvm # invaders.rgr → tmp/invaders-native/invaders.ll → native binary
449
- npm run test:llvm # LLVM/WASM fixture tests (vitest)
450
- npm run demo:wasm # smaller freestanding WASM demo (tests/fixtures/llvm_wasm_demo.rgr)
451
- ```
452
-
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)).
454
-
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`.
456
-
457
- #### Cross-Compiling the Game
458
-
459
- The Space Invaders game demonstrates cross-platform compilation from a single source file.
460
-
461
- **JavaScript (ES6)**
462
-
463
- ```bash
464
- npm run game:compile # Generates invaders.js
465
- node gallery/invaders/invaders.js
466
- ```
467
-
468
- **Rust**
469
-
470
- ```bash
471
- npm run game:compile:rust # Generates invaders.rs
472
- cd gallery/invaders && rustc invaders.rs -o invaders_rust.exe
473
- # Or use the combined command:
474
- npm run game:build:rust
475
- ```
476
-
477
- **Go**
478
-
479
- ```bash
480
- npm run game:compile:go # Generates invaders.go
481
- cd gallery/invaders && go build -o invaders_go.exe invaders.go
482
- # Or use the combined command:
483
- npm run game:build:go
484
- ```
485
-
486
- **C++ (Windows via WSL)**
487
-
488
- C++ compilation requires POSIX-threaded MinGW for `std::thread` and `std::mutex` support:
489
-
490
- ```bash
491
- npm run game:compile:cpp # Generates invaders.cpp
492
-
493
- # Cross-compile from WSL to Windows:
494
- wsl -d Ubuntu -- bash -c "
495
- cd /mnt/c/path/to/Ranger/gallery/invaders && \
496
- sed -i 's/\r$//' invaders.cpp && \
497
- x86_64-w64-mingw32-g++-posix -std=c++17 -static -pthread invaders.cpp -o invaders_cpp.exe
498
- "
499
- ```
500
-
501
- > **Note:** The standard MinGW compiler (`x86_64-w64-mingw32-g++`) uses win32 threads which don't support `<mutex>` and `<thread>`. You must use the POSIX variant (`g++-posix`).
502
-
503
- **Swift (macOS/Linux only)**
504
-
505
- ```bash
506
- npm run game:compile:swift # Generates invaders.swift
507
- swiftc invaders.swift -o invaders_swift
508
- ```
509
-
510
- **LLVM native (macOS / Linux, experimental)**
511
-
512
- ```bash
513
- npm run game:build:llvm
514
- ./tmp/invaders-native/invaders
515
- ```
516
-
517
- Requires `clang` on `PATH`. On macOS the script picks `arm64-apple-macos` or `x86_64-apple-macos` automatically.
518
-
519
- #### Platform-Specific Keyboard Input
520
-
521
- The game uses `on_keypress` and `poll_keypress` operators with platform-specific implementations:
522
-
523
- | Platform | Windows | Unix/Linux/macOS |
524
- | -------- | --------------------------------- | -------------------------- |
525
- | Rust | `windows-sys` crate | `termios` + `libc` |
526
- | Go | `msvcrt.dll` (`_kbhit`, `_getch`) | `stty` + `os.Stdin` |
527
- | C++ | `<conio.h>` (`_kbhit`, `_getch`) | `<termios.h>` + `read()` |
528
- | Swift | `_kbhit` / `_getch` via C interop | `Darwin` / `Glibc` termios |
529
-
530
- The game uses terminal control operators (`clear_screen`, `move_cursor`, `hide_cursor`, etc.) and keyboard input (`on_keypress`, `poll_keypress`) that have platform-specific implementations for Windows and Unix.
531
-
532
- #### Target Comparison: Code Size and Executable Size
533
-
534
- The Space Invaders game provides an interesting comparison of how the same Ranger source code translates to different targets.
535
-
536
- **Source Code Sizes:**
537
-
538
- | Target | Generated File | Size (bytes) | Lines | Notes |
539
- | ---------- | ---------------- | ------------ | ----- | ------------------------------- |
540
- | **Ranger** | `invaders.rgr` | 11,289 | ~400 | Original source |
541
- | **LLVM IR**| `llvm/invaders.ll` | ~60,000 | ~1,700 | Low-level IR (sample in repo) |
542
- | Python | `invaders.py` | 9,271 | ~330 | Most compact generated code |
543
- | JavaScript | `invaders.js` | 10,301 | ~350 | Clean, readable output |
544
- | Swift | `invaders.swift` | 12,554 | ~470 | Verbose type annotations |
545
- | Go | `invaders.go` | 13,701 | ~480 | Explicit error handling |
546
- | C++ | `invaders.cpp` | 14,148 | ~500 | Headers and type declarations |
547
- | Rust | `invaders.rs` | 17,918 | ~600 | Most verbose (ownership, types) |
548
-
549
- **Executable Sizes (native binaries):**
550
-
551
- | Target | Executable | Size | Notes |
552
- | ------ | -------------------- | ------ | --------------------------------- |
553
- | **LLVM** | `tmp/invaders-native/invaders` | ~34 KB (arm64 macOS) | Smallest in recent local builds; libc + minimal runtime |
554
- | Swift | `invaders_swift.exe` | 76 KB | Dynamic link to system libraries |
555
- | Rust | `invaders_rust.exe` | 291 KB | Optimized, statically linked |
556
- | Go | `invaders_go.exe` | 2.3 MB | Includes Go runtime |
557
- | C++ | `invaders_cpp.exe` | 3.0 MB | Static linking with MinGW/pthread |
558
-
559
- **Analysis:**
560
-
561
- - **Python** generates the most compact code due to its concise syntax (no type annotations, no braces)
562
- - **Rust** generates the most verbose code because of explicit ownership (`clone()`, `&mut`), type annotations, and safety features
563
- - **Swift** produces the smallest native executable because it links dynamically to system libraries
564
- - **Go** and **C++** have large executables due to static linking of their runtimes
565
- - **JavaScript** runs on Node.js, so there's no standalone executable (interpreter required)
566
-
567
- The ~11KB Ranger source compiles to native executables ranging from 76KB to 3MB, demonstrating the trade-offs between different target languages' runtime requirements and linking strategies.
568
-
569
- **Known Issues:**
570
-
571
- - Console rendering may have timing artifacts on some terminals
572
- - Swift target requires macOS or Linux (not available on Windows)
573
-
574
- ### JavaScript ES6+ Parser
575
-
576
- A comprehensive JavaScript ES6+ parser written entirely in Ranger, demonstrating the language's capability to build complex tools. The parser includes a full lexer, recursive descent parser, and pretty-printer.
577
-
578
- **Features:**
579
-
580
- - **Full ES6+ support** - Classes, arrow functions, async/await, generators, destructuring, spread operators, template literals
581
- - **Pretty-printer** - Parses JavaScript and outputs formatted code
582
- - **Comment preservation** - Line comments, block comments, and JSDoc are attached to AST nodes
583
- - **Multi-target** - Parser compiles to JavaScript, Swift, Go, Python, etc.
584
-
585
- **Quick Start (JavaScript):**
586
-
587
- ```bash
588
- # Compile the parser
589
- node bin/output.js gallery/js_parser/js_parser_main.rgr -o=js_parser.js -d=gallery/js_parser
590
-
591
- # Parse and pretty-print a JavaScript file
592
- node gallery/js_parser/js_parser.js -i input.js -o output.js
593
-
594
- # Show AST structure
595
- node gallery/js_parser/js_parser.js -i input.js --ast
596
- ```
597
-
598
- **Quick Start (Swift):**
599
-
600
- ```bash
601
- # Compile to Swift (from gallery/js_parser directory)
602
- cd gallery/js_parser
603
- node ../../bin/output.js js_parser_main.rgr -l=swift6 -o js_parser.swift
604
-
605
- # Fix line endings and compile
606
- sed -i '' $'s/\r$//' bin/js_parser_main.swift
607
- swiftc -o js_parser_swift bin/js_parser_main.swift
608
-
609
- # Run the native Swift binary
610
- ./js_parser_swift -i input.js --ast
611
- ./js_parser_swift -d
612
- ```
613
-
614
- **Quick Start (C++ on Windows via WSL):**
615
-
616
- ```bash
617
- # Compile to C++ (from Ranger root)
618
- node bin/output.js gallery/js_parser/js_parser_main.rgr -l=cpp -d=gallery/js_parser -o=js_parser.cpp
619
-
620
- # Cross-compile from WSL to Windows
621
- wsl -d Ubuntu -- bash -c "
622
- cd /mnt/c/path/to/Ranger/gallery/js_parser && \
623
- sed -i 's/\r$//' js_parser.cpp && \
624
- x86_64-w64-mingw32-g++-posix -std=c++17 -static -o js_parser_cpp.exe js_parser.cpp
625
- "
626
-
627
- # Run the native Windows binary
628
- ./js_parser_cpp.exe -i input.js --ast
629
- ./js_parser_cpp.exe -d
630
- ```
631
-
632
- **Supported ES6+ Features:**
633
-
634
- | Category | Features |
635
- | ------------ | ---------------------------------------------------------------------- |
636
- | Declarations | `let`, `const`, `var`, function declarations/expressions |
637
- | Classes | `class`, `extends`, `constructor`, `static`, getters, `super` |
638
- | Functions | Arrow functions (`=>`), async/await, generators (`function*`, `yield`) |
639
- | Operators | Spread (`...`), rest parameters, destructuring (array/object) |
640
- | Literals | Template literals with interpolation, computed property names |
641
- | Control Flow | `for-of`, `for-in`, `while`, `if/else`, `switch`, `try/catch` |
642
-
643
- **Performance Benchmark (vs popular parsers):**
644
-
645
- The Ranger js_parser was benchmarked against popular JavaScript parsers. All parsers run in-process with warm-up:
646
-
647
- | Rank | Parser | Large (17KB) | XL (35KB) |
648
- | ------ | -------------------- | ------------ | ----------- |
649
- | #1 | meriyah | 0.51 ms | 0.84 ms |
650
- | **#2** | **Ranger js_parser** | **0.88 ms** | **1.39 ms** |
651
- | #3 | acorn | 1.41 ms | 2.70 ms |
652
- | #4 | esprima | 1.41 ms | 2.33 ms |
653
- | #5 | espree (ESLint) | 1.58 ms | 3.47 ms |
654
- | #6 | @babel/parser | 2.63 ms | 3.06 ms |
655
-
656
- 🥈 **Ranger ranks #2**, outperforming espree, acorn, esprima, and @babel/parser by **2-4x**.
657
-
658
- ```bash
659
- # Run the benchmark yourself
660
- cd gallery/js_parser/benchmark
661
- npm install
662
- npm run benchmark:large
663
- ```
664
-
665
- See [gallery/js_parser/benchmark](gallery/js_parser/benchmark) for the full benchmark suite.
666
-
667
- **Example transformation:**
668
-
669
- ```javascript
670
- // Input
671
- const greet = async (name) => {
672
- const msg = `Hello, ${name}!`;
673
- return msg;
674
- };
675
-
676
- // Output (pretty-printed)
677
- const greet = async (name) => {
678
- const msg = `Hello, ${name}!`;
679
- return msg;
680
- };
681
- ```
682
-
683
- See [gallery/js_parser/README.md](gallery/js_parser/README.md) for complete documentation.
684
-
685
- ### Polyfill System
686
-
687
- Ranger supports automatic polyfill generation for operators that require helper functions in the target language. Polyfills are utility functions, types, or constants that are automatically added to the generated output when an operator needs them.
688
-
689
- Key features:
690
-
691
- - **Automatic deduplication** - Polyfills are only generated once even if the operator is used multiple times
692
- - **Per-target definitions** - Each target language can have its own polyfill implementation
693
- - **Platform-specific code** - Polyfills can contain platform conditionals (e.g., `#[cfg(windows)]` in Rust)
694
-
695
- Example: The `on_keypress` operator in Rust generates polyfill functions for raw terminal input handling that work on both Windows and Unix platforms.
696
-
697
- See the `ai/INSTRUCTIONS.md` file for details on creating operators with polyfills.
698
-
699
- ### Unit Test Suite
700
-
701
- A comprehensive test suite has been added using Vitest:
702
-
703
- ```bash
704
- npm test # Run all tests
705
- npm run test:es6 # JavaScript/ES6 tests only
706
- npm run test:python # Python target tests
707
- npm run test:go # Go target tests
708
- npm run test:rust # Rust target tests
709
- ```
710
-
711
- Test coverage includes:
712
-
713
- - **ES6/JavaScript**: Full runtime tests (array operations, classes, inheritance, string operations, math, etc.)
714
- - **Python**: Compilation and runtime tests with pytest
715
- - **Go**: Compilation and runtime tests
716
- - **Rust**: Compilation tests (runtime tests in progress)
717
-
718
- ### Known Issues
719
-
720
- See `ISSUES.md` for a comprehensive list of known issues and their status. Key issues include:
721
-
722
- - `toString` method name causes compiler crash (use `getSymbol` or similar instead)
723
- - Go target has integer division type conversion issues
724
- - Python target has inheritance constructor argument issues
725
-
726
- ### AI Documentation
727
-
728
- The `ai/` folder contains documentation optimized for AI assistants:
729
-
730
- - `INSTRUCTIONS.md` - Complete language guide
731
- - `EXAMPLES.md` - Code examples for common patterns
732
- - `GRAMMAR.md` - Formal grammar reference
733
- - `QUICKREF.md` - Quick reference card
734
- - `INTROSPECTION.md` - Compiler introspection API for IDE/AI integration
735
-
736
- These files are also useful for human readers who want the shortest path to understanding Ranger without reading the whole README front to back.
737
-
738
- ### Compiler Introspection API (New)
739
-
740
- The compiler now exposes powerful introspection capabilities for IDE integration and AI-assisted development:
741
-
742
- **Position-Based Type Querying**
743
-
744
- - Query what type is at any line/column position in source code
745
- - Convert between line/column and byte offsets
746
- - Find all typed nodes in a source file
747
-
748
- **Class Structure Introspection**
749
-
750
- - Check if classes have specific properties with optional type verification
751
- - Check if classes have specific methods with optional return type verification
752
- - Get all properties and methods with full signatures
753
- - Track inheritance relationships
754
-
755
- **Use Cases**
756
-
757
- - IDE autocomplete and hover information
758
- - AI code generation with type-safe suggestions
759
- - Incremental compilation planning
760
- - Codebase analysis and documentation
761
-
762
- Example usage:
763
-
764
- ```typescript
765
- import {
766
- compileForIntrospection,
767
- classHasProperty,
768
- getTypeAtPosition,
769
- } from "./tests/helpers/introspection";
770
-
771
- // Compile source code
772
- const result = await compileForIntrospection(sourceCode);
773
-
774
- // Check class structure
775
- if (classHasProperty(result, "Person", "name", "string")) {
776
- // Safe to reference person.name
777
- }
778
-
779
- // Query type at cursor position (1-based line/column)
780
- const typeInfo = getTypeAtPosition(result.rootNode, sourceCode, 5, 12);
781
- console.log(typeInfo.evalTypeName); // e.g., "int"
782
- ```
783
-
784
- See `ai/INTROSPECTION.md` for complete API documentation.
785
-
786
- ## Installing the compiler
787
-
788
- Install the compiler from npm:
789
-
790
- ```
791
- npm install -g ranger-compiler
792
- ```
793
-
794
- Running `ranger-compiler` without arguments shows available command-line options:
795
-
796
- ```
797
- Ranger Compiler v3.0.1
798
-
799
- Usage: rgrc <file> [options] [flags]
800
- Options: -<option>=<value>
801
- -l=<value> Selected language, one of es6, go, scala, java7, swift3, swift6, kotlin, cpp, php, csharp, python, rust
802
- -d=<value> output directory, default directory is "bin/"
803
- -o=<value> output file, default is "output.<language>"
804
- -classdoc=<value> write class documentation .md file
805
- -operatordoc=<value> write operator documention into .md file
806
- Flags: -<flag>
807
- -forever Leave the main program into eternal loop (Go, Swift)
808
- -allowti Allow type inference at target lang (creates slightly smaller code)
809
- -plugins-only ignore built-in language output and use only plugins
810
- -plugins (node compiler only) run specified npm plugins -plugins="plugin1,plugin2"
811
- -strict Strict mode. Do not allow automatic unwrapping of optionals outside of try blocks.
812
- -typescript Writes JavaScript code with TypeScript annotations
813
- -npm Write the package.json to the output directory
814
- -nodecli Insert node.js command line header #!/usr/bin/env node to the beginning of the JavaScript file
815
- -nodemodule Export classes as CommonJS modules using module.exports (disables static main function)
816
- -esm Export classes as ES6/ESM modules using export keyword (disables static main function)
817
- -sourcemap Emit .js.map / .ts.map with embedded .rgr sourcesContent (ES6/TypeScript only)
818
- -client the code is ment to be run in the client environment
819
- -scalafiddle scalafiddle.io compatible output
820
- -compiler recompile the compiler
821
- -copysrc copy all the source codes into the target directory
822
- Pragmas: (inside the source code files)
823
- @noinfix(true) disable operator infix parsing and automatic type definition checking
824
- ```
825
-
826
- ### JavaScript Module Formats
827
-
828
- The compiler supports three JavaScript module output formats:
829
-
830
- | Flag | Format | Output | Use Case |
831
- | ------------- | -------- | ------------------------- | -------------------------------- |
832
- | (none) | Plain JS | No exports, runs `main()` | Standalone scripts |
833
- | `-nodemodule` | CommonJS | `module.exports.X = X;` | Node.js require() |
834
- | `-esm` | ES6/ESM | `export class X` | Modern ES modules, import/export |
835
-
836
- **Examples:**
837
-
838
- ```bash
839
- # Standalone JavaScript (runs main function)
840
- node bin/output.js -es6 myfile.rgr -o=myfile.js
841
-
842
- # CommonJS module (.cjs)
843
- node bin/output.js -es6 -nodemodule myfile.rgr -o=myfile.cjs
844
-
845
- # ES6/ESM module (.mjs)
846
- node bin/output.js -es6 -esm myfile.rgr -o=myfile.mjs
847
- ```
848
-
849
- **File Extensions:**
850
- The compiler automatically detects JavaScript-related extensions (`.js`, `.ts`, `.mjs`, `.cjs`) and won't double-add them. You can safely specify the full filename with extension.
851
-
852
- ### JavaScript / TypeScript source maps (`-sourcemap`)
853
-
854
- Use `-sourcemap` with `-es6` or `-typescript` to emit a sibling `.js.map` / `.ts.map` file and append `//# sourceMappingURL=…` to the generated output. Kotlin, Swift, and other non-JS targets ignore this flag.
855
-
856
- **What you get**
857
-
858
- | Output | Purpose |
859
- | --- | --- |
860
- | `output.js.map` | Source map v3 (VLQ `mappings`, `names`, `sources`) |
861
- | `sourcesContent` | Full `.rgr` source embedded in the map — Chrome DevTools can open `.rgr` files without a separate file server |
862
- | Statement + expression mappings | `LiveCompiler.WalkNode` walk context plus `outMapped()` on identifiers, calls, literals |
863
-
864
- **Compile example**
865
-
866
- ```bash
867
- # Standalone ES module + map
868
- node bin/output.js -es6 -esm -nodemodule -sourcemap ./myapp/App.rgr -o=app.js
869
-
870
- # Result: bin/app.js and bin/app.js.map
871
- ```
872
-
873
- **Debug in Chrome / Edge**
874
-
875
- 1. Serve the generated `.js` (and `.map` beside it). Vite/webpack are optional when `sourcesContent` is embedded.
876
- 2. Open DevTools → **Sources**. Original `.rgr` files appear under the map tree (from `sourcesContent`).
877
- 3. Set breakpoints on **executable** lines (e.g. `def`, `if`, `return`) — not only blank lines or signatures.
878
- 4. Breakpoints must be **solid red**. A hollow/grey breakpoint means no mapping for that line; rebuild with `-sourcemap` and hard-refresh (disable cache).
879
-
880
- **Tests**
881
-
882
- ```bash
883
- npm run compile
884
- npx vitest run tests/compiler-sourcemap.test.ts
885
- ```
886
-
887
- **Implementation notes** (for compiler hackers)
888
-
889
- - `compiler/ng_SourceMap.rgr` — `SourceMapBuilder`, VLQ encoder, `addMappingFromNode()` uses `node.getLine()` + `node.code.getColumn(sp)` (not stale `node.row`).
890
- - `compiler/ng_writer.rgr` — `lineNumber` / `columnNumber` on emit, `walkNodeStack`, `outMapped()`, `.map` write in `CodeFileSystem.saveTo`.
891
- - Flag: `compiler/ng_Compiler.rgr` → `flag sourcemap`; enabled in `VirtualCompiler.rgr` via `fileSystem.enableSourceMaps()`.
892
-
893
- ## Getting started with Hello World
894
-
895
- Create file `hello.rgr`
896
-
897
- ```
898
- class Hello {
899
- sfn m@(main):void () {
900
- print "Hello World"
901
- }
902
- }
903
-
904
- ```
905
-
906
- Then compile it using `ranger-compiler` from the command line:
907
-
908
- ```
909
- ranger-compiler hello.rgr
910
- ```
911
-
912
- The result will be written to `bin/output.js` by default, or you can choose the output name explicitly:
913
-
914
- ```
915
- ranger-compiler hello.rgr -o=hello.js
916
- ```
917
-
918
- ## Compiling using TypeScript
919
-
920
- The compiler can be used from TypeScript, which makes possible to create new versions of the
921
- compiler just using TypeScript.
922
-
923
- Note: the example requires `Lang`, `stdlib`, `stdops`, and `JSON` to be loaded for the compiler. In this example they are loaded from the filesystem using `readFileSync`.
924
-
925
- ```typescript
926
- // Notice this part of example is required:
927
- addFile("Lang.rgr", fs.readFileSync("./libs/Lang.rgr", "utf8"));
928
- addFile("stdlib.rgr", fs.readFileSync("./libs/stdlib.rgr", "utf8"));
929
- addFile("stdops.clj", fs.readFileSync("./libs/stdops.clj", "utf8"));
930
- addFile("JSON.clj", fs.readFileSync("./libs/JSON.clj", "utf8"));
931
- ```
932
-
933
- The full compiler code:
934
-
935
- ```typescript
936
- import * as R from "ranger-compiler";
937
- import { CodeNode } from "ranger-compiler";
938
-
939
- const compilerInput = new R.InputEnv();
940
- compilerInput.use_real = false;
941
-
942
- // manually create a filesystem
943
- const folder = new R.InputFSFolder();
944
- const addFile = (name: string, contents: string) => {
945
- const newFile = new R.InputFSFile();
946
- newFile.name = name;
947
- newFile.data = contents;
948
- folder.files.push(newFile);
949
- };
950
- addFile(
951
- "hello.clj",
952
- `
953
- class hello {
954
- static fn main() {
955
- print "Hello World"
956
- }
957
- }
958
- `
959
- );
960
-
961
- // compiler requires language definition and libraries to work
962
- const fs = require("fs");
963
- addFile("Lang.clj", fs.readFileSync("./libs/Lang.clj", "utf8"));
964
- addFile("stdlib.clj", fs.readFileSync("./libs/stdlib.clj", "utf8"));
965
- addFile("stdops.clj", fs.readFileSync("./libs/stdops.clj", "utf8"));
966
- addFile("JSON.clj", fs.readFileSync("./libs/JSON.clj", "utf8"));
967
-
968
- compilerInput.filesystem = folder;
969
-
970
- // set compiler options -l=es6 -typescript
971
- const params = new R.CmdParams();
972
- // target language is Go
973
- params.params["l"] = "go";
974
- params.params["o"] = "hello.go";
975
- params.values.push("hello.clj");
976
- compilerInput.commandLine = params;
977
-
978
- // Run compiler
979
- const vComp = new R.VirtualCompiler();
980
-
981
- // Check results...
982
- const res = await vComp.run(compilerInput);
983
-
984
- // browse through the target compiler file system
985
- res.fileSystem.files.forEach((file) => {
986
- console.log(file.getCode());
987
- });
988
- ```
989
-
990
- ## Switching to different target language
991
-
992
- Include command line parameter `-l=<language>` and the compiler will produce the output files for the language in the output directory.
993
- Available languages are listed when you run the compiler without any parameters.
994
-
995
- ## Languages and versions supported
996
-
997
- Currently the compiler supports at least following language versions:
998
-
999
- - JavaScript ES2015
1000
- - PHP versions 5.4 and above
1001
- - C++ version C++14
1002
- - Java version 7
1003
- - Swift version 3
1004
- - Golang version 1.8
1005
- - Scala 2.xx
1006
- - CSharp 7.0
1007
- - Python 3.x
1008
- - Rust (preliminary support)
1009
-
1010
- However, it is possible to add support for older versions by implementing custom operators, which target to certain compiler flags.
1011
-
1012
- Additionally, JavaScript has '-typescript' flag, which will add typescript annotations to the source file.
1013
-
1014
- # Operators
1015
-
1016
- Operators enable creating short, funtional commands like 'get' or 'push' that operate on certain, typed parameters. Whenever there is
1017
- need for some functionality it is woth considering whether it is best implemented using operator or a function or a class method. A
1018
- simple operator definition would be `M_PI` which is defined in the Compilers internal Lang.clj file as
1019
-
1020
- ```
1021
- M_PI mathPi:double () {
1022
- templates {
1023
- es6 ("Math.PI")
1024
- go ( "math.Pi" (imp "math"))
1025
- swift3 ( "Double.pi" (imp "Foundation"))
1026
- java7 ( "Math.PI" (imp "java.lang.Math"))
1027
- php ("pi()")
1028
- cpp ("M_PI" (imp "<math.h>"))
1029
- }
1030
- }
1031
- ```
1032
-
1033
- Oops! Looks like C# defintion is missing! It should be `Math.PI` and it requires `System`. We can add that easily to Lang.clj
1034
-
1035
- ```
1036
- M_PI mathPi:double () {
1037
- templates {
1038
- es6 ("Math.PI")
1039
- go ( "math.Pi" (imp "math"))
1040
- swift3 ( "Double.pi" (imp "Foundation"))
1041
- java7 ( "Math.PI" (imp "java.lang.Math"))
1042
- php ("pi()")
1043
- cpp ("M_PI" (imp "<math.h>"))
1044
- csharp ("Math.PI" (imp "System"))
1045
- }
1046
- }
1047
- ```
1048
-
1049
- Thus, the platform specific code is implemented using operators, which can also implement native polyfills in the target language.
1050
-
1051
- Operators also be written can be as macros in Ranger language itself.
1052
-
1053
- For a quick reference of available basic operators see [Operators doc](operators.md)
1054
-
1055
- # Plugins
1056
-
1057
- Compiling as CommonJS module:
1058
-
1059
- ```
1060
- ranger-compiler hello.clj -npm -nodemodule
1061
- ```
1062
-
1063
- Compiling as ES6/ESM module:
1064
-
1065
- ```
1066
- ranger-compiler hello.clj -npm -esm
1067
- ```
1068
-
1069
- Example
1070
-
1071
- ```javascript
1072
- Import "VirtualCompiler.clj"
1073
-
1074
- flag npm (
1075
- name "hello"
1076
- version "0.0.1"
1077
- description "Plugin Hello World"
1078
- author "Tero Tolonen"
1079
- license "MIT"
1080
- )
1081
-
1082
- class Plugin {
1083
- fn features:[string] () {
1084
- return ([] "postprocess")
1085
- }
1086
- fn postprocess (root:CodeNode ctx:RangerAppWriterContext wr:CodeWriter) {
1087
- print "*** plugin postprocess was called ***"
1088
- }
1089
- }
1090
- ```
1091
-
1092
- # Notes about the syntax
1093
-
1094
- Ranger syntax is originally based on Lisp -language syntax and most operators will use prefix notation. However, the Ranger modifies
1095
- the original Lisp so that inside block expression `{ ... }` there is no need to insert parenthesis which makes the language appear to
1096
- be a bit more like standard languages. Thus you can write exressions like
1097
-
1098
- ```
1099
- class Hello {
1100
- fn sayHello:void () {
1101
- def x 20
1102
- if ( x < 10 ) {
1103
- print "x < 10"
1104
- } {
1105
- print "x >= 10"
1106
- }
1107
- }
1108
- }
1109
- ```
1110
-
1111
- However, when you go deeper in the expression you may have to include the parenthesis, for example when invoking object you have to write
1112
-
1113
- ```
1114
- def obj (new Hello)
1115
- ```
1116
-
1117
- For most common mathematical symbols and boolean operators infix notation can be used and they are automatically converted to lisp expressions.
1118
- Thus you can write expressions such as `(x + y * z)` instead of `(+ x (* y z))`
1119
-
1120
- ```
1121
- def x 100
1122
- def y 200
1123
- def z ( x + y * 10)
1124
- if ( x < 20 || y == 0 ) {
1125
-
1126
- }
1127
- ```
1128
-
1129
- The assigment operator is also automatically prefixed from infix notation so you can say
1130
-
1131
- ```
1132
- x = y
1133
- ```
1134
-
1135
- Instead of common lisp syntax `(= x y)`
1136
-
1137
- ## Main function
1138
-
1139
- Each file can have a static main function, which is executed as the main program.
1140
-
1141
- ```
1142
- class Hello {
1143
- static fn main() {
1144
- }
1145
- }
1146
-
1147
- ```
1148
-
1149
- This is a static function which marks the start of execution for the program.
1150
-
1151
- ## Functions and Static functions
1152
-
1153
- ```
1154
- class Hello {
1155
- fn SomeNonStaticFn () {
1156
- }
1157
- sfn SomeStaticFn () {
1158
- ; static function which instantiates Hello and calls non-static
1159
- def o (new Hello)
1160
- o.SomeNonStaticFn()
1161
- }
1162
- }
1163
-
1164
- ```
1165
-
1166
- Calling static function of a class can be done with
1167
-
1168
- ```
1169
- Hello.SomeStaticFn()
1170
- ```
1171
-
1172
- ## Return values of functions
1173
-
1174
- Function not inferred or declared as `void` should always return value with `return` statement.
1175
-
1176
- ## Comments
1177
-
1178
- ```
1179
- ; here is a comment
1180
- class Hello {
1181
-
1182
- }
1183
- ```
1184
-
1185
- ## Type inference and variable definition
1186
-
1187
- Type inference can be used to determine variable type for local variables and class properties
1188
-
1189
- ```
1190
- def x 100 ; inferred type = int
1191
- def y:int 200
1192
- def o (new myClass) ; inferred type myClass
1193
- ```
1194
-
1195
- ## Standard types
1196
-
1197
- Basic primitive types are
1198
-
1199
- - int
1200
- - boolean
1201
- - string
1202
- - double
1203
- - char
1204
- - charbuffer
1205
-
1206
- Type of function returning nothing is
1207
-
1208
- - void
1209
-
1210
- Type which can be used as variable types, but require signature are
1211
-
1212
- - Arrays
1213
- - Hashes
1214
- - Anonymous functions
1215
-
1216
- Types which require type declaration are
1217
-
1218
- - Enum
1219
- - class
1220
- - systemclass
1221
- - systemunion
1222
- - trait
1223
-
1224
- ## String literals
1225
-
1226
- String literals are escaped using JSON escaping rules and can be multilne
1227
-
1228
- ```
1229
- def long_string "
1230
- this is
1231
- a multiline string
1232
- "
1233
- ```
1234
-
1235
- ## String Operations
1236
-
1237
- Ranger provides a comprehensive set of string manipulation operators. Here are some commonly used ones:
1238
-
1239
- ```
1240
- def text "Hello World"
1241
-
1242
- ; Length and substring operations
1243
- def len (strlen text) ; returns 11
1244
- def sub (substring text 0 5) ; returns "Hello"
1245
-
1246
- ; Case conversion
1247
- def lower (to_lowercase text) ; returns "hello world"
1248
- def upper (to_uppercase text) ; returns "HELLO WORLD"
1249
-
1250
- ; Search operations
1251
- def idx (indexOf text "World") ; returns 6
1252
- def hasWorld (contains text "World") ; returns true
1253
- def starts (startsWith text "Hello") ; returns true
1254
- def ends (endsWith text "World") ; returns true
1255
-
1256
- ; String manipulation
1257
- def replaced (replace text "World" "Ranger") ; returns "Hello Ranger"
1258
- def parts (strsplit text " ") ; returns ["Hello", "World"]
1259
- def trimmed (trim " hello ") ; returns "hello"
1260
- ```
1261
-
1262
- For the complete list of string operators, see [Operators doc](operators.md).
1263
-
1264
- ## Enums
1265
-
1266
- Enums will be compiled to type `int` but are type checked by the Ranger preprosessor
1267
-
1268
- ```
1269
- Enum LineJoin (
1270
- Undefined
1271
- Miter
1272
- Round
1273
- Bevel
1274
- )
1275
- class foo {
1276
- def lineType:LineJoin LineJoin.Undefined
1277
- }
1278
- ```
1279
-
1280
- ## Arrays and Hashes
1281
-
1282
- Arrays and hashes are automatically initialized and are ready to be used after their declaration
1283
-
1284
- ```
1285
- def list:[string]
1286
- def usedKeywords:[string:string]
1287
- def classMap:[string:myClass]
1288
- ```
1289
-
1290
- ### Operators for hashes
1291
-
1292
- if we have a hashmap
1293
-
1294
- ```
1295
- def someMap:[string:string]
1296
- ```
1297
-
1298
- Operator `set` can be used to set key/value pair
1299
-
1300
- ```
1301
- set someMap "foo" "bar"
1302
- ```
1303
-
1304
- Operator `has` can be used to check if a key exists in the hash
1305
-
1306
- ```
1307
- if (has someMap "a key") {
1308
-
1309
- }
1310
- ```
1311
-
1312
- Get is used to read the value associated with a key. The result is `@(optional)`
1313
-
1314
- ```
1315
- (get someMap "foo")
1316
- ```
1317
-
1318
- ## Anonymous functions / lambdas
1319
-
1320
- Anonymous function type declaration is automatically inferred
1321
-
1322
- ```
1323
- def name "foo"
1324
- def myFilter (fn:boolean (param:string) {
1325
- return (param == name)
1326
- })
1327
- if(myFilter("foo")) {
1328
- print "it was foo"
1329
- }
1330
- ```
1331
-
1332
- To give declare Anonymous function as parameter of function you must include the full signature, for
1333
- example for a callback taking `string` and `int` signature is `fn:void (txt:string i:int)`
1334
-
1335
- ```
1336
- fn foo:void ( callback:( fn:void (txt:string i:int)) ) {
1337
- callback("got this?" 10)
1338
- }
1339
- ```
1340
-
1341
- When giving lambda as a parameter, the formal type definition can be omitted, the named parameters are
1342
- automatically declared to the block scope of the lambda.
1343
-
1344
- ```
1345
- this.foo({
1346
- print txt + " = " i
1347
- })
1348
- ```
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
-
1352
- # Automatically infixed math support
1353
-
1354
- It is easy to define new mathematical operations in the Lang.clj file or in modules. However, some mathematical operations are automatically infixed
1355
- for easier usage. Thus, instead of using common lips notation `(* 4 10)` you can use easier to read infixed `4 * 10` -syntax
1356
-
1357
- ## Boolean logic operators
1358
-
1359
- ```
1360
- a && b
1361
- a || b
1362
- ```
1363
-
1364
- ## Math operators
1365
-
1366
- ```
1367
- a * b
1368
- a / b
1369
- a - b
1370
- a + b
1371
- ```
1372
-
1373
- ## Logical comparisions
1374
-
1375
- ```
1376
- a < b
1377
- a <= b
1378
- a > b
1379
- a >= b
1380
- a != b
1381
- ```
1382
-
1383
- # Common set of Operators and the Grammar file
1384
-
1385
- The file `Lang.clj` is used by the compiler for the common set of operators and compilation rules. The
1386
- most common operators for example
1387
-
1388
- - to_double
1389
- - read_file
1390
- - array_length
1391
-
1392
- Are defined in this file. Using the Lang.clj -file it is quite easy to extend the language to support new operators
1393
- or to modify the existing rules for better results, if so required. However, the Lang.clj is not ment for daily
1394
- modifications, rather it describes common set of rules used and thus should be edited sparingly.
1395
-
1396
- The file has couple of sections, but the `reserved_words` and `commands`. The Reserved words section declares (surprise!)
1397
- the reserved words and their transformation. This is required because for example in Go the word `map` is a keyword and can
1398
- not be used unless it is conveted to some other name, for example to `FnMap`.
1399
-
1400
- ```
1401
- reserved_words {
1402
- * {
1403
- map FnMap
1404
- forEach forEachItem
1405
- self _self
1406
- func _func
1407
- }
1408
- cpp {
1409
- operator _operator
1410
- static _static
1411
- union _union
1412
- bool _bool
1413
- ref _ref
1414
- class _class
1415
- new _new
1416
- delete _delete
1417
- template _template
1418
- namespace _namespace
1419
- virtual _virtual
1420
- public _public
1421
- private _private
1422
- protected _protected
1423
- }
1424
- go {
1425
- type _type
1426
- }
1427
- rust {
1428
- type r#type
1429
- static r#static
1430
- ref r#ref
1431
- union r#union
1432
- bool r#bool
1433
- }
1434
- swift3 {
1435
- operator _operator
1436
- static _static
1437
- init _init
1438
- }
1439
- swift6 {
1440
- operator _operator
1441
- static _static
1442
- init _init
1443
- }
1444
- }
1445
- ```
1446
-
1447
- The `*` section defines global mappings that apply to all target languages. Language-specific sections (like `cpp`, `rust`, `go`, `swift3`, `swift6`) define additional reserved word mappings for that particular target. For Rust, the `r#` prefix is used to escape keywords (raw identifiers).
1448
-
1449
- What the result should be is of course highly opinionated. In this example, the line `map FnMap` means that if possible the
1450
- compiler will transform anything named `map` to `fnMap` if possible. If transformation is not possible, compiler error is
1451
- generated.
1452
-
1453
- The common operators are declared in section `commands`, which describe commands, their expected parameters
1454
- and return values and rules on how they should be compiled into the target languages, possible imported libraries
1455
- and possible macros or helper function which should be created if the operator is used.
1456
-
1457
- Example of simple operator is `(M_PI)` which will return double value of mathematical symbol "pi".
1458
-
1459
- ```
1460
- commands {
1461
- M_PI mathPi:double () {
1462
- templates {
1463
- es6 ("Math.PI")
1464
- go ( "math.Pi" (imp "math"))
1465
- swift3 ( "Double.pi" (imp "Foundation"))
1466
- java7 ( "Math.PI" (imp "java.lang.Math"))
1467
- php ("pi()")
1468
- cpp ("M_PI" (imp "<math.h>"))
1469
- }
1470
- }
1471
- ...
1472
- ```
1473
-
1474
- Most operators are simple, but some require creating custom macros, helpoer functions and some of them are so complex
1475
- that they may be implemented in the compiler core.
1476
-
1477
- # Modules, classes and operators
1478
-
1479
- The basic unit of the program is class. The functions of classes can not be overloaded at the moment, which means that you can not
1480
- have two functions with different parameters or different return values.
1481
-
1482
- Each source file can import other files using `Import` command.
1483
-
1484
- ```
1485
- Import "Vec2.clj"
1486
-
1487
- class vectorTest {
1488
- fn testVectors () {
1489
- def v (new Vec2 ( 5 4 ))
1490
- }
1491
- }
1492
- ```
1493
-
1494
- ## Class declaration
1495
-
1496
- ```
1497
- class fatherClass {
1498
- def msg "Hello "
1499
- fn foo:string ( txt:string ) {
1500
- return (msg + txt)
1501
- }
1502
- }
1503
- class childClass {
1504
- Extends( fatherClass )
1505
- }
1506
- class mainProgram {
1507
- sfn m@(main) {
1508
- ; invoke the class
1509
- def cc (new childClass)
1510
- cc.foo("World!")
1511
- }
1512
- }
1513
-
1514
- ```
1515
-
1516
- ## Class constructor
1517
-
1518
- ```
1519
- class myClass {
1520
- def name:string ""
1521
- Constructor (n:string) {
1522
- name = s
1523
- }
1524
- }
1525
- ```
1526
-
1527
- Notes:
1528
-
1529
- 1. currently only a single variant of the constructor is possible.
1530
- 2. as of this writing calling the parent class constructor does not work properly
1531
-
1532
- ## Class invocation
1533
-
1534
- ```
1535
- def obj (new myClass ("name"))
1536
- ```
1537
-
1538
- classes without constructor can be invocated without arguments
1539
-
1540
- ```
1541
- def obj (new simpleClass)
1542
- ```
1543
-
1544
- ## Creating a class extension
1545
-
1546
- Class extensions are useful for keeping classes simple and moving dependencies to external Modules
1547
- which can extend the classes.
1548
-
1549
- Extension can
1550
-
1551
- - add new functions to the class
1552
- - add new member variables to the class
1553
-
1554
- ```
1555
- extension childClass {
1556
- def name:string ""
1557
- fn bar:string ( txt:string ) {
1558
- return ("Hello from exteision: " + txt)
1559
- }
1560
- }
1561
- ```
1562
-
1563
- ## Optional variables
1564
-
1565
- In several target languages so called "optional" type can be used. In Ranger Option -type can be used as function or operator
1566
- return value and as filter to opertors. To use optional variable directly it should be first unwrapped. Also, trying to unwrap
1567
- non-nullable value should cause compiler error. In Ranger any variable which is declared not given value is considered optional.
1568
- This corresponds to Swift `?` optional type.
1569
-
1570
- You can also declare variables optional using @optional annotation
1571
-
1572
- ```
1573
- def item@(optional):myClass
1574
- ```
1575
-
1576
- Some operators also return optional values, for example `(get <hash> <key>)` operator is returning always optional value. To use
1577
- the value you must use `(unwrap <value>)` operator
1578
-
1579
- ```
1580
- def strMap:[string:string]
1581
- def str (get strMap "myKey")
1582
- if(!null? str) {
1583
- print (unwrap str)
1584
- }
1585
- ```
1586
-
1587
- **Warning\*** currently optinal variables in Ranger are not "safe" in the sense the language makes sure that you can not make
1588
- programming errors - it is possible to create programming mistake by using a variable which automatically unwrapped. The plan
1589
- is to try to make them safer in the future, and options are considered how to enable them
1590
-
1591
- Another warning: Ranger does not protect you from mistakes when automatically unwrapping long reference chains like
1592
- `obj.property.subProperty.foo` where `property` and `subProperty ` are optional variables.
1593
-
1594
- ## Control flow
1595
-
1596
- ### if
1597
-
1598
- If statement is quite similar to other language, but `then` and `else` keywords are not used
1599
-
1600
- ```
1601
- def x 100
1602
- if ( x < 10 ) {
1603
- ; then branch
1604
- } {
1605
- ; else branch
1606
- }
1607
- ```
1608
-
1609
- ### switch - case
1610
-
1611
- Note: currently case statement does not support multiple matching values, it is planned to add support for that later.
1612
-
1613
- ```
1614
- def name "John"
1615
- switch name {
1616
- case "John" {
1617
-
1618
- }
1619
- case "Flat Eric" {
1620
-
1621
- }
1622
- default {
1623
-
1624
- }
1625
- }
1626
- ```
1627
-
1628
- ## Loops
1629
-
1630
- ### for -loop
1631
-
1632
- ```
1633
- def list:[string]
1634
- for list s:string i {
1635
- print s
1636
- }
1637
- ```
1638
-
1639
- You can use `break` and `continue` to control the for -loop.
1640
-
1641
- ### while -loop
1642
-
1643
- ```
1644
- def cnt 10
1645
- while (cnt > 0 ) {
1646
- print "round " + cnt
1647
- }
1648
- ```
1649
-
1650
- You can use `break` and `continue` to control the while -loop.
1651
-
1652
- ## Custom operators
1653
-
1654
- One of the most important features or Ranger is the ability to create custom operators which can target some specific language or all languages
1655
- using macros. Together with `systemclass` they allow the system to integrate to target environment or to create new abstraction over existing
1656
- native API's.
1657
-
1658
- Operators allow type matching against
1659
-
1660
- - defined primitive types
1661
- - defined classes
1662
- - Enums
1663
- - optionality
1664
- - traits
1665
-
1666
- Operators can be writing directly target language construct or they can be macros, which write code in Ranger and the compiler will then
1667
- transform the resulting AST tree into the target language's code using the conventions of target language. Which is better depends on the
1668
- situation, for example operators for system classes usually are written directly to the traget language while operators which are using
1669
- Ranger's own classes or datatypes are usually better to write with macros.
1670
-
1671
- Simple example of useful macro is Matrix and Vector multiplication. Let's say that you have defined a Matrix class and
1672
- want to overload the `*` -operator for easy matrix multiplication.
1673
-
1674
- ```
1675
- class Mat2 {
1676
- def m0 1.0
1677
- def m1 0.0
1678
- def m2 0.0
1679
- def m3 1.0
1680
- def m4 0.0
1681
- def m5 0.0
1682
- fn multiply:Mat2 ( b:Mat2 ) {
1683
- def t0 (m0*b.m0 + m1 * b.m2)
1684
- def t2 (m2*b.m0 + m3 * b.m2)
1685
- def t4 (m4*b.m0 + m5 * b.m2 + b.m4)
1686
-
1687
- def res (new Mat2)
1688
- res.m1 = (m0 * b.m1 + m1 * b.m3)
1689
- res.m3 = (m2 * b.m1 + m3 * b.m3)
1690
- res.m5 = (m4 * b.m1 + m5 * b.m3 + b.m5)
1691
- res.m0 = t0
1692
- res.m2 = t2
1693
- res.m4 = t4
1694
- return res
1695
- }
1696
- }
1697
- operators {
1698
- * base:Mat2 ( a:Mat2 b:Mat2) {
1699
- templates {
1700
- * @macro(true) ( (e 1 ) ".multiply(" (e 2) " )" )
1701
- }
1702
- }
1703
- }
1704
-
1705
- ```
1706
-
1707
- The `* @macro(true)` means that we target all languages and this is a macro, not actual target language construct.
1708
-
1709
- ## Custom operators and System classes
1710
-
1711
- To integrate with the target languages running environment, Ranger modules can declare `systemclass` which can be used
1712
- together with the code.
1713
-
1714
- ```
1715
- systemclass DOMElement {
1716
- es6 DOMElement
1717
- }
1718
-
1719
- operators {
1720
- find base:DOMElement ( id:string) {
1721
- templates {
1722
- es6 ("document.getElementById( " (e 1) " )")
1723
- }
1724
- }
1725
- setAttribute _:void ( elem:DOMElement name:string value:string) {
1726
- templates {
1727
- es6 ( (e 1) ".setAttribute(" (e 2) ", " (e 3) ")" )
1728
- }
1729
- }
1730
- }
1731
-
1732
- class tester {
1733
- fn modifyDom () {
1734
- def e (find "#someelem")
1735
- setAttribute( e "className", "activeElement")
1736
- }
1737
- }
1738
- ```
1739
-
1740
- Note: Definition of system classes will be revisited in near future and there will be potentially small changes to it.
1741
-
1742
- ## Unions of system classes
1743
-
1744
- Sometimes the system class can be of union type. This means that the traget language can accept multiple types in place of
1745
- a single type.
1746
-
1747
- ```
1748
- systemunion DOMElementUnion ( DOMElement string )
1749
- ```
1750
-
1751
- The you can create operator which accepts either `DOMElement` or `string` and reduces that to a single type.
1752
-
1753
- ## Traits
1754
-
1755
- Traits are like extensions, which can be plugged into several classes using `does` keyword.
1756
-
1757
- Traits
1758
-
1759
- ```
1760
- trait bar {
1761
- fn hello() {
1762
- print "Hello"
1763
- }
1764
- }
1765
-
1766
- ; foo implements "bar" trait
1767
- class foo {
1768
- does bar
1769
- }
1770
- ```
1771
-
1772
- Traits are very useful when used together with custom operators, because operators can also match traits.
1773
-
1774
- Another useful feature of traits is their genericity. While classes can not be generic, traits can and thus
1775
- it is possible to implement for example generic collections using generic traits.
1776
-
1777
- ```
1778
- trait GenericCollection @params(T S) {
1779
- def items:[T]
1780
- fn add (item:T) {
1781
- push items item
1782
- }
1783
- fn map:S ( callback:( f:T (item:T)) ) {
1784
- def res:S (new S ())
1785
- for items ch@(lives):T i {
1786
- def new_item@(lives):T (callback (ch))
1787
- res.add(new_item)
1788
- }
1789
- return res
1790
- }
1791
- ; ... TODO: add more collection functions...
1792
- }
1793
-
1794
- ; then create a specific "string" collection..
1795
- class StringCollection {
1796
- does GenericCollection @params(string StringCollection)
1797
- }
1798
-
1799
- class Main {
1800
- fn testCollection:void () {
1801
- def coll:StringCollection (new StringCollection)
1802
- coll.add("A")
1803
- coll.add("B")
1804
- def n (coll.map({
1805
- return ("item = " + item)
1806
- }))
1807
- print (join n.items " ")
1808
- }
1809
- sfn hello@(main):void () {
1810
- def hello (new Main ())
1811
- hello.testCollection()
1812
- }
1813
- }
1814
- ```
1815
-
1816
- ## Variable definitions
1817
-
1818
- Values can be defined using `def` keyword.
1819
-
1820
- ```
1821
- def x:double
1822
- def x:double 0.4 ; double with initializer
1823
- def list1:[double] ; list of doubles
1824
- def strList:[string] ; list of Strings
1825
- def strMap:[string:string] ; map of string -> string
1826
- def strObjMap:[string:someClass] ; map of string -> object of type someClass
1827
- ```
1828
-
1829
- # Advanced topics
1830
-
1831
- ## Compiling a new version of the compiler
1832
-
1833
- Then run command
1834
-
1835
- ```
1836
- ranger-compiler -compiler -copysrc
1837
- ```
1838
-
1839
- The result will be written to directory `bin/ng_Compiler.js`.
1840
-
1841
- # Annotations
1842
-
1843
- Compiler is using annotation syntax for specifying some parameters for class, trait and variable construction.
1844
-
1845
- ## sfn someFn@(main)
1846
-
1847
- Static functions can be annotated to be the start point of compiled application using `@(main)` annotation.
1848
-
1849
- ## trait myTrait @params(...)
1850
-
1851
- @params(...) annotation can be used to greate generic traits.
1852
-
1853
- ```
1854
- trait GenericCollection @paras(T V) {
1855
- def items:[T]
1856
- fn map:S ( callback:( f:T (item:T)) ) {
1857
- def res:S (new S ())
1858
- for children ch@(lives):T i {
1859
- def new_item@(lives):T (callback (ch))
1860
- res.add(new_item)
1861
- }
1862
- return res
1863
- }
1864
- }
1865
-
1866
- class StringCollection {
1867
- does GenericCollection @params(string StringCollection)
1868
- }
1869
- ```
1870
-
1871
- ## def variableName@(optional)
1872
-
1873
- Optional variables can be used as return values of functions where the result is not certain. You can
1874
- force the unwrapping of the variable with `(unwrap <variable>)`
1875
-
1876
- ## def variableName@(weak)
1877
-
1878
- Weak variables are ment to be compiled in the target language as weak references
1879
-
1880
- ## def variableName@(strong)
1881
-
1882
- Weak variables are ment to be compiled in the target language as strong references
1883
-
1884
- ## def variableName@(lives)
1885
-
1886
- @(lives) annotation can be used to note the compiler that the variable is supposed to outlive it's current scope.
1887
-
1888
- The variables have lifetime, which determines the point where the variable should be removed. In garbage collected
1889
- languages you do not have to worry about the lifetime, but in the future there can be target languages which require
1890
- the lifetime calculations.
1891
-
1892
- ## def variableName@(temp)
1893
-
1894
- @(temp) annotation can be used to note the compiler that it should not worry about freeing the variable, in case the
1895
- target language has option to release the variable.
1
+ # Ranger cross language compiler
2
+
3
+ **Version 3.3.0** | Status: `experimental`
4
+
5
+ **Licensing:** Ranger-authored compiler and language sources are MIT licensed,
6
+ unless a file says otherwise. Ranger-authored applications and technology
7
+ under `gallery/`, including EVG and the Office document stack, are licensed
8
+ under AGPL-3.0-or-later unless a file says otherwise. Third-party files keep
9
+ their own licenses. See [`LICENSE`](LICENSE) for details.
10
+
11
+ 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.
12
+
13
+ It includes a compact typed language with classes, inheritance, traits, lambdas, type inference, extension methods, custom operators, and host integration through system classes.
14
+
15
+ Ranger is best approached today as a compiler and language lab with practical multi-target output, not as a polished general-purpose language ecosystem.
16
+
17
+ Ranger language = permissive. Ranger Gallery technology = copyleft.
18
+
19
+ The compiler does not put a license on a program you write and compile.
20
+ The license of the output follows the source. Compiled gallery programs
21
+ stay AGPL. Using EVG or another `gallery/` module is using that
22
+ framework, not only the language. Alternative commercial licenses may
23
+ be available for `gallery/` components. Full texts:
24
+ [`LICENSE-MIT`](LICENSE-MIT), [`LICENSE-AGPL-3.0`](LICENSE-AGPL-3.0),
25
+ [`gallery/LICENSE`](gallery/LICENSE). The path rule, the output rule,
26
+ and third-party exceptions are in [`LICENSING.md`](LICENSING.md).
27
+
28
+ ## What Ranger Is Good At
29
+
30
+ - Writing one algorithm or tool and emitting several target languages from the same source
31
+ - Building parsers, analyzers, generators, and DSL-like tooling with a small runtime surface
32
+ - Experimenting with language design, operator templates, and code generation strategies
33
+ - Studying a self-hosting compiler that is actively used to compile itself
34
+
35
+ ## Word of Warning
36
+
37
+ - Ranger is still `experimental`, which means: be ready to fix bugs or add new capabilities when needed
38
+ - Target quality varies by language and by feature area
39
+ - The compiler is self-hosting, but the official and best-supported host is Node.js
40
+ - Not every example in this repository works fully out of the box on every machine
41
+ - Several examples in `gallery/` are research or showcase projects and may require extra toolchains, platform-specific commands, or manual setup
42
+
43
+ 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.
44
+
45
+ ## `@process` runtime (experimental)
46
+
47
+ 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()`.
48
+
49
+ | Piece | What it does |
50
+ | --- | --- |
51
+ | `@process` / `@process(true)` | Process instance with lifecycle hooks (`start`, `stop`, …) |
52
+ | `proc_start` / `proc_stop` | Activate or tear down a process subtree (children first) |
53
+ | `proc_send target handler arg…` | Typed message dispatch to `fn on…` handlers on a live instance |
54
+ | `ProcessNameRegistry.findProcess(path)` | Lookup by `@name("app.foo")` (TypeScript path literals when using `-typescript`) |
55
+ | `markStateDirty()` | Bump generation + notify host (`ProcessUiHost`) for UI binding |
56
+ | `beginSuppressUiNotify` / `endSuppressUiNotify` | Batch parent↔child sync without notify storms |
57
+
58
+ **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.
59
+
60
+ ```ranger
61
+ class CounterPage @process @name("app.counter") extends RangerProcessBase {
62
+ def count:int 0
63
+ fn onUiIncrement:void () {
64
+ count = count + 1
65
+ this.markStateDirty()
66
+ }
67
+ }
68
+ ```
69
+
70
+ ```typescript
71
+ // Host (TypeScript): wire notify once, then bind UI to process fields
72
+ const host = ProcessUiHost.__singleton();
73
+ host.notifyPath = (path) => { /* sync view model + re-render */ };
74
+ ```
75
+
76
+ **Docs:** [PROCESS_MVP.md](PROCESS_MVP.md) (scope), [PROCESS_STATUS.md](PROCESS_STATUS.md) (compiler checklist), [PROCESS_RUNTIME_INVARIANTS.md](PROCESS_RUNTIME_INVARIANTS.md) (dispatch turn / one notify), [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) (Vite + React host for `@process`).
77
+
78
+ ## Where To Start
79
+
80
+ - [Documentation site](https://terotests.github.io/Ranger/docs/) — install, first program, types, optionals, and the **generated operator reference** (838 operators, compiled from the sources of the commit that publishes the site, so it cannot drift)
81
+ - [The front page](https://terotests.github.io/Ranger/) — what Ranger is, the targets, the platforms and the gallery (`landing/`)
82
+ - [Online playground](https://terotests.github.io/Ranger/playground/) — try Ranger in the browser (`playground/`, Vite + current compiler)
83
+ - `README.md` - language overview, installation, and syntax notes
84
+ - [`gallery/README.md`](gallery/README.md) - index of the application stack (AGPL): EVG, Office, DataGrid, parsers, games, and `@process` host apps
85
+ - [`LICENSING.md`](LICENSING.md) - MIT compiler vs AGPL gallery
86
+ - [`TARGET_NOTES.md`](TARGET_NOTES.md) - what each target language supports and where it falls short
87
+ - [`PLAN_FORMATS.md`](PLAN_FORMATS.md) — the architecture for reading more than three file formats: the layer stack, the internal models, and the phased roadmap after DOCX/XLSX/PPTX. Phase 1 is `.odp` beside `.pptx`, run as the experiment that proves or disproves the shared scene
88
+ - [`PLAN_API_DOCS.md`](PLAN_API_DOCS.md) — the design for `doc { … }` declarations: API metadata attached to a Ranger declaration that never restates what the compiler already knows, the `no doc` / `doc` / `doc public` visibility rule, a canonical **ApiIR** that names a logical module and no namespace, and the outputs built from it — native doc comments and annotations per target (XML doc, TSDoc, DocC, KDoc, rustdoc, Javadoc, dartdoc, Doxygen, docstrings), and the language × platform split that separates C# from Unity and Dart from Flutter
89
+ - [`CHANGELOG.md`](CHANGELOG.md) - version history
90
+ - [`AGENTS.md`](AGENTS.md) — git/PR rules and Ranger gotchas for AI agents; links the [FAQ](https://terotests.github.io/Ranger/docs/faq/)
91
+ - `ai/` — short offline notes for assistants (`README.md`, `QUICKREF.md`, `GRAMMAR.md`, `INTROSPECTION.md`); prefer the docs site when online
92
+
93
+ ## Targets and compatibility
94
+
95
+ The compiler is _self hosting_: it is written in Ranger and compiled by itself,
96
+ so it can run on several platforms. Node.js is the official host, because
97
+ external plugins are only available as npm packages.
98
+
99
+ **Primary targets** — exercised by the test suite and large gallery programs:
100
+ `JavaScript` / `TypeScript` (ES2015), `Go`, `Python` (3.x), `Kotlin`, `C#`
101
+ (Mono `mcs` in CI; modern .NET also fine), `Rust`, `Dart`, `Swift` (3 and 6),
102
+ and `C++` (C++14).
103
+
104
+ The TypeScript/ES5 interpreter in `gallery/game_engine/v2/interp` is the largest
105
+ cross-target gate: Go, Kotlin, Python, C#, Dart and Swift 6 each compile, and
106
+ where the toolchain is on `PATH` they build and answer the Node benchmark cases
107
+ (`npm run test:tsengine`). Dart also keeps the `gallery/ts_parser` golden
108
+ (AST identical to the JS `-d` demo). Details:
109
+ [`TS_ENGINE_PERF.md`](TS_ENGINE_PERF.md), [`TARGET_NOTES.md`](TARGET_NOTES.md).
110
+
111
+ **Thinner CI** — `PHP`, `Java 7` and `Scala` still have operator templates and
112
+ appear in the syntax-app matrix, but they are not on the large-engine golden
113
+ path. Treat them as usable with more gaps.
114
+
115
+ Older language versions can be supported by writing custom operators that target
116
+ a compiler flag. Support is uneven:
117
+
118
+ | Area | Current expectation |
119
+ | --- | --- |
120
+ | Host/runtime | Node.js is the primary supported host for the compiler |
121
+ | Self-hosting | Actively used. JavaScript is the reference; the compiler also compiles **itself for C++, Dart, Python, C#, Go and Kotlin** — each of those builds accepts the result and compiles the compiler again, all but C++ byte-for-byte identically ([`TARGET_NOTES.md`](TARGET_NOTES.md#cross-target-gate-the-compiler-itself-on-c-dart-python-c-go-and-kotlin)) |
122
+ | JavaScript / ES6 | Best baseline target and most reliable place to start |
123
+ | Go / Python / Kotlin / C# / Rust / C++ | Large programs verified (TS engine and/or jpeg / parsers); expect remaining edge cases |
124
+ | Dart / Swift | Strong for substantial modules (ts_parser; TS engine builds and matches Node on both when toolchains are present); Flutter packages via `-l=dart -pubspec` |
125
+ | PHP / Java / Scala | Templates + syntax-app coverage; fewer end-to-end goldens |
126
+ | **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. Another codegen path from the same sources, not a separate surface language. **The compiler self-hosts on LLVM.** It compiles for LLVM with no errors, the 22 MB of IR it emits for itself passes `opt -passes=verify` and links into a native `rangerc` — and that binary compiles the compiler, producing JavaScript byte-identical to the Node build's, which then reproduces itself (`npm run selfhost:round:llvm`). See [`TARGET_NOTES.md`](TARGET_NOTES.md#the-compiler-on-llvm-how-far-it-gets), `npm run test:llvm`, `npm run selfhost:check:llvm`, `npm run game:build:llvm`. |
127
+ | Gallery examples | Good for understanding direction and capability, but some require manual setup or platform-specific tooling |
128
+
129
+ **Targets are not equally demanding of the source.** A target with a garbage
130
+ collector — JavaScript, Go, Python, Kotlin, Dart, Java, C# — takes almost any Ranger
131
+ source as written. A target without one — C++, Rust, and Swift's reference
132
+ counting — needs the ownership annotations (`@(weak)`, `@(strong)`, `@(lives)`,
133
+ `@(temp)`, see [Annotations](#annotations)) wherever the object graph has a
134
+ cycle or a non-owning reference. Code that runs on ES6 can therefore still fail
135
+ to build, or leak, on C++ or Rust until those annotations are added. Write for
136
+ the strictest target you intend to support, not for JavaScript.
137
+
138
+ [Target languages](https://terotests.github.io/Ranger/docs/targets/overview/)
139
+ documents what each target writes and the semantic differences a portable
140
+ program has to know.
141
+
142
+ ### Conformance suite (Track 1)
143
+
144
+ Cross-target semantic fixtures live in `tests/conformance/`. Run `npx vitest run tests/compiler-conformance.test.ts`.
145
+ Regenerate the fixture list with `node scripts/generate-conformance-table.mjs`.
146
+
147
+ <!-- BEGIN CONFORMANCE_TABLE -->
148
+ | Fixture | Topic | Targets |
149
+ | --- | --- | --- |
150
+ | `array_param_mutate` | array parameters use reference semantics (Issue #58; Go known gap) | ES6, Go, Kotlin, Dart (when toolchain present) |
151
+ | `clear_then_push` | clear resets slice without nil, push refills (Issue #59) | ES6, Go, Kotlin, Dart (when toolchain present) |
152
+ | `int_division_to_double` | Conformance: integer division promoted to double (Issue #4) | ES6, Go, Kotlin, Dart (when toolchain present) |
153
+ | `lf_line_endings` | LF-only source (Issue #12 class must not break operator spacing) | ES6, Go, Kotlin, Dart (when toolchain present) |
154
+ | `math_ops` | Conformance: arithmetic and comparisons | ES6, Go, Kotlin, Dart (when toolchain present) |
155
+ | `string_codepoint_index` | Conformance: Unicode code-point string indexing (Issue #57) | ES6, Go, Kotlin, Dart (when toolchain present) |
156
+ | `while_loop` | Conformance: while loop control flow | ES6, Go, Kotlin, Dart (when toolchain present) |
157
+ <!-- END CONFORMANCE_TABLE -->
158
+
159
+ ## Quick start
160
+
161
+ Source files use the `.rgr` extension (`.clj` is the legacy extension and still
162
+ works) and the CLI is `rgrc`:
163
+
164
+ ```bash
165
+ # Install globally
166
+ npm install -g ranger-compiler
167
+
168
+ # Compile to JavaScript
169
+ rgrc -l=es6 myfile.rgr -o=output.js
170
+
171
+ # Compile to TypeScript
172
+ rgrc -l=es6 -typescript myfile.rgr -o=output.ts
173
+
174
+ # Compile to Python
175
+ rgrc -l=python myfile.rgr -o=output.py
176
+ ```
177
+
178
+ See [CHANGELOG.md](CHANGELOG.md) for the version history.
179
+
180
+ ---
181
+
182
+ ## Common pitfalls
183
+
184
+ The things people hit first, in the order they usually hit them.
185
+
186
+ **Ranger is still a Lisp.** It reads like a braces-and-newlines language, but
187
+ that surface is a set of shortcuts for S-expressions — the expressions are
188
+ still there underneath. When something does not parse or does not type-check,
189
+ the fix is almost always a pair of parentheses.
190
+
191
+ **Negation needs the parentheses.** `!` is an operator like any other, so it
192
+ takes its argument in prefix form:
193
+
194
+ ```
195
+ if (! this.flag) { } ; correct
196
+ if !this.flag { } ; FAIL: Could not match argument types for if
197
+ ```
198
+
199
+ Because the bare form does not compile, people fall back to
200
+ `if (this.flag == false)`. That works, but it is not needed — `(! this.flag)`
201
+ is the direct form.
202
+
203
+ **Singletons are an annotation, not a pattern you write by hand.** Mark the
204
+ class and call the generated accessor:
205
+
206
+ ```
207
+ class Config @singleton(true) {
208
+ def path:string "/etc/app"
209
+ }
210
+
211
+ def c (Config.__singleton())
212
+ ```
213
+
214
+ **Optionals have to be wrapped and unwrapped.** Any variable declared without a
215
+ value is optional, and several operators — `get` on a hash above all — always
216
+ return one. Reading the value takes `unwrap` / `!!`, or `??` for a default;
217
+ `wrap` makes an optional out of a plain value. Forgetting this shows up as a
218
+ type error between `T` and `<optional>T`. See
219
+ [Optional variables](#optional-variables).
220
+
221
+ **Extending the language does not mean recompiling the compiler.**
222
+ `compiler/Lang.rgr` is read at compile time, not baked into `bin/output.js`.
223
+ Add or change an operator template there, and the very next compile uses it —
224
+ no `npm run compile` in between. The compiler looks for `Lang.rgr` in the
225
+ working directory first, so a copy beside your sources overrides the installed
226
+ one, which makes an experiment cheap to try and cheap to throw away.
227
+
228
+ ## Gallery and demos
229
+
230
+ The `gallery/` folder holds the larger examples: parsers, EVG/TSX document
231
+ tooling, games, and `@process` host apps. They show what the compiler can do,
232
+ but they are research and showcase projects — some need extra toolchains or
233
+ platform-specific setup. [gallery/README.md](gallery/README.md) indexes them
234
+ and holds the writeups (build commands, benchmarks, target comparisons); each
235
+ project also has its own README.
236
+
237
+ One of them is worth pointing at from here, because it is the clearest thing
238
+ this compiler does. `gallery/pptx` reads `.pptx` decks and paints them through
239
+ an EVG display list; [`gallery/pptx/android`](gallery/pptx/android/README.md)
240
+ is the **same source** compiled to Kotlin and drawn with `android.graphics` —
241
+ a real Android app with no PowerPoint code in it, because the ZIP reader, the
242
+ OOXML parser, the theme resolver, the JPEG and PNG decoders, the TrueType
243
+ reader and the layout engine are all Ranger and none of them were forked. The
244
+ port is a facade, one walk over the display list, and two implementations of an
245
+ eight-method surface — the second being Java2D, so a deck renders to PNGs on
246
+ any JVM and the port can be checked without a device.
247
+
248
+ ## Calling other programs, and building an iOS app with no Xcode project
249
+
250
+ Ranger can run another command line program:
251
+
252
+ ```ranger
253
+ Import "lib/Shell.rgr"
254
+
255
+ def sh:Shell (new Shell)
256
+ def argv:[string]
257
+ push argv "rev-parse"
258
+ push argv "HEAD"
259
+ def r:ShellResult (sh.capture("git" argv))
260
+ if (r.ok()) {
261
+ print (r.outText())
262
+ }
263
+ ```
264
+
265
+ One compiler primitive is behind that — `run_process_result`, which answers
266
+ `([exit code, stdout, stderr])` and can either capture the child's output or let
267
+ it stream to this program's own. It takes a working directory and extra
268
+ environment entries, passes the arguments as a **vector** so nothing inside one
269
+ is ever re-read by a shell, and has a backend on thirteen targets.
270
+ [`lib/Shell.rgr`](lib/Shell.rgr) is the API over it: a result object, a log of
271
+ every command line, and a **dry run** that records instead of executing.
272
+
273
+ That dry run is what makes a build driver testable, and
274
+ [`lib/apple/`](lib/apple/README.md) is the driver it was built for:
275
+ `AppleTarget`, `AppleAppSpec`, `AppleToolchain` and `AppleAppBuilder` turn a
276
+ list of Swift files into an installed, running iOS, iPadOS or watchOS app using
277
+ `xcrun`, `swiftc`, `plutil`, `codesign` and `simctl` — **no `.xcodeproj`, no
278
+ `xcodebuild`, and nothing that opens Xcode.** 151 checks assert the plan it
279
+ produces (which program, which arguments, in which order) and they run on
280
+ JavaScript, Python, Go, Rust, C++, Java and PHP, on any machine.
281
+
282
+ [`gallery/ui/ios`](gallery/ui/ios/README.md) is the whole of it worked through:
283
+ the `gallery/ui` dashboard demo compiled to Swift, painted with CoreGraphics,
284
+ and built for an iPhone, an iPad and an Apple Watch by a **Ranger program**.
285
+
286
+ ```bash
287
+ npm run shell:test # the process operator and lib/Shell, on this machine
288
+ npm run apple:test # the Apple build driver, without a Mac
289
+ npm run ui:ios:plan # the entire iOS build, printed and not run
290
+ npm run ui:ios:verify # the port's own logic — 82 checks, no Mac
291
+ npm run ui:ios:run # ...and on a Mac, the app on a simulator
292
+ npm run ui:ios:device # ...or on the iPhone or iPad on the cable
293
+ ```
294
+
295
+ `ui:ios:device` is one command because the three things a device build needs —
296
+ the connected device, a codesigning identity and a matching provisioning
297
+ profile — are all already on the machine, so they are found rather than typed.
298
+
299
+ ## Target-specific notes
300
+
301
+ Swift 6, Rust, the C++ static analysis optimizer, HTTP servers on the Go
302
+ target, and the operator polyfill system are documented in
303
+ [TARGET_NOTES.md](TARGET_NOTES.md).
304
+
305
+ ## Testing
306
+
307
+ ```bash
308
+ npm test # Run all tests
309
+ npm run test:es6 # JavaScript/ES6 tests only
310
+ npm run test:python # Python target tests
311
+ npm run test:dart # Dart target tests (requires Dart SDK)
312
+ npm run test:go # Go target tests
313
+ npm run test:rust # Rust target tests
314
+ npm run test:syntaxapp # The syntax app on every target
315
+ ```
316
+
317
+ ES6/JavaScript has full runtime coverage; Python and Go have compilation and
318
+ runtime tests; Rust has compilation tests, with runtime tests in progress.
319
+
320
+ ### Syntax app (every target, measured)
321
+
322
+ [`tests/syntax_app/`](tests/syntax_app/README.md) is one program that uses 203
323
+ of the 207 core operator names of `compiler/Lang.rgr` together with classes,
324
+ inheritance, traits, records, enums, extensions, lambdas, optionals, buffers,
325
+ custom operators and the collection methods of `lib/stdlib.rgr`.
326
+ `npm run test:syntaxapp` compiles it — and each of its sections on its own — to
327
+ all fifteen targets the CLI accepts, then builds and runs the output with
328
+ `node`, `tsc`, `go`, `python3`, `rustc`, `g++`, `javac`, `php` and `lli`,
329
+ whichever of them the machine has, and compares what each one printed with the
330
+ output of the reference target.
331
+
332
+ The result is a matrix that the test asserts against a checked-in baseline, so
333
+ a target getting worse **and** a target getting better both fail until the
334
+ record is updated (`npm run test:syntaxapp:update`). The current measurement is
335
+ in [`tests/syntax_app/TARGET_REPORT.md`](tests/syntax_app/TARGET_REPORT.md) and
336
+ what does not work at all is in
337
+ [`tests/syntax_app/known_gaps.md`](tests/syntax_app/known_gaps.md).
338
+
339
+ ## The JavaScript engine, built and measured
340
+
341
+ `gallery/game_engine/v2/interp` is a JavaScript interpreter written in Ranger —
342
+ so it compiles to every target the compiler has. Four commands build it and
343
+ measure it, and each skips any target whose toolchain is not installed.
344
+
345
+ ```bash
346
+ npm run jsengine:check # report which C++/libstdc++ toolchain is usable
347
+ npm run jsengine:build # es6 module + cpp and rust binaries
348
+ npm run jsengine:bench # against Node, and QuickJS if `qjs` is on PATH
349
+ npm run jsengine:conformance # 1303 JS probes, answers checked against Node
350
+ npm run jsengine:all # all three in order
351
+ ```
352
+
353
+ `jsengine:build` needs a C++ compiler for the cpp target and `rustc` for the
354
+ Rust one; without them it still writes the generated source and says which it
355
+ skipped. Run `npm run jsengine:check` first if a native build fails — it probes
356
+ for a compiler that can use `-cpp-single-thread` (libstdc++
357
+ `__shared_ptr` / `_S_single`).
358
+
359
+ On **macOS**, Apple's Command Line Tools `g++` is clang + libc++ and **cannot**
360
+ build that fast path. Install real GCC:
361
+
362
+ ```bash
363
+ brew install gcc # provides g++-14 / g++-15 / …
364
+ npm run jsengine:check # should report rg_ptr: OK
365
+ npm run jsengine:build
366
+ ```
367
+
368
+ Without Homebrew GCC the build still proceeds, but omits `-cpp-single-thread`
369
+ (atomic `std::shared_ptr` — correct, slower). Set `CXX` to pin a compiler.
370
+ `jsengine:bench` reports one table across every engine that got built:
371
+
372
+ ```
373
+ case node qjs es6 cpp rust vs node ok
374
+ loop 0.031 0.530 2.574 1.574 2.229 51.4x OK
375
+ fib 0.134 0.690 7.079 6.380 6.262 46.9x OK
376
+ ...
377
+ against QuickJS (lower is better, 1.0x would be parity):
378
+ es6 4.0x (6.2x excluding strcat)
379
+ cpp 2.9x (4.7x excluding strcat)
380
+ rust 3.1x (5.0x excluding strcat)
381
+ ```
382
+
383
+ Two things about that table are worth knowing before quoting it. **Every row is
384
+ checked against Node's answer** and the command exits non-zero if any engine
385
+ disagrees — a fast wrong answer is not a result. And **the strcat row flatters
386
+ us**: QuickJS 2021-03-27 has no string ropes, so its `s += "ab"` is quadratic.
387
+ Quote the excluding-strcat number. [`QUICKJS_COMPARISON.md`](QUICKJS_COMPARISON.md)
388
+ has the measurement methodology and a source-level comparison of what the two
389
+ engines do differently.
390
+
391
+ `jsengine:octane` runs the same Octane v9 suites published on
392
+ [zoo.js.org](https://zoo.js.org/) — Richards, DeltaBlue, Crypto, RayTrace,
393
+ EarleyBoyer, RegExp, Splay and NavierStokes — on every target that was built,
394
+ and places the result against same-machine Node and the published amd64 V8
395
+ column.
396
+
397
+ Octane’s harness times suites with `performance.now` (fractional `liveClock`
398
+ ms). Prefer that over quoting older tables that used `new Date()` — Date is
399
+ TimeClip’d to whole milliseconds and pinned many native scores to a few
400
+ discrete buckets. `jsengine:bench` still times from outside the engine and is
401
+ the one to use for cross-target microbenchmarks.
402
+
403
+ For the engine's own test coverage:
404
+
405
+ ```bash
406
+ npm run jsengine:test # the runtime-conformance suite, in-process
407
+ npm run jsengine:test:targets # the engine compiled to every other target
408
+ ```
409
+
410
+ ## Known issues
411
+
412
+ `toString` as a method name crashes the compiler (use `getSymbol` or a similar
413
+ name), the Go target has integer division type conversion issues, and the
414
+ Python target has inheritance constructor argument issues. See
415
+ [ISSUES.md](ISSUES.md) for the full list and status.
416
+
417
+ ## AI documentation
418
+
419
+ Language questions for humans and agents belong on the
420
+ [documentation site](https://terotests.github.io/Ranger/docs/), especially the
421
+ [FAQ](https://terotests.github.io/Ranger/docs/faq/). Repo-local agent notes:
422
+
423
+ - [`AGENTS.md`](AGENTS.md) — git/PR workflow and syntax gotchas
424
+ - [`ai/README.md`](ai/README.md) — index of what remains under `ai/`
425
+ - [`ai/QUICKREF.md`](ai/QUICKREF.md) — offline syntax card
426
+ - [`ai/GRAMMAR.md`](ai/GRAMMAR.md) — simplified BNF and operator-template notation
427
+ - [`ai/INTROSPECTION.md`](ai/INTROSPECTION.md) — compiler introspection API (type at
428
+ line/column, class shape) for IDE hover, autocomplete, and type-safe generation
429
+
430
+ The long `ai/INSTRUCTIONS.md` / `ai/EXAMPLES.md` guides were removed; they
431
+ duplicated the docs site and had drifted (`.clj` paths, obsolete operators).
432
+
433
+ ## Installing the compiler
434
+
435
+ Install the compiler from npm:
436
+
437
+ ```
438
+ npm install -g ranger-compiler
439
+ ```
440
+
441
+ Running `ranger-compiler` without arguments shows available command-line options:
442
+
443
+ ```
444
+ Ranger Compiler v3.0.1
445
+
446
+ Usage: rgrc <file> [options] [flags]
447
+ Options: -<option>=<value>
448
+ -l=<value> Selected language, one of es6, go, scala, java7, swift3, swift6, kotlin, cpp, php, csharp, python, rust
449
+ -d=<value> output directory, default directory is "bin/"
450
+ -o=<value> output file, default is "output.<language>"
451
+ -classdoc=<value> write class documentation .md file
452
+ -operatordoc=<value> write operator documention into .md file
453
+ -apidoc=<value> write the API documentation artifacts into this subdirectory
454
+ -apiformat=<value> which API artifacts to write: json, markdown, report (default json,markdown)
455
+ -csnamespace=<value> C# namespace for the generated types
456
+ -ktpackage=<value> Kotlin package for the generated types
457
+ Flags: -<flag>
458
+ -apipackage Write the packaging the target ecosystem expects (package.json for npm, .csproj and docfx.json for NuGet)
459
+ -apistrict An undocumented public declaration or parameter is an error, not a warning
460
+ -keep-examples Emit the functions named by `example`. They are type checked either way; by default they are left out
461
+ -forever Leave the main program into eternal loop (Go, Swift)
462
+ -allowti Allow type inference at target lang (creates slightly smaller code)
463
+ -plugins-only ignore built-in language output and use only plugins
464
+ -plugins (node compiler only) run specified npm plugins -plugins="plugin1,plugin2"
465
+ -strict Strict mode. Do not allow automatic unwrapping of optionals outside of try blocks.
466
+ -typescript Writes JavaScript code with TypeScript annotations
467
+ -npm Write the package.json to the output directory
468
+ -nodecli Insert node.js command line header #!/usr/bin/env node to the beginning of the JavaScript file
469
+ -nodemodule Export classes as CommonJS modules using module.exports (disables static main function)
470
+ -esm Export classes as ES6/ESM modules using export keyword (disables static main function)
471
+ -sourcemap Emit .js.map / .ts.map with embedded .rgr sourcesContent (ES6/TypeScript only)
472
+ -client the code is ment to be run in the client environment
473
+ -scalafiddle scalafiddle.io compatible output
474
+ -compiler recompile the compiler
475
+ -copysrc copy all the source codes into the target directory
476
+ Pragmas: (inside the source code files)
477
+ @noinfix(true) disable operator infix parsing and automatic type definition checking
478
+ ```
479
+
480
+ ### JavaScript Module Formats
481
+
482
+ The compiler supports three JavaScript module output formats:
483
+
484
+ | Flag | Format | Output | Use Case |
485
+ | ------------- | -------- | ------------------------- | -------------------------------- |
486
+ | (none) | Plain JS | No exports, runs `main()` | Standalone scripts |
487
+ | `-nodemodule` | CommonJS | `module.exports.X = X;` | Node.js require() |
488
+ | `-esm` | ES6/ESM | `export class X` | Modern ES modules, import/export |
489
+
490
+ **Examples:**
491
+
492
+ ```bash
493
+ # Standalone JavaScript (runs main function)
494
+ node bin/output.js -es6 myfile.rgr -o=myfile.js
495
+
496
+ # CommonJS module (.cjs)
497
+ node bin/output.js -es6 -nodemodule myfile.rgr -o=myfile.cjs
498
+
499
+ # ES6/ESM module (.mjs)
500
+ node bin/output.js -es6 -esm myfile.rgr -o=myfile.mjs
501
+ ```
502
+
503
+ **File Extensions:**
504
+ The compiler automatically detects JavaScript-related extensions (`.js`, `.ts`, `.mjs`, `.cjs`) and won't double-add them. You can safely specify the full filename with extension.
505
+
506
+ ### JavaScript / TypeScript source maps (`-sourcemap`)
507
+
508
+ Use `-sourcemap` with `-es6` or `-typescript` to emit a sibling `.js.map` / `.ts.map` file and append `//# sourceMappingURL=…` to the generated output. Kotlin, Swift, and other non-JS targets ignore this flag.
509
+
510
+ **What you get**
511
+
512
+ | Output | Purpose |
513
+ | --- | --- |
514
+ | `output.js.map` | Source map v3 (VLQ `mappings`, `names`, `sources`) |
515
+ | `sourcesContent` | Full `.rgr` source embedded in the map — Chrome DevTools can open `.rgr` files without a separate file server |
516
+ | Statement + expression mappings | `LiveCompiler.WalkNode` walk context plus `outMapped()` on identifiers, calls, literals |
517
+
518
+ **Compile example**
519
+
520
+ ```bash
521
+ # Standalone ES module + map
522
+ node bin/output.js -es6 -esm -nodemodule -sourcemap ./myapp/App.rgr -o=app.js
523
+
524
+ # Result: bin/app.js and bin/app.js.map
525
+ ```
526
+
527
+ **Debug in Chrome / Edge**
528
+
529
+ 1. Serve the generated `.js` (and `.map` beside it). Vite/webpack are optional when `sourcesContent` is embedded.
530
+ 2. Open DevTools → **Sources**. Original `.rgr` files appear under the map tree (from `sourcesContent`).
531
+ 3. Set breakpoints on **executable** lines (e.g. `def`, `if`, `return`) — not only blank lines or signatures.
532
+ 4. Breakpoints must be **solid red**. A hollow/grey breakpoint means no mapping for that line; rebuild with `-sourcemap` and hard-refresh (disable cache).
533
+
534
+ **Tests**
535
+
536
+ ```bash
537
+ npm run compile
538
+ npx vitest run tests/compiler-sourcemap.test.ts
539
+ ```
540
+
541
+ **Implementation notes** (for compiler hackers)
542
+
543
+ - `compiler/ng_SourceMap.rgr` — `SourceMapBuilder`, VLQ encoder, `addMappingFromNode()` uses `node.getLine()` + `node.code.getColumn(sp)` (not stale `node.row`).
544
+ - `compiler/ng_writer.rgr` — `lineNumber` / `columnNumber` on emit, `walkNodeStack`, `outMapped()`, `.map` write in `CodeFileSystem.saveTo`.
545
+ - Flag: `compiler/ng_Compiler.rgr` → `flag sourcemap`; enabled in `VirtualCompiler.rgr` via `fileSystem.enableSourceMaps()`.
546
+
547
+ ## Getting started with Hello World
548
+
549
+ Create file `hello.rgr`
550
+
551
+ ```
552
+ class Hello {
553
+ sfn m@(main):void () {
554
+ print "Hello World"
555
+ }
556
+ }
557
+ ```
558
+
559
+ ```
560
+ ranger-compiler hello.rgr ; writes bin/output.js
561
+ ranger-compiler hello.rgr -o=hello.js
562
+ ```
563
+
564
+ [The first program](https://terotests.github.io/Ranger/docs/start/first-program/)
565
+ walks through the same program and shows the output the compiler writes for Go,
566
+ Python and Rust.
567
+
568
+ ## Compiling using TypeScript
569
+
570
+ The compiler can be used from TypeScript, which makes possible to create new versions of the
571
+ compiler just using TypeScript.
572
+
573
+ Note: the example requires `Lang`, `stdlib`, `stdops`, and `JSON` to be loaded for the compiler. In this example they are loaded from the filesystem using `readFileSync`.
574
+
575
+ ```typescript
576
+ // Notice this part of example is required:
577
+ addFile("Lang.rgr", fs.readFileSync("./libs/Lang.rgr", "utf8"));
578
+ addFile("stdlib.rgr", fs.readFileSync("./libs/stdlib.rgr", "utf8"));
579
+ addFile("stdops.clj", fs.readFileSync("./libs/stdops.clj", "utf8"));
580
+ addFile("JSON.clj", fs.readFileSync("./libs/JSON.clj", "utf8"));
581
+ ```
582
+
583
+ The full compiler code:
584
+
585
+ ```typescript
586
+ import * as R from "ranger-compiler";
587
+ import { CodeNode } from "ranger-compiler";
588
+
589
+ const compilerInput = new R.InputEnv();
590
+ compilerInput.use_real = false;
591
+
592
+ // manually create a filesystem
593
+ const folder = new R.InputFSFolder();
594
+ const addFile = (name: string, contents: string) => {
595
+ const newFile = new R.InputFSFile();
596
+ newFile.name = name;
597
+ newFile.data = contents;
598
+ folder.files.push(newFile);
599
+ };
600
+ addFile(
601
+ "hello.clj",
602
+ `
603
+ class hello {
604
+ static fn main() {
605
+ print "Hello World"
606
+ }
607
+ }
608
+ `
609
+ );
610
+
611
+ // compiler requires language definition and libraries to work
612
+ const fs = require("fs");
613
+ addFile("Lang.clj", fs.readFileSync("./libs/Lang.clj", "utf8"));
614
+ addFile("stdlib.clj", fs.readFileSync("./libs/stdlib.clj", "utf8"));
615
+ addFile("stdops.clj", fs.readFileSync("./libs/stdops.clj", "utf8"));
616
+ addFile("JSON.clj", fs.readFileSync("./libs/JSON.clj", "utf8"));
617
+
618
+ compilerInput.filesystem = folder;
619
+
620
+ // set compiler options -l=es6 -typescript
621
+ const params = new R.CmdParams();
622
+ // target language is Go
623
+ params.params["l"] = "go";
624
+ params.params["o"] = "hello.go";
625
+ params.values.push("hello.clj");
626
+ compilerInput.commandLine = params;
627
+
628
+ // Run compiler
629
+ const vComp = new R.VirtualCompiler();
630
+
631
+ // Check results...
632
+ const res = await vComp.run(compilerInput);
633
+
634
+ // browse through the target compiler file system
635
+ res.fileSystem.files.forEach((file) => {
636
+ console.log(file.getCode());
637
+ });
638
+ ```
639
+
640
+ ## Switching to different target language
641
+
642
+ `-l=<language>` selects the target; running the compiler with no arguments
643
+ lists the available ones, and the supported versions are under
644
+ [Targets and compatibility](#targets-and-compatibility). JavaScript
645
+ additionally has `-typescript`, which adds TypeScript annotations to the
646
+ generated source.
647
+
648
+ [Target languages](https://terotests.github.io/Ranger/docs/targets/overview/)
649
+ documents what each target writes for the main function, and the semantic
650
+ differences a portable program has to know (integer division, the sign of `%`,
651
+ string indexing, reference counting).
652
+
653
+ # Operators
654
+
655
+ Operators are short, typed commands like `get` or `push`. The compiler holds no
656
+ code for them, only one emission template per target language, which is how
657
+ platform-specific code and native polyfills are expressed. `M_PI` in
658
+ `compiler/Lang.rgr` is a small example:
659
+
660
+ ```
661
+ M_PI mathPi:double () {
662
+ templates {
663
+ es6 ("Math.PI")
664
+ go ( "math.Pi" (imp "math"))
665
+ swift3 ( "Double.pi" (imp "Foundation"))
666
+ java7 ( "Math.PI" (imp "java.lang.Math"))
667
+ php ("pi()")
668
+ cpp ("M_PI" (imp "<math.h>"))
669
+ csharp ("Math.PI" (imp "System"))
670
+ }
671
+ }
672
+ ```
673
+
674
+ A target with no template of its own, and no `*` fallback, emits nothing — so
675
+ adding a target to an operator is a one-line change. Operators can also be
676
+ written as macros in Ranger itself.
677
+
678
+ **Reference:**
679
+ [the operator concept page](https://terotests.github.io/Ranger/docs/language/operators/)
680
+ explains templates and the second mechanism (type methods, which every target
681
+ gets for free), and
682
+ [the generated reference](https://terotests.github.io/Ranger/docs/reference/operators/statements/)
683
+ lists every operator with its signature, per-target support, and compiled
684
+ example output. The `-operatordoc=<file>` compiler option writes the same kind
685
+ of table locally when you need one without a network.
686
+
687
+ # Plugins
688
+
689
+ Compiling as CommonJS module:
690
+
691
+ ```
692
+ ranger-compiler hello.clj -npm -nodemodule
693
+ ```
694
+
695
+ Compiling as ES6/ESM module:
696
+
697
+ ```
698
+ ranger-compiler hello.clj -npm -esm
699
+ ```
700
+
701
+ Example
702
+
703
+ ```javascript
704
+ Import "VirtualCompiler.clj"
705
+
706
+ flag npm (
707
+ name "hello"
708
+ version "0.0.1"
709
+ description "Plugin Hello World"
710
+ author "Tero Tolonen"
711
+ license "MIT"
712
+ )
713
+
714
+ class Plugin {
715
+ fn features:[string] () {
716
+ return ([] "postprocess")
717
+ }
718
+ fn postprocess (root:CodeNode ctx:RangerAppWriterContext wr:CodeWriter) {
719
+ print "*** plugin postprocess was called ***"
720
+ }
721
+ }
722
+ ```
723
+
724
+ # Notes about the syntax
725
+
726
+ > The rest of this README is the language reference. The documentation site
727
+ > covers part of the same ground in shorter, edited pages —
728
+ > [program structure](https://terotests.github.io/Ranger/docs/language/structure/),
729
+ > [types](https://terotests.github.io/Ranger/docs/language/types/) and
730
+ > [optional values](https://terotests.github.io/Ranger/docs/language/optionals/) —
731
+ > and those pages carry the per-target detail (what an optional compiles to in
732
+ > Go, Rust and Swift, for instance) that the sections below do not. What follows
733
+ > here is the material the site does not have yet: traits, custom operators,
734
+ > system classes, unions, class extensions, and the annotation list.
735
+
736
+ Ranger syntax is originally based on Lisp -language syntax and most operators will use prefix notation. However, the Ranger modifies
737
+ the original Lisp so that inside block expression `{ ... }` there is no need to insert parenthesis which makes the language appear to
738
+ be a bit more like standard languages. Thus you can write exressions like
739
+
740
+ ```
741
+ class Hello {
742
+ fn sayHello:void () {
743
+ def x 20
744
+ if ( x < 10 ) {
745
+ print "x < 10"
746
+ } {
747
+ print "x >= 10"
748
+ }
749
+ }
750
+ }
751
+ ```
752
+
753
+ However, when you go deeper in the expression you may have to include the parenthesis, for example when invoking object you have to write
754
+
755
+ ```
756
+ def obj (new Hello)
757
+ ```
758
+
759
+ For most common mathematical symbols and boolean operators infix notation can be used and they are automatically converted to lisp expressions.
760
+ Thus you can write expressions such as `(x + y * z)` instead of `(+ x (* y z))`
761
+
762
+ ```
763
+ def x 100
764
+ def y 200
765
+ def z ( x + y * 10)
766
+ if ( x < 20 || y == 0 ) {
767
+
768
+ }
769
+ ```
770
+
771
+ The assigment operator is also automatically prefixed from infix notation so you can say
772
+
773
+ ```
774
+ x = y
775
+ ```
776
+
777
+ Instead of common lisp syntax `(= x y)`
778
+
779
+ ## Functions, main and comments
780
+
781
+ `fn` declares a function of an object and `sfn` a static function of the class.
782
+ Each file can have a static main function, which is the entry point of the
783
+ program. A function that is not `void` must return a value with `return`. A
784
+ comment starts with `;`.
785
+
786
+ ```
787
+ ; here is a comment
788
+ class Hello {
789
+ sfn main@(main):void () {
790
+ def o (new Hello)
791
+ o.SomeNonStaticFn()
792
+ }
793
+ fn SomeNonStaticFn () {
794
+ }
795
+ sfn SomeStaticFn () {
796
+ }
797
+ }
798
+
799
+ Hello.SomeStaticFn() ; calling a static function of a class
800
+ ```
801
+
802
+ [Program structure](https://terotests.github.io/Ranger/docs/language/structure/)
803
+ covers `class`, `record`, `systemclass`, `Import`, `Extend` and `Enum` in one
804
+ table, and explains how blocks are passed to operators.
805
+
806
+ ## API documentation: the `doc { }` tail
807
+
808
+ A declaration can carry a `doc { … }` block as its **tail**, after the body. The
809
+ signature stays clean and the documentation is visibly an attachment to the
810
+ declaration rather than a part of the program. It is compiler metadata, not a
811
+ comment: `;` still documents the implementation, `doc` documents the interface.
812
+
813
+ ```
814
+ class EVGA11yTree {
815
+ def focusId:string "" doc {
816
+ public
817
+ description "The identifier of the currently focused node."
818
+ }
819
+
820
+ fn find:EVGA11yNode ( id:string ) {
821
+ ...
822
+ } doc {
823
+ public
824
+ description "Finds an accessibility node by its stable identifier."
825
+ param id "The stable accessibility identifier."
826
+ returns "The matching node."
827
+ since "1.2"
828
+ see EVGA11yNode
829
+ }
830
+
831
+ fn rebuildIndex:void () {
832
+ } doc {
833
+ description "Rebuilds the lookup index. Not part of the public API."
834
+ }
835
+ } doc {
836
+ public
837
+ description "A platform independent accessibility tree."
838
+ }
839
+ ```
840
+
841
+ **The block never restates what the compiler already knows.** There is no
842
+ `param x int` and no `returns void`: the types come from the signature, and a
843
+ `param` line that carries a type is a compile error. What the doc block carries
844
+ is what no analysis can recover — prose, audience, version history and
845
+ cross-references.
846
+
847
+ **Documentation is the API declaration.** There is no `export fn` keyword:
848
+
849
+ ```
850
+ no doc block -> internal, undocumented
851
+ doc { … } -> documented, internal
852
+ doc { public … } -> exported public API
853
+ ```
854
+
855
+ An undocumented function has no spelling for `public`. The block is checked
856
+ against the signature it documents, which is the point of it being structured:
857
+ a `param` that names no parameter, a `param` documented twice, a `returns` on a
858
+ void function and a public member of an internal class are all compile errors.
859
+
860
+ Entries: `public`, `internal`, `description`, `param`, `returns`, `throws`,
861
+ `since`, `deprecated { since use description }`, `see`, `example`, `category`,
862
+ `experimental`, `platform`, and `target <name> { … }` for markup that only one
863
+ language has.
864
+
865
+ **`example` names a function, not a string.** The sample is compiled and type
866
+ checked with the rest of the program, rendered into each target's doc comment
867
+ in *that target's* syntax — `const g = new Greeter()` on JavaScript,
868
+ `g.greet(name : "world")` on Swift — and then left out of the emitted code.
869
+ `-keep-examples` puts it back.
870
+
871
+ ```ranger
872
+ fn greet:string ( name:string ) {
873
+ ...
874
+ } doc {
875
+ public
876
+ description "Builds a greeting for a name."
877
+ example greetExample
878
+ }
879
+
880
+ class GreeterExamples {
881
+ sfn greetExample:void () {
882
+ def g:Greeter (new Greeter())
883
+ print (g.greet("world"))
884
+ }
885
+ }
886
+ ```
887
+
888
+ The compiler writes the documentation into the generated code in the target's
889
+ own form and, with `-apidoc=<dir>`, a target-independent `api.json`, a Markdown
890
+ reference and an `api.txt` report of the public surface. `-apipackage` adds the
891
+ packaging the ecosystem expects:
892
+
893
+ ```bash
894
+ # an npm package documentation.js renders with no configuration
895
+ rgrc -es6 a11y.rgr -d=out -o=index.js -nodemodule \
896
+ -apidoc=docs -apipackage -name=evg-a11y -version=1.2.0 -license=MIT
897
+
898
+ # a NuGet project whose XML documentation DocFX reads
899
+ rgrc -l=csharp a11y.rgr -d=out -o=EvgA11y.cs \
900
+ -apidoc=docs -apipackage -name=Evg.A11y -version=1.2.0 -license=MIT
901
+
902
+ # a pub package `dart doc` renders. The layout matters: dart doc reads lib/
903
+ # and skips lib/src/, so `public` decides what the documentation shows.
904
+ rgrc -l=dart a11y.rgr -d=pkg/lib/src -o=evg_a11y_impl.dart \
905
+ -apidoc=docs -apipackage -name=evg_a11y -version=1.2.0
906
+ ```
907
+
908
+ Six targets carry the documentation into the generated code today:
909
+
910
+ | Target | Comment form | Packaging | Doc tool |
911
+ | --- | --- | --- | --- |
912
+ | JavaScript | JSDoc (`@param {string} id`, `@public` / `@private`) | `package.json` | documentation.js |
913
+ | C# | XML docs (`<summary>`, `<param>`, `<seealso cref>`, `[System.Obsolete]`) | `.csproj`, `docfx.json` | DocFX, Sandcastle |
914
+ | Kotlin | KDoc (`@param`, `@return`, `@see`, `@Deprecated(… ReplaceWith)`) | `build.gradle.kts` | Dokka |
915
+ | Swift | DocC (`- Parameter`, `- Returns`, `> Since:`, `@available`) | `Package.swift`, `.docc` catalog | DocC |
916
+ | Python | Google docstrings (`Args:`, `Returns:`, `.. versionadded::`) | `pyproject.toml`, `__all__` | pdoc, Sphinx |
917
+ | Dart | dartdoc (`/// [id] …`, `[Symbol]`, `@Deprecated`) | `pubspec.yaml`, generated `export … show` | `dart doc` |
918
+
919
+ Each also gets a namespace, package or export surface from the doc blocks. On
920
+ Dart and Python `public` writes the export list itself — a barrel file and
921
+ `__all__` are exactly the kind of list that rots when a person maintains it. A
922
+ class with no doc block is not opted into the API model and compiles exactly as
923
+ it did before. Design and the remaining targets:
924
+ [`PLAN_API_DOCS.md`](PLAN_API_DOCS.md).
925
+
926
+ ## Types
927
+
928
+ Type inference determines the type of local variables and class properties, or
929
+ the program declares it after a colon:
930
+
931
+ ```
932
+ def x 100 ; inferred type = int
933
+ def y:int 200
934
+ def o (new myClass) ; inferred type myClass
935
+ ```
936
+
937
+ The primitive types are `int`, `boolean`, `string`, `double`, `char` and
938
+ `charbuffer`, plus the fixed-width integer types; a function that returns
939
+ nothing is `void`. Arrays, hashes and anonymous functions are usable as
940
+ variable types but need a signature. `Enum`, `class`, `systemclass`,
941
+ `systemunion` and `trait` need a declaration of their own.
942
+
943
+ [Types](https://terotests.github.io/Ranger/docs/language/types/) has the full
944
+ table with example values, the buffer types and what each one compiles to.
945
+
946
+ ## Strings, enums and collections
947
+
948
+ String literals use JSON escaping rules and can be multiline:
949
+
950
+ ```
951
+ def long_string "
952
+ this is
953
+ a multiline string
954
+ "
955
+ ```
956
+
957
+ The rest moved to the documentation site, where each operator also shows the
958
+ code it writes per target:
959
+ [Strings](https://terotests.github.io/Ranger/docs/language/strings/) (the
960
+ operator set, concatenation, the code-point index rule) and
961
+ [Types](https://terotests.github.io/Ranger/docs/language/types/) (arrays,
962
+ hashes, `get` returning an optional, and `Enum`).
963
+
964
+ ## Anonymous functions / lambdas
965
+
966
+ Anonymous function type declaration is automatically inferred
967
+
968
+ ```
969
+ def name "foo"
970
+ def myFilter (fn:boolean (param:string) {
971
+ return (param == name)
972
+ })
973
+ if(myFilter("foo")) {
974
+ print "it was foo"
975
+ }
976
+ ```
977
+
978
+ To give declare Anonymous function as parameter of function you must include the full signature, for
979
+ example for a callback taking `string` and `int` signature is `fn:void (txt:string i:int)`
980
+
981
+ ```
982
+ fn foo:void ( callback:( fn:void (txt:string i:int)) ) {
983
+ callback("got this?" 10)
984
+ }
985
+ ```
986
+
987
+ When giving lambda as a parameter, the formal type definition can be omitted, the named parameters are
988
+ automatically declared to the block scope of the lambda.
989
+
990
+ ```
991
+ this.foo({
992
+ print txt + " = " i
993
+ })
994
+ ```
995
+
996
+ 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.
997
+
998
+ # Automatically infixed math support
999
+
1000
+ It is easy to define new mathematical operations in the Lang.clj file or in modules. However, some mathematical operations are automatically infixed
1001
+ for easier usage. Thus, instead of using common lips notation `(* 4 10)` you can use easier to read infixed `4 * 10` -syntax
1002
+
1003
+ ## Boolean logic operators
1004
+
1005
+ ```
1006
+ a && b
1007
+ a || b
1008
+ ```
1009
+
1010
+ ## Math operators
1011
+
1012
+ ```
1013
+ a * b
1014
+ a / b
1015
+ a - b
1016
+ a + b
1017
+ ```
1018
+
1019
+ ## Logical comparisions
1020
+
1021
+ ```
1022
+ a < b
1023
+ a <= b
1024
+ a > b
1025
+ a >= b
1026
+ a != b
1027
+ ```
1028
+
1029
+ # Common set of Operators and the Grammar file
1030
+
1031
+ The file `compiler/Lang.rgr` holds the common set of operators and the
1032
+ compilation rules. The most common operators — `to_double`, `read_file`,
1033
+ `array_length` and the rest — are defined there, and the
1034
+ [generated reference](https://terotests.github.io/Ranger/docs/reference/operators/statements/)
1035
+ is built from it. Editing the file makes it easy to extend the language with
1036
+ new operators or to change existing rules, but it describes the common set of
1037
+ rules and should be edited sparingly, not daily.
1038
+
1039
+ The file has couple of sections, but the `reserved_words` and `commands`. The Reserved words section declares (surprise!)
1040
+ the reserved words and their transformation. This is required because for example in Go the word `map` is a keyword and can
1041
+ not be used unless it is conveted to some other name, for example to `FnMap`.
1042
+
1043
+ ```
1044
+ reserved_words {
1045
+ * {
1046
+ map FnMap
1047
+ forEach forEachItem
1048
+ self _self
1049
+ func _func
1050
+ }
1051
+ cpp {
1052
+ operator _operator
1053
+ static _static
1054
+ union _union
1055
+ bool _bool
1056
+ ref _ref
1057
+ class _class
1058
+ new _new
1059
+ delete _delete
1060
+ template _template
1061
+ namespace _namespace
1062
+ virtual _virtual
1063
+ public _public
1064
+ private _private
1065
+ protected _protected
1066
+ }
1067
+ go {
1068
+ type _type
1069
+ }
1070
+ rust {
1071
+ type r#type
1072
+ static r#static
1073
+ ref r#ref
1074
+ union r#union
1075
+ bool r#bool
1076
+ }
1077
+ swift3 {
1078
+ operator _operator
1079
+ static _static
1080
+ init _init
1081
+ }
1082
+ swift6 {
1083
+ operator _operator
1084
+ static _static
1085
+ init _init
1086
+ }
1087
+ }
1088
+ ```
1089
+
1090
+ The `*` section defines global mappings that apply to all target languages. Language-specific sections (like `cpp`, `rust`, `go`, `swift3`, `swift6`) define additional reserved word mappings for that particular target. For Rust, the `r#` prefix is used to escape keywords (raw identifiers).
1091
+
1092
+ What the result should be is of course highly opinionated. In this example, the line `map FnMap` means that if possible the
1093
+ compiler will transform anything named `map` to `fnMap` if possible. If transformation is not possible, compiler error is
1094
+ generated.
1095
+
1096
+ The common operators are declared in section `commands`, which describe commands, their expected parameters
1097
+ and return values and rules on how they should be compiled into the target languages, possible imported libraries
1098
+ and possible macros or helper function which should be created if the operator is used.
1099
+
1100
+ Example of simple operator is `(M_PI)` which will return double value of mathematical symbol "pi".
1101
+
1102
+ ```
1103
+ commands {
1104
+ M_PI mathPi:double () {
1105
+ templates {
1106
+ es6 ("Math.PI")
1107
+ go ( "math.Pi" (imp "math"))
1108
+ swift3 ( "Double.pi" (imp "Foundation"))
1109
+ java7 ( "Math.PI" (imp "java.lang.Math"))
1110
+ php ("pi()")
1111
+ cpp ("M_PI" (imp "<math.h>"))
1112
+ }
1113
+ }
1114
+ ...
1115
+ ```
1116
+
1117
+ Most operators are simple, but some require creating custom macros, helpoer functions and some of them are so complex
1118
+ that they may be implemented in the compiler core.
1119
+
1120
+ # Modules, classes and operators
1121
+
1122
+ The basic unit of the program is class. The functions of classes can not be overloaded at the moment, which means that you can not
1123
+ have two functions with different parameters or different return values.
1124
+
1125
+ Each source file can import other files using `Import` command.
1126
+
1127
+ ```
1128
+ Import "Vec2.clj"
1129
+
1130
+ class vectorTest {
1131
+ fn testVectors () {
1132
+ def v (new Vec2 ( 5 4 ))
1133
+ }
1134
+ }
1135
+ ```
1136
+
1137
+ ## Class declaration
1138
+
1139
+ ```
1140
+ class fatherClass {
1141
+ def msg "Hello "
1142
+ fn foo:string ( txt:string ) {
1143
+ return (msg + txt)
1144
+ }
1145
+ }
1146
+ class childClass {
1147
+ Extends( fatherClass )
1148
+ }
1149
+ class mainProgram {
1150
+ sfn m@(main) {
1151
+ ; invoke the class
1152
+ def cc (new childClass)
1153
+ cc.foo("World!")
1154
+ }
1155
+ }
1156
+
1157
+ ```
1158
+
1159
+ ## Class constructor
1160
+
1161
+ ```
1162
+ class myClass {
1163
+ def name:string ""
1164
+ Constructor (n:string) {
1165
+ name = s
1166
+ }
1167
+ }
1168
+ ```
1169
+
1170
+ Notes:
1171
+
1172
+ 1. currently only a single variant of the constructor is possible.
1173
+ 2. as of this writing calling the parent class constructor does not work properly
1174
+
1175
+ ## Class invocation
1176
+
1177
+ ```
1178
+ def obj (new myClass ("name"))
1179
+ ```
1180
+
1181
+ classes without constructor can be invocated without arguments
1182
+
1183
+ ```
1184
+ def obj (new simpleClass)
1185
+ ```
1186
+
1187
+ ## Creating a class extension
1188
+
1189
+ Class extensions are useful for keeping classes simple and moving dependencies to external Modules
1190
+ which can extend the classes.
1191
+
1192
+ Extension can
1193
+
1194
+ - add new functions to the class
1195
+ - add new member variables to the class
1196
+
1197
+ ```
1198
+ extension childClass {
1199
+ def name:string ""
1200
+ fn bar:string ( txt:string ) {
1201
+ return ("Hello from exteision: " + txt)
1202
+ }
1203
+ }
1204
+ ```
1205
+
1206
+ ## Optional variables
1207
+
1208
+ An optional value must be unwrapped before use, and unwrapping a non-nullable
1209
+ value is a compiler error. Any variable declared without a value is optional,
1210
+ which corresponds to the Swift `?` type. The `@(optional)` annotation declares
1211
+ one explicitly, and some operators — `(get <hash> <key>)` among them — always
1212
+ return one.
1213
+
1214
+ ```
1215
+ def item@(optional):myClass
1216
+
1217
+ def strMap:[string:string]
1218
+ def str (get strMap "myKey")
1219
+ if(!null? str) {
1220
+ print (unwrap str)
1221
+ }
1222
+ ```
1223
+
1224
+ [Optional values](https://terotests.github.io/Ranger/docs/language/optionals/)
1225
+ lists the operators (`??`, `!!`, `unwrap`, `null?`, `!null?`, `wrap`,
1226
+ `nullify`), what each target uses for an empty value, and the `-strict` flag.
1227
+
1228
+ **Two warnings that apply to the current implementation.** Optionals are not
1229
+ "safe" in the sense of preventing programming errors: a variable that was
1230
+ unwrapped automatically can still be misused. Making them safer is planned, and
1231
+ the options are being considered. Ranger also does not protect against mistakes
1232
+ when automatically unwrapping long reference chains such as
1233
+ `obj.property.subProperty.foo`, where `property` and `subProperty` are optional.
1234
+
1235
+ ## Control flow
1236
+
1237
+ ### if
1238
+
1239
+ If statement is quite similar to other language, but `then` and `else` keywords are not used
1240
+
1241
+ ```
1242
+ def x 100
1243
+ if ( x < 10 ) {
1244
+ ; then branch
1245
+ } {
1246
+ ; else branch
1247
+ }
1248
+ ```
1249
+
1250
+ ### switch - case
1251
+
1252
+ Note: currently case statement does not support multiple matching values, it is planned to add support for that later.
1253
+
1254
+ ```
1255
+ def name "John"
1256
+ switch name {
1257
+ case "John" {
1258
+
1259
+ }
1260
+ case "Flat Eric" {
1261
+
1262
+ }
1263
+ default {
1264
+
1265
+ }
1266
+ }
1267
+ ```
1268
+
1269
+ ## Loops
1270
+
1271
+ ### for -loop
1272
+
1273
+ ```
1274
+ def list:[string]
1275
+ for list s:string i {
1276
+ print s
1277
+ }
1278
+ ```
1279
+
1280
+ You can use `break` and `continue` to control the for -loop.
1281
+
1282
+ ### while -loop
1283
+
1284
+ ```
1285
+ def cnt 10
1286
+ while (cnt > 0 ) {
1287
+ print "round " + cnt
1288
+ }
1289
+ ```
1290
+
1291
+ You can use `break` and `continue` to control the while -loop.
1292
+
1293
+ ## Custom operators
1294
+
1295
+ One of the most important features or Ranger is the ability to create custom operators which can target some specific language or all languages
1296
+ using macros. Together with `systemclass` they allow the system to integrate to target environment or to create new abstraction over existing
1297
+ native API's.
1298
+
1299
+ Operators allow type matching against
1300
+
1301
+ - defined primitive types
1302
+ - defined classes
1303
+ - Enums
1304
+ - optionality
1305
+ - traits
1306
+
1307
+ Operators can be writing directly target language construct or they can be macros, which write code in Ranger and the compiler will then
1308
+ transform the resulting AST tree into the target language's code using the conventions of target language. Which is better depends on the
1309
+ situation, for example operators for system classes usually are written directly to the traget language while operators which are using
1310
+ Ranger's own classes or datatypes are usually better to write with macros.
1311
+
1312
+ Simple example of useful macro is Matrix and Vector multiplication. Let's say that you have defined a Matrix class and
1313
+ want to overload the `*` -operator for easy matrix multiplication.
1314
+
1315
+ ```
1316
+ class Mat2 {
1317
+ def m0 1.0
1318
+ def m1 0.0
1319
+ def m2 0.0
1320
+ def m3 1.0
1321
+ def m4 0.0
1322
+ def m5 0.0
1323
+ fn multiply:Mat2 ( b:Mat2 ) {
1324
+ def t0 (m0*b.m0 + m1 * b.m2)
1325
+ def t2 (m2*b.m0 + m3 * b.m2)
1326
+ def t4 (m4*b.m0 + m5 * b.m2 + b.m4)
1327
+
1328
+ def res (new Mat2)
1329
+ res.m1 = (m0 * b.m1 + m1 * b.m3)
1330
+ res.m3 = (m2 * b.m1 + m3 * b.m3)
1331
+ res.m5 = (m4 * b.m1 + m5 * b.m3 + b.m5)
1332
+ res.m0 = t0
1333
+ res.m2 = t2
1334
+ res.m4 = t4
1335
+ return res
1336
+ }
1337
+ }
1338
+ operators {
1339
+ * base:Mat2 ( a:Mat2 b:Mat2) {
1340
+ templates {
1341
+ * @macro(true) ( (e 1 ) ".multiply(" (e 2) " )" )
1342
+ }
1343
+ }
1344
+ }
1345
+
1346
+ ```
1347
+
1348
+ The `* @macro(true)` means that we target all languages and this is a macro, not actual target language construct.
1349
+
1350
+ ## Custom operators and System classes
1351
+
1352
+ To integrate with the target languages running environment, Ranger modules can declare `systemclass` which can be used
1353
+ together with the code.
1354
+
1355
+ ```
1356
+ systemclass DOMElement {
1357
+ es6 DOMElement
1358
+ }
1359
+
1360
+ operators {
1361
+ find base:DOMElement ( id:string) {
1362
+ templates {
1363
+ es6 ("document.getElementById( " (e 1) " )")
1364
+ }
1365
+ }
1366
+ setAttribute _:void ( elem:DOMElement name:string value:string) {
1367
+ templates {
1368
+ es6 ( (e 1) ".setAttribute(" (e 2) ", " (e 3) ")" )
1369
+ }
1370
+ }
1371
+ }
1372
+
1373
+ class tester {
1374
+ fn modifyDom () {
1375
+ def e (find "#someelem")
1376
+ setAttribute( e "className", "activeElement")
1377
+ }
1378
+ }
1379
+ ```
1380
+
1381
+ Note: Definition of system classes will be revisited in near future and there will be potentially small changes to it.
1382
+
1383
+ ## Unions of system classes
1384
+
1385
+ Sometimes the system class can be of union type. This means that the traget language can accept multiple types in place of
1386
+ a single type.
1387
+
1388
+ ```
1389
+ systemunion DOMElementUnion ( DOMElement string )
1390
+ ```
1391
+
1392
+ The you can create operator which accepts either `DOMElement` or `string` and reduces that to a single type.
1393
+
1394
+ ## Traits
1395
+
1396
+ Traits are like extensions, which can be plugged into several classes using `does` keyword.
1397
+
1398
+ Traits
1399
+
1400
+ ```
1401
+ trait bar {
1402
+ fn hello() {
1403
+ print "Hello"
1404
+ }
1405
+ }
1406
+
1407
+ ; foo implements "bar" trait
1408
+ class foo {
1409
+ does bar
1410
+ }
1411
+ ```
1412
+
1413
+ Traits are very useful when used together with custom operators, because operators can also match traits.
1414
+
1415
+ Another useful feature of traits is their genericity. Traits take type
1416
+ parameters, and so do classes — see [Generic classes](#generic-classes) below —
1417
+ so a generic collection can be written either way.
1418
+
1419
+ ```
1420
+ trait GenericCollection @params(T S) {
1421
+ def items:[T]
1422
+ fn add (item:T) {
1423
+ push items item
1424
+ }
1425
+ fn map:S ( callback:( f:T (item:T)) ) {
1426
+ def res:S (new S ())
1427
+ for items ch@(lives):T i {
1428
+ def new_item@(lives):T (callback (ch))
1429
+ res.add(new_item)
1430
+ }
1431
+ return res
1432
+ }
1433
+ ; ... TODO: add more collection functions...
1434
+ }
1435
+
1436
+ ; then create a specific "string" collection..
1437
+ class StringCollection {
1438
+ does GenericCollection @params(string StringCollection)
1439
+ }
1440
+
1441
+ class Main {
1442
+ fn testCollection:void () {
1443
+ def coll:StringCollection (new StringCollection)
1444
+ coll.add("A")
1445
+ coll.add("B")
1446
+ def n (coll.map({
1447
+ return ("item = " + item)
1448
+ }))
1449
+ print (join n.items " ")
1450
+ }
1451
+ sfn hello@(main):void () {
1452
+ def hello (new Main ())
1453
+ hello.testCollection()
1454
+ }
1455
+ }
1456
+ ```
1457
+
1458
+ ## Generic classes
1459
+
1460
+ A class takes type parameters with the same `@params(...)` annotation a trait
1461
+ uses, and a reference names the arguments with `@(...)`:
1462
+
1463
+ ```
1464
+ class History @params(Op) {
1465
+ def ops:[Op]
1466
+
1467
+ fn record:void (op:Op) {
1468
+ push ops op
1469
+ }
1470
+ fn count:int () {
1471
+ return (array_length ops)
1472
+ }
1473
+ fn newest:Op () {
1474
+ def v:Op (last ops)
1475
+ return v
1476
+ }
1477
+ }
1478
+
1479
+ class Main {
1480
+ fn run:void () {
1481
+ def ints:History@(int) (new History@(int) ())
1482
+ ints.record(3)
1483
+ def strs:History@(string) (new History@(string) ())
1484
+ strs.record("a")
1485
+ }
1486
+ }
1487
+ ```
1488
+
1489
+ A type parameter can be used as an array element, a map value, a parameter
1490
+ type and a return type, and a generic class may hold another one at its own
1491
+ parameter:
1492
+
1493
+ ```
1494
+ class Store @params(T) {
1495
+ def byId:[string:T] ; a map value
1496
+ def slot:Cell@(T) (new Cell@(T) ()) ; another generic, at T
1497
+ fn take:T (id:string) { ; a return type
1498
+ def v:T (unwrap (get byId id))
1499
+ return v
1500
+ }
1501
+ }
1502
+ ```
1503
+
1504
+ An instantiation is an ordinary type, so it can be the element type of a
1505
+ collection, and a generic class may name itself at its own parameter:
1506
+
1507
+ ```
1508
+ class Tree @params(T) {
1509
+ def held:[T]
1510
+ def kids:[Tree@(T)] ; an array of instantiations
1511
+ fn adopt:void (k:Tree@(T)) { ; and itself as a parameter type
1512
+ push kids k
1513
+ }
1514
+ }
1515
+
1516
+ def byName:[string:Tree@(int)] ; and as a map value
1517
+ ```
1518
+
1519
+ A generic class may have a constructor with arguments and may `Extends` a
1520
+ plain class. The type argument itself may be a class, a record, a `shape`, a
1521
+ primitive, an array (`History@([string])`) or a map (`Store@([string:int])`).
1522
+
1523
+ There are no bounds, no constraints and no variance: nothing is asked of the
1524
+ argument type. Where a generic container needs to compare two values, pass the
1525
+ comparison in rather than reaching for a constraint.
1526
+
1527
+ **A generic class has no static side.** `sfn` inside one is not reachable —
1528
+ only the instantiations exist at run time, and `History_int.describe()` is not
1529
+ a name anybody should have to write. Put statics on a plain class beside it;
1530
+ `gallery/office/editor/OfficeHistory.rgr` splits exactly that way.
1531
+
1532
+ **How it compiles.** Each distinct instantiation is expanded into an ordinary
1533
+ concrete class before any writer runs — `History@(int)` becomes `History_int`
1534
+ — so the fourteen targets need no notion of generics at all. Two
1535
+ instantiations are two unrelated classes; neither can see the other's fields.
1536
+ Type arguments may themselves be collections (`History@([string])`
1537
+ instantiates `History_arr_string`), and a nested element type is spelled by
1538
+ nesting, as everywhere else in the language.
1539
+
1540
+ Because expansion happens at each reference, a generic class is declared once
1541
+ and never emitted on its own: nothing is written for `History` itself, only
1542
+ for the instantiations a program actually asks for.
1543
+
1544
+ **`@(optional)` is deliberately outside this.** An optional is not one thing
1545
+ across the targets — an optional string is a pointer on es6 and a plain
1546
+ `std::string` on C++, so `if body` compiles on JavaScript, Go and Python and
1547
+ does not compile on C++ at all (`gallery/book/ISSUES.md` #13). A `Maybe@(T)`
1548
+ built on top of that inconsistency would inherit it and spread it into every
1549
+ generic container, so the representation has to be settled first. Until it is,
1550
+ write a generic container over concrete values and let the caller decide what
1551
+ absence means.
1552
+
1553
+ ## Variable definitions
1554
+
1555
+ Values can be defined using `def` keyword.
1556
+
1557
+ ```
1558
+ def x:double
1559
+ def x:double 0.4 ; double with initializer
1560
+ def list1:[double] ; list of doubles
1561
+ def strList:[string] ; list of Strings
1562
+ def strMap:[string:string] ; map of string -> string
1563
+ def strObjMap:[string:someClass] ; map of string -> object of type someClass
1564
+ ```
1565
+
1566
+ # Advanced topics
1567
+
1568
+ ## Changing the compiler
1569
+
1570
+ There are two levels of change, and only the second one needs a rebuild.
1571
+
1572
+ **Level 1 — the language definition.** Operators, their per-target templates,
1573
+ the reserved words and the compilation rules live in `compiler/Lang.rgr`, which
1574
+ the compiler reads at compile time. Adding a target to an existing operator, or
1575
+ adding a whole operator, takes effect on the next compile with no rebuild step.
1576
+ The compiler looks for `Lang.rgr` in the working directory first and falls back
1577
+ to the copy beside `bin/output.js`, so a modified copy next to your sources
1578
+ overrides the installed one — which makes an experiment cheap to try and cheap
1579
+ to revert. `lib/stdops.rgr` (macros, the `ret` operator) works the same way.
1580
+
1581
+ **Level 2 — the compiler itself.** The parser, the type checker and the writers
1582
+ are Ranger source under `compiler/`. Changing them means compiling the compiler
1583
+ with itself:
1584
+
1585
+ ```bash
1586
+ npm run compile # compiler/ng_Compiler.rgr -> bin/output.js, and copies Lang.rgr to bin/
1587
+ npm test # the suite runs against the compiler you just built
1588
+ ```
1589
+
1590
+ `npm run compile` is the self-hosting step: the current `bin/output.js` compiles
1591
+ the new sources into the next `bin/output.js`. A change that breaks codegen can
1592
+ therefore break the compiler that builds the next one, so keep the previous
1593
+ `bin/output.js` until the tests pass; `versions/<target>/compiler.js` holds
1594
+ earlier builds. The standalone form is `ranger-compiler -compiler -copysrc`,
1595
+ which writes `bin/ng_Compiler.js`.
1596
+
1597
+ # Annotations
1598
+
1599
+ Compiler is using annotation syntax for specifying some parameters for class, trait and variable construction.
1600
+
1601
+ ## sfn someFn@(main)
1602
+
1603
+ Static functions can be annotated to be the start point of compiled application using `@(main)` annotation.
1604
+
1605
+ ## trait myTrait @params(...)
1606
+
1607
+ @params(...) annotation can be used to greate generic traits.
1608
+
1609
+ ```
1610
+ trait GenericCollection @paras(T V) {
1611
+ def items:[T]
1612
+ fn map:S ( callback:( f:T (item:T)) ) {
1613
+ def res:S (new S ())
1614
+ for children ch@(lives):T i {
1615
+ def new_item@(lives):T (callback (ch))
1616
+ res.add(new_item)
1617
+ }
1618
+ return res
1619
+ }
1620
+ }
1621
+
1622
+ class StringCollection {
1623
+ does GenericCollection @params(string StringCollection)
1624
+ }
1625
+ ```
1626
+
1627
+ ## def variableName@(optional)
1628
+
1629
+ Optional variables can be used as return values of functions where the result is not certain. You can
1630
+ force the unwrapping of the variable with `(unwrap <variable>)`
1631
+
1632
+ ## def variableName@(weak)
1633
+
1634
+ Weak variables are ment to be compiled in the target language as weak references
1635
+
1636
+ ## def variableName@(strong)
1637
+
1638
+ Weak variables are ment to be compiled in the target language as strong references
1639
+
1640
+ ## def variableName@(lives)
1641
+
1642
+ @(lives) annotation can be used to note the compiler that the variable is supposed to outlive it's current scope.
1643
+
1644
+ The variables have lifetime, which determines the point where the variable should be removed. In garbage collected
1645
+ languages you do not have to worry about the lifetime, but in the future there can be target languages which require
1646
+ the lifetime calculations.
1647
+
1648
+ ## def variableName@(temp)
1649
+
1650
+ @(temp) annotation can be used to note the compiler that it should not worry about freeing the variable, in case the
1651
+ target language has option to release the variable.