【Rust】スタックマシンで関数を実装する

変数と同じように、関数名とinstructionをhashmapに保存する

/x 2 def /double { x 2 * } fn

        "fn" => {
            let tmp = func(&mut stack, instructions.clone());
            for (key, value) in &tmp {
                funcs.insert(key.to_string(), value.to_vec());
            }
            println!("{:?}", funcs); 
        } 

// 省略

fn func(stack: &mut Vec<String>, instructions: Vec<String>) -> HashMap<String, Vec<String>> {
    let mut funcs: HashMap<String, Vec<String>> = HashMap::new();
    let name = stack.pop().unwrap();

    funcs.insert(name, instructions);
    funcs
}

{“double”: [“x”, “2”, “*”]}

関数名と式をhashmapに保存したので、後は関数名が書かれていた際に、関数の内容を呼び出すだけ


    } else if funcs.contains_key(word) {
      let _ = op_fn(&mut stack, funcs.get(word).unwrap(), vars.clone());
//

fn op_fn(stack: &mut Vec<String>, input: &Vec<String>, vars: HashMap<String, String>) {
    let mut words = input.clone();
    while let Some((&ref word, rest)) = words.split_first() {
        if word.is_empty() {
          break;
        }
        if let Ok(parsed) = word.parse::<i32>() {
          stack.push(parsed.to_string());
        } else if word.starts_with("/") {
          stack.push((word[1..]).to_string());
        } else if vars.contains_key(word) {
          stack.push(vars.get(word).unwrap().to_string());
        } else {
          match word.as_str() {
            "+" => add(stack),
            "-" => sub(stack),
            "*" => mul(stack),
            "/" => div(stack),
            "%" => r#mod(stack),
            "<" => lt(stack),
            ">" => rt(stack),
            "==" => equal(stack),
            "!=" => nequal(stack),
            "sqrt" => r#sqrt(stack),
            "if" => op_if(stack),
            _ => todo!(),
            }
        }
        words = rest.to_vec();
      }
}

/x 2 def /double { x 2 * } fn double
=> [“4”]

【Rust】スタックマシンでループ文を実装するための準備

ループで実行する処理文を記憶するために、”{ }”で囲まれている中身を取得してベクターで保存しておく。

fn main() {
    let mut stack = vec![];
    let mut instructions = vec![];

    let str = "1 2 + { 10 20 + }".to_string();
    let mut words: Vec<_> = str.split(" ").collect();

    while let Some((&word, mut rest)) = words.split_first() {
        if word.is_empty() {
            break;
        }
        if word == "{" {
            (instructions, rest) = parse_instruction(rest);
        } else if let Ok(parsed) = word.parse::<i32>() {
            stack.push(parsed.to_string());
        } else {
            match word {
              "+" => add(&mut stack),
              _ => panic!("{word:?} could not be parsed"),
            }
        }
        words = rest.to_vec();
    }
    println!("{:?}", stack);
    println!("{:?}", instructions);
}

fn parse_instruction<'src, 'a>(input: &'a [&'src str])-> (Vec<String>, &'a [&'src str]) {
    let mut tokens: Vec<String> = vec![];
    let mut words = input;

    while let Some((&word, mut rest)) = words.split_first() {
        if word.is_empty() {
            break;
        }
        if word == "}" {
            return (tokens, rest);
        } else {
            tokens.push(word.to_string())
        }
        words = rest;
    }
    (tokens, words)
}

fn add(stack: &mut Vec<String>) {
    let rhs: i32 = stack.pop().unwrap().parse::<i32>().unwrap();
    let lhs: i32 = stack.pop().unwrap().parse::<i32>().unwrap();
    stack.push((lhs + rhs).to_string());
}

Running `target/debug/sample`
[“3”]
[“10”, “20”, “+”]

【Rust】sqrtの計算

f64でsqrt()の関数があるので、f64に変換して計算する
https://doc.rust-lang.org/std/primitive.f64.html#method.sqrt

fn main() {
    let n = 10;
    let f = n as f64;
    let s = f.sqrt();
    println!("{}", s);
    println!("{}", s as i32);
}

これをスタックマシンに実装します。とりあえずi32で統一しているので、ここでは、sqrt後 as i32としています。

