nomen-lang 0.6.0 → 0.6.1

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.
@@ -120,6 +120,121 @@ pub struct StringBuilder {
120
120
  }
121
121
  }
122
122
 
123
+ // Read-back: how many bytes the buffer currently holds. (The `len` field
124
+ // is private; this is the O(1) peek the incremental-builder flow needs —
125
+ // e.g. recording a mark before appending a section.)
126
+ pub func length = (self, out int) {
127
+ return self.len
128
+ }
129
+
130
+ // Whether the buffer's current bytes end with `suffix` (an empty suffix
131
+ // is always true). Reads the buffer tail in place — no copy of either
132
+ // side (`view string` avoids materializing the checked suffix).
133
+ pub func ends_with = (ref self, view string suffix, out bool) {
134
+ unsafe {
135
+ var int sl = suffix.length
136
+ if sl > self.len {
137
+ return false
138
+ }
139
+ var int start = self.len - sl
140
+ var int i = 0
141
+ while i < sl; i += 1 {
142
+ if (self.data as ptr char)[start + i] != (suffix as ptr char)[i] {
143
+ return false
144
+ }
145
+ }
146
+ }
147
+ return true
148
+ }
149
+
150
+ // Truncate the buffer to `new_len` bytes (clamped to [0, len]). Capacity
151
+ // is kept, so a subsequent append reuses the storage without a realloc.
152
+ pub func truncate = (ref self, int new_len) {
153
+ var int n = new_len
154
+ if n < 0 {
155
+ n = 0
156
+ }
157
+ if n > self.len {
158
+ n = self.len
159
+ }
160
+ self.len = n
161
+ }
162
+
163
+ // Drop the last byte (a no-op when the buffer is empty). The common
164
+ // chomp-the-trailing-separator shape.
165
+ pub func chomp = (ref self) {
166
+ if self.len > 0 {
167
+ self.len = self.len - 1
168
+ }
169
+ }
170
+
171
+ // Extract the bytes from `mark` to the end as a fresh owned string and
172
+ // rewind the buffer to `mark` bytes (capacity kept). The mid-render
173
+ // measure-and-restart shape: record `length()`, append a section, then
174
+ // `take_since(mark)` to measure it and re-emit decorated. `mark` clamps
175
+ // to [0, len]; the returned string is always an independent allocation.
176
+ // (`move out string` — the raw body mallocs the result, so the caller
177
+ // owns it by signature, like `Console.read_line`.)
178
+ func take_since = (ref self, int mark, move out string) {
179
+ ```
180
+ #arch: c
181
+ int start = mark;
182
+ if (start < 0) start = 0;
183
+ if (start > self->len) start = self->len;
184
+ int n = self->len - start;
185
+ char* buf = (char*)malloc((size_t)n + 1);
186
+ if (n > 0) memcpy(buf, (void*)(unsigned long long)(self->data + start), n);
187
+ buf[n] = 0;
188
+ self->len = start;
189
+ // Fat-string ABI: return the (ptr, len) pair — no strlen.
190
+ return (nomen_string){ buf, n };
191
+ ```
192
+ ```
193
+ #arch: aarch64
194
+ // x19 = self, x1 = mark. Clamp start to [0, len]; malloc(n+1),
195
+ // copy the tail, null-terminate, rewind len. Live values live in a
196
+ // 32-byte (16-aligned) frame across the calls (x0-x18 are
197
+ // caller-saved): [sp]=start, [sp+8]=n, [sp+16]=buf.
198
+ ldr x2, [x19, #16] // len
199
+ mov x3, x1 // start = mark
200
+ cmp x3, #0
201
+ b.ge .Lsb_tk_hi
202
+ mov x3, #0
203
+ .Lsb_tk_hi:
204
+ cmp x3, x2
205
+ b.le .Lsb_tk_ok
206
+ mov x3, x2 // start = min(start, len)
207
+ .Lsb_tk_ok:
208
+ sub x4, x2, x3 // n = len - start
209
+ sub sp, sp, #32
210
+ str x3, [sp, #0] // start
211
+ str x4, [sp, #8] // n
212
+ add x0, x4, #1
213
+ // buf = malloc(n + 1) — bare call line: the audit pass rewrites
214
+ // `bl _malloc` (line-exact) to the counter-wrapped allocator.
215
+ bl _malloc
216
+ str x0, [sp, #16]
217
+ ldr x1, [sp, #8] // n
218
+ cbz x1, .Lsb_tk_copied
219
+ ldr x0, [sp, #16] // dst = buf
220
+ ldr x3, [x19, #8] // data
221
+ ldr x4, [sp, #0] // start
222
+ add x1, x3, x4 // src = data + start
223
+ ldr x2, [sp, #8] // n
224
+ bl _memcpy
225
+ .Lsb_tk_copied:
226
+ ldr x0, [sp, #16] // buf
227
+ ldr x1, [sp, #8] // n
228
+ add x2, x0, x1
229
+ strb wzr, [x2] // buf[n] = 0
230
+ ldr x3, [sp, #0] // start
231
+ str x3, [x19, #16] // len = start
232
+ ldr x0, [sp, #16] // return pair: (buf, n)
233
+ ldr x1, [sp, #8]
234
+ add sp, sp, #32
235
+ ```
236
+ }
237
+
123
238
  // Finalize: null-terminate, return the byte buffer as a string, and
