Forth

Предадени решения

Краен срок:
27.01.2019 17:00
Точки:
20

Срокът за предаване на решения е отминал

// Бележка: името на проекта трябва да се казва "solution". Ако не се казва така, променете го на
// тези два реда:
extern crate solution;
use solution::*;
#[test]
fn test_builtin_arithmetic() {
assert_eq!(Interpreter::new().run("1 2 3 ADD"), Ok(Some(5)));
assert_eq!(Interpreter::new().run("12 3 ADD"), Ok(Some(15)));
assert_eq!(Interpreter::new().run("1 ADD"), Err(RuntimeError::StackUnderflow));
assert_eq!(Interpreter::new().run("ADD"), Err(RuntimeError::StackUnderflow));
assert_eq!(Interpreter::new().run("1 2 3 SUB"), Ok(Some(1)));
assert_eq!(Interpreter::new().run("8 3 SUB"), Ok(Some(-5)));
assert_eq!(Interpreter::new().run("1 SUB"), Err(RuntimeError::StackUnderflow));
assert_eq!(Interpreter::new().run("SUB"), Err(RuntimeError::StackUnderflow));
assert_eq!(Interpreter::new().run("1 2 3 MUL"), Ok(Some(6)));
assert_eq!(Interpreter::new().run("12 3 MUL"), Ok(Some(36)));
assert_eq!(Interpreter::new().run("1 MUL"), Err(RuntimeError::StackUnderflow));
assert_eq!(Interpreter::new().run("MUL"), Err(RuntimeError::StackUnderflow));
assert_eq!(Interpreter::new().run("1 2 6 DIV"), Ok(Some(3)));
assert_eq!(Interpreter::new().run("3 12 DIV"), Ok(Some(4)));
assert_eq!(Interpreter::new().run("1 DIV"), Err(RuntimeError::StackUnderflow));
assert_eq!(Interpreter::new().run("DIV"), Err(RuntimeError::StackUnderflow));
assert_eq!(Interpreter::new().run("0 DIV"), Err(RuntimeError::StackUnderflow));
assert_eq!(Interpreter::new().run("0 1 DIV"), Err(RuntimeError::DivideByZero));
assert_eq!(Interpreter::new().run("1 0 DIV"), Ok(Some(0)));
}
#[test]
fn test_builtin_pop() {
{
let mut interpreter = Interpreter::new();
assert_eq!(interpreter.run("1 2 3 POP"), Ok(Some(2)));
assert_eq!(interpreter.stack().collect::<Vec<_>>(), vec![2, 1]);
}
assert_eq!(Interpreter::new().run("POP"), Err(RuntimeError::StackUnderflow));
}
#[test]
fn test_builtin_dup() {
{
let mut interpreter = Interpreter::new();
assert_eq!(interpreter.run("1 2 DUP"), Ok(Some(2)));
assert_eq!(interpreter.stack().collect::<Vec<_>>(), vec![2, 2, 1]);
}
assert_eq!(Interpreter::new().run("DUP"), Err(RuntimeError::StackUnderflow));
}
#[test]
fn test_builtin_swap() {
{
let mut interpreter = Interpreter::new();
assert_eq!(interpreter.run("1 2 3 SWAP"), Ok(Some(2)));
assert_eq!(interpreter.stack().collect::<Vec<_>>(), vec![2, 3, 1]);
}
assert_eq!(Interpreter::new().run("1 SWAP"), Err(RuntimeError::StackUnderflow));
assert_eq!(Interpreter::new().run("SWAP"), Err(RuntimeError::StackUnderflow));
}
#[test]
fn test_def_var() {
let mut interpreter = Interpreter::new();
interpreter.def_var("THREE", 3);
assert_eq!(interpreter.run("THREE"), Ok(Some(3)));
}
#[test]
fn test_def_unary() {
let mut interpreter = Interpreter::new();
interpreter.def_unary_op("PLUS1", |x| x + 1);
assert_eq!(interpreter.run("2 PLUS1 PLUS1"), Ok(Some(4)));
}
#[test]
fn test_def_binary() {
let mut interpreter = Interpreter::new();
interpreter.def_binary_op("PLUS", |x, y| x + y);
interpreter.def_binary_op("MULT", |x, y| x * y);
assert_eq!(
interpreter.run("1 2 PLUS 3 4 PLUS MULT"),
Ok(Some(21))
);
}
#[test]
fn test_unknown_word() {
assert_eq!(Interpreter::new().run("1 2 UNKNOWN 3"), Err(RuntimeError::UnknownWord));
assert_eq!(Interpreter::new().run("OTHER"), Err(RuntimeError::UnknownWord));
let mut interpreter = Interpreter::new();
interpreter.def_var("ZERO", 0);
interpreter.def_unary_op("ONE", |_x| 1);
interpreter.def_binary_op("TWO", |_x, _y| 1);
assert_eq!(interpreter.run("THREE"), Err(RuntimeError::UnknownWord));
}
#[test]
fn test_multiple_run() {
let mut interpreter = Interpreter::new();
assert_eq!(interpreter.run("1 2"), Ok(Some(2)));
assert_eq!(interpreter.run("ADD"), Ok(Some(3)));
assert_eq!(interpreter.run("3"), Ok(Some(3)));
assert_eq!(interpreter.run("4"), Ok(Some(4)));
assert_eq!(interpreter.run("ADD MUL"), Ok(Some(21)));
assert_eq!(interpreter.run("POP"), Ok(None));
}
#[test]
fn test_whitespace() {
let source = " 1\t\t2 ADD\n3\t4\n\nADD MUL\t\n";
assert_eq!(Interpreter::new().run(source), Ok(Some(21)));
}

