conjure-js 0.0.11 → 0.0.12

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.
@@ -47,7 +47,8 @@ var tokenKeywords = {
47
47
  AnonFnStart: "AnonFnStart",
48
48
  Deref: "Deref",
49
49
  Regex: "Regex",
50
- VarQuote: "VarQuote"
50
+ VarQuote: "VarQuote",
51
+ Meta: "Meta"
51
52
  };
52
53
  var tokenSymbols = {
53
54
  Quote: "quote",
@@ -86,12 +87,18 @@ class ReaderError extends Error {
86
87
  class EvaluationError extends Error {
87
88
  context;
88
89
  pos;
90
+ data;
89
91
  constructor(message, context, pos) {
90
92
  super(message);
91
93
  this.name = "EvaluationError";
92
94
  this.context = context;
93
95
  this.pos = pos;
94
96
  }
97
+ static atArg(message, context, argIndex) {
98
+ const err = new EvaluationError(message, context);
99
+ err.data = { argIndex };
100
+ return err;
101
+ }
95
102
  }
96
103
 
97
104
  class CljThrownSignal {
@@ -101,6 +108,72 @@ class CljThrownSignal {
101
108
  }
102
109
  }
103
110
 
111
+ // src/core/factories.ts
112
+ var cljNumber = (value) => ({ kind: "number", value });
113
+ var cljString = (value) => ({ kind: "string", value });
114
+ var cljBoolean = (value) => ({ kind: "boolean", value });
115
+ var cljKeyword = (name) => ({ kind: "keyword", name });
116
+ var cljNil = () => ({ kind: "nil", value: null });
117
+ var cljSymbol = (name) => ({ kind: "symbol", name });
118
+ var cljList = (value) => ({ kind: "list", value });
119
+ var cljVector = (value) => ({ kind: "vector", value });
120
+ var cljMap = (entries) => ({ kind: "map", entries });
121
+ var cljMultiArityFunction = (arities, env) => ({
122
+ kind: "function",
123
+ arities,
124
+ env
125
+ });
126
+ var cljNativeFunction = (name, fn) => ({ kind: "native-function", name, fn });
127
+ var cljNativeFunctionWithContext = (name, fn) => ({
128
+ kind: "native-function",
129
+ name,
130
+ fn: () => {
131
+ throw new EvaluationError("Native function called without context", {
132
+ name
133
+ });
134
+ },
135
+ fnWithContext: fn
136
+ });
137
+ var cljMultiArityMacro = (arities, env) => ({
138
+ kind: "macro",
139
+ arities,
140
+ env
141
+ });
142
+ var cljRegex = (pattern, flags = "") => ({
143
+ kind: "regex",
144
+ pattern,
145
+ flags
146
+ });
147
+ var cljVar = (ns, name, value, meta) => ({ kind: "var", ns, name, value, meta });
148
+ var cljAtom = (value) => ({ kind: "atom", value });
149
+ var cljReduced = (value) => ({
150
+ kind: "reduced",
151
+ value
152
+ });
153
+ var cljVolatile = (value) => ({
154
+ kind: "volatile",
155
+ value
156
+ });
157
+ var withDoc = (fn, doc, arglists) => ({
158
+ ...fn,
159
+ meta: cljMap([
160
+ [cljKeyword(":doc"), cljString(doc)],
161
+ ...arglists ? [
162
+ [
163
+ cljKeyword(":arglists"),
164
+ cljVector(arglists.map((args) => cljVector(args.map(cljSymbol))))
165
+ ]
166
+ ] : []
167
+ ])
168
+ });
169
+ var cljMultiMethod = (name, dispatchFn, methods, defaultMethod) => ({
170
+ kind: "multi-method",
171
+ name,
172
+ dispatchFn,
173
+ methods,
174
+ defaultMethod
175
+ });
176
+
104
177
  // src/core/env.ts
105
178
  class EnvError extends Error {
106
179
  context;
@@ -111,7 +184,12 @@ class EnvError extends Error {
111
184
  }
112
185
  }
113
186
  function derefValue(val) {
114
- return val.kind === "var" ? val.value : val;
187
+ if (val.kind !== "var")
188
+ return val;
189
+ if (val.dynamic && val.bindingStack && val.bindingStack.length > 0) {
190
+ return val.bindingStack[val.bindingStack.length - 1];
191
+ }
192
+ return val.value;
115
193
  }
116
194
  function makeNamespace(name) {
117
195
  return { name, vars: new Map, aliases: new Map, readerAliases: new Map };
@@ -130,7 +208,7 @@ function lookup(name, env) {
130
208
  return derefValue(raw);
131
209
  const v = current.ns?.vars.get(name);
132
210
  if (v !== undefined)
133
- return v.value;
211
+ return derefValue(v);
134
212
  current = current.outer;
135
213
  }
136
214
  throw new EvaluationError(`Symbol ${name} not found`, { name });
@@ -143,11 +221,22 @@ function tryLookup(name, env) {
143
221
  return derefValue(raw);
144
222
  const v = current.ns?.vars.get(name);
145
223
  if (v !== undefined)
146
- return v.value;
224
+ return derefValue(v);
147
225
  current = current.outer;
148
226
  }
149
227
  return;
150
228
  }
229
+ function internVar(name, value, nsEnv, meta) {
230
+ const ns = nsEnv.ns;
231
+ const existing = ns.vars.get(name);
232
+ if (existing) {
233
+ existing.value = value;
234
+ if (meta)
235
+ existing.meta = meta;
236
+ } else {
237
+ ns.vars.set(name, cljVar(ns.name, name, value, meta));
238
+ }
239
+ }
151
240
  function lookupVar(name, env) {
152
241
  let current = env;
153
242
  while (current) {
@@ -195,71 +284,43 @@ function getNamespaceEnv(env) {
195
284
  return getRootEnv(env);
196
285
  }
197
286
 
198
- // src/core/factories.ts
199
- var cljNumber = (value) => ({ kind: "number", value });
200
- var cljString = (value) => ({ kind: "string", value });
201
- var cljBoolean = (value) => ({ kind: "boolean", value });
202
- var cljKeyword = (name) => ({ kind: "keyword", name });
203
- var cljNil = () => ({ kind: "nil", value: null });
204
- var cljSymbol = (name) => ({ kind: "symbol", name });
205
- var cljList = (value) => ({ kind: "list", value });
206
- var cljVector = (value) => ({ kind: "vector", value });
207
- var cljMap = (entries) => ({ kind: "map", entries });
208
- var cljMultiArityFunction = (arities, env) => ({
209
- kind: "function",
210
- arities,
211
- env
212
- });
213
- var cljNativeFunction = (name, fn) => ({ kind: "native-function", name, fn });
214
- var cljNativeFunctionWithContext = (name, fn) => ({
215
- kind: "native-function",
216
- name,
217
- fn: () => {
218
- throw new EvaluationError("Native function called without context", {
219
- name
220
- });
221
- },
222
- fnWithContext: fn
223
- });
224
- var cljMultiArityMacro = (arities, env) => ({
225
- kind: "macro",
226
- arities,
227
- env
228
- });
229
- var cljRegex = (pattern, flags = "") => ({
230
- kind: "regex",
231
- pattern,
232
- flags
233
- });
234
- var cljVar = (ns, name, value, meta) => ({ kind: "var", ns, name, value, meta });
235
- var cljAtom = (value) => ({ kind: "atom", value });
236
- var cljReduced = (value) => ({
237
- kind: "reduced",
238
- value
239
- });
240
- var cljVolatile = (value) => ({
241
- kind: "volatile",
242
- value
243
- });
244
- var withDoc = (fn, doc, arglists) => ({
245
- ...fn,
246
- meta: cljMap([
247
- [cljKeyword(":doc"), cljString(doc)],
248
- ...arglists ? [
249
- [
250
- cljKeyword(":arglists"),
251
- cljVector(arglists.map((args) => cljVector(args.map(cljSymbol))))
252
- ]
253
- ] : []
254
- ])
255
- });
256
- var cljMultiMethod = (name, dispatchFn, methods, defaultMethod) => ({
257
- kind: "multi-method",
258
- name,
259
- dispatchFn,
260
- methods,
261
- defaultMethod
262
- });
287
+ // src/core/positions.ts
288
+ function setPos(val, pos) {
289
+ Object.defineProperty(val, "_pos", {
290
+ value: pos,
291
+ enumerable: false,
292
+ writable: true,
293
+ configurable: true
294
+ });
295
+ }
296
+ function getPos(val) {
297
+ return val._pos;
298
+ }
299
+ function getLineCol(source, offset) {
300
+ const lines = source.split(`
301
+ `);
302
+ let pos = 0;
303
+ for (let i = 0;i < lines.length; i++) {
304
+ const lineEnd = pos + lines[i].length;
305
+ if (offset <= lineEnd) {
306
+ return { line: i + 1, col: offset - pos, lineText: lines[i] };
307
+ }
308
+ pos = lineEnd + 1;
309
+ }
310
+ const last = lines[lines.length - 1];
311
+ return { line: lines.length, col: last.length, lineText: last };
312
+ }
313
+ function formatErrorContext(source, pos, opts) {
314
+ const { line, col, lineText } = getLineCol(source, pos.start);
315
+ const absLine = line + (opts?.lineOffset ?? 0);
316
+ const absCol = line === 1 ? col + (opts?.colOffset ?? 0) : col;
317
+ const span = Math.max(1, pos.end - pos.start);
318
+ const caret = " ".repeat(col) + "^".repeat(span);
319
+ return `
320
+ at line ${absLine}, col ${absCol + 1}:
321
+ ${lineText}
322
+ ${caret}`;
323
+ }
263
324
 
264
325
  // src/core/evaluator/destructure.ts
265
326
  function toSeqSafe(value) {
@@ -665,6 +726,16 @@ function validateForm(form, inTail) {
665
726
  }
666
727
 
667
728
  // src/core/evaluator/special-forms.ts
729
+ function hasDynamicMeta(meta) {
730
+ if (!meta)
731
+ return false;
732
+ for (const [k, v] of meta.entries) {
733
+ if (k.kind === "keyword" && k.name === ":dynamic" && v.kind === "boolean" && v.value === true) {
734
+ return true;
735
+ }
736
+ }
737
+ return false;
738
+ }
668
739
  var specialFormKeywords = {
669
740
  quote: "quote",
670
741
  def: "def",
@@ -680,7 +751,9 @@ var specialFormKeywords = {
680
751
  defmulti: "defmulti",
681
752
  defmethod: "defmethod",
682
753
  try: "try",
683
- var: "var"
754
+ var: "var",
755
+ binding: "binding",
756
+ "set!": "set!"
684
757
  };
685
758
  function keywordToDispatchFn(kw) {
686
759
  return cljNativeFunction(`kw:${kw.name}`, (...args) => {
@@ -795,6 +868,27 @@ function evaluateQuote(list, _env, _ctx) {
795
868
  function evalQuasiquote(list, env, ctx) {
796
869
  return evaluateQuasiquote(list.value[1], env, new Map, ctx);
797
870
  }
871
+ function buildVarMeta(symMeta, ctx, nameVal) {
872
+ const pos = nameVal ? getPos(nameVal) : undefined;
873
+ const hasPosInfo = pos && ctx.currentSource;
874
+ if (!symMeta && !hasPosInfo)
875
+ return;
876
+ const posEntries = [];
877
+ if (hasPosInfo) {
878
+ const { line, col } = getLineCol(ctx.currentSource, pos.start);
879
+ const lineOffset = ctx.currentLineOffset ?? 0;
880
+ const colOffset = ctx.currentColOffset ?? 0;
881
+ posEntries.push([cljKeyword(":line"), cljNumber(line + lineOffset)]);
882
+ posEntries.push([cljKeyword(":column"), cljNumber(line === 1 ? col + colOffset : col)]);
883
+ if (ctx.currentFile) {
884
+ posEntries.push([cljKeyword(":file"), cljString(ctx.currentFile)]);
885
+ }
886
+ }
887
+ const POS_KEYS = new Set([":line", ":column", ":file"]);
888
+ const baseEntries = (symMeta?.entries ?? []).filter(([k]) => !(k.kind === "keyword" && POS_KEYS.has(k.name)));
889
+ const allEntries = [...baseEntries, ...posEntries];
890
+ return allEntries.length > 0 ? cljMap(allEntries) : undefined;
891
+ }
798
892
  function evaluateDef(list, env, ctx) {
799
893
  const name = list.value[1];
800
894
  if (name.kind !== "symbol") {
@@ -809,11 +903,20 @@ function evaluateDef(list, env, ctx) {
809
903
  const nsEnv = getNamespaceEnv(env);
810
904
  const cljNs = nsEnv.ns;
811
905
  const newValue = ctx.evaluate(list.value[2], env);
906
+ const varMeta = buildVarMeta(name.meta, ctx, name);
812
907
  const existing = cljNs.vars.get(name.name);
813
908
  if (existing) {
814
909
  existing.value = newValue;
910
+ if (varMeta) {
911
+ existing.meta = varMeta;
912
+ if (hasDynamicMeta(varMeta))
913
+ existing.dynamic = true;
914
+ }
815
915
  } else {
816
- cljNs.vars.set(name.name, cljVar(cljNs.name, name.name, newValue));
916
+ const v = cljVar(cljNs.name, name.name, newValue, varMeta);
917
+ if (hasDynamicMeta(varMeta))
918
+ v.dynamic = true;
919
+ cljNs.vars.set(name.name, v);
817
920
  }
818
921
  return cljNil();
819
922
  }
@@ -872,7 +975,7 @@ function evaluateDefmacro(list, env, _ctx) {
872
975
  }
873
976
  const arities = parseArities(list.value.slice(2), env);
874
977
  const macro = cljMultiArityMacro(arities, env);
875
- define(name.name, macro, getRootEnv(env));
978
+ internVar(name.name, macro, getNamespaceEnv(env));
876
979
  return cljNil();
877
980
  }
878
981
  function evaluateLoop(list, env, ctx) {
@@ -944,7 +1047,7 @@ function evaluateDefmulti(list, env, ctx) {
944
1047
  dispatchFn = evaluated;
945
1048
  }
946
1049
  const mm = cljMultiMethod(mmName.name, dispatchFn, []);
947
- define(mmName.name, mm, getNamespaceEnv(env));
1050
+ internVar(mmName.name, mm, getNamespaceEnv(env));
948
1051
  return cljNil();
949
1052
  }
950
1053
  function evaluateDefmethod(list, env, ctx) {
@@ -973,7 +1076,12 @@ function evaluateDefmethod(list, env, ctx) {
973
1076
  { dispatchVal, fn: methodFn }
974
1077
  ]);
975
1078
  }
976
- define(mmName.name, updated, getNamespaceEnv(env));
1079
+ const v = lookupVar(mmName.name, env);
1080
+ if (v) {
1081
+ v.value = updated;
1082
+ } else {
1083
+ define(mmName.name, updated, getNamespaceEnv(env));
1084
+ }
977
1085
  return cljNil();
978
1086
  }
979
1087
  function evaluateVar(list, env, _ctx) {
@@ -1008,6 +1116,66 @@ function evaluateVar(list, env, _ctx) {
1008
1116
  }
1009
1117
  return v;
1010
1118
  }
1119
+ function evaluateBinding(list, env, ctx) {
1120
+ const bindings = list.value[1];
1121
+ if (!isVector(bindings)) {
1122
+ throw new EvaluationError("binding requires a vector of bindings", {
1123
+ list,
1124
+ env
1125
+ });
1126
+ }
1127
+ if (bindings.value.length % 2 !== 0) {
1128
+ throw new EvaluationError("binding vector must have an even number of forms", { list, env });
1129
+ }
1130
+ const body = list.value.slice(2);
1131
+ const boundVars = [];
1132
+ for (let i = 0;i < bindings.value.length; i += 2) {
1133
+ const sym = bindings.value[i];
1134
+ if (!isSymbol(sym)) {
1135
+ throw new EvaluationError("binding left-hand side must be a symbol", { sym });
1136
+ }
1137
+ const newVal = ctx.evaluate(bindings.value[i + 1], env);
1138
+ const v = lookupVar(sym.name, env);
1139
+ if (!v) {
1140
+ throw new EvaluationError(`No var found for symbol '${sym.name}' in binding form`, { sym });
1141
+ }
1142
+ if (!v.dynamic) {
1143
+ throw new EvaluationError(`Cannot use binding with non-dynamic var ${v.ns}/${v.name}. Mark it dynamic with (def ^:dynamic ${sym.name} ...)`, { sym });
1144
+ }
1145
+ v.bindingStack ??= [];
1146
+ v.bindingStack.push(newVal);
1147
+ boundVars.push(v);
1148
+ }
1149
+ try {
1150
+ return ctx.evaluateForms(body, env);
1151
+ } finally {
1152
+ for (const v of boundVars) {
1153
+ v.bindingStack.pop();
1154
+ }
1155
+ }
1156
+ }
1157
+ function evaluateSet(list, env, ctx) {
1158
+ if (list.value.length !== 3) {
1159
+ throw new EvaluationError(`set! requires exactly 2 arguments, got ${list.value.length - 1}`, { list, env });
1160
+ }
1161
+ const symForm = list.value[1];
1162
+ if (!isSymbol(symForm)) {
1163
+ throw new EvaluationError(`set! first argument must be a symbol, got ${symForm.kind}`, { symForm, env });
1164
+ }
1165
+ const v = lookupVar(symForm.name, env);
1166
+ if (!v) {
1167
+ throw new EvaluationError(`Unable to resolve var: ${symForm.name} in this context`, { symForm, env });
1168
+ }
1169
+ if (!v.dynamic) {
1170
+ throw new EvaluationError(`Cannot set! non-dynamic var ${v.ns}/${v.name}. Mark it with ^:dynamic.`, { symForm, env });
1171
+ }
1172
+ if (!v.bindingStack || v.bindingStack.length === 0) {
1173
+ throw new EvaluationError(`Cannot set! ${v.ns}/${v.name} — no active binding. Use set! only inside a (binding [...] ...) form.`, { symForm, env });
1174
+ }
1175
+ const newVal = ctx.evaluate(list.value[2], env);
1176
+ v.bindingStack[v.bindingStack.length - 1] = newVal;
1177
+ return newVal;
1178
+ }
1011
1179
  var specialFormEvaluatorEntries = {
1012
1180
  try: evaluateTry,
1013
1181
  quote: evaluateQuote,
@@ -1023,7 +1191,9 @@ var specialFormEvaluatorEntries = {
1023
1191
  recur: evaluateRecur,
1024
1192
  defmulti: evaluateDefmulti,
1025
1193
  defmethod: evaluateDefmethod,
1026
- var: evaluateVar
1194
+ var: evaluateVar,
1195
+ binding: evaluateBinding,
1196
+ "set!": evaluateSet
1027
1197
  };
1028
1198
  function evaluateSpecialForm(symbol, list, env, ctx) {
1029
1199
  const evalFn = specialFormEvaluatorEntries[symbol];
@@ -1209,10 +1379,9 @@ var arithmeticFunctions = {
1209
1379
  if (nums.length === 0) {
1210
1380
  return cljNumber(0);
1211
1381
  }
1212
- if (nums.some((arg) => arg.kind !== "number")) {
1213
- throw new EvaluationError("+ expects all arguments to be numbers", {
1214
- args: nums
1215
- });
1382
+ const badIdx = nums.findIndex((a) => a.kind !== "number");
1383
+ if (badIdx !== -1) {
1384
+ throw EvaluationError.atArg("+ expects all arguments to be numbers", { args: nums }, badIdx);
1216
1385
  }
1217
1386
  return nums.reduce((acc, arg) => {
1218
1387
  return cljNumber(acc.value + arg.value);
@@ -1224,10 +1393,9 @@ var arithmeticFunctions = {
1224
1393
  args: nums
1225
1394
  });
1226
1395
  }
1227
- if (nums.some((arg) => arg.kind !== "number")) {
1228
- throw new EvaluationError("- expects all arguments to be numbers", {
1229
- args: nums
1230
- });
1396
+ const badIdx = nums.findIndex((a) => a.kind !== "number");
1397
+ if (badIdx !== -1) {
1398
+ throw EvaluationError.atArg("- expects all arguments to be numbers", { args: nums }, badIdx);
1231
1399
  }
1232
1400
  return nums.slice(1).reduce((acc, arg) => {
1233
1401
  return cljNumber(acc.value - arg.value);
@@ -1237,10 +1405,9 @@ var arithmeticFunctions = {
1237
1405
  if (nums.length === 0) {
1238
1406
  return cljNumber(1);
1239
1407
  }
1240
- if (nums.some((arg) => arg.kind !== "number")) {
1241
- throw new EvaluationError("* expects all arguments to be numbers", {
1242
- args: nums
1243
- });
1408
+ const badIdx = nums.findIndex((a) => a.kind !== "number");
1409
+ if (badIdx !== -1) {
1410
+ throw EvaluationError.atArg("* expects all arguments to be numbers", { args: nums }, badIdx);
1244
1411
  }
1245
1412
  return nums.slice(1).reduce((acc, arg) => {
1246
1413
  return cljNumber(acc.value * arg.value);
@@ -1252,14 +1419,15 @@ var arithmeticFunctions = {
1252
1419
  args: nums
1253
1420
  });
1254
1421
  }
1255
- if (nums.some((arg) => arg.kind !== "number")) {
1256
- throw new EvaluationError("/ expects all arguments to be numbers", {
1257
- args: nums
1258
- });
1422
+ const badIdx = nums.findIndex((a) => a.kind !== "number");
1423
+ if (badIdx !== -1) {
1424
+ throw EvaluationError.atArg("/ expects all arguments to be numbers", { args: nums }, badIdx);
1259
1425
  }
1260
- return nums.slice(1).reduce((acc, arg) => {
1426
+ return nums.slice(1).reduce((acc, arg, reduceIdx) => {
1261
1427
  if (arg.value === 0) {
1262
- throw new EvaluationError("division by zero", { args: nums });
1428
+ const err = new EvaluationError("division by zero", { args: nums });
1429
+ err.data = { argIndex: reduceIdx + 1 };
1430
+ throw err;
1263
1431
  }
1264
1432
  return cljNumber(acc.value / arg.value);
1265
1433
  }, nums[0]);
@@ -1270,10 +1438,9 @@ var arithmeticFunctions = {
1270
1438
  args: nums
1271
1439
  });
1272
1440
  }
1273
- if (nums.some((arg) => arg.kind !== "number")) {
1274
- throw new EvaluationError("> expects all arguments to be numbers", {
1275
- args: nums
1276
- });
1441
+ const badIdx = nums.findIndex((a) => a.kind !== "number");
1442
+ if (badIdx !== -1) {
1443
+ throw EvaluationError.atArg("> expects all arguments to be numbers", { args: nums }, badIdx);
1277
1444
  }
1278
1445
  for (let i = 1;i < nums.length; i++) {
1279
1446
  if (nums[i].value >= nums[i - 1].value) {
@@ -1288,10 +1455,9 @@ var arithmeticFunctions = {
1288
1455
  args: nums
1289
1456
  });
1290
1457
  }
1291
- if (nums.some((arg) => arg.kind !== "number")) {
1292
- throw new EvaluationError("< expects all arguments to be numbers", {
1293
- args: nums
1294
- });
1458
+ const badIdx = nums.findIndex((a) => a.kind !== "number");
1459
+ if (badIdx !== -1) {
1460
+ throw EvaluationError.atArg("< expects all arguments to be numbers", { args: nums }, badIdx);
1295
1461
  }
1296
1462
  for (let i = 1;i < nums.length; i++) {
1297
1463
  if (nums[i].value <= nums[i - 1].value) {
@@ -1306,10 +1472,9 @@ var arithmeticFunctions = {
1306
1472
  args: nums
1307
1473
  });
1308
1474
  }
1309
- if (nums.some((arg) => arg.kind !== "number")) {
1310
- throw new EvaluationError(">= expects all arguments to be numbers", {
1311
- args: nums
1312
- });
1475
+ const badIdx = nums.findIndex((a) => a.kind !== "number");
1476
+ if (badIdx !== -1) {
1477
+ throw EvaluationError.atArg(">= expects all arguments to be numbers", { args: nums }, badIdx);
1313
1478
  }
1314
1479
  for (let i = 1;i < nums.length; i++) {
1315
1480
  if (nums[i].value > nums[i - 1].value) {
@@ -1324,10 +1489,9 @@ var arithmeticFunctions = {
1324
1489
  args: nums
1325
1490
  });
1326
1491
  }
1327
- if (nums.some((arg) => arg.kind !== "number")) {
1328
- throw new EvaluationError("<= expects all arguments to be numbers", {
1329
- args: nums
1330
- });
1492
+ const badIdx = nums.findIndex((a) => a.kind !== "number");
1493
+ if (badIdx !== -1) {
1494
+ throw EvaluationError.atArg("<= expects all arguments to be numbers", { args: nums }, badIdx);
1331
1495
  }
1332
1496
  for (let i = 1;i < nums.length; i++) {
1333
1497
  if (nums[i].value < nums[i - 1].value) {
@@ -1351,13 +1515,13 @@ var arithmeticFunctions = {
1351
1515
  }), "Compares adjacent arguments left to right, returns true if all values are structurally equal, false otherwise.", [["&", "vals"]]),
1352
1516
  inc: withDoc(cljNativeFunction("inc", (x) => {
1353
1517
  if (x === undefined || x.kind !== "number") {
1354
- throw new EvaluationError(`inc expects a number${x !== undefined ? `, got ${printString(x)}` : ""}`, { x });
1518
+ throw EvaluationError.atArg(`inc expects a number${x !== undefined ? `, got ${printString(x)}` : ""}`, { x }, 0);
1355
1519
  }
1356
1520
  return cljNumber(x.value + 1);
1357
1521
  }), "Returns the argument incremented by 1. Throws on non-number arguments.", [["x"]]),
1358
1522
  dec: withDoc(cljNativeFunction("dec", (x) => {
1359
1523
  if (x === undefined || x.kind !== "number") {
1360
- throw new EvaluationError(`dec expects a number${x !== undefined ? `, got ${printString(x)}` : ""}`, { x });
1524
+ throw EvaluationError.atArg(`dec expects a number${x !== undefined ? `, got ${printString(x)}` : ""}`, { x }, 0);
1361
1525
  }
1362
1526
  return cljNumber(x.value - 1);
1363
1527
  }), "Returns the argument decremented by 1. Throws on non-number arguments.", [["x"]]),
@@ -1367,10 +1531,9 @@ var arithmeticFunctions = {
1367
1531
  args: nums
1368
1532
  });
1369
1533
  }
1370
- if (nums.some((arg) => arg.kind !== "number")) {
1371
- throw new EvaluationError("max expects all arguments to be numbers", {
1372
- args: nums
1373
- });
1534
+ const badIdx = nums.findIndex((a) => a.kind !== "number");
1535
+ if (badIdx !== -1) {
1536
+ throw EvaluationError.atArg("max expects all arguments to be numbers", { args: nums }, badIdx);
1374
1537
  }
1375
1538
  return nums.reduce((best, arg) => arg.value > best.value ? arg : best);
1376
1539
  }), "Returns the largest of the arguments. Throws on non-number arguments.", [["&", "nums"]]),
@@ -1380,53 +1543,54 @@ var arithmeticFunctions = {
1380
1543
  args: nums
1381
1544
  });
1382
1545
  }
1383
- if (nums.some((arg) => arg.kind !== "number")) {
1384
- throw new EvaluationError("min expects all arguments to be numbers", {
1385
- args: nums
1386
- });
1546
+ const badIdx = nums.findIndex((a) => a.kind !== "number");
1547
+ if (badIdx !== -1) {
1548
+ throw EvaluationError.atArg("min expects all arguments to be numbers", { args: nums }, badIdx);
1387
1549
  }
1388
1550
  return nums.reduce((best, arg) => arg.value < best.value ? arg : best);
1389
1551
  }), "Returns the smallest of the arguments. Throws on non-number arguments.", [["&", "nums"]]),
1390
1552
  mod: withDoc(cljNativeFunction("mod", (n, d) => {
1391
1553
  if (n === undefined || n.kind !== "number") {
1392
- throw new EvaluationError(`mod expects a number as first argument${n !== undefined ? `, got ${printString(n)}` : ""}`, { n });
1554
+ throw EvaluationError.atArg(`mod expects a number as first argument${n !== undefined ? `, got ${printString(n)}` : ""}`, { n }, 0);
1393
1555
  }
1394
1556
  if (d === undefined || d.kind !== "number") {
1395
- throw new EvaluationError(`mod expects a number as second argument${d !== undefined ? `, got ${printString(d)}` : ""}`, { d });
1557
+ throw EvaluationError.atArg(`mod expects a number as second argument${d !== undefined ? `, got ${printString(d)}` : ""}`, { d }, 1);
1396
1558
  }
1397
1559
  if (d.value === 0) {
1398
- throw new EvaluationError("mod: division by zero", { n, d });
1560
+ const err = new EvaluationError("mod: division by zero", { n, d });
1561
+ err.data = { argIndex: 1 };
1562
+ throw err;
1399
1563
  }
1400
1564
  const result = n.value % d.value;
1401
1565
  return cljNumber(result < 0 ? result + Math.abs(d.value) : result);
1402
1566
  }), "Returns the remainder of the first argument divided by the second argument. Throws on non-number arguments or division by zero.", [["n", "d"]]),
1403
1567
  "even?": withDoc(cljNativeFunction("even?", (n) => {
1404
1568
  if (n === undefined || n.kind !== "number") {
1405
- throw new EvaluationError(`even? expects a number${n !== undefined ? `, got ${printString(n)}` : ""}`, { n });
1569
+ throw EvaluationError.atArg(`even? expects a number${n !== undefined ? `, got ${printString(n)}` : ""}`, { n }, 0);
1406
1570
  }
1407
1571
  return cljBoolean(n.value % 2 === 0);
1408
1572
  }), "Returns true if the argument is an even number, false otherwise.", [["n"]]),
1409
1573
  "odd?": withDoc(cljNativeFunction("odd?", (n) => {
1410
1574
  if (n === undefined || n.kind !== "number") {
1411
- throw new EvaluationError(`odd? expects a number${n !== undefined ? `, got ${printString(n)}` : ""}`, { n });
1575
+ throw EvaluationError.atArg(`odd? expects a number${n !== undefined ? `, got ${printString(n)}` : ""}`, { n }, 0);
1412
1576
  }
1413
1577
  return cljBoolean(Math.abs(n.value) % 2 !== 0);
1414
1578
  }), "Returns true if the argument is an odd number, false otherwise.", [["n"]]),
1415
1579
  "pos?": withDoc(cljNativeFunction("pos?", (n) => {
1416
1580
  if (n === undefined || n.kind !== "number") {
1417
- throw new EvaluationError(`pos? expects a number${n !== undefined ? `, got ${printString(n)}` : ""}`, { n });
1581
+ throw EvaluationError.atArg(`pos? expects a number${n !== undefined ? `, got ${printString(n)}` : ""}`, { n }, 0);
1418
1582
  }
1419
1583
  return cljBoolean(n.value > 0);
1420
1584
  }), "Returns true if the argument is a positive number, false otherwise.", [["n"]]),
1421
1585
  "neg?": withDoc(cljNativeFunction("neg?", (n) => {
1422
1586
  if (n === undefined || n.kind !== "number") {
1423
- throw new EvaluationError(`neg? expects a number${n !== undefined ? `, got ${printString(n)}` : ""}`, { n });
1587
+ throw EvaluationError.atArg(`neg? expects a number${n !== undefined ? `, got ${printString(n)}` : ""}`, { n }, 0);
1424
1588
  }
1425
1589
  return cljBoolean(n.value < 0);
1426
1590
  }), "Returns true if the argument is a negative number, false otherwise.", [["n"]]),
1427
1591
  "zero?": withDoc(cljNativeFunction("zero?", (n) => {
1428
1592
  if (n === undefined || n.kind !== "number") {
1429
- throw new EvaluationError(`zero? expects a number${n !== undefined ? `, got ${printString(n)}` : ""}`, { n });
1593
+ throw EvaluationError.atArg(`zero? expects a number${n !== undefined ? `, got ${printString(n)}` : ""}`, { n }, 0);
1430
1594
  }
1431
1595
  return cljBoolean(n.value === 0);
1432
1596
  }), "Returns true if the argument is zero, false otherwise.", [["n"]])
@@ -1444,14 +1608,14 @@ var atomFunctions = {
1444
1608
  return value.value;
1445
1609
  if (isReduced(value))
1446
1610
  return value.value;
1447
- throw new EvaluationError(`deref expects an atom, volatile, or reduced value, got ${value.kind}`, { value });
1611
+ throw EvaluationError.atArg(`deref expects an atom, volatile, or reduced value, got ${value.kind}`, { value }, 0);
1448
1612
  }), "Returns the wrapped value from an atom, volatile, or reduced value.", [["value"]]),
1449
1613
  "swap!": withDoc(cljNativeFunctionWithContext("swap!", (ctx, callEnv, atomVal, fn, ...extraArgs) => {
1450
1614
  if (!isAtom(atomVal)) {
1451
- throw new EvaluationError(`swap! expects an atom as its first argument, got ${atomVal.kind}`, { atomVal });
1615
+ throw EvaluationError.atArg(`swap! expects an atom as its first argument, got ${atomVal.kind}`, { atomVal }, 0);
1452
1616
  }
1453
1617
  if (!isAFunction(fn)) {
1454
- throw new EvaluationError(`swap! expects a function as its second argument, got ${fn.kind}`, { fn });
1618
+ throw EvaluationError.atArg(`swap! expects a function as its second argument, got ${fn.kind}`, { fn }, 1);
1455
1619
  }
1456
1620
  const newVal = ctx.applyFunction(fn, [atomVal.value, ...extraArgs], callEnv);
1457
1621
  atomVal.value = newVal;
@@ -1459,7 +1623,7 @@ var atomFunctions = {
1459
1623
  }), "Applies fn to the current value of the atom, replacing the current value with the result. Returns the new value.", [["atomVal", "fn", "&", "extraArgs"]]),
1460
1624
  "reset!": withDoc(cljNativeFunction("reset!", (atomVal, newVal) => {
1461
1625
  if (!isAtom(atomVal)) {
1462
- throw new EvaluationError(`reset! expects an atom as its first argument, got ${atomVal.kind}`, { atomVal });
1626
+ throw EvaluationError.atArg(`reset! expects an atom as its first argument, got ${atomVal.kind}`, { atomVal }, 0);
1463
1627
  }
1464
1628
  atomVal.value = newVal;
1465
1629
  return newVal;
@@ -1563,7 +1727,7 @@ var collectionFunctions = {
1563
1727
  if (coll.kind === "nil")
1564
1728
  return cljNil();
1565
1729
  if (!isSeqable(coll)) {
1566
- throw new EvaluationError(`seq expects a collection, string, or nil, got ${printString(coll)}`, { collection: coll });
1730
+ throw EvaluationError.atArg(`seq expects a collection, string, or nil, got ${printString(coll)}`, { collection: coll }, 0);
1567
1731
  }
1568
1732
  const items = toSeq(coll);
1569
1733
  return items.length === 0 ? cljNil() : cljList(items);
@@ -1572,9 +1736,7 @@ var collectionFunctions = {
1572
1736
  if (collection.kind === "nil")
1573
1737
  return cljNil();
1574
1738
  if (!isSeqable(collection)) {
1575
- throw new EvaluationError("first expects a collection or string", {
1576
- collection
1577
- });
1739
+ throw EvaluationError.atArg("first expects a collection or string", { collection }, 0);
1578
1740
  }
1579
1741
  const entries = toSeq(collection);
1580
1742
  return entries.length === 0 ? cljNil() : entries[0];
@@ -1583,9 +1745,7 @@ var collectionFunctions = {
1583
1745
  if (collection.kind === "nil")
1584
1746
  return cljList([]);
1585
1747
  if (!isSeqable(collection)) {
1586
- throw new EvaluationError("rest expects a collection or string", {
1587
- collection
1588
- });
1748
+ throw EvaluationError.atArg("rest expects a collection or string", { collection }, 0);
1589
1749
  }
1590
1750
  if (isList(collection)) {
1591
1751
  if (collection.value.length === 0) {
@@ -1606,7 +1766,7 @@ var collectionFunctions = {
1606
1766
  const chars = toSeq(collection);
1607
1767
  return cljList(chars.slice(1));
1608
1768
  }
1609
- throw new EvaluationError(`rest expects a collection or string, got ${printString(collection)}`, { collection });
1769
+ throw EvaluationError.atArg(`rest expects a collection or string, got ${printString(collection)}`, { collection }, 0);
1610
1770
  }), "Returns a sequence of the given collection or string excluding the first element.", [["coll"]]),
1611
1771
  conj: withDoc(cljNativeFunction("conj", (collection, ...args) => {
1612
1772
  if (!collection) {
@@ -1616,7 +1776,7 @@ var collectionFunctions = {
1616
1776
  return collection;
1617
1777
  }
1618
1778
  if (!isCollection(collection)) {
1619
- throw new EvaluationError(`conj expects a collection, got ${printString(collection)}`, { collection });
1779
+ throw EvaluationError.atArg(`conj expects a collection, got ${printString(collection)}`, { collection }, 0);
1620
1780
  }
1621
1781
  if (isList(collection)) {
1622
1782
  const newItems = [];
@@ -1632,11 +1792,12 @@ var collectionFunctions = {
1632
1792
  const newEntries = [...collection.entries];
1633
1793
  for (let i = 0;i < args.length; i += 1) {
1634
1794
  const pair = args[i];
1795
+ const pairArgIndex = i + 1;
1635
1796
  if (pair.kind !== "vector") {
1636
- throw new EvaluationError(`conj on maps expects each argument to be a vector key-pair for maps, got ${printString(pair)}`, { pair });
1797
+ throw EvaluationError.atArg(`conj on maps expects each argument to be a vector key-pair for maps, got ${printString(pair)}`, { pair }, pairArgIndex);
1637
1798
  }
1638
1799
  if (pair.value.length !== 2) {
1639
- throw new EvaluationError(`conj on maps expects each argument to be a vector key-pair for maps, got ${printString(pair)}`, { pair });
1800
+ throw EvaluationError.atArg(`conj on maps expects each argument to be a vector key-pair for maps, got ${printString(pair)}`, { pair }, pairArgIndex);
1640
1801
  }
1641
1802
  const key = pair.value[0];
1642
1803
  const keyIdx = newEntries.findIndex((entry) => isEqual(entry[0], key));
@@ -1652,10 +1813,10 @@ var collectionFunctions = {
1652
1813
  }), "Appends args to the given collection. Lists append in reverse order to the head, vectors append to the tail.", [["collection", "&", "args"]]),
1653
1814
  cons: withDoc(cljNativeFunction("cons", (x, xs) => {
1654
1815
  if (!isCollection(xs)) {
1655
- throw new EvaluationError(`cons expects a collection as second argument, got ${printString(xs)}`, { xs });
1816
+ throw EvaluationError.atArg(`cons expects a collection as second argument, got ${printString(xs)}`, { xs }, 1);
1656
1817
  }
1657
1818
  if (isMap(xs)) {
1658
- throw new EvaluationError("cons on maps is not supported, use vectors instead", { xs });
1819
+ throw EvaluationError.atArg("cons on maps is not supported, use vectors instead", { xs }, 1);
1659
1820
  }
1660
1821
  const wrap = isList(xs) ? cljList : cljVector;
1661
1822
  const newItems = [x, ...xs.value];
@@ -1672,7 +1833,7 @@ var collectionFunctions = {
1672
1833
  throw new EvaluationError("assoc on lists is not supported, use vectors instead", { collection });
1673
1834
  }
1674
1835
  if (!isCollection(collection)) {
1675
- throw new EvaluationError(`assoc expects a collection, got ${printString(collection)}`, { collection });
1836
+ throw EvaluationError.atArg(`assoc expects a collection, got ${printString(collection)}`, { collection }, 0);
1676
1837
  }
1677
1838
  if (args.length < 2) {
1678
1839
  throw new EvaluationError("assoc expects at least two arguments", {
@@ -1689,10 +1850,10 @@ var collectionFunctions = {
1689
1850
  for (let i = 0;i < args.length; i += 2) {
1690
1851
  const index = args[i];
1691
1852
  if (index.kind !== "number") {
1692
- throw new EvaluationError(`assoc on vectors expects each key argument to be a index (number), got ${printString(index)}`, { index });
1853
+ throw EvaluationError.atArg(`assoc on vectors expects each key argument to be a index (number), got ${printString(index)}`, { index }, i + 1);
1693
1854
  }
1694
1855
  if (index.value > newValues.length) {
1695
- throw new EvaluationError(`assoc index ${index.value} is out of bounds for vector of length ${newValues.length}`, { index, collection });
1856
+ throw EvaluationError.atArg(`assoc index ${index.value} is out of bounds for vector of length ${newValues.length}`, { index, collection }, i + 1);
1696
1857
  }
1697
1858
  newValues[index.value] = args[i + 1];
1698
1859
  }
@@ -1719,10 +1880,10 @@ var collectionFunctions = {
1719
1880
  throw new EvaluationError("dissoc expects a collection as first argument", { collection });
1720
1881
  }
1721
1882
  if (isList(collection)) {
1722
- throw new EvaluationError("dissoc on lists is not supported, use vectors instead", { collection });
1883
+ throw EvaluationError.atArg("dissoc on lists is not supported, use vectors instead", { collection }, 0);
1723
1884
  }
1724
1885
  if (!isCollection(collection)) {
1725
- throw new EvaluationError(`dissoc expects a collection, got ${printString(collection)}`, { collection });
1886
+ throw EvaluationError.atArg(`dissoc expects a collection, got ${printString(collection)}`, { collection }, 0);
1726
1887
  }
1727
1888
  if (isVector(collection)) {
1728
1889
  if (collection.value.length === 0) {
@@ -1732,10 +1893,10 @@ var collectionFunctions = {
1732
1893
  for (let i = 0;i < args.length; i += 1) {
1733
1894
  const index = args[i];
1734
1895
  if (index.kind !== "number") {
1735
- throw new EvaluationError(`dissoc on vectors expects each key argument to be a index (number), got ${printString(index)}`, { index });
1896
+ throw EvaluationError.atArg(`dissoc on vectors expects each key argument to be a index (number), got ${printString(index)}`, { index }, i + 1);
1736
1897
  }
1737
1898
  if (index.value >= newValues.length) {
1738
- throw new EvaluationError(`dissoc index ${index.value} is out of bounds for vector of length ${newValues.length}`, { index, collection });
1899
+ throw EvaluationError.atArg(`dissoc index ${index.value} is out of bounds for vector of length ${newValues.length}`, { index, collection }, i + 1);
1739
1900
  }
1740
1901
  newValues.splice(index.value, 1);
1741
1902
  }
@@ -1799,7 +1960,9 @@ var collectionFunctions = {
1799
1960
  if (index < 0 || index >= items.length) {
1800
1961
  if (notFound !== undefined)
1801
1962
  return notFound;
1802
- throw new EvaluationError(`nth index ${index} is out of bounds for collection of length ${items.length}`, { coll, n });
1963
+ const err = new EvaluationError(`nth index ${index} is out of bounds for collection of length ${items.length}`, { coll, n });
1964
+ err.data = { argIndex: 1 };
1965
+ throw err;
1803
1966
  }
1804
1967
  return items[index];
1805
1968
  }), "Returns the nth element of the given collection. If not-found is provided, it is returned if the index is out of bounds, otherwise an error is thrown.", [["coll", "n", "not-found"]]),
@@ -1838,27 +2001,27 @@ var collectionFunctions = {
1838
2001
  }), "Returns the last element of the given collection.", [["coll"]]),
1839
2002
  reverse: withDoc(cljNativeFunction("reverse", (coll) => {
1840
2003
  if (coll === undefined || !isList(coll) && !isVector(coll)) {
1841
- throw new EvaluationError(`reverse expects a list or vector${coll !== undefined ? `, got ${printString(coll)}` : ""}`, { coll });
2004
+ throw EvaluationError.atArg(`reverse expects a list or vector${coll !== undefined ? `, got ${printString(coll)}` : ""}`, { coll }, 0);
1842
2005
  }
1843
2006
  return cljList([...coll.value].reverse());
1844
2007
  }), "Returns a new sequence with the elements of the given collection in reverse order.", [["coll"]]),
1845
2008
  "empty?": withDoc(cljNativeFunction("empty?", (coll) => {
1846
2009
  if (coll === undefined) {
1847
- throw new EvaluationError("empty? expects one argument", {});
2010
+ throw EvaluationError.atArg("empty? expects one argument", {}, 0);
1848
2011
  }
1849
2012
  if (coll.kind === "nil")
1850
2013
  return cljBoolean(true);
1851
2014
  if (!isSeqable(coll)) {
1852
- throw new EvaluationError(`empty? expects a collection, string, or nil, got ${printString(coll)}`, { coll });
2015
+ throw EvaluationError.atArg(`empty? expects a collection, string, or nil, got ${printString(coll)}`, { coll }, 0);
1853
2016
  }
1854
2017
  return cljBoolean(toSeq(coll).length === 0);
1855
2018
  }), "Returns true if coll has no items. Accepts collections, strings, and nil.", [["coll"]]),
1856
2019
  "contains?": withDoc(cljNativeFunction("contains?", (coll, key) => {
1857
2020
  if (coll === undefined) {
1858
- throw new EvaluationError("contains? expects a collection as first argument", {});
2021
+ throw EvaluationError.atArg("contains? expects a collection as first argument", {}, 0);
1859
2022
  }
1860
2023
  if (key === undefined) {
1861
- throw new EvaluationError("contains? expects a key as second argument", {});
2024
+ throw EvaluationError.atArg("contains? expects a key as second argument", {}, 1);
1862
2025
  }
1863
2026
  if (coll.kind === "nil")
1864
2027
  return cljBoolean(false);
@@ -1870,11 +2033,11 @@ var collectionFunctions = {
1870
2033
  return cljBoolean(false);
1871
2034
  return cljBoolean(key.value >= 0 && key.value < coll.value.length);
1872
2035
  }
1873
- throw new EvaluationError(`contains? expects a map, vector, or nil, got ${printString(coll)}`, { coll });
2036
+ throw EvaluationError.atArg(`contains? expects a map, vector, or nil, got ${printString(coll)}`, { coll }, 0);
1874
2037
  }), "Returns true if key is present in coll. For maps checks key existence (including keys with nil values). For vectors checks index bounds.", [["coll", "key"]]),
1875
2038
  repeat: withDoc(cljNativeFunction("repeat", (n, x) => {
1876
2039
  if (n === undefined || n.kind !== "number") {
1877
- throw new EvaluationError(`repeat expects a number as first argument${n !== undefined ? `, got ${printString(n)}` : ""}`, { n });
2040
+ throw EvaluationError.atArg(`repeat expects a number as first argument${n !== undefined ? `, got ${printString(n)}` : ""}`, { n }, 0);
1878
2041
  }
1879
2042
  return cljList(Array(n.value).fill(x));
1880
2043
  }), "Returns a sequence of n copies of x.", [["n", "x"]]),
@@ -1882,8 +2045,9 @@ var collectionFunctions = {
1882
2045
  if (args.length === 0 || args.length > 3) {
1883
2046
  throw new EvaluationError("range expects 1, 2, or 3 arguments: (range n), (range start end), or (range start end step)", { args });
1884
2047
  }
1885
- if (args.some((a) => a.kind !== "number")) {
1886
- throw new EvaluationError("range expects number arguments", { args });
2048
+ const badIdx = args.findIndex((a) => a.kind !== "number");
2049
+ if (badIdx !== -1) {
2050
+ throw EvaluationError.atArg("range expects number arguments", { args }, badIdx);
1887
2051
  }
1888
2052
  let start;
1889
2053
  let end;
@@ -1902,7 +2066,7 @@ var collectionFunctions = {
1902
2066
  step = args[2].value;
1903
2067
  }
1904
2068
  if (step === 0) {
1905
- throw new EvaluationError("range step cannot be zero", { args });
2069
+ throw EvaluationError.atArg("range step cannot be zero", { args }, args.length - 1);
1906
2070
  }
1907
2071
  const result = [];
1908
2072
  if (step > 0) {
@@ -1918,13 +2082,13 @@ var collectionFunctions = {
1918
2082
  }), "Returns a sequence of numbers from start (inclusive) to end (exclusive), incrementing by step. If step is positive, the sequence is generated from start to end, otherwise it is generated from end to start.", [["n"], ["start", "end"], ["start", "end", "step"]]),
1919
2083
  keys: withDoc(cljNativeFunction("keys", (m) => {
1920
2084
  if (m === undefined || !isMap(m)) {
1921
- throw new EvaluationError(`keys expects a map${m !== undefined ? `, got ${printString(m)}` : ""}`, { m });
2085
+ throw EvaluationError.atArg(`keys expects a map${m !== undefined ? `, got ${printString(m)}` : ""}`, { m }, 0);
1922
2086
  }
1923
2087
  return cljVector(m.entries.map(([k]) => k));
1924
2088
  }), "Returns a vector of the keys of the given map.", [["m"]]),
1925
2089
  vals: withDoc(cljNativeFunction("vals", (m) => {
1926
2090
  if (m === undefined || !isMap(m)) {
1927
- throw new EvaluationError(`vals expects a map${m !== undefined ? `, got ${printString(m)}` : ""}`, { m });
2091
+ throw EvaluationError.atArg(`vals expects a map${m !== undefined ? `, got ${printString(m)}` : ""}`, { m }, 0);
1928
2092
  }
1929
2093
  return cljVector(m.entries.map(([, v]) => v));
1930
2094
  }), "Returns a vector of the values of the given map.", [["m"]]),
@@ -1935,9 +2099,7 @@ var collectionFunctions = {
1935
2099
  valueKeywords.map,
1936
2100
  valueKeywords.string
1937
2101
  ].includes(countable.kind)) {
1938
- throw new EvaluationError(`count expects a countable value, got ${printString(countable)}`, {
1939
- countable
1940
- });
2102
+ throw EvaluationError.atArg(`count expects a countable value, got ${printString(countable)}`, { countable }, 0);
1941
2103
  }
1942
2104
  switch (countable.kind) {
1943
2105
  case valueKeywords.list:
@@ -2006,7 +2168,7 @@ var errorFunctions = {
2006
2168
  var hofFunctions = {
2007
2169
  reduce: withDoc(cljNativeFunctionWithContext("reduce", (ctx, callEnv, fn, ...rest) => {
2008
2170
  if (fn === undefined || !isAFunction(fn)) {
2009
- throw new EvaluationError(`reduce expects a function as first argument${fn !== undefined ? `, got ${printString(fn)}` : ""}`, { fn });
2171
+ throw EvaluationError.atArg(`reduce expects a function as first argument${fn !== undefined ? `, got ${printString(fn)}` : ""}`, { fn }, 0);
2010
2172
  }
2011
2173
  if (rest.length === 0 || rest.length > 2) {
2012
2174
  throw new EvaluationError("reduce expects 2 or 3 arguments: (reduce f coll) or (reduce f init coll)", { fn });
@@ -2021,7 +2183,7 @@ var hofFunctions = {
2021
2183
  return init;
2022
2184
  }
2023
2185
  if (!isSeqable(collection)) {
2024
- throw new EvaluationError(`reduce expects a collection or string, got ${printString(collection)}`, { collection });
2186
+ throw EvaluationError.atArg(`reduce expects a collection or string, got ${printString(collection)}`, { collection }, rest.length);
2025
2187
  }
2026
2188
  const items = toSeq(collection);
2027
2189
  if (!hasInit) {
@@ -2053,7 +2215,7 @@ var hofFunctions = {
2053
2215
  ]),
2054
2216
  apply: withDoc(cljNativeFunctionWithContext("apply", (ctx, callEnv, fn, ...rest) => {
2055
2217
  if (fn === undefined || !isCallable(fn)) {
2056
- throw new EvaluationError(`apply expects a callable as first argument${fn !== undefined ? `, got ${printString(fn)}` : ""}`, { fn });
2218
+ throw EvaluationError.atArg(`apply expects a callable as first argument${fn !== undefined ? `, got ${printString(fn)}` : ""}`, { fn }, 0);
2057
2219
  }
2058
2220
  if (rest.length === 0) {
2059
2221
  throw new EvaluationError("apply expects at least 2 arguments", {
@@ -2062,7 +2224,7 @@ var hofFunctions = {
2062
2224
  }
2063
2225
  const lastArg = rest[rest.length - 1];
2064
2226
  if (!isNil(lastArg) && !isSeqable(lastArg)) {
2065
- throw new EvaluationError(`apply expects a collection or string as last argument, got ${printString(lastArg)}`, { lastArg });
2227
+ throw EvaluationError.atArg(`apply expects a collection or string as last argument, got ${printString(lastArg)}`, { lastArg }, rest.length);
2066
2228
  }
2067
2229
  const args = [
2068
2230
  ...rest.slice(0, -1),
@@ -2075,7 +2237,7 @@ var hofFunctions = {
2075
2237
  ]),
2076
2238
  partial: withDoc(cljNativeFunction("partial", (fn, ...preArgs) => {
2077
2239
  if (fn === undefined || !isCallable(fn)) {
2078
- throw new EvaluationError(`partial expects a callable as first argument${fn !== undefined ? `, got ${printString(fn)}` : ""}`, { fn });
2240
+ throw EvaluationError.atArg(`partial expects a callable as first argument${fn !== undefined ? `, got ${printString(fn)}` : ""}`, { fn }, 0);
2079
2241
  }
2080
2242
  const capturedFn = fn;
2081
2243
  return cljNativeFunctionWithContext("partial", (ctx, callEnv, ...moreArgs) => {
@@ -2086,8 +2248,9 @@ var hofFunctions = {
2086
2248
  if (fns.length === 0) {
2087
2249
  return cljNativeFunction("identity", (x) => x);
2088
2250
  }
2089
- if (fns.some((f) => !isCallable(f))) {
2090
- throw new EvaluationError("comp expects functions or other callable values (keywords, maps)", { fns });
2251
+ const badIdx = fns.findIndex((f) => !isCallable(f));
2252
+ if (badIdx !== -1) {
2253
+ throw EvaluationError.atArg("comp expects functions or other callable values (keywords, maps)", { fns }, badIdx);
2091
2254
  }
2092
2255
  const capturedFns = fns;
2093
2256
  return cljNativeFunctionWithContext("composed", (ctx, callEnv, ...args) => {
@@ -2100,7 +2263,7 @@ var hofFunctions = {
2100
2263
  }), "Returns the composition of fns, applied right-to-left. (comp f g) is equivalent to (fn [x] (f (g x))). Accepts any callable: functions, keywords, and maps.", [[], ["f"], ["f", "g"], ["f", "g", "&", "fns"]]),
2101
2264
  identity: withDoc(cljNativeFunction("identity", (x) => {
2102
2265
  if (x === undefined) {
2103
- throw new EvaluationError("identity expects one argument", {});
2266
+ throw EvaluationError.atArg("identity expects one argument", {}, 0);
2104
2267
  }
2105
2268
  return x;
2106
2269
  }), "Returns its single argument unchanged.", [["x"]])
@@ -2110,31 +2273,51 @@ var hofFunctions = {
2110
2273
  var metaFunctions = {
2111
2274
  meta: withDoc(cljNativeFunction("meta", (val) => {
2112
2275
  if (val === undefined) {
2113
- throw new EvaluationError("meta expects one argument", {});
2276
+ throw EvaluationError.atArg("meta expects one argument", {}, 0);
2114
2277
  }
2115
- if (val.kind === "function" || val.kind === "native-function") {
2278
+ if (val.kind === "function" || val.kind === "native-function" || val.kind === "var" || val.kind === "list" || val.kind === "vector" || val.kind === "map" || val.kind === "symbol" || val.kind === "atom") {
2116
2279
  return val.meta ?? cljNil();
2117
2280
  }
2118
- if (val.kind === "var")
2119
- return val.meta ?? cljNil();
2120
2281
  return cljNil();
2121
2282
  }), "Returns the metadata map of a value, or nil if the value has no metadata.", [["val"]]),
2122
2283
  "with-meta": withDoc(cljNativeFunction("with-meta", (val, m) => {
2123
2284
  if (val === undefined) {
2124
- throw new EvaluationError("with-meta expects two arguments", {});
2285
+ throw EvaluationError.atArg("with-meta expects two arguments", {}, 0);
2125
2286
  }
2126
2287
  if (m === undefined) {
2127
- throw new EvaluationError("with-meta expects two arguments", {});
2288
+ throw EvaluationError.atArg("with-meta expects two arguments", {}, 1);
2128
2289
  }
2129
2290
  if (m.kind !== "map" && m.kind !== "nil") {
2130
- throw new EvaluationError(`with-meta expects a map as second argument, got ${printString(m)}`, { m });
2291
+ throw EvaluationError.atArg(`with-meta expects a map as second argument, got ${printString(m)}`, { m }, 1);
2131
2292
  }
2132
- if (val.kind !== "function" && val.kind !== "native-function") {
2133
- throw new EvaluationError(`with-meta only supports functions, got ${printString(val)}`, { val });
2293
+ const metaSupported = val.kind === "function" || val.kind === "native-function" || val.kind === "list" || val.kind === "vector" || val.kind === "map" || val.kind === "symbol";
2294
+ if (!metaSupported) {
2295
+ throw EvaluationError.atArg(`with-meta does not support ${val.kind}, got ${printString(val)}`, { val }, 0);
2134
2296
  }
2135
2297
  const meta = m.kind === "nil" ? undefined : m;
2136
2298
  return { ...val, meta };
2137
- }), "Returns a new value with the metadata map m applied to val.", [["val", "m"]])
2299
+ }), "Returns a new value with the metadata map m applied to val.", [["val", "m"]]),
2300
+ "alter-meta!": withDoc(cljNativeFunctionWithContext("alter-meta!", (ctx, callEnv, ref, f, ...args) => {
2301
+ if (ref === undefined) {
2302
+ throw EvaluationError.atArg("alter-meta! expects at least two arguments", {}, 0);
2303
+ }
2304
+ if (f === undefined) {
2305
+ throw EvaluationError.atArg("alter-meta! expects at least two arguments", {}, 1);
2306
+ }
2307
+ if (ref.kind !== "var" && ref.kind !== "atom") {
2308
+ throw EvaluationError.atArg(`alter-meta! expects a Var or Atom as first argument, got ${ref.kind}`, {}, 0);
2309
+ }
2310
+ if (!isAFunction(f)) {
2311
+ throw EvaluationError.atArg(`alter-meta! expects a function as second argument, got ${f.kind}`, {}, 1);
2312
+ }
2313
+ const currentMeta = ref.meta ?? cljNil();
2314
+ const newMeta = ctx.applyCallable(f, [currentMeta, ...args], callEnv);
2315
+ if (newMeta.kind !== "map" && newMeta.kind !== "nil") {
2316
+ throw new EvaluationError(`alter-meta! function must return a map or nil, got ${newMeta.kind}`, {});
2317
+ }
2318
+ ref.meta = newMeta.kind === "nil" ? undefined : newMeta;
2319
+ return newMeta;
2320
+ }), "Applies f to ref's current metadata (with optional args), sets the result as the new metadata, and returns it.", [["ref", "f", "&", "args"]])
2138
2321
  };
2139
2322
 
2140
2323
  // src/core/evaluator/apply.ts
@@ -2233,45 +2416,12 @@ function macroExpandAllWithContext(form, env, ctx) {
2233
2416
  return expanded.every((e, i) => e === form.value[i]) ? form : cljList(expanded);
2234
2417
  }
2235
2418
 
2236
- // src/core/positions.ts
2237
- function setPos(val, pos) {
2238
- Object.defineProperty(val, "_pos", {
2239
- value: pos,
2240
- enumerable: false,
2241
- writable: true,
2242
- configurable: true
2243
- });
2244
- }
2245
- function getPos(val) {
2246
- return val._pos;
2247
- }
2248
- function getLineCol(source, offset) {
2249
- const lines = source.split(`
2250
- `);
2251
- let pos = 0;
2252
- for (let i = 0;i < lines.length; i++) {
2253
- const lineEnd = pos + lines[i].length;
2254
- if (offset <= lineEnd) {
2255
- return { line: i + 1, col: offset - pos, lineText: lines[i] };
2256
- }
2257
- pos = lineEnd + 1;
2258
- }
2259
- const last = lines[lines.length - 1];
2260
- return { line: lines.length, col: last.length, lineText: last };
2261
- }
2262
- function formatErrorContext(source, pos) {
2263
- const { line, col, lineText } = getLineCol(source, pos.start);
2264
- const span = Math.max(1, pos.end - pos.start);
2265
- const caret = " ".repeat(col) + "^".repeat(span);
2266
- return `
2267
- at line ${line}, col ${col + 1}:
2268
- ${lineText}
2269
- ${caret}`;
2270
- }
2271
-
2272
2419
  // src/core/evaluator/collections.ts
2273
2420
  function evaluateVector(vector, env, ctx) {
2274
- return cljVector(vector.value.map((v) => ctx.evaluate(v, env)));
2421
+ const evaluated = vector.value.map((v) => ctx.evaluate(v, env));
2422
+ if (vector.meta)
2423
+ return { kind: "vector", value: evaluated, meta: vector.meta };
2424
+ return cljVector(evaluated);
2275
2425
  }
2276
2426
  function evaluateMap(map, env, ctx) {
2277
2427
  let entries = [];
@@ -2280,6 +2430,8 @@ function evaluateMap(map, env, ctx) {
2280
2430
  const evaluatedValue = ctx.evaluate(value, env);
2281
2431
  entries.push([evaluatedKey, evaluatedValue]);
2282
2432
  }
2433
+ if (map.meta)
2434
+ return { kind: "map", entries, meta: map.meta };
2283
2435
  return cljMap(entries);
2284
2436
  }
2285
2437
 
@@ -2311,7 +2463,19 @@ function evaluateList(list, env, ctx) {
2311
2463
  throw new EvaluationError(`${name} is not callable`, { list, env });
2312
2464
  }
2313
2465
  const args = list.value.slice(1).map((v) => ctx.evaluate(v, env));
2314
- return ctx.applyCallable(evaledFirst, args, env);
2466
+ try {
2467
+ return ctx.applyCallable(evaledFirst, args, env);
2468
+ } catch (e) {
2469
+ if (e instanceof EvaluationError && e.data?.argIndex !== undefined && !e.pos) {
2470
+ const argForm = list.value[e.data.argIndex + 1];
2471
+ if (argForm) {
2472
+ const pos = getPos(argForm);
2473
+ if (pos)
2474
+ e.pos = pos;
2475
+ }
2476
+ }
2477
+ throw e;
2478
+ }
2315
2479
  }
2316
2480
 
2317
2481
  // src/core/evaluator/evaluate.ts
@@ -2447,13 +2611,13 @@ var predicateFunctions = {
2447
2611
  "coll?": withDoc(cljNativeFunction("coll?", (x) => cljBoolean(x !== undefined && isCollection(x))), "Returns true if the value is a collection, false otherwise.", [["x"]]),
2448
2612
  some: withDoc(cljNativeFunction("some", (pred, coll) => {
2449
2613
  if (pred === undefined || !isAFunction(pred)) {
2450
- throw new EvaluationError(`some expects a function as first argument${pred !== undefined ? `, got ${printString(pred)}` : ""}`, { pred });
2614
+ throw EvaluationError.atArg(`some expects a function as first argument${pred !== undefined ? `, got ${printString(pred)}` : ""}`, { pred }, 0);
2451
2615
  }
2452
2616
  if (coll === undefined) {
2453
2617
  return cljNil();
2454
2618
  }
2455
2619
  if (!isSeqable(coll)) {
2456
- throw new EvaluationError(`some expects a collection or string as second argument, got ${printString(coll)}`, { coll });
2620
+ throw EvaluationError.atArg(`some expects a collection or string as second argument, got ${printString(coll)}`, { coll }, 1);
2457
2621
  }
2458
2622
  for (const item of toSeq(coll)) {
2459
2623
  const result = applyFunction(pred, [item]);
@@ -2465,10 +2629,10 @@ var predicateFunctions = {
2465
2629
  }), "Returns the first truthy result of applying pred to each item in coll, or nil if no item satisfies pred.", [["pred", "coll"]]),
2466
2630
  "every?": withDoc(cljNativeFunction("every?", (pred, coll) => {
2467
2631
  if (pred === undefined || !isAFunction(pred)) {
2468
- throw new EvaluationError(`every? expects a function as first argument${pred !== undefined ? `, got ${printString(pred)}` : ""}`, { pred });
2632
+ throw EvaluationError.atArg(`every? expects a function as first argument${pred !== undefined ? `, got ${printString(pred)}` : ""}`, { pred }, 0);
2469
2633
  }
2470
2634
  if (coll === undefined || !isSeqable(coll)) {
2471
- throw new EvaluationError(`every? expects a collection or string as second argument${coll !== undefined ? `, got ${printString(coll)}` : ""}`, { coll });
2635
+ throw EvaluationError.atArg(`every? expects a collection or string as second argument${coll !== undefined ? `, got ${printString(coll)}` : ""}`, { coll }, 1);
2472
2636
  }
2473
2637
  for (const item of toSeq(coll)) {
2474
2638
  if (isFalsy(applyFunction(pred, [item]))) {
@@ -2875,13 +3039,13 @@ var utilFunctions = {
2875
3039
  }), "Returns a concatenated string representation of the given values.", [["&", "args"]]),
2876
3040
  subs: withDoc(cljNativeFunction("subs", (s, start, end) => {
2877
3041
  if (s === undefined || s.kind !== "string") {
2878
- throw new EvaluationError(`subs expects a string as first argument${s !== undefined ? `, got ${printString(s)}` : ""}`, { s });
3042
+ throw EvaluationError.atArg(`subs expects a string as first argument${s !== undefined ? `, got ${printString(s)}` : ""}`, { s }, 0);
2879
3043
  }
2880
3044
  if (start === undefined || start.kind !== "number") {
2881
- throw new EvaluationError(`subs expects a number as second argument${start !== undefined ? `, got ${printString(start)}` : ""}`, { start });
3045
+ throw EvaluationError.atArg(`subs expects a number as second argument${start !== undefined ? `, got ${printString(start)}` : ""}`, { start }, 1);
2882
3046
  }
2883
3047
  if (end !== undefined && end.kind !== "number") {
2884
- throw new EvaluationError(`subs expects a number as optional third argument${end !== undefined ? `, got ${printString(end)}` : ""}`, { end });
3048
+ throw EvaluationError.atArg(`subs expects a number as optional third argument${end !== undefined ? `, got ${printString(end)}` : ""}`, { end }, 2);
2885
3049
  }
2886
3050
  const from = start.value;
2887
3051
  const to = end?.value;
@@ -2921,7 +3085,7 @@ var utilFunctions = {
2921
3085
  }
2922
3086
  const prefix = args[0];
2923
3087
  if (prefix !== undefined && prefix.kind !== "string") {
2924
- throw new EvaluationError(`gensym prefix must be a string${prefix !== undefined ? `, got ${printString(prefix)}` : ""}`, { prefix });
3088
+ throw EvaluationError.atArg(`gensym prefix must be a string${prefix !== undefined ? `, got ${printString(prefix)}` : ""}`, { prefix }, 0);
2925
3089
  }
2926
3090
  const p = prefix?.kind === "string" ? prefix.value : "G";
2927
3091
  return cljSymbol(makeGensym(p));
@@ -2932,9 +3096,8 @@ var utilFunctions = {
2932
3096
  form
2933
3097
  });
2934
3098
  }
2935
- const rootEnv = getRootEnv(callEnv);
2936
- const expanded = ctx.expandAll(form, rootEnv);
2937
- return ctx.evaluate(expanded, rootEnv);
3099
+ const expanded = ctx.expandAll(form, callEnv);
3100
+ return ctx.evaluate(expanded, callEnv);
2938
3101
  }), "Evaluates the given form in the global environment and returns the result.", [["form"]]),
2939
3102
  "macroexpand-1": withDoc(cljNativeFunctionWithContext("macroexpand-1", (ctx, callEnv, form) => {
2940
3103
  if (!isList(form) || form.value.length === 0)
@@ -2942,7 +3105,7 @@ var utilFunctions = {
2942
3105
  const head = form.value[0];
2943
3106
  if (!isSymbol(head))
2944
3107
  return form;
2945
- const macroValue = tryLookup(head.name, getRootEnv(callEnv));
3108
+ const macroValue = tryLookup(head.name, callEnv);
2946
3109
  if (macroValue === undefined)
2947
3110
  return form;
2948
3111
  if (!isMacro(macroValue))
@@ -2950,7 +3113,6 @@ var utilFunctions = {
2950
3113
  return ctx.applyMacro(macroValue, form.value.slice(1));
2951
3114
  }), "If the head of the form is a macro, expands it and returns the resulting forms. Otherwise, returns the form unchanged.", [["form"]]),
2952
3115
  macroexpand: withDoc(cljNativeFunctionWithContext("macroexpand", (ctx, callEnv, form) => {
2953
- const rootEnv = getRootEnv(callEnv);
2954
3116
  let current = form;
2955
3117
  while (true) {
2956
3118
  if (!isList(current) || current.value.length === 0)
@@ -2958,7 +3120,7 @@ var utilFunctions = {
2958
3120
  const head = current.value[0];
2959
3121
  if (!isSymbol(head))
2960
3122
  return current;
2961
- const macroValue = tryLookup(head.name, rootEnv);
3123
+ const macroValue = tryLookup(head.name, callEnv);
2962
3124
  if (macroValue === undefined)
2963
3125
  return current;
2964
3126
  if (!isMacro(macroValue))
@@ -2970,7 +3132,7 @@ var utilFunctions = {
2970
3132
  "",
2971
3133
  "Note neither macroexpand-1 nor macroexpand will expand macros in sub-forms"
2972
3134
  ]), [["form"]]),
2973
- "macroexpand-all": withDoc(cljNativeFunctionWithContext("macroexpand-all", (ctx, callEnv, form) => ctx.expandAll(form, getRootEnv(callEnv))), joinLines([
3135
+ "macroexpand-all": withDoc(cljNativeFunctionWithContext("macroexpand-all", (ctx, callEnv, form) => ctx.expandAll(form, callEnv)), joinLines([
2974
3136
  "Fully expands all macros in a form recursively — including in sub-forms.",
2975
3137
  "",
2976
3138
  "Unlike macroexpand, this descends into every sub-expression.",
@@ -2978,7 +3140,7 @@ var utilFunctions = {
2978
3140
  ]), [["form"]]),
2979
3141
  namespace: withDoc(cljNativeFunction("namespace", (x) => {
2980
3142
  if (x === undefined) {
2981
- throw new EvaluationError("namespace expects an argument", { x });
3143
+ throw EvaluationError.atArg("namespace expects an argument", { x }, 0);
2982
3144
  }
2983
3145
  let raw;
2984
3146
  if (isKeyword(x)) {
@@ -2986,7 +3148,7 @@ var utilFunctions = {
2986
3148
  } else if (isSymbol(x)) {
2987
3149
  raw = x.name;
2988
3150
  } else {
2989
- throw new EvaluationError(`namespace expects a keyword or symbol, got ${printString(x)}`, { x });
3151
+ throw EvaluationError.atArg(`namespace expects a keyword or symbol, got ${printString(x)}`, { x }, 0);
2990
3152
  }
2991
3153
  const slashIdx = raw.indexOf("/");
2992
3154
  if (slashIdx <= 0)
@@ -2995,7 +3157,7 @@ var utilFunctions = {
2995
3157
  }), "Returns the namespace string of a qualified keyword or symbol, or nil if the argument is not qualified.", [["x"]]),
2996
3158
  name: withDoc(cljNativeFunction("name", (x) => {
2997
3159
  if (x === undefined) {
2998
- throw new EvaluationError("name expects an argument", { x });
3160
+ throw EvaluationError.atArg("name expects an argument", { x }, 0);
2999
3161
  }
3000
3162
  let raw;
3001
3163
  if (isKeyword(x)) {
@@ -3005,7 +3167,7 @@ var utilFunctions = {
3005
3167
  } else if (x.kind === "string") {
3006
3168
  return x;
3007
3169
  } else {
3008
- throw new EvaluationError(`name expects a keyword, symbol, or string, got ${printString(x)}`, { x });
3170
+ throw EvaluationError.atArg(`name expects a keyword, symbol, or string, got ${printString(x)}`, { x }, 0);
3009
3171
  }
3010
3172
  const slashIdx = raw.indexOf("/");
3011
3173
  return cljString(slashIdx >= 0 ? raw.slice(slashIdx + 1) : raw);
@@ -3017,13 +3179,13 @@ var utilFunctions = {
3017
3179
  });
3018
3180
  }
3019
3181
  if (args[0].kind !== "string") {
3020
- throw new EvaluationError(`keyword expects a string, got ${printString(args[0])}`, { args });
3182
+ throw EvaluationError.atArg(`keyword expects a string, got ${printString(args[0])}`, { args }, 0);
3021
3183
  }
3022
3184
  if (args.length === 1) {
3023
3185
  return cljKeyword(`:${args[0].value}`);
3024
3186
  }
3025
3187
  if (args[1].kind !== "string") {
3026
- throw new EvaluationError(`keyword second argument must be a string, got ${printString(args[1])}`, { args });
3188
+ throw EvaluationError.atArg(`keyword second argument must be a string, got ${printString(args[1])}`, { args }, 1);
3027
3189
  }
3028
3190
  return cljKeyword(`:${args[0].value}/${args[1].value}`);
3029
3191
  }), joinLines([
@@ -3078,14 +3240,14 @@ var nativeFunctions = {
3078
3240
  };
3079
3241
  function loadCoreFunctions(env, output) {
3080
3242
  for (const [key, value] of Object.entries(nativeFunctions)) {
3081
- define(key, value, env);
3243
+ internVar(key, value, env);
3082
3244
  }
3083
3245
  const emit = output ?? ((text) => console.log(text));
3084
- define("println", cljNativeFunction("println", (...args) => {
3246
+ internVar("println", cljNativeFunction("println", (...args) => {
3085
3247
  emit(args.map(valueToString).join(" "));
3086
3248
  return cljNil();
3087
3249
  }), env);
3088
- define("print", cljNativeFunction("print", (...args) => {
3250
+ internVar("print", cljNativeFunction("print", (...args) => {
3089
3251
  emit(args.map(valueToString).join(" "));
3090
3252
  return cljNil();
3091
3253
  }), env);
@@ -3201,7 +3363,8 @@ var isNumber = (char) => {
3201
3363
  var isDot = (char) => char === ".";
3202
3364
  var isKeywordStart = (char) => char === ":";
3203
3365
  var isHash = (char) => char === "#";
3204
- var isDelimiter = (char) => isLParen(char) || isRParen(char) || isLBracket(char) || isRBracket(char) || isLBrace(char) || isRBrace(char) || isBacktick(char) || isSingleQuote(char) || isAt(char);
3366
+ var isCaret = (char) => char === "^";
3367
+ var isDelimiter = (char) => isLParen(char) || isRParen(char) || isLBracket(char) || isRBracket(char) || isLBrace(char) || isRBrace(char) || isBacktick(char) || isSingleQuote(char) || isAt(char) || isCaret(char);
3205
3368
  var parseWhitespace = (ctx) => {
3206
3369
  const scanner = ctx.scanner;
3207
3370
  const start = scanner.position();
@@ -3347,6 +3510,12 @@ var parseDerefToken = (ctx) => {
3347
3510
  scanner.advance();
3348
3511
  return { kind: "Deref", start, end: scanner.position() };
3349
3512
  };
3513
+ var parseMetaToken = (ctx) => {
3514
+ const scanner = ctx.scanner;
3515
+ const start = scanner.position();
3516
+ scanner.advance();
3517
+ return { kind: "Meta", start, end: scanner.position() };
3518
+ };
3350
3519
  var parseRegexLiteral = (ctx, start) => {
3351
3520
  const scanner = ctx.scanner;
3352
3521
  scanner.advance();
@@ -3463,6 +3632,7 @@ var tokenParseEntries = [
3463
3632
  ],
3464
3633
  [isTilde, parseTilde],
3465
3634
  [isAt, parseDerefToken],
3635
+ [isCaret, parseMetaToken],
3466
3636
  [isHash, parseDispatch]
3467
3637
  ];
3468
3638
  function parseNextToken(ctx) {
@@ -3612,6 +3782,35 @@ var readUnquote = (ctx) => {
3612
3782
  }
3613
3783
  return { kind: valueKeywords.list, value: [cljSymbol("unquote"), value] };
3614
3784
  };
3785
+ var readMeta = (ctx) => {
3786
+ const scanner = ctx.scanner;
3787
+ const token = scanner.peek();
3788
+ if (!token) {
3789
+ throw new ReaderError("Unexpected end of input while parsing metadata", scanner.position());
3790
+ }
3791
+ scanner.advance();
3792
+ const metaForm = readForm(ctx);
3793
+ const target = readForm(ctx);
3794
+ let metaEntries;
3795
+ if (metaForm.kind === "keyword") {
3796
+ metaEntries = [[metaForm, cljBoolean(true)]];
3797
+ } else if (metaForm.kind === "map") {
3798
+ metaEntries = metaForm.entries;
3799
+ } else if (metaForm.kind === "symbol") {
3800
+ metaEntries = [[cljKeyword(":tag"), metaForm]];
3801
+ } else {
3802
+ throw new ReaderError("Metadata must be a keyword, map, or symbol", token);
3803
+ }
3804
+ if (target.kind === "symbol" || target.kind === "list" || target.kind === "vector" || target.kind === "map") {
3805
+ const existingEntries = target.meta ? target.meta.entries : [];
3806
+ const result = { ...target, meta: cljMap([...existingEntries, ...metaEntries]) };
3807
+ const pos = getPos(target);
3808
+ if (pos)
3809
+ setPos(result, pos);
3810
+ return result;
3811
+ }
3812
+ return target;
3813
+ };
3615
3814
  var readVarQuote = (ctx) => {
3616
3815
  const scanner = ctx.scanner;
3617
3816
  const token = scanner.peek();
@@ -3945,6 +4144,8 @@ function readForm(ctx) {
3945
4144
  return readDeref(ctx);
3946
4145
  case tokenKeywords.VarQuote:
3947
4146
  return readVarQuote(ctx);
4147
+ case tokenKeywords.Meta:
4148
+ return readMeta(ctx);
3948
4149
  case tokenKeywords.Regex:
3949
4150
  return readRegex(ctx);
3950
4151
  default:
@@ -3979,6 +4180,13 @@ var clojure_coreSource = `(ns clojure.core)
3979
4180
  \`(def ~name (with-meta (fn ~@rest-decl) {:doc ~doc :arglists '~arglists}))
3980
4181
  \`(def ~name (with-meta (fn ~@rest-decl) {:arglists '~arglists})))))
3981
4182
 
4183
+
4184
+ (defn vary-meta
4185
+ "Returns an object of the same type and value as obj, with
4186
+ (apply f (meta obj) args) as its metadata."
4187
+ [obj f & args]
4188
+ (with-meta obj (apply f (meta obj) args)))
4189
+
3982
4190
  (defn next
3983
4191
  "Returns a seq of the items after the first. Calls seq on its
3984
4192
  argument. If there are no more items, returns nil."
@@ -5009,11 +5217,11 @@ function buildSessionApi(state, options) {
5009
5217
  const coreEnv = registry.get("clojure.core");
5010
5218
  coreEnv.resolveNs = (name) => registry.get(name) ?? null;
5011
5219
  const emitFn = options?.output ?? ((text) => console.log(text));
5012
- define("println", cljNativeFunction("println", (...args) => {
5220
+ internVar("println", cljNativeFunction("println", (...args) => {
5013
5221
  emitFn(args.map(valueToString).join(" "));
5014
5222
  return cljNil();
5015
5223
  }), coreEnv);
5016
- define("print", cljNativeFunction("print", (...args) => {
5224
+ internVar("print", cljNativeFunction("print", (...args) => {
5017
5225
  emitFn(args.map(valueToString).join(" "));
5018
5226
  return cljNil();
5019
5227
  }), coreEnv);
@@ -5063,14 +5271,14 @@ function buildSessionApi(state, options) {
5063
5271
  function getNsEnv(name) {
5064
5272
  return registry.get(name) ?? null;
5065
5273
  }
5066
- define("require", cljNativeFunction("require", (...args) => {
5274
+ internVar("require", cljNativeFunction("require", (...args) => {
5067
5275
  const currentEnv = registry.get(currentNs);
5068
5276
  for (const arg of args) {
5069
5277
  processRequireSpec(arg, currentEnv, registry, resolveNamespace);
5070
5278
  }
5071
5279
  return cljNil();
5072
5280
  }), coreEnv);
5073
- define("resolve", cljNativeFunction("resolve", (sym) => {
5281
+ internVar("resolve", cljNativeFunction("resolve", (sym) => {
5074
5282
  if (!isSymbol(sym))
5075
5283
  return cljNil();
5076
5284
  const slashIdx = sym.name.indexOf("/");
@@ -5093,16 +5301,25 @@ function buildSessionApi(state, options) {
5093
5301
  }
5094
5302
  }
5095
5303
  }
5096
- function loadFile(source, nsName) {
5304
+ function loadFile(source, nsName, filePath) {
5097
5305
  const tokens = tokenize(source);
5098
5306
  const targetNs = extractNsNameFromTokens(tokens) ?? nsName ?? "user";
5099
5307
  const aliasMap = extractAliasMapFromTokens(tokens);
5100
5308
  const forms = readForms(tokens, targetNs, aliasMap);
5101
5309
  const env = ensureNs(targetNs);
5310
+ ctx.currentSource = source;
5311
+ ctx.currentFile = filePath;
5312
+ ctx.currentLineOffset = 0;
5313
+ ctx.currentColOffset = 0;
5102
5314
  processNsRequires(forms, env);
5103
- for (const form of forms) {
5104
- const expanded = ctx.expandAll(form, env);
5105
- ctx.evaluate(expanded, env);
5315
+ try {
5316
+ for (const form of forms) {
5317
+ const expanded = ctx.expandAll(form, env);
5318
+ ctx.evaluate(expanded, env);
5319
+ }
5320
+ } finally {
5321
+ ctx.currentSource = undefined;
5322
+ ctx.currentFile = undefined;
5106
5323
  }
5107
5324
  return targetNs;
5108
5325
  }
@@ -5115,7 +5332,11 @@ function buildSessionApi(state, options) {
5115
5332
  getNs,
5116
5333
  loadFile,
5117
5334
  addSourceRoot,
5118
- evaluate(source) {
5335
+ evaluate(source, opts) {
5336
+ ctx.currentSource = source;
5337
+ ctx.currentFile = opts?.file;
5338
+ ctx.currentLineOffset = opts?.lineOffset ?? 0;
5339
+ ctx.currentColOffset = opts?.colOffset ?? 0;
5119
5340
  try {
5120
5341
  const tokens = tokenize(source);
5121
5342
  const declaredNs = extractNsNameFromTokens(tokens);
@@ -5149,9 +5370,15 @@ function buildSessionApi(state, options) {
5149
5370
  });
5150
5371
  }
5151
5372
  if ((e instanceof EvaluationError || e instanceof ReaderError) && e.pos) {
5152
- e.message += formatErrorContext(source, e.pos);
5373
+ e.message += formatErrorContext(source, e.pos, {
5374
+ lineOffset: ctx.currentLineOffset,
5375
+ colOffset: ctx.currentColOffset
5376
+ });
5153
5377
  }
5154
5378
  throw e;
5379
+ } finally {
5380
+ ctx.currentSource = undefined;
5381
+ ctx.currentFile = undefined;
5155
5382
  }
5156
5383
  },
5157
5384
  evaluateForms(forms) {
@@ -5431,7 +5658,7 @@ class BDecoderStream extends stream.Transform {
5431
5658
  }
5432
5659
 
5433
5660
  // src/bin/version.ts
5434
- var VERSION = "0.0.11";
5661
+ var VERSION = "0.0.12";
5435
5662
 
5436
5663
  // src/host/node.ts
5437
5664
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs";
@@ -5534,8 +5761,10 @@ function handleEval(msg, managed, encoder) {
5534
5761
  const id = msg["id"] ?? "";
5535
5762
  const code = msg["code"] ?? "";
5536
5763
  managed.currentMsgId = id;
5764
+ const lineOffset = typeof msg["line"] === "number" ? msg["line"] - 1 : 0;
5765
+ const colOffset = typeof msg["column"] === "number" ? msg["column"] - 1 : 0;
5537
5766
  try {
5538
- const result = managed.session.evaluate(code);
5767
+ const result = managed.session.evaluate(code, { lineOffset, colOffset });
5539
5768
  done(encoder, id, managed.id, {
5540
5769
  value: printString(result),
5541
5770
  ns: managed.session.currentNs
@@ -5565,7 +5794,7 @@ function handleLoadFile(msg, managed, encoder) {
5565
5794
  }
5566
5795
  }
5567
5796
  const nsHint = fileName.replace(/\.clj$/, "").replace(/\//g, ".") || undefined;
5568
- const loadedNs = managed.session.loadFile(source, nsHint);
5797
+ const loadedNs = managed.session.loadFile(source, nsHint, filePath || undefined);
5569
5798
  if (filePath && loadedNs) {
5570
5799
  managed.nsToFile.set(loadedNs, filePath);
5571
5800
  }
@@ -5705,13 +5934,30 @@ function handleInfo(msg, managed, encoder) {
5705
5934
  }
5706
5935
  const meta = extractMeta(resolved.value);
5707
5936
  const file = managed.nsToFile.get(resolved.resolvedNs);
5937
+ let varLine;
5938
+ let varColumn;
5939
+ let varFile;
5940
+ if (resolved.value.kind === "var" && resolved.value.meta) {
5941
+ for (const [k, v] of resolved.value.meta.entries) {
5942
+ if (k.kind !== "keyword")
5943
+ continue;
5944
+ if (k.name === ":line" && v.kind === "number")
5945
+ varLine = v.value;
5946
+ if (k.name === ":column" && v.kind === "number")
5947
+ varColumn = v.value;
5948
+ if (k.name === ":file" && v.kind === "string")
5949
+ varFile = v.value;
5950
+ }
5951
+ }
5708
5952
  done(encoder, id, managed.id, {
5709
5953
  ns: resolved.resolvedNs,
5710
5954
  name: resolved.localName,
5711
5955
  doc: meta.doc,
5712
5956
  "arglists-str": meta.arglistsStr,
5713
5957
  type: meta.type,
5714
- ...file ? { file } : {}
5958
+ ...varFile ?? file ? { file: varFile ?? file } : {},
5959
+ ...varLine !== undefined ? { line: varLine } : {},
5960
+ ...varColumn !== undefined ? { column: varColumn } : {}
5715
5961
  });
5716
5962
  }
5717
5963
  function handleLookup(msg, managed, encoder) {