97 lines
2.8 KiB
Rust
97 lines
2.8 KiB
Rust
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<Value<'a>>,
|
|
pub bytecode: Vec<u8>,
|
|
pub spans: Vec<SimpleSpan>,
|
|
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<Ast>) {
|
|
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!(),
|
|
}
|
|
}
|
|
}
|
|
}
|