diff --git a/crates/my-lang/src/stdlib.rs b/crates/my-lang/src/stdlib.rs index 07ffd17..8b8d3bb 100644 --- a/crates/my-lang/src/stdlib.rs +++ b/crates/my-lang/src/stdlib.rs @@ -1471,6 +1471,37 @@ fn register_fs_functions(define: &mut impl FnMut(String, Value)) { }, }), ); + + // fs_list_dir(path) -> Array - entry names (not full paths) in + // `path`, sorted for deterministic tooling output. Errors if `path` is not + // a readable directory. See hyperpolymath/my-lang#55. + define( + "fs_list_dir".to_string(), + Value::NativeFunction(NativeFunction { + name: "fs_list_dir".to_string(), + arity: 1, + func: |args| match &args[0] { + Value::String(path) => { + let rd = std::fs::read_dir(path).map_err(|e| { + RuntimeError::Custom(format!("fs_list_dir({}) failed: {}", path, e)) + })?; + let mut names: Vec = Vec::new(); + for entry in rd { + let entry = entry.map_err(|e| { + RuntimeError::Custom(format!("fs_list_dir({}) failed: {}", path, e)) + })?; + names.push(entry.file_name().to_string_lossy().into_owned()); + } + names.sort(); + Ok(Value::Array(names.into_iter().map(Value::String).collect())) + } + _ => Err(RuntimeError::TypeError { + expected: "string".to_string(), + got: format!("{:?}", args[0]), + }), + }, + }), + ); } // ============================================================================ @@ -1880,6 +1911,7 @@ pub fn stdlib_functions() -> Vec<&'static str> { "fs_read_file", "fs_create_dir_all", "fs_exists", + "fs_list_dir", // Env extras "env_args", // Map / dict diff --git a/examples/fs_list_dir.my b/examples/fs_list_dir.my new file mode 100644 index 0000000..ae9a23a --- /dev/null +++ b/examples/fs_list_dir.my @@ -0,0 +1,12 @@ +// Solo fs_list_dir() builtin (hyperpolymath/my-lang#55). +// +// my run examples/fs_list_dir.my +// +// Lists this repo's examples/ directory, sorted. Output includes the other +// example files (date.my, json.my, map.my, fs_list_dir.my, ...). + +fn main() -> Int { + let entries = fs_list_dir("examples"); + println(str_join(entries, "\n")); + return 0; +} diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 11f3012..79a3c39 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -293,6 +293,27 @@ fn test_eval_date_today() { } } +// Solo fs_list_dir() stdlib builtin (hyperpolymath/my-lang#55): enumerate a +// directory, sorted entry names. Self-contained: builds a dir with fs_* builtins +// then lists it (exercises fs_create_dir_all/fs_write_file/fs_list_dir/str_join). +#[test] +fn test_eval_fs_list_dir() { + let source = r#" + fn main() -> String { + let dir = "target/.it_fs_list_dir"; + fs_create_dir_all(dir); + fs_write_file("target/.it_fs_list_dir/b.txt", "x"); + fs_write_file("target/.it_fs_list_dir/a.txt", "y"); + let names = fs_list_dir(dir); + return str_join(names, ","); + } + "#; + match eval(source) { + Ok(Value::String(s)) => assert_eq!(s, "a.txt,b.txt"), + other => panic!("expected sorted dir listing, got {:?}", other), + } +} + // AI Runtime tests (require API keys, so just test initialization) #[test] fn test_ai_runtime_creation() {