Решение на Basic BASIC от Берна Ахад
Резултати
- 20 точки от тестове
- 0 бонус точки
- 20 точки общо
- 15 успешни тест(а)
- 0 неуспешни тест(а)
Код
use std::{
collections::{BTreeMap, 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 },
}
pub struct Interpreter<'a, R: Read, W: Write> {
input: BufReader<R>,
output: &'a mut W,
vars: HashMap<String, u16>,
lines: BTreeMap<u16, String>,
}
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
pub fn new(input: R, output: &'a mut W) -> Self {
Self {
input: BufReader::new(input),
output: output,
vars: HashMap::new(),
lines: BTreeMap::new(),
}
}
}
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
pub fn add(&mut self, code: &str) -> Result<(), InterpreterError> {
let commands = code.split_whitespace().collect::<Vec<&str>>();
if commands.len() < 3 {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
} else if commands.len() == 3 {
if !commands[0].parse::<u16>().is_ok() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
if commands[1] == "READ" {
if !commands[2].chars().next().unwrap().is_uppercase() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
self.lines
.insert(commands[0].parse::<u16>().unwrap(), code.to_string());
return Ok(());
} else if commands[1] == "PRINT" {
self.lines
.insert(commands[0].parse::<u16>().unwrap(), code.to_string());
return Ok(());
} else if commands[1] == "GOTO" {
if !commands[2].parse::<u16>().is_ok() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
self.lines
.insert(commands[0].parse::<u16>().unwrap(), code.to_string());
return Ok(());
} else {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
} else if commands.len() == 7 {
if !commands[0].parse::<u16>().is_ok() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
if commands[1] != "IF" {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
if commands[5] != "GOTO" {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
} else if !commands[6].parse::<u16>().is_ok() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
self.lines
.insert(commands[0].parse::<u16>().unwrap(), code.to_string());
return Ok(());
} else {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
}
}
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
pub fn eval_value(&self, value: &str) -> Result<u16, InterpreterError> {
if value.chars().next().unwrap().is_uppercase() {
if self.vars.contains_key(value) {
return Ok(self.vars[value]);
}
return Err(InterpreterError::UnknownVariable {
name: value.to_string(),
});
}
let parsed = value.parse::<u16>();
if parsed.is_ok() {
return Ok(parsed.unwrap());
} else {
return Err(InterpreterError::NotANumber {
value: value.to_string(),
});
}
}
}
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
fn iterate(&mut self, min_key: u16) -> Result<(), InterpreterError> {
let keys: Vec<u16> = self.lines.keys().cloned().collect();
for key in keys {
if key < min_key {
continue;
}
let value = self.lines.get(&key).unwrap();
let commands = value.split_whitespace().collect::<Vec<&str>>();
if commands[1] == "PRINT" {
let res = self.eval_value(commands[2]);
if res.is_ok() {
let res2 = res.unwrap();
let write = writeln!(self.output, "{}", res2);
if write.is_ok() {
write.unwrap()
} else {
return Err(InterpreterError::IoError(write.unwrap_err()));
}
} else {
match res.err() {
Some(InterpreterError::UnknownVariable { name }) => {
print!("{}", name);
return Err(InterpreterError::RuntimeError {
line_number: key,
message: "Not found!".to_string(),
});
}
_ => {
let write = writeln!(self.output, "{}", commands[2]);
if write.is_ok() {
write.unwrap()
} else {
return Err(InterpreterError::IoError(write.unwrap_err()));
}
}
}
}
} else if commands[1] == "READ" {
let mut input = String::new();
let read = self.input.read_line(&mut input);
if read.is_ok() {
if input.is_empty() {
return Err(InterpreterError::RuntimeError {
line_number: key,
message: "Empty input!".to_string(),
});
}
read.unwrap();
} else {
return Err(InterpreterError::IoError(read.unwrap_err()));
}
let value = input.trim_end_matches('\n');
if value.parse::<u16>().is_ok() {
self.vars
.insert(commands[2].to_string(), value.parse::<u16>().unwrap());
} else {
return Err(InterpreterError::RuntimeError {
line_number: key,
message: "Can read and save only u16".to_string(),
});
}
} else if commands[1] == "GOTO" {
if !self
.lines
.contains_key(&commands[2].parse::<u16>().unwrap())
{
return Err(InterpreterError::RuntimeError {
line_number: key,
message: "Line not found!".to_string(),
});
}
return self.iterate(commands[2].parse::<u16>().unwrap());
} else if commands[1] == "IF" {
let val1 = self.eval_value(commands[2]);
if val1.is_err() {
return Err(InterpreterError::RuntimeError {
line_number: key,
message: "Wrong statement value".to_string(),
});
}
let val2 = self.eval_value(commands[4]);
if val2.is_err() {
return Err(InterpreterError::RuntimeError {
line_number: key,
message: "Wrong statement value".to_string(),
});
}
let result = match commands[3] {
"<" => val1.unwrap() < val2.unwrap(),
">" => val1.unwrap() > val2.unwrap(),
"=" => val1.unwrap() == val2.unwrap(),
_ => {
return Err(InterpreterError::RuntimeError {
line_number: key,
message: "Wrong operation!".to_string(),
});
}
};
if result {
if !self
.lines
.contains_key(&commands[6].parse::<u16>().unwrap())
{
return Err(InterpreterError::RuntimeError {
line_number: key,
message: "Line not found!".to_string(),
});
}
return self.iterate(commands[6].parse::<u16>().unwrap());
}
}
}
return Ok(());
}
pub fn run(&mut self) -> Result<(), InterpreterError> {
let keys: Vec<u16> = self.lines.keys().cloned().collect();
for key in keys {
let res = self.iterate(key);
if res.is_ok() {
return Ok(());
}
return Err(res.unwrap_err());
}
return Ok(());
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20230111-3772066-qa1thg/solution) Finished test [unoptimized + debuginfo] target(s) in 1.81s 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_erroring_goto ... ok test solution_test::test_basic_read ... ok test solution_test::test_io_error_read ... ok test solution_test::test_io_error_write ... ok test solution_test::test_full_program ... ok test solution_test::test_line_order_and_overwriting ... ok test solution_test::test_print_cyrillic ... ok test solution_test::test_print_vars_and_strings ... ok test solution_test::test_syntax_errors_1 ... ok test solution_test::test_runtime_errors ... ok test solution_test::test_syntax_errors_2 ... ok test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s