use std::{mem, rc::Rc};
use super::{variables::VariableAllocation, CodeGenerator, Expression, ExpressionResult};
use crate::{
ll::{
ast::{Ast, NodeId},
bytecode::{Function, FunctionIndex, FunctionKind, Opcode, Opr24},
error::{LanguageError, LanguageErrorKind},
},
FunctionParameterCount,
};
#[derive(Debug, Clone, Copy)]
pub(super) enum FunctionCallConv {
Bare,
Static,
Instance,
Constructor { allow_new_fields: bool },
}
impl FunctionCallConv {
fn has_field_access(&self) -> bool {
matches!(self, Self::Instance | Self::Constructor { .. })
}
fn has_visible_self(&self) -> bool {
matches!(self, Self::Static | Self::Instance)
}
fn is_constructor(&self) -> bool {
matches!(self, Self::Constructor { .. })
}
fn allow_new_fields(&self) -> bool {
matches!(self, Self::Constructor { allow_new_fields: true })
}
}
pub(super) struct GenerateFunctionOptions {
pub(crate) name: Rc<str>,
pub(crate) call_conv: FunctionCallConv,
}
pub(super) struct GeneratedFunction {
pub(crate) id: FunctionIndex,
pub(crate) parameter_count: u16,
}
impl<'e> CodeGenerator<'e> {
pub(super) fn generate_function(
&mut self,
ast: &Ast,
node: NodeId,
GenerateFunctionOptions { name, call_conv }: GenerateFunctionOptions,
) -> Result<GeneratedFunction, LanguageError> {
let (head, body) = ast.node_pair(node);
let (_, parameters) = ast.node_pair(head);
let parameter_list = ast.children(parameters).unwrap();
let mut generator =
CodeGenerator::new(Rc::clone(&self.chunk.module_name), self.env, self.builtin_traits);
generator.locals.parent = Some(mem::take(&mut self.locals));
if call_conv.has_field_access() {
generator.struct_data = self.struct_data.take();
generator.allow_new_fields = call_conv.allow_new_fields();
}
generator.is_constructor = call_conv.is_constructor();
generator.push_scope();
let receiver = if call_conv.has_visible_self() {
let receiver = generator.create_variable("self", VariableAllocation::Inherit).unwrap();
if let Some(struct_data) = generator.struct_data.as_deref_mut() {
struct_data.receiver = Some(receiver);
}
receiver
} else {
generator.create_variable("<receiver>", VariableAllocation::Inherit).unwrap()
};
for ¶meter in parameter_list {
let parameter_name = ast.string(parameter).unwrap();
generator
.create_variable(parameter_name, VariableAllocation::Inherit)
.map_err(|kind| ast.error(parameter, kind))?;
}
let create_struct = if call_conv.is_constructor() {
generator.generate_variable_load(receiver);
let instruction = generator.chunk.emit(Opcode::Nop);
let receiver = generator
.create_variable("self", VariableAllocation::Allocate)
.map_err(|kind| ast.error(node, kind))?;
generator.generate_variable_assign(receiver);
generator.chunk.emit(Opcode::Discard);
generator.struct_data.as_deref_mut().unwrap().receiver = Some(receiver);
Some(instruction)
} else {
None
};
generator.generate_node(ast, body, Expression::Used)?;
if let Some(create_struct) = create_struct {
let field_count = generator.struct_data.as_ref().unwrap().fields.len();
let field_count = Opr24::try_from(field_count).map_err(|_| {
ast.error(ast.node_pair(parameters).0, LanguageErrorKind::TooManyFields)
})?;
generator.chunk.patch(create_struct, (Opcode::CreateStruct, field_count));
generator.chunk.emit(Opcode::Discard);
let receiver = generator.struct_data.as_ref().unwrap().receiver.unwrap();
generator.generate_variable_load(receiver);
}
if call_conv.is_constructor() && !call_conv.allow_new_fields() {
let struct_data = generator.struct_data.as_ref().unwrap();
let missing: Vec<_> = struct_data
.fields
.keys()
.filter(|field| !generator.assigned_fields.contains(*field))
.cloned()
.collect();
if !missing.is_empty() {
return Err(ast.error(node, LanguageErrorKind::MissingFields(missing)));
}
}
generator.pop_scope();
generator.chunk.emit(Opcode::Return);
self.locals = generator.locals.parent.take().unwrap();
if call_conv.has_field_access() {
self.struct_data = generator.struct_data;
}
let parameter_count = u16::try_from(parameter_list.len())
.map_err(|_| ast.error(parameters, LanguageErrorKind::TooManyParameters))?;
let function = Function {
name,
parameter_count: FunctionParameterCount::Fixed(parameter_count),
kind: FunctionKind::Bytecode {
chunk: Rc::new(generator.chunk),
captured_locals: generator.locals.captures,
},
hidden_in_stack_traces: false,
};
let function_id =
self.env.create_function(function).map_err(|kind| ast.error(node, kind))?;
Ok(GeneratedFunction { id: function_id, parameter_count })
}
fn ensure_valid_bare_function(ast: &Ast, node: NodeId) -> Result<(), LanguageError> {
let (head, body) = ast.node_pair(node);
let (_name, parameters) = ast.node_pair(head);
let function_kind = ast.node_pair(parameters).0;
if function_kind != NodeId::EMPTY {
return Err(ast.error(function_kind, LanguageErrorKind::FunctionKindOutsideImpl));
}
if body == NodeId::EMPTY {
return Err(ast.error(node, LanguageErrorKind::MissingFunctionBody));
}
Ok(())
}
pub(super) fn generate_function_declaration(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, LanguageError> {
Self::ensure_valid_bare_function(ast, node)?;
let (head, _) = ast.node_pair(node);
let (name_node, _) = ast.node_pair(head);
let name = ast.string(name_node).unwrap();
let variable = self
.create_variable(name, VariableAllocation::Allocate)
.map_err(|kind| ast.error(name_node, kind))?;
let function = self.generate_function(
ast,
node,
GenerateFunctionOptions { name: Rc::clone(name), call_conv: FunctionCallConv::Bare },
)?;
self.chunk.emit((Opcode::CreateClosure, function.id.to_opr24()));
self.generate_variable_sink(variable);
Ok(ExpressionResult::Absent)
}
pub(super) fn generate_function_expression(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, LanguageError> {
Self::ensure_valid_bare_function(ast, node)?;
let function = self.generate_function(
ast,
node,
GenerateFunctionOptions {
name: Rc::from("<anonymous>"),
call_conv: FunctionCallConv::Bare,
},
)?;
self.chunk.emit((Opcode::CreateClosure, function.id.to_opr24()));
Ok(ExpressionResult::Present)
}
}