use core::fmt::{self, Display}; use alloc::{borrow::ToOwned, string::String, vec::Vec}; use crate::source::Span; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(u8)] pub enum Opcode { // Push literal values onto the stack. Nil, False, True, Tag, Number, // (float: f32) Rgba, // (r: u8, g: u8, b: u8, a: u8) // Duplicate existing values. /// Push a value relative to the bottom of the current stack window. Local, // (index: u8) /// Set the value of a value relative to the bottom of the current stack window. SetLocal, // (index: u8) /// Push a captured value. Capture, // (index: u8) /// Get the value of a definition. Def, // (index: u16) /// Set the value of a definition. SetDef, // (index: u16) // Create lists. List, // (len: u16) // Create literal functions. Function, // (params: u8, then: u16), at `then`: (local_count: u8, capture_count: u8, captures: [(source: u8, index: u8); capture_count]) // Control flow. Jump, // (offset: u16) JumpIfNot, // (offset: u16) Field, // (count: u8, tags: [u16; count]) // Function calls. Call, // (argc: u8) /// This is a fast path for system calls, which are quite common (e.g. basic arithmetic.) System, // (index: u8, argc: u8) Return, // NOTE: There must be no more opcodes after this. // They will get treated as invalid. } // Constants used by the Function opcode to indicate capture sources. pub const CAPTURE_LOCAL: u8 = 0; pub const CAPTURE_CAPTURE: u8 = 1; #[derive(Debug, Clone)] pub struct Chunk { pub bytecode: Vec, pub span_info: Vec, pub current_span: Span, } #[derive(Debug, Clone)] pub struct SpanRun { pub span: Span, pub len: u16, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Offset(u16); impl Chunk { pub fn new(capacity: usize) -> Result { if capacity <= (1 << 16) { Ok(Chunk { bytecode: Vec::with_capacity(capacity), span_info: Vec::new(), current_span: Span::new(0, 0), }) } else { Err(ChunkSizeError) } } pub fn offset(&self) -> Offset { Offset(self.bytecode.len() as u16) } fn push_span_info(&mut self, span: Span, len: u16) { if let Some(info) = self.span_info.last_mut() { if info.span == span { info.len += len; return; } } self.span_info.push(SpanRun { span, len }); } pub fn emit_bytes(&mut self, bytes: &[u8]) -> Result { if self.bytecode.len() + bytes.len() > self.bytecode.capacity() { return Err(EmitError); } let offset = Offset(self.bytecode.len() as u16); self.bytecode.extend_from_slice(bytes); self.push_span_info(self.current_span, bytes.len() as u16); Ok(offset) } pub fn emit_opcode(&mut self, opcode: Opcode) -> Result { self.emit_bytes(&[opcode as u8]) } pub fn emit_u8(&mut self, x: u8) -> Result { self.emit_bytes(&[x]) } pub fn emit_u16(&mut self, x: u16) -> Result { self.emit_bytes(&x.to_le_bytes()) } pub fn emit_u32(&mut self, x: u32) -> Result { self.emit_bytes(&x.to_le_bytes()) } pub fn emit_f32(&mut self, x: f32) -> Result { self.emit_bytes(&x.to_le_bytes()) } pub fn patch_u8(&mut self, offset: Offset, x: u8) { self.bytecode[offset.0 as usize] = x; } pub fn patch_u16(&mut self, offset: Offset, x: u16) { let b = x.to_le_bytes(); let i = offset.0 as usize; self.bytecode[i] = b[0]; self.bytecode[i + 1] = b[1]; } pub fn patch_offset(&mut self, offset: Offset, x: Offset) { self.patch_u16(offset, x.0); } pub fn find_span(&self, pc: u16) -> Option<&Span> { let mut cur = 0; for info in &self.span_info { if pc >= cur && pc < cur + info.len { return Some(&info.span); } cur += info.len; } None } } impl Offset { pub fn to_u16(self) -> u16 { self.0 } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ChunkSizeError; impl Display for ChunkSizeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "chunk size must be less than 64 KiB") } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct EmitError; impl Display for EmitError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "out of space in chunk") } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub struct DefId(u16); impl DefId { pub fn to_u16(self) -> u16 { self.0 } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct TagId(u16); impl TagId { pub(crate) fn from_u16(x: u16) -> Self { Self(x) } pub fn to_u16(self) -> u16 { self.0 } } #[derive(Debug, Clone)] pub struct Defs { defs: Vec, tags: Vec, } #[derive(Debug, Clone, Copy)] pub struct DefsLimits { pub max_defs: usize, pub max_tags: usize, } #[derive(Debug, Clone, Copy)] pub struct DefsImage { defs: usize, tags: usize, } impl Defs { pub fn new(limits: &DefsLimits) -> Self { assert!(limits.max_defs < u16::MAX as usize + 1); assert!(limits.max_tags < u16::MAX as usize + 1); let mut tags = Vec::with_capacity(limits.max_tags); add_well_known_tags(&mut tags); Self { defs: Vec::with_capacity(limits.max_defs), tags, } } pub fn len(&self) -> u16 { self.defs.len() as u16 } pub fn is_empty(&self) -> bool { self.len() != 0 } pub fn get_def(&mut self, name: &str) -> Option { self.defs .iter() .position(|n| *n == name) .map(|index| DefId(index as u16)) } pub fn add_def(&mut self, name: &str) -> Result { if self.defs.iter().any(|n| n == name) { Err(DefError::Exists) } else { if self.defs.len() >= self.defs.capacity() { return Err(DefError::OutOfSpace); } let id = DefId(self.defs.len() as u16); self.defs.push(name.to_owned()); Ok(id) } } fn add_tag(tags: &mut Vec, name: &str) -> Result { if tags.len() >= tags.capacity() { return Err(TagError::OutOfSpace); } let id = TagId(tags.len() as u16); tags.push(name.to_owned()); Ok(id) } pub fn get_or_add_tag(&mut self, name: &str) -> Result { if let Some(index) = self.tags.iter().position(|n| n == name) { Ok(TagId(index as u16)) } else { Self::add_tag(&mut self.tags, name) } } pub fn image(&self) -> DefsImage { DefsImage { defs: self.defs.len(), tags: self.tags.len(), } } pub fn restore_image(&mut self, image: &DefsImage) { self.defs.resize_with(image.defs, || { panic!("image must be a subset of the current defs") }); self.tags.resize_with(image.tags, || { panic!("image must be a subset of the current defs") }); } pub fn serialize_defs(&self) -> String { let mut result = String::new(); for def in &self.defs { result.push_str(def); result.push('\n'); } result } pub fn serialize_tags(&self) -> String { let mut result = String::new(); for tag in &self.tags { result.push_str(tag); result.push('\n'); } result } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DefError { Exists, OutOfSpace, } impl Display for DefError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match self { DefError::Exists => "definition already exists", DefError::OutOfSpace => "too many definitions", }) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TagError { OutOfSpace, } impl Display for TagError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match self { TagError::OutOfSpace => "too many tags", }) } } macro_rules! well_known_tags { ($($ident:tt = $value:tt),* $(,)?) => { impl TagId { $( #[allow(non_upper_case_globals)] pub const $ident: Self = Self($value); )* } fn add_well_known_tags(tags: &mut Vec) { $( let id = Defs::add_tag(tags, stringify!($ident)).unwrap(); assert_eq!(id, TagId::from_u16($value)); )* } } } well_known_tags! { // NOTE: The numbers must be sorted from 0 to N, due to limitations of Rust's macro system. // https://github.com/rust-lang/rust/issues/83527 Nil = 0, From = 1, To = 2, Num = 3, }