@xnoxs/flux-lang 3.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +1089 -0
  3. package/bin/flux.js +1397 -0
  4. package/dist/flux.cjs.js +6664 -0
  5. package/dist/flux.esm.js +6674 -0
  6. package/dist/flux.min.js +263 -0
  7. package/index.d.ts +202 -0
  8. package/index.js +26 -0
  9. package/package.json +77 -0
  10. package/scripts/build.js +76 -0
  11. package/src/bundler.js +216 -0
  12. package/src/checker.js +322 -0
  13. package/src/codegen.js +785 -0
  14. package/src/css-preprocessor.js +399 -0
  15. package/src/formatter.js +140 -0
  16. package/src/jsx.js +480 -0
  17. package/src/lexer.js +518 -0
  18. package/src/linter.js +758 -0
  19. package/src/mangler.js +280 -0
  20. package/src/parser.js +1671 -0
  21. package/src/self/bundler.flux +167 -0
  22. package/src/self/bundler.js +187 -0
  23. package/src/self/checker.flux +249 -0
  24. package/src/self/checker.js +338 -0
  25. package/src/self/codegen.flux +555 -0
  26. package/src/self/codegen.js +784 -0
  27. package/src/self/css-preprocessor.flux +373 -0
  28. package/src/self/css-preprocessor.js +387 -0
  29. package/src/self/formatter.flux +93 -0
  30. package/src/self/formatter.js +114 -0
  31. package/src/self/jsx.flux +430 -0
  32. package/src/self/jsx.js +396 -0
  33. package/src/self/lexer.flux +529 -0
  34. package/src/self/lexer.js +709 -0
  35. package/src/self/lexer.stage2.js +700 -0
  36. package/src/self/linter.flux +515 -0
  37. package/src/self/linter.js +804 -0
  38. package/src/self/mangler.flux +253 -0
  39. package/src/self/mangler.js +348 -0
  40. package/src/self/parser.flux +1146 -0
  41. package/src/self/parser.js +1571 -0
  42. package/src/self/sourcemap.flux +66 -0
  43. package/src/self/sourcemap.js +72 -0
  44. package/src/self/stdlib.flux +356 -0
  45. package/src/self/stdlib.js +396 -0
  46. package/src/self/test-runner.flux +201 -0
  47. package/src/self/test-runner.js +132 -0
  48. package/src/self/transpiler.flux +123 -0
  49. package/src/self/transpiler.js +83 -0
  50. package/src/self/type-checker.flux +821 -0
  51. package/src/self/type-checker.js +1106 -0
  52. package/src/sourcemap.js +82 -0
  53. package/src/stdlib.js +436 -0
  54. package/src/test-runner.js +239 -0
  55. package/src/transpiler.js +172 -0
  56. package/src/type-checker.js +1206 -0
