@xnoxs/flux-lang 3.1.2 → 3.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xnoxs/flux-lang",
3
- "version": "3.1.2",
3
+ "version": "3.2.1",
4
4
  "description": "Flux — A modern language that transpiles to JavaScript. Python-clean syntax, TypeScript-level safety, Rust-inspired pattern matching.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/src/codegen.js CHANGED
@@ -93,7 +93,7 @@ class CodeGenerator {
93
93
  this.level = 0;
94
94
  this._needsFmt = false;
95
95
 
96
- this.emit('// Generated by Flux Transpiler v3.1.0');
96
+ this.emit('// Generated by Flux Transpiler v3.2.0');
97
97
  this.emit('"use strict";');
98
98
  this.blank();
99
99
 
@@ -189,6 +189,16 @@ class CodeGenerator {
189
189
  this.out();
190
190
  this.emit('}');
191
191
  }
192
+ // Apply decorators: @log fn foo() → foo = log(foo);
193
+ if (node.name && node.decorators && node.decorators.length > 0) {
194
+ for (let i = node.decorators.length - 1; i >= 0; i--) {
195
+ const dec = node.decorators[i];
196
+ const decArgs = dec.args.length > 0
197
+ ? `(${dec.args.map(a => this.genExpr(a)).join(', ')})(${node.name})`
198
+ : `(${node.name})`;
199
+ this.emit(`${node.name} = ${dec.name}${decArgs};`);
200
+ }
201
+ }
192
202
  }
193
203
 
194
204
  // ── class ──────────────────────────────────────────────────────
