Skip to content

Commit

Permalink
Improved JS clone function that better handles nesting (#993)
Browse files Browse the repository at this point in the history
* Add a clone function to the JS std backing

* Actually use the variable name I chose

* Getting rusty dealing with the wonky chaining in Javascript vs Alan...

* With the new Object catch-all, I need to handle Map properly, too

* Bind the new clone function for JS and add a regression test for it
  • Loading branch information
dfellis authored Dec 6, 2024
1 parent f372086 commit a43eaa3
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 7 deletions.
17 changes: 17 additions & 0 deletions alan/src/compile/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,23 @@ test!(mutable_functions => r#"
stdout "3\n5\n";
);

test!(complex_cloning => r#"
type struct = b: bool, d: Dict{string, Set{i64}};
export fn main {
let s = Set([1, 2, 3]);
s.len.print;
s.clone.len.print;
let d = Dict('foo', s);
d.len.print;
d.clone.len.print;
let b = struct(true, d);
b.d.len.print;
b.clone{struct}.d.len.print;
}"#;
stdout "3\n3\n1\n1\n1\n1\n";
);

// Conditionals

test_ignore!(basic_conditionals => r#"
Expand Down
8 changes: 4 additions & 4 deletions alan/src/program/ctype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl CType {
Box::new(CType::Node(Box::new(CType::Dependency(
Box::new(CType::TString("alan_std".to_string())),
Box::new(CType::TString(
"https://github.com/alantech/alan.git".to_string(),
"https://github.com/alantech/alan.git#js-clone-fix".to_string(),
)),
)))),
)),
Expand Down Expand Up @@ -135,7 +135,7 @@ impl CType {
Box::new(CType::Node(Box::new(CType::Dependency(
Box::new(CType::TString("alan_std".to_string())),
Box::new(CType::TString(
"https://github.com/alantech/alan.git".to_string(),
"https://github.com/alantech/alan.git#js-clone-fix".to_string(),
)),
)))),
)),
Expand Down Expand Up @@ -168,7 +168,7 @@ impl CType {
Box::new(CType::Node(Box::new(CType::Dependency(
Box::new(CType::TString("alan_std".to_string())),
Box::new(CType::TString(
"https://github.com/alantech/alan.git".to_string(),
"https://github.com/alantech/alan.git#js-clone-fix".to_string(),
)),
)))),
)),
Expand Down Expand Up @@ -201,7 +201,7 @@ impl CType {
Box::new(CType::Node(Box::new(CType::Dependency(
Box::new(CType::TString("alan_std".to_string())),
Box::new(CType::TString(
"https://github.com/alantech/alan.git".to_string(),
"https://github.com/alantech/alan.git#js-clone-fix".to_string(),
)),
)))),
)),
Expand Down
5 changes: 2 additions & 3 deletions alan/src/std/root.ln
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ type Js = Env{"ALAN_OUTPUT_LANG"} == "js";

// Importing the Root Scope backing implementation and supporting 3rd party libraries
type{Rs} RootBacking = Rust{"alan_std" @ "https://github.com/alantech/alan.git"};
type{Js} RootBacking = Node{"alan_std" @ "https://github.com/alantech/alan.git"};
type{Js} RootBacking = Node{"alan_std" @ "https://github.com/alantech/alan.git#js-clone-fix"};

// Defining derived types
type void = ();
Expand Down Expand Up @@ -221,8 +221,7 @@ infix store as = precedence 0;

/// Functions for (potentially) every type
fn{Rs} clone{T} (v: T) -> T = {Method{"clone"} :: T -> T}(v);
// TODO: This needs to be turned into a JS function that's bound
fn{Js} clone{T} (v: T) -> T = {"(function clone(t) { if (t instanceof Array) { return t.map(clone); } else if (t.build instanceof Function) { return t.build(t.val); } else if (t instanceof Set) { return t.union(new Set()); } else { return structuredClone(t); } })" :: T -> T}(v);
fn{Js} clone{T} (v: T) -> T = {"alan_std.clone" <- RootBacking :: T -> T}(v);
fn void{T}(v: T) -> void {}
fn void() -> void {}
fn{Rs} store{T} (a: Mut{T}, b: T) -> T = {"std::mem::replace" :: (Mut{T}, Own{T}) -> T}(a, b);
Expand Down
16 changes: 16 additions & 0 deletions alan_std.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ export function ifbool(b, t, f) {
}
}

export function clone(v) {
if (v instanceof Array) {
return v.map(clone);
} else if (v instanceof Set) {
return v.union(new Set());
} else if (v instanceof Map) {
return new Map(v.entries().map((kv) => [clone(kv[0]), clone(kv[1])]));
} else if (v.build instanceof Function) {
return v.build(v.val);
} else if (v instanceof Object) {
return Object.fromEntries(Object.entries(v).map((kv) => [kv[0], clone(kv[1])]));
} else {
return structuredClone(v);
}
}

// For those reading this binding support code, you might be wondering *why* all of the primitive
// types are now boxed in their own classes. The reason is that in Alan (and Rust), you can mark
// any input argument as mutable instead of the default being immutable, but in Javascript, all
Expand Down

0 comments on commit a43eaa3

Please sign in to comment.