fazer-lang 2.1.1 → 2.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/fazer.js CHANGED
@@ -22,8 +22,8 @@ const Comment = createToken({ name: "Comment", pattern: /#[^\n]*/, group: Lexer.
22
22
 
23
23
  const Assign = createToken({ name: "Assign", pattern: /:=/ });
24
24
 
25
- const Arrow = createToken({ name: "Arrow", pattern: /→/ });
26
- const DoublePipe = createToken({ name: "DoublePipe", pattern: /→>/ });
25
+ const Arrow = createToken({ name: "Arrow", pattern: /->|→/ });
26
+ const DoublePipe = createToken({ name: "DoublePipe", pattern: /\|>|→>/ });
27
27
 
28
28
  const Case = createToken({ name: "Case", pattern: /case\b/ });
29
29
  const Else = createToken({ name: "Else", pattern: /else\b/ });
@@ -593,6 +593,8 @@ class FazerRuntime {
593
593
 
594
594
  _installStdlib(argv) {
595
595
  const { execFileSync } = require("child_process");
596
+ let WebSocket = null;
597
+ try { WebSocket = require("ws"); } catch (e) {}
596
598
 
597
599
  const ANSI = {
598
600
  reset: "\x1b[0m",
@@ -710,6 +712,7 @@ class FazerRuntime {
710
712
  const cwdFn = () => process.cwd();
711
713
 
712
714
  const http = require("http");
715
+ const https = require("https");
713
716
  const child_process = require("child_process");
714
717
 
715
718
  // register builtins in global scope
@@ -723,7 +726,10 @@ class FazerRuntime {
723
726
  try {
724
727
  while (true) {
725
728
  const n = fs.readSync(0, buf, 0, 1, null);
726
- if (n === 0) break;
729
+ if (n === 0) {
730
+ if (str === "") return null;
731
+ break;
732
+ }
727
733
  const c = buf.toString();
728
734
  if (c === "\r") continue;
729
735
  if (c === "\n") break;
@@ -784,7 +790,7 @@ class FazerRuntime {
784
790
  },
785
791
 
786
792
  server: (port, handlerName) => {
787
- const srv = http.createServer((req, res) => {
793
+ const srv = http.createServer(async (req, res) => {
788
794
  // We need to call the Fazer function `handlerName`
789
795
  // But `this._call` requires scope context.
790
796
  // This is tricky in a sync interpreter loop.
@@ -811,7 +817,7 @@ class FazerRuntime {
811
817
  try {
812
818
  const inner = new Scope(fn.closure);
813
819
  inner.set(fn.params[0], reqObj, true);
814
- const result = this._execBlock(fn.body, inner);
820
+ const result = await this._execBlock(fn.body, inner);
815
821
  const out = (result instanceof ReturnSignal) ? result.value : result;
816
822
 
817
823
  const status = (out && out.status) || 200;
@@ -830,6 +836,70 @@ class FazerRuntime {
830
836
  console.log(`Server listening on port ${port}`);
831
837
  },
832
838
 
839
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, Number(ms))),
840
+
841
+ fetch: async (url, opts = {}) => {
842
+ return new Promise((resolve, reject) => {
843
+ const req = https.request(url, { method: opts.method || "GET", headers: opts.headers || {} }, (res) => {
844
+ let data = "";
845
+ res.on("data", (chunk) => data += chunk);
846
+ res.on("end", () => resolve({ status: res.statusCode, body: data, headers: res.headers }));
847
+ });
848
+ req.on("error", reject);
849
+ if (opts.body) req.write(String(opts.body));
850
+ req.end();
851
+ });
852
+ },
853
+
854
+ discord: (token) => {
855
+ if (!WebSocket) throw new FazerError("WebSocket module (ws) not found. Install it to use discord.");
856
+ const listeners = {};
857
+ const ws = new WebSocket("wss://gateway.discord.gg/?v=10&encoding=json");
858
+
859
+ ws.on("open", () => {
860
+ ws.send(JSON.stringify({
861
+ op: 2,
862
+ d: { token: String(token), intents: 33280, properties: { os: "fazer", browser: "fazer", device: "fazer" } }
863
+ }));
864
+ });
865
+
866
+ ws.on("message", async (data) => {
867
+ const p = JSON.parse(data);
868
+ if (p.op === 10) {
869
+ setInterval(() => ws.send(JSON.stringify({ op: 1, d: null })), p.d.heartbeat_interval);
870
+ }
871
+ if (p.t === "MESSAGE_CREATE") {
872
+ const handlers = listeners["message"];
873
+ if (handlers) {
874
+ for (const fn of handlers) {
875
+ try {
876
+ await this._call(fn, [p.d], this.global);
877
+ } catch (e) { console.error("Discord handler error:", e); }
878
+ }
879
+ }
880
+ }
881
+ });
882
+
883
+ return {
884
+ on: (ev, fn) => { listeners[ev] = listeners[ev] || []; listeners[ev].push(fn); },
885
+ send: async (chanId, content) => {
886
+ const body = JSON.stringify({ content: String(content) });
887
+ return new Promise((resolve) => {
888
+ const req = https.request(`https://discord.com/api/v10/channels/${chanId}/messages`, {
889
+ method: "POST",
890
+ headers: { "Authorization": `Bot ${token}`, "Content-Type": "application/json" }
891
+ }, (res) => {
892
+ let d = "";
893
+ res.on("data", c => d += c);
894
+ res.on("end", () => resolve(JSON.parse(d)));
895
+ });
896
+ req.write(body);
897
+ req.end();
898
+ });
899
+ }
900
+ };
901
+ },
902
+
833
903
  argv: argvFn,
834
904
  env: envFn,
835
905
  cwd: cwdFn,
@@ -840,28 +910,28 @@ class FazerRuntime {
840
910
  for (const [k, v] of Object.entries(builtins)) this.global.set(k, v, false);
841
911
  }
842
912
 
843
- run(ast) {
913
+ async run(ast) {
844
914
  try {
845
- return this._execBlock(ast, this.global);
915
+ return await this._execBlock(ast, this.global);
846
916
  } catch (e) {
847
917
  throw e;
848
918
  }
849
919
  }
850
920
 
851
- _execBlock(stmts, scope) {
921
+ async _execBlock(stmts, scope) {
852
922
  let last = null;
853
923
  for (const s of stmts) {
854
- const v = this._execStmt(s, scope);
924
+ const v = await this._execStmt(s, scope);
855
925
  if (v instanceof ReturnSignal) return v;
856
926
  last = v;
857
927
  }
858
928
  return last;
859
929
  }
860
930
 
861
- _execStmt(stmt, scope) {
931
+ async _execStmt(stmt, scope) {
862
932
  switch (stmt.type) {
863
933
  case "assign": {
864
- const val = this._eval(stmt.value, scope);
934
+ const val = await this._eval(stmt.value, scope);
865
935
  if (scope.hasHere(stmt.name)) {
866
936
  scope.assign(stmt.name, val);
867
937
  } else {
@@ -870,7 +940,7 @@ class FazerRuntime {
870
940
  return val;
871
941
  }
872
942
  case "exprstmt":
873
- return this._eval(stmt.expr, scope);
943
+ return await this._eval(stmt.expr, scope);
874
944
 
875
945
  case "fn": {
876
946
  this.fns.set(stmt.name, { params: stmt.params, body: stmt.body, closure: scope });
@@ -879,18 +949,18 @@ class FazerRuntime {
879
949
  }
880
950
 
881
951
  case "return": {
882
- const v = stmt.value ? this._eval(stmt.value, scope) : null;
952
+ const v = stmt.value ? await this._eval(stmt.value, scope) : null;
883
953
  return new ReturnSignal(v);
884
954
  }
885
955
 
886
956
  case "case": {
887
- const val = this._eval(stmt.expr, scope);
957
+ const val = await this._eval(stmt.expr, scope);
888
958
  for (const arm of stmt.arms) {
889
- const { matched, bindings } = this._matchPattern(val, arm.pat, scope);
959
+ const { matched, bindings } = await this._matchPattern(val, arm.pat, scope);
890
960
  if (matched) {
891
961
  const inner = new Scope(scope);
892
962
  for (const [k, v] of Object.entries(bindings)) inner.set(k, v, true);
893
- const out = this._execBlock(arm.body, inner);
963
+ const out = await this._execBlock(arm.body, inner);
894
964
  if (out instanceof ReturnSignal) return out;
895
965
  return out;
896
966
  }
@@ -903,7 +973,7 @@ class FazerRuntime {
903
973
  }
904
974
  }
905
975
 
906
- _eval(expr, scope) {
976
+ async _eval(expr, scope) {
907
977
  if (!expr) return null;
908
978
 
909
979
  switch (expr.type) {
@@ -915,14 +985,17 @@ class FazerRuntime {
915
985
  case "null":
916
986
  return null;
917
987
 
918
- case "list":
919
- return expr.items.map((it) => this._eval(it, scope));
988
+ case "list": {
989
+ const items = [];
990
+ for (const it of expr.items) items.push(await this._eval(it, scope));
991
+ return items;
992
+ }
920
993
 
921
994
  case "map": {
922
995
  const o = {};
923
996
  for (const ent of expr.entries) {
924
- const k = this._eval(ent.key, scope);
925
- o[String(k)] = this._eval(ent.value, scope);
997
+ const k = await this._eval(ent.key, scope);
998
+ o[String(k)] = await this._eval(ent.value, scope);
926
999
  }
927
1000
  return o;
928
1001
  }
@@ -931,33 +1004,31 @@ class FazerRuntime {
931
1004
  const cell = scope.get(expr.name);
932
1005
  if (!cell) throw new FazerError(`Undefined variable '${expr.name}'`);
933
1006
  const v = cell.value;
934
- // fnref is stored as {__fnref__: name} to avoid collisions with user objects
935
1007
  if (v && typeof v === "object" && v.__fnref__) return v;
936
1008
  return v;
937
1009
  }
938
1010
 
939
1011
  case "un": {
940
- const v = this._eval(expr.expr, scope);
1012
+ const v = await this._eval(expr.expr, scope);
941
1013
  if (expr.op === "not") return !truthy(v);
942
1014
  if (expr.op === "-") return -Number(v);
943
1015
  throw new FazerError(`Unknown unary op ${expr.op}`);
944
1016
  }
945
1017
 
946
1018
  case "bin": {
947
- // short-circuit for and/or
948
1019
  if (expr.op === "and") {
949
- const l = this._eval(expr.left, scope);
1020
+ const l = await this._eval(expr.left, scope);
950
1021
  if (!truthy(l)) return l;
951
- return this._eval(expr.right, scope);
1022
+ return await this._eval(expr.right, scope);
952
1023
  }
953
1024
  if (expr.op === "or") {
954
- const l = this._eval(expr.left, scope);
1025
+ const l = await this._eval(expr.left, scope);
955
1026
  if (truthy(l)) return l;
956
- return this._eval(expr.right, scope);
1027
+ return await this._eval(expr.right, scope);
957
1028
  }
958
1029
 
959
- const l = this._eval(expr.left, scope);
960
- const r = this._eval(expr.right, scope);
1030
+ const l = await this._eval(expr.left, scope);
1031
+ const r = await this._eval(expr.right, scope);
961
1032
 
962
1033
  switch (expr.op) {
963
1034
  case "+": return (typeof l === "string" || typeof r === "string") ? String(l) + String(r) : Number(l) + Number(r);
@@ -978,46 +1049,44 @@ class FazerRuntime {
978
1049
  }
979
1050
 
980
1051
  case "get": {
981
- const obj = this._eval(expr.obj, scope);
982
- const key = this._eval(expr.key, scope);
1052
+ const obj = await this._eval(expr.obj, scope);
1053
+ const key = await this._eval(expr.key, scope);
983
1054
  return (obj == null) ? null : obj[String(key)];
984
1055
  }
985
1056
 
986
1057
  case "idx": {
987
- const obj = this._eval(expr.obj, scope);
988
- const idx = this._eval(expr.idx, scope);
1058
+ const obj = await this._eval(expr.obj, scope);
1059
+ const idx = await this._eval(expr.idx, scope);
989
1060
  if (obj == null) return null;
990
1061
  if (Array.isArray(obj)) return obj[Number(idx)];
991
1062
  return obj[String(idx)];
992
1063
  }
993
1064
 
994
1065
  case "call": {
995
- const callee = this._eval(expr.callee, scope);
996
- const args = expr.args.map((a) => this._eval(a, scope));
997
- return this._call(callee, args, scope);
1066
+ const callee = await this._eval(expr.callee, scope);
1067
+ const args = [];
1068
+ for (const a of expr.args) args.push(await this._eval(a, scope));
1069
+ return await this._call(callee, args, scope);
998
1070
  }
999
1071
 
1000
1072
  case "pipe": {
1001
- // left →> right
1002
- // If right is a call: pass left as first arg
1003
- // If right is an identifier: treat as function name, call with left
1004
- // Otherwise: if right evaluates to function, call it with left
1005
- const leftVal = this._eval(expr.left, scope);
1073
+ const leftVal = await this._eval(expr.left, scope);
1006
1074
  const rightNode = expr.right;
1007
1075
 
1008
1076
  if (rightNode.type === "call") {
1009
- const callee = this._eval(rightNode.callee, scope);
1010
- const args = [leftVal, ...rightNode.args.map((a) => this._eval(a, scope))];
1011
- return this._call(callee, args, scope);
1077
+ const callee = await this._eval(rightNode.callee, scope);
1078
+ const args = [leftVal];
1079
+ for (const a of rightNode.args) args.push(await this._eval(a, scope));
1080
+ return await this._call(callee, args, scope);
1012
1081
  }
1013
1082
 
1014
1083
  if (rightNode.type === "ident") {
1015
- const fn = this._eval(rightNode, scope);
1016
- return this._call(fn, [leftVal], scope);
1084
+ const fn = await this._eval(rightNode, scope);
1085
+ return await this._call(fn, [leftVal], scope);
1017
1086
  }
1018
1087
 
1019
- const fn = this._eval(rightNode, scope);
1020
- return this._call(fn, [leftVal], scope);
1088
+ const fn = await this._eval(rightNode, scope);
1089
+ return await this._call(fn, [leftVal], scope);
1021
1090
  }
1022
1091
 
1023
1092
  default:
@@ -1025,11 +1094,22 @@ class FazerRuntime {
1025
1094
  }
1026
1095
  }
1027
1096
 
1028
- _call(callee, args, scope) {
1029
- // builtin JS function
1030
- if (typeof callee === "function") return callee(...args);
1097
+ async _call(callee, args, scope) {
1098
+ if (typeof callee === "function") return await callee(...args);
1099
+
1100
+ // Support calling by string name (e.g. from callbacks stored as strings)
1101
+ if (typeof callee === "string") {
1102
+ if (this.fns.has(callee)) {
1103
+ callee = { __fnref__: callee };
1104
+ } else {
1105
+ // Try to look up variable in scope, maybe it's a function ref
1106
+ const cell = scope.get(callee);
1107
+ if (cell && cell.value && cell.value.__fnref__) {
1108
+ callee = cell.value;
1109
+ }
1110
+ }
1111
+ }
1031
1112
 
1032
- // function reference stored as {__fnref__: "name"}
1033
1113
  if (callee && typeof callee === "object" && callee.__fnref__) {
1034
1114
  const name = callee.__fnref__;
1035
1115
  const fn = this.fns.get(name);
@@ -1039,7 +1119,7 @@ class FazerRuntime {
1039
1119
  }
1040
1120
  const inner = new Scope(fn.closure);
1041
1121
  for (let i = 0; i < fn.params.length; i++) inner.set(fn.params[i], args[i], true);
1042
- const out = this._execBlock(fn.body, inner);
1122
+ const out = await this._execBlock(fn.body, inner);
1043
1123
  if (out instanceof ReturnSignal) return out.value;
1044
1124
  return out;
1045
1125
  }
@@ -1047,8 +1127,7 @@ class FazerRuntime {
1047
1127
  throw new FazerError(`Value is not callable`);
1048
1128
  }
1049
1129
 
1050
- _matchPattern(value, pat, scope) {
1051
- // returns { matched, bindings }
1130
+ async _matchPattern(value, pat, scope) {
1052
1131
  if (!pat) return { matched: false, bindings: {} };
1053
1132
 
1054
1133
  if (pat.type === "else") return { matched: true, bindings: {} };
@@ -1059,24 +1138,25 @@ class FazerRuntime {
1059
1138
  }
1060
1139
 
1061
1140
  if (pat.type === "cmpPat") {
1062
- const rhs = this._eval(pat.rhs, scope);
1063
- const l = value;
1064
- const r = rhs;
1065
- switch (pat.op) {
1066
- case "==": return { matched: deepEqual(l, r), bindings: {} };
1067
- case "!=": return { matched: !deepEqual(l, r), bindings: {} };
1068
- case ">": return { matched: Number(l) > Number(r), bindings: {} };
1069
- case "<": return { matched: Number(l) < Number(r), bindings: {} };
1070
- case ">=": return { matched: Number(l) >= Number(r), bindings: {} };
1071
- case "<=": return { matched: Number(l) <= Number(r), bindings: {} };
1072
- default: return { matched: false, bindings: {} };
1073
- }
1141
+ const rhs = await this._eval(pat.rhs, scope);
1142
+ const op = pat.op;
1143
+ let m = false;
1144
+ if (op === "==") m = deepEqual(value, rhs);
1145
+ else if (op === "!=") m = !deepEqual(value, rhs);
1146
+ else if (op === ">") m = (value > rhs);
1147
+ else if (op === "<") m = (value < rhs);
1148
+ else if (op === ">=") m = (value >= rhs);
1149
+ else if (op === "<=") m = (value <= rhs);
1150
+
1151
+ return { matched: m, bindings: {} };
1074
1152
  }
1075
1153
 
1076
- // literal patterns
1077
- if (pat.type === "num" || pat.type === "str" || pat.type === "bool" || pat.type === "null") {
1078
- const p = pat.type === "null" ? null : pat.value;
1079
- return { matched: deepEqual(value, p), bindings: {} };
1154
+ if (pat.type === "num" || pat.type === "str" || pat.type === "bool") {
1155
+ return { matched: deepEqual(value, pat.value), bindings: {} };
1156
+ }
1157
+
1158
+ if (pat.type === "null") {
1159
+ return { matched: value === null, bindings: {} };
1080
1160
  }
1081
1161
 
1082
1162
  return { matched: false, bindings: {} };
@@ -1151,29 +1231,176 @@ function prettyError(err, filename, code) {
1151
1231
  ].join("\n");
1152
1232
  }
1153
1233
 
1234
+ /* ────────────────────────────────────────────────────────────────────────── */
1235
+ /* REPL */
1236
+ /* ────────────────────────────────────────────────────────────────────────── */
1237
+
1238
+ async function repl() {
1239
+ const readline = require("readline");
1240
+ const rl = readline.createInterface({
1241
+ input: process.stdin,
1242
+ output: process.stdout,
1243
+ prompt: "\x1b[36mfz>\x1b[0m "
1244
+ });
1245
+
1246
+ const version = require("./package.json").version;
1247
+ const arch = process.arch;
1248
+ const platform = process.platform;
1249
+
1250
+ process.title = `Fazer v${version}`;
1251
+
1252
+ console.log(`Fazer v${version} (${platform}-${arch})`);
1253
+ console.log(`Type "help", "copyright" or "license" for more information.`);
1254
+ console.log(`Type "load('file.fz')" to execute a script.`);
1255
+
1256
+ const rt = new FazerRuntime({ filename: "<repl>", args: [] });
1257
+
1258
+ // Add a helper to run files
1259
+ rt.global.set("load", (p) => {
1260
+ const pAbs = path.resolve(String(p));
1261
+ if (!fs.existsSync(pAbs)) throw new FazerError("File not found: " + p);
1262
+ const code = fs.readFileSync(pAbs, "utf8");
1263
+ const lex = lexer.tokenize(code);
1264
+ if (lex.errors.length) throw new FazerError("Lexer error: " + lex.errors[0].message);
1265
+ const parser = new FazerParser();
1266
+ parser.input = lex.tokens;
1267
+ const ast = parser.program();
1268
+ if (parser.errors.length) throw new FazerError("Parser error: " + parser.errors[0].message);
1269
+ return rt.run(ast);
1270
+ }, false);
1271
+
1272
+ rl.prompt();
1273
+
1274
+ for await (const line of rl) {
1275
+ const code = line.trim();
1276
+ if (code === "exit") break;
1277
+ if (code === "") {
1278
+ rl.prompt();
1279
+ continue;
1280
+ }
1281
+
1282
+ try {
1283
+ const lex = lexer.tokenize(code);
1284
+ if (lex.errors.length) {
1285
+ console.error("Syntax Error: " + lex.errors[0].message);
1286
+ } else {
1287
+ const parser = new FazerParser();
1288
+ parser.input = lex.tokens;
1289
+ const ast = parser.program();
1290
+
1291
+ if (parser.errors.length) {
1292
+ console.error("Parse Error: " + parser.errors[0].message);
1293
+ } else {
1294
+ // Execute with existing global scope
1295
+ const res = await rt.run(ast);
1296
+ if (res !== null && res !== undefined) {
1297
+ const builtins = rt.global.get("__builtins__");
1298
+ if (builtins && builtins.style) {
1299
+ console.log(builtins.style(res, "green"));
1300
+ } else {
1301
+ console.log("\x1b[32m" + String(res) + "\x1b[0m");
1302
+ }
1303
+ }
1304
+ }
1305
+ }
1306
+ } catch (e) {
1307
+ console.error("\x1b[31mError: " + (e.message || e) + "\x1b[0m");
1308
+ }
1309
+ rl.prompt();
1310
+ }
1311
+ }
1312
+
1154
1313
  /* ────────────────────────────────────────────────────────────────────────── */
1155
1314
  /* CLI */
1156
1315
  /* ────────────────────────────────────────────────────────────────────────── */
1157
1316
 
1158
1317
  function usage() {
1159
- console.log("Usage:");
1160
- console.log(" fazer run <file.fz> [-- args...]");
1161
- process.exit(1);
1318
+ console.log(`
1319
+ Fazer v${require("./package.json").version} The next-gen pipe-based language.
1320
+
1321
+ Usage:
1322
+ fazer Start interactive shell (REPL)
1323
+ fazer <file.fz> [args...] Run a Fazer script
1324
+ fazer run <file.fz> Run a Fazer script (explicit)
1325
+
1326
+ Flags:
1327
+ --help, -h Show this help message
1328
+ --version, -v Show version
1329
+ --license Show license information
1330
+ `);
1331
+ process.exit(0);
1332
+ }
1333
+
1334
+ function printLicense() {
1335
+ console.log(`
1336
+ Fazer Language
1337
+ Copyright (c) 2026 L'EMPRISE
1338
+
1339
+ Permission is hereby granted, free of charge, to any person obtaining a copy
1340
+ of this software and associated documentation files (the "Software"), to deal
1341
+ in the Software without restriction, including without limitation the rights
1342
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1343
+ copies of the Software, and to permit persons to whom the Software is
1344
+ furnished to do so, subject to the following conditions:
1345
+
1346
+ The above copyright notice and this permission notice shall be included in all
1347
+ copies or substantial portions of the Software.
1348
+
1349
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1350
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1351
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1352
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1353
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1354
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1355
+ SOFTWARE.
1356
+ `);
1357
+ process.exit(0);
1162
1358
  }
1163
1359
 
1164
- function main() {
1360
+ async function main() {
1165
1361
  const argv = process.argv.slice(2);
1166
- if (argv.length === 0) usage();
1362
+
1363
+ if (argv.length === 0) {
1364
+ await repl();
1365
+ return;
1366
+ }
1167
1367
 
1168
1368
  const cmd = argv[0];
1169
1369
 
1170
- if (cmd !== "run") usage();
1171
- const fileArg = argv[1];
1172
- if (!fileArg) usage();
1370
+ if (cmd === "--help" || cmd === "-h") {
1371
+ usage();
1372
+ }
1373
+ if (cmd === "--version" || cmd === "-v") {
1374
+ console.log(`Fazer v${require("./package.json").version}`);
1375
+ process.exit(0);
1376
+ }
1377
+ if (cmd === "--license") {
1378
+ printLicense();
1379
+ }
1173
1380
 
1174
- // forward args after "--" to the script
1175
- const sep = argv.indexOf("--");
1176
- const forwarded = sep >= 0 ? argv.slice(sep + 1) : [];
1381
+ let fileArg = null;
1382
+ let forwarded = [];
1383
+
1384
+ // Logic:
1385
+ // 1. fazer run file.fz ...
1386
+ // 2. fazer file.fz ...
1387
+
1388
+ if (cmd === "run") {
1389
+ fileArg = argv[1];
1390
+ if (!fileArg) usage();
1391
+ const sep = argv.indexOf("--");
1392
+ forwarded = sep >= 0 ? argv.slice(sep + 1) : [];
1393
+ } else {
1394
+ // Check if cmd looks like a file or we treat it as one
1395
+ if (cmd.endsWith(".fz") || fs.existsSync(cmd)) {
1396
+ fileArg = cmd;
1397
+ // All subsequent args are passed to script
1398
+ forwarded = argv.slice(1);
1399
+ } else {
1400
+ console.error(`Unknown command or file not found: '${cmd}'\n`);
1401
+ usage();
1402
+ }
1403
+ }
1177
1404
 
1178
1405
  const filePath = path.resolve(fileArg);
1179
1406
  if (!fs.existsSync(filePath)) {
@@ -1203,7 +1430,7 @@ function main() {
1203
1430
 
1204
1431
  const rt = new FazerRuntime({ argv: forwarded, filename: filePath, code });
1205
1432
  try {
1206
- rt.run(ast);
1433
+ await rt.run(ast);
1207
1434
  } catch (err) {
1208
1435
  if (err instanceof FazerError) {
1209
1436
  console.error(prettyError(err, filePath, code));
@@ -1225,4 +1452,4 @@ module.exports = {
1225
1452
  locOf
1226
1453
  };
1227
1454
 
1228
- if (require.main === module) main();
1455
+ if (require.main === module) main().catch(e => { console.error(e); process.exit(1); });
package/package.json CHANGED
@@ -1,23 +1,25 @@
1
1
  {
2
2
  "name": "fazer-lang",
3
- "version": "2.1.1",
3
+ "version": "2.2.1",
4
4
  "description": "Fazer — A unique, high-performance scripting language with powerful pipe operators (→>) and batteries-included stdlib (crypto, http, fs, json).",
5
5
  "main": "fazer.js",
6
6
  "bin": {
7
7
  "fazer": "fazer.js"
8
8
  },
9
+ "files": [
10
+ "fazer.js",
11
+ "README.md",
12
+ "LICENSE",
13
+ "package.json"
14
+ ],
9
15
  "dependencies": {
10
- "chevrotain": "^11.0.3"
11
- },
12
- "devDependencies": {
13
- "esbuild": "^0.27.2",
14
- "pkg": "^5.8.1",
15
- "rcedit": "^5.0.2"
16
+ "chevrotain": "^11.0.3",
17
+ "ws": "^8.18.0"
16
18
  },
19
+ "devDependencies": {},
17
20
  "scripts": {
18
21
  "start": "node fazer.js",
19
- "test": "node fazer.js run test.fz",
20
- "build": "pkg fazer.js --targets node18-win-x64,node18-linux-x64 --output dist/fazer"
22
+ "test": "node fazer.js --version"
21
23
  },
22
24
  "keywords": [
23
25
  "fazer",
package/apply_icon.js DELETED
@@ -1,28 +0,0 @@
1
- const { rcedit } = require('rcedit');
2
- const path = require('path');
3
-
4
- async function main() {
5
- const exePath = path.resolve(__dirname, '../FzEncrypt.exe');
6
- const iconPath = path.resolve(__dirname, '../fazer.ico');
7
-
8
- console.log(`Applying icon to: ${exePath}`);
9
- console.log(`Using icon: ${iconPath}`);
10
-
11
- try {
12
- await rcedit(exePath, {
13
- icon: iconPath,
14
- 'version-string': {
15
- 'CompanyName': 'Fazer Corp',
16
- 'FileDescription': 'Fz Encrypt',
17
- 'ProductName': 'Fz Encrypt',
18
- 'LegalCopyright': 'Copyright (c) 2026'
19
- }
20
- });
21
- console.log('Icon applied successfully!');
22
- } catch (error) {
23
- console.error('Error applying icon:', error);
24
- process.exit(1);
25
- }
26
- }
27
-
28
- main();