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

TyRoXx / NonlocalityOS / 15096156172

18 May 2025 12:55PM UTC coverage: 72.333% (+0.3%) from 72.054%
15096156172

Pull #245

github

web-flow
Merge 483a08bbc into 4e7b17925
Pull Request #245: GH-244: multiple parameters actually work

169 of 192 new or added lines in 7 files covered. (88.02%)

1 existing line in 1 file now uncovered.

3200 of 4424 relevant lines covered (72.33%)

2242.25 hits per line

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

86.03
/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
}
30

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

75
impl<E, TreeLike> Expression<E, TreeLike>
76
where
77
    E: Clone + Display + PrintExpression,
78
    TreeLike: Clone + std::fmt::Debug,
79
{
80
    pub fn make_literal(value: TreeLike) -> Self {
16✔
81
        Expression::Literal(value)
16✔
82
    }
83

84
    pub fn make_apply(callee: E, argument: E) -> Self {
6✔
85
        Expression::Apply { callee, argument }
86
    }
87

88
    pub fn make_argument() -> Self {
20✔
89
        Expression::Argument
20✔
90
    }
91

92
    pub fn make_environment() -> Self {
4✔
93
        Expression::Environment
4✔
94
    }
95

96
    pub fn make_lambda(environment: E, body: E) -> Self {
14✔
97
        Expression::Lambda { environment, body }
98
    }
99

100
    pub fn make_construct_tree(arguments: Vec<E>) -> Self {
41✔
101
        Expression::ConstructTree(arguments)
41✔
102
    }
103

104
    pub async fn map_child_expressions<
86✔
105
        't,
106
        Expr: Clone + Display + PrintExpression,
107
        TreeLike2: Clone + std::fmt::Debug,
108
        Error,
109
        F,
110
        G,
111
    >(
112
        &self,
113
        transform_expression: &'t F,
114
        transform_tree: &'t G,
115
    ) -> Result<Expression<Expr, TreeLike2>, Error>
116
    where
117
        F: Fn(&E) -> Pin<Box<dyn Future<Output = Result<Expr, Error>> + 't>>,
118
        G: Fn(&TreeLike) -> Pin<Box<dyn Future<Output = Result<TreeLike2, Error>> + 't>>,
119
    {
120
        match self {
86✔
121
            Expression::Literal(value) => Ok(Expression::Literal(transform_tree(value).await?)),
30✔
122
            Expression::Apply { callee, argument } => Ok(Expression::Apply {
6✔
123
                callee: transform_expression(callee).await?,
6✔
124
                argument: transform_expression(argument).await?,
6✔
125
            }),
126
            Expression::Argument => Ok(Expression::Argument),
9✔
127
            Expression::Environment => Ok(Expression::Environment),
7✔
128
            Expression::Lambda { environment, body } => Ok(Expression::Lambda {
12✔
129
                environment: transform_expression(environment).await?,
12✔
130
                body: transform_expression(body).await?,
12✔
131
            }),
132
            Expression::ConstructTree(items) => {
22✔
133
                let mut transformed_items = Vec::new();
22✔
134
                for item in items.iter() {
50✔
135
                    transformed_items.push(transform_expression(item).await?);
84✔
136
                }
137
                Ok(Expression::ConstructTree(transformed_items))
22✔
138
            }
139
        }
140
    }
141
}
142

143
impl<E, V> Display for Expression<E, V>
144
where
145
    E: Clone + Display + PrintExpression,
146
    V: Clone + std::fmt::Debug,
147
{
148
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
149
        self.print(f, 0)
×
150
    }
151
}
152

153
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
154
pub struct DeepExpression(pub Expression<Arc<DeepExpression>, Tree>);
155

156
impl PrintExpression for DeepExpression {
157
    fn print(&self, writer: &mut dyn std::fmt::Write, level: usize) -> std::fmt::Result {
1✔
158
        self.0.print(writer, level)
1✔
159
    }
160
}
161

162
impl PrintExpression for Arc<DeepExpression> {
163
    fn print(&self, writer: &mut dyn std::fmt::Write, level: usize) -> std::fmt::Result {
15✔
164
        self.0.print(writer, level)
15✔
165
    }
166
}
167

168
impl Display for DeepExpression {
169
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
170
        write!(f, "{}", self.0)
×
171
    }
172
}
173

174
pub type ShallowExpression = Expression<BlobDigest, BlobDigest>;
175

176
impl PrintExpression for BlobDigest {
177
    fn print(&self, writer: &mut dyn std::fmt::Write, _level: usize) -> std::fmt::Result {
1✔
178
        write!(writer, "{self}")
1✔
179
    }
180
}
181

