223 lines
5.6 KiB
Rust
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;
|
|
}
|
|
}
|
|
|
|
|