epoxylang 0.1.10 → 0.1.12

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.
@@ -0,0 +1,86 @@
1
+ {
2
+ "comments": {
3
+ "lineComment": "$"
4
+ },
5
+ "brackets": [
6
+ [
7
+ "{",
8
+ "}"
9
+ ],
10
+ [
11
+ "[",
12
+ "]"
13
+ ],
14
+ [
15
+ "(",
16
+ ")"
17
+ ]
18
+ ],
19
+ "autoClosingPairs": [
20
+ {
21
+ "open": "{",
22
+ "close": "}"
23
+ },
24
+ {
25
+ "open": "[",
26
+ "close": "]"
27
+ },
28
+ {
29
+ "open": "(",
30
+ "close": ")"
31
+ },
32
+ {
33
+ "open": "\"",
34
+ "close": "\""
35
+ },
36
+ {
37
+ "open": "'",
38
+ "close": "'"
39
+ },
40
+ {
41
+ "open": "`",
42
+ "close": "`"
43
+ },
44
+ {
45
+ "open": "@js :~",
46
+ "close": "~:"
47
+ }
48
+ ],
49
+ "surroundingPairs": [
50
+ [
51
+ "{",
52
+ "}"
53
+ ],
54
+ [
55
+ "[",
56
+ "]"
57
+ ],
58
+ [
59
+ "(",
60
+ ")"
61
+ ],
62
+ [
63
+ "\"",
64
+ "\""
65
+ ],
66
+ [
67
+ "'",
68
+ "'"
69
+ ],
70
+ [
71
+ "`",
72
+ "`"
73
+ ]
74
+ ],
75
+ "folding": {
76
+ "markers": {
77
+ "start": "^\\s*\\{",
78
+ "end": "^\\s*\\}"
79
+ }
80
+ },
81
+ "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)",
82
+ "indentationRules": {
83
+ "increaseIndentPattern": "^.*\\{[^}]*$",
84
+ "decreaseIndentPattern": "^\\s*\\}"
85
+ }
86
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "epoxy-language",
3
+ "displayName": "Epoxy Language",
4
+ "description": "Syntax highlighting for the Epoxy programming language",
5
+ "version": "0.1.0",
6
+ "publisher": "iamsatyanchal",
7
+ "engines": {
8
+ "vscode": "^1.75.0"
9
+ },
10
+ "categories": ["Programming Languages"],
11
+ "contributes": {
12
+ "languages": [
13
+ {
14
+ "id": "epoxy",
15
+ "aliases": ["Epoxy", "epx"],
16
+ "extensions": [".epx"],
17
+ "configuration": "./language-configuration.json"
18
+ }
19
+ ],
20
+ "grammars": [
21
+ {
22
+ "language": "epoxy",
23
+ "scopeName": "source.epoxy",
24
+ "path": "./syntaxes/epoxy.tmLanguage.json"
25
+ }
26
+ ]
27
+ }
28
+ }
@@ -0,0 +1,280 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
3
+ "name": "Epoxy",
4
+ "scopeName": "source.epoxy",
5
+ "patterns": [
6
+ { "include": "#comments" },
7
+ { "include": "#raw-js-block" },
8
+ { "include": "#keywords" },
9
+ { "include": "#types" },
10
+ { "include": "#constants" },
11
+ { "include": "#strings" },
12
+ { "include": "#numbers" },
13
+ { "include": "#operators" },
14
+ { "include": "#punctuation" },
15
+ { "include": "#special-input" },
16
+ { "include": "#function-declaration" },
17
+ { "include": "#function-call" },
18
+ { "include": "#identifiers" }
19
+ ],
20
+ "repository": {
21
+ "comments": {
22
+ "patterns": [
23
+ {
24
+ "name": "comment.line.dollar.epoxy",
25
+ "match": "\\$.*$",
26
+ "captures": {
27
+ "0": { "name": "punctuation.definition.comment.epoxy" }
28
+ }
29
+ }
30
+ ]
31
+ },
32
+ "raw-js-block": {
33
+ "patterns": [
34
+ {
35
+ "name": "meta.embedded.block.javascript",
36
+ "begin": "(@js)\\s*(:~)",
37
+ "end": "(~:)",
38
+ "beginCaptures": {
39
+ "1": { "name": "keyword.control.js.epoxy" },
40
+ "2": { "name": "punctuation.definition.js-block.begin.epoxy" }
41
+ },
42
+ "endCaptures": {
43
+ "1": { "name": "punctuation.definition.js-block.end.epoxy" }
44
+ },
45
+ "patterns": [
46
+ { "include": "source.js" }
47
+ ]
48
+ }
49
+ ]
50
+ },
51
+ "keywords": {
52
+ "patterns": [
53
+ {
54
+ "name": "keyword.control.flow.epoxy",
55
+ "match": "\\b(check|alt|repeat|until|give)\\b"
56
+ },
57
+ {
58
+ "name": "keyword.control.loop.epoxy",
59
+ "match": "\\b(in|to)\\b"
60
+ },
61
+ {
62
+ "name": "keyword.declaration.epoxy",
63
+ "match": "\\b(assign|store|make|update)\\b"
64
+ },
65
+ {
66
+ "name": "keyword.modifier.epoxy",
67
+ "match": "\\b(all|fix)\\b"
68
+ },
69
+ {
70
+ "name": "keyword.other.epoxy",
71
+ "match": "\\b(call|show|as)\\b"
72
+ },
73
+ {
74
+ "name": "keyword.operator.logical.epoxy",
75
+ "match": "\\b(and|or)\\b"
76
+ }
77
+ ]
78
+ },
79
+ "types": {
80
+ "patterns": [
81
+ {
82
+ "name": "storage.type.epoxy",
83
+ "match": "\\b(int|double|string|bool|array|object)\\b"
84
+ }
85
+ ]
86
+ },
87
+ "constants": {
88
+ "patterns": [
89
+ {
90
+ "name": "constant.language.boolean.true.epoxy",
91
+ "match": "\\btrue\\b"
92
+ },
93
+ {
94
+ "name": "constant.language.boolean.false.epoxy",
95
+ "match": "\\bfalse\\b"
96
+ },
97
+ {
98
+ "name": "constant.language.null.epoxy",
99
+ "match": "\\bnull\\b"
100
+ },
101
+ {
102
+ "name": "constant.language.undefined.epoxy",
103
+ "match": "\\bundefined\\b"
104
+ }
105
+ ]
106
+ },
107
+ "strings": {
108
+ "patterns": [
109
+ {
110
+ "name": "string.quoted.backtick.epoxy",
111
+ "begin": "`",
112
+ "end": "`",
113
+ "patterns": [
114
+ {
115
+ "name": "meta.interpolation.epoxy",
116
+ "begin": "\\[",
117
+ "end": "\\]",
118
+ "beginCaptures": {
119
+ "0": { "name": "punctuation.definition.interpolation.begin.epoxy" }
120
+ },
121
+ "endCaptures": {
122
+ "0": { "name": "punctuation.definition.interpolation.end.epoxy" }
123
+ },
124
+ "patterns": [
125
+ { "include": "#identifiers" },
126
+ { "include": "#function-call" },
127
+ { "include": "#array-access" },
128
+ { "include": "#numbers" }
129
+ ]
130
+ },
131
+ {
132
+ "name": "constant.character.escape.epoxy",
133
+ "match": "\\\\."
134
+ }
135
+ ]
136
+ },
137
+ {
138
+ "name": "string.quoted.double.epoxy",
139
+ "begin": "\"",
140
+ "end": "\"",
141
+ "patterns": [
142
+ {
143
+ "name": "constant.character.escape.epoxy",
144
+ "match": "\\\\."
145
+ }
146
+ ]
147
+ },
148
+ {
149
+ "name": "string.quoted.single.epoxy",
150
+ "begin": "'",
151
+ "end": "'",
152
+ "patterns": [
153
+ {
154
+ "name": "constant.character.escape.epoxy",
155
+ "match": "\\\\."
156
+ }
157
+ ]
158
+ }
159
+ ]
160
+ },
161
+ "numbers": {
162
+ "patterns": [
163
+ {
164
+ "name": "constant.numeric.decimal.epoxy",
165
+ "match": "\\b\\d+\\.\\d+\\b"
166
+ },
167
+ {
168
+ "name": "constant.numeric.integer.epoxy",
169
+ "match": "\\b\\d+\\b"
170
+ }
171
+ ]
172
+ },
173
+ "operators": {
174
+ "patterns": [
175
+ {
176
+ "name": "keyword.operator.comparison.epoxy",
177
+ "match": "(===|!==|==|!=|>=|<=|>|<)"
178
+ },
179
+ {
180
+ "name": "keyword.operator.arithmetic.epoxy",
181
+ "match": "(\\+|\\-|\\*|\\/)"
182
+ },
183
+ {
184
+ "name": "keyword.operator.assignment.epoxy",
185
+ "match": "="
186
+ }
187
+ ]
188
+ },
189
+ "punctuation": {
190
+ "patterns": [
191
+ {
192
+ "name": "punctuation.terminator.statement.epoxy",
193
+ "match": ";"
194
+ },
195
+ {
196
+ "name": "punctuation.separator.comma.epoxy",
197
+ "match": ","
198
+ },
199
+ {
200
+ "name": "punctuation.section.block.begin.epoxy",
201
+ "match": "\\{"
202
+ },
203
+ {
204
+ "name": "punctuation.section.block.end.epoxy",
205
+ "match": "\\}"
206
+ },
207
+ {
208
+ "name": "punctuation.section.brackets.begin.epoxy",
209
+ "match": "\\["
210
+ },
211
+ {
212
+ "name": "punctuation.section.brackets.end.epoxy",
213
+ "match": "\\]"
214
+ },
215
+ {
216
+ "name": "punctuation.section.parens.begin.epoxy",
217
+ "match": "\\("
218
+ },
219
+ {
220
+ "name": "punctuation.section.parens.end.epoxy",
221
+ "match": "\\)"
222
+ }
223
+ ]
224
+ },
225
+ "special-input": {
226
+ "patterns": [
227
+ {
228
+ "name": "variable.language.input.epoxy",
229
+ "match": ":input\\b"
230
+ }
231
+ ]
232
+ },
233
+ "function-declaration": {
234
+ "patterns": [
235
+ {
236
+ "name": "meta.function.epoxy",
237
+ "match": "\\b(make)\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*(\\[)",
238
+ "captures": {
239
+ "1": { "name": "keyword.declaration.epoxy" },
240
+ "2": { "name": "entity.name.function.epoxy" },
241
+ "3": { "name": "punctuation.section.brackets.begin.epoxy" }
242
+ }
243
+ }
244
+ ]
245
+ },
246
+ "function-call": {
247
+ "patterns": [
248
+ {
249
+ "name": "meta.function-call.epoxy",
250
+ "match": "\\b(call)\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*(\\[)",
251
+ "captures": {
252
+ "1": { "name": "keyword.other.epoxy" },
253
+ "2": { "name": "entity.name.function.epoxy" },
254
+ "3": { "name": "punctuation.section.brackets.begin.epoxy" }
255
+ }
256
+ }
257
+ ]
258
+ },
259
+ "array-access": {
260
+ "patterns": [
261
+ {
262
+ "name": "meta.array-access.epoxy",
263
+ "match": "([a-zA-Z_][a-zA-Z0-9_]*)\\s*(\\{)",
264
+ "captures": {
265
+ "1": { "name": "variable.other.epoxy" },
266
+ "2": { "name": "punctuation.section.braces.begin.epoxy" }
267
+ }
268
+ }
269
+ ]
270
+ },
271
+ "identifiers": {
272
+ "patterns": [
273
+ {
274
+ "name": "variable.other.epoxy",
275
+ "match": "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b"
276
+ }
277
+ ]
278
+ }
279
+ }
280
+ }
@@ -3,16 +3,24 @@ assign numbers as array = {};
3
3
  repeat[x in 0 to 4, 1]{
4
4
  show "Enter a number: ";
5
5
  assign num_insert = :input;
6
- @js :~
7
- numbers.push(num_insert);
8
- ~:
6
+ method:array numbers.append[num_insert];
9
7
  }
