rip-lang 3.3.0 → 3.4.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 +1 -1
- package/bin/rip +29 -2
- package/docs/dist/rip.browser.js +163 -12
- package/docs/dist/rip.browser.min.js +123 -120
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/index.html +8 -9
- package/package.json +4 -3
- package/src/compiler.js +74 -3
- package/src/grammar/solar.rip +4 -9
- package/src/parser.js +6 -7
- package/src/sourcemap.js +121 -0
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ dog = Dog.new("Buddy") # Ruby-style constructor
|
|
|
82
82
|
```coffee
|
|
83
83
|
"Hello, #{name}!" # CoffeeScript-style
|
|
84
84
|
"Hello, ${name}!" # JavaScript-style
|
|
85
|
-
"#{a} + #{b} = #{a + b}"
|
|
85
|
+
"#{a} + #{b} = #{a + b}" # Expressions work in both
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
Both `#{}` and `${}` compile to JavaScript template literals. Use whichever you prefer.
|
package/bin/rip
CHANGED
|
@@ -26,6 +26,7 @@ Usage:
|
|
|
26
26
|
Options:
|
|
27
27
|
-c, --compile Show compiled JavaScript output
|
|
28
28
|
-d, --dts Generate .d.ts type declaration file
|
|
29
|
+
-m, --map Generate .js.map source map file
|
|
29
30
|
-h, --help Show this help message
|
|
30
31
|
-o, --output <file> Write JavaScript to file
|
|
31
32
|
-q, --quiet Suppress headers
|
|
@@ -46,6 +47,7 @@ Examples:
|
|
|
46
47
|
rip -s -c example.rip # Show s-expressions AND JavaScript
|
|
47
48
|
rip -s -t -c example.rip # Show everything (full debug mode)
|
|
48
49
|
rip -d example.rip # Generate example.d.ts
|
|
50
|
+
rip -m example.rip # Generate example.js.map
|
|
49
51
|
rip -cd example.rip # Compile JS and generate .d.ts
|
|
50
52
|
rip -q -c example.rip # Just the JS, no headers (for piping)
|
|
51
53
|
rip -w # Launch browser REPL (auto-opens)
|
|
@@ -146,13 +148,16 @@ async function main() {
|
|
|
146
148
|
const showSExpr = ripOptions.includes('-s') || ripOptions.includes('--sexpr');
|
|
147
149
|
const showCompiled = ripOptions.includes('-c') || ripOptions.includes('--compile');
|
|
148
150
|
const generateDts = ripOptions.includes('-d') || ripOptions.includes('--dts');
|
|
151
|
+
const generateMap = ripOptions.includes('-m') || ripOptions.includes('--map');
|
|
149
152
|
const quiet = ripOptions.includes('-q') || ripOptions.includes('--quiet');
|
|
150
153
|
|
|
151
154
|
const options = {
|
|
152
155
|
showTokens,
|
|
153
156
|
showSExpr,
|
|
154
157
|
quiet,
|
|
155
|
-
types: generateDts ? 'emit' : undefined
|
|
158
|
+
types: generateDts ? 'emit' : undefined,
|
|
159
|
+
sourceMap: generateMap ? true : undefined,
|
|
160
|
+
filename: null, // set below after determining input file
|
|
156
161
|
};
|
|
157
162
|
|
|
158
163
|
// Find input file and output file from ripOptions only
|
|
@@ -167,7 +172,7 @@ async function main() {
|
|
|
167
172
|
}
|
|
168
173
|
|
|
169
174
|
// If .rip file without compile flags → execute instead of compile
|
|
170
|
-
const hasCompileFlag = showCompiled || showTokens || showSExpr || generateDts || outputFile;
|
|
175
|
+
const hasCompileFlag = showCompiled || showTokens || showSExpr || generateDts || generateMap || outputFile;
|
|
171
176
|
if (inputFile && inputFile.endsWith('.rip') && !hasCompileFlag) {
|
|
172
177
|
// Check if file exists
|
|
173
178
|
if (!existsSync(inputFile)) {
|
|
@@ -238,6 +243,11 @@ async function main() {
|
|
|
238
243
|
source = readFileSync(inputFile, 'utf-8');
|
|
239
244
|
}
|
|
240
245
|
|
|
246
|
+
// Set filename for source map generation
|
|
247
|
+
if (inputFile) {
|
|
248
|
+
options.filename = inputFile.replace(/\.rip$/, '');
|
|
249
|
+
}
|
|
250
|
+
|
|
241
251
|
// Compile
|
|
242
252
|
const compiler = new Compiler(options);
|
|
243
253
|
const result = compiler.compile(source);
|
|
@@ -277,6 +287,23 @@ async function main() {
|
|
|
277
287
|
console.log(result.dts);
|
|
278
288
|
}
|
|
279
289
|
}
|
|
290
|
+
|
|
291
|
+
// Write .js.map file
|
|
292
|
+
if (generateMap && result.map) {
|
|
293
|
+
if (inputFile) {
|
|
294
|
+
let mapFile = inputFile.replace(/\.rip$/, '.js.map');
|
|
295
|
+
writeFileSync(mapFile, result.map, 'utf-8');
|
|
296
|
+
if (!options.quiet) {
|
|
297
|
+
console.log(`Generated ${mapFile}`);
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
// stdin — print source map to stdout
|
|
301
|
+
if (!options.quiet) {
|
|
302
|
+
console.log(`// == Source map == //\n`);
|
|
303
|
+
}
|
|
304
|
+
console.log(result.map);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
280
307
|
} catch (error) {
|
|
281
308
|
console.error('Compilation Error:', error.message);
|
|
282
309
|
if (error.stack) {
|
package/docs/dist/rip.browser.js
CHANGED
|
@@ -2966,7 +2966,7 @@ var parserInstance = {
|
|
|
2966
2966
|
return this.trace(str);
|
|
2967
2967
|
else {
|
|
2968
2968
|
line = (hash.line || 0) + 1;
|
|
2969
|
-
col = hash.loc?.
|
|
2969
|
+
col = hash.loc?.c || 0;
|
|
2970
2970
|
token = hash.token ? ` (token: ${hash.token})` : "";
|
|
2971
2971
|
text = hash.text ? ` near '${hash.text}'` : "";
|
|
2972
2972
|
location = `line ${line}, column ${col}`;
|
|
@@ -2977,7 +2977,7 @@ var parserInstance = {
|
|
|
2977
2977
|
}
|
|
2978
2978
|
},
|
|
2979
2979
|
parse(input) {
|
|
2980
|
-
let EOF, TERROR, action, errStr, expected, len, lex, lexer,
|
|
2980
|
+
let EOF, TERROR, action, errStr, expected, len, lex, lexer, loc, locs, newState, p, parseTable, preErrorSymbol, r, recovering, rv, sharedState, state, stk, symbol, tokenLen, tokenLine, tokenLoc, tokenText, vals;
|
|
2981
2981
|
[stk, vals, locs] = [[0], [null], []];
|
|
2982
2982
|
[parseTable, tokenText, tokenLine, tokenLen, recovering] = [this.parseTable, "", 0, 0, 0];
|
|
2983
2983
|
[TERROR, EOF] = [2, 1];
|
|
@@ -2994,7 +2994,6 @@ var parserInstance = {
|
|
|
2994
2994
|
lexer.loc = {};
|
|
2995
2995
|
tokenLoc = lexer.loc;
|
|
2996
2996
|
locs.push(tokenLoc);
|
|
2997
|
-
ranges = lexer.options?.ranges;
|
|
2998
2997
|
this.parseError = typeof sharedState.ctx.parseError === "function" ? sharedState.ctx.parseError : Object.getPrototypeOf(this).parseError;
|
|
2999
2998
|
lex = () => {
|
|
3000
2999
|
let token;
|
|
@@ -3049,13 +3048,13 @@ Expecting ${expected.join(", ")}, got '${this.tokenNames[symbol] || symbol}'`;
|
|
|
3049
3048
|
} else if (action < 0) {
|
|
3050
3049
|
len = this.ruleTable[-action * 2 + 1];
|
|
3051
3050
|
rv.$ = vals[vals.length - len];
|
|
3052
|
-
|
|
3053
|
-
rv._$ = {
|
|
3054
|
-
if (ranges)
|
|
3055
|
-
rv._$.range = [locFirst.range[0], locLast.range[1]];
|
|
3051
|
+
loc = locs[locs.length - (len || 1)];
|
|
3052
|
+
rv._$ = { r: loc.r, c: loc.c };
|
|
3056
3053
|
r = this.ruleActions.call(rv, -action, vals, locs, sharedState.ctx);
|
|
3057
3054
|
if (r != null)
|
|
3058
3055
|
rv.$ = r;
|
|
3056
|
+
if (Array.isArray(rv.$))
|
|
3057
|
+
rv.$.loc = rv._$;
|
|
3059
3058
|
if (len) {
|
|
3060
3059
|
stk.length -= len * 2;
|
|
3061
3060
|
vals.length -= len;
|
|
@@ -3984,6 +3983,92 @@ if (typeof globalThis !== 'undefined') {
|
|
|
3984
3983
|
};
|
|
3985
3984
|
}
|
|
3986
3985
|
|
|
3986
|
+
// src/sourcemap.js
|
|
3987
|
+
var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
3988
|
+
function vlqEncode(value) {
|
|
3989
|
+
let result = "";
|
|
3990
|
+
let vlq = value < 0 ? -value << 1 | 1 : value << 1;
|
|
3991
|
+
do {
|
|
3992
|
+
let digit = vlq & 31;
|
|
3993
|
+
vlq >>>= 5;
|
|
3994
|
+
if (vlq > 0)
|
|
3995
|
+
digit |= 32;
|
|
3996
|
+
result += B64[digit];
|
|
3997
|
+
} while (vlq > 0);
|
|
3998
|
+
return result;
|
|
3999
|
+
}
|
|
4000
|
+
|
|
4001
|
+
class SourceMapGenerator {
|
|
4002
|
+
constructor(file, source, sourceContent = null) {
|
|
4003
|
+
this.file = file;
|
|
4004
|
+
this.source = source;
|
|
4005
|
+
this.sourceContent = sourceContent;
|
|
4006
|
+
this.names = [];
|
|
4007
|
+
this.nameIndex = new Map;
|
|
4008
|
+
this.lines = [];
|
|
4009
|
+
this.mappings = [];
|
|
4010
|
+
this.prevGenCol = 0;
|
|
4011
|
+
this.prevOrigLine = 0;
|
|
4012
|
+
this.prevOrigCol = 0;
|
|
4013
|
+
this.prevNameIdx = 0;
|
|
4014
|
+
this.currentLine = -1;
|
|
4015
|
+
}
|
|
4016
|
+
ensureLine(line) {
|
|
4017
|
+
while (this.lines.length <= line)
|
|
4018
|
+
this.lines.push([]);
|
|
4019
|
+
}
|
|
4020
|
+
addName(name) {
|
|
4021
|
+
if (this.nameIndex.has(name))
|
|
4022
|
+
return this.nameIndex.get(name);
|
|
4023
|
+
let idx = this.names.length;
|
|
4024
|
+
this.names.push(name);
|
|
4025
|
+
this.nameIndex.set(name, idx);
|
|
4026
|
+
return idx;
|
|
4027
|
+
}
|
|
4028
|
+
addMapping(genLine, genCol, origLine, origCol, name) {
|
|
4029
|
+
this.ensureLine(genLine);
|
|
4030
|
+
if (this.currentLine !== genLine) {
|
|
4031
|
+
this.prevGenCol = 0;
|
|
4032
|
+
this.currentLine = genLine;
|
|
4033
|
+
}
|
|
4034
|
+
if (origLine == null) {
|
|
4035
|
+
this.lines[genLine].push(vlqEncode(genCol - this.prevGenCol));
|
|
4036
|
+
this.prevGenCol = genCol;
|
|
4037
|
+
return;
|
|
4038
|
+
}
|
|
4039
|
+
this.mappings.push({ genLine, genCol, origLine, origCol });
|
|
4040
|
+
let segment = vlqEncode(genCol - this.prevGenCol);
|
|
4041
|
+
this.prevGenCol = genCol;
|
|
4042
|
+
segment += vlqEncode(0);
|
|
4043
|
+
segment += vlqEncode(origLine - this.prevOrigLine);
|
|
4044
|
+
this.prevOrigLine = origLine;
|
|
4045
|
+
segment += vlqEncode(origCol - this.prevOrigCol);
|
|
4046
|
+
this.prevOrigCol = origCol;
|
|
4047
|
+
if (name != null) {
|
|
4048
|
+
let idx = this.addName(name);
|
|
4049
|
+
segment += vlqEncode(idx - this.prevNameIdx);
|
|
4050
|
+
this.prevNameIdx = idx;
|
|
4051
|
+
}
|
|
4052
|
+
this.lines[genLine].push(segment);
|
|
4053
|
+
}
|
|
4054
|
+
toReverseMap() {
|
|
4055
|
+
let reverse = new Map;
|
|
4056
|
+
for (let m of this.mappings) {
|
|
4057
|
+
if (!reverse.has(m.origLine)) {
|
|
4058
|
+
reverse.set(m.origLine, { genLine: m.genLine, genCol: m.genCol });
|
|
4059
|
+
}
|
|
4060
|
+
}
|
|
4061
|
+
return reverse;
|
|
4062
|
+
}
|
|
4063
|
+
toJSON() {
|
|
4064
|
+
let mappings = this.lines.map((segs) => segs.join(",")).join(";");
|
|
4065
|
+
let map = { version: 3, file: this.file, sources: [this.source], names: this.names, mappings };
|
|
4066
|
+
if (this.sourceContent != null)
|
|
4067
|
+
map.sourcesContent = [this.sourceContent];
|
|
4068
|
+
return JSON.stringify(map);
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
|
|
3987
4072
|
// src/compiler.js
|
|
3988
4073
|
var meta = (node, key) => node instanceof String ? node[key] : undefined;
|
|
3989
4074
|
var str = (node) => node instanceof String ? node.valueOf() : node;
|
|
@@ -4267,6 +4352,7 @@ class CodeGenerator {
|
|
|
4267
4352
|
this.indentString = " ";
|
|
4268
4353
|
this.comprehensionDepth = 0;
|
|
4269
4354
|
this.dataSection = options.dataSection;
|
|
4355
|
+
this.sourceMap = options.sourceMap || null;
|
|
4270
4356
|
if (options.reactiveVars) {
|
|
4271
4357
|
this.reactiveVars = new Set(options.reactiveVars);
|
|
4272
4358
|
}
|
|
@@ -4276,7 +4362,56 @@ class CodeGenerator {
|
|
|
4276
4362
|
this.functionVars = new Map;
|
|
4277
4363
|
this.helpers = new Set;
|
|
4278
4364
|
this.collectProgramVariables(sexpr);
|
|
4279
|
-
|
|
4365
|
+
let code = this.generate(sexpr);
|
|
4366
|
+
if (this.sourceMap)
|
|
4367
|
+
this.buildMappings(code, sexpr);
|
|
4368
|
+
return code;
|
|
4369
|
+
}
|
|
4370
|
+
buildMappings(code, sexpr) {
|
|
4371
|
+
if (!sexpr || sexpr[0] !== "program")
|
|
4372
|
+
return;
|
|
4373
|
+
let locs = [];
|
|
4374
|
+
let collect = (node) => {
|
|
4375
|
+
if (!Array.isArray(node))
|
|
4376
|
+
return;
|
|
4377
|
+
let head = node[0];
|
|
4378
|
+
if (head === "program" || head === "block") {
|
|
4379
|
+
for (let i = 1;i < node.length; i++) {
|
|
4380
|
+
let child = node[i];
|
|
4381
|
+
if (Array.isArray(child) && child.loc)
|
|
4382
|
+
locs.push(child.loc);
|
|
4383
|
+
collect(child);
|
|
4384
|
+
}
|
|
4385
|
+
} else {
|
|
4386
|
+
for (let i = 1;i < node.length; i++)
|
|
4387
|
+
collect(node[i]);
|
|
4388
|
+
}
|
|
4389
|
+
};
|
|
4390
|
+
collect(sexpr);
|
|
4391
|
+
let lines = code.split(`
|
|
4392
|
+
`);
|
|
4393
|
+
let locIdx = 0;
|
|
4394
|
+
for (let outLine = 0;outLine < lines.length; outLine++) {
|
|
4395
|
+
let line = lines[outLine];
|
|
4396
|
+
let trimmed = line.trim();
|
|
4397
|
+
if (!trimmed || trimmed === "}" || trimmed === "});")
|
|
4398
|
+
continue;
|
|
4399
|
+
if (trimmed.startsWith("let ") || trimmed.startsWith("var "))
|
|
4400
|
+
continue;
|
|
4401
|
+
if (trimmed.startsWith("const slice") || trimmed.startsWith("const modulo") || trimmed.startsWith("const toSearchable"))
|
|
4402
|
+
continue;
|
|
4403
|
+
if (trimmed.startsWith("const {") && trimmed.includes("__"))
|
|
4404
|
+
continue;
|
|
4405
|
+
if (trimmed.startsWith("} else"))
|
|
4406
|
+
continue;
|
|
4407
|
+
if (trimmed.startsWith("//# source"))
|
|
4408
|
+
continue;
|
|
4409
|
+
if (locIdx < locs.length) {
|
|
4410
|
+
let indent = line.length - trimmed.length;
|
|
4411
|
+
this.sourceMap.addMapping(outLine, indent, locs[locIdx].r, locs[locIdx].c);
|
|
4412
|
+
locIdx++;
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4280
4415
|
}
|
|
4281
4416
|
collectProgramVariables(sexpr) {
|
|
4282
4417
|
if (!Array.isArray(sexpr))
|
|
@@ -7353,14 +7488,30 @@ class Compiler {
|
|
|
7353
7488
|
console.log(formatSExpr(sexpr, 0, true));
|
|
7354
7489
|
console.log();
|
|
7355
7490
|
}
|
|
7491
|
+
let sourceMap = null;
|
|
7492
|
+
if (this.options.sourceMap) {
|
|
7493
|
+
let file = (this.options.filename || "output") + ".js";
|
|
7494
|
+
let sourceFile = this.options.filename || "input.rip";
|
|
7495
|
+
sourceMap = new SourceMapGenerator(file, sourceFile, source);
|
|
7496
|
+
}
|
|
7356
7497
|
let generator = new CodeGenerator({
|
|
7357
7498
|
dataSection,
|
|
7358
7499
|
skipReactiveRuntime: this.options.skipReactiveRuntime,
|
|
7359
7500
|
skipComponentRuntime: this.options.skipComponentRuntime,
|
|
7360
|
-
reactiveVars: this.options.reactiveVars
|
|
7501
|
+
reactiveVars: this.options.reactiveVars,
|
|
7502
|
+
sourceMap
|
|
7361
7503
|
});
|
|
7362
7504
|
let code = generator.compile(sexpr);
|
|
7363
|
-
|
|
7505
|
+
let map = sourceMap ? sourceMap.toJSON() : null;
|
|
7506
|
+
let reverseMap = sourceMap ? sourceMap.toReverseMap() : null;
|
|
7507
|
+
if (map && this.options.sourceMap === "inline") {
|
|
7508
|
+
code += `
|
|
7509
|
+
//# sourceMappingURL=data:application/json;base64,${btoa(map)}`;
|
|
7510
|
+
} else if (map && this.options.filename) {
|
|
7511
|
+
code += `
|
|
7512
|
+
//# sourceMappingURL=${this.options.filename}.js.map`;
|
|
7513
|
+
}
|
|
7514
|
+
return { tokens, sexpr, code, dts, map, reverseMap, data: dataSection, reactiveVars: generator.reactiveVars };
|
|
7364
7515
|
}
|
|
7365
7516
|
compileToJS(source) {
|
|
7366
7517
|
return this.compile(source).code;
|
|
@@ -7384,8 +7535,8 @@ function getComponentRuntime() {
|
|
|
7384
7535
|
return new CodeGenerator({}).getComponentRuntime();
|
|
7385
7536
|
}
|
|
7386
7537
|
// src/browser.js
|
|
7387
|
-
var VERSION = "3.
|
|
7388
|
-
var BUILD_DATE = "2026-02-09@
|
|
7538
|
+
var VERSION = "3.4.0";
|
|
7539
|
+
var BUILD_DATE = "2026-02-09@07:03:58GMT";
|
|
7389
7540
|
var dedent = (s) => {
|
|
7390
7541
|
const m = s.match(/^[ \t]*(?=\S)/gm);
|
|
7391
7542
|
const i = Math.min(...(m || []).map((x) => x.length));
|