Целта на задачата е да се имплементира интерпретатор за стеков език. В такъв вид език за подаване на параметри на функции се използва структурата стек. Функциите не приемат аргументи, вместо това директно работят със стойностите записани върху стека.

Например операцията 2 * 3 написана на стеков език би се представила като 3 2 mul - 3 - добави 3 към стека, стека вече съдържа 3 - 2 - добави 2 към стека, стека вече съдържа 3, 2 - mul - извади два елемента от върха на стека (първият аргумент е 2, вторият аргумент е 3), умножи ги и постави резултата в стека. Стека вече съдържа 6

Език

Граматиката на езика с който ще работим съдържа два елемента:

  • число - цяло число със знак в границите на i32.
  • дума - всичко останало. По-точно поредица от символи, не съдържаща whitespace, която не може да се интерпретира като число.

Горе дефинираните елементи са разделени с произволен брой whitespace символи помежду им.

Числата директно се добавят към върха на стека. Думите могат да са вградени оператори или оператори дефинирани от потребителя.

Вградени оператори

Трябва да имплементирате следните вградени оператори

  • ADD, SUB, MUL, DIV - математическите операции събиране, изваждане, умножение и деление върху две числа
  • DUP - push-ва дупликат на елемента на върха на стека
  • POP - премахва елемента на върха на стека
  • SWAP - разменя двата елемента на върха на стека

Оператори дефинирани от потребителя

Потребителя може да дефинира три вида оператори.

Променлива - при срещане в програмата в стека се добавя стойността и. Пример:

interpreter.def_var("THREE", 3);
interpreter.run("1 2 THREE");
// стека съдържа 1, 2, 3

Едноместен оператор - потребителя предоставя closure, който трябва да се извика когато се срещне думата. Аргумента се взима от стека и резултата се добавя в стека.

interpreter.def_unary_op("NEG", |x| -x);
interpreter.run("3 NEG");
// стека съдържа -3

Двуместен оператор - същото като едноместния, но с два аргумента.

interpreter.def_binary_op("PLUS", |x, y| x + y);
interpreter.run("2 3 PLUS");
// стека съдържа 5

Имплементация

Очаква се да напишете интерпретатор със следния интерфейс

/// Грешки, които могат да се срещнат по време на изпълнение на програмата
///
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
    DivideByZero,
    StackUnderflow,
    UnknownWord,
}

pub struct Interpreter { ... }

impl Interpreter {
    /// Създава нов интерпретатор
    ///
    pub fn new() -> Self {
        unimplemented!()
    }

    /// Дефинира нова дума за променлива.
    ///
    /// При срещане на думата `name` се добавя стойността `val`
    /// на върха на стека.
    ///
    pub fn def_var(&mut self, name: &str, val: i32) {
        unimplemented!()
    }

    /// Дефинира нова дума за едноместен оператор
    ///
    /// При срещане на думата `name` се изважда най-горният елемент
    /// от стека, изпълнява се `op` и резултатът се добавя в стека.
    ///
    pub fn def_unary_op<F>(&mut self, name: &str, op: F)
    where
        F: Fn(i32) -> i32 + 'static,
    {
        unimplemented!()
    }

    /// Дефинира нова дума за едноместен оператор
    ///
    /// При срещане на думата `name` се изваждат най-горните два елемента
    /// от стека, изпълнява се `op` и резултатът се добавя в стека.
    ///
    pub fn def_binary_op<F>(&mut self, name: &str, op: F)
    where
        F: Fn(i32, i32) -> i32 + 'static,
    {
        unimplemented!()
    }

    /// Изпълнява програмата `input` в този интерпретатор.
    ///
    /// Ако програмата се изпълни успрешно се връща `Ok(top)`, където
    /// `top` е елемента на върха на стека, или `None` ако стекът е празен.
    ///
    /// Ако по време на изпълнението се срещне грешка се връща `Err`.
    ///
    pub fn run(&mut self, input: &str) -> Result<Option<i32>, RuntimeError> {
        unimplemented!()
    }

    /// Връща итератор по текущото съдържание на стека.
    ///
    /// Първият елемент на итератора е върха на стека.
    ///
    pub fn stack<'a>(&'a self) -> impl Iterator<Item = i32> + 'a {
        unimplemented!()
    }
}

Примерният тест се намира тук.

Задължително прочетете (или си припомнете): Указания за предаване на домашни