From e87368a02fc0f3e6d83567da7faa4e23a0f09a33 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Tue, 25 Feb 2025 09:25:19 -0600 Subject: [PATCH 1/6] wip --- alan/test.ln | 20 ++++++++++ alan_compiler/src/lntojs/function.rs | 44 +++++++++++++++++++++ alan_compiler/src/std/root.ln | 59 ++++++++++++++++++++++------ 3 files changed, 111 insertions(+), 12 deletions(-) diff --git a/alan/test.ln b/alan/test.ln index 3ff95391..4f9b11f5 100644 --- a/alan/test.ln +++ b/alan/test.ln @@ -1276,6 +1276,26 @@ export fn{Test} main { // TODO: Get these tests working // test.assert(eq, bayNode.parent!! ?? 'wrong', 'bar'); // test.assert(eq, myTree.children.map(fn (c: Node{string}) = c ?? 'wrong').join(', '), 'bar, baz'); + }) + .it('prune', fn (test: Mut{Testing}) { + let myTree = Tree('foo'); + const barNode = myTree.addChild('bar'); + const bazNode = barNode.addChild('baz'); + const bayNode = barNode.addChild('bay'); + + barNode.prune; + + test.assert(eq, myTree.rootNode.children.map(fn (c: Node{string}) = c ?? 'wrong').join(', '), ''); + }) + .it('delete', fn (test: Mut{Testing}) { + let myTree = Tree('foo'); + const barNode = myTree.addChild('bar'); + const bazNode = barNode.addChild('baz'); + const bayNode = barNode.addChild('bay'); + + barNode.delete; + + test.assert(eq, myTree.children.map(fn (c: Node{string}) = c!!).join(', '), 'baz, bay'); }); test.describe('Pack and Unpack') diff --git a/alan_compiler/src/lntojs/function.rs b/alan_compiler/src/lntojs/function.rs index 635815f7..8acab116 100644 --- a/alan_compiler/src/lntojs/function.rs +++ b/alan_compiler/src/lntojs/function.rs @@ -959,6 +959,7 @@ pub fn from_microstatement( CType::Array(_) => Ok(enum_type.clone().to_callable_string()), CType::Binds(..) => Ok(enum_type.clone().to_callable_string()), CType::Tuple(_) => Ok(enum_type.clone().to_callable_string()), + CType::Either(_) => Ok(enum_type.clone().to_callable_string()), otherwise => Err(format!("Cannot generate an constructor function for {} type as the input type has no name?, {:?}", function.name, otherwise)), }?; for t in ts { @@ -1010,6 +1011,49 @@ pub fn from_microstatement( } return Ok((argstrs[0].clone(), out, deps)); } + CType::Either(ts) + if inner_type.clone().to_callable_string() + == enum_name => + { + // Special-casing for Option and Result mapping. TODO: + // Make this more centralized + if ts.len() == 2 { + if let CType::Void = &*ts[1] { + if let CType::Void = &**t { + return Ok(("null".to_string(), out, deps)); + } else { + return Ok((argstrs[0].clone(), out, deps)); + } + } else if let CType::Type(name, _) = &*ts[1] { + if name == "Error" { + let (_, d) = typen::ctype_to_jtype( + ts[0].clone(), + deps, + )?; + deps = d; + let (_, d) = typen::ctype_to_jtype( + ts[1].clone(), + deps, + )?; + deps = d; + if let CType::Binds(..) = &**t { + return Ok(( + argstrs[0].clone(), + out, + deps, + )); + } else { + return Ok(( + argstrs[0].clone(), + out, + deps, + )); + } + } + } + } + return Ok((argstrs[0].clone(), out, deps)); + } CType::Field(n, _) if *n == enum_name => { // Special-casing for Option and Result mapping. TODO: // Make this more centralized diff --git a/alan_compiler/src/std/root.ln b/alan_compiler/src/std/root.ln index a75c95f1..9093ea75 100644 --- a/alan_compiler/src/std/root.ln +++ b/alan_compiler/src/std/root.ln @@ -1708,23 +1708,24 @@ fn parent{T}(n: Node{T}) { fn children{T}(n: Node{T}) = if( n.tree.len > n.id, - fn () { + fn { let childIds = n.tree.children[n.id] ?? Array{i64}(); return childIds - .filter(fn (id: i64) = n.tree.parents[id] ?? -1 == n.id) - .map(fn (id: i64) = Node{T}(id, n.tree)); + .filter(fn (id: i64) = n.tree.parents[id]!! ?? -1 == n.id) + .map(fn (id: i64) = Node{T}(id, n.tree.clone)); // TODO: Eliminate this clone }, - fn () = Array{Node{T}}()); + fn = Array{Node{T}}()); fn children{T}(t: Tree{T}) = t.rootNode.children; fn addChild{T}(n: Node{T}, val: T) { - let idx = n.tree.len; + let tree = n.tree; + let idx = tree.len; let parentIdx = n.id; - n.tree.vals.push(val); - n.tree.parents.push(Maybe{i64}(parentIdx)); - n.tree.children.push(Array{i64}()); - return Node{T}(idx, n.tree); + tree.vals.push(val); + tree.parents.push(Maybe{i64}(parentIdx)); + tree.children.push(Array{i64}()); + return Node{T}(idx, n.tree.clone); } fn addChild{T}(n: Node{T}, t: Tree{T}) { let idxOffset = n.tree.len; @@ -1742,17 +1743,51 @@ fn addChild{T}(n: Node{T}, t: Tree{T}) { } fn addChild{T}(t: Tree{T}, val: T) = t.rootNode.addChild(val); +fn delete{T}(n: Node{T}) { + // TODO: Actually drop the node value after the tree has too many dead values. + let nodeId = n.id; + let parentId = n.tree.parents[n.id].getOrExit; + let children = n.children; + // First, update the children to point at their grandparent + children.map(fn (childNode: Node{T}) { + n.tree.parents.store(childNode.id, parentId.clone); + }); + // Next, update the current node's parent to no longer count the node as a child, assuming there + // is a parent + if(parentId.exists, fn { + let parentsChildren = n.tree + .children[parentId.getOrExit] + .getOrExit + .filter(fn (childId: i64) = childId != nodeId); + n.tree.children.store(parentId.getOrExit, parentsChildren); + }); +} -// TODO: Implement `addChild` when the child is itself a `Node{T}` +fn prune{T}(n: Node{T}) { + // TODO: Actually drop the sub-tree after the tree has too many dead values. + // Update the current node's parent to no longer count the node as a child, assuming there is a + // parent + let tree = n.tree; + let nodeId = n.id; + let parentId = tree.parents[n.id].getOrExit; + if(parentId.exists, fn { + let parentsChildren = tree + .children[parentId.getOrExit] + .getOrExit + .filter(fn (childId: i64) = childId != nodeId); + tree.children.store(parentId.getOrExit, parentsChildren); + }); +} -// TODO: Implement `prune` to pull a node out of the tree and re-attach its children to its own -// parent. +// TODO: Implement `addChild` when the child is itself a `Node{T}` // TODO: Implement `subtree` to create a new tree consisting of the specified node as its root and // only its own children as the children of the tree. fn getOr{T}(n: Node{T}, t: T) = n.tree.vals[n.id] ?? t; +fn getOrExit{T}(n: Node{T}) = n.tree.vals[n.id].getOrExit; + fn Array{T}(t: Tree{T}) = t.vals.map( fn (_: T, i: i64) = Node{T}(t, i)!!); From adb761f023326bb6b12f51cab11b5966e32ed041 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Tue, 25 Feb 2025 16:18:44 -0600 Subject: [PATCH 2/6] wip --- alan_compiler/src/std/root.ln | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/alan_compiler/src/std/root.ln b/alan_compiler/src/std/root.ln index 9093ea75..720bd78e 100644 --- a/alan_compiler/src/std/root.ln +++ b/alan_compiler/src/std/root.ln @@ -1747,18 +1747,21 @@ fn delete{T}(n: Node{T}) { // TODO: Actually drop the node value after the tree has too many dead values. let nodeId = n.id; let parentId = n.tree.parents[n.id].getOrExit; - let children = n.children; + let childrenIds = n.children.map(fn (childNode: Node{T}) = childNode.id); + childrenIds.print; // First, update the children to point at their grandparent - children.map(fn (childNode: Node{T}) { - n.tree.parents.store(childNode.id, parentId.clone); + childrenIds.map(fn (childId: i64) { + n.tree.parents.store(childId, parentId.clone); }); // Next, update the current node's parent to no longer count the node as a child, assuming there - // is a parent + // is a parent, and attach the children if(parentId.exists, fn { let parentsChildren = n.tree .children[parentId.getOrExit] .getOrExit .filter(fn (childId: i64) = childId != nodeId); + parentsChildren.append(childrenIds); + parentsChildren.print; n.tree.children.store(parentId.getOrExit, parentsChildren); }); } From 24ace56dbc45d5e4d7ff220887f02745dd6d3b44 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Mon, 17 Mar 2025 17:42:24 -0500 Subject: [PATCH 3/6] Compiler fixes to get auto-generated functions to show up for generic types --- alan_compiler/src/program/ctype.rs | 6 +++--- alan_compiler/src/program/function.rs | 4 ++-- alan_compiler/src/program/microstatement.rs | 10 +++++++--- alan_compiler/src/program/scope.rs | 14 +++++++++++++- alan_compiler/src/std/root.ln | 11 +++++------ 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/alan_compiler/src/program/ctype.rs b/alan_compiler/src/program/ctype.rs index 8b507fcf..3c62533c 100644 --- a/alan_compiler/src/program/ctype.rs +++ b/alan_compiler/src/program/ctype.rs @@ -3158,7 +3158,7 @@ impl CType { })); } } - CType::Type(n, _) => { + CType::Type(n, t) => { // This is just an alias fs.push(Arc::new(Function { name: constructor_fn_name.clone(), @@ -3778,10 +3778,10 @@ impl CType { name_fn_pairs.insert(f.name.clone(), vec![f.clone()]); } } - for (name, fns) in name_fn_pairs.drain() { + for (name, mut fns) in name_fn_pairs.drain() { if scope.functions.contains_key(&name) { let func_vec = scope.functions.get_mut(&name).unwrap(); - func_vec.splice(0..0, fns); + func_vec.append(&mut fns); } else { scope.functions.insert(name, fns); } diff --git a/alan_compiler/src/program/function.rs b/alan_compiler/src/program/function.rs index bc7b93a9..e420eabb 100644 --- a/alan_compiler/src/program/function.rs +++ b/alan_compiler/src/program/function.rs @@ -698,7 +698,7 @@ impl Function { }); if scope.functions.contains_key(&f.name) { let func_vec = scope.functions.get_mut(&f.name).unwrap(); - func_vec.insert(0, f.clone()); + func_vec.push(f.clone()); } else { scope.functions.insert(f.name.clone(), vec![f.clone()]); } @@ -806,7 +806,7 @@ impl Function { }); if scope.functions.contains_key(&f.name) { let func_vec = scope.functions.get_mut(&f.name).unwrap(); - func_vec.insert(0, f.clone()); + func_vec.push(f.clone()); } else { scope.functions.insert(f.name.clone(), vec![f.clone()]); } diff --git a/alan_compiler/src/program/microstatement.rs b/alan_compiler/src/program/microstatement.rs index 6d71afea..962e9ce9 100644 --- a/alan_compiler/src/program/microstatement.rs +++ b/alan_compiler/src/program/microstatement.rs @@ -926,14 +926,15 @@ pub fn baseassignablelist_to_microstatements<'a>( Some((mut temp_scope, fun)) => { // Success! Let's emit this // TODO: Do a better job at type rewriting here + let funargs = fun.args(); #[allow(clippy::needless_range_loop)] - for i in 0..fun.args().len() { + for i in 0..funargs.len() { match &arg_microstatements[i] { Microstatement::Value { typen, representation, } => { - let actual_typen = fun.args()[i].2.clone(); + let actual_typen = funargs[i].2.clone(); if typen != &actual_typen { if matches!(&*actual_typen, CType::Function(..)) { let temp_scope_2 = temp_scope.child(); @@ -1570,7 +1571,10 @@ pub fn statement_to_microstatements<'a>( // TODO: Do we really need this temp_scope? let temp_scope = scope.child(); match temp_scope.resolve_function(&"store".to_string(), &arg_types) { - Some((_, f)) => Ok(f), + Some((s, f)) => { + merge!(scope, s); + Ok(f) + } None => Err(format!( "Could not find store function with arguments {}", arg_types diff --git a/alan_compiler/src/program/scope.rs b/alan_compiler/src/program/scope.rs index a1feb311..593ef3ff 100644 --- a/alan_compiler/src/program/scope.rs +++ b/alan_compiler/src/program/scope.rs @@ -490,7 +490,19 @@ impl<'a> Scope<'a> { let temp_scope = self.child(); match Function::from_generic_function(temp_scope, generic_f, generic_types.to_vec()) { Err(_) => return None, // TODO: Should this be a panic? - Ok((_, realized_f)) => { + Ok((mut temp_scope, realized_f)) => { + // Don't merge the generic types + match &generic_f.kind { + FnKind::Generic(gen_args, _) + | FnKind::BoundGeneric(gen_args, _) + | FnKind::ExternalGeneric(gen_args, _, _) => { + for arg in gen_args { + temp_scope.types.remove(&arg.0); + } + } + _ => unreachable!(), + } + merge!(self, temp_scope); return Some((self, realized_f)); } } diff --git a/alan_compiler/src/std/root.ln b/alan_compiler/src/std/root.ln index 720bd78e..45cf5a72 100644 --- a/alan_compiler/src/std/root.ln +++ b/alan_compiler/src/std/root.ln @@ -1719,14 +1719,14 @@ fn children{T}(n: Node{T}) = if( fn children{T}(t: Tree{T}) = t.rootNode.children; fn addChild{T}(n: Node{T}, val: T) { - let tree = n.tree; - let idx = tree.len; + let idx = n.tree.len; let parentIdx = n.id; - tree.vals.push(val); - tree.parents.push(Maybe{i64}(parentIdx)); - tree.children.push(Array{i64}()); + n.tree.vals.push(val); + n.tree.parents.push(Maybe{i64}(parentIdx)); + n.tree.children.push(Array{i64}()); return Node{T}(idx, n.tree.clone); } + fn addChild{T}(n: Node{T}, t: Tree{T}) { let idxOffset = n.tree.len; let idx = idxOffset.clone(); // TODO: Lower stage of compiler should be doing this @@ -1748,7 +1748,6 @@ fn delete{T}(n: Node{T}) { let nodeId = n.id; let parentId = n.tree.parents[n.id].getOrExit; let childrenIds = n.children.map(fn (childNode: Node{T}) = childNode.id); - childrenIds.print; // First, update the children to point at their grandparent childrenIds.map(fn (childId: i64) { n.tree.parents.store(childId, parentId.clone); From 092feb05a96c7f72eae0d0dda709280aa9e2e134 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 20 Mar 2025 21:19:24 -0500 Subject: [PATCH 4/6] Actually get things working again --- alan_compiler/src/program/ctype.rs | 30 ++++++++++++++------------- alan_compiler/src/program/function.rs | 8 +++---- alan_compiler/src/std/root.ln | 24 +++++++-------------- 3 files changed, 28 insertions(+), 34 deletions(-) diff --git a/alan_compiler/src/program/ctype.rs b/alan_compiler/src/program/ctype.rs index 3c62533c..cdfdb3da 100644 --- a/alan_compiler/src/program/ctype.rs +++ b/alan_compiler/src/program/ctype.rs @@ -3158,18 +3158,20 @@ impl CType { })); } } - CType::Type(n, t) => { - // This is just an alias - fs.push(Arc::new(Function { - name: constructor_fn_name.clone(), - typen: Arc::new(CType::Function( - Arc::new(CType::Field(n.clone(), self.clone())), - t.clone(), - )), - microstatements: Vec::new(), - kind: FnKind::Derived, - origin_scope_path: scope.path.clone(), - })); + CType::Type(n, _) => { + // This is just an alias, but avoid circular derives + if name != constructor_fn_name { + fs.push(Arc::new(Function { + name: constructor_fn_name.clone(), + typen: Arc::new(CType::Function( + Arc::new(CType::Field(n.clone(), self.clone())), + t.clone(), + )), + microstatements: Vec::new(), + kind: FnKind::Derived, + origin_scope_path: scope.path.clone(), + })); + } } CType::Tuple(ts) => { // The constructor function needs to grab the types from all @@ -3778,10 +3780,10 @@ impl CType { name_fn_pairs.insert(f.name.clone(), vec![f.clone()]); } } - for (name, mut fns) in name_fn_pairs.drain() { + for (name, fns) in name_fn_pairs.drain() { if scope.functions.contains_key(&name) { let func_vec = scope.functions.get_mut(&name).unwrap(); - func_vec.append(&mut fns); + func_vec.splice(0..0, fns); } else { scope.functions.insert(name, fns); } diff --git a/alan_compiler/src/program/function.rs b/alan_compiler/src/program/function.rs index e420eabb..8fa13fcf 100644 --- a/alan_compiler/src/program/function.rs +++ b/alan_compiler/src/program/function.rs @@ -698,13 +698,13 @@ impl Function { }); if scope.functions.contains_key(&f.name) { let func_vec = scope.functions.get_mut(&f.name).unwrap(); - func_vec.push(f.clone()); + func_vec.insert(0, f.clone()); } else { scope.functions.insert(f.name.clone(), vec![f.clone()]); } let res = match scope.functions.get(&f.name) { None => Err("This should be impossible. Cannot get the function we just added to the scope"), - Some(fs) => Ok(fs.last().unwrap().clone()), // We know it's the last one + Some(fs) => Ok(fs.first().unwrap().clone()), // We know it's the first one // because we just put it there }?; Ok((scope, res)) @@ -806,13 +806,13 @@ impl Function { }); if scope.functions.contains_key(&f.name) { let func_vec = scope.functions.get_mut(&f.name).unwrap(); - func_vec.push(f.clone()); + func_vec.insert(0, f.clone()); } else { scope.functions.insert(f.name.clone(), vec![f.clone()]); } let res = match scope.functions.get(&f.name) { None => Err("This should be impossible. Cannot get the function we just added to the scope"), - Some(fs) => Ok(fs.last().unwrap().clone()), // We know it's the last one + Some(fs) => Ok(fs.first().unwrap().clone()), // We know it's the first one // because we just put it there }?; Ok((scope, res)) diff --git a/alan_compiler/src/std/root.ln b/alan_compiler/src/std/root.ln index 45cf5a72..f60c38d3 100644 --- a/alan_compiler/src/std/root.ln +++ b/alan_compiler/src/std/root.ln @@ -2577,33 +2577,33 @@ fn gFn16{A, O}( fn gPrimitiveConvert{A, O}(a: A) = gFn1{A, O}({O.typeName}(), a); +fn gu32{T}(u: T) = gu32(u.u32); fn gu32(u: u32) = gu32(u.string.concat('u'), Dict{string, string}(), Set{GBufferTagged}()); fn gu32(gu: gu32) = gu; fn gu32(gi: gi32) = gPrimitiveConvert{gi32, gu32}(gi); fn gu32(gf: gf32) = gPrimitiveConvert{gf32, gu32}(gf); fn gu32(gb: gbool) = gPrimitiveConvert{gbool, gu32}(gb); -fn gu32{T}(u: T) = gu32(u.u32); +fn gi32{T}(i: T) = gi32(i.i32); fn gi32(i: i32) = gi32(i.string, Dict{string, string}(), Set{GBufferTagged}()); fn gi32(gu: gu32) = gPrimitiveConvert{gu32, gi32}(gu); fn gi32(gi: gi32) = gi; fn gi32(gf: gf32) = gPrimitiveConvert{gf32, gi32}(gf); fn gi32(gb: gbool) = gPrimitiveConvert{gbool, gi32}(gb); -fn gi32{T}(i: T) = gi32(i.i32); +fn gf32{T}(f: T) = gf32(f.f32); fn gf32(f: f32) = gf32(if(f.string.eq(f.i32.string), f.string(1), f.string), Dict{string, string}(), Set{GBufferTagged}()); fn gf32(gu: gu32) = gPrimitiveConvert{gu32, gf32}(gu); fn gf32(gi: gi32) = gPrimitiveConvert{gi32, gf32}(gi); fn gf32(gf: gf32) = gf; fn gf32(gb: gbool) = gPrimitiveConvert{gbool, gf32}(gb); -fn gf32{T}(f: T) = gf32(f.f32); +fn gbool{T}(b: T) = gbool(b.bool); fn gbool(b: bool) = gbool(b.string, Dict{string, string}(), Set{GBufferTagged}()); fn gbool(gu: gu32) = gPrimitiveConvert{gu32, gbool}(gu); fn gbool(gi: gi32) = gPrimitiveConvert{gi32, gbool}(gi); fn gbool(gf: gf32) = gPrimitiveConvert{gf32, gbool}(gf); fn gbool(gb: gbool) = gb; -fn gbool{T}(b: T) = gbool(b.bool); fn len{T}(gb: GBuffer{T}) = gu32( 'arrayLength(&'.concat(gb.id).concat(')'), @@ -2689,31 +2689,23 @@ fn gvec3b{T}(a: T) = gvec3b(a, a, a); fn gvec4Primitive{I, O}(a: I, b: I, c: I, d: I) = gFn4{I, O}({O.typeName}(), a, b, c, d); fn gvec4u() = gFn0{gvec4u}({gvec4u.typeName}()); -fn gvec4u(a: gu32, b: gu32, c: gu32, d: gu32) = gvec4Primitive{gu32, gvec4u}( - a, b, c, d -); +fn gvec4u(a: gu32, b: gu32, c: gu32, d: gu32) = gvec4Primitive{gu32, gvec4u}(a, b, c, d); fn gvec4u{T}(a: T, b: T, c: T, d: T) = gvec4u(a.gu32, b.gu32, c.gu32, d.gu32); fn gvec4u{T}(a: T) = gvec4u(a, a, a, a); fn gvec4i() = gFn0{gvec4i}({gvec4i.typeName}()); -fn gvec4i(a: gi32, b: gi32, c: gi32, d: gi32) = gvec4Primitive{gi32, gvec4i}( - a, b, c, d -); +fn gvec4i(a: gi32, b: gi32, c: gi32, d: gi32) = gvec4Primitive{gi32, gvec4i}(a, b, c, d); fn gvec4i{T}(a: T, b: T, c: T, d: T) = gvec4i(a.gi32, b.gi32, c.gi32, d.gi32); fn gvec4i{T}(a: T) = gvec4i(a, a, a, a); fn gvec4f() = gFn0{gvec4f}({gvec4f.typeName}()); fn gvec4f{A, B, C, D}(a: A, b: B, c: C, d: D) = gvec4f(a.gf32, b.gf32, c.gf32, d.gf32); -fn gvec4f(a: gf32, b: gf32, c: gf32, d: gf32) = gvec4Primitive{gf32, gvec4f}( - a, b, c, d -); +fn gvec4f(a: gf32, b: gf32, c: gf32, d: gf32) = gvec4Primitive{gf32, gvec4f}(a, b, c, d); fn gvec4f{T}(a: T, b: T, c: T, d: T) = gvec4f(a.gf32, b.gf32, c.gf32, d.gf32); fn gvec4f{T}(a: T) = gvec4f(a, a, a, a); fn gvec4b() = gFn0{gvec4b}({gvec4b.typeName}()); -fn gvec4b(a: gbool, b: gbool, c: gbool, d: gbool) = gvec4Primitive{gbool, gvec4b}( - a, b, c, d -); +fn gvec4b(a: gbool, b: gbool, c: gbool, d: gbool) = gvec4Primitive{gbool, gvec4b}(a, b, c, d); fn gvec4b{T}(a: T, b: T, c: T, d: T) = gvec4b(a.gbool, b.gbool, c.gbool, d.gbool); fn gvec4b{T}(a: T) = gvec4b(a, a, a, a); From 97e5155d5059f0aaf5520533fd24721df4ce25f4 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Fri, 21 Mar 2025 09:31:27 -0500 Subject: [PATCH 5/6] I was accidentally double-defining 'children' for the Tree{T} type, so I removed the ambiguity that was causing a test to fail --- alan_compiler/src/std/root.ln | 44 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/alan_compiler/src/std/root.ln b/alan_compiler/src/std/root.ln index f60c38d3..3e4ea588 100644 --- a/alan_compiler/src/std/root.ln +++ b/alan_compiler/src/std/root.ln @@ -1667,8 +1667,8 @@ fn mul{V}(a: Set{V}, b: Set{V}) = product(a, b); // `void` if it has no parent and a positive integer otherwise. type Tree{T} = vals: Array{T}, - parents: Array{Maybe{i64}}, - children: Array{Array{i64}}; + parentIdxs: Array{Maybe{i64}}, + childrenIdxs: Array{Array{i64}}; // The Node type simply holds the index to look into the tree for a particular value-parent-children // triplet, where that index is reffered to as a node ID. This allows node-based code to be written @@ -1686,7 +1686,7 @@ fn Tree{T}(n: Node{T}) = n.tree; // TODO: This is a more correct solution, but any Tree constructed by us will always have the root // node be the first element of the array, so we're just doing that so we don't have a `Maybe` here /*export fn rootNode{T}(t: Tree{T}) -> Maybe{Node{T}} { - let rootIdx = t.parents.index(Maybe{i64}()); + let rootIdx = t.parentIdxs.index(Maybe{i64}()); return if( rootIdx.exists, fn () -> Node{T} = Node{T}(rootIdx.getOrExit, t) @@ -1699,7 +1699,7 @@ fn len{T}(t: Tree{T}) = t.vals.len; fn Node{T}(t: Tree{T}, i: i64) = if(t.len > i, fn () = Node{T}(i, t)); fn parent{T}(n: Node{T}) { - let parentId = n.tree.parents[n.id]; + let parentId = n.tree.parentIdxs[n.id]; return if(parentId.exists, if(exists(parentId!!), fn () = Node{T}(parentId.getOrExit.getOrExit, n.tree)), @@ -1709,9 +1709,9 @@ fn parent{T}(n: Node{T}) { fn children{T}(n: Node{T}) = if( n.tree.len > n.id, fn { - let childIds = n.tree.children[n.id] ?? Array{i64}(); + let childIds = n.tree.childrenIdxs[n.id] ?? Array{i64}(); return childIds - .filter(fn (id: i64) = n.tree.parents[id]!! ?? -1 == n.id) + .filter(fn (id: i64) = n.tree.parentIdxs[id]!! ?? -1 == n.id) .map(fn (id: i64) = Node{T}(id, n.tree.clone)); // TODO: Eliminate this clone }, fn = Array{Node{T}}()); @@ -1722,8 +1722,8 @@ fn addChild{T}(n: Node{T}, val: T) { let idx = n.tree.len; let parentIdx = n.id; n.tree.vals.push(val); - n.tree.parents.push(Maybe{i64}(parentIdx)); - n.tree.children.push(Array{i64}()); + n.tree.parentIdxs.push(Maybe{i64}(parentIdx)); + n.tree.childrenIdxs.push(Array{i64}()); return Node{T}(idx, n.tree.clone); } @@ -1732,11 +1732,11 @@ fn addChild{T}(n: Node{T}, t: Tree{T}) { let idx = idxOffset.clone(); // TODO: Lower stage of compiler should be doing this let parentIdx = n.id; n.tree.vals.append(t.vals); - n.tree.parents.append(t.parents.map(fn (pId: Maybe{i64}) = if(pId.exists, + n.tree.parentIdxs.append(t.parentIdxs.map(fn (pId: Maybe{i64}) = if(pId.exists, fn () = Maybe{i64}(pId!! + idxOffset), fn () = Maybe{i64}(idx)))); - n.tree.children.append( - t.children.map( + n.tree.childrenIdxs.append( + t.childrenIdxs.map( fn (ids: Array{i64}) = ids.map( fn (id: i64) = id + idxOffset))); return Node{T}(idx, n.tree); @@ -1746,22 +1746,22 @@ fn addChild{T}(t: Tree{T}, val: T) = t.rootNode.addChild(val); fn delete{T}(n: Node{T}) { // TODO: Actually drop the node value after the tree has too many dead values. let nodeId = n.id; - let parentId = n.tree.parents[n.id].getOrExit; + let parentId = n.tree.parentIdxs[n.id].getOrExit; let childrenIds = n.children.map(fn (childNode: Node{T}) = childNode.id); // First, update the children to point at their grandparent childrenIds.map(fn (childId: i64) { - n.tree.parents.store(childId, parentId.clone); + n.tree.parentIdxs.store(childId, parentId.clone); }); // Next, update the current node's parent to no longer count the node as a child, assuming there // is a parent, and attach the children if(parentId.exists, fn { let parentsChildren = n.tree - .children[parentId.getOrExit] + .childrenIdxs[parentId.getOrExit] .getOrExit .filter(fn (childId: i64) = childId != nodeId); parentsChildren.append(childrenIds); parentsChildren.print; - n.tree.children.store(parentId.getOrExit, parentsChildren); + n.tree.childrenIdxs.store(parentId.getOrExit, parentsChildren); }); } @@ -1771,13 +1771,13 @@ fn prune{T}(n: Node{T}) { // parent let tree = n.tree; let nodeId = n.id; - let parentId = tree.parents[n.id].getOrExit; + let parentId = tree.parentIdxs[n.id].getOrExit; if(parentId.exists, fn { let parentsChildren = tree - .children[parentId.getOrExit] + .childrenIdxs[parentId.getOrExit] .getOrExit .filter(fn (childId: i64) = childId != nodeId); - tree.children.store(parentId.getOrExit, parentsChildren); + tree.childrenIdxs.store(parentId.getOrExit, parentsChildren); }); } @@ -1795,12 +1795,12 @@ fn Array{T}(t: Tree{T}) = t.vals.map( fn map{T, U}(t: Tree{T}, mapper: (Node{T}) -> Node{U}) = Tree{U}( t.Array.map(mapper), - t.parents.clone(), - t.children.clone()); + t.parentIdxs.clone(), + t.childrenIdxs.clone()); fn map{T, U}(t: Tree{T}, mapper: (Node{T}, i64) -> Node{U}) = Tree{U}( t.Array.map(mapper), - t.parents, - t.children); + t.parentIdxs, + t.childrenIdxs); fn every{T}(t: Tree{T}, f: (Node{T}) -> bool) = t.Array.every(f); From 15a22722e565033fb273c0a9eae2f539c03f4018 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Mon, 7 Apr 2025 11:13:48 -0500 Subject: [PATCH 6/6] WIP. Implemented BindsAs type but still not quite working right --- alan_compiler/src/lntors/typen.rs | 1 + alan_compiler/src/program/ctype.rs | 25 +++++++++- alan_compiler/src/program/scope.rs | 2 +- alan_compiler/src/std/root.ln | 75 +++++++++++++++++++----------- 4 files changed, 73 insertions(+), 30 deletions(-) diff --git a/alan_compiler/src/lntors/typen.rs b/alan_compiler/src/lntors/typen.rs index 74891f42..f594b4d5 100644 --- a/alan_compiler/src/lntors/typen.rs +++ b/alan_compiler/src/lntors/typen.rs @@ -284,6 +284,7 @@ pub fn ctype_to_rtype( _ => CType::fail("Bound types must be strings or rust imports"), } } + CType::BindsAs(a, _b) => ctype_to_rtype(a.clone(), deps), CType::IntrinsicGeneric(name, _) => Ok((name.clone(), deps)), // How would this even be reached? CType::Int(i) => Ok((i.to_string(), deps)), CType::Float(f) => Ok((f.to_string(), deps)), diff --git a/alan_compiler/src/program/ctype.rs b/alan_compiler/src/program/ctype.rs index cdfdb3da..8ada689c 100644 --- a/alan_compiler/src/program/ctype.rs +++ b/alan_compiler/src/program/ctype.rs @@ -21,6 +21,7 @@ pub enum CType { Type(String, Arc), Generic(String, Vec, Arc), Binds(Arc, Vec>), + BindsAs(Arc, Arc), IntrinsicGeneric(String, usize), IntCast(Arc), Int(i128), @@ -218,6 +219,13 @@ impl CType { } ctype_stack.push(n); } + CType::BindsAs(a, b) => { + str_parts.push("BindsAs{"); + ctype_stack.push(close_brace); + ctype_stack.push(b); + ctype_stack.push(comma); + ctype_stack.push(a); + } CType::IntrinsicGeneric(s, l) => { str_parts.push(s); str_parts.push("{"); @@ -712,6 +720,13 @@ impl CType { } ctype_stack.push(n); } + CType::BindsAs(a, b) => { + str_parts.push("BindsAs{"); + ctype_stack.push(close_brace); + ctype_stack.push(b); + ctype_stack.push(comma); + ctype_stack.push(a); + } CType::IntrinsicGeneric(s, u) => { str_parts.push(s); str_parts.push("{"); @@ -1287,7 +1302,8 @@ impl CType { CType::Binds(t, ts) | CType::TIf(t, ts) => { t.clone().has_infer() || ts.iter().any(|t| t.clone().has_infer()) } - CType::Function(a, b) + CType::BindsAs(a, b) + | CType::Function(a, b) | CType::Call(a, b) | CType::Dependency(a, b) | CType::Import(a, b) @@ -1333,6 +1349,7 @@ impl CType { .map(|t| t.clone().degroup()) .collect::>>(), )), + CType::BindsAs(a, b) => Arc::new(CType::BindsAs(a.clone().degroup(), b.clone().degroup())), CType::IntrinsicGeneric(..) => self, CType::IntCast(t) => Arc::new(CType::IntCast(t.clone().degroup())), CType::Int(_) => self, @@ -2714,6 +2731,7 @@ impl CType { } pub fn accepts(self: Arc, arg: Arc) -> bool { match (&*self, &*arg) { + (_a, CType::BindsAs(_b, c)) => self.accepts(c.clone()), (_a, CType::AnyOf(ts)) => { for t in ts { if self.clone().accepts(t.clone()) { @@ -3873,6 +3891,10 @@ impl CType { .map(|gtr| gtr.clone().swap_subtype(old_type.clone(), new_type.clone())) .collect::>>(), )), + CType::BindsAs(a, b) => Arc::new(CType::BindsAs( + a.clone().swap_subtype(old_type.clone(), new_type.clone()), + b.clone().swap_subtype(old_type, new_type), + )), CType::IntCast(i) => CType::intcast(i.clone().swap_subtype(old_type, new_type)), CType::FloatCast(f) => CType::floatcast(f.clone().swap_subtype(old_type, new_type)), CType::BoolCast(b) => CType::boolcast(b.clone().swap_subtype(old_type, new_type)), @@ -5524,6 +5546,7 @@ pub fn typebaselist_to_ctype( // TODO: Is there a better way to do this? match name.as_str() { "Binds" => CType::binds(args), + "BindsAs" => Arc::new(CType::BindsAs(args[0].clone(), args[1].clone())), "Int" => CType::intcast(args[0].clone()), "Float" => CType::floatcast(args[0].clone()), "Bool" => CType::boolcast(args[0].clone()), diff --git a/alan_compiler/src/program/scope.rs b/alan_compiler/src/program/scope.rs index 593ef3ff..5fb94d5c 100644 --- a/alan_compiler/src/program/scope.rs +++ b/alan_compiler/src/program/scope.rs @@ -88,7 +88,7 @@ impl<'a> Scope<'a> { | "Prefix" | "Method" | "Property" | "Cast" | "Own" | "Deref" | "Mut" | "Rust" | "Nodejs" | "From" | "Array" | "Fail" | "Neg" | "Len" | "Size" | "FileStr" | "Env" | "EnvExists" | "Not") => s = CType::from_generic(s, g, 1), - g @ ("Function" | "Call" | "Dependency" | "Import" | "Field" + g @ ("BindsAs" | "Function" | "Call" | "Dependency" | "Import" | "Field" | "Prop" | "Buffer" | "Add" | "Sub" | "Mul" | "Div" | "Mod" | "Pow" | "Min" | "Max" | "Concat" | "And" | "Or" | "Xor" | "Nand" | "Nor" | "Xnor" | "Eq" | "Neq" | "Lt" | "Lte" | "Gt" | "Gte") => s = CType::from_generic(s, g, 2), diff --git a/alan_compiler/src/std/root.ln b/alan_compiler/src/std/root.ln index 3e4ea588..e3bdd524 100644 --- a/alan_compiler/src/std/root.ln +++ b/alan_compiler/src/std/root.ln @@ -21,6 +21,7 @@ ctype AnyOf{A, B}; // The AnyOf type is kinda like a Tuple, in that all sub-type /// Host types ctype Binds{T}; // A direct reference into the platform language's type system +ctype BindsAs{A, B}; // Specify that the first type should be *treated* as the second within Alan. Useful for binding special types in Rust ctype Call{N, F}; // A reference to a function call (or method, etc) in the platform language ctype Infix{O}; // A reference to a native infix operation in the platform language ctype Prefix{O}; // A reference to a native prefix operation in the platform language @@ -189,6 +190,12 @@ type{Js} Dict{K, V} = Binds{"Map", K, V}; type{Rs} Set{V} = Binds{"std::collections::HashSet", V}; type{Js} Set{V} = Binds{"alan_std.FuzzySet" <- RootBacking, V}; +// Binding the Arc, RwLock and creating the Shared types +type{Rs} Arc{T} = Binds{"std::sync::Arc", T}; +type{Rs} RwLock{T} = Binds{"std::sync::RwLock", T}; +type{Rs} Shared{T} = Arc{RwLock{T}}; +type{Js} Shared{T} = T; // TODO: Better solution here + // Basic trig constants (this language is meant for GPGPU, this makes sense in the root scope) const e = 2.718281828459045; const pi = 3.141592653589793; @@ -1660,16 +1667,24 @@ fn{Rs} product{V} "alan_std::productset" <- RootBacking :: (Set{V}, Set{V}) -> S fn{Js} product{V} (a: Set{V}, b: Set{V}) = {Method{"product"} :: (Set{V}, Set{V}) -> Set{(V, V)}}(a, b); fn mul{V}(a: Set{V}, b: Set{V}) = product(a, b); +/// Shared implementation +fn{Rs} Shared{T}(v: T) = {"std::sync::Arc::new" :: (arg: Own{RwLock{T}}) -> Arc{RwLock{T}}}({"std::sync::RwLock::new" :: (arg: Own{T}) -> RwLock{T}}(v)); +fn{Rs} read{T}(v: Shared{T}) = {Method{"read().unwrap"} :: (arg: Shared{T}) -> BindsAs{Binds{"std::sync::RwLockReadGuard", T}, T}}(v); // TODO: Expose this Result type when multithreading is accessible in the language +fn{Rs} write{T}(v: Shared{T}) = {Method{"write().unwrap"} :: (arg: Shared{T}) -> BindsAs{Binds{"std::sync::RwLockWriteGuard", T}, T}}(v); // TODO: Expose this Result type when multithreading is accessible in the language +fn{Rs} close{T}(v: T) = {Method{"drop"} :: (arg: T) -> ()}(v); // TODO: How would this even work in JS? + /// Tree implementation -// The Tree type houses all of the values attached to a tree in an array and two secondary arrays to -// hold the metadata on which value is the parent and which are children, if any. The parent value -// `void` if it has no parent and a positive integer otherwise. -type Tree{T} = +// The TreeInner type houses all of the values attached to a tree in an array and two secondary +// arrays to hold the metadata on which value is the parent and which are children, if any. The +// parent value `void` if it has no parent and a positive integer otherwise. +type TreeInner{T} = vals: Array{T}, parentIdxs: Array{Maybe{i64}}, childrenIdxs: Array{Array{i64}}; +type Tree{T} = Shared{TreeInner{T}}; + // The Node type simply holds the index to look into the tree for a particular value-parent-children // triplet, where that index is reffered to as a node ID. This allows node-based code to be written // while not actually having a recursive data structure that a traditional Node type would defined. @@ -1677,10 +1692,10 @@ type Node{T} = id: i64, tree: Tree{T}; -fn Tree{T}(rootVal: T) = Tree{T}( +fn Tree{T}(rootVal: T) = Shared(TreeInner{T}( Array{T}(rootVal), Array{Maybe{i64}}(Maybe{i64}()), - Array{Array{i64}}(Array{i64}())); + Array{Array{i64}}(Array{i64}()))); fn Tree{T}(n: Node{T}) = n.tree; // TODO: This is a more correct solution, but any Tree constructed by us will always have the root @@ -1694,12 +1709,12 @@ fn Tree{T}(n: Node{T}) = n.tree; }*/ fn rootNode{T}(t: Tree{T}) = Node{T}(0, t); -fn len{T}(t: Tree{T}) = t.vals.len; +fn len{T}(t: Tree{T}) = t.read.vals.len; fn Node{T}(t: Tree{T}, i: i64) = if(t.len > i, fn () = Node{T}(i, t)); fn parent{T}(n: Node{T}) { - let parentId = n.tree.parentIdxs[n.id]; + let parentId = n.tree.read.parentIdxs[n.id]; return if(parentId.exists, if(exists(parentId!!), fn () = Node{T}(parentId.getOrExit.getOrExit, n.tree)), @@ -1709,10 +1724,11 @@ fn parent{T}(n: Node{T}) { fn children{T}(n: Node{T}) = if( n.tree.len > n.id, fn { - let childIds = n.tree.childrenIdxs[n.id] ?? Array{i64}(); + let tree = n.tree.read; + let childIds = tree.childrenIdxs[n.id] ?? Array{i64}(); return childIds - .filter(fn (id: i64) = n.tree.parentIdxs[id]!! ?? -1 == n.id) - .map(fn (id: i64) = Node{T}(id, n.tree.clone)); // TODO: Eliminate this clone + .filter(fn (id: i64) = tree.parentIdxs[id]!! ?? -1 == n.id) + .map(fn (id: i64) = Node{T}(id, n.tree.clone)); }, fn = Array{Node{T}}()); @@ -1721,9 +1737,10 @@ fn children{T}(t: Tree{T}) = t.rootNode.children; fn addChild{T}(n: Node{T}, val: T) { let idx = n.tree.len; let parentIdx = n.id; - n.tree.vals.push(val); - n.tree.parentIdxs.push(Maybe{i64}(parentIdx)); - n.tree.childrenIdxs.push(Array{i64}()); + let tree = n.tree.write; + tree.vals.push(val); + tree.parentIdxs.push(Maybe{i64}(parentIdx)); + tree.childrenIdxs.push(Array{i64}()); return Node{T}(idx, n.tree.clone); } @@ -1731,11 +1748,12 @@ fn addChild{T}(n: Node{T}, t: Tree{T}) { let idxOffset = n.tree.len; let idx = idxOffset.clone(); // TODO: Lower stage of compiler should be doing this let parentIdx = n.id; - n.tree.vals.append(t.vals); - n.tree.parentIdxs.append(t.parentIdxs.map(fn (pId: Maybe{i64}) = if(pId.exists, + let tree = n.tree.write; + tree.vals.append(t.vals); + tree.parentIdxs.append(t.parentIdxs.map(fn (pId: Maybe{i64}) = if(pId.exists, fn () = Maybe{i64}(pId!! + idxOffset), fn () = Maybe{i64}(idx)))); - n.tree.childrenIdxs.append( + tree.childrenIdxs.append( t.childrenIdxs.map( fn (ids: Array{i64}) = ids.map( fn (id: i64) = id + idxOffset))); @@ -1746,22 +1764,23 @@ fn addChild{T}(t: Tree{T}, val: T) = t.rootNode.addChild(val); fn delete{T}(n: Node{T}) { // TODO: Actually drop the node value after the tree has too many dead values. let nodeId = n.id; - let parentId = n.tree.parentIdxs[n.id].getOrExit; + let tree = n.tree.write; + let parentId = tree.parentIdxs[n.id].getOrExit; let childrenIds = n.children.map(fn (childNode: Node{T}) = childNode.id); // First, update the children to point at their grandparent childrenIds.map(fn (childId: i64) { - n.tree.parentIdxs.store(childId, parentId.clone); + tree.parentIdxs.store(childId, parentId.clone); }); // Next, update the current node's parent to no longer count the node as a child, assuming there // is a parent, and attach the children if(parentId.exists, fn { - let parentsChildren = n.tree + let parentsChildren = tree .childrenIdxs[parentId.getOrExit] .getOrExit .filter(fn (childId: i64) = childId != nodeId); parentsChildren.append(childrenIds); parentsChildren.print; - n.tree.childrenIdxs.store(parentId.getOrExit, parentsChildren); + tree.childrenIdxs.store(parentId.getOrExit, parentsChildren); }); } @@ -1786,21 +1805,21 @@ fn prune{T}(n: Node{T}) { // TODO: Implement `subtree` to create a new tree consisting of the specified node as its root and // only its own children as the children of the tree. -fn getOr{T}(n: Node{T}, t: T) = n.tree.vals[n.id] ?? t; +fn getOr{T}(n: Node{T}, t: T) = n.tree.read.vals[n.id] ?? t; -fn getOrExit{T}(n: Node{T}) = n.tree.vals[n.id].getOrExit; +fn getOrExit{T}(n: Node{T}) = n.tree.read.vals[n.id].getOrExit; -fn Array{T}(t: Tree{T}) = t.vals.map( +fn Array{T}(t: Tree{T}) = t.read.vals.map( fn (_: T, i: i64) = Node{T}(t, i)!!); fn map{T, U}(t: Tree{T}, mapper: (Node{T}) -> Node{U}) = Tree{U}( t.Array.map(mapper), - t.parentIdxs.clone(), - t.childrenIdxs.clone()); + t.read.parentIdxs.clone(), + t.read.childrenIdxs.clone()); fn map{T, U}(t: Tree{T}, mapper: (Node{T}, i64) -> Node{U}) = Tree{U}( t.Array.map(mapper), - t.parentIdxs, - t.childrenIdxs); + t.read.parentIdxs, + t.read.childrenIdxs); fn every{T}(t: Tree{T}, f: (Node{T}) -> bool) = t.Array.every(f);