10
8
  show "The numbers you entered are: " + numbers;
11
- repeat[i in numbers]{
12
- assign n as int = numbers{i};
13
- @js :~
14
- if(n % 2 == 0){
15
- console.log(n);
16
- }
17
- ~:
18
- }
9
+
10
+
11
+
12
+
13
+ $ make test[name]{
14
+ $ all assign ok as string = :input;
15
+ $ fix assign tes as int = 3.14159;
16
+ $ store bubu = `[ok] and tes is [tes]`;
17
+ $ give bubu;
18
+ $ }
19
+ $ repeat[i in numbers]{
20
+ $ assign n as int = numbers{i};
21
+ $ @js :~
22
+ $ if(n % 2 == 0){
23
+ $ console.log(n);
24
+ $ }
25
+ $ ~:
26
+ $ }
@@ -0,0 +1,48 @@
1
+ $ Test array methods
2
+ assign numbers as array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
3
+ show "Original array:";
4
+ show numbers;
5
+
6
+ $ Test filter with lambda and modulo
7
+ assign evens as array = method:array numbers.filter[call [x] -> x % 2 == 0];
8
+ show "Even numbers (filter):";
9
+ show evens;
10
+
11
+ $ Test map with lambda
12
+ assign doubled as array = method:array numbers.map[call [x] -> x * 2];
13
+ show "Doubled numbers (map):";
14
+ show doubled;
15
+
16
+ $ Test slice - reverse array (note: slice takes string literal)
17
+ assign reversed as array = method:array numbers.slice["::-1"];
18
+ show "Reversed array:";
19
+ show reversed;
20
+
21
+ $ Test slice - every 2nd element
22
+ assign everynd as array = method:array numbers.slice["::2"];
23
+ show "Every 2nd element:";
24
+ show everynd;
25
+
26
+ $ Test slice - range
27
+ assign range as array = method:array numbers.slice["2:5"];
28
+ show "Elements 2-4:";
29
+ show range;
30
+
31
+ $ Test string methods
32
+ assign message as string = "Hello World";
33
+ assign upper as string = method:string message.upper[];
34
+ show "Uppercase:";
35
+ show upper;
36
+
37
+ assign lower as string = method:string message.lower[];
38
+ show "Lowercase:";
39
+ show lower;
40
+ show method:string lower.size[];
41
+ show method:string lower.replace["world", "epoxy"];
42
+
43
+ $ Test modulo operator
44
+ assign remainder as int = 17 % 5;
45
+ assign okko as double = 3.14;
46
+ show okko;
47
+ show "17 % 5 =";
48
+ show remainder;
@@ -2,8 +2,12 @@ make square[n] {
2
2
  give n * n;
3
3
  }
