Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
68d9108
Add baml.fs.read_dir, baml.fs.mkdir, baml.glob namespace, and fix par…
aaronvg Apr 23, 2026
1b6afd7
Finish BEP 37 filesystem glob implementation
antoniosarosi Apr 28, 2026
f54a106
Test WASM filesystem glob runtime wiring
antoniosarosi Apr 28, 2026
6e30cf7
Extract glob pattern matcher into shared sys_glob crate
antoniosarosi Apr 28, 2026
506ed49
WIP: WASM read_dir/mkdir + new fs/glob tests + MIR field-order workar…
antoniosarosi Apr 28, 2026
a40872b
Fix MIR field-order bug: preserve qualified type paths through TIR
antoniosarosi Apr 28, 2026
9db8793
Native Glob.scan: propagate real I/O errors, scope throw-on-broken co…
antoniosarosi Apr 28, 2026
0277dae
Native Glob.scan: prune dot directories during walk
antoniosarosi Apr 28, 2026
51fecad
Glob.scan: pick canonical path form by pattern shape
antoniosarosi Apr 28, 2026
bde42c6
WASM mkdir(recursive=true): don't turn create errors into success
antoniosarosi Apr 29, 2026
2c7b72d
WASM read_dir: rich readDirEntries contract with symlink info
antoniosarosi Apr 29, 2026
bd606da
Move external_as_string/bool helpers to BexExternalValue
antoniosarosi Apr 29, 2026
afb2092
Codegen: unify emit_result_conversion + emit_result_conversion_inner
antoniosarosi Apr 29, 2026
a98e4be
WASM Glob.scan: anchor `cwd: "."` to the VFS root
antoniosarosi Apr 29, 2026
6bf2691
Native fs/glob: add coverage gaps from review
antoniosarosi Apr 29, 2026
4920205
Trim redundant fs_open_only test
antoniosarosi Apr 29, 2026
399ebe4
Merge branch 'canary' into aaron/add-readdir-function-to-bamlfs-module
antoniosarosi Apr 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions baml_language/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions baml_language/crates/baml_builtins2/baml_std/baml/ns_fs/fs.baml
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,22 @@ function write(path: string, content: string) -> int throws root.errors.Io {
function write_bytes(path: string, content: uint8array) -> int throws root.errors.Io {
$rust_io_function
}

class DirEntry {
name string
is_dir bool
is_file bool
is_symlink bool
}

class MkdirOptions {
recursive bool
}

function read_dir(path: string) -> DirEntry[] throws root.errors.Io {
$rust_io_function
}

function mkdir(path: string, options: MkdirOptions) -> null throws root.errors.Io {
$rust_io_function
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class ScanOptions {
cwd string?
dot bool?
absolute bool?
follow_symlinks bool?
throw_error_on_broken_symlink bool?
only_files bool?
}

class Glob {
_handle $rust_type

function scan(self, root: string | ScanOptions) -> string[] throws root.errors.Io {
$rust_io_function
}

function matches(self, path: string) -> bool throws root.errors.Io {
$rust_io_function
}
}

function new(pattern: string) -> Glob throws root.errors.Io {
$rust_io_function
}
1 change: 1 addition & 0 deletions baml_language/crates/baml_builtins2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ pub const ALL: &[BuiltinFile] = &[
builtin!("baml", "ns_math/math.baml"),
builtin!("baml", "ns_sys/sys.baml"),
builtin!("baml", "ns_fs/fs.baml"),
builtin!("baml", "ns_glob/glob.baml"),
builtin!("baml", "ns_net/net.baml"),
builtin!("baml", "ns_media/media.baml"),
builtin!("baml", "ns_unstable/unstable.baml"),
Expand Down
178 changes: 163 additions & 15 deletions baml_language/crates/baml_builtins2_codegen/src/codegen_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1422,6 +1422,44 @@ fn emit_one_namespace_trait(
}
}

/// Generate the `.into_result(...)` call for a given return type.
///
/// - Scalar types implementing `AsBexExternalValue` use `.into_result(op)`.
/// - `List(Named(...))` uses `.into_result_mapped(op, |v| ...)` because
/// `Vec<ClassName>` does not implement `AsBexExternalValue` (orphan rules
/// prevent it in the generated crate).
fn emit_into_result_call(
return_type: &BamlType,
variant_ident: &syn::Ident,
call_expr: &TokenStream,
class_ns_map: &BTreeMap<String, String>,
paths: &CodegenPaths,
) -> TokenStream {
if let BamlType::List(inner) = return_type {
if let BamlType::Named(name) = inner.as_ref() {
if let Some(ns) = class_ns_map.get(name.as_str()) {
let owned = &paths.owned;
let ns_ident = format_ident!("{}", ns);
let name_ident = format_ident!("{}", name);
return quote! {
#call_expr
.into_result_mapped(SysOp::#variant_ident, |v| {
BexExternalValue::Array {
element_type: baml_type::Ty::unknown(),
items: v.into_iter()
.map(|item| <#owned::#ns_ident::#name_ident as AsBexExternalValue>::into_bex_external_value(item))
.collect(),
}
})
};
}
}
}
quote! {
#call_expr.into_result(SysOp::#variant_ident)
}
}

fn emit_free_fn_glue(
builtin: &NativeBuiltin,
ns_trait_ident: &syn::Ident,
Expand All @@ -1433,6 +1471,16 @@ fn emit_free_fn_glue(
let clean_ident = format_ident!("{}", io_method_name(builtin));

if builtin.params.is_empty() {
let call_expr = quote! {
#ns_trait_ident::#clean_ident(self, heap, call_id, ctx)
};
let into_result = emit_into_result_call(
&builtin.return_type,
&variant_ident,
&call_expr,
class_ns_map,
paths,
);
return quote! {
fn #glue_ident<'a>(
&self,
Expand All @@ -1442,7 +1490,7 @@ fn emit_free_fn_glue(
ctx: &SysOpContext,
call_id: CallId,
) -> SysOpResult {
#ns_trait_ident::#clean_ident(self, heap, call_id, ctx).into_result(SysOp::#variant_ident)
#into_result
}
};
}
Expand Down Expand Up @@ -1487,6 +1535,17 @@ fn emit_free_fn_glue(
quote! { Ok::<_, AccessError>((#(#param_idents),*)) }
};

let call_expr = quote! {
#ns_trait_ident::#clean_ident(self, heap, call_id, #(#param_idents,)* ctx)
};
let into_result = emit_into_result_call(
&builtin.return_type,
&variant_ident,
&call_expr,
class_ns_map,
paths,
);

quote! {
fn #glue_ident<'a>(
&self,
Expand All @@ -1506,8 +1565,7 @@ fn emit_free_fn_glue(

match __extraction {
Ok(#ok_pattern) => {
#ns_trait_ident::#clean_ident(self, heap, call_id, #(#param_idents,)* ctx)
.into_result(SysOp::#variant_ident)
#into_result
}
Err(e) => SysOpResult::Ready(Err(OpError::new(
SysOp::#variant_ident,
Expand Down Expand Up @@ -2064,8 +2122,7 @@ fn emit_result_conversion(
},
BamlType::Null => quote! { Ok(()) },
BamlType::Optional(inner) => {
// Create a temporary builtin with the inner type to recurse.
let inner_conv = emit_result_conversion_simple(inner, class_ns_map, paths);
let inner_conv = emit_result_conversion_inner(inner, class_ns_map, paths);
quote! {
match __val {
BexExternalValue::Null => Ok(None),
Expand Down Expand Up @@ -2110,49 +2167,140 @@ fn emit_result_conversion(
}
}
},
BamlType::List(inner) => {
let inner_conv = emit_result_conversion_inner(inner, class_ns_map, paths);
quote! {
match __val {
BexExternalValue::Array { items, .. } => {
items.into_iter()
.map(|__val| { #inner_conv })
.collect::<Result<Vec<_>, _>>()
}
other => Err(RuntimeIoError::Other(
format!("expected array, got {}", other.type_name()),
)),
}
}
}
_ => quote! { Ok(__val) },
}
}

/// Simplified result conversion for inner types (used in Optional).
fn emit_result_conversion_simple(
/// Per-element result conversion for list inner types.
/// Used by the `BamlType::List` arm of `emit_result_conversion`.
fn emit_result_conversion_inner(
ty: &BamlType,
_class_ns_map: &BTreeMap<String, String>,
_paths: &CodegenPaths,
class_ns_map: &BTreeMap<String, String>,
paths: &CodegenPaths,
) -> TokenStream {
match ty {
BamlType::String => quote! {
match __val {
BexExternalValue::String(s) => Ok(s),
other => Err(RuntimeIoError::Other(
format!("expected string, got {}", other.type_name()),
format!("expected string in list, got {}", other.type_name()),
)),
}
},
BamlType::Int => quote! {
match __val {
BexExternalValue::Int(v) => Ok(v),
other => Err(RuntimeIoError::Other(
format!("expected int, got {}", other.type_name()),
format!("expected int in list, got {}", other.type_name()),
)),
}
},
BamlType::Bool => quote! {
match __val {
BexExternalValue::Bool(v) => Ok(v),
other => Err(RuntimeIoError::Other(
format!("expected bool in list, got {}", other.type_name()),
)),
}
},
BamlType::Float => quote! {
match __val {
BexExternalValue::Float(v) => Ok(v),
other => Err(RuntimeIoError::Other(
format!("expected float, got {}", other.type_name()),
format!("expected float in list, got {}", other.type_name()),
)),
}
},
BamlType::Bool => quote! {
BamlType::Uint8Array => quote! {
match __val {
BexExternalValue::Bool(v) => Ok(v),
BexExternalValue::Uint8Array(v) => Ok(v),
other => Err(RuntimeIoError::Other(
format!("expected bool, got {}", other.type_name()),
format!("expected uint8array in list, got {}", other.type_name()),
)),
}
},
BamlType::Optional(inner) => {
let inner_conv = emit_result_conversion_inner(inner, class_ns_map, paths);
quote! {
match __val {
BexExternalValue::Null => Ok(None),
other => {
let __val = other;
Ok(Some({ #inner_conv }?))
}
}
}
}
BamlType::List(inner) => {
let inner_conv = emit_result_conversion_inner(inner, class_ns_map, paths);
quote! {
match __val {
BexExternalValue::Array { items, .. } => {
items.into_iter()
.map(|__val| { #inner_conv })
.collect::<Result<Vec<_>, _>>()
}
other => Err(RuntimeIoError::Other(
format!("expected array in list, got {}", other.type_name()),
)),
}
}
}
BamlType::Map(key, value) if matches!(key.as_ref(), BamlType::String) => {
let value_conv = emit_result_conversion_inner(value, class_ns_map, paths);
quote! {
match __val {
BexExternalValue::Map { entries, .. } => {
entries.into_iter()
.map(|(__key, __val)| Ok((__key, { #value_conv }?)))
.collect::<Result<indexmap::IndexMap<_, _>, _>>()
}
other => Err(RuntimeIoError::Other(
format!("expected map in list, got {}", other.type_name()),
)),
}
}
}
BamlType::Named(name) => match name.as_str() {
"type" => quote! {
match __val {
BexExternalValue::Adt(
bex_external_types::BexExternalAdt::Type(ty),
) => Ok(ty),
other => Err(RuntimeIoError::Other(
format!("expected type in list, got {}", other.type_name()),
)),
}
},
_ => {
if let Some(ns) = class_ns_map.get(name.as_str()) {
let owned = &paths.owned;
let ns_ident = format_ident!("{}", ns);
let name_ident = format_ident!("{}", name);
quote! {
#owned::#ns_ident::#name_ident::from_external(__val)
.map_err(|e| RuntimeIoError::Other(format!("{e:?}")))
}
} else {
quote! { Ok(__val) }
}
}
},
_ => quote! { Ok(__val) },
}
}
Expand Down
Loading
Loading