Решение на Basic BASIC от Билян Хаджи

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

Към профила на Билян Хаджи

Резултати

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

Код

use std::io::{self, Write, Read};
use std::collections::{BTreeMap, HashMap};
use std::io::{BufRead, BufReader, BufWriter};
use std::str::FromStr;
use std::collections::hash_map::Entry::Vacant;
use std::collections::hash_map::Entry::Occupied;
use std::collections::btree_map::Entry::Vacant as BTreeEntryVacant;
use std::collections::btree_map::Entry::Vacant as BTreeEntryOccupied;
// Така и не ми стигна време да направя run(), така че самата програма няма да работи :(
// Опитах се да разделя валидацията от самото изпълнение вместо да са на едно място,
// но просто накрая всичко стана мазало.
#[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, PartialEq, Clone)]
enum InterpreterCommand {
Print { value: String },
Read { variable_name: String },
Goto { line_number: String },
IfGoto { condition: String, line_number: String }
}
impl FromStr for InterpreterCommand {
type Err = ();
fn from_str(input: &str) -> Result<InterpreterCommand, Self::Err> {
let tokens = input.split_whitespace().collect::<Vec<_>>();
if tokens.len() < 2 {
return Err(());
}
let command = tokens[0];
match command {
"PRINT" => {
if tokens.len() != 2 {
Err(())
} else {
Ok(InterpreterCommand::Print { value: tokens[1].to_string() })
}
},
"READ" => {
if tokens.len() != 2 {
Err(())
} else {
Ok(InterpreterCommand::Read { variable_name: tokens[1].to_string() })
}
},
"GOTO" => {
if tokens.len() != 2 {
Err(())
} else {
Ok(InterpreterCommand::Goto { line_number: tokens[1].to_string() })
}
},
"IF" => {
if tokens.len() != 6 {
Err(())
} else {
if tokens[4] != "GOTO" {
return Err(());
}
let condition = &tokens[1..4].to_vec().join(" ");
Ok(InterpreterCommand::IfGoto { condition: condition.to_string(), line_number: tokens[5].to_string() })
}
},
_ => Err(())
}
}
}
// ВАЖНО: тук ще трябва да се добави lifetime анотация!
pub struct Interpreter<'a, R: Read, W: Write> {
input: BufReader<R>,
output: BufWriter<&'a mut W>,
// Каквито полета ви трябват
lines: BTreeMap<u16, InterpreterCommand>,
variables: HashMap<String, u16>,
}
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Конструира нов интерпретатор, който чете вход от `input` чрез `READ` командата и пише в
/// `output` чрез `PRINT`.
///
pub fn new(input: R, output: &'a mut W) -> Self {
let lines = BTreeMap::new();
let buf_input = BufReader::new(input);
let buf_output = BufWriter::new(output);
let variables = HashMap::new();
Interpreter {
input: buf_input,
output: buf_output,
lines,
variables
}
}
}
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
fn validate_command(&self, command: InterpreterCommand, code: String) -> Result<(), InterpreterError> {
match command {
InterpreterCommand::Print{ value } => {
let first_char = value.chars().nth(0).expect("At least one char");
if !first_char.is_alphanumeric() {
return Err(InterpreterError::SyntaxError{ code: code.to_string() } );
}
if first_char.is_numeric() {
match value.to_string().parse::<u16>() {
Err(_) => Err(InterpreterError::NotANumber{ value: value.to_string() }),
Ok(_) => Ok(())
}
} else {
Ok(())
}
},
InterpreterCommand::Read { variable_name } => {
let first_char = variable_name.chars().nth(0).expect("At least one char");
if !first_char.is_uppercase() {
Err(InterpreterError::SyntaxError{ code })
} else {
Ok(())
}
},
InterpreterCommand::Goto { line_number } => {
match line_number.parse::<u16>() {
Err(_) => Err(InterpreterError::SyntaxError{ code }),
Ok(_) => Ok(())
}
},
InterpreterCommand::IfGoto { condition, line_number } => {
match line_number.parse::<u16>() {
Err(_) => Err(InterpreterError::SyntaxError{ code }),
Ok(_) => Ok(())
}
}
}
}
}
// Не забравяйте 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> {
let tokens = code.split_whitespace().collect::<Vec<_>>();
if tokens.len() < 1 {
return Err(InterpreterError::SyntaxError { code: code.to_string() });
}
let first_token = tokens[0].parse::<u16>();
match first_token {
Ok(line_number) => {
if tokens.len() == 1 {
return Ok(());
}
let command_res = tokens[1..].to_vec().join(" ").parse::<InterpreterCommand>();
match command_res {
Err(_) => Err(InterpreterError::SyntaxError { code: code.to_string() }),
Ok(command) => {
let validation_result = self.validate_command(command.clone(), code.to_string());
match validation_result {
Err(err) => Err(err),
Ok(_) => {
self.lines.insert(line_number, command.clone());
Ok(())
}
}
}
}
},
Err(_) => Err(InterpreterError::SyntaxError { code: code.to_string() })
}
}
}
// Не забравяйте 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().nth(0).expect("At least one char").is_uppercase() {
match self.variables.get(value) {
None => Err(InterpreterError::UnknownVariable{ name: value.to_string() }),
Some(&result) => Ok(result.clone())
}
} else {
match value.to_string().parse::<u16>() {
Err(_) => Err(InterpreterError::NotANumber{ value: value.to_string() }),
Ok(result) => Ok(result)
}
}
}
}
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
fn generate_order_of_execution(&mut self) -> BTreeMap<&u16, usize> {
self.lines.keys().enumerate().map(|(i, j)| (j, i)).into_iter().collect()
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Функцията започва да изпълнява редовете на програмата в нарастващ ред. Ако стигне до GOTO,
/// скача на съответния ред и продължава от него в нарастващ ред. Когато вече няма ред с
/// по-голямо число, функцията свършва и връща `Ok(())`.
///
/// Вижте по-долу за отделните команди и какви грешки могат да върнат.
///
pub fn run(&mut self) -> Result<(), InterpreterError> {
if self.lines.len() < 1 {
return Ok(());
}
let order_of_execution = self.generate_order_of_execution();
let mut prev_line = 0;
while let Some(current_line_entry) = self.lines.range(prev_line..).next() {
let current_command = self.lines.get(current_line_entry.0).unwrap();
match current_command {
InterpreterCommand::Print { value } => {
let first_char = value.chars().nth(0).expect("At least one char");
if first_char.is_lowercase() {
self.output.write(value.as_bytes());
} else {
match self.eval_value(value) {
Err(err) => return Err(err),
Ok(result) => {
self.output.write(result.to_string().as_bytes());
return Ok(());
}
}
}
}
InterpreterCommand::Read { variable_name } => {
todo!()
// let first_char = variable_name.chars().nth(0).expect("At least one char");
// let variable_value_res = self.input.lines().next().unwrap().unwrap().clone().parse::<u16>();
// match variable_value_res {
// Err(_) => return Err(InterpreterError::RuntimeError{ line_number: current_line_entry.0.clone(), message: "Cannot read value".to_string() }),
// Ok(variable_value) => {
// self.variables.insert(variable_name.clone(), variable_value);
// return Ok(());
// }
// }
}
InterpreterCommand::Goto { line_number } => {
let line_number_val = line_number.parse::<u16>().unwrap();
todo!()
// match self.lines.entry(line_number_val) {
// BTreeEntryVacant(_) => return Err(InterpreterError::RuntimeError{ line_number: line_number_val, message: "Line not found".to_string() }),
// BTreeEntryOccupied(mut entry) => {
// prev_line = self.lines.range(..line_number_val).next_back().unwrap().0.clone();
// return Ok(());
// },
// }
}
InterpreterCommand::IfGoto { condition, line_number } => {
todo!()
}
}
}
Ok(())
}
}

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

