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/LICENSE +21 -0
- package/README.md +37 -175
- package/fazer.js +312 -85
- package/package.json +11 -9
- package/apply_icon.js +0 -28
- package/apply_icon_robust.js +0 -51
- package/bin/fazer.exe +0 -0
- package/package.json.bak +0 -44
- package/standalone.bundled.js +0 -12910
- package/standalone.js +0 -186
- package/test.fz +0 -92
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)
|
|
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
|
-
|
|
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 =
|
|
997
|
-
|
|
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
|
-
|
|
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
|
|
1011
|
-
|
|
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
|
-
|
|
1030
|
-
|
|
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
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
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
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
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(
|
|
1160
|
-
|
|
1161
|
-
|
|
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
|
-
|
|
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
|
|
1171
|
-
|
|
1172
|
-
|
|
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
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
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.
|
|
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
|
|
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();
|