package/README.md ADDED
@@ -0,0 +1,1089 @@
1
+ # Flux Lang v3.1.1 — TypeScript Parity Edition
2
+
3
+ **Flux** adalah bahasa pemrograman modern yang di-transpile ke JavaScript — menggabungkan sintaks bersih ala Python (indentasi berbasis blok), tipe statis ala TypeScript, dan ekspresi kuat ala Rust/Haskell.
4
+
5
+ ```
6
+ source.flux → [Lexer → Parser → TypeChecker → CodeGen] → output.js
7
+ ```
8
+
9
+ ---
10
+
11
+ ## Instalasi & Penggunaan
12
+
13
+ ```bash
14
+ # Jalankan langsung
15
+ node bin/flux.js run app.flux
16
+
17
+ # Compile ke JS
18
+ node bin/flux.js compile app.flux -o dist/app.js
19
+
20
+ # Type check (temukan kesalahan tipe sebelum runtime)
21
+ node bin/flux.js check app.flux
22
+
23
+ # Format kode
24
+ node bin/flux.js fmt app.flux
25
+
26
+ # Jalankan semua test
27
+ node bin/flux.js test tests/
28
+
29
+ # REPL interaktif
30
+ node bin/flux.js repl
31
+
32
+ # Bundle beberapa file
33
+ node bin/flux.js bundle main.flux -o bundle.js
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Sintaks Dasar
39
+
40
+ ### Variabel
41
+
42
+ ```flux
43
+ val x = 42 // immutable (const)
44
+ var count = 0 // mutable (let)
45
+
46
+ // Dengan type annotation
47
+ val name: String = "Flux"
48
+ val age: Int = 3
49
+ val score: Float = 9.5
50
+ val active: Bool = true
51
+ val nothing: Null = null
52
+
53
+ // Tanpa annotation — type diinfer otomatis
54
+ val items = [1, 2, 3] // infer: Array<Int>
55
+ val greeting = "Hello" // infer: String
56
+ ```
57
+
58
+ ### Print & Template Strings
59
+
60
+ ```flux
61
+ print("Hello, World!")
62
+ print("Dua ditambah dua: {2 + 2}")
63
+ print("Pi: {3.14159:.2f}") // format spec
64
+ print("Besar: {n:,}") // separator ribuan
65
+ print("Persen: {rate:.1%}") // format persen
66
+ ```
67
+
68
+ ### Operator
69
+
70
+ ```flux
71
+ // Aritmatika
72
+ val a = 10 + 3 // 13
73
+ val b = 10 - 3 // 7
74
+ val c = 10 * 3 // 30
75
+ val d = 10 / 3 // 3.333...
76
+ val e = 10 % 3 // 1
77
+ val f = 2 ** 10 // 1024
78
+
79
+ // Perbandingan
80
+ val eq = a == b // false
81
+ val neq = a != b // true
82
+ val lt = a < b // false
83
+
84
+ // Logika
85
+ val and_ = true && false // false
86
+ val or_ = true || false // true
87
+ val not_ = !true // false
88
+
89
+ // Nullish coalescing
90
+ val result = maybeNull ?? "default"
91
+
92
+ // Optional chaining
93
+ val len = user?.name?.length
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Fungsi
99
+
100
+ ### Deklarasi Dasar
101
+
102
+ ```flux
103
+ // Inline (satu baris)
104
+ fn double(x) -> x * 2
105
+ fn add(a, b) -> a + b
106
+ fn greet(name) -> "Hello, {name}!"
107
+
108
+ // Block body
109
+ fn factorial(n):
110
+ if n <= 1: return 1
111
+ return n * factorial(n - 1)
112
+ ```
113
+
114
+ ### Type Annotations
115
+
116
+ ```flux
117
+ // Parameter dan return type
118
+ fn greet(name: String) -> String:
119
+ return "Hello, {name}!"
120
+
121
+ fn add(a: Int, b: Int) -> Int:
122
+ return a + b
123
+
124
+ fn area(w: Float, h: Float) -> Float:
125
+ return w * h
126
+
127
+ // Parameter opsional
128
+ fn greetUser(name: String, title?: String) -> String:
129
+ val prefix = title ?? ""
130
+ if prefix != "":
131
+ return "{prefix} {name}"
132
+ return name
133
+ ```
134
+
135
+ ### Sintaks Return Type
136
+
137
+ Gunakan `-> ReturnType:` sebelum badan blok fungsi:
138
+
139
+ ```flux
140
+ // Blok body dengan return type annotation
141
+ fn multiply(a: Int, b: Int) -> Int:
142
+ return a * b
143
+
144
+ // Inline body (tanpa return type — diinfer otomatis)
145
+ fn square(x) -> x * x
146
+
147
+ // Nullable return type
148
+ fn findUser(id: Int) -> String?:
149
+ if id > 0: return "user_{id}"
150
+ return null
151
+ ```
152
+
153
+ ### Tipe Return Khusus
154
+
155
+ ```flux
156
+ // Void — tidak mengembalikan nilai
157
+ fn logMessage(msg: String) -> Void:
158
+ print(msg)
159
+
160
+ // Never — selalu melempar error (tidak pernah selesai)
161
+ fn crash(msg: String) -> Never:
162
+ throw new Error(msg)
163
+ ```
164
+
165
+ ### Parameter Rest & Default
166
+
167
+ ```flux
168
+ // Rest parameter
169
+ fn sum(...nums):
170
+ return nums.reduce((acc, x) -> acc + x, 0)
171
+
172
+ // Default value
173
+ fn repeat(str: String, times: Int = 3) -> String:
174
+ return str.repeat(times)
175
+ ```
176
+
177
+ ### Fungsi Async
178
+
179
+ ```flux
180
+ async fn fetchData(url: String) -> String:
181
+ val res = await fetch(url)
182
+ return await res.text()
183
+
184
+ async fn main():
185
+ val data = await fetchData("https://api.example.com")
186
+ print(data)
187
+ ```
188
+
189
+ ### Higher-Order Functions & Lambda
190
+
191
+ ```flux
192
+ // Lambda ekspresi
193
+ val square = x -> x * x
194
+ val add = (a, b) -> a + b
195
+
196
+ // Fungsi sebagai argumen
197
+ val doubled = [1, 2, 3].map(x -> x * 2) // [2, 4, 6]
198
+ val evens = [1, 2, 3, 4, 5].filter(x -> x % 2 == 0) // [2, 4]
199
+ val total = [1, 2, 3, 4].reduce((acc, x) -> acc + x, 0) // 10
200
+
201
+ // Pipe operator — rantai transformasi
202
+ val result = [1, 2, 3, 4, 5]
203
+ |> filter(x -> x > 2)
204
+ |> map(x -> x * 10)
205
+ |> sum
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Tipe Data (Type System)
211
+
212
+ Flux v3.0.0 memiliki sistem tipe yang diperiksa saat kompilasi (`flux check`). Tipe dihapus pada output JavaScript — sama seperti TypeScript.
213
+
214
+ ### Tipe Primitif
215
+
216
+ | Tipe | Deskripsi | Contoh |
217
+ |-----------|------------------------------|-----------------------|
218
+ | `String` | Teks | `"Hello"`, `'World'` |
219
+ | `Int` | Bilangan bulat | `42`, `-7` |
220
+ | `Float` | Bilangan desimal | `3.14`, `-0.5` |
221
+ | `Number` | Int atau Float | `100`, `3.14` |
222
+ | `Bool` | Benar atau salah | `true`, `false` |
223
+ | `Null` | Nilai null | `null` |
224
+ | `Void` | Tidak mengembalikan nilai | return kosong |
225
+ | `Never` | Tidak pernah selesai | throw / infinite loop |
226
+ | `Any` | Lewati type check | semua tipe |
227
+ | `Unknown` | Perlu narrowing dulu | — |
228
+
229
+ ### Union Types
230
+
231
+ ```flux
232
+ val id: Int | String = 42 // Int atau String
233
+ val value: Int | String = "hello" // juga valid
234
+
235
+ fn stringify(x: Int | String | Bool) -> String:
236
+ return "{x}"
237
+ ```
238
+
239
+ ### Nullable Types
240
+
241
+ ```flux
242
+ // String? adalah singkatan dari String | Null
243
+ val name: String? = null
244
+ val other: String? = "Flux"
245
+
246
+ fn findById(id: Int) -> String?:
247
+ if id > 0: return "item_{id}"
248
+ return null
249
+ ```
250
+
251
+ ### Generic Types
252
+
253
+ ```flux
254
+ val names: Array<String> = ["Andi", "Budi", "Cici"]
255
+ val nums: Array<Int> = [1, 2, 3, 4, 5]
256
+
257
+ fn first(items: Array<String>) -> String?:
258
+ if items.length == 0: return null
259
+ return items[0]
260
+ ```
261
+
262
+ ---
263
+
264
+ ## Interface
265
+
266
+ Interface mendefinisikan kontrak yang harus dipenuhi class. `flux check` melaporkan **error** jika ada anggota yang tidak diimplementasi.
267
+
268
+ ```flux
269
+ interface Printable:
270
+ fn print() -> Void
271
+
272
+ interface Shape:
273
+ name: String
274
+ fn area() -> Float
275
+ fn perimeter() -> Float
276
+
277
+ // Interface dengan inheritance
278
+ interface ColoredShape extends Shape:
279
+ color: String
280
+ fn setColor(c: String) -> Void
281
+
282
+ // Generic interface
283
+ interface Container<T>:
284
+ fn get() -> T
285
+ fn set(value: T) -> Void
286
+ ```
287
+
288
+ ### Class Mengimplementasi Interface
289
+
290
+ ```flux
291
+ class Rectangle implements Shape:
292
+ name: String
293
+ width: Float
294
+ height: Float
295
+
296
+ fn area() -> Float:
297
+ return self.width * self.height
298
+
299
+ fn perimeter() -> Float:
300
+ return 2.0 * (self.width + self.height)
301
+
302
+ class Circle implements Shape:
303
+ name: String
304
+ radius: Float
305
+
306
+ fn area() -> Float:
307
+ return 3.14159 * self.radius * self.radius
308
+
309
+ fn perimeter() -> Float:
310
+ return 2.0 * 3.14159 * self.radius
311
+
312
+ val r = new Rectangle("Persegi", 5.0, 3.0)
313
+ print(r.area()) // 15
314
+ print(r.perimeter()) // 16
315
+ ```
316
+
317
+ ---
318
+
319
+ ## Class
320
+
321
+ ```flux
322
+ class Animal:
323
+ name: String
324
+ sound: String
325
+
326
+ fn speak() -> String:
327
+ return "{self.name} says {self.sound}!"
328
+
329
+ // Inheritance
330
+ class Dog extends Animal:
331
+ breed: String
332
+
333
+ fn info() -> String:
334
+ return "Dog: {self.name} ({self.breed})"
335
+
336
+ val dog = new Dog("Rex", "Woof", "Labrador")
337
+ print(dog.speak()) // Rex says Woof!
338
+ print(dog.info()) // Dog: Rex (Labrador)
339
+ ```
340
+
341
+ ### Access Modifiers
342
+
343
+ ```flux
344
+ class BankAccount:
345
+ private balance: Float
346
+ readonly owner: String
347
+
348
+ fn deposit(amount: Float) -> Void:
349
+ self.balance += amount
350
+
351
+ fn getBalance() -> Float:
352
+ return self.balance
353
+ ```
354
+
355
+ ---
356
+
357
+ ## Algebraic Data Types (ADT)
358
+
359
+ ```flux
360
+ // Definisi
361
+ type Option = Some(value) | None
362
+ type Result = Ok(value) | Err(message)
363
+ type Shape = Circle(radius) | Rectangle(w, h) | Triangle(base, height)
364
+ ```
365
+
366
+ ### Pattern Matching dengan ADT
367
+
368
+ ```flux
369
+ fn safeDivide(a: Float, b: Float) -> Result:
370
+ if b == 0.0: return Err("Division by zero")
371
+ return Ok(a / b)
372
+
373
+ val result = safeDivide(10.0, 2.0)
374
+
375
+ match result:
376
+ when Ok(v) -> print("Result: {v}")
377
+ when Err(e) -> print("Error: {e}")
378
+
379
+ // Dengan guard
380
+ match result:
381
+ when Ok(v) if v > 0 -> print("Positive: {v}")
382
+ when Ok(v) -> print("Non-positive: {v}")
383
+ when Err(e) -> print("Error: {e}")
384
+ ```
385
+
386
+ ---
387
+
388
+ ## Enum
389
+
390
+ ```flux
391
+ enum Direction:
392
+ North = 0
393
+ South = 1
394
+ East = 2
395
+ West = 3
396
+
397
+ enum Status:
398
+ Active
399
+ Inactive
400
+ Pending
401
+
402
+ print(Direction.North) // 0
403
+ print(Status.Active) // 0
404
+ ```
405
+
406
+ ---
407
+
408
+ ## Kontrol Alur
409
+
410
+ ### If / Else
411
+
412
+ ```flux
413
+ if x > 0:
414
+ print("positif")
415
+ else if x < 0:
416
+ print("negatif")
417
+ else:
418
+ print("nol")
419
+ ```
420
+
421
+ ### For / While / Do-While
422
+
423
+ ```flux
424
+ // Iterasi array
425
+ for item in items:
426
+ print(item)
427
+
428
+ // Range
429
+ for i in 0..10:
430
+ print(i)
431
+
432
+ // While
433
+ var n = 10
434
+ while n > 0:
435
+ print(n)
436
+ n -= 1
437
+
438
+ // Do-while
439
+ do:
440
+ print("minimal sekali")
441
+ while false
442
+ ```
443
+
444
+ ### Match (Pattern Matching)
445
+
446
+ ```flux
447
+ match nilai:
448
+ when 0 -> print("nol")
449
+ when 1..9 -> print("satu digit")
450
+ when 10..99 -> print("dua digit")
451
+ when _ -> print("lainnya")
452
+
453
+ // Match dengan guard
454
+ match score:
455
+ when s if s >= 90 -> print("A")
456
+ when s if s >= 80 -> print("B")
457
+ when s if s >= 70 -> print("C")
458
+ when _ -> print("D")
459
+ ```
460
+
461
+ ---
462
+
463
+ ## Error Handling
464
+
465
+ ```flux
466
+ // Try / Catch / Finally
467
+ try:
468
+ val data = JSON.parse(input)
469
+ print(data)
470
+ catch(e):
471
+ print("Error: {e.message}")
472
+ finally:
473
+ print("Selesai")
474
+
475
+ // Throw
476
+ fn validateAge(age: Int) -> Void:
477
+ if age < 0:
478
+ throw new Error("Usia tidak boleh negatif: {age}")
479
+
480
+ // Dengan ADT Result
481
+ type Result = Ok(value) | Err(message)
482
+
483
+ fn parseNumber(s: String) -> Result:
484
+ val n = parseInt(s, 10)
485
+ if isNaN(n): return Err("Bukan angka: {s}")
486
+ return Ok(n)
487
+ ```
488
+
489
+ ---
490
+
491
+ ## Destructuring
492
+
493
+ ```flux
494
+ // Object destructuring
495
+ val person = { name: "Budi", age: 25, city: "Jakarta" }
496
+ val { name, age, city } = person
497
+
498
+ // Dengan alias
499
+ val { name: personName, age: personAge } = person
500
+
501
+ // Array destructuring
502
+ val [first, second, ...rest] = [1, 2, 3, 4, 5]
503
+
504
+ // Dengan default value
505
+ val { title = "Tanpa Judul", content } = post
506
+ ```
507
+
508
+ ---
509
+
510
+ ## Import / Export
511
+
512
+ ```flux
513
+ // Import default
514
+ import express from 'express'
515
+
516
+ // Import named
517
+ import { readFileSync, writeFileSync } from 'fs'
518
+
519
+ // Import namespace
520
+ import * as path from 'path'
521
+
522
+ // Export
523
+ export fn calculateTax(amount: Float, rate: Float) -> Float:
524
+ return amount * rate
525
+
526
+ export default fn main():
527
+ print("Hello from main!")
528
+ ```
529
+
530
+ ---
531
+
532
+ ## Interop dengan npm
533
+
534
+ Flux di-transpile ke JavaScript — semua package npm dapat digunakan langsung:
535
+
536
+ ```flux
537
+ import express from 'express'
538
+ import { readFileSync } from 'fs'
539
+
540
+ val app = express()
541
+
542
+ app.get("/", fn(req, res):
543
+ res.json({ status: "ok", lang: "Flux" })
544
+ )
545
+
546
+ app.listen(3000, fn():
547
+ print("Server Flux berjalan di port 3000")
548
+ )
549
+ ```
550
+
551
+ ---
552
+
553
+ ## JSX (React)
554
+
555
+ ```flux
556
+ // Server-side JSX
557
+ val heading = <h1 class="title">Hello Flux!</h1>
558
+ val card = <div class="card">
559
+ <h2>{title}</h2>
560
+ <p>{description}</p>
561
+ </div>
562
+ ```
563
+
564
+ ---
565
+
566
+ ## CSS Preprocessor
567
+
568
+ ```flux
569
+ val styles = css`
570
+ .button {
571
+ background: var(--primary);
572
+ border-radius: 8px;
573
+ padding: 8px 16px;
574
+
575
+ &:hover {
576
+ opacity: 0.9;
577
+ }
578
+ }
579
+ `
580
+ ```
581
+
582
+ ---
583
+
584
+ ## Perintah CLI
585
+
586
+ | Perintah | Deskripsi |
587
+ |---------------------------------|------------------------------------------------------|
588
+ | `flux init [nama]` | Buat project Flux baru |
589
+ | `flux compile file.flux` | Compile ke JavaScript |
590
+ | `flux compile file.flux -o out` | Compile ke file output tertentu |
591
+ | `flux bundle main.flux` | Bundle semua import menjadi satu file |
592
+ | `flux run file.flux` | Compile dan langsung jalankan |
593
+ | `flux watch file.flux` | Pantau perubahan dan compile otomatis |
594
+ | `flux check file.flux` | **Type check** + analisis statis |
595
+ | `flux lint file.flux` | **Lint** — unused vars, unreachable code, shadowing |
596
+ | `flux fmt file.flux` | Format kode sumber |
597
+ | `flux test [dir]` | Temukan dan jalankan file `*.test.flux` |
598
+ | `flux tokens file.flux` | Tampilkan daftar token dari lexer |
599
+ | `flux ast file.flux` | Tampilkan AST dalam format JSON |
600
+ | `flux repl` | Mode REPL interaktif |
601
+ | `flux version` | Tampilkan versi |
602
+
603
+ ### Flag
604
+
605
+ ```
606
+ --out, -o <file> File output
607
+ --sourcemap, -m Hasilkan source map (.js.map)
608
+ --stdout Cetak output ke terminal
609
+ --no-color Nonaktifkan warna
610
+ ```
611
+
612
+ ---
613
+
614
+ ## Sistem Tipe — TypeScript Parity
615
+
616
+ Flux v3.1.0 memiliki sistem tipe yang setara dengan TypeScript. Berikut fitur lengkapnya:
617
+
618
+ ### Intersection Types (`A & B`)
619
+
620
+ ```flux
621
+ interface Named:
622
+ name: String
623
+
624
+ interface Aged:
625
+ age: Int
626
+
627
+ // Intersection: class harus memenuhi SEMUA interface
628
+ class Person implements Named, Aged:
629
+ name: String
630
+ age: Int
631
+ ```
632
+
633
+ ### Tuple Types
634
+
635
+ ```flux
636
+ fn getCoord() -> [Float, Float]:
637
+ return [3.14, 2.71]
638
+
639
+ fn parseKV(pair: String) -> [String, String]:
640
+ val parts = pair.split("=")
641
+ return [parts[0], parts[1]]
642
+
643
+ val [lat, lng] = getCoord() // destructure tuple
644
+ val [key, val_] = parseKV("a=b")
645
+ ```
646
+
647
+ ### Inline Object Types
648
+
649
+ ```flux
650
+ fn makePoint(x: Float, y: Float) -> { x: Float, y: Float }:
651
+ return { x, y }
652
+
653
+ fn distance(p1: { x: Float, y: Float }, p2: { x: Float, y: Float }) -> Float:
654
+ val dx = p1.x - p2.x
655
+ val dy = p1.y - p2.y
656
+ return Math.sqrt(dx * dx + dy * dy)
657
+ ```
658
+
659
+ ### Structural Typing
660
+
661
+ ```flux
662
+ interface Printable:
663
+ fn toString() -> String
664
+
665
+ // Tidak perlu implements jika shape cocok (duck typing)
666
+ class Config implements Printable:
667
+ key: String
668
+ fn toString() -> String -> "Config({self.key})"
669
+ ```
670
+
671
+ ### Type Narrowing via Control Flow
672
+
673
+ ```flux
674
+ fn getUser(id: Int) -> String?:
675
+ if id > 0: return "User_{id}"
676
+ return null
677
+
678
+ val user = getUser(1)
679
+
680
+ // Flux mengecilkan tipe di dalam blok null-check
681
+ if user != null:
682
+ print(user.toUpperCase()) // OK — user adalah String di sini, bukan String?
683
+
684
+ // typeof narrowing
685
+ fn process(x: Int | String | Bool) -> String:
686
+ if typeof x == "number": return "num: {x}"
687
+ if typeof x == "string": return "str: {x}"
688
+ return "bool: {x}"
689
+ ```
690
+
691
+ ### Function Types
692
+
693
+ ```flux
694
+ // Anotasi tipe fungsi sebagai parameter
695
+ fn mapInts(arr: Array<Int>, f: fn(Int) -> Int) -> Array<Int>:
696
+ return arr.map(f)
697
+
698
+ fn double(x: Int) -> Int:
699
+ return x * 2
700
+
701
+ val results = mapInts([1, 2, 3], double)
702
+
703
+ // Tipe fungsi di interface
704
+ interface Handler:
705
+ fn handle(req: String) -> String
706
+ ```
707
+
708
+ ### `keyof` & `typeof` di Tipe
709
+
710
+ ```flux
711
+ // keyof T — union dari semua key interface
712
+ fn getField(key: keyof Config) -> String:
713
+ return key
714
+
715
+ // typeof x — tipe dari variabel
716
+ val config = new Config("host", "localhost")
717
+ fn cloneConfig(src: typeof config) -> typeof config:
718
+ return new Config(src.key, src.value)
719
+ ```
720
+
721
+ ### Utility Types
722
+
723
+ ```flux
724
+ // Semua utility type TypeScript tersedia:
725
+ fn setPartial(opts: Partial<Config>) -> Void:
726
+ // semua field optional
727
+ pass
728
+
729
+ fn setRequired(opts: Required<Config>) -> Void:
730
+ // semua field wajib
731
+ pass
732
+
733
+ fn makeReadonly(opts: Readonly<Config>) -> Void:
734
+ // tidak bisa diubah
735
+ pass
736
+
737
+ // Record<K, V> — dictionary type
738
+ fn buildMap(keys: Array<String>) -> Record<String, Int>:
739
+ return {}
740
+
741
+ // NonNullable<T> — hapus null dari union
742
+ fn safeGet(x: NonNullable<String?>) -> String:
743
+ return x
744
+
745
+ // ReturnType<T> — extract return type
746
+ // Pick<T, K>, Omit<T, K>, Exclude<T, U>, Extract<T, U>
747
+ // Awaited<T>, Parameters<T>, InstanceType<T>
748
+ ```
749
+
750
+ ### Index Signatures
751
+
752
+ ```flux
753
+ // { [key: String]: T } — map arbitrary keys to type
754
+ fn buildRegistry() -> { [key: String]: String }:
755
+ return { name: "Flux", version: "3.1.0" }
756
+ ```
757
+
758
+ ### Generic Type Parameters di ADT
759
+
760
+ ```flux
761
+ // Type parameter <T> di ADT declarations
762
+ type Result<T> = Ok(value) | Err(message)
763
+ type Option<T> = Some(value) | None
764
+ type Either<L, R> = Left(value) | Right(value)
765
+
766
+ fn safeDivide(a: Float, b: Float) -> Result:
767
+ if b == 0.0: return Err("Division by zero")
768
+ return Ok(a / b)
769
+ ```
770
+
771
+ ---
772
+
773
+ ## `flux check` — Type Checker
774
+
775
+ Perintah `flux check` menjalankan tiga lapisan:
776
+
777
+ 1. **Syntax check** — pastikan kode bisa diparse
778
+ 2. **Val immutability** — cegah reassignment `val`
779
+ 3. **Type checker** — periksa tipe annotations, return types, interface implementations, dan type narrowing
780
+
781
+ ### Contoh Output
782
+
783
+ ```
784
+ $ flux check app.flux
785
+
786
+ ✗ app.flux: 2 type error(s)
787
+
788
+ [TypeError]:12:5 Type 'Int' is not assignable to 'name: String'
789
+ hint: Change the value to match type 'String', or update the annotation
790
+ 12 │ val name: String = 42
791
+ │ ^
792
+
793
+ [TypeError]:28:1 Class 'Cat' does not implement method 'area()' required by interface 'Shape'
794
+ hint: Add 'fn area()' to the class
795
+
796
+ ✗ app.flux — 2 type errors
797
+ Functions: 5 | Classes: 2 | JS output: 47 lines
798
+ ```
799
+
800
+ ### Hal yang Diperiksa
801
+
802
+ | Pemeriksaan | Contoh yang ditangkap |
803
+ |-------------------------------|-----------------------------------------------------------|
804
+ | Mismatch tipe variabel | `val name: String = 42` → error |
805
+ | Return type mismatch | `fn f() -> Int: return "hello"` → error |
806
+ | Interface tidak diimplementasi| class tidak punya method yang diwajibkan interface |
807
+ | Val reassignment | `val x = 1; x = 2` → error |
808
+ | Nullable / union assignability| `val x: Int = null` → error |
809
+
810
+ ---
811
+
812
+ ## `flux lint` — Linter
813
+
814
+ Perintah `flux lint` menjalankan **semua** lapisan pemeriksaan `flux check`, ditambah tiga aturan AST-level yang menangkap masalah logika tanpa melihat tipe:
815
+
816
+ | Lapisan | Aturan | Keparahan |
817
+ |---------|--------|-----------|
818
+ | Syntax | Parse errors | Error (fatal) |
819
+ | Immutability | Reassignment `val` | Error |
820
+ | Type checker | Mismatch tipe, interface | Error |
821
+ | **unused-var** | `val`/`var` dideklarasi tapi tidak pernah dibaca | Warning |
822
+ | **unreachable** | Statement setelah `return`/`throw`/`break` | Error |
823
+ | **shadow-val** | `val x` di scope dalam menutupi `val x` di scope luar | Warning |
824
+ | Format | File tidak terformat | Warning |
825
+ | Style | Baris terlalu panjang (>120 karakter), TODO/FIXME | Info |
826
+
827
+ ### Perbedaan `flux check` vs `flux lint`
828
+
829
+ | | `flux check` | `flux lint` |
830
+ |-|---|---|
831
+ | Type errors | ✓ | ✓ |
832
+ | Immutability | ✓ | ✓ |
833
+ | **Unused variables** | ✗ | ✓ |
834
+ | **Unreachable code** | ✗ | ✓ |
835
+ | **Variable shadowing** | ✗ | ✓ |
836
+ | Format check | ✗ | ✓ |
837
+
838
+ ### Contoh Output
839
+
840
+ ```
841
+ $ flux lint app.flux
842
+
843
+ ⊛ Linting: app.flux
844
+
845
+ [E] unreachable:15:5 Unreachable code after 'return'
846
+ hint: Remove or move the unreachable statement
847
+ 15 │ val dead = 99
848
+ │ ^
849
+
850
+ [W] unused-var:8:5 'temp' is declared but never used
851
+ hint: Prefix with '_' (e.g. '_temp') to silence this warning, or remove the declaration
852
+ 8 │ val temp = calculate()
853
+ │ ^
854
+
855
+ [W] shadow-val:22:9 'x' shadows an outer declaration
856
+ hint: Rename one of the 'x' variables to avoid confusion
857
+ 22 │ val x = 0
858
+ │ ^
859
+
860
+ ──────────────────────────────────────────────────
861
+ ✗ app.flux — 3 issue(s) (45 lines)
862
+ ```
863
+
864
+ ### Cara Menonaktifkan Warning Tertentu
865
+
866
+ Prefiks nama variabel dengan `_` untuk menonaktifkan `unused-var` dan `shadow-val`:
867
+
868
+ ```flux
869
+ val _ignored = sideEffect() // tidak dilaporkan unused
870
+ fn process(_unused, value): // param tidak dilaporkan unused
871
+ return value * 2
872
+ ```
873
+
874
+ ---
875
+
876
+ ## Bug Fixes & Riwayat Perubahan
877
+
878
+ ### v3.1.1 — Perbaikan Bug (Juni 2026)
879
+
880
+ Empat bug ditemukan melalui analisis statis dan dibuktikan dengan test sebelum diperbaiki. Semua perubahan diverifikasi dengan suite test penuh (71 test, 0 gagal).
881
+
882
+ ---
883
+
884
+ #### Bug #1 — `codegen.js`: Wildcard arm pertama menghasilkan `else {` tanpa `if`
885
+
886
+ **Dampak:** `SyntaxError` runtime di JavaScript — kode tidak bisa dijalankan sama sekali.
887
+
888
+ **Penyebab:** `genMatch` di `src/codegen.js` menghasilkan `else {` untuk arm wildcard, tetapi jika wildcard adalah arm *pertama*, tidak ada `if` sebelumnya → JS tidak valid.
889
+
890
+ **Sebelum:**
891
+ ```javascript
892
+ // Jika wildcard adalah arm pertama, menghasilkan:
893
+ else { // ← SyntaxError: tidak ada `if` sebelumnya
894
+ return "matched";
895
+ }
896
+ ```
897
+
898
+ **Sesudah:** Ditambahkan flag `hasOpenIf` — arm wildcard pertama menghasilkan `if (true) {` sehingga JS tetap valid.
899
+
900
+ ```flux
901
+ // Kode Flux ini sekarang berfungsi benar:
902
+ fn classify(x):
903
+ match x:
904
+ when _ -> "default"
905
+ when 1 -> "one"
906
+ ```
907
+
908
+ ---
909
+
910
+ #### Bug #2 — `stdlib.js`: `truncate` menghasilkan string terlalu panjang jika suffix ≥ len
911
+
912
+ **Dampak:** Hasil `truncate` melebihi panjang yang diminta.
913
+
914
+ **Penyebab:** `slice(0, len - suffix.length)` menghasilkan indeks negatif jika `suffix.length >= len`, yang diinterpretasi JavaScript sebagai `slice(0, negatif)` → mengembalikan karakter dari akhir string alih-alih awal.
915
+
916
+ **Sebelum:**
917
+ ```javascript
918
+ return str.slice(0, len - suffix.length) + suffix;
919
+ // truncate("hello world", 3, "......") → hasil > 3 karakter ❌
920
+ ```
921
+
922
+ **Sesudah:** Ditambahkan guard — jika `suffix.length >= len`, kembalikan `suffix.slice(0, len)`:
923
+ ```javascript
924
+ const cut = len - suffix.length;
925
+ if (cut <= 0) return suffix.slice(0, len);
926
+ return str.slice(0, cut) + suffix;
927
+ // truncate("hello world", 3, "......") → "..." (3 karakter) ✓
928
+ ```
929
+
930
+ ---
931
+
932
+ #### Bug #3 — `parser.js`: `(a, b, c)` tanpa `->` diam-diam membuang nilai
933
+
934
+ **Dampak:** Nilai di posisi 1, 2, ... dalam ekspresi kurung banyak diam-diam hilang tanpa pesan error apapun — sangat sulit di-debug.
935
+
936
+ **Penyebab:** Parser membangun `items[]` dari ekspresi berkoma di dalam `(...)`, tetapi jika tidak ada `->` (bukan lambda), ia mengembalikan `items[0]` saja.
937
+
938
+ **Sebelum:**
939
+ ```flux
940
+ val x = (10, 20, 30)
941
+ // Dikompilasi menjadi: const x = 10; ← 20 dan 30 hilang diam-diam
942
+ ```
943
+
944
+ **Sesudah:** Jika `items.length > 1` tanpa `->`, ParseError yang jelas dilempar:
945
+ ```
946
+ ParseError: Unexpected comma in expression — did you mean a lambda?
947
+ Write (p0, p1, p2) -> expr
948
+ ```
949
+
950
+ ---
951
+
952
+ #### Bug #4 — `parser.js`: Auto-index enum tidak direset setelah nilai eksplisit
953
+
954
+ **Dampak:** Member enum setelah nilai eksplisit mendapat indeks yang salah.
955
+
956
+ **Penyebab:** `autoIndex` hanya dimulai dari 0 dan naik +1, tanpa pernah mengambil nilai dari assignment eksplisit `member = N`.
957
+
958
+ **Sebelum:**
959
+ ```flux
960
+ enum Color:
961
+ Red // 0 ✓
962
+ Green = 10 // 10 ✓
963
+ Blue // 2 ❌ (seharusnya 11)
964
+ ```
965
+
966
+ **Sesudah:** Setelah nilai eksplisit `N`, `autoIndex` diatur ke `N` sehingga member berikutnya mendapat `N + 1`:
967
+ ```flux
968
+ enum Color:
969
+ Red // 0 ✓
970
+ Green = 10 // 10 ✓
971
+ Blue // 11 ✓
972
+ ```
973
+
974
+ ---
975
+
976
+ #### Regresi
977
+
978
+ Semua 4 perbaikan diverifikasi tidak merusak test yang ada:
979
+
980
+ | Suite | Test | Hasil |
981
+ |-------|------|-------|
982
+ | `01_basics` — `09_ts_compat` | 53 test | ✅ Semua lulus |
983
+ | `10_bugfixes` (baru) | 18 test | ✅ Semua lulus |
984
+ | `prove_bugs.js` (bukti bug) | 7 test | ✅ Semua lulus |
985
+
986
+ ---
987
+
988
+ ## Arsitektur Transpiler
989
+
990
+ ```
991
+ Flux Source (.flux)
992
+
993
+
994
+ [CSS Preprocessor] css`...` → string template
995
+
996
+
997
+ [JSX Preprocessor] <div>...</div> → createElement(...)
998
+
999
+
1000
+ [Lexer] Source → Token stream
1001
+
1002
+
1003
+ [Parser] Tokens → AST (Abstract Syntax Tree)
1004
+
1005
+ ├──▶ [Val Checker] AST → immutability errors
1006
+
1007
+ ├──▶ [Type Checker] AST → type errors & warnings
1008
+
1009
+
1010
+ [Code Generator] AST → JavaScript output
1011
+
1012
+
1013
+ JavaScript (.js) Siap dijalankan di Node.js / browser
1014
+ ```
1015
+
1016
+ ---
1017
+
1018
+ ## Perbedaan dengan TypeScript
1019
+
1020
+ | Fitur | TypeScript | Flux Lang |
1021
+ |--------------------------|--------------------|---------------------------------|
1022
+ | Sintaks | C-style `{}` | Indentasi (Python-style) |
1023
+ | Type annotations | `x: string` | `x: String` |
1024
+ | Return type | `(): string {}` | `() -> String:` |
1025
+ | Union types | `string \| number` | `String \| Int` |
1026
+ | Nullable | `string \| null` | `String?` |
1027
+ | Pattern matching | Tidak ada | `match / when` |
1028
+ | ADT | Tidak ada | `type Result = Ok(v) \| Err(e)` |
1029
+ | Pipe operator | Tidak ada | `\|>` (F# style) |
1030
+ | Template strings | `` `${x}` `` | `"{x}"` |
1031
+ | Format spec | Tidak ada | `"{n:.2f}"`, `"{n:,}"` |
1032
+ | Val (immutable) | `const` | `val` |
1033
+ | Var (mutable) | `let` | `var` |
1034
+ | npm interop | Ya | Ya (100% kompatibel) |
1035
+ | Transpiler output | `.js` | `.js` |
1036
+
1037
+ ---
1038
+
1039
+ ## Contoh Lengkap — Server HTTP
1040
+
1041
+ ```flux
1042
+ import express from 'express'
1043
+
1044
+ type Result = Ok(data) | Err(message)
1045
+
1046
+ interface User:
1047
+ id: Int
1048
+ name: String
1049
+ email: String
1050
+
1051
+ fn validateEmail(email: String) -> Bool:
1052
+ return email.includes("@")
1053
+
1054
+ fn createUser(id: Int, name: String, email: String) -> Result:
1055
+ if !validateEmail(email):
1056
+ return Err("Email tidak valid: {email}")
1057
+ return Ok({ id, name, email })
1058
+
1059
+ val app = express()
1060
+ app.use(express.json())
1061
+
1062
+ var users = []
1063
+
1064
+ app.post("/users", fn(req, res):
1065
+ val { id, name, email } = req.body
1066
+ val result = createUser(id, name, email)
1067
+
1068
+ match result:
1069
+ when Ok(user):
1070
+ users.push(user)
1071
+ res.json({ success: true, user })
1072
+ when Err(msg):
1073
+ res.status(400).json({ error: msg })
1074
+ )
1075
+
1076
+ app.get("/users", fn(req, res):
1077
+ res.json({ users, count: users.length })
1078
+ )
1079
+
1080
+ app.listen(3000, fn():
1081
+ print("Server Flux berjalan di port 3000")
1082
+ )
1083
+ ```
1084
+
1085
+ ---
1086
+
1087
+ ## Lisensi
1088
+
1089
+ MIT — Flux Lang v3.0.0