nomen-lang 0.0.10 → 0.0.11
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/dist/index.mjs +215 -27
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3742,6 +3742,9 @@ var ForLoopNode = class extends BaseNode {
|
|
|
3742
3742
|
index;
|
|
3743
3743
|
update;
|
|
3744
3744
|
statements;
|
|
3745
|
+
/** `for ref x of arr` — the user wants mutable element access. For arrays
|
|
3746
|
+
* this desugars to copy-out / mutate / copy-back via .at()/.set(). */
|
|
3747
|
+
item_is_ref;
|
|
3745
3748
|
constructor(start, item, list, statements, update) {
|
|
3746
3749
|
super("for", start);
|
|
3747
3750
|
this.item = item;
|
|
@@ -5094,6 +5097,7 @@ function build_break_node$1(status) {
|
|
|
5094
5097
|
const loop = status.loop_labels?.[status.loop_labels.length - 1];
|
|
5095
5098
|
if (loop) {
|
|
5096
5099
|
emit_cleanup_to_loop_depth(status);
|
|
5100
|
+
status.loop_writebacks?.[status.loop_writebacks.length - 1]?.();
|
|
5097
5101
|
status.code += `b ${loop.end}\n`;
|
|
5098
5102
|
}
|
|
5099
5103
|
}
|
|
@@ -5172,6 +5176,7 @@ function build_continue_node$1(status) {
|
|
|
5172
5176
|
const loop = status.loop_labels?.[status.loop_labels.length - 1];
|
|
5173
5177
|
if (loop) {
|
|
5174
5178
|
emit_cleanup_to_loop_depth(status);
|
|
5179
|
+
status.loop_writebacks?.[status.loop_writebacks.length - 1]?.();
|
|
5175
5180
|
status.code += `b ${loop.start}\n`;
|
|
5176
5181
|
}
|
|
5177
5182
|
}
|
|
@@ -6604,6 +6609,8 @@ function build_for_loop_node$1(node, status) {
|
|
|
6604
6609
|
end: end_label,
|
|
6605
6610
|
cleanup_depth
|
|
6606
6611
|
});
|
|
6612
|
+
if (!status.loop_writebacks) status.loop_writebacks = [];
|
|
6613
|
+
status.loop_writebacks.push(void 0);
|
|
6607
6614
|
if (status.function_return_label) {
|
|
6608
6615
|
const item_offset = allocate_stack_space(status, 8);
|
|
6609
6616
|
status.stack_offsets.set(item_name, item_offset);
|
|
@@ -6817,7 +6824,49 @@ function build_for_loop_node$1(node, status) {
|
|
|
6817
6824
|
else status.code += `ldr x0, [x0]\n`;
|
|
6818
6825
|
emit_var_store(status, "x0", item_name, element_size);
|
|
6819
6826
|
}
|
|
6827
|
+
if (node.item_is_ref) {
|
|
6828
|
+
const wb_struct_type = struct_type;
|
|
6829
|
+
const wb_element_size = element_size;
|
|
6830
|
+
const wb_item_name = item_name;
|
|
6831
|
+
const wb_idx_name = idx_name;
|
|
6832
|
+
const wb_list_name = list_name;
|
|
6833
|
+
const wb_list_is_pointer = list_is_pointer;
|
|
6834
|
+
const wb_shift = shift;
|
|
6835
|
+
status.loop_writebacks[status.loop_writebacks.length - 1] = () => {
|
|
6836
|
+
if (wb_list_is_pointer) {
|
|
6837
|
+
emit_var_load(status, "x9", wb_list_name, 8);
|
|
6838
|
+
if (status.heap_array_vars?.has(wb_list_name)) status.code += `add x9, x9, #8\n`;
|
|
6839
|
+
} else emit_var_address(status, "x9", wb_list_name);
|
|
6840
|
+
emit_var_load(status, "x10", wb_idx_name, 8);
|
|
6841
|
+
if (Number.isInteger(wb_shift) && wb_shift > 0) status.code += `add x9, x9, x10, lsl #${wb_shift}\n`;
|
|
6842
|
+
else {
|
|
6843
|
+
status.code += `mov x11, #${wb_element_size}\n`;
|
|
6844
|
+
status.code += `mul x10, x10, x11\n`;
|
|
6845
|
+
status.code += `add x9, x9, x10\n`;
|
|
6846
|
+
}
|
|
6847
|
+
if (wb_struct_type) {
|
|
6848
|
+
const item_offset = status.stack_offsets.get(wb_item_name);
|
|
6849
|
+
if (item_offset !== void 0) {
|
|
6850
|
+
const words = Math.ceil(wb_element_size / 8);
|
|
6851
|
+
for (let w = 0; w < words; w++) {
|
|
6852
|
+
status.code += `ldr x10, [x29, #${item_offset + w * 8}]\n`;
|
|
6853
|
+
status.code += `str x10, [x9, #${w * 8}]\n`;
|
|
6854
|
+
}
|
|
6855
|
+
}
|
|
6856
|
+
} else if (wb_element_size === 1) {
|
|
6857
|
+
emit_var_load(status, "x10", wb_item_name, wb_element_size);
|
|
6858
|
+
status.code += `strb w10, [x9]\n`;
|
|
6859
|
+
} else if (wb_element_size === 4) {
|
|
6860
|
+
emit_var_load(status, "x10", wb_item_name, wb_element_size);
|
|
6861
|
+
status.code += `str w10, [x9]\n`;
|
|
6862
|
+
} else {
|
|
6863
|
+
emit_var_load(status, "x10", wb_item_name, wb_element_size);
|
|
6864
|
+
status.code += `str x10, [x9]\n`;
|
|
6865
|
+
}
|
|
6866
|
+
};
|
|
6867
|
+
}
|
|
6820
6868
|
build_block_node$1(node, status);
|
|
6869
|
+
status.loop_writebacks[status.loop_writebacks.length - 1]?.();
|
|
6821
6870
|
if (node.update) {
|
|
6822
6871
|
status.code += `${continue_label}:\n`;
|
|
6823
6872
|
build_node$1(node.update, status);
|
|
@@ -6835,6 +6884,7 @@ function build_for_loop_node$1(node, status) {
|
|
|
6835
6884
|
else status.register_allocations = void 0;
|
|
6836
6885
|
status.buffer_data_cache = saved_buffer_cache;
|
|
6837
6886
|
status.loop_labels.pop();
|
|
6887
|
+
status.loop_writebacks?.pop();
|
|
6838
6888
|
status.scoped_declarations = old_scoped_declarations;
|
|
6839
6889
|
}
|
|
6840
6890
|
function is_enumerable_type$1(node, status) {
|
|
@@ -14064,6 +14114,7 @@ function build_async_block_node(node, status) {
|
|
|
14064
14114
|
//#region ../src/build_c/build_break_node.ts
|
|
14065
14115
|
function build_break_node(node, status) {
|
|
14066
14116
|
reclaim_to_loop_body(status);
|
|
14117
|
+
status.loop_writebacks?.[status.loop_writebacks.length - 1]?.();
|
|
14067
14118
|
status.code += `break;\n`;
|
|
14068
14119
|
}
|
|
14069
14120
|
//#endregion
|
|
@@ -14088,6 +14139,7 @@ function build_cast_node(node, status) {
|
|
|
14088
14139
|
//#region ../src/build_c/build_continue_node.ts
|
|
14089
14140
|
function build_continue_node(node, status) {
|
|
14090
14141
|
reclaim_to_loop_body(status);
|
|
14142
|
+
status.loop_writebacks?.[status.loop_writebacks.length - 1]?.();
|
|
14091
14143
|
status.code += `continue;\n`;
|
|
14092
14144
|
}
|
|
14093
14145
|
//#endregion
|
|
@@ -14373,6 +14425,7 @@ function build_for_loop_node(node, status) {
|
|
|
14373
14425
|
const old_deferred_frees = status.deferred_frees;
|
|
14374
14426
|
status.deferred_frees = [];
|
|
14375
14427
|
push_c_loop_frame(status);
|
|
14428
|
+
let ref_writeback;
|
|
14376
14429
|
if (node.item && node.list) if (node.list.node_type == "range") {
|
|
14377
14430
|
status.code += "{\n";
|
|
14378
14431
|
status.code += `${c_type("int")} `;
|
|
@@ -14439,14 +14492,27 @@ function build_for_loop_node(node, status) {
|
|
|
14439
14492
|
build_node(node.list, status);
|
|
14440
14493
|
status.code += `[${idx_var}];\n`;
|
|
14441
14494
|
}
|
|
14495
|
+
if (node.item_is_ref) {
|
|
14496
|
+
let wb_target;
|
|
14497
|
+
if (is_heap) wb_target = `((${elem_is_class ? `struct ${element_type} **` : `${elem_struct ? `struct ${element_type}` : c_type(element_type)} *`})((char *)${list_name} + sizeof(struct Array_${element_type})))[${idx_var}]`;
|
|
14498
|
+
else wb_target = `${list_name}[${idx_var}]`;
|
|
14499
|
+
const wb_code = `${wb_target} = ${node.item.value};\n`;
|
|
14500
|
+
ref_writeback = () => {
|
|
14501
|
+
status.code += wb_code;
|
|
14502
|
+
};
|
|
14503
|
+
}
|
|
14442
14504
|
}
|
|
14505
|
+
if (!status.loop_writebacks) status.loop_writebacks = [];
|
|
14506
|
+
status.loop_writebacks.push(ref_writeback);
|
|
14443
14507
|
build_block_node(node, status);
|
|
14444
14508
|
if (node.update) {
|
|
14445
14509
|
build_node(node.update, status);
|
|
14446
14510
|
status.code += ";\n";
|
|
14447
14511
|
}
|
|
14512
|
+
if (ref_writeback) ref_writeback();
|
|
14448
14513
|
build_auto_free(status);
|
|
14449
14514
|
status.code += `}\n`;
|
|
14515
|
+
status.loop_writebacks.pop();
|
|
14450
14516
|
if (node.list && (node.list.node_type == "range" || is_enumerable_type(node.list, status))) status.code += `}\n`;
|
|
14451
14517
|
pop_c_loop_frame(status);
|
|
14452
14518
|
leave_c_scope(status);
|
|
@@ -15410,8 +15476,7 @@ const CALL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
15410
15476
|
"trait",
|
|
15411
15477
|
"enum",
|
|
15412
15478
|
"bitset",
|
|
15413
|
-
"cast"
|
|
15414
|
-
"view"
|
|
15479
|
+
"cast"
|
|
15415
15480
|
]);
|
|
15416
15481
|
const NO_SPACE_BEFORE = /* @__PURE__ */ new Set([
|
|
15417
15482
|
",",
|
|
@@ -16021,9 +16086,10 @@ const PAIRS = {
|
|
|
16021
16086
|
* formatted source doesn't tokenize to exactly the same stream, the original is
|
|
16022
16087
|
* returned untouched. Nomen's tokenizer folds a sign into the number that
|
|
16023
16088
|
* follows it (`a -1` is not `a - 1`), so a spacing slip really can change the
|
|
16024
|
-
* meaning of a program — never let one through.
|
|
16089
|
+
* meaning of a program — never let one through. Pass `force` to apply the
|
|
16090
|
+
* layout anyway (e.g. for an explicit "force format" command).
|
|
16025
16091
|
*/
|
|
16026
|
-
function format_source(source, options) {
|
|
16092
|
+
function format_source(source, options, force = false) {
|
|
16027
16093
|
const config = {
|
|
16028
16094
|
...default_format_options,
|
|
16029
16095
|
...options
|
|
@@ -16034,7 +16100,7 @@ function format_source(source, options) {
|
|
|
16034
16100
|
strip_redundant_types: false
|
|
16035
16101
|
});
|
|
16036
16102
|
const difference = token_difference(source, layout);
|
|
16037
|
-
if (difference) return {
|
|
16103
|
+
if (difference && !force) return {
|
|
16038
16104
|
code: source,
|
|
16039
16105
|
changed: false,
|
|
16040
16106
|
unsafe: difference
|
|
@@ -16767,14 +16833,20 @@ function struct_owns_heap(s, status, visited) {
|
|
|
16767
16833
|
//#endregion
|
|
16768
16834
|
//#region ../src/check/utils/type_name.ts
|
|
16769
16835
|
function type_name(type) {
|
|
16770
|
-
|
|
16836
|
+
const prefix = type_modifier(type);
|
|
16837
|
+
if (type.tuple_types?.length && (type.name === "tuple" || type.name.startsWith("_Tuple_"))) return `${prefix}[${type.tuple_types.map((t) => type_name(t)).join(", ")}]${type.is_nullable ? "?" : ""}`;
|
|
16771
16838
|
if (type.is_array) return `Array<${type_name_without_array(type)}>${type.is_nullable ? "?" : ""}`;
|
|
16772
16839
|
const args = type.type_args?.length ? `<${type.type_args.map((t) => type_name(t)).join(", ")}>` : "";
|
|
16773
|
-
return `${
|
|
16840
|
+
return `${prefix}${type.name}${args}${type.is_nullable ? "?" : ""}`;
|
|
16841
|
+
}
|
|
16842
|
+
function type_modifier(type) {
|
|
16843
|
+
if (type.is_view) return "view ";
|
|
16844
|
+
if (type.is_ref) return "ref ";
|
|
16845
|
+
return "";
|
|
16774
16846
|
}
|
|
16775
16847
|
function type_name_without_array(type) {
|
|
16776
16848
|
const args = type.type_args?.length ? `<${type.type_args.map((t) => type_name(t)).join(", ")}>` : "";
|
|
16777
|
-
return `${type
|
|
16849
|
+
return `${type_modifier(type)}${type.name}${args}`;
|
|
16778
16850
|
}
|
|
16779
16851
|
//#endregion
|
|
16780
16852
|
//#region ../src/check/utils/check_type_and_value_match.ts
|
|
@@ -17693,6 +17765,7 @@ function evaluate_operation(op, status) {
|
|
|
17693
17765
|
if (op.op === "<" || op.op === "<=" || op.op === ">" || op.op === ">=") {
|
|
17694
17766
|
const num = evaluate_numeric_comparison(op, status);
|
|
17695
17767
|
if (num !== void 0) return num;
|
|
17768
|
+
if (op.op === "<=" && is_zero(op.left_value, status) && is_nonnegative_access(op.right_value) || op.op === ">=" && is_zero(op.right_value, status) && is_nonnegative_access(op.left_value)) return true;
|
|
17696
17769
|
let left_var;
|
|
17697
17770
|
let left_offset = 0;
|
|
17698
17771
|
if (op.left_value.node_type === "value") left_var = op.left_value.value;
|
|
@@ -17989,6 +18062,25 @@ function evaluate_numeric_or_bool(node, status) {
|
|
|
17989
18062
|
return evaluate_const_condition(op_node, status);
|
|
17990
18063
|
}
|
|
17991
18064
|
}
|
|
18065
|
+
const NON_NEGATIVE_FIELDS = /* @__PURE__ */ new Set([
|
|
18066
|
+
"length",
|
|
18067
|
+
"cap",
|
|
18068
|
+
"count",
|
|
18069
|
+
"size"
|
|
18070
|
+
]);
|
|
18071
|
+
/** Does `node` read a container's length/capacity, which is always >= 0? */
|
|
18072
|
+
function is_nonnegative_access(node) {
|
|
18073
|
+
if (node.node_type !== "access") return false;
|
|
18074
|
+
const access = node;
|
|
18075
|
+
if (access.access.node_type === "access_field") return NON_NEGATIVE_FIELDS.has(access.access.name);
|
|
18076
|
+
if (access.access.node_type === "access_func") return NON_NEGATIVE_FIELDS.has(access.access.name);
|
|
18077
|
+
return false;
|
|
18078
|
+
}
|
|
18079
|
+
/** Does `node` evaluate to the compile-time integer 0 (literal or const)? */
|
|
18080
|
+
function is_zero(node, status) {
|
|
18081
|
+
const value = evaluate_numeric_or_bool(node, status);
|
|
18082
|
+
return typeof value === "number" && value === 0;
|
|
18083
|
+
}
|
|
17992
18084
|
//#endregion
|
|
17993
18085
|
//#region ../src/check/utils/is_visible.ts
|
|
17994
18086
|
/**
|
|
@@ -18360,10 +18452,12 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
18360
18452
|
}
|
|
18361
18453
|
const satisfied = evaluate_const_condition(func_param.constraint, status);
|
|
18362
18454
|
status.values.length = saved_values_length;
|
|
18363
|
-
|
|
18455
|
+
const constraint_source = expression_to_source(func_param.constraint);
|
|
18456
|
+
const suffix = constraint_source ? `\n ${constraint_source}` : "";
|
|
18457
|
+
if (satisfied === false) add_error(status, `Parameter constraint not satisfied: ${func_param.name}${suffix}`, param.start);
|
|
18364
18458
|
else if (satisfied === void 0) {
|
|
18365
|
-
if (!is_inside_core_method(status)) add_error(status, `Parameter constraint cannot be verified: ${func_param.name}`, param.start);
|
|
18366
|
-
} else if (satisfied === "unsafe") add_error(status, `Parameter constraint not satisfied: ${func_param.name}`, param.start);
|
|
18459
|
+
if (!is_inside_core_method(status)) add_error(status, `Parameter constraint cannot be verified: ${func_param.name}${suffix}`, param.start);
|
|
18460
|
+
} else if (satisfied === "unsafe") add_error(status, `Parameter constraint not satisfied: ${func_param.name}${suffix}`, param.start);
|
|
18367
18461
|
}
|
|
18368
18462
|
if (param.node_type !== "value" && !has_ref_keyword && !node.swap_params?.has(i) && !(i === node.variadic_param_index && param.node_type === "array")) {
|
|
18369
18463
|
const declaration_name = `_param_${status.var_name_counter.value++}`;
|
|
@@ -18452,6 +18546,29 @@ function find_lhs_var_name(status) {
|
|
|
18452
18546
|
if (node.node_type === "func") return void 0;
|
|
18453
18547
|
}
|
|
18454
18548
|
}
|
|
18549
|
+
/** Render a constraint expression AST back to a source-like string. */
|
|
18550
|
+
function expression_to_source(node) {
|
|
18551
|
+
if (!node) return "";
|
|
18552
|
+
if (node.node_type === "value") return node.value;
|
|
18553
|
+
if (node.node_type === "access") {
|
|
18554
|
+
const access = node;
|
|
18555
|
+
const target = expression_to_source(access.target);
|
|
18556
|
+
if (access.access.node_type === "access_field") {
|
|
18557
|
+
const name = access.access.name;
|
|
18558
|
+
return target ? `${target}.${name}` : name;
|
|
18559
|
+
}
|
|
18560
|
+
if (access.access.node_type === "access_func") {
|
|
18561
|
+
const name = access.access.name;
|
|
18562
|
+
return target ? `${target}.${name}()` : `${name}()`;
|
|
18563
|
+
}
|
|
18564
|
+
return target;
|
|
18565
|
+
}
|
|
18566
|
+
if (node.node_type === "op") {
|
|
18567
|
+
const op = node;
|
|
18568
|
+
return `${expression_to_source(op.left_value)} ${op.op} ${expression_to_source(op.right_value)}`;
|
|
18569
|
+
}
|
|
18570
|
+
return "";
|
|
18571
|
+
}
|
|
18455
18572
|
//#endregion
|
|
18456
18573
|
//#region ../src/nodes/clone_node.ts
|
|
18457
18574
|
function clone_type$2(type) {
|
|
@@ -19032,7 +19149,12 @@ function materialize_tuple_type(type, status) {
|
|
|
19032
19149
|
//#endregion
|
|
19033
19150
|
//#region ../src/check/check_declaration_node.ts
|
|
19034
19151
|
function check_declaration_node(decl, status) {
|
|
19152
|
+
const is_view_keyword = decl.declaration === "view";
|
|
19153
|
+
const declaration = decl.declaration === "view" ? "const" : decl.declaration;
|
|
19154
|
+
decl.declaration = declaration;
|
|
19155
|
+
const declared_type_is_view = !!decl.type.is_view;
|
|
19035
19156
|
if (decl.func_params) {
|
|
19157
|
+
if (is_view_keyword) add_error(status, `'view' cannot declare a function-typed binding`, decl.start);
|
|
19036
19158
|
if (decl.func_return_type) {
|
|
19037
19159
|
check_type_exists(decl.func_return_type, status, -1);
|
|
19038
19160
|
if (decl.func_return_type.name === "tuple" && decl.func_return_type.tuple_types?.length) decl.func_return_type = materialize_tuple_type(decl.func_return_type, status);
|
|
@@ -19070,13 +19192,13 @@ function check_declaration_node(decl, status) {
|
|
|
19070
19192
|
status.stack.pop();
|
|
19071
19193
|
}
|
|
19072
19194
|
status.values.push({
|
|
19073
|
-
declaration
|
|
19195
|
+
declaration,
|
|
19074
19196
|
name: decl.name,
|
|
19075
19197
|
type: decl.func_return_type || decl.type,
|
|
19076
19198
|
is_set: !!decl.value,
|
|
19077
19199
|
start: decl.start,
|
|
19078
19200
|
is_null: decl.value?.node_type === "value" && decl.value.value === "null",
|
|
19079
|
-
const_value:
|
|
19201
|
+
const_value: declaration === "const" ? extract_const_value(decl.value) : void 0,
|
|
19080
19202
|
constraint: decl.constraint,
|
|
19081
19203
|
func_params: decl.func_params?.map((p) => ({
|
|
19082
19204
|
name: p.name,
|
|
@@ -19103,6 +19225,8 @@ function check_declaration_node(decl, status) {
|
|
|
19103
19225
|
}
|
|
19104
19226
|
status.stack.pop();
|
|
19105
19227
|
}
|
|
19228
|
+
if (is_view_keyword && decl.value && !decl.type.is_view) add_error(status, `'view' binding requires a view value (e.g. a .slice() result), got ${decl.type.name}`, decl.value.start);
|
|
19229
|
+
if (!is_view_keyword && !declared_type_is_view && decl.type.is_view && decl.value) add_error(status, `binding a view requires the 'view' keyword (e.g. 'view name = ...' or 'var view name = ...')`, decl.value.start);
|
|
19106
19230
|
if (decl.type.name === "string" && decl.value && decl.value.node_type === "value" && decl.value.value.startsWith("\"") && decl.value.value.endsWith("\"")) {
|
|
19107
19231
|
const len = decl.value.value.length - 2;
|
|
19108
19232
|
decl.type.length = new ValueNode(decl.value.start, len.toString(), new Type("int"));
|
|
@@ -19127,13 +19251,13 @@ function check_declaration_node(decl, status) {
|
|
|
19127
19251
|
check_constraint(decl, status);
|
|
19128
19252
|
const class_alias_src = decl.value?.node_type === "value" && !decl.value.is_moved && decl.value.value !== "null" && decl.type.name && is_class_type$1(decl.type.name, status) ? decl.value.value : void 0;
|
|
19129
19253
|
status.values.push({
|
|
19130
|
-
declaration
|
|
19254
|
+
declaration,
|
|
19131
19255
|
name: decl.name,
|
|
19132
19256
|
type: decl.type,
|
|
19133
19257
|
is_set: !!decl.value,
|
|
19134
19258
|
start: decl.start,
|
|
19135
19259
|
is_null: decl.value?.node_type === "value" && decl.value.value === "null",
|
|
19136
|
-
const_value:
|
|
19260
|
+
const_value: declaration === "const" ? extract_const_value(decl.value) : void 0,
|
|
19137
19261
|
constraint: decl.constraint,
|
|
19138
19262
|
decl_depth: status.scope_depth,
|
|
19139
19263
|
borrow_depth: decl.value ? borrow_depth_of(decl.value, status) : void 0,
|
|
@@ -19257,8 +19381,8 @@ function check_struct_node(struct, status) {
|
|
|
19257
19381
|
const values_length_before_fields = status.values.length;
|
|
19258
19382
|
for (let decl of struct.fields) {
|
|
19259
19383
|
decl.scope = struct;
|
|
19260
|
-
if (decl.type.is_ref || decl.type.is_view) {
|
|
19261
|
-
add_error(status, `${struct.is_class ? "class" : "struct"} fields cannot be '${decl.type.is_view ? "view" : "ref"}'`, decl.start);
|
|
19384
|
+
if (decl.type.is_ref || decl.type.is_view || decl.declaration === "view") {
|
|
19385
|
+
add_error(status, `${struct.is_class ? "class" : "struct"} fields cannot be '${decl.type.is_view || decl.declaration === "view" ? "view" : "ref"}'`, decl.start);
|
|
19262
19386
|
continue;
|
|
19263
19387
|
}
|
|
19264
19388
|
if (!struct.is_class && is_class_type$1(decl.type.name, status)) add_error(status, `struct fields cannot be class types, use a class instead`, decl.start);
|
|
@@ -21723,6 +21847,7 @@ function check_for_loop_node(for_loop, status) {
|
|
|
21723
21847
|
const has_at = list_type.name !== void 0 && !!status.structs.find((s) => s.name === "Array_" + list_type.name && s.functions.some((f) => f.name === "at"));
|
|
21724
21848
|
if (list_type.is_array && !enumerable && has_at) {
|
|
21725
21849
|
desugar_array_for_loop(for_loop, list_type);
|
|
21850
|
+
for_loop.item_is_ref = false;
|
|
21726
21851
|
check_for_loop_node(for_loop, status);
|
|
21727
21852
|
return;
|
|
21728
21853
|
}
|
|
@@ -21733,7 +21858,13 @@ function check_for_loop_node(for_loop, status) {
|
|
|
21733
21858
|
const list_type = type_from_value_node(for_loop.list, for_status);
|
|
21734
21859
|
const is_enumerable = list_type.name ? has_trait(list_type.name, "Enumerable", for_status) : false;
|
|
21735
21860
|
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);
|
|
21861
|
+
if (for_loop.item_is_ref && list_type.is_array && for_loop.list.node_type === "value") {
|
|
21862
|
+
const list_name = for_loop.list.value;
|
|
21863
|
+
const list_value = for_status.values.find((v) => v.name === list_name);
|
|
21864
|
+
if (list_value && list_value.declaration !== "var") add_error(for_status, `'ref' iteration requires a 'var' array, but '${list_name}' is const`, for_loop.list.start);
|
|
21865
|
+
}
|
|
21736
21866
|
if (for_loop.item) {
|
|
21867
|
+
if (for_loop.item_is_ref && (for_loop.list instanceof RangeNode || is_enumerable)) add_error(for_status, `'ref' is only valid for array element iteration (for ref x of arr), not ranges or Enumerable types`, for_loop.item.start);
|
|
21737
21868
|
if (is_enumerable) for_loop.item.type = new Type("int", true);
|
|
21738
21869
|
else for_loop.item.type = new Type(list_type.name);
|
|
21739
21870
|
let range_lower;
|
|
@@ -21754,7 +21885,7 @@ function check_for_loop_node(for_loop, status) {
|
|
|
21754
21885
|
if (upper_bound_expr) upper_bound_expr = upper_bound_expr + ".length";
|
|
21755
21886
|
}
|
|
21756
21887
|
for_status.values.push({
|
|
21757
|
-
declaration: "var",
|
|
21888
|
+
declaration: for_loop.item_is_ref ? "var" : "const",
|
|
21758
21889
|
name: for_loop.item.value,
|
|
21759
21890
|
type: for_loop.item.type,
|
|
21760
21891
|
is_set: true,
|
|
@@ -21769,19 +21900,56 @@ function check_for_loop_node(for_loop, status) {
|
|
|
21769
21900
|
if (for_loop.update) check_node(for_loop.update, for_status);
|
|
21770
21901
|
persist_invalidated(status, for_status);
|
|
21771
21902
|
}
|
|
21772
|
-
/**
|
|
21903
|
+
/**
|
|
21904
|
+
* Rewrite `for x of arr` in place into `for __idx of 0..arr.length { x = arr.at(__idx) }`.
|
|
21905
|
+
*
|
|
21906
|
+
* By default `x` is const (read-only). For `for ref x of arr` the desugaring is
|
|
21907
|
+
* copy-out / mutate / copy-back: `var x = arr.at(__idx)` is prepended and
|
|
21908
|
+
* `arr.set(__idx, x)` is appended, with the write-back duplicated before every
|
|
21909
|
+
* break/continue so mutations persist on all exit paths. (The write-back is
|
|
21910
|
+
* skipped on `return`, matching Rust's copy semantics.)
|
|
21911
|
+
*/
|
|
21773
21912
|
function desugar_array_for_loop(for_loop, array_type) {
|
|
21774
21913
|
const list = for_loop.list;
|
|
21775
21914
|
const start = list.start;
|
|
21776
21915
|
const original_item = for_loop.item;
|
|
21777
21916
|
const idx_name = `__for_idx_${original_item.value}`;
|
|
21917
|
+
const is_ref = !!for_loop.item_is_ref;
|
|
21778
21918
|
for_loop.item = new ValueNode(original_item.start, idx_name);
|
|
21779
21919
|
for_loop.list = new RangeNode(start, new ValueNode(start, "0"), array_type.length ? clone_node(array_type.length) : new AccessNode(start, clone_node(list), new AccessFieldNode(start, "length")));
|
|
21780
21920
|
const at_call = new AccessFunctionCallNode(start, "at", new Type(""), [new ValueNode(start, idx_name)]);
|
|
21781
21921
|
const at_access = new AccessNode(start, clone_node(list), at_call);
|
|
21782
|
-
const decl = new DeclarationNode(original_item.start, "private", "var", original_item.value, new Type(array_type.name), at_access);
|
|
21922
|
+
const decl = new DeclarationNode(original_item.start, "private", is_ref ? "var" : "const", original_item.value, new Type(array_type.name), at_access);
|
|
21783
21923
|
decl.is_loop_iterator = true;
|
|
21784
21924
|
for_loop.statements.unshift(decl);
|
|
21925
|
+
if (is_ref) {
|
|
21926
|
+
const make_writeback = () => {
|
|
21927
|
+
const set_call = new AccessFunctionCallNode(start, "set", new Type(""), [new ValueNode(start, idx_name), new ValueNode(start, original_item.value)]);
|
|
21928
|
+
return new AccessNode(start, clone_node(list), set_call);
|
|
21929
|
+
};
|
|
21930
|
+
insert_writebacks(for_loop.statements, make_writeback);
|
|
21931
|
+
for_loop.statements.push(make_writeback());
|
|
21932
|
+
}
|
|
21933
|
+
}
|
|
21934
|
+
/**
|
|
21935
|
+
* Walk a statement array and insert a write-back node before every break /
|
|
21936
|
+
* continue that belongs to the current loop. Nested for/while loops and nested
|
|
21937
|
+
* function bodies are not descended into (their exits belong to them).
|
|
21938
|
+
*/
|
|
21939
|
+
function insert_writebacks(statements, make_writeback) {
|
|
21940
|
+
for (let i = statements.length - 1; i >= 0; i--) {
|
|
21941
|
+
const stmt = statements[i];
|
|
21942
|
+
if (stmt.node_type === "break" || stmt.node_type === "continue") {
|
|
21943
|
+
statements.splice(i, 0, make_writeback());
|
|
21944
|
+
continue;
|
|
21945
|
+
}
|
|
21946
|
+
if (stmt.node_type === "for" || stmt.node_type === "while" || stmt.node_type === "func") continue;
|
|
21947
|
+
for (const key of Object.keys(stmt)) {
|
|
21948
|
+
if (key === "parent" || key === "scope") continue;
|
|
21949
|
+
const val = stmt[key];
|
|
21950
|
+
if (Array.isArray(val)) insert_writebacks(val, make_writeback);
|
|
21951
|
+
}
|
|
21952
|
+
}
|
|
21785
21953
|
}
|
|
21786
21954
|
function evaluate_range_bound_value(node, status) {
|
|
21787
21955
|
const val = evaluate_numeric_or_bool(node, status);
|
|
@@ -22696,6 +22864,10 @@ function warn_var_not_changed(decl, locals, status) {
|
|
|
22696
22864
|
if (is_discard(decl.name)) return;
|
|
22697
22865
|
if (locals.assigned.has(decl.name)) return;
|
|
22698
22866
|
if (status.mutated_local_names?.has(decl.name)) return;
|
|
22867
|
+
if (decl.type?.is_view) {
|
|
22868
|
+
add_warning(status, `View '${decl.name}' is never re-pointed, consider dropping 'var' (use 'view ${decl.name} = ...')`, decl.name_start ?? decl.start);
|
|
22869
|
+
return;
|
|
22870
|
+
}
|
|
22699
22871
|
add_warning(status, `Variable '${decl.name}' is never changed, consider using const`, decl.name_start ?? decl.start);
|
|
22700
22872
|
}
|
|
22701
22873
|
/** Names that count as a "use": call targets, method names, and value reads. */
|
|
@@ -23048,12 +23220,23 @@ function parse_declaration(visibility, declaration, status) {
|
|
|
23048
23220
|
decl.type_start = get_index(status);
|
|
23049
23221
|
decl.type = parse_type(status);
|
|
23050
23222
|
if (peek_current(status) === "=" || status.i >= status.tokens.length) {
|
|
23051
|
-
|
|
23052
|
-
|
|
23053
|
-
|
|
23054
|
-
|
|
23055
|
-
|
|
23056
|
-
|
|
23223
|
+
const modifier = status.tokens[saved_i]?.value;
|
|
23224
|
+
if ((modifier === "view" || modifier === "ref") && decl.type.name) {
|
|
23225
|
+
decl.name = decl.type.name;
|
|
23226
|
+
decl.name_start = saved_i + 1;
|
|
23227
|
+
const inferred = new Type("");
|
|
23228
|
+
if (modifier === "view") inferred.is_view = true;
|
|
23229
|
+
else inferred.is_ref = true;
|
|
23230
|
+
decl.type = inferred;
|
|
23231
|
+
decl.type_start = void 0;
|
|
23232
|
+
} else {
|
|
23233
|
+
status.i = saved_i;
|
|
23234
|
+
status.errors.length = saved_errors_length;
|
|
23235
|
+
decl.type = new Type("");
|
|
23236
|
+
decl.type_start = void 0;
|
|
23237
|
+
decl.name_start = get_index(status);
|
|
23238
|
+
decl.name = consume(status);
|
|
23239
|
+
}
|
|
23057
23240
|
} else {
|
|
23058
23241
|
decl.name_start = get_index(status);
|
|
23059
23242
|
decl.name = consume(status);
|
|
@@ -24278,13 +24461,16 @@ function parse_extend(visibility, status) {
|
|
|
24278
24461
|
function parse_for_loop(status) {
|
|
24279
24462
|
const for_start = get_index(status);
|
|
24280
24463
|
accept("for", status);
|
|
24281
|
-
const
|
|
24464
|
+
const start = get_index(status);
|
|
24465
|
+
const item_is_ref = accept("ref", status);
|
|
24466
|
+
const item = new ValueNode(start, consume(status));
|
|
24282
24467
|
if (expect("of", status)) {
|
|
24283
24468
|
const list = parse_expression(status);
|
|
24284
24469
|
let update;
|
|
24285
24470
|
if (accept(";", status)) update = parse_expression(status);
|
|
24286
24471
|
if (expect("{", status)) {
|
|
24287
24472
|
const for_loop = new ForLoopNode(for_start, item, list, void 0, update);
|
|
24473
|
+
for_loop.item_is_ref = item_is_ref;
|
|
24288
24474
|
status.stack.push(for_loop);
|
|
24289
24475
|
parse_statement(status);
|
|
24290
24476
|
expect("}", status);
|
|
@@ -24382,6 +24568,7 @@ function parse_visibility(visibility, status) {
|
|
|
24382
24568
|
case "const":
|
|
24383
24569
|
case "var":
|
|
24384
24570
|
case "mov":
|
|
24571
|
+
case "view":
|
|
24385
24572
|
if (visibility === "private" && status.stack.at(-1)?.node_type === "trait") {
|
|
24386
24573
|
add_error(status, `Trait fields cannot be private`, get_index(status));
|
|
24387
24574
|
consume(status);
|
|
@@ -24473,6 +24660,7 @@ function parse_statement(status) {
|
|
|
24473
24660
|
case "const":
|
|
24474
24661
|
case "var":
|
|
24475
24662
|
case "mov":
|
|
24663
|
+
case "view":
|
|
24476
24664
|
parse_declaration(default_visibility(status), value, status);
|
|
24477
24665
|
break;
|
|
24478
24666
|
case "struct":
|