バイトコードに変換するのはあくまでOpCode, Argであって、OpCodeと引数を元に処理するプログラム自体はバイトコードには変換しない。Instruction自体は別に持っておく。
use std::io::{BufReader, BufWriter, Read, Write};
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum OpCode {
LoadLiteral,
Add,
}
impl From<u8> for OpCode {
#[allow(non_upper_case_globals)]
fn from(o: u8) -> Self {
const LoadLiteral: u8 = OpCode::LoadLiteral as u8;
const Add: u8 = OpCode::Add as u8;
match o {
LoadLiteral => OpCode::LoadLiteral,
Add => OpCode::Add,
_ => panic!("Opcode \"{:02X}\" unrecognized!", o),
}
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct Instruction {
op: OpCode,
arg0: u8,
}
impl Instruction {
fn new(op: OpCode, arg0: u8) -> Self {
Self { op, arg0 }
}
fn serialize(
&self,
writer: &mut impl Write,
) -> Result<(), std::io::Error> {
writer.write_all(&[self.op as u8, self.arg0])?;
Ok(())
}
}
fn write_program(file: &str) {
let instructions = [
Instruction::new(OpCode::LoadLiteral, 42),
Instruction::new(OpCode::LoadLiteral, 36),
Instruction::new(OpCode::Add, 0),
];
if let Ok(writer) = std::fs::File::create(file) {
let mut writer = BufWriter::new(writer);
for instruction in &instructions {
instruction.serialize(&mut writer).unwrap();
}
println!("Written {} instructions", instructions.len());
}
}
fn main() {
write_program("bytecode.bin");
}
fn interpret(instructions: &[Instruction]) -> Option<i64> {
let mut stack = vec![];
for instruction in instructions {
match instruction.op {
OpCode::LoadLiteral => {
stack.push(instruction.arg0 as i64);
}
OpCode::Add => {
let rhs = stack.pop().expect("Stack underflow");
let lhs = stack.pop().expect("Stack underflow");
stack.push(lhs + rhs);
}
}
}
stack.pop()
}
なるほど、データの流れは理解できました。
### デシリアライズ
use std::io::{BufReader, BufWriter, Read, Write};
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum OpCode {
LoadLiteral,
Add,
}
impl From<u8> for OpCode {
#[allow(non_upper_case_globals)]
fn from(o: u8) -> Self {
const LoadLiteral: u8 = OpCode::LoadLiteral as u8;
const Add: u8 = OpCode::Add as u8;
match o {
LoadLiteral => OpCode::LoadLiteral,
Add => OpCode::Add,
_ => panic!("Opcode \"{:02X}\" unrecognized!", o),
}
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct Instruction {
op: OpCode,
arg0: u8,
}
impl Instruction {
fn new(op: OpCode, arg0: u8) -> Self {
Self { op, arg0 }
}
fn serialize(
&self,
writer: &mut impl Write,
) -> Result<(), std::io::Error> {
writer.write_all(&[self.op as u8, self.arg0])?;
Ok(())
}
fn deserialize(
reader: &mut impl Read,
) -> Result<Self, std::io::Error> {
let mut buf = [0u8; 2];
reader.read_exact(&mut buf)?;
Ok(Self::new(buf[0].into(), buf[1]))
}
}
fn write_program(file: &str) {
let instructions = [
Instruction::new(OpCode::LoadLiteral, 42),
Instruction::new(OpCode::LoadLiteral, 36),
Instruction::new(OpCode::Add, 0),
];
if let Ok(writer) = std::fs::File::create(file) {
let mut writer = BufWriter::new(writer);
for instruction in &instructions {
instruction.serialize(&mut writer).unwrap();
}
println!("Written {} instructions", instructions.len());
}
}
fn read_program(file: &str) -> Option<Vec<Instruction>> {
if let Ok(reader) = std::fs::File::open(file) {
let mut reader = BufReader::new(reader);
let mut instructions = vec![];
while let Ok(inst) = Instruction::deserialize(&mut reader) {
instructions.push(inst);
}
Some(instructions)
} else {
None
}
}
fn main() {
let mut args = std::env::args();
args.next();
match args.next().as_ref().map(|s| s as &str) {
Some("w") => write_program("bytecode.bin"),
Some("r") => {
if let Some(instructions) = read_program("bytecode.bin") {
let result = interpret(&instructions);
println!("result: {result:?}");
}
}
_ => println!("Please specify w or r as an argument"),
}
}
fn interpret(instructions: &[Instruction]) -> Option<i64> {
let mut stack = vec![];
for instruction in instructions {
match instruction.op {
OpCode::LoadLiteral => {
stack.push(instruction.arg0 as i64);
}
OpCode::Add => {
let rhs = stack.pop().expect("Stack underflow");
let lhs = stack.pop().expect("Stack underflow");
stack.push(lhs + rhs);
}
}
}
stack.pop()
}
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.61s
Running `target/debug/ruscal r`
result: Some(78)
バイトコードを読み取って、スタックを実行している。