@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/LICENSE +21 -0
- package/README.md +934 -0
- package/bin/bro.js +164 -0
- package/examples/classes.bro +43 -0
- package/examples/hello.bro +46 -0
- package/examples/promise.bro +6 -0
- package/package.json +38 -0
- package/src/keywords.js +67 -0
- package/src/runtime.js +169 -0
- package/src/transpiler.js +261 -0
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.*
|