1mod coll;
7mod conc;
8mod core;
9pub mod fiber;
10mod net;
11mod script;
12mod sys;
13
14use std::io::Write;
15use std::sync::OnceLock;
16
17use catallaxy_runtime::class::class_of;
18use catallaxy_runtime::object::{kind_of, str_of};
19use catallaxy_runtime::ops;
20use catallaxy_runtime::ownership;
21use catallaxy_runtime::{panic, ObjKind, RtResult, Trap, Value};
22use catallaxy_vm::{NativeFn, Vm};
23use parking_lot::Mutex;
24
25pub static ARGS: OnceLock<Vec<String>> = OnceLock::new();
27
28static STDOUT: Mutex<Option<std::io::BufWriter<std::io::Stdout>>> = Mutex::new(None);
29
30pub fn write_out(s: &str) {
32 let mut g = STDOUT.lock();
33 let w = g.get_or_insert_with(|| std::io::BufWriter::with_capacity(1 << 16, std::io::stdout()));
34 let _ = w.write_all(s.as_bytes());
35}
36
37pub fn flush() {
39 if let Some(w) = STDOUT.lock().as_mut() {
40 let _ = w.flush();
41 }
42}
43
44static EXTENSIONS: std::sync::LazyLock<parking_lot::RwLock<rustc_hash::FxHashMap<String, NativeFn>>> =
45 std::sync::LazyLock::new(Default::default);
46
47pub fn register_native(name: &str, f: NativeFn) {
51 EXTENSIONS.write().insert(name.to_string(), f);
52}
53
54pub fn lookup(name: &str) -> Option<NativeFn> {
56 core::lookup(name)
57 .or_else(|| coll::lookup(name))
58 .or_else(|| conc::lookup(name))
59 .or_else(|| sys::lookup(name))
60 .or_else(|| script::lookup(name))
61 .or_else(|| net::lookup(name))
62 .or_else(|| EXTENSIONS.read().get(name).copied())
63}
64
65pub(crate) fn opt(v: Value) -> Option<Value> {
68 if v.is_empty_marker() {
69 None
70 } else {
71 Some(v)
72 }
73}
74
75pub(crate) fn arg(args: &[Value], i: usize) -> Option<Value> {
76 args.get(i).copied().and_then(opt)
77}
78
79pub(crate) fn s<'a>(v: Value) -> RtResult<&'a str> {
80 match str_of(v) {
81 Some(s) => Ok(s),
82 None => panic(format!("TypeError: expected String, got {}", class_of(v).name)),
83 }
84}
85
86pub(crate) fn int(v: Value) -> RtResult<i64> {
87 if v.is_small_int() {
88 return Ok(v.as_small_int());
89 }
90 if ops::is_int(v) {
91 return match ops::int_as_i64(v) {
92 Some(i) => Ok(i),
93 None => panic("RangeError: integer too large"),
94 };
95 }
96 panic(format!("TypeError: expected Int, got {}", class_of(v).name))
97}
98
99pub(crate) fn float(v: Value) -> RtResult<f64> {
100 if v.is_float() {
101 Ok(v.as_f64())
102 } else if ops::is_int(v) {
103 Ok(ops::int_to_f64(v))
104 } else {
105 panic(format!("TypeError: expected Float, got {}", class_of(v).name))
106 }
107}
108
109pub(crate) fn new_str(vm: &mut Vm, s: &str) -> Value {
110 vm.m.alloc_str(s)
111}
112
113pub(crate) fn new_list(vm: &mut Vm, items: Vec<Value>) -> Value {
114 vm.m.alloc_list(items)
115}
116
117pub(crate) fn list_items(vm: &Vm, l: Value) -> RtResult<Vec<Value>> {
119 if kind_of(l) != ObjKind::List {
120 return panic(format!("TypeError: expected List, got {}", class_of(l).name));
121 }
122 Ok(ownership::read_list(&vm.m, l)?.to_vec())
123}
124
125pub(crate) fn to_s(vm: &mut Vm, v: Value) -> RtResult<String> {
126 ops::to_s(vm, v)
127}
128
129pub(crate) fn ok(vm: &mut Vm, v: Value) -> Value {
130 catallaxy_vm::slow::make_result(vm, true, v)
131}
132
133pub(crate) fn err(vm: &mut Vm, msg: &str) -> Value {
134 let s = new_str(vm, msg);
135 catallaxy_vm::slow::make_result(vm, false, s)
136}
137
138pub(crate) fn exit_trap(code: i32) -> Trap {
139 Trap::Exit(code)
140}
141
142pub(crate) fn blocking<R>(vm: &Vm, f: impl FnOnce() -> R) -> R {
144 flush();
145 catallaxy_gc::blocking(&vm.m.rec, f)
146}