epoxylang 0.1.7 → 0.1.9
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/examples/hybrid.epx +3 -5
- package/examples/square.epx +1 -3
- package/examples/test.epx +5 -4
- package/package.json +1 -1
- package/src/generator/jsgenerator.js +14 -2
- package/src/lexer/lexer.js +16 -20
- package/src/parser/ast.js +2 -1
- package/src/parser/parser.js +35 -13
package/examples/hybrid.epx
CHANGED
package/examples/square.epx
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
make square[n] {
|
|
2
2
|
give n * n;
|
|
3
3
|
}
|
|
4
|
-
|
|
5
4
|
assign nums as array = {2, 4, 6};
|
|
6
|
-
|
|
7
|
-
repeat[x in 0 to 2, 1]{
|
|
5
|
+
repeat[x in nums]{
|
|
8
6
|
assign result as int = call square[nums{x}]; $ yaha pe comment dal rhe hai bhaii..
|
|
9
7
|
store msg = `square of [nums{x}] is [result]`;
|
|
10
8
|
show msg;
|
package/examples/test.epx
CHANGED
package/package.json
CHANGED
|
@@ -205,13 +205,25 @@ class JSCodeGenerator {
|
|
|
205
205
|
|
|
206
206
|
visitRepeatFor(node) {
|
|
207
207
|
const v = node.varName;
|
|
208
|
+
const body = node.body.map(s => this.visit(s)).join("\n");
|
|
209
|
+
|
|
210
|
+
// Handle array iteration: repeat[x in arrayName]
|
|
211
|
+
if (node.arrayName) {
|
|
212
|
+
return `
|
|
213
|
+
for (let ${v} = 0; ${v} < ${node.arrayName}.length; ${v}++) {
|
|
214
|
+
${body}
|
|
215
|
+
}`.trim();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Handle numeric range with bidirectional support
|
|
208
219
|
const start = this.visit(node.start);
|
|
209
220
|
const end = this.visit(node.end);
|
|
210
221
|
const step = this.visit(node.step);
|
|
211
|
-
const body = node.body.map(s => this.visit(s)).join("\n");
|
|
212
222
|
|
|
223
|
+
// Generate bidirectional loop: compare start and end at runtime
|
|
224
|
+
// If start <= end: increment, else: decrement
|
|
213
225
|
return `
|
|
214
|
-
for (let ${v} = ${start}; ${v} <= ${end}; ${v} += ${step}) {
|
|
226
|
+
for (let ${v} = ${start}; ${start} <= ${end} ? ${v} <= ${end} : ${v} >= ${end}; ${start} <= ${end} ? ${v} += ${step} : ${v} -= ${step}) {
|
|
215
227
|
${body}
|
|
216
228
|
}`.trim();
|
|
217
229
|
}
|
package/src/lexer/lexer.js
CHANGED
|
@@ -88,7 +88,7 @@ class Lexer {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
readJSBlock() {
|
|
91
|
-
// We're at '@', check if next chars are 'js
|
|
91
|
+
// We're at '@', check if next chars are 'js~'
|
|
92
92
|
this.advance(); // skip '@'
|
|
93
93
|
|
|
94
94
|
// Read 'js'
|
|
@@ -102,36 +102,32 @@ class Lexer {
|
|
|
102
102
|
}
|
|
103
103
|
this.advance();
|
|
104
104
|
|
|
105
|
-
// Skip any whitespace before
|
|
105
|
+
// Skip any whitespace before ~
|
|
106
106
|
this.skipWhitespace();
|
|
107
|
+
if (this.current !== ':') {
|
|
108
|
+
throw new Error("Expected ':' after '@js'");
|
|
109
|
+
}
|
|
110
|
+
this.advance();
|
|
107
111
|
|
|
108
|
-
if (this.current !== '
|
|
109
|
-
throw new Error("Expected '
|
|
112
|
+
if (this.current !== '~') {
|
|
113
|
+
throw new Error("Expected '~' after '@js'");
|
|
110
114
|
}
|
|
111
|
-
this.advance(); // skip '
|
|
115
|
+
this.advance(); // skip opening '~'
|
|
112
116
|
|
|
113
|
-
// Capture everything until
|
|
117
|
+
// Capture everything until closing '~'
|
|
114
118
|
let code = "";
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
while (this.current && braceDepth > 0) {
|
|
118
|
-
if (this.current === '{') {
|
|
119
|
-
braceDepth++;
|
|
120
|
-
} else if (this.current === '}') {
|
|
121
|
-
braceDepth--;
|
|
122
|
-
if (braceDepth === 0) {
|
|
123
|
-
break; // Don't include the closing brace
|
|
124
|
-
}
|
|
125
|
-
}
|
|
119
|
+
|
|
120
|
+
while (this.current && this.current !== '~' && this.peek !== ':') {
|
|
126
121
|
code += this.current;
|
|
127
122
|
this.advance();
|
|
128
123
|
}
|
|
129
124
|
|
|
130
|
-
if (
|
|
131
|
-
throw new Error("
|
|
125
|
+
if (this.current !== '~' && this.peek !== ':') {
|
|
126
|
+
throw new Error("Unterminated @js block - missing closing '~:'");
|
|
132
127
|
}
|
|
133
128
|
|
|
134
|
-
this.advance(); // skip
|
|
129
|
+
this.advance(); // skip closing '~'
|
|
130
|
+
this.advance(); // skip closing ':'
|
|
135
131
|
|
|
136
132
|
return new Token(TokenType.JSBLOCK, code);
|
|
137
133
|
}
|
package/src/parser/ast.js
CHANGED
|
@@ -121,13 +121,14 @@ class ShowStatement {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
class RepeatFor {
|
|
124
|
-
constructor(varName, start, end, step, body) {
|
|
124
|
+
constructor(varName, start, end, step, body, arrayName = null) {
|
|
125
125
|
this.type = "RepeatFor";
|
|
126
126
|
this.varName = varName;
|
|
127
127
|
this.start = start;
|
|
128
128
|
this.end = end;
|
|
129
129
|
this.step = step;
|
|
130
130
|
this.body = body;
|
|
131
|
+
this.arrayName = arrayName; // For array iteration: repeat[x in arrayName]
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
|
package/src/parser/parser.js
CHANGED
|
@@ -542,23 +542,45 @@ class Parser {
|
|
|
542
542
|
this.eat(TokenType.IDENTIFIER);
|
|
543
543
|
|
|
544
544
|
this.eat(TokenType.IN);
|
|
545
|
-
const start = this.parseExpression();
|
|
546
|
-
this.eat(TokenType.TO);
|
|
547
|
-
const end = this.parseExpression();
|
|
548
545
|
|
|
549
|
-
this
|
|
550
|
-
|
|
546
|
+
// Check if this is array iteration: repeat[x in arrayName]
|
|
547
|
+
// or numeric range: repeat[i in 0 to 5, 1]
|
|
548
|
+
const firstToken = this.current();
|
|
551
549
|
|
|
552
|
-
this.
|
|
553
|
-
|
|
550
|
+
if (firstToken.type === TokenType.IDENTIFIER && this.peekType(1) === TokenType.RBRACKET) {
|
|
551
|
+
// Array iteration: repeat[x in arrayName]
|
|
552
|
+
const arrayName = firstToken.value;
|
|
553
|
+
this.eat(TokenType.IDENTIFIER);
|
|
554
|
+
this.eat(TokenType.RBRACKET);
|
|
555
|
+
this.eat(TokenType.LBRACE);
|
|
554
556
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
557
|
+
const body = [];
|
|
558
|
+
while (this.current().type !== TokenType.RBRACE) {
|
|
559
|
+
body.push(this.parseStatement());
|
|
560
|
+
}
|
|
561
|
+
this.eat(TokenType.RBRACE);
|
|
562
|
+
|
|
563
|
+
return new RepeatFor(varName, null, null, null, body, arrayName);
|
|
564
|
+
} else {
|
|
565
|
+
// Numeric range: repeat[i in 0 to 5, 1]
|
|
566
|
+
const start = this.parseExpression();
|
|
567
|
+
this.eat(TokenType.TO);
|
|
568
|
+
const end = this.parseExpression();
|
|
569
|
+
|
|
570
|
+
this.eat(TokenType.COMMA);
|
|
571
|
+
const step = this.parseExpression();
|
|
572
|
+
|
|
573
|
+
this.eat(TokenType.RBRACKET);
|
|
574
|
+
this.eat(TokenType.LBRACE);
|
|
560
575
|
|
|
561
|
-
|
|
576
|
+
const body = [];
|
|
577
|
+
while (this.current().type !== TokenType.RBRACE) {
|
|
578
|
+
body.push(this.parseStatement());
|
|
579
|
+
}
|
|
580
|
+
this.eat(TokenType.RBRACE);
|
|
581
|
+
|
|
582
|
+
return new RepeatFor(varName, start, end, step, body);
|
|
583
|
+
}
|
|
562
584
|
}
|
|
563
585
|
|
|
564
586
|
parseShow() {
|