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 +133 -0
- package/docs/dist/rip.browser.js +5 -5
- package/docs/dist/rip.browser.min.js +2 -2
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/package.json +1 -1
- package/src/codegen.js +4 -4
- package/src/grammar/solar.rip +53 -12
- package/src/parser.js +1 -1
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
|
package/docs/dist/rip.browser.js
CHANGED
|
@@ -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
|
|
5330
|
-
const
|
|
5331
|
-
if (!
|
|
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.
|
|
6835
|
-
var BUILD_DATE = "2025-11-06@
|
|
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.
|
|
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
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
|
-
//
|
|
3158
|
-
const
|
|
3159
|
-
const
|
|
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 (!
|
|
3161
|
+
if (!isBlockStatement || !generated.endsWith('}')) {
|
|
3162
3162
|
return generated + ';';
|
|
3163
3163
|
}
|
|
3164
3164
|
}
|
package/src/grammar/solar.rip
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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'
|
|
889
|
-
when '-v', '--version'
|
|
890
|
-
when '-i', '--info'
|
|
891
|
-
when '-s', '--sexpr'
|
|
892
|
-
when '-
|
|
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
|
|
897
|
-
if options.version
|
|
898
|
-
if not grammarFile
|
|
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
|
-
|
|
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.
|
|
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},
|