starlight-cli 1.0.20 → 1.0.21

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
@@ -1718,7 +1718,7 @@ class Lexer {
1718
1718
  constructor(input) {
1719
1719
  this.input = input;
1720
1720
  this.pos = 0;
1721
- this.currentChar = input[0] || null;
1721
+ this.currentChar = input[this.pos];
1722
1722
  }
1723
1723
 
1724
1724
  advance() {
@@ -1726,211 +1726,155 @@ class Lexer {
1726
1726
  this.currentChar = this.pos < this.input.length ? this.input[this.pos] : null;
1727
1727
  }
1728
1728
 
1729
- peek(n = 1) {
1730
- return this.pos + n < this.input.length ? this.input[this.pos + n] : null;
1731
- }
1732
-
1733
- error(msg) {
1734
- throw new Error(`LEXER ERROR: ${msg} at position ${this.pos}`);
1729
+ peek() {
1730
+ return this.pos + 1 < this.input.length ? this.input[this.pos + 1] : null;
1735
1731
  }
1736
1732
 
1737
1733
  skipWhitespace() {
1738
1734
  while (this.currentChar && /\s/.test(this.currentChar)) this.advance();
1739
1735
  }
1740
1736
 
1741
- skipComment() {
1742
- if (this.currentChar === '#') {
1743
- if (this.peek() === '*') {
1744
- this.advance(); this.advance(); // #*
1745
- while (this.currentChar !== null) {
1746
- if (this.currentChar === '*' && this.peek() === '#') {
1747
- this.advance(); this.advance();
1748
- return;
1749
- }
1750
- this.advance();
1751
- }
1752
- this.error('Unterminated multi-line comment (#* ... *#)');
1753
- } else {
1754
- while (this.currentChar && this.currentChar !== '\n') this.advance();
1755
- }
1756
- }
1737
+ matchKeyword(id) {
1738
+ const keywords = {
1739
+ 'let': 'LET',
1740
+ 'define': 'DEFINE',
1741
+ 'if': 'IF',
1742
+ 'else': 'ELSE',
1743
+ 'while': 'WHILE',
1744
+ 'for': 'FOR',
1745
+ 'break': 'BREAK',
1746
+ 'continue': 'CONTINUE',
1747
+ 'func': 'FUNC',
1748
+ 'return': 'RETURN',
1749
+ 'import': 'IMPORT',
1750
+ 'from': 'FROM',
1751
+ 'as': 'AS',
1752
+ 'true': 'TRUE',
1753
+ 'false': 'FALSE',
1754
+ 'null': 'NULL',
1755
+ 'undefined': 'UNDEFINED',
1756
+ 'ask': 'ASK',
1757
+ 'sldeploy': 'SLDEPLOY'
1758
+ };
1759
+ return keywords[id] || null;
1757
1760
  }
1758
1761
 
1759
1762
  number() {
1760
1763
  let result = '';
1761
- while (this.currentChar && /[0-9]/.test(this.currentChar)) {
1764
+ while (this.currentChar && /[0-9.]/.test(this.currentChar)) {
1762
1765
  result += this.currentChar;
1763
1766
  this.advance();
1764
1767
  }
1765
-
1766
- if (this.currentChar === '.' && /[0-9]/.test(this.peek())) {
1767
- result += '.';
1768
- this.advance();
1769
- while (this.currentChar && /[0-9]/.test(this.currentChar)) {
1770
- result += this.currentChar;
1771
- this.advance();
1772
- }
1773
- }
1774
-
1775
- if (this.currentChar && /[eE]/.test(this.currentChar)) {
1776
- result += this.currentChar;
1777
- this.advance();
1778
- if (this.currentChar === '+' || this.currentChar === '-') {
1779
- result += this.currentChar;
1780
- this.advance();
1781
- }
1782
- if (!/[0-9]/.test(this.currentChar)) this.error('Invalid exponent in number');
1783
- while (this.currentChar && /[0-9]/.test(this.currentChar)) {
1784
- result += this.currentChar;
1785
- this.advance();
1786
- }
1787
- }
1788
-
1789
1768
  return { type: 'NUMBER', value: parseFloat(result) };
1790
1769
  }
1791
1770
 
1792
1771
  identifier() {
1793
1772
  let result = '';
1794
- while (this.currentChar && /[A-Za-z0-9_]/.test(this.currentChar)) {
1773
+ while (this.currentChar && /[a-zA-Z0-9_]/.test(this.currentChar)) {
1795
1774
  result += this.currentChar;
1796
1775
  this.advance();
1797
1776
  }
1798
-
1799
- const keywords = [
1800
- 'let', 'sldeploy', 'if', 'else', 'while', 'for',
1801
- 'break', 'continue', 'func', 'return',
1802
- 'true', 'false', 'null', 'undefined',
1803
- 'ask', 'define', 'import', 'from', 'as'
1804
- ];
1805
-
1806
- if (keywords.includes(result)) {
1807
- return { type: result.toUpperCase(), value: result };
1808
- }
1809
-
1810
- return { type: 'IDENTIFIER', value: result };
1777
+ const type = this.matchKeyword(result) || 'IDENTIFIER';
1778
+ return { type, value: result };
1811
1779
  }
1812
1780
 
1813
- string() {
1814
- const quote = this.currentChar;
1815
- this.advance();
1781
+ string(quoteType) {
1782
+ this.advance(); // skip opening quote
1816
1783
  let result = '';
1817
-
1818
- while (this.currentChar && this.currentChar !== quote) {
1784
+ while (this.currentChar && this.currentChar !== quoteType) {
1819
1785
  if (this.currentChar === '\\') {
1820
1786
  this.advance();
1821
- const map = { n: '\n', t: '\t', '"': '"', "'": "'", '\\': '\\' };
1822
- result += map[this.currentChar] ?? this.currentChar;
1787
+ if (this.currentChar) {
1788
+ const escapeChars = { n: '\n', r: '\r', t: '\t', '\\': '\\', '"': '"', "'": "'" };
1789
+ result += escapeChars[this.currentChar] || this.currentChar;
1790
+ this.advance();
1791
+ }
1823
1792
  } else {
1824
1793
  result += this.currentChar;
1794
+ this.advance();
1825
1795
  }
1826
- this.advance();
1827
1796
  }
1828
-
1829
- if (this.currentChar !== quote) this.error('Unterminated string literal');
1830
-
1831
- this.advance();
1797
+ this.advance(); // skip closing quote
1832
1798
  return { type: 'STRING', value: result };
1833
1799
  }
1834
1800
 
1835
-
1836
- templateString(tokens) {
1837
- this.advance(); // skip opening `
1838
-
1839
- let buffer = '';
1840
-
1801
+ templateLiteral() {
1802
+ let result = '';
1803
+ const tokens = [];
1804
+ this.advance(); // skip initial backtick
1841
1805
  while (this.currentChar !== null) {
1842
- if (this.currentChar === '`') {
1843
- if (buffer.length > 0) {
1844
- tokens.push({ type: 'TEMPLATE_STRING', value: buffer });
1845
- }
1846
- this.advance();
1847
- return;
1848
- }
1849
-
1850
1806
  if (this.currentChar === '$' && this.peek() === '{') {
1851
- if (buffer.length > 0) {
1852
- tokens.push({ type: 'TEMPLATE_STRING', value: buffer });
1853
- buffer = '';
1854
- }
1855
- this.advance(); // $
1856
- this.advance(); // {
1807
+ if (result) tokens.push({ type: 'TEMPLATE_STRING', value: result });
1808
+ result = '';
1857
1809
  tokens.push({ type: 'DOLLAR_LBRACE' });
1858
- return;
1859
- }
1860
-
1861
- if (this.currentChar === '\\') {
1862
1810
  this.advance();
1863
- buffer += this.currentChar;
1811
+ this.advance(); // skip "${"
1812
+ } else if (this.currentChar === '`') {
1813
+ if (result) tokens.push({ type: 'TEMPLATE_STRING', value: result });
1814
+ this.advance();
1815
+ break;
1816
+ } else {
1817
+ result += this.currentChar;
1864
1818
  this.advance();
1865
- continue;
1866
1819
  }
1867
-
1868
- buffer += this.currentChar;
1869
- this.advance();
1870
1820
  }
1871
-
1872
- this.error('Unterminated template string');
1821
+ return tokens;
1873
1822
  }
1874
1823
 
1875
1824
  getTokens() {
1876
1825
  const tokens = [];
1877
-
1878
1826
  while (this.currentChar !== null) {
1879
- if (/\s/.test(this.currentChar)) { this.skipWhitespace(); continue; }
1880
- if (this.currentChar === '#') { this.skipComment(); continue; }
1881
- if (/[0-9]/.test(this.currentChar)) { tokens.push(this.number()); continue; }
1882
- if (/[A-Za-z_]/.test(this.currentChar)) { tokens.push(this.identifier()); continue; }
1883
- if (this.currentChar === '"' || this.currentChar === "'") { tokens.push(this.string()); continue; }
1884
-
1885
- if (this.currentChar === '`') {
1886
- this.templateString(tokens);
1887
- continue;
1888
- }
1827
+ this.skipWhitespace();
1889
1828
 
1890
- const char = this.currentChar;
1891
- const next = this.peek();
1892
- const next2 = this.peek(2);
1893
-
1894
- if (char === '=' && next === '=' && next2 === '=') { tokens.push({ type: 'STRICT_EQ' }); this.advance(); this.advance(); this.advance(); continue; }
1895
- if (char === '!' && next === '=' && next2 === '=') { tokens.push({ type: 'STRICT_NOTEQ' }); this.advance(); this.advance(); this.advance(); continue; }
1896
- if (char === '=' && next === '=') { tokens.push({ type: 'EQEQ' }); this.advance(); this.advance(); continue; }
1897
- if (char === '=' && next === '>') { tokens.push({ type: 'ARROW' }); this.advance(); this.advance(); continue; }
1898
- if (char === '!' && next === '=') { tokens.push({ type: 'NOTEQ' }); this.advance(); this.advance(); continue; }
1899
- if (char === '<' && next === '=') { tokens.push({ type: 'LTE' }); this.advance(); this.advance(); continue; }
1900
- if (char === '>' && next === '=') { tokens.push({ type: 'GTE' }); this.advance(); this.advance(); continue; }
1901
- if (char === '<' && next === '<') { tokens.push({ type: 'LSHIFT' }); this.advance(); this.advance(); continue; }
1902
- if (char === '>' && next === '>') { tokens.push({ type: 'RSHIFT' }); this.advance(); this.advance(); continue; }
1903
- if (char === '&' && next === '&') { tokens.push({ type: 'AND' }); this.advance(); this.advance(); continue; }
1904
- if (char === '|' && next === '|') { tokens.push({ type: 'OR' }); this.advance(); this.advance(); continue; }
1905
- if (char === '+' && next === '+') { tokens.push({ type: 'PLUSPLUS' }); this.advance(); this.advance(); continue; }
1906
- if (char === '-' && next === '-') { tokens.push({ type: 'MINUSMINUS' }); this.advance(); this.advance(); continue; }
1907
-
1908
- const compound = { '+': 'PLUSEQ', '-': 'MINUSEQ', '*': 'STAREQ', '/': 'SLASHEQ', '%': 'MODEQ' };
1909
- if (next === '=' && compound[char]) {
1910
- tokens.push({ type: compound[char] });
1911
- this.advance(); this.advance();
1912
- continue;
1913
- }
1829
+ if (!this.currentChar) break;
1914
1830
 
1915
- const singles = {
1916
- '+': 'PLUS', '-': 'MINUS', '*': 'STAR', '/': 'SLASH', '%': 'MOD',
1917
- '=': 'EQUAL', '<': 'LT', '>': 'GT', '!': 'NOT',
1918
- '(': 'LPAREN', ')': 'RPAREN',
1919
- '{': 'LBRACE', '}': 'RBRACE',
1920
- '[': 'LBRACKET', ']': 'RBRACKET',
1921
- ';': 'SEMICOLON', ',': 'COMMA',
1922
- ':': 'COLON', '.': 'DOT'
1923
- };
1924
-
1925
- if (singles[char]) {
1926
- tokens.push({ type: singles[char] });
1831
+ if (/[0-9]/.test(this.currentChar)) tokens.push(this.number());
1832
+ else if (/[a-zA-Z_]/.test(this.currentChar)) tokens.push(this.identifier());
1833
+ else if (this.currentChar === '"' || this.currentChar === "'") tokens.push(this.string(this.currentChar));
1834
+ else if (this.currentChar === '`') {
1835
+ const parts = this.templateLiteral();
1836
+ tokens.push(...parts);
1837
+ }
1838
+ else {
1839
+ const char = this.currentChar;
1840
+ switch (char) {
1841
+ case '+':
1842
+ if (this.peek() === '+') { tokens.push({ type: 'PLUSPLUS' }); this.advance(); }
1843
+ else if (this.peek() === '=') { tokens.push({ type: 'PLUSEQ' }); this.advance(); }
1844
+ else tokens.push({ type: 'PLUS' });
1845
+ break;
1846
+ case '-':
1847
+ if (this.peek() === '-') { tokens.push({ type: 'MINUSMINUS' }); this.advance(); }
1848
+ else if (this.peek() === '=') { tokens.push({ type: 'MINUSEQ' }); this.advance(); }
1849
+ else tokens.push({ type: 'MINUS' });
1850
+ break;
1851
+ case '*': tokens.push(this.peek() === '=' ? (this.advance(), { type: 'STAREQ' }) : { type: 'STAR' }); break;
1852
+ case '/': tokens.push(this.peek() === '=' ? (this.advance(), { type: 'SLASHEQ' }) : { type: 'SLASH' }); break;
1853
+ case '%': tokens.push(this.peek() === '=' ? (this.advance(), { type: 'MODEQ' }) : { type: 'MOD' }); break;
1854
+ case '=': tokens.push(this.peek() === '=' ? (this.advance(), this.peek() === '=' ? (this.advance(), { type: 'STRICT_EQ' }) : { type: 'EQEQ' }) : { type: 'EQUAL' }); break;
1855
+ case '!': tokens.push(this.peek() === '=' ? (this.advance(), this.peek() === '=' ? (this.advance(), { type: 'STRICT_NOTEQ' }) : { type: 'NOTEQ' }) : { type: 'NOT' }); break;
1856
+ case '<': tokens.push(this.peek() === '<' ? (this.advance(), { type: 'LSHIFT' }) : this.peek() === '=' ? (this.advance(), { type: 'LTE' }) : { type: 'LT' }); break;
1857
+ case '>': tokens.push(this.peek() === '>' ? (this.advance(), { type: 'RSHIFT' }) : this.peek() === '=' ? (this.advance(), { type: 'GTE' }) : { type: 'GT' }); break;
1858
+ case '&': tokens.push(this.peek() === '&' ? (this.advance(), { type: 'AND' }) : { type: 'AMP' }); break;
1859
+ case '|': tokens.push(this.peek() === '|' ? (this.advance(), { type: 'OR' }) : { type: 'PIPE' }); break;
1860
+ case '(': tokens.push({ type: 'LPAREN' }); break;
1861
+ case ')': tokens.push({ type: 'RPAREN' }); break;
1862
+ case '{': tokens.push({ type: 'LBRACE' }); break;
1863
+ case '}': tokens.push({ type: 'RBRACE' }); break;
1864
+ case '[': tokens.push({ type: 'LBRACKET' }); break;
1865
+ case ']': tokens.push({ type: 'RBRACKET' }); break;
1866
+ case ',': tokens.push({ type: 'COMMA' }); break;
1867
+ case ';': tokens.push({ type: 'SEMICOLON' }); break;
1868
+ case '.': tokens.push({ type: 'DOT' }); break;
1869
+ case ':': tokens.push({ type: 'COLON' }); break;
1870
+ case '*': tokens.push({ type: 'STAR' }); break;
1871
+ case '$': tokens.push({ type: 'DOLLAR' }); break;
1872
+ case '?': tokens.push({ type: 'QUESTION' }); break;
1873
+ default: throw new Error(`Unexpected character: ${char} at position ${this.pos}`);
1874
+ }
1927
1875
  this.advance();
1928
- continue;
1929
1876
  }
1930
-
1931
- this.error(`Unexpected character: ${char}`);
1932
1877
  }
1933
-
1934
1878
  tokens.push({ type: 'EOF' });
1935
1879
  return tokens;
1936
1880
  }
@@ -2510,7 +2454,7 @@ const Lexer = __nccwpck_require__(211);
2510
2454
  const Parser = __nccwpck_require__(222);
2511
2455
  const Evaluator = __nccwpck_require__(112);
2512
2456
 
2513
- const VERSION = '1.0.20';
2457
+ const VERSION = '1.0.21';
2514
2458
 
2515
2459
  const COLOR = {
2516
2460
  reset: '\x1b[0m',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-cli",
3
- "version": "1.0.20",
3
+ "version": "1.0.21",
4
4
  "description": "Starlight Programming Language CLI",
5
5
  "bin": {
6
6
  "starlight": "index.js"
package/src/lexer.js CHANGED
@@ -2,7 +2,7 @@ class Lexer {
2
2
  constructor(input) {
3
3
  this.input = input;
4
4
  this.pos = 0;
5
- this.currentChar = input[0] || null;
5
+ this.currentChar = input[this.pos];
6
6
  }
7
7
 
8
8
  advance() {
@@ -10,211 +10,155 @@ class Lexer {
10
10
  this.currentChar = this.pos < this.input.length ? this.input[this.pos] : null;
11
11
  }
12
12
 
13
- peek(n = 1) {
14
- return this.pos + n < this.input.length ? this.input[this.pos + n] : null;
15
- }
16
-
17
- error(msg) {
18
- throw new Error(`LEXER ERROR: ${msg} at position ${this.pos}`);
13
+ peek() {
14
+ return this.pos + 1 < this.input.length ? this.input[this.pos + 1] : null;
19
15
  }
20
16
 
21
17
  skipWhitespace() {
22
18
  while (this.currentChar && /\s/.test(this.currentChar)) this.advance();
23
19
  }
24
20
 
25
- skipComment() {
26
- if (this.currentChar === '#') {
27
- if (this.peek() === '*') {
28
- this.advance(); this.advance(); // #*
29
- while (this.currentChar !== null) {
30
- if (this.currentChar === '*' && this.peek() === '#') {
31
- this.advance(); this.advance();
32
- return;
33
- }
34
- this.advance();
35
- }
36
- this.error('Unterminated multi-line comment (#* ... *#)');
37
- } else {
38
- while (this.currentChar && this.currentChar !== '\n') this.advance();
39
- }
40
- }
21
+ matchKeyword(id) {
22
+ const keywords = {
23
+ 'let': 'LET',
24
+ 'define': 'DEFINE',
25
+ 'if': 'IF',
26
+ 'else': 'ELSE',
27
+ 'while': 'WHILE',
28
+ 'for': 'FOR',
29
+ 'break': 'BREAK',
30
+ 'continue': 'CONTINUE',
31
+ 'func': 'FUNC',
32
+ 'return': 'RETURN',
33
+ 'import': 'IMPORT',
34
+ 'from': 'FROM',
35
+ 'as': 'AS',
36
+ 'true': 'TRUE',
37
+ 'false': 'FALSE',
38
+ 'null': 'NULL',
39
+ 'undefined': 'UNDEFINED',
40
+ 'ask': 'ASK',
41
+ 'sldeploy': 'SLDEPLOY'
42
+ };
43
+ return keywords[id] || null;
41
44
  }
42
45
 
43
46
  number() {
44
47
  let result = '';
45
- while (this.currentChar && /[0-9]/.test(this.currentChar)) {
46
- result += this.currentChar;
47
- this.advance();
48
- }
49
-
50
- if (this.currentChar === '.' && /[0-9]/.test(this.peek())) {
51
- result += '.';
52
- this.advance();
53
- while (this.currentChar && /[0-9]/.test(this.currentChar)) {
54
- result += this.currentChar;
55
- this.advance();
56
- }
57
- }
58
-
59
- if (this.currentChar && /[eE]/.test(this.currentChar)) {
48
+ while (this.currentChar && /[0-9.]/.test(this.currentChar)) {
60
49
  result += this.currentChar;
61
50
  this.advance();
62
- if (this.currentChar === '+' || this.currentChar === '-') {
63
- result += this.currentChar;
64
- this.advance();
65
- }
66
- if (!/[0-9]/.test(this.currentChar)) this.error('Invalid exponent in number');
67
- while (this.currentChar && /[0-9]/.test(this.currentChar)) {
68
- result += this.currentChar;
69
- this.advance();
70
- }
71
51
  }
72
-
73
52
  return { type: 'NUMBER', value: parseFloat(result) };
74
53
  }
75
54
 
76
55
  identifier() {
77
56
  let result = '';
78
- while (this.currentChar && /[A-Za-z0-9_]/.test(this.currentChar)) {
57
+ while (this.currentChar && /[a-zA-Z0-9_]/.test(this.currentChar)) {
79
58
  result += this.currentChar;
80
59
  this.advance();
81
60
  }
82
-
83
- const keywords = [
84
- 'let', 'sldeploy', 'if', 'else', 'while', 'for',
85
- 'break', 'continue', 'func', 'return',
86
- 'true', 'false', 'null', 'undefined',
87
- 'ask', 'define', 'import', 'from', 'as'
88
- ];
89
-
90
- if (keywords.includes(result)) {
91
- return { type: result.toUpperCase(), value: result };
92
- }
93
-
94
- return { type: 'IDENTIFIER', value: result };
61
+ const type = this.matchKeyword(result) || 'IDENTIFIER';
62
+ return { type, value: result };
95
63
  }
96
64
 
97
- string() {
98
- const quote = this.currentChar;
99
- this.advance();
65
+ string(quoteType) {
66
+ this.advance(); // skip opening quote
100
67
  let result = '';
101
-
102
- while (this.currentChar && this.currentChar !== quote) {
68
+ while (this.currentChar && this.currentChar !== quoteType) {
103
69
  if (this.currentChar === '\\') {
104
70
  this.advance();
105
- const map = { n: '\n', t: '\t', '"': '"', "'": "'", '\\': '\\' };
106
- result += map[this.currentChar] ?? this.currentChar;
71
+ if (this.currentChar) {
72
+ const escapeChars = { n: '\n', r: '\r', t: '\t', '\\': '\\', '"': '"', "'": "'" };
73
+ result += escapeChars[this.currentChar] || this.currentChar;
74
+ this.advance();
75
+ }
107
76
  } else {
108
77
  result += this.currentChar;
78
+ this.advance();
109
79
  }
110
- this.advance();
111
80
  }
112
-
113
- if (this.currentChar !== quote) this.error('Unterminated string literal');
114
-
115
- this.advance();
81
+ this.advance(); // skip closing quote
116
82
  return { type: 'STRING', value: result };
117
83
  }
118
84
 
119
-
120
- templateString(tokens) {
121
- this.advance(); // skip opening `
122
-
123
- let buffer = '';
124
-
85
+ templateLiteral() {
86
+ let result = '';
87
+ const tokens = [];
88
+ this.advance(); // skip initial backtick
125
89
  while (this.currentChar !== null) {
126
- if (this.currentChar === '`') {
127
- if (buffer.length > 0) {
128
- tokens.push({ type: 'TEMPLATE_STRING', value: buffer });
129
- }
130
- this.advance();
131
- return;
132
- }
133
-
134
90
  if (this.currentChar === '$' && this.peek() === '{') {
135
- if (buffer.length > 0) {
136
- tokens.push({ type: 'TEMPLATE_STRING', value: buffer });
137
- buffer = '';
138
- }
139
- this.advance(); // $
140
- this.advance(); // {
91
+ if (result) tokens.push({ type: 'TEMPLATE_STRING', value: result });
92
+ result = '';
141
93
  tokens.push({ type: 'DOLLAR_LBRACE' });
142
- return;
143
- }
144
-
145
- if (this.currentChar === '\\') {
146
94
  this.advance();
147
- buffer += this.currentChar;
95
+ this.advance(); // skip "${"
96
+ } else if (this.currentChar === '`') {
97
+ if (result) tokens.push({ type: 'TEMPLATE_STRING', value: result });
98
+ this.advance();
99
+ break;
100
+ } else {
101
+ result += this.currentChar;
148
102
  this.advance();
149
- continue;
150
103
  }
151
-
152
- buffer += this.currentChar;
153
- this.advance();
154
104
  }
155
-
156
- this.error('Unterminated template string');
105
+ return tokens;
157
106
  }
158
107
 
159
108
  getTokens() {
160
109
  const tokens = [];
161
-
162
110
  while (this.currentChar !== null) {
163
- if (/\s/.test(this.currentChar)) { this.skipWhitespace(); continue; }
164
- if (this.currentChar === '#') { this.skipComment(); continue; }
165
- if (/[0-9]/.test(this.currentChar)) { tokens.push(this.number()); continue; }
166
- if (/[A-Za-z_]/.test(this.currentChar)) { tokens.push(this.identifier()); continue; }
167
- if (this.currentChar === '"' || this.currentChar === "'") { tokens.push(this.string()); continue; }
111
+ this.skipWhitespace();
168
112
 
169
- if (this.currentChar === '`') {
170
- this.templateString(tokens);
171
- continue;
172
- }
173
-
174
- const char = this.currentChar;
175
- const next = this.peek();
176
- const next2 = this.peek(2);
113
+ if (!this.currentChar) break;
177
114
 
178
- if (char === '=' && next === '=' && next2 === '=') { tokens.push({ type: 'STRICT_EQ' }); this.advance(); this.advance(); this.advance(); continue; }
179
- if (char === '!' && next === '=' && next2 === '=') { tokens.push({ type: 'STRICT_NOTEQ' }); this.advance(); this.advance(); this.advance(); continue; }
180
- if (char === '=' && next === '=') { tokens.push({ type: 'EQEQ' }); this.advance(); this.advance(); continue; }
181
- if (char === '=' && next === '>') { tokens.push({ type: 'ARROW' }); this.advance(); this.advance(); continue; }
182
- if (char === '!' && next === '=') { tokens.push({ type: 'NOTEQ' }); this.advance(); this.advance(); continue; }
183
- if (char === '<' && next === '=') { tokens.push({ type: 'LTE' }); this.advance(); this.advance(); continue; }
184
- if (char === '>' && next === '=') { tokens.push({ type: 'GTE' }); this.advance(); this.advance(); continue; }
185
- if (char === '<' && next === '<') { tokens.push({ type: 'LSHIFT' }); this.advance(); this.advance(); continue; }
186
- if (char === '>' && next === '>') { tokens.push({ type: 'RSHIFT' }); this.advance(); this.advance(); continue; }
187
- if (char === '&' && next === '&') { tokens.push({ type: 'AND' }); this.advance(); this.advance(); continue; }
188
- if (char === '|' && next === '|') { tokens.push({ type: 'OR' }); this.advance(); this.advance(); continue; }
189
- if (char === '+' && next === '+') { tokens.push({ type: 'PLUSPLUS' }); this.advance(); this.advance(); continue; }
190
- if (char === '-' && next === '-') { tokens.push({ type: 'MINUSMINUS' }); this.advance(); this.advance(); continue; }
191
-
192
- const compound = { '+': 'PLUSEQ', '-': 'MINUSEQ', '*': 'STAREQ', '/': 'SLASHEQ', '%': 'MODEQ' };
193
- if (next === '=' && compound[char]) {
194
- tokens.push({ type: compound[char] });
195
- this.advance(); this.advance();
196
- continue;
115
+ if (/[0-9]/.test(this.currentChar)) tokens.push(this.number());
116
+ else if (/[a-zA-Z_]/.test(this.currentChar)) tokens.push(this.identifier());
117
+ else if (this.currentChar === '"' || this.currentChar === "'") tokens.push(this.string(this.currentChar));
118
+ else if (this.currentChar === '`') {
119
+ const parts = this.templateLiteral();
120
+ tokens.push(...parts);
197
121
  }
198
-
199
- const singles = {
200
- '+': 'PLUS', '-': 'MINUS', '*': 'STAR', '/': 'SLASH', '%': 'MOD',
201
- '=': 'EQUAL', '<': 'LT', '>': 'GT', '!': 'NOT',
202
- '(': 'LPAREN', ')': 'RPAREN',
203
- '{': 'LBRACE', '}': 'RBRACE',
204
- '[': 'LBRACKET', ']': 'RBRACKET',
205
- ';': 'SEMICOLON', ',': 'COMMA',
206
- ':': 'COLON', '.': 'DOT'
207
- };
208
-
209
- if (singles[char]) {
210
- tokens.push({ type: singles[char] });
122
+ else {
123
+ const char = this.currentChar;
124
+ switch (char) {
125
+ case '+':
126
+ if (this.peek() === '+') { tokens.push({ type: 'PLUSPLUS' }); this.advance(); }
127
+ else if (this.peek() === '=') { tokens.push({ type: 'PLUSEQ' }); this.advance(); }
128
+ else tokens.push({ type: 'PLUS' });
129
+ break;
130
+ case '-':
131
+ if (this.peek() === '-') { tokens.push({ type: 'MINUSMINUS' }); this.advance(); }
132
+ else if (this.peek() === '=') { tokens.push({ type: 'MINUSEQ' }); this.advance(); }
133
+ else tokens.push({ type: 'MINUS' });
134
+ break;
135
+ case '*': tokens.push(this.peek() === '=' ? (this.advance(), { type: 'STAREQ' }) : { type: 'STAR' }); break;
136
+ case '/': tokens.push(this.peek() === '=' ? (this.advance(), { type: 'SLASHEQ' }) : { type: 'SLASH' }); break;
137
+ case '%': tokens.push(this.peek() === '=' ? (this.advance(), { type: 'MODEQ' }) : { type: 'MOD' }); break;
138
+ case '=': tokens.push(this.peek() === '=' ? (this.advance(), this.peek() === '=' ? (this.advance(), { type: 'STRICT_EQ' }) : { type: 'EQEQ' }) : { type: 'EQUAL' }); break;
139
+ case '!': tokens.push(this.peek() === '=' ? (this.advance(), this.peek() === '=' ? (this.advance(), { type: 'STRICT_NOTEQ' }) : { type: 'NOTEQ' }) : { type: 'NOT' }); break;
140
+ case '<': tokens.push(this.peek() === '<' ? (this.advance(), { type: 'LSHIFT' }) : this.peek() === '=' ? (this.advance(), { type: 'LTE' }) : { type: 'LT' }); break;
141
+ case '>': tokens.push(this.peek() === '>' ? (this.advance(), { type: 'RSHIFT' }) : this.peek() === '=' ? (this.advance(), { type: 'GTE' }) : { type: 'GT' }); break;
142
+ case '&': tokens.push(this.peek() === '&' ? (this.advance(), { type: 'AND' }) : { type: 'AMP' }); break;
143
+ case '|': tokens.push(this.peek() === '|' ? (this.advance(), { type: 'OR' }) : { type: 'PIPE' }); break;
144
+ case '(': tokens.push({ type: 'LPAREN' }); break;
145
+ case ')': tokens.push({ type: 'RPAREN' }); break;
146
+ case '{': tokens.push({ type: 'LBRACE' }); break;
147
+ case '}': tokens.push({ type: 'RBRACE' }); break;
148
+ case '[': tokens.push({ type: 'LBRACKET' }); break;
149
+ case ']': tokens.push({ type: 'RBRACKET' }); break;
150
+ case ',': tokens.push({ type: 'COMMA' }); break;
151
+ case ';': tokens.push({ type: 'SEMICOLON' }); break;
152
+ case '.': tokens.push({ type: 'DOT' }); break;
153
+ case ':': tokens.push({ type: 'COLON' }); break;
154
+ case '*': tokens.push({ type: 'STAR' }); break;
155
+ case '$': tokens.push({ type: 'DOLLAR' }); break;
156
+ case '?': tokens.push({ type: 'QUESTION' }); break;
157
+ default: throw new Error(`Unexpected character: ${char} at position ${this.pos}`);
158
+ }
211
159
  this.advance();
212
- continue;
213
160
  }
214
-
215
- this.error(`Unexpected character: ${char}`);
216
161
  }
217
-
218
162
  tokens.push({ type: 'EOF' });
219
163
  return tokens;
220
164
  }
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.20';
12
+ const VERSION = '1.0.21';
13
13
 
14
14
  const COLOR = {
15
15
  reset: '\x1b[0m',