talc/talc-lang/src/chunk.rs

223 lines
5.6 KiB
Rust

use crate::{value::Value, ast::{UnaryOp, BinaryOp}, symbol::Symbol};
#[derive(Clone, Copy, Debug, Default)]
pub struct Arg24([u8; 3]);
impl Arg24 {
#[inline]
pub fn from_u32(n: u32) -> Self {
assert!(n <= 0xff_ffff, "value out of range for argument");
// can't panic: size of slice guaranteed
Self(n.to_le_bytes()[0..3].try_into().unwrap())
}
#[inline]
pub fn from_i32(n: i32) -> Self {
assert!((-0x80_0000..=0x7f_ffff).contains(&n), "value out of range for argument");
// can't panic: size of slice guaranteed
Self(n.to_le_bytes()[0..3].try_into().unwrap())
}
#[inline]
pub fn from_i64(n: i64) -> Self {
assert!((-0x80_0000..=0x7f_ffff).contains(&n), "value out of range for argument");
// can't panic: size of slice guaranteed
Self(n.to_le_bytes()[0..3].try_into().unwrap())
}
#[inline]
pub fn from_usize(n: usize) -> Self {
assert!(n <= 0xff_ffff, "value out of range for argument");
// can't panic: size of slice guaranteed
Self(n.to_le_bytes()[0..3].try_into().unwrap())
}
#[inline]
pub fn from_symbol(s: Symbol) -> Self {
Self::from_u32(s.id())
}
/// # Safety
/// Safe if argument is a valid symbol ID
#[inline]
pub unsafe fn to_symbol_unchecked(self) -> Symbol {
Symbol::from_id_unchecked(u32::from(self))
}
}
impl From<Arg24> for u32 {
#[inline]
fn from(v: Arg24) -> Self {
u32::from_le_bytes([v.0[0], v.0[1], v.0[2], 0])
}
}
impl From<Arg24> for usize {
#[inline]
fn from(v: Arg24) -> Self {
u32::from(v) as usize
}
}
impl From<Arg24> for i32 {
#[inline]
fn from(v: Arg24) -> Self {
let mut n = u32::from(v);
// sign-extend
if n & 0x00_800000 != 0 { n |= 0xff_000000; }
n as i32
}
}
impl From<Arg24> for i64 {
#[inline]
fn from(v: Arg24) -> Self {
i32::from(v) as i64
}
}
impl TryFrom<Arg24> for Symbol {
type Error = ();
#[inline]
fn try_from(value: Arg24) -> Result<Self, Self::Error> {
Symbol::from_id(u32::from(value)).ok_or(())
}
}
#[repr(u8, C, align(4))]
#[derive(Clone, Copy, Debug, Default)]
pub enum Instruction {
#[default]
Nop,
LoadLocal(Arg24),
StoreLocal(Arg24),
NewLocal,
DropLocal(Arg24),
LoadGlobal(Arg24), StoreGlobal(Arg24),
Const(Arg24),
Int(Arg24),
Symbol(Arg24),
Bool(bool),
Nil,
Dup, DupTwo, Drop(Arg24), Swap,
UnaryOp(UnaryOp),
BinaryOp(BinaryOp),
NewList(u8), GrowList(u8),
NewTable(u8), GrowTable(u8),
Index, StoreIndex,
Jump(Arg24),
JumpTrue(Arg24),
JumpFalse(Arg24),
IterBegin, IterTest(Arg24),
BeginTry(Arg24), EndTry,
Call(u8),
Return,
}
impl std::fmt::Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::Nop => write!(f, "nop"),
Self::LoadLocal(a) => write!(f, "load {}", usize::from(a)),
Self::StoreLocal(a) => write!(f, "store {}", usize::from(a)),
Self::NewLocal => write!(f, "newlocal"),
Self::DropLocal(n) => write!(f, "discardlocal {}", usize::from(n)),
Self::LoadGlobal(s) => write!(f, "loadglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()),
Self::StoreGlobal(s) => write!(f, "storeglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()),
Self::Const(c) => write!(f, "const {}", usize::from(c)),
Self::Int(i) => write!(f, "int {}", i64::from(i)),
Self::Symbol(s) => write!(f, "symbol {}",
Symbol::try_from(s).expect("symbol does not exist").name()),
Self::Bool(b) => write!(f, "bool {b}"),
Self::Nil => write!(f, "nil"),
Self::Dup => write!(f, "dup"),
Self::DupTwo => write!(f, "duptwo"),
Self::Drop(n) => write!(f, "discard {}", usize::from(n)),
Self::Swap => write!(f, "swap"),
Self::UnaryOp(o) => write!(f, "unary {o:?}"),
Self::BinaryOp(o) => write!(f, "binary {o:?}"),
Self::NewList(n) => write!(f, "newlist {n}"),
Self::GrowList(n) => write!(f, "growlist {n}"),
Self::NewTable(n) => write!(f, "newtable {n}"),
Self::GrowTable(n) => write!(f, "growtable {n}"),
Self::Index => write!(f, "index"),
Self::StoreIndex => write!(f, "storeindex"),
Self::Jump(a) => write!(f, "jump {}", usize::from(a)),
Self::JumpTrue(a) => write!(f, "jumptrue {}", usize::from(a)),
Self::JumpFalse(a) => write!(f, "jumpfalse {}", usize::from(a)),
Self::IterBegin => write!(f, "iterbegin"),
Self::IterTest(a) => write!(f, "itertest {}", usize::from(a)),
Self::BeginTry(t) => write!(f, "begintry {}", usize::from(t)),
Self::EndTry => write!(f, "endtry"),
Self::Call(n) => write!(f, "call {n}"),
Self::Return => write!(f, "return"),
}
}
}
#[derive(Clone, Debug)]
pub struct Catch {
pub addr: usize,
pub types: Option<Vec<Symbol>>,
}
#[derive(Clone, Debug)]
pub struct TryTable {
pub catches: Vec<Catch>,
pub local_count: usize,
}
#[derive(Clone, Debug, Default)]
pub struct Chunk {
pub consts: Vec<Value>,
pub instrs: Vec<Instruction>,
pub try_tables: Vec<TryTable>,
}
impl Chunk {
pub fn new() -> Self {
Self::default()
}
pub fn add_const(&mut self, v: Value) -> usize {
assert!(self.consts.len() < 0xff_ffff, "too many constants in a chunk");
self.consts.push(v);
self.consts.len() - 1
}
pub fn add_instr(&mut self, i: Instruction) -> usize {
assert!(self.instrs.len() < 0xff_ffff, "too many instructions in a chunk");
self.instrs.push(i);
self.instrs.len() - 1
}
pub fn begin_try_table(&mut self, local_count: usize) -> (usize, TryTable) {
assert!(self.try_tables.len() < 0xff_ffff, "too many catch tables in a chunk");
let table = TryTable {
catches: Vec::new(),
local_count,
};
self.try_tables.push(table.clone());
(self.try_tables.len() - 1, table)
}
pub fn finish_catch_table(&mut self, idx: usize, table: TryTable) {
self.try_tables[idx] = table;
}
}