@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/bin/bro.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const fs = require('fs')
|
|
6
|
+
const { spawn } = require('child_process')
|
|
7
|
+
const repl = require('repl')
|
|
8
|
+
|
|
9
|
+
const { transpile, substituteKeywords } = require('../src/transpiler')
|
|
10
|
+
const { buildRuntimeContext } = require('../src/runtime')
|
|
11
|
+
const { version } = require('../package.json')
|
|
12
|
+
|
|
13
|
+
// ── ANSI colors ───────────────────────────────────────────────────────────────
|
|
14
|
+
const red = (s) => `\x1b[31m${s}\x1b[0m`
|
|
15
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`
|
|
16
|
+
const cyan = (s) => `\x1b[1;36m${s}\x1b[0m`
|
|
17
|
+
const gray = (s) => `\x1b[90m${s}\x1b[0m`
|
|
18
|
+
|
|
19
|
+
// ── CLI entry ─────────────────────────────────────────────────────────────────
|
|
20
|
+
const [,, cmd, ...args] = process.argv
|
|
21
|
+
|
|
22
|
+
const HELP = `
|
|
23
|
+
BroLang ${version} — JavaScript for people who peaked in 1997
|
|
24
|
+
|
|
25
|
+
Usage: bro <command> [options]
|
|
26
|
+
|
|
27
|
+
Commands:
|
|
28
|
+
run <file> Transpile and execute a .bro file
|
|
29
|
+
build <file> [-o out] Transpile a .bro file to JavaScript
|
|
30
|
+
check <file> Check a .bro file for transpile errors
|
|
31
|
+
repl Start an interactive BroLang REPL
|
|
32
|
+
|
|
33
|
+
Options:
|
|
34
|
+
-v, --version Show version
|
|
35
|
+
-h, --help Show this help
|
|
36
|
+
`.trim()
|
|
37
|
+
|
|
38
|
+
if (!cmd || cmd === '-h' || cmd === '--help') {
|
|
39
|
+
console.log(HELP)
|
|
40
|
+
process.exit(0)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (cmd === '-v' || cmd === '--version') {
|
|
44
|
+
console.log(version)
|
|
45
|
+
process.exit(0)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (cmd === 'run') runFile(args[0])
|
|
49
|
+
else if (cmd === 'build') buildFile(args)
|
|
50
|
+
else if (cmd === 'check') checkFile(args[0])
|
|
51
|
+
else if (cmd === 'repl') startRepl()
|
|
52
|
+
else {
|
|
53
|
+
console.error(red(`bro: unknown command '${cmd}'. Run 'bro --help' for usage.`))
|
|
54
|
+
process.exit(1)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ── helpers ───────────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
function resolveFile(file) {
|
|
60
|
+
if (!file) {
|
|
61
|
+
console.error(red('bro: no file specified'))
|
|
62
|
+
process.exit(1)
|
|
63
|
+
}
|
|
64
|
+
let p = path.resolve(file)
|
|
65
|
+
if (!fs.existsSync(p) && !file.endsWith('.bro')) {
|
|
66
|
+
p = path.resolve(file + '.bro')
|
|
67
|
+
}
|
|
68
|
+
if (!fs.existsSync(p)) {
|
|
69
|
+
console.error(red(`bro: file not found: ${file}`))
|
|
70
|
+
process.exit(1)
|
|
71
|
+
}
|
|
72
|
+
return p
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── commands ──────────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
function runFile(file) {
|
|
78
|
+
const filePath = resolveFile(file)
|
|
79
|
+
const source = fs.readFileSync(filePath, 'utf8')
|
|
80
|
+
|
|
81
|
+
let js
|
|
82
|
+
try {
|
|
83
|
+
js = transpile(source)
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error(red('transpile error:'), err.message)
|
|
86
|
+
process.exit(1)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const hasModuleSyntax = /^(?:import|export)\s/m.test(js)
|
|
90
|
+
const ext = hasModuleSyntax ? '.mjs' : '.js'
|
|
91
|
+
const tmp = path.join(path.dirname(filePath), `__bro_${process.pid}_${Date.now()}${ext}`)
|
|
92
|
+
|
|
93
|
+
fs.writeFileSync(tmp, js, 'utf8')
|
|
94
|
+
|
|
95
|
+
const child = spawn(process.execPath, [tmp], { stdio: 'inherit' })
|
|
96
|
+
child.on('exit', (code, signal) => {
|
|
97
|
+
fs.unlink(tmp, () => {})
|
|
98
|
+
if (signal) process.kill(process.pid, signal)
|
|
99
|
+
else process.exit(code ?? 0)
|
|
100
|
+
})
|
|
101
|
+
child.on('error', (err) => {
|
|
102
|
+
console.error(red('run error:'), err.message)
|
|
103
|
+
fs.unlink(tmp, () => {})
|
|
104
|
+
process.exit(1)
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildFile(args) {
|
|
109
|
+
let file = null
|
|
110
|
+
let out = null
|
|
111
|
+
for (let i = 0; i < args.length; i++) {
|
|
112
|
+
if ((args[i] === '-o' || args[i] === '--out') && args[i + 1]) {
|
|
113
|
+
out = args[++i]
|
|
114
|
+
} else if (!file) {
|
|
115
|
+
file = args[i]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const filePath = resolveFile(file)
|
|
120
|
+
const source = fs.readFileSync(filePath, 'utf8')
|
|
121
|
+
|
|
122
|
+
let js
|
|
123
|
+
try {
|
|
124
|
+
js = transpile(source)
|
|
125
|
+
} catch (err) {
|
|
126
|
+
console.error(red('transpile error:'), err.message)
|
|
127
|
+
process.exit(1)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const outPath = out ? path.resolve(out) : filePath.replace(/\.bro$/, '.js')
|
|
131
|
+
fs.writeFileSync(outPath, js, 'utf8')
|
|
132
|
+
console.log(green('built:'), outPath)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function checkFile(file) {
|
|
136
|
+
const filePath = resolveFile(file)
|
|
137
|
+
const source = fs.readFileSync(filePath, 'utf8')
|
|
138
|
+
try {
|
|
139
|
+
transpile(source)
|
|
140
|
+
console.log(green(`✓ ${file} — looks good, bro`))
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.error(red(`✗ ${file} — ${err.message}`))
|
|
143
|
+
process.exit(1)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function startRepl() {
|
|
148
|
+
console.log(cyan('BroLang REPL') + gray(' (.exit to quit)\n'))
|
|
149
|
+
|
|
150
|
+
const rtFns = buildRuntimeContext()
|
|
151
|
+
|
|
152
|
+
const r = repl.start({
|
|
153
|
+
prompt: cyan('bro> '),
|
|
154
|
+
useGlobal: true,
|
|
155
|
+
ignoreUndefined: true,
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
Object.assign(r.context, rtFns)
|
|
159
|
+
|
|
160
|
+
const origEval = r.eval
|
|
161
|
+
r.eval = function broEval(input, ctx, filename, cb) {
|
|
162
|
+
origEval.call(r, substituteKeywords(input), ctx, filename, cb)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// classes.bro — OOP but make it bro
|
|
2
|
+
|
|
3
|
+
crew Animal {
|
|
4
|
+
setup(name, sound) {
|
|
5
|
+
me.name = name
|
|
6
|
+
me.sound = sound
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
speak() {
|
|
10
|
+
fuckoff me.name + " says " + me.sound
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static describe() {
|
|
14
|
+
fuckoff "Animals are cool bro"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
crew Dog extends Animal {
|
|
19
|
+
setup(name) {
|
|
20
|
+
super(name, "woof")
|
|
21
|
+
me.tricks = []
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
learn(trick) {
|
|
25
|
+
me.tricks.stuffin(trick)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
showoff() {
|
|
29
|
+
sus (me.tricks.howmany === 0) {
|
|
30
|
+
spam(me.name + " doesn't know any tricks yet, sad")
|
|
31
|
+
fuckoff
|
|
32
|
+
}
|
|
33
|
+
spam(me.name + " knows: " + me.tricks.glue(", "))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
lilbro dog = fresh Dog("Rex")
|
|
38
|
+
spam(dog.speak())
|
|
39
|
+
dog.learn("sit")
|
|
40
|
+
dog.learn("roll over")
|
|
41
|
+
dog.learn("vibe")
|
|
42
|
+
dog.showoff()
|
|
43
|
+
spam(Animal.describe())
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// hello.bro — the classic first program, but make it bro
|
|
2
|
+
|
|
3
|
+
bro greet(name) {
|
|
4
|
+
fuckoff "Yo, " + name + "! Welcome to BroLang."
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
lilbro name = "Chad"
|
|
8
|
+
spam(greet(name))
|
|
9
|
+
|
|
10
|
+
// Arrays
|
|
11
|
+
lilbro nums = [1, 2, 3, 4, 5]
|
|
12
|
+
lilbro doubled = nums.cook(bro(n) { fuckoff n * 2 })
|
|
13
|
+
lilbro evens = nums.keeponly(bro(n) { fuckoff n % 2 === 0 })
|
|
14
|
+
lilbro total = nums.smashdown(bro(acc, n) { fuckoff acc + n }, 0)
|
|
15
|
+
|
|
16
|
+
spam("doubled:", doubled)
|
|
17
|
+
spam("evens:", evens)
|
|
18
|
+
spam("total:", total)
|
|
19
|
+
spam("count:", nums.howmany)
|
|
20
|
+
|
|
21
|
+
// Control flow
|
|
22
|
+
sus (total > 10) {
|
|
23
|
+
spam("big number energy")
|
|
24
|
+
} nah sus (total > 5) {
|
|
25
|
+
spam("mid number energy")
|
|
26
|
+
} nah {
|
|
27
|
+
spam("smol number energy")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Loop
|
|
31
|
+
grind(lilbro i = 0; i < 3; i++) {
|
|
32
|
+
spam("lap", i)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Error handling
|
|
36
|
+
bro riskyBusiness() {
|
|
37
|
+
yolo {
|
|
38
|
+
yeet fresh Error("something broke lol")
|
|
39
|
+
} lol(err) {
|
|
40
|
+
omfg("caught:", err.message)
|
|
41
|
+
} anyway {
|
|
42
|
+
spam("tried my best")
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
riskyBusiness()
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dramxx/brolang",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "JavaScript transpiler for people who peaked in 1997",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "dmaxx",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"brolang",
|
|
9
|
+
"transpiler",
|
|
10
|
+
"javascript",
|
|
11
|
+
"language",
|
|
12
|
+
"cli",
|
|
13
|
+
"bro"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=14.0.0"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/dramxx/brolang.git"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/dramxx/brolang#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/dramxx/brolang/issues"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin",
|
|
28
|
+
"src",
|
|
29
|
+
"examples",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"bin": {
|
|
33
|
+
"bro": "./bin/bro.js"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"test": "node test/transpiler.test.js"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/keywords.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Multi-word patterns — checked FIRST, only horizontal whitespace is skipped
|
|
4
|
+
// between parts (no newline-crossing to avoid surprises).
|
|
5
|
+
// Format: [broLangPattern, jsEquivalent]
|
|
6
|
+
const MULTI_WORD_MAP = [
|
|
7
|
+
['nocap bro', 'async function'],
|
|
8
|
+
['nah sus', 'else if'],
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
// Single-word keyword substitutions.
|
|
12
|
+
// With word-boundary matching, ordering only matters to break ties on prefix
|
|
13
|
+
// overlaps — there are none here, so order is just for readability.
|
|
14
|
+
const KEYWORD_MAP = [
|
|
15
|
+
// Declarations
|
|
16
|
+
['lilbro', 'let'],
|
|
17
|
+
['crew', 'class'],
|
|
18
|
+
['setup', 'constructor'],
|
|
19
|
+
['fresh', 'new'],
|
|
20
|
+
|
|
21
|
+
// Functions / async
|
|
22
|
+
['bro', 'function'],
|
|
23
|
+
['nocap', 'async'],
|
|
24
|
+
['holdmybeer', 'await'],
|
|
25
|
+
['fuckoff', 'return'],
|
|
26
|
+
|
|
27
|
+
// Control flow
|
|
28
|
+
['sus', 'if'],
|
|
29
|
+
['nah', 'else'],
|
|
30
|
+
['pickone', 'switch'],
|
|
31
|
+
['when', 'case'],
|
|
32
|
+
['whatever', 'default'],
|
|
33
|
+
|
|
34
|
+
// Loops
|
|
35
|
+
['nosyping', 'for'],
|
|
36
|
+
['vibing', 'for'],
|
|
37
|
+
['grind', 'for'],
|
|
38
|
+
['keepgoing', 'while'],
|
|
39
|
+
['doitonce', 'do'],
|
|
40
|
+
['gtfo', 'break'],
|
|
41
|
+
['peace', 'break'],
|
|
42
|
+
['skip', 'continue'],
|
|
43
|
+
|
|
44
|
+
// Error handling
|
|
45
|
+
['yolo', 'try'],
|
|
46
|
+
['lol', 'catch'],
|
|
47
|
+
['anyway', 'finally'],
|
|
48
|
+
// 'yeet' is handled separately (throw vs delete heuristic)
|
|
49
|
+
|
|
50
|
+
// Modules
|
|
51
|
+
['shareall', 'export default'],
|
|
52
|
+
['yoink', 'import'],
|
|
53
|
+
['share', 'export'],
|
|
54
|
+
|
|
55
|
+
// Operators / misc
|
|
56
|
+
['ispartof', 'instanceof'],
|
|
57
|
+
['wtf', 'typeof'],
|
|
58
|
+
['me', 'this'],
|
|
59
|
+
|
|
60
|
+
// Literals
|
|
61
|
+
['cool', 'true'],
|
|
62
|
+
['notcool', 'false'],
|
|
63
|
+
['shit', 'null'],
|
|
64
|
+
['idk', 'undefined'],
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
module.exports = { KEYWORD_MAP, MULTI_WORD_MAP }
|
package/src/runtime.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// The runtime is injected as a plain JS block at the top of every transpiled file.
|
|
4
|
+
// It's self-contained (no require/import) so the output works standalone.
|
|
5
|
+
const RUNTIME_CODE = `
|
|
6
|
+
/* ── BroLang Runtime ── */
|
|
7
|
+
const spam = (...a) => console.log(...a)
|
|
8
|
+
const omfg = (...a) => console.error(...a)
|
|
9
|
+
const omg = (...a) => console.warn(...a)
|
|
10
|
+
const spreadshit = (...a) => console.table(...a)
|
|
11
|
+
const debugshit = (...a) => console.debug(...a)
|
|
12
|
+
const clearshit = () => console.clear()
|
|
13
|
+
const timeshit = (l) => console.time(l)
|
|
14
|
+
const timeshitdone = (l) => console.timeEnd(l)
|
|
15
|
+
|
|
16
|
+
const laterbruh = (fn, ms) => setTimeout(fn, ms)
|
|
17
|
+
const keepspamming = (fn, ms) => setInterval(fn, ms)
|
|
18
|
+
const nevermind = (id) => clearTimeout(id)
|
|
19
|
+
const shutup = (id) => clearInterval(id)
|
|
20
|
+
|
|
21
|
+
const dice = () => Math.random()
|
|
22
|
+
const lowball = (n) => Math.floor(n)
|
|
23
|
+
const highball = (n) => Math.ceil(n)
|
|
24
|
+
const ballpark = (n) => Math.round(n)
|
|
25
|
+
const bigbro = (...a) => Math.max(...a)
|
|
26
|
+
const smolbro = (...a) => Math.min(...a)
|
|
27
|
+
const nominus = (n) => Math.abs(n)
|
|
28
|
+
const whatroot = (n) => Math.sqrt(n)
|
|
29
|
+
const tothepower = (b, e) => Math.pow(b, e)
|
|
30
|
+
const PI = Math.PI
|
|
31
|
+
|
|
32
|
+
const listkeys = (o) => Object.keys(o)
|
|
33
|
+
const listvals = (o) => Object.values(o)
|
|
34
|
+
const listpairs = (o) => Object.entries(o)
|
|
35
|
+
const mash = (t, ...s) => Object.assign(t, ...s)
|
|
36
|
+
const frozensolid = (o) => Object.freeze(o)
|
|
37
|
+
const sealitup = (o) => Object.seal(o)
|
|
38
|
+
|
|
39
|
+
const textify = (v) => JSON.stringify(v)
|
|
40
|
+
const textifypretty = (v, n=2) => JSON.stringify(v, null, n)
|
|
41
|
+
const untext = (s) => JSON.parse(s)
|
|
42
|
+
|
|
43
|
+
const isalist = (x) => Array.isArray(x)
|
|
44
|
+
const isnothing = (x) => x === null || x === undefined
|
|
45
|
+
const exists = (x) => x !== null && x !== undefined
|
|
46
|
+
const freshobj = (proto) => Object.create(proto)
|
|
47
|
+
|
|
48
|
+
const grab = (...a) => fetch(...a)
|
|
49
|
+
|
|
50
|
+
const pinkyswear = Object.assign(
|
|
51
|
+
function(fn) { return new Promise(fn) },
|
|
52
|
+
{
|
|
53
|
+
sorted: (v) => Promise.resolve(v),
|
|
54
|
+
noped: (e) => Promise.reject(e),
|
|
55
|
+
waitforall: (a) => Promise.all(a),
|
|
56
|
+
firstfinish: (a) => Promise.race(a),
|
|
57
|
+
nodrama: (a) => Promise.allSettled(a),
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
// DOM helpers (no-op stubs in Node; real functions in browser)
|
|
62
|
+
const findthisshit = typeof document !== 'undefined' ? (id) => document.getElementById(id) : () => null
|
|
63
|
+
const findme = typeof document !== 'undefined' ? (sel) => document.querySelector(sel) : () => null
|
|
64
|
+
const findallofthem = typeof document !== 'undefined' ? (sel) => document.querySelectorAll(sel): () => []
|
|
65
|
+
const makething = typeof document !== 'undefined' ? (tag) => document.createElement(tag) : () => null
|
|
66
|
+
|
|
67
|
+
// Array prototype extensions
|
|
68
|
+
Array.prototype.cook = Array.prototype.map
|
|
69
|
+
Array.prototype.keeponly = Array.prototype.filter
|
|
70
|
+
Array.prototype.smashdown = Array.prototype.reduce
|
|
71
|
+
Array.prototype.vibecheck = Array.prototype.forEach
|
|
72
|
+
Array.prototype.findone = Array.prototype.find
|
|
73
|
+
Array.prototype.findindex = Array.prototype.findIndex
|
|
74
|
+
Array.prototype.gotthis = Array.prototype.includes
|
|
75
|
+
Array.prototype.allfine = Array.prototype.every
|
|
76
|
+
Array.prototype.anyonegood = Array.prototype.some
|
|
77
|
+
Array.prototype.stuffin = Array.prototype.push
|
|
78
|
+
Array.prototype.yeetlast = Array.prototype.pop
|
|
79
|
+
Array.prototype.stufffirst = Array.prototype.unshift
|
|
80
|
+
Array.prototype.yeetfirst = Array.prototype.shift
|
|
81
|
+
Array.prototype.glue = Array.prototype.join
|
|
82
|
+
Array.prototype.cutout = Array.prototype.slice
|
|
83
|
+
Array.prototype.surgery = Array.prototype.splice
|
|
84
|
+
Array.prototype.sortitout = Array.prototype.sort
|
|
85
|
+
Array.prototype.flipit = Array.prototype.reverse
|
|
86
|
+
Array.prototype.flatten = Array.prototype.flat
|
|
87
|
+
Array.prototype.flatmash = Array.prototype.flatMap
|
|
88
|
+
Object.defineProperty(Array.prototype, 'howmany', {
|
|
89
|
+
get() { return this.length },
|
|
90
|
+
configurable: true,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// String prototype extensions
|
|
94
|
+
String.prototype.cutout = String.prototype.slice
|
|
95
|
+
String.prototype.chop = String.prototype.split
|
|
96
|
+
String.prototype.cleanitup = String.prototype.trim
|
|
97
|
+
String.prototype.cleanfront = String.prototype.trimStart
|
|
98
|
+
String.prototype.cleanback = String.prototype.trimEnd
|
|
99
|
+
String.prototype.gotthis = String.prototype.includes
|
|
100
|
+
String.prototype.swapout = String.prototype.replace
|
|
101
|
+
String.prototype.swapallout = String.prototype.replaceAll
|
|
102
|
+
String.prototype.smallify = String.prototype.toLowerCase
|
|
103
|
+
String.prototype.bigify = String.prototype.toUpperCase
|
|
104
|
+
String.prototype.startswith = String.prototype.startsWith
|
|
105
|
+
String.prototype.endswith = String.prototype.endsWith
|
|
106
|
+
String.prototype.pad = String.prototype.padStart
|
|
107
|
+
String.prototype.padback = String.prototype.padEnd
|
|
108
|
+
String.prototype.atpos = String.prototype.charAt
|
|
109
|
+
String.prototype.numcode = String.prototype.charCodeAt
|
|
110
|
+
Object.defineProperty(String.prototype, 'howlong', {
|
|
111
|
+
get() { return this.length },
|
|
112
|
+
configurable: true,
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
// Promise prototype extensions
|
|
116
|
+
Promise.prototype.thendo = Promise.prototype.then
|
|
117
|
+
Promise.prototype.otherwise = Promise.prototype.catch
|
|
118
|
+
Promise.prototype.nomatterwhat = Promise.prototype.finally
|
|
119
|
+
|
|
120
|
+
// DOM prototype extensions (browser only)
|
|
121
|
+
if (typeof EventTarget !== 'undefined') {
|
|
122
|
+
EventTarget.prototype.whenthishappens = EventTarget.prototype.addEventListener
|
|
123
|
+
EventTarget.prototype.stoplistening = EventTarget.prototype.removeEventListener
|
|
124
|
+
}
|
|
125
|
+
if (typeof Node !== 'undefined') {
|
|
126
|
+
Node.prototype.addtodoc = Node.prototype.appendChild
|
|
127
|
+
Node.prototype.kickout = Node.prototype.removeChild
|
|
128
|
+
}
|
|
129
|
+
if (typeof Element !== 'undefined') {
|
|
130
|
+
Element.prototype.attr = Element.prototype.setAttribute
|
|
131
|
+
Element.prototype.getattr = Element.prototype.getAttribute
|
|
132
|
+
Object.defineProperty(Element.prototype, 'text', {
|
|
133
|
+
get() { return this.textContent },
|
|
134
|
+
set(v) { this.textContent = v },
|
|
135
|
+
configurable: true,
|
|
136
|
+
})
|
|
137
|
+
Object.defineProperty(Element.prototype, 'html', {
|
|
138
|
+
get() { return this.innerHTML },
|
|
139
|
+
set(v) { this.innerHTML = v },
|
|
140
|
+
configurable: true,
|
|
141
|
+
})
|
|
142
|
+
Object.defineProperty(Element.prototype, 'classes', {
|
|
143
|
+
get() { return this.classList },
|
|
144
|
+
configurable: true,
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
/* ── End BroLang Runtime ── */
|
|
148
|
+
`
|
|
149
|
+
|
|
150
|
+
function getRuntimeCode() {
|
|
151
|
+
return RUNTIME_CODE
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Runtime function object for REPL injection (evaluated fresh each call)
|
|
155
|
+
function buildRuntimeContext() {
|
|
156
|
+
const ctx = {}
|
|
157
|
+
// Evaluate the runtime block to capture var-declared names
|
|
158
|
+
// We rebuild it using Function so const → visible in returned ctx
|
|
159
|
+
const lines = RUNTIME_CODE
|
|
160
|
+
.split('\n')
|
|
161
|
+
.filter(l => /^\s*const\s+/.test(l))
|
|
162
|
+
.map(l => l.trim())
|
|
163
|
+
const names = lines.map(l => l.match(/^const\s+(\w+)/)[1])
|
|
164
|
+
const body = RUNTIME_CODE + '\nreturn {' + names.join(',') + '}'
|
|
165
|
+
// eslint-disable-next-line no-new-func
|
|
166
|
+
return new Function(body)()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = { getRuntimeCode, buildRuntimeContext }
|