use std::{collections::HashSet, fmt, rc::Rc};
pub use self::traits::TraitBuilder;
use self::{control_flow::BreakableBlock, structs::StructData, variables::Locals};
use crate::ll::{
ast::{Ast, NodeId, NodeKind},
bytecode::{BuiltinTraits, Chunk, Environment, Opcode},
error::{LanguageError, LanguageErrorKind},
};
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 generate_node_list(&mut self, ast: &Ast, nodes: &[NodeId]) -> Result<(), LanguageError> {
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_node(
&mut self,
ast: &Ast,
node: NodeId,
expr: Expression,
) -> Result<(), LanguageError> {
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, LanguageErrorKind::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>, LanguageError> {
self.generate_node(ast, root_node, Expression::Used)?;
self.chunk.emit(Opcode::Halt);
Ok(Rc::new(self.chunk))
}
}
impl<'e> fmt::Debug for CodeGenerator<'e> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CodeGenerator").finish_non_exhaustive()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Expression {
Discarded,
Used,
}
#[must_use]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ExpressionResult {
Present,
Absent,
NoReturn,
}
mod assignment;
mod calls;
mod control_flow;
mod functions;
mod impls;
mod literals;
mod operators;
mod structs;
mod traits;
pub mod variables;