@dramxx/brolang 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,934 @@
1
+ # BroLang
2
+
3
+ A programming language for developers who communicate in modern vernacular. Write code the way you actually talk.
4
+
5
+ **File extension:** `.bro`
6
+ **Runtime:** Node.js
7
+ **Transpiles to:** Vanilla JavaScript
8
+
9
+ ---
10
+
11
+ ## Table of Contents
12
+
13
+ 1. [Installation](#installation)
14
+ 2. [CLI](#cli)
15
+ 3. [Language Reference](#language-reference)
16
+ - [Variables](#variables)
17
+ - [Functions](#functions)
18
+ - [Control Flow](#control-flow)
19
+ - [Loops](#loops)
20
+ - [Error Handling](#error-handling)
21
+ - [Classes](#classes)
22
+ - [Modules](#modules)
23
+ - [Async / Await](#async--await)
24
+ - [Values & Operators](#values--operators)
25
+ 4. [Standard Library](#standard-library)
26
+ - [Console](#console)
27
+ - [Timers](#timers)
28
+ - [Math](#math)
29
+ - [Array Methods](#array-methods)
30
+ - [String Methods](#string-methods)
31
+ - [Object Utilities](#object-utilities)
32
+ - [JSON](#json)
33
+ - [Promises](#promises)
34
+ - [DOM](#dom-browser)
35
+ - [Type Checking](#type-checking)
36
+ 5. [Quick Reference](#quick-reference)
37
+
38
+ ---
39
+
40
+ ## Installation
41
+
42
+ ```sh
43
+ npm install -g brolang
44
+ ```
45
+
46
+ ---
47
+
48
+ ## CLI
49
+
50
+ ```sh
51
+ bro run <file.bro> # Execute a .bro file
52
+ bro build <file.bro> # Transpile to .js
53
+ bro check <file.bro> # Check for syntax errors without running
54
+ bro repl # Start interactive REPL
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Language Reference
60
+
61
+ ### Variables
62
+
63
+ Variables are declared with `lilbro`. There is one declaration keyword — BroLang does not distinguish between mutable and immutable at the declaration site.
64
+
65
+ ```js
66
+ lilbro name = "Chad"
67
+ lilbro count = 0
68
+ lilbro isActive = cool
69
+ lilbro data = shit
70
+ ```
71
+
72
+ ```js
73
+ let name = "Chad";
74
+ let count = 0;
75
+ let isActive = true;
76
+ let data = null;
77
+ ```
78
+
79
+ ---
80
+
81
+ ### Functions
82
+
83
+ Function declarations use `bro`. Functions return values with `fuckoff`.
84
+
85
+ ```js
86
+ bro greet(name) {
87
+ fuckoff "Yo " + name
88
+ }
89
+
90
+ lilbro add = bro(a, b) {
91
+ fuckoff a + b
92
+ }
93
+ ```
94
+
95
+ ```js
96
+ function greet(name) {
97
+ return "Yo " + name;
98
+ }
99
+
100
+ const add = function(a, b) {
101
+ return a + b;
102
+ };
103
+ ```
104
+
105
+ Arrow functions use native `=>` syntax unchanged:
106
+
107
+ ```js
108
+ lilbro double = (n) => n * 2
109
+ lilbro greetAll = (names) => names.cook((n) => greet(n))
110
+ ```
111
+
112
+ ```js
113
+ const double = (n) => n * 2;
114
+ const greetAll = (names) => names.map((n) => greet(n));
115
+ ```
116
+
117
+ ---
118
+
119
+ ### Control Flow
120
+
121
+ #### Conditionals
122
+
123
+ ```js
124
+ sus (score > 90) {
125
+ spam("legend")
126
+ } nah sus (score > 50) {
127
+ spam("mid")
128
+ } nah {
129
+ spam("skill issue")
130
+ }
131
+ ```
132
+
133
+ ```js
134
+ if (score > 90) {
135
+ console.log("legend");
136
+ } else if (score > 50) {
137
+ console.log("mid");
138
+ } else {
139
+ console.log("skill issue");
140
+ }
141
+ ```
142
+
143
+ #### Switch
144
+
145
+ `pickone` evaluates a value against `when` clauses. Use `peace` to exit a case.
146
+
147
+ ```js
148
+ pickone (day) {
149
+ when "Monday":
150
+ spam("ugh")
151
+ peace
152
+ when "Friday":
153
+ spam("let's go")
154
+ peace
155
+ whatever:
156
+ spam("whatever")
157
+ }
158
+ ```
159
+
160
+ ```js
161
+ switch (day) {
162
+ case "Monday":
163
+ console.log("ugh");
164
+ break;
165
+ case "Friday":
166
+ console.log("let's go");
167
+ break;
168
+ default:
169
+ console.log("whatever");
170
+ }
171
+ ```
172
+
173
+ ---
174
+
175
+ ### Loops
176
+
177
+ #### Index loop
178
+
179
+ ```js
180
+ grind (lilbro i = 0; i < 10; i++) {
181
+ spam(i)
182
+ }
183
+ ```
184
+
185
+ ```js
186
+ for (let i = 0; i < 10; i++) {
187
+ console.log(i);
188
+ }
189
+ ```
190
+
191
+ #### Iteration
192
+
193
+ ```js
194
+ vibing (lilbro item of items) {
195
+ spam(item)
196
+ }
197
+ ```
198
+
199
+ ```js
200
+ for (let item of items) {
201
+ console.log(item);
202
+ }
203
+ ```
204
+
205
+ #### Object key enumeration
206
+
207
+ ```js
208
+ nosyping (lilbro key in obj) {
209
+ spam(key, obj[key])
210
+ }
211
+ ```
212
+
213
+ ```js
214
+ for (let key in obj) {
215
+ console.log(key, obj[key]);
216
+ }
217
+ ```
218
+
219
+ #### Condition-based loops
220
+
221
+ ```js
222
+ keepgoing (queue.howmany > 0) {
223
+ process(queue.yeetfirst())
224
+ }
225
+
226
+ doitonce {
227
+ tryThis()
228
+ } keepgoing (failed)
229
+ ```
230
+
231
+ ```js
232
+ while (queue.length > 0) {
233
+ process(queue.shift());
234
+ }
235
+
236
+ do {
237
+ tryThis();
238
+ } while (failed);
239
+ ```
240
+
241
+ Use `skip` to advance to the next iteration. Use `gtfo` to exit the loop entirely.
242
+
243
+ ```js
244
+ vibing (lilbro n of nums) {
245
+ sus (n === 0) { skip }
246
+ sus (n < 0) { gtfo }
247
+ spam(n)
248
+ }
249
+ ```
250
+
251
+ ```js
252
+ for (let n of nums) {
253
+ if (n === 0) { continue; }
254
+ if (n < 0) { break; }
255
+ console.log(n);
256
+ }
257
+ ```
258
+
259
+ ---
260
+
261
+ ### Error Handling
262
+
263
+ ```js
264
+ bro riskyBusiness() {
265
+ yolo {
266
+ lilbro result = doSomethingStupid()
267
+ fuckoff result
268
+ } lol (err) {
269
+ omfg("bruh:", err)
270
+ yeet fresh Error("it broke again")
271
+ } anyway {
272
+ spam("tried my best")
273
+ }
274
+ }
275
+ ```
276
+
277
+ ```js
278
+ function riskyBusiness() {
279
+ try {
280
+ let result = doSomethingStupid();
281
+ return result;
282
+ } catch (err) {
283
+ console.error("bruh:", err);
284
+ throw new Error("it broke again");
285
+ } finally {
286
+ console.log("tried my best");
287
+ }
288
+ }
289
+ ```
290
+
291
+ `yeet` throws any value as an error. When followed by a property access, `yeet` acts as `delete` instead — see [Values & Operators](#values--operators).
292
+
293
+ ---
294
+
295
+ ### Classes
296
+
297
+ ```js
298
+ crew Animal {
299
+ setup (name) {
300
+ me.name = name
301
+ }
302
+
303
+ speak() {
304
+ fuckoff me.name + " says something"
305
+ }
306
+ }
307
+
308
+ crew Dog extends Animal {
309
+ setup (name) {
310
+ super(name)
311
+ }
312
+
313
+ speak() {
314
+ fuckoff me.name + " says woof"
315
+ }
316
+ }
317
+
318
+ lilbro dog = fresh Dog("Rex")
319
+ spam(dog.speak())
320
+ ```
321
+
322
+ ```js
323
+ class Animal {
324
+ constructor(name) {
325
+ this.name = name;
326
+ }
327
+
328
+ speak() {
329
+ return this.name + " says something";
330
+ }
331
+ }
332
+
333
+ class Dog extends Animal {
334
+ constructor(name) {
335
+ super(name);
336
+ }
337
+
338
+ speak() {
339
+ return this.name + " says woof";
340
+ }
341
+ }
342
+
343
+ let dog = new Dog("Rex");
344
+ console.log(dog.speak());
345
+ ```
346
+
347
+ - `crew` defines a class
348
+ - `setup()` is the constructor
349
+ - `me` is the current instance
350
+ - `fresh` instantiates a new object
351
+ - `extends`, `super`, and `static` are unchanged from their standard forms
352
+
353
+ ---
354
+
355
+ ### Modules
356
+
357
+ ```js
358
+ yoink { useState, useEffect } from 'react'
359
+ yoink axios from 'axios'
360
+ yoink * as utils from './utils'
361
+
362
+ shareall bro MyComponent() {
363
+ fuckoff shit
364
+ }
365
+
366
+ share lilbro MAX_RETRIES = 3
367
+ ```
368
+
369
+ ```js
370
+ import { useState, useEffect } from 'react';
371
+ import axios from 'axios';
372
+ import * as utils from './utils';
373
+
374
+ export default function MyComponent() {
375
+ return null;
376
+ }
377
+
378
+ export let MAX_RETRIES = 3;
379
+ ```
380
+
381
+ ---
382
+
383
+ ### Async / Await
384
+
385
+ ```js
386
+ nocap bro fetchData(url) {
387
+ yolo {
388
+ lilbro res = holdmybeer grab(url)
389
+ lilbro data = holdmybeer res.json()
390
+ fuckoff data
391
+ } lol (err) {
392
+ omfg("fetch failed:", err)
393
+ }
394
+ }
395
+
396
+ lilbro getData = nocap bro(id) {
397
+ fuckoff holdmybeer fetchData("/api/" + id)
398
+ }
399
+ ```
400
+
401
+ ```js
402
+ async function fetchData(url) {
403
+ try {
404
+ let res = await fetch(url);
405
+ let data = await res.json();
406
+ return data;
407
+ } catch (err) {
408
+ console.error("fetch failed:", err);
409
+ }
410
+ }
411
+
412
+ const getData = async function(id) {
413
+ return await fetchData("/api/" + id);
414
+ };
415
+ ```
416
+
417
+ - `nocap bro name()` declares an async named function
418
+ - `nocap` alone marks an arrow function as async: `lilbro fn = nocap (x) => doThing(x)`
419
+ - `holdmybeer` suspends execution until the awaited expression resolves
420
+
421
+ ---
422
+
423
+ ### Values & Operators
424
+
425
+ #### Literal values
426
+
427
+ | BroLang | Value |
428
+ |-----------|-------------|
429
+ | `cool` | `true` |
430
+ | `notcool` | `false` |
431
+ | `shit` | `null` |
432
+ | `idk` | `undefined` |
433
+
434
+ ```js
435
+ lilbro flag = cool
436
+ lilbro empty = shit
437
+ lilbro unknown = idk
438
+ lilbro opposite = notcool
439
+ ```
440
+
441
+ ```js
442
+ let flag = true;
443
+ let empty = null;
444
+ let unknown = undefined;
445
+ let opposite = false;
446
+ ```
447
+
448
+ All standard operators work unchanged: `+`, `-`, `*`, `/`, `%`, `===`, `!==`, `&&`, `||`, `??`, `?.`, spread `...`, and so on.
449
+
450
+ #### Delete
451
+
452
+ `yeet` before a property access removes that property:
453
+
454
+ ```js
455
+ yeet obj.secretField
456
+ ```
457
+
458
+ ```js
459
+ delete obj.secretField;
460
+ ```
461
+
462
+ When used with `fresh` (or any non-property expression), `yeet` throws instead:
463
+
464
+ ```js
465
+ yeet fresh Error("unacceptable")
466
+ ```
467
+
468
+ ```js
469
+ throw new Error("unacceptable");
470
+ ```
471
+
472
+ #### Type inspection
473
+
474
+ ```js
475
+ sus (wtf(x) === "number") {
476
+ spam("it's a number")
477
+ }
478
+
479
+ sus (dog ispartof Animal) {
480
+ spam("it's an animal")
481
+ }
482
+ ```
483
+
484
+ ```js
485
+ if (typeof x === "number") {
486
+ console.log("it's a number");
487
+ }
488
+
489
+ if (dog instanceof Animal) {
490
+ console.log("it's an animal");
491
+ }
492
+ ```
493
+
494
+ ---
495
+
496
+ ## Standard Library
497
+
498
+ The BroLang runtime is injected into every `.bro` file automatically. No imports required.
499
+
500
+ ---
501
+
502
+ ### Console
503
+
504
+ | Function | Description |
505
+ |------------------------|------------------------------------------|
506
+ | `spam(...args)` | Log to stdout |
507
+ | `omfg(...args)` | Log to stderr |
508
+ | `omg(...args)` | Log a warning |
509
+ | `spreadshit(data)` | Print tabular data |
510
+ | `debugshit(...args)` | Debug output |
511
+ | `clearshit()` | Clear the console |
512
+ | `timeshit(label)` | Start a named timer |
513
+ | `timeshitdone(label)` | End a timer and print elapsed time |
514
+
515
+ ```js
516
+ spam("hello world")
517
+ omfg("critical failure")
518
+ omg("this seems sketchy")
519
+ spreadshit(users)
520
+ timeshit("load")
521
+ loadData()
522
+ timeshitdone("load")
523
+ ```
524
+
525
+ ```js
526
+ console.log("hello world");
527
+ console.error("critical failure");
528
+ console.warn("this seems sketchy");
529
+ console.table(users);
530
+ console.time("load");
531
+ loadData();
532
+ console.timeEnd("load");
533
+ ```
534
+
535
+ ---
536
+
537
+ ### Timers
538
+
539
+ | Function | Description |
540
+ |----------------------------|-----------------------------------------------|
541
+ | `laterbruh(fn, ms)` | Execute `fn` after `ms` milliseconds |
542
+ | `keepspamming(fn, ms)` | Execute `fn` every `ms` milliseconds |
543
+ | `nevermind(id)` | Cancel a `laterbruh` timer |
544
+ | `shutup(id)` | Cancel a `keepspamming` interval |
545
+
546
+ ```js
547
+ lilbro id = laterbruh(bro() {
548
+ spam("finally")
549
+ }, 3000)
550
+
551
+ lilbro ticker = keepspamming(bro() {
552
+ spam("tick")
553
+ }, 1000)
554
+
555
+ nevermind(id)
556
+ shutup(ticker)
557
+ ```
558
+
559
+ ```js
560
+ let id = setTimeout(function() {
561
+ console.log("finally");
562
+ }, 3000);
563
+
564
+ let ticker = setInterval(function() {
565
+ console.log("tick");
566
+ }, 1000);
567
+
568
+ clearTimeout(id);
569
+ clearInterval(ticker);
570
+ ```
571
+
572
+ ---
573
+
574
+ ### Math
575
+
576
+ | Function | Description |
577
+ |-----------------------------|--------------------------|
578
+ | `dice()` | Random float in [0, 1) |
579
+ | `lowball(n)` | Floor |
580
+ | `highball(n)` | Ceiling |
581
+ | `ballpark(n)` | Round |
582
+ | `bigbro(...n)` | Maximum value |
583
+ | `smolbro(...n)` | Minimum value |
584
+ | `nominus(n)` | Absolute value |
585
+ | `whatroot(n)` | Square root |
586
+ | `tothepower(base, exp)` | Exponentiation |
587
+ | `PI` | 3.14159... |
588
+
589
+ ```js
590
+ lilbro rand = lowball(dice() * 100)
591
+ lilbro hyp = whatroot(tothepower(a, 2) + tothepower(b, 2))
592
+ lilbro capped = smolbro(bigbro(val, 0), 100)
593
+ ```
594
+
595
+ ```js
596
+ let rand = Math.floor(Math.random() * 100);
597
+ let hyp = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
598
+ let capped = Math.min(Math.max(val, 0), 100);
599
+ ```
600
+
601
+ ---
602
+
603
+ ### Array Methods
604
+
605
+ Available on all arrays via prototype extension.
606
+
607
+ | Method | Description |
608
+ |-----------------------------|----------------------------------------|
609
+ | `.cook(fn)` | Transform each element |
610
+ | `.keeponly(fn)` | Filter elements |
611
+ | `.smashdown(fn, init)` | Reduce to a single value |
612
+ | `.vibecheck(fn)` | Iterate without collecting results |
613
+ | `.findone(fn)` | First element matching predicate |
614
+ | `.findindex(fn)` | Index of first match |
615
+ | `.gotthis(val)` | Test element inclusion |
616
+ | `.allfine(fn)` | Test if all elements satisfy predicate |
617
+ | `.anyonegood(fn)` | Test if any element satisfies |
618
+ | `.stuffin(val)` | Append element |
619
+ | `.yeetlast()` | Remove and return last element |
620
+ | `.stufffirst(val)` | Prepend element |
621
+ | `.yeetfirst()` | Remove and return first element |
622
+ | `.glue(sep)` | Join elements into a string |
623
+ | `.cutout(s, e)` | Extract subarray |
624
+ | `.surgery(s, d, ...items)` | In-place insertion/removal |
625
+ | `.sortitout(fn)` | Sort in place |
626
+ | `.flipit()` | Reverse in place |
627
+ | `.flatten()` | Flatten one level of nesting |
628
+ | `.flatmash(fn)` | Map then flatten |
629
+ | `.howmany` | Element count (property, not method) |
630
+
631
+ ```js
632
+ lilbro nums = [1, 2, 3, 4, 5]
633
+
634
+ lilbro result = nums
635
+ .keeponly(bro(n) { fuckoff n % 2 === 0 })
636
+ .cook(bro(n) { fuckoff n * 10 })
637
+ .smashdown(bro(acc, n) { fuckoff acc + n }, 0)
638
+
639
+ spam(result) // 60
640
+ spam(nums.howmany) // 5
641
+ ```
642
+
643
+ ```js
644
+ let nums = [1, 2, 3, 4, 5];
645
+
646
+ let result = nums
647
+ .filter((n) => n % 2 === 0)
648
+ .map((n) => n * 10)
649
+ .reduce((acc, n) => acc + n, 0);
650
+
651
+ console.log(result); // 60
652
+ console.log(nums.length); // 5
653
+ ```
654
+
655
+ ---
656
+
657
+ ### String Methods
658
+
659
+ Available on all strings via prototype extension.
660
+
661
+ | Method | Description |
662
+ |--------------------------|--------------------------------------|
663
+ | `.chop(sep)` | Split into array |
664
+ | `.cleanitup()` | Trim leading and trailing whitespace |
665
+ | `.cleanfront()` | Trim leading whitespace only |
666
+ | `.cleanback()` | Trim trailing whitespace only |
667
+ | `.gotthis(sub)` | Test substring inclusion |
668
+ | `.swapout(old, new)` | Replace first occurrence |
669
+ | `.swapallout(old, new)` | Replace all occurrences |
670
+ | `.smallify()` | Lowercase |
671
+ | `.bigify()` | Uppercase |
672
+ | `.startswith(s)` | Test prefix |
673
+ | `.endswith(s)` | Test suffix |
674
+ | `.pad(n, char)` | Left-pad to target length |
675
+ | `.padback(n, char)` | Right-pad to target length |
676
+ | `.cutout(s, e)` | Substring extraction |
677
+ | `.atpos(i)` | Character at index |
678
+ | `.numcode(i)` | Character code at index |
679
+ | `.howlong` | Character count (property, not method) |
680
+
681
+ ```js
682
+ lilbro str = " Hello World "
683
+ spam(str.cleanitup().smallify()) // "hello world"
684
+ spam(str.cleanitup().chop(" ")) // ["Hello", "World"]
685
+ spam(str.howlong) // 15
686
+ ```
687
+
688
+ ```js
689
+ let str = " Hello World ";
690
+ console.log(str.trim().toLowerCase()); // "hello world"
691
+ console.log(str.trim().split(" ")); // ["Hello", "World"]
692
+ console.log(str.length); // 15
693
+ ```
694
+
695
+ ---
696
+
697
+ ### Object Utilities
698
+
699
+ | Function | Description |
700
+ |-----------------------------|-------------------------------------------|
701
+ | `listkeys(obj)` | Array of own enumerable keys |
702
+ | `listvals(obj)` | Array of own enumerable values |
703
+ | `listpairs(obj)` | Array of `[key, value]` pairs |
704
+ | `mash(target, ...sources)` | Merge sources into target (mutates) |
705
+ | `frozensolid(obj)` | Prevent any modification to object |
706
+ | `sealitup(obj)` | Prevent adding or deleting properties |
707
+ | `freshobj(proto)` | Create new object with given prototype |
708
+
709
+ ```js
710
+ lilbro person = { name: "Chad", age: 42 }
711
+ lilbro extended = mash({}, person, { role: "admin" })
712
+
713
+ vibing (lilbro [key, val] of listpairs(person)) {
714
+ spam(`${key}: ${val}`)
715
+ }
716
+ ```
717
+
718
+ ```js
719
+ let person = { name: "Chad", age: 42 };
720
+ let extended = Object.assign({}, person, { role: "admin" });
721
+
722
+ for (let [key, val] of Object.entries(person)) {
723
+ console.log(`${key}: ${val}`);
724
+ }
725
+ ```
726
+
727
+ ---
728
+
729
+ ### JSON
730
+
731
+ | Function | Description |
732
+ |---------------------------|------------------------------------------|
733
+ | `textify(val)` | Serialize value to JSON string |
734
+ | `textifypretty(val, n)` | Serialize with `n`-space indentation |
735
+ | `untext(str)` | Parse JSON string to value |
736
+
737
+ ```js
738
+ lilbro payload = { name: "BroLang", version: 1 }
739
+ lilbro json = textifypretty(payload, 2)
740
+ spam(json)
741
+
742
+ lilbro parsed = untext(json)
743
+ spam(parsed.name)
744
+ ```
745
+
746
+ ```js
747
+ let payload = { name: "BroLang", version: 1 };
748
+ let json = JSON.stringify(payload, null, 2);
749
+ console.log(json);
750
+
751
+ let parsed = JSON.parse(json);
752
+ console.log(parsed.name);
753
+ ```
754
+
755
+ ---
756
+
757
+ ### Promises
758
+
759
+ `pinkyswear` is the Promise interface. Use `fresh pinkyswear(fn)` to construct a new promise.
760
+
761
+ | Expression | Description |
762
+ |----------------------------------|------------------------------------------|
763
+ | `fresh pinkyswear(fn)` | Create a new promise |
764
+ | `pinkyswear.sorted(val)` | Resolved promise wrapping `val` |
765
+ | `pinkyswear.noped(err)` | Rejected promise with `err` |
766
+ | `pinkyswear.waitforall(arr)` | Resolve when all promises resolve |
767
+ | `pinkyswear.firstfinish(arr)` | Settle when first promise settles |
768
+ | `pinkyswear.nodrama(arr)` | Wait for all, regardless of rejection |
769
+
770
+ Promise chain methods:
771
+
772
+ | Method | Description |
773
+ |---------------------|------------------------------------------|
774
+ | `.thendo(fn)` | Run `fn` on fulfillment |
775
+ | `.otherwise(fn)` | Run `fn` on rejection |
776
+ | `.nomatterwhat(fn)` | Run `fn` on either outcome |
777
+
778
+ ```js
779
+ lilbro p = fresh pinkyswear(bro(win, fail) {
780
+ sus (condition) {
781
+ win("sorted!")
782
+ } nah {
783
+ fail("noped")
784
+ }
785
+ })
786
+
787
+ p
788
+ .thendo(bro(val) { spam(val) })
789
+ .otherwise(bro(err) { omfg(err) })
790
+ .nomatterwhat(bro() { spam("done") })
791
+ ```
792
+
793
+ ```js
794
+ let p = new Promise(function(resolve, reject) {
795
+ if (condition) {
796
+ resolve("sorted!");
797
+ } else {
798
+ reject("noped");
799
+ }
800
+ });
801
+
802
+ p
803
+ .then((val) => console.log(val))
804
+ .catch((err) => console.error(err))
805
+ .finally(() => console.log("done"));
806
+ ```
807
+
808
+ ---
809
+
810
+ ### DOM (Browser)
811
+
812
+ DOM helpers are available in browser environments. In Node.js, query functions return `null` or `[]` stubs.
813
+
814
+ | Function / Property | Description |
815
+ |------------------------------|------------------------------------------|
816
+ | `findthisshit(id)` | Element by ID |
817
+ | `findme(sel)` | First element matching CSS selector |
818
+ | `findallofthem(sel)` | All elements matching CSS selector |
819
+ | `makething(tag)` | Create a new element |
820
+ | `.whenthishappens(ev, fn)` | Attach event listener |
821
+ | `.stoplistening(ev, fn)` | Remove event listener |
822
+ | `.addtodoc(child)` | Append child node |
823
+ | `.kickout(child)` | Remove child node |
824
+ | `.text` | Get/set text content (property) |
825
+ | `.html` | Get/set inner HTML (property) |
826
+ | `.classes` | ClassList reference |
827
+ | `.attr(name, val)` | Set attribute |
828
+ | `.getattr(name)` | Get attribute |
829
+
830
+ ```js
831
+ lilbro btn = findme("#submit")
832
+
833
+ btn.whenthishappens("click", bro(e) {
834
+ lilbro div = makething("div")
835
+ div.text = "Submitted!"
836
+ div.classes.add("success")
837
+ findme("#output").addtodoc(div)
838
+ })
839
+ ```
840
+
841
+ ```js
842
+ let btn = document.querySelector("#submit");
843
+
844
+ btn.addEventListener("click", function(e) {
845
+ let div = document.createElement("div");
846
+ div.textContent = "Submitted!";
847
+ div.classList.add("success");
848
+ document.querySelector("#output").appendChild(div);
849
+ });
850
+ ```
851
+
852
+ ---
853
+
854
+ ### Type Checking
855
+
856
+ | Function | Description |
857
+ |-----------------|----------------------------------------------------|
858
+ | `wtf(x)` | Type of `x` as a string |
859
+ | `x ispartof Y` | `true` if `x` is an instance of `Y` |
860
+ | `isalist(x)` | `true` if `x` is an array |
861
+ | `isnothing(x)` | `true` if `x` is `shit` (`null`) or `idk` (`undefined`) |
862
+ | `exists(x)` | `true` if `x` is neither `shit` nor `idk` |
863
+
864
+ ```js
865
+ sus (isnothing(user)) {
866
+ fuckoff
867
+ }
868
+
869
+ sus (isalist(data)) {
870
+ data.vibecheck(bro(item) {
871
+ sus (wtf(item.id) === "number") {
872
+ spam(item.id)
873
+ }
874
+ })
875
+ }
876
+ ```
877
+
878
+ ```js
879
+ if (user === null || user === undefined) {
880
+ return;
881
+ }
882
+
883
+ if (Array.isArray(data)) {
884
+ data.forEach(function(item) {
885
+ if (typeof item.id === "number") {
886
+ console.log(item.id);
887
+ }
888
+ });
889
+ }
890
+ ```
891
+
892
+ ---
893
+
894
+ ## Quick Reference
895
+
896
+ ```
897
+ lilbro x = 5 Variable declaration
898
+ bro name(args) {} Function declaration
899
+ fuckoff value Return value
900
+ sus(cond) {} nah {} If / else
901
+ nah sus(cond) {} Else if
902
+ pickone(val) {} Switch
903
+ when x: Case
904
+ whatever: Default
905
+ grind(i=0; i<n; i++) {} For loop
906
+ vibing(x of list) {} For...of
907
+ nosyping(k in obj) {} For...in
908
+ keepgoing(cond) {} While
909
+ doitonce {} keepgoing(c) Do...while
910
+ gtfo / peace Break
911
+ skip Continue
912
+ yolo {} lol(e) {} Try / catch
913
+ anyway {} Finally
914
+ yeet value Throw
915
+ yeet obj.prop Delete property
916
+ crew Name {} Class declaration
917
+ setup() {} Constructor
918
+ me Instance reference (this)
919
+ fresh ClassName() Instantiation (new)
920
+ yoink { x } from 'y' Named import
921
+ yoink x from 'y' Default import
922
+ share lilbro x = 5 Named export
923
+ shareall Default export
924
+ nocap bro name() {} Async function
925
+ holdmybeer expr Await expression
926
+ cool / notcool true / false
927
+ shit / idk null / undefined
928
+ wtf(x) typeof x
929
+ x ispartof Y x instanceof Y
930
+ ```
931
+
932
+ ---
933
+
934
+ *BroLang — because someone had to do it.*