rip-lang 1.3.2 → 1.3.3

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/CHANGELOG.md CHANGED
@@ -5,6 +5,139 @@ All notable changes to Rip will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.3] - 2025-11-06
9
+
10
+ ### Changed
11
+ - **Cleaner JavaScript output** (#34) - Removed unnecessary semicolons after block statements
12
+ - Function and class declarations no longer have trailing semicolons
13
+ - Control flow blocks (if/for/while/switch/try) no longer have trailing semicolons
14
+ - Produces more idiomatic, professional-looking JavaScript
15
+ - Matches standard formatters (Prettier, ESLint)
16
+ - Test count: 878 → 891 (+13 tests)
17
+
18
+ ## [1.3.2] - 2025-11-05
19
+
20
+ ### Changed
21
+ - Minor code cleanup and refinements
22
+ - Updated solar.rip indentation handling for better code generation
23
+
24
+ ## [1.3.1] - 2025-11-05
25
+
26
+ ### Added
27
+ - **Otherwise operator (`!?`)** (#32) - Undefined-only coalescing
28
+ - Returns first value that is NOT `undefined`
29
+ - Unlike `??` (nullish), treats `null`, `false`, `0`, `""` as valid values
30
+ - Perfect for optional parameters with meaningful falsy values
31
+ - Syntax: `value1 !? value2 !? 'default'`
32
+ - Example: `timeout = config.timeout !? 5000` (null/0 are valid!)
33
+ - Test count: 868 → 878 (+10 tests)
34
+
35
+ ## [1.3.0] - 2025-11-05
36
+
37
+ ### Added
38
+ - **Script execution with proper argument passing** - `rip script.rip -h` now passes `-h` to script
39
+ - Arguments before script name → rip options
40
+ - Arguments after script name → script arguments
41
+ - Fixes issue where rip would consume script's flags
42
+ - **Solar.rip synchronization** - Updated to match solar-parser 1.2.0
43
+ - New CLI options: `--version`, `--info`, `--sexpr`
44
+ - Removed `commonCode` architecture for simpler code generation
45
+ - Fixed file writing bug (was using `unless` incorrectly)
46
+
47
+ ### Changed
48
+ - Improved CLI argument parsing for better script execution
49
+
50
+ ## [1.2.2] - 2025-11-04
51
+
52
+ ### Added
53
+ - **Browser REPL UI improvements** - Cleaner, more intuitive interface
54
+ - Made "Live Compiler" the default tab
55
+ - Added Clear and Run buttons to Rip Source panel
56
+ - Converted checkboxes to toggle buttons (gray/blue states)
57
+ - Consistent header layout across both panes
58
+ - Helpful tooltips on all buttons
59
+
60
+ ### Fixed
61
+ - **For-loop destructuring with defaults** (#30) - Full CoffeeScript compatibility
62
+ - `for [a, b = 99, c = 88] in arr` now works correctly
63
+ - Generates proper ES6 destructuring with defaults
64
+ - Previously generated invalid s-expressions in patterns
65
+
66
+ ### Changed
67
+ - Test count: 867 → 868 (+1 test)
68
+
69
+ ## [1.2.1] - 2025-11-04
70
+
71
+ ### Fixed
72
+ - **Slice syntax with nested indices** (#28) - Property access after nested brackets now works
73
+ - `line[_[0].length..]` now compiles correctly
74
+ - Fixed lexer rewriter to use bracket depth counting
75
+ - Previously failed with parse error
76
+ - Example: `arr[obj.data[0].length..]` → `arr.slice(obj.data[0].length)`
77
+ - Test count: 865 → 867 (+2 tests)
78
+
79
+ ## [1.2.0] - 2025-11-04
80
+
81
+ ### Fixed
82
+ - **Switch without discriminant context bug** (#26) - Statement context no longer adds returns
83
+ - Switch in loops now generates correct code (no invalid returns)
84
+ - Made `generateSwitchAsIfChain()` context-aware
85
+ - Value context: adds returns (switch as expression)
86
+ - Statement context: plain statements (side effects only)
87
+ - **__DATA__ section generation** - Natural code emission without string surgery
88
+ - `var DATA;` now positioned logically before `_setDataSection()` call
89
+ - Removed ~50 lines of complex regex/string manipulation
90
+ - Clean, obvious code generation in `generateProgram()`
91
+ - **S-expression pretty printer** - Restored missing features
92
+ - Fixed heregex handling (multi-line regex collapsing)
93
+ - Fixed program node formatting with proper indentation
94
+ - Faster with Set lookup for INLINE_FORMS
95
+ - Modern JS with optional chaining and nullish coalescing
96
+
97
+ ### Changed
98
+ - Test count: 864 → 865 (+1 test)
99
+ - Improved code generation architecture
100
+
101
+ ## [1.1.5] - 2025-11-03
102
+
103
+ ### Fixed
104
+ - **npm package files** - Added `scripts/serve.js` for `rip -w` command
105
+ - Browser REPL now works for npm users
106
+ - Fixed missing dependency for web server functionality
107
+
108
+ ### Changed
109
+ - S-expression printer improvements backported to docs/examples/sexpr.rip
110
+
111
+ ## [1.1.4] - 2025-11-03
112
+
113
+ ### Fixed
114
+ - **S-expression printer** - Improved performance and correctness
115
+ - Faster Set-based INLINE_FORMS lookup
116
+ - Modern JavaScript features (optional chaining, nullish coalescing)
117
+ - Proper heregex and program node formatting
118
+ - All functionality restored and working
119
+
120
+ ## [1.1.3] - 2025-11-03
121
+
122
+ ### Fixed
123
+ - **Package files** - Included `scripts/serve.js` in npm package
124
+ - Required for `rip -w` browser REPL command
125
+
126
+ ## [1.1.2] - 2025-11-02
127
+
128
+ ### Added
129
+ - **npm publishing safeguards** (#24) - Professional package configuration
130
+ - `files` whitelist (only 51 essential files published)
131
+ - `prepublishOnly` script (runs tests + rebuilds browser bundle)
132
+ - `pack` script for previewing package contents
133
+ - **Comprehension optimization** (#22) - Eliminated wasteful IIFEs
134
+ - Function-final comprehension assignments ~24% smaller
135
+ - Smarter context detection for when to use IIFE vs plain loop
136
+
137
+ ### Changed
138
+ - Package size optimized: excludes test/, scripts/, dev files
139
+ - Test count: 864 tests (all passing)
140
+
8
141
  ## [1.1.1] - 2025-11-01
9
142
 
10
143
  ### Fixed
@@ -5326,9 +5326,9 @@ export default ${target}`;
5326
5326
  }
5327
5327
  if (generated && !generated.endsWith(";")) {
5328
5328
  const head = Array.isArray(stmt) ? stmt[0] : null;
5329
- const controlFlowStatements = ["if", "unless", "for-in", "for-of", "while", "until", "loop", "switch", "try"];
5330
- const isControlFlow = controlFlowStatements.includes(head);
5331
- if (!isControlFlow || !generated.endsWith("}")) {
5329
+ const blockStatements = ["def", "class", "if", "unless", "for-in", "for-of", "for-from", "while", "until", "loop", "switch", "try"];
5330
+ const isBlockStatement = blockStatements.includes(head);
5331
+ if (!isBlockStatement || !generated.endsWith("}")) {
5332
5332
  return generated + ";";
5333
5333
  }
5334
5334
  }
@@ -6831,8 +6831,8 @@ function compileToJS(source, options = {}) {
6831
6831
  return compiler.compileToJS(source);
6832
6832
  }
6833
6833
  // src/browser.js
6834
- var VERSION = "1.3.2";
6835
- var BUILD_DATE = "2025-11-06@04:15:20GMT";
6834
+ var VERSION = "1.3.3";
6835
+ var BUILD_DATE = "2025-11-06@19:46:44GMT";
6836
6836
  var dedent = (s) => {
6837
6837
  const m = s.match(/^[ \t]*(?=\S)/gm);
6838
6838
  const i = Math.min(...(m || []).map((x) => x.length));
@@ -103,7 +103,7 @@ ${this.indent()}}`}}let X=`throw ${this.generate(_,"value")}`;if(U==="value")ret
103
103
  `;this.indentLevel--}else{this.indentLevel++;for(let J of G)if(Array.isArray(J)&&J[0]==="="&&Array.isArray(J[1])&&J[1][0]==="."&&J[1][1]==="this"){let W=J[1][2],Q=this.generate(J[2],"value");R+=this.indent()+`static ${W} = ${Q};
104
104
  `}else R+=this.indent()+this.generate(J,"statement")+`;
105
105
  `;this.indentLevel--}}}return R+=this.indent()+"}",R}case"super":{if(E.length===0){if(this.currentMethodName&&this.currentMethodName!=="constructor")return`super.${this.currentMethodName}()`;return"super"}let _=E.map((A)=>this.generate(A,"value")).join(", ");if(this.currentMethodName&&this.currentMethodName!=="constructor")return`super.${this.currentMethodName}(${_})`;return`super(${_})`}case"?call":{let[_,...A]=E,X=this.generate(_,"value"),R=A.map((F)=>this.generate(F,"value")).join(", ");return`(typeof ${X} === 'function' ? ${X}(${R}) : undefined)`}case"?super":{let _=E.map((A)=>this.generate(A,"value")).join(", ");if(this.currentMethodName&&this.currentMethodName!=="constructor")return`(typeof super.${this.currentMethodName} === 'function' ? super.${this.currentMethodName}(${_}) : undefined)`;return`super(${_})`}case"await":{let[_]=E;return`await ${this.generate(_,"value")}`}case"yield":{if(E.length===0)return"yield";let[_]=E;return`yield ${this.generate(_,"value")}`}case"yield-from":{let[_]=E;return`yield* ${this.generate(_,"value")}`}case"import":{if(E.length===1){let[R]=E;return`import(${this.generate(R,"value")})`}let[_,A]=E,X=this.addJsExtensionAndAssertions(A);if(typeof _==="string")return`import ${_} from ${X}`;if(Array.isArray(_)){if(_[0]==="*"&&_.length===2)return`import * as ${_[1]} from ${X}`;if(typeof _[0]==="string"&&Array.isArray(_[1])){let F=_[0],G=_[1];if(G[0]==="*"&&G.length===2)return`import ${F}, * as ${G[1]} from ${X}`;let Z=(Array.isArray(G)?G:[G]).map((J)=>{if(Array.isArray(J)&&J.length===2)return`${J[0]} as ${J[1]}`;return J}).join(", ");return`import ${F}, { ${Z} } from ${X}`}return`import { ${_.map((F)=>{if(Array.isArray(F)&&F.length===2)return`${F[0]} as ${F[1]}`;return F}).join(", ")} } from ${X}`}return`import ${this.generate(_,"value")} from ${X}`}case"export":{let[_]=E;if(Array.isArray(_)&&_.every((A)=>typeof A==="string"))return`export { ${_.join(", ")} }`;if(Array.isArray(_)&&_[0]==="="){let[,A,X]=_;return`export const ${A} = ${this.generate(X,"value")}`}return`export ${this.generate(_,"statement")}`}case"export-default":{let[_]=E;if(Array.isArray(_)&&_[0]==="="){let[,A,X]=_;return`${`const ${A} = ${this.generate(X,"value")}`};
106
- export default ${A}`}return`export default ${this.generate(_,"statement")}`}case"export-all":{let[_]=E;return`export * from ${this.addJsExtensionAndAssertions(_)}`}case"export-from":{let[_,A]=E,X=this.addJsExtensionAndAssertions(A);if(Array.isArray(_))return`export { ${_.map((F)=>{if(Array.isArray(F)&&F.length===2)return`${F[0]} as ${F[1]}`;return F}).join(", ")} } from ${X}`;return`export ${_} from ${X}`}case"do-iife":{let[_]=E;return`(${this.generate(_,"statement")})()`}case"regex":{if(E.length===0)return Y;let[_]=E;return this.generate(_,"value")}case"tagged-template":{let[_,A]=E,X=this.generate(_,"value"),R=this.generate(A,"value");if(R.startsWith("`"))return`${X}${R}`;if(R.startsWith('"')||R.startsWith("'")){let F=R.slice(1,-1);return`${X}\`${F}\``}return`${X}\`${R}\``}case"str":{let _="`";for(let A=0;A<E.length;A++){let X=E[A];if(X instanceof String)_+=this.extractStringContent(X);else if(typeof X==="string")if(X.startsWith('"')||X.startsWith("'")){if(this.options.debug)console.warn("[RIP] Unexpected quoted primitive in str interpolation:",X);_+=X.slice(1,-1)}else _+=X;else if(Array.isArray(X))if(X.length===1&&typeof X[0]==="string"&&!Array.isArray(X[0])){let R=X[0];if(/^[\d"']/.test(R))_+="${"+this.generate(R,"value")+"}";else _+="${"+R+"}"}else{let R=X;if(X.length===1&&Array.isArray(X[0]))R=X[0];_+="${"+this.generate(R,"value")+"}"}}return _+="`",_}default:{if(typeof Y==="string"&&!Y.startsWith('"')&&!Y.startsWith("'")){if(/^-?\d/.test(Y))return Y;if(Y==="super"&&this.currentMethodName&&this.currentMethodName!=="constructor"){let Z=E.map((J)=>this.generate(J,"value")).join(", ");return`super.${this.currentMethodName}(${Z})`}let A=(Z)=>{if(!Array.isArray(Z))return null;let J=Z[0];if((J==="unless"||J==="if")&&Z.length===3)return{type:J,condition:Z[1],value:Z[2]};if(J==="+"||J==="-"||J==="*"||J==="/")for(let W=1;W<Z.length;W++){let Q=A(Z[W]);if(Q)return Q.parentOp=J,Q.operandIndex=W,Q.otherOperands=Z.slice(1).filter((H,z)=>z!==W-1),Q}return null};if(U==="statement"&&E.length===1){let Z=A(E[0]);if(Z){let J;if(Z.parentOp){let N=Array.isArray(Z.value)&&Z.value.length===1?Z.value[0]:Z.value;J=[Z.parentOp,...Z.otherOperands,N]}else J=Array.isArray(Z.value)&&Z.value.length===1?Z.value[0]:Z.value;let W=this.generate(Y,"value"),Q=this.generate(Z.condition,"value"),H=this.generate(J,"value"),z=`${W}(${H})`;if(Z.type==="unless")return`if (!${Q}) ${z}`;else return`if (${Q}) ${z}`}}let X=D===!0,R=this.generate(Y,"value"),F=E.map((Z)=>this.generate(Z,"value")).join(", "),G=`${R}(${F})`;return X?`await ${G}`:G}if(Array.isArray(Y)&&typeof Y[0]==="string"){if(["=","+=","-=","*=","/=","%=","**=","&&=","||=","??=","if","unless","return","throw"].includes(Y[0]))return`(${M.map((X)=>this.generate(X,"value")).join(", ")})`}if(Array.isArray(Y)){let _=(G)=>{if(!Array.isArray(G))return null;let Z=G[0];if((Z==="unless"||Z==="if")&&G.length===3)return{type:Z,condition:G[1],value:G[2]};if(Z==="+"||Z==="-"||Z==="*"||Z==="/")for(let J=1;J<G.length;J++){let W=_(G[J]);if(W)return W.parentOp=Z,W.operandIndex=J,W.otherOperands=G.slice(1).filter((Q,H)=>H!==J-1),W}return null};if(U==="statement"&&E.length===1){let G=_(E[0]);if(G){let Z;if(G.parentOp){let z=Array.isArray(G.value)&&G.value.length===1?G.value[0]:G.value;Z=[G.parentOp,...G.otherOperands,z]}else Z=Array.isArray(G.value)&&G.value.length===1?G.value[0]:G.value;let J=this.generate(Y,"value"),W=this.generate(G.condition,"value"),Q=this.generate(Z,"value"),H=`${J}(${Q})`;if(G.type==="unless")return`if (!${W}) ${H}`;else return`if (${W}) ${H}`}}let A=!1,X;if(Array.isArray(Y)&&(Y[0]==="."||Y[0]==="::")&&Y[2]instanceof String&&Y[2].await===!0){A=!0;let[G,Z]=Y.slice(1),J=this.generate(G,"value"),W=/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(J),Q=Array.isArray(G)&&G[0]==="object",H=Array.isArray(G)&&(G[0]==="await"||G[0]==="yield"),N=W||Q||H?`(${J})`:J,K=Z.valueOf();if(Y[0]==="::")X=`${N}.prototype.${K}`;else X=`${N}.${K}`}else X=this.generate(Y,"value");let R=E.map((G)=>this.generate(G,"value")).join(", "),F=`${X}(${R})`;return A?`await ${F}`:F}throw new Error(`Unknown s-expression type: ${Y}`)}}}generateProgram(M){let U="",Y=[],E=[],D=[];M.forEach((X)=>{if(Array.isArray(X)){let R=X[0];if(R==="import")Y.push(X);else if(R==="export"||R==="export-default"||R==="export-all"||R==="export-from")E.push(X);else D.push(X)}else D.push(X)});let _=D.map((X,R)=>{let F=D.length===1&&Y.length===0&&E.length===0,G=Array.isArray(X)&&X[0]==="object",Z=G&&X.length===2&&Array.isArray(X[1])&&Array.isArray(X[1][1])&&X[1][1][0]==="comprehension",J=Array.isArray(X)&&(X[0]==="comprehension"||X[0]==="object-comprehension"||X[0]==="do-iife"),W=this.programVars.size===0,Q=F&&G&&W&&!J&&!Z,z=R===D.length-1&&J,N;if(Q)N=`(${this.generate(X,"value")})`;else if(z)N=this.generate(X,"value");else N=this.generate(X,"statement");if(N&&!N.endsWith(";")){let K=Array.isArray(X)?X[0]:null;if(!["if","unless","for-in","for-of","while","until","loop","switch","try"].includes(K)||!N.endsWith("}"))return N+";"}return N}).join(`
106
+ export default ${A}`}return`export default ${this.generate(_,"statement")}`}case"export-all":{let[_]=E;return`export * from ${this.addJsExtensionAndAssertions(_)}`}case"export-from":{let[_,A]=E,X=this.addJsExtensionAndAssertions(A);if(Array.isArray(_))return`export { ${_.map((F)=>{if(Array.isArray(F)&&F.length===2)return`${F[0]} as ${F[1]}`;return F}).join(", ")} } from ${X}`;return`export ${_} from ${X}`}case"do-iife":{let[_]=E;return`(${this.generate(_,"statement")})()`}case"regex":{if(E.length===0)return Y;let[_]=E;return this.generate(_,"value")}case"tagged-template":{let[_,A]=E,X=this.generate(_,"value"),R=this.generate(A,"value");if(R.startsWith("`"))return`${X}${R}`;if(R.startsWith('"')||R.startsWith("'")){let F=R.slice(1,-1);return`${X}\`${F}\``}return`${X}\`${R}\``}case"str":{let _="`";for(let A=0;A<E.length;A++){let X=E[A];if(X instanceof String)_+=this.extractStringContent(X);else if(typeof X==="string")if(X.startsWith('"')||X.startsWith("'")){if(this.options.debug)console.warn("[RIP] Unexpected quoted primitive in str interpolation:",X);_+=X.slice(1,-1)}else _+=X;else if(Array.isArray(X))if(X.length===1&&typeof X[0]==="string"&&!Array.isArray(X[0])){let R=X[0];if(/^[\d"']/.test(R))_+="${"+this.generate(R,"value")+"}";else _+="${"+R+"}"}else{let R=X;if(X.length===1&&Array.isArray(X[0]))R=X[0];_+="${"+this.generate(R,"value")+"}"}}return _+="`",_}default:{if(typeof Y==="string"&&!Y.startsWith('"')&&!Y.startsWith("'")){if(/^-?\d/.test(Y))return Y;if(Y==="super"&&this.currentMethodName&&this.currentMethodName!=="constructor"){let Z=E.map((J)=>this.generate(J,"value")).join(", ");return`super.${this.currentMethodName}(${Z})`}let A=(Z)=>{if(!Array.isArray(Z))return null;let J=Z[0];if((J==="unless"||J==="if")&&Z.length===3)return{type:J,condition:Z[1],value:Z[2]};if(J==="+"||J==="-"||J==="*"||J==="/")for(let W=1;W<Z.length;W++){let Q=A(Z[W]);if(Q)return Q.parentOp=J,Q.operandIndex=W,Q.otherOperands=Z.slice(1).filter((H,z)=>z!==W-1),Q}return null};if(U==="statement"&&E.length===1){let Z=A(E[0]);if(Z){let J;if(Z.parentOp){let N=Array.isArray(Z.value)&&Z.value.length===1?Z.value[0]:Z.value;J=[Z.parentOp,...Z.otherOperands,N]}else J=Array.isArray(Z.value)&&Z.value.length===1?Z.value[0]:Z.value;let W=this.generate(Y,"value"),Q=this.generate(Z.condition,"value"),H=this.generate(J,"value"),z=`${W}(${H})`;if(Z.type==="unless")return`if (!${Q}) ${z}`;else return`if (${Q}) ${z}`}}let X=D===!0,R=this.generate(Y,"value"),F=E.map((Z)=>this.generate(Z,"value")).join(", "),G=`${R}(${F})`;return X?`await ${G}`:G}if(Array.isArray(Y)&&typeof Y[0]==="string"){if(["=","+=","-=","*=","/=","%=","**=","&&=","||=","??=","if","unless","return","throw"].includes(Y[0]))return`(${M.map((X)=>this.generate(X,"value")).join(", ")})`}if(Array.isArray(Y)){let _=(G)=>{if(!Array.isArray(G))return null;let Z=G[0];if((Z==="unless"||Z==="if")&&G.length===3)return{type:Z,condition:G[1],value:G[2]};if(Z==="+"||Z==="-"||Z==="*"||Z==="/")for(let J=1;J<G.length;J++){let W=_(G[J]);if(W)return W.parentOp=Z,W.operandIndex=J,W.otherOperands=G.slice(1).filter((Q,H)=>H!==J-1),W}return null};if(U==="statement"&&E.length===1){let G=_(E[0]);if(G){let Z;if(G.parentOp){let z=Array.isArray(G.value)&&G.value.length===1?G.value[0]:G.value;Z=[G.parentOp,...G.otherOperands,z]}else Z=Array.isArray(G.value)&&G.value.length===1?G.value[0]:G.value;let J=this.generate(Y,"value"),W=this.generate(G.condition,"value"),Q=this.generate(Z,"value"),H=`${J}(${Q})`;if(G.type==="unless")return`if (!${W}) ${H}`;else return`if (${W}) ${H}`}}let A=!1,X;if(Array.isArray(Y)&&(Y[0]==="."||Y[0]==="::")&&Y[2]instanceof String&&Y[2].await===!0){A=!0;let[G,Z]=Y.slice(1),J=this.generate(G,"value"),W=/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(J),Q=Array.isArray(G)&&G[0]==="object",H=Array.isArray(G)&&(G[0]==="await"||G[0]==="yield"),N=W||Q||H?`(${J})`:J,K=Z.valueOf();if(Y[0]==="::")X=`${N}.prototype.${K}`;else X=`${N}.${K}`}else X=this.generate(Y,"value");let R=E.map((G)=>this.generate(G,"value")).join(", "),F=`${X}(${R})`;return A?`await ${F}`:F}throw new Error(`Unknown s-expression type: ${Y}`)}}}generateProgram(M){let U="",Y=[],E=[],D=[];M.forEach((X)=>{if(Array.isArray(X)){let R=X[0];if(R==="import")Y.push(X);else if(R==="export"||R==="export-default"||R==="export-all"||R==="export-from")E.push(X);else D.push(X)}else D.push(X)});let _=D.map((X,R)=>{let F=D.length===1&&Y.length===0&&E.length===0,G=Array.isArray(X)&&X[0]==="object",Z=G&&X.length===2&&Array.isArray(X[1])&&Array.isArray(X[1][1])&&X[1][1][0]==="comprehension",J=Array.isArray(X)&&(X[0]==="comprehension"||X[0]==="object-comprehension"||X[0]==="do-iife"),W=this.programVars.size===0,Q=F&&G&&W&&!J&&!Z,z=R===D.length-1&&J,N;if(Q)N=`(${this.generate(X,"value")})`;else if(z)N=this.generate(X,"value");else N=this.generate(X,"statement");if(N&&!N.endsWith(";")){let K=Array.isArray(X)?X[0]:null;if(!["def","class","if","unless","for-in","for-of","for-from","while","until","loop","switch","try"].includes(K)||!N.endsWith("}"))return N+";"}return N}).join(`
107
107
  `),A=!1;if(Y.length>0)U+=Y.map((X)=>this.generate(X,"statement")+";").join(`
108
108
  `),A=!0;if(this.programVars.size>0){let X=Array.from(this.programVars).sort().join(", ");if(A)U+=`
109
109
  `;U+=`let ${X};
@@ -238,4 +238,4 @@ ${this.indent()}}`}return`{ if (${this.generate(U,"value")}) ${this.generate(M,"
238
238
  `:"",M=Y.slice(0,E).join(`
239
239
  `)}let _=new x1().tokenize(M);if(this.options.showTokens)_.forEach((F)=>{console.log(`${F[0].padEnd(12)} ${JSON.stringify(F[1])}`)}),console.log();A1.lexer={tokens:_,pos:0,setInput:function(F,G){},lex:function(){if(this.pos>=this.tokens.length)return 1;let F=this.tokens[this.pos++];return this.yytext=F[1],this.yylloc=F[2],F[0]}};let A;try{A=A1.parse(M)}catch(F){if(/\?\s*\([^)]*\?[^)]*:[^)]*\)\s*:/.test(M)||/\?\s+\w+\s+\?\s+/.test(M))throw new Error(`Nested ternary operators are not supported. Use if/else statements instead:
240
240
  Instead of: x ? (y ? a : b) : c
241
- Use: if x then (if y then a else b) else c`);throw F}if(this.options.showSExpr)console.log(_1(A,0,!0)),console.log();let R=new R1({dataSection:U}).compile(A);return{tokens:_,sexpr:A,code:R,data:U}}compileToJS(M){return this.compile(M).code}compileToSExpr(M){return this.compile(M).sexpr}}function o2(M,U={}){return new N1(U).compile(M)}function K1(M,U={}){return new N1(U).compileToJS(M)}var F3="1.3.2",G3="2025-11-06@04:15:20GMT",t2=(M)=>{let U=M.match(/^[ \t]*(?=\S)/gm),Y=Math.min(...(U||[]).map((E)=>E.length));return M.replace(RegExp(`^[ ]{${Y}}`,"gm"),"").trim()};async function v2(){let M=document.querySelectorAll('script[type="text/rip"]');for(let U of M){if(U.hasAttribute("data-rip-processed"))continue;try{let Y=t2(U.textContent),E=K1(Y);(0,eval)(E),U.setAttribute("data-rip-processed","true")}catch(Y){console.error("Error compiling Rip script:",Y),console.error("Script content:",U.textContent)}}}if(typeof document!=="undefined")if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",v2);else v2();function e2(M){try{let Y=K1(M).replace(/^let\s+[^;]+;\s*\n\s*/m,"");Y=Y.replace(/^const\s+/gm,"var ");let E=(0,eval)(Y);if(E!==void 0)globalThis._=E;return E}catch(U){console.error("Rip compilation error:",U.message);return}}if(typeof globalThis!=="undefined")globalThis.rip=e2;export{e2 as rip,v2 as processRipScripts,A1 as parser,_1 as formatSExpr,K1 as compileToJS,o2 as compile,F3 as VERSION,x1 as Lexer,N1 as Compiler,R1 as CodeGenerator,G3 as BUILD_DATE};
241
+ Use: if x then (if y then a else b) else c`);throw F}if(this.options.showSExpr)console.log(_1(A,0,!0)),console.log();let R=new R1({dataSection:U}).compile(A);return{tokens:_,sexpr:A,code:R,data:U}}compileToJS(M){return this.compile(M).code}compileToSExpr(M){return this.compile(M).sexpr}}function o2(M,U={}){return new N1(U).compile(M)}function K1(M,U={}){return new N1(U).compileToJS(M)}var F3="1.3.3",G3="2025-11-06@19:46:44GMT",t2=(M)=>{let U=M.match(/^[ \t]*(?=\S)/gm),Y=Math.min(...(U||[]).map((E)=>E.length));return M.replace(RegExp(`^[ ]{${Y}}`,"gm"),"").trim()};async function v2(){let M=document.querySelectorAll('script[type="text/rip"]');for(let U of M){if(U.hasAttribute("data-rip-processed"))continue;try{let Y=t2(U.textContent),E=K1(Y);(0,eval)(E),U.setAttribute("data-rip-processed","true")}catch(Y){console.error("Error compiling Rip script:",Y),console.error("Script content:",U.textContent)}}}if(typeof document!=="undefined")if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",v2);else v2();function e2(M){try{let Y=K1(M).replace(/^let\s+[^;]+;\s*\n\s*/m,"");Y=Y.replace(/^const\s+/gm,"var ");let E=(0,eval)(Y);if(E!==void 0)globalThis._=E;return E}catch(U){console.error("Rip compilation error:",U.message);return}}if(typeof globalThis!=="undefined")globalThis.rip=e2;export{e2 as rip,v2 as processRipScripts,A1 as parser,_1 as formatSExpr,K1 as compileToJS,o2 as compile,F3 as VERSION,x1 as Lexer,N1 as Compiler,R1 as CodeGenerator,G3 as BUILD_DATE};
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "A lightweight scripting language that compiles to modern JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
package/src/codegen.js CHANGED
@@ -3154,11 +3154,11 @@ export class CodeGenerator {
3154
3154
  if (generated && !generated.endsWith(';')) {
3155
3155
  // Check if this statement type needs a semicolon
3156
3156
  const head = Array.isArray(stmt) ? stmt[0] : null;
3157
- // Control flow statements (if, for, while, switch, try) ending with } don't need semicolons
3158
- const controlFlowStatements = ['if', 'unless', 'for-in', 'for-of', 'while', 'until', 'loop', 'switch', 'try'];
3159
- const isControlFlow = controlFlowStatements.includes(head);
3157
+ // Block statements ending with } don't need semicolons
3158
+ const blockStatements = ['def', 'class', 'if', 'unless', 'for-in', 'for-of', 'for-from', 'while', 'until', 'loop', 'switch', 'try'];
3159
+ const isBlockStatement = blockStatements.includes(head);
3160
3160
 
3161
- if (!isControlFlow || !generated.endsWith('}')) {
3161
+ if (!isBlockStatement || !generated.endsWith('}')) {
3162
3162
  return generated + ';';
3163
3163
  }
3164
3164
  }
@@ -14,7 +14,7 @@ import fs from 'fs'
14
14
  import path from 'path'
15
15
  import { fileURLToPath, pathToFileURL } from 'url'
16
16
 
17
- VERSION = '1.3.0'
17
+ VERSION = '1.3.1'
18
18
 
19
19
  # Token: A terminal symbol that cannot be broken down further
20
20
  class Token
@@ -84,6 +84,7 @@ class Generator
84
84
  @rules = []
85
85
  @operators = {}
86
86
  @conflicts = 0
87
+ @conflictDetails = [] # Track conflict details for debugging
87
88
 
88
89
  # Initialize symbol table with special symbols
89
90
  @symbolTable = new Map
@@ -561,7 +562,34 @@ class Generator
561
562
  solution = @_resolveConflict item.rule, op, [REDUCE, item.rule.id], which
562
563
 
563
564
  if solution.bydefault
564
- @conflicts++
565
+ # Categorize conflict type
566
+ isEmpty = item.rule.symbols.length is 0 or (item.rule.symbols.length is 1 and item.rule.symbols[0] is '')
567
+ isPassthrough = item.rule.symbols.length is 1 and types[item.rule.symbols[0]] # Single nonterminal
568
+ hasPrecedence = op and item.rule.precedence > 0
569
+ isReduceReduce = which[0] is REDUCE
570
+
571
+ # Determine category
572
+ category = 'empty-optional' if isEmpty
573
+ category = 'passthrough' if !isEmpty and isPassthrough
574
+ category = 'precedence' if !isEmpty and !isPassthrough and hasPrecedence
575
+ category = 'reduce-reduce' if !isEmpty and !isPassthrough and !hasPrecedence and isReduceReduce
576
+ category = 'ambiguous' if !isEmpty and !isPassthrough and !hasPrecedence and !isReduceReduce
577
+
578
+ # Only count and track problematic conflicts (exclude benign ones)
579
+ if category is 'reduce-reduce' or category is 'ambiguous'
580
+ @conflicts++
581
+ @conflictDetails.push {
582
+ state: k,
583
+ lookahead: stackSymbol,
584
+ lookaheadName: @tokenNames[@symbolIds[stackSymbol]] or stackSymbol,
585
+ rule: item.rule.id,
586
+ ruleType: item.rule.type,
587
+ ruleSymbols: item.rule.symbols.join(' '),
588
+ shift: if which[0] is SHIFT then which[1] else null,
589
+ reduce: item.rule.id,
590
+ resolution: if which[0] is SHIFT then 'shift' else 'reduce',
591
+ category: category
592
+ }
565
593
  else
566
594
  action = solution.action
567
595
  else
@@ -851,11 +879,13 @@ if isRunAsScript
851
879
  -v, --version Show version
852
880
  -i, --info Show grammar information
853
881
  -s, --sexpr Show grammar as s-expression
882
+ -c, --conflicts Show conflict details (use with --info)
854
883
  -o, --output <file> Output file (default: parser.js)
855
884
 
856
885
  Examples:
857
886
  solar grammar.js
858
887
  solar --info grammar.js
888
+ solar --info --conflicts grammar.js
859
889
  solar --sexpr grammar.js
860
890
  solar -o parser.js grammar.js
861
891
  """
@@ -877,25 +907,35 @@ if isRunAsScript
877
907
  • Conflicts: #{conflicts}
878
908
  """
879
909
 
910
+ # Show conflict details if requested
911
+ if options.conflicts and generator.conflictDetails?.length
912
+ console.log "\n🔧 Conflict Details (first 30):"
913
+ for conflict, i in generator.conflictDetails.slice(0, 30)
914
+ console.log "\n #{i + 1}. State #{conflict.state}, lookahead '#{conflict.lookaheadName}':"
915
+ console.log " Rule #{conflict.rule}: #{conflict.ruleType} → #{conflict.ruleSymbols}"
916
+ console.log " Resolution: #{conflict.resolution} (by default)"
917
+
880
918
  # Parse command line
881
- options = {help: false, version: false, info: false, sexpr: false, output: 'parser.js'}
919
+ options = {help: false, version: false, info: false, sexpr: false, conflicts: false, output: 'parser.js'}
882
920
  grammarFile = null
883
921
  i = 0
884
922
 
885
923
  while i < process.argv.length - 2
886
924
  arg = process.argv[i + 2]
887
925
  switch arg
888
- when '-h', '--help' then options.help = true
889
- when '-v', '--version' then options.version = true
890
- when '-i', '--info' then options.info = true
891
- when '-s', '--sexpr' then options.sexpr = true
892
- when '-o', '--output' then options.output = process.argv[++i + 2]
926
+ when '-h', '--help' then options.help = true
927
+ when '-v', '--version' then options.version = true
928
+ when '-i', '--info' then options.info = true
929
+ when '-s', '--sexpr' then options.sexpr = true
930
+ when '-c', '--conflicts' then options.conflicts = true
931
+ when '-o', '--output' then options.output = process.argv[++i + 2]
893
932
  else grammarFile = arg unless arg.startsWith('-')
894
933
  i++
895
934
 
896
- if options.help then showHelp() ; process.exit 0
897
- if options.version then showVersion() ; process.exit 0
898
- if not grammarFile then showHelp() ; process.exit 0
935
+ if options.help then showHelp() ; process.exit 0
936
+ if options.version then showVersion() ; process.exit 0
937
+ if not grammarFile then showHelp() ; process.exit 0
938
+ if options.conflicts then options.info = true # --conflicts implies --info
899
939
 
900
940
  try
901
941
  unless fs.existsSync grammarFile
@@ -926,7 +966,8 @@ if isRunAsScript
926
966
  action ?= 1 # Default to 1 if not provided
927
967
  actionStr = if typeof action is 'string' then action else JSON.stringify(action)
928
968
  optsStr = if opts then " #{JSON.stringify(opts)}" else ''
929
- parts.push " (#{pattern || ''} #{actionStr}#{optsStr})"
969
+ patternStr = if pattern is '' then '""' else pattern
970
+ parts.push " (#{patternStr} #{actionStr}#{optsStr})"
930
971
  parts.push " )"
931
972
  parts.push ' )'
932
973
 
package/src/parser.js CHANGED
@@ -1,4 +1,4 @@
1
- // ES6 Parser generated by Solar 1.3.0
1
+ // ES6 Parser generated by Solar 1.3.1
2
2
 
3
3
  const parserInstance = {
4
4
  symbolIds: {"$accept":0,"$end":1,"error":2,"Root":3,"Body":4,"Line":5,"TERMINATOR":6,"Expression":7,"ExpressionLine":8,"Statement":9,"Return":10,"STATEMENT":11,"Import":12,"Export":13,"Value":14,"Code":15,"Operation":16,"Assign":17,"If":18,"Try":19,"While":20,"For":21,"Switch":22,"Class":23,"Throw":24,"Yield":25,"Def":26,"DEF":27,"Identifier":28,"CALL_START":29,"ParamList":30,"CALL_END":31,"Block":32,"CodeLine":33,"OperationLine":34,"YIELD":35,"INDENT":36,"Object":37,"OUTDENT":38,"FROM":39,"IDENTIFIER":40,"Property":41,"PROPERTY":42,"AlphaNumeric":43,"NUMBER":44,"String":45,"STRING":46,"STRING_START":47,"Interpolations":48,"STRING_END":49,"InterpolationChunk":50,"INTERPOLATION_START":51,"INTERPOLATION_END":52,"Regex":53,"REGEX":54,"REGEX_START":55,"Invocation":56,"REGEX_END":57,"RegexWithIndex":58,",":59,"Literal":60,"JS":61,"UNDEFINED":62,"NULL":63,"BOOL":64,"INFINITY":65,"NAN":66,"Assignable":67,"=":68,"AssignObj":69,"ObjAssignable":70,"ObjRestValue":71,":":72,"SimpleObjAssignable":73,"ThisProperty":74,"[":75,"]":76,"@":77,"...":78,"ObjSpreadExpr":79,"Parenthetical":80,"Super":81,"This":82,"SUPER":83,"OptFuncExist":84,"Arguments":85,"DYNAMIC_IMPORT":86,".":87,"?.":88,"::":89,"?::":90,"INDEX_START":91,"INDEX_END":92,"INDEX_SOAK":93,"RETURN":94,"PARAM_START":95,"PARAM_END":96,"FuncGlyph":97,"->":98,"=>":99,"OptComma":100,"Param":101,"ParamVar":102,"Array":103,"Splat":104,"SimpleAssignable":105,"Slice":106,"ES6_OPTIONAL_INDEX":107,"Range":108,"DoIife":109,"MetaProperty":110,"NEW_TARGET":111,"IMPORT_META":112,"{":113,"FOR":114,"ForVariables":115,"FOROF":116,"}":117,"WHEN":118,"OWN":119,"AssignList":120,"CLASS":121,"EXTENDS":122,"IMPORT":123,"ImportDefaultSpecifier":124,"ImportNamespaceSpecifier":125,"ImportSpecifierList":126,"ImportSpecifier":127,"AS":128,"DEFAULT":129,"IMPORT_ALL":130,"EXPORT":131,"ExportSpecifierList":132,"EXPORT_ALL":133,"ExportSpecifier":134,"ES6_OPTIONAL_CALL":135,"FUNC_EXIST":136,"ArgList":137,"THIS":138,"Elisions":139,"ArgElisionList":140,"OptElisions":141,"RangeDots":142,"..":143,"Arg":144,"ArgElision":145,"Elision":146,"SimpleArgs":147,"TRY":148,"Catch":149,"FINALLY":150,"CATCH":151,"THROW":152,"(":153,")":154,"WhileSource":155,"WHILE":156,"UNTIL":157,"Loop":158,"LOOP":159,"FORIN":160,"BY":161,"FORFROM":162,"AWAIT":163,"ForValue":164,"ForVar":165,"SWITCH":166,"Whens":167,"ELSE":168,"When":169,"LEADING_WHEN":170,"IfBlock":171,"IF":172,"UnlessBlock":173,"UNLESS":174,"POST_IF":175,"POST_UNLESS":176,"UNARY":177,"DO":178,"DO_IIFE":179,"UNARY_MATH":180,"-":181,"+":182,"--":183,"++":184,"?":185,"MATH":186,"**":187,"SHIFT":188,"COMPARE":189,"&":190,"^":191,"|":192,"&&":193,"||":194,"??":195,"!?":196,"RELATION":197,"SPACE?":198,"COMPOUND_ASSIGN":199},