starlight-cli 1.0.18 → 1.0.19

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/dist/index.js CHANGED
@@ -1457,9 +1457,20 @@ class Evaluator {
1457
1457
  return result;
1458
1458
  }
1459
1459
 
1460
- evalTemplateLiteral(node, env) {
1461
- return node.value;
1460
+ evalTemplateLiteral(node, env) {
1461
+ // node.parts is an array from parser: Literal or expression nodes
1462
+ let result = '';
1463
+ for (const part of node.parts) {
1464
+ if (part.type === 'Literal') {
1465
+ result += part.value;
1466
+ } else {
1467
+ const val = this.evaluate(part, env);
1468
+ result += val != null ? val.toString() : '';
1469
+ }
1462
1470
  }
1471
+ return result;
1472
+ }
1473
+
1463
1474
 
1464
1475
  evalImport(node, env) {
1465
1476
  const spec = node.path;
@@ -1826,38 +1837,45 @@ class Lexer {
1826
1837
  }
1827
1838
 
1828
1839
  templateString() {
1829
- this.advance();
1830
- let result = '';
1831
- while (this.currentChar && this.currentChar !== '`') {
1832
- if (this.currentChar === '$' && this.peek() === '{') {
1833
- this.advance(); this.advance(); // skip ${
1834
- result += '${'; // we leave the interpolation as-is for parser
1835
- continue;
1836
- }
1837
-
1838
- if (this.currentChar === '\\') {
1839
- this.advance();
1840
- switch (this.currentChar) {
1841
- case 'n': result += '\n'; break;
1842
- case 't': result += '\t'; break;
1843
- case '`': result += '`'; break;
1844
- case '\\': result += '\\'; break;
1845
- default: result += this.currentChar;
1846
- }
1847
- } else {
1848
- result += this.currentChar;
1840
+ this.advance(); // skip initial backtick `
1841
+ let buffer = '';
1842
+ const tokens = [];
1843
+
1844
+ while (this.currentChar && this.currentChar !== '`') {
1845
+ if (this.currentChar === '$' && this.peek() === '{') {
1846
+ if (buffer.length > 0) {
1847
+ tokens.push({ type: 'TEMPLATE_STRING', value: buffer });
1848
+ buffer = '';
1849
1849
  }
1850
- this.advance();
1850
+ tokens.push({ type: 'DOLLAR_LBRACE' }); // ${
1851
+ this.advance(); this.advance();
1852
+ continue;
1851
1853
  }
1852
1854
 
1853
- if (this.currentChar !== '`') {
1854
- this.error('Unterminated template string');
1855
+ if (this.currentChar === '\\') {
1856
+ this.advance();
1857
+ switch (this.currentChar) {
1858
+ case 'n': buffer += '\n'; break;
1859
+ case 't': buffer += '\t'; break;
1860
+ case '`': buffer += '`'; break;
1861
+ case '\\': buffer += '\\'; break;
1862
+ default: buffer += this.currentChar;
1863
+ }
1864
+ } else {
1865
+ buffer += this.currentChar;
1855
1866
  }
1856
-
1857
1867
  this.advance();
1858
- return { type: 'TEMPLATE_STRING', value: result };
1859
1868
  }
1860
1869
 
1870
+ if (buffer.length > 0) tokens.push({ type: 'TEMPLATE_STRING', value: buffer });
1871
+
1872
+ if (this.currentChar !== '`') this.error('Unterminated template string');
1873
+ this.advance(); // skip closing backtick
1874
+
1875
+ return tokens; // return array of tokens
1876
+ }
1877
+
1878
+
1861
1879
  getTokens() {
1862
1880
  const tokens = [];
1863
1881
 
@@ -1867,7 +1885,12 @@ class Lexer {
1867
1885
  if (/[0-9]/.test(this.currentChar)) { tokens.push(this.number()); continue; }
1868
1886
  if (/[A-Za-z_]/.test(this.currentChar)) { tokens.push(this.identifier()); continue; }
1869
1887
  if (this.currentChar === '"' || this.currentChar === "'") { tokens.push(this.string()); continue; }
1870
- if (this.currentChar === '`') { tokens.push(this.templateString()); continue; }
1888
+ if (this.currentChar === '`') {
1889
+ const tTokens = this.templateString();
1890
+ tokens.push(...tTokens); // push all tokens returned from templateString
1891
+ continue;
1892
+ }
1893
+
1871
1894
 
1872
1895
  const char = this.currentChar;
1873
1896
  const next = this.peek();
@@ -2142,6 +2165,23 @@ class Parser {
2142
2165
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2143
2166
  return { type: 'ExpressionStatement', expression: expr };
2144
2167
  }
2168
+ parseTemplateLiteral() {
2169
+ const parts = [];
2170
+ while (true) {
2171
+ if (this.current.type === 'TEMPLATE_STRING') {
2172
+ parts.push({ type: 'Literal', value: this.current.value });
2173
+ this.eat('TEMPLATE_STRING');
2174
+ } else if (this.current.type === 'DOLLAR_LBRACE') {
2175
+ this.eat('DOLLAR_LBRACE');
2176
+ const expr = this.expression();
2177
+ parts.push(expr);
2178
+ this.eat('RBRACE');
2179
+ } else {
2180
+ break;
2181
+ }
2182
+ }
2183
+ return { type: 'TemplateLiteral', parts };
2184
+ }
2145
2185
 
2146
2186
  expression() {
2147
2187
  return this.assignment();
@@ -2289,7 +2329,10 @@ class Parser {
2289
2329
 
2290
2330
  if (t.type === 'NUMBER') { this.eat('NUMBER'); return { type: 'Literal', value: t.value }; }
2291
2331
  if (t.type === 'STRING') { this.eat('STRING'); return { type: 'Literal', value: t.value }; }
2292
- if (t.type === 'TEMPLATE_STRING') { this.eat('TEMPLATE_STRING'); return { type: 'TemplateLiteral', value: t.value }; }
2332
+ if (t.type === 'TEMPLATE_STRING' || t.type === 'DOLLAR_LBRACE') {
2333
+ return this.parseTemplateLiteral();
2334
+ }
2335
+
2293
2336
  if (t.type === 'TRUE') { this.eat('TRUE'); return { type: 'Literal', value: true }; }
2294
2337
  if (t.type === 'FALSE') { this.eat('FALSE'); return { type: 'Literal', value: false }; }
2295
2338
  if (t.type === 'NULL') { this.eat('NULL'); return { type: 'Literal', value: null }; }
@@ -2461,7 +2504,7 @@ const Lexer = __nccwpck_require__(211);
2461
2504
  const Parser = __nccwpck_require__(222);
2462
2505
  const Evaluator = __nccwpck_require__(112);
2463
2506
 
2464
- const VERSION = '1.0.18';
2507
+ const VERSION = '1.0.19';
2465
2508
 
2466
2509
  const COLOR = {
2467
2510
  reset: '\x1b[0m',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-cli",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "Starlight Programming Language CLI",
5
5
  "bin": {
6
6
  "starlight": "index.js"
package/src/evaluator.js CHANGED
@@ -114,9 +114,20 @@ class Evaluator {
114
114
  return result;
115
115
  }
116
116
 
117
- evalTemplateLiteral(node, env) {
118
- return node.value;
117
+ evalTemplateLiteral(node, env) {
118
+ // node.parts is an array from parser: Literal or expression nodes
119
+ let result = '';
120
+ for (const part of node.parts) {
121
+ if (part.type === 'Literal') {
122
+ result += part.value;
123
+ } else {
124
+ const val = this.evaluate(part, env);
125
+ result += val != null ? val.toString() : '';
126
+ }
119
127
  }
128
+ return result;
129
+ }
130
+
120
131
 
121
132
  evalImport(node, env) {
122
133
  const spec = node.path;
package/src/lexer.js CHANGED
@@ -121,38 +121,45 @@ class Lexer {
121
121
  }
122
122
 
123
123
  templateString() {
124
- this.advance();
125
- let result = '';
126
- while (this.currentChar && this.currentChar !== '`') {
127
- if (this.currentChar === '$' && this.peek() === '{') {
128
- this.advance(); this.advance(); // skip ${
129
- result += '${'; // we leave the interpolation as-is for parser
130
- continue;
131
- }
132
-
133
- if (this.currentChar === '\\') {
134
- this.advance();
135
- switch (this.currentChar) {
136
- case 'n': result += '\n'; break;
137
- case 't': result += '\t'; break;
138
- case '`': result += '`'; break;
139
- case '\\': result += '\\'; break;
140
- default: result += this.currentChar;
141
- }
142
- } else {
143
- result += this.currentChar;
124
+ this.advance(); // skip initial backtick `
125
+ let buffer = '';
126
+ const tokens = [];
127
+
128
+ while (this.currentChar && this.currentChar !== '`') {
129
+ if (this.currentChar === '$' && this.peek() === '{') {
130
+ if (buffer.length > 0) {
131
+ tokens.push({ type: 'TEMPLATE_STRING', value: buffer });
132
+ buffer = '';
144
133
  }
145
- this.advance();
134
+ tokens.push({ type: 'DOLLAR_LBRACE' }); // ${
135
+ this.advance(); this.advance();
136
+ continue;
146
137
  }
147
138
 
148
- if (this.currentChar !== '`') {
149
- this.error('Unterminated template string');
139
+ if (this.currentChar === '\\') {
140
+ this.advance();
141
+ switch (this.currentChar) {
142
+ case 'n': buffer += '\n'; break;
143
+ case 't': buffer += '\t'; break;
144
+ case '`': buffer += '`'; break;
145
+ case '\\': buffer += '\\'; break;
146
+ default: buffer += this.currentChar;
147
+ }
148
+ } else {
149
+ buffer += this.currentChar;
150
150
  }
151
-
152
151
  this.advance();
153
- return { type: 'TEMPLATE_STRING', value: result };
154
152
  }
155
153
 
154
+ if (buffer.length > 0) tokens.push({ type: 'TEMPLATE_STRING', value: buffer });
155
+
156
+ if (this.currentChar !== '`') this.error('Unterminated template string');
157
+ this.advance(); // skip closing backtick
158
+
159
+ return tokens; // return array of tokens
160
+ }
161
+
162
+
156
163
  getTokens() {
157
164
  const tokens = [];
158
165
 
@@ -162,7 +169,12 @@ class Lexer {
162
169
  if (/[0-9]/.test(this.currentChar)) { tokens.push(this.number()); continue; }
163
170
  if (/[A-Za-z_]/.test(this.currentChar)) { tokens.push(this.identifier()); continue; }
164
171
  if (this.currentChar === '"' || this.currentChar === "'") { tokens.push(this.string()); continue; }
165
- if (this.currentChar === '`') { tokens.push(this.templateString()); continue; }
172
+ if (this.currentChar === '`') {
173
+ const tTokens = this.templateString();
174
+ tokens.push(...tTokens); // push all tokens returned from templateString
175
+ continue;
176
+ }
177
+
166
178
 
167
179
  const char = this.currentChar;
168
180
  const next = this.peek();
package/src/parser.js CHANGED
@@ -224,6 +224,23 @@ class Parser {
224
224
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
225
225
  return { type: 'ExpressionStatement', expression: expr };
226
226
  }
227
+ parseTemplateLiteral() {
228
+ const parts = [];
229
+ while (true) {
230
+ if (this.current.type === 'TEMPLATE_STRING') {
231
+ parts.push({ type: 'Literal', value: this.current.value });
232
+ this.eat('TEMPLATE_STRING');
233
+ } else if (this.current.type === 'DOLLAR_LBRACE') {
234
+ this.eat('DOLLAR_LBRACE');
235
+ const expr = this.expression();
236
+ parts.push(expr);
237
+ this.eat('RBRACE');
238
+ } else {
239
+ break;
240
+ }
241
+ }
242
+ return { type: 'TemplateLiteral', parts };
243
+ }
227
244
 
228
245
  expression() {
229
246
  return this.assignment();
@@ -371,7 +388,10 @@ class Parser {
371
388
 
372
389
  if (t.type === 'NUMBER') { this.eat('NUMBER'); return { type: 'Literal', value: t.value }; }
373
390
  if (t.type === 'STRING') { this.eat('STRING'); return { type: 'Literal', value: t.value }; }
374
- if (t.type === 'TEMPLATE_STRING') { this.eat('TEMPLATE_STRING'); return { type: 'TemplateLiteral', value: t.value }; }
391
+ if (t.type === 'TEMPLATE_STRING' || t.type === 'DOLLAR_LBRACE') {
392
+ return this.parseTemplateLiteral();
393
+ }
394
+
375
395
  if (t.type === 'TRUE') { this.eat('TRUE'); return { type: 'Literal', value: true }; }
376
396
  if (t.type === 'FALSE') { this.eat('FALSE'); return { type: 'Literal', value: false }; }
377
397
  if (t.type === 'NULL') { this.eat('NULL'); return { type: 'Literal', value: null }; }
package/src/starlight.js CHANGED
@@ -9,7 +9,7 @@ const Lexer = require('./lexer');
9
9
  const Parser = require('./parser');
10
10
  const Evaluator = require('./evaluator');
11
11
 
12
- const VERSION = '1.0.18';
12
+ const VERSION = '1.0.19';
13
13
 
14
14
  const COLOR = {
15
15
  reset: '\x1b[0m',