nomen-lang 0.2.2 → 0.2.3

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,5 +1,7 @@
1
1
  // Trait for collection types that can be iterated with `for x of collection`.
2
2
  // The loop variable receives int indices 0..length-1; use .at(i) to access values.
3
+ // (`List<T>` is deliberately NOT Enumerable: `for x of some_list` desugars to
4
+ // element iteration like arrays, which is what loop bodies almost always want.)
3
5
  /**
4
6
  * A trait for collections that can be iterated with `for x of collection`
5
7
  **/
package/dist/index.mjs CHANGED
@@ -37940,7 +37940,28 @@ function add_source(folder_path, file_path, inputs, lib_path) {
37940
37940
  source = source.replaceAll(/^import(.*)$/gm, (match, name) => {
37941
37941
  const trimmed = name.trim();
37942
37942
  if (trimmed === "System" || trimmed.startsWith("System::")) return match;
37943
- const import_file_path = `./${trimmed.split("::").join("/")}.nm`;
37943
+ const segments = trimmed.split("::").map((s) => s.trim()).filter((s) => s.length > 0);
37944
+ if (!segments.length) return "";
37945
+ const rel = segments.join("/");
37946
+ let is_dir = false;
37947
+ try {
37948
+ is_dir = fs.statSync(path.resolve(folder_path, rel)).isDirectory();
37949
+ } catch {}
37950
+ if (is_dir) {
37951
+ let names;
37952
+ try {
37953
+ names = fs.readdirSync(path.resolve(folder_path, rel));
37954
+ } catch {
37955
+ return "";
37956
+ }
37957
+ for (const name of names.sort()) {
37958
+ if (!name.endsWith(".nm")) continue;
37959
+ const import_file_path = `./${rel}/${name}`;
37960
+ if (!inputs.has(import_file_path)) add_source(folder_path, import_file_path, inputs, lib_path);
37961
+ }
37962
+ return "";
37963
+ }
37964
+ const import_file_path = `./${rel}.nm`;
37944
37965
  if (!inputs.has(import_file_path)) add_source(folder_path, import_file_path, inputs, lib_path);
37945
37966
  return "";
37946
37967
  });
@@ -45418,6 +45439,25 @@ function has_trait(type_name, trait_name, status) {
45418
45439
  if (!struct) return false;
45419
45440
  return struct.traits.includes(trait_name);
45420
45441
  }
45442
+ /**
45443
+ * The element type of a `List<T>` value, from either the generic annotation
45444
+ * (`List` + type args) or the monomorphized struct (`List_Diff` via its
45445
+ * recorded source args) — mirroring the lookup in check_access_node.
45446
+ * Returns undefined when the List struct isn't in scope at all (e.g. bare
45447
+ * parse without the System library) or the element can't be recovered, in
45448
+ * which case the loop falls through to the array-or-Enumerable error.
45449
+ */
45450
+ function list_element_type(t, status) {
45451
+ if (t.name === "List") {
45452
+ const generic = status.structs.find((s) => s.name === "List");
45453
+ if (!generic || !generic.functions.some((f) => f.name === "at")) return void 0;
45454
+ return t.type_args?.[0];
45455
+ }
45456
+ const struct = t.name ? status.structs.find((s) => s.name === t.name) : void 0;
45457
+ if (!struct?.name.startsWith("List_")) return void 0;
45458
+ if (!struct.functions.some((f) => f.name === "at")) return void 0;
45459
+ return struct.source_type_args?.[0];
45460
+ }
45421
45461
  function check_for_loop_node(for_loop, status) {
45422
45462
  if (for_loop.list && !(for_loop.list instanceof RangeNode) && for_loop.list.node_type === "value") {
45423
45463
  const list_type = type_from_value_node(for_loop.list, status);
@@ -45429,13 +45469,23 @@ function check_for_loop_node(for_loop, status) {
45429
45469
  check_for_loop_node(for_loop, status);
45430
45470
  return;
45431
45471
  }
45472
+ const list_elem = list_element_type(list_type, status);
45473
+ if (list_elem && !enumerable) {
45474
+ if (for_loop.item_is_ref) {
45475
+ add_error(status, `'ref' iteration is not supported for List<T> — index explicitly (for i of 0..xs.length) and use .set to write back`, for_loop.item.start);
45476
+ return;
45477
+ }
45478
+ desugar_array_for_loop(for_loop, list_type, list_elem);
45479
+ check_for_loop_node(for_loop, status);
45480
+ return;
45481
+ }
45432
45482
  }
45433
45483
  let for_status = clone_status(status);
45434
45484
  if (for_loop.list) {
45435
45485
  check_node(for_loop.list, for_status);
45436
45486
  const list_type = type_from_value_node(for_loop.list, for_status);
45437
45487
  const is_enumerable = list_type.name ? has_trait(list_type.name, "Enumerable", for_status) : false;
45438
- if (!list_type.is_array && !is_enumerable && list_type.name) add_error(for_status, `For loop list must be an array or Enumerable, not ${list_type.name}`, for_loop.list.start);
45488
+ if (!list_type.is_array && !is_enumerable && list_type.name) add_error(for_status, `For loop list must be an array, List, or Enumerable, not ${list_type.name}`, for_loop.list.start);
45439
45489
  if (for_loop.item_is_ref && list_type.is_array && for_loop.list.node_type === "value") {
45440
45490
  const list_name = for_loop.list.value;
45441
45491
  const list_value = for_status.values.findLast((v) => v.name === list_name);
@@ -45487,17 +45537,17 @@ function check_for_loop_node(for_loop, status) {
45487
45537
  * break/continue so mutations persist on all exit paths. (The write-back is
45488
45538
  * skipped on `return`, matching Rust's copy semantics.)
45489
45539
  */
45490
- function desugar_array_for_loop(for_loop, array_type) {
45540
+ function desugar_array_for_loop(for_loop, array_type, elem_type) {
45491
45541
  const list = for_loop.list;
45492
45542
  const start = list.start;
45493
45543
  const original_item = for_loop.item;
45494
45544
  const idx_name = `__for_idx_${original_item.value}`;
45495
45545
  const is_ref = !!for_loop.item_is_ref;
45496
45546
  for_loop.item = new ValueNode(original_item.start, idx_name);
45497
- for_loop.list = new RangeNode(start, new ValueNode(start, "0"), array_type.storage_kind === "heap_array" || !array_type.length ? new AccessNode(start, clone_node(list), new AccessFieldNode(start, "length")) : clone_node(array_type.length));
45547
+ for_loop.list = new RangeNode(start, new ValueNode(start, "0"), elem_type !== void 0 || array_type.storage_kind === "heap_array" || !array_type.length ? new AccessNode(start, clone_node(list), new AccessFieldNode(start, "length")) : clone_node(array_type.length));
45498
45548
  const at_call = new AccessFunctionCallNode(start, "at", new Type(""), [new ValueNode(start, idx_name)]);
45499
45549
  const at_access = new AccessNode(start, clone_node(list), at_call);
45500
- const decl = new DeclarationNode(original_item.start, "private", is_ref ? "var" : "const", original_item.value, new Type(array_type.name), at_access);
45550
+ const decl = new DeclarationNode(original_item.start, "private", is_ref ? "var" : "const", original_item.value, elem_type ?? new Type(array_type.name), at_access);
45501
45551
  decl.is_loop_iterator = true;
45502
45552
  for_loop.statements.unshift(decl);
45503
45553
  if (is_ref) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nomen-lang",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "The CLI for the Nomen programming language.",
5
5
  "keywords": [],
6
6
  "license": "ISC",