Решение на Basic BASIC от Георги Бойчев
Резултати
- 15 точки от тестове
- 0 бонус точки
- 15 точки общо
- 11 успешни тест(а)
- 4 неуспешни тест(а)
Код
use std::{
collections::{BTreeMap, HashMap},
fmt::Error,
hash::Hash,
io::{self, BufRead, BufReader, BufWriter, ErrorKind, 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 анотация!
pub struct Interpreter<'a, R: Read, W: Write + 'a> {
// Тези полета не са публични, така че може да си ги промените, ако искате:
input: BufReader<R>,
output: &'a mut W,
realOrderedText: BTreeMap<u32, String>,
variables: HashMap<String, u16>, // inputBuf: BufReader<R>,
// outputBuf: BufWriter<&'a mut W>
// Каквито други полета ви трябват
}
impl<'a, R: Read, W: Write + 'a> Interpreter<'a, R, W> {
/// Конструира нов интерпретатор, който чете вход от `input` чрез `READ` командата и пише в
/// `output` чрез `PRINT`.
///
pub fn new(input: R, output: &'a mut W) -> Self {
let reader = BufReader::new(input);
let lines = BTreeMap::<u32, String>::new();
let variables = HashMap::<String, u16>::new();
// Interpreter { input: input, output: output, inputBuf: reader, outputBuf: writer }
Interpreter {
input: reader,
output: output,
realOrderedText: lines,
variables: variables,
}
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write + 'a> 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> {
let chunks: Vec<&str> = code.split(" ").collect();
if chunks.is_empty() || chunks.len() < 3 {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
let mut is_numeric: bool = true;
for c in chunks[0].chars() {
if !c.is_numeric() {
is_numeric = false;
}
}
if is_numeric == false {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
match chunks[1] {
"PRINT" => {
if chunks.len() > 3 {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
}
"READ" => {
if chunks.len() > 3 {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
let mut first_letter_is_uppercase = true;
for c in chunks[2].chars() {
if c.is_uppercase() {
first_letter_is_uppercase = true;
} else {
first_letter_is_uppercase = false;
}
break;
}
if first_letter_is_uppercase == false {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
}
"GOTO" => {
if chunks.len() > 3 {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
if chunks[2].parse::<u16>().is_err() {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
}
"IF" => {
if chunks.len() != 7 {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
}
_ => {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
})
}
}
// if !chunks[1].eq("PRINT") && !chunks[1].eq("READ") && !chunks[1].eq("GOTO") && !chunks[1].eq("IF") {
// return Err(InterpreterError::SyntaxError { code: String::from(code) })
// }
match code.split_once(' ') {
Some((num, rest)) => {
self.realOrderedText.insert(
num.parse().expect("first thing should be a number"),
String::from(rest),
);
}
None => {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
}
return Ok(());
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write + 'a> 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> {
let mut first_letter_is_uppercase = true;
for c in value.chars() {
if c.is_uppercase() {
first_letter_is_uppercase = true;
} else {
first_letter_is_uppercase = false;
}
break;
}
if first_letter_is_uppercase {
match self.variables.get(value) {
Some(_u16) => Ok(*self.variables.get(value).unwrap()),
None => {
return Err(InterpreterError::UnknownVariable {
name: String::from(value),
})
}
}
} else {
if value.trim().parse::<u16>().is_ok() {
return Ok(value.trim().parse::<u16>().unwrap());
} else {
return Err(InterpreterError::NotANumber {
value: String::from(value),
});
}
}
}
}
impl<'a, R: Read, W: Write + 'a> Interpreter<'a, R, W> {
/// Функцията започва да изпълнява редовете на програмата в нарастващ ред. Ако стигне до GOTO,
/// скача на съответния ред и продължава от него в нарастващ ред. Когато вече няма ред с
/// по-голямо число, функцията свършва и връща `Ok(())`.
///
/// Вижте по-долу за отделните команди и какви грешки могат да върнат.
///
pub fn run(&mut self) -> Result<(), InterpreterError> {
let max = self.realOrderedText.keys().max().unwrap();
let mut counter: u32 = 0;
while counter <= *max {
if self.realOrderedText.get(&counter).is_none() {
counter += 1;
continue;
} else {
let value = self.realOrderedText.get(&counter).unwrap();
let key = &counter;
let chunks: Vec<&str> = value.split(" ").collect();
match chunks[0] {
"READ" => {
let key_for_insert = chunks[1];
let mut value = String::from("");
self.input.read_line(&mut value).unwrap();
if value.trim().parse::<u16>().is_err() {
return Err(InterpreterError::RuntimeError {
line_number: *key as u16,
message: String::from("Runtime Error message from READ"),
});
}
let real_value: u16 = value.trim().parse().expect("noo");
self.variables
.insert(String::from(key_for_insert), real_value);
}
"GOTO" => {
if self.realOrderedText.get(&chunks[1].parse::<u32>().unwrap()).is_none() {
return Err(InterpreterError::RuntimeError {
line_number: *key as u16,
message: String::from("Runtime Error message from GOTO"),
});
}
counter = chunks[1].parse().unwrap();
continue;
}
"PRINT" => {
let mut first_letter_is_uppercase = true;
let mut first_letter_is_numeric = true;
for c in chunks[1].chars() {
if c.is_uppercase() {
first_letter_is_uppercase = true;
} else {
first_letter_is_uppercase = false;
}
if c.is_numeric() {
first_letter_is_numeric = true;
} else {
first_letter_is_numeric = false;
}
break;
}
if first_letter_is_uppercase == false && first_letter_is_numeric == false {
self.output
.write_fmt(format_args!("{}", chunks[1]))
.unwrap();
self.output.write(b"\n").unwrap();
self.output.flush().unwrap();
} else {
let result = self.eval_value(chunks[1]);
match result {
Ok(u16) => {
self.output.write_fmt(format_args!("{}", u16)).unwrap();
self.output.write(b"\n").unwrap();
self.output.flush().unwrap();
}
Err(_returned_error) => {
return Err(InterpreterError::RuntimeError {
line_number: *key as u16,
message: String::from("Runtime Error message from PRINT"),
});
}
}
}
}
"IF" => {
// println!("HERE 1");
let val1 = self.eval_value(chunks[1]).ok().unwrap();
// println!("HERE 2");
let op = chunks[2];
let val2 = self.eval_value(chunks[3]).ok().unwrap();
// println!("HERE 3");
let goto = chunks[4];
// println!("{} THIS HERE IS GOTO", goto);
if !goto.eq("GOTO") {
return Err(InterpreterError::SyntaxError { code: String::from("isajdoiasj") })
}
let row_num = chunks[5];
match op {
">" => if val1 > val2 {
if self.realOrderedText.get(&chunks[5].parse::<u32>().unwrap()).is_none() {
return Err(InterpreterError::RuntimeError {
line_number: *key as u16,
message: String::from("Runtime Error message from GOTO IN IF"),
});
}
counter = row_num.parse().unwrap();
continue;
}
"<" => if val1 < val2 {
if self.realOrderedText.get(&chunks[5].parse::<u32>().unwrap()).is_none() {
return Err(InterpreterError::RuntimeError {
line_number: *key as u16,
message: String::from("Runtime Error message from GOTO IN IF"),
});
}
counter = row_num.parse().unwrap();
continue;
}
"=" => if val2 == val1 {
if self.realOrderedText.get(&chunks[5].parse::<u32>().unwrap()).is_none() {
return Err(InterpreterError::RuntimeError {
line_number: *key as u16,
message: String::from("Runtime Error message from GOTO IN IF"),
});
}
counter = row_num.parse().unwrap();
continue;
}
_ => {}
}
},
_ => { return Err(InterpreterError::RuntimeError {
line_number: *key as u16,
message: String::from("Runtime Error message from I DONT KNOW WHERE"),
})
}
}
}
counter += 1;
}
Ok(())
}
}
// fn main() {
// 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");
// }
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20230111-3772066-urmzjr/solution) warning: unused imports: `BufWriter`, `ErrorKind`, `fmt::Error`, `hash::Hash` --> src/lib.rs:3:5 | 3 | fmt::Error, | ^^^^^^^^^^ 4 | hash::Hash, | ^^^^^^^^^^ 5 | io::{self, BufRead, BufReader, BufWriter, ErrorKind, Read, Write}, | ^^^^^^^^^ ^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default warning: structure field `realOrderedText` should have a snake case name --> src/lib.rs:22:5 | 22 | realOrderedText: BTreeMap<u32, String>, | ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `real_ordered_text` | = note: `#[warn(non_snake_case)]` on by default warning: `solution` (lib) generated 2 warnings Finished test [unoptimized + debuginfo] target(s) in 1.56s 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_print ... ok test solution_test::test_basic_input ... 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 ... FAILED test solution_test::test_line_order_and_overwriting ... ok test solution_test::test_io_error_write ... FAILED test solution_test::test_print_cyrillic ... ok test solution_test::test_print_vars_and_strings ... ok test solution_test::test_syntax_errors_1 ... FAILED test solution_test::test_runtime_errors ... FAILED test solution_test::test_syntax_errors_2 ... ok failures: ---- solution_test::test_io_error_read stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "read error!" }', /tmp/d20230111-3772066-urmzjr/solution/src/lib.rs:216:58 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'solution_test::test_io_error_read' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "read error!" }', tests/solution_test.rs:344:5 ---- solution_test::test_io_error_write stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "Write Error!" }', /tmp/d20230111-3772066-urmzjr/solution/src/lib.rs:263:84 thread 'solution_test::test_io_error_write' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "Write Error!" }', tests/solution_test.rs:358:5 ---- solution_test::test_syntax_errors_1 stdout ---- thread '<unnamed>' panicked at 'Expression Ok(()) does not match the pattern "Err(InterpreterError::SyntaxError { .. })"', tests/solution_test.rs:267:9 thread 'solution_test::test_syntax_errors_1' panicked at 'Expression Ok(()) does not match the pattern "Err(InterpreterError::SyntaxError { .. })"', tests/solution_test.rs:254:5 ---- solution_test::test_runtime_errors stdout ---- thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', /tmp/d20230111-3772066-urmzjr/solution/src/lib.rs:281:68 thread 'solution_test::test_runtime_errors' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:301:5 failures: solution_test::test_io_error_read solution_test::test_io_error_write solution_test::test_runtime_errors solution_test::test_syntax_errors_1 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`
История (2 версии и 0 коментара)
Георги качи решение на 10.01.2023 16:45 (преди 5 месеца)
use std::{
collections::{BTreeMap, HashMap},
fmt::Error,
hash::Hash,
io::{self, BufRead, BufReader, BufWriter, ErrorKind, 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 анотация!
pub struct Interpreter<'a, R: Read, W: Write + 'a> {
// Тези полета не са публични, така че може да си ги промените, ако искате:
input: BufReader<R>,
output: &'a mut W,
realOrderedText: BTreeMap<u32, String>,
variables: HashMap<String, u16>, // inputBuf: BufReader<R>,
// outputBuf: BufWriter<&'a mut W>
// Каквито други полета ви трябват
}
impl<'a, R: Read, W: Write + 'a> Interpreter<'a, R, W> {
/// Конструира нов интерпретатор, който чете вход от `input` чрез `READ` командата и пише в
/// `output` чрез `PRINT`.
///
pub fn new(input: R, output: &'a mut W) -> Self {
let reader = BufReader::new(input);
let lines = BTreeMap::<u32, String>::new();
let variables = HashMap::<String, u16>::new();
// Interpreter { input: input, output: output, inputBuf: reader, outputBuf: writer }
Interpreter {
input: reader,
output: output,
realOrderedText: lines,
variables: variables,
}
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write + 'a> 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> {
let chunks: Vec<&str> = code.split(" ").collect();
if chunks.is_empty() || chunks.len() < 3 {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
let mut is_numeric: bool = true;
for c in chunks[0].chars() {
if !c.is_numeric() {
is_numeric = false;
}
}
if is_numeric == false {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
match chunks[1] {
"PRINT" => {
if chunks.len() > 3 {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
}
"READ" => {
if chunks.len() > 3 {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
let mut first_letter_is_uppercase = true;
for c in chunks[2].chars() {
if c.is_uppercase() {
first_letter_is_uppercase = true;
} else {
first_letter_is_uppercase = false;
}
break;
}
if first_letter_is_uppercase == false {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
}
"GOTO" => {
if chunks.len() > 3 {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
if chunks[2].parse::<u16>().is_err() {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
}
"IF" => {
if chunks.len() != 7 {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
}
_ => {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
})
}
}
// if !chunks[1].eq("PRINT") && !chunks[1].eq("READ") && !chunks[1].eq("GOTO") && !chunks[1].eq("IF") {
// return Err(InterpreterError::SyntaxError { code: String::from(code) })
// }
match code.split_once(' ') {
Some((num, rest)) => {
self.realOrderedText.insert(
num.parse().expect("first thing should be a number"),
String::from(rest),
);
}
None => {
return Err(InterpreterError::SyntaxError {
code: String::from(code),
});
}
}
return Ok(());
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write + 'a> 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> {
let mut first_letter_is_uppercase = true;
for c in value.chars() {
if c.is_uppercase() {
first_letter_is_uppercase = true;
} else {
first_letter_is_uppercase = false;
}
break;
}
if first_letter_is_uppercase {
match self.variables.get(value) {
Some(_u16) => Ok(*self.variables.get(value).unwrap()),
None => {
return Err(InterpreterError::UnknownVariable {
name: String::from(value),
})
}
}
} else {
if value.trim().parse::<u16>().is_ok() {
return Ok(value.trim().parse::<u16>().unwrap());
} else {
return Err(InterpreterError::NotANumber {
value: String::from(value),
});
}
}
}
}
impl<'a, R: Read, W: Write + 'a> Interpreter<'a, R, W> {
/// Функцията започва да изпълнява редовете на програмата в нарастващ ред. Ако стигне до GOTO,
/// скача на съответния ред и продължава от него в нарастващ ред. Когато вече няма ред с
/// по-голямо число, функцията свършва и връща `Ok(())`.
///
/// Вижте по-долу за отделните команди и какви грешки могат да върнат.
///
pub fn run(&mut self) -> Result<(), InterpreterError> {
let max = self.realOrderedText.keys().max().unwrap();
let mut counter: u32 = 0;
while counter <= *max {
if self.realOrderedText.get(&counter).is_none() {
counter += 1;
continue;
} else {
let value = self.realOrderedText.get(&counter).unwrap();
let key = &counter;
let chunks: Vec<&str> = value.split(" ").collect();
match chunks[0] {
"READ" => {
let key_for_insert = chunks[1];
let mut value = String::from("");
self.input.read_line(&mut value).unwrap();
if value.trim().parse::<u16>().is_err() {
return Err(InterpreterError::RuntimeError {
line_number: *key as u16,
message: String::from("Runtime Error message from READ"),
});
}
let real_value: u16 = value.trim().parse().expect("noo");
self.variables
.insert(String::from(key_for_insert), real_value);
}
"GOTO" => {
+ if self.realOrderedText.get(&chunks[1].parse::<u32>().unwrap()).is_none() {
+ return Err(InterpreterError::RuntimeError {
+ line_number: *key as u16,
+ message: String::from("Runtime Error message from GOTO"),
+ });
+ }
counter = chunks[1].parse().unwrap();
continue;
}
"PRINT" => {
let mut first_letter_is_uppercase = true;
let mut first_letter_is_numeric = true;
for c in chunks[1].chars() {
if c.is_uppercase() {
first_letter_is_uppercase = true;
} else {
first_letter_is_uppercase = false;
}
if c.is_numeric() {
first_letter_is_numeric = true;
} else {
first_letter_is_numeric = false;
}
break;
}
if first_letter_is_uppercase == false && first_letter_is_numeric == false {
self.output
.write_fmt(format_args!("{}", chunks[1]))
.unwrap();
self.output.write(b"\n").unwrap();
self.output.flush().unwrap();
} else {
let result = self.eval_value(chunks[1]);
match result {
Ok(u16) => {
self.output.write_fmt(format_args!("{}", u16)).unwrap();
self.output.write(b"\n").unwrap();
self.output.flush().unwrap();
}
Err(_returned_error) => {
return Err(InterpreterError::RuntimeError {
line_number: *key as u16,
message: String::from("Runtime Error message from PRINT"),
});
}
}
}
}
"IF" => {
// println!("HERE 1");
let val1 = self.eval_value(chunks[1]).ok().unwrap();
// println!("HERE 2");
let op = chunks[2];
let val2 = self.eval_value(chunks[3]).ok().unwrap();
// println!("HERE 3");
let goto = chunks[4];
// println!("{} THIS HERE IS GOTO", goto);
if !goto.eq("GOTO") {
return Err(InterpreterError::SyntaxError { code: String::from("isajdoiasj") })
}
let row_num = chunks[5];
match op {
">" => if val1 > val2 {
+ if self.realOrderedText.get(&chunks[5].parse::<u32>().unwrap()).is_none() {
+ return Err(InterpreterError::RuntimeError {
+ line_number: *key as u16,
+ message: String::from("Runtime Error message from GOTO IN IF"),
+ });
+ }
counter = row_num.parse().unwrap();
continue;
}
"<" => if val1 < val2 {
+ if self.realOrderedText.get(&chunks[5].parse::<u32>().unwrap()).is_none() {
+ return Err(InterpreterError::RuntimeError {
+ line_number: *key as u16,
+ message: String::from("Runtime Error message from GOTO IN IF"),
+ });
+ }
counter = row_num.parse().unwrap();
continue;
}
"=" => if val2 == val1 {
+ if self.realOrderedText.get(&chunks[5].parse::<u32>().unwrap()).is_none() {
+ return Err(InterpreterError::RuntimeError {
+ line_number: *key as u16,
+ message: String::from("Runtime Error message from GOTO IN IF"),
+ });
+ }
counter = row_num.parse().unwrap();
continue;
}
_ => {}
}
},
_ => { return Err(InterpreterError::RuntimeError {
line_number: *key as u16,
message: String::from("Runtime Error message from I DONT KNOW WHERE"),
})
}
}
}
counter += 1;
}
Ok(())
}
}
// fn main() {
// 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");
// }