4
4
  assign nums as array = {2, 4, 6};
5
+ assign ok as string;
5
6
  repeat[x in nums]{
6
7
  assign result as int = call square[nums{x}]; $ yaha pe comment dal rhe hai bhaii..
8
+ $ assign add as int = call [x,y] -> give x +y
7
9
  store msg = `square of [nums{x}] is [result]`;
8
10
  show msg;
9
11
  }
12
+ update ok = :input;
13
+ show ok;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "epoxylang",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "epoxy": "./bin/epoxy.js"
@@ -35,6 +35,8 @@ class JSCodeGenerator {
35
35
  case "RepeatUntil": return this.visitRepeatUntil(node);
36
36
  case "ArrayLiteral": return this.visitArrayLiteral(node);
37
37
  case "ArrayAccess": return this.visitArrayAccess(node);
38
+ case "MethodCall": return this.visitMethodCall(node);
39
+ case "LambdaExpression": return this.visitLambdaExpression(node);
38
40
  default:
39
41
  throw new Error("Unknown AST node: " + node.type);
40
42
  }
@@ -91,7 +93,7 @@ class JSCodeGenerator {
91
93
  }
92
94
 
93
95
  visitInputExpression(node) {
94
- return "await input_of_epoxy_lang_dont_use_this_name()";
96
+ return "input_of_epoxy_lang_dont_use_this_name()";
95
97
  }
