nomen-lang 0.0.8 → 0.0.9

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/NOMEN_AGENTS.md CHANGED
@@ -68,11 +68,11 @@ func safe = (string[] s, int i: i >= 0 && i < s.length, out string) {
68
68
 
69
69
  ### Memory modifiers
70
70
 
71
- | Modifier | Meaning |
72
- |----------|---------|
73
- | `ref T` | mutable borrow; caller must also write `ref` |
74
- | `view T` | read-only borrow |
75
- | `mov T` | transfer ownership; caller writes `mov` |
71
+ | Modifier | Meaning |
72
+ | -------- | ---------------------------------------------- |
73
+ | `ref T` | mutable borrow; caller must also write `ref` |
74
+ | `view T` | read-only borrow |
75
+ | `mov T` | transfer ownership; caller writes `mov` |
76
76
  | `out T` | output parameter, assigned inside the function |
77
77
 
78
78
  `const` values cannot be passed to `ref` — rebind the caller as `var` first.
package/dist/index.mjs CHANGED
@@ -16310,14 +16310,23 @@ function describe(token) {
16310
16310
  //#region ../src/join.ts
16311
16311
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
16312
16312
  const DEFAULT_LIB_PATH = path.resolve(__dirname, "../../core/src");
16313
- function join$1(entry_file_path, lib_path) {
16313
+ let test_src_dir;
16314
+ function join$1(entry_file_path, lib_path, options) {
16314
16315
  const folder_path = path.dirname(entry_file_path);
16315
16316
  const file_path = path.basename(entry_file_path);
16316
16317
  const inputs = /* @__PURE__ */ new Map();
16317
16318
  const resolved_lib_path = lib_path ? path.resolve(lib_path, "src") : DEFAULT_LIB_PATH;
16318
- add_source(folder_path, `./${file_path}`, inputs, resolved_lib_path);
16319
- gather_module_siblings(folder_path, file_path, inputs, resolved_lib_path);
16320
- gather_module_parent(folder_path, file_path, inputs, resolved_lib_path);
16319
+ const for_test = options?.for_test ?? file_path.endsWith(".test.nm");
16320
+ const prev_test_src_dir = test_src_dir;
16321
+ test_src_dir = for_test ? resolve_src_module(folder_path) : void 0;
16322
+ try {
16323
+ add_source(folder_path, `./${file_path}`, inputs, resolved_lib_path);
16324
+ gather_module_siblings(folder_path, file_path, inputs, resolved_lib_path);
16325
+ gather_module_parent(folder_path, file_path, inputs, resolved_lib_path);
16326
+ if (test_src_dir) gather_src_module(test_src_dir, inputs, resolved_lib_path);
16327
+ } finally {
16328
+ test_src_dir = prev_test_src_dir;
16329
+ }
16321
16330
  return Array.from(inputs.values()).join("\n\n") + "\n";
16322
16331
  }
16323
16332
  function gather_module_siblings(folder_path, entry_file, inputs, lib_path) {
@@ -16361,6 +16370,26 @@ function gather_module_parent(folder_path, entry_file, inputs, lib_path) {
16361
16370
  add_source(parent_path, `./${name}`, inputs, lib_path);
16362
16371
  }
16363
16372
  }
16373
+ /** The program's `src/` module dir for a file in `entry_dir`, if one exists. */
16374
+ function resolve_src_module(entry_dir) {
16375
+ for (const dir of [path.join(entry_dir, "src"), path.join(path.dirname(entry_dir), "src")]) try {
16376
+ if (fs.statSync(dir).isDirectory()) return dir;
16377
+ } catch {}
16378
+ }
16379
+ function gather_src_module(src_dir, inputs, lib_path) {
16380
+ let names;
16381
+ try {
16382
+ names = fs.readdirSync(src_dir);
16383
+ } catch {
16384
+ return;
16385
+ }
16386
+ for (const name of names.sort()) {
16387
+ if (!name.endsWith(".nm")) continue;
16388
+ const import_file_path = `./${name}`;
16389
+ if (inputs.has(import_file_path)) continue;
16390
+ add_source(src_dir, import_file_path, inputs, lib_path);
16391
+ }
16392
+ }
16364
16393
  function has_main(source) {
16365
16394
  return /\bfunc\s+main\b/.test(source);
16366
16395
  }
@@ -16370,8 +16399,9 @@ function add_source(folder_path, file_path, inputs, lib_path) {
16370
16399
  const lib_source_path = path.resolve(lib_path, file_path);
16371
16400
  if (fs.existsSync(lib_source_path)) source_path = lib_source_path;
16372
16401
  }
16373
- let source = `// file://${source_path}\n`;
16374
- source += fs.readFileSync(source_path, "utf8");
16402
+ let source = fs.readFileSync(source_path, "utf8");
16403
+ if (test_src_dir && is_within$1(source_path, test_src_dir)) source = strip_main_functions(source);
16404
+ source = `// file://${source_path}\n` + source;
16375
16405
  source = source.replaceAll(/^import(.*)$/gm, (match, name) => {
16376
16406
  const trimmed = name.trim();
16377
16407
  if (trimmed === "System" || trimmed.startsWith("System/")) return match;
@@ -16381,6 +16411,105 @@ function add_source(folder_path, file_path, inputs, lib_path) {
16381
16411
  });
16382
16412
  inputs.set(file_path, source);
16383
16413
  }
16414
+ function is_within$1(child, parent) {
16415
+ const rel = path.relative(parent, child);
16416
+ return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
16417
+ }
16418
+ /**
16419
+ * Remove every top-level `func main` (optionally `pub`) declaration, body and
16420
+ * all. Used when compiling a test: the generated harness provides `main`, so
16421
+ * the program's own `main` would collide as a duplicate declaration.
16422
+ */
16423
+ function strip_main_functions(source) {
16424
+ let result = "";
16425
+ let i = 0;
16426
+ const n = source.length;
16427
+ let depth = 0;
16428
+ while (i < n) {
16429
+ if (depth === 0 && is_main_declaration(source, i)) {
16430
+ i = skip_main_function(source, i);
16431
+ continue;
16432
+ }
16433
+ const ch = source[i];
16434
+ result += ch;
16435
+ if (ch === "\"" || ch === "'") {
16436
+ i++;
16437
+ let escaped = false;
16438
+ while (i < n) {
16439
+ const c = source[i];
16440
+ result += c;
16441
+ i++;
16442
+ if (escaped) escaped = false;
16443
+ else if (c === "\\") escaped = true;
16444
+ else if (c === ch) break;
16445
+ }
16446
+ continue;
16447
+ }
16448
+ if (ch === "{") depth++;
16449
+ if (ch === "}") depth--;
16450
+ i++;
16451
+ }
16452
+ return result;
16453
+ }
16454
+ function is_main_declaration(source, i) {
16455
+ if (i > 0 && source[i - 1] !== "\n") return false;
16456
+ const line_end = source.indexOf("\n", i);
16457
+ const line = line_end === -1 ? source.slice(i) : source.slice(i, line_end);
16458
+ return /^\s*(pub\s+)?func\s+main\b/.test(line);
16459
+ }
16460
+ function skip_main_function(source, i) {
16461
+ const n = source.length;
16462
+ let line_end = i;
16463
+ while (line_end < n && source[line_end] !== "\n") line_end++;
16464
+ const decl_line = source.slice(i, line_end);
16465
+ const brace = decl_line.indexOf("{");
16466
+ if (brace === -1) {
16467
+ if (decl_line.includes("=>")) return line_end < n ? line_end + 1 : n;
16468
+ let j = line_end;
16469
+ while (j < n && source[j] !== "{") {
16470
+ if (source[j] === "\"" || source[j] === "'") {
16471
+ const quote = source[j];
16472
+ j++;
16473
+ while (j < n && source[j] !== quote) j++;
16474
+ }
16475
+ j++;
16476
+ }
16477
+ if (j >= n) return n;
16478
+ return skip_balanced_block(source, j);
16479
+ }
16480
+ return skip_balanced_block(source, i + brace);
16481
+ }
16482
+ function skip_balanced_block(source, brace) {
16483
+ const n = source.length;
16484
+ let depth = 0;
16485
+ let j = brace;
16486
+ while (j < n) {
16487
+ const ch = source[j];
16488
+ if (ch === "\"" || ch === "'") {
16489
+ const quote = ch;
16490
+ j++;
16491
+ let escaped = false;
16492
+ while (j < n) {
16493
+ const c = source[j++];
16494
+ if (escaped) escaped = false;
16495
+ else if (c === "\\") escaped = true;
16496
+ else if (c === quote) break;
16497
+ }
16498
+ continue;
16499
+ }
16500
+ if (ch === "{") depth++;
16501
+ if (ch === "}") {
16502
+ depth--;
16503
+ if (depth === 0) {
16504
+ j++;
16505
+ while (j < n && (source[j] === "\r" || source[j] === "\n")) j++;
16506
+ return j;
16507
+ }
16508
+ }
16509
+ j++;
16510
+ }
16511
+ return n;
16512
+ }
16384
16513
  //#endregion
16385
16514
  //#region ../src/lib.ts
16386
16515
  function read_library_config(lib_dir) {
@@ -22513,6 +22642,7 @@ function dedupe_warnings(warnings) {
22513
22642
  function warn_unused_function(func, referenced, status, struct) {
22514
22643
  if (func.is_library || struct && struct.is_library) return;
22515
22644
  if (func.name === "main" || is_discard(func.name) || func.name.startsWith("#")) return;
22645
+ if (func.visibility === "pub") return;
22516
22646
  if (func.is_generic) return;
22517
22647
  if (struct && struct.traits.length > 0) return;
22518
22648
  if (referenced.has(func.name)) return;
@@ -25084,22 +25214,22 @@ function package_jsonc(name) {
25084
25214
  "entry": "src/main.nm"
25085
25215
  // The System library is resolved automatically from your nomen-lang
25086
25216
  // install. To pin a local checkout instead, uncomment:
25087
- // "imports": { "System": "../core" }
25217
+ // "imports": { "System": "~/Source/nomen/core" }
25088
25218
  }
25089
25219
  `;
25090
25220
  }
25091
25221
  const MAIN_NM = `import System
25092
25222
 
25223
+ pub func add = (int a, int b) => a + b
25224
+
25093
25225
  pub func main = (Init init) {
25094
- Console.write("Hello world!\\n")
25226
+ Console.write("Hello world, 2 + 2 is \\{add(a + b)}!\\n")
25095
25227
  }
25096
25228
  `;
25097
25229
  const TEST_NM = `import System
25098
25230
  import System/Test
25099
25231
 
25100
- func add = (int a, int b, out int) => a + b
25101
-
25102
- pub func test_smoke = (ref Tester t) {
25232
+ pub func test_add = (ref Tester t) {
25103
25233
  t.expect(add(2, 2) == 4, "2 + 2 should equal 4")
25104
25234
  }
25105
25235
  `;
@@ -25164,14 +25294,14 @@ function run_init(name) {
25164
25294
  fs.writeFileSync(path.join(target, ".gitignore"), GITIGNORE);
25165
25295
  fs.writeFileSync(path.join(target, "package.jsonc"), package_jsonc(name));
25166
25296
  fs.writeFileSync(path.join(src, "main.nm"), MAIN_NM);
25167
- fs.writeFileSync(path.join(test, "smoke.test.nm"), TEST_NM);
25297
+ fs.writeFileSync(path.join(test, "main.test.nm"), TEST_NM);
25168
25298
  fs.writeFileSync(path.join(target, "README.md"), readme(name));
25169
25299
  fs.writeFileSync(path.join(target, "AGENTS.md"), fs.readFileSync(agents_template));
25170
25300
  console.log(`Created ${name}/`);
25171
25301
  console.log(` ${name}/.gitignore`);
25172
25302
  console.log(` ${name}/package.jsonc`);
25173
25303
  console.log(` ${name}/src/main.nm`);
25174
- console.log(` ${name}/test/smoke.test.nm`);
25304
+ console.log(` ${name}/test/main.test.nm`);
25175
25305
  console.log(` ${name}/README.md`);
25176
25306
  console.log(` ${name}/AGENTS.md`);
25177
25307
  console.log(`\nNext: cd ${name} && nomen run`);
@@ -25455,7 +25585,7 @@ function runTests(root, options = {}) {
25455
25585
  const arch = options.arch ?? "aarch64";
25456
25586
  const lib = resolve_lib_for(root);
25457
25587
  const files = collect_test_files(root).filter((f) => !options.filter || options.filter.test(f));
25458
- console.log(`\n~ NOMEN TEST ~ ${files.length} file(s)\n`);
25588
+ console.log(`Found ${files.length} test file(s)\n`);
25459
25589
  const startTime = performance.now();
25460
25590
  const results = [];
25461
25591
  for (const file of files) {
@@ -25465,14 +25595,16 @@ function runTests(root, options = {}) {
25465
25595
  }
25466
25596
  const elapsed = performance.now() - startTime;
25467
25597
  const totalFiles = results.length;
25598
+ const totalFilesFailed = results.reduce((a, r) => a + (r.fails ? 1 : 0), 0);
25599
+ const totalFilesPassed = totalFiles - totalFilesFailed;
25468
25600
  const totalTests = results.reduce((a, r) => a + r.tests.length, 0);
25469
25601
  const totalFailed = results.reduce((a, r) => a + r.tests.reduce((x, t) => x + t.failed, 0), 0);
25470
25602
  const totalPassed = totalTests - totalFailed;
25471
25603
  const anyFailed = results.some((r) => !r.ok);
25472
25604
  console.log("");
25473
- console.log(` ${C.bold("Files ")} ${totalFiles} ${anyFailed ? C.red("failed") : C.green("passed")} (${totalFiles})`);
25474
- console.log(` ${C.bold("Tests ")} ${totalPassed} ${C.green("passed")}` + (totalFailed ? ` | ${C.red(`${totalFailed} failed`)}` : "") + ` (${totalTests})`);
25475
- console.log(` ${C.bold("Time ")} ${format_duration(elapsed)}`);
25605
+ console.log(` ${C.dim("Files ")} ${C.green(`${totalFilesPassed} passed`)}` + (totalFilesFailed ? ` ${C.dim("|")} ${C.red(`${totalFilesFailed} failed`)}` : "") + C.dim(` (${totalFiles})`));
25606
+ console.log(` ${C.dim("Tests ")} ${C.green(`${totalPassed} passed`)}` + (totalFailed ? ` ${C.dim("|")} ${C.red(`${totalFailed} failed`)}` : "") + C.dim(` (${totalTests})`));
25607
+ console.log(` ${C.dim(" Time ")} ${format_duration(elapsed)}`);
25476
25608
  console.log("");
25477
25609
  return !anyFailed;
25478
25610
  }
@@ -25536,7 +25668,7 @@ function collect_nm_files(folder) {
25536
25668
  return out;
25537
25669
  }
25538
25670
  let build_root;
25539
- console.log("\n~ NOMEN ~\n");
25671
+ console.error("\n~ NOMEN ~\n");
25540
25672
  const args = parse_args();
25541
25673
  const command = args.command;
25542
25674
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nomen-lang",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "The CLI for the Nomen programming language.",
5
5
  "keywords": [],
6
6
  "license": "ISC",
package/src/index.ts CHANGED
@@ -71,7 +71,7 @@ function collect_nm_files(folder: string): string[] {
71
71
  // package.jsonc discovery — the package folder (cwd), not the entry's folder.
72
72
  let build_root: string | undefined;
73
73
 
74
- console.log("\n~ NOMEN ~\n");
74
+ console.error("\n~ NOMEN ~\n");
75
75
 
76
76
  const args: Args = parse_args();
77
77
  const command = args.command;
package/src/init.ts CHANGED
@@ -16,24 +16,24 @@ function package_jsonc(name: string): string {
16
16
  "entry": "src/main.nm"
17
17
  // The System library is resolved automatically from your nomen-lang
18
18
  // install. To pin a local checkout instead, uncomment:
19
- // "imports": { "System": "../core" }
19
+ // "imports": { "System": "~/Source/nomen/core" }
20
20
  }
21
21
  `;
22
22
  }
23
23
 
24
24
  const MAIN_NM = `import System
25
25
 
26
+ pub func add = (int a, int b) => a + b
27
+
26
28
  pub func main = (Init init) {
27
- Console.write("Hello world!\\n")
29
+ Console.write("Hello world, 2 + 2 is \\{add(a + b)}!\\n")
28
30
  }
29
31
  `;
30
32
 
31
33
  const TEST_NM = `import System
32
34
  import System/Test
33
35
 
34
- func add = (int a, int b, out int) => a + b
35
-
36
- pub func test_smoke = (ref Tester t) {
36
+ pub func test_add = (ref Tester t) {
37
37
  t.expect(add(2, 2) == 4, "2 + 2 should equal 4")
38
38
  }
39
39
  `;
@@ -113,7 +113,7 @@ export function run_init(name: string | undefined): void {
113
113
  fs.writeFileSync(path.join(target, ".gitignore"), GITIGNORE);
114
114
  fs.writeFileSync(path.join(target, "package.jsonc"), package_jsonc(name));
115
115
  fs.writeFileSync(path.join(src, "main.nm"), MAIN_NM);
116
- fs.writeFileSync(path.join(test, "smoke.test.nm"), TEST_NM);
116
+ fs.writeFileSync(path.join(test, "main.test.nm"), TEST_NM);
117
117
  fs.writeFileSync(path.join(target, "README.md"), readme(name));
118
118
  fs.writeFileSync(path.join(target, "AGENTS.md"), fs.readFileSync(agents_template));
119
119
 
@@ -121,7 +121,7 @@ export function run_init(name: string | undefined): void {
121
121
  console.log(` ${name}/.gitignore`);
122
122
  console.log(` ${name}/package.jsonc`);
123
123
  console.log(` ${name}/src/main.nm`);
124
- console.log(` ${name}/test/smoke.test.nm`);
124
+ console.log(` ${name}/test/main.test.nm`);
125
125
  console.log(` ${name}/README.md`);
126
126
  console.log(` ${name}/AGENTS.md`);
127
127
  console.log(`\nNext: cd ${name} && nomen run`);
package/src/test.ts CHANGED
@@ -434,7 +434,8 @@ export function runTests(root: string, options: RunTestsOptions = {}): boolean {
434
434
  const lib = resolve_lib_for(root);
435
435
  const files = collect_test_files(root).filter((f) => !options.filter || options.filter.test(f));
436
436
 
437
- console.log(`\n~ NOMEN TEST ~ ${files.length} file(s)\n`);
437
+ console.log(`Found ${files.length} test file(s)\n`);
438
+
438
439
  const startTime = performance.now();
439
440
 
440
441
  const results: TestFileResult[] = [];
@@ -446,6 +447,8 @@ export function runTests(root: string, options: RunTestsOptions = {}): boolean {
446
447
 
447
448
  const elapsed = performance.now() - startTime;
448
449
  const totalFiles = results.length;
450
+ const totalFilesFailed = results.reduce((a, r) => a + (r.fails ? 1 : 0), 0);
451
+ const totalFilesPassed = totalFiles - totalFilesFailed;
449
452
  // `tests[].failed` (from each test's `done` record) is the authoritative
450
453
  // failure count; `result.fails` holds the same failures' messages for
451
454
  // display, so don't sum both or failures are double-counted.
@@ -455,15 +458,20 @@ export function runTests(root: string, options: RunTestsOptions = {}): boolean {
455
458
  const anyFailed = results.some((r) => !r.ok);
456
459
 
457
460
  console.log("");
461
+ //console.log(
462
+ // ` ${C.dim("Files ")} ${totalFiles} ${anyFailed ? C.red("failed") : C.green("passed")} (${totalFiles})`,
463
+ //);
458
464
  console.log(
459
- ` ${C.bold("Files ")} ${totalFiles} ${anyFailed ? C.red("failed") : C.green("passed")} (${totalFiles})`,
465
+ ` ${C.dim("Files ")} ${C.green(`${totalFilesPassed} passed`)}` +
466
+ (totalFilesFailed ? ` ${C.dim("|")} ${C.red(`${totalFilesFailed} failed`)}` : "") +
467
+ C.dim(` (${totalFiles})`),
460
468
  );
461
469
  console.log(
462
- ` ${C.bold("Tests ")} ${totalPassed} ${C.green("passed")}` +
463
- (totalFailed ? ` | ${C.red(`${totalFailed} failed`)}` : "") +
464
- ` (${totalTests})`,
470
+ ` ${C.dim("Tests ")} ${C.green(`${totalPassed} passed`)}` +
471
+ (totalFailed ? ` ${C.dim("|")} ${C.red(`${totalFailed} failed`)}` : "") +
472
+ C.dim(` (${totalTests})`),
465
473
  );
466
- console.log(` ${C.bold("Time ")} ${format_duration(elapsed)}`);
474
+ console.log(` ${C.dim(" Time ")} ${format_duration(elapsed)}`);
467
475
  console.log("");
468
476
 
469
477
  return !anyFailed;