182
pub type ReferenceExpression = Expression<ReferenceIndex, ReferenceIndex>;
183

184
impl PrintExpression for ReferenceIndex {
185
    fn print(&self, writer: &mut dyn std::fmt::Write, _level: usize) -> std::fmt::Result {
1✔
186
        write!(writer, "{self}")
1✔
187
    }
188
}
189

190
pub fn to_reference_expression(
31✔
191
    expression: &ShallowExpression,
192
) -> (ReferenceExpression, Vec<BlobDigest>) {
193
    match expression {
31✔
194
        Expression::Literal(value) => (
10✔
195
            ReferenceExpression::Literal(ReferenceIndex(0)),
10✔
196
            vec![*value],
10✔
197
        ),
198
        Expression::Apply { callee, argument } => (
2✔
199
            ReferenceExpression::Apply {
2✔
200
                callee: ReferenceIndex(0),
2✔
201
                argument: ReferenceIndex(1),
2✔
202
            },
203
            // TODO: deduplicate?
204
            vec![*callee, *argument],
2✔
205
        ),
206
        Expression::Argument => (ReferenceExpression::Argument, vec![]),
4✔
207
        Expression::Environment => (ReferenceExpression::Environment, vec![]),
3✔
208
        Expression::Lambda { environment, body } => (
4✔
209
            ReferenceExpression::Lambda {
4✔
210
                environment: ReferenceIndex(0),
4✔
211
                body: ReferenceIndex(1),
4✔
212
            },
213
            vec![*environment, *body],
4✔
214
        ),
215
        Expression::ConstructTree(items) => (
8✔
216
            ReferenceExpression::ConstructTree(
8✔
217
                (0..items.len())
8✔
218
                    .map(|index| ReferenceIndex(index as u64))
18✔
219
                    .collect(),
8✔
220
            ),
221
            // TODO: deduplicate?
222
            items.clone(),
8✔
223
        ),
224
    }
225
}
226

227
pub async fn deserialize_shallow(tree: &Tree) -> Result<ShallowExpression, ()> {
56✔
228
    let reference_expression: ReferenceExpression = postcard::from_bytes(tree.blob().as_slice())
28✔
229
        .unwrap(/*TODO*/);
230
    reference_expression
28✔
231
        .map_child_expressions(
232
            &|child: &ReferenceIndex| -> Pin<Box<dyn Future<Output = Result<BlobDigest, ()>>>> {
21✔
233
                let child = tree.references()[child.0 as usize];
21✔
234
                Box::pin(async move { Ok(child) })
42✔
235
            },
236
            &|child: &ReferenceIndex| -> Pin<Box<dyn Future<Output = Result<BlobDigest, ()>>>> {
10✔
237
                let child = tree.references()[child.0 as usize];
10✔
238
                Box::pin(async move { Ok(child) })
20✔
239
            },
240
        )
241
        .await
28✔
242
}
243

244
pub async fn deserialize_recursively(
28✔
245
    root: &BlobDigest,
246
    load_tree: &(dyn LoadTree + Sync),
247
) -> Result<DeepExpression, ()> {
248
    let root_loaded = load_tree.load_tree(root).await.unwrap(/*TODO*/).hash().unwrap(/*TODO*/);
84✔
249
    let shallow = deserialize_shallow(root_loaded.tree()).await?;
56✔
250
    let deep = shallow
28✔
251
        .map_child_expressions(
252
            &|child: &BlobDigest| -> Pin<Box<dyn Future<Output = Result<Arc<DeepExpression>, ()>>>> {
21✔
253
                let child = *child;
21✔
254
                Box::pin(async move { deserialize_recursively(&child, load_tree)
42✔
255
                    .await
21✔
256
                    .map(Arc::new) })
21✔
257
            },
258
            &|child: &BlobDigest| -> Pin<Box<dyn Future<Output = Result<Tree, ()>>>> {
10✔
259
                let child = *child;
10✔
260
                Box::pin(async move { Ok((**load_tree.load_tree(&child).await
20✔
261
                    .map(|tree| tree.hash().unwrap(/*TODO*/) ).unwrap(/*TODO*/).tree()).clone())})
20✔
262
            },
263
        )
264
        .await?;
×
265
    Ok(DeepExpression(deep))
×
266
}
267

268
pub fn expression_to_tree(expression: &ShallowExpression) -> Tree {
30✔
269
    let (reference_expression, references) = to_reference_expression(expression);
30✔
270
    let blob = postcard::to_allocvec(&reference_expression).unwrap(/*TODO*/);
30✔
271
    Tree::new(
272
        TreeBlob::try_from(bytes::Bytes::from_owner(blob)).unwrap(/*TODO*/),
30✔
273
        references,
30✔
274
    )
275
}
276