124
239
  // release ownership (sets `data` to 0 so the destructor won't free it).
125
240
  func to_string = (ref self, out string) {
@@ -469,10 +469,10 @@ func collect_first_bytes = (
469
469
  //
470
470
  // Handles the shapes the engine's matcher can produce: a leading anchor is
471
471
  // zero-width (skipped), a leading group contributes its CONTENT's first bytes
472
- // (recursively over the group's own top-level alternatives), and a group
473
- // quantified with '?'/'*' is nullable — what FOLLOWS it can also be first, so
474
- // the scan continues past it. A wildcard '.' or an empty alternative makes the
475
- // set indeterminable.
472
+ // (recursively over the group's own top-level alternatives), and ANY leading
473
+ // atom — literal, escaped, class, or group — quantified with '?'/'*' is
474
+ // nullable: what FOLLOWS it can also be first, so the scan continues past it.
475
+ // A wildcard '.' or an empty alternative makes the set indeterminable.
476
476
  func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb, out int) {
477
477
  var int i = start
478
478
  while i < end {
@@ -490,9 +490,17 @@ func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb
490
490
  if shorthand_members(e, ref sb) < 0 {
491
491
  return -1
492
492
  }
493
+ } else {
494
+ sb.append_char(e)
495
+ }
496
+ // A '?'/'*' quantifier makes the atom nullable — the first
497
+ // byte can also come from whatever follows it.
498
+ var int cont = nullable_quant_after(pattern, i + 2, end)
499
+ if cont < 0 {
493
500
  return 0
494
501
  }
495
- sb.append_char(e)
502
+ i = cont
503
+ continue
496
504
  }
497
505
  return 0
498
506
  }
@@ -509,7 +517,12 @@ func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb
509
517
  }
510
518
  }
511
519
  add_class_chars(pattern, i + 1, a_end - 1, ref sb)
512
- return 0
520
+ var int cont = nullable_quant_after(pattern, a_end, end)
521
+ if cont < 0 {
522
+ return 0
523
+ }
524
+ i = cont
525
+ continue
513
526
  }
