• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TyRoXx / NonlocalityOS / 15227855601

24 May 2025 02:10PM UTC coverage: 72.437% (+0.1%) from 72.333%
15227855601

Pull #255

github

web-flow
Merge 5166f509f into 43d85118a
Pull Request #255: GH-244: Multiple lambda parameters work

24 of 27 new or added lines in 2 files covered. (88.89%)

1 existing line in 1 file now uncovered.

3222 of 4448 relevant lines covered (72.44%)

2235.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.17
/lambda/src/expressions.rs
1
use crate::name::Name;
2
use astraea::tree::{BlobDigest, HashedTree, ReferenceIndex, Tree, TreeDeserializationError};
3
use astraea::{
4
    storage::{LoadTree, StoreError, StoreTree},
5
    tree::TreeBlob,
6
};
7
use serde::{Deserialize, Serialize};
8
use std::fmt::Display;
9
use std::future::Future;
10
use std::hash::Hash;
11
use std::{pin::Pin, sync::Arc};
12

13
pub trait PrintExpression {
14
    fn print(&self, writer: &mut dyn std::fmt::Write, level: usize) -> std::fmt::Result;
15
}
16

17
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Serialize, Deserialize)]
18
pub enum Expression<E, TreeLike>
19
where
20
    E: Clone + Display + PrintExpression,
21
    TreeLike: Clone + std::fmt::Debug,
22
{
23
    Literal(TreeLike),
24
    Apply { callee: E, argument: E },
25
    Argument,
26
    Environment,
27
    Lambda { environment: E, body: E },
28
    ConstructTree(Vec<E>),
29
    GetChild { parent: E, index: u16 },
30
}
31

32
impl<E, V> PrintExpression for Expression<E, V>
33
where
34
    E: Clone + Display + PrintExpression,
35
    V: Clone + std::fmt::Debug,
36
{
37
    fn print(&self, writer: &mut dyn std::fmt::Write, level: usize) -> std::fmt::Result {
19✔
38
        match self {
19✔
39
            Expression::Literal(literal_value) => {
8✔
40
                write!(writer, "literal({literal_value:?})")
8✔
41
            }
42
            Expression::Apply { callee, argument } => {
1✔
43
                callee.print(writer, level)?;
1✔
44
                write!(writer, "(")?;
1✔
45
                argument.print(writer, level)?;
1✔
46
                write!(writer, ")")
1✔
47
            }
48
            Expression::Argument => {
×
49
                write!(writer, "$arg")
1✔
50
            }
51
            Expression::Environment => {
×
52
                write!(writer, "$env")
2✔
53
            }
54
            Expression::Lambda { environment, body } => {
4✔
55
                write!(writer, "$env={{")?;
4✔
56
                let indented = level + 1;
4✔
57
                environment.print(writer, indented)?;
4✔
58
                writeln!(writer, "}}($arg) =>")?;
4✔
59
                for _ in 0..(indented * 2) {
4✔
60
                    write!(writer, " ")?;
10✔
61
                }
62
                body.print(writer, indented)
4✔
63
            }
64
            Expression::ConstructTree(arguments) => {
3✔
65
                write!(writer, "[")?;
3✔
66
                for argument in arguments {
13✔
67
                    argument.print(writer, level)?;
5✔
68
                    write!(writer, ", ")?;
5✔
69
                }
70
                write!(writer, "]")
3✔
71
            }
NEW
72
            Expression::GetChild { parent, index } => {
×
NEW
73
                parent.print(writer, level)?;
×
NEW
74
                write!(writer, ".{index}")
×
75
            }
76
        }
77
    }
78
}
79

80
impl<E, TreeLike> Expression<E, TreeLike>
81
where
82
    E: Clone + Display + PrintExpression,
83
    TreeLike: Clone + std::fmt::Debug,
