novac 2.0.1 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,846 +1,1823 @@
1
- # Nova Language v2
1
+ # novac Language Reference
2
2
 
3
- A scripting language built on JavaScript with expressive syntax, a static-ish type system, and unique features beyond JS.
3
+ novac is a dynamic, expressive scripting language with a JavaScript-style syntax, a rich infix operator set, built-in event system, HTTP primitives, a Nova Classic layer, and a comprehensive standard library. Files use the `.nv` or `.nova` extension and are run with the `novac` CLI.
4
4
 
5
5
  ---
6
6
 
7
- ## What's new in v2
8
-
9
- ### Removed
10
- - Backtick `` ` `` template literals — replaced by **f-strings**
11
-
12
- ### Added JS parity fixes
13
- | Feature | Syntax |
14
- |---|---|
15
- | f-strings | `f"Hello {name}!"` |
16
- | Compound assignment | `+= -= *= /= %= **=` |
17
- | Logical assign | `&&= \|\|= ??=` |
18
- | Nullish coalescing | `??` |
19
- | Optional chaining | `?. ?.[ ?.(` |
20
- | Exponentiation | `** **=` |
21
- | Spread / rest | `...arr` `func f(...args)` |
22
- | Default parameters | `func f(x = 0)` |
23
- | Array destructuring | `let [a, b, c] = arr` |
24
- | Object destructuring | `let { x, y } = obj` |
25
- | Destructure defaults | `let { x = 5 } = obj` |
26
- | for-of | `for (let x of iterable)` |
27
- | for-in | `for (let k in obj)` |
28
- | Prefix ++ / -- | `++i` |
29
- | `typeof` | `typeof x` |
30
- | `void` | `void expr` |
31
- | `instanceof` | `x instanceof Cls` |
32
- | `in` | `"key" in obj` |
33
- | `break` / `continue` | inside any loop |
34
- | `switch` / `case` / `default` | JS-style with fall-through |
35
- | Ternary | `cond ? a : b` |
36
- | Bitwise ops | `& \| ^ ~ << >> >>>` |
37
- | Range operator | `1..10` |
38
- | Pipe operator | `x \|> fn` |
39
- | Hex literals | `0xFF` |
40
- | Binary literals | `0b1010` |
41
- | Numeric separators | `1_000_000` |
42
- | Scientific notation | `1.5e3` |
43
- | Block comment | `/* ... */` |
44
- | Optional catch | `try { } catch { }` (no binding required) |
45
- | Computed property keys | `{ [expr]: val }` |
46
- | Getter/setter methods | `get prop() { }` / `set prop(v) { }` |
47
- | `new` expression | `new ClassName()` |
48
- | Keyword operators | `and or not` |
49
- | `as` cast | `x as int` |
50
-
51
- ### Added — Nova-unique features
52
- | Feature | Syntax |
53
- |---|---|
54
- | `each` loop | `each x, i of arr { }` |
55
- | `match` expression | pattern matching, no fall-through |
56
- | `match` guards | `when val where cond { }` |
57
- | `emit` / `on` events | `emit "event", val` / `on "event"(v) { }` |
58
- | `where` block | scoped bindings `where(x=1, y=2) { }` |
59
- | `assert` | `assert cond, "message"` |
60
- | `give` | early-return alias |
61
- | `unless` | `unless (cond) { }` |
62
- | `until` | `until (cond) { }` |
63
- | `repeat` | `repeat (n) { }` |
64
- | **Custom types** | `type`, `struct`, `interface`, `enum`, `trait`, `impl` |
65
- | Preprocessor | `#define`, `#ifdef`, `#ifndef`, `#endif`, `#register`, `#inject` |
66
- | Pointer syntax | `let *p = val`, `*p = newVal` |
67
- | Currency units | `5 k` → 5000, `3 m` → 3000000 |
68
- | `__identifier indentifier__` | double-underscore identifier escaping that work just like strings but give out indents|
69
- | Runtime type registry | `typecheck(v, "int")`, `typeOf(v)`, `satisfies(v, "IFace")` |
7
+ ## Table of Contents
8
+
9
+ 1. [Comments](#comments)
10
+ 2. [Preprocessor Directives](#preprocessor-directives)
11
+ 3. [Literals & Values](#literals--values)
12
+ 4. [Variables & Smart Modifiers](#variables--smart-modifiers)
13
+ 5. [Types](#types)
14
+ 6. [Operators](#operators)
15
+ 7. [Strings & F-Strings](#strings--f-strings)
16
+ 8. [Control Flow](#control-flow)
17
+ 9. [Functions](#functions)
18
+ 10. [Classes](#classes)
19
+ 11. [Pattern Matching](#pattern-matching)
20
+ 12. [Events](#events)
21
+ 13. [Error Handling](#error-handling)
22
+ 14. [Modules & Namespaces](#modules--namespaces)
23
+ 15. [HTTP & Networking](#http--networking)
24
+ 16. [Nova Classic Features](#nova-classic-features)
25
+ 17. [Built-in Global Objects](#built-in-global-objects)
26
+ 18. [Built-in Class Objects](#built-in-class-objects)
27
+ 19. [Standard Library (std)](#standard-library-std)
28
+ 20. [Standard Built-ins (bstd / import_builtin)](#standard-builtins-bstd--import_builtin)
29
+ 21. [Array Methods](#array-methods)
30
+ 22. [Object Methods](#object-methods)
31
+ 23. [String Methods](#string-methods)
32
+ 24. [Type System & Casting](#type-system--casting)
33
+ 25. [Threads](#threads)
70
34
 
71
35
  ---
72
36
 
73
- ## Type System
37
+ ## Comments
74
38
 
75
- ### `type` — type aliases
76
39
  ```nova
77
- type ID = int
78
- type Name = string
79
- type Numeric = int | float
80
- let x: int = 42;
40
+ // single-line comment
41
+ /! also a single-line comment
42
+ /* multi-line block comment */
43
+
44
+ /?/ core.print("executed at lex time") // executable comment — runs during lexing
81
45
  ```
82
46
 
83
- ### `struct` — typed value objects
47
+ ---
48
+
49
+ ## Preprocessor Directives
50
+
84
51
  ```nova
85
- struct Point {
86
- x: int = 0,
87
- y: int = 0
52
+ #define NAME value // substitution macro (NAME replaced everywhere in source)
53
+ #define FLAG // valueless flag (for ifdef/ifndef)
54
+ #undef NAME // remove a macro
55
+ #ifdef FLAG // include following block only if FLAG is defined
56
+ #ifndef FLAG // include following block only if FLAG is NOT defined
57
+ #endif // close an ifdef/ifndef block
58
+ #register operator NAME {"precedence":N, "isUnary":bool}
59
+ #inject <json-token> // inject a raw token into the token stream
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Literals & Values
65
+
66
+ ### Numbers
67
+
68
+ | Form | Example | Notes |
69
+ |------|---------|-------|
70
+ | Decimal | `42`, `3.14` | Standard |
71
+ | Separator | `1_000_000` | Underscores ignored |
72
+ | Hex | `0xFF` | Case insensitive |
73
+ | Binary | `0b1010` | Case insensitive |
74
+ | Custom base | `0r16FF` | `0r<base><digits>`, base 2–36 |
75
+ | Holland/scale | `0hbillion`, `0hmega` | Named SI / large-number literals |
76
+ | BigInt | `42n` | Suffix `n` |
77
+ | Scientific | `1e6`, `2.5E-3` | Standard scientific |
78
+ | Short suffix | `10k`, `2m`, `1b`, `4t` | k=1e3, m=1e6, b=1e9, t=1e12 |
79
+
80
+ **Holland scale literals** — `0h` prefix:
81
+
82
+ | Name | Value | Name | Value |
83
+ |------|-------|------|-------|
84
+ | `kilo` | 1e3 | `milli` | 1e-3 |
85
+ | `mega` | 1e6 | `micro` | 1e-6 |
86
+ | `giga` | 1e9 | `nano` | 1e-9 |
87
+ | `tera` | 1e12 | `pico` | 1e-12 |
88
+ | `million` | 1e6 | `femto` | 1e-15 |
89
+ | `billion` | 1e9 | `atto` | 1e-18 |
90
+ | `trillion` | 1e12 | `googol` | 1e100 |
91
+ | `quadrillion` | 1e15 | | |
92
+ | `quintillion` | 1e18 | | |
93
+
94
+ ### Special Literals
95
+
96
+ | Token | Value |
97
+ |-------|-------|
98
+ | `true` | boolean true |
99
+ | `false` | boolean false |
100
+ | `null` | null |
101
+ | `nstr` | `""` (empty string) |
102
+ | `nfunc` | A function that returns null |
103
+
104
+ ### URL Literals
105
+
106
+ Tokens beginning with a recognized scheme are URL literals:
107
+
108
+ ```nova
109
+ let url = https://api.example.com/users
110
+ let local = localhost:3000
111
+ // Recognized: http, https, ftp, ws, wss, content, localhost:<port>
112
+ ```
113
+
114
+ ### Arrays
115
+
116
+ ```nova
117
+ let arr = [1, 2, 3]
118
+ let spread = [...arr, 4, 5]
119
+ ```
120
+
121
+ ### Objects
122
+
123
+ ```nova
124
+ let obj = { x: 1, 'y': 2 }
125
+ let method = { greet() { give "hi"; } }
126
+ let overload = {
127
+ [op]: {
128
+ unary: { '~': () => 17 }
129
+ binary: { '+': (a) => a*10 }
130
+ }
88
131
  }
132
+ core.print(~obj + obj); // ~obj becomes 17, now 17 + obj becomes 17*10 which is 170.
133
+ let computed = { ["key" + idx]: value }
134
+ let withSpread = { ...obj, z: 3 }
135
+ let getset = {
136
+ get prop() { give this._val; },
137
+ set prop(v) { this._val = v; }
138
+ }
139
+ let presence = { active? true } // boolean-constrained property: must be true or false
140
+ ```
89
141
 
90
- let p = Point({ x: 3, y: 4 });
91
- core.print(p.x); // 3
92
- core.print(typeOf(p)); // struct:Point
93
- core.print(p instanceof Point); // true
142
+ ---
143
+
144
+ ## Variables & Smart Modifiers
145
+
146
+ ```nova
147
+ var x = 10 // mutable, function-scoped
148
+ let y = 20 // mutable, block-scoped
149
+ const Z = 30 // immutable binding/
150
+ // special __...__ identifiers:
151
+ let __my cool variable++-__ = 19; // when novac sees __, it collects everything until __ as the var name and gives an identifier token type, so you can even name a variable __26__ and the token with be { type: 'IDENTIFIER', value: '26' }
94
152
  ```
95
153
 
96
- ### `enum` — discriminated unions
154
+ ### Destructuring
155
+
97
156
  ```nova
98
- enum Direction { North, South, East, West }
99
- enum Color { Rgb(int, int, int), Hex(string) }
157
+ let { a, b } = obj
158
+ let { a: renamed, b: other = "default" } = obj
159
+ let [first, second] = arr
160
+ let [head, ...tail] = arr
161
+ ```
100
162
 
101
- let d = Direction.North;
102
- core.print(d.__variant__); // North
163
+ ### Smart Variable Modifiers
103
164
 
104
- let c = Color.Rgb(255, 0, 128);
105
- core.print(c.values); // [255, 0, 128]
165
+ Placed between the variable name and `=`:
166
+
167
+ | Modifier | Effect |
168
+ |----------|--------|
169
+ | `frozen` | Throws on any reassignment after first set |
170
+ | `lazy` | Expression evaluated only on first read |
171
+ | `tracked` | `console.log`s every assignment change |
172
+ | `nonull` | Throws if assigned `null` or `undefined` |
173
+ | `once` | Can only be set once; further assigns silently ignored |
174
+ | `setter <fn>` | Calls `fn(newVal, oldVal)` on every assignment; if fn returns non-undefined, that becomes the stored value |
175
+ | `getter <fn>` | Calls `fn()` on every read |
176
+ | `as fnum [min, max]` | Clamps to float range on every assign |
177
+ | `as fint [min, max]` | Clamps to integer range (`Math.trunc`) on every assign |
178
+
179
+ ```nova
180
+ let x frozen = 5
181
+ let y lazy = computeHeavy()
182
+ let z tracked = 0
183
+ let n nonull = getValue()
184
+ let once_val once = initialize()
185
+ let watched setter mySetFn = 0
186
+ let computed getter myGetFn = 0
187
+ let clamped as fnum [0.0, 1.0] = 0.5
188
+ let bounded as fint [0, 255] = 128
189
+ ```
190
+
191
+ ### Links and $-notations
192
+ ```nova
193
+ let obj = { test: { example: 29 } };
194
+ let link2example = link(obj.test.example); // link is not a function, its a special keyword function
195
+ link2example = 3; // example is also set
196
+ obj.test.example; // 3
197
+ obj.test.example = 2;
198
+ link2example; // 2
199
+ // you can also directly set a link call:
200
+ link(obj.test.example) = 1; // works
201
+ // nest links:
202
+ link(link2example) = 3; // now obj.test.example is also 3
106
203
  ```
107
204
 
108
- ### `interface` — structural contracts
109
205
  ```nova
110
- interface Drawable {
111
- draw()
112
- color: string
206
+ // $ notation:
207
+ // when used as a normal call:
208
+ $29; // noop, just 29
209
+ ${a: 6 + 3}; // noop again, just the object
210
+ // real magic in $ assingments:
211
+ $"hi" = 29; // sets variable hi
212
+ $26 = 1; // set variable 26, even tho you can't just read from a var directly called 26, you can still use __26__ to read (novac trims __...__ and just leaves an ident with value ...)
213
+ ```
214
+
215
+ ### Type Annotation
216
+
217
+ ```nova
218
+ let count: int = 0
219
+ let name: string = "Nova"
220
+ func add(a: int, b: int): int => { give a + b }
221
+ ```
222
+
223
+ ### Pointer Declaration & Dereference
224
+
225
+ ```nova
226
+ var *ptr = someValue
227
+ let result = *ptr // dereference
228
+ *ptr = newValue // assign through pointer
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Types
234
+
235
+ ### Type Alias
236
+
237
+ ```nova
238
+ type ID = string
239
+ type Status = "active" | "inactive" | "pending"
240
+ type Point = { x: int, y: int }
241
+ type Result<T> = T | null
242
+ ```
243
+
244
+ ### Struct
245
+
246
+ ```nova
247
+ struct Vec2 {
248
+ x: int = 0,
249
+ y: int = 0
113
250
  }
251
+ let v = Vec2({ x: 3, y: 4 })
252
+ v.x // 3
253
+ ```
254
+
255
+ ### Interface
114
256
 
115
- let obj = { draw() { core.print("drawn"); }, color: "red" };
116
- core.print(satisfies(obj, "Drawable")); // true
257
+ ```nova
258
+ interface Serializable {
259
+ serialize(): string
260
+ deserialize(data: string): bool
261
+ name?: string // optional member
262
+ }
263
+ interface Extended extends Serializable, Printable { }
117
264
  ```
118
265
 
119
- ### `trait` + `impl` — behaviour contracts
266
+ ### Enum
267
+
120
268
  ```nova
121
- trait Printable {
122
- print() { core.print(this.toString()); }
269
+ enum Status { Active, Inactive, Pending }
270
+ enum Shape {
271
+ Circle(int),
272
+ Rect(int, int),
273
+ Point = 0
123
274
  }
275
+ let s = Status.Active
276
+ s.__variant__ // "Active"
277
+ s.__enum_type__ // "Status"
278
+ ```
279
+
280
+ ### Trait & Impl
124
281
 
125
- impl Printable of Point { }
282
+ ```nova
283
+ trait Drawable {
284
+ draw(ctx) { }
285
+ }
286
+ impl Drawable of Circle {
287
+ draw(ctx) { ctx.circle(this.x, this.y, this.r); }
288
+ }
289
+ // Also: impl Drawable for Circle { }
126
290
  ```
127
291
 
292
+ ### Type Expressions
293
+
294
+ ```nova
295
+ string | null // union
296
+ int & Serializable // intersection
297
+ string[] // array type
298
+ { name: string, age?: int } // inline shape type
299
+ Map<string, int> // generic
300
+ ```
301
+
302
+ ---
303
+
304
+ ## Operators
305
+
306
+ ### Arithmetic
307
+
308
+ | Op | Meaning |
309
+ |----|---------|
310
+ | `+` `-` `*` `/` `%` | Standard arithmetic |
311
+ | `**` | Exponentiation (right-associative) |
312
+ | `++` `--` | Increment / decrement (prefix or postfix) |
313
+
314
+ ### Assignment
315
+
316
+ | Op | Meaning |
317
+ |----|---------|
318
+ | `=` | Assign |
319
+ | `+=` `-=` `*=` `/=` `%=` `**=` | Compound assign |
320
+ | `&&=` | Logical-AND assign |
321
+ | `\|\|=` | Assign if current is null/undefined |
322
+ | `??=` | Nullish-coalesce assign |
323
+
324
+ ### Comparison
325
+
326
+ | Op | Meaning |
327
+ |----|---------|
328
+ | `==` `!=` | Loose equality |
329
+ | `===` `!==` | Strict equality |
330
+ | `<` `>` `<=` `>=` | Relational |
331
+ | `is` | Strict equality alias |
332
+ | `isnt` | Non-identity alias |
333
+ | `equals` | `===` alias |
334
+ | `equals_ignore` | Case-insensitive string equality |
335
+ | `cmp` | `localeCompare` — returns -1/0/1 |
336
+
337
+ ### Logical
338
+
339
+ | Op | Meaning |
340
+ |----|---------|
341
+ | `&&` / `and` | Logical AND |
342
+ | `\|\|` / `or` | Logical OR |
343
+ | `!` / `not` | Logical NOT (unary) |
344
+ | `??` | Nullish coalesce |
345
+ | `xor` | Exclusive OR |
346
+ | `nand` | NOT AND |
347
+ | `nor` | NOT OR |
348
+ | `xnor` | Exclusive NOR |
349
+
350
+ ### Bitwise
351
+
352
+ | Op | Meaning |
353
+ |----|---------|
354
+ | `&` `\|` `^` `~` | Bitwise AND / OR / XOR / NOT |
355
+ | `<<` `>>` `>>>` | Left / right / unsigned right shift |
356
+
357
+ ### Type & Predicate
358
+
359
+ | Op | Meaning |
360
+ |----|---------|
361
+ | `typeof` | Returns type string (unary) |
362
+ | `instanceof` | Instance check |
363
+ | `in` | Membership / range containment |
364
+ | `istypeof` | `typeOf(left) === right` |
365
+ | `matches` | Regex test of left string against right |
366
+ | `between` | `left > arr[0] && left < arr[1]` |
367
+
368
+ ### Special / Flow
369
+
370
+ | Op | Meaning |
371
+ |----|---------|
372
+ | `\|>` | Pipe: `x \|> f` → `f(x)` |
373
+ | `=>` | Arrow function body |
374
+ | `..` | Inclusive range: `1..10` → `NovaRange(1,10)` |
375
+ | `...` (unary) | Spread / rest |
376
+ | `...` (binary) | Exclusive range: `1...10` → array `[1..9]` |
377
+ | `?.` | Optional chain — null if left is null |
378
+ | `?` | Ternary start |
379
+ | `::` | Namespace / property access |
380
+ | `>>` | Pipe-right: `val >> fn` → `fn(val)`; also used in `compose` / `engage` |
381
+ | `delete` | Delete variable, property, or subscript |
382
+ | `void` | Evaluate and return undefined |
383
+ | `#` | Size of right operand (array/object/string length) |
384
+
385
+ ### Array / Collection Infix
386
+
387
+ | Op | Meaning |
388
+ |----|---------|
389
+ | `intersect` | Elements in both arrays |
390
+ | `union` | Deduplicated merge |
391
+ | `diff_arr` | Elements in left not in right |
392
+ | `zip` | `[[a0,b0],[a1,b1],...]` |
393
+ | `extend` | Merge objects / concatenate arrays |
394
+ | `concat` | Concatenate |
395
+ | `index` | `arr index 2` → element at 2 |
396
+ | `step` | `0..10 step 2` → `[0,2,4,6,8,10]` |
397
+
398
+ ### Numeric Infix
399
+
400
+ | Op | Meaning |
401
+ |----|---------|
402
+ | `avg` | `(a + b) / 2` |
403
+ | `diff` | `Math.abs(a - b)` |
404
+ | `ratio` | `a / b` |
405
+ | `mult_of` | `a % b === 0` |
406
+ | `gcd` | Greatest common divisor |
407
+ | `lcm` | Least common multiple |
408
+ | `pow` | Alias for `**` |
409
+ | `bigger` / `smaller` | `a > b` / `a < b` aliases |
410
+
411
+ ### String Infix
412
+
413
+ | Op | Meaning |
414
+ |----|---------|
415
+ | `pad_start` | `.padStart(right, ' ')` |
416
+ | `pad_end` | `.padEnd(right, ' ')` |
417
+
418
+ ### Operator Precedence (highest → lowest)
419
+
420
+ | Prec | Operators |
421
+ |------|-----------|
422
+ | 15 | `++` `--` (postfix), `?.`, `::`, `#:`, `$:`, `index` |
423
+ | 14 | Unary: `!` `~` `-` `+` `*` `...` `not` `typeof` `void` `delete` |
424
+ | 13 | `**` `pow` (right-assoc) |
425
+ | 12 | `*` `/` `%` `ratio` `mult_of` `gcd` `lcm` |
426
+ | 11 | `+` `-` `avg` `diff` `pad_start` `pad_end` `concat` |
427
+ | 10 | `>>` `<<` `>>>` `..` `step` `zip` |
428
+ | 9 | `<` `>` `<=` `>=` `bigger` `smaller` |
429
+ | 8 | `==` `!=` `===` `!==` `is` `isnt` `equals` `equals_ignore` `cmp` `istypeof` `matches` `instanceof` `in` |
430
+ | 7 | `&` `intersect` `diff_arr` |
431
+ | 6 | `^` |
432
+ | 5 | `\|` `union` `extend` |
433
+ | 4 | `&&` `and` `nand` |
434
+ | 3 | `??` `xor` `xnor` |
435
+ | 2 | `\|\|` `or` `nor` |
436
+ | 0 | `=` `+=` `-=` `*=` `/=` `%=` `**=` `&&=` `\|\|=` `??=` `=>` `\|>` |
437
+
128
438
  ---
129
439
 
130
- ## Syntax Reference
440
+ ## Strings & F-Strings
131
441
 
132
- ### Variables
133
442
  ```nova
134
- let x = 10;
135
- const PI = 3.14;
136
- var y = "hello";
137
- let n: int = 5; // typed
138
- let *ptr = someVal; // pointer
443
+ let a = "hello"
444
+ let b = 'world'
445
+ // Escape sequences: \n \t \r \\ \' \"...etc
446
+
447
+ // F-strings: f"..." with {} interpolation — any expression allowed
448
+ let msg = f"\color{blue}Hello {name}!\reset"
449
+ let math = f"2^10 = {2 ** 10}"
450
+ let complex = f"items: {arr.map(x => x * 2).join(', ')}"
139
451
  ```
140
452
 
141
- ### Functions
453
+ ---
454
+
455
+ ## Control Flow
456
+
457
+ ### If / Else
458
+
142
459
  ```nova
143
- func add(a, b) => { give a + b; }
144
- func greet(name = "World") => { core.print(f"Hi {name}!"); }
145
- func sum(...nums) => { give nums.reduce((a,x) => a+x, 0); }
460
+ if (cond) { } else if (other) { } else { }
461
+
462
+ let x = a > b ? a : b // ternary
463
+ let y = value if condition else alt // postfix if-expression
464
+ ```
465
+
466
+ ### Unless / Until / Repeat
146
467
 
147
- let double = x => x * 2;
148
- let add5 = (x) => x + 5;
468
+ ```nova
469
+ unless (cond) { } // runs when cond is false
470
+ until (cond) { } // runs while cond is false
471
+ repeat (n) { } // runs exactly n times
149
472
  ```
150
473
 
151
- ### Strings
474
+ ### While / Do-While
475
+
152
476
  ```nova
153
- let name = "Nova";
154
- let msg = f"Hello {name}, 2+2={2+2}";
477
+ while (cond) { }
478
+ do { } while (cond)
155
479
  ```
156
480
 
157
- ### Control flow
481
+ ### For
482
+
158
483
  ```nova
159
- if (x > 0) { ... } else { ... }
160
- unless (x == 0) { ... }
161
- while (cond) { ... }
162
- until (done) { ... }
163
- do { ... } while (cond);
164
- repeat (5) { ... }
165
- for (let i = 0; i < 10; i++) { ... }
166
- for (let x of array) { ... }
167
- for (let k in obj) { ... }
168
- each val, idx of array { ... }
484
+ for (let i = 0; i < 10; i++) { }
485
+ for (let item of array) { }
486
+ for (let key in object) { }
169
487
  ```
170
488
 
171
- ### Pattern matching
489
+ ### Each
490
+
172
491
  ```nova
173
- match (value) {
174
- when 0 { core.print("zero"); }
175
- when 1, 2, 3 { core.print("small"); }
176
- when 4..10 { core.print("range"); } // range pattern!
177
- when x where x > 100 { core.print("big"); } // guarded
178
- default { core.print("other"); }
179
- }
492
+ each item of array { }
493
+ each item, index of array { }
180
494
  ```
181
495
 
182
- ### Classes
496
+ ### Switch / Case
497
+
183
498
  ```nova
184
- class Animal {
185
- sound: "..."
186
- speak() { core.print(f"I say {this.sound}"); }
187
- get label() { give this.sound; }
499
+ switch (value) {
500
+ case 1: core.print("one"); break;
501
+ default: core.print("other"); break;
188
502
  }
503
+ ```
189
504
 
190
- class Dog extends Animal {
191
- sound: "Woof"
192
- }
505
+ ### Match
193
506
 
194
- let d = Dog();
195
- d.speak(); // I say Woof
196
- core.print(d.label); // Woof
507
+ ```nova
508
+ match (value) {
509
+ when 1 { }
510
+ when 2, 3 { } // comma = multi-pattern
511
+ when 1..10 { } // range
512
+ when "ok" where cond { } // with guard
513
+ default { }
514
+ }
197
515
  ```
198
516
 
199
- ### Pipe operator
517
+ ### Loop Control
518
+
519
+ | Keyword | Effect |
520
+ |---------|--------|
521
+ | `break` | Exit loop |
522
+ | `continue` | Next iteration |
523
+ | `skip` | Alias for `continue` (parsed as `skip`, no-op in execute) |
524
+ | `goback` | Throw `{ __return: scope.__return }` — return from function |
525
+ | `end` | Throw `{ __return: undefined }` — terminate scope |
526
+
527
+ ### Guard
528
+
200
529
  ```nova
201
- func double(x) => { give x * 2; }
202
- func addOne(x) => { give x + 1; }
530
+ guard (cond) {
531
+ // body runs only when cond is TRUE (passes through)
532
+ } else {
533
+ // runs when cond is FALSE
534
+ }
535
+ ```
536
+
537
+ ### When
203
538
 
204
- let result = 5 |> double |> addOne; // 11
539
+ ```nova
540
+ when cond do { } // runs body once if cond is truthy
541
+ when cond then { } // same
205
542
  ```
206
543
 
207
- ### Ranges
544
+ ### Where (scoped bindings)
545
+
208
546
  ```nova
209
- for (let i of 1..10) { core.print(i); }
210
- core.print(5 in 1..10); // true
211
- let arr = (1..5).toArray ? [1,2,3,4,5];
547
+ where (base = 100, rate = 0.15) {
548
+ let tax = base * rate
549
+ }
212
550
  ```
213
551
 
214
- ### Events
552
+ ### With
553
+
215
554
  ```nova
216
- on "userLogin" (user) {
217
- core.print(f"Welcome {user}");
555
+ with (object) {
556
+ // object properties brought into scope
557
+ }
558
+ with option featureFlag {
559
+ // body runs with flag set; removed after block
218
560
  }
561
+ ```
219
562
 
220
- emit "userLogin", "Alice";
563
+ ### Loop (Classic iteration)
564
+
565
+ ```nova
566
+ loop item in iterable { }
221
567
  ```
222
568
 
223
- ### `where` scoping
569
+ ### Foreach (Classic key-value)
570
+
224
571
  ```nova
225
- where (x = compute(), y = transform()) {
226
- core.print(x + y);
227
- }
228
- // x, y not accessible here
572
+ foreach(collection)(key, value) { }
573
+ foreach(collection)(key, value, length) { } // third var = total length
229
574
  ```
230
575
 
231
- ### Preprocessor
576
+ ### Wait (sync sleep)
577
+
232
578
  ```nova
233
- #define DEBUG
234
- #define MAX_SIZE 100
579
+ wait(1000) // blocks for 1000ms (Atomics.wait, fallback: child process)
580
+ ```
235
581
 
236
- #ifdef DEBUG
237
- core.print("debug mode");
238
- #endif
582
+ ### Time Block
239
583
 
240
- #define DOUBLE(x) x * 2
584
+ ```nova
585
+ time {
586
+ // times this block; prints elapsed ms on completion
587
+ }
241
588
  ```
242
589
 
243
590
  ---
244
591
 
245
- ## Built-in Array Methods
592
+ ## Functions
246
593
 
247
- `map`, `filter`, `reduce`, `find`, `findIndex`, `some`, `every`,
248
- `flat`, `flatMap`, `sort`, `reverse`, `slice`, `splice`,
249
- `indexOf`, `includes`, `join`, `push`, `pop`, `shift`, `unshift`,
250
- `concat`, `fill`, `forEach`, `keys`, `values`, `entries`, `at`
594
+ ### Named Functions
251
595
 
252
- ## Built-in stdlib (`std.*`)
596
+ ```nova
597
+ func add(a, b) => {
598
+ give a + b
599
+ }
600
+ function greet(name) {
601
+ return f"Hello {name}"
602
+ }
603
+ ```
253
604
 
254
- `Math`, `Array`, `Object`, `String`, `is`, `num`, `str`, `bool`,
255
- `int`, `console`, `Date`, `json`, `Promise`, `fn`, `range`, `error`
605
+ ### Function Modifiers
256
606
 
257
- ---
607
+ Modifiers come **after** `func`/`function` and **before** the function name. Multiple modifiers can be chained in any order.
608
+
609
+ | Modifier | Effect |
610
+ |----------|--------|
611
+ | `async` | Marks async; `await` blocks synchronously via `SharedArrayBuffer` |
612
+ | `Strict` | Throws if argument count doesn't match exactly |
613
+ | `once` | Body only runs once; later calls return the first result |
614
+ | `memo` | Results memoized by `JSON.stringify(args)` key |
615
+ | `generator` | Returns an iterator; `yield` inside collects values |
616
+ | `timeout <expr>` | Throws if function exceeds `expr` ms |
617
+ | `defer <stmt>` | Executes `stmt` when function returns (in `finally`) |
618
+
619
+ ```nova
620
+ func async load(url) => { }
621
+ func Strict strict(a, b) => { }
622
+ func once init() => { }
623
+ func memo fib(n) => { }
624
+ func generator counter() => { }
625
+ func async memo cachedFetch(url) => { }
626
+ func timeout 5000 riskyOp() => { }
627
+ func defer cleanup() riskyOp() => { }
628
+
629
+ // function keyword works identically
630
+ function async load(url) { }
631
+ function Strict strict(a, b) { }
632
+ ```
633
+
634
+ in objects, you can have a [expr] quick access method that runs on
635
+ obj.(...args)
636
+ example:
637
+ ```nova
638
+ let obj = { [expr]: (a, b) => return a + b }
639
+ core.print(obj.(1,5)); // 6
640
+ ```
641
+
642
+ ### Generator Iterator API
643
+
644
+ | Method | Description |
645
+ |--------|-------------|
646
+ | `.next()` | `{ value, done }` |
647
+ | `.last()` | Step back one |
648
+ | `.current()` | Peek without advancing |
649
+ | `.seek(n)` | Jump to index n |
650
+ | `.setIndex(n)` | Set position |
651
+ | `.currentIndex()` | Current index |
652
+ | `.length()` | Total collected values |
653
+ | `.at(n)` | Value at index n |
654
+ | `[Symbol.iterator]` | Makes it iterable in `for-of` |
655
+
656
+ ### Return / Give / Goback
258
657
 
259
- ## HTTP & Server
658
+ ```nova
659
+ return value // sets __return, does NOT terminate
660
+ give value // alias — same as return, but terminates the function by throwing
661
+ goback // throws { __return: scope.__return }
662
+ yield value // collects into generator's output array
663
+ ```
260
664
 
261
- Nova has first-class HTTP support built in. URLs are native tokens and HTTP verbs are keywords.
665
+ ### Arrow Functions
262
666
 
263
- ### First-class URL literals
667
+ ```nova
668
+ let double = x => x * 2
669
+ let add = (a, b) => a + b
670
+ ```
671
+
672
+ ### Rest & Default Parameters
673
+
674
+ ```nova
675
+ func sum(...nums) => { give nums.reduce((a, b) => a + b, 0) }
676
+ func greet(name = "World") => { }
677
+ ```
264
678
 
679
+ ### Block functions:
680
+ these auto call on reference, and use the returned value
265
681
  ```nova
266
- let api = https://api.example.com/users;
267
- core.print(api); // https://api.example.com/users
682
+ block coolBlock { core.print(8); return 8; }
683
+ core.print(coolBlock); /* result:
684
+ 8 //from inside coolBlock
685
+ 8 //from core.print(coolBlock);
686
+ */
687
+ // you can also just run the block using nova.runBlock('coolBlock')
268
688
  ```
269
689
 
270
- ### HTTP method expressions
690
+ ### Run bounder:
691
+ ```nova
692
+ // run CODE
693
+ // or
694
+ // run { ... }
695
+ // example:
696
+ core.print(run let x = 10); //10 because run uses last result if no return/give
697
+ core.print(x); // 10
698
+ core.print(run { let c = 8; return c; }); // 8
699
+ core.print(c); // 8
700
+
701
+ ```
271
702
 
272
- Any HTTP verb followed by a URL (or string path) acts like a function call.
273
- Optional `(body, headers)` args are passed as the request body and headers.
703
+ ### Pipe Operator
274
704
 
275
705
  ```nova
276
- // GET
277
- let todos = get https://jsonplaceholder.typicode.com/todos/1();
278
- core.print(todos.status); // 200
279
- core.print(todos.ok); // true
280
- core.print(todos.body.title);
706
+ let result = 5 |> double |> addOne
707
+ ```
281
708
 
282
- // POST with body
283
- let created = post https://api.example.com/users({ name: "Alice", age: 30 });
709
+ ---
284
710
 
285
- // PUT / PATCH
286
- let updated = put https://api.example.com/users/1({ name: "Bob" });
287
- let patched = patch https://api.example.com/users/1({ age: 31 });
711
+ ## Classes
288
712
 
289
- // DELETE
290
- let removed = delete https://api.example.com/users/1();
713
+ ```nova
714
+ class Animal {
715
+ name: "unknown"
716
+ speak() { core.print(f"{this.name} speaks") }
717
+ }
718
+ class Dog extends Animal {
719
+ breed: "mixed"
720
+ speak() { core.print(f"{this.name} barks") }
721
+ }
722
+ class Doc implements Serializable {
723
+ // must satisfy all non-optional interface members
724
+ }
725
+ ```
726
+
727
+ ### Getter / Setter
291
728
 
292
- // HEAD / OPTIONS
293
- let info = head https://api.example.com();
729
+ ```nova
730
+ class Box {
731
+ _val: 0
732
+ get value() { give this._val; }
733
+ set value(v) { this._val = v; }
734
+ }
294
735
  ```
295
736
 
296
- ### `fetch()` — sync fetch
737
+ ### Decorators
297
738
 
298
739
  ```nova
299
- // As expression
300
- let data = fetch("https://api.example.com/items");
301
- core.print(data.body);
740
+ @logged
741
+ @deprecated("use newFn")
742
+ class OldClass {
743
+ @readonly
744
+ name: "old"
745
+ }
746
+ ```
302
747
 
303
- // As statement with => binding
304
- fetch("https://api.example.com/items") => items;
748
+ ---
749
+
750
+ ## Pattern Matching
305
751
 
306
- // With options
307
- let resp = fetch("https://api.example.com/items", {
308
- method: "POST",
309
- headers: { "Authorization": "Bearer token" },
310
- body: { name: "Nova" }
311
- });
752
+ ```nova
753
+ match (value) {
754
+ when 90..100 { } // range — uses NovaRange.includes()
755
+ when "ok" { } // scalar — uses ==
756
+ when 1, 2, 3 { } // any of these — comma-separated
757
+ when x where x > 0 { } // with guard expression
758
+ default { }
759
+ }
312
760
  ```
313
761
 
314
- All `fetch` / HTTP method calls return a response object:
762
+ ---
763
+
764
+ ## Events
765
+
315
766
  ```nova
316
- resp.ok // bool — true if status 200-299
317
- resp.status // int — HTTP status code
318
- resp.body // parsed JSON or raw string
319
- resp.text // raw response text
767
+ emit "eventName"
768
+ emit "eventName", payload
769
+
770
+ on "eventName" (data) {
771
+ core.print(data)
772
+ }
773
+
774
+ // Internal: fires on every statement execution step
775
+ on "nv:tick" () { }
320
776
  ```
321
777
 
322
- ### `server(port)` — HTTP server
778
+ ---
779
+
780
+ ## Error Handling
323
781
 
324
782
  ```nova
325
- server(3000) {
326
- get "/users"(req, res) {
327
- return [{ id: 1, name: "Alice" }];
328
- }
783
+ try {
784
+ riskyOp()
785
+ } catch (err) {
786
+ core.print(f"caught: {err}")
787
+ } finally {
788
+ cleanup()
789
+ }
329
790
 
330
- post "/users"(req, res) {
331
- let body = req.body;
332
- return { created: true, name: body.name };
333
- }
791
+ try { riskyOp() } catch { } // no binding — catch without name
334
792
 
335
- put "/users/:id"(req, res) {
336
- return { updated: req.params.id };
337
- }
793
+ throw "plain string"
794
+ throw new Error("typed")
338
795
 
339
- delete "/users/:id"(req) {
340
- return { deleted: req.params.id };
341
- }
796
+ assert condition
797
+ assert condition, "failure message"
342
798
 
343
- get "*"(req, res) {
344
- res.status(404);
345
- return { error: "Not found" };
346
- }
799
+ expt "expectedOutput" from {
800
+ core.print("expectedOutput")
347
801
  }
348
802
  ```
349
803
 
350
- **Request object (`req`)**
351
- | Property | Description |
352
- |---|---|
353
- | `req.method` | HTTP method string |
354
- | `req.url` | Full URL including query string |
355
- | `req.path` | Pathname only |
356
- | `req.params` | Object of `:param` captures |
357
- | `req.query` | Object of `?key=value` pairs |
358
- | `req.headers` | Request headers |
359
- | `req.body` | Parsed JSON body |
804
+ ---
360
805
 
361
- **Response object (`res`)**
362
- | Method | Description |
363
- |---|---|
364
- | `res.status(code)` | Set status code (chainable) |
365
- | `res.json(data)` | Send JSON response |
366
- | `res.send(text)` | Send plain text |
806
+ ## Modules & Namespaces
367
807
 
368
- Returning a value from a route handler automatically sends it as JSON.
808
+ ### Import
369
809
 
370
- ### Calling a local server from the same script
810
+ ```nova
811
+ import "path/to/module"
812
+ import "path/to/module" as alias
813
+ import "path/to/module" as name1, name2
814
+ from "module" import func1, func2
815
+ import_builtin Air, Chalk, Input, Shema
816
+ ```
817
+
818
+ ### Export
819
+
820
+ ```nova
821
+ export myValue
822
+ export { name = value, other = value2 }
823
+ default myMainExport
824
+ ```
371
825
 
372
- This works without deadlocking — Nova detects local server ports and dispatches requests in-process:
826
+ ### Namespace
373
827
 
374
828
  ```nova
375
- server(4000) {
376
- get "/hello"(req) { return { msg: "hi" }; }
829
+ namespace Math {
830
+ func add(a, b) => { give a + b }
377
831
  }
832
+ Math::add(1, 2)
833
+ using namespace Math // bring all members into scope
834
+ ```
835
+
836
+ ### Using / Unuse
378
837
 
379
- let r = get http://localhost:4000/hello();
380
- core.print(r.body.msg); // hi
838
+ ```nova
839
+ using featureFlag
840
+ using namespace MyNS
841
+ unuse featureFlag
381
842
  ```
382
843
 
383
- ### Platform support for `fetch`
844
+ ### Environment Variable
384
845
 
385
- Nova's sync HTTP works on every platform — it tries strategies in order:
386
- 1. **In-process dispatch** for local Nova servers (no network needed)
387
- 2. **curl** — available on Unix/Mac and Windows 10+ (build 17063)
388
- 3. **PowerShell** `Invoke-WebRequest` — Windows fallback
389
- 4. **Node.js built-in** `http`/`https` — universal last resort (always available)
846
+ ```nova
847
+ env VAR_NAME // reads process.env.VAR_NAME; throws if undefined
848
+ ```
390
849
 
391
850
  ---
392
851
 
393
- ## Running Nova
852
+ ## HTTP & Networking
394
853
 
854
+ ### First-Class HTTP Verbs
395
855
 
396
- ```bash
397
- # Run a file
398
- novac script.nv
856
+ ```nova
857
+ get https://api.example.com/users
858
+ post https://api.example.com/users({ name: "novac" })
859
+ put https://api.example.com/users/1({ name: "Updated" })
860
+ delete https://api.example.com/users/1()
861
+ patch https://api.example.com/users/1({ active: false })
862
+ head https://api.example.com/health
863
+ options https://api.example.com/
864
+ // Returns: { ok: bool, status: int, body: object|string, text: string }
865
+ ```
866
+
867
+ **Sync fetch strategies (tried in order):** in-process local Nova server dispatch → curl → PowerShell (Windows) → Node child process.
399
868
 
400
- # REPL
401
- novac repl
869
+ ### Fetch
402
870
 
403
- # Evaluate inline
404
- novac eval 'core.print("hello")'
871
+ ```nova
872
+ fetch(url)
873
+ fetch(url, { method: "POST", headers: { Authorization: "Bearer x" }, body: payload })
874
+ fetch(url, opts) => resultVar // statement form — assigns result
405
875
  ```
406
876
 
877
+ ### Server Declaration
407
878
 
408
- ---
879
+ ```nova
880
+ server(3000) {
881
+ get "/api/users" (req, res) {
882
+ res.json([])
883
+ }
884
+ post "/api/users" (req, res) {
885
+ res.json({ created: true, body: req.body })
886
+ }
887
+ delete "/api/users/:id" (req, res) {
888
+ res.json({ deleted: req.params.id })
889
+ }
890
+ }
891
+ // req: { method, url, path, headers, body, params, query }
892
+ // res: .status(code) .header(k,v) .json(data) .send(data)
893
+ // :param values also bound directly into route scope
894
+ // If res.json/send never called, route handler's return value is auto-sent
895
+ ```
409
896
 
410
- ## Nova Classic Features (from legacy implementation)
897
+ ---
411
898
 
412
- These features were ported from the original Nova interpreter.
899
+ ## @classic Classic Compatibility Block
413
900
 
414
- ### Iteration
901
+ Classic novac keywords are **only valid inside an `@classic { }` block**. Outside of one they are plain identifiers and cause a parse error if used as statements. This keeps the core language surface area clean while preserving full backward compatibility for legacy code.
415
902
 
416
903
  ```nova
417
- // foreach — iterate object keys/values with custom vars
418
- foreach (myObj)(key, value) {
419
- core.print(f"{key} = {value}");
904
+ @classic {
905
+ // any classic keyword works in here
906
+ echo "hello"
907
+ keep port = 8080
908
+ gear(500) poll {
909
+ let data = fetch(https://api.example.com/data)
910
+ echo data.body
911
+ }
912
+ engage poll
420
913
  }
421
914
  ```
422
915
 
423
- ### Scoped Bindings
916
+ If you use a Classic keyword outside `@classic`, the parser throws:
917
+
918
+ ```
919
+ ClassicError: 'echo' is a Classic novac keyword and must be used inside an @classic { } block.
920
+ Wrap your legacy code: @classic { echo ... }
921
+ ```
922
+
923
+ `@classic` blocks can appear anywhere a statement is valid — top-level, inside functions, inside `if` bodies, etc. They share the same scope as their surroundings.
424
924
 
425
925
  ```nova
426
- // temp bind a variable only for a block
427
- let env = "production";
428
- temp env = "test" => {
429
- core.print(env); // test
926
+ func migrate() => {
927
+ @classic {
928
+ backup session = currentSession
929
+ temp session = "migration" => {
930
+ runMigration()
931
+ }
932
+ retrieve session
933
+ }
430
934
  }
431
- core.print(env); // production
935
+ ```
432
936
 
433
- // keep set-once (won't overwrite if already set)
434
- keep port = 8080;
435
- keep port = 9999; // ignored
436
- core.print(port); // 8080
937
+ ### Classic Keywords (only valid inside `@classic { }`)
938
+
939
+ **Output:**
940
+
941
+ | Keyword | Behavior |
942
+ |---------|----------|
943
+ | `echo value` | `process.stdout.write(String(value))` — no newline |
944
+ | `print(val, ...)` | Space-joined args + newline |
945
+ | `println(value)` | `process.stdout.write(value)` — no newline |
946
+ | `log(val, ...)` | `console.log(...args)` |
947
+ | `logln(value)` | `process.stdout.write(value)` |
948
+ | `warn "msg"` | `"Warning: msg\n"` |
949
+ | `info "msg"` | `"INFO: msg\n"` |
950
+ | `hello` | Prints `"Hello, !"` |
951
+ | `hello "a", "b"` | Prints `"Hello, a, and b!"` |
952
+ | `banner "text"` | Box-drawn ASCII banner |
953
+
954
+ **Scoped Binding:**
955
+
956
+ | Keyword | Behavior |
957
+ |---------|----------|
958
+ | `temp x = val => { }` | Override x for the block duration, then restore |
959
+ | `keep x = val` | Set x only if currently null/undefined; silent otherwise |
960
+
961
+ **Macros & Named Code:**
962
+
963
+ | Keyword | Behavior |
964
+ |---------|----------|
965
+ | `macro NAME = val` | Store value in `nova.macros` and scope |
966
+ | `snippet name { }` | Register named snippet; run with `nova.runSnippet("name")` |
967
+ | `defunc name(args) => { }` | Declare function (arrow-body style) |
968
+ | `lambda name = x => expr` | Declare arrow function |
969
+ | `compose name = f >> g` | Compose functions left-to-right: `g(f(x))` |
970
+ | `partial name = fn(arg)` | Partial application |
971
+
972
+ **Iteration:**
973
+
974
+ | Keyword | Behavior |
975
+ |---------|----------|
976
+ | `loop item in iterable { }` | For-of style |
977
+ | `foreach(col)(key, val) { }` | Key-value iteration |
978
+ | `foreach(col)(key, val, len) { }` | With length variable |
979
+
980
+ **State:**
981
+
982
+ | Keyword | Behavior |
983
+ |---------|----------|
984
+ | `backup x = x` | Save value to executor backup store |
985
+ | `retrieve x` | Restore from backup store |
986
+ | `addto arr val` | Push value to array |
987
+ | `addto map val : key` | Set `map[key] = val` |
988
+
989
+ **Async Tasks:**
990
+
991
+ | Keyword | Behavior |
992
+ |---------|----------|
993
+ | `gear(ms) name { }` | Named loop task; waits `ms` between iterations |
994
+ | `gear name { }` | Named loop task; no wait |
995
+ | `engage a >> b` | Run gears left-to-right in continuous loop until `break` |
996
+
997
+ **Isolation:**
998
+
999
+ | Keyword | Behavior |
1000
+ |---------|----------|
1001
+ | `sandbox { }` | Run in isolated Node.js vm context; scope vars snapshotted in/out |
1002
+ | `infer ("model") => var: "prompt"` | Run local AI model via `ollama` |
1003
+
1004
+ **Data:**
1005
+
1006
+ | Keyword | Behavior |
1007
+ |---------|----------|
1008
+ | `sstream name => { put val }` | Named sync stream with `put` accumulator |
1009
+ | `lend fn src to dst` | Merge `src` function body into `dst` |
1010
+ | `lend m map with fn` | Attach `fn` as method on `map` object |
1011
+
1012
+ **Session / Resumable:**
1013
+
1014
+ | Keyword | Behavior |
1015
+ |---------|----------|
1016
+ | `session("name") { }` | Store named session body |
1017
+ | `enter key type` | Log `[enter] key type` — interactive entry point marker |
1018
+ | `resu name(args) => { },` | Resumable: re-parsed and run fresh on each call |
1019
+ | `keyfunc name(x) { } { }` | Pattern-matched function dispatch |
1020
+
1021
+ **Annotation / Registry:**
1022
+
1023
+ | Keyword | Behavior |
1024
+ |---------|----------|
1025
+ | `describe "text"` | Append to `nova.descriptions` |
1026
+ | `using flagName` | Set `this.options[flag] = true` |
1027
+ | `unuse flagName` | Delete `this.options[flag]` |
1028
+ | `using namespace MyNS` | Bring all namespace members into scope |
1029
+
1030
+ **Type Ops:**
1031
+
1032
+ | Keyword | Behavior |
1033
+ |---------|----------|
1034
+ | `classify value` | Print/return type string |
1035
+ | `classify value as Type` | Return bool |
1036
+ | `rate(v) type` | Type-cast (see full table in Type System section) |
1037
+
1038
+ **Control:**
1039
+
1040
+ | Keyword | Behavior |
1041
+ |---------|----------|
1042
+ | `skip` | No-op (parsed, returns undefined) |
1043
+ | `end` | Terminate current scope: throws `{ __return: undefined }` |
1044
+ | `clear` | Reset `nova.descriptions` to `[]` |
1045
+ | `time { }` | Time a block; print elapsed ms |
1046
+ | `wait(ms)` | Sync sleep via Atomics.wait |
1047
+
1048
+ **Misc:**
1049
+
1050
+ | Keyword | Behavior |
1051
+ |---------|----------|
1052
+ | `expt "val" from { }` | Capture stdout; error if `actual.trim() !== "val"` |
1053
+ | `option name = fn` | Set runtime option/hook |
1054
+ | `env VAR` | Read `process.env.VAR` into scope; throws if undefined |
1055
+ | `eval expr` | Re-evaluate an already-parsed AST node |
1056
+ | `declare (expr) as x` | Assign to `x` |
1057
+ | `declare (expr) into x` | Append string to `x` |
1058
+ | `declare (expr) as x log` | Assign and print |
1059
+ | `declare (expr) as x throw` | Assign and throw |
1060
+ | `declare (expr) as x finalize` | Assign and `return` |
1061
+
1062
+
1063
+ ## Built-in Global Objects
1064
+
1065
+ Always in scope — no import needed.
1066
+
1067
+ ### `core`
1068
+
1069
+ | Member | Description |
1070
+ |--------|-------------|
1071
+ | `core.print(v)` | `process.stdout.write(stringify(v) + '\n')` |
1072
+ | `core.json` | Reference to `JSON` |
1073
+ | `core.getAst()` | Returns current program AST |
1074
+ | `core.vars` | Reference to global scope variables |
1075
+ | `core.types` | Reference to the TypeRegistry |
1076
+ | `core.register(fn)` | Wraps a novac function as a native callable |
1077
+ | `core.import` | Module import function |
1078
+ | `core.ndb()` | Dev bridge: `{ exe, scopes, types, eventBus, gears, backups, macros, blocks, snippets, keyfuncs, sessions, resus, namespaces }` |
1079
+
1080
+ ### `nova` (Unified Registry)
1081
+
1082
+ Live proxy over all internal registries:
1083
+
1084
+ | Member | Description |
1085
+ |--------|-------------|
1086
+ | `nova.version` | `"2.0"` |
1087
+ | `nova.macros` | All defined macros |
1088
+ | `nova.blocks` | All defined blocks |
1089
+ | `nova.snippets` | All defined snippets |
1090
+ | `nova.gears` | All defined gears |
1091
+ | `nova.sessions` | All defined sessions |
1092
+ | `nova.resus` | All defined resus |
1093
+ | `nova.keyfuncs` | All defined keyfuncs |
1094
+ | `nova.backups` | All backed-up values |
1095
+ | `nova.options` | Current option flags |
1096
+ | `nova.descriptions` | Array of all `describe` annotations |
1097
+ | `nova.scope` | Reference to global scope |
1098
+ | `nova.types.structs` | Registered struct names |
1099
+ | `nova.types.interfaces` | Registered interface names |
1100
+ | `nova.types.enums` | Registered enum names |
1101
+ | `nova.types.traits` | Registered trait names |
1102
+ | `nova.types.check(val, typeName)` | Runtime type check → bool |
1103
+ | `nova.types.list()` | All registered type names |
1104
+ | `nova.events.events()` | Active event names |
1105
+ | `nova.events.count(ev)` | Number of listeners |
1106
+ | `nova.events.has(ev)` | Has listeners? |
1107
+ | `nova.events.clear(ev)` | Remove all listeners for event |
1108
+ | `nova.events.clearAll()` | Remove all events |
1109
+ | `nova.setMacro(name, val)` | Define/update a macro |
1110
+ | `nova.getMacro(name)` | Get macro value |
1111
+ | `nova.hasMacro(name)` | Check if macro exists |
1112
+ | `nova.setBlock(name, fn)` | Register a native block |
1113
+ | `nova.runBlock(name)` | Execute a named block |
1114
+ | `nova.runSnippet(name, ...args)` | Execute a named snippet |
1115
+ | `nova.emit(event, val)` | Emit event |
1116
+ | `nova.on(event, fn)` | Listen for event |
1117
+ | `nova.eval(code)` | Parse and run a novac code string |
1118
+ | `nova.inspect(name)` | `{ inScope, isMacro, isBlock, isSnippet, isGear, isSession, isResu, value }` |
1119
+ | `nova.clearAll()` | Clear macros, blocks, snippets, gears, sessions, descriptions |
1120
+
1121
+ ### `nvk` (Platform / System Namespace)
1122
+
1123
+ `nvk` exposes all system and I/O operations. These replaced the previous statement-based system keywords.
1124
+
1125
+ | Member | Description |
1126
+ |--------|-------------|
1127
+ | `nvk.notify(title, content)` | Termux notification (fallback: stdout) |
1128
+ | `nvk.toast(msg)` | Termux toast |
1129
+ | `nvk.vibrate(ms)` | Termux vibrate |
1130
+ | `nvk.clipboard(text)` | Set clipboard via Termux |
1131
+ | `nvk.camera(path)` | Take photo via Termux |
1132
+ | `nvk.share(path)` | Share file via Termux |
1133
+ | `nvk.open(path)` | Open file via Termux |
1134
+ | `nvk.cpu` | Array of CPU model strings |
1135
+ | `nvk.mem` | Total memory e.g. `"8.00GB"` |
1136
+ | `nvk.hostname` | `os.hostname()` |
1137
+ | `nvk.uptime` | `os.uptime()` in seconds |
1138
+ | `nvk.pid` | `process.pid` |
1139
+ | `nvk.arch` | `os.arch()` |
1140
+ | `nvk.platform` | `process.platform` |
1141
+ | `nvk.osPlatform` | `os.platform()` |
1142
+ | `nvk.userInfo` | `os.userInfo()` as NovaObject |
1143
+ | `nvk.network` | `os.networkInterfaces()` as NovaObject |
1144
+ | `nvk.load` | `os.loadavg()` as NovaArray |
1145
+ | `nvk.tmpDir` | `os.tmpdir()` |
1146
+ | `nvk.cwd` | `process.cwd()` |
1147
+ | `nvk.pathDir(p)` | `path.dirname(p)` |
1148
+ | `nvk.pathBase(p)` | `path.basename(p)` |
1149
+ | `nvk.pathExt(p)` | `path.extname(p)` |
1150
+ | `nvk.pathJoin(...parts)` | `path.join(...parts)` |
1151
+ | `nvk.sha256(s)` | SHA-256 hex digest |
1152
+ | `nvk.randomBytes(n)` | N random bytes as hex string |
1153
+ | `nvk.uuid()` | Random UUID v4 |
1154
+ | `nvk.parseURL(raw)` | `{ hostname, pathname, search, protocol, port }` |
1155
+ | `nvk.readFile(path)` | `fs.readFileSync(path, 'utf8')` |
1156
+ | `nvk.writeFile(path, content)` | `fs.writeFileSync` |
1157
+ | `nvk.createFile(path, content)` | Create/overwrite file |
1158
+ | `nvk.deleteFile(path)` | `fs.unlinkSync` |
1159
+ | `nvk.listFiles(dir)` | `fs.readdirSync(dir)` as NovaArray |
1160
+ | `nvk.exists(path)` | `fs.existsSync(path)` |
1161
+ | `nvk.execFile(path)` | Parse and run a `.nova` file |
1162
+ | `nvk.sh(cmd)` | `execSync(cmd)` — prints and returns stdout |
1163
+ | `nvk.exec(code)` | Parse and run a novac code string |
1164
+ | `nvk.term(cmd, shell)` | Run cmd in specified shell (`bash` default) |
1165
+ | `nvk.banner(text)` | Print box-drawn banner |
1166
+ | `nvk.warn(msg)` | `"Warning: msg"` |
1167
+ | `nvk.info(msg)` | `"INFO: msg"` |
1168
+
1169
+ ### `qae` (Query Assertion Engine)
1170
+
1171
+ All members are functions `(a) => bool` or `(a) => value`:
1172
+
1173
+ | Member | Description |
1174
+ |--------|-------------|
1175
+ | `qae.even(a)` / `qae.odd(a)` | Parity check |
1176
+ | `qae.integer(a)` | `Number.isInteger(a)` |
1177
+ | `qae.positive(a)` / `qae.negative(a)` / `qae.zero(a)` | Sign check |
1178
+ | `qae.finite_(a)` / `qae.infinite(a)` / `qae.nan_(a)` | Numeric state |
1179
+ | `qae.isnull(a)` / `qae.defined(a)` | Nullability |
1180
+ | `qae.trimable(a)` / `qae.trimmed(a)` | Whitespace |
1181
+ | `qae.uppercase(a)` / `qae.lowercase(a)` | Case transform |
1182
+ | `qae.numeric(a)` / `qae.alpha(a)` / `qae.alnum(a)` / `qae.blank(a)` | String class |
1183
+ | `qae.palindrome(a)` | Is palindrome (case-insensitive, strips spaces) |
1184
+ | `qae.empty(a)` / `qae.nonempty(a)` | Emptiness |
1185
+ | `qae.unique(a)` | Array has no duplicates |
1186
+ | `qae.first(a)` / `qae.last(a)` / `qae.length(a)` | Array/string access |
1187
+ | `qae.truthy(a)` / `qae.falsy(a)` | Boolean coercion |
1188
+ | `qae.type_(a)` | `typeof a` |
1189
+ | `qae.string_(a)` / `qae.number_(a)` / `qae.boolean_(a)` / `qae.object_(a)` / `qae.array_(a)` / `qae.function_(a)` | Type checks |
1190
+ | `qae.prime(a)` / `qae.composite(a)` / `qae.oddprime(a)` | Number theory |
1191
+ | `qae.vowel(a)` / `qae.consonant(a)` | Character class |
1192
+ | `qae.sorted(a)` / `qae.ascending(a)` / `qae.descending(a)` | Order checks |
1193
+ | `qae.contains(a, b)` / `qae.startsWith_(a, b)` / `qae.endsWith_(a, b)` / `qae.matches_(a, b)` | Containment |
1194
+ | `qae.divisible(a, b)` / `qae.between_(a, lo, hi)` | Numeric predicates |
1195
+ | `qae.check(a, pred)` | Apply function or named qae predicate to a |
1196
+
1197
+ ### `novaRegex(pattern, flags)`
1198
+
1199
+ Semantic regex with `<name>` placeholders. Returns a JS `RegExp`.
1200
+
1201
+ ```nova
1202
+ let emailRe = novaRegex("<email>")
1203
+ emailRe.test("a@b.com") // true
1204
+ novaRegex("<email|url>") // combined with |
437
1205
  ```
438
1206
 
439
- ### Collections
1207
+ Semantic names: `keyword` `symbol` `digit` `nondigit` `whitespace` `tab` `newline` `start` `end` `any` `wordboundary` `word` `variable` `integer` `float` `hex` `binary` `space` `title` `email` `url` `uuid` `date` `time` `color`
1208
+
1209
+ ### Global Functions
1210
+
1211
+ | Function | Description |
1212
+ |----------|-------------|
1213
+ | `typeOf(v)` | `"number"` `"string"` `"bool"` `"null"` `"array"` `"object"` `"range"` `"pointer"` `"function"` `"struct:Name"` `"enum:Name"` |
1214
+ | `typecheck(v, typeName)` | Runtime type check → bool |
1215
+ | `satisfies(v, ifaceName)` | Does value satisfy all required interface members? |
1216
+ | `fetch(url, options?)` | Sync HTTP → `{ ok, status, body, text }` |
1217
+ | `setTimeout(fn, delay)` | Schedule function after delay |
1218
+
1219
+ ---
1220
+
1221
+ ## Built-in Class Objects
1222
+
1223
+ All available globally:
1224
+
1225
+ ### `ForLoop(opts)`
440
1226
 
441
1227
  ```nova
442
- // addto push to array or set map key
443
- let log = [];
444
- addto log "startup";
445
- addto log "ready";
1228
+ ForLoop({ from: 1, to: 10 }).map(i => i * i).collect()
1229
+ ForLoop({ from: 0, to: 20, step: 2 }).toArray()
1230
+ ForLoop({ from: 1, to: 100 }).do(i => { sum += i }).run()
1231
+ ```
1232
+
1233
+ | Method | Description |
1234
+ |--------|-------------|
1235
+ | `.from(n)` `.to(n)` `.step(n)` | Configure range |
1236
+ | `.do(fn)` / `.each(fn)` / `.map(fn)` | Set body |
1237
+ | `.run()` | Execute without collecting |
1238
+ | `.collect()` | Execute and collect results |
1239
+ | `.toArray()` | Collect index values |
1240
+ | `.filter(fn)` | Convert to Pipeline then filter |
1241
+ | `.pipe()` | Convert to Pipeline |
1242
+ | `.toStream()` | Convert to DataStream |
1243
+
1244
+ ### `WhileLoop(opts)`
446
1245
 
447
- let config = {};
448
- addto config "v2":version;
1246
+ ```nova
1247
+ WhileLoop({}).cond(() => n < 32).do(() => { n *= 2 }).maxIter(100).run()
449
1248
  ```
450
1249
 
451
- ### Code Reuse
1250
+ ### `IfBlock(opts)`
452
1251
 
453
1252
  ```nova
454
- // macro text substitution constant
455
- macro PI = 3.14159;
456
- macro MAX_RETRY = 3;
1253
+ IfBlock({ cond: score >= 90, then: () => "A" })
1254
+ .elseIf(score >= 80, () => "B").else(() => "F").run()
1255
+ ```
457
1256
 
458
- // lambda — inline function binding
459
- lambda square = x => x * x;
1257
+ ### `MatchBlock(subject)`
460
1258
 
461
- // compose — pipeline two or more functions with >>
462
- func trim(s) => { give s; }
463
- func upper(s) => { give f"{s}!"; }
464
- compose shout = trim >> upper;
1259
+ ```nova
1260
+ MatchBlock(404)
1261
+ .when(200, () => "OK")
1262
+ .when(404, () => "Not Found")
1263
+ .when(90..100, x => "A")
1264
+ .when(x => x > 500, x => "server err")
1265
+ .default(() => "Unknown")
1266
+ .run()
1267
+ ```
465
1268
 
466
- // partial — pre-bind arguments
467
- func multiply(a, b) => { give a * b; }
468
- partial double = multiply(2);
1269
+ ### `TryCatch()`
469
1270
 
470
- // defunc — named function with arrow syntax
471
- defunc greet(name) => {
472
- core.print(f"Hello {name}");
473
- }
1271
+ ```nova
1272
+ TryCatch().try(() => { throw "oops" }).catch(e => core.print(e)).finally(() => {}).run()
1273
+ ```
1274
+
1275
+ ### `Pipeline(initial)`
474
1276
 
475
- // block/snippet — named code blocks (run on demand)
476
- block setup { core.print("setup run"); }
477
- snippet cleanup { core.print("cleanup run"); }
1277
+ ```nova
1278
+ Pipeline([1,2,3,4,5]).filter(x => x % 2 == 0).map(x => x*x).take(2).collect()
478
1279
  ```
479
1280
 
480
- ### State
1281
+ | Method | Description |
1282
+ |--------|-------------|
1283
+ | `.pipe(fn)` / `.map(fn)` | Transform |
1284
+ | `.filter(fn)` | Filter |
1285
+ | `.tap(fn)` | Side-effect passthrough |
1286
+ | `.flatMap(fn)` | Map + flatten |
1287
+ | `.reduce(fn, init)` | Immediate reduce |
1288
+ | `.take(n)` / `.skip(n)` | Slice |
1289
+ | `.run()` / `.collect()` | Execute |
1290
+
1291
+ ### `FuncDef(opts)`
481
1292
 
482
1293
  ```nova
483
- // backup / retrieve save and restore a variable
484
- let session = "user_abc";
485
- backup session = session;
486
- let session = "guest";
487
- retrieve session;
488
- core.print(session); // user_abc
1294
+ FuncDef({ args: ["a", "b"], body: (a, b) => a + b }).named("add").call(10, 32)
1295
+ ```
489
1296
 
490
- // describe — attach documentation annotations
491
- describe "This module handles authentication";
1297
+ ### `Timer()`
1298
+
1299
+ ```nova
1300
+ let t = Timer(); t.start(); t.lap(); let ms = t.stop()
1301
+ t.elapsed // ms since start
1302
+ t.laps // NovaArray of lap times
492
1303
  ```
493
1304
 
494
- ### Feature Flags
1305
+ ### `Counter(initial, step)`
495
1306
 
496
1307
  ```nova
497
- using strictMode; // enable a runtime flag
498
- unuse strictMode; // disable it
1308
+ let c = Counter(0); c.increment(5); c.decrement(2); c.clamp(0, 100); c.value
499
1309
  ```
500
1310
 
501
- ### Nova Classic Infix Operators
1311
+ ### `Stack()` / `Queue()` / `LinkedList()`
502
1312
 
503
1313
  ```nova
504
- // Boolean logic
505
- true xor true // false exclusive or
506
- true nand true // false not and
507
- false nor false // true — not or
508
- true xnor true // true — not xor
1314
+ let s = Stack(); s.push(10); s.pop(); s.peek(); s.size; s.empty
1315
+ let q = Queue(); q.enqueue("a"); q.dequeue(); q.peek(); q.size
1316
+ let ll = LinkedList(); ll.push("a"); ll.shift(); ll.pop(); ll.unshift("z"); ll.size
1317
+ ```
509
1318
 
510
- // Type checks
511
- 42 is 42 // true — strict identity
512
- 1 isnt 2 // true — not identical
513
- 42 istypeof "number" // true
514
- "hello" matches "hell" // regex test
1319
+ ### `State(initial)`
515
1320
 
516
- // Set operations
517
- [1,2,3] intersect [2,3,4] // [2,3]
518
- [1,2] union [2,3] // [1,2,3]
519
- [1,2,3] diff_arr [2,3] // [1]
520
- [1,2,3] zip ["a","b","c"] // [[1,"a"],[2,"b"],[3,"c"]]
1321
+ ```nova
1322
+ let sm = State("idle")
1323
+ sm.add("idle", "start", "running").add("running", "done", "idle")
1324
+ sm.onEnter("running", s => {}).onExit("idle", s => {})
1325
+ sm.dispatch("start") // true = success
1326
+ sm.current; sm.history; sm.is("running")
1327
+ ```
521
1328
 
522
- // Numeric
523
- 4 avg 6 // 5
524
- 12 gcd 8 // 4
525
- 4 lcm 6 // 12
526
- "a" cmp "b" // -1 (locale compare)
527
- "Hello" equals_ignore "hello" // true
1329
+ ### `Observable(initial)` / `Signal(initial)`
528
1330
 
529
- // Array with step
530
- 0..20 step 5 // [0, 5, 10, 15, 20]
1331
+ ```nova
1332
+ let obs = Observable(0)
1333
+ obs.subscribe((newVal, oldVal) => {})
1334
+ obs.value = 42
1335
+ let derived = obs.pipe(x => x * 2)
1336
+
1337
+ let sig = Signal(1)
1338
+ let doubled = sig.derive(x => x * 2)
1339
+ sig.value = 5 // doubled.value = 10 (automatic propagation)
1340
+ sig.effect(v => {}) // runs immediately and on every change
1341
+ ```
1342
+
1343
+ ### `Validator()`
531
1344
 
532
- // Object/array merge
533
- {x:1} extend {y:2} // {x:1, y:2}
534
- [1,2] concat [3,4] // [1,2,3,4]
1345
+ ```nova
1346
+ Validator().required().type("number").min(0).max(100).validate(50)
1347
+ // { valid: true, errors: [], value: 50 }
535
1348
  ```
536
1349
 
537
- ### Type Casting (`rate`)
1350
+ | Method | Description |
1351
+ |--------|-------------|
1352
+ | `.required()` `.type(t)` `.min(n)` `.max(n)` | Core validators |
1353
+ | `.minLen(n)` `.maxLen(n)` `.pattern(re)` `.email()` | String validators |
1354
+ | `.custom(fn, msg)` | Custom validator |
1355
+ | `.validate(v)` | Returns `{ valid, errors: NovaArray, value }` |
1356
+
1357
+ ### `DataStream(source)`
1358
+
1359
+ ```nova
1360
+ DataStream([5,3,8,1,9]).filter(x => x > 3).sort().reverse().take(3).collect()
1361
+ ```
1362
+
1363
+ | Method | Description |
1364
+ |--------|-------------|
1365
+ | `.map` `.filter` `.take` `.skip` `.flatMap` | Transform |
1366
+ | `.distinct()` | Deduplicate via Set |
1367
+ | `.sort(fn?)` `.reverse()` `.zip(other)` | Reorder |
1368
+ | `.collect()` `.reduce(fn, init)` `.forEach(fn)` | Materialize |
1369
+ | `.first()` `.last()` `.count()` | Inspect |
1370
+
1371
+ ### `Transformer()` / `TransformerJSON()` / `TransformerBase64()`
538
1372
 
539
1373
  ```nova
540
- rate(3.9) int // 3 — truncate
541
- rate(300) u8 // 44 — 300 & 0xFF
542
- rate(200) i8 // -56 — signed 8-bit
543
- rate(300) u16 // 300
544
- rate(5) bigint // 5n
545
- rate("hi") char // "h"
546
- rate(1.5) f32 // 32-bit float precision
1374
+ let jt = TransformerJSON(); jt.to({ x: 1 }); jt.from('{"x":1}')
1375
+ let b64 = TransformerBase64(); b64.to("Hello!"); b64.from(encoded)
1376
+ // Static presets: Transformer.upper(), Transformer.trim_(), Transformer.number_()
547
1377
  ```
548
1378
 
549
- ### Type Classification (`classify`)
1379
+ ### `Router()`
550
1380
 
551
1381
  ```nova
552
- classify 42 // "number"
553
- classify "x" // "string"
554
- classify [1,2] // "array"
555
- classify 42 as int // true
556
- classify "x" as int // false
1382
+ let router = Router()
1383
+ router.on("/api/users/(.*)", id => f"user {id}").default(p => f"404: {p}")
1384
+ router.dispatch("/api/users/42") // "user 42"
557
1385
  ```
558
1386
 
559
- ### QAEs — Query Assertions / Predicates
1387
+ ### `EventBus()`
560
1388
 
561
1389
  ```nova
562
- qae.prime(17) // true
563
- qae.even(4) // true
564
- qae.palindrome("racecar") // true
565
- qae.ascending([1,2,3]) // true
566
- qae.blank("") // true
567
- qae.contains([1,2],2) // true
568
- qae.vowel("a") // true
569
- qae.divisible(12, 4) // true
570
- qae.between_(5, 1, 10)// true
571
- // 40+ predicates total
1390
+ let bus = EventBus()
1391
+ bus.on("data", v => {}).once("ready", () => {}).off("data", handler)
1392
+ bus.emit("data", 42); bus.events()
572
1393
  ```
573
1394
 
574
- ### Nova Semantic Regex
1395
+ ### `Memo(fn, keyFn?)` / `Lazy(fn)`
575
1396
 
576
1397
  ```nova
577
- let emailRx = novaRegex("<email>");
578
- let intRx = novaRegex("<integer>");
579
- let urlRx = novaRegex("<url>");
580
- emailRx.test("a@b.com"); // true
1398
+ let m = Memo(x => x * x * x)
1399
+ m.call(5); m.stats; m.clear(); m.invalidate(5)
581
1400
 
582
- // Built-in pattern names:
583
- // integer, float, word, variable, keyword, digit, whitespace,
584
- // email, url, uuid, date, time, color, hex, binary, title, etc.
1401
+ let lazy = Lazy(() => expensiveOp())
1402
+ lazy.value; lazy.reset(); lazy.map(fn)
585
1403
  ```
586
1404
 
587
- ### stdlib extras
1405
+ ---
1406
+
1407
+ ## Standard Library (`std`)
1408
+
1409
+ Always available as `std.X`.
1410
+
1411
+ ### `std.Math`
1412
+
1413
+ `abs` `ceil` `floor` `round` `sqrt` `pow` `max` `min` `log` `log2` `log10` `sin` `cos` `tan` `PI` `E` `trunc` `sign` `random` plus:
1414
+
1415
+ | Member | Description |
1416
+ |--------|-------------|
1417
+ | `clamp(v, lo, hi)` | `Math.min(Math.max(v,lo),hi)` |
1418
+ | `floorTo(a, b)` | Floor to nearest multiple of b |
1419
+ | `ceilTo(a, b)` | Ceil to nearest multiple of b |
1420
+ | `factorial(n)` | Memoized factorial |
1421
+ | `fibonacci(n)` | Memoized fibonacci |
1422
+ | `divmod(a, b)` | `[quotient, remainder]` |
1423
+
1424
+ ### `std.Array`
1425
+
1426
+ `from(it)` `of(...items)` `range(start, end, step)` `isArray(v)` `fill(n, val)` `zip(...arrs)`
1427
+
1428
+ ### `std.Object`
1429
+
1430
+ `keys(o)` `values(o)` `entries(o)` `assign(target, ...srcs)` `freeze(o)` `has(o, k)` `create(proto)`
1431
+
1432
+ ### `std.String`
1433
+
1434
+ `from(v)` `padStart` `padEnd` `repeat` `includes` `startsWith` `endsWith` `trim` `split` `replace` `replaceAll` `toUpper` `toLower` `charAt` `charCodeAt` `fromCharCode` `slice` `indexOf`
1435
+
1436
+ ### `std.is`
588
1437
 
589
1438
  ```nova
590
- std.fnum(0, 100, 50) // clamped float var
591
- std.fint(0, 10, 5) // clamped integer var
592
- std.convertCase("helloWorld", "snake") // "hello_world"
593
- std.randomWord() // random pronounceable word
594
- std.randomName() // random first name
1439
+ std.is.number(v) std.is.string(v) std.is.bool(v) std.is.array(v)
1440
+ std.is.object(v) std.is.null(v) std.is.func(v) std.is.range(v)
1441
+ std.is.integer(v) std.is.finite(v) std.is.NaN(v)
595
1442
  ```
596
1443
 
1444
+ ### `std.fn`
1445
+
1446
+ `identity` `compose` `pipe` `memoize` `once` `partial` `curry` `noop` `always` `flip` `not`
1447
+
1448
+ ### Other `std` Members
1449
+
1450
+ | Member | Description |
1451
+ |--------|-------------|
1452
+ | `std.num(v)` / `std.str(v)` / `std.bool(v)` / `std.int(v)` | Coercions |
1453
+ | `std.print(...)` | `console.log(...)` |
1454
+ | `std.error(msg)` | Throw an Error |
1455
+ | `std.range(start, end, step)` | Create NovaRange |
1456
+ | `std.fnum(min, max, initial)` | Clamped float with `.value` get/set |
1457
+ | `std.fint(min, max, initial)` | Clamped integer with `.value` get/set |
1458
+ | `std.convertCase(str, target)` | `"snake"` `"camel"` `"pascal"` `"kebab"` `"upper"` `"lower"` |
1459
+ | `std.randomWord()` | Random pronounceable word |
1460
+ | `std.randomName(format?, minLen?, maxLen?)` | Capitalized random name |
1461
+ | `std.console.*` | `log warn error info time timeEnd` |
1462
+ | `std.Date.now()` / `std.Date.create(...)` | Date utilities |
1463
+ | `std.json.parse(s)` / `std.json.stringify(v, sp?)` | JSON with NovaObject unwrapping |
1464
+ | `std.Promise.*` | `resolve reject all race allSettled any` |
597
1465
 
598
1466
  ---
599
1467
 
600
- ## Unified `nova` Registry Object
1468
+ ## Standard Built-ins (`bstd` / `import_builtin`)
1469
+
1470
+ ### `Air`
601
1471
 
602
- All Nova registries are merged into a single live `nova` object accessible from any Nova code.
1472
+ Object of references to all novac core modules keyed by filename.
1473
+
1474
+ ### `Chalk`
1475
+
1476
+ ANSI terminal colors (same API as `chalk` npm package):
603
1477
 
604
1478
  ```nova
605
- // Read registries
606
- nova.macros // all macros as an object
607
- nova.blocks // all declared blocks
608
- nova.snippets // all declared snippets
609
- nova.gears // all declared gears
610
- nova.sessions // all declared sessions
611
- nova.options // current feature flags
612
- nova.descriptions // describe() annotations
613
- nova.types // type registry (.structs, .interfaces, .enums, .traits)
614
- nova.events // event bus view (.events(), .count(ev), .has(ev))
615
- nova.version // "2.0"
1479
+ import_builtin Chalk
1480
+ core.print(Chalk.red("error"))
1481
+ core.print(Chalk.bold.green("success"))
1482
+ ```
616
1483
 
617
- // Macro operations
618
- nova.getMacro("KEY") // get a macro value
619
- nova.hasMacro("KEY") // check existence
620
- nova.setMacro("KEY", val) // set programmatically
1484
+ ### `History`
621
1485
 
622
- // Block/snippet operations
623
- nova.runBlock("blockName") // execute a block
624
- nova.runSnippet("snippetName", args) // execute a snippet with args
1486
+ File-backed input history stored as `.184.<name>`:
625
1487
 
626
- // Event operations
627
- nova.emit("eventName", value) // fire an event
628
- nova.on("eventName", fn) // subscribe (native fn)
1488
+ `getKey(name)` `exists(key)` `createHistory(key)` `appendHistory(key, item, mode?, index?)` `readHistory(key)` `rawSetHistory(key, array)` `rawGetHistory(key)` `clearHistory(key)`
629
1489
 
630
- // Inspect any name across all registries
631
- let info = nova.inspect("myVar");
632
- // info.inScope, info.isMacro, info.isBlock, info.value ...
1490
+ ### `Input`
633
1491
 
634
- // Dynamic evaluation
635
- let result = nova.eval("1 + 2 + 3");
1492
+ Synchronous terminal input with full line-editing:
636
1493
 
637
- // Clear all registries
638
- nova.clearAll();
1494
+ ```nova
1495
+ import_builtin Input
1496
+ let r = Input.prompt("Name: ")
1497
+ // r: { result: "typed text", history: [] }
1498
+ Input.prompt("Pwd: ", { password: true, passwordHash: "*" })
1499
+ Input.prompt("Cmd: ", true) // enable history
1500
+ Input.prompt("Search: ", { completer: buf => "suggestion" })
639
1501
  ```
640
1502
 
641
- ---
1503
+ Features: cursor, history `↑↓`, backspace, delete, tab completion, Ctrl+C, password masking.
642
1504
 
643
- ## Built-in Class Objects
1505
+ ### `Shema` / `ShemaType`
1506
+
1507
+ ```nova
1508
+ import_builtin Shema, ShemaType
1509
+ let schema = new Shema({ name: ShemaTypes.String, age: ShemaTypes.Int })
1510
+ schema.validate({ name: "novac", age: 2 })
1511
+ schema.defaultObj()
1512
+ new ShemaType("PositiveInt", val => Number.isInteger(val) && val > 0)
1513
+ ```
1514
+
1515
+ Built-in `ShemaTypes`: `Int` `Float` `String` `Boolean` `Array` `Object` `Function` `Null` `Undefined` `Symbol` `BigInt` `Date` `RegExp` `Shema` `Any`
644
1516
 
645
- Every control flow statement and common pattern has an equivalent OOP class.
1517
+ ### `ConfigFile`
646
1518
 
647
- ### `ForLoop`
648
1519
  ```nova
649
- // Range iteration as object
650
- let fl = ForLoop({ from: 1, to: 10, step: 2 });
651
- fl.do(i => core.print(i)).run();
652
- let arr = fl.collect(); // returns NovaArray of values
653
- let range = fl.toRange(); // returns NovaRange
1520
+ import_builtin ConfigFile
1521
+ let cfg = new ConfigFile("app", "json", false, mySchema)
1522
+ cfg.writeFull({ host: "localhost", port: 8080 })
1523
+ cfg.write(["database", "host"], "db.example.com")
1524
+ cfg.readFull(); cfg.read(["database", "host"])
1525
+ cfg.interactiveSetup(defaultConfig)
1526
+ ```
654
1527
 
655
- // Chained builder style
656
- ForLoop({}).from(1).to(100).step(5).do(i => core.print(i)).run();
1528
+ ### `JsDB`
1529
+
1530
+ `JsDB.class(JsClass)` `JsDB.run(code)` `JsDB.require` `JsDB.gt` `JsDB.fetch(url, opts)` `JsDB.process` `JsDB.version` `JsDB.platform` `JsDB.env`
1531
+
1532
+ ### `Storage`
1533
+
1534
+ ```nova
1535
+ import_builtin Storage
1536
+ Storage.setItem("key", { data: 42 })
1537
+ Storage.getItem("key")
1538
+ Storage.removeItem("key")
1539
+ Storage.clear()
657
1540
  ```
658
1541
 
659
- ### `WhileLoop`
1542
+ ### `Crypto`
1543
+
660
1544
  ```nova
661
- let n = 0;
662
- WhileLoop({ cond: () => n < 10, body: () => n++ }).run();
663
- WhileLoop({}).cond(() => n < 20).do(() => n++).maxIter(100).run();
1545
+ import_builtin Crypto
1546
+ Crypto.hash("data") // SHA-256 hex
1547
+ Crypto.hash("data", "sha512") // any Node.js crypto algo
664
1548
  ```
665
1549
 
666
- ### `IfBlock`
1550
+ ### `PathUtils`
1551
+
1552
+ `join` `resolve` `basename` `dirname` `extname`
1553
+
1554
+ ### `Base64`
1555
+
1556
+ `Base64.encode(s)` `Base64.decode(s)`
1557
+
1558
+ ### `OsUtils`
1559
+
1560
+ `platform` `homedir()` `tmpdir()` `cpus()` `totalmem()` `freemem()` `uptime()` `openFile(path)` (Windows: `requestAdminPrivileges()`)
1561
+
1562
+ ### `Zip`
1563
+
667
1564
  ```nova
668
- let result = IfBlock({ cond: score >= 90, then: () => "A", else: () => "B" }).run();
1565
+ import_builtin Zip
1566
+ await Zip.zip(["a.txt"], "out.zip")
1567
+ await Zip.unzip("out.zip", "./output/")
1568
+ ```
1569
+
1570
+ ---
1571
+
1572
+ ## Array Methods
1573
+
1574
+ Auto-dispatched on any NovaArray:
669
1575
 
670
- // With elseIf chaining
671
- IfBlock({ cond: false })
672
- .elseIf(score >= 80, () => "B")
673
- .elseIf(score >= 70, () => "C")
674
- .else(() => "F")
675
- .run();
1576
+ `map` `filter` `reduce` `find` `findIndex` `some` `every` `flat` `flatMap` `sort` `reverse` `slice` `splice` `indexOf` `includes` `join` `push` `pop` `shift` `unshift` `concat` `fill` `forEach` `keys` `values` `entries` `at` `first` `last` `random` `unique` `count` `groupBy` `shuffle` `min` `max` `sum` `avg` `product` `toArray` `toObject` `toString` | `.length` (property)
1577
+
1578
+ ---
1579
+
1580
+ ## Object Methods
1581
+
1582
+ Auto-dispatched on any NovaObject:
1583
+
1584
+ `keys()` `values()` `entries()` `has(k)` `assign(...srcs)` | `.length` (property)
1585
+
1586
+ ---
1587
+
1588
+ ## String Methods
1589
+
1590
+ Auto-dispatched on any string:
1591
+
1592
+ `toUpperCase` `toLowerCase` `trim` `split` `includes` `startsWith` `endsWith` `slice` `indexOf` `lastIndexOf` `repeat` `replace` `match` | `.length` (property)
1593
+
1594
+ ---
1595
+
1596
+ ## Type System & Casting
1597
+
1598
+ ### `as` Cast
1599
+
1600
+ ```nova
1601
+ let n = value as int // Math.trunc(Number(v))
1602
+ let f = value as float // Number(v)
1603
+ let s = value as string // String(v)
1604
+ let b = value as bool // Boolean(v)
1605
+ let a = value as array // _toIterable(v) → NovaArray
1606
+ let st = value as MyStruct
676
1607
  ```
677
1608
 
678
- ### `TryCatch`
1609
+ ### `rate()` Cast — Full Type Table
1610
+
1611
+ | Type | Operation |
1612
+ |------|-----------|
1613
+ | `int` | `Math.trunc(Number(v))` |
1614
+ | `float` / `f64` | `Number(v)` |
1615
+ | `f32` | `Math.fround(Number(v))` |
1616
+ | `string` | `String(v)` |
1617
+ | `bool` | `Boolean(v)` |
1618
+ | `bigint` | `BigInt(Math.trunc(Number(v)))` |
1619
+ | `u8` | `parseInt(v) & 0xFF` |
1620
+ | `u16` | `parseInt(v) & 0xFFFF` |
1621
+ | `u32` | `parseInt(v) >>> 0` |
1622
+ | `i8` | `(parseInt(v) << 24) >> 24` |
1623
+ | `i16` | `(parseInt(v) << 16) >> 16` |
1624
+ | `i32` | `parseInt(v) \| 0` |
1625
+ | `char` | `String(v)[0]` |
1626
+ | `array` | `_toIterable(v)` → NovaArray |
1627
+ | `StructName` | `createStruct("StructName", v)` |
1628
+
1629
+ ### Type Check
1630
+
679
1631
  ```nova
680
- TryCatch()
681
- .try(() => riskyOperation())
682
- .catch(e => core.print(f"Error: {e}"))
683
- .finally(() => cleanup())
684
- .run();
1632
+ typecheck(42, "number") // true
1633
+ satisfies(doc, "Serializable") // true if all required interface members present
1634
+ classify value // returns type string
1635
+ classify value as TypeName // returns bool
1636
+ ```
1637
+
1638
+ ---
1639
+
1640
+ ## CLI — novac
1641
+
1642
+ ```sh
1643
+ novac <file> [args...] Run a .nova / .nv file
1644
+ novac test <file> Check syntax (exit 0 = OK, prints "Syntax OK")
1645
+ novac format [file] Format a file (or stdin if no file given)
1646
+ novac eval <code> Evaluate a novac code string inline
1647
+ novac tokens <file> Print token stream as JSON
1648
+ novac ast <file> Print AST as JSON
1649
+ novac repl Start interactive REPL
1650
+ novac new <project> Scaffold a new project directory
1651
+ novac new-kit <dirname> Scaffold a new kit in current directory
1652
+ novac init PATH Add npm bin to system PATH (writes to shell rc)
1653
+ novac init build Bundle project into a .novamod file
1654
+ novac config get [key] Read global/project config (all keys if no key given)
1655
+ novac config set <key> <value> Write a config key
1656
+ novac config init Interactive config setup wizard
1657
+ novac etc notices Show runtime notices from stdlib
1658
+ novac etc describe <file> Print a human-readable description of a .nova file's AST
1659
+ novac etc kit <name> Install a built-in kit into ./nova_modules/<name>
1660
+ novac etc kit <name> --global Install a built-in kit into ~/.novac/nova_modules/<name>
1661
+ novac module install [path] Install a .novamod bundle (or all deps from nova.config.json)
1662
+ novac module install [path] -g Install globally into ~/.novac/nova_modules
1663
+ novac module list List all installed modules in nova_modules/
1664
+ novac module remove <name> Remove an installed module
1665
+ ```
1666
+
1667
+ ### Running a File
1668
+
1669
+ ```sh
1670
+ novac src/main.nova
1671
+ novac src/main.nova arg1 arg2
1672
+ novac src/main.nova --port 8080 --debug
1673
+ novac src/main.nova @addr1 +feature1
685
1674
  ```
686
1675
 
687
- ### `Pipeline`
1676
+ All arguments after the file are parsed and exposed as the `cli` object inside the script (see below).
1677
+
1678
+ ### Argument Parsing Rules
1679
+
1680
+ | Argument form | Goes into |
1681
+ |---------------|-----------|
1682
+ | `positional` (no prefix) | `cli.args` array |
1683
+ | `-flag` | `cli.options.flag = true` |
1684
+ | `--flag` | `cli.options.flag = true` |
1685
+ | `--flag value` | `cli.options.flag = "value"` |
1686
+ | `--flag=value` | `cli.options.flag = "value"` |
1687
+ | `@name` | `cli.addrs.name = true` |
1688
+ | `+feature` | `cli.additions.feature = true` |
1689
+
1690
+ Arguments that start with `-`, `@`, or `+` are **excluded** from `cli.args`.
1691
+
1692
+ ### REPL Commands
1693
+
1694
+ Inside `novac repl`:
1695
+
1696
+ | Command | Description |
1697
+ |---------|-------------|
1698
+ | `.exit` | Exit the REPL |
1699
+ | `.clear` | Clear the screen |
1700
+ | `.ast <code>` | Print AST of inline code |
1701
+ | `.tokens <code>` | Print token stream of inline code |
1702
+ | `.help` | Show REPL help |
1703
+
1704
+ History is persisted to `~/.nova_repl_history`.
1705
+
1706
+ ### `novac new <project>`
1707
+
1708
+ Scaffolds a project directory with:
1709
+
1710
+ ```
1711
+ <project>/
1712
+ src/main.nova // starter file with print("Hello, Nova!")
1713
+ bin/<project>.nv // entry script (empty, chmod 755)
1714
+ nova_modules/ // local module directory
1715
+ nova.config.json // project manifest
1716
+ README.md
1717
+ .gitignore
1718
+ ```
1719
+
1720
+ `nova.config.json` fields: `name`, `version`, `description`, `author`, `license`, `main`, `srcDir`, `scripts.run`, `dependencies`, `devDependencies`.
1721
+
1722
+ ### `novac init build`
1723
+
1724
+ Reads `nova.config.json` from `cwd`, collects all `.nova`/`.nv` files from `srcDir`, and bundles them into `<name>.novamod` — a JSON file with:
1725
+
1726
+ ```json
1727
+ {
1728
+ "manifest": { "name", "version", "main", ...config },
1729
+ "sources": { "relative/path.nova": "source code", ... },
1730
+ "buildTime": "ISO timestamp"
1731
+ }
1732
+ ```
1733
+
1734
+ Install in another project: `novac module install ./<name>.novamod`
1735
+
1736
+ ### `novac etc kit <name>`
1737
+
1738
+ Installs a built-in kit from `novac/kits/<name>/` into `nova_modules/`. Available built-in kits: `kitnovacweb`, `kitlibfs`, `kitlibproc`.
1739
+
1740
+ ---
1741
+
1742
+ ## Built-in `cli` Object
1743
+
1744
+ When a file is run via `novac <file> [args...]`, a `cli` object is automatically injected into the global scope:
1745
+
688
1746
  ```nova
689
- let result = Pipeline([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
690
- .filter(x => x % 2 == 0)
691
- .map(x => x * x)
692
- .take(3)
693
- .skip(1)
694
- .collect();
1747
+ // novac myapp.nova hello world --port 8080 --debug @prod +cache
695
1748
 
696
- // Reduce
697
- let sum = Pipeline([1, 2, 3, 4]).reduce((acc, x) => acc + x, 0);
1749
+ cli.args // ["hello", "world"] — positional args (no flag prefix)
1750
+ cli.options // { port: "8080", debug: true }
1751
+ cli.addrs // { prod: true }
1752
+ cli.additions // { cache: true }
1753
+ cli.raw // process.argv — full raw argument array
698
1754
  ```
699
1755
 
700
- ### `MatchBlock`
1756
+ ### Full `cli` Object Shape
1757
+
1758
+ | Property | Type | Description |
1759
+ |----------|------|-------------|
1760
+ | `cli.args` | Array | Positional arguments (no `-`, `@`, `+` prefix) |
1761
+ | `cli.options` | Object | All `-flag`, `--flag`, `--flag=val`, `--flag val` arguments |
1762
+ | `cli.addrs` | Object | All `@name` arguments, values are `true` |
1763
+ | `cli.additions` | Object | All `+name` arguments, values are `true` |
1764
+ | `cli.raw` | Array | `process.argv` — complete unprocessed argument list |
1765
+
1766
+ ### Usage Examples
1767
+
701
1768
  ```nova
702
- let grade = MatchBlock(score)
703
- .when(90..100, x => "A")
704
- .when(80..89, x => "B")
705
- .when(x => x >= 70, x => "C") // predicate pattern
706
- .when([60, 65, 69], x => "D") // array of values
707
- .default(x => "F")
708
- .run();
1769
+ // novac server.nova --port 3000 --host localhost
1770
+ let port = cli.options.port or "8080"
1771
+ let host = cli.options.host or "0.0.0.0"
1772
+ server(port) {
1773
+ get "/" (req, res) { res.json({ host, port }) }
1774
+ }
709
1775
  ```
710
1776
 
711
- ### `FuncDef`
712
1777
  ```nova
713
- let fn = FuncDef({ args: ["a", "b"], body: (a, b) => a + b });
714
- core.print(fn.call(3, 4)); // 7
1778
+ // novac build.nova src/main.nova --minify --output dist/app.js
1779
+ let inputFile = cli.args[0]
1780
+ let minify = cli.options.minify or false
1781
+ let output = cli.options.output or "out.js"
1782
+ ```
715
1783
 
716
- let arrow = fn.toArrow();
717
- core.print(arrow(5, 6)); // 11
1784
+ ```nova
1785
+ // novac deploy.nova @production +cache
1786
+ if (cli.addrs.production) {
1787
+ core.print("Deploying to production")
1788
+ }
1789
+ if (cli.additions.cache) {
1790
+ core.print("Cache warming enabled")
1791
+ }
718
1792
  ```
719
1793
 
720
- ### Data Structures
1794
+ ---
1795
+
1796
+ ## Threads
721
1797
 
722
1798
  ```nova
723
- // Counter
724
- let c = Counter(0, 1); // value=0, step=1
725
- c.increment(5); // +5
726
- c.decrement(); // -1
727
- c.clamp(0, 100); // enforce bounds
728
- core.print(c.value); // 4
1799
+ func worker() => {
1800
+ give heavyComputation()
1801
+ }
1802
+
1803
+ let t = Thread(worker)
1804
+ t.start()
1805
+ t.send("message")
1806
+ t.on_message(msg => { core.print(msg) })
1807
+ let result = t.join()
1808
+ ```
1809
+
1810
+ | Method / Property | Description |
1811
+ |-------------------|-------------|
1812
+ | `.start()` | Spawn the worker thread |
1813
+ | `.join()` | Block until done; applies scope mutations; returns result |
1814
+ | `.send(value)` | Post message to worker |
1815
+ | `.on_message(fn)` | Handler for messages from worker |
1816
+ | `.result` | Final return value |
1817
+ | `.error` | Error string if worker threw |
1818
+ | `.done` | Boolean: completed |
1819
+
1820
+ Scope snapshot: user-defined variables serialized and sent. Functions serialized via `toString()`. Worker can write back via `thread.set(name, val)` — applied atomically on `.join()`.
1821
+
729
1822
 
730
- // Stack (LIFO)
731
- let s = Stack(1, 2, 3);
732
- s.push(4);
733
- core.print(s.pop()); // 4
734
- core.print(s.peek()); // 3
735
-
736
- // Queue (FIFO)
737
- let q = Queue();
738
- q.enqueue("a"); q.enqueue("b");
739
- core.print(q.dequeue()); // a
740
-
741
- // LinkedList
742
- let ll = LinkedList();
743
- ll.push(1).push(2).push(3);
744
- core.print(ll.size); // 3
745
- core.print(ll.shift()); // 1
746
-
747
- // Timer
748
- let t = Timer();
749
- t.start();
750
- // ... work ...
751
- core.print(t.elapsed); // ms elapsed
752
- t.lap(); // record a lap
753
-
754
- // State Machine
755
- let sm = State("idle");
756
- sm.add("idle", "start", "running")
757
- .add("running", "pause", "paused")
758
- .add("paused", "resume", "running")
759
- .add("running", "stop", "idle");
760
- sm.onEnter("running", s => core.print(f"entered {s}"));
761
- sm.dispatch("start");
762
- core.print(sm.current); // "running"
763
- ```
764
-
765
- ### Reactive Primitives
766
-
767
- ```nova
768
- // Observable — reactive value with subscribers
769
- let count = Observable(0);
770
- count.subscribe((newVal, oldVal) => core.print(f"{oldVal} → {newVal}"));
771
- count.value = 42; // triggers subscriber
772
-
773
- // Derived observable
774
- let doubled = count.pipe(x => x * 2);
775
- count.value = 10; // doubled.value = 20 automatically
776
-
777
- // Signal — simpler reactive primitive
778
- let x = Signal(5);
779
- let y = x.derive(v => v * 3);
780
- core.print(y.value); // 15
781
- x.value = 10;
782
- core.print(y.value); // 30
783
-
784
- x.effect(v => core.print(f"x changed to {v}"));
785
- ```
786
-
787
- ### Utilities
788
-
789
- ```nova
790
- // Validator
791
- let v = Validator()
792
- .required()
793
- .type("number")
794
- .min(0).max(100)
795
- .custom(x => x % 2 == 0, "must be even");
796
-
797
- let result = v.validate(42);
798
- core.print(result.valid); // true
799
- core.print(result.errors); // []
800
-
801
- // Special built-in validators
802
- let emailV = Validator().email();
803
-
804
- // Memo — memoized function
805
- let fib = Memo(n => n <= 1 ? n : fib.call(n-1) + fib.call(n-2));
806
- core.print(fib.call(10)); // 55
807
- core.print(fib.stats.hits); // cache hits
808
-
809
- // Lazy — deferred evaluation
810
- let heavy = Lazy(() => expensiveComputation());
811
- // Not evaluated until:
812
- core.print(heavy.value); // computed once and cached
813
- core.print(heavy.value); // served from cache
814
-
815
- // DataStream — lazy collection transformations
816
- let ds = DataStream(hugeArray)
817
- .filter(x => x.active)
818
- .map(x => x.value)
819
- .distinct()
820
- .sort()
821
- .take(10)
822
- .collect();
823
-
824
- // Transformer — bidirectional value mapping
825
- let t = Transformer().encode(v => v * 2).decode(v => v / 2);
826
- core.print(t.to(5)); // 10
827
- core.print(t.from(10)); // 5
828
-
829
- // Built-in transformers
830
- let json = TransformerJSON();
831
- let b64 = TransformerBase64();
832
-
833
- // Router — pattern dispatcher
834
- let r = Router();
835
- r.on("/users/(.*)", id => handleUser(id));
836
- r.on("/posts/(.*)/(.*)$", (id, action) => handlePost(id, action));
837
- r.default(path => notFound(path));
838
- r.dispatch("/users/42");
839
-
840
- // EventBus — standalone pub/sub (separate from Nova event system)
841
- let bus = EventBus();
842
- bus.on("data", v => process(v));
843
- bus.once("ready", () => init());
844
- bus.emit("data", payload);
845
- bus.off("data", handler);
846
- ```
1823
+ Read also: NVML readme (./kits/kitnovacweb/README.md)