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.
Files changed (2) hide show
  1. package/dist/index.mjs +215 -27
  2. 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
- if (type.tuple_types?.length && (type.name === "tuple" || type.name.startsWith("_Tuple_"))) return `[${type.tuple_types.map((t) => type_name(t)).join(", ")}]${type.is_nullable ? "?" : ""}`;
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 `${type.is_ref ? "ref " : ""}${type.name}${args}${type.is_nullable ? "?" : ""}`;
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.is_ref ? "ref " : ""}${type.name}${args}`;
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
- if (satisfied === false) add_error(status, `Parameter constraint not satisfied: ${func_param.name}`, param.start);
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: decl.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: decl.declaration === "const" ? extract_const_value(decl.value) : void 0,
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: decl.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: decl.declaration === "const" ? extract_const_value(decl.value) : void 0,
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
- /** Rewrite `for x of arr` in place into `for __idx of 0..arr.length { var x = arr.at(__idx) }`. */
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
- status.i = saved_i;
23052
- status.errors.length = saved_errors_length;
23053
- decl.type = new Type("");
23054
- decl.type_start = void 0;
23055
- decl.name_start = get_index(status);
23056
- decl.name = consume(status);
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 item = new ValueNode(get_index(status), consume(status));
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":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nomen-lang",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "The CLI for the Nomen programming language.",
5
5
  "keywords": [],
6
6
  "license": "ISC",