84
{
85
    pub fn make_literal(value: TreeLike) -> Self {
16✔
86
        Expression::Literal(value)
16✔
87
    }
88

89
    pub fn make_apply(callee: E, argument: E) -> Self {
6✔
90
        Expression::Apply { callee, argument }
91
    }
92

93
    pub fn make_argument() -> Self {
23✔
94
        Expression::Argument
23✔
95
    }
96

97
    pub fn make_environment() -> Self {
4✔
98
        Expression::Environment
4✔
99
    }
100

101
    pub fn make_lambda(environment: E, body: E) -> Self {
15✔
102
        Expression::Lambda { environment, body }
103
    }
104

105
    pub fn make_construct_tree(arguments: Vec<E>) -> Self {
43✔
106
        Expression::ConstructTree(arguments)
43✔
107
    }
108

109
    pub fn make_get_child(parent: E, index: u16) -> Self {
20✔
110
        Expression::GetChild { parent, index }
111
    }
112

113
    pub async fn map_child_expressions<
110✔
114
        't,
115
        Expr: Clone + Display + PrintExpression,
116
        TreeLike2: Clone + std::fmt::Debug,
117
        Error,
118
        F,
119
        G,
120
    >(
121
        &self,
122
        transform_expression: &'t F,
123
        transform_tree: &'t G,
124
    ) -> Result<Expression<Expr, TreeLike2>, Error>
125
    where
126
        F: Fn(&E) -> Pin<Box<dyn Future<Output = Result<Expr, Error>> + 't>>,
127
        G: Fn(&TreeLike) -> Pin<Box<dyn Future<Output = Result<TreeLike2, Error>> + 't>>,
128
    {
129
        match self {
110✔
130
            Expression::Literal(value) => Ok(Expression::Literal(transform_tree(value).await?)),
30✔
131
            Expression::Apply { callee, argument } => Ok(Expression::Apply {
6✔
132
                callee: transform_expression(callee).await?,
6✔
133
                argument: transform_expression(argument).await?,
6✔
134
            }),
135
            Expression::Argument => Ok(Expression::Argument),
15✔
136
            Expression::Environment => Ok(Expression::Environment),
7✔
137
            Expression::Lambda { environment, body } => Ok(Expression::Lambda {
12✔
138
                environment: transform_expression(environment).await?,
12✔
139
                body: transform_expression(body).await?,
12✔
140
            }),
141
            Expression::ConstructTree(items) => {
28✔
142
                let mut transformed_items = Vec::new();
28✔
143
                for item in items.iter() {
68✔
144
                    transformed_items.push(transform_expression(item).await?);
120✔
145
                }
146
                Ok(Expression::ConstructTree(transformed_items))
28✔
147
            }
148
            Expression::GetChild { parent, index } => Ok(Expression::GetChild {
12✔
149
                parent: transform_expression(parent).await?,
12✔
150
                index: *index,
12✔
151
            }),
152
        }
153
    }
154
}
155

156
impl<E, V> Display for Expression<E, V>
157
where
158
    E: Clone + Display + PrintExpression,
159
    V: Clone + std::fmt::Debug,
160
{
161
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
162
        self.print(f, 0)
×
163
    }
164
}
165

166
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
167
pub struct DeepExpression(pub Expression<Arc<DeepExpression>, Tree>);
168

169
impl PrintExpression for DeepExpression {
170
    fn print(&self, writer: &mut dyn std::fmt::Write, level: usize) -> std::fmt::Result {
1✔
171
        self.0.print(writer, level)
1✔
172
    }
173
}
174

175
impl PrintExpression for Arc<DeepExpression> {
176
    fn print(&self, writer: &mut dyn std::fmt::Write, level: usize) -> std::fmt::Result {
15✔
177
        self.0.print(writer, level)
15✔
178
    }
179
}
180

181
impl Display for DeepExpression {
182
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
183
        write!(f, "{}", self.0)
×
184
    }
185
}
186

187
pub type ShallowExpression = Expression<BlobDigest, BlobDigest>;
188

189
impl PrintExpression for BlobDigest {
190
    fn print(&self, writer: &mut dyn std::fmt::Write, _level: usize) -> std::fmt::Result {
1✔
191
        write!(writer, "{self}")
1✔
192
    }
193
}
194

195
pub type ReferenceExpression = Expression<ReferenceIndex, ReferenceIndex>;
196

197
impl PrintExpression for ReferenceIndex {
198
    fn print(&self, writer: &mut dyn std::fmt::Write, _level: usize) -> std::fmt::Result {
1✔
199
        write!(writer, "{self}")
1✔
200
    }
201
}
202

