use inkwell::values::{BasicMetadataValueEnum, BasicValueEnum};
use crate::ast::{ExpressionNode, PrimitiveType};
use crate::semantics::Type;
use super::CodeGenerator;
fn gen_one_expr<'a>(
s: &mut CodeGenerator<'a>,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
if args.len() != 1 {
return Err("method takes exactly 1 argument".to_string());
}
s.generate_expression(&args[0])
}
fn gen_two_expr<'a>(
s: &mut CodeGenerator<'a>,
args: &[ExpressionNode],
) -> Result<(BasicValueEnum<'a>, BasicValueEnum<'a>), String> {
if args.len() != 2 {
return Err("method takes exactly 2 arguments".to_string());
}
let a = s.generate_expression(&args[0])?;
let b = s.generate_expression(&args[1])?;
Ok((a, b))
}
impl<'a> CodeGenerator<'a> {
fn call_cstr_to_mux_string(
&self,
cstr_ptr: BasicValueEnum<'a>,
) -> Result<BasicValueEnum<'a>, String> {
let new_string = self
.runtime_function("mux_new_string_from_cstr")
.ok_or("mux_new_string_from_cstr not found")?;
let call = self
.builder
.build_call(new_string, &[cstr_ptr.into()], "new_string")
.map_err(|e| e.to_string())?;
Ok(call
.try_as_basic_value()
.left()
.expect("mux_new_string_from_cstr should return a basic value"))
}
fn call_runtime_function(
&self,
func_name: &str,
args: &[BasicValueEnum<'a>],
) -> Result<BasicValueEnum<'a>, String> {
let func = self
.runtime_function(func_name)
.ok_or(format!("Function '{}' not found", func_name))?;
let call = self
.builder
.build_call(
func,
&args.iter().map(|v| (*v).into()).collect::<Vec<_>>(),
"call",
)
.map_err(|e| e.to_string())?;
call.try_as_basic_value()
.left()
.ok_or_else(|| format!("{} should return a basic value", func_name))
}
fn call_runtime_to_string(
&self,
value: BasicValueEnum<'a>,
func_name: &str,
) -> Result<BasicValueEnum<'a>, String> {
let to_cstr = self
.runtime_function(func_name)
.ok_or(format!("{} not found", func_name))?;
let call = self
.builder
.build_call(to_cstr, &[value.into()], "to_cstr")
.map_err(|e| e.to_string())?;
let cstr = call
.try_as_basic_value()
.left()
.ok_or(format!("{} should return a basic value", func_name))?;
self.call_cstr_to_mux_string(cstr)
}
fn call_runtime_to_string_from_call(
&self,
call: inkwell::values::CallSiteValue<'a>,
) -> Result<BasicValueEnum<'a>, String> {
let cstr = call
.try_as_basic_value()
.left()
.ok_or_else(|| "Function should return a basic value".to_string())?;
self.call_cstr_to_mux_string(cstr)
}
fn generate_to_string_call(
&self,
obj_value: BasicValueEnum<'a>,
) -> Result<BasicValueEnum<'a>, String> {
let func = self
.runtime_function("mux_value_to_string")
.ok_or("mux_value_to_string not found")?;
let call = self
.builder
.build_call(func, &[obj_value.into()], "val_to_str")
.map_err(|e| e.to_string())?;
let cstr = call
.try_as_basic_value()
.left()
.expect("mux_value_to_string should return a basic value");
self.call_cstr_to_mux_string(cstr)
}
fn extract_raw_pointer(
&self,
obj_value: BasicValueEnum<'a>,
getter_func: &str,
extract_name: &str,
) -> Result<BasicValueEnum<'a>, String> {
let getter = self
.runtime_function(getter_func)
.ok_or(format!("{} not found", getter_func))?;
let raw = self
.builder
.build_call(getter, &[obj_value.into()], extract_name)
.map_err(|e| e.to_string())?;
raw.try_as_basic_value()
.left()
.ok_or_else(|| format!("{} should return a basic value", getter_func))
}
fn free_raw_pointer(&self, raw_ptr: BasicValueEnum<'a>, free_func: &str) -> Result<(), String> {
let free_fn = self
.runtime_function(free_func)
.ok_or(format!("{} not found", free_func))?;
self.builder
.build_call(free_fn, &[raw_ptr.into()], "free_raw")
.map_err(|e| e.to_string())?;
Ok(())
}
fn with_extracted<F>(
&mut self,
obj_value: BasicValueEnum<'a>,
get_func: &str,
free_func: &str,
f: F,
) -> Result<BasicValueEnum<'a>, String>
where
F: FnOnce(&mut Self, BasicValueEnum<'a>) -> Result<BasicValueEnum<'a>, String>,
{
let raw = self.extract_raw_pointer(obj_value, get_func, "extract")?;
let result = f(self, raw)?;
self.free_raw_pointer(raw, free_func)?;
Ok(result)
}
fn with_extracted_list<F>(
&mut self,
obj_value: BasicValueEnum<'a>,
f: F,
) -> Result<BasicValueEnum<'a>, String>
where
F: FnOnce(&mut Self, BasicValueEnum<'a>) -> Result<BasicValueEnum<'a>, String>,
{
self.with_extracted(obj_value, "mux_value_get_list", "mux_free_list", f)
}
fn with_extracted_map<F>(
&mut self,
obj_value: BasicValueEnum<'a>,
f: F,
) -> Result<BasicValueEnum<'a>, String>
where
F: FnOnce(&mut Self, BasicValueEnum<'a>) -> Result<BasicValueEnum<'a>, String>,
{
self.with_extracted(obj_value, "mux_value_get_map", "mux_free_map", f)
}
fn with_extracted_set<F>(
&mut self,
obj_value: BasicValueEnum<'a>,
f: F,
) -> Result<BasicValueEnum<'a>, String>
where
F: FnOnce(&mut Self, BasicValueEnum<'a>) -> Result<BasicValueEnum<'a>, String>,
{
self.with_extracted(obj_value, "mux_value_get_set", "mux_free_set", f)
}
fn call_string_conversion_func(
&self,
obj_value: BasicValueEnum<'a>,
conversion_func: &str,
) -> Result<BasicValueEnum<'a>, String> {
let func_to_cstr = self
.runtime_function("mux_value_to_string")
.ok_or("mux_value_to_string not found")?;
let cstr = self
.builder
.build_call(func_to_cstr, &[obj_value.into()], "str_to_cstr")
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.expect("mux_value_to_string should return a basic value");
let func = self
.runtime_function(conversion_func)
.ok_or(format!("{} not found", conversion_func))?;
let call = self
.builder
.build_call(func, &[cstr.into()], "str_conv")
.map_err(|e| e.to_string())?;
Ok(call
.try_as_basic_value()
.left()
.unwrap_or_else(|| panic!("{} should return a basic value", conversion_func)))
}
fn call_unary_predicate(
&self,
obj_value: BasicValueEnum<'a>,
func_name: &str,
) -> Result<BasicValueEnum<'a>, String> {
let func = self
.runtime_function(func_name)
.ok_or(format!("{} not found", func_name))?;
let call = self
.builder
.build_call(func, &[obj_value.into()], "predicate_call")
.map_err(|e| e.to_string())?;
Ok(call
.try_as_basic_value()
.left()
.unwrap_or_else(|| panic!("{} should return a basic value", func_name)))
}
fn ensure_arg_count(
&self,
method: &str,
args: &[ExpressionNode],
expected: usize,
) -> Result<(), String> {
if args.len() != expected {
Err(format!(
"{}() method takes exactly {} argument(s)",
method, expected
))
} else {
Ok(())
}
}
fn ensure_no_args(&self, method: &str, args: &[ExpressionNode]) -> Result<(), String> {
if !args.is_empty() {
Err(format!("{}() method takes no arguments", method))
} else {
Ok(())
}
}
fn invoke_class_instance_method(
&mut self,
class_name: &str,
type_args: &[Type],
obj_value: BasicValueEnum<'a>,
method_name: &str,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
let class = self
.analyzer
.symbol_table()
.lookup(class_name)
.ok_or_else(|| format!("Class {} not found", class_name))?;
let method = class
.methods
.get(method_name)
.ok_or_else(|| format!("Method {} not found on class {}", method_name, class_name))?;
if method.is_static {
return Err(format!(
"Cannot call static method {} on instance",
method_name
));
}
let method_func_name = self.resolve_method_func_name(class_name, type_args, method_name)?;
let is_specialized = method_func_name.contains('$');
let call_args = self.build_method_call_args(obj_value, args, is_specialized)?;
let func = self
.module
.get_function(&method_func_name)
.ok_or_else(|| format!("Method '{}' not found", method_func_name))?;
let call = self
.builder
.build_call(
func,
&call_args,
&format!("{}_call", method_func_name.replace('.', "_")),
)
.map_err(|e| e.to_string())?;
self.handle_method_return_value(call, &method.return_type)
}
fn resolve_method_func_name(
&self,
class_name: &str,
type_args: &[Type],
method_name: &str,
) -> Result<String, String> {
if type_args.is_empty()
&& let Some(current_fn) = &self.current_function_name
&& let Some((current_class_part, _)) = current_fn.split_once('.')
&& current_class_part.starts_with(&format!("{}$", class_name))
{
let contextual_name = format!("{}.{}", current_class_part, method_name);
if self.module.get_function(&contextual_name).is_some() {
return Ok(contextual_name);
}
}
let specialized_method_name =
self.create_specialized_method_name(class_name, type_args, method_name);
if self.module.get_function(&specialized_method_name).is_some() {
Ok(specialized_method_name)
} else {
Ok(format!("{}.{}", class_name, method_name))
}
}
fn build_method_call_args(
&mut self,
obj_value: BasicValueEnum<'a>,
args: &[ExpressionNode],
is_specialized: bool,
) -> Result<Vec<BasicMetadataValueEnum<'a>>, String> {
let mut call_args: Vec<BasicMetadataValueEnum<'a>> = vec![obj_value.into()];
for arg in args {
let arg_val = self.generate_expression(arg)?;
if is_specialized {
call_args.push(self.box_value(arg_val).into());
} else {
call_args.push(arg_val.into());
}
}
Ok(call_args)
}
fn handle_method_return_value(
&self,
call: inkwell::values::CallSiteValue<'a>,
return_type: &Type,
) -> Result<BasicValueEnum<'a>, String> {
if let Some(value) = call.try_as_basic_value().left() {
Ok(value)
} else if *return_type == Type::Void {
Ok(self.context.i32_type().const_int(0, false).into())
} else {
Err("Method call failed to return value".to_string())
}
}
pub(super) fn build_net_call(
&mut self,
func_name: &str,
args: &[BasicValueEnum<'a>],
) -> Result<BasicValueEnum<'a>, String> {
let func = self
.runtime_function(func_name)
.ok_or(format!("{} not found", func_name))?;
let metadata_args = args.iter().map(|v| (*v).into()).collect::<Vec<_>>();
let call = self
.builder
.build_call(
func,
&metadata_args,
&format!("{}_call", func_name.replace('.', "_")),
)
.map_err(|e| e.to_string())?;
if let Some(value) = call.try_as_basic_value().left() {
Ok(value)
} else {
Ok(self.context.i32_type().const_int(0, false).into())
}
}
pub(super) fn bool_to_i32(
&mut self,
value: BasicValueEnum<'a>,
) -> Result<BasicValueEnum<'a>, String> {
let int_value = if value.is_int_value() {
value.into_int_value()
} else if value.is_pointer_value() {
self.extract_raw_pointer(value, "mux_value_get_bool", "extract_bool")?
.into_int_value()
} else {
return Err("Expected boolean argument".to_string());
};
let extended = self
.builder
.build_int_z_extend(int_value, self.context.i32_type(), "bool_to_i32")
.map_err(|e| e.to_string())?;
Ok(extended.into())
}
pub(super) fn try_generate_net_static_method_call(
&mut self,
class_name: &str,
method_name: &str,
args: &[ExpressionNode],
) -> Result<Option<BasicValueEnum<'a>>, String> {
fn gen_one_arg<'a>(
s: &mut CodeGenerator<'a>,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
if args.len() != 1 {
return Err("method takes exactly 1 argument".to_string());
}
s.generate_expression(&args[0])
}
match (class_name, method_name) {
("TcpListener", "bind") => {
let addr = gen_one_arg(self, args)?;
let call = self.build_net_call("mux_net_tcp_listener_bind", &[addr])?;
Ok(Some(call))
}
("TcpStream", "connect") => {
let addr = gen_one_arg(self, args)?;
let call = self.build_net_call("mux_net_tcp_connect", &[addr])?;
Ok(Some(call))
}
("UdpSocket", "bind") => {
let addr = gen_one_arg(self, args)?;
let call = self.build_net_call("mux_net_udp_bind", &[addr])?;
Ok(Some(call))
}
_ => Ok(None),
}
}
pub(super) fn try_generate_net_instance_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
obj_type: &Type,
method_name: &str,
args: &[ExpressionNode],
) -> Result<Option<BasicValueEnum<'a>>, String> {
let type_name = if let Type::Named(name, _) = obj_type {
name
} else {
return Ok(None);
};
match type_name.as_str() {
"TcpStream" => match method_name {
"read" => {
let size = gen_one_expr(self, args)?;
let call = self.build_net_call("mux_net_tcp_read", &[obj_value, size])?;
Ok(Some(call))
}
"write" => {
let data = gen_one_expr(self, args)?;
let call = self.build_net_call("mux_net_tcp_write", &[obj_value, data])?;
Ok(Some(call))
}
"close" => {
self.ensure_no_args("close", args)?;
let call = self.build_net_call("mux_net_tcp_close", &[obj_value])?;
Ok(Some(call))
}
"set_nonblocking" => {
let bool_val = gen_one_expr(self, args)?;
let converted = self.bool_to_i32(bool_val)?;
let call = self
.build_net_call("mux_net_tcp_set_nonblocking", &[obj_value, converted])?;
Ok(Some(call))
}
"peer_addr" => {
self.ensure_no_args("peer_addr", args)?;
let call = self.build_net_call("mux_net_tcp_peer_addr", &[obj_value])?;
Ok(Some(call))
}
"local_addr" => {
self.ensure_no_args("local_addr", args)?;
let call = self.build_net_call("mux_net_tcp_local_addr", &[obj_value])?;
Ok(Some(call))
}
_ => Ok(None),
},
"TcpListener" => match method_name {
"accept" => {
self.ensure_no_args("accept", args)?;
let call = self.build_net_call("mux_net_tcp_listener_accept", &[obj_value])?;
Ok(Some(call))
}
"close" => {
self.ensure_no_args("close", args)?;
let call = self.build_net_call("mux_net_tcp_listener_close", &[obj_value])?;
Ok(Some(call))
}
"set_nonblocking" => {
let bool_val = gen_one_expr(self, args)?;
let converted = self.bool_to_i32(bool_val)?;
let call = self.build_net_call(
"mux_net_tcp_listener_set_nonblocking",
&[obj_value, converted],
)?;
Ok(Some(call))
}
"local_addr" => {
self.ensure_no_args("local_addr", args)?;
let call =
self.build_net_call("mux_net_tcp_listener_local_addr", &[obj_value])?;
Ok(Some(call))
}
_ => Ok(None),
},
"UdpSocket" => match method_name {
"send_to" => {
let (data, addr) = gen_two_expr(self, args)?;
let call =
self.build_net_call("mux_net_udp_send_to", &[obj_value, data, addr])?;
Ok(Some(call))
}
"recv_from" => {
let size = gen_one_expr(self, args)?;
let call = self.build_net_call("mux_net_udp_recv_from", &[obj_value, size])?;
Ok(Some(call))
}
"close" => {
self.ensure_no_args("close", args)?;
let call = self.build_net_call("mux_net_udp_close", &[obj_value])?;
Ok(Some(call))
}
"set_nonblocking" => {
let bool_val = gen_one_expr(self, args)?;
let converted = self.bool_to_i32(bool_val)?;
let call = self
.build_net_call("mux_net_udp_set_nonblocking", &[obj_value, converted])?;
Ok(Some(call))
}
"peer_addr" => {
self.ensure_no_args("peer_addr", args)?;
let call = self.build_net_call("mux_net_udp_peer_addr", &[obj_value])?;
Ok(Some(call))
}
"local_addr" => {
self.ensure_no_args("local_addr", args)?;
let call = self.build_net_call("mux_net_udp_local_addr", &[obj_value])?;
Ok(Some(call))
}
_ => Ok(None),
},
_ => Ok(None),
}
}
pub(super) fn try_generate_sync_instance_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
obj_type: &Type,
method_name: &str,
args: &[ExpressionNode],
) -> Result<Option<BasicValueEnum<'a>>, String> {
let type_name = if let Type::Named(name, _) = obj_type {
name
} else {
return Ok(None);
};
match type_name.as_str() {
"Thread" => {
self.ensure_no_args("Thread", args)?;
match method_name {
"join" => self
.call_runtime_function("mux_thread_join", &[obj_value])
.map(Some),
"detach" => self
.call_runtime_function("mux_thread_detach", &[obj_value])
.map(Some),
_ => Ok(None),
}
}
"Mutex" => {
self.ensure_no_args("Mutex", args)?;
match method_name {
"lock" => self
.call_runtime_function("mux_mutex_lock", &[obj_value])
.map(Some),
"unlock" => self
.call_runtime_function("mux_mutex_unlock", &[obj_value])
.map(Some),
_ => Ok(None),
}
}
"RwLock" => {
self.ensure_no_args("RwLock", args)?;
match method_name {
"read_lock" => self
.call_runtime_function("mux_rwlock_read_lock", &[obj_value])
.map(Some),
"write_lock" => self
.call_runtime_function("mux_rwlock_write_lock", &[obj_value])
.map(Some),
"unlock" => self
.call_runtime_function("mux_rwlock_unlock", &[obj_value])
.map(Some),
_ => Ok(None),
}
}
"CondVar" => match method_name {
"wait" => {
if args.len() != 1 {
return Err("wait() method takes exactly 1 argument".to_string());
}
let mutex_val = self.generate_expression(&args[0])?;
let mutex_boxed = self.box_value(mutex_val);
self.call_runtime_function("mux_condvar_wait", &[obj_value, mutex_boxed.into()])
.map(Some)
}
"signal" => {
self.ensure_no_args("signal", args)?;
self.call_runtime_function("mux_condvar_signal", &[obj_value])
.map(Some)
}
"broadcast" => {
self.ensure_no_args("broadcast", args)?;
self.call_runtime_function("mux_condvar_broadcast", &[obj_value])
.map(Some)
}
_ => Ok(None),
},
_ => Ok(None),
}
}
pub(super) fn try_generate_sql_instance_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
obj_type: &Type,
method_name: &str,
args: &[ExpressionNode],
) -> Result<Option<BasicValueEnum<'a>>, String> {
let type_name = if let Type::Named(name, _) = obj_type {
name
} else {
return Ok(None);
};
match type_name.as_str() {
"Connection" => match method_name {
"close" => {
self.ensure_no_args("close", args)?;
self.build_net_call("mux_sql_connection_close", &[obj_value])
.map(Some)
}
"execute" => {
let sql = gen_one_expr(self, args)?;
self.build_net_call("mux_sql_connection_execute", &[obj_value, sql])
.map(Some)
}
"execute_params" => {
let (sql, params) = gen_two_expr(self, args)?;
self.build_net_call(
"mux_sql_connection_execute_params",
&[obj_value, sql, params],
)
.map(Some)
}
"query" => {
let sql = gen_one_expr(self, args)?;
self.build_net_call("mux_sql_connection_query", &[obj_value, sql])
.map(Some)
}
"query_params" => {
let (sql, params) = gen_two_expr(self, args)?;
self.build_net_call(
"mux_sql_connection_query_params",
&[obj_value, sql, params],
)
.map(Some)
}
"begin_transaction" => {
self.ensure_no_args("begin_transaction", args)?;
self.build_net_call("mux_sql_connection_begin_transaction", &[obj_value])
.map(Some)
}
_ => Ok(None),
},
"Transaction" => match method_name {
"commit" => {
self.ensure_no_args("commit", args)?;
self.build_net_call("mux_sql_transaction_commit", &[obj_value])
.map(Some)
}
"rollback" => {
self.ensure_no_args("rollback", args)?;
self.build_net_call("mux_sql_transaction_rollback", &[obj_value])
.map(Some)
}
"execute" => {
let sql = gen_one_expr(self, args)?;
self.build_net_call("mux_sql_transaction_execute", &[obj_value, sql])
.map(Some)
}
"execute_params" => {
let (sql, params) = gen_two_expr(self, args)?;
self.build_net_call(
"mux_sql_transaction_execute_params",
&[obj_value, sql, params],
)
.map(Some)
}
"query" => {
let sql = gen_one_expr(self, args)?;
self.build_net_call("mux_sql_transaction_query", &[obj_value, sql])
.map(Some)
}
"query_params" => {
let (sql, params) = gen_two_expr(self, args)?;
self.build_net_call(
"mux_sql_transaction_query_params",
&[obj_value, sql, params],
)
.map(Some)
}
_ => Ok(None),
},
"ResultSet" => match method_name {
"rows" => {
self.ensure_no_args("rows", args)?;
self.build_net_call("mux_sql_resultset_rows", &[obj_value])
.map(Some)
}
"next" => {
self.ensure_no_args("next", args)?;
self.build_net_call("mux_sql_resultset_next", &[obj_value])
.map(Some)
}
"columns" => {
self.ensure_no_args("columns", args)?;
self.build_net_call("mux_sql_resultset_columns", &[obj_value])
.map(Some)
}
_ => Ok(None),
},
_ => Ok(None),
}
}
pub(super) fn generate_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
obj_type: &Type,
method_name: &str,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
let resolved_obj_type = self.resolve_type(obj_type).map_err(|e| {
format!(
"Unresolved receiver type for method '{}': {}",
method_name, e
)
})?;
if let Some(call) = self.try_generate_net_instance_method_call(
obj_value,
&resolved_obj_type,
method_name,
args,
)? {
return Ok(call);
}
if let Some(call) = self.try_generate_sync_instance_method_call(
obj_value,
&resolved_obj_type,
method_name,
args,
)? {
return Ok(call);
}
if let Some(call) = self.try_generate_sql_instance_method_call(
obj_value,
&resolved_obj_type,
method_name,
args,
)? {
return Ok(call);
}
match &resolved_obj_type {
Type::Primitive(prim) => {
self.generate_primitive_method_call(obj_value, prim, method_name)
}
Type::List(_) => self.generate_list_method_call(obj_value, method_name, args),
Type::Map(_, _) => self.generate_map_method_call(obj_value, method_name, args),
Type::Set(_) => self.generate_set_method_call(obj_value, method_name, args),
Type::Tuple(_, _) => self.generate_tuple_method_call(obj_value, method_name, args),
Type::Named(name, _type_args) if name == "Csv" => {
self.generate_csv_method_call(obj_value, method_name, args)
}
Type::Named(name, _type_args) if name == "Json" => {
self.generate_json_method_call(obj_value, method_name, args)
}
Type::Named(name, _type_args) if name == "SqlValue" => {
self.generate_sql_value_method_call(obj_value, method_name, args)
}
Type::Named(name, type_args) => {
self.invoke_class_instance_method(name, type_args, obj_value, method_name, args)
}
Type::Optional(_) => self.generate_optional_method_call(obj_value, method_name, args),
Type::Result(_, _) => self.generate_result_method_call(obj_value, method_name, args),
_ => Err(format!(
"Method {} not implemented for type {:?}",
method_name, resolved_obj_type
)),
}
}
fn generate_primitive_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
prim: &PrimitiveType,
method_name: &str,
) -> Result<BasicValueEnum<'a>, String> {
match prim {
PrimitiveType::Int => match method_name {
"to_string" => self.call_runtime_to_string(obj_value, "mux_int_to_string"),
"to_float" => {
let float_val = self
.builder
.build_signed_int_to_float(
obj_value.into_int_value(),
self.context.f64_type(),
"int_to_float",
)
.map_err(|e| e.to_string())?;
Ok(float_val.into())
}
"to_int" => Ok(obj_value),
_ => Err(format!("Method {} not implemented for int", method_name)),
},
PrimitiveType::Float => match method_name {
"to_string" => self.call_runtime_to_string(obj_value, "mux_float_to_string"),
"to_int" => {
let float_val = obj_value.into_float_value();
let int_val = self
.builder
.build_float_to_signed_int(
float_val,
self.context.i64_type(),
"float_to_int",
)
.map_err(|e| e.to_string())?;
Ok(int_val.into())
}
"to_float" => Ok(obj_value),
_ => Err(format!("Method {} not implemented for float", method_name)),
},
PrimitiveType::Str => match method_name {
"to_string" => self.call_runtime_to_string(obj_value, "mux_value_to_string"),
"message" => Ok(obj_value),
"to_int" => self.call_string_conversion_func(obj_value, "mux_string_to_int"),
"to_float" => self.call_string_conversion_func(obj_value, "mux_string_to_float"),
"length" => self.call_runtime_function("mux_string_length", &[obj_value]),
_ => Err(format!("Method {} not implemented for string", method_name)),
},
PrimitiveType::Bool => match method_name {
"to_string" => {
let bool_i32 = self.bool_to_i32(obj_value)?;
let bool_func = self
.runtime_function("mux_bool_to_string")
.ok_or("mux_bool_to_string not found".to_string())?;
let call = self
.builder
.build_call(bool_func, &[bool_i32.into()], "bool_to_str")
.map_err(|e| e.to_string())?;
self.call_runtime_to_string_from_call(call)
}
"to_int" => {
let bool_i32 = self.bool_to_i32(obj_value)?.into_int_value();
let int_val = self
.builder
.build_int_z_extend(bool_i32, self.context.i64_type(), "bool_to_int")
.map_err(|e| e.to_string())?;
Ok(int_val.into())
}
"to_float" => {
let bool_i32 = self.bool_to_i32(obj_value)?.into_int_value();
let float_val = self
.builder
.build_unsigned_int_to_float(
bool_i32,
self.context.f64_type(),
"bool_to_float",
)
.map_err(|e| e.to_string())?;
Ok(float_val.into())
}
_ => Err(format!("Method {} not implemented for bool", method_name)),
},
PrimitiveType::Char => match method_name {
"to_string" => self.call_runtime_to_string(obj_value, "mux_char_to_string"),
"to_int" => self.call_runtime_function("mux_char_to_int", &[obj_value]),
_ => Err(format!("Method {} not implemented for char", method_name)),
},
_ => Err(format!(
"Method {} not implemented for primitive type {:?}",
method_name, prim
)),
}
}
fn generate_list_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
method_name: &str,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
match method_name {
"get" => {
self.ensure_arg_count("get", args, 1)?;
let index_val = self.generate_expression(&args[0])?;
self.with_extracted_list(obj_value, |me, raw_list| {
let call = me
.builder
.build_call(
me.runtime_function("mux_list_get")
.expect("mux_list_get must be declared in runtime"),
&[raw_list.into(), index_val.into()],
"list_get",
)
.map_err(|e| e.to_string())?;
Ok(call
.try_as_basic_value()
.left()
.expect("mux_list_get should return a basic value"))
})
}
"push_back" => {
self.ensure_arg_count("push_back", args, 1)?;
let elem_val = self.generate_expression(&args[0])?;
let elem_ptr = self.box_value(elem_val);
self.generate_runtime_call(
"mux_list_push_back_value",
&[obj_value.into(), elem_ptr.into()],
);
Ok(self.context.i32_type().const_int(0, false).into())
}
"pop_back" => {
self.ensure_no_args("pop_back", args)?;
self.call_runtime_function("mux_list_pop_back_value", &[obj_value])
}
"push" => {
self.ensure_arg_count("push", args, 1)?;
let elem_val = self.generate_expression(&args[0])?;
let elem_ptr = self.box_value(elem_val);
self.generate_runtime_call(
"mux_list_push_value",
&[obj_value.into(), elem_ptr.into()],
);
Ok(self.context.i32_type().const_int(0, false).into())
}
"pop" => {
self.ensure_no_args("pop", args)?;
self.call_runtime_function("mux_list_pop_value", &[obj_value])
}
"is_empty" => {
self.ensure_no_args("is_empty", args)?;
self.with_extracted_list(obj_value, |me, raw_list| {
me.call_runtime_function("mux_list_is_empty", &[raw_list])
})
}
"size" => {
self.ensure_no_args("size", args)?;
self.with_extracted_list(obj_value, |me, raw_list| {
me.call_runtime_function("mux_list_length", &[raw_list])
})
}
"to_string" => {
self.ensure_no_args("to_string", args)?;
self.with_extracted_list(obj_value, |me, raw_list| {
let cstr = me.call_runtime_function("mux_list_to_string", &[raw_list])?;
me.call_cstr_to_mux_string(cstr)
})
}
_ => Err(format!("Method {} not implemented for lists", method_name)),
}
}
fn generate_csv_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
method_name: &str,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
match method_name {
"stringify" => {
if !args.is_empty() {
return Err("stringify() expects no arguments".to_string());
}
self.call_runtime_function("mux_csv_to_string", &[obj_value])
}
_ => Err(format!("Method {} not implemented for Csv", method_name)),
}
}
fn generate_json_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
method_name: &str,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
match method_name {
"stringify" => {
if args.len() != 1 {
return Err(
"stringify() expects exactly 1 argument (optional indent)".to_string()
);
}
let indent_arg = self.generate_expression(&args[0])?;
self.call_runtime_function("mux_json_stringify", &[obj_value, indent_arg])
}
_ => Err(format!("Method {} not implemented for Json", method_name)),
}
}
fn generate_sql_value_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
method_name: &str,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
match method_name {
"is_null" => {
self.ensure_no_args("is_null", args)?;
self.call_runtime_function("mux_sql_value_is_null", &[obj_value])
}
"as_bool" => {
self.ensure_no_args("as_bool", args)?;
self.call_runtime_function("mux_sql_value_as_bool", &[obj_value])
}
"as_int" => {
self.ensure_no_args("as_int", args)?;
self.call_runtime_function("mux_sql_value_as_int", &[obj_value])
}
"as_float" => {
self.ensure_no_args("as_float", args)?;
self.call_runtime_function("mux_sql_value_as_float", &[obj_value])
}
"as_string" => {
self.ensure_no_args("as_string", args)?;
self.call_runtime_function("mux_sql_value_as_string", &[obj_value])
}
"as_bytes" => {
self.ensure_no_args("as_bytes", args)?;
self.call_runtime_function("mux_sql_value_as_bytes", &[obj_value])
}
"to_string" => {
self.ensure_no_args("to_string", args)?;
self.generate_to_string_call(obj_value)
}
_ => Err(format!(
"Method {} not implemented for SqlValue",
method_name
)),
}
}
fn generate_map_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
method_name: &str,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
match method_name {
"to_string" => {
self.ensure_no_args("to_string", args)?;
self.generate_to_string_call(obj_value)
}
"put" => {
if args.len() != 2 {
return Err("put() method takes exactly 2 arguments".to_string());
}
let key_val = self.generate_expression(&args[0])?;
let value_val = self.generate_expression(&args[1])?;
let boxed_key = self.box_value(key_val);
let boxed_value = self.box_value(value_val);
self.builder
.build_call(
self.runtime_function("mux_map_put_value")
.expect("mux_map_put_value must be declared in runtime"),
&[obj_value.into(), boxed_key.into(), boxed_value.into()],
"map_put_value",
)
.map_err(|e| e.to_string())?;
Ok(self.context.i64_type().const_int(0, false).into())
}
"get" => {
if args.len() != 1 {
return Err("get() method takes exactly 1 argument".to_string());
}
let key_val = self.generate_expression(&args[0])?;
self.with_extracted_map(obj_value, |me, extract_map| {
let result = me
.builder
.build_call(
me.runtime_function("mux_map_get")
.expect("mux_map_get must be declared in runtime"),
&[extract_map.into(), key_val.into()],
"map_get",
)
.map_err(|e| e.to_string())?
.try_as_basic_value()
.left()
.expect("mux_map_get should return a basic value");
Ok(result)
})
}
"get_keys" => {
self.ensure_no_args("get_keys", args)?;
self.with_extracted_map(obj_value, |me, extract_map| {
me.call_runtime_function("mux_map_keys", &[extract_map])
})
}
"get_values" => {
self.ensure_no_args("get_values", args)?;
self.with_extracted_map(obj_value, |me, extract_map| {
me.call_runtime_function("mux_map_values", &[extract_map])
})
}
"get_pairs" => {
self.ensure_no_args("get_pairs", args)?;
self.with_extracted_map(obj_value, |me, extract_map| {
me.call_runtime_function("mux_map_pairs", &[extract_map])
})
}
"contains" => {
if args.len() != 1 {
return Err("contains() method takes exactly 1 argument".to_string());
}
let key_val = self.generate_expression(&args[0])?;
self.with_extracted_map(obj_value, |me, extract_map| {
let call = me
.builder
.build_call(
me.runtime_function("mux_map_contains")
.expect("mux_map_contains must be declared in runtime"),
&[extract_map.into(), key_val.into()],
"map_contains",
)
.map_err(|e| e.to_string())?;
Ok(call
.try_as_basic_value()
.left()
.expect("mux_map_contains should return a basic value"))
})
}
"size" => {
self.ensure_no_args("size", args)?;
self.with_extracted_map(obj_value, |me, extract_map| {
me.call_runtime_function("mux_map_size", &[extract_map])
})
}
"is_empty" => {
self.ensure_no_args("is_empty", args)?;
self.with_extracted_map(obj_value, |me, extract_map| {
me.call_runtime_function("mux_map_is_empty", &[extract_map])
})
}
"remove" => {
self.ensure_arg_count("remove", args, 1)?;
let key_val = self.generate_expression(&args[0])?;
let key_ptr = self.box_value(key_val);
let optional_ptr = self
.generate_runtime_call(
"mux_map_remove_value",
&[obj_value.into(), key_ptr.into()],
)
.ok_or("mux_map_remove_value should return a value")?;
Ok(optional_ptr)
}
_ => Err(format!("Method {} not implemented for maps", method_name)),
}
}
fn generate_set_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
method_name: &str,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
match method_name {
"to_string" => {
self.ensure_no_args("to_string", args)?;
self.generate_to_string_call(obj_value)
}
"add" => {
self.ensure_arg_count("add", args, 1)?;
let elem_val = self.generate_expression(&args[0])?;
let elem_ptr = self.box_value(elem_val);
self.generate_runtime_call(
"mux_set_add_value",
&[obj_value.into(), elem_ptr.into()],
);
Ok(self.context.i32_type().const_int(0, false).into())
}
"remove" => {
self.ensure_arg_count("remove", args, 1)?;
let elem_val = self.generate_expression(&args[0])?;
let elem_ptr = self.box_value(elem_val);
self.call_runtime_function("mux_set_remove_value", &[obj_value, elem_ptr.into()])
}
"contains" => {
self.ensure_arg_count("contains", args, 1)?;
let elem_val = self.generate_expression(&args[0])?;
let elem_ptr = self.box_value(elem_val);
self.with_extracted_set(obj_value, |me, extract_set| {
me.call_runtime_function("mux_set_contains", &[extract_set, elem_ptr.into()])
})
}
"size" => {
self.ensure_no_args("size", args)?;
self.with_extracted_set(obj_value, |me, extract_set| {
me.call_runtime_function("mux_set_size", &[extract_set])
})
}
"is_empty" => {
self.ensure_no_args("is_empty", args)?;
self.with_extracted_set(obj_value, |me, extract_set| {
me.call_runtime_function("mux_set_is_empty", &[extract_set])
})
}
_ => Err(format!("Method {} not implemented for sets", method_name)),
}
}
fn generate_optional_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
method_name: &str,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
match method_name {
"to_string" => {
self.ensure_no_args("to_string", args)?;
self.generate_to_string_call(obj_value)
}
"is_some" => {
self.ensure_no_args("is_some", args)?;
self.call_unary_predicate(obj_value, "mux_optional_is_some")
}
"is_none" => {
self.ensure_no_args("is_none", args)?;
self.call_unary_predicate(obj_value, "mux_optional_is_none")
}
_ => Err(format!(
"Method {} not implemented for Optionals",
method_name
)),
}
}
fn generate_result_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
method_name: &str,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
match method_name {
"to_string" => {
self.ensure_no_args("to_string", args)?;
self.generate_to_string_call(obj_value)
}
"is_ok" => {
self.ensure_no_args("is_ok", args)?;
self.call_unary_predicate(obj_value, "mux_result_is_ok")
}
"is_err" => {
self.ensure_no_args("is_err", args)?;
self.call_unary_predicate(obj_value, "mux_result_is_err")
}
_ => Err(format!(
"Method {} not implemented for Results",
method_name
)),
}
}
fn generate_tuple_method_call(
&mut self,
obj_value: BasicValueEnum<'a>,
method_name: &str,
args: &[ExpressionNode],
) -> Result<BasicValueEnum<'a>, String> {
match method_name {
"to_string" => {
self.ensure_no_args("to_string", args)?;
let tuple_ptr = if obj_value.is_pointer_value() {
self.extract_raw_pointer(obj_value, "mux_value_get_tuple", "get_tuple")?
.into_pointer_value()
} else {
return Err("Tuple method receiver must be a pointer value".to_string());
};
let cstr =
self.call_runtime_function("mux_tuple_to_string", &[tuple_ptr.into()])?;
self.call_cstr_to_mux_string(cstr)
}
_ => Err(format!("Method {} not implemented for tuples", method_name)),
}
}
}