Решение на Basic BASIC от Мирела Граматикова

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

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

Резултати

  • 16 точки от тестове
  • 0 бонус точки
  • 16 точки общо
  • 12 успешни тест(а)
  • 3 неуспешни тест(а)

Код

use std::{io::{self, Write, Read, BufReader, BufRead}, collections::HashMap};
#[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,
code: Vec<(u16, Vec<String>)>,
vars: HashMap<String, u16>
}
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
pub fn new(input: R, output: &'a mut W) -> Self {
Interpreter {
input: BufReader::new(input),
output: output,
code: Vec::new(),
vars: HashMap::new()
}
}
pub fn add(&mut self, code: &str) -> Result<(), InterpreterError> {
let mut code_no_whitespaces = code.split_whitespace();
let mut str = code_no_whitespaces.next();
let line_num: u16;
let mut line_code: Vec<String> = Vec::new();
// Line number
if let Some(str_num) = str {
if let Ok(number) = str_num.to_string().parse::<u16>() {
line_num = number;
} else {
return Err(InterpreterError::SyntaxError { code: String::from(code) })
}
} else {
return Err(InterpreterError::SyntaxError { code: String::from(code) })
}
// Command
str = code_no_whitespaces.next();
if let Some(str_command) = str {
if str_command != "PRINT" && str_command != "READ" &&
str_command != "GOTO" && str_command != "IF" {
return Err(InterpreterError::SyntaxError { code: String::from(code) })
} else {
line_code.push(str_command.to_string());
}
} else {
return Err(InterpreterError::SyntaxError { code: String::from(code) })
}
// Get second argument of command
str = code_no_whitespaces.next();
if let Some(str_part_of_command) = str {
if line_code[0] == "READ" {
// Check for uppercase
if str_part_of_command.chars().next().unwrap().is_uppercase() {
line_code.push(str_part_of_command.to_string());
}
} else if line_code[0] == "GOTO" {
// Try to parse to u16, since GOTO must take valid u16.
if let Ok(_) = str_part_of_command.to_string().parse::<u16>() {
line_code.push(str_part_of_command.to_string());
} else {
return Err(InterpreterError::SyntaxError { code: String::from(code) })
}
} else {
line_code.push(str_part_of_command.to_string());
}
} else {
return Err(InterpreterError::SyntaxError { code: String::from(code) })
}
for remaining in code_no_whitespaces {
line_code.push(remaining.to_string());
}
if line_code.len() > 2 && line_code[0] != "IF" {
return Err(InterpreterError::SyntaxError { code: String::from(code) })
}
if line_code.len() != 2 && line_code[0] != "IF" {
return Err(InterpreterError::SyntaxError { code: String::from(code) })
}
// Extra validation for IF
if line_code[0] == "IF" {
if line_code.len() != 6 {
return Err(InterpreterError::SyntaxError { code: String::from(code) })
}
if let Err(_) = line_code[5].parse::<u16>() {
return Err(InterpreterError::SyntaxError { code: String::from(code) })
}
if line_code[4] != "GOTO" {
return Err(InterpreterError::SyntaxError { code: String::from(code) })
}
}
// Find if such a line already exists, so we can clobber it.
let index = self.code.clone().iter().position(|a| a.0 == line_num && a.1 == line_code);
if let Some(i) = index {
let element_ref = self.code.get_mut(i).unwrap();
*element_ref = (line_num, line_code);
} else {
self.code.push((line_num, line_code));
}
Ok(())
}
pub fn eval_value(&self, value: &str) -> Result<u16, InterpreterError> {
if value.chars().next().unwrap().is_uppercase() {
if self.vars.contains_key(value) {
Ok(*self.vars.get(value).unwrap())
} else {
Err(InterpreterError::UnknownVariable { name: String::from(value) })
}
} else {
if let Ok(number) = value.parse::<u16>() {
Ok(number)
} else {
Err(InterpreterError::NotANumber { value: value.to_string() })
}
}
}
fn read_c(&mut self, line_number: u16, code: &Vec<String>) -> Result<(), InterpreterError> {
if code[1].chars().next().unwrap().is_uppercase() {
let mut line = String::new();
let line_len = self.input.read_line(&mut line);
if let Err(err) = line_len {
return Err(InterpreterError::IoError(err))
}
// Remove the newline before parsing
let mut tmp = line.chars();
tmp.next_back();
line = tmp.as_str().to_string();
if let Ok(number) = line.parse::<u16>() {
self.vars.insert(code[1].clone(), number);
return Ok(())
} else {
return Err(InterpreterError::RuntimeError { line_number: line_number, message: "Input given to READ command should be a valid u16".to_string() })
}
} else {
return Err(InterpreterError::RuntimeError { line_number: line_number, message: "Invalid variable name. Variables should start with an uppercased letter".to_string() })
}
}
fn print_c(&mut self, code: &Vec<String>) -> Result<(), InterpreterError> {
if code[1].chars().next().unwrap().is_uppercase() {
match self.eval_value(code[1].as_str()) {
Ok(number) => {
if let Err(err) = self.output.write_fmt(format_args!("{}\n", number)) {
return Err(InterpreterError::IoError(err))
} else {
return Ok(())
}
},
Err(err) => return Err(err)
}
} else {
// Here we don't care if it's a number or a string starting with a lowercase letter
// we print it all the same.
if let Err(err) = self.output.write_fmt(format_args!("{}\n", code[1])) {
return Err(InterpreterError::IoError(err))
}
Ok(())
}
}
fn goto_c(&mut self, line_number: u16, code: &Vec<String>, result: &mut u16) -> Result<(), InterpreterError> {
if let Ok(number) = code[1].parse::<u16>() {
for (key, _) in self.code.clone() {
if key == number {
*result = number;
return Ok(())
}
}
Err(InterpreterError::RuntimeError { line_number: line_number, message: "Invalid line number passed to GOTO".to_string() })
} else {
Err(InterpreterError::RuntimeError { line_number: line_number, message: "Non u16 value passed to GOTO".to_string() })
}
}
fn if_c(&mut self, line_number: u16, code: &Vec<String>, result: &mut u16) -> Result<bool, InterpreterError> {
let first = self.eval_value(code[1].as_str()).unwrap();
let second = self.eval_value(code[3].as_str()).unwrap();
if code[2] == "==" {
if first == second {
let tmp = code.as_slice()[code.len()-2..].to_vec();
self.goto_c(line_number, &tmp, result)?;
return Ok(true);
}
return Ok(false)
} else if code[2] == "<" {
if first < second {
let tmp = code.as_slice()[code.len()-2..].to_vec();
self.goto_c(line_number, &tmp, result)?;
return Ok(true);
}
return Ok(false)
} else if code[2] == ">" {
if first > second {
let tmp = code.as_slice()[code.len()-2..].to_vec();
self.goto_c(line_number, &tmp, result)?;
return Ok(true);
}
return Ok(false)
} else {
return Err(InterpreterError::RuntimeError { line_number: line_number, message: "Unknown operator".to_string() })
}
}
pub fn run(&mut self) -> Result<(), InterpreterError> {
self.code.sort_by_key(|tup| tup.0);
let tmp_vec = self.code.clone();
let mut current_code_line_index = 0;
loop {
for (line_number, code) in tmp_vec.iter().skip(current_code_line_index) {
if code[0] == "READ" {
self.read_c(*line_number, code)?;
current_code_line_index += 1;
continue;
}
if code[0] == "PRINT" {
self.print_c(code)?;
current_code_line_index += 1;
continue;
}
if code[0] == "GOTO" {
let mut result: u16 = 0;
self.goto_c(*line_number, code, &mut result)?;
for (index, value) in tmp_vec.iter().enumerate() {
if value.0 == result {
current_code_line_index = index;
break;
}
}
break;
}
if code[0] == "IF" {
let mut result: u16 = 0;
let should_goto = self.if_c(*line_number, code, &mut result)?;
if should_goto {
for (index, value) in tmp_vec.iter().enumerate() {
if value.0 == result {
current_code_line_index = index;
break;
}
}
break;
} else {
current_code_line_index += 1;
}
}
}
if current_code_line_index == self.code.len() {
break;
}
}
Ok(())
}
}

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

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

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

