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.
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
- evalResult = replContext.eval js
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.3",
3
+ "version": "3.13.4",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
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
 
@@ -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
- let IDENTIFIER_RE = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+(?:!|[?](?![.?[(]))?)([^\n\S]*:(?![=:]))?/;
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)