203
pub fn to_reference_expression(
39✔
204
    expression: &ShallowExpression,
205
) -> (ReferenceExpression, Vec<BlobDigest>) {
206
    match expression {
39✔
207
        Expression::Literal(value) => (
10✔
208
            ReferenceExpression::Literal(ReferenceIndex(0)),
10✔
209
            vec![*value],
10✔
210
        ),
211
        Expression::Apply { callee, argument } => (
2✔
212
            ReferenceExpression::Apply {
2✔
213
                callee: ReferenceIndex(0),
2✔
214
                argument: ReferenceIndex(1),
2✔
215
            },
216
            // TODO: deduplicate?
217
            vec![*callee, *argument],
2✔
218
        ),
219
        Expression::Argument => (ReferenceExpression::Argument, vec![]),
6✔
220
        Expression::Environment => (ReferenceExpression::Environment, vec![]),
3✔
221
        Expression::Lambda { environment, body } => (
4✔
222
            ReferenceExpression::Lambda {
4✔
223
                environment: ReferenceIndex(0),
4✔
224
                body: ReferenceIndex(1),
4✔
225
            },
226
            vec![*environment, *body],
4✔
227
        ),
228
        Expression::ConstructTree(items) => (
10✔
229
            ReferenceExpression::ConstructTree(
10✔
230
                (0..items.len())
10✔
231
                    .map(|index| ReferenceIndex(index as u64))
24✔
232
                    .collect(),
10✔
233
            ),
234
            // TODO: deduplicate?
235
            items.clone(),
10✔
236
        ),
237
        Expression::GetChild { parent, index } => (
4✔
238
            ReferenceExpression::GetChild {
4✔
239
                parent: ReferenceIndex(0),
4✔
240
                index: *index,
4✔
241
            },
242
            vec![*parent],
4✔
243
        ),
244
    }
245
}
246

247
pub async fn deserialize_shallow(tree: &Tree) -> Result<ShallowExpression, ()> {
72✔
248
    let reference_expression: ReferenceExpression = postcard::from_bytes(tree.blob().as_slice())
36✔
249
        .unwrap(/*TODO*/);
250
    reference_expression
36✔
251
        .map_child_expressions(
252
            &|child: &ReferenceIndex| -> Pin<Box<dyn Future<Output = Result<BlobDigest, ()>>>> {
29✔
253
                let child = tree.references()[child.0 as usize];
29✔
254
                Box::pin(async move { Ok(child) })
58✔
255
            },
256
            &|child: &ReferenceIndex| -> Pin<Box<dyn Future<Output = Result<BlobDigest, ()>>>> {
10✔
257
                let child = tree.references()[child.0 as usize];
10✔
258
                Box::pin(async move { Ok(child) })
20✔
259
            },
260
        )
261
        .await
36✔
262
}
263

264
pub async fn deserialize_recursively(
36✔
265
    root: &BlobDigest,
266
    load_tree: &(dyn LoadTree + Sync),
267
) -> Result<DeepExpression, ()> {
268
    let root_loaded = load_tree.load_tree(root).await.unwrap(/*TODO*/).hash().unwrap(/*TODO*/);
108✔
269
    let shallow = deserialize_shallow(root_loaded.tree()).await?;
72✔
270
    let deep = shallow
36✔
271
        .map_child_expressions(
272
            &|child: &BlobDigest| -> Pin<Box<dyn Future<Output = Result<Arc<DeepExpression>, ()>>>> {
29✔
273
                let child = *child;
29✔
274
                Box::pin(async move { deserialize_recursively(&child, load_tree)
58✔
275
                    .await
29✔
276
                    .map(Arc::new) })
29✔
277
            },
278
            &|child: &BlobDigest| -> Pin<Box<dyn Future<Output = Result<Tree, ()>>>> {
10✔
279
                let child = *child;
10✔
280
                Box::pin(async move { Ok((**load_tree.load_tree(&child).await
20✔
281
                    .map(|tree| tree.hash().unwrap(/*TODO*/) ).unwrap(/*TODO*/).tree()).clone())})
20✔
282
            },
283
        )
284
        .await?;
×
285
    Ok(DeepExpression(deep))
×
286
}
287

288
pub fn expression_to_tree(expression: &ShallowExpression) -> Tree {
38✔
289
    let (reference_expression, references) = to_reference_expression(expression);
38✔
290
    let blob = postcard::to_allocvec(&reference_expression).unwrap(/*TODO*/);
38✔
291
    Tree::new(
292
        TreeBlob::try_from(bytes::Bytes::from_owner(blob)).unwrap(/*TODO*/),
38✔
293
        references,
38✔
294
    )
295
}
296

297
pub async fn serialize_shallow(
38✔
298
    expression: &ShallowExpression,
299
    storage: &(dyn StoreTree + Sync),
300
) -> std::result::Result<BlobDigest, StoreError> {
301
    let tree = expression_to_tree(expression);
38✔
302
    storage.store_tree(&HashedTree::from(Arc::new(tree))).await
38✔
303
}
304