514
527
  if c == '(' {
515
528
  var int a_end = atom_end_pos(pattern, i, end)
@@ -518,27 +531,11 @@ func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb
518
531
  if collect_first_bytes(pattern, i + 1, a_end - 1, ref sb) < 0 {
519
532
  return -1
520
533
  }
521
- // A '?'/'*' quantifier makes the group nullable — the first byte
522
- // can also come from whatever follows it. A second '?' marks the
523
- // quantifier lazy and is skipped either way.
524
- var int after = a_end
525
- var nullable = 0
526
- if after < end {
527
- var char q = pattern.at(after)
528
- if q == '?' || q == '*' {
529
- nullable = 1
530
- after += 1
531
- if after < end {
532
- if pattern.at(after) == '?' {
533
- after += 1
534
- }
535
- }
536
- }
537
- }
538
- if nullable == 0 {
534
+ var int cont = nullable_quant_after(pattern, a_end, end)
535
+ if cont < 0 {
539
536
  return 0
540
537
  }
541
- i = after
538
+ i = cont
542
539
  continue
543
540
  }
544
541
  if c == '^' || c == '$' {
@@ -552,9 +549,40 @@ func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb
552
549
  return -1
553
550
  }
554
551
  sb.append_char(c)
555
- return 0
552
+ var int cont = nullable_quant_after(pattern, i + 1, end)
553
+ if cont < 0 {
554
+ return 0
555
+ }
556
+ i = cont
557
+ continue
558
+ }
559
+ // An empty alternative (nothing left after nullable atoms — or nothing at
560
+ // all) can match at any position — indeterminable.
561
+ return -1
562
+ }
563
+
564
+ // If the atom ending at `after` carries a nullable quantifier ('?' or '*',
565
+ // optionally marked lazy by a second '?'), return the position just past the
566
+ // quantifier; otherwise return -1. Callers keep scanning from the returned
567
+ // position: a nullable leading atom means the alternative's match can also
568
+ // START with whatever follows, so those bytes belong in the first-byte set
569
+ // too. ('+' is NOT nullable — the atom must match once — so it leaves the
570
+ // atom's own bytes as the whole first set.)
571
+ func nullable_quant_after = (string pattern, int after, int end, out int) {
572
+ var int j = after
573
+ if j >= end {
574
+ return -1
575
+ }
576
+ var char q = pattern.at(j)
577
+ if q == '?' || q == '*' {
578
+ j += 1
579
+ if j < end {
580
+ if pattern.at(j) == '?' {
581
+ j += 1
582
+ }
583
+ }
584
+ return j
556
585
  }
557
- // An empty alternative can match at any position — indeterminable.
558
586
  return -1
559
587
  }
560
588
 
