use super::CodeGenerator;
use crate::ast::PrimitiveType;
use crate::semantics::Type;
use inkwell::AddressSpace;
use inkwell::context::Context;
use inkwell::module::Module;
use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum, PointerType};
use inkwell::values::{
BasicMetadataValueEnum, BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue,
};
impl<'a> CodeGenerator<'a> {
pub(super) fn generate_runtime_call(
&mut self,
name: &str,
args: &[BasicMetadataValueEnum<'a>],
) -> Option<BasicValueEnum<'a>> {
let func = match self.module.get_function(name) {
Some(f) => f,
None => {
panic!("Function '{}' not found in module", name);
}
};
let call = self
.builder
.build_call(func, args, "call")
.expect("build_call should always return Some");
call.try_as_basic_value().left()
}
pub(super) fn declare_runtime_functions<'b>(module: &Module<'b>, context: &'b Context) {
fn add_i8_fn<'c>(
module: &Module<'c>,
i8_ptr: PointerType<'c>,
name: &str,
params: &[BasicTypeEnum<'c>],
) -> FunctionValue<'c> {
let llvm_params: Vec<BasicMetadataTypeEnum<'c>> =
params.iter().copied().map(Into::into).collect();
module.add_function(name, i8_ptr.fn_type(&llvm_params, false), None)
}
fn add_conversion_fn<'c>(
module: &Module<'c>,
i8_ptr: PointerType<'c>,
mux_name: &str,
from: BasicTypeEnum<'c>,
) -> FunctionValue<'c> {
add_i8_fn(module, i8_ptr, mux_name, &[from])
}
fn add_typed_getter<'c>(
module: &Module<'c>,
i8_ptr: PointerType<'c>,
name: &str,
return_type: BasicTypeEnum<'c>,
) -> FunctionValue<'c> {
module.add_function(name, return_type.fn_type(&[i8_ptr.into()], false), None)
}
let void_type = context.void_type();
let i64_type = context.i64_type();
let i32_type = context.i32_type();
let f64_type = context.f64_type();
let i8_ptr = context.ptr_type(AddressSpace::default());
let list_ptr = i8_ptr;
let map_ptr = i8_ptr;
let set_ptr = i8_ptr;
module.add_function(
"mux_value_from_string",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_new_string_from_cstr",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_print",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function("mux_read_line", i8_ptr.fn_type(&[], false), None);
module.add_function(
"exit",
void_type.fn_type(&[context.i32_type().into()], false),
None,
);
module.add_function("malloc", i8_ptr.fn_type(&[i64_type.into()], false), None);
let params = &[i8_ptr.into(), i8_ptr.into()];
let fn_type = i8_ptr.fn_type(params, false);
module.add_function("mux_string_concat", fn_type, None);
module.add_function(
"mux_string_contains",
context
.bool_type()
.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_string_contains_char",
context
.bool_type()
.fn_type(&[i8_ptr.into(), i64_type.into()], false),
None,
);
module.add_function(
"mux_string_equal",
context
.i32_type()
.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_string_not_equal",
context
.i32_type()
.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_value_equal",
context
.i32_type()
.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_value_not_equal",
context
.i32_type()
.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_value_get_string",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
for (name, from_ty) in [
("mux_int_to_string", i64_type.into()),
("mux_int_to_float", i64_type.into()),
("mux_float_to_int", f64_type.into()),
("mux_float_to_string", f64_type.into()),
("mux_bool_to_string", i32_type.into()),
("mux_char_to_int", i64_type.into()),
("mux_char_to_string", i64_type.into()),
] {
add_conversion_fn(module, i8_ptr, name, from_ty);
}
for name in [
"mux_bool_to_int",
"mux_bool_to_float",
"mux_string_to_string",
"mux_string_to_int",
"mux_string_to_float",
"mux_list_to_string",
"mux_list_value",
"mux_map_value",
"mux_set_value",
"mux_map_to_string",
] {
add_i8_fn(module, i8_ptr, name, &[i8_ptr.into()]);
}
module.add_function(
"mux_register_object_type",
i32_type.fn_type(&[i8_ptr.into(), i64_type.into()], false),
None,
);
module.add_function(
"mux_alloc_object",
i8_ptr.fn_type(&[i32_type.into()], false),
None,
);
module.add_function(
"mux_free_object",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_get_object_ptr",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_copy_object",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_set_to_string",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_optional_to_string",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_optional_into_value",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_value_get_list",
list_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_value_get_map",
map_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_value_get_set",
set_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_list_concat",
list_ptr.fn_type(&[list_ptr.into(), list_ptr.into()], false),
None,
);
module.add_function(
"mux_map_merge",
map_ptr.fn_type(&[map_ptr.into(), map_ptr.into()], false),
None,
);
module.add_function(
"mux_set_union",
set_ptr.fn_type(&[set_ptr.into(), set_ptr.into()], false),
None,
);
module.add_function(
"mux_value_to_string",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_range",
list_ptr.fn_type(&[i64_type.into(), i64_type.into()], false),
None,
);
module.add_function("mux_new_list", list_ptr.fn_type(&[], false), None);
module.add_function("mux_new_map", list_ptr.fn_type(&[], false), None);
module.add_function("mux_new_set", list_ptr.fn_type(&[], false), None);
module.add_function(
"mux_new_tuple",
list_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_tuple_value",
i8_ptr.fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_tuple_left",
i8_ptr.fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_tuple_right",
i8_ptr.fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_tuple_eq",
context
.bool_type()
.fn_type(&[list_ptr.into(), list_ptr.into()], false),
None,
);
module.add_function(
"mux_tuple_to_string",
i8_ptr.fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_value_get_tuple",
list_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_value_add",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
add_typed_getter(module, i8_ptr, "mux_value_get_int", i64_type.into());
add_typed_getter(module, i8_ptr, "mux_value_get_float", f64_type.into());
add_typed_getter(module, i8_ptr, "mux_value_get_bool", i32_type.into());
add_typed_getter(module, i8_ptr, "mux_value_get_type_tag", i32_type.into());
module.add_function(
"mux_free_value",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_list_push_back",
void_type.fn_type(&[list_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_list_get",
i8_ptr.fn_type(&[list_ptr.into(), i64_type.into()], false),
None,
);
module.add_function(
"mux_list_get_value",
i8_ptr.fn_type(&[list_ptr.into(), i64_type.into()], false),
None,
);
module.add_function(
"mux_list_set",
void_type.fn_type(&[list_ptr.into(), i64_type.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_list_set_value",
void_type.fn_type(&[i8_ptr.into(), i64_type.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_list_length",
i64_type.fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_list_contains",
context
.bool_type()
.fn_type(&[list_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_value_list_length",
i64_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_value_list_get_value",
i8_ptr.fn_type(&[i8_ptr.into(), i64_type.into()], false),
None,
);
module.add_function(
"mux_value_list_slice",
i8_ptr.fn_type(&[i8_ptr.into(), i64_type.into(), i64_type.into()], false),
None,
);
module.add_function(
"mux_list_pop_back",
i8_ptr.fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_list_push",
void_type.fn_type(&[list_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_list_pop",
i8_ptr.fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_list_push_back_value",
void_type.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_list_push_value",
void_type.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_list_pop_back_value",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_list_pop_value",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_list_is_empty",
context.bool_type().fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_map_put",
void_type.fn_type(&[map_ptr.into(), i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_map_put_value",
void_type.fn_type(&[i8_ptr.into(), i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_map_get",
i8_ptr.fn_type(&[list_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_map_contains",
context
.bool_type()
.fn_type(&[map_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_map_remove",
i8_ptr.fn_type(&[map_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_map_remove_value",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_set_add",
void_type.fn_type(&[list_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_set_add_value",
void_type.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_set_contains",
context
.bool_type()
.fn_type(&[list_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_set_remove",
context
.bool_type()
.fn_type(&[list_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_set_remove_value",
context
.bool_type()
.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_set_size",
i64_type.fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_set_is_empty",
context.bool_type().fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_map_size",
i64_type.fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_map_is_empty",
context.bool_type().fn_type(&[list_ptr.into()], false),
None,
);
module.add_function(
"mux_map_keys",
i8_ptr.fn_type(&[map_ptr.into()], false),
None,
);
module.add_function(
"mux_map_values",
i8_ptr.fn_type(&[map_ptr.into()], false),
None,
);
module.add_function(
"mux_map_pairs",
i8_ptr.fn_type(&[map_ptr.into()], false),
None,
);
for (name, from_ty) in [
("mux_int_value", i64_type.into()),
("mux_float_value", f64_type.into()),
("mux_bool_value", i32_type.into()),
] {
add_conversion_fn(module, i8_ptr, name, from_ty);
}
add_i8_fn(module, i8_ptr, "mux_string_value", &[i8_ptr.into()]);
add_typed_getter(module, i8_ptr, "mux_int_from_value", i64_type.into());
add_typed_getter(module, i8_ptr, "mux_float_from_value", f64_type.into());
add_typed_getter(module, i8_ptr, "mux_bool_from_value", i32_type.into());
add_i8_fn(module, i8_ptr, "mux_string_from_value", &[i8_ptr.into()]);
for (name, from_ty) in [
("mux_optional_some_int", i64_type.into()),
("mux_optional_some_float", f64_type.into()),
("mux_optional_some_bool", i32_type.into()),
("mux_optional_some_char", i64_type.into()),
("mux_result_ok_int", i64_type.into()),
("mux_result_ok_float", f64_type.into()),
("mux_result_ok_bool", i32_type.into()),
("mux_result_ok_char", i64_type.into()),
] {
add_conversion_fn(module, i8_ptr, name, from_ty);
}
for name in [
"mux_optional_some_string",
"mux_optional_some_value",
"mux_result_ok_string",
"mux_result_ok_value",
"mux_result_err_str",
"mux_result_err_value",
"mux_optional_data",
"mux_optional_get_value",
"mux_result_data",
] {
add_i8_fn(module, i8_ptr, name, &[i8_ptr.into()]);
}
module.add_function("mux_optional_none", i8_ptr.fn_type(&[], false), None);
add_typed_getter(module, i8_ptr, "mux_optional_discriminant", i32_type.into());
add_typed_getter(
module,
i8_ptr,
"mux_value_optional_discriminant",
i32_type.into(),
);
add_typed_getter(
module,
i8_ptr,
"mux_optional_is_some",
context.bool_type().into(),
);
add_typed_getter(
module,
i8_ptr,
"mux_optional_is_none",
context.bool_type().into(),
);
module.add_function(
"mux_free_optional",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
add_typed_getter(module, i8_ptr, "mux_result_discriminant", i32_type.into());
add_typed_getter(
module,
i8_ptr,
"mux_value_result_discriminant",
i32_type.into(),
);
add_typed_getter(
module,
i8_ptr,
"mux_result_is_ok",
context.bool_type().into(),
);
add_typed_getter(
module,
i8_ptr,
"mux_result_is_err",
context.bool_type().into(),
);
module.add_function(
"mux_int_pow",
i64_type.fn_type(&[i64_type.into(), i64_type.into()], false),
None,
);
module.add_function(
"mux_math_pow",
f64_type.fn_type(&[f64_type.into(), f64_type.into()], false),
None,
);
macro_rules! declare_extern_batch {
($module:expr, $names:expr, $fn_type:expr) => {
for name in $names {
$module.add_function(name, $fn_type, None);
}
};
}
declare_extern_batch!(
module,
&[
"mux_math_sqrt",
"mux_math_sin",
"mux_math_cos",
"mux_math_tan",
"mux_math_asin",
"mux_math_acos",
"mux_math_atan",
"mux_math_ln",
"mux_math_log2",
"mux_math_log10",
"mux_math_exp",
"mux_math_abs",
"mux_math_floor",
"mux_math_ceil",
"mux_math_round",
],
f64_type.fn_type(&[f64_type.into()], false)
);
declare_extern_batch!(
module,
&[
"mux_math_atan2",
"mux_math_log",
"mux_math_min",
"mux_math_max",
"mux_math_hypot",
],
f64_type.fn_type(&[f64_type.into(), f64_type.into()], false)
);
declare_extern_batch!(
module,
&["mux_math_pi", "mux_math_e"],
f64_type.fn_type(&[], false)
);
module.add_function("mux_read_int", i64_type.fn_type(&[], false), None);
module.add_function("mux_flush_stdout", void_type.fn_type(&[], false), None);
for name in [
"mux_io_read_file",
"mux_io_exists",
"mux_io_remove",
"mux_io_is_file",
"mux_io_is_dir",
"mux_io_mkdir",
"mux_io_listdir",
"mux_io_basename",
"mux_io_dirname",
] {
add_i8_fn(module, i8_ptr, name, &[i8_ptr.into()]);
}
add_i8_fn(module, i8_ptr, "mux_env_get", &[i8_ptr.into()]);
add_i8_fn(module, i8_ptr, "mux_json_parse", &[i8_ptr.into()]);
module.add_function(
"mux_json_stringify",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
add_i8_fn(module, i8_ptr, "mux_json_from_map", &[i8_ptr.into()]);
add_i8_fn(module, i8_ptr, "mux_json_to_map", &[i8_ptr.into()]);
add_i8_fn(module, i8_ptr, "mux_csv_parse", &[i8_ptr.into()]);
add_i8_fn(
module,
i8_ptr,
"mux_csv_parse_with_headers",
&[i8_ptr.into()],
);
module.add_function(
"mux_csv_to_string",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
add_i8_fn(
module,
i8_ptr,
"mux_io_write_file",
&[i8_ptr.into(), i8_ptr.into()],
);
add_i8_fn(
module,
i8_ptr,
"mux_io_join",
&[i8_ptr.into(), i8_ptr.into()],
);
for name in ["mux_datetime_now", "mux_datetime_now_millis"] {
module.add_function(name, i8_ptr.fn_type(&[], false), None);
}
for name in [
"mux_datetime_year",
"mux_datetime_month",
"mux_datetime_day",
"mux_datetime_hour",
"mux_datetime_minute",
"mux_datetime_second",
"mux_datetime_weekday",
"mux_datetime_sleep",
"mux_datetime_sleep_millis",
] {
module.add_function(name, i8_ptr.fn_type(&[i64_type.into()], false), None);
}
for name in ["mux_datetime_format", "mux_datetime_format_local"] {
module.add_function(
name,
i8_ptr.fn_type(&[i64_type.into(), i8_ptr.into()], false),
None,
);
}
module.add_function(
"mux_sync_spawn",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sync_sleep",
void_type.fn_type(&[i64_type.into()], false),
None,
);
module.add_function(
"mux_thread_join",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_thread_detach",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function("mux_mutex_new", i8_ptr.fn_type(&[], false), None);
module.add_function(
"mux_mutex_lock",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_mutex_unlock",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function("mux_rwlock_new", i8_ptr.fn_type(&[], false), None);
module.add_function(
"mux_rwlock_read_lock",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_rwlock_write_lock",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_rwlock_unlock",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function("mux_condvar_new", i8_ptr.fn_type(&[], false), None);
module.add_function(
"mux_condvar_wait",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_condvar_signal",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_condvar_broadcast",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_rc_inc",
context.void_type().fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_rc_dec",
context.bool_type().fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_rand_init",
void_type.fn_type(&[i64_type.into()], false),
None,
);
module.add_function("mux_rand_int", i64_type.fn_type(&[], false), None);
module.add_function(
"mux_rand_range",
i64_type.fn_type(&[i64_type.into(), i64_type.into()], false),
None,
);
module.add_function("mux_rand_float", f64_type.fn_type(&[], false), None);
module.add_function(
"mux_rand_bool",
context.bool_type().fn_type(&[], false),
None,
);
module.add_function(
"mux_assert_assert",
void_type.fn_type(&[context.i32_type().into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_assert_eq",
void_type.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_assert_ne",
void_type.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_assert_true",
void_type.fn_type(&[context.i32_type().into()], false),
None,
);
module.add_function(
"mux_assert_false",
void_type.fn_type(&[context.i32_type().into()], false),
None,
);
module.add_function(
"mux_assert_some",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_assert_none",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_assert_ok",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_assert_err",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_http_request",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_http_read_request",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_http_write_response",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_tcp_listener_bind",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_tcp_listener_accept",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_tcp_listener_set_nonblocking",
i8_ptr.fn_type(&[i8_ptr.into(), i32_type.into()], false),
None,
);
module.add_function(
"mux_net_tcp_listener_local_addr",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_tcp_listener_close",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_tcp_connect",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_tcp_read",
i8_ptr.fn_type(&[i8_ptr.into(), i64_type.into()], false),
None,
);
module.add_function(
"mux_net_tcp_write",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_tcp_close",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_tcp_set_nonblocking",
i8_ptr.fn_type(&[i8_ptr.into(), i32_type.into()], false),
None,
);
module.add_function(
"mux_net_tcp_peer_addr",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_tcp_local_addr",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_udp_bind",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_udp_send_to",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_udp_recv_from",
i8_ptr.fn_type(&[i8_ptr.into(), i64_type.into()], false),
None,
);
module.add_function(
"mux_net_udp_close",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_udp_set_nonblocking",
i8_ptr.fn_type(&[i8_ptr.into(), i32_type.into()], false),
None,
);
module.add_function(
"mux_net_udp_peer_addr",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_net_udp_local_addr",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_connect",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_value_int",
i8_ptr.fn_type(&[i64_type.into()], false),
None,
);
module.add_function(
"mux_sql_value_float",
i8_ptr.fn_type(&[f64_type.into()], false),
None,
);
module.add_function(
"mux_sql_value_bool",
i8_ptr.fn_type(&[context.bool_type().into()], false),
None,
);
module.add_function(
"mux_sql_value_string",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_value_bytes",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function("mux_sql_value_null", i8_ptr.fn_type(&[], false), None);
module.add_function(
"mux_sql_value_is_null",
context.bool_type().fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_value_as_bool",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_value_as_int",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_value_as_float",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_value_as_string",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_value_as_bytes",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_connection_close",
void_type.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_connection_execute",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_connection_execute_params",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_connection_query",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_connection_query_params",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_connection_begin_transaction",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_transaction_commit",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_transaction_rollback",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_transaction_execute",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_transaction_execute_params",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_transaction_query",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_transaction_query_params",
i8_ptr.fn_type(&[i8_ptr.into(), i8_ptr.into(), i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_resultset_rows",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_resultset_next",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
module.add_function(
"mux_sql_resultset_columns",
i8_ptr.fn_type(&[i8_ptr.into()], false),
None,
);
}
pub(super) fn box_value(&mut self, val: BasicValueEnum<'a>) -> PointerValue<'a> {
if val.is_int_value() {
let int_val = val.into_int_value();
if int_val.get_type().get_bit_width() == 1 {
let i32_val = self
.builder
.build_int_z_extend(int_val, self.context.i32_type(), "bool_to_i32")
.expect("bool extension should succeed");
let call = self
.generate_runtime_call("mux_bool_value", &[i32_val.into()])
.expect("mux_bool_value should always return a value");
call.into_pointer_value()
} else {
let call = self
.generate_runtime_call("mux_int_value", &[int_val.into()])
.expect("mux_int_value should always return a value");
call.into_pointer_value()
}
} else if val.is_float_value() {
let call = self
.generate_runtime_call("mux_float_value", &[val.into()])
.expect("mux_float_value should always return a value");
call.into_pointer_value()
} else if val.is_pointer_value() {
val.into_pointer_value()
} else {
panic!("Unexpected value type in box_value")
}
}
fn extract_primitive_from_ptr<T: TryFrom<BasicValueEnum<'a>>>(
&mut self,
ptr: PointerValue<'a>,
getter_func_name: &str,
error_msg: &str,
) -> Result<T, String>
where
<T as TryFrom<BasicValueEnum<'a>>>::Error: std::fmt::Debug,
{
let func = self
.module
.get_function(getter_func_name)
.ok_or(format!("{} not found", getter_func_name))?;
let result = self
.builder
.build_call(func, &[ptr.into()], "extract")
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.ok_or(error_msg)?;
result
.try_into()
.map_err(|_| format!("Conversion failed for {}", getter_func_name))
}
pub(super) fn get_raw_int_value(
&mut self,
val: BasicValueEnum<'a>,
) -> Result<IntValue<'a>, String> {
if val.is_int_value() {
Ok(val.into_int_value())
} else if val.is_pointer_value() {
let ptr = val.into_pointer_value();
self.extract_primitive_from_ptr(ptr, "mux_value_get_int", "Call returned no value")
} else {
Err("Expected int value or pointer".to_string())
}
}
pub(super) fn get_raw_float_value(
&mut self,
val: BasicValueEnum<'a>,
) -> Result<FloatValue<'a>, String> {
if val.is_float_value() {
Ok(val.into_float_value())
} else if val.is_pointer_value() {
let ptr = val.into_pointer_value();
self.extract_primitive_from_ptr(ptr, "mux_value_get_float", "Call returned no value")
} else {
Err("Expected float value or pointer".to_string())
}
}
pub(super) fn get_raw_bool_value(
&mut self,
val: BasicValueEnum<'a>,
) -> Result<IntValue<'a>, String> {
if val.is_int_value() {
let int_val = val.into_int_value();
if int_val.get_type().get_bit_width() == 1 {
Ok(int_val)
} else {
let i1_val = self
.builder
.build_int_truncate(int_val, self.context.bool_type(), "trunc_to_i1")
.map_err(|e| e.to_string())?;
Ok(i1_val)
}
} else if val.is_pointer_value() {
let ptr = val.into_pointer_value();
let get_bool_fn = self
.module
.get_function("mux_value_get_bool")
.ok_or("mux_value_get_bool not found")?;
let i32_result = self
.builder
.build_call(get_bool_fn, &[ptr.into()], "get_bool")
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.ok_or("Call returned no value")?
.into_int_value();
let i1_val = self
.builder
.build_int_truncate(i32_result, self.context.bool_type(), "trunc_to_i1")
.map_err(|e| e.to_string())?;
Ok(i1_val)
} else {
Err("Expected bool value or pointer".to_string())
}
}
pub(super) fn extract_value_from_ptr(
&mut self,
data_ptr: PointerValue<'a>,
wrapped_type: &Type,
variant_name: &str,
) -> Result<(BasicValueEnum<'a>, Type), String> {
match wrapped_type {
Type::Primitive(PrimitiveType::Int) => {
let get_int_func = self
.module
.get_function("mux_value_get_int")
.ok_or(format!(
"Failed to extract int from {}: mux_value_get_int not found",
variant_name
))?;
let val = self
.builder
.build_call(get_int_func, &[data_ptr.into()], "get_int")
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.ok_or("mux_value_get_int returned no value")?;
Ok((val, Type::Primitive(PrimitiveType::Int)))
}
Type::Primitive(PrimitiveType::Float) => {
let get_float_func =
self.module
.get_function("mux_value_get_float")
.ok_or(format!(
"Failed to extract float from {}: mux_value_get_float not found",
variant_name
))?;
let val = self
.builder
.build_call(get_float_func, &[data_ptr.into()], "get_float")
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.ok_or("mux_value_get_float returned no value")?;
Ok((val, Type::Primitive(PrimitiveType::Float)))
}
Type::Primitive(PrimitiveType::Bool) => {
let get_bool_func =
self.module
.get_function("mux_value_get_bool")
.ok_or(format!(
"Failed to extract bool from {}: mux_value_get_bool not found",
variant_name
))?;
let val = self
.builder
.build_call(get_bool_func, &[data_ptr.into()], "get_bool")
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.ok_or("mux_value_get_bool returned no value")?
.into_int_value();
let i1_val = self
.builder
.build_int_truncate(val, self.context.bool_type(), "trunc_to_i1")
.map_err(|e| e.to_string())?;
Ok((i1_val.into(), Type::Primitive(PrimitiveType::Bool)))
}
Type::Primitive(PrimitiveType::Char) => {
let get_int_func = self
.module
.get_function("mux_value_get_int")
.ok_or(format!(
"Failed to extract char from {}: mux_value_get_int not found",
variant_name
))?;
let val = self
.builder
.build_call(get_int_func, &[data_ptr.into()], "get_int")
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.ok_or("mux_value_get_int returned no value")?;
Ok((val, Type::Primitive(PrimitiveType::Char)))
}
Type::Primitive(PrimitiveType::Str) => {
let get_string_func =
self.module
.get_function("mux_value_get_string")
.ok_or(format!(
"Failed to extract string from {}: mux_value_get_string not found",
variant_name
))?;
let c_str = self
.builder
.build_call(get_string_func, &[data_ptr.into()], "get_string")
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.ok_or("mux_value_get_string returned no value")?
.into_pointer_value();
let new_string_func = self
.module
.get_function("mux_new_string_from_cstr")
.ok_or("mux_new_string_from_cstr not found")?;
let mux_string = self
.builder
.build_call(new_string_func, &[c_str.into()], "new_string")
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.ok_or("mux_new_string_from_cstr returned no value")?;
Ok((mux_string, Type::Primitive(PrimitiveType::Str)))
}
Type::Primitive(PrimitiveType::Void) => Err(format!(
"Unsupported type Void for extraction from {}",
variant_name
)),
Type::Primitive(PrimitiveType::Auto) => Err(format!(
"Unsupported type Auto for extraction from {}",
variant_name
)),
Type::List(_)
| Type::Map(_, _)
| Type::Set(_)
| Type::Tuple(_, _)
| Type::Named(_, _)
| Type::Optional(_)
| Type::Result(_, _)
| Type::Instantiated(_, _) => {
Ok((data_ptr.into(), wrapped_type.clone()))
}
Type::Reference(inner) => self.extract_value_from_ptr(data_ptr, inner, variant_name),
Type::Void | Type::Never | Type::EmptyList | Type::EmptyMap | Type::EmptySet => {
Err(format!(
"Unsupported type {:?} for extraction from {}",
wrapped_type, variant_name
))
}
Type::Function { .. } => Err(format!(
"Unsupported type Function for extraction from {}",
variant_name
)),
Type::Variable(v) | Type::Generic(v) => Err(format!(
"Unresolved generic type {} for extraction from {}",
v, variant_name
)),
Type::Module(_) => {
panic!("Module types should not appear in codegen - they are compile-time only")
}
}
}
fn call_value_getter(
&mut self,
func_name: &str,
value_ptr: PointerValue<'a>,
result_name: &str,
) -> Result<PointerValue<'a>, String> {
let func = self
.module
.get_function(func_name)
.ok_or_else(|| format!("{} not found", func_name))?;
self.builder
.build_call(func, &[value_ptr.into()], result_name)
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.ok_or_else(|| format!("{} returned no value", func_name))
.map(|v| v.into_pointer_value())
}
pub(super) fn extract_c_string_from_value(
&mut self,
value_ptr: PointerValue<'a>,
) -> Result<PointerValue<'a>, String> {
self.call_value_getter("mux_value_get_string", value_ptr, "get_string")
}
pub(super) fn box_string_value(
&mut self,
cstr_ptr: PointerValue<'a>,
) -> Result<BasicValueEnum<'a>, String> {
let func = self
.module
.get_function("mux_value_from_string")
.ok_or("mux_value_from_string not found")?;
self.builder
.build_call(func, &[cstr_ptr.into()], "from_string")
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.ok_or_else(|| "mux_value_from_string returned no value".to_string())
}
pub(super) fn extract_list_from_value(
&mut self,
value_ptr: PointerValue<'a>,
) -> Result<PointerValue<'a>, String> {
self.call_value_getter("mux_value_get_list", value_ptr, "get_list")
}
pub(super) fn extract_map_from_value(
&mut self,
value_ptr: PointerValue<'a>,
) -> Result<PointerValue<'a>, String> {
self.call_value_getter("mux_value_get_map", value_ptr, "get_map")
}
pub(super) fn extract_set_from_value(
&mut self,
value_ptr: PointerValue<'a>,
) -> Result<PointerValue<'a>, String> {
self.call_value_getter("mux_value_get_set", value_ptr, "get_set")
}
}