Compiling solution v0.1.0 (/tmp/d20230111-3772066-1jtd67l/solution)
warning: unused import: `std::collections::hash_map::Entry::Vacant`
 --> src/lib.rs:5:5
  |
5 | use std::collections::hash_map::Entry::Vacant;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused import: `std::collections::hash_map::Entry::Occupied`
 --> src/lib.rs:6:5
  |
6 | use std::collections::hash_map::Entry::Occupied;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: unused import: `std::collections::btree_map::Entry::Vacant as BTreeEntryVacant`
 --> src/lib.rs:7:5
  |
7 | use std::collections::btree_map::Entry::Vacant as BTreeEntryVacant;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: unused import: `std::collections::btree_map::Entry::Vacant as BTreeEntryOccupied`
 --> src/lib.rs:8:5
  |
8 | use std::collections::btree_map::Entry::Vacant as BTreeEntryOccupied;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: unused import: `BufRead`
 --> src/lib.rs:3:15
  |
3 | use std::io::{BufRead, BufReader, BufWriter};
  |               ^^^^^^^

warning: unused variable: `condition`
   --> src/lib.rs:141:42
    |
141 |             InterpreterCommand::IfGoto { condition, line_number } => {
    |                                          ^^^^^^^^^ help: try ignoring the field: `condition: _`
    |
    = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `order_of_execution`
   --> src/lib.rs:246:13
    |
246 |         let order_of_execution = self.generate_order_of_execution();
    |             ^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_order_of_execution`

warning: unused variable: `variable_name`
   --> src/lib.rs:268:44
    |
268 |                 InterpreterCommand::Read { variable_name } => {
    |                                            ^^^^^^^^^^^^^ help: try ignoring the field: `variable_name: _`

warning: unused variable: `line_number_val`
   --> src/lib.rs:283:25
    |
283 |                     let line_number_val = line_number.parse::<u16>().unwrap();
    |                         ^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_line_number_val`

warning: unused variable: `condition`
   --> src/lib.rs:293:46
    |
293 |                 InterpreterCommand::IfGoto { condition, line_number } => {
    |                                              ^^^^^^^^^ help: try ignoring the field: `condition: _`

warning: unused variable: `line_number`
   --> src/lib.rs:293:57
    |
293 |                 InterpreterCommand::IfGoto { condition, line_number } => {
    |                                                         ^^^^^^^^^^^ help: try ignoring the field: `line_number: _`

warning: variable does not need to be mutable
   --> src/lib.rs:248:13
    |
248 |         let mut prev_line = 0;
    |             ----^^^^^^^^^
    |             |
    |             help: remove this `mut`
    |
    = note: `#[warn(unused_mut)]` on by default

warning: field `input` is never read
  --> src/lib.rs:83:5
   |
82 | pub struct Interpreter<'a, R: Read, W: Write> {
   |            ----------- field in this struct
83 |     input: BufReader<R>,
   |     ^^^^^
   |
   = note: `#[warn(dead_code)]` on by default

warning: unused `Result` that must be used
   --> src/lib.rs:257:25
    |
257 |                         self.output.write(value.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:262:33
    |
262 | ...                   self.output.write(result.to_string().as_bytes());
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: this `Result` may be an `Err` variant, which should be handled

warning: `solution` (lib) generated 15 warnings
    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_goto ... FAILED
test solution_test::test_basic_if ... FAILED
test solution_test::test_basic_input ... FAILED
test solution_test::test_basic_print ... FAILED
test solution_test::test_basic_read ... FAILED
test solution_test::test_erroring_goto ... FAILED
test solution_test::test_full_program ... FAILED
test solution_test::test_io_error_write ... FAILED
test solution_test::test_line_order_and_overwriting ... FAILED
test solution_test::test_print_cyrillic ... FAILED
test solution_test::test_print_vars_and_strings ... FAILED
test solution_test::test_runtime_errors ... FAILED
test solution_test::test_syntax_errors_1 ... ok
test solution_test::test_syntax_errors_2 ... ok
test solution_test::test_io_error_read ... FAILED

failures:

---- solution_test::test_basic_goto stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"1"`,
 right: `"1\n3\n"`', tests/solution_test.rs:193:9
thread 'solution_test::test_basic_goto' panicked at 'assertion failed: `(left == right)`
  left: `"1"`,
 right: `"1\n3\n"`', tests/solution_test.rs:180:5

---- solution_test::test_basic_if stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20230111-3772066-1jtd67l/solution/src/lib.rs:294:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'solution_test::test_basic_if' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:212:5

---- solution_test::test_basic_input stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20230111-3772066-1jtd67l/solution/src/lib.rs:269:21
thread 'solution_test::test_basic_input' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:103:5

---- solution_test::test_basic_print stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"42"`,
 right: `"42\n24\n"`', tests/solution_test.rs:78:9
thread 'solution_test::test_basic_print' panicked at 'assertion failed: `(left == right)`
  left: `"42"`,
 right: `"42\n24\n"`', tests/solution_test.rs:67:5

---- solution_test::test_basic_read stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20230111-3772066-1jtd67l/solution/src/lib.rs:269:21
thread 'solution_test::test_basic_read' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:120:5

---- solution_test::test_erroring_goto stdout ----
thread '<unnamed>' panicked at 'Expression Ok(()) does not match the pattern "Err(InterpreterError::RuntimeError { line_number: 20, .. })"', tests/solution_test.rs:206:9
thread 'solution_test::test_erroring_goto' panicked at 'Expression Ok(()) does not match the pattern "Err(InterpreterError::RuntimeError { line_number: 20, .. })"', tests/solution_test.rs:199:5

---- solution_test::test_full_program stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20230111-3772066-1jtd67l/solution/src/lib.rs:269:21
thread 'solution_test::test_full_program' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:230:5

---- solution_test::test_io_error_write stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20230111-3772066-1jtd67l/solution/src/lib.rs:269:21
thread 'solution_test::test_io_error_write' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:358:5

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

---- solution_test::test_print_cyrillic stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20230111-3772066-1jtd67l/solution/src/lib.rs:269:21
thread 'solution_test::test_print_cyrillic' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:160:5

---- solution_test::test_print_vars_and_strings stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20230111-3772066-1jtd67l/solution/src/lib.rs:269:21
thread 'solution_test::test_print_vars_and_strings' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:140:5

---- solution_test::test_runtime_errors stdout ----
thread '<unnamed>' panicked at 'Expression Ok(()) 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 Ok(()) does not match the pattern "Err(InterpreterError::RuntimeError { line_number: 11, .. })"', tests/solution_test.rs:301:5

---- solution_test::test_io_error_read stdout ----
thread 'solution_test::test_io_error_read' panicked at 'called `Result::unwrap()` on an `Err` value: Timeout', tests/solution_test.rs:344:5


failures:
    solution_test::test_basic_goto
    solution_test::test_basic_if
    solution_test::test_basic_input
    solution_test::test_basic_print
    solution_test::test_basic_read
    solution_test::test_erroring_goto
    solution_test::test_full_program
    solution_test::test_io_error_read
    solution_test::test_io_error_write
    solution_test::test_line_order_and_overwriting
    solution_test::test_print_cyrillic
    solution_test::test_print_vars_and_strings
    solution_test::test_runtime_errors

test result: FAILED. 2 passed; 13 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.00s

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

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

Билян качи първо решение на 10.01.2023 16:58 (преди над 2 години)