arc-lang 0.5.6 → 0.5.8

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.
@@ -1,6 +1,8 @@
1
1
  // Arc Language Tree-Walking Interpreter
2
2
  import * as nodeCrypto from "crypto";
3
3
  import * as nodeOs from "os";
4
+ import * as nodeFs from "fs";
5
+ import { execSync } from "child_process";
4
6
  class Env {
5
7
  parent;
6
8
  vars = new Map();
@@ -71,6 +73,67 @@ function toStr(v) {
71
73
  return `<async>`;
72
74
  return String(v);
73
75
  }
76
+ function syncFetch(method, url, body) {
77
+ // Build a small Node script that does fetch and prints JSON result
78
+ // Extract body string: if it's a map with a "data" field, use that; otherwise stringify
79
+ let bodyStr = null;
80
+ if (body != null) {
81
+ if (typeof body === "object" && "__map" in body) {
82
+ const m = body.entries;
83
+ const d = m.get("data");
84
+ bodyStr = d != null ? toStr(d) : toStr(body);
85
+ }
86
+ else {
87
+ bodyStr = toStr(body);
88
+ }
89
+ }
90
+ const bodyJson = bodyStr != null ? JSON.stringify(bodyStr) : "null";
91
+ // Pass config via env to avoid shell escaping issues
92
+ const fetchConfig = JSON.stringify({ method, url, body: bodyStr });
93
+ const script = `const c=JSON.parse(process.env.ARC_FETCH);(async()=>{const o={method:c.method};if(c.body!==null){o.body=c.body;o.headers={"Content-Type":"application/json"};}try{const r=await fetch(c.url,o);const t=await r.text();let d;try{d=JSON.parse(t)}catch{d=t}console.log(JSON.stringify({ok:true,status:r.status,data:d}))}catch(e){console.log(JSON.stringify({ok:false,status:0,data:e.message}))}})()`;
94
+ try {
95
+ const raw = execSync(`node -e "${script.replace(/"/g, '\\"')}"`, {
96
+ timeout: 30000,
97
+ encoding: "utf-8",
98
+ stdio: ["pipe", "pipe", "pipe"],
99
+ env: { ...process.env, ARC_FETCH: fetchConfig },
100
+ }).trim();
101
+ const parsed = JSON.parse(raw);
102
+ const entries = new Map();
103
+ entries.set("ok", parsed.ok);
104
+ entries.set("status", parsed.status);
105
+ // Convert nested objects/arrays to Arc values
106
+ entries.set("data", jsToArc(parsed.data));
107
+ entries.set("method", method);
108
+ entries.set("url", url);
109
+ return { __map: true, entries };
110
+ }
111
+ catch (e) {
112
+ const entries = new Map();
113
+ entries.set("ok", false);
114
+ entries.set("status", 0);
115
+ entries.set("data", e.message || "fetch error");
116
+ entries.set("method", method);
117
+ entries.set("url", url);
118
+ return { __map: true, entries };
119
+ }
120
+ }
121
+ function jsToArc(v) {
122
+ if (v === null || v === undefined)
123
+ return null;
124
+ if (typeof v === "number" || typeof v === "string" || typeof v === "boolean")
125
+ return v;
126
+ if (Array.isArray(v))
127
+ return v.map(jsToArc);
128
+ if (typeof v === "object") {
129
+ const entries = new Map();
130
+ for (const [k, val] of Object.entries(v)) {
131
+ entries.set(k, jsToArc(val));
132
+ }
133
+ return { __map: true, entries };
134
+ }
135
+ return String(v);
136
+ }
74
137
  function resolveAsync(v) {
75
138
  if (v && typeof v === "object" && "__async" in v) {
76
139
  return v.thunk();
@@ -644,7 +707,7 @@ function makePrelude(env) {
644
707
  case "os.list_dir": {
645
708
  try {
646
709
  const fs = require("fs");
647
- return fs.readdirSync(args[0]);
710
+ return nodeFs.readdirSync(args[0]);
648
711
  }
649
712
  catch {
650
713
  return [];
@@ -653,7 +716,7 @@ function makePrelude(env) {
653
716
  case "os.is_file": {
654
717
  try {
655
718
  const fs = require("fs");
656
- return fs.statSync(args[0]).isFile();
719
+ return nodeFs.statSync(args[0]).isFile();
657
720
  }
658
721
  catch {
659
722
  return false;
@@ -662,7 +725,7 @@ function makePrelude(env) {
662
725
  case "os.is_dir": {
663
726
  try {
664
727
  const fs = require("fs");
665
- return fs.statSync(args[0]).isDirectory();
728
+ return nodeFs.statSync(args[0]).isDirectory();
666
729
  }
667
730
  catch {
668
731
  return false;
@@ -671,7 +734,7 @@ function makePrelude(env) {
671
734
  case "os.mkdir": {
672
735
  try {
673
736
  const fs = require("fs");
674
- fs.mkdirSync(args[0], { recursive: true });
737
+ nodeFs.mkdirSync(args[0], { recursive: true });
675
738
  return true;
676
739
  }
677
740
  catch {
@@ -681,7 +744,7 @@ function makePrelude(env) {
681
744
  case "os.rmdir": {
682
745
  try {
683
746
  const fs = require("fs");
684
- fs.rmdirSync(args[0]);
747
+ nodeFs.rmdirSync(args[0]);
685
748
  return true;
686
749
  }
687
750
  catch {
@@ -691,7 +754,7 @@ function makePrelude(env) {
691
754
  case "os.remove": {
692
755
  try {
693
756
  const fs = require("fs");
694
- fs.unlinkSync(args[0]);
757
+ nodeFs.unlinkSync(args[0]);
695
758
  return true;
696
759
  }
697
760
  catch {
@@ -701,7 +764,7 @@ function makePrelude(env) {
701
764
  case "os.rename": {
702
765
  try {
703
766
  const fs = require("fs");
704
- fs.renameSync(args[0], args[1]);
767
+ nodeFs.renameSync(args[0], args[1]);
705
768
  return true;
706
769
  }
707
770
  catch {
@@ -711,7 +774,7 @@ function makePrelude(env) {
711
774
  case "os.copy": {
712
775
  try {
713
776
  const fs = require("fs");
714
- fs.copyFileSync(args[0], args[1]);
777
+ nodeFs.copyFileSync(args[0], args[1]);
715
778
  return true;
716
779
  }
717
780
  catch {
@@ -721,7 +784,7 @@ function makePrelude(env) {
721
784
  case "os.file_size": {
722
785
  try {
723
786
  const fs = require("fs");
724
- return fs.statSync(args[0]).size;
787
+ return nodeFs.statSync(args[0]).size;
725
788
  }
726
789
  catch {
727
790
  return null;
@@ -736,7 +799,7 @@ function makePrelude(env) {
736
799
  throw new Error(`Potentially unsafe command (injection risk): ${cmd}`);
737
800
  }
738
801
  const cp = require("child_process");
739
- return cp.execSync(cmd, { encoding: "utf-8", timeout: 10000 }).trim();
802
+ return execSync(cmd, { encoding: "utf-8", timeout: 10000 }).trim();
740
803
  }
741
804
  catch (e) {
742
805
  if (e.message?.includes("injection risk"))
@@ -747,13 +810,38 @@ function makePrelude(env) {
747
810
  default: return null;
748
811
  }
749
812
  },
813
+ // --- file I/O (used by stdlib/io.arc) ---
814
+ read: (path) => {
815
+ try {
816
+ return nodeFs.readFileSync(path, "utf-8");
817
+ }
818
+ catch {
819
+ return null;
820
+ }
821
+ },
822
+ write: (path, content) => {
823
+ try {
824
+ nodeFs.writeFileSync(path, content, "utf-8");
825
+ return true;
826
+ }
827
+ catch {
828
+ return false;
829
+ }
830
+ },
750
831
  };
751
832
  function callFn(fn, args) {
752
833
  if (fn && typeof fn === "object" && "__fn" in fn) {
753
834
  const f = fn;
754
835
  const fnEnv = new Env(f.closure);
755
836
  bindParams(f, args, fnEnv, evalExpr);
756
- return evalExpr(f.body, fnEnv);
837
+ try {
838
+ return evalExpr(f.body, fnEnv);
839
+ }
840
+ catch (e) {
841
+ if (e instanceof ReturnSignal)
842
+ return e.value;
843
+ throw e;
844
+ }
757
845
  }
758
846
  // It might be a native function stored as a special wrapper
759
847
  if (typeof fn === "function")
@@ -1127,18 +1215,17 @@ function evalExpr(expr, env) {
1127
1215
  const method = expr.method.toUpperCase();
1128
1216
  const arg = evalExpr(expr.arg, env);
1129
1217
  const url = toStr(arg);
1130
- // Mock HTTP tool calls
1218
+ // Real HTTP tool calls via synchronous fetch
1131
1219
  if (["GET", "POST", "PUT", "DELETE", "PATCH"].includes(method)) {
1132
- console.log(`[mock ${method} ${url}]`);
1220
+ let bodyArg = null;
1133
1221
  if (expr.body) {
1134
- const body = evalExpr(expr.body, env);
1135
- return { __map: true, entries: new Map([["status", 200], ["method", method], ["url", url], ["body", body]]) };
1222
+ bodyArg = evalExpr(expr.body, env);
1136
1223
  }
1137
- return { __map: true, entries: new Map([["status", 200], ["method", method], ["url", url], ["data", `mock-data-from-${url}`]]) };
1224
+ return syncFetch(method, url, bodyArg);
1138
1225
  }
1139
1226
  // Custom tool call
1140
- console.log(`[mock tool @${expr.method}(${url})]`);
1141
- return `mock-result-from-${expr.method}`;
1227
+ console.log(`[tool @${expr.method}(${url})]`);
1228
+ return `result-from-${expr.method}`;
1142
1229
  }
1143
1230
  case "AsyncExpr": {
1144
1231
  const capturedEnv = env;
package/dist/lexer.js CHANGED
@@ -138,8 +138,9 @@ export function lex(source) {
138
138
  let hasInterp = false;
139
139
  while (i < source.length && peek() !== '"') {
140
140
  if (peek() === "\n") {
141
- // Unterminated string - newline before closing quote
142
- throw new Error(`Unterminated string literal at line ${sl}, col ${sc}`);
141
+ // Allow multiline strings
142
+ str += advance();
143
+ continue;
143
144
  }
144
145
  if (peek() === "{") {
145
146
  hasInterp = true;
package/dist/modules.js CHANGED
@@ -109,11 +109,18 @@ export function handleUse(stmt, env, currentFile) {
109
109
  }
110
110
  }
111
111
  else {
112
- // No selective imports: bind all exports
112
+ // No selective imports: bind all exports flat
113
113
  for (const [name, value] of Object.entries(exports)) {
114
114
  env.set(name, value);
115
115
  }
116
116
  }
117
+ // Also create a namespace object so `module.fn()` style access works
118
+ const nsName = stmt.path[stmt.path.length - 1];
119
+ const entries = new Map();
120
+ for (const [name, value] of Object.entries(exports)) {
121
+ entries.set(name, value);
122
+ }
123
+ env.set(nsName, { __map: true, entries });
117
124
  }
118
125
  /**
119
126
  * Create a UseHandler bound to a specific file path.
package/dist/parser.js CHANGED
@@ -518,7 +518,7 @@ export class Parser {
518
518
  case TokenType.Slash:
519
519
  case TokenType.Percent: return 6;
520
520
  case TokenType.Power: return 7;
521
- case TokenType.Range: return 8;
521
+ case TokenType.Range: return 4;
522
522
  case TokenType.Dot:
523
523
  case TokenType.LBracket:
524
524
  case TokenType.LParen:
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const ARC_VERSION = "0.5.5";
1
+ export declare const ARC_VERSION = "0.5.8";
2
2
  export declare const ARC_BUILD_DATE: string;
3
3
  export declare const ARC_PLATFORM: string;
4
4
  /** Print version info */
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Arc Version System
2
- export const ARC_VERSION = "0.5.5";
2
+ export const ARC_VERSION = "0.5.8";
3
3
  export const ARC_BUILD_DATE = new Date().toISOString().split("T")[0];
4
4
  export const ARC_PLATFORM = `${process.platform}-${process.arch}`;
5
5
  /** Print version info */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arc-lang",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "Arc ⚡ — A programming language designed by AI agents, for AI agents. 27-63% fewer tokens than JavaScript.",
5
5
  "type": "module",
6
6
  "bin": {