Решение на Basic BASIC от Искендер Чобанов

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

Към профила на Искендер Чобанов

Резултати

  • 9 точки от тестове
  • 0 бонус точки
  • 9 точки общо
  • 7 успешни тест(а)
  • 8 неуспешни тест(а)

Код

use std::io::{self, Write, Read,BufReader,BufRead,ErrorKind,Error};
//#[feature(map_first_last)]
use std::collections::{BTreeMap};
use std::ops::Bound::Included;
#[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> {
pub input: R,
output: &'a mut W,
pub lines: BTreeMap<u16,String>,
pub vars: BTreeMap<String,u16>,
pub input_vec : Vec<String>,
pub start : 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 mut buf = BufReader::new(input);
// println!("{:?}",&buf);
let mut num_bytes = 1;
let mut vec: Vec<String> = Vec::new();
let mut b = String::new();
while num_bytes > 0
{
num_bytes = buf.read_line(&mut b).unwrap();
// println!("{:?}",buf);
vec.push(b.clone());
b.clear();
}
Interpreter{input: buf.into_inner(),output,lines: BTreeMap::new() ,vars: BTreeMap::new(),input_vec: vec.into_iter().rev().collect(),start: 0}
}
pub fn add(&mut self, code: &str) -> Result<(), InterpreterError> {
let mut c = code.split_whitespace();
let lnum = c.next().expect("REASON").parse::<u16>().unwrap();
let command = c.next();
match command
{
Some("READ")=> {
let var = c.next();
if let None = var
{
return Err(InterpreterError::SyntaxError{code: format!("Missing argument after {} READ _<--",lnum)});
}
if !var.expect("Doesn't exist").chars().next().unwrap().is_uppercase()
{
return Err(InterpreterError::SyntaxError{code: code.to_string()});
}
self.lines.insert(lnum,format!("{} {}",command.unwrap(),var.unwrap()));
// ще подаваме на PRINT само синтактично-валидни стойности в тестовете
Ok(())},
Some("PRINT") => {
let var = c.next();
if let None = var
{
return Err(InterpreterError::SyntaxError{code: format!("Missing argument after READ _<--")});
}
if let Ok(_a) = var.unwrap().parse::<u16>()
{
self.lines.insert(lnum,format!("{} {}",command.unwrap(),var.unwrap()));return Ok(());
}
if !var.expect("Doesn't exist").chars().next().unwrap().is_uppercase()
{
return Err(InterpreterError::SyntaxError{code: code.to_string()});
}
self.lines.insert(lnum,format!("{} {}",command.unwrap(),var.unwrap())); Ok(())},
Some("GOTO") => {
let var = c.next();
let test = var.expect("REASON").parse::<u16>();
if let Err(_i) = test
{
return Err(InterpreterError::SyntaxError{code: code.to_string()});
}
self.lines.insert(lnum,format!("{} {}",command.unwrap(),var.unwrap()));
// ще подаваме на PRINT само синтактично-валидни стойности в тестовете
Ok(())},
Some("IF") => {
let v1 = c.next();
let op = c.next();
let v2 = c.next();
//condition will be saved in splitters |v1 op v2|
let goto = c.next();
let val = c.next();
let test = val.expect("REASON").parse::<u16>();
if let Some(_g) = Some("GOTO") {
if let Err(_i) = test
{
return Err(InterpreterError::SyntaxError{code: code.to_string()});
}
self.lines.insert(lnum,format!("{}|{} {} {}|{} {}",command.unwrap(),v1.unwrap(),op.unwrap()
,v2.unwrap(),goto.unwrap(),val.unwrap()));
// ще подаваме на PRINT само синтактично-валидни стойности в тестовете
return Ok(());}
Err(InterpreterError::SyntaxError{code: code.to_string()})},
_=>Err(InterpreterError::RuntimeError{line_number:lnum,message:"Command doens't exist".to_string()})
}
}
pub fn eval_value(&self, value: &str) -> Result<u16, InterpreterError> {
if let Ok(a) = value.parse::<u16>()
{
return Ok(a);
}
let get = self.vars.get_key_value(value);
match get {
None => Err(InterpreterError::SyntaxError{code: format!("Variable with name {} :does not exist",value)}),
Some((_v,a)) => {Ok(*a)}
}
}
pub fn basic_print(&mut self,line: u16,value: &str) -> Result<(), InterpreterError>{
let a = value;
if !(a.chars().next().unwrap().is_uppercase())
{
if let Err(_) = self.output.write(value.as_bytes())
{
return Err(InterpreterError::IoError(Error::new(ErrorKind::Other, format!("Could not write {} in the output stream",value))));
};
self.output.write("\n".as_bytes());
return Ok(());
}
let get = self.vars.get_key_value(value);
match get {
None => Err(InterpreterError::RuntimeError{line_number: line,message: format!("Variable with name {} :does not exist",value)}),
Some((_v,a)) => {self.output.write(a.to_string().as_bytes());self.output.write("\n".as_bytes()); Ok(())}
}
}
pub fn basic_read(&mut self,line: u16,var: &str) -> Result<(), InterpreterError>{
let num = self.input_vec.pop();
match num {
Some(n) => {let valid = n.trim().parse::<u16>();
if let Ok(x) = valid {self.vars.insert(var.to_string(),x); return Ok(())} else {
return Err(InterpreterError::RuntimeError{line_number: line,message: format!("Data not valid u16 number to bind {}",var)});
}},
_ => Err(InterpreterError::RuntimeError{line_number: line,message: format!("No more input values to bind to {}",var)}),
}
}
pub fn basic_goto(&mut self,line: u16,value: u16)-> Result<(), InterpreterError>
{
let l = self.lines.get_key_value(&value);
if let Some((x,_)) = l
{
self.start = *x;
return Ok(());
}
else {return Err(InterpreterError::RuntimeError{line_number: line,message: format!("GOTO Failed: No line: {} exists",value)});}
}
pub fn basic_if(&mut self,line: u16)-> Result<(u16,String,u16), InterpreterError>
{
let l = self.lines.get_key_value(&line);
if let Some((_x,body)) = l
{
let mut split = body.split("|");
if split.next()==Some("IF")
{
let col: Vec<&str> = split.next().expect("wont break").split_whitespace().collect();
let a = self.eval_value(col[0]);
let b = self.eval_value(col[2]);
return Ok((a.unwrap(),col[1].to_string(),b.unwrap()));
}
else {return Err(InterpreterError::RuntimeError{line_number: line,message: "IF FAILED".to_string()});}
}
else {return Err(InterpreterError::RuntimeError{line_number: line,message: "IF FAILED".to_string()});}
}
/// Функцията започва да изпълнява редовете на програмата в нарастващ ред. Ако стигне до GOTO,
/// скача на съответния ред и продължава от него в нарастващ ред. Когато вече няма ред с
/// по-голямо число, функцията свършва и връща `Ok(())`.
///
/// Вижте по-долу за отделните команди и какви грешки могат да върнат.
///
pub fn run(&mut self) -> Result<(), InterpreterError> {
let (mut k, _v1) : (u16,String) = (0,String::new());
let (mut l,mut _v2) : (u16,String) = (0,String::new());
if let Some(e) = self.lines.first_entry()
{
if *e.key() > 0 {
k=*e.key();
}
}
self.start = k;
if let Some(e) = self.lines.last_entry()
{
if *e.key() > 0 {
l=*e.key();
// println!("{:?}",l);
}
}
let mut stop = true;
let c = self.lines.clone();
while stop {
for (&key, val) in c.range((Included(&self.start), Included(&l))) {
let mut full = val.split_whitespace();
let command = full.next();
let par = full.next();
if command == Some("READ")
{
self.basic_read(key,par.unwrap());
}
if command == Some("PRINT")
{
self.basic_print(key,par.unwrap());
}
if command == Some("GOTO")
{
self.basic_goto(key,par.expect("nocrash").parse::<u16>().unwrap());
break;
}
if let Ok((a,b,c)) = self.basic_if(key)
{
let mut split = val.split("|");
split.next();
split.next();
let goto = split.next();
let mut v = goto.expect("").split_whitespace();
v.next();
//println!("{},{},{}",a,b,c);
if (b=="<" && a<c) || (b==">" && a>c) || (b=="=" && a==c)
{
self.basic_goto(key,v.next().expect("nocrash").parse::<u16>().unwrap());
break;
}
}
if key==l
{
stop=false;
break;
}
}
}
Ok(())
}
}
macro_rules! assert_match {
($expr:expr, $pat:pat) => {
let result = $expr;
if let $pat = result {
// all good
} else {
assert!(false, "Expression {:?} does not match the pattern {:?}", result, stringify!($pat));
}
}
}
/*
fn main()
{
let stdin = std::io::stdin();
let mut stdout = std::io::stdout();
let mut interpreter = Interpreter::new(stdin, &mut stdout);
interpreter.add("1 READ A").unwrap();
interpreter.add("2 READ B").unwrap();
interpreter.add("3 IF A > B GOTO 1").unwrap();
interpreter.add("4 PRINT B").unwrap();
println!("\n Answer should be 4 if input is \n2\n1\n3\n4");
// interpreter.add(...
print!("Answer:");
interpreter.run().unwrap();
}
*/
#[test]
fn test_basic_1() {
// Забележете `b""` -- низ от байтове
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");
}
#[test]
fn test_basic_2() {
let input: &[u8] = b"";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
assert_match!(interpreter.add("10 PRINT"), Err(InterpreterError::SyntaxError { .. }));
}
#[derive(Debug)]
struct NotBuffered {}
impl std::io::Read for NotBuffered {
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
Ok(0)
}
}
#[test]
fn test_not_buffered() {
let input = NotBuffered {};
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 PRINT 10").unwrap();
interpreter.run().unwrap();
}

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

