Решение на Basic BASIC от Николай Рангелов
Към профила на Николай Рангелов
Резултати
- 13 точки от тестове
- 0 бонус точки
- 13 точки общо
- 10 успешни тест(а)
- 5 неуспешни тест(а)
Код
use std::io::{self, Write, Read, BufRead};
use std::io::BufReader;
use std::collections::BTreeMap;
use std::num::ParseIntError;
#[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: std::io::Read, W: Write> {
input: BufReader<R>,
output: &'a mut W,
lines: BTreeMap<u16, String>,
variables: BTreeMap<String, u16>,
}
impl<'a, R: std::io::Read, W: Write> Interpreter<'a, R, W> {
pub fn new(_input: R, output: &'a mut W) -> Self {
return Self {input: BufReader::new(_input), output: output, lines: BTreeMap::new(), variables: BTreeMap::new()};
}
pub fn add(&mut self, code: &str) -> Result<(), InterpreterError> {
let commands: Vec<&str> = code.split(" ").collect::<Vec<&str>>();
if commands.len() < 3 {
return Err(InterpreterError::SyntaxError { code: code.to_string() });
}
let line_parsing_result: Result<u16, ParseIntError> = commands[0].parse::<u16>();
let line: u16 = match line_parsing_result {
Ok(number) => number,
Err(_) => return Err(InterpreterError::SyntaxError { code: code.to_string() }),
};
self.lines.insert(line, code.to_string());
return Ok(());
}
pub fn eval_value(&self, value: &str) -> Result<u16, InterpreterError> {
let value_parsing_result: Result<u16, ParseIntError> = value.parse::<u16>();
if char::is_uppercase(value.chars().nth(0).unwrap()) {
if self.variables.contains_key(value) {
return Ok(self.variables[value]);
} else {
return Err(InterpreterError::UnknownVariable { name: value.to_string() });
}
}
match value_parsing_result {
Ok(number) => return Ok(number),
_ => 1,
};
return Err(InterpreterError::NotANumber { value: value.to_string() });
}
pub fn run(&mut self) -> Result<(), InterpreterError> {
let k : Vec<&u16> = self.lines.keys().collect();
let mut i = 0;
while i < k.len() {
let line = *k.get(i).unwrap();
let instruction = self.lines.get(line).unwrap();
let commands: Vec<&str> = instruction.split(" ").collect::<Vec<&str>>();
let command: &str = commands[1];
if command == "PRINT" {
let mut to_be_printed: u16 = 0;
let newline: String = "\n".to_string();
if char::is_uppercase(commands[2].chars().nth(0).unwrap()) {
match self.eval_value(commands[2]) {
Ok(n) => to_be_printed = n,
Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}),
};
self.output.write(to_be_printed.to_string().as_bytes());
self.output.write(newline.as_bytes());
} else {
self.output.write(commands[2].as_bytes());
self.output.write(newline.as_bytes());
}
}
if command == "READ" {
if char::is_uppercase(commands[2].chars().nth(0).unwrap()) {
let mut buf = String::new();
self.input.read_line(&mut buf);
buf.pop();
let new_int = match buf.parse::<u16>() {
Ok(n) => n,
Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}),
};
self.variables.insert(commands[2].to_string(), new_int);
} else {
return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()});
}
}
let mut shouldimove = true;
if command == "GOTO" {
let new_int = match commands[2].parse::<u16>() {
Ok(n) => n,
Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}),
};
let index = k.iter().position(|&r| *r == new_int);
if index == None {
return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string() });
}
i = index.unwrap();
shouldimove = false;
}
if command == "IF" {
if (commands[3] == "<") {
if self.eval_value(commands[2]).unwrap() < self.eval_value(commands[4]).unwrap() {
let new_int = match commands[6].parse::<u16>() {
Ok(n) => n,
Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}),
};
let index = k.iter().position(|&r| *r == new_int);
if index == None {
return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string() });
}
i = index.unwrap();
shouldimove = false;
}
}
if (commands[3] == ">") {
if self.eval_value(commands[2]).unwrap() > self.eval_value(commands[4]).unwrap() {
let new_int = match commands[6].parse::<u16>() {
Ok(n) => n,
Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}),
};
let index = k.iter().position(|&r| *r == new_int);
if index == None {
return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string() });
}
i = index.unwrap();
shouldimove = false;
}
}
if (commands[3] == "=") {
if self.eval_value(commands[2]).unwrap() == self.eval_value(commands[4]).unwrap() {
let new_int = match commands[6].parse::<u16>() {
Ok(n) => n,
Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}),
};
let index = k.iter().position(|&r| *r == new_int);
if index == None {
return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string() });
}
i = index.unwrap();
shouldimove = false;
}
}
}
if shouldimove {
i += 1;
}
}
return Ok(());
}
}
fn main() {
let stdin = std::io::stdin();
let mut stdout = std::io::stdout();
let mut interpreter = Interpreter::new(stdin, &mut stdout);
interpreter.add("10 PRINT guess_a_number").unwrap();
interpreter.add("20 READ Guess").unwrap();
interpreter.add("30 IF Guess > 42 GOTO 100").unwrap();
interpreter.add("40 IF Guess < 42 GOTO 200").unwrap();
interpreter.add("50 IF Guess = 42 GOTO 300").unwrap();
interpreter.add("100 PRINT too_high").unwrap();
interpreter.add("110 GOTO 10").unwrap();
interpreter.add("200 PRINT too_low").unwrap();
interpreter.add("210 GOTO 10").unwrap();
interpreter.add("300 PRINT you_got_it!").unwrap();
interpreter.run().unwrap();
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20230111-3772066-loj1rw/solution) warning: unused import: `Read` --> src/lib.rs:1:28 | 1 | use std::io::{self, Write, Read, BufRead}; | ^^^^ | = note: `#[warn(unused_imports)]` on by default warning: unnecessary parentheses around `if` condition --> src/lib.rs:114:20 | 114 | if (commands[3] == "<") { | ^ ^ | = note: `#[warn(unused_parens)]` on by default help: remove these parentheses | 114 - if (commands[3] == "<") { 114 + if commands[3] == "<" { | warning: unnecessary parentheses around `if` condition --> src/lib.rs:128:20 | 128 | if (commands[3] == ">") { | ^ ^ | help: remove these parentheses | 128 - if (commands[3] == ">") { 128 + if commands[3] == ">" { | warning: unnecessary parentheses around `if` condition --> src/lib.rs:142:20 | 142 | if (commands[3] == "=") { | ^ ^ | help: remove these parentheses | 142 - if (commands[3] == "=") { 142 + if commands[3] == "=" { | warning: value assigned to `to_be_printed` is never read --> src/lib.rs:69:25 | 69 | let mut to_be_printed: u16 = 0; | ^^^^^^^^^^^^^ | = help: maybe it is overwritten before being read? = note: `#[warn(unused_assignments)]` on by default warning: unused variable: `e` --> src/lib.rs:75:29 | 75 | Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}), | ^ help: if this is intentional, prefix it with an underscore: `_e` | = note: `#[warn(unused_variables)]` on by default warning: unused variable: `e` --> src/lib.rs:93:29 | 93 | Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}), | ^ help: if this is intentional, prefix it with an underscore: `_e` warning: unused variable: `e` --> src/lib.rs:104:25 | 104 | Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}), | ^ help: if this is intentional, prefix it with an underscore: `_e` warning: unused variable: `e` --> src/lib.rs:118:33 | 118 | ... Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}), | ^ help: if this is intentional, prefix it with an underscore: `_e` warning: unused variable: `e` --> src/lib.rs:132:33 | 132 | ... Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}), | ^ help: if this is intentional, prefix it with an underscore: `_e` warning: unused variable: `e` --> src/lib.rs:146:33 | 146 | ... Err(e) => return Err(InterpreterError::RuntimeError { line_number: *line, message: instruction.to_string()}), | ^ help: if this is intentional, prefix it with an underscore: `_e` warning: function `main` is never used --> src/lib.rs:165:4 | 165 | fn main() { | ^^^^ | = note: `#[warn(dead_code)]` on by default warning: unused `Result` that must be used --> src/lib.rs:77:21 | 77 | self.output.write(to_be_printed.to_string().as_bytes()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled = note: `#[warn(unused_must_use)]` on by default warning: unused `Result` that must be used --> src/lib.rs:78:21 | 78 | self.output.write(newline.as_bytes()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled warning: unused `Result` that must be used --> src/lib.rs:80:21 | 80 | self.output.write(commands[2].as_bytes()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled warning: unused `Result` that must be used --> src/lib.rs:81:21 | 81 | self.output.write(newline.as_bytes()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled warning: unused `Result` that must be used --> src/lib.rs:89:21 | 89 | self.input.read_line(&mut buf); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled warning: `solution` (lib) generated 17 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_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 ... FAILED test solution_test::test_io_error_write ... FAILED 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 ... FAILED test solution_test::test_runtime_errors ... FAILED test solution_test::test_syntax_errors_2 ... FAILED failures: ---- solution_test::test_io_error_read stdout ---- thread '<unnamed>' panicked at 'Expression Err(RuntimeError { line_number: 20, message: "20 READ Oh_No" }) does not match the pattern "Err(InterpreterError::IoError(_))"', tests/solution_test.rs:352:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'solution_test::test_io_error_read' panicked at 'Expression Err(RuntimeError { line_number: 20, message: "20 READ Oh_No" }) does not match the pattern "Err(InterpreterError::IoError(_))"', tests/solution_test.rs:344:5 ---- solution_test::test_io_error_write stdout ---- thread '<unnamed>' panicked at 'Expression Ok(()) does not match the pattern "Err(InterpreterError::IoError(_))"', tests/solution_test.rs:366:9 thread 'solution_test::test_io_error_write' panicked at 'Expression Ok(()) does not match the pattern "Err(InterpreterError::IoError(_))"', 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:259: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 `Result::unwrap()` on an `Err` value: UnknownVariable { name: "C" }', /tmp/d20230111-3772066-loj1rw/solution/src/lib.rs:143:94 thread 'solution_test::test_runtime_errors' panicked at 'called `Result::unwrap()` on an `Err` value: UnknownVariable { name: "C" }', tests/solution_test.rs:301:5 ---- solution_test::test_syntax_errors_2 stdout ---- thread '<unnamed>' panicked at 'Expression Ok(()) does not match the pattern "Err(InterpreterError::SyntaxError { .. })"', tests/solution_test.rs:280:9 thread 'solution_test::test_syntax_errors_2' panicked at 'Expression Ok(()) does not match the pattern "Err(InterpreterError::SyntaxError { .. })"', tests/solution_test.rs:275: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 solution_test::test_syntax_errors_2 test result: FAILED. 10 passed; 5 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass `--test solution_test`