use std::collections::{HashMap, HashSet};
use std::mem;
use std::rc::Rc;
use crate::ast::{Ast, NodeId, NodeKind};
use crate::bytecode::{
BuiltinTraits, CaptureKind, Chunk, Environment, Function, FunctionKind, FunctionSignature,
Opcode, Opr24, Prototype,
};
use crate::common::{Error, ErrorKind, Location, RenderedSignature};
#[derive(Debug)]
struct Variable {
stack_slot: Opr24,
is_captured: bool,
}
#[derive(Debug, Default)]
struct Scope {
variables_by_name: HashMap<String, Variable>,
allocated_variable_count: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum VariablePlace {
Global(Opr24),
Local(Opr24),
Upvalue(Opr24),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum VariableAllocation {
Inherit,
Allocate,
}
#[derive(Default)]
struct Locals {
parent: Option<Box<Self>>,
scopes: Vec<Scope>,
local_count: u32,
allocated_local_count: u32,
captures: Vec<CaptureKind>,
}
impl Locals {
fn create_local(
&mut self,
name: &str,
allocation: VariableAllocation,
) -> Result<VariablePlace, ErrorKind> {
let slot = self.local_count;
let slot = Opr24::new(slot).map_err(|_| ErrorKind::TooManyLocals)?;
let scope = self.scopes.last_mut().unwrap();
scope.variables_by_name.insert(
name.to_owned(),
Variable {
stack_slot: slot,
is_captured: false,
},
);
self.local_count += 1;
if allocation == VariableAllocation::Allocate {
self.allocated_local_count += 1;
scope.allocated_variable_count += 1;
}
Ok(VariablePlace::Local(slot))
}
fn variables_in_scope_mut(&mut self) -> impl Iterator<Item = (&str, &'_ mut Variable)> {
self
.scopes
.iter_mut()
.rev()
.flat_map(|scope| scope.variables_by_name.iter_mut().map(|(s, v)| (s.as_str(), v)))
}
fn capture_index(&mut self, capture: CaptureKind) -> Result<Opr24, ErrorKind> {
let index = self.captures.iter().rposition(|c| c.eq(&capture)).unwrap_or_else(|| {
let index = self.captures.len();
self.captures.push(capture);
index
});
Opr24::try_from(index).map_err(|_| ErrorKind::TooManyCaptures)
}
fn lookup(&mut self, name: &str) -> Result<Option<VariablePlace>, ErrorKind> {
for scope in self.scopes.iter().rev() {
if let Some(var) = scope.variables_by_name.get(name) {
return Ok(Some(VariablePlace::Local(var.stack_slot)));
}
}
if let Some(parent) = self.parent.as_mut() {
if let Some(place) = parent.lookup(name)? {
match place {
VariablePlace::Local(local_slot) => {
let (_, variable) = parent
.variables_in_scope_mut()
.find(|(_, var)| var.stack_slot == local_slot)
.unwrap();
variable.is_captured = true;
let stack_slot = variable.stack_slot;
let upvalue_index = self.capture_index(CaptureKind::Local(stack_slot))?;
return Ok(Some(VariablePlace::Upvalue(upvalue_index)));
}
VariablePlace::Upvalue(upvalue_index) => {
let own_index = self.capture_index(CaptureKind::Upvalue(upvalue_index))?;
return Ok(Some(VariablePlace::Upvalue(own_index)));
}
VariablePlace::Global(_) => unreachable!(),
}
}
}
Ok(None)
}
fn push_scope(&mut self) {
self.scopes.push(Default::default());
}
fn pop_scope(&mut self) -> Scope {
let scope = self.scopes.pop().expect("no scopes left on the stack");
self.local_count -= scope.variables_by_name.len() as u32;
self.allocated_local_count -= scope.allocated_variable_count;
scope
}
}
#[derive(Debug, Default)]
struct BreakableBlock {
breaks: Vec<usize>,
start: usize,
}
#[derive(Debug, Default)]
struct StructData {
fields: HashMap<Rc<str>, Opr24>,
receiver: Option<VariablePlace>,
}
impl StructData {
fn get_or_create_field(&mut self, name: &str) -> Result<Opr24, ErrorKind> {
if !self.fields.contains_key(name) {
let index = Opr24::try_from(self.fields.len()).map_err(|_| ErrorKind::TooManyFields)?;
self.fields.insert(Rc::from(name), index);
Ok(index)
} else {
Ok(*self.fields.get(name).unwrap())
}
}
fn get_field(&self, name: &str) -> Option<Opr24> {
self.fields.get(name).copied()
}
}
pub struct CodeGenerator<'e> {
env: &'e mut Environment,
builtin_traits: &'e BuiltinTraits,
chunk: Chunk,
locals: Box<Locals>,
breakable_blocks: Vec<BreakableBlock>,
struct_data: Option<Box<StructData>>,
allow_new_fields: bool,
is_constructor: bool,
assigned_fields: HashSet<Rc<str>>,
}
impl<'e> CodeGenerator<'e> {
pub fn new(
module_name: Rc<str>,
env: &'e mut Environment,
builtin_traits: &'e BuiltinTraits,
) -> Self {
Self {
env,
chunk: Chunk::new(module_name),
builtin_traits,
locals: Default::default(),
breakable_blocks: Vec::new(),
struct_data: None,
allow_new_fields: false,
is_constructor: false,
assigned_fields: HashSet::new(),
}
}
fn create_variable(
&mut self,
name: &str,
allocation: VariableAllocation,
) -> Result<VariablePlace, ErrorKind> {
if !self.locals.scopes.is_empty() {
let place = self.locals.create_local(name, allocation)?;
self.chunk.preallocate_stack_slots =
self.chunk.preallocate_stack_slots.max(self.locals.allocated_local_count);
Ok(place)
} else {
let slot = self.env.create_global(name)?;
Ok(VariablePlace::Global(slot))
}
}
fn lookup_variable(&mut self, name: &str) -> Result<Option<VariablePlace>, ErrorKind> {
if let Some(place) = self.locals.lookup(name)? {
return Ok(Some(place));
}
Ok(self.env.get_global(name).map(VariablePlace::Global))
}
fn push_scope(&mut self) {
self.locals.push_scope();
}
fn pop_scope(&mut self) {
let scope = self.locals.pop_scope();
for variable in scope.variables_by_name.into_values() {
if variable.is_captured {
self.chunk.emit((Opcode::CloseLocal, variable.stack_slot));
}
}
}
fn generate_variable_load(&mut self, variable: VariablePlace) {
self.chunk.emit(match variable {
VariablePlace::Global(slot) => (Opcode::GetGlobal, slot),
VariablePlace::Local(slot) => (Opcode::GetLocal, slot),
VariablePlace::Upvalue(slot) => (Opcode::GetUpvalue, slot),
});
}
fn generate_variable_assign(&mut self, variable: VariablePlace) {
self.chunk.emit(match variable {
VariablePlace::Global(slot) => (Opcode::AssignGlobal, slot),
VariablePlace::Local(slot) => (Opcode::AssignLocal, slot),
VariablePlace::Upvalue(slot) => (Opcode::AssignUpvalue, slot),
});
}
fn generate_variable_sink(&mut self, variable: VariablePlace) {
self.chunk.emit(match variable {
VariablePlace::Global(slot) => (Opcode::SinkGlobal, slot),
VariablePlace::Local(slot) => (Opcode::SinkLocal, slot),
VariablePlace::Upvalue(slot) => (Opcode::SinkUpvalue, slot),
});
}
fn push_breakable_block(&mut self) {
let start = self.chunk.emit(Opcode::Nop);
self.breakable_blocks.push(BreakableBlock {
breaks: Vec::new(),
start,
});
}
fn pop_breakable_block(&mut self) {
let block = self.breakable_blocks.pop().unwrap();
if !block.breaks.is_empty() {
self.chunk.patch(block.start, Opcode::EnterBreakableBlock);
for jump in block.breaks {
self.chunk.patch(jump, Opcode::jump_forward(jump, self.chunk.len()).unwrap());
}
self.chunk.emit((Opcode::ExitBreakableBlock, 1));
}
}
fn generate_node_list(&mut self, ast: &Ast, nodes: &[NodeId]) -> Result<(), Error> {
if nodes.is_empty() {
let _ = self.generate_nil();
} else {
for (i, &node) in nodes.iter().enumerate() {
self.generate_node(
ast,
node,
if i != nodes.len() - 1 {
Expression::Discarded
} else {
Expression::Used
},
)?;
}
}
Ok(())
}
fn generate_nil(&mut self) -> ExpressionResult {
self.chunk.emit(Opcode::PushNil);
ExpressionResult::Present
}
fn generate_boolean(&mut self, ast: &Ast, node: NodeId) -> ExpressionResult {
self.chunk.emit(match ast.kind(node) {
NodeKind::True => Opcode::PushTrue,
NodeKind::False => Opcode::PushFalse,
_ => unreachable!(),
});
ExpressionResult::Present
}
fn generate_number(&mut self, ast: &Ast, node: NodeId) -> ExpressionResult {
self.chunk.emit(Opcode::PushNumber);
let number = ast.number(node).unwrap();
self.chunk.emit_number(number);
ExpressionResult::Present
}
fn generate_string(&mut self, ast: &Ast, node: NodeId) -> ExpressionResult {
self.chunk.emit(Opcode::PushString);
let string = ast.string(node).unwrap();
self.chunk.emit_string(string);
ExpressionResult::Present
}
fn generate_unary(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (left, _) = ast.node_pair(node);
self.generate_node(ast, left, Expression::Used)?;
match ast.kind(node) {
NodeKind::Negate => self.chunk.emit(Opcode::Negate),
NodeKind::Not => self.chunk.emit(Opcode::Not),
_ => unreachable!(),
};
Ok(ExpressionResult::Present)
}
fn generate_binary(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (left, right) = ast.node_pair(node);
self.generate_node(ast, left, Expression::Used)?;
self.generate_node(ast, right, Expression::Used)?;
match ast.kind(node) {
NodeKind::Negate => self.chunk.emit(Opcode::Negate),
NodeKind::Add => self.chunk.emit(Opcode::Add),
NodeKind::Subtract => self.chunk.emit(Opcode::Subtract),
NodeKind::Multiply => self.chunk.emit(Opcode::Multiply),
NodeKind::Divide => self.chunk.emit(Opcode::Divide),
NodeKind::Equal => self.chunk.emit(Opcode::Equal),
NodeKind::NotEqual => {
self.chunk.emit(Opcode::Equal);
self.chunk.emit(Opcode::Not)
}
NodeKind::Less => self.chunk.emit(Opcode::Less),
NodeKind::LessEqual => self.chunk.emit(Opcode::LessEqual),
NodeKind::Greater => {
self.chunk.emit(Opcode::Swap);
self.chunk.emit(Opcode::Less)
}
NodeKind::GreaterEqual => {
self.chunk.emit(Opcode::Swap);
self.chunk.emit(Opcode::LessEqual)
}
_ => unreachable!(),
};
Ok(ExpressionResult::Present)
}
fn generate_variable(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let name = ast.string(node).unwrap();
if let Some(variable) = self.lookup_variable(name).map_err(|kind| ast.error(node, kind))? {
self.generate_variable_load(variable);
Ok(ExpressionResult::Present)
} else {
Err(ast.error(node, ErrorKind::VariableDoesNotExist(name.to_owned())))
}
}
fn generate_field(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (name, _) = ast.node_pair(node);
let name = ast.string(name).unwrap();
let struct_data = self
.struct_data
.as_deref()
.ok_or_else(|| ast.error(node, ErrorKind::FieldOutsideOfImpl))?;
let field_id = struct_data
.get_field(name)
.ok_or_else(|| ast.error(node, ErrorKind::FieldDoesNotExist(Rc::clone(name))))?;
let receiver = struct_data.receiver.unwrap();
self.generate_variable_load(receiver);
self.chunk.emit((Opcode::GetField, field_id));
Ok(ExpressionResult::Present)
}
fn generate_pattern_destructuring(&mut self, ast: &Ast, node: NodeId) -> Result<(), Error> {
match ast.kind(node) {
NodeKind::Identifier => {
let name = ast.string(node).unwrap();
let variable = self
.create_variable(name, VariableAllocation::Allocate)
.map_err(|kind| ast.error(node, kind))?;
self.generate_variable_sink(variable);
}
_ => return Err(ast.error(node, ErrorKind::InvalidPattern)),
}
Ok(())
}
fn generate_assignment(
&mut self,
ast: &Ast,
node: NodeId,
result: Expression,
) -> Result<ExpressionResult, Error> {
let (target, value) = ast.node_pair(node);
self.generate_node(ast, value, Expression::Used)?;
match ast.kind(target) {
NodeKind::Identifier => {
let name = ast.string(target).unwrap();
let variable = if let Some(slot) =
self.lookup_variable(name).map_err(|kind| ast.error(target, kind))?
{
slot
} else {
self
.create_variable(name, VariableAllocation::Allocate)
.map_err(|kind| ast.error(node, kind))?
};
match result {
Expression::Used => self.generate_variable_assign(variable),
Expression::Discarded => self.generate_variable_sink(variable),
}
}
NodeKind::Field => {
let (name, _) = ast.node_pair(target);
let name = ast.string(name).unwrap();
if let Some(struct_data) = self.struct_data.as_mut() {
let field = if self.allow_new_fields {
struct_data.get_or_create_field(name).map_err(|kind| ast.error(node, kind))?
} else {
struct_data.get_field(name).ok_or_else(|| {
ast.error(target, ErrorKind::FieldDoesNotExist(Rc::clone(name)))
})?
};
let receiver = struct_data.receiver.unwrap();
self.generate_variable_load(receiver);
self.chunk.emit(match result {
Expression::Used => (Opcode::AssignField, field),
Expression::Discarded => (Opcode::SinkField, field),
});
} else {
return Err(ast.error(target, ErrorKind::FieldOutsideOfImpl));
}
if self.is_constructor && !self.allow_new_fields {
self.assigned_fields.insert(Rc::clone(name));
}
}
_ => return Err(ast.error(target, ErrorKind::InvalidAssignment)),
}
Ok(match result {
Expression::Discarded => ExpressionResult::Absent,
Expression::Used => ExpressionResult::Present,
})
}
fn generate_list(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let children = ast.children(node).unwrap();
let len =
Opr24::try_from(children.len()).map_err(|_| ast.error(node, ErrorKind::ListIsTooLong))?;
for &child in children {
self.generate_node(ast, child, Expression::Used)?;
}
self.chunk.emit((Opcode::CreateList, len));
Ok(ExpressionResult::Present)
}
fn generate_dict(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let children = ast.children(node).unwrap();
let len =
Opr24::try_from(children.len()).map_err(|_| ast.error(node, ErrorKind::DictIsTooLarge))?;
for &child in children {
let (key, value) = ast.node_pair(child);
self.generate_node(ast, key, Expression::Used)?;
self.generate_node(ast, value, Expression::Used)?;
}
self.chunk.emit((Opcode::CreateDict, len));
Ok(ExpressionResult::Present)
}
fn generate_do(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let children = ast.children(node).unwrap();
self.push_scope();
self.generate_node_list(ast, children)?;
self.pop_scope();
Ok(ExpressionResult::Present)
}
fn generate_if(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let branches = ast.children(node).unwrap();
let mut jumps_to_end = Vec::new();
for (i, &branch) in branches.iter().enumerate() {
if i > 0 {
self.chunk.emit(Opcode::Discard);
}
let then = ast.children(branch).unwrap();
match ast.kind(branch) {
NodeKind::IfBranch => {
let (condition, _) = ast.node_pair(branch);
self.push_scope();
self.generate_node(ast, condition, Expression::Used)?;
let jump = self.chunk.emit(Opcode::Nop);
self.chunk.emit(Opcode::Discard); self.generate_node_list(ast, then)?;
self.pop_scope();
let jump_to_end = self.chunk.emit(Opcode::Nop);
jumps_to_end.push(jump_to_end);
self.chunk.patch(
jump,
Opcode::jump_forward_if_falsy(jump, self.chunk.len())
.map_err(|_| ast.error(branch, ErrorKind::IfBranchTooLarge))?,
);
}
NodeKind::ElseBranch => {
self.push_scope();
self.generate_node_list(ast, then)?;
self.pop_scope();
}
_ => unreachable!(),
}
}
if ast.kind(*branches.last().unwrap()) != NodeKind::ElseBranch {
self.chunk.emit(Opcode::Discard);
self.chunk.emit(Opcode::PushNil);
}
for jump in jumps_to_end {
self.chunk.patch(
jump,
Opcode::jump_forward(jump, self.chunk.len())
.map_err(|_| ast.error(node, ErrorKind::IfExpressionTooLarge))?,
);
}
Ok(ExpressionResult::Present)
}
fn generate_and(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (left, right) = ast.node_pair(node);
self.push_scope();
self.generate_node(ast, left, Expression::Used)?;
let jump_past_right = self.chunk.emit(Opcode::Nop);
self.chunk.emit(Opcode::Discard);
self.generate_node(ast, right, Expression::Used)?;
self.chunk.patch(
jump_past_right,
Opcode::jump_forward_if_falsy(jump_past_right, self.chunk.len())
.map_err(|_| ast.error(node, ErrorKind::OperatorRhsTooLarge))?,
);
self.pop_scope();
Ok(ExpressionResult::Present)
}
fn generate_or(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (left, right) = ast.node_pair(node);
self.push_scope();
self.generate_node(ast, left, Expression::Used)?;
let jump_past_right = self.chunk.emit(Opcode::Nop);
self.chunk.emit(Opcode::Discard);
self.generate_node(ast, right, Expression::Used)?;
self.chunk.patch(
jump_past_right,
Opcode::jump_forward_if_truthy(jump_past_right, self.chunk.len())
.map_err(|_| ast.error(node, ErrorKind::OperatorRhsTooLarge))?,
);
self.pop_scope();
Ok(ExpressionResult::Present)
}
fn generate_conditional_loop(
&mut self,
ast: &Ast,
node: NodeId,
generate_condition: &dyn Fn(&mut CodeGenerator<'_>) -> Result<(), Error>,
generate_body: &dyn Fn(&mut CodeGenerator<'_>) -> Result<(), Error>,
) -> Result<(), Error> {
self.push_scope();
self.push_breakable_block();
let start = self.chunk.len();
generate_condition(self)?;
let jump_to_end = self.chunk.emit(Opcode::Nop);
self.chunk.emit(Opcode::Discard);
generate_body(self)?;
self.chunk.emit(Opcode::Discard);
self.chunk.emit(
Opcode::jump_backward(self.chunk.len(), start)
.map_err(|_| ast.error(node, ErrorKind::LoopTooLarge))?,
);
self.chunk.patch(
jump_to_end,
Opcode::jump_forward_if_falsy(jump_to_end, self.chunk.len())
.map_err(|_| ast.error(node, ErrorKind::LoopTooLarge))?,
);
self.chunk.emit(Opcode::Discard);
self.chunk.emit(Opcode::PushNil);
self.pop_breakable_block();
self.pop_scope();
Ok(())
}
fn generate_while(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (condition, _) = ast.node_pair(node);
let body = ast.children(node).unwrap();
self.generate_conditional_loop(
ast,
node,
&|generator| generator.generate_node(ast, condition, Expression::Used),
&|generator| generator.generate_node_list(ast, body),
)?;
Ok(ExpressionResult::Present)
}
fn generate_for(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (binding, iterator) = ast.node_pair(node);
let body = ast.children(node).unwrap();
self.push_scope();
let iterator_var = self
.create_variable("<iterator>", VariableAllocation::Allocate)
.map_err(|kind| ast.error(iterator, kind))?;
self.generate_node(ast, iterator, Expression::Used)?;
self.generate_variable_sink(iterator_var);
self.generate_conditional_loop(
ast,
node,
&|generator| {
generator.generate_variable_load(iterator_var);
generator.chunk.emit((
Opcode::CallMethod,
Opr24::pack((generator.builtin_traits.iterator_has_next, 1)),
));
Ok(())
},
&|generator| {
generator.generate_variable_load(iterator_var);
generator.chunk.emit((
Opcode::CallMethod,
Opr24::pack((generator.builtin_traits.iterator_next, 1)),
));
generator.generate_pattern_destructuring(ast, binding)?;
generator.generate_node_list(ast, body)?;
Ok(())
},
)?;
self.pop_scope();
Ok(ExpressionResult::Present)
}
fn generate_break(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (right, _) = ast.node_pair(node);
if right != NodeId::EMPTY {
self.generate_node(ast, right, Expression::Used)?;
} else {
let _ = self.generate_nil();
}
let jump = self.chunk.emit(Opcode::Nop);
if let Some(block) = self.breakable_blocks.last_mut() {
block.breaks.push(jump);
} else {
return Err(ast.error(node, ErrorKind::BreakOutsideOfLoop));
}
Ok(ExpressionResult::NoReturn)
}
fn generate_call(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (function, _) = ast.node_pair(node);
match ast.kind(function) {
NodeKind::Dot => {
let (receiver, name) = ast.node_pair(function);
if ast.kind(name) != NodeKind::Identifier {
return Err(ast.error(name, ErrorKind::InvalidMethodName));
}
let name = ast.string(name).unwrap();
self.generate_node(ast, receiver, Expression::Used)?;
let arguments = ast.children(node).unwrap();
for &argument in arguments {
self.generate_node(ast, argument, Expression::Used)?;
}
let arity: u8 = (1 + arguments.len())
.try_into()
.map_err(|_| ast.error(node, ErrorKind::TooManyArguments))?;
let signature = FunctionSignature::new(Rc::clone(name), arity as u16);
let method_index =
self.env.get_method_index(&signature).map_err(|kind| ast.error(node, kind))?;
self.chunk.emit((Opcode::CallMethod, Opr24::pack((method_index, arity))));
}
_ => {
self.generate_node(ast, function, Expression::Used)?;
let arguments = ast.children(node).unwrap();
for &argument in arguments {
self.generate_node(ast, argument, Expression::Used)?;
}
self.chunk.emit((
Opcode::Call,
Opr24::try_from(arguments.len())
.map_err(|_| ast.error(node, ErrorKind::TooManyArguments))?,
));
}
}
Ok(ExpressionResult::Present)
}
fn generate_dot(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (left, method) = ast.node_pair(node);
self.generate_node(ast, left, Expression::Used)?;
if ast.kind(method) != NodeKind::Identifier {
return Err(ast.error(method, ErrorKind::InvalidMethodName));
}
let signature = FunctionSignature::new(Rc::clone(ast.string(method).unwrap()), 1);
let method_index =
self.env.get_method_index(&signature).map_err(|kind| ast.error(node, kind))?;
self.chunk.emit((Opcode::CallMethod, Opr24::pack((method_index, 1))));
Ok(ExpressionResult::Present)
}
fn generate_function(
&mut self,
ast: &Ast,
node: NodeId,
GenerateFunctionOptions { name, call_conv }: GenerateFunctionOptions,
) -> Result<GeneratedFunction, Error> {
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, ErrorKind::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, ErrorKind::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, ErrorKind::TooManyParameters))?;
let function = Function {
name,
parameter_count: Some(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<(), Error> {
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, ErrorKind::FunctionKindOutsideImpl));
}
if body == NodeId::EMPTY {
return Err(ast.error(node, ErrorKind::MissingFunctionBody));
}
Ok(())
}
fn generate_function_declaration(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, Error> {
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));
self.generate_variable_sink(variable);
Ok(ExpressionResult::Absent)
}
fn generate_function_expression(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, Error> {
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));
Ok(ExpressionResult::Present)
}
fn generate_return(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (value, _) = ast.node_pair(node);
if value == NodeId::EMPTY {
let _ = self.generate_nil();
} else {
self.generate_node(ast, value, Expression::Used)?;
}
self.chunk.emit(Opcode::Return);
Ok(ExpressionResult::NoReturn)
}
fn generate_struct(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (name, _) = ast.node_pair(node);
let name = ast.string(name).unwrap();
self.chunk.emit(Opcode::CreateType);
self.chunk.emit_string(name);
let variable = self
.create_variable(name, VariableAllocation::Allocate)
.map_err(|kind| ast.error(node, kind))?;
self.generate_variable_assign(variable);
Ok(ExpressionResult::Present)
}
fn generate_impl(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (implementee, _) = ast.node_pair(node);
let items = ast.children(node).unwrap();
self.generate_node(ast, implementee, Expression::Used)?;
self.struct_data = Some(Box::new(StructData::default()));
let mut proto = Prototype::default();
let mut state = ImplGenerationState {
proto: &mut proto,
has_constructor: false,
trait_index: None,
};
for &node in items {
self.generate_impl_item(ast, node, &mut state, true)?;
}
let proto_id = self.env.create_prototype(proto).map_err(|kind| ast.error(node, kind))?;
self.chunk.emit((Opcode::Implement, proto_id));
self.struct_data = None;
Ok(ExpressionResult::Present)
}
fn generate_impl_item(
&mut self,
ast: &Ast,
node: NodeId,
state: &mut ImplGenerationState,
allow_as: bool,
) -> Result<(), Error> {
match ast.kind(node) {
NodeKind::Func => {
let (head, _) = ast.node_pair(node);
let (name_node, parameters) = ast.node_pair(head);
if ast.kind(name_node) != NodeKind::Identifier {
return Err(ast.error(node, ErrorKind::MissingMethodName));
}
let name = Rc::clone(ast.string(name_node).unwrap());
let (kind, _) = ast.node_pair(parameters);
let call_conv = match ast.kind(kind) {
NodeKind::Empty => FunctionCallConv::Instance,
NodeKind::Static => FunctionCallConv::Static,
NodeKind::Constructor => {
let allow_new_fields = !state.has_constructor;
state.has_constructor = true;
FunctionCallConv::Constructor { allow_new_fields }
}
_ => unreachable!(),
};
let function = self.generate_function(
ast,
node,
GenerateFunctionOptions {
name: Rc::clone(&name),
call_conv,
},
)?;
if let Some(trait_index) = state.trait_index {
let key = (Rc::clone(&name), 1 + function.parameter_count, trait_index);
if state.proto.trait_instance.insert(key, function.id).is_some() {
return Err(ast.error(
name_node,
ErrorKind::MethodAlreadyImplemented(RenderedSignature {
name,
arity: Some(function.parameter_count),
trait_name: None,
}),
));
}
} else {
let signature = FunctionSignature::new(name, 1 + function.parameter_count);
let method_id =
self.env.get_method_index(&signature).map_err(|kind| ast.error(node, kind))?;
let map = match ast.kind(kind) {
NodeKind::Empty => &mut state.proto.instance,
NodeKind::Static | NodeKind::Constructor => &mut state.proto.statics,
_ => unreachable!(),
};
if map.insert(method_id, function.id).is_some() {
return Err(ast.error(
name_node,
ErrorKind::MethodAlreadyImplemented(RenderedSignature {
name: signature.name,
arity: Some(function.parameter_count),
trait_name: None,
}),
));
}
}
}
NodeKind::ImplAs => {
if !allow_as {
return Err(ast.error(node, ErrorKind::AsCannotNest));
}
let (trait_expr, _) = ast.node_pair(node);
let as_items = ast.children(node).unwrap();
self.generate_node(ast, trait_expr, Expression::Used)?;
let trait_index = state.proto.implemented_trait_count;
state.proto.implemented_trait_count = state
.proto
.implemented_trait_count
.checked_add(1)
.ok_or_else(|| ast.error(node, ErrorKind::TooManyTraitsInImpl))?;
let mut state = ImplGenerationState {
proto: state.proto,
trait_index: Some(trait_index),
..*state
};
for &item in as_items {
self.generate_impl_item(ast, item, &mut state, false)?;
}
}
_ => return Err(ast.error(node, ErrorKind::InvalidImplItem)),
}
Ok(())
}
fn generate_trait(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let (trait_name, _) = ast.node_pair(node);
let trait_name = ast.string(trait_name).unwrap();
let items = ast.children(node).unwrap();
let mut builder = TraitBuilder::new(self.env, Some(&self.chunk), Rc::clone(trait_name))
.map_err(|e| ast.error(node, e))?;
for &item in items {
match ast.kind(item) {
NodeKind::Func => {
let (head, body) = ast.node_pair(item);
let (name, params) = ast.node_pair(head);
if body != NodeId::EMPTY {
return Err(ast.error(body, ErrorKind::TraitMethodCannotHaveBody));
}
if name == NodeId::EMPTY {
return Err(ast.error(head, ErrorKind::MissingMethodName));
}
if ast.node_pair(params).0 != NodeId::EMPTY {
return Err(ast.error(head, ErrorKind::FunctionKindInTrait));
}
let _method_id = builder
.add_method(
Rc::clone(ast.string(name).unwrap()),
u16::try_from(ast.len(params).unwrap())
.map_err(|_| ast.error(item, ErrorKind::TooManyParameters))?,
)
.map_err(|e| ast.error(item, e))?;
}
_ => return Err(ast.error(item, ErrorKind::InvalidTraitItem)),
}
}
let (trait_id, _) = builder.build();
self.chunk.emit((Opcode::CreateTrait, trait_id));
let variable = self
.create_variable(trait_name, VariableAllocation::Allocate)
.map_err(|k| ast.error(node, k))?;
self.generate_variable_assign(variable);
Ok(ExpressionResult::Present)
}
fn generate_node(&mut self, ast: &Ast, node: NodeId, expr: Expression) -> Result<(), Error> {
let previous_codegen_location = self.chunk.codegen_location;
self.chunk.codegen_location = ast.location(node);
let result = match ast.kind(node) {
NodeKind::Empty => panic!("empty nodes must never be generated"),
NodeKind::Nil => self.generate_nil(),
NodeKind::False | NodeKind::True => self.generate_boolean(ast, node),
NodeKind::Number => self.generate_number(ast, node),
NodeKind::String => self.generate_string(ast, node),
NodeKind::Identifier => self.generate_variable(ast, node)?,
NodeKind::List => self.generate_list(ast, node)?,
NodeKind::Dict => self.generate_dict(ast, node)?,
NodeKind::Negate | NodeKind::Not => self.generate_unary(ast, node)?,
| NodeKind::Add
| NodeKind::Subtract
| NodeKind::Multiply
| NodeKind::Divide
| NodeKind::Equal
| NodeKind::NotEqual
| NodeKind::Less
| NodeKind::Greater
| NodeKind::LessEqual
| NodeKind::GreaterEqual => self.generate_binary(ast, node)?,
NodeKind::And => self.generate_and(ast, node)?,
NodeKind::Or => self.generate_or(ast, node)?,
NodeKind::Assign => self.generate_assignment(ast, node, expr)?,
NodeKind::Dot => self.generate_dot(ast, node)?,
NodeKind::Field => self.generate_field(ast, node)?,
NodeKind::Main => {
self.generate_node_list(ast, ast.children(node).unwrap())?;
ExpressionResult::Present
}
NodeKind::Do => self.generate_do(ast, node)?,
NodeKind::If => self.generate_if(ast, node)?,
NodeKind::While => self.generate_while(ast, node)?,
NodeKind::For => self.generate_for(ast, node)?,
NodeKind::Break => self.generate_break(ast, node)?,
NodeKind::Func => {
let (head, _) = ast.node_pair(node);
let (name, _) = ast.node_pair(head);
if name != NodeId::EMPTY {
self.generate_function_declaration(ast, node)?
} else {
self.generate_function_expression(ast, node)?
}
}
NodeKind::Call => self.generate_call(ast, node)?,
NodeKind::Return => self.generate_return(ast, node)?,
NodeKind::Struct => self.generate_struct(ast, node)?,
NodeKind::Impl => self.generate_impl(ast, node)?,
NodeKind::Trait => self.generate_trait(ast, node)?,
NodeKind::ImplAs => return Err(ast.error(node, ErrorKind::AsOutsideOfImpl)),
| NodeKind::DictPair
| NodeKind::IfBranch
| NodeKind::ElseBranch
| NodeKind::FunctionHead
| NodeKind::Parameters
| NodeKind::Static
| NodeKind::Constructor => {
unreachable!("AST implementation detail")
}
};
match (result, expr) {
(ExpressionResult::Absent, Expression::Used) => {
let _ = self.generate_nil();
}
(ExpressionResult::Present, Expression::Discarded) => {
let _ = self.chunk.emit(Opcode::Discard);
}
_ => (),
}
self.chunk.codegen_location = previous_codegen_location;
Ok(())
}
pub fn generate(mut self, ast: &Ast, root_node: NodeId) -> Result<Rc<Chunk>, Error> {
self.generate_node(ast, root_node, Expression::Used)?;
self.chunk.emit(Opcode::Halt);
Ok(Rc::new(self.chunk))
}
}
#[derive(Debug, Clone, Copy)]
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
}
)
}
}
struct GenerateFunctionOptions {
name: Rc<str>,
call_conv: FunctionCallConv,
}
struct GeneratedFunction {
id: Opr24,
parameter_count: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Expression {
Discarded,
Used,
}
#[must_use]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ExpressionResult {
Present,
Absent,
NoReturn,
}
struct ImplGenerationState<'p> {
proto: &'p mut Prototype,
has_constructor: bool,
trait_index: Option<u16>,
}
pub struct TraitBuilder<'b> {
pub env: &'b mut Environment,
parent_chunk: Option<&'b Chunk>,
trait_id: Opr24,
required_methods: HashSet<u16>,
shims: Vec<(u16, Opr24)>,
}
impl<'b> TraitBuilder<'b> {
pub fn new(
env: &'b mut Environment,
parent_chunk: Option<&'b Chunk>,
name: Rc<str>,
) -> Result<Self, ErrorKind> {
let trait_id = env.create_trait(name)?;
Ok(Self {
env,
parent_chunk,
trait_id,
required_methods: HashSet::new(),
shims: vec![],
})
}
fn generate_method_shim(
&mut self,
trait_name: &str,
method_id: u16,
method_signature: &FunctionSignature,
) -> Result<Opr24, ErrorKind> {
let arity = method_signature.arity.unwrap() as u8;
let (module_name, codegen_location) = if let Some(parent_chunk) = self.parent_chunk {
(
Rc::clone(&parent_chunk.module_name),
parent_chunk.codegen_location,
)
} else {
(Rc::from("FFI"), Location::UNINIT)
};
let mut chunk = Chunk::new(module_name);
chunk.codegen_location = codegen_location;
chunk.emit((Opcode::CallMethod, Opr24::pack((method_id, arity as u8))));
chunk.emit(Opcode::Return);
let shim_name = Rc::from(format!(
"trait {trait_name}.{} <shim>",
method_signature.name
));
let chunk = Rc::new(chunk);
let function_id = self.env.create_function(Function {
name: shim_name,
parameter_count: method_signature.arity,
kind: FunctionKind::Bytecode {
chunk,
captured_locals: vec![],
},
hidden_in_stack_traces: true,
})?;
Ok(function_id)
}
pub fn add_method(&mut self, name: Rc<str>, arity: u16) -> Result<u16, ErrorKind> {
let trait_name = Rc::clone(&self.env.get_trait(self.trait_id).unwrap().name);
let signature = FunctionSignature {
name,
arity: Some(arity.checked_add(1).ok_or(ErrorKind::TooManyParameters)?),
trait_id: Some(self.trait_id),
};
let method_id = self.env.get_method_index(&signature)?;
let shim_signature = FunctionSignature {
arity: Some(arity.checked_add(2).ok_or(ErrorKind::TooManyParameters)?),
trait_id: None,
..signature.clone()
};
let shim_method_id = self.env.get_method_index(&shim_signature)?;
let shim_function_id = self.generate_method_shim(&trait_name, method_id, &signature)?;
self.shims.push((shim_method_id, shim_function_id));
if !self.required_methods.insert(method_id) {
return Err(ErrorKind::TraitAlreadyHasMethod(RenderedSignature {
name: signature.name,
arity: signature.arity,
trait_name: None,
}));
}
Ok(method_id)
}
pub fn build(self) -> (Opr24, &'b mut Environment) {
let prototype = self.env.get_trait_mut(self.trait_id).unwrap();
prototype.required = self.required_methods;
prototype.shims = self.shims;
(self.trait_id, self.env)
}
}