rip-lang 3.13.3 → 3.13.4
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 +28 -4
- package/bin/rip +6 -0
- package/docs/RIP-INTERNALS.md +13 -0
- package/docs/RIP-LANG.md +107 -71
- package/docs/dist/rip.min.js +125 -125
- package/docs/dist/rip.min.js.br +0 -0
- package/docs/index.html +14 -3
- package/package.json +1 -1
- package/rip-loader.js +10 -0
- package/src/app.rip +0 -2
- package/src/browser.js +4 -3
- package/src/compiler.js +5 -0
- package/src/grammar/grammar.rip +4 -1
- package/src/lexer.js +4 -1
- package/src/parser.js +28 -27
package/docs/dist/rip.min.js.br
CHANGED
|
Binary file
|
package/docs/index.html
CHANGED
|
@@ -813,7 +813,7 @@
|
|
|
813
813
|
# Load Compiler Exports + Monaco (in parallel)
|
|
814
814
|
# ====================================================================
|
|
815
815
|
|
|
816
|
-
{ compile, formatSExpr, VERSION, BUILD_DATE, getReactiveRuntime } = window.__ripExports
|
|
816
|
+
{ compile, formatSExpr, getStdlibCode, VERSION, BUILD_DATE, getReactiveRuntime } = window.__ripExports
|
|
817
817
|
|
|
818
818
|
MONACO_CDN =! 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.0'
|
|
819
819
|
|
|
@@ -1217,6 +1217,7 @@
|
|
|
1217
1217
|
replContext.showSexp = false
|
|
1218
1218
|
replContext.showTokens = false
|
|
1219
1219
|
replContext.__reactiveVars = {}
|
|
1220
|
+
replContext.eval getStdlibCode()
|
|
1220
1221
|
replContext.eval getReactiveRuntime()
|
|
1221
1222
|
|
|
1222
1223
|
# Built-in properties to preserve when clearing
|
|
@@ -1289,8 +1290,18 @@
|
|
|
1289
1290
|
# Remove remaining const
|
|
1290
1291
|
js .= replace /^const\s+(\w+)\s*=/gm, '$1 ='
|
|
1291
1292
|
|
|
1292
|
-
# Evaluate in iframe context
|
|
1293
|
-
|
|
1293
|
+
# Evaluate in iframe context (wrap in async IIFE to support await)
|
|
1294
|
+
# Capture the last expression as the return value
|
|
1295
|
+
lines = js.trimEnd().split('\n')
|
|
1296
|
+
last = lines.length - 1
|
|
1297
|
+
unless /^\s*(import |export |let |const |var |for |while |if |else |switch |try |class |function )/.test(lines[last])
|
|
1298
|
+
lines[last] = "return #{lines[last].replace(/;$/, '')}"
|
|
1299
|
+
wrapped = "(async()=>{\n#{lines.join('\n')}\n})()"
|
|
1300
|
+
evalResult = replContext.eval wrapped
|
|
1301
|
+
|
|
1302
|
+
# Handle async results
|
|
1303
|
+
if evalResult and typeof evalResult.then is 'function'
|
|
1304
|
+
evalResult = await evalResult
|
|
1294
1305
|
|
|
1295
1306
|
replContext._ = evalResult if evalResult isnt undefined
|
|
1296
1307
|
|
package/package.json
CHANGED
package/rip-loader.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
// Bun loader for .rip files
|
|
2
2
|
|
|
3
3
|
import { plugin } from "bun";
|
|
4
|
+
import { dirname } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
4
6
|
import { compileToJS } from "./src/compiler.js";
|
|
5
7
|
|
|
8
|
+
// Ensure NODE_PATH includes the node_modules where rip-lang is installed, so workers
|
|
9
|
+
// (and any code running through this loader) can resolve @rip-lang/* packages even
|
|
10
|
+
// when the script lives in a directory without its own node_modules.
|
|
11
|
+
const _nodeModules = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
12
|
+
if (!process.env.NODE_PATH?.split(':').includes(_nodeModules)) {
|
|
13
|
+
process.env.NODE_PATH = [_nodeModules, process.env.NODE_PATH].filter(Boolean).join(':');
|
|
14
|
+
}
|
|
15
|
+
|
|
6
16
|
await plugin({
|
|
7
17
|
name: "rip-loader",
|
|
8
18
|
async setup(build) {
|
package/src/app.rip
CHANGED
|
@@ -926,8 +926,6 @@ export launch = (appBase = '', opts = {}) ->
|
|
|
926
926
|
name += '.rip' unless name.endsWith('.rip')
|
|
927
927
|
components["components/#{name}"] = script.textContent
|
|
928
928
|
bundle = { components, data: {} }
|
|
929
|
-
else if opts.bundle is false
|
|
930
|
-
bundle = { components: {}, data: {} }
|
|
931
929
|
else
|
|
932
930
|
bundleUrl = "#{appBase}/bundle"
|
|
933
931
|
res = await fetch(bundleUrl, cache: 'no-cache')
|
package/src/browser.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
export { Lexer } from './lexer.js';
|
|
5
5
|
export { parser } from './parser.js';
|
|
6
|
-
export { CodeGenerator, Compiler, compile, compileToJS, formatSExpr, getReactiveRuntime, getComponentRuntime } from './compiler.js';
|
|
6
|
+
export { CodeGenerator, Compiler, compile, compileToJS, formatSExpr, getStdlibCode, getReactiveRuntime, getComponentRuntime } from './compiler.js';
|
|
7
|
+
import { getStdlibCode } from './compiler.js';
|
|
7
8
|
|
|
8
9
|
// Version info (replaced during build)
|
|
9
10
|
export const VERSION = "0.0.0";
|
|
@@ -130,7 +131,7 @@ if (typeof globalThis !== 'undefined') {
|
|
|
130
131
|
globalThis.rip = rip;
|
|
131
132
|
globalThis.importRip = importRip;
|
|
132
133
|
globalThis.compileToJS = compileToJS;
|
|
133
|
-
globalThis.__ripExports = { compile, compileToJS, formatSExpr, VERSION, BUILD_DATE, getReactiveRuntime, getComponentRuntime };
|
|
134
|
+
globalThis.__ripExports = { compile, compileToJS, formatSExpr, getStdlibCode, VERSION, BUILD_DATE, getReactiveRuntime, getComponentRuntime };
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
// Auto-launch: if app.rip is bundled and the page has component scripts or a data-url,
|
|
@@ -143,8 +144,8 @@ async function autoLaunch() {
|
|
|
143
144
|
const cfg = document.querySelector('script[data-hash], script[data-url]');
|
|
144
145
|
const url = cfg?.getAttribute('data-url') || '';
|
|
145
146
|
const hash = cfg?.getAttribute('data-hash');
|
|
147
|
+
if (cfg?.hasAttribute('data-url') && !url) return;
|
|
146
148
|
const opts = { hash: hash !== 'false' };
|
|
147
|
-
if (cfg?.hasAttribute('data-url') && !url) opts.bundle = false;
|
|
148
149
|
await ui.launch(url, opts);
|
|
149
150
|
}
|
|
150
151
|
|
package/src/compiler.js
CHANGED
|
@@ -204,6 +204,7 @@ export class CodeGenerator {
|
|
|
204
204
|
'break': 'generateBreak',
|
|
205
205
|
'continue': 'generateContinue',
|
|
206
206
|
'?': 'generateExistential',
|
|
207
|
+
'defined': 'generateDefined',
|
|
207
208
|
'?:': 'generateTernary',
|
|
208
209
|
'|>': 'generatePipe',
|
|
209
210
|
'loop': 'generateLoop',
|
|
@@ -1112,6 +1113,10 @@ export class CodeGenerator {
|
|
|
1112
1113
|
return `(${this.generate(rest[0], 'value')} != null)`;
|
|
1113
1114
|
}
|
|
1114
1115
|
|
|
1116
|
+
generateDefined(head, rest) {
|
|
1117
|
+
return `(${this.generate(rest[0], 'value')} !== undefined)`;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1115
1120
|
generateTernary(head, rest, context) {
|
|
1116
1121
|
let [cond, then_, else_] = rest;
|
|
1117
1122
|
|
package/src/grammar/grammar.rip
CHANGED
|
@@ -849,6 +849,9 @@ grammar =
|
|
|
849
849
|
# Postfix existence check: expr? → (expr != null)
|
|
850
850
|
o 'Value ?' , '["?", 1]'
|
|
851
851
|
|
|
852
|
+
# Postfix defined check: expr!? → (expr !== undefined)
|
|
853
|
+
o 'Value DEFINED' , '["defined", 1]'
|
|
854
|
+
|
|
852
855
|
# Await
|
|
853
856
|
o 'AWAIT Expression' , '["await", 2]'
|
|
854
857
|
o 'AWAIT INDENT Object OUTDENT' , '["await", 3]'
|
|
@@ -912,7 +915,7 @@ operators = """
|
|
|
912
915
|
right DO_IIFE
|
|
913
916
|
left . ?.
|
|
914
917
|
left CALL_START CALL_END
|
|
915
|
-
nonassoc ++ -- ?
|
|
918
|
+
nonassoc ++ -- ? DEFINED
|
|
916
919
|
right UNARY DO
|
|
917
920
|
right AWAIT
|
|
918
921
|
right **
|
package/src/lexer.js
CHANGED
|
@@ -211,7 +211,8 @@ let UNARY_MATH = new Set(['!', '~']);
|
|
|
211
211
|
// Identifier: word chars + optional trailing ! (await) or ? (predicate)
|
|
212
212
|
// The ? suffix is only captured when NOT followed by . ? [ ( to avoid
|
|
213
213
|
// conflict with ?. (optional chaining), ?? (nullish), ?.( and ?.[
|
|
214
|
-
|
|
214
|
+
// The ! suffix is NOT captured when followed by ? to preserve !? as operator
|
|
215
|
+
let IDENTIFIER_RE = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+(?:!(?!\?)|[?](?![.?[(]))?)([^\n\S]*:(?![=:]))?/;
|
|
215
216
|
let NUMBER_RE = /^0b[01](?:_?[01])*n?|^0o[0-7](?:_?[0-7])*n?|^0x[\da-f](?:_?[\da-f])*n?|^\d+(?:_\d+)*n|^(?:\d+(?:_\d+)*)?\.?\d+(?:_\d+)*(?:e[+-]?\d+(?:_\d+)*)?/i;
|
|
216
217
|
let OPERATOR_RE = /^(?:<=>|::=|::|[-=]>|~>|~=|:=|=!|===|!==|!\?|\?\?|=~|\|>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?\.?|\.{2,3})/;
|
|
217
218
|
let WHITESPACE_RE = /^[^\n\S]+/;
|
|
@@ -1226,6 +1227,8 @@ export class Lexer {
|
|
|
1226
1227
|
else if (SHIFT.has(val)) tag = 'SHIFT';
|
|
1227
1228
|
// Spaced ? → SPACE? (ternary)
|
|
1228
1229
|
else if (val === '?' && prev?.spaced) tag = 'SPACE?';
|
|
1230
|
+
// Unspaced !? → DEFINED (postfix defined check: v!? → v !== undefined)
|
|
1231
|
+
else if (val === '!?' && prev && !prev.spaced) tag = 'DEFINED';
|
|
1229
1232
|
// ?[ and ?( without dot → treat as optional chaining (?.)
|
|
1230
1233
|
else if (val === '?' && (this.chunk[1] === '[' || this.chunk[1] === '(')) tag = '?.';
|
|
1231
1234
|
// Call/index context (ES6 optional chaining only)
|