1mod coll;
7mod conc;
8pub use conc::report_unjoined_panics;
9mod core;
10mod data;
11pub mod fiber;
12mod net;
13mod script;
14mod sys;
15
16use std::io::Write;
17use std::sync::OnceLock;
18
19use catallaxy_runtime::class::class_of;
20use catallaxy_runtime::object::{kind_of, str_of};
21use catallaxy_runtime::ops;
22use catallaxy_runtime::ownership;
23use catallaxy_runtime::{panic, try_push, try_vec, ObjKind, RtResult, Trap, Value};
24use catallaxy_vm::{NativeFn, Vm};
25use parking_lot::Mutex;
26
27pub static ARGS: OnceLock<Vec<String>> = OnceLock::new();
29
30static STDOUT: Mutex<Option<std::io::BufWriter<std::io::Stdout>>> = Mutex::new(None);
31
32pub fn write_out(s: &str) {
34 let mut g = STDOUT.lock();
35 let w = g.get_or_insert_with(|| std::io::BufWriter::with_capacity(1 << 16, std::io::stdout()));
36 let _ = w.write_all(s.as_bytes());
37}
38
39pub fn flush() {
41 if let Some(w) = STDOUT.lock().as_mut() {
42 let _ = w.flush();
43 }
44}
45
46static EXTENSIONS: std::sync::LazyLock<parking_lot::RwLock<rustc_hash::FxHashMap<String, NativeFn>>> =
47 std::sync::LazyLock::new(Default::default);
48
49pub fn register_native(name: &str, f: NativeFn) {
53 EXTENSIONS.write().insert(name.to_string(), f);
54}
55
56pub fn lookup(name: &str) -> Option<NativeFn> {
58 sys::init_clock();
61 core::lookup(name)
62 .or_else(|| coll::lookup(name))
63 .or_else(|| conc::lookup(name))
64 .or_else(|| sys::lookup(name))
65 .or_else(|| script::lookup(name))
66 .or_else(|| net::lookup(name))
67 .or_else(|| data::lookup(name))
68 .or_else(|| EXTENSIONS.read().get(name).copied())
69}
70
71pub(crate) fn opt(v: Value) -> Option<Value> {
74 if v.is_empty_marker() {
75 None
76 } else {
77 Some(v)
78 }
79}
80
81pub(crate) fn arg(args: &[Value], i: usize) -> Option<Value> {
82 args.get(i).copied().and_then(opt)
83}
84
85pub(crate) fn s<'a>(v: Value) -> RtResult<&'a str> {
86 match str_of(v) {
87 Some(s) => Ok(s),
88 None => panic(format!("TypeError: expected String, got {}", class_of(v).name)),
89 }
90}
91
92pub(crate) fn str_obj<'a>(v: Value) -> RtResult<&'a catallaxy_runtime::object::StrObj> {
94 s(v)?;
95 Ok(unsafe { catallaxy_runtime::object::as_str_obj(v) })
96}
97
98pub(crate) fn int(v: Value) -> RtResult<i64> {
99 if v.is_small_int() {
100 return Ok(v.as_small_int());
101 }
102 if ops::is_int(v) {
103 return match ops::int_as_i64(v) {
104 Some(i) => Ok(i),
105 None => panic("RangeError: integer too large"),
106 };
107 }
108 panic(format!("TypeError: expected Int, got {}", class_of(v).name))
109}
110
111pub(crate) fn float(v: Value) -> RtResult<f64> {
112 if v.is_float() {
113 Ok(v.as_f64())
114 } else if ops::is_int(v) {
115 Ok(ops::int_to_f64(v))
116 } else {
117 panic(format!("TypeError: expected Float, got {}", class_of(v).name))
118 }
119}
120
121pub(crate) fn new_str(vm: &mut Vm, s: &str) -> Value {
122 vm.m.alloc_str(s)
123}
124
125pub(crate) fn new_list(vm: &mut Vm, items: Vec<Value>) -> RtResult<Value> {
128 vm.m.try_alloc_list(items)
129}
130
131pub(crate) fn new_str_list<'a>(vm: &mut Vm, parts: impl IntoIterator<Item = &'a str>) -> RtResult<Value> {
135 let parts = parts.into_iter();
136 let mut items = try_vec(parts.size_hint().0)?;
137 for p in parts {
138 let v = vm.m.try_alloc_str_nogc(p)?;
139 try_push(&mut items, v)?;
140 }
141 new_list(vm, items)
142}
143
144pub(crate) fn list_items(vm: &Vm, l: Value) -> RtResult<Vec<Value>> {
146 if kind_of(l) != ObjKind::List {
147 return panic(format!("TypeError: expected List, got {}", class_of(l).name));
148 }
149 ownership::read_list(&vm.m, l)?.try_to_vec()
150}
151
152pub(crate) fn to_s(vm: &mut Vm, v: Value) -> RtResult<String> {
153 ops::to_s(vm, v)
154}
155
156pub(crate) fn ok(vm: &mut Vm, v: Value) -> Value {
157 catallaxy_vm::slow::make_result(vm, true, v)
158}
159
160pub(crate) fn err(vm: &mut Vm, msg: &str) -> Value {
161 let s = new_str(vm, msg);
162 catallaxy_vm::slow::make_result(vm, false, s)
163}
164
165pub(crate) fn io_err(vm: &mut Vm, path: &str, e: std::io::Error) -> RtResult<Value> {
168 if e.kind() == std::io::ErrorKind::OutOfMemory {
169 return catallaxy_runtime::refused(format!("cannot read {path} into memory"));
170 }
171 Ok(err(vm, &format!("{path}: {e}")))
172}
173
174pub(crate) fn exit_trap(code: i32) -> Trap {
175 Trap::Exit(code)
176}
177
178pub(crate) fn blocking<R>(vm: &Vm, f: impl FnOnce() -> R) -> R {
180 flush();
181 catallaxy_gc::blocking(&vm.m.rec, f)
182}