use crate::parser::Ast; use crate::spans::Spanned; use crate::value::*; use chumsky::prelude::SimpleSpan; use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::FromPrimitive; #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)] enum Op { Return, Constant, } #[derive(Clone, Debug, PartialEq)] struct Chunk<'a> { pub constants: Vec>, pub bytecode: Vec, pub spans: Vec, pub strings: Vec<&'static str>, pub keywords: Vec<&'static str>, pub nodes: Vec<&'a Ast>, pub ast: &'a Ast, pub span: SimpleSpan, pub src: &'static str, pub name: &'static str, } impl<'a> Chunk<'a> { fn visit(&mut self, node: &'a Spanned) { let root_node = self.ast; let root_span = self.span; let (ast, span) = node; self.ast = ast; self.span = *span; self.compile(); self.ast = root_node; self.span = root_span; } fn emit_constant(&mut self, val: Value<'a>) { let constant_index = self.constants.len(); if constant_index > u8::MAX as usize { panic!( "internal Ludus compiler error: too many constants in chunk:{}:: {}", self.span, self.ast ) } self.constants.push(val); self.bytecode.push(Op::Constant as u8); self.spans.push(self.span); self.bytecode.push(constant_index as u8); self.spans.push(self.span); } fn emit_op(&mut self, op: Op) { self.bytecode.push(op as u8); self.spans.push(self.span); } fn compile(&mut self) { match self.ast { Ast::Nil => self.emit_constant(Value::Nil), Ast::Number(n) => self.emit_constant(Value::Number(*n)), Ast::Boolean(b) => self.emit_constant(Value::Boolean(*b)), Ast::String(s) => { let str_index = self.strings.len(); self.strings.push(s); self.emit_constant(Value::InternedString(s)); } Ast::Block(lines) => { for expr in lines { self.visit(expr); } self.emit_op(Op::Return); } _ => todo!(), } } fn disassemble(&self) { println!("=== chunk: {} ===", self.name); let mut codes = self.bytecode.iter().enumerate(); while let Some((i, byte)) = codes.next() { let op = Op::from_u8(*byte); match op { Some(Op::Return) => println!("{i:04}: {byte:16}"), Some(Op::Constant) => { let (_, next) = codes.next().unwrap(); let value = &self.constants[*next as usize]; println!("i:04: {byte:16} {next:16} {value}") } _ => unreachable!(), } } } }