277
pub async fn serialize_shallow(
30✔
278
    expression: &ShallowExpression,
279
    storage: &(dyn StoreTree + Sync),
280
) -> std::result::Result<BlobDigest, StoreError> {
281
    let tree = expression_to_tree(expression);
30✔
282
    storage.store_tree(&HashedTree::from(Arc::new(tree))).await
30✔
283
}
284

285
pub async fn serialize_recursively(
30✔
286
    expression: &DeepExpression,
287
    storage: &(dyn StoreTree + Sync),
288
) -> std::result::Result<BlobDigest, StoreError> {
289
    let shallow_expression: ShallowExpression = expression
60✔
290
        .0
30✔
291
        .map_child_expressions(&|child: &Arc<DeepExpression>| -> Pin<
30✔
292
            Box<dyn Future<Output = Result<BlobDigest, StoreError>>>,
293
        > {
22✔
294
            let child = child.clone();
22✔
295
            Box::pin(async move {
44✔
296
                serialize_recursively(&child, storage)
22✔
297
                    .await
22✔
298
            })
299
        },&|child: &Tree| -> Pin<
22✔
300
        Box<dyn Future<Output = Result<BlobDigest, StoreError>>>,
301
        > {
10✔
302
            let child = child.clone();
10✔
303
            Box::pin(async move {
20✔
304
                storage.store_tree(&HashedTree::from(Arc::new(child))).await
10✔
305
            })
306
        })
307
        .await?;
30✔
308
    serialize_shallow(&shallow_expression, storage).await
×
309
}
310

311
#[derive(Debug)]
312
pub struct Closure {
313
    environment: BlobDigest,
314
    body: Arc<DeepExpression>,
315
}
316

317
#[derive(Debug, Serialize, Deserialize)]
318
pub struct ClosureBlob {}
319

320
impl Default for ClosureBlob {
NEW
321
    fn default() -> Self {
×
NEW
322
        Self::new()
×
323
    }
324
}
325

326
impl ClosureBlob {
327
    pub fn new() -> Self {
7✔
328
        Self {}
329
    }
330
}
331

332
impl Closure {
333
    pub fn new(environment: BlobDigest, body: Arc<DeepExpression>) -> Self {
13✔
334
        Self { environment, body }
335
    }
336

337
    pub async fn serialize(
7✔
338
        &self,
339
        store_tree: &(dyn StoreTree + Sync),
340
    ) -> Result<BlobDigest, StoreError> {
341
        let references = vec![
14✔
342
            self.environment,
7✔
343
            serialize_recursively(&self.body, store_tree).await?,
7✔
344
        ];
NEW
345
        let closure_blob = ClosureBlob::new();
×
346
        let closure_blob_bytes = postcard::to_allocvec(&closure_blob).unwrap(/*TODO*/);
×
347
        store_tree
×
348
            .store_tree(&HashedTree::from(Arc::new(Tree::new(
×
349
                TreeBlob::try_from(bytes::Bytes::from_owner(closure_blob_bytes)).unwrap(/*TODO*/),
×
350
                references,
×
351
            ))))
352
            .await
×
353
    }
354

355
    pub async fn deserialize(
6✔
356
        root: &BlobDigest,
357
        load_tree: &(dyn LoadTree + Sync),
358
    ) -> Result<Closure, TreeDeserializationError> {
359
        let loaded_root = match load_tree.load_tree(root).await {
12✔
360
            Some(success) => success,
6✔
361
            None => return Err(TreeDeserializationError::BlobUnavailable(*root)),
×
362
        };
363
        let root_tree = loaded_root.hash().unwrap(/*TODO*/).tree().clone();
6✔
364
        let _closure_blob: ClosureBlob = match postcard::from_bytes(root_tree.blob().as_slice()) {
12✔
365
            Ok(success) => success,
×
366
            Err(error) => return Err(TreeDeserializationError::Postcard(error)),
×
367
        };
NEW
368
        let environment_reference = &root_tree.references()[0];
×
NEW
369
        let body_reference = &root_tree.references()[1];
×
370
        let body = deserialize_recursively(body_reference, load_tree).await.unwrap(/*TODO*/);
12✔
371
        Ok(Closure::new(*environment_reference, Arc::new(body)))
6✔
372
    }
373
}
374