96
98
 
97
99
  convertInterpolation(text) {
@@ -243,6 +245,140 @@ ${body}
243
245
  } while (!(${condition}));`.trim();
244
246
  }
245
247
 
248
+ visitMethodCall(node) {
249
+ const target = node.target;
250
+ const methodName = node.methodName;
251
+ const args = node.args;
252
+
253
+ // Array methods
254
+ if (node.targetType === "array") {
255
+ switch (methodName) {
256
+ case "append":
257
+ // .append[value] -> .push(value)
258
+ if (args.length !== 1) {
259
+ throw new Error("append requires exactly 1 argument");
260
+ }
261
+ return `${target}.push(${this.visit(args[0])})`;
262
+
263
+ case "pop":
264
+ // .pop[] -> .pop()
265
+ return `${target}.pop()`;
266
+
267
+ case "includes":
268
+ // .includes[value] -> .includes(value)
269
+ if (args.length !== 1) {
270
+ throw new Error("includes requires exactly 1 argument");
271
+ }
272
+ return `${target}.includes(${this.visit(args[0])})`;
273
+
274
+ case "filter":
275
+ // .filter[call [x] -> x % 2 == 0] -> .filter((x) => x % 2 === 0)
276
+ if (args.length !== 1) {
277
+ throw new Error("filter requires exactly 1 argument (lambda function)");
278
+ }
279
+ return `${target}.filter(${this.visit(args[0])})`;
280
+
281
+ case "map":
282
+ // .map[call [x] -> x * 2] -> .map((x) => x * 2)
283
+ if (args.length !== 1) {
284
+ throw new Error("map requires exactly 1 argument (lambda function)");
285
+ }
286
+ return `${target}.map(${this.visit(args[0])})`;
287
+
288
+ case "join":
289
+ // .join[separator] -> .join(separator) or .join() for default
290
+ if (args.length === 0) {
291
+ return `${target}.join()`;
292
+ } else if (args.length === 1) {
293
+ return `${target}.join(${this.visit(args[0])})`;
294
+ } else {
295
+ throw new Error("join requires 0 or 1 argument");
296
+ }
297
+
298
+ case "slice":
299
+ // .slice[pythonSlice] -> .slice(start, end)
300
+ if (args.length !== 1) {
301
+ throw new Error("slice requires exactly 1 argument (slice notation)");
302
+ }
303
+ return this.convertPythonSlice(target, args[0]);
304
+
305
+ default:
306
+ throw new Error(`Unknown array method: ${methodName}`);
307
+ }
308
+ }
309
+
310
+ // String methods
311
+ if (node.targetType === "string") {
312
+ switch (methodName) {
313
+ case "upper":
314
+ // .upper[] -> .toUpperCase()
315
+ return `${target}.toUpperCase()`;
316
+
317
+ case "lower":
318
+ // .lower[] -> .toLowerCase()
319
+ return `${target}.toLowerCase()`;
320
+
321
+ case "size":
322
+ // .size[] -> .length
323
+ return `${target}.length`;
324
+
325
+ case "includes":
326
+ // .includes[value] -> .includes(value)
327
+ if (args.length !== 1) {
328
+ throw new Error("includes requires exactly 1 argument");
329
+ }
330
+ return `${target}.includes(${this.visit(args[0])})`;
331
+
332
+ case "replace":
333
+ // .replace["old" with "new"] -> .replace("old", "new")
334
+ if (args.length !== 2) {
335
+ throw new Error("replace requires exactly 2 arguments (old, new)");
336
+ }
337
+ return `${target}.replace(${this.visit(args[0])}, ${this.visit(args[1])})`;
338
+
339
+ default:
340
+ throw new Error(`Unknown string method: ${methodName}`);
341
+ }
342
+ }
343
+
344
+ throw new Error(`Unknown target type: ${node.targetType}`);
345
+ }
346
+
347
+ visitLambdaExpression(node) {
348
+ // Convert lambda to arrow function: call [x] -> x % 2 == 0 => (x) => x % 2 === 0
349
+ const params = node.params.join(", ");
350
+ const body = this.visit(node.body);
351
+ return `(${params}) => ${body}`;
352
+ }
353
+
354
+ convertPythonSlice(target, sliceExpr) {
355
+ // Handle Python-style slicing: ::2, 2:5, ::-1
356
+ // This is a simplified version - you'd need to parse the slice notation
357
+ // For now, let's assume sliceExpr is a string literal
358
+
359
+ if (sliceExpr.type === "Literal" && typeof sliceExpr.value === "string") {
360
+ const slice = sliceExpr.value;
361
+
362
+ // Parse slice notation
363
+ if (slice === "::-1") {
364
+ // Reverse array
365
+ return `${target}.slice().reverse();`;
366
+ } else if (slice.startsWith("::")) {
367
+ // Every nth element
368
+ const step = parseInt(slice.substring(2));
369
+ return `${target}.filter((_, i) => i % ${step} === 0);`;
370
+ } else if (slice.includes(":")) {
371
+ // Range slice
372
+ const parts = slice.split(":");
373
+ const start = parts[0] || "0";
374
+ const end = parts[1] || `${target}.length`;
375
+ return `${target}.slice(${start}, ${end});`;
376
+ }
377
+ }
378
+
379
+ throw new Error("Invalid slice notation");
380
+ }
381
+
246
382
  }
247
383
 
248
384
  export { JSCodeGenerator };
@@ -170,6 +170,7 @@ class Lexer {
170
170
  // Not :input, restore position
171
171
  this.pos = saved;
172
172
  this.current = this.input[this.pos];
173
+ // If not :input, fall through to handle : as COLON token
173
174
  }
174
175
 
175
176
  // double-char operators
@@ -178,6 +179,7 @@ class Lexer {
178
179
  if (twoChar === "!=") { this.advance(); this.advance(); return new Token(TokenType.NOTEQ); }
179
180
  if (twoChar === ">=") { this.advance(); this.advance(); return new Token(TokenType.GTE); }
180
181
  if (twoChar === "<=") { this.advance(); this.advance(); return new Token(TokenType.LTE); }
182
+ if (twoChar === "->") { this.advance(); this.advance(); return new Token(TokenType.ARROW); }
181
183
 
182
184
  // single-char
183
185
  const single = {
@@ -186,6 +188,7 @@ class Lexer {
186
188
  "-": TokenType.MINUS,
187
189
  "*": TokenType.STAR,
188
190
  "/": TokenType.SLASH,
191
+ "%": TokenType.MODULO,
189
192
  ">": TokenType.GT,
190
193
  "<": TokenType.LT,
191
194
  "{": TokenType.LBRACE,
@@ -195,7 +198,9 @@ class Lexer {
195
198
  "(": TokenType.LPAREN,
196
199
  ")": TokenType.RPAREN,
197
200
  ";": TokenType.SEMICOLON,
198
- ",": TokenType.COMMA
201
+ ",": TokenType.COMMA,
202
+ ".": TokenType.DOT,
203
+ ":": TokenType.COLON
199
204
  };
200
205
 
201
206
  if (single[this.current]) {
@@ -18,6 +18,7 @@ const TokenType = {
18
18
  AND: "AND",
19
19
  AS: "AS",
20
20
  SHOW: "SHOW",
21
+ METHOD: "METHOD",
21
22
 
22
23
  // datatypes
23
24
  TYPE: "TYPE",
@@ -41,12 +42,14 @@ const TokenType = {
41
42
  MINUS: "MINUS",
42
43
  STAR: "STAR",
43
44
  SLASH: "SLASH",
45
+ MODULO: "MODULO",
44
46
  GT: "GT",
45
47
  LT: "LT",
46
48
  GTE: "GTE",
47
49
  LTE: "LTE",
48
50
  EQEQ: "EQEQ",
49
51
  NOTEQ: "NOTEQ",
52
+ ARROW: "ARROW",
50
53
 
51
54
  // symbols
52
55
  LBRACE: "LBRACE",
@@ -57,6 +60,8 @@ const TokenType = {
57
60
  RPAREN: "RPAREN",
58
61
  SEMICOLON: "SEMICOLON",
59
62
  COMMA: "COMMA",
63
+ DOT: "DOT",
64
+ COLON: "COLON",
60
65
 
61
66
  EOF: "EOF"
62
67
  };
@@ -88,6 +93,7 @@ const KEYWORDS = {
88
93
  and: TokenType.AND,
89
94
  as: TokenType.AS,
90
95
  show: TokenType.SHOW,
96
+ method: TokenType.METHOD,
91
97
  };
92
98
 
93
99
  const TYPES = [
@@ -106,6 +112,7 @@ const OP_MAP = {
106
112
  MINUS: "-",
107
113
  STAR: "*",
108
114
  SLASH: "/",
115
+ MODULO: "%",
109
116
  GT: ">",
110
117
  LT: "<",
111
118
  GTE: ">=",
package/src/parser/ast.js CHANGED
@@ -145,6 +145,24 @@ class InputExpression {
145
145
  }
146
146
  }
147
147
 
148
+ class MethodCall {
149
+ constructor(targetType, target, methodName, args) {
150
+ this.type = "MethodCall";
151
+ this.targetType = targetType; // "array" or "string"
152
+ this.target = target; // variable name (Identifier or expression)
153
+ this.methodName = methodName; // method name like "append", "upper"
154
+ this.args = args; // array of argument expressions
155
+ }
156
+ }
157
+
158
+ class LambdaExpression {
159
+ constructor(params, body) {
160
+ this.type = "LambdaExpression";
161
+ this.params = params; // array of parameter names
162
+ this.body = body; // expression
163
+ }
164
+ }
165
+
148
166
  export {
149
167
  Program,
150
168
  AssignStatement,
@@ -163,5 +181,7 @@ export {
163
181
  ShowStatement,
164
182
  RepeatFor,
165
183
  RawJSBlock,
166
- InputExpression
184
+ InputExpression,
185
+ MethodCall,
186
+ LambdaExpression
167
187
  };
@@ -16,7 +16,9 @@ import {
16
16
  ShowStatement,
17
17
  RepeatFor,
18
18
  RawJSBlock,
19
- InputExpression
19
+ InputExpression,
20
+ MethodCall,
21
+ LambdaExpression
20
22
  } from "./ast.js";
21
23
 
22
24
  class Parser {
@@ -94,6 +96,7 @@ class Parser {
94
96
  if (t === TokenType.REPEAT && this.peekType() === TokenType.LBRACKET) return this.parseRepeatFor();
95
97
  if (t === TokenType.REPEAT) return this.parseRepeatUntil();
96
98
  if (t === TokenType.SHOW) return this.parseShow();
99
+ if (t === TokenType.METHOD) return this.parseMethodCall();
97
100
 
98
101
  throw new Error("Unknown statement: " + t);
99
102
  }
@@ -279,10 +282,38 @@ class Parser {
279
282
  return new InputExpression();
280
283
  }
281
284
 
282
- // call expression
285
+ // call expression or lambda
283
286
  if (tok.type === TokenType.CALL) {
284
287
  this.eat(TokenType.CALL);
285
288
 
289
+ // Check if this is a lambda: call [param] -> body
290
+ if (this.current().type === TokenType.LBRACKET) {
291
+ this.eat(TokenType.LBRACKET);
292
+
293
+ // Parse parameters
294
+ const params = [];
295
+ if (this.current().type !== TokenType.RBRACKET) {
296
+ params.push(this.current().value);
297
+ this.eat(TokenType.IDENTIFIER);
298
+
299
+ while (this.current().type === TokenType.COMMA) {
300
+ this.eat(TokenType.COMMA);
301
+ params.push(this.current().value);
302
+ this.eat(TokenType.IDENTIFIER);
303
+ }
304
+ }
305
+
306
+ this.eat(TokenType.RBRACKET);
307
+
308
+ // Check for arrow
309
+ if (this.current().type === TokenType.ARROW) {
310
+ this.eat(TokenType.ARROW);
311
+ const body = this.parseExpression();
312
+ return new LambdaExpression(params, body);
313
+ }
314
+ }
315
+
316
+ // Regular function call: call functionName[args]
286
317
  const name = this.current().value;
287
318
  this.eat(TokenType.IDENTIFIER);
288
319
 
@@ -337,6 +368,41 @@ class Parser {
337
368
  return node;
338
369
  }
339
370
 
371
+ // method call as expression
372
+ if (tok.type === TokenType.METHOD) {
373
+ this.eat(TokenType.METHOD);
374
+ this.eat(TokenType.COLON);
375
+
376
+ // Get the target type (array or string)
377
+ const targetType = this.current().value;
378
+ this.eat(TokenType.TYPE);
379
+
380
+ // Get the target variable
381
+ const target = this.current().value;
382
+ this.eat(TokenType.IDENTIFIER);
383
+
384
+ this.eat(TokenType.DOT);
385
+
386
+ // Get the method name
387
+ const methodName = this.current().value;
388
+ this.eat(TokenType.IDENTIFIER);
389
+
390
+ // Parse arguments in brackets
391
+ this.eat(TokenType.LBRACKET);
392
+ const args = [];
393
+
394
+ if (this.current().type !== TokenType.RBRACKET) {
395
+ args.push(this.parseExpression());
396
+ while (this.current().type === TokenType.COMMA) {
397
+ this.eat(TokenType.COMMA);
398
+ args.push(this.parseExpression());
399
+ }
400
+ }
401
+
402
+ this.eat(TokenType.RBRACKET);
403
+
404
+ return new MethodCall(targetType, target, methodName, args);
405
+ }
340
406
 
341
407
  throw new Error("Invalid primary expression: " + tok.type);
342
408
  }
@@ -346,7 +412,8 @@ class Parser {
346
412
 
347
413
  while (
348
414
  this.current().type === TokenType.STAR ||
349
- this.current().type === TokenType.SLASH
415
+ this.current().type === TokenType.SLASH ||
416
+ this.current().type === TokenType.MODULO
350
417
  ) {
351
418
  const op = this.current().type;
352
419
  this.pos++;
@@ -601,6 +668,42 @@ class Parser {
601
668
  return new ShowStatement(value);
602
669
  }
603
670
 
671
+ parseMethodCall() {
672
+ this.eat(TokenType.METHOD);
673
+ this.eat(TokenType.COLON);
674
+
675
+ // Get the target type (array or string)
676
+ const targetType = this.current().value;
677
+ this.eat(TokenType.TYPE);
678
+
679
+ // Get the target variable
680
+ const target = this.current().value;
681
+ this.eat(TokenType.IDENTIFIER);
682
+
683
+ this.eat(TokenType.DOT);
684
+
685
+ // Get the method name
686
+ const methodName = this.current().value;
687
+ this.eat(TokenType.IDENTIFIER);
688
+
689
+ // Parse arguments in brackets
690
+ this.eat(TokenType.LBRACKET);
691
+ const args = [];
692
+
693
+ if (this.current().type !== TokenType.RBRACKET) {
694
+ args.push(this.parseExpression());
695
+ while (this.current().type === TokenType.COMMA) {
696
+ this.eat(TokenType.COMMA);
697
+ args.push(this.parseExpression());
698
+ }
699
+ }
700
+
701
+ this.eat(TokenType.RBRACKET);
702
+ this.eat(TokenType.SEMICOLON);
703
+
704
+ return new MethodCall(targetType, target, methodName, args);
705
+ }
706
+
604
707
  }
605
708
 
606
709
  export { Parser };
@@ -2,6 +2,9 @@ import { Lexer } from "../lexer/lexer.js";
2
2
  import { TokenType } from "../lexer/tokens.js";
3
3
  import { Parser } from "../parser/parser.js";
4
4
  import { JSCodeGenerator } from "../generator/jsgenerator.js";
5
+ import { createRequire } from "module";
6
+
7
+ const require = createRequire(import.meta.url);
5
8
 
6
9
  function tokenizeAll(code) {
7
10
  const lexer = new Lexer(code);
@@ -10,7 +13,6 @@ function tokenizeAll(code) {
10
13
  do {
11
14
  t = lexer.getNextToken();
12
15
  tokens.push(t);
13
- //console.log(t);
14
16
  } while (t.type !== TokenType.EOF);
15
17
  return tokens;
16
18
  }
@@ -23,30 +25,18 @@ function compile(code) {
23
25
 
24
26
  function run(code) {
25
27
  const js = compile(code);
28
+
26
29
  const finalcode_input = `
