- PVSM.RU - https://www.pvsm.ru -

Пишем стэковую виртуальную машину на Rust’e

Здравствуй! Уже несколько недель я занят разработкой своего языка программирования на Rust. Мне бы хотелось расказать о том с чем может столкнуться новичок в этом деле и о чем ему следует знать.

Краткая предистория

Все началось с форка ein [1], форкнул я его дабы изучить как строятся языки программирования. Так-как ein интерпретируется от и до, то его скорость исполнения оказалась не самой высокой и после того как я начал хоть что то понимать я решил начать писать свой интерпретатор который в конце концов тоже забросил.

Но отчаиваться еще рано! Я прочел пару статей о VM и о том какие они бывают и решил написать простую стековую VM.

Что за "стековая виртуальная машина" и как она работает?

На хабре есть отедльная статья [2] про это, но что бы не гонять по ссылкам изложу вкратце смысл этой вещицы.

Стековая VM выполняет все операции над данными, которые хранятся в виде стека, каждая операция извлекает нужное количество ей данных для операции и после выполнения ее может "отправить" в стек новое число.

Начинаем

Для начала необходимо создать новый проект с помощью cargo:

cargo new habr_vm

Сперва нам необходимо создать несколько базовых операций для нашей VM:

enum Opcode {
      Push(i32),
      Add,
      AddAssign(i32),
      Sub,
      SubAssign(i32),
}

Это наши базовые операции, команда Push будет добавлять в стек новое число, Add и Sub будут брать из стека два числа и выполнять с ними действия(сложение и вычитание соответственно), думаю о AddAssign и SubAssign объяснять не надо.

Cледующая задача это создать саму виртуальную машину, для этого мы создадим не сложную структуру:

struct Vm {
    pub stack: Vec<i32>,
}

И имплементируем ее:

impl Vm {
    //Упрощяем себе жизнь тем что сокращаем писанину
    pub fn pop(&mut self) -> i32 {
        self.stack.pop().unwrap()
    }
    //Здесь будет происходить все самое интересное
    pub fn run(&mut self,program: Vec<Opcode>) {
        for opcode in program { //Проверяем каждую команду в нашей программе
            match opcode {
                Opcode::Push(n) => {
                    //Просто добавляем число в наш стек
                    self.stack.push(n);
                }
                Opcode::Add => {
                    // Берем два числа из стека и складываем их, добавляем в стек уже новое число
                    let value = self.pop() + self.pop();
                    self.stack.push(value);
                }
                Opcode::Sub => {
                   // Тут то же самое что и с складыванием только наоборот 
                    let value = self.pop() - self.pop();
                    self.stack.push(value);
                }
                // Думаю следующие две операции объяснять не нужно
                Opcode::AddAssign(n) => {
                    let mut value = self.pop();
                    value += n;
                    self.stack.push(value);
                }
                Opcode::SubAssign(n) => {
                    let mut value = self.pop();
                    value += n;
                    self.stack.push(value);
                }
            }
        }
    }
}

Мы иплементировали нашу структуру, что дальше? Дальше надо создать нашу "программу".

Вот как это должно выглядеть:

let program = vec![
     Opcode::Push(2),//Добавляем 2 в наш стек
     Opcode::Push(4),// Добавляем 3 в наш стек
     Opcode::Sub,// Вычитаем 4 - 2
];

Все просто, не так ли? Раз так, то давайте уже запустим нашу программу!

let mut vm = Vm {stack: Vec::new()};
vm.run(program);

// Выводим все числа из стека, на данный момент это будет просто 2 
for i in vm.stack() {
     println!("{}", i);
}
// Вывод
2

Очень просто как по мне, таким образом вы можете добавить еще достаточно опкодов для нужной вам операции.

Заключение

Думаю что я вполне понятно объяснил как это все написать на расте и как это работает.

Хочется добавить что вы с легкостью сможете написать свой ЯП благодаря подобному VM, вам по сути остается написать только парсер, лексер и "компилятор", а если вам охото посмотреть на уже готовый проект то можете пройти по данной ссылке [3].

Весь код из статьи доступен в данном репозитории [4]

Удачи Хабр!

Автор: aprokurov

Источник [5]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/rust/285421

Ссылки в тексте:

[1] ein: https://github.com/17cupsofcoffee/ein

[2] статья: https://habr.com/post/138667/

[3] ссылке: https://gitlab.com/playX/Neo

[4] репозитории: https://gitlab.com/playX/habr_vm/blob/master/LICENSE

[5] Источник: https://habr.com/post/416505/?utm_source=habrahabr&utm_medium=rss&utm_campaign=416505