375
async fn call_method(
6✔
376
    body: &DeepExpression,
377
    argument: &BlobDigest,
378
    load_tree: &(dyn LoadTree + Sync),
379
    store_tree: &(dyn StoreTree + Sync),
380
) -> std::result::Result<BlobDigest, StoreError> {
381
    Box::pin(evaluate(body, load_tree, store_tree, &Some(*argument))).await
6✔
382
}
383

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

387
pub async fn apply_evaluated_argument(
6✔
388
    callee: &DeepExpression,
389
    evaluated_argument: &BlobDigest,
390
    load_tree: &(dyn LoadTree + Sync),
391
    store_tree: &(dyn StoreTree + Sync),
392
    current_lambda_argument: &Option<BlobDigest>,
393
) -> std::result::Result<BlobDigest, StoreError> {
394
    let evaluated_callee = Box::pin(evaluate(
12✔
395
        callee,
6✔
396
        load_tree,
6✔
397
        store_tree,
6✔
398
        current_lambda_argument,
6✔
399
    ))
400
    .await?;
6✔
401
    let closure = match Closure::deserialize(&evaluated_callee, load_tree).await {
6✔
402
        Ok(success) => success,
6✔
403
        Err(_) => todo!(),
404
    };
405
    call_method(&closure.body, evaluated_argument, load_tree, store_tree).await
6✔
406
}
407

408
pub async fn evaluate_apply(
4✔
409
    callee: &DeepExpression,
410
    argument: &DeepExpression,
411
    load_tree: &(dyn LoadTree + Sync),
412
    store_tree: &(dyn StoreTree + Sync),
413
    current_lambda_argument: &Option<BlobDigest>,
414
) -> std::result::Result<BlobDigest, StoreError> {
415
    let evaluated_argument = Box::pin(evaluate(
8✔
416
        argument,
4✔
417
        load_tree,
4✔
418
        store_tree,
4✔
419
        current_lambda_argument,
4✔
420
    ))
421
    .await?;
4✔
422
    apply_evaluated_argument(
423
        callee,
×
424
        &evaluated_argument,
×
425
        load_tree,
×
426
        store_tree,
×
NEW
427
        current_lambda_argument,
×
428
    )
429
    .await
×
430
}
431

432
pub async fn evaluate(
31✔
433
    expression: &DeepExpression,
434
    load_tree: &(dyn LoadTree + Sync),
435
    store_tree: &(dyn StoreTree + Sync),
436
    current_lambda_argument: &Option<BlobDigest>,
437
) -> std::result::Result<BlobDigest, StoreError> {
438
    match &expression.0 {
31✔
439
        Expression::Literal(literal_value) => {
12✔
440
            store_tree
12✔
441
                .store_tree(&HashedTree::from(Arc::new(literal_value.clone())))
12✔
442
                .await
12✔
443
        }
444
        Expression::Apply { callee, argument } => {
4✔
445
            evaluate_apply(
446
                callee,
4✔
447
                argument,
4✔
448
                load_tree,
4✔
449
                store_tree,
4✔
450
                current_lambda_argument,
4✔
451
            )
452
            .await
4✔
453
        }
454
        Expression::Argument => {
455
            if let Some(argument) = current_lambda_argument {
4✔
456
                Ok(*argument)
2✔
457
            } else {
458
                todo!("We are not in a lambda context; argument is not available")
459
            }
460
        }
461
        Expression::Environment => {
462
            todo!()
463
        }
464
        Expression::Lambda { environment, body } => {
7✔
465
            let evaluated_environment = Box::pin(evaluate(
14✔
466
                environment,
7✔
467
                load_tree,
7✔
468
                store_tree,
7✔
469
                current_lambda_argument,
7✔
470
            ))
471
            .await?;
7✔
NEW
472
            let closure = Closure::new(evaluated_environment, body.clone());
×
473
            let serialized = closure.serialize(store_tree).await?;
7✔
474
            Ok(serialized)
×
475
        }
476
        Expression::ConstructTree(arguments) => {
6✔
477
            let mut evaluated_arguments = Vec::new();
6✔
478
            for argument in arguments {
16✔
479
                let evaluated_argument = Box::pin(evaluate(
10✔
480
                    argument,
5✔
481
                    load_tree,
5✔
482
                    store_tree,
5✔
483
                    current_lambda_argument,
5✔
484
                ))
485
                .await?;
5✔
UNCOV
486
                evaluated_arguments.push(evaluated_argument);
×
487
            }
488
            store_tree
6✔
489
                .store_tree(&HashedTree::from(Arc::new(Tree::new(
6✔
490
                    TreeBlob::empty(),
6✔
491
                    evaluated_arguments,
6✔
492
                ))))
493
                .await
6✔
494
        }
495
    }
496
}
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