Skip to content

Commit cd60f92

Browse files
committed
basic recur implementation works
1 parent 1691905 commit cd60f92

File tree

5 files changed

+167
-3
lines changed

5 files changed

+167
-3
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ license = "MIT"
88
name = "uiua"
99
readme = "readme.md"
1010
repository = "https://github.com/uiua-lang/uiua"
11-
rust-version = "1.82.0"
11+
rust-version = "1.83.0"
1212
version = "0.17.0-dev.2"
1313

1414
[dependencies]

parser/src/defs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3061,6 +3061,8 @@ primitive!(
30613061
/// ex: ⊢path(+⊙¤ ⊂¯.⊞=. °⊏)≍ "thud" "uiua"
30623062
/// [path] is designed to be maximally flexible, so it can be used with graphs or grids or any other structure.
30633063
((2)[2], Path, Algorithm, "path"),
3064+
/// Execute a recursive or tree algorithm
3065+
([3], Recur, Algorithm, "recur", { experimental: true }),
30643066
/// Calculate the derivative of a mathematical expression
30653067
///
30663068
/// Basic polynomials are supported, along with [sine] and [logarithm].

src/algorithm/recur.rs

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,158 @@
1-
use crate::{Uiua, UiuaResult};
1+
use crate::{SigNode, Uiua, UiuaResult, Value};
2+
3+
pub fn recur(is_leaf: SigNode, children: SigNode, combine: SigNode, env: &mut Uiua) -> UiuaResult {
4+
// Signature validation
5+
if is_leaf.sig.args() == 0 {
6+
return Err(env.error(format!(
7+
"Leaf function must have at least 1 argument, but its signature is {}",
8+
is_leaf.sig
9+
)));
10+
}
11+
if is_leaf.sig.outputs() != 1 {
12+
return Err(env.error(format!(
13+
"Leaf function must have 1 output, but its signature is {}",
14+
is_leaf.sig
15+
)));
16+
}
17+
if children.sig.args() == 0 {
18+
return Err(env.error(format!(
19+
"Children function must have at least 1 argument, but its signature is {}",
20+
children.sig
21+
)));
22+
}
23+
if children.sig.outputs() != 1 {
24+
return Err(env.error(format!(
25+
"Children function must have 1 output, but its signature is {}",
26+
children.sig
27+
)));
28+
}
29+
if combine.sig.args() == 0 {
30+
return Err(env.error(format!(
31+
"Combine function must have at least 1 argument, but its signature is {}",
32+
combine.sig
33+
)));
34+
}
35+
if combine.sig.outputs() != 1 {
36+
return Err(env.error(format!(
37+
"Combine function must have 1 output, but its signature is {}",
38+
combine.sig
39+
)));
40+
}
41+
42+
// State initialization
43+
let arg_count = (is_leaf.sig.args())
44+
.max(children.sig.args())
45+
.max(combine.sig.args() - 1);
46+
let const_count = arg_count - 1;
47+
let initial = env.pop(1)?;
48+
let mut consts = Vec::with_capacity(const_count);
49+
for i in 0..const_count {
50+
consts.push(env.pop(i + 2)?);
51+
}
52+
53+
struct RecNode {
54+
parent: Option<usize>,
55+
value: Value,
56+
child_nodes: Option<Vec<Value>>,
57+
scalar_child: bool,
58+
}
59+
let mut stack = vec![RecNode {
60+
parent: None,
61+
value: initial,
62+
child_nodes: None,
63+
scalar_child: false,
64+
}];
65+
66+
// Run algorithm
67+
while let Some(RecNode {
68+
parent,
69+
mut value,
70+
child_nodes,
71+
scalar_child,
72+
}) = stack.pop()
73+
{
74+
println!("value: {value:?}, parent: {parent:?}, child_nodes: {child_nodes:?}");
75+
if let Some(child_nodes) = child_nodes {
76+
env.push_all(
77+
(consts.iter())
78+
.take(is_leaf.sig.args().saturating_sub(2))
79+
.cloned(),
80+
);
81+
let children_value = if scalar_child && child_nodes.len() == 1 {
82+
child_nodes.into_iter().next().unwrap()
83+
} else {
84+
Value::from_row_values(child_nodes, env)?
85+
};
86+
env.push(children_value);
87+
if combine.sig.args() > 1 {
88+
env.push(value);
89+
}
90+
env.exec(combine.clone())?;
91+
value = env.pop("combined")?;
92+
if let Some(parent) = parent {
93+
stack[parent].child_nodes.as_mut().unwrap().push(value);
94+
} else {
95+
env.push(value);
96+
break;
97+
}
98+
} else {
99+
env.push_all(consts.iter().take(is_leaf.sig.args() - 1).cloned());
100+
env.push(value.clone());
101+
env.exec(is_leaf.clone())?;
102+
let this_is_leaf = env.pop("leaf check result")?;
103+
let this_is_leaf = match this_is_leaf {
104+
Value::Num(arr) if arr.rank() == 0 && arr.data[0] == 1.0 => true,
105+
Value::Num(arr) if arr.rank() == 0 && arr.data[0] == 0.0 => false,
106+
Value::Byte(arr) if arr.rank() == 0 && arr.data[0] == 1 => true,
107+
Value::Byte(arr) if arr.rank() == 0 && arr.data[0] == 0 => false,
108+
value if value.rank() >= 1 && value.row_count() == 0 => false,
109+
mut val if val.rank() >= 1 && val.row_count() == 1 => {
110+
val.shape.make_row();
111+
value = val;
112+
true
113+
}
114+
value => {
115+
return Err(env.error(format!(
116+
"Leaf check result must be a boolean or have \
117+
a length of 1 but it is {} {}",
118+
value.shape,
119+
value.type_name_plural()
120+
)))
121+
}
122+
};
123+
if this_is_leaf {
124+
// This is a leaf node
125+
if let Some(parent) = parent {
126+
stack[parent].child_nodes.as_mut().unwrap().push(value);
127+
} else {
128+
env.push(value);
129+
break;
130+
}
131+
} else {
132+
// This is a branch node
133+
env.push_all(consts.iter().take(is_leaf.sig.args() - 1).cloned());
134+
env.push(value.clone());
135+
env.exec(children.clone())?;
136+
let children = env.pop("child nodes")?;
137+
let index = stack.len();
138+
println!("{value:?} has children {children:?}");
139+
stack.push(RecNode {
140+
parent,
141+
value,
142+
child_nodes: Some(Vec::new()),
143+
scalar_child: children.rank() == 0,
144+
});
145+
for value in children.into_rows() {
146+
stack.push(RecNode {
147+
parent: Some(index),
148+
value,
149+
child_nodes: None,
150+
scalar_child: false,
151+
});
152+
}
153+
}
154+
}
155+
}
2156

3-
pub fn recur(env: &mut Uiua) -> UiuaResult {
4157
Ok(())
5158
}

src/check.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,11 @@ impl VirtualEnv {
425425
let [f] = get_args(args)?;
426426
self.handle_args_outputs(f.args(), f.outputs() + 1);
427427
}
428+
Recur => {
429+
let [_is_leaf, children, _combine] = get_args(args)?;
430+
let args = children.args();
431+
self.handle_args_outputs(args, 1);
432+
}
428433
Sys(SysOp::ReadLines) => {
429434
let [f] = get_args(args)?;
430435
self.handle_sig(f);

src/run_prim.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,10 @@ pub fn run_prim_mod(prim: &Primitive, mut ops: Ops, env: &mut Uiua) -> UiuaResul
385385
}
386386
Primitive::Tuples => tuples::tuples(ops, env)?,
387387
Primitive::Stencil => stencil::stencil(ops, env)?,
388+
Primitive::Recur => {
389+
let [is_leaf, children, combine] = get_ops(ops, env)?;
390+
recur::recur(is_leaf, children, combine, env)?;
391+
}
388392

389393
// Stack
390394
Primitive::Fork => {

0 commit comments

Comments
 (0)