introduce tags, structs, and reticles

this was meant to be split into smaller changes, but I realised I edited my existing revision too late.
This commit is contained in:
りき萌 2024-09-08 13:53:29 +02:00
parent 8356b6c750
commit 5b7d9586ea
26 changed files with 1113 additions and 351 deletions

View file

@ -12,6 +12,7 @@ pub enum Opcode {
Nil,
False,
True,
Tag,
Number, // (float: f32)
Rgba, // (r: u8, g: u8, b: u8, a: u8)
@ -36,6 +37,7 @@ pub enum Opcode {
// Control flow.
Jump, // (offset: u16)
JumpIfNot, // (offset: u16)
Field, // (count: u8, tags: [u16; count])
// Function calls.
Call, // (argc: u8)
@ -157,6 +159,12 @@ impl Chunk {
}
}
impl Offset {
pub fn to_u16(self) -> u16 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ChunkSizeError;
@ -193,21 +201,48 @@ impl DefId {
}
}
#[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<String>,
tags: Vec<String>,
}
#[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(capacity: usize) -> Self {
assert!(capacity < u16::MAX as usize + 1);
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(capacity),
defs: Vec::with_capacity(limits.max_defs),
tags,
}
}
@ -219,14 +254,14 @@ impl Defs {
self.len() != 0
}
pub fn get(&mut self, name: &str) -> Option<DefId> {
pub fn get_def(&mut self, name: &str) -> Option<DefId> {
self.defs
.iter()
.position(|n| *n == name)
.map(|index| DefId(index as u16))
}
pub fn add(&mut self, name: &str) -> Result<DefId, DefError> {
pub fn add_def(&mut self, name: &str) -> Result<DefId, DefError> {
if self.defs.iter().any(|n| n == name) {
Err(DefError::Exists)
} else {
@ -239,9 +274,27 @@ impl Defs {
}
}
fn add_tag(tags: &mut Vec<String>, name: &str) -> Result<TagId, TagError> {
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<TagId, TagError> {
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(),
}
}
@ -249,6 +302,9 @@ impl Defs {
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")
});
}
}
@ -266,3 +322,45 @@ impl Display for DefError {
})
}
}
#[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<String>) {
$(
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,
}