package/dist/index.mjs CHANGED
@@ -19242,6 +19242,7 @@ function build_function_node$1(node, status) {
19242
19242
  const old_struct_param_slots = status.function_struct_param_slots;
19243
19243
  const old_function_param_types = status.function_param_types;
19244
19244
  const old_function_local_types = status.function_local_types;
19245
+ const old_variable_types = status.variable_types;
19245
19246
  status.function_param_regs = /* @__PURE__ */ new Map();
19246
19247
  status.function_param_vars = /* @__PURE__ */ new Set();
19247
19248
  status.function_array_params = /* @__PURE__ */ new Set();
@@ -19250,6 +19251,7 @@ function build_function_node$1(node, status) {
19250
19251
  status.function_struct_param_slots = /* @__PURE__ */ new Set();
19251
19252
  status.function_param_types = /* @__PURE__ */ new Map();
19252
19253
  status.function_local_types = /* @__PURE__ */ new Map();
19254
+ status.variable_types = /* @__PURE__ */ new Map();
19253
19255
  const old_variadic_params_aarch64 = status.function_variadic_params;
19254
19256
  const old_view_params = status.function_view_params;
19255
19257
  status.function_variadic_params = /* @__PURE__ */ new Set();
@@ -19658,6 +19660,7 @@ function build_function_node$1(node, status) {
19658
19660
  status.function_struct_param_slots = old_struct_param_slots;
19659
19661
  status.function_param_types = old_function_param_types;
19660
19662
  status.function_local_types = old_function_local_types;
19663
+ status.variable_types = old_variable_types;
19661
19664
  status.function_variadic_params = old_variadic_params_aarch64;
19662
19665
  status.function_view_params = old_view_params;
19663
19666
  status.function_return_label = old_return_label;
@@ -35126,6 +35129,8 @@ function build_function_node(node, status) {
35126
35129
  status.function_view_params = /* @__PURE__ */ new Set();
35127
35130
  const old_return_type = status.function_return_type;
35128
35131
  status.function_return_type = node.return_type;
35132
+ const old_variable_types = status.variable_types;
35133
+ status.variable_types = /* @__PURE__ */ new Map();
35129
35134
  const old_nullable_ret_has = status.nullable_ret_has_param;
35130
35135
  if (is_nullable_struct_type(node.return_type, status)) status.nullable_ret_has_param = "_ret_has";
35131
35136
  else status.nullable_ret_has_param = void 0;
@@ -35181,6 +35186,7 @@ function build_function_node(node, status) {
35181
35186
  status.function_variadic_params = old_variadic_params;
35182
35187
  status.function_view_params = old_view_params;
35183
35188
  status.function_return_type = old_return_type;
35189
+ status.variable_types = old_variable_types;
35184
35190
  status.nullable_ret_has_param = old_nullable_ret_has;
35185
35191
  status.current_function_name = old_function_name;
35186
35192
  status.current_function = old_current_function;
@@ -43171,7 +43177,7 @@ function build_access_method(node, access_func, status) {
43171
43177
  emit_asm(status, `str x0, [sp, #-16]!\n`);
43172
43178
  emit_asm(status, `str x1, [sp, #-16]!\n`);
43173
43179
  emit_asm(status, `mov x0, x9\n`);
43174
- emit_asm(status, `bl _nomen_free_wrap\n`);
43180
+ emit_free(status);
43175
43181
  emit_asm(status, `ldr x1, [sp], #16\n`);
43176
43182
  emit_asm(status, `ldr x0, [sp], #16\n`);
43177
43183
  emit_asm(status, `add sp, sp, #16\n`);
@@ -55547,7 +55553,13 @@ function check_return_node(ret, status) {
55547
55553
  } else if (!func.is_arrow_body) {
55548
55554
  add_error(status, `Function returns a value but has no 'out' return type`, ret.start);
55549
55555
  func.return_type = ret.type;
55550
- } else func.return_type = ret.type;
55556
+ } else {
55557
+ func.return_type = ret.type;
55558
+ if (!ret.type?.name) {
55559
+ func.return_type = new Type("void");
55560
+ ret.type = new Type("void");
55561
+ }
55562
+ }
55551
55563
  }
55552
55564
  }
55553
55565
  //#endregion
@@ -58515,7 +58527,24 @@ function resolve_linked_types(source, library, file_path) {
58515
58527
  }
58516
58528
  const user_declared_funcs = /* @__PURE__ */ new Set();
58517
58529
  for (let i = 0; i < tokens.length - 1; i++) if (tokens[i].value === "func" && /^\w+$/.test(tokens[i + 1].value)) user_declared_funcs.add(tokens[i + 1].value);
58518
- for (const token of tokens) {
58530
+ const import_tokens = /* @__PURE__ */ new Set();
58531
+ for (let i = 0; i < tokens.length; i++) {
58532
+ if (tokens[i].value !== "import") continue;
58533
+ import_tokens.add(i);
58534
+ let j = i + 1;
58535
+ if (j < tokens.length && /^\w+$/.test(tokens[j].value)) {
58536
+ import_tokens.add(j);
58537
+ j += 1;
58538
+ while (j + 1 < tokens.length && tokens[j].value === "::" && /^\w+$/.test(tokens[j + 1].value)) {
58539
+ import_tokens.add(j);
58540
+ import_tokens.add(j + 1);
58541
+ j += 2;
58542
+ }
58543
+ }
58544
+ }
58545
+ for (let i = 0; i < tokens.length; i++) {
58546
+ if (import_tokens.has(i)) continue;
58547
+ const token = tokens[i];
58519
58548
  if (user_defined.has(token.value)) continue;
58520
58549
  if (library.types.has(token.value) && !BASE_TYPES.includes(token.value)) needed.add(token.value);
58521
58550
  else if (library.functions.has(token.value) && !user_declared_funcs.has(token.value)) needed.add(token.value);
@@ -59356,7 +59385,8 @@ function run_test_file(entry_path, lib_path, arch, audit = false, audit_runtime)
59356
59385
  audit_obj
59357
59386
  ]);
59358
59387
  }
59359
- const ext = arch === "aarch64" ? ".s" : ".c";
59388
+ const platform = default_platform();
59389
+ const ext = arch === "aarch64" ? ".s" : platform === "macos" || platform === "ios" ? ".m" : ".c";
59360
59390
  const codefile = path.join(buildDir, path.basename(entry_path, ".nm") + ext);
59361
59391
  const outfile = path.join(buildDir, path.basename(entry_path, ".nm"));
59362
59392
  fs.writeFileSync(path.join(buildDir, "main.h"), buildResult.headers ?? "");
@@ -59367,6 +59397,7 @@ function run_test_file(entry_path, lib_path, arch, audit = false, audit_runtime)
59367
59397
  codefile
59368
59398
  ];
59369
59399
  if (audit_obj) link_args.push(audit_obj);
59400
+ if (platform === "macos" || platform === "ios") link_args.push("-framework", "CoreGraphics", "-framework", "Foundation", "-framework", "AppKit", "-lobjc");
59370
59401
  try {
59371
59402
  execFileSync("clang", link_args, {
59372
59403
  encoding: "utf8",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nomen-lang",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "The CLI for the Nomen programming language.",
5
5
  "keywords": [],
6
6
  "license": "ISC",
package/src/test.ts CHANGED
@@ -3,7 +3,7 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
 
6
- import build from "../../src/build.ts";
6
+ import build, { default_platform } from "../../src/build.ts";
7
7
  import join from "../../src/join.ts";
8
8
  import { get_library } from "../../src/lib.ts";
9
9
  import parse from "../../src/parse.ts";
@@ -329,7 +329,12 @@ export function run_test_file(
329
329
  audit_obj = path.join(buildDir, "audit_runtime.o");
330
330
  execFileSync("clang", ["-c", runtime_src, "-o", audit_obj]);
331
331
  }
332
- const ext = arch === "aarch64" ? ".s" : ".c";
332
+ const platform = default_platform();
333
+ // Match the run/build command's emission: ObjC-bearing sources (GUI code
334
+ // pulled in through the library graph) must land in a `.m` TU on Apple
335
+ // platforms — the ObjC framework headers are Objective-C, and including
336
+ // them from a plain `.c` TU fails with `@class NSString` syntax errors.
337
+ const ext = arch === "aarch64" ? ".s" : platform === "macos" || platform === "ios" ? ".m" : ".c";
333
338
  const codefile = path.join(buildDir, path.basename(entry_path, ".nm") + ext);
334
339
  const outfile = path.join(buildDir, path.basename(entry_path, ".nm"));
335
340
  // The C backend's generated source does `#include "main.h"`, so write the
@@ -337,10 +342,23 @@ export function run_test_file(
337
342
  fs.writeFileSync(path.join(buildDir, "main.h"), buildResult.headers ?? "");
338
343
  fs.writeFileSync(codefile, buildResult.code);
339
344
 
340
- // Link the harness binary. The harness uses only Console/Time (printf,
341
- // clock_gettime), so no platform frameworks are required.
345
+ // Link the harness binary. Frameworks are passed on Apple platforms for
346
+ // the same reason as the run/build command: the joined source may reference
347
+ // ObjC runtime symbols even when the test body doesn't, and unused
348
+ // frameworks cost nothing at link time.
342
349
  const link_args = ["-o", outfile, codefile];
343
350
  if (audit_obj) link_args.push(audit_obj);
351
+ if (platform === "macos" || platform === "ios") {
352
+ link_args.push(
353
+ "-framework",
354
+ "CoreGraphics",
355
+ "-framework",
356
+ "Foundation",
357
+ "-framework",
358
+ "AppKit",
359
+ "-lobjc",
360
+ );
361
+ }
344
362
  try {
345
363
  execFileSync("clang", link_args, {
346
364
  encoding: "utf8",