sim_lib_sequence/
runtime.rs1use std::sync::Arc;
11
12use sim_kernel::{
13 AbiVersion, Args, Callable, ClassRef, Cx, Error, Export, Lib, LibManifest, LibTarget, Linker,
14 Object, ObjectCompat, Result, Symbol, Value, Version, force_list_to_vec,
15};
16
17const SEQUENCE_LIB_ID: &str = "sequence";
18
19pub fn manifest_name() -> Symbol {
21 Symbol::qualified("sim", SEQUENCE_LIB_ID)
22}
23
24#[derive(Clone, Copy)]
26pub enum SeqOp {
27 Map,
29 Filter,
31 Fold,
33}
34
35impl SeqOp {
36 pub const ALL: [SeqOp; 3] = [SeqOp::Map, SeqOp::Filter, SeqOp::Fold];
38
39 pub fn symbol(self) -> Symbol {
41 let name = match self {
42 SeqOp::Map => "map",
43 SeqOp::Filter => "filter",
44 SeqOp::Fold => "fold",
45 };
46 Symbol::qualified("seq", name)
47 }
48
49 fn arity(self) -> usize {
50 match self {
51 SeqOp::Map | SeqOp::Filter => 2,
52 SeqOp::Fold => 3,
53 }
54 }
55
56 fn elements(cx: &mut Cx, value: &Value, context: &'static str) -> Result<Vec<Value>> {
58 let object = value.object();
59 let Some(list) = object.as_list() else {
60 return Err(Error::TypeMismatch {
61 expected: "list",
62 found: context,
63 });
64 };
65 force_list_to_vec(cx, list, context)
66 }
67
68 fn run(self, cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
69 if args.len() != self.arity() {
70 return Err(Error::Eval(format!(
71 "{} expects {} argument(s), got {}",
72 self.symbol(),
73 self.arity(),
74 args.len()
75 )));
76 }
77 match self {
78 SeqOp::Map => {
79 let items = Self::elements(cx, &args[1], "seq/map list")?;
80 let func = args[0].clone();
81 let mut out = Vec::with_capacity(items.len());
82 for item in items {
83 out.push(cx.call_value(func.clone(), Args::new(vec![item]))?);
84 }
85 cx.factory().list(out)
86 }
87 SeqOp::Filter => {
88 let items = Self::elements(cx, &args[1], "seq/filter list")?;
89 let pred = args[0].clone();
90 let mut out = Vec::new();
91 for item in items {
92 let keep = cx
93 .call_value(pred.clone(), Args::new(vec![item.clone()]))?
94 .object()
95 .truth(cx)?;
96 if keep {
97 out.push(item);
98 }
99 }
100 cx.factory().list(out)
101 }
102 SeqOp::Fold => {
103 let items = Self::elements(cx, &args[2], "seq/fold list")?;
104 let func = args[0].clone();
105 let mut acc = args[1].clone();
106 for item in items {
107 acc = cx.call_value(func.clone(), Args::new(vec![acc, item]))?;
108 }
109 Ok(acc)
110 }
111 }
112 }
113}
114
115#[derive(Clone)]
117pub struct SequenceFunction {
118 op: SeqOp,
119}
120
121impl SequenceFunction {
122 pub fn new(op: SeqOp) -> Self {
124 Self { op }
125 }
126}
127
128impl Object for SequenceFunction {
129 fn display(&self, _cx: &mut Cx) -> Result<String> {
130 Ok(format!("#<function {}>", self.op.symbol()))
131 }
132
133 fn as_any(&self) -> &dyn std::any::Any {
134 self
135 }
136}
137
138impl ObjectCompat for SequenceFunction {
139 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
140 cx.resolve_class(&Symbol::qualified("core", "Function"))
141 }
142
143 fn as_callable(&self) -> Option<&dyn Callable> {
144 Some(self)
145 }
146}
147
148impl Callable for SequenceFunction {
149 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
150 self.op.run(cx, args.into_vec())
151 }
152}
153
154pub struct SequenceLib;
156
157impl Lib for SequenceLib {
158 fn manifest(&self) -> LibManifest {
159 LibManifest {
160 id: manifest_name(),
161 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
162 abi: AbiVersion { major: 0, minor: 1 },
163 target: LibTarget::HostRegistered,
164 requires: Vec::new(),
165 capabilities: Vec::new(),
166 exports: sequence_exports(),
167 }
168 }
169
170 fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
171 for op in SeqOp::ALL {
172 let function = SequenceFunction::new(op);
173 linker.function_value(op.symbol(), cx.factory().opaque(Arc::new(function))?)?;
174 }
175 Ok(())
176 }
177}
178
179pub fn sequence_exports() -> Vec<Export> {
181 SeqOp::ALL
182 .into_iter()
183 .map(|op| Export::Function {
184 symbol: op.symbol(),
185 function_id: None,
186 })
187 .collect()
188}
189
190pub fn install_sequence_lib(cx: &mut Cx) -> Result<()> {
192 if cx.registry().lib(&manifest_name()).is_some() {
193 return Ok(());
194 }
195 cx.load_lib(&SequenceLib)?;
196 Ok(())
197}