@@ -197,16 +207,26 @@ class CodeGenerator {
197
207
  this.emit(`class ${node.name}${ext} {`);
198
208
  this.in();
199
209
 
210
+ // Determine which fields are private (use JS #field syntax)
211
+ const isPrivate = f => f.modifiers && f.modifiers.has('private');
212
+ const fieldName = f => isPrivate(f) ? `#${f.name}` : f.name;
213
+ const fieldRef = f => isPrivate(f) ? `this.#${f.name}` : `this.${f.name}`;
214
+
215
+ // Declare private fields at the top of the class body
216
+ const privateFields = node.fields.filter(isPrivate);
217
+ for (const f of privateFields) this.emit(`#${f.name};`);
218
+ if (privateFields.length > 0) this.blank();
219
+
200
220
  // Constructor
201
221
  // Fields split into:
202
222
  // paramFields — no default init (become constructor params)
203
223
  // initFields — have a default init expression (assigned in body, not params)
204
- const allFields = getAllFields(node.name, this.clsReg);
205
- const paramFields = allFields.filter(f => f.init == null);
224
+ const allFields = getAllFields(node.name, this.clsReg);
225
+ const paramFields = allFields.filter(f => f.init == null && !isPrivate(f));
206
226
  const initFields = allFields.filter(f => f.init != null);
207
227
  const ownInitFields = node.fields.filter(f => f.init != null);
208
228
 
209
- const needsCtor = paramFields.length > 0 || initFields.length > 0;
229
+ const needsCtor = paramFields.length > 0 || initFields.length > 0 || privateFields.length > 0;
210
230
  if (needsCtor) {
211
231
  const params = paramFields.map(f => f.name).join(', ');
212
232
  this.emit(`constructor(${params}) {`);
@@ -220,13 +240,13 @@ class CodeGenerator {
220
240
  this.emit(`super(${parentParams.map(f => f.name).join(', ')});`);
221
241
  }
222
242
 
223
- // Assign own param-fields
224
- for (const f of node.fields.filter(f => f.init == null))
225
- this.emit(`this.${f.name} = ${f.name};`);
243
+ // Assign own non-private param-fields
244
+ for (const f of node.fields.filter(f => f.init == null && !isPrivate(f)))
245
+ this.emit(`${fieldRef(f)} = ${f.name};`);
226
246
 
227
- // Assign own init-fields
247
+ // Assign own init-fields (including private ones with defaults)
228
248
  for (const f of ownInitFields)
229
- this.emit(`this.${f.name} = ${this.genExpr(f.init)};`);
249
+ this.emit(`${fieldRef(f)} = ${this.genExpr(f.init)};`);
230
250
 
231
251
  this.out();
232
252
  this.emit('}');
@@ -257,6 +277,18 @@ class CodeGenerator {
257
277
 
258
278
  this.out();
259
279
  this.emit('}');
280
+
281
+ // Apply class decorators: @injectable class Foo → Foo = injectable(Foo);
282
+ if (node.decorators && node.decorators.length > 0) {
283
+ for (let i = node.decorators.length - 1; i >= 0; i--) {
284
+ const dec = node.decorators[i];
285
+ const decArgs = dec.args.length > 0
286
+ ? `(${dec.args.map(a => this.genExpr(a)).join(', ')})(${node.name})`
287
+ : `(${node.name})`;
288
+ this.emit(`${node.name} = ${dec.name}${decArgs};`);
289
+ }
290
+ }
291
+
260
292
  this.blank();
261
293
  }
262
294
 
package/src/linter.js CHANGED
@@ -75,9 +75,35 @@ const BUILTIN_NAMES = new Set([
75
75
  'WeakMap', 'WeakSet', 'Proxy', 'Reflect', 'undefined', 'null', 'true', 'false',
76
76
  'NaN', 'Infinity', 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'setTimeout',
77
77
  'clearTimeout', 'setInterval', 'clearInterval', 'fetch', 'globalThis',
78
- // Flux stdlib names
79
- 'print', 'truncate', 'range', 'sum', 'first', 'last', 'zip', 'flatten',
80
- 'groupBy', 'unique', 'sortBy', 'chunk',
78
+ // Flux stdlib — sequences
79
+ 'range', 'zip', 'enumerate', 'flatten', 'chunk', 'unique', 'groupBy', 'sortBy',
80
+ // Flux stdlib — array pipe helpers
81
+ 'map', 'filter', 'reduce', 'forEach', 'find', 'findIndex',
82
+ 'some', 'every', 'join', 'sort', 'flat', 'flatMap', 'includes',
83
+ // Flux stdlib — array extras
84
+ 'first', 'last', 'take', 'drop', 'takeWhile', 'dropWhile',
85
+ 'compact', 'intersection', 'difference', 'arrayUnion', 'unzip',
86
+ 'countBy', 'minBy', 'maxBy', 'toPairs', 'partition', 'count',
87
+ 'head', 'tail', 'nth', 'rotate', 'sliding',
88
+ // Flux stdlib — math
89
+ 'clamp', 'sum', 'product', 'min', 'max', 'abs', 'floor', 'ceil', 'round',
90
+ 'mean', 'median', 'stdDev', 'lerp', 'randInt', 'sample', 'shuffle',
91
+ // Flux stdlib — objects
92
+ 'pick', 'omit', 'mapValues', 'filterValues', 'fromEntries',
93
+ 'keys', 'values', 'entries', 'merge', 'invert', 'defaults',
94
+ 'deepEqual', 'deepClone',
95
+ // Flux stdlib — strings
96
+ 'capitalize', 'camelCase', 'snakeCase', 'kebabCase', 'truncate', 'pad',
97
+ 'padStart', 'padEnd', 'trim', 'trimStart', 'trimEnd',
98
+ 'words', 'lines', 'startsWith', 'endsWith', 'repeat', 'replaceAll', 'reverseStr',
99
+ // Flux stdlib — type checks
100
+ 'isNil', 'isString', 'isNumber', 'isArray', 'isObject', 'isFunction', 'isBool',
101
+ // Flux stdlib — async
102
+ 'sleep', 'retry', 'memoize', 'timeout', 'debounce', 'throttle', 'allSettled',
103
+ // Flux stdlib — functional
104
+ 'pipe', 'compose', 'partial', 'curry', 'identity', 'noop', 'once', 'flip', 'complement',
105
+ // Flux builtins
106
+ 'print',
81
107
  ]);
82
108
 
83
109
  function isIgnoredName(name) {
package/src/parser.js CHANGED
@@ -226,14 +226,38 @@ class Parser {
226
226
  return stmts;
227
227
  }
228
228
 
229
+ // ── Decorator parsing: @name or @name(args) ───────────────────
230
+ parseDecorators() {
231
+ const decorators = [];
232
+ while (this.check(T.AT)) {
233
+ const loc = this.skip(); // consume @
234
+ const name = this.eat(T.IDENT).value;
235
+ let args = [];
236
+ if (this.check(T.LPAREN)) {
237
+ this.pos++; // consume (
238
+ while (!this.check(T.RPAREN) && !this.check(T.EOF)) {
239
+ args.push(this.parseExpr());
240
+ if (!this.maybe(T.COMMA)) break;
241
+ }
242
+ this.eat(T.RPAREN);
243
+ }
244
+ decorators.push({ name, args, loc });
245
+ this.skipNewlines();
246
+ }
247
+ return decorators;
248
+ }
249
+
229
250
  parseOneStmt() {
251
+ // Collect decorators (@decorator) before any declaration
252
+ const decorators = this.check(T.AT) ? this.parseDecorators() : [];
253
+
230
254
  const tok = this.peek();
231
255
  switch (tok.type) {
232
256
  case T.VAR:
233
257
  case T.VAL: return this.parseVarDecl();
234
- case T.FN: return this.parseFnDecl();
235
- case T.ASYNC: return this.parseAsyncFn();
236
- case T.CLASS: return this.parseClassDecl();
258
+ case T.FN: { const n = this.parseFnDecl(); if (decorators.length) n.decorators = decorators; return n; }
259
+ case T.ASYNC: { const n = this.parseAsyncFn(); if (decorators.length) n.decorators = decorators; return n; }
260
+ case T.CLASS: { const n = this.parseClassDecl(); if (decorators.length) n.decorators = decorators; return n; }
237
261
  case T.IF: return this.parseIf();
238
262
  case T.FOR: return this.parseFor();
239
263
  case T.WHILE: return this.parseWhile();