interpreter: refactor IR to use native enums

That was huge lol.
This commit is contained in:
Guillem L. Jara
2026-05-26 20:39:28 +02:00
parent c5ae5cd710
commit 2ee3c722f5
3 changed files with 262 additions and 402 deletions
+110 -268
View File
@@ -32,71 +32,51 @@ pub struct Label(pub u16);
#[repr(transparent)]
pub struct ArgCount(u16);
#[repr(u8, align(1))]
#[repr(u8, C, align(8))]
#[derive(Clone, Copy, Debug)]
pub enum OpCode {
pub enum Instruction {
// Unary operations
Record,
Negation,
ToInt,
Negative,
Record(UnaryArg),
Negation(UnaryArg),
ToInt(UnaryArg),
Negative(UnaryArg),
Copy(UnaryArg),
// Binary operations
Eq,
NEq,
Gt,
Lt,
LtE,
GtE,
And,
Or,
Matches,
MatchesNot,
Add,
Subtract,
Multiply,
Divide,
Raise,
Modulo,
Concat,
Eq(BinaryArg),
NEq(BinaryArg),
Gt(BinaryArg),
Lt(BinaryArg),
LtE(BinaryArg),
GtE(BinaryArg),
And(BinaryArg),
Or(BinaryArg),
Matches(BinaryArg),
MatchesNot(BinaryArg),
Add(BinaryArg),
Subtract(BinaryArg),
Multiply(BinaryArg),
Divide(BinaryArg),
Raise(BinaryArg),
Modulo(BinaryArg),
Concat(BinaryArg),
// Intrinsic operations
LoadUser,
LoadBultin,
LoadConst,
Copy,
StoreUser,
StoreBuiltin,
IntrinsicCall,
UserCall,
IndirectCall,
Jump,
Return,
Branch,
LoadUser(LoadStoreArg),
LoadBultin(LoadStoreArg),
LoadConst(LoadStoreArg),
StoreUser(LoadStoreArg),
StoreBuiltin(LoadStoreArg),
IntrinsicCall(CallArgs),
UserCall(IndCallArgs),
IndirectCall(CallArgs),
Jump(JumpArg),
Return(RetArg),
Branch(BranchArg),
}
const _: () = const { assert!(size_of::<Instruction>() <= 8) };
#[derive(Clone, Copy)]
#[repr(C, align(8))]
pub struct Instruction {
pub opcode: OpCode,
pub args: Arguments,
}
#[derive(Clone, Copy)]
#[repr(C, align(2))]
pub union Arguments {
unary_local: UnaryArg,
binary_local: BinaryArg,
load_store: LoadStoreArg,
jump: JumpArg,
ret: RetArg,
branch: BranchArg,
call: CallArgs,
ind_call: IndCallArgs,
}
pub type UnaryArg = (Reg, Reg);
pub type BinaryArg = (Reg, Reg, Reg);
pub type LoadStoreArg = (Reg, NonLocal);
@@ -107,202 +87,65 @@ pub type CallArgs = (Reg, NonLocal, ArgCount);
pub type IndCallArgs = (Reg, Reg, ArgCount);
impl Instruction {
fn unary(opcode: impl Into<OpCode>, dest: Reg, src: Reg) -> Self {
let opcode = opcode.into();
debug_assert!(opcode.is_unary());
Self {
opcode,
args: Arguments { unary_local: (dest, src) },
fn set_label(&mut self, label: Label) {
match self {
Self::Jump(lx) | Self::Branch((_, _, lx)) => *lx = label,
_ => debug_assert!(false, "Incorrect label set!"),
}
}
fn binary(opcode: impl Into<OpCode>, dest: Reg, lhs: Reg, rhs: Reg) -> Self {
let opcode = opcode.into();
debug_assert!(opcode.is_binary());
Self {
opcode,
args: Arguments { binary_local: (dest, lhs, rhs) },
}
}
fn load_store(opcode: impl Into<OpCode>, dest: Reg, src: NonLocal) -> Self {
let opcode = opcode.into();
debug_assert!(opcode.is_load_store());
Self {
opcode,
args: Arguments { load_store: (dest, src) },
}
}
fn jump(to: Label) -> Self {
Self { opcode: OpCode::Jump, args: Arguments { jump: to } }
}
fn branch(cond: Reg, true_to: Label, false_to: Label) -> Self {
Self {
opcode: OpCode::Branch,
args: Arguments { branch: (cond, true_to, false_to) },
}
}
pub fn get_unary(&self) -> Option<&UnaryArg> {
self.opcode
.is_unary()
.then_some(unsafe { &self.args.unary_local })
}
pub fn get_binary(&self) -> Option<&BinaryArg> {
self.opcode
.is_binary()
.then_some(unsafe { &self.args.binary_local })
}
pub fn get_load_store(&self) -> Option<&LoadStoreArg> {
self.opcode
.is_load_store()
.then_some(unsafe { &self.args.load_store })
}
pub fn get_branch(&self) -> Option<&BranchArg> {
self.opcode
.is_branch()
.then_some(unsafe { &self.args.branch })
}
pub fn get_jump(&self) -> Option<&JumpArg> {
self.opcode.is_jump().then_some(unsafe { &self.args.jump })
}
}
impl OpCode {
fn is_unary(self) -> bool {
matches!(
self,
Self::Record | Self::Negation | Self::ToInt | Self::Negative
)
}
fn is_binary(self) -> bool {
matches!(
self,
Self::Eq
| Self::NEq
| Self::Gt
| Self::Lt
| Self::LtE
| Self::GtE
| Self::And
| Self::Or
| Self::Matches
| Self::MatchesNot
| Self::Add
| Self::Subtract
| Self::Multiply
| Self::Divide
| Self::Raise
| Self::Modulo
| Self::Concat
)
}
fn is_load_store(self) -> bool {
matches!(
self,
Self::LoadUser
| Self::LoadBultin
| Self::LoadConst
| Self::StoreUser
| Self::StoreBuiltin
)
}
fn is_jump(self) -> bool {
matches!(self, Self::Jump)
}
fn is_branch(self) -> bool {
matches!(self, Self::Branch)
}
}
impl Debug for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Instruction::{:?}", self.opcode)?;
match self.opcode {
op if op.is_unary() => {
let (dest, data) = unsafe { &self.args.unary_local };
write!(f, "({dest:?}, {data:?})")
}
op if op.is_binary() => {
let (dest, lhs, rhs) = unsafe { &self.args.binary_local };
write!(f, "({dest:?}, {lhs:?}, {rhs:?})")
}
op if op.is_load_store() => {
let (dest, src) = unsafe { &self.args.load_store };
write!(f, "({dest:?}, {src:?})")
}
OpCode::Branch => {
let (cond, label_then, label_else) = unsafe { self.args.branch };
write!(f, "({cond:?}, {label_then:?}, {label_else:?})")
}
OpCode::Jump => {
let label = unsafe { self.args.jump };
write!(f, "({label:?})")
}
_ => todo!(),
fn push_start_label(&mut self) {
if let Self::Branch((_, label, _)) = self {
label.0 += 1;
} else {
debug_assert!(false, "Incorrect label set!");
}
}
}
impl Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.opcode {
op @ (OpCode::Record
| OpCode::Negation
| OpCode::ToInt
| OpCode::Negative
| OpCode::Copy) => {
let (dest, data) = unsafe { &self.args.unary_local };
let op = self.display_name();
match self {
Self::Record((dest, data))
| Self::Negation((dest, data))
| Self::ToInt((dest, data))
| Self::Negative((dest, data))
| Self::Copy((dest, data)) => {
write!(f, "{dest} <- {op} {data}")
}
op @ (OpCode::Eq
| OpCode::NEq
| OpCode::Gt
| OpCode::Lt
| OpCode::LtE
| OpCode::GtE
| OpCode::And
| OpCode::Or
| OpCode::Matches
| OpCode::MatchesNot
| OpCode::Add
| OpCode::Subtract
| OpCode::Multiply
| OpCode::Divide
| OpCode::Raise
| OpCode::Concat
| OpCode::Modulo) => {
let (dest, lhs, rhs) = unsafe { &self.args.binary_local };
Self::Eq((dest, lhs, rhs))
| Self::NEq((dest, lhs, rhs))
| Self::Gt((dest, lhs, rhs))
| Self::Lt((dest, lhs, rhs))
| Self::LtE((dest, lhs, rhs))
| Self::GtE((dest, lhs, rhs))
| Self::And((dest, lhs, rhs))
| Self::Or((dest, lhs, rhs))
| Self::Matches((dest, lhs, rhs))
| Self::MatchesNot((dest, lhs, rhs))
| Self::Add((dest, lhs, rhs))
| Self::Subtract((dest, lhs, rhs))
| Self::Multiply((dest, lhs, rhs))
| Self::Divide((dest, lhs, rhs))
| Self::Raise((dest, lhs, rhs))
| Self::Concat((dest, lhs, rhs))
| Self::Modulo((dest, lhs, rhs)) => {
write!(f, "{dest} <- {op} {lhs}, {rhs}")
}
op @ (OpCode::LoadUser | OpCode::StoreUser) => {
let (dest, src) = unsafe { &self.args.load_store };
Self::LoadUser((dest, src)) | Self::StoreUser((dest, src)) => {
write!(f, "{dest} <- {op} user[{src}]")
}
op @ OpCode::LoadConst => {
let (dest, src) = unsafe { &self.args.load_store };
Self::LoadConst((dest, src)) => {
write!(f, "{dest} <- {op} mem[{src}]")
}
op @ (OpCode::StoreBuiltin | OpCode::LoadBultin) => {
let (dest, src) = unsafe { &self.args.load_store };
Self::StoreBuiltin((dest, src)) | Self::LoadBultin((dest, src)) => {
write!(f, "{dest} <- {op} intrinsic[{src}]")
}
op @ OpCode::Branch => {
let (cond, label_then, label_else) = unsafe { self.args.branch };
Self::Branch((cond, label_then, label_else)) => {
write!(f, "{op} {cond}, {label_then}, {label_else}")
}
op @ OpCode::Jump => {
let label = unsafe { self.args.jump };
Self::Jump(label) => {
write!(f, "{op} {label}")
}
_ => todo!(),
@@ -310,44 +153,43 @@ impl Display for Instruction {
}
}
impl Display for OpCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
Self::Record => "rec",
Self::Negation => "not",
Self::ToInt => "int",
Self::Negative => "neg",
Self::Concat => "cat",
Self::Eq => "eq",
Self::NEq => "neq",
Self::Gt => "gt",
Self::Lt => "lt",
Self::LtE => "le",
Self::GtE => "ge",
Self::And => "and",
Self::Or => "or",
Self::Matches => "mtch",
Self::MatchesNot => "nmtch",
Self::Add => "add",
Self::Subtract => "sub",
Self::Multiply => "mul",
Self::Divide => "div",
Self::Raise => "pow",
Self::Modulo => "mod",
Self::LoadUser => "vload",
Self::LoadBultin => "iload",
Self::LoadConst => "cload",
Self::StoreUser => "vstore",
Self::StoreBuiltin => "istore",
Self::Copy => "cpy",
Self::IntrinsicCall => "icall",
Self::UserCall => "ucall",
Self::IndirectCall => "vcall",
Self::Jump => "jmp",
Self::Return => "ret",
Self::Branch => "brif",
};
<_ as Display>::fmt(str, f)
impl Instruction {
fn display_name(self) -> &'static str {
match self {
Self::Record(_) => "rec",
Self::Negation(_) => "not",
Self::ToInt(_) => "int",
Self::Negative(_) => "neg",
Self::Concat(_) => "cat",
Self::Eq(_) => "eq",
Self::NEq(_) => "neq",
Self::Gt(_) => "gt",
Self::Lt(_) => "lt",
Self::LtE(_) => "le",
Self::GtE(_) => "ge",
Self::And(_) => "and",
Self::Or(_) => "or",
Self::Matches(_) => "mtch",
Self::MatchesNot(_) => "nmtch",
Self::Add(_) => "add",
Self::Subtract(_) => "sub",
Self::Multiply(_) => "mul",
Self::Divide(_) => "div",
Self::Raise(_) => "pow",
Self::Modulo(_) => "mod",
Self::LoadUser(_) => "vload",
Self::LoadBultin(_) => "iload",
Self::LoadConst(_) => "cload",
Self::StoreUser(_) => "vstore",
Self::StoreBuiltin(_) => "istore",
Self::Copy(_) => "cpy",
Self::IntrinsicCall(_) => "icall",
Self::UserCall(_) => "ucall",
Self::IndirectCall(_) => "vcall",
Self::Jump(_) => "jmp",
Self::Return(_) => "ret",
Self::Branch(_) => "brif",
}
}
}
+66 -65
View File
@@ -12,7 +12,7 @@ use parser::{
};
use crate::{
ir::{Instruction, Label, NonLocal, OpCode, Reg},
ir::{BinaryArg, Instruction, Label, NonLocal, Reg, UnaryArg},
types::Value,
vm::{Consts, ExecMode, Interpreter, SymbolTable},
};
@@ -45,43 +45,46 @@ impl<'a> Code<'a> {
let mut state = RegsState::new(self);
let condition = self.lower_expr(condition);
let label_then = self.following_instr(1);
let if_label = self
.bc
.emit(Instruction::branch(*condition, label_then, Label(0)));
let if_label =
self.bc
.emit(Instruction::Branch((*condition, label_then, Label(0))));
self.free_reg(condition);
self.lower_body(then_body);
self.bc.nth(if_label).args.branch.2 = self.following_instr(0);
let next = self.following_instr(0);
self.bc.nth(if_label).set_label(next);
if let Some(else_body) = else_body {
state.reg_pointer += 1;
unsafe { self.bc.nth(if_label).args.branch.2.0 += 1 };
let end_label = self.bc.emit(Instruction::jump(Label(0)));
self.bc.nth(if_label).push_start_label();
let end_label = self.bc.emit(Instruction::Jump(Label(0)));
state.scope_hwm(self, |c| c.lower_body(else_body));
self.bc.nth(end_label).args.jump = self.following_instr(0);
let next = self.following_instr(0);
self.bc.nth(end_label).set_label(next);
}
}
Statement::While { condition, then_body } => {
let cond_label = self.following_instr(0);
let condition = self.lower_expr(condition);
let while_label = self.bc.emit(Instruction::branch(
let while_label = self.bc.emit(Instruction::Branch((
*condition,
self.following_instr(1),
Label(0),
));
)));
self.free_reg(condition);
self.lower_body(then_body);
self.bc.emit(Instruction::jump(cond_label));
self.bc.nth(while_label).args.branch.2 = self.following_instr(0);
self.bc.emit(Instruction::Jump(cond_label));
let next = self.following_instr(0);
self.bc.nth(while_label).set_label(next);
}
Statement::DoWhile { then_body, condition } => {
let do_label = self.following_instr(0);
self.lower_body(then_body);
let condition = self.lower_expr(condition);
self.bc.emit(Instruction::branch(
self.bc.emit(Instruction::Branch((
*condition,
do_label,
self.following_instr(1),
));
)));
self.free_reg(condition);
}
Statement::For { init, condition, update, body } => {
@@ -95,22 +98,23 @@ impl<'a> Code<'a> {
let body_label = self.following_instr(1);
let while_label =
self.bc
.emit(Instruction::branch(*condition, body_label, Label(0)));
.emit(Instruction::Branch((*condition, body_label, Label(0))));
self.free_reg(condition);
self.lower_body(body);
if let Some(SimpleStatement::Expression(expr)) = update {
let reg = self.lower_expr(expr);
self.free_reg(reg);
}
self.bc.emit(Instruction::jump(cond_label));
self.bc.nth(while_label).args.branch.2 = self.following_instr(0);
self.bc.emit(Instruction::Jump(cond_label));
let next = self.following_instr(0);
self.bc.nth(while_label).set_label(next);
} else {
self.lower_body(body);
if let Some(SimpleStatement::Expression(expr)) = update {
let reg = self.lower_expr(expr);
self.free_reg(reg);
}
self.bc.emit(Instruction::jump(cond_label));
self.bc.emit(Instruction::Jump(cond_label));
}
}
Statement::Simple(SimpleStatement::Expression(expr)) => {
@@ -132,13 +136,11 @@ impl<'a> Code<'a> {
Expr::Leaf(atom) => match atom {
Atom::Variable(Variable::User(ident)) => {
let src = self.symbols.register_user_var(ident, self.arena);
self.bc
.emit(Instruction::load_store(OpCode::LoadUser, dest, src));
self.bc.emit(Instruction::LoadUser((dest, src)));
}
&Atom::Number(n) => {
let src = self.register_const(Value::Float(n));
self.bc
.emit(Instruction::load_store(OpCode::LoadConst, dest, src));
self.bc.emit(Instruction::LoadConst((dest, src)));
}
atom @ (Atom::String(s) | Atom::TypedRegex(s)) => {
let val = if matches!(atom, Atom::String(_)) {
@@ -149,18 +151,15 @@ impl<'a> Code<'a> {
let src = self.register_const(val(Cow::Borrowed(
self.arena.alloc_slice_copy(s.as_ref()),
)));
self.bc
.emit(Instruction::load_store(OpCode::LoadConst, dest, src));
self.bc.emit(Instruction::LoadConst((dest, src)));
}
Atom::Regex(r) => {
let src = self.register_const(Value::Regex(Cow::Borrowed(
self.arena.alloc_slice_copy(r.as_ref()),
)));
self.bc
.emit(Instruction::load_store(OpCode::LoadConst, dest, src));
self.bc.emit(Instruction::LoadConst((dest, src)));
let rec = self.lower_expr(&Expr::Leaf(Atom::Number(0.)));
self.bc
.emit(Instruction::binary(OpCode::Matches, dest, *rec, dest));
self.bc.emit(Instruction::Matches((dest, *rec, dest)));
self.free_reg(rec);
}
_ => todo!(),
@@ -168,13 +167,13 @@ impl<'a> Code<'a> {
Expr::Node(node) => match node.as_ref() {
ExprNode::UnaryOperation(op, expr) => {
let src = self.lower_expr(expr);
self.bc.emit(Instruction::unary(*op, dest, *src));
self.bc.emit(Instruction::from((*op, (dest, *src))));
self.free_reg(src);
}
ExprNode::BinaryOperation(op, lhs, rhs) => {
let lhs = self.lower_expr(lhs);
let rhs = self.lower_expr(rhs);
self.bc.emit(Instruction::binary(*op, dest, *lhs, *rhs));
self.bc.emit(Instruction::from((*op, (dest, *lhs, *rhs))));
self.free_reg(lhs);
self.free_reg(rhs);
}
@@ -182,23 +181,26 @@ impl<'a> Code<'a> {
self.lower_expr_into(cond, dest);
let mut state = RegsState::new(self);
let branch =
self.bc
.emit(Instruction::branch(dest, self.following_instr(1), Label(0)));
let branch = self.bc.emit(Instruction::Branch((
dest,
self.following_instr(1),
Label(0),
)));
state = state.scope(self, |c| {
c.lower_expr_into(true_then, dest);
});
let jump = self.bc.emit(Instruction::jump(Label(0)));
let jump = self.bc.emit(Instruction::Jump(Label(0)));
let label = self.following_instr(0);
state.scope_hwm(self, |c| {
c.lower_expr_into(false_then, dest);
});
self.bc.nth(branch).args.branch.2 = label;
self.bc.nth(jump).args.jump = self.following_instr(0);
self.bc.nth(branch).set_label(label);
let next = self.following_instr(0);
self.bc.nth(jump).set_label(next);
}
ExprNode::BinaryPlaceOperation(BinaryPlaceOperator::Assignment, place, expr) => {
self.lower_expr_into(expr, dest);
@@ -206,8 +208,7 @@ impl<'a> Code<'a> {
todo!()
};
let var = self.symbols.register_user_var(var, self.arena);
self.bc
.emit(Instruction::load_store(OpCode::StoreUser, dest, var));
self.bc.emit(Instruction::StoreUser((dest, var)));
}
_ => todo!(),
},
@@ -303,37 +304,37 @@ pub fn test_interpreter(stmnt: &Body<'_>) -> String {
vm.to_string()
}
impl From<UnaryOperator> for OpCode {
fn from(value: UnaryOperator) -> Self {
match value {
UnaryOperator::Record => Self::Record,
UnaryOperator::Negation => Self::Negation,
UnaryOperator::ToInt => Self::ToInt,
UnaryOperator::Negative => Self::Negative,
impl From<(UnaryOperator, UnaryArg)> for Instruction {
fn from((op, args): (UnaryOperator, UnaryArg)) -> Self {
match op {
UnaryOperator::Record => Self::Record(args),
UnaryOperator::Negation => Self::Negation(args),
UnaryOperator::ToInt => Self::ToInt(args),
UnaryOperator::Negative => Self::Negative(args),
}
}
}
impl From<BinaryOperator> for OpCode {
fn from(value: BinaryOperator) -> Self {
match value {
BinaryOperator::Concat => Self::Concat,
BinaryOperator::Eq => Self::Eq,
BinaryOperator::NEq => Self::NEq,
BinaryOperator::Gt => Self::Gt,
BinaryOperator::Lt => Self::Lt,
BinaryOperator::LtE => Self::LtE,
BinaryOperator::GtE => Self::GtE,
BinaryOperator::And => Self::And,
BinaryOperator::Or => Self::Or,
BinaryOperator::Matches => Self::Matches,
BinaryOperator::MatchesNot => Self::MatchesNot,
BinaryOperator::Add => Self::Add,
BinaryOperator::Subtract => Self::Subtract,
BinaryOperator::Multiply => Self::Multiply,
BinaryOperator::Divide => Self::Divide,
BinaryOperator::Raise => Self::Raise,
BinaryOperator::Modulo => Self::Modulo,
impl From<(BinaryOperator, BinaryArg)> for Instruction {
fn from((op, args): (BinaryOperator, BinaryArg)) -> Self {
match op {
BinaryOperator::Concat => Self::Concat(args),
BinaryOperator::Eq => Self::Eq(args),
BinaryOperator::NEq => Self::NEq(args),
BinaryOperator::Gt => Self::Gt(args),
BinaryOperator::Lt => Self::Lt(args),
BinaryOperator::LtE => Self::LtE(args),
BinaryOperator::GtE => Self::GtE(args),
BinaryOperator::And => Self::And(args),
BinaryOperator::Or => Self::Or(args),
BinaryOperator::Matches => Self::Matches(args),
BinaryOperator::MatchesNot => Self::MatchesNot(args),
BinaryOperator::Add => Self::Add(args),
BinaryOperator::Subtract => Self::Subtract(args),
BinaryOperator::Multiply => Self::Multiply(args),
BinaryOperator::Divide => Self::Divide(args),
BinaryOperator::Raise => Self::Raise(args),
BinaryOperator::Modulo => Self::Modulo(args),
}
}
}
+86 -69
View File
@@ -17,7 +17,7 @@ use parser::Identifier;
use crate::{
ir::{
Label, NonLocal, OpCode, Reg,
Instruction, Label, NonLocal, Reg,
lower::{Bytecode, Code},
},
types::Value,
@@ -107,81 +107,98 @@ impl<'a> Consts<'a> {
impl Interpreter<'_> {
pub fn run(&mut self) {
while let Some(instr) = self.bc.code.get(self.program_counter) {
macro_rules! rx {
(self, $dest:expr, $src:ident, $e:expr) => {{
let $src = self.registers.get($src);
self.registers.write($dest, $e);
}};
(self, $lhs:ident, $rhs:ident) => {
let ($lhs, $rhs) = (self.registers.get($lhs), self.registers.get($rhs));
};
(self, $dest:expr, $lhs:ident, $rhs:ident, $e:expr) => {{
rx!(self, $lhs, $rhs);
self.registers.write($dest, $e);
}};
}
while let Some(&instr) = self.bc.code.get(self.program_counter) {
match instr {
ix if let Some(&(dest, src)) = ix.get_unary() => {
let src = self.registers.get(src);
let val = match ix.opcode {
OpCode::Record => todo!(),
OpCode::Negation => Value::b2f(!src.to_bool()),
OpCode::ToInt => Value::Float(src.to_num()),
OpCode::Negative => Value::Float(-src.to_num()),
_ => unreachable!(),
};
Instruction::Record(_) => todo!(),
Instruction::Negation((dest, src)) => {
rx!(self, dest, src, Value::b2f(src.to_bool()));
}
Instruction::ToInt((dest, src)) => {
rx!(self, dest, src, Value::Float(src.to_num()));
}
Instruction::Negative((dest, src)) => {
rx!(self, dest, src, Value::Float(src.to_num()));
}
Instruction::Copy((dest, src)) => rx!(self, dest, src, src.clone()),
Instruction::Eq((dest, lhs, rhs)) => {
rx!(self, dest, lhs, rhs, Value::b2f(lhs == rhs));
}
Instruction::NEq((dest, lhs, rhs)) => {
rx!(self, dest, lhs, rhs, Value::b2f(lhs != rhs));
}
Instruction::Gt((dest, lhs, rhs)) => {
rx!(self, dest, lhs, rhs, Value::b2f(lhs > rhs));
}
Instruction::Lt((dest, lhs, rhs)) => {
rx!(self, dest, lhs, rhs, Value::b2f(lhs < rhs));
}
Instruction::LtE((dest, lhs, rhs)) => {
rx!(self, dest, lhs, rhs, Value::b2f(lhs <= rhs));
}
Instruction::GtE((dest, lhs, rhs)) => {
rx!(self, dest, lhs, rhs, Value::b2f(lhs >= rhs));
}
Instruction::And((_dest, _lhs, _rhs)) => todo!(),
Instruction::Or((_dest, _lhs, _rhs)) => todo!(),
Instruction::Matches((_dest, _lhs, _rhs)) => todo!(),
Instruction::MatchesNot((_dest, _lhs, _rhs)) => todo!(),
Instruction::Add((dest, lhs, rhs)) => rx!(self, dest, lhs, rhs, lhs + rhs),
Instruction::Subtract((dest, lhs, rhs)) => rx!(self, dest, lhs, rhs, lhs - rhs),
Instruction::Multiply((dest, lhs, rhs)) => rx!(self, dest, lhs, rhs, lhs * rhs),
Instruction::Divide((dest, lhs, rhs)) => rx!(self, dest, lhs, rhs, lhs / rhs),
Instruction::Raise((dest, lhs, rhs)) => rx!(self, dest, lhs, rhs, lhs ^ rhs),
Instruction::Modulo((dest, lhs, rhs)) => rx!(self, dest, lhs, rhs, lhs % rhs),
Instruction::Concat((dest, lhs, rhs)) => {
rx!(self, lhs, rhs);
let mut buf =
StdVec::with_capacity(lhs.string_size_hint() + rhs.string_size_hint());
lhs.write_string(&mut buf);
rhs.write_string(&mut buf);
self.registers.write(dest, Value::String(buf.into()));
}
Instruction::LoadUser((dest, src)) => {
let val = self.symbols.lookup_user_scalar(src);
self.registers.write(dest, val.clone());
}
Instruction::LoadBultin((_dest, _src)) => todo!(),
Instruction::LoadConst((dest, src)) => {
let val = self.consts.0.get_index(src.0 as _).unwrap().clone();
self.registers.write(dest, val);
}
ix if let Some(&(dest, lhs, rhs)) = ix.get_binary() => {
let lhs = self.registers.get(lhs);
let rhs = self.registers.get(rhs);
let val = match ix.opcode {
OpCode::Add => lhs + rhs,
OpCode::Subtract => lhs - rhs,
OpCode::Multiply => lhs * rhs,
OpCode::Divide => lhs / rhs,
OpCode::Raise => lhs ^ rhs,
OpCode::Modulo => lhs % rhs,
// Float values on boolean cmps are intentional.
OpCode::Eq => Value::b2f(lhs == rhs),
OpCode::NEq => Value::b2f(lhs != rhs),
OpCode::Gt => Value::b2f(lhs > rhs),
OpCode::Lt => Value::b2f(lhs < rhs),
OpCode::GtE => Value::b2f(lhs >= rhs),
OpCode::LtE => Value::b2f(lhs <= rhs),
OpCode::And => todo!(),
OpCode::Or => todo!(),
OpCode::Matches => todo!(),
OpCode::MatchesNot => todo!(),
OpCode::Concat => {
let mut buf = StdVec::with_capacity(
lhs.string_size_hint() + rhs.string_size_hint(),
);
lhs.write_string(&mut buf);
rhs.write_string(&mut buf);
Value::String(buf.into())
}
_ => unreachable!(),
};
self.registers.write(dest, val);
Instruction::StoreUser((dest, src)) => {
let val = self.registers.get(dest).clone();
self.symbols.write_user_val(src, val);
}
ix if let Some(&(dest, src)) = ix.get_load_store() => match ix.opcode {
OpCode::LoadConst => {
let val = self.consts.0.get_index(src.0 as _).unwrap().clone();
self.registers.write(dest, val);
}
OpCode::LoadUser => {
let val = self.symbols.lookup_user_scalar(src).clone();
self.registers.write(dest, val);
}
OpCode::StoreUser => {
let val = self.registers.get(dest).clone();
self.symbols.write_user_val(src, val);
}
_ => todo!(),
},
ix if let Some((cond, Label(true_to), Label(false_to))) = ix.get_branch() => {
let label = if self.registers.get(*cond).to_bool() {
*true_to
Instruction::StoreBuiltin((_dest, _src)) => todo!(),
Instruction::IntrinsicCall((_dest, _code, _args)) => todo!(),
Instruction::UserCall((_dest, _code, _args)) => todo!(),
Instruction::IndirectCall((_dest, _code, _args)) => todo!(),
Instruction::Jump(Label(label)) => {
self.program_counter = label as _;
continue;
}
Instruction::Return(_src) => todo!(),
Instruction::Branch((src, Label(true_to), Label(false_to))) => {
if self.registers.get(src).to_bool() {
self.program_counter = true_to as _;
} else {
*false_to
};
self.program_counter = label as _;
self.program_counter = false_to as _;
}
continue;
}
ix if let Some(&Label(label)) = ix.get_jump() => {
self.program_counter = label as _;
continue;
}
ix => todo!("{ix:?}"),
}
self.program_counter += 1;
}