Решение на Basic BASIC от Климент Бербатов

Обратно към всички решения

Към профила на Климент Бербатов

Резултати

  • 15 точки от тестове
  • 0 бонус точки
  • 15 точки общо
  • 11 успешни тест(а)
  • 4 неуспешни тест(а)

Код

use std::{
collections::HashMap,
io::{self, BufRead, BufReader, Read, Write},
};
#[derive(Debug)]
pub enum InterpreterError {
IoError(io::Error),
UnknownVariable { name: String },
NotANumber { value: String },
SyntaxError { code: String },
RuntimeError { line_number: u16, message: String },
}
// ВАЖНО: тук ще трябва да се добави lifetime анотация!
#[derive(Debug)]
pub struct Interpreter<'a, R: Read, W: Write> {
input: BufReader<R>,
output: &'a mut W,
rows: Vec<Vec<&'a str>>,
vars: HashMap<&'a str, u16>,
}
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Конструира нов интерпретатор, който чете вход от `input` чрез `READ` командата и пише в
/// `output` чрез `PRINT`.
///
pub fn new(input: R, output: &'a mut W) -> Self {
Interpreter {
input: BufReader::new(input),
output: output,
rows: Vec::new(),
vars: HashMap::new(),
}
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Тази функция добавя ред код към интерпретатора. Този ред се очаква да започва с 16-битово
/// unsigned число, последвано от някаква команда и нейните параметри. Всички компоненти на
/// реда ще бъдат разделени точно с един интервал -- или поне така ще ги тестваме.
///
/// Примерни редове:
///
/// 10 IF 2 > 1 GOTO 30
/// 20 GOTO 10
/// 30 READ V
/// 40 PRINT V
///
/// В случай, че реда не е валидна команда (детайли по-долу), очакваме да върнете
/// `InterpreterError::SyntaxError` с кода, който е бил подаден.
///
pub fn add(&mut self, code: &'a str) -> Result<(), InterpreterError> {
let row: Vec<&str> = code.split(' ').collect();
let error = Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
match row[0].parse::<u16>() {
Ok(_) => (),
_ => return error,
}
match row[1] {
"PRINT" => {
if row.len() != 3 {
return error;
}
self.rows.push(row);
()
}
"READ" => {
if row.len() != 3 {
return error;
} else if !row[2].chars().nth(0).unwrap().is_uppercase() {
return error;
}
self.rows.push(row);
()
}
"GOTO" => {
if row.len() != 3 {
return error;
}
match row[2].parse::<u16>() {
Ok(_) => self.rows.push(row),
_ => return error,
}
()
}
"IF" => {
if row.len() != 7 {
return error;
}
if !vec![">", "<", "="].contains(&row[3]) || row[5] != "GOTO" {
return error;
}
match row[6].parse::<u16>() {
Ok(_) => self.rows.push(row),
_ => return error,
}
()
}
_ => return error,
}
return Ok(());
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Оценява `value` като стойност в контекста на интерпретатора:
///
/// - Ако `value` е низ, който започва с главна буква (съгласно `char::is_uppercase`), търсим
/// дефинирана променлива с това име и връщаме нейната стойност.
/// -> Ако няма такава, връщаме `InterpreterError::UnknownVariable` с това име.
/// - Ако `value` е валидно u16 число, връщаме числото
/// -> Иначе, връщаме `InterpreterError::NotANumber` с тази стойност
///
pub fn eval_value(&self, value: &'a str) -> Result<u16, InterpreterError> {
if value.chars().nth(0).unwrap().is_uppercase() {
let val = self.vars.get(value);
match val {
Some(num) => return Ok(*num),
_ => {
return Err(InterpreterError::UnknownVariable {
name: value.to_string(),
})
}
}
} else if let Ok(num) = value.parse::<u16>() {
return Ok(num);
} else {
return Err(InterpreterError::NotANumber {
value: value.to_string(),
});
}
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Функцията започва да изпълнява редовете на програмата в нарастващ ред. Ако стигне до GOTO,
/// скача на съответния ред и продължава от него в нарастващ ред. Когато вече няма ред с
/// по-голямо число, функцията свършва и връща `Ok(())`.
///
/// Вижте по-долу за отделните команди и какви грешки могат да върнат.
///
pub fn run(&mut self) -> Result<(), InterpreterError> {
let mut code = HashMap::new();
for row in self.rows.iter() {
let key = row[0].parse::<u16>().unwrap();
code.insert(key, row);
}
let mut lines: Vec<&u16> = code.keys().clone().collect();
lines.sort();
let mut row_ind = 0;
// let reader = BufReader::new(self.input.by_ref());
while row_ind < lines.len() {
let cur_row = &self.rows[row_ind as usize];
match cur_row[1] {
"PRINT" => {
let var = cur_row[2];
if var.chars().nth(0).unwrap().is_uppercase() {
// if "PRINT <var name>"
let value = self.vars.get(var);
match value {
Some(num) => {
let bytes = format!("{}\n",num).into_bytes();
match self.output.write(&bytes) {
// write the variable to output
Ok(_) => (),
Err(ioerror) => return Err(InterpreterError::IoError(ioerror)),
}
}
_ => {
//if variable does not exist
return Err(InterpreterError::RuntimeError {
line_number: cur_row[0].parse::<u16>().unwrap(),
message: "no such variable recorded\n".to_string(),
});
}
}
} else if let Ok(num) = var.parse::<u16>() {
// if "PRINT <num>"
match self.output.write(&num.to_be_bytes()) {
Ok(_) => (),
Err(ioerror) => return Err(InterpreterError::IoError(ioerror)),
}
} else {
// lowercase is treated as string
match self.output.write(&format!("{}\n",var).as_bytes()) {
Ok(_) => (),
Err(ioerror) => return Err(InterpreterError::IoError(ioerror)),
}
}
row_ind = row_ind + 1;
}
"READ" => {
let mut from_input = String::new();
match self.input.read_line(&mut from_input) {
// read from input
Ok(_) => (),
Err(ioerror) => return Err(InterpreterError::IoError(ioerror)),
};
match from_input.trim_end().parse::<u16>() {
// check if read a valid number
Ok(num) => {
self.vars.insert(cur_row[2], num);
}
Err(_) => {
return Err(InterpreterError::RuntimeError {
line_number: cur_row[0].parse::<u16>().unwrap(),
message: "input is not a number\n".to_string(),
})
}
}
row_ind = row_ind + 1;
}
"GOTO" => {
let new_row = cur_row[2].parse::<u16>().unwrap();
if let Ok(k) = lines.binary_search(&&new_row) {
row_ind = k;
} else {
return Err(InterpreterError::RuntimeError {
line_number: cur_row[0].parse::<u16>().unwrap(),
message: "GOTO call failed".to_string(),
});
}
}
"IF" => {
let left = self.eval_value(cur_row[2]);
let right = self.eval_value(cur_row[4]);
match (left, right) {
(Ok(lhs), Ok(rhs)) => match cur_row[3] {
">" => {
if lhs > rhs {
let new_row = cur_row[6].parse::<u16>().unwrap();
if let Ok(k) = lines.binary_search(&&new_row) {
row_ind = k;
} else {
return Err(InterpreterError::RuntimeError {
line_number: cur_row[0].parse::<u16>().unwrap(),
message: "GOTO call failed".to_string(),
});
}
} else {
row_ind = row_ind + 1;
}
}
"<" => {
if lhs < rhs {
let new_row = cur_row[6].parse::<u16>().unwrap();
if let Ok(k) = lines.binary_search(&&new_row) {
row_ind = k;
} else {
return Err(InterpreterError::RuntimeError {
line_number: cur_row[0].parse::<u16>().unwrap(),
message: "GOTO call failed".to_string(),
});
}
} else {
row_ind = row_ind + 1;
}
}
_ => {
if lhs == rhs {
let new_row = cur_row[6].parse::<u16>().unwrap();
if let Ok(k) = lines.binary_search(&&new_row) {
row_ind = k;
} else {
return Err(InterpreterError::RuntimeError {
line_number: cur_row[0].parse::<u16>().unwrap(),
message: "GOTO call failed".to_string(),
});
}
} else {
row_ind = row_ind + 1;
}
}
},
_ => {
//if left or right is incorrect
return Err(InterpreterError::RuntimeError {
line_number: cur_row[0].parse::<u16>().unwrap(),
message: "values for logical operation are incorrect".to_string(),
});
}
}
}
_ => (),
}
}
Ok(())
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20230111-3772066-kapmj0/solution)
    Finished test [unoptimized + debuginfo] target(s) in 1.51s
     Running tests/solution_test.rs (target/debug/deps/solution_test-0edbea2040daef01)

running 15 tests
test solution_test::test_basic_goto ... FAILED
test solution_test::test_basic_if ... FAILED
test solution_test::test_basic_input ... ok
test solution_test::test_basic_print ... FAILED
test solution_test::test_basic_read ... ok
test solution_test::test_erroring_goto ... ok
test solution_test::test_full_program ... ok
test solution_test::test_io_error_read ... ok
test solution_test::test_io_error_write ... ok
test solution_test::test_line_order_and_overwriting ... FAILED
test solution_test::test_print_vars_and_strings ... ok
test solution_test::test_print_cyrillic ... ok
test solution_test::test_runtime_errors ... ok
test solution_test::test_syntax_errors_1 ... ok
test solution_test::test_syntax_errors_2 ... ok

failures:

---- solution_test::test_basic_goto stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"\0\u{1}\0\u{3}"`,
 right: `"1\n3\n"`', tests/solution_test.rs:193:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'solution_test::test_basic_goto' panicked at 'assertion failed: `(left == right)`
  left: `"\0\u{1}\0\u{3}"`,
 right: `"1\n3\n"`', tests/solution_test.rs:180:5

---- solution_test::test_basic_if stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"\0\u{1}"`,
 right: `"1\n"`', tests/solution_test.rs:224:9
thread 'solution_test::test_basic_if' panicked at 'assertion failed: `(left == right)`
  left: `"\0\u{1}"`,
 right: `"1\n"`', tests/solution_test.rs:212:5

---- solution_test::test_basic_print stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"\0*\0\u{18}"`,
 right: `"42\n24\n"`', tests/solution_test.rs:78:9
thread 'solution_test::test_basic_print' panicked at 'assertion failed: `(left == right)`
  left: `"\0*\0\u{18}"`,
 right: `"42\n24\n"`', tests/solution_test.rs:67:5

---- solution_test::test_line_order_and_overwriting stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"\0\u{2}\0\u{1}\0\u{3}"`,
 right: `"1\n2\n4\n"`', tests/solution_test.rs:97:9
thread 'solution_test::test_line_order_and_overwriting' panicked at 'assertion failed: `(left == right)`
  left: `"\0\u{2}\0\u{1}\0\u{3}"`,
 right: `"1\n2\n4\n"`', tests/solution_test.rs:84:5


failures:
    solution_test::test_basic_goto
    solution_test::test_basic_if
    solution_test::test_basic_print
    solution_test::test_line_order_and_overwriting

test result: FAILED. 11 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--test solution_test`

История (1 версия и 0 коментара)

Климент качи първо решение на 07.01.2023 18:25 (преди 5 месеца)