305
pub async fn serialize_recursively(
38✔
306
    expression: &DeepExpression,
307
    storage: &(dyn StoreTree + Sync),
308
) -> std::result::Result<BlobDigest, StoreError> {
309
    let shallow_expression: ShallowExpression = expression
76✔
310
        .0
38✔
311
        .map_child_expressions(&|child: &Arc<DeepExpression>| -> Pin<
38✔
312
            Box<dyn Future<Output = Result<BlobDigest, StoreError>>>,
313
        > {
30✔
314
            let child = child.clone();
30✔
315
            Box::pin(async move {
60✔
316
                serialize_recursively(&child, storage)
30✔
317
                    .await
30✔
318
            })
319
        },&|child: &Tree| -> Pin<
30✔
320
        Box<dyn Future<Output = Result<BlobDigest, StoreError>>>,
321
        > {
10✔
322
            let child = child.clone();
10✔
323
            Box::pin(async move {
20✔
324
                storage.store_tree(&HashedTree::from(Arc::new(child))).await
10✔
325
            })
326
        })
327
        .await?;
38✔
328
    serialize_shallow(&shallow_expression, storage).await
×
329
}
330

331
#[derive(Debug)]
332
pub struct Closure {
333
    environment: BlobDigest,
334
    body: Arc<DeepExpression>,
335
}
336

337
#[derive(Debug, Serialize, Deserialize)]
338
pub struct ClosureBlob {}
339

340
impl Default for ClosureBlob {
341
    fn default() -> Self {
×
342
        Self::new()
×
343
    }
344
}
345

346
impl ClosureBlob {
347
    pub fn new() -> Self {
7✔
348
        Self {}
349
    }
350
}
351

352
impl Closure {
353
    pub fn new(environment: BlobDigest, body: Arc<DeepExpression>) -> Self {
13✔
354
        Self { environment, body }
355
    }
356

357
    pub async fn serialize(
7✔
358
        &self,
359
        store_tree: &(dyn StoreTree + Sync),
360
    ) -> Result<BlobDigest, StoreError> {
361
        let references = vec![
14✔
362
            self.environment,
7✔
363
            serialize_recursively(&self.body, store_tree).await?,
7✔
364
        ];
365
        let closure_blob = ClosureBlob::new();
×
366
        let closure_blob_bytes = postcard::to_allocvec(&closure_blob).unwrap(/*TODO*/);
×
367
        store_tree
×
368
            .store_tree(&HashedTree::from(Arc::new(Tree::new(
×
369
                TreeBlob::try_from(bytes::Bytes::from_owner(closure_blob_bytes)).unwrap(/*TODO*/),
×
370
                references,
×
371
            ))))
372
            .await
×
373
    }
374

375
    pub async fn deserialize(
6✔
376
        root: &BlobDigest,
377
        load_tree: &(dyn LoadTree + Sync),
378
    ) -> Result<Closure, TreeDeserializationError> {
379
        let loaded_root = match load_tree.load_tree(root).await {
12✔
380
            Some(success) => success,
6✔
381
            None => return Err(TreeDeserializationError::BlobUnavailable(*root)),
×
382
        };
383
        let root_tree = loaded_root.hash().unwrap(/*TODO*/).tree().clone();
6✔
384
        let _closure_blob: ClosureBlob = match postcard::from_bytes(root_tree.blob().as_slice()) {
12✔
385
            Ok(success) => success,
×
386
            Err(error) => return Err(TreeDeserializationError::Postcard(error)),
×
387
        };
388
        let environment_reference = &root_tree.references()[0];
×
389
        let body_reference = &root_tree.references()[1];
×
390
        let body = deserialize_recursively(body_reference, load_tree).await.unwrap(/*TODO*/);
12✔
391
        Ok(Closure::new(*environment_reference, Arc::new(body)))
6✔
392
    }
393
}
394

395
async fn call_method(
6✔
396
    body: &DeepExpression,
397
    argument: &BlobDigest,
398
    load_tree: &(dyn LoadTree + Sync),
399
    store_tree: &(dyn StoreTree + Sync),
400
) -> std::result::Result<BlobDigest, StoreError> {
401
    Box::pin(evaluate(body, load_tree, store_tree, &Some(*argument))).await
6✔
402
}
403

404
pub type ReadVariable =
405
    dyn Fn(&Name) -> Pin<Box<dyn core::future::Future<Output = BlobDigest> + Send>> + Send + Sync;
406

407
pub async fn apply_evaluated_argument(
6✔
408
    callee: &DeepExpression,
409
    evaluated_argument: &BlobDigest,
410
    load_tree: &(dyn LoadTree + Sync),
411
    store_tree: &(dyn StoreTree + Sync),
412
    current_lambda_argument: &Option<BlobDigest>,
413
) -> std::result::Result<BlobDigest, StoreError> {
414
    let evaluated_callee = Box::pin(evaluate(
12✔
415
        callee,
6✔
416
        load_tree,
6✔
417
        store_tree,
6✔
418
        current_lambda_argument,
6✔
419
    ))
420
    .await?;
6✔
421
    let closure = match Closure::deserialize(&evaluated_callee, load_tree).await {
6✔
422
        Ok(success) => success,
6✔
423
        Err(_) => todo!(),
424
    };
425
    call_method(&closure.body, evaluated_argument, load_tree, store_tree).await
6✔
426
}
427

