separate compiler & chunk

This commit is contained in:
Scott Richmond 2024-12-26 19:03:09 -05:00
parent 40d4f48878
commit 4fa2ce5e78
3 changed files with 111 additions and 102 deletions

View File

@ -80,14 +80,41 @@ pub struct Binding {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Chunk { pub struct Chunk {
pub constants: Vec<Value>,
pub bytecode: Vec<u8>,
pub strings: Vec<&'static str>,
pub keywords: Vec<&'static str>,
}
impl Chunk {
pub fn dissasemble_instr(&self, i: usize) {
let op = Op::from_u8(self.bytecode[i]).unwrap();
use Op::*;
match op {
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
| PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch => {
println!("{i:04}: {op}")
}
Constant | MatchConstant => {
let next = self.bytecode[i + 1];
let value = &self.constants[next as usize].show(self);
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
}
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
| JumpIfFalse | JumpIfNoMatch => {
let next = self.bytecode[i + 1];
println!("{i:04}: {:16} {next:04}", op.to_string());
}
}
}
}
pub struct Compiler {
pub chunk: Chunk,
pub bindings: Vec<Binding>, pub bindings: Vec<Binding>,
scope_depth: isize, scope_depth: isize,
num_bindings: usize, num_bindings: usize,
pub constants: Vec<Value>,
pub bytecode: Vec<u8>,
pub spans: Vec<SimpleSpan>, pub spans: Vec<SimpleSpan>,
pub strings: Vec<&'static str>,
pub keywords: Vec<&'static str>,
pub nodes: Vec<&'static Ast>, pub nodes: Vec<&'static Ast>,
pub ast: &'static Ast, pub ast: &'static Ast,
pub span: SimpleSpan, pub span: SimpleSpan,
@ -105,19 +132,22 @@ fn is_binding(expr: &Spanned<Ast>) -> bool {
} }
} }
impl Chunk { impl Compiler {
pub fn new(ast: &'static Spanned<Ast>, name: &'static str, src: &'static str) -> Chunk { pub fn new(ast: &'static Spanned<Ast>, name: &'static str, src: &'static str) -> Compiler {
Chunk { let chunk = Chunk {
bindings: vec![],
scope_depth: -1,
num_bindings: 0,
constants: vec![], constants: vec![],
bytecode: vec![], bytecode: vec![],
spans: vec![],
strings: vec![], strings: vec![],
keywords: vec![ keywords: vec![
"nil", "bool", "number", "keyword", "string", "tuple", "list", "dict", "box", "fn", "nil", "bool", "number", "keyword", "string", "tuple", "list", "dict", "box", "fn",
], ],
};
Compiler {
chunk,
bindings: vec![],
scope_depth: -1,
num_bindings: 0,
spans: vec![],
nodes: vec![], nodes: vec![],
ast: &ast.0, ast: &ast.0,
span: ast.1, span: ast.1,
@ -131,7 +161,7 @@ impl Chunk {
} }
pub fn kw_index_from(&self, kw: &str) -> Option<usize> { pub fn kw_index_from(&self, kw: &str) -> Option<usize> {
self.keywords.iter().position(|s| *s == kw) self.chunk.keywords.iter().position(|s| *s == kw)
} }
pub fn visit(&mut self, node: &'static Spanned<Ast>) { pub fn visit(&mut self, node: &'static Spanned<Ast>) {
@ -146,24 +176,24 @@ impl Chunk {
} }
fn emit_constant(&mut self, val: Value) { fn emit_constant(&mut self, val: Value) {
let constant_index = self.constants.len(); let constant_index = self.chunk.constants.len();
if constant_index > u8::MAX as usize { if constant_index > u8::MAX as usize {
panic!( panic!(
"internal Ludus compiler error: too many constants in chunk:{}:: {}", "internal Ludus compiler error: too many constants in chunk:{}:: {}",
self.span, self.ast self.span, self.ast
) )
} }
self.constants.push(val); self.chunk.constants.push(val);
self.bytecode.push(Op::Constant as u8); self.chunk.bytecode.push(Op::Constant as u8);
self.spans.push(self.span); self.spans.push(self.span);
self.bytecode.push(constant_index as u8); self.chunk.bytecode.push(constant_index as u8);
self.spans.push(self.span); self.spans.push(self.span);
} }
fn match_constant(&mut self, val: Value) { fn match_constant(&mut self, val: Value) {
let constant_index = match self.constants.iter().position(|v| *v == val) { let constant_index = match self.chunk.constants.iter().position(|v| *v == val) {
Some(idx) => idx, Some(idx) => idx,
None => self.constants.len(), None => self.chunk.constants.len(),
}; };
if constant_index > u8::MAX as usize { if constant_index > u8::MAX as usize {
panic!( panic!(
@ -171,18 +201,18 @@ impl Chunk {
self.span, self.ast self.span, self.ast
) )
} }
if constant_index == self.constants.len() { if constant_index == self.chunk.constants.len() {
self.constants.push(val); self.chunk.constants.push(val);
} }
self.bytecode.push(Op::MatchConstant as u8); self.chunk.bytecode.push(Op::MatchConstant as u8);
self.spans.push(self.span); self.spans.push(self.span);
self.bytecode.push(constant_index as u8); self.chunk.bytecode.push(constant_index as u8);
self.spans.push(self.span); self.spans.push(self.span);
self.bind(""); self.bind("");
} }
fn emit_op(&mut self, op: Op) { fn emit_op(&mut self, op: Op) {
self.bytecode.push(op as u8); self.chunk.bytecode.push(op as u8);
self.spans.push(self.span); self.spans.push(self.span);
} }
@ -200,22 +230,22 @@ impl Chunk {
Number(n) => self.emit_constant(Value::Number(*n)), Number(n) => self.emit_constant(Value::Number(*n)),
Boolean(b) => self.emit_op(if *b { Op::True } else { Op::False }), Boolean(b) => self.emit_op(if *b { Op::True } else { Op::False }),
String(s) => { String(s) => {
let existing_str = self.strings.iter().position(|e| e == s); let existing_str = self.chunk.strings.iter().position(|e| e == s);
let str_index = match existing_str { let str_index = match existing_str {
Some(idx) => idx, Some(idx) => idx,
None => self.strings.len(), None => self.chunk.strings.len(),
}; };
self.strings.push(s); self.chunk.strings.push(s);
self.emit_constant(Value::Interned(str_index)); self.emit_constant(Value::Interned(str_index));
} }
Keyword(s) => { Keyword(s) => {
let existing_kw = self.keywords.iter().position(|kw| kw == s); let existing_kw = self.chunk.keywords.iter().position(|kw| kw == s);
let kw_index = match existing_kw { let kw_index = match existing_kw {
Some(index) => index, Some(index) => index,
None => self.keywords.len(), None => self.chunk.keywords.len(),
}; };
if kw_index == self.keywords.len() { if kw_index == self.chunk.keywords.len() {
self.keywords.push(s); self.chunk.keywords.push(s);
} }
self.emit_constant(Value::Keyword(kw_index)); self.emit_constant(Value::Keyword(kw_index));
} }
@ -244,19 +274,19 @@ impl Chunk {
} }
If(cond, then, r#else) => { If(cond, then, r#else) => {
self.visit(cond); self.visit(cond);
let jif_idx = self.bytecode.len(); let jif_idx = self.chunk.bytecode.len();
self.emit_op(Op::JumpIfFalse); self.emit_op(Op::JumpIfFalse);
self.bytecode.push(0xff); self.chunk.bytecode.push(0xff);
self.visit(then); self.visit(then);
let jump_idx = self.bytecode.len(); let jump_idx = self.chunk.bytecode.len();
self.emit_op(Op::Jump); self.emit_op(Op::Jump);
self.bytecode.push(0xff); self.chunk.bytecode.push(0xff);
self.visit(r#else); self.visit(r#else);
let end_idx = self.bytecode.len(); let end_idx = self.chunk.bytecode.len();
let jif_offset = jump_idx - jif_idx; let jif_offset = jump_idx - jif_idx;
let jump_offset = end_idx - jump_idx; let jump_offset = end_idx - jump_idx;
self.bytecode[jif_idx + 1] = jif_offset as u8; self.chunk.bytecode[jif_idx + 1] = jif_offset as u8;
self.bytecode[jump_idx + 1] = jump_offset as u8; self.chunk.bytecode[jump_idx + 1] = jump_offset as u8;
} }
Let(patt, expr) => { Let(patt, expr) => {
self.emit_op(Op::ResetMatch); self.emit_op(Op::ResetMatch);
@ -273,7 +303,7 @@ impl Chunk {
let biter = self.bindings.iter().enumerate().rev(); let biter = self.bindings.iter().enumerate().rev();
for (i, binding) in biter { for (i, binding) in biter {
if binding.name == *name { if binding.name == *name {
self.bytecode.push(i as u8); self.chunk.bytecode.push(i as u8);
break; break;
} }
} }
@ -299,24 +329,24 @@ impl Chunk {
self.match_constant(Value::Number(*n)); self.match_constant(Value::Number(*n));
} }
KeywordPattern(s) => { KeywordPattern(s) => {
let existing_kw = self.keywords.iter().position(|kw| kw == s); let existing_kw = self.chunk.keywords.iter().position(|kw| kw == s);
let kw_index = match existing_kw { let kw_index = match existing_kw {
Some(index) => index, Some(index) => index,
None => self.keywords.len(), None => self.chunk.keywords.len(),
}; };
if kw_index == self.keywords.len() { if kw_index == self.chunk.keywords.len() {
self.keywords.push(s); self.chunk.keywords.push(s);
} }
self.match_constant(Value::Keyword(kw_index)); self.match_constant(Value::Keyword(kw_index));
} }
StringPattern(s) => { StringPattern(s) => {
let existing_str = self.strings.iter().position(|e| e == s); let existing_str = self.chunk.strings.iter().position(|e| e == s);
let str_index = match existing_str { let str_index = match existing_str {
Some(idx) => idx, Some(idx) => idx,
None => self.strings.len(), None => self.chunk.strings.len(),
}; };
if str_index == self.strings.len() { if str_index == self.chunk.strings.len() {
self.strings.push(s) self.chunk.strings.push(s)
} }
self.match_constant(Value::Interned(str_index)); self.match_constant(Value::Interned(str_index));
} }
@ -325,14 +355,14 @@ impl Chunk {
self.visit(member); self.visit(member);
} }
self.emit_op(Op::PushTuple); self.emit_op(Op::PushTuple);
self.bytecode.push(members.len() as u8); self.chunk.bytecode.push(members.len() as u8);
} }
List(members) => { List(members) => {
for member in members { for member in members {
self.visit(member); self.visit(member);
} }
self.emit_op(Op::PushList); self.emit_op(Op::PushList);
self.bytecode.push(members.len() as u8); self.chunk.bytecode.push(members.len() as u8);
} }
LBox(name, expr) => { LBox(name, expr) => {
self.visit(expr); self.visit(expr);
@ -344,16 +374,16 @@ impl Chunk {
self.visit(pair); self.visit(pair);
} }
self.emit_op(Op::PushDict); self.emit_op(Op::PushDict);
self.bytecode.push(pairs.len() as u8); self.chunk.bytecode.push(pairs.len() as u8);
} }
Pair(key, value) => { Pair(key, value) => {
let existing_kw = self.keywords.iter().position(|kw| kw == key); let existing_kw = self.chunk.keywords.iter().position(|kw| kw == key);
let kw_index = match existing_kw { let kw_index = match existing_kw {
Some(index) => index, Some(index) => index,
None => self.keywords.len(), None => self.chunk.keywords.len(),
}; };
if kw_index == self.keywords.len() { if kw_index == self.chunk.keywords.len() {
self.keywords.push(key); self.chunk.keywords.push(key);
} }
self.emit_constant(Value::Keyword(kw_index)); self.emit_constant(Value::Keyword(kw_index));
self.visit(value); self.visit(value);
@ -386,18 +416,18 @@ impl Chunk {
while let Some((WhenClause(cond, body), _)) = clauses.next() { while let Some((WhenClause(cond, body), _)) = clauses.next() {
self.visit(cond.as_ref()); self.visit(cond.as_ref());
self.emit_op(Op::JumpIfFalse); self.emit_op(Op::JumpIfFalse);
let jif_jump_idx = self.bytecode.len(); let jif_jump_idx = self.chunk.bytecode.len();
self.bytecode.push(0xff); self.chunk.bytecode.push(0xff);
self.visit(body); self.visit(body);
self.emit_op(Op::Jump); self.emit_op(Op::Jump);
jump_idxes.push(self.bytecode.len()); jump_idxes.push(self.chunk.bytecode.len());
self.bytecode.push(0xff); self.chunk.bytecode.push(0xff);
self.bytecode[jif_jump_idx] = self.chunk.bytecode[jif_jump_idx] =
self.bytecode.len() as u8 - jif_jump_idx as u8 - 1; self.chunk.bytecode.len() as u8 - jif_jump_idx as u8 - 1;
} }
self.emit_op(Op::PanicNoWhen); self.emit_op(Op::PanicNoWhen);
for idx in jump_idxes { for idx in jump_idxes {
self.bytecode[idx] = self.bytecode.len() as u8 - idx as u8 + 1; self.chunk.bytecode[idx] = self.chunk.bytecode.len() as u8 - idx as u8 + 1;
} }
} }
Match(scrutinee, clauses) => { Match(scrutinee, clauses) => {
@ -409,8 +439,8 @@ impl Chunk {
self.scope_depth += 1; self.scope_depth += 1;
self.visit(pattern); self.visit(pattern);
self.emit_op(Op::JumpIfNoMatch); self.emit_op(Op::JumpIfNoMatch);
let jnm_jump_idx = self.bytecode.len(); let jnm_jump_idx = self.chunk.bytecode.len();
self.bytecode.push(0xff); self.chunk.bytecode.push(0xff);
self.visit(body); self.visit(body);
self.emit_op(Op::Store); self.emit_op(Op::Store);
self.scope_depth -= 1; self.scope_depth -= 1;
@ -423,15 +453,15 @@ impl Chunk {
} }
} }
self.emit_op(Op::Jump); self.emit_op(Op::Jump);
jump_idxes.push(self.bytecode.len()); jump_idxes.push(self.chunk.bytecode.len());
self.bytecode.push(0xff); self.chunk.bytecode.push(0xff);
self.bytecode[jnm_jump_idx] = self.chunk.bytecode[jnm_jump_idx] =
self.bytecode.len() as u8 - jnm_jump_idx as u8 - 1; self.chunk.bytecode.len() as u8 - jnm_jump_idx as u8 - 1;
} }
self.emit_op(Op::PanicNoMatch); self.emit_op(Op::PanicNoMatch);
self.emit_op(Op::Load); self.emit_op(Op::Load);
for idx in jump_idxes { for idx in jump_idxes {
self.bytecode[idx] = self.bytecode.len() as u8 - idx as u8 + 2; self.chunk.bytecode[idx] = self.chunk.bytecode.len() as u8 - idx as u8 + 2;
} }
} }
Fn(name, body, doc) => { Fn(name, body, doc) => {
@ -442,17 +472,17 @@ impl Chunk {
self.bind(name); self.bind(name);
// compile the function // compile the function
let mut chunk = Chunk::new(body, self.name, self.src); let mut compiler = Compiler::new(body, self.name, self.src);
chunk.compile(); compiler.compile();
if crate::DEBUG_COMPILE { if crate::DEBUG_COMPILE {
println!("==function: {name}=="); println!("==function: {name}==");
chunk.disassemble(); compiler.disassemble();
} }
let lfn = crate::value::LFn { let lfn = crate::value::LFn {
name, name,
doc: *doc, doc: *doc,
chunk, chunk: compiler.chunk,
closed: vec![], closed: vec![],
}; };
@ -475,7 +505,7 @@ impl Chunk {
pub fn disassemble(&self) { pub fn disassemble(&self) {
println!("=== chunk: {} ===", self.name); println!("=== chunk: {} ===", self.name);
println!("IDX | CODE | INFO"); println!("IDX | CODE | INFO");
let mut codes = self.bytecode.iter().enumerate(); let mut codes = self.chunk.bytecode.iter().enumerate();
while let Some((i, byte)) = codes.next() { while let Some((i, byte)) = codes.next() {
let op = Op::from_u8(*byte).unwrap(); let op = Op::from_u8(*byte).unwrap();
use Op::*; use Op::*;
@ -486,7 +516,7 @@ impl Chunk {
} }
Constant | MatchConstant => { Constant | MatchConstant => {
let (_, next) = codes.next().unwrap(); let (_, next) = codes.next().unwrap();
let value = &self.constants[*next as usize].show(self); let value = &self.chunk.constants[*next as usize].show(&self.chunk);
println!("{i:04}: {:16} {next:04}: {value}", op.to_string()); println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
} }
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
@ -497,25 +527,4 @@ impl Chunk {
} }
} }
} }
pub fn dissasemble_instr(&self, i: usize) {
let op = Op::from_u8(self.bytecode[i]).unwrap();
use Op::*;
match op {
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
| PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch => {
println!("{i:04}: {op}")
}
Constant | MatchConstant => {
let next = self.bytecode[i + 1];
let value = &self.constants[next as usize].show(self);
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
}
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
| JumpIfFalse | JumpIfNoMatch => {
let next = self.bytecode[i + 1];
println!("{i:04}: {:16} {next:04}", op.to_string());
}
}
}
} }