failures:

---- solution_test::test_full_program stdout ----
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: RuntimeError { line_number: 40, message: "Unknown operator" }', tests/solution_test.rs:245:31
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'solution_test::test_full_program' panicked at 'called `Result::unwrap()` on an `Err` value: RuntimeError { line_number: 40, message: "Unknown operator" }', tests/solution_test.rs:230:5

---- solution_test::test_line_order_and_overwriting stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"1\n2\n3\n4\n"`,
 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: `"1\n2\n3\n4\n"`,
 right: `"1\n2\n4\n"`', tests/solution_test.rs:84:5

---- solution_test::test_runtime_errors stdout ----
thread '<unnamed>' panicked at 'Expression Err(UnknownVariable { name: "A" }) does not match the pattern "Err(InterpreterError::RuntimeError { line_number: 11, .. })"', tests/solution_test.rs:307:9
thread 'solution_test::test_runtime_errors' panicked at 'Expression Err(UnknownVariable { name: "A" }) does not match the pattern "Err(InterpreterError::RuntimeError { line_number: 11, .. })"', tests/solution_test.rs:301:5


failures:
    solution_test::test_full_program
    solution_test::test_line_order_and_overwriting
    solution_test::test_runtime_errors

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

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

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

Мирела качи първо решение на 09.01.2023 20:54 (преди над 2 години)