From 35fc591c76ba68e94f9a25a0095f653cf263e651 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Sun, 15 Dec 2024 23:28:57 -0500 Subject: [PATCH] make some progress: atoms and ifs --- src/compiler.rs | 127 ++++++++++++++++++++++++++++++++++++++++------- src/validator.rs | 12 ++--- src/value.rs | 45 +++++++++++++++-- 3 files changed, 155 insertions(+), 29 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index e5cd28e..402ec18 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -6,14 +6,37 @@ use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::FromPrimitive; #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)] -enum Op { +pub enum Op { Return, Constant, + Jump, + JumpIfFalse, +} + +impl std::fmt::Display for Op { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use Op::*; + match self { + Return => write!(f, "return"), + Constant => write!(f, "constant"), + Jump => write!(f, "jump"), + JumpIfFalse => write!(f, "jump_if_false"), + } + } } #[derive(Clone, Debug, PartialEq)] -struct Chunk<'a> { - pub constants: Vec>, +pub struct Local { + name: &'static str, + depth: u8, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Chunk<'a> { + pub locals: Vec, + scope_depth: usize, + local_count: usize, + pub constants: Vec, pub bytecode: Vec, pub spans: Vec, pub strings: Vec<&'static str>, @@ -26,7 +49,34 @@ struct Chunk<'a> { } impl<'a> Chunk<'a> { - fn visit(&mut self, node: &'a Spanned) { + pub fn new(ast: &'a Spanned, name: &'static str, src: &'static str) -> Chunk<'a> { + Chunk { + locals: vec![], + scope_depth: 0, + local_count: 0, + constants: vec![], + bytecode: vec![], + spans: vec![], + strings: vec![], + keywords: vec![ + "nil", "bool", "number", "keyword", "string", "tuple", "list", "", + ], + nodes: vec![], + ast: &ast.0, + span: ast.1, + src, + name, + } + } + + pub fn kw_from(&self, kw: &str) -> Option { + self.keywords + .iter() + .position(|s| *s == kw) + .map(Value::Keyword) + } + + pub fn visit(&mut self, node: &'a Spanned) { let root_node = self.ast; let root_span = self.span; let (ast, span) = node; @@ -37,7 +87,7 @@ impl<'a> Chunk<'a> { self.span = root_span; } - fn emit_constant(&mut self, val: Value<'a>) { + fn emit_constant(&mut self, val: Value) { let constant_index = self.constants.len(); if constant_index > u8::MAX as usize { panic!( @@ -57,39 +107,78 @@ impl<'a> Chunk<'a> { self.spans.push(self.span); } - fn compile(&mut self) { + pub fn compile(&mut self) { + use Ast::*; 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) => { + Nil => self.emit_constant(Value::Nil), + Number(n) => self.emit_constant(Value::Number(*n)), + Boolean(b) => self.emit_constant(if *b { Value::True } else { Value::False }), + String(s) => { let str_index = self.strings.len(); self.strings.push(s); - self.emit_constant(Value::InternedString(s)); + self.emit_constant(Value::Interned(str_index)); } - Ast::Block(lines) => { + Keyword(s) => { + let existing_kw = self.keywords.iter().position(|kw| kw == s); + let kw_index = match existing_kw { + Some(index) => index, + None => self.keywords.len(), + }; + self.keywords.push(s); + self.emit_constant(Value::Keyword(kw_index)); + } + Block(lines) => { + self.scope_depth += 1; for expr in lines { self.visit(expr); } self.emit_op(Op::Return); + self.scope_depth -= 1; } + If(cond, then, r#else) => { + self.visit(cond); + let jif_idx = self.bytecode.len(); + self.emit_op(Op::JumpIfFalse); + self.bytecode.push(0xff); + self.visit(then); + let jump_idx = self.bytecode.len(); + self.emit_op(Op::Jump); + self.bytecode.push(0xff); + self.visit(r#else); + let end_idx = self.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; + } + // Let(patt, expr) => { + // self.visit(expr); + // self.visit(patt); + // } + // WordPattern(name) => {} + // PlaceholderPattern => {} _ => todo!(), } } - fn disassemble(&self) { + pub fn disassemble(&self) { println!("=== chunk: {} ===", self.name); + println!("IDX | CODE | INFO"); let mut codes = self.bytecode.iter().enumerate(); while let Some((i, byte)) = codes.next() { - let op = Op::from_u8(*byte); + let op = Op::from_u8(*byte).unwrap(); + use Op::*; match op { - Some(Op::Return) => println!("{i:04}: {byte:16}"), - Some(Op::Constant) => { + Return => println!("{i:04}: {op}"), + Constant => { let (_, next) = codes.next().unwrap(); - let value = &self.constants[*next as usize]; - println!("i:04: {byte:16} {next:16} {value}") + let value = &self.constants[*next as usize].show(self); + println!("{i:04}: {:16} {next:04}: {value}", op.to_string()); + } + Jump | JumpIfFalse => { + let (_, next) = codes.next().unwrap(); + println!("{i:04}: {:16} {next:04}", op.to_string()) } - _ => unreachable!(), } } } diff --git a/src/validator.rs b/src/validator.rs index 4ff9ade..7b9698a 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -53,9 +53,9 @@ fn match_arities(arities: &HashSet, num_args: u8) -> bool { } #[derive(Debug, PartialEq)] -pub struct Validator<'a, 'src> { +pub struct Validator<'a> { pub locals: Vec<(String, Span, FnInfo)>, - pub prelude: &'a Vec<(String, Value<'src>)>, + pub prelude: &'a Vec<(&'static str, Value)>, pub input: &'static str, pub src: &'static str, pub ast: &'a Ast, @@ -65,14 +65,14 @@ pub struct Validator<'a, 'src> { status: VStatus, } -impl<'a, 'src: 'a> Validator<'a, 'src> { +impl<'a> Validator<'a> { pub fn new( ast: &'a Ast, span: Span, input: &'static str, src: &'static str, - prelude: &'a Vec<(String, Value<'src>)>, - ) -> Validator<'a, 'src> { + prelude: &'a Vec<(&'static str, Value)>, + ) -> Validator<'a> { Validator { input, src, @@ -109,7 +109,7 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { fn resolved(&self, name: &str) -> bool { self.locals.iter().any(|(bound, ..)| name == bound.as_str()) - || self.prelude.iter().any(|(bound, _)| name == bound.as_str()) + || self.prelude.iter().any(|(bound, _)| name == *bound) } fn bound(&self, name: &str) -> Option<&(String, Span, FnInfo)> { diff --git a/src/value.rs b/src/value.rs index 077e646..4cfbc79 100644 --- a/src/value.rs +++ b/src/value.rs @@ -25,23 +25,60 @@ pub struct LFn { #[derive(Clone, Debug, PartialEq)] pub enum Value { Nil, - Boolean(bool), + True, + False, Keyword(usize), // use an idx, rather than a raw index Interned(usize), FnDecl(usize), String(Rc), Number(f64), + Tuple(Rc>), + TupleStart { len: u8, size: u16 }, + TupleEnd { len: u8, size: u16 }, List(Box>), - Dict(Box>), + Dict(Box>), Box(Rc), Fn(Rc>), } impl Value { - fn show(&self, ctx: &Chunk) -> String { + pub fn show(&self, ctx: &Chunk) -> String { use Value::*; match &self { - Nil => format!("nil"), + Nil => "nil".to_string(), + True => "true".to_string(), + False => "false".to_string(), + Number(n) => format!("{n}"), + Interned(i) => { + let str_str = ctx.strings[*i]; + format!("\"{str_str}\"") + } + Keyword(i) => { + let kw_str = ctx.keywords[*i]; + format!(":{kw_str}") + } + Tuple(t) => { + let members = t.iter().map(|e| e.show(ctx)).collect::>().join(", "); + format!("({members})") + } + List(l) => { + let members = l.iter().map(|e| e.show(ctx)).collect::>().join(", "); + format!("[{members}]") + } + Dict(d) => { + let members = d + .iter() + .map(|(k, v)| { + let key_show = Value::Keyword(*k).show(ctx); + let value_show = v.show(ctx); + format!("{key_show} {value_show}") + }) + .collect::>() + .join(", "); + format!("#{{{members}}}") + } + String(s) => s.as_ref().clone(), + _ => todo!(), } } }