1use std::sync::Arc;
2
3mod helpers;
4
5use sim_kernel::{
6 AbiVersion, Args, Callable, ClassRef, Cx, Error, Export, Expr, Lib, LibManifest, LibTarget,
7 Linker, Object, ObjectCompat, RawArgs, Result, Symbol, Value, Version,
8};
9use sim_lib_stream_combinators::{filter_data_kind, window_by_count};
10use sim_lib_stream_core::{
11 StreamPacket, install_stream_core_classes, install_stream_core_shapes_lib,
12 stream_cancel_symbol, stream_metadata_symbol, stream_next_symbol, stream_run_symbol,
13 stream_stats_symbol,
14};
15
16use crate::{
17 cap::{
18 stream_open_capability, stream_read_capability, stream_transform_capability,
19 stream_write_capability,
20 },
21 card::{stats_value, stream_card},
22 handle::{StageHandle, StreamHandle},
23 live::StreamRuntime,
24 live_control::{
25 cancel_older_than_fn, cell_fn, cell_set_fn, cell_value_fn, describe_fn,
26 explain_diagnostic_fn, graph_lisp_fn, list_fn, reroute_fn,
27 },
28 spec::{memory_specs_value, open_spec_from_expr},
29};
30
31use helpers::{
32 collect_stream_to_handle, data_expr, ensure_done, eval_value, handle_arg, handle_stream,
33 handle_value_from_items, map_data_payload, run_report_value, symbol_arg, usize_arg,
34};
35
36const STREAM_PRELUDE_LIB_ID: &str = "stream-prelude";
37
38pub struct StreamPreludeLib;
46
47impl Lib for StreamPreludeLib {
48 fn manifest(&self) -> LibManifest {
49 LibManifest {
50 id: manifest_name(),
51 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
52 abi: AbiVersion { major: 0, minor: 1 },
53 target: LibTarget::HostRegistered,
54 requires: Vec::new(),
55 capabilities: Vec::new(),
56 exports: stream_prelude_exports(),
57 }
58 }
59
60 fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
61 let runtime = Arc::new(StreamRuntime::default());
62 for (symbol, implementation) in function_table() {
63 linker.function_value(
64 symbol.clone(),
65 cx.factory().opaque(Arc::new(StreamFunction {
66 symbol,
67 implementation,
68 runtime: Arc::clone(&runtime),
69 }))?,
70 )?;
71 }
72 linker.value(stream_memory_specs_symbol(), memory_specs_value(cx)?)?;
73 Ok(())
74 }
75}
76
77pub fn install_stream_prelude_lib(cx: &mut Cx) -> Result<()> {
84 install_stream_core_classes(cx)?;
85 install_stream_core_shapes_lib(cx)?;
86 sim_lib_core::install_once(cx, &StreamPreludeLib).map(|_| ())
87}
88
89pub fn stream_prelude_exports() -> Vec<Export> {
94 let mut exports = vec![Export::Value {
95 symbol: stream_memory_specs_symbol(),
96 }];
97 exports.extend(
98 function_table()
99 .into_iter()
100 .map(|(symbol, _)| Export::Function {
101 symbol,
102 function_id: None,
103 }),
104 );
105 exports
106}
107
108pub fn manifest_name() -> Symbol {
110 Symbol::new(STREAM_PRELUDE_LIB_ID)
111}
112
113pub fn stream_open_symbol() -> Symbol {
125 Symbol::qualified("stream", "open")
126}
127
128pub fn stream_write_symbol() -> Symbol {
132 Symbol::qualified("stream", "write!")
133}
134
135pub fn stream_pipe_symbol() -> Symbol {
140 Symbol::qualified("stream", "pipe")
141}
142
143pub fn stream_card_symbol() -> Symbol {
147 Symbol::qualified("stream", "card")
148}
149
150pub fn stream_sink_packets_symbol() -> Symbol {
154 Symbol::qualified("stream", "sink-packets")
155}
156
157pub fn stream_memory_specs_symbol() -> Symbol {
162 Symbol::qualified("stream", "memory-specs")
163}
164
165fn stream_identity_symbol() -> Symbol {
166 Symbol::qualified("stream", "identity")
167}
168
169fn stream_list_symbol() -> Symbol {
170 Symbol::qualified("stream", "list")
171}
172
173fn stream_describe_symbol() -> Symbol {
174 Symbol::qualified("stream", "describe")
175}
176
177fn stream_graph_lisp_symbol() -> Symbol {
178 Symbol::qualified("stream", "graph-lisp")
179}
180
181fn stream_explain_diagnostic_symbol() -> Symbol {
182 Symbol::qualified("stream", "explain-diagnostic")
183}
184
185fn stream_cell_symbol() -> Symbol {
186 Symbol::qualified("stream", "cell")
187}
188
189fn stream_cell_value_symbol() -> Symbol {
190 Symbol::qualified("stream", "cell-value")
191}
192
193fn stream_cell_set_symbol() -> Symbol {
194 Symbol::qualified("stream", "cell-set!")
195}
196
197fn stream_reroute_symbol() -> Symbol {
198 Symbol::qualified("stream", "reroute!")
199}
200
201fn stream_cancel_older_than_symbol() -> Symbol {
202 Symbol::qualified("stream", "cancel-older-than!")
203}
204
205fn stream_filter_kind_symbol() -> Symbol {
206 Symbol::qualified("stream", "filter-kind")
207}
208
209fn stream_filter_shape_symbol() -> Symbol {
210 Symbol::qualified("stream", "filter-shape")
211}
212
213fn stream_map_expr_symbol() -> Symbol {
214 Symbol::qualified("stream", "map-expr")
215}
216
217fn stream_window_symbol() -> Symbol {
218 Symbol::qualified("stream", "window")
219}
220
221fn function_table() -> Vec<(Symbol, StreamFn)> {
222 vec![
223 (stream_open_symbol(), open_fn),
224 (stream_next_symbol(), next_fn),
225 (stream_write_symbol(), write_fn),
226 (stream_pipe_symbol(), pipe_fn),
227 (stream_identity_symbol(), identity_fn),
228 (stream_run_symbol(), run_fn),
229 (stream_cancel_symbol(), cancel_fn),
230 (stream_stats_symbol(), stats_fn),
231 (stream_metadata_symbol(), metadata_fn),
232 (stream_card_symbol(), card_fn),
233 (stream_sink_packets_symbol(), sink_packets_fn),
234 (stream_list_symbol(), list_fn),
235 (stream_describe_symbol(), describe_fn),
236 (stream_graph_lisp_symbol(), graph_lisp_fn),
237 (stream_explain_diagnostic_symbol(), explain_diagnostic_fn),
238 (stream_cell_symbol(), cell_fn),
239 (stream_cell_value_symbol(), cell_value_fn),
240 (stream_cell_set_symbol(), cell_set_fn),
241 (stream_reroute_symbol(), reroute_fn),
242 (stream_cancel_older_than_symbol(), cancel_older_than_fn),
243 (stream_filter_kind_symbol(), filter_kind_fn),
244 (stream_filter_shape_symbol(), filter_shape_fn),
245 (stream_map_expr_symbol(), map_expr_fn),
246 (stream_window_symbol(), window_fn),
247 ]
248}
249
250type StreamFn = fn(&StreamRuntime, &mut Cx, &[Expr]) -> Result<Value>;
251
252struct StreamFunction {
253 symbol: Symbol,
254 implementation: StreamFn,
255 runtime: Arc<StreamRuntime>,
256}
257
258impl Object for StreamFunction {
259 fn display(&self, _cx: &mut Cx) -> Result<String> {
260 Ok(format!("#<function {}>", self.symbol))
261 }
262
263 fn as_any(&self) -> &dyn std::any::Any {
264 self
265 }
266}
267
268impl ObjectCompat for StreamFunction {
269 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
270 cx.factory().class_stub(
271 sim_kernel::CORE_FUNCTION_CLASS_ID,
272 Symbol::qualified("core", "Function"),
273 )
274 }
275
276 fn as_callable(&self) -> Option<&dyn Callable> {
277 Some(self)
278 }
279}
280
281impl Callable for StreamFunction {
282 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
283 let exprs = args
284 .into_vec()
285 .into_iter()
286 .map(|value| value.object().as_expr(cx))
287 .collect::<Result<Vec<_>>>()?;
288 (self.implementation)(&self.runtime, cx, &exprs)
289 }
290
291 fn call_exprs(&self, cx: &mut Cx, args: RawArgs) -> Result<Value> {
292 (self.implementation)(&self.runtime, cx, args.exprs())
293 }
294}
295
296fn open_fn(runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
297 cx.require(&stream_open_capability())?;
298 let [spec] = args else {
299 return Err(Error::Eval(
300 "stream/open expects one memory spec".to_owned(),
301 ));
302 };
303 let spec = open_spec_from_expr(data_expr(cx, spec)?)?;
304 let handle = spec.into_handle(cx)?;
305 runtime.register_stream(handle.clone())?;
306 cx.factory().opaque(Arc::new(handle))
307}
308
309fn next_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
310 cx.require(&stream_read_capability())?;
311 let [stream] = args else {
312 return Err(Error::Eval(
313 "stream/next! expects one stream handle".to_owned(),
314 ));
315 };
316 match handle_arg(cx, stream)?.next_packet()? {
317 Some(item) => cx.factory().expr(item.packet().to_expr()),
318 None => cx.factory().nil(),
319 }
320}
321
322fn write_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
323 cx.require(&stream_write_capability())?;
324 let [sink, packet] = args else {
325 return Err(Error::Eval(
326 "stream/write! expects a sink handle and packet".to_owned(),
327 ));
328 };
329 let sink = handle_arg(cx, sink)?;
330 let packet = StreamPacket::try_from(data_expr(cx, packet)?)?;
331 sink.write_packet(packet)?;
332 cx.factory().bool(true)
333}
334
335fn pipe_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
336 let [source, rest @ ..] = args else {
337 return Err(Error::Eval(
338 "stream/pipe expects a source handle".to_owned(),
339 ));
340 };
341 let source = handle_arg(cx, source)?;
342 let mut sink = None;
343 for expr in rest {
344 let value = eval_value(cx, expr)?;
345 if let Some(stage) = value.object().downcast_ref::<StageHandle>() {
346 if !stage.is_identity() {
347 return Err(Error::Eval("unsupported stream stage".to_owned()));
348 }
349 continue;
350 }
351 let handle = value
352 .object()
353 .downcast_ref::<StreamHandle>()
354 .cloned()
355 .ok_or(Error::TypeMismatch {
356 expected: "stream stage or sink handle",
357 found: "non-stream-pipeline-argument",
358 })?;
359 if sink.replace(handle).is_some() {
360 return Err(Error::Eval(
361 "stream/pipe accepts at most one sink handle in STR6.8".to_owned(),
362 ));
363 }
364 }
365 cx.factory()
366 .opaque(Arc::new(StreamHandle::pipeline(source, sink)?))
367}
368
369fn identity_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
370 if !args.is_empty() {
371 return Err(Error::Eval(
372 "stream/identity expects no arguments".to_owned(),
373 ));
374 }
375 cx.factory().opaque(Arc::new(StageHandle::identity()))
376}
377
378fn run_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
379 let [stream] = args else {
380 return Err(Error::Eval(
381 "stream/run! expects one stream handle".to_owned(),
382 ));
383 };
384 let stream = handle_arg(cx, stream)?;
385 cx.require(&stream_read_capability())?;
386 if stream.is_pipeline_with_sink() {
387 cx.require(&stream_write_capability())?;
388 }
389 run_report_value(cx, stream.run()?)
390}
391
392fn cancel_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
393 let [stream] = args else {
394 return Err(Error::Eval(
395 "stream/cancel! expects one stream handle".to_owned(),
396 ));
397 };
398 handle_arg(cx, stream)?.cancel()?;
399 cx.factory().nil()
400}
401
402fn stats_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
403 let [stream] = args else {
404 return Err(Error::Eval(
405 "stream/stats expects one stream handle".to_owned(),
406 ));
407 };
408 let handle = handle_arg(cx, stream)?;
409 let stats = handle.stats()?;
410 stats_value(cx, &stats)
411}
412
413fn metadata_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
414 let [stream] = args else {
415 return Err(Error::Eval(
416 "stream/metadata expects one stream handle".to_owned(),
417 ));
418 };
419 let handle = handle_arg(cx, stream)?;
420 cx.factory().expr(handle.metadata().table_expr())
421}
422
423fn card_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
424 let [stream] = args else {
425 return Err(Error::Eval(
426 "stream/card expects one stream handle".to_owned(),
427 ));
428 };
429 let handle = handle_arg(cx, stream)?;
430 stream_card(cx, &handle)
431}
432
433fn sink_packets_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
434 cx.require(&stream_read_capability())?;
435 let [sink] = args else {
436 return Err(Error::Eval(
437 "stream/sink-packets expects one sink handle".to_owned(),
438 ));
439 };
440 let packets = handle_arg(cx, sink)?.sink_packets()?;
441 cx.factory().list(
442 packets
443 .into_iter()
444 .map(|packet| cx.factory().expr(packet.to_expr()))
445 .collect::<Result<Vec<_>>>()?,
446 )
447}
448
449fn filter_kind_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
450 cx.require(&stream_read_capability())?;
451 let [stream, kind] = args else {
452 return Err(Error::Eval(
453 "stream/filter-kind expects a stream handle and data kind".to_owned(),
454 ));
455 };
456 let source = handle_arg(cx, stream)?;
457 let kind = symbol_arg(cx, kind)?;
458 let transformed = filter_data_kind(handle_stream(source), kind);
459 collect_stream_to_handle(cx, transformed)
460}
461
462fn filter_shape_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
463 cx.require(&stream_read_capability())?;
464 cx.require(&stream_transform_capability())?;
465 let [stream, shape] = args else {
466 return Err(Error::Eval(
467 "stream/filter-shape expects a stream handle and shape".to_owned(),
468 ));
469 };
470 let source = handle_arg(cx, stream)?;
471 let shape = eval_value(cx, shape)?;
472 if shape.object().as_shape().is_none() {
473 return Err(Error::TypeMismatch {
474 expected: "shape",
475 found: "non-shape",
476 });
477 }
478 let metadata = source.metadata().clone();
479 let mut items = Vec::new();
480 while let Some(item) = source.next_packet()? {
481 let StreamPacket::Data(packet) = item.packet() else {
482 continue;
483 };
484 let shape_ref = shape.object().as_shape().ok_or(Error::TypeMismatch {
485 expected: "shape",
486 found: "non-shape",
487 })?;
488 if shape_ref.check_expr(cx, &packet.payload)?.accepted {
489 items.push(item);
490 }
491 }
492 ensure_done(&source, "stream/filter-shape")?;
493 handle_value_from_items(cx, metadata, items)
494}
495
496fn map_expr_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
497 cx.require(&stream_read_capability())?;
498 cx.require(&stream_transform_capability())?;
499 let [stream, mapper] = args else {
500 return Err(Error::Eval(
501 "stream/map-expr expects a stream handle and callable".to_owned(),
502 ));
503 };
504 let source = handle_arg(cx, stream)?;
505 let mapper = eval_value(cx, mapper)?;
506 if mapper.object().as_callable().is_none() {
507 return Err(Error::TypeMismatch {
508 expected: "callable",
509 found: "non-callable",
510 });
511 }
512 let metadata = source.metadata().clone();
513 let mut items = Vec::new();
514 while let Some(item) = source.next_packet()? {
515 items.push(map_data_payload(cx, item, mapper.clone())?);
516 }
517 ensure_done(&source, "stream/map-expr")?;
518 handle_value_from_items(cx, metadata, items)
519}
520
521fn window_fn(_runtime: &StreamRuntime, cx: &mut Cx, args: &[Expr]) -> Result<Value> {
522 cx.require(&stream_read_capability())?;
523 let [stream, count] = args else {
524 return Err(Error::Eval(
525 "stream/window expects a stream handle and count".to_owned(),
526 ));
527 };
528 let count = usize_arg(cx, count)?;
529 if count == 0 {
530 return Err(Error::Eval(
531 "stream/window count must be greater than zero".to_owned(),
532 ));
533 }
534 let source = handle_arg(cx, stream)?;
535 let transformed = window_by_count(handle_stream(source), count);
536 collect_stream_to_handle(cx, transformed)
537}