1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//! Language-neutral runtime policy kit for guest profiles.
use std::{fmt, sync::Arc};
use sim_kernel::{Cx, Result, Value};
/// How a guest language decides whether a runtime value is truthy.
pub trait TruthPolicy: Send + Sync {
/// Return whether `value` counts as truthy for this guest profile.
fn is_truthy(&self, cx: &mut Cx, value: &Value) -> Result<bool>;
}
/// How a guest language coerces values at number and string boundaries.
pub trait CoercionPolicy: Send + Sync {
/// Convert `value` to a number value when this profile accepts such a coercion.
fn to_number(&self, cx: &mut Cx, value: &Value) -> Result<Option<Value>>;
/// Convert `value` to a string value when this profile accepts such a coercion.
fn to_string(&self, cx: &mut Cx, value: &Value) -> Result<Option<Value>>;
}
/// Multivalue arity rule applied at a guest language boundary.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Arity {
/// Keep exactly this many values, padding with the profile nil value.
Exact(usize),
/// Keep one value, padding with the profile nil value when none were returned.
AtLeastOne,
/// Keep every returned value.
All,
}
/// Apply `rule` to returned values, using `nil` when padding is needed.
pub fn adjust_values(mut values: Vec<Value>, rule: Arity, nil: Value) -> Vec<Value> {
match rule {
Arity::All => values,
Arity::AtLeastOne => {
if values.is_empty() {
vec![nil]
} else {
values.truncate(1);
values
}
}
Arity::Exact(count) => {
values.resize(count, nil);
values
}
}
}
/// Runtime policy bundle a language profile configures once and reuses.
#[derive(Clone)]
pub struct GuestRuntimeKit {
/// Truthiness policy for this profile.
pub truth: Arc<dyn TruthPolicy>,
/// Boundary coercion policy for this profile.
pub coerce: Arc<dyn CoercionPolicy>,
/// Profile-specific nil value used for arity padding.
pub nil: Value,
}
impl GuestRuntimeKit {
/// Build a kit from truthiness, coercion, and nil policies.
pub fn new(truth: Arc<dyn TruthPolicy>, coerce: Arc<dyn CoercionPolicy>, nil: Value) -> Self {
Self { truth, coerce, nil }
}
/// Return whether `value` counts as truthy for this profile.
pub fn is_truthy(&self, cx: &mut Cx, value: &Value) -> Result<bool> {
self.truth.is_truthy(cx, value)
}
/// Convert `value` to a number value when this profile accepts such a coercion.
pub fn to_number(&self, cx: &mut Cx, value: &Value) -> Result<Option<Value>> {
self.coerce.to_number(cx, value)
}
/// Convert `value` to a string value when this profile accepts such a coercion.
pub fn to_string(&self, cx: &mut Cx, value: &Value) -> Result<Option<Value>> {
self.coerce.to_string(cx, value)
}
/// Apply an arity rule using this profile's nil value for padding.
pub fn adjust_values(&self, values: Vec<Value>, rule: Arity) -> Vec<Value> {
adjust_values(values, rule, self.nil.clone())
}
}
impl fmt::Debug for GuestRuntimeKit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GuestRuntimeKit")
.field("nil", &self.nil)
.finish_non_exhaustive()
}
}