diff --git a/src/compiler.rs b/src/compiler.rs index 56501f0..15502cc 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -80,14 +80,41 @@ pub struct Binding { #[derive(Clone, Debug, PartialEq)] pub struct Chunk { + pub constants: Vec, + pub bytecode: Vec, + 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, scope_depth: isize, num_bindings: usize, - pub constants: Vec, - pub bytecode: Vec, pub spans: Vec, - pub strings: Vec<&'static str>, - pub keywords: Vec<&'static str>, pub nodes: Vec<&'static Ast>, pub ast: &'static Ast, pub span: SimpleSpan, @@ -105,19 +132,22 @@ fn is_binding(expr: &Spanned) -> bool { } } -impl Chunk { - pub fn new(ast: &'static Spanned, name: &'static str, src: &'static str) -> Chunk { - Chunk { - bindings: vec![], - scope_depth: -1, - num_bindings: 0, +impl Compiler { + pub fn new(ast: &'static Spanned, name: &'static str, src: &'static str) -> Compiler { + let chunk = Chunk { constants: vec![], bytecode: vec![], - spans: vec![], strings: vec![], keywords: vec![ "nil", "bool", "number", "keyword", "string", "tuple", "list", "dict", "box", "fn", ], + }; + Compiler { + chunk, + bindings: vec![], + scope_depth: -1, + num_bindings: 0, + spans: vec![], nodes: vec![], ast: &ast.0, span: ast.1, @@ -131,7 +161,7 @@ impl Chunk { } pub fn kw_index_from(&self, kw: &str) -> Option { - self.keywords.iter().position(|s| *s == kw) + self.chunk.keywords.iter().position(|s| *s == kw) } pub fn visit(&mut self, node: &'static Spanned) { @@ -146,24 +176,24 @@ impl Chunk { } 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 { 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.chunk.constants.push(val); + self.chunk.bytecode.push(Op::Constant as u8); 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); } 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, - None => self.constants.len(), + None => self.chunk.constants.len(), }; if constant_index > u8::MAX as usize { panic!( @@ -171,18 +201,18 @@ impl Chunk { self.span, self.ast ) } - if constant_index == self.constants.len() { - self.constants.push(val); + if constant_index == self.chunk.constants.len() { + 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.bytecode.push(constant_index as u8); + self.chunk.bytecode.push(constant_index as u8); self.spans.push(self.span); self.bind(""); } 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); } @@ -200,22 +230,22 @@ impl Chunk { Number(n) => self.emit_constant(Value::Number(*n)), Boolean(b) => self.emit_op(if *b { Op::True } else { Op::False }), 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 { 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)); } 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 { Some(index) => index, - None => self.keywords.len(), + None => self.chunk.keywords.len(), }; - if kw_index == self.keywords.len() { - self.keywords.push(s); + if kw_index == self.chunk.keywords.len() { + self.chunk.keywords.push(s); } self.emit_constant(Value::Keyword(kw_index)); } @@ -244,19 +274,19 @@ impl Chunk { } If(cond, then, r#else) => { self.visit(cond); - let jif_idx = self.bytecode.len(); + let jif_idx = self.chunk.bytecode.len(); self.emit_op(Op::JumpIfFalse); - self.bytecode.push(0xff); + self.chunk.bytecode.push(0xff); self.visit(then); - let jump_idx = self.bytecode.len(); + let jump_idx = self.chunk.bytecode.len(); self.emit_op(Op::Jump); - self.bytecode.push(0xff); + self.chunk.bytecode.push(0xff); 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 jump_offset = end_idx - jump_idx; - self.bytecode[jif_idx + 1] = jif_offset as u8; - self.bytecode[jump_idx + 1] = jump_offset as u8; + self.chunk.bytecode[jif_idx + 1] = jif_offset as u8; + self.chunk.bytecode[jump_idx + 1] = jump_offset as u8; } Let(patt, expr) => { self.emit_op(Op::ResetMatch); @@ -273,7 +303,7 @@ impl Chunk { let biter = self.bindings.iter().enumerate().rev(); for (i, binding) in biter { if binding.name == *name { - self.bytecode.push(i as u8); + self.chunk.bytecode.push(i as u8); break; } } @@ -299,24 +329,24 @@ impl Chunk { self.match_constant(Value::Number(*n)); } 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 { Some(index) => index, - None => self.keywords.len(), + None => self.chunk.keywords.len(), }; - if kw_index == self.keywords.len() { - self.keywords.push(s); + if kw_index == self.chunk.keywords.len() { + self.chunk.keywords.push(s); } self.match_constant(Value::Keyword(kw_index)); } 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 { Some(idx) => idx, - None => self.strings.len(), + None => self.chunk.strings.len(), }; - if str_index == self.strings.len() { - self.strings.push(s) + if str_index == self.chunk.strings.len() { + self.chunk.strings.push(s) } self.match_constant(Value::Interned(str_index)); } @@ -325,14 +355,14 @@ impl Chunk { self.visit(member); } self.emit_op(Op::PushTuple); - self.bytecode.push(members.len() as u8); + self.chunk.bytecode.push(members.len() as u8); } List(members) => { for member in members { self.visit(member); } self.emit_op(Op::PushList); - self.bytecode.push(members.len() as u8); + self.chunk.bytecode.push(members.len() as u8); } LBox(name, expr) => { self.visit(expr); @@ -344,16 +374,16 @@ impl Chunk { self.visit(pair); } self.emit_op(Op::PushDict); - self.bytecode.push(pairs.len() as u8); + self.chunk.bytecode.push(pairs.len() as u8); } 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 { Some(index) => index, - None => self.keywords.len(), + None => self.chunk.keywords.len(), }; - if kw_index == self.keywords.len() { - self.keywords.push(key); + if kw_index == self.chunk.keywords.len() { + self.chunk.keywords.push(key); } self.emit_constant(Value::Keyword(kw_index)); self.visit(value); @@ -386,18 +416,18 @@ impl Chunk { while let Some((WhenClause(cond, body), _)) = clauses.next() { self.visit(cond.as_ref()); self.emit_op(Op::JumpIfFalse); - let jif_jump_idx = self.bytecode.len(); - self.bytecode.push(0xff); + let jif_jump_idx = self.chunk.bytecode.len(); + self.chunk.bytecode.push(0xff); self.visit(body); self.emit_op(Op::Jump); - jump_idxes.push(self.bytecode.len()); - self.bytecode.push(0xff); - self.bytecode[jif_jump_idx] = - self.bytecode.len() as u8 - jif_jump_idx as u8 - 1; + jump_idxes.push(self.chunk.bytecode.len()); + self.chunk.bytecode.push(0xff); + self.chunk.bytecode[jif_jump_idx] = + self.chunk.bytecode.len() as u8 - jif_jump_idx as u8 - 1; } self.emit_op(Op::PanicNoWhen); 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) => { @@ -409,8 +439,8 @@ impl Chunk { self.scope_depth += 1; self.visit(pattern); self.emit_op(Op::JumpIfNoMatch); - let jnm_jump_idx = self.bytecode.len(); - self.bytecode.push(0xff); + let jnm_jump_idx = self.chunk.bytecode.len(); + self.chunk.bytecode.push(0xff); self.visit(body); self.emit_op(Op::Store); self.scope_depth -= 1; @@ -423,15 +453,15 @@ impl Chunk { } } self.emit_op(Op::Jump); - jump_idxes.push(self.bytecode.len()); - self.bytecode.push(0xff); - self.bytecode[jnm_jump_idx] = - self.bytecode.len() as u8 - jnm_jump_idx as u8 - 1; + jump_idxes.push(self.chunk.bytecode.len()); + self.chunk.bytecode.push(0xff); + self.chunk.bytecode[jnm_jump_idx] = + self.chunk.bytecode.len() as u8 - jnm_jump_idx as u8 - 1; } self.emit_op(Op::PanicNoMatch); self.emit_op(Op::Load); 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) => { @@ -442,17 +472,17 @@ impl Chunk { self.bind(name); // compile the function - let mut chunk = Chunk::new(body, self.name, self.src); - chunk.compile(); + let mut compiler = Compiler::new(body, self.name, self.src); + compiler.compile(); if crate::DEBUG_COMPILE { println!("==function: {name}=="); - chunk.disassemble(); + compiler.disassemble(); } let lfn = crate::value::LFn { name, doc: *doc, - chunk, + chunk: compiler.chunk, closed: vec![], }; @@ -475,7 +505,7 @@ impl Chunk { pub fn disassemble(&self) { println!("=== chunk: {} ===", self.name); 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() { let op = Op::from_u8(*byte).unwrap(); use Op::*; @@ -486,7 +516,7 @@ impl Chunk { } Constant | MatchConstant => { 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()); } 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()); - } - } - } } diff --git a/src/main.rs b/src/main.rs index 7a65c1e..14c0b3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use crate::parser::{parser, Ast}; mod validator; mod compiler; -use crate::compiler::Chunk; +use crate::compiler::Compiler; mod value; @@ -46,10 +46,10 @@ pub fn run(src: &'static str) { // in any event, the AST should live forever let parsed: &'static Spanned = Box::leak(Box::new(parse_result.unwrap())); - let mut chunk = Chunk::new(parsed, "test", src); - chunk.compile(); + let mut compiler = Compiler::new(parsed, "test", src); + compiler.compile(); if DEBUG_COMPILE { - chunk.disassemble(); + compiler.disassemble(); println!("\n\n") } @@ -57,10 +57,10 @@ pub fn run(src: &'static str) { println!("=== vm run: test ==="); } - let mut vm = Vm::new(&chunk); + let mut vm = Vm::new(&compiler.chunk); let result = vm.interpret(); let output = match result { - Ok(val) => val.show(&chunk), + Ok(val) => val.show(&compiler.chunk), Err(panic) => format!("{:?}", panic), }; println!("{output}"); diff --git a/src/value.rs b/src/value.rs index fcf0ce9..2b5ca14 100644 --- a/src/value.rs +++ b/src/value.rs @@ -78,7 +78,7 @@ impl std::fmt::Display for Value { .join(", ") ), 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!(), } } @@ -122,7 +122,7 @@ impl Value { } String(s) => s.as_ref().clone(), 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!(), } }