use harn_parser::{Attribute, DictEntry, Node, SNode, TypedParam};
use crate::chunk::{CompiledFunction, Constant, Op};
use super::error::CompileError;
use super::Compiler;
impl Compiler {
pub(super) fn compile_enum_construct(
&mut self,
enum_name: &str,
variant: &str,
args: &[SNode],
) -> Result<(), CompileError> {
for arg in args {
self.compile_node(arg)?;
}
let enum_idx = self
.chunk
.add_constant(Constant::String(enum_name.to_string()));
let var_idx = self
.chunk
.add_constant(Constant::String(variant.to_string()));
self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
let hi = (var_idx >> 8) as u8;
let lo = var_idx as u8;
self.chunk.code.push(hi);
self.chunk.code.push(lo);
self.chunk.lines.push(self.line);
self.chunk.columns.push(self.column);
self.chunk.lines.push(self.line);
self.chunk.columns.push(self.column);
let fc = args.len() as u16;
let fhi = (fc >> 8) as u8;
let flo = fc as u8;
self.chunk.code.push(fhi);
self.chunk.code.push(flo);
self.chunk.lines.push(self.line);
self.chunk.columns.push(self.column);
self.chunk.lines.push(self.line);
self.chunk.columns.push(self.column);
Ok(())
}
pub(super) fn compile_struct_construct(
&mut self,
struct_name: &str,
fields: &[DictEntry],
) -> Result<(), CompileError> {
let make_idx = self
.chunk
.add_constant(Constant::String("__make_struct".to_string()));
let struct_name_idx = self
.chunk
.add_constant(Constant::String(struct_name.to_string()));
self.chunk.emit_u16(Op::Constant, make_idx, self.line);
self.chunk
.emit_u16(Op::Constant, struct_name_idx, self.line);
for entry in fields {
self.compile_node(&entry.key)?;
self.compile_node(&entry.value)?;
}
self.chunk
.emit_u16(Op::BuildDict, fields.len() as u16, self.line);
self.chunk.emit_u8(Op::Call, 2, self.line);
Ok(())
}
pub(super) fn compile_impl_block(
&mut self,
type_name: &str,
methods: &[SNode],
) -> Result<(), CompileError> {
for method_sn in methods {
if let Node::FnDecl {
name, params, body, ..
} = &method_sn.node
{
let key_idx = self.chunk.add_constant(Constant::String(name.clone()));
self.chunk.emit_u16(Op::Constant, key_idx, self.line);
let mut fn_compiler = Compiler::for_nested_body();
fn_compiler.enum_names = self.enum_names.clone();
fn_compiler.emit_default_preamble(params)?;
fn_compiler.emit_type_checks(params);
fn_compiler.compile_block(body)?;
fn_compiler.chunk.emit(Op::Nil, self.line);
fn_compiler.chunk.emit(Op::Return, self.line);
let func = CompiledFunction {
name: format!("{}.{}", type_name, name),
params: TypedParam::names(params),
default_start: TypedParam::default_start(params),
chunk: fn_compiler.chunk,
is_generator: false,
has_rest_param: false,
};
let fn_idx = self.chunk.functions.len();
self.chunk.functions.push(func);
self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
}
}
let method_count = methods
.iter()
.filter(|m| matches!(m.node, Node::FnDecl { .. }))
.count();
self.chunk
.emit_u16(Op::BuildDict, method_count as u16, self.line);
let impl_name = format!("__impl_{}", type_name);
let name_idx = self.chunk.add_constant(Constant::String(impl_name));
self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
Ok(())
}
pub(super) fn compile_struct_decl(&mut self, name: &str) -> Result<(), CompileError> {
let mut fn_compiler = Compiler::for_nested_body();
fn_compiler.enum_names = self.enum_names.clone();
let params = vec![TypedParam::untyped("__fields")];
fn_compiler.emit_default_preamble(¶ms)?;
let make_idx = fn_compiler
.chunk
.add_constant(Constant::String("__make_struct".into()));
fn_compiler
.chunk
.emit_u16(Op::Constant, make_idx, self.line);
let sname_idx = fn_compiler
.chunk
.add_constant(Constant::String(name.to_string()));
fn_compiler
.chunk
.emit_u16(Op::Constant, sname_idx, self.line);
let fields_idx = fn_compiler
.chunk
.add_constant(Constant::String("__fields".into()));
fn_compiler
.chunk
.emit_u16(Op::GetVar, fields_idx, self.line);
fn_compiler.chunk.emit_u8(Op::Call, 2, self.line);
fn_compiler.chunk.emit(Op::Return, self.line);
let func = CompiledFunction {
name: name.to_string(),
params: TypedParam::names(¶ms),
default_start: None,
chunk: fn_compiler.chunk,
is_generator: false,
has_rest_param: false,
};
let fn_idx = self.chunk.functions.len();
self.chunk.functions.push(func);
self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
let name_idx = self.chunk.add_constant(Constant::String(name.to_string()));
self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
Ok(())
}
pub(super) fn compile_attributed_decl(
&mut self,
attributes: &[Attribute],
inner: &SNode,
) -> Result<(), CompileError> {
for attr in attributes {
if attr.name == "acp_tool" && !matches!(inner.node, Node::FnDecl { .. }) {
return Err(CompileError {
message: "@acp_tool can only be applied to function declarations".into(),
line: self.line,
});
}
}
self.compile_node(inner)?;
for attr in attributes {
if attr.name == "acp_tool" {
if let Node::FnDecl { name, .. } = &inner.node {
self.emit_acp_tool_registration(attr, name)?;
}
}
}
Ok(())
}
pub(super) fn emit_acp_tool_registration(
&mut self,
attr: &harn_parser::Attribute,
fn_name: &str,
) -> Result<(), CompileError> {
let tool_name = attr
.string_arg("name")
.unwrap_or_else(|| fn_name.to_string());
let define_idx = self
.chunk
.add_constant(Constant::String("tool_define".into()));
self.chunk.emit_u16(Op::Constant, define_idx, self.line);
let reg_idx = self
.chunk
.add_constant(Constant::String("tool_registry".into()));
self.chunk.emit_u16(Op::Constant, reg_idx, self.line);
self.chunk.emit_u8(Op::Call, 0, self.line);
let name_const = self.chunk.add_constant(Constant::String(tool_name));
self.chunk.emit_u16(Op::Constant, name_const, self.line);
let desc_const = self.chunk.add_constant(Constant::String(String::new()));
self.chunk.emit_u16(Op::Constant, desc_const, self.line);
let handler_key = self.chunk.add_constant(Constant::String("handler".into()));
self.chunk.emit_u16(Op::Constant, handler_key, self.line);
let fn_name_const = self
.chunk
.add_constant(Constant::String(fn_name.to_string()));
self.chunk.emit_u16(Op::GetVar, fn_name_const, self.line);
let mut ann_count: u16 = 0;
for arg in &attr.args {
let Some(ref key) = arg.name else {
continue;
};
if key == "name" {
continue;
}
let key_idx = self.chunk.add_constant(Constant::String(key.clone()));
self.chunk.emit_u16(Op::Constant, key_idx, self.line);
self.compile_attribute_value(&arg.value)?;
ann_count += 1;
}
let ann_key_idx = self
.chunk
.add_constant(Constant::String("annotations".into()));
self.chunk.emit_u16(Op::Constant, ann_key_idx, self.line);
self.chunk.emit_u16(Op::BuildDict, ann_count, self.line);
self.chunk.emit_u16(Op::BuildDict, 2, self.line);
self.chunk.emit_u8(Op::Call, 4, self.line);
self.chunk.emit(Op::Pop, self.line);
Ok(())
}
pub(super) fn compile_attribute_value(&mut self, node: &SNode) -> Result<(), CompileError> {
match &node.node {
Node::StringLiteral(s) | Node::RawStringLiteral(s) => {
let idx = self.chunk.add_constant(Constant::String(s.clone()));
self.chunk.emit_u16(Op::Constant, idx, self.line);
}
Node::IntLiteral(i) => {
let idx = self.chunk.add_constant(Constant::Int(*i));
self.chunk.emit_u16(Op::Constant, idx, self.line);
}
Node::FloatLiteral(f) => {
let idx = self.chunk.add_constant(Constant::Float(*f));
self.chunk.emit_u16(Op::Constant, idx, self.line);
}
Node::BoolLiteral(b) => {
self.chunk
.emit(if *b { Op::True } else { Op::False }, self.line);
}
Node::NilLiteral => {
self.chunk.emit(Op::Nil, self.line);
}
Node::Identifier(name) => {
let idx = self.chunk.add_constant(Constant::String(name.clone()));
self.chunk.emit_u16(Op::Constant, idx, self.line);
}
_ => {
return Err(CompileError {
message: "attribute argument must be a literal value".into(),
line: self.line,
});
}
}
Ok(())
}
}