use super::*;
impl Codegen {
pub(super) fn function(
&self,
span: Span,
emit: &mut Emit,
out: &mut String,
) -> Result<(), Diagnostic> {
let info = self.fn_info(emit, span);
self.emit_callable(
&func_wrapper(emit.file_id, &info.name),
&func_body(emit.file_id, &info.name),
&[],
&info.params,
&info.body,
&info.cell_names,
emit,
out,
)
}
pub(super) fn method(
&self,
class: &Class,
span: Span,
emit: &mut Emit,
out: &mut String,
) -> Result<(), Diagnostic> {
let info = self.fn_info(emit, span);
self.emit_callable(
&format!("{METHOD_PREFIX}{}_{}", class.id, info.name),
&format!("{METHOD_BODY_PREFIX}{}_{}", class.id, info.name),
&[],
&info.params,
&info.body,
&info.cell_names,
emit,
out,
)
}
pub(super) fn closure(
&self,
info: &FnInfo,
emit: &mut Emit,
out: &mut String,
) -> Result<(), Diagnostic> {
let id = info.fn_id.expect("compiler bug: closure without an id");
self.emit_callable(
&format!("{CLOSURE_PREFIX}{id}"),
&format!("{CLOSURE_BODY_PREFIX}{id}"),
&info.captures,
&info.params,
&info.body,
&info.cell_names,
emit,
out,
)
}
#[allow(clippy::too_many_arguments)]
pub(super) fn emit_callable(
&self,
wrapper_name: &str,
body_name: &str,
captures: &[String],
params: &[String],
body: &[Stmt],
cell_names: &HashSet<String>,
emit: &mut Emit,
out: &mut String,
) -> Result<(), Diagnostic> {
let wrapper_params = signature(captures, params, false);
out.push_str(&format!(
"\nfn {wrapper_name}({wrapper_params}) -> DogeResult<Value> {{\n"
));
out.push_str(" enter_call(&mut env.depth)?;\n");
let call_args = {
let mut v: Vec<String> = captures
.iter()
.chain(params.iter())
.map(|p| format!("{NAME_PREFIX}{p}"))
.collect();
v.push("env".to_string());
v.join(", ")
};
out.push_str(&format!(" let result = {body_name}({call_args});\n"));
out.push_str(" exit_call(&mut env.depth);\n");
out.push_str(" result\n");
out.push_str("}\n");
let body_params = signature(captures, params, true);
out.push_str(&format!(
"\nfn {body_name}({body_params}) -> DogeResult<Value> {{\n"
));
let mut locals: HashMap<String, Local> = HashMap::new();
for name in captures {
locals.insert(name.clone(), Local::Cell);
}
for param in params {
if cell_names.contains(param) {
out.push_str(&format!(
" let {NAME_PREFIX}{param} = Rc::new(RefCell::new({NAME_PREFIX}{param}));\n"
));
locals.insert(param.clone(), Local::Cell);
} else {
locals.insert(param.clone(), Local::Plain);
}
}
for name in hoisted_names(body) {
if params.iter().any(|p| p == &name) {
continue;
}
if cell_names.contains(&name) {
out.push_str(&format!(
" let {NAME_PREFIX}{name}: Cell = Rc::new(RefCell::new(Value::None));\n"
));
locals.insert(name, Local::Cell);
} else {
out.push_str(&format!(
" let mut {NAME_PREFIX}{name}: Value = Value::None;\n"
));
locals.insert(name, Local::Plain);
}
}
emit.locals = locals;
emit.local_funcs = child_funcdefs(body)
.into_iter()
.map(|(name, params, _, _)| (name.to_string(), params.clone()))
.collect();
emit.try_stack.clear();
emit.loop_stack.clear();
for stmt in body {
self.stmt(stmt, 1, emit, out)?;
}
out.push_str(" Ok(Value::None)\n");
out.push_str("}\n");
Ok(())
}
pub(super) fn constructor(&self, classes: &[Class], class: &Class, out: &mut String) {
let init = effective_init(classes, class);
let init_binding = init
.map(|(_, params)| params.binding_names())
.unwrap_or_default();
let ctor_params = signature(&[], &init_binding, false);
out.push_str(&format!(
"\nfn {CTOR_PREFIX}{}({ctor_params}) -> DogeResult<Value> {{\n",
class.id
));
out.push_str(&format!(
" let obj = Value::object({}u32, \"{}\");\n",
class.id,
escape_str(&class.name)
));
if let Some((def, _)) = init {
let mut args: Vec<String> = vec!["obj.clone()".to_string()];
args.extend(init_binding.iter().map(|p| format!("{NAME_PREFIX}{p}")));
args.push("env".to_string());
out.push_str(&format!(
" {METHOD_PREFIX}{}_init({})?;\n",
def.id,
args.join(", ")
));
}
out.push_str(" Ok(obj)\n");
out.push_str("}\n");
}
}