epoxylang 0.1.2 → 0.1.4

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,30 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions:
9
+ contents: read
10
+ id-token: write
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - name: Checkout repo
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Setup Node
21
+ uses: actions/setup-node@v4
22
+ with:
23
+ node-version: 20
24
+ registry-url: https://registry.npmjs.org/
25
+
26
+ - name: Install dependencies
27
+ run: npm install
28
+
29
+ - name: Publish package
30
+ run: npm publish --access public
package/examples/demo.epx CHANGED
@@ -3,7 +3,9 @@ make square[n] {
3
3
  }
4
4
 
5
5
  assign nums as array = {2, 4, 6};
6
- assign result as int = call square[nums{1}];
7
6
 
8
- store msg = `square of [nums{1}] is [result]`;
9
- show msg;
7
+ repeat[x in 0 to 2, 1]{
8
+ assign result as int = call square[nums{x}]; $ yaha pe comment dal rhe hai bhaii..
9
+ store msg = `square of [nums{x}] is [result]`;
10
+ show msg;
11
+ }
@@ -0,0 +1,11 @@
1
+ all assign hello as array = {"satya0", "satya1", "satya2"};
2
+ show hello{0};
3
+
4
+ assign numbers as array = {1, 2, 3, 4, 5, 6};
5
+
6
+ repeat[i in 0 to 5, 1]{
7
+ assign n as int = numbers{i};
8
+ check [(n / 2 == 1) or (n / 2 == 2) or (n / 2 == 3)] {
9
+ show n;
10
+ }
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "epoxylang",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "epoxy": "./bin/epoxy.js"
@@ -17,6 +17,8 @@ class JSCodeGenerator {
17
17
  return node.statements.map(s => this.visit(s)).join("\n");
18
18
  case "AssignStatement":
19
19
  return this.visitAssignStatement(node);
20
+ case "UpdateStatement":
21
+ return this.visitUpdateStatement(node);
20
22
  case "BinaryExpression": return this.visitBinaryExpression(node);
21
23
  case "Literal": return this.visitLiteral(node);
22
24
  case "Identifier": return this.visitIdentifier(node);
@@ -37,11 +39,30 @@ class JSCodeGenerator {
37
39
 
38
40
 
39
41
  visitAssignStatement(node) {
40
- const keyword = node.isGlobal ? "var" : "let";
42
+ // Determine the JavaScript keyword based on mutability flags
43
+ let keyword;
44
+ if (node.isFix) {
45
+ keyword = "const";
46
+ } else if (node.isGlobal) {
47
+ keyword = "var";
48
+ } else {
49
+ keyword = "let";
50
+ }
51
+
52
+ // Handle declaration without assignment
53
+ if (node.value === null) {
54
+ return `${keyword} ${node.name};`;
55
+ }
56
+
41
57
  const value = this.visit(node.value);
42
58
  return `${keyword} ${node.name} = ${value};`;
43
59
  }
44
60
 
61
+ visitUpdateStatement(node) {
62
+ const value = this.visit(node.value);
63
+ return `${node.name} = ${value};`;
64
+ }
65
+
45
66
  visitBinaryExpression(node) {
46
67
  const left = this.visit(node.left);
47
68
  const right = this.visit(node.right);
@@ -111,8 +132,18 @@ class JSCodeGenerator {
111
132
 
112
133
 
113
134
  visitStoreStatement(node) {
135
+ // Determine the JavaScript keyword based on mutability flags
136
+ let keyword;
137
+ if (node.isFix) {
138
+ keyword = "const";
139
+ } else if (node.isGlobal) {
140
+ keyword = "var";
141
+ } else {
142
+ keyword = "let";
143
+ }
144
+
114
145
  const interpolated = this.convertInterpolation(node.value);
115
- return `const ${node.name} = \`${interpolated}\`;`;
146
+ return `${keyword} ${node.name} = \`${interpolated}\`;`;
116
147
  }
117
148
 
118
149
  visitShowStatement(node) {
@@ -24,7 +24,12 @@ class Lexer {
24
24
 
25
25
  readNumber() {
26
26
  let num = "";
27
- while (this.current && /[0-9]/.test(this.current)) {
27
+ let hasDecimal = false;
28
+
29
+ while (this.current && (/[0-9]/.test(this.current) || (this.current === "." && !hasDecimal))) {
30
+ if (this.current === ".") {
31
+ hasDecimal = true;
32
+ }
28
33
  num += this.current;
29
34
  this.advance();
30
35
  }
@@ -121,6 +126,8 @@ class Lexer {
121
126
  "}": TokenType.RBRACE,
122
127
  "[": TokenType.LBRACKET,
123
128
  "]": TokenType.RBRACKET,
129
+ "(": TokenType.LPAREN,
130
+ ")": TokenType.RPAREN,
124
131
  ";": TokenType.SEMICOLON,
125
132
  ",": TokenType.COMMA
126
133
  };
@@ -2,6 +2,8 @@ const TokenType = {
2
2
  // keywords
3
3
  ASSIGN: "ASSIGN",
4
4
  ALL: "ALL",
5
+ FIX: "FIX",
6
+ UPDATE: "UPDATE",
5
7
  STORE: "STORE",
6
8
  MAKE: "MAKE",
7
9
  CALL: "CALL",
@@ -47,6 +49,8 @@ const TokenType = {
47
49
  RBRACE: "RBRACE",
48
50
  LBRACKET: "LBRACKET",
49
51
  RBRACKET: "RBRACKET",
52
+ LPAREN: "LPAREN",
53
+ RPAREN: "RPAREN",
50
54
  SEMICOLON: "SEMICOLON",
51
55
  COMMA: "COMMA",
52
56
 
@@ -64,6 +68,8 @@ class Token {
64
68
  const KEYWORDS = {
65
69
  assign: TokenType.ASSIGN,
66
70
  all: TokenType.ALL,
71
+ fix: TokenType.FIX,
72
+ update: TokenType.UPDATE,
67
73
  store: TokenType.STORE,
68
74
  make: TokenType.MAKE,
69
75
  call: TokenType.CALL,
@@ -83,6 +89,7 @@ const KEYWORDS = {
83
89
  const TYPES = [
84
90
  "string",
85
91
  "int",
92
+ "double",
86
93
  "bool",
87
94
  "array",
88
95
  "null",
package/src/parser/ast.js CHANGED
@@ -6,9 +6,10 @@ class Program {
6
6
  }
7
7
 
8
8
  class AssignStatement {
9
- constructor({ isGlobal, name, dataType, value }) {
9
+ constructor({ isGlobal, isFix, name, dataType, value }) {
10
10
  this.type = "AssignStatement";
11
- this.isGlobal = isGlobal;
11
+ this.isGlobal = isGlobal; // all -> var
12
+ this.isFix = isFix; // fix -> const
12
13
  this.name = name;
13
14
  this.dataType = dataType; //null allowed
14
15
  this.value = value;
@@ -16,8 +17,18 @@ class AssignStatement {
16
17
  }
17
18
 
18
19
  class StoreStatement {
19
- constructor({ name, value }) {
20
+ constructor({ isGlobal, isFix, name, value }) {
20
21
  this.type = "StoreStatement";
22
+ this.isGlobal = isGlobal; // all -> var
23
+ this.isFix = isFix; // fix -> const
24
+ this.name = name;
25
+ this.value = value;
26
+ }
27
+ }
28
+
29
+ class UpdateStatement {
30
+ constructor({ name, value }) {
31
+ this.type = "UpdateStatement";
21
32
  this.name = name;
22
33
  this.value = value;
23
34
  }
@@ -124,6 +135,7 @@ export {
124
135
  Program,
125
136
  AssignStatement,
126
137
  StoreStatement,
138
+ UpdateStatement,
127
139
  BinaryExpression,
128
140
  Identifier,
129
141
  Literal,
@@ -3,6 +3,7 @@ import {
3
3
  Program,
4
4
  AssignStatement,
5
5
  StoreStatement,
6
+ UpdateStatement,
6
7
  BinaryExpression,
7
8
  Identifier,
8
9
  Literal,
@@ -64,29 +65,48 @@ class Parser {
64
65
  return new Program(statements);
65
66
  }
66
67
 
68
+
67
69
  parseStatement() {
68
70
  const t = this.current().type;
69
71
 
70
- if (t === TokenType.ALL || t === TokenType.ASSIGN) return this.parseAssign();
71
- if (t === TokenType.STORE) return this.parseStore();
72
+ // Handle assign with optional all/fix prefix
73
+ if (t === TokenType.ALL || t === TokenType.FIX || t === TokenType.ASSIGN) {
74
+ // Check if it's assign (could be "assign", "all assign", or "fix assign")
75
+ if (t === TokenType.ASSIGN) return this.parseAssign();
76
+ if (this.peekType() === TokenType.ASSIGN) return this.parseAssign();
77
+ }
78
+
79
+ // Handle store with optional all/fix prefix
80
+ if (t === TokenType.ALL || t === TokenType.FIX || t === TokenType.STORE) {
81
+ // Check if it's store (could be "store", "all store", or "fix store")
82
+ if (t === TokenType.STORE) return this.parseStore();
83
+ if (this.peekType() === TokenType.STORE) return this.parseStore();
84
+ }
85
+
86
+ if (t === TokenType.UPDATE) return this.parseUpdate();
72
87
  if (t === TokenType.CHECK) return this.parseCheck();
73
88
  if (t === TokenType.MAKE) return this.parseMake();
74
89
  if (t === TokenType.CALL) return this.parseCall();
75
90
  if (t === TokenType.GIVE) return this.parseGive();
76
91
  if (t === TokenType.REPEAT && this.peekType() === TokenType.LBRACKET) return this.parseRepeatFor();
77
92
  if (t === TokenType.REPEAT) return this.parseRepeatUntil();
78
-
79
- if (t === TokenType.SHOW) return this.parseShow(); // 🔥 ADD THIS
93
+ if (t === TokenType.SHOW) return this.parseShow();
80
94
 
81
95
  throw new Error("Unknown statement: " + t);
82
96
  }
83
97
 
98
+
84
99
  parseAssign() {
85
100
  let isGlobal = false;
101
+ let isFix = false;
86
102
 
103
+ // Check for 'all' or 'fix' prefix
87
104
  if (this.current().type === TokenType.ALL) {
88
105
  isGlobal = true;
89
106
  this.eat(TokenType.ALL);
107
+ } else if (this.current().type === TokenType.FIX) {
108
+ isFix = true;
109
+ this.eat(TokenType.FIX);
90
110
  }
91
111
 
92
112
  this.eat(TokenType.ASSIGN);
@@ -101,9 +121,25 @@ class Parser {
101
121
  this.eat(TokenType.TYPE);
102
122
  }
103
123
 
104
- this.eat(TokenType.EQUAL);
124
+ // Check if this is a declaration without assignment
125
+ if (this.current().type === TokenType.SEMICOLON) {
126
+ // Declaration only: assign name as type;
127
+ if (!dataType) {
128
+ throw new Error(
129
+ "assign: datatype required when declaring variable without value"
130
+ );
131
+ }
132
+ this.eat(TokenType.SEMICOLON);
133
+ return new AssignStatement({
134
+ isGlobal,
135
+ isFix,
136
+ name,
137
+ dataType,
138
+ value: null
139
+ });
140
+ }
105
141
 
106
- // 🔥 FIX IS HERE
142
+ this.eat(TokenType.EQUAL);
107
143
  const value = this.parseExpression();
108
144
 
109
145
  // enforce rule
@@ -116,11 +152,11 @@ class Parser {
116
152
  throw new Error("assign: array literal requires 'as array'");
117
153
  }
118
154
 
119
-
120
155
  this.eat(TokenType.SEMICOLON);
121
156
 
122
157
  return new AssignStatement({
123
158
  isGlobal,
159
+ isFix,
124
160
  name,
125
161
  dataType,
126
162
  value
@@ -128,6 +164,18 @@ class Parser {
128
164
  }
129
165
 
130
166
  parseStore() {
167
+ let isGlobal = false;
168
+ let isFix = false;
169
+
170
+ // Check for 'all' or 'fix' prefix
171
+ if (this.current().type === TokenType.ALL) {
172
+ isGlobal = true;
173
+ this.eat(TokenType.ALL);
174
+ } else if (this.current().type === TokenType.FIX) {
175
+ isFix = true;
176
+ this.eat(TokenType.FIX);
177
+ }
178
+
131
179
  this.eat(TokenType.STORE);
132
180
  const name = this.current().value;
133
181
  this.eat(TokenType.IDENTIFIER);
@@ -143,7 +191,18 @@ class Parser {
143
191
  this.eat(TokenType.STRING);
144
192
  this.eat(TokenType.SEMICOLON);
145
193
 
146
- return new StoreStatement({ name, value: str.value });
194
+ return new StoreStatement({ isGlobal, isFix, name, value: str.value });
195
+ }
196
+
197
+ parseUpdate() {
198
+ this.eat(TokenType.UPDATE);
199
+ const name = this.current().value;
200
+ this.eat(TokenType.IDENTIFIER);
201
+ this.eat(TokenType.EQUAL);
202
+ const value = this.parseExpression();
203
+ this.eat(TokenType.SEMICOLON);
204
+
205
+ return new UpdateStatement({ name, value });
147
206
  }
148
207
 
149
208
  parseValue() {
@@ -167,6 +226,32 @@ class Parser {
167
226
  parsePrimary() {
168
227
  const tok = this.current();
169
228
 
229
+ // Handle unary minus for negative numbers
230
+ if (tok.type === TokenType.MINUS) {
231
+ this.eat(TokenType.MINUS);
232
+ const expr = this.parsePrimary();
233
+ // If it's a literal number, negate it directly
234
+ if (expr.type === "Literal" && typeof expr.value === "number") {
235
+ return new Literal(-expr.value);
236
+ }
237
+ // Otherwise create a binary expression: 0 - expr
238
+ return new BinaryExpression(new Literal(0), TokenType.MINUS, expr);
239
+ }
240
+
241
+ // Handle unary plus (just ignore it)
242
+ if (tok.type === TokenType.PLUS) {
243
+ this.eat(TokenType.PLUS);
244
+ return this.parsePrimary();
245
+ }
246
+
247
+ // Parentheses for precedence
248
+ if (tok.type === TokenType.LPAREN) {
249
+ this.eat(TokenType.LPAREN);
250
+ const expr = this.parseExpression();
251
+ this.eat(TokenType.RPAREN);
252
+ return expr;
253
+ }
254
+
170
255
  // literals
171
256
  if (
172
257
  tok.type === TokenType.NUMBER ||
package/src/test.js ADDED
@@ -0,0 +1,11 @@
1
+ import { compile } from './index.js';
2
+ import { readFileSync } from 'fs';
3
+ console.log("=== Testing test.epx ===");
4
+ const code1 = readFileSync('./examples/test.epx', 'utf-8');
5
+ console.log("Epoxy code:");
6
+ console.log(code1);
7
+ console.log("\nCompiled JavaScript:");
8
+ const js1 = compile(code1);
9
+ console.log(js1);
10
+ console.log("\nRunning JavaScript:");
11
+ eval(js1);