View File

@ -17,7 +17,7 @@ use crate::parser::{parser, Ast};
mod validator; mod validator;
mod compiler; mod compiler;
use crate::compiler::Chunk; use crate::compiler::Compiler;
mod value; mod value;
@ -46,10 +46,10 @@ pub fn run(src: &'static str) {
// in any event, the AST should live forever // in any event, the AST should live forever
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap())); let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));
let mut chunk = Chunk::new(parsed, "test", src); let mut compiler = Compiler::new(parsed, "test", src);
chunk.compile(); compiler.compile();
if DEBUG_COMPILE { if DEBUG_COMPILE {
chunk.disassemble(); compiler.disassemble();
println!("\n\n") println!("\n\n")
} }
@ -57,10 +57,10 @@ pub fn run(src: &'static str) {
println!("=== vm run: test ==="); println!("=== vm run: test ===");
} }
let mut vm = Vm::new(&chunk); let mut vm = Vm::new(&compiler.chunk);
let result = vm.interpret(); let result = vm.interpret();
let output = match result { let output = match result {
Ok(val) => val.show(&chunk), Ok(val) => val.show(&compiler.chunk),
Err(panic) => format!("{:?}", panic), Err(panic) => format!("{:?}", panic),
}; };
println!("{output}"); println!("{output}");

View File

@ -78,7 +78,7 @@ impl std::fmt::Display for Value {
.join(", ") .join(", ")
), ),
Box(value) => write!(f, "box {}", value.as_ref().borrow()), Box(value) => write!(f, "box {}", value.as_ref().borrow()),
Fn(lfn) => write!(f, "fn {}", lfn.name), Fn(lfn) => write!(f, "fn {}", lfn.get().unwrap().name),
_ => todo!(), _ => todo!(),
} }
} }
@ -122,7 +122,7 @@ impl Value {
} }
String(s) => s.as_ref().clone(), String(s) => s.as_ref().clone(),
Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show(ctx)), Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show(ctx)),
Fn(lfn) => format!("fn {}", lfn.name), Fn(lfn) => format!("fn {}", lfn.get().unwrap().name),
_ => todo!(), _ => todo!(),
} }
} }