epoxylang 0.1.4 → 0.1.6
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 +8 -0
- package/examples/test.epx +5 -7
- package/package.json +1 -1
- package/src/generator/jsgenerator.js +7 -0
- package/src/lexer/lexer.js +52 -0
- package/src/lexer/tokens.js +3 -0
- package/src/parser/ast.js +9 -1
- package/src/parser/parser.js +9 -1
- package/src/runtime/runner.js +1 -1
- /package/examples/{demo.epx → square.epx} +0 -0
package/examples/test.epx
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
show hello{0};
|
|
3
|
-
|
|
4
|
-
assign numbers as array = {1, 2, 3, 4, 5, 6};
|
|
5
|
-
|
|
1
|
+
assign numbers as array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
|
6
2
|
repeat[i in 0 to 5, 1]{
|
|
7
3
|
assign n as int = numbers{i};
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
@js {
|
|
5
|
+
if(n % 2 == 0){
|
|
6
|
+
console.log(n);
|
|
7
|
+
}
|
|
10
8
|
}
|
|
11
9
|
}
|
package/package.json
CHANGED
|
@@ -19,6 +19,8 @@ class JSCodeGenerator {
|
|
|
19
19
|
return this.visitAssignStatement(node);
|
|
20
20
|
case "UpdateStatement":
|
|
21
21
|
return this.visitUpdateStatement(node);
|
|
22
|
+
case "RawJSBlock":
|
|
23
|
+
return this.visitRawJSBlock(node);
|
|
22
24
|
case "BinaryExpression": return this.visitBinaryExpression(node);
|
|
23
25
|
case "Literal": return this.visitLiteral(node);
|
|
24
26
|
case "Identifier": return this.visitIdentifier(node);
|
|
@@ -63,6 +65,11 @@ class JSCodeGenerator {
|
|
|
63
65
|
return `${node.name} = ${value};`;
|
|
64
66
|
}
|
|
65
67
|
|
|
68
|
+
visitRawJSBlock(node) {
|
|
69
|
+
// Return the raw JavaScript code as-is
|
|
70
|
+
return node.code;
|
|
71
|
+
}
|
|
72
|
+
|
|
66
73
|
visitBinaryExpression(node) {
|
|
67
74
|
const left = this.visit(node.left);
|
|
68
75
|
const right = this.visit(node.right);
|
package/src/lexer/lexer.js
CHANGED
|
@@ -87,6 +87,55 @@ class Lexer {
|
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
readJSBlock() {
|
|
91
|
+
// We're at '@', check if next chars are 'js{'
|
|
92
|
+
this.advance(); // skip '@'
|
|
93
|
+
|
|
94
|
+
// Read 'js'
|
|
95
|
+
if (this.current !== 'j') {
|
|
96
|
+
throw new Error("Expected 'js' after '@'");
|
|
97
|
+
}
|
|
98
|
+
this.advance();
|
|
99
|
+
|
|
100
|
+
if (this.current !== 's') {
|
|
101
|
+
throw new Error("Expected 'js' after '@'");
|
|
102
|
+
}
|
|
103
|
+
this.advance();
|
|
104
|
+
|
|
105
|
+
// Skip any whitespace before {
|
|
106
|
+
this.skipWhitespace();
|
|
107
|
+
|
|
108
|
+
if (this.current !== '{') {
|
|
109
|
+
throw new Error("Expected '{' after '@js'");
|
|
110
|
+
}
|
|
111
|
+
this.advance(); // skip '{'
|
|
112
|
+
|
|
113
|
+
// Capture everything until matching '}'
|
|
114
|
+
let code = "";
|
|
115
|
+
let braceDepth = 1;
|
|
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
|
+
}
|
|
126
|
+
code += this.current;
|
|
127
|
+
this.advance();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (braceDepth !== 0) {
|
|
131
|
+
throw new Error("Unmatched braces in @js block");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.advance(); // skip final '}'
|
|
135
|
+
|
|
136
|
+
return new Token(TokenType.JSBLOCK, code);
|
|
137
|
+
}
|
|
138
|
+
|
|
90
139
|
getNextToken() {
|
|
91
140
|
while (this.current) {
|
|
92
141
|
if (/\s/.test(this.current)) {
|
|
@@ -102,6 +151,9 @@ class Lexer {
|
|
|
102
151
|
continue;
|
|
103
152
|
}
|
|
104
153
|
|
|
154
|
+
// @js blocks for raw JavaScript
|
|
155
|
+
if (this.current === "@") return this.readJSBlock();
|
|
156
|
+
|
|
105
157
|
if (/[0-9]/.test(this.current)) return this.readNumber();
|
|
106
158
|
if (/[a-zA-Z_]/.test(this.current)) return this.readWord();
|
|
107
159
|
if (`'"\``.includes(this.current)) return this.readString();
|
package/src/lexer/tokens.js
CHANGED
package/src/parser/ast.js
CHANGED
|
@@ -131,6 +131,13 @@ class RepeatFor {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
class RawJSBlock {
|
|
135
|
+
constructor(code) {
|
|
136
|
+
this.type = "RawJSBlock";
|
|
137
|
+
this.code = code;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
134
141
|
export {
|
|
135
142
|
Program,
|
|
136
143
|
AssignStatement,
|
|
@@ -147,5 +154,6 @@ export {
|
|
|
147
154
|
CallExpression,
|
|
148
155
|
RepeatUntil,
|
|
149
156
|
ShowStatement,
|
|
150
|
-
RepeatFor
|
|
157
|
+
RepeatFor,
|
|
158
|
+
RawJSBlock
|
|
151
159
|
};
|
package/src/parser/parser.js
CHANGED
|
@@ -14,7 +14,8 @@ import {
|
|
|
14
14
|
CallExpression,
|
|
15
15
|
RepeatUntil,
|
|
16
16
|
ShowStatement,
|
|
17
|
-
RepeatFor
|
|
17
|
+
RepeatFor,
|
|
18
|
+
RawJSBlock
|
|
18
19
|
} from "./ast.js";
|
|
19
20
|
|
|
20
21
|
class Parser {
|
|
@@ -84,6 +85,7 @@ class Parser {
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
if (t === TokenType.UPDATE) return this.parseUpdate();
|
|
88
|
+
if (t === TokenType.JSBLOCK) return this.parseJSBlock();
|
|
87
89
|
if (t === TokenType.CHECK) return this.parseCheck();
|
|
88
90
|
if (t === TokenType.MAKE) return this.parseMake();
|
|
89
91
|
if (t === TokenType.CALL) return this.parseCall();
|
|
@@ -205,6 +207,12 @@ class Parser {
|
|
|
205
207
|
return new UpdateStatement({ name, value });
|
|
206
208
|
}
|
|
207
209
|
|
|
210
|
+
parseJSBlock() {
|
|
211
|
+
const code = this.current().value;
|
|
212
|
+
this.eat(TokenType.JSBLOCK);
|
|
213
|
+
return new RawJSBlock(code);
|
|
214
|
+
}
|
|
215
|
+
|
|
208
216
|
parseValue() {
|
|
209
217
|
const token = this.current();
|
|
210
218
|
|
package/src/runtime/runner.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Lexer } from "../lexer/lexer.js";
|
|
2
2
|
import { TokenType } from "../lexer/tokens.js";
|
|
3
3
|
import { Parser } from "../parser/parser.js";
|
|
4
|
-
import { JSCodeGenerator } from "../generator/
|
|
4
|
+
import { JSCodeGenerator } from "../generator/jsgenerator.js";
|
|
5
5
|
|
|
6
6
|
function tokenizeAll(code) {
|
|
7
7
|
const lexer = new Lexer(code);
|
|
File without changes
|