428
pub async fn evaluate_apply(
4✔
429
    callee: &DeepExpression,
430
    argument: &DeepExpression,
431
    load_tree: &(dyn LoadTree + Sync),
432
    store_tree: &(dyn StoreTree + Sync),
433
    current_lambda_argument: &Option<BlobDigest>,
434
) -> std::result::Result<BlobDigest, StoreError> {
435
    let evaluated_argument = Box::pin(evaluate(
8✔
436
        argument,
4✔
437
        load_tree,
4✔
438
        store_tree,
4✔
439
        current_lambda_argument,
4✔
440
    ))
441
    .await?;
4✔
442
    apply_evaluated_argument(
443
        callee,
×
444
        &evaluated_argument,
×
445
        load_tree,
×
446
        store_tree,
×
447
        current_lambda_argument,
×
448
    )
449
    .await
×
450
}
451

452
pub async fn evaluate(
35✔
453
    expression: &DeepExpression,
454
    load_tree: &(dyn LoadTree + Sync),
455
    store_tree: &(dyn StoreTree + Sync),
456
    current_lambda_argument: &Option<BlobDigest>,
457
) -> std::result::Result<BlobDigest, StoreError> {
458
    match &expression.0 {
35✔
459
        Expression::Literal(literal_value) => {
12✔
460
            store_tree
12✔
461
                .store_tree(&HashedTree::from(Arc::new(literal_value.clone())))
12✔
462
                .await
12✔
463
        }
464
        Expression::Apply { callee, argument } => {
4✔
465
            evaluate_apply(
466
                callee,
4✔
467
                argument,
4✔
468
                load_tree,
4✔
469
                store_tree,
4✔
470
                current_lambda_argument,
4✔
471
            )
472
            .await
4✔
473
        }
474
        Expression::Argument => {
475
            if let Some(argument) = current_lambda_argument {
6✔
476
                Ok(*argument)
3✔
477
            } else {
478
                todo!("We are not in a lambda context; argument is not available")
479
            }
480
        }
481
        Expression::Environment => {
482
            todo!()
483
        }
484
        Expression::Lambda { environment, body } => {
7✔
485
            let evaluated_environment = Box::pin(evaluate(
14✔
486
                environment,
7✔
487
                load_tree,
7✔
488
                store_tree,
7✔
489
                current_lambda_argument,
7✔
490
            ))
491
            .await?;
7✔
492
            let closure = Closure::new(evaluated_environment, body.clone());
×
493
            let serialized = closure.serialize(store_tree).await?;
7✔
494
            Ok(serialized)
×
495
        }
496
        Expression::ConstructTree(arguments) => {
7✔
497
            let mut evaluated_arguments = Vec::new();
7✔
498
            for argument in arguments {
21✔
499
                let evaluated_argument = Box::pin(evaluate(
14✔
500
                    argument,
7✔
501
                    load_tree,
7✔
502
                    store_tree,
7✔
503
                    current_lambda_argument,
7✔
504
                ))
505
                .await?;
7✔
506
                evaluated_arguments.push(evaluated_argument);
×
507
            }
508
            store_tree
7✔
509
                .store_tree(&HashedTree::from(Arc::new(Tree::new(
7✔
510
                    TreeBlob::empty(),
7✔
511
                    evaluated_arguments,
7✔
512
                ))))
513
                .await
7✔
514
        }
515
        Expression::GetChild { parent, index } => {
2✔
516
            let evaluated_parent = Box::pin(evaluate(
4✔
517
                parent,
2✔
518
                load_tree,
2✔
519
                store_tree,
2✔
520
                current_lambda_argument,
2✔
521
            ))
522
            .await?;
2✔
523
            let loaded_parent = load_tree.load_tree(&evaluated_parent).await.unwrap(/*TODO*/);
4✔
524
            let hashed_tree = loaded_parent
2✔
525
                .hash()
526
                .unwrap(/*TODO*/);
527
            let child = hashed_tree
2✔
528
                .tree()
529
                .references()
530
                .get(*index as usize)
2✔
531
                .expect("TODO handle out of range error");
532
            Ok(*child)
2✔
533
        }
534
    }
535
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc