Решение на Basic BASIC от Петко Каменов

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

Към профила на Петко Каменов

Резултати

  • 19 точки от тестове
  • 0 бонус точки
  • 19 точки общо
  • 14 успешни тест(а)
  • 1 неуспешни тест(а)

Код

use std::{
collections::HashMap,
io::{self, BufRead, BufReader, Read, Write}, cmp::Ordering,
};
#[derive(Debug)]
pub enum InterpreterError {
IoError(io::Error),
UnknownVariable { name: String },
NotANumber { value: String },
SyntaxError { code: String },
RuntimeError { line_number: u16, message: String },
}
#[derive(Debug, Eq, PartialEq)]
pub enum Operator {
GreaterThan,
LessThan,
Equal,
}
#[derive(Debug, Eq, PartialEq)]
pub enum Command {
Read {
variable: String,
},
Print {
value: String,
},
Goto {
line_number: u16,
},
If {
left: String,
operator: String,
right: String,
line_number: u16,
},
}
#[derive(Debug)]
pub struct Row {
line_number: u16,
command: Command,
}
// ВАЖНО: тук ще трябва да се добави lifetime анотация!
pub struct Interpreter<'a, R: Read, W: Write> {
output: &'a mut W,
rows: Vec<Row>,
variables: HashMap<String, u16>,
buf_reader: BufReader<R>,
// Каквито полета ви трябват
}
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,
output,
rows: Vec::new(),
variables: HashMap::new(),
buf_reader: BufReader::new(input),
}
}
}
// Не забравяйте 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: &str) -> Result<(), InterpreterError> {
if code.is_empty() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
let mut iter = code.split_whitespace();
let line_number = match iter.next().unwrap().parse::<u16>() {
Ok(line_number) => line_number,
Err(_) => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
let command = match iter.next() {
Some(command) => command,
None => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
match command {
"PRINT" => {
let value = match iter.next() {
Some(value) => value,
None => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
if iter.next().is_some() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
self.rows.push(Row {
line_number,
command: Command::Print {
value: value.to_string(),
},
});
}
"READ" => {
let variable = match iter.next() {
Some(variable) => variable,
None => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
if !variable.chars().next().unwrap().is_uppercase() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
if iter.next().is_some() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
self.rows.push(Row {
line_number,
command: Command::Read {
variable: variable.to_string(),
},
});
}
"GOTO" => {
let goto_line_number = match iter.next() {
Some(goto_line_number) => goto_line_number,
None => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
let goto_line_number = match goto_line_number.parse::<u16>() {
Ok(goto_line_number) => goto_line_number,
Err(_) => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
if iter.next().is_some() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
self.rows.push(Row {
line_number,
command: Command::Goto {
line_number: goto_line_number,
},
});
}
"IF" => {
let left = match iter.next() {
Some(left) => left,
None => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
let operator = match iter.next() {
Some(operator) => operator,
None => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
let right = match iter.next() {
Some(right) => right,
None => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
iter.next().unwrap();
let goto_line_number = match iter.next() {
Some(goto_line_number) => goto_line_number,
None => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
let goto_line_number = match goto_line_number.parse::<u16>() {
Ok(goto_line_number) => goto_line_number,
Err(_) => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
if iter.next().is_some() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
self.rows.push(Row {
line_number,
command: Command::If {
left: left.to_string(),
operator: operator.to_string(),
right: right.to_string(),
line_number: goto_line_number,
},
});
}
_ => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
}
Ok(())
}
}
fn remove_duplicates(rows: &mut Vec<Row>) {
let mut i = 0;
while !rows.is_empty() && i < rows.len() - 1 {
if rows[i].line_number == rows[i + 1].line_number {
rows.remove(i);
} else {
i += 1;
}
}
}
// Не забравяйте 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: &str) -> Result<u16, InterpreterError> {
if value.chars().next().unwrap().is_uppercase() {
match self.variables.get(value) {
Some(value) => Ok(*value),
None => Err(InterpreterError::UnknownVariable {
name: value.to_string(),
}),
}
} else {
match value.parse::<u16>() {
Ok(value) => Ok(value),
Err(_) => Err(InterpreterError::NotANumber {
value: value.to_string(),
}),
}
}
}
}
fn is_string(value: &str) -> bool {
!value.chars().next().unwrap().is_uppercase() && value.parse::<u16>().is_err()
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Функцията започва да изпълнява редовете на програмата в нарастващ ред. Ако стигне до GOTO,
/// скача на съответния ред и продължава от него в нарастващ ред. Когато вече няма ред с
/// по-голямо число, функцията свършва и връща `Ok(())`.
///
/// Вижте по-долу за отделните команди и какви грешки могат да върнат.
///
pub fn run(&mut self) -> Result<(), InterpreterError> {
self.rows.sort_by(|a, b| a.line_number.cmp(&b.line_number));
remove_duplicates(&mut self.rows);
let mut i = 0;
while i < self.rows.len() {
match &self.rows[i].command {
Command::Print { ref value } => {
let value_for_print = if is_string(value) {
value.clone()
} else {
self.validate_evaluate(value, i)?.to_string()
};
match writeln!(self.output, "{}", value_for_print) {
Ok(_) => {}
Err(err) => return Err(InterpreterError::IoError(err)),
}
i += 1;
}
Command::Read { variable } => {
let mut input = String::new();
match self.buf_reader.read_line(&mut input) {
Ok(_) => {
let input = input.trim();
match input.parse::<u16>() {
Ok(value) => {
self.variables.insert(variable.clone(), value);
}
Err(_) => {
return Err(InterpreterError::RuntimeError {
line_number: self.rows[i].line_number,
message: format!("{} is not a number!", input),
});
}
}
}
Err(err) => return Err(InterpreterError::IoError(err)),
}
i += 1;
}
Command::Goto { line_number } => {
//BinarySearch
let new_row = binary_search(&self.rows, line_number);
match new_row {
Some(new_row) => i = new_row,
None => {
return Err(InterpreterError::RuntimeError {
line_number: self.rows[i].line_number,
message: format!("Line {} does not exist!", line_number),
});
}
}
}
Command::If {
left,
operator,
right,
line_number,
} => {
let left = self.validate_evaluate(left, i)?;
let right = self.validate_evaluate(right, i)?;
let operator = match match_operator(operator) {
Some(operator) => operator,
None => {
return Err(InterpreterError::RuntimeError {
line_number: self.rows[i].line_number,
message: format!("{} is not a valid operator!", operator),
});
}
};
let validity = evaluate_boolean_expression(left, operator, right);
if !validity {
i += 1;
} else {
let new_row = binary_search(&self.rows, line_number);
match new_row {
Some(new_row) => i = new_row,
None => {
return Err(InterpreterError::RuntimeError {
line_number: self.rows[i].line_number,
message: format!("Line {} does not exist!", line_number),
});
}
}
}
}
}
}
Ok(())
}
fn validate_evaluate(&self, value: &str, row: usize) -> Result<u16, InterpreterError> {
match self.eval_value(value) {
Ok(value) => Ok(value),
Err(err) => match err {
InterpreterError::NotANumber { value } => Err(InterpreterError::RuntimeError {
line_number: self.rows[row].line_number,
message: format!("{} is not a number neither a valid variable!", value),
}),
InterpreterError::UnknownVariable { name } => Err(InterpreterError::RuntimeError {
line_number: self.rows[row].line_number,
message: format!("{} is not a number neither a valid variable!", name),
}),
_ => Err(err),
},
}
}
}
fn match_operator(operator: &str) -> Option<Operator> {
match operator {
"<" => Some(Operator::LessThan),
">" => Some(Operator::GreaterThan),
"=" => Some(Operator::Equal),
_ => None,
}
}
fn evaluate_boolean_expression(left: u16, operator: Operator, right: u16) -> bool {
match operator {
Operator::LessThan => left < right,
Operator::GreaterThan => left > right,
Operator::Equal => left == right,
}
}
fn binary_search(rows : &Vec<Row>, line_number : &u16) -> Option<usize> {
let mut new_row = None;
let mut left = 0;
let mut right = rows.len() - 1;
let mut middle = (left + right) / 2;
while left <= right {
match rows[middle].line_number.cmp(line_number) {
Ordering::Equal => {
new_row = Some(middle);
break;
}
Ordering::Less => {
left = middle + 1;
}
Ordering::Greater => {
right = middle - 1;
}
}
middle = (left + right) / 2;
}
new_row
}
#[cfg(test)]
mod tests {
use super::*;
// За тестване че някакъв резултат пасва на някакъв pattern:
macro_rules! assert_match {
($expr:expr, $pat:pat) => {
let result = $expr;
if let $pat = result {
// all good
} else {
assert!(
false,
"Expression {:?} does not match the pattern {:?}",
result,
stringify!($pat)
);
}
};
}
#[test]
fn test_basic_1() {
// Забележете `b""` -- низ от байтове
let input: &[u8] = b"1\n2\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 READ A").unwrap();
interpreter.add("20 READ B").unwrap();
interpreter.add("30 PRINT A").unwrap();
interpreter.add("40 PRINT B").unwrap();
interpreter.run().unwrap();
assert_eq!(interpreter.eval_value("A").unwrap(), 1_u16);
assert_eq!(String::from_utf8(output).unwrap(), "1\n2\n");
}
#[test]
fn test_basic_2() {
let input: &[u8] = b"";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
assert_match!(
interpreter.add("10 PRINT"),
Err(InterpreterError::SyntaxError { .. })
);
}
struct NotBuffered {}
impl std::io::Read for NotBuffered {
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
Ok(0)
}
}
#[test]
fn test_not_buffered() {
let input = NotBuffered {};
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 PRINT 10").unwrap();
interpreter.run().unwrap();
}
#[test]
fn test_adding_to_interpreter() {
let input: &[u8] = b"1\n2\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
assert_match!(interpreter.add("10 READ A"), Ok(()));
assert_match!(interpreter.add("20 READ B"), Ok(()));
assert_match!(interpreter.add("30 PRINT A"), Ok(()));
assert_match!(interpreter.add("40 PRINT B"), Ok(()));
assert_match!(interpreter.add("50 GOTO 20"), Ok(()));
assert_match!(interpreter.add("60 IF A > B GOTO 40"), Ok(()));
assert_eq!(interpreter.rows.len(), 6);
assert_eq!(interpreter.rows[0].line_number, 10);
assert_eq!(interpreter.rows[1].line_number, 20);
assert_eq!(interpreter.rows[2].line_number, 30);
assert_eq!(interpreter.rows[3].line_number, 40);
assert_eq!(interpreter.rows[4].line_number, 50);
assert_eq!(interpreter.rows[5].line_number, 60);
assert_eq!(
interpreter.rows[0].command,
Command::Read {
variable: "A".to_string()
}
);
assert_eq!(
interpreter.rows[1].command,
Command::Read {
variable: "B".to_string()
}
);
assert_eq!(
interpreter.rows[2].command,
Command::Print {
value: "A".to_string()
}
);
assert_eq!(
interpreter.rows[3].command,
Command::Print {
value: "B".to_string()
}
);
assert_eq!(
interpreter.rows[4].command,
Command::Goto { line_number: 20 }
);
assert_eq!(
interpreter.rows[5].command,
Command::If {
left: "A".to_string(),
operator: ">".to_string(),
right: "B".to_string(),
line_number: 40
}
);
}
#[test]
fn cyrillic_test() {
let input: &[u8] = b"1\n2\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
assert_match!(interpreter.add("10 READ А"), Ok(()));
assert_match!(interpreter.add("20 READ Ы"), Ok(()));
assert_match!(interpreter.add("30 PRINT А"), Ok(()));
assert_match!(interpreter.add("40 PRINT Ы"), Ok(()));
assert_match!(interpreter.add("50 GOTO 20"), Ok(()));
assert_match!(interpreter.add("60 IF А > Ы GOTO 40"), Ok(()));
assert_match!(interpreter.add("70 IF А < Ы GOTO 50"), Ok(()));
assert_match!(interpreter.add("80 IF А = Ы GOTO 90"), Ok(()));
assert_match!(interpreter.add("90 GOTO 10"), Ok(()));
assert_eq!(interpreter.rows.len(), 9);
assert_eq!(interpreter.rows[0].line_number, 10);
assert_eq!(interpreter.rows[1].line_number, 20);
assert_eq!(interpreter.rows[2].line_number, 30);
assert_eq!(interpreter.rows[3].line_number, 40);
assert_eq!(interpreter.rows[4].line_number, 50);
assert_eq!(interpreter.rows[5].line_number, 60);
assert_eq!(interpreter.rows[6].line_number, 70);
assert_eq!(interpreter.rows[7].line_number, 80);
assert_eq!(interpreter.rows[8].line_number, 90);
assert_eq!(
interpreter.rows[0].command,
Command::Read {
variable: "А".to_string()
}
);
assert_eq!(
interpreter.rows[1].command,
Command::Read {
variable: "Ы".to_string()
}
);
assert_eq!(
interpreter.rows[2].command,
Command::Print {
value: "А".to_string()
}
);
assert_eq!(
interpreter.rows[3].command,
Command::Print {
value: "Ы".to_string()
}
);
assert_eq!(
interpreter.rows[4].command,
Command::Goto { line_number: 20 }
);
assert_eq!(
interpreter.rows[5].command,
Command::If {
left: "А".to_string(),
operator: ">".to_string(),
right: "Ы".to_string(),
line_number: 40
}
);
assert_eq!(
interpreter.rows[6].command,
Command::If {
left: "А".to_string(),
operator: "<".to_string(),
right: "Ы".to_string(),
line_number: 50
}
);
assert_eq!(
interpreter.rows[7].command,
Command::If {
left: "А".to_string(),
operator: "=".to_string(),
right: "Ы".to_string(),
line_number: 90
}
);
assert_eq!(
interpreter.rows[8].command,
Command::Goto { line_number: 10 }
);
}
#[test]
fn longer_variable_names() {
let input: &[u8] = b"1\n2\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
assert_match!(interpreter.add("10 READ FOO"), Ok(()));
assert_match!(interpreter.add("20 READ BAR"), Ok(()));
assert_match!(interpreter.add("30 PRINT FOO"), Ok(()));
assert_match!(interpreter.add("40 PRINT BAR"), Ok(()));
assert_eq!(interpreter.rows.len(), 4);
assert_eq!(interpreter.rows[0].line_number, 10);
assert_eq!(interpreter.rows[1].line_number, 20);
assert_eq!(interpreter.rows[2].line_number, 30);
assert_eq!(interpreter.rows[3].line_number, 40);
assert_eq!(
interpreter.rows[0].command,
Command::Read {
variable: "FOO".to_string()
}
);
assert_eq!(
interpreter.rows[1].command,
Command::Read {
variable: "BAR".to_string()
}
);
assert_eq!(
interpreter.rows[2].command,
Command::Print {
value: "FOO".to_string()
}
);
assert_eq!(
interpreter.rows[3].command,
Command::Print {
value: "BAR".to_string()
}
);
}
#[test]
pub fn syntax_errors_test() {
let input: &[u8] = b"1\n2\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
assert_match!(
interpreter.add("").unwrap_err(),
InterpreterError::SyntaxError { .. }
);
assert_match!(
interpreter.add("10 PRINT A B").unwrap_err(),
InterpreterError::SyntaxError { .. }
);
assert_match!(
interpreter.add("20 PRINT A + B").unwrap_err(),
InterpreterError::SyntaxError { .. }
);
assert_match!(
interpreter.add("20 IF 13 > 18 GOTO").unwrap_err(),
InterpreterError::SyntaxError { .. }
);
assert_match!(
interpreter.add("READ X").unwrap_err(),
InterpreterError::SyntaxError { .. }
);
assert_match!(
interpreter.add("PRINT X").unwrap_err(),
InterpreterError::SyntaxError { .. }
);
assert_match!(
interpreter.add("GOTO 10").unwrap_err(),
InterpreterError::SyntaxError { .. }
);
assert_match!(
interpreter.add("IF 13 > 18 GOTO 10").unwrap_err(),
InterpreterError::SyntaxError { .. }
);
assert_match!(
interpreter.add("30").unwrap_err(),
InterpreterError::SyntaxError { .. }
);
}
#[test]
fn eval_value_tests() {
let input: &[u8] = b"1\n2\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.variables.insert("A".to_string(), 10);
interpreter.variables.insert("B".to_string(), 20);
interpreter.variables.insert("FOO".to_string(), 30);
interpreter.variables.insert("БАР".to_string(), 40);
interpreter.variables.insert("ЦЫПЛЁНОК".to_string(), 505);
assert_match!(interpreter.eval_value("A"), Ok(10));
assert_match!(interpreter.eval_value("B"), Ok(20));
assert_match!(interpreter.eval_value("FOO"), Ok(30));
assert_match!(interpreter.eval_value("БАР"), Ok(40));
assert_match!(interpreter.eval_value("ЦЫПЛЁНОК"), Ok(505));
assert_match!(interpreter.eval_value("1"), Ok(1));
assert_match!(interpreter.eval_value("234"), Ok(234));
assert_match!(interpreter.eval_value("0"), Ok(0));
assert_match!(
interpreter.eval_value("C"),
Err(InterpreterError::UnknownVariable { .. })
);
assert_match!(
interpreter.eval_value("abc"),
Err(InterpreterError::NotANumber { .. })
);
}
#[test]
fn test_print() {
let input: &[u8] = b"1\n2\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.variables.insert("A".to_string(), 10);
interpreter.variables.insert("B".to_string(), 20);
interpreter.variables.insert("FOO".to_string(), 30);
interpreter.variables.insert("БАР".to_string(), 40);
interpreter.add("10 PRINT A").unwrap();
interpreter.add("20 PRINT B").unwrap();
interpreter.add("9 PRINT FOO").unwrap();
interpreter.add("12 PRINT БАР").unwrap();
interpreter.add("22 PRINT 321").unwrap();
interpreter.add("21 PRINT rust").unwrap();
interpreter.add("1 PRINT ръсти").unwrap();
assert_match!(interpreter.run(), Ok(()));
assert_eq!(
std::str::from_utf8(&interpreter.output).unwrap(),
"ръсти\n30\n10\n40\n20\nrust\n321\n"
);
}
#[test]
fn test_print_with_errors_unknown_variable() {
let input: &[u8] = b"1\n2\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 PRINT A").unwrap();
interpreter.add("20 PRINT B").unwrap();
interpreter.add("10 PRINT АБЦ").unwrap();
interpreter.add("5 PRINT 123").unwrap();
interpreter.add("20 PRINT 321").unwrap();
assert_match!(
interpreter.run(),
Err(InterpreterError::RuntimeError {
line_number: 10,
..
})
);
}
#[test]
fn test_read() {
let input: &[u8] = b"1\n21\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 READ A").unwrap();
interpreter.add("20 READ БЕ").unwrap();
interpreter.add("30 PRINT A").unwrap();
interpreter.add("40 PRINT БЕ").unwrap();
assert_match!(interpreter.run(), Ok(()));
assert_eq!(std::str::from_utf8(&interpreter.output).unwrap(), "1\n21\n");
}
#[test]
fn test_invalid_read() {
let input: &[u8] = b"1\n2s\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 READ A").unwrap();
interpreter.add("20 READ B").unwrap();
assert_match!(
interpreter.run(),
Err(InterpreterError::RuntimeError {
line_number: 20,
..
})
);
}
#[test]
fn test_goto() {
let input: &[u8] = b"1\n2\n99\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 GOTO 20").unwrap();
interpreter.add("15 PRINT 99").unwrap();
interpreter.add("20 PRINT beggining").unwrap();
interpreter.add("30 PRINT 321").unwrap();
interpreter.add("25 GOTO 30").unwrap();
interpreter.add("30 PRINT end").unwrap();
assert_match!(interpreter.run(), Ok(()));
assert_eq!(
std::str::from_utf8(&interpreter.output).unwrap(),
"beggining\nend\n"
);
}
#[test]
fn test_failing_goto() {
let input: &[u8] = b"1\n2\n99\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 GOTO 20").unwrap();
interpreter.add("15 PRINT 99").unwrap();
interpreter.add("21 PRINT 123").unwrap();
assert_match!(
interpreter.run(),
Err(InterpreterError::RuntimeError {
line_number: 10,
..
})
);
}
#[test]
fn test_run_empty_program() {
let input: &[u8] = b"1\n2\n99\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
assert_match!(interpreter.run(), Ok(()));
}
#[test]
fn test_if_operator() {
let input: &[u8] = b"1\n2\n99\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 READ A").unwrap();
interpreter.add("20 READ B").unwrap();
interpreter.add("30 IF A > B GOTO 40").unwrap();
interpreter.add("35 IF A < B GOTO 45").unwrap();
interpreter.add("40 PRINT A").unwrap();
interpreter.add("45 PRINT B").unwrap();
interpreter.add("50 IF B = 2 GOTO 60").unwrap();
interpreter.add("55 PRINT B").unwrap();
interpreter.add("60 PRINT end").unwrap();
assert_match!(interpreter.run(), Ok(()));
assert_eq!(
std::str::from_utf8(&interpreter.output).unwrap(),
"2\nend\n"
);
}
#[test]
fn test_if_operator_2() {
let input: &[u8] = b"1\n2\n99\n99\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 READ A").unwrap();
interpreter.add("20 READ B").unwrap();
interpreter.add("30 READ C").unwrap();
interpreter.add("40 READ D").unwrap();
interpreter.add("50 IF C = D GOTO 60").unwrap();
interpreter.add("55 PRINT should_not_print").unwrap();
interpreter.add("60 IF A < B GOTO 75").unwrap();
interpreter.add("70 PRINT should_not_print").unwrap();
interpreter.add("75 PRINT C").unwrap();
interpreter.add("80 IF 51 > 49 GOTO 90").unwrap();
interpreter.add("85 PRINT should_not_print").unwrap();
interpreter.add("90 PRINT end").unwrap();
assert_match!(interpreter.run(), Ok(()));
assert_eq!(
std::str::from_utf8(&interpreter.output).unwrap(),
"99\nend\n"
);
}
#[test]
fn test_failing_if() {
let input: &[u8] = b"1\n2\n99\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 READ A").unwrap();
interpreter.add("20 READ B").unwrap();
interpreter.add("30 IF A < B GOTO 40").unwrap();
assert_match!(
interpreter.run(),
Err(InterpreterError::RuntimeError {
line_number: 30,
..
})
);
}
#[test]
fn test_failing_if_2() {
let input: &[u8] = b"1\n2\n99\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 READ A").unwrap();
interpreter.add("20 READ B").unwrap();
interpreter.add("30 IF A > D GOTO 40").unwrap();
interpreter.add("40 PRINT A").unwrap();
assert_match!(
interpreter.run(),
Err(InterpreterError::RuntimeError {
line_number: 30,
..
})
);
}
#[test]
fn test_failing_if_3() {
let input: &[u8] = b"1\n2\n99\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 READ A").unwrap();
interpreter.add("20 READ B").unwrap();
interpreter.add("30 IF A > dff GOTO 40").unwrap();
assert_match!(
interpreter.run(),
Err(InterpreterError::RuntimeError {
line_number: 30,
..
})
);
}
}

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

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

running 15 tests
test solution_test::test_basic_if ... ok
test solution_test::test_basic_goto ... ok
test solution_test::test_basic_input ... ok
test solution_test::test_basic_print ... ok
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 ... ok
test solution_test::test_print_cyrillic ... ok
test solution_test::test_runtime_errors ... ok
test solution_test::test_print_vars_and_strings ... ok
test solution_test::test_syntax_errors_1 ... ok
test solution_test::test_syntax_errors_2 ... FAILED

failures:

---- solution_test::test_syntax_errors_2 stdout ----
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', /tmp/d20230111-3772066-1qr6bre/solution/src/lib.rs:227:29
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'solution_test::test_syntax_errors_2' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:275:5


failures:
    solution_test::test_syntax_errors_2

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

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

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

Петко качи първо решение на 05.01.2023 18:57 (преди над 2 години)