27
- let buffer_of_epoxy_lang_dont_use_this_name = "";
28
- let resolvers_of_epoxy_lang_dont_use_this_name = [];
29
- process.stdin.setEncoding("utf8");
30
- process.stdin.on("data", chunk => {
31
- buffer_of_epoxy_lang_dont_use_this_name += chunk;
32
- while (buffer_of_epoxy_lang_dont_use_this_name.includes("\\n") && resolvers_of_epoxy_lang_dont_use_this_name.length > 0) {
33
- const line = buffer_of_epoxy_lang_dont_use_this_name.slice(0, buffer_of_epoxy_lang_dont_use_this_name.indexOf("\\n")).trim();
34
- buffer_of_epoxy_lang_dont_use_this_name = buffer_of_epoxy_lang_dont_use_this_name.slice(buffer_of_epoxy_lang_dont_use_this_name.indexOf("\\n") + 1);
35
- const resolve_of_epoxy_lang_dont_use_this_name = resolvers_of_epoxy_lang_dont_use_this_name.shift();
36
- resolve_of_epoxy_lang_dont_use_this_name(line);
37
- }
38
- });
39
- function input_of_epoxy_lang_dont_use_this_name() {
40
- return new Promise(resolve => {
41
- resolvers_of_epoxy_lang_dont_use_this_name.push(resolve);
42
- })}
43
- async function main_of_epoxy_lang_dont_use_this_name() {
30
+ const promptSync_of_epoxy_lang_dont_use_this_name = require("prompt-sync");
31
+ const input_of_epoxy_lang_dont_use_this_name = promptSync_of_epoxy_lang_dont_use_this_name();
44
32
  ${js}
45
- process.stdin.pause();
46
- }
47
- main_of_epoxy_lang_dont_use_this_name();`;
48
- const thisisthecode = js.includes("input_of_epoxy_lang_dont_use_this_name()") ? finalcode_input : js;
33
+ `;
34
+
35
+ const thisisthecode = js.includes("input_of_epoxy_lang_dont_use_this_name()")
36
+ ? finalcode_input
37
+ : js;
38
+
49
39
  return eval(thisisthecode);