match word {
        "+" => add(&mut stack),
        "-" => sub(&mut stack),
        "*" => mul(&mut stack),
        "/" => div(&mut stack),
        "%" => r#mod(&mut stack),
        "<" => lt(&mut stack),
        ">" => rt(&mut stack),
        "sqrt" => r#sqrt(&mut stack),
        "if" => op_if(&mut stack),
        "def" => {  
          let tmp = op_def(&mut stack);
          for (key, value) in &tmp {
            vars.insert(key.to_string(), value.to_string());
          }
        },
        _ => panic!("{word:?} could not be parsed"),
      }
//

fn r#sqrt(stack: &mut Vec<String>) {
  let f: f64 = stack.pop().unwrap().parse::<f64>().unwrap();
  let s = f.sqrt();
  stack.push((s as i32).to_string());
}

【Rust】OpCodeのバイトコード(u8)へのシリアライズ•デシリアライズ

バイトコードに変換するのはあくまで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)

バイトコードを読み取って、スタックを実行している。

【Rust】括弧によるグループ化

fn main() {
    let input = "123 456 world";
    println!("source: {}, parsed: {:?}", input, source(input));

    let input = "(car cdr) cdr";
    println!("source: {}, parsed: {:?}", input, source(input));

    let input = "()())))((()))";
    println!("source: {}, parsed: {:?}", input, source(input));
}

fn advance_char(input: &str) -> &str {
    let mut chars = input.chars();
    chars.next();
    chars.as_str()
}

fn peek_char(input: &str) -> Option<char> {
    input.chars().next()
}

fn source(mut input: &str) -> Vec<Token> {
    let mut tokens = vec![];
    while !input.is_empty() {
        input = if let (next_input, Some(token)) = token(input) {
            tokens.push(token);
            next_input
        } else {
            break;
        }
    }
    tokens
}

#[derive(Debug, PartialEq, Eq)]
enum Token {
    Ident,
    Number,
    LParen,
    RParen,
}

fn token(i: &str) -> (&str, Option<Token>) {
    if let (i, Some(ident_res)) = ident(whitespace(i)) {
        return (i, Some(ident_res));
    }

    if let (i, Some(number_res)) = number(whitespace(i)) {
        return (i, Some(number_res));
    }

    if let (i, Some(lparen_res)) = lparen(whitespace(i)) {
        return (i, Some(lparen_res));
    }

    if let (i, Some(rparen_res)) = rparen(whitespace(i)) {
        return (i, Some(rparen_res));
    }

    (whitespace(i), None)
}

fn whitespace(mut input: &str) -> &str {
    while matches!(input.chars().next(), Some(' ')) {
        input = advance_char(input);
    }
    input
}

fn ident(mut input: &str) -> (&str, Option<Token>) {
    if matches!(
        peek_char(input),
        Some(_x @ ('a'..='z' | 'A'..='Z'))
    ) {
        input = advance_char(input);
        while matches!(
            peek_char(input),
            Some(_x @ ('a'..='z' | 'A'..='Z' | '0'..='9'))
        ) {
            input = advance_char(input);
        }
        (input, Some(Token::Ident))
    } else {
        (input, None)
    }
}

fn number(mut input: &str) -> (&str, Option<Token>) {
    if matches!(
        peek_char(input),
        Some(_x @ ('-'|'+'|'.'|'0'..='9'))
    ) {
        input = advance_char(input);
        while matches!(
            peek_char(input),
            Some(_x @ ('.'|'0'..='9'))
        ) {
            input = advance_char(input);
        }
        (input, Some(Token::Number))
    } else {
        (input, None)
    }
}

fn lparen(mut input: &str) -> (&str, Option<Token>) {
    if matches!(peek_char(input), Some('(')) {
        input = advance_char(input);
        (input, Some(Token::LParen))
    } else {
        (input, None)
    }
}

