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.
- package/core/System/Enumerable.nm +2 -0
- package/dist/index.mjs +55 -5
- package/package.json +1 -1
|
@@ -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
|
|
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) {
|