50
40
  }
51
41
 
52
- export { tokenizeAll, compile, run };
42
+ export { tokenizeAll, compile, run };
package/src/test.js CHANGED
@@ -1,33 +1,18 @@
1
1
  import { compile } from './index.js';
2
2
  import { readFileSync } from 'fs';
3
+ import { createRequire } from "module";
4
+ const require = createRequire(import.meta.url);
3
5
  console.log("=== Testing test.epx ===");
4
- const inputfile = readFileSync('./examples/square.epx', 'utf-8');
6
+ const inputfile = readFileSync('./examples/hybrid.epx', 'utf-8');
5
7
  console.log("Epoxy code:");
6
8
  console.log(inputfile);
7
9
  console.log("\nCompiled JavaScript:");
8
10
  const epx_to_js_raw = compile(inputfile);
9
11
  const finalcode_input = `
10
- let buffer_of_epoxy_lang_dont_use_this_name = "";
11
- let resolvers_of_epoxy_lang_dont_use_this_name = [];
12
- process.stdin.setEncoding("utf8");
13
- process.stdin.on("data", chunk => {
14
- buffer_of_epoxy_lang_dont_use_this_name += chunk;
15
- while (buffer_of_epoxy_lang_dont_use_this_name.includes("\\n") && resolvers_of_epoxy_lang_dont_use_this_name.length > 0) {
16
- const line = buffer_of_epoxy_lang_dont_use_this_name.slice(0, buffer_of_epoxy_lang_dont_use_this_name.indexOf("\\n")).trim();
17
- buffer_of_epoxy_lang_dont_use_this_name = buffer_of_epoxy_lang_dont_use_this_name.slice(buffer_of_epoxy_lang_dont_use_this_name.indexOf("\\n") + 1);
18
- const resolve_of_epoxy_lang_dont_use_this_name = resolvers_of_epoxy_lang_dont_use_this_name.shift();
19
- resolve_of_epoxy_lang_dont_use_this_name(line);
20
- }
21
- });
22
- function input_of_epoxy_lang_dont_use_this_name() {
23
- return new Promise(resolve => {
24
- resolvers_of_epoxy_lang_dont_use_this_name.push(resolve);
25
- })}
26
- async function main_of_epoxy_lang_dont_use_this_name() {
12
+ const promptSync_of_epoxy_lang_dont_use_this_name = require("prompt-sync");
13
+ const input_of_epoxy_lang_dont_use_this_name = promptSync_of_epoxy_lang_dont_use_this_name();
27
14
  ${epx_to_js_raw}
28
- process.stdin.pause();
29
- }
30
- main_of_epoxy_lang_dont_use_this_name();`;
15
+ `;
31
16
  const thisisthecode = epx_to_js_raw.includes("input_of_epoxy_lang_dont_use_this_name()") ? finalcode_input : epx_to_js_raw;
32
17
  console.log(thisisthecode);
33
18
  console.log("\nRunning JavaScript:");