fn rparen(mut input: &str) -> (&str, Option<Token>) {
    if matches!(peek_char(input), Some(')')) {
        input = advance_char(input);
        (input, Some(Token::RParen))
    } else {
        (input, None)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_whitespace() {
        assert_eq!(whitespace("    "), "");
    }

    #[test]
    fn test_indent() {
        assert_eq!(ident("Adam"), "");
    }

    #[test]
    fn test_number() {
        assert_eq!(number("123.45 "), " ");
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/ruscal`
source: 123 456 world, parsed: [Number, Number, Ident]
source: (car cdr) cdr, parsed: [LParen, Ident, Ident, RParen, Ident]
source: ()())))((())), parsed: [LParen, RParen, LParen, RParen, RParen, RParen, RParen, LParen, LParen, LParen, RParen, RParen, RParen]

【Rust】文字列のトークナイズ

fn main() {
    let input = "123 world";
    println!("source: {}, parsed: {:?}", input, source(input));

    let input = "Hello world";
    println!("source: {}, parsed: {:?}", input, source(input));

    let input = "      world";
    println!("source: {}, parsed: {:?}", input, source(input));
}

fn source(mut input: &str) -> Vec<Token> {
    let mut tokens = vec![];
    while !input.is_empty() {
        input = if let (next_input, Some(token)) = token(input) {
            tokens.push(token);
            next_input
        } else {
            break;
        }
    }
    tokens
}

#[derive(Debug, PartialEq, Eq)]
enum Token {
    Ident,
    Number,
}

fn token(i: &str) -> (&str, Option<Token>) {
    if let (i, Some(ident_res)) = ident(whitespace(i)) {
        return (i, Some(ident_res));
    }

    if let (i, Some(number_res)) = number(whitespace(i)) {
        return (i, Some(number_res));
    }
    (i, None)
}

fn whitespace(mut input: &str) -> &str {
    while matches!(input.chars().next(), Some(' ')) {
        let mut chars = input.chars();
        chars.next();
        input = chars.as_str();
    }
    input
}

fn ident(mut input: &str) -> (&str, Option<Token>) {
    if matches!(
        input.chars().next(),
        Some(_x @ ('a'..='z' | 'A'..='Z'))
    ) {
        while matches!(
            input.chars().next(),
            Some(_x @ ('a'..='z' | 'A'..='Z' | '0'..='9'))
        ) {
            let mut chars = input.chars();
            chars.next();
            input = chars.as_str();
        }
        (input, Some(Token::Ident))
    } else {
        (input, None)
    }
}

fn number(mut input: &str) -> (&str, Option<Token>) {
    if matches!(
        input.chars().next(),
        Some(_x @ ('-'|'+'|'.'|'0'..='9'))
    ) {
        while matches!(
            input.chars().next(),
            Some(_x @ ('.'|'0'..='9'))
        ) {
            let mut chars = input.chars();
            chars.next();
            input = chars.as_str();
        }
        (input, Some(Token::Number))
    } else {
        (input, None)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_whitespace() {
        assert_eq!(whitespace("    "), "");
    }

    #[test]
    fn test_indent() {
        assert_eq!(ident("Adam"), "");
    }

    #[test]
    fn test_number() {
        assert_eq!(number("123.45 "), " ");
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.04s
Running `target/debug/ruscal`
source: 123 world, parsed: [Number, Ident]
source: Hello world, parsed: [Ident, Ident]
source: world, parsed: [Ident]

【Rust】文字列の正規表現

一文字ずつ処理したい場合
matches!の場合は、Someと比較する

fn main() {
    let mut str = "hello world!";
    while matches!(str.chars().next(), Some(_x @ ('a'..='z' | 'A'..='Z'))) {
        let mut chars = str.chars();
        chars.next();
        str = chars.as_str();
        println!("{:?}", str);
    }
}

Running `target/debug/sample`
“ello world!”
“llo world!”
“lo world!”
“o world!”
” world!”

これを応用して、文字列をパースする

fn main() {
    let source = "123 world";
    println!(
        "source: {}, parsed: {:?}",
        source,
        ident(whitespace(number(source)))
    );
}

fn whitespace(mut input: &str) -> &str {
    while matches!(input.chars().next(), Some(' ')) {
        let mut chars = input.chars();
        chars.next();
        input = chars.as_str();
    }
    input
}

fn ident(mut input: &str) -> &str {
    if matches!(
        input.chars().next(),
        Some(_x @ ('a'..='z' | 'A'..='Z'))
    ) {
        while matches!(
            input.chars().next(),
            Some(_x @ ('a'..='z' | 'A'..='Z' | '0'..='9'))
        ) {
            let mut chars = input.chars();
            chars.next();
            input = chars.as_str();
        }
    }
    input
}

fn number(mut input: &str) -> &str {
    if matches!(
        input.chars().next(),
        Some(_x @ ('-'|'+'|'.'|'0'..='9'))
    ) {
        while matches!(
            input.chars().next(),
            Some(_x @ ('.'|'0'..='9'))
        ) {
            let mut chars = input.chars();
            chars.next();
            input = chars.as_str();
        }
    }
    input
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_whitespace() {
        assert_eq!(whitespace("    "), "");
    }

    #[test]
    fn test_indent() {
        assert_eq!(ident("Adam"), "");
    }

    #[test]
    fn test_number() {
        assert_eq!(number("123.45 "), " ");
    }
}

【Rust】スタックマシンの関数

関数を値として持っておくのだが変数と同じように、/で始まり、かつ{ } の場合、{ }の中身をoperationとして評価している

use std::{
    collections::HashMap,
    io::{BufRead, BufReader},
};

#[derive(Debug, Clone, PartialEq, Eq)]
enum Value {
    Num(i32),
    Op(String),
    Sym(String),
    Block(Vec<Value>),
    Native(NativeOp),
}

impl Value {
    fn as_num(&self) -> i32 {
        match self {
        Self::Num(val) => *val,
        _ => panic!("Value is not a number"),
        }
    }

    fn to_block(self) -> Vec<Value> {
        match self {
            Self::Block(val) => val,
            _ => panic!("Value is not a block"),
        }
    }

    fn as_sym(&self) -> &str {
        if let Self::Sym(sym) = self {
            sym
        } else {
            panic!("Value is not a symbol");
        }
    }

    fn to_string(&self) -> String {
        match self {
            Self::Num(i) => i.to_string(),
            Self::Op(ref s) | Self::Sym(ref s) => s.clone(),
            Self::Block(_) => "<Block>".to_string(),
            Self::Native(_) => "<Native>".to_string(),
        }
    }
}

#[derive(Clone)]
struct NativeOp(fn(&mut Vm));

impl PartialEq for NativeOp {
    fn eq(&self, other: &NativeOp) -> bool {
        self.0 as *const fn() == other.0 as *const fn()
    }
}

impl Eq for NativeOp {}

impl std::fmt::Debug for NativeOp {
    fn fmt(
        &self,
        f: &mut std::fmt::Formatter<'_>,
    ) -> std::fmt::Result {
        write!(f, "<NativeOp>")
    }
}

struct Vm {
    stack: Vec<Value>,
    vars: HashMap<String, Value>,
    blocks: Vec<Vec<Value>>,
}

impl Vm {
  fn new() -> Self {
    let functions: [(&str, fn(&mut Vm)); 12] = [
      ("+", add),
      ("-", sub),
      ("*", mul),
      ("/", div),
      ("<", lt),
      ("if", op_if),
      ("def", op_def),
      ("puts", puts),
      ("pop", pop),
      ("dup", dup),
      ("exch", exch),
      ("index", index),
    ];
    Self {
      stack: vec![],
      vars: functions
        .into_iter()
        .map(|(name, fun)| {
          (name.to_owned(), Value::Native(NativeOp(fun)))
        })
        .collect(),
      blocks: vec![],
    }
  }
}

fn main() {
  if let Some(f) = std::env::args()
    .nth(1)
    .and_then(|f| std::fs::File::open(f).ok())
  {
    parse_batch(BufReader::new(f));
  } else {
    parse_interactive();
  }
}

fn parse_batch(source: impl BufRead) -> Vec<Value> {
  let mut vm = Vm::new();
  for line in source.lines().flatten() {
    for word in line.split(" ") {
      parse_word(word, &mut vm);
    }
  }
  vm.stack
}

fn parse_interactive() {
  let mut vm = Vm::new();
  for line in std::io::stdin().lines().flatten() {
    for word in line.split(" ") {
      parse_word(word, &mut vm);
    }
    println!("stack: {:?}", vm.stack);
  }
}

fn parse_word(word: &str, vm: &mut Vm) {
  if word.is_empty() {
    return;
  }
  if word == "{" {
    vm.blocks.push(vec![]);
  } else if word == "}" {
    let top_block =
      vm.blocks.pop().expect("Block stack underflow!");
    eval(Value::Block(top_block), vm);
  } else {
    let code = if let Ok(num) = word.parse::<i32>() {
      Value::Num(num)
    } else if word.starts_with("/") {
      Value::Sym(word[1..].to_string())
    } else {
      Value::Op(word.to_string())
    };
    eval(code, vm);
  }
}


fn eval(code: Value, vm: &mut Vm) {
  if let Some(top_block) = vm.blocks.last_mut() {
    top_block.push(code);
    return;
  }
  if let Value::Op(ref op) = code {
    let val = vm
      .vars
      .get(op)
      .expect(&format!("{op:?} is not a defined operation"))
      .clone();
    match val {
      Value::Block(block) => {
        for code in block {
          eval(code, vm);
        }
      }
      Value::Native(op) => op.0(vm),
      _ => vm.stack.push(val),
    }
  } else {
    vm.stack.push(code.clone());
  }
}

macro_rules! impl_op {
    {$name:ident, $op:tt} => {
        fn $name(vm: &mut Vm) {
            let rhs = vm.stack.pop().unwrap().as_num();
            let lhs = vm.stack.pop().unwrap().as_num();
            vm.stack.push(Value::Num((lhs $op rhs) as i32));
        }
    }
}

impl_op!(add, +);
impl_op!(sub, -);
impl_op!(mul, *);
impl_op!(div, /);
impl_op!(lt, <);

fn op_if(vm: &mut Vm) {
    let false_branch = vm.stack.pop().unwrap().to_block();
    let true_branch = vm.stack.pop().unwrap().to_block();
    let cond = vm.stack.pop().unwrap().to_block();

    for code in cond {
        eval(code, vm);
    }

    let cond_result = vm.stack.pop().unwrap().as_num();

    if cond_result != 0 {
        for code in true_branch {
            eval(code, vm);
        }
    } else {
        for code in false_branch {
            eval(code, vm);
        }
    }
}

fn op_def(vm: &mut Vm) {
    let value = vm.stack.pop().unwrap();
    eval(value, vm);
    let value = vm.stack.pop().unwrap();
    let sym = vm.stack.pop().unwrap().as_sym().to_string();

    vm.vars.insert(sym, value);
}

fn puts(vm: &mut Vm){
    let value = vm.stack.pop().unwrap();
    println!("{}", value.to_string());
}

fn pop(vm: &mut Vm) {
    vm.stack.pop().unwrap();
}

fn dup(vm: &mut Vm) {
    let value = vm.stack.last().unwrap();
    vm.stack.push(value.clone());
}

fn exch(vm: &mut Vm) {
    let last = vm.stack.pop().unwrap();
    let second = vm.stack.pop().unwrap();
    vm.stack.push(last);
    vm.stack.push(second);
}

fn index(vm: &mut Vm) {
    let index = vm.stack.pop().unwrap().as_num() as usize;
    let value = vm.stack[vm.stack.len() - index - 1].clone();
    vm.stack.push(value);
}

#[cfg(test)]
mod test {
    use super::{Value::*, *};
    use std::io::Cursor;

    fn parse(input: &str) -> Vec<Value> {
        parse_batch(Cursor::new(input))
    }

    #[test]
    fn test_group(){
        assert_eq!(
            parse("1 2 + { 3 4 }"),
            vec![Num(3), Block(vec![Num(3), Num(4)])]
        );
    }

    #[test]
    fn test_if_false(){
        assert_eq!(
            parse("{ 1 -1 + } { 100 } { -100 } if"),
            vec![Num(-100)]
        );
    }

    #[test]
    fn test_if_true() {
        assert_eq!(
            parse("{ 1 1 + } { 100 } { -100 } if"),
            vec![Num(100)]
        );
    }

    #[test]
    fn test_var() {
        assert_eq!(
            parse("/x 10 def /y 20 def x y *"),
            vec![Num(200)]
        )
    }

    #[test]
    fn test_var_if() {
        assert_eq!(
            parse("/x 10 def /y 20 def { x y < } { x } { y } if"),
            vec![Num(10)]
        );
    }

    #[test]
    fn test_multiline() {
        assert_eq!(
            parse(
                r#"
                
            /x 10 def
            /y 20 def

            { x y < }
            { x }
            { y }
            if
            "#
            ),
            vec![Num(10)]
        );
    }

    #[test]
    fn test_function() {
        assert_eq!(
            parse(
                r#"
            /double { 2 * } def
            10 double"#
            ),
            vec![Num(20)]
        );
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.48s
Running `target/debug/stackmachine`
/double { 2 * } def
stack: []
10 double puts
20
stack: []

ルールとしてわかりやすいように定義していくのね
考え方としては非常に参考になるものがあります

【Rust】スタックマシンでbufReaderを使ったソースファイルの読み込み

BufReader

use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;

fn main() -> std::io::Result<()> {
    let f = File::open("./src/address.rs")?;
    let mut reader = BufReader::new(f);

    let mut line = String::new();
    let len = reader.read_line(&mut line)?;
    println!("First line is {len} bytes long");
    Ok(())
}

ファイルを読み込んで1行ずつ処理の際は同じになる。

use std::{
    collections::HashMap,
    io::{BufRead, BufReader},
};

#[derive(Debug, Clone, PartialEq, Eq)]
enum Value {
    Num(i32),
    Op(String),
    Sym(String),
    Block(Vec<Value>),
}

impl Value {
    fn as_num(&self) -> i32 {
        match self {
        Self::Num(val) => *val,
        _ => panic!("Value is not a number"),
        }
    }

    fn to_block(self) -> Vec<Value> {
        match self {
            Self::Block(val) => val,
            _ => panic!("Value is not a block"),
        }
    }

    fn as_sym(&self) -> &str {
        if let Self::Sym(sym) = self {
            sym
        } else {
            panic!("Value is not a symbol");
        }
    }

    fn to_string(&self) -> String {
        match self {
            Self::Num(i) => i.to_string(),
            Self::Op(ref s) | Self::Sym(ref s) => s.clone(),
            Self::Block(_) => "<Block>".to_string(),
        }
    }
}

struct Vm {
    stack: Vec<Value>,
    vars: HashMap<String, Value>,
    blocks: Vec<Vec<Value>>,
}

impl Vm {
    fn new() -> Self {
        Self {
            stack: vec![],
            vars: HashMap::new(),
            blocks: vec![],
        }
    }
}

fn main() {
  if  let Some(f) = std::env::args()
    .nth(1)
    .and_then(|f| std::fs::File::open(f).ok())
    {
        parse_batch(BufReader::new(f));
    } else {
        parse_interactive();
    }
}

fn parse_batch(source: impl BufRead) -> Vec<Value> {
    let mut vm = Vm::new();
    for line in source.lines().flatten() {
        for word in line.split(" ") {
            parse_word(word, &mut vm);
        }
    }
    vm.stack
}

fn parse_interactive() {
    let mut vm = Vm::new();
    for line in std::io::stdin().lines().flatten() {
        for word in line.split(" ") {
            parse_word(word, &mut vm);
        }
        println!("stack: {:?}", vm.stack);
    }
}

fn parse_word(word: &str, vm: &mut Vm) {
    if word.is_empty() {
        return;
    }
    if word == "{" {
        vm.blocks.push(vec![]);
    } else if word == "}" {
        let top_block =
            vm.blocks.pop().expect("Block stack underrun!");
        eval(Value::Block(top_block), vm);
    } else {
        let code = if let Ok(num) = word.parse::<i32>() {
            Value::Num(num)
        } else if word.starts_with("/") {
            Value::Sym(word[1..].to_string())
        } else {
            Value::Op(word.to_string())
        };
        eval(code, vm);
    }
}


fn eval<'src>(code: Value, vm: &mut Vm) {
    if let Some(top_block) = vm.blocks.last_mut() {
        top_block.push(code);
        return;
    }
    match code {
        Value::Op(ref op) => match op as &str {
            "+" => add(&mut vm.stack),
            "-" => sub(&mut vm.stack),
            "*" => mul(&mut vm.stack),
            "/" => div(&mut vm.stack),
            "<" => lt(&mut vm.stack),
            "if" => op_if(vm),
            "def" => op_def(vm),
            "puts" => puts(vm),
            _ => {
                let val = vm.vars.get(op).expect(&format!(
                    "{op:?} is not a defined operation"
                ));
                vm.stack.push(val.clone());
            }
        },
        _ => vm.stack.push(code.clone()),
    }
}

macro_rules! impl_op {
    {$name:ident, $op:tt} => {
        fn $name(stack: &mut Vec<Value>) {
            let rhs = stack.pop().unwrap().as_num();
            let lhs = stack.pop().unwrap().as_num();
            stack.push(Value::Num((lhs $op rhs) as i32));
        }
    }
}

impl_op!(add, +);
impl_op!(sub, -);
impl_op!(mul, *);
impl_op!(div, /);
impl_op!(lt, <);

fn op_if(vm: &mut Vm) {
    let false_branch = vm.stack.pop().unwrap().to_block();
    let true_branch = vm.stack.pop().unwrap().to_block();
    let cond = vm.stack.pop().unwrap().to_block();

    for code in cond {
        eval(code, vm);
    }

    let cond_result = vm.stack.pop().unwrap().as_num();

    if cond_result != 0 {
        for code in true_branch {
            eval(code, vm);
        }
    } else {
        for code in false_branch {
            eval(code, vm);
        }
    }
}

fn op_def(vm: &mut Vm) {
    let value = vm.stack.pop().unwrap();
    eval(value, vm);
    let value = vm.stack.pop().unwrap();
    let sym = vm.stack.pop().unwrap().as_sym().to_string();

    vm.vars.insert(sym, value);
}

fn puts(vm: &mut Vm){
    let value = vm.stack.pop().unwrap();
    println!("{}", value.to_string());
}

#[cfg(test)]
mod test {
    use super::{Value::*, *};
    use std::io::Cursor;

    fn parse(input: &str) -> Vec<Value> {
        parse_batch(Cursor::new(input))
    }

    #[test]
    fn test_group(){
        assert_eq!(
            parse("1 2 + { 3 4 }"),
            vec![Num(3), Block(vec![Num(3), Num(4)])]
        );
    }

    #[test]
    fn test_if_false(){
        assert_eq!(
            parse("{ 1 -1 + } { 100 } { -100 } if"),
            vec![Num(-100)]
        );
    }

    #[test]
    fn test_if_true() {
        assert_eq!(
            parse("{ 1 1 + } { 100 } { -100 } if"),
            vec![Num(100)]
        );
    }

    #[test]
    fn test_var() {
        assert_eq!(
            parse("/x 10 def /y 20 def x y *"),
            vec![Num(200)]
        )
    }

    #[test]
    fn test_var_if() {
        assert_eq!(
            parse("/x 10 def /y 20 def { x y < } { x } { y } if"),
            vec![Num(10)]
        );
    }

    #[test]
    fn test_multiline() {
        assert_eq!(
            parse(
                r#"
                
            /x 10 def
            /y 20 def

            { x y < }
            { x }
            { y }
            if
            "#
            ),
            vec![Num(10)]
        );
    }
}

$ cargo run — if.txt
Compiling stackmachine v0.1.0 (/home/vagrant/dev/rust/stackmachine)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.49s
Running `target/debug/stackmachine if.txt`
10

【Rust】スタックマシンの変数

hashmapに変数名と値を入れて評価する。

use std::collections::HashMap;

#[derive(Debug, Clone, PartialEq, Eq)]
enum Value<'src> {
    Num(i32),
    Op(&'src str),
    Sym(&'src str),
    Block(Vec<Value<'src>>),
}

impl<'src> Value<'src> {
    fn as_num(&self) -> i32 {
        match self {
        Self::Num(val) => *val,
        _ => panic!("Value is not a number"),
        }
    }

    fn to_block(self) -> Vec<Value<'src>> {
        match self {
            Self::Block(val) => val,
            _ => panic!("Value is not a block"),
        }
    }

    fn as_sym(&self) -> &'src str {
        if let Self::Sym(sym) = self {
            *sym
        } else {
            panic!("Value is not a symbol");
        }
    }
}

struct Vm<'src> {
    stack: Vec<Value<'src>>,
    vars: HashMap<&'src str, Value<'src>>,
}

impl<'src> Vm<'src> {
    fn new() -> Self {
        Self {
            stack: vec![],
            vars: HashMap::new(),
        }
    }
}

fn main() {
  for line in std::io::stdin().lines().flatten() {
    parse(&line);
  }
}

fn parse<'a>(line: &'a str) -> Vec<Value> {
    let mut vm = Vm::new();
    let input: Vec<_> = line.split(" ").collect();
    let mut words = &input[..];

    while let Some((&word, mut rest)) = words.split_first(){
        if word.is_empty() {
            break;
        }
        if word == "{" {
            let value;
            (value, rest) = parse_block(rest);
            vm.stack.push(value);
        } else {
            let code = if let Ok(num) = word.parse::<i32>() {
                Value::Num(num)
            } else if word.starts_with("/") {
                Value::Sym(&word[1..])
            } else {
                Value::Op(word)
            };
            eval(code, &mut vm);
        }
        words = rest;
    }
    println!("stack: {:?}", vm.stack);
    vm.stack
}

fn eval<'src>(code: Value<'src>, vm: &mut Vm<'src>) {
    match code {
        Value::Op(op) => match op {
            "+" => add(&mut vm.stack),
            "-" => sub(&mut vm.stack),
            "*" => mul(&mut vm.stack),
            "/" => div(&mut vm.stack),
            "<" => lt(&mut vm.stack),
            "if" => op_if(vm),
            "def" => op_def(vm),
            _ => {
                let val = vm.vars.get(op).expect(&format!(
                    "{op:?} is not a defined operation"
                ));
                vm.stack.push(val.clone());
            }
        },
        _ => vm.stack.push(code.clone()),
    }
}

fn parse_block<'src, 'a>(
    input: &'a [&'src  str],
) -> (Value<'src>, &'a [&'src str]){
    let mut tokens = vec![];
    let mut words = input;

    while let Some((&word, mut rest)) = words.split_first(){
        if word.is_empty() {
            break;
        }
        if word == "{" {
            let value;
            (value, rest) = parse_block(rest);
            tokens.push(value);
        } else if word == "}" {
            return (Value::Block(tokens), rest);
        } else if let Ok(value) = word.parse::<i32>() {
            tokens.push(Value::Num(value));
        } else {
            tokens.push(Value::Op(word));
        }
        words = rest;
    }
    (Value::Block(tokens), words)
}

macro_rules! impl_op {
    {$name:ident, $op:tt} => {
        fn $name(stack: &mut Vec<Value>) {
            let rhs = stack.pop().unwrap().as_num();
            let lhs = stack.pop().unwrap().as_num();
            stack.push(Value::Num((lhs $op rhs) as i32));
        }
    }
}

impl_op!(add, +);
impl_op!(sub, -);
impl_op!(mul, *);
impl_op!(div, /);
impl_op!(lt, <);

fn op_if(vm: &mut Vm) {
    let false_branch = vm.stack.pop().unwrap().to_block();
    let true_branch = vm.stack.pop().unwrap().to_block();
    let cond = vm.stack.pop().unwrap().to_block();

    for code in cond {
        eval(code, vm);
    }

    let cond_result = vm.stack.pop().unwrap().as_num();

    if cond_result != 0 {
        for code in true_branch {
            eval(code, vm);
        }
    } else {
        for code in false_branch {
            eval(code, vm);
        }
    }
}

fn op_def(vm: &mut Vm) {
    let value = vm.stack.pop().unwrap();
    eval(value, vm);
    let value = vm.stack.pop().unwrap();
    let sym = vm.stack.pop().unwrap().as_sym();

    vm.vars.insert(sym, value);
}

#[cfg(test)]
mod test {
    use super::{parse, Value::*};

    #[test]
    fn test_group(){
        assert_eq!(
            parse("1 2 + { 3 4 }"),
            vec![Num(3), Block(vec![Num(3), Num(4)])]
        );
    }

    #[test]
    fn test_if_false(){
        assert_eq!(
            parse("{ 1 -1 + } { 100 } { -100 } if"),
            vec![Num(-100)]
        );
    }

    #[test]
    fn test_if_true() {
        assert_eq!(
            parse("{ 1 1 + } { 100 } { -100 } if"),
            vec![Num(100)]
        );
    }

    #[test]
    fn test_var() {
        assert_eq!(
            parse("/x 10 def /y 20 def x y *"),
            vec![Num(200)]
        )
    }

    #[test]
    fn test_var_if() {
        assert_eq!(
            parse("/x 10 def /y 20 def { x y < } { x } { y } if"),
            vec![Num(10)]
        );
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/stackmachine`
/a 100 def /b 200 def { a b < } { a } { 0 } if stack: [Num(100)] /a 100 def /b 200 def { b a < } { a } { 0 } if stack: [Num(0)]