Compiling solution v0.1.0 (/tmp/d20230111-3772066-2g6hur/solution)
warning: unused macro definition: `assert_match`
   --> src/lib.rs:263:14
    |
263 | macro_rules! assert_match {
    |              ^^^^^^^^^^^^
    |
    = note: `#[warn(unused_macros)]` on by default

warning: unused `Result` that must be used
   --> src/lib.rs:138:13
    |
138 |             self.output.write("\n".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:144:30
    |
144 |             Some((_v,a)) => {self.output.write(a.to_string().as_bytes());self.output.write("\n".as_bytes()); Ok(())}
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: this `Result` may be an `Err` variant, which should be handled

warning: unused `Result` that must be used
   --> src/lib.rs:144:74
    |
144 |             Some((_v,a)) => {self.output.write(a.to_string().as_bytes());self.output.write("\n".as_bytes()); Ok(())}
    |                                                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: this `Result` may be an `Err` variant, which should be handled

warning: unused `Result` that must be used
   --> src/lib.rs:220:17
    |
220 |                 self.basic_read(key,par.unwrap());
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: this `Result` may be an `Err` variant, which should be handled

warning: unused `Result` that must be used
   --> src/lib.rs:225:17
    |
225 |                 self.basic_print(key,par.unwrap());
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: this `Result` may be an `Err` variant, which should be handled

warning: unused `Result` that must be used
   --> src/lib.rs:230:17
    |
230 |                 self.basic_goto(key,par.expect("nocrash").parse::<u16>().unwrap());
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: this `Result` may be an `Err` variant, which should be handled

warning: unused `Result` that must be used
   --> src/lib.rs:245:20
    |
245 |                    self.basic_goto(key,v.next().expect("nocrash").parse::<u16>().unwrap());
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: this `Result` may be an `Err` variant, which should be handled

warning: `solution` (lib) generated 8 warnings
    Finished test [unoptimized + debuginfo] target(s) in 1.66s
     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_full_program ... FAILED
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 ... 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 ... FAILED
test solution_test::test_erroring_goto ... FAILED

failures:

---- solution_test::test_full_program stdout ----
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: SyntaxError { code: "100 PRINT too_high" }', tests/solution_test.rs:240:51
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: SyntaxError { code: "100 PRINT too_high" }', tests/solution_test.rs:230:5

---- 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-2g6hur/solution/src/lib.rs:40:47
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 '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_print_cyrillic stdout ----
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: SyntaxError { code: "50 PRINT евала" }', tests/solution_test.rs:170:47
thread 'solution_test::test_print_cyrillic' panicked at 'called `Result::unwrap()` on an `Err` value: SyntaxError { code: "50 PRINT евала" }', tests/solution_test.rs:160:5

---- solution_test::test_print_vars_and_strings stdout ----
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: SyntaxError { code: "50 PRINT abc" }', tests/solution_test.rs:150:45
thread 'solution_test::test_print_vars_and_strings' panicked at 'called `Result::unwrap()` on an `Err` value: SyntaxError { code: "50 PRINT abc" }', 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_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

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


failures:
    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_print_cyrillic
    solution_test::test_print_vars_and_strings
    solution_test::test_runtime_errors
    solution_test::test_syntax_errors_2

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

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

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

Искендер качи първо решение на 09